diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 9a1764b619..b875d9b2dc 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -1,12 +1,12 @@ { "name": "@umbraco-cms/backoffice", - "version": "14.0.0--preview008", + "version": "14.0.0--beta001", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@umbraco-cms/backoffice", - "version": "14.0.0--preview008", + "version": "14.0.0--beta001", "license": "MIT", "dependencies": { "@openid/appauth": "^1.3.1", diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 9ad39c7abb..a5dee3f1d1 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -1,7 +1,7 @@ { "name": "@umbraco-cms/backoffice", "license": "MIT", - "version": "14.0.0--preview008", + "version": "14.0.0--beta001", "type": "module", "exports": { ".": null, diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extension-initializer.controller.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extension-initializer.controller.ts index 4effd65a63..8df56129a9 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extension-initializer.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extension-initializer.controller.ts @@ -244,9 +244,8 @@ export abstract class UmbBaseExtensionInitializer< const newPermission = await this._conditionsAreGood(); // Only set new permission if we are still positive, otherwise it means that we have been destroyed in the mean time. if (newPermission === false || this._isConditionsPositive === false) { - console.warn( - 'If this happens then please inform Niels Lyngsø on CMS Team. We are still investigating wether this is a situation we should handle. Ref. No.: 1.', - ); + // Then we need to revert the above work: + this._conditionsAreBad(); return; } // We update the oldValue as this point, cause in this way we are sure its the value at this point, when doing async code someone else might have changed the state in the mean time. @@ -259,9 +258,6 @@ export abstract class UmbBaseExtensionInitializer< // Only continue if we are still negative, otherwise it means that something changed in the mean time. if (this._isConditionsPositive === true) { - console.warn( - 'If this happens then please inform Niels Lyngsø on CMS Team. We are still investigating wether this is a situation we should handle. Ref. No.: 2.', - ); return; } // We update the oldValue as this point, cause in this way we are sure its the value at this point, when doing async code someone else might have changed the state in the mean time. diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-tree.manager.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-tree.manager.ts index 3405a88309..1f4a0ed427 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-tree.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-tree.manager.ts @@ -1,3 +1,4 @@ +import { pagedResult } from '../paged-result.js'; import type { UmbEntityMockDbBase } from './entity-base.js'; import { UmbId } from '@umbraco-cms/backoffice/id'; import type { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; @@ -11,18 +12,18 @@ export class UmbMockEntityTreeManager item.parent === null); - const treeItems = items.map((item) => this.#treeItemMapper(item)); - const total = items.length; - return { items: treeItems, total }; + const paged = pagedResult(items, skip, take); + const treeItems = paged.items.map((item) => this.#treeItemMapper(item)); + return { items: treeItems, total: paged.total }; } - getChildrenOf(parentId: string) { + getChildrenOf({ parentId, skip = 0, take = 100 }: { parentId: string; skip?: number; take?: number }) { const items = this.#db.getAll().filter((item) => item.parent?.id === parentId); - const treeItems = items.map((item) => this.#treeItemMapper(item)); - const total = items.length; - return { items: treeItems, total }; + const paged = pagedResult(items, skip, take); + const treeItems = paged.items.map((item) => this.#treeItemMapper(item)); + return { items: treeItems, total: paged.total }; } move(ids: Array, destinationId: string) { diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/utils/file-system/file-system-tree.manager.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/utils/file-system/file-system-tree.manager.ts index 05c243ce76..f1f2fe291a 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/utils/file-system/file-system-tree.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/utils/file-system/file-system-tree.manager.ts @@ -1,5 +1,6 @@ import type { UmbMockDBBase } from '../mock-db-base.js'; import { createFileSystemTreeItem } from '../../utils.js'; +import { pagedResult } from '../paged-result.js'; import type { FileSystemTreeItemPresentationModel } from '@umbraco-cms/backoffice/external/backend-api'; export class UmbMockFileSystemTreeManager> { @@ -9,20 +10,23 @@ export class UmbMockFileSystemTreeManager>; total: number } { + getRoot({ skip = 0, take = 100 }: { skip?: number; take?: number } = {}): { + items: Array>; + total: number; + } { const items = this.#db.getAll().filter((item) => item.parent === null); - const treeItems = items.map((item) => createFileSystemTreeItem(item)); - const total = items.length; - return { items: treeItems, total }; + const paged = pagedResult(items, skip, take); + const treeItems = paged.items.map((item) => createFileSystemTreeItem(item)); + return { items: treeItems, total: paged.total }; } - getChildrenOf(parentPath: string): { + getChildrenOf({ parentPath, skip = 0, take = 100 }: { parentPath: string; skip?: number; take?: number }): { items: Array>; total: number; } { const items = this.#db.getAll().filter((item) => item.parent?.path === parentPath); - const treeItems = items.map((item) => createFileSystemTreeItem(item)); - const total = items.length; - return { items: treeItems, total }; + const paged = pagedResult(items, skip, take); + const treeItems = paged.items.map((item) => createFileSystemTreeItem(item)); + return { items: treeItems, total: paged.total }; } } diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/data-type/tree.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/data-type/tree.handlers.ts index 6b40a709ad..7c3973e38b 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/data-type/tree.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/data-type/tree.handlers.ts @@ -5,14 +5,18 @@ import { umbracoPath } from '@umbraco-cms/backoffice/utils'; export const treeHandlers = [ rest.get(umbracoPath(`/tree${UMB_SLUG}/root`), (req, res, ctx) => { - const response = umbDataTypeMockDb.tree.getRoot(); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbDataTypeMockDb.tree.getRoot({ skip, take }); return res(ctx.status(200), ctx.json(response)); }), rest.get(umbracoPath(`/tree${UMB_SLUG}/children`), (req, res, ctx) => { const parentId = req.url.searchParams.get('parentId'); if (!parentId) return; - const response = umbDataTypeMockDb.tree.getChildrenOf(parentId); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbDataTypeMockDb.tree.getChildrenOf({ parentId, skip, take }); return res(ctx.status(200), ctx.json(response)); }), ]; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/dictionary/tree.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/dictionary/tree.handlers.ts index 22d46b92fd..6d79c28104 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/dictionary/tree.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/dictionary/tree.handlers.ts @@ -5,14 +5,18 @@ import { umbracoPath } from '@umbraco-cms/backoffice/utils'; export const treeHandlers = [ rest.get(umbracoPath(`/tree${UMB_SLUG}/root`), (req, res, ctx) => { - const response = umbDictionaryMockDb.tree.getRoot(); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbDictionaryMockDb.tree.getRoot({ skip, take }); return res(ctx.status(200), ctx.json(response)); }), rest.get(umbracoPath(`/tree${UMB_SLUG}/children`), (req, res, ctx) => { const parentId = req.url.searchParams.get('parentId'); if (!parentId) return; - const response = umbDictionaryMockDb.tree.getChildrenOf(parentId); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbDictionaryMockDb.tree.getChildrenOf({ parentId, skip, take }); return res(ctx.status(200), ctx.json(response)); }), ]; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/document-type/tree.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/document-type/tree.handlers.ts index 79b722f63c..9f8a2297ce 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/document-type/tree.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/document-type/tree.handlers.ts @@ -5,14 +5,18 @@ import { umbracoPath } from '@umbraco-cms/backoffice/utils'; export const treeHandlers = [ rest.get(umbracoPath(`/tree${UMB_SLUG}/root`), (req, res, ctx) => { - const response = umbDocumentTypeMockDb.tree.getRoot(); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbDocumentTypeMockDb.tree.getRoot({ skip, take }); return res(ctx.status(200), ctx.json(response)); }), rest.get(umbracoPath(`/tree${UMB_SLUG}/children`), (req, res, ctx) => { const parentId = req.url.searchParams.get('parentId'); if (!parentId) return; - const response = umbDocumentTypeMockDb.tree.getChildrenOf(parentId); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbDocumentTypeMockDb.tree.getChildrenOf({ parentId, skip, take }); return res(ctx.status(200), ctx.json(response)); }), ]; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/document/recycle-bin.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/document/recycle-bin.handlers.ts index 2b842595f7..4841e79474 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/document/recycle-bin.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/document/recycle-bin.handlers.ts @@ -5,14 +5,18 @@ import { umbracoPath } from '@umbraco-cms/backoffice/utils'; export const recycleBinHandlers = [ rest.get(umbracoPath(`/recycle-bin${UMB_SLUG}/root`), (req, res, ctx) => { - const response = umbDocumentMockDb.recycleBin.tree.getRoot(); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbDocumentMockDb.recycleBin.tree.getRoot({ skip, take }); return res(ctx.status(200), ctx.json(response)); }), rest.get(umbracoPath(`/recycle-bin${UMB_SLUG}/children`), (req, res, ctx) => { const parentId = req.url.searchParams.get('parentId'); if (!parentId) return; - const response = umbDocumentMockDb.recycleBin.tree.getChildrenOf(parentId); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbDocumentMockDb.recycleBin.tree.getChildrenOf({ parentId, skip, take }); return res(ctx.status(200), ctx.json(response)); }), diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/document/tree.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/document/tree.handlers.ts index e2a0aacc7e..e148fc83da 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/document/tree.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/document/tree.handlers.ts @@ -5,14 +5,18 @@ import { umbracoPath } from '@umbraco-cms/backoffice/utils'; export const treeHandlers = [ rest.get(umbracoPath(`/tree${UMB_SLUG}/root`), (req, res, ctx) => { - const response = umbDocumentMockDb.tree.getRoot(); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbDocumentMockDb.tree.getRoot({ skip, take }); return res(ctx.status(200), ctx.json(response)); }), rest.get(umbracoPath(`/tree${UMB_SLUG}/children`), (req, res, ctx) => { const parentId = req.url.searchParams.get('parentId'); if (!parentId) return; - const response = umbDocumentMockDb.tree.getChildrenOf(parentId); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbDocumentMockDb.tree.getChildrenOf({ parentId, skip, take }); return res(ctx.status(200), ctx.json(response)); }), ]; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/media-type/tree.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/media-type/tree.handlers.ts index 335f3d1728..2d113dee35 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/media-type/tree.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/media-type/tree.handlers.ts @@ -5,14 +5,18 @@ import { umbracoPath } from '@umbraco-cms/backoffice/utils'; export const treeHandlers = [ rest.get(umbracoPath(`/tree${UMB_SLUG}/root`), (req, res, ctx) => { - const response = umbMediaTypeMockDb.tree.getRoot(); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbMediaTypeMockDb.tree.getRoot({ skip, take }); return res(ctx.status(200), ctx.json(response)); }), rest.get(umbracoPath(`/tree${UMB_SLUG}/children`), (req, res, ctx) => { const parentId = req.url.searchParams.get('parentId'); if (!parentId) return; - const response = umbMediaTypeMockDb.tree.getChildrenOf(parentId); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbMediaTypeMockDb.tree.getChildrenOf({ parentId, skip, take }); return res(ctx.status(200), ctx.json(response)); }), ]; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/media/recycle-bin.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/media/recycle-bin.handlers.ts index edc15eff9d..cf260a2912 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/media/recycle-bin.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/media/recycle-bin.handlers.ts @@ -5,14 +5,18 @@ import { umbracoPath } from '@umbraco-cms/backoffice/utils'; export const recycleBinHandlers = [ rest.get(umbracoPath(`/recycle-bin${UMB_SLUG}/root`), (req, res, ctx) => { - const response = umbMediaMockDb.recycleBin.tree.getRoot(); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbMediaMockDb.recycleBin.tree.getRoot({ skip, take }); return res(ctx.status(200), ctx.json(response)); }), rest.get(umbracoPath(`/recycle-bin${UMB_SLUG}/children`), (req, res, ctx) => { const parentId = req.url.searchParams.get('parentId'); if (!parentId) return; - const response = umbMediaMockDb.recycleBin.tree.getChildrenOf(parentId); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbMediaMockDb.recycleBin.tree.getChildrenOf({ parentId, skip, take }); return res(ctx.status(200), ctx.json(response)); }), diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/media/tree.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/media/tree.handlers.ts index b57b17e3a1..308c04be52 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/media/tree.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/media/tree.handlers.ts @@ -5,14 +5,18 @@ import { umbracoPath } from '@umbraco-cms/backoffice/utils'; export const treeHandlers = [ rest.get(umbracoPath(`/tree${UMB_SLUG}/root`), (req, res, ctx) => { - const response = umbMediaMockDb.tree.getRoot(); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbMediaMockDb.tree.getRoot({ skip, take }); return res(ctx.status(200), ctx.json(response)); }), rest.get(umbracoPath(`/tree${UMB_SLUG}/children`), (req, res, ctx) => { const parentId = req.url.searchParams.get('parentId'); if (!parentId) return; - const response = umbMediaMockDb.tree.getChildrenOf(parentId); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbMediaMockDb.tree.getChildrenOf({ parentId, skip, take }); return res(ctx.status(200), ctx.json(response)); }), ]; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/member-type/tree.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/member-type/tree.handlers.ts index 78d543c445..0aef1a0996 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/member-type/tree.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/member-type/tree.handlers.ts @@ -5,14 +5,18 @@ import { umbracoPath } from '@umbraco-cms/backoffice/utils'; export const treeHandlers = [ rest.get(umbracoPath(`/tree${UMB_SLUG}/root`), (req, res, ctx) => { - const response = umbMemberTypeMockDb.tree.getRoot(); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbMemberTypeMockDb.tree.getRoot({ skip, take }); return res(ctx.status(200), ctx.json(response)); }), rest.get(umbracoPath(`/tree${UMB_SLUG}/children`), (req, res, ctx) => { const parentId = req.url.searchParams.get('parentId'); if (!parentId) return; - const response = umbMemberTypeMockDb.tree.getChildrenOf(parentId); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbMemberTypeMockDb.tree.getChildrenOf({ parentId, skip, take }); return res(ctx.status(200), ctx.json(response)); }), ]; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/partial-view/tree.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/partial-view/tree.handlers.ts index 8848ec8be6..6838f10ca6 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/partial-view/tree.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/partial-view/tree.handlers.ts @@ -5,14 +5,18 @@ import { umbracoPath } from '@umbraco-cms/backoffice/utils'; export const treeHandlers = [ rest.get(umbracoPath(`/tree${UMB_SLUG}/root`), (req, res, ctx) => { - const response = umbPartialViewMockDB.tree.getRoot(); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbPartialViewMockDB.tree.getRoot({ skip, take }); return res(ctx.status(200), ctx.json(response)); }), rest.get(umbracoPath(`/tree${UMB_SLUG}/children`), (req, res, ctx) => { const parentPath = req.url.searchParams.get('parentPath'); if (!parentPath) return res(ctx.status(400)); - const response = umbPartialViewMockDB.tree.getChildrenOf(parentPath); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbPartialViewMockDB.tree.getChildrenOf({ parentPath, skip, take }); return res(ctx.status(200), ctx.json(response)); }), ]; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/script/tree.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/script/tree.handlers.ts index 89972b3c6e..216321e50f 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/script/tree.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/script/tree.handlers.ts @@ -5,14 +5,18 @@ import { umbracoPath } from '@umbraco-cms/backoffice/utils'; export const treeHandlers = [ rest.get(umbracoPath(`/tree${UMB_SLUG}/root`), (req, res, ctx) => { - const response = umbScriptMockDb.tree.getRoot(); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbScriptMockDb.tree.getRoot({ skip, take }); return res(ctx.status(200), ctx.json(response)); }), rest.get(umbracoPath(`/tree${UMB_SLUG}/children`), (req, res, ctx) => { const parentPath = req.url.searchParams.get('parentPath'); if (!parentPath) return res(ctx.status(400)); - const response = umbScriptMockDb.tree.getChildrenOf(parentPath); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbScriptMockDb.tree.getChildrenOf({ parentPath, skip, take }); return res(ctx.status(200), ctx.json(response)); }), ]; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/static-file/tree.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/static-file/tree.handlers.ts index ca0a038895..0568111018 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/static-file/tree.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/static-file/tree.handlers.ts @@ -5,14 +5,18 @@ import { umbracoPath } from '@umbraco-cms/backoffice/utils'; export const treeHandlers = [ rest.get(umbracoPath(`/tree${UMB_SLUG}/root`), (req, res, ctx) => { - const response = umbStaticFileMockDb.tree.getRoot(); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbStaticFileMockDb.tree.getRoot({ skip, take }); return res(ctx.status(200), ctx.json(response)); }), rest.get(umbracoPath(`/tree${UMB_SLUG}/children`), (req, res, ctx) => { const parentPath = req.url.searchParams.get('parentPath'); if (!parentPath) return res(ctx.status(400)); - const response = umbStaticFileMockDb.tree.getChildrenOf(parentPath); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbStaticFileMockDb.tree.getChildrenOf({ parentPath, skip, take }); return res(ctx.status(200), ctx.json(response)); }), ]; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/stylesheet/tree.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/stylesheet/tree.handlers.ts index 7441982c84..760a43d9c3 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/stylesheet/tree.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/stylesheet/tree.handlers.ts @@ -5,14 +5,18 @@ import { umbracoPath } from '@umbraco-cms/backoffice/utils'; export const treeHandlers = [ rest.get(umbracoPath(`/tree${UMB_SLUG}/root`), (req, res, ctx) => { - const response = umbStylesheetMockDb.tree.getRoot(); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbStylesheetMockDb.tree.getRoot({ skip, take }); return res(ctx.status(200), ctx.json(response)); }), rest.get(umbracoPath(`/tree${UMB_SLUG}/children`), (req, res, ctx) => { const parentPath = req.url.searchParams.get('parentPath'); if (!parentPath) return res(ctx.status(400)); - const response = umbStylesheetMockDb.tree.getChildrenOf(parentPath); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbStylesheetMockDb.tree.getChildrenOf({ parentPath, skip, take }); return res(ctx.status(200), ctx.json(response)); }), ]; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/template/tree.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/template/tree.handlers.ts index 813d7432dd..d177d705fd 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/template/tree.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/template/tree.handlers.ts @@ -5,14 +5,18 @@ import { umbracoPath } from '@umbraco-cms/backoffice/utils'; export const treeHandlers = [ rest.get(umbracoPath(`/tree${UMB_SLUG}/root`), (req, res, ctx) => { - const response = umbTemplateMockDb.tree.getRoot(); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbTemplateMockDb.tree.getRoot({ skip, take }); return res(ctx.status(200), ctx.json(response)); }), rest.get(umbracoPath(`/tree${UMB_SLUG}/children`), (req, res, ctx) => { const parentId = req.url.searchParams.get('parentId'); if (!parentId) return; - const response = umbTemplateMockDb.tree.getChildrenOf(parentId); + const skip = Number(req.url.searchParams.get('skip')); + const take = Number(req.url.searchParams.get('take')); + const response = umbTemplateMockDb.tree.getChildrenOf({ parentId, skip, take }); return res(ctx.status(200), ctx.json(response)); }), ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/block-grid-area-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/block-grid-area-type-workspace.context.ts index 83ab39bc2f..305508d2b5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/block-grid-area-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/block-grid-area-type-workspace.context.ts @@ -119,7 +119,8 @@ export class UmbBlockGridAreaTypeWorkspaceContext context.setValue(appendToFrozenArray(context.getValue() ?? [], this.#data.getValue(), (x) => x?.key)); }); - this.saveComplete(this.#data.value); + this.setIsNew(false); + this.workspaceComplete(this.#data.value); } public destroy(): void { diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/manifests.ts index 73b05e3f9a..cd6fc0512d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/manifests.ts @@ -14,6 +14,11 @@ export const manifest: ManifestPropertyEditorUi = { group: 'richContent', settings: { properties: [ + { + alias: 'blockGroups', + label: '', + propertyEditorUiAlias: 'Umb.PropertyEditorUi.BlockTypeGroupConfiguration', + }, { alias: 'useLiveEditing', label: 'Live editing mode', diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-type-configuration/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-type-configuration/manifests.ts index c8f4c0ed73..17bf63c9ec 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-type-configuration/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-type-configuration/manifests.ts @@ -7,7 +7,6 @@ export const manifest: ManifestPropertyEditorUi = { js: () => import('./property-editor-ui-block-grid-type-configuration.element.js'), meta: { label: 'Block Grid Block Configuration', - propertyEditorSchemaAlias: 'Umbraco.BlockGrid.BlockConfiguration', icon: 'icon-autofill', group: 'blocks', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-type-configuration/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-type-configuration/manifests.ts index 6f702cbdeb..d94806f492 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-type-configuration/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-type-configuration/manifests.ts @@ -7,7 +7,6 @@ export const manifest: ManifestPropertyEditorUi = { js: () => import('./property-editor-ui-block-list-type-configuration.element.js'), meta: { label: 'Block List Type Configuration', - propertyEditorSchemaAlias: '', icon: 'icon-autofill', group: 'common', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/property-editors/block-type-group-configuration/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/property-editors/block-type-group-configuration/manifests.ts index 36f42bea54..8160246fa7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/property-editors/block-type-group-configuration/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/property-editors/block-type-group-configuration/manifests.ts @@ -7,7 +7,6 @@ export const manifest: ManifestPropertyEditorUi = { js: () => import('./property-editor-ui-block-type-group-configuration.element.js'), meta: { label: 'Block Grid Group Configuration', - propertyEditorSchemaAlias: 'Umbraco.BlockGrid.GroupConfiguration', icon: 'icon-autofill', group: 'blocks', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context.ts index 74f35a0bcc..791af6f084 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context.ts @@ -121,7 +121,8 @@ export class UmbBlockTypeWorkspaceContext { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/collection/components/pagination/collection-pagination.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/collection/components/pagination/collection-pagination.element.ts index e30a056762..7b128ac0db 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/collection/components/pagination/collection-pagination.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/collection/components/pagination/collection-pagination.element.ts @@ -8,7 +8,7 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; @customElement('umb-collection-pagination') export class UmbCollectionPaginationElement extends UmbLitElement { @state() - _totalPages = 0; + _totalPages = 1; @state() _currentPage = 1; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.element.ts index d97aa8407a..929fb667ac 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.element.ts @@ -1,5 +1,6 @@ import { UMB_DEFAULT_COLLECTION_CONTEXT, UmbDefaultCollectionContext } from './collection-default.context.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import type { UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; @@ -35,6 +36,11 @@ export class UmbCollectionDefaultElement extends UmbLitElement { }); } + protected firstUpdated(_changedProperties: PropertyValueMap | Map): void { + super.firstUpdated(_changedProperties); + this.#collectionContext?.requestCollection(); + } + #observeCollectionRoutes() { if (!this.#collectionContext) return; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-number-range/input-number-range.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-number-range/input-number-range.element.ts index d22ec8280d..3a5c17e825 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-number-range/input-number-range.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-number-range/input-number-range.element.ts @@ -1,4 +1,4 @@ -import { css, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; +import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; @@ -17,25 +17,27 @@ export class UmbInputNumberRangeElement extends FormControlMixin(UmbLitElement) @state() private _minValue?: number; - @property() - public get minValue() { - return this._minValue; - } + + @property({ type: Number }) public set minValue(value: number | undefined) { this._minValue = value; this.updateValue(); } + public get minValue() { + return this._minValue; + } @state() private _maxValue?: number; - @property() - public get maxValue() { - return this._maxValue; - } + + @property({ type: Number }) public set maxValue(value: number | undefined) { this._maxValue = value; this.updateValue(); } + public get maxValue() { + return this._maxValue; + } private updateValue() { const newValue = this._minValue || this._maxValue ? (this._minValue || '') + ',' + (this._maxValue || '') : ''; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-color-picker-input/multiple-color-picker-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-color-picker-input/multiple-color-picker-input.element.ts index 261d39ef7a..619a58c0cc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-color-picker-input/multiple-color-picker-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-color-picker-input/multiple-color-picker-input.element.ts @@ -1,32 +1,21 @@ import type { UmbMultipleColorPickerItemInputElement } from './multiple-color-picker-item-input.element.js'; -import type { UmbSwatchDetails } from '@umbraco-cms/backoffice/models'; import { css, + customElement, html, + ifDefined, nothing, repeat, - customElement, property, state, - ifDefined, } from '@umbraco-cms/backoffice/external/lit'; import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui'; -import { type UmbInputEvent, UmbChangeEvent, type UmbDeleteEvent } from '@umbraco-cms/backoffice/event'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { type UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter'; +import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property'; - -const SORTER_CONFIG: UmbSorterConfig = { - getUniqueOfElement: (element) => { - return element.value.toString(); - }, - getUniqueOfModel: (modelEntry) => { - return modelEntry.value; - }, - identifier: 'Umb.SorterIdentifier.ColorEditor', - itemSelector: 'umb-multiple-color-picker-item-input', - containerSelector: '#sorter-wrapper', -}; +import type { UmbInputEvent, UmbDeleteEvent } from '@umbraco-cms/backoffice/event'; +import type { UmbSwatchDetails } from '@umbraco-cms/backoffice/models'; /** * @element umb-multiple-color-picker-input @@ -34,7 +23,15 @@ const SORTER_CONFIG: UmbSorterConfig { + return element.value.toString(); + }, + getUniqueOfModel: (modelEntry: UmbSwatchDetails) => { + return modelEntry.value; + }, + identifier: 'Umb.SorterIdentifier.ColorEditor', + itemSelector: 'umb-multiple-color-picker-item-input', + containerSelector: '#sorter-wrapper', onChange: ({ model }) => { const oldValue = this._items; this._items = model; @@ -194,7 +191,6 @@ export class UmbMultipleColorPickerInputElement extends FormControlMixin(UmbLitE ?showLabels=${this.showLabels} value=${item.value} label=${ifDefined(item.label)} - name="item-${index}" @change=${(event: UmbChangeEvent) => this.#onChange(event, index)} @delete="${(event: UmbDeleteEvent) => this.#deleteItem(event, index)}" ?disabled=${this.disabled} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-text-string-input/input-multiple-text-string.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-text-string-input/input-multiple-text-string.element.ts index 996b992a0b..93b381c130 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-text-string-input/input-multiple-text-string.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-text-string-input/input-multiple-text-string.element.ts @@ -1,37 +1,26 @@ import type { UmbInputMultipleTextStringItemElement } from './input-multiple-text-string-item.element.js'; import { css, html, nothing, repeat, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui'; -import type { UmbInputEvent, UmbDeleteEvent } from '@umbraco-cms/backoffice/event'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { UmbSorterConfig } from '@umbraco-cms/backoffice/sorter'; import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; - -export type MultipleTextStringValue = Array; - -export interface MultipleTextStringValueItem { - value: string; -} - -const SORTER_CONFIG: UmbSorterConfig = { - getUniqueOfElement: (element) => { - return element.getAttribute('data-sort-entry-id'); - }, - getUniqueOfModel: (modelEntry) => { - return modelEntry.value; - }, - identifier: 'Umb.SorterIdentifier.ColorEditor', - itemSelector: 'umb-input-multiple-text-string-item', - containerSelector: '#sorter-wrapper', -}; +import type { UmbInputEvent, UmbDeleteEvent } from '@umbraco-cms/backoffice/event'; /** * @element umb-input-multiple-text-string */ @customElement('umb-input-multiple-text-string') export class UmbInputMultipleTextStringElement extends FormControlMixin(UmbLitElement) { - #prevalueSorter = new UmbSorterController(this, { - ...SORTER_CONFIG, + #sorter = new UmbSorterController(this, { + getUniqueOfElement: (element) => { + return element.getAttribute('data-sort-entry-id'); + }, + getUniqueOfModel: (modelEntry: string) => { + return modelEntry; + }, + identifier: 'Umb.SorterIdentifier.ColorEditor', + itemSelector: 'umb-input-multiple-text-string-item', + containerSelector: '#sorter-wrapper', onChange: ({ model }) => { const oldValue = this._items; this._items = model; @@ -120,17 +109,17 @@ export class UmbInputMultipleTextStringElement extends FormControlMixin(UmbLitEl } @state() - private _items: MultipleTextStringValue = []; + private _items: Array = []; @property({ type: Array }) - public get items(): MultipleTextStringValue { + public get items(): Array { return this._items; } - public set items(items: MultipleTextStringValue) { + public set items(items: Array) { // TODO: when we have a way to overwrite the missing value validator we can remove this this.value = items?.length > 0 ? 'some value' : ''; this._items = items ?? []; - this.#prevalueSorter.setModel(this.items); + this.#sorter.setModel(this.items); } // TODO: Some inputs might not have a value that is either FormDataEntryValue or FormData. @@ -146,7 +135,7 @@ export class UmbInputMultipleTextStringElement extends FormControlMixin(UmbLitEl */ #onAdd() { - this._items = [...this._items, { value: '' }]; + this._items = [...this._items, '']; this.pristine = false; this.dispatchEvent(new UmbChangeEvent()); this.#focusNewItem(); @@ -156,7 +145,7 @@ export class UmbInputMultipleTextStringElement extends FormControlMixin(UmbLitEl 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._items = this._items.map((item, index) => (index === currentIndex ? value : item)); this.dispatchEvent(new UmbChangeEvent()); } @@ -192,9 +181,9 @@ export class UmbInputMultipleTextStringElement extends FormControlMixin(UmbLitEl (item, index) => index, (item, index) => html` this.#onInput(event, index)} @delete="${(event: UmbDeleteEvent) => this.#deleteItem(event, index)}" ?disabled=${this.disabled} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/common/delete/delete.action.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/common/delete/delete.action.ts index 649931e37d..17e7a9fbf7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/common/delete/delete.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/common/delete/delete.action.ts @@ -18,6 +18,7 @@ export class UmbDeleteEntityAction< color: 'danger', confirmLabel: 'Delete', }); + await this.repository?.delete(this.unique); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/collection/extension-collection.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/collection/extension-collection.element.ts index 5fc7239181..df8289d9a6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/collection/extension-collection.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/collection/extension-collection.element.ts @@ -27,6 +27,7 @@ export class UmbExtensionCollectionElement extends UmbCollectionDefaultElement { }); } + // TODO: make this a utility function, please check that we do not already have on for this: [NL] // credit: https://stackoverflow.com/a/7225450/12787 [LK] #camelCaseToWords(input: string) { const result = input.replace(/([A-Z])/g, ' $1'); @@ -35,7 +36,6 @@ export class UmbExtensionCollectionElement extends UmbCollectionDefaultElement { #onChange(event: UUISelectEvent) { const extensionType = event.target.value; - console.log('onChange', extensionType); this.#collectionContext?.setFilter({ type: extensionType }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/extension-initializer-element-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/extension-initializer-element-base.ts new file mode 100644 index 0000000000..76135380e6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/extension-initializer-element-base.ts @@ -0,0 +1,88 @@ +import { umbExtensionsRegistry } from './registry.js'; +import { html, property, state } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import type { ManifestWithDynamicConditions } from '@umbraco-cms/backoffice/extension-api'; +import { UmbExtensionElementInitializer, createExtensionApi } from '@umbraco-cms/backoffice/extension-api'; + +// TODO: Eslint: allow abstract element class to end with "ElementBase" instead of "Element" +// eslint-disable-next-line local-rules/enforce-element-suffix-on-element-class-name +export abstract class UmbExtensionInitializerElementBase< + ManifestType extends ManifestWithDynamicConditions, +> extends UmbLitElement { + _alias?: string; + @property({ type: String, reflect: true }) + get alias() { + return this._alias; + } + set alias(newVal) { + this._alias = newVal; + this.#observeManifest(); + } + + @property({ type: Object, attribute: false }) + get props() { + return this.#props; + } + set props(newVal: Record | undefined) { + // TODO, compare changes since last time. only reset the ones that changed. This might be better done by the controller is self: + this.#props = newVal; + if (this.#extensionElementController) { + this.#extensionElementController.properties = newVal; + } + } + #props?: Record = {}; + + #extensionElementController?: UmbExtensionElementInitializer; + + @state() + _element: HTMLElement | undefined; + + abstract getExtensionType(): string; + abstract getDefaultElementName(): string; + + #observeManifest() { + if (!this._alias) return; + this.observe( + umbExtensionsRegistry.byTypeAndAlias(this.getExtensionType(), this._alias), + async (m) => { + if (!m) return; + const manifest = m as unknown as ManifestType; + this.createApi(manifest); + this.createElement(manifest); + }, + 'umbObserveTreeManifest', + ); + } + + protected async createApi(manifest?: ManifestType) { + if (!manifest) throw new Error('No manifest'); + const api = (await createExtensionApi(manifest, [this])) as unknown as any; + if (!api) throw new Error('No api'); + api.setManifest(manifest); + } + + protected async createElement(manifest?: ManifestType) { + if (!manifest) throw new Error('No manifest'); + + const extController = new UmbExtensionElementInitializer( + this, + umbExtensionsRegistry, + manifest.alias, + this.#extensionChanged, + this.getDefaultElementName(), + ); + + extController.properties = this.#props; + + this.#extensionElementController = extController; + } + + #extensionChanged = (isPermitted: boolean, controller: UmbExtensionElementInitializer) => { + this._element = isPermitted ? controller.component : undefined; + this.requestUpdate('_element'); + }; + + render() { + return html`${this._element}`; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/index.ts index 0d8fb0ff4d..964c344673 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/index.ts @@ -2,3 +2,5 @@ export * from './conditions/index.js'; export * from './interfaces/index.js'; export * from './models/index.js'; export * from './registry.js'; + +export { UmbExtensionInitializerElementBase } from './extension-initializer-element-base.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/index.ts index 3e14e3ad9a..1721b7dfec 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/index.ts @@ -6,5 +6,4 @@ export * from './property-editor-ui-element.interface.js'; export * from './section-element.interface.js'; export * from './section-sidebar-app-element.interface.js'; export * from './section-view-element.interface.js'; -export * from './tree-item-element.interface.js'; export * from './workspace-view-element.interface.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/tree-item-element.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/tree-item-element.interface.ts deleted file mode 100644 index d109e0bacd..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/tree-item-element.interface.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { UmbTreeItemModelBase } from '@umbraco-cms/backoffice/tree'; - -export interface UmbTreeItemElement extends HTMLElement { - item?: UmbTreeItemModelBase; -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/tree-item.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/tree-item.model.ts index 0f30b9dec6..aff81bc5d9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/tree-item.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/tree-item.model.ts @@ -1,7 +1,7 @@ -import type { UmbTreeItemElement } from '../interfaces/index.js'; -import type { ManifestElement } from '@umbraco-cms/backoffice/extension-api'; +import type { UmbTreeItemContext, UmbTreeItemModelBase } from '../../index.js'; +import type { ManifestElementAndApi } from '@umbraco-cms/backoffice/extension-api'; -export interface ManifestTreeItem extends ManifestElement { +export interface ManifestTreeItem extends ManifestElementAndApi> { type: 'treeItem'; meta: MetaTreeItem; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/tree.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/tree.model.ts index ffcc2bf64d..333784eb4f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/tree.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/tree.model.ts @@ -1,6 +1,6 @@ -import type { ManifestBase } from '@umbraco-cms/backoffice/extension-api'; +import type { ManifestElementAndApi } from '@umbraco-cms/backoffice/extension-api'; -export interface ManifestTree extends ManifestBase { +export interface ManifestTree extends ManifestElementAndApi { type: 'tree'; meta: MetaTree; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.controller.ts index 191cbbd410..a419a132a9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.controller.ts @@ -13,9 +13,11 @@ export class UmbConfirmModalController extends UmbControllerBase { data: args, }); - await modalContext.onSubmit().catch(() => { + const p = modalContext.onSubmit(); + p.catch(() => { this.destroy(); }); + await p; // This is a one time off, so we can destroy our selfs. this.destroy(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/link-picker/link-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/link-picker/link-picker-modal.element.ts index 50ada2ee67..5f7de072f6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/link-picker/link-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/link-picker/link-picker-modal.element.ts @@ -98,6 +98,7 @@ export class UmbLinkPickerModalElement extends UmbModalBaseElement this.#handleSelectionChange(event, 'document')} - .selectionConfiguration=${this._selectionConfiguration}> + .props=${{ + hideTreeRoot: true, + selectionConfiguration: this._selectionConfiguration, + }} + @selection-change=${(event: CustomEvent) => this.#handleSelectionChange(event, 'document')}>
${this.localize.term('defaultdialogs_linkToMedia')}
this.#handleSelectionChange(event, 'media')} - .selectionConfiguration=${this._selectionConfiguration}> + .props=${{ + hideTreeRoot: true, + selectionConfiguration: this._selectionConfiguration, + }} + @selection-change=${(event: CustomEvent) => this.#handleSelectionChange(event, 'media')}>
`; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/manifests.ts index 3728ed4d0d..1252ac2c69 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/manifests.ts @@ -43,12 +43,6 @@ const modals: Array = [ name: 'Embedded Media Modal', js: () => import('./embedded-media/embedded-media-modal.element.js'), }, - { - type: 'modal', - alias: 'Umb.Modal.TreePicker', - name: 'Tree Picker Modal', - js: () => import('./tree-picker/tree-picker-modal.element.js'), - }, ]; export const manifests = [...modals]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/property-settings/property-settings-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/property-settings/property-settings-modal.element.ts index d973b5bbf2..832bd3cdc0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/property-settings/property-settings-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/property-settings/property-settings-modal.element.ts @@ -6,7 +6,7 @@ import { UMB_DOCUMENT_TYPE_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/doc import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UUIBooleanInputEvent, UUIInputEvent, UUISelectEvent } from '@umbraco-cms/backoffice/external/uui'; import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; -import { css, html, nothing, customElement, state } from '@umbraco-cms/backoffice/external/lit'; +import { css, html, nothing, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import type { UmbPropertySettingsModalValue, UmbPropertySettingsModalData } from '@umbraco-cms/backoffice/modal'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; import { generateAlias } from '@umbraco-cms/backoffice/utils'; @@ -234,6 +234,7 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement< @@ -259,7 +260,7 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement< .value=${this.value.description}>
diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.BlockGrid.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.BlockGrid.ts index fb5d4ec47b..98a32721fb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.BlockGrid.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.BlockGrid.ts @@ -14,11 +14,6 @@ export const manifest: ManifestPropertyEditorSchema = { description: 'Define Blocks based on Element Types.', propertyEditorUiAlias: 'Umb.PropertyEditorUi.BlockGridTypeConfiguration', }, - { - alias: 'blockGroups', - label: '', - propertyEditorUiAlias: 'Umb.PropertyEditorUi.BlockTypeGroupConfiguration', - }, { alias: 'validationLimit', label: 'Amount', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.Dropdown.Flexible.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.Dropdown.Flexible.ts index c9fb91ab8b..98d4b1ff7d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.Dropdown.Flexible.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.Dropdown.Flexible.ts @@ -7,5 +7,19 @@ export const manifest: ManifestPropertyEditorSchema = { alias: 'Umbraco.DropDown.Flexible', meta: { defaultPropertyEditorUiAlias: 'Umb.PropertyEditorUi.Dropdown', + settings: { + properties: [ + { + alias: 'multiple', + label: 'Enable multiple choice', + propertyEditorUiAlias: 'Umb.PropertyEditorUi.Toggle', + }, + { + alias: 'items', + label: 'Add options', + propertyEditorUiAlias: 'Umb.PropertyEditorUi.MultipleTextString', + }, + ], + }, }, }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.RadioButtonList.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.RadioButtonList.ts index 0f7b7db6c9..75af2e3224 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.RadioButtonList.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.RadioButtonList.ts @@ -6,5 +6,15 @@ export const manifest: ManifestPropertyEditorSchema = { alias: 'Umbraco.RadioButtonList', meta: { defaultPropertyEditorUiAlias: 'Umb.PropertyEditorUi.RadioButtonList', + settings: { + properties: [ + { + alias: 'items', + label: 'Add option', + description: 'Add, remove or sort options for the list.', + propertyEditorUiAlias: 'Umb.PropertyEditorUi.MultipleTextString', + }, + ], + }, }, }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.RichText.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.RichText.ts index d13575c27f..d74d2e1a9e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.RichText.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.RichText.ts @@ -12,13 +12,20 @@ export const manifest: ManifestPropertyEditorSchema = { alias: 'mediaParentId', label: 'Image Upload Folder', description: 'Choose the upload location of pasted images', - propertyEditorUiAlias: 'Umb.PropertyEditorUi.TreePicker', + propertyEditorUiAlias: 'Umb.PropertyEditorUi.MediaPicker', + config: [{ alias: 'validationLimit', value: { min: 0, max: 1 } }], }, { alias: 'ignoreUserStartNodes', label: 'Ignore User Start Nodes', propertyEditorUiAlias: 'Umb.PropertyEditorUi.Toggle', }, + { + alias: 'blocks', + label: 'Available Blocks', + description: 'Define the available blocks.', + propertyEditorUiAlias: 'Umb.PropertyEditorUi.BlockRteTypeConfiguration', + }, ], }, }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.TrueFalse.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.TrueFalse.ts index faa04eaf19..78ff015221 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.TrueFalse.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.TrueFalse.ts @@ -6,6 +6,6 @@ export const manifest: ManifestPropertyEditorSchema = { name: 'Date/Time', alias: 'Umbraco.TrueFalse', meta: { - defaultPropertyEditorUiAlias: 'Umb.PropertyEditorUi.TrueFalse', + defaultPropertyEditorUiAlias: 'Umb.PropertyEditorUi.Toggle', }, }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts index 23c065b5dc..f0fda71aaa 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts @@ -7,7 +7,6 @@ export const manifest: ManifestPropertyEditorUi = { element: () => import('./property-editor-ui-collection-view-bulk-action-permissions.element.js'), meta: { label: 'Collection View Bulk Action Permissions', - propertyEditorSchemaAlias: '', icon: 'icon-autofill', group: 'lists', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/manifests.ts index 2dca3ff796..d06c7f1ade 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/manifests.ts @@ -7,7 +7,6 @@ export const manifest: ManifestPropertyEditorUi = { element: () => import('./property-editor-ui-collection-view-column-configuration.element.js'), meta: { label: 'Collection View Column Configuration', - propertyEditorSchemaAlias: '', icon: 'icon-autofill', group: 'lists', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/manifests.ts index ac838969fb..411d187f3d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/manifests.ts @@ -7,7 +7,6 @@ export const manifest: ManifestPropertyEditorUi = { element: () => import('./property-editor-ui-collection-view-layout-configuration.element.js'), meta: { label: 'Collection View Layout Configuration', - propertyEditorSchemaAlias: '', icon: 'icon-autofill', group: 'lists', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/property-editor-ui-collection-view-layout-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/property-editor-ui-collection-view-layout-configuration.element.ts index ea49184b4a..b20d40cee9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/property-editor-ui-collection-view-layout-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/property-editor-ui-collection-view-layout-configuration.element.ts @@ -26,7 +26,7 @@ export class UmbPropertyEditorUICollectionViewLayoutConfigurationElement implements UmbPropertyEditorUiElement { @property({ type: Array }) - value: Array = []; + value?: Array; @property({ type: Object, attribute: false }) public config?: UmbPropertyEditorConfigCollection; @@ -41,47 +41,47 @@ export class UmbPropertyEditorUICollectionViewLayoutConfigurationElement } #onAdd() { - this.value = [...this.value, { isSystem: false, icon: 'icon-stop', selected: true }]; + this.value = [...(this.value ?? []), { isSystem: false, icon: 'icon-stop', selected: true }]; this.dispatchEvent(new UmbPropertyValueChangeEvent()); } #onRemove(unique: number) { - const values = [...this.value]; + const values = [...(this.value ?? [])]; values.splice(unique, 1); this.value = values; this.dispatchEvent(new UmbPropertyValueChangeEvent()); } #onChangePath(e: UUIInputEvent, index: number) { - const values = [...this.value]; + const values = [...(this.value ?? [])]; values[index] = { ...values[index], path: e.target.value as string }; this.value = values; this.dispatchEvent(new UmbPropertyValueChangeEvent()); } #onChangeName(e: UUIInputEvent, index: number) { - const values = [...this.value]; + const values = [...(this.value ?? [])]; values[index] = { ...values[index], name: e.target.value as string }; this.value = values; this.dispatchEvent(new UmbPropertyValueChangeEvent()); } #onChangeSelected(e: UUIBooleanInputEvent, index: number) { - const values = [...this.value]; + const values = [...(this.value ?? [])]; values[index] = { ...values[index], selected: e.target.checked }; this.value = values; this.dispatchEvent(new UmbPropertyValueChangeEvent()); } async #onIconChange(index: number) { - const icon = this.#iconReader(this.value[index].icon ?? ''); + const icon = this.#iconReader((this.value ? this.value[index].icon : undefined) ?? ''); // TODO: send icon data to modal const modalContext = this._modalContext?.open(UMB_ICON_PICKER_MODAL); const picked = await modalContext?.onSubmit(); if (!picked) return; - const values = [...this.value]; + const values = [...(this.value ?? [])]; values[index] = { ...values[index], icon: `${picked.icon} color-${picked.color}` }; this.value = values; this.dispatchEvent(new UmbPropertyValueChangeEvent()); @@ -89,16 +89,18 @@ export class UmbPropertyEditorUICollectionViewLayoutConfigurationElement render() { return html`
- ${repeat( - this.value, - (layout, index) => '' + layout.name + layout.icon, - (layout, index) => - html`
- ${layout.isSystem - ? this.renderSystemFieldRow(layout, index) - : this.renderCustomFieldRow(layout, index)} -
`, - )} + ${this.value + ? repeat( + this.value, + (layout, index) => '' + layout.name + layout.icon, + (layout, index) => + html`
+ ${layout.isSystem + ? this.renderSystemFieldRow(layout, index) + : this.renderCustomFieldRow(layout, index)} +
`, + ) + : ''}
import('./property-editor-ui-collection-view-order-by.element.js'), meta: { label: 'Collection View Order By', - propertyEditorSchemaAlias: '', icon: 'icon-autofill', group: 'lists', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/color-swatches-editor/property-editor-ui-color-swatches-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/color-swatches-editor/property-editor-ui-color-swatches-editor.element.ts index eee1b042aa..d2d4f13a77 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/color-swatches-editor/property-editor-ui-color-swatches-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/color-swatches-editor/property-editor-ui-color-swatches-editor.element.ts @@ -21,10 +21,6 @@ export class UmbPropertyEditorUIColorSwatchesEditorElement extends UmbLitElement @property({ attribute: false }) public set config(config: UmbPropertyEditorConfigCollection | undefined) { this._showLabels = config?.getValueByAlias('useLabel') ?? this.#defaultShowLabels; - const items = config?.getValueByAlias('items') as typeof this.value; - if (items) { - this.value = items; - } } #onChange(event: CustomEvent) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/dropdown/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/dropdown/manifests.ts index 117cefb0db..c639aea701 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/dropdown/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/dropdown/manifests.ts @@ -10,19 +10,5 @@ export const manifest: ManifestPropertyEditorUi = { propertyEditorSchemaAlias: 'Umbraco.DropDown.Flexible', icon: 'icon-time', group: 'pickers', - settings: { - properties: [ - { - alias: 'multiple', - label: 'Enable multiple choice', - propertyEditorUiAlias: 'Umb.PropertyEditorUi.Toggle', - }, - { - alias: 'items', - label: 'Add options', - propertyEditorUiAlias: 'Umb.PropertyEditorUi.MultipleTextString', - }, - ], - }, }, }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/multiple-text-string/property-editor-ui-multiple-text-string.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/multiple-text-string/property-editor-ui-multiple-text-string.element.ts index 6e9ff243ed..328ac08bb3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/multiple-text-string/property-editor-ui-multiple-text-string.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/multiple-text-string/property-editor-ui-multiple-text-string.element.ts @@ -4,7 +4,7 @@ import { html, customElement, property, state, ifDefined } from '@umbraco-cms/ba import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { MultipleTextStringValue, UmbInputMultipleTextStringElement } from '@umbraco-cms/backoffice/components'; +import type { UmbInputMultipleTextStringElement } from '@umbraco-cms/backoffice/components'; /** * @element umb-property-editor-ui-multiple-text-string @@ -12,7 +12,7 @@ import type { MultipleTextStringValue, UmbInputMultipleTextStringElement } from @customElement('umb-property-editor-ui-multiple-text-string') export class UmbPropertyEditorUIMultipleTextStringElement extends UmbLitElement implements UmbPropertyEditorUiElement { @property({ type: Array }) - public value: MultipleTextStringValue = []; + public value: Array = []; @property({ attribute: false }) public set config(config: UmbPropertyEditorConfigCollection | undefined) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/number-range/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/number-range/manifests.ts index bf7a813e17..c38e28e388 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/number-range/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/number-range/manifests.ts @@ -7,7 +7,6 @@ export const manifest: ManifestPropertyEditorUi = { element: () => import('./property-editor-ui-number-range.element.js'), meta: { label: 'Number Range', - propertyEditorSchemaAlias: '', icon: 'icon-autofill', group: 'common', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/order-direction/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/order-direction/manifests.ts index 3b6762e689..ad94dd8cbe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/order-direction/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/order-direction/manifests.ts @@ -7,7 +7,6 @@ export const manifest: ManifestPropertyEditorUi = { element: () => import('./property-editor-ui-order-direction.element.js'), meta: { label: 'Order Direction', - propertyEditorSchemaAlias: '', icon: 'icon-autofill', group: 'common', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/overlay-size/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/overlay-size/manifests.ts index 286fc765d6..d18d334a3f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/overlay-size/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/overlay-size/manifests.ts @@ -7,7 +7,6 @@ export const manifest: ManifestPropertyEditorUi = { element: () => import('./property-editor-ui-overlay-size.element.js'), meta: { label: 'Overlay Size', - propertyEditorSchemaAlias: '', icon: 'icon-document', group: '', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/radio-button-list/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/radio-button-list/manifests.ts index 8debec0510..794be47a3d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/radio-button-list/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/radio-button-list/manifests.ts @@ -10,15 +10,5 @@ export const manifest: ManifestPropertyEditorUi = { propertyEditorSchemaAlias: 'Umbraco.RadioButtonList', icon: 'icon-target', group: 'lists', - settings: { - properties: [ - { - alias: 'items', - label: 'Add option', - description: 'Add, remove or sort options for the list.', - propertyEditorUiAlias: 'Umb.PropertyEditorUi.MultipleTextString', - }, - ], - }, }, }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/config/source-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/config/source-picker/manifests.ts index 2f0177ac56..2dba1aa2cc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/config/source-picker/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/config/source-picker/manifests.ts @@ -9,6 +9,5 @@ export const manifest: ManifestPropertyEditorUi = { label: 'Tree Picker Source Picker', icon: 'icon-page-add', group: 'pickers', - propertyEditorSchemaAlias: '', }, }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/config/source-type-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/config/source-type-picker/manifests.ts index 2473ac7989..291e99be3c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/config/source-type-picker/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/config/source-type-picker/manifests.ts @@ -9,6 +9,5 @@ export const manifest: ManifestPropertyEditorUi = { label: 'Tree Picker Source Type Picker', icon: 'icon-page-add', group: 'pickers', - propertyEditorSchemaAlias: '', }, }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts index d177891bc1..fe39b0a587 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts @@ -110,12 +110,21 @@ export class UmbPropertyContext extends UmbControllerBase { public setAlias(alias: string | undefined): void { this.#alias.setValue(alias); } + public getAlias(): string | undefined { + return this.#alias.getValue(); + } public setLabel(label: string | undefined): void { this.#label.setValue(label); } + public getLabel(): string | undefined { + return this.#label.getValue(); + } public setDescription(description: string | undefined): void { this.#description.setValue(description); } + public getDescription(): string | undefined { + return this.#description.getValue(); + } /** * Set the value of this property. * @param value {ValueType} the whole value to be set @@ -136,6 +145,9 @@ export class UmbPropertyContext extends UmbControllerBase { public setConfig(config: Array | undefined): void { this.#configValues.setValue(config ?? []); } + public getConfig(): Array | undefined { + return this.#configValues.getValue(); + } public setVariantId(variantId: UmbVariantId | undefined): void { this.#variantId.setValue(variantId); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts index 1219343b35..ca2364b4d8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts @@ -27,9 +27,12 @@ export class UmbPropertyElement extends UmbLitElement { * @default '' */ @property({ type: String }) - public set label(label: string) { + public set label(label: string | undefined) { this.#propertyContext.setLabel(label); } + public get label() { + return this.#propertyContext.getLabel(); + } /** * Description: render a description underneath the label. @@ -38,9 +41,12 @@ export class UmbPropertyElement extends UmbLitElement { * @default '' */ @property({ type: String }) - public set description(description: string) { + public set description(description: string | undefined) { this.#propertyContext.setDescription(description); } + public get description() { + return this.#propertyContext.getDescription(); + } /** * Alias @@ -53,6 +59,9 @@ export class UmbPropertyElement extends UmbLitElement { public set alias(alias: string) { this.#propertyContext.setAlias(alias); } + public get alias() { + return this.#propertyContext.getAlias() ?? ''; + } /** * Property Editor UI Alias. Render the Property Editor UI registered for this alias. @@ -62,12 +71,14 @@ export class UmbPropertyElement extends UmbLitElement { * @default '' */ @property({ type: String, attribute: 'property-editor-ui-alias' }) - public set propertyEditorUiAlias(value: string) { - if (this._propertyEditorUiAlias === value) return; + public set propertyEditorUiAlias(value: string | undefined) { this._propertyEditorUiAlias = value; this._observePropertyEditorUI(); } - private _propertyEditorUiAlias = ''; + public get propertyEditorUiAlias(): string { + return this._propertyEditorUiAlias ?? ''; + } + private _propertyEditorUiAlias?: string; /** * Config. Configuration to pass to the Property Editor UI. This is also the configuration data stored on the Data Type. @@ -80,6 +91,9 @@ export class UmbPropertyElement extends UmbLitElement { public set config(value: UmbPropertyEditorConfig | undefined) { this.#propertyContext.setConfig(value); } + public get config(): UmbPropertyEditorConfig | undefined { + return this.#propertyContext.getConfig(); + } @state() private _variantDifference?: string; @@ -130,13 +144,15 @@ export class UmbPropertyElement extends UmbLitElement { }; private _observePropertyEditorUI(): void { - this.observe( - umbExtensionsRegistry.byTypeAndAlias('propertyEditorUi', this._propertyEditorUiAlias), - (manifest) => { - this._gotEditorUI(manifest); - }, - '_observePropertyEditorUI', - ); + if (this._propertyEditorUiAlias) { + this.observe( + umbExtensionsRegistry.byTypeAndAlias('propertyEditorUi', this._propertyEditorUiAlias), + (manifest) => { + this._gotEditorUI(manifest); + }, + '_observePropertyEditorUI', + ); + } } private async _gotEditorUI(manifest?: ManifestPropertyEditorUi | null): Promise { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/data-source/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data-source/index.ts deleted file mode 100644 index 620557f531..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/data-source/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type { UmbTreeDataSource } from './tree-data-source.interface.js'; -export { UmbTreeServerDataSourceBase } from './tree-server-data-source-base.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/index.ts new file mode 100644 index 0000000000..c2ab9f12b2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/index.ts @@ -0,0 +1,10 @@ +export { UmbTreeServerDataSourceBase } from './tree-server-data-source-base.js'; +export { UmbTreeRepositoryBase } from './tree-repository-base.js'; + +export type { UmbTreeDataSource } from './tree-data-source.interface.js'; +export type { UmbTreeRepository } from './tree-repository.interface.js'; +export type { UmbTreeStore } from './tree-store.interface.js'; + +export type { UmbTreeRootItemsRequestArgs, UmbTreeChildrenOfRequestArgs } from './types.js'; + +export { UmbUniqueTreeStore } from './unique-tree-store.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/data-source/tree-data-source.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-data-source.interface.ts similarity index 65% rename from src/Umbraco.Web.UI.Client/src/packages/core/tree/data-source/tree-data-source.interface.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-data-source.interface.ts index d44e36fb4c..120d398903 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/data-source/tree-data-source.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-data-source.interface.ts @@ -1,4 +1,5 @@ import type { UmbTreeItemModelBase } from '../types.js'; +import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from './types.js'; import type { UmbPagedModel, UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; @@ -21,16 +22,15 @@ export interface UmbTreeDataSourceConstructor { /** * Gets the root items of the tree. - * @return {*} {Promise>>} + * @return {*} {Promise>>} * @memberof UmbTreeDataSource */ - getRootItems(): Promise>>; + getRootItems(args: UmbTreeRootItemsRequestArgs): Promise>>; /** * Gets the children of the given parent item. - * @param {(string | null)} parentUnique - * @return {*} {Promise>} + * @return {*} {Promise>} * @memberof UmbTreeDataSource */ - getChildrenOf(parentUnique: string | null): Promise>>; + getChildrenOf(args: UmbTreeChildrenOfRequestArgs): Promise>>; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-repository-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-repository-base.ts similarity index 88% rename from src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-repository-base.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-repository-base.ts index 47bd0f09b5..0ea9f637b0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-repository-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-repository-base.ts @@ -1,7 +1,7 @@ +import type { UmbUniqueTreeItemModel, UmbUniqueTreeRootModel } from '../types.js'; import type { UmbTreeStore } from './tree-store.interface.js'; -import type { UmbUniqueTreeItemModel, UmbUniqueTreeRootModel } from './types.js'; import type { UmbTreeRepository } from './tree-repository.interface.js'; -import type { UmbTreeDataSource, UmbTreeDataSourceConstructor } from './data-source/tree-data-source.interface.js'; +import type { UmbTreeDataSource, UmbTreeDataSourceConstructor } from './tree-data-source.interface.js'; import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; @@ -61,10 +61,10 @@ export abstract class UmbTreeRepositoryBase< * @return {*} * @memberof UmbTreeRepositoryBase */ - async requestRootTreeItems() { + async requestRootTreeItems(args: any) { await this._init; - const { data, error } = await this.#treeSource.getRootItems(); + const { data, error } = await this.#treeSource.getRootItems(args); if (data) { this._treeStore!.appendItems(data.items); @@ -79,17 +79,17 @@ export abstract class UmbTreeRepositoryBase< * @return {*} * @memberof UmbTreeRepositoryBase */ - async requestTreeItemsOf(parentUnique: string | null) { - if (parentUnique === undefined) throw new Error('Parent unique is missing'); + async requestTreeItemsOf(args: any) { + if (args.parentUnique === undefined) throw new Error('Parent unique is missing'); await this._init; - const { data, error } = await this.#treeSource.getChildrenOf(parentUnique); + const { data, error } = await this.#treeSource.getChildrenOf(args); if (data) { this._treeStore!.appendItems(data.items); } - return { data, error, asObservable: () => this._treeStore!.childrenOf(parentUnique) }; + return { data, error, asObservable: () => this._treeStore!.childrenOf(args.parentUnique) }; } /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-repository.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-repository.interface.ts similarity index 78% rename from src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-repository.interface.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-repository.interface.ts index 153a65f287..e7cbbf4223 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-repository.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-repository.interface.ts @@ -1,4 +1,5 @@ -import type { UmbTreeItemModelBase } from './types.js'; +import type { UmbTreeItemModelBase } from '../types.js'; +import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from './types.js'; import type { UmbPagedModel } from '@umbraco-cms/backoffice/repository'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; import type { ProblemDetails } from '@umbraco-cms/backoffice/external/backend-api'; @@ -27,20 +28,21 @@ export interface UmbTreeRepository< /** * Requests the root items of the tree. + * @param {UmbTreeRootItemsRequestArgs} args * @memberof UmbTreeRepository */ - requestRootTreeItems: () => Promise<{ + requestRootTreeItems: (args: UmbTreeRootItemsRequestArgs) => Promise<{ data?: UmbPagedModel; error?: ProblemDetails; asObservable?: () => Observable; }>; /** - * Requests the items of a item in the tree. - * @param {(string | null)} parentUnique + * Requests the children of the given parent item. + * @param {UmbTreeChildrenOfRequestArgs} args * @memberof UmbTreeRepository */ - requestTreeItemsOf: (parentUnique: string | null) => Promise<{ + requestTreeItemsOf: (args: UmbTreeChildrenOfRequestArgs) => Promise<{ data?: UmbPagedModel; error?: ProblemDetails; asObservable?: () => Observable; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/data-source/tree-server-data-source-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-server-data-source-base.ts similarity index 77% rename from src/Umbraco.Web.UI.Client/src/packages/core/tree/data-source/tree-server-data-source-base.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-server-data-source-base.ts index 22bea85ee8..457ad915f5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/data-source/tree-server-data-source-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-server-data-source-base.ts @@ -1,7 +1,8 @@ import type { UmbTreeItemModelBase } from '../types.js'; import type { UmbTreeDataSource } from './tree-data-source.interface.js'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from './types.js'; import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { TreeItemPresentationModel } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbPagedModel } from '@umbraco-cms/backoffice/repository'; @@ -9,8 +10,8 @@ export interface UmbTreeServerDataSourceBaseArgs< ServerTreeItemType extends TreeItemPresentationModel, ClientTreeItemType extends UmbTreeItemModelBase, > { - getRootItems: () => Promise>; - getChildrenOf: (parentUnique: string | null) => Promise>; + getRootItems: (args: UmbTreeRootItemsRequestArgs) => Promise>; + getChildrenOf: (args: UmbTreeChildrenOfRequestArgs) => Promise>; mapper: (item: ServerTreeItemType) => ClientTreeItemType; } @@ -44,11 +45,12 @@ export abstract class UmbTreeServerDataSourceBase< /** * Fetches the root items for the tree from the server + * @param {UmbTreeRootItemsRequestArgs} args * @return {*} * @memberof UmbTreeServerDataSourceBase */ - async getRootItems() { - const { data, error } = await tryExecuteAndNotify(this.#host, this.#getRootItems()); + async getRootItems(args: UmbTreeRootItemsRequestArgs) { + const { data, error } = await tryExecuteAndNotify(this.#host, this.#getRootItems(args)); if (data) { const items = data?.items.map((item) => this.#mapper(item)); @@ -60,14 +62,14 @@ export abstract class UmbTreeServerDataSourceBase< /** * Fetches the children of a given parent unique from the server - * @param {(string)} parentUnique + * @param {UmbTreeChildrenOfRequestArgs} args * @return {*} * @memberof UmbTreeServerDataSourceBase */ - async getChildrenOf(parentUnique: string | null) { - if (parentUnique === undefined) throw new Error('Parent unique is missing'); + async getChildrenOf(args: UmbTreeChildrenOfRequestArgs) { + if (args.parentUnique === undefined) throw new Error('Parent unique is missing'); - const { data, error } = await tryExecuteAndNotify(this.#host, this.#getChildrenOf(parentUnique)); + const { data, error } = await tryExecuteAndNotify(this.#host, this.#getChildrenOf(args)); if (data) { const items = data?.items.map((item: ServerTreeItemType) => this.#mapper(item)); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-store.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-store.interface.ts similarity index 94% rename from src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-store.interface.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-store.interface.ts index dbeefe8ed6..11355a21f4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-store.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-store.interface.ts @@ -1,4 +1,4 @@ -import type { UmbTreeItemModelBase } from './types.js'; +import type { UmbTreeItemModelBase } from '../types.js'; import type { UmbStore } from '@umbraco-cms/backoffice/store'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/types.ts new file mode 100644 index 0000000000..3a74d850d7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/types.ts @@ -0,0 +1,10 @@ +export interface UmbTreeRootItemsRequestArgs { + skip: number; + take: number; +} + +export interface UmbTreeChildrenOfRequestArgs { + parentUnique: string | null; + skip: number; + take: number; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/unique-tree-store.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/unique-tree-store.ts similarity index 96% rename from src/Umbraco.Web.UI.Client/src/packages/core/tree/unique-tree-store.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/tree/data/unique-tree-store.ts index b1e25e9366..9c03a87030 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/unique-tree-store.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/unique-tree-store.ts @@ -1,5 +1,5 @@ +import type { UmbUniqueTreeItemModel } from '../types.js'; import type { UmbTreeStore } from './tree-store.interface.js'; -import type { UmbUniqueTreeItemModel } from './types.js'; import { UmbStoreBase } from '@umbraco-cms/backoffice/store'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/default/default-tree.context.ts similarity index 55% rename from src/Umbraco.Web.UI.Client/src/packages/core/tree/tree.context.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/tree/default/default-tree.context.ts index e76e3e2b01..cd276f59d4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/default/default-tree.context.ts @@ -1,47 +1,43 @@ -import { UmbReloadTreeItemChildrenRequestEntityActionEvent } from './reload-tree-item-children/index.js'; -import type { UmbTreeItemModelBase } from './types.js'; -import type { UmbTreeRepository } from './tree-repository.interface.js'; +import { UmbReloadTreeItemChildrenRequestEntityActionEvent } from '../reload-tree-item-children/index.js'; +import type { UmbTreeItemModelBase } from '../types.js'; +import type { UmbTreeRepository } from '../data/tree-repository.interface.js'; +import type { UmbTreeContext } from '../tree-context.interface.js'; import { type UmbActionEventContext, UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; -import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; -import type { UmbPagedModel } from '@umbraco-cms/backoffice/repository'; import { type ManifestRepository, type ManifestTree, umbExtensionsRegistry, } from '@umbraco-cms/backoffice/extension-registry'; -import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { UmbExtensionApiInitializer } from '@umbraco-cms/backoffice/extension-api'; -import type { ProblemDetails } from '@umbraco-cms/backoffice/external/backend-api'; -import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; +import { UmbPaginationManager, UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; import type { UmbEntityActionEvent } from '@umbraco-cms/backoffice/entity-action'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; -// TODO: update interface -export interface UmbTreeContext extends UmbControllerBase { - selection: UmbSelectionManager; - requestChildrenOf: (parentUnique: string | null) => Promise<{ - data?: UmbPagedModel; - error?: ProblemDetails; - asObservable?: () => Observable; - }>; -} - -export class UmbTreeContextBase - extends UmbControllerBase - implements UmbTreeContext +export class UmbDefaultTreeContext + extends UmbContextBase> + implements UmbTreeContext { #treeRoot = new UmbObjectState(undefined); treeRoot = this.#treeRoot.asObservable(); - public repository?: UmbTreeRepository; public selectableFilter?: (item: TreeItemType) => boolean = () => true; public filter?: (item: TreeItemType) => boolean = () => true; public readonly selection = new UmbSelectionManager(this._host); + public readonly pagination = new UmbPaginationManager(); - #treeAlias?: string; + #manifest?: ManifestTree; + #repository?: UmbTreeRepository; #actionEventContext?: UmbActionEventContext; + #paging = { + skip: 0, + take: 50, + }; + #initResolver?: () => void; #initialized = false; @@ -50,9 +46,79 @@ export class UmbTreeContextBase }); constructor(host: UmbControllerHostElement) { - super(host); - this.provideContext('umbTreeContext', this); + super(host, UMB_DEFAULT_TREE_CONTEXT); + this.pagination.setPageSize(this.#paging.take); + this.#consumeContexts(); + // listen for page changes on the pagination manager + this.pagination.addEventListener(UmbChangeEvent.TYPE, this.#onPageChange); + this.requestTreeRoot(); + } + + // TODO: find a generic way to do this + #checkIfInitialized() { + if (this.#repository) { + this.#initialized = true; + this.#initResolver?.(); + } + } + + /** + * Sets the manifest + * @param {ManifestCollection} manifest + * @memberof UmbCollectionContext + */ + public setManifest(manifest: ManifestTree | undefined) { + if (this.#manifest === manifest) return; + this.#manifest = manifest; + this.#observeRepository(this.#manifest?.meta.repositoryAlias); + } + + /** + * Returns the manifest. + * @return {ManifestCollection} + * @memberof UmbCollectionContext + */ + public getManifest() { + return this.#manifest; + } + + public getRepository() { + return this.#repository; + } + + public async requestTreeRoot() { + await this.#init; + const { data } = await this.#repository!.requestTreeRoot(); + + if (data) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + this.#treeRoot.setValue(data); + } + } + + public async requestRootItems() { + await this.#init; + + const { data, error, asObservable } = await this.#repository!.requestRootTreeItems({ + skip: this.#paging.skip, + take: this.#paging.take, + }); + + if (data) { + this.pagination.setTotalItems(data.total); + } + + return { data, error, asObservable }; + } + + public async rootItems() { + await this.#init; + return this.#repository!.rootTreeItems(); + } + + #consumeContexts() { this.consumeContext(UMB_ACTION_EVENT_CONTEXT, (instance) => { this.#actionEventContext = instance; this.#actionEventContext.removeEventListener( @@ -64,76 +130,15 @@ export class UmbTreeContextBase this.#onReloadRequest as EventListener, ); }); - - this.requestTreeRoot(); } - // TODO: find a generic way to do this - #checkIfInitialized() { - if (this.repository) { - this.#initialized = true; - this.#initResolver?.(); - } - } + #onPageChange = (event: UmbChangeEvent) => { + const target = event.target as UmbPaginationManager; + this.#paging.skip = target.getSkip(); + this.requestRootItems(); + }; - public async setTreeAlias(treeAlias?: string) { - if (this.#treeAlias === treeAlias) return; - this.#treeAlias = treeAlias; - - this.#observeTreeManifest(); - } - - public getTreeAlias() { - return this.#treeAlias; - } - - public async requestTreeRoot() { - await this.#init; - const { data } = await this.repository!.requestTreeRoot(); - - if (data) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - this.#treeRoot.setValue(data); - } - } - - public async requestRootItems() { - await this.#init; - return this.repository!.requestRootTreeItems(); - } - - public async requestChildrenOf(parentUnique: string | null) { - await this.#init; - if (parentUnique === undefined) throw new Error('Parent unique cannot be undefined.'); - return this.repository!.requestTreeItemsOf(parentUnique); - } - - public async rootItems() { - await this.#init; - return this.repository!.rootTreeItems(); - } - - public async childrenOf(parentUnique: string | null) { - await this.#init; - return this.repository!.treeItemsOf(parentUnique); - } - - #observeTreeManifest() { - if (this.#treeAlias) { - this.observe( - umbExtensionsRegistry.byTypeAndAlias('tree', this.#treeAlias), - async (treeManifest) => { - if (!treeManifest) return; - this.#observeRepository(treeManifest); - }, - '_observeTreeManifest', - ); - } - } - - #observeRepository(treeManifest: ManifestTree) { - const repositoryAlias = treeManifest.meta.repositoryAlias; + #observeRepository(repositoryAlias?: string) { if (!repositoryAlias) throw new Error('Tree must have a repository alias.'); new UmbExtensionApiInitializer>>( @@ -142,7 +147,7 @@ export class UmbTreeContextBase repositoryAlias, [this._host], (permitted, ctrl) => { - this.repository = permitted ? ctrl.api : undefined; + this.#repository = permitted ? ctrl.api : undefined; this.#checkIfInitialized(); }, ); @@ -167,3 +172,7 @@ export class UmbTreeContextBase super.destroy(); } } + +export default UmbDefaultTreeContext; + +export const UMB_DEFAULT_TREE_CONTEXT = new UmbContextToken>('UmbTreeContext'); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/default/default-tree.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/default/default-tree.element.ts new file mode 100644 index 0000000000..4988ab4c42 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/default/default-tree.element.ts @@ -0,0 +1,168 @@ +import type { UmbTreeItemModelBase, UmbTreeSelectionConfiguration } from '../types.js'; +import type { UmbDefaultTreeContext } from './default-tree.context.js'; +import { UMB_DEFAULT_TREE_CONTEXT } from './default-tree.context.js'; +import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; +import { html, nothing, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; + +@customElement('umb-default-tree') +export class UmbDefaultTreeElement extends UmbLitElement { + private _selectionConfiguration: UmbTreeSelectionConfiguration = { + multiple: false, + selectable: true, + selection: [], + }; + + @property({ type: Object, attribute: false }) + selectionConfiguration: UmbTreeSelectionConfiguration = this._selectionConfiguration; + + @property({ type: Boolean, attribute: false }) + hideTreeRoot: boolean = false; + + @property({ attribute: false }) + selectableFilter: (item: UmbTreeItemModelBase) => boolean = () => true; + + @property({ attribute: false }) + filter: (item: UmbTreeItemModelBase) => boolean = () => true; + + @state() + private _items: UmbTreeItemModelBase[] = []; + + @state() + private _treeRoot?: UmbTreeItemModelBase; + + @state() + private _currentPage = 1; + + @state() + private _totalPages = 1; + + #treeContext?: UmbDefaultTreeContext; + #init: Promise; + + constructor() { + super(); + + this.#init = Promise.all([ + this.consumeContext(UMB_DEFAULT_TREE_CONTEXT, (instance) => { + this.#treeContext = instance; + + this.observe(this.#treeContext.pagination.currentPage, (value) => (this._currentPage = value)); + this.observe(this.#treeContext.pagination.totalPages, (value) => (this._totalPages = value)); + + this.#observeTreeRoot(); + }).asPromise(), + ]); + } + + connectedCallback(): void { + super.connectedCallback(); + this.#init; + } + + protected async updated(_changedProperties: PropertyValueMap | Map): Promise { + super.updated(_changedProperties); + await this.#init; + + if (_changedProperties.has('selectionConfiguration')) { + this._selectionConfiguration = this.selectionConfiguration; + + this.#treeContext!.selection.setMultiple(this._selectionConfiguration.multiple ?? false); + this.#treeContext!.selection.setSelectable(this._selectionConfiguration.selectable ?? true); + this.#treeContext!.selection.setSelection(this._selectionConfiguration.selection ?? []); + } + + if (_changedProperties.has('hideTreeRoot')) { + if (this.hideTreeRoot === true) { + this.#observeRootItems(); + } + } + + if (_changedProperties.has('selectableFilter')) { + this.#treeContext!.selectableFilter = this.selectableFilter; + } + + if (_changedProperties.has('filter')) { + this.#treeContext!.filter = this.filter; + } + } + + #observeTreeRoot() { + if (!this.#treeContext) return; + this.observe( + this.#treeContext.treeRoot, + (treeRoot) => { + this._treeRoot = treeRoot; + }, + 'umbTreeRootObserver', + ); + } + + async #observeRootItems() { + await this.#init; + if (!this.#treeContext?.requestRootItems) throw new Error('Tree does not support root items'); + + const { asObservable } = await this.#treeContext.requestRootItems(); + + if (asObservable) { + this.observe( + asObservable(), + (rootItems) => { + const oldValue = this._items; + this._items = rootItems; + this.requestUpdate('_items', oldValue); + }, + 'umbRootItemsObserver', + ); + } + } + + getSelection() { + return this.#treeContext?.selection.getSelection(); + } + + render() { + return html` ${this.#renderTreeRoot()} ${this.#renderRootItems()}`; + } + + #renderTreeRoot() { + if (this.hideTreeRoot || this._treeRoot === undefined) return nothing; + return html` + + `; + } + + #renderRootItems() { + if (this._items?.length === 0) return nothing; + return html` + ${repeat( + this._items, + (item, index) => item.name + '___' + index, + (item) => html``, + )} + ${this.#renderPaging()} + `; + } + + #onLoadMoreClick = (event: any) => { + event.stopPropagation(); + const next = (this._currentPage = this._currentPage + 1); + this.#treeContext?.pagination.setCurrentPageNumber(next); + }; + + #renderPaging() { + if (this._totalPages <= 1 || this._currentPage === this._totalPages) { + return nothing; + } + + return html` `; + } +} + +export default UmbDefaultTreeElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-default-tree': UmbDefaultTreeElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/default/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/default/index.ts new file mode 100644 index 0000000000..b81a955993 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/default/index.ts @@ -0,0 +1,2 @@ +export { UmbDefaultTreeElement as UmbTreeDefaultElement } from './default-tree.element.js'; +export { UmbDefaultTreeContext as UmbTreeDefaultContext } from './default-tree.context.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/default/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/default/manifests.ts new file mode 100644 index 0000000000..d184b7952b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/default/manifests.ts @@ -0,0 +1,15 @@ +import type { UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +const defaultTreeKind: UmbBackofficeManifestKind = { + type: 'kind', + alias: 'Umb.Kind.Tree.Default', + matchKind: 'default', + matchType: 'tree', + manifest: { + type: 'tree', + api: () => import('./default-tree.context.js'), + element: () => import('./default-tree.element.js'), + }, +}; + +export const manifests = [defaultTreeKind]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/index.ts index 01d898642e..48f1346f9c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/index.ts @@ -1,27 +1,14 @@ export * from './components/index.js'; -export * from './tree-item-default/index.js'; -export * from './tree-item-base/index.js'; +export * from './tree-item/index.js'; +export * from './default/index.js'; +export * from './data/index.js'; export * from './tree-menu-item-default/index.js'; -export * from './tree.context.js'; -export * from './tree.element.js'; -export * from './types.js'; -export * from './tree-repository.interface.js'; -export * from './tree-store.interface.js'; - -// Unique -export * from './unique-tree-store.js'; -export * from './unique-tree-item/index.js'; - -// Data Source -export * from './data-source/index.js'; - -// Folder export * from './folder/index.js'; +export * from './tree.element.js'; -// export { UmbReloadTreeItemChildrenEntityAction, UmbReloadTreeItemChildrenRequestEntityActionEvent, } from './reload-tree-item-children/index.js'; -export { UmbTreeRepositoryBase } from './tree-repository-base.js'; +export * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/manifests.ts index 8426073c94..199493e22d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/manifests.ts @@ -1,3 +1,11 @@ import { manifests as folderManifests } from './folder/manifests.js'; +import { manifests as defaultTreeItemManifests } from './tree-item/tree-item-default/manifests.js'; +import { manifests as defaultTreeManifests } from './default/manifests.js'; +import { manifests as treePickerManifests } from './tree-picker/manifests.js'; -export const manifests = [...folderManifests]; +export const manifests = [ + ...defaultTreeManifests, + ...folderManifests, + ...defaultTreeItemManifests, + ...treePickerManifests, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-context.interface.ts new file mode 100644 index 0000000000..dfca3c3672 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-context.interface.ts @@ -0,0 +1,7 @@ +import type { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; +import type { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; + +// TODO: update interface +export interface UmbTreeContext extends UmbContextBase { + selection: UmbSelectionManager; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-base/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-base/index.ts deleted file mode 100644 index 8e2440f40d..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-base/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './tree-item-base.context.js'; -export * from './tree-item-base.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-base/tree-item-base.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-base/tree-item-base.stories.ts deleted file mode 100644 index c946750acb..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-base/tree-item-base.stories.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/web-components'; -import './tree-item-base.element.js'; -import type { UmbTreeItemBaseElement } from './tree-item-base.element.js'; - -// TODO: provide tree item context to make this element render properly -const meta: Meta = { - title: 'Components/Tree/Tree Item Default', - component: 'umb-tree-item-default', -}; - -export default meta; -type Story = StoryObj; - -export const Overview: Story = { - args: {}, -}; - -export const WithChildren: Story = { - args: {}, -}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-default/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-default/index.ts deleted file mode 100644 index 1ee7965541..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-default/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './tree-item.context.interface.js'; -export * from './tree-item.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-default/tree-item.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-default/tree-item.element.ts deleted file mode 100644 index 9ae2c30f17..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-default/tree-item.element.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { UmbTreeItemModelBase } from '../types.js'; -import { css, html, nothing, customElement, property } from '@umbraco-cms/backoffice/external/lit'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { ManifestTreeItem } from '@umbraco-cms/backoffice/extension-registry'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; - -@customElement('umb-tree-item-default') -export class UmbTreeItemDefaultElement extends UmbLitElement { - @property({ type: Object, attribute: false }) - item?: UmbTreeItemModelBase; - - render() { - if (!this.item) return nothing; - return html` manifests.meta.entityTypes.includes(this.item!.entityType)} - .props=${{ - item: this.item, - }}>`; - } - - static styles = [ - UmbTextStyles, - css` - :host { - display: block; - } - `, - ]; -} - -declare global { - interface HTMLElementTagNameMap { - 'umb-tree-item-default': UmbTreeItemDefaultElement; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/index.ts new file mode 100644 index 0000000000..3a28d10485 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/index.ts @@ -0,0 +1,4 @@ +export * from './tree-item-context.interface.js'; +export * from './tree-item.element.js'; +export * from './tree-item-default/index.js'; +export * from './tree-item-base/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-base/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-base/index.ts new file mode 100644 index 0000000000..c32ffc8077 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-base/index.ts @@ -0,0 +1,2 @@ +export * from './tree-item-context-base.js'; +export * from './tree-item-element-base.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-base/tree-item-base.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-base/tree-item-context-base.ts similarity index 73% rename from src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-base/tree-item-base.context.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-base/tree-item-context-base.ts index c2586c7d4d..6498941ccd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-base/tree-item-base.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-base/tree-item-context-base.ts @@ -1,28 +1,34 @@ -import type { UmbTreeItemContext } from '../tree-item-default/tree-item.context.interface.js'; -import type { UmbTreeContextBase } from '../tree.context.js'; -import type { UmbTreeItemModelBase } from '../types.js'; -import { UmbReloadTreeItemChildrenRequestEntityActionEvent } from '../reload-tree-item-children/index.js'; +import type { UmbTreeItemContext } from '../tree-item-context.interface.js'; +import { UMB_DEFAULT_TREE_CONTEXT, type UmbDefaultTreeContext } from '../../default/default-tree.context.js'; +import type { UmbTreeItemModelBase } from '../../types.js'; +import { UmbReloadTreeItemChildrenRequestEntityActionEvent } from '../../reload-tree-item-children/index.js'; import { map } from '@umbraco-cms/backoffice/external/rxjs'; import { UMB_SECTION_CONTEXT, UMB_SECTION_SIDEBAR_CONTEXT } from '@umbraco-cms/backoffice/section'; import type { UmbSectionContext, UmbSectionSidebarContext } from '@umbraco-cms/backoffice/section'; +import type { ManifestTreeItem } from '@umbraco-cms/backoffice/extension-registry'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { UmbBooleanState, UmbDeepState, UmbStringState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UMB_ACTION_EVENT_CONTEXT, type UmbActionEventContext } from '@umbraco-cms/backoffice/action'; import type { UmbEntityActionEvent } from '@umbraco-cms/backoffice/entity-action'; +import { UmbPaginationManager } from '@umbraco-cms/backoffice/utils'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; export type UmbTreeItemUniqueFunction = ( x: TreeItemType, ) => string | null | undefined; -export class UmbTreeItemContextBase - extends UmbControllerBase +export abstract class UmbTreeItemContextBase + extends UmbContextBase> implements UmbTreeItemContext { public unique?: string | null; public entityType?: string; + public readonly pagination = new UmbPaginationManager(); + + #manifest?: ManifestTreeItem; #treeItem = new UmbDeepState(undefined); treeItem = this.#treeItem.asObservable(); @@ -52,17 +58,46 @@ export class UmbTreeItemContextBase #path = new UmbStringState(''); path = this.#path.asObservable(); - treeContext?: UmbTreeContextBase; + treeContext?: UmbDefaultTreeContext; #sectionContext?: UmbSectionContext; #sectionSidebarContext?: UmbSectionSidebarContext; #actionEventContext?: UmbActionEventContext; #getUniqueFunction: UmbTreeItemUniqueFunction; + // TODO: get this from the tree context + #paging = { + skip: 0, + take: 50, + }; + constructor(host: UmbControllerHost, getUniqueFunction: UmbTreeItemUniqueFunction) { - super(host); + super(host, UMB_TREE_ITEM_CONTEXT); + this.pagination.setPageSize(this.#paging.take); this.#getUniqueFunction = getUniqueFunction; this.#consumeContexts(); - this.provideContext(UMB_TREE_ITEM_CONTEXT, this); + + // listen for page changes on the pagination manager + this.pagination.addEventListener(UmbChangeEvent.TYPE, this.#onPageChange); + } + + /** + * Sets the manifest + * @param {ManifestCollection} manifest + * @memberof UmbCollectionContext + */ + // TODO: Revisit if this instead should be a getter/setter property because it might be set by extension initializer + public setManifest(manifest: ManifestTreeItem | undefined) { + if (this.#manifest === manifest) return; + this.#manifest = manifest; + } + + /** + * Returns the manifest. + * @return {ManifestCollection} + * @memberof UmbCollectionContext + */ + public getManifest() { + return this.#manifest; } public setTreeItem(treeItem: TreeItemType | undefined) { @@ -91,12 +126,23 @@ export class UmbTreeItemContextBase public async requestChildren() { if (this.unique === undefined) throw new Error('Could not request children, unique key is missing'); - // TODO: wait for tree context to be ready + const repository = this.treeContext?.getRepository(); + if (!repository) throw new Error('Could not request children, repository is missing'); + this.#isLoading.setValue(true); - const response = await this.treeContext!.requestChildrenOf(this.unique); + const { data, error, asObservable } = await repository.requestTreeItemsOf({ + parentUnique: this.unique, + skip: this.#paging.skip, + take: this.#paging.take, + }); + + if (data) { + this.pagination.setTotalItems(data.total); + } + this.#isLoading.setValue(false); - return response; + return { data, error, asObservable }; } public toggleContextMenu() { @@ -127,7 +173,7 @@ export class UmbTreeItemContextBase this.#sectionSidebarContext = instance; }); - this.consumeContext('umbTreeContext', (treeContext: UmbTreeContextBase) => { + this.consumeContext(UMB_DEFAULT_TREE_CONTEXT, (treeContext: UmbDefaultTreeContext) => { this.treeContext = treeContext; this.#observeIsSelectable(); this.#observeIsSelected(); @@ -209,10 +255,16 @@ export class UmbTreeItemContextBase async #observeHasChildren() { if (!this.treeContext || !this.unique) return; - const observable = await this.treeContext.childrenOf(this.unique); + const repository = this.treeContext.getRepository(); + if (!repository) return; + + // TODO: use createObservablePart, to prevent unnesecary changes. + const hasChildrenObservable = (await repository.treeItemsOf(this.unique)).pipe( + map((children) => children.length > 0), + ); // observe if any children will be added runtime to a tree item. Nested items/folders etc. - this.observe(observable.pipe(map((children) => children.length > 0)), (hasChildren) => { + this.observe(hasChildrenObservable, (hasChildren) => { // we need to skip the first value, because it will also return false until a child is in the store // we therefor rely on the value from the tree item itself if (this.#hasChildrenInitValueFlag === true) { @@ -230,6 +282,12 @@ export class UmbTreeItemContextBase this.requestChildren(); }; + #onPageChange = (event: UmbChangeEvent) => { + const target = event.target as UmbPaginationManager; + this.#paging.skip = target.getSkip(); + this.requestChildren(); + }; + // TODO: use router context constructPath(pathname: string, entityType: string, unique: string | null) { return `section/${pathname}/workspace/${entityType}/edit/${unique}`; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-base/tree-item-base.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-base/tree-item-element-base.ts similarity index 67% rename from src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-base/tree-item-base.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-base/tree-item-element-base.ts index 0616bdef99..e1407dd680 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-base/tree-item-base.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-base/tree-item-element-base.ts @@ -1,16 +1,23 @@ -import type { UmbTreeItemContext } from '../tree-item-default/index.js'; -import type { UmbTreeItemModelBase } from '../types.js'; -import { UMB_TREE_ITEM_CONTEXT } from './tree-item-base.context.js'; -import { html, nothing, customElement, state, ifDefined, repeat } from '@umbraco-cms/backoffice/external/lit'; +import type { UmbTreeItemContext } from '../index.js'; +import type { UmbTreeItemModelBase } from '../../types.js'; +import { UMB_TREE_ITEM_CONTEXT } from './tree-item-context-base.js'; +import { html, nothing, state, ifDefined, repeat, property } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -@customElement('umb-tree-item-base') -export class UmbTreeItemBaseElement extends UmbLitElement { - @state() - private _item?: UmbTreeItemModelBase; +// eslint-disable-next-line local-rules/enforce-element-suffix-on-element-class-name +export abstract class UmbTreeItemElementBase extends UmbLitElement { + _item?: TreeItemModelType; + @property({ type: Object, attribute: false }) + get item(): TreeItemModelType | undefined { + return this._item; + } + set item(newVal: TreeItemModelType) { + this._item = newVal; + this.#initTreeItem(); + } @state() - private _childItems?: UmbTreeItemModelBase[]; + private _childItems?: TreeItemModelType[]; @state() private _href?: string; @@ -33,7 +40,13 @@ export class UmbTreeItemBaseElement extends UmbLitElement { @state() private _iconSlotHasChildren = false; - #treeItemContext?: UmbTreeItemContext; + @state() + private _totalPages = 1; + + @state() + private _currentPage = 1; + + #treeItemContext?: UmbTreeItemContext; constructor() { super(); @@ -41,6 +54,9 @@ export class UmbTreeItemBaseElement extends UmbLitElement { this.consumeContext(UMB_TREE_ITEM_CONTEXT, (instance) => { this.#treeItemContext = instance; if (!this.#treeItemContext) return; + + this.#initTreeItem(); + // TODO: investigate if we can make an observe decorator this.observe(this.#treeItemContext.treeItem, (value) => (this._item = value)); this.observe(this.#treeItemContext.hasChildren, (value) => (this._hasChildren = value)); @@ -49,9 +65,17 @@ export class UmbTreeItemBaseElement extends UmbLitElement { this.observe(this.#treeItemContext.isSelectable, (value) => (this._isSelectable = value)); this.observe(this.#treeItemContext.isSelected, (value) => (this._isSelected = value)); this.observe(this.#treeItemContext.path, (value) => (this._href = value)); + this.observe(this.#treeItemContext.pagination.currentPage, (value) => (this._currentPage = value)); + this.observe(this.#treeItemContext.pagination.totalPages, (value) => (this._totalPages = value)); }); } + #initTreeItem() { + if (!this.#treeItemContext) return; + if (!this._item) return; + this.#treeItemContext.setTreeItem(this._item); + } + private _handleSelectedItem(event: Event) { event.stopPropagation(); this.#treeItemContext?.select(); @@ -81,9 +105,11 @@ export class UmbTreeItemBaseElement extends UmbLitElement { }); } - private _openActions() { - this.#treeItemContext?.toggleContextMenu(); - } + #onLoadMoreClick = (event: any) => { + event.stopPropagation(); + const next = (this._currentPage = this._currentPage + 1); + this.#treeItemContext?.pagination.setCurrentPageNumber(next); + }; // Note: Currently we want to prevent opening when the item is in a selectable context, but this might change in the future. // If we like to be able to open items in selectable context, then we might want to make it as a menu item action, so you have to click ... and chose an action called 'Edit' @@ -100,8 +126,9 @@ export class UmbTreeItemBaseElement extends UmbLitElement { .hasChildren=${this._hasChildren} label="${ifDefined(this._item?.name)}" href="${ifDefined(this._isSelectableContext ? undefined : this._href)}"> - ${this.#renderIconContainer()} ${this.#renderLabel()} ${this.#renderActions()} ${this.#renderChildItems()} + ${this.renderIconContainer()} ${this.renderLabel()} ${this.#renderActions()} ${this.#renderChildItems()} + ${this.#renderPaging()} `; } @@ -110,7 +137,7 @@ export class UmbTreeItemBaseElement extends UmbLitElement { return (e.target as HTMLSlotElement).assignedNodes({ flatten: true }).length > 0; }; - #renderIconContainer() { + renderIconContainer() { return html` `; } - #renderLabel() { + renderLabel() { return html``; } @@ -157,17 +184,18 @@ export class UmbTreeItemBaseElement extends UmbLitElement { ${this._childItems ? repeat( this._childItems, - // TODO: get unique here instead of name. we might be able to get it from the context - (item) => item.name, - (item) => html``, + (item, index) => item.name + '___' + index, + (item) => html``, ) : ''} `; } -} -declare global { - interface HTMLElementTagNameMap { - 'umb-tree-item-base': UmbTreeItemBaseElement; + #renderPaging() { + if (this._totalPages <= 1 || this._currentPage === this._totalPages) { + return nothing; + } + + return html` `; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-default/tree-item.context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-context.interface.ts similarity index 82% rename from src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-default/tree-item.context.interface.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-context.interface.ts index a75249bf0a..8ae64f95df 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item-default/tree-item.context.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-context.interface.ts @@ -1,9 +1,11 @@ import type { UmbTreeItemModelBase } from '../types.js'; +import type { UmbPaginationManager } from '../../utils/pagination-manager/pagination.manager.js'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; import type { ProblemDetails } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbPagedModel } from '@umbraco-cms/backoffice/repository'; +import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; -export interface UmbTreeItemContext { +export interface UmbTreeItemContext extends UmbApi { unique?: string | null; entityType?: string; treeItem: Observable; @@ -15,7 +17,7 @@ export interface UmbTreeItemContext { isActive: Observable; hasActions: Observable; path: Observable; - + pagination: UmbPaginationManager; setTreeItem(treeItem: TreeItemType | undefined): void; requestChildren(): Promise<{ data?: UmbPagedModel | undefined; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-default/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-default/index.ts new file mode 100644 index 0000000000..4ff8f02b95 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-default/index.ts @@ -0,0 +1,2 @@ +export * from './tree-item-default.context.js'; +export * from './tree-item-default.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-default/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-default/manifests.ts new file mode 100644 index 0000000000..c12ffa730c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-default/manifests.ts @@ -0,0 +1,15 @@ +import type { UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +const kind: UmbBackofficeManifestKind = { + type: 'kind', + alias: 'Umb.Kind.TreeItem.Default', + matchKind: 'default', + matchType: 'treeItem', + manifest: { + type: 'treeItem', + api: () => import('./tree-item-default.context.js'), + element: () => import('./tree-item-default.element.js'), + }, +}; + +export const manifests = [kind]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/unique-tree-item/unique-tree-item.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-default/tree-item-default.context.ts similarity index 57% rename from src/Umbraco.Web.UI.Client/src/packages/core/tree/unique-tree-item/unique-tree-item.context.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-default/tree-item-default.context.ts index 830384a3b3..04021f3c1a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/unique-tree-item/unique-tree-item.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-default/tree-item-default.context.ts @@ -1,11 +1,13 @@ -import { UmbTreeItemContextBase } from '../tree-item-base/tree-item-base.context.js'; -import type { UmbUniqueTreeItemModel } from '../types.js'; +import { UmbTreeItemContextBase } from '../tree-item-base/index.js'; +import type { UmbUniqueTreeItemModel } from '../../types.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -export class UmbUniqueTreeItemContext< +export class UmbDefaultTreeItemContext< TreeItemModelType extends UmbUniqueTreeItemModel, > extends UmbTreeItemContextBase { constructor(host: UmbControllerHost) { super(host, (x: UmbUniqueTreeItemModel) => x.unique); } } + +export default UmbDefaultTreeItemContext; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-default/tree-item-default.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-default/tree-item-default.element.ts new file mode 100644 index 0000000000..f512cc5f2c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-default/tree-item-default.element.ts @@ -0,0 +1,14 @@ +import { UmbTreeItemElementBase } from '../tree-item-base/index.js'; +import type { UmbUniqueTreeItemModel } from '../../types.js'; +import { customElement } from '@umbraco-cms/backoffice/external/lit'; + +@customElement('umb-default-tree-item') +export class UmbDefaultTreeItemElement extends UmbTreeItemElementBase {} + +export default UmbDefaultTreeItemElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-default-tree-item': UmbDefaultTreeItemElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item.element.ts new file mode 100644 index 0000000000..dd750b2ac8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item.element.ts @@ -0,0 +1,51 @@ +import { customElement, property } from '@umbraco-cms/backoffice/external/lit'; +import type { ManifestTreeItem } from '@umbraco-cms/backoffice/extension-registry'; +import { UmbExtensionInitializerElementBase, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; + +@customElement('umb-tree-item') +export class UmbTreeItemElement extends UmbExtensionInitializerElementBase { + _entityType?: string; + @property({ type: String, reflect: true }) + get entityType() { + return this._entityType; + } + set entityType(newVal) { + this._entityType = newVal; + this.#observeManifest(); + } + + #observeManifest() { + if (!this._entityType) return; + + const filterByEntityType = (manifest: ManifestTreeItem) => { + if (!this._entityType) return false; + return manifest.meta.entityTypes.includes(this._entityType); + }; + + this.observe( + umbExtensionsRegistry.byTypeAndFilter(this.getExtensionType(), filterByEntityType), + (manifests) => { + if (!manifests) return; + // TODO: what should we do if there are multiple tree items for an entity type? + const manifest = manifests[0]; + this.createApi(manifest); + this.createElement(manifest); + }, + 'umbObserveTreeManifest', + ); + } + + getExtensionType() { + return 'treeItem'; + } + + getDefaultElementName() { + return 'umb-default-tree-item'; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'umb-tree-item': UmbTreeItemElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item-default/tree-menu-item-default.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item-default/tree-menu-item-default.element.ts index 8478ba005d..01d57bc565 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item-default/tree-menu-item-default.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item-default/tree-menu-item-default.element.ts @@ -30,7 +30,13 @@ export class UmbMenuItemTreeDefaultElement extends UmbLitElement implements UmbM ? html` + .props=${{ + hideTreeRoot: this.manifest?.meta.hideTreeRoot === true, + selectionConfiguration: { + selectable: false, + multiple: false, + }, + }}> ` : nothing; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-picker/manifests.ts new file mode 100644 index 0000000000..8bbb3ffe7d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-picker/manifests.ts @@ -0,0 +1,8 @@ +export const manifests = [ + { + type: 'modal', + alias: 'Umb.Modal.TreePicker', + name: 'Tree Picker Modal', + js: () => import('./tree-picker-modal.element.js'), + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/tree-picker/tree-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-picker/tree-picker-modal.element.ts similarity index 84% rename from src/Umbraco.Web.UI.Client/src/packages/core/modal/common/tree-picker/tree-picker-modal.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-picker/tree-picker-modal.element.ts index d0f3fb25d0..da3a4232e9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/tree-picker/tree-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-picker/tree-picker-modal.element.ts @@ -1,8 +1,9 @@ +import type { UmbTreeSelectionConfiguration } from '../types.js'; import { html, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import type { UmbTreePickerModalData, UmbPickerModalValue } from '@umbraco-cms/backoffice/modal'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; import { UmbDeselectedEvent, UmbSelectedEvent, UmbSelectionChangeEvent } from '@umbraco-cms/backoffice/event'; -import type { UmbTreeElement, UmbTreeItemModelBase, UmbTreeSelectionConfiguration } from '@umbraco-cms/backoffice/tree'; +import type { UmbTreeElement, UmbTreeItemModelBase } from '@umbraco-cms/backoffice/tree'; @customElement('umb-tree-picker-modal') export class UmbTreePickerModalElement extends UmbModalBaseElement< @@ -51,14 +52,16 @@ export class UmbTreePickerModalElement + @deselected=${this.#onDeselected}>
diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree.element.ts index d62ad4de7b..a009817be2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree.element.ts @@ -1,139 +1,22 @@ -import { UmbTreeContextBase } from './tree.context.js'; -import type { UmbTreeItemModelBase } from './types.js'; -import { html, nothing, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; - -import './tree-item-default/tree-item.element.js'; -import './tree-item-base/tree-item-base.element.js'; - -export type UmbTreeSelectionConfiguration = { - multiple?: boolean; - selectable?: boolean; - selection?: Array; -}; +import { customElement } from '@umbraco-cms/backoffice/external/lit'; +import type { ManifestTree } from '@umbraco-cms/backoffice/extension-registry'; +import { UmbExtensionInitializerElementBase } from '@umbraco-cms/backoffice/extension-registry'; @customElement('umb-tree') -export class UmbTreeElement extends UmbLitElement { - @property({ type: String, reflect: true }) - set alias(newVal) { - this.#treeContext.setTreeAlias(newVal); - } - get alias() { - return this.#treeContext.getTreeAlias(); +export class UmbTreeElement extends UmbExtensionInitializerElementBase { + getExtensionType() { + return 'tree'; } - private _selectionConfiguration: UmbTreeSelectionConfiguration = { - multiple: false, - selectable: true, - selection: [], - }; - - @property({ type: Object }) - set selectionConfiguration(config: UmbTreeSelectionConfiguration) { - this._selectionConfiguration = config; - this.#treeContext.selection.setMultiple(config.multiple ?? false); - this.#treeContext.selection.setSelectable(config.selectable ?? true); - this.#treeContext.selection.setSelection(config.selection ?? []); - } - get selectionConfiguration(): UmbTreeSelectionConfiguration { - return this._selectionConfiguration; - } - - // TODO: what is the best name for this functionality? - private _hideTreeRoot = false; - @property({ type: Boolean, attribute: 'hide-tree-root' }) - set hideTreeRoot(newVal: boolean) { - const oldVal = this._hideTreeRoot; - this._hideTreeRoot = newVal; - if (newVal === true) { - this.#observeRootItems(); - } - - this.requestUpdate('hideTreeRoot', oldVal); - } - get hideTreeRoot() { - return this._hideTreeRoot; - } - - @property() - set selectableFilter(newVal) { - this.#treeContext.selectableFilter = newVal; - } - get selectableFilter() { - return this.#treeContext.selectableFilter; - } - - @property() - set filter(newVal) { - this.#treeContext.filter = newVal; - } - get filter() { - return this.#treeContext.filter; - } - - @state() - private _items: UmbTreeItemModelBase[] = []; - - @state() - private _treeRoot?: UmbTreeItemModelBase; - - #treeContext = new UmbTreeContextBase(this); - #rootItemsObserver?: UmbObserverController>; - - constructor() { - super(); - this.#observeTreeRoot(); - } - - #observeTreeRoot() { - this.observe( - this.#treeContext.treeRoot, - (treeRoot) => { - this._treeRoot = treeRoot; - }, - 'umbTreeRootObserver', - ); - } - - async #observeRootItems() { - if (!this.#treeContext?.requestRootItems) throw new Error('Tree does not support root items'); - this.#rootItemsObserver?.destroy(); - - const { asObservable } = await this.#treeContext.requestRootItems(); - - if (asObservable) { - this.#rootItemsObserver = this.observe(asObservable(), (rootItems) => { - const oldValue = this._items; - this._items = rootItems; - this.requestUpdate('_items', oldValue); - }); - } + getDefaultElementName() { + return 'umb-default-tree'; } getSelection() { - return this.#treeContext.selection.getSelection(); - } - - render() { - return html` ${this.#renderTreeRoot()} ${this.#renderRootItems()}`; - } - - #renderTreeRoot() { - if (this.hideTreeRoot || this._treeRoot === undefined) return nothing; - return html` `; - } - - #renderRootItems() { - if (this._items?.length === 0) return nothing; - return html` - ${repeat( - this._items, - // TODO: use unique here: - (item, index) => item.name + '___' + index, - (item) => html``, - )} - `; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + // TODO: make base interface for a tree element + return this._element?.getSelection?.() ?? []; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree.stories.ts index a09b75ff64..2e81b4daeb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree.stories.ts @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/web-components'; -import './tree.element.js'; +import './default/default-tree.element.js'; import type { UmbTreeElement } from './tree.element.js'; const meta: Meta = { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/types.ts index 81920d665f..8ca974ed9d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/types.ts @@ -14,3 +14,9 @@ export interface UmbUniqueTreeItemModel extends UmbTreeItemModelBase { export interface UmbUniqueTreeRootModel extends UmbTreeItemModelBase { unique: null; } + +export type UmbTreeSelectionConfiguration = { + multiple?: boolean; + selectable?: boolean; + selection?: Array; +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/unique-tree-item/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/unique-tree-item/index.ts deleted file mode 100644 index 12e991943d..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/unique-tree-item/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './unique-tree-item.context.js'; -export * from './unique-tree-item.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/unique-tree-item/unique-tree-item.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/unique-tree-item/unique-tree-item.element.ts deleted file mode 100644 index eabd7b456c..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/unique-tree-item/unique-tree-item.element.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { UmbUniqueTreeItemModel } from '../types.js'; -import { UmbUniqueTreeItemContext } from './unique-tree-item.context.js'; -import { html, nothing, customElement, property } from '@umbraco-cms/backoffice/external/lit'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { UmbBackofficeManifestKind, UmbTreeItemElement } from '@umbraco-cms/backoffice/extension-registry'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; - -// TODO: Move to separate file: -const manifest: UmbBackofficeManifestKind = { - type: 'kind', - alias: 'Umb.Kind.UniqueTreeItem', - matchKind: 'unique', - matchType: 'treeItem', - manifest: { - type: 'treeItem', - elementName: 'umb-unique-tree-item', - }, -}; -umbExtensionsRegistry.register(manifest); - -@customElement('umb-unique-tree-item') -export class UmbUniqueTreeItemElement extends UmbLitElement implements UmbTreeItemElement { - private _item?: UmbUniqueTreeItemModel; - @property({ type: Object, attribute: false }) - public get item() { - return this._item; - } - public set item(value: UmbUniqueTreeItemModel | undefined) { - this._item = value; - this.#context.setTreeItem(value); - } - - #context = new UmbUniqueTreeItemContext(this); - - render() { - if (!this.item) return nothing; - return html``; - } -} - -declare global { - interface HTMLElementTagNameMap { - 'umb-unique-tree-item': UmbUniqueTreeItemElement; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/pagination-manager/pagination.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/pagination-manager/pagination.manager.ts index 148e6850fa..369cc39820 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/pagination-manager/pagination.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/pagination-manager/pagination.manager.ts @@ -8,7 +8,7 @@ export class UmbPaginationManager extends EventTarget { #totalItems = new UmbNumberState(0); public readonly totalItems = this.#totalItems.asObservable(); - #totalPages = new UmbNumberState(0); + #totalPages = new UmbNumberState(1); public readonly totalPages = this.#totalPages.asObservable(); #currentPage = new UmbNumberState(1); @@ -106,7 +106,8 @@ export class UmbPaginationManager extends EventTarget { * @memberof UmbPaginationManager */ #calculateTotalPages() { - const totalPages = Math.ceil(this.#totalItems.getValue() / this.#pageSize.getValue()); + let totalPages = Math.ceil(this.#totalItems.getValue() / this.#pageSize.getValue()); + totalPages = totalPages === 0 ? 1 : totalPages; this.#totalPages.setValue(totalPages); /* If we currently are on a page higher than the total pages. We need to reset the current page to the last page. @@ -117,7 +118,7 @@ export class UmbPaginationManager extends EventTarget { } #calculateSkip() { - const skip = (this.#currentPage.getValue() - 1) * this.#pageSize.getValue(); + const skip = Math.max(0, (this.#currentPage.getValue() - 1) * this.#pageSize.getValue()); this.#skip.setValue(skip); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/variant-selector/variant-selector.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/variant-selector/variant-selector.element.ts index a939bcb278..ce0148af9d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/variant-selector/variant-selector.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/variant-selector/variant-selector.element.ts @@ -201,7 +201,12 @@ export class UmbVariantSelectorElement extends UmbLitElement { render() { return html` - + ${ this._variants?.length ? html` @@ -246,8 +251,7 @@ export class UmbVariantSelectorElement extends UmbLitElement { ? html`` : nothing}
- ${variant.title} - ${variant.culture ? html` (${variant.culture})` : ''} + ${variant.title} ${variant.culture ? html` (${variant.culture})` : ''} ${variant.segment}
${variant.state}
diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view.element.ts index 840765e937..88b284ede4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view.element.ts @@ -21,6 +21,9 @@ export class UmbWorkspaceSplitViewElement extends UmbLitElement { public set splitViewIndex(index: number) { this.splitViewContext.setSplitViewIndex(index); } + public get splitViewIndex(): number { + return this.splitViewContext.getSplitViewIndex()!; + } splitViewContext = new UmbWorkspaceSplitViewContext(this); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/editable-workspace-context-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/editable-workspace-context-base.ts index 9c3d9f74be..3aab9c5b0d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/editable-workspace-context-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/editable-workspace-context-base.ts @@ -46,13 +46,12 @@ export abstract class UmbEditableWorkspaceContextBase this.#isNew.setValue(isNew); } - protected saveComplete(data: WorkspaceDataModelType) { + protected workspaceComplete(data: WorkspaceDataModelType | undefined) { if (this.modalContext) { - this.modalContext?.setValue(data); + if (data) { + this.modalContext?.setValue(data); + } this.modalContext?.submit(); - } else { - // No need to have UI changing to "not new" if we are in a modal. - this.setIsNew(false); } } 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 e8bd5cb161..dd2e5889e4 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 @@ -27,9 +27,6 @@ export class UmbInputDataTypeElement extends FormControlMixin(UmbLitElement) { * @default [] */ @property({ type: String, attribute: false }) - get value(): string { - return super.value?.toString() ?? ''; - } set value(dataTypeId: string) { super.value = dataTypeId ?? ''; this._ids = super.value @@ -37,6 +34,9 @@ export class UmbInputDataTypeElement extends FormControlMixin(UmbLitElement) { .map((tag) => tag.trim()) .filter((id) => id.length !== 0); } + get value(): string { + return super.value?.toString() ?? ''; + } #editDataTypeModal?: UmbModalRouteRegistrationController; 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 eb853a72f4..74f7d98e5e 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 @@ -38,7 +38,7 @@ export class UmbDataTypePickerFlowDataTypePickerModalElement extends UmbModalBas // TODO: This is a hack to get the data types of a property editor ui alias. // TODO: Make sure filtering works data-type that does not have a property editor ui, but should be using the default property editor UI for those. // TODO: make an end-point just retrieving the data types using a given property editor ui alias. - const { data } = await dataTypeTreeRepository.requestRootTreeItems(); + const { data } = await dataTypeTreeRepository.requestRootTreeItems({ skip: 0, take: 100 }); if (!data) return; 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 55acd43813..2dd55d5a39 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 @@ -98,7 +98,7 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< async #init() { // TODO: Get ALL items, or traverse the structure aka. multiple recursive calls. this.observe( - (await this.#treeRepository.requestRootTreeItems()).asObservable(), + (await this.#treeRepository.requestRootTreeItems({ skip: 0, take: 100 })).asObservable(), (items) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/index.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/index.ts index a0e4f41ce6..c573c05244 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/index.ts @@ -1,2 +1,3 @@ export { UmbDataTypeDetailRepository } from './data-type-detail.repository.js'; export { UMB_DATA_TYPE_DETAIL_REPOSITORY_ALIAS } from './manifests.js'; +export { UMB_DATA_TYPE_DETAIL_STORE_CONTEXT } from './data-type-detail.store.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/index.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/index.ts index bb1cf117cd..71a8f8fe99 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/index.ts @@ -1,5 +1,9 @@ export { UmbCopyDataTypeRepository, COPY_DATA_TYPE_REPOSITORY_ALIAS } from './copy/index.js'; -export { UmbDataTypeDetailRepository, UMB_DATA_TYPE_DETAIL_REPOSITORY_ALIAS } from './detail/index.js'; +export { + UmbDataTypeDetailRepository, + UMB_DATA_TYPE_DETAIL_REPOSITORY_ALIAS, + UMB_DATA_TYPE_DETAIL_STORE_CONTEXT, +} from './detail/index.js'; export { UmbDataTypeItemRepository, UMB_DATA_TYPE_ITEM_REPOSITORY_ALIAS } from './item/index.js'; export { UmbMoveDataTypeRepository, MOVE_DATA_TYPE_REPOSITORY_ALIAS } from './move/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.repository.ts index b0a3b12a27..8e1702f7ef 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.repository.ts @@ -1,8 +1,8 @@ -import { UmbTreeRepositoryBase } from '../../core/tree/tree-repository-base.js'; import { UMB_DATA_TYPE_ROOT_ENTITY_TYPE } from '../entity.js'; import { UmbDataTypeTreeServerDataSource } from './data-type-tree.server.data-source.js'; import { UMB_DATA_TYPE_TREE_STORE_CONTEXT } from './data-type-tree.store.js'; import type { UmbDataTypeTreeItemModel, UmbDataTypeTreeRootModel } from './types.js'; +import { UmbTreeRepositoryBase } from '@umbraco-cms/backoffice/tree'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.server.data-source.ts index cd7d0e88bc..d4fda7664a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.server.data-source.ts @@ -1,4 +1,5 @@ import type { UmbDataTypeTreeItemModel } from './types.js'; +import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from '@umbraco-cms/backoffice/tree'; import { UmbTreeServerDataSourceBase } from '@umbraco-cms/backoffice/tree'; import type { DataTypeTreeItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { DataTypeResource } from '@umbraco-cms/backoffice/external/backend-api'; @@ -28,16 +29,17 @@ export class UmbDataTypeTreeServerDataSource extends UmbTreeServerDataSourceBase } } -// eslint-disable-next-line local-rules/no-direct-api-import -const getRootItems = () => DataTypeResource.getTreeDataTypeRoot({}); +const getRootItems = (args: UmbTreeRootItemsRequestArgs) => + // eslint-disable-next-line local-rules/no-direct-api-import + DataTypeResource.getTreeDataTypeRoot({ skip: args.skip, take: args.take }); -const getChildrenOf = (parentUnique: string | null) => { - if (parentUnique === null) { - return getRootItems(); +const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => { + if (args.parentUnique === null) { + return getRootItems(args); } else { // eslint-disable-next-line local-rules/no-direct-api-import return DataTypeResource.getTreeDataTypeChildren({ - parentId: parentUnique, + parentId: args.parentUnique, }); } }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.store.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.store.ts index c4091fbb6d..e6febc1a44 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.store.ts @@ -1,8 +1,8 @@ -import { UmbStoreConnector } from '../../core/store/store-connector.js'; -import { UmbUniqueTreeStore } from '../../core/tree/unique-tree-store.js'; -import { UMB_DATA_TYPE_DETAIL_STORE_CONTEXT } from '../repository/detail/data-type-detail.store.js'; +import { UMB_DATA_TYPE_DETAIL_STORE_CONTEXT } from '../repository/index.js'; import type { UmbDataTypeDetailModel } from '../types.js'; import type { UmbDataTypeTreeItemModel } from './types.js'; +import { UmbUniqueTreeStore } from '@umbraco-cms/backoffice/tree'; +import { UmbStoreConnector } from '@umbraco-cms/backoffice/store'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/manifests.ts index 6ab730ac33..3131fa4ca8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/manifests.ts @@ -28,6 +28,7 @@ const treeStore: ManifestTreeStore = { const tree: ManifestTree = { type: 'tree', + kind: 'default', alias: 'Umb.Tree.DataTypes', name: 'Data Types Tree', meta: { @@ -37,7 +38,7 @@ const tree: ManifestTree = { const treeItem: ManifestTreeItem = { type: 'treeItem', - kind: 'unique', + kind: 'default', alias: 'Umb.TreeItem.DataType', name: 'Data Type Tree Item', meta: { diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context.ts index c579048efd..48f9ba838e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context.ts @@ -50,21 +50,21 @@ export class UmbDataTypeWorkspaceContext #properties = new UmbArrayState([], (x) => x.alias); readonly properties = this.#properties.asObservable(); - private _propertyEditorSchemaConfigDefaultData: Array = []; - private _propertyEditorUISettingsDefaultData: Array = []; - - private _propertyEditorSchemaConfigProperties: Array = []; - private _propertyEditorUISettingsProperties: Array = []; - - private _propertyEditorSchemaConfigDefaultUIAlias: string | null = null; - - private _configDefaultData?: Array; - - private _propertyEditorUISettingsSchemaAlias?: string; - #defaults = new UmbArrayState([], (entry) => entry.alias); readonly defaults = this.#defaults.asObservable(); + #propertyEditorSchemaSettingsDefaultData: Array = []; + #propertyEditorUISettingsDefaultData: Array = []; + + #propertyEditorSchemaSettingsProperties: Array = []; + #propertyEditorUISettingsProperties: Array = []; + + #propertyEditorSchemaConfigDefaultUIAlias: string | null = null; + + #settingsDefaultData?: Array; + + #propertyEditorUISettingsSchemaAlias?: string; + #propertyEditorUiIcon = new UmbStringState(null); readonly propertyEditorUiIcon = this.#propertyEditorUiIcon.asObservable(); @@ -92,10 +92,10 @@ export class UmbDataTypeWorkspaceContext // if the property editor ui alias is not set, we use the default alias from the schema if (propertyEditorUiAlias === null) { await this.#observePropertyEditorSchemaAlias(); - this.setPropertyEditorUiAlias(this._propertyEditorSchemaConfigDefaultUIAlias!); + this.setPropertyEditorUiAlias(this.#propertyEditorSchemaConfigDefaultUIAlias!); } else { await this.#setPropertyEditorUIConfig(propertyEditorUiAlias); - this.setPropertyEditorSchemaAlias(this._propertyEditorUISettingsSchemaAlias!); + this.setPropertyEditorSchemaAlias(this.#propertyEditorUISettingsSchemaAlias!); await this.#observePropertyEditorSchemaAlias(); } @@ -125,9 +125,9 @@ export class UmbDataTypeWorkspaceContext return this.observe( umbExtensionsRegistry.byTypeAndAlias('propertyEditorSchema', propertyEditorSchemaAlias), (manifest) => { - this._propertyEditorSchemaConfigProperties = manifest?.meta.settings?.properties || []; - this._propertyEditorSchemaConfigDefaultData = manifest?.meta.settings?.defaultData || []; - this._propertyEditorSchemaConfigDefaultUIAlias = manifest?.meta.defaultPropertyEditorUiAlias || null; + this.#propertyEditorSchemaSettingsProperties = manifest?.meta.settings?.properties || []; + this.#propertyEditorSchemaSettingsDefaultData = manifest?.meta.settings?.defaultData || []; + this.#propertyEditorSchemaConfigDefaultUIAlias = manifest?.meta.defaultPropertyEditorUiAlias || null; }, 'schema', ).asPromise(); @@ -140,35 +140,35 @@ export class UmbDataTypeWorkspaceContext this.#propertyEditorUiIcon.setValue(manifest?.meta.icon || null); this.#propertyEditorUiName.setValue(manifest?.name || null); - this._propertyEditorUISettingsSchemaAlias = manifest?.meta.propertyEditorSchemaAlias; - this._propertyEditorUISettingsProperties = manifest?.meta.settings?.properties || []; - this._propertyEditorUISettingsDefaultData = manifest?.meta.settings?.defaultData || []; + this.#propertyEditorUISettingsSchemaAlias = manifest?.meta.propertyEditorSchemaAlias; + this.#propertyEditorUISettingsProperties = manifest?.meta.settings?.properties || []; + this.#propertyEditorUISettingsDefaultData = manifest?.meta.settings?.defaultData || []; }, 'editorUi', ).asPromise(); } private _mergeConfigProperties() { - if (this._propertyEditorSchemaConfigProperties && this._propertyEditorUISettingsProperties) { + if (this.#propertyEditorSchemaSettingsProperties && this.#propertyEditorUISettingsProperties) { // Reset the value to this array, and then afterwards append: - this.#properties.setValue(this._propertyEditorSchemaConfigProperties); + this.#properties.setValue(this.#propertyEditorSchemaSettingsProperties); // Append the UI settings properties to the schema properties, so they can override the schema properties: - this.#properties.append(this._propertyEditorUISettingsProperties); + this.#properties.append(this.#propertyEditorUISettingsProperties); } } private _mergeConfigDefaultData() { - if (!this._propertyEditorSchemaConfigDefaultData || !this._propertyEditorUISettingsDefaultData) return; + if (!this.#propertyEditorSchemaSettingsDefaultData || !this.#propertyEditorUISettingsDefaultData) return; - this._configDefaultData = [ - ...this._propertyEditorSchemaConfigDefaultData, - ...this._propertyEditorUISettingsDefaultData, + this.#settingsDefaultData = [ + ...this.#propertyEditorSchemaSettingsDefaultData, + ...this.#propertyEditorUISettingsDefaultData, ]; - this.#defaults.setValue(this._configDefaultData); + this.#defaults.setValue(this.#settingsDefaultData); } public getPropertyDefaultValue(alias: string) { - return this._configDefaultData?.find((x) => x.alias === alias)?.value; + return this.#settingsDefaultData?.find((x) => x.alias === alias)?.value; } createPropertyDatasetContext(host: UmbControllerHost): UmbPropertyDatasetContext { @@ -177,8 +177,9 @@ export class UmbDataTypeWorkspaceContext async load(unique: string) { this.resetState(); - this.#getDataPromise = this.repository.requestByUnique(unique); - const { data } = await this.#getDataPromise; + const request = this.repository.requestByUnique(unique); + this.#getDataPromise = request; + const { data } = await request; if (!data) return undefined; this.setIsNew(false); @@ -189,8 +190,10 @@ export class UmbDataTypeWorkspaceContext async create(parent: { entityType: string; unique: string | null }) { this.resetState(); this.#parent = parent; - this.#getDataPromise = this.repository.createScaffold(); - let { data } = await this.#getDataPromise; + const request = this.repository.createScaffold(); + this.#getDataPromise = request; + let { data } = await request; + if (!data) return undefined; if (this.modalContext) { data = { ...data, ...this.modalContext.data.preset }; } @@ -282,7 +285,8 @@ export class UmbDataTypeWorkspaceContext await this.repository.save(this.#currentData.value); } - this.saveComplete(this.#currentData.value); + this.setIsNew(false); + this.workspaceComplete(this.#currentData.value); } async delete(unique: string) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/tree/dictionary-tree.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/tree/dictionary-tree.server.data-source.ts index 1521134377..843cfcd8d2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/tree/dictionary-tree.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/tree/dictionary-tree.server.data-source.ts @@ -1,5 +1,6 @@ import { UMB_DICTIONARY_ENTITY_TYPE } from '../entity.js'; import type { UmbDictionaryTreeItemModel } from './types.js'; +import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from '@umbraco-cms/backoffice/tree'; import { UmbTreeServerDataSourceBase } from '@umbraco-cms/backoffice/tree'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { NamedEntityTreeItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; @@ -29,16 +30,17 @@ export class UmbDictionaryTreeServerDataSource extends UmbTreeServerDataSourceBa } } -// eslint-disable-next-line local-rules/no-direct-api-import -const getRootItems = () => DictionaryResource.getTreeDictionaryRoot({}); +const getRootItems = (args: UmbTreeRootItemsRequestArgs) => + // eslint-disable-next-line local-rules/no-direct-api-import + DictionaryResource.getTreeDictionaryRoot({ skip: args.skip, take: args.take }); -const getChildrenOf = (parentUnique: string | null) => { - if (parentUnique === null) { - return getRootItems(); +const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => { + if (args.parentUnique === null) { + return getRootItems(args); } else { // eslint-disable-next-line local-rules/no-direct-api-import return DictionaryResource.getTreeDictionaryChildren({ - parentId: parentUnique, + parentId: args.parentUnique, }); } }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/tree/manifests.ts index fb1e411cb0..1076ff9165 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/tree/manifests.ts @@ -29,6 +29,7 @@ const treeStore: ManifestTreeStore = { const tree: ManifestTree = { type: 'tree', + kind: 'default', alias: UMB_DICTIONARY_TREE_ALIAS, name: 'Dictionary Tree', meta: { @@ -38,7 +39,7 @@ const tree: ManifestTree = { const treeItem: ManifestTreeItem = { type: 'treeItem', - kind: 'unique', + kind: 'default', alias: 'Umb.TreeItem.Dictionary', name: 'Dictionary Tree Item', meta: { diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/workspace/dictionary-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/workspace/dictionary-workspace.context.ts index 326750bb4d..5779f153be 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/workspace/dictionary-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/workspace/dictionary-workspace.context.ts @@ -114,7 +114,10 @@ export class UmbDictionaryWorkspaceContext } const data = this.getData(); - if (data) this.saveComplete(data); + if (!data) return; + + this.setIsNew(false); + this.workspaceComplete(data); } public destroy(): void { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/document-type.tree.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/document-type.tree.server.data-source.ts index 4aa07032c0..d0e170aaed 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/document-type.tree.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/document-type.tree.server.data-source.ts @@ -1,5 +1,6 @@ import { UMB_DOCUMENT_TYPE_ENTITY_TYPE, UMB_DOCUMENT_TYPE_FOLDER_ENTITY_TYPE } from '../entity.js'; import type { UmbDocumentTypeTreeItemModel } from './types.js'; +import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from '@umbraco-cms/backoffice/tree'; import { UmbTreeServerDataSourceBase } from '@umbraco-cms/backoffice/tree'; import type { DocumentTypeTreeItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { DocumentTypeResource } from '@umbraco-cms/backoffice/external/backend-api'; @@ -29,16 +30,19 @@ export class UmbDocumentTypeTreeServerDataSource extends UmbTreeServerDataSource } } -// eslint-disable-next-line local-rules/no-direct-api-import -const getRootItems = () => DocumentTypeResource.getTreeDocumentTypeRoot({}); +const getRootItems = (args: UmbTreeRootItemsRequestArgs) => + // eslint-disable-next-line local-rules/no-direct-api-import + DocumentTypeResource.getTreeDocumentTypeRoot({ skip: args.skip, take: args.take }); -const getChildrenOf = (parentUnique: string | null) => { - if (parentUnique === null) { - return getRootItems(); +const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => { + if (args.parentUnique === null) { + return getRootItems({ skip: args.skip, take: args.take }); } else { // eslint-disable-next-line local-rules/no-direct-api-import return DocumentTypeResource.getTreeDocumentTypeChildren({ - parentId: parentUnique, + parentId: args.parentUnique, + skip: args.skip, + take: args.take, }); } }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/manifests.ts index 9a9bc9e598..649ca15c51 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/manifests.ts @@ -34,6 +34,7 @@ const treeStore: ManifestTreeStore = { const tree: ManifestTree = { type: 'tree', + kind: 'default', alias: UMB_DOCUMENT_TYPE_TREE_ALIAS, name: 'Document Type Tree', meta: { @@ -43,7 +44,7 @@ const tree: ManifestTree = { const treeItem: ManifestTreeItem = { type: 'treeItem', - kind: 'unique', + kind: 'default', alias: 'Umb.TreeItem.DocumentType', name: 'Document Type Tree Item', meta: { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts index 6cd968db7e..389130e141 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts @@ -187,7 +187,8 @@ export class UmbDocumentTypeWorkspaceContext await this.structure.save(); } - this.saveComplete(data); + this.setIsNew(false); + this.workspaceComplete(data); } public destroy(): void { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.element.ts index 2d5f350910..fc7abd197c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.element.ts @@ -163,7 +163,7 @@ export class UmbInputDocumentElement extends FormControlMixin(UmbLitElement) { } #renderItems() { - if (!this._items) return; + if (!this._items?.length) return; return html` ${repeat( this._items, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/pick-document-variant-modal.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/pick-document-variant-modal.controller.ts index cdb45ade87..ded9a0e18e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/pick-document-variant-modal.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/pick-document-variant-modal.controller.ts @@ -26,16 +26,19 @@ export class UmbPickDocumentVariantModalController extends UmbControllerBase { }; if (modalData.options.length === 0) { - // TODO: What do to when there is no options? + // TODO: What do to when there is no options? [NL] } const modalContext = modalManagerContext.open(UMB_DOCUMENT_LANGUAGE_PICKER_MODAL, { data: modalData, // We need to turn the selected variant ids into strings for them to be serializable to the value state, in other words the value of a modal cannot hold class instances: - value: { selection: selected.map((x) => x.toString()) ?? [] }, + // Make selection unique by filtering out duplicates: + value: { selection: selected.map((x) => x.toString()).filter((v, i, a) => a.indexOf(v) === i) ?? [] }, }); - const result = await modalContext.onSubmit().catch(() => undefined); + const p = modalContext.onSubmit(); + p.catch(() => this.destroy()); + const result = await p; // This is a one time off, so we can destroy our selfs. this.destroy(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-picker-modal.element.ts index 5d237d3d46..22c07a7555 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-picker-modal.element.ts @@ -36,16 +36,16 @@ export class UmbDocumentVariantPickerModalElement extends UmbModalBaseElement< async #setInitialSelection() { let selected = this.value?.selection ?? []; + // Filter selection based on options: + selected = selected.filter((s) => this.data?.options.some((o) => o.unique === s)); + if (selected.length === 0) { - // TODO: Make it possible to use consume context without callback. [NL] - const ctrl = this.consumeContext(UMB_APP_LANGUAGE_CONTEXT, () => {}); - const context = await ctrl.asPromise(); + const context = await this.getContext(UMB_APP_LANGUAGE_CONTEXT); const appCulture = context.getAppCulture(); // If the app language is one of the options, select it by default: if (appCulture && this.data?.options.some((o) => o.language.unique === appCulture)) { selected = appendToFrozenArray(selected, new UmbVariantId(appCulture, null).toString()); } - ctrl.destroy(); } this.#selectionManager.setMultiple(true); 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 9fa451cfde..d04d9fd5a1 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 @@ -16,7 +16,13 @@ export const manifest: ManifestPropertyEditorUi = { alias: 'startNodeId', label: 'Start node', description: '', - propertyEditorUiAlias: 'Umb.PropertyEditorUi.TreePicker', + propertyEditorUiAlias: 'Umb.PropertyEditorUi.DocumentPicker', + config: [ + { + alias: 'validationLimit', + value: { min: 0, max: 1 }, + }, + ], }, { alias: 'showOpenButton', diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-editors/document-picker/property-editor-ui-document-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-editors/document-picker/property-editor-ui-document-picker.element.ts index 9f7d2a0574..1239e92c6b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-editors/document-picker/property-editor-ui-document-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-editors/document-picker/property-editor-ui-document-picker.element.ts @@ -13,7 +13,7 @@ export class UmbPropertyEditorUIDocumentPickerElement extends UmbLitElement impl return this._value; } public set value(value: Array) { - this._value = value || []; + this._value = Array.isArray(value) ? value : value ? [value] : []; } @property({ attribute: false }) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/tree/document-recycle-bin-tree.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/tree/document-recycle-bin-tree.server.data-source.ts index 6bc7b5cf8c..a3466f28b6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/tree/document-recycle-bin-tree.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/tree/document-recycle-bin-tree.server.data-source.ts @@ -2,6 +2,7 @@ import type { UmbDocumentRecycleBinTreeItemModel } from './types.js'; import type { DocumentRecycleBinItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { DocumentResource } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from '@umbraco-cms/backoffice/tree'; import { UmbTreeServerDataSourceBase } from '@umbraco-cms/backoffice/tree'; /** @@ -28,16 +29,17 @@ export class UmbDocumentRecycleBinTreeServerDataSource extends UmbTreeServerData } } -// eslint-disable-next-line local-rules/no-direct-api-import -const getRootItems = () => DocumentResource.getRecycleBinDocumentRoot({}); +const getRootItems = (args: UmbTreeRootItemsRequestArgs) => + // eslint-disable-next-line local-rules/no-direct-api-import + DocumentResource.getRecycleBinDocumentRoot({ skip: args.skip, take: args.take }); -const getChildrenOf = (parentUnique: string | null) => { - if (parentUnique === null) { - return getRootItems(); +const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => { + if (args.parentUnique === null) { + return getRootItems(args); } else { // eslint-disable-next-line local-rules/no-direct-api-import return DocumentResource.getRecycleBinDocumentChildren({ - parentId: parentUnique, + parentId: args.parentUnique, }); } }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/tree/manifests.ts index 98b65c8230..6af74406b4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/tree/manifests.ts @@ -29,6 +29,7 @@ const treeStore: ManifestTreeStore = { const tree: ManifestTree = { type: 'tree', + kind: 'default', alias: UMB_DOCUMENT_RECYCLE_BIN_TREE_ALIAS, name: 'Document Recycle Bin Tree', meta: { @@ -38,7 +39,7 @@ const tree: ManifestTree = { const treeItem: ManifestTreeItem = { type: 'treeItem', - kind: 'unique', + kind: 'default', alias: 'Umb.TreeItem.DocumentRecycleBin', name: 'DocumentRecycleBin Tree Item', meta: { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/document-tree.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/document-tree.server.data-source.ts index e724a5dc64..238a334d1b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/document-tree.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/document-tree.server.data-source.ts @@ -1,5 +1,6 @@ import { UMB_DOCUMENT_ENTITY_TYPE } from '../entity.js'; import type { UmbDocumentTreeItemModel } from './types.js'; +import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from '@umbraco-cms/backoffice/tree'; import { UmbTreeServerDataSourceBase } from '@umbraco-cms/backoffice/tree'; import type { DocumentTreeItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { DocumentResource } from '@umbraco-cms/backoffice/external/backend-api'; @@ -29,16 +30,17 @@ export class UmbDocumentTreeServerDataSource extends UmbTreeServerDataSourceBase } } -// eslint-disable-next-line local-rules/no-direct-api-import -const getRootItems = () => DocumentResource.getTreeDocumentRoot({}); +const getRootItems = (args: UmbTreeRootItemsRequestArgs) => + // eslint-disable-next-line local-rules/no-direct-api-import + DocumentResource.getTreeDocumentRoot({ skip: args.skip, take: args.take }); -const getChildrenOf = (parentUnique: string | null) => { - if (parentUnique === null) { - return getRootItems(); +const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => { + if (args.parentUnique === null) { + return getRootItems(args); } else { // eslint-disable-next-line local-rules/no-direct-api-import return DocumentResource.getTreeDocumentChildren({ - parentId: parentUnique, + parentId: args.parentUnique, }); } }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/manifests.ts index 0253a05463..d070bd03ee 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/manifests.ts @@ -1,5 +1,6 @@ import { UMB_DOCUMENT_ENTITY_TYPE, UMB_DOCUMENT_ROOT_ENTITY_TYPE } from '../entity.js'; import { manifests as reloadTreeItemChildrenManifests } from './reload-tree-item-children/manifests.js'; +import { UmbDocumentTreeItemContext } from './tree-item/document-tree-item.context.js'; import type { ManifestRepository, ManifestTree, @@ -27,6 +28,7 @@ const treeStore: ManifestTreeStore = { const tree: ManifestTree = { type: 'tree', + kind: 'default', alias: UMB_DOCUMENT_TREE_ALIAS, name: 'Document Tree', meta: { @@ -38,7 +40,8 @@ const treeItem: ManifestTreeItem = { type: 'treeItem', alias: 'Umb.TreeItem.Document', name: 'Document Tree Item', - js: () => import('./tree-item/document-tree-item.element.js'), + element: () => import('./tree-item/document-tree-item.element.js'), + api: UmbDocumentTreeItemContext, meta: { entityTypes: [UMB_DOCUMENT_ROOT_ENTITY_TYPE, UMB_DOCUMENT_ENTITY_TYPE], }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/tree-item/document-tree-item.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/tree-item/document-tree-item.context.ts index e4d60cda12..e6dada196c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/tree-item/document-tree-item.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/tree-item/document-tree-item.context.ts @@ -1,8 +1,8 @@ import type { UmbDocumentTreeItemModel } from '../types.js'; -import { UmbUniqueTreeItemContext } from '@umbraco-cms/backoffice/tree'; +import { UmbDefaultTreeItemContext } from '@umbraco-cms/backoffice/tree'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -export class UmbDocumentTreeItemContext extends UmbUniqueTreeItemContext { +export class UmbDocumentTreeItemContext extends UmbDefaultTreeItemContext { constructor(host: UmbControllerHost) { super(host); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/tree-item/document-tree-item.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/tree-item/document-tree-item.element.ts index d733c7fccf..096af384fc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/tree-item/document-tree-item.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/tree-item/document-tree-item.element.ts @@ -1,33 +1,12 @@ import type { UmbDocumentTreeItemModel } from '../types.js'; -import { UmbDocumentTreeItemContext } from './document-tree-item.context.js'; -import { css, html, nothing, customElement, property } from '@umbraco-cms/backoffice/external/lit'; +import { css, html, nothing, customElement } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { UmbTreeItemElement } from '@umbraco-cms/backoffice/extension-registry'; +import { UmbTreeItemElementBase } from '@umbraco-cms/backoffice/tree'; @customElement('umb-document-tree-item') -export class UmbDocumentTreeItemElement extends UmbLitElement implements UmbTreeItemElement { - private _item?: UmbDocumentTreeItemModel; - @property({ type: Object, attribute: false }) - public get item() { - return this._item; - } - public set item(value: UmbDocumentTreeItemModel | undefined) { - this._item = value; - this.#context.setTreeItem(value); - } - - #context = new UmbDocumentTreeItemContext(this); - - render() { - if (!this.item) return nothing; - return html` - ${this.#renderIconWithStatusSymbol()} ${this.#renderLabel()} - `; - } - +export class UmbDocumentTreeItemElement extends UmbTreeItemElementBase { // TODO: implement correct status symbol - #renderIconWithStatusSymbol() { + renderIconContainer() { return html` ${this.item?.documentType.icon @@ -42,8 +21,8 @@ export class UmbDocumentTreeItemElement extends UmbLitElement implements UmbTree // TODO: lower opacity if item is not published // TODO: get correct variant name - #renderLabel() { - return html` ${this.item?.variants[0].name} `; + renderLabel() { + return html`${this.item?.variants[0].name} `; } static styles = [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index 8a060619ca..08c5865a48 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -353,12 +353,15 @@ export class UmbDocumentWorkspaceContext } } - async #pickVariantsForAction(type: UmbDocumentVariantPickerModalType): Promise { + async #runUserFlorFor(type: UmbDocumentVariantPickerModalType): Promise { const activeVariants = this.splitView.getActiveVariants(); const activeVariantIds = activeVariants.map((activeVariant) => UmbVariantId.Create(activeVariant)); // TODO: We need to filter the selected array, so it only contains one of each variantId. [NL] - const selected = activeVariantIds.concat(this.#calculateChangedVariants()); + const changedVariantIds = this.#calculateChangedVariants(); + const selected = activeVariantIds.concat(changedVariantIds); + // Selected can contain entries that are not part of the options, therefor the modal filters selection based on options. + const options = await firstValueFrom(this.variantOptions); // If there is only one variant, we don't need to open the modal. @@ -461,21 +464,22 @@ export class UmbDocumentWorkspaceContext } async save() { - await this.#pickVariantsForAction('save'); + await this.#runUserFlorFor('save'); const data = this.getData(); if (!data) throw new Error('Data is missing'); this.#persistedData.setValue(data); this.#currentData.setValue(data); - this.saveComplete(data); + this.workspaceComplete(data); } public async publish() { - const variantIds = await this.#pickVariantsForAction('publish'); + const variantIds = await this.#runUserFlorFor('publish'); const unique = this.getUnique(); if (variantIds.length && unique) { await this.publishingRepository.publish(unique, variantIds); + this.workspaceComplete(this.#currentData.getValue()); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info.element.ts index 86318b9381..457d7466d6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info.element.ts @@ -253,7 +253,6 @@ export class UmbDocumentWorkspaceViewInfoElement extends UmbLitElement { } async #openTemplatePicker() { - console.log(this._allowedTemplates); const modal = this.#modalManagerContext?.open(UMB_TEMPLATE_PICKER_MODAL, { data: { hideTreeRoot: true, diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/tree/manifests.ts index b68b726998..a9fd9e8812 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/tree/manifests.ts @@ -32,6 +32,7 @@ const treeStore: ManifestTreeStore = { const tree: ManifestTree = { type: 'tree', + kind: 'default', alias: UMB_MEDIA_TYPE_TREE_ALIAS, name: 'Media Type Tree', meta: { @@ -41,7 +42,7 @@ const tree: ManifestTree = { const treeItem: ManifestTreeItem = { type: 'treeItem', - kind: 'unique', + kind: 'default', alias: 'Umb.TreeItem.MediaType', name: 'Media Type Tree Item', meta: { diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/tree/media-type-tree.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/tree/media-type-tree.server.data-source.ts index db463397d2..7ca5d45402 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/tree/media-type-tree.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/tree/media-type-tree.server.data-source.ts @@ -3,6 +3,7 @@ import type { UmbMediaTypeTreeItemModel } from './types.js'; import type { MediaTypeTreeItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { MediaTypeResource } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from '@umbraco-cms/backoffice/tree'; import { UmbTreeServerDataSourceBase } from '@umbraco-cms/backoffice/tree'; /** @@ -29,16 +30,17 @@ export class UmbMediaTypeTreeServerDataSource extends UmbTreeServerDataSourceBas } } -// eslint-disable-next-line local-rules/no-direct-api-import -const getRootItems = () => MediaTypeResource.getTreeMediaTypeRoot({}); +const getRootItems = (args: UmbTreeRootItemsRequestArgs) => + // eslint-disable-next-line local-rules/no-direct-api-import + MediaTypeResource.getTreeMediaTypeRoot({ skip: args.skip, take: args.take }); -const getChildrenOf = (parentUnique: string | null) => { - if (parentUnique === null) { - return getRootItems(); +const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => { + if (args.parentUnique === null) { + return getRootItems(args); } else { // eslint-disable-next-line local-rules/no-direct-api-import return MediaTypeResource.getTreeMediaTypeChildren({ - parentId: parentUnique, + parentId: args.parentUnique, }); } }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.context.ts index be0416953a..b8c79e6d5f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.context.ts @@ -153,7 +153,8 @@ export class UmbMediaTypeWorkspaceContext await this.structure.save(); } - this.saveComplete(data); + this.setIsNew(false); + this.workspaceComplete(data); } public destroy(): void { diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-crops-configuration/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-crops-configuration/manifests.ts index 691e2da56f..5fffda0938 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-crops-configuration/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-crops-configuration/manifests.ts @@ -9,6 +9,5 @@ export const manifest: ManifestPropertyEditorUi = { label: 'Image Crops Configuration', icon: 'icon-autofill', group: 'common', - propertyEditorSchemaAlias: 'Umbraco.ImageCropper.Configuration', }, }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-crops-configuration/property-editor-ui-image-crops-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-crops-configuration/property-editor-ui-image-crops-configuration.element.ts index 75bc97b2a9..f712272b8c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-crops-configuration/property-editor-ui-image-crops-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-crops-configuration/property-editor-ui-image-crops-configuration.element.ts @@ -4,6 +4,7 @@ import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extensi import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; export type UmbCrop = { + label: string; alias: string; width: number; height: number; @@ -35,12 +36,14 @@ export class UmbPropertyEditorUIImageCropsConfigurationElement const form = this.shadowRoot?.querySelector('form') as HTMLFormElement; if (!form) return; + const label = form.querySelector('#label') as HTMLInputElement; const alias = form.querySelector('#alias') as HTMLInputElement; const width = form.querySelector('#width') as HTMLInputElement; const height = form.querySelector('#height') as HTMLInputElement; if (!alias || !width || !height) return; + label.value = crop.label; alias.value = crop.alias; width.value = crop.width.toString(); height.value = crop.height.toString(); @@ -59,14 +62,16 @@ export class UmbPropertyEditorUIImageCropsConfigurationElement const formData = new FormData(form); + const label = formData.get('label') as string; const alias = formData.get('alias') as string; const width = formData.get('width') as string; const height = formData.get('height') as string; - if (!alias || !width || !height) return; + if (!label || !alias || !width || !height) return; if (!this.value) this.value = []; const newCrop = { + label, alias, width: parseInt(width), height: parseInt(height), @@ -101,6 +106,10 @@ export class UmbPropertyEditorUIImageCropsConfigurationElement return html`
+
+ Label + +
Alias @@ -127,7 +136,7 @@ export class UmbPropertyEditorUIImageCropsConfigurationElement (item) => html`
+ - ${item.alias} + ${item.label} (${item.alias}) (${item.width} x ${item.height}px)
this.#onEdit(item)}>Edit @@ -163,9 +172,6 @@ export class UmbPropertyEditorUIImageCropsConfigurationElement color: var(--uui-color-disabled-contrast); font-weight: bold; } - .crop-alias { - font-weight: bold; - } .crop-size { font-size: 0.9em; padding-inline: var(--uui-size-space-4); diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/tree/manifests.ts index ec00860ebd..ee11e6f7f6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/tree/manifests.ts @@ -27,6 +27,7 @@ const treeStore: ManifestTreeStore = { const tree: ManifestTree = { type: 'tree', + kind: 'default', alias: UMB_MEDIA_TREE_ALIAS, name: 'Media Tree', meta: { @@ -36,7 +37,7 @@ const tree: ManifestTree = { const treeItem: ManifestTreeItem = { type: 'treeItem', - kind: 'unique', + kind: 'default', alias: 'Umb.TreeItem.Media', name: 'Media Tree Item', meta: { diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/tree/media-tree.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/tree/media-tree.server.data-source.ts index 66bcdce677..dd491954d4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/tree/media-tree.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/tree/media-tree.server.data-source.ts @@ -1,5 +1,6 @@ import { UMB_MEDIA_ENTITY_TYPE } from '../entity.js'; import type { UmbMediaTreeItemModel } from './types.js'; +import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from '@umbraco-cms/backoffice/tree'; import { UmbTreeServerDataSourceBase } from '@umbraco-cms/backoffice/tree'; import { MediaResource, type MediaTreeItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; @@ -28,16 +29,17 @@ export class UmbMediaTreeServerDataSource extends UmbTreeServerDataSourceBase< } } -// eslint-disable-next-line local-rules/no-direct-api-import -const getRootItems = () => MediaResource.getTreeMediaRoot({}); +const getRootItems = (args: UmbTreeRootItemsRequestArgs) => + // eslint-disable-next-line local-rules/no-direct-api-import + MediaResource.getTreeMediaRoot({ skip: args.skip, take: args.take }); -const getChildrenOf = (parentUnique: string | null) => { - if (parentUnique === null) { - return getRootItems(); +const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => { + if (args.parentUnique === null) { + return getRootItems(args); } else { // eslint-disable-next-line local-rules/no-direct-api-import return MediaResource.getTreeMediaChildren({ - parentId: parentUnique, + parentId: args.parentUnique, }); } }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context.ts index 3df9ded52e..948265af11 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context.ts @@ -228,7 +228,8 @@ export class UmbMediaWorkspaceContext const data = this.getData(); if (!data) throw new Error('Data is missing'); await this.#createOrSave(); - this.saveComplete(data); + this.setIsNew(false); + this.workspaceComplete(data); } async delete() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/workspace/member-group-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/workspace/member-group-workspace.context.ts index a2c1042243..876e83f8a3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/workspace/member-group-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/workspace/member-group-workspace.context.ts @@ -61,7 +61,8 @@ export class UmbMemberGroupWorkspaceContext await this.detailRepository.save(data); } - this.saveComplete(data); + this.setIsNew(false); + this.workspaceComplete(data); } getData() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/tree/manifests.ts index eaea29ea76..afd14e1972 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/tree/manifests.ts @@ -29,6 +29,7 @@ const treeStore: ManifestTreeStore = { const tree: ManifestTree = { type: 'tree', + kind: 'default', alias: UMB_MEMBER_TYPE_TREE_ALIAS, name: 'Member Type Tree', meta: { @@ -38,7 +39,7 @@ const tree: ManifestTree = { const treeItem: ManifestTreeItem = { type: 'treeItem', - kind: 'unique', + kind: 'default', alias: 'Umb.TreeItem.MemberType', name: 'Member Type Tree Item', meta: { diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/tree/member-type-tree.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/tree/member-type-tree.server.data-source.ts index 05bdff135d..aa8948ed8c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/tree/member-type-tree.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/tree/member-type-tree.server.data-source.ts @@ -1,5 +1,6 @@ import { UMB_MEMBER_TYPE_ENTITY_TYPE } from '../entity.js'; import type { UmbMemberTypeTreeItemModel } from './types.js'; +import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from '@umbraco-cms/backoffice/tree'; import { UmbTreeServerDataSourceBase } from '@umbraco-cms/backoffice/tree'; import type { NamedEntityTreeItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { MemberTypeResource } from '@umbraco-cms/backoffice/external/backend-api'; @@ -29,12 +30,13 @@ export class UmbMemberTypeTreeServerDataSource extends UmbTreeServerDataSourceBa } } -// eslint-disable-next-line local-rules/no-direct-api-import -const getRootItems = () => MemberTypeResource.getTreeMemberTypeRoot({}); +const getRootItems = (args: UmbTreeRootItemsRequestArgs) => + // eslint-disable-next-line local-rules/no-direct-api-import + MemberTypeResource.getTreeMemberTypeRoot({ skip: args.skip, take: args.take }); -const getChildrenOf = (parentUnique: string | null) => { - if (parentUnique === null) { - return getRootItems(); +const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => { + if (args.parentUnique === null) { + return getRootItems(args); } else { throw new Error('Not supported for the member type tree'); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/member-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/member-type-workspace.context.ts index b53ac9deab..bbad734734 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/member-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/member-type-workspace.context.ts @@ -74,7 +74,8 @@ export class UmbMemberTypeWorkspaceContext await this.detailRepository.save(data); } - this.saveComplete(data); + this.setIsNew(false); + this.workspaceComplete(data); } getData() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member-workspace.context.ts index afdbe7e572..9ac78ffc49 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member-workspace.context.ts @@ -68,7 +68,8 @@ export class UmbMemberWorkspaceContext await this.repository.save(data); } - this.saveComplete(data); + this.setIsNew(false); + this.workspaceComplete(data); } // Only for CRUD demonstration purposes diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/tree/manifests.ts index 56c3e6eb4f..b837545824 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/tree/manifests.ts @@ -27,6 +27,7 @@ const treeStore: ManifestTreeStore = { const tree: ManifestTree = { type: 'tree', + kind: 'default', alias: UMB_RELATION_TYPE_TREE_ALIAS, name: 'Relation Type Tree', meta: { @@ -36,7 +37,7 @@ const tree: ManifestTree = { const treeItem: ManifestTreeItem = { type: 'treeItem', - kind: 'unique', + kind: 'default', alias: 'Umb.TreeItem.RelationType', name: 'RelationType Tree Item', meta: { diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/tree/relation-type-tree.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/tree/relation-type-tree.server.data-source.ts index 9bcf4d684c..d513e4ccb3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/tree/relation-type-tree.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/tree/relation-type-tree.server.data-source.ts @@ -2,6 +2,7 @@ import type { UmbRelationTypeTreeItemModel } from './types.js'; import type { NamedEntityTreeItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { RelationTypeResource } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from '@umbraco-cms/backoffice/tree'; import { UmbTreeServerDataSourceBase } from '@umbraco-cms/backoffice/tree'; /** @@ -28,12 +29,13 @@ export class UmbRelationTypeTreeServerDataSource extends UmbTreeServerDataSource } } -// eslint-disable-next-line local-rules/no-direct-api-import -const getRootItems = () => RelationTypeResource.getTreeRelationTypeRoot({}); +const getRootItems = (args: UmbTreeRootItemsRequestArgs) => + // eslint-disable-next-line local-rules/no-direct-api-import + RelationTypeResource.getTreeRelationTypeRoot({ skip: args.skip, take: args.take }); -const getChildrenOf = (parentUnique: string | null) => { - if (parentUnique === null) { - return getRootItems(); +const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => { + if (args.parentUnique === null) { + return getRootItems(args); } else { throw new Error('Not supported for the relation type tree'); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/manifests.ts index fdbb8c36ad..980b7d8fbd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/manifests.ts @@ -31,6 +31,7 @@ const treeStore: ManifestTreeStore = { const tree: ManifestTree = { type: 'tree', + kind: 'default', alias: UMB_STATIC_FILE_TREE_ALIAS, name: 'Static File Tree', meta: { @@ -40,7 +41,7 @@ const tree: ManifestTree = { const treeItem: ManifestTreeItem = { type: 'treeItem', - kind: 'unique', + kind: 'default', alias: UMB_STATIC_FILE_TREE_ITEM_ALIAS, name: 'Static File Tree Item', meta: { diff --git a/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/static-file-tree.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/static-file-tree.server.data-source.ts index 268ce50525..bb153b6af8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/static-file-tree.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/static-file/tree/static-file-tree.server.data-source.ts @@ -1,6 +1,7 @@ import { UMB_STATIC_FILE_ENTITY_TYPE, UMB_STATIC_FILE_FOLDER_ENTITY_TYPE } from '../entity.js'; import type { UmbStaticFileTreeItemModel } from './types.js'; import { UmbServerFilePathUniqueSerializer } from '@umbraco-cms/backoffice/server-file-system'; +import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from '@umbraco-cms/backoffice/tree'; import { UmbTreeServerDataSourceBase } from '@umbraco-cms/backoffice/tree'; import { StaticFileResource, @@ -32,14 +33,15 @@ export class UmbStaticFileTreeServerDataSource extends UmbTreeServerDataSourceBa } } -// eslint-disable-next-line local-rules/no-direct-api-import -const getRootItems = () => StaticFileResource.getTreeStaticFileRoot({}); +const getRootItems = (args: UmbTreeRootItemsRequestArgs) => + // eslint-disable-next-line local-rules/no-direct-api-import + StaticFileResource.getTreeStaticFileRoot({ skip: args.skip, take: args.take }); -const getChildrenOf = (parentUnique: string | null) => { - const parentPath = new UmbServerFilePathUniqueSerializer().toServerPath(parentUnique); +const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => { + const parentPath = new UmbServerFilePathUniqueSerializer().toServerPath(args.parentUnique); if (parentPath === null) { - return getRootItems(); + return getRootItems(args); } else { // eslint-disable-next-line local-rules/no-direct-api-import return StaticFileResource.getTreeStaticFileChildren({ diff --git a/src/Umbraco.Web.UI.Client/src/packages/tags/property-editors/tags/config/storage-type/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/tags/property-editors/tags/config/storage-type/manifests.ts index 15692889bf..c0ed857842 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tags/property-editors/tags/config/storage-type/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tags/property-editors/tags/config/storage-type/manifests.ts @@ -8,7 +8,6 @@ export const manifest: ManifestPropertyEditorUi = { js: () => import('./property-editor-ui-tags-storage-type.element.js'), meta: { label: 'Tags Storage Type', - propertyEditorSchemaAlias: '', icon: 'icon-autofill', group: 'common', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/index.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/index.ts index aefe614b56..e1c9b4be07 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/index.ts @@ -1,4 +1,5 @@ export * from './components/index.js'; export * from './templates/index.js'; +export * from './stylesheets/index.js'; export * from './modals/index.js'; export * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/tree/manifests.ts index 31f220b973..28fbf69294 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/tree/manifests.ts @@ -32,6 +32,7 @@ const treeStore: ManifestTreeStore = { const tree: ManifestTree = { type: 'tree', + kind: 'default', alias: UMB_PARTIAL_VIEW_TREE_ALIAS, name: 'Partial View Tree', meta: { @@ -41,7 +42,7 @@ const tree: ManifestTree = { const treeItem: ManifestTreeItem = { type: 'treeItem', - kind: 'unique', + kind: 'default', alias: 'Umb.TreeItem.PartialView', name: 'Partial View Tree Item', meta: { diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/tree/partial-view-tree.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/tree/partial-view-tree.server.data-source.ts index 78885a7c05..0780960c72 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/tree/partial-view-tree.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/tree/partial-view-tree.server.data-source.ts @@ -1,6 +1,7 @@ import { UMB_PARTIAL_VIEW_ENTITY_TYPE, UMB_PARTIAL_VIEW_FOLDER_ENTITY_TYPE } from '../entity.js'; import type { UmbPartialViewTreeItemModel } from './types.js'; import { UmbServerFilePathUniqueSerializer } from '@umbraco-cms/backoffice/server-file-system'; +import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from '@umbraco-cms/backoffice/tree'; import { UmbTreeServerDataSourceBase } from '@umbraco-cms/backoffice/tree'; import type { FileSystemTreeItemPresentationModel } from '@umbraco-cms/backoffice/external/backend-api'; import { PartialViewResource } from '@umbraco-cms/backoffice/external/backend-api'; @@ -30,14 +31,15 @@ export class UmbPartialViewTreeServerDataSource extends UmbTreeServerDataSourceB } } -// eslint-disable-next-line local-rules/no-direct-api-import -const getRootItems = () => PartialViewResource.getTreePartialViewRoot({}); +const getRootItems = (args: UmbTreeRootItemsRequestArgs) => + // eslint-disable-next-line local-rules/no-direct-api-import + PartialViewResource.getTreePartialViewRoot({ skip: args.skip, take: args.take }); -const getChildrenOf = (parentUnique: string | null) => { - const parentPath = new UmbServerFilePathUniqueSerializer().toServerPath(parentUnique); +const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => { + const parentPath = new UmbServerFilePathUniqueSerializer().toServerPath(args.parentUnique); if (parentPath === null) { - return getRootItems(); + return getRootItems(args); } else { // eslint-disable-next-line local-rules/no-direct-api-import return PartialViewResource.getTreePartialViewChildren({ diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts index 6371ca98e8..1720214e3b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts @@ -123,7 +123,8 @@ export class UmbPartialViewWorkspaceContext if (newData) { this.#data.setValue(newData); - this.saveComplete(newData); + this.setIsNew(false); + this.workspaceComplete(newData); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/tree/manifests.ts index f6f6c03a56..18d5517377 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/tree/manifests.ts @@ -28,6 +28,7 @@ const treeStore: ManifestTreeStore = { const tree: ManifestTree = { type: 'tree', + kind: 'default', alias: UMB_SCRIPT_TREE_ALIAS, name: 'Script Tree', meta: { @@ -37,7 +38,7 @@ const tree: ManifestTree = { const treeItem: ManifestTreeItem = { type: 'treeItem', - kind: 'unique', + kind: 'default', alias: 'Umb.TreeItem.Script', name: 'Script Tree Item', meta: { diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/tree/script-tree.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/tree/script-tree.server.data-source.ts index e5786fd862..af3b7c3f5c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/tree/script-tree.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/tree/script-tree.server.data-source.ts @@ -1,6 +1,7 @@ import { UMB_SCRIPT_ENTITY_TYPE, UMB_SCRIPT_FOLDER_ENTITY_TYPE } from '../entity.js'; import type { UmbScriptTreeItemModel } from './types.js'; import { UmbServerFilePathUniqueSerializer } from '@umbraco-cms/backoffice/server-file-system'; +import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from '@umbraco-cms/backoffice/tree'; import { UmbTreeServerDataSourceBase } from '@umbraco-cms/backoffice/tree'; import type { FileSystemTreeItemPresentationModel } from '@umbraco-cms/backoffice/external/backend-api'; import { ScriptResource } from '@umbraco-cms/backoffice/external/backend-api'; @@ -30,14 +31,15 @@ export class UmbScriptTreeServerDataSource extends UmbTreeServerDataSourceBase< } } -// eslint-disable-next-line local-rules/no-direct-api-import -const getRootItems = () => ScriptResource.getTreeScriptRoot({}); +const getRootItems = (args: UmbTreeRootItemsRequestArgs) => + // eslint-disable-next-line local-rules/no-direct-api-import + ScriptResource.getTreeScriptRoot({ skip: args.skip, take: args.take }); -const getChildrenOf = (parentUnique: string | null) => { - const parentPath = new UmbServerFilePathUniqueSerializer().toServerPath(parentUnique); +const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => { + const parentPath = new UmbServerFilePathUniqueSerializer().toServerPath(args.parentUnique); if (parentPath === null) { - return getRootItems(); + return getRootItems(args); } else { // eslint-disable-next-line local-rules/no-direct-api-import return ScriptResource.getTreeScriptChildren({ diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace.context.ts index 7b85448a51..1b32266490 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace.context.ts @@ -108,7 +108,9 @@ export class UmbScriptWorkspaceContext extends UmbEditableWorkspaceContextBase StylesheetResource.getTreeStylesheetRoot({}); +const getRootItems = (args: UmbTreeRootItemsRequestArgs) => + // eslint-disable-next-line local-rules/no-direct-api-import + StylesheetResource.getTreeStylesheetRoot({ skip: args.skip, take: args.take }); -const getChildrenOf = (parentUnique: string | null) => { - const parentPath = new UmbServerFilePathUniqueSerializer().toServerPath(parentUnique); +const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => { + const parentPath = new UmbServerFilePathUniqueSerializer().toServerPath(args.parentUnique); if (parentPath === null) { - return getRootItems(); + return getRootItems(args); } else { // eslint-disable-next-line local-rules/no-direct-api-import return StylesheetResource.getTreeStylesheetChildren({ diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context.ts index a89df8dea5..de210a6cfc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context.ts @@ -116,7 +116,7 @@ export class UmbStylesheetWorkspaceContext if (newData) { this.#data.setValue(newData); - this.saveComplete(newData); + this.workspaceComplete(newData); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/tree/manifests.ts index 7f582a0222..dec71d61e4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/tree/manifests.ts @@ -27,6 +27,7 @@ const treeStore: ManifestTreeStore = { const tree: ManifestTree = { type: 'tree', + kind: 'default', alias: UMB_TEMPLATE_TREE_ALIAS, name: 'Template Tree', meta: { @@ -36,7 +37,7 @@ const tree: ManifestTree = { const treeItem: ManifestTreeItem = { type: 'treeItem', - kind: 'unique', + kind: 'default', alias: 'Umb.TreeItem.Template', name: 'Template Tree Item', meta: { diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/tree/template-tree.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/tree/template-tree.server.data-source.ts index 9b86bb4d07..a8c15cc1b1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/tree/template-tree.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/tree/template-tree.server.data-source.ts @@ -1,5 +1,6 @@ import { UMB_TEMPLATE_ENTITY_TYPE } from '../entity.js'; import type { UmbTemplateTreeItemModel } from './types.js'; +import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from '@umbraco-cms/backoffice/tree'; import { UmbTreeServerDataSourceBase } from '@umbraco-cms/backoffice/tree'; import type { NamedEntityTreeItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { TemplateResource } from '@umbraco-cms/backoffice/external/backend-api'; @@ -29,16 +30,17 @@ export class UmbTemplateTreeServerDataSource extends UmbTreeServerDataSourceBase } } -// eslint-disable-next-line local-rules/no-direct-api-import -const getRootItems = () => TemplateResource.getTreeTemplateRoot({}); +const getRootItems = (args: UmbTreeRootItemsRequestArgs) => + // eslint-disable-next-line local-rules/no-direct-api-import + TemplateResource.getTreeTemplateRoot({ skip: args.skip, take: args.take }); -const getChildrenOf = (parentUnique: string | null) => { - if (parentUnique === null) { - return getRootItems(); +const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => { + if (args.parentUnique === null) { + return getRootItems(args); } else { // eslint-disable-next-line local-rules/no-direct-api-import return TemplateResource.getTreeTemplateChildren({ - parentId: parentUnique, + parentId: args.parentUnique, }); } }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace.context.ts index 7978356cee..0093016c50 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace.context.ts @@ -175,7 +175,7 @@ ${currentContent}`; if (newData) { this.#data.setValue(newData); - this.saveComplete(newData); + this.workspaceComplete(newData); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/umbraco-package.ts index 7c36d4038e..bb38899cf5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/umbraco-package.ts @@ -1,3 +1,5 @@ +import './index.js'; + export const name = 'Umbraco.Core.Templating'; export const extensions = [ { diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-linkpicker.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-linkpicker.plugin.ts index df05ce8762..ae6bfd8ee9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-linkpicker.plugin.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-linkpicker.plugin.ts @@ -105,7 +105,8 @@ export default class UmbTinyMceLinkPickerPlugin extends UmbTinyMcePluginBase { const linkPickerData = await modalHandler.onSubmit().catch(() => undefined); if (!linkPickerData) return; - this.#linkPickerData = linkPickerData; + // TODO: This is a workaround for the issue where the link picker modal is returning a frozen object, and we need to extract the link into smaller parts to avoid the frozen object issue. + this.#linkPickerData = { link: { ...linkPickerData.link } }; this.#updateLink(); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/manifests.ts index ed50bb5c09..0e30b9b8ee 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/manifests.ts @@ -12,7 +12,6 @@ export const manifests: Array = [ js: () => import('./toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.js'), meta: { label: 'TinyMCE Toolbar Configuration', - propertyEditorSchemaAlias: 'Umbraco.RichText.Configuration', icon: 'icon-autofill', group: 'common', }, @@ -24,7 +23,6 @@ export const manifests: Array = [ js: () => import('./stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.js'), meta: { label: 'TinyMCE Stylesheets Configuration', - propertyEditorSchemaAlias: 'Umbraco.RichText.Configuration', icon: 'icon-autofill', group: 'common', }, @@ -36,7 +34,6 @@ export const manifests: Array = [ js: () => import('./dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.js'), meta: { label: 'TinyMCE Dimensions Configuration', - propertyEditorSchemaAlias: 'Umbraco.RichText.Configuration', icon: 'icon-autofill', group: 'common', }, @@ -48,7 +45,6 @@ export const manifests: Array = [ js: () => import('./max-image-size/property-editor-ui-tiny-mce-maximagesize-configuration.element.js'), meta: { label: 'TinyMCE Max Image Size Configuration', - propertyEditorSchemaAlias: 'Umbraco.RichText.Configuration', icon: 'icon-autofill', group: 'common', }, 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 338f11af6a..061aef02a3 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 @@ -231,12 +231,6 @@ export const manifest: ManifestPropertyEditorUi = { label: 'Hide Label', propertyEditorUiAlias: 'Umb.PropertyEditorUi.Toggle', }, - { - alias: 'blocks', - label: 'Available Blocks', - description: 'Define the available blocks.', - propertyEditorUiAlias: 'Umb.PropertyEditorUi.BlockRteTypeConfiguration', - }, ], }, }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/umbraco-package.ts index ba0cf8d5f2..1ef4351731 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/umbraco-package.ts @@ -1,3 +1,5 @@ +import './index.js'; + export const name = 'Umbraco.Core.UmbracoNews'; export const extensions = [ { diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace.context.ts index 9863a34596..2b1a3249f4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace.context.ts @@ -85,7 +85,8 @@ export class UmbUserWorkspaceContext if (newData) { this.#persistedData.setValue(newData); this.#currentData.setValue(newData); - this.saveComplete(newData); + this.setIsNew(false); + this.workspaceComplete(newData); } }