Skip to content
Open
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
11 changes: 11 additions & 0 deletions examples/06-custom-schema/08-custom-table-props/.bnexample.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"playground": true,
"docs": true,
"author": "Richard Misiak",
"tags": [
"Intermediate",
"UI Components",
"Tables",
"Appearance & Styling"
]
}
8 changes: 8 additions & 0 deletions examples/06-custom-schema/08-custom-table-props/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Custom table props

This example demonstrates overriding the schema for tables to allow for custom props to be used.

**Relevant Docs:**

- [Tables](/docs/features/custom-schemas)
- [Custom Schemas](/docs/features/custom-schemas)
14 changes: 14 additions & 0 deletions examples/06-custom-schema/08-custom-table-props/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Custom table props</title>
<script>
<!-- AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY -->
</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="./main.tsx"></script>
</body>
</html>
11 changes: 11 additions & 0 deletions examples/06-custom-schema/08-custom-table-props/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./src/App.jsx";

const root = createRoot(document.getElementById("root")!);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
31 changes: 31 additions & 0 deletions examples/06-custom-schema/08-custom-table-props/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "@blocknote/example-custom-schema-custom-table-props",
"description": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY",
"type": "module",
"private": true,
"version": "0.12.4",
"scripts": {
"start": "vite",
"dev": "vite",
"build:prod": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@blocknote/ariakit": "latest",
"@blocknote/core": "latest",
"@blocknote/mantine": "latest",
"@blocknote/react": "latest",
"@blocknote/shadcn": "latest",
"@mantine/core": "^8.3.11",
"@mantine/hooks": "^8.3.11",
"@mantine/utils": "^6.0.22",
"react": "^19.2.3",
"react-dom": "^19.2.3"
},
"devDependencies": {
"@types/react": "^19.2.3",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^4.7.0",
"vite": "^5.4.20"
}
}
147 changes: 147 additions & 0 deletions examples/06-custom-schema/08-custom-table-props/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import "@blocknote/core/fonts/inter.css";
import "@blocknote/mantine/style.css";
import { BlockNoteView } from "@blocknote/mantine";
import { useCreateBlockNote } from "@blocknote/react";
import {
BlockNoteSchema,
defaultBlockSpecs,
createTableBlockSpec,
} from "@blocknote/core";
import { useState } from "react";

const customTable = createTableBlockSpec({
customProp: {
default: "",
},
});

const schema = BlockNoteSchema.create({
blockSpecs: {
...defaultBlockSpecs,
table: customTable,
},
});

export default function App() {
const editor = useCreateBlockNote({
schema,
initialContent: [
{
type: "table",
props: {
textColor: "default",
customProp: "custom",
},
content: {
type: "tableContent",

headerRows: 1,
rows: [
{
cells: [
{
type: "tableCell",
content: [
{
type: "text",
text: "Foo",
styles: {},
},
],
},
{
type: "tableCell",
content: [
{
type: "text",
text: "Bar",
styles: {},
},
],
},
],
},
{
cells: [
{
type: "tableCell",
content: [
{
type: "text",
text: "value",
styles: {},
},
],
},
{
type: "tableCell",
content: [
{
type: "text",
text: "value",
styles: {},
},
],
},
],
},
],
},
children: [],
},
{
type: "paragraph",
content: [
{
type: "text",
text: "This is a table that has a custom prop `customProp` defined. Click on the button to change it to a random value. The JSON dump of the document is shown.",
},
],
},
],
});

const [doc, setDoc] = useState(editor.document);

editor.onChange((editor) => {
setDoc(editor.document); // re-render on changes
});

return (
<div style={{ display: "flex", flexDirection: "row", height: "100vh" }}>
<div
style={{
display: "flex",
flexDirection: "column",
gap: "10px",
flex: 1,
}}
>
<BlockNoteView editor={editor}></BlockNoteView>
<button
onClick={() =>
editor.updateBlock(editor.document[0], {
props: { customProp: Math.random().toString() },
})
}
>
Change the prop
</button>
</div>
<div
style={{
display: "flex",
flexDirection: "column",
gap: "10px",
flex: 1,
minHeight: 0,
}}
>
<p>Document:</p>
<pre style={{ flex: 1, overflow: "auto", margin: 0 }}>
{JSON.stringify(editor.document, null, 2)}
</pre>
</div>
</div>
);
}
36 changes: 36 additions & 0 deletions examples/06-custom-schema/08-custom-table-props/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"__comment": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY",
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": [
"DOM",
"DOM.Iterable",
"ESNext"
],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"composite": true
},
"include": [
"."
],
"__ADD_FOR_LOCAL_DEV_references": [
{
"path": "../../../packages/core/"
},
{
"path": "../../../packages/react/"
}
]
}
32 changes: 32 additions & 0 deletions examples/06-custom-schema/08-custom-table-props/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY
import react from "@vitejs/plugin-react";
import * as fs from "fs";
import * as path from "path";
import { defineConfig } from "vite";
// import eslintPlugin from "vite-plugin-eslint";
// https://vitejs.dev/config/
export default defineConfig((conf) => ({
plugins: [react()],
optimizeDeps: {},
build: {
sourcemap: true,
},
resolve: {
alias:
conf.command === "build" ||
!fs.existsSync(path.resolve(__dirname, "../../packages/core/src"))
? {}
: ({
// Comment out the lines below to load a built version of blocknote
// or, keep as is to load live from sources with live reload working
"@blocknote/core": path.resolve(
__dirname,
"../../packages/core/src/"
),
"@blocknote/react": path.resolve(
__dirname,
"../../packages/react/src/"
),
} as any),
},
}));
20 changes: 16 additions & 4 deletions packages/core/src/api/nodeConversions/nodeToBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,11 +425,25 @@ export function nodeToBlock<
throw Error("Block is of an unrecognized type: " + blockInfo.blockNoteType);
}

