diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multiple-text-string/input-multiple-text-string-item/input-multiple-text-string-item.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multiple-text-string/input-multiple-text-string-item/input-multiple-text-string-item.element.ts new file mode 100644 index 0000000000..840cf73484 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multiple-text-string/input-multiple-text-string-item/input-multiple-text-string-item.element.ts @@ -0,0 +1,143 @@ +import { css, html, nothing } from 'lit'; +import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; +import { customElement, property, query } from 'lit/decorators.js'; +import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins'; +import { UUIInputEvent } from '@umbraco-ui/uui-input'; +import { UUIInputElement } from '@umbraco-ui/uui'; +import { UmbLitElement } from '@umbraco-cms/element'; +import { UmbModalService, UMB_MODAL_SERVICE_CONTEXT_TOKEN } from 'src/core/modal'; +import { UmbChangeEvent } from 'src/core/events/change.event'; +import { UmbDeleteEvent } from 'src/core/events/delete.event'; +import { UmbInputEvent } from 'src/core/events/input.event'; +import { elementUpdated } from '@open-wc/testing'; + +/** + * @element umb-input-multiple-text-string-item + */ +@customElement('umb-input-multiple-text-string-item') +export class UmbInputMultipleTextStringItemElement extends FormControlMixin(UmbLitElement) { + static styles = [ + UUITextStyles, + css` + :host { + display: flex; + align-items: center; + margin-bottom: var(--uui-size-space-3); + gap: var(--uui-size-space-3); + } + + #input { + flex: 1; + } + `, + ]; + + /** + * Disables the input + * @type {boolean} + * @attr + * @default false + */ + @property({ type: Boolean, reflect: true }) + disabled = false; + + /** + * Disables the input + * @type {boolean} + * @attr + * @default false + */ + @property({ type: Boolean, reflect: true }) + readonly = false; + + /** + * Makes the input required + * @type {boolean} + * @attr + * @default false + */ + @property({ type: Boolean, reflect: true }) + required = false; + + @query('#input') + protected _input?: UUIInputElement; + + private _modalService?: UmbModalService; + + constructor() { + super(); + + this.consumeContext(UMB_MODAL_SERVICE_CONTEXT_TOKEN, (modalService) => { + this._modalService = modalService; + }); + } + + #onDelete() { + const modalHandler = this._modalService?.confirm({ + headline: `Delete ${this._value || 'item'}`, + content: 'Are you sure you want to delete this item?', + color: 'danger', + confirmLabel: 'Delete', + }); + + modalHandler?.onClose().then(({ confirmed }: any) => { + if (confirmed) this.dispatchEvent(new UmbDeleteEvent()); + }); + } + + #onInput(event: UUIInputEvent) { + event.stopPropagation(); + const target = event.currentTarget as UUIInputElement; + this._value = target.value as string; + this.dispatchEvent(new UmbInputEvent()); + } + + #onChange(event: UUIInputEvent) { + event.stopPropagation(); + const target = event.currentTarget as UUIInputElement; + this._value = target.value as string; + this.dispatchEvent(new UmbChangeEvent()); + } + + public async focus() { + await this.updateComplete; + this._input?.focus(); + } + + protected getFormElement() { + return undefined; + } + + render() { + return html` + ${this.disabled || this.readonly ? nothing : html``} + + + ${this.readonly + ? nothing + : html` + + `} + `; + } +} + +export default UmbInputMultipleTextStringItemElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-input-multiple-text-string-item': UmbInputMultipleTextStringItemElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multiple-text-string/input-multiple-text-string/input-multiple-text-string.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multiple-text-string/input-multiple-text-string/input-multiple-text-string.element.ts index 9c4317f9c5..1367b61847 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multiple-text-string/input-multiple-text-string/input-multiple-text-string.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multiple-text-string/input-multiple-text-string/input-multiple-text-string.element.ts @@ -3,10 +3,14 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, property, state } from 'lit/decorators.js'; import { repeat } from 'lit/directives/repeat.js'; import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins'; -import { UUIInputElement, UUIInputEvent } from '@umbraco-ui/uui-input'; +import UmbInputMultipleTextStringItemElement from '../input-multiple-text-string-item/input-multiple-text-string-item.element'; import { UmbLitElement } from '@umbraco-cms/element'; import { UmbModalService, UMB_MODAL_SERVICE_CONTEXT_TOKEN } from 'src/core/modal'; import { UmbChangeEvent } from 'src/core/events/change.event'; +import { UmbDeleteEvent } from 'src/core/events/delete.event'; + +import '../input-multiple-text-string-item/input-multiple-text-string-item.element'; +import { UmbInputEvent } from 'src/core/events/input.event'; export type MultipleTextStringValue = Array; @@ -22,17 +26,6 @@ export class UmbInputMultipleTextStringElement extends FormControlMixin(UmbLitEl static styles = [ UUITextStyles, css` - .item { - display: flex; - align-items: center; - margin-bottom: var(--uui-size-space-3); - gap: var(--uui-size-space-3); - } - - .item #text-field { - flex: 1; - } - #action { display: block; } @@ -93,8 +86,6 @@ export class UmbInputMultipleTextStringElement extends FormControlMixin(UmbLitEl @property({ type: Boolean, reflect: true }) readonly = false; - private _modalService?: UmbModalService; - constructor() { super(); @@ -108,10 +99,6 @@ export class UmbInputMultipleTextStringElement extends FormControlMixin(UmbLitEl () => this.maxMessage, () => !!this.max && this._items.length > this.max ); - - this.consumeContext(UMB_MODAL_SERVICE_CONTEXT_TOKEN, (modalService) => { - this._modalService = modalService; - }); } @state() @@ -137,23 +124,9 @@ export class UmbInputMultipleTextStringElement extends FormControlMixin(UmbLitEl this.#focusNewItem(); } - #onDelete(index: number) { - const item = this._items[index]; - - const modalHandler = this._modalService?.confirm({ - headline: `Delete ${item.value || 'item'}`, - content: 'Are you sure you want to delete this item?', - color: 'danger', - confirmLabel: 'Delete', - }); - - modalHandler?.onClose().then(({ confirmed }: any) => { - if (confirmed) this.#deleteItem(index); - }); - } - - #onInput(event: UUIInputEvent, currentIndex: number) { - const target = event.currentTarget as UUIInputElement; + #onInput(event: UmbInputEvent, currentIndex: number) { + event.stopPropagation(); + const target = event.currentTarget as UmbInputMultipleTextStringItemElement; const value = target.value as string; this._items = this._items.map((item, index) => (index === currentIndex ? { value } : item)); this.dispatchEvent(new UmbChangeEvent()); @@ -161,12 +134,15 @@ export class UmbInputMultipleTextStringElement extends FormControlMixin(UmbLitEl async #focusNewItem() { await this.updateComplete; - const inputs = this.shadowRoot?.querySelectorAll('uui-input') as NodeListOf; + const inputs = this.shadowRoot?.querySelectorAll( + 'umb-input-multiple-text-string-item' + ) as NodeListOf; const lastInput = inputs[inputs.length - 1]; lastInput.focus(); } - #deleteItem(itemIndex: number) { + #deleteItem(event: UmbDeleteEvent, itemIndex: number) { + event.stopPropagation(); this._items = this._items.filter((item, index) => index !== itemIndex); this.dispatchEvent(new UmbChangeEvent()); } @@ -195,34 +171,16 @@ export class UmbInputMultipleTextStringElement extends FormControlMixin(UmbLitEl ${repeat( this._items, (item, index) => index, - (item, index) => html`${this._renderItem(item, index)}` + (item, index) => + html` this.#onInput(event, index)} + @delete="${(event: UmbDeleteEvent) => this.#deleteItem(event, index)}" + ?disabled=${this.disabled} + ?readonly=${this.readonly}>` )} `; } - - private _renderItem(item: MultipleTextStringValueItem, index: number) { - return html`
- ${this.disabled || this.readonly ? nothing : html``} - - - ${this.readonly - ? nothing - : html` - - `} -
`; - } } export default UmbInputMultipleTextStringElement; diff --git a/src/Umbraco.Web.UI.Client/src/core/events/delete.event.ts b/src/Umbraco.Web.UI.Client/src/core/events/delete.event.ts new file mode 100644 index 0000000000..d690607e0a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/events/delete.event.ts @@ -0,0 +1,5 @@ +export class UmbDeleteEvent extends Event { + public constructor() { + super('delete', { bubbles: true, composed: false, cancelable: false }); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/core/events/input.event.ts b/src/Umbraco.Web.UI.Client/src/core/events/input.event.ts new file mode 100644 index 0000000000..d7f22fe548 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/events/input.event.ts @@ -0,0 +1,6 @@ +export class UmbInputEvent extends Event { + public constructor() { + // mimics the native input event + super('input', { bubbles: true, composed: true, cancelable: false }); + } +}