@blocknote/core
Version:
A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.
87 lines (73 loc) • 2.22 kB
text/typescript
import { Mark } from "@tiptap/core";
import { ParseRule } from "@tiptap/pm/model";
import { UnreachableCaseError } from "../../util/typescript.js";
import {
addStyleAttributes,
createInternalStyleSpec,
stylePropsToAttributes,
} from "./internal.js";
import { StyleConfig, StyleSpec } from "./types.js";
export type CustomStyleImplementation<T extends StyleConfig> = {
render: T["propSchema"] extends "boolean"
? () => {
dom: HTMLElement;
contentDOM?: HTMLElement;
}
: (value: string) => {
dom: HTMLElement;
contentDOM?: HTMLElement;
};
};
// TODO: support serialization
export function getStyleParseRules(config: StyleConfig): ParseRule[] {
return [
{
tag: `[data-style-type="${config.type}"]`,
contentElement: (element) => {
const htmlElement = element as HTMLElement;
if (htmlElement.matches("[data-editable]")) {
return htmlElement;
}
return htmlElement.querySelector("[data-editable]") || htmlElement;
},
},
];
}
export function createStyleSpec<T extends StyleConfig>(
styleConfig: T,
styleImplementation: CustomStyleImplementation<T>,
): StyleSpec<T> {
const mark = Mark.create({
name: styleConfig.type,
addAttributes() {
return stylePropsToAttributes(styleConfig.propSchema);
},
parseHTML() {
return getStyleParseRules(styleConfig);
},
renderHTML({ mark }) {
let renderResult: {
dom: HTMLElement;
contentDOM?: HTMLElement;
};
if (styleConfig.propSchema === "boolean") {
// @ts-ignore not sure why this is complaining
renderResult = styleImplementation.render();
} else if (styleConfig.propSchema === "string") {
renderResult = styleImplementation.render(mark.attrs.stringValue);
} else {
throw new UnreachableCaseError(styleConfig.propSchema);
}
// const renderResult = styleImplementation.render();
return addStyleAttributes(
renderResult,
styleConfig.type,
mark.attrs.stringValue,
styleConfig.propSchema,
);
},
});
return createInternalStyleSpec(styleConfig, {
mark,
});
}