Merge branch 'main' into v14/feature/readonly-properties
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
# Property Dataset Dashboard Example
|
||||
|
||||
This example is a work in progress example of how to write a property editor.
|
||||
|
||||
This example covers a few points:
|
||||
|
||||
- Using an existing Property Editor Schema
|
||||
@@ -0,0 +1,42 @@
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { html, customElement, LitElement, property, css } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
|
||||
import type { UmbBlockDataType, UmbBlockEditorCustomViewElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
@customElement('example-block-custom-view')
|
||||
export class ExampleBlockCustomView extends UmbElementMixin(LitElement) implements UmbBlockEditorCustomViewElement {
|
||||
//
|
||||
@property({ attribute: false })
|
||||
content?: UmbBlockDataType;
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<div class="uui-text">
|
||||
<h5 class="uui-text">My Custom View</h5>
|
||||
<p>Headline: ${this.content.headline}</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
UmbTextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
background-color: #dddddd;
|
||||
border-radius: 9px;
|
||||
padding: 12px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default ExampleBlockCustomView;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'example-block-custom-view': ExampleBlockCustomView;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { ManifestBlockEditorCustomView } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const manifests: Array<ManifestBlockEditorCustomView> = [
|
||||
{
|
||||
type: 'blockEditorCustomView',
|
||||
alias: 'Umb.blockEditorCustomView.TestView',
|
||||
name: 'Block Editor Custom View Test',
|
||||
element: () => import('./block-custom-view.js'),
|
||||
forContentTypeAlias: 'headlineUmbracoDemoBlock',
|
||||
forBlockEditor: 'block-grid',
|
||||
},
|
||||
];
|
||||
40
src/Umbraco.Web.UI.Client/package-lock.json
generated
40
src/Umbraco.Web.UI.Client/package-lock.json
generated
@@ -24,6 +24,7 @@
|
||||
"./src/packages/property-editors",
|
||||
"./src/packages/tags",
|
||||
"./src/packages/templating",
|
||||
"./src/packages/tiny-mce",
|
||||
"./src/packages/umbraco-news",
|
||||
"./src/packages/user",
|
||||
"./src/packages/webhook"
|
||||
@@ -52,7 +53,7 @@
|
||||
"@hey-api/openapi-ts": "^0.48.3",
|
||||
"@mdx-js/react": "^3.0.1",
|
||||
"@open-wc/testing": "^4.0.0",
|
||||
"@playwright/test": "^1.45.2",
|
||||
"@playwright/test": "^1.45.3",
|
||||
"@rollup/plugin-commonjs": "^26.0.1",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
@@ -97,7 +98,7 @@
|
||||
"storybook": "^7.6.17",
|
||||
"tiny-glob": "^0.2.9",
|
||||
"tsc-alias": "^1.8.10",
|
||||
"typedoc": "^0.26.4",
|
||||
"typedoc": "^0.26.5",
|
||||
"typescript": "^5.5.3",
|
||||
"typescript-eslint": "^7.16.1",
|
||||
"typescript-json-schema": "^0.64.0",
|
||||
@@ -3386,12 +3387,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.45.2",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.2.tgz",
|
||||
"integrity": "sha512-JxG9eq92ET75EbVi3s+4sYbcG7q72ECeZNbdBlaMkGcNbiDQ4cAi8U2QP5oKkOx+1gpaiL1LDStmzCaEM1Z6fQ==",
|
||||
"version": "1.45.3",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.3.tgz",
|
||||
"integrity": "sha512-UKF4XsBfy+u3MFWEH44hva1Q8Da28G6RFtR2+5saw+jgAFQV5yYnB1fu68Mz7fO+5GJF3wgwAIs0UelU8TxFrA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"playwright": "1.45.2"
|
||||
"playwright": "1.45.3"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
@@ -7412,6 +7413,10 @@
|
||||
"resolved": "src/packages/templating",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@umbraco-backoffice/tiny-mce": {
|
||||
"resolved": "src/packages/tiny-mce",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@umbraco-backoffice/umbraco-news": {
|
||||
"resolved": "src/packages/umbraco-news",
|
||||
"link": true
|
||||
@@ -17498,12 +17503,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.45.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.2.tgz",
|
||||
"integrity": "sha512-ReywF2t/0teRvNBpfIgh5e4wnrI/8Su8ssdo5XsQKpjxJj+jspm00jSoz9BTg91TT0c9HRjXO7LBNVrgYj9X0g==",
|
||||
"version": "1.45.3",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.3.tgz",
|
||||
"integrity": "sha512-QhVaS+lpluxCaioejDZ95l4Y4jSFCsBvl2UZkpeXlzxmqS+aABr5c82YmfMHrL6x27nvrvykJAFpkzT2eWdJww==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"playwright-core": "1.45.2"
|
||||
"playwright-core": "1.45.3"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
@@ -17516,9 +17521,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.45.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.2.tgz",
|
||||
"integrity": "sha512-ha175tAWb0dTK0X4orvBIqi3jGEt701SMxMhyujxNrgd8K0Uy5wMSwwcQHtyB4om7INUkfndx02XnQ2p6dvLDw==",
|
||||
"version": "1.45.3",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.3.tgz",
|
||||
"integrity": "sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
@@ -20084,9 +20089,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/typedoc": {
|
||||
"version": "0.26.4",
|
||||
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.26.4.tgz",
|
||||
"integrity": "sha512-FlW6HpvULDKgc3rK04V+nbFyXogPV88hurarDPOjuuB5HAwuAlrCMQ5NeH7Zt68a/ikOKu6Z/0hFXAeC9xPccQ==",
|
||||
"version": "0.26.5",
|
||||
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.26.5.tgz",
|
||||
"integrity": "sha512-Vn9YKdjKtDZqSk+by7beZ+xzkkr8T8CYoiasqyt4TTRFy5+UHzL/mF/o4wGBjRF+rlWQHDb0t6xCpA3JNL5phg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lunr": "^2.3.9",
|
||||
@@ -21741,6 +21746,9 @@
|
||||
"src/packages/templating": {
|
||||
"name": "@umbraco-backoffice/templating"
|
||||
},
|
||||
"src/packages/tiny-mce": {
|
||||
"name": "@umbraco-backoffice/tiny-mce"
|
||||
},
|
||||
"src/packages/umbraco-news": {
|
||||
"name": "@umbraco-backoffice/umbraco-news"
|
||||
},
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
"./src/packages/property-editors",
|
||||
"./src/packages/tags",
|
||||
"./src/packages/templating",
|
||||
"./src/packages/tiny-mce",
|
||||
"./src/packages/umbraco-news",
|
||||
"./src/packages/user",
|
||||
"./src/packages/webhook"
|
||||
@@ -222,7 +223,7 @@
|
||||
"@hey-api/openapi-ts": "^0.48.3",
|
||||
"@mdx-js/react": "^3.0.1",
|
||||
"@open-wc/testing": "^4.0.0",
|
||||
"@playwright/test": "^1.45.2",
|
||||
"@playwright/test": "^1.45.3",
|
||||
"@rollup/plugin-commonjs": "^26.0.1",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
@@ -267,7 +268,7 @@
|
||||
"storybook": "^7.6.17",
|
||||
"tiny-glob": "^0.2.9",
|
||||
"tsc-alias": "^1.8.10",
|
||||
"typedoc": "^0.26.4",
|
||||
"typedoc": "^0.26.5",
|
||||
"typescript": "^5.5.3",
|
||||
"typescript-eslint": "^7.16.1",
|
||||
"typescript-json-schema": "^0.64.0",
|
||||
|
||||
@@ -1562,6 +1562,7 @@ export default {
|
||||
ascending: 'ascending',
|
||||
descending: 'descending',
|
||||
template: 'Template',
|
||||
systemFields: 'System fields',
|
||||
},
|
||||
grid: {
|
||||
media: 'Image',
|
||||
|
||||
@@ -14,6 +14,7 @@ import { UmbArrayState, UmbObjectState, appendToFrozenArray } from '@umbraco-cms
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { ManifestWorkspace, PropertyEditorSettingsProperty } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { UmbId } from '@umbraco-cms/backoffice/id';
|
||||
|
||||
export class UmbBlockGridAreaTypeWorkspaceContext
|
||||
extends UmbSubmittableWorkspaceContextBase<UmbBlockGridTypeAreaType>
|
||||
@@ -43,12 +44,20 @@ export class UmbBlockGridAreaTypeWorkspaceContext
|
||||
{
|
||||
path: 'edit/:id',
|
||||
component: () => import('./block-grid-area-type-workspace-editor.element.js'),
|
||||
setup: (_component, info) => {
|
||||
setup: (component, info) => {
|
||||
const id = info.match.params.id;
|
||||
(_component as any).workspaceAlias = manifest.alias;
|
||||
(component as any).workspaceAlias = manifest.alias;
|
||||
this.load(id);
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
component: () => import('./block-grid-area-type-workspace-editor.element.js'),
|
||||
setup: (component) => {
|
||||
(component as any).workspaceAlias = manifest.alias;
|
||||
this.create();
|
||||
},
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -78,17 +87,24 @@ export class UmbBlockGridAreaTypeWorkspaceContext
|
||||
}
|
||||
|
||||
async create() {
|
||||
throw new Error('Method not implemented.');
|
||||
/*
|
||||
//Only set groupKey property if it exists
|
||||
const data: UmbBlockGridTypeAreaType = {
|
||||
this.resetState();
|
||||
let data: UmbBlockGridTypeAreaType = {
|
||||
key: UmbId.new(),
|
||||
alias: '',
|
||||
columnSpan: 12,
|
||||
rowSpan: 1,
|
||||
minAllowed: 0,
|
||||
maxAllowed: undefined,
|
||||
specifiedAllowance: [],
|
||||
};
|
||||
|
||||
// If we have a modal context, we blend in the modal preset data: [NL]
|
||||
if (this.modalContext) {
|
||||
data = { ...data, ...this.modalContext.data.preset };
|
||||
}
|
||||
|
||||
this.setIsNew(true);
|
||||
this.#data.setValue(data);
|
||||
return { data };
|
||||
*/
|
||||
}
|
||||
|
||||
getData() {
|
||||
|
||||
@@ -6,11 +6,7 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { html, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit';
|
||||
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property';
|
||||
import {
|
||||
UmbPropertyValueChangeEvent,
|
||||
type UmbPropertyEditorConfigCollection,
|
||||
} from '@umbraco-cms/backoffice/property-editor';
|
||||
import { UmbId } from '@umbraco-cms/backoffice/id';
|
||||
import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
|
||||
import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router';
|
||||
import { incrementString } from '@umbraco-cms/backoffice/utils';
|
||||
|
||||
@@ -61,7 +57,17 @@ export class UmbPropertyEditorUIBlockGridAreasConfigElement
|
||||
new UmbModalRouteRegistrationController(this, UMB_BLOCK_GRID_AREA_TYPE_WORKSPACE_MODAL)
|
||||
.addAdditionalPath('block-grid-area-type')
|
||||
.onSetup(() => {
|
||||
return { data: { entityType: 'block-grid-area-type', preset: {} }, modal: { size: 'large' } };
|
||||
if (!this._areaGridColumns) return false;
|
||||
const halfGridColumns = this._areaGridColumns * 0.5;
|
||||
const columnSpan = halfGridColumns === Math.round(halfGridColumns) ? halfGridColumns : this._areaGridColumns;
|
||||
|
||||
return {
|
||||
data: {
|
||||
entityType: 'block-grid-area-type',
|
||||
preset: { columnSpan, alias: this.#generateUniqueAreaAlias('area') },
|
||||
},
|
||||
modal: { size: 'large' },
|
||||
};
|
||||
})
|
||||
.observeRouteBuilder((routeBuilder) => {
|
||||
this._workspacePath = routeBuilder({});
|
||||
@@ -104,29 +110,6 @@ export class UmbPropertyEditorUIBlockGridAreasConfigElement
|
||||
return alias;
|
||||
}
|
||||
|
||||
#addNewArea() {
|
||||
if (!this._areaGridColumns) return;
|
||||
const halfGridColumns = this._areaGridColumns * 0.5;
|
||||
const columnSpan = halfGridColumns === Math.round(halfGridColumns) ? halfGridColumns : this._areaGridColumns;
|
||||
|
||||
this._value = [
|
||||
...this._value,
|
||||
{
|
||||
key: UmbId.new(),
|
||||
alias: this.#generateUniqueAreaAlias('area'),
|
||||
columnSpan: columnSpan,
|
||||
rowSpan: 1,
|
||||
minAllowed: 0,
|
||||
maxAllowed: undefined,
|
||||
specifiedAllowance: [],
|
||||
},
|
||||
];
|
||||
this.requestUpdate('_value');
|
||||
this.dispatchEvent(new UmbPropertyValueChangeEvent());
|
||||
|
||||
//TODO: open area edit workspace
|
||||
}
|
||||
|
||||
override render() {
|
||||
return this._areaGridColumns
|
||||
? html`${this._styleElement}
|
||||
@@ -144,7 +127,11 @@ export class UmbPropertyEditorUIBlockGridAreasConfigElement
|
||||
.key=${area.key}></umb-block-area-config-entry>`,
|
||||
)}
|
||||
</div>
|
||||
<uui-button id="add-button" look="placeholder" label=${'Add area'} @click=${this.#addNewArea}></uui-button>`
|
||||
<uui-button
|
||||
id="add-button"
|
||||
look="placeholder"
|
||||
label=${'Add area'}
|
||||
href=${this._workspacePath + 'create'}></uui-button>`
|
||||
: '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,6 @@ export class UmbBlockTypeWorkspaceContext<BlockTypeData extends UmbBlockTypeWith
|
||||
|
||||
this.setIsNew(true);
|
||||
this.#data.setValue(data);
|
||||
return { data };
|
||||
}
|
||||
|
||||
getData() {
|
||||
|
||||
@@ -148,7 +148,7 @@ export class UmbInputEntityElement extends UmbFormControlMixin<string | undefine
|
||||
id="btn-add"
|
||||
look="placeholder"
|
||||
@click=${this.#openPicker}
|
||||
label="${this.localize.term('general_choose')}"></uui-button>
|
||||
label=${this.localize.term('general_choose')}></uui-button>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -90,17 +90,18 @@ export class UmbCompositionPickerModalElement extends UmbModalBaseElement<
|
||||
await this.#init;
|
||||
if (!this.#compositionRepository) return;
|
||||
|
||||
const isElement = this.data?.isElement;
|
||||
const currentPropertyAliases = this.data?.currentPropertyAliases;
|
||||
// Notice isElement is not available on all types that can be composed.
|
||||
const isElement = this.data?.isElement ?? undefined;
|
||||
const currentPropertyAliases = this.data?.currentPropertyAliases ?? [];
|
||||
|
||||
const { data } = await this.#compositionRepository.availableCompositions({
|
||||
unique: this.#unique,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
// TODO: isElement is not available on all types that can be composed.
|
||||
isElement: isElement ?? false,
|
||||
isElement: isElement,
|
||||
currentCompositeUniques: this._selection,
|
||||
currentPropertyAliases: currentPropertyAliases ?? [],
|
||||
currentPropertyAliases: currentPropertyAliases,
|
||||
});
|
||||
|
||||
if (!data) return;
|
||||
@@ -129,11 +130,13 @@ export class UmbCompositionPickerModalElement extends UmbModalBaseElement<
|
||||
<div slot="actions">
|
||||
<uui-button label=${this.localize.term('general_close')} @click=${this._rejectModal}></uui-button>
|
||||
${!this._references.length
|
||||
? html`<uui-button
|
||||
label=${this.localize.term('general_submit')}
|
||||
look="primary"
|
||||
color="positive"
|
||||
@click=${this._submitModal}></uui-button>`
|
||||
? html`
|
||||
<uui-button
|
||||
label=${this.localize.term('general_submit')}
|
||||
look="primary"
|
||||
color="positive"
|
||||
@click=${this._submitModal}></uui-button>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
</umb-body-layout>
|
||||
@@ -141,7 +144,8 @@ export class UmbCompositionPickerModalElement extends UmbModalBaseElement<
|
||||
}
|
||||
|
||||
#renderHasReference() {
|
||||
return html` <umb-localize key="contentTypeEditor_compositionInUse">
|
||||
return html`
|
||||
<umb-localize key="contentTypeEditor_compositionInUse">
|
||||
This Content Type is used in a composition, and therefore cannot be composed itself.
|
||||
</umb-localize>
|
||||
<h4>
|
||||
@@ -154,19 +158,22 @@ export class UmbCompositionPickerModalElement extends UmbModalBaseElement<
|
||||
${repeat(
|
||||
this._references,
|
||||
(item) => item.unique,
|
||||
(item) =>
|
||||
html`<uui-ref-node-document-type
|
||||
(item) => html`
|
||||
<uui-ref-node-document-type
|
||||
href=${'/section/settings/workspace/document-type/edit/' + item.unique}
|
||||
name=${item.name}>
|
||||
name=${this.localize.string(item.name)}>
|
||||
<umb-icon slot="icon" name=${item.icon}></umb-icon>
|
||||
</uui-ref-node-document-type>`,
|
||||
</uui-ref-node-document-type>
|
||||
`,
|
||||
)}
|
||||
</div>`;
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderAvailableCompositions() {
|
||||
if (this._compatibleCompositions) {
|
||||
return html`<umb-localize key="contentTypeEditor_compositionsDescription">
|
||||
return html`
|
||||
<umb-localize key="contentTypeEditor_compositionsDescription">
|
||||
Inherit tabs and properties from an existing Document Type. New tabs will be<br />added to the current
|
||||
Document Type or merged if a tab with an identical name exists.<br />
|
||||
</umb-localize>
|
||||
@@ -184,11 +191,14 @@ export class UmbCompositionPickerModalElement extends UmbModalBaseElement<
|
||||
: nothing}
|
||||
${this.#renderCompositionsItems(folder.compositions)}`,
|
||||
)}
|
||||
</div>`;
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
return html`<umb-localize key="contentTypeEditor_noAvailableCompositions">
|
||||
There are no Content Types available to use as a composition
|
||||
</umb-localize>`;
|
||||
return html`
|
||||
<umb-localize key="contentTypeEditor_noAvailableCompositions">
|
||||
There are no Content Types available to use as a composition
|
||||
</umb-localize>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,15 +206,16 @@ export class UmbCompositionPickerModalElement extends UmbModalBaseElement<
|
||||
return repeat(
|
||||
compositionsList,
|
||||
(compositions) => compositions.unique,
|
||||
(compositions) =>
|
||||
html`<uui-menu-item
|
||||
label=${compositions.name}
|
||||
(compositions) => html`
|
||||
<uui-menu-item
|
||||
label=${this.localize.string(compositions.name)}
|
||||
selectable
|
||||
@selected=${() => this.#onSelectionAdd(compositions.unique)}
|
||||
@deselected=${() => this.#onSelectionRemove(compositions.unique)}
|
||||
?selected=${this._selection.find((unique) => unique === compositions.unique)}>
|
||||
<umb-icon name=${compositions.icon} slot="icon"></umb-icon>
|
||||
</uui-menu-item>`,
|
||||
</uui-menu-item>
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { UmbContentTypeModel, UmbPropertyContainerTypes, UmbPropertyTypeContainerModel } from '../types.js';
|
||||
import type { UmbContentTypeStructureManager } from './content-type-structure-manager.class.js';
|
||||
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { UmbController, UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
|
||||
|
||||
/**
|
||||
@@ -17,6 +17,8 @@ export class UmbContentTypeContainerStructureHelper<T extends UmbContentTypeMode
|
||||
|
||||
#structure?: UmbContentTypeStructureManager<T>;
|
||||
|
||||
#containerObservers: Array<UmbController> = [];
|
||||
|
||||
// State containing the all containers defined in the data:
|
||||
#childContainers = new UmbArrayState<UmbPropertyTypeContainerModel>([], (x) => x.id);
|
||||
readonly containers = this.#childContainers.asObservable();
|
||||
@@ -150,30 +152,31 @@ export class UmbContentTypeContainerStructureHelper<T extends UmbContentTypeMode
|
||||
this.#parentType,
|
||||
),
|
||||
(containers) => {
|
||||
// We want to remove hasProperties of groups that does not exist anymore.:
|
||||
// this.#removeHasPropertiesOfGroup()
|
||||
this.#hasProperties.setValue([]);
|
||||
this.#childContainers.setValue([]);
|
||||
this.#containerObservers.forEach((x) => x.destroy());
|
||||
this.#containerObservers = [];
|
||||
|
||||
containers.forEach((container) => {
|
||||
this.#observeHasPropertiesOf(container.id);
|
||||
|
||||
this.observe(
|
||||
this.#structure!.containersOfParentId(container.id, this.#childType!),
|
||||
(containers) => {
|
||||
// get the direct owner containers of this container id:
|
||||
this.#ownerChildContainers =
|
||||
this.#structure!.getOwnerContainers(this.#childType!, this.#containerId!) ?? [];
|
||||
// TODO: Maybe check for dif before setting it? Cause currently we are setting it every time one of the containers change. [NL]
|
||||
this.#containerObservers.push(
|
||||
this.observe(
|
||||
this.#structure!.containersOfParentId(container.id, this.#childType!),
|
||||
(containers) => {
|
||||
// get the direct owner containers of this container id: [NL]
|
||||
this.#ownerChildContainers =
|
||||
this.#structure!.getOwnerContainers(this.#childType!, this.#containerId!) ?? [];
|
||||
|
||||
// Remove existing containers that are not the parent of the new containers:
|
||||
this.#childContainers.filter(
|
||||
(x) => x.parent?.id !== container.id || containers.some((y) => y.id === x.id),
|
||||
);
|
||||
// Remove existing containers that are not the parent of the new containers: [NL]
|
||||
this.#childContainers.filter(
|
||||
(x) => x.parent?.id !== container.id || containers.some((y) => y.id === x.id),
|
||||
);
|
||||
|
||||
this.#childContainers.append(containers);
|
||||
},
|
||||
'_observeGroupsOf_' + container.id,
|
||||
this.#childContainers.append(containers);
|
||||
},
|
||||
'_observeGroupsOf_' + container.id,
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@ export class UmbContentTypePropertyStructureHelper<T extends UmbContentTypeModel
|
||||
|
||||
#structure?: UmbContentTypeStructureManager<T>;
|
||||
|
||||
private _containerId?: string | null;
|
||||
#containerId?: string | null;
|
||||
|
||||
// State which holds all the properties of the current container, this is a composition of all properties from the containers that matches our target [NL]
|
||||
#propertyStructure = new UmbArrayState<UmbPropertyTypeModel>([], (x) => x.id);
|
||||
@@ -59,12 +59,12 @@ export class UmbContentTypePropertyStructureHelper<T extends UmbContentTypeModel
|
||||
}
|
||||
|
||||
public setContainerId(value?: string | null) {
|
||||
if (this._containerId === value) return;
|
||||
this._containerId = value;
|
||||
if (this.#containerId === value) return;
|
||||
this.#containerId = value;
|
||||
this.#observeContainers();
|
||||
}
|
||||
public getContainerId() {
|
||||
return this._containerId;
|
||||
return this.#containerId;
|
||||
}
|
||||
|
||||
private _containerName?: string;
|
||||
@@ -74,9 +74,9 @@ export class UmbContentTypePropertyStructureHelper<T extends UmbContentTypeModel
|
||||
|
||||
#containers?: Array<UmbPropertyTypeContainerModel>;
|
||||
#observeContainers() {
|
||||
if (!this.#structure || this._containerId === undefined) return;
|
||||
if (!this.#structure || this.#containerId === undefined) return;
|
||||
|
||||
if (this._containerId === null) {
|
||||
if (this.#containerId === null) {
|
||||
this.observe(
|
||||
this.#structure.propertyStructuresOf(null),
|
||||
(properties) => {
|
||||
@@ -87,7 +87,7 @@ export class UmbContentTypePropertyStructureHelper<T extends UmbContentTypeModel
|
||||
this.removeUmbControllerByAlias('_observeContainers');
|
||||
} else {
|
||||
this.observe(
|
||||
this.#structure.containerById(this._containerId),
|
||||
this.#structure.containerById(this.#containerId),
|
||||
(container) => {
|
||||
if (container) {
|
||||
this._containerName = container.name ?? '';
|
||||
|
||||
@@ -42,10 +42,13 @@ export class UmbContentTypeStructureManager<
|
||||
readonly ownerContentType = this.#contentTypes.asObservablePart((x) =>
|
||||
x.find((y) => y.unique === this.#ownerContentTypeUnique),
|
||||
);
|
||||
|
||||
private readonly _contentTypeContainers = this.#contentTypes.asObservablePart((x) =>
|
||||
x.flatMap((x) => x.containers ?? []),
|
||||
readonly ownerContentTypeCompositions = this.#contentTypes.asObservablePart(
|
||||
(x) => x.find((y) => y.unique === this.#ownerContentTypeUnique)?.compositions,
|
||||
);
|
||||
|
||||
readonly #contentTypeContainers = this.#contentTypes.asObservablePart(() => {
|
||||
return this.#contentTypes.getValue().flatMap((x) => x.containers ?? []);
|
||||
});
|
||||
readonly contentTypeUniques = this.#contentTypes.asObservablePart((x) => x.map((y) => y.unique));
|
||||
readonly contentTypeAliases = this.#contentTypes.asObservablePart((x) => x.map((y) => y.alias));
|
||||
|
||||
@@ -61,12 +64,12 @@ export class UmbContentTypeStructureManager<
|
||||
super(host);
|
||||
this.#repository = typeRepository;
|
||||
|
||||
this.observe(this.contentTypes, (contentTypes) => {
|
||||
contentTypes.forEach((contentType) => {
|
||||
this._loadContentTypeCompositions(contentType);
|
||||
});
|
||||
// Observe owner content type compositions, as we only allow one level of compositions at this moment. [NL]
|
||||
// But, we could support more, we would just need to flatMap all compositions and make sure the entries are unique and then base the observation on that. [NL]
|
||||
this.observe(this.ownerContentTypeCompositions, (ownerContentTypeCompositions) => {
|
||||
this._loadContentTypeCompositions(ownerContentTypeCompositions);
|
||||
});
|
||||
this.observe(this._contentTypeContainers, (contentTypeContainers) => {
|
||||
this.observe(this.#contentTypeContainers, (contentTypeContainers) => {
|
||||
this.#containers.setValue(contentTypeContainers);
|
||||
});
|
||||
}
|
||||
@@ -136,8 +139,24 @@ export class UmbContentTypeStructureManager<
|
||||
this._observeContentType(data);
|
||||
}
|
||||
|
||||
private async _loadContentTypeCompositions(contentType: T) {
|
||||
contentType.compositions?.forEach((composition) => {
|
||||
private async _loadContentTypeCompositions(ownerContentTypeCompositions: T['compositions'] | undefined) {
|
||||
if (!ownerContentTypeCompositions) {
|
||||
// Owner content type was undefined, so we can not load compositions. But at this point we neither offload existing compositions, this is most likely not a case that needs to be handled.
|
||||
return;
|
||||
}
|
||||
|
||||
const ownerUnique = this.getOwnerContentTypeUnique();
|
||||
// Remove content types that does not exist as compositions anymore:
|
||||
this.#contentTypes.getValue().forEach((x) => {
|
||||
if (
|
||||
x.unique !== ownerUnique &&
|
||||
!ownerContentTypeCompositions.find((comp) => comp.contentType.unique === x.unique)
|
||||
) {
|
||||
this.#contentTypeObservers.find((y) => y.controllerAlias === 'observeContentType_' + x.unique)?.destroy();
|
||||
this.#contentTypes.removeOne(x.unique);
|
||||
}
|
||||
});
|
||||
ownerContentTypeCompositions.forEach((composition) => {
|
||||
this._ensureType(composition.contentType.unique);
|
||||
});
|
||||
}
|
||||
@@ -164,23 +183,19 @@ export class UmbContentTypeStructureManager<
|
||||
|
||||
// Notice we do not store the content type in the store here, cause it will happen shortly after when the observations gets its first initial callback. [NL]
|
||||
|
||||
// Load inherited and composed types:
|
||||
//this._loadContentTypeCompositions(data);// Should not be necessary as this will be done when appended to the contentTypes state. [NL]
|
||||
|
||||
const ctrl = this.observe(
|
||||
// Then lets start observation of the content type:
|
||||
await this.#repository.byUnique(data.unique),
|
||||
(docType) => {
|
||||
if (docType) {
|
||||
// TODO: Handle if there was changes made to the owner document type in this context. [NL]
|
||||
/*
|
||||
possible easy solutions could be to notify user wether they want to update(Discard the changes to accept the new ones). [NL]
|
||||
*/
|
||||
this.#contentTypes.appendOne(docType);
|
||||
} else {
|
||||
// Remove the content type from the store, if it does not exist anymore.
|
||||
this.#contentTypes.removeOne(data.unique);
|
||||
}
|
||||
// TODO: Do we need to handle the undefined case? [NL]
|
||||
},
|
||||
'observeContentType_' + data.unique,
|
||||
// Controller Alias is used to stop observation when no longer needed. [NL]
|
||||
);
|
||||
|
||||
this.#contentTypeObservers.push(ctrl);
|
||||
|
||||
@@ -416,14 +416,16 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements
|
||||
return html`
|
||||
<div id="actions">
|
||||
${this._compositionRepositoryAlias
|
||||
? html` <uui-button
|
||||
look="outline"
|
||||
label=${this.localize.term('contentTypeEditor_compositions')}
|
||||
compact
|
||||
@click=${this.#openCompositionModal}>
|
||||
<uui-icon name="icon-merge"></uui-icon>
|
||||
${this.localize.term('contentTypeEditor_compositions')}
|
||||
</uui-button>`
|
||||
? html`
|
||||
<uui-button
|
||||
look="outline"
|
||||
label=${this.localize.term('contentTypeEditor_compositions')}
|
||||
compact
|
||||
@click=${this.#openCompositionModal}>
|
||||
<uui-icon name="icon-merge"></uui-icon>
|
||||
${this.localize.term('contentTypeEditor_compositions')}
|
||||
</uui-button>
|
||||
`
|
||||
: ''}
|
||||
<uui-button look="outline" label=${sortButtonText} compact @click=${this.#toggleSortMode}>
|
||||
<uui-icon name="icon-navigation"></uui-icon>
|
||||
|
||||
@@ -63,7 +63,7 @@ export class UmbContentWorkspaceViewEditTabElement extends UmbLitElement {
|
||||
this._groups,
|
||||
(group) => group.id,
|
||||
(group) =>
|
||||
html`<uui-box .headline=${group.name ?? ''}>
|
||||
html`<uui-box .headline=${this.localize.string(group.name) ?? ''}>
|
||||
<umb-content-workspace-view-edit-properties
|
||||
class="properties"
|
||||
.containerId=${group.id}></umb-content-workspace-view-edit-properties>
|
||||
|
||||
@@ -138,7 +138,7 @@ export class UmbContentWorkspaceViewEditElement extends UmbLitElement implements
|
||||
(tab) => {
|
||||
const path = this._routerPath + '/tab/' + encodeFolderName(tab.name || '');
|
||||
return html`<uui-tab label=${tab.name ?? 'Unnamed'} .active=${path === this._activePath} href=${path}
|
||||
>${tab.name}</uui-tab
|
||||
>${this.localize.string(tab.name)}</uui-tab
|
||||
>`;
|
||||
},
|
||||
)}
|
||||
|
||||
@@ -1,30 +1,76 @@
|
||||
import { AsyncDirective, directive, nothing, type ElementPart } from '@umbraco-cms/backoffice/external/lit';
|
||||
/**
|
||||
*
|
||||
* test if a element has focus
|
||||
* this also returns true if the focused element is a child of the target.
|
||||
* @param current
|
||||
* @param target
|
||||
* @returns bool
|
||||
*/
|
||||
function hasFocus(current: any, target: HTMLElement): boolean {
|
||||
if (current === target) {
|
||||
return true;
|
||||
}
|
||||
if (current.shadowRoot) {
|
||||
const node = current.shadowRoot.activeElement;
|
||||
if (node) {
|
||||
return hasFocus(node, target);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `focus` directive sets focus on the given element once its connected to the DOM.
|
||||
*/
|
||||
class UmbFocusDirective extends AsyncDirective {
|
||||
private _el?: HTMLElement;
|
||||
static #next?: HTMLElement;
|
||||
#el?: HTMLElement;
|
||||
#timeout?: number;
|
||||
|
||||
override render() {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
override update(part: ElementPart) {
|
||||
if (this._el !== part.element) {
|
||||
// This does feel wrong that we need to wait one render. [NL]
|
||||
// Because even if our elements focus method is implemented so it can be called initially, my research shows that calling the focus method at this point is too early, thought the element is connected to the DOM and the focus method is available. [NL]
|
||||
// This smells a bit like the DOMPart of which the directive is in is not connected to the main DOM yet, and therefor cant receive focus. [NL]
|
||||
// Which is why we need to await one render: [NL]
|
||||
requestAnimationFrame(() => {
|
||||
(this._el = part.element as HTMLElement).focus();
|
||||
});
|
||||
if (this.#el !== part.element) {
|
||||
UmbFocusDirective.#next = this.#el = part.element as HTMLElement;
|
||||
this.#setFocus();
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method tries to set focus, if it did not succeed, it will try again.
|
||||
* It always tests against the latest element, because the directive can be used multiple times in the same render.
|
||||
* This is NOT needed because the elements focus method isn't ready to be called, but due to something with rendering of the DOM.
|
||||
* But I'm not completely sure at this movement why the browser does not accept the focus call.
|
||||
* But I have tested that everything is in place for it to be good, so something else must have an effect,
|
||||
* setting the focus somewhere else, maybe a re-appending of some sort?
|
||||
* cause Lit does not re-render the element but also notice reconnect callback on the directive is not triggered either. [NL]
|
||||
*/
|
||||
#setFocus = () => {
|
||||
// Make sure we clear the timeout, so we don't get multiple timeouts running.
|
||||
if (this.#timeout) {
|
||||
clearTimeout(this.#timeout);
|
||||
this.#timeout = undefined;
|
||||
}
|
||||
// If this is the next element to focus, then try to focus it.
|
||||
if (this.#el && this.#el === UmbFocusDirective.#next) {
|
||||
this.#el.focus();
|
||||
if (hasFocus(document.activeElement, this.#el) === false) {
|
||||
this.#timeout = setTimeout(this.#setFocus, 100) as unknown as number;
|
||||
} else {
|
||||
UmbFocusDirective.#next = undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
override disconnected() {
|
||||
this._el = undefined;
|
||||
if (this.#el === UmbFocusDirective.#next) {
|
||||
UmbFocusDirective.#next = undefined;
|
||||
}
|
||||
this.#el = undefined;
|
||||
}
|
||||
|
||||
//override reconnected() {}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type {
|
||||
UmbLocalizationSetBase,
|
||||
UmbLocalizationDictionary,
|
||||
UmbLocalizationFlatDictionary,
|
||||
} from '@umbraco-cms/backoffice/localization-api';
|
||||
import {
|
||||
type UmbLocalizationSetBase,
|
||||
type UmbLocalizationDictionary,
|
||||
type UmbLocalizationFlatDictionary,
|
||||
UMB_DEFAULT_LOCALIZATION_CULTURE
|
||||
} from "@umbraco-cms/backoffice/localization-api";
|
||||
import { umbLocalizationManager } from '@umbraco-cms/backoffice/localization-api';
|
||||
import type { ManifestLocalization, UmbBackofficeExtensionRegistry } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
|
||||
@@ -21,7 +22,7 @@ function addOrUpdateDictionary(
|
||||
}
|
||||
|
||||
export class UmbLocalizationRegistry {
|
||||
#currentLanguage = new UmbStringState(document.documentElement.lang ?? 'en-us');
|
||||
#currentLanguage = new UmbStringState(document.documentElement.lang !== '' ? document.documentElement.lang : UMB_DEFAULT_LOCALIZATION_CULTURE);
|
||||
readonly currentLanguage = this.#currentLanguage.asObservable();
|
||||
|
||||
#loadedExtAliases: Array<string> = [];
|
||||
|
||||
@@ -51,7 +51,7 @@ export class UmbItemPickerModalElement extends UmbModalBaseElement<UmbItemPicker
|
||||
if (!this.data) return nothing;
|
||||
const items = this._filtered;
|
||||
return html`
|
||||
<umb-body-layout headline=${this.data.headline}>
|
||||
<umb-body-layout headline=${this.localize.string(this.data.headline)}>
|
||||
<div id="main">
|
||||
<uui-input
|
||||
type="search"
|
||||
@@ -72,7 +72,7 @@ export class UmbItemPickerModalElement extends UmbModalBaseElement<UmbItemPicker
|
||||
(item) => item.value,
|
||||
(item) => html`
|
||||
<umb-ref-item
|
||||
name=${item.label}
|
||||
name=${this.localize.string(item.label)}
|
||||
detail=${ifDefined(item.description)}
|
||||
icon=${ifDefined(item.icon)}
|
||||
@click=${() => this.#submit(item)}>
|
||||
|
||||
@@ -215,6 +215,7 @@ export class UmbWorkspaceSplitViewVariantSelectorElement extends UmbLitElement {
|
||||
return html`
|
||||
<uui-input
|
||||
id="name-input"
|
||||
label=${this.localize.term('placeholders_entername')}
|
||||
.value=${this._name ?? ''}
|
||||
@input=${this.#handleInput}
|
||||
${umbFocus()}
|
||||
|
||||
@@ -87,7 +87,9 @@ export class UmbDataTypeWorkspaceViewInfoReferenceElement extends UmbLitElement
|
||||
(item) => html`
|
||||
<uui-table-row>
|
||||
<uui-table-cell>
|
||||
<uui-ref-node-document-type href=${this.#getEditPath(item)} name=${item.name ?? item.unique}>
|
||||
<uui-ref-node-document-type
|
||||
href=${this.#getEditPath(item)}
|
||||
name=${this.localize.string(item.name ?? item.unique)}>
|
||||
<umb-icon slot="icon" name=${item.icon ?? 'icon-document'}></umb-icon>
|
||||
</uui-ref-node-document-type>
|
||||
</uui-table-cell>
|
||||
|
||||
@@ -204,7 +204,7 @@ export class UmbInputDocumentTypeElement extends UmbFormControlMixin<string | un
|
||||
if (!item.unique) return;
|
||||
const href = `${this._editPath}edit/${item.unique}`;
|
||||
return html`
|
||||
<uui-ref-node-document-type name=${item.name} id=${item.unique}>
|
||||
<uui-ref-node-document-type name=${this.localize.string(item.name)} id=${item.unique}>
|
||||
${this.#renderIcon(item)}
|
||||
<uui-action-bar slot="actions">
|
||||
<uui-button href=${href} label=${this.localize.term('general_open')}></uui-button>
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
export { UmbDocumentTypeCompositionRepository } from './document-type-composition.repository.js';
|
||||
export { UMB_DOCUMENT_TYPE_COMPOSITION_REPOSITORY_ALIAS } from './manifests.js';
|
||||
|
||||
@@ -2,11 +2,11 @@ import type { ManifestRepository, ManifestTypes } from '@umbraco-cms/backoffice/
|
||||
|
||||
export const UMB_DOCUMENT_TYPE_COMPOSITION_REPOSITORY_ALIAS = 'Umb.Repository.DocumentType.Composition';
|
||||
|
||||
const queryRepository: ManifestRepository = {
|
||||
const compositionRepository: ManifestRepository = {
|
||||
type: 'repository',
|
||||
alias: UMB_DOCUMENT_TYPE_COMPOSITION_REPOSITORY_ALIAS,
|
||||
name: 'Document Type Composition Repository',
|
||||
api: () => import('./document-type-composition.repository.js'),
|
||||
};
|
||||
|
||||
export const manifests: Array<ManifestTypes> = [queryRepository];
|
||||
export const manifests: Array<ManifestTypes> = [compositionRepository];
|
||||
|
||||
@@ -133,60 +133,65 @@ export class UmbDocumentCreateOptionsModalElement extends UmbModalBaseElement<
|
||||
}
|
||||
|
||||
#renderDocumentTypes() {
|
||||
return html`<uui-box .headline=${this._headline}>
|
||||
${when(
|
||||
this._allowedDocumentTypes.length === 0,
|
||||
() => html`
|
||||
<umb-localize key="create_noDocumentTypes">
|
||||
There are no allowed Document Types available for creating content here. You must enable these in
|
||||
<strong>Document Types</strong> within the <strong>Settings</strong> section, by editing the
|
||||
<strong>Allowed child node types</strong> under <strong>Permissions</strong>.<br />
|
||||
</umb-localize>
|
||||
<uui-button
|
||||
id="edit-permissions"
|
||||
look="secondary"
|
||||
@click=${() => this._rejectModal()}
|
||||
href=${`/section/settings/workspace/document-type/edit/${this.data?.documentType?.unique}/view/structure`}
|
||||
label=${this.localize.term('create_noDocumentTypesEditPermissions')}></uui-button>
|
||||
`,
|
||||
() =>
|
||||
repeat(
|
||||
this._allowedDocumentTypes,
|
||||
(documentType) => documentType.unique,
|
||||
(documentType) =>
|
||||
html` <uui-ref-node-document-type
|
||||
data-id=${ifDefined(documentType.unique)}
|
||||
.name=${documentType.name}
|
||||
.alias=${documentType.description ?? ''}
|
||||
select-only
|
||||
selectable
|
||||
@selected=${() => this.#onSelectDocumentType(documentType.unique)}>
|
||||
<umb-icon slot="icon" name=${documentType.icon || 'icon-circle-dotted'}></umb-icon>
|
||||
</uui-ref-node-document-type>`,
|
||||
),
|
||||
)}
|
||||
</uui-box>`;
|
||||
return html`
|
||||
<uui-box .headline=${this._headline}>
|
||||
${when(
|
||||
this._allowedDocumentTypes.length === 0,
|
||||
() => html`
|
||||
<umb-localize key="create_noDocumentTypes">
|
||||
There are no allowed Document Types available for creating content here. You must enable these in
|
||||
<strong>Document Types</strong> within the <strong>Settings</strong> section, by editing the
|
||||
<strong>Allowed child node types</strong> under <strong>Permissions</strong>.<br />
|
||||
</umb-localize>
|
||||
<uui-button
|
||||
id="edit-permissions"
|
||||
look="secondary"
|
||||
href=${`/section/settings/workspace/document-type/edit/${this.data?.documentType?.unique}/view/structure`}
|
||||
label=${this.localize.term('create_noDocumentTypesEditPermissions')}
|
||||
@click=${() => this._rejectModal()}></uui-button>
|
||||
`,
|
||||
() =>
|
||||
repeat(
|
||||
this._allowedDocumentTypes,
|
||||
(documentType) => documentType.unique,
|
||||
(documentType) => html`
|
||||
<uui-ref-node-document-type
|
||||
data-id=${ifDefined(documentType.unique)}
|
||||
.name=${this.localize.string(documentType.name)}
|
||||
.alias=${this.localize.string(documentType.description ?? '')}
|
||||
select-only
|
||||
selectable
|
||||
@selected=${() => this.#onSelectDocumentType(documentType.unique)}>
|
||||
<umb-icon slot="icon" name=${documentType.icon || 'icon-circle-dotted'}></umb-icon>
|
||||
</uui-ref-node-document-type>
|
||||
`,
|
||||
),
|
||||
)}
|
||||
</uui-box>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderBlueprints() {
|
||||
return html`<uui-box headline=${this.localize.term('blueprints_selectBlueprint')}>
|
||||
<uui-menu-item
|
||||
id="blank"
|
||||
label=${this.localize.term('blueprints_blankBlueprint')}
|
||||
@click=${() => this.#onNavigate(this.#documentTypeUnique)}>
|
||||
<umb-icon slot="icon" name=${this.#documentTypeIcon}></umb-icon>
|
||||
</uui-menu-item>
|
||||
${repeat(
|
||||
this._availableBlueprints,
|
||||
(blueprint) => blueprint.unique,
|
||||
(blueprint) =>
|
||||
html`<uui-menu-item
|
||||
label=${blueprint.name}
|
||||
@click=${() => this.#onNavigate(this.#documentTypeUnique, blueprint.unique)}>
|
||||
<umb-icon slot="icon" name="icon-blueprint"></umb-icon>
|
||||
</uui-menu-item>`,
|
||||
)}</uui-box
|
||||
> `;
|
||||
return html`
|
||||
<uui-box headline=${this.localize.term('blueprints_selectBlueprint')}>
|
||||
<uui-menu-item
|
||||
id="blank"
|
||||
label=${this.localize.term('blueprints_blankBlueprint')}
|
||||
@click=${() => this.#onNavigate(this.#documentTypeUnique)}>
|
||||
<umb-icon slot="icon" name=${this.#documentTypeIcon}></umb-icon>
|
||||
</uui-menu-item>
|
||||
${repeat(
|
||||
this._availableBlueprints,
|
||||
(blueprint) => blueprint.unique,
|
||||
(blueprint) =>
|
||||
html`<uui-menu-item
|
||||
label=${blueprint.name}
|
||||
@click=${() => this.#onNavigate(this.#documentTypeUnique, blueprint.unique)}>
|
||||
<umb-icon slot="icon" name="icon-blueprint"></umb-icon>
|
||||
</uui-menu-item>`,
|
||||
)}
|
||||
</uui-box>
|
||||
`;
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
|
||||
@@ -252,7 +252,7 @@ export class UmbDocumentWorkspaceViewInfoElement extends UmbLitElement {
|
||||
<uui-ref-node-document-type
|
||||
standalone
|
||||
href=${editDocumentTypePath + 'edit/' + this._documentTypeUnique}
|
||||
name=${ifDefined(this._documentTypeName)}>
|
||||
name=${ifDefined(this.localize.string(this._documentTypeName ?? ''))}>
|
||||
<umb-icon slot="icon" name=${ifDefined(this._documentTypeIcon)}></umb-icon>
|
||||
</uui-ref-node-document-type>
|
||||
</div>
|
||||
|
||||
@@ -64,25 +64,25 @@ export class UmbHealthCheckGroupBoxOverviewElement extends UmbLitElement {
|
||||
_renderCheckResults(resultObject: any) {
|
||||
return html`${resultObject.success > 0
|
||||
? html`<uui-tag look="secondary" color="positive">
|
||||
<uui-icon name="icon-check"></uui-icon>
|
||||
<uui-icon name="check"></uui-icon>
|
||||
${resultObject.success}
|
||||
</uui-tag> `
|
||||
: nothing}
|
||||
${resultObject.warning > 0
|
||||
? html`<uui-tag look="secondary" color="warning">
|
||||
<uui-icon name="icon-alert"></uui-icon>
|
||||
<uui-icon name="alert"></uui-icon>
|
||||
${resultObject.warning}
|
||||
</uui-tag>`
|
||||
: nothing}
|
||||
${resultObject.error > 0
|
||||
? html`<uui-tag look="secondary" color="danger">
|
||||
<uui-icon name="icon-wrong"></uui-icon>
|
||||
<uui-icon name="remove"></uui-icon>
|
||||
${resultObject.error}
|
||||
</uui-tag>`
|
||||
: nothing}
|
||||
${resultObject.info > 0
|
||||
? html`<uui-tag look="secondary">
|
||||
<uui-icon name="icon-info"></uui-icon>
|
||||
<uui-icon name="info"></uui-icon>
|
||||
${resultObject.info}
|
||||
</uui-tag>`
|
||||
: nothing} `;
|
||||
|
||||
@@ -129,7 +129,7 @@ export class UmbDashboardHealthCheckGroupElement extends UmbLitElement {
|
||||
case StatusResultTypeModel.SUCCESS:
|
||||
return html`<uui-icon style="color: var(--uui-color-positive);" name="check"></uui-icon>`;
|
||||
case StatusResultTypeModel.WARNING:
|
||||
return html`<uui-icon style="color: var(--uui-color-warning);" name="alert"></uui-icon>`;
|
||||
return html`<uui-icon style="color: var(--uui-color-warning-standalone);" name="alert"></uui-icon>`;
|
||||
case StatusResultTypeModel.ERROR:
|
||||
return html`<uui-icon style="color: var(--uui-color-danger);" name="remove"></uui-icon>`;
|
||||
case StatusResultTypeModel.INFO:
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export const UMB_LANGUAGE_WORKSPACE_ALIAS = 'Umb.Workspace.Language';
|
||||
@@ -1,6 +1,7 @@
|
||||
import { UmbLanguageDetailRepository } from '../../repository/index.js';
|
||||
import type { UmbLanguageDetailModel } from '../../types.js';
|
||||
import { UmbLanguageWorkspaceEditorElement } from './language-workspace-editor.element.js';
|
||||
import { UMB_LANGUAGE_WORKSPACE_ALIAS } from './constants.js';
|
||||
import {
|
||||
type UmbSubmittableWorkspaceContext,
|
||||
UmbSubmittableWorkspaceContextBase,
|
||||
@@ -27,7 +28,7 @@ export class UmbLanguageWorkspaceContext
|
||||
readonly validationErrors = this.#validationErrors.asObservable();
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, 'Umb.Workspace.Language');
|
||||
super(host, UMB_LANGUAGE_WORKSPACE_ALIAS);
|
||||
|
||||
this.routes.setRoutes([
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UMB_LANGUAGE_WORKSPACE_ALIAS } from './constants.js';
|
||||
import { UmbSubmitWorkspaceAction } from '@umbraco-cms/backoffice/workspace';
|
||||
import type {
|
||||
ManifestWorkspaces,
|
||||
@@ -9,7 +10,7 @@ import type {
|
||||
const workspace: ManifestWorkspaces = {
|
||||
type: 'workspace',
|
||||
kind: 'routable',
|
||||
alias: 'Umb.Workspace.Language',
|
||||
alias: UMB_LANGUAGE_WORKSPACE_ALIAS,
|
||||
name: 'Language Workspace',
|
||||
api: () => import('./language-workspace.context.js'),
|
||||
meta: {
|
||||
|
||||
@@ -174,7 +174,7 @@ export class UmbInputMediaTypeElement extends UmbFormControlMixin<string | undef
|
||||
if (!item.unique) return;
|
||||
const href = `${this._editPath}edit/${item.unique}`;
|
||||
return html`
|
||||
<uui-ref-node-document-type name=${item.name} id=${item.unique}>
|
||||
<uui-ref-node-document-type name=${this.localize.string(item.name)} id=${item.unique}>
|
||||
${this.#renderIcon(item)}
|
||||
<uui-action-bar slot="actions">
|
||||
<uui-button href=${href} label=${this.localize.term('general_open')}></uui-button>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import './components/index.js';
|
||||
|
||||
export * from './components/index.js';
|
||||
export * from './workspace/index.js';
|
||||
|
||||
export * from './entity.js';
|
||||
export * from './repository/index.js';
|
||||
export * from './tree/types.js';
|
||||
export * from './utils.ts/index.js';
|
||||
export * from './types.js';
|
||||
export * from './entity.js';
|
||||
export * from './utils.ts/index.js';
|
||||
export * from './workspace/index.js';
|
||||
|
||||
export { UMB_MEDIA_TYPE_PICKER_MODAL } from './tree/index.js';
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { UMB_MEDIA_TYPE_COMPOSITION_REPOSITORY_ALIAS } from './manifests.js';
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { ManifestRepository, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const UMB_MEDIA_TYPE_COMPOSITION_REPOSITORY_ALIAS = 'Umb.Repository.MediaType.Composition';
|
||||
|
||||
const compositionRepository: ManifestRepository = {
|
||||
type: 'repository',
|
||||
alias: UMB_MEDIA_TYPE_COMPOSITION_REPOSITORY_ALIAS,
|
||||
name: 'Media Type Composition Repository',
|
||||
api: () => import('./media-type-composition.repository.js'),
|
||||
};
|
||||
|
||||
export const manifests: Array<ManifestTypes> = [compositionRepository];
|
||||
@@ -0,0 +1,36 @@
|
||||
import { UmbMediaTypeCompositionServerDataSource } from './media-type-composition.server.data-source.js';
|
||||
import type { UmbContentTypeCompositionRepository } from '@umbraco-cms/backoffice/content-type';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type {
|
||||
UmbMediaTypeAvailableCompositionRequestModel,
|
||||
UmbMediaTypeCompositionCompatibleModel,
|
||||
UmbMediaTypeCompositionReferenceModel,
|
||||
} from '@umbraco-cms/backoffice/media-type';
|
||||
import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
export class UmbMediaTypeCompositionRepository
|
||||
extends UmbRepositoryBase
|
||||
implements
|
||||
UmbContentTypeCompositionRepository<
|
||||
UmbMediaTypeCompositionReferenceModel,
|
||||
UmbMediaTypeCompositionCompatibleModel,
|
||||
UmbMediaTypeAvailableCompositionRequestModel
|
||||
>
|
||||
{
|
||||
#compositionSource: UmbMediaTypeCompositionServerDataSource;
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host);
|
||||
this.#compositionSource = new UmbMediaTypeCompositionServerDataSource(this);
|
||||
}
|
||||
|
||||
async getReferences(unique: string) {
|
||||
return this.#compositionSource.getReferences(unique);
|
||||
}
|
||||
|
||||
async availableCompositions(args: UmbMediaTypeAvailableCompositionRequestModel) {
|
||||
return this.#compositionSource.availableCompositions(args);
|
||||
}
|
||||
}
|
||||
|
||||
export { UmbMediaTypeCompositionRepository as api };
|
||||
@@ -0,0 +1,86 @@
|
||||
import type {
|
||||
UmbMediaTypeCompositionCompatibleModel,
|
||||
UmbMediaTypeCompositionReferenceModel,
|
||||
UmbMediaTypeAvailableCompositionRequestModel,
|
||||
} from '../../types.js';
|
||||
import { type MediaTypeCompositionRequestModel, MediaTypeService } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
import type { UmbContentTypeCompositionDataSource } from '@umbraco-cms/backoffice/content-type';
|
||||
|
||||
/**
|
||||
* A data source for the Media Type Composition that fetches data from the server
|
||||
* @export
|
||||
* @class UmbMediaTypeCompositionServerDataSource
|
||||
*/
|
||||
export class UmbMediaTypeCompositionServerDataSource
|
||||
implements
|
||||
UmbContentTypeCompositionDataSource<
|
||||
UmbMediaTypeCompositionReferenceModel,
|
||||
UmbMediaTypeCompositionCompatibleModel,
|
||||
UmbMediaTypeAvailableCompositionRequestModel
|
||||
>
|
||||
{
|
||||
#host: UmbControllerHost;
|
||||
|
||||
/**
|
||||
* Creates an instance of UmbMediaTypeCompositionServerDataSource.
|
||||
* @param {UmbControllerHost} host
|
||||
* @memberof UmbMediaTypeCompositionServerDataSource
|
||||
*/
|
||||
constructor(host: UmbControllerHost) {
|
||||
this.#host = host;
|
||||
}
|
||||
/**
|
||||
* Fetches the compatible compositions for a Media type from the server
|
||||
* @param {string} unique
|
||||
* @return {*}
|
||||
* @memberof UmbMediaTypeCompositionServerDataSource
|
||||
*/
|
||||
async getReferences(unique: string) {
|
||||
const response = await tryExecuteAndNotify(
|
||||
this.#host,
|
||||
MediaTypeService.getMediaTypeByIdCompositionReferences({ id: unique }),
|
||||
);
|
||||
const error = response.error;
|
||||
const data: Array<UmbMediaTypeCompositionReferenceModel> | undefined = response.data?.map((reference) => {
|
||||
return {
|
||||
unique: reference.id,
|
||||
icon: reference.icon,
|
||||
name: reference.name,
|
||||
};
|
||||
});
|
||||
|
||||
return { data, error };
|
||||
}
|
||||
/**
|
||||
* Updates the compositions for a media type on the server
|
||||
* @param {MediaTypeCompositionRequestModel} requestBody
|
||||
* @return {*}
|
||||
* @memberof UmbMediaTypeCompositionServerDataSource
|
||||
*/
|
||||
async availableCompositions(args: UmbMediaTypeAvailableCompositionRequestModel) {
|
||||
const requestBody: MediaTypeCompositionRequestModel = {
|
||||
id: args.unique,
|
||||
currentCompositeIds: args.currentCompositeUniques,
|
||||
currentPropertyAliases: args.currentPropertyAliases,
|
||||
};
|
||||
|
||||
const response = await tryExecuteAndNotify(
|
||||
this.#host,
|
||||
MediaTypeService.postMediaTypeAvailableCompositions({ requestBody }),
|
||||
);
|
||||
const error = response.error;
|
||||
const data: Array<UmbMediaTypeCompositionCompatibleModel> | undefined = response.data?.map((composition) => {
|
||||
return {
|
||||
unique: composition.id,
|
||||
name: composition.name,
|
||||
icon: composition.icon,
|
||||
folderPath: composition.folderPath,
|
||||
isCompatible: composition.isCompatible,
|
||||
};
|
||||
});
|
||||
|
||||
return { data, error };
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './item/index.js';
|
||||
export * from './composition/index.js';
|
||||
export * from './detail/index.js';
|
||||
export * from './item/index.js';
|
||||
export * from './structure/index.js';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { manifests as detailManifests } from './detail/manifests.js';
|
||||
import { manifests as itemManifests } from './item/manifests.js';
|
||||
import { manifests as compositionManifests } from './composition/manifests.js';
|
||||
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const manifests: Array<ManifestTypes> = [...detailManifests, ...itemManifests];
|
||||
export const manifests: Array<ManifestTypes> = [...detailManifests, ...itemManifests, ...compositionManifests];
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
import type { UmbMediaTypeEntityType } from './entity.js';
|
||||
import type { UmbContentTypeModel } from '@umbraco-cms/backoffice/content-type';
|
||||
import type {
|
||||
UmbContentTypeAvailableCompositionRequestModel,
|
||||
UmbContentTypeCompositionCompatibleModel,
|
||||
UmbContentTypeCompositionReferenceModel,
|
||||
UmbContentTypeModel,
|
||||
} from '@umbraco-cms/backoffice/content-type';
|
||||
|
||||
export interface UmbMediaTypeDetailModel extends UmbContentTypeModel {
|
||||
entityType: UmbMediaTypeEntityType;
|
||||
}
|
||||
|
||||
export interface UmbMediaTypeAvailableCompositionRequestModel extends UmbContentTypeAvailableCompositionRequestModel {}
|
||||
|
||||
export interface UmbMediaTypeCompositionCompatibleModel extends UmbContentTypeCompositionCompatibleModel {}
|
||||
|
||||
export interface UmbMediaTypeCompositionReferenceModel extends UmbContentTypeCompositionReferenceModel {}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UMB_MEDIA_TYPE_COMPOSITION_REPOSITORY_ALIAS } from '../repository/index.js';
|
||||
import { UMB_MEDIA_TYPE_WORKSPACE_ALIAS } from './constants.js';
|
||||
import type {
|
||||
ManifestWorkspaces,
|
||||
@@ -29,6 +30,7 @@ const workspaceViews: Array<ManifestWorkspaceViews> = [
|
||||
label: '#general_design',
|
||||
pathname: 'design',
|
||||
icon: 'icon-document-dashed-line',
|
||||
compositionRepositoryAlias: UMB_MEDIA_TYPE_COMPOSITION_REPOSITORY_ALIAS,
|
||||
},
|
||||
conditions: [
|
||||
{
|
||||
|
||||
@@ -89,7 +89,8 @@ export class UmbMediaCreateOptionsModalElement extends UmbModalBaseElement<
|
||||
}
|
||||
|
||||
#renderNotAllowed() {
|
||||
return html`<umb-localize key="create_noMediaTypes">
|
||||
return html`
|
||||
<umb-localize key="create_noMediaTypes">
|
||||
There are no allowed Media Types available for creating media here. You must enable these in
|
||||
<strong>Media Types</strong> within the <strong>Settings</strong> section, by editing the
|
||||
<strong>Allowed child node types</strong> under <strong>Permissions</strong>. </umb-localize
|
||||
@@ -99,23 +100,25 @@ export class UmbMediaCreateOptionsModalElement extends UmbModalBaseElement<
|
||||
look="secondary"
|
||||
@click=${() => this._rejectModal()}
|
||||
href=${`/section/settings/workspace/media-type/edit/${this.data?.mediaType?.unique}/view/structure`}
|
||||
label=${this.localize.term('create_noMediaTypesEditPermissions')}></uui-button>`;
|
||||
label=${this.localize.term('create_noMediaTypesEditPermissions')}></uui-button>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderAllowedMediaTypes() {
|
||||
return repeat(
|
||||
this._allowedMediaTypes,
|
||||
(mediaType) => mediaType.unique,
|
||||
(mediaType) =>
|
||||
html`<uui-ref-node-document-type
|
||||
(mediaType) => html`
|
||||
<uui-ref-node-document-type
|
||||
data-id=${ifDefined(mediaType.unique)}
|
||||
.name=${mediaType.name}
|
||||
.alias=${mediaType.description ?? ''}
|
||||
.name=${this.localize.string(mediaType.name)}
|
||||
.alias=${this.localize.string(mediaType.description ?? '')}
|
||||
select-only
|
||||
selectable
|
||||
@selected=${() => this.#onNavigate(mediaType)}>
|
||||
${mediaType.icon ? html`<umb-icon slot="icon" name=${mediaType.icon}></umb-icon>` : nothing}
|
||||
</uui-ref-node-document-type>`,
|
||||
</uui-ref-node-document-type>
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ export class UmbInputMemberTypeElement extends UmbFormControlMixin<string | unde
|
||||
#renderItem(item: UmbUniqueItemModel) {
|
||||
if (!item.unique) return;
|
||||
return html`
|
||||
<uui-ref-node-document-type name=${item.name}>
|
||||
<uui-ref-node-document-type name=${this.localize.string(item.name)}>
|
||||
${when(item.icon, () => html`<umb-icon slot="icon" name=${item.icon!}></umb-icon>`)}
|
||||
<uui-action-bar slot="actions">
|
||||
<uui-button
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import './components/index.js';
|
||||
|
||||
export * from './workspace/index.js';
|
||||
export * from './components/index.js';
|
||||
export * from './repository/index.js';
|
||||
export * from './entity.js';
|
||||
export * from './tree/index.js';
|
||||
export * from './modal/member-type-picker-modal.token.js';
|
||||
|
||||
export type { UmbMemberTypeDetailModel } from './types.js';
|
||||
export * from './repository/index.js';
|
||||
export * from './tree/index.js';
|
||||
export * from './types.js';
|
||||
export * from './workspace/index.js';
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { UMB_MEMBER_TYPE_COMPOSITION_REPOSITORY_ALIAS } from './manifests.js';
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { ManifestRepository, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const UMB_MEMBER_TYPE_COMPOSITION_REPOSITORY_ALIAS = 'Umb.Repository.MemberType.Composition';
|
||||
|
||||
const compositionRepository: ManifestRepository = {
|
||||
type: 'repository',
|
||||
alias: UMB_MEMBER_TYPE_COMPOSITION_REPOSITORY_ALIAS,
|
||||
name: 'Member Type Composition Repository',
|
||||
api: () => import('./member-type-composition.repository.js'),
|
||||
};
|
||||
|
||||
export const manifests: Array<ManifestTypes> = [compositionRepository];
|
||||
@@ -0,0 +1,36 @@
|
||||
import { UmbMemberTypeCompositionServerDataSource } from './member-type-composition.server.data-source.js';
|
||||
import type { UmbContentTypeCompositionRepository } from '@umbraco-cms/backoffice/content-type';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type {
|
||||
UmbMemberTypeAvailableCompositionRequestModel,
|
||||
UmbMemberTypeCompositionCompatibleModel,
|
||||
UmbMemberTypeCompositionReferenceModel,
|
||||
} from '@umbraco-cms/backoffice/member-type';
|
||||
import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
export class UmbMemberTypeCompositionRepository
|
||||
extends UmbRepositoryBase
|
||||
implements
|
||||
UmbContentTypeCompositionRepository<
|
||||
UmbMemberTypeCompositionReferenceModel,
|
||||
UmbMemberTypeCompositionCompatibleModel,
|
||||
UmbMemberTypeAvailableCompositionRequestModel
|
||||
>
|
||||
{
|
||||
#compositionSource: UmbMemberTypeCompositionServerDataSource;
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host);
|
||||
this.#compositionSource = new UmbMemberTypeCompositionServerDataSource(this);
|
||||
}
|
||||
|
||||
async getReferences(unique: string) {
|
||||
return this.#compositionSource.getReferences(unique);
|
||||
}
|
||||
|
||||
async availableCompositions(args: UmbMemberTypeAvailableCompositionRequestModel) {
|
||||
return this.#compositionSource.availableCompositions(args);
|
||||
}
|
||||
}
|
||||
|
||||
export { UmbMemberTypeCompositionRepository as api };
|
||||
@@ -0,0 +1,89 @@
|
||||
import type {
|
||||
UmbMemberTypeCompositionCompatibleModel,
|
||||
UmbMemberTypeCompositionReferenceModel,
|
||||
UmbMemberTypeAvailableCompositionRequestModel,
|
||||
} from '../../types.js';
|
||||
import {
|
||||
type MemberTypeCompositionRequestModel,
|
||||
MemberTypeService,
|
||||
} from '@umbraco-cms/backoffice/external/backend-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
import type { UmbContentTypeCompositionDataSource } from '@umbraco-cms/backoffice/content-type';
|
||||
|
||||
/**
|
||||
* A data source for the Member Type Composition that fetches data from the server
|
||||
* @export
|
||||
* @class UmbMemberTypeCompositionServerDataSource
|
||||
*/
|
||||
export class UmbMemberTypeCompositionServerDataSource
|
||||
implements
|
||||
UmbContentTypeCompositionDataSource<
|
||||
UmbMemberTypeCompositionReferenceModel,
|
||||
UmbMemberTypeCompositionCompatibleModel,
|
||||
UmbMemberTypeAvailableCompositionRequestModel
|
||||
>
|
||||
{
|
||||
#host: UmbControllerHost;
|
||||
|
||||
/**
|
||||
* Creates an instance of UmbMemberTypeCompositionServerDataSource.
|
||||
* @param {UmbControllerHost} host
|
||||
* @memberof UmbMemberTypeCompositionServerDataSource
|
||||
*/
|
||||
constructor(host: UmbControllerHost) {
|
||||
this.#host = host;
|
||||
}
|
||||
/**
|
||||
* Fetches the compatible compositions for a document type from the server
|
||||
* @param {string} unique
|
||||
* @return {*}
|
||||
* @memberof UmbMemberTypeCompositionServerDataSource
|
||||
*/
|
||||
async getReferences(unique: string) {
|
||||
const response = await tryExecuteAndNotify(
|
||||
this.#host,
|
||||
MemberTypeService.getMemberTypeByIdCompositionReferences({ id: unique }),
|
||||
);
|
||||
const error = response.error;
|
||||
const data: Array<UmbMemberTypeCompositionReferenceModel> | undefined = response.data?.map((reference) => {
|
||||
return {
|
||||
unique: reference.id,
|
||||
icon: reference.icon,
|
||||
name: reference.name,
|
||||
};
|
||||
});
|
||||
|
||||
return { data, error };
|
||||
}
|
||||
/**
|
||||
* Updates the compositions for a document type on the server
|
||||
* @param {MemberTypeCompositionRequestModel} requestBody
|
||||
* @return {*}
|
||||
* @memberof UmbMemberTypeCompositionServerDataSource
|
||||
*/
|
||||
async availableCompositions(args: UmbMemberTypeAvailableCompositionRequestModel) {
|
||||
const requestBody: MemberTypeCompositionRequestModel = {
|
||||
id: args.unique,
|
||||
currentCompositeIds: args.currentCompositeUniques,
|
||||
currentPropertyAliases: args.currentPropertyAliases,
|
||||
};
|
||||
|
||||
const response = await tryExecuteAndNotify(
|
||||
this.#host,
|
||||
MemberTypeService.postMemberTypeAvailableCompositions({ requestBody }),
|
||||
);
|
||||
const error = response.error;
|
||||
const data: Array<UmbMemberTypeCompositionCompatibleModel> | undefined = response.data?.map((composition) => {
|
||||
return {
|
||||
unique: composition.id,
|
||||
name: composition.name,
|
||||
icon: composition.icon,
|
||||
folderPath: composition.folderPath,
|
||||
isCompatible: composition.isCompatible,
|
||||
};
|
||||
});
|
||||
|
||||
return { data, error };
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
export { UmbMemberTypeDetailRepository, UMB_MEMBER_TYPE_DETAIL_REPOSITORY_ALIAS } from './detail/index.js';
|
||||
export { UmbMemberTypeItemRepository, UMB_MEMBER_TYPE_ITEM_REPOSITORY_ALIAS } from './item/index.js';
|
||||
export { UMB_MEMBER_TYPE_COMPOSITION_REPOSITORY_ALIAS } from './composition/index.js';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { manifests as detailManifests } from './detail/manifests.js';
|
||||
import { manifests as itemManifests } from './item/manifests.js';
|
||||
import { manifests as compositionManifests } from './composition/manifests.js';
|
||||
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const manifests: Array<ManifestTypes> = [...detailManifests, ...itemManifests];
|
||||
export const manifests: Array<ManifestTypes> = [...detailManifests, ...itemManifests, ...compositionManifests];
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
import type { UmbMemberTypeEntityType } from './entity.js';
|
||||
import type { UmbContentTypeModel } from '@umbraco-cms/backoffice/content-type';
|
||||
import type {
|
||||
UmbContentTypeAvailableCompositionRequestModel,
|
||||
UmbContentTypeCompositionCompatibleModel,
|
||||
UmbContentTypeCompositionReferenceModel,
|
||||
UmbContentTypeModel,
|
||||
} from '@umbraco-cms/backoffice/content-type';
|
||||
|
||||
export interface UmbMemberTypeDetailModel extends UmbContentTypeModel {
|
||||
entityType: UmbMemberTypeEntityType;
|
||||
}
|
||||
|
||||
export interface UmbMemberTypeAvailableCompositionRequestModel extends UmbContentTypeAvailableCompositionRequestModel {}
|
||||
|
||||
export interface UmbMemberTypeCompositionCompatibleModel extends UmbContentTypeCompositionCompatibleModel {}
|
||||
|
||||
export interface UmbMemberTypeCompositionReferenceModel extends UmbContentTypeCompositionReferenceModel {}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { UMB_MEMBER_TYPE_COMPOSITION_REPOSITORY_ALIAS } from '../repository/index.js';
|
||||
import type {
|
||||
ManifestWorkspaces,
|
||||
ManifestWorkspaceActions,
|
||||
ManifestWorkspaceView,
|
||||
ManifestWorkspaceViews,
|
||||
ManifestTypes,
|
||||
} from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { UmbSubmitWorkspaceAction } from '@umbraco-cms/backoffice/workspace';
|
||||
@@ -19,7 +20,7 @@ const workspace: ManifestWorkspaces = {
|
||||
},
|
||||
};
|
||||
|
||||
const workspaceViews: Array<ManifestWorkspaceView> = [
|
||||
const workspaceViews: Array<ManifestWorkspaceViews> = [
|
||||
{
|
||||
type: 'workspaceView',
|
||||
kind: 'contentTypeDesignEditor',
|
||||
@@ -29,6 +30,7 @@ const workspaceViews: Array<ManifestWorkspaceView> = [
|
||||
label: '#general_design',
|
||||
pathname: 'design',
|
||||
icon: 'icon-member-dashed-line',
|
||||
compositionRepositoryAlias: UMB_MEMBER_TYPE_COMPOSITION_REPOSITORY_ALIAS,
|
||||
},
|
||||
conditions: [
|
||||
{
|
||||
|
||||
@@ -152,7 +152,7 @@ export class UmbTemplateFieldDropdownListElement extends UmbLitElement {
|
||||
<uui-combobox id="preview">
|
||||
<uui-combobox-list @change=${this.#onChange}>
|
||||
<uui-combobox-list-option value="system">
|
||||
<strong>${this.localize.term('formSettings_systemFields')}</strong>
|
||||
<strong>${this.localize.term('template_systemFields')}</strong>
|
||||
</uui-combobox-list-option>
|
||||
<uui-combobox-list-option value="document-type" display-value=${this.localize.term('content_documentType')}>
|
||||
<strong> ${this.localize.term('content_documentType')} </strong>
|
||||
@@ -174,7 +174,8 @@ export class UmbTemplateFieldDropdownListElement extends UmbLitElement {
|
||||
|
||||
#renderAliasDropdown() {
|
||||
if (this._type !== FieldType.SYSTEM && !this._unique) return;
|
||||
return html`<strong>${this._uniqueName}</strong>
|
||||
return html`
|
||||
<strong>${this.localize.string(this._uniqueName ?? '')}</strong>
|
||||
<uui-combobox id="value" value=${ifDefined(this.value?.alias)}>
|
||||
<uui-combobox-list @change=${this.#onChangeValue}>
|
||||
${repeat(
|
||||
@@ -184,7 +185,8 @@ export class UmbTemplateFieldDropdownListElement extends UmbLitElement {
|
||||
html`<uui-combobox-list-option value=${ifDefined(field.alias)}>${field.alias}</uui-combobox-list-option>`,
|
||||
)}
|
||||
</uui-combobox-list>
|
||||
</uui-combobox>`;
|
||||
</uui-combobox>
|
||||
`;
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "@umbraco-backoffice/tiny-mce",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "vite build"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import { rmSync } from 'fs';
|
||||
import { getDefaultConfig } from '../../vite-config-base';
|
||||
|
||||
const dist = '../../../dist-cms/packages/tiny-mce';
|
||||
|
||||
// delete the unbundled dist folder
|
||||
rmSync(dist, { recursive: true, force: true });
|
||||
|
||||
export default defineConfig({
|
||||
...getDefaultConfig({ dist }),
|
||||
});
|
||||
Reference in New Issue
Block a user