Skip to content

Commit e7665ce

Browse files
authored
Merge pull request #1865 from umbraco/feature/block-grid-create-inline-buttons
Feat: Block Grid: create before and after buttons
2 parents 6edb36b + 382434d commit e7665ce

File tree

9 files changed

+153
-61
lines changed

9 files changed

+153
-61
lines changed

src/libs/observable-api/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export * from './json-string-comparison.function.js';
88
export * from './merge-observables.function.js';
99
export * from './observe-multiple.function.js';
1010
export * from './partial-update-frozen-array.function.js';
11+
export * from './push-at-to-unique-array.function.js';
1112
export * from './push-to-unique-array.function.js';
1213
export * from './simple-hash-code.function.js';
1314
export * from './strict-equality-memoization.function.js';

src/packages/block/block-grid/components/block-grid-entries/block-grid-entries.element.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ export class UmbBlockGridEntriesElement extends UmbLitElement {
208208
(layoutEntry, index) =>
209209
html`<umb-block-grid-entry
210210
class="umb-block-grid__layout-item"
211-
.index=${index}
211+
index=${index}
212212
.contentUdi=${layoutEntry.contentUdi}
213213
.layout=${layoutEntry}>
214214
</umb-block-grid-entry>`,

src/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.ts

Lines changed: 97 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { UmbBlockGridEntryContext } from '../../context/block-grid-entry.context.js';
22
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
33
import { html, css, customElement, property, state, nothing } from '@umbraco-cms/backoffice/external/lit';
4+
import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit';
45
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
56
import type { UmbBlockViewPropsType } from '@umbraco-cms/backoffice/block';
67
import type { UmbBlockGridLayoutModel } from '@umbraco-cms/backoffice/block-grid';
@@ -14,7 +15,7 @@ import '../block-scale-handler/index.js';
1415
@customElement('umb-block-grid-entry')
1516
export class UmbBlockGridEntryElement extends UmbLitElement implements UmbPropertyEditorUiElement {
1617
//
17-
@property({ type: Number })
18+
@property({ type: Number, reflect: true })
1819
public get index(): number | undefined {
1920
return this.#context.getIndex();
2021
}
@@ -37,6 +38,7 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper
3738
//
3839

3940
#context = new UmbBlockGridEntryContext(this);
41+
#renderTimeout: number | undefined;
4042

4143
@state()
4244
_columnSpan?: number;
@@ -51,7 +53,9 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper
5153

5254
// If _createPath is undefined, its because no blocks are allowed to be created here[NL]
5355
@state()
54-
_createPath?: string;
56+
_createBeforePath?: string;
57+
@state()
58+
_createAfterPath?: string;
5559

5660
@state()
5761
_label = '';
@@ -68,6 +72,13 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper
6872
@state()
6973
_canScale?: boolean;
7074

75+
@state()
76+
_showInlineCreateBefore?: boolean;
77+
@state()
78+
_showInlineCreateAfter?: boolean;
79+
@state()
80+
_inlineCreateAboveWidth?: string;
81+
7182
// TODO: use this type on the Element Interface for the Manifest.
7283
@state()
7384
_blockViewProps: UmbBlockViewPropsType<UmbBlockGridLayoutModel> = { contentUdi: undefined!, urls: {} }; // Set to undefined cause it will be set before we render.
@@ -110,10 +121,15 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper
110121
});
111122

112123
// Paths:
113-
this.observe(this.#context.createPath, (createPath) => {
114-
const oldValue = this._createPath;
115-
this._createPath = createPath;
116-
this.requestUpdate('_createPath', oldValue);
124+
this.observe(this.#context.createBeforePath, (createPath) => {
125+
//const oldValue = this._createBeforePath;
126+
this._createBeforePath = createPath;
127+
//this.requestUpdate('_createPath', oldValue);
128+
});
129+
this.observe(this.#context.createAfterPath, (createPath) => {
130+
//const oldValue = this._createAfterPath;
131+
this._createAfterPath = createPath;
132+
//this.requestUpdate('_createPath', oldValue);
117133
});
118134
this.observe(this.#context.workspaceEditContentPath, (path) => {
119135
this._workspaceEditContentPath = path;
@@ -156,8 +172,52 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper
156172
this.setAttribute('data-content-element-type-alias', contentElementTypeAlias);
157173
}
158174
});
175+
176+
this.#callUpdateInlineCreateButtons();
159177
}
160178

