Merge branch 'main' into bugfix/silo-based-audit-log
This commit is contained in:
@@ -34,7 +34,6 @@
|
||||
"./document-blueprint": "./dist-cms/packages/documents/document-blueprints/index.js",
|
||||
"./document-type": "./dist-cms/packages/documents/document-types/index.js",
|
||||
"./document": "./dist-cms/packages/documents/documents/index.js",
|
||||
"./dynamic-root": "./dist-cms/packages/dynamic-root/index.js",
|
||||
"./entity": "./dist-cms/packages/core/entity/index.js",
|
||||
"./entity-action": "./dist-cms/packages/core/entity-action/index.js",
|
||||
"./entity-bulk-action": "./dist-cms/packages/core/entity-bulk-action/index.js",
|
||||
@@ -70,6 +69,7 @@
|
||||
"./resources": "./dist-cms/packages/core/resources/index.js",
|
||||
"./router": "./dist-cms/packages/core/router/index.js",
|
||||
"./section": "./dist-cms/packages/core/section/index.js",
|
||||
"./settings": "./dist-cms/packages/settings/index.js",
|
||||
"./server-file-system": "./dist-cms/packages/core/server-file-system/index.js",
|
||||
"./sorter": "./dist-cms/packages/core/sorter/index.js",
|
||||
"./static-file": "./dist-cms/packages/static-file/index.js",
|
||||
|
||||
@@ -16,7 +16,6 @@ const CORE_PACKAGES = [
|
||||
import('../../packages/data-type/umbraco-package.js'),
|
||||
import('../../packages/dictionary/umbraco-package.js'),
|
||||
import('../../packages/documents/umbraco-package.js'),
|
||||
import('../../packages/dynamic-root/umbraco-package.js'),
|
||||
import('../../packages/health-check/umbraco-package.js'),
|
||||
import('../../packages/language/umbraco-package.js'),
|
||||
import('../../packages/log-viewer/umbraco-package.js'),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { UmbBackofficeContext } from '../backoffice.context.js';
|
||||
import { UMB_BACKOFFICE_CONTEXT } from '../backoffice.context.js';
|
||||
import { css, html, customElement, state, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbSectionContext, UMB_SECTION_CONTEXT } from '@umbraco-cms/backoffice/section';
|
||||
import { UmbSectionContext, UMB_SECTION_CONTEXT, UMB_SECTION_PATH_PATTERN } from '@umbraco-cms/backoffice/section';
|
||||
import type { UmbRoute, UmbRouterSlotChangeEvent } from '@umbraco-cms/backoffice/router';
|
||||
import type { ManifestSection, UmbSectionElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type { UmbExtensionManifestInitializer } from '@umbraco-cms/backoffice/extension-api';
|
||||
@@ -16,7 +16,6 @@ export class UmbBackofficeMainElement extends UmbLitElement {
|
||||
@state()
|
||||
private _sections: Array<UmbExtensionManifestInitializer<ManifestSection>> = [];
|
||||
|
||||
private _routePrefix = 'section/';
|
||||
private _backofficeContext?: UmbBackofficeContext;
|
||||
private _sectionContext?: UmbSectionContext;
|
||||
|
||||
@@ -56,7 +55,7 @@ export class UmbBackofficeMainElement extends UmbLitElement {
|
||||
} else {
|
||||
return {
|
||||
alias: section.alias,
|
||||
path: this._routePrefix + (section.manifest as ManifestSection).meta.pathname,
|
||||
path: UMB_SECTION_PATH_PATTERN.generateLocal({ sectionName: section.manifest!.meta.pathname }),
|
||||
component: () => createExtensionElement(section.manifest!, 'umb-section-default'),
|
||||
setup: (component) => {
|
||||
(component as UmbSectionElement).manifest = section.manifest as ManifestSection;
|
||||
@@ -66,10 +65,18 @@ export class UmbBackofficeMainElement extends UmbLitElement {
|
||||
});
|
||||
|
||||
if (this._sections.length > 0) {
|
||||
const fallbackSectionPath = UMB_SECTION_PATH_PATTERN.generateLocal({
|
||||
sectionName: this._sections[0].manifest!.meta.pathname,
|
||||
});
|
||||
this._routes.push({
|
||||
alias: '__redirect',
|
||||
path: '/',
|
||||
redirectTo: 'section/content',
|
||||
redirectTo: fallbackSectionPath,
|
||||
});
|
||||
this._routes.push({
|
||||
alias: '__redirect',
|
||||
path: '/section/',
|
||||
redirectTo: fallbackSectionPath,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -78,7 +85,9 @@ export class UmbBackofficeMainElement extends UmbLitElement {
|
||||
|
||||
private _onRouteChange = async (event: UmbRouterSlotChangeEvent) => {
|
||||
const currentPath = event.target.localActiveViewPath || '';
|
||||
const section = this._sections.find((s) => this._routePrefix + s.manifest?.meta.pathname === currentPath);
|
||||
const section = this._sections.find(
|
||||
(s) => UMB_SECTION_PATH_PATTERN.generateLocal({ sectionName: s.manifest!.meta.pathname }) === currentPath,
|
||||
);
|
||||
if (!section) return;
|
||||
await section.asPromise();
|
||||
if (section.manifest) {
|
||||
|
||||
@@ -91,9 +91,10 @@ export class UmbContextConsumer<BaseType = unknown, ResultType extends BaseType
|
||||
|
||||
protected setInstance(instance: ResultType): void {
|
||||
this.#instance = instance;
|
||||
this.#callback?.(instance);
|
||||
if (instance !== undefined) {
|
||||
this.#promiseResolver?.(instance);
|
||||
const promiseResolver = this.#promiseResolver; // Get the promise resolver, as it might be destroyed as a reaction of the callback [NL]
|
||||
this.#callback?.(instance); // Resolve callback first as it might perform something you like completed before resolving the promise, as the promise might be used to determine when things are ready/initiated [NL]
|
||||
if (promiseResolver && instance !== undefined) {
|
||||
promiseResolver(instance);
|
||||
this.#promise = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { UmbWorkspaceData, UmbWorkspaceValue } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbWorkspaceModalData, UmbWorkspaceModalValue } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbBlockGridAreaTypeWorkspaceData extends UmbWorkspaceData {}
|
||||
export interface UmbBlockGridAreaTypeWorkspaceData extends UmbWorkspaceModalData {}
|
||||
|
||||
export const UMB_BLOCK_GRID_AREA_TYPE_WORKSPACE_MODAL = new UmbModalToken<
|
||||
UmbBlockGridAreaTypeWorkspaceData,
|
||||
UmbWorkspaceValue
|
||||
UmbWorkspaceModalValue
|
||||
>('Umb.Modal.Workspace', {
|
||||
modal: {
|
||||
type: 'sidebar',
|
||||
@@ -13,4 +13,4 @@ export const UMB_BLOCK_GRID_AREA_TYPE_WORKSPACE_MODAL = new UmbModalToken<
|
||||
},
|
||||
data: { entityType: 'block-grid-area-type', preset: {} },
|
||||
// Recast the type, so the entityType data prop is not required:
|
||||
}) as UmbModalToken<Omit<UmbBlockGridAreaTypeWorkspaceData, 'entityType'>, UmbWorkspaceValue>;
|
||||
}) as UmbModalToken<Omit<UmbBlockGridAreaTypeWorkspaceData, 'entityType'>, UmbWorkspaceModalValue>;
|
||||
|
||||
@@ -19,7 +19,11 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UMB_BLOCK_GRID_TYPE, type UmbBlockGridTypeGroupType } from '@umbraco-cms/backoffice/block-grid';
|
||||
import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UMB_PROPERTY_DATASET_CONTEXT, type UmbPropertyDatasetContext } from '@umbraco-cms/backoffice/property';
|
||||
import {
|
||||
UMB_PROPERTY_CONTEXT,
|
||||
UMB_PROPERTY_DATASET_CONTEXT,
|
||||
type UmbPropertyDatasetContext,
|
||||
} from '@umbraco-cms/backoffice/property';
|
||||
import { UMB_WORKSPACE_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbSorterController } from '@umbraco-cms/backoffice/sorter';
|
||||
|
||||
@@ -70,6 +74,9 @@ export class UmbPropertyEditorUIBlockGridTypeConfigurationElement
|
||||
this.#mapValuesToBlockGroups();
|
||||
}
|
||||
|
||||
@state()
|
||||
public _alias?: string;
|
||||
|
||||
@property({ type: Object, attribute: false })
|
||||
public config?: UmbPropertyEditorConfigCollection;
|
||||
|
||||
@@ -86,8 +93,13 @@ export class UmbPropertyEditorUIBlockGridTypeConfigurationElement
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, async (instance) => {
|
||||
this.#datasetContext = instance;
|
||||
|
||||
this.consumeContext(UMB_PROPERTY_CONTEXT, async (context) => {
|
||||
this._alias = context.getAlias();
|
||||
});
|
||||
|
||||
this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, async (context) => {
|
||||
this.#datasetContext = context;
|
||||
//this.#observeBlocks();
|
||||
this.#observeBlockGroups();
|
||||
});
|
||||
@@ -203,6 +215,7 @@ export class UmbPropertyEditorUIBlockGridTypeConfigurationElement
|
||||
return html`<div id="groups">
|
||||
${this._notGroupedBlockTypes
|
||||
? html`<umb-input-block-type
|
||||
.propertyAlias=${this._alias}
|
||||
.value=${this._notGroupedBlockTypes}
|
||||
.workspacePath=${this._workspacePath}
|
||||
@change=${this.#onChange}
|
||||
@@ -217,6 +230,7 @@ export class UmbPropertyEditorUIBlockGridTypeConfigurationElement
|
||||
${group.key ? this.#renderGroupInput(group.key, group.name) : nothing}
|
||||
<umb-input-block-type
|
||||
data-umb-group-key=${group.key}
|
||||
.propertyAlias=${this._alias + '_' + group.key}
|
||||
.value=${group.blocks}
|
||||
.workspacePath=${this._workspacePath}
|
||||
@change=${this.#onChange}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { UmbBlockWorkspaceData } from '@umbraco-cms/backoffice/block';
|
||||
import type { UmbWorkspaceData, UmbWorkspaceValue } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbWorkspaceModalData, UmbWorkspaceModalValue } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbBlockGridWorkspaceData
|
||||
@@ -9,7 +9,7 @@ export interface UmbBlockGridWorkspaceData
|
||||
areaKey?: string;
|
||||
}> {}
|
||||
|
||||
export const UMB_BLOCK_GRID_WORKSPACE_MODAL = new UmbModalToken<UmbBlockGridWorkspaceData, UmbWorkspaceValue>(
|
||||
export const UMB_BLOCK_GRID_WORKSPACE_MODAL = new UmbModalToken<UmbBlockGridWorkspaceData, UmbWorkspaceModalValue>(
|
||||
'Umb.Modal.Workspace',
|
||||
{
|
||||
modal: {
|
||||
@@ -19,4 +19,4 @@ export const UMB_BLOCK_GRID_WORKSPACE_MODAL = new UmbModalToken<UmbBlockGridWork
|
||||
data: { entityType: 'block', preset: {}, originData: { index: -1, parentUnique: null } },
|
||||
// Recast the type, so the entityType data prop is not required:
|
||||
},
|
||||
) as UmbModalToken<Omit<UmbWorkspaceData, 'entityType'>, UmbWorkspaceValue>;
|
||||
) as UmbModalToken<Omit<UmbWorkspaceModalData, 'entityType'>, UmbWorkspaceModalValue>;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { UmbBlockWorkspaceData } from '@umbraco-cms/backoffice/block';
|
||||
import type { UmbWorkspaceData, UmbWorkspaceValue } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbWorkspaceModalData, UmbWorkspaceModalValue } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbBlockListWorkspaceData
|
||||
@@ -7,7 +7,7 @@ export interface UmbBlockListWorkspaceData
|
||||
index: number;
|
||||
}> {}
|
||||
|
||||
export const UMB_BLOCK_LIST_WORKSPACE_MODAL = new UmbModalToken<UmbBlockListWorkspaceData, UmbWorkspaceValue>(
|
||||
export const UMB_BLOCK_LIST_WORKSPACE_MODAL = new UmbModalToken<UmbBlockListWorkspaceData, UmbWorkspaceModalValue>(
|
||||
'Umb.Modal.Workspace',
|
||||
{
|
||||
modal: {
|
||||
@@ -17,4 +17,4 @@ export const UMB_BLOCK_LIST_WORKSPACE_MODAL = new UmbModalToken<UmbBlockListWork
|
||||
data: { entityType: 'block', preset: {}, originData: { index: -1 } },
|
||||
// Recast the type, so the entityType data prop is not required:
|
||||
},
|
||||
) as UmbModalToken<Omit<UmbWorkspaceData, 'entityType'>, UmbWorkspaceValue>;
|
||||
) as UmbModalToken<Omit<UmbWorkspaceModalData, 'entityType'>, UmbWorkspaceModalValue>;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { UmbBlockWorkspaceData } from '@umbraco-cms/backoffice/block';
|
||||
import type { UmbWorkspaceData, UmbWorkspaceValue } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbWorkspaceModalData, UmbWorkspaceModalValue } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbBlockRTEWorkspaceData
|
||||
@@ -7,7 +7,7 @@ export interface UmbBlockRTEWorkspaceData
|
||||
index: number;
|
||||
}> {}
|
||||
|
||||
export const UMB_BLOCK_RTE_WORKSPACE_MODAL = new UmbModalToken<UmbBlockRTEWorkspaceData, UmbWorkspaceValue>(
|
||||
export const UMB_BLOCK_RTE_WORKSPACE_MODAL = new UmbModalToken<UmbBlockRTEWorkspaceData, UmbWorkspaceModalValue>(
|
||||
'Umb.Modal.Workspace',
|
||||
{
|
||||
modal: {
|
||||
@@ -17,4 +17,4 @@ export const UMB_BLOCK_RTE_WORKSPACE_MODAL = new UmbModalToken<UmbBlockRTEWorksp
|
||||
data: { entityType: 'block', preset: {}, originData: { index: -1 } },
|
||||
// Recast the type, so the entityType data prop is not required:
|
||||
},
|
||||
) as UmbModalToken<Omit<UmbWorkspaceData, 'entityType'>, UmbWorkspaceValue>;
|
||||
) as UmbModalToken<Omit<UmbWorkspaceModalData, 'entityType'>, UmbWorkspaceModalValue>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { UmbBlockTypeCardElement } from '../block-type-card/index.js';
|
||||
import type { UmbBlockTypeBaseModel, UmbBlockTypeWithGroupKey } from '../../types.js';
|
||||
import { UMB_MODAL_MANAGER_CONTEXT, umbConfirmModal } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalRouteRegistrationController, umbConfirmModal } from '@umbraco-cms/backoffice/modal';
|
||||
import '../block-type-card/index.js';
|
||||
import { css, html, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
@@ -29,9 +29,11 @@ export class UmbInputBlockTypeElement<
|
||||
this.dispatchEvent(new CustomEvent('change', { detail: { item } }));
|
||||
},
|
||||
onEnd: () => {
|
||||
// TODO: Investigate if onEnd is called when a container move has been performed, if not then I would say it should be. [NL]
|
||||
this.dispatchEvent(new CustomEvent('change', { detail: { moveComplete: true } }));
|
||||
},
|
||||
});
|
||||
#elementPickerModal;
|
||||
|
||||
@property({ type: Array, attribute: false })
|
||||
public set value(items) {
|
||||
@@ -42,9 +44,20 @@ export class UmbInputBlockTypeElement<
|
||||
return this._items;
|
||||
}
|
||||
|
||||
@property({ type: String })
|
||||
public set propertyAlias(value: string | undefined) {
|
||||
this.#elementPickerModal.setUniquePathValue('propertyAlias', value);
|
||||
}
|
||||
public get propertyAlias(): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@property({ type: String })
|
||||
workspacePath?: string;
|
||||
|
||||
@state()
|
||||
private _pickerPath?: string;
|
||||
|
||||
@state()
|
||||
private _items: Array<BlockType> = [];
|
||||
|
||||
@@ -60,31 +73,44 @@ export class UmbInputBlockTypeElement<
|
||||
this.#filter = value as Array<UmbBlockTypeBaseModel>;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async create() {
|
||||
const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);
|
||||
this.#elementPickerModal = new UmbModalRouteRegistrationController(this, UMB_DOCUMENT_TYPE_PICKER_MODAL)
|
||||
.addUniquePaths(['propertyAlias'])
|
||||
.onSetup(() => {
|
||||
return {
|
||||
data: {
|
||||
hideTreeRoot: true,
|
||||
multiple: false,
|
||||
createAction: {
|
||||
extendWithPathParams: {
|
||||
parentUnique: null,
|
||||
presetAlias: 'element',
|
||||
},
|
||||
},
|
||||
pickableFilter: (docType) =>
|
||||
// Only pick elements:
|
||||
docType.isElement &&
|
||||
// Prevent picking the an already used element type:
|
||||
this.#filter &&
|
||||
this.#filter.find((x) => x.contentElementTypeKey === docType.unique) === undefined,
|
||||
},
|
||||
value: {
|
||||
selection: [],
|
||||
},
|
||||
};
|
||||
})
|
||||
.onSubmit((value) => {
|
||||
const selectedElementType = value.selection[0];
|
||||
|
||||
// TODO: Make as mode for the Picker Modal, so the click to select immediately submits the modal(And in that mode we do not want to see a Submit button).
|
||||
const modalContext = modalManager.open(this, UMB_DOCUMENT_TYPE_PICKER_MODAL, {
|
||||
data: {
|
||||
hideTreeRoot: true,
|
||||
multiple: false,
|
||||
pickableFilter: (docType) =>
|
||||
// Only pick elements:
|
||||
docType.isElement &&
|
||||
// Prevent picking the an already used element type:
|
||||
this.#filter &&
|
||||
this.#filter.find((x) => x.contentElementTypeKey === docType.unique) === undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const modalValue = await modalContext?.onSubmit();
|
||||
const selectedElementType = modalValue.selection[0];
|
||||
|
||||
if (selectedElementType) {
|
||||
this.dispatchEvent(new CustomEvent('create', { detail: { contentElementTypeKey: selectedElementType } }));
|
||||
}
|
||||
if (selectedElementType) {
|
||||
this.dispatchEvent(new CustomEvent('create', { detail: { contentElementTypeKey: selectedElementType } }));
|
||||
}
|
||||
})
|
||||
.observeRouteBuilder((routeBuilder) => {
|
||||
const oldPath = this._pickerPath;
|
||||
this._pickerPath = routeBuilder({});
|
||||
this.requestUpdate('_pickerPath', oldPath);
|
||||
});
|
||||
}
|
||||
|
||||
deleteItem(contentElementTypeKey: string) {
|
||||
@@ -131,12 +157,14 @@ export class UmbInputBlockTypeElement<
|
||||
};
|
||||
|
||||
#renderButton() {
|
||||
return html`
|
||||
<uui-button id="add-button" look="placeholder" @click=${() => this.create()} label="open">
|
||||
<uui-icon name="icon-add"></uui-icon>
|
||||
Add
|
||||
</uui-button>
|
||||
`;
|
||||
return this._pickerPath
|
||||
? html`
|
||||
<uui-button id="add-button" look="placeholder" href=${this._pickerPath} label="open">
|
||||
<uui-icon name="icon-add"></uui-icon>
|
||||
Add
|
||||
</uui-button>
|
||||
`
|
||||
: null;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { UmbWorkspaceData, UmbWorkspaceValue } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbWorkspaceModalData, UmbWorkspaceModalValue } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbBlockWorkspaceData<OriginDataType = unknown> extends UmbWorkspaceData {
|
||||
export interface UmbBlockWorkspaceData<OriginDataType = unknown> extends UmbWorkspaceModalData {
|
||||
originData: OriginDataType;
|
||||
}
|
||||
|
||||
export const UMB_BLOCK_WORKSPACE_MODAL = new UmbModalToken<UmbBlockWorkspaceData, UmbWorkspaceValue>(
|
||||
export const UMB_BLOCK_WORKSPACE_MODAL = new UmbModalToken<UmbBlockWorkspaceData, UmbWorkspaceModalValue>(
|
||||
'Umb.Modal.Workspace',
|
||||
{
|
||||
modal: {
|
||||
@@ -15,4 +15,4 @@ export const UMB_BLOCK_WORKSPACE_MODAL = new UmbModalToken<UmbBlockWorkspaceData
|
||||
data: { entityType: 'block', preset: {}, originData: {} },
|
||||
// Recast the type, so the entityType data prop is not required:
|
||||
},
|
||||
) as UmbModalToken<Omit<UmbWorkspaceData, 'entityType'>, UmbWorkspaceValue>;
|
||||
) as UmbModalToken<Omit<UmbWorkspaceModalData, 'entityType'>, UmbWorkspaceModalValue>;
|
||||
|
||||
@@ -23,7 +23,6 @@ export * from './input-multi-url/index.js';
|
||||
export * from './input-number-range/index.js';
|
||||
export * from './input-radio-button-list/index.js';
|
||||
export * from './input-slider/index.js';
|
||||
export * from './input-tree-picker-source/index.js';
|
||||
export * from './input-toggle/index.js';
|
||||
export * from './input-upload-field/index.js';
|
||||
export * from './multiple-color-picker-input/index.js';
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from './input-tree-picker-source.element.js';
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './composition-picker/composition-picker-modal.token.js';
|
||||
export * from './property-type-settings/property-type-settings-modal.token.js';
|
||||
export * from './property-type-settings/property-type-settings-modal.context-token.js';
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { UmbPropertyTypeWorkspaceContext } from './property-type-settings-modal.context.js';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||
|
||||
export const UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT = new UmbContextToken<
|
||||
UmbWorkspaceContext,
|
||||
UmbPropertyTypeWorkspaceContext
|
||||
>(
|
||||
'UmbWorkspaceContext',
|
||||
undefined,
|
||||
(context): context is UmbPropertyTypeWorkspaceContext => context.getEntityType() === 'property-type',
|
||||
);
|
||||
@@ -1,7 +1,12 @@
|
||||
import { UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT } from './property-type-settings-modal.context-token.js';
|
||||
import type {
|
||||
UmbPropertyTypeSettingsModalData,
|
||||
UmbPropertyTypeSettingsModalValue,
|
||||
} from './property-type-settings-modal.token.js';
|
||||
import type { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import { UMB_MODAL_CONTEXT, type UmbModalContext } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export const UMB_PROPERTY_TYPE_WORKSPACE_ALIAS = 'Umb.Workspace.PropertyType';
|
||||
|
||||
@@ -12,8 +17,14 @@ export class UmbPropertyTypeWorkspaceContext
|
||||
extends UmbContextBase<UmbPropertyTypeWorkspaceContext>
|
||||
implements UmbWorkspaceContext
|
||||
{
|
||||
#modal?: UmbModalContext<UmbPropertyTypeSettingsModalData, UmbPropertyTypeSettingsModalValue>;
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT);
|
||||
|
||||
this.consumeContext(UMB_MODAL_CONTEXT, (context) => {
|
||||
this.#modal = context as UmbModalContext<UmbPropertyTypeSettingsModalData, UmbPropertyTypeSettingsModalValue>;
|
||||
});
|
||||
}
|
||||
|
||||
get workspaceAlias() {
|
||||
@@ -21,21 +32,16 @@ export class UmbPropertyTypeWorkspaceContext
|
||||
}
|
||||
|
||||
getUnique() {
|
||||
return undefined;
|
||||
return this.#modal?.getValue()?.alias ?? '';
|
||||
}
|
||||
|
||||
getEntityType() {
|
||||
return 'property-type';
|
||||
}
|
||||
|
||||
getLabel() {
|
||||
return this.#modal?.getValue()?.name ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
export default UmbPropertyTypeWorkspaceContext;
|
||||
|
||||
export const UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT = new UmbContextToken<
|
||||
UmbWorkspaceContext,
|
||||
UmbPropertyTypeWorkspaceContext
|
||||
>(
|
||||
'UmbWorkspaceContext',
|
||||
undefined,
|
||||
(context): context is UmbPropertyTypeWorkspaceContext => context.getEntityType() === 'property-type',
|
||||
);
|
||||
|
||||
@@ -64,8 +64,7 @@ export class UmbPropertyTypeSettingsModalElement extends UmbModalBaseElement<
|
||||
super.connectedCallback();
|
||||
|
||||
this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, (instance) => {
|
||||
if (!this.data?.contentTypeId) return;
|
||||
if (instance.getUnique() !== this.data.contentTypeId) {
|
||||
if (!this.data?.contentTypeId || instance.getUnique() !== this.data.contentTypeId) {
|
||||
// We can currently only edit properties that are part of a content type workspace, which has to be present outside of the modal. [NL]
|
||||
throw new Error(
|
||||
'The content type workspace context does not match the content type id of the property type settings modal.',
|
||||
|
||||
@@ -120,18 +120,18 @@ export class UmbContentTypeStructureManager<
|
||||
*/
|
||||
public async create(parentUnique: string | null) {
|
||||
const contentType = this.getOwnerContentType();
|
||||
if (!contentType || !contentType.unique) return false;
|
||||
if (!contentType || !contentType.unique) {
|
||||
throw new Error('Could not find the Content Type to create');
|
||||
}
|
||||
|
||||
const { data } = await this.#repository.create(contentType, parentUnique);
|
||||
if (!data) return false;
|
||||
if (!data) return Promise.reject();
|
||||
|
||||
// Update state with latest version:
|
||||
this.#contentTypes.updateOne(contentType.unique, data);
|
||||
|
||||
// Start observe the new content type in the store, as we did not do that when it was a scaffold/local-version.
|
||||
this._observeContentType(data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async _loadContentTypeCompositions(contentType: T) {
|
||||
|
||||
@@ -8,6 +8,7 @@ export interface UmbContentTypeWorkspaceContext<ContentTypeType extends UmbConte
|
||||
readonly IS_CONTENT_TYPE_WORKSPACE_CONTEXT: true;
|
||||
|
||||
readonly name: Observable<string | undefined>;
|
||||
getName(): string | undefined;
|
||||
readonly alias: Observable<string | undefined>;
|
||||
readonly description: Observable<string | undefined>;
|
||||
readonly icon: Observable<string | undefined>;
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export type * from './content-type-workspace-context.interface.js';
|
||||
export * from './content-type-workspace.context-token.js';
|
||||
export * from './views/design/content-type-design-editor-property.context-token.js';
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
import type { UmbPropertyTypeContext } from './content-type-design-editor-property.context.js';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
|
||||
export const UMB_PROPERTY_TYPE_CONTEXT = new UmbContextToken<UmbPropertyTypeContext>('UmbPropertyTypeContext');
|
||||
@@ -0,0 +1,34 @@
|
||||
import { UMB_PROPERTY_TYPE_CONTEXT } from './content-type-design-editor-property.context-token.js';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import { UmbStringState } from '@umbraco-cms/backoffice/observable-api';
|
||||
|
||||
export class UmbPropertyTypeContext extends UmbContextBase<UmbPropertyTypeContext> {
|
||||
#alias = new UmbStringState(undefined);
|
||||
public readonly alias = this.#alias.asObservable();
|
||||
#label = new UmbStringState(undefined);
|
||||
public readonly label = this.#label.asObservable();
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, UMB_PROPERTY_TYPE_CONTEXT);
|
||||
}
|
||||
|
||||
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 destroy(): void {
|
||||
super.destroy();
|
||||
this.#alias.destroy();
|
||||
this.#label.destroy();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UmbPropertyTypeContext } from './content-type-design-editor-property.context.js';
|
||||
import { UmbDataTypeDetailRepository } from '@umbraco-cms/backoffice/data-type';
|
||||
import type { UUIInputElement } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
@@ -25,6 +26,7 @@ export class UmbContentTypeDesignEditorPropertyElement extends UmbLitElement {
|
||||
#dataTypeDetailRepository = new UmbDataTypeDetailRepository(this);
|
||||
#settingsModal;
|
||||
#dataTypeUnique?: string;
|
||||
#context = new UmbPropertyTypeContext(this);
|
||||
|
||||
@property({ attribute: false })
|
||||
public set propertyStructureHelper(value: UmbContentTypePropertyStructureHelper<UmbContentTypeModel> | undefined) {
|
||||
@@ -51,6 +53,8 @@ export class UmbContentTypeDesignEditorPropertyElement extends UmbLitElement {
|
||||
const oldValue = this._property;
|
||||
if (value === oldValue) return;
|
||||
this._property = value;
|
||||
this.#context.setAlias(value?.alias);
|
||||
this.#context.setLabel(value?.name);
|
||||
this.#checkInherited();
|
||||
this.#settingsModal.setUniquePathValue('propertyId', value?.id);
|
||||
this.#setDataType(this._property?.dataType?.unique);
|
||||
|
||||
@@ -38,7 +38,7 @@ export class UmbModalManagerContext extends UmbContextBase<UmbModalManagerContex
|
||||
* @memberof UmbModalManagerContext
|
||||
*/
|
||||
public open<
|
||||
ModalData extends object = object,
|
||||
ModalData extends { [key: string]: any } = { [key: string]: any },
|
||||
ModalValue = unknown,
|
||||
ModalAliasTypeAsToken extends UmbModalToken = UmbModalToken<ModalData, ModalValue>,
|
||||
>(
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbId } from '@umbraco-cms/backoffice/id';
|
||||
import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import { type UmbDeepPartialObject, umbDeepMerge } from '@umbraco-cms/backoffice/utils';
|
||||
|
||||
export interface UmbModalRejectReason {
|
||||
type: string;
|
||||
@@ -13,7 +14,9 @@ export interface UmbModalRejectReason {
|
||||
|
||||
export type UmbModalContextClassArgs<
|
||||
ModalAliasType extends string | UmbModalToken,
|
||||
ModalAliasTypeAsToken extends UmbModalToken = ModalAliasType extends UmbModalToken ? ModalAliasType : UmbModalToken,
|
||||
ModalAliasTypeAsToken extends UmbModalToken = ModalAliasType extends UmbModalToken
|
||||
? ModalAliasType
|
||||
: UmbModalToken<never, never>,
|
||||
> = {
|
||||
router?: IRouterSlot | null;
|
||||
data?: ModalAliasTypeAsToken['DATA'];
|
||||
@@ -22,7 +25,10 @@ export type UmbModalContextClassArgs<
|
||||
};
|
||||
|
||||
// TODO: consider splitting this into two separate handlers
|
||||
export class UmbModalContext<ModalPreset extends object = object, ModalValue = any> extends UmbControllerBase {
|
||||
export class UmbModalContext<
|
||||
ModalPreset extends { [key: string]: any } = { [key: string]: any },
|
||||
ModalValue = any,
|
||||
> extends UmbControllerBase {
|
||||
//
|
||||
#submitPromise: Promise<ModalValue>;
|
||||
#submitResolver?: (value: ModalValue) => void;
|
||||
@@ -60,7 +66,13 @@ export class UmbModalContext<ModalPreset extends object = object, ModalValue = a
|
||||
this.backdropBackground = args.modal?.backdropBackground || this.backdropBackground;
|
||||
|
||||
const defaultData = this.alias instanceof UmbModalToken ? this.alias.getDefaultData() : undefined;
|
||||
this.data = Object.freeze({ ...defaultData, ...args.data } as ModalPreset);
|
||||
this.data = Object.freeze(
|
||||
// If we have both data and defaultData perform a deep merge
|
||||
args.data && defaultData
|
||||
? (umbDeepMerge(args.data as UmbDeepPartialObject<ModalPreset>, defaultData) as ModalPreset)
|
||||
: // otherwise pick one of them:
|
||||
(args.data as ModalPreset) ?? defaultData,
|
||||
);
|
||||
|
||||
const initValue =
|
||||
args.value ?? (this.alias instanceof UmbModalToken ? (this.alias as UmbModalToken).getDefaultValue() : undefined);
|
||||
|
||||
@@ -1,25 +1,42 @@
|
||||
import type { UmbModalToken } from '../token/index.js';
|
||||
import type { UmbModalConfig, UmbModalContext, UmbModalManagerContext, UmbModalRouteRegistration } from '../index.js';
|
||||
import type { UmbModalContextClassArgs } from '../context/modal.context.js';
|
||||
import { type Params, type IRouterSlot, UMB_ROUTE_CONTEXT, encodeFolderName } from '@umbraco-cms/backoffice/router';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import { UmbId } from '@umbraco-cms/backoffice/id';
|
||||
import type { UmbDeepPartialObject } from '@umbraco-cms/backoffice/utils';
|
||||
|
||||
export type UmbModalRouteBuilder = (params: { [key: string]: string | number } | null) => string;
|
||||
|
||||
export type UmbModalRouteSetupReturn<UmbModalTokenData, UmbModalTokenValue> = UmbModalTokenValue extends undefined
|
||||
? {
|
||||
modal?: UmbModalConfig;
|
||||
data: UmbModalTokenData;
|
||||
value?: UmbModalTokenValue;
|
||||
}
|
||||
: {
|
||||
modal?: UmbModalConfig;
|
||||
data: UmbModalTokenData;
|
||||
value: UmbModalTokenValue;
|
||||
};
|
||||
export class UmbModalRouteRegistrationController<UmbModalTokenData extends object = object, UmbModalTokenValue = any>
|
||||
? UmbModalTokenValue extends undefined
|
||||
? {
|
||||
modal?: UmbDeepPartialObject<UmbModalConfig>;
|
||||
data?: UmbDeepPartialObject<UmbModalTokenData>;
|
||||
value?: UmbModalTokenValue;
|
||||
}
|
||||
: {
|
||||
modal?: UmbDeepPartialObject<UmbModalConfig>;
|
||||
data?: UmbDeepPartialObject<UmbModalTokenData>;
|
||||
value: UmbModalTokenValue;
|
||||
}
|
||||
: UmbModalTokenValue extends undefined
|
||||
? {
|
||||
modal?: UmbDeepPartialObject<UmbModalConfig>;
|
||||
data: UmbDeepPartialObject<UmbModalTokenData>;
|
||||
value?: UmbModalTokenValue;
|
||||
}
|
||||
: {
|
||||
modal?: UmbDeepPartialObject<UmbModalConfig>;
|
||||
data: UmbDeepPartialObject<UmbModalTokenData>;
|
||||
value: UmbModalTokenValue;
|
||||
};
|
||||
export class UmbModalRouteRegistrationController<
|
||||
UmbModalTokenData extends { [key: string]: any } = { [key: string]: any },
|
||||
UmbModalTokenValue = any,
|
||||
>
|
||||
extends UmbControllerBase
|
||||
implements UmbModalRouteRegistration<UmbModalTokenData, UmbModalTokenValue>
|
||||
{
|
||||
@@ -293,8 +310,8 @@ export class UmbModalRouteRegistrationController<UmbModalTokenData extends objec
|
||||
modal: {},
|
||||
...modalData,
|
||||
router,
|
||||
};
|
||||
args.modal.key = this.#key;
|
||||
} as UmbModalContextClassArgs<UmbModalToken<UmbModalTokenData, UmbModalTokenValue>>;
|
||||
args.modal!.key = this.#key;
|
||||
|
||||
this.#modalContext = modalManagerContext.open(this, this.#modalAlias, args);
|
||||
this.#modalContext.onSubmit().then(this.#onSubmit, this.#onReject);
|
||||
|
||||
@@ -3,7 +3,10 @@ import type { UmbModalContext, UmbModalRouteBuilder } from '../index.js';
|
||||
import type { UmbModalToken } from '../token/modal-token.js';
|
||||
import type { IRouterSlot, Params } from '@umbraco-cms/backoffice/router';
|
||||
|
||||
export interface UmbModalRouteRegistration<UmbModalTokenData extends object = object, UmbModalTokenValue = any> {
|
||||
export interface UmbModalRouteRegistration<
|
||||
UmbModalTokenData extends { [key: string]: any } = { [key: string]: any },
|
||||
UmbModalTokenValue = any,
|
||||
> {
|
||||
key: string;
|
||||
alias: UmbModalToken<UmbModalTokenData, UmbModalTokenValue> | string;
|
||||
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import type { UmbModalConfig } from '../context/modal-manager.context.js';
|
||||
|
||||
export interface UmbModalTokenDefaults<ModalDataType extends object = object, ModalValueType = unknown> {
|
||||
export interface UmbModalTokenDefaults<
|
||||
ModalDataType extends { [key: string]: any } = { [key: string]: any },
|
||||
ModalValueType = unknown,
|
||||
> {
|
||||
modal?: UmbModalConfig;
|
||||
data?: ModalDataType;
|
||||
value?: ModalValueType;
|
||||
}
|
||||
|
||||
export class UmbModalToken<ModalDataType extends object = object, ModalValueType = unknown> {
|
||||
export class UmbModalToken<
|
||||
ModalDataType extends { [key: string]: any } = { [key: string]: any },
|
||||
ModalValueType = unknown,
|
||||
> {
|
||||
/**
|
||||
* Get the data type of the token's data.
|
||||
*
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import { UmbModalToken } from './modal-token.js';
|
||||
export interface UmbWorkspaceData<DataModelType = unknown> {
|
||||
export interface UmbWorkspaceModalData<DataModelType = unknown> {
|
||||
entityType: string;
|
||||
preset: Partial<DataModelType>;
|
||||
}
|
||||
|
||||
// TODO: It would be good with a WorkspaceValueBaseType, to avoid the hardcoded type for unique here:
|
||||
export type UmbWorkspaceValue =
|
||||
export type UmbWorkspaceModalValue =
|
||||
| {
|
||||
unique: string;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
export const UMB_WORKSPACE_MODAL = new UmbModalToken<UmbWorkspaceData, UmbWorkspaceValue>('Umb.Modal.Workspace', {
|
||||
modal: {
|
||||
type: 'sidebar',
|
||||
size: 'large',
|
||||
export const UMB_WORKSPACE_MODAL = new UmbModalToken<UmbWorkspaceModalData, UmbWorkspaceModalValue>(
|
||||
'Umb.Modal.Workspace',
|
||||
{
|
||||
modal: {
|
||||
type: 'sidebar',
|
||||
size: 'large',
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
import { expect } from '@open-wc/testing';
|
||||
import { createRoutePathBuilder } from './generate-route-path-builder.function.js';
|
||||
import { umbGenerateRoutePathBuilder } from './generate-route-path-builder.function.js';
|
||||
|
||||
describe('createRoutePathBuilder', () => {
|
||||
it('should return a function that builds a route path without parameters', () => {
|
||||
const buildPath = createRoutePathBuilder('test/path');
|
||||
const buildPath = umbGenerateRoutePathBuilder('test/path');
|
||||
expect(buildPath(null)).to.eq('/test/path/');
|
||||
});
|
||||
|
||||
it('should return a function that builds a route path with parameters', () => {
|
||||
const buildPath = createRoutePathBuilder(':param0/test/:param1/path/:param2');
|
||||
const buildPath = umbGenerateRoutePathBuilder(':param0/test/:param1/path/:param2');
|
||||
expect(buildPath({ param0: 'value0', param1: 'value1', param2: 'value2' })).to.eq(
|
||||
'/value0/test/value1/path/value2/',
|
||||
);
|
||||
});
|
||||
|
||||
it('should convert number parameters to strings', () => {
|
||||
const buildPath = createRoutePathBuilder('test/:param1/path/:param2');
|
||||
const buildPath = umbGenerateRoutePathBuilder('test/:param1/path/:param2');
|
||||
expect(buildPath({ param1: 123, param2: 456 })).to.eq('/test/123/path/456/');
|
||||
});
|
||||
|
||||
it('should not consider route segments that resembles parameters as parameters', () => {
|
||||
const buildPath = createRoutePathBuilder('test/uc:store/path');
|
||||
const buildPath = umbGenerateRoutePathBuilder('test/uc:store/path');
|
||||
expect(buildPath({ someOtherParam: 'test' })).to.eq('/test/uc:store/path/');
|
||||
});
|
||||
|
||||
it('should support multiple parameters with the same name', () => {
|
||||
const buildPath = createRoutePathBuilder('test/:param1/path/:param1');
|
||||
const buildPath = umbGenerateRoutePathBuilder('test/:param1/path/:param1');
|
||||
expect(buildPath({ param1: 'value1' })).to.eq('/test/value1/path/value1/');
|
||||
});
|
||||
|
||||
it('should not consider parameters that are not in the params object', () => {
|
||||
const buildPath = createRoutePathBuilder('test/:param1/path/:param2');
|
||||
const buildPath = umbGenerateRoutePathBuilder('test/:param1/path/:param2');
|
||||
expect(buildPath({ param1: 'value1' })).to.eq('/test/value1/path/:param2/');
|
||||
});
|
||||
|
||||
it('should support complex objects as parameters with a custom toString method', () => {
|
||||
const buildPath = createRoutePathBuilder('test/:param1/path/:param2');
|
||||
const buildPath = umbGenerateRoutePathBuilder('test/:param1/path/:param2');
|
||||
const obj = {
|
||||
toString() {
|
||||
return 'value1';
|
||||
|
||||
@@ -1,21 +1,13 @@
|
||||
/* eslint-disable */
|
||||
import { type UrlParametersRecord, umbUrlPatternToString } from '../utils/path/url-pattern-to-string.function.js';
|
||||
import { stripSlash } from '@umbraco-cms/backoffice/external/router-slot'; // This must only include the util to avoid side effects of registering the route element.
|
||||
|
||||
const PARAM_IDENTIFIER = /:([^\/]+)/g;
|
||||
|
||||
export function createRoutePathBuilder(path: string) {
|
||||
return (params: { [key: string]: string | number | { toString: () => string } } | null) => {
|
||||
return (
|
||||
'/' +
|
||||
stripSlash(
|
||||
params
|
||||
? path.replace(PARAM_IDENTIFIER, (_substring: string, ...args: string[]) => {
|
||||
// Replace the parameter with the value from the params object or the parameter name if it doesn't exist (args[0] is the parameter name without the colon)
|
||||
return typeof params[args[0]] !== 'undefined' ? params[args[0]].toString() : `:${args[0]}`;
|
||||
})
|
||||
: path,
|
||||
) +
|
||||
'/'
|
||||
);
|
||||
export function umbGenerateRoutePathBuilder(path: string) {
|
||||
return (params: UrlParametersRecord | null) => {
|
||||
return '/' + stripSlash(umbUrlPatternToString(path, params)) + '/';
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `umbGenerateRoutePathBuilder` instead.
|
||||
*/
|
||||
export { umbGenerateRoutePathBuilder as umbCreateRoutePathBuilder };
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
export * from '@umbraco-cms/backoffice/external/router-slot';
|
||||
export * from './encode-folder-name.function.js';
|
||||
export * from './generate-route-path-builder.function.js';
|
||||
export * from './route.context.js';
|
||||
export * from './route.interface.js';
|
||||
export * from './router-slot-change.event.js';
|
||||
export * from './router-slot-init.event.js';
|
||||
export * from './router-slot.element.js';
|
||||
export * from './path-pattern.class.js';
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { umbUrlPatternToString } from '../utils/path/url-pattern-to-string.function.js';
|
||||
|
||||
export type UmbPathPatternParamsType = { [key: string]: any };
|
||||
|
||||
// Replaces property union type null with 'null' [NL]
|
||||
type ReplaceNull<T> = T extends null ? 'null' : T;
|
||||
export type UmbPathPatternTypeAsEncodedParamsType<T> = {
|
||||
[P in keyof T]: T[P] extends (infer R)[] ? ReplaceNull<R>[] : ReplaceNull<T[P]>;
|
||||
};
|
||||
|
||||
export class UmbPathPattern<
|
||||
LocalParamsType extends UmbPathPatternParamsType = UmbPathPatternParamsType,
|
||||
BaseParamsType extends UmbPathPatternParamsType = LocalParamsType,
|
||||
> {
|
||||
#local: string;
|
||||
#base: string;
|
||||
|
||||
/**
|
||||
* Get the params type of the path pattern
|
||||
*
|
||||
* @public
|
||||
* @type {T}
|
||||
* @memberOf UmbPathPattern
|
||||
* @example `typeof MyPathPattern.PARAMS`
|
||||
*/
|
||||
readonly PARAMS!: LocalParamsType;
|
||||
|
||||
/**
|
||||
* Get absolute params type of the path pattern
|
||||
*
|
||||
* @public
|
||||
* @type {T}
|
||||
* @memberOf UmbPathPattern
|
||||
* @example `typeof MyPathPattern.ABSOLUTE_PARAMS`
|
||||
*/
|
||||
readonly ABSOLUTE_PARAMS!: LocalParamsType & BaseParamsType;
|
||||
|
||||
constructor(localPattern: string, basePath?: UmbPathPattern | string) {
|
||||
this.#local = localPattern;
|
||||
basePath = basePath?.toString() ?? '';
|
||||
this.#base = basePath.lastIndexOf('/') !== basePath.length - 1 ? basePath + '/' : basePath;
|
||||
}
|
||||
|
||||
generateLocal(params: LocalParamsType) {
|
||||
return umbUrlPatternToString(this.#local, params);
|
||||
}
|
||||
/**
|
||||
* generate an absolute path from the path pattern
|
||||
* @param params
|
||||
* @param baseParams
|
||||
* @returns
|
||||
*/
|
||||
generateAbsolute(params: LocalParamsType & BaseParamsType) {
|
||||
return (
|
||||
(this.#base.indexOf(':') !== -1 ? umbUrlPatternToString(this.#base, params) : this.#base) +
|
||||
umbUrlPatternToString(this.#local, params)
|
||||
);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.#local;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { UmbRoute } from './route.interface.js';
|
||||
import { createRoutePathBuilder } from './generate-route-path-builder.function.js';
|
||||
import { umbGenerateRoutePathBuilder } from './generate-route-path-builder.function.js';
|
||||
import type { IRoutingInfo, IRouterSlot } from '@umbraco-cms/backoffice/external/router-slot';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
@@ -70,8 +70,8 @@ export class UmbRouteContext extends UmbContextBase<UmbRouteContext> {
|
||||
}
|
||||
|
||||
#removeModalPath(info: IRoutingInfo) {
|
||||
// Reset the URL to the routerBasePath + routerActiveLocalPath
|
||||
const folderToRemove = info.match.match.input;
|
||||
// Reset the URL to the routerBasePath + routerActiveLocalPath [NL]
|
||||
const folderToRemove = info.match.fragments.consumed;
|
||||
if (folderToRemove && window.location.href.includes(folderToRemove)) {
|
||||
window.history.pushState({}, '', this.#routerBasePath + '/' + this.#routerActiveLocalPath);
|
||||
}
|
||||
@@ -147,7 +147,7 @@ export class UmbRouteContext extends UmbContextBase<UmbRouteContext> {
|
||||
: this.#routerActiveLocalPath + '/'
|
||||
: '';
|
||||
const localPath = routeBasePath + routeActiveLocalPath + modalRegistration.generateModalPath();
|
||||
const urlBuilder = createRoutePathBuilder(localPath);
|
||||
const urlBuilder = umbGenerateRoutePathBuilder(localPath);
|
||||
|
||||
modalRegistration._internal_setRouteBuilder(urlBuilder);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './components/index.js';
|
||||
export * from './paths.js';
|
||||
export * from './section-default.element.js';
|
||||
export * from './section-main/index.js';
|
||||
export * from './section-picker-modal/section-picker-modal.token.js';
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import { UmbPathPattern } from '../router/path-pattern.class.js';
|
||||
|
||||
export const UMB_SECTION_PATH_PATTERN = new UmbPathPattern<{ sectionName: string }>('section/:sectionName');
|
||||
@@ -13,6 +13,7 @@ import type { UmbRoute } from '@umbraco-cms/backoffice/router';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import type { UmbExtensionElementInitializer } from '@umbraco-cms/backoffice/extension-api';
|
||||
import { UmbExtensionsElementInitializer } from '@umbraco-cms/backoffice/extension-api';
|
||||
import { UMB_WORKSPACE_PATH_PATTERN } from '@umbraco-cms/backoffice/workspace';
|
||||
|
||||
/**
|
||||
* @export
|
||||
@@ -58,7 +59,7 @@ export class UmbSectionDefaultElement extends UmbLitElement implements UmbSectio
|
||||
#createRoutes() {
|
||||
this._routes = [
|
||||
{
|
||||
path: 'workspace/:entityType',
|
||||
path: UMB_WORKSPACE_PATH_PATTERN.toString(),
|
||||
component: () => import('../workspace/workspace.element.js'),
|
||||
setup: (element, info) => {
|
||||
(element as UmbWorkspaceElement).entityType = info.match.params.entityType;
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from './input-tree/index.js';
|
||||
@@ -1 +0,0 @@
|
||||
export * from './input-tree.element.js';
|
||||
@@ -1,15 +0,0 @@
|
||||
import type { Meta, StoryObj } from '@storybook/web-components';
|
||||
import './input-tree.element.js';
|
||||
import type { UmbInputTreeElement } from './input-tree.element.js';
|
||||
|
||||
const meta: Meta<UmbInputTreeElement> = {
|
||||
title: 'Components/Inputs/Tree',
|
||||
component: 'umb-input-tree',
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<UmbInputTreeElement>;
|
||||
|
||||
export const Overview: Story = {
|
||||
args: {},
|
||||
};
|
||||
@@ -1,6 +1,5 @@
|
||||
import { UmbRequestReloadTreeItemChildrenEvent } from './reload-tree-item-children/index.js';
|
||||
|
||||
export * from './components/index.js';
|
||||
export * from './tree-item/index.js';
|
||||
export * from './default/index.js';
|
||||
export * from './data/index.js';
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import type { UmbTreeSelectionConfiguration } from '../types.js';
|
||||
import type { UmbTreePickerModalData, UmbTreePickerModalValue } from './tree-picker-modal.token.js';
|
||||
import { html, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
|
||||
import { html, customElement, state, ifDefined, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import {
|
||||
UMB_WORKSPACE_MODAL,
|
||||
UmbModalBaseElement,
|
||||
UmbModalRouteRegistrationController,
|
||||
} from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbDeselectedEvent, UmbSelectedEvent, UmbSelectionChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
import type { UmbTreeElement, UmbTreeItemModelBase } from '@umbraco-cms/backoffice/tree';
|
||||
|
||||
@@ -17,17 +21,57 @@ export class UmbTreePickerModalElement<TreeItemType extends UmbTreeItemModelBase
|
||||
selection: [],
|
||||
};
|
||||
|
||||
@state()
|
||||
_createPath?: string;
|
||||
|
||||
@state()
|
||||
_createLabel?: string;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
// TODO: We should make a nicer way to observe the value..
|
||||
// TODO: We should make a nicer way to observe the value.. [NL]
|
||||
// This could be by observing when the modalCOntext gets set. [NL]
|
||||
if (this.modalContext) {
|
||||
this.observe(this.modalContext.value, (value) => {
|
||||
this._selectionConfiguration.selection = value?.selection ?? [];
|
||||
});
|
||||
}
|
||||
|
||||
// Same here [NL]
|
||||
this._selectionConfiguration.multiple = this.data?.multiple ?? false;
|
||||
|
||||
// TODO: If data.enableCreate is true, we should add a button to create a new item. [NL]
|
||||
// Does the tree know enough about this, for us to be able to create a new item? [NL]
|
||||
// I think we need to be able to get entityType and a parentId?, or do we only allow creation in the root? and then create via entity actions? [NL]
|
||||
// To remove the hardcoded URLs for workspaces of entity types, we could make an create event from the tree, which either this or the sidebar impl. will pick up and react to. [NL]
|
||||
// Or maybe the tree item context base can handle this? [NL]
|
||||
// Maybe its a general item context problem to be solved. [NL]
|
||||
const createAction = this.data?.createAction;
|
||||
if (createAction) {
|
||||
this._createLabel = createAction.label;
|
||||
new UmbModalRouteRegistrationController(
|
||||
this,
|
||||
(createAction.modalToken as typeof UMB_WORKSPACE_MODAL) ?? UMB_WORKSPACE_MODAL,
|
||||
)
|
||||
.onSetup(() => {
|
||||
return { data: createAction.modalData };
|
||||
})
|
||||
.onSubmit((value) => {
|
||||
if (value) {
|
||||
this.value = { selection: [value.unique] };
|
||||
this._submitModal();
|
||||
} else {
|
||||
this._rejectModal();
|
||||
}
|
||||
})
|
||||
.observeRouteBuilder((routeBuilder) => {
|
||||
const oldPath = this._createPath;
|
||||
this._createPath =
|
||||
routeBuilder({}) + createAction.extendWithPathPattern.generateLocal(createAction.extendWithPathParams);
|
||||
this.requestUpdate('_createPath', oldPath);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#onSelectionChange(event: UmbSelectionChangeEvent) {
|
||||
@@ -66,6 +110,12 @@ export class UmbTreePickerModalElement<TreeItemType extends UmbTreeItemModelBase
|
||||
</uui-box>
|
||||
<div slot="actions">
|
||||
<uui-button label=${this.localize.term('general_close')} @click=${this._rejectModal}></uui-button>
|
||||
${this._createPath
|
||||
? html` <uui-button
|
||||
label=${this.localize.string(this._createLabel ?? 'general_create')}
|
||||
look="secondary"
|
||||
href=${this._createPath}></uui-button>`
|
||||
: nothing}
|
||||
<uui-button
|
||||
label=${this.localize.term('general_choose')}
|
||||
look="primary"
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
import { UMB_TREE_PICKER_MODAL_ALIAS } from './constants.js';
|
||||
import type { UmbPickerModalData, UmbPickerModalValue } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbPickerModalData, UmbPickerModalValue, UmbWorkspaceModalData } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbPathPattern, UmbPathPatternParamsType } from '@umbraco-cms/backoffice/router';
|
||||
|
||||
export interface UmbTreePickerModalData<TreeItemType = any> extends UmbPickerModalData<TreeItemType> {
|
||||
export interface UmbTreePickerModalCreateActionData<PathPatternParamsType extends UmbPathPatternParamsType> {
|
||||
label: string;
|
||||
modalData: UmbWorkspaceModalData;
|
||||
modalToken?: UmbModalToken;
|
||||
extendWithPathPattern: UmbPathPattern;
|
||||
extendWithPathParams: PathPatternParamsType;
|
||||
}
|
||||
|
||||
export interface UmbTreePickerModalData<
|
||||
TreeItemType,
|
||||
PathPatternParamsType extends UmbPathPatternParamsType = UmbPathPatternParamsType,
|
||||
> extends UmbPickerModalData<TreeItemType> {
|
||||
treeAlias?: string;
|
||||
// Consider if it makes sense to move this into the UmbPickerModalData interface, but for now this is a TreePicker feature. [NL]
|
||||
createAction?: UmbTreePickerModalCreateActionData<PathPatternParamsType>;
|
||||
}
|
||||
|
||||
export interface UmbTreePickerModalValue extends UmbPickerModalValue {}
|
||||
|
||||
export const UMB_TREE_PICKER_MODAL = new UmbModalToken<UmbTreePickerModalData, UmbTreePickerModalValue>(
|
||||
export const UMB_TREE_PICKER_MODAL = new UmbModalToken<UmbTreePickerModalData<unknown>, UmbTreePickerModalValue>(
|
||||
UMB_TREE_PICKER_MODAL_ALIAS,
|
||||
{
|
||||
modal: {
|
||||
|
||||
@@ -3,12 +3,14 @@ export * from './direction/index.js';
|
||||
export * from './download/blob-download.function.js';
|
||||
export * from './get-processed-image-url.function.js';
|
||||
export * from './math/math.js';
|
||||
export * from './object/deep-merge.function.js';
|
||||
export * from './pagination-manager/pagination.manager.js';
|
||||
export * from './path/ensure-path-ends-with-slash.function.js';
|
||||
export * from './path/path-decode.function.js';
|
||||
export * from './path/path-encode.function.js';
|
||||
export * from './path/path-folder-name.function.js';
|
||||
export * from './path/umbraco-path.function.js';
|
||||
export * from './path/url-pattern-to-string.function.js';
|
||||
export * from './selection-manager/selection.manager.js';
|
||||
export * from './string/from-camel-case.function.js';
|
||||
export * from './string/generate-umbraco-alias.function.js';
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { UmbDeepPartialObject } from '../type/deep-partial-object.type.js';
|
||||
|
||||
/**
|
||||
* Deep merge two objects.
|
||||
* @param target
|
||||
* @param ...sources
|
||||
*/
|
||||
export function umbDeepMerge<
|
||||
T extends { [key: string]: any },
|
||||
PartialType extends UmbDeepPartialObject<T> = UmbDeepPartialObject<T>,
|
||||
>(source: PartialType, fallback: T) {
|
||||
const result = { ...fallback };
|
||||
|
||||
for (const key in source) {
|
||||
if (Object.prototype.hasOwnProperty.call(source, key) && source[key] !== undefined) {
|
||||
if (source[key]?.constructor === Object && fallback[key].constructor === Object) {
|
||||
result[key] = umbDeepMerge(source[key] as any, fallback[key]);
|
||||
} else {
|
||||
result[key] = source[key] as any;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { expect } from '@open-wc/testing';
|
||||
import { umbDeepMerge } from './deep-merge.function.js';
|
||||
|
||||
describe('UmbDeepMerge', () => {
|
||||
beforeEach(() => {});
|
||||
|
||||
describe('merge just objects', () => {
|
||||
it('transfers defined properties', () => {
|
||||
const defaults = {
|
||||
prop1: {
|
||||
name: 'prop1',
|
||||
value: 'value1',
|
||||
},
|
||||
prop2: {
|
||||
name: 'prop2',
|
||||
value: 'value2',
|
||||
},
|
||||
};
|
||||
const source = {
|
||||
prop2: {
|
||||
name: 'prop2_updatedName',
|
||||
},
|
||||
};
|
||||
const result = umbDeepMerge(source, defaults);
|
||||
|
||||
expect(result.prop1.name).to.equal('prop1');
|
||||
expect(result.prop2.name).to.equal('prop2_updatedName');
|
||||
});
|
||||
});
|
||||
|
||||
describe('merge objects with arrays', () => {
|
||||
// The arrays should not be merged, but take the value from the main object. [NL]
|
||||
it('transfers defined properties', () => {
|
||||
const defaults = {
|
||||
prop1: {
|
||||
name: 'prop1',
|
||||
value: ['entry1', 'entry2', 'entry3'],
|
||||
},
|
||||
prop2: {
|
||||
name: 'prop2',
|
||||
value: ['entry4', 'entry4', 'entry5'],
|
||||
},
|
||||
};
|
||||
const source = {
|
||||
prop1: {
|
||||
name: 'prop1_updatedName',
|
||||
},
|
||||
prop2: {
|
||||
name: 'prop2_updatedName',
|
||||
value: ['entry666'],
|
||||
},
|
||||
};
|
||||
const result = umbDeepMerge(source, defaults);
|
||||
|
||||
expect(result.prop1.name).to.equal('prop1_updatedName');
|
||||
expect(result.prop1.value.join(',')).to.equal(defaults.prop1.value.join(','));
|
||||
expect(result.prop2.name).to.equal('prop2_updatedName');
|
||||
expect(result.prop2.value.join(',')).to.equal(source.prop2.value.join(','));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
export type UrlParametersRecord = Record<string, string | number | { toString: () => string } | null>;
|
||||
|
||||
const PARAM_IDENTIFIER = /:([^/]+)/g;
|
||||
|
||||
export function umbUrlPatternToString(pattern: string, params: UrlParametersRecord | null): string {
|
||||
return params
|
||||
? pattern.replace(PARAM_IDENTIFIER, (_substring: string, ...args: string[]) => {
|
||||
const segmentValue = params![args[0]]; // (segmentValue is the value to replace the parameter)
|
||||
// Replace the path-segment with the value from the params object or 'null' if it doesn't exist
|
||||
return segmentValue === undefined ? `:${args[0]}` : segmentValue === null ? 'null' : segmentValue.toString();
|
||||
})
|
||||
: pattern;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*export type DeepPartial<T> = T extends { [key: string]: any }
|
||||
? {
|
||||
[P in keyof T]?: DeepPartial<T[P]>;
|
||||
}
|
||||
: T;
|
||||
*/
|
||||
|
||||
// Notice this can be way more complex, but in this case I just wanted to cover pure objects, to match our deep merge function [NL]
|
||||
// See https://stackoverflow.com/questions/61132262/typescript-deep-partial for more extensive solutions.
|
||||
/**
|
||||
* Deep partial object type, making objects and their properties optional, but only until a property of a different type is encountered.
|
||||
* This means if an object holds a property with an array that holds objects, the array will be made optional, but the properties of the objects inside the array will not be changed.
|
||||
* @type UmbDeepPartialObject
|
||||
* @generic T - The object to make partial.
|
||||
* @returns A type with all properties of objects made optional.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export type UmbDeepPartialObject<T> = T extends Function
|
||||
? T
|
||||
: // Thing extends Array<infer InferredArrayMember>
|
||||
// ? DeepPartialArray<InferredArrayMember> :
|
||||
T extends { [key: string]: any }
|
||||
? UmbDeepPartialObjectProperty<T>
|
||||
: T | undefined;
|
||||
|
||||
//interface DeepPartialArray<Thing> extends Array<DeepPartial<Thing>> {}
|
||||
|
||||
type UmbDeepPartialObjectProperty<Thing> = {
|
||||
[Key in keyof Thing]?: UmbDeepPartialObject<Thing[Key]>;
|
||||
};
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './deep-partial-object.type.js';
|
||||
export * from './diff.type.js';
|
||||
export * from './partial-some.type.js';
|
||||
|
||||
@@ -1 +1 @@
|
||||
export type PartialSome<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
||||
export type UmbPartialSome<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { PartialSome } from '@umbraco-cms/backoffice/utils';
|
||||
import type { UmbPartialSome } from '@umbraco-cms/backoffice/utils';
|
||||
import type { UmbVariantPropertyValueModel } from '@umbraco-cms/backoffice/variant';
|
||||
|
||||
/**
|
||||
@@ -9,7 +9,7 @@ import type { UmbVariantPropertyValueModel } from '@umbraco-cms/backoffice/varia
|
||||
* @returns
|
||||
*/
|
||||
export function UmbDataPathPropertyValueFilter(
|
||||
value: PartialSome<Omit<UmbVariantPropertyValueModel, 'value'>, 'culture' | 'segment'>,
|
||||
value: UmbPartialSome<Omit<UmbVariantPropertyValueModel, 'value'>, 'culture' | 'segment'>,
|
||||
): string {
|
||||
// write a array of strings for each property, where alias must be present and culture and segment are optional
|
||||
const filters: Array<string> = [`@.alias = '${value.alias}'`];
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { UmbSubmittableWorkspaceContextBase } from '../contexts/index.js';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import { createRoutePathBuilder, type UmbRouterSlotElement } from '@umbraco-cms/backoffice/router';
|
||||
import { ensurePathEndsWithSlash } from '@umbraco-cms/backoffice/utils';
|
||||
import type { UmbRouterSlotElement } from '@umbraco-cms/backoffice/router';
|
||||
import { ensurePathEndsWithSlash, umbUrlPatternToString } from '@umbraco-cms/backoffice/utils';
|
||||
|
||||
/**
|
||||
* Observe the workspace context to see if the entity is new or not.
|
||||
@@ -28,7 +28,7 @@ export class UmbWorkspaceIsNewRedirectController extends UmbControllerBase {
|
||||
if (router && unique) {
|
||||
const routerPath = router.absoluteRouterPath;
|
||||
if (routerPath) {
|
||||
const newPath: string = createRoutePathBuilder(ensurePathEndsWithSlash(routerPath) + 'edit/:id')({
|
||||
const newPath: string = umbUrlPatternToString(ensurePathEndsWithSlash(routerPath) + 'edit/:id', {
|
||||
id: unique,
|
||||
});
|
||||
this.destroy();
|
||||
|
||||
@@ -4,6 +4,7 @@ export * from './controllers/index.js';
|
||||
export * from './modals/index.js';
|
||||
export * from './workspace-property-dataset/index.js';
|
||||
export * from './workspace.element.js';
|
||||
export * from './paths.js';
|
||||
|
||||
export type * from './conditions/index.js';
|
||||
export type * from './types.js';
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import type { CSSResultGroup } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { css, html, customElement, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import type { UmbWorkspaceData } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbWorkspaceModalData } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
|
||||
@customElement('umb-workspace-modal')
|
||||
export class UmbWorkspaceModalElement extends UmbLitElement {
|
||||
@property({ attribute: false })
|
||||
data?: UmbWorkspaceData;
|
||||
data?: UmbWorkspaceModalData;
|
||||
|
||||
/**
|
||||
* TODO: Consider if this binding and events integration is the right for communicating back the modal handler. Or if we should go with some Context API. like a Modal Context API.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { UmbPathPattern } from '@umbraco-cms/backoffice/router';
|
||||
import { UMB_SECTION_PATH_PATTERN } from '@umbraco-cms/backoffice/section';
|
||||
|
||||
export const UMB_WORKSPACE_PATH_PATTERN = new UmbPathPattern<
|
||||
{ entityType: string },
|
||||
typeof UMB_SECTION_PATH_PATTERN.ABSOLUTE_PARAMS
|
||||
>('workspace/:entityType', UMB_SECTION_PATH_PATTERN);
|
||||
@@ -3,7 +3,7 @@ import type {
|
||||
UmbDataTypePickerFlowDataTypePickerModalData,
|
||||
UmbDataTypePickerFlowDataTypePickerModalValue,
|
||||
} from './data-type-picker-flow-data-type-picker-modal.token.js';
|
||||
import { css, html, customElement, state, repeat, when } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { css, html, customElement, state, repeat } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbDataTypeItemModel } from '@umbraco-cms/backoffice/data-type';
|
||||
@@ -72,12 +72,8 @@ export class UmbDataTypePickerFlowDataTypePickerModalElement extends UmbModalBas
|
||||
}
|
||||
|
||||
private _renderDataTypes() {
|
||||
const shouldRender = this._dataTypes && this._dataTypes.length > 0;
|
||||
|
||||
return when(
|
||||
shouldRender,
|
||||
() =>
|
||||
html`<ul id="item-grid">
|
||||
return this._dataTypes && this._dataTypes.length > 0
|
||||
? html`<ul id="item-grid">
|
||||
${repeat(
|
||||
this._dataTypes!,
|
||||
(dataType) => dataType.unique,
|
||||
@@ -93,8 +89,8 @@ export class UmbDataTypePickerFlowDataTypePickerModalElement extends UmbModalBas
|
||||
</li>`
|
||||
: '',
|
||||
)}
|
||||
</ul>`,
|
||||
);
|
||||
</ul>`
|
||||
: '';
|
||||
}
|
||||
private _renderCreate() {
|
||||
return html`
|
||||
|
||||
@@ -15,6 +15,10 @@ import type { UmbDataTypeItemModel } from '@umbraco-cms/backoffice/data-type';
|
||||
import type { UmbModalRouteBuilder } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { umbFocus } from '@umbraco-cms/backoffice/lit-element';
|
||||
import {
|
||||
UMB_CONTENT_TYPE_WORKSPACE_CONTEXT,
|
||||
UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT,
|
||||
} from '@umbraco-cms/backoffice/content-type';
|
||||
|
||||
interface GroupedItems<T> {
|
||||
[key: string]: Array<T>;
|
||||
@@ -24,6 +28,8 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement<
|
||||
UmbDataTypePickerFlowModalData,
|
||||
UmbDataTypePickerFlowModalValue
|
||||
> {
|
||||
#initPromise!: Promise<unknown>;
|
||||
|
||||
public set data(value: UmbDataTypePickerFlowModalData) {
|
||||
super.data = value;
|
||||
this._submitLabel = this.data?.submitLabel ?? this._submitLabel;
|
||||
@@ -41,7 +47,7 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement<
|
||||
@state()
|
||||
private _dataTypePickerModalRouteBuilder?: UmbModalRouteBuilder;
|
||||
|
||||
private _createDataTypeModal: UmbModalRouteRegistrationController;
|
||||
private _createDataTypeModal!: UmbModalRouteRegistrationController;
|
||||
|
||||
#collectionRepository;
|
||||
#dataTypes: Array<UmbDataTypeItemModel> = [];
|
||||
@@ -52,6 +58,37 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement<
|
||||
super();
|
||||
|
||||
this.#collectionRepository = new UmbDataTypeCollectionRepository(this);
|
||||
this.#init();
|
||||
}
|
||||
|
||||
private _createDataType(propertyEditorUiAlias: string) {
|
||||
// TODO: Could be nice with a more pretty way to prepend to the URL:
|
||||
// Open create modal:
|
||||
console.log('_createDataType', propertyEditorUiAlias);
|
||||
this._createDataTypeModal.open(
|
||||
{ uiAlias: propertyEditorUiAlias },
|
||||
`create/parent/${UMB_DATA_TYPE_ENTITY_TYPE}/null`,
|
||||
);
|
||||
}
|
||||
|
||||
async #init() {
|
||||
this.#initPromise = Promise.all([
|
||||
this.observe(
|
||||
(await this.#collectionRepository.requestCollection({ skip: 0, take: 100 })).asObservable(),
|
||||
(dataTypes) => {
|
||||
this.#dataTypes = dataTypes;
|
||||
this._performFiltering();
|
||||
},
|
||||
'_repositoryItemsObserver',
|
||||
).asPromise(),
|
||||
this.observe(umbExtensionsRegistry.byType('propertyEditorUi'), (propertyEditorUIs) => {
|
||||
// Only include Property Editor UIs which has Property Editor Schema Alias
|
||||
this.#propertyEditorUIs = propertyEditorUIs.filter(
|
||||
(propertyEditorUi) => !!propertyEditorUi.meta.propertyEditorSchemaAlias,
|
||||
);
|
||||
this._performFiltering();
|
||||
}).asPromise(),
|
||||
]);
|
||||
|
||||
new UmbModalRouteRegistrationController(this, UMB_DATA_TYPE_PICKER_FLOW_DATA_TYPE_PICKER_MODAL)
|
||||
.addAdditionalPath(':uiAlias')
|
||||
@@ -78,43 +115,32 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement<
|
||||
|
||||
this._createDataTypeModal = new UmbModalRouteRegistrationController(this, UMB_DATATYPE_WORKSPACE_MODAL)
|
||||
.addAdditionalPath(':uiAlias')
|
||||
.onSetup((params) => {
|
||||
return { data: { entityType: UMB_DATA_TYPE_ENTITY_TYPE, preset: { editorUiAlias: params.uiAlias } } };
|
||||
.onSetup(async (params) => {
|
||||
const contentContextConsumer = this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, () => {
|
||||
this.removeUmbController(contentContextConsumer);
|
||||
}).passContextAliasMatches();
|
||||
const propContextConsumer = this.consumeContext(UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT, () => {
|
||||
this.removeUmbController(propContextConsumer);
|
||||
}).passContextAliasMatches();
|
||||
const [contentContext, propContext] = await Promise.all([
|
||||
contentContextConsumer.asPromise(),
|
||||
propContextConsumer.asPromise(),
|
||||
this.#initPromise,
|
||||
]);
|
||||
const propertyEditorName = this.#propertyEditorUIs.find((ui) => ui.alias === params.uiAlias)?.name;
|
||||
const dataTypeName = `${contentContext?.getName() ?? ''} - ${propContext.getLabel() ?? ''} - ${propertyEditorName}`;
|
||||
|
||||
return {
|
||||
data: {
|
||||
entityType: UMB_DATA_TYPE_ENTITY_TYPE,
|
||||
preset: { editorUiAlias: params.uiAlias, name: dataTypeName },
|
||||
},
|
||||
};
|
||||
})
|
||||
.onSubmit((value) => {
|
||||
this._select(value?.unique);
|
||||
this._submitModal();
|
||||
});
|
||||
|
||||
this.#init();
|
||||
}
|
||||
|
||||
private _createDataType(propertyEditorUiAlias: string) {
|
||||
// TODO: Could be nice with a more pretty way to prepend to the URL:
|
||||
// Open create modal:
|
||||
this._createDataTypeModal.open(
|
||||
{ uiAlias: propertyEditorUiAlias },
|
||||
`create/parent/${UMB_DATA_TYPE_ENTITY_TYPE}/null`,
|
||||
);
|
||||
}
|
||||
|
||||
async #init() {
|
||||
this.observe(
|
||||
(await this.#collectionRepository.requestCollection({ skip: 0, take: 100 })).asObservable(),
|
||||
(dataTypes) => {
|
||||
this.#dataTypes = dataTypes;
|
||||
this._performFiltering();
|
||||
},
|
||||
'_repositoryItemsObserver',
|
||||
);
|
||||
|
||||
this.observe(umbExtensionsRegistry.byType('propertyEditorUi'), (propertyEditorUIs) => {
|
||||
// Only include Property Editor UIs which has Property Editor Schema Alias
|
||||
this.#propertyEditorUIs = propertyEditorUIs.filter(
|
||||
(propertyEditorUi) => !!propertyEditorUi.meta.propertyEditorSchemaAlias,
|
||||
);
|
||||
this._performFiltering();
|
||||
});
|
||||
}
|
||||
|
||||
private _handleDataTypeClick(dataType: UmbDataTypeItemModel) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { UmbDataTypeDetailModel } from '../types.js';
|
||||
import type { UmbWorkspaceData, UmbWorkspaceValue } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbWorkspaceModalData, UmbWorkspaceModalValue } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export const UMB_DATATYPE_WORKSPACE_MODAL = new UmbModalToken<
|
||||
UmbWorkspaceData<UmbDataTypeDetailModel>,
|
||||
UmbWorkspaceValue
|
||||
UmbWorkspaceModalData<UmbDataTypeDetailModel>,
|
||||
UmbWorkspaceModalValue
|
||||
>('Umb.Modal.Workspace', {
|
||||
modal: {
|
||||
type: 'sidebar',
|
||||
@@ -12,4 +12,4 @@ export const UMB_DATATYPE_WORKSPACE_MODAL = new UmbModalToken<
|
||||
},
|
||||
data: { entityType: 'data-type', preset: {} },
|
||||
// Recast the type, so the entityType data prop is not required:
|
||||
}) as UmbModalToken<Omit<UmbWorkspaceData<UmbDataTypeDetailModel>, 'entityType'>, UmbWorkspaceValue>;
|
||||
}) as UmbModalToken<Omit<UmbWorkspaceModalData<UmbDataTypeDetailModel>, 'entityType'>, UmbWorkspaceModalValue>;
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
import './input-document-type/input-document-type.element.js';
|
||||
|
||||
export * from './input-document-type/input-document-type.element.js';
|
||||
export { UmbDocumentTypePickerContext } from './input-document-type/input-document-type.context.js';
|
||||
export { UmbInputDocumentTypeElement } from './input-document-type/input-document-type.element.js';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { UMB_DOCUMENT_TYPE_PICKER_MODAL } from '../../modals/index.js';
|
||||
import type { UmbDocumentTypeItemModel } from '../../repository/index.js';
|
||||
import type { UmbDocumentTypeTreeItemModel } from '../../tree/types.js';
|
||||
import { UMB_DOCUMENT_TYPE_ITEM_REPOSITORY_ALIAS } from '../../repository/index.js';
|
||||
import { UmbPickerInputContext } from '@umbraco-cms/backoffice/picker-input';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UMB_DOCUMENT_TYPE_PICKER_MODAL } from '@umbraco-cms/backoffice/document-type';
|
||||
|
||||
export class UmbDocumentTypePickerContext extends UmbPickerInputContext<
|
||||
UmbDocumentTypeItemModel,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import type { UmbDocumentTypeItemModel } from '../../repository/index.js';
|
||||
import { UMB_DOCUMENT_TYPE_WORKSPACE_MODAL } from '../../workspace/document-type-workspace.modal-token.js';
|
||||
import type { UmbDocumentTypeTreeItemModel } from '../../tree/types.js';
|
||||
import { UmbDocumentTypePickerContext } from './input-document-type.context.js';
|
||||
import { css, html, customElement, property, state, repeat, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { splitStringToArray } from '@umbraco-cms/backoffice/utils';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbModalRouteRegistrationController, UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbSorterController } from '@umbraco-cms/backoffice/sorter';
|
||||
import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui';
|
||||
|
||||
@@ -107,7 +108,6 @@ export class UmbInputDocumentTypeElement extends UUIFormControlMixin(UmbLitEleme
|
||||
public get value(): string {
|
||||
return this.selection.join(',');
|
||||
}
|
||||
|
||||
@state()
|
||||
private _items?: Array<UmbDocumentTypeItemModel>;
|
||||
|
||||
@@ -119,10 +119,10 @@ export class UmbInputDocumentTypeElement extends UUIFormControlMixin(UmbLitEleme
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL)
|
||||
new UmbModalRouteRegistrationController(this, UMB_DOCUMENT_TYPE_WORKSPACE_MODAL)
|
||||
.addAdditionalPath('document-type')
|
||||
.onSetup(() => {
|
||||
return { data: { entityType: 'document-type', preset: {} } };
|
||||
return {};
|
||||
})
|
||||
.observeRouteBuilder((routeBuilder) => {
|
||||
this._editPath = routeBuilder({});
|
||||
|
||||
@@ -2,7 +2,7 @@ import { UMB_DOCUMENT_TYPE_CREATE_OPTIONS_MODAL } from './modal/index.js';
|
||||
import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action';
|
||||
import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export class UmbCreateDataTypeEntityAction extends UmbEntityActionBase<never> {
|
||||
export class UmbCreateDocumentTypeEntityAction extends UmbEntityActionBase<never> {
|
||||
async execute() {
|
||||
const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);
|
||||
const modalContext = modalManager.open(this, UMB_DOCUMENT_TYPE_CREATE_OPTIONS_MODAL, {
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
UMB_DOCUMENT_TYPE_FOLDER_ENTITY_TYPE,
|
||||
UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE,
|
||||
} from '../../entity.js';
|
||||
import { UmbCreateDataTypeEntityAction } from './create.action.js';
|
||||
import { UmbCreateDocumentTypeEntityAction } from './create.action.js';
|
||||
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
const entityActions: Array<ManifestTypes> = [
|
||||
@@ -13,7 +13,7 @@ const entityActions: Array<ManifestTypes> = [
|
||||
alias: 'Umb.EntityAction.DocumentType.Create',
|
||||
name: 'Create Document Type Entity Action',
|
||||
weight: 1200,
|
||||
api: UmbCreateDataTypeEntityAction,
|
||||
api: UmbCreateDocumentTypeEntityAction,
|
||||
forEntityTypes: [
|
||||
UMB_DOCUMENT_TYPE_ENTITY_TYPE,
|
||||
UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE,
|
||||
|
||||
@@ -1,16 +1,29 @@
|
||||
import { UMB_DOCUMENT_TYPE_FOLDER_REPOSITORY_ALIAS } from '../../../tree/index.js';
|
||||
import type { UmbDocumentTypeCreateOptionsModalData } from './index.js';
|
||||
import {
|
||||
UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PATH_PATTERN,
|
||||
type UmbCreateDocumentTypeWorkspacePresetType,
|
||||
type UmbDocumentTypeEntityTypeUnion,
|
||||
} from '@umbraco-cms/backoffice/document-type';
|
||||
import { html, customElement, map } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbCreateFolderEntityAction } from '@umbraco-cms/backoffice/tree';
|
||||
|
||||
// Include the types from the DocumentTypeWorkspacePresetType + folder.
|
||||
type OptionsPresetType = UmbCreateDocumentTypeWorkspacePresetType | 'folder' | null;
|
||||
|
||||
@customElement('umb-document-type-create-options-modal')
|
||||
export class UmbDataTypeCreateOptionsModalElement extends UmbModalBaseElement<UmbDocumentTypeCreateOptionsModalData> {
|
||||
#createFolderAction?: UmbCreateFolderEntityAction;
|
||||
|
||||
#items: Array<{ preset: string; label: string; description: string; icon: string }> = [
|
||||
#items: Array<{
|
||||
preset: OptionsPresetType;
|
||||
label: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
}> = [
|
||||
{
|
||||
preset: 'null',
|
||||
preset: null,
|
||||
label: this.localize.term('create_documentType'),
|
||||
description: this.localize.term('create_documentTypeDescription'),
|
||||
icon: 'icon-document',
|
||||
@@ -53,12 +66,13 @@ export class UmbDataTypeCreateOptionsModalElement extends UmbModalBaseElement<Um
|
||||
meta: { icon: '', label: '', folderRepositoryAlias: UMB_DOCUMENT_TYPE_FOLDER_REPOSITORY_ALIAS },
|
||||
});
|
||||
}
|
||||
async #onClick(preset: string) {
|
||||
switch (preset) {
|
||||
async #onClick(presetAlias: OptionsPresetType) {
|
||||
switch (presetAlias) {
|
||||
case 'folder': {
|
||||
try {
|
||||
await this.#createFolderAction?.execute();
|
||||
this._submitModal();
|
||||
return;
|
||||
} catch (error) {
|
||||
//console.error(error);
|
||||
}
|
||||
@@ -67,9 +81,14 @@ export class UmbDataTypeCreateOptionsModalElement extends UmbModalBaseElement<Um
|
||||
}
|
||||
|
||||
default: {
|
||||
const entityType = this.data?.parent.entityType;
|
||||
const unique = this.data?.parent.unique || 'null';
|
||||
const href = `section/settings/workspace/document-type/create/${entityType}/${unique}/${preset}`;
|
||||
const parentEntityType = this.data?.parent.entityType as UmbDocumentTypeEntityTypeUnion;
|
||||
if (!parentEntityType) throw new Error('Entity type is required to create a document type');
|
||||
const parentUnique = this.data?.parent.unique ?? null;
|
||||
const href = UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PATH_PATTERN.generateAbsolute({
|
||||
parentEntityType,
|
||||
parentUnique,
|
||||
presetAlias,
|
||||
});
|
||||
window.history.pushState({}, '', href);
|
||||
|
||||
this._submitModal();
|
||||
|
||||
@@ -5,3 +5,8 @@ export const UMB_DOCUMENT_TYPE_FOLDER_ENTITY_TYPE = 'document-type-folder';
|
||||
export type UmbDocumentTypeEntityType = typeof UMB_DOCUMENT_TYPE_ENTITY_TYPE;
|
||||
export type UmbDocumentTypeRootEntityType = typeof UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE;
|
||||
export type UmbDocumentTypeFolderEntityType = typeof UMB_DOCUMENT_TYPE_FOLDER_ENTITY_TYPE;
|
||||
|
||||
export type UmbDocumentTypeEntityTypeUnion =
|
||||
| UmbDocumentTypeEntityType
|
||||
| UmbDocumentTypeRootEntityType
|
||||
| UmbDocumentTypeFolderEntityType;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import './components/index.js';
|
||||
|
||||
export * from './components/index.js';
|
||||
export * from './workspace/index.js';
|
||||
|
||||
export * from './entity.js';
|
||||
export * from './modals/index.js';
|
||||
export * from './paths.js';
|
||||
export * from './repository/index.js';
|
||||
export * from './tree/types.js';
|
||||
export * from './types.js';
|
||||
|
||||
export * from './modals/index.js';
|
||||
export * from './workspace/index.js';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PATH_PATTERN } from '../paths.js';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbDocumentTypeTreeItemModel } from '@umbraco-cms/backoffice/document-type';
|
||||
import {
|
||||
@@ -6,8 +7,15 @@ import {
|
||||
UMB_TREE_PICKER_MODAL_ALIAS,
|
||||
} from '@umbraco-cms/backoffice/tree';
|
||||
|
||||
export type UmbDocumentTypePickerModalData = UmbTreePickerModalData<UmbDocumentTypeTreeItemModel>;
|
||||
export type UmbDocumentTypePickerModalValue = UmbTreePickerModalValue;
|
||||
/*export interface UmbDocumentTypePickerModalData
|
||||
extends UmbTreePickerModalData<UmbDocumentTypeTreeItemModel, typeof umbCreateDocumentTypeWorkspacePathGenerator> {}
|
||||
*/
|
||||
export type UmbDocumentTypePickerModalData = UmbTreePickerModalData<
|
||||
UmbDocumentTypeTreeItemModel,
|
||||
typeof UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PATH_PATTERN.PARAMS
|
||||
>;
|
||||
|
||||
export interface UmbDocumentTypePickerModalValue extends UmbTreePickerModalValue {}
|
||||
|
||||
export const UMB_DOCUMENT_TYPE_PICKER_MODAL = new UmbModalToken<
|
||||
UmbDocumentTypePickerModalData,
|
||||
@@ -19,5 +27,18 @@ export const UMB_DOCUMENT_TYPE_PICKER_MODAL = new UmbModalToken<
|
||||
},
|
||||
data: {
|
||||
treeAlias: 'Umb.Tree.DocumentType',
|
||||
createAction: {
|
||||
label: '#content_createEmpty',
|
||||
modalData: {
|
||||
entityType: 'document-type',
|
||||
preset: {},
|
||||
},
|
||||
extendWithPathPattern: UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PATH_PATTERN,
|
||||
extendWithPathParams: {
|
||||
parentEntityType: 'document-type-root',
|
||||
parentUnique: null,
|
||||
presetAlias: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { UMB_DOCUMENT_TYPE_ENTITY_TYPE, type UmbDocumentTypeEntityTypeUnion } from './entity.js';
|
||||
import { UMB_SETTINGS_SECTION_PATHNAME } from '@umbraco-cms/backoffice/settings';
|
||||
import { UmbPathPattern } from '@umbraco-cms/backoffice/router';
|
||||
import { UMB_WORKSPACE_PATH_PATTERN } from '@umbraco-cms/backoffice/workspace';
|
||||
|
||||
export const UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PRESET_TEMPLATE = 'template';
|
||||
export const UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PRESET_ELEMENT = 'element';
|
||||
|
||||
export type UmbCreateDocumentTypeWorkspacePresetTemplateType =
|
||||
typeof UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PRESET_TEMPLATE;
|
||||
export type UmbCreateDocumentTypeWorkspacePresetElementType = // line break thanks!
|
||||
typeof UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PRESET_ELEMENT;
|
||||
|
||||
export type UmbCreateDocumentTypeWorkspacePresetType =
|
||||
| UmbCreateDocumentTypeWorkspacePresetTemplateType
|
||||
| UmbCreateDocumentTypeWorkspacePresetElementType;
|
||||
|
||||
export const UMB_DOCUMENT_TYPE_WORKSPACE_PATH = UMB_WORKSPACE_PATH_PATTERN.generateAbsolute({
|
||||
sectionName: UMB_SETTINGS_SECTION_PATHNAME,
|
||||
entityType: UMB_DOCUMENT_TYPE_ENTITY_TYPE,
|
||||
});
|
||||
|
||||
export const UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PATH_PATTERN = new UmbPathPattern<{
|
||||
parentEntityType: UmbDocumentTypeEntityTypeUnion;
|
||||
parentUnique?: string | null;
|
||||
presetAlias?: UmbCreateDocumentTypeWorkspacePresetType | null;
|
||||
}>('create/parent/:parentEntityType/:parentUnique/:presetAlias', UMB_DOCUMENT_TYPE_WORKSPACE_PATH);
|
||||
|
||||
export const UMB_EDIT_DOCUMENT_TYPE_WORKSPACE_PATH_PATTERN = new UmbPathPattern<{ unique: string }>('edit/:unique');
|
||||
@@ -1,6 +1,13 @@
|
||||
import { UmbDocumentTypeDetailRepository } from '../repository/detail/document-type-detail.repository.js';
|
||||
import { UMB_DOCUMENT_TYPE_ENTITY_TYPE } from '../entity.js';
|
||||
import type { UmbDocumentTypeDetailModel } from '../types.js';
|
||||
import {
|
||||
UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PATH_PATTERN,
|
||||
UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PRESET_ELEMENT,
|
||||
UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PRESET_TEMPLATE,
|
||||
UMB_EDIT_DOCUMENT_TYPE_WORKSPACE_PATH_PATTERN,
|
||||
type UmbCreateDocumentTypeWorkspacePresetType,
|
||||
} from '../paths.js';
|
||||
import { UmbDocumentTypeWorkspaceEditorElement } from './document-type-workspace-editor.element.js';
|
||||
import { UmbContentTypeStructureManager } from '@umbraco-cms/backoffice/content-type';
|
||||
import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
|
||||
@@ -21,6 +28,7 @@ import type {
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { UmbReferenceByUnique } from '@umbraco-cms/backoffice/models';
|
||||
import type { UmbRoutableWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbPathPatternTypeAsEncodedParamsType } from '@umbraco-cms/backoffice/router';
|
||||
|
||||
type EntityType = UmbDocumentTypeDetailModel;
|
||||
export class UmbDocumentTypeWorkspaceContext
|
||||
@@ -41,6 +49,9 @@ export class UmbDocumentTypeWorkspaceContext
|
||||
//readonly data;
|
||||
readonly unique;
|
||||
readonly name;
|
||||
getName(): string | undefined {
|
||||
return this.structure.getOwnerContentType()?.name;
|
||||
}
|
||||
readonly alias;
|
||||
readonly description;
|
||||
readonly icon;
|
||||
@@ -89,12 +100,18 @@ export class UmbDocumentTypeWorkspaceContext
|
||||
|
||||
this.routes.setRoutes([
|
||||
{
|
||||
path: 'create/:entityType/:parentUnique/:presetAlias',
|
||||
path: UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PATH_PATTERN.toString(),
|
||||
component: UmbDocumentTypeWorkspaceEditorElement,
|
||||
setup: (_component, info) => {
|
||||
const parentEntityType = info.match.params.entityType;
|
||||
const parentUnique = info.match.params.parentUnique === 'null' ? null : info.match.params.parentUnique;
|
||||
const presetAlias = info.match.params.presetAlias === 'null' ? null : info.match.params.presetAlias;
|
||||
const params = info.match.params as unknown as UmbPathPatternTypeAsEncodedParamsType<
|
||||
typeof UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PATH_PATTERN.PARAMS
|
||||
>;
|
||||
const parentEntityType = params.parentEntityType;
|
||||
const parentUnique = params.parentUnique === 'null' ? null : params.parentUnique;
|
||||
const presetAlias = params.presetAlias === 'null' ? null : params.presetAlias ?? null;
|
||||
if (parentUnique === undefined) {
|
||||
throw new Error('ParentUnique url parameter is required to create a document type');
|
||||
}
|
||||
this.create({ entityType: parentEntityType, unique: parentUnique }, presetAlias);
|
||||
|
||||
new UmbWorkspaceIsNewRedirectController(
|
||||
@@ -105,12 +122,12 @@ export class UmbDocumentTypeWorkspaceContext
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'edit/:id',
|
||||
path: UMB_EDIT_DOCUMENT_TYPE_WORKSPACE_PATH_PATTERN.toString(),
|
||||
component: UmbDocumentTypeWorkspaceEditorElement,
|
||||
setup: (_component, info) => {
|
||||
this.removeUmbControllerByAlias('isNewRedirectController');
|
||||
const id = info.match.params.id;
|
||||
this.load(id);
|
||||
const unique = info.match.params.unique;
|
||||
this.load(unique);
|
||||
},
|
||||
},
|
||||
]);
|
||||
@@ -198,12 +215,12 @@ export class UmbDocumentTypeWorkspaceContext
|
||||
if (!data) return undefined;
|
||||
|
||||
switch (presetAlias) {
|
||||
case 'template': {
|
||||
case UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PRESET_TEMPLATE satisfies UmbCreateDocumentTypeWorkspacePresetType: {
|
||||
this.setIcon('icon-notepad');
|
||||
this.createTemplateMode = true;
|
||||
break;
|
||||
}
|
||||
case 'element': {
|
||||
case UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PRESET_ELEMENT satisfies UmbCreateDocumentTypeWorkspacePresetType: {
|
||||
this.setIcon('icon-plugin');
|
||||
this.setIsElement(true);
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { UMB_DOCUMENT_TYPE_ENTITY_TYPE } from '../entity.js';
|
||||
import { UMB_WORKSPACE_MODAL, UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbDeepPartialObject } from '@umbraco-cms/backoffice/utils';
|
||||
import type { UmbWorkspaceModalData, UmbWorkspaceModalValue } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbDocumentTypeWorkspaceData extends UmbWorkspaceModalData {}
|
||||
|
||||
export const UMB_DOCUMENT_TYPE_WORKSPACE_MODAL = new UmbModalToken<
|
||||
UmbDocumentTypeWorkspaceData,
|
||||
UmbWorkspaceModalValue
|
||||
>('Umb.Modal.Workspace', {
|
||||
modal: UMB_WORKSPACE_MODAL.getDefaultModal(),
|
||||
data: { entityType: UMB_DOCUMENT_TYPE_ENTITY_TYPE, preset: {} },
|
||||
// Recast the type, so the entityType data prop is not required:
|
||||
}) as UmbModalToken<UmbDeepPartialObject<UmbDocumentTypeWorkspaceData>, UmbWorkspaceModalValue>;
|
||||
@@ -1 +1,2 @@
|
||||
export * from './document-type-workspace.context-token.js';
|
||||
export * from './document-type-workspace.modal-token.js';
|
||||
|
||||
@@ -2,7 +2,12 @@ import { html, customElement, property, state, map } from '@umbraco-cms/backoffi
|
||||
import { UmbDocumentTypeStructureRepository } from '@umbraco-cms/backoffice/document-type';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UMB_DEFAULT_COLLECTION_CONTEXT } from '@umbraco-cms/backoffice/collection';
|
||||
import { UMB_DOCUMENT_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/document';
|
||||
import {
|
||||
UMB_CREATE_DOCUMENT_WORKSPACE_PATH_PATTERN,
|
||||
UMB_DOCUMENT_ENTITY_TYPE,
|
||||
UMB_DOCUMENT_ROOT_ENTITY_TYPE,
|
||||
UMB_DOCUMENT_WORKSPACE_CONTEXT,
|
||||
} from '@umbraco-cms/backoffice/document';
|
||||
import type { ManifestCollectionAction } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type { UmbAllowedDocumentTypeModel } from '@umbraco-cms/backoffice/document-type';
|
||||
import { UMB_WORKSPACE_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal';
|
||||
@@ -83,9 +88,19 @@ export class UmbCreateDocumentCollectionActionElement extends UmbLitElement {
|
||||
|
||||
#getCreateUrl(item: UmbAllowedDocumentTypeModel) {
|
||||
// TODO: [LK] I need help with this. I don't know what the infinity editor URL should be.
|
||||
// TODO: Yes, revisit the path extension of the routable modal, cause this is not pretty...? [NL]
|
||||
return this._useInfiniteEditor
|
||||
? `${this._createDocumentPath}create/${this._documentUnique ?? 'null'}/${item.unique}`
|
||||
: `section/content/workspace/document/create/${this._documentUnique ?? 'null'}/${item.unique}`;
|
||||
? this._createDocumentPath +
|
||||
UMB_CREATE_DOCUMENT_WORKSPACE_PATH_PATTERN.generateLocal({
|
||||
parentEntityType: this._documentUnique ? UMB_DOCUMENT_ENTITY_TYPE : UMB_DOCUMENT_ROOT_ENTITY_TYPE,
|
||||
parentUnique: this._documentUnique ?? 'null',
|
||||
documentTypeUnique: item.unique,
|
||||
})
|
||||
: UMB_CREATE_DOCUMENT_WORKSPACE_PATH_PATTERN.generateAbsolute({
|
||||
parentEntityType: this._documentUnique ? UMB_DOCUMENT_ENTITY_TYPE : UMB_DOCUMENT_ROOT_ENTITY_TYPE,
|
||||
parentUnique: this._documentUnique ?? 'null',
|
||||
documentTypeUnique: item.unique,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './input-document/input-document.element.js';
|
||||
export * from './input-document-root-picker/input-document-root-picker.element.js';
|
||||
export { UmbDocumentPickerContext } from './input-document/input-document.context.js';
|
||||
export { UmbInputDocumentElement } from './input-document/input-document.element.js';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { UMB_DOCUMENT_PICKER_MODAL } from '../../modals/index.js';
|
||||
import { UMB_DOCUMENT_ITEM_REPOSITORY_ALIAS } from '../../repository/index.js';
|
||||
import type { UmbDocumentItemModel } from '../../repository/index.js';
|
||||
import { UMB_DOCUMENT_PICKER_MODAL } from '../../modals/index.js';
|
||||
import { UmbPickerInputContext } from '@umbraco-cms/backoffice/picker-input';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
|
||||
@@ -238,8 +238,6 @@ export class UmbInputDocumentElement extends UUIFormControlMixin(UmbLitElement,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbInputDocumentElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-input-document': UmbInputDocumentElement;
|
||||
|
||||
@@ -14,6 +14,11 @@ import {
|
||||
UmbDocumentBlueprintItemRepository,
|
||||
type UmbDocumentBlueprintItemBaseModel,
|
||||
} from '@umbraco-cms/backoffice/document-blueprint';
|
||||
import {
|
||||
UMB_CREATE_DOCUMENT_WORKSPACE_PATH_PATTERN,
|
||||
UMB_CREATE_FROM_BLUEPRINT_DOCUMENT_WORKSPACE_PATH_PATTERN,
|
||||
type UmbDocumentEntityTypeUnion,
|
||||
} from '@umbraco-cms/backoffice/document';
|
||||
|
||||
@customElement('umb-document-create-options-modal')
|
||||
export class UmbDocumentCreateOptionsModalElement extends UmbModalBaseElement<
|
||||
@@ -68,17 +73,29 @@ export class UmbDocumentCreateOptionsModalElement extends UmbModalBaseElement<
|
||||
|
||||
// close the modal when navigating to data type
|
||||
#onNavigate(documentTypeUnique: string, blueprintUnique?: string) {
|
||||
if (!this.data) {
|
||||
throw new Error('Data is not defined');
|
||||
}
|
||||
if (!blueprintUnique) {
|
||||
history.pushState(
|
||||
null,
|
||||
'',
|
||||
`section/content/workspace/document/create/parent/${this.data?.parent.entityType}/${this.data?.parent.unique ?? 'null'}/${documentTypeUnique}`,
|
||||
UMB_CREATE_DOCUMENT_WORKSPACE_PATH_PATTERN.generateAbsolute({
|
||||
parentEntityType: this.data.parent.entityType as UmbDocumentEntityTypeUnion,
|
||||
parentUnique: this.data.parent.unique,
|
||||
documentTypeUnique,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
history.pushState(
|
||||
null,
|
||||
'',
|
||||
`section/content/workspace/document/create/parent/${this.data?.parent.entityType}/${this.data?.parent.unique ?? 'null'}/${documentTypeUnique}/blueprint/${blueprintUnique}`,
|
||||
UMB_CREATE_FROM_BLUEPRINT_DOCUMENT_WORKSPACE_PATH_PATTERN.generateAbsolute({
|
||||
parentEntityType: this.data.parent.entityType as UmbDocumentEntityTypeUnion,
|
||||
parentUnique: this.data.parent.unique,
|
||||
documentTypeUnique,
|
||||
blueprintUnique,
|
||||
}),
|
||||
);
|
||||
}
|
||||
this._submitModal();
|
||||
|
||||
@@ -3,3 +3,5 @@ export const UMB_DOCUMENT_ROOT_ENTITY_TYPE = 'document-root';
|
||||
|
||||
export type UmbDocumentEntityType = typeof UMB_DOCUMENT_ENTITY_TYPE;
|
||||
export type UmbDocumentRootEntityType = typeof UMB_DOCUMENT_ROOT_ENTITY_TYPE;
|
||||
|
||||
export type UmbDocumentEntityTypeUnion = UmbDocumentEntityType | UmbDocumentRootEntityType;
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import './components/index.js';
|
||||
|
||||
export * from './repository/index.js';
|
||||
export * from './reference/index.js';
|
||||
export * from './workspace/index.js';
|
||||
export * from './recycle-bin/index.js';
|
||||
export * from './user-permissions/index.js';
|
||||
export * from './components/index.js';
|
||||
export * from './entity.js';
|
||||
export * from './entity-actions/index.js';
|
||||
export * from './modals/index.js';
|
||||
export * from './entity.js';
|
||||
export * from './global-contexts/index.js';
|
||||
export * from './modals/index.js';
|
||||
export * from './paths.js';
|
||||
export * from './recycle-bin/index.js';
|
||||
export * from './reference/index.js';
|
||||
export * from './repository/index.js';
|
||||
export * from './user-permissions/index.js';
|
||||
export * from './workspace/index.js';
|
||||
|
||||
export * from './tree/index.js';
|
||||
export { UMB_CONTENT_MENU_ALIAS } from './menu/manifests.js';
|
||||
|
||||
@@ -10,6 +10,7 @@ import '../shared/document-variant-language-picker.element.js';
|
||||
import { UmbUserItemRepository } from '@umbraco-cms/backoffice/user';
|
||||
import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property';
|
||||
import type { UUISelectEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UMB_EDIT_DOCUMENT_WORKSPACE_PATH_PATTERN } from '../../paths.js';
|
||||
|
||||
type DocumentVersion = {
|
||||
id: string;
|
||||
@@ -147,7 +148,7 @@ export class UmbRollbackModalElement extends UmbModalBaseElement<UmbRollbackModa
|
||||
const docUnique = this.#workspaceContext?.getUnique() ?? '';
|
||||
// TODO Use the load method on the context instead of location.href, when it works.
|
||||
// this.#workspaceContext?.load(docUnique);
|
||||
location.href = 'section/content/workspace/document/edit/' + docUnique;
|
||||
location.href = UMB_EDIT_DOCUMENT_WORKSPACE_PATH_PATTERN.generateAbsolute({ unique: docUnique });
|
||||
this.modalContext?.reject();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { UMB_DOCUMENTS_SECTION_PATHNAME } from '../paths.js';
|
||||
import { UMB_DOCUMENT_ENTITY_TYPE, type UmbDocumentEntityTypeUnion } from './entity.js';
|
||||
import { UmbPathPattern } from '@umbraco-cms/backoffice/router';
|
||||
import { UMB_WORKSPACE_PATH_PATTERN } from '@umbraco-cms/backoffice/workspace';
|
||||
|
||||
export const UMB_DOCUMENT_WORKSPACE_PATH = UMB_WORKSPACE_PATH_PATTERN.generateAbsolute({
|
||||
sectionName: UMB_DOCUMENTS_SECTION_PATHNAME,
|
||||
entityType: UMB_DOCUMENT_ENTITY_TYPE,
|
||||
});
|
||||
|
||||
export const UMB_CREATE_FROM_BLUEPRINT_DOCUMENT_WORKSPACE_PATH_PATTERN = new UmbPathPattern<{
|
||||
parentEntityType: UmbDocumentEntityTypeUnion;
|
||||
parentUnique?: string | null;
|
||||
documentTypeUnique: string;
|
||||
blueprintUnique: string;
|
||||
}>(
|
||||
'create/parent/:parentEntityType/:parentUnique/:documentTypeUnique/blueprint/:blueprintUnique',
|
||||
UMB_DOCUMENT_WORKSPACE_PATH,
|
||||
);
|
||||
|
||||
export const UMB_CREATE_DOCUMENT_WORKSPACE_PATH_PATTERN = new UmbPathPattern<{
|
||||
parentEntityType: UmbDocumentEntityTypeUnion;
|
||||
parentUnique?: string | null;
|
||||
documentTypeUnique: string;
|
||||
}>('create/parent/:parentEntityType/:parentUnique/:documentTypeUnique', UMB_DOCUMENT_WORKSPACE_PATH);
|
||||
|
||||
export const UMB_EDIT_DOCUMENT_WORKSPACE_PATH_PATTERN = new UmbPathPattern<{ unique: string }>('edit/:unique');
|
||||
@@ -18,6 +18,12 @@ import {
|
||||
import { UmbDocumentPublishingRepository } from '../repository/publishing/index.js';
|
||||
import { UmbUnpublishDocumentEntityAction } from '../entity-actions/unpublish.action.js';
|
||||
import { UmbDocumentValidationRepository } from '../repository/validation/document-validation.repository.js';
|
||||
import {
|
||||
UMB_CREATE_DOCUMENT_WORKSPACE_PATH_PATTERN,
|
||||
UMB_CREATE_FROM_BLUEPRINT_DOCUMENT_WORKSPACE_PATH_PATTERN,
|
||||
UMB_EDIT_DOCUMENT_WORKSPACE_PATH_PATTERN,
|
||||
} from '../paths.js';
|
||||
import { UMB_DOCUMENTS_SECTION_PATH } from '../../paths.js';
|
||||
import { UMB_DOCUMENT_WORKSPACE_ALIAS } from './manifests.js';
|
||||
import { UmbEntityContext } from '@umbraco-cms/backoffice/entity';
|
||||
import { UMB_INVARIANT_CULTURE, UmbVariantId } from '@umbraco-cms/backoffice/variant';
|
||||
@@ -159,10 +165,10 @@ export class UmbDocumentWorkspaceContext
|
||||
|
||||
this.routes.setRoutes([
|
||||
{
|
||||
path: 'create/parent/:entityType/:parentUnique/:documentTypeUnique/blueprint/:blueprintUnique',
|
||||
path: UMB_CREATE_FROM_BLUEPRINT_DOCUMENT_WORKSPACE_PATH_PATTERN.toString(),
|
||||
component: () => import('./document-workspace-editor.element.js'),
|
||||
setup: async (_component, info) => {
|
||||
const parentEntityType = info.match.params.entityType;
|
||||
const parentEntityType = info.match.params.parentEntityType;
|
||||
const parentUnique: string | null =
|
||||
info.match.params.parentUnique === 'null' ? null : info.match.params.parentUnique;
|
||||
const documentTypeUnique = info.match.params.documentTypeUnique;
|
||||
@@ -177,10 +183,10 @@ export class UmbDocumentWorkspaceContext
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'create/parent/:entityType/:parentUnique/:documentTypeUnique',
|
||||
path: UMB_CREATE_DOCUMENT_WORKSPACE_PATH_PATTERN.toString(),
|
||||
component: () => import('./document-workspace-editor.element.js'),
|
||||
setup: async (_component, info) => {
|
||||
const parentEntityType = info.match.params.entityType;
|
||||
const parentEntityType = info.match.params.parentEntityType;
|
||||
const parentUnique = info.match.params.parentUnique === 'null' ? null : info.match.params.parentUnique;
|
||||
const documentTypeUnique = info.match.params.documentTypeUnique;
|
||||
this.create({ entityType: parentEntityType, unique: parentUnique }, documentTypeUnique);
|
||||
@@ -193,7 +199,7 @@ export class UmbDocumentWorkspaceContext
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'edit/:unique',
|
||||
path: UMB_EDIT_DOCUMENT_WORKSPACE_PATH_PATTERN.toString(),
|
||||
component: () => import('./document-workspace-editor.element.js'),
|
||||
setup: (_component, info) => {
|
||||
const unique = info.match.params.unique;
|
||||
@@ -235,7 +241,7 @@ export class UmbDocumentWorkspaceContext
|
||||
#onStoreChange(entity: EntityType | undefined) {
|
||||
if (!entity) {
|
||||
//TODO: This solution is alright for now. But reconsider when we introduce signal-r
|
||||
history.pushState(null, '', 'section/content');
|
||||
history.pushState(null, '', UMB_DOCUMENTS_SECTION_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import './document-blueprints/index.js';
|
||||
import './document-types/index.js';
|
||||
import './documents/index.js';
|
||||
import './document-blueprints/index.js';
|
||||
import './paths.js';
|
||||
|
||||
export * from './manifests.js';
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { UMB_SECTION_PATH_PATTERN } from '@umbraco-cms/backoffice/section';
|
||||
|
||||
export const UMB_DOCUMENTS_SECTION_PATHNAME = 'content';
|
||||
export const UMB_DOCUMENTS_SECTION_PATH = UMB_SECTION_PATH_PATTERN.generateAbsolute({
|
||||
sectionName: UMB_DOCUMENTS_SECTION_PATHNAME,
|
||||
});
|
||||
@@ -1,37 +0,0 @@
|
||||
import {
|
||||
UMB_DYNAMIC_ROOT_ORIGIN_PICKER_MODAL_ALIAS,
|
||||
UMB_DYNAMIC_ROOT_QUERY_STEP_PICKER_MODAL_ALIAS,
|
||||
} from './manifests.js';
|
||||
import type {
|
||||
ManifestDynamicRootOrigin,
|
||||
ManifestDynamicRootQueryStep,
|
||||
} from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbDynamicRootOriginModalData {
|
||||
items: Array<ManifestDynamicRootOrigin>;
|
||||
}
|
||||
|
||||
export interface UmbDynamicRootQueryStepModalData {
|
||||
items: Array<ManifestDynamicRootQueryStep>;
|
||||
}
|
||||
|
||||
export const UMB_DYNAMIC_ROOT_ORIGIN_PICKER_MODAL = new UmbModalToken<UmbDynamicRootOriginModalData>(
|
||||
UMB_DYNAMIC_ROOT_ORIGIN_PICKER_MODAL_ALIAS,
|
||||
{
|
||||
modal: {
|
||||
type: 'sidebar',
|
||||
size: 'small',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export const UMB_DYNAMIC_ROOT_QUERY_STEP_PICKER_MODAL = new UmbModalToken<UmbDynamicRootQueryStepModalData>(
|
||||
UMB_DYNAMIC_ROOT_QUERY_STEP_PICKER_MODAL_ALIAS,
|
||||
{
|
||||
modal: {
|
||||
type: 'sidebar',
|
||||
size: 'small',
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -1,42 +0,0 @@
|
||||
import { UmbDynamicRootServerDataSource } from './dynamic-root.server.data.js';
|
||||
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { DynamicRootRequestModel } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
import type { UmbTreePickerDynamicRoot } from '@umbraco-cms/backoffice/components';
|
||||
|
||||
const GUID_EMPTY: string = '00000000-0000-0000-0000-000000000000';
|
||||
|
||||
export class UmbDynamicRootRepository extends UmbControllerBase {
|
||||
#dataSource: UmbDynamicRootServerDataSource;
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host);
|
||||
|
||||
this.#dataSource = new UmbDynamicRootServerDataSource(host);
|
||||
}
|
||||
|
||||
async postDynamicRootQuery(query: UmbTreePickerDynamicRoot, entityId: string, parentId?: string) {
|
||||
const model: DynamicRootRequestModel = {
|
||||
context: {
|
||||
id: entityId,
|
||||
parent: { id: parentId ?? GUID_EMPTY },
|
||||
},
|
||||
query: {
|
||||
origin: {
|
||||
alias: query.originAlias,
|
||||
id: query.originKey,
|
||||
},
|
||||
steps: query.querySteps!.map((step) => {
|
||||
return {
|
||||
alias: step.alias!,
|
||||
documentTypeIds: step.anyOfDocTypeKeys!,
|
||||
};
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
const result = await this.#dataSource.postDynamicRootQuery(model);
|
||||
|
||||
return result?.roots;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export { UMB_DYNAMIC_ROOT_REPOSITORY_ALIAS } from './manifests.js';
|
||||
export { UmbDynamicRootRepository } from './dynamic-root.repository.js';
|
||||
@@ -1,5 +0,0 @@
|
||||
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const UMB_DYNAMIC_ROOT_REPOSITORY_ALIAS = 'Umb.Repository.DynamicRoot';
|
||||
|
||||
export const manifests: Array<ManifestTypes> = [];
|
||||
@@ -1,9 +0,0 @@
|
||||
export const name = 'Umbraco.Core.DynamicRoot';
|
||||
export const extensions = [
|
||||
{
|
||||
name: 'Dynamic Root Bundle',
|
||||
alias: 'Umb.Bundle.DynamicRoot',
|
||||
type: 'bundle',
|
||||
js: () => import('./manifests.js'),
|
||||
},
|
||||
];
|
||||
@@ -40,6 +40,9 @@ export class UmbMediaTypeWorkspaceContext
|
||||
readonly data;
|
||||
readonly unique;
|
||||
readonly name;
|
||||
getName(): string | undefined {
|
||||
return this.structure.getOwnerContentType()?.name;
|
||||
}
|
||||
readonly alias;
|
||||
readonly description;
|
||||
readonly icon;
|
||||
|
||||
@@ -37,6 +37,9 @@ export class UmbMemberTypeWorkspaceContext
|
||||
readonly data;
|
||||
readonly unique;
|
||||
readonly name;
|
||||
getName(): string | undefined {
|
||||
return this.structure.getOwnerContentType()?.name;
|
||||
}
|
||||
readonly alias;
|
||||
readonly description;
|
||||
readonly icon;
|
||||
|
||||
@@ -5,7 +5,7 @@ export const manifest: ManifestPropertyEditorSchema = {
|
||||
name: 'Multi Node Tree Picker',
|
||||
alias: 'Umbraco.MultiNodeTreePicker',
|
||||
meta: {
|
||||
defaultPropertyEditorUiAlias: 'Umb.PropertyEditorUi.TreePicker',
|
||||
defaultPropertyEditorUiAlias: 'Umb.PropertyEditorUi.ContentPicker',
|
||||
settings: {
|
||||
properties: [
|
||||
{
|
||||
@@ -32,7 +32,7 @@ export const manifest: ManifestPropertyEditorSchema = {
|
||||
alias: 'startNode',
|
||||
label: 'Node type',
|
||||
description: '',
|
||||
propertyEditorUiAlias: 'Umb.PropertyEditorUi.TreePicker.SourcePicker',
|
||||
propertyEditorUiAlias: 'Umb.PropertyEditorUi.ContentPicker.Source',
|
||||
},
|
||||
],
|
||||
defaultData: [
|
||||
@@ -0,0 +1 @@
|
||||
export * from './input-content/index.js';
|
||||
@@ -0,0 +1 @@
|
||||
export * from './input-content.element.js';
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { UmbContentPickerSource } from '../../types.js';
|
||||
import { css, html, customElement, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
@@ -6,24 +7,24 @@ import type { UmbInputDocumentElement } from '@umbraco-cms/backoffice/document';
|
||||
import type { UmbInputMediaElement } from '@umbraco-cms/backoffice/media';
|
||||
import type { UmbInputMemberElement } from '@umbraco-cms/backoffice/member';
|
||||
import type { UmbReferenceByUniqueAndType } from '@umbraco-cms/backoffice/models';
|
||||
import type { UmbTreePickerSource } from '@umbraco-cms/backoffice/components';
|
||||
|
||||
@customElement('umb-input-tree')
|
||||
export class UmbInputTreeElement extends UUIFormControlMixin(UmbLitElement, '') {
|
||||
const elementName = 'umb-input-content';
|
||||
@customElement(elementName)
|
||||
export class UmbInputContentElement extends UUIFormControlMixin(UmbLitElement, '') {
|
||||
protected getFormElement() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private _type: UmbTreePickerSource['type'] = 'content';
|
||||
private _type: UmbContentPickerSource['type'] = 'content';
|
||||
@property()
|
||||
public set type(newType: UmbTreePickerSource['type']) {
|
||||
public set type(newType: UmbContentPickerSource['type']) {
|
||||
const oldType = this._type;
|
||||
if (newType?.toLowerCase() !== this._type) {
|
||||
this._type = newType?.toLowerCase() as UmbTreePickerSource['type'];
|
||||
this._type = newType?.toLowerCase() as UmbContentPickerSource['type'];
|
||||
this.requestUpdate('type', oldType);
|
||||
}
|
||||
}
|
||||
public get type(): UmbTreePickerSource['type'] {
|
||||
public get type(): UmbContentPickerSource['type'] {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
@@ -152,10 +153,10 @@ export class UmbInputTreeElement extends UUIFormControlMixin(UmbLitElement, '')
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbInputTreeElement;
|
||||
export default UmbInputContentElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-input-tree': UmbInputTreeElement;
|
||||
[elementName]: UmbInputContentElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { Meta, StoryObj } from '@storybook/web-components';
|
||||
import './input-content.element.js';
|
||||
import type { UmbInputContentElement } from './input-content.element.js';
|
||||
|
||||
const meta: Meta<UmbInputContentElement> = {
|
||||
title: 'Components/Inputs/Content',
|
||||
component: 'umb-input-content',
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<UmbInputContentElement>;
|
||||
|
||||
export const Overview: Story = {
|
||||
args: {},
|
||||
};
|
||||
@@ -1,15 +1,15 @@
|
||||
import { expect, fixture, html } from '@open-wc/testing';
|
||||
import { UmbInputTreeElement } from './input-tree.element.js';
|
||||
import { UmbInputContentElement } from './input-content.element.js';
|
||||
import { type UmbTestRunnerWindow, defaultA11yConfig } from '@umbraco-cms/internal/test-utils';
|
||||
describe('UmbInputTreeElement', () => {
|
||||
let element: UmbInputTreeElement;
|
||||
describe('UmbInputContentElement', () => {
|
||||
let element: UmbInputContentElement;
|
||||
|
||||
beforeEach(async () => {
|
||||
element = await fixture(html` <umb-input-tree></umb-input-tree> `);
|
||||
element = await fixture(html` <umb-input-content></umb-input-content> `);
|
||||
});
|
||||
|
||||
it('is defined with its own instance', () => {
|
||||
expect(element).to.be.instanceOf(UmbInputTreeElement);
|
||||
expect(element).to.be.instanceOf(UmbInputContentElement);
|
||||
});
|
||||
|
||||
if ((window as UmbTestRunnerWindow).__UMBRACO_TEST_RUN_A11Y_TEST) {
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const manifest: ManifestPropertyEditorUi = {
|
||||
type: 'propertyEditorUi',
|
||||
alias: 'Umb.PropertyEditorUi.ContentPicker.SourceType',
|
||||
name: 'Content Picker Source Type Property Editor UI',
|
||||
element: () => import('./property-editor-ui-content-picker-source-type.element.js'),
|
||||
meta: {
|
||||
label: 'Content Picker Source Type Picker',
|
||||
icon: 'icon-page-add',
|
||||
group: 'pickers',
|
||||
},
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { UmbContentPickerSource } from '../../types.js';
|
||||
import type { UmbInputMemberTypeElement } from '@umbraco-cms/backoffice/member-type';
|
||||
import type { UmbInputDocumentTypeElement } from '@umbraco-cms/backoffice/document-type';
|
||||
import type { UmbInputMediaTypeElement } from '@umbraco-cms/backoffice/media-type';
|
||||
import type { UmbTreePickerSource } from '@umbraco-cms/backoffice/components';
|
||||
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
@@ -9,10 +9,10 @@ import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property';
|
||||
import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor';
|
||||
|
||||
/**
|
||||
* @element umb-property-editor-ui-tree-picker-source-type-picker
|
||||
* @element umb-property-editor-ui-content-picker-source-type
|
||||
*/
|
||||
@customElement('umb-property-editor-ui-tree-picker-source-type-picker')
|
||||
export class UmbPropertyEditorUITreePickerSourceTypePickerElement
|
||||
@customElement('umb-property-editor-ui-content-picker-source-type')
|
||||
export class UmbPropertyEditorUIContentPickerSourceTypeElement
|
||||
extends UmbLitElement
|
||||
implements UmbPropertyEditorUiElement
|
||||
{
|
||||
@@ -51,7 +51,7 @@ export class UmbPropertyEditorUITreePickerSourceTypePickerElement
|
||||
(value) => {
|
||||
if (!value) return;
|
||||
|
||||
const startNode = value as UmbTreePickerSource;
|
||||
const startNode = value as UmbContentPickerSource;
|
||||
if (startNode?.type) {
|
||||
// If we had a sourceType before, we can see this as a change and not the initial value,
|
||||
// so let's reset the value, so we don't carry over content-types to the new source type.
|
||||
@@ -127,10 +127,10 @@ export class UmbPropertyEditorUITreePickerSourceTypePickerElement
|
||||
}
|
||||
}
|
||||
|
||||
export default UmbPropertyEditorUITreePickerSourceTypePickerElement;
|
||||
export default UmbPropertyEditorUIContentPickerSourceTypeElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-property-editor-ui-tree-picker-source-type-picker': UmbPropertyEditorUITreePickerSourceTypePickerElement;
|
||||
'umb-property-editor-ui-content-picker-source-type': UmbPropertyEditorUIContentPickerSourceTypeElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './input-content-picker-source.element.js';
|
||||
@@ -1,40 +1,23 @@
|
||||
import type { UmbInputDocumentRootPickerElement } from '@umbraco-cms/backoffice/document';
|
||||
import type { UmbContentPickerDynamicRoot, UmbContentPickerSourceType } from '../../../types.js';
|
||||
import type { UmbInputContentPickerDocumentRootElement } from '../../../dynamic-root/input-content-picker-document-root/input-content-picker-document-root.element.js';
|
||||
import { html, customElement, property, css, state, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import type { UUISelectEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
|
||||
export type UmbTreePickerSource = {
|
||||
type: UmbTreePickerSourceType;
|
||||
id?: string;
|
||||
dynamicRoot?: UmbTreePickerDynamicRoot;
|
||||
};
|
||||
import '../../../dynamic-root/input-content-picker-document-root/input-content-picker-document-root.element.js';
|
||||
|
||||
export type UmbTreePickerSourceType = 'content' | 'member' | 'media';
|
||||
|
||||
export type UmbTreePickerDynamicRoot = {
|
||||
originAlias: string;
|
||||
originKey?: string;
|
||||
querySteps?: Array<UmbTreePickerDynamicRootQueryStep>;
|
||||
};
|
||||
|
||||
export type UmbTreePickerDynamicRootQueryStep = {
|
||||
unique: string;
|
||||
alias: string;
|
||||
anyOfDocTypeKeys?: Array<string>;
|
||||
};
|
||||
|
||||
@customElement('umb-input-tree-picker-source')
|
||||
export class UmbInputTreePickerSourceElement extends UUIFormControlMixin(UmbLitElement, '') {
|
||||
@customElement('umb-input-content-picker-source')
|
||||
export class UmbInputContentPickerSourceElement extends UUIFormControlMixin(UmbLitElement, '') {
|
||||
protected getFormElement() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
#type: UmbTreePickerSourceType = 'content';
|
||||
#type: UmbContentPickerSourceType = 'content';
|
||||
|
||||
@property()
|
||||
public set type(value: UmbTreePickerSourceType) {
|
||||
public set type(value: UmbContentPickerSourceType) {
|
||||
if (value === undefined) {
|
||||
value = this.#type;
|
||||
}
|
||||
@@ -49,7 +32,7 @@ export class UmbInputTreePickerSourceElement extends UUIFormControlMixin(UmbLitE
|
||||
|
||||
this.requestUpdate('type', oldValue);
|
||||
}
|
||||
public get type(): UmbTreePickerSourceType {
|
||||
public get type(): UmbContentPickerSourceType {
|
||||
return this.#type;
|
||||
}
|
||||
|
||||
@@ -57,7 +40,7 @@ export class UmbInputTreePickerSourceElement extends UUIFormControlMixin(UmbLitE
|
||||
nodeId?: string;
|
||||
|
||||
@property({ attribute: false })
|
||||
dynamicRoot?: UmbTreePickerDynamicRoot | undefined;
|
||||
dynamicRoot?: UmbContentPickerDynamicRoot | undefined;
|
||||
|
||||
@state()
|
||||
_options: Array<Option> = [
|
||||
@@ -78,7 +61,7 @@ export class UmbInputTreePickerSourceElement extends UUIFormControlMixin(UmbLitE
|
||||
#onContentTypeChange(event: UUISelectEvent) {
|
||||
event.stopPropagation();
|
||||
|
||||
this.type = event.target.value as UmbTreePickerSourceType;
|
||||
this.type = event.target.value as UmbContentPickerSourceType;
|
||||
|
||||
this.nodeId = undefined;
|
||||
this.dynamicRoot = undefined;
|
||||
@@ -86,10 +69,10 @@ export class UmbInputTreePickerSourceElement extends UUIFormControlMixin(UmbLitE
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
|
||||
#onDocumentRootChange(event: CustomEvent) {
|
||||
#onDocumentRootChange(event: CustomEvent & { target: UmbInputContentPickerDocumentRootElement }) {
|
||||
switch (this.type) {
|
||||
case 'content':
|
||||
this.dynamicRoot = (event?.target as unknown as UmbInputDocumentRootPickerElement).data;
|
||||
this.dynamicRoot = event.target.data;
|
||||
|
||||
// HACK: Workaround consolidating the old content-picker and dynamic-root. [LK:2024-01-24]
|
||||
if (this.dynamicRoot?.originAlias === 'ByKey') {
|
||||
@@ -131,9 +114,10 @@ export class UmbInputTreePickerSourceElement extends UUIFormControlMixin(UmbLitE
|
||||
}
|
||||
|
||||
#renderDocumentSourcePicker() {
|
||||
return html`<umb-input-document-root-picker
|
||||
@change=${this.#onDocumentRootChange}
|
||||
.data=${this.dynamicRoot}></umb-input-document-root-picker>`;
|
||||
return html`
|
||||
<umb-input-content-picker-document-root .data=${this.dynamicRoot} @change=${this.#onDocumentRootChange}>
|
||||
</umb-input-content-picker-document-root>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
@@ -147,10 +131,10 @@ export class UmbInputTreePickerSourceElement extends UUIFormControlMixin(UmbLitE
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbInputTreePickerSourceElement;
|
||||
export default UmbInputContentPickerSourceElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-input-tree-picker-source': UmbInputTreePickerSourceElement;
|
||||
'umb-input-content-picker-source': UmbInputContentPickerSourceElement;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user