Skip to content

Playground custom elements #430

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
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
143 changes: 143 additions & 0 deletions examples/editor/examples/react-custom-blocks/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { defaultBlockSpecs, defaultProps } from "@blocknote/core";
import "@blocknote/core/style.css";
import {
BlockNoteView,
createReactBlockSpec,
useBlockNote,
} from "@blocknote/react";

type WindowWithProseMirror = Window & typeof globalThis & { ProseMirror: any };

// The types of alerts that users can choose from
const alertTypes = {
warning: {
icon: "⚠️",
color: "#e69819",
backgroundColor: "#fff6e6",
},
error: {
icon: "⛔",
color: "#d80d0d",
backgroundColor: "#ffe6e6",
},
info: {
icon: "ℹ️",
color: "#507aff",
backgroundColor: "#e6ebff",
},
success: {
icon: "✅",
color: "#0bc10b",
backgroundColor: "#e6ffe6",
},
};

export const alertBlock = createReactBlockSpec(
{
type: "alert",
propSchema: {
textAlignment: defaultProps.textAlignment,
textColor: defaultProps.textColor,
type: {
default: "warning" as const,
values: ["warning", "error", "info", "success"] as const,
},
},
content: "inline",
},
{
render: (props) => (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
flexGrow: "1",
height: "48px",
padding: "4px",
maxWidth: "100%",
backgroundColor: alertTypes[props.block.props.type].backgroundColor,
}}>
<select
contentEditable={false}
value={props.block.props.type}
onChange={(event) => {
props.editor.updateBlock(props.block, {
type: "alert",
props: { type: event.target.value as keyof typeof alertTypes },
});
}}>
<option value="warning">{alertTypes["warning"].icon}</option>
<option value="error">{alertTypes["error"].icon}</option>
<option value="info">{alertTypes["info"].icon}</option>
<option value="success">{alertTypes["success"].icon}</option>
</select>
<div style={{ flexGrow: 1 }} ref={props.contentRef} />
</div>
),
}
);

export const bracketsParagraphBlock = createReactBlockSpec(
{
type: "bracketsParagraph",
content: "inline",
propSchema: {
...defaultProps,
},
},
{
render: (props) => (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
flexGrow: "1",
height: "48px",
padding: "4px",
maxWidth: "100%",
}}>
<div contentEditable={"false"}>{"["}</div>
<span contentEditable={"false"}>{"{"}</span>
<div style={{ flexGrow: 1 }} ref={props.contentRef} />
<span contentEditable={"false"}>{"}"}</span>
<div contentEditable={"false"}>{"]"}</div>
</div>
),
}
);

