Skip to content

Commit 13a488e

Browse files
authored
Merge branch 'v15/feature/tiptap' into v15/feature/rte/tiptap/toolbar-manifest-tweaks
2 parents f383878 + bacf0e5 commit 13a488e

File tree

11 files changed

+159
-68
lines changed

11 files changed

+159
-68
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ export abstract class UmbBlockEntriesContext<
9595
layoutEntry: BlockLayoutType,
9696
content: UmbBlockDataModel,
9797
settings: UmbBlockDataModel | undefined,
98-
originData: UmbBlockWorkspaceOriginData,
98+
originData: BlockOriginData,
9999
): Promise<boolean>;
100100
//edit?
101101
//editSettings

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ export abstract class UmbBlockManagerContext<
9191
setContents(contents: Array<UmbBlockDataModel>) {
9292
this.#contents.setValue(contents);
9393
}
94+
getContents() {
95+
return this.#contents.value;
96+
}
9497
setSettings(settings: Array<UmbBlockDataModel>) {
9598
this.#settings.setValue(settings);
9699
}

src/packages/rte/components/rte-base.element.ts

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import {
1212
type UmbBlockRteTypeModel,
1313
} from '@umbraco-cms/backoffice/block-rte';
1414
import { UMB_PROPERTY_CONTEXT, UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property';
15-
import { observeMultiple } from '@umbraco-cms/backoffice/observable-api';
16-
import { debounceTime } from '@umbraco-cms/backoffice/external/rxjs';
1715

1816
export abstract class UmbRteBaseElement extends UmbLitElement implements UmbPropertyEditorUiElement {
1917
public set config(config: UmbPropertyEditorConfigCollection | undefined) {
@@ -27,13 +25,6 @@ export abstract class UmbRteBaseElement extends UmbLitElement implements UmbProp
2725
this.#managerContext.setEditorConfiguration(config);
2826
}
2927

30-
/**
31-
* Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
32-
* @default false
33-
*/
34-
@property({ type: Boolean, reflect: true })
35-
readonly = false;
36-
3728
@property({
3829
attribute: false,
3930
type: Object,
@@ -45,6 +36,7 @@ export abstract class UmbRteBaseElement extends UmbLitElement implements UmbProp
4536
const buildUpValue: Partial<UmbPropertyEditorUiValueType> = value ? { ...value } : {};
4637
buildUpValue.markup ??= '';
4738
buildUpValue.blocks ??= { layout: {}, contentData: [], settingsData: [], expose: [] };
39+
buildUpValue.blocks.layout ??= {};
4840
buildUpValue.blocks.contentData ??= [];
4941
buildUpValue.blocks.settingsData ??= [];
5042
buildUpValue.blocks.expose ??= [];
@@ -64,6 +56,13 @@ export abstract class UmbRteBaseElement extends UmbLitElement implements UmbProp
6456
return this._value;
6557
}
6658

59+
/**
60+
* Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
61+
* @default false
62+
*/
63+
@property({ type: Boolean, reflect: true })
64+
readonly = false;
65+
6766
@state()
6867
protected _config?: UmbPropertyEditorConfigCollection;
6968

@@ -119,7 +118,34 @@ export abstract class UmbRteBaseElement extends UmbLitElement implements UmbProp
119118
'observePropertyAlias',
120119
);
121120

122-
this.observe(
121+
this.observe(this.#entriesContext.layoutEntries, (layouts) => {
122+
// Update manager:
123+
this.#managerContext.setLayouts(layouts);
124+
});
125+
126+
// Observe the value of the property and update the editor value.
127+
this.observe(this.#managerContext.layouts, (layouts) => {
128+
this._value = {
129+
...this._value,
130+
blocks: { ...this._value.blocks, layout: { [UMB_BLOCK_RTE_PROPERTY_EDITOR_SCHEMA_ALIAS]: layouts } },
131+
};
132+
this._fireChangeEvent();
133+
});
134+
this.observe(this.#managerContext.contents, (contents) => {
135+
this._value = { ...this._value, blocks: { ...this._value.blocks, contentData: contents } };
136+
this._fireChangeEvent();
137+
});
138+
this.observe(this.#managerContext.settings, (settings) => {
139+
this._value = { ...this._value, blocks: { ...this._value.blocks, settingsData: settings } };
140+
this._fireChangeEvent();
141+
});
142+
this.observe(this.#managerContext.exposes, (exposes) => {
143+
this._value = { ...this._value, blocks: { ...this._value.blocks, expose: exposes } };
144+
this._fireChangeEvent();
145+
});
146+
147+
// The above could potentially be replaced with a single observeMultiple call, but it is not done for now to avoid potential issues with the order of the updates.
148+
/*this.observe(
123149
observeMultiple([
124150
this.#managerContext.layouts,
125151
this.#managerContext.contents,
@@ -140,7 +166,7 @@ export abstract class UmbRteBaseElement extends UmbLitElement implements UmbProp
140166
this._fireChangeEvent();
141167
},
142168
'motherObserver',
143-
);
169+
);*/
144170
});
145171
this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, (context) => {
146172
this.#managerContext.setVariantId(context.getVariantId());
@@ -153,6 +179,10 @@ export abstract class UmbRteBaseElement extends UmbLitElement implements UmbProp
153179
}
154180

