@use-kona/editor 0.1.29 → 0.1.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/createEditable.js +4 -1
- package/dist/plugins/CodeBlockPlugin/styles_module.css +1 -0
- package/dist/plugins/CollapsibleBlocksPlugin/CollapsibleBlocksPlugin.js +123 -0
- package/dist/plugins/CollapsibleBlocksPlugin/index.js +2 -0
- package/dist/plugins/CollapsibleBlocksPlugin/styles.module.js +8 -0
- package/dist/plugins/CollapsibleBlocksPlugin/styles_module.css +38 -0
- package/dist/plugins/DnDPlugin/DnDPlugin.js +62 -19
- package/dist/plugins/index.js +2 -1
- package/dist/src/plugins/CollapsibleBlocksPlugin/CollapsibleBlocksPlugin.d.ts +14 -0
- package/dist/src/plugins/CollapsibleBlocksPlugin/index.d.ts +1 -0
- package/dist/src/plugins/DnDPlugin/DnDPlugin.d.ts +2 -1
- package/dist/src/plugins/DnDPlugin/types.d.ts +1 -0
- package/dist/src/plugins/index.d.ts +1 -0
- package/dist/src/types.d.ts +5 -0
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
- package/src/core/createEditable.tsx +5 -1
- package/src/plugins/CodeBlockPlugin/styles.module.css +1 -0
- package/src/plugins/CollapsibleBlocksPlugin/CollapsibleBlocksPlugin.spec.tsx +216 -0
- package/src/plugins/CollapsibleBlocksPlugin/CollapsibleBlocksPlugin.tsx +190 -0
- package/src/plugins/CollapsibleBlocksPlugin/index.ts +1 -0
- package/src/plugins/CollapsibleBlocksPlugin/styles.module.css +37 -0
- package/src/plugins/DnDPlugin/DnDPlugin.spec.ts +408 -0
- package/src/plugins/DnDPlugin/DnDPlugin.tsx +122 -32
- package/src/plugins/DnDPlugin/types.ts +1 -0
- package/src/plugins/index.ts +1 -0
- package/src/types.ts +5 -0
|
@@ -19,6 +19,9 @@ const SUPPORTED_HANDLERS = [
|
|
|
19
19
|
const createEditable = (editor, plugins)=>({ readOnly })=>{
|
|
20
20
|
const [selectedNodes] = useState(()=>atom([]));
|
|
21
21
|
const $selectedNodes = useStore(selectedNodes);
|
|
22
|
+
const renderBlockPlugins = [
|
|
23
|
+
...plugins
|
|
24
|
+
].sort((left, right)=>(left.renderBlockPriority || 0) - (right.renderBlockPriority || 0));
|
|
22
25
|
const renderElement = (props)=>{
|
|
23
26
|
let result = null;
|
|
24
27
|
for (const plugin of plugins)for (const element of plugin.blocks || []){
|
|
@@ -26,7 +29,7 @@ const createEditable = (editor, plugins)=>({ readOnly })=>{
|
|
|
26
29
|
const newElement = element.render(props, editor);
|
|
27
30
|
result = newElement || result;
|
|
28
31
|
}
|
|
29
|
-
for (const plugin of
|
|
32
|
+
for (const plugin of renderBlockPlugins)if (plugin.renderBlock) result = plugin.renderBlock({
|
|
30
33
|
...props,
|
|
31
34
|
children: result || props.children
|
|
32
35
|
});
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Editor, Element, Range, Transforms } from "slate";
|
|
3
|
+
import { ReactEditor, useSlate } from "slate-react";
|
|
4
|
+
import styles_module from "./styles.module.js";
|
|
5
|
+
const sectionTypesByEditor = new WeakMap();
|
|
6
|
+
class CollapsibleBlocksPlugin {
|
|
7
|
+
types;
|
|
8
|
+
renderBlockPriority = -1;
|
|
9
|
+
constructor(types){
|
|
10
|
+
this.types = types;
|
|
11
|
+
}
|
|
12
|
+
init(editor) {
|
|
13
|
+
sectionTypesByEditor.set(editor, this.types);
|
|
14
|
+
return editor;
|
|
15
|
+
}
|
|
16
|
+
renderBlock = (props)=>{
|
|
17
|
+
const editor = useSlate();
|
|
18
|
+
const path = ReactEditor.findPath(editor, props.element);
|
|
19
|
+
if (1 !== path.length) return props.children;
|
|
20
|
+
const isHidden = isHiddenByCollapsedSection(editor, path);
|
|
21
|
+
if (isHidden) return /*#__PURE__*/ jsx("div", {
|
|
22
|
+
className: styles_module.hidden,
|
|
23
|
+
"data-collapsible-block-hidden": true,
|
|
24
|
+
hidden: true,
|
|
25
|
+
children: props.children
|
|
26
|
+
});
|
|
27
|
+
if (this.types.includes(props.element.type)) return /*#__PURE__*/ jsx(CollapsibleBoundary, {
|
|
28
|
+
editor: editor,
|
|
29
|
+
element: props.element,
|
|
30
|
+
path: path,
|
|
31
|
+
types: this.types,
|
|
32
|
+
children: props.children
|
|
33
|
+
});
|
|
34
|
+
return props.children;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const CollapsibleBoundary = ({ children, editor, element, path, types })=>{
|
|
38
|
+
const isCollapsed = true === element.collapsed;
|
|
39
|
+
const toggle = ()=>{
|
|
40
|
+
const willCollapse = !isCollapsed;
|
|
41
|
+
Transforms.setNodes(editor, {
|
|
42
|
+
collapsed: willCollapse
|
|
43
|
+
}, {
|
|
44
|
+
at: path
|
|
45
|
+
});
|
|
46
|
+
if (willCollapse && isSelectionInsideSection(editor, path, types)) Transforms.select(editor, Editor.start(editor, path));
|
|
47
|
+
};
|
|
48
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
49
|
+
className: styles_module.boundary,
|
|
50
|
+
children: [
|
|
51
|
+
/*#__PURE__*/ jsx("button", {
|
|
52
|
+
"aria-expanded": !isCollapsed,
|
|
53
|
+
"aria-label": isCollapsed ? 'Expand section' : 'Collapse section',
|
|
54
|
+
className: isCollapsed ? styles_module.chevron : `${styles_module.chevron} ${styles_module.expanded}`,
|
|
55
|
+
contentEditable: false,
|
|
56
|
+
onClick: toggle,
|
|
57
|
+
onMouseDown: (event)=>event.preventDefault(),
|
|
58
|
+
type: "button",
|
|
59
|
+
children: /*#__PURE__*/ jsxs("svg", {
|
|
60
|
+
"aria-hidden": "true",
|
|
61
|
+
fill: "none",
|
|
62
|
+
height: "16",
|
|
63
|
+
stroke: "currentColor",
|
|
64
|
+
strokeLinecap: "round",
|
|
65
|
+
strokeLinejoin: "round",
|
|
66
|
+
strokeWidth: "1.5",
|
|
67
|
+
viewBox: "0 0 24 24",
|
|
68
|
+
width: "16",
|
|
69
|
+
children: [
|
|
70
|
+
/*#__PURE__*/ jsx("path", {
|
|
71
|
+
d: "M0 0h24v24H0z",
|
|
72
|
+
fill: "none",
|
|
73
|
+
stroke: "none"
|
|
74
|
+
}),
|
|
75
|
+
/*#__PURE__*/ jsx("path", {
|
|
76
|
+
d: "M9 6l6 6l-6 6"
|
|
77
|
+
})
|
|
78
|
+
]
|
|
79
|
+
})
|
|
80
|
+
}),
|
|
81
|
+
children
|
|
82
|
+
]
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
const getCollapsibleSectionPaths = (editor, path)=>{
|
|
86
|
+
const types = sectionTypesByEditor.get(editor);
|
|
87
|
+
if (!types || 1 !== path.length) return null;
|
|
88
|
+
const boundary = editor.children[path[0]];
|
|
89
|
+
if (!boundary || !types.includes(boundary.type)) return null;
|
|
90
|
+
const end = getSectionEnd(editor, path[0], types);
|
|
91
|
+
return Array.from({
|
|
92
|
+
length: end - path[0]
|
|
93
|
+
}, (_, index)=>[
|
|
94
|
+
path[0] + index
|
|
95
|
+
]);
|
|
96
|
+
};
|
|
97
|
+
const isHiddenByCollapsedSection = (editor, path)=>{
|
|
98
|
+
const types = sectionTypesByEditor.get(editor);
|
|
99
|
+
if (!types) return false;
|
|
100
|
+
for(let index = 0; index < path[0]; index++){
|
|
101
|
+
const node = editor.children[index];
|
|
102
|
+
if (Element.isElement(node) && true === node.collapsed && types.includes(node.type) && path[0] < getSectionEnd(editor, index, types)) return true;
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
};
|
|
106
|
+
const isSelectionInsideSection = (editor, boundaryPath, types)=>{
|
|
107
|
+
const { selection } = editor;
|
|
108
|
+
if (!selection || !Range.isCollapsed(selection)) return false;
|
|
109
|
+
const selectionIndex = selection.focus.path[0];
|
|
110
|
+
if (selectionIndex <= boundaryPath[0]) return false;
|
|
111
|
+
return selectionIndex < getSectionEnd(editor, boundaryPath[0], types);
|
|
112
|
+
};
|
|
113
|
+
const getSectionEnd = (editor, boundaryIndex, types)=>{
|
|
114
|
+
const boundary = editor.children[boundaryIndex];
|
|
115
|
+
const boundaryLevel = types.indexOf(boundary.type);
|
|
116
|
+
for(let index = boundaryIndex + 1; index < editor.children.length; index++){
|
|
117
|
+
const node = editor.children[index];
|
|
118
|
+
const nodeLevel = Element.isElement(node) ? types.indexOf(node.type) : -1;
|
|
119
|
+
if (-1 !== nodeLevel && nodeLevel <= boundaryLevel) return index;
|
|
120
|
+
}
|
|
121
|
+
return editor.children.length;
|
|
122
|
+
};
|
|
123
|
+
export { CollapsibleBlocksPlugin, getCollapsibleSectionPaths, isHiddenByCollapsedSection };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
.boundary-gLj40W {
|
|
2
|
+
position: relative;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.chevron-up0Fid {
|
|
6
|
+
appearance: none;
|
|
7
|
+
color: inherit;
|
|
8
|
+
cursor: pointer;
|
|
9
|
+
inset-inline-start: -20px;
|
|
10
|
+
width: 16px;
|
|
11
|
+
height: 16px;
|
|
12
|
+
font: inherit;
|
|
13
|
+
background: none;
|
|
14
|
+
border: 0;
|
|
15
|
+
justify-content: center;
|
|
16
|
+
align-items: center;
|
|
17
|
+
margin: 0;
|
|
18
|
+
padding: 0;
|
|
19
|
+
line-height: 1;
|
|
20
|
+
transition: transform .15s;
|
|
21
|
+
display: flex;
|
|
22
|
+
position: absolute;
|
|
23
|
+
top: 50%;
|
|
24
|
+
transform: translateY(-50%);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.expanded-HCZXmg {
|
|
28
|
+
transform: translateY(-50%)rotate(90deg);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.chevron-up0Fid svg {
|
|
32
|
+
display: block;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.hidden-icZZ78 {
|
|
36
|
+
display: none;
|
|
37
|
+
}
|
|
38
|
+
|
|
@@ -6,6 +6,7 @@ import { NativeTypes } from "react-dnd-html5-backend";
|
|
|
6
6
|
import { Editor, Element, Path, Transforms } from "slate";
|
|
7
7
|
import { ReactEditor, useReadOnly, useSlate } from "slate-react";
|
|
8
8
|
import { useEditorContext } from "../../provider.js";
|
|
9
|
+
import { getCollapsibleSectionPaths, isHiddenByCollapsedSection } from "../CollapsibleBlocksPlugin/CollapsibleBlocksPlugin.js";
|
|
9
10
|
import { getNodesByNodeIds } from "./utils.js";
|
|
10
11
|
class DnDPlugin {
|
|
11
12
|
options;
|
|
@@ -33,11 +34,14 @@ class DnDPlugin {
|
|
|
33
34
|
const dropTargetRef = useRef(null);
|
|
34
35
|
const customType = options.customTypes?.[props.element.type];
|
|
35
36
|
const currentElement = props.element;
|
|
37
|
+
const path = ReactEditor.findPath(editor, props.element);
|
|
38
|
+
const isHidden = isHiddenByCollapsedSection(editor, path);
|
|
36
39
|
const [, drag, preview] = useDrag({
|
|
37
40
|
type: customType?.type || DnDPlugin.DND_BLOCK_ELEMENT,
|
|
38
41
|
item: ()=>{
|
|
39
42
|
const selected = $selectedNodes;
|
|
40
|
-
const
|
|
43
|
+
const sectionPaths = getCollapsibleSectionPaths(editor, path);
|
|
44
|
+
const nodes = sectionPaths ? getNodesAtPaths(editor, sectionPaths) : selected.length > 0 && selected.find((node)=>node.nodeId === currentElement.nodeId) ? getNodesByNodeIds(editor, selected.map((node)=>node.nodeId)) : [
|
|
41
45
|
structuredClone(props.element)
|
|
42
46
|
];
|
|
43
47
|
const item = this.options.customTypes?.[currentElement.type]?.getData?.(currentElement);
|
|
@@ -52,7 +56,9 @@ class DnDPlugin {
|
|
|
52
56
|
items,
|
|
53
57
|
source: {
|
|
54
58
|
editor,
|
|
55
|
-
documentId: options.documentId
|
|
59
|
+
documentId: options.documentId,
|
|
60
|
+
path,
|
|
61
|
+
sectionPaths: sectionPaths || void 0
|
|
56
62
|
}
|
|
57
63
|
};
|
|
58
64
|
return dragItem;
|
|
@@ -77,14 +83,8 @@ class DnDPlugin {
|
|
|
77
83
|
};
|
|
78
84
|
},
|
|
79
85
|
hover (_, monitor) {
|
|
80
|
-
const
|
|
81
|
-
if (
|
|
82
|
-
const hoverBoundingRect = element.getBoundingClientRect();
|
|
83
|
-
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
|
|
84
|
-
const clientOffset = monitor.getClientOffset();
|
|
85
|
-
if (!clientOffset) return;
|
|
86
|
-
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
|
|
87
|
-
hoverClientY < hoverMiddleY ? setDropPosition('top') : setDropPosition('bottom');
|
|
86
|
+
const position = getDropPosition(dropTargetRef.current, monitor);
|
|
87
|
+
if (position) setDropPosition(position);
|
|
88
88
|
},
|
|
89
89
|
drop: (data, monitor)=>{
|
|
90
90
|
const itemType = monitor.getItemType();
|
|
@@ -93,7 +93,8 @@ class DnDPlugin {
|
|
|
93
93
|
const isSameEditor = 'inner' === data.kind && data.source?.editor === editor;
|
|
94
94
|
const sourceTo = ReactEditor.findPath(editor, props.element);
|
|
95
95
|
if (!sourceTo) return;
|
|
96
|
-
const
|
|
96
|
+
const position = getDropPosition(dropTargetRef.current, monitor) || dropPosition || 'bottom';
|
|
97
|
+
const insertAt = getDropPath(editor, sourceTo, position);
|
|
97
98
|
if (!insertAt) return;
|
|
98
99
|
const customDropHandler = itemType && options.customDropHandlers?.[itemType];
|
|
99
100
|
if (customDropHandler && 'outer' === data.kind) return void customDropHandler.onDrop({
|
|
@@ -109,18 +110,18 @@ class DnDPlugin {
|
|
|
109
110
|
{
|
|
110
111
|
const dragItem = data;
|
|
111
112
|
if ('outer' === dragItem.kind) return;
|
|
112
|
-
if (!
|
|
113
|
-
const nodeIds = dragItem.nodes.map((node)=>node.nodeId);
|
|
113
|
+
if (!dragItem.nodes.length) return;
|
|
114
|
+
const nodeIds = dragItem.nodes.map((node)=>node.nodeId).filter((nodeId)=>'string' == typeof nodeId);
|
|
114
115
|
if (isSameDocument || isSameEditor) if (isCopying.current) {
|
|
115
116
|
if (!generateId) return;
|
|
116
117
|
const clonedNodes = cloneWithNewIds(dragItem.nodes, generateId);
|
|
117
118
|
insertNodes(editor, insertAt, clonedNodes);
|
|
118
|
-
} else moveNode(editor, sourceTo, nodeIds,
|
|
119
|
+
} else moveNode(editor, sourceTo, nodeIds, position, dragItem.source.path, dragItem.source.sectionPaths);
|
|
119
120
|
else {
|
|
120
121
|
if (!generateId) return;
|
|
121
122
|
const clonedNodes = cloneWithNewIds(dragItem.nodes, generateId);
|
|
122
123
|
insertNodes(editor, insertAt, clonedNodes);
|
|
123
|
-
if (!isCopying.current && sourceEditor) removeNodes(sourceEditor, nodeIds);
|
|
124
|
+
if (!isCopying.current && sourceEditor) removeNodes(sourceEditor, nodeIds, dragItem.source.sectionPaths);
|
|
124
125
|
}
|
|
125
126
|
selectedNodes.set([]);
|
|
126
127
|
break;
|
|
@@ -142,6 +143,7 @@ class DnDPlugin {
|
|
|
142
143
|
if (this.options.ignoreNodes?.includes(props.element.type)) return props.children;
|
|
143
144
|
if (editor.isInline(props.element)) return props.children;
|
|
144
145
|
return /*#__PURE__*/ jsx("div", {
|
|
146
|
+
hidden: isHidden,
|
|
145
147
|
children: options.renderBlock?.({
|
|
146
148
|
props,
|
|
147
149
|
dragRef: drag,
|
|
@@ -174,11 +176,33 @@ const getDropPath = (editor, targetNodePath, position)=>{
|
|
|
174
176
|
return Path.next(target[1]);
|
|
175
177
|
}
|
|
176
178
|
};
|
|
177
|
-
const
|
|
179
|
+
const getDropPosition = (element, monitor)=>{
|
|
180
|
+
if (!element) return null;
|
|
181
|
+
const clientOffset = monitor.getClientOffset();
|
|
182
|
+
if (!clientOffset) return null;
|
|
183
|
+
const rect = element.getBoundingClientRect();
|
|
184
|
+
return clientOffset.y - rect.top < (rect.bottom - rect.top) / 2 ? 'top' : 'bottom';
|
|
185
|
+
};
|
|
186
|
+
const moveNode = (editor, targetNodePath, nodeIds, position, sourcePath, sectionPaths)=>{
|
|
178
187
|
const dropPath = getDropPath(editor, targetNodePath, position);
|
|
179
188
|
if (!dropPath) return;
|
|
180
|
-
if (!
|
|
189
|
+
if (!isValidInsertPath(editor, dropPath)) return;
|
|
190
|
+
if (sectionPaths?.some((path)=>Path.equals(path, targetNodePath))) return;
|
|
181
191
|
Editor.withoutNormalizing(editor, ()=>{
|
|
192
|
+
if (sectionPaths?.length) {
|
|
193
|
+
const sectionNodes = new Set(sectionPaths.filter((path)=>Editor.hasPath(editor, path)).map((path)=>Editor.node(editor, path)[0]));
|
|
194
|
+
Transforms.moveNodes(editor, {
|
|
195
|
+
at: [],
|
|
196
|
+
match: (node)=>sectionNodes.has(node),
|
|
197
|
+
to: dropPath,
|
|
198
|
+
mode: 'highest'
|
|
199
|
+
});
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (0 === nodeIds.length && sourcePath && Editor.hasPath(editor, sourcePath)) return void Transforms.moveNodes(editor, {
|
|
203
|
+
at: sourcePath,
|
|
204
|
+
to: dropPath
|
|
205
|
+
});
|
|
182
206
|
Transforms.moveNodes(editor, {
|
|
183
207
|
at: [],
|
|
184
208
|
match: (n)=>{
|
|
@@ -194,6 +218,13 @@ const moveNode = (editor, targetNodePath, nodeIds, position)=>{
|
|
|
194
218
|
});
|
|
195
219
|
Editor.normalize(editor);
|
|
196
220
|
};
|
|
221
|
+
const isValidInsertPath = (editor, path)=>{
|
|
222
|
+
const parentPath = Path.parent(path);
|
|
223
|
+
const index = path[path.length - 1];
|
|
224
|
+
if (void 0 === index || !Editor.hasPath(editor, parentPath)) return false;
|
|
225
|
+
const [parent] = Editor.node(editor, parentPath);
|
|
226
|
+
return 'children' in parent && index <= parent.children.length;
|
|
227
|
+
};
|
|
197
228
|
const insertNodes = (editor, path, nodes)=>{
|
|
198
229
|
Editor.withoutNormalizing(editor, ()=>{
|
|
199
230
|
Transforms.insertNodes(editor, nodes, {
|
|
@@ -201,8 +232,20 @@ const insertNodes = (editor, path, nodes)=>{
|
|
|
201
232
|
});
|
|
202
233
|
});
|
|
203
234
|
};
|
|
204
|
-
const
|
|
235
|
+
const getNodesAtPaths = (editor, paths)=>paths.filter((path)=>Editor.hasPath(editor, path)).map((path)=>structuredClone(Editor.node(editor, path)[0]));
|
|
236
|
+
const removeNodes = (editor, nodeIds, sectionPaths)=>{
|
|
205
237
|
try {
|
|
238
|
+
if (sectionPaths?.length) {
|
|
239
|
+
const paths = sectionPaths.filter((path)=>Editor.hasPath(editor, path)).sort((left, right)=>Path.compare(right, left));
|
|
240
|
+
Editor.withoutNormalizing(editor, ()=>{
|
|
241
|
+
paths.forEach((path)=>{
|
|
242
|
+
Transforms.removeNodes(editor, {
|
|
243
|
+
at: path
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
206
249
|
Transforms.removeNodes(editor, {
|
|
207
250
|
at: [],
|
|
208
251
|
match: (n)=>{
|
|
@@ -228,4 +271,4 @@ const cloneWithNewIds = (nodes, generateId)=>{
|
|
|
228
271
|
copy.forEach(assignId);
|
|
229
272
|
return copy;
|
|
230
273
|
};
|
|
231
|
-
export { DnDPlugin };
|
|
274
|
+
export { DnDPlugin, moveNode };
|
package/dist/plugins/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { AttachmentsPlugin } from "./AttachmentsPlugin/index.js";
|
|
|
2
2
|
import { BasicFormattingPlugin } from "./BasicFormattingPlugin/index.js";
|
|
3
3
|
import { BreaksPlugin } from "./BreaksPlugin/index.js";
|
|
4
4
|
import { CodeBlockPlugin } from "./CodeBlockPlugin/index.js";
|
|
5
|
+
import { CollapsibleBlocksPlugin } from "./CollapsibleBlocksPlugin/index.js";
|
|
5
6
|
import { CommandsPlugin } from "./CommandsPlugin/index.js";
|
|
6
7
|
import { DnDPlugin } from "./DnDPlugin/index.js";
|
|
7
8
|
import { EmojiPlugin } from "./EmojiPlugin/index.js";
|
|
@@ -15,4 +16,4 @@ import { NodeIdPlugin } from "./NodeIdPlugin/index.js";
|
|
|
15
16
|
import { PlaceholderPlugin } from "./PlaceholderPlugin/index.js";
|
|
16
17
|
import { ShortcutsPlugin } from "./ShortcutsPlugin/index.js";
|
|
17
18
|
import { TableOfContentsPlugin } from "./TableOfContentsPlugin/index.js";
|
|
18
|
-
export { AttachmentsPlugin, BasicFormattingPlugin, BreaksPlugin, CodeBlockPlugin, CommandsPlugin, DnDPlugin, EmojiPlugin, FloatingMenuPlugin, HeadingsPlugin, HighlightsPlugin, LinksPlugin, ListsPlugin, MenuPlugin, NodeIdPlugin, PlaceholderPlugin, ShortcutsPlugin, TableOfContentsPlugin, isSafeLinkUrl };
|
|
19
|
+
export { AttachmentsPlugin, BasicFormattingPlugin, BreaksPlugin, CodeBlockPlugin, CollapsibleBlocksPlugin, CommandsPlugin, DnDPlugin, EmojiPlugin, FloatingMenuPlugin, HeadingsPlugin, HighlightsPlugin, LinksPlugin, ListsPlugin, MenuPlugin, NodeIdPlugin, PlaceholderPlugin, ShortcutsPlugin, TableOfContentsPlugin, isSafeLinkUrl };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Editor, type Path } from 'slate';
|
|
2
|
+
import { ReactEditor, type RenderElementProps } from 'slate-react';
|
|
3
|
+
import type { IPlugin } from '../../types';
|
|
4
|
+
export declare class CollapsibleBlocksPlugin implements IPlugin {
|
|
5
|
+
private types;
|
|
6
|
+
renderBlockPriority: number;
|
|
7
|
+
constructor(types: string[]);
|
|
8
|
+
init(editor: Editor): import("slate").BaseEditor & ReactEditor & {
|
|
9
|
+
getCommands: <T extends IPlugin>(plugin: new () => T) => T["commands"];
|
|
10
|
+
};
|
|
11
|
+
renderBlock: (props: RenderElementProps) => any;
|
|
12
|
+
}
|
|
13
|
+
export declare const getCollapsibleSectionPaths: (editor: Editor, path: Path) => number[][] | null;
|
|
14
|
+
export declare const isHiddenByCollapsedSection: (editor: Editor, path: Path) => boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { CollapsibleBlocksPlugin } from './CollapsibleBlocksPlugin';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Editor } from 'slate';
|
|
1
|
+
import { Editor, Path } from 'slate';
|
|
2
2
|
import { ReactEditor, type RenderElementProps } from 'slate-react';
|
|
3
3
|
import type { IPlugin } from '../../types';
|
|
4
4
|
import type { Options } from './types';
|
|
@@ -14,3 +14,4 @@ export declare class DnDPlugin implements IPlugin {
|
|
|
14
14
|
};
|
|
15
15
|
renderBlock: (props: RenderElementProps) => any;
|
|
16
16
|
}
|
|
17
|
+
export declare const moveNode: (editor: Editor, targetNodePath: Path, nodeIds: string[], position: "top" | "bottom", sourcePath?: Path, sectionPaths?: Path[]) => void;
|
|
@@ -2,6 +2,7 @@ export { AttachmentsPlugin } from './AttachmentsPlugin';
|
|
|
2
2
|
export { BasicFormattingPlugin } from './BasicFormattingPlugin';
|
|
3
3
|
export { BreaksPlugin } from './BreaksPlugin';
|
|
4
4
|
export { CodeBlockPlugin, type CodeElement } from './CodeBlockPlugin';
|
|
5
|
+
export { CollapsibleBlocksPlugin } from './CollapsibleBlocksPlugin';
|
|
5
6
|
export { type Command, type CommandPathEntry, CommandsPlugin, type GetCommandsContext, } from './CommandsPlugin';
|
|
6
7
|
export { DnDPlugin } from './DnDPlugin';
|
|
7
8
|
export { EmojiPlugin } from './EmojiPlugin';
|
package/dist/src/types.d.ts
CHANGED
|
@@ -8,6 +8,11 @@ export interface IPlugin<TEditor extends Editor = Editor, TBlock extends CustomE
|
|
|
8
8
|
blocks?: Block<TEditor, TBlock>[];
|
|
9
9
|
leafs?: Leaf<TEditor, TLeaf>[];
|
|
10
10
|
hotkeys?: Hotkey[];
|
|
11
|
+
/**
|
|
12
|
+
* Render block wrappers from lower to higher priority. Plugins with the same
|
|
13
|
+
* priority retain the order supplied to KonaEditor.
|
|
14
|
+
*/
|
|
15
|
+
renderBlockPriority?: number;
|
|
11
16
|
renderBlock?: (props: RenderElementProps) => ReactElement | null;
|
|
12
17
|
renderLeaf?: (props: RenderLeafProps) => ReactElement | null;
|
|
13
18
|
handlers?: {
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -47,6 +47,10 @@ export const createEditable =
|
|
|
47
47
|
const [selectedNodes] = useState(() => atom<CustomElement[]>([]));
|
|
48
48
|
|
|
49
49
|
const $selectedNodes = useStore(selectedNodes);
|
|
50
|
+
const renderBlockPlugins = [...plugins].sort(
|
|
51
|
+
(left, right) =>
|
|
52
|
+
(left.renderBlockPriority || 0) - (right.renderBlockPriority || 0),
|
|
53
|
+
);
|
|
50
54
|
|
|
51
55
|
const renderElement = (props: RenderElementProps): ReactElement => {
|
|
52
56
|
let result: ReactElement | null = null;
|
|
@@ -60,7 +64,7 @@ export const createEditable =
|
|
|
60
64
|
}
|
|
61
65
|
}
|
|
62
66
|
|
|
63
|
-
for (const plugin of
|
|
67
|
+
for (const plugin of renderBlockPlugins) {
|
|
64
68
|
if (plugin.renderBlock) {
|
|
65
69
|
result = plugin.renderBlock({
|
|
66
70
|
...props,
|