export function ReactCustomBlocks() {
const editor = useBlockNote({
domAttributes: {
editor: {
class: "editor",
"data-test": "editor",
},
},
blockSpecs: {
...defaultBlockSpecs,
alert: alertBlock,
bracketsParagraph: bracketsParagraphBlock,
},
initialContent: [
{
type: "alert",
props: {
type: "success",
},
content: "Alert",
},
{
type: "bracketsParagraph",
content: "Brackets Paragraph",
},
],
});

// Give tests a way to get prosemirror instance
(window as WindowWithProseMirror).ProseMirror = editor?._tiptapEditor;

return <BlockNoteView className="root" editor={editor} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ export function ReactInlineContent() {
"I love ",
{
type: "tag",
// props: {},
content: "BlockNote",
} as any,
],
Expand Down
213 changes: 213 additions & 0 deletions examples/editor/examples/vanilla-custom-blocks/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import {
createBlockSpec,
defaultBlockSpecs,
defaultProps,
} from "@blocknote/core";
import "@blocknote/core/style.css";
import { BlockNoteView, useBlockNote } from "@blocknote/react";

type WindowWithProseMirror = Window & typeof globalThis & { ProseMirror: any };

// The types of alerts that users can choose from
const alertTypes = {
warning: {
icon: "⚠️",
color: "#e69819",
backgroundColor: "#fff6e6",
},
error: {
icon: "⛔",
color: "#d80d0d",
backgroundColor: "#ffe6e6",
},
info: {
icon: "ℹ️",
color: "#507aff",
backgroundColor: "#e6ebff",
},
success: {
icon: "✅",
color: "#0bc10b",
backgroundColor: "#e6ffe6",
},
};

const alertBlock = createBlockSpec(
{
type: "alert",
propSchema: {
textAlignment: defaultProps.textAlignment,
textColor: defaultProps.textColor,
type: {
default: "warning" as const,
values: ["warning", "error", "info", "success"] as const,
},
},
content: "inline",
},
{
render: (block, editor) => {
const alert = document.createElement("div");
Object.entries(alertStyles).forEach(([key, value]) => {
alert.style[key as any] = value;
});
alert.style.backgroundColor =
alertTypes[block.props.type].backgroundColor;

const dropdown = document.createElement("select");
dropdown.contentEditable = "false";
dropdown.addEventListener("change", () => {
// TODO: Something is not quite right with the typing seems like
editor.updateBlock(block, {
type: "alert",
props: { type: dropdown.value as keyof typeof alertTypes },
});
});
dropdown.options.add(
new Option(
alertTypes["warning"].icon,
"warning",
block.props.type === "warning",
block.props.type === "warning"
)
);
dropdown.options.add(
new Option(
alertTypes["error"].icon,
"error",
block.props.type === "error",
block.props.type === "error"
)
);
dropdown.options.add(
new Option(
alertTypes["info"].icon,
"info",
block.props.type === "info",
block.props.type === "info"
)
);
dropdown.options.add(
new Option(
alertTypes["success"].icon,
"success",
block.props.type === "success",
block.props.type === "success"
)
);
alert.appendChild(dropdown);

const inlineContent = document.createElement("div");
inlineContent.style.flexGrow = "1";

alert.appendChild(inlineContent);

return {
dom: alert,
contentDOM: inlineContent,
};
},
}
);

// TODO: use CSS?
const alertStyles = {
display: "flex",
justifyContent: "center",
alignItems: "center",
flexGrow: "1",
height: "48px",
padding: "4px",
maxWidth: "100%",
};

const bracketsParagraphBlock = createBlockSpec(
{
type: "bracketsParagraph",
content: "inline",
propSchema: {
...defaultProps,
},
},
{
render: () => {
const bracketsParagraph = document.createElement("div");
Object.entries(bracketsParagraphStyles).forEach(([key, value]) => {
bracketsParagraph.style[key as any] = value;
});

const leftBracket = document.createElement("div");
leftBracket.contentEditable = "false";
leftBracket.innerText = "[";
bracketsParagraph.appendChild(leftBracket);
const leftCurlyBracket = document.createElement("span");
leftCurlyBracket.contentEditable = "false";
leftCurlyBracket.innerText = "{";
bracketsParagraph.appendChild(leftCurlyBracket);

const inlineContent = document.createElement("div");
inlineContent.style.flexGrow = "1";

bracketsParagraph.appendChild(inlineContent);

const rightCurlyBracket = document.createElement("span");
rightCurlyBracket.contentEditable = "false";
rightCurlyBracket.innerText = "}";
bracketsParagraph.appendChild(rightCurlyBracket);
const rightBracket = document.createElement("div");
rightBracket.contentEditable = "false";
rightBracket.innerText = "]";
bracketsParagraph.appendChild(rightBracket);

return {
dom: bracketsParagraph,
contentDOM: inlineContent,
};
},
}
);

// TODO: use CSS
const bracketsParagraphStyles = {
display: "flex",
justifyContent: "center",
alignItems: "center",
flexGrow: "1",
height: "48px",
padding: "4px",
maxWidth: "100%",
};

export function CustomBlocks() {
const editor = useBlockNote({
domAttributes: {
editor: {
class: "editor",
"data-test": "editor",
},
},
blockSpecs: {
...defaultBlockSpecs,
alert: alertBlock,
bracketsParagraph: bracketsParagraphBlock,
},
initialContent: [
{
type: "alert",
props: {
type: "success",
},
content: ["Alert"],
},
{
type: "bracketsParagraph",
content: "Brackets Paragraph",
},
],
});

// Give tests a way to get prosemirror instance
(window as WindowWithProseMirror).ProseMirror = editor?._tiptapEditor;

return <BlockNoteView className="root" editor={editor} />;
}
Loading