179+
protected updated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
180+
super.updated(_changedProperties);
181+
if (_changedProperties.has('_blockViewProps') || _changedProperties.has('_columnSpan')) {
182+
this.#callUpdateInlineCreateButtons();
183+
}
184+
}
185+
186+
#callUpdateInlineCreateButtons() {
187+
clearTimeout(this.#renderTimeout);
188+
this.#renderTimeout = setTimeout(this.#updateInlineCreateButtons, 100) as unknown as number;
189+
}
190+
191+
#updateInlineCreateButtons = () => {
192+
// TODO: Could we optimize this, so it wont break?, cause currently we trust blindly that parentElement is '.umb-block-grid__layout-container' [NL]
193+
const layoutContainer = this.parentElement;
194+
if (!layoutContainer) return;
195+
const layoutContainerRect = layoutContainer.getBoundingClientRect();
196+
197+
if (layoutContainerRect.width === 0) {
198+
this._showInlineCreateBefore = false;
199+
this._showInlineCreateAfter = false;
200+
this._inlineCreateAboveWidth = undefined;
201+
this.#renderTimeout = setTimeout(this.#updateInlineCreateButtons, 100) as unknown as number;
202+
return;
203+
}
204+
205+
const layoutItemRect = this.getBoundingClientRect();
206+
if (layoutItemRect.right > layoutContainerRect.right - 5) {
207+
this._showInlineCreateAfter = false;
208+
} else {
209+
this._showInlineCreateAfter = true;
210+
}
211+
212+
if (layoutItemRect.left > layoutContainerRect.left + 5) {
213+
this._showInlineCreateBefore = false;
214+
this._inlineCreateAboveWidth = undefined;
215+
} else {
216+
this._inlineCreateAboveWidth = getComputedStyle(layoutContainer).width;
217+
this._showInlineCreateBefore = true;
218+
}
219+
};
220+
161221
#renderInlineEditBlock() {
162222
return html`<umb-block-grid-block-inline
163223
.contentUdi=${this.contentUdi}
@@ -171,8 +231,13 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper
171231
#renderBlock() {
172232
return this.contentUdi
173233
? html`
174-
${this._createPath
175-
? html`<uui-button-inline-create href=${this._createPath}></uui-button-inline-create>`
234+
${this._createBeforePath && this._showInlineCreateBefore
235+
? html`<uui-button-inline-create
236+
href=${this._createBeforePath}
237+
label=${this.localize.term('blockEditor_addBlock')}
238+
style=${this._inlineCreateAboveWidth
239+
? `width: ${this._inlineCreateAboveWidth}`
240+
: ''}></uui-button-inline-create>`
176241
: nothing}
177242
<div class="umb-block-grid__block" part="umb-block-grid__block">
178243
<umb-extension-slot
@@ -204,6 +269,12 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper
204269
</umb-block-scale-handler>`
205270
: nothing}
206271
</div>
272+
${this._createAfterPath && this._showInlineCreateAfter
273+
? html`<uui-button-inline-create
274+
vertical
275+
label=${this.localize.term('blockEditor_addBlock')}
276+
href=${this._createAfterPath}></uui-button-inline-create>`
277+
: nothing}
207278
`
208279
: nothing;
209280
}
@@ -223,6 +294,24 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper
223294
top: var(--uui-size-2);
224295
right: var(--uui-size-2);
225296
}
297+
uui-button-inline-create {
298+
top: 0px;
299+
position: absolute;
300+
301+
// Avoid showing inline-create in dragging-mode
302+
--umb-block-grid__block--inline-create-button-display--condition: var(--umb-block-grid--dragging-mode) none;
303+
display: var(--umb-block-grid__block--inline-create-button-display--condition);
304+
}
305+
uui-button-inline-create:not([vertical]) {
306+
left: 0;
307+
width: var(--umb-block-grid-editor--inline-create-width, 100%);
308+
}
309+
:host(:not([index='0'])) uui-button-inline-create:not([vertical]) {
310+
top: calc(var(--umb-block-grid--row-gap, 0px) * -0.5);
311+
}
312+
uui-button-inline-create[vertical] {
313+
right: calc(1px - (var(--umb-block-grid--column-gap, 0px) * 0.5));
314+
}
226315
227316
:host([drag-placeholder]) {
228317
opacity: 0.2;

src/packages/block/block-grid/context/block-grid-entry.context.ts

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,12 @@ export class UmbBlockGridEntryContext
102102

103103
columnSpan = this.#calcColumnSpan(columnSpan, this.getRelevantColumnSpanOptions(), layoutColumns);
104104
if (columnSpan === this.getColumnSpan()) return;
105-
//this._layout.update({ columnSpan });
106-
//const contentUdi = this.getContentUdi();
107-
//if (!contentUdi) return;
108-
//this._manager?.updateLayout({ contentUdi, columnSpan });
109-
this._layout.update({ columnSpan });
105+
const layoutValue = this._layout.getValue();
106+
if (!layoutValue) return;
107+
this._layout.setValue({
108+
...layoutValue,
109+
columnSpan,
110+
});
110111
}
111112
/**
112113
* Get the column span of this entry.
@@ -125,11 +126,12 @@ export class UmbBlockGridEntryContext
125126
if (!minMax) return;
126127
rowSpan = Math.max(minMax[0], Math.min(rowSpan, minMax[1]));
127128
if (rowSpan === this.getRowSpan()) return;
128-
//this._layout.update({ rowSpan });
129-
//const contentUdi = this.getContentUdi();
130-
//if (!contentUdi) return;
131-
//this._manager?.updateLayout({ contentUdi, rowSpan });
132-
this._layout.update({ rowSpan });
129+
const layoutValue = this._layout.getValue();
130+
if (!layoutValue) return;
131+
this._layout.setValue({
132+
...layoutValue,
133+
rowSpan,
134+
});
133135
}
134136
/**
135137
* Get the row span of this entry.
@@ -145,7 +147,6 @@ export class UmbBlockGridEntryContext
145147

146148
_gotEntries() {
147149
this.scaleManager.setEntriesContext(this._entries);
148-
149150
if (!this._entries) return;
150151

151152
this.#gotEntriesAndManager();
@@ -185,23 +186,17 @@ export class UmbBlockGridEntryContext
185186
if (!this._entries || !this._manager) return;
186187

187188
// Secure areas fits options:
188-
// NOLZ
189189
this.observe(
190190
observeMultiple([this.areas, this.layoutAreas]),
191191
([areas, layoutAreas]) => {
192192
if (!areas || !layoutAreas) return;
193193
const areasAreIdentical =
194194
areas.length === layoutAreas.length && areas.every((area) => layoutAreas.some((y) => y.key === area.key));
195195
if (areasAreIdentical === false) {
196-
/*
197-
const contentUdi = this.getContentUdi();
198-
if (!contentUdi) return;
199-
this._manager?.updateLayout({
200-
contentUdi,
201-
areas: layoutAreas.map((x) => (areas.find((y) => y.key === x.key) ? x : { key: x.key, items: [] })),
202-
});
203-
*/
204-
this._layout.update({
196+
const layoutValue = this._layout.getValue();
197+
if (!layoutValue) return;
198+
this._layout.setValue({
199+
...layoutValue,
205200
areas: layoutAreas.map((x) => (areas.find((y) => y.key === x.key) ? x : { key: x.key, items: [] })),
206201
});
207202
}
@@ -210,7 +205,6 @@ export class UmbBlockGridEntryContext
210205
);
211206