155181
protected _filterUnusedBlocks(usedContentKeys: (string | null)[]) {
182+
const unusedBlockContents = this.#managerContext.getContents().filter((x) => usedContentKeys.indexOf(x.key) === -1);
183+
unusedBlockContents.forEach((blockContent) => {
184+
this.#managerContext.removeOneContent(blockContent.key);
185+
});
156186
const unusedBlocks = this.#managerContext.getLayouts().filter((x) => usedContentKeys.indexOf(x.contentKey) === -1);
157187
unusedBlocks.forEach((blockLayout) => {
158188
this.#managerContext.removeOneLayout(blockLayout.contentKey);

src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export const defaultFallbackConfig: RawEditorOptions = {
1313
'+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-s[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],-sub[style|class],-sup[style|class],-blockquote[dir|style|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align|style],address[class|align|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align|style],hr[class|style],small[class|style],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*],figure,figcaption,cite,video[*],audio[*],picture[*],source[*],canvas[*]',
1414
invalid_elements: 'font',
1515
extended_valid_elements:
16-
'@[id|class|style],+umb-rte-block[!data-content-udi],+umb-rte-block-inline[!data-content-udi],-div[id|dir|class|align|style],ins[datetime|cite],-ul[class|style],-li[class|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align],span[id|class|style|lang],figure,figcaption',
16+
'@[id|class|style],+umb-rte-block[!data-content-key],+umb-rte-block-inline[!data-content-key],-div[id|dir|class|align|style],ins[datetime|cite],-ul[class|style],-li[class|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align],span[id|class|style|lang],figure,figcaption',
1717
custom_elements: 'umb-rte-block,~umb-rte-block-inline',
1818
toolbar: [
1919
'styles',

src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ export default class UmbTinyMceMultiUrlPickerPlugin extends UmbTinyMcePluginBase
114114

115115
const blockEl = `<${blockTag} ${UMB_BLOCK_RTE_DATA_CONTENT_KEY}="${block.key}"></${blockTag}>`;
116116

117-
editor.insertContent(blockEl);
117+
editor.selection.setContent(blockEl);
118+
editor.setDirty(true);
118119
});
119120
}
120121
}

src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { UmbTiptapExtensionApi } from '../../extensions/types.js';
1+
import type { UmbTiptapExtensionApi, UmbTiptapToolbarValue } from '../../extensions/types.js';
22
import { css, customElement, html, property, state, when } from '@umbraco-cms/backoffice/external/lit';
33
import { loadManifestApi } from '@umbraco-cms/backoffice/extension-api';
44
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
@@ -75,7 +75,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin<string, typeof Um
7575
private _editor!: Editor;
7676

7777
@state()
78-
_toolbar: string[][][] = [[[]]];
78+
_toolbar: UmbTiptapToolbarValue = [[[]]];
7979

8080
protected override async firstUpdated() {
8181
await Promise.all([await this.#loadExtensions(), await this.#loadEditor()]);
@@ -105,12 +105,11 @@ export class UmbInputTiptapElement extends UmbFormControlMixin<string, typeof Um
105105
const element = this.shadowRoot?.querySelector('#editor');
106106
if (!element) return;
107107

108-
const maxWidth = this.configuration?.getValueByAlias<number>('maxWidth');
109-
const maxHeight = this.configuration?.getValueByAlias<number>('maxHeight');
110-
if (maxWidth) this.setAttribute('style', `max-width: ${maxWidth}px;`);
111-
if (maxHeight) element.setAttribute('style', `max-height: ${maxHeight}px;`);
108+
const dimensions = this.configuration?.getValueByAlias<{ width?: number; height?: number }>('dimensions');
109+
if (dimensions?.width) this.setAttribute('style', `max-width: ${dimensions.width}px;`);
110+
if (dimensions?.height) element.setAttribute('style', `max-height: ${dimensions.height}px;`);
112111

113-
this._toolbar = this.configuration?.getValueByAlias<string[][][]>('toolbar') ?? [[[]]];
112+
this._toolbar = this.configuration?.getValueByAlias<UmbTiptapToolbarValue>('toolbar') ?? [[[]]];
114113

115114
const extensions = this._extensions
116115
.map((ext) => ext.getTiptapExtensions({ configuration: this.configuration }))
@@ -264,6 +263,8 @@ export class UmbInputTiptapElement extends UmbFormControlMixin<string, typeof Um
264263
265264
.umb-embed-holder.ProseMirror-selectednode::before {
266265
background: rgba(0, 0, 0, 0.025);
266+
}
267+
267268
/* Table-specific styling */
268269
.tableWrapper {
269270
margin: 1.5rem 0;
@@ -318,7 +319,6 @@ export class UmbInputTiptapElement extends UmbFormControlMixin<string, typeof Um
318319
width: 3px;
319320
}
320321
}
321-
}
322322
323323
.resize-cursor {
324324
cursor: ew-resize;

src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts

Lines changed: 75 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import type { ManifestTiptapToolbarExtension } from '../../extensions/tiptap-toolbar-extension.js';
2-
import { css, customElement, html, property } from '@umbraco-cms/backoffice/external/lit';
1+
import type { UmbTiptapToolbarValue } from '../../extensions/types.js';
2+
import { css, customElement, html, map, property, state } from '@umbraco-cms/backoffice/external/lit';
3+
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
4+
import { UmbExtensionsElementAndApiInitializer } from '@umbraco-cms/backoffice/extension-api';
35
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
46
import type { Editor } from '@umbraco-cms/backoffice/external/tiptap';
57
import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
@@ -10,6 +12,12 @@ const elementName = 'umb-tiptap-fixed-menu';
1012

1113
@customElement(elementName)
1214
export class UmbTiptapFixedMenuElement extends UmbLitElement {
15+
#attached = false;
16+
#extensionsController?: UmbExtensionsElementAndApiInitializer;
17+
18+
@state()
19+
private _lookup?: Map<string, unknown>;
20+
1321
@property({ type: Boolean, reflect: true })
1422
readonly = false;
1523

@@ -20,21 +28,61 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement {
2028
configuration?: UmbPropertyEditorConfigCollection;
2129

2230
@property({ attribute: false })
23-
toolbar: string[][][] = [[[]]];
31+
toolbar: UmbTiptapToolbarValue = [[[]]];
32+
33+
override connectedCallback(): void {
34+
super.connectedCallback();
35+
this.#attached = true;
36+
this.#observeExtensions();
37+
}
38+
override disconnectedCallback(): void {
39+
this.#attached = false;
40+
this.#extensionsController?.destroy();
41+
this.#extensionsController = undefined;
42+
super.disconnectedCallback();
43+
}
44+
45+
#observeExtensions(): void {
46+
if (!this.#attached) return;
47+
this.#extensionsController?.destroy();
48+
49+
this.#extensionsController = new UmbExtensionsElementAndApiInitializer(
50+
this,
51+
umbExtensionsRegistry,
52+
'tiptapToolbarExtension',
53+
[],
54+
(manifest) => this.toolbar.flat(2).includes(manifest.alias),
55+
(extensionControllers) => {
56+
this._lookup = new Map(extensionControllers.map((ext) => [ext.alias, ext.component]));
57+
},
58+
);
59+
60+
this.#extensionsController.apiProperties = { configuration: this.configuration };
61+
this.#extensionsController.elementProperties = { editor: this.editor, configuration: this.configuration };
62+
}
2463

2564
override render() {
26-
return html`
27-
<umb-extension-with-api-slot
28-
type="tiptapToolbarExtension"
29-
.filter=${(ext: ManifestTiptapToolbarExtension) =>
30-
this.toolbar.flat(2).includes(ext.alias) && (!!ext.kind || !!ext.element)}
31-
.elementProps=${{ editor: this.editor, configuration: this.configuration }}
32-
.apiProps=${{ configuration: this.configuration }}>
33-
</umb-extension-with-api-slot>
34-
`;
65+
return html`${map(this.toolbar, (row, rowIndex) =>
66+
map(
67+
row,
68+
(group, groupIndex) =>
69+
html`${map(group, (alias, aliasIndex) => {
70+
const newRow = rowIndex !== 0 && groupIndex === 0 && aliasIndex === 0;
71+
return html`<div class="item" ?data-new-row=${newRow} style="${newRow ? 'grid-column: 1 / span 3' : ''}">
72+
${this._lookup?.get(alias)}
73+
</div>`;
74+
})}
75+
<div class="separator"></div> `,
76+
),
77+
)} `;
3578
}
3679

3780
static override readonly styles = css`
81+
:host([readonly]) {
82+
pointer-events: none;
83+
background-color: var(--uui-color-surface-alt);
84+
}
85+
3886
:host {
3987
border-radius: var(--uui-border-radius);
4088
border: 1px solid var(--uui-color-border);
@@ -43,20 +91,29 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement {
4391
background-color: var(--uui-color-surface);
4492
color: var(--color-text);
4593
display: grid;
46-
grid-template-columns: repeat(auto-fill, minmax(24px, 1fr));
47-
gap: var(--uui-size-space-1);
94+
grid-template-columns: repeat(auto-fill, 10px);
95+
grid-auto-flow: row;
4896
position: sticky;
4997
top: -25px;
5098
left: 0px;
5199
right: 0px;
52100
padding: var(--uui-size-space-3);
53-
align-items: center;
54101
z-index: 9999999;
55102
}
56103
57-
:host([readonly]) {
58-
pointer-events: none;
59-
background-color: var(--uui-color-surface-alt);
104+
.item {
105+
grid-column: span 3;
106+
}
107+
108+
.separator {
109+
background-color: var(--uui-color-border);
110+
width: 1px;
111+
place-self: center;
112+
height: 22px;
113+
}
114+
.separator:last-child,
115+
.separator:has(+ [data-new-row]) {
116+
display: none;
60117
}
61118
`;
62119
}

src/packages/rte/tiptap/extensions/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,5 @@ export abstract class UmbTiptapToolbarElementApiBase extends UmbControllerBase i
9898
return editor && this.manifest?.meta.alias ? editor?.isActive(this.manifest.meta.alias) : false;
9999
}
100100
}
101+
102+
export type UmbTiptapToolbarValue = Array<Array<Array<string>>>;

src/packages/rte/tiptap/extensions/umb/link.extension.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { UMB_LINK_PICKER_MODAL } from '@umbraco-cms/backoffice/multi-url-picker'
44
import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
55
import type { Editor } from '@umbraco-cms/backoffice/external/tiptap';
66
import type { UmbLinkPickerLink } from '@umbraco-cms/backoffice/multi-url-picker';
7+
import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui';
78

89
export default class UmbTiptapLinkExtensionApi extends UmbTiptapToolbarElementApiBase {
910
override async execute(editor?: Editor) {
@@ -12,8 +13,10 @@ export default class UmbTiptapLinkExtensionApi extends UmbTiptapToolbarElementAp
1213
const data = { config: {}, index: null };
1314
const value = { link };
1415

16+
const overlaySize = this.configuration?.getValueByAlias<UUIModalSidebarSize>('overlaySize') ?? 'small';
17+
1518
const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);
16-
const modalHandler = modalManager.open(this, UMB_LINK_PICKER_MODAL, { data, value });
19+
const modalHandler = modalManager.open(this, UMB_LINK_PICKER_MODAL, { data, value, modal: { size: overlaySize } });
1720

1821
if (!modalHandler) return;
1922

src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
1-
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
2-
import {
3-
customElement,
4-
css,
5-
html,
6-
property,
7-
state,
8-
repeat,
9-
nothing,
10-
type PropertyValueMap,
11-
} from '@umbraco-cms/backoffice/external/lit';
12-
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
1+
import type { UmbTiptapToolbarValue } from '../extensions/types.js';
2+
import { customElement, css, html, property, state, repeat, nothing } from '@umbraco-cms/backoffice/external/lit';
133
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
4+
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
5+
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
146
import { UmbPropertyValueChangeEvent, type UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/property-editor';
7+
import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit';
158

169
type Extension = {
1710
alias: string;
@@ -25,7 +18,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement
2518
implements UmbPropertyEditorUiElement
2619
{
2720
@property({ attribute: false })
28-
set value(value: string[][][] | undefined) {
21+
set value(value: UmbTiptapToolbarValue | undefined) {
2922
if (!value) {
3023
this.#useDefault = true;
3124
this.#value = [[[]]];
@@ -36,14 +29,14 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement
3629
this.#value = value.map((rows) => rows.map((groups) => [...groups]));
3730
}
3831

39-
get value(): string[][][] {
32+
get value(): UmbTiptapToolbarValue {
4033
// TODO: This can be optimized with cashing;
4134
return this.#value.map((rows) => rows.map((groups) => [...groups]));
4235
}
4336

4437
#useDefault = false;
4538

46-
#value: string[][][] = [[[]]];
39+
#value: UmbTiptapToolbarValue = [[[]]];
4740

4841
@state()
4942
_extensions: Extension[] = [];

0 commit comments

Comments
 (0)