diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/dictionary-item-picker-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/dictionary-item-picker-modal.token.ts index 95dfd0fbbd..0e6966abbf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/dictionary-item-picker-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/dictionary-item-picker-modal.token.ts @@ -14,6 +14,7 @@ export const UMB_DICTIONARY_ITEM_PICKER_MODAL = new UmbModalToken< size: 'small', }, data: { + hideTreeRoot: true, treeAlias: 'Umb.Tree.Dictionary', }, }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/components/templating-item-menu/templating-item-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/components/templating-item-menu/templating-item-menu.element.ts index 4caa96f023..b21e364421 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/components/templating-item-menu/templating-item-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/components/templating-item-menu/templating-item-menu.element.ts @@ -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` - + ${this.localize.term('template_insert')} + @click=${this.#openPageFieldBuilderModal}> + @click=${this.#openPartialViewPickerModal}> + @click=${this.#openDictionaryItemPickerModal}> diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/index.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/index.ts index b95cabeeec..a3237a45c0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/index.ts @@ -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'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/manifests.ts index fac1177afb..4270e4c145 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/manifests.ts @@ -13,6 +13,12 @@ const modals: Array = [ 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]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-item-picker/templating-item-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-item-picker/templating-item-picker-modal.element.ts index 895a0b478d..6239cbbc1d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-item-picker/templating-item-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-item-picker/templating-item-picker-modal.element.ts @@ -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`
console.log('to be continued')} + @click=${this.#openTemplatingPageFieldModal} look="placeholder" label=${this.localize.term('template_insert')}> -

Value (Not implemented)

+

Value

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<

Partial view

@@ -101,7 +117,7 @@ export class UmbTemplatingItemPickerModalElement extends UmbModalBaseElement<

Dictionary Item

diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-item-picker/templating-item-picker-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-item-picker/templating-item-picker-modal.token.ts index 79e593dae8..9a0a15e733 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-item-picker/templating-item-picker-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-item-picker/templating-item-picker-modal.token.ts @@ -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; }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-page-field-builder/index.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-page-field-builder/index.ts new file mode 100644 index 0000000000..696a5c05e5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-page-field-builder/index.ts @@ -0,0 +1,2 @@ +export * from './templating-page-field-builder-modal.element.js'; +export * from './templating-page-field-builder-modal.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-page-field-builder/templating-page-field-builder-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-page-field-builder/templating-page-field-builder-modal.element.ts new file mode 100644 index 0000000000..37afe148ca --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-page-field-builder/templating-page-field-builder-modal.element.ts @@ -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` + + +
+ + Choose field + + (Not implemented yet) + + + Default value + + ${!this._haveDefault + ? html` (this._haveDefault = true)}>` + : html``} + + Recursive + + + Output sample + ${this._field ? getUmbracoFieldSnippet(this._field, this._default, this._recursive) : ''} +
+
+ + +
+ `; + } + + 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; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-page-field-builder/templating-page-field-builder-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-page-field-builder/templating-page-field-builder-modal.token.ts new file mode 100644 index 0000000000..ad621e61b5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-page-field-builder/templating-page-field-builder-modal.token.ts @@ -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', + }, +}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-section-picker/index.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-section-picker/index.ts index 5bb66c95b5..d5571f146d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-section-picker/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-section-picker/index.ts @@ -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'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-section-picker/templating-section-picker-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-section-picker/templating-section-picker-input.element.ts deleted file mode 100644 index c8cf5632d1..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-section-picker/templating-section-picker-input.element.ts +++ /dev/null @@ -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()} -

- ${this.checked ? html`` : ''}${this.label} -

-
-

here goes some description

-
- ${this.checked && this.showInput - ? html` -
- - Section name - ${this.showMandatory - ? html`

- Section is mandatory
- If mandatory, the child template must contain a @section definition, otherwise an - error is shown. -

` - : ''} -
-
` - : ''} - `; - } - /* 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; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-section-picker/templating-section-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-section-picker/templating-section-picker-modal.element.ts index c8aa7f5056..fd92e4c79c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-section-picker/templating-section-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-section-picker/templating-section-picker-modal.element.ts @@ -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; + @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` -
- - -

- - Renders the contents of a child template, by inserting a @RenderBody() placeholder. - -

-
+ +
+ ${this.#renderRenderChildTemplate()} ${this.#renderRenderANamedSection()} + ${this.#renderDefineANamedSection()} +
+
- -

- - Renders a named area of a child template, by inserting a - @RenderSection(name) placeholder. This renders an area of a child template which is - wrapped in a corresponding @section [name]{ ... } definition. - -

-
- - -

- - Renders a named area of a child template, by inserting a - @RenderSection(name) placeholder. This renders an area of a child template which is - wrapped in a corresponding @section [name]{ ... } definition. - -

-
-
-
- Close - Submit + +
`; } + #renderRenderChildTemplate() { + return html` (this._pickedSection = TemplatingSectionType.renderChildTemplate)} + look="placeholder"> + ${this._pickedSection === TemplatingSectionType.renderChildTemplate + ? html`` + : ''} +

Render Child Template

+

+ + Renders the contents of a child template, by inserting a @RenderBody() placeholder. + +

+
`; + } + + #renderRenderANamedSection() { + return html` (this._pickedSection = TemplatingSectionType.renderANamedSection)} + look="placeholder"> + ${this._pickedSection === TemplatingSectionType.renderANamedSection + ? html`` + : ''} +

Render a named section

+

+ + Renders a named area of a child template, by inserting a + @RenderSection(name) placeholder. This renders an area of a child template which is wrapped in a + corresponding @section [name]{ ... } definition. + +

+ ${this._pickedSection === TemplatingSectionType.renderANamedSection + ? html`
+ + Section Name + + + + + + If mandatory, the child template must contain a @section definition, otherwise an error is + shown. + + +
` + : ''} +
`; + } + + #renderDefineANamedSection() { + return html` (this._pickedSection = TemplatingSectionType.defineANamedSection)} + look="placeholder"> + ${this._pickedSection === TemplatingSectionType.defineANamedSection + ? html`` + : ''} +

Define a named section

+

+ + Defines a part of your template as a named section by wrapping it in @section { ... }. This can + be rendered in a specific area of the parent of this template, by using @RenderSection. + +

+ ${this._pickedSection === TemplatingSectionType.defineANamedSection + ? html`
+ + Section Name + + +
` + : ''} +
`; + } + 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); + } `, ]; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace-editor.element.ts index a551b3c7d1..5e88ed098d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace-editor.element.ts @@ -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); diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/types.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/types.ts index 01a83f31b0..46069918b5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/types.ts @@ -1,4 +1,11 @@ export enum CodeSnippetType { partialView = 'partialView', dictionaryItem = 'dictionaryItem', + pageField = 'pageField', +} + +export enum TemplatingSectionType { + renderChildTemplate = 'RenderChildTemplate', + renderANamedSection = 'RenderANamedSection', + defineANamedSection = 'DefineANamedSection', } diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/utils/index.ts index 57c84ea4bf..43446d5e20 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/utils/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/utils/index.ts @@ -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; };