diff --git a/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/combine-observables.function.ts b/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/combine-observables.function.ts deleted file mode 100644 index 7e1cef737d..0000000000 --- a/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/combine-observables.function.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { MemoizationFunction } from '../types/memoization-function.type.js'; -import type { MappingFunction } from '../types/mapping-function.type.js'; -import { defaultMemoization } from './default-memoization.function.js'; -import { type Observable, combineLatest } from '@umbraco-cms/backoffice/external/rxjs'; -import { distinctUntilChanged, map, shareReplay } from '@umbraco-cms/backoffice/external/rxjs'; - -type ArrayToObservableTypes>> = { - [K in keyof T]: T[K] extends Observable ? U : never; -}; - -/** - * @export - * @method combineObservables - * @param {Array>} sources - an Array of RxJS Subjects to use for this Observable. - * @param {(mappable: Array) => R} mappingFunction - Method to return the part for this Observable to return. - * @param {(previousResult: R, currentResult: R) => boolean} [memoizationFunction] - Method to Compare if the data has changed. Should return true when data is different. - * @description - Creates a RxJS Observable from two other Observables. - */ - -export function combineObservables< - R, - SourceTypes extends Array>, - SourceTypeArray = ArrayToObservableTypes, ->( - sources: [...SourceTypes], - mappingFunction: MappingFunction, - memoizationFunction?: MemoizationFunction, -): Observable { - return combineLatest(sources).pipe( - map(mappingFunction as MappingFunction), - distinctUntilChanged(memoizationFunction || defaultMemoization), - shareReplay(1), - ); -} diff --git a/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/index.ts b/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/index.ts index fd1b6f1525..c0c329548a 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/index.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/index.ts @@ -1,9 +1,10 @@ export * from './append-to-frozen-array.function.js'; -export * from './combine-observables.function.js'; export * from './create-observable-part.function.js'; export * from './deep-freeze.function.js'; export * from './default-memoization.function.js'; export * from './filter-frozen-array.function.js'; +export * from './merge-observables.function.js'; export * from './naive-object-comparison.function.js'; +export * from './observe-multiple.function.js'; export * from './partial-update-frozen-array.function.js'; export * from './push-to-unique-array.function.js'; diff --git a/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/merge-observables.function.ts b/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/merge-observables.function.ts new file mode 100644 index 0000000000..30b28ea65f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/merge-observables.function.ts @@ -0,0 +1,44 @@ +import type { MemoizationFunction } from '../types/memoization-function.type.js'; +import type { MappingFunction } from '../types/mapping-function.type.js'; +import { defaultMemoization } from './default-memoization.function.js'; +import { type Observable, combineLatest } from '@umbraco-cms/backoffice/external/rxjs'; +import { distinctUntilChanged, map, shareReplay } from '@umbraco-cms/backoffice/external/rxjs'; + +type ArrayToObservableTypes>> = { + [K in keyof T]: T[K] extends Observable ? U : never; +}; + +/** + * @export + * @method mergeObservables + * @param {Array>} sources - an Array of Observables to merge for this Observable. + * @param {(mappable: Array) => R} mergeFunction - Merge method to return the part for this Observable to return. + * @param {(previousResult: R, currentResult: R) => boolean} [memoizationFunction] - Method to Compare if the merged data has changed. Should return true when data is different. + * @returns {Observable} - Returns a new Observable that combines the Observables into a single Observable. + * @description - Creates a Observable from two or more Observables. + * @example + * + * const mergedObservable = mergeObservables( + * [observable1, observable2], + * ([value1, value2]) => value1 + value2, + * ); + * + * if observable1 has the value of 10 and observable2 has the value of 4, the mergedObservable will return the value of 14. + * if one of them changes the mergedObservable will return the new value. + */ + +export function mergeObservables< + R, + SourceTypes extends Array>, + SourceTypeArray = ArrayToObservableTypes, +>( + sources: [...SourceTypes], + mergeFunction: MappingFunction, + memoizationFunction?: MemoizationFunction, +): Observable { + return combineLatest(sources).pipe( + map(mergeFunction as MappingFunction), + distinctUntilChanged(memoizationFunction || defaultMemoization), + shareReplay(1), + ); +} diff --git a/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/observe-multiple.function.ts b/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/observe-multiple.function.ts new file mode 100644 index 0000000000..8a4704a280 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/observe-multiple.function.ts @@ -0,0 +1,15 @@ +import { combineLatest } from '@umbraco-cms/backoffice/external/rxjs'; + +/** + * @export + * @method observeMultiple + * @param {Array>} sources - an Array of Observables to use for this combined observation. + * @description - combines multiple Observables into a single Observable that can be observed. + * @returns {Observable>} - Returns a new Observable that combines the Observables into a single Observable with the values of the given Observables in an Array with the same order as the Array of Observables. + * @example + * + * this.observe(observeMultiple([observable1, observable2]), ([value1, value2]) => { + * console.log(value1, value2); + * }); + */ +export const observeMultiple = combineLatest; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts index b05cea02f5..d1544fbc6e 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts @@ -706,6 +706,7 @@ export const data: Array = [ allowInAreas: true, rowMinSpan: 1, rowMaxSpan: 2, + inlineEditing: true, columnSpanOptions: [ { columnSpan: 1, diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/block-grid-area-config-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/block-grid-area-config-entry.element.ts index db0c7bc262..027a60dc36 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/block-grid-area-config-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/block-grid-area-config-entry.element.ts @@ -2,7 +2,7 @@ import { UmbBlockGridAreaConfigEntryContext } from './block-grid-area-config-ent import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { html, css, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; -import '../block-grid-block-view/index.js'; +import '../block-grid-block/index.js'; import '../block-scale-handler/index.js'; /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/block-grid-block-inline.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/block-grid-block-inline.element.ts new file mode 100644 index 0000000000..c1aacdc98e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/block-grid-block-inline.element.ts @@ -0,0 +1,68 @@ +import { UMB_BLOCK_GRID_ENTRY_CONTEXT } from '../../context/block-grid-entry.context-token.js'; +import { UmbBlockGridInlinePropertyDatasetContext } from './block-grid-inline-property-dataset.context.js'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; +import type { UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type'; +import type { UmbBlockViewUrlsPropType } from '@umbraco-cms/backoffice/block'; +import '../block-grid-areas-container/index.js'; +import '../ref-grid-block/index.js'; + +/** + * @element umb-block-grid-block-inline + */ +@customElement('umb-block-grid-block-inline') +export class UmbBlockGridBlockInlineElement extends UmbLitElement { + // + @property({ attribute: false }) + label?: string; + + @property({ attribute: false }) + urls?: UmbBlockViewUrlsPropType; + + @state() + _inlineProperty: UmbPropertyTypeModel | undefined; + + constructor() { + super(); + + this.consumeContext(UMB_BLOCK_GRID_ENTRY_CONTEXT, (context) => { + new UmbBlockGridInlinePropertyDatasetContext(this, context); + + this.observe( + context.firstPropertyType, + (property) => { + this._inlineProperty = property; + }, + 'inlineProperty', + ); + }); + } + + render() { + return html` + + + `; + } + + static styles = [ + css` + umb-block-grid-areas-container { + margin-top: calc(var(--uui-size-2) + 1px); + } + umb-block-grid-areas-container::part(area) { + margin: var(--uui-size-2); + } + `, + ]; +} + +export default UmbBlockGridBlockInlineElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-block-grid-block-inline': UmbBlockGridBlockInlineElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/block-grid-inline-property-dataset.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/block-grid-inline-property-dataset.context-token.ts new file mode 100644 index 0000000000..56cb885b93 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/block-grid-inline-property-dataset.context-token.ts @@ -0,0 +1,6 @@ +import type { UmbBlockGridInlinePropertyDatasetContext } from './block-grid-inline-property-dataset.context.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +// TODO: Add discriminator: +export const UMB_BLOCK_GRID_INLINE_PROPERTY_DATASET_CONTEXT = + new UmbContextToken('UmbPropertyDatasetContext'); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/block-grid-inline-property-dataset.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/block-grid-inline-property-dataset.context.ts new file mode 100644 index 0000000000..283d120415 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/block-grid-inline-property-dataset.context.ts @@ -0,0 +1,51 @@ +import type { UmbBlockGridEntryContext } from '../../context/block-grid-entry.context.js'; +import { UMB_BLOCK_GRID_INLINE_PROPERTY_DATASET_CONTEXT } from './block-grid-inline-property-dataset.context-token.js'; +import type { UmbPropertyDatasetContext } from '@umbraco-cms/backoffice/property'; +import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbBaseController } from '@umbraco-cms/backoffice/class-api'; +import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; +import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; + +export class UmbBlockGridInlinePropertyDatasetContext extends UmbBaseController implements UmbPropertyDatasetContext { + #entryContext: UmbBlockGridEntryContext; + + // default data: + + getVariantId() { + return UmbVariantId.CreateInvariant(); + } + getEntityType() { + return this.#entryContext.getEntityType(); + } + getUnique() { + return this.#entryContext.getEntityId(); + } + + getName(): string | undefined { + return 'TODO: get label'; + } + readonly name: Observable = 'TODO: get label observable' as any; + + constructor(host: UmbControllerHost, entryContext: UmbBlockGridEntryContext) { + // The controller alias, is a very generic name cause we want only one of these for this controller host. + super(host, UMB_PROPERTY_DATASET_CONTEXT.toString()); + this.#entryContext = entryContext; + + this.provideContext(UMB_BLOCK_GRID_INLINE_PROPERTY_DATASET_CONTEXT, this); + } + + /** + * TODO: Write proper JSDocs here. + */ + async propertyValueByAlias(propertyAlias: string) { + return await this.#entryContext.propertyValueByAlias(propertyAlias); + } + + /** + * TODO: Write proper JSDocs here. + */ + async setPropertyValue(propertyAlias: string, value: unknown) { + return this.#entryContext.setPropertyValue(propertyAlias, value); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/index.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/index.ts new file mode 100644 index 0000000000..3654873d8d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/index.ts @@ -0,0 +1 @@ +export * from './block-grid-block-inline.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-view/index.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-view/index.ts deleted file mode 100644 index ec5c93e6e1..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-view/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './block-grid-block-view.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-view/block-grid-block-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block/block-grid-block.element.ts similarity index 83% rename from src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-view/block-grid-block-view.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block/block-grid-block.element.ts index 5b46584014..1e95bc3460 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-view/block-grid-block-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block/block-grid-block.element.ts @@ -8,7 +8,7 @@ import type { UmbBlockViewUrlsPropType } from '@umbraco-cms/backoffice/block'; * @element umb-block-grid-block */ @customElement('umb-block-grid-block') -export class UmbRefGridBlockElement extends UmbLitElement { +export class UmbBlockGridBlockElement extends UmbLitElement { // @property({ attribute: false }) label?: string; @@ -24,9 +24,6 @@ export class UmbRefGridBlockElement extends UmbLitElement { static styles = [ css` - uui-ref-node { - min-height: var(--uui-size-16); - } umb-block-grid-areas-container { margin-top: calc(var(--uui-size-2) + 1px); } @@ -37,10 +34,10 @@ export class UmbRefGridBlockElement extends UmbLitElement { ]; } -export default UmbRefGridBlockElement; +export default UmbBlockGridBlockElement; declare global { interface HTMLElementTagNameMap { - 'umb-block-grid-block': UmbRefGridBlockElement; + 'umb-block-grid-block': UmbBlockGridBlockElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block/index.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block/index.ts new file mode 100644 index 0000000000..8afd0511df --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block/index.ts @@ -0,0 +1 @@ +export * from './block-grid-block.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.ts index c109befd22..36725cdb5e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.ts @@ -2,10 +2,11 @@ import { UmbBlockGridEntryContext } from '../../context/block-grid-entry.context import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { html, css, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; -import '../block-grid-block-view/index.js'; -import '../block-scale-handler/index.js'; import type { UmbBlockViewPropsType } from '@umbraco-cms/backoffice/block'; import type { UmbBlockGridLayoutModel } from '@umbraco-cms/backoffice/block-grid'; +import '../block-grid-block-inline/index.js'; +import '../block-grid-block/index.js'; +import '../block-scale-handler/index.js'; /** * @element umb-block-grid-entry @@ -78,11 +79,11 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper super(); // Misc: - this.observe(this.#context.showContentEdit, (showContentEdit) => { - this._showContentEdit = showContentEdit; + this.observe(this.#context.showContentEdit, (show) => { + this._showContentEdit = show; }); - this.observe(this.#context.settingsElementTypeKey, (settingsElementTypeKey) => { - this._hasSettings = !!settingsElementTypeKey; + this.observe(this.#context.settingsElementTypeKey, (key) => { + this._hasSettings = !!key; }); this.observe(this.#context.canScale, (canScale) => { this._canScale = canScale; @@ -91,6 +92,9 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper this.#updateBlockViewProps({ label }); this._label = label; }); + this.observe(this.#context.inlineEditingMode, (mode) => { + this._inlineEditingMode = mode; + }); // Data: this.observe(this.#context.layout, (layout) => { @@ -158,6 +162,12 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper }); } + #renderInlineEditBlock() { + return html``; + } + #renderRefBlock() { return html``; } @@ -171,7 +181,7 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper type="blockEditorCustomView" default-element="umb-block-grid-block" .props=${this._blockViewProps} - >${this.#renderRefBlock()}${this._inlineEditingMode ? this.#renderInlineEditBlock() : this.#renderRefBlock()} ${this._showContentEdit && this._workspaceEditContentPath diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-scale-handler/block-scale-handler.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-scale-handler/block-scale-handler.element.ts index c4488d81fa..92c048bbcf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-scale-handler/block-scale-handler.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-scale-handler/block-scale-handler.element.ts @@ -1,7 +1,7 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { html, css, customElement } from '@umbraco-cms/backoffice/external/lit'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; -import '../block-grid-block-view/index.js'; +import '../block-grid-block/index.js'; /** * @element umb-block-scale-handler diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entry.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entry.context.ts index 225e2932ce..819e70f08a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entry.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entry.context.ts @@ -6,15 +6,17 @@ import { UmbBlockGridScaleManager, } from './block-grid-scale-manager/block-grid-scale-manager.controller.js'; import { UmbBlockEntryContext } from '@umbraco-cms/backoffice/block'; +import type { UmbContentTypeModel, UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type'; import type { UmbBlockGridTypeModel, UmbBlockGridLayoutModel } from '@umbraco-cms/backoffice/block-grid'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbArrayState, UmbBooleanState, UmbNumberState, + UmbObjectState, appendToFrozenArray, + observeMultiple, } from '@umbraco-cms/backoffice/observable-api'; -import { combineLatest } from '@umbraco-cms/backoffice/external/rxjs'; export class UmbBlockGridEntryContext extends UmbBlockEntryContext< @@ -38,6 +40,7 @@ export class UmbBlockGridEntryContext const x = this._blockType.getValue(); return [x?.rowMinSpan ?? 1, x?.rowMaxSpan ?? 1]; } + readonly inlineEditingMode = this._blockType.asObservablePart((x) => x?.inlineEditing === true); #relevantColumnSpanOptions = new UmbArrayState([], (x) => x); readonly relevantColumnSpanOptions = this.#relevantColumnSpanOptions.asObservable(); @@ -53,6 +56,9 @@ export class UmbBlockGridEntryContext readonly showContentEdit = this._blockType.asObservablePart((x) => !x?.forceHideContentEditorInOverlay); + #firstPropertyType = new UmbObjectState(undefined); + readonly firstPropertyType = this.#firstPropertyType.asObservable(); + readonly scaleManager = new UmbBlockGridScaleManager(this); constructor(host: UmbControllerHost) { @@ -95,21 +101,8 @@ export class UmbBlockGridEntryContext if (!this._entries) return; const layoutColumns = this._entries.getLayoutColumns(); if (!layoutColumns) return; - /* - const oldColumnSpan = this._layout.getValue()?.columnSpan; - if (!oldColumnSpan) { - // Some fallback solution, to reset it so something that makes sense. - return; - } - */ columnSpan = Math.max(1, Math.min(columnSpan, layoutColumns)); - - /* - const columnSpanOptions = this.#relevantColumnSpanOptions.getValue(); - if (columnSpanOptions.length > 0) { - columnSpan = closestColumnSpanOption(columnSpan, columnSpanOptions, layoutColumns) ?? layoutColumns; - }*/ this._layout.update({ columnSpan }); } getColumnSpan() { @@ -127,19 +120,7 @@ export class UmbBlockGridEntryContext return this._layout.getValue()?.rowSpan; } - _gotManager() { - if (this._manager) { - /*this.observe( - this._manager.inlineEditingMode, - (inlineEditingMode) => { - this.#inlineEditingMode.setValue(inlineEditingMode); - }, - 'observeInlineEditingMode', - );*/ - } else { - //this.removeControllerByAlias('observeInlineEditingMode'); - } - } + _gotManager() {} _gotEntries() { this.scaleManager.setEntriesContext(this._entries); @@ -147,7 +128,7 @@ export class UmbBlockGridEntryContext if (!this._entries) return; this.observe( - combineLatest([this.minMaxRowSpan, this.columnSpanOptions, this._entries.layoutColumns]), + observeMultiple([this.minMaxRowSpan, this.columnSpanOptions, this._entries.layoutColumns]), ([minMaxRowSpan, columnSpanOptions, layoutColumns]) => { if (!layoutColumns) return; const relevantColumnSpanOptions = columnSpanOptions @@ -167,7 +148,7 @@ export class UmbBlockGridEntryContext ); this.observe( - combineLatest([this.areaTypeGridColumns, this._entries.layoutColumns]), + observeMultiple([this.areaTypeGridColumns, this._entries.layoutColumns]), ([areaTypeGridColumns, layoutColumns]) => { this.#areaGridColumns.setValue(areaTypeGridColumns ?? layoutColumns); }, @@ -175,7 +156,7 @@ export class UmbBlockGridEntryContext ); this.observe( - combineLatest([this.columnSpan, this.relevantColumnSpanOptions, this._entries.layoutColumns]), + observeMultiple([this.columnSpan, this.relevantColumnSpanOptions, this._entries.layoutColumns]), ([columnSpan, relevantColumnSpanOptions, layoutColumns]) => { if (!columnSpan || !layoutColumns) return; if (relevantColumnSpanOptions.length > 0) { @@ -197,4 +178,8 @@ export class UmbBlockGridEntryContext 'observeColumnSpanValidation', ); } + + _gotContentType(contentType: UmbContentTypeModel | undefined) { + this.#firstPropertyType.setValue(contentType?.properties[0]); + } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/types.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/types.ts index c4163bdd97..7925df893a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/types.ts @@ -13,6 +13,7 @@ export interface UmbBlockGridTypeModel extends UmbBlockTypeWithGroupKey { thumbnail?: string; areaGridColumns?: number; areas: Array; + inlineEditing?: boolean; } export type UmbBlockGridTypeColumnSpanOption = { diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-entry.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-entry.context.ts index c31356811a..f12e328afe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-entry.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-entry.context.ts @@ -2,7 +2,7 @@ import { UMB_BLOCK_LIST_MANAGER_CONTEXT } from './block-list-manager.context.js' import { UMB_BLOCK_LIST_ENTRIES_CONTEXT } from './block-list-entries.context-token.js'; import { UmbBlockEntryContext } from '@umbraco-cms/backoffice/block'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UmbBooleanState, combineObservables } from '@umbraco-cms/backoffice/observable-api'; +import { UmbBooleanState, mergeObservables } from '@umbraco-cms/backoffice/observable-api'; export class UmbBlockListEntryContext extends UmbBlockEntryContext< typeof UMB_BLOCK_LIST_MANAGER_CONTEXT, typeof UMB_BLOCK_LIST_MANAGER_CONTEXT.TYPE, @@ -15,7 +15,7 @@ export class UmbBlockListEntryContext extends UmbBlockEntryContext< (x) => !!x?.forceHideContentEditorInOverlay, ); - readonly showContentEdit = combineObservables( + readonly showContentEdit = mergeObservables( [this.forceHideContentEditorInOverlay, this.inlineEditingMode], ([forceHide, inlineMode]): boolean => { return !forceHide && !inlineMode; @@ -41,4 +41,6 @@ export class UmbBlockListEntryContext extends UmbBlockEntryContext< } _gotEntries() {} + + _gotContentType() {} } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts index d5be2fe1f0..323889f06a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts @@ -8,6 +8,7 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbNumberState, UmbObjectState, UmbStringState } from '@umbraco-cms/backoffice/observable-api'; import { encodeFilePath } from '@umbraco-cms/backoffice/utils'; import { UMB_CONFIRM_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import type { UmbContentTypeModel } from '@umbraco-cms/backoffice/content-type'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; export abstract class UmbBlockEntryContext< @@ -29,6 +30,23 @@ export abstract class UmbBlockEntryContext< #contentUdi?: string; + // Workspace alike methods, to enables editing of data without the need of a workspace (Custom views and block grid inline editing mode for example). + getEntityType() { + return 'block'; + } + getEntityId() { + return this.getContentUdi(); + } + propertyValueByAlias(propertyAlias: string) { + return this.#content.asObservablePart((x) => x?.[propertyAlias] as ReturnType | undefined); + } + setPropertyValue(propertyAlias: string, value: unknown) { + this.#content.setValue({ + ...this.#content.getValue()!, + [propertyAlias]: value, + }); + } + #index = new UmbNumberState(undefined); readonly index = this.#index.asObservable(); getIndex() { @@ -279,10 +297,14 @@ export abstract class UmbBlockEntryContext< (contentType) => { this.#contentElementTypeAlias.setValue(contentType?.alias); this.#contentElementTypeName.setValue(contentType?.name); + this._gotContentType(contentType); }, 'observeContentElementType', ); } + + abstract _gotContentType(contentType: UmbContentTypeModel | undefined): void; + #observeBlockType() { if (!this._manager) return; const contentTypeKey = this.#content.value?.contentTypeKey; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts index 17c5023ab9..03d5ec7a0e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts @@ -101,26 +101,29 @@ export abstract class UmbBlockManagerContext< 'observePropertyVariantId', ); }); + + this.observe(this.blockTypes, (blockTypes) => { + blockTypes.forEach((x) => { + this.#ensureContentType(x.contentElementTypeKey); + if (x.settingsElementTypeKey) { + this.#ensureContentType(x.settingsElementTypeKey); + } + }); + }); } - async ensureContentType(unique?: string) { - if (!unique) return; + async #ensureContentType(unique: string) { if (this.#contentTypes.getValue().find((x) => x.unique === unique)) return; - const contentType = await this.#loadContentType(unique); - return contentType; - } - - async #loadContentType(unique?: string) { - if (!unique) return {}; const { data } = await this.#contentTypeRepository.requestByUnique(unique); - if (!data) return {}; + if (!data) { + this.#contentTypes.removeOne(unique); + return; + } // We could have used the global store of Document Types, but to ensure we first react ones the latest is loaded then we have our own local store: - // TODO: Revisit if this is right to do. Notice this can potentially be proxied to the global store. + // TODO: Revisit if this is right to do. Notice this can potentially be proxied to the global store. In that way we do not need to observe and we can just use the global store for data. this.#contentTypes.appendOne(data); - - return data; } contentTypeOf(contentTypeKey: string) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/modals/block-catalogue/block-catalogue-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/modals/block-catalogue/block-catalogue-modal.element.ts index 0d45a39709..7935c14d6f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/modals/block-catalogue/block-catalogue-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/modals/block-catalogue/block-catalogue-modal.element.ts @@ -7,6 +7,8 @@ import { UmbModalBaseElement, UmbModalRouteRegistrationController, } from '@umbraco-cms/backoffice/modal'; +// TODO: This is across packages, how should we go about getting just a single element from another package? like here we just need the umb-block-type-card element +import '@umbraco-cms/backoffice/block-type'; @customElement('umb-block-catalogue-modal') export class UmbBlockCatalogueModalElement extends UmbModalBaseElement<