Skip to content

Commit f1d6e89

Browse files
fix: parsing priority for custom inline content and styles (#2119)
1 parent 7493c4e commit f1d6e89

File tree

18 files changed

+211
-158
lines changed

18 files changed

+211
-158
lines changed

packages/core/src/extensions/BackgroundColor/BackgroundColorMark.ts

Lines changed: 0 additions & 46 deletions
This file was deleted.

packages/core/src/extensions/TextColor/TextColorMark.ts

Lines changed: 0 additions & 38 deletions
This file was deleted.

packages/core/src/schema/inlineContent/createSpec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ export type CustomInlineContentImplementation<
8181
contentDOM?: HTMLElement;
8282
}
8383
| undefined;
84+
85+
runsBefore?: string[];
8486
};
8587

8688
export function getInlineContentParseRules<C extends CustomInlineContentConfig>(
@@ -220,6 +222,7 @@ export function createInlineContentSpec<
220222
node,
221223
inlineContentConfig.propSchema,
222224
{
225+
...inlineContentImplementation,
223226
toExternalHTML: inlineContentImplementation.toExternalHTML,
224227
render(inlineContent, updateInlineContent, editor) {
225228
const output = inlineContentImplementation.render(

packages/core/src/schema/inlineContent/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export type InlineContentImplementation<T extends InlineContentConfig> =
4343
ignoreMutation?: (mutation: ViewMutationRecord) => boolean;
4444
destroy?: () => void;
4545
};
46+
runsBefore?: string[];
4647
};
4748

4849
export type InlineContentSchemaWithInlineContent<

packages/core/src/schema/schema.ts

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,12 @@ export class CustomBlockNoteSchema<
8484
const defaultSet = new Set<string>();
8585
dag.set("default", defaultSet);
8686

87-
for (const [key, specDef] of Object.entries(this.opts.blockSpecs)) {
88-
if (specDef.implementation.runsBefore) {
87+
for (const [key, specDef] of Object.entries({
88+
...this.opts.blockSpecs,
89+
...this.opts.inlineContentSpecs,
90+
...this.opts.styleSpecs,
91+
})) {
92+
if (specDef.implementation?.runsBefore) {
8993
dag.set(key, new Set(specDef.implementation.runsBefore));
9094
} else {
9195
defaultSet.add(key);
@@ -130,19 +134,58 @@ export class CustomBlockNoteSchema<
130134
: never;
131135
};
132136

137+
const inlineContentSpecs = Object.fromEntries(
138+
Object.entries(this.opts.inlineContentSpecs).map(
139+
([key, inlineContentSpec]) => {
140+
// Case for text and links.
141+
if (typeof inlineContentSpec.config !== "object") {
142+
return [key, inlineContentSpec];
143+
}
144+
145+
return [
146+
key,
147+
{
148+
...inlineContentSpec,
149+
implementation: {
150+
...inlineContentSpec.implementation,
151+
node: inlineContentSpec.implementation?.node.extend({
152+
priority: getPriority(key),
153+
}),
154+
},
155+
},
156+
];
157+
},
158+
),
159+
) as InlineContentSpecs;
160+
161+
const styleSpecs = Object.fromEntries(
162+
Object.entries(this.opts.styleSpecs).map(([key, styleSpec]) => [
163+
key,
164+
{
165+
...styleSpec,
166+
implementation: {
167+
...styleSpec.implementation,
168+
mark: styleSpec.implementation?.mark.extend({
169+
priority: getPriority(key),
170+
}),
171+
},
172+
},
173+
]),
174+
) as StyleSpecs;
175+
133176
return {
134177
blockSpecs,
135178
blockSchema: Object.fromEntries(
136179
Object.entries(blockSpecs).map(([key, blockDef]) => {
137180
return [key, blockDef.config];
138181
}),
139182
) as any,
140-
inlineContentSpecs: removeUndefined(this.opts.inlineContentSpecs),
141-
styleSpecs: removeUndefined(this.opts.styleSpecs),
183+
inlineContentSpecs: removeUndefined(inlineContentSpecs),
184+
styleSpecs: removeUndefined(styleSpecs),
142185
inlineContentSchema: getInlineContentSchemaFromSpecs(
143-
this.opts.inlineContentSpecs,
186+
inlineContentSpecs,
144187
) as any,
145-
styleSchema: getStyleSchemaFromSpecs(this.opts.styleSpecs) as any,
188+
styleSchema: getStyleSchemaFromSpecs(styleSpecs) as any,
146189
};
147190
}
148191

packages/core/src/schema/styles/createSpec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export type CustomStyleImplementation<T extends StyleConfig> = {
2222
parse?: (
2323
element: HTMLElement,
2424
) => (T["propSchema"] extends "boolean" ? true : string) | undefined;
25+
runsBefore?: string[];
2526
};
2627

2728
export function getStyleParseRules<T extends StyleConfig>(
@@ -46,6 +47,10 @@ export function getStyleParseRules<T extends StyleConfig>(
4647
if (customParseFunction) {
4748
rules.push({
4849
tag: "*",
50+
// By default, styles can overlap each other, so the rules should not
51+
// completely consume the element they parse (which can have multiple
52+
// styles).
53+
consuming: false,
4954
getAttrs(node: string | HTMLElement) {
5055
if (typeof node === "string") {
5156
return false;
@@ -107,6 +112,7 @@ export function createStyleSpec<const T extends StyleConfig>(
107112
});
108113

109114
return createInternalStyleSpec(styleConfig, {
115+
...styleImplementation,
110116
mark,
111117
render: (value) => {
112118
const renderResult = styleImplementation.render(value as any);

packages/core/src/schema/styles/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export type StyleImplementation<T extends StyleConfig> = {
2828
dom: HTMLElement;
2929
contentDOM?: HTMLElement;
3030
};
31+
runsBefore?: string[];
3132
};
3233

3334
// Container for both the config and implementation of a Style,

packages/react/src/schema/ReactInlineContentSpec.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ export function createReactInlineContentSpec<
233233
return createInternalInlineContentSpec(
234234
inlineContentConfig as CustomInlineContentConfig,
235235
{
236+
...inlineContentImplementation,
236237
node,
237238
render(inlineContent, updateInlineContent, editor) {
238239
const Content = inlineContentImplementation.render;

packages/react/src/schema/ReactStyleSpec.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export type ReactCustomStyleImplementation<T extends StyleConfig> = {
2323
contentRef: (el: HTMLElement | null) => void;
2424
editor: BlockNoteEditor<any, any, any>;
2525
}>;
26+
runsBefore?: string[];
2627
};
2728

2829
// A function to create custom block for API consumers
@@ -116,6 +117,7 @@ export function createReactStyleSpec<T extends StyleConfig>(
116117
});
117118

118119
return createInternalStyleSpec(styleConfig, {
120+
...styleImplementation,
119121
mark,
120122
render(value, editor) {
121123
const Content = styleImplementation.render;

0 commit comments

Comments
 (0)