Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions packages/core/src/editor/Block.css
Original file line number Diff line number Diff line change
Expand Up @@ -542,15 +542,11 @@ NESTED BLOCKS
}

/* PLACEHOLDERS*/
.bn-inline-content:has(> .ProseMirror-trailingBreak:only-child):before {
/*float: left; */
/* TODO: should this be here? */
.bn-inline-content:has(> .ProseMirror-trailingBreak:only-child):after {
pointer-events: none;
height: 0;
/* width: 0; */
position: absolute;
font-style: italic;
}
/* TODO: should this be here? */

/* TEXT COLORS */
[data-style-type="textColor"][data-value="gray"],
Expand Down
68 changes: 44 additions & 24 deletions packages/core/src/extensions/Placeholder/Placeholder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ export const PlaceholderExtension = createExtension(
new Plugin({
key: PLUGIN_KEY,
view: (view) => {
view.dom.setAttribute(
"data-selection-empty",
view.state.selection.empty ? "true" : "false",
);

const uniqueEditorSelector = `placeholder-selector-${v4()}`;
view.dom.classList.add(uniqueEditorSelector);
const styleEl = document.createElement("style");
Expand All @@ -40,9 +45,22 @@ export const PlaceholderExtension = createExtension(

const styleSheet = styleEl.sheet!;

const getSelector = (additionalSelectors = "") =>
`.${uniqueEditorSelector} .bn-block-content${additionalSelectors} .bn-inline-content:has(> .ProseMirror-trailingBreak:only-child):before`;

const createPlaceholderRule = (
placeholder: string | undefined,
additionalEditorSelectors = "",
additionalBlockSelectors = "",
) => {
// Creates CSS rule to set placeholder content at the given selector.
styleSheet.insertRule(
`.${uniqueEditorSelector}${additionalEditorSelectors} .bn-block-content${additionalBlockSelectors} .bn-inline-content:has(> .ProseMirror-trailingBreak:only-child):after { content: ${JSON.stringify(placeholder)}; }`,
);
// Creates CSS rule to hide the trailing break node while the
// placeholder is visible. This is because it's rendered as a
// `br` element, forcing the placeholder onto the next line.
styleSheet.insertRule(
`.${uniqueEditorSelector}${additionalEditorSelectors} .bn-block-content${additionalBlockSelectors} .bn-inline-content > .ProseMirror-trailingBreak:only-child { display: none; }`,
);
};
try {
// FIXME: the names "default" and "emptyDocument" are hardcoded
const {
Expand All @@ -53,30 +71,30 @@ export const PlaceholderExtension = createExtension(

// add block specific placeholders
for (const [blockType, placeholder] of Object.entries(rest)) {
const blockTypeSelector = `[data-content-type="${blockType}"]`;

styleSheet.insertRule(
`${getSelector(blockTypeSelector)} { content: ${JSON.stringify(
placeholder,
)}; }`,
createPlaceholderRule(
placeholder,
"[data-selection-empty='true']",
`[data-content-type="${blockType}"]`,
);
createPlaceholderRule(
placeholder,
"[data-selection-empty='false']",
`[data-content-type="${blockType}"]:not([data-is-empty-and-focused])`,
);
}

const onlyBlockSelector = `[data-is-only-empty-block]`;
const mustBeFocusedSelector = `[data-is-empty-and-focused]`;

// placeholder for when there's only one empty block
styleSheet.insertRule(
`${getSelector(onlyBlockSelector)} { content: ${JSON.stringify(
emptyPlaceholder,
)}; }`,
createPlaceholderRule(
emptyPlaceholder,
"[data-selection-empty='true']",
"[data-is-only-empty-block]",
);

// placeholder for default blocks, only when the cursor is in the block (mustBeFocused)
styleSheet.insertRule(
`${getSelector(mustBeFocusedSelector)} { content: ${JSON.stringify(
defaultPlaceholder,
)}; }`,
createPlaceholderRule(
defaultPlaceholder,
"[data-selection-empty='true']",
"[data-is-empty-and-focused]",
);
} catch (e) {
// eslint-disable-next-line no-console
Expand All @@ -87,6 +105,12 @@ export const PlaceholderExtension = createExtension(
}

return {
update: (view) => {
view.dom.setAttribute(
"data-selection-empty",
view.state.selection.empty ? "true" : "false",
);
},
destroy: () => {
if (view.root instanceof window.ShadowRoot) {
view.root.removeChild(styleEl);
Expand All @@ -104,10 +128,6 @@ export const PlaceholderExtension = createExtension(
return;
}

if (!selection.empty) {
return;
}

// Don't show placeholder when the cursor is inside a code block
if (selection.$from.parent.type.spec.code) {
return;
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/editor/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
}

/* Placeholder styling */
.bn-inline-content:has(> .ProseMirror-trailingBreak):before {
.bn-inline-content:has(> .ProseMirror-trailingBreak):after {
color: var(--bn-colors-side-menu);
}

Expand Down
Loading