Skip to content

Commit 240ed26

Browse files
chore(block-editor): Replaced custom dotPlaceholder with official Placeholder (dotCMS#32552)
https://github.com/user-attachments/assets/36401348-0a51-43be-bc8a-36fc6aa2a3fb This pull request refactors the placeholder functionality in the block editor by replacing the `DotPlaceholder` extension with a new `DotCMSPlusButton` extension. The changes include updating the placeholder behavior, improving the user experience with a "plus" button for adding blocks, and modifying dependencies accordingly. ### Placeholder and Plus Button Refactor: * Replaced the `DotPlaceholder` extension with `DotCMSPlusButton`, which introduces a "plus" button for adding blocks instead of using placeholder text. The new extension is configurable with options such as `showOnlyWhenEditable`, `showOnlyCurrent`, and `includeChildren`. (`core-web/libs/block-editor/src/lib/extensions/dot-plus-button/dot-plus-button.plugin.ts`) [[1]](diffhunk://#diff-77f12fdd87c8cf5382ab3d6747b850b40c6706e7418f093ab24fa0e5208f3a52L7-R7) [[2]](diffhunk://#diff-77f12fdd87c8cf5382ab3d6747b850b40c6706e7418f093ab24fa0e5208f3a52L60-L67) * Removed placeholder-specific configurations such as `emptyEditorClass`, `emptyNodeClass`, and `placeholder` from the editor setup. The new `DotCMSPlusButton` focuses on block-level interactions instead. (`core-web/libs/block-editor/src/lib/components/dot-block-editor/dot-block-editor.component.ts`) [[1]](diffhunk://#diff-266eab162f8661e695c3e40956692fa3696fbd4e8cd3e0352ea9e01f90b13609L437) [[2]](diffhunk://#diff-266eab162f8661e695c3e40956692fa3696fbd4e8cd3e0352ea9e01f90b13609L462-R472) ### Dependency Updates: * Added the `@tiptap/extension-placeholder` dependency to support the new placeholder functionality. (`core-web/package.json`) ### Code Cleanup: * Removed unused methods and properties from the old `DotPlaceholder` implementation, such as `toTitleCase` and placeholder-related decorations. (`core-web/libs/block-editor/src/lib/extensions/dot-plus-button/dot-plus-button.plugin.ts`) [[1]](diffhunk://#diff-77f12fdd87c8cf5382ab3d6747b850b40c6706e7418f093ab24fa0e5208f3a52L21-L26) [[2]](diffhunk://#diff-77f12fdd87c8cf5382ab3d6747b850b40c6706e7418f093ab24fa0e5208f3a52L77-L125) ### File Renaming: * Renamed the file `dot-placeholder-plugin.ts` to `dot-plus-button.plugin.ts` to reflect the new functionality. --------- Co-authored-by: Kevin <[email protected]>
1 parent 5ffdf4e commit 240ed26

File tree

4 files changed

+54
-52
lines changed

4 files changed

+54
-52
lines changed

core-web/libs/block-editor/src/lib/components/dot-block-editor/dot-block-editor.component.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import CharacterCount, { CharacterCountStorage } from '@tiptap/extension-charact
2525
import { Level } from '@tiptap/extension-heading';
2626
import { Highlight } from '@tiptap/extension-highlight';
2727
import { Link } from '@tiptap/extension-link';
28+
import Placeholder from '@tiptap/extension-placeholder';
2829
import { Subscript } from '@tiptap/extension-subscript';
2930
import { Superscript } from '@tiptap/extension-superscript';
3031
import { TableRow } from '@tiptap/extension-table-row';
@@ -63,7 +64,7 @@ import {
6364
FreezeScroll,
6465
IndentExtension
6566
} from '../../extensions';
66-
import { DotPlaceholder } from '../../extensions/dot-placeholder/dot-placeholder-plugin';
67+
import { DotCMSPlusButton } from '../../extensions/dot-plus-button/dot-plus-button.plugin';
6768
import { AIContentNode, ContentletBlock, ImageNode, LoaderNode, VideoNode } from '../../nodes';
6869
import {
6970
DotMarketingConfigService,
@@ -434,7 +435,6 @@ export class DotBlockEditorComponent implements OnInit, OnDestroy, ControlValueA
434435
contentletIdentifier: this.contentletIdentifier
435436
}),
436437
DotComands,
437-
DotPlaceholder.configure({ placeholder: 'Type "/" for commands' }),
438438
Youtube.configure({
439439
height: 300,
440440
width: 400,
@@ -459,7 +459,17 @@ export class DotBlockEditorComponent implements OnInit, OnDestroy, ControlValueA
459459
FreezeScroll,
460460
CharacterCount,
461461
AssetUploader(this.#injector, this.viewContainerRef),
462-
IndentExtension
462+
IndentExtension,
463+
Placeholder.configure({
464+
placeholder: 'Start writing or type / to choose a block',
465+
emptyEditorClass: 'is-editor-empty',
466+
emptyNodeClass: 'is-empty'
467+
}),
468+
DotCMSPlusButton.configure({
469+
showOnlyWhenEditable: true,
470+
showOnlyCurrent: true,
471+
includeChildren: false
472+
})
463473
];
464474

465475
if (isAIPluginInstalled) {

core-web/libs/block-editor/src/lib/extensions/dot-placeholder/dot-placeholder-plugin.ts renamed to core-web/libs/block-editor/src/lib/extensions/dot-plus-button/dot-plus-button.plugin.ts

Lines changed: 7 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@ import { Decoration, DecorationSet } from '@tiptap/pm/view';
44

55
import { NodeTypes } from '../bubble-menu/models';
66

7-
export interface PlaceholderOptions {
8-
emptyEditorClass: string;
9-
emptyNodeClass: string;
10-
placeholder: string;
7+
export interface DotPlusButtonOptions {
118
showOnlyWhenEditable: boolean;
129
showOnlyCurrent: boolean;
1310
includeChildren: boolean;
@@ -18,12 +15,6 @@ export enum PositionHeadings {
1815
TOP_CURRENT = '26px'
1916
}
2017

21-
function toTitleCase(str) {
22-
return str.replace(/\p{L}+('\p{L}+)?/gu, function (txt) {
23-
return txt.charAt(0).toUpperCase() + txt.slice(1);
24-
});
25-
}
26-
2718
const addPlusButton = (pos: number, node, editor: Editor) => {
2819
const button = document.createElement('button');
2920
button.classList.add('add-button');
@@ -33,6 +24,7 @@ const addPlusButton = (pos: number, node, editor: Editor) => {
3324
const div = document.createElement('div');
3425
div.style.position = 'relative';
3526
div.setAttribute('draggable', 'false');
27+
3628
if (pos === 0 && node.type.name === NodeTypes.HEADING) {
3729
button.style.top = PositionHeadings.TOP_INITIAL;
3830
}
@@ -57,14 +49,11 @@ const addPlusButton = (pos: number, node, editor: Editor) => {
5749
return div;
5850
};
5951

60-
export const DotPlaceholder = Extension.create<PlaceholderOptions>({
61-
name: 'dotPlaceholder',
52+
export const DotCMSPlusButton = Extension.create<DotPlusButtonOptions>({
53+
name: 'dotCMSPlusButton',
6254

6355
addOptions() {
6456
return {
65-
emptyEditorClass: 'is-editor-empty',
66-
emptyNodeClass: 'is-empty',
67-
placeholder: 'Write something …',
6857
showOnlyWhenEditable: true,
6958
showOnlyCurrent: true,
7059
includeChildren: false
@@ -74,55 +63,27 @@ export const DotPlaceholder = Extension.create<PlaceholderOptions>({
7463
addProseMirrorPlugins() {
7564
return [
7665
new Plugin({
77-
key: new PluginKey('dotPlaceholder'),
66+
key: new PluginKey('dotCMSPlusButton'),
7867
props: {
7968
decorations: ({ doc, selection }) => {
8069
const active = this.editor.isEditable || !this.options.showOnlyWhenEditable;
8170

82-
const { anchor } = selection;
83-
const decorations: Decoration[] = [];
84-
8571
if (!active) {
8672
return null;
8773
}
8874

89-
// only calculate isEmpty once due to its performance impacts (see issue #3360)
90-
const emptyDocInstance = doc.type.createAndFill();
91-
const isEditorEmpty =
92-
emptyDocInstance?.sameMarkup(doc) &&
93-
emptyDocInstance.content.findDiffStart(doc.content) === null;
75+
const { anchor } = selection;
76+
const decorations: Decoration[] = [];
9477

9578
doc.descendants((node, pos) => {
9679
const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize;
9780
const isEmpty = !node.isLeaf && !node.childCount;
9881

9982
if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {
100-
const classes = [this.options.emptyNodeClass];
101-
102-
if (isEditorEmpty) {
103-
classes.push(this.options.emptyEditorClass);
104-
}
105-
10683
const decoration = Decoration.widget(
10784
pos,
10885
addPlusButton(pos, node, this.editor)
10986
);
110-
111-
const decorationContent = Decoration.node(
112-
pos,
113-
pos + node.nodeSize,
114-
{
115-
class: classes.join(' '),
116-
'data-placeholder':
117-
node.type.name === NodeTypes.HEADING
118-
? `${toTitleCase(node.type.name)} ${
119-
node.attrs.level
120-
}`
121-
: this.options.placeholder
122-
}
123-
);
124-
125-
decorations.push(decorationContent);
12687
decorations.push(decoration);
12788
}
12889

core-web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
"@tiptap/extension-highlight": "^2.14.0",
8383
"@tiptap/extension-image": "^2.14.0",
8484
"@tiptap/extension-link": "^2.14.0",
85+
"@tiptap/extension-placeholder": "^2.23.0",
8586
"@tiptap/extension-subscript": "^2.14.0",
8687
"@tiptap/extension-superscript": "^2.14.0",
8788
"@tiptap/extension-table": "^2.14.0",

core-web/yarn.lock

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6032,6 +6032,11 @@
60326032
resolved "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.14.0.tgz"
60336033
integrity sha512-bsQesVpgvDS2e+wr2fp59QO7rWRp2FqcJvBafwXS3Br9U5Mx3eFYryx4wC7cUnhlhUwX5pmaoA7zISgV9dZDgg==
60346034

6035+
"@tiptap/extension-placeholder@^2.23.0":
6036+
version "2.23.0"
6037+
resolved "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.23.0.tgz#30474158b2c0d633fd8d2313b8e25c527803dd52"
6038+
integrity sha512-I5RQk0qn6nj7l7z4mWKIxjO2nluvKsm00W2CbC75b4YcScBfsMInHQdjN2s+W8xuF0zquhwVITxA+Bmn4zynqg==
6039+
60356040
"@tiptap/extension-strike@^2.14.0":
60366041
version "2.14.0"
60376042
resolved "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.14.0.tgz"
@@ -22037,7 +22042,7 @@ string-length@^4.0.1:
2203722042
char-regex "^1.0.2"
2203822043
strip-ansi "^6.0.0"
2203922044

22040-
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
22045+
"string-width-cjs@npm:string-width@^4.2.0":
2204122046
version "4.2.3"
2204222047
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
2204322048
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -22055,6 +22060,15 @@ string-width@^1.0.1:
2205522060
is-fullwidth-code-point "^1.0.0"
2205622061
strip-ansi "^3.0.0"
2205722062

22063+
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
22064+
version "4.2.3"
22065+
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
22066+
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
22067+
dependencies:
22068+
emoji-regex "^8.0.0"
22069+
is-fullwidth-code-point "^3.0.0"
22070+
strip-ansi "^6.0.1"
22071+
2205822072
string-width@^2.0.0, string-width@^2.1.1:
2205922073
version "2.1.1"
2206022074
resolved "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz"
@@ -22165,7 +22179,7 @@ stringify-package@^1.0.0, stringify-package@^1.0.1:
2216522179
resolved "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz"
2216622180
integrity sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==
2216722181

22168-
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
22182+
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
2216922183
version "6.0.1"
2217022184
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
2217122185
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -22193,6 +22207,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
2219322207
dependencies:
2219422208
ansi-regex "^4.1.0"
2219522209

22210+
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
22211+
version "6.0.1"
22212+
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
22213+
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
22214+
dependencies:
22215+
ansi-regex "^5.0.1"
22216+
2219622217
strip-ansi@^7.0.1, strip-ansi@^7.1.0:
2219722218
version "7.1.0"
2219822219
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz"
@@ -24278,7 +24299,7 @@ worker-farm@^1.6.0, worker-farm@^1.7.0:
2427824299
dependencies:
2427924300
errno "~0.1.7"
2428024301

24281-
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
24302+
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
2428224303
version "7.0.0"
2428324304
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
2428424305
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -24305,6 +24326,15 @@ wrap-ansi@^6.2.0:
2430524326
string-width "^4.1.0"
2430624327
strip-ansi "^6.0.0"
2430724328

24329+
wrap-ansi@^7.0.0:
24330+
version "7.0.0"
24331+
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
24332+
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
24333+
dependencies:
24334+
ansi-styles "^4.0.0"
24335+
string-width "^4.1.0"
24336+
strip-ansi "^6.0.0"
24337+
2430824338
wrap-ansi@^8.1.0:
2430924339
version "8.1.0"
2431024340
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz"

0 commit comments

Comments
 (0)