Skip to content

Commit

Permalink
Merge pull request #2215 from umbraco/v14/bugfix/block-list-inline-ed…
Browse files Browse the repository at this point in the history
…iting

Fix: inline editing mode for Block List Editor
  • Loading branch information
nielslyngsoe authored Aug 21, 2024
2 parents 70343dc + 9b4fe92 commit 1c8c812
Show file tree
Hide file tree
Showing 35 changed files with 426 additions and 201 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { UMB_BLOCK_CATALOGUE_MODAL, UmbBlockEntriesContext } from '../../block/i
import {
UMB_BLOCK_GRID_ENTRY_CONTEXT,
UMB_BLOCK_GRID_WORKSPACE_MODAL,
type UmbBlockGridWorkspaceData,
type UmbBlockGridWorkspaceOriginData,
} from '../index.js';
import type { UmbBlockGridLayoutModel, UmbBlockGridTypeAreaType, UmbBlockGridTypeModel } from '../types.js';
import { UMB_BLOCK_GRID_MANAGER_CONTEXT } from './block-grid-manager.context-token.js';
Expand All @@ -19,13 +19,17 @@ export class UmbBlockGridEntriesContext
typeof UMB_BLOCK_GRID_MANAGER_CONTEXT,
typeof UMB_BLOCK_GRID_MANAGER_CONTEXT.TYPE,
UmbBlockGridTypeModel,
UmbBlockGridLayoutModel
UmbBlockGridLayoutModel,
UmbBlockGridWorkspaceOriginData
>
implements UmbBlockGridScalableContainerContext
{
//
#catalogueModal: UmbModalRouteRegistrationController<typeof UMB_BLOCK_CATALOGUE_MODAL.DATA, undefined>;
#workspaceModal: UmbModalRouteRegistrationController;
#catalogueModal: UmbModalRouteRegistrationController<
typeof UMB_BLOCK_CATALOGUE_MODAL.DATA,
typeof UMB_BLOCK_CATALOGUE_MODAL.VALUE
>;
#workspaceModal;

#parentEntry?: typeof UMB_BLOCK_GRID_ENTRY_CONTEXT.TYPE;

Expand Down Expand Up @@ -128,7 +132,7 @@ export class UmbBlockGridEntriesContext
blocks: this.#allowedBlockTypes.getValue(),
blockGroups: this._manager?.getBlockGroups() ?? [],
openClipboard: routingInfo.view === 'clipboard',
blockOriginData: { index: index, areaKey: this.#areaKey, parentUnique: this.#parentUnique },
originData: { index: index, areaKey: this.#areaKey, parentUnique: this.#parentUnique },
},
};
})
Expand Down Expand Up @@ -312,10 +316,10 @@ export class UmbBlockGridEntriesContext
async create(
contentElementTypeKey: string,
partialLayoutEntry?: Omit<UmbBlockGridLayoutModel, 'contentUdi'>,
modalData?: UmbBlockGridWorkspaceData,
originData?: UmbBlockGridWorkspaceOriginData,
) {
await this._retrieveManager;
return this._manager?.create(contentElementTypeKey, partialLayoutEntry, modalData);
return this._manager?.create(contentElementTypeKey, partialLayoutEntry, originData);
}

// insert Block?
Expand All @@ -324,11 +328,11 @@ export class UmbBlockGridEntriesContext
layoutEntry: UmbBlockGridLayoutModel,
content: UmbBlockDataType,
settings: UmbBlockDataType | undefined,
modalData: UmbBlockGridWorkspaceData,
originData: UmbBlockGridWorkspaceOriginData,
) {
await this._retrieveManager;
// TODO: Insert layout entry at the right spot.
return this._manager?.insert(layoutEntry, content, settings, modalData) ?? false;
return this._manager?.insert(layoutEntry, content, settings, originData) ?? false;
}

