mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-08 00:54:15 +01:00
LinkApp selection
This commit is contained in:
parent
c174e29238
commit
aa20af559a
217
api/js/etemplate/Et2Link/Et2LinkAppSelect.ts
Normal file
217
api/js/etemplate/Et2Link/Et2LinkAppSelect.ts
Normal file
@ -0,0 +1,217 @@
|
||||
import {SelectOption} from "../Et2Select/FindSelectOptions";
|
||||
import {css, html, TemplateResult} from "@lion/core";
|
||||
import {LionOption} from "@lion/select-rich";
|
||||
import {Et2Select} from "../Et2Select/Et2Select";
|
||||
import shoelace from "../Styles/shoelace";
|
||||
|
||||
let i : LionOption;
|
||||
|
||||
export class Et2LinkAppSelect extends Et2Select
|
||||
{
|
||||
static get styles()
|
||||
{
|
||||
return [
|
||||
...super.styles,
|
||||
shoelace,
|
||||
css`
|
||||
:host {
|
||||
--icon-width: 20px;
|
||||
}
|
||||
::part(control) {
|
||||
border: none;
|
||||
box-shadow: initial;
|
||||
}
|
||||
et2-image {
|
||||
width: var(--icon-width);
|
||||
}
|
||||
`
|
||||
]
|
||||
}
|
||||
|
||||
static get properties()
|
||||
{
|
||||
return {
|
||||
...super.properties,
|
||||
|
||||
/**
|
||||
* Limit to just this one application, and hide the selection
|
||||
*/
|
||||
"only_app": {type: String},
|
||||
/**
|
||||
* Limit to these applications (comma seperated).
|
||||
*/
|
||||
"application_list": {type: String},
|
||||
/**
|
||||
* Show application icons instead of application names
|
||||
*/
|
||||
"app_icons": {type: Boolean}
|
||||
}
|
||||
};
|
||||
|
||||
get slots()
|
||||
{
|
||||
return {
|
||||
...super.slots,
|
||||
input: () =>
|
||||
{
|
||||
const select = document.createElement("sl-select");
|
||||
const icon = document.createElement("et2-image");
|
||||
icon.setAttribute("slot", "prefix");
|
||||
icon.setAttribute("src", "api/navbar");
|
||||
icon.style.width = "var(--icon-width)";
|
||||
select.appendChild(icon);
|
||||
return select;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected __app_icons : boolean;
|
||||
protected __application_list : string[];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
*/
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
this.__app_icons = true;
|
||||
this.__application_list = [];
|
||||
|
||||
// Select options are based off abilities registered with link system
|
||||
this._reset_select_options();
|
||||
}
|
||||
|
||||
connectedCallback()
|
||||
{
|
||||
super.connectedCallback();
|
||||
|
||||
if(this.select_options != null)
|
||||
{
|
||||
// Preset to last application
|
||||
if(!this.value)
|
||||
{
|
||||
this.value = <string>this.egw().preference('link_app', this.egw().app_name());
|
||||
}
|
||||
}
|
||||
// Set icon
|
||||
this._inputNode.querySelector("[slot='prefix']").setAttribute("src", this.value + "/navbar");
|
||||
|
||||
// Register to
|
||||
this._inputNode.addEventListener("sl-change", () =>
|
||||
{
|
||||
// Set icon
|
||||
this._inputNode.querySelector("[slot='prefix']").setAttribute("src", this.value + "/navbar");
|
||||
|
||||
// update preference
|
||||
let appname = "";
|
||||
if(typeof this.value != 'undefined' && this.parentNode && this.parentNode.to_app)
|
||||
{
|
||||
appname = this.parentNode.to_app;
|
||||
}
|
||||
this.egw().set_preference(appname || this.egw().app_name(), 'link_app', this.value);
|
||||
this.dispatchEvent(new Event("change"));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before update() to compute values needed during the update
|
||||
*
|
||||
* @param changedProperties
|
||||
*/
|
||||
willUpdate(changedProperties)
|
||||
{
|
||||
super.willUpdate(changedProperties);
|
||||
|
||||
if(changedProperties.has("only_app") || changedProperties.has("application_list"))
|
||||
{
|
||||
this._reset_select_options();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the node where we're putting the selection options
|
||||
*
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
get _optionTargetNode() : HTMLElement
|
||||
{
|
||||
return this._inputNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwritten as sometimes called before this._inputNode is available,
|
||||
* and our options are text anyway
|
||||
*
|
||||
* @param {*} v - modelValue: can be an Object, Number, String depending on the
|
||||
* input type(date, number, email etc)
|
||||
* @returns {string} formattedValue
|
||||
*/
|
||||
formatter(v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limited select options here
|
||||
* This method will check properties and set select options appropriately
|
||||
*/
|
||||
private _reset_select_options()
|
||||
{
|
||||
let select_options = [];
|
||||
|
||||
// Limit to one app
|
||||
if(this.only_app)
|
||||
{
|
||||
select_options[this.only_app] = this.egw().lang(this.only_app);
|
||||
}
|
||||
else if(this.application_list.length > 0)
|
||||
{
|
||||
select_options = this.application_list;
|
||||
}
|
||||
else
|
||||
{
|
||||
//@ts-ignore link_app_list gives {app:name} instead of an array, but parent will fix it
|
||||
select_options = this.egw().link_app_list('query');
|
||||
if(typeof select_options['addressbook-email'] !== 'undefined')
|
||||
{
|
||||
delete select_options['addressbook-email'];
|
||||
}
|
||||
}
|
||||
this.select_options = select_options;
|
||||
}
|
||||
|
||||
|
||||
_optionTemplate(option : SelectOption) : TemplateResult
|
||||
{
|
||||
return html`
|
||||
<sl-menu-item value="${option.value}" title="${option.title}">
|
||||
${this.app_icons ? "" : option.label}
|
||||
${this._iconTemplate(option.value)}
|
||||
</sl-menu-item>`;
|
||||
}
|
||||
|
||||
_iconTemplate(appname)
|
||||
{
|
||||
let url = this.egw().image('navbar', appname);
|
||||
return html`
|
||||
<et2-image style="width: var(--icon-width)" slot="prefix" src="${url}"></et2-image>`;
|
||||
}
|
||||
|
||||
set value(new_value)
|
||||
{
|
||||
super.value = new_value;
|
||||
if(this._inputNode)
|
||||
{
|
||||
this._inputNode.value = new_value;
|
||||
}
|
||||
}
|
||||
|
||||
get value()
|
||||
{
|
||||
return this._inputNode?.value || "";
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore TypeScript is not recognizing that this widget is a LitElement
|
||||
customElements.define("et2-link-apps", Et2LinkAppSelect);
|
@ -19,6 +19,8 @@ export default [sl_css, css`
|
||||
:root,
|
||||
:host,
|
||||
.sl-theme-light {
|
||||
--sl-font-size-medium: 11px;
|
||||
|
||||
--sl-input-height-small: 18px;
|
||||
--sl-input-height-medium: 24px;
|
||||
|
||||
@ -27,6 +29,7 @@ export default [sl_css, css`
|
||||
|
||||
--sl-input-border-radius-small: 2px;
|
||||
--sl-input-border-radius-medium: 3px;
|
||||
--sl-input-border-color-focus: #E6E6E6;
|
||||
}
|
||||
|
||||
`];
|
@ -31,7 +31,7 @@ import {et2_vfsSelect} from "./et2_widget_vfs";
|
||||
import {egw} from "../jsapi/egw_global";
|
||||
import {et2_tabbox} from "./et2_widget_tabs";
|
||||
import {et2_csvSplit, et2_no_init} from "./et2_core_common";
|
||||
import {et2_IDetachedDOM, et2_IExposable} from "./et2_core_interfaces";
|
||||
import {et2_IDetachedDOM} from "./et2_core_interfaces";
|
||||
import {et2_DOMWidget} from "./et2_core_DOMWidget";
|
||||
import {Et2LinkList} from "./Et2Link/Et2LinkList";
|
||||
import type {Et2LinkString} from "./Et2Link/Et2LinkString";
|
||||
@ -700,44 +700,8 @@ export class et2_link_entry extends et2_inputWidget
|
||||
var self = this;
|
||||
this.div = jQuery(document.createElement("div")).addClass("et2_link_entry");
|
||||
|
||||
// Application selection
|
||||
jQuery.widget("custom.iconselectmenu", jQuery.ui.selectmenu, {
|
||||
_renderButtonItem: function (value)
|
||||
{
|
||||
var _value = value.value;
|
||||
var url = self.egw().image('navbar', _value);
|
||||
var buttonItem = jQuery("<span>", {
|
||||
"class": "ui-selectmenu-text",
|
||||
title: value.label
|
||||
});
|
||||
|
||||
buttonItem.css('background-image', 'url(' + url + ')');
|
||||
return buttonItem;
|
||||
},
|
||||
_renderItem: function (ul, item)
|
||||
{
|
||||
var li = jQuery("<li>", {class: "et2_link_entry_app_option"}),
|
||||
wrapper = jQuery("<div>", {text: item.label});
|
||||
|
||||
if (item.disabled)
|
||||
{
|
||||
li.addClass("ui-state-disabled");
|
||||
}
|
||||
ul.addClass(self.div.attr("class"));
|
||||
var url = self.egw().image('navbar', item.value);
|
||||
jQuery("<span>", {
|
||||
style: 'background-image: url("' + url + '");',
|
||||
"class": "ui-icon " + item.element.attr("data-class"),
|
||||
title: item.label
|
||||
})
|
||||
.appendTo(wrapper);
|
||||
|
||||
return li.append(wrapper).appendTo(ul);
|
||||
}
|
||||
});
|
||||
|
||||
this.app_select = jQuery(document.createElement("select")).appendTo(this.div)
|
||||
.change(function (e)
|
||||
this.app_select = jQuery(document.createElement("et2-link-apps")).appendTo(this.div)
|
||||
.on("change", function(e)
|
||||
{
|
||||
// Clear cache when app changes
|
||||
self.cache = {};
|
||||
@ -745,37 +709,23 @@ export class et2_link_entry extends et2_inputWidget
|
||||
// Update preference with new value
|
||||
egw.set_preference(self.options.value.to_app || self.egw().getAppName(), 'link_app', self.app_select.val());
|
||||
|
||||
if (typeof self.options.value != 'object') self.options.value = {};
|
||||
if(typeof self.options.value != 'object')
|
||||
{
|
||||
self.options.value = {};
|
||||
}
|
||||
self.options.value.app = self.app_select.val();
|
||||
})
|
||||
.attr("aria-label", egw.lang("linkapps"));
|
||||
var opt_count = 0;
|
||||
for (var key in this.options.select_options)
|
||||
{
|
||||
opt_count++;
|
||||
var option = jQuery(document.createElement("option"))
|
||||
.attr("value", key)
|
||||
.text(this.options.select_options[key]);
|
||||
option.appendTo(this.app_select);
|
||||
}
|
||||
if (this.options.only_app)
|
||||
{
|
||||
this.app_select.val(this.options.only_app);
|
||||
this.app_select.hide();
|
||||
if (this.options.app_icons && this.app_select.iconselectmenu('instance'))
|
||||
{
|
||||
this.app_select.iconselectmenu('widget').hide();
|
||||
}
|
||||
this.div.addClass("no_app");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Now that options are in, set to last used app
|
||||
this.app_select.val(this.options.value.app || '');
|
||||
if (this.app_select.iconselectmenu('instance'))
|
||||
{
|
||||
this.app_select.iconselectmenu('update');
|
||||
}
|
||||
}
|
||||
|
||||
// Search input
|
||||
@ -787,14 +737,7 @@ export class et2_link_entry extends et2_inputWidget
|
||||
{
|
||||
// Adjust width, leave room for app select & link button
|
||||
self.div.removeClass("no_app");
|
||||
if (self.options.app_icons)
|
||||
{
|
||||
self.app_select.iconselectmenu('widget').show();
|
||||
}
|
||||
else
|
||||
{
|
||||
self.app_select.show();
|
||||
}
|
||||
self.app_select.show();
|
||||
}
|
||||
})
|
||||
.blur(function (e)
|
||||
@ -804,7 +747,6 @@ export class et2_link_entry extends et2_inputWidget
|
||||
{
|
||||
// Adjust width, leave room for app select & link button
|
||||
self.div.addClass("no_app");
|
||||
self.app_select.iconselectmenu('widget').hide();
|
||||
}
|
||||
else if (self.search.val())
|
||||
{
|
||||
@ -822,6 +764,7 @@ export class et2_link_entry extends et2_inputWidget
|
||||
this.set_blur(this.options.blur ? this.options.blur : this.egw().lang("search"), this.search);
|
||||
|
||||
// Autocomplete
|
||||
/*
|
||||
this.search.autocomplete({
|
||||
source: function (request, response)
|
||||
{
|
||||
@ -849,8 +792,9 @@ export class et2_link_entry extends et2_inputWidget
|
||||
disabled: self.options.disabled,
|
||||
appendTo: self.div
|
||||
});
|
||||
|
||||
*/
|
||||
// Custom display (colors)
|
||||
/*
|
||||
this.search.data("uiAutocomplete")._renderItem = function (ul, item)
|
||||
{
|
||||
var li = jQuery(document.createElement('li'))
|
||||
@ -890,11 +834,13 @@ export class et2_link_entry extends et2_inputWidget
|
||||
return li;
|
||||
};
|
||||
|
||||
*/
|
||||
|
||||
// Bind to enter key to start search early
|
||||
this.search.keydown(function (e)
|
||||
this.search.keydown(function(e)
|
||||
{
|
||||
var keycode = (e.keyCode ? e.keyCode : e.which);
|
||||
if (keycode == 13 && !self.processing)
|
||||
if(keycode == 13 && !self.processing)
|
||||
{
|
||||
self.search.autocomplete("option", "minLength", 0);
|
||||
self.search.autocomplete("search");
|
||||
@ -990,23 +936,6 @@ export class et2_link_entry extends et2_inputWidget
|
||||
this.app_select.val(this.options.value.app);
|
||||
}
|
||||
|
||||
if (this.options.app_icons)
|
||||
{
|
||||
var self = this;
|
||||
this.div.addClass('app_icons');
|
||||
this.app_select.iconselectmenu({
|
||||
width: 50,
|
||||
change: function ()
|
||||
{
|
||||
window.setTimeout(function ()
|
||||
{
|
||||
self.app_select.trigger("change");
|
||||
}, 0);
|
||||
}
|
||||
})
|
||||
.iconselectmenu("menuWidget");
|
||||
this.app_select.iconselectmenu('widget').hide();
|
||||
}
|
||||
return super.doLoadingFinished.apply(this, arguments);
|
||||
}
|
||||
|
||||
@ -1102,10 +1031,7 @@ export class et2_link_entry extends et2_inputWidget
|
||||
|
||||
jQuery("option[value='" + _value.app + "']", this.app_select).prop("selected", true);
|
||||
this.app_select.hide();
|
||||
if (this.options.app_icons && this.app_select.iconselectmenu('instance'))
|
||||
{
|
||||
this.app_select.iconselectmenu('widget').hide();
|
||||
}
|
||||
|
||||
this.div.addClass("no_app");
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@ import './Expose/Et2DescriptionExpose';
|
||||
import './Et2Favorites/Et2Favorites';
|
||||
import './Et2Image/Et2Image';
|
||||
import './Et2Link/Et2Link';
|
||||
import './Et2Link/Et2LinkAppSelect';
|
||||
import './Et2Link/Et2LinkList';
|
||||
import './Et2Link/Et2LinkString';
|
||||
import './Et2Select/Et2Select';
|
||||
|
@ -162,6 +162,7 @@ class Select extends Etemplate\Widget
|
||||
{
|
||||
if ($child->type == 'option') $allowed[] = (string)$child->attrs['value'];
|
||||
}
|
||||
$allowed = array_map('strval', $allowed);
|
||||
|
||||
if (!$multiple || $this->attrs['multiple'] === "dynamic") $allowed[] = '';
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user