const blockConfig = blockSchema[blockInfo.blockNoteType];

const props: any = {};
for (const [attr, value] of Object.entries({

// Collect attributes from node, blockContent, and table content node (for tables)
const attrsToCheck = {
...node.attrs,
...(blockInfo.isBlockContainer ? blockInfo.blockContent.node.attrs : {}),
})) {
};

// For table blocks, also check the table content node for custom props
if (blockConfig.content === "table" && blockInfo.isBlockContainer) {
const tableContentNode = blockInfo.blockContent.node;
if (tableContentNode && tableContentNode.attrs) {
Object.assign(attrsToCheck, tableContentNode.attrs);
}
}

for (const [attr, value] of Object.entries(attrsToCheck)) {
const propSchema = blockSpec.propSchema;

if (
Expand All @@ -440,8 +454,6 @@ export function nodeToBlock<
}
}

const blockConfig = blockSchema[blockInfo.blockNoteType];

const children: Block<BSchema, I, S>[] = [];
blockInfo.childContainer?.node.forEach((child) => {
children.push(
Expand Down
24 changes: 20 additions & 4 deletions packages/core/src/blocks/Table/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
createBlockSpecFromTiptapNode,
TableContent,
} from "../../schema/index.js";
import { propsToAttributes } from "../../schema/blocks/internal.js";
import { PropSchema } from "../../schema/propTypes.js";
import { mergeCSSClasses } from "../../util/browser.js";
import { createDefaultBlockDOMOutputSpec } from "../defaultBlockHelpers.js";
import { defaultProps } from "../defaultProps.js";
Expand Down Expand Up @@ -381,10 +383,23 @@ export type TableBlockConfig = BlockConfig<
"table"
>;

export const createTableBlockSpec = () =>
createBlockSpecFromTiptapNode(
{ node: TiptapTableNode, type: "table", content: "table" },
tablePropSchema,
export const createTableBlockSpec = (additionalPropSchema?: PropSchema) => {
// Merge the base table prop schema with any additional props
const mergedPropSchema = {
...tablePropSchema,
...(additionalPropSchema || {}),
};

// Extend the TiptapTableNode to add attributes based on the merged prop schema
const extendedTableNode = TiptapTableNode.extend({
addAttributes() {
return propsToAttributes(mergedPropSchema);
},
});

return createBlockSpecFromTiptapNode(
{ node: extendedTableNode, type: "table", content: "table" },
mergedPropSchema,
[
createExtension({
key: "table-extensions",
Expand Down Expand Up @@ -452,6 +467,7 @@ export const createTableBlockSpec = () =>
}),
],
);
};

// We need to declare this here because we aren't using the table extensions from tiptap, so the types are not automatically inferred.
declare module "@tiptap/core" {
Expand Down
Loading