diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 03bac9db89..73ce5263a3 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -61,6 +61,7 @@ "./member-type": "./dist-cms/packages/members/member-types/index.js", "./package": "./dist-cms/packages/packages/package/index.js", "./data-type": "./dist-cms/packages/core/data-type/index.js", + "./dynamic-root": "./dist-cms/packages/dynamic-root/index.js", "./language": "./dist-cms/packages/settings/languages/index.js", "./logviewer": "./dist-cms/packages/settings/logviewer/index.js", "./relation-type": "./dist-cms/packages/relations/relation-types/index.js", diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts index 84243dafc2..997f5a79fa 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts @@ -30,6 +30,7 @@ const CORE_PACKAGES = [ import('../../packages/log-viewer/umbraco-package.js'), import('../../packages/health-check/umbraco-package.js'), import('../../packages/static-file/umbraco-package.js'), + import('../../packages/dynamic-root/umbraco-package.js'), ]; @customElement('umb-backoffice') diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts index 9a5db47c5d..07529c2a54 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts @@ -1156,6 +1156,38 @@ export default { pickedTrashedItem: 'Du har valgt et dokument som er slettet eller lagt i papirkurven', pickedTrashedItems: 'Du har valgt dokumenter som er slettede eller lagt i papirkurven', }, + dynamicRoot: { + configurationTitle: 'Dynamisk udgangspunkts forespørgsel', + pickDynamicRootOriginTitle: 'Vælg begyndelsen', + pickDynamicRootOriginDesc: 'Beskriv begyndelsen for dynamisk udgangspunkts forespørgselen', + originRootTitle: 'Roden', + originRootDesc: 'Rod noden for denne kilde', + originParentTitle: 'Overliggende', + originParentDesc: 'Den overliggende node af kilden i denne redigerings session', + originCurrentTitle: 'Nuværende', + originCurrentDesc: 'Kilde noden for denne redigerings session', + originSiteTitle: 'Siden', + originSiteDesc: 'Nærmeste node med et domæne', + originByKeyTitle: 'Specifik Node', + originByKeyDesc: 'Vælg en specifik Node', + pickDynamicRootQueryStepTitle: 'Tilføj skridt til forespørgsel', + pickDynamicRootQueryStepDesc: 'Specificer næste skridt i din dynamisk udgangspunkts forespørgsel', + queryStepNearestAncestorOrSelfTitle: 'Nærmeste forældre eller selv', + queryStepNearestAncestorOrSelfDesc: 'Forespørg the nærmeste forældre eller selv der passer på en af de givne typer', + queryStepFurthestAncestorOrSelfTitle: 'Fjerneste forældre eller selv', + queryStepFurthestAncestorOrSelfDesc: 'Forespørg fjerneste forældre eller selv der passer på en af de givne typer', + queryStepNearestDescendantOrSelfTitle: 'Nærmeste barn eller selv', + queryStepNearestDescendantOrSelfDesc: 'Forespørg nærmeste barn eller selv der passer på en af de givne typer', + queryStepFurthestDescendantOrSelfTitle: 'Fjerneste barn eller selv', + queryStepFurthestDescendantOrSelfDesc: 'Forespørg fjerneste barn eller selv der passer på en af de givne typer', + queryStepCustomTitle: 'Brugerdefineret', + queryStepCustomDesc: 'Forespørg med et skræddersyet forespørgsels skridt', + addQueryStep: 'Tilføj skridt', + queryStepTypes: 'der passer med typerne: ', + noValidStartNodeTitle: 'Intet passende indhold', + noValidStartNodeDesc: + 'Konfigurationen af dette felt passer ikke med noget indhold. Opret det manglende indhold eller kontakt din adminnistrator for at tilpasse Dynamisk Udgangspunkts Forespørgselen for dette felt.', + }, mediaPicker: { deletedItem: 'Slettet medie', pickedTrashedItem: 'Du har valgt et medie som er slettet eller lagt i papirkurven', diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts index 1c05c9a743..8a10ec8a7f 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts @@ -1148,11 +1148,46 @@ export default { }, contentPicker: { allowedItemTypes: 'You can only select items of type(s): %0%', - defineDynamicRoot: 'Specify a Dynamic Root', + defineDynamicRoot: 'Specify root node', defineRootNode: 'Pick root node', pickedTrashedItem: 'You have picked a content item currently deleted or in the recycle bin', pickedTrashedItems: 'You have picked content items currently deleted or in the recycle bin', }, + dynamicRoot: { + configurationTitle: 'Dynamic Root Query', + pickDynamicRootOriginTitle: 'Pick origin', + pickDynamicRootOriginDesc: 'Define the origin for your Dynamic Root Query', + originRootTitle: 'Root', + originRootDesc: 'Root node of this editing session', + originParentTitle: 'Parent', + originParentDesc: 'The parent node of the source in this editing session', + originCurrentTitle: 'Current', + originCurrentDesc: 'The content node that is source for this editing session', + originSiteTitle: 'Site', + originSiteDesc: 'Find nearest node with a hostname', + originByKeyTitle: 'Specific Node', + originByKeyDesc: 'Pick a specific Node as the origin for this query', + pickDynamicRootQueryStepTitle: 'Append step to query', + pickDynamicRootQueryStepDesc: 'Define the next step of your Dynamic Root Query', + queryStepNearestAncestorOrSelfTitle: 'Nearest Ancestor Or Self', + queryStepNearestAncestorOrSelfDesc: 'Query the nearest ancestor or self that fits with one of the configured types', + queryStepFurthestAncestorOrSelfTitle: 'Furthest Ancestor Or Self', + queryStepFurthestAncestorOrSelfDesc: + 'Query the Furthest ancestor or self that fits with one of the configured types', + queryStepNearestDescendantOrSelfTitle: 'Nearest Descendant Or Self', + queryStepNearestDescendantOrSelfDesc: + 'Query the nearest descendant or self that fits with one of the configured types', + queryStepFurthestDescendantOrSelfTitle: 'Furthest Descendant Or Self', + queryStepFurthestDescendantOrSelfDesc: + 'Query the Furthest descendant or self that fits with one of the configured types', + queryStepCustomTitle: 'Custom', + queryStepCustomDesc: 'Query the using a custom Query Step', + addQueryStep: 'Add query step', + queryStepTypes: 'That matches types: ', + noValidStartNodeTitle: 'No matching content', + noValidStartNodeDesc: + 'The configuration of this property does not match any content. Create the missing content or contact your administrator to adjust the Dynamic Root settings for this property.', + }, mediaPicker: { deletedItem: 'Deleted item', pickedTrashedItem: 'You have picked a media item currently deleted or in the recycle bin', diff --git a/src/Umbraco.Web.UI.Client/src/mocks/browser-handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/browser-handlers.ts index cde1bb1640..0cc2f7eb41 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/browser-handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/browser-handlers.ts @@ -36,6 +36,7 @@ import { handlers as partialViewHandlers } from './handlers/partial-view/index.j import { handlers as tagHandlers } from './handlers/tag-handlers.js'; import { handlers as configHandlers } from './handlers/config.handlers.js'; import { handlers as scriptHandlers } from './handlers/script/index.js'; +import { handlers as dynamicRootHandlers } from './handlers/dynamic-root.handlers.js'; const handlers = [ serverHandlers.serverInformationHandler, @@ -46,6 +47,7 @@ const handlers = [ ...dictionaryHandlers, ...documentHandlers, ...documentTypeHandlers, + ...dynamicRootHandlers, ...examineManagementHandlers, ...healthCheckHandlers, ...installHandlers, diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts index a9976a2bda..c7bb12cbcf 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts @@ -227,6 +227,15 @@ export const data: Array = [ value: { type: 'content', id: null, + dynamicRoot: { + originAlias: 'Root', + querySteps: [ + { + alias: 'FurthestAncestorOrSelf', + anyOfDocTypeKeys: ['all-property-editors-document-type-id'], + }, + ], + }, }, }, { diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/dynamic-root.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/dynamic-root.handlers.ts new file mode 100644 index 0000000000..c7c0072f06 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/dynamic-root.handlers.ts @@ -0,0 +1,15 @@ +import { umbDocumentMockDb } from '../data/document/document.db.js'; +import type { DynamicRootRequestModel } from '@umbraco-cms/backoffice/backend-api'; +import { umbracoPath } from '@umbraco-cms/backoffice/utils'; + +const { rest } = window.MockServiceWorker; + +export const handlers = [ + rest.post(umbracoPath('/dynamic-root/query'), async (req, res, ctx) => { + const response = umbDocumentMockDb.tree + .getRoot() + .items.map((item) => item.id) + .slice(0, 1); + return res(ctx.status(200), ctx.json(response)); + }), +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-tree-picker-source/input-tree-picker-source.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-tree-picker-source/input-tree-picker-source.element.ts index 79207bb441..c7337d6cac 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-tree-picker-source/input-tree-picker-source.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-tree-picker-source/input-tree-picker-source.element.ts @@ -6,21 +6,23 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; export type UmbTreePickerSource = { - type?: UmbTreePickerSourceType; - id?: string | null; - dynamicRoot?: UmbTreePickerDynamicRoot | null; + type: UmbTreePickerSourceType; + id?: string; + dynamicRoot?: UmbTreePickerDynamicRoot; }; export type UmbTreePickerSourceType = 'content' | 'member' | 'media'; export type UmbTreePickerDynamicRoot = { originAlias: string; - querySteps?: Array | null; + originKey?: string; + querySteps?: Array; }; export type UmbTreePickerDynamicRootQueryStep = { + unique: string; alias: string; - anyOfDocTypeKeys: Array; + anyOfDocTypeKeys?: Array; }; @customElement('umb-input-tree-picker-source') @@ -29,33 +31,33 @@ export class UmbInputTreePickerSourceElement extends FormControlMixin(UmbLitElem return undefined; } - private _type: UmbTreePickerSource['type'] = 'content'; + #type: UmbTreePickerSourceType = 'content'; @property() - public set type(value: UmbTreePickerSource['type']) { + public set type(value: UmbTreePickerSourceType) { if (value === undefined) { - value = this._type; + value = this.#type; } - const oldValue = this._type; + const oldValue = this.#type; this._options = this._options.map((option) => option.value === value ? { ...option, selected: true } : { ...option, selected: false }, ); - this._type = value; + this.#type = value; this.requestUpdate('type', oldValue); } - public get type(): UmbTreePickerSource['type'] { - return this._type; + public get type(): UmbTreePickerSourceType { + return this.#type; } @property({ attribute: 'node-id' }) - nodeId?: string | null; + nodeId?: string; @property({ attribute: false }) - dynamicRoot?: UmbTreePickerDynamicRoot | null; + dynamicRoot?: UmbTreePickerDynamicRoot | undefined; @state() _options: Array