From 5b6c5174c2d7780740b20dd0c32c0c23b80751df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Sat, 24 Feb 2024 20:49:59 +0100 Subject: [PATCH 1/7] import --- .../modals/block-catalogue/block-catalogue-modal.element.ts | 2 ++ 1 file changed, 2 insertions(+) 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< From 590b87efa61d71f2d11a2fed9a3e850c93e40078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Sat, 24 Feb 2024 20:51:18 +0100 Subject: [PATCH 2/7] remove log --- .../components/block-type-card/block-type-card.element.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts index fa4093cf82..d1f317c37d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts @@ -55,7 +55,6 @@ export class UmbBlockTypeCardElement extends UmbLitElement { this.observe(this.#itemManager.items, (items) => { const item = items[0]; if (item) { - console.log('got item', item); this._fallbackIcon = item.icon; this._fallbackName = item.name; } From 9aa8ac93890cdbb653211a446e5fa131b6dcad47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Sat, 24 Feb 2024 22:24:26 +0100 Subject: [PATCH 3/7] inlineEditingMode --- .../context/block-grid-entry.context.ts | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) 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..c332673a83 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 @@ -38,6 +38,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(); @@ -127,19 +128,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); From 8112fb80ec7c2101025db5c281fdf25a2e6d5078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Sat, 24 Feb 2024 22:30:31 +0100 Subject: [PATCH 4/7] mergeObservables + a helper method for observing multiple observables --- .../utils/combine-observables.function.ts | 34 -------------- .../src/libs/observable-api/utils/index.ts | 3 +- .../utils/merge-observables.function.ts | 44 +++++++++++++++++++ .../utils/observe-multiple.function.ts | 9 ++++ .../context/block-grid-entry.context.ts | 8 ++-- .../src/packages/block/block-grid/types.ts | 1 + .../context/block-list-entry.context.ts | 4 +- 7 files changed, 62 insertions(+), 41 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/combine-observables.function.ts create mode 100644 src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/merge-observables.function.ts create mode 100644 src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/observe-multiple.function.ts 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..0f047eb9f7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/observe-multiple.function.ts @@ -0,0 +1,9 @@ +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. + */ +export const observeMultiple = combineLatest; 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 c332673a83..dacedcc710 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 @@ -13,8 +13,8 @@ import { UmbBooleanState, UmbNumberState, appendToFrozenArray, + observeMultiple, } from '@umbraco-cms/backoffice/observable-api'; -import { combineLatest } from '@umbraco-cms/backoffice/external/rxjs'; export class UmbBlockGridEntryContext extends UmbBlockEntryContext< @@ -136,7 +136,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 @@ -156,7 +156,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); }, @@ -164,7 +164,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) { 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..fd24e45a02 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; From a92b6808fdc1ce6953309f85479eff0711a47494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Sat, 24 Feb 2024 22:32:26 +0100 Subject: [PATCH 5/7] append an example --- .../libs/observable-api/utils/observe-multiple.function.ts | 6 ++++++ 1 file changed, 6 insertions(+) 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 index 0f047eb9f7..8a4704a280 100644 --- 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 @@ -5,5 +5,11 @@ import { combineLatest } from '@umbraco-cms/backoffice/external/rxjs'; * @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; From 727bc3779b6c5ec901f28fa00e3ad5f455b4ef9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Sat, 24 Feb 2024 22:33:00 +0100 Subject: [PATCH 6/7] clean up --- .../block-grid/context/block-grid-entry.context.ts | 13 ------------- 1 file changed, 13 deletions(-) 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 dacedcc710..d0e874d2ff 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 @@ -96,21 +96,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() { From 9f39a8f5b3584ac415c39e890acfc660794e8fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Sat, 24 Feb 2024 23:24:14 +0100 Subject: [PATCH 7/7] inline editing mode --- .../mocks/data/data-type/data-type.data.ts | 1 + .../block-grid-area-config-entry.element.ts | 2 +- .../block-grid-block-inline.element.ts | 68 +++++++++++++++++++ ...d-inline-property-dataset.context-token.ts | 6 ++ ...ck-grid-inline-property-dataset.context.ts | 51 ++++++++++++++ .../block-grid-block-inline/index.ts | 1 + .../components/block-grid-block-view/index.ts | 1 - .../block-grid-block.element.ts} | 9 +-- .../components/block-grid-block/index.ts | 1 + .../block-grid-entry.element.ts | 24 +++++-- .../block-scale-handler.element.ts | 2 +- .../context/block-grid-entry.context.ts | 9 +++ .../context/block-list-entry.context.ts | 2 + .../block/context/block-entry.context.ts | 22 ++++++ .../block/context/block-manager.context.ts | 27 ++++---- 15 files changed, 198 insertions(+), 28 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/block-grid-block-inline.element.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/block-grid-inline-property-dataset.context-token.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/block-grid-inline-property-dataset.context.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/index.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-view/index.ts rename src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/{block-grid-block-view/block-grid-block-view.element.ts => block-grid-block/block-grid-block.element.ts} (83%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block/index.ts 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 2b4f4af2b3..9e10a05344 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 @@ -678,6 +678,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 d0e874d2ff..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,12 +6,14 @@ 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'; @@ -54,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) { @@ -173,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-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 fd24e45a02..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 @@ -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) {