diff --git a/src/Umbraco.Web.UI.Client/devops/localization/unused-language-keys.js b/src/Umbraco.Web.UI.Client/devops/localization/unused-language-keys.js index 6fa81fc45e..f462faf4c3 100644 --- a/src/Umbraco.Web.UI.Client/devops/localization/unused-language-keys.js +++ b/src/Umbraco.Web.UI.Client/devops/localization/unused-language-keys.js @@ -26,7 +26,7 @@ const mainMap = buildMap(mainKeys); const keys = Array.from(mainMap.keys()); const usedKeys = new Set(); -const elementAndControllerFiles = await glob(`${__dirname}/../../src/**/*.ts`); +const elementAndControllerFiles = await glob(`${__dirname}/../../src/**/*.ts`, { filesOnly: true }); console.log(`Checking ${elementAndControllerFiles.length} files for unused keys`); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entry.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entry.context.ts index 2b026e2e92..4f58426a82 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entry.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entry.context.ts @@ -206,9 +206,9 @@ export class UmbBlockGridEntryContext // Secure columnSpan fits options: this.observe( - observeMultiple([this.layout, this.columnSpan, this.relevantColumnSpanOptions, this._entries.layoutColumns]), - ([layout, columnSpan, relevantColumnSpanOptions, layoutColumns]) => { - if (!layout || !layoutColumns) return; + observeMultiple([this.columnSpan, this.relevantColumnSpanOptions, this._entries.layoutColumns]), + ([columnSpan, relevantColumnSpanOptions, layoutColumns]) => { + if (!layoutColumns) return; const newColumnSpan = this.#calcColumnSpan( columnSpan ?? layoutColumns, relevantColumnSpanOptions, diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts index 422391fe62..c4c814e876 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts @@ -146,15 +146,15 @@ export abstract class UmbBlockEntryContext< // Consume block manager: this.consumeContext(blockManagerContextToken, (manager) => { this._manager = manager; - this.#gotManager(); this._gotManager(); + this.#gotManager(); }); // Consume block entries: this.consumeContext(blockEntriesContextToken, (entries) => { this._entries = entries; - this.#gotEntries(); this._gotEntries(); + this.#gotEntries(); }); // Observe UDI: diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts index c6218caef2..2e80104b45 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts @@ -46,6 +46,8 @@ export class UmbContentTypeStructureManager< private readonly _contentTypeContainers = this.#contentTypes.asObservablePart((x) => x.flatMap((x) => x.containers ?? []), ); + readonly contentTypeUniques = this.#contentTypes.asObservablePart((x) => x.map((y) => y.unique)); + readonly contentTypeAliases = this.#contentTypes.asObservablePart((x) => x.map((y) => y.alias)); #containers: UmbArrayState = new UmbArrayState( [], @@ -205,6 +207,12 @@ export class UmbContentTypeStructureManager< getContentTypes() { return this.#contentTypes.getValue(); } + getContentTypeUniques() { + return this.#contentTypes.getValue().map((x) => x.unique); + } + getContentTypeAliases() { + return this.#contentTypes.getValue().map((x) => x.alias); + } // TODO: We could move the actions to another class? diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.ts index dd5201daee..37f501f27d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.ts @@ -72,16 +72,21 @@ export type resolvePlacementArgs = { pointerY: number; }; +type UniqueType = string | symbol | number; + +/** + * Internal type, which is adjusted to become the public one. + * @internal */ type INTERNAL_UmbSorterConfig = { /** * Define how to retrive the unique identifier of an element. If this method returns undefined, the move will be cancelled. */ - getUniqueOfElement: (element: ElementType) => string | null | symbol | number | undefined; - getUniqueOfModel: (modeEntry: T) => string | null | symbol | number | undefined; + getUniqueOfElement: (element: ElementType) => UniqueType | null | undefined; + getUniqueOfModel: (modeEntry: T) => UniqueType | null | undefined; /** * Optionally define a unique identifier for each sorter experience, all Sorters that uses the same identifier to connect with other sorters. */ - identifier: string | symbol; + identifier: UniqueType; /** * A query selector for the item element. */ @@ -107,6 +112,7 @@ type INTERNAL_UmbSorterConfig = { * The selector to find the draggable element within the item. */ draggableSelector?: string; + //boundarySelector?: string; dataTransferResolver?: (dataTransfer: DataTransfer | null, currentItem: T) => void; onStart?: (argument: { item: T; element: ElementType }) => void; @@ -166,7 +172,7 @@ type INTERNAL_UmbSorterConfig = { performItemRemove?: (argument: { item: T }) => Promise | boolean; }; -// External type with some properties optional, as they have defaults: +// External type with some properties optional, as they have fallback values: export type UmbSorterConfig = Omit< INTERNAL_UmbSorterConfig, 'ignorerSelector' | 'containerSelector' | 'identifier' @@ -178,6 +184,19 @@ export type UmbSorterConfig = * @class UmbSorterController * @implements {UmbControllerInterface} * @description This controller can make user able to sort items. + * @example + * + * This example shows how to setup a sorter controller with no special needs. + * Assuming your declaring this on a Umbraco Element(UmbControllerHostElement): + * + * ```ts + * const sorter = new UmbSorterController(this, { + * itemSelector: '.item', + * containerSelector: '.container', + * getUniqueOfElement: (element) => element.dataset.id, + * getUniqueOfModel: (model) => model.id + * }); + * ``` */ export class UmbSorterController extends UmbControllerBase { // @@ -274,16 +293,13 @@ export class UmbSorterController this.#config.getUniqueOfModel(x) === unique) !== undefined; } - /* - getItem(unique: string) { - if (!unique) return undefined; + getItem(unique: UniqueType) { return this.#model.find((x) => this.#config.getUniqueOfModel(x) === unique); } - */ hostConnected() { this.#isConnected = true; @@ -453,7 +469,7 @@ export class UmbSorterController; + // Notice, it is acceptable here to get index via object reference, but only cause there has been no change at this stage, otherwise we cannot trust the object instance is represented in the model — it could have mutated or been cloned [NL] UmbSorterController.originalIndex = this.#model.indexOf(UmbSorterController.activeItem); if (!UmbSorterController.activeItem) { @@ -478,7 +495,7 @@ export class UmbSorterController) { - const item = UmbSorterController.activeItem; + public async moveItemInModel(newIndex: number, fromCtrl: UmbSorterController) { + if (!UmbSorterController.activeItem) { + console.error('There is no active item to move'); + return false; + } + const itemUnique = this.#config.getUniqueOfModel(UmbSorterController.activeItem); + if (!itemUnique) { + console.error('Failed to retrieve active item unique'); + return false; + } + // We use the getItem method to find the current item/object of this entry, as we cannot trust the object instance(activeItem) to be the same as in the model. [NL] + // So notice, item in this method is the real modal entry reference, where in many other cases we use the activeItem which might not be up to date with the real entry of the model. [NL] + const item = fromCtrl.getItem(itemUnique); if (!item) { - console.error('Could not find item of sync item'); + console.error('Could not find item of model to move', itemUnique, this.#model); return false; } if (this.notifyRequestDrop({ item }) === false) { @@ -819,10 +847,9 @@ export class UmbSorterController, }); this.#config.onChange?.({ model: newModel, item }); - } - // If everything went well, we can set new activeSorter to this: - UmbSorterController.activeSorter = this as unknown as UmbSorterController; - UmbSorterController.activeIndex = newIndex; + // If everything went well, we can set the new activeSorter (and dropSorter) to this, as we are switching container. [NL] + UmbSorterController.activeSorter = this as unknown as UmbSorterController; + UmbSorterController.dropSorter = this as unknown as UmbSorterController; + UmbSorterController.activeIndex = newIndex; + } } return true; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/utils.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/utils.ts index 5a4f3cac89..5dc112913f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/utils.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/utils.ts @@ -127,7 +127,7 @@ export function getDocumentHistoryTagStyleAndText(type: UmbDocumentAuditLogType) case UmbDocumentAuditLog.CUSTOM: return { style: { look: 'placeholder', color: 'default' }, - text: { label: 'auditTrails_custom', desc: '' }, + text: { label: 'auditTrails_smallCustom', desc: 'auditTrails_custom' }, }; default: diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/image-cropper-field.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/image-cropper-field.element.ts index d6c523764c..84fcd88681 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/image-cropper-field.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/image-cropper-field.element.ts @@ -8,7 +8,7 @@ import type { UmbImageCropperFocalPoint, UmbImageCropperPropertyEditorValue, } from './index.js'; -import { css, html, customElement, property, state, repeat, nothing } from '@umbraco-cms/backoffice/external/lit'; +import { css, customElement, html, property, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; @@ -151,11 +151,13 @@ export class UmbInputImageCropperFieldElement extends UmbLitElement {
- ${!this.hideFocalPoint - ? html` + html`` - : nothing} + @click=${this.#onResetFocalPoint}>`, + )}
`; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/image-cropper-focus-setter.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/image-cropper-focus-setter.element.ts index 370172ead7..d9e4b0caab 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/image-cropper-focus-setter.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/image-cropper-focus-setter.element.ts @@ -34,7 +34,7 @@ export class UmbImageCropperFocusSetterElement extends LitElement { protected updated(_changedProperties: PropertyValueMap | Map): void { super.updated(_changedProperties); - if (!this.hideFocalPoint) return; + if (this.hideFocalPoint) return; if (_changedProperties.has('focalPoint') && this.focalPointElement) { this.focalPointElement.style.left = `calc(${this.focalPoint.left * 100}% - ${this.#DOT_RADIUS}px)`; @@ -142,7 +142,6 @@ export class UmbImageCropperFocusSetterElement extends LitElement { render() { if (!this.src) return nothing; - return html`
nothing} src=${this.src} alt="" /> @@ -150,6 +149,7 @@ export class UmbImageCropperFocusSetterElement extends LitElement {
`; } + static styles = css` :host { display: flex; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/image-cropper-editor/image-cropper-editor-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/image-cropper-editor/image-cropper-editor-modal.element.ts index 0e3b172680..7b4763193b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/image-cropper-editor/image-cropper-editor-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/image-cropper-editor/image-cropper-editor-modal.element.ts @@ -100,10 +100,15 @@ export class UmbImageCropperEditorModalElement extends UmbModalBaseElement< this.#getSrc(); } - #onChange(e: CustomEvent) { - const value = (e.target as UmbInputImageCropperFieldElement).value; + #onChange(e: CustomEvent & { target: UmbInputImageCropperFieldElement }) { + const value = e.target.value; if (!value) return; + if (this._imageCropperValue) { + this._imageCropperValue.crops = value.crops; + this._imageCropperValue.focalPoint = value.focalPoint; + } + this.value = { key: this._key, unique: this._unique, crops: value.crops, focalPoint: value.focalPoint }; } @@ -124,22 +129,24 @@ export class UmbImageCropperEditorModalElement extends UmbModalBaseElement< } #renderBody() { - return html`
- -
- - - - - - + return html` +
+ +
+ + + + + + +
-
`; + `; } static styles = [