mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-22 07:53:39 +01:00
Home WIP Favorites working a little better
This commit is contained in:
parent
59c4070733
commit
d37143c842
@ -13,8 +13,9 @@
|
|||||||
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
||||||
import {SlCard} from "@shoelace-style/shoelace";
|
import {SlCard} from "@shoelace-style/shoelace";
|
||||||
import interact from "@interactjs/interactjs";
|
import interact from "@interactjs/interactjs";
|
||||||
|
import type {InteractEvent} from "@interactjs/core/InteractEvent";
|
||||||
import {egw} from "../../jsapi/egw_global";
|
import {egw} from "../../jsapi/egw_global";
|
||||||
import {classMap, css, html} from "@lion/core";
|
import {classMap, css, html, TemplateResult} from "@lion/core";
|
||||||
import {HasSlotController} from "@shoelace-style/shoelace/dist/internal/slot";
|
import {HasSlotController} from "@shoelace-style/shoelace/dist/internal/slot";
|
||||||
import shoelace from "../Styles/shoelace";
|
import shoelace from "../Styles/shoelace";
|
||||||
import {Et2Dialog} from "../Et2Dialog/Et2Dialog";
|
import {Et2Dialog} from "../Et2Dialog/Et2Dialog";
|
||||||
@ -25,7 +26,7 @@ import {HomeApp} from "../../../../home/js/app";
|
|||||||
* Participate in Home
|
* Participate in Home
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class Et2Portlet extends (Et2Widget(SlCard))
|
export class Et2Portlet extends Et2Widget(SlCard)
|
||||||
{
|
{
|
||||||
static get properties()
|
static get properties()
|
||||||
{
|
{
|
||||||
@ -62,8 +63,12 @@ export class Et2Portlet extends (Et2Widget(SlCard))
|
|||||||
...shoelace,
|
...shoelace,
|
||||||
...(super.styles || []),
|
...(super.styles || []),
|
||||||
css`
|
css`
|
||||||
|
:host {
|
||||||
|
--header-spacing: var(--sl-spacing-medium);
|
||||||
|
}
|
||||||
|
|
||||||
.portlet__header {
|
.portlet__header {
|
||||||
flex: 1 0 auto;
|
flex: 0 0 auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
font-style: inherit;
|
font-style: inherit;
|
||||||
font-variant: inherit;
|
font-variant: inherit;
|
||||||
@ -73,7 +78,9 @@ export class Et2Portlet extends (Et2Widget(SlCard))
|
|||||||
font-size: var(--sl-font-size-medium);
|
font-size: var(--sl-font-size-medium);
|
||||||
line-height: var(--sl-line-height-dense);
|
line-height: var(--sl-line-height-dense);
|
||||||
padding: var(--header-spacing);
|
padding: var(--header-spacing);
|
||||||
|
padding-right: calc(2em + var(--header-spacing));
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.portlet__title {
|
.portlet__title {
|
||||||
@ -84,8 +91,8 @@ export class Et2Portlet extends (Et2Widget(SlCard))
|
|||||||
|
|
||||||
.portlet__header et2-button-icon {
|
.portlet__header et2-button-icon {
|
||||||
display: none;
|
display: none;
|
||||||
order: 99;
|
position: absolute;
|
||||||
margin-left: auto;
|
right: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.portlet__header:hover et2-button-icon {
|
.portlet__header:hover et2-button-icon {
|
||||||
@ -97,11 +104,17 @@ export class Et2Portlet extends (Et2Widget(SlCard))
|
|||||||
height: 100%
|
height: 100%
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card_header {
|
||||||
|
margin-right: calc(var(--sl-spacing-medium) + 1em);
|
||||||
|
}
|
||||||
|
|
||||||
.card__body {
|
.card__body {
|
||||||
/* display block to prevent overflow from our size */
|
/* display block to prevent overflow from our size */
|
||||||
display: block;
|
display: block;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
padding: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -151,6 +164,19 @@ export class Et2Portlet extends (Et2Widget(SlCard))
|
|||||||
.then(() => this._setupMoveResize());
|
.then(() => this._setupMoveResize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load further details from content
|
||||||
|
*
|
||||||
|
* Normal load & attribute assign will cast our settings object to a string
|
||||||
|
* @param _template_node
|
||||||
|
*/
|
||||||
|
transformAttributes(attrs)
|
||||||
|
{
|
||||||
|
super.transformAttributes(attrs);
|
||||||
|
let data = this.getArrayMgr("content").data.find(e => e.id && e.id == this.id);
|
||||||
|
this.settings = typeof attrs.settings == "string" ? data.value || data.settings || {} : attrs.settings;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overriden from parent to add in default actions
|
* Overriden from parent to add in default actions
|
||||||
*/
|
*/
|
||||||
@ -316,16 +342,17 @@ export class Et2Portlet extends (Et2Widget(SlCard))
|
|||||||
headerTemplate()
|
headerTemplate()
|
||||||
{
|
{
|
||||||
return html`
|
return html`
|
||||||
<header class="portlet__header">
|
<h2 class="portlet__title">${this.title}</h2>`;
|
||||||
<h2 class="portlet__title">${this.title}</h2>
|
|
||||||
<et2-button-icon name="gear" label="Settings" noSubmit=true
|
|
||||||
@click="${() => this.edit_settings()}"></et2-button-icon>
|
|
||||||
</header>`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footerTemplate()
|
bodyTemplate() : TemplateResult
|
||||||
{
|
{
|
||||||
return '';
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
footerTemplate() : TemplateResult
|
||||||
|
{
|
||||||
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -343,7 +370,27 @@ export class Et2Portlet extends (Et2Widget(SlCard))
|
|||||||
value: {
|
value: {
|
||||||
content: this.settings
|
content: this.settings
|
||||||
},
|
},
|
||||||
buttons: Et2Dialog.BUTTONS_OK_CANCEL
|
buttons: [
|
||||||
|
{
|
||||||
|
"button_id": Et2Dialog.OK_BUTTON,
|
||||||
|
label: this.egw().lang('ok'),
|
||||||
|
id: 'dialog[ok]',
|
||||||
|
image: 'check',
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.egw().lang('delete'),
|
||||||
|
id: 'delete',
|
||||||
|
image: 'delete',
|
||||||
|
align: "right"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"button_id": Et2Dialog.CANCEL_BUTTON,
|
||||||
|
label: this.egw().lang('cancel'),
|
||||||
|
id: 'cancel',
|
||||||
|
image: 'cancel'
|
||||||
|
}
|
||||||
|
],
|
||||||
});
|
});
|
||||||
// Set separately to avoid translation
|
// Set separately to avoid translation
|
||||||
dialog.title = this.egw().lang("Edit") + " " + (this.title || '');
|
dialog.title = this.egw().lang("Edit") + " " + (this.title || '');
|
||||||
@ -354,11 +401,18 @@ export class Et2Portlet extends (Et2Widget(SlCard))
|
|||||||
{
|
{
|
||||||
if(button_id != Et2Dialog.OK_BUTTON)
|
if(button_id != Et2Dialog.OK_BUTTON)
|
||||||
{
|
{
|
||||||
|
if(button_id == "delete")
|
||||||
|
{
|
||||||
|
this.update_settings('~remove~').then(() =>
|
||||||
|
{
|
||||||
|
this.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass updated settings, unless we're removing
|
// Pass updated settings, unless we're removing
|
||||||
this.update_settings((typeof value == 'string') ? {} : this.settings || {});
|
this.update_settings({...this.settings, ...value});
|
||||||
|
|
||||||
// Extend, not replace, because settings has types while value has just value
|
// Extend, not replace, because settings has types while value has just value
|
||||||
if(typeof value == 'object')
|
if(typeof value == 'object')
|
||||||
@ -367,13 +421,19 @@ export class Et2Portlet extends (Et2Widget(SlCard))
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected update_settings(settings)
|
public update_settings(settings)
|
||||||
{
|
{
|
||||||
|
// Skip any updates during loading
|
||||||
|
if(!this.getInstanceManager().isReady)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Save settings - server might reply with new content if the portlet needs an update,
|
// Save settings - server might reply with new content if the portlet needs an update,
|
||||||
// but ideally it doesn't
|
// but ideally it doesn't
|
||||||
this.classList.add("loading");
|
this.classList.add("loading");
|
||||||
|
|
||||||
this.egw().jsonq("home.home_ui.ajax_set_properties", [this.id, [], settings, this.settings ? this.settings.group : false],
|
return this.egw().jsonq("home.home_ui.ajax_set_properties", [this.id, [], settings, this.settings ? this.settings.group : false],
|
||||||
function(data)
|
function(data)
|
||||||
{
|
{
|
||||||
// This section not for us
|
// This section not for us
|
||||||
@ -437,8 +497,13 @@ export class Et2Portlet extends (Et2Widget(SlCard))
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<slot name="image" part="image" class="card__image">${this.imageTemplate()}</slot>
|
<slot name="image" part="image" class="card__image">${this.imageTemplate()}</slot>
|
||||||
<slot name="header" part="header" class="card__header">${this.headerTemplate()}</slot>
|
|
||||||
<slot part="body" class="card__body"></slot>
|
<header class="portlet__header">
|
||||||
|
<slot name="header" part="header" class="card__header">${this.headerTemplate()}</slot>
|
||||||
|
<et2-button-icon name="gear" label="Settings" noSubmit=true
|
||||||
|
@click="${() => this.edit_settings()}"></et2-button-icon>
|
||||||
|
</header>
|
||||||
|
<slot part="body" class="card__body">${this.bodyTemplate()}</slot>
|
||||||
<slot name="footer" part="footer" class="card__footer">${this.footerTemplate()}</slot>
|
<slot name="footer" part="footer" class="card__footer">${this.footerTemplate()}</slot>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
@ -94,7 +94,7 @@ export class et2_box extends et2_baseWidget implements et2_IDetachedDOM
|
|||||||
|
|
||||||
// Create the new element, if no expansion needed
|
// Create the new element, if no expansion needed
|
||||||
var id = et2_readAttrWithDefault(node, "id", "");
|
var id = et2_readAttrWithDefault(node, "id", "");
|
||||||
if(id.indexOf('$') < 0 || ['box', 'grid', 'et2-box'].indexOf(widgetType) == -1)
|
if(id.indexOf('$') < 0 || ['box', 'grid', 'et2-box'].indexOf(widgetType) == -1 && typeof customElements.get(widgetType) == "undefined")
|
||||||
{
|
{
|
||||||
this.createElementFromNode(node);
|
this.createElementFromNode(node);
|
||||||
childIndex++;
|
childIndex++;
|
||||||
|
@ -145,16 +145,57 @@ class home_favorite_portlet extends home_portlet
|
|||||||
|
|
||||||
public static function process($content = array())
|
public static function process($content = array())
|
||||||
{
|
{
|
||||||
unset($content); // not used, but required by function signature
|
unset($content); // not used, but required by function signature
|
||||||
|
|
||||||
// We need to keep the template going, thanks.
|
// We need to keep the template going, thanks.
|
||||||
Etemplate\Widget::setElementAttribute('','','');
|
Etemplate\Widget::setElementAttribute('', '', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_actions(){
|
public function get_actions()
|
||||||
|
{
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function get_type()
|
||||||
|
{
|
||||||
|
return 'et2-portlet-favorite';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of "Add" actions
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_add_actions()
|
||||||
|
{
|
||||||
|
$desc = $this->get_description();
|
||||||
|
$actions = array();
|
||||||
|
|
||||||
|
// Add a list of favorites
|
||||||
|
if($this->context['appname'] && ($favorites = Framework\Favorites::get_favorites($this->context['appname'])))
|
||||||
|
{
|
||||||
|
foreach($favorites as $name => $favorite)
|
||||||
|
{
|
||||||
|
$actions[] = array(
|
||||||
|
'id' => __CLASS__ . $name,
|
||||||
|
'caption' => $name,
|
||||||
|
'onExecute' => 'javaScript:app.home.add'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$actions[] = array(
|
||||||
|
'id' => __CLASS__,
|
||||||
|
'caption' => lang('List'),
|
||||||
|
'hint' => $desc['description'],
|
||||||
|
'onExecute' => 'javaScript:app.home.add',
|
||||||
|
'acceptedTypes' => $this->accept_drop(),
|
||||||
|
'allowOnMultiple' => $this->accept_multiple()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $actions;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Some descriptive information about the portlet, so that users can decide if
|
* Some descriptive information about the portlet, so that users can decide if
|
||||||
* they want it or not, and for inclusion in lists, hover text, etc.
|
* they want it or not, and for inclusion in lists, hover text, etc.
|
||||||
|
@ -92,17 +92,22 @@ class home_list_portlet extends home_portlet
|
|||||||
public function get_description()
|
public function get_description()
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
'displayName'=> 'List of entries',
|
'displayName' => 'List of entries',
|
||||||
'title'=> $this->title,
|
'title' => $this->title,
|
||||||
'description'=> lang('Show a list of entries')
|
'description' => lang('Show a list of entries')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function get_type()
|
||||||
|
{
|
||||||
|
return 'et2-portlet-list';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a fragment of HTML for display
|
* Get a fragment of HTML for display
|
||||||
*
|
*
|
||||||
* @param id String unique ID, provided to the portlet so it can make sure content is
|
* @param id String unique ID, provided to the portlet so it can make sure content is
|
||||||
* unique, if needed.
|
* unique, if needed.
|
||||||
* @return string HTML fragment for display
|
* @return string HTML fragment for display
|
||||||
*/
|
*/
|
||||||
public function exec($id = null, Etemplate &$etemplate = null)
|
public function exec($id = null, Etemplate &$etemplate = null)
|
||||||
@ -127,7 +132,7 @@ class home_list_portlet extends home_portlet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$etemplate->exec('home.home_list_portlet.exec',$content);
|
//$etemplate->exec('home.home_list_portlet.exec',$content);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,11 +49,39 @@ abstract class home_portlet
|
|||||||
*/
|
*/
|
||||||
public abstract function get_description();
|
public abstract function get_description();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the web-component tag used client side
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get_type()
|
||||||
|
{
|
||||||
|
return 'et2-portlet';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of "Add" actions
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_add_actions()
|
||||||
|
{
|
||||||
|
$desc = $this->get_description();
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
'id' => __CLASS__,
|
||||||
|
'caption' => $desc['displayName'],
|
||||||
|
'hint' => $desc['description'],
|
||||||
|
'onExecute' => 'javaScript:app.home.add',
|
||||||
|
'acceptedTypes' => $this->accept_drop(),
|
||||||
|
'allowOnMultiple' => $this->accept_multiple()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the display for the portlet
|
* Generate the display for the portlet
|
||||||
*
|
*
|
||||||
* @param id String unique ID, provided to the portlet so it can make sure content is
|
* @param id String unique ID, provided to the portlet so it can make sure content is
|
||||||
* unique, if needed.
|
* unique, if needed.
|
||||||
* @param Etemplate $etemplate eTemplate to generate content
|
* @param Etemplate $etemplate eTemplate to generate content
|
||||||
*/
|
*/
|
||||||
public abstract function exec($id = null, Etemplate &$etemplate = null);
|
public abstract function exec($id = null, Etemplate &$etemplate = null);
|
||||||
|
@ -115,8 +115,7 @@ class home_ui
|
|||||||
'onExecute' => 'javaScript:app.home.add',
|
'onExecute' => 'javaScript:app.home.add',
|
||||||
'children' => $add_portlets
|
'children' => $add_portlets
|
||||||
),
|
),
|
||||||
// Favorites are sortable which needs special handling,
|
// Favorites are sortable which needs special handling
|
||||||
// handled directly through jQuery
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add all known portlets as drop actions too. If there are multiple matches, there will be a menu
|
// Add all known portlets as drop actions too. If there are multiple matches, there will be a menu
|
||||||
@ -180,8 +179,9 @@ class home_ui
|
|||||||
$portlet = new $classname($context);
|
$portlet = new $classname($context);
|
||||||
$desc = $portlet->get_description();
|
$desc = $portlet->get_description();
|
||||||
$portlet_content = array(
|
$portlet_content = array(
|
||||||
'id' => $id
|
'id' => $id,
|
||||||
) + $desc + $context;
|
'type' => $portlet->get_type()
|
||||||
|
) + $desc + $context;
|
||||||
|
|
||||||
|
|
||||||
// Get settings
|
// Get settings
|
||||||
@ -566,6 +566,7 @@ class home_ui
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
$prefs->delete('home', $portlet_id);
|
$prefs->delete('home', $portlet_id);
|
||||||
|
$response->data([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -594,7 +595,7 @@ class home_ui
|
|||||||
$classname = substr($classname, 4);
|
$classname = substr($classname, 4);
|
||||||
}
|
}
|
||||||
$content = null;
|
$content = null;
|
||||||
$portlet = $this->get_portlet($portlet_id, $context, $content, $attributes, $full_exec);
|
$portlet = $this->get_portlet("home-index_portlets_" . $portlet_id, $context, $content, $attributes, $full_exec);
|
||||||
|
|
||||||
$context['class'] = get_class($portlet);
|
$context['class'] = get_class($portlet);
|
||||||
foreach($portlet->get_properties() as $property)
|
foreach($portlet->get_properties() as $property)
|
||||||
|
@ -39,7 +39,7 @@ class home_weather_portlet extends home_portlet
|
|||||||
if (false) parent::__construct();
|
if (false) parent::__construct();
|
||||||
|
|
||||||
// City not set for new widgets created via context menu
|
// City not set for new widgets created via context menu
|
||||||
if(!$context['city'] || $context['height'] < 2)
|
if(!$context['city'])
|
||||||
{
|
{
|
||||||
// Set initial size to 3x2, default is too small
|
// Set initial size to 3x2, default is too small
|
||||||
$context['width'] = 3;
|
$context['width'] = 3;
|
||||||
|
@ -1,7 +1,58 @@
|
|||||||
import {Et2Portlet} from "../../api/js/etemplate/Et2Portlet/Et2Portlet";
|
import {Et2Portlet} from "../../api/js/etemplate/Et2Portlet/Et2Portlet";
|
||||||
|
import {css, html} from "@lion/core";
|
||||||
|
import shoelace from "../../api/js/etemplate/Styles/shoelace";
|
||||||
|
import {etemplate2} from "../../api/js/etemplate/etemplate2";
|
||||||
|
|
||||||
export class Et2PortletFavorite extends Et2Portlet
|
export class Et2PortletFavorite extends Et2Portlet
|
||||||
{
|
{
|
||||||
|
static get styles()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
...shoelace,
|
||||||
|
...(super.styles || []),
|
||||||
|
css`
|
||||||
|
.portlet__header et2-button {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portlet__header:hover et2-button {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.toggleHeader = this.toggleHeader.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
headerTemplate()
|
||||||
|
{
|
||||||
|
return html`${super.headerTemplate()}
|
||||||
|
<et2-button id="header_toggle" slot="header" image="egw_action/arrows" class="closed"
|
||||||
|
noSubmit=true
|
||||||
|
@click=${this.toggleHeader}
|
||||||
|
></et2-button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toggleHeader()
|
||||||
|
{
|
||||||
|
//widget.set_class(widget.class == 'opened' ? 'closed' : 'opened');
|
||||||
|
// We operate on the DOM here, nm should be unaware of our fiddling
|
||||||
|
let nm = this.getWidgetById('nm') || etemplate2.getById(this.id) && etemplate2.getById(this.id).widgetContainer.getWidgetById('nm') || false;
|
||||||
|
if(!nm)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide header
|
||||||
|
nm.div.toggleClass('header_hidden');
|
||||||
|
nm.set_hide_header(nm.div.hasClass('header_hidden'));
|
||||||
|
nm.resize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!customElements.get("et2-portlet-favorite"))
|
if(!customElements.get("et2-portlet-favorite"))
|
||||||
|
175
home/js/Et2PortletList.ts
Normal file
175
home/js/Et2PortletList.ts
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
import {Et2Portlet} from "../../api/js/etemplate/Et2Portlet/Et2Portlet";
|
||||||
|
import {et2_createWidget} from "../../api/js/etemplate/et2_core_widget";
|
||||||
|
import {css, html, TemplateResult} from "@lion/core";
|
||||||
|
import shoelace from "../../api/js/etemplate/Styles/shoelace";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Home portlet to show a list of entries
|
||||||
|
*/
|
||||||
|
export class Et2PortletList extends Et2Portlet
|
||||||
|
{
|
||||||
|
static get styles()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
...shoelace,
|
||||||
|
...(super.styles || []),
|
||||||
|
css`
|
||||||
|
.delete_button {
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.link_change = this.link_change.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For list_portlet - opens a dialog to add a new entry to the list
|
||||||
|
*
|
||||||
|
* @param {egwAction} action Drop or add action
|
||||||
|
* @param {egwActionObject[]} Selected entries
|
||||||
|
* @param {egwActionObject} target_action Drop target
|
||||||
|
*/
|
||||||
|
add_link(action, source, target_action)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
debugger;
|
||||||
|
|
||||||
|
// Actions got confused drop vs popup
|
||||||
|
if(source[0].id == 'portlets')
|
||||||
|
{
|
||||||
|
return this.add_link(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get widget
|
||||||
|
let widget = null;
|
||||||
|
while(action.parent != null)
|
||||||
|
{
|
||||||
|
if(action.data && action.data.widget)
|
||||||
|
{
|
||||||
|
widget = action.data.widget;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
action = action.parent;
|
||||||
|
}
|
||||||
|
if(target_action == null)
|
||||||
|
{
|
||||||
|
// use template base url from initial template, to continue using webdav, if that was loaded via webdav
|
||||||
|
let splitted = 'home.edit'.split('.');
|
||||||
|
let path = app.home.portlet_container.getRoot()._inst.template_base_url + splitted.shift() + "/templates/default/" +
|
||||||
|
splitted.join('.') + ".xet";
|
||||||
|
let dialog = et2_createWidget("dialog", {
|
||||||
|
callback: function(button_id, value)
|
||||||
|
{
|
||||||
|
if(button_id == et2_dialog.CANCEL_BUTTON)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let new_list = widget.options.settings.list || [];
|
||||||
|
for(let i = 0; i < new_list.length; i++)
|
||||||
|
{
|
||||||
|
if(new_list[i].app == value.add.app && new_list[i].id == value.add.id)
|
||||||
|
{
|
||||||
|
// Duplicate - skip it
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value.add.link_id = value.add.app + ':' + value.add.id;
|
||||||
|
// Update server side
|
||||||
|
new_list.push(value.add);
|
||||||
|
widget._process_edit(button_id, {list: new_list});
|
||||||
|
// Update client side
|
||||||
|
let list = widget.getWidgetById('list');
|
||||||
|
if(list)
|
||||||
|
{
|
||||||
|
list.set_value(new_list);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
buttons: et2_dialog.BUTTONS_OK_CANCEL,
|
||||||
|
title: app.home.egw.lang('add'),
|
||||||
|
template: path,
|
||||||
|
value: {content: [{label: app.home.egw.lang('add'), type: 'link-entry', name: 'add', size: ''}]}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Drag'n'dropped something on the list - just send action IDs
|
||||||
|
let new_list = widget.options.settings.list || [];
|
||||||
|
let changed = false;
|
||||||
|
for(let i = 0; i < new_list.length; i++)
|
||||||
|
{
|
||||||
|
// Avoid duplicates
|
||||||
|
for(let j = 0; j < source.length; j++)
|
||||||
|
{
|
||||||
|
if(!source[j].id || new_list[i].app + "::" + new_list[i].id == source[j].id)
|
||||||
|
{
|
||||||
|
// Duplicate - skip it
|
||||||
|
source.splice(j, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(let i = 0; i < source.length; i++)
|
||||||
|
{
|
||||||
|
let explode = source[i].id.split('::');
|
||||||
|
new_list.push({app: explode[0], id: explode[1], link_id: explode.join(':')});
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if(changed)
|
||||||
|
{
|
||||||
|
widget._process_edit(et2_dialog.OK_BUTTON, {
|
||||||
|
list: new_list || {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Filemanager support - links need app = 'file' and type set
|
||||||
|
for(let i = 0; i < new_list.length; i++)
|
||||||
|
{
|
||||||
|
if(new_list[i]['app'] == 'filemanager')
|
||||||
|
{
|
||||||
|
new_list[i]['app'] = 'file';
|
||||||
|
new_list[i]['path'] = new_list[i]['title'] = new_list[i]['icon'] = new_list[i]['id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
widget.getWidgetById('list').set_value(new_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a link from the list
|
||||||
|
*/
|
||||||
|
link_change(event)
|
||||||
|
{
|
||||||
|
if(!this.getInstanceManager()?.isReady)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
debugger;
|
||||||
|
// Not used, but delete puts link in event.data
|
||||||
|
let link_data = event.data || false;
|
||||||
|
|
||||||
|
// Update settings on link delete
|
||||||
|
if(link_data)
|
||||||
|
{
|
||||||
|
this.update_settings({list: this.settings.list});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyTemplate() : TemplateResult
|
||||||
|
{
|
||||||
|
return html`
|
||||||
|
<et2-link-list .value=${this.settings?.list || []}
|
||||||
|
@change=${this.link_change}
|
||||||
|
>
|
||||||
|
</et2-link-list>`
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!customElements.get("et2-portlet-list"))
|
||||||
|
{
|
||||||
|
customElements.define("et2-portlet-list", Et2PortletList);
|
||||||
|
}
|
309
home/js/app.ts
309
home/js/app.ts
@ -8,12 +8,14 @@
|
|||||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AppJS} from "../../api/js/jsapi/app_base.js";
|
|
||||||
import {et2_createWidget} from "../../api/js/etemplate/et2_core_widget";
|
import {et2_createWidget} from "../../api/js/etemplate/et2_core_widget";
|
||||||
import {EgwApp} from "../../api/js/jsapi/egw_app";
|
import {EgwApp} from "../../api/js/jsapi/egw_app";
|
||||||
import {etemplate2} from "../../api/js/etemplate/etemplate2";
|
import {etemplate2} from "../../api/js/etemplate/etemplate2";
|
||||||
import {Et2Portlet} from "../../api/js/etemplate/Et2Portlet/Et2Portlet";
|
import {Et2Portlet} from "../../api/js/etemplate/Et2Portlet/Et2Portlet";
|
||||||
import {Et2PortletFavorite} from "./Et2PortletFavorite";
|
import {Et2PortletFavorite} from "./Et2PortletFavorite";
|
||||||
|
import {loadWebComponent} from "../../api/js/etemplate/Et2Widget/Et2Widget";
|
||||||
|
import "./Et2PortletList";
|
||||||
|
import Sortable from "sortablejs/modular/sortable.complete.esm.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JS for home application
|
* JS for home application
|
||||||
@ -30,7 +32,7 @@ export class HomeApp extends EgwApp
|
|||||||
/**
|
/**
|
||||||
* Grid resolution. Must match et2_portlet GRID
|
* Grid resolution. Must match et2_portlet GRID
|
||||||
*/
|
*/
|
||||||
public static GRID = 50;
|
public static GRID = 150;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default size for new portlets
|
* Default size for new portlets
|
||||||
@ -43,6 +45,7 @@ export class HomeApp extends EgwApp
|
|||||||
// List of portlets
|
// List of portlets
|
||||||
private portlets = {};
|
private portlets = {};
|
||||||
portlet_container : any;
|
portlet_container : any;
|
||||||
|
private sortable : Sortable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
@ -68,10 +71,13 @@ export class HomeApp extends EgwApp
|
|||||||
super.destroy(this.appname);
|
super.destroy(this.appname);
|
||||||
|
|
||||||
// Make sure all other sub-etemplates in portlets are done
|
// Make sure all other sub-etemplates in portlets are done
|
||||||
let others = etemplate2.getByApplication(this.appname);
|
if(this == window.app.home)
|
||||||
for(let i = 0; i < others.length; i++)
|
|
||||||
{
|
{
|
||||||
others[i].clear();
|
let others = etemplate2.getByApplication(this.appname);
|
||||||
|
for(let i = 0; i < others.length; i++)
|
||||||
|
{
|
||||||
|
others[i].clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,12 +102,9 @@ export class HomeApp extends EgwApp
|
|||||||
|
|
||||||
this.portlet_container = this.et2.getWidgetById("portlets");
|
this.portlet_container = this.et2.getWidgetById("portlets");
|
||||||
|
|
||||||
// Set up sorting of portlets
|
|
||||||
//this._do_ordering();
|
|
||||||
|
|
||||||
// Accept drops of favorites, which aren't part of action system
|
// Accept drops of favorites, which aren't part of action system
|
||||||
jQuery(this.et2.getDOMNode().parentNode).droppable({
|
this.sortable = new Sortable(this.et2.getDOMNode().parentNode, {
|
||||||
hoverClass: 'drop-hover',
|
chosenClass: 'drop-hover',
|
||||||
accept: function(draggable)
|
accept: function(draggable)
|
||||||
{
|
{
|
||||||
// Check for direct support for that application
|
// Check for direct support for that application
|
||||||
@ -111,8 +114,9 @@ export class HomeApp extends EgwApp
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
drop: function(event, ui)
|
onAdd: function(event, ui)
|
||||||
{
|
{
|
||||||
|
debugger;
|
||||||
// Favorite dropped on home - fake an action and divert to normal handler
|
// Favorite dropped on home - fake an action and divert to normal handler
|
||||||
let action = {
|
let action = {
|
||||||
data: {
|
data: {
|
||||||
@ -128,8 +132,9 @@ export class HomeApp extends EgwApp
|
|||||||
action.ui = ui;
|
action.ui = ui;
|
||||||
app.home.add_from_drop(action, [{data: ui.helper.context.dataset}])
|
app.home.add_from_drop(action, [{data: ui.helper.context.dataset}])
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
// Bind to unload to remove it from our list
|
// Bind to unload to remove it from our list
|
||||||
|
/*
|
||||||
.on('clear', '.et2_container[id]', jQuery.proxy(function(e)
|
.on('clear', '.et2_container[id]', jQuery.proxy(function(e)
|
||||||
{
|
{
|
||||||
if(e.target && e.target.id && this.portlets[e.target.id])
|
if(e.target && e.target.id && this.portlets[e.target.id])
|
||||||
@ -138,6 +143,8 @@ export class HomeApp extends EgwApp
|
|||||||
delete this.portlets[e.target.id];
|
delete this.portlets[e.target.id];
|
||||||
}
|
}
|
||||||
}, this));
|
}, this));
|
||||||
|
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
else if(et2.uniqueId)
|
else if(et2.uniqueId)
|
||||||
{
|
{
|
||||||
@ -148,7 +155,7 @@ export class HomeApp extends EgwApp
|
|||||||
window.setTimeout(() => {this.et2_ready(et2, name);}, 200);
|
window.setTimeout(() => {this.et2_ready(et2, name);}, 200);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let portlet = portlet_container.getWidgetById(et2.uniqueId);
|
let portlet = portlet_container.getWidgetById(et2.uniqueId) || et2.DOMContainer;
|
||||||
// Check for existing etemplate, this one loaded over it
|
// Check for existing etemplate, this one loaded over it
|
||||||
// NOTE: Moving them around like this can cause problems with event handlers
|
// NOTE: Moving them around like this can cause problems with event handlers
|
||||||
let existing = etemplate2.getById(et2.uniqueId);
|
let existing = etemplate2.getById(et2.uniqueId);
|
||||||
@ -163,14 +170,19 @@ export class HomeApp extends EgwApp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Set size & position
|
// Set size & position
|
||||||
let settings = portlet_container.getArrayMgr("content").data.find(e => e.id == et2.uniqueId) || {};
|
let et2_data = et2.widgetContainer.getArrayMgr("content").data;
|
||||||
|
let settings = et2_data && et2_data.id == portlet.id && et2_data || portlet_container.getArrayMgr("content").data.find(e => et2.uniqueId.endsWith(e.id)) || {settings: {}};
|
||||||
|
portlet.settings = settings.settings || {};
|
||||||
portlet.style.gridArea = settings.row + "/" + settings.col + "/ span " + (settings.height || 1) + "/ span " + (settings.width || 1);
|
portlet.style.gridArea = settings.row + "/" + settings.col + "/ span " + (settings.height || 1) + "/ span " + (settings.width || 1);
|
||||||
|
|
||||||
|
|
||||||
// It's in the right place for original load, but move it into portlet
|
// It's in the right place for original load, but move it into portlet
|
||||||
|
|
||||||
let misplaced = jQuery(etemplate2.getById('home-index').DOMContainer).siblings('#' + et2.DOMContainer.id);
|
let misplaced = jQuery(etemplate2.getById('home-index').DOMContainer).siblings('#' + et2.DOMContainer.id);
|
||||||
if(portlet)
|
|
||||||
|
if(portlet && et2.DOMContainer !== portlet)
|
||||||
{
|
{
|
||||||
portlet.addChild(et2.widgetContainer);
|
portlet.append(et2.DOMContainer);
|
||||||
et2.resize();
|
et2.resize();
|
||||||
}
|
}
|
||||||
if(portlet && misplaced.length)
|
if(portlet && misplaced.length)
|
||||||
@ -180,6 +192,10 @@ export class HomeApp extends EgwApp
|
|||||||
|
|
||||||
// Instanciate custom code for this portlet
|
// Instanciate custom code for this portlet
|
||||||
this._get_portlet_code(portlet);
|
this._get_portlet_code(portlet);
|
||||||
|
|
||||||
|
// Ordering of portlets
|
||||||
|
// Only needs to be done once, but its hard to tell when everything is loaded
|
||||||
|
this._do_ordering();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,10 +264,11 @@ export class HomeApp extends EgwApp
|
|||||||
{
|
{
|
||||||
let $portlet_container = jQuery(this.portlet_container.getDOMNode());
|
let $portlet_container = jQuery(this.portlet_container.getDOMNode());
|
||||||
attrs.row = Math.max(1, Math.round((action.menu_context.posy - $portlet_container.offset().top) / HomeApp.GRID) + 1);
|
attrs.row = Math.max(1, Math.round((action.menu_context.posy - $portlet_container.offset().top) / HomeApp.GRID) + 1);
|
||||||
attrs.col = Math.max(1, Math.round((action.menu_context.posx - $portlet_container.offset().left) / HomeApp.GRID) + 1);
|
// Use "auto" col to avoid any overlap or overflow
|
||||||
|
attrs.col = "auto";
|
||||||
}
|
}
|
||||||
|
|
||||||
let portlet = <Et2Portlet>et2_createWidget('et2-portlet', attrs, this.portlet_container);
|
let portlet = <Et2Portlet>loadWebComponent('et2-portlet', attrs, this.portlet_container);
|
||||||
portlet.loadingFinished();
|
portlet.loadingFinished();
|
||||||
|
|
||||||
// Get actual attributes & settings, since they're not available client side yet
|
// Get actual attributes & settings, since they're not available client side yet
|
||||||
@ -277,48 +294,38 @@ export class HomeApp extends EgwApp
|
|||||||
|
|
||||||
// Basic portlet attributes
|
// Basic portlet attributes
|
||||||
let attrs = {
|
let attrs = {
|
||||||
|
...HomeApp.DEFAULT,
|
||||||
id: this._create_id(),
|
id: this._create_id(),
|
||||||
class: action.data.class || action.id.substr(5),
|
class: action.data.class || action.id.substr(5),
|
||||||
width: this.DEFAULT.WIDTH,
|
dropped_data: []
|
||||||
height: this.DEFAULT.HEIGHT
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Try to find where the drop was
|
// Try to find where the drop was
|
||||||
if(action != null && action.ui && action.ui.position)
|
if(action != null && action.ui && action.ui.position)
|
||||||
{
|
{
|
||||||
attrs.row = Math.max(1, Math.round((action.ui.position.top - $portlet_container.offset().top) / this.GRID));
|
attrs.row = Math.max(1, Math.round((action.ui.position.top - $portlet_container.offset().top) / HomeApp.GRID));
|
||||||
attrs.col = Math.max(1, Math.round((action.ui.position.left - $portlet_container.offset().left) / this.GRID));
|
// Use "auto" col to avoid any overlap or overflow
|
||||||
|
attrs.col = "auto";
|
||||||
}
|
}
|
||||||
|
|
||||||
let portlet = <Et2Portlet>et2_createWidget('portlet', jQuery.extend({}, attrs), this.portlet_container);
|
|
||||||
portlet.loadingFinished();
|
|
||||||
// Immediately add content ID so etemplate loads into the right place
|
|
||||||
portlet.content.append('<div id="' + attrs.id + '" class="et2_container"/>');
|
|
||||||
|
|
||||||
// Get actual attributes & settings, since they're not available client side yet
|
// Get actual attributes & settings, since they're not available client side yet
|
||||||
let drop_data = [];
|
|
||||||
for(let i = 0; i < source.length; i++)
|
for(let i = 0; i < source.length; i++)
|
||||||
{
|
{
|
||||||
if(source[i].id)
|
if(source[i].id)
|
||||||
{
|
{
|
||||||
drop_data.push(source[i].id);
|
attrs.dropped_data.push(source[i].id);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
drop_data.push(source[i].data);
|
attrs.dropped_data.push(source[i].data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Don't pass default width & height so class can set it
|
|
||||||
delete attrs.width;
|
|
||||||
delete attrs.height;
|
|
||||||
portlet._process_edit(et2_dialog.OK_BUTTON, jQuery.extend({dropped_data: drop_data}, attrs));
|
|
||||||
|
|
||||||
// Set up sorting/grid of new portlet
|
let portlet = <Et2Portlet>loadWebComponent('et2-portlet', attrs, this.portlet_container);
|
||||||
$portlet_container.data("gridster").add_widget(
|
portlet.loadingFinished();
|
||||||
portlet.getDOMNode(),
|
|
||||||
this.DEFAULT.WIDTH, this.DEFAULT.HEIGHT,
|
// Get actual attributes & settings, since they're not available client side yet
|
||||||
attrs.col, attrs.row
|
portlet.update_settings(attrs);
|
||||||
);
|
|
||||||
|
|
||||||
// Instanciate custom code for this portlet
|
// Instanciate custom code for this portlet
|
||||||
this._get_portlet_code(portlet);
|
this._get_portlet_code(portlet);
|
||||||
@ -399,7 +406,7 @@ export class HomeApp extends EgwApp
|
|||||||
let p = this.portlet_container.getWidgetById(id);
|
let p = this.portlet_container.getWidgetById(id);
|
||||||
if(p)
|
if(p)
|
||||||
{
|
{
|
||||||
p._process_edit(et2_dialog.OK_BUTTON, '~reload~');
|
p.update_settings('~reload~');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,95 +469,25 @@ export class HomeApp extends EgwApp
|
|||||||
*/
|
*/
|
||||||
_do_ordering()
|
_do_ordering()
|
||||||
{
|
{
|
||||||
let $portlet_container = jQuery(this.portlet_container.getDOMNode());
|
if(!this.portlet_container)
|
||||||
$portlet_container
|
|
||||||
/* Gridster */
|
|
||||||
.gridster({
|
|
||||||
widget_selector: 'div.et2_portlet',
|
|
||||||
// Dimensions + margins = grid spacing
|
|
||||||
widget_base_dimensions: [home.GRID - 5, home.GRID - 5],
|
|
||||||
widget_margins: [5, 5],
|
|
||||||
extra_rows: 1,
|
|
||||||
extra_cols: 1,
|
|
||||||
min_cols: 3,
|
|
||||||
min_rows: 3,
|
|
||||||
/**
|
|
||||||
* Set which parameters we want when calling serialize().
|
|
||||||
* @param $w jQuery jQuery-wrapped element
|
|
||||||
* @param grid Object Grid settings
|
|
||||||
* @return Object - will be returned by gridster.serialize()
|
|
||||||
*/
|
|
||||||
serialize_params: function($w, grid)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
id: $w.attr('id').replace(app.home.portlet_container.getInstanceManager().uniqueId + '_', ''),
|
|
||||||
row: grid.row,
|
|
||||||
col: grid.col,
|
|
||||||
width: grid.size_x,
|
|
||||||
height: grid.size_y
|
|
||||||
};
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Gridster's internal drag settings
|
|
||||||
*/
|
|
||||||
draggable: {
|
|
||||||
handle: '.ui-widget-header',
|
|
||||||
stop: function(event, ui)
|
|
||||||
{
|
|
||||||
// Update widget(s)
|
|
||||||
let changed = this.serialize_changed();
|
|
||||||
|
|
||||||
// Reset changed, or they keep accumulating
|
|
||||||
this.$changed = jQuery([]);
|
|
||||||
|
|
||||||
for(let key in changed)
|
|
||||||
{
|
|
||||||
if(!changed[key].id)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Changed ID is the ID
|
|
||||||
let widget = window.app.home.portlet_container.getWidgetById(changed[key].id);
|
|
||||||
if(!widget || widget == window.app.home.portlet_container)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
egw().jsonq("home.home_ui.ajax_set_properties", [changed[key].id, {}, {
|
|
||||||
row: changed[key].row,
|
|
||||||
col: changed[key].col
|
|
||||||
}, widget.settings ? widget.settings.group : false],
|
|
||||||
null,
|
|
||||||
widget, true, widget
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
// Rescue selectboxes from Firefox
|
|
||||||
$portlet_container.on('mousedown touchstart', 'select', function(e)
|
|
||||||
{
|
{
|
||||||
e.stopPropagation();
|
return;
|
||||||
});
|
}
|
||||||
// Bind window resize to re-layout gridster
|
|
||||||
jQuery(window).one("resize." + this.et2._inst.uniqueId, function()
|
let col_map = {};
|
||||||
|
this.portlet_container.getDOMNode().querySelectorAll("[style*='grid-area']").forEach((n) =>
|
||||||
{
|
{
|
||||||
// Note this doesn't change the positions, just makes them invalid
|
let [col, span] = (getComputedStyle(n).gridColumn || "").split(" / ");
|
||||||
$portlet_container.data('gridster').recalculate_faux_grid();
|
if(typeof col_map[col] !== "undefined")
|
||||||
});
|
|
||||||
// Bind resize to update gridster - this may happen _before_ the widget gets a
|
|
||||||
// chance to update itself, so we can't use the widget
|
|
||||||
$portlet_container
|
|
||||||
.on("resizestop", function(event, ui)
|
|
||||||
{
|
{
|
||||||
$portlet_container.data("gridster").resize_widget(
|
// Set column to auto to avoid overlap
|
||||||
ui.element,
|
n.style.gridColumn = "auto / " + span;
|
||||||
Math.round(ui.size.width / app.home.GRID),
|
}
|
||||||
Math.round(ui.size.height / app.home.GRID)
|
else
|
||||||
);
|
{
|
||||||
});
|
col_map[col] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -572,126 +509,12 @@ export class HomeApp extends EgwApp
|
|||||||
/**
|
/**
|
||||||
* Functions for the list portlet
|
* Functions for the list portlet
|
||||||
*/
|
*/
|
||||||
/**
|
|
||||||
* For list_portlet - opens a dialog to add a new entry to the list
|
|
||||||
*
|
|
||||||
* @param {egwAction} action Drop or add action
|
|
||||||
* @param {egwActionObject[]} Selected entries
|
|
||||||
* @param {egwActionObject} target_action Drop target
|
|
||||||
*/
|
|
||||||
add_link(action, source, target_action)
|
|
||||||
{
|
|
||||||
// Actions got confused drop vs popup
|
|
||||||
if(source[0].id == 'portlets')
|
|
||||||
{
|
|
||||||
return this.add_link(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get widget
|
|
||||||
let widget = null;
|
|
||||||
while(action.parent != null)
|
|
||||||
{
|
|
||||||
if(action.data && action.data.widget)
|
|
||||||
{
|
|
||||||
widget = action.data.widget;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
action = action.parent;
|
|
||||||
}
|
|
||||||
if(target_action == null)
|
|
||||||
{
|
|
||||||
// use template base url from initial template, to continue using webdav, if that was loaded via webdav
|
|
||||||
let splitted = 'home.edit'.split('.');
|
|
||||||
let path = app.home.portlet_container.getRoot()._inst.template_base_url + splitted.shift() + "/templates/default/" +
|
|
||||||
splitted.join('.') + ".xet";
|
|
||||||
let dialog = et2_createWidget("dialog", {
|
|
||||||
callback: function(button_id, value)
|
|
||||||
{
|
|
||||||
if(button_id == et2_dialog.CANCEL_BUTTON)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let new_list = widget.options.settings.list || [];
|
|
||||||
for(let i = 0; i < new_list.length; i++)
|
|
||||||
{
|
|
||||||
if(new_list[i].app == value.add.app && new_list[i].id == value.add.id)
|
|
||||||
{
|
|
||||||
// Duplicate - skip it
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
value.add.link_id = value.add.app + ':' + value.add.id;
|
|
||||||
// Update server side
|
|
||||||
new_list.push(value.add);
|
|
||||||
widget._process_edit(button_id, {list: new_list});
|
|
||||||
// Update client side
|
|
||||||
let list = widget.getWidgetById('list');
|
|
||||||
if(list)
|
|
||||||
{
|
|
||||||
list.set_value(new_list);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
buttons: et2_dialog.BUTTONS_OK_CANCEL,
|
|
||||||
title: app.home.egw.lang('add'),
|
|
||||||
template: path,
|
|
||||||
value: {content: [{label: app.home.egw.lang('add'), type: 'link-entry', name: 'add', size: ''}]}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Drag'n'dropped something on the list - just send action IDs
|
|
||||||
let new_list = widget.options.settings.list || [];
|
|
||||||
let changed = false;
|
|
||||||
for(let i = 0; i < new_list.length; i++)
|
|
||||||
{
|
|
||||||
// Avoid duplicates
|
|
||||||
for(let j = 0; j < source.length; j++)
|
|
||||||
{
|
|
||||||
if(!source[j].id || new_list[i].app + "::" + new_list[i].id == source[j].id)
|
|
||||||
{
|
|
||||||
// Duplicate - skip it
|
|
||||||
source.splice(j, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(let i = 0; i < source.length; i++)
|
|
||||||
{
|
|
||||||
let explode = source[i].id.split('::');
|
|
||||||
new_list.push({app: explode[0], id: explode[1], link_id: explode.join(':')});
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if(changed)
|
|
||||||
{
|
|
||||||
widget._process_edit(et2_dialog.OK_BUTTON, {
|
|
||||||
list: new_list || {}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Filemanager support - links need app = 'file' and type set
|
|
||||||
for(let i = 0; i < new_list.length; i++)
|
|
||||||
{
|
|
||||||
if(new_list[i]['app'] == 'filemanager')
|
|
||||||
{
|
|
||||||
new_list[i]['app'] = 'file';
|
|
||||||
new_list[i]['path'] = new_list[i]['title'] = new_list[i]['icon'] = new_list[i]['id'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
widget.getWidgetById('list').set_value(new_list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a link from the list
|
* Remove a link from the list
|
||||||
*/
|
*/
|
||||||
link_change(list, link_id, row)
|
link_change(list, link_id, row)
|
||||||
{
|
{
|
||||||
// Quick response client side
|
list.link_change(link_id, row);
|
||||||
row.slideUp(row.remove);
|
|
||||||
|
|
||||||
// Actual removal
|
|
||||||
let portlet = list._parent._parent;
|
|
||||||
portlet.options.settings.list.splice(row.index(), 1);
|
|
||||||
portlet._process_edit(et2_dialog.OK_BUTTON, {list: portlet.options.settings.list || {}});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,8 +11,9 @@
|
|||||||
#home-index_portlets {
|
#home-index_portlets {
|
||||||
background-color: inherit;
|
background-color: inherit;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-auto-columns: 50ex;
|
grid-auto-columns: 25ex;
|
||||||
grid-auto-rows: 50ex;
|
grid-auto-rows: 25ex;
|
||||||
|
grid-auto-flow: dense;
|
||||||
|
|
||||||
gap: 2ex;
|
gap: 2ex;
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
<!DOCTYPE overlay PUBLIC "-//EGroupware GmbH//eTemplate 2.0//EN" "https://www.egroupware.org/etemplate2.0.dtd">
|
<!DOCTYPE overlay PUBLIC "-//EGroupware GmbH//eTemplate 2.0//EN" "https://www.egroupware.org/etemplate2.0.dtd">
|
||||||
<overlay>
|
<overlay>
|
||||||
<template id="home.favorite" template="" lang="" group="0" version="1.9.001">
|
<template id="home.favorite" template="" lang="" group="0" version="1.9.001">
|
||||||
<et2-button id="header_toggle" image="egw_action/arrows" class="closed" onclick="app.home.nextmatch_toggle_header" parentId="@header_node"></et2-button>
|
|
||||||
<nextmatch id="nm" class="header_hidden" hide_header="true"/>
|
<nextmatch id="nm" class="header_hidden" hide_header="true"/>
|
||||||
</template>
|
</template>
|
||||||
</overlay>
|
</overlay>
|
@ -8,26 +8,13 @@
|
|||||||
</et2-vbox>
|
</et2-vbox>
|
||||||
</template>
|
</template>
|
||||||
<template id="home.index" template="" lang="" group="0" version="1.9.001">
|
<template id="home.index" template="" lang="" group="0" version="1.9.001">
|
||||||
<grid width="100%">
|
|
||||||
<columns>
|
<old-box id="portlets">
|
||||||
<column/>
|
<!-- Box wrapper needed to get box to auto-repeat -->
|
||||||
</columns>
|
<et2-portlet type="${row_cont[type]}" id="${row_cont[id]}" title="$row_cont[title]" color="$row_cont[color]"
|
||||||
<rows>
|
parent_node="home-index_portlets" settings="$row_cont[settings]"
|
||||||
<row disabled="!@mainscreen_message">
|
value="$row_cont[content]" class="$row_cont[class]"
|
||||||
<html id="mainscreen_message"/>
|
actions="$row_cont[actions]"/>
|
||||||
</row>
|
</old-box>
|
||||||
<row>
|
</template>
|
||||||
<old-box id="portlets">
|
|
||||||
<!-- Box wrapper needed to get box to auto-repeat -->
|
|
||||||
<et2-box id="${row}">
|
|
||||||
<et2-portlet id="${_cont[id]}" title="$_cont[title]" color="@color"
|
|
||||||
parent_node="home-index_portlets" settings="@settings" width="@width"
|
|
||||||
height="@height" row="@row" col="@col" value="@content" class="@class"
|
|
||||||
actions="@actions"/>
|
|
||||||
</et2-box>
|
|
||||||
</old-box>
|
|
||||||
</row>
|
|
||||||
</rows>
|
|
||||||
</grid>
|
|
||||||
</template>
|
|
||||||
</overlay>
|
</overlay>
|
Loading…
Reference in New Issue
Block a user