common workspace-view-collection

This commit is contained in:
Niels Lyngsø
2022-12-22 15:17:48 +01:00
parent b66ef5c418
commit b2366fcc38
39 changed files with 167 additions and 247 deletions

View File

@@ -86,6 +86,7 @@ export class UmbCollectionViewsMediaGridElement extends UmbContextConsumerMixin(
this.toggleAttribute('dragging', false);
});
this.consumeContext('umbCollectionContext', (instance) => {
console.log("umbCollectionContext", instance)
this._collectionContext = instance;
this._observeCollectionContext();
});

View File

@@ -4,7 +4,8 @@ import { map } from 'rxjs';
import { repeat } from 'lit/directives/repeat.js';
import { ManifestTypes, umbExtensionsRegistry } from '@umbraco-cms/extensions-registry';
import { UmbObserverMixin } from '@umbraco-cms/observable-api';
import { createExtensionElement } from '@umbraco-cms/extensions-api';
import { createExtensionElement, isManifestElementNameType } from '@umbraco-cms/extensions-api';
import { isManifestElementableType } from 'src/core/extensions-api/is-manifest-elementable-type.function';
type InitializedExtensionItem = {alias: string, weight: number, component: HTMLElement|null}
@@ -62,7 +63,12 @@ export class UmbExtensionSlotElement extends UmbObserverMixin(LitElement) {
if(!hasExt) {
const extensionObject:InitializedExtensionItem = {alias: extension.alias, weight: (extension as any).weight || 0, component: null};
this._extensions.push(extensionObject);
const component = await createExtensionElement(extension);
let component;
if(isManifestElementableType(extension)) {
component = await createExtensionElement(extension);
} else {
// TODO: ability to use a default element. or fail at creating this type.
}
if(component) {
(component as any).manifest = extension;

View File

@@ -6,9 +6,9 @@ import { manifests as settingsSectionManifests } from './settings/manifests';
import { manifests as translationSectionManifests } from './translation/manifests';
import { manifests as userSectionManifests } from './users/manifests';
import type { ManifestDashboard, ManifestSection, ManifestSectionView } from '@umbraco-cms/models';
import type { ManifestDashboard, ManifestDashboardCollection, ManifestSection, ManifestSectionView } from '@umbraco-cms/models';
export const manifests: Array<ManifestSection | ManifestDashboard | ManifestSectionView> = [
export const manifests: Array<ManifestSection | ManifestDashboardCollection | ManifestDashboard | ManifestSectionView> = [
...contentSectionManifests,
...mediaSectionManifests,
...memberSectionManifests,

View File

@@ -17,7 +17,7 @@ const DefaultDataTypeData = ({
export class UmbWorkspaceDataTypeContext extends UmbWorkspaceNodeContext<UmbDataTypeStoreItemType, UmbDataTypeStore> {
constructor(target:HTMLElement, entityKey: string) {
super(target, DefaultDataTypeData, 'umbDataTypeStore', entityKey);
super(target, DefaultDataTypeData, 'umbDataTypeStore', entityKey, 'dataType');
}
public setPropertyValue(propertyAlias: string, value: any) {

View File

@@ -20,7 +20,7 @@ export class UmbWorkspaceDictionaryElement extends LitElement {
render() {
return html`
<umb-workspace-entity-layout alias="Umb.Workspace.Dictionary">Dictionary Workspace</umb-workspace-entity-layout>
<umb-workspace-entity alias="Umb.Workspace.Dictionary">Dictionary Workspace</umb-workspace-entity>
`;
}
}

View File

@@ -15,7 +15,7 @@ const DefaultDocumentTypeData = ({
export class UmbWorkspaceDocumentTypeContext extends UmbWorkspaceNodeContext<UmbDocumentTypeStoreItemType, UmbDocumentTypeStore> {
constructor(target:HTMLElement, entityKey: string) {
super(target, DefaultDocumentTypeData, 'umbDocumentTypeStore', entityKey);
super(target, DefaultDocumentTypeData, 'umbDocumentTypeStore', entityKey, 'documentType');
}
}

View File

@@ -34,7 +34,7 @@ const DefaultDocumentData = ({
export class UmbWorkspaceDocumentContext extends UmbWorkspaceNodeContext<UmbDocumentStoreItemType, UmbDocumentStore> {
constructor(target:HTMLElement, entityKey: string) {
super(target, DefaultDocumentData, 'umbDocumentStore', entityKey);
super(target, DefaultDocumentData, 'umbDocumentStore', entityKey, 'document');
}
}

View File

@@ -1,7 +1,7 @@
import { html, LitElement } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { UmbObserverMixin } from '@umbraco-cms/observable-api';
import { isManifestElementType } from '@umbraco-cms/extensions-api';
import { isManifestElementNameType } from '@umbraco-cms/extensions-api';
import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry';
import { UmbContextConsumerMixin } from '@umbraco-cms/context-api';
import type { ManifestTypes } from '@umbraco-cms/models';
@@ -40,7 +40,7 @@ export class UmbWorkspaceExtensionRootElement extends UmbContextConsumerMixin(Um
<uui-table-row>
<uui-table-cell>${extension.type}</uui-table-cell>
<uui-table-cell>
${isManifestElementType(extension) ? extension.name : 'Custom extension'}
${isManifestElementNameType(extension) ? extension.name : 'Custom extension'}
</uui-table-cell>
<uui-table-cell>${extension.alias}</uui-table-cell>
<uui-table-cell>

View File

@@ -34,7 +34,7 @@ const DefaultMediaData = ({
export class UmbWorkspaceMediaContext extends UmbWorkspaceNodeContext<UmbMediaStoreItemType, UmbMediaStore> {
constructor(target:HTMLElement, entityKey: string) {
super(target, DefaultMediaData, 'umbMediaStore', entityKey);
super(target, DefaultMediaData, 'umbMediaStore', entityKey, 'media');
}
}

View File

@@ -2,7 +2,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { css, html, LitElement } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { UmbWorkspaceMediaContext } from './workspace-media.context';
import type { ManifestWorkspaceView } from '@umbraco-cms/models';
import type { ManifestWorkspaceView, ManifestWorkspaceViewCollection } from '@umbraco-cms/models';
import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry';
import { UmbContextConsumerMixin, UmbContextProviderMixin } from '@umbraco-cms/context-api';
@@ -60,18 +60,19 @@ export class UmbWorkspaceMediaElement extends UmbContextConsumerMixin(UmbContext
}
private _registerWorkspaceViews() {
const dashboards: Array<ManifestWorkspaceView> = [
const dashboards: Array<ManifestWorkspaceView | ManifestWorkspaceViewCollection> = [
{
type: 'workspaceView',
type: 'workspaceViewCollection',
alias: 'Umb.WorkspaceView.Media.Collection',
name: 'Media Workspace Collection View',
loader: () => import('../shared/workspace-content/views/collection/workspace-view-media-collection.element'),
weight: 300,
meta: {
workspaces: ['Umb.Workspace.Media'],
label: 'Media',
pathname: 'collection',
icon: 'umb:grid',
entityType: 'media',
storeAlias: 'umbMediaStore'
},
},
{

View File

@@ -1,6 +1,7 @@
import { css, html, LitElement } from 'lit';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { customElement } from 'lit/decorators.js';
import { ifDefined } from 'lit-html/directives/if-defined.js';
import type { UmbWorkspaceNodeContext } from '../../../workspace-context/workspace-node.context';
import { UmbContextConsumerMixin, UmbContextProviderMixin } from '@umbraco-cms/context-api';
import { UmbObserverMixin } from '@umbraco-cms/observable-api';
@@ -10,8 +11,8 @@ import 'src/backoffice/dashboards/collection/dashboard-collection.element';
import { UmbCollectionContext } from '@umbraco-cms/components/collection/collection.context';
import { UmbMediaStore, UmbMediaStoreItemType } from '@umbraco-cms/stores/media/media.store';
@customElement('umb-workspace-view-collection-media')
export class UmbWorkspaceViewCollectionMediaElement extends UmbContextProviderMixin(UmbContextConsumerMixin(UmbObserverMixin(LitElement))) {
@customElement('umb-workspace-view-collection')
export class UmbWorkspaceViewCollectionElement extends UmbContextProviderMixin(UmbContextConsumerMixin(UmbObserverMixin(LitElement))) {
static styles = [
UUITextStyles,
css`
@@ -25,6 +26,7 @@ export class UmbWorkspaceViewCollectionMediaElement extends UmbContextProviderMi
private _workspaceContext?: UmbWorkspaceNodeContext;
private _collectionContext?:UmbCollectionContext<UmbMediaStoreItemType, UmbMediaStore>;
constructor() {
super();
@@ -48,7 +50,8 @@ export class UmbWorkspaceViewCollectionMediaElement extends UmbContextProviderMi
protected _provideWorkspace() {
if(this._workspaceContext?.entityKey != null) {
this._collectionContext = new UmbCollectionContext(this, this._workspaceContext.entityKey, 'umbMediaStore');
console.log("collection:", this._workspaceContext.entityType, this._workspaceContext.entityKey, this._workspaceContext.getStore().storeAlias)
this._collectionContext = new UmbCollectionContext(this, this._workspaceContext.entityKey, this._workspaceContext.getStore().storeAlias);
this._collectionContext.connectedCallback();
this.provideContext('umbCollectionContext', this._collectionContext);
}
@@ -57,14 +60,14 @@ export class UmbWorkspaceViewCollectionMediaElement extends UmbContextProviderMi
render() {
return html`<umb-collection entityType="media"></umb-collection>`;
return html`<umb-collection entityType=${ifDefined(this._workspaceContext?.entityType)}></umb-collection>`;
}
}
export default UmbWorkspaceViewCollectionMediaElement;
export default UmbWorkspaceViewCollectionElement;
declare global {
interface HTMLElementTagNameMap {
'umb-workspace-view-collection-media': UmbWorkspaceViewCollectionMediaElement;
'umb-workspace-view-collection': UmbWorkspaceViewCollectionElement;
}
}

View File

@@ -12,8 +12,9 @@ export class UmbWorkspaceNodeContext<ContentTypeType extends ContentTreeItem = C
protected _notificationConsumer!:UmbContextConsumer;
public entityKey:string;
public entityType:string;
constructor(target:HTMLElement, defaultData:ContentTypeType, storeAlias:string, entityKey: string) {
constructor(target:HTMLElement, defaultData:ContentTypeType, storeAlias:string, entityKey: string, entityType: string) {
super(target, defaultData, storeAlias);
this._notificationConsumer = new UmbContextConsumer(this._target, 'umbNotificationService', (_instance: UmbNotificationService) => {
@@ -21,6 +22,7 @@ export class UmbWorkspaceNodeContext<ContentTypeType extends ContentTreeItem = C
});
this.entityKey = entityKey;
this.entityType = entityType;
}

View File

@@ -1,180 +0,0 @@
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { css, html, LitElement, nothing } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { IRoute, IRoutingInfo, PageComponent, RouterSlot } from 'router-slot';
import { map } from 'rxjs';
import { UmbObserverMixin } from '@umbraco-cms/observable-api';
import { createExtensionElement } from '@umbraco-cms/extensions-api';
import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry';
import { UmbContextConsumerMixin } from '@umbraco-cms/context-api';
import type { ManifestWorkspaceView } from '@umbraco-cms/models';
import '../../../components/body-layout/body-layout.element';
import '../workspace-action-extension/workspace-action-extension.element';
/**
* @element umb-workspace-entity-layout
* @description
* @slot icon - Slot for rendering the entity icon
* @slot name - Slot for rendering the entity name
* @slot footer - Slot for rendering the entity footer
* @slot actions - Slot for rendering the entity actions
* @slot default - slot for main content
* @export
* @class UmbWorkspaceEntityLayout
* @extends {UmbContextConsumerMixin(LitElement)}
*/
@customElement('umb-workspace-entity-layout')
export class UmbWorkspaceEntityLayout extends UmbContextConsumerMixin(UmbObserverMixin(LitElement)) {
static styles = [
UUITextStyles,
css`
:host {
display: block;
width: 100%;
height: 100%;
}
uui-input {
width: 100%;
}
uui-tab-group {
--uui-tab-divider: var(--uui-color-border);
border-left: 1px solid var(--uui-color-border);
border-right: 1px solid var(--uui-color-border);
}
router-slot {
height: 100%;
}
`,
];
/**
* Alias of the workspace. The Layout will render the workspace views that are registered for this workspace alias.
* @public
* @type {string}
* @attr
* @default ''
*/
@property()
public headline = '';
@property()
public alias = '';
@state()
private _workspaceViews: Array<ManifestWorkspaceView> = [];
@state()
private _currentView = '';
@state()
private _routes: Array<IRoute> = [];
private _routerFolder = '';
connectedCallback(): void {
super.connectedCallback();
this._observeWorkspaceViews();
/* TODO: find a way to construct absolute urls */
this._routerFolder = window.location.pathname.split('/view')[0];
}
private _observeWorkspaceViews() {
this.observe<ManifestWorkspaceView[]>(
umbExtensionsRegistry
.extensionsOfType('workspaceView')
.pipe(map((extensions) => extensions.filter((extension) => extension.meta.workspaces.includes(this.alias)))),
(workspaceViews) => {
this._workspaceViews = workspaceViews;
this._createRoutes();
}
);
}
private async _createRoutes() {
if (this._workspaceViews.length > 0) {
this._routes = [];
this._routes = this._workspaceViews.map((view) => {
return {
path: `view/${view.meta.pathname}`,
component: () => createExtensionElement(view) as unknown as PageComponent,
setup: (_element: HTMLElement, info: IRoutingInfo) => {
this._currentView = info.match.route.path;
},
};
});
this._routes.push({
path: '**',
redirectTo: `view/${this._workspaceViews?.[0].meta.pathname}`,
});
this.requestUpdate();
await this.updateComplete;
this._forceRouteRender();
}
}
// TODO: Figure out why this has been necessary for this case. Come up with another case
private _forceRouteRender() {
const routerSlotEl = this.shadowRoot?.querySelector('router-slot') as RouterSlot;
if (routerSlotEl) {
routerSlotEl.render();
}
}
private _renderTabs() {
return html`
${this._workspaceViews?.length > 0
? html`
<uui-tab-group slot="tabs">
${this._workspaceViews.map(
(view: ManifestWorkspaceView) => html`
<uui-tab
.label="${view.meta.label || view.name}"
href="${this._routerFolder}/view/${view.meta.pathname}"
?active="${this._currentView.includes(view.meta.pathname)}">
<uui-icon slot="icon" name="${view.meta.icon}"></uui-icon>
${view.meta.label || view.name}
</uui-tab>
`
)}
</uui-tab-group>
`
: nothing}
`;
}
render() {
return html`
<umb-body-layout .headline=${this.headline}>
<slot name="header" slot="header"></slot>
${this._renderTabs()}
<router-slot .routes="${this._routes}"></router-slot>
<slot></slot>
<slot name="footer" slot="footer"></slot>
<umb-extension-slot
slot="actions"
type="workspaceAction"
.filter=${(extension: any) => extension.meta.workspaces.includes(this.alias)}></umb-extension-slot>
<slot name="actions" slot="actions"></slot>
</umb-body-layout>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'umb-workspace-entity-layout': UmbWorkspaceEntityLayout;
}
}

View File

@@ -1,14 +1,14 @@
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { css, html, LitElement, nothing } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { IRoute, IRoutingInfo, PageComponent, RouterSlot } from 'router-slot';
import { IRoutingInfo, RouterSlot } from 'router-slot';
import { map } from 'rxjs';
import { UmbObserverMixin } from '@umbraco-cms/observable-api';
import { createExtensionElement } from '@umbraco-cms/extensions-api';
import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry';
import { UmbContextConsumerMixin } from '@umbraco-cms/context-api';
import type { ManifestWorkspaceView } from '@umbraco-cms/models';
import type { ManifestWithMeta, ManifestWorkspaceView, ManifestWorkspaceViewCollection } from '@umbraco-cms/models';
import '../../../components/body-layout/body-layout.element';
import '../../../components/extension-slot/extension-slot.element';
@@ -65,13 +65,13 @@ export class UmbWorkspaceEntity extends UmbContextConsumerMixin(UmbObserverMixin
public alias = '';
@state()
private _workspaceViews: Array<ManifestWorkspaceView> = [];
private _workspaceViews: Array<ManifestWorkspaceView | ManifestWorkspaceViewCollection> = [];
@state()
private _currentView = '';
@state()
private _routes: Array<IRoute> = [];
private _routes: Array<any> = [];
private _routerFolder = '';
@@ -87,8 +87,8 @@ export class UmbWorkspaceEntity extends UmbContextConsumerMixin(UmbObserverMixin
private _observeWorkspaceViews() {
this.observe<ManifestWorkspaceView[]>(
umbExtensionsRegistry
.extensionsOfType('workspaceView')
.pipe(map((extensions) => extensions.filter((extension) => extension.meta.workspaces.includes(this.alias)))),
.extensionsOfTypes(['workspaceView', 'workspaceViewCollection'])
.pipe(map((extensions) => extensions.filter((extension) => (extension as ManifestWithMeta).meta.workspaces.includes(this.alias)))),
(workspaceViews) => {
this._workspaceViews = workspaceViews;
this._createRoutes();
@@ -103,10 +103,21 @@ export class UmbWorkspaceEntity extends UmbContextConsumerMixin(UmbObserverMixin
this._routes = this._workspaceViews.map((view) => {
return {
path: `view/${view.meta.pathname}`,
component: () => createExtensionElement(view) as unknown as PageComponent,
setup: (_element: HTMLElement, info: IRoutingInfo) => {
this._currentView = info.match.route.path;
component: () => {
if (view.type === 'workspaceViewCollection') {
console.log("!!!!!workspaceViewCollection")
return import('src/backoffice/workspaces/shared/workspace-content/views/collection/workspace-view-collection.element');
}
return createExtensionElement(view)
},
setup: (component: Promise<HTMLElement> | HTMLElement, info: IRoutingInfo) => {
this._currentView = info.match.route.path;
// When its using import, we get an element, when using createExtensionElement we get a Promise.
(component as any).manifest = view;
if((component as any).then) {
(component as any).then((el: any) => el.manifest = view);
}
}
};
});
@@ -136,7 +147,7 @@ export class UmbWorkspaceEntity extends UmbContextConsumerMixin(UmbObserverMixin
? html`
<uui-tab-group slot="tabs">
${this._workspaceViews.map(
(view: ManifestWorkspaceView) => html`
(view: ManifestWorkspaceView | ManifestWorkspaceViewCollection) => html`
<uui-tab
.label="${view.meta.label || view.name}"
href="${this._routerFolder}/view/${view.meta.pathname}"

View File

@@ -16,7 +16,7 @@ const DefaultDataTypeData = ({
export class UmbWorkspaceUserGroupContext extends UmbWorkspaceNodeContext<UmbUserGroupStoreItemType, UmbUserGroupStore> {
constructor(target:HTMLElement, entityKey: string) {
super(target, DefaultDataTypeData, 'umbUserStore', entityKey);
super(target, DefaultDataTypeData, 'umbUserStore', entityKey, 'userGroup');
}
}

View File

@@ -23,7 +23,7 @@ const DefaultDataTypeData = ({
export class UmbWorkspaceUserContext extends UmbWorkspaceNodeContext<UmbUserStoreItemType, UmbUserStore> {
constructor(target:HTMLElement, entityKey: string) {
super(target, DefaultDataTypeData, 'umbUserStore', entityKey);
super(target, DefaultDataTypeData, 'umbUserStore', entityKey, 'user');
}
}

View File

@@ -1,13 +1,14 @@
import type { ManifestTypes } from '../models';
import type { ManifestElementType } from '../models';
import { hasDefaultExport } from './has-default-export.function';
import { isManifestElementType } from './is-extension.function';
import { isManifestElementNameType } from './is-manifest-element-name-type.function';
import { loadExtension } from './load-extension.function';
export async function createExtensionElement(manifest: ManifestTypes): Promise<HTMLElement | undefined> {
export async function createExtensionElement(manifest: ManifestElementType): Promise<HTMLElement | undefined> {
//TODO: Write tests for these extension options:
const js = await loadExtension(manifest);
if (isManifestElementType(manifest) && manifest.elementName) {
if (isManifestElementNameType(manifest)) {
// created by manifest method providing HTMLElement
return document.createElement(manifest.elementName);
}
@@ -19,14 +20,12 @@ export async function createExtensionElement(manifest: ManifestTypes): Promise<H
return new js.default();
}
if(!Object.getOwnPropertyDescriptor(manifest, 'element')) {
console.error('-- Extension did not succeed creating an element, missing the manifest `element` or default export', manifest);
}
console.error('-- Extension did not succeed creating an element, missing a default export of the served JavaScript file', manifest);
// If some JS was loaded and it did not at least contain a default export, then we are safe to assume that it executed as a module and does not need to be returned
return undefined;
}
console.error('-- Extension did not succeed creating an element', manifest);
console.error('-- Extension did not succeed creating an element, missing a default export or `elementName` in the manifest.', manifest);
return undefined;
}

View File

@@ -1,5 +1,5 @@
export * from './registry/extension.registry';
export * from './create-extension-element.function';
export * from './has-default-export.function';
export * from './is-extension.function';
export * from './is-manifest-element-name-type.function';
export * from './load-extension.function';

View File

@@ -1,7 +0,0 @@
import type { ManifestElementType } from '../models';
export function isManifestElementType(manifest: unknown): manifest is ManifestElementType {
return (
typeof manifest === 'object' && manifest !== null && (manifest as ManifestElementType).elementName !== undefined
);
}

View File

@@ -0,0 +1,7 @@
import type { ManifestElementType, ManifestElementWithElementName } from '../models';
export function isManifestElementNameType(manifest: unknown): manifest is ManifestElementWithElementName {
return (
typeof manifest === 'object' && manifest !== null && (manifest as ManifestElementType).elementName !== undefined
);
}

View File

@@ -0,0 +1,8 @@
import type { ManifestElementType, ManifestTypes } from "../extensions-registry/models";
import { isManifestElementNameType } from "./is-manifest-element-name-type.function";
import { isManifestJSType } from "./is-manifest-js-type.function";
import { isManifestLoaderType } from "./is-manifest-loader-type.function";
export function isManifestElementableType(manifest: ManifestTypes): manifest is ManifestElementType {
return isManifestElementNameType(manifest) || isManifestLoaderType(manifest) || isManifestJSType(manifest);
}

View File

@@ -0,0 +1,6 @@
import type { ManifestTypes } from "../extensions-registry/models";
import { ManifestJSType } from "./load-extension.function";
export function isManifestJSType(manifest: ManifestTypes): manifest is ManifestJSType {
return (manifest as ManifestJSType).js !== undefined;
}

View File

@@ -0,0 +1,6 @@
import type { ManifestTypes } from "../extensions-registry/models";
import { ManifestLoaderType } from "./load-extension.function";
export function isManifestLoaderType(manifest: ManifestTypes): manifest is ManifestLoaderType {
return typeof (manifest as ManifestLoaderType).loader === 'function';
}

View File

@@ -1,4 +1,6 @@
import type { ManifestTypes } from '../models';
import { isManifestJSType } from './is-manifest-js-type.function';
import { isManifestLoaderType } from './is-manifest-loader-type.function';
export type ManifestLoaderType = ManifestTypes & { loader: () => Promise<object | HTMLElement> };
export type ManifestJSType = ManifestTypes & { js: string };
@@ -18,12 +20,4 @@ export async function loadExtension(manifest: ManifestTypes): Promise<object | H
}
return Promise.resolve(null);
}
export function isManifestLoaderType(manifest: ManifestTypes): manifest is ManifestLoaderType {
return typeof (manifest as ManifestLoaderType).loader === 'function';
}
export function isManifestJSType(manifest: ManifestTypes): manifest is ManifestJSType {
return (manifest as ManifestJSType).js !== undefined;
}
}

View File

@@ -20,7 +20,8 @@ import type {
ManifestCollectionView,
ManifestCollectionBulkAction,
} from '../../models';
import { createExtensionElement } from '../create-extension-element.function';
import { hasDefaultExport } from '../has-default-export.function';
import { loadExtension } from '../load-extension.function';
export class UmbExtensionRegistry {
private _extensions = new BehaviorSubject<Array<ManifestTypes>>([]);
@@ -37,9 +38,16 @@ export class UmbExtensionRegistry {
this._extensions.next([...extensionsValues, manifest]);
// If entrypoint extension, we should load it immediately
// If entrypoint extension, we should load and run it immediately
if (manifest.type === 'entrypoint') {
createExtensionElement(manifest);
loadExtension(manifest).then((js) => {
if (hasDefaultExport(js)) {
new js.default();
} else {
console.error(`Extension with alias '${manifest.alias}' of type 'entrypoint' must have a default export of its JavaScript module.`)
}
});
}
}

View File

@@ -9,6 +9,6 @@ export interface MetaDashboardCollection {
sections: string[];
pathname: string;
label?: string;
entityType: string,
storeAlias: string
entityType: string;
storeAlias: string;
}

View File

@@ -6,6 +6,7 @@ import type { ManifestTreeItemAction } from './tree-item-action.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';
@@ -24,6 +25,7 @@ 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';
@@ -42,6 +44,7 @@ export type ManifestTypes =
| ManifestWorkspace
| ManifestWorkspaceAction
| ManifestWorkspaceView
| ManifestWorkspaceViewCollection
| ManifestTreeItemAction
| ManifestPropertyEditorUI
| ManifestPropertyEditorModel
@@ -62,8 +65,9 @@ export type ManifestStandardTypes =
| 'sectionView'
| 'tree'
| 'workspace'
| 'workspaceView'
| 'workspaceAction'
| 'workspaceView'
| 'workspaceViewCollection'
| 'treeItemAction'
| 'propertyEditorUI'
| 'propertyEditorModel'
@@ -83,12 +87,11 @@ export type ManifestElementType =
| ManifestTree
| ManifestTreeItemAction
| ManifestWorkspace
| ManifestWorkspaceView
| ManifestPropertyAction
| ManifestPropertyEditorUI
| ManifestDashboard
| ManifestDashboardCollection
| ManifestUserDashboard
| ManifestWorkspaceView
| ManifestWorkspaceAction
| ManifestPackageView
| ManifestExternalLoginProvider
@@ -110,6 +113,10 @@ export interface ManifestElement extends ManifestBase {
meta?: any;
}
export interface ManifestElementWithElementName extends ManifestElement {
elementName: string;
}
export interface ManifestCustom extends ManifestBase {
type: 'custom';
meta?: any;

View File

@@ -0,0 +1,15 @@
import type { ManifestBase } from './models';
export interface ManifestWorkspaceViewCollection extends ManifestBase {
type: 'workspaceViewCollection';
meta: MetaEditorViewCollection;
}
export interface MetaEditorViewCollection {
workspaces: string[];
pathname: string;
label: string;
icon: string;
entityType: string;
storeAlias: string;
}

View File

@@ -17,6 +17,9 @@ export type UmbDataTypeStoreItemType = DataTypeDetails | FolderTreeItem;
* @description - Data Store for Data Types
*/
export class UmbDataTypeStore extends UmbDataStoreBase<UmbDataTypeStoreItemType> {
public readonly storeAlias = 'umbDataTypeStore';
/**
* @description - Request a Data Type by key. The Data Type is added to the store and is returned as an Observable.
* @param {string} key

View File

@@ -9,6 +9,9 @@ import { ApiError, DictionaryResource, EntityTreeItem, ProblemDetails } from '@u
* @description - Data Store for Dictionary Items.
*/
export class UmbDictionaryStore extends UmbDataStoreBase<EntityTreeItem> {
public readonly storeAlias = 'umbDictionaryStore';
/**
* @description - Get the root of the tree.
* @return {*} {Observable<Array<PagedEntityTreeItem>>}

View File

@@ -16,6 +16,9 @@ export type UmbDocumentTypeStoreItemType = DocumentTypeDetails | DocumentTypeTre
* @description - Data Store for Document Types
*/
export class UmbDocumentTypeStore extends UmbDataStoreBase<UmbDocumentTypeStoreItemType> {
public readonly storeAlias = 'umbDocumentTypeStore';
getByKey(key: string): Observable<DocumentTypeDetails | null> {
// TODO: use Fetcher API.
// TODO: only fetch if the data type is not in the store?

View File

@@ -16,6 +16,9 @@ export type UmbDocumentStoreItemType = DocumentDetails | DocumentTreeItem
* @description - Data Store for Documents
*/
export class UmbDocumentStore extends UmbNodeStoreBase<UmbDocumentStoreItemType> {
public readonly storeAlias = 'umbDocumentStore';
getByKey(key: string): Observable<DocumentDetails | null> {
// TODO: use backend cli when available.
fetch(`/umbraco/management/api/v1/document/details/${key}`)

View File

@@ -13,6 +13,8 @@ export type UmbMediaTypeStoreItemType = MediaTypeDetails | FolderTreeItem;
*/
export class UmbMediaTypeStore extends UmbNodeStoreBase<UmbMediaTypeStoreItemType> {
public readonly storeAlias = 'umbMediaTypeStore';
/**
* @description - Request a Data Type by key. The Data Type is added to the store and is returned as an Observable.
* @param {string} key

View File

@@ -17,6 +17,9 @@ export type UmbMediaStoreItemType = MediaDetails | ContentTreeItem;
* @description - Data Store for Media
*/
export class UmbMediaStore extends UmbDataStoreBase<UmbMediaStoreItemType> {
public readonly storeAlias = 'umbMediaStore';
getByKey(key: string): Observable<MediaDetails | null> {
// fetch from server and update store
fetch(`/umbraco/management/api/v1/media/details/${key}`)

View File

@@ -13,6 +13,8 @@ export type UmbMemberGroupStoreItemType = MemberGroupDetails | EntityTreeItem;
*/
export class UmbMemberGroupStore extends UmbNodeStoreBase<UmbMemberGroupStoreItemType> {
public readonly storeAlias = 'umbMemberGroupStore';
getByKey(key: string): Observable<UmbMemberGroupStoreItemType | null> {
return null as any;
}

View File

@@ -13,6 +13,7 @@ export type UmbMemberTypeStoreItemType = MemberTypeDetails | EntityTreeItem
*/
export class UmbMemberTypeStore extends UmbDataStoreBase<UmbMemberTypeStoreItemType> {
public readonly storeAlias = 'umbMemberTypeStore';
getByKey(key: string): Observable<UmbMemberTypeStoreItemType | null> {
return null as any;

View File

@@ -6,6 +6,7 @@ export interface UmbDataStoreIdentifiers {
}
export interface UmbDataStore<T> {
readonly storeAlias: string;
readonly items: Observable<Array<T>>;
updateItems(items: Array<T>): void;
}
@@ -23,6 +24,9 @@ export interface UmbTreeDataStore<T> extends UmbDataStore<T> {
* @description - Base class for Data Stores
*/
export abstract class UmbDataStoreBase<T extends UmbDataStoreIdentifiers> implements UmbDataStore<T> {
public abstract readonly storeAlias:string;
protected _items: BehaviorSubject<Array<T>> = new BehaviorSubject(<Array<T>>[]);
public readonly items: Observable<Array<T>> = this._items.asObservable();

View File

@@ -12,6 +12,10 @@ export type UmbUserGroupStoreItemType = UserGroupDetails & { users?: Array<strin
* @description - Data Store for Users
*/
export class UmbUserGroupStore extends UmbDataStoreBase<UmbUserGroupStoreItemType> {
public readonly storeAlias = 'umbUserGroupStore';
getAll(): Observable<Array<UmbUserGroupStoreItemType>> {
// TODO: use Fetcher API.
// TODO: only fetch if the data type is not in the store?

View File

@@ -11,6 +11,11 @@ export type UmbUserStoreItemType = UserDetails;
* @description - Data Store for Users
*/
export class UmbUserStore extends UmbDataStoreBase<UmbUserStoreItemType> {
public readonly storeAlias = 'umbUserStore';
private _totalUsers: BehaviorSubject<number> = new BehaviorSubject(0);
public readonly totalUsers: Observable<number> = this._totalUsers.asObservable();