212207
// Secure columnSpan fits options:
213-
// NOLZ
214208
this.observe(
215209
observeMultiple([this.layout, this.columnSpan, this.relevantColumnSpanOptions, this._entries.layoutColumns]),
216210
([layout, columnSpan, relevantColumnSpanOptions, layoutColumns]) => {
@@ -221,27 +215,30 @@ export class UmbBlockGridEntryContext
221215
layoutColumns,
222216
);
223217
if (newColumnSpan !== columnSpan) {
224-
//const contentUdi = this.getContentUdi();
225-
//if (!contentUdi) return;
226-
//this._manager?.updateLayout({ contentUdi, columnSpan: newColumnSpan });
227-
this._layout.update({ columnSpan: newColumnSpan });
218+
const layoutValue = this._layout.getValue();
219+
if (!layoutValue) return;
220+
this._layout.setValue({
221+
...layoutValue,
222+
columnSpan: newColumnSpan,
223+
});
228224
}
229225
},
230226
'observeColumnSpanValidation',
231227
);
232228

233229
// Secure rowSpan fits options:
234-
// NOLZ
235230
this.observe(
236231
observeMultiple([this.minMaxRowSpan, this.rowSpan]),
237232
([minMax, rowSpan]) => {
238233
if (minMax) {
239234
const newRowSpan = Math.max(minMax[0], Math.min(rowSpan ?? 1, minMax[1]));
240235
if (newRowSpan !== rowSpan) {
241-
//const contentUdi = this.getContentUdi();
242-
//if (!contentUdi) return;
243-
//this._manager!.updateLayout({ contentUdi, rowSpan: newRowSpan });
244-
this._layout.update({ rowSpan: newRowSpan });
236+
const layoutValue = this._layout.getValue();
237+
if (!layoutValue) return;
238+
this._layout.setValue({
239+
...layoutValue,
240+
rowSpan: newRowSpan,
241+
});
245242
}
246243
}
247244
},

src/packages/block/block-grid/context/block-grid-manager.context.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { UmbBlockGridLayoutModel, UmbBlockGridTypeModel } from '../types.js';
22
import type { UmbBlockGridWorkspaceData } from '../index.js';
33
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
4-
import { UmbArrayState, appendToFrozenArray, partialUpdateFrozenArray } from '@umbraco-cms/backoffice/observable-api';
4+
import { UmbArrayState, appendToFrozenArray, pushAtToUniqueArray } from '@umbraco-cms/backoffice/observable-api';
55
import { type UmbBlockDataType, UmbBlockManagerContext } from '@umbraco-cms/backoffice/block';
66
import type { UmbBlockTypeGroup } from '@umbraco-cms/backoffice/block-type';
77

@@ -71,7 +71,10 @@ export class UmbBlockGridManagerContext<
7171
// Append the layout entry to be inserted and unfreeze the rest of the data:
7272
const areas = currentEntry.areas.map((x) =>
7373
x.key === areaKey
74-
? { ...x, items: appendToFrozenArray(x.items, insert, (x) => x.contentUdi === insert.contentUdi) }
74+
? {
75+
...x,
76+
items: pushAtToUniqueArray([...x.items], insert, (x) => x.contentUdi === insert.contentUdi, index),
77+
}
7578
: x,
7679
);
7780
return appendToFrozenArray(

src/packages/block/block-grid/context/block-grid-scale-manager/block-grid-scale-manager.controller.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,11 @@ export class UmbBlockGridScaleManager extends UmbControllerBase {
128128

129129
let newColumnSpan = Math.max(blockEndCol - blockStartCol, 1);
130130

131-
const spanOptions = this._host.getRelevantColumnSpanOptions();
132-
if (!spanOptions) return;
131+
const columnOptions = this._host.getRelevantColumnSpanOptions();
132+
if (!columnOptions) return;
133133

134134
// Find nearest allowed Column:
135-
const bestColumnSpanOption = closestColumnSpanOption(newColumnSpan, spanOptions, layoutColumns - blockStartCol);
135+
const bestColumnSpanOption = closestColumnSpanOption(newColumnSpan, columnOptions, layoutColumns - blockStartCol);
136136
newColumnSpan = bestColumnSpanOption ?? layoutColumns;
137137

138138
// Find allowed row spans:

src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement
176176
(x) => x.contentUdi,
177177
(layoutEntry, index) =>
178178
html`<uui-button-inline-create
179+
label=${this._createButtonLabel}
179180
href=${this._catalogueRouteBuilder?.({ view: 'create', index: index }) ?? ''}></uui-button-inline-create>
180181
<umb-block-list-entry .contentUdi=${layoutEntry.contentUdi} .layout=${layoutEntry}>
181182
</umb-block-list-entry> `,

src/packages/block/block/context/block-entries.context.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,11 @@ export abstract class UmbBlockEntriesContext<
121121
}
122122

123123
if (layout.settingsUdi) {
124-
this._manager?.removeOneSettings(layout.settingsUdi);
124+
this._manager!.removeOneSettings(layout.settingsUdi);
125125
}
126+
this._manager!.removeOneContent(contentUdi);
126127

127-
this._manager?.removeOneContent(contentUdi);
128-
129-
//this._layoutEntries.removeOne(contentUdi);
130-
alert('TODO: Remove layout entry..');
128+
this._layoutEntries.removeOne(contentUdi);
131129
}
132130
//copy
133131
}

0 commit comments

Comments
 (0)