diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-entity/input-entity.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-entity/input-entity.element.ts index 3e2f05b7be..6800377a71 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-entity/input-entity.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-entity/input-entity.element.ts @@ -1,14 +1,30 @@ -import { css, html, customElement, property, state, repeat, when } from '@umbraco-cms/backoffice/external/lit'; +import { css, html, customElement, property, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; import { splitStringToArray } from '@umbraco-cms/backoffice/utils'; -import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; +import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbPickerInputContext } from '@umbraco-cms/backoffice/picker-input'; import type { UmbUniqueItemModel } from '@umbraco-cms/backoffice/models'; @customElement('umb-input-entity') export class UmbInputEntityElement extends UUIFormControlMixin(UmbLitElement, '') { - // TODO: [LK] Add sort ordering. + #sorter = new UmbSorterController(this, { + getUniqueOfElement: (element) => { + return element.id; + }, + getUniqueOfModel: (modelEntry) => { + return modelEntry; + }, + identifier: 'Umb.SorterIdentifier.InputEntity', + itemSelector: 'uui-ref-node', + containerSelector: 'uui-ref-list', + onChange: ({ model }) => { + this.selection = model; + this.dispatchEvent(new UmbChangeEvent()); + }, + }); protected getFormElement() { return undefined; @@ -41,14 +57,23 @@ export class UmbInputEntityElement extends UUIFormControlMixin(UmbLitElement, '' #max: number = Infinity; @property({ attribute: false }) - getIcon?: (item: any) => string; + getIcon?: (item: UmbUniqueItemModel) => string; @property({ type: String, attribute: 'min-message' }) maxMessage = 'This field exceeds the allowed amount of items'; + @property({ type: Array }) + public set selection(uniques: Array) { + this.#pickerContext?.setSelection(uniques); + this.#sorter.setModel(uniques); + } + public get selection(): Array | undefined { + return this.#pickerContext?.getSelection(); + } + @property() - public set value(value: string) { - this.selection = splitStringToArray(value); + public set value(uniques: string) { + this.selection = splitStringToArray(uniques); } public get value(): string { return this.selection?.join(',') ?? ''; @@ -60,18 +85,12 @@ export class UmbInputEntityElement extends UUIFormControlMixin(UmbLitElement, '' this.#pickerContext = new ctor(this); this.#observePickerContext(); } - #pickerContext?: UmbPickerInputContext; - - public set selection(value: Array) { - this.#pickerContext?.setSelection(value); - } - public get selection(): Array | undefined { - return this.#pickerContext?.getSelection(); - } @state() private _items?: Array; + #pickerContext?: UmbPickerInputContext; + constructor() { super(); } @@ -100,19 +119,8 @@ export class UmbInputEntityElement extends UUIFormControlMixin(UmbLitElement, '' this.#pickerContext.min = this.min; this.#pickerContext.max = this.max; - this.observe( - this.#pickerContext.selection, - (selection) => (this.value = selection?.join(',') ?? ''), - 'observeSelection', - ); - - this.observe( - this.#pickerContext.selectedItems, - (selectedItems) => { - this._items = selectedItems; - }, - 'observeSelectedItems', - ); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(',')), '_observeSelection'); + this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems), '_observerItems'); } #openPicker() { @@ -121,6 +129,10 @@ export class UmbInputEntityElement extends UUIFormControlMixin(UmbLitElement, '' }); } + #removeItem(item: UmbUniqueItemModel) { + this.#pickerContext?.requestRemoveItem(item.unique); + } + render() { return html`${this.#renderItems()} ${this.#renderAddButton()}`; } @@ -153,12 +165,10 @@ export class UmbInputEntityElement extends UUIFormControlMixin(UmbLitElement, '' if (!item.unique) return; const icon = this.getIcon?.(item) ?? item.icon ?? ''; return html` - + ${when(icon, () => html``)} - this.#pickerContext?.requestRemoveItem(item.unique)} - label=${this.localize.term('general_remove')}> + this.#removeItem(item)} label=${this.localize.term('general_remove')}> `; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-input/data-type-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-input/data-type-input.element.ts index 504d13de9c..78c572f3b9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-input/data-type-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-input/data-type-input.element.ts @@ -1,12 +1,31 @@ import type { UmbDataTypeItemModel } from '../../repository/item/types.js'; import { UmbDataTypePickerContext } from './data-type-input.context.js'; -import { css, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; -import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { css, html, customElement, nothing, property, repeat, state } from '@umbraco-cms/backoffice/external/lit'; import { splitStringToArray } from '@umbraco-cms/backoffice/utils'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; +import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; +// TODO: Rename to 'umb-input-data-type'. [LK] @customElement('umb-data-type-input') export class UmbDataTypeInputElement extends UUIFormControlMixin(UmbLitElement, '') { + #sorter = new UmbSorterController(this, { + getUniqueOfElement: (element) => { + return element.id; + }, + getUniqueOfModel: (modelEntry) => { + return modelEntry; + }, + identifier: 'Umb.SorterIdentifier.InputDataType', + itemSelector: 'uui-ref-node-data-type', + containerSelector: 'uui-ref-list', + onChange: ({ model }) => { + this.selection = model; + this.dispatchEvent(new UmbChangeEvent()); + }, + }); + /** * This is a minimum amount of selected items in this input. * @type {number} @@ -54,20 +73,20 @@ export class UmbDataTypeInputElement extends UUIFormControlMixin(UmbLitElement, maxMessage = 'This field exceeds the allowed amount of items'; @property({ type: Array }) - public set selection(ids: Array | undefined) { - this.#pickerContext.setSelection(ids ?? []); + public set selection(uniques: Array) { + this.#pickerContext.setSelection(uniques ?? []); + this.#sorter.setModel(uniques); } - public get selection(): Array | undefined { + public get selection(): Array { return this.#pickerContext.getSelection(); } @property() - public set value(idsString: string) { - // Its with full purpose we don't call super.value, as thats being handled by the observation of the context selection. - this.selection = splitStringToArray(idsString); + public set value(uniques: string) { + this.selection = splitStringToArray(uniques); } public get value(): string { - return this.selection?.join(',') ?? ''; + return this.selection.join(','); } @state() @@ -90,33 +109,58 @@ export class UmbDataTypeInputElement extends UUIFormControlMixin(UmbLitElement, () => !!this.max && this.#pickerContext.getSelection().length > this.max, ); - this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(','))); - this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems)); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(',')), '_observeSelection'); + this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems), '_observerItems'); } protected getFormElement() { return undefined; } + #openPicker() { + this.#pickerContext.openPicker({ + hideTreeRoot: true, + }); + } + + #removeItem(item: UmbDataTypeItemModel) { + this.#pickerContext.requestRemoveItem(item.unique); + } + render() { + return html`${this.#renderItems()} ${this.#renderAddButton()}`; + } + + #renderAddButton() { + if (this.max > 0 && this.selection.length >= this.max) return nothing; return html` - ${this._items?.map((item) => this._renderItem(item))} this.#pickerContext.openPicker({ hideTreeRoot: true })} - label=${this.localize.term('general_choose')}> + @click=${this.#openPicker} + label="${this.localize.term('general_choose')}"> `; } - private _renderItem(item: UmbDataTypeItemModel) { + #renderItems() { + if (!this._items) return nothing; + return html` + + ${repeat( + this._items, + (item) => item.unique, + (item) => this.#renderItem(item), + )} + + `; + } + + #renderItem(item: UmbDataTypeItemModel) { if (!item.unique) return; return html` - + - this.#pickerContext.requestRemoveItem(item.unique)} - label=${this.localize.term('general_remove')}> + this.#removeItem(item)} label=${this.localize.term('general_remove')}> `; @@ -124,7 +168,7 @@ export class UmbDataTypeInputElement extends UUIFormControlMixin(UmbLitElement, static styles = [ css` - #add-button { + #btn-add { width: 100%; } `, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/input-document-type.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/input-document-type.element.ts index 86fa8b08ed..0e0833545a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/input-document-type.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/input-document-type.element.ts @@ -1,22 +1,31 @@ import type { UmbDocumentTypeItemModel } from '../../repository/index.js'; import { UmbDocumentTypePickerContext } from './input-document-type.context.js'; -import { - css, - html, - customElement, - property, - state, - ifDefined, - repeat, - nothing, -} from '@umbraco-cms/backoffice/external/lit'; -import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { css, html, customElement, property, state, repeat, nothing } from '@umbraco-cms/backoffice/external/lit'; import { splitStringToArray } from '@umbraco-cms/backoffice/utils'; -import { UMB_WORKSPACE_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbModalRouteRegistrationController, UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/modal'; +import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; +import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; @customElement('umb-input-document-type') export class UmbInputDocumentTypeElement extends UUIFormControlMixin(UmbLitElement, '') { + #sorter = new UmbSorterController(this, { + getUniqueOfElement: (element) => { + return element.id; + }, + getUniqueOfModel: (modelEntry) => { + return modelEntry; + }, + identifier: 'Umb.SorterIdentifier.InputDocumentType', + itemSelector: 'uui-ref-node-document-type', + containerSelector: 'uui-ref-list', + onChange: ({ model }) => { + this.selection = model; + this.dispatchEvent(new UmbChangeEvent()); + }, + }); + /** * Limits to only select Element Types * @type {boolean} @@ -73,17 +82,17 @@ export class UmbInputDocumentTypeElement extends UUIFormControlMixin(UmbLitEleme maxMessage = 'This field exceeds the allowed amount of items'; @property({ type: Array }) - public set selection(ids: Array | undefined) { - this.#pickerContext.setSelection(ids ?? []); + public set selection(uniques: Array) { + this.#pickerContext.setSelection(uniques); + this.#sorter.setModel(uniques); } public get selection(): Array { return this.#pickerContext.getSelection(); } @property() - public set value(idsString: string) { - // Its with full purpose we don't call super.value, as thats being handled by the observation of the context selection. - this.selection = splitStringToArray(idsString); + public set value(uniques: string) { + this.selection = splitStringToArray(uniques); } public get value(): string { return this.selection.join(','); @@ -93,7 +102,7 @@ export class UmbInputDocumentTypeElement extends UUIFormControlMixin(UmbLitEleme private _items?: Array; @state() - private _editDocumentTypePath = ''; + private _editPath = ''; #pickerContext = new UmbDocumentTypePickerContext(this); @@ -106,7 +115,7 @@ export class UmbInputDocumentTypeElement extends UUIFormControlMixin(UmbLitEleme return { data: { entityType: 'document-type', preset: {} } }; }) .observeRouteBuilder((routeBuilder) => { - this._editDocumentTypePath = routeBuilder({}); + this._editPath = routeBuilder({}); }); this.addValidator( @@ -121,8 +130,8 @@ export class UmbInputDocumentTypeElement extends UUIFormControlMixin(UmbLitEleme () => !!this.max && this.#pickerContext.getSelection().length > this.max, ); - this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(','))); - this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems)); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(',')), '_observeSelection'); + this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems), '_observerItems'); } protected getFormElement() { @@ -130,64 +139,53 @@ export class UmbInputDocumentTypeElement extends UUIFormControlMixin(UmbLitEleme } #openPicker() { - if (this.elementTypesOnly) { - this.#pickerContext.openPicker({ - hideTreeRoot: true, - pickableFilter: (x) => x.isElement, - }); - } else { - this.#pickerContext.openPicker({ - hideTreeRoot: true, - }); - } + this.#pickerContext.openPicker({ + hideTreeRoot: true, + pickableFilter: this.elementTypesOnly ? (x) => x.isElement : undefined, + }); + } + + #removeItem(item: UmbDocumentTypeItemModel) { + this.#pickerContext.requestRemoveItem(item.unique); } render() { - return html` ${this.#renderItems()} ${this.#renderAddButton()} `; - } - - #renderItems() { - if (!this._items) return nothing; - return html` - ${repeat( - this._items, - (item) => item.unique, - (item) => this.#renderItem(item), - )} - `; + return html`${this.#renderItems()} ${this.#renderAddButton()}`; } #renderAddButton() { if (this.max > 0 && this.selection.length >= this.max) return nothing; return html` `; } + #renderItems() { + if (!this._items) return nothing; + return html` + + ${repeat( + this._items, + (item) => item.unique, + (item) => this.#renderItem(item), + )} + + `; + } + #renderItem(item: UmbDocumentTypeItemModel) { if (!item.unique) return; + const href = `${this._editPath}edit/${item.unique}`; return html` - + ${this.#renderIcon(item)} - - - - this.#pickerContext.requestRemoveItem(item.unique)} - label="Edit Document Type ${item.name}"> - - + + this.#removeItem(item)} label=${this.localize.term('general_remove')}> `; @@ -200,7 +198,7 @@ export class UmbInputDocumentTypeElement extends UUIFormControlMixin(UmbLitEleme static styles = [ css` - #add-button { + #btn-add { width: 100%; } `, diff --git a/src/Umbraco.Web.UI.Client/src/packages/language/components/input-language/input-language.element.ts b/src/Umbraco.Web.UI.Client/src/packages/language/components/input-language/input-language.element.ts index 69d0f4a540..28b8baac25 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/language/components/input-language/input-language.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/language/components/input-language/input-language.element.ts @@ -1,12 +1,30 @@ import type { UmbLanguageItemModel } from '../../repository/index.js'; import { UmbLanguagePickerContext } from './input-language.context.js'; -import { css, html, ifDefined, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; -import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { css, html, customElement, property, state, repeat, nothing } from '@umbraco-cms/backoffice/external/lit'; import { splitStringToArray } from '@umbraco-cms/backoffice/utils'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; +import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; @customElement('umb-input-language') export class UmbInputLanguageElement extends UUIFormControlMixin(UmbLitElement, '') { + #sorter = new UmbSorterController(this, { + getUniqueOfElement: (element) => { + return element.id; + }, + getUniqueOfModel: (modelEntry) => { + return modelEntry; + }, + identifier: 'Umb.SorterIdentifier.InputLanguage', + itemSelector: 'uui-ref-node', + containerSelector: 'uui-ref-list', + onChange: ({ model }) => { + this.selection = model; + this.dispatchEvent(new UmbChangeEvent()); + }, + }); + /** * This is a minimum amount of selected items in this input. * @type {number} @@ -56,8 +74,10 @@ export class UmbInputLanguageElement extends UUIFormControlMixin(UmbLitElement, @property({ type: Object, attribute: false }) public filter: (language: UmbLanguageItemModel) => boolean = () => true; + @property({ type: Array }) public set selection(uniques: Array) { this.#pickerContext.setSelection(uniques); + this.#sorter.setModel(uniques); } public get selection(): Array { return this.#pickerContext.getSelection(); @@ -65,7 +85,6 @@ export class UmbInputLanguageElement extends UUIFormControlMixin(UmbLitElement, @property() public set value(uniques: string) { - // Its with full purpose we don't call super.value, as thats being handled by the observation of the context selection. this.selection = splitStringToArray(uniques); } public get value(): string { @@ -92,8 +111,8 @@ export class UmbInputLanguageElement extends UUIFormControlMixin(UmbLitElement, () => !!this.max && this.#pickerContext.getSelection().length > this.max, ); - this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(','))); - this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems)); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(',')), '_observeSelection'); + this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems), '_observerItems'); } protected getFormElement() { @@ -106,14 +125,35 @@ export class UmbInputLanguageElement extends UUIFormControlMixin(UmbLitElement, }); } + #removeItem(item: UmbLanguageItemModel) { + this.#pickerContext.requestRemoveItem(item.unique); + } + render() { + return html`${this.#renderItems()} ${this.#renderAddButton()}`; + } + + #renderAddButton() { + if (this.max > 0 && this.selection.length >= this.max) return nothing; return html` - ${this._items.map((item) => this.#renderItem(item))} + label="${this.localize.term('general_choose')}"> + `; + } + + #renderItems() { + if (!this._items) return nothing; + return html` + + ${repeat( + this._items, + (item) => item.unique, + (item) => this.#renderItem(item), + )} + `; } @@ -121,11 +161,9 @@ export class UmbInputLanguageElement extends UUIFormControlMixin(UmbLitElement, if (!item.unique) return; return html` - + - this.#pickerContext.requestRemoveItem(item.unique)} - label=${this.localize.term('general_remove')}> + this.#removeItem(item)} label=${this.localize.term('general_remove')}> `; @@ -133,7 +171,7 @@ export class UmbInputLanguageElement extends UUIFormControlMixin(UmbLitElement, static styles = [ css` - #add-button { + #btn-add { width: 100%; } `, diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/components/input-media-type/input-media-type.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/components/input-media-type/input-media-type.element.ts index 6201d1da8d..8c9aab8d6b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/components/input-media-type/input-media-type.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/components/input-media-type/input-media-type.element.ts @@ -1,12 +1,31 @@ import type { UmbMediaTypeItemModel } from '../../repository/index.js'; import { UmbMediaTypePickerContext } from './input-media-type.context.js'; -import { css, html, customElement, property, state, ifDefined, repeat } from '@umbraco-cms/backoffice/external/lit'; -import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { css, html, customElement, property, state, repeat, nothing } from '@umbraco-cms/backoffice/external/lit'; import { splitStringToArray } from '@umbraco-cms/backoffice/utils'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbModalRouteRegistrationController, UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/modal'; +import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; +import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; @customElement('umb-input-media-type') export class UmbInputMediaTypeElement extends UUIFormControlMixin(UmbLitElement, '') { + #sorter = new UmbSorterController(this, { + getUniqueOfElement: (element) => { + return element.id; + }, + getUniqueOfModel: (modelEntry) => { + return modelEntry; + }, + identifier: 'Umb.SorterIdentifier.InputMediaType', + itemSelector: 'uui-ref-node-document-type', + containerSelector: 'uui-ref-list', + onChange: ({ model }) => { + this.selection = model; + this.dispatchEvent(new UmbChangeEvent()); + }, + }); + /** * This is a minimum amount of selected items in this input. * @type {number} @@ -53,30 +72,43 @@ export class UmbInputMediaTypeElement extends UUIFormControlMixin(UmbLitElement, @property({ type: String, attribute: 'min-message' }) maxMessage = 'This field exceeds the allowed amount of items'; - public set selection(ids: Array) { - this.#pickerContext.setSelection(ids); + @property({ type: Array }) + public set selection(uniques: Array) { + this.#pickerContext.setSelection(uniques); + this.#sorter.setModel(uniques); } public get selection(): Array { return this.#pickerContext.getSelection(); } @property() - public set value(idsString: string) { - // Its with full purpose we don't call super.value, as thats being handled by the observation of the context selection. - this.selection = splitStringToArray(idsString); + public set value(uniques: string) { + this.selection = splitStringToArray(uniques); } - public get value() { + public get value(): string { return this.selection.join(','); } @state() private _items?: Array; + @state() + private _editPath = ''; + #pickerContext = new UmbMediaTypePickerContext(this); constructor() { super(); + new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) + .addAdditionalPath('media-type') + .onSetup(() => { + return { data: { entityType: 'media-type', preset: {} } }; + }) + .observeRouteBuilder((routeBuilder) => { + this._editPath = routeBuilder({}); + }); + this.addValidator( 'rangeUnderflow', () => this.minMessage, @@ -89,8 +121,8 @@ export class UmbInputMediaTypeElement extends UUIFormControlMixin(UmbLitElement, () => !!this.max && this.#pickerContext.getSelection().length > this.max, ); - this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(','))); - this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems)); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(',')), '_observeSelection'); + this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems), '_observerItems'); } protected getFormElement() { @@ -103,12 +135,27 @@ export class UmbInputMediaTypeElement extends UUIFormControlMixin(UmbLitElement, }); } + #removeItem(item: UmbMediaTypeItemModel) { + this.#pickerContext.requestRemoveItem(item.unique); + } + render() { - return html` ${this.#renderItems()} ${this.#renderAddButton()} `; + return html`${this.#renderItems()} ${this.#renderAddButton()}`; + } + + #renderAddButton() { + if (this.max > 0 && this.selection.length >= this.max) return nothing; + return html` + + `; } #renderItems() { - if (!this._items) return; + if (!this._items) return nothing; return html` ${repeat( @@ -120,26 +167,15 @@ export class UmbInputMediaTypeElement extends UUIFormControlMixin(UmbLitElement, `; } - #renderAddButton() { - if (this.max === 1 && this.selection.length >= this.max) return; - return html` - - `; - } - #renderItem(item: UmbMediaTypeItemModel) { if (!item.unique) return; + const href = `${this._editPath}edit/${item.unique}`; return html` - + ${this.#renderIcon(item)} - this.#pickerContext.requestRemoveItem(item.unique)} - label="${this.localize.term('general_remove')} ${item.name}"> + + this.#removeItem(item)} label=${this.localize.term('general_remove')}> `; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/components/stylesheet-rule-input/stylesheet-rule-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/components/stylesheet-rule-input/stylesheet-rule-input.element.ts index 0aebf5c162..3f04921e11 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/components/stylesheet-rule-input/stylesheet-rule-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/components/stylesheet-rule-input/stylesheet-rule-input.element.ts @@ -1,15 +1,26 @@ import type { UmbStylesheetRule } from '../../types.js'; import { UMB_STYLESHEET_RULE_SETTINGS_MODAL } from './stylesheet-rule-settings-modal.token.js'; -import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { css, html, customElement, repeat, property } from '@umbraco-cms/backoffice/external/lit'; -import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; - -// TODO: add sorting when we have a generic sorting component/functionality for ref lists +import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; +import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; @customElement('umb-stylesheet-rule-input') export class UmbStylesheetRuleInputElement extends UUIFormControlMixin(UmbLitElement, '') { + #sorter = new UmbSorterController(this, { + getUniqueOfElement: (element) => element.id, + getUniqueOfModel: (modelEntry) => modelEntry.name, + identifier: 'Umb.SorterIdentifier.InputStylesheetRule', + itemSelector: 'umb-stylesheet-rule-ref', + containerSelector: 'uui-ref-list', + onChange: ({ model }) => { + this.rules = model; + this.dispatchEvent(new UmbChangeEvent()); + }, + }); + @property({ type: Array, attribute: false }) rules: UmbStylesheetRule[] = []; @@ -57,6 +68,10 @@ export class UmbStylesheetRuleInputElement extends UUIFormControlMixin(UmbLitEle this.dispatchEvent(new UmbChangeEvent()); }; + firstUpdated() { + this.#sorter.setModel(this.rules); + } + render() { return html` @@ -64,16 +79,20 @@ export class UmbStylesheetRuleInputElement extends UUIFormControlMixin(UmbLitEle this.rules, (rule, index) => rule.name + index, (rule, index) => html` - + - this.#editRule(rule, index)} label="Edit ${rule.name}">Edit - this.#removeRule(rule)} label="Remove ${rule.name}">Remove + this.#editRule(rule, index)}> + this.#removeRule(rule)}> `, )} - Add + `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-input/user-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-input/user-input.element.ts index f7918ae903..d8334de70d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-input/user-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-input/user-input.element.ts @@ -1,13 +1,30 @@ import type { UmbUserItemModel } from '../../repository/index.js'; import { UmbUserPickerContext } from './user-input.context.js'; -import { css, html, customElement, property, state, nothing } from '@umbraco-cms/backoffice/external/lit'; -import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { css, customElement, html, nothing, property, repeat, state } from '@umbraco-cms/backoffice/external/lit'; import { splitStringToArray } from '@umbraco-cms/backoffice/utils'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; +import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; +// TODO: Shall we rename to 'umb-input-user'? [LK] @customElement('umb-user-input') export class UmbUserInputElement extends UUIFormControlMixin(UmbLitElement, '') { - // TODO: [LK] Add sorting! + #sorter = new UmbSorterController(this, { + getUniqueOfElement: (element) => { + return element.id; + }, + getUniqueOfModel: (modelEntry) => { + return modelEntry; + }, + identifier: 'Umb.SorterIdentifier.InputUser', + itemSelector: 'uui-ref-node-user', + containerSelector: 'uui-ref-list', + onChange: ({ model }) => { + this.selection = model; + this.dispatchEvent(new UmbChangeEvent()); + }, + }); /** * This is a minimum amount of selected items in this input. @@ -55,17 +72,18 @@ export class UmbUserInputElement extends UUIFormControlMixin(UmbLitElement, '') @property({ type: String, attribute: 'min-message' }) maxMessage = 'This field exceeds the allowed amount of items'; + @property({ type: Array }) + public set selection(uniques: Array) { + this.#pickerContext.setSelection(uniques); + this.#sorter.setModel(uniques); + } public get selection(): Array { return this.#pickerContext.getSelection(); } - public set selection(ids: Array) { - this.#pickerContext.setSelection(ids); - } @property() - public set value(idsString: string) { - // Its with full purpose we don't call super.value, as thats being handled by the observation of the context selection. - this.selection = splitStringToArray(idsString); + public set value(uniques: string) { + this.selection = splitStringToArray(uniques); } public get value(): string { return this.selection.join(','); @@ -91,16 +109,8 @@ export class UmbUserInputElement extends UUIFormControlMixin(UmbLitElement, '') () => !!this.max && this.#pickerContext.getSelection().length > this.max, ); - this.observe( - this.#pickerContext.selection, - (selection) => (this.value = selection.join(',')), - 'umbUserInputSelectionObserver', - ); - this.observe( - this.#pickerContext.selectedItems, - (selectedItems) => (this._items = selectedItems), - 'umbUserInputItemsObserver', - ); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(',')), '_observeSelection'); + this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems), '_observerItems'); } protected getFormElement() { @@ -111,35 +121,49 @@ export class UmbUserInputElement extends UUIFormControlMixin(UmbLitElement, '') this.#pickerContext.openPicker({}); } + #removeItem(item: UmbUserItemModel) { + this.#pickerContext.requestRemoveItem(item.unique); + } + render() { + return html`${this.#renderItems()} ${this.#renderAddButton()}`; + } + + #renderAddButton() { + if (this.max > 0 && this.selection.length >= this.max) return nothing; return html` - ${this._items?.map((item) => this.#renderItem(item))} - ${this.#renderAddButton()} + + `; + } + + #renderItems() { + if (!this._items) return nothing; + return html` + + ${repeat( + this._items, + (item) => item.unique, + (item) => this.#renderItem(item), + )} + `; } #renderItem(item: UmbUserItemModel) { if (!item.unique) return; return html` - + - this.#pickerContext.requestRemoveItem(item.unique)} - label=${this.localize.term('general_remove')}> + this.#removeItem(item)}> `; } - #renderAddButton() { - if (this.max === 1 && this.selection.length >= this.max) return nothing; - return html``; - } - static styles = [ css` #btn-add {