// create Block?
Expand Down
24 changes: 12 additions & 12 deletions src/packages/block/block-grid/context/block-grid-manager.context.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { UmbBlockGridLayoutModel, UmbBlockGridTypeModel } from '../types.js';
import type { UmbBlockGridWorkspaceData } from '../index.js';
import type { UmbBlockGridWorkspaceOriginData } from '../index.js';
import { UmbArrayState, appendToFrozenArray, pushAtToUniqueArray } from '@umbraco-cms/backoffice/observable-api';
import { removeLastSlashFromPath, transformServerPathToClientPath } from '@umbraco-cms/backoffice/utils';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
Expand All @@ -16,7 +16,7 @@ export const UMB_BLOCK_GRID_DEFAULT_LAYOUT_STYLESHEET = '/umbraco/backoffice/css
*/
export class UmbBlockGridManagerContext<
BlockLayoutType extends UmbBlockGridLayoutModel = UmbBlockGridLayoutModel,
> extends UmbBlockManagerContext<UmbBlockGridTypeModel, UmbBlockGridLayoutModel> {
> extends UmbBlockManagerContext<UmbBlockGridTypeModel, UmbBlockGridLayoutModel, UmbBlockGridWorkspaceOriginData> {
//
#initAppUrl: Promise<void>;
#appUrl?: string;
Expand Down Expand Up @@ -74,9 +74,9 @@ export class UmbBlockGridManagerContext<
create(
contentElementTypeKey: string,
partialLayoutEntry?: Omit<BlockLayoutType, 'contentUdi'>,
// TODO: [v15] Ignore unused parameter to avoid breaking changes
// This property is used by some implementations, but not used in this.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
modalData?: UmbBlockGridWorkspaceData,
originData?: UmbBlockGridWorkspaceOriginData,
) {
return super.createBlockData(contentElementTypeKey, partialLayoutEntry);
}
Expand Down Expand Up @@ -165,24 +165,24 @@ export class UmbBlockGridManagerContext<
layoutEntry: BlockLayoutType,
content: UmbBlockDataType,
settings: UmbBlockDataType | undefined,
modalData: UmbBlockGridWorkspaceData,
originData: UmbBlockGridWorkspaceOriginData,
) {
this.setOneLayout(layoutEntry, modalData);
this.insertBlockData(layoutEntry, content, settings, modalData);
this.setOneLayout(layoutEntry, originData);
this.insertBlockData(layoutEntry, content, settings, originData);

return true;
}

override setOneLayout(layoutEntry: BlockLayoutType, modalData?: UmbBlockGridWorkspaceData) {
const index = modalData?.originData.index ?? -1;
override setOneLayout(layoutEntry: BlockLayoutType, originData?: UmbBlockGridWorkspaceOriginData) {
const index = originData?.index ?? -1;

if (modalData?.originData.parentUnique && modalData?.originData.areaKey) {
if (originData?.parentUnique && originData?.areaKey) {
// Find layout entry based on parentUnique, recursively, as it needs to check layout of areas as well:
const layoutEntries = this.#appendLayoutEntryToArea(
layoutEntry,
this._layouts.getValue(),
modalData.originData.parentUnique,
modalData.originData.areaKey,
originData?.parentUnique,
originData?.areaKey,
index,
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { UmbBlockWorkspaceData } from '@umbraco-cms/backoffice/block';
import type { UmbBlockWorkspaceData, UmbBlockWorkspaceOriginData } from '@umbraco-cms/backoffice/block';
import type { UmbWorkspaceModalData, UmbWorkspaceModalValue } from '@umbraco-cms/backoffice/modal';
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';

export interface UmbBlockGridWorkspaceOriginData {
export interface UmbBlockGridWorkspaceOriginData extends UmbBlockWorkspaceOriginData {
index: number;
parentUnique: string | null;
areaKey?: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { UmbBlockListEntryContext } from '../../context/block-list-entry.context.js';
import { UMB_BLOCK_LIST, type UmbBlockListLayoutModel } from '../../types.js';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { html, css, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
import { html, css, customElement, property, state, nothing } from '@umbraco-cms/backoffice/external/lit';
import type {
ManifestBlockEditorCustomView,
UmbBlockEditorCustomViewProperties,
Expand All @@ -8,8 +10,6 @@ import type {
import '../ref-list-block/index.js';
import '../inline-list-block/index.js';
import { stringOrStringArrayContains } from '@umbraco-cms/backoffice/utils';
import { UmbBlockListEntryContext } from '../../context/block-list-entry.context.js';
import { UMB_BLOCK_LIST, type UmbBlockListLayoutModel } from '../../types.js';
import { UmbObserveValidationStateController } from '@umbraco-cms/backoffice/validation';
import { UmbDataPathBlockElementDataQuery } from '@umbraco-cms/backoffice/block';

Expand Down Expand Up @@ -234,11 +234,11 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper
};

#renderRefBlock() {
return html`<umb-ref-list-block .label=${this._label}></umb-ref-list-block>`;
return html`<umb-ref-list-block .label=${this._label} .icon=${this._icon}></umb-ref-list-block>`;
}

#renderInlineBlock() {
return html`<umb-inline-list-block .label=${this._label}></umb-inline-list-block>`;
return html`<umb-inline-list-block .label=${this._label} .icon=${this._icon}></umb-inline-list-block>`;
}

#renderBlock() {
Expand All @@ -259,10 +259,10 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper
href=${this._workspaceEditContentPath}>
<uui-icon name="icon-edit"></uui-icon>
${this._contentInvalid
? html`<uui-badge attention color="danger" label="Invalid settings">!</uui-badge>`
: ''}
? html`<uui-badge attention color="danger" label="Invalid content">!</uui-badge>`
: nothing}
</uui-button>`
: ''}
: nothing}
${this._hasSettings && this._workspaceEditSettingsPath
? html`<uui-button
label="Edit settings"
Expand All @@ -272,13 +272,16 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper
<uui-icon name="icon-settings"></uui-icon>
${this._settingsInvalid
? html`<uui-badge attention color="danger" label="Invalid settings">!</uui-badge>`
: ''}
: nothing}
</uui-button>`
: ''}
: nothing}
<uui-button label="delete" look="secondary" @click=${() => this.#context.requestDelete()}>
<uui-icon name="icon-remove"></uui-icon>
</uui-button>
</uui-action-bar>
${!this._showContentEdit && this._contentInvalid
? html`<uui-badge attention color="danger" label="Invalid content">!</uui-badge>`
: nothing}
`;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ export class UmbInlineListBlockElement extends UmbLitElement {
#workspaceContext?: typeof UMB_BLOCK_WORKSPACE_CONTEXT.TYPE;
#contentUdi?: string;

@property({ type: String })
@property({ type: String, reflect: false })
label?: string;

@property({ type: String, reflect: false })
icon?: string;

@state()
_isOpen = false;

Expand All @@ -42,6 +45,7 @@ export class UmbInlineListBlockElement extends UmbLitElement {
createExtensionApi(this, manifest, [{ manifest: manifest }]).then((context) => {
if (context) {
this.#workspaceContext = context as typeof UMB_BLOCK_WORKSPACE_CONTEXT.TYPE;
this.#workspaceContext.establishLiveSync();
this.#load();

new UmbExtensionsApiInitializer(this, umbExtensionsRegistry, 'workspaceContext', [
Expand All @@ -60,35 +64,141 @@ export class UmbInlineListBlockElement extends UmbLitElement {
}

override render() {
return html` <uui-box>
<button
slot="header"
id="accordion-button"
@click=${() => {
this._isOpen = !this._isOpen;
}}>
<uui-icon name="icon-document"></uui-icon>
<uui-symbol-expand .open=${this._isOpen}></uui-symbol-expand>
<span>${this.label}</span>
</button>
${this._isOpen === true
? html`<umb-block-workspace-view-edit-content-no-router></umb-block-workspace-view-edit-content-no-router>`
: ''}
</uui-box>`;
return html`
<div id="host">
<button
slot="header"
id="open-part"
tabindex="0"
@keydown=${(e: KeyboardEvent) => {
if (e.key !== ' ' && e.key !== 'Enter') return;
e.preventDefault();
e.stopPropagation();
this._isOpen = !this._isOpen;
}}
@click=${() => {
this._isOpen = !this._isOpen;
}}>
<uui-symbol-expand .open=${this._isOpen}></uui-symbol-expand>
${this.#renderContent()}
<slot></slot>
<slot name="tag"></slot>
</button>
${this._isOpen === true
? html`<umb-block-workspace-view-edit-content-no-router></umb-block-workspace-view-edit-content-no-router>`
: ''}
</div>
`;
}

#renderContent() {
return html`
<span id="content">
<span id="icon">
<umb-icon .name=${this.icon}></umb-icon>
</span>
<div id="info">
<div id="name">${this.label}</div>
</div>
</span>
`;
}

static override styles = [
UmbTextStyles,
css`
#accordion-button {
#host {
position: relative;
display: block;
width: 100%;
box-sizing: border-box;
border-radius: var(--uui-border-radius);
background-color: var(--uui-color-surface);
border: 1px solid var(--uui-color-border);
transition: border-color 80ms;
min-width: 250px;
}
#open-part + * {
border-top: 1px solid var(--uui-color-border);
}
:host([disabled]) #open-part {
cursor: default;
transition: border-color 80ms;
}
:host(:not([disabled])) #host:has(#open-part:hover) {
border-color: var(--uui-color-border-emphasis);
}
:host(:not([disabled])) #open-part:hover + * {
border-color: var(--uui-color-border-emphasis);
}
:host([disabled]) #host {
border-color: var(--uui-color-disabled-standalone);
}
slot[name='tag'] {
flex-grow: 1;
display: flex;
justify-content: flex-end;
align-items: center;
}
button {
font-size: inherit;
font-family: inherit;
border: 0;
padding: 0;
background-color: transparent;
text-align: left;
color: var(--uui-color-text);
}
#content {
align-self: stretch;
line-height: normal;
display: flex;
position: relative;
align-items: center;
}
#open-part {
color: inherit;
text-decoration: none;
cursor: pointer;
display: flex;
text-align: left;
align-items: center;
justify-content: flex-start;
width: 100%;
border: none;
background: none;
padding: 0;
min-height: var(--uui-size-16);
padding: calc(var(--uui-size-2) + 1px);
}
#icon {
font-size: 1.2em;
margin-left: var(--uui-size-2);
margin-right: var(--uui-size-1);
}
:host(:not([disabled])) #open-part:hover #icon {
color: var(--uui-color-interactive-emphasis);
}
:host(:not([disabled])) #open-part:hover #name {
color: var(--uui-color-interactive-emphasis);
}
:host([disabled]) #icon {
color: var(--uui-color-disabled-contrast);
}
:host([disabled]) #name {
color: var(--uui-color-disabled-contrast);
}
`,
];
Expand Down
Loading

0 comments on commit 1c8c812

Please sign in to comment.