Skip to content

Commit 541c747

Browse files
feat: allow inline content to be draggable (#1818)
1 parent b7e2d55 commit 541c747

File tree

13 files changed

+269
-0
lines changed

13 files changed

+269
-0
lines changed

docs/pages/docs/custom-schemas/custom-inline-content.mdx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ type CustomInlineContentConfig = {
4747
type: string;
4848
content: "styled" | "none";
4949
readonly propSchema: PropSchema;
50+
draggable?: boolean;
5051
};
5152
```
5253

@@ -84,6 +85,12 @@ type PropSchema<
8485
8586
_In the mentions demo, we add a `user` prop for the user that's being mentioned._
8687
88+
**`draggable`**
89+
90+
Specifies whether the inline content can be dragged within the editor. If set to `true`, the inline content will be draggable. Defaults to `false` if not specified.
91+
92+
If this is true, you should add `data-drag-handle` to the DOM element that should function as the drag handle.
93+
8794
#### Inline Content Implementation (`ReactCustomInlineContentImplementation`)
8895
8996
The Inline Content Implementation defines how the inline content should be rendered to HTML.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"playground": true,
3+
"docs": false,
4+
"author": "hectorzhuang",
5+
"tags": []
6+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { BlockNoteSchema, defaultInlineContentSpecs } from "@blocknote/core";
2+
import "@blocknote/core/fonts/inter.css";
3+
import {
4+
createReactInlineContentSpec,
5+
useCreateBlockNote,
6+
} from "@blocknote/react";
7+
import { BlockNoteView } from "@blocknote/mantine";
8+
import "@blocknote/mantine/style.css";
9+
10+
const draggableButton = createReactInlineContentSpec(
11+
{
12+
type: "draggableButton",
13+
propSchema: {
14+
title: {
15+
default: "",
16+
},
17+
},
18+
draggable: true,
19+
content: "none"
20+
},
21+
{
22+
render: (props) => {
23+
return (
24+
<span
25+
style={{
26+
border: "none",
27+
background: "blue",
28+
color: "white",
29+
padding: "5px 10px",
30+
borderRadius: "4px",
31+
cursor: "move",
32+
}}
33+
data-drag-handle>
34+
<span>{props.inlineContent.props.title}</span>
35+
</span>
36+
);
37+
},
38+
}
39+
);
40+
41+
const schema = BlockNoteSchema.create({
42+
inlineContentSpecs: {
43+
draggableButton,
44+
...defaultInlineContentSpecs,
45+
},
46+
});
47+
48+
export default function ReactInlineContent() {
49+
const editor = useCreateBlockNote({
50+
schema,
51+
initialContent: [
52+
{
53+
type: "paragraph",
54+
content: [
55+
"I enjoy working with ",
56+
{
57+
type: "draggableButton",
58+
props: {
59+
title: "Hector",
60+
},
61+
},
62+
],
63+
},
64+
{
65+
type: "paragraph",
66+
content: [
67+
"I love ",
68+
{
69+
type: "draggableButton",
70+
props: {
71+
title: "BlockNote",
72+
},
73+
},
74+
],
75+
},
76+
],
77+
});
78+
79+
return <BlockNoteView editor={editor} />;
80+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Draggable Inline Content
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<html lang="en">
2+
<head>
3+
<meta charset="UTF-8" />
4+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
5+
<title>Draggable Inline Content</title>
6+
<script>
7+
<!-- AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY -->
8+
</script>
9+
</head>
10+
<body>
11+
<div id="root"></div>
12+
<script type="module" src="./main.tsx"></script>
13+
</body>
14+
</html>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY
2+
import React from "react";
3+
import { createRoot } from "react-dom/client";
4+
import App from "./App.jsx";
5+
6+
const root = createRoot(document.getElementById("root")!);
7+
root.render(
8+
<React.StrictMode>
9+
<App />
10+
</React.StrictMode>
11+
);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "@blocknote/example-custom-schema-draggable-inline-content",
3+
"description": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY",
4+
"private": true,
5+
"version": "0.12.4",
6+
"scripts": {
7+
"start": "vite",
8+
"dev": "vite",
9+
"build:prod": "tsc && vite build",
10+
"preview": "vite preview"
11+
},
12+
"dependencies": {
13+
"@blocknote/core": "latest",
14+
"@blocknote/react": "latest",
15+
"@blocknote/ariakit": "latest",
16+
"@blocknote/mantine": "latest",
17+
"@blocknote/shadcn": "latest",
18+
"react": "^19.1.0",
19+
"react-dom": "^19.1.0"
20+
},
21+
"devDependencies": {
22+
"@types/react": "^19.1.0",
23+
"@types/react-dom": "^19.1.0",
24+
"@vitejs/plugin-react": "^4.3.1",
25+
"vite": "^5.3.4"
26+
}
27+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"__comment": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY",
3+
"compilerOptions": {
4+
"target": "ESNext",
5+
"useDefineForClassFields": true,
6+
"lib": [
7+
"DOM",
8+
"DOM.Iterable",
9+
"ESNext"
10+
],
11+
"allowJs": false,
12+
"skipLibCheck": true,
13+
"esModuleInterop": false,
14+
"allowSyntheticDefaultImports": true,
15+
"strict": true,
16+
"forceConsistentCasingInFileNames": true,
17+
"module": "ESNext",
18+
"moduleResolution": "bundler",
19+
"resolveJsonModule": true,
20+
"isolatedModules": true,
21+
"noEmit": true,
22+
"jsx": "react-jsx",
23+
"composite": true
24+
},
25+
"include": [
26+
"."
27+
],
28+
"__ADD_FOR_LOCAL_DEV_references": [
29+
{
30+
"path": "../../../packages/core/"
31+
},
32+
{
33+
"path": "../../../packages/react/"
34+
}
35+
]
36+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY
2+
import react from "@vitejs/plugin-react";
3+
import * as fs from "fs";
4+
import * as path from "path";
5+
import { defineConfig } from "vite";
6+
// import eslintPlugin from "vite-plugin-eslint";
7+
// https://vitejs.dev/config/
8+
export default defineConfig((conf) => ({
9+
plugins: [react()],
10+
optimizeDeps: {},
11+
build: {
12+
sourcemap: true,
13+
},
14+
resolve: {
15+
alias:
16+
conf.command === "build" ||
17+
!fs.existsSync(path.resolve(__dirname, "../../packages/core/src"))
18+
? {}
19+
: ({
20+
// Comment out the lines below to load a built version of blocknote
21+
// or, keep as is to load live from sources with live reload working
22+
"@blocknote/core": path.resolve(
23+
__dirname,
24+
"../../packages/core/src/"
25+
),
26+
"@blocknote/react": path.resolve(
27+
__dirname,
28+
"../../packages/react/src/"
29+
),
30+
} as any),
31+
},
32+
}));

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { StyleSchema, Styles } from "../styles/types.js";
55
export type CustomInlineContentConfig = {
66
type: string;
77
content: "styled" | "none"; // | "plain"
8+
draggable?: boolean;
89
readonly propSchema: PropSchema;
910
// content: "inline" | "none" | "table";
1011
};

0 commit comments

Comments
 (0)