From ab2d551af34cef71219673129e534b91184fb5c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 3 Feb 2023 11:54:26 +0100 Subject: [PATCH 1/3] ClassConstructor and ManifestClass type --- .../libs/extensions-registry/models.ts | 104 ++++++++++-------- .../extensions-registry/repository.models.ts | 5 + .../libs/extensions-registry/tree.models.ts | 4 +- .../libs/models/index.ts | 10 +- 4 files changed, 66 insertions(+), 57 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/libs/extensions-registry/repository.models.ts diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-registry/models.ts b/src/Umbraco.Web.UI.Client/libs/extensions-registry/models.ts index 0b8f3a8890..daaef92105 100644 --- a/src/Umbraco.Web.UI.Client/libs/extensions-registry/models.ts +++ b/src/Umbraco.Web.UI.Client/libs/extensions-registry/models.ts @@ -1,75 +1,78 @@ +import type { ManifestCollectionBulkAction } from './collection-bulk-action.models'; +import type { ManifestCollectionView } from './collection-view.models'; +import type { ManifestDashboard } from './dashboard.models'; +import type { ManifestDashboardCollection } from './dashboard-collection.models'; +import type { ManifestEntityAction } from './entity-action.models'; +import type { ManifestExternalLoginProvider } from './external-login-provider.models'; import type { ManifestHeaderApp } from './header-app.models'; +import type { ManifestHealthCheck } from './health-check.models'; +import type { ManifestPackageView } from './package-view.models'; +import type { ManifestPropertyAction } from './property-action.models'; +import type { ManifestPropertyEditorUI, ManifestPropertyEditorModel } from './property-editor.models'; import type { ManifestSection } from './section.models'; import type { ManifestSectionView } from './section-view.models'; +import type { ManifestSidebarMenuItem } from './sidebar-menu-item.models'; +import type { ManifestTheme } from './theme.models'; import type { ManifestTree } from './tree.models'; import type { ManifestTreeItemAction } from './tree-item-action.models'; +import type { ManifestUserDashboard } from './user-dashboard.models'; import type { ManifestWorkspace } from './workspace.models'; import type { ManifestWorkspaceAction } from './workspace-action.models'; import type { ManifestWorkspaceView } from './workspace-view.models'; import type { ManifestWorkspaceViewCollection } from './workspace-view-collection.models'; -import type { ManifestPropertyEditorUI, ManifestPropertyEditorModel } from './property-editor.models'; -import type { ManifestDashboard } from './dashboard.models'; -import type { ManifestDashboardCollection } from './dashboard-collection.models'; -import type { ManifestUserDashboard } from './user-dashboard.models'; -import type { ManifestPropertyAction } from './property-action.models'; -import type { ManifestPackageView } from './package-view.models'; -import type { ManifestExternalLoginProvider } from './external-login-provider.models'; -import type { ManifestCollectionBulkAction } from './collection-bulk-action.models'; -import type { ManifestCollectionView } from './collection-view.models'; -import type { ManifestHealthCheck } from './health-check.models'; -import type { ManifestSidebarMenuItem } from './sidebar-menu-item.models'; -import type { ManifestTheme } from './theme.models'; -import type { ManifestEntityAction } from './entity-action.models'; +import { ManifestRepository } from './repository.models'; +import type { ClassConstructor } from '@umbraco-cms/models'; -export * from './header-app.models'; -export * from './section.models'; -export * from './section-view.models'; -export * from './tree.models'; -export * from './tree-item-action.models'; -export * from './workspace.models'; -export * from './workspace-action.models'; -export * from './workspace-view.models'; -export * from './workspace-view-collection.models'; -export * from './property-editor.models'; -export * from './dashboard.models'; -export * from './dashboard-collection.models'; -export * from './user-dashboard.models'; -export * from './property-action.models'; -export * from './package-view.models'; -export * from './external-login-provider.models'; export * from './collection-bulk-action.models'; export * from './collection-view.models'; +export * from './dashboard-collection.models'; +export * from './dashboard.models'; +export * from './entity-action.models'; +export * from './external-login-provider.models'; +export * from './header-app.models'; export * from './health-check.models'; +export * from './package-view.models'; +export * from './property-action.models'; +export * from './property-editor.models'; +export * from './section-view.models'; +export * from './section.models'; export * from './sidebar-menu-item.models'; export * from './theme.models'; -export * from './entity-action.models'; +export * from './tree-item-action.models'; +export * from './tree.models'; +export * from './user-dashboard.models'; +export * from './workspace-action.models'; +export * from './workspace-view-collection.models'; +export * from './workspace-view.models'; +export * from './workspace.models'; export type ManifestTypes = + | ManifestCollectionBulkAction + | ManifestCollectionView | ManifestCustom + | ManifestDashboard + | ManifestDashboardCollection + | ManifestEntityAction + | ManifestEntrypoint + | ManifestExternalLoginProvider | ManifestHeaderApp + | ManifestHealthCheck + | ManifestPackageView + | ManifestPropertyAction + | ManifestPropertyEditorModel + | ManifestPropertyEditorUI + | ManifestRepository | ManifestSection | ManifestSectionView + | ManifestSidebarMenuItem + | ManifestTheme | ManifestTree + | ManifestTreeItemAction + | ManifestUserDashboard | ManifestWorkspace | ManifestWorkspaceAction | ManifestWorkspaceView - | ManifestWorkspaceViewCollection - | ManifestTreeItemAction - | ManifestPropertyEditorUI - | ManifestPropertyEditorModel - | ManifestDashboard - | ManifestDashboardCollection - | ManifestUserDashboard - | ManifestPropertyAction - | ManifestPackageView - | ManifestExternalLoginProvider - | ManifestEntrypoint - | ManifestCollectionBulkAction - | ManifestCollectionView - | ManifestHealthCheck - | ManifestSidebarMenuItem - | ManifestTheme - | ManifestEntityAction; + | ManifestWorkspaceViewCollection; export type ManifestStandardTypes = ManifestTypes['type']; @@ -88,6 +91,13 @@ export interface ManifestWithLoader extends ManifestBase { loader?: () => Promise; } +export interface ManifestClass extends ManifestWithLoader { + type: ManifestStandardTypes; + js?: string; + className?: string; + class?: ClassConstructor; + //loader?: () => Promise; +} export interface ManifestElement extends ManifestWithLoader { type: ManifestStandardTypes; js?: string; diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-registry/repository.models.ts b/src/Umbraco.Web.UI.Client/libs/extensions-registry/repository.models.ts new file mode 100644 index 0000000000..7f4847dd74 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/extensions-registry/repository.models.ts @@ -0,0 +1,5 @@ +import type { ManifestClass } from './models'; + +export interface ManifestRepository extends ManifestClass { + type: 'repository'; +} diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-registry/tree.models.ts b/src/Umbraco.Web.UI.Client/libs/extensions-registry/tree.models.ts index 017bede8bd..b0f83e99f7 100644 --- a/src/Umbraco.Web.UI.Client/libs/extensions-registry/tree.models.ts +++ b/src/Umbraco.Web.UI.Client/libs/extensions-registry/tree.models.ts @@ -1,5 +1,5 @@ import type { ManifestBase } from './models'; -import type { UmbRepositoryFactory } from '@umbraco-cms/models'; +import type { ClassConstructor } from '@umbraco-cms/models'; export interface ManifestTree extends ManifestBase { type: 'tree'; @@ -8,5 +8,5 @@ export interface ManifestTree extends ManifestBase { export interface MetaTree { storeAlias?: string; - repository?: UmbRepositoryFactory; + repository?: ClassConstructor; } diff --git a/src/Umbraco.Web.UI.Client/libs/models/index.ts b/src/Umbraco.Web.UI.Client/libs/models/index.ts index 22756bffb9..fcf4400f8f 100644 --- a/src/Umbraco.Web.UI.Client/libs/models/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/models/index.ts @@ -4,11 +4,8 @@ import { DocumentTypeTreeItem, EntityTreeItem, FolderTreeItem, - PagedEntityTreeItem, ProblemDetails, } from '@umbraco-cms/backend-api'; -import { UmbTreeRepository } from 'libs/repository/tree-repository.interface'; -import { Observable } from 'rxjs'; // Extension Manifests export * from '@umbraco-cms/extensions-registry'; @@ -16,6 +13,8 @@ export * from '@umbraco-cms/extensions-registry'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type HTMLElementConstructor = new (...args: any[]) => T; +export type ClassConstructor = new (...args: any[]) => T; + // Users // TODO: would the right name be Node? as entity is just something with a Key. But node is something in a content structure, aka. with hasChildren and parentKey. export interface Entity { @@ -158,8 +157,3 @@ export interface DataSourceResponse { data?: T; error?: ProblemDetails; } - -// TODO; figure out why we can't add UmbControllerHostInterface as host type -export interface UmbRepositoryFactory { - new (host: any): UmbTreeRepository; -} From 5f204d5f171b27e78eeeb20395de117eb2331675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 3 Feb 2023 14:17:24 +0100 Subject: [PATCH 2/3] create extension class --- .../create-extension-class.function.ts | 27 +++++++++++++++++++ .../create-extension-element.function.ts | 6 ++--- .../has-default-export.function.ts | 4 +-- ...s-manifest-class-instance-type.function.ts | 7 +++++ .../is-manifest-classable-type.function.ts | 8 ++++++ .../libs/extensions-registry/models.ts | 5 ++++ 6 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/libs/extensions-api/create-extension-class.function.ts create mode 100644 src/Umbraco.Web.UI.Client/libs/extensions-api/is-manifest-class-instance-type.function.ts create mode 100644 src/Umbraco.Web.UI.Client/libs/extensions-api/is-manifest-classable-type.function.ts diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-api/create-extension-class.function.ts b/src/Umbraco.Web.UI.Client/libs/extensions-api/create-extension-class.function.ts new file mode 100644 index 0000000000..cd5c3e1b58 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/extensions-api/create-extension-class.function.ts @@ -0,0 +1,27 @@ +import type { ClassConstructor, ManifestClass } from '../models'; +import { hasDefaultExport } from './has-default-export.function'; +import { isManifestClassConstructorType } from './is-manifest-class-instance-type.function'; +import { loadExtension } from './load-extension.function'; + +//TODO: Write tests for this method: +export async function createExtensionClass(manifest: ManifestClass): Promise { + + const js = await loadExtension(manifest); + + if (isManifestClassConstructorType(manifest)) { + return new manifest.class() as T; + } + + if (js) { + if (hasDefaultExport>(js)) { + return new js.default(); + } + + console.error('-- Extension did not succeed creating an class instance, missing a default export of the served JavaScript file', manifest); + + return undefined; + } + + console.error('-- Extension did not succeed creating an class instance, missing a default export or `class` in the manifest.', manifest); + return undefined; +} diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-api/create-extension-element.function.ts b/src/Umbraco.Web.UI.Client/libs/extensions-api/create-extension-element.function.ts index 9bdf96dc5c..2cd7f10c0d 100644 --- a/src/Umbraco.Web.UI.Client/libs/extensions-api/create-extension-element.function.ts +++ b/src/Umbraco.Web.UI.Client/libs/extensions-api/create-extension-element.function.ts @@ -1,10 +1,10 @@ -import type { ManifestElement } from '../models'; +import type { HTMLElementConstructor, ManifestElement } from '../models'; import { hasDefaultExport } from './has-default-export.function'; import { isManifestElementNameType } from './is-manifest-element-name-type.function'; import { loadExtension } from './load-extension.function'; export async function createExtensionElement(manifest: ManifestElement): Promise { - + //TODO: Write tests for these extension options: const js = await loadExtension(manifest); @@ -15,7 +15,7 @@ export async function createExtensionElement(manifest: ManifestElement): Promise // TODO: Do we need this except for the default() loader? if (js) { - if (hasDefaultExport(js)) { + if (hasDefaultExport(js)) { // created by default class return new js.default(); } diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-api/has-default-export.function.ts b/src/Umbraco.Web.UI.Client/libs/extensions-api/has-default-export.function.ts index de21ac631b..2353e38f4c 100644 --- a/src/Umbraco.Web.UI.Client/libs/extensions-api/has-default-export.function.ts +++ b/src/Umbraco.Web.UI.Client/libs/extensions-api/has-default-export.function.ts @@ -1,5 +1,3 @@ -import type { HTMLElementConstructor } from '../models'; - -export function hasDefaultExport(object: unknown): object is { default: HTMLElementConstructor } { +export function hasDefaultExport(object: unknown): object is { default: ConstructorType } { return typeof object === 'object' && object !== null && 'default' in object; } diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-api/is-manifest-class-instance-type.function.ts b/src/Umbraco.Web.UI.Client/libs/extensions-api/is-manifest-class-instance-type.function.ts new file mode 100644 index 0000000000..a66cd5de85 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/extensions-api/is-manifest-class-instance-type.function.ts @@ -0,0 +1,7 @@ +import type { ManifestClass, ManifestClassWithClassConstructor } from '../models'; + +export function isManifestClassConstructorType(manifest: unknown): manifest is ManifestClassWithClassConstructor { + return ( + typeof manifest === 'object' && manifest !== null && (manifest as ManifestClass).class !== undefined + ); +} diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-api/is-manifest-classable-type.function.ts b/src/Umbraco.Web.UI.Client/libs/extensions-api/is-manifest-classable-type.function.ts new file mode 100644 index 0000000000..e6f247b3f4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/extensions-api/is-manifest-classable-type.function.ts @@ -0,0 +1,8 @@ +import { isManifestJSType } from './is-manifest-js-type.function'; +import { isManifestLoaderType } from './is-manifest-loader-type.function'; +import { isManifestClassConstructorType } from './is-manifest-class-instance-type.function'; +import type { ManifestBase, ManifestClass } from '@umbraco-cms/extensions-registry'; + +export function isManifestClassableType(manifest: ManifestBase): manifest is ManifestClass { + return isManifestClassConstructorType(manifest) || isManifestLoaderType(manifest) || isManifestJSType(manifest); +} diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-registry/models.ts b/src/Umbraco.Web.UI.Client/libs/extensions-registry/models.ts index daaef92105..bd71c6c25b 100644 --- a/src/Umbraco.Web.UI.Client/libs/extensions-registry/models.ts +++ b/src/Umbraco.Web.UI.Client/libs/extensions-registry/models.ts @@ -98,6 +98,11 @@ export interface ManifestClass extends ManifestWithLoader { class?: ClassConstructor; //loader?: () => Promise; } + +export interface ManifestClassWithClassConstructor extends ManifestClass { + class: ClassConstructor; +} + export interface ManifestElement extends ManifestWithLoader { type: ManifestStandardTypes; js?: string; From 3de8d31e96cfc732edbe8a150d6d5252404c1329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 3 Feb 2023 15:00:41 +0100 Subject: [PATCH 3/3] initial collection for Documents with repository --- .../create-extension-class.function.ts | 6 +- .../workspace-view-collection.models.ts | 4 +- .../documents/documents/manifests.ts | 2 + .../documents/repository/manifests.ts | 13 ++ .../workspace/document-workspace.context.ts | 8 +- .../workspace/document-workspace.element.ts | 2 +- .../documents/workspace/manifests.ts | 22 ++- .../shared/collection/collection.context.ts | 40 +++-- .../collection-view-document-table.element.ts | 139 ++++++++++++++++++ .../shared/collection/views/manifests.ts | 18 +++ .../workspace-view-collection.element.ts | 3 +- 11 files changed, 238 insertions(+), 19 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-document-table.element.ts diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-api/create-extension-class.function.ts b/src/Umbraco.Web.UI.Client/libs/extensions-api/create-extension-class.function.ts index cd5c3e1b58..8057ab4b62 100644 --- a/src/Umbraco.Web.UI.Client/libs/extensions-api/create-extension-class.function.ts +++ b/src/Umbraco.Web.UI.Client/libs/extensions-api/create-extension-class.function.ts @@ -4,17 +4,17 @@ import { isManifestClassConstructorType } from './is-manifest-class-instance-typ import { loadExtension } from './load-extension.function'; //TODO: Write tests for this method: -export async function createExtensionClass(manifest: ManifestClass): Promise { +export async function createExtensionClass(manifest: ManifestClass, constructorArguments: unknown[]): Promise { const js = await loadExtension(manifest); if (isManifestClassConstructorType(manifest)) { - return new manifest.class() as T; + return new manifest.class(...constructorArguments) as T; } if (js) { if (hasDefaultExport>(js)) { - return new js.default(); + return new js.default(...constructorArguments); } console.error('-- Extension did not succeed creating an class instance, missing a default export of the served JavaScript file', manifest); diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-registry/workspace-view-collection.models.ts b/src/Umbraco.Web.UI.Client/libs/extensions-registry/workspace-view-collection.models.ts index b14f232a5c..366dd3cdbe 100644 --- a/src/Umbraco.Web.UI.Client/libs/extensions-registry/workspace-view-collection.models.ts +++ b/src/Umbraco.Web.UI.Client/libs/extensions-registry/workspace-view-collection.models.ts @@ -5,11 +5,13 @@ export interface ManifestWorkspaceViewCollection extends ManifestBase { meta: MetaEditorViewCollection; } +// TODO: Get rid of store alias, when we are done migrating to repositories(remember to enforce repositoryAlias): export interface MetaEditorViewCollection { workspaces: string[]; pathname: string; label: string; icon: string; entityType: string; - storeAlias: string; + storeAlias?: string; + repositoryAlias?: string; } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/manifests.ts index 84d347e156..891ab35e74 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/manifests.ts @@ -1,4 +1,5 @@ import { manifests as sidebarMenuItemManifests } from './sidebar-menu-item/manifests'; +import { manifests as repositoryManifests } from './repository/manifests'; import { manifests as treeManifests } from './tree/manifests'; import { manifests as workspaceManifests } from './workspace/manifests'; import { manifests as entityActionManifests } from './entity-actions/manifests'; @@ -6,6 +7,7 @@ import { manifests as entityActionManifests } from './entity-actions/manifests'; export const manifests = [ ...sidebarMenuItemManifests, ...treeManifests, + ...repositoryManifests, ...workspaceManifests, ...entityActionManifests, ]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/manifests.ts new file mode 100644 index 0000000000..9df7f79d54 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/manifests.ts @@ -0,0 +1,13 @@ +import { UmbDocumentRepository } from '../repository/document.repository'; +import { ManifestRepository } from 'libs/extensions-registry/repository.models'; + +export const DOCUMENT_REPOSITORY_ALIAS = 'Umb.Repository.Documents'; + +const repository: ManifestRepository = { + type: 'repository', + alias: DOCUMENT_REPOSITORY_ALIAS, + name: 'Documents Repository', + class: UmbDocumentRepository, +}; + +export const manifests = [repository]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts index 0654ce12f4..75b136ec3e 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts @@ -1,6 +1,6 @@ import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context'; import { UmbDocumentRepository } from '../repository/document.repository'; -import { UmbWorkspaceContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-context.interface'; +import type { UmbWorkspaceEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-entity-context.interface'; import type { DocumentDetails } from '@umbraco-cms/models'; import { appendToFrozenArray, ObjectState } from '@umbraco-cms/observable-api'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -8,7 +8,7 @@ import { UmbControllerHostInterface } from '@umbraco-cms/controller'; // TODO: should this contex be called DocumentDraft instead of workspace? or should the draft be part of this? type EntityType = DocumentDetails; -export class UmbDocumentWorkspaceContext extends UmbWorkspaceContext implements UmbWorkspaceContextInterface { +export class UmbDocumentWorkspaceContext extends UmbWorkspaceContext implements UmbWorkspaceEntityContextInterface { #host: UmbControllerHostInterface; #templateDetailRepo: UmbDocumentRepository; @@ -33,6 +33,10 @@ export class UmbDocumentWorkspaceContext extends UmbWorkspaceContext implements } */ + getEntityKey() { + return this.getData()?.key || ''; + } + getEntityType() { return 'document'; } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.element.ts index e3d635ef3f..126c82d915 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.element.ts @@ -1,6 +1,6 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html, nothing } from 'lit'; -import { customElement, property, state } from 'lit/decorators.js'; +import { customElement, state } from 'lit/decorators.js'; import type { UmbWorkspaceEntityElement } from '../../../shared/components/workspace/workspace-entity-element.interface'; import { UmbDocumentWorkspaceContext } from './document-workspace.context'; import { UmbLitElement } from '@umbraco-cms/element'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/manifests.ts index 40045ad298..0c58e5719b 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/manifests.ts @@ -1,4 +1,5 @@ -import type { ManifestWorkspace, ManifestWorkspaceAction, ManifestWorkspaceView } from '@umbraco-cms/models'; +import { DOCUMENT_REPOSITORY_ALIAS } from '../repository/manifests'; +import type { ManifestWorkspace, ManifestWorkspaceAction, ManifestWorkspaceView, ManifestWorkspaceViewCollection } from '@umbraco-cms/models'; const workspace: ManifestWorkspace = { type: 'workspace', @@ -41,6 +42,23 @@ const workspaceViews: Array = [ }, ]; +const workspaceViewCollections: Array = [ + { + type: 'workspaceViewCollection', + alias: 'Umb.WorkspaceView.Document.Collection', + name: 'Document Workspace Collection View', + weight: 300, + meta: { + workspaces: ['Umb.Workspace.Document'], + label: 'Documents', + pathname: 'collection', + icon: 'umb:grid', + entityType: 'document', + repositoryAlias: DOCUMENT_REPOSITORY_ALIAS + }, + }, +]; + const workspaceActions: Array = [ { type: 'workspaceAction', @@ -77,4 +95,4 @@ const workspaceActions: Array = [ }, ]; -export const manifests = [workspace, ...workspaceViews, ...workspaceActions]; +export const manifests = [workspace, ...workspaceViews, ...workspaceViewCollections, ...workspaceActions]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts index ed0477d569..1e9fe47243 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts @@ -3,6 +3,9 @@ import { UmbTreeStore } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; import { UmbContextToken, UmbContextConsumerController } from '@umbraco-cms/context-api'; import { ArrayState, UmbObserverController } from '@umbraco-cms/observable-api'; +import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; +import { createExtensionClass } from 'libs/extensions-api/create-extension-class.function'; +import { UmbTreeRepository } from '@umbraco-cms/repository'; export class UmbCollectionContext< DataType extends ContentTreeItem, StoreType extends UmbTreeStore = UmbTreeStore @@ -11,6 +14,8 @@ export class UmbCollectionContext< private _host: UmbControllerHostInterface; private _entityKey: string | null; + #repository?: UmbTreeRepository; + private _store?: StoreType; protected _dataObserver?: UmbObserverController; @@ -26,18 +31,35 @@ export class UmbCollectionContext< public readonly search = this._search.asObservable(); */ - constructor(host: UmbControllerHostInterface, entityKey: string | null, storeAlias: string) { + constructor(host: UmbControllerHostInterface, entityKey: string | null, storeAlias?: string, repositoryAlias?: string) { this._host = host; this._entityKey = entityKey; - new UmbContextConsumerController(this._host, storeAlias, (_instance: StoreType) => { - this._store = _instance; - if (!this._store) { - // TODO: if we keep the type assumption of _store existing, then we should here make sure to break the application in a good way. - return; - } - this._onStoreSubscription(); - }); + if(storeAlias) { + new UmbContextConsumerController(this._host, storeAlias, (_instance: StoreType) => { + this._store = _instance; + if (!this._store) { + // TODO: if we keep the type assumption of _store existing, then we should here make sure to break the application in a good way. + return; + } + this._onStoreSubscription(); + }); + } else if (repositoryAlias) { + console.log("has repo alias:", repositoryAlias); + new UmbObserverController(this._host, + umbExtensionsRegistry.getByTypeAndAlias('repository', repositoryAlias), + async (repositoryManifest) => { + // Do something.. + if(repositoryManifest) { + // TODO: use the right interface here, we might need a collection repository interface. + const result = await createExtensionClass(repositoryManifest, [this._host]); + this.#repository = result; + console.log("this.#repository", this.#repository) + } + } + ); + + } } /* diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-document-table.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-document-table.element.ts new file mode 100644 index 0000000000..64fffb7c7b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-document-table.element.ts @@ -0,0 +1,139 @@ +import { UUITextStyles } from '@umbraco-ui/uui-css'; +import { css, html } from 'lit'; +import { customElement, state } from 'lit/decorators.js'; +import { UmbCollectionContext, UMB_COLLECTION_CONTEXT_TOKEN } from '../collection.context'; +import { + UmbTableColumn, + UmbTableConfig, + UmbTableDeselectedEvent, + UmbTableElement, + UmbTableItem, + UmbTableOrderedEvent, + UmbTableSelectedEvent, +} from '../../components/table'; +import type { DocumentDetails } from '@umbraco-cms/models'; +import { UmbLitElement } from '@umbraco-cms/element'; + +type EntityType = DocumentDetails; + +@customElement('umb-collection-view-document-table') +export class UmbCollectionViewDocumentTableElement extends UmbLitElement { + + static styles = [ + UUITextStyles, + css` + :host { + display: block; + box-sizing: border-box; + height: 100%; + width: 100%; + padding: var(--uui-size-space-3) var(--uui-size-space-6); + } + + /* TODO: Should we have embedded padding in the table component? */ + umb-table { + padding: 0; /* To fix the embedded padding in the table component. */ + } + `, + ]; + + @state() + private _items?: Array; + + @state() + private _tableConfig: UmbTableConfig = { + allowSelection: true, + }; + + @state() + private _tableColumns: Array = [ + { + name: 'Name', + alias: 'entityName', + }, + ]; + + @state() + private _tableItems: Array = []; + + @state() + private _selection: Array = []; + + private _collectionContext?: UmbCollectionContext; + + constructor() { + super(); + this.consumeContext(UMB_COLLECTION_CONTEXT_TOKEN, (instance) => { + this._collectionContext = instance; + this._observeCollectionContext(); + }); + } + + private _observeCollectionContext() { + if (!this._collectionContext) return; + + this.observe(this._collectionContext.data, (items) => { + this._items = items; + this._createTableItems(this._items); + }); + + this.observe(this._collectionContext.selection, (selection) => { + this._selection = selection; + }); + } + + private _createTableItems(items: Array) { + this._tableItems = items.map((item) => { + return { + key: item.key, + icon: item.icon, + data: [ + { + columnAlias: 'entityName', + value: item.name || 'Untitled', + }, + ], + }; + }); + } + + private _handleSelect(event: UmbTableSelectedEvent) { + event.stopPropagation(); + const table = event.target as UmbTableElement; + const selection = table.selection; + this._collectionContext?.setSelection(selection); + } + + private _handleDeselect(event: UmbTableDeselectedEvent) { + event.stopPropagation(); + const table = event.target as UmbTableElement; + const selection = table.selection; + this._collectionContext?.setSelection(selection); + } + + private _handleOrdering(event: UmbTableOrderedEvent) { + const table = event.target as UmbTableElement; + const orderingColumn = table.orderingColumn; + const orderingDesc = table.orderingDesc; + console.log(`fetch media items, order column: ${orderingColumn}, desc: ${orderingDesc}`); + } + + render() { + return html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'umb-collection-view-document-table': UmbCollectionViewDocumentTableElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/manifests.ts index 87f14782b3..fcbd10da47 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/manifests.ts @@ -1,6 +1,24 @@ import type { ManifestCollectionView } from '@umbraco-cms/models'; export const manifests: Array = [ + + { + type: 'collectionView', + alias: 'Umb.CollectionView.Table', + name: 'Table', + elementName: 'umb-collection-view-document-table', + loader: () => import('./collection-view-document-table.element'), + weight: 200, + meta: { + label: 'Table', + icon: 'umb:box', + entityType: 'document', + pathName: 'table', + }, + }, + + + { type: 'collectionView', alias: 'Umb.CollectionView.Grid', diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/collection/workspace-view-collection.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/collection/workspace-view-collection.element.ts index d4f1646864..3420722139 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/collection/workspace-view-collection.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/collection/workspace-view-collection.element.ts @@ -52,7 +52,8 @@ export class UmbWorkspaceViewCollectionElement extends UmbLitElement { this._collectionContext = new UmbCollectionContext( this, entityKey, - manifestMeta.storeAlias + manifestMeta.storeAlias, + manifestMeta.repositoryAlias ); this.provideContext(UMB_COLLECTION_CONTEXT_TOKEN, this._collectionContext); }