@use-kona/editor 0.1.20 → 0.1.22

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.
Files changed (89) hide show
  1. package/dist/editor.js +15 -0
  2. package/dist/plugins/DnDPlugin/DnDPlugin.js +35 -26
  3. package/dist/plugins/ListsPlugin/ListsPlugin.js +15 -39
  4. package/dist/plugins/ListsPlugin/styles_module.css +1 -0
  5. package/dist/plugins/TableOfContentsPlugin/TableOfContentsPlugin.js +2 -2
  6. package/dist/{plugins → src/plugins}/CodeBlockPlugin/CodeBlockPlugin.d.ts +1 -2
  7. package/dist/{plugins → src/plugins}/HeadingsPlugin/HeadingsPlugin.d.ts +1 -1
  8. package/dist/{plugins → src/plugins}/LinksPlugin/LinksPlugin.d.ts +1 -1
  9. package/dist/{plugins → src/plugins}/ListsPlugin/ListsPlugin.d.ts +1 -3
  10. package/dist/src/plugins/MenuPlugin/MenuPlugin.d.ts +7 -0
  11. package/dist/src/types.d.ts +60 -0
  12. package/dist/types.d.ts +5 -57
  13. package/dist/utils.js +1 -1
  14. package/package.json +11 -11
  15. package/src/editor.tsx +16 -0
  16. package/src/plugins/DnDPlugin/DnDPlugin.tsx +45 -35
  17. package/src/plugins/ListsPlugin/ListsPlugin.spec.tsx +166 -6
  18. package/src/plugins/ListsPlugin/ListsPlugin.tsx +27 -63
  19. package/src/plugins/ListsPlugin/styles.module.css +1 -0
  20. package/src/plugins/TableOfContentsPlugin/TableOfContentsPlugin.tsx +2 -2
  21. package/src/types.ts +2 -0
  22. package/src/utils.ts +4 -2
  23. package/LICENSE +0 -21
  24. package/dist/plugins/CommandsPlugin/resolveCommands.spec.d.ts +0 -1
  25. package/dist/plugins/CommandsPlugin/resolveCommands.spec.js +0 -323
  26. package/dist/plugins/ListsPlugin/ListsPlugin.spec.d.ts +0 -3
  27. package/dist/plugins/ListsPlugin/ListsPlugin.spec.js +0 -47
  28. package/dist/plugins/MenuPlugin/MenuPlugin.d.ts +0 -7
  29. /package/dist/{constants.d.ts → src/constants.d.ts} +0 -0
  30. /package/dist/{core → src/core}/createEditable.d.ts +0 -0
  31. /package/dist/{core → src/core}/createEditor.d.ts +0 -0
  32. /package/dist/{core → src/core}/deserialize.d.ts +0 -0
  33. /package/dist/{core → src/core}/queries.d.ts +0 -0
  34. /package/dist/{core → src/core}/serialize.d.ts +0 -0
  35. /package/dist/{defaultValue.d.ts → src/defaultValue.d.ts} +0 -0
  36. /package/dist/{editor.d.ts → src/editor.d.ts} +0 -0
  37. /package/dist/{elements → src/elements}/BaseElement.d.ts +0 -0
  38. /package/dist/{index.d.ts → src/index.d.ts} +0 -0
  39. /package/dist/{plugins → src/plugins}/AttachmentsPlugin/AttachmentsPlugin.d.ts +0 -0
  40. /package/dist/{plugins → src/plugins}/AttachmentsPlugin/index.d.ts +0 -0
  41. /package/dist/{plugins → src/plugins}/AttachmentsPlugin/types.d.ts +0 -0
  42. /package/dist/{plugins → src/plugins}/BasicFormattingPlugin/BasicFormattingPlugin.d.ts +0 -0
  43. /package/dist/{plugins → src/plugins}/BasicFormattingPlugin/index.d.ts +0 -0
  44. /package/dist/{plugins → src/plugins}/BreaksPlugin/BreaksPlugin.d.ts +0 -0
  45. /package/dist/{plugins → src/plugins}/BreaksPlugin/index.d.ts +0 -0
  46. /package/dist/{plugins → src/plugins}/CodeBlockPlugin/CodeBlock.d.ts +0 -0
  47. /package/dist/{plugins → src/plugins}/CodeBlockPlugin/CodeBlockLine.d.ts +0 -0
  48. /package/dist/{plugins → src/plugins}/CodeBlockPlugin/index.d.ts +0 -0
  49. /package/dist/{plugins → src/plugins}/CodeBlockPlugin/types.d.ts +0 -0
  50. /package/dist/{plugins → src/plugins}/CodeBlockPlugin/utils.d.ts +0 -0
  51. /package/dist/{plugins → src/plugins}/CommandsPlugin/CommandsPlugin.d.ts +0 -0
  52. /package/dist/{plugins → src/plugins}/CommandsPlugin/Menu.d.ts +0 -0
  53. /package/dist/{plugins → src/plugins}/CommandsPlugin/actions.d.ts +0 -0
  54. /package/dist/{plugins → src/plugins}/CommandsPlugin/index.d.ts +0 -0
  55. /package/dist/{plugins → src/plugins}/CommandsPlugin/resolveCommands.d.ts +0 -0
  56. /package/dist/{plugins → src/plugins}/CommandsPlugin/types.d.ts +0 -0
  57. /package/dist/{plugins → src/plugins}/CommandsPlugin/useResolvedCommands.d.ts +0 -0
  58. /package/dist/{plugins → src/plugins}/DnDPlugin/DnDPlugin.d.ts +0 -0
  59. /package/dist/{plugins → src/plugins}/DnDPlugin/index.d.ts +0 -0
  60. /package/dist/{plugins → src/plugins}/EmojiPlugin/EmojiPlugin.d.ts +0 -0
  61. /package/dist/{plugins → src/plugins}/EmojiPlugin/Menu.d.ts +0 -0
  62. /package/dist/{plugins → src/plugins}/EmojiPlugin/index.d.ts +0 -0
  63. /package/dist/{plugins → src/plugins}/EmojiPlugin/types.d.ts +0 -0
  64. /package/dist/{plugins → src/plugins}/FloatingMenuPlugin/FloatingMenu.d.ts +0 -0
  65. /package/dist/{plugins → src/plugins}/FloatingMenuPlugin/FloatingMenuPlugin.d.ts +0 -0
  66. /package/dist/{plugins → src/plugins}/FloatingMenuPlugin/index.d.ts +0 -0
  67. /package/dist/{plugins → src/plugins}/FloatingMenuPlugin/types.d.ts +0 -0
  68. /package/dist/{plugins → src/plugins}/HeadingsPlugin/index.d.ts +0 -0
  69. /package/dist/{plugins → src/plugins}/HighlightsPlugin/HighlightsPlugin.d.ts +0 -0
  70. /package/dist/{plugins → src/plugins}/HighlightsPlugin/index.d.ts +0 -0
  71. /package/dist/{plugins → src/plugins}/LinksPlugin/Link.d.ts +0 -0
  72. /package/dist/{plugins → src/plugins}/LinksPlugin/constants.d.ts +0 -0
  73. /package/dist/{plugins → src/plugins}/LinksPlugin/index.d.ts +0 -0
  74. /package/dist/{plugins → src/plugins}/LinksPlugin/types.d.ts +0 -0
  75. /package/dist/{plugins → src/plugins}/ListsPlugin/index.d.ts +0 -0
  76. /package/dist/{plugins → src/plugins}/MenuPlugin/Menu.d.ts +0 -0
  77. /package/dist/{plugins → src/plugins}/MenuPlugin/index.d.ts +0 -0
  78. /package/dist/{plugins → src/plugins}/MenuPlugin/types.d.ts +0 -0
  79. /package/dist/{plugins → src/plugins}/NodeIdPlugin/NodeIdPlugin.d.ts +0 -0
  80. /package/dist/{plugins → src/plugins}/NodeIdPlugin/index.d.ts +0 -0
  81. /package/dist/{plugins → src/plugins}/PlaceholderPlugin/PlaceholderPlugin.d.ts +0 -0
  82. /package/dist/{plugins → src/plugins}/PlaceholderPlugin/index.d.ts +0 -0
  83. /package/dist/{plugins → src/plugins}/ShortcutsPlugin/ShortcutsPlugin.d.ts +0 -0
  84. /package/dist/{plugins → src/plugins}/ShortcutsPlugin/index.d.ts +0 -0
  85. /package/dist/{plugins → src/plugins}/TableOfContentsPlugin/TableOfContentsPlugin.d.ts +0 -0
  86. /package/dist/{plugins → src/plugins}/TableOfContentsPlugin/index.d.ts +0 -0
  87. /package/dist/{plugins → src/plugins}/index.d.ts +0 -0
  88. /package/dist/{queries → src/queries}/getMatchedGroups.d.ts +0 -0
  89. /package/dist/{utils.d.ts → src/utils.d.ts} +0 -0
package/dist/editor.js CHANGED
@@ -18,9 +18,24 @@ const KonaEditor = /*#__PURE__*/ forwardRef((props, ref)=>{
18
18
  match
19
19
  });
20
20
  };
21
+ const insertNodes = (nodes, at)=>{
22
+ if (!editor || !Array.isArray(nodes) || !nodes.length) return false;
23
+ const target = at ?? editor.selection ?? [
24
+ editor.children.length
25
+ ];
26
+ try {
27
+ Transforms.insertNodes(editor, nodes, {
28
+ at: target
29
+ });
30
+ return true;
31
+ } catch {
32
+ return false;
33
+ }
34
+ };
21
35
  return {
22
36
  serialize: serialize(plugins),
23
37
  deserialize: deserialize(plugins),
38
+ insertNodes,
24
39
  deleteNode,
25
40
  isEmpty: ()=>isEmpty(editor.children),
26
41
  focus: (mode)=>{
@@ -1,9 +1,9 @@
1
1
  import { useStore } from "@nanostores/react";
2
2
  import { map } from "nanostores";
3
- import { useEffect, useState } from "react";
3
+ import { useEffect, useRef, useState } from "react";
4
4
  import { useDrag, useDrop } from "react-dnd";
5
5
  import { NativeTypes } from "react-dnd-html5-backend";
6
- import { Editor, Path, Transforms } from "slate";
6
+ import { Editor, Transforms } from "slate";
7
7
  import { ReactEditor, useReadOnly, useSlate } from "slate-react";
8
8
  class DnDPlugin {
9
9
  options;
@@ -34,14 +34,18 @@ class DnDPlugin {
34
34
  const options = this.options;
35
35
  const isReadOnly = useReadOnly();
36
36
  const [dropPosition, setDropPosition] = useState(null);
37
+ const dropTargetRef = useRef(null);
37
38
  const customType = options.customTypes?.[props.element.type];
38
39
  const currentElement = props.element;
40
+ const selected = Array.from($store.selected.values());
39
41
  const [, drag, preview] = useDrag({
40
42
  type: customType?.type || DnDPlugin.DND_BLOCK_ELEMENT,
41
43
  item: {
42
44
  ...customType?.getData?.(props.element) || {},
43
45
  element: props.element,
44
- nodeIds: Array.from($store.selected.values())
46
+ nodeIds: currentElement?.nodeId && selected.includes(currentElement?.nodeId) ? selected : [
47
+ currentElement?.nodeId
48
+ ]
45
49
  },
46
50
  ...customType?.getDndItem?.(props.element) || {},
47
51
  canDrag: !isReadOnly
@@ -61,7 +65,7 @@ class DnDPlugin {
61
65
  };
62
66
  },
63
67
  hover (_, monitor) {
64
- const element = ReactEditor.toDOMNode(editor, props.element);
68
+ const element = dropTargetRef.current;
65
69
  if (!element) return;
66
70
  const hoverBoundingRect = element.getBoundingClientRect();
67
71
  const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
@@ -74,7 +78,7 @@ class DnDPlugin {
74
78
  const itemType = monitor.getItemType();
75
79
  const sourceTo = ReactEditor.findPath(editor, props.element);
76
80
  if (!sourceTo) return;
77
- const dropPath = getDropPath(editor, sourceTo, dropPosition);
81
+ const dropPath = getDropPath(editor, sourceTo);
78
82
  if (!dropPath) return;
79
83
  switch(itemType){
80
84
  case NativeTypes.FILE:
@@ -84,13 +88,18 @@ class DnDPlugin {
84
88
  {
85
89
  const dragItem = item;
86
90
  if (!dropPosition || !dragItem.nodeIds?.length) return;
87
- moveNode(editor, sourceTo, dragItem.nodeIds, dropPosition);
91
+ moveNode(editor, sourceTo, dragItem.nodeIds);
92
+ $store.selected.clear();
88
93
  break;
89
94
  }
90
95
  }
91
96
  setDropPosition(null);
92
97
  }
93
98
  });
99
+ const connectDropRef = (el)=>{
100
+ dropTargetRef.current = el;
101
+ return drop(el);
102
+ };
94
103
  useEffect(()=>{
95
104
  setDropPosition(null);
96
105
  }, [
@@ -101,7 +110,7 @@ class DnDPlugin {
101
110
  return options.renderBlock?.({
102
111
  props,
103
112
  dragRef: drag,
104
- dropRef: drop,
113
+ dropRef: connectDropRef,
105
114
  previewRef: preview,
106
115
  position: dropPosition,
107
116
  selected: 'string' == typeof currentElement.nodeId ? $store.selected.has(currentElement.nodeId) : false,
@@ -111,28 +120,28 @@ class DnDPlugin {
111
120
  });
112
121
  };
113
122
  }
114
- const getDropPath = (editor, targetNodePath, position)=>{
123
+ const getDropPath = (editor, targetNodePath)=>{
115
124
  const target = Editor.node(editor, targetNodePath);
116
- switch(position){
117
- case 'top':
118
- return target[1];
119
- case 'bottom':
120
- return Path.next(target[1]);
121
- }
125
+ return target[1];
122
126
  };
123
- const moveNode = (editor, targetNodePath, nodeIds, position)=>{
124
- const dropPath = getDropPath(editor, targetNodePath, position);
127
+ const moveNode = (editor, targetNodePath, nodeIds)=>{
128
+ const dropPath = getDropPath(editor, targetNodePath);
125
129
  if (!dropPath) return;
126
- Transforms.moveNodes(editor, {
127
- at: [],
128
- match: (n)=>{
129
- if (!Editor.isBlock(editor, n)) return false;
130
- const node = n;
131
- const shouldMove = 'string' == typeof node.nodeId && nodeIds.includes(node.nodeId);
132
- return shouldMove;
133
- },
134
- to: dropPath,
135
- mode: 'highest'
130
+ if (!Editor.hasPath(editor, dropPath)) return;
131
+ Editor.withoutNormalizing(editor, ()=>{
132
+ Transforms.moveNodes(editor, {
133
+ at: [],
134
+ match: (n)=>{
135
+ if (!Editor.isBlock(editor, n)) return false;
136
+ if (Editor.isEditor(n)) return false;
137
+ const node = n;
138
+ const shouldMove = 'string' == typeof node.nodeId && nodeIds.includes(node.nodeId);
139
+ return shouldMove;
140
+ },
141
+ to: dropPath,
142
+ mode: 'highest'
143
+ });
136
144
  });
145
+ Editor.normalize(editor);
137
146
  };
138
147
  export { DnDPlugin };
@@ -26,6 +26,9 @@ class ListsPlugin {
26
26
  editor.normalizeNode = (entry)=>{
27
27
  const [node, path] = entry;
28
28
  if (this.isList(editor, node)) {
29
+ if (0 === node.children.length) return void Transforms.removeNodes(editor, {
30
+ at: path
31
+ });
29
32
  for (const [child, childPath] of Array.from(Node.children(editor, path)))if (!this.isListItem(editor, child) && !this.isList(editor, child)) return void Transforms.unwrapNodes(editor, {
30
33
  at: childPath,
31
34
  match: (n)=>this.isList(editor, n),
@@ -47,25 +50,12 @@ class ListsPlugin {
47
50
  split: true
48
51
  });
49
52
  }
50
- if (this.isList(editor, node)) {
51
- if (!Path.hasPrevious(path)) return void normalizeNode(entry);
52
- const prevListItemPath = Editor.before(editor, path, {
53
- unit: 'block'
54
- });
55
- const prevList = prevListItemPath && Editor.above(editor, {
56
- at: prevListItemPath,
57
- match: (n)=>this.isList(editor, n)
53
+ if (this.isList(editor, node) && Path.hasPrevious(path)) {
54
+ const prevPath = Path.previous(path);
55
+ const prevNode = Node.get(editor, prevPath);
56
+ if (this.isList(editor, prevNode) && prevNode.type === node.type) return void Transforms.mergeNodes(editor, {
57
+ at: path
58
58
  });
59
- if (prevList) {
60
- const currentDepth = this.getListDepth(editor, path);
61
- const prevDepth = this.getListDepth(editor, prevList[1]);
62
- if (this.isList(editor, prevList[0]) && currentDepth === prevDepth && prevList[0].type === node.type) try {
63
- Transforms.mergeNodes(editor, {
64
- at: path,
65
- match: (n)=>this.isList(editor, n)
66
- });
67
- } catch (e) {}
68
- }
69
59
  }
70
60
  normalizeNode(entry);
71
61
  };
@@ -238,19 +228,19 @@ class ListsPlugin {
238
228
  mode: 'highest'
239
229
  }));
240
230
  if (!nodes.length) return;
241
- Transforms.wrapNodes(editor, {
242
- type: listType,
243
- children: []
244
- }, {
245
- mode: 'highest',
246
- match: (n)=>Element.isElement(n) && n.type === listItemType
247
- });
248
231
  Transforms.setNodes(editor, {
249
232
  type: listItemType
250
233
  }, {
251
234
  at: nodes[0][1],
252
235
  split: true
253
236
  });
237
+ Transforms.wrapNodes(editor, {
238
+ type: listType,
239
+ children: []
240
+ }, {
241
+ at: nodes[0][1],
242
+ match: (n)=>n.type === listItemType
243
+ });
254
244
  }
255
245
  });
256
246
  };
@@ -267,20 +257,6 @@ class ListsPlugin {
267
257
  }
268
258
  return 0;
269
259
  };
270
- getListDepth = (editor, path)=>{
271
- if (path) {
272
- const node = Node.get(editor, path);
273
- if (!this.isList(editor, node)) return 0;
274
- let count = 0;
275
- let parent = Editor.parent(editor, path);
276
- while(parent && this.isList(editor, parent[0])){
277
- count++;
278
- parent = Editor.parent(editor, parent[1]);
279
- }
280
- return count;
281
- }
282
- return 0;
283
- };
284
260
  getListItem = (editor, path)=>{
285
261
  if (path) {
286
262
  const node = Node.get(editor, path);
@@ -1,4 +1,5 @@
1
1
  .list-YEmg4D {
2
+ margin: 0;
2
3
  padding: 0;
3
4
 
4
5
  & & {
@@ -2,7 +2,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useStore } from "@nanostores/react";
3
3
  import { map } from "nanostores";
4
4
  import { useEffect } from "react";
5
- import { Editor, Operation } from "slate";
5
+ import { Editor, Operation, Text } from "slate";
6
6
  import { ReactEditor } from "slate-react";
7
7
  import styles_module from "./styles.module.js";
8
8
  class TableOfContentsPlugin {
@@ -56,7 +56,7 @@ class TableOfContentsPlugin {
56
56
  return /*#__PURE__*/ jsx("div", {
57
57
  className: styles_module[`h${level}`],
58
58
  onMouseDown: handleMouseDown,
59
- children: node.children[0].text
59
+ children: Text.isText(node.children[0]) ? node.children[0].text : ''
60
60
  }, path.join('-'));
61
61
  });
62
62
  return /*#__PURE__*/ jsxs("div", {
@@ -4,7 +4,6 @@ import { type RenderElementProps, type RenderLeafProps } from 'slate-react';
4
4
  import type { IPlugin } from '../../types';
5
5
  import type { CodeElement } from './types';
6
6
  import 'prismjs/themes/prism.css';
7
- import type { CustomElement } from '../../../types';
8
7
  import 'prismjs/components/prism-javascript';
9
8
  import 'prismjs/components/prism-typescript';
10
9
  import 'prismjs/components/prism-json';
@@ -47,7 +46,7 @@ export declare class CodeBlockPlugin implements IPlugin {
47
46
  blocks: ({
48
47
  type: string;
49
48
  render: (props: RenderElementProps, editor: Editor) => import("react/jsx-runtime").JSX.Element;
50
- deserialize: (el: HTMLElement, children: any) => CustomElement | undefined;
49
+ deserialize: (el: HTMLElement, children: any) => import("../../../types").CustomElement | undefined;
51
50
  } | {
52
51
  type: string;
53
52
  render: (props: RenderElementProps) => import("react/jsx-runtime").JSX.Element;
@@ -8,7 +8,7 @@ export declare class HeadingsPlugin implements IPlugin {
8
8
  blocks: {
9
9
  type: string;
10
10
  render: (props: RenderElementProps) => import("react/jsx-runtime").JSX.Element;
11
- deserialize: (element: HTMLElement, children: any) => import("../..").CustomElement | undefined;
11
+ deserialize: (element: HTMLElement, children: any) => import("../../../types").CustomElement | undefined;
12
12
  serialize: (node: Descendant, children: any) => string | undefined;
13
13
  }[];
14
14
  static isHeading1Active(editor: Editor): boolean;
@@ -14,7 +14,7 @@ export declare class LinksPlugin implements IPlugin {
14
14
  type: string;
15
15
  render: (props: RenderElementProps) => import("react/jsx-runtime").JSX.Element;
16
16
  serialize: (element: any, children: any) => string | undefined;
17
- deserialize: (element: HTMLElement, children: any) => import("../..").CustomElement | undefined;
17
+ deserialize: (element: HTMLElement, children: any) => import("../../../types").CustomElement | undefined;
18
18
  }[];
19
19
  static addLink: (editor: Editor, url: string) => void;
20
20
  static removeLink: (editor: Editor) => void;
@@ -1,7 +1,6 @@
1
1
  import type { KeyboardEvent } from 'react';
2
2
  import { Editor } from 'slate';
3
3
  import type { RenderElementProps } from 'slate-react';
4
- import type { CustomElement } from '../../../types';
5
4
  import type { IPlugin } from '../../types';
6
5
  type Options = {
7
6
  listTypes?: string[];
@@ -20,7 +19,7 @@ export declare class ListsPlugin implements IPlugin {
20
19
  type: string;
21
20
  render: (props: RenderElementProps) => import("react/jsx-runtime").JSX.Element;
22
21
  serialize: (element: any, children: any) => string | undefined;
23
- deserialize: (element: HTMLElement, children: any) => CustomElement | undefined;
22
+ deserialize: (element: HTMLElement, children: any) => import("../../../types").CustomElement | undefined;
24
23
  }[];
25
24
  handlers: {
26
25
  onKeyDown: (event: KeyboardEvent, editor: Editor) => true | undefined;
@@ -36,7 +35,6 @@ export declare class ListsPlugin implements IPlugin {
36
35
  private isListItem;
37
36
  static toggleList: (editor: Editor, listType: string, listItemType?: string) => void;
38
37
  private getListItemDepth;
39
- private getListDepth;
40
38
  private getListItem;
41
39
  }
42
40
  export {};
@@ -0,0 +1,7 @@
1
+ import type { IPlugin, UiParams } from '../../types';
2
+ import type { Options } from './types';
3
+ export declare class MenuPlugin implements IPlugin {
4
+ private options;
5
+ constructor(options: Options);
6
+ ui: (params: UiParams) => string | number | bigint | boolean | import("react/jsx-runtime").JSX.Element | Iterable<import("react").ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode> | null | undefined> | null | undefined;
7
+ }
@@ -0,0 +1,60 @@
1
+ import type { KeyboardEvent, ReactElement, ReactNode } from 'react';
2
+ import type { DecoratedRange, Descendant, Editor, Location, NodeEntry, NodeMatch } from 'slate';
3
+ import type { RenderElementProps, RenderLeafProps } from 'slate-react';
4
+ import type { CustomElement, CustomText } from '../types';
5
+ export interface IPlugin<TEditor extends Editor = Editor, TBlock extends CustomElement = CustomElement, TLeaf extends CustomText = CustomText> {
6
+ init?: (editor: TEditor) => TEditor;
7
+ blocks?: Block<TEditor, TBlock>[];
8
+ leafs?: Leaf<TEditor, TLeaf>[];
9
+ hotkeys?: Hotkey[];
10
+ renderBlock?: (props: RenderElementProps) => ReactElement | null;
11
+ renderLeaf?: (props: RenderLeafProps) => ReactElement | null;
12
+ handlers?: {
13
+ onDrop?: (event: DragEvent, editor: Editor) => void;
14
+ onDragOver?: (event: DragEvent, editor: Editor) => void;
15
+ onKeyDown?: (event: KeyboardEvent, editor: Editor) => void;
16
+ onPaste?: (event: ClipboardEvent, editor: Editor) => void;
17
+ };
18
+ decorate?: (entry: NodeEntry) => DecoratedRange[];
19
+ commands?: {
20
+ [key: string]: (editor: TEditor, ...args: any[]) => void;
21
+ };
22
+ ui?: (params: UiParams) => ReactNode;
23
+ }
24
+ export type Block<TEditor extends Editor = Editor, TBlock extends CustomElement = CustomElement> = {
25
+ type: string;
26
+ render: (props: RenderElementProps & {
27
+ element: CustomElement & TBlock;
28
+ }, editor: TEditor) => ReactElement | null;
29
+ serialize?: Serialize;
30
+ deserialize?: Deserialize;
31
+ isVoid?: boolean;
32
+ isInline?: boolean;
33
+ onBeforeDelete?: (blocks: TBlock[]) => Promise<boolean>;
34
+ onDelete?: (blocks: TBlock[]) => void;
35
+ };
36
+ export type Leaf<T extends Editor, TLeaf extends CustomText = CustomText> = {
37
+ render: (props: RenderLeafProps & {
38
+ leaf: CustomText & TLeaf;
39
+ }, editor: T) => ReactElement | null;
40
+ isVoid?: boolean;
41
+ serialize?: Serialize;
42
+ deserialize?: Deserialize;
43
+ };
44
+ type Hotkey = readonly [string, (event: KeyboardEvent, editor: Editor) => void];
45
+ export type UiParams = {
46
+ editor: Editor;
47
+ readOnly: boolean;
48
+ children: ReactNode;
49
+ };
50
+ export type EditorRef = {
51
+ serialize: (node: CustomElement | CustomText) => string;
52
+ deserialize: (element: HTMLElement) => (Descendant | string)[] | string | Descendant | null;
53
+ insertNodes: (nodes: Descendant[], at?: Location) => boolean;
54
+ isEmpty: () => boolean;
55
+ deleteNode: (match: NodeMatch<CustomElement>) => void;
56
+ focus: (mode?: 'end') => void;
57
+ };
58
+ export type Serialize = (node: CustomElement | CustomText, children?: string) => string | undefined;
59
+ export type Deserialize = (element: HTMLElement, children?: (string | Descendant)[]) => CustomElement | CustomText[] | undefined;
60
+ export {};
package/dist/types.d.ts CHANGED
@@ -1,59 +1,7 @@
1
- import type { KeyboardEvent, ReactElement, ReactNode } from 'react';
2
- import type { DecoratedRange, Descendant, Editor, NodeEntry, NodeMatch } from 'slate';
3
- import type { RenderElementProps, RenderLeafProps } from 'slate-react';
4
- import type { CustomElement, CustomText } from '../types';
5
- export interface IPlugin<TEditor extends Editor = Editor, TBlock extends CustomElement = CustomElement, TLeaf extends CustomText = CustomText> {
6
- init?: (editor: TEditor) => TEditor;
7
- blocks?: Block<TEditor, TBlock>[];
8
- leafs?: Leaf<TEditor, TLeaf>[];
9
- hotkeys?: Hotkey[];
10
- renderBlock?: (props: RenderElementProps) => ReactElement | null;
11
- renderLeaf?: (props: RenderLeafProps) => ReactElement | null;
12
- handlers?: {
13
- onDrop?: (event: DragEvent, editor: Editor) => void;
14
- onDragOver?: (event: DragEvent, editor: Editor) => void;
15
- onKeyDown?: (event: KeyboardEvent, editor: Editor) => void;
16
- onPaste?: (event: ClipboardEvent, editor: Editor) => void;
17
- };
18
- decorate?: (entry: NodeEntry) => DecoratedRange[];
19
- commands?: {
20
- [key: string]: (editor: TEditor, ...args: any[]) => void;
21
- };
22
- ui?: (params: UiParams) => ReactNode;
23
- }
24
- export type Block<TEditor extends Editor = Editor, TBlock extends CustomElement = CustomElement> = {
25
- type: string;
26
- render: (props: RenderElementProps & {
27
- element: CustomElement & TBlock;
28
- }, editor: TEditor) => ReactElement | null;
29
- serialize?: Serialize;
30
- deserialize?: Deserialize;
31
- isVoid?: boolean;
32
- isInline?: boolean;
33
- onBeforeDelete?: (blocks: TBlock[]) => Promise<boolean>;
34
- onDelete?: (blocks: TBlock[]) => void;
35
- };
36
- export type Leaf<T extends Editor, TLeaf extends CustomText = CustomText> = {
37
- render: (props: RenderLeafProps & {
38
- leaf: CustomText & TLeaf;
39
- }, editor: T) => ReactElement | null;
40
- isVoid?: boolean;
41
- serialize?: Serialize;
42
- deserialize?: Deserialize;
1
+ export type CustomText = {
2
+ text: string;
43
3
  };
44
- type Hotkey = readonly [string, (event: KeyboardEvent, editor: Editor) => void];
45
- export type UiParams = {
46
- editor: Editor;
47
- readOnly: boolean;
48
- children: ReactNode;
49
- };
50
- export type EditorRef = {
51
- serialize: (node: CustomElement | CustomText) => string;
52
- deserialize: (element: HTMLElement) => (Descendant | string)[] | string | Descendant | null;
53
- isEmpty: () => boolean;
54
- deleteNode: (match: NodeMatch<CustomElement>) => void;
55
- focus: (mode?: 'end') => void;
4
+ export type CustomElement = {
5
+ type: string;
6
+ children: (CustomElement | CustomText)[];
56
7
  };
57
- export type Serialize = (node: CustomElement | CustomText, children?: string) => string | undefined;
58
- export type Deserialize = (element: HTMLElement, children?: (string | Descendant)[]) => CustomElement | CustomText[] | undefined;
59
- export {};
package/dist/utils.js CHANGED
@@ -2,7 +2,7 @@ import { Text } from "slate";
2
2
  const isEmpty = (children)=>{
3
3
  const [first] = children || [];
4
4
  if (Text.isText(first)) return '' === first.text.trim();
5
- if (children && children.length <= 1 && Text.isText(first?.children?.[0])) return first.children.every((child)=>child.text?.trim() === '');
5
+ if (children && children.length <= 1 && first && 'children' in first) return first.children.every((child)=>Text.isText(child) && child.text?.trim() === '');
6
6
  return false;
7
7
  };
8
8
  export { isEmpty };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@use-kona/editor",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/index.ts"
@@ -11,6 +11,15 @@
11
11
  "dist",
12
12
  "src"
13
13
  ],
14
+ "scripts": {
15
+ "build": "rslib build && find dist -type f \\( -name '*.spec.*' -o -name '*.test.*' \\) -delete || true",
16
+ "build:storybook": "storybook build",
17
+ "check": "biome check --write",
18
+ "dev": "rslib build --watch",
19
+ "format": "biome format --write",
20
+ "storybook": "NODE_OPTIONS=\"--inspect=9229\" storybook dev",
21
+ "test": "vitest run"
22
+ },
14
23
  "devDependencies": {
15
24
  "@rsbuild/core": "~1.4.0",
16
25
  "@rsbuild/plugin-react": "^1.3.2",
@@ -62,14 +71,5 @@
62
71
  "is-url": "^1.2.4",
63
72
  "nanostores": "^1.0.1",
64
73
  "prismjs": "^1.30.0"
65
- },
66
- "scripts": {
67
- "build": "rslib build",
68
- "build:storybook": "storybook build",
69
- "check": "biome check --write",
70
- "dev": "rslib build --watch",
71
- "format": "biome format --write",
72
- "storybook": "storybook dev",
73
- "test": "vitest run"
74
74
  }
75
- }
75
+ }
package/src/editor.tsx CHANGED
@@ -29,9 +29,25 @@ export const KonaEditor = forwardRef<EditorRef, KonaEditorProps>(
29
29
  Transforms.removeNodes(editor, { at: [], match });
30
30
  };
31
31
 
32
+ const insertNodes: EditorRef['insertNodes'] = (nodes, at) => {
33
+ if (!editor || !Array.isArray(nodes) || !nodes.length) {
34
+ return false;
35
+ }
36
+
37
+ const target = at ?? editor.selection ?? [editor.children.length];
38
+
39
+ try {
40
+ Transforms.insertNodes(editor, nodes, { at: target });
41
+ return true;
42
+ } catch {
43
+ return false;
44
+ }
45
+ };
46
+
32
47
  return {
33
48
  serialize: serialize(plugins),
34
49
  deserialize: deserialize(plugins),
50
+ insertNodes,
35
51
  deleteNode,
36
52
  isEmpty: () => {
37
53
  return isEmpty(editor.children);