LinkApp selection

This commit is contained in:
nathan 2022-05-26 14:47:52 -06:00
parent c174e29238
commit aa20af559a
5 changed files with 238 additions and 90 deletions

View 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);

View File

@ -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;
}
`];

View File

@ -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");
}

View File

@ -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';

View File

@ -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[] = '';