Merge branch 'main' into v14/feature/search-in-pickers
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,44 @@
|
||||
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';
|
||||
|
||||
// eslint-disable-next-line local-rules/enforce-umb-prefix-on-element-name
|
||||
@customElement('example-block-custom-view')
|
||||
// eslint-disable-next-line local-rules/umb-class-prefix
|
||||
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',
|
||||
},
|
||||
];
|
||||
@@ -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
|
||||
31
src/Umbraco.Web.UI.Client/examples/property-editor/index.ts
Normal file
31
src/Umbraco.Web.UI.Client/examples/property-editor/index.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const manifests: Array<ManifestPropertyEditorUi> = [
|
||||
{
|
||||
type: 'propertyEditorUi',
|
||||
alias: 'example.propertyEditorUi.propertyEditor',
|
||||
name: 'Example Property Editor UI',
|
||||
element: () => import('./property-editor.js'),
|
||||
meta: {
|
||||
label: 'Example Editor',
|
||||
propertyEditorSchemaAlias: 'Umbraco.ListView',
|
||||
icon: 'icon-code',
|
||||
group: 'common',
|
||||
settings: {
|
||||
properties: [
|
||||
{
|
||||
alias: 'customText',
|
||||
label: 'Custom text',
|
||||
propertyEditorUiAlias: 'Umb.PropertyEditorUi.TextBox',
|
||||
},
|
||||
],
|
||||
defaultData: [
|
||||
{
|
||||
alias: 'customText',
|
||||
value: 'Default value',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,20 @@
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { html, customElement, LitElement } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
|
||||
|
||||
@customElement('example-property-editor')
|
||||
export class ExamplePropertyEditor extends UmbElementMixin(LitElement) {
|
||||
override render() {
|
||||
return html` <h1 class="uui-h2">Property Editor Example</h1> `;
|
||||
}
|
||||
|
||||
static override styles = [UmbTextStyles];
|
||||
}
|
||||
|
||||
export default ExamplePropertyEditor;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'example-property-editor': ExamplePropertyEditor;
|
||||
}
|
||||
}
|
||||
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",
|
||||
|
||||
@@ -19,8 +19,8 @@ export class UmbBackofficeContext extends UmbContextBase<UmbBackofficeContext> {
|
||||
#allowedSections = new UmbBasicState<Array<UmbExtensionManifestInitializer<ManifestSection>>>([]);
|
||||
public readonly allowedSections = this.#allowedSections.asObservable();
|
||||
|
||||
#verison = new UmbStringState(undefined);
|
||||
public readonly version = this.#verison.asObservable();
|
||||
#version = new UmbStringState(undefined);
|
||||
public readonly version = this.#version.asObservable();
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, UMB_BACKOFFICE_CONTEXT);
|
||||
@@ -70,7 +70,7 @@ export class UmbBackofficeContext extends UmbContextBase<UmbBackofficeContext> {
|
||||
) ?? [];
|
||||
|
||||
const version = [major, minor, patch].join('.') + (prerelease ? `-${prerelease}` : '');
|
||||
this.#verison.setValue(version);
|
||||
this.#version.setValue(version);
|
||||
}
|
||||
|
||||
public setActiveSectionAlias(alias: string) {
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
/** Example of how a grid layout stylehseet could be done with Flex box: */
|
||||
|
||||
.umb-block-grid__layout-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--umb-block-grid--row-gap, 0) var(--umb-block-grid--column-gap, 0);
|
||||
}
|
||||
.umb-block-grid__layout-item {
|
||||
position: relative;
|
||||
--umb-block-grid__layout-item-calc: calc(var(--umb-block-grid--item-column-span) / var(--umb-block-grid--grid-columns));
|
||||
width: calc(var(--umb-block-grid__layout-item-calc) * 100% - (1 - var(--umb-block-grid__layout-item-calc)) * var(--umb-block-grid--column-gap, 0px));
|
||||
}
|
||||
|
||||
|
||||
.umb-block-grid__area-container, .umb-block-grid__block--view::part(area-container) {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
gap: var(--umb-block-grid--areas-row-gap, 0) var(--umb-block-grid--areas-column-gap, 0);
|
||||
}
|
||||
.umb-block-grid__area {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
--umb-block-grid__area-calc: calc(var(--umb-block-grid--area-column-span) / var(--umb-block-grid--area-grid-columns, 1));
|
||||
width: calc(var(--umb-block-grid__area-calc) * 100% - (1 - var(--umb-block-grid__area-calc)) * var(--umb-block-grid--areas-column-gap, 0px));
|
||||
}
|
||||
|
||||
|
||||
.umb-block-grid__actions {
|
||||
clear: both;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
.umb-block-grid__layout-container {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--umb-block-grid--grid-columns, 1), minmax(0, 1fr));
|
||||
grid-auto-flow: row;
|
||||
grid-auto-rows: minmax(50px, min-content);
|
||||
|
||||
column-gap: var(--umb-block-grid--column-gap, 0);
|
||||
row-gap: var(--umb-block-grid--row-gap, 0);
|
||||
}
|
||||
.umb-block-grid__layout-item {
|
||||
position: relative;
|
||||
/* For small devices we scale columnSpan by three, to make everything bigger than 1/3 take full width: */
|
||||
grid-column-end: span min(calc(var(--umb-block-grid--item-column-span, 1) * 3), var(--umb-block-grid--grid-columns));
|
||||
grid-row: span var(--umb-block-grid--item-row-span, 1);
|
||||
}
|
||||
|
||||
|
||||
.umb-block-grid__area-container, .umb-block-grid__block--view::part(area-container) {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--umb-block-grid--area-grid-columns, var(--umb-block-grid--grid-columns, 1)), minmax(0, 1fr));
|
||||
grid-auto-flow: row;
|
||||
grid-auto-rows: minmax(50px, min-content);
|
||||
|
||||
column-gap: var(--umb-block-grid--areas-column-gap, 0);
|
||||
row-gap: var(--umb-block-grid--areas-row-gap, 0);
|
||||
}
|
||||
.umb-block-grid__area {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* For small devices we scale columnSpan by three, to make everything bigger than 1/3 take full width: */
|
||||
grid-column-end: span min(calc(var(--umb-block-grid--area-column-span, 1) * 3), var(--umb-block-grid--area-grid-columns));
|
||||
grid-row: span var(--umb-block-grid--area-row-span, 1);
|
||||
}
|
||||
|
||||
@media (min-width:1024px) {
|
||||
.umb-block-grid__layout-item {
|
||||
grid-column-end: span min(var(--umb-block-grid--item-column-span, 1), var(--umb-block-grid--grid-columns));
|
||||
}
|
||||
.umb-block-grid__area {
|
||||
grid-column-end: span min(var(--umb-block-grid--area-column-span, 1), var(--umb-block-grid--area-grid-columns));
|
||||
}
|
||||
}
|
||||
@@ -695,6 +695,7 @@ export default {
|
||||
hasReferencesDeleteConsequence:
|
||||
'Deleting <strong>%0%</strong> will delete the properties and their data from the following items',
|
||||
acceptDeleteConsequence: 'I understand this action will delete the properties and data based on this Data Type',
|
||||
noConfiguration: 'There is no configuration for this property editor.',
|
||||
},
|
||||
errorHandling: {
|
||||
errorButDataWasSaved:
|
||||
@@ -1561,6 +1562,7 @@ export default {
|
||||
ascending: 'ascending',
|
||||
descending: 'descending',
|
||||
template: 'Template',
|
||||
systemFields: 'System fields',
|
||||
},
|
||||
grid: {
|
||||
media: 'Image',
|
||||
@@ -1734,11 +1736,11 @@ export default {
|
||||
enabled: 'Enabled',
|
||||
events: 'Events',
|
||||
event: 'Event',
|
||||
url: 'Url',
|
||||
url: 'URL',
|
||||
types: 'Types',
|
||||
webhookKey: 'Webhook key',
|
||||
retryCount: 'Retry count',
|
||||
urlDescription: 'The url to call when the webhook is triggered.',
|
||||
urlDescription: 'The URL to call when the webhook is triggered.',
|
||||
eventDescription: 'The events for which the webhook should be triggered.',
|
||||
contentTypeDescription: 'Only trigger the webhook for a specific content type.',
|
||||
enabledDescription: 'Is the webhook enabled?',
|
||||
@@ -1746,6 +1748,7 @@ export default {
|
||||
contentType: 'Content Type',
|
||||
headers: 'Headers',
|
||||
selectEventFirst: 'Please select an event first.',
|
||||
selectEvents: 'Select events',
|
||||
},
|
||||
languages: {
|
||||
addLanguage: 'Add language',
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -7,6 +7,11 @@ import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-ed
|
||||
import { UMB_DATA_TYPE_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/data-type';
|
||||
import type { UmbBlockTypeWithGroupKey } from '@umbraco-cms/backoffice/block-type';
|
||||
import type { UUIComboboxElement, UUIComboboxEvent, UUIInputEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbRepositoryItemsManager } from '@umbraco-cms/backoffice/repository';
|
||||
import {
|
||||
UMB_DOCUMENT_TYPE_ITEM_REPOSITORY_ALIAS,
|
||||
type UmbDocumentTypeItemModel,
|
||||
} from '@umbraco-cms/backoffice/document-type';
|
||||
|
||||
@customElement('umb-property-editor-ui-block-grid-area-type-permission')
|
||||
export class UmbPropertyEditorUIBlockGridAreaTypePermissionElement
|
||||
@@ -24,20 +29,41 @@ export class UmbPropertyEditorUIBlockGridAreaTypePermissionElement
|
||||
@state()
|
||||
private _value: Array<UmbBlockGridTypeAreaTypePermission> = [];
|
||||
|
||||
_blockTypes: Array<UmbBlockTypeWithGroupKey> = [];
|
||||
|
||||
@state()
|
||||
private _blockTypes: Array<UmbBlockTypeWithGroupKey> = [];
|
||||
private _blockTypesWithElementName: Array<{ type: UmbBlockTypeWithGroupKey; name: string }> = [];
|
||||
|
||||
@state()
|
||||
private _blockGroups: Array<UmbBlockGridTypeGroupType> = [];
|
||||
|
||||
#itemsManager = new UmbRepositoryItemsManager<UmbDocumentTypeItemModel>(
|
||||
this,
|
||||
UMB_DOCUMENT_TYPE_ITEM_REPOSITORY_ALIAS,
|
||||
(x) => x.unique,
|
||||
);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.observe(this.#itemsManager.items, (items) => {
|
||||
this._blockTypesWithElementName = items
|
||||
.map((item) => {
|
||||
const blockType = this._blockTypes.find((block) => block.contentElementTypeKey === item.unique);
|
||||
if (blockType) {
|
||||
return { type: blockType, name: item.name };
|
||||
}
|
||||
return undefined;
|
||||
})
|
||||
.filter((x) => x !== undefined) as Array<{ type: UmbBlockTypeWithGroupKey; name: string }>;
|
||||
});
|
||||
|
||||
this.consumeContext(UMB_DATA_TYPE_WORKSPACE_CONTEXT, async (context) => {
|
||||
this.observe(
|
||||
await context.propertyValueByAlias<Array<UmbBlockTypeWithGroupKey>>('blocks'),
|
||||
(blockTypes) => {
|
||||
this._blockTypes = blockTypes ?? [];
|
||||
this.#itemsManager.setUniques(blockTypes.map((block) => block.contentElementTypeKey));
|
||||
},
|
||||
'observeBlockType',
|
||||
);
|
||||
@@ -103,7 +129,7 @@ export class UmbPropertyEditorUIBlockGridAreaTypePermissionElement
|
||||
this._value,
|
||||
(permission) => permission,
|
||||
(permission, index) => {
|
||||
const showCategoryHeader = this._blockGroups.length && this._blockTypes.length;
|
||||
const showCategoryHeader = this._blockGroups.length > 0 && this._blockTypesWithElementName.length > 0;
|
||||
|
||||
return html`<div class="permission-setting">
|
||||
<uui-combobox
|
||||
@@ -169,13 +195,13 @@ export class UmbPropertyEditorUIBlockGridAreaTypePermissionElement
|
||||
|
||||
#renderBlockTypes(area: UmbBlockGridTypeAreaTypePermission) {
|
||||
return repeat(
|
||||
this._blockTypes,
|
||||
(block) => block.contentElementTypeKey,
|
||||
this._blockTypesWithElementName,
|
||||
(block) => block.type.contentElementTypeKey,
|
||||
(block) =>
|
||||
html`<uui-combobox-list-option
|
||||
.value=${block.contentElementTypeKey}
|
||||
?selected=${area.elementTypeKey === block.contentElementTypeKey}>
|
||||
${block.label}
|
||||
.value=${block.type.contentElementTypeKey}
|
||||
?selected=${area.elementTypeKey === block.type.contentElementTypeKey}>
|
||||
${block.name}
|
||||
</uui-combobox-list-option>`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>`
|
||||
: '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,9 @@ export class UmbPropertyEditorUIBlockGridLayoutStylesheetElement
|
||||
.min=${this._limitMin}
|
||||
.max=${this._limitMax}></umb-input-static-file>
|
||||
<br />
|
||||
<a href="/umbraco/backoffice/assets/css/umbraco-blockgridlayout.css">Link to default layout stylesheet</a>
|
||||
<a href="/umbraco/backoffice/css/umbraco-blockgridlayout.css" target="_blank"
|
||||
>Link to default layout stylesheet</a
|
||||
>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
>`;
|
||||
},
|
||||
)}
|
||||
|
||||
@@ -18,3 +18,5 @@ export class UmbCultureRepository extends UmbControllerBase implements UmbApi {
|
||||
|
||||
override destroy() {}
|
||||
}
|
||||
|
||||
export { UmbCultureRepository as api };
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { UmbCultureRepository } from './culture.repository.js';
|
||||
import type { ManifestRepository, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const UMB_CULTURE_REPOSITORY_ALIAS = 'Umb.Repository.Culture';
|
||||
@@ -7,7 +6,7 @@ const repository: ManifestRepository = {
|
||||
type: 'repository',
|
||||
alias: UMB_CULTURE_REPOSITORY_ALIAS,
|
||||
name: 'Cultures Repository',
|
||||
api: UmbCultureRepository,
|
||||
api: () => import('./culture.repository.js'),
|
||||
};
|
||||
|
||||
export const manifests: Array<ManifestTypes> = [repository];
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import type { ManifestTypes } from '../models/index.js';
|
||||
import { manifest as menuAliasConditionManifest } from './menu-alias.condition.js';
|
||||
import { manifest as multipleAppLanguagesConditionManifest } from './multiple-app-languages.condition.js';
|
||||
import { manifest as sectionAliasConditionManifest } from './section-alias.condition.js';
|
||||
import { manifest as switchConditionManifest } from './switch.condition.js';
|
||||
|
||||
export const manifests: Array<ManifestTypes> = [
|
||||
menuAliasConditionManifest,
|
||||
multipleAppLanguagesConditionManifest,
|
||||
sectionAliasConditionManifest,
|
||||
switchConditionManifest,
|
||||
];
|
||||
|
||||
@@ -17,9 +17,13 @@ export class UmbMenuAliasCondition extends UmbConditionBase<MenuAliasConditionCo
|
||||
super(host, args);
|
||||
|
||||
this.consumeContext(UMB_MENU_CONTEXT, (context) => {
|
||||
this.observe(context.alias, (MenuAlias) => {
|
||||
this.permitted = MenuAlias === this.config.match;
|
||||
});
|
||||
this.observe(
|
||||
context.alias,
|
||||
(MenuAlias) => {
|
||||
this.permitted = MenuAlias === this.config.match;
|
||||
},
|
||||
'observeAlias',
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { UmbConditionBase } from './condition-base.controller.js';
|
||||
import { UMB_APP_LANGUAGE_CONTEXT } from '@umbraco-cms/backoffice/language';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type {
|
||||
ManifestCondition,
|
||||
UmbConditionConfigBase,
|
||||
UmbConditionControllerArguments,
|
||||
UmbExtensionCondition,
|
||||
} from '@umbraco-cms/backoffice/extension-api';
|
||||
|
||||
export type UmbMultipleAppLanguageConditionConfig = UmbConditionConfigBase;
|
||||
|
||||
export class UmbMultipleAppLanguageCondition
|
||||
extends UmbConditionBase<UmbMultipleAppLanguageConditionConfig>
|
||||
implements UmbExtensionCondition
|
||||
{
|
||||
constructor(host: UmbControllerHost, args: UmbConditionControllerArguments<UmbMultipleAppLanguageConditionConfig>) {
|
||||
super(host, args);
|
||||
|
||||
this.consumeContext(UMB_APP_LANGUAGE_CONTEXT, (context) => {
|
||||
this.observe(
|
||||
context.moreThanOneLanguage,
|
||||
(moreThanOneLanguage) => {
|
||||
this.permitted = moreThanOneLanguage;
|
||||
},
|
||||
'observeLanguages',
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const manifest: ManifestCondition = {
|
||||
type: 'condition',
|
||||
name: 'Multiple App Languages Condition',
|
||||
alias: 'Umb.Condition.MultipleAppLanguages',
|
||||
api: UmbMultipleAppLanguageCondition,
|
||||
};
|
||||
@@ -24,9 +24,13 @@ export class UmbSectionAliasCondition
|
||||
|
||||
if (permissionCheck !== undefined) {
|
||||
this.consumeContext(UMB_SECTION_CONTEXT, (context) => {
|
||||
this.observe(context.alias, (sectionAlias) => {
|
||||
this.permitted = sectionAlias ? permissionCheck!(sectionAlias) : false;
|
||||
});
|
||||
this.observe(
|
||||
context.alias,
|
||||
(sectionAlias) => {
|
||||
this.permitted = sectionAlias ? permissionCheck!(sectionAlias) : false;
|
||||
},
|
||||
'observeAlias',
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { CollectionBulkActionPermissionConditionConfig } from '../../collec
|
||||
import type { UmbSectionUserPermissionConditionConfig } from '../../section/conditions/index.js';
|
||||
import type { SectionAliasConditionConfig } from './section-alias.condition.js';
|
||||
import type { SwitchConditionConfig } from './switch.condition.js';
|
||||
import type { UmbMultipleAppLanguageConditionConfig } from './multiple-app-languages.condition.js';
|
||||
import type {
|
||||
WorkspaceAliasConditionConfig,
|
||||
WorkspaceEntityTypeConditionConfig,
|
||||
@@ -29,8 +30,9 @@ export type ConditionTypes =
|
||||
| CollectionBulkActionPermissionConditionConfig
|
||||
| SectionAliasConditionConfig
|
||||
| SwitchConditionConfig
|
||||
| UmbConditionConfigBase
|
||||
| UmbDocumentUserPermissionConditionConfig
|
||||
| UmbMultipleAppLanguageConditionConfig
|
||||
| UmbSectionUserPermissionConditionConfig
|
||||
| WorkspaceAliasConditionConfig
|
||||
| WorkspaceEntityTypeConditionConfig
|
||||
| UmbConditionConfigBase;
|
||||
| WorkspaceEntityTypeConditionConfig;
|
||||
|
||||
@@ -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> = [];
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { UmbOEmbedRepository } from './oembed.repository.js';
|
||||
import type { ManifestRepository, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const UMB_OEMBED_REPOSITORY_ALIAS = 'Umb.Repository.OEmbed';
|
||||
@@ -7,7 +6,7 @@ const repository: ManifestRepository = {
|
||||
type: 'repository',
|
||||
alias: UMB_OEMBED_REPOSITORY_ALIAS,
|
||||
name: 'OEmbed Repository',
|
||||
api: UmbOEmbedRepository,
|
||||
api: () => import('./oembed.repository.js'),
|
||||
};
|
||||
|
||||
export const manifests: Array<ManifestTypes> = [repository];
|
||||
|
||||
@@ -18,3 +18,5 @@ export class UmbOEmbedRepository extends UmbControllerBase implements UmbApi {
|
||||
return { error };
|
||||
}
|
||||
}
|
||||
|
||||
export { UmbOEmbedRepository as api };
|
||||
|
||||
@@ -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)}>
|
||||
|
||||
@@ -212,7 +212,7 @@ export class UmbWorkspaceSplitViewVariantSelectorElement extends UmbLitElement {
|
||||
return html`
|
||||
<uui-input
|
||||
id="name-input"
|
||||
label="Document name (TODO: Localize)"
|
||||
label=${this.localize.term('placeholders_entername')}
|
||||
.value=${this._name ?? ''}
|
||||
@input=${this.#handleInput}
|
||||
${umbFocus()}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { UmbDataPathPropertyValueFilter } from '@umbraco-cms/backoffice/validati
|
||||
*/
|
||||
@customElement('umb-property-editor-config')
|
||||
export class UmbPropertyEditorConfigElement extends UmbLitElement {
|
||||
// TODO: Make this element generic, so its not bound to DATA-TYPEs. This will require moving some functionality of Data-Type-Context to this. and this might need to self provide a variant Context for its inner property editor UIs.
|
||||
// TODO: Make this element generic, so its not bound to DATA-TYPEs. This will require moving some functionality of Data-Type-Context to this. and this might need to self provide a variant Context for its inner property editor UIs. [NL]
|
||||
#workspaceContext?: typeof UMB_DATA_TYPE_WORKSPACE_CONTEXT.TYPE;
|
||||
|
||||
@state()
|
||||
@@ -53,7 +53,9 @@ export class UmbPropertyEditorConfigElement extends UmbLitElement {
|
||||
property-editor-ui-alias=${property.propertyEditorUiAlias}
|
||||
.config=${property.config}></umb-property>`,
|
||||
)
|
||||
: html`<div>No configuration</div>`;
|
||||
: html`<umb-localize key="editdatatype_noConfiguration"
|
||||
>There is no configuration for this property editor.</umb-localize
|
||||
>`;
|
||||
}
|
||||
|
||||
static override styles = [UmbTextStyles];
|
||||
|
||||
@@ -30,6 +30,24 @@ import {
|
||||
} from '@umbraco-cms/backoffice/entity-action';
|
||||
|
||||
type EntityType = UmbDataTypeDetailModel;
|
||||
|
||||
/**
|
||||
* @class UmbDataTypeWorkspaceContext
|
||||
* @description - Context for handling data type workspace
|
||||
* There is two overall code flows to be aware about:
|
||||
*
|
||||
* propertyEditorUiAlias is observed
|
||||
* loads propertyEditorUi manifest
|
||||
* then the propertyEditorSchemaAlias is set to what the UI is configured for.
|
||||
*
|
||||
* propertyEditorSchemaAlias is observed
|
||||
* loads the propertyEditorSchema manifest
|
||||
* if no UI is defined then the propertyEditorSchema manifest default ui is set for the propertyEditorUiAlias.
|
||||
*
|
||||
* This supports two cases:
|
||||
* - when editing an existing data type that only has a schema alias set, then it gets the UI set.
|
||||
* - a new property editor ui is picked for a data-type, uses the data-type configuration to set the schema, if such is configured for the Property Editor UI. (The user picks the UI via the UI, the schema comes from the UI that the user picked, we store both on the data-type)
|
||||
*/
|
||||
export class UmbDataTypeWorkspaceContext
|
||||
extends UmbSubmittableWorkspaceContextBase<EntityType>
|
||||
implements UmbInvariantDatasetWorkspaceContext, UmbRoutableWorkspaceContext
|
||||
@@ -72,8 +90,6 @@ export class UmbDataTypeWorkspaceContext
|
||||
|
||||
#settingsDefaultData?: Array<PropertyEditorSettingsDefaultData>;
|
||||
|
||||
#propertyEditorUISettingsSchemaAlias?: string;
|
||||
|
||||
#propertyEditorUiIcon = new UmbStringState<string | null>(null);
|
||||
readonly propertyEditorUiIcon = this.#propertyEditorUiIcon.asObservable();
|
||||
|
||||
@@ -82,6 +98,8 @@ export class UmbDataTypeWorkspaceContext
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, 'Umb.Workspace.DataType');
|
||||
|
||||
this.#observePropertyEditorSchemaAlias();
|
||||
this.#observePropertyEditorUIAlias();
|
||||
|
||||
this.routes.setRoutes([
|
||||
@@ -121,7 +139,7 @@ export class UmbDataTypeWorkspaceContext
|
||||
this.#propertyEditorUISettingsDefaultData = [];
|
||||
this.#settingsDefaultData = undefined;
|
||||
|
||||
this._mergeConfigProperties();
|
||||
this.#mergeConfigProperties();
|
||||
}
|
||||
|
||||
// Hold the last set property editor ui alias, so we know when it changes, so we can reset values. [NL]
|
||||
@@ -131,30 +149,13 @@ export class UmbDataTypeWorkspaceContext
|
||||
this.observe(
|
||||
this.propertyEditorUiAlias,
|
||||
async (propertyEditorUiAlias) => {
|
||||
const previousPropertyEditorUIAlias = this.#lastPropertyEditorUIAlias;
|
||||
this.#lastPropertyEditorUIAlias = propertyEditorUiAlias;
|
||||
this.#propertyEditorUISettingsProperties = [];
|
||||
this.#propertyEditorUISettingsDefaultData = [];
|
||||
|
||||
// we only want to react on the change if the alias is set or null. When it is undefined something is still loading
|
||||
if (propertyEditorUiAlias === undefined) return;
|
||||
|
||||
// if the property editor ui alias is not set, we use the default alias from the schema
|
||||
if (propertyEditorUiAlias === null) {
|
||||
await this.#observePropertyEditorSchemaAlias();
|
||||
if (this.#propertyEditorSchemaConfigDefaultUIAlias !== null) {
|
||||
this.setPropertyEditorUiAlias(this.#propertyEditorSchemaConfigDefaultUIAlias);
|
||||
}
|
||||
} else {
|
||||
await this.#setPropertyEditorUIConfig(propertyEditorUiAlias);
|
||||
this.setPropertyEditorSchemaAlias(this.#propertyEditorUISettingsSchemaAlias!);
|
||||
await this.#observePropertyEditorSchemaAlias();
|
||||
}
|
||||
|
||||
if (
|
||||
this.getIsNew() ||
|
||||
(previousPropertyEditorUIAlias && previousPropertyEditorUIAlias !== propertyEditorUiAlias)
|
||||
) {
|
||||
this.#transferConfigDefaultData();
|
||||
}
|
||||
this._mergeConfigProperties();
|
||||
this.#observePropertyEditorUIManifest(propertyEditorUiAlias);
|
||||
},
|
||||
'editorUiAlias',
|
||||
);
|
||||
@@ -164,13 +165,19 @@ export class UmbDataTypeWorkspaceContext
|
||||
return this.observe(
|
||||
this.propertyEditorSchemaAlias,
|
||||
(propertyEditorSchemaAlias) => {
|
||||
this.#setPropertyEditorSchemaConfig(propertyEditorSchemaAlias);
|
||||
this.#propertyEditorSchemaSettingsProperties = [];
|
||||
this.#propertyEditorSchemaSettingsDefaultData = [];
|
||||
this.#observePropertyEditorSchemaManifest(propertyEditorSchemaAlias);
|
||||
},
|
||||
'schemaAlias',
|
||||
).asPromise();
|
||||
);
|
||||
}
|
||||
|
||||
#setPropertyEditorSchemaConfig(propertyEditorSchemaAlias?: string) {
|
||||
#observePropertyEditorSchemaManifest(propertyEditorSchemaAlias?: string) {
|
||||
if (!propertyEditorSchemaAlias) {
|
||||
this.removeUmbControllerByAlias('schema');
|
||||
return;
|
||||
}
|
||||
this.observe(
|
||||
propertyEditorSchemaAlias
|
||||
? umbExtensionsRegistry.byTypeAndAlias('propertyEditorSchema', propertyEditorSchemaAlias)
|
||||
@@ -183,36 +190,56 @@ export class UmbDataTypeWorkspaceContext
|
||||
}));
|
||||
this.#propertyEditorSchemaSettingsDefaultData = manifest?.meta.settings?.defaultData || [];
|
||||
this.#propertyEditorSchemaConfigDefaultUIAlias = manifest?.meta.defaultPropertyEditorUiAlias || null;
|
||||
if (this.#propertyEditorSchemaConfigDefaultUIAlias && this.getPropertyEditorUiAlias() === null) {
|
||||
// Fallback to the default property editor ui for this property editor schema.
|
||||
this.setPropertyEditorUiAlias(this.#propertyEditorSchemaConfigDefaultUIAlias);
|
||||
}
|
||||
this.#mergeConfigProperties();
|
||||
},
|
||||
'schema',
|
||||
);
|
||||
}
|
||||
|
||||
#setPropertyEditorUIConfig(propertyEditorUIAlias: string) {
|
||||
return this.observe(
|
||||
#observePropertyEditorUIManifest(propertyEditorUIAlias: string | null) {
|
||||
if (!propertyEditorUIAlias) {
|
||||
this.removeUmbControllerByAlias('editorUi');
|
||||
return;
|
||||
}
|
||||
this.observe(
|
||||
umbExtensionsRegistry.byTypeAndAlias('propertyEditorUi', propertyEditorUIAlias),
|
||||
(manifest) => {
|
||||
this.#propertyEditorUiIcon.setValue(manifest?.meta.icon || null);
|
||||
this.#propertyEditorUiName.setValue(manifest?.name || null);
|
||||
|
||||
this.#propertyEditorUISettingsSchemaAlias = manifest?.meta.propertyEditorSchemaAlias;
|
||||
// Maps properties to have a weight, so they can be sorted, notice UI properties have a +1000 weight compared to schema properties.
|
||||
this.#propertyEditorUISettingsProperties = (manifest?.meta.settings?.properties ?? []).map((x, i) => ({
|
||||
...x,
|
||||
weight: x.weight ?? 1000 + i,
|
||||
}));
|
||||
this.#propertyEditorUISettingsDefaultData = manifest?.meta.settings?.defaultData || [];
|
||||
this.setPropertyEditorSchemaAlias(manifest?.meta.propertyEditorSchemaAlias);
|
||||
this.#mergeConfigProperties();
|
||||
},
|
||||
'editorUi',
|
||||
).asPromise();
|
||||
);
|
||||
}
|
||||
|
||||
private _mergeConfigProperties() {
|
||||
#mergeConfigProperties() {
|
||||
if (this.#propertyEditorSchemaSettingsProperties && this.#propertyEditorUISettingsProperties) {
|
||||
// Reset the value to this array, and then afterwards append:
|
||||
this.#properties.setValue(this.#propertyEditorSchemaSettingsProperties);
|
||||
// Append the UI settings properties to the schema properties, so they can override the schema properties:
|
||||
this.#properties.append(this.#propertyEditorUISettingsProperties);
|
||||
|
||||
// If new or if the alias was changed then set default values. This 'complexity' to prevent setting default data when initialized [NL]
|
||||
const previousPropertyEditorUIAlias = this.#lastPropertyEditorUIAlias;
|
||||
this.#lastPropertyEditorUIAlias = this.getPropertyEditorUiAlias();
|
||||
if (
|
||||
this.getIsNew() ||
|
||||
(previousPropertyEditorUIAlias && previousPropertyEditorUIAlias !== this.#lastPropertyEditorUIAlias)
|
||||
) {
|
||||
this.#transferConfigDefaultData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,9 +328,15 @@ export class UmbDataTypeWorkspaceContext
|
||||
this.#currentData.update({ name });
|
||||
}
|
||||
|
||||
getPropertyEditorSchemaAlias() {
|
||||
return this.#currentData.getValue()?.editorAlias;
|
||||
}
|
||||
setPropertyEditorSchemaAlias(alias?: string) {
|
||||
this.#currentData.update({ editorAlias: alias });
|
||||
}
|
||||
getPropertyEditorUiAlias() {
|
||||
return this.#currentData.getValue()?.editorUiAlias;
|
||||
}
|
||||
setPropertyEditorUiAlias(alias?: string) {
|
||||
this.#currentData.update({ editorUiAlias: alias });
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -45,7 +45,7 @@ export class UmbPropertyEditorUIDocumentTypePickerElement extends UmbLitElement
|
||||
.min=${this.min}
|
||||
.max=${this.max}
|
||||
.value=${this.value}
|
||||
?elementTypesOnly=${this.onlyElementTypes}
|
||||
.elementTypesOnly=${this.onlyElementTypes ?? false}
|
||||
?showOpenButton=${this.showOpenButton}
|
||||
@change=${this.#onChange}>
|
||||
</umb-input-document-type>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -12,6 +12,9 @@ const entityActions: Array<ManifestSectionSidebarApp> = [
|
||||
alias: 'Umb.Condition.SectionAlias',
|
||||
match: 'Umb.Section.Content',
|
||||
},
|
||||
{
|
||||
alias: 'Umb.Condition.MultipleAppLanguages',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -12,6 +12,7 @@ import { UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth';
|
||||
export class UmbAppLanguageContext extends UmbContextBase<UmbAppLanguageContext> implements UmbApi {
|
||||
#languageCollectionRepository: UmbLanguageCollectionRepository;
|
||||
#languages = new UmbArrayState<UmbLanguageDetailModel>([], (x) => x.unique);
|
||||
moreThanOneLanguage = this.#languages.asObservablePart((x) => x.length > 1);
|
||||
|
||||
#appLanguage = new UmbObjectState<UmbLanguageDetailModel | undefined>(undefined);
|
||||
appLanguage = this.#appLanguage.asObservable();
|
||||
|
||||
@@ -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 }),
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { html, nothing, customElement, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
|
||||
@customElement('umb-webhook-table-boolean-column-layout')
|
||||
@@ -7,7 +7,7 @@ export class UmbWebhookTableBooleanColumnLayoutElement extends UmbLitElement {
|
||||
value = false;
|
||||
|
||||
override render() {
|
||||
return this.value ? html`<uui-icon name="icon-check"></uui-icon>` : nothing;
|
||||
return html`<uui-icon name="${this.value ? 'check' : 'remove'}"></uui-icon>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ export class UmbWebhookTableContentTypeColumnLayoutElement extends UmbLitElement
|
||||
|
||||
if (this.value?.contentTypeName && this.#repository) {
|
||||
const { data } = await this.#repository.requestItems(this.value.contentTypes);
|
||||
this._contentTypes = data?.map((item) => item.name).join(', ') ?? '';
|
||||
this._contentTypes = data?.map((item) => this.localize.string(item.name)).join(', ') ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import type { UmbWebhookDetailModel } from '../../../types.js';
|
||||
import type { UmbDefaultCollectionContext } from '@umbraco-cms/backoffice/collection';
|
||||
import { css, customElement, html, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UMB_COLLECTION_CONTEXT } from '@umbraco-cms/backoffice/collection';
|
||||
import type { UmbTableColumn, UmbTableConfig, UmbTableItem } from '@umbraco-cms/backoffice/components';
|
||||
import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
|
||||
import './column-layouts/boolean/webhook-table-boolean-column-layout.element.js';
|
||||
import './column-layouts/name/webhook-table-name-column-layout.element.js';
|
||||
@@ -21,25 +20,25 @@ export class UmbWebhookTableCollectionViewElement extends UmbLitElement {
|
||||
@state()
|
||||
private _tableColumns: Array<UmbTableColumn> = [
|
||||
{
|
||||
name: 'Name',
|
||||
name: this.localize.term('general_name'),
|
||||
alias: 'name',
|
||||
elementName: 'umb-webhook-table-name-column-layout',
|
||||
},
|
||||
{
|
||||
name: 'Enabled',
|
||||
name: this.localize.term('webhooks_enabled'),
|
||||
alias: 'enabled',
|
||||
elementName: 'umb-webhook-table-boolean-column-layout',
|
||||
},
|
||||
{
|
||||
name: 'URL',
|
||||
name: this.localize.term('webhooks_url'),
|
||||
alias: 'url',
|
||||
},
|
||||
{
|
||||
name: 'Events',
|
||||
name: this.localize.term('webhooks_events'),
|
||||
alias: 'events',
|
||||
},
|
||||
{
|
||||
name: 'Types',
|
||||
name: this.localize.term('webhooks_types'),
|
||||
alias: 'types',
|
||||
elementName: 'umb-webhook-table-content-type-column-layout',
|
||||
},
|
||||
@@ -112,7 +111,6 @@ export class UmbWebhookTableCollectionViewElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
UmbTextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: flex;
|
||||
|
||||
@@ -47,23 +47,29 @@ export class UmbInputWebhookEventsElement extends UmbLitElement {
|
||||
if (!this.events.length) return nothing;
|
||||
|
||||
return html`
|
||||
${repeat(
|
||||
this.events,
|
||||
(item) => item.alias,
|
||||
(item) => html`
|
||||
<span>${item.eventName}</span>
|
||||
<uui-button
|
||||
label=${this.localize.term('general_remove')}
|
||||
@click=${() => this.#removeEvent(item.alias)}></uui-button>
|
||||
`,
|
||||
)}
|
||||
<uui-ref-list>
|
||||
${repeat(
|
||||
this.events,
|
||||
(item) => item.alias,
|
||||
(item) => html`
|
||||
<uui-ref-node name=${item.eventName} @open=${this.#openModal}>
|
||||
<umb-icon slot="icon" name="icon-globe"></umb-icon>
|
||||
<uui-action-bar slot="actions">
|
||||
<uui-button
|
||||
label=${this.localize.term('general_remove')}
|
||||
@click=${() => this.#removeEvent(item.alias)}></uui-button>
|
||||
</uui-action-bar>
|
||||
</uui-ref-node>
|
||||
`,
|
||||
)}
|
||||
</uui-ref-list>
|
||||
`;
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`${this.#renderEvents()}
|
||||
<uui-button
|
||||
id="choose"
|
||||
id="btn-add"
|
||||
look="placeholder"
|
||||
label=${this.localize.term('general_choose')}
|
||||
@click=${this.#openModal}></uui-button>`;
|
||||
@@ -72,15 +78,8 @@ export class UmbInputWebhookEventsElement extends UmbLitElement {
|
||||
static override styles = [
|
||||
UmbTextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: var(--uui-size-space-2) var(--uui-size-space-2);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#choose {
|
||||
grid-column: -1 / 1;
|
||||
#btn-add {
|
||||
display: block;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -14,7 +14,7 @@ export class UmbInputWebhookHeadersElement extends UmbLitElement {
|
||||
private _headers: Array<{ name: string; value: string }> = [];
|
||||
|
||||
@state()
|
||||
private _headerNames: string[] = ['Accept', 'Content-Type', 'User-Agent', 'Content-Length'];
|
||||
private _headerNames: string[] = ['Accept', 'Content-Length', 'Content-Type', 'User-Agent'];
|
||||
|
||||
get #filterHeaderNames() {
|
||||
return this._headerNames.filter((name) => !this._headers.find((header) => header.name === name));
|
||||
@@ -78,7 +78,7 @@ export class UmbInputWebhookHeadersElement extends UmbLitElement {
|
||||
.value=${header.value}
|
||||
@input=${(e: InputEvent) => this.#onInput(e, 'value', index)}
|
||||
list="valueList" />
|
||||
<uui-button @click=${() => this.#removeHeader(index)} label="Remove"></uui-button>
|
||||
<uui-button @click=${() => this.#removeHeader(index)} label=${this.localize.term('general_remove')}></uui-button>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -105,8 +105,8 @@ export class UmbInputWebhookHeadersElement extends UmbLitElement {
|
||||
if (!this._headers.length) return nothing;
|
||||
|
||||
return html`
|
||||
<span class="grid-top">KEY</span>
|
||||
<span class="grid-top">VALUE</span>
|
||||
<span class="grid-top"><umb-localize key="general_name">Name</umb-localize></span>
|
||||
<span class="grid-top"><umb-localize key="general_value">Value</umb-localize></span>
|
||||
<span class="grid-top"></span>
|
||||
${repeat(
|
||||
this._headers,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { UmbWebhookEventRepository } from '../../repository/event/webhook-event.repository.js';
|
||||
import type { UmbWebhookEventModel } from '../../types.js';
|
||||
import type { UmbWebhookPickerModalData, UmbWebhookPickerModalValue } from './webhook-events-modal.token.js';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { css, html, customElement, state, repeat } from '@umbraco-cms/backoffice/external/lit';
|
||||
|
||||
import { customElement, html, state, repeat } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils';
|
||||
|
||||
@@ -67,32 +65,36 @@ export class UmbWebhookEventsModalElement extends UmbModalBaseElement<
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`<umb-body-layout headline="Select events">
|
||||
<uui-box>
|
||||
${repeat(
|
||||
this._events,
|
||||
(item) => item.alias,
|
||||
(item) => html`
|
||||
<uui-menu-item
|
||||
label=${item.eventName}
|
||||
?disabled=${this.#getItemDisabled(item)}
|
||||
selectable
|
||||
@selected=${() => this.#selectionManager.select(item.alias)}
|
||||
@deselected=${() => this.#selectionManager.deselect(item.alias)}
|
||||
?selected=${this.value.events.includes(item)}></uui-menu-item>
|
||||
<uui-icon slot="icon" name="icon-globe"></uui-icon>
|
||||
</uui-menu-item>
|
||||
`,
|
||||
)}
|
||||
</uui-box>
|
||||
<div slot="actions">
|
||||
<uui-button label="Close" @click=${this.#close}></uui-button>
|
||||
<uui-button label="Submit" look="primary" color="positive" @click=${this.#submit}></uui-button>
|
||||
</div>
|
||||
</umb-body-layout> `;
|
||||
return html`
|
||||
<umb-body-layout headline=${this.localize.term('webhooks_selectEvents')}>
|
||||
<uui-box>
|
||||
${repeat(
|
||||
this._events,
|
||||
(item) => item.alias,
|
||||
(item) => html`
|
||||
<uui-menu-item
|
||||
label=${item.eventName}
|
||||
?disabled=${this.#getItemDisabled(item)}
|
||||
selectable
|
||||
@selected=${() => this.#selectionManager.select(item.alias)}
|
||||
@deselected=${() => this.#selectionManager.deselect(item.alias)}
|
||||
?selected=${this.value.events.includes(item)}>
|
||||
<uui-icon slot="icon" name="icon-globe"></uui-icon>
|
||||
</uui-menu-item>
|
||||
`,
|
||||
)}
|
||||
</uui-box>
|
||||
<div slot="actions">
|
||||
<uui-button label=${this.localize.term('general_cancel')} @click=${this.#close}></uui-button>
|
||||
<uui-button
|
||||
label=${this.localize.term('general_submit')}
|
||||
look="primary"
|
||||
color="positive"
|
||||
@click=${this.#submit}></uui-button>
|
||||
</div>
|
||||
</umb-body-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
static override styles = [UmbTextStyles, css``];
|
||||
}
|
||||
|
||||
export default UmbWebhookEventsModalElement;
|
||||
|
||||
@@ -5,9 +5,11 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
@customElement('umb-webhook-root-workspace')
|
||||
export class UmbWebhookRootWorkspaceElement extends UmbLitElement {
|
||||
override render() {
|
||||
return html` <umb-body-layout main-no-padding headline="Webhooks">
|
||||
<umb-collection alias=${UMB_WEBHOOK_COLLECTION_ALIAS}></umb-collection>;
|
||||
</umb-body-layout>`;
|
||||
return html`
|
||||
<umb-body-layout main-no-padding headline=${this.localize.term('treeHeaders_webhooks')}>
|
||||
<umb-collection alias=${UMB_WEBHOOK_COLLECTION_ALIAS}></umb-collection>;
|
||||
</umb-body-layout>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import type { UmbInputWebhookHeadersElement } from '../../../components/input-webhook-headers.element.js';
|
||||
import { UMB_WEBHOOK_WORKSPACE_CONTEXT } from '../webhook-workspace.context-token.js';
|
||||
import type { UmbInputWebhookHeadersElement } from '../../../components/input-webhook-headers.element.js';
|
||||
import type { UmbInputWebhookEventsElement } from '../../../components/input-webhook-events.element.js';
|
||||
import { css, html, customElement, state, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { css, customElement, html, state, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import '@umbraco-cms/backoffice/culture';
|
||||
import type { UmbWebhookDetailModel } from '@umbraco-cms/backoffice/webhook';
|
||||
|
||||
import type { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
import type { UmbInputDocumentTypeElement } from '@umbraco-cms/backoffice/document-type';
|
||||
import type { UmbWebhookDetailModel } from '@umbraco-cms/backoffice/webhook';
|
||||
import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type { UUIBooleanInputEvent, UUIInputEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
|
||||
import '@umbraco-cms/backoffice/culture';
|
||||
import '../../../components/input-webhook-headers.element.js';
|
||||
import '../../../components/input-webhook-events.element.js';
|
||||
|
||||
@@ -43,9 +42,9 @@ export class UmbWebhookDetailsWorkspaceViewElement extends UmbLitElement impleme
|
||||
});
|
||||
}
|
||||
|
||||
#onEventsChange(event: UmbChangeEvent) {
|
||||
const events = (event.target as UmbInputWebhookEventsElement).events;
|
||||
if (events[0].eventType !== this.contentType) {
|
||||
#onEventsChange(event: UmbChangeEvent & { target: UmbInputWebhookEventsElement }) {
|
||||
const events = event.target.events ?? [];
|
||||
if (events.length && events[0].eventType !== this.contentType) {
|
||||
this.#webhookWorkspaceContext?.setTypes([]);
|
||||
}
|
||||
this.#webhookWorkspaceContext?.setEvents(events);
|
||||
@@ -75,7 +74,9 @@ export class UmbWebhookDetailsWorkspaceViewElement extends UmbLitElement impleme
|
||||
if (this.contentType !== 'Content' && this.contentType !== 'Media') return nothing;
|
||||
|
||||
return html`
|
||||
<umb-property-layout label="Content Type" description="Only trigger the webhook for a specific content type.">
|
||||
<umb-property-layout
|
||||
label=${this.localize.term('webhooks_contentType')}
|
||||
description=${this.localize.term('webhooks_contentTypeDescription')}>
|
||||
${this.#renderContentTypePickerEditor()}
|
||||
</umb-property-layout>
|
||||
`;
|
||||
@@ -84,17 +85,20 @@ export class UmbWebhookDetailsWorkspaceViewElement extends UmbLitElement impleme
|
||||
#renderContentTypePickerEditor() {
|
||||
switch (this.contentType) {
|
||||
case 'Content':
|
||||
return html`<umb-input-document-type
|
||||
@change=${this.#onTypesChange}
|
||||
.selection=${this._webhook?.contentTypes ?? []}
|
||||
slot="editor"
|
||||
?elementTypesOnly=${true}></umb-input-document-type>`;
|
||||
return html`
|
||||
<umb-input-document-type
|
||||
slot="editor"
|
||||
@change=${this.#onTypesChange}
|
||||
.selection=${this._webhook?.contentTypes ?? []}
|
||||
.documentTypesOnly=${true}></umb-input-document-type>
|
||||
`;
|
||||
case 'Media':
|
||||
return html`<umb-input-media-type
|
||||
@change=${this.#onTypesChange}
|
||||
.selection=${this._webhook?.contentTypes ?? []}
|
||||
slot="editor"
|
||||
?elementTypesOnly=${true}></umb-input-media-type>`;
|
||||
return html`
|
||||
<umb-input-media-type
|
||||
slot="editor"
|
||||
@change=${this.#onTypesChange}
|
||||
.selection=${this._webhook?.contentTypes ?? []}></umb-input-media-type>
|
||||
`;
|
||||
default:
|
||||
return nothing;
|
||||
}
|
||||
@@ -105,20 +109,28 @@ export class UmbWebhookDetailsWorkspaceViewElement extends UmbLitElement impleme
|
||||
|
||||
return html`
|
||||
<uui-box>
|
||||
<umb-property-layout label="Url" description="The url to call when the webhook is triggered.">
|
||||
<umb-property-layout
|
||||
label=${this.localize.term('webhooks_url')}
|
||||
description=${this.localize.term('webhooks_urlDescription')}>
|
||||
<uui-input @input=${this.#onUrlChange} .value=${this._webhook.url} slot="editor"></uui-input>
|
||||
</umb-property-layout>
|
||||
<umb-property-layout label="Events" description="The events for which the webhook should be triggered.">
|
||||
<umb-property-layout
|
||||
label=${this.localize.term('webhooks_events')}
|
||||
description=${this.localize.term('webhooks_eventDescription')}>
|
||||
<umb-input-webhook-events
|
||||
@change=${this.#onEventsChange}
|
||||
.events=${this._webhook.events ?? []}
|
||||
slot="editor"></umb-input-webhook-events>
|
||||
</umb-property-layout>
|
||||
${this.#renderContentTypePicker()}
|
||||
<umb-property-layout label="Enabled" description="Is the webhook enabled?">
|
||||
<uui-toggle slot="editor" .checked=${this._webhook.enabled} @input=${this.#onEnabledChange}></uui-toggle>
|
||||
<umb-property-layout
|
||||
label=${this.localize.term('webhooks_enabled')}
|
||||
description=${this.localize.term('webhooks_enabledDescription')}>
|
||||
<uui-toggle slot="editor" .checked=${this._webhook.enabled} @change=${this.#onEnabledChange}></uui-toggle>
|
||||
</umb-property-layout>
|
||||
<umb-property-layout label="Headers" description="Custom headers to include in the webhook request.">
|
||||
<umb-property-layout
|
||||
label=${this.localize.term('webhooks_headers')}
|
||||
description=${this.localize.term('webhooks_headersDescription')}>
|
||||
<umb-input-webhook-headers
|
||||
@change=${this.#onHeadersChange}
|
||||
.headers=${this._webhook.headers}
|
||||
|
||||
@@ -4,9 +4,11 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
@customElement('umb-webhook-workspace-editor')
|
||||
export class UmbWebhookWorkspaceEditorElement extends UmbLitElement {
|
||||
override render() {
|
||||
return html`<umb-workspace-editor
|
||||
alias="Umb.Workspace.Webhook"
|
||||
back-path="section/settings/workspace/webhook-root"></umb-workspace-editor>`;
|
||||
return html`
|
||||
<umb-workspace-editor
|
||||
alias="Umb.Workspace.Webhook"
|
||||
back-path="section/settings/workspace/webhook-root"></umb-workspace-editor>
|
||||
`;
|
||||
}
|
||||
|
||||
static override styles = [UmbTextStyles];
|
||||
|
||||
Reference in New Issue
Block a user