diff --git a/addressbook/js/app.ts b/addressbook/js/app.ts index 1fb2056d17..8510adbb92 100644 --- a/addressbook/js/app.ts +++ b/addressbook/js/app.ts @@ -21,18 +21,20 @@ import {fetchAll, nm_action, nm_compare_field} from "../../api/js/etemplate/et2_ import "./CRM"; import {egw} from "../../api/js/jsapi/egw_global"; import {LitElement} from "@lion/core"; -import {Et2SelectState} from "../../api/js/etemplate/Et2Select/Et2Select"; -import {Et2SelectCountry} from "../../api/js/etemplate/Et2Select/Et2SelectCountry"; +import {Et2SelectCountry} from "../../api/js/etemplate/Et2Select/Select/Et2SelectCountry"; + +import {Et2SelectState} from "../../api/js/etemplate/Et2Select/Select/Et2SelectState"; /** * Object to call app.addressbook.openCRMview with */ -export interface CrmParams { - contact_id: number|string; - crm_list?: "infolog"|"tracker"|"infolog-organisation"; // default: use preference - title?: string; // default: link-title of contact_id - icon?: string; // default: avatar for contact_id - index?: number; +export interface CrmParams +{ + contact_id : number | string; + crm_list? : "infolog" | "tracker" | "infolog-organisation"; // default: use preference + title? : string; // default: link-title of contact_id + icon? : string; // default: avatar for contact_id + index? : number; } /** diff --git a/api/js/etemplate/ActivateLinksDirective.ts b/api/js/etemplate/ActivateLinksDirective.ts index 810f2e1d42..b3f3870538 100644 --- a/api/js/etemplate/ActivateLinksDirective.ts +++ b/api/js/etemplate/ActivateLinksDirective.ts @@ -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"; /** diff --git a/api/js/etemplate/Et2Avatar/Et2Avatar.ts b/api/js/etemplate/Et2Avatar/Et2Avatar.ts index 81a959b6be..67dc3cef4e 100644 --- a/api/js/etemplate/Et2Avatar/Et2Avatar.ts +++ b/api/js/etemplate/Et2Avatar/Et2Avatar.ts @@ -9,7 +9,8 @@ */ 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 {et2_IDetachedDOM} from "../et2_core_interfaces"; import {egw} from "../../jsapi/egw_global"; diff --git a/api/js/etemplate/Et2Avatar/Et2AvatarGroup.ts b/api/js/etemplate/Et2Avatar/Et2AvatarGroup.ts index 638c48914e..b1b6e36c63 100644 --- a/api/js/etemplate/Et2Avatar/Et2AvatarGroup.ts +++ b/api/js/etemplate/Et2Avatar/Et2AvatarGroup.ts @@ -1,5 +1,6 @@ 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"; /** diff --git a/api/js/etemplate/Et2Avatar/Et2LAvatar.ts b/api/js/etemplate/Et2Avatar/Et2LAvatar.ts index 2185e8b5bf..458ea945f1 100644 --- a/api/js/etemplate/Et2Avatar/Et2LAvatar.ts +++ b/api/js/etemplate/Et2Avatar/Et2LAvatar.ts @@ -10,7 +10,7 @@ import {Et2Avatar} from "./Et2Avatar"; import shoelace from "../Styles/shoelace"; -import {css} from "@lion/core"; +import {css} from "lit"; export class Et2LAvatar extends Et2Avatar { diff --git a/api/js/etemplate/Et2Avatar/cropperStyles.ts b/api/js/etemplate/Et2Avatar/cropperStyles.ts index db4ba7de36..05d96b2371 100644 --- a/api/js/etemplate/Et2Avatar/cropperStyles.ts +++ b/api/js/etemplate/Et2Avatar/cropperStyles.ts @@ -1,7 +1,7 @@ /** * Cropper styles constant */ -import {css} from "@lion/core"; +import {css} from "lit"; /*! * Cropper.js v1.5.12 diff --git a/api/js/etemplate/Et2Button/ButtonMixin.ts b/api/js/etemplate/Et2Button/ButtonMixin.ts index 922c91ca0e..abf4f79a3e 100644 --- a/api/js/etemplate/Et2Button/ButtonMixin.ts +++ b/api/js/etemplate/Et2Button/ButtonMixin.ts @@ -9,7 +9,7 @@ */ -import {css, LitElement, PropertyValues} from "@lion/core"; +import {css, LitElement, PropertyValues} from "lit"; import '../Et2Image/Et2Image'; import shoelace from "../Styles/shoelace"; diff --git a/api/js/etemplate/Et2Button/Et2Button.ts b/api/js/etemplate/Et2Button/Et2Button.ts index 906ea90f4f..fa35b1dcae 100644 --- a/api/js/etemplate/Et2Button/Et2Button.ts +++ b/api/js/etemplate/Et2Button/Et2Button.ts @@ -13,7 +13,7 @@ import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget"; import '../Et2Image/Et2Image'; import {SlButton} from "@shoelace-style/shoelace"; import {ButtonMixin} from "./ButtonMixin"; -import {PropertyValues} from "@lion/core"; +import {PropertyValues} from "lit"; export class Et2Button extends ButtonMixin(Et2InputWidget(SlButton)) diff --git a/api/js/etemplate/Et2Button/Et2ButtonIcon.ts b/api/js/etemplate/Et2Button/Et2ButtonIcon.ts index 6f7673d431..3f610b00ad 100644 --- a/api/js/etemplate/Et2Button/Et2ButtonIcon.ts +++ b/api/js/etemplate/Et2Button/Et2ButtonIcon.ts @@ -14,7 +14,7 @@ import '../Et2Image/Et2Image'; import {SlIconButton} from "@shoelace-style/shoelace"; import {ButtonMixin} from "./ButtonMixin"; import shoelace from "../Styles/shoelace"; -import {css} from "@lion/core"; +import {css} from "lit"; export class Et2ButtonIcon extends ButtonMixin(Et2InputWidget(SlIconButton)) diff --git a/api/js/etemplate/Et2Button/Et2ButtonScroll.ts b/api/js/etemplate/Et2Button/Et2ButtonScroll.ts index a5bb50682b..870b83e272 100644 --- a/api/js/etemplate/Et2Button/Et2ButtonScroll.ts +++ b/api/js/etemplate/Et2Button/Et2ButtonScroll.ts @@ -8,7 +8,7 @@ * @author Nathan Gray */ -import {css, html, LitElement} from "@lion/core"; +import {css, html, LitElement} from "lit"; import {ButtonMixin} from "./ButtonMixin"; /** diff --git a/api/js/etemplate/Et2Checkbox/Et2Checkbox.ts b/api/js/etemplate/Et2Checkbox/Et2Checkbox.ts index 4141f41966..8e6460c42b 100644 --- a/api/js/etemplate/Et2Checkbox/Et2Checkbox.ts +++ b/api/js/etemplate/Et2Checkbox/Et2Checkbox.ts @@ -9,7 +9,7 @@ */ -import {css} from "@lion/core"; +import {css} from "lit"; import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget"; import '../Et2Image/Et2Image'; import {SlCheckbox} from "@shoelace-style/shoelace"; diff --git a/api/js/etemplate/Et2Checkbox/Et2CheckboxReadonly.ts b/api/js/etemplate/Et2Checkbox/Et2CheckboxReadonly.ts index ba63a515a0..843a6a8e5c 100644 --- a/api/js/etemplate/Et2Checkbox/Et2CheckboxReadonly.ts +++ b/api/js/etemplate/Et2Checkbox/Et2CheckboxReadonly.ts @@ -1,7 +1,8 @@ import {et2_IDetachedDOM} from "../et2_core_interfaces"; import {et2_checkbox} from "../et2_widget_checkbox"; 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"; /** diff --git a/api/js/etemplate/Et2Colorpicker/Et2Colorpicker.ts b/api/js/etemplate/Et2Colorpicker/Et2Colorpicker.ts index 24bbf0d450..49e8018b78 100644 --- a/api/js/etemplate/Et2Colorpicker/Et2Colorpicker.ts +++ b/api/js/etemplate/Et2Colorpicker/Et2Colorpicker.ts @@ -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 {SlColorPicker} from "@shoelace-style/shoelace"; import shoelace from "../Styles/shoelace"; diff --git a/api/js/etemplate/Et2Date/DateStyles.ts b/api/js/etemplate/Et2Date/DateStyles.ts index e0686c80da..382235508e 100644 --- a/api/js/etemplate/Et2Date/DateStyles.ts +++ b/api/js/etemplate/Et2Date/DateStyles.ts @@ -2,7 +2,7 @@ * Sharable date styles constant */ -import {css} from "@lion/core"; +import {css} from "lit"; import {colorsDefStyles} from "../Styles/colorsDefStyles"; import {cssImage} from "../Et2Widget/Et2Widget"; diff --git a/api/js/etemplate/Et2Date/Et2Date.ts b/api/js/etemplate/Et2Date/Et2Date.ts index c34dd1309e..514ffbe52f 100644 --- a/api/js/etemplate/Et2Date/Et2Date.ts +++ b/api/js/etemplate/Et2Date/Et2Date.ts @@ -9,7 +9,7 @@ */ -import {css, html} from "@lion/core"; +import {css, html} from "lit"; import 'lit-flatpickr'; import {dateStyles} from "./DateStyles"; import type {Instance} from 'flatpickr/dist/types/instance'; @@ -19,15 +19,11 @@ import flatpickr from "flatpickr"; import {egw} from "../../jsapi/egw_global"; import type {HTMLElementWithValue} from "@lion/form-core/types/FormControlMixinTypes"; import {Et2Textbox} from "../Et2Textbox/Et2Textbox"; -import {Et2ButtonIcon} from "../Et2Button/Et2ButtonIcon"; import {FormControlMixin} from "@lion/form-core"; import {LitFlatpickr} from "lit-flatpickr"; import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget"; import shoelace from "../Styles/shoelace"; -const textbox = new Et2Textbox(); -const button = new Et2ButtonIcon(); - // list of existing localizations from node_modules/flatpicker/dist/l10n directory: const l10n = [ 'ar', 'at', 'az', 'be', 'bg', 'bn', 'bs', 'cat', 'cs', 'cy', 'da', 'de', 'eo', 'es', 'et', 'fa', 'fi', 'fo', diff --git a/api/js/etemplate/Et2Date/Et2DateDuration.ts b/api/js/etemplate/Et2Date/Et2DateDuration.ts index ac53aa1131..200a0b0641 100644 --- a/api/js/etemplate/Et2Date/Et2DateDuration.ts +++ b/api/js/etemplate/Et2Date/Et2DateDuration.ts @@ -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 {sprintf} from "../../egw_action/egw_action_common"; import {dateStyles} from "./DateStyles"; @@ -132,6 +133,7 @@ export class Et2DateDuration extends Et2InputWidget(FormControlMixin(LitElement) } .input-group__after { + display: contents; margin-inline-start: var(--sl-input-spacing-medium); } @@ -612,9 +614,9 @@ export class Et2DateDuration extends Et2InputWidget(FormControlMixin(LitElement) ${[...this.displayFormat].map((format : string) => html` - + ${this.time_formats[format]} - ` + ` )} `; diff --git a/api/js/etemplate/Et2Date/Et2DateDurationReadonly.ts b/api/js/etemplate/Et2Date/Et2DateDurationReadonly.ts index 32a1832a6d..9463b00b44 100644 --- a/api/js/etemplate/Et2Date/Et2DateDurationReadonly.ts +++ b/api/js/etemplate/Et2Date/Et2DateDurationReadonly.ts @@ -8,7 +8,7 @@ */ -import {css, html} from "@lion/core"; +import {css, html} from "lit"; import {Et2DateDuration, formatOptions} from "./Et2DateDuration"; import {dateStyles} from "./DateStyles"; diff --git a/api/js/etemplate/Et2Date/Et2DateRange.ts b/api/js/etemplate/Et2Date/Et2DateRange.ts index 473899003c..97d9e852f9 100644 --- a/api/js/etemplate/Et2Date/Et2DateRange.ts +++ b/api/js/etemplate/Et2Date/Et2DateRange.ts @@ -1,6 +1,8 @@ import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget"; 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 {dateStyles} from "./DateStyles"; import {formatDate, parseDate} from "./Et2Date"; diff --git a/api/js/etemplate/Et2Date/Et2DateReadonly.ts b/api/js/etemplate/Et2Date/Et2DateReadonly.ts index c4dd1b4d7c..d6a70e9c06 100644 --- a/api/js/etemplate/Et2Date/Et2DateReadonly.ts +++ b/api/js/etemplate/Et2Date/Et2DateReadonly.ts @@ -8,7 +8,7 @@ */ -import {html, LitElement} from "@lion/core"; +import {html, LitElement} from "lit"; import {formatDate, parseDate} from "./Et2Date"; import {et2_IDetachedDOM} from "../et2_core_interfaces"; import {Et2Widget} from "../Et2Widget/Et2Widget"; diff --git a/api/js/etemplate/Et2Date/Et2DateSinceReadonly.ts b/api/js/etemplate/Et2Date/Et2DateSinceReadonly.ts index 30fbc7aaa9..1c3091de95 100644 --- a/api/js/etemplate/Et2Date/Et2DateSinceReadonly.ts +++ b/api/js/etemplate/Et2Date/Et2DateSinceReadonly.ts @@ -8,7 +8,7 @@ */ -import {html} from "@lion/core"; +import {html} from "lit"; import {parseDate, parseDateTime} from "./Et2Date"; import {Et2DateReadonly} from "./Et2DateReadonly"; diff --git a/api/js/etemplate/Et2Date/Et2DateTime.ts b/api/js/etemplate/Et2Date/Et2DateTime.ts index 1d27e10a6e..6240b89e52 100644 --- a/api/js/etemplate/Et2Date/Et2DateTime.ts +++ b/api/js/etemplate/Et2Date/Et2DateTime.ts @@ -9,7 +9,7 @@ */ -import {css} from "@lion/core"; +import {css} from "lit"; import {Et2Date, formatDate, formatDateTime} from "./Et2Date"; import type {Instance} from "flatpickr/dist/types/instance"; import {default as ShortcutButtonsPlugin} from "shortcut-buttons-flatpickr/dist/shortcut-buttons-flatpickr"; diff --git a/api/js/etemplate/Et2Description/Et2Description.ts b/api/js/etemplate/Et2Description/Et2Description.ts index 900d340527..3348fda86e 100644 --- a/api/js/etemplate/Et2Description/Et2Description.ts +++ b/api/js/etemplate/Et2Description/Et2Description.ts @@ -8,7 +8,7 @@ */ 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 {activateLinks} from "../ActivateLinksDirective"; import {et2_csvSplit} from "../et2_core_common"; diff --git a/api/js/etemplate/Et2Dialog/Et2Dialog.ts b/api/js/etemplate/Et2Dialog/Et2Dialog.ts index 2c96c2db55..91e88434a1 100644 --- a/api/js/etemplate/Et2Dialog/Et2Dialog.ts +++ b/api/js/etemplate/Et2Dialog/Et2Dialog.ts @@ -12,7 +12,12 @@ import {Et2Widget} from "../Et2Widget/Et2Widget"; import {et2_button} from "../et2_widget_button"; 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 {etemplate2} from "../etemplate2"; import {egw, IegwAppLocal} from "../../jsapi/egw_global"; diff --git a/api/js/etemplate/Et2DropdownButton/Et2DropdownButton.ts b/api/js/etemplate/Et2DropdownButton/Et2DropdownButton.ts index 3a74f97d92..d675d047a9 100644 --- a/api/js/etemplate/Et2DropdownButton/Et2DropdownButton.ts +++ b/api/js/etemplate/Et2DropdownButton/Et2DropdownButton.ts @@ -9,11 +9,11 @@ */ -import {Et2Button} from "../Et2Button/Et2Button"; 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 {SelectOption} from "../Et2Select/FindSelectOptions"; +import shoelace from "../Styles/shoelace"; /** * 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. * */ -export class Et2DropdownButton extends Et2WidgetWithSelectMixin(Et2Button) +export class Et2DropdownButton extends Et2WidgetWithSelectMixin(LitElement) { static get styles() { return [ ...super.styles, + shoelace, css` :host { /* 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 } + render() : TemplateResult { if(this.readonly) @@ -129,10 +131,10 @@ export class Et2DropdownButton extends Et2WidgetWithSelectMixin(Et2Button) ` : ''; return html` - + ${icon} ${this.noLang ? option.label : this.egw().lang(option.label)} - `; + `; } protected _handleSelect(ev) diff --git a/api/js/etemplate/Et2Favorites/Et2Favorites.ts b/api/js/etemplate/Et2Favorites/Et2Favorites.ts index ab8c4177bb..3eccdb09c5 100644 --- a/api/js/etemplate/Et2Favorites/Et2Favorites.ts +++ b/api/js/etemplate/Et2Favorites/Et2Favorites.ts @@ -10,7 +10,7 @@ */ 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 {et2_INextmatchHeader, et2_nextmatch} from "../et2_extension_nextmatch"; import {Et2Image} from "../Et2Image/Et2Image"; @@ -76,24 +76,24 @@ export class Et2Favorites extends Et2DropdownButton implements et2_INextmatchHea min-width: 15em; } - sl-menu-item:hover et2-image[src="trash"] { + sl-option:hover et2-image[src="trash"] { display: initial; } /* Add star icons - radio button is already in prefix */ - sl-menu-item::part(base) { + sl-option::part(base) { background-image: ${cssImage("fav_filter")}; background-repeat: no-repeat; background-size: 16px 16px; background-position: 5px center; } - sl-menu-item[checked]::part(base) { + sl-option[checked]::part(base) { background-image: ${cssImage("favorites")}; } - sl-menu-item:last-child::part(base) { + sl-option:last-child::part(base) { background-image: none; } `, @@ -185,11 +185,11 @@ export class Et2Favorites extends Et2DropdownButton implements et2_INextmatchHea statustext="${this.egw().lang("Delete")}">`; return html` - + ${option.value !== Et2Favorites.ADD_VALUE ? radio : ""} ${icon} ${option.label} - `; + `; } diff --git a/api/js/etemplate/Et2Iframe/Et2Iframe.ts b/api/js/etemplate/Et2Iframe/Et2Iframe.ts index 4b70a954cd..308b31ee75 100644 --- a/api/js/etemplate/Et2Iframe/Et2Iframe.ts +++ b/api/js/etemplate/Et2Iframe/Et2Iframe.ts @@ -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"; export class Et2Iframe extends Et2Widget(SlotMixin(LitElement)) diff --git a/api/js/etemplate/Et2Image/Et2Image.ts b/api/js/etemplate/Et2Image/Et2Image.ts index 2d33ea2eb4..891c87fcb8 100644 --- a/api/js/etemplate/Et2Image/Et2Image.ts +++ b/api/js/etemplate/Et2Image/Et2Image.ts @@ -8,8 +8,8 @@ * @author Nathan Gray */ - -import {css, html, LitElement, render, SlotMixin} from "@lion/core"; +import {css, html, LitElement, render} from "lit"; +import {SlotMixin} from "@lion/core"; import {Et2Widget} from "../Et2Widget/Et2Widget"; import {et2_IDetachedDOM} from "../et2_core_interfaces"; diff --git a/api/js/etemplate/Et2InputWidget/Et2InputWidget.ts b/api/js/etemplate/Et2InputWidget/Et2InputWidget.ts index 7a563f4c54..e1c3013fab 100644 --- a/api/js/etemplate/Et2InputWidget/Et2InputWidget.ts +++ b/api/js/etemplate/Et2InputWidget/Et2InputWidget.ts @@ -1,10 +1,11 @@ import {et2_IInput, et2_IInputNode, et2_ISubmitListener} from "../et2_core_interfaces"; 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 {ManualMessage} from "../Validators/ManualMessage"; import {LionValidationFeedback, Validator} from "@lion/form-core"; import {et2_csvSplit} from "../et2_core_common"; +import {dedupeMixin} from "@lion/core"; /** * This mixin will allow any LitElement to become an Et2InputWidget diff --git a/api/js/etemplate/Et2Link/Et2Link.ts b/api/js/etemplate/Et2Link/Et2Link.ts index f371086e69..08c82ca081 100644 --- a/api/js/etemplate/Et2Link/Et2Link.ts +++ b/api/js/etemplate/Et2Link/Et2Link.ts @@ -11,7 +11,7 @@ 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 {et2_IDetachedDOM} from "../et2_core_interfaces"; diff --git a/api/js/etemplate/Et2Link/Et2LinkAdd.ts b/api/js/etemplate/Et2Link/Et2LinkAdd.ts index d92a10ead8..a35736f5d5 100644 --- a/api/js/etemplate/Et2Link/Et2LinkAdd.ts +++ b/api/js/etemplate/Et2Link/Et2LinkAdd.ts @@ -1,6 +1,7 @@ import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget"; +import {css, html, LitElement, PropertyValues} from "lit"; 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 {LinkInfo} from "./Et2Link"; import {Et2Button} from "../Et2Button/Et2Button"; diff --git a/api/js/etemplate/Et2Link/Et2LinkAppSelect.ts b/api/js/etemplate/Et2Link/Et2LinkAppSelect.ts index a9c4cacec2..2890ae7ca1 100644 --- a/api/js/etemplate/Et2Link/Et2LinkAppSelect.ts +++ b/api/js/etemplate/Et2Link/Et2LinkAppSelect.ts @@ -1,9 +1,9 @@ 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"; -export class Et2LinkAppSelect extends SlotMixin(Et2Select) +export class Et2LinkAppSelect extends Et2Select { static get styles() { @@ -49,22 +49,11 @@ export class Et2LinkAppSelect extends SlotMixin(Et2Select) } }; - get slots() - { - return { - ...super.slots, - "": () => - { + /* + icon.style.width = "var(--icon-width)"; + icon.style.height = "var(--icon-width)"; - 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 __onlyApp : string; @@ -104,9 +93,6 @@ export class Et2LinkAppSelect extends SlotMixin(Et2Select) { 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) { // use preference @@ -118,7 +104,7 @@ export class Et2LinkAppSelect extends SlotMixin(Et2Select) this.value = this.egw().preference('link_app', appname || this.egw().app_name()); } // Register to - this.addEventListener("change", this._handleChange); + this.addEventListener("sl-change", this._handleChange); if(this.__onlyApp) { @@ -129,7 +115,7 @@ export class Et2LinkAppSelect extends SlotMixin(Et2Select) 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; } - _handleChange(e) + handleValueChange(e) { - // Set icon - this.querySelector(":scope > [slot='prefix']").setAttribute("src", this.egw().link_get_registry(this.value, 'icon')); + super.handleValueChange(e); // update preference let appname = ""; @@ -198,13 +183,21 @@ export class Et2LinkAppSelect extends SlotMixin(Et2Select) // Limit to one app 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) { 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 @@ -215,29 +208,27 @@ export class Et2LinkAppSelect extends SlotMixin(Et2Select) { 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) { this.value = this.egw().preference('link_app', this.egw().app_name()); } - this.select_options = cleanSelectOptions(select_options); + this.select_options = select_options; } _optionTemplate(option : SelectOption) : TemplateResult { return html` - + ${this.appIcons ? "" : option.label} - ${this._iconTemplate(option.value)} - `; - } - - _iconTemplate(appname) - { - let url = appname ? this.egw().link_get_registry(appname, 'icon') : ""; - return html` - `; + ${this._iconTemplate(option)} + `; } } diff --git a/api/js/etemplate/Et2Link/Et2LinkEntry.ts b/api/js/etemplate/Et2Link/Et2LinkEntry.ts index a9bc984538..6d08b9eaed 100644 --- a/api/js/etemplate/Et2Link/Et2LinkEntry.ts +++ b/api/js/etemplate/Et2Link/Et2LinkEntry.ts @@ -6,8 +6,8 @@ * @link https://www.egroupware.org * @author Nathan Gray */ - -import {css, html, LitElement, PropertyValues, SlotMixin} from "@lion/core"; +import {css, html, LitElement, PropertyValues} from "lit"; +import {SlotMixin} from "@lion/core"; import {Et2LinkAppSelect} from "./Et2LinkAppSelect"; import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget"; import {FormControlMixin} from "@lion/form-core"; diff --git a/api/js/etemplate/Et2Link/Et2LinkList.ts b/api/js/etemplate/Et2Link/Et2LinkList.ts index 93893da8cf..cf956ad3e9 100644 --- a/api/js/etemplate/Et2Link/Et2LinkList.ts +++ b/api/js/etemplate/Et2Link/Et2LinkList.ts @@ -10,8 +10,9 @@ */ -import {css, html, repeat, TemplateResult} from "@lion/core"; -import {Et2Link, LinkInfo} from "./Et2Link"; +import {css, html, TemplateResult} from "lit"; +import {repeat} from "lit/directives/repeat.js"; +import {LinkInfo} from "./Et2Link"; import {egw} from "../../jsapi/egw_global"; import {Et2LinkString} from "./Et2LinkString"; import {egwMenu} from "../../egw_action/egw_menu"; diff --git a/api/js/etemplate/Et2Link/Et2LinkSearch.ts b/api/js/etemplate/Et2Link/Et2LinkSearch.ts index efebaec920..d453f4c354 100644 --- a/api/js/etemplate/Et2Link/Et2LinkSearch.ts +++ b/api/js/etemplate/Et2Link/Et2LinkSearch.ts @@ -7,7 +7,7 @@ * @author Nathan Gray */ -import {css} from "@lion/core"; +import {css} from "lit"; import {Et2Select} from "../Et2Select/Et2Select"; import {Et2LinkAppSelect} from "./Et2LinkAppSelect"; import {Et2Link} from "./Et2Link"; @@ -86,9 +86,9 @@ export class Et2LinkSearch extends Et2Select super.updated(changedProperties); // Set a value we don't have as an option? That's OK, we'll just add it - if(changedProperties.has("value") && this.value && ( - this.menuItems && this.menuItems.length == 0 || - this.menuItems?.filter && this.menuItems.filter(item => this.value.includes(item.value)).length == 0 + if(changedProperties.has("value") && this.value && this.value.length > 0 && ( + this.getAllOptions().length == 0 || + this.getAllOptions().filter && this.getAllOptions().filter(item => this.getValueAsArray().includes(item.value)).length == 0 )) { this._missingOption(this.value) @@ -120,19 +120,16 @@ export class Et2LinkSearch extends Et2Select option.label = title || Et2Link.MISSING_TITLE; option.class = ""; // 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) { item.textContent = title; item.classList.remove("loading"); - this.syncItemsFromValue(); } else { // Not already rendered, update the select option this.requestUpdate("select_options"); - // update the displayed text - this.updateComplete.then(() => this.syncItemsFromValue()); } }); } diff --git a/api/js/etemplate/Et2Link/Et2LinkString.ts b/api/js/etemplate/Et2Link/Et2LinkString.ts index 29a1b3342c..70fc0d5ecc 100644 --- a/api/js/etemplate/Et2Link/Et2LinkString.ts +++ b/api/js/etemplate/Et2Link/Et2LinkString.ts @@ -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 {Et2Link, LinkInfo} from "./Et2Link"; import {et2_IDetachedDOM} from "../et2_core_interfaces"; diff --git a/api/js/etemplate/Et2Link/Et2LinkTo.ts b/api/js/etemplate/Et2Link/Et2LinkTo.ts index 8e454aa124..8a6f6f6308 100644 --- a/api/js/etemplate/Et2Link/Et2LinkTo.ts +++ b/api/js/etemplate/Et2Link/Et2LinkTo.ts @@ -12,7 +12,8 @@ import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget"; 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_file} from "../et2_widget_file"; import {Et2Button} from "../Et2Button/Et2Button"; diff --git a/api/js/etemplate/Et2Nextmatch/ColumnSelection.ts b/api/js/etemplate/Et2Nextmatch/ColumnSelection.ts index 8230ed9d4c..5292b747ee 100644 --- a/api/js/etemplate/Et2Nextmatch/ColumnSelection.ts +++ b/api/js/etemplate/Et2Nextmatch/ColumnSelection.ts @@ -1,7 +1,9 @@ /** * 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 {et2_nextmatch_customfields} from "../et2_extension_nextmatch"; import shoelace from "../Styles/shoelace"; @@ -85,7 +87,7 @@ export class Et2ColumnSelection extends Et2InputWidget(LitElement) { this.sort = Sortable.create(this.shadowRoot.querySelector('sl-menu'), { ghostClass: 'ui-fav-sortable-placeholder', - draggable: 'sl-menu-item.column', + draggable: 'sl-option.column', dataIdAttr: 'value', direction: 'vertical', delay: 25 @@ -141,10 +143,11 @@ export class Et2ColumnSelection extends Et2InputWidget(LitElement) return html``; } return html` - {item.checked = !checked}); + let checked = (this.shadowRoot.querySelector("sl-option")).checked || false; + this.shadowRoot.querySelectorAll('sl-option').forEach((item) => {item.checked = !checked}); } set columns(new_columns) diff --git a/api/js/etemplate/Et2Nextmatch/Headers/AccountFilterHeader.ts b/api/js/etemplate/Et2Nextmatch/Headers/AccountFilterHeader.ts index 3f6bfc3080..7e248d68b1 100644 --- a/api/js/etemplate/Et2Nextmatch/Headers/AccountFilterHeader.ts +++ b/api/js/etemplate/Et2Nextmatch/Headers/AccountFilterHeader.ts @@ -1,4 +1,4 @@ -import {Et2SelectAccount} from "../../Et2Select/Et2SelectAccount"; +import {Et2SelectAccount} from "../../Et2Select/Select/Et2SelectAccount"; import {et2_INextmatchHeader} from "../../et2_extension_nextmatch"; import {FilterMixin} from "./FilterMixin"; diff --git a/api/js/etemplate/Et2Nextmatch/Headers/CustomFilterHeader.ts b/api/js/etemplate/Et2Nextmatch/Headers/CustomFilterHeader.ts index 069a66fbf9..701fe8fe7c 100644 --- a/api/js/etemplate/Et2Nextmatch/Headers/CustomFilterHeader.ts +++ b/api/js/etemplate/Et2Nextmatch/Headers/CustomFilterHeader.ts @@ -2,7 +2,7 @@ import {loadWebComponent} from "../../Et2Widget/Et2Widget"; import {Et2Select} from "../../Et2Select/Et2Select"; import {Et2InputWidget, Et2InputWidgetInterface} from "../../Et2InputWidget/Et2InputWidget"; import {FilterMixin} from "./FilterMixin"; -import {html, LitElement} from "@lion/core"; +import {html, LitElement} from "lit"; /** * Filter by some other type of widget diff --git a/api/js/etemplate/Et2Nextmatch/Headers/FilterMixin.ts b/api/js/etemplate/Et2Nextmatch/Headers/FilterMixin.ts index 33d390a37d..c2ea61fd9a 100644 --- a/api/js/etemplate/Et2Nextmatch/Headers/FilterMixin.ts +++ b/api/js/etemplate/Et2Nextmatch/Headers/FilterMixin.ts @@ -1,6 +1,6 @@ import {egw} from "../../../jsapi/egw_global"; import {et2_INextmatchHeader, et2_nextmatch} from "../../et2_extension_nextmatch"; -import {LitElement} from "@lion/core"; +import {LitElement} from "lit"; // Export the Interface for TypeScript type Constructor = new (...args : any[]) => T; diff --git a/api/js/etemplate/Et2Portlet/Et2Portlet.ts b/api/js/etemplate/Et2Portlet/Et2Portlet.ts index 16cb372d6f..afad5417ec 100644 --- a/api/js/etemplate/Et2Portlet/Et2Portlet.ts +++ b/api/js/etemplate/Et2Portlet/Et2Portlet.ts @@ -15,8 +15,9 @@ import {SlCard} from "@shoelace-style/shoelace"; import interact from "@interactjs/interactjs"; import type {InteractEvent} from "@interactjs/core/InteractEvent"; import {egw} from "../../jsapi/egw_global"; -import {classMap, css, html, TemplateResult} from "@lion/core"; -import {HasSlotController} from "@shoelace-style/shoelace/dist/internal/slot"; +import {css, html, TemplateResult} from "lit"; +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 {Et2Dialog} from "../Et2Dialog/Et2Dialog"; import {et2_IResizeable} from "../et2_core_interfaces"; diff --git a/api/js/etemplate/Et2Select/Et2Listbox.ts b/api/js/etemplate/Et2Select/Et2Listbox.ts index 2d6bd7d850..384edc7f8b 100644 --- a/api/js/etemplate/Et2Select/Et2Listbox.ts +++ b/api/js/etemplate/Et2Select/Et2Listbox.ts @@ -2,7 +2,7 @@ import {SlMenu} from "@shoelace-style/shoelace"; import {Et2WidgetWithSelectMixin} from "./Et2WidgetWithSelectMixin"; import {RowLimitedMixin} from "../Layout/RowLimitedMixin"; import shoelace from "../Styles/shoelace"; -import {css, html, TemplateResult} from "@lion/core"; +import {css, html, TemplateResult} from "lit"; import {SelectOption} from "./FindSelectOptions"; /** @@ -40,7 +40,8 @@ export class Et2Listbox extends RowLimitedMixin(Et2WidgetWithSelectMixin(SlMenu) overflow-x: clip; } /* Ellipsis when too small */ - sl-menu-item.menu-item__label { + + sl-option.option__label { display: block; text-overflow: ellipsis; /* 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. // Pass option along so SearchMixin can grab it if needed return html` - ${icon} ${this.noLang ? option.label : this.egw().lang(option.label)} - `; + `; } } diff --git a/api/js/etemplate/Et2Select/Et2Select.ts b/api/js/etemplate/Et2Select/Et2Select.ts index 9a16de9cdc..12d767605b 100644 --- a/api/js/etemplate/Et2Select/Et2Select.ts +++ b/api/js/etemplate/Et2Select/Et2Select.ts @@ -8,20 +8,27 @@ */ -import {css, html, PropertyValues, TemplateResult} from "@lion/core"; -import {Et2StaticSelectMixin, StaticOptions as so} from "./StaticOptions"; +import {css, LitElement, nothing, PropertyValues, TemplateResult} from "lit"; +import {html, literal, StaticValue} from "lit/static-html.js"; import {Et2WidgetWithSelectMixin} from "./Et2WidgetWithSelectMixin"; -import {cleanSelectOptions, SelectOption} from "./FindSelectOptions"; -import {SlMenuItem, SlSelect} from "@shoelace-style/shoelace"; +import {SelectOption} from "./FindSelectOptions"; import shoelace from "../Styles/shoelace"; -import {Et2WithSearchMixin} from "./SearchMixin"; -import {Et2Tag} from "./Tag/Et2Tag"; -import {LionValidationFeedback} from "@lion/form-core"; import {RowLimitedMixin} from "../Layout/RowLimitedMixin"; +import {Et2Tag} from "./Tag/Et2Tag"; +import {Et2WithSearchMixin} from "./SearchMixin"; +import {property} from "lit/decorators/property.js"; +import {SlChangeEvent, SlSelect} from "@shoelace-style/shoelace"; +import {repeat} from "lit/directives/repeat.js"; // export Et2WidgetWithSelect which is used as type in other modules -export class Et2WidgetWithSelect extends RowLimitedMixin(Et2WidgetWithSelectMixin(SlSelect)) +export class Et2WidgetWithSelect extends RowLimitedMixin(Et2WidgetWithSelectMixin(LitElement)) { + // Gets an array of all elements + protected getAllOptions() + { + // @ts-ignore + return [...this.querySelectorAll('sl-option')]; + } }; /** @@ -65,14 +72,14 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) shoelace, super.styles, css` - :host { + :host { display: block; flex: 1 0 auto; --icon-width: 20px; - } - - - ::slotted(img), img { + } + + + ::slotted(img), img { vertical-align: middle; } @@ -137,7 +144,7 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) /* Hide dropdown trigger when multiple & readonly */ - :host([readonly][multiple]) .select__icon { + :host([readonly][multiple]) .select__expand-icon { display: none; } @@ -167,9 +174,7 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) width: fill-available; } - /* Style for the popup */ - - ::part(popup) { + ::part(listbox) { z-index: 1; background: var(--sl-input-background-color); padding: var(--sl-input-spacing-small); @@ -186,10 +191,11 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) ::part(display-label) { margin: 0; } - :host::part(display-label) { - max-height: 8em; + + :host::part(display-label) { + max-height: 8em; overflow-y: auto; - } + } ` ]; } @@ -216,6 +222,42 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) } } + + /** Placeholder text to show as a hint when the select is empty. */ + @property() placeholder = ''; + /** Allows more than one option to be selected. */ + @property({type: Boolean, reflect: true}) multiple = false; + /** Disables the select control. */ + @property({type: Boolean, reflect: true}) disabled = false; + + /** Adds a clear button when the select is not empty. */ + @property({type: Boolean}) clearable = false; + + /** The select's label. If you need to display HTML, use the `label` slot instead. */ + @property() label = ''; + + /** + * The preferred placement of the select's menu. Note that the actual placement may vary as needed to keep the listbox + * inside of the viewport. + */ + @property({reflect: true}) placement : 'top' | 'bottom' = 'bottom'; + + /** The select's help text. If you need to display HTML, use the `help-text` slot instead. */ + @property({attribute: 'help-text'}) helpText = ''; + + /** The select's required attribute. */ + @property({type: Boolean, reflect: true}) required = false; + + + private __value : string | string[] = ""; + + constructor() + { + super(); + this.hoist = true; + + this._tagTemplate = this._tagTemplate.bind(this); + } /** * List of properties that get translated * @@ -229,73 +271,16 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) } } - get slots() - { - return { - ...super.slots, - input: () => - { - return document.createElement("select"); - } - } - } - - /** - * If fix_bad_value() has to change the value, the update will trigger a change event. - * We don't want that event to fire since it happens too soon, before the handler is ready and app.ts has set up - * @type {boolean} - * @private - */ - private _block_change_event = false; - - /** - * Close the dropdown after user selects an option. - * Only applies when multiple="true". We initialize it in constructor to the common preference "select_multiple_close" - * @type {boolean} - * @private - */ - private _close_on_select : boolean; - - constructor(...args : any[]) - { - super(); - // We want this on more often than off - this.hoist = true; - - this._close_on_select = this.egw().preference("select_multiple_close") == "close"; - - this._triggerChange = this._triggerChange.bind(this); - this._doResize = this._doResize.bind(this); - this._handleMouseWheel = this._handleMouseWheel.bind(this); - this._handleMouseEnter = this._handleMouseEnter.bind(this); - this._handleMouseLeave = this._handleMouseLeave.bind(this); - this.handleOptionClick = this.handleOptionClick.bind(this); - this.handleKeyDown = this.handleKeyDown.bind(this); - this.handleTagRemove = this.handleTagRemove.bind(this); - } - connectedCallback() { super.connectedCallback(); - - // Re-bind focus/blur to after show/hide to avoid buggy behaviour like menu won't hide - this.removeEventListener("blur", this.et2HandleBlur); - this.removeEventListener("focus", this.et2HandleFocus); - this.addEventListener("sl-after-show", this.et2HandleFocus); - this.addEventListener("sl-after-hide", this.et2HandleBlur); - - this.addEventListener("mousewheel", this._handleMouseWheel); - this.addEventListener("mouseenter", this._handleMouseEnter); - this.addEventListener("mouseup", this.handleOptionClick); - this.addEventListener("keydown", this.handleKeyDown); - this.updateComplete.then(() => { this.addEventListener("sl-change", this._triggerChange); - this.addEventListener("sl-after-show", this._doResize) - - /* A hack to deal with how we do dark mode to avoid re-coloring the dropdown icon */ - this.shadowRoot.querySelector(".select__icon").setAttribute("part", "dropdown-icon"); + // Fixes missing empty label + this.select?.requestUpdate("value"); + // Fixes incorrect opening position + this.select?.popup.handleAnchorChange(); }); } @@ -303,17 +288,7 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) { super.disconnectedCallback(); - this.removeEventListener("mousewheel", this._handleMouseWheel); - this.removeEventListener("sl-clear", this._triggerChange) this.removeEventListener("sl-change", this._triggerChange); - this.removeEventListener("sl-after-show", this._doResize); - this.removeEventListener("sl-after-show", this.et2HandleFocus); - this.removeEventListener("sl-after-hide", this.et2HandleBlur); - } - - firstUpdated(changedProperties?) - { - super.firstUpdated(changedProperties); } _triggerChange(e) @@ -328,176 +303,21 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) } } - /** - * Change the menu sizing to allow the menu to be wider than the field width, but no smaller - * - * @param e - * @private - */ - private _doResize(e) + @property() + get value() { - this.menu.style.minWidth = this.menu.style.width; - this.menu.style.width = ""; + return this.multiple ? + this.__value ?? [] : + this.__value ?? ""; } - /** - * Stop scroll from bubbling so the sidemenu doesn't scroll too - * - * @param {MouseEvent} e - */ - private _handleMouseWheel(e : MouseEvent) + // @ts-ignore + set value(val : string | string[] | number | number[]) { - e.stopPropagation(); - } - - /** - * If rows=1 and multiple=true, when they put the mouse over the widget show all tags - * @param {MouseEvent} e - * @private - */ - private _handleMouseEnter(e : MouseEvent) - { - if(this.rows == 1 && this.multiple == true && this.value.length > 1) + if(typeof val === "undefined" || val == null) { - e.stopPropagation(); - let distance = (-1 * parseInt(getComputedStyle(this).height)) - 2; - - // Show all tags - this._oldMaxTagsVisible = this.maxTagsVisible; - this.maxTagsVisible = 0; - this._oldRows = this.rows; - this.rows = 10; - this.syncItemsFromValue(); - - // Bind to turn this all off - this.addEventListener("mouseleave", this._handleMouseLeave); - - // Popup - this might get wiped out next render(), might not - this.updateComplete.then(() => - { - let label = this.dropdown.querySelector(".select__label"); - let popup = document.createElement("sl-popup"); - popup.anchor = this; - popup.distance = distance; - popup.placement = "bottom"; - popup.strategy = "fixed"; - popup.active = true; - popup.sync = "width"; - popup.classList.add("hover__popup", "details", "hoist", "details__body"); - label.parentNode.insertBefore(popup, label); - popup.appendChild(label); - label.style.width = getComputedStyle(this).width; - label.style.margin = 0; - }); + val = ""; } - } - - /** - * If we're showing all rows because of _handleMouseEnter, reset when mouse leaves - * @param {MouseEvent} e - * @private - */ - private _handleMouseLeave(e : MouseEvent) - { - let popup = this.dropdown.querySelector("sl-popup"); - if(popup) - { - // Popup still here. Remove it - let label = popup.firstChild; - popup.parentNode.insertBefore(label, popup); - popup.remove(); - } - this.maxTagsVisible = this._oldMaxTagsVisible; - delete this._oldMaxTagsVisible; - this.rows = this._oldRows; - delete this._oldRows; - this.syncItemsFromValue(); - this.removeEventListener("mouseleave", this._handleMouseLeave); - this.dropdown.requestUpdate(); - } - - /** - * Get the node where we're putting the selection options - * - * @returns {HTMLElement} - */ - get _optionTargetNode() : HTMLElement - { - return this; - } - - /** - * Handle the case where there is no value set, or the value provided is not an option. - * If this happens, we choose the first option or empty label. - * - * Careful when this is called. We change the value here, so an infinite loop is possible if the widget has - * onchange. - * - */ - protected fix_bad_value() - { - // Stop if there are no options - if(!Array.isArray(this.select_options) || this.select_options.length == 0) - { - // Nothing to do here - return; - } - - let valueArray = Array.isArray(this.value) ? this.value : ( - !this.value ? [] : (this.multiple ? this.value.toString().split(',') : [this.value]) - ); - - // Check for value using missing options (deleted or otherwise not allowed) - let filtered = this.filterOutMissingOptions(valueArray); - if(filtered.length != valueArray.length) - { - this.value = filtered; - return; - } - - // Multiple is allowed to be empty, and if we don't have an emptyLabel or options nothing to do - if(this.multiple || (!this.emptyLabel && this.select_options.length === 0)) - { - return; - } - - // See if parent (search / free entry) is OK with it - if(super.fix_bad_value()) - { - return; - } - // If somebody gave '' as a select_option, let it be - if(this.value === '' && this.select_options.filter((option) => this.value === option.value).length == 1) - { - return; - } - // If no value is set, choose the first option - // Only do this on once during initial setup, or it can be impossible to clear the value - - // value not in options --> use emptyLabel, if exists, or first option otherwise - if(this.select_options.filter((option) => valueArray.find(val => val == option.value) || - Array.isArray(option.value) && option.value.filter(o => valueArray.find(val => val == o.value))).length === 0) - { - let oldValue = this.value; - this.value = this.emptyLabel ? "" : "" + this.select_options[0]?.value; - this._block_change_event = (oldValue != this.value); - // ""+ to cast value of 0 to "0", to not replace with "" - this.requestUpdate("value", oldValue); - } - } - - /** - * @deprecated use this.multiple = multi - * - * @param multi - */ - set_multiple(multi) - { - this.multiple = multi; - } - - set_value(val : string | string[] | number | number[]) - { if(typeof val === 'string' && val.indexOf(',') !== -1 && this.multiple) { val = val.split(','); @@ -506,12 +326,25 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) { val = val.toString(); } + const oldValue = this.value; if(Array.isArray(val)) { // Make sure value has no duplicates, and values are strings - val = [...new Set(val.map(v => typeof v === 'number' ? v.toString() : v || ''))]; + this.__value = [...new Set(val.map(v => (typeof v === 'number' ? v.toString() : v || '')))]; } - this.value = val || ''; + else + { + this.__value = val; + } + if(this.multiple && typeof this.__value == "string") + { + this.__value = this.__value != "" ? [this.__value] : []; + } + if(this.select) + { + this.select.value = this.__value; + } + this.requestUpdate("value", oldValue); } /** @@ -561,23 +394,10 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) return value; } - transformAttributes(attrs) - { - super.transformAttributes(attrs); - - // Deal with initial value of multiple set as CSV - if(this.multiple && typeof this.value == "string") - { - this.value = this.value.length ? this.value.split(",") : []; - } - } - /** - * Load extra stuff from the template node. - * Overridden from parent to force value to be "good", since this is the earliest place we have both value and - * select options when loading from a template. + * Add an option for the "empty label" option, used if there's no value * - * @param {Element} _node + * @returns {TemplateResult} */ loadFromXML(_node : Element) { @@ -592,20 +412,22 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) { super.willUpdate(changedProperties); - if(changedProperties.has('select_options') || changedProperties.has("value") || changedProperties.has('emptyLabel')) + if(changedProperties.has("multiple")) + { + this.value = this.__value; + } + if(changedProperties.has("select_options") || changedProperties.has("value") || changedProperties.has("emptyLabel")) { this.updateComplete.then(() => this.fix_bad_value()); } if(changedProperties.has("select_options") && changedProperties.has("value")) { - // Re-set value, the option for it may have just shown up - this.updateComplete.then(() => this.syncItemsFromValue()) } } /** * Override this method from SlSelect to stick our own tags in there - */ + * syncItemsFromValue() { if(typeof super.syncItemsFromValue === "function") @@ -620,7 +442,7 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) } let overflow = null; - if(this.maxTagsVisible > 0 && this.displayTags.length > this.maxTagsVisible) + if(this.maxOptionsVisible > 0 && this.displayTags.length > this.maxOptionsVisible) { overflow = this.displayTags.pop(); } @@ -650,7 +472,7 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) // Re-slice & add overflow tag if(overflow) { - this.displayTags = this.displayTags.slice(0, this.maxTagsVisible); + this.displayTags = this.displayTags.slice(0, this.maxOptionsVisible); this.displayTags.push(overflow); } else if(this.multiple && this.rows == 1 && this.readonly && this.value.length > 1) @@ -660,6 +482,7 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) ${this.value.length} `); } } + */ _emptyLabelTemplate() : TemplateResult { @@ -668,34 +491,50 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) return html``; } return html` - ${this.emptyLabel}`; + v == "")} + > + ${this.emptyLabel} + `; } + protected _optionsTemplate() : TemplateResult + { + return html`${repeat(this.select_options + // Filter out empty values if we have empty label to avoid duplicates + .filter(o => this.emptyLabel ? o.value !== '' : o), this._groupTemplate.bind(this)) + }`; + } /** - * Tag used for rendering options - * Used for finding & filtering options, they're created by the mixed-in class - * @returns {string} + * Used to render each option into the select + * + * @param {SelectOption} option + * @returns {TemplateResult} */ - public get optionTag() + protected _optionTemplate(option : SelectOption) : TemplateResult { - return "sl-menu-item"; - } + // Exclude non-matches when searching + if(typeof option.isMatch == "boolean" && !option.isMatch) + { + return html``; + } - - _optionTemplate(option : SelectOption) : TemplateResult - { // Tag used must match this.optionTag, but you can't use the variable directly. // Pass option along so SearchMixin can grab it if needed + const value = (option.value).replaceAll(" ", "___"); return html` - v == value)} + ?disabled=${option.disabled} > ${this._iconTemplate(option)} ${this.noLang ? option.label : this.egw().lang(option.label)} - `; + `; } /** @@ -704,9 +543,58 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) * @see createTagNode() * @returns {string} */ - public get tagTag() : string + public get tagTag() : StaticValue { - return "et2-tag"; + return literal`et2-tag`; + } + + /** + * Custom tag + * @param {Et2Option} option + * @param {number} index + * @returns {TemplateResult} + * @protected + */ + protected _tagTemplate(option : Et2Option, index : number) : TemplateResult + { + const readonly = (this.readonly || option && typeof (option.disabled) != "undefined" && option.disabled); + const isEditable = this.editModeEnabled && !readonly; + const image = this._createImage(option); + const tagName = this.tagTag; + return html` + <${tagName} + part="tag" + exportparts=" + base:tag__base, + content:tag__content, + remove-button:tag__remove-button, + remove-button__base:tag__remove-button__base, + icon:icon + " + class=${"search_tag " + option.classList.value} + ?pill=${this.pill} + size=${this.size} + ?removable=${!readonly} + ?readonly=${readonly} + ?editable=${isEditable} + value=${option.value} + @dblclick=${this._handleDoubleClick} + @click=${typeof this.onTagClick == "function" ? (e) => this.onTagClick(e, e.target) : nothing} + > + ${image ?? nothing} + ${option.getTextLabel().trim()} + + `; + } + + /** + * Additional customisation template + * @returns {*} + * @protected + */ + protected _extraTemplate() + { + return typeof super._extraTemplate == "function" ? super._extraTemplate() : nothing; } /** @@ -719,6 +607,8 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) */ protected _createTagNode(item) { + console.warn("Deprecated"); + debugger; let tag; if(typeof super._createTagNode == "function") { @@ -770,6 +660,7 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) this.dropdown.hide(); } + /* Parent should be fine now? private handleTagRemove(event : CustomEvent, option) { event.stopPropagation(); @@ -784,11 +675,12 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) } this.dispatchEvent(new CustomEvent('sl-input')); this.dispatchEvent(new CustomEvent('sl-change')); - this.syncItemsFromValue(); this.validate(); } } + */ + /** * Apply the user preference to close the dropdown if an option is clicked, even if multiple=true. * The default (from SlSelect) leaves the dropdown open for multiple=true @@ -798,21 +690,11 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) */ private handleOptionClick(event : MouseEvent) { - if(event.target == this) - { - // Don't hide dropdown when clicking on select. That can close it after user opens it. - return; - } + super.handleOptionClick(event); + if(this._close_on_select) { - this.dropdown.hide().then(() => - { - if(typeof this.handleMenuHide == "function") - { - // Make sure search gets hidden - this.handleMenuHide(); - } - }); + this.hide(); } } @@ -825,6 +707,14 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) this.dropdown?.hide(); } + + protected handleValueChange(e : SlChangeEvent) + { + const old_value = this.__value; + this.__value = this.select.value; + this.requestUpdate("value", old_value); + } + /** * Always close the dropdown if an option is clicked, even if multiple=true. This differs from SlSelect, * which leaves the dropdown open for multiple=true @@ -880,410 +770,82 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) return ""; } - public get _menuItems() : HTMLElement[] + /** Shows the listbox. */ + async show() { - return [...this.querySelectorAll(this.optionTag)]; + return this.select.show(); } - - /** - * Override parent to always call validate(), as our simple implementation needs to validate on clear as well. - * - * @param {string | false} err - */ - set_validation_error(err : string | false) + /** Hides the listbox. */ + async hide() { - super.set_validation_error(err); - if(err == false) + this.select.hide(); + } + + get open() + { + return this.select?.open ?? false; + } + + protected _renderOptions() + {return Promise.resolve();} + + protected get select() : SlSelect + { + return this.shadowRoot?.querySelector("sl-select"); + } + + public render() + { + const value = Array.isArray(this.value) ? + this.value.map(v => { return v.replaceAll(" ", "___"); }) : + (typeof this.value == "string" ? this.value.replaceAll(" ", "___") : ""); + + let icon : TemplateResult | typeof nothing = nothing; + if(!this.multiple) { - this.validate(); - } - } -} - -customElements.define("et2-select", Et2Select); -if(typeof customElements.get("lion-validation-feedback") === "undefined") -{ - customElements.define("lion-validation-feedback", LionValidationFeedback); -} - -export class Et2SelectApp extends Et2StaticSelectMixin(Et2Select) -{ - constructor() - { - super(...arguments); - } - - public connectedCallback() - { - super.connectedCallback() - this.fetchComplete = so.app(this, {}).then((options) => - { - this.set_static_options(cleanSelectOptions(options)); - }) - } -} - -// @ts-ignore TypeScript is not recognizing that this widget is a LitElement -customElements.define("et2-select-app", Et2SelectApp); - -export class Et2SelectTab extends Et2SelectApp -{ - constructor() - { - super(...arguments); - - 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 icon_option = this.select_options.find(o => (o.value == value || Array.isArray(value) && value.includes(o.value)) && o.icon); + if(icon_option) { - 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; - } -} - -// @ts-ignore TypeScript is not recognizing that this widget is a LitElement -customElements.define("et2-select-tab", Et2SelectTab); - -export class Et2SelectBitwise extends Et2StaticSelectMixin(Et2Select) -{ - set value(new_value) - { - /* beforeSendToClient does this, we don't want it twice - 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); + icon = this._iconTemplate(icon_option); } } - super.value = expanded_value; - */ - super.value = new_value; + return html` + + ${icon} + ${this._emptyLabelTemplate()} + ${this._optionsTemplate()} + ${this._extraTemplate()} + + + `; } } -// @ts-ignore TypeScript is not recognizing that this widget is a LitElement -customElements.define("et2-select-bitwise", Et2SelectBitwise); - -export class Et2SelectBool extends Et2StaticSelectMixin(Et2Select) +if(typeof customElements.get("et2-select") === "undefined") { - constructor() - { - super(...arguments); - - this.static_options = so.bool(this); - } + customElements.define("et2-select", Et2Select); } -// @ts-ignore TypeScript is not recognizing that this widget is a LitElement -customElements.define("et2-select-bool", Et2SelectBool); - - -export class Et2SelectDay extends Et2StaticSelectMixin(Et2Select) +declare global { - constructor() + interface HTMLElementTagNameMap { - super(...arguments); - - this.static_options = so.day(this, {}); + "et2-select" : Et2Select; } -} - -// @ts-ignore TypeScript is not recognizing that this widget is a LitElement -customElements.define("et2-select-day", Et2SelectDay); - -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 = so.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; - } -} - -// @ts-ignore TypeScript is not recognizing that this widget is a LitElement -customElements.define("et2-select-dow", Et2SelectDayOfWeek); - -export class Et2SelectHour extends Et2StaticSelectMixin(Et2Select) -{ - constructor() - { - super(...arguments); - - this.static_options = so.hour(this, {other: this.other || []}); - } -} - -// @ts-ignore TypeScript is not recognizing that this widget is a LitElement -customElements.define("et2-select-hour", Et2SelectHour); - -export class Et2SelectMonth extends Et2StaticSelectMixin(Et2Select) -{ - constructor() - { - super(...arguments); - - this.static_options = so.month(this); - } -} - -// @ts-ignore TypeScript is not recognizing that this widget is a LitElement -customElements.define("et2-select-month", Et2SelectMonth); - -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(...args) - { - super(...args); - 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 = so.number(this); - this.requestUpdate("select_options"); - } - } -} - -// @ts-ignore TypeScript is not recognizing that this widget is a LitElement -customElements.define("et2-select-number", Et2SelectNumber); - -export class Et2SelectPercent extends Et2SelectNumber -{ - constructor(...args) - { - super(...args); - this.min = 0; - this.max = 100; - this.interval = 10; - this.suffix = "%%"; - } -} - -// @ts-ignore TypeScript is not recognizing that this widget is a LitElement -customElements.define("et2-select-percent", Et2SelectPercent); - -export class Et2SelectPriority extends Et2StaticSelectMixin(Et2Select) -{ - constructor() - { - super(...arguments); - - this.static_options = so.priority(this); - } -} - -// @ts-ignore TypeScript is not recognizing that this widget is a LitElement -customElements.define("et2-select-priority", Et2SelectPriority); - -export class Et2SelectState extends Et2StaticSelectMixin(Et2Select) -{ - /** - * Two-letter ISO country code - */ - protected __country_code; - - static get properties() - { - return { - ...super.properties, - countryCode: String, - } - } - - constructor() - { - super(...arguments); - - this.countryCode = 'DE'; - } - - get countryCode() - { - return this.__countryCode; - } - - set countryCode(code : string) - { - this.__countryCode = code; - this.static_options = so.state(this, {country_code: code}); - this.requestUpdate("select_options"); - } - - set_country_code(code) - { - this.countryCode = code; - } -} - -// @ts-ignore TypeScript is not recognizing that this widget is a LitElement -customElements.define("et2-select-state", Et2SelectState); - -export class Et2SelectTimezone extends Et2StaticSelectMixin(Et2Select) -{ - constructor() - { - super(...arguments); - - const options = so.timezone(this, {other: this.other || []}); - if(Array.isArray(options)) - { - this.static_options = options; - } - } -} - -// @ts-ignore TypeScript is not recognizing that this widget is a LitElement -customElements.define("et2-select-timezone", Et2SelectTimezone); - -export class Et2SelectYear extends Et2SelectNumber -{ - constructor(args) - { - super(...args); - 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.select_options = so.year(this); - } - } -} - -// @ts-ignore TypeScript is not recognizing that this widget is a LitElement -customElements.define("et2-select-year", Et2SelectYear); - -export class Et2SelectLang extends Et2StaticSelectMixin(Et2Select) -{ - constructor() - { - super(...arguments); - - this.static_options = so.lang(this, {other: this.other || []}); - } -} - -// @ts-ignore TypeScript is not recognizing that this widget is a LitElement -customElements.define("et2-select-lang", Et2SelectLang); \ No newline at end of file +} \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/Et2WidgetWithSelectMixin.ts b/api/js/etemplate/Et2Select/Et2WidgetWithSelectMixin.ts index 3da224c1ae..da586de4d1 100644 --- a/api/js/etemplate/Et2Select/Et2WidgetWithSelectMixin.ts +++ b/api/js/etemplate/Et2Select/Et2WidgetWithSelectMixin.ts @@ -8,7 +8,8 @@ */ 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 {cleanSelectOptions, find_select_options, SelectOption} from "./FindSelectOptions"; 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 * 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: * - _optionTargetNode(): Return the HTMLElement where the "options" go. * - _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 * 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 e.stopPropagation()} + @blur=${this.stopEdit.bind(this)} + />`; + } + // I can't figure out how to get this full width via CSS + return html ` + + ${edit} + `; + } + _noResultsTemplate() { + return html ` +
${this.egw().lang("no suggestions")}
`; + } + /** + * Do we have the needed properties set, so we can actually do searching + * + * @returns {boolean} + */ + get searchEnabled() { + return !this.readonly && (this.search || this.searchUrl.length > 0); + } + get _searchInputNode() { + var _a; + return (_a = this._activeControls) === null || _a === void 0 ? void 0 : _a.querySelector("#search"); + } + get _editInputNode() { + var _a; + return (_a = this._activeControls) === null || _a === void 0 ? void 0 : _a.querySelector("input#edit"); + } + get _activeControls() { + var _a; + return ((_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(".search_input")) || + this.querySelector(".search_input"); + } + /** + * Only local options, excludes server options + * + * @protected + */ + get localItems() { + return this.querySelectorAll(this.optionTag + ":not(.remote)"); + } + /** + * Only remote options from search results + * @returns {NodeList} + * @protected + */ + get remoteItems() { + return this.querySelectorAll(this.optionTag + ".remote"); + } + /** + * Only free entries + * @returns {NodeList} + * @protected + */ + get freeEntries() { + return this.querySelectorAll(this.optionTag + ".freeEntry"); + } + get select_options() { + let options = []; + // Any provided options + options = options.concat(this.__select_options); + // Any kept remote options + options = options.concat(this._selected_remote); + if (this.allowFreeEntries) { + this.freeEntries.forEach((item) => { + if (!options.some(i => i.value == item.value)) { + options.push({ value: item.value, label: item.textContent, class: item.classList.toString() }); + } + }); + } + return options; + } + set select_options(options) { + var _a; + super.select_options = options; + // Remove any selected remote, they're real options now + for (let remote_index = this._selected_remote.length - 1; remote_index >= 0; remote_index--) { + let remote = this._selected_remote[remote_index]; + if (options.findIndex(o => o.value == remote.value) != -1) { + this._selected_remote.splice(remote_index, 1); + (_a = this.querySelector('[value="' + remote.value + '"]')) === null || _a === void 0 ? void 0 : _a.classList.remove("remote"); + } + } + } + get value() { + return super.value; + } + set value(new_value) { + super.value = new_value; + if (!new_value || !this.allowFreeEntries && !this.searchUrl) { + return; + } + } + /** + * Some [part of a] value is missing from the available options, but should be there, so find and add it. + * + * This is used when not all options are sent to the client (search, link list). Ideally we want to send + * the options for the current value, but sometimes this is not the best option so here we search or create + * the option as needed. These are not free entries, but need to match some list somewhere. + * + * @param {string} newValueElement + * @protected + */ + _missingOption(newValueElement) { + // Given a value we need to search for - this will add in all matches, including the one needed + this.remoteSearch(newValueElement, this.searchOptions).then((result) => { + const option = result.find(o => o.value == newValueElement); + if (option) { + this._selected_remote.push(option); + } + }); + } + fix_bad_value() { + if (!this.allowFreeEntries && !this.searchEnabled) { + // Let regular select deal with it + return false; + } + const valueArray = Array.isArray(this.value) ? this.value : (!this.value ? [] : this.value.toString().split(',')); + // Check any already found options + if (Object.values(this.menuItems).filter((option) => valueArray.find(val => val == option.value)).length === 0) { + return false; + } + return true; + // TODO? Should we check the server, or just be OK with it? Passing the "current" value in sel_options makes sure the value is there + } + _bindListeners() { + this.addEventListener("sl-clear", this._handleClear); + this.addEventListener("sl-after-show", this._handleAfterShow); + // Need our own change to catch the change event from search input + this.addEventListener("change", this._handleChange); + if (this.allowFreeEntries) { + this.addEventListener("paste", this._handlePaste); + } + this.updateComplete.then(() => { + var _a, _b; + // Search messes up event order. Since it throws its own bubbling change event, + // selecting an option fires 2 change events - 1 before the widget is finished adjusting, losing the value + // We catch all change events, then call this._oldChange only when value changes + this.removeEventListener("change", this._oldChange); + (_a = this._searchInputNode) === null || _a === void 0 ? void 0 : _a.removeEventListener("change", this._searchInputNode.handleChange); + (_b = this._searchInputNode) === null || _b === void 0 ? void 0 : _b.addEventListener("change", this._handleSearchChange); + this.dropdown.querySelector('.select__label').addEventListener("change", this.handleTagEdit); + }); + } + _unbindListeners() { + var _a; + this.removeEventListener("sl-select", this._handleSelect); + this.removeEventListener("sl-after-show", this._handleAfterShow); + this.removeEventListener("sl-clear", this._handleClear); + this.removeEventListener("change", this._handleChange); + this.removeEventListener("paste", this._handlePaste); + (_a = this._searchInputNode) === null || _a === void 0 ? void 0 : _a.removeEventListener("change", this._handleSearchChange); + } + handleMenuShow() { + var _a, _b; + if (this.readonly) { + return; + } + // Move search (& menu) if there's no value + (_a = this._activeControls) === null || _a === void 0 ? void 0 : _a.classList.toggle("novalue", this.multiple && this.value == '' || !this.multiple); + // Reset for parent calculations, will be adjusted after if needed + this.dropdown.setAttribute("distance", 0); + super.handleMenuShow(); + if (this.searchEnabled || this.allowFreeEntries) { + (_b = this._activeControls) === null || _b === void 0 ? void 0 : _b.classList.add("active"); + this._searchInputNode.focus(); + this._searchInputNode.select(); + // Hide edit explicitly since it's so hard via CSS + if (this._editInputNode) { + this._editInputNode.style.display = "none"; + } + } + if (this.editModeEnabled && this.allowFreeEntries && !this.multiple && this.value) { + this.startEdit(); + this._editInputNode.select(); + // Hide search explicitly since it's so hard via CSS + this._searchInputNode.style.display = "none"; + } + } + /** + * Reposition the dropdown to allow space for current value and search. If the dropdown was positioned above + * instead of below, we don't need the extra space - remove it. + */ + _handleAfterShow() { + // Need to give positioner a chance to position. + // If we call it right away, it has not updated. + // I haven't found an event or Promise to hook on to + window.setTimeout(() => { + var _a, _b, _c, _d, _e; + if (((_a = this.dropdown) === null || _a === void 0 ? void 0 : _a.getAttribute("distance")) && ((_c = (_b = this.dropdown) === null || _b === void 0 ? void 0 : _b.popup) === null || _c === void 0 ? void 0 : _c.dataset.currentPlacement) == "top") { + this.dropdown.setAttribute("distance", 0); + this.dropdown.reposition(); + } + else { + (_d = this.dropdown) === null || _d === void 0 ? void 0 : _d.setAttribute("distance", !this._activeControls || ((_e = this._activeControls) === null || _e === void 0 ? void 0 : _e.classList.contains("novalue")) ? + parseInt(getComputedStyle(this.control).getPropertyValue("border-width")) : + // Make room for search below + parseInt(getComputedStyle(this._activeControls).getPropertyValue("--sl-input-height-medium"))); + } + }, 100); + } + focus() { + var _a; + (_a = this.dropdown) === null || _a === void 0 ? void 0 : _a.show().then(() => { + this._searchInputNode.focus(); + }); + } + handleMenuHide() { + var _a; + if (this.readonly) { + return; + } + clearTimeout(this._searchTimeout); + super.handleMenuHide(); + // Reset display + if (this._searchInputNode) { + this._searchInputNode.style.display = ""; + } + if (this._editInputNode) { + this._editInputNode.style.display = ""; + } + if (this.searchEnabled || this.allowFreeEntries) { + (_a = this._activeControls) === null || _a === void 0 ? void 0 : _a.classList.remove("active"); + this.shadowRoot.querySelector('.select__label').style.display = ""; + } + } + _triggerChange(event) { + // Don't want searchbox events to trigger change event + if (event.target == this._searchInputNode) { + event.stopImmediatePropagation(); + event.preventDefault(); + return false; + } + return true; + } + _handleChange(event) { + if (event.target == this._searchInputNode) { + event.stopImmediatePropagation(); + event.preventDefault(); + return false; + } + return this._oldChange(event); + } + _handleDoubleClick(event) { + // No edit (shouldn't happen...) + if (!this.editModeEnabled) { + return; + } + // Find the tag + const path = event.composedPath(); + const tag = path.find((el) => el instanceof Et2Tag); + this.dropdown.hide(); + this.updateComplete.then(() => { + tag.startEdit(event); + }); + } + /** + * An option was selected + */ + handleMenuSelect(event) { + // 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)) { + this._selected_remote.push(Object.assign({}, event.detail.item.option)); + } + super.handleMenuSelect(event); + this.updateComplete.then(() => { + // If they just chose one from the list, re-focus the search + if (this.multiple && this.searchEnabled) { + this._searchInputNode.focus(); + this._searchInputNode.select(); + // If we were overlapping, reset + if (this._activeControls.classList.contains("novalue")) { + this.handleMenuShow(); + this._handleAfterShow(); + } + // Scroll the new tag into view + if (event.detail && event.detail.item) { + // Causes sidemenu (calendar) to scroll to top & get stuck + /* + this.updateComplete.then(() => + { + this.shadowRoot.querySelector("et2-tag[value='" + event.detail.item.value.replace(/'/g, "\\\'") + "']")?.scrollIntoView({block: "nearest"}); + }); + */ + } + } + else if (!this.multiple && this.searchEnabled) { + // Stop all the search stuff when they select an option + // this shows all non-matching options again + this._handleSearchAbort(event); + } + }); + } + /** + * Value was cleared + */ + _handleClear(e) { + // Only keep remote options that are still used + this._selected_remote = this._selected_remote.filter((option) => this.getValueAsArray().indexOf(option.value) !== -1); + if (!this.multiple && this.searchEnabled) { + this._handleSearchAbort(e); + // Restore label styling + this.shadowRoot.querySelector("[part='display-label']").style.display = ""; + // Start searching again + this.updateComplete.then(() => this.handleMenuShow()); + } + } + /** + * Handle blur from search field + * + * Either the user changed fields, or selected an option. For selecting don't interfere, but for + * changing fields we need to make sure the menu is hidden. + * + * @param event + */ + _handleSearchBlur(event) { + return __awaiter(this, void 0, void 0, function* () { + 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(); + }); + } + /** + * Handle keypresses inside the search input + * @param {KeyboardEvent} event + * @protected + */ + _handleSearchKeyDown(event) { + var _a; + clearTimeout(this._searchTimeout); + (_a = this._activeControls) === null || _a === void 0 ? void 0 : _a.classList.add("active"); + this.dropdown.show(); + // Pass off some keys to select + if (['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)) { + // Strip out hidden non-matching selected & disabled items so key navigation works + this.menuItems = this.menuItems.filter(i => !i.disabled); + return super.handleKeyDown(event); + } + event.stopPropagation(); + // Don't allow event to bubble or it will interact with select + event.stopImmediatePropagation(); + if (Et2WidgetWithSearch.TAG_BREAK.indexOf(event.key) !== -1 && this.allowFreeEntries && this.createFreeEntry(this._searchInputNode.value)) { + event.preventDefault(); + this._searchInputNode.value = ""; + this.dropdown.hide().then(() => __awaiter(this, void 0, void 0, function* () { + // update sizing / position before getting ready for another one + if (this.multiple) { + yield this.dropdown.show(); + this._searchInputNode.focus(); + } + })); + } + else if (event.key == "Enter") { + event.preventDefault(); + this.startSearch(); + return; + } + else if (event.key == "Escape") { + this._handleSearchAbort(event); + this.dropdown.hide(); + return; + } + // Start the search automatically if they have enough letters + // -1 because we're in keyDown handler, and value is from _before_ this key was pressed + if (this._searchInputNode.value.length >= Et2WidgetWithSearch.MIN_CHARS - 1) { + this._searchTimeout = window.setTimeout(() => { this.startSearch(); }, Et2WidgetWithSearch.SEARCH_TIMEOUT); + } + } + _handleEditKeyDown(event) { + // Stop propagation, or parent key handler will add again + event.stopImmediatePropagation(); + if (Et2WidgetWithSearch.TAG_BREAK.indexOf(event.key) !== -1 && this.allowFreeEntries) { + // Prevent default, since that would try to submit + event.preventDefault(); + this.stopEdit(); + } + // Abort edit, put original value back + else if (event.key == "Escape") { + this.stopEdit(true); + } + } + /** + * Sometimes users paste multiple comma separated values at once. Split them then handle normally. + * + * @param {ClipboardEvent} event + * @protected + */ + _handlePaste(event) { + event.preventDefault(); + let paste = event.clipboardData.getData('text'); + if (!paste) { + return; + } + const selection = window.getSelection(); + if (selection.rangeCount) { + selection.deleteFromDocument(); + } + let values = paste.split(/,\t/); + values.forEach(v => { + this.createFreeEntry(v.trim()); + }); + this.dropdown.hide(); + } + /** + * Start searching + * + * If we have local options, we'll search & display any matches. + * If serverUrl is set, we'll ask the server for results as well. + */ + startSearch() { + return __awaiter(this, void 0, void 0, function* () { + // Stop timeout timer + clearTimeout(this._searchTimeout); + // Show a spinner + let spinner = document.createElement("sl-spinner"); + spinner.slot = "suffix"; + this.appendChild(spinner); + // Hide clear button + let clear_button = this._searchInputNode.shadowRoot.querySelector(".input__clear"); + if (clear_button) { + clear_button.style.display = "none"; + } + // Clear previous results + this._clearResults(); + yield this.updateComplete; + // Start the searches + return Promise.all([ + this.localSearch(this._searchInputNode.value, this.searchOptions), + this.remoteSearch(this._searchInputNode.value, this.searchOptions) + ]).then(() => { + // Show no results indicator + if (this.menuItems.filter(e => !e.classList.contains("no-match")).length == 0) { + let target = this._optionTargetNode || this; + let temp = document.createElement("div"); + render(this._noResultsTemplate(), temp); + target.append(temp.children[0]); + } + // Remove spinner + spinner.remove(); + // Restore clear button + if (clear_button) { + 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(); + }); + }); + } + /** + * Clear search term and any search results + * + * Local options are not removed, but remote options are + */ + clearSearch() { + // Stop timeout timer + clearTimeout(this._searchTimeout); + this._clearResults(); + // Clear search term + if (this._searchInputNode) { + this._searchInputNode.value = ""; + } + } + _clearResults() { + var _a; + let target = this._optionTargetNode || this; + // Remove "no suggestions" + (_a = target.querySelector(".no-results")) === null || _a === void 0 ? void 0 : _a.remove(); + // Remove any previously selected remote options that aren't used anymore + this._selected_remote = this._selected_remote.filter((option) => { + return this.multiple ? this.value.indexOf(option.value) != -1 : this.value == option.value; + }); + // Remove remote options that aren't used + let keepers = this._selected_remote.reduce((prev, current) => { + return prev + ":not([value='" + ('' + current.value).replace(/'/g, "\\\'") + "'])"; + }, ""); + target.querySelectorAll(".remote" + keepers).forEach(o => o.remove()); + target.childNodes.forEach((n) => { + if (n.nodeType == Node.COMMENT_NODE) { + n.remove(); + } + }); + // Reset remaining options. It might be faster to re-create instead. + this._menuItems.forEach((item) => { + var _a; + item.disabled = ((_a = item.option) === null || _a === void 0 ? void 0 : _a.disabled) || false; + item.classList.remove("match"); + item.classList.remove("no-match"); + }); + } + /** + * Filter the local options + * + * @param {string} search + * @protected + */ + localSearch(search, options) { + return new Promise((resolve) => { + this.localItems.forEach((item) => { + let match = this.searchMatch(search, item); + 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); + }); + resolve(); + }); + } + /** + * Ask for remote options and add them in unconditionally + * @param {string} search + * @protected + */ + remoteSearch(search, options) { + if (!this.searchUrl) { + return Promise.resolve([]); + } + // Check our URL: JSON file or URL? + if (this.searchUrl.includes(".json")) { + // Get the file, search it + return this.jsonQuery(search, options); + } + else { + // Fire off the query to the server + return this.remoteQuery(search, options); + } + } + /** + * Search through a JSON file in the browser + * + * @param {string} search + * @param {object} options + * @protected + */ + jsonQuery(search, options) { + // Get the file + const controller = new AbortController(); + const signal = controller.signal; + let response_ok = false; + return StaticOptions.cached_from_file(this, this.searchUrl) + .then(options => { + // Filter the options + const lower_search = search.toLowerCase(); + const filtered = options.filter(option => { + return option.label.toLowerCase().includes(lower_search) || option.value.includes(search); + }); + // Limit results + const totalCount = filtered.length; + if (filtered.length > Et2WidgetWithSearch.RESULT_LIMIT) { + filtered.splice(Et2WidgetWithSearch.RESULT_LIMIT); + } + // Add the matches + this.processRemoteResults(filtered, totalCount); + return filtered; + }) + .catch((_err) => { + this.egw().message(_err.statusText || this.searchUrl, "error"); + return []; + }); + } + /** + * Actually query the server. + * + * This can be overridden to change request parameters or eg. send them as POST parameters. + * + * Default implementation here sends search string and options: + * - as two parameters to the AJAX function + * - and (additional) as GET parameters plus search string as "query" + * + * This is done to support as well the old taglist callbacks, as the regular select ones! + * + * @param {string} search + * @param {object} options + * @returns {any} + * @protected + */ + remoteQuery(search, options) { + // Include a limit, even if options don't, to avoid massive lists breaking the UI + let sendOptions = Object.assign({ num_rows: Et2WidgetWithSearch.RESULT_LIMIT }, options); + return this.egw().request(this.egw().link(this.egw().ajaxUrl(this.egw().decodePath(this.searchUrl)), Object.assign({ query: search }, sendOptions)), [search, sendOptions]).then((results) => { + // If results have a total included, pull it out. + // It will cause errors if left in the results + let total = null; + if (typeof results.total !== "undefined") { + total = results.total; + delete results.total; + } + let entries = cleanSelectOptions(results); + this.processRemoteResults(entries, total); + return entries; + }); + } + /** + * Add in remote results + * @param results + * @param totalResults If there are more results than were returned, total number of matches + * @protected + */ + processRemoteResults(entries, totalResults = 0) { + if (!(entries === null || entries === void 0 ? void 0 : entries.length)) { + return Promise.resolve(); + } + // Add a "remote" class so we can tell these apart from any local results + entries.forEach((entry) => entry.class = (entry.class || "") + " remote"); + let target = this._optionTargetNode || this; + if (target) { + // Add in remote options, avoiding duplicates + this.select_options.filter(function (item) { + let i = entries.findIndex(x => (x.value == item.value)); + if (i <= -1) { + entries.push(item); + } + return null; + }); + let options = html `${entries.map(this._optionTemplate.bind(this))}`; + /** + * 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); + } + }); + } + } + /** + * Check if one of our [local] items matches the search + * + * @param search + * @param item + * @returns {boolean} + * @protected + */ + searchMatch(search, item) { + var _a; + if (!item || !item.value) { + return false; + } + if ((_a = item.textContent) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(search.toLowerCase())) { + return true; + } + if (typeof item.value == "string") { + return item.value.includes(search.toLowerCase()); + } + return item.value == search; + } + /** + * Create an entry that is not in the options and add it to the value + * + * @param {string} text Used as both value and label + */ + createFreeEntry(text) { + var _a; + if (!text || !this.validateFreeEntry(text)) { + return false; + } + // Make sure not to double-add + if (!this.querySelector("[value='" + text.replace(/'/g, "\\\'") + "']") && !this.__select_options.find(o => o.value == text)) { + this.__select_options.push({ + value: text.trim(), + label: text.trim(), + class: "freeEntry" + }); + this.requestUpdate('select_options'); + } + // Make sure not to double-add + if (this.multiple && this.value.indexOf(text) == -1) { + this.value.push(text); + } + 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 (!this.readonly && ((_a = this._activeControls) === null || _a === void 0 ? void 0 : _a.classList.contains("novalue"))) { + this._searchInputNode.style.display = ""; + } + return true; + } + /** + * Check if a free entry value is acceptable. + * We use validators directly using the proposed value + * + * @param text + * @returns {boolean} + */ + validateFreeEntry(text) { + let validators = [...this.validators, ...this.defaultValidators]; + let result = validators.filter(v => v.execute(text, v.param, { node: this })); + return validators.length > 0 && result.length == 0 || validators.length == 0; + } + handleTagEdit(event) { + var _a; + let value = event.target.value; + let original = event.target.dataset.original_value; + if (!value || !this.allowFreeEntries || !this.validateFreeEntry(value)) { + // Not a good value, reset it. + event.target.variant = "danger"; + return false; + } + event.target.variant = "success"; + // Add to internal list + this.createFreeEntry(value); + // Remove original from value & DOM + if (value != original) { + if (this.multiple) { + this.value = this.value.filter(v => v !== original); + } + else { + this.value = value; + } + (_a = this.querySelector("[value='" + original.replace(/'/g, "\\\'") + "']")) === null || _a === void 0 ? void 0 : _a.remove(); + this.__select_options = this.__select_options.filter(v => v.value !== original); + } + } + /** + * Start editing the current value if multiple=false + * + * @param {Et2Tag} tag + */ + startEdit(tag) { + const tag_value = tag ? tag.value : this.value; + // hide the menu + this.dropdown.hide(); + waitForEvent(this, "sl-after-hide").then(() => { + // Turn on edit UI + this._activeControls.classList.add("editing", "active"); + // Pre-set value to tag value + this._editInputNode.style.display = ""; + this._editInputNode.value = tag_value; + this._editInputNode.focus(); + // If they abort the edit, they'll want the original back. + this._editInputNode.dataset.initial = tag_value; + }); + } + stopEdit(abort = false) { + var _a, _b; + // 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 (((_a = abort.relatedTarget) === null || _a === void 0 ? void 0 : _a.localName) == "sl-menu-item") { + return; + } + // Edit lost focus, accept changes + abort = false; + } + const original = this._editInputNode.dataset.initial; + delete this._editInputNode.dataset.initial; + let value = abort ? original : this._editInputNode.value; + this._editInputNode.value = ""; + if (value && value != original) { + this.createFreeEntry(value); + this.updateComplete.then(() => { + const item = this.querySelector("[value='" + value.replace(/'/g, "\\\'") + "']"); + item.dispatchEvent(new CustomEvent("sl-select", { detail: { item } })); + }); + } + // Remove original from value & DOM + if (value != original) { + if (this.multiple) { + this.value = this.value.filter(v => v !== original); + (_b = this.querySelector("[value='" + original.replace(/'/g, "\\\'") + "']")) === null || _b === void 0 ? void 0 : _b.remove(); + } + else { + this.value = value; + } + this.select_options = this.select_options.filter(v => v.value !== original); + } + this._activeControls.classList.remove("editing", "active"); + if (!this.multiple) { + this.updateComplete.then(() => __awaiter(this, void 0, void 0, function* () { + // Don't know why, but this doesn't always work leaving the value hidden by prefix + yield this.dropdown.hide(); + this.dropdown.classList.remove("select--open"); + this.dropdown.panel.setAttribute("hidden", ""); + })); + } + this.syncItemsFromValue(); + } + _handleSearchAbort(e) { + this._activeControls.classList.remove("active"); + this.clearSearch(); + this.syncItemsFromValue(); + } + /** + * et2-searchbox (SlInput) sends out an event on change. + * We don't care, and if we let it bubble it'll get in the way. + * @param e + * @protected + */ + _handleSearchChange(e) { + e.stopImmediatePropagation(); + e.preventDefault(); + return false; + } + } + /** + * When user is typing, we wait this long for them to be finished before we start the search + * @type {number} + * @protected + */ + Et2WidgetWithSearch.SEARCH_TIMEOUT = 500; + /** + * We need at least this many characters before we start the search + * + * @type {number} + * @protected + */ + Et2WidgetWithSearch.MIN_CHARS = 2; + /** + * Limit server searches to 100 results, matches Link::DEFAULT_NUM_ROWS + * @type {number} + */ + Et2WidgetWithSearch.RESULT_LIMIT = 100; + /** + * These characters will end a free tag + * @type {string[]} + */ + Et2WidgetWithSearch.TAG_BREAK = ["Tab", "Enter", ","]; + return Et2WidgetWithSearch; +}; +//# sourceMappingURL=SearchMixin.js.map \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/SearchMixin.ts b/api/js/etemplate/Et2Select/SearchMixin.ts index 42e80cd834..f5db4adebc 100644 --- a/api/js/etemplate/Et2Select/SearchMixin.ts +++ b/api/js/etemplate/Et2Select/SearchMixin.ts @@ -7,14 +7,13 @@ * @author Nathan Gray */ - -import {css, html, LitElement, render, SlotMixin} from "@lion/core"; +import {css, CSSResultGroup, html, LitElement, nothing, render, TemplateResult} from "lit"; import {cleanSelectOptions, SelectOption} from "./FindSelectOptions"; import {Validator} from "@lion/form-core"; import {Et2Tag} from "./Tag/Et2Tag"; import {SlMenuItem} from "@shoelace-style/shoelace"; -import {waitForEvent} from "@shoelace-style/shoelace/dist/internal/event"; import {StaticOptions} from "./StaticOptions"; +import {dedupeMixin} from "@open-wc/dedupe-mixin"; // Otherwise import gets stripped let keep_import : Et2Tag; @@ -66,6 +65,13 @@ export declare class SearchMixinInterface * Check a [local] item to see if it matches */ 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 */ -export const Et2WithSearchMixin = >(superclass : T) => +export const Et2WithSearchMixin = dedupeMixin(>(superclass : T) => { - class Et2WidgetWithSearch extends SlotMixin(superclass) + class Et2WidgetWithSearch extends superclass { static get properties() { @@ -105,54 +111,18 @@ export const Et2WithSearchMixin = >(superclass } } - static get styles() + static get styles() : CSSResultGroup { return [ // @ts-ignore ...(super.styles ? (Symbol.iterator in Object(super.styles) ? super.styles : [super.styles]) : []), 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 */ ::slotted(sl-spinner) { 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 */ .search_input #edit { display: none; @@ -163,36 +133,55 @@ export const Et2WithSearchMixin = >(superclass .search_input.editing #edit { 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-wrap: wrap; width: 100%; } - :host([search]:not([multiple])) .select--open .select__label { - margin: 0px; - } - :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 { + + :host([search]) sl-select[open]::part(display-input), :host([allowfreeentries]) sl-select[open]::part(display-input) { 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 */ - .select__prefix ::slotted(.search_input), .search_input { + .search_input { display: none; + /* See also etemplate2.css, searchbox border turned off in there */ + border: none; flex: 1 1 auto; + order: 2; margin-left: 0px; - width: 100%; height: var(--sl-input-height-medium); - position: absolute; + width: 100%; background-color: white; z-index: var(--sl-z-index-dropdown); } + :host([search]) et2-textbox::part(base) { + border: none; + box-shadow: none; + } + /* Search UI active - show textbox & stuff */ - ::slotted(.search_input.active), .search_input.active, + .search_input.active, .search_input.editing { display: flex; } @@ -204,7 +193,8 @@ export const Et2WithSearchMixin = >(superclass } /* Hide options that do not match current search text */ - ::slotted(.no-match) { + + .no-match { display: none; } /* Different cursor for editable tags */ @@ -221,10 +211,6 @@ export const Et2WithSearchMixin = >(superclass :host([readonly]) .form-control-input:focus-within { box-shadow: none; } - /* no menu */ - :host([readonly]) sl-menu { - display: none; - } /* normal cursor */ :host([readonly]) .select__control { cursor: initial; @@ -264,6 +250,11 @@ export const Et2WithSearchMixin = >(superclass // Hold the original option data from earlier search results, since we discard on subsequent search private _selected_remote = []; + // Hold current search results, selected or otherwise + private _remote_options = []; + + private _total_result_count = 0; + /** * These characters will end a free tag * @type {string[]} @@ -283,7 +274,7 @@ export const Et2WithSearchMixin = >(superclass // Hiding the selected options from the dropdown means we can't un-select the tags // hidden by the max limit. Prefer no limit. - this.maxTagsVisible = -1; + this.maxOptionsVisible = -1; this.validators = []; /** @@ -297,16 +288,19 @@ export const Et2WithSearchMixin = >(superclass */ this.defaultValidators = []; - this.handleMenuSelect = this.handleMenuSelect.bind(this); + this.handleOptionClick = this.handleOptionClick.bind(this); this._handleChange = this._handleChange.bind(this); this.handleTagEdit = this.handleTagEdit.bind(this); this._handleAfterShow = this._handleAfterShow.bind(this); + this._handleMenuHide = this._handleMenuHide.bind(this); this._handleSearchBlur = this._handleSearchBlur.bind(this); this._handleClear = this._handleClear.bind(this); this._handleDoubleClick = this._handleDoubleClick.bind(this); this._handleSearchAbort = this._handleSearchAbort.bind(this); + this._handleSearchClear = this._handleSearchClear.bind(this); this._handleSearchChange = this._handleSearchChange.bind(this); this._handleSearchKeyDown = this._handleSearchKeyDown.bind(this); + this._handleSearchMouseDown = this._handleSearchMouseDown.bind(this); this._handleEditKeyDown = this._handleEditKeyDown.bind(this); this._handlePaste = this._handlePaste.bind(this); } @@ -324,7 +318,6 @@ export const Et2WithSearchMixin = >(superclass return; } - this._addNodes(); this._bindListeners(); } @@ -363,7 +356,7 @@ export const Et2WithSearchMixin = >(superclass } else if(this.allowFreeEntries && this.multiple) { - this.value.forEach((e) => + this.getValueAsArray().forEach((e) => { if(!this.select_options.find(o => o.value == e)) { @@ -402,7 +395,7 @@ export const Et2WithSearchMixin = >(superclass } // 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 if(changedProperties.has("editModeEnabled") || changedProperties.has("readonly")) @@ -416,54 +409,33 @@ export const Et2WithSearchMixin = >(superclass } } - /** - * Add the nodes we need to search - adjust parent shadowDOM - * - * @protected - */ - protected _addNodes() + protected _extraTemplate() { - if(this._activeControls) + if(!this.searchEnabled && !this.editModeEnabled && !this.allowFreeEntries || this.readonly) { - // Already there - return; + return nothing; } - const div = document.createElement("div"); - div.classList.add("search_input"); - render(this._searchInputTemplate(), div); - if(!super.multiple) - { - div.slot = "prefix"; - this.appendChild(div); - return; - } - - super.updateComplete.then(() => - { - let control = this.shadowRoot.querySelector(".form-control-input"); - control.append(div); - }); + return html` + ${this._searchInputTemplate()} + ${this._moreResultsTemplate()} + `; } - /** - * Customise how tags are rendered. - * Override to add edit - * - * @param item - * @protected - */ - protected _createTagNode(item) + protected _moreResultsTemplate() { - let tag = document.createElement(this.tagTag); - tag.editable = this.editModeEnabled && !this.readonly; + if(this._total_result_count == 0 || this._total_result_count - this._remote_options.length == 0) + { + return nothing; + } + const more = this.egw().lang("%1 more...", this._total_result_count - this._remote_options.length); - return tag; + return html`${more}`; } protected _searchInputTemplate() { - let edit = null; + let edit = nothing; if(this.editModeEnabled) { edit = html`>(superclass @blur=${this.stopEdit.bind(this)} />`; } - // I can't figure out how to get this full width via CSS return html` - + ${edit} + `; } @@ -517,6 +493,10 @@ export const Et2WithSearchMixin = >(superclass this.querySelector(".search_input"); } + protected get optionTag() + { + return 'sl-option'; + } /** * Only local options, excludes server options @@ -525,7 +505,7 @@ export const Et2WithSearchMixin = >(superclass */ 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 = >(superclass */ protected get remoteItems() : NodeList { - return this.querySelectorAll(this.optionTag + ".remote"); + return this.select?.querySelectorAll(this.optionTag + ".remote") ?? []; } /** @@ -545,7 +525,7 @@ export const Et2WithSearchMixin = >(superclass */ protected get freeEntries() : NodeList { - return this.querySelectorAll(this.optionTag + ".freeEntry"); + return this.select?.querySelectorAll(this.optionTag + ".freeEntry") ?? []; } get select_options() : SelectOption[] @@ -558,6 +538,9 @@ export const Et2WithSearchMixin = >(superclass // Any kept remote options options = options.concat(this._selected_remote ?? []); + // Current search results + options = options.concat(this._remote_options ?? []); + if(this.allowFreeEntries) { this.freeEntries.forEach((item : SlMenuItem) => @@ -600,11 +583,11 @@ export const Et2WithSearchMixin = >(superclass { return; } - + // If widget is currently open, we may need to re-calculate search / dropdown positioning if(this.isOpen) { - this.handleMenuShow(); + this._handleMenuShow(); } } @@ -641,7 +624,7 @@ export const Et2WithSearchMixin = >(superclass const valueArray = Array.isArray(this.value) ? this.value : (!this.value ? [] : this.value.toString().split(',')); // 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; } @@ -653,7 +636,9 @@ export const Et2WithSearchMixin = >(superclass protected _bindListeners() { this.addEventListener("sl-clear", this._handleClear); + this.addEventListener("sl-show", this._handleMenuShow); 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 this.addEventListener("change", this._handleChange); @@ -673,14 +658,16 @@ export const Et2WithSearchMixin = >(superclass this._searchInputNode?.removeEventListener("change", this._searchInputNode.handleChange); 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() { this.removeEventListener("sl-select", this._handleSelect); + this.removeEventListener("sl-show", this._handleMenuShow); this.removeEventListener("sl-after-show", this._handleAfterShow); + this.removeEventListener("sl-hide", this._handleMenuHide); this.removeEventListener("sl-clear", this._handleClear) this.removeEventListener("change", this._handleChange); this.removeEventListener("paste", this._handlePaste); @@ -688,7 +675,7 @@ export const Et2WithSearchMixin = >(superclass this._searchInputNode?.removeEventListener("change", this._handleSearchChange); } - handleMenuShow() + _handleMenuShow() { if(this.readonly) { @@ -698,15 +685,11 @@ export const Et2WithSearchMixin = >(superclass this._activeControls?.classList.toggle("novalue", this.multiple && this.value == '' || !this.multiple); // Reset for parent calculations, will be adjusted after if needed - this.dropdown.setAttribute("distance", 0); - - super.handleMenuShow(); + //this.dropdown.setAttribute("distance", 0); if(this.searchEnabled || this.allowFreeEntries) { this._activeControls?.classList.add("active"); - this._searchInputNode.focus(); - this._searchInputNode.select(); // Hide edit explicitly since it's so hard via CSS if(this._editInputNode) { @@ -729,6 +712,12 @@ export const Et2WithSearchMixin = >(superclass */ _handleAfterShow() { + if(this.searchEnabled || this.allowFreeEntries) + { + this._searchInputNode.focus(); + this._searchInputNode.select(); + } + return; // Need to give positioner a chance to position. // If we call it right away, it has not updated. // I haven't found an event or Promise to hook on to @@ -749,24 +738,22 @@ export const Et2WithSearchMixin = >(superclass ); } }, 100); + } focus() { - this.dropdown?.show().then(() => - { - this._searchInputNode.focus(); - }); + this.show(); + this._searchInputNode.focus(); } - handleMenuHide() + _handleMenuHide() { if(this.readonly) { return; } - clearTimeout(this._searchTimeout); - super.handleMenuHide(); + this.clearSearch(); // Reset display if(this._searchInputNode) @@ -778,11 +765,7 @@ export const Et2WithSearchMixin = >(superclass this._editInputNode.style.display = ""; } - if(this.searchEnabled || this.allowFreeEntries) - { - this._activeControls?.classList.remove("active"); - this.shadowRoot.querySelector('.select__label').style.display = ""; - } + this._activeControls?.classList.remove("active"); } _triggerChange(event) @@ -830,14 +813,14 @@ export const Et2WithSearchMixin = >(superclass /** * An option was selected */ - handleMenuSelect(event) + handleOptionClick(event) { // 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(() => { @@ -850,12 +833,12 @@ export const Et2WithSearchMixin = >(superclass // If we were overlapping, reset if(this._activeControls.classList.contains("novalue")) { - this.handleMenuShow(); + this._handleMenuShow(); this._handleAfterShow(); } // Scroll the new tag into view - if(event.detail && event.detail.item) + if(event.detail) { // Causes sidemenu (calendar) to scroll to top & get stuck /* @@ -881,17 +864,14 @@ export const Et2WithSearchMixin = >(superclass _handleClear(e) { // 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) { this._handleSearchAbort(e); - // Restore label styling - this.shadowRoot.querySelector("[part='display-label']").style.display = ""; - // Start searching again - this.updateComplete.then(() => this.handleMenuShow()) + this._handleMenuShow(); } } @@ -906,17 +886,6 @@ export const Et2WithSearchMixin = >(superclass async _handleSearchBlur(event : FocusEvent) { 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 = >(superclass { clearTimeout(this._searchTimeout); this._activeControls?.classList.add("active"); - this.dropdown.show(); // Pass off some keys to select if(['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)) { // Strip out hidden non-matching selected & disabled items so key navigation works - this.menuItems = this.menuItems.filter(i => !i.disabled); - return super.handleKeyDown(event); + // TODO + return; } event.stopPropagation(); @@ -946,12 +914,12 @@ export const Et2WithSearchMixin = >(superclass { event.preventDefault(); this._searchInputNode.value = ""; - this.dropdown.hide().then(async() => + this.updateComplete.then(async() => { // update sizing / position before getting ready for another one if(this.multiple) { - await this.dropdown.show(); + // await this.show(); this._searchInputNode.focus(); } }); @@ -977,6 +945,17 @@ export const Et2WithSearchMixin = >(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) { // Stop propagation, or parent key handler will add again @@ -1037,7 +1016,7 @@ export const Et2WithSearchMixin = >(superclass // Show a spinner let spinner = document.createElement("sl-spinner"); - spinner.slot = "suffix"; + spinner.slot = "expand-icon"; this.appendChild(spinner); // Hide clear button @@ -1048,6 +1027,7 @@ export const Et2WithSearchMixin = >(superclass } // Clear previous results + this._total_result_count = 0; this._clearResults(); await this.updateComplete; @@ -1058,7 +1038,7 @@ export const Et2WithSearchMixin = >(superclass ]).then(() => { // 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 temp = document.createElement("div"); @@ -1074,13 +1054,6 @@ export const Et2WithSearchMixin = >(superclass { 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 = >(superclass n.remove(); } }) - - // Reset remaining options. It might be faster to re-create instead. - this._menuItems.forEach((item) => - { - item.disabled = item.option?.disabled || false; - item.classList.remove("match"); - item.classList.remove("no-match"); - }); + // Not searching anymore, clear flag + this.select_options.map((o) => o.isMatch = null); + this.requestUpdate("select_options"); } /** @@ -1148,14 +1116,11 @@ export const Et2WithSearchMixin = >(superclass { return new Promise((resolve) => { - this.localItems.forEach((item) => + this.select_options.forEach((option) => { - let match = this.searchMatch(search, item); - 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); + option.isMatch = this.searchMatch(search, option); }) + this.requestUpdate("select_options"); resolve(); }); } @@ -1208,13 +1173,13 @@ export const Et2WithSearchMixin = >(superclass return option.label.toLowerCase().includes(lower_search) || option.value.includes(search) }); // Limit results - const totalCount = filtered.length; + this._total_result_count = filtered.length; if(filtered.length > Et2WidgetWithSearch.RESULT_LIMIT) { filtered.splice(Et2WidgetWithSearch.RESULT_LIMIT); } // Add the matches - this.processRemoteResults(filtered, totalCount); + this.processRemoteResults(filtered); return filtered; }) .catch((_err) => @@ -1252,14 +1217,14 @@ export const Et2WithSearchMixin = >(superclass { // If results have a total included, pull it out. // It will cause errors if left in the results - let total = null; + this._total_result_count = results.length; if(typeof results.total !== "undefined") { - total = results.total; + this._total_result_count = results.total; delete results.total; } let entries = cleanSelectOptions(results); - this.processRemoteResults(entries, total); + this.processRemoteResults(entries); return entries; }); } @@ -1267,68 +1232,36 @@ export const Et2WithSearchMixin = >(superclass /** * Add in remote results * @param results - * @param totalResults If there are more results than were returned, total number of matches * @protected */ - protected processRemoteResults(entries, totalResults = 0) + protected processRemoteResults(entries) { if(!entries?.length) { return Promise.resolve(); } // Add a "remote" class so we can tell these apart from any local results - entries.forEach((entry) => entry.class = (entry.class || "") + " remote"); - - let target = this._optionTargetNode || this; - if(target) + entries.forEach((entry) => { - // Add in remote options, avoiding duplicates - this.select_options.filter(function(item) + entry.class = (entry.class || "") + " remote"; + // 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)); - if(i <= -1) - { - entries.push(item); - } - return null; - }); + entries.push(item); + } + return null; + }); - let options = html`${entries.map(this._optionTemplate.bind(this))}`; - - /** - * 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); - } - }); - } + this._remote_options = entries; + this.requestUpdate("select_options"); } /** @@ -1339,21 +1272,21 @@ export const Et2WithSearchMixin = >(superclass * @returns {boolean} * @protected */ - protected searchMatch(search, item) : boolean + protected searchMatch(search, option : SelectOption) : boolean { - if(!item || !item.value) + if(!option || !option.value) { return false; } - if(item.textContent?.toLowerCase().includes(search.toLowerCase())) + if(option.label?.toLowerCase().includes(search.toLowerCase())) { 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 = >(superclass this.requestUpdate('select_options'); } - // Make sure not to double-add - if(this.multiple && this.value.indexOf(text) == -1) + // Make sure not to double-add, but wait until the option is there + this.updateComplete.then(() => { - this.value.push(text); - } - else if(!this.multiple && this.value !== text) - { - this.value = text; - } - this.requestUpdate("value"); + if(this.multiple && this.getValueAsArray().indexOf(text) == -1) + { + let value = this.getValueAsArray(); + value.push(text); + this.value = 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(!this.readonly && this._activeControls?.classList.contains("novalue")) @@ -1478,7 +1416,7 @@ export const Et2WithSearchMixin = >(superclass // 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(abort.relatedTarget?.localName == "sl-menu-item") + if(abort.relatedTarget?.localName == this.optionTag) { return; } @@ -1530,14 +1468,12 @@ export const Et2WithSearchMixin = >(superclass this.dropdown.panel.setAttribute("hidden", ""); }); } - this.syncItemsFromValue(); } protected _handleSearchAbort(e) { this._activeControls.classList.remove("active"); this.clearSearch(); - this.syncItemsFromValue(); } /** @@ -1552,7 +1488,14 @@ export const Et2WithSearchMixin = >(superclass e.preventDefault(); return false; } + + protected _handleSearchClear(e) + { + e.stopImmediatePropagation(); + e.preventDefault(); + this.clearSearch(); + } } return Et2WidgetWithSearch as unknown as Constructor & T; -} \ No newline at end of file +}); \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/Et2SelectAccount.ts b/api/js/etemplate/Et2Select/Select/Et2SelectAccount.ts similarity index 77% rename from api/js/etemplate/Et2Select/Et2SelectAccount.ts rename to api/js/etemplate/Et2Select/Select/Et2SelectAccount.ts index fc45fab108..537d0cb213 100644 --- a/api/js/etemplate/Et2Select/Et2SelectAccount.ts +++ b/api/js/etemplate/Et2Select/Select/Et2SelectAccount.ts @@ -7,13 +7,13 @@ * @author Ralf Becker */ -import {Et2Select} from "./Et2Select"; -import {cleanSelectOptions, SelectOption} from "./FindSelectOptions"; -import {SelectAccountMixin} from "./SelectAccountMixin"; -import {Et2StaticSelectMixin} from "./StaticOptions"; -import {html, nothing} from "@lion/core"; +import {Et2Select} from "../Et2Select"; +import {cleanSelectOptions, SelectOption} from "../FindSelectOptions"; +import {SelectAccountMixin} from "../SelectAccountMixin"; +import {Et2StaticSelectMixin} from "../StaticOptions"; +import {html, nothing} from "lit"; -export type AccountType = 'accounts'|'groups'|'both'|'owngroups'; +export type AccountType = 'accounts' | 'groups' | 'both' | 'owngroups'; /** * @customElement et2-select-account @@ -58,17 +58,16 @@ export class Et2SelectAccount extends SelectAccountMixin(Et2StaticSelectMixin(Et { 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 { - 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) - .then(() => this._renderOptions()); + this.fetchComplete = Promise.all(fetch); } @@ -102,12 +101,7 @@ export class Et2SelectAccount extends SelectAccountMixin(Et2StaticSelectMixin(Et { return []; } - let select_options : Array = [...(this.static_options || []), ...super.select_options]; - - return select_options.filter((value, index, self) => - { - return self.findIndex(v => v.value === value.value) === index; - }); + return super.select_options; } set select_options(new_options : SelectOption[]) diff --git a/api/js/etemplate/Et2Select/Select/Et2SelectApp.ts b/api/js/etemplate/Et2Select/Select/Et2SelectApp.ts new file mode 100644 index 0000000000..c748c6edad --- /dev/null +++ b/api/js/etemplate/Et2Select/Select/Et2SelectApp.ts @@ -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); \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/Select/Et2SelectBitwise.ts b/api/js/etemplate/Et2Select/Select/Et2SelectBitwise.ts new file mode 100644 index 0000000000..84d8abf661 --- /dev/null +++ b/api/js/etemplate/Et2Select/Select/Et2SelectBitwise.ts @@ -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); \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/Select/Et2SelectBool.ts b/api/js/etemplate/Et2Select/Select/Et2SelectBool.ts new file mode 100644 index 0000000000..f91675acfa --- /dev/null +++ b/api/js/etemplate/Et2Select/Select/Et2SelectBool.ts @@ -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); diff --git a/api/js/etemplate/Et2Select/Et2SelectCategory.ts b/api/js/etemplate/Et2Select/Select/Et2SelectCategory.ts similarity index 52% rename from api/js/etemplate/Et2Select/Et2SelectCategory.ts rename to api/js/etemplate/Et2Select/Select/Et2SelectCategory.ts index d614d383ce..c462612b1e 100644 --- a/api/js/etemplate/Et2Select/Et2SelectCategory.ts +++ b/api/js/etemplate/Et2Select/Select/Et2SelectCategory.ts @@ -8,10 +8,13 @@ */ -import {css, PropertyValues} from "@lion/core"; -import {Et2Select} from "./Et2Select"; -import {Et2StaticSelectMixin, StaticOptions as so} from "./StaticOptions"; -import {cleanSelectOptions} from "./FindSelectOptions"; +import {css, html, nothing, PropertyValues, TemplateResult, unsafeCSS} from "lit"; +import {Et2Select} from "../Et2Select"; +import {Et2StaticSelectMixin, StaticOptions as so} from "../StaticOptions"; +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 @@ -25,12 +28,14 @@ export class Et2SelectCategory extends Et2StaticSelectMixin(Et2Select) ...super.styles, css` /* Category color on options */ - ::slotted(*) { + + sl-option { border-left: 6px solid var(--category-color, transparent); } /* 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.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.static_options = cleanSelectOptions(options); + this._static_options = cleanSelectOptions(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. - * Here's where we add the icon & color border - */ - doLabelChange() + + protected handleValueChange(e) { - // Update the display label when checked menu item's label changes - if(this.multiple) - { - return; - } + super.handleValueChange(e); - const checkedItem = this.menuItems.find(item => item.value === this.value); - this.displayLabel = checkedItem ? checkedItem.textContent : ''; - 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") || ""; - } + // Just re-draw to get the colors & icon + this.requestUpdate(); } /** - * 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. - * Initial load order / lifecycle does not have all the options at the right time - * @protected + * @param {SelectOption} option + * @returns {TemplateResult} */ - protected _renderOptions() + public render() : TemplateResult { - // @ts-ignore Doesn't know about Et2WidgetWithSelectMixin._renderOptions() - return super._renderOptions().then(() => - { - // @ts-ignore Doesn't know about SlSelect.menuItems - if(this.menuItems.length > 0) - { - this.doLabelChange(); - } - }); + /** CSS variables are not making it through to options, re-declaring them here works */ + return html` + + ${super.render()} + `; } - + /** * Use a custom tag for when multiple=true * * @returns {string} */ - get tagTag() : string + public get tagTag() : StaticValue { - return "et2-category-tag"; + return literal`et2-category-tag`; } /** diff --git a/api/js/etemplate/Et2Select/Et2SelectCountry.ts b/api/js/etemplate/Et2Select/Select/Et2SelectCountry.ts similarity index 82% rename from api/js/etemplate/Et2Select/Et2SelectCountry.ts rename to api/js/etemplate/Et2Select/Select/Et2SelectCountry.ts index 1cd7d3aaa9..d031d0be9d 100644 --- a/api/js/etemplate/Et2Select/Et2SelectCountry.ts +++ b/api/js/etemplate/Et2Select/Select/Et2SelectCountry.ts @@ -8,10 +8,10 @@ */ -import {Et2Select} from "./Et2Select"; -import {Et2StaticSelectMixin, StaticOptions as so} from "./StaticOptions"; -import {egw} from "../../jsapi/egw_global"; -import {SelectOption} from "./FindSelectOptions"; +import {Et2Select} from "../Et2Select"; +import {Et2StaticSelectMixin, StaticOptions as so} from "../StaticOptions"; +import {egw} from "../../../jsapi/egw_global"; +import {SelectOption} from "../FindSelectOptions"; /** * Customised Select widget for countries @@ -38,7 +38,7 @@ export class Et2SelectCountry extends Et2StaticSelectMixin(Et2Select) (>so.country(this, {}, true)).then(options => { - this.static_options = options + this._static_options = options this.requestUpdate("select_options"); }); } diff --git a/api/js/etemplate/Et2Select/Select/Et2SelectDay.ts b/api/js/etemplate/Et2Select/Select/Et2SelectDay.ts new file mode 100644 index 0000000000..e2979c5c7b --- /dev/null +++ b/api/js/etemplate/Et2Select/Select/Et2SelectDay.ts @@ -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); \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/Select/Et2SelectDayOfWeek.ts b/api/js/etemplate/Et2Select/Select/Et2SelectDayOfWeek.ts new file mode 100644 index 0000000000..0ebee30b58 --- /dev/null +++ b/api/js/etemplate/Et2Select/Select/Et2SelectDayOfWeek.ts @@ -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); \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/Et2SelectEmail.ts b/api/js/etemplate/Et2Select/Select/Et2SelectEmail.ts similarity index 84% rename from api/js/etemplate/Et2Select/Et2SelectEmail.ts rename to api/js/etemplate/Et2Select/Select/Et2SelectEmail.ts index cffdd01183..c74f59fb2b 100644 --- a/api/js/etemplate/Et2Select/Et2SelectEmail.ts +++ b/api/js/etemplate/Et2Select/Select/Et2SelectEmail.ts @@ -7,11 +7,12 @@ * @author Nathan Gray */ -import {Et2Select} from "./Et2Select"; -import {css, html, nothing, PropertyValues} from "@lion/core"; -import {IsEmail} from "../Validators/IsEmail"; +import {Et2Select} from "../Et2Select"; +import {css, html, nothing, PropertyValues} from "lit"; +import {IsEmail} from "../../Validators/IsEmail"; import interact from "@interactjs/interact"; import {Validator} from "@lion/form-core"; +import {classMap} from "lit/directives/class-map.js"; /** * Select email address(es) @@ -100,6 +101,7 @@ export class Et2SelectEmail extends Et2Select this.defaultValidators.push(new IsEmail(this.allowPlaceholder)); } + /** @param {import('@lion/core').PropertyValues } changedProperties */ willUpdate(changedProperties : PropertyValues) { @@ -112,6 +114,40 @@ export class Et2SelectEmail extends Et2Select } } + updated(changedProperties : Map) + { + // 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() { 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. * @@ -187,55 +205,21 @@ export class Et2SelectEmail extends Et2Select * * @returns {string} */ - get tagTag() : string + _tagTemplate(option, index) { - return "et2-email-tag"; - } - - /** - * override tag creation in order to add DND functionality - * @param item - * @protected - */ - protected _createTagNode(item) - { - let tag = super._createTagNode(item); - - 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; + return html` + + ${option.getTextLabel().trim()} + + `; } /** diff --git a/api/js/etemplate/Et2Select/Select/Et2SelectHour.ts b/api/js/etemplate/Et2Select/Select/Et2SelectHour.ts new file mode 100644 index 0000000000..4423a53d88 --- /dev/null +++ b/api/js/etemplate/Et2Select/Select/Et2SelectHour.ts @@ -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); \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/Select/Et2SelectLang.ts b/api/js/etemplate/Et2Select/Select/Et2SelectLang.ts new file mode 100644 index 0000000000..6ea9f7eb3e --- /dev/null +++ b/api/js/etemplate/Et2Select/Select/Et2SelectLang.ts @@ -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); \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/Select/Et2SelectMonth.ts b/api/js/etemplate/Et2Select/Select/Et2SelectMonth.ts new file mode 100644 index 0000000000..72d9a8577a --- /dev/null +++ b/api/js/etemplate/Et2Select/Select/Et2SelectMonth.ts @@ -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); \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/Select/Et2SelectNumber.ts b/api/js/etemplate/Et2Select/Select/Et2SelectNumber.ts new file mode 100644 index 0000000000..960dff879b --- /dev/null +++ b/api/js/etemplate/Et2Select/Select/Et2SelectNumber.ts @@ -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); \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/Select/Et2SelectPercent.ts b/api/js/etemplate/Et2Select/Select/Et2SelectPercent.ts new file mode 100644 index 0000000000..491fcb58bc --- /dev/null +++ b/api/js/etemplate/Et2Select/Select/Et2SelectPercent.ts @@ -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); \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/Select/Et2SelectPriority.ts b/api/js/etemplate/Et2Select/Select/Et2SelectPriority.ts new file mode 100644 index 0000000000..265477369f --- /dev/null +++ b/api/js/etemplate/Et2Select/Select/Et2SelectPriority.ts @@ -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); \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/Et2SelectReadonly.ts b/api/js/etemplate/Et2Select/Select/Et2SelectReadonly.ts similarity index 92% rename from api/js/etemplate/Et2Select/Et2SelectReadonly.ts rename to api/js/etemplate/Et2Select/Select/Et2SelectReadonly.ts index fa5053e954..3c49b1b3b4 100644 --- a/api/js/etemplate/Et2Select/Et2SelectReadonly.ts +++ b/api/js/etemplate/Et2Select/Select/Et2SelectReadonly.ts @@ -8,12 +8,13 @@ */ -import {css, html, LitElement, repeat, TemplateResult} from "@lion/core"; -import {et2_IDetachedDOM} from "../et2_core_interfaces"; -import {Et2Widget} from "../Et2Widget/Et2Widget"; -import {Et2StaticSelectMixin, StaticOptions, StaticOptions as so} from "./StaticOptions"; -import {cleanSelectOptions, find_select_options, SelectOption} from "./FindSelectOptions"; -import {SelectAccountMixin} from "./SelectAccountMixin"; +import {css, html, LitElement, TemplateResult} from "lit"; +import {repeat} from "lit/directives/repeat.js"; +import {et2_IDetachedDOM} from "../../et2_core_interfaces"; +import {Et2Widget} from "../../Et2Widget/Et2Widget"; +import {Et2StaticSelectMixin, StaticOptions, StaticOptions as so} from "../StaticOptions"; +import {cleanSelectOptions, find_select_options, SelectOption} from "../FindSelectOptions"; +import {SelectAccountMixin} from "../SelectAccountMixin"; /** * This is a stripped-down read-only widget used in nextmatch @@ -143,6 +144,11 @@ li { return this.value; } + getValueAsArray() + { + return (Array.isArray(this.value) ? this.value : [this.value]); + } + set value(new_value : string | string[]) { // Split anything that is still a CSV @@ -206,10 +212,11 @@ li { render() { + const value = this.getValueAsArray(); return html`
    ${repeat( - (Array.isArray(this.value) ? this.value : [this.value]), + this.getValueAsArray(), (val : string) => val, (val) => { let option = (this.select_options).find(option => option.value == val); @@ -282,14 +289,16 @@ customElements.define("et2-select-app_ro", Et2SelectAppReadonly); export class Et2SelectBitwiseReadonly extends Et2SelectReadonly { + /* Currently handled server side, we get an array render() { let new_value = []; + let int_value = parseInt(this.value); for(let index in this.select_options) { let option = this.select_options[index]; let right = parseInt(option && option.value ? option.value : index); - if(!!(this.value & right)) + if(!!(int_value & right)) { new_value.push(right); } @@ -307,6 +316,8 @@ export class Et2SelectBitwiseReadonly extends Et2SelectReadonly })}
`; } + + */ } // @ts-ignore TypeScript is not recognizing that this widget is a LitElement @@ -349,7 +360,6 @@ export class Et2SelectPercentReadonly extends Et2SelectReadonly constructor() { super(...arguments); - this.suffix = "%%"; this.select_options = so.percent(this); } } @@ -391,6 +401,23 @@ export class Et2SelectDayOfWeekReadonly extends Et2StaticSelectMixin(Et2SelectRe 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(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 @@ -422,7 +449,7 @@ export class Et2SelectNumberReadonly extends Et2StaticSelectMixin(Et2SelectReado { protected find_select_options(_attrs) { - this.static_options = so.number(this, _attrs); + this._static_options = so.number(this, _attrs); } } diff --git a/api/js/etemplate/Et2Select/Select/Et2SelectState.ts b/api/js/etemplate/Et2Select/Select/Et2SelectState.ts new file mode 100644 index 0000000000..9c1ac5336b --- /dev/null +++ b/api/js/etemplate/Et2Select/Select/Et2SelectState.ts @@ -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 = StaticOptions.state(this, {country_code: code}); + this.requestUpdate("select_options"); + } + + set_country_code(code) + { + this.countryCode = code; + } +} + +customElements.define("et2-select-state", Et2SelectState); \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/Select/Et2SelectTab.ts b/api/js/etemplate/Et2Select/Select/Et2SelectTab.ts new file mode 100644 index 0000000000..ec39624827 --- /dev/null +++ b/api/js/etemplate/Et2Select/Select/Et2SelectTab.ts @@ -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); \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/Et2SelectThumbnail.ts b/api/js/etemplate/Et2Select/Select/Et2SelectThumbnail.ts similarity index 95% rename from api/js/etemplate/Et2Select/Et2SelectThumbnail.ts rename to api/js/etemplate/Et2Select/Select/Et2SelectThumbnail.ts index a651703903..1313af2891 100644 --- a/api/js/etemplate/Et2Select/Et2SelectThumbnail.ts +++ b/api/js/etemplate/Et2Select/Select/Et2SelectThumbnail.ts @@ -7,9 +7,9 @@ * @author Nathan Gray */ -import {Et2Select} from "./Et2Select"; -import {css} from "@lion/core"; -import {SelectOption} from "./FindSelectOptions"; +import {Et2Select} from "../Et2Select"; +import {css} from "lit"; +import {SelectOption} from "../FindSelectOptions"; export class Et2SelectThumbnail extends Et2Select { diff --git a/api/js/etemplate/Et2Select/Select/Et2SelectTimezone.ts b/api/js/etemplate/Et2Select/Select/Et2SelectTimezone.ts new file mode 100644 index 0000000000..ff8dd669a1 --- /dev/null +++ b/api/js/etemplate/Et2Select/Select/Et2SelectTimezone.ts @@ -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); \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/Select/Et2SelectYear.ts b/api/js/etemplate/Et2Select/Select/Et2SelectYear.ts new file mode 100644 index 0000000000..db231aa2fc --- /dev/null +++ b/api/js/etemplate/Et2Select/Select/Et2SelectYear.ts @@ -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); \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/SelectAccountMixin.ts b/api/js/etemplate/Et2Select/SelectAccountMixin.ts index 429ad91672..1c35111b59 100644 --- a/api/js/etemplate/Et2Select/SelectAccountMixin.ts +++ b/api/js/etemplate/Et2Select/SelectAccountMixin.ts @@ -1,5 +1,5 @@ import {SelectOption} from "./FindSelectOptions"; -import {LitElement} from "@lion/core"; +import {LitElement} from "lit"; /** * EGroupware eTemplate2 - SelectAccountMixin diff --git a/api/js/etemplate/Et2Select/SelectTypes.ts b/api/js/etemplate/Et2Select/SelectTypes.ts new file mode 100644 index 0000000000..ab23cdb7f8 --- /dev/null +++ b/api/js/etemplate/Et2Select/SelectTypes.ts @@ -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'; \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/StaticOptions.js b/api/js/etemplate/Et2Select/StaticOptions.js new file mode 100644 index 0000000000..fa371e0a0e --- /dev/null +++ b/api/js/etemplate/Et2Select/StaticOptions.js @@ -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} 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 \ No newline at end of file diff --git a/api/js/etemplate/Et2Select/StaticOptions.ts b/api/js/etemplate/Et2Select/StaticOptions.ts index dc74ad8491..6bc1110ef6 100644 --- a/api/js/etemplate/Et2Select/StaticOptions.ts +++ b/api/js/etemplate/Et2Select/StaticOptions.ts @@ -8,11 +8,18 @@ * @param {type} widget */ 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 {Et2Select, Et2WidgetWithSelect} from "./Et2Select"; +import {state} from "lit/decorators/state.js"; export type Et2SelectWidgets = Et2Select | Et2WidgetWithSelect | Et2SelectReadonly; +type NumberOptions = { + min? : number, + max? : number, + interval? : number, + format? : string +}; // Export the Interface for TypeScript type Constructor = new (...args : any[]) => T; @@ -31,27 +38,25 @@ export const Et2StaticSelectMixin = > // Hold the static widget options separately so other options (like sent from server in sel_options) won't // 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 - protected fetchComplete : Promise; + @state() + protected fetchComplete : Promise = Promise.resolve(); - constructor(...args) + async getUpdateComplete() : Promise { - super(...args); - - this.static_options = []; - this.fetchComplete = Promise.resolve(); - - // Trigger the options to get rendered into the DOM - this.requestUpdate("select_options"); + const result = await super.getUpdateComplete(); + await this.fetchComplete; + return result; } get select_options() : SelectOption[] { // @ts-ignore const options = super.select_options || []; - const statics = this.static_options || []; + const statics = this._static_options || []; if(options.length == 0) { @@ -62,7 +67,7 @@ export const Et2StaticSelectMixin = > return options; } // 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()]; } @@ -75,7 +80,7 @@ export const Et2StaticSelectMixin = > set_static_options(new_static_options) { - this.static_options = new_static_options; + this._static_options = new_static_options; this.requestUpdate("select_options"); } @@ -273,19 +278,14 @@ export const StaticOptions = new class StaticOptionsType ]; } - number(widget : Et2SelectWidgets, attrs = { - min: undefined, - max: undefined, - interval: undefined, - format: undefined - }) : SelectOption[] + number(widget : Et2SelectWidgets, attrs : NumberOptions = {}) : SelectOption[] { - var options = []; - var min = parseFloat(attrs.min ?? widget.min ?? 1); - var max = parseFloat(attrs.max ?? widget.max ?? 10); - var interval = parseFloat(attrs.interval ?? widget.interval ?? 1); - var format = attrs.format ?? '%d'; + const options = []; + const min = parseFloat(attrs.min ?? widget.min ?? 1); + const max = parseFloat(attrs.max ?? widget.max ?? 10); + let interval = parseFloat(attrs.interval ?? widget.interval ?? 1); + let format = attrs.format ?? '%d'; // leading zero specified in interval if(widget.leading_zero && widget.leading_zero[0] == '0') @@ -313,7 +313,7 @@ export const StaticOptions = new class StaticOptionsType 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[] @@ -323,15 +323,14 @@ export const StaticOptions = new class StaticOptionsType attrs = {} } var t = new Date(); - attrs.min = t.getFullYear() + parseInt(widget.min); - attrs.max = t.getFullYear() + parseInt(widget.max); + attrs.min = t.getFullYear() + parseInt(attrs.min ?? widget.min ?? -3); + attrs.max = t.getFullYear() + parseInt(attrs.max ?? widget.max ?? 2); return this.number(widget, attrs); } day(widget : Et2SelectWidgets, attrs) : SelectOption[] { - attrs.other = [1, 31, 1]; - return this.number(widget, attrs); + return this.number(widget, {min: 1, max: 31, interval: 1}); } hour(widget : Et2SelectWidgets, attrs) : SelectOption[] diff --git a/api/js/etemplate/Et2Select/Tag/Et2CategoryTag.ts b/api/js/etemplate/Et2Select/Tag/Et2CategoryTag.ts index 62bb2c61b8..77270b9431 100644 --- a/api/js/etemplate/Et2Select/Tag/Et2CategoryTag.ts +++ b/api/js/etemplate/Et2Select/Tag/Et2CategoryTag.ts @@ -6,7 +6,7 @@ * @link https://www.egroupware.org * @author Nathan Gray */ -import {css, html, TemplateResult} from "@lion/core"; +import {css, html, TemplateResult} from "lit"; import shoelace from "../../Styles/shoelace"; import {Et2Tag} from "./Et2Tag"; diff --git a/api/js/etemplate/Et2Select/Tag/Et2EmailTag.ts b/api/js/etemplate/Et2Select/Tag/Et2EmailTag.ts index 8ea12f53cb..fdcd6a3883 100644 --- a/api/js/etemplate/Et2Select/Tag/Et2EmailTag.ts +++ b/api/js/etemplate/Et2Select/Tag/Et2EmailTag.ts @@ -6,7 +6,8 @@ * @link https://www.egroupware.org * @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 {Et2Tag} from "./Et2Tag"; @@ -95,8 +96,8 @@ export class Et2EmailTag extends Et2Tag this.onlyEmail = false; this.handleMouseEnter = this.handleMouseEnter.bind(this); this.handleMouseLeave = this.handleMouseLeave.bind(this); - this.handleClick = this.handleClick.bind(this); - this.handleContactClick = this.handleContactClick.bind(this); + this.handleMouseDown = this.handleMouseDown.bind(this); + this.handleContactMouseDown = this.handleContactMouseDown.bind(this); } connectedCallback() @@ -166,7 +167,7 @@ export class Et2EmailTag extends Et2Tag this.shadowRoot.querySelector(".tag").classList.remove("contact_plus"); } - handleClick(e : MouseEvent) + handleMouseDown(e : MouseEvent) { e.stopPropagation(); @@ -177,7 +178,7 @@ export class Et2EmailTag extends Et2Tag this.egw().open('', 'addressbook', 'add', extra); } - handleContactClick(e : MouseEvent) + handleContactMouseDown(e : MouseEvent) { e.stopPropagation(); this.checkContact(this.value).then((result) => @@ -255,7 +256,7 @@ export class Et2EmailTag extends Et2Tag button_or_avatar = html` `; diff --git a/api/js/etemplate/Et2Select/Tag/Et2Tag.ts b/api/js/etemplate/Et2Select/Tag/Et2Tag.ts index d9e50c1d46..24fbbe26d0 100644 --- a/api/js/etemplate/Et2Select/Tag/Et2Tag.ts +++ b/api/js/etemplate/Et2Select/Tag/Et2Tag.ts @@ -8,7 +8,8 @@ */ import {Et2Widget} from "../../Et2Widget/Et2Widget"; 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"; /** @@ -23,7 +24,6 @@ export class Et2Tag extends Et2Widget(SlTag) shoelace, css` :host { flex: 1 1 auto; - overflow: hidden; } .tag--pill { @@ -113,11 +113,12 @@ export class Et2Tag extends Et2Widget(SlTag) ` : ''} diff --git a/api/js/etemplate/Et2Select/Tag/Et2ThumbnailTag.ts b/api/js/etemplate/Et2Select/Tag/Et2ThumbnailTag.ts index ff4c8d2e32..d09cd429ad 100644 --- a/api/js/etemplate/Et2Select/Tag/Et2ThumbnailTag.ts +++ b/api/js/etemplate/Et2Select/Tag/Et2ThumbnailTag.ts @@ -6,7 +6,7 @@ * @link https://www.egroupware.org * @author Nathan Gray */ -import {css} from "@lion/core"; +import {css} from "lit"; import shoelace from "../../Styles/shoelace"; import {Et2Tag} from "./Et2Tag"; diff --git a/api/js/etemplate/Et2Select/test/EditableTag.test.ts b/api/js/etemplate/Et2Select/test/EditableTag.test.ts index 5a3976d8e6..20c649c4fa 100644 --- a/api/js/etemplate/Et2Select/test/EditableTag.test.ts +++ b/api/js/etemplate/Et2Select/test/EditableTag.test.ts @@ -20,8 +20,8 @@ async function before(editable = true) // @ts-ignore element = await fixture(html` - One - Two + One + Two `); // Stub egw() diff --git a/api/js/etemplate/Et2Select/test/Et2EmailTag.test.ts b/api/js/etemplate/Et2Select/test/Et2EmailTag.test.ts index 3d9ec68de7..8c36db5cfa 100644 --- a/api/js/etemplate/Et2Select/test/Et2EmailTag.test.ts +++ b/api/js/etemplate/Et2Select/test/Et2EmailTag.test.ts @@ -57,7 +57,7 @@ describe('Et2EmailTag', () => 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() => @@ -79,6 +79,6 @@ describe('Et2EmailTag', () => assert.deepEqual(extra, {title: contact.n_fn, icon: contact.photo}); } }); - await component.handleContactClick(new MouseEvent('click')); + await component.handleContactMouseDown(new MouseEvent('click')); }); }); diff --git a/api/js/etemplate/Et2Select/test/Et2SelectBasic.test.ts b/api/js/etemplate/Et2Select/test/Et2SelectBasic.test.ts index 6d37fa068b..825d072ed2 100644 --- a/api/js/etemplate/Et2Select/test/Et2SelectBasic.test.ts +++ b/api/js/etemplate/Et2Select/test/Et2SelectBasic.test.ts @@ -97,8 +97,8 @@ describe("Multiple", () => // @ts-ignore element = await fixture(html` - One - Two + One + Two `); element.set_value("one,two"); @@ -111,7 +111,7 @@ describe("Multiple", () => 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"]); let tags = element.shadowRoot.querySelectorAll('.select__tags > *'); diff --git a/api/js/etemplate/Et2Select/test/Et2SelectOptions.test.ts b/api/js/etemplate/Et2Select/test/Et2SelectOptions.test.ts index 357216a683..dcfd397319 100644 --- a/api/js/etemplate/Et2Select/test/Et2SelectOptions.test.ts +++ b/api/js/etemplate/Et2Select/test/Et2SelectOptions.test.ts @@ -86,7 +86,7 @@ describe("Select widget", () => await element.updateComplete; /** 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() => @@ -109,7 +109,7 @@ describe("Select widget", () => /** TESTING **/ // @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.includeMembers(option_keys, ["1", "2", "option"], "Option mis-match"); assert.equal(option_keys.length, 3); diff --git a/api/js/etemplate/Et2Select/test/SearchActions.test.ts b/api/js/etemplate/Et2Select/test/SearchActions.test.ts index 2b2f4d683f..74c1dd2ee2 100644 --- a/api/js/etemplate/Et2Select/test/SearchActions.test.ts +++ b/api/js/etemplate/Et2Select/test/SearchActions.test.ts @@ -97,13 +97,13 @@ describe("Trigger search", () => // @ts-ignore element = await fixture(html` - One - Two - Three - Four - Five - Six - Seven + One + Two + Three + Four + Five + Six + Seven `); // Stub egw() diff --git a/api/js/etemplate/Et2Spinner/Et2Spinner.ts b/api/js/etemplate/Et2Spinner/Et2Spinner.ts index c4efa49393..f311cbe381 100644 --- a/api/js/etemplate/Et2Spinner/Et2Spinner.ts +++ b/api/js/etemplate/Et2Spinner/Et2Spinner.ts @@ -11,7 +11,7 @@ import {Et2Widget} from "../Et2Widget/Et2Widget"; import {SlSpinner} from "@shoelace-style/shoelace"; import shoelace from "../Styles/shoelace"; -import {css} from "@lion/core"; +import {css} from "lit"; export class Et2Spinner extends Et2Widget(SlSpinner) { diff --git a/api/js/etemplate/Et2Switch/Et2Switch.ts b/api/js/etemplate/Et2Switch/Et2Switch.ts index bd93c87108..e47f62839a 100644 --- a/api/js/etemplate/Et2Switch/Et2Switch.ts +++ b/api/js/etemplate/Et2Switch/Et2Switch.ts @@ -8,8 +8,8 @@ * @author Hadi Nategh */ - -import {css, html, SlotMixin} from "@lion/core"; +import {css, html} from "lit"; +import {SlotMixin} from "@lion/core"; import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget"; import '../Et2Image/Et2Image'; import {SlSwitch} from "@shoelace-style/shoelace"; diff --git a/api/js/etemplate/Et2Textarea/Et2Textarea.ts b/api/js/etemplate/Et2Textarea/Et2Textarea.ts index 4d1b115d88..4153c003b8 100644 --- a/api/js/etemplate/Et2Textarea/Et2Textarea.ts +++ b/api/js/etemplate/Et2Textarea/Et2Textarea.ts @@ -9,7 +9,7 @@ */ -import {css} from "@lion/core"; +import {css} from "lit"; import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget"; import {SlTextarea} from "@shoelace-style/shoelace"; import shoelace from "../Styles/shoelace"; diff --git a/api/js/etemplate/Et2Textbox/Et2Number.ts b/api/js/etemplate/Et2Textbox/Et2Number.ts index 879a20adce..ab02b8df9e 100644 --- a/api/js/etemplate/Et2Textbox/Et2Number.ts +++ b/api/js/etemplate/Et2Textbox/Et2Number.ts @@ -9,7 +9,7 @@ */ import {Et2Textbox} from "./Et2Textbox"; -import {css, html, render} from "@lion/core"; +import {css, html, render} from "lit"; export class Et2Number extends Et2Textbox { diff --git a/api/js/etemplate/Et2Textbox/Et2Password.ts b/api/js/etemplate/Et2Textbox/Et2Password.ts index 08b58b2174..86feddd275 100644 --- a/api/js/etemplate/Et2Textbox/Et2Password.ts +++ b/api/js/etemplate/Et2Textbox/Et2Password.ts @@ -11,7 +11,9 @@ import {Et2InvokerMixin} from "../Et2Url/Et2InvokerMixin"; import {Et2Textbox} from "./Et2Textbox"; 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"; const isChromium = navigator.userAgentData?.brands.some(b => b.brand.includes('Chromium')); diff --git a/api/js/etemplate/Et2Textbox/Et2Textbox.ts b/api/js/etemplate/Et2Textbox/Et2Textbox.ts index f21aa390d5..f17fbea6ee 100644 --- a/api/js/etemplate/Et2Textbox/Et2Textbox.ts +++ b/api/js/etemplate/Et2Textbox/Et2Textbox.ts @@ -9,7 +9,7 @@ */ -import {css, PropertyValues} from "@lion/core"; +import {css, PropertyValues} from "lit"; import {Regex} from "../Validators/Regex"; import {SlInput} from "@shoelace-style/shoelace"; import shoelace from "../Styles/shoelace"; diff --git a/api/js/etemplate/Et2Url/Et2InvokerMixin.ts b/api/js/etemplate/Et2Url/Et2InvokerMixin.ts index 811e76346a..eccfe96a54 100644 --- a/api/js/etemplate/Et2Url/Et2InvokerMixin.ts +++ b/api/js/etemplate/Et2Url/Et2InvokerMixin.ts @@ -8,7 +8,8 @@ */ /* 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 {colorsDefStyles} from "../Styles/colorsDefStyles"; diff --git a/api/js/etemplate/Et2Url/Et2Url.ts b/api/js/etemplate/Et2Url/Et2Url.ts index 7f3f98d82b..4320510078 100644 --- a/api/js/etemplate/Et2Url/Et2Url.ts +++ b/api/js/etemplate/Et2Url/Et2Url.ts @@ -11,7 +11,7 @@ import {Et2InvokerMixin} from "./Et2InvokerMixin"; import {Et2Textbox} from "../Et2Textbox/Et2Textbox"; import {colorsDefStyles} from "../Styles/colorsDefStyles"; -import {css} from "@lion/core"; +import {css} from "lit"; import {egw} from "../../jsapi/egw_global"; /** diff --git a/api/js/etemplate/Et2Url/Et2UrlEmail.ts b/api/js/etemplate/Et2Url/Et2UrlEmail.ts index 9e2c527423..ab3d89a13c 100644 --- a/api/js/etemplate/Et2Url/Et2UrlEmail.ts +++ b/api/js/etemplate/Et2Url/Et2UrlEmail.ts @@ -12,7 +12,7 @@ import {Et2InvokerMixin} from "./Et2InvokerMixin"; import {IsEmail} from "../Validators/IsEmail"; import {Et2Textbox} from "../Et2Textbox/Et2Textbox"; import {colorsDefStyles} from "../Styles/colorsDefStyles"; -import {css} from "@lion/core"; +import {css} from "lit"; import {egw} from "../../jsapi/egw_global"; /** diff --git a/api/js/etemplate/Et2Url/Et2UrlFax.ts b/api/js/etemplate/Et2Url/Et2UrlFax.ts index cfa4bed99b..0566eb33e7 100644 --- a/api/js/etemplate/Et2Url/Et2UrlFax.ts +++ b/api/js/etemplate/Et2Url/Et2UrlFax.ts @@ -11,7 +11,7 @@ import {Et2UrlPhone} from "./Et2UrlPhone"; import {Et2UrlEmail} from "./Et2UrlEmail"; import {colorsDefStyles} from "../Styles/colorsDefStyles"; -import {css} from "@lion/core"; +import {css} from "lit"; /** * @customElement et2-url-phone diff --git a/api/js/etemplate/Et2Url/Et2UrlPhone.ts b/api/js/etemplate/Et2Url/Et2UrlPhone.ts index 118e0fd8c1..719707e1fb 100644 --- a/api/js/etemplate/Et2Url/Et2UrlPhone.ts +++ b/api/js/etemplate/Et2Url/Et2UrlPhone.ts @@ -11,7 +11,7 @@ import {Et2InvokerMixin} from "./Et2InvokerMixin"; import {Et2Textbox} from "../Et2Textbox/Et2Textbox"; import {colorsDefStyles} from "../Styles/colorsDefStyles"; -import {css} from "@lion/core"; +import {css} from "lit"; /** * @customElement et2-url-phone diff --git a/api/js/etemplate/Et2Url/Et2UrlReadonly.ts b/api/js/etemplate/Et2Url/Et2UrlReadonly.ts index 4f9c1a8a2a..f3ec0826cb 100644 --- a/api/js/etemplate/Et2Url/Et2UrlReadonly.ts +++ b/api/js/etemplate/Et2Url/Et2UrlReadonly.ts @@ -9,7 +9,7 @@ /* eslint-disable import/no-extraneous-dependencies */ import {Et2Description} from "../Et2Description/Et2Description"; -import {css, TemplateResult} from "@lion/core"; +import {css, TemplateResult} from "lit"; import {Et2Url} from "./Et2Url"; /** diff --git a/api/js/etemplate/Et2Vfs/Et2VfsMime.ts b/api/js/etemplate/Et2Vfs/Et2VfsMime.ts index a22efed82f..5dd8bc0bcb 100644 --- a/api/js/etemplate/Et2Vfs/Et2VfsMime.ts +++ b/api/js/etemplate/Et2Vfs/Et2VfsMime.ts @@ -1,7 +1,7 @@ import {ExposeValue} from "../Expose/ExposeMixin"; import {et2_vfsMode} from "../et2_widget_vfs"; import {Et2ImageExpose} from "../Expose/Et2ImageExpose"; -import {css, html} from "@lion/core"; +import {css, html} from "lit"; export class Et2VfsMime extends Et2ImageExpose diff --git a/api/js/etemplate/Et2Vfs/Et2VfsUid.ts b/api/js/etemplate/Et2Vfs/Et2VfsUid.ts index d6458a60f8..1ddb492341 100644 --- a/api/js/etemplate/Et2Vfs/Et2VfsUid.ts +++ b/api/js/etemplate/Et2Vfs/Et2VfsUid.ts @@ -7,7 +7,7 @@ * @author Ralf Becker */ -import {Et2SelectAccountReadonly} from "../Et2Select/Et2SelectReadonly"; +import {Et2SelectAccountReadonly} from "../Et2Select/Select/Et2SelectReadonly"; export class Et2VfsUid extends Et2SelectAccountReadonly { diff --git a/api/js/etemplate/Et2Widget/Et2Widget.ts b/api/js/etemplate/Et2Widget/Et2Widget.ts index 708f476bc1..2312ea7800 100644 --- a/api/js/etemplate/Et2Widget/Et2Widget.ts +++ b/api/js/etemplate/Et2Widget/Et2Widget.ts @@ -8,7 +8,8 @@ import {et2_cloneObject, et2_csvSplit} from "../et2_core_common"; import type {IegwAppLocal} from "../../jsapi/egw_global"; import {egw} from "../../jsapi/egw_global"; 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_DOMWidget} from "../et2_core_DOMWidget"; diff --git a/api/js/etemplate/Expose/Et2DescriptionExpose.ts b/api/js/etemplate/Expose/Et2DescriptionExpose.ts index 2331bf7e73..80560af7ff 100644 --- a/api/js/etemplate/Expose/Et2DescriptionExpose.ts +++ b/api/js/etemplate/Expose/Et2DescriptionExpose.ts @@ -12,7 +12,7 @@ import {ExposeMixin, ExposeValue, MediaValue} from "./ExposeMixin"; import {Et2Description} from "../Et2Description/Et2Description"; 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. diff --git a/api/js/etemplate/Expose/ExposeMixin.ts b/api/js/etemplate/Expose/ExposeMixin.ts index a18db22e60..7850759888 100644 --- a/api/js/etemplate/Expose/ExposeMixin.ts +++ b/api/js/etemplate/Expose/ExposeMixin.ts @@ -11,7 +11,7 @@ // Don't import this more than once 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 {Et2Dialog} from "../Et2Dialog/Et2Dialog"; import {ET2_DATAVIEW_STEPSIZE} from "../et2_dataview_controller"; diff --git a/api/js/etemplate/Layout/Et2Box/Et2Box.ts b/api/js/etemplate/Layout/Et2Box/Et2Box.ts index 4e52dfabf2..b5423eaf4f 100644 --- a/api/js/etemplate/Layout/Et2Box/Et2Box.ts +++ b/api/js/etemplate/Layout/Et2Box/Et2Box.ts @@ -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 {et2_IDetachedDOM} from "../../et2_core_interfaces"; diff --git a/api/js/etemplate/Layout/Et2Details/Et2Details.ts b/api/js/etemplate/Layout/Et2Details/Et2Details.ts index e9ceaba950..5ad85ceeb0 100644 --- a/api/js/etemplate/Layout/Et2Details/Et2Details.ts +++ b/api/js/etemplate/Layout/Et2Details/Et2Details.ts @@ -8,7 +8,7 @@ */ import {Et2Widget} from "../../Et2Widget/Et2Widget"; -import {css} from "@lion/core"; +import {css} from "lit"; import {SlDetails} from "@shoelace-style/shoelace"; import shoelace from "../../Styles/shoelace"; diff --git a/api/js/etemplate/Layout/Et2Split/Et2Split.ts b/api/js/etemplate/Layout/Et2Split/Et2Split.ts index 64d497948a..d6c4c2ca42 100644 --- a/api/js/etemplate/Layout/Et2Split/Et2Split.ts +++ b/api/js/etemplate/Layout/Et2Split/Et2Split.ts @@ -12,7 +12,8 @@ import {cssImage, Et2Widget} from "../../Et2Widget/Et2Widget"; import {SlSplitPanel} from "@shoelace-style/shoelace"; import {et2_IDOMNode, et2_IResizeable} from "../../et2_core_interfaces"; import {et2_DOMWidget} from "../../et2_core_DOMWidget"; -import {css, html, SlotMixin} from "@lion/core"; +import {css, html} from "lit"; +import {SlotMixin} from "@lion/core"; import {colorsDefStyles} from "../../Styles/colorsDefStyles"; export class Et2Split extends Et2Widget(SlotMixin(SlSplitPanel)) @@ -132,8 +133,8 @@ export class Et2Split extends Et2Widget(SlotMixin(SlSplitPanel)) let outerNodetopOffset = widget.dynheight.outerNode.offset().top; widget.dynheight.outerNode = { // Random 3px deducted to make things fit better. Otherwise nm edges are hidden - width: () => parseInt(getComputedStyle(this.shadowRoot.querySelector(".start")).width) - 3, - height: () => parseInt(getComputedStyle(this.shadowRoot.querySelector(".start")).height) - 3, + width: () => parseInt(getComputedStyle(this.querySelector("[slot='start']")).width) - 3, + height: () => parseInt(getComputedStyle(this.querySelector("[slot='start']")).height) - 3, offset: () => {return {top:outerNodetopOffset}} }; widget.dynheight._collectBottomNodes = function() diff --git a/api/js/etemplate/Layout/Et2Tabs/Et2Tab.ts b/api/js/etemplate/Layout/Et2Tabs/Et2Tab.ts index 8446e60d03..bc2d4ca158 100644 --- a/api/js/etemplate/Layout/Et2Tabs/Et2Tab.ts +++ b/api/js/etemplate/Layout/Et2Tabs/Et2Tab.ts @@ -1,7 +1,7 @@ import {Et2Widget} from "../../Et2Widget/Et2Widget"; import {SlTab} from "@shoelace-style/shoelace"; import shoelace from "../../Styles/shoelace"; -import {css} from "@lion/core"; +import {css} from "lit"; export class Et2Tab extends Et2Widget(SlTab) { diff --git a/api/js/etemplate/Layout/Et2Tabs/Et2TabPanel.ts b/api/js/etemplate/Layout/Et2Tabs/Et2TabPanel.ts index 08ccbc0b20..93ddc49ee3 100644 --- a/api/js/etemplate/Layout/Et2Tabs/Et2TabPanel.ts +++ b/api/js/etemplate/Layout/Et2Tabs/Et2TabPanel.ts @@ -1,7 +1,7 @@ import {Et2Widget} from "../../Et2Widget/Et2Widget"; import {SlTabPanel} from "@shoelace-style/shoelace"; import shoelace from "../../Styles/shoelace"; -import {css} from "@lion/core"; +import {css} from "lit"; export class Et2TabPanel extends Et2Widget(SlTabPanel) { diff --git a/api/js/etemplate/Layout/Et2Tabs/Et2Tabs.ts b/api/js/etemplate/Layout/Et2Tabs/Et2Tabs.ts index 11b8e6dac9..fb5d4dbaa3 100644 --- a/api/js/etemplate/Layout/Et2Tabs/Et2Tabs.ts +++ b/api/js/etemplate/Layout/Et2Tabs/Et2Tabs.ts @@ -10,7 +10,7 @@ import {SlTab, SlTabGroup, SlTabPanel} from "@shoelace-style/shoelace"; import {loadWebComponent} from "../../Et2Widget/Et2Widget"; import {et2_directChildrenByTagName, et2_filteredNodeIterator, et2_readAttrWithDefault} from "../../et2_core_xml"; -import {css, PropertyValues} from "@lion/core"; +import {css, PropertyValues} from "lit"; import shoelace from "../../Styles/shoelace"; import {et2_createWidget} from "../../et2_core_widget"; import {colorsDefStyles} from "../../Styles/colorsDefStyles"; @@ -391,9 +391,14 @@ export class Et2Tabs extends Et2InputWidget(SlTabGroup) implements et2_IResizeab }); } + /** + * Overridden to allow et2-tab-panel as well as sl-tab-panel + * + * @returns {[SlTabPanel]} + */ getAllPanels() { - const slot = this.body.querySelector('slot')!; + const slot = this.body!; return [...slot.assignedElements()].filter(el => ['et2-tab-panel', 'sl-tab-panel'].indexOf(el.tagName.toLowerCase()) != -1) as [SlTabPanel]; } diff --git a/api/js/etemplate/Layout/Et2Tabs/Et2TabsMobile.ts b/api/js/etemplate/Layout/Et2Tabs/Et2TabsMobile.ts index 602266bbf9..8cd4f8c69c 100644 --- a/api/js/etemplate/Layout/Et2Tabs/Et2TabsMobile.ts +++ b/api/js/etemplate/Layout/Et2Tabs/Et2TabsMobile.ts @@ -1,5 +1,7 @@ import {Et2Tabs} from "./Et2Tabs"; -import {classMap, html, repeat, TemplateResult} from "@lion/core"; +import {html, TemplateResult} from "lit"; +import {classMap} from "lit/directives/class-map.js"; +import {repeat} from "lit/directives/repeat.js"; import {Et2Details} from "../Et2Details/Et2Details"; import {SlTab, SlTabPanel} from "@shoelace-style/shoelace"; diff --git a/api/js/etemplate/Layout/RowLimitedMixin.ts b/api/js/etemplate/Layout/RowLimitedMixin.ts index 88e559d510..f2d26f95bd 100644 --- a/api/js/etemplate/Layout/RowLimitedMixin.ts +++ b/api/js/etemplate/Layout/RowLimitedMixin.ts @@ -1,5 +1,5 @@ // Export the Interface for TypeScript -import {LitElement} from "@lion/core"; +import {LitElement} from "lit"; type Constructor = new (...args : any[]) => T; diff --git a/api/js/etemplate/Styles/colorsDefStyles.ts b/api/js/etemplate/Styles/colorsDefStyles.ts index 1c8f7d2909..422d11ecda 100644 --- a/api/js/etemplate/Styles/colorsDefStyles.ts +++ b/api/js/etemplate/Styles/colorsDefStyles.ts @@ -2,7 +2,7 @@ * Sharable colors definition styles constant */ -import {css} from "@lion/core"; +import {css} from "lit"; export const colorsDefStyles = css` :host { diff --git a/api/js/etemplate/et2_extension_customfields.ts b/api/js/etemplate/et2_extension_customfields.ts index 58e4b467ae..62c080d43f 100644 --- a/api/js/etemplate/et2_extension_customfields.ts +++ b/api/js/etemplate/et2_extension_customfields.ts @@ -25,7 +25,7 @@ import {et2_IDetachedDOM, et2_IInput} from "./et2_core_interfaces"; import {et2_cloneObject, et2_no_init} from "./et2_core_common"; import {et2_DOMWidget} from "./et2_core_DOMWidget"; import {loadWebComponent} from "./Et2Widget/Et2Widget"; -import {LitElement} from "@lion/core"; +import {LitElement} from "lit"; export class et2_customfields_list extends et2_valueWidget implements et2_IDetachedDOM, et2_IInput { diff --git a/api/js/etemplate/et2_extension_nextmatch.ts b/api/js/etemplate/et2_extension_nextmatch.ts index 00a74b682e..5770784340 100644 --- a/api/js/etemplate/et2_extension_nextmatch.ts +++ b/api/js/etemplate/et2_extension_nextmatch.ts @@ -73,9 +73,9 @@ import {Et2Dialog} from "./Et2Dialog/Et2Dialog"; import {Et2Select} from "./Et2Select/Et2Select"; import {loadWebComponent} from "./Et2Widget/Et2Widget"; import {Et2AccountFilterHeader} from "./Et2Nextmatch/Headers/AccountFilterHeader"; -import {Et2SelectCategory} from "./Et2Select/Et2SelectCategory"; +import {Et2SelectCategory} from "./Et2Select/Select/Et2SelectCategory"; import {Et2Searchbox} from "./Et2Textbox/Et2Searchbox"; -import {LitElement} from "@lion/core"; +import type {LitElement} from "lit"; //import {et2_selectAccount} from "./et2_widget_SelectAccount"; let keep_import : Et2AccountFilterHeader @@ -3464,7 +3464,6 @@ export class et2_nextmatch_header_bar extends et2_DOMWidget implements et2_INext this.category = this._build_select('cat_id', settings.cat_is_select ? 'et2-select' : 'et2-select-cat', settings.cat_id, settings.cat_is_select !== true, { multiple: false, - tags: true, class: "select-cat", value_class: settings.cat_id_class }); @@ -3482,7 +3481,6 @@ export class et2_nextmatch_header_bar extends et2_DOMWidget implements et2_INext this.filter2 = this._build_select('filter2', 'et2-select', settings.filter2, settings.filter2_no_lang, { multiple: false, - tags: settings.filter2_tags, class: "select-cat", value_class: settings.filter2_class }); @@ -3689,7 +3687,7 @@ export class et2_nextmatch_header_bar extends et2_DOMWidget implements et2_INext // Not mail, since it needs to be different && !['mail'].includes(this.getInstanceManager().app)) { - widget_options.empty_label = this.egw().lang('All categories'); + widget_options.emptyLabel = this.egw().lang('All categories'); } // Create widget @@ -3741,11 +3739,11 @@ export class et2_nextmatch_header_bar extends et2_DOMWidget implements et2_INext }); } // Sometimes the filter does not display the current value - // Call sync to try to get it to display + // Work-around: Request another update to get it to display select.updateComplete.then(async() => { await select.updateComplete; - select.syncItemsFromValue(); + select.requestUpdate("value"); }) return select; } diff --git a/api/js/etemplate/et2_widget_selectAccount.ts b/api/js/etemplate/et2_widget_selectAccount.ts index 0569ea3c20..2cc11f682a 100644 --- a/api/js/etemplate/et2_widget_selectAccount.ts +++ b/api/js/etemplate/et2_widget_selectAccount.ts @@ -12,8 +12,8 @@ * @copyright Nathan Gray 2012 */ -import type {Et2SelectAccountReadonly} from "./Et2Select/Et2SelectReadonly"; -import type {Et2SelectAccount} from "./Et2Select/Et2SelectAccount"; +import type {Et2SelectAccountReadonly} from "./Et2Select/Select/Et2SelectReadonly"; +import type {Et2SelectAccount} from "./Et2Select/Select/Et2SelectAccount"; /** * @deprecated use Et2SelectAccount diff --git a/api/js/etemplate/et2_widget_selectbox.ts b/api/js/etemplate/et2_widget_selectbox.ts index 16a2e6487b..f0e131616a 100644 --- a/api/js/etemplate/et2_widget_selectbox.ts +++ b/api/js/etemplate/et2_widget_selectbox.ts @@ -11,7 +11,7 @@ */ import {Et2Select} from "./Et2Select/Et2Select"; -import {Et2SelectReadonly} from "./Et2Select/Et2SelectReadonly"; +import {Et2SelectReadonly} from "./Et2Select/Select/Et2SelectReadonly"; /** * @deprecated use Et2Select diff --git a/api/js/etemplate/et2_widget_taglist.ts b/api/js/etemplate/et2_widget_taglist.ts index e6638b05da..0f5511f93c 100644 --- a/api/js/etemplate/et2_widget_taglist.ts +++ b/api/js/etemplate/et2_widget_taglist.ts @@ -9,11 +9,13 @@ * @copyright Nathan Gray 2013 */ -import type {Et2Select, Et2SelectState} from "./Et2Select/Et2Select"; -import type {Et2SelectAccount} from "./Et2Select/Et2SelectAccount"; -import type {Et2SelectEmail} from "./Et2Select/Et2SelectEmail"; -import type {Et2SelectCategory} from "./Et2Select/Et2SelectCategory"; -import type {Et2SelectThumbnail} from "./Et2Select/Et2SelectThumbnail"; +import type {Et2Select} from "./Et2Select/Et2Select"; +import type {Et2SelectAccount} from "./Et2Select/Select/Et2SelectAccount"; +import type {Et2SelectEmail} from "./Et2Select/Select/Et2SelectEmail"; +import type {Et2SelectCategory} from "./Et2Select/Select/Et2SelectCategory"; +import type {Et2SelectThumbnail} from "./Et2Select/Select/Et2SelectThumbnail"; + +import {Et2SelectState} from "./Et2Select/Select/Et2SelectState"; /** * @deprecated use Et2Select diff --git a/api/js/etemplate/etemplate2.ts b/api/js/etemplate/etemplate2.ts index 9876a4325c..daf92ff45d 100644 --- a/api/js/etemplate/etemplate2.ts +++ b/api/js/etemplate/etemplate2.ts @@ -71,12 +71,7 @@ import './Et2Nextmatch/Headers/EntryHeader'; import './Et2Nextmatch/Headers/FilterHeader'; import './Et2Select/Et2Listbox'; import './Et2Select/Et2Select'; -import './Et2Select/Et2SelectAccount'; -import './Et2Select/Et2SelectCategory'; -import './Et2Select/Et2SelectCountry'; -import './Et2Select/Et2SelectEmail'; -import './Et2Select/Et2SelectReadonly'; -import './Et2Select/Et2SelectThumbnail' +import './Et2Select/SelectTypes'; import './Et2Select/Tag/Et2Tag'; import './Et2Select/Tag/Et2CategoryTag'; import './Et2Select/Tag/Et2EmailTag'; diff --git a/api/js/jsapi/egw_links.js b/api/js/jsapi/egw_links.js index 4b1dd0bcc0..a70c88a414 100644 --- a/api/js/jsapi/egw_links.js +++ b/api/js/jsapi/egw_links.js @@ -506,40 +506,57 @@ egw.extend('links', egw.MODULE_GLOBAL, function() link_quick_add: function(_parent) { // check if quick-add selectbox is already there, only create it again if not - if (document.getElementById('quick_add_selectbox') || egwIsMobile()) return; + if (document.getElementById('quick_add_selectbox') || egwIsMobile()) + { + return; + } + // Use node as the trigger + const parent = document.getElementById(_parent); const select = document.createElement('et2-select'); select.setAttribute('id', 'quick_add_selectbox'); - document.getElementById(_parent).append(select); + select.placement = "bottom end"; + parent.append(select); + const plus = parent.querySelector("span"); + plus.addEventListener("click", () => { + select.show(); + }) + // bind change handler select.addEventListener('change', () => { - if (select.value) this.open('', select.value, 'add', {}, undefined, select.value, true); + if (select.value) + { + this.open('', select.value, 'add', {}, undefined, select.value, true); + } select.value = ''; }); // need to load common translations for app-names this.langRequire(window, [{app: 'common', lang: this.preference('lang')}], () => { - let options = [{value:'', label: this.lang('Select one...')}]; + let options = []; const apps = this.link_app_list('add'); for(let app in apps) { - if(this.link_get_registry(app, 'no_quick_add')) + if (this.link_get_registry(app, 'no_quick_add')) { continue; } options.push({ - value:app, - label:this.lang(this.link_get_registry(app,'entry') || apps[app]), + value: app, + label: this.lang(this.link_get_registry(app, 'entry') || apps[app]), icon: this.image('navbar', app) }); } select.select_options = options; + /* select.updateComplete.then(() => { - select.dropdown.trigger.style.visibility = 'hidden'; - select.dropdown.trigger.style.height = '0px'; + // Adjust popup positioning to account for hidden select parts + select.popup.distance = -32; }); + + */ }); }, diff --git a/api/templates/default/css/flags.css b/api/templates/default/css/flags.css index 24c66ce15f..1beffdcfa7 100644 --- a/api/templates/default/css/flags.css +++ b/api/templates/default/css/flags.css @@ -1,4 +1,4 @@ -et2-select-country sl-menu-item::part(prefix), et2-select-country .flag { +et2-select-country sl-option::part(prefix), et2-select-country .flag { position: relative; background-image: url(../images/flags.png); background-position: 0 100px; /* to NOT display a flag for every value not explict defined below */ diff --git a/api/templates/default/etemplate2.css b/api/templates/default/etemplate2.css index 25ec88990b..1498a704f3 100644 --- a/api/templates/default/etemplate2.css +++ b/api/templates/default/etemplate2.css @@ -544,6 +544,14 @@ textarea.et2_textbox_ro { /** * Multi-select widget */ +[search][open] et2-textbox::part(base) { + border: none; + box-shadow: none; +} + +[search][open]::part(combobox) { + flex-flow: wrap; +} button.et2_selectbox_expand { width: 16px; display: inline-block; diff --git a/calendar/js/CalendarOwner.ts b/calendar/js/CalendarOwner.ts index c0b771c37a..b84c2d595e 100644 --- a/calendar/js/CalendarOwner.ts +++ b/calendar/js/CalendarOwner.ts @@ -56,18 +56,24 @@ export class CalendarOwner extends Et2StaticSelectMixin(Et2Select) */ _optionTemplate(option : SelectOption) : TemplateResult { + + const checked = this.value == null ? + option.value === this.value || this.multiple && this.value.indexOf(option.value) >= 0 : + this.value.indexOf(option.value) >= 0; + // Tag used must match this.optionTag, but you can't use the variable directly. // Pass option along so SearchMixin can grab it if needed return html` - ${this._iconTemplate(option)} ${this.noLang ? option.label : this.egw().lang(option.label)} ${option.title} - `; + `; } /** diff --git a/calendar/templates/mobile/app.css b/calendar/templates/mobile/app.css index 93f76d17a1..b4e6b7d26b 100644 --- a/calendar/templates/mobile/app.css +++ b/calendar/templates/mobile/app.css @@ -2359,12 +2359,52 @@ e.g. the div with class calendar_calTimeGrid is generated by the timeGridWidget background: #f2f2f2; } .calendar_calDayTodos .calendar_calDayTodosTable table td { - display: inline-block; padding: 3px; } +.calendar_calDayTodos .calendar_calDayTodosTable table td img[src$="svg"] { + background-color: #0c5da5; + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIHZpZXdCb3g9IjAgMCAxIDEiIHByZXNlcnZlQXNwZWN0UmF0aW89Im5vbmUiPjxsaW5lYXJHcmFkaWVudCBpZD0iZzc0MyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMCUiIHkyPSIxMDAlIj48c3RvcCBzdG9wLWNvbG9yPSIjMEM1REE1IiBvZmZzZXQ9IjAiLz48c3RvcCBzdG9wLWNvbG9yPSIjMEM1REE1IiBvZmZzZXQ9IjEiLz48L2xpbmVhckdyYWRpZW50PjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9InVybCgjZzc0MykiLz48L3N2Zz4=); + background-image: -moz-linear-gradient(top, #0C5DA5, #0C5DA5); + background-image: -ms-linear-gradient(top, #0C5DA5, #0C5DA5); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0C5DA5), to(#0C5DA5)); + background-image: -webkit-linear-gradient(top, #0C5DA5, #0C5DA5); + background-image: -o-linear-gradient(top, #0C5DA5, #0C5DA5); + background-image: linear-gradient(top, #0C5DA5, #0C5DA5); + background-repeat: repeat-x; +} .calendar_calDayTodos .calendar_calDayTodosTable table td img { width: 12px; height: 12px; + /*.background_color_10_gray;*/ + -webkit-box-shadow: 0px 1px 0px rgba(0, 0, 0, 0.5); + -moz-box-shadow: 0px 1px 0px rgba(0, 0, 0, 0.5); + box-shadow: 0px 1px 0px rgba(0, 0, 0, 0.5); + border: 1px solid rgba(0, 0, 0, 0.15); + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.calendar_calDayTodos .calendar_calDayTodosTable table td img:hover { + /*.background_color_20_gray;*/ + -webkit-box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.6); + -moz-box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.6); + box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.6); + border: 1px solid rgba(0, 0, 0, 0.5); + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.calendar_calDayTodos .calendar_calDayTodosTable table td img:active { + /*.background_color_30_gray;*/ + border: 1px solid rgba(0, 0, 0, 0.9); + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: inset 1px 2px 1px rgba(0, 0, 0, 0.5); + -moz-box-shadow: inset 1px 2px 1px rgba(0, 0, 0, 0.5); + box-shadow: inset 1px 2px 1px rgba(0, 0, 0, 0.5); + background-color: #b3e4a6; + background-color: #ace29e !important; } .calendar_calDayTodosHeader { text-align: center; diff --git a/home/js/app.ts b/home/js/app.ts index 5f8565bf9b..edf6ee8d0d 100644 --- a/home/js/app.ts +++ b/home/js/app.ts @@ -18,7 +18,7 @@ import "./Et2PortletLink"; import "./Et2PortletList"; import "./Et2PortletNote"; import './Et2PortletWeather'; -import "../../calendar/js/Et2PortletCalendar" +import "../../calendar/js/Et2PortletCalendar"; import Sortable from "sortablejs/modular/sortable.complete.esm.js"; /** diff --git a/mail/js/app.js b/mail/js/app.js index 876e6b4919..fa164efb04 100644 --- a/mail/js/app.js +++ b/mail/js/app.js @@ -24,7 +24,7 @@ import { egw_keyHandler } from "../../api/js/egw_action/egw_keymanager"; import {Et2UrlEmailReadonly} from "../../api/js/etemplate/Et2Url/Et2UrlEmailReadonly"; -import {Et2SelectEmail} from "../../api/js/etemplate/Et2Select/Et2SelectEmail"; +import {Et2SelectEmail} from "../../api/js/etemplate/Et2Select/Select/Et2SelectEmail"; /* required dependency, commented out because no module, but egw:uses is no longer parsed */ diff --git a/mail/templates/default/display.xet b/mail/templates/default/display.xet index 70ea9eab99..68a1d5f4d8 100644 --- a/mail/templates/default/display.xet +++ b/mail/templates/default/display.xet @@ -83,19 +83,19 @@ - + - + - + - + diff --git a/package-lock.json b/package-lock.json index bcbdb9fb02..cd055225a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,26 +10,22 @@ "license": "GPL-2.0", "dependencies": { "@bundled-es-modules/pdfjs-dist": "^2.5.207-rc1", - "@lion/button": "^0.16.0", - "@lion/combobox": "^0.9.0", "@lion/core": "^0.21.1", - "@lion/dialog": "^0.14.0", "@lion/form-core": "^0.16.0", - "@lion/input": "^0.16.0", - "@lion/listbox": "^0.12.0", - "@lion/select": "^0.15.0", - "@lion/textarea": "^0.14.0", "@rollup/plugin-commonjs": "^24.0.1", - "@shoelace-style/shoelace": "2.0.0-beta.81", + "@shoelace-style/shoelace": "2.8.0", "blueimp-gallery": "^3.4.0", "colortranslator": "^1.9.2", "core-js": "^3.29.1", + "lit": "^2.7.5", "lit-flatpickr": "^0.3.0", "shortcut-buttons-flatpickr": "^0.4.0", "sortablejs": "^1.14.0" }, "devDependencies": { "@babel/core": "^7.14.6", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-decorators": "^7.22.10", "@babel/preset-env": "^7.20.2", "@babel/preset-typescript": "^7.14.5", "@interactjs/interactjs": "^1.10.11", @@ -597,6 +593,7 @@ "version": "7.20.7", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead.", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", @@ -615,6 +612,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", "dev": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.18.6", @@ -631,6 +629,7 @@ "version": "7.21.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-static-block instead.", "dev": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.21.0", @@ -644,10 +643,317 @@ "@babel/core": "^7.12.0" } }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.22.10.tgz", + "integrity": "sha512-KxN6TqZzcFi4uD3UifqXElBTBNLAEH1l3vzMQj6JwJZbL2sZlThxSViOKCYY+4Ah4V4JhQ95IVB7s/Y6SJSlMQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.10", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/plugin-syntax-decorators": "^7.22.10" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/@babel/code-frame": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", + "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.10", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.10.tgz", + "integrity": "sha512-5IBb77txKYQPpOEdUdIhBx8VrZyDCQ+H82H0+5dX1TmuscP5vJKEE3cKurjtIw/vFwzbVH48VweE78kVDBrqjA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/@babel/helper-environment-visitor": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/@babel/helper-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", + "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/@babel/helper-replace-supers": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz", + "integrity": "sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/@babel/helper-validator-identifier": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/@babel/highlight": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", + "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/@babel/parser": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz", + "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/@babel/types": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", + "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-proposal-decorators/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/plugin-proposal-dynamic-import": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-dynamic-import instead.", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", @@ -664,6 +970,7 @@ "version": "7.18.9", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-export-namespace-from instead.", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.18.9", @@ -680,6 +987,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-json-strings instead.", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", @@ -696,6 +1004,7 @@ "version": "7.20.7", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-logical-assignment-operators instead.", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.20.2", @@ -712,6 +1021,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", @@ -728,6 +1038,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", @@ -744,6 +1055,7 @@ "version": "7.20.7", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", "dev": true, "dependencies": { "@babel/compat-data": "^7.20.5", @@ -763,6 +1075,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead.", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", @@ -779,6 +1092,7 @@ "version": "7.21.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.20.2", @@ -796,6 +1110,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", "dev": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.18.6", @@ -812,6 +1127,7 @@ "version": "7.21.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz", "integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", @@ -830,6 +1146,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-unicode-property-regex instead.", "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", @@ -881,6 +1198,30 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.22.10.tgz", + "integrity": "sha512-z1KTVemBjnz+kSEilAsI4lbkPOl5TvJH7YDSY1CTIzvLWJ+KHXp+mRe8VPmfnyvqOPqar1V2gid2PleKzRUstQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators/node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/plugin-syntax-dynamic-import": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", @@ -1772,6 +2113,14 @@ "resolved": "https://registry.npmjs.org/@bundled-es-modules/pdfjs-dist/-/pdfjs-dist-2.5.207-rc1.tgz", "integrity": "sha512-e/UVP1g6dwjQLnu4MPf/mlESCIvyr/KgpoMUyxGcv4evCIuJwKR/fcfhG3p1NYo+49gJsd0hL2yz9kzhkCZ32A==" }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "engines": { + "node": ">=10" + } + }, "node_modules/@esm-bundle/chai": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/@esm-bundle/chai/-/chai-4.3.4.tgz", @@ -1782,18 +2131,27 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.1.0.tgz", - "integrity": "sha512-zbsLwtnHo84w1Kc8rScAo5GMk1GdecSlrflIbfnEBJwvTSj1SL6kkOYV+nHraMCPEy+RNZZUaZyL8JosDGCtGQ==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.4.1.tgz", + "integrity": "sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ==", + "dependencies": { + "@floating-ui/utils": "^0.1.1" + } }, "node_modules/@floating-ui/dom": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.1.0.tgz", - "integrity": "sha512-TSogMPVxbRe77QCj1dt8NmRiJasPvuc+eT5jnJ6YpLqgOD2zXc5UA3S1qwybN+GVCDNdKfpKy1oj8RpzLJvh6A==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.1.tgz", + "integrity": "sha512-KwvVcPSXg6mQygvA1TjbN/gh///36kKtllIF8SUm0qpFj8+rvYrpvlYdL1JoA71SHpDqgSSdGOSoQ0Mp3uY5aw==", "dependencies": { - "@floating-ui/core": "^1.0.5" + "@floating-ui/core": "^1.4.1", + "@floating-ui/utils": "^0.1.1" } }, + "node_modules/@floating-ui/utils": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.1.tgz", + "integrity": "sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==" + }, "node_modules/@interactjs/actions": { "version": "1.10.11", "resolved": "https://registry.npmjs.org/@interactjs/actions/-/actions-1.10.11.tgz", @@ -2038,25 +2396,6 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, - "node_modules/@lion/button": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@lion/button/-/button-0.16.0.tgz", - "integrity": "sha512-9HspdzHo4oKel8Zx/lIU1QfhMGsOn3FWskQZdSL6EHAD9yuxuVCIHjCdxzX9wP5zTfDrE23f35g6mOM9FAzp2w==", - "dependencies": { - "@lion/core": "^0.21.0" - } - }, - "node_modules/@lion/combobox": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@lion/combobox/-/combobox-0.9.0.tgz", - "integrity": "sha512-FEOnuH+s9NSFZTcCEYvLIy6IWz4c8qUgGhackcXEGjDf5/Z8VItbhWdfPZ0WrA1bFnR+S0n4vovJWsYWSFovzw==", - "dependencies": { - "@lion/core": "^0.21.0", - "@lion/form-core": "^0.16.0", - "@lion/listbox": "^0.12.0", - "@lion/overlays": "^0.31.0" - } - }, "node_modules/@lion/core": { "version": "0.21.1", "resolved": "https://registry.npmjs.org/@lion/core/-/core-0.21.1.tgz", @@ -2067,15 +2406,6 @@ "lit": "^2.0.2" } }, - "node_modules/@lion/dialog": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@lion/dialog/-/dialog-0.14.0.tgz", - "integrity": "sha512-YU8A693un6BxiACOInEp+a1YFLB2/wapoYlAfpli2LfWPuM3e7WSGrEn8d8nn75/A3LBPza1PhrZyJ16AWxG1A==", - "dependencies": { - "@lion/core": "^0.21.0", - "@lion/overlays": "^0.31.0" - } - }, "node_modules/@lion/form-core": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/@lion/form-core/-/form-core-0.16.0.tgz", @@ -2085,23 +2415,6 @@ "@lion/localize": "^0.23.0" } }, - "node_modules/@lion/input": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@lion/input/-/input-0.16.0.tgz", - "integrity": "sha512-AmhMkM1MUqu3aju0BJ5QINkSAbuLlYiK/pQqjvEX4T2+GQJPAl4Do5u4Ks2zLY3RgOzIsfg8cOjTJDlum93mNA==", - "dependencies": { - "@lion/form-core": "^0.16.0" - } - }, - "node_modules/@lion/listbox": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@lion/listbox/-/listbox-0.12.0.tgz", - "integrity": "sha512-RupZ6KIsZk7D62ZrSmk0WXt5W0d86ynzUyzPAOGW3tby58LmpKEjt3NwyhMCrCpjpH6Ib7sPsy0/46wVk4ynRw==", - "dependencies": { - "@lion/core": "^0.21.0", - "@lion/form-core": "^0.16.0" - } - }, "node_modules/@lion/localize": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@lion/localize/-/localize-0.23.0.tgz", @@ -2112,40 +2425,13 @@ "singleton-manager": "^1.4.3" } }, - "node_modules/@lion/overlays": { - "version": "0.31.0", - "resolved": "https://registry.npmjs.org/@lion/overlays/-/overlays-0.31.0.tgz", - "integrity": "sha512-4jCoan6QjUARx7UddsZogqgQAVyacu82JaR8raMzIb1eZWo3m8w/hxDCssdYrsDbXJCiOgwgAfE0Kd929GBUpw==", - "dependencies": { - "@lion/core": "^0.21.0", - "@popperjs/core": "^2.5.4", - "singleton-manager": "^1.4.3" - } - }, - "node_modules/@lion/select": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@lion/select/-/select-0.15.0.tgz", - "integrity": "sha512-cnwSJALSQdqpN9BNYrogq5L4lp7QjvgLgZ20kDNn4xPyzMbRe/NDumHPgAr/YiP9op5AlYFN9jfG8wnd5q7tvA==", - "dependencies": { - "@lion/core": "^0.21.0", - "@lion/form-core": "^0.16.0" - } - }, - "node_modules/@lion/textarea": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@lion/textarea/-/textarea-0.14.0.tgz", - "integrity": "sha512-Ldj2EUtaKEiA0XyZVFH65C7IiGdKrUPU7X6t8RSfzt4PAWXJKWTzt9498k8nJdWZmTuFtV9E5SZyQsZO/OPGTQ==", - "dependencies": { - "@lion/core": "^0.21.0", - "@lion/form-core": "^0.16.0", - "@types/autosize": "^3.0.7", - "autosize": "4.0.2" - } - }, "node_modules/@lit-labs/react": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@lit-labs/react/-/react-1.1.1.tgz", - "integrity": "sha512-9TC+/ZWb6BJlWCyUr14FKFlaGnyKpeEDorufXozQgke/VoVrslUQNaL7nBmrAWdNrmzx5jWgi8lFmWwrxMjnlA==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@lit-labs/react/-/react-2.0.1.tgz", + "integrity": "sha512-Nj+XB3HamqaWefN91lpFPJaqjJ78XzGkPWCedB4jyH22GBFEenpE9A/h8B/2dnIGXtNtd9D/RFpUdQ/dBtWFqA==", + "peerDependencies": { + "@types/react": "17 || 18" + } }, "node_modules/@lit-labs/ssr-dom-shim": { "version": "1.0.0", @@ -2268,15 +2554,6 @@ "lit": "^2.0.0" } }, - "node_modules/@popperjs/core": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz", - "integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, "node_modules/@rollup/plugin-babel": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz", @@ -2496,21 +2773,22 @@ } }, "node_modules/@shoelace-style/localize": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@shoelace-style/localize/-/localize-3.0.4.tgz", - "integrity": "sha512-HFY90KD+b1Td2otSBryCOpQjBEArIwlV6Tv4J4rC/E/D5wof2eLF6JUVrbiRNn8GRmwATe4YDAEK7NUD08xO1w==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@shoelace-style/localize/-/localize-3.1.1.tgz", + "integrity": "sha512-NkM/hj3Js6yXCU9WxhsyxRUdyqUUUl/BSvIluUMptQteUWGOJaoyP1iMbOMqO544DYMzBfnoCw66ZHkGuTdKgA==" }, "node_modules/@shoelace-style/shoelace": { - "version": "2.0.0-beta.81", - "resolved": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.0.0-beta.81.tgz", - "integrity": "sha512-mHxE450EiPnZeDGwJ8Dn0pXUz9U5nkjHJhXZ6REewq4RFeRFadaxlc7j/ic076U7Vdx/0PXWeubZOnWWXrMw4w==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.8.0.tgz", + "integrity": "sha512-WigVUaW+MptefOW4zUlZaq+zn0n2hP+I4/mztoeJii5u3ex1CGexfQGcdw2gx6d7P7LAa6/NW0TlgAELzJQnCA==", "dependencies": { - "@floating-ui/dom": "^1.0.1", - "@lit-labs/react": "^1.0.7", + "@ctrl/tinycolor": "^3.5.0", + "@floating-ui/dom": "^1.2.1", + "@lit-labs/react": "^2.0.1", "@shoelace-style/animations": "^1.1.0", - "@shoelace-style/localize": "^3.0.1", - "color": "4.2", - "lit": "^2.2.8", + "@shoelace-style/localize": "^3.1.1", + "composed-offset-position": "^0.0.4", + "lit": "^2.7.5", "qr-creator": "^1.0.0" }, "engines": { @@ -2565,14 +2843,6 @@ "@types/node": "*" } }, - "node_modules/@types/autosize": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/autosize/-/autosize-3.0.7.tgz", - "integrity": "sha512-D46m3aBNg81QKk9ZigmDFuhXUkD4IpBSrkGUKpYo2QBETbUjqEe8msXNCcECaXLXv1O4ppdMpizgFRzpfrgOxA==", - "dependencies": { - "@types/jquery": "*" - } - }, "node_modules/@types/babel__code-frame": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/babel__code-frame/-/babel__code-frame-7.0.3.tgz", @@ -2723,14 +2993,6 @@ "@types/istanbul-lib-report": "*" } }, - "node_modules/@types/jquery": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.5.tgz", - "integrity": "sha512-6RXU9Xzpc6vxNrS6FPPapN1SxSHgQ336WC6Jj/N8q30OiaBZ00l1GBgeP7usjVZPivSkGUfL1z/WW6TX989M+w==", - "dependencies": { - "@types/sizzle": "*" - } - }, "node_modules/@types/keygrip": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz", @@ -2786,6 +3048,12 @@ "integrity": "sha512-ARATsLdrGPUnaBvxLhUlnltcMgn7pQG312S8ccdYlnyijabrX9RN/KN/iGj9Am96CoW8e/K9628BA7Bv4XHdrA==", "dev": true }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "peer": true + }, "node_modules/@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", @@ -2798,6 +3066,17 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", "dev": true }, + "node_modules/@types/react": { + "version": "18.2.21", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz", + "integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==", + "peer": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -2807,6 +3086,12 @@ "@types/node": "*" } }, + "node_modules/@types/scheduler": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", + "peer": true + }, "node_modules/@types/serve-static": { "version": "1.13.10", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", @@ -2836,15 +3121,10 @@ "@types/sinon": "*" } }, - "node_modules/@types/sizzle": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", - "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==" - }, "node_modules/@types/trusted-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", - "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", + "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==" }, "node_modules/@types/uuid": { "version": "8.3.1", @@ -2896,9 +3176,9 @@ } }, "node_modules/@web/config-loader/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -3540,11 +3820,6 @@ "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", "dev": true }, - "node_modules/autosize": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/autosize/-/autosize-4.0.2.tgz", - "integrity": "sha512-jnSyH2d+qdfPGpWlcuhGiHmqBJ6g3X+8T+iRwFrHPLVcdoGJE/x6Qicm6aDHfTsbgZKxyV8UU/YB2p4cjKDRRA==" - }, "node_modules/axe-core": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.5.tgz", @@ -3941,22 +4216,11 @@ "type-is": "^1.6.16" } }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -3967,16 +4231,8 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/colors": { "version": "1.1.2", @@ -4113,6 +4369,11 @@ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" }, + "node_modules/composed-offset-position": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/composed-offset-position/-/composed-offset-position-0.0.4.tgz", + "integrity": "sha512-vMlvu1RuNegVE0YsCDSV/X4X10j56mq7PCIyOKK74FxkXzGLwhOUmdkJLSdOBOMwWycobGUMgft2lp+YgTe8hw==" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4185,6 +4446,12 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", + "peer": true + }, "node_modules/dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -5283,11 +5550,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -5820,24 +6082,30 @@ "dev": true }, "node_modules/lit": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/lit/-/lit-2.6.1.tgz", - "integrity": "sha512-DT87LD64f8acR7uVp7kZfhLRrHkfC/N4BVzAtnw9Yg8087mbBJ//qedwdwX0kzDbxgPccWRW6mFwGbRQIxy0pw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/lit/-/lit-2.8.0.tgz", + "integrity": "sha512-4Sc3OFX9QHOJaHbmTMk28SYgVxLN3ePDjg7hofEft2zWlehFL3LiAuapWc4U/kYwMYJSh2hTCPZ6/LIC7ii0MA==", "dependencies": { "@lit/reactive-element": "^1.6.0", - "lit-element": "^3.2.0", - "lit-html": "^2.6.0" + "lit-element": "^3.3.0", + "lit-html": "^2.8.0" } }, "node_modules/lit-element": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.2.0.tgz", - "integrity": "sha512-HbE7yt2SnUtg5DCrWt028oaU4D5F4k/1cntAFHTkzY8ZIa8N0Wmu92PxSxucsQSOXlODFrICkQ5x/tEshKi13g==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.3.3.tgz", + "integrity": "sha512-XbeRxmTHubXENkV4h8RIPyr8lXc+Ff28rkcQzw3G6up2xg5E8Zu1IgOWIwBLEQsu3cOVFqdYwiVi0hv0SlpqUA==", "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.1.0", "@lit/reactive-element": "^1.3.0", - "lit-html": "^2.2.0" + "lit-html": "^2.8.0" } }, + "node_modules/lit-element/node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.1.1.tgz", + "integrity": "sha512-kXOeFbfCm4fFf2A3WwVEeQj55tMZa8c8/f9AKHMobQMkzNUfUj+antR3fRPaZJawsa1aZiP/Da3ndpZrwEe4rQ==" + }, "node_modules/lit-flatpickr": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/lit-flatpickr/-/lit-flatpickr-0.3.0.tgz", @@ -5849,9 +6117,9 @@ } }, "node_modules/lit-html": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.6.1.tgz", - "integrity": "sha512-Z3iw+E+3KKFn9t2YKNjsXNEu/LRLI98mtH/C6lnFg7kvaqPIzPn124Yd4eT/43lyqrejpc5Wb6BHq3fdv4S8Rw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.8.0.tgz", + "integrity": "sha512-o9t+MQM3P4y7M7yNzqAyjp7z+mQGa4NS4CxiyLqFPyFWyc4O+nodLrkrxSaCTrla6M5YOLaT3RpbbqjszB5g3Q==", "dependencies": { "@types/trusted-types": "^2.0.2" } @@ -7019,7 +7287,7 @@ "version": "2.79.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", - "dev": true, + "devOptional": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -7034,6 +7302,7 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser", "dev": true, "dependencies": { "@babel/code-frame": "^7.10.4", @@ -7099,9 +7368,9 @@ "dev": true }, "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -7147,14 +7416,6 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, "node_modules/singleton-manager": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/singleton-manager/-/singleton-manager-1.4.3.tgz", @@ -8396,6 +8657,233 @@ "@babel/plugin-syntax-class-static-block": "^7.14.5" } }, + "@babel/plugin-proposal-decorators": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.22.10.tgz", + "integrity": "sha512-KxN6TqZzcFi4uD3UifqXElBTBNLAEH1l3vzMQj6JwJZbL2sZlThxSViOKCYY+4Ah4V4JhQ95IVB7s/Y6SJSlMQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.22.10", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/plugin-syntax-decorators": "^7.22.10" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", + "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.22.10", + "chalk": "^2.4.2" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.10.tgz", + "integrity": "sha512-5IBb77txKYQPpOEdUdIhBx8VrZyDCQ+H82H0+5dX1TmuscP5vJKEE3cKurjtIw/vFwzbVH48VweE78kVDBrqjA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "dev": true + }, + "@babel/helper-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "dev": true, + "requires": { + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", + "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true + }, + "@babel/helper-replace-supers": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz", + "integrity": "sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "dev": true + }, + "@babel/highlight": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", + "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz", + "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==", + "dev": true + }, + "@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + } + }, + "@babel/types": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", + "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "to-fast-properties": "^2.0.0" + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "@babel/plugin-proposal-dynamic-import": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", @@ -8549,6 +9037,23 @@ "@babel/helper-plugin-utils": "^7.14.5" } }, + "@babel/plugin-syntax-decorators": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.22.10.tgz", + "integrity": "sha512-z1KTVemBjnz+kSEilAsI4lbkPOl5TvJH7YDSY1CTIzvLWJ+KHXp+mRe8VPmfnyvqOPqar1V2gid2PleKzRUstQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true + } + } + }, "@babel/plugin-syntax-dynamic-import": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", @@ -9164,6 +9669,11 @@ "resolved": "https://registry.npmjs.org/@bundled-es-modules/pdfjs-dist/-/pdfjs-dist-2.5.207-rc1.tgz", "integrity": "sha512-e/UVP1g6dwjQLnu4MPf/mlESCIvyr/KgpoMUyxGcv4evCIuJwKR/fcfhG3p1NYo+49gJsd0hL2yz9kzhkCZ32A==" }, + "@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==" + }, "@esm-bundle/chai": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/@esm-bundle/chai/-/chai-4.3.4.tgz", @@ -9174,18 +9684,27 @@ } }, "@floating-ui/core": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.1.0.tgz", - "integrity": "sha512-zbsLwtnHo84w1Kc8rScAo5GMk1GdecSlrflIbfnEBJwvTSj1SL6kkOYV+nHraMCPEy+RNZZUaZyL8JosDGCtGQ==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.4.1.tgz", + "integrity": "sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ==", + "requires": { + "@floating-ui/utils": "^0.1.1" + } }, "@floating-ui/dom": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.1.0.tgz", - "integrity": "sha512-TSogMPVxbRe77QCj1dt8NmRiJasPvuc+eT5jnJ6YpLqgOD2zXc5UA3S1qwybN+GVCDNdKfpKy1oj8RpzLJvh6A==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.1.tgz", + "integrity": "sha512-KwvVcPSXg6mQygvA1TjbN/gh///36kKtllIF8SUm0qpFj8+rvYrpvlYdL1JoA71SHpDqgSSdGOSoQ0Mp3uY5aw==", "requires": { - "@floating-ui/core": "^1.0.5" + "@floating-ui/core": "^1.4.1", + "@floating-ui/utils": "^0.1.1" } }, + "@floating-ui/utils": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.1.tgz", + "integrity": "sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==" + }, "@interactjs/actions": { "version": "1.10.11", "resolved": "https://registry.npmjs.org/@interactjs/actions/-/actions-1.10.11.tgz", @@ -9217,7 +9736,8 @@ "version": "1.10.11", "resolved": "https://registry.npmjs.org/@interactjs/core/-/core-1.10.11.tgz", "integrity": "sha512-aJ50ccVeszpJt7wPH7Yfqm7f1aG1SA94qd90P0NaESh5/QUXn4CESO6igobo4DFHQ5z+1Rfdl8aphP4JxlH4gw==", - "dev": true + "dev": true, + "requires": {} }, "@interactjs/dev-tools": { "version": "1.10.11", @@ -9375,25 +9895,6 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, - "@lion/button": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@lion/button/-/button-0.16.0.tgz", - "integrity": "sha512-9HspdzHo4oKel8Zx/lIU1QfhMGsOn3FWskQZdSL6EHAD9yuxuVCIHjCdxzX9wP5zTfDrE23f35g6mOM9FAzp2w==", - "requires": { - "@lion/core": "^0.21.0" - } - }, - "@lion/combobox": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@lion/combobox/-/combobox-0.9.0.tgz", - "integrity": "sha512-FEOnuH+s9NSFZTcCEYvLIy6IWz4c8qUgGhackcXEGjDf5/Z8VItbhWdfPZ0WrA1bFnR+S0n4vovJWsYWSFovzw==", - "requires": { - "@lion/core": "^0.21.0", - "@lion/form-core": "^0.16.0", - "@lion/listbox": "^0.12.0", - "@lion/overlays": "^0.31.0" - } - }, "@lion/core": { "version": "0.21.1", "resolved": "https://registry.npmjs.org/@lion/core/-/core-0.21.1.tgz", @@ -9404,15 +9905,6 @@ "lit": "^2.0.2" } }, - "@lion/dialog": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@lion/dialog/-/dialog-0.14.0.tgz", - "integrity": "sha512-YU8A693un6BxiACOInEp+a1YFLB2/wapoYlAfpli2LfWPuM3e7WSGrEn8d8nn75/A3LBPza1PhrZyJ16AWxG1A==", - "requires": { - "@lion/core": "^0.21.0", - "@lion/overlays": "^0.31.0" - } - }, "@lion/form-core": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/@lion/form-core/-/form-core-0.16.0.tgz", @@ -9422,23 +9914,6 @@ "@lion/localize": "^0.23.0" } }, - "@lion/input": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@lion/input/-/input-0.16.0.tgz", - "integrity": "sha512-AmhMkM1MUqu3aju0BJ5QINkSAbuLlYiK/pQqjvEX4T2+GQJPAl4Do5u4Ks2zLY3RgOzIsfg8cOjTJDlum93mNA==", - "requires": { - "@lion/form-core": "^0.16.0" - } - }, - "@lion/listbox": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@lion/listbox/-/listbox-0.12.0.tgz", - "integrity": "sha512-RupZ6KIsZk7D62ZrSmk0WXt5W0d86ynzUyzPAOGW3tby58LmpKEjt3NwyhMCrCpjpH6Ib7sPsy0/46wVk4ynRw==", - "requires": { - "@lion/core": "^0.21.0", - "@lion/form-core": "^0.16.0" - } - }, "@lion/localize": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@lion/localize/-/localize-0.23.0.tgz", @@ -9449,40 +9924,11 @@ "singleton-manager": "^1.4.3" } }, - "@lion/overlays": { - "version": "0.31.0", - "resolved": "https://registry.npmjs.org/@lion/overlays/-/overlays-0.31.0.tgz", - "integrity": "sha512-4jCoan6QjUARx7UddsZogqgQAVyacu82JaR8raMzIb1eZWo3m8w/hxDCssdYrsDbXJCiOgwgAfE0Kd929GBUpw==", - "requires": { - "@lion/core": "^0.21.0", - "@popperjs/core": "^2.5.4", - "singleton-manager": "^1.4.3" - } - }, - "@lion/select": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@lion/select/-/select-0.15.0.tgz", - "integrity": "sha512-cnwSJALSQdqpN9BNYrogq5L4lp7QjvgLgZ20kDNn4xPyzMbRe/NDumHPgAr/YiP9op5AlYFN9jfG8wnd5q7tvA==", - "requires": { - "@lion/core": "^0.21.0", - "@lion/form-core": "^0.16.0" - } - }, - "@lion/textarea": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@lion/textarea/-/textarea-0.14.0.tgz", - "integrity": "sha512-Ldj2EUtaKEiA0XyZVFH65C7IiGdKrUPU7X6t8RSfzt4PAWXJKWTzt9498k8nJdWZmTuFtV9E5SZyQsZO/OPGTQ==", - "requires": { - "@lion/core": "^0.21.0", - "@lion/form-core": "^0.16.0", - "@types/autosize": "^3.0.7", - "autosize": "4.0.2" - } - }, "@lit-labs/react": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@lit-labs/react/-/react-1.1.1.tgz", - "integrity": "sha512-9TC+/ZWb6BJlWCyUr14FKFlaGnyKpeEDorufXozQgke/VoVrslUQNaL7nBmrAWdNrmzx5jWgi8lFmWwrxMjnlA==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@lit-labs/react/-/react-2.0.1.tgz", + "integrity": "sha512-Nj+XB3HamqaWefN91lpFPJaqjJ78XzGkPWCedB4jyH22GBFEenpE9A/h8B/2dnIGXtNtd9D/RFpUdQ/dBtWFqA==", + "requires": {} }, "@lit-labs/ssr-dom-shim": { "version": "1.0.0", @@ -9598,11 +10044,6 @@ "lit": "^2.0.0" } }, - "@popperjs/core": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz", - "integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==" - }, "@rollup/plugin-babel": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz", @@ -9752,21 +10193,22 @@ "integrity": "sha512-Be+cahtZyI2dPKRm8EZSx3YJQ+jLvEcn3xzRP7tM4tqBnvd/eW/64Xh0iOf0t2w5P8iJKfdBbpVNE9naCaOf2g==" }, "@shoelace-style/localize": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@shoelace-style/localize/-/localize-3.0.4.tgz", - "integrity": "sha512-HFY90KD+b1Td2otSBryCOpQjBEArIwlV6Tv4J4rC/E/D5wof2eLF6JUVrbiRNn8GRmwATe4YDAEK7NUD08xO1w==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@shoelace-style/localize/-/localize-3.1.1.tgz", + "integrity": "sha512-NkM/hj3Js6yXCU9WxhsyxRUdyqUUUl/BSvIluUMptQteUWGOJaoyP1iMbOMqO544DYMzBfnoCw66ZHkGuTdKgA==" }, "@shoelace-style/shoelace": { - "version": "2.0.0-beta.81", - "resolved": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.0.0-beta.81.tgz", - "integrity": "sha512-mHxE450EiPnZeDGwJ8Dn0pXUz9U5nkjHJhXZ6REewq4RFeRFadaxlc7j/ic076U7Vdx/0PXWeubZOnWWXrMw4w==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.8.0.tgz", + "integrity": "sha512-WigVUaW+MptefOW4zUlZaq+zn0n2hP+I4/mztoeJii5u3ex1CGexfQGcdw2gx6d7P7LAa6/NW0TlgAELzJQnCA==", "requires": { - "@floating-ui/dom": "^1.0.1", - "@lit-labs/react": "^1.0.7", + "@ctrl/tinycolor": "^3.5.0", + "@floating-ui/dom": "^1.2.1", + "@lit-labs/react": "^2.0.1", "@shoelace-style/animations": "^1.1.0", - "@shoelace-style/localize": "^3.0.1", - "color": "4.2", - "lit": "^2.2.8", + "@shoelace-style/localize": "^3.1.1", + "composed-offset-position": "^0.0.4", + "lit": "^2.7.5", "qr-creator": "^1.0.0" } }, @@ -9814,14 +10256,6 @@ "@types/node": "*" } }, - "@types/autosize": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/autosize/-/autosize-3.0.7.tgz", - "integrity": "sha512-D46m3aBNg81QKk9ZigmDFuhXUkD4IpBSrkGUKpYo2QBETbUjqEe8msXNCcECaXLXv1O4ppdMpizgFRzpfrgOxA==", - "requires": { - "@types/jquery": "*" - } - }, "@types/babel__code-frame": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/babel__code-frame/-/babel__code-frame-7.0.3.tgz", @@ -9972,14 +10406,6 @@ "@types/istanbul-lib-report": "*" } }, - "@types/jquery": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.5.tgz", - "integrity": "sha512-6RXU9Xzpc6vxNrS6FPPapN1SxSHgQ336WC6Jj/N8q30OiaBZ00l1GBgeP7usjVZPivSkGUfL1z/WW6TX989M+w==", - "requires": { - "@types/sizzle": "*" - } - }, "@types/keygrip": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz", @@ -10035,6 +10461,12 @@ "integrity": "sha512-ARATsLdrGPUnaBvxLhUlnltcMgn7pQG312S8ccdYlnyijabrX9RN/KN/iGj9Am96CoW8e/K9628BA7Bv4XHdrA==", "dev": true }, + "@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "peer": true + }, "@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", @@ -10047,6 +10479,17 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", "dev": true }, + "@types/react": { + "version": "18.2.21", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz", + "integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==", + "peer": true, + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, "@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -10056,6 +10499,12 @@ "@types/node": "*" } }, + "@types/scheduler": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", + "peer": true + }, "@types/serve-static": { "version": "1.13.10", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", @@ -10085,15 +10534,10 @@ "@types/sinon": "*" } }, - "@types/sizzle": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", - "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==" - }, "@types/trusted-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", - "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", + "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==" }, "@types/uuid": { "version": "8.3.1", @@ -10139,9 +10583,9 @@ }, "dependencies": { "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -10623,11 +11067,6 @@ "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", "dev": true }, - "autosize": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/autosize/-/autosize-4.0.2.tgz", - "integrity": "sha512-jnSyH2d+qdfPGpWlcuhGiHmqBJ6g3X+8T+iRwFrHPLVcdoGJE/x6Qicm6aDHfTsbgZKxyV8UU/YB2p4cjKDRRA==" - }, "axe-core": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.5.tgz", @@ -10900,19 +11339,11 @@ "type-is": "^1.6.16" } }, - "color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "requires": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - } - }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } @@ -10920,16 +11351,8 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "colors": { "version": "1.1.2", @@ -11041,6 +11464,11 @@ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" }, + "composed-offset-position": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/composed-offset-position/-/composed-offset-position-0.0.4.tgz", + "integrity": "sha512-vMlvu1RuNegVE0YsCDSV/X4X10j56mq7PCIyOKK74FxkXzGLwhOUmdkJLSdOBOMwWycobGUMgft2lp+YgTe8hw==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -11095,6 +11523,12 @@ "browserslist": "^4.21.5" } }, + "csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", + "peer": true + }, "dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -11926,11 +12360,6 @@ "is-windows": "^1.0.1" } }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -12360,22 +12789,30 @@ } }, "lit": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/lit/-/lit-2.6.1.tgz", - "integrity": "sha512-DT87LD64f8acR7uVp7kZfhLRrHkfC/N4BVzAtnw9Yg8087mbBJ//qedwdwX0kzDbxgPccWRW6mFwGbRQIxy0pw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/lit/-/lit-2.8.0.tgz", + "integrity": "sha512-4Sc3OFX9QHOJaHbmTMk28SYgVxLN3ePDjg7hofEft2zWlehFL3LiAuapWc4U/kYwMYJSh2hTCPZ6/LIC7ii0MA==", "requires": { "@lit/reactive-element": "^1.6.0", - "lit-element": "^3.2.0", - "lit-html": "^2.6.0" + "lit-element": "^3.3.0", + "lit-html": "^2.8.0" } }, "lit-element": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.2.0.tgz", - "integrity": "sha512-HbE7yt2SnUtg5DCrWt028oaU4D5F4k/1cntAFHTkzY8ZIa8N0Wmu92PxSxucsQSOXlODFrICkQ5x/tEshKi13g==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.3.3.tgz", + "integrity": "sha512-XbeRxmTHubXENkV4h8RIPyr8lXc+Ff28rkcQzw3G6up2xg5E8Zu1IgOWIwBLEQsu3cOVFqdYwiVi0hv0SlpqUA==", "requires": { + "@lit-labs/ssr-dom-shim": "^1.1.0", "@lit/reactive-element": "^1.3.0", - "lit-html": "^2.2.0" + "lit-html": "^2.8.0" + }, + "dependencies": { + "@lit-labs/ssr-dom-shim": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.1.1.tgz", + "integrity": "sha512-kXOeFbfCm4fFf2A3WwVEeQj55tMZa8c8/f9AKHMobQMkzNUfUj+antR3fRPaZJawsa1aZiP/Da3ndpZrwEe4rQ==" + } } }, "lit-flatpickr": { @@ -12389,9 +12826,9 @@ } }, "lit-html": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.6.1.tgz", - "integrity": "sha512-Z3iw+E+3KKFn9t2YKNjsXNEu/LRLI98mtH/C6lnFg7kvaqPIzPn124Yd4eT/43lyqrejpc5Wb6BHq3fdv4S8Rw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.8.0.tgz", + "integrity": "sha512-o9t+MQM3P4y7M7yNzqAyjp7z+mQGa4NS4CxiyLqFPyFWyc4O+nodLrkrxSaCTrla6M5YOLaT3RpbbqjszB5g3Q==", "requires": { "@types/trusted-types": "^2.0.2" } @@ -13289,7 +13726,7 @@ "version": "2.79.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", - "dev": true, + "devOptional": true, "requires": { "fsevents": "~2.3.2" } @@ -13342,9 +13779,9 @@ "dev": true }, "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, "serialize-javascript": { @@ -13384,14 +13821,6 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "requires": { - "is-arrayish": "^0.3.1" - } - }, "singleton-manager": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/singleton-manager/-/singleton-manager-1.4.3.tgz", @@ -13936,7 +14365,8 @@ "version": "7.5.3", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", - "dev": true + "dev": true, + "requires": {} }, "yallist": { "version": "4.0.0", diff --git a/package.json b/package.json index f5b0bc74bc..e6076e9af5 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ }, "devDependencies": { "@babel/core": "^7.14.6", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-decorators": "^7.22.10", "@babel/preset-env": "^7.20.2", "@babel/preset-typescript": "^7.14.5", "@interactjs/interactjs": "^1.10.11", @@ -54,20 +56,14 @@ }, "dependencies": { "@bundled-es-modules/pdfjs-dist": "^2.5.207-rc1", - "@lion/button": "^0.16.0", - "@lion/combobox": "^0.9.0", "@lion/core": "^0.21.1", - "@lion/dialog": "^0.14.0", "@lion/form-core": "^0.16.0", - "@lion/input": "^0.16.0", - "@lion/listbox": "^0.12.0", - "@lion/select": "^0.15.0", - "@lion/textarea": "^0.14.0", "@rollup/plugin-commonjs": "^24.0.1", - "@shoelace-style/shoelace": "2.0.0-beta.81", + "@shoelace-style/shoelace": "2.8.0", "blueimp-gallery": "^3.4.0", "colortranslator": "^1.9.2", "core-js": "^3.29.1", + "lit": "^2.7.5", "lit-flatpickr": "^0.3.0", "shortcut-buttons-flatpickr": "^0.4.0", "sortablejs": "^1.14.0" @@ -75,4 +71,4 @@ "engines": { "node": ">=14.0.0" } -} \ No newline at end of file +} diff --git a/pixelegg/css/mobile.css b/pixelegg/css/mobile.css index a5757d6c8d..7e92110e0b 100644 --- a/pixelegg/css/mobile.css +++ b/pixelegg/css/mobile.css @@ -5606,13 +5606,12 @@ div.timesheet_timer { height: 0px; float: left; display: inline-block; - /* do NOT show empty label */ } -#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox sl-menu-item[value=""], -#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox sl-menu-item[value=""], -#egw_fw_topmenu_info_items #topmenu_info_quick_add #timer_selectbox sl-menu-item[value=""], -#egw_fw_topmenu_info_items #topmenu_info_timer #timer_selectbox sl-menu-item[value=""] { - display: none; +#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox sl-option, +#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox sl-option, +#egw_fw_topmenu_info_items #topmenu_info_quick_add #timer_selectbox sl-option, +#egw_fw_topmenu_info_items #topmenu_info_timer #timer_selectbox sl-option { + white-space: nowrap; } #egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(form-control-input), #egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(form-control-input), @@ -5626,12 +5625,20 @@ div.timesheet_timer { #egw_fw_topmenu_info_items #topmenu_info_timer #timer_selectbox::part(form-control) { margin-left: -3em; } -#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(menu), -#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(menu), +#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(listbox), +#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(listbox), #egw_fw_topmenu_info_items #topmenu_info_quick_add #timer_selectbox::part(menu), #egw_fw_topmenu_info_items #topmenu_info_timer #timer_selectbox::part(menu) { max-height: 60vh; } +#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(listbox), +#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(listbox) { + float: right; +} +#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(combobox), +#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(combobox) { + visibility: hidden; +} #egw_fw_topmenu_info_items img#topmenu_info_error { width: 16px; height: 16px; diff --git a/pixelegg/css/monochrome.css b/pixelegg/css/monochrome.css index fbd6cbd09f..d7cd72b205 100644 --- a/pixelegg/css/monochrome.css +++ b/pixelegg/css/monochrome.css @@ -5586,13 +5586,12 @@ div.timesheet_timer { height: 0px; float: left; display: inline-block; - /* do NOT show empty label */ } -#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox sl-menu-item[value=""], -#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox sl-menu-item[value=""], -#egw_fw_topmenu_info_items #topmenu_info_quick_add #timer_selectbox sl-menu-item[value=""], -#egw_fw_topmenu_info_items #topmenu_info_timer #timer_selectbox sl-menu-item[value=""] { - display: none; +#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox sl-option, +#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox sl-option, +#egw_fw_topmenu_info_items #topmenu_info_quick_add #timer_selectbox sl-option, +#egw_fw_topmenu_info_items #topmenu_info_timer #timer_selectbox sl-option { + white-space: nowrap; } #egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(form-control-input), #egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(form-control-input), @@ -5606,12 +5605,20 @@ div.timesheet_timer { #egw_fw_topmenu_info_items #topmenu_info_timer #timer_selectbox::part(form-control) { margin-left: -3em; } -#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(menu), -#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(menu), +#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(listbox), +#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(listbox), #egw_fw_topmenu_info_items #topmenu_info_quick_add #timer_selectbox::part(menu), #egw_fw_topmenu_info_items #topmenu_info_timer #timer_selectbox::part(menu) { max-height: 60vh; } +#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(listbox), +#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(listbox) { + float: right; +} +#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(combobox), +#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(combobox) { + visibility: hidden; +} #egw_fw_topmenu_info_items img#topmenu_info_error { width: 16px; height: 16px; diff --git a/pixelegg/css/pixelegg.css b/pixelegg/css/pixelegg.css index ef08882207..871b1f5df2 100644 --- a/pixelegg/css/pixelegg.css +++ b/pixelegg/css/pixelegg.css @@ -5596,13 +5596,12 @@ div.timesheet_timer { height: 0px; float: left; display: inline-block; - /* do NOT show empty label */ } -#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox sl-menu-item[value=""], -#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox sl-menu-item[value=""], -#egw_fw_topmenu_info_items #topmenu_info_quick_add #timer_selectbox sl-menu-item[value=""], -#egw_fw_topmenu_info_items #topmenu_info_timer #timer_selectbox sl-menu-item[value=""] { - display: none; +#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox sl-option, +#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox sl-option, +#egw_fw_topmenu_info_items #topmenu_info_quick_add #timer_selectbox sl-option, +#egw_fw_topmenu_info_items #topmenu_info_timer #timer_selectbox sl-option { + white-space: nowrap; } #egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(form-control-input), #egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(form-control-input), @@ -5616,12 +5615,20 @@ div.timesheet_timer { #egw_fw_topmenu_info_items #topmenu_info_timer #timer_selectbox::part(form-control) { margin-left: -3em; } -#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(menu), -#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(menu), +#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(listbox), +#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(listbox), #egw_fw_topmenu_info_items #topmenu_info_quick_add #timer_selectbox::part(menu), #egw_fw_topmenu_info_items #topmenu_info_timer #timer_selectbox::part(menu) { max-height: 60vh; } +#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(listbox), +#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(listbox) { + float: right; +} +#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(combobox), +#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(combobox) { + visibility: hidden; +} #egw_fw_topmenu_info_items img#topmenu_info_error { width: 16px; height: 16px; diff --git a/pixelegg/js/slider.js b/pixelegg/js/slider.js index d477cab7ba..b2eb3dcecc 100644 --- a/pixelegg/js/slider.js +++ b/pixelegg/js/slider.js @@ -19,13 +19,13 @@ egw_ready.then(function() // do NOT react on bubbeling events from contained selectbox var select = document.getElementById('quick_add_selectbox'); ev.stopImmediatePropagation(); - if (select.dropdown.open) + if (select.open) { - select.dropdown.hide(); + select.hide(); } else { - select.dropdown.show(); + select.show(); } } diff --git a/pixelegg/less/layout_raster_buttons.less b/pixelegg/less/layout_raster_buttons.less index fce28b85b0..eace485d0b 100644 --- a/pixelegg/less/layout_raster_buttons.less +++ b/pixelegg/less/layout_raster_buttons.less @@ -288,25 +288,36 @@ div.timesheet_timer { // quick_add and timer selectbox / menu #topmenu_info_quick_add, #topmenu_info_timer { - #quick_add_selectbox, #timer_selectbox { - height: 0px; - float: left; - display: inline-block; - /* do NOT show empty label */ - sl-menu-item[value=""] { - display: none; - } + #quick_add_selectbox, #timer_selectbox { + height: 0px; + float: left; + display: inline-block; + + sl-option { + white-space: nowrap; } - #quick_add_selectbox::part(form-control-input), #timer_selectbox::part(form-control-input) { - border: none !important; - } - #quick_add_selectbox::part(form-control), #timer_selectbox::part(form-control) { - margin-left: -3em; - } - #quick_add_selectbox::part(menu), #timer_selectbox::part(menu) { - max-height: 60vh; - } - } + } + + #quick_add_selectbox::part(form-control-input), #timer_selectbox::part(form-control-input) { + border: none !important; + } + + #quick_add_selectbox::part(form-control), #timer_selectbox::part(form-control) { + margin-left: -3em; + } + + #quick_add_selectbox::part(listbox), #timer_selectbox::part(menu) { + max-height: 60vh; + } + + #quick_add_selectbox::part(listbox) { + float: right; + } + + #quick_add_selectbox::part(combobox) { + visibility: hidden; + } + } // ############################################################################## // JS ERROR BUTTON diff --git a/pixelegg/mobile/fw_mobile.css b/pixelegg/mobile/fw_mobile.css index af3fb51dbb..9fb573261e 100644 --- a/pixelegg/mobile/fw_mobile.css +++ b/pixelegg/mobile/fw_mobile.css @@ -5617,13 +5617,12 @@ div.timesheet_timer { height: 0px; float: left; display: inline-block; - /* do NOT show empty label */ } -#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox sl-menu-item[value=""], -#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox sl-menu-item[value=""], -#egw_fw_topmenu_info_items #topmenu_info_quick_add #timer_selectbox sl-menu-item[value=""], -#egw_fw_topmenu_info_items #topmenu_info_timer #timer_selectbox sl-menu-item[value=""] { - display: none; +#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox sl-option, +#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox sl-option, +#egw_fw_topmenu_info_items #topmenu_info_quick_add #timer_selectbox sl-option, +#egw_fw_topmenu_info_items #topmenu_info_timer #timer_selectbox sl-option { + white-space: nowrap; } #egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(form-control-input), #egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(form-control-input), @@ -5637,12 +5636,20 @@ div.timesheet_timer { #egw_fw_topmenu_info_items #topmenu_info_timer #timer_selectbox::part(form-control) { margin-left: -3em; } -#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(menu), -#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(menu), +#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(listbox), +#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(listbox), #egw_fw_topmenu_info_items #topmenu_info_quick_add #timer_selectbox::part(menu), #egw_fw_topmenu_info_items #topmenu_info_timer #timer_selectbox::part(menu) { max-height: 60vh; } +#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(listbox), +#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(listbox) { + float: right; +} +#egw_fw_topmenu_info_items #topmenu_info_quick_add #quick_add_selectbox::part(combobox), +#egw_fw_topmenu_info_items #topmenu_info_timer #quick_add_selectbox::part(combobox) { + visibility: hidden; +} #egw_fw_topmenu_info_items img#topmenu_info_error { width: 16px; height: 16px; diff --git a/rollup.config.js b/rollup.config.js index 1af1c00cb0..57fbedc672 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -135,6 +135,10 @@ const config = { // plugins: stage3Syntax, errorRecovery: true }, + plugins: [ + ['@babel/plugin-proposal-decorators', {legacy: true}], + ['@babel/plugin-proposal-class-properties', {loose: false}] + ], presets: [ ['@babel/preset-typescript', { //onlyRemoveTypeImports: true // seems not necessary and generates a lot of warnings about not exported symbols