diff --git a/src/Umbraco.Web.UI.Client/src/packages/code-editor/property-editor/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/code-editor/property-editor/manifests.ts index 5e6142daa1..e31e338113 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/code-editor/property-editor/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/code-editor/property-editor/manifests.ts @@ -8,8 +8,8 @@ export const manifest: ManifestPropertyEditorUi = { meta: { label: 'Code Editor', propertyEditorSchemaAlias: 'Umbraco.Plain.String', - icon: 'icon-code', - group: 'common', + icon: 'icon-brackets', + group: 'richContent', settings: { properties: [ { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/property-editor-ui-picker-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/property-editor-ui-picker-modal.token.ts index 7a96e4c8ce..185f5ff781 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/property-editor-ui-picker-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/property-editor-ui-picker-modal.token.ts @@ -1,6 +1,7 @@ import { UmbModalToken } from './modal-token.js'; export interface UmbPropertyEditorUIPickerModalData { + /** @deprecated This property will be removed in Umbraco 15. */ submitLabel?: string; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts index 62700d299f..0b433b76ed 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts @@ -52,9 +52,7 @@ export class UmbInputDataTypeElement extends UUIFormControlMixin(UmbLitElement, new UmbModalRouteRegistrationController(this, UMB_DATA_TYPE_PICKER_FLOW_MODAL) .onSetup(() => { return { - data: { - submitLabel: 'Submit', - }, + data: {}, value: { selection: this._ids ?? [] }, }; }) diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-data-type-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-data-type-picker-modal.element.ts index 09b740d180..aaf4b0dfdf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-data-type-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-data-type-picker-modal.element.ts @@ -3,9 +3,9 @@ import type { UmbDataTypePickerFlowDataTypePickerModalData, UmbDataTypePickerFlowDataTypePickerModalValue, } from './data-type-picker-flow-data-type-picker-modal.token.js'; -import { css, html, customElement, state, repeat } from '@umbraco-cms/backoffice/external/lit'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { css, customElement, html, repeat, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbDataTypeItemModel } from '@umbraco-cms/backoffice/data-type'; @customElement('umb-data-type-picker-flow-data-type-picker-modal') @@ -25,10 +25,10 @@ export class UmbDataTypePickerFlowDataTypePickerModalElement extends UmbModalBas this._propertyEditorUiAlias = this.data.propertyEditorUiAlias; - this._observeDataTypesOf(this._propertyEditorUiAlias); + this.#observeDataTypesOf(this._propertyEditorUiAlias); } - private async _observeDataTypesOf(propertyEditorUiAlias: string) { + async #observeDataTypesOf(propertyEditorUiAlias: string) { if (!this.data) return; const dataTypeCollectionRepository = new UmbDataTypeCollectionRepository(this); @@ -40,64 +40,65 @@ export class UmbDataTypePickerFlowDataTypePickerModalElement extends UmbModalBas }); this.observe(collection.asObservable(), (dataTypes) => { - this._dataTypes = dataTypes; + this._dataTypes = dataTypes.sort((a, b) => a.name.localeCompare(b.name)); }); } - private _handleClick(dataType: UmbDataTypeItemModel) { + #handleClick(dataType: UmbDataTypeItemModel) { if (dataType.unique) { this.value = { dataTypeId: dataType.unique }; this.modalContext?.submit(); } } - private _handleCreate() { + #handleCreate() { this.value = { createNewWithPropertyEditorUiAlias: this._propertyEditorUiAlias }; this.modalContext?.submit(); } - private _close() { + #close() { this.modalContext?.reject(); } override render() { return html` - - ${this._renderDataTypes()} ${this._renderCreate()} + + ${this.#renderDataTypes()} ${this.#renderCreate()}
- +
`; } - private _renderDataTypes() { - return this._dataTypes && this._dataTypes.length > 0 - ? html`` - : ''; - } - private _renderCreate() { + #renderDataTypes() { + if (!this._dataTypes?.length) return; return html` - + + `; + } + + #renderCreate() { + return html` +
- Create new + Create new
`; @@ -174,12 +175,6 @@ export class UmbDataTypePickerFlowDataTypePickerModalElement extends UmbModalBas margin: auto; } - #category-name { - text-align: center; - display: block; - text-transform: capitalize; - font-size: 1.2rem; - } #create-button { max-width: 100px; --uui-button-padding-left-factor: 0; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts index a898f756b9..cf2af5dd45 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts @@ -6,23 +6,20 @@ import type { UmbDataTypePickerFlowModalData, UmbDataTypePickerFlowModalValue, } from './data-type-picker-flow-modal.token.js'; -import { css, html, repeat, customElement, state, when, nothing } from '@umbraco-cms/backoffice/external/lit'; +import { css, customElement, html, nothing, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { umbFocus } from '@umbraco-cms/backoffice/lit-element'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router'; +import { UmbPaginationManager, debounce, fromCamelCase } from '@umbraco-cms/backoffice/utils'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/content-type'; +import { UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/property-type'; import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbDataTypeItemModel } from '@umbraco-cms/backoffice/data-type'; import type { UmbModalRouteBuilder } from '@umbraco-cms/backoffice/router'; import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; -import { umbFocus } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/content-type'; -import { UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/property-type'; -import { UmbPaginationManager, debounce } from '@umbraco-cms/backoffice/utils'; -interface GroupedItems { - [key: string]: Array; -} @customElement('umb-data-type-picker-flow-modal') export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< UmbDataTypePickerFlowModalData, @@ -32,17 +29,13 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< public override set data(value: UmbDataTypePickerFlowModalData) { super.data = value; - this._submitLabel = this.data?.submitLabel ?? this._submitLabel; } @state() - private _groupedDataTypes?: GroupedItems; + private _groupedDataTypes?: Array<{ key: string; items: Array }> = []; @state() - private _groupedPropertyEditorUIs: GroupedItems = {}; - - @state() - private _submitLabel = 'Select'; + private _groupedPropertyEditorUIs: Array<{ key: string; items: Array }> = []; @state() private _currentPage = 1; @@ -52,13 +45,18 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< pagination = new UmbPaginationManager(); - private _createDataTypeModal!: UmbModalRouteRegistrationController; - #collectionRepository; - #dataTypes: Array = []; - #propertyEditorUIs: Array = []; + + #createDataTypeModal!: UmbModalRouteRegistrationController; + #currentFilterQuery = ''; + #dataTypes: Array = []; + + #groupLookup: Record = {}; + + #propertyEditorUIs: Array = []; + constructor() { super(); @@ -66,10 +64,10 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< this.#init(); } - private _createDataType(propertyEditorUiAlias: string) { + #createDataType(propertyEditorUiAlias: string) { // TODO: Could be nice with a more pretty way to prepend to the URL: // Open create modal: - this._createDataTypeModal.open( + this.#createDataTypeModal.open( { uiAlias: propertyEditorUiAlias }, `create/parent/${UMB_DATA_TYPE_ENTITY_TYPE}/null`, ); @@ -82,10 +80,13 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< this.#initPromise = Promise.all([ this.observe(umbExtensionsRegistry.byType('propertyEditorUi'), (propertyEditorUIs) => { // Only include Property Editor UIs which has Property Editor Schema Alias - this.#propertyEditorUIs = propertyEditorUIs.filter( - (propertyEditorUi) => !!propertyEditorUi.meta.propertyEditorSchemaAlias, - ); - this._performFiltering(); + this.#propertyEditorUIs = propertyEditorUIs + .filter((propertyEditorUi) => !!propertyEditorUi.meta.propertyEditorSchemaAlias) + .sort((a, b) => a.meta.label.localeCompare(b.meta.label)); + + this.#groupLookup = Object.fromEntries(propertyEditorUIs.map((ui) => [ui.alias, ui.meta.group])); + + this.#performFiltering(); }).asPromise(), ]); @@ -101,10 +102,10 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< }) .onSubmit((submitData) => { if (submitData?.dataTypeId) { - this._select(submitData.dataTypeId); + this.#select(submitData.dataTypeId); this._submitModal(); } else if (submitData?.createNewWithPropertyEditorUiAlias) { - this._createDataType(submitData.createNewWithPropertyEditorUiAlias); + this.#createDataType(submitData.createNewWithPropertyEditorUiAlias); } }) .observeRouteBuilder((routeBuilder) => { @@ -112,7 +113,7 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< this.requestUpdate('_dataTypePickerModalRouteBuilder'); }); - this._createDataTypeModal = new UmbModalRouteRegistrationController(this, UMB_DATATYPE_WORKSPACE_MODAL) + this.#createDataTypeModal = new UmbModalRouteRegistrationController(this, UMB_DATATYPE_WORKSPACE_MODAL) .addAdditionalPath(':uiAlias') .onSetup(async (params) => { const contentContextConsumer = this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, () => { @@ -137,7 +138,7 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< }; }) .onSubmit((value) => { - this._select(value?.unique); + this.#select(value?.unique); this._submitModal(); }); } @@ -160,14 +161,14 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< } } - private _handleDataTypeClick(dataType: UmbDataTypeItemModel) { + #handleDataTypeClick(dataType: UmbDataTypeItemModel) { if (dataType.unique) { - this._select(dataType.unique); + this.#select(dataType.unique); this._submitModal(); } } - private _select(unique: string | undefined) { + #select(unique: string | undefined) { this.value = { selection: unique ? [unique] : [] }; } @@ -188,98 +189,100 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< async #handleFiltering() { await this.#getDataTypes(); - this._performFiltering(); + this.#performFiltering(); } - private _performFiltering() { + #performFiltering() { if (this.#currentFilterQuery) { - const filteredDataTypes = this.#dataTypes.filter((dataType) => - dataType.name?.toLowerCase().includes(this.#currentFilterQuery), + const filteredDataTypes = this.#dataTypes + .filter((dataType) => dataType.name?.toLowerCase().includes(this.#currentFilterQuery)) + .sort((a, b) => a.name.localeCompare(b.name)); + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + const grouped = Object.groupBy(filteredDataTypes, (dataType: UmbDataTypeItemModel) => + fromCamelCase(this.#groupLookup[dataType.propertyEditorUiAlias] ?? 'Uncategorized'), ); - /* TODO: data type items doesn't have a group property. We will need a reference to the Property Editor UI to get the group. - this is a temp solution to group them as uncategorized. The same result as with the lodash groupBy. - */ - this._groupedDataTypes = { - undefined: filteredDataTypes, - }; + this._groupedDataTypes = Object.keys(grouped) + .sort((a, b) => a.localeCompare(b)) + .map((key) => ({ key, items: grouped[key] })); } else { - this._groupedDataTypes = undefined; + this._groupedDataTypes = []; } const filteredUIs = !this.#currentFilterQuery ? this.#propertyEditorUIs - : this.#propertyEditorUIs.filter((propertyEditorUI) => { - return ( + : this.#propertyEditorUIs.filter( + (propertyEditorUI) => propertyEditorUI.name.toLowerCase().includes(this.#currentFilterQuery) || - propertyEditorUI.alias.toLowerCase().includes(this.#currentFilterQuery) - ); - }); + propertyEditorUI.alias.toLowerCase().includes(this.#currentFilterQuery), + ); - // TODO: groupBy is not known by TS yet // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error - this._groupedPropertyEditorUIs = Object.groupBy( - filteredUIs, - (propertyEditorUI: ManifestPropertyEditorUi) => propertyEditorUI.meta.group, + const grouped = Object.groupBy(filteredUIs, (propertyEditorUi: ManifestPropertyEditorUi) => + fromCamelCase(propertyEditorUi.meta.group ?? 'Uncategorized'), ); + + this._groupedPropertyEditorUIs = Object.keys(grouped) + .sort((a, b) => a.localeCompare(b)) + .map((key) => ({ key, items: grouped[key] })); } override render() { return html` - - ${this._renderFilter()} ${this._renderGrid()} + + ${this.#renderFilter()} ${this.#renderGrid()}
- +
`; } - private _renderGrid() { - return this.#currentFilterQuery ? this._renderFilteredList() : this._renderUIs(); + #renderGrid() { + return this.#currentFilterQuery ? this.#renderFilteredList() : this.#renderUIs(); } - private _renderFilter() { + #renderFilter() { return html` `; } - private _renderFilteredList() { + #renderFilteredList() { if (!this._groupedDataTypes) return nothing; - const dataTypesEntries = Object.entries(this._groupedDataTypes); - if (!this._groupedPropertyEditorUIs) return nothing; - const editorUIEntries = Object.entries(this._groupedPropertyEditorUIs); - - if (dataTypesEntries.length === 0 && editorUIEntries.length === 0) { - return html`Nothing matches your search, try another search term.`; + if (this._groupedDataTypes.length === 0 && this._groupedPropertyEditorUIs.length === 0) { + return html`

Nothing matches your search, try another search term.

`; } return html` ${when( - dataTypesEntries.length > 0, - () => - html`
- Available configurations -
- ${this._renderDataTypes()}${this.#renderLoadMore()}`, + this._groupedDataTypes.length > 0, + () => html` +
+ Available configurations +
+ ${this.#renderDataTypes()} ${this.#renderLoadMore()} + `, )} ${when( - editorUIEntries.length > 0, - () => - html`
- Create a new configuration -
- ${this._renderUIs(true)}`, + this._groupedPropertyEditorUIs.length > 0, + () => html` +
+ Create a new configuration +
+ ${this.#renderUIs(true)} + `, )} `; } @@ -289,83 +292,93 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< return html``; } - private _renderDataTypes() { + #renderDataTypes() { if (!this._groupedDataTypes) return nothing; - const entries = Object.entries(this._groupedDataTypes); - // TODO: Fix so we can have Data Types grouped. (or choose not to group them) - return entries.map( - ([key, value]) => - html`
${key === 'undefined' ? 'Uncategorized' : key}
- ${this._renderGroupDataTypes(value)}`, + return this._groupedDataTypes.map( + (group) => html` +
${group.key}
+ ${this.#renderGroupDataTypes(group.items)} + `, ); } - private _renderUIs(createAsNewOnPick?: boolean) { + #renderUIs(createAsNewOnPick?: boolean) { if (!this._groupedPropertyEditorUIs) return nothing; - const entries = Object.entries(this._groupedPropertyEditorUIs); - - return entries.map( - ([key, value]) => - html`
${key === 'undefined' ? 'Uncategorized' : key}
- ${this._renderGroupUIs(value, createAsNewOnPick)}`, + return this._groupedPropertyEditorUIs.map( + (group) => html` +
${group.key}
+ ${this.#renderGroupUIs(group.items, createAsNewOnPick)} + `, ); } - private _renderGroupUIs(uis: Array, createAsNewOnPick?: boolean) { - return html`
    - ${this._dataTypePickerModalRouteBuilder - ? repeat( - uis, - (propertyEditorUI) => propertyEditorUI.alias, - (propertyEditorUI) => { - return html`
  • ${this._renderDataTypeButton(propertyEditorUI, createAsNewOnPick)}
  • `; - }, - ) - : ''} -
`; + #renderGroupUIs(uis: Array, createAsNewOnPick?: boolean) { + return html` +
    + ${this._dataTypePickerModalRouteBuilder + ? repeat( + uis, + (propertyEditorUI) => propertyEditorUI.alias, + (propertyEditorUI) => { + return html`
  • ${this.#renderDataTypeButton(propertyEditorUI, createAsNewOnPick)}
  • `; + }, + ) + : ''} +
+ `; } - private _renderDataTypeButton(propertyEditorUI: ManifestPropertyEditorUi, createAsNewOnPick?: boolean) { + #renderDataTypeButton(propertyEditorUI: ManifestPropertyEditorUi, createAsNewOnPick?: boolean) { if (createAsNewOnPick) { - return html` this._createDataType(propertyEditorUI.alias)}> - ${this._renderItemContent(propertyEditorUI)} - `; + return html` + this.#createDataType(propertyEditorUI.alias)}> + ${this.#renderItemContent(propertyEditorUI)} + + `; } else { - return html` - ${this._renderItemContent(propertyEditorUI)} - `; + return html` + + ${this.#renderItemContent(propertyEditorUI)} + + `; } } - private _renderItemContent(propertyEditorUI: ManifestPropertyEditorUi) { - return html`
- - ${propertyEditorUI.meta.label || propertyEditorUI.name} -
`; + + #renderItemContent(propertyEditorUI: ManifestPropertyEditorUi) { + return html` +
+ + ${propertyEditorUI.meta.label || propertyEditorUI.name} +
+ `; } - private _renderGroupDataTypes(dataTypes: Array) { - return html`
    - ${repeat( - dataTypes, - (dataType) => dataType.unique, - (dataType) => - html`
  • - -
    - - ${dataType.name} -
    -
    -
  • `, - )} -
`; + #renderGroupDataTypes(dataTypes: Array) { + return html` +
    + ${repeat( + dataTypes, + (dataType) => dataType.unique, + (dataType) => html` +
  • + this.#handleDataTypeClick(dataType)}> +
    + + ${dataType.name} +
    +
    +
  • + `, + )} +
+ `; } static override styles = [ @@ -430,6 +443,7 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< height: 100%; width: 100%; } + #item-grid .item .icon { font-size: 2em; margin: auto; @@ -440,7 +454,6 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< } .choice-type-headline { - text-transform: capitalize; border-bottom: 1px solid var(--uui-color-divider); } `, diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.token.ts index d6acec2fda..55a1643d90 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.token.ts @@ -1,6 +1,7 @@ import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; export interface UmbDataTypePickerFlowModalData { + /** @deprecated This property will be removed in Umbraco 15. */ submitLabel?: string; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts index a8ea26c931..3fb5bd97c2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts @@ -1,141 +1,131 @@ -import { css, html, customElement, state, repeat } from '@umbraco-cms/backoffice/external/lit'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; +import { css, customElement, html, repeat, state } from '@umbraco-cms/backoffice/external/lit'; +import { fromCamelCase } from '@umbraco-cms/backoffice/utils'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { umbFocus } from '@umbraco-cms/backoffice/lit-element'; +import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; +import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbPropertyEditorUIPickerModalData, UmbPropertyEditorUIPickerModalValue, } from '@umbraco-cms/backoffice/modal'; -import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; -import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/extension-registry'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; -import { umbFocus } from '@umbraco-cms/backoffice/lit-element'; +import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; -interface GroupedPropertyEditorUIs { - [key: string]: Array; -} @customElement('umb-property-editor-ui-picker-modal') export class UmbPropertyEditorUIPickerModalElement extends UmbModalBaseElement< UmbPropertyEditorUIPickerModalData, UmbPropertyEditorUIPickerModalValue > { @state() - private _groupedPropertyEditorUIs: GroupedPropertyEditorUIs = {}; + private _groupedPropertyEditorUIs: Array<{ key: string; items: Array }> = []; @state() private _propertyEditorUIs: Array = []; - @state() - private _submitLabel = 'Select'; - override connectedCallback(): void { super.connectedCallback(); - // TODO: We never parse on a submit label, so this seem weird as we don't enable this of other places. - //this._submitLabel = this.data?.submitLabel ?? this._submitLabel; - this.#usePropertyEditorUIs(); } #usePropertyEditorUIs() { this.observe(umbExtensionsRegistry.byType('propertyEditorUi'), (propertyEditorUIs) => { // Only include Property Editor UIs which has Property Editor Schema Alias - this._propertyEditorUIs = propertyEditorUIs.filter( - (propertyEditorUi) => !!propertyEditorUi.meta.propertyEditorSchemaAlias, - ); + this._propertyEditorUIs = propertyEditorUIs + .filter((propertyEditorUi) => !!propertyEditorUi.meta.propertyEditorSchemaAlias) + .sort((a, b) => a.meta.label.localeCompare(b.meta.label)); - // TODO: groupBy is not known by TS yet - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - this._groupedPropertyEditorUIs = Object.groupBy( - this._propertyEditorUIs, - (propertyEditorUi: ManifestPropertyEditorUi) => propertyEditorUi.meta.group, - ); + this.#groupPropertyEditorUIs(this._propertyEditorUIs); }); } #handleClick(propertyEditorUi: ManifestPropertyEditorUi) { - this.#select(propertyEditorUi.alias); - } - - #select(alias: string) { - this.value = { selection: [alias] }; + this.value = { selection: [propertyEditorUi.alias] }; + this._submitModal(); } #handleFilterInput(event: UUIInputEvent) { - let query = (event.target.value as string) || ''; - query = query.toLowerCase(); + const query = ((event.target.value as string) || '').toLowerCase(); const result = !query ? this._propertyEditorUIs - : this._propertyEditorUIs.filter((propertyEditorUI) => { - return ( - propertyEditorUI.name.toLowerCase().includes(query) || propertyEditorUI.alias.toLowerCase().includes(query) - ); - }); + : this._propertyEditorUIs.filter( + (propertyEditorUI) => + propertyEditorUI.name.toLowerCase().includes(query) || propertyEditorUI.alias.toLowerCase().includes(query), + ); - // TODO: groupBy is not known by TS yet + this.#groupPropertyEditorUIs(result); + } + + #groupPropertyEditorUIs(items: Array) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error - this._groupedPropertyEditorUIs = Object.groupBy( - result, - (propertyEditorUI: ManifestPropertyEditorUi) => propertyEditorUI.meta.group, + const grouped = Object.groupBy(items, (propertyEditorUi: ManifestPropertyEditorUi) => + fromCamelCase(propertyEditorUi.meta.group), ); + + this._groupedPropertyEditorUIs = Object.keys(grouped) + .sort((a, b) => a.localeCompare(b)) + .map((key) => ({ key, items: grouped[key] })); } override render() { return html` - ${this._renderFilter()} ${this._renderGrid()} + ${this.#renderFilter()} ${this.#renderGrid()}
- - +
`; } - private _renderFilter() { - return html` - - `; + #renderFilter() { + return html` + + + + `; } - private _renderGrid() { - return html` ${Object.entries(this._groupedPropertyEditorUIs).map( - ([key, value]) => - html`

${key}

- ${this._renderGroupItems(value)}`, - )}`; - } - - private _renderGroupItems(groupItems: Array) { - return html`
    + #renderGrid() { + return html` ${repeat( - groupItems, - (propertyEditorUI) => propertyEditorUI.alias, - (propertyEditorUI) => - html`
  • - -
  • `, + this._groupedPropertyEditorUIs, + (group) => group.key, + (group) => html` +

    ${group.key}

    + ${this.#renderGroupItems(group.items)} + `, )} -
`; + `; + } + + #renderGroupItems(groupItems: Array) { + return html` +
    + ${repeat( + groupItems, + (propertyEditorUI) => propertyEditorUI.alias, + (propertyEditorUI) => html` +
  • + +
  • + `, + )} +
+ `; } static override styles = [ - UmbTextStyles, css` #filter { width: 100%; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-editors/document-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-editors/document-picker/manifests.ts index 0bcb58633f..cfff881f56 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-editors/document-picker/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-editors/document-picker/manifests.ts @@ -11,7 +11,7 @@ export const manifests: Array = [ label: 'Document Picker', propertyEditorSchemaAlias: 'Umbraco.ContentPicker', icon: 'icon-document', - group: 'common', + group: 'pickers', settings: { properties: [ { diff --git a/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/property-editors/markdown-editor/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/property-editors/markdown-editor/manifests.ts index 3e7ac5cd37..ee9dc738f5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/property-editors/markdown-editor/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/property-editors/markdown-editor/manifests.ts @@ -10,7 +10,7 @@ const manifest: ManifestPropertyEditorUi = { label: 'Markdown Editor', propertyEditorSchemaAlias: 'Umbraco.MarkdownEditor', icon: 'icon-code', - group: 'pickers', + group: 'richContent', settings: { properties: [ { diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/manifests.ts index 2a2b5f688e..dfd31736c4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/manifests.ts @@ -9,7 +9,7 @@ const manifest: ManifestPropertyEditorUi = { meta: { label: 'Image Cropper', icon: 'icon-crop', - group: 'pickers', + group: 'media', propertyEditorSchemaAlias: 'Umbraco.ImageCropper', }, }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/manifests.ts index 8db76a1bcc..669c8977f6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/manifests.ts @@ -10,7 +10,7 @@ const manifest: ManifestPropertyEditorUi = { label: 'Media Picker', propertyEditorSchemaAlias: 'Umbraco.MediaPicker3', icon: 'icon-picture', - group: 'pickers', + group: 'media', }, }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/upload-field/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/upload-field/manifests.ts index f7da784d05..311f8d61b9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/upload-field/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/upload-field/manifests.ts @@ -10,7 +10,7 @@ const manifest: ManifestPropertyEditorUi = { label: 'Upload Field', propertyEditorSchemaAlias: 'Umbraco.UploadField', icon: 'icon-download-alt', - group: 'common', + group: 'media', }, }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/dropdown/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/dropdown/manifests.ts index 6635738b2d..e0be2a8a08 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/dropdown/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/dropdown/manifests.ts @@ -11,7 +11,7 @@ export const manifests: Array = [ label: 'Dropdown', propertyEditorSchemaAlias: 'Umbraco.DropDown.Flexible', icon: 'icon-list', - group: 'pickers', + group: 'lists', }, }, schemaManifest, diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/label/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/label/manifests.ts index 34341e6edb..942843cf4a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/label/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/label/manifests.ts @@ -10,7 +10,7 @@ export const manifests: Array = [ meta: { label: 'Label', icon: 'icon-readonly', - group: 'pickers', + group: 'common', propertyEditorSchemaAlias: 'Umbraco.Label', }, }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/manifests.ts index aaa3f6880e..c4c40d5be4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/manifests.ts @@ -13,7 +13,7 @@ const manifest: ManifestPropertyEditorUi = { label: 'Rich Text Editor', propertyEditorSchemaAlias: UMB_BLOCK_RTE_PROPERTY_EDITOR_SCHEMA_ALIAS, icon: 'icon-browser-window', - group: 'richText', + group: 'richContent', settings: { properties: [ {