Merge pull request #1234 from umbraco/feature/templating-page-field-modal
Feature: Page Field Modal + Section Picker Modal
This commit is contained in:
@@ -14,6 +14,7 @@ export const UMB_DICTIONARY_ITEM_PICKER_MODAL = new UmbModalToken<
|
||||
size: 'small',
|
||||
},
|
||||
data: {
|
||||
hideTreeRoot: true,
|
||||
treeAlias: 'Umb.Tree.Dictionary',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import {
|
||||
UMB_PARTIAL_VIEW_PICKER_MODAL,
|
||||
type UmbPartialViewPickerModalValue,
|
||||
} from '../../modals/partial-view-picker/partial-view-picker-modal.token.js';
|
||||
import { UMB_PARTIAL_VIEW_PICKER_MODAL } from '../../modals/partial-view-picker/partial-view-picker-modal.token.js';
|
||||
import { UMB_TEMPLATING_PAGE_FIELD_BUILDER_MODAL } from '../../modals/templating-page-field-builder/templating-page-field-builder-modal.token.js';
|
||||
import { CodeSnippetType } from '../../types.js';
|
||||
import {
|
||||
UMB_TEMPLATING_ITEM_PICKER_MODAL,
|
||||
@@ -11,11 +9,7 @@ import { getInsertDictionarySnippet, getInsertPartialSnippet } from '../../utils
|
||||
import { UmbDictionaryDetailRepository } from '@umbraco-cms/backoffice/dictionary';
|
||||
import { customElement, property, css, html } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import type {
|
||||
UmbDictionaryItemPickerModalValue,
|
||||
UmbModalManagerContext,
|
||||
UmbModalContext,
|
||||
} from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbModalManagerContext } from '@umbraco-cms/backoffice/modal';
|
||||
import { UMB_DICTIONARY_ITEM_PICKER_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
|
||||
@@ -26,8 +20,6 @@ export class UmbTemplatingInsertMenuElement extends UmbLitElement {
|
||||
|
||||
private _modalContext?: UmbModalManagerContext;
|
||||
|
||||
#openModal?: UmbModalContext;
|
||||
|
||||
#dictionaryDetailRepository = new UmbDictionaryDetailRepository(this);
|
||||
|
||||
constructor() {
|
||||
@@ -42,78 +34,82 @@ export class UmbTemplatingInsertMenuElement extends UmbLitElement {
|
||||
|
||||
switch (type) {
|
||||
case CodeSnippetType.partialView: {
|
||||
this.#getPartialViewSnippet(value as UmbPartialViewPickerModalValue);
|
||||
this.value = getInsertPartialSnippet(value);
|
||||
this.#dispatchInsertEvent();
|
||||
break;
|
||||
}
|
||||
case CodeSnippetType.dictionaryItem: {
|
||||
await this.#getDictionaryItemSnippet(value as UmbDictionaryItemPickerModalValue);
|
||||
await this.#getDictionaryItemSnippet(value);
|
||||
this.#dispatchInsertEvent();
|
||||
break;
|
||||
}
|
||||
case CodeSnippetType.pageField: {
|
||||
this.value = value;
|
||||
this.#dispatchInsertEvent();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#getDictionaryItemSnippet = async (modalValue: UmbDictionaryItemPickerModalValue) => {
|
||||
const unique = modalValue.selection[0];
|
||||
async #getDictionaryItemSnippet(unique: string) {
|
||||
if (unique === null) return;
|
||||
const { data } = await this.#dictionaryDetailRepository.requestByUnique(unique);
|
||||
this.value = getInsertDictionarySnippet(data?.name ?? '');
|
||||
};
|
||||
|
||||
#getPartialViewSnippet = async (modalValue: UmbPartialViewPickerModalValue) => {
|
||||
this.value = getInsertPartialSnippet(modalValue.selection?.[0] ?? '');
|
||||
};
|
||||
|
||||
#openChooseTypeModal = () => {
|
||||
this.#openModal = this._modalContext?.open(UMB_TEMPLATING_ITEM_PICKER_MODAL, {
|
||||
data: {
|
||||
hidePartialViews: this.hidePartialView,
|
||||
},
|
||||
});
|
||||
this.#openModal?.onSubmit().then((closedModal: UmbTemplatingItemPickerModalValue) => {
|
||||
this.determineInsertValue(closedModal);
|
||||
});
|
||||
};
|
||||
|
||||
#openInsertPageFieldSidebar() {
|
||||
//this.#openModel = this._modalContext?.open();
|
||||
}
|
||||
|
||||
#openInsertPartialViewSidebar() {
|
||||
this.#openModal = this._modalContext?.open(UMB_PARTIAL_VIEW_PICKER_MODAL);
|
||||
this.#openModal?.onSubmit().then((value) => {
|
||||
this.#getPartialViewSnippet(value).then(() => {
|
||||
this.#dispatchInsertEvent();
|
||||
});
|
||||
});
|
||||
async #openTemplatingItemPickerModal() {
|
||||
const itemPickerContext = this._modalContext?.open(UMB_TEMPLATING_ITEM_PICKER_MODAL);
|
||||
await itemPickerContext?.onSubmit();
|
||||
|
||||
const value = itemPickerContext?.getValue();
|
||||
if (!value) return;
|
||||
|
||||
this.determineInsertValue(value);
|
||||
}
|
||||
|
||||
#openInsertDictionaryItemModal() {
|
||||
this.#openModal = this._modalContext?.open(UMB_DICTIONARY_ITEM_PICKER_MODAL, {
|
||||
data: {
|
||||
pickableFilter: (item) => item.unique !== null,
|
||||
hideTreeRoot: true,
|
||||
},
|
||||
});
|
||||
this.#openModal?.onSubmit().then((value) => {
|
||||
this.#getDictionaryItemSnippet(value).then(() => {
|
||||
this.#dispatchInsertEvent();
|
||||
});
|
||||
});
|
||||
async #openPartialViewPickerModal() {
|
||||
const partialViewPickerContext = this._modalContext?.open(UMB_PARTIAL_VIEW_PICKER_MODAL);
|
||||
await partialViewPickerContext?.onSubmit();
|
||||
|
||||
const path = partialViewPickerContext?.getValue().selection[0];
|
||||
if (!path) return;
|
||||
|
||||
this.determineInsertValue({ type: CodeSnippetType.partialView, value: path });
|
||||
}
|
||||
|
||||
async #openDictionaryItemPickerModal() {
|
||||
const dictionaryItemPickerContext = this._modalContext?.open(UMB_DICTIONARY_ITEM_PICKER_MODAL);
|
||||
await dictionaryItemPickerContext?.onSubmit();
|
||||
|
||||
const item = dictionaryItemPickerContext?.getValue().selection[0];
|
||||
if (!item) return;
|
||||
|
||||
this.determineInsertValue({ type: CodeSnippetType.dictionaryItem, value: item });
|
||||
}
|
||||
|
||||
async #openPageFieldBuilderModal() {
|
||||
const pageFieldBuilderContext = this._modalContext?.open(UMB_TEMPLATING_PAGE_FIELD_BUILDER_MODAL);
|
||||
await pageFieldBuilderContext?.onSubmit();
|
||||
|
||||
const output = pageFieldBuilderContext?.getValue().output;
|
||||
if (!output) return;
|
||||
|
||||
// The output is already built due to the preview in the modal. Can insert it directly now.
|
||||
this.value = output;
|
||||
this.#dispatchInsertEvent();
|
||||
}
|
||||
|
||||
#dispatchInsertEvent() {
|
||||
this.dispatchEvent(new CustomEvent('insert', { bubbles: false, cancelable: true, composed: false }));
|
||||
}
|
||||
|
||||
@property({ type: Boolean })
|
||||
hidePartialView = false;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<uui-button-group>
|
||||
<uui-button look="secondary" @click=${this.#openChooseTypeModal} label=${this.localize.term('template_insert')}>
|
||||
<uui-button
|
||||
look="secondary"
|
||||
@click=${this.#openTemplatingItemPickerModal}
|
||||
label=${this.localize.term('template_insert')}>
|
||||
<uui-icon name="icon-add"></uui-icon>${this.localize.term('template_insert')}
|
||||
</uui-button>
|
||||
<umb-dropdown
|
||||
@@ -126,18 +122,18 @@ export class UmbTemplatingInsertMenuElement extends UmbLitElement {
|
||||
class="insert-menu-item"
|
||||
label=${this.localize.term('template_insertPageField')}
|
||||
title=${this.localize.term('template_insertPageField')}
|
||||
@click=${this.#openInsertPageFieldSidebar}></uui-menu-item>
|
||||
@click=${this.#openPageFieldBuilderModal}></uui-menu-item>
|
||||
<uui-menu-item
|
||||
class="insert-menu-item"
|
||||
label=${this.localize.term('template_insertPartialView')}
|
||||
title=${this.localize.term('template_insertPartialView')}
|
||||
@click=${this.#openInsertPartialViewSidebar}>
|
||||
@click=${this.#openPartialViewPickerModal}>
|
||||
</uui-menu-item>
|
||||
<uui-menu-item
|
||||
class="insert-menu-item"
|
||||
label=${this.localize.term('template_insertDictionaryItem')}
|
||||
title=${this.localize.term('template_insertDictionaryItem')}
|
||||
@click=${this.#openInsertDictionaryItemModal}>
|
||||
@click=${this.#openDictionaryItemPickerModal}>
|
||||
</uui-menu-item>
|
||||
</umb-dropdown>
|
||||
</uui-button-group>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './templating-section-picker/index.js';
|
||||
export * from './templating-item-picker/index.js';
|
||||
export * from './partial-view-picker/index.js';
|
||||
export * from './templating-page-field-builder/index.js';
|
||||
|
||||
@@ -13,6 +13,12 @@ const modals: Array<ManifestModal> = [
|
||||
name: 'Templating Section Picker Modal',
|
||||
js: () => import('./templating-section-picker/templating-section-picker-modal.element.js'),
|
||||
},
|
||||
{
|
||||
type: 'modal',
|
||||
alias: 'Umb.Modal.TemplatingPageFieldBuilder',
|
||||
name: 'Templating Page Field Builder Modal',
|
||||
js: () => import('./templating-page-field-builder/templating-page-field-builder-modal.element.js'),
|
||||
},
|
||||
];
|
||||
|
||||
export const manifests = [...modals];
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { CodeSnippetType } from '../../types.js';
|
||||
import { UMB_PARTIAL_VIEW_PICKER_MODAL } from '../partial-view-picker/partial-view-picker-modal.token.js';
|
||||
import { UMB_TEMPLATING_PAGE_FIELD_BUILDER_MODAL } from '../templating-page-field-builder/templating-page-field-builder-modal.token.js';
|
||||
import type {
|
||||
UmbTemplatingItemPickerModalData,
|
||||
UmbTemplatingItemPickerModalValue,
|
||||
} from './templating-item-picker-modal.token.js';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { css, html, customElement } from '@umbraco-cms/backoffice/external/lit';
|
||||
import type { UmbModalManagerContext, UmbModalContext } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbModalManagerContext } from '@umbraco-cms/backoffice/modal';
|
||||
import {
|
||||
UMB_MODAL_MANAGER_CONTEXT,
|
||||
UMB_DICTIONARY_ITEM_PICKER_MODAL,
|
||||
@@ -22,43 +23,58 @@ export class UmbTemplatingItemPickerModalElement extends UmbModalBaseElement<
|
||||
this.modalContext?.reject();
|
||||
}
|
||||
|
||||
private _modalContext?: UmbModalManagerContext;
|
||||
private _itemModalContext?: UmbModalManagerContext;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (instance) => {
|
||||
this._modalContext = instance;
|
||||
this._itemModalContext = instance;
|
||||
});
|
||||
}
|
||||
|
||||
#openModal?: UmbModalContext;
|
||||
async #openTemplatingPageFieldModal() {
|
||||
const pageFieldBuilderContext = this._itemModalContext?.open(UMB_TEMPLATING_PAGE_FIELD_BUILDER_MODAL);
|
||||
await pageFieldBuilderContext?.onSubmit();
|
||||
|
||||
#openInsertPartialViewSidebar() {
|
||||
this.#openModal = this._modalContext?.open(UMB_PARTIAL_VIEW_PICKER_MODAL);
|
||||
this.#openModal?.onSubmit().then((partialViewPickerModalValue) => {
|
||||
if (partialViewPickerModalValue) {
|
||||
this.value = {
|
||||
type: CodeSnippetType.partialView,
|
||||
value: partialViewPickerModalValue.selection[0],
|
||||
};
|
||||
this.modalContext?.submit();
|
||||
}
|
||||
});
|
||||
const output = pageFieldBuilderContext?.getValue().output;
|
||||
|
||||
if (output) {
|
||||
this.value = { value: output, type: CodeSnippetType.pageField };
|
||||
this.modalContext?.submit();
|
||||
}
|
||||
}
|
||||
|
||||
#openInsertDictionaryItemModal() {
|
||||
this.#openModal = this._modalContext?.open(UMB_DICTIONARY_ITEM_PICKER_MODAL, {
|
||||
async #openPartialViewPickerModal() {
|
||||
const partialViewPickerContext = this._itemModalContext?.open(UMB_PARTIAL_VIEW_PICKER_MODAL);
|
||||
await partialViewPickerContext?.onSubmit();
|
||||
|
||||
const path = partialViewPickerContext?.getValue().selection[0];
|
||||
|
||||
if (path) {
|
||||
const regex = /^%2F|%25dot%25cshtml$/g;
|
||||
const prettyPath = path.replace(regex, '').replace(/%2F/g, '/');
|
||||
this.value = {
|
||||
value: prettyPath,
|
||||
type: CodeSnippetType.partialView,
|
||||
};
|
||||
this.modalContext?.submit();
|
||||
}
|
||||
}
|
||||
|
||||
async #openDictionaryItemPickerModal() {
|
||||
const dictionaryItemPickerModal = this._itemModalContext?.open(UMB_DICTIONARY_ITEM_PICKER_MODAL, {
|
||||
data: {
|
||||
hideTreeRoot: true,
|
||||
pickableFilter: (item) => item.unique !== null,
|
||||
},
|
||||
});
|
||||
this.#openModal?.onSubmit().then((dictionaryItemPickerModalValue) => {
|
||||
if (dictionaryItemPickerModalValue) {
|
||||
this.value = { value: dictionaryItemPickerModalValue, type: CodeSnippetType.dictionaryItem };
|
||||
this.modalContext?.submit();
|
||||
}
|
||||
});
|
||||
await dictionaryItemPickerModal?.onSubmit();
|
||||
|
||||
const dictionaryItem = dictionaryItemPickerModal?.getValue().selection[0];
|
||||
|
||||
if (dictionaryItem) {
|
||||
this.value = { value: dictionaryItem, type: CodeSnippetType.dictionaryItem };
|
||||
this.modalContext?.submit();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -77,10 +93,10 @@ export class UmbTemplatingItemPickerModalElement extends UmbModalBaseElement<
|
||||
#renderItems() {
|
||||
return html`<div id="main">
|
||||
<uui-button
|
||||
@click=${() => console.log('to be continued')}
|
||||
@click=${this.#openTemplatingPageFieldModal}
|
||||
look="placeholder"
|
||||
label=${this.localize.term('template_insert')}>
|
||||
<h3><umb-localize key="template_insertPageField">Value</umb-localize> (Not implemented)</h3>
|
||||
<h3><umb-localize key="template_insertPageField">Value</umb-localize></h3>
|
||||
<p>
|
||||
<umb-localize key="template_insertPageFieldDesc">
|
||||
Displays the value of a named field from the current page, with options to modify the value or fallback to
|
||||
@@ -89,7 +105,7 @@ export class UmbTemplatingItemPickerModalElement extends UmbModalBaseElement<
|
||||
</p>
|
||||
</uui-button>
|
||||
<uui-button
|
||||
@click=${this.#openInsertPartialViewSidebar}
|
||||
@click=${this.#openPartialViewPickerModal}
|
||||
look="placeholder"
|
||||
label=${this.localize.term('template_insert')}>
|
||||
<h3><umb-localize key="template_insertPartialView">Partial view</umb-localize></h3>
|
||||
@@ -101,7 +117,7 @@ export class UmbTemplatingItemPickerModalElement extends UmbModalBaseElement<
|
||||
</p>
|
||||
</uui-button>
|
||||
<uui-button
|
||||
@click=${this.#openInsertDictionaryItemModal}
|
||||
@click=${this.#openDictionaryItemPickerModal}
|
||||
look="placeholder"
|
||||
label=${this.localize.term('template_insertDictionaryItem')}>
|
||||
<h3><umb-localize key="template_insertDictionaryItem">Dictionary Item</umb-localize></h3>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { CodeSnippetType } from '../../types.js';
|
||||
import type { UmbPartialViewPickerModalValue, UmbTemplatingPageFieldBuilderModalValue } from '../index.js';
|
||||
import { type UmbDictionaryItemPickerModalValue, UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbTemplatingItemPickerModalData {
|
||||
@@ -6,7 +7,7 @@ export interface UmbTemplatingItemPickerModalData {
|
||||
}
|
||||
|
||||
export type UmbTemplatingItemPickerModalValue = {
|
||||
value: string | UmbDictionaryItemPickerModalValue;
|
||||
value: string;
|
||||
type: CodeSnippetType;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './templating-page-field-builder-modal.element.js';
|
||||
export * from './templating-page-field-builder-modal.token.js';
|
||||
@@ -0,0 +1,109 @@
|
||||
import { getUmbracoFieldSnippet } from '../../utils/index.js';
|
||||
import type {
|
||||
UmbTemplatingPageFieldBuilderModalData,
|
||||
UmbTemplatingPageFieldBuilderModalValue,
|
||||
} from './templating-page-field-builder-modal.token.js';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
@customElement('umb-templating-page-field-builder-modal')
|
||||
export class UmbTemplatingPageFieldBuilderModalElement extends UmbModalBaseElement<
|
||||
UmbTemplatingPageFieldBuilderModalData,
|
||||
UmbTemplatingPageFieldBuilderModalValue
|
||||
> {
|
||||
private _close() {
|
||||
this.modalContext?.reject();
|
||||
}
|
||||
|
||||
private _submit() {
|
||||
if (!this._field) return;
|
||||
this.value = { output: getUmbracoFieldSnippet(this._field, this._default, this._recursive) };
|
||||
this.modalContext?.submit();
|
||||
}
|
||||
|
||||
@state()
|
||||
private _field?: string;
|
||||
|
||||
@state()
|
||||
private _haveDefault: boolean = false;
|
||||
|
||||
@state()
|
||||
private _default?: string;
|
||||
|
||||
@state()
|
||||
private _recursive: boolean = false;
|
||||
|
||||
/** TODO: Implement "Choose field" */
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<umb-body-layout headline=${this.localize.term('template_insert')}>
|
||||
<uui-box>
|
||||
<div>
|
||||
<uui-label for="page-field-value">
|
||||
<umb-localize key="templateEditor_chooseField">Choose field</umb-localize>
|
||||
</uui-label>
|
||||
(Not implemented yet)
|
||||
|
||||
<uui-label for="page-field-default-value">
|
||||
<umb-localize key="templateEditor_defaultValue">Default value</umb-localize>
|
||||
</uui-label>
|
||||
${!this._haveDefault
|
||||
? html`<uui-button
|
||||
label=${this.localize.term('templateEditor_addDefaultValue')}
|
||||
look="placeholder"
|
||||
@click=${() => (this._haveDefault = true)}></uui-button>`
|
||||
: html`<uui-input
|
||||
id="page-field-default-value"
|
||||
label=${this.localize.term('templateEditor_defaultValue')}></uui-input>`}
|
||||
|
||||
<uui-label for="recursive"><umb-localize key="templateEditor_recursive">Recursive</umb-localize></uui-label>
|
||||
<uui-checkbox
|
||||
id="recursive"
|
||||
label=${this.localize.term('templateEditor_recursiveDescr')}
|
||||
?disabled=${this._field ? false : true}></uui-checkbox>
|
||||
|
||||
<uui-label><umb-localize key="templateEditor_outputSample">Output sample</umb-localize></uui-label>
|
||||
<umb-code-block language="C#" copy
|
||||
>${this._field ? getUmbracoFieldSnippet(this._field, this._default, this._recursive) : ''}</umb-code-block
|
||||
>
|
||||
</div>
|
||||
</uui-box>
|
||||
<uui-button
|
||||
slot="actions"
|
||||
@click=${this._close}
|
||||
look="secondary"
|
||||
label=${this.localize.term('general_close')}></uui-button>
|
||||
<uui-button
|
||||
slot="actions"
|
||||
@click=${this._submit}
|
||||
color="positive"
|
||||
look="primary"
|
||||
label=${this.localize.term('general_submit')}></uui-button>
|
||||
</umb-body-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UmbTextStyles,
|
||||
css`
|
||||
uui-box > div {
|
||||
display: grid;
|
||||
gap: var(--uui-size-space-2);
|
||||
}
|
||||
|
||||
uui-label:not(:first-child) {
|
||||
margin-top: var(--uui-size-space-6);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbTemplatingPageFieldBuilderModalElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-templating-page-field-builder-modal': UmbTemplatingPageFieldBuilderModalElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbTemplatingPageFieldBuilderModalData {}
|
||||
|
||||
export type UmbTemplatingPageFieldBuilderModalValue = {
|
||||
output: string;
|
||||
};
|
||||
|
||||
export const UMB_TEMPLATING_PAGE_FIELD_BUILDER_MODAL = new UmbModalToken<
|
||||
UmbTemplatingPageFieldBuilderModalData,
|
||||
UmbTemplatingPageFieldBuilderModalValue
|
||||
>('Umb.Modal.TemplatingPageFieldBuilder', {
|
||||
modal: {
|
||||
type: 'sidebar',
|
||||
size: 'small',
|
||||
},
|
||||
});
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './templating-section-picker-input.element.js';
|
||||
export * from './templating-section-picker-modal.element.js';
|
||||
export * from './templating-section-picker-modal.token.js';
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
import type { UUIInputElement } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UUIBooleanInputElement } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { css, html, customElement, property, query } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
|
||||
@customElement('umb-insert-section-checkbox')
|
||||
export class UmbInsertSectionCheckboxElement extends UUIBooleanInputElement {
|
||||
renderCheckbox() {
|
||||
return html``;
|
||||
}
|
||||
|
||||
@property({ type: Boolean, attribute: 'show-mandatory' })
|
||||
showMandatory = false;
|
||||
|
||||
@property({ type: Boolean, attribute: 'show-input' })
|
||||
showInput = false;
|
||||
|
||||
@query('uui-input')
|
||||
input?: UUIInputElement;
|
||||
|
||||
@query('form')
|
||||
form?: HTMLFormElement;
|
||||
|
||||
@query('uui-checkbox')
|
||||
checkbox?: HTMLFormElement;
|
||||
|
||||
get snippet() {
|
||||
if (!this.snippetMethod) return '';
|
||||
const snippet = this.snippetMethod(this.inputValue as string, this.isMandatory) ?? '';
|
||||
return snippet;
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
snippetMethod?: (value: string, isMandatory: boolean) => string;
|
||||
|
||||
validate() {
|
||||
if (!this.form) return true;
|
||||
|
||||
this.form.requestSubmit();
|
||||
return this.form.checkValidity();
|
||||
}
|
||||
|
||||
#preventDefault(event: Event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
get inputValue() {
|
||||
return this.input?.value;
|
||||
}
|
||||
|
||||
get isMandatory() {
|
||||
return this.checkbox?.checked;
|
||||
}
|
||||
|
||||
/* eslint-disable lit-a11y/click-events-have-key-events */
|
||||
render() {
|
||||
return html`
|
||||
${super.render()}
|
||||
<h3 class="uui-h3" @click=${this.click}>
|
||||
${this.checked ? html`<uui-icon name="icon-check"></uui-icon>` : ''}${this.label}
|
||||
</h3>
|
||||
<div @click=${this.click}>
|
||||
<slot name="description"><p>here goes some description</p></slot>
|
||||
</div>
|
||||
${this.checked && this.showInput
|
||||
? html`<uui-form>
|
||||
<form @submit=${this.#preventDefault}>
|
||||
<uui-form-layout-item>
|
||||
<uui-label slot="label" for="section-name-input" required>Section name</uui-label>
|
||||
<uui-input
|
||||
required
|
||||
placeholder="Enter section name"
|
||||
label="Section name"
|
||||
id="section-name-input"></uui-input> </uui-form-layout-item
|
||||
>${this.showMandatory
|
||||
? html`<p slot="if-checked">
|
||||
<uui-checkbox label="Section is mandatory">Section is mandatory </uui-checkbox><br />
|
||||
<small
|
||||
>If mandatory, the child template must contain a <code>@section</code> definition, otherwise an
|
||||
error is shown.</small
|
||||
>
|
||||
</p>`
|
||||
: ''}
|
||||
</form>
|
||||
</uui-form>`
|
||||
: ''}
|
||||
`;
|
||||
}
|
||||
/* eslint-enable lit-a11y/click-events-have-key-events */
|
||||
|
||||
static styles = [
|
||||
...UUIBooleanInputElement.styles,
|
||||
UmbTextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
border-style: dashed;
|
||||
background-color: transparent;
|
||||
color: var(--uui-color-default-standalone, rgb(28, 35, 59));
|
||||
border-color: var(--uui-color-border-standalone, #c2c2c2);
|
||||
border-radius: var(--uui-border-radius, 3px);
|
||||
border-width: 1px;
|
||||
line-height: normal;
|
||||
padding: 6px 18px;
|
||||
}
|
||||
|
||||
:host(:hover),
|
||||
:host(:focus),
|
||||
:host(:focus-within) {
|
||||
background-color: var(--uui-button-background-color-hover, transparent);
|
||||
color: var(--uui-color-default-emphasis, #3544b1);
|
||||
border-color: var(--uui-color-default-emphasis, #3544b1);
|
||||
}
|
||||
|
||||
uui-icon {
|
||||
background-color: var(--uui-color-positive-emphasis);
|
||||
border-radius: 50%;
|
||||
padding: 0.2em;
|
||||
margin-right: 1ch;
|
||||
color: var(--uui-color-positive-contrast);
|
||||
font-size: 0.7em;
|
||||
}
|
||||
|
||||
::slotted(*) {
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
h3,
|
||||
p {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
uui-input {
|
||||
width: 100%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbInsertSectionCheckboxElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-insert-section-input': UmbInsertSectionCheckboxElement;
|
||||
}
|
||||
}
|
||||
@@ -1,143 +1,186 @@
|
||||
import { getAddSectionSnippet, getRenderBodySnippet, getRenderSectionSnippet } from '../../utils/index.js';
|
||||
import { TemplatingSectionType } from '../../types.js';
|
||||
import type {
|
||||
UmbTemplatingSectionPickerModalData,
|
||||
UmbTemplatingSectionPickerModalValue,
|
||||
} from './templating-section-picker-modal.token.js';
|
||||
import type { UmbInsertSectionCheckboxElement } from './templating-section-picker-input.element.js';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { css, html, customElement, queryAll, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { css, html, customElement, state, query } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
|
||||
import './templating-section-picker-input.element.js';
|
||||
import type { UUIBooleanInputElement, UUIInputElement } from '@umbraco-cms/backoffice/external/uui';
|
||||
|
||||
@customElement('umb-templating-section-picker-modal')
|
||||
export class UmbTemplatingSectionPickerModalElement extends UmbModalBaseElement<
|
||||
UmbTemplatingSectionPickerModalData,
|
||||
UmbTemplatingSectionPickerModalValue
|
||||
> {
|
||||
@queryAll('umb-insert-section-checkbox')
|
||||
checkboxes!: NodeListOf<UmbInsertSectionCheckboxElement>;
|
||||
@query('#render-named-section-name')
|
||||
private _renderNamedSectionNameInput?: UUIInputElement;
|
||||
|
||||
@query('#define-named-section-name')
|
||||
private _defineNamedSectionNameInput?: UUIInputElement;
|
||||
|
||||
@query('#render-named-section-is-mandatory')
|
||||
private _renderNamedSectionIsMandatoryCheckbox?: UUIBooleanInputElement;
|
||||
|
||||
@state()
|
||||
selectedCheckbox?: UmbInsertSectionCheckboxElement | null = null;
|
||||
|
||||
@state()
|
||||
snippet = '';
|
||||
|
||||
#chooseSection(event: Event) {
|
||||
const target = event.target as UmbInsertSectionCheckboxElement;
|
||||
const checkboxes = Array.from(this.checkboxes);
|
||||
if (checkboxes.every((checkbox) => checkbox.checked === false)) {
|
||||
this.selectedCheckbox = null;
|
||||
return;
|
||||
}
|
||||
if (target.checked) {
|
||||
this.selectedCheckbox = target;
|
||||
this.snippet = this.selectedCheckbox.snippet ?? '';
|
||||
checkboxes.forEach((checkbox) => {
|
||||
if (checkbox !== target) {
|
||||
checkbox.checked = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.selectedCheckbox = this.checkboxes[0];
|
||||
}
|
||||
|
||||
snippetMethods = [getRenderBodySnippet, getRenderSectionSnippet, getAddSectionSnippet];
|
||||
private _pickedSection: TemplatingSectionType = TemplatingSectionType.renderChildTemplate;
|
||||
|
||||
#close() {
|
||||
this.modalContext?.reject();
|
||||
}
|
||||
|
||||
#submit() {
|
||||
const value = this.selectedCheckbox?.snippet;
|
||||
if (this.selectedCheckbox?.validate()) {
|
||||
this.value = { value: value ?? '' };
|
||||
this.modalContext?.submit();
|
||||
switch (this._pickedSection) {
|
||||
case TemplatingSectionType.renderChildTemplate:
|
||||
this.value = { value: getRenderBodySnippet() };
|
||||
break;
|
||||
case TemplatingSectionType.renderANamedSection:
|
||||
this.value = {
|
||||
value: getRenderSectionSnippet(
|
||||
this._renderNamedSectionNameInput?.value as string,
|
||||
this._renderNamedSectionIsMandatoryCheckbox?.checked ?? false,
|
||||
),
|
||||
};
|
||||
break;
|
||||
case TemplatingSectionType.defineANamedSection:
|
||||
this.value = { value: getAddSectionSnippet(this._defineNamedSectionNameInput?.value as string) };
|
||||
break;
|
||||
}
|
||||
this.modalContext?.submit();
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<umb-body-layout headline=${this.localize.term('template_insert')}>
|
||||
<div id="main">
|
||||
<uui-box>
|
||||
<umb-insert-section-checkbox
|
||||
@change=${this.#chooseSection}
|
||||
label=${this.localize.term('template_renderBody')}
|
||||
checked
|
||||
.snippetMethod=${getRenderBodySnippet}>
|
||||
<p slot="description">
|
||||
<umb-localize key="template_renderBodyDesc">
|
||||
Renders the contents of a child template, by inserting a <code>@RenderBody()</code> placeholder.
|
||||
</umb-localize>
|
||||
</p>
|
||||
</umb-insert-section-checkbox>
|
||||
<uui-box>
|
||||
<div id="main">
|
||||
${this.#renderRenderChildTemplate()} ${this.#renderRenderANamedSection()}
|
||||
${this.#renderDefineANamedSection()}
|
||||
</div>
|
||||
</uui-box>
|
||||
|
||||
<umb-insert-section-checkbox
|
||||
@change=${this.#chooseSection}
|
||||
label=${this.localize.term('template_renderSection')}
|
||||
show-mandatory
|
||||
show-input
|
||||
.snippetMethod=${getRenderSectionSnippet}>
|
||||
<p slot="description">
|
||||
<umb-localize key="template_renderSectionDesc">
|
||||
Renders a named area of a child template, by inserting a
|
||||
<code>@RenderSection(name)</code> placeholder. This renders an area of a child template which is
|
||||
wrapped in a corresponding <code>@section [name]{ ... }</code> definition.
|
||||
</umb-localize>
|
||||
</p>
|
||||
</umb-insert-section-checkbox>
|
||||
|
||||
<umb-insert-section-checkbox
|
||||
@change=${this.#chooseSection}
|
||||
label=${this.localize.term('template_defineSection')}
|
||||
show-input
|
||||
.snippetMethod=${getAddSectionSnippet}>
|
||||
<p slot="description">
|
||||
<umb-localize key="template_defineSectionDesc">
|
||||
Renders a named area of a child template, by inserting a
|
||||
<code>@RenderSection(name)</code> placeholder. This renders an area of a child template which is
|
||||
wrapped in a corresponding <code>@section [name]{ ... }</code> definition.
|
||||
</umb-localize>
|
||||
</p>
|
||||
</umb-insert-section-checkbox>
|
||||
</uui-box>
|
||||
</div>
|
||||
<div slot="actions">
|
||||
<uui-button @click=${this.#close} look="secondary" label="Close">Close</uui-button>
|
||||
<uui-button @click=${this.#submit} look="primary" color="positive" label="Submit">Submit</uui-button>
|
||||
<uui-button @click=${this.#close} look="secondary" label=${this.localize.term('general_close')}></uui-button>
|
||||
<uui-button
|
||||
@click=${this.#submit}
|
||||
look="primary"
|
||||
color="positive"
|
||||
label=${this.localize.term('general_submit')}></uui-button>
|
||||
</div>
|
||||
</umb-body-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderRenderChildTemplate() {
|
||||
return html`<uui-button
|
||||
label=${this.localize.term('template_renderBody')}
|
||||
@click=${() => (this._pickedSection = TemplatingSectionType.renderChildTemplate)}
|
||||
look="placeholder">
|
||||
${this._pickedSection === TemplatingSectionType.renderChildTemplate
|
||||
? html`<uui-badge color="positive"><uui-icon name="icon-check"></uui-icon></uui-badge>`
|
||||
: ''}
|
||||
<h3><umb-localize key="template_renderBody">Render Child Template</umb-localize></h3>
|
||||
<p>
|
||||
<umb-localize key="template_renderBodyDesc">
|
||||
Renders the contents of a child template, by inserting a <code>@RenderBody()</code> placeholder.
|
||||
</umb-localize>
|
||||
</p>
|
||||
</uui-button>`;
|
||||
}
|
||||
|
||||
#renderRenderANamedSection() {
|
||||
return html`<uui-button
|
||||
label=${this.localize.term('template_renderSection')}
|
||||
@click=${() => (this._pickedSection = TemplatingSectionType.renderANamedSection)}
|
||||
look="placeholder">
|
||||
${this._pickedSection === TemplatingSectionType.renderANamedSection
|
||||
? html`<uui-badge color="positive"><uui-icon name="icon-check"></uui-icon></uui-badge>`
|
||||
: ''}
|
||||
<h3><umb-localize key="template_renderSection">Render a named section</umb-localize></h3>
|
||||
<p>
|
||||
<umb-localize key="template_renderSectionDesc">
|
||||
Renders a named area of a child template, by inserting a
|
||||
<code>@RenderSection(name)</code> placeholder. This renders an area of a child template which is wrapped in a
|
||||
corresponding <code>@section [name]{ ... }</code> definition.
|
||||
</umb-localize>
|
||||
</p>
|
||||
${this._pickedSection === TemplatingSectionType.renderANamedSection
|
||||
? html`<div class="section">
|
||||
<uui-label for="render-named-section-name" required>
|
||||
<umb-localize key="template_sectionName">Section Name</umb-localize>
|
||||
</uui-label>
|
||||
<uui-input id="render-named-section-name" label=${this.localize.term('template_sectionName')}></uui-input>
|
||||
<uui-checkbox
|
||||
id="render-named-section-is-mandatory"
|
||||
label=${this.localize.term('template_sectionMandatory')}></uui-checkbox>
|
||||
<small>
|
||||
<umb-localize key="template_sectionMandatoryDesc">
|
||||
If mandatory, the child template must contain a <code>@section</code> definition, otherwise an error is
|
||||
shown.
|
||||
</umb-localize>
|
||||
</small>
|
||||
</div>`
|
||||
: ''}
|
||||
</uui-button>`;
|
||||
}
|
||||
|
||||
#renderDefineANamedSection() {
|
||||
return html`<uui-button
|
||||
label=${this.localize.term('template_defineSection')}
|
||||
@click=${() => (this._pickedSection = TemplatingSectionType.defineANamedSection)}
|
||||
look="placeholder">
|
||||
${this._pickedSection === TemplatingSectionType.defineANamedSection
|
||||
? html`<uui-badge color="positive"><uui-icon name="icon-check"></uui-icon></uui-badge>`
|
||||
: ''}
|
||||
<h3><umb-localize key="template_defineSection">Define a named section</umb-localize></h3>
|
||||
<p>
|
||||
<umb-localize key="template_defineSectionDesc">
|
||||
Defines a part of your template as a named section by wrapping it in <code>@section { ... }</code>. This can
|
||||
be rendered in a specific area of the parent of this template, by using <code>@RenderSection</code>.
|
||||
</umb-localize>
|
||||
</p>
|
||||
${this._pickedSection === TemplatingSectionType.defineANamedSection
|
||||
? html`<div class="section">
|
||||
<uui-label for="define-named-section-name" required>
|
||||
<umb-localize key="template_sectionName">Section Name</umb-localize>
|
||||
</uui-label>
|
||||
<uui-input id="define-named-section-name" label=${this.localize.term('template_sectionName')}></uui-input>
|
||||
</div>`
|
||||
: ''}
|
||||
</uui-button>`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UmbTextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
color: var(--uui-color-text);
|
||||
--umb-header-layout-height: 70px;
|
||||
}
|
||||
|
||||
#main {
|
||||
box-sizing: border-box;
|
||||
height: calc(
|
||||
100dvh - var(--umb-header-layout-height) - var(--umb-footer-layout-height) - 2 * var(--uui-size-layout-1)
|
||||
);
|
||||
}
|
||||
|
||||
#main umb-insert-section-checkbox:not(:last-of-type) {
|
||||
margin-bottom: var(--uui-size-space-5);
|
||||
}
|
||||
code {
|
||||
background-color: var(--uui-color-surface-alt);
|
||||
border: 1px solid var(--uui-color-border);
|
||||
border-radius: var(--uui-border-radius);
|
||||
}
|
||||
|
||||
#main {
|
||||
display: grid;
|
||||
grid-gap: var(--uui-size-space-5);
|
||||
}
|
||||
|
||||
.section {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
uui-button {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
uui-button p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
uui-input,
|
||||
small {
|
||||
margin-block: var(--uui-size-space-2) var(--uui-size-space-6);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -109,6 +109,7 @@ export class UmbTemplateWorkspaceEditorElement extends UmbLitElement {
|
||||
|
||||
#openInsertSectionModal() {
|
||||
const sectionModal = this._modalContext?.open(UMB_TEMPLATING_SECTION_PICKER_MODAL);
|
||||
|
||||
sectionModal?.onSubmit().then((insertSectionModalValue) => {
|
||||
if (insertSectionModalValue?.value) {
|
||||
this._codeEditor?.insert(insertSectionModalValue.value);
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
export enum CodeSnippetType {
|
||||
partialView = 'partialView',
|
||||
dictionaryItem = 'dictionaryItem',
|
||||
pageField = 'pageField',
|
||||
}
|
||||
|
||||
export enum TemplatingSectionType {
|
||||
renderChildTemplate = 'RenderChildTemplate',
|
||||
renderANamedSection = 'RenderANamedSection',
|
||||
defineANamedSection = 'DefineANamedSection',
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ export const getUmbracoFieldSnippet = (field: string, defaultValue: string | nul
|
||||
|
||||
const value = `${field !== null ? `@Model.Value("${field}"` : ''}${
|
||||
fallback !== null ? `, fallback: ${fallback}` : ''
|
||||
}${defaultValue !== null ? `, defaultValue: new HtmlString("${defaultValue}")` : ''}${field ? ')' : ''}`;
|
||||
}${defaultValue !== null ? `, defaultValue: new HtmlString("${defaultValue}")` : ''}${field ? ')' : ')'}`;
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user