mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-28 02:43:22 +01:00
parent
0f77eca5c4
commit
e323cd1d79
@ -21,18 +21,20 @@ import {fetchAll, nm_action, nm_compare_field} from "../../api/js/etemplate/et2_
|
|||||||
import "./CRM";
|
import "./CRM";
|
||||||
import {egw} from "../../api/js/jsapi/egw_global";
|
import {egw} from "../../api/js/jsapi/egw_global";
|
||||||
import {LitElement} from "@lion/core";
|
import {LitElement} from "@lion/core";
|
||||||
import {Et2SelectState} from "../../api/js/etemplate/Et2Select/Et2Select";
|
import {Et2SelectCountry} from "../../api/js/etemplate/Et2Select/Select/Et2SelectCountry";
|
||||||
import {Et2SelectCountry} from "../../api/js/etemplate/Et2Select/Et2SelectCountry";
|
|
||||||
|
import {Et2SelectState} from "../../api/js/etemplate/Et2Select/Select/Et2SelectState";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object to call app.addressbook.openCRMview with
|
* Object to call app.addressbook.openCRMview with
|
||||||
*/
|
*/
|
||||||
export interface CrmParams {
|
export interface CrmParams
|
||||||
contact_id: number|string;
|
{
|
||||||
crm_list?: "infolog"|"tracker"|"infolog-organisation"; // default: use preference
|
contact_id : number | string;
|
||||||
title?: string; // default: link-title of contact_id
|
crm_list? : "infolog" | "tracker" | "infolog-organisation"; // default: use preference
|
||||||
icon?: string; // default: avatar for contact_id
|
title? : string; // default: link-title of contact_id
|
||||||
index?: number;
|
icon? : string; // default: avatar for contact_id
|
||||||
|
index? : number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import {Directive, directive, html, repeat} from "@lion/core";
|
import {html} from "lit";
|
||||||
|
import {Directive, directive} from "lit/directive.js";
|
||||||
|
import {repeat} from "lit/directives/repeat.js";
|
||||||
import {et2_activateLinks} from "./et2_core_common";
|
import {et2_activateLinks} from "./et2_core_common";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
||||||
import {css, SlotMixin} from "@lion/core";
|
import {css} from "lit";
|
||||||
|
import {SlotMixin} from "@lion/core";
|
||||||
import {SlAvatar} from "@shoelace-style/shoelace";
|
import {SlAvatar} from "@shoelace-style/shoelace";
|
||||||
import {et2_IDetachedDOM} from "../et2_core_interfaces";
|
import {et2_IDetachedDOM} from "../et2_core_interfaces";
|
||||||
import {egw} from "../../jsapi/egw_global";
|
import {egw} from "../../jsapi/egw_global";
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
||||||
import {css, html, LitElement, repeat} from "@lion/core";
|
import {css, html, LitElement} from "lit";
|
||||||
|
import {repeat} from "lit/directives/repeat.js";
|
||||||
import shoelace from "../Styles/shoelace";
|
import shoelace from "../Styles/shoelace";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
import {Et2Avatar} from "./Et2Avatar";
|
import {Et2Avatar} from "./Et2Avatar";
|
||||||
import shoelace from "../Styles/shoelace";
|
import shoelace from "../Styles/shoelace";
|
||||||
import {css} from "@lion/core";
|
import {css} from "lit";
|
||||||
|
|
||||||
export class Et2LAvatar extends Et2Avatar
|
export class Et2LAvatar extends Et2Avatar
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* Cropper styles constant
|
* Cropper styles constant
|
||||||
*/
|
*/
|
||||||
import {css} from "@lion/core";
|
import {css} from "lit";
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Cropper.js v1.5.12
|
* Cropper.js v1.5.12
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {css, LitElement, PropertyValues} from "@lion/core";
|
import {css, LitElement, PropertyValues} from "lit";
|
||||||
import '../Et2Image/Et2Image';
|
import '../Et2Image/Et2Image';
|
||||||
import shoelace from "../Styles/shoelace";
|
import shoelace from "../Styles/shoelace";
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
|||||||
import '../Et2Image/Et2Image';
|
import '../Et2Image/Et2Image';
|
||||||
import {SlButton} from "@shoelace-style/shoelace";
|
import {SlButton} from "@shoelace-style/shoelace";
|
||||||
import {ButtonMixin} from "./ButtonMixin";
|
import {ButtonMixin} from "./ButtonMixin";
|
||||||
import {PropertyValues} from "@lion/core";
|
import {PropertyValues} from "lit";
|
||||||
|
|
||||||
|
|
||||||
export class Et2Button extends ButtonMixin(Et2InputWidget(SlButton))
|
export class Et2Button extends ButtonMixin(Et2InputWidget(SlButton))
|
||||||
|
@ -14,7 +14,7 @@ import '../Et2Image/Et2Image';
|
|||||||
import {SlIconButton} from "@shoelace-style/shoelace";
|
import {SlIconButton} from "@shoelace-style/shoelace";
|
||||||
import {ButtonMixin} from "./ButtonMixin";
|
import {ButtonMixin} from "./ButtonMixin";
|
||||||
import shoelace from "../Styles/shoelace";
|
import shoelace from "../Styles/shoelace";
|
||||||
import {css} from "@lion/core";
|
import {css} from "lit";
|
||||||
|
|
||||||
|
|
||||||
export class Et2ButtonIcon extends ButtonMixin(Et2InputWidget(SlIconButton))
|
export class Et2ButtonIcon extends ButtonMixin(Et2InputWidget(SlIconButton))
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* @author Nathan Gray
|
* @author Nathan Gray
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {css, html, LitElement} from "@lion/core";
|
import {css, html, LitElement} from "lit";
|
||||||
import {ButtonMixin} from "./ButtonMixin";
|
import {ButtonMixin} from "./ButtonMixin";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {css} from "@lion/core";
|
import {css} from "lit";
|
||||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
||||||
import '../Et2Image/Et2Image';
|
import '../Et2Image/Et2Image';
|
||||||
import {SlCheckbox} from "@shoelace-style/shoelace";
|
import {SlCheckbox} from "@shoelace-style/shoelace";
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import {et2_IDetachedDOM} from "../et2_core_interfaces";
|
import {et2_IDetachedDOM} from "../et2_core_interfaces";
|
||||||
import {et2_checkbox} from "../et2_widget_checkbox";
|
import {et2_checkbox} from "../et2_widget_checkbox";
|
||||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
||||||
import {classMap, css, html, LitElement} from "@lion/core";
|
import {css, html, LitElement} from "lit";
|
||||||
|
import {classMap} from "lit/directives/class-map.js"
|
||||||
import shoelace from "../Styles/shoelace";
|
import shoelace from "../Styles/shoelace";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {css, html, PropertyValues, render} from "@lion/core";
|
import {css, html, PropertyValues, render} from "lit";
|
||||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
||||||
import {SlColorPicker} from "@shoelace-style/shoelace";
|
import {SlColorPicker} from "@shoelace-style/shoelace";
|
||||||
import shoelace from "../Styles/shoelace";
|
import shoelace from "../Styles/shoelace";
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* Sharable date styles constant
|
* Sharable date styles constant
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {css} from "@lion/core";
|
import {css} from "lit";
|
||||||
import {colorsDefStyles} from "../Styles/colorsDefStyles";
|
import {colorsDefStyles} from "../Styles/colorsDefStyles";
|
||||||
import {cssImage} from "../Et2Widget/Et2Widget";
|
import {cssImage} from "../Et2Widget/Et2Widget";
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {css, html} from "@lion/core";
|
import {css, html} from "lit";
|
||||||
import 'lit-flatpickr';
|
import 'lit-flatpickr';
|
||||||
import {dateStyles} from "./DateStyles";
|
import {dateStyles} from "./DateStyles";
|
||||||
import type {Instance} from 'flatpickr/dist/types/instance';
|
import type {Instance} from 'flatpickr/dist/types/instance';
|
||||||
@ -19,15 +19,11 @@ import flatpickr from "flatpickr";
|
|||||||
import {egw} from "../../jsapi/egw_global";
|
import {egw} from "../../jsapi/egw_global";
|
||||||
import type {HTMLElementWithValue} from "@lion/form-core/types/FormControlMixinTypes";
|
import type {HTMLElementWithValue} from "@lion/form-core/types/FormControlMixinTypes";
|
||||||
import {Et2Textbox} from "../Et2Textbox/Et2Textbox";
|
import {Et2Textbox} from "../Et2Textbox/Et2Textbox";
|
||||||
import {Et2ButtonIcon} from "../Et2Button/Et2ButtonIcon";
|
|
||||||
import {FormControlMixin} from "@lion/form-core";
|
import {FormControlMixin} from "@lion/form-core";
|
||||||
import {LitFlatpickr} from "lit-flatpickr";
|
import {LitFlatpickr} from "lit-flatpickr";
|
||||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
||||||
import shoelace from "../Styles/shoelace";
|
import shoelace from "../Styles/shoelace";
|
||||||
|
|
||||||
const textbox = new Et2Textbox();
|
|
||||||
const button = new Et2ButtonIcon();
|
|
||||||
|
|
||||||
// list of existing localizations from node_modules/flatpicker/dist/l10n directory:
|
// list of existing localizations from node_modules/flatpicker/dist/l10n directory:
|
||||||
const l10n = [
|
const l10n = [
|
||||||
'ar', 'at', 'az', 'be', 'bg', 'bn', 'bs', 'cat', 'cs', 'cy', 'da', 'de', 'eo', 'es', 'et', 'fa', 'fi', 'fo',
|
'ar', 'at', 'az', 'be', 'bg', 'bn', 'bs', 'cat', 'cs', 'cy', 'da', 'de', 'eo', 'es', 'et', 'fa', 'fi', 'fo',
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {classMap, css, html, LitElement} from "@lion/core";
|
import {css, html, LitElement} from "lit";
|
||||||
|
import {classMap} from "lit/directives/class-map.js";
|
||||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
||||||
import {sprintf} from "../../egw_action/egw_action_common";
|
import {sprintf} from "../../egw_action/egw_action_common";
|
||||||
import {dateStyles} from "./DateStyles";
|
import {dateStyles} from "./DateStyles";
|
||||||
@ -132,6 +133,7 @@ export class Et2DateDuration extends Et2InputWidget(FormControlMixin(LitElement)
|
|||||||
}
|
}
|
||||||
|
|
||||||
.input-group__after {
|
.input-group__after {
|
||||||
|
display: contents;
|
||||||
margin-inline-start: var(--sl-input-spacing-medium);
|
margin-inline-start: var(--sl-input-spacing-medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -612,9 +614,9 @@ export class Et2DateDuration extends Et2InputWidget(FormControlMixin(LitElement)
|
|||||||
<et2-select value="${this._display.unit || this.displayFormat[0]}">
|
<et2-select value="${this._display.unit || this.displayFormat[0]}">
|
||||||
${[...this.displayFormat].map((format : string) =>
|
${[...this.displayFormat].map((format : string) =>
|
||||||
html`
|
html`
|
||||||
<sl-menu-item value=${format} ?checked=${this._display.unit === format}>
|
<sl-option value=${format}>
|
||||||
${this.time_formats[format]}
|
${this.time_formats[format]}
|
||||||
</sl-menu-item>`
|
</sl-option>`
|
||||||
)}
|
)}
|
||||||
</et2-select>
|
</et2-select>
|
||||||
`;
|
`;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {css, html} from "@lion/core";
|
import {css, html} from "lit";
|
||||||
import {Et2DateDuration, formatOptions} from "./Et2DateDuration";
|
import {Et2DateDuration, formatOptions} from "./Et2DateDuration";
|
||||||
import {dateStyles} from "./DateStyles";
|
import {dateStyles} from "./DateStyles";
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
||||||
import {FormControlMixin} from "@lion/form-core";
|
import {FormControlMixin} from "@lion/form-core";
|
||||||
import {classMap, css, html, ifDefined, LitElement, TemplateResult} from "@lion/core";
|
import {css, html, LitElement, TemplateResult} from "lit";
|
||||||
|
import {classMap} from "lit/directives/class-map.js";
|
||||||
|
import {ifDefined} from "lit/directives/if-defined.js";
|
||||||
import shoelace from "../Styles/shoelace";
|
import shoelace from "../Styles/shoelace";
|
||||||
import {dateStyles} from "./DateStyles";
|
import {dateStyles} from "./DateStyles";
|
||||||
import {formatDate, parseDate} from "./Et2Date";
|
import {formatDate, parseDate} from "./Et2Date";
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {html, LitElement} from "@lion/core";
|
import {html, LitElement} from "lit";
|
||||||
import {formatDate, parseDate} from "./Et2Date";
|
import {formatDate, parseDate} from "./Et2Date";
|
||||||
import {et2_IDetachedDOM} from "../et2_core_interfaces";
|
import {et2_IDetachedDOM} from "../et2_core_interfaces";
|
||||||
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {html} from "@lion/core";
|
import {html} from "lit";
|
||||||
import {parseDate, parseDateTime} from "./Et2Date";
|
import {parseDate, parseDateTime} from "./Et2Date";
|
||||||
import {Et2DateReadonly} from "./Et2DateReadonly";
|
import {Et2DateReadonly} from "./Et2DateReadonly";
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {css} from "@lion/core";
|
import {css} from "lit";
|
||||||
import {Et2Date, formatDate, formatDateTime} from "./Et2Date";
|
import {Et2Date, formatDate, formatDateTime} from "./Et2Date";
|
||||||
import type {Instance} from "flatpickr/dist/types/instance";
|
import type {Instance} from "flatpickr/dist/types/instance";
|
||||||
import {default as ShortcutButtonsPlugin} from "shortcut-buttons-flatpickr/dist/shortcut-buttons-flatpickr";
|
import {default as ShortcutButtonsPlugin} from "shortcut-buttons-flatpickr/dist/shortcut-buttons-flatpickr";
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
||||||
import {css, html, LitElement, render} from "@lion/core";
|
import {css, html, LitElement, render} from "lit";
|
||||||
import {et2_IDetachedDOM} from "../et2_core_interfaces";
|
import {et2_IDetachedDOM} from "../et2_core_interfaces";
|
||||||
import {activateLinks} from "../ActivateLinksDirective";
|
import {activateLinks} from "../ActivateLinksDirective";
|
||||||
import {et2_csvSplit} from "../et2_core_common";
|
import {et2_csvSplit} from "../et2_core_common";
|
||||||
|
@ -12,7 +12,12 @@
|
|||||||
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
||||||
import {et2_button} from "../et2_widget_button";
|
import {et2_button} from "../et2_widget_button";
|
||||||
import {et2_widget} from "../et2_core_widget";
|
import {et2_widget} from "../et2_core_widget";
|
||||||
import {classMap, css, html, ifDefined, LitElement, render, repeat, SlotMixin, styleMap} from "@lion/core";
|
import {css, html, LitElement, render} from "lit";
|
||||||
|
import {classMap} from "lit/directives/class-map.js";
|
||||||
|
import {ifDefined} from "lit/directives/if-defined.js";
|
||||||
|
import {repeat} from "lit/directives/repeat.js";
|
||||||
|
import {styleMap} from "lit/directives/style-map.js";
|
||||||
|
import {SlotMixin} from "@lion/core";
|
||||||
import {et2_template} from "../et2_widget_template";
|
import {et2_template} from "../et2_widget_template";
|
||||||
import {etemplate2} from "../etemplate2";
|
import {etemplate2} from "../etemplate2";
|
||||||
import {egw, IegwAppLocal} from "../../jsapi/egw_global";
|
import {egw, IegwAppLocal} from "../../jsapi/egw_global";
|
||||||
|
@ -9,11 +9,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {Et2Button} from "../Et2Button/Et2Button";
|
|
||||||
import {SlButtonGroup, SlDropdown} from "@shoelace-style/shoelace";
|
import {SlButtonGroup, SlDropdown} from "@shoelace-style/shoelace";
|
||||||
import {css, html, TemplateResult} from "@lion/core";
|
import {css, html, LitElement, TemplateResult} from "lit";
|
||||||
import {Et2WidgetWithSelectMixin} from "../Et2Select/Et2WidgetWithSelectMixin";
|
import {Et2WidgetWithSelectMixin} from "../Et2Select/Et2WidgetWithSelectMixin";
|
||||||
import {SelectOption} from "../Et2Select/FindSelectOptions";
|
import {SelectOption} from "../Et2Select/FindSelectOptions";
|
||||||
|
import shoelace from "../Styles/shoelace";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A split button - a button with a dropdown list
|
* A split button - a button with a dropdown list
|
||||||
@ -28,13 +28,14 @@ import {SelectOption} from "../Et2Select/FindSelectOptions";
|
|||||||
* as for a select box, but the title can also be full HTML if needed.
|
* as for a select box, but the title can also be full HTML if needed.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export class Et2DropdownButton extends Et2WidgetWithSelectMixin(Et2Button)
|
export class Et2DropdownButton extends Et2WidgetWithSelectMixin(LitElement)
|
||||||
{
|
{
|
||||||
|
|
||||||
static get styles()
|
static get styles()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
...super.styles,
|
...super.styles,
|
||||||
|
shoelace,
|
||||||
css`
|
css`
|
||||||
:host {
|
:host {
|
||||||
/* Avoid unwanted style overlap from button */
|
/* Avoid unwanted style overlap from button */
|
||||||
@ -98,6 +99,7 @@ export class Et2DropdownButton extends Et2WidgetWithSelectMixin(Et2Button)
|
|||||||
// We have our own render, so we can handle it internally
|
// We have our own render, so we can handle it internally
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
render() : TemplateResult
|
render() : TemplateResult
|
||||||
{
|
{
|
||||||
if(this.readonly)
|
if(this.readonly)
|
||||||
@ -129,10 +131,10 @@ export class Et2DropdownButton extends Et2WidgetWithSelectMixin(Et2Button)
|
|||||||
<et2-image slot="prefix" src=${option.icon} icon></et2-image>` : '';
|
<et2-image slot="prefix" src=${option.icon} icon></et2-image>` : '';
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<sl-menu-item value="${option.value}">
|
<sl-option value="${option.value}">
|
||||||
${icon}
|
${icon}
|
||||||
${this.noLang ? option.label : this.egw().lang(option.label)}
|
${this.noLang ? option.label : this.egw().lang(option.label)}
|
||||||
</sl-menu-item>`;
|
</sl-option>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _handleSelect(ev)
|
protected _handleSelect(ev)
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Et2DropdownButton} from "../Et2DropdownButton/Et2DropdownButton";
|
import {Et2DropdownButton} from "../Et2DropdownButton/Et2DropdownButton";
|
||||||
import {css, html, PropertyValues, TemplateResult} from "@lion/core";
|
import {css, html, PropertyValues, TemplateResult} from "lit";
|
||||||
import {SelectOption} from "../Et2Select/FindSelectOptions";
|
import {SelectOption} from "../Et2Select/FindSelectOptions";
|
||||||
import {et2_INextmatchHeader, et2_nextmatch} from "../et2_extension_nextmatch";
|
import {et2_INextmatchHeader, et2_nextmatch} from "../et2_extension_nextmatch";
|
||||||
import {Et2Image} from "../Et2Image/Et2Image";
|
import {Et2Image} from "../Et2Image/Et2Image";
|
||||||
@ -76,24 +76,24 @@ export class Et2Favorites extends Et2DropdownButton implements et2_INextmatchHea
|
|||||||
min-width: 15em;
|
min-width: 15em;
|
||||||
}
|
}
|
||||||
|
|
||||||
sl-menu-item:hover et2-image[src="trash"] {
|
sl-option:hover et2-image[src="trash"] {
|
||||||
display: initial;
|
display: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add star icons - radio button is already in prefix */
|
/* Add star icons - radio button is already in prefix */
|
||||||
|
|
||||||
sl-menu-item::part(base) {
|
sl-option::part(base) {
|
||||||
background-image: ${cssImage("fav_filter")};
|
background-image: ${cssImage("fav_filter")};
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: 16px 16px;
|
background-size: 16px 16px;
|
||||||
background-position: 5px center;
|
background-position: 5px center;
|
||||||
}
|
}
|
||||||
|
|
||||||
sl-menu-item[checked]::part(base) {
|
sl-option[checked]::part(base) {
|
||||||
background-image: ${cssImage("favorites")};
|
background-image: ${cssImage("favorites")};
|
||||||
}
|
}
|
||||||
|
|
||||||
sl-menu-item:last-child::part(base) {
|
sl-option:last-child::part(base) {
|
||||||
background-image: none;
|
background-image: none;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
@ -185,11 +185,11 @@ export class Et2Favorites extends Et2DropdownButton implements et2_INextmatchHea
|
|||||||
statustext="${this.egw().lang("Delete")}"></et2-image>`;
|
statustext="${this.egw().lang("Delete")}"></et2-image>`;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<sl-menu-item value="${option.value}" ?checked="${option.value == this._preferred}">
|
<sl-option value="${option.value}" ?checked="${option.value == this._preferred}">
|
||||||
${option.value !== Et2Favorites.ADD_VALUE ? radio : ""}
|
${option.value !== Et2Favorites.ADD_VALUE ? radio : ""}
|
||||||
${icon}
|
${icon}
|
||||||
${option.label}
|
${option.label}
|
||||||
</sl-menu-item>`;
|
</sl-option>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {css, html, LitElement, SlotMixin} from "@lion/core";
|
import {css, html, LitElement} from "lit";
|
||||||
|
import {SlotMixin} from "@lion/core";
|
||||||
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
||||||
|
|
||||||
export class Et2Iframe extends Et2Widget(SlotMixin(LitElement))
|
export class Et2Iframe extends Et2Widget(SlotMixin(LitElement))
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
* @author Nathan Gray
|
* @author Nathan Gray
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {css, html, LitElement, render} from "lit";
|
||||||
import {css, html, LitElement, render, SlotMixin} from "@lion/core";
|
import {SlotMixin} from "@lion/core";
|
||||||
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
||||||
import {et2_IDetachedDOM} from "../et2_core_interfaces";
|
import {et2_IDetachedDOM} from "../et2_core_interfaces";
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import {et2_IInput, et2_IInputNode, et2_ISubmitListener} from "../et2_core_interfaces";
|
import {et2_IInput, et2_IInputNode, et2_ISubmitListener} from "../et2_core_interfaces";
|
||||||
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
||||||
import {css, dedupeMixin, LitElement, PropertyValues} from "@lion/core";
|
import {css, LitElement, PropertyValues} from "lit";
|
||||||
import {Required} from "../Validators/Required";
|
import {Required} from "../Validators/Required";
|
||||||
import {ManualMessage} from "../Validators/ManualMessage";
|
import {ManualMessage} from "../Validators/ManualMessage";
|
||||||
import {LionValidationFeedback, Validator} from "@lion/form-core";
|
import {LionValidationFeedback, Validator} from "@lion/form-core";
|
||||||
import {et2_csvSplit} from "../et2_core_common";
|
import {et2_csvSplit} from "../et2_core_common";
|
||||||
|
import {dedupeMixin} from "@lion/core";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This mixin will allow any LitElement to become an Et2InputWidget
|
* This mixin will allow any LitElement to become an Et2InputWidget
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import {ExposeMixin, ExposeValue} from "../Expose/ExposeMixin";
|
import {ExposeMixin, ExposeValue} from "../Expose/ExposeMixin";
|
||||||
import {css, html, LitElement, TemplateResult} from "@lion/core";
|
import {css, html, LitElement, TemplateResult} from "lit";
|
||||||
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
||||||
import {et2_IDetachedDOM} from "../et2_core_interfaces";
|
import {et2_IDetachedDOM} from "../et2_core_interfaces";
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
||||||
|
import {css, html, LitElement, PropertyValues} from "lit";
|
||||||
import {FormControlMixin, ValidateMixin} from "@lion/form-core";
|
import {FormControlMixin, ValidateMixin} from "@lion/form-core";
|
||||||
import {css, html, LitElement, PropertyValues, SlotMixin} from "@lion/core";
|
import {SlotMixin} from "@lion/core";
|
||||||
import {Et2LinkAppSelect} from "./Et2LinkAppSelect";
|
import {Et2LinkAppSelect} from "./Et2LinkAppSelect";
|
||||||
import {LinkInfo} from "./Et2Link";
|
import {LinkInfo} from "./Et2Link";
|
||||||
import {Et2Button} from "../Et2Button/Et2Button";
|
import {Et2Button} from "../Et2Button/Et2Button";
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import {cleanSelectOptions, SelectOption} from "../Et2Select/FindSelectOptions";
|
import {cleanSelectOptions, SelectOption} from "../Et2Select/FindSelectOptions";
|
||||||
import {css, html, SlotMixin, TemplateResult} from "@lion/core";
|
import {css, html, TemplateResult} from "lit";
|
||||||
import {Et2Select} from "../Et2Select/Et2Select";
|
import {Et2Select} from "../Et2Select/Et2Select";
|
||||||
|
|
||||||
|
|
||||||
export class Et2LinkAppSelect extends SlotMixin(Et2Select)
|
export class Et2LinkAppSelect extends Et2Select
|
||||||
{
|
{
|
||||||
static get styles()
|
static get styles()
|
||||||
{
|
{
|
||||||
@ -49,22 +49,11 @@ export class Et2LinkAppSelect extends SlotMixin(Et2Select)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
get slots()
|
/*
|
||||||
{
|
icon.style.width = "var(--icon-width)";
|
||||||
return {
|
icon.style.height = "var(--icon-width)";
|
||||||
...super.slots,
|
|
||||||
"": () =>
|
|
||||||
{
|
|
||||||
|
|
||||||
const icon = document.createElement("et2-image");
|
*/
|
||||||
icon.setAttribute("slot", "prefix");
|
|
||||||
icon.setAttribute("src", "api/navbar");
|
|
||||||
icon.style.width = "var(--icon-width)";
|
|
||||||
icon.style.height = "var(--icon-width)";
|
|
||||||
return icon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected __applicationList : string[];
|
protected __applicationList : string[];
|
||||||
protected __onlyApp : string;
|
protected __onlyApp : string;
|
||||||
@ -104,9 +93,6 @@ export class Et2LinkAppSelect extends SlotMixin(Et2Select)
|
|||||||
{
|
{
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
|
||||||
// Set icon
|
|
||||||
this.querySelector(":scope > [slot='prefix']").setAttribute("src", this.egw().link_get_registry(this.value, 'icon') ?? this.value + "/navbar");
|
|
||||||
|
|
||||||
if(!this.value)
|
if(!this.value)
|
||||||
{
|
{
|
||||||
// use preference
|
// use preference
|
||||||
@ -118,7 +104,7 @@ export class Et2LinkAppSelect extends SlotMixin(Et2Select)
|
|||||||
this.value = this.egw().preference('link_app', appname || this.egw().app_name());
|
this.value = this.egw().preference('link_app', appname || this.egw().app_name());
|
||||||
}
|
}
|
||||||
// Register to
|
// Register to
|
||||||
this.addEventListener("change", this._handleChange);
|
this.addEventListener("sl-change", this._handleChange);
|
||||||
|
|
||||||
if(this.__onlyApp)
|
if(this.__onlyApp)
|
||||||
{
|
{
|
||||||
@ -129,7 +115,7 @@ export class Et2LinkAppSelect extends SlotMixin(Et2Select)
|
|||||||
disconnectedCallback()
|
disconnectedCallback()
|
||||||
{
|
{
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
this.removeEventListener("change", this._handleChange);
|
this.removeEventListener("sl-change", this._handleChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,10 +159,9 @@ export class Et2LinkAppSelect extends SlotMixin(Et2Select)
|
|||||||
super.value = new_value;
|
super.value = new_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleChange(e)
|
handleValueChange(e)
|
||||||
{
|
{
|
||||||
// Set icon
|
super.handleValueChange(e);
|
||||||
this.querySelector(":scope > [slot='prefix']").setAttribute("src", this.egw().link_get_registry(this.value, 'icon'));
|
|
||||||
|
|
||||||
// update preference
|
// update preference
|
||||||
let appname = "";
|
let appname = "";
|
||||||
@ -198,13 +183,21 @@ export class Et2LinkAppSelect extends SlotMixin(Et2Select)
|
|||||||
// Limit to one app
|
// Limit to one app
|
||||||
if(this.onlyApp)
|
if(this.onlyApp)
|
||||||
{
|
{
|
||||||
select_options.push({value: this.onlyApp, label: this.egw().lang(this.onlyApp)});
|
select_options.push({
|
||||||
|
value: this.onlyApp,
|
||||||
|
label: this.egw().lang(this.onlyApp),
|
||||||
|
icon: this.egw().link_get_registry(this.onlyApp, 'icon') ?? this.onlyApp + "/navbar"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else if(this.applicationList.length > 0)
|
else if(this.applicationList.length > 0)
|
||||||
{
|
{
|
||||||
select_options = this.applicationList.map((app) =>
|
select_options = this.applicationList.map((app) =>
|
||||||
{
|
{
|
||||||
return {value: app, label: this.egw().lang(app)};
|
return {
|
||||||
|
value: app,
|
||||||
|
label: this.egw().lang(app),
|
||||||
|
icon: this.egw().link_get_registry(app, 'icon') ?? app + "/navbar"
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -215,29 +208,27 @@ export class Et2LinkAppSelect extends SlotMixin(Et2Select)
|
|||||||
{
|
{
|
||||||
delete select_options['addressbook-email'];
|
delete select_options['addressbook-email'];
|
||||||
}
|
}
|
||||||
|
select_options = cleanSelectOptions(select_options);
|
||||||
|
select_options.map((option) =>
|
||||||
|
{
|
||||||
|
option.icon = this.egw().link_get_registry(option.value, 'icon') ?? option.value + "/navbar"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (!this.value)
|
if (!this.value)
|
||||||
{
|
{
|
||||||
this.value = <string>this.egw().preference('link_app', this.egw().app_name());
|
this.value = <string>this.egw().preference('link_app', this.egw().app_name());
|
||||||
}
|
}
|
||||||
this.select_options = cleanSelectOptions(select_options);
|
this.select_options = select_options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_optionTemplate(option : SelectOption) : TemplateResult
|
_optionTemplate(option : SelectOption) : TemplateResult
|
||||||
{
|
{
|
||||||
return html`
|
return html`
|
||||||
<sl-menu-item value="${option.value}" title="${option.title}">
|
<sl-option value="${option.value}" title="${option.title}">
|
||||||
${this.appIcons ? "" : option.label}
|
${this.appIcons ? "" : option.label}
|
||||||
${this._iconTemplate(option.value)}
|
${this._iconTemplate(option)}
|
||||||
</sl-menu-item>`;
|
</sl-option>`;
|
||||||
}
|
|
||||||
|
|
||||||
_iconTemplate(appname)
|
|
||||||
{
|
|
||||||
let url = appname ? this.egw().link_get_registry(appname, 'icon') : "";
|
|
||||||
return html`
|
|
||||||
<et2-image style="width: var(--icon-width)" slot="prefix" src="${url}"></et2-image>`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
* @link https://www.egroupware.org
|
* @link https://www.egroupware.org
|
||||||
* @author Nathan Gray
|
* @author Nathan Gray
|
||||||
*/
|
*/
|
||||||
|
import {css, html, LitElement, PropertyValues} from "lit";
|
||||||
import {css, html, LitElement, PropertyValues, SlotMixin} from "@lion/core";
|
import {SlotMixin} from "@lion/core";
|
||||||
import {Et2LinkAppSelect} from "./Et2LinkAppSelect";
|
import {Et2LinkAppSelect} from "./Et2LinkAppSelect";
|
||||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
||||||
import {FormControlMixin} from "@lion/form-core";
|
import {FormControlMixin} from "@lion/form-core";
|
||||||
|
@ -10,8 +10,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {css, html, repeat, TemplateResult} from "@lion/core";
|
import {css, html, TemplateResult} from "lit";
|
||||||
import {Et2Link, LinkInfo} from "./Et2Link";
|
import {repeat} from "lit/directives/repeat.js";
|
||||||
|
import {LinkInfo} from "./Et2Link";
|
||||||
import {egw} from "../../jsapi/egw_global";
|
import {egw} from "../../jsapi/egw_global";
|
||||||
import {Et2LinkString} from "./Et2LinkString";
|
import {Et2LinkString} from "./Et2LinkString";
|
||||||
import {egwMenu} from "../../egw_action/egw_menu";
|
import {egwMenu} from "../../egw_action/egw_menu";
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* @author Nathan Gray
|
* @author Nathan Gray
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {css} from "@lion/core";
|
import {css} from "lit";
|
||||||
import {Et2Select} from "../Et2Select/Et2Select";
|
import {Et2Select} from "../Et2Select/Et2Select";
|
||||||
import {Et2LinkAppSelect} from "./Et2LinkAppSelect";
|
import {Et2LinkAppSelect} from "./Et2LinkAppSelect";
|
||||||
import {Et2Link} from "./Et2Link";
|
import {Et2Link} from "./Et2Link";
|
||||||
@ -86,9 +86,9 @@ export class Et2LinkSearch extends Et2Select
|
|||||||
super.updated(changedProperties);
|
super.updated(changedProperties);
|
||||||
|
|
||||||
// Set a value we don't have as an option? That's OK, we'll just add it
|
// Set a value we don't have as an option? That's OK, we'll just add it
|
||||||
if(changedProperties.has("value") && this.value && (
|
if(changedProperties.has("value") && this.value && this.value.length > 0 && (
|
||||||
this.menuItems && this.menuItems.length == 0 ||
|
this.getAllOptions().length == 0 ||
|
||||||
this.menuItems?.filter && this.menuItems.filter(item => this.value.includes(item.value)).length == 0
|
this.getAllOptions().filter && this.getAllOptions().filter(item => this.getValueAsArray().includes(item.value)).length == 0
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
this._missingOption(this.value)
|
this._missingOption(this.value)
|
||||||
@ -120,19 +120,16 @@ export class Et2LinkSearch extends Et2Select
|
|||||||
option.label = title || Et2Link.MISSING_TITLE;
|
option.label = title || Et2Link.MISSING_TITLE;
|
||||||
option.class = "";
|
option.class = "";
|
||||||
// It's probably already been rendered, find the item
|
// It's probably already been rendered, find the item
|
||||||
let item = this.menuItems.find(i => i.value === option.value);
|
let item = this.getAllOptions().find(i => i.value === option.value);
|
||||||
if(item)
|
if(item)
|
||||||
{
|
{
|
||||||
item.textContent = title;
|
item.textContent = title;
|
||||||
item.classList.remove("loading");
|
item.classList.remove("loading");
|
||||||
this.syncItemsFromValue();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Not already rendered, update the select option
|
// Not already rendered, update the select option
|
||||||
this.requestUpdate("select_options");
|
this.requestUpdate("select_options");
|
||||||
// update the displayed text
|
|
||||||
this.updateComplete.then(() => this.syncItemsFromValue());
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {css, html, LitElement, PropertyValues, render, TemplateResult, until} from "@lion/core";
|
import {css, html, LitElement, PropertyValues, render, TemplateResult} from "lit";
|
||||||
|
import {until} from "lit/directives/until.js";
|
||||||
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
||||||
import {Et2Link, LinkInfo} from "./Et2Link";
|
import {Et2Link, LinkInfo} from "./Et2Link";
|
||||||
import {et2_IDetachedDOM} from "../et2_core_interfaces";
|
import {et2_IDetachedDOM} from "../et2_core_interfaces";
|
||||||
|
@ -12,7 +12,8 @@
|
|||||||
|
|
||||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
||||||
import {FormControlMixin, ValidateMixin} from "@lion/form-core";
|
import {FormControlMixin, ValidateMixin} from "@lion/form-core";
|
||||||
import {css, html, LitElement, ScopedElementsMixin} from "@lion/core";
|
import {css, html, LitElement} from "lit";
|
||||||
|
import {ScopedElementsMixin} from "@lion/core";
|
||||||
import {et2_createWidget, et2_widget} from "../et2_core_widget";
|
import {et2_createWidget, et2_widget} from "../et2_core_widget";
|
||||||
import {et2_file} from "../et2_widget_file";
|
import {et2_file} from "../et2_widget_file";
|
||||||
import {Et2Button} from "../Et2Button/Et2Button";
|
import {Et2Button} from "../Et2Button/Et2Button";
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* Column selector for nextmatch
|
* Column selector for nextmatch
|
||||||
*/
|
*/
|
||||||
import {classMap, css, html, LitElement, repeat, TemplateResult} from "@lion/core";
|
import {css, html, LitElement, TemplateResult} from "lit";
|
||||||
|
import {classMap} from "lit/directives/class-map.js";
|
||||||
|
import {repeat} from "lit/directives/repeat.js";
|
||||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
||||||
import {et2_nextmatch_customfields} from "../et2_extension_nextmatch";
|
import {et2_nextmatch_customfields} from "../et2_extension_nextmatch";
|
||||||
import shoelace from "../Styles/shoelace";
|
import shoelace from "../Styles/shoelace";
|
||||||
@ -85,7 +87,7 @@ export class Et2ColumnSelection extends Et2InputWidget(LitElement)
|
|||||||
{
|
{
|
||||||
this.sort = Sortable.create(this.shadowRoot.querySelector('sl-menu'), {
|
this.sort = Sortable.create(this.shadowRoot.querySelector('sl-menu'), {
|
||||||
ghostClass: 'ui-fav-sortable-placeholder',
|
ghostClass: 'ui-fav-sortable-placeholder',
|
||||||
draggable: 'sl-menu-item.column',
|
draggable: 'sl-option.column',
|
||||||
dataIdAttr: 'value',
|
dataIdAttr: 'value',
|
||||||
direction: 'vertical',
|
direction: 'vertical',
|
||||||
delay: 25
|
delay: 25
|
||||||
@ -141,10 +143,11 @@ export class Et2ColumnSelection extends Et2InputWidget(LitElement)
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<sl-menu-item
|
<sl-option
|
||||||
value="${column.id}"
|
value="${column.id}"
|
||||||
?checked=${alwaysOn || column.visibility == et2_dataview_column.ET2_COL_VISIBILITY_VISIBLE}
|
?checked=${alwaysOn || column.visibility == et2_dataview_column.ET2_COL_VISIBILITY_VISIBLE}
|
||||||
?disabled=${alwaysOn}
|
?disabled=${alwaysOn}
|
||||||
|
.selected="${this.value.some(v => v == column.id)}"
|
||||||
title="${column.title}"
|
title="${column.title}"
|
||||||
class="${classMap({
|
class="${classMap({
|
||||||
select_row: true,
|
select_row: true,
|
||||||
@ -154,7 +157,7 @@ export class Et2ColumnSelection extends Et2InputWidget(LitElement)
|
|||||||
${column.caption}
|
${column.caption}
|
||||||
<!-- Custom fields get listed separately -->
|
<!-- Custom fields get listed separately -->
|
||||||
${isCustom ? this.customFieldsTemplate(column) : ''}
|
${isCustom ? this.customFieldsTemplate(column) : ''}
|
||||||
</sl-menu-item>`;
|
</sl-option>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -198,8 +201,8 @@ export class Et2ColumnSelection extends Et2InputWidget(LitElement)
|
|||||||
|
|
||||||
handleSelectAll(event)
|
handleSelectAll(event)
|
||||||
{
|
{
|
||||||
let checked = (<SlMenuItem>this.shadowRoot.querySelector("sl-menu-item")).checked || false;
|
let checked = (<SlMenuItem>this.shadowRoot.querySelector("sl-option")).checked || false;
|
||||||
this.shadowRoot.querySelectorAll('sl-menu-item').forEach((item) => {item.checked = !checked});
|
this.shadowRoot.querySelectorAll('sl-option').forEach((item) => {item.checked = !checked});
|
||||||
}
|
}
|
||||||
|
|
||||||
set columns(new_columns)
|
set columns(new_columns)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {Et2SelectAccount} from "../../Et2Select/Et2SelectAccount";
|
import {Et2SelectAccount} from "../../Et2Select/Select/Et2SelectAccount";
|
||||||
import {et2_INextmatchHeader} from "../../et2_extension_nextmatch";
|
import {et2_INextmatchHeader} from "../../et2_extension_nextmatch";
|
||||||
import {FilterMixin} from "./FilterMixin";
|
import {FilterMixin} from "./FilterMixin";
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import {loadWebComponent} from "../../Et2Widget/Et2Widget";
|
|||||||
import {Et2Select} from "../../Et2Select/Et2Select";
|
import {Et2Select} from "../../Et2Select/Et2Select";
|
||||||
import {Et2InputWidget, Et2InputWidgetInterface} from "../../Et2InputWidget/Et2InputWidget";
|
import {Et2InputWidget, Et2InputWidgetInterface} from "../../Et2InputWidget/Et2InputWidget";
|
||||||
import {FilterMixin} from "./FilterMixin";
|
import {FilterMixin} from "./FilterMixin";
|
||||||
import {html, LitElement} from "@lion/core";
|
import {html, LitElement} from "lit";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter by some other type of widget
|
* Filter by some other type of widget
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {egw} from "../../../jsapi/egw_global";
|
import {egw} from "../../../jsapi/egw_global";
|
||||||
import {et2_INextmatchHeader, et2_nextmatch} from "../../et2_extension_nextmatch";
|
import {et2_INextmatchHeader, et2_nextmatch} from "../../et2_extension_nextmatch";
|
||||||
import {LitElement} from "@lion/core";
|
import {LitElement} from "lit";
|
||||||
|
|
||||||
// Export the Interface for TypeScript
|
// Export the Interface for TypeScript
|
||||||
type Constructor<T = LitElement> = new (...args : any[]) => T;
|
type Constructor<T = LitElement> = new (...args : any[]) => T;
|
||||||
|
@ -15,8 +15,9 @@ import {SlCard} from "@shoelace-style/shoelace";
|
|||||||
import interact from "@interactjs/interactjs";
|
import interact from "@interactjs/interactjs";
|
||||||
import type {InteractEvent} from "@interactjs/core/InteractEvent";
|
import type {InteractEvent} from "@interactjs/core/InteractEvent";
|
||||||
import {egw} from "../../jsapi/egw_global";
|
import {egw} from "../../jsapi/egw_global";
|
||||||
import {classMap, css, html, TemplateResult} from "@lion/core";
|
import {css, html, TemplateResult} from "lit";
|
||||||
import {HasSlotController} from "@shoelace-style/shoelace/dist/internal/slot";
|
import {classMap} from "lit/directives/class-map.js";
|
||||||
|
import type {HasSlotController} from "../../../../node_modules/@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";
|
||||||
import {et2_IResizeable} from "../et2_core_interfaces";
|
import {et2_IResizeable} from "../et2_core_interfaces";
|
||||||
|
@ -2,7 +2,7 @@ import {SlMenu} from "@shoelace-style/shoelace";
|
|||||||
import {Et2WidgetWithSelectMixin} from "./Et2WidgetWithSelectMixin";
|
import {Et2WidgetWithSelectMixin} from "./Et2WidgetWithSelectMixin";
|
||||||
import {RowLimitedMixin} from "../Layout/RowLimitedMixin";
|
import {RowLimitedMixin} from "../Layout/RowLimitedMixin";
|
||||||
import shoelace from "../Styles/shoelace";
|
import shoelace from "../Styles/shoelace";
|
||||||
import {css, html, TemplateResult} from "@lion/core";
|
import {css, html, TemplateResult} from "lit";
|
||||||
import {SelectOption} from "./FindSelectOptions";
|
import {SelectOption} from "./FindSelectOptions";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,7 +40,8 @@ export class Et2Listbox extends RowLimitedMixin(Et2WidgetWithSelectMixin(SlMenu)
|
|||||||
overflow-x: clip;
|
overflow-x: clip;
|
||||||
}
|
}
|
||||||
/* Ellipsis when too small */
|
/* Ellipsis when too small */
|
||||||
sl-menu-item.menu-item__label {
|
|
||||||
|
sl-option.option__label {
|
||||||
display: block;
|
display: block;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
/* This is usually not used due to flex, but is the basis for ellipsis calculation */
|
/* This is usually not used due to flex, but is the basis for ellipsis calculation */
|
||||||
@ -153,15 +154,15 @@ export class Et2Listbox extends RowLimitedMixin(Et2WidgetWithSelectMixin(SlMenu)
|
|||||||
// Tag used must match this.optionTag, but you can't use the variable directly.
|
// Tag used must match this.optionTag, but you can't use the variable directly.
|
||||||
// Pass option along so SearchMixin can grab it if needed
|
// Pass option along so SearchMixin can grab it if needed
|
||||||
return html`
|
return html`
|
||||||
<sl-menu-item
|
<sl-option
|
||||||
value="${option.value}"
|
value="${option.value}"
|
||||||
title="${!option.title || this.noLang ? option.title : this.egw().lang(option.title)}"
|
title="${!option.title || this.noLang ? option.title : this.egw().lang(option.title)}"
|
||||||
class="${option.class}" .option=${option}
|
class="${option.class}" .option=${option}
|
||||||
?checked=${checked}
|
.selected=${checked}
|
||||||
>
|
>
|
||||||
${icon}
|
${icon}
|
||||||
${this.noLang ? option.label : this.egw().lang(option.label)}
|
${this.noLang ? option.label : this.egw().lang(option.label)}
|
||||||
</sl-menu-item>`;
|
</sl-option>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Et2InputWidget, Et2InputWidgetInterface} from "../Et2InputWidget/Et2InputWidget";
|
import {Et2InputWidget, Et2InputWidgetInterface} from "../Et2InputWidget/Et2InputWidget";
|
||||||
import {html, LitElement, PropertyValues, render, TemplateResult} from "@lion/core";
|
import {html, LitElement, PropertyValues, render, TemplateResult} from "lit";
|
||||||
|
import {property} from "lit/decorators/property.js";
|
||||||
import {et2_readAttrWithDefault} from "../et2_core_xml";
|
import {et2_readAttrWithDefault} from "../et2_core_xml";
|
||||||
import {cleanSelectOptions, find_select_options, SelectOption} from "./FindSelectOptions";
|
import {cleanSelectOptions, find_select_options, SelectOption} from "./FindSelectOptions";
|
||||||
import {SearchMixinInterface} from "./SearchMixin";
|
import {SearchMixinInterface} from "./SearchMixin";
|
||||||
@ -17,7 +18,7 @@ import {SearchMixinInterface} from "./SearchMixin";
|
|||||||
* Base class for things that do selectbox type behaviour, to avoid putting too much or copying into read-only
|
* Base class for things that do selectbox type behaviour, to avoid putting too much or copying into read-only
|
||||||
* selectboxes, also for common handling of properties for more special selectboxes.
|
* selectboxes, also for common handling of properties for more special selectboxes.
|
||||||
*
|
*
|
||||||
* As with most other widgets that extend Lion components, do not override render().
|
* As with most other widgets that extend Shoelace components, do not override render() without good reason.
|
||||||
* To extend this mixin, override:
|
* To extend this mixin, override:
|
||||||
* - _optionTargetNode(): Return the HTMLElement where the "options" go.
|
* - _optionTargetNode(): Return the HTMLElement where the "options" go.
|
||||||
* - _optionTemplate(option:SelectOption): Renders the option. To use a special widget, use its tag in render.
|
* - _optionTemplate(option:SelectOption): Renders the option. To use a special widget, use its tag in render.
|
||||||
@ -46,15 +47,6 @@ import {SearchMixinInterface} from "./SearchMixin";
|
|||||||
* You can specify something else, or return {} to do your own thing. This is a little more complicated. You should
|
* You can specify something else, or return {} to do your own thing. This is a little more complicated. You should
|
||||||
* also override _inputGroupInputTemplate() to do what you normally would in render().
|
* also override _inputGroupInputTemplate() to do what you normally would in render().
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* Technical note:
|
|
||||||
* LionSelect (and any other LionField) use slots to wrap a real DOM node. ET2 doesn't expect this,
|
|
||||||
* so we have to create the input node (via slots()) and respect that it is _external_ to the Web Component.
|
|
||||||
* This complicates things like adding the options, since we can't just override _inputGroupInputTemplate()
|
|
||||||
* and include them when rendering - the parent expects to find the <select> added via a slot, render() would
|
|
||||||
* put it inside the shadowDOM. That's fine, but then it doesn't get created until render(), and the parent
|
|
||||||
* (LionField) can't find it when it looks for it before then.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
// Export the Interface for TypeScript
|
// Export the Interface for TypeScript
|
||||||
type Constructor<T = {}> = new (...args : any[]) => T;
|
type Constructor<T = {}> = new (...args : any[]) => T;
|
||||||
@ -63,29 +55,46 @@ export const Et2WidgetWithSelectMixin = <T extends Constructor<LitElement>>(supe
|
|||||||
{
|
{
|
||||||
class Et2WidgetWithSelect extends Et2InputWidget(superclass)
|
class Et2WidgetWithSelect extends Et2InputWidget(superclass)
|
||||||
{
|
{
|
||||||
static get properties()
|
/**
|
||||||
{
|
* The current value of the select, submitted as a name/value pair with form data. When `multiple` is enabled, the
|
||||||
return {
|
* value attribute will be a space-delimited list of values based on the options selected, and the value property will
|
||||||
...super.properties,
|
* be an array.
|
||||||
/**
|
*
|
||||||
* Textual label for first row, eg: 'All' or 'None'. It's value will be ''
|
@property({
|
||||||
*/
|
noAccessor: true,
|
||||||
emptyLabel: String,
|
converter: {
|
||||||
|
fromAttribute: (value : string) => value.split(',')
|
||||||
/**
|
|
||||||
* Select box options
|
|
||||||
*
|
|
||||||
* Will be found automatically based on ID and type, or can be set explicitly in the template using
|
|
||||||
* <option/> children, or using widget.select_options = SelectOption[]
|
|
||||||
*/
|
|
||||||
select_options: {type: Object, noAccessor: true},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Limit size
|
|
||||||
*/
|
|
||||||
rows: {type: Number, noAccessor: true, reflect: true}
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
value : string | string[] = "";
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Textual label for first row, eg: 'All' or 'None'. It's value will be ''
|
||||||
|
*/
|
||||||
|
@property({type: String})
|
||||||
|
emptyLabel : String = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Limit size
|
||||||
|
*/
|
||||||
|
@property({type: Number, noAccessor: true, reflect: true})
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal list of possible select options
|
||||||
|
*
|
||||||
|
* This is where we keep options sent from the server. This is not always the complete list, as extending
|
||||||
|
* classes may have their own options to add in. For example, static options are kept separate, as are search
|
||||||
|
* results. The select_options getter should give the complete list.
|
||||||
|
*/
|
||||||
|
private __select_options : SelectOption[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When we create the select option elements, it takes a while.
|
||||||
|
* If we don't wait for them, it causes issues in SlSelect
|
||||||
|
*/
|
||||||
|
protected _optionRenderPromise : Promise<void> = Promise.resolve();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options found in the XML when reading the template
|
* Options found in the XML when reading the template
|
||||||
@ -101,6 +110,13 @@ export const Et2WidgetWithSelectMixin = <T extends Constructor<LitElement>>(supe
|
|||||||
this.__select_options = <SelectOption[]>[];
|
this.__select_options = <SelectOption[]>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getUpdateComplete() : Promise<boolean>
|
||||||
|
{
|
||||||
|
const result = await super.getUpdateComplete();
|
||||||
|
await this._optionRenderPromise;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/** @param {import('@lion/core').PropertyValues } changedProperties */
|
/** @param {import('@lion/core').PropertyValues } changedProperties */
|
||||||
updated(changedProperties : PropertyValues)
|
updated(changedProperties : PropertyValues)
|
||||||
{
|
{
|
||||||
@ -116,26 +132,48 @@ export const Et2WidgetWithSelectMixin = <T extends Constructor<LitElement>>(supe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
willUpdate(changedProperties : PropertyValues<this>)
|
||||||
|
{
|
||||||
// Add in actual option tags to the DOM based on the new select_options
|
// Add in actual option tags to the DOM based on the new select_options
|
||||||
if(changedProperties.has('select_options') || changedProperties.has("emptyLabel"))
|
if(changedProperties.has('select_options') || changedProperties.has("emptyLabel"))
|
||||||
{
|
{
|
||||||
// Add in options as children to the target node
|
// Add in options as children to the target node
|
||||||
this._renderOptions();
|
const optionPromise = this._renderOptions();
|
||||||
|
|
||||||
// This is needed to display initial load value in some cases, like infolog nm header filters
|
// This is needed to display initial load value in some cases, like infolog nm header filters
|
||||||
if(this.handleMenuSlotChange && !this.hasUpdated)
|
if(typeof this.selectionChanged !== "undefined")
|
||||||
{
|
{
|
||||||
this.handleMenuSlotChange();
|
optionPromise.then(async() =>
|
||||||
|
{
|
||||||
|
await this.updateComplete;
|
||||||
|
this.selectionChanged();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getValueAsArray()
|
||||||
|
{
|
||||||
|
if(Array.isArray(this.value))
|
||||||
|
{
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
if(this.value == "null" || typeof this.value == "undefined" || !this.emptyLabel && this.value == "")
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return [this.value];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render select_options as child DOM Nodes
|
* Render select_options as child DOM Nodes
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected _renderOptions()
|
protected _renderOptions()
|
||||||
{
|
{
|
||||||
|
return Promise.resolve();
|
||||||
// Add in options as children to the target node
|
// Add in options as children to the target node
|
||||||
if(!this._optionTargetNode)
|
if(!this._optionTargetNode)
|
||||||
{
|
{
|
||||||
@ -156,7 +194,7 @@ export const Et2WidgetWithSelectMixin = <T extends Constructor<LitElement>>(supe
|
|||||||
.map(this._groupTemplate.bind(this))}`;
|
.map(this._groupTemplate.bind(this))}`;
|
||||||
|
|
||||||
render(options, temp_target);
|
render(options, temp_target);
|
||||||
return Promise.all(([...temp_target.querySelectorAll(":scope > *")].map(item => item.render)))
|
this._optionRenderPromise = Promise.all(([...temp_target.querySelectorAll(":scope > *")].map(item => item.render)))
|
||||||
.then(() =>
|
.then(() =>
|
||||||
{
|
{
|
||||||
this._optionTargetNode.replaceChildren(
|
this._optionTargetNode.replaceChildren(
|
||||||
@ -168,23 +206,7 @@ export const Et2WidgetWithSelectMixin = <T extends Constructor<LitElement>>(supe
|
|||||||
this.handleMenuSlotChange();
|
this.handleMenuSlotChange();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return this._optionRenderPromise;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overwritten as sometimes called before this._inputNode is available
|
|
||||||
*
|
|
||||||
* @param {*} v - modelValue: can be an Object, Number, String depending on the
|
|
||||||
* input type(date, number, email etc)
|
|
||||||
* @returns {string} formattedValue
|
|
||||||
*/
|
|
||||||
formatter(v)
|
|
||||||
{
|
|
||||||
if (!this._inputNode)
|
|
||||||
{
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
return super.formatter(v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -212,6 +234,13 @@ export const Et2WidgetWithSelectMixin = <T extends Constructor<LitElement>>(supe
|
|||||||
this.select_options = <SelectOption[]>new_options;
|
this.select_options = <SelectOption[]>new_options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select box options
|
||||||
|
*
|
||||||
|
* Will be found automatically based on ID and type, or can be set explicitly in the template using
|
||||||
|
* <option/> children, or using widget.select_options = SelectOption[]
|
||||||
|
*/
|
||||||
|
@property({type: Object})
|
||||||
get select_options() : SelectOption[]
|
get select_options() : SelectOption[]
|
||||||
{
|
{
|
||||||
return this.__select_options;
|
return this.__select_options;
|
||||||
@ -262,7 +291,7 @@ export const Et2WidgetWithSelectMixin = <T extends Constructor<LitElement>>(supe
|
|||||||
* @param {SelectOption} option
|
* @param {SelectOption} option
|
||||||
* @returns {TemplateResult}
|
* @returns {TemplateResult}
|
||||||
*/
|
*/
|
||||||
_optionTemplate(option : SelectOption) : TemplateResult
|
protected _optionTemplate(option : SelectOption) : TemplateResult
|
||||||
{
|
{
|
||||||
return html`
|
return html`
|
||||||
<span>Override _optionTemplate(). ${option.value} => ${option.label}</span>`;
|
<span>Override _optionTemplate(). ${option.value} => ${option.label}</span>`;
|
||||||
@ -276,7 +305,7 @@ export const Et2WidgetWithSelectMixin = <T extends Constructor<LitElement>>(supe
|
|||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
|
|
||||||
<sl-menu-label>${this.noLang ? option.label : this.egw().lang(option.label)}</sl-menu-label>
|
<small>${this.noLang ? option.label : this.egw().lang(option.label)}</small>
|
||||||
${option.value.map(this._optionTemplate.bind(this))}
|
${option.value.map(this._optionTemplate.bind(this))}
|
||||||
<sl-divider></sl-divider>
|
<sl-divider></sl-divider>
|
||||||
`;
|
`;
|
||||||
|
@ -18,6 +18,9 @@ export interface SelectOption
|
|||||||
// Show the option, but it is not selectable.
|
// Show the option, but it is not selectable.
|
||||||
// If multiple=true and the option is in the value, it is not removable.
|
// If multiple=true and the option is in the value, it is not removable.
|
||||||
disabled? : boolean;
|
disabled? : boolean;
|
||||||
|
// If a search is in progress, does this option match.
|
||||||
|
// Automatically changed.
|
||||||
|
isMatch? : boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
1181
api/js/etemplate/Et2Select/SearchMixin.js
Normal file
1181
api/js/etemplate/Et2Select/SearchMixin.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -7,14 +7,13 @@
|
|||||||
* @author Nathan Gray
|
* @author Nathan Gray
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {css, CSSResultGroup, html, LitElement, nothing, render, TemplateResult} from "lit";
|
||||||
import {css, html, LitElement, render, SlotMixin} from "@lion/core";
|
|
||||||
import {cleanSelectOptions, SelectOption} from "./FindSelectOptions";
|
import {cleanSelectOptions, SelectOption} from "./FindSelectOptions";
|
||||||
import {Validator} from "@lion/form-core";
|
import {Validator} from "@lion/form-core";
|
||||||
import {Et2Tag} from "./Tag/Et2Tag";
|
import {Et2Tag} from "./Tag/Et2Tag";
|
||||||
import {SlMenuItem} from "@shoelace-style/shoelace";
|
import {SlMenuItem} from "@shoelace-style/shoelace";
|
||||||
import {waitForEvent} from "@shoelace-style/shoelace/dist/internal/event";
|
|
||||||
import {StaticOptions} from "./StaticOptions";
|
import {StaticOptions} from "./StaticOptions";
|
||||||
|
import {dedupeMixin} from "@open-wc/dedupe-mixin";
|
||||||
|
|
||||||
// Otherwise import gets stripped
|
// Otherwise import gets stripped
|
||||||
let keep_import : Et2Tag;
|
let keep_import : Et2Tag;
|
||||||
@ -66,6 +65,13 @@ export declare class SearchMixinInterface
|
|||||||
* Check a [local] item to see if it matches
|
* Check a [local] item to see if it matches
|
||||||
*/
|
*/
|
||||||
searchMatch(search : string, options : object, item : LitElement) : boolean
|
searchMatch(search : string, options : object, item : LitElement) : boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Additional customisation location, where we stick the search elements
|
||||||
|
*
|
||||||
|
* @type {TemplateResult}
|
||||||
|
*/
|
||||||
|
_extraTemplate : TemplateResult
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,9 +80,9 @@ export declare class SearchMixinInterface
|
|||||||
*
|
*
|
||||||
* Currently I assume we're extending an Et2Select, so changes may need to be made for better abstraction
|
* Currently I assume we're extending an Et2Select, so changes may need to be made for better abstraction
|
||||||
*/
|
*/
|
||||||
export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass : T) =>
|
export const Et2WithSearchMixin = dedupeMixin(<T extends Constructor<LitElement>>(superclass : T) =>
|
||||||
{
|
{
|
||||||
class Et2WidgetWithSearch extends SlotMixin(superclass)
|
class Et2WidgetWithSearch extends superclass
|
||||||
{
|
{
|
||||||
static get properties()
|
static get properties()
|
||||||
{
|
{
|
||||||
@ -105,54 +111,18 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles()
|
static get styles() : CSSResultGroup
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
...(super.styles ? (Symbol.iterator in Object(super.styles) ? super.styles : [super.styles]) : []),
|
...(super.styles ? (Symbol.iterator in Object(super.styles) ? super.styles : [super.styles]) : []),
|
||||||
css`
|
css`
|
||||||
/* Move the widget border
|
|
||||||
.form-control-input {
|
|
||||||
border: solid var(--sl-input-border-width) var(--sl-input-border-color);
|
|
||||||
border-radius: var(--sl-input-border-radius-medium);
|
|
||||||
}
|
|
||||||
.form-control-input:hover {
|
|
||||||
background-color: var(--sl-input-background-color-hover);
|
|
||||||
border-color: var(--sl-input-border-color-hover);
|
|
||||||
color: var(--sl-input-color-hover);
|
|
||||||
}
|
|
||||||
.select--standard .select__control {
|
|
||||||
border-style: none;
|
|
||||||
}
|
|
||||||
/* Move focus highlight */
|
|
||||||
.form-control-input:focus-within {
|
|
||||||
box-shadow: var(--sl-focus-ring);
|
|
||||||
}
|
|
||||||
.select--standard.select--focused:not(.select--disabled) .select__control {
|
|
||||||
box-shadow: initial;
|
|
||||||
}
|
|
||||||
/* Show / hide SlSelect icons - dropdown arrow, etc but not loading spinner */
|
|
||||||
:host([allowFreeEntries]) ::slotted(sl-icon[slot="suffix"]) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
/* Make search textbox take full width */
|
|
||||||
::slotted(.search_input), ::slotted(.search_input) input, .search_input, .search_input input {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.search_input input {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
/* Full width search textbox covers loading spinner, lift it up */
|
/* Full width search textbox covers loading spinner, lift it up */
|
||||||
::slotted(sl-spinner) {
|
::slotted(sl-spinner) {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
/* Don't show the current value while searching for single, we want the space
|
|
||||||
This lets the current value shrink to nothing so the input can expand
|
|
||||||
*/
|
|
||||||
.select__label {
|
|
||||||
flex: 1 15 auto;
|
|
||||||
}
|
|
||||||
/* Show edit textbox only when editing */
|
/* Show edit textbox only when editing */
|
||||||
.search_input #edit {
|
.search_input #edit {
|
||||||
display: none;
|
display: none;
|
||||||
@ -163,36 +133,55 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
.search_input.editing #edit {
|
.search_input.editing #edit {
|
||||||
display: initial;
|
display: initial;
|
||||||
}
|
}
|
||||||
:host([search]:not([multiple])) .select--open .select__prefix {
|
|
||||||
|
|
||||||
|
:host([search]) sl-select[open]::part(prefix), :host([allowfreeentries]) sl-select[open]::part(prefix) {
|
||||||
|
order: 9;
|
||||||
flex: 2 1 auto;
|
flex: 2 1 auto;
|
||||||
|
flex-wrap: wrap;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
:host([search]:not([multiple])) .select--open .select__label {
|
|
||||||
margin: 0px;
|
:host([search]) sl-select[open]::part(display-input), :host([allowfreeentries]) sl-select[open]::part(display-input) {
|
||||||
}
|
|
||||||
:host([allowfreeentries]:not([multiple])) .select--standard.select--open:not(.select--disabled) .select__control .select__prefix {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
}
|
|
||||||
:host([allowfreeentries]:not([multiple])) .select--standard.select--open:not(.select--disabled) .select__control .select__label {
|
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:host([search][multiple]) sl-select[open]::part(expand-icon) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([multiple]) sl-select[open]::part(tags) {
|
||||||
|
flex-basis: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([multiple]) sl-select[open]::part(combobox) {
|
||||||
|
flex-flow: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Search textbox general styling, starts hidden */
|
/* Search textbox general styling, starts hidden */
|
||||||
|
|
||||||
.select__prefix ::slotted(.search_input), .search_input {
|
.search_input {
|
||||||
display: none;
|
display: none;
|
||||||
|
/* See also etemplate2.css, searchbox border turned off in there */
|
||||||
|
border: none;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
order: 2;
|
||||||
margin-left: 0px;
|
margin-left: 0px;
|
||||||
width: 100%;
|
|
||||||
height: var(--sl-input-height-medium);
|
height: var(--sl-input-height-medium);
|
||||||
position: absolute;
|
width: 100%;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
z-index: var(--sl-z-index-dropdown);
|
z-index: var(--sl-z-index-dropdown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:host([search]) et2-textbox::part(base) {
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Search UI active - show textbox & stuff */
|
/* Search UI active - show textbox & stuff */
|
||||||
|
|
||||||
::slotted(.search_input.active), .search_input.active,
|
.search_input.active,
|
||||||
.search_input.editing {
|
.search_input.editing {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
@ -204,7 +193,8 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Hide options that do not match current search text */
|
/* Hide options that do not match current search text */
|
||||||
::slotted(.no-match) {
|
|
||||||
|
.no-match {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
/* Different cursor for editable tags */
|
/* Different cursor for editable tags */
|
||||||
@ -221,10 +211,6 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
:host([readonly]) .form-control-input:focus-within {
|
:host([readonly]) .form-control-input:focus-within {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
/* no menu */
|
|
||||||
:host([readonly]) sl-menu {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
/* normal cursor */
|
/* normal cursor */
|
||||||
:host([readonly]) .select__control {
|
:host([readonly]) .select__control {
|
||||||
cursor: initial;
|
cursor: initial;
|
||||||
@ -264,6 +250,11 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
// Hold the original option data from earlier search results, since we discard on subsequent search
|
// Hold the original option data from earlier search results, since we discard on subsequent search
|
||||||
private _selected_remote = <SelectOption[]>[];
|
private _selected_remote = <SelectOption[]>[];
|
||||||
|
|
||||||
|
// Hold current search results, selected or otherwise
|
||||||
|
private _remote_options = <SelectOption[]>[];
|
||||||
|
|
||||||
|
private _total_result_count = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These characters will end a free tag
|
* These characters will end a free tag
|
||||||
* @type {string[]}
|
* @type {string[]}
|
||||||
@ -283,7 +274,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
|
|
||||||
// Hiding the selected options from the dropdown means we can't un-select the tags
|
// Hiding the selected options from the dropdown means we can't un-select the tags
|
||||||
// hidden by the max limit. Prefer no limit.
|
// hidden by the max limit. Prefer no limit.
|
||||||
this.maxTagsVisible = -1;
|
this.maxOptionsVisible = -1;
|
||||||
|
|
||||||
this.validators = [];
|
this.validators = [];
|
||||||
/**
|
/**
|
||||||
@ -297,16 +288,19 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
*/
|
*/
|
||||||
this.defaultValidators = [];
|
this.defaultValidators = [];
|
||||||
|
|
||||||
this.handleMenuSelect = this.handleMenuSelect.bind(this);
|
this.handleOptionClick = this.handleOptionClick.bind(this);
|
||||||
this._handleChange = this._handleChange.bind(this);
|
this._handleChange = this._handleChange.bind(this);
|
||||||
this.handleTagEdit = this.handleTagEdit.bind(this);
|
this.handleTagEdit = this.handleTagEdit.bind(this);
|
||||||
this._handleAfterShow = this._handleAfterShow.bind(this);
|
this._handleAfterShow = this._handleAfterShow.bind(this);
|
||||||
|
this._handleMenuHide = this._handleMenuHide.bind(this);
|
||||||
this._handleSearchBlur = this._handleSearchBlur.bind(this);
|
this._handleSearchBlur = this._handleSearchBlur.bind(this);
|
||||||
this._handleClear = this._handleClear.bind(this);
|
this._handleClear = this._handleClear.bind(this);
|
||||||
this._handleDoubleClick = this._handleDoubleClick.bind(this);
|
this._handleDoubleClick = this._handleDoubleClick.bind(this);
|
||||||
this._handleSearchAbort = this._handleSearchAbort.bind(this);
|
this._handleSearchAbort = this._handleSearchAbort.bind(this);
|
||||||
|
this._handleSearchClear = this._handleSearchClear.bind(this);
|
||||||
this._handleSearchChange = this._handleSearchChange.bind(this);
|
this._handleSearchChange = this._handleSearchChange.bind(this);
|
||||||
this._handleSearchKeyDown = this._handleSearchKeyDown.bind(this);
|
this._handleSearchKeyDown = this._handleSearchKeyDown.bind(this);
|
||||||
|
this._handleSearchMouseDown = this._handleSearchMouseDown.bind(this);
|
||||||
this._handleEditKeyDown = this._handleEditKeyDown.bind(this);
|
this._handleEditKeyDown = this._handleEditKeyDown.bind(this);
|
||||||
this._handlePaste = this._handlePaste.bind(this);
|
this._handlePaste = this._handlePaste.bind(this);
|
||||||
}
|
}
|
||||||
@ -324,7 +318,6 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._addNodes();
|
|
||||||
this._bindListeners();
|
this._bindListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,7 +356,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
}
|
}
|
||||||
else if(this.allowFreeEntries && this.multiple)
|
else if(this.allowFreeEntries && this.multiple)
|
||||||
{
|
{
|
||||||
this.value.forEach((e) =>
|
this.getValueAsArray().forEach((e) =>
|
||||||
{
|
{
|
||||||
if(!this.select_options.find(o => o.value == e))
|
if(!this.select_options.find(o => o.value == e))
|
||||||
{
|
{
|
||||||
@ -402,7 +395,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Normally this should be handled in render(), but we have to add our nodes in
|
// Normally this should be handled in render(), but we have to add our nodes in
|
||||||
this._addNodes();
|
//this._addNodes();
|
||||||
}
|
}
|
||||||
// Update any tags if edit mode changes
|
// Update any tags if edit mode changes
|
||||||
if(changedProperties.has("editModeEnabled") || changedProperties.has("readonly"))
|
if(changedProperties.has("editModeEnabled") || changedProperties.has("readonly"))
|
||||||
@ -416,54 +409,33 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected _extraTemplate()
|
||||||
* Add the nodes we need to search - adjust parent shadowDOM
|
|
||||||
*
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
protected _addNodes()
|
|
||||||
{
|
{
|
||||||
if(this._activeControls)
|
if(!this.searchEnabled && !this.editModeEnabled && !this.allowFreeEntries || this.readonly)
|
||||||
{
|
{
|
||||||
// Already there
|
return nothing;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const div = document.createElement("div");
|
return html`
|
||||||
div.classList.add("search_input");
|
${this._searchInputTemplate()}
|
||||||
render(this._searchInputTemplate(), div);
|
${this._moreResultsTemplate()}
|
||||||
if(!super.multiple)
|
`;
|
||||||
{
|
|
||||||
div.slot = "prefix";
|
|
||||||
this.appendChild(div);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
super.updateComplete.then(() =>
|
|
||||||
{
|
|
||||||
let control = this.shadowRoot.querySelector(".form-control-input");
|
|
||||||
control.append(div);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected _moreResultsTemplate()
|
||||||
* Customise how tags are rendered.
|
|
||||||
* Override to add edit
|
|
||||||
*
|
|
||||||
* @param item
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
protected _createTagNode(item)
|
|
||||||
{
|
{
|
||||||
let tag = <Et2Tag>document.createElement(this.tagTag);
|
if(this._total_result_count == 0 || this._total_result_count - this._remote_options.length == 0)
|
||||||
tag.editable = this.editModeEnabled && !this.readonly;
|
{
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
const more = this.egw().lang("%1 more...", this._total_result_count - this._remote_options.length);
|
||||||
|
|
||||||
return tag;
|
return html`<span class="more">${more}</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _searchInputTemplate()
|
protected _searchInputTemplate()
|
||||||
{
|
{
|
||||||
let edit = null;
|
let edit = nothing;
|
||||||
if(this.editModeEnabled)
|
if(this.editModeEnabled)
|
||||||
{
|
{
|
||||||
edit = html`<input id="edit" type="text" part="input" autocomplete="off" style="width:100%"
|
edit = html`<input id="edit" type="text" part="input" autocomplete="off" style="width:100%"
|
||||||
@ -472,16 +444,20 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
@blur=${this.stopEdit.bind(this)}
|
@blur=${this.stopEdit.bind(this)}
|
||||||
/>`;
|
/>`;
|
||||||
}
|
}
|
||||||
// I can't figure out how to get this full width via CSS
|
|
||||||
return html`
|
return html`
|
||||||
<et2-textbox id="search" type="text" part="input" clearable
|
<div class="search_input" slot="prefix">
|
||||||
|
<et2-textbox id="search" type="text" part="input"
|
||||||
|
exportparts="base:search__base"
|
||||||
|
clearable
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
placeholder="${this.egw().lang("search")}"
|
placeholder="${this.egw().lang("search")}"
|
||||||
style="width:100%"
|
style="flex: 1 1 auto;"
|
||||||
@keydown=${this._handleSearchKeyDown}
|
@keydown=${this._handleSearchKeyDown}
|
||||||
@blur=${this._handleSearchBlur}
|
@blur=${this._handleSearchBlur}
|
||||||
|
@sl-clear=${this._handleSearchClear}
|
||||||
></et2-textbox>
|
></et2-textbox>
|
||||||
${edit}
|
${edit}
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,6 +493,10 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
this.querySelector(".search_input");
|
this.querySelector(".search_input");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected get optionTag()
|
||||||
|
{
|
||||||
|
return 'sl-option';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only local options, excludes server options
|
* Only local options, excludes server options
|
||||||
@ -525,7 +505,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
*/
|
*/
|
||||||
protected get localItems() : NodeList
|
protected get localItems() : NodeList
|
||||||
{
|
{
|
||||||
return this.querySelectorAll(this.optionTag + ":not(.remote)");
|
return this.select.querySelectorAll(this.optionTag + ":not(.remote)");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -535,7 +515,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
*/
|
*/
|
||||||
protected get remoteItems() : NodeList
|
protected get remoteItems() : NodeList
|
||||||
{
|
{
|
||||||
return this.querySelectorAll(this.optionTag + ".remote");
|
return this.select?.querySelectorAll(this.optionTag + ".remote") ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -545,7 +525,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
*/
|
*/
|
||||||
protected get freeEntries() : NodeList
|
protected get freeEntries() : NodeList
|
||||||
{
|
{
|
||||||
return this.querySelectorAll(this.optionTag + ".freeEntry");
|
return this.select?.querySelectorAll(this.optionTag + ".freeEntry") ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
get select_options() : SelectOption[]
|
get select_options() : SelectOption[]
|
||||||
@ -558,6 +538,9 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
// Any kept remote options
|
// Any kept remote options
|
||||||
options = options.concat(this._selected_remote ?? []);
|
options = options.concat(this._selected_remote ?? []);
|
||||||
|
|
||||||
|
// Current search results
|
||||||
|
options = options.concat(this._remote_options ?? []);
|
||||||
|
|
||||||
if(this.allowFreeEntries)
|
if(this.allowFreeEntries)
|
||||||
{
|
{
|
||||||
this.freeEntries.forEach((item : SlMenuItem) =>
|
this.freeEntries.forEach((item : SlMenuItem) =>
|
||||||
@ -600,11 +583,11 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If widget is currently open, we may need to re-calculate search / dropdown positioning
|
// If widget is currently open, we may need to re-calculate search / dropdown positioning
|
||||||
if(this.isOpen)
|
if(this.isOpen)
|
||||||
{
|
{
|
||||||
this.handleMenuShow();
|
this._handleMenuShow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -641,7 +624,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
const valueArray = Array.isArray(this.value) ? this.value : (!this.value ? [] : this.value.toString().split(','));
|
const valueArray = Array.isArray(this.value) ? this.value : (!this.value ? [] : this.value.toString().split(','));
|
||||||
|
|
||||||
// Check any already found options
|
// Check any already found options
|
||||||
if(Object.values(this.menuItems).filter((option) => valueArray.find(val => val == option.value)).length === 0)
|
if(Object.values(this.getAllOptions()).filter((option) => valueArray.find(val => val == option.value)).length === 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -653,7 +636,9 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
protected _bindListeners()
|
protected _bindListeners()
|
||||||
{
|
{
|
||||||
this.addEventListener("sl-clear", this._handleClear);
|
this.addEventListener("sl-clear", this._handleClear);
|
||||||
|
this.addEventListener("sl-show", this._handleMenuShow);
|
||||||
this.addEventListener("sl-after-show", this._handleAfterShow);
|
this.addEventListener("sl-after-show", this._handleAfterShow);
|
||||||
|
this.addEventListener("sl-hide", this._handleMenuHide);
|
||||||
|
|
||||||
// Need our own change to catch the change event from search input
|
// Need our own change to catch the change event from search input
|
||||||
this.addEventListener("change", this._handleChange);
|
this.addEventListener("change", this._handleChange);
|
||||||
@ -673,14 +658,16 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
this._searchInputNode?.removeEventListener("change", this._searchInputNode.handleChange);
|
this._searchInputNode?.removeEventListener("change", this._searchInputNode.handleChange);
|
||||||
this._searchInputNode?.addEventListener("change", this._handleSearchChange);
|
this._searchInputNode?.addEventListener("change", this._handleSearchChange);
|
||||||
|
|
||||||
this.dropdown.querySelector('.select__label').addEventListener("change", this.handleTagEdit);
|
// this.dropdown.querySelector('.select__label').addEventListener("change", this.handleTagEdit);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _unbindListeners()
|
protected _unbindListeners()
|
||||||
{
|
{
|
||||||
this.removeEventListener("sl-select", this._handleSelect);
|
this.removeEventListener("sl-select", this._handleSelect);
|
||||||
|
this.removeEventListener("sl-show", this._handleMenuShow);
|
||||||
this.removeEventListener("sl-after-show", this._handleAfterShow);
|
this.removeEventListener("sl-after-show", this._handleAfterShow);
|
||||||
|
this.removeEventListener("sl-hide", this._handleMenuHide);
|
||||||
this.removeEventListener("sl-clear", this._handleClear)
|
this.removeEventListener("sl-clear", this._handleClear)
|
||||||
this.removeEventListener("change", this._handleChange);
|
this.removeEventListener("change", this._handleChange);
|
||||||
this.removeEventListener("paste", this._handlePaste);
|
this.removeEventListener("paste", this._handlePaste);
|
||||||
@ -688,7 +675,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
this._searchInputNode?.removeEventListener("change", this._handleSearchChange);
|
this._searchInputNode?.removeEventListener("change", this._handleSearchChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMenuShow()
|
_handleMenuShow()
|
||||||
{
|
{
|
||||||
if(this.readonly)
|
if(this.readonly)
|
||||||
{
|
{
|
||||||
@ -698,15 +685,11 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
this._activeControls?.classList.toggle("novalue", this.multiple && this.value == '' || !this.multiple);
|
this._activeControls?.classList.toggle("novalue", this.multiple && this.value == '' || !this.multiple);
|
||||||
|
|
||||||
// Reset for parent calculations, will be adjusted after if needed
|
// Reset for parent calculations, will be adjusted after if needed
|
||||||
this.dropdown.setAttribute("distance", 0);
|
//this.dropdown.setAttribute("distance", 0);
|
||||||
|
|
||||||
super.handleMenuShow();
|
|
||||||
|
|
||||||
if(this.searchEnabled || this.allowFreeEntries)
|
if(this.searchEnabled || this.allowFreeEntries)
|
||||||
{
|
{
|
||||||
this._activeControls?.classList.add("active");
|
this._activeControls?.classList.add("active");
|
||||||
this._searchInputNode.focus();
|
|
||||||
this._searchInputNode.select();
|
|
||||||
// Hide edit explicitly since it's so hard via CSS
|
// Hide edit explicitly since it's so hard via CSS
|
||||||
if(this._editInputNode)
|
if(this._editInputNode)
|
||||||
{
|
{
|
||||||
@ -729,6 +712,12 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
*/
|
*/
|
||||||
_handleAfterShow()
|
_handleAfterShow()
|
||||||
{
|
{
|
||||||
|
if(this.searchEnabled || this.allowFreeEntries)
|
||||||
|
{
|
||||||
|
this._searchInputNode.focus();
|
||||||
|
this._searchInputNode.select();
|
||||||
|
}
|
||||||
|
return;
|
||||||
// Need to give positioner a chance to position.
|
// Need to give positioner a chance to position.
|
||||||
// If we call it right away, it has not updated.
|
// If we call it right away, it has not updated.
|
||||||
// I haven't found an event or Promise to hook on to
|
// I haven't found an event or Promise to hook on to
|
||||||
@ -749,24 +738,22 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
focus()
|
focus()
|
||||||
{
|
{
|
||||||
this.dropdown?.show().then(() =>
|
this.show();
|
||||||
{
|
this._searchInputNode.focus();
|
||||||
this._searchInputNode.focus();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMenuHide()
|
_handleMenuHide()
|
||||||
{
|
{
|
||||||
if(this.readonly)
|
if(this.readonly)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
clearTimeout(this._searchTimeout);
|
this.clearSearch();
|
||||||
super.handleMenuHide();
|
|
||||||
|
|
||||||
// Reset display
|
// Reset display
|
||||||
if(this._searchInputNode)
|
if(this._searchInputNode)
|
||||||
@ -778,11 +765,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
this._editInputNode.style.display = "";
|
this._editInputNode.style.display = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.searchEnabled || this.allowFreeEntries)
|
this._activeControls?.classList.remove("active");
|
||||||
{
|
|
||||||
this._activeControls?.classList.remove("active");
|
|
||||||
this.shadowRoot.querySelector('.select__label').style.display = "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_triggerChange(event)
|
_triggerChange(event)
|
||||||
@ -830,14 +813,14 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
/**
|
/**
|
||||||
* An option was selected
|
* An option was selected
|
||||||
*/
|
*/
|
||||||
handleMenuSelect(event)
|
handleOptionClick(event)
|
||||||
{
|
{
|
||||||
// Need to keep the remote option - only if selected
|
// Need to keep the remote option - only if selected
|
||||||
if(event.detail.item.classList.contains("remote") && !this.select_options.find(o => o.value == event.detail.item.value))
|
if(event.target.classList.contains("remote") && !this.select_options.find(o => o.value == event.target.value))
|
||||||
{
|
{
|
||||||
this._selected_remote.push({...event.detail.item.option});
|
this._selected_remote.push({...event.target.option});
|
||||||
}
|
}
|
||||||
super.handleMenuSelect(event);
|
super.handleOptionClick(event);
|
||||||
|
|
||||||
this.updateComplete.then(() =>
|
this.updateComplete.then(() =>
|
||||||
{
|
{
|
||||||
@ -850,12 +833,12 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
// If we were overlapping, reset
|
// If we were overlapping, reset
|
||||||
if(this._activeControls.classList.contains("novalue"))
|
if(this._activeControls.classList.contains("novalue"))
|
||||||
{
|
{
|
||||||
this.handleMenuShow();
|
this._handleMenuShow();
|
||||||
this._handleAfterShow();
|
this._handleAfterShow();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scroll the new tag into view
|
// Scroll the new tag into view
|
||||||
if(event.detail && event.detail.item)
|
if(event.detail)
|
||||||
{
|
{
|
||||||
// Causes sidemenu (calendar) to scroll to top & get stuck
|
// Causes sidemenu (calendar) to scroll to top & get stuck
|
||||||
/*
|
/*
|
||||||
@ -881,17 +864,14 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
_handleClear(e)
|
_handleClear(e)
|
||||||
{
|
{
|
||||||
// Only keep remote options that are still used
|
// Only keep remote options that are still used
|
||||||
this._selected_remote = this._selected_remote.filter((option) => this.getValueAsArray().indexOf(option.value) !== -1);
|
this._selected_remote = this._selected_remote.filter((option) => this.value.indexOf(option.value) !== -1);
|
||||||
|
|
||||||
if(!this.multiple && this.searchEnabled)
|
if(!this.multiple && this.searchEnabled)
|
||||||
{
|
{
|
||||||
this._handleSearchAbort(e);
|
this._handleSearchAbort(e);
|
||||||
|
|
||||||
// Restore label styling
|
|
||||||
this.shadowRoot.querySelector("[part='display-label']").style.display = "";
|
|
||||||
|
|
||||||
// Start searching again
|
// Start searching again
|
||||||
this.updateComplete.then(() => this.handleMenuShow())
|
this._handleMenuShow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -906,17 +886,6 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
async _handleSearchBlur(event : FocusEvent)
|
async _handleSearchBlur(event : FocusEvent)
|
||||||
{
|
{
|
||||||
clearTimeout(this._searchTimeout);
|
clearTimeout(this._searchTimeout);
|
||||||
if(event.relatedTarget && event.relatedTarget instanceof SlMenuItem)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try any value they had in progress
|
|
||||||
if(this._searchInputNode.value && this.allowFreeEntries)
|
|
||||||
{
|
|
||||||
this.createFreeEntry(this._searchInputNode.value);
|
|
||||||
}
|
|
||||||
this.clearSearch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -928,15 +897,14 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
{
|
{
|
||||||
clearTimeout(this._searchTimeout);
|
clearTimeout(this._searchTimeout);
|
||||||
this._activeControls?.classList.add("active");
|
this._activeControls?.classList.add("active");
|
||||||
this.dropdown.show();
|
|
||||||
|
|
||||||
// Pass off some keys to select
|
// Pass off some keys to select
|
||||||
if(['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key))
|
if(['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key))
|
||||||
{
|
{
|
||||||
|
|
||||||
// Strip out hidden non-matching selected & disabled items so key navigation works
|
// Strip out hidden non-matching selected & disabled items so key navigation works
|
||||||
this.menuItems = this.menuItems.filter(i => !i.disabled);
|
// TODO
|
||||||
return super.handleKeyDown(event);
|
return;
|
||||||
}
|
}
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
@ -946,12 +914,12 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
{
|
{
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this._searchInputNode.value = "";
|
this._searchInputNode.value = "";
|
||||||
this.dropdown.hide().then(async() =>
|
this.updateComplete.then(async() =>
|
||||||
{
|
{
|
||||||
// update sizing / position before getting ready for another one
|
// update sizing / position before getting ready for another one
|
||||||
if(this.multiple)
|
if(this.multiple)
|
||||||
{
|
{
|
||||||
await this.dropdown.show();
|
// await this.show();
|
||||||
this._searchInputNode.focus();
|
this._searchInputNode.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -977,6 +945,17 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combobox listens for mousedown, which interferes with search clear button.
|
||||||
|
* Here we block it from bubbling
|
||||||
|
* @param {MouseEvent} event
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
protected _handleSearchMouseDown(event : MouseEvent)
|
||||||
|
{
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
protected _handleEditKeyDown(event : KeyboardEvent)
|
protected _handleEditKeyDown(event : KeyboardEvent)
|
||||||
{
|
{
|
||||||
// Stop propagation, or parent key handler will add again
|
// Stop propagation, or parent key handler will add again
|
||||||
@ -1037,7 +1016,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
|
|
||||||
// Show a spinner
|
// Show a spinner
|
||||||
let spinner = document.createElement("sl-spinner");
|
let spinner = document.createElement("sl-spinner");
|
||||||
spinner.slot = "suffix";
|
spinner.slot = "expand-icon";
|
||||||
this.appendChild(spinner);
|
this.appendChild(spinner);
|
||||||
|
|
||||||
// Hide clear button
|
// Hide clear button
|
||||||
@ -1048,6 +1027,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clear previous results
|
// Clear previous results
|
||||||
|
this._total_result_count = 0;
|
||||||
this._clearResults();
|
this._clearResults();
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
|
|
||||||
@ -1058,7 +1038,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
]).then(() =>
|
]).then(() =>
|
||||||
{
|
{
|
||||||
// Show no results indicator
|
// Show no results indicator
|
||||||
if(this.menuItems.filter(e => !e.classList.contains("no-match")).length == 0)
|
if(this.getAllOptions().filter(e => !e.classList.contains("no-match")).length == 0)
|
||||||
{
|
{
|
||||||
let target = this._optionTargetNode || this;
|
let target = this._optionTargetNode || this;
|
||||||
let temp = document.createElement("div");
|
let temp = document.createElement("div");
|
||||||
@ -1074,13 +1054,6 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
{
|
{
|
||||||
clear_button.style.display = "";
|
clear_button.style.display = "";
|
||||||
}
|
}
|
||||||
}).then(() =>
|
|
||||||
{
|
|
||||||
// Not sure why this stays hidden if there's no results, but it sticks and hides all results afterward
|
|
||||||
this.dropdown.shadowRoot.querySelector(".dropdown__panel").removeAttribute("hidden");
|
|
||||||
|
|
||||||
// Call our resize stuff explicitly
|
|
||||||
this._handleAfterShow();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1128,14 +1101,9 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
n.remove();
|
n.remove();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
// Not searching anymore, clear flag
|
||||||
// Reset remaining options. It might be faster to re-create instead.
|
this.select_options.map((o) => o.isMatch = null);
|
||||||
this._menuItems.forEach((item) =>
|
this.requestUpdate("select_options");
|
||||||
{
|
|
||||||
item.disabled = item.option?.disabled || false;
|
|
||||||
item.classList.remove("match");
|
|
||||||
item.classList.remove("no-match");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1148,14 +1116,11 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
{
|
{
|
||||||
return new Promise((resolve) =>
|
return new Promise((resolve) =>
|
||||||
{
|
{
|
||||||
this.localItems.forEach((item) =>
|
this.select_options.forEach((option) =>
|
||||||
{
|
{
|
||||||
let match = this.searchMatch(search, item);
|
option.isMatch = this.searchMatch(search, option);
|
||||||
item.classList.toggle("match", match);
|
|
||||||
// set disabled so arrow keys step over. Might be a better way to handle that
|
|
||||||
item.disabled = !match;
|
|
||||||
item.classList.toggle("no-match", !match);
|
|
||||||
})
|
})
|
||||||
|
this.requestUpdate("select_options");
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1208,13 +1173,13 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
return option.label.toLowerCase().includes(lower_search) || option.value.includes(search)
|
return option.label.toLowerCase().includes(lower_search) || option.value.includes(search)
|
||||||
});
|
});
|
||||||
// Limit results
|
// Limit results
|
||||||
const totalCount = filtered.length;
|
this._total_result_count = filtered.length;
|
||||||
if(filtered.length > Et2WidgetWithSearch.RESULT_LIMIT)
|
if(filtered.length > Et2WidgetWithSearch.RESULT_LIMIT)
|
||||||
{
|
{
|
||||||
filtered.splice(Et2WidgetWithSearch.RESULT_LIMIT);
|
filtered.splice(Et2WidgetWithSearch.RESULT_LIMIT);
|
||||||
}
|
}
|
||||||
// Add the matches
|
// Add the matches
|
||||||
this.processRemoteResults(filtered, totalCount);
|
this.processRemoteResults(filtered);
|
||||||
return filtered;
|
return filtered;
|
||||||
})
|
})
|
||||||
.catch((_err) =>
|
.catch((_err) =>
|
||||||
@ -1252,14 +1217,14 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
{
|
{
|
||||||
// If results have a total included, pull it out.
|
// If results have a total included, pull it out.
|
||||||
// It will cause errors if left in the results
|
// It will cause errors if left in the results
|
||||||
let total = null;
|
this._total_result_count = results.length;
|
||||||
if(typeof results.total !== "undefined")
|
if(typeof results.total !== "undefined")
|
||||||
{
|
{
|
||||||
total = results.total;
|
this._total_result_count = results.total;
|
||||||
delete results.total;
|
delete results.total;
|
||||||
}
|
}
|
||||||
let entries = cleanSelectOptions(results);
|
let entries = cleanSelectOptions(results);
|
||||||
this.processRemoteResults(entries, total);
|
this.processRemoteResults(entries);
|
||||||
return entries;
|
return entries;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1267,68 +1232,36 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
/**
|
/**
|
||||||
* Add in remote results
|
* Add in remote results
|
||||||
* @param results
|
* @param results
|
||||||
* @param totalResults If there are more results than were returned, total number of matches
|
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected processRemoteResults(entries, totalResults = 0)
|
protected processRemoteResults(entries)
|
||||||
{
|
{
|
||||||
if(!entries?.length)
|
if(!entries?.length)
|
||||||
{
|
{
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
// Add a "remote" class so we can tell these apart from any local results
|
// Add a "remote" class so we can tell these apart from any local results
|
||||||
entries.forEach((entry) => entry.class = (entry.class || "") + " remote");
|
entries.forEach((entry) =>
|
||||||
|
|
||||||
let target = this._optionTargetNode || this;
|
|
||||||
if(target)
|
|
||||||
{
|
{
|
||||||
// Add in remote options, avoiding duplicates
|
entry.class = (entry.class || "") + " remote";
|
||||||
this.select_options.filter(function(item)
|
// Server says it's a match
|
||||||
|
entry.isMatch = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Add in remote options, avoiding duplicates
|
||||||
|
this.select_options.filter(function(item)
|
||||||
|
{
|
||||||
|
let i = entries.findIndex(x => (x.value == item.value));
|
||||||
|
if(i <= -1)
|
||||||
{
|
{
|
||||||
let i = entries.findIndex(x => (x.value == item.value));
|
entries.push(item);
|
||||||
if(i <= -1)
|
}
|
||||||
{
|
return null;
|
||||||
entries.push(item);
|
});
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
let options = html`${entries.map(this._optionTemplate.bind(this))}`;
|
this._remote_options = entries;
|
||||||
|
this.requestUpdate("select_options");
|
||||||
/**
|
|
||||||
* Add in new options.
|
|
||||||
* Rendering directly into target will remove existing options, which we don't need to do
|
|
||||||
*/
|
|
||||||
|
|
||||||
let temp_target = document.createElement("div");
|
|
||||||
let resultCount = entries.length;
|
|
||||||
|
|
||||||
render(options, temp_target);
|
|
||||||
return Promise.all(([...temp_target.querySelectorAll(":scope > *")].map(item => item.render)))
|
|
||||||
.then(() =>
|
|
||||||
{
|
|
||||||
temp_target.querySelectorAll(":scope > *").forEach((item) =>
|
|
||||||
{
|
|
||||||
// Avoid duplicate error
|
|
||||||
if(!target.querySelector("[value='" + ('' + item.value).replace(/'/g, "\\\'") + "']"))
|
|
||||||
{
|
|
||||||
target.appendChild(item);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this.handleMenuSlotChange();
|
|
||||||
})
|
|
||||||
.then(() =>
|
|
||||||
{
|
|
||||||
if(totalResults && totalResults > resultCount)
|
|
||||||
{
|
|
||||||
// More results available that were not sent
|
|
||||||
let count = document.createElement("span")
|
|
||||||
count.classList.add("remote");
|
|
||||||
count.textContent = this.egw().lang("%1 more...", totalResults - resultCount);
|
|
||||||
target.appendChild(count);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1339,21 +1272,21 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected searchMatch(search, item) : boolean
|
protected searchMatch(search, option : SelectOption) : boolean
|
||||||
{
|
{
|
||||||
if(!item || !item.value)
|
if(!option || !option.value)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(item.textContent?.toLowerCase().includes(search.toLowerCase()))
|
if(option.label?.toLowerCase().includes(search.toLowerCase()))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(typeof item.value == "string")
|
if(typeof option.value == "string")
|
||||||
{
|
{
|
||||||
return item.value.includes(search.toLowerCase());
|
return option.value.includes(search.toLowerCase());
|
||||||
}
|
}
|
||||||
return item.value == search;
|
return option.value == search;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1378,16 +1311,21 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
this.requestUpdate('select_options');
|
this.requestUpdate('select_options');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure not to double-add
|
// Make sure not to double-add, but wait until the option is there
|
||||||
if(this.multiple && this.value.indexOf(text) == -1)
|
this.updateComplete.then(() =>
|
||||||
{
|
{
|
||||||
this.value.push(text);
|
if(this.multiple && this.getValueAsArray().indexOf(text) == -1)
|
||||||
}
|
{
|
||||||
else if(!this.multiple && this.value !== text)
|
let value = this.getValueAsArray();
|
||||||
{
|
value.push(text);
|
||||||
this.value = text;
|
this.value = value;
|
||||||
}
|
}
|
||||||
this.requestUpdate("value");
|
else if(!this.multiple && this.value !== text)
|
||||||
|
{
|
||||||
|
this.value = text;
|
||||||
|
}
|
||||||
|
this.requestUpdate("value");
|
||||||
|
});
|
||||||
|
|
||||||
// If we were overlapping edit inputbox with the value display, reset
|
// If we were overlapping edit inputbox with the value display, reset
|
||||||
if(!this.readonly && this._activeControls?.classList.contains("novalue"))
|
if(!this.readonly && this._activeControls?.classList.contains("novalue"))
|
||||||
@ -1478,7 +1416,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
// type to select will focus matching entries, but we don't want to stop the edit yet
|
// type to select will focus matching entries, but we don't want to stop the edit yet
|
||||||
if(typeof abort == "object" && abort.type == "blur")
|
if(typeof abort == "object" && abort.type == "blur")
|
||||||
{
|
{
|
||||||
if(abort.relatedTarget?.localName == "sl-menu-item")
|
if(abort.relatedTarget?.localName == this.optionTag)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1530,14 +1468,12 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
this.dropdown.panel.setAttribute("hidden", "");
|
this.dropdown.panel.setAttribute("hidden", "");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.syncItemsFromValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _handleSearchAbort(e)
|
protected _handleSearchAbort(e)
|
||||||
{
|
{
|
||||||
this._activeControls.classList.remove("active");
|
this._activeControls.classList.remove("active");
|
||||||
this.clearSearch();
|
this.clearSearch();
|
||||||
this.syncItemsFromValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1552,7 +1488,14 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected _handleSearchClear(e)
|
||||||
|
{
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
this.clearSearch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Et2WidgetWithSearch as unknown as Constructor<SearchMixinInterface> & T;
|
return Et2WidgetWithSearch as unknown as Constructor<SearchMixinInterface> & T;
|
||||||
}
|
});
|
@ -7,13 +7,13 @@
|
|||||||
* @author Ralf Becker <rb@egroupware.org>
|
* @author Ralf Becker <rb@egroupware.org>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Et2Select} from "./Et2Select";
|
import {Et2Select} from "../Et2Select";
|
||||||
import {cleanSelectOptions, SelectOption} from "./FindSelectOptions";
|
import {cleanSelectOptions, SelectOption} from "../FindSelectOptions";
|
||||||
import {SelectAccountMixin} from "./SelectAccountMixin";
|
import {SelectAccountMixin} from "../SelectAccountMixin";
|
||||||
import {Et2StaticSelectMixin} from "./StaticOptions";
|
import {Et2StaticSelectMixin} from "../StaticOptions";
|
||||||
import {html, nothing} from "@lion/core";
|
import {html, nothing} from "lit";
|
||||||
|
|
||||||
export type AccountType = 'accounts'|'groups'|'both'|'owngroups';
|
export type AccountType = 'accounts' | 'groups' | 'both' | 'owngroups';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @customElement et2-select-account
|
* @customElement et2-select-account
|
||||||
@ -58,17 +58,16 @@ export class Et2SelectAccount extends SelectAccountMixin(Et2StaticSelectMixin(Et
|
|||||||
{
|
{
|
||||||
if(this.accountType === 'both')
|
if(this.accountType === 'both')
|
||||||
{
|
{
|
||||||
fetch.push(this.egw().accounts('accounts').then(options => {this.static_options = this.static_options.concat(cleanSelectOptions(options))}));
|
fetch.push(this.egw().accounts('accounts').then(options => {this._static_options = this._static_options.concat(cleanSelectOptions(options))}));
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch.push(this.egw().accounts('owngroups').then(options => {this.static_options = this.static_options.concat(cleanSelectOptions(options))}));
|
fetch.push(this.egw().accounts('owngroups').then(options => {this._static_options = this._static_options.concat(cleanSelectOptions(options))}));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fetch.push(this.egw().accounts(this.accountType).then(options => {this.static_options = this.static_options.concat(cleanSelectOptions(options))}));
|
fetch.push(this.egw().accounts(this.accountType).then(options => {this._static_options = this._static_options.concat(cleanSelectOptions(options))}));
|
||||||
}
|
}
|
||||||
this.fetchComplete = Promise.all(fetch)
|
this.fetchComplete = Promise.all(fetch);
|
||||||
.then(() => this._renderOptions());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -102,12 +101,7 @@ export class Et2SelectAccount extends SelectAccountMixin(Et2StaticSelectMixin(Et
|
|||||||
{
|
{
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
let select_options : Array<SelectOption> = [...(this.static_options || []), ...super.select_options];
|
return super.select_options;
|
||||||
|
|
||||||
return select_options.filter((value, index, self) =>
|
|
||||||
{
|
|
||||||
return self.findIndex(v => v.value === value.value) === index;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set select_options(new_options : SelectOption[])
|
set select_options(new_options : SelectOption[])
|
17
api/js/etemplate/Et2Select/Select/Et2SelectApp.ts
Normal file
17
api/js/etemplate/Et2Select/Select/Et2SelectApp.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import {Et2Select} from "../Et2Select";
|
||||||
|
import {Et2StaticSelectMixin, StaticOptions as so} from "../StaticOptions";
|
||||||
|
import {cleanSelectOptions} from "../FindSelectOptions";
|
||||||
|
|
||||||
|
export class Et2SelectApp extends Et2StaticSelectMixin(Et2Select)
|
||||||
|
{
|
||||||
|
public connectedCallback()
|
||||||
|
{
|
||||||
|
super.connectedCallback()
|
||||||
|
this.fetchComplete = so.app(this, {}).then((options) =>
|
||||||
|
{
|
||||||
|
this.set_static_options(cleanSelectOptions(options));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("et2-select-app", Et2SelectApp);
|
26
api/js/etemplate/Et2Select/Select/Et2SelectBitwise.ts
Normal file
26
api/js/etemplate/Et2Select/Select/Et2SelectBitwise.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import {Et2Select} from "../Et2Select";
|
||||||
|
import {Et2StaticSelectMixin} from "../StaticOptions";
|
||||||
|
|
||||||
|
export class Et2SelectBitwise extends Et2StaticSelectMixin(Et2Select)
|
||||||
|
{
|
||||||
|
/* currently handled server-side */
|
||||||
|
/*
|
||||||
|
set value(new_value)
|
||||||
|
{
|
||||||
|
let oldValue = this._value;
|
||||||
|
let expanded_value = [];
|
||||||
|
let options = this.select_options;
|
||||||
|
for(let index in options)
|
||||||
|
{
|
||||||
|
let right = parseInt(options[index].value);
|
||||||
|
if(!!(new_value & right))
|
||||||
|
{
|
||||||
|
expanded_value.push(right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.value = expanded_value;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("et2-select-bitwise", Et2SelectBitwise);
|
28
api/js/etemplate/Et2Select/Select/Et2SelectBool.ts
Normal file
28
api/js/etemplate/Et2Select/Select/Et2SelectBool.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import {Et2Select} from "../Et2Select";
|
||||||
|
import {Et2StaticSelectMixin, StaticOptions} from "../StaticOptions";
|
||||||
|
|
||||||
|
export class Et2SelectBool extends Et2StaticSelectMixin(Et2Select)
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._static_options = StaticOptions.bool(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
get value()
|
||||||
|
{
|
||||||
|
return super.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boolean option values are "0" and "1", so change boolean to those
|
||||||
|
* @param {string | string[]} new_value
|
||||||
|
*/
|
||||||
|
set value(new_value)
|
||||||
|
{
|
||||||
|
super.value = new_value ? "1" : "0";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("et2-select-bool", Et2SelectBool);
|
@ -8,10 +8,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {css, PropertyValues} from "@lion/core";
|
import {css, html, nothing, PropertyValues, TemplateResult, unsafeCSS} from "lit";
|
||||||
import {Et2Select} from "./Et2Select";
|
import {Et2Select} from "../Et2Select";
|
||||||
import {Et2StaticSelectMixin, StaticOptions as so} from "./StaticOptions";
|
import {Et2StaticSelectMixin, StaticOptions as so} from "../StaticOptions";
|
||||||
import {cleanSelectOptions} from "./FindSelectOptions";
|
import {cleanSelectOptions} from "../FindSelectOptions";
|
||||||
|
import {StaticValue} from "lit/development/static-html";
|
||||||
|
import {literal} from "lit/static-html.js";
|
||||||
|
import {repeat} from "lit/directives/repeat.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Customised Select widget for categories
|
* Customised Select widget for categories
|
||||||
@ -25,12 +28,14 @@ export class Et2SelectCategory extends Et2StaticSelectMixin(Et2Select)
|
|||||||
...super.styles,
|
...super.styles,
|
||||||
css`
|
css`
|
||||||
/* Category color on options */
|
/* Category color on options */
|
||||||
::slotted(*) {
|
|
||||||
|
sl-option {
|
||||||
border-left: 6px solid var(--category-color, transparent);
|
border-left: 6px solid var(--category-color, transparent);
|
||||||
}
|
}
|
||||||
/* Border on the (single) selected value */
|
/* Border on the (single) selected value */
|
||||||
:host(.hasValue:not([multiple])) .select--standard .select__control {
|
|
||||||
border-left: 6px solid var(--sl-input-border-color);
|
:host(:not([multiple]))::part(combobox) {
|
||||||
|
border-left: 6px solid var(--category-color, var(--sl-input-border-color));
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
]
|
]
|
||||||
@ -73,13 +78,6 @@ export class Et2SelectCategory extends Et2StaticSelectMixin(Et2Select)
|
|||||||
(this.getInstanceManager() && this.getInstanceManager().app) ||
|
(this.getInstanceManager() && this.getInstanceManager().app) ||
|
||||||
this.egw().app_name();
|
this.egw().app_name();
|
||||||
}
|
}
|
||||||
// If app passes options (addressbook index) we'll use those instead.
|
|
||||||
// They will be found automatically by update() after ID is set.
|
|
||||||
await this.updateComplete;
|
|
||||||
if(this.select_options.length == 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -91,72 +89,57 @@ export class Et2SelectCategory extends Et2StaticSelectMixin(Et2Select)
|
|||||||
{
|
{
|
||||||
this.fetchComplete = so.cat(this).then(options =>
|
this.fetchComplete = so.cat(this).then(options =>
|
||||||
{
|
{
|
||||||
this.static_options = cleanSelectOptions(options);
|
this._static_options = cleanSelectOptions(options);
|
||||||
this.requestUpdate("select_options");
|
this.requestUpdate("select_options");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(changedProperties.has("value") || changedProperties.has('select_options'))
|
|
||||||
{
|
|
||||||
this.doLabelChange()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Override from parent (SlSelect) to customise display of the current value.
|
protected handleValueChange(e)
|
||||||
* Here's where we add the icon & color border
|
|
||||||
*/
|
|
||||||
doLabelChange()
|
|
||||||
{
|
{
|
||||||
// Update the display label when checked menu item's label changes
|
super.handleValueChange(e);
|
||||||
if(this.multiple)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkedItem = this.menuItems.find(item => item.value === this.value);
|
// Just re-draw to get the colors & icon
|
||||||
this.displayLabel = checkedItem ? checkedItem.textContent : '';
|
this.requestUpdate();
|
||||||
this.querySelector("[slot=prefix].tag_image")?.remove();
|
|
||||||
if(checkedItem)
|
|
||||||
{
|
|
||||||
let image = this._createImage(checkedItem)
|
|
||||||
if(image)
|
|
||||||
{
|
|
||||||
this.append(image);
|
|
||||||
}
|
|
||||||
this.dropdown.querySelector(".select__control").style.borderColor =
|
|
||||||
getComputedStyle(checkedItem).getPropertyValue("--category-color") || "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render select_options as child DOM Nodes
|
* Used to render each option into the select
|
||||||
|
* Overridden for colors
|
||||||
*
|
*
|
||||||
* Overridden here so we can re-do the displayed label after first load of select options.
|
* @param {SelectOption} option
|
||||||
* Initial load order / lifecycle does not have all the options at the right time
|
* @returns {TemplateResult}
|
||||||
* @protected
|
|
||||||
*/
|
*/
|
||||||
protected _renderOptions()
|
public render() : TemplateResult
|
||||||
{
|
{
|
||||||
// @ts-ignore Doesn't know about Et2WidgetWithSelectMixin._renderOptions()
|
/** CSS variables are not making it through to options, re-declaring them here works */
|
||||||
return super._renderOptions().then(() =>
|
return html`
|
||||||
{
|
<style>
|
||||||
// @ts-ignore Doesn't know about SlSelect.menuItems
|
${repeat(this.select_options, (option) =>
|
||||||
if(this.menuItems.length > 0)
|
{
|
||||||
{
|
if(typeof option.color == "undefined" || !option.color)
|
||||||
this.doLabelChange();
|
{
|
||||||
}
|
return nothing;
|
||||||
});
|
}
|
||||||
|
return unsafeCSS(
|
||||||
|
(this.getValueAsArray().includes(option.value) ? "::part(combobox) { --category-color: " + option.color + ";}" : "") +
|
||||||
|
".cat_" + option.value + " {--category-color: " + option.color + ";}"
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</style>
|
||||||
|
${super.render()}
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use a custom tag for when multiple=true
|
* Use a custom tag for when multiple=true
|
||||||
*
|
*
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
get tagTag() : string
|
public get tagTag() : StaticValue
|
||||||
{
|
{
|
||||||
return "et2-category-tag";
|
return literal`et2-category-tag`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
@ -8,10 +8,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {Et2Select} from "./Et2Select";
|
import {Et2Select} from "../Et2Select";
|
||||||
import {Et2StaticSelectMixin, StaticOptions as so} from "./StaticOptions";
|
import {Et2StaticSelectMixin, StaticOptions as so} from "../StaticOptions";
|
||||||
import {egw} from "../../jsapi/egw_global";
|
import {egw} from "../../../jsapi/egw_global";
|
||||||
import {SelectOption} from "./FindSelectOptions";
|
import {SelectOption} from "../FindSelectOptions";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Customised Select widget for countries
|
* Customised Select widget for countries
|
||||||
@ -38,7 +38,7 @@ export class Et2SelectCountry extends Et2StaticSelectMixin(Et2Select)
|
|||||||
|
|
||||||
(<Promise<SelectOption[]>>so.country(this, {}, true)).then(options =>
|
(<Promise<SelectOption[]>>so.country(this, {}, true)).then(options =>
|
||||||
{
|
{
|
||||||
this.static_options = options
|
this._static_options = options
|
||||||
this.requestUpdate("select_options");
|
this.requestUpdate("select_options");
|
||||||
});
|
});
|
||||||
}
|
}
|
14
api/js/etemplate/Et2Select/Select/Et2SelectDay.ts
Normal file
14
api/js/etemplate/Et2Select/Select/Et2SelectDay.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import {Et2Select} from "../Et2Select";
|
||||||
|
import {Et2StaticSelectMixin, StaticOptions as so} from "../StaticOptions";
|
||||||
|
|
||||||
|
export class Et2SelectDay extends Et2StaticSelectMixin(Et2Select)
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._static_options = so.day(this, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("et2-select-day", Et2SelectDay);
|
53
api/js/etemplate/Et2Select/Select/Et2SelectDayOfWeek.ts
Normal file
53
api/js/etemplate/Et2Select/Select/Et2SelectDayOfWeek.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import {Et2Select} from "../Et2Select";
|
||||||
|
import {Et2StaticSelectMixin, StaticOptions} from "../StaticOptions";
|
||||||
|
import {cleanSelectOptions} from "../FindSelectOptions";
|
||||||
|
|
||||||
|
export class Et2SelectDayOfWeek extends Et2StaticSelectMixin(Et2Select)
|
||||||
|
{
|
||||||
|
connectedCallback()
|
||||||
|
{
|
||||||
|
super.connectedCallback();
|
||||||
|
|
||||||
|
// Wait for connected instead of constructor because attributes make a difference in
|
||||||
|
// which options are offered
|
||||||
|
this.fetchComplete = StaticOptions.dow(this, {other: this.other || []}).then(options =>
|
||||||
|
{
|
||||||
|
this.set_static_options(cleanSelectOptions(options));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(new_value)
|
||||||
|
{
|
||||||
|
let expanded_value = typeof new_value == "object" ? new_value : [];
|
||||||
|
if(new_value && (typeof new_value == "string" || typeof new_value == "number"))
|
||||||
|
{
|
||||||
|
let int_value = parseInt(new_value);
|
||||||
|
this.updateComplete.then(() =>
|
||||||
|
{
|
||||||
|
this.fetchComplete.then(() =>
|
||||||
|
{
|
||||||
|
let options = this.select_options;
|
||||||
|
for(let index in options)
|
||||||
|
{
|
||||||
|
let right = parseInt(options[index].value);
|
||||||
|
|
||||||
|
if((int_value & right) == right)
|
||||||
|
{
|
||||||
|
expanded_value.push("" + right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.value = expanded_value;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.value = expanded_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get value()
|
||||||
|
{
|
||||||
|
return super.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("et2-select-dow", Et2SelectDayOfWeek);
|
@ -7,11 +7,12 @@
|
|||||||
* @author Nathan Gray
|
* @author Nathan Gray
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Et2Select} from "./Et2Select";
|
import {Et2Select} from "../Et2Select";
|
||||||
import {css, html, nothing, PropertyValues} from "@lion/core";
|
import {css, html, nothing, PropertyValues} from "lit";
|
||||||
import {IsEmail} from "../Validators/IsEmail";
|
import {IsEmail} from "../../Validators/IsEmail";
|
||||||
import interact from "@interactjs/interact";
|
import interact from "@interactjs/interact";
|
||||||
import {Validator} from "@lion/form-core";
|
import {Validator} from "@lion/form-core";
|
||||||
|
import {classMap} from "lit/directives/class-map.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select email address(es)
|
* Select email address(es)
|
||||||
@ -100,6 +101,7 @@ export class Et2SelectEmail extends Et2Select
|
|||||||
this.defaultValidators.push(new IsEmail(this.allowPlaceholder));
|
this.defaultValidators.push(new IsEmail(this.allowPlaceholder));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** @param {import('@lion/core').PropertyValues } changedProperties */
|
/** @param {import('@lion/core').PropertyValues } changedProperties */
|
||||||
willUpdate(changedProperties : PropertyValues)
|
willUpdate(changedProperties : PropertyValues)
|
||||||
{
|
{
|
||||||
@ -112,6 +114,40 @@ export class Et2SelectEmail extends Et2Select
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updated(changedProperties : Map<string, any>)
|
||||||
|
{
|
||||||
|
// Make tags draggable
|
||||||
|
if(!this.readonly && this.allowFreeEntries && this.allowDragAndDrop)
|
||||||
|
{
|
||||||
|
let dragTranslate = {x: 0, y: 0};
|
||||||
|
const tags = this.shadowRoot.querySelectorAll(".select__tags [part='tag']");
|
||||||
|
let draggable = interact(tags).draggable({
|
||||||
|
startAxis: 'xy',
|
||||||
|
listeners: {
|
||||||
|
start: function(e)
|
||||||
|
{
|
||||||
|
let dragPosition = {x: e.page.x, y: e.page.y};
|
||||||
|
dragTranslate = {x: 0, y: 0};
|
||||||
|
e.target.setAttribute('style', `width:${e.target.clientWidth}px !important`);
|
||||||
|
e.target.style.position = 'fixed';
|
||||||
|
e.target.style.zIndex = 10;
|
||||||
|
e.target.style.transform =
|
||||||
|
`translate(${dragPosition.x}px, ${dragPosition.y}px)`;
|
||||||
|
},
|
||||||
|
move: function(e)
|
||||||
|
{
|
||||||
|
dragTranslate.x += e.delta.x;
|
||||||
|
dragTranslate.y += e.delta.y;
|
||||||
|
e.target.style.transform =
|
||||||
|
`translate(${dragTranslate.x}px, ${dragTranslate.y}px)`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// set parent_node with widget context in order to make it accessible after drop
|
||||||
|
draggable.parent_node = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
connectedCallback()
|
connectedCallback()
|
||||||
{
|
{
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
@ -145,25 +181,7 @@ export class Et2SelectEmail extends Et2Select
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle keypresses inside the search input
|
|
||||||
* Overridden from parent to also skip the hidden selected options, which other selects do not do
|
|
||||||
*
|
|
||||||
* @param {KeyboardEvent} event
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
protected _handleSearchKeyDown(event : KeyboardEvent)
|
|
||||||
{
|
|
||||||
// Pass off some keys to select
|
|
||||||
if(['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key))
|
|
||||||
{
|
|
||||||
// Strip out hidden non-matching selected so key navigation works
|
|
||||||
this.menuItems = this.menuItems.filter(i => !i.checked);
|
|
||||||
}
|
|
||||||
return super._handleSearchKeyDown(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Actually query the server.
|
* Actually query the server.
|
||||||
*
|
*
|
||||||
@ -187,55 +205,21 @@ export class Et2SelectEmail extends Et2Select
|
|||||||
*
|
*
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
get tagTag() : string
|
_tagTemplate(option, index)
|
||||||
{
|
{
|
||||||
return "et2-email-tag";
|
return html`
|
||||||
}
|
<et2-email-tag
|
||||||
|
class=${classMap({
|
||||||
/**
|
...option.classList,
|
||||||
* override tag creation in order to add DND functionality
|
"et2-select-draggable": !this.readonly && this.allowFreeEntries && this.allowDragAndDrop
|
||||||
* @param item
|
})}
|
||||||
* @protected
|
?.fullEmail=${this.fullEmail}
|
||||||
*/
|
?.onlyEmail=${this.onlyEmail}
|
||||||
protected _createTagNode(item)
|
value=${option.value}
|
||||||
{
|
>
|
||||||
let tag = super._createTagNode(item);
|
${option.getTextLabel().trim()}
|
||||||
|
</et2-email-tag>
|
||||||
tag.fullEmail = this.fullEmail;
|
`;
|
||||||
tag.onlyEmail = this.onlyEmail;
|
|
||||||
|
|
||||||
// Re-set after setting fullEmail as that can change what we show
|
|
||||||
tag.textContent = item.getTextLabel().trim();
|
|
||||||
|
|
||||||
if(!this.readonly && this.allowFreeEntries && this.allowDragAndDrop)
|
|
||||||
{
|
|
||||||
let dragTranslate = {x: 0, y: 0};
|
|
||||||
tag.class = item.classList.value + " et2-select-draggable";
|
|
||||||
let draggable = interact(tag).draggable({
|
|
||||||
startAxis: 'xy',
|
|
||||||
listeners: {
|
|
||||||
start: function(e)
|
|
||||||
{
|
|
||||||
let dragPosition = {x:e.page.x, y:e.page.y};
|
|
||||||
e.target.setAttribute('style', `width:${e.target.clientWidth}px !important`);
|
|
||||||
e.target.style.position = 'fixed';
|
|
||||||
e.target.style.zIndex = 10;
|
|
||||||
e.target.style.transform =
|
|
||||||
`translate(${dragPosition.x}px, ${dragPosition.y}px)`;
|
|
||||||
},
|
|
||||||
move : function(e)
|
|
||||||
{
|
|
||||||
dragTranslate.x += e.delta.x;
|
|
||||||
dragTranslate.y += e.delta.y;
|
|
||||||
e.target.style.transform =
|
|
||||||
`translate(${dragTranslate.x}px, ${dragTranslate.y}px)`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// set parent_node with widget context in order to make it accessible after drop
|
|
||||||
draggable.parent_node = this;
|
|
||||||
}
|
|
||||||
return tag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
14
api/js/etemplate/Et2Select/Select/Et2SelectHour.ts
Normal file
14
api/js/etemplate/Et2Select/Select/Et2SelectHour.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import {Et2Select} from "../Et2Select";
|
||||||
|
import {Et2StaticSelectMixin, StaticOptions} from "../StaticOptions";
|
||||||
|
|
||||||
|
export class Et2SelectHour extends Et2StaticSelectMixin(Et2Select)
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._static_options = StaticOptions.hour(this, {other: this.other || []});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("et2-select-hour", Et2SelectHour);
|
14
api/js/etemplate/Et2Select/Select/Et2SelectLang.ts
Normal file
14
api/js/etemplate/Et2Select/Select/Et2SelectLang.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import {Et2Select} from "../Et2Select";
|
||||||
|
import {Et2StaticSelectMixin, StaticOptions} from "../StaticOptions";
|
||||||
|
|
||||||
|
export class Et2SelectLang extends Et2StaticSelectMixin(Et2Select)
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._static_options = StaticOptions.lang(this, {other: this.other || []});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("et2-select-lang", Et2SelectLang);
|
14
api/js/etemplate/Et2Select/Select/Et2SelectMonth.ts
Normal file
14
api/js/etemplate/Et2Select/Select/Et2SelectMonth.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import {Et2Select} from "../Et2Select";
|
||||||
|
import {Et2StaticSelectMixin, StaticOptions} from "../StaticOptions";
|
||||||
|
|
||||||
|
export class Et2SelectMonth extends Et2StaticSelectMixin(Et2Select)
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._static_options = StaticOptions.month(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("et2-select-month", Et2SelectMonth);
|
52
api/js/etemplate/Et2Select/Select/Et2SelectNumber.ts
Normal file
52
api/js/etemplate/Et2Select/Select/Et2SelectNumber.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import {Et2Select} from "../Et2Select";
|
||||||
|
import {Et2StaticSelectMixin, StaticOptions} from "../StaticOptions";
|
||||||
|
import {PropertyValues} from 'lit';
|
||||||
|
|
||||||
|
export class Et2SelectNumber extends Et2StaticSelectMixin(Et2Select)
|
||||||
|
{
|
||||||
|
static get properties()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
...super.properties,
|
||||||
|
/**
|
||||||
|
* Step between numbers
|
||||||
|
*/
|
||||||
|
interval: {type: Number},
|
||||||
|
min: {type: Number},
|
||||||
|
max: {type: Number},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add one or more leading zeros
|
||||||
|
* Set to how many zeros you want (000)
|
||||||
|
*/
|
||||||
|
leading_zero: {type: String},
|
||||||
|
/**
|
||||||
|
* Appended after every number
|
||||||
|
*/
|
||||||
|
suffix: {type: String}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.min = 1;
|
||||||
|
this.max = 10;
|
||||||
|
this.interval = 1;
|
||||||
|
this.leading_zero = "";
|
||||||
|
this.suffix = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
updated(changedProperties : PropertyValues)
|
||||||
|
{
|
||||||
|
super.updated(changedProperties);
|
||||||
|
|
||||||
|
if(changedProperties.has('min') || changedProperties.has('max') || changedProperties.has('interval') || changedProperties.has('suffix'))
|
||||||
|
{
|
||||||
|
this._static_options = StaticOptions.number(this);
|
||||||
|
this.requestUpdate("select_options");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("et2-select-number", Et2SelectNumber);
|
15
api/js/etemplate/Et2Select/Select/Et2SelectPercent.ts
Normal file
15
api/js/etemplate/Et2Select/Select/Et2SelectPercent.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import {Et2SelectNumber} from "./Et2SelectNumber";
|
||||||
|
|
||||||
|
export class Et2SelectPercent extends Et2SelectNumber
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.min = 0;
|
||||||
|
this.max = 100;
|
||||||
|
this.interval = 10;
|
||||||
|
this.suffix = "%%";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("et2-select-percent", Et2SelectPercent);
|
14
api/js/etemplate/Et2Select/Select/Et2SelectPriority.ts
Normal file
14
api/js/etemplate/Et2Select/Select/Et2SelectPriority.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import {Et2Select} from "../Et2Select";
|
||||||
|
import {Et2StaticSelectMixin, StaticOptions} from "../StaticOptions";
|
||||||
|
|
||||||
|
export class Et2SelectPriority extends Et2StaticSelectMixin(Et2Select)
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._static_options = StaticOptions.priority(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("et2-select-priority", Et2SelectPriority);
|
@ -8,12 +8,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {css, html, LitElement, repeat, TemplateResult} from "@lion/core";
|
import {css, html, LitElement, TemplateResult} from "lit";
|
||||||
import {et2_IDetachedDOM} from "../et2_core_interfaces";
|
import {repeat} from "lit/directives/repeat.js";
|
||||||
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
import {et2_IDetachedDOM} from "../../et2_core_interfaces";
|
||||||
import {Et2StaticSelectMixin, StaticOptions, StaticOptions as so} from "./StaticOptions";
|
import {Et2Widget} from "../../Et2Widget/Et2Widget";
|
||||||
import {cleanSelectOptions, find_select_options, SelectOption} from "./FindSelectOptions";
|
import {Et2StaticSelectMixin, StaticOptions, StaticOptions as so} from "../StaticOptions";
|
||||||
import {SelectAccountMixin} from "./SelectAccountMixin";
|
import {cleanSelectOptions, find_select_options, SelectOption} from "../FindSelectOptions";
|
||||||
|
import {SelectAccountMixin} from "../SelectAccountMixin";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a stripped-down read-only widget used in nextmatch
|
* This is a stripped-down read-only widget used in nextmatch
|
||||||
@ -143,6 +144,11 @@ li {
|
|||||||
return this.value;
|
return this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getValueAsArray()
|
||||||
|
{
|
||||||
|
return (Array.isArray(this.value) ? this.value : [this.value]);
|
||||||
|
}
|
||||||
|
|
||||||
set value(new_value : string | string[])
|
set value(new_value : string | string[])
|
||||||
{
|
{
|
||||||
// Split anything that is still a CSV
|
// Split anything that is still a CSV
|
||||||
@ -206,10 +212,11 @@ li {
|
|||||||
|
|
||||||
render()
|
render()
|
||||||
{
|
{
|
||||||
|
const value = this.getValueAsArray();
|
||||||
return html`
|
return html`
|
||||||
<ul>
|
<ul>
|
||||||
${repeat(
|
${repeat(
|
||||||
(Array.isArray(this.value) ? this.value : [this.value]),
|
this.getValueAsArray(),
|
||||||
(val : string) => val, (val) =>
|
(val : string) => val, (val) =>
|
||||||
{
|
{
|
||||||
let option = (<SelectOption[]>this.select_options).find(option => option.value == val);
|
let option = (<SelectOption[]>this.select_options).find(option => option.value == val);
|
||||||
@ -282,14 +289,16 @@ customElements.define("et2-select-app_ro", Et2SelectAppReadonly);
|
|||||||
|
|
||||||
export class Et2SelectBitwiseReadonly extends Et2SelectReadonly
|
export class Et2SelectBitwiseReadonly extends Et2SelectReadonly
|
||||||
{
|
{
|
||||||
|
/* Currently handled server side, we get an array
|
||||||
render()
|
render()
|
||||||
{
|
{
|
||||||
let new_value = [];
|
let new_value = [];
|
||||||
|
let int_value = parseInt(this.value);
|
||||||
for(let index in this.select_options)
|
for(let index in this.select_options)
|
||||||
{
|
{
|
||||||
let option = this.select_options[index];
|
let option = this.select_options[index];
|
||||||
let right = parseInt(option && option.value ? option.value : index);
|
let right = parseInt(option && option.value ? option.value : index);
|
||||||
if(!!(this.value & right))
|
if(!!(int_value & right))
|
||||||
{
|
{
|
||||||
new_value.push(right);
|
new_value.push(right);
|
||||||
}
|
}
|
||||||
@ -307,6 +316,8 @@ export class Et2SelectBitwiseReadonly extends Et2SelectReadonly
|
|||||||
})}
|
})}
|
||||||
</ul>`;
|
</ul>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore TypeScript is not recognizing that this widget is a LitElement
|
// @ts-ignore TypeScript is not recognizing that this widget is a LitElement
|
||||||
@ -349,7 +360,6 @@ export class Et2SelectPercentReadonly extends Et2SelectReadonly
|
|||||||
constructor()
|
constructor()
|
||||||
{
|
{
|
||||||
super(...arguments);
|
super(...arguments);
|
||||||
this.suffix = "%%";
|
|
||||||
this.select_options = so.percent(this);
|
this.select_options = so.percent(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -391,6 +401,23 @@ export class Et2SelectDayOfWeekReadonly extends Et2StaticSelectMixin(Et2SelectRe
|
|||||||
this.set_static_options(cleanSelectOptions(options));
|
this.set_static_options(cleanSelectOptions(options));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getValueAsArray()
|
||||||
|
{
|
||||||
|
let expanded_value = [];
|
||||||
|
let int_value = parseInt(this.value);
|
||||||
|
let options = this.select_options;
|
||||||
|
for(let index in options)
|
||||||
|
{
|
||||||
|
let right = parseInt(<string>options[index].value);
|
||||||
|
|
||||||
|
if((int_value & right) == right)
|
||||||
|
{
|
||||||
|
expanded_value.push("" + right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return expanded_value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore TypeScript is not recognizing that this widget is a LitElement
|
// @ts-ignore TypeScript is not recognizing that this widget is a LitElement
|
||||||
@ -422,7 +449,7 @@ export class Et2SelectNumberReadonly extends Et2StaticSelectMixin(Et2SelectReado
|
|||||||
{
|
{
|
||||||
protected find_select_options(_attrs)
|
protected find_select_options(_attrs)
|
||||||
{
|
{
|
||||||
this.static_options = so.number(this, _attrs);
|
this._static_options = so.number(this, _attrs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
45
api/js/etemplate/Et2Select/Select/Et2SelectState.ts
Normal file
45
api/js/etemplate/Et2Select/Select/Et2SelectState.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import {Et2Select} from "../Et2Select";
|
||||||
|
import {Et2StaticSelectMixin, StaticOptions} from "../StaticOptions";
|
||||||
|
import {SelectOption} from "../FindSelectOptions";
|
||||||
|
|
||||||
|
export class Et2SelectState extends Et2StaticSelectMixin(Et2Select)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Two-letter ISO country code
|
||||||
|
*/
|
||||||
|
protected __countryCode;
|
||||||
|
|
||||||
|
static get properties()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
...super.properties,
|
||||||
|
countryCode: String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.countryCode = 'DE';
|
||||||
|
}
|
||||||
|
|
||||||
|
get countryCode()
|
||||||
|
{
|
||||||
|
return this.__countryCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
set countryCode(code : string)
|
||||||
|
{
|
||||||
|
this.__countryCode = code;
|
||||||
|
this._static_options = <SelectOption[]>StaticOptions.state(this, {country_code: code});
|
||||||
|
this.requestUpdate("select_options");
|
||||||
|
}
|
||||||
|
|
||||||
|
set_country_code(code)
|
||||||
|
{
|
||||||
|
this.countryCode = code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("et2-select-state", Et2SelectState);
|
61
api/js/etemplate/Et2Select/Select/Et2SelectTab.ts
Normal file
61
api/js/etemplate/Et2Select/Select/Et2SelectTab.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import {Et2SelectApp} from "./Et2SelectApp";
|
||||||
|
import {SelectOption} from "../FindSelectOptions";
|
||||||
|
|
||||||
|
export class Et2SelectTab extends Et2SelectApp
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.allowFreeEntries = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(new_value)
|
||||||
|
{
|
||||||
|
if(!new_value)
|
||||||
|
{
|
||||||
|
super.value = new_value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const values = Array.isArray(new_value) ? new_value : [new_value];
|
||||||
|
const options = this.select_options;
|
||||||
|
values.forEach(value =>
|
||||||
|
{
|
||||||
|
if(!options.filter(option => option.value == value).length)
|
||||||
|
{
|
||||||
|
const matches = value.match(/^([a-z0-9]+)\-/i);
|
||||||
|
let option : SelectOption = {value: value, label: value};
|
||||||
|
if(matches)
|
||||||
|
{
|
||||||
|
option = options.filter(option => option.value == matches[1])[0] || {
|
||||||
|
value: value,
|
||||||
|
label: this.egw().lang(matches[1])
|
||||||
|
};
|
||||||
|
option.value = value;
|
||||||
|
option.label += ' ' + this.egw().lang('Tab');
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const app = opener?.framework.getApplicationByName(value);
|
||||||
|
if(app && app.displayName)
|
||||||
|
{
|
||||||
|
option.label = app.displayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(e)
|
||||||
|
{
|
||||||
|
// ignore security exception, if opener is not accessible
|
||||||
|
}
|
||||||
|
this.select_options.concat(option);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
super.value = new_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get value()
|
||||||
|
{
|
||||||
|
return super.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("et2-select-tab", Et2SelectTab);
|
@ -7,9 +7,9 @@
|
|||||||
* @author Nathan Gray
|
* @author Nathan Gray
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Et2Select} from "./Et2Select";
|
import {Et2Select} from "../Et2Select";
|
||||||
import {css} from "@lion/core";
|
import {css} from "lit";
|
||||||
import {SelectOption} from "./FindSelectOptions";
|
import {SelectOption} from "../FindSelectOptions";
|
||||||
|
|
||||||
export class Et2SelectThumbnail extends Et2Select
|
export class Et2SelectThumbnail extends Et2Select
|
||||||
{
|
{
|
14
api/js/etemplate/Et2Select/Select/Et2SelectTimezone.ts
Normal file
14
api/js/etemplate/Et2Select/Select/Et2SelectTimezone.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import {Et2Select} from "../Et2Select";
|
||||||
|
import {Et2StaticSelectMixin, StaticOptions} from "../StaticOptions";
|
||||||
|
|
||||||
|
export class Et2SelectTimezone extends Et2StaticSelectMixin(Et2Select)
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._static_options = StaticOptions.timezone(this, {other: this.other || []});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("et2-select-timezone", Et2SelectTimezone);
|
25
api/js/etemplate/Et2Select/Select/Et2SelectYear.ts
Normal file
25
api/js/etemplate/Et2Select/Select/Et2SelectYear.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import {Et2SelectNumber} from "./Et2SelectNumber";
|
||||||
|
import {PropertyValues} from "lit";
|
||||||
|
import {StaticOptions} from "../StaticOptions";
|
||||||
|
|
||||||
|
export class Et2SelectYear extends Et2SelectNumber
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.min = -3;
|
||||||
|
this.max = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
updated(changedProperties : PropertyValues)
|
||||||
|
{
|
||||||
|
super.updated(changedProperties);
|
||||||
|
|
||||||
|
if(changedProperties.has('min') || changedProperties.has('max') || changedProperties.has('interval') || changedProperties.has('suffix'))
|
||||||
|
{
|
||||||
|
this._static_options = StaticOptions.year(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("et2-select-year", Et2SelectYear);
|
@ -1,5 +1,5 @@
|
|||||||
import {SelectOption} from "./FindSelectOptions";
|
import {SelectOption} from "./FindSelectOptions";
|
||||||
import {LitElement} from "@lion/core";
|
import {LitElement} from "lit";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EGroupware eTemplate2 - SelectAccountMixin
|
* EGroupware eTemplate2 - SelectAccountMixin
|
||||||
|
25
api/js/etemplate/Et2Select/SelectTypes.ts
Normal file
25
api/js/etemplate/Et2Select/SelectTypes.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* Import all our sub-types
|
||||||
|
*/
|
||||||
|
|
||||||
|
import './Select/Et2SelectAccount';
|
||||||
|
import './Select/Et2SelectApp';
|
||||||
|
import './Select/Et2SelectBitwise';
|
||||||
|
import './Select/Et2SelectBool';
|
||||||
|
import './Select/Et2SelectCategory';
|
||||||
|
import './Select/Et2SelectCountry';
|
||||||
|
import './Select/Et2SelectDay';
|
||||||
|
import './Select/Et2SelectDayOfWeek';
|
||||||
|
import './Select/Et2SelectEmail';
|
||||||
|
import './Select/Et2SelectHour';
|
||||||
|
import './Select/Et2SelectLang';
|
||||||
|
import './Select/Et2SelectMonth';
|
||||||
|
import './Select/Et2SelectNumber';
|
||||||
|
import './Select/Et2SelectPercent';
|
||||||
|
import './Select/Et2SelectPriority';
|
||||||
|
import './Select/Et2SelectReadonly';
|
||||||
|
import './Select/Et2SelectState';
|
||||||
|
import './Select/Et2SelectTab';
|
||||||
|
import './Select/Et2SelectThumbnail';
|
||||||
|
import './Select/Et2SelectTimezone';
|
||||||
|
import './Select/Et2SelectYear';
|
285
api/js/etemplate/Et2Select/StaticOptions.js
Normal file
285
api/js/etemplate/Et2Select/StaticOptions.js
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
/**
|
||||||
|
* Some static options, no need to transfer them over and over.
|
||||||
|
* We still need the same thing on the server side to validate, so they
|
||||||
|
* have to match. See Etemplate\Widget\Select::typeOptions()
|
||||||
|
* The type specific legacy options wind up in attrs.other, but should be explicitly
|
||||||
|
* defined and set.
|
||||||
|
*
|
||||||
|
* @param {type} widget
|
||||||
|
*/
|
||||||
|
import { sprintf } from "../../egw_action/egw_action_common";
|
||||||
|
import { cleanSelectOptions, find_select_options } from "./FindSelectOptions";
|
||||||
|
/**
|
||||||
|
* Base class for things that have static options
|
||||||
|
*
|
||||||
|
* We keep static options separate and concatenate them in to allow for extra options without
|
||||||
|
* overwriting them when we get static options from the server
|
||||||
|
*/
|
||||||
|
export const Et2StaticSelectMixin = (superclass) => {
|
||||||
|
class Et2StaticSelectOptions extends (superclass) {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
this.static_options = [];
|
||||||
|
this.fetchComplete = Promise.resolve();
|
||||||
|
// Trigger the options to get rendered into the DOM
|
||||||
|
this.requestUpdate("select_options");
|
||||||
|
}
|
||||||
|
get select_options() {
|
||||||
|
// @ts-ignore
|
||||||
|
const options = super.select_options || [];
|
||||||
|
// make sure result is unique
|
||||||
|
return [...new Map([...(this.static_options || []), ...options].map(item => [item.value, item])).values()];
|
||||||
|
}
|
||||||
|
set select_options(new_options) {
|
||||||
|
// @ts-ignore IDE doesn't recognise property
|
||||||
|
super.select_options = new_options;
|
||||||
|
}
|
||||||
|
set_static_options(new_static_options) {
|
||||||
|
this.static_options = new_static_options;
|
||||||
|
this.requestUpdate("select_options");
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Override the parent fix_bad_value() to wait for server-side options
|
||||||
|
* to come back before we check to see if the value is not there.
|
||||||
|
*/
|
||||||
|
fix_bad_value() {
|
||||||
|
this.fetchComplete.then(() => {
|
||||||
|
// @ts-ignore Doesn't know it's an Et2Select
|
||||||
|
if (typeof super.fix_bad_value == "function") {
|
||||||
|
// @ts-ignore Doesn't know it's an Et2Select
|
||||||
|
super.fix_bad_value();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Et2StaticSelectOptions;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Some options change, or are too complicated to have twice, so we get the
|
||||||
|
* options from the server once, then keep them to use if they're needed again.
|
||||||
|
* We use the options string to keep the different possibilities (eg. categories
|
||||||
|
* for different apps) separate.
|
||||||
|
*
|
||||||
|
* @param {et2_selectbox} widget Selectbox we're looking at
|
||||||
|
* @param {string} options_string
|
||||||
|
* @param {Object} attrs Widget attributes (not yet fully set)
|
||||||
|
* @param {boolean} return_promise true: always return a promise
|
||||||
|
* @returns {Object[]|Promise<Object[]>} Array of options, or empty and they'll get filled in later, or Promise
|
||||||
|
*/
|
||||||
|
export const StaticOptions = new class StaticOptionsType {
|
||||||
|
cached_server_side(widget, type, options_string, return_promise) {
|
||||||
|
// normalize options by removing trailing commas
|
||||||
|
options_string = options_string.replace(/,+$/, '');
|
||||||
|
const cache_id = widget.nodeName + '_' + options_string;
|
||||||
|
const cache_owner = widget.egw().getCache('Et2Select');
|
||||||
|
let cache = cache_owner[cache_id];
|
||||||
|
if (typeof cache === 'undefined') {
|
||||||
|
// Fetch with json instead of jsonq because there may be more than
|
||||||
|
// one widget listening for the response by the time it gets back,
|
||||||
|
// and we can't do that when it's queued.
|
||||||
|
const req = widget.egw().json('EGroupware\\Api\\Etemplate\\Widget\\Select::ajax_get_options', [type, options_string, widget.value]).sendRequest();
|
||||||
|
if (typeof cache === 'undefined') {
|
||||||
|
cache_owner[cache_id] = req;
|
||||||
|
}
|
||||||
|
cache = req;
|
||||||
|
}
|
||||||
|
if (typeof cache.then === 'function') {
|
||||||
|
// pending, wait for it
|
||||||
|
const promise = cache.then((response) => {
|
||||||
|
cache = cache_owner[cache_id] = response.response[0].data || undefined;
|
||||||
|
if (return_promise)
|
||||||
|
return cache;
|
||||||
|
// Set select_options in attributes in case we get a response before
|
||||||
|
// the widget is finished loading (otherwise it will re-set to {})
|
||||||
|
//widget.select_options = cache;
|
||||||
|
// Avoid errors if widget is destroyed before the timeout
|
||||||
|
if (widget && typeof widget.id !== 'undefined') {
|
||||||
|
if (typeof widget.set_static_options == "function") {
|
||||||
|
widget.set_static_options(cache);
|
||||||
|
}
|
||||||
|
else if (typeof widget.set_select_options == "function") {
|
||||||
|
widget.set_select_options(find_select_options(widget, {}, cache));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return return_promise ? promise : [];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Check that the value is in there
|
||||||
|
// Make sure we are not requesting server for an empty value option or
|
||||||
|
// other widgets but select-timezone as server won't find anything and
|
||||||
|
// it will fall into an infinitive loop, e.g. select-cat widget.
|
||||||
|
if (widget.value && widget.value != "" && widget.value != "0" && type == "select-timezone") {
|
||||||
|
var missing_option = true;
|
||||||
|
for (var i = 0; i < cache.length && missing_option; i++) {
|
||||||
|
if (cache[i].value == widget.value) {
|
||||||
|
missing_option = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Try again - ask the server with the current value this time
|
||||||
|
if (missing_option) {
|
||||||
|
delete cache_owner[cache_id];
|
||||||
|
return this.cached_server_side(widget, type, options_string);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (widget.value && widget && widget.get_value() !== widget.value) {
|
||||||
|
egw.window.setTimeout(function () {
|
||||||
|
// Avoid errors if widget is destroyed before the timeout
|
||||||
|
if (this.widget && typeof this.widget.id !== 'undefined') {
|
||||||
|
this.widget.set_value(this.widget.options.value);
|
||||||
|
}
|
||||||
|
}.bind({ widget: widget }), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return return_promise ? Promise.resolve(cache) : cache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cached_from_file(widget, file) {
|
||||||
|
const cache_owner = widget.egw().getCache('Et2Select');
|
||||||
|
let cache = cache_owner[file];
|
||||||
|
if (typeof cache === 'undefined') {
|
||||||
|
cache_owner[file] = cache = widget.egw().window.fetch(file)
|
||||||
|
.then((response) => {
|
||||||
|
// Get the options
|
||||||
|
if (!response.ok) {
|
||||||
|
throw response;
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(options => {
|
||||||
|
var _a;
|
||||||
|
// Need to clean the options because file may be key=>value, may have option list, may be mixed
|
||||||
|
cache_owner[file] = (_a = cleanSelectOptions(options)) !== null && _a !== void 0 ? _a : [];
|
||||||
|
return cache_owner[file];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (cache && typeof cache.then === "undefined") {
|
||||||
|
return Promise.resolve(cache);
|
||||||
|
}
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
priority(widget) {
|
||||||
|
return [
|
||||||
|
{ value: "1", label: 'low' },
|
||||||
|
{ value: "2", label: 'normal' },
|
||||||
|
{ value: "3", label: 'high' },
|
||||||
|
{ value: "0", label: 'undefined' }
|
||||||
|
];
|
||||||
|
}
|
||||||
|
bool(widget) {
|
||||||
|
return [
|
||||||
|
{ value: "0", label: 'no' },
|
||||||
|
{ value: "1", label: 'yes' }
|
||||||
|
];
|
||||||
|
}
|
||||||
|
month(widget) {
|
||||||
|
return [
|
||||||
|
{ value: "1", label: 'January' },
|
||||||
|
{ value: "2", label: 'February' },
|
||||||
|
{ value: "3", label: 'March' },
|
||||||
|
{ value: "4", label: 'April' },
|
||||||
|
{ value: "5", label: 'May' },
|
||||||
|
{ value: "6", label: 'June' },
|
||||||
|
{ value: "7", label: 'July' },
|
||||||
|
{ value: "8", label: 'August' },
|
||||||
|
{ value: "9", label: 'September' },
|
||||||
|
{ value: "10", label: 'October' },
|
||||||
|
{ value: "11", label: 'November' },
|
||||||
|
{ value: "12", label: 'December' }
|
||||||
|
];
|
||||||
|
}
|
||||||
|
number(widget, attrs = {
|
||||||
|
min: undefined,
|
||||||
|
max: undefined,
|
||||||
|
interval: undefined,
|
||||||
|
format: undefined
|
||||||
|
}) {
|
||||||
|
var _a, _b, _c, _d;
|
||||||
|
var options = [];
|
||||||
|
var min = (_a = attrs.min) !== null && _a !== void 0 ? _a : parseFloat(widget.min);
|
||||||
|
var max = (_b = attrs.max) !== null && _b !== void 0 ? _b : parseFloat(widget.max);
|
||||||
|
var interval = (_c = attrs.interval) !== null && _c !== void 0 ? _c : parseFloat(widget.interval);
|
||||||
|
var format = (_d = attrs.format) !== null && _d !== void 0 ? _d : '%d';
|
||||||
|
// leading zero specified in interval
|
||||||
|
if (widget.leading_zero && widget.leading_zero[0] == '0') {
|
||||||
|
format = '%0' + ('' + interval).length + 'd';
|
||||||
|
}
|
||||||
|
// Suffix
|
||||||
|
if (widget.suffix) {
|
||||||
|
format += widget.egw().lang(widget.suffix);
|
||||||
|
}
|
||||||
|
// Avoid infinite loop if interval is the wrong direction
|
||||||
|
if ((min <= max) != (interval > 0)) {
|
||||||
|
interval = -interval;
|
||||||
|
}
|
||||||
|
for (var i = 0, n = min; n <= max && i <= 100; n += interval, ++i) {
|
||||||
|
options.push({ value: "" + n, label: sprintf(format, n) });
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
percent(widget) {
|
||||||
|
return this.number(widget);
|
||||||
|
}
|
||||||
|
year(widget, attrs) {
|
||||||
|
if (typeof attrs != 'object') {
|
||||||
|
attrs = {};
|
||||||
|
}
|
||||||
|
var t = new Date();
|
||||||
|
attrs.min = t.getFullYear() + parseInt(widget.min);
|
||||||
|
attrs.max = t.getFullYear() + parseInt(widget.max);
|
||||||
|
return this.number(widget, attrs);
|
||||||
|
}
|
||||||
|
day(widget, attrs) {
|
||||||
|
attrs.other = [1, 31, 1];
|
||||||
|
return this.number(widget, attrs);
|
||||||
|
}
|
||||||
|
hour(widget, attrs) {
|
||||||
|
var options = [];
|
||||||
|
var timeformat = widget.egw().preference('common', 'timeformat');
|
||||||
|
for (var h = 0; h <= 23; ++h) {
|
||||||
|
options.push({
|
||||||
|
value: h,
|
||||||
|
label: timeformat == 12 ?
|
||||||
|
((12 ? h % 12 : 12) + ' ' + (h < 12 ? egw.lang('am') : egw.lang('pm'))) :
|
||||||
|
sprintf('%02d', h)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
app(widget, attrs) {
|
||||||
|
var options = ',' + (attrs.other || []).join(',');
|
||||||
|
return this.cached_server_side(widget, 'select-app', options);
|
||||||
|
}
|
||||||
|
cat(widget) {
|
||||||
|
var options = [widget.globalCategories, /*?*/ , widget.application, widget.parentCat];
|
||||||
|
if (typeof options[3] == 'undefined') {
|
||||||
|
options[3] = widget.application ||
|
||||||
|
// When the widget is first created, it doesn't have a parent and can't find it's instanceManager
|
||||||
|
(widget.getInstanceManager() && widget.getInstanceManager().app) ||
|
||||||
|
widget.egw().app_name();
|
||||||
|
}
|
||||||
|
return this.cached_server_side(widget, 'select-cat', options.join(','), true);
|
||||||
|
}
|
||||||
|
country(widget, attrs, return_promise) {
|
||||||
|
var options = ',';
|
||||||
|
return this.cached_server_side(widget, 'select-country', options, return_promise);
|
||||||
|
}
|
||||||
|
state(widget, attrs) {
|
||||||
|
var options = attrs.country_code ? attrs.country_code : 'de';
|
||||||
|
return this.cached_server_side(widget, 'select-state', options);
|
||||||
|
}
|
||||||
|
dow(widget, attrs) {
|
||||||
|
var options = (widget.rows || "") + ',' + (attrs.other || []).join(',');
|
||||||
|
return this.cached_server_side(widget, 'select-dow', options, true);
|
||||||
|
}
|
||||||
|
lang(widget, attrs) {
|
||||||
|
var options = ',' + (attrs.other || []).join(',');
|
||||||
|
return this.cached_server_side(widget, 'select-lang', options);
|
||||||
|
}
|
||||||
|
timezone(widget, attrs) {
|
||||||
|
var options = ',' + (attrs.other || []).join(',');
|
||||||
|
return this.cached_server_side(widget, 'select-timezone', options);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=StaticOptions.js.map
|
@ -8,11 +8,18 @@
|
|||||||
* @param {type} widget
|
* @param {type} widget
|
||||||
*/
|
*/
|
||||||
import {sprintf} from "../../egw_action/egw_action_common";
|
import {sprintf} from "../../egw_action/egw_action_common";
|
||||||
import {Et2SelectReadonly} from "./Et2SelectReadonly";
|
import {Et2SelectReadonly} from "./Select/Et2SelectReadonly";
|
||||||
import {cleanSelectOptions, find_select_options, SelectOption} from "./FindSelectOptions";
|
import {cleanSelectOptions, find_select_options, SelectOption} from "./FindSelectOptions";
|
||||||
import {Et2Select, Et2WidgetWithSelect} from "./Et2Select";
|
import {Et2Select, Et2WidgetWithSelect} from "./Et2Select";
|
||||||
|
import {state} from "lit/decorators/state.js";
|
||||||
|
|
||||||
export type Et2SelectWidgets = Et2Select | Et2WidgetWithSelect | Et2SelectReadonly;
|
export type Et2SelectWidgets = Et2Select | Et2WidgetWithSelect | Et2SelectReadonly;
|
||||||
|
type NumberOptions = {
|
||||||
|
min? : number,
|
||||||
|
max? : number,
|
||||||
|
interval? : number,
|
||||||
|
format? : string
|
||||||
|
};
|
||||||
|
|
||||||
// Export the Interface for TypeScript
|
// Export the Interface for TypeScript
|
||||||
type Constructor<T = {}> = new (...args : any[]) => T;
|
type Constructor<T = {}> = new (...args : any[]) => T;
|
||||||
@ -31,27 +38,25 @@ export const Et2StaticSelectMixin = <T extends Constructor<Et2WidgetWithSelect>>
|
|||||||
|
|
||||||
// Hold the static widget options separately so other options (like sent from server in sel_options) won't
|
// Hold the static widget options separately so other options (like sent from server in sel_options) won't
|
||||||
// conflict or be wiped out
|
// conflict or be wiped out
|
||||||
protected static_options : SelectOption[];
|
@state()
|
||||||
|
protected _static_options : SelectOption[] = [];
|
||||||
|
|
||||||
// If widget needs to fetch options from server, we might want to wait for them
|
// If widget needs to fetch options from server, we might want to wait for them
|
||||||
protected fetchComplete : Promise<SelectOption[] | void>;
|
@state()
|
||||||
|
protected fetchComplete : Promise<SelectOption[] | void> = Promise.resolve();
|
||||||
|
|
||||||
constructor(...args)
|
async getUpdateComplete() : Promise<boolean>
|
||||||
{
|
{
|
||||||
super(...args);
|
const result = await super.getUpdateComplete();
|
||||||
|
await this.fetchComplete;
|
||||||
this.static_options = [];
|
return result;
|
||||||
this.fetchComplete = Promise.resolve();
|
|
||||||
|
|
||||||
// Trigger the options to get rendered into the DOM
|
|
||||||
this.requestUpdate("select_options");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get select_options() : SelectOption[]
|
get select_options() : SelectOption[]
|
||||||
{
|
{
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const options = super.select_options || [];
|
const options = super.select_options || [];
|
||||||
const statics = this.static_options || [];
|
const statics = this._static_options || [];
|
||||||
|
|
||||||
if(options.length == 0)
|
if(options.length == 0)
|
||||||
{
|
{
|
||||||
@ -62,7 +67,7 @@ export const Et2StaticSelectMixin = <T extends Constructor<Et2WidgetWithSelect>>
|
|||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
// Merge & make sure result is unique
|
// Merge & make sure result is unique
|
||||||
return [...new Map([...(this.static_options || []), ...options].map(item =>
|
return [...new Map([...(this._static_options || []), ...options].map(item =>
|
||||||
[item.value, item])).values()];
|
[item.value, item])).values()];
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -75,7 +80,7 @@ export const Et2StaticSelectMixin = <T extends Constructor<Et2WidgetWithSelect>>
|
|||||||
|
|
||||||
set_static_options(new_static_options)
|
set_static_options(new_static_options)
|
||||||
{
|
{
|
||||||
this.static_options = new_static_options;
|
this._static_options = new_static_options;
|
||||||
this.requestUpdate("select_options");
|
this.requestUpdate("select_options");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,19 +278,14 @@ export const StaticOptions = new class StaticOptionsType
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
number(widget : Et2SelectWidgets, attrs = {
|
number(widget : Et2SelectWidgets, attrs : NumberOptions = {}) : SelectOption[]
|
||||||
min: undefined,
|
|
||||||
max: undefined,
|
|
||||||
interval: undefined,
|
|
||||||
format: undefined
|
|
||||||
}) : SelectOption[]
|
|
||||||
{
|
{
|
||||||
|
|
||||||
var options = [];
|
const options = [];
|
||||||
var min = parseFloat(attrs.min ?? widget.min ?? 1);
|
const min = parseFloat(attrs.min ?? widget.min ?? 1);
|
||||||
var max = parseFloat(attrs.max ?? widget.max ?? 10);
|
const max = parseFloat(attrs.max ?? widget.max ?? 10);
|
||||||
var interval = parseFloat(attrs.interval ?? widget.interval ?? 1);
|
let interval = parseFloat(attrs.interval ?? widget.interval ?? 1);
|
||||||
var format = attrs.format ?? '%d';
|
let format = attrs.format ?? '%d';
|
||||||
|
|
||||||
// leading zero specified in interval
|
// leading zero specified in interval
|
||||||
if(widget.leading_zero && widget.leading_zero[0] == '0')
|
if(widget.leading_zero && widget.leading_zero[0] == '0')
|
||||||
@ -313,7 +313,7 @@ export const StaticOptions = new class StaticOptionsType
|
|||||||
|
|
||||||
percent(widget : Et2SelectWidgets) : SelectOption[]
|
percent(widget : Et2SelectWidgets) : SelectOption[]
|
||||||
{
|
{
|
||||||
return this.number(widget, {min: 0, max: 100, interval: 10, format: undefined});
|
return this.number(widget, {min: 0, max: 100, interval: 10, format: "%d%%"});
|
||||||
}
|
}
|
||||||
|
|
||||||
year(widget : Et2SelectWidgets, attrs?) : SelectOption[]
|
year(widget : Et2SelectWidgets, attrs?) : SelectOption[]
|
||||||
@ -323,15 +323,14 @@ export const StaticOptions = new class StaticOptionsType
|
|||||||
attrs = {}
|
attrs = {}
|
||||||
}
|
}
|
||||||
var t = new Date();
|
var t = new Date();
|
||||||
attrs.min = t.getFullYear() + parseInt(widget.min);
|
attrs.min = t.getFullYear() + parseInt(attrs.min ?? widget.min ?? -3);
|
||||||
attrs.max = t.getFullYear() + parseInt(widget.max);
|
attrs.max = t.getFullYear() + parseInt(attrs.max ?? widget.max ?? 2);
|
||||||
return this.number(widget, attrs);
|
return this.number(widget, attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
day(widget : Et2SelectWidgets, attrs) : SelectOption[]
|
day(widget : Et2SelectWidgets, attrs) : SelectOption[]
|
||||||
{
|
{
|
||||||
attrs.other = [1, 31, 1];
|
return this.number(widget, {min: 1, max: 31, interval: 1});
|
||||||
return this.number(widget, attrs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hour(widget : Et2SelectWidgets, attrs) : SelectOption[]
|
hour(widget : Et2SelectWidgets, attrs) : SelectOption[]
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* @link https://www.egroupware.org
|
* @link https://www.egroupware.org
|
||||||
* @author Nathan Gray
|
* @author Nathan Gray
|
||||||
*/
|
*/
|
||||||
import {css, html, TemplateResult} from "@lion/core";
|
import {css, html, TemplateResult} from "lit";
|
||||||
import shoelace from "../../Styles/shoelace";
|
import shoelace from "../../Styles/shoelace";
|
||||||
import {Et2Tag} from "./Et2Tag";
|
import {Et2Tag} from "./Et2Tag";
|
||||||
|
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
* @link https://www.egroupware.org
|
* @link https://www.egroupware.org
|
||||||
* @author Nathan Gray
|
* @author Nathan Gray
|
||||||
*/
|
*/
|
||||||
import {classMap, css, html, nothing, PropertyValues, TemplateResult} from "@lion/core";
|
import {css, html, nothing, PropertyValues, TemplateResult} from "lit";
|
||||||
|
import {classMap} from "lit/directives/class-map.js";
|
||||||
import shoelace from "../../Styles/shoelace";
|
import shoelace from "../../Styles/shoelace";
|
||||||
import {Et2Tag} from "./Et2Tag";
|
import {Et2Tag} from "./Et2Tag";
|
||||||
|
|
||||||
@ -95,8 +96,8 @@ export class Et2EmailTag extends Et2Tag
|
|||||||
this.onlyEmail = false;
|
this.onlyEmail = false;
|
||||||
this.handleMouseEnter = this.handleMouseEnter.bind(this);
|
this.handleMouseEnter = this.handleMouseEnter.bind(this);
|
||||||
this.handleMouseLeave = this.handleMouseLeave.bind(this);
|
this.handleMouseLeave = this.handleMouseLeave.bind(this);
|
||||||
this.handleClick = this.handleClick.bind(this);
|
this.handleMouseDown = this.handleMouseDown.bind(this);
|
||||||
this.handleContactClick = this.handleContactClick.bind(this);
|
this.handleContactMouseDown = this.handleContactMouseDown.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback()
|
connectedCallback()
|
||||||
@ -166,7 +167,7 @@ export class Et2EmailTag extends Et2Tag
|
|||||||
this.shadowRoot.querySelector(".tag").classList.remove("contact_plus");
|
this.shadowRoot.querySelector(".tag").classList.remove("contact_plus");
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClick(e : MouseEvent)
|
handleMouseDown(e : MouseEvent)
|
||||||
{
|
{
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
@ -177,7 +178,7 @@ export class Et2EmailTag extends Et2Tag
|
|||||||
this.egw().open('', 'addressbook', 'add', extra);
|
this.egw().open('', 'addressbook', 'add', extra);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleContactClick(e : MouseEvent)
|
handleContactMouseDown(e : MouseEvent)
|
||||||
{
|
{
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.checkContact(this.value).then((result) =>
|
this.checkContact(this.value).then((result) =>
|
||||||
@ -255,7 +256,7 @@ export class Et2EmailTag extends Et2Tag
|
|||||||
|
|
||||||
button_or_avatar = html`
|
button_or_avatar = html`
|
||||||
<et2-lavatar slot="prefix" part="icon"
|
<et2-lavatar slot="prefix" part="icon"
|
||||||
@click=${this.handleContactClick}
|
@mousedown=${this.handleContactMouseDown}
|
||||||
.size=${style.getPropertyValue("--icon-width")}
|
.size=${style.getPropertyValue("--icon-width")}
|
||||||
lname=${option.lname || nothing}
|
lname=${option.lname || nothing}
|
||||||
fname=${option.fname || nothing}
|
fname=${option.fname || nothing}
|
||||||
@ -269,7 +270,7 @@ export class Et2EmailTag extends Et2Tag
|
|||||||
// Show a button to add as new contact
|
// Show a button to add as new contact
|
||||||
classes['tag__has_plus'] = true;
|
classes['tag__has_plus'] = true;
|
||||||
button_or_avatar = html`
|
button_or_avatar = html`
|
||||||
<et2-button-icon image="add" @click=${this.handleClick}
|
<et2-button-icon image="add" @mousedown=${this.handleMouseDown}
|
||||||
label="${this.egw().lang("Add a new contact")}"
|
label="${this.egw().lang("Add a new contact")}"
|
||||||
statustext="${this.egw().lang("Add a new contact")}">
|
statustext="${this.egw().lang("Add a new contact")}">
|
||||||
</et2-button-icon>`;
|
</et2-button-icon>`;
|
||||||
|
@ -8,7 +8,8 @@
|
|||||||
*/
|
*/
|
||||||
import {Et2Widget} from "../../Et2Widget/Et2Widget";
|
import {Et2Widget} from "../../Et2Widget/Et2Widget";
|
||||||
import {SlTag} from "@shoelace-style/shoelace";
|
import {SlTag} from "@shoelace-style/shoelace";
|
||||||
import {classMap, css, html, TemplateResult} from "@lion/core";
|
import {css, html, TemplateResult} from "lit";
|
||||||
|
import {classMap} from "lit/directives/class-map.js";
|
||||||
import shoelace from "../../Styles/shoelace";
|
import shoelace from "../../Styles/shoelace";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,7 +24,6 @@ export class Et2Tag extends Et2Widget(SlTag)
|
|||||||
shoelace, css`
|
shoelace, css`
|
||||||
:host {
|
:host {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag--pill {
|
.tag--pill {
|
||||||
@ -113,11 +113,12 @@ export class Et2Tag extends Et2Widget(SlTag)
|
|||||||
<sl-icon-button
|
<sl-icon-button
|
||||||
part="remove-button"
|
part="remove-button"
|
||||||
exportparts="base:remove-button__base"
|
exportparts="base:remove-button__base"
|
||||||
name="x"
|
name="x-lg"
|
||||||
library="system"
|
library="system"
|
||||||
label=${this.egw().lang('remove')}
|
label=${this.egw().lang('remove')}
|
||||||
class="tag__remove"
|
class="tag__remove"
|
||||||
@click=${this.handleRemoveClick}
|
@click=${this.handleRemoveClick}
|
||||||
|
tabindex="-1"
|
||||||
></sl-icon-button>
|
></sl-icon-button>
|
||||||
`
|
`
|
||||||
: ''}
|
: ''}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* @link https://www.egroupware.org
|
* @link https://www.egroupware.org
|
||||||
* @author Nathan Gray
|
* @author Nathan Gray
|
||||||
*/
|
*/
|
||||||
import {css} from "@lion/core";
|
import {css} from "lit";
|
||||||
import shoelace from "../../Styles/shoelace";
|
import shoelace from "../../Styles/shoelace";
|
||||||
import {Et2Tag} from "./Et2Tag";
|
import {Et2Tag} from "./Et2Tag";
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ async function before(editable = true)
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
element = await fixture<Et2Select>(html`
|
element = await fixture<Et2Select>(html`
|
||||||
<et2-select label="I'm a select" value="one" multiple="true" .editModeEnabled=${editable}>
|
<et2-select label="I'm a select" value="one" multiple="true" .editModeEnabled=${editable}>
|
||||||
<sl-menu-item value="one">One</sl-menu-item>
|
<sl-option value="one">One</sl-option>
|
||||||
<sl-menu-item value="two">Two</sl-menu-item>
|
<sl-option value="two">Two</sl-option>
|
||||||
</et2-select>
|
</et2-select>
|
||||||
`);
|
`);
|
||||||
// Stub egw()
|
// Stub egw()
|
||||||
|
@ -57,7 +57,7 @@ describe('Et2EmailTag', () =>
|
|||||||
assert.equal(extra['presets[email]'], 'test@example.com');
|
assert.equal(extra['presets[email]'], 'test@example.com');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
component.handleClick(new MouseEvent('click'));
|
component.handleMouseDown(new MouseEvent('click'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should open addressbook CRM on avatar click', async() =>
|
it('should open addressbook CRM on avatar click', async() =>
|
||||||
@ -79,6 +79,6 @@ describe('Et2EmailTag', () =>
|
|||||||
assert.deepEqual(extra, {title: contact.n_fn, icon: contact.photo});
|
assert.deepEqual(extra, {title: contact.n_fn, icon: contact.photo});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await component.handleContactClick(new MouseEvent('click'));
|
await component.handleContactMouseDown(new MouseEvent('click'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -97,8 +97,8 @@ describe("Multiple", () =>
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
element = await fixture<Et2Select>(html`
|
element = await fixture<Et2Select>(html`
|
||||||
<et2-select label="I'm a select" multiple="true">
|
<et2-select label="I'm a select" multiple="true">
|
||||||
<sl-menu-item value="one">One</sl-menu-item>
|
<sl-option value="one">One</sl-option>
|
||||||
<sl-menu-item value="two">Two</sl-menu-item>
|
<sl-option value="two">Two</sl-option>
|
||||||
</et2-select>
|
</et2-select>
|
||||||
`);
|
`);
|
||||||
element.set_value("one,two");
|
element.set_value("one,two");
|
||||||
@ -111,7 +111,7 @@ describe("Multiple", () =>
|
|||||||
|
|
||||||
it("Can remove tags", async() =>
|
it("Can remove tags", async() =>
|
||||||
{
|
{
|
||||||
assert.equal(element.querySelectorAll("sl-menu-item").length, 2, "Did not find options");
|
assert.equal(element.querySelectorAll("sl-option").length, 2, "Did not find options");
|
||||||
|
|
||||||
assert.sameMembers(element.value, ["one", "two"]);
|
assert.sameMembers(element.value, ["one", "two"]);
|
||||||
let tags = element.shadowRoot.querySelectorAll('.select__tags > *');
|
let tags = element.shadowRoot.querySelectorAll('.select__tags > *');
|
||||||
|
@ -86,7 +86,7 @@ describe("Select widget", () =>
|
|||||||
await element.updateComplete;
|
await element.updateComplete;
|
||||||
|
|
||||||
/** TESTING **/
|
/** TESTING **/
|
||||||
assert.equal(element.querySelectorAll("sl-menu-item").length, 2);
|
assert.equal(element.querySelectorAll("sl-option").length, 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("merges static options with sel_options", async() =>
|
it("merges static options with sel_options", async() =>
|
||||||
@ -109,7 +109,7 @@ describe("Select widget", () =>
|
|||||||
/** TESTING **/
|
/** TESTING **/
|
||||||
|
|
||||||
// @ts-ignore o.value isn't known by TypeScript, but it's there
|
// @ts-ignore o.value isn't known by TypeScript, but it's there
|
||||||
let option_keys = Object.values(element.querySelectorAll("sl-menu-item")).map(o => o.value);
|
let option_keys = Object.values(element.querySelectorAll("sl-option")).map(o => o.value);
|
||||||
assert.include(option_keys, "option", "Static option missing");
|
assert.include(option_keys, "option", "Static option missing");
|
||||||
assert.includeMembers(option_keys, ["1", "2", "option"], "Option mis-match");
|
assert.includeMembers(option_keys, ["1", "2", "option"], "Option mis-match");
|
||||||
assert.equal(option_keys.length, 3);
|
assert.equal(option_keys.length, 3);
|
||||||
|
@ -97,13 +97,13 @@ describe("Trigger search", () =>
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
element = await fixture<Et2Select>(html`
|
element = await fixture<Et2Select>(html`
|
||||||
<et2-select label="I'm a select" search=true>
|
<et2-select label="I'm a select" search=true>
|
||||||
<sl-menu-item value="one">One</sl-menu-item>
|
<sl-option value="one">One</sl-option>
|
||||||
<sl-menu-item value="two">Two</sl-menu-item>
|
<sl-option value="two">Two</sl-option>
|
||||||
<sl-menu-item value="three">Three</sl-menu-item>
|
<sl-option value="three">Three</sl-option>
|
||||||
<sl-menu-item value="four">Four</sl-menu-item>
|
<sl-option value="four">Four</sl-option>
|
||||||
<sl-menu-item value="five">Five</sl-menu-item>
|
<sl-option value="five">Five</sl-option>
|
||||||
<sl-menu-item value="six">Six</sl-menu-item>
|
<sl-option value="six">Six</sl-option>
|
||||||
<sl-menu-item value="seven">Seven</sl-menu-item>
|
<sl-option value="seven">Seven</sl-option>
|
||||||
</et2-select>
|
</et2-select>
|
||||||
`);
|
`);
|
||||||
// Stub egw()
|
// Stub egw()
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
||||||
import {SlSpinner} from "@shoelace-style/shoelace";
|
import {SlSpinner} from "@shoelace-style/shoelace";
|
||||||
import shoelace from "../Styles/shoelace";
|
import shoelace from "../Styles/shoelace";
|
||||||
import {css} from "@lion/core";
|
import {css} from "lit";
|
||||||
|
|
||||||
export class Et2Spinner extends Et2Widget(SlSpinner)
|
export class Et2Spinner extends Et2Widget(SlSpinner)
|
||||||
{
|
{
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
* @author Hadi Nategh
|
* @author Hadi Nategh
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {css, html} from "lit";
|
||||||
import {css, html, SlotMixin} from "@lion/core";
|
import {SlotMixin} from "@lion/core";
|
||||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
||||||
import '../Et2Image/Et2Image';
|
import '../Et2Image/Et2Image';
|
||||||
import {SlSwitch} from "@shoelace-style/shoelace";
|
import {SlSwitch} from "@shoelace-style/shoelace";
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {css} from "@lion/core";
|
import {css} from "lit";
|
||||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
||||||
import {SlTextarea} from "@shoelace-style/shoelace";
|
import {SlTextarea} from "@shoelace-style/shoelace";
|
||||||
import shoelace from "../Styles/shoelace";
|
import shoelace from "../Styles/shoelace";
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Et2Textbox} from "./Et2Textbox";
|
import {Et2Textbox} from "./Et2Textbox";
|
||||||
import {css, html, render} from "@lion/core";
|
import {css, html, render} from "lit";
|
||||||
|
|
||||||
export class Et2Number extends Et2Textbox
|
export class Et2Number extends Et2Textbox
|
||||||
{
|
{
|
||||||
|
@ -11,7 +11,9 @@
|
|||||||
import {Et2InvokerMixin} from "../Et2Url/Et2InvokerMixin";
|
import {Et2InvokerMixin} from "../Et2Url/Et2InvokerMixin";
|
||||||
import {Et2Textbox} from "./Et2Textbox";
|
import {Et2Textbox} from "./Et2Textbox";
|
||||||
import {Et2Dialog} from "../Et2Dialog/Et2Dialog";
|
import {Et2Dialog} from "../Et2Dialog/Et2Dialog";
|
||||||
import {classMap, html, ifDefined} from "@lion/core";
|
import {html} from "lit";
|
||||||
|
import {classMap} from "lit/directives/class-map.js";
|
||||||
|
import {ifDefined} from "lit/directives/if-defined.js";
|
||||||
import {egw} from "../../jsapi/egw_global";
|
import {egw} from "../../jsapi/egw_global";
|
||||||
|
|
||||||
const isChromium = navigator.userAgentData?.brands.some(b => b.brand.includes('Chromium'));
|
const isChromium = navigator.userAgentData?.brands.some(b => b.brand.includes('Chromium'));
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {css, PropertyValues} from "@lion/core";
|
import {css, PropertyValues} from "lit";
|
||||||
import {Regex} from "../Validators/Regex";
|
import {Regex} from "../Validators/Regex";
|
||||||
import {SlInput} from "@shoelace-style/shoelace";
|
import {SlInput} from "@shoelace-style/shoelace";
|
||||||
import shoelace from "../Styles/shoelace";
|
import shoelace from "../Styles/shoelace";
|
||||||
|
@ -8,7 +8,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable import/no-extraneous-dependencies */
|
/* eslint-disable import/no-extraneous-dependencies */
|
||||||
import {css, dedupeMixin, html, LitElement, SlotMixin} from '@lion/core';
|
import {css, html, LitElement} from 'lit';
|
||||||
|
import {dedupeMixin, SlotMixin} from '@lion/core';
|
||||||
import {Et2InputWidget, Et2InputWidgetInterface} from "../Et2InputWidget/Et2InputWidget";
|
import {Et2InputWidget, Et2InputWidgetInterface} from "../Et2InputWidget/Et2InputWidget";
|
||||||
import {colorsDefStyles} from "../Styles/colorsDefStyles";
|
import {colorsDefStyles} from "../Styles/colorsDefStyles";
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
import {Et2InvokerMixin} from "./Et2InvokerMixin";
|
import {Et2InvokerMixin} from "./Et2InvokerMixin";
|
||||||
import {Et2Textbox} from "../Et2Textbox/Et2Textbox";
|
import {Et2Textbox} from "../Et2Textbox/Et2Textbox";
|
||||||
import {colorsDefStyles} from "../Styles/colorsDefStyles";
|
import {colorsDefStyles} from "../Styles/colorsDefStyles";
|
||||||
import {css} from "@lion/core";
|
import {css} from "lit";
|
||||||
import {egw} from "../../jsapi/egw_global";
|
import {egw} from "../../jsapi/egw_global";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,7 +12,7 @@ import {Et2InvokerMixin} from "./Et2InvokerMixin";
|
|||||||
import {IsEmail} from "../Validators/IsEmail";
|
import {IsEmail} from "../Validators/IsEmail";
|
||||||
import {Et2Textbox} from "../Et2Textbox/Et2Textbox";
|
import {Et2Textbox} from "../Et2Textbox/Et2Textbox";
|
||||||
import {colorsDefStyles} from "../Styles/colorsDefStyles";
|
import {colorsDefStyles} from "../Styles/colorsDefStyles";
|
||||||
import {css} from "@lion/core";
|
import {css} from "lit";
|
||||||
import {egw} from "../../jsapi/egw_global";
|
import {egw} from "../../jsapi/egw_global";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
import {Et2UrlPhone} from "./Et2UrlPhone";
|
import {Et2UrlPhone} from "./Et2UrlPhone";
|
||||||
import {Et2UrlEmail} from "./Et2UrlEmail";
|
import {Et2UrlEmail} from "./Et2UrlEmail";
|
||||||
import {colorsDefStyles} from "../Styles/colorsDefStyles";
|
import {colorsDefStyles} from "../Styles/colorsDefStyles";
|
||||||
import {css} from "@lion/core";
|
import {css} from "lit";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @customElement et2-url-phone
|
* @customElement et2-url-phone
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
import {Et2InvokerMixin} from "./Et2InvokerMixin";
|
import {Et2InvokerMixin} from "./Et2InvokerMixin";
|
||||||
import {Et2Textbox} from "../Et2Textbox/Et2Textbox";
|
import {Et2Textbox} from "../Et2Textbox/Et2Textbox";
|
||||||
import {colorsDefStyles} from "../Styles/colorsDefStyles";
|
import {colorsDefStyles} from "../Styles/colorsDefStyles";
|
||||||
import {css} from "@lion/core";
|
import {css} from "lit";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @customElement et2-url-phone
|
* @customElement et2-url-phone
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
/* eslint-disable import/no-extraneous-dependencies */
|
/* eslint-disable import/no-extraneous-dependencies */
|
||||||
import {Et2Description} from "../Et2Description/Et2Description";
|
import {Et2Description} from "../Et2Description/Et2Description";
|
||||||
import {css, TemplateResult} from "@lion/core";
|
import {css, TemplateResult} from "lit";
|
||||||
import {Et2Url} from "./Et2Url";
|
import {Et2Url} from "./Et2Url";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {ExposeValue} from "../Expose/ExposeMixin";
|
import {ExposeValue} from "../Expose/ExposeMixin";
|
||||||
import {et2_vfsMode} from "../et2_widget_vfs";
|
import {et2_vfsMode} from "../et2_widget_vfs";
|
||||||
import {Et2ImageExpose} from "../Expose/Et2ImageExpose";
|
import {Et2ImageExpose} from "../Expose/Et2ImageExpose";
|
||||||
import {css, html} from "@lion/core";
|
import {css, html} from "lit";
|
||||||
|
|
||||||
|
|
||||||
export class Et2VfsMime extends Et2ImageExpose
|
export class Et2VfsMime extends Et2ImageExpose
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* @author Ralf Becker <rb@egroupware.org>
|
* @author Ralf Becker <rb@egroupware.org>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Et2SelectAccountReadonly} from "../Et2Select/Et2SelectReadonly";
|
import {Et2SelectAccountReadonly} from "../Et2Select/Select/Et2SelectReadonly";
|
||||||
|
|
||||||
export class Et2VfsUid extends Et2SelectAccountReadonly
|
export class Et2VfsUid extends Et2SelectAccountReadonly
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,8 @@ import {et2_cloneObject, et2_csvSplit} from "../et2_core_common";
|
|||||||
import type {IegwAppLocal} from "../../jsapi/egw_global";
|
import type {IegwAppLocal} from "../../jsapi/egw_global";
|
||||||
import {egw} from "../../jsapi/egw_global";
|
import {egw} from "../../jsapi/egw_global";
|
||||||
import {ClassWithAttributes, ClassWithInterfaces} from "../et2_core_inheritance";
|
import {ClassWithAttributes, ClassWithInterfaces} from "../et2_core_inheritance";
|
||||||
import {css, dedupeMixin, LitElement, PropertyValues, unsafeCSS} from "@lion/core";
|
import {css, LitElement, PropertyValues, unsafeCSS} from "lit";
|
||||||
|
import {dedupeMixin} from "@lion/core";
|
||||||
import type {et2_container} from "../et2_core_baseWidget";
|
import type {et2_container} from "../et2_core_baseWidget";
|
||||||
import type {et2_DOMWidget} from "../et2_core_DOMWidget";
|
import type {et2_DOMWidget} from "../et2_core_DOMWidget";
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
import {ExposeMixin, ExposeValue, MediaValue} from "./ExposeMixin";
|
import {ExposeMixin, ExposeValue, MediaValue} from "./ExposeMixin";
|
||||||
import {Et2Description} from "../Et2Description/Et2Description";
|
import {Et2Description} from "../Et2Description/Et2Description";
|
||||||
import {et2_IDetachedDOM} from "../et2_core_interfaces";
|
import {et2_IDetachedDOM} from "../et2_core_interfaces";
|
||||||
import {html} from "@lion/core";
|
import {html} from "lit";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows a description and if you click on it, it shows the file specified by href in gallery.
|
* Shows a description and if you click on it, it shows the file specified by href in gallery.
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
// Don't import this more than once
|
// Don't import this more than once
|
||||||
import "../../../../node_modules/blueimp-gallery/js/blueimp-gallery.min";
|
import "../../../../node_modules/blueimp-gallery/js/blueimp-gallery.min";
|
||||||
import {css, html, LitElement, render} from "@lion/core";
|
import {css, html, LitElement, render} from "lit";
|
||||||
import {et2_nextmatch} from "../et2_extension_nextmatch";
|
import {et2_nextmatch} from "../et2_extension_nextmatch";
|
||||||
import {Et2Dialog} from "../Et2Dialog/Et2Dialog";
|
import {Et2Dialog} from "../Et2Dialog/Et2Dialog";
|
||||||
import {ET2_DATAVIEW_STEPSIZE} from "../et2_dataview_controller";
|
import {ET2_DATAVIEW_STEPSIZE} from "../et2_dataview_controller";
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {classMap, css, html, LitElement} from "@lion/core";
|
import {css, html, LitElement} from "lit";
|
||||||
|
import {classMap} from "lit/directives/class-map.js";
|
||||||
import {Et2Widget} from "../../Et2Widget/Et2Widget";
|
import {Et2Widget} from "../../Et2Widget/Et2Widget";
|
||||||
import {et2_IDetachedDOM} from "../../et2_core_interfaces";
|
import {et2_IDetachedDOM} from "../../et2_core_interfaces";
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Et2Widget} from "../../Et2Widget/Et2Widget";
|
import {Et2Widget} from "../../Et2Widget/Et2Widget";
|
||||||
import {css} from "@lion/core";
|
import {css} from "lit";
|
||||||
import {SlDetails} from "@shoelace-style/shoelace";
|
import {SlDetails} from "@shoelace-style/shoelace";
|
||||||
import shoelace from "../../Styles/shoelace";
|
import shoelace from "../../Styles/shoelace";
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user