diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/views/design/workspace-view-document-type-design.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/views/design/workspace-view-document-type-design.element.ts
index 2b50b0310e..a27bfb57fb 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/views/design/workspace-view-document-type-design.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/views/design/workspace-view-document-type-design.element.ts
@@ -123,8 +123,9 @@ export class UmbWorkspaceViewDocumentTypeDesignElement extends UmbLitElement {
-
-
+
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/views/templates/workspace-view-document-type-templates.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/views/templates/workspace-view-document-type-templates.element.ts
index 99fbdacaa7..cc89db1358 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/views/templates/workspace-view-document-type-templates.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/views/templates/workspace-view-document-type-templates.element.ts
@@ -1,10 +1,9 @@
import { css, html } from 'lit';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
-import { customElement, property, state } from 'lit/decorators.js';
+import { customElement, state } from 'lit/decorators.js';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { DocumentTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbWorkspaceDocumentTypeContext } from '../../document-type-workspace.context';
-
import '../../../../../shared/property-creator/property-creator.element.ts';
@customElement('umb-workspace-view-document-type-templates')
@@ -37,24 +36,13 @@ export class UmbWorkspaceViewDocumentTypeTemplatesElement extends UmbLitElement
`,
];
- @property()
- defaultTemplateKey?: string = '123';
-
@state()
_documentType?: DocumentTypeResponseModel;
- @state()
- _templates = [
- { key: '123', name: 'Blog Post Page' },
- { key: '456', name: 'Blog Entry Page' },
- ];
-
private _workspaceContext?: UmbWorkspaceDocumentTypeContext;
constructor() {
super();
-
- // TODO: Figure out if this is the best way to consume the context or if it can be strongly typed with an UmbContextToken
this.consumeContext
('umbWorkspaceContext', (documentTypeContext) => {
this._workspaceContext = documentTypeContext;
this._observeDocumentType();
@@ -69,13 +57,14 @@ export class UmbWorkspaceViewDocumentTypeTemplatesElement extends UmbLitElement
});
}
- #changeDefaultTemplate(e: CustomEvent) {
- //this.defaultTemplateKey = (e.target as UmbTemplateCardElement).value as string;
- console.log('default template key', this.defaultTemplateKey);
+ async #changeDefaultKey(e: CustomEvent) {
+ // save new default key
+ console.log('workspace: default template key', e);
}
- #removeTemplate(key: string) {
- console.log('remove template', key);
+ #changeAllowedKeys(e: CustomEvent) {
+ // save new allowed keys
+ console.log('workspace: allowed templates changed', e);
}
render() {
@@ -83,7 +72,11 @@ export class UmbWorkspaceViewDocumentTypeTemplatesElement extends UmbLitElement
Choose which templates editors are allowed to use on content of this type
-
+
`;
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-template-picker/input-template-picker.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-template-picker/input-template-picker.element.ts
index 8311ae6099..828f13bfe1 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-template-picker/input-template-picker.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-template-picker/input-template-picker.element.ts
@@ -1,15 +1,14 @@
import { css, html } from 'lit';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
-import { customElement, property, queryAll, state } from 'lit/decorators.js';
-import { repeat } from 'lit/directives/repeat.js';
+import { customElement, property, state } from 'lit/decorators.js';
import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins';
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
-
-import { TemplateResource } from '@umbraco-cms/backoffice/backend-api';
-import { UMB_CONFIRM_MODAL_TOKEN } from '../../modals/confirm';
+import { TemplateResource, TemplateResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbTemplateCardElement } from '../template-card/template-card.element';
+import { UMB_TEMPLATE_PICKER_MODAL_TOKEN } from '../../modals/template-picker';
+import { UMB_TEMPLATE_MODAL_TOKEN } from '../../modals/template';
@customElement('umb-input-template-picker')
export class UmbInputTemplatePickerElement extends FormControlMixin(UmbLitElement) {
@@ -31,19 +30,6 @@ export class UmbInputTemplatePickerElement extends FormControlMixin(UmbLitElemen
min-width: 180px;
min-height: 150px;
}
-
- .fade-in {
- animation: fadeIn 1s;
- }
-
- @keyframes fadeIn {
- 0% {
- opacity: 0;
- }
- 100% {
- opacity: 1;
- }
- }
`,
];
/**
@@ -82,30 +68,43 @@ export class UmbInputTemplatePickerElement extends FormControlMixin(UmbLitElemen
@property({ type: String, attribute: 'min-message' })
maxMessage = 'This field exceeds the allowed amount of items';
- @state()
- private _items: Array = [
- { key: '2bf464b6-3aca-4388-b043-4eb439cc2643', name: 'Doc 1', default: false },
- { key: '9a84c0b3-03b4-4dd4-84ac-706740ac0f71', name: 'Test', default: true },
- ];
+ _allowedKeys: Array = [];
+ @property({ type: Array })
+ public get allowedKeys() {
+ return this._allowedKeys;
+ }
+ public set allowedKeys(newKeys: Array) {
+ //this.#observePickedTemplates();
+ this._allowedKeys = newKeys;
+ }
+
+ _defaultKey = '';
+ @property({ type: String })
+ public get defaultKey(): string {
+ return this._defaultKey;
+ }
+ public set defaultKey(newKey: string) {
+ this._defaultKey = newKey;
+ super.value = newKey;
+ }
private _modalContext?: UmbModalContext;
- //private _documentStore?: UmbDocumentTreeStore;
- //private _pickedItemsObserver?: UmbObserverController;
+ //private _templateStore?: UmbTemplateTreeStore;
+ //private _pickedItemsObserver?: UmbObserverController;
+
+ @state()
+ _templates: TemplateResponseModel[] = [];
+
+ public get templates(): TemplateResponseModel[] {
+ return this._templates;
+ }
+ public set templates(newTemplates: TemplateResponseModel[]) {
+ this._templates = newTemplates;
+ this.allowedKeys = newTemplates.map((template) => template.key ?? '');
+ }
constructor() {
super();
-
- this.addValidator(
- 'rangeUnderflow',
- () => this.minMessage,
- () => !!this.min && this._items.length < this.min
- );
- this.addValidator(
- 'rangeOverflow',
- () => this.maxMessage,
- () => !!this.max && this._items.length > this.max
- );
-
this.consumeContext(UMB_MODAL_CONTEXT_TOKEN, (instance) => {
this._modalContext = instance;
});
@@ -113,79 +112,83 @@ export class UmbInputTemplatePickerElement extends FormControlMixin(UmbLitElemen
connectedCallback(): void {
super.connectedCallback();
- this._items = this._items.sort((a, b) => b.default - a.default);
- this.#setup();
+ this.allowedKeys.forEach((key) => this.#setup(key));
}
- async #setup() {
- const templates = await tryExecuteAndNotify(this, TemplateResource.getTreeTemplateRoot({ skip: 0, take: 9999 }));
- console.log(templates);
+ async #setup(templateKey: string) {
+ const { data } = await tryExecuteAndNotify(this, TemplateResource.getTemplateByKey({ key: templateKey }));
+ if (!data) return;
+ this.templates = [...this.templates, data];
}
protected getFormElement() {
return undefined;
}
- #openTemplatePickerModal() {
- console.log('template picker modal');
- }
-
- #changeSelected() {
- console.log('selected');
- }
-
- /** Clicking the template card buttons */
-
#changeDefault(e: CustomEvent) {
- const key = (e.target as UmbTemplateCardElement).value;
+ e.stopPropagation();
+ const newKey = (e.target as UmbTemplateCardElement).value as string;
+ this.defaultKey = newKey;
+ this.dispatchEvent(new CustomEvent('change-default', { bubbles: true, composed: true }));
+ }
- const oldDefault = this._items.find((x) => x.default === true);
- const newDefault = this._items.find((x) => x.key === key);
-
- const items = this._items.map((item) => {
- if (item.default === true) return { ...newDefault, default: true };
- if (item.key === key) return { ...oldDefault, default: false };
- return item;
+ #openPicker() {
+ //TODO: Tree-picker modal?
+ const modalHandler = this._modalContext?.open(UMB_TEMPLATE_PICKER_MODAL_TOKEN, {
+ multiple: true,
+ selection: [...this.allowedKeys],
});
- this._items = items;
- }
-
- #openTemplate(e: CustomEvent) {
- const key = (e.target as UmbTemplateCardElement).value;
- console.log('open', key);
+ modalHandler?.onSubmit().then((data) => {
+ console.log(data.selection);
+ this.dispatchEvent(new CustomEvent('change-allowed', { bubbles: true, composed: true }));
+ });
}
#removeTemplate(key: string) {
- console.log('remove', key);
+ console.log('picker: remove', key);
+ const templateIndex = this.templates.findIndex((x) => x.key === key);
+ this.templates.splice(templateIndex, 1);
+ this.templates = [...this._templates];
}
render() {
return html`
- ${repeat(
- this._items,
- (template) => template.default,
- (template, index) => html`
+ ${this.templates.map(
+ (template) => html`
+ ?default="${template.key === this.defaultKey}">
this.#removeTemplate(template.key)}"
+ @click="${() => this.#removeTemplate(template.key ?? '')}"
compact>
-
`
+ `
)}
- Add
+ Add
`;
}
+
+ #openTemplate(e: CustomEvent) {
+ const key = (e.target as UmbTemplateCardElement).value;
+
+ const modalHandler = this._modalContext?.open(UMB_TEMPLATE_MODAL_TOKEN, {
+ multiple: true,
+ selection: [...this.allowedKeys],
+ });
+
+ modalHandler?.onSubmit().then((res) => {
+ console.log('save template');
+ });
+ }
}
export default UmbInputTemplatePickerElement;
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/template-card/template-card.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/template-card/template-card.element.ts
index c971f22dbb..50f8cc5484 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/template-card/template-card.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/template-card/template-card.element.ts
@@ -139,7 +139,7 @@ export class UmbTemplateCardElement extends FormControlMixin(UmbLitElement) {
e.preventDefault();
e.stopPropagation();
//this.selected = true;
- this.dispatchEvent(new CustomEvent('default-change', { bubbles: true, composed: true }));
+ this.dispatchEvent(new CustomEvent('change-default', { bubbles: true, composed: true }));
}
#openTemplate(e: KeyboardEvent) {
e.preventDefault();
@@ -149,7 +149,6 @@ export class UmbTemplateCardElement extends FormControlMixin(UmbLitElement) {
render() {
return html`
-
`;
}
}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-layout/workspace-layout.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-layout/workspace-layout.element.ts
index bbe2cf2a8e..f1fe4b7ff2 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-layout/workspace-layout.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-layout/workspace-layout.element.ts
@@ -40,9 +40,9 @@ export class UmbWorkspaceLayout extends UmbLitElement {
}
#router-slot {
- display:flex;
- flex-direction:column;
- height:100%;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
}
uui-input {
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/manifests.ts
index ac7dfa75d5..78fa2bd2b7 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/manifests.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/manifests.ts
@@ -31,6 +31,18 @@ const modals: Array = [
name: 'Section Picker Modal',
loader: () => import('./section-picker/section-picker-modal.element'),
},
+ {
+ type: 'modal',
+ alias: 'Umb.Modal.TemplatePicker',
+ name: 'Template Picker Modal',
+ loader: () => import('./template-picker/template-picker-modal.element'),
+ },
+ {
+ type: 'modal',
+ alias: 'Umb.Modal.Template',
+ name: 'Template Modal',
+ loader: () => import('./template/template-modal.element'),
+ },
];
export const manifests = [...modals];
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/template-picker/index.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/template-picker/index.ts
new file mode 100644
index 0000000000..f8f2560e3e
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/template-picker/index.ts
@@ -0,0 +1,18 @@
+import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
+
+export interface UmbTemplatePickerModalData {
+ multiple: boolean;
+ selection: string[];
+}
+
+export interface UmbTemplatePickerModalResult {
+ selection: string[] | undefined;
+}
+
+export const UMB_TEMPLATE_PICKER_MODAL_TOKEN = new UmbModalToken<
+ UmbTemplatePickerModalData,
+ UmbTemplatePickerModalResult
+>('Umb.Modal.TemplatePicker', {
+ type: 'sidebar',
+ size: 'small',
+});
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/template-picker/template-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/template-picker/template-picker-modal.element.ts
new file mode 100644
index 0000000000..503b3113a8
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/template-picker/template-picker-modal.element.ts
@@ -0,0 +1,104 @@
+import { css, html } from 'lit';
+import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
+import { customElement, state } from 'lit/decorators.js';
+import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
+import { UmbTreeElement } from '../../components/tree/tree.element';
+import { UmbTemplatePickerModalData, UmbTemplatePickerModalResult } from '.';
+
+//TODO: make a default tree-picker that can be used across multiple pickers
+// TODO: make use of UmbPickerLayoutBase
+@customElement('umb-template-picker-modal')
+export class UmbTemplatePickerModalElement extends UmbModalBaseElement<
+ UmbTemplatePickerModalData,
+ UmbTemplatePickerModalResult
+> {
+ static styles = [
+ UUITextStyles,
+ css`
+ h3 {
+ margin-left: var(--uui-size-space-5);
+ margin-right: var(--uui-size-space-5);
+ }
+
+ uui-input {
+ width: 100%;
+ }
+
+ hr {
+ border: none;
+ border-bottom: 1px solid var(--uui-color-divider);
+ margin: 16px 0;
+ }
+
+ #content-list {
+ display: flex;
+ flex-direction: column;
+ gap: var(--uui-size-space-3);
+ }
+
+ .content-item {
+ cursor: pointer;
+ }
+
+ .content-item.selected {
+ background-color: var(--uui-color-selected);
+ color: var(--uui-color-selected-contrast);
+ }
+ `,
+ ];
+
+ @state()
+ _selection: Array = [];
+
+ @state()
+ _multiple = true;
+
+ connectedCallback() {
+ super.connectedCallback();
+ this._selection = this.data?.selection ?? [];
+ this._multiple = this.data?.multiple ?? true;
+ }
+
+ private _handleSelectionChange(e: CustomEvent) {
+ e.stopPropagation();
+ const element = e.target as UmbTreeElement;
+ //TODO: Should multiple property be implemented here or be passed down into umb-tree?
+ this._selection = this._multiple ? element.selection : [element.selection[element.selection.length - 1]];
+ }
+
+ private _submit() {
+ this.modalHandler?.submit({ selection: this._selection });
+ }
+
+ private _close() {
+ this.modalHandler?.reject();
+ }
+
+ render() {
+ return html`
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ }
+}
+
+export default UmbTemplatePickerModalElement;
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'umb-template-picker-modal': UmbTemplatePickerModalElement;
+ }
+}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/template/index.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/template/index.ts
new file mode 100644
index 0000000000..28f1568e30
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/template/index.ts
@@ -0,0 +1,18 @@
+import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
+
+export interface UmbTemplateModalData {
+ multiple: boolean;
+ selection: string[];
+}
+
+export interface UmbTemplateModalResult {
+ selection: string[] | undefined;
+}
+
+export const UMB_TEMPLATE_MODAL_TOKEN = new UmbModalToken(
+ 'Umb.Modal.Template',
+ {
+ type: 'sidebar',
+ size: 'large',
+ }
+);
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/template/template-modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/template/template-modal.element.ts
new file mode 100644
index 0000000000..89a6f66061
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/template/template-modal.element.ts
@@ -0,0 +1,97 @@
+import { css, html } from 'lit';
+import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
+import { customElement, state } from 'lit/decorators.js';
+import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
+import { UmbTreeElement } from '../../components/tree/tree.element';
+import { UmbTemplateModalData, UmbTemplateModalResult } from '.';
+
+//TODO: make a default tree-picker that can be used across multiple pickers
+// TODO: make use of UmbPickerLayoutBase
+@customElement('umb-template-modal')
+export class UmbTemplateModalElement extends UmbModalBaseElement {
+ static styles = [
+ UUITextStyles,
+ css`
+ h3 {
+ margin-left: var(--uui-size-space-5);
+ margin-right: var(--uui-size-space-5);
+ }
+
+ uui-input {
+ width: 100%;
+ }
+
+ hr {
+ border: none;
+ border-bottom: 1px solid var(--uui-color-divider);
+ margin: 16px 0;
+ }
+
+ #content-list {
+ display: flex;
+ flex-direction: column;
+ gap: var(--uui-size-space-3);
+ }
+
+ .content-item {
+ cursor: pointer;
+ }
+
+ .content-item.selected {
+ background-color: var(--uui-color-selected);
+ color: var(--uui-color-selected-contrast);
+ }
+ `,
+ ];
+
+ @state()
+ _selection: Array = [];
+
+ @state()
+ _multiple = true;
+
+ connectedCallback() {
+ super.connectedCallback();
+ this._selection = this.data?.selection ?? [];
+ this._multiple = this.data?.multiple ?? true;
+ }
+
+ private _handleSelectionChange(e: CustomEvent) {
+ e.stopPropagation();
+ const element = e.target as UmbTreeElement;
+ //TODO: Should multiple property be implemented here or be passed down into umb-tree?
+ this._selection = this._multiple ? element.selection : [element.selection[element.selection.length - 1]];
+ }
+
+ private _submit() {
+ this.modalHandler?.submit({ selection: this._selection });
+ }
+
+ private _close() {
+ this.modalHandler?.reject();
+ }
+
+ render() {
+ return html`
+
+
+
+
+ Code editor?
+
+
+
+
+
+
+ `;
+ }
+}
+
+export default UmbTemplateModalElement;
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'umb-template-modal': UmbTemplateModalElement;
+ }
+}
diff --git a/src/Umbraco.Web.UI.Client/src/core/mocks/data/document-type.data.ts b/src/Umbraco.Web.UI.Client/src/core/mocks/data/document-type.data.ts
index b02af718db..1f418b6b0c 100644
--- a/src/Umbraco.Web.UI.Client/src/core/mocks/data/document-type.data.ts
+++ b/src/Umbraco.Web.UI.Client/src/core/mocks/data/document-type.data.ts
@@ -890,8 +890,12 @@ export const data: Array = [
},
},
{
- allowedTemplateKeys: [],
- defaultTemplateKey: null,
+ allowedTemplateKeys: [
+ '2bf464b6-3aca-4388-b043-4eb439cc2643',
+ '9a84c0b3-03b4-4dd4-84ac-706740ac0f71',
+ '9a84c0b3-03b4-4dd4-84ac-706740ac0f72',
+ ],
+ defaultTemplateKey: '2bf464b6-3aca-4388-b043-4eb439cc2643',
key: 'simple-document-type-key',
alias: 'simpleDocumentType',
name: 'Simple Document Type',