@use-kona/editor 0.1.5 → 0.1.7

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.
@@ -1,4 +1,4 @@
1
- import type { Editor } from 'slate';
1
+ import { Editor } from 'slate';
2
2
  import type { IPlugin } from '../types';
3
3
  type Props = {
4
4
  readOnly?: boolean;
@@ -1,8 +1,10 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import is_hotkey from "is-hotkey";
3
3
  import react, { isValidElement, useCallback } from "react";
4
+ import { Transforms } from "slate";
4
5
  import { Editable, useReadOnly } from "slate-react";
5
6
  import { BaseElement } from "../elements/BaseElement.js";
7
+ import { deserialize } from "./deserialize.js";
6
8
  import styles_module from "./styles.module.js";
7
9
  const SUPPORTED_HANDLERS = [
8
10
  'onDrop',
@@ -37,6 +39,7 @@ const createEditable = (editor, plugins)=>({ readOnly })=>{
37
39
  return result;
38
40
  };
39
41
  const handleHotkey = (event)=>{
42
+ if ('Tab' === event.key) event.preventDefault();
40
43
  for (const plugin of plugins)if (plugin.hotkeys) {
41
44
  for (const hotkey of plugin.hotkeys)if (is_hotkey(hotkey[0], event) && hotkey[1]) {
42
45
  event.preventDefault();
@@ -44,6 +47,14 @@ const createEditable = (editor, plugins)=>({ readOnly })=>{
44
47
  }
45
48
  }
46
49
  };
50
+ const handlePaste = (event)=>{
51
+ const html = event.clipboardData?.getData('text/html');
52
+ if (!html) return;
53
+ const parsed = new DOMParser().parseFromString(html, 'text/html');
54
+ const value = deserialize(plugins)(parsed.body);
55
+ event.preventDefault();
56
+ Transforms.insertNodes(editor, value);
57
+ };
47
58
  const decorate = (entry)=>{
48
59
  const decorators = plugins.filter((plugin)=>!!plugin.decorate);
49
60
  return decorators.reduce((decorations, plugin)=>{
@@ -66,7 +77,8 @@ const createEditable = (editor, plugins)=>({ readOnly })=>{
66
77
  });
67
78
  return handlers;
68
79
  }, {
69
- onKeyDown: handleHotkey
80
+ onKeyDown: handleHotkey,
81
+ onPaste: handlePaste
70
82
  });
71
83
  const Ui = useCallback(({ children })=>{
72
84
  const ui = plugins.filter((p)=>Boolean(p.ui));
@@ -74,7 +74,7 @@ const createEditor_createEditor = (plugins)=>()=>{
74
74
  const result = matchedBlock.onBeforeDelete ? await matchedBlock?.onBeforeDelete?.([
75
75
  node
76
76
  ]) : true;
77
- if (result) {
77
+ if (result && Editor.isVoid(editorWithPlugins, node)) {
78
78
  Transforms.removeNodes(editorWithPlugins, {
79
79
  at: path
80
80
  });
@@ -92,19 +92,19 @@ const createEditor_createEditor = (plugins)=>()=>{
92
92
  const { selection } = editorWithPlugins;
93
93
  if (!selection) return;
94
94
  if (Range.isCollapsed(selection)) {
95
- const firstEntry = Editor.above(editorWithPlugins, {
95
+ const currentEntry = Editor.above(editorWithPlugins, {
96
96
  at: selection.anchor,
97
97
  match: (n)=>Editor.isBlock(editorWithPlugins, n),
98
98
  mode: 'lowest'
99
99
  });
100
- const secondEntry = Editor.above(editorWithPlugins, {
100
+ const previousEntry = Editor.above(editorWithPlugins, {
101
101
  at: Editor.before(editorWithPlugins, selection.anchor),
102
102
  match: (n)=>Editor.isBlock(editorWithPlugins, n),
103
103
  mode: 'lowest'
104
104
  });
105
- if (!firstEntry || !secondEntry) return;
106
- const [_, currentPath] = firstEntry;
107
- const [node, path] = secondEntry;
105
+ if (!currentEntry || !previousEntry) return;
106
+ const [_, currentPath] = currentEntry;
107
+ const [node, path] = previousEntry;
108
108
  if (node && Editor.isStart(editorWithPlugins, selection.anchor, currentPath)) {
109
109
  const matchedBlock = plugins.reduce((block, plugin)=>{
110
110
  const match = plugin.blocks?.find((b)=>b.type === node.type);
@@ -114,15 +114,15 @@ const createEditor_createEditor = (plugins)=>()=>{
114
114
  const result = matchedBlock.onBeforeDelete ? await matchedBlock.onBeforeDelete([
115
115
  node
116
116
  ]) : true;
117
- if (result) {
117
+ if (result && Editor.isVoid(editorWithPlugins, node)) {
118
118
  Transforms.removeNodes(editorWithPlugins, {
119
119
  at: path
120
120
  });
121
121
  matchedBlock.onDelete?.([
122
122
  node
123
123
  ]);
124
+ return;
124
125
  }
125
- return;
126
126
  }
127
127
  }
128
128
  }
@@ -1,2 +1,5 @@
1
- import { Editor, NodeEntry, Node } from 'slate';
2
- export declare const getPrev: (editor: Editor, node: NodeEntry) => Node | null;
1
+ import { Editor, type NodeEntry } from 'slate';
2
+ /**
3
+ * @deprecated
4
+ */
5
+ export declare const getPrev: (editor: Editor, node: NodeEntry) => NodeEntry<import("slate").Ancestor> | null | undefined;
@@ -1,8 +1,11 @@
1
- import { Node, Path } from "slate";
1
+ import { Editor, Path } from "slate";
2
2
  const getPrev = (editor, node)=>{
3
3
  try {
4
4
  const [, path] = node;
5
- return Node.get(editor, Path.previous(path));
5
+ return Editor.above(editor, {
6
+ at: Path.previous(path),
7
+ mode: 'lowest'
8
+ });
6
9
  } catch (e) {
7
10
  return null;
8
11
  }
@@ -4,6 +4,7 @@ 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';
7
8
  import 'prismjs/components/prism-javascript';
8
9
  import 'prismjs/components/prism-typescript';
9
10
  import 'prismjs/components/prism-json';
@@ -43,10 +44,15 @@ export declare class CodeBlockPlugin implements IPlugin {
43
44
  } & {
44
45
  token: boolean;
45
46
  })[];
46
- blocks: {
47
+ blocks: ({
47
48
  type: string;
48
49
  render: (props: RenderElementProps, editor: Editor) => import("react/jsx-runtime").JSX.Element;
49
- }[];
50
+ deserialize: (el: HTMLElement, children: any) => CustomElement | undefined;
51
+ } | {
52
+ type: string;
53
+ render: (props: RenderElementProps) => import("react/jsx-runtime").JSX.Element;
54
+ deserialize?: undefined;
55
+ })[];
50
56
  leafs: {
51
57
  render: (props: RenderLeafProps) => import("react/jsx-runtime").JSX.Element;
52
58
  }[];
@@ -26,6 +26,7 @@ import "prismjs/components/prism-bash";
26
26
  import "prismjs/components/prism-sql";
27
27
  import "prismjs/components/prism-yaml";
28
28
  import "prismjs/components/prism-markdown";
29
+ import { jsx as external_slate_hyperscript_jsx } from "slate-hyperscript";
29
30
  class CodeBlockPlugin {
30
31
  options;
31
32
  constructor(options){
@@ -155,6 +156,17 @@ class CodeBlockPlugin {
155
156
  Content
156
157
  })
157
158
  });
159
+ },
160
+ deserialize: (el, children)=>{
161
+ const { nodeName } = el;
162
+ if ('PRE' === nodeName) {
163
+ const lines = children.join('').split('\n');
164
+ return external_slate_hyperscript_jsx('element', {
165
+ type: CodeBlockPlugin.CODE_ELEMENT
166
+ }, lines.map((line)=>external_slate_hyperscript_jsx('element', {
167
+ type: CodeBlockPlugin.CODE_LINE_ELEMENT
168
+ }, line)));
169
+ }
158
170
  }
159
171
  },
160
172
  {
@@ -30,6 +30,7 @@ export declare class ListsPlugin implements IPlugin {
30
30
  private isListItem;
31
31
  static toggleList: (editor: Editor, listType: string, listItemType?: string) => void;
32
32
  private getListItemDepth;
33
+ private getListDepth;
33
34
  private getListItem;
34
35
  }
35
36
  export {};
@@ -1,7 +1,6 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { Editor, Element, Node, Transforms } from "slate";
3
3
  import { jsx as external_slate_hyperscript_jsx } from "slate-hyperscript";
4
- import { getPrev } from "../../core/queries.js";
5
4
  import styles_module from "./styles.module.js";
6
5
  class ListsPlugin {
7
6
  options;
@@ -48,6 +47,24 @@ class ListsPlugin {
48
47
  split: true
49
48
  });
50
49
  }
50
+ if (this.isList(editor, node)) {
51
+ const prevListItemPath = Editor.before(editor, path, {
52
+ unit: 'block'
53
+ });
54
+ const prevList = Editor.above(editor, {
55
+ at: prevListItemPath,
56
+ match: (n)=>this.isList(editor, n)
57
+ });
58
+ if (prevList) {
59
+ const currentDepth = this.getListDepth(editor, path);
60
+ const prevDepth = this.getListDepth(editor, prevList[1]);
61
+ if (this.isList(editor, prevList[0]) && currentDepth === prevDepth) try {
62
+ Transforms.mergeNodes(editor, {
63
+ match: (n)=>this.isList(editor, n)
64
+ });
65
+ } catch (e) {}
66
+ }
67
+ }
51
68
  normalizeNode(entry);
52
69
  };
53
70
  editor.insertBreak = ()=>{
@@ -152,9 +169,15 @@ class ListsPlugin {
152
169
  else if (!event.shiftKey) {
153
170
  const currentListItem = this.getListItem(editor);
154
171
  if (!currentListItem) return;
155
- const prevListItem = getPrev(editor, currentListItem);
156
- if (!prevListItem) return;
157
- if (currentListItem && prevListItem) {
172
+ const prevListItemPath = Editor.before(editor, currentListItem[1], {
173
+ unit: 'block'
174
+ });
175
+ if (currentListItem && prevListItemPath) {
176
+ const prevListItem = Editor.above(editor, {
177
+ at: prevListItemPath,
178
+ match: (n)=>this.isListItem(editor, n)
179
+ });
180
+ if (!prevListItem) return;
158
181
  const currentDepth = this.getListItemDepth(editor, currentListItem[1]);
159
182
  const prevDepth = this.getListItemDepth(editor, prevListItem[1]);
160
183
  if (prevListItem && prevDepth >= currentDepth) Transforms.wrapNodes(editor, {
@@ -220,9 +243,23 @@ class ListsPlugin {
220
243
  if (item) {
221
244
  let count = 0;
222
245
  let parent = Editor.parent(editor, item[1]);
223
- while(null !== parent && this.isList(editor, parent[0])){
246
+ do {
224
247
  parent = Editor.parent(editor, parent[1]);
225
248
  count++;
249
+ }while (null !== parent && this.isList(editor, parent[0]));
250
+ return count;
251
+ }
252
+ return 0;
253
+ };
254
+ getListDepth = (editor, path)=>{
255
+ if (path) {
256
+ const node = Node.get(editor, path);
257
+ if (!this.isList(editor, node)) return 0;
258
+ let count = 0;
259
+ let parent = Editor.parent(editor, path);
260
+ while(parent && this.isList(editor, parent[0])){
261
+ count++;
262
+ parent = Editor.parent(editor, parent[1]);
226
263
  }
227
264
  return count;
228
265
  }
@@ -25,12 +25,12 @@ class ShortcutsPlugin {
25
25
  const currentPoint = Range.start(selection);
26
26
  const [currentNode] = Editor.node(editor, currentPoint.path);
27
27
  const textBeforeCursor = Node.string(currentNode).slice(0, currentPoint.offset);
28
- matchBefore = new RegExp(shortcut.before.source, 'g').exec(textBeforeCursor);
28
+ matchBefore = new RegExp(`${shortcut.before.source}`, 'g').exec(textBeforeCursor);
29
29
  }
30
30
  if (shortcut.after && matchBefore) {
31
31
  const currentPoint = Range.start(selection);
32
32
  const [currentNode] = Editor.node(editor, currentPoint.path);
33
- const textBetweenMatches = Node.string(currentNode).slice(matchBefore.index, currentPoint.offset);
33
+ const textBetweenMatches = Node.string(currentNode).slice(matchBefore.index + matchBefore[0].length, currentPoint.offset);
34
34
  matchAfter = new RegExp(shortcut.after.source + '$', 'g').exec(textBetweenMatches);
35
35
  }
36
36
  if (matchBefore && !shortcut.after) return void shortcut.change(editor, {
@@ -42,8 +42,8 @@ class ShortcutsPlugin {
42
42
  if (matchBefore && matchAfter && shortcut.before && shortcut.after) return void shortcut.change(editor, {
43
43
  before: matchBefore,
44
44
  after: matchAfter,
45
- text: matchAfter.input,
46
- cleanText: matchBefore.input.slice(matchBefore.index + matchBefore[0].length, matchBefore.index + matchBefore[0].length + matchAfter.index - matchAfter[0].length)
45
+ text: matchBefore.input.slice(matchBefore.index),
46
+ cleanText: matchBefore.input.slice(matchBefore.index + matchBefore[0].length, matchBefore.input.length - matchAfter[0].length)
47
47
  });
48
48
  }
49
49
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@use-kona/editor",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/index.ts"
@@ -1,5 +1,6 @@
1
1
  import isHotkey from 'is-hotkey';
2
2
  import React, {
3
+ type ClipboardEvent,
3
4
  isValidElement,
4
5
  type JSX,
5
6
  type KeyboardEvent,
@@ -7,7 +8,13 @@ import React, {
7
8
  type ReactNode,
8
9
  useCallback,
9
10
  } from 'react';
10
- import type { DecoratedRange, Editor, NodeEntry } from 'slate';
11
+ import {
12
+ DecoratedRange,
13
+ Descendant,
14
+ Editor,
15
+ NodeEntry,
16
+ Transforms,
17
+ } from 'slate';
11
18
  import {
12
19
  Editable,
13
20
  type RenderElementProps,
@@ -16,6 +23,7 @@ import {
16
23
  } from 'slate-react';
17
24
  import { BaseElement } from '../elements/BaseElement';
18
25
  import type { IPlugin } from '../types';
26
+ import { deserialize } from './deserialize';
19
27
  import styles from './styles.module.css';
20
28
 
21
29
  const SUPPORTED_HANDLERS = ['onDrop', 'onKeyDown', 'onPaste'];
@@ -72,6 +80,10 @@ export const createEditable =
72
80
  };
73
81
 
74
82
  const handleHotkey = (event: KeyboardEvent) => {
83
+ if (event.key === 'Tab') {
84
+ event.preventDefault();
85
+ }
86
+
75
87
  for (const plugin of plugins) {
76
88
  if (plugin.hotkeys) {
77
89
  for (const hotkey of plugin.hotkeys) {
@@ -84,6 +96,17 @@ export const createEditable =
84
96
  }
85
97
  };
86
98
 
99
+ const handlePaste = (event: ClipboardEvent<HTMLDivElement>) => {
100
+ const html = event.clipboardData?.getData('text/html');
101
+ if (!html) return;
102
+
103
+ const parsed = new DOMParser().parseFromString(html, 'text/html');
104
+ const value = deserialize(plugins)(parsed.body);
105
+ event.preventDefault();
106
+
107
+ Transforms.insertNodes(editor, value as Descendant);
108
+ };
109
+
87
110
  const decorate = (entry: NodeEntry) => {
88
111
  const decorators: IPlugin[] = plugins.filter(
89
112
  (plugin) => !!plugin.decorate,
@@ -117,6 +140,7 @@ export const createEditable =
117
140
  },
118
141
  {
119
142
  onKeyDown: handleHotkey,
143
+ onPaste: handlePaste,
120
144
  },
121
145
  );
122
146
 
@@ -119,7 +119,7 @@ export const createEditor = (plugins: IPlugin[]) => () => {
119
119
  const result = matchedBlock.onBeforeDelete
120
120
  ? await matchedBlock?.onBeforeDelete?.([node])
121
121
  : true;
122
- if (result) {
122
+ if (result && Editor.isVoid(editorWithPlugins, node)) {
123
123
  Transforms.removeNodes(editorWithPlugins, { at: path });
124
124
  matchedBlock.onDelete?.([node]);
125
125
  }
@@ -139,23 +139,23 @@ export const createEditor = (plugins: IPlugin[]) => () => {
139
139
  }
140
140
 
141
141
  if (Range.isCollapsed(selection)) {
142
- const firstEntry = Editor.above<CustomElement>(editorWithPlugins, {
142
+ const currentEntry = Editor.above<CustomElement>(editorWithPlugins, {
143
143
  at: selection.anchor,
144
144
  match: (n) => Editor.isBlock(editorWithPlugins, n as CustomElement),
145
145
  mode: 'lowest',
146
146
  });
147
- const secondEntry = Editor.above<CustomElement>(editorWithPlugins, {
147
+ const previousEntry = Editor.above<CustomElement>(editorWithPlugins, {
148
148
  at: Editor.before(editorWithPlugins, selection.anchor),
149
149
  match: (n) => Editor.isBlock(editorWithPlugins, n as CustomElement),
150
150
  mode: 'lowest',
151
151
  });
152
152
 
153
- if (!firstEntry || !secondEntry) {
153
+ if (!currentEntry || !previousEntry) {
154
154
  return;
155
155
  }
156
156
 
157
- const [_, currentPath] = firstEntry;
158
- const [node, path] = secondEntry;
157
+ const [_, currentPath] = currentEntry;
158
+ const [node, path] = previousEntry;
159
159
 
160
160
  if (
161
161
  node &&
@@ -170,11 +170,11 @@ export const createEditor = (plugins: IPlugin[]) => () => {
170
170
  const result = matchedBlock.onBeforeDelete
171
171
  ? await matchedBlock.onBeforeDelete([node])
172
172
  : true;
173
- if (result) {
173
+ if (result && Editor.isVoid(editorWithPlugins, node)) {
174
174
  Transforms.removeNodes(editorWithPlugins, { at: path });
175
175
  matchedBlock.onDelete?.([node]);
176
+ return;
176
177
  }
177
- return;
178
178
  }
179
179
  }
180
180
  }
@@ -1,6 +1,5 @@
1
- import { type Descendant, Element, type Node } from 'slate';
1
+ import { type Descendant, Element } from 'slate';
2
2
  import { jsx } from 'slate-hyperscript';
3
- import { CustomElement, CustomText } from '../../types';
4
3
  import type { IPlugin } from '../types';
5
4
 
6
5
  export const deserialize =
@@ -36,7 +35,8 @@ export const deserialize =
36
35
  return jsx('element', { type: 'paragraph' }, children);
37
36
  }
38
37
 
39
- let result: CustomElement | CustomText[] | null = null;
38
+ let result: (Descendant | string)[] | string | Descendant | null = null;
39
+
40
40
  for (const plugin of plugins) {
41
41
  if (plugin.blocks?.some((b) => b.deserialize)) {
42
42
  plugin.blocks.forEach((e) => {
@@ -61,7 +61,9 @@ export const deserialize =
61
61
  | string
62
62
  | Descendant
63
63
  )[];
64
+
64
65
  const newResult = e.deserialize(element, childrenAsDescendants);
66
+
65
67
  if (newResult) {
66
68
  result = newResult;
67
69
  }
@@ -1,9 +1,15 @@
1
- import { Editor, NodeEntry, Node, Path } from 'slate';
1
+ import { Editor, type NodeEntry, Path } from 'slate';
2
2
 
3
+ /**
4
+ * @deprecated
5
+ */
3
6
  export const getPrev = (editor: Editor, node: NodeEntry) => {
4
7
  try {
5
8
  const [, path] = node;
6
- return Node.get(editor, Path.previous(path));
9
+ return Editor.above(editor, {
10
+ at: Path.previous(path),
11
+ mode: 'lowest',
12
+ });
7
13
  } catch (e) {
8
14
  return null;
9
15
  }
@@ -42,6 +42,7 @@ import 'prismjs/components/prism-bash';
42
42
  import 'prismjs/components/prism-sql';
43
43
  import 'prismjs/components/prism-yaml';
44
44
  import 'prismjs/components/prism-markdown';
45
+ import { jsx } from 'slate-hyperscript';
45
46
 
46
47
  type Options = {
47
48
  renderCodeBlock: (
@@ -211,6 +212,21 @@ export class CodeBlockPlugin implements IPlugin {
211
212
  </>
212
213
  );
213
214
  },
215
+ deserialize: (el: HTMLElement, children) => {
216
+ const { nodeName } = el;
217
+
218
+ if (nodeName === 'PRE') {
219
+ const lines = children.join('').split('\n');
220
+
221
+ return jsx(
222
+ 'element',
223
+ { type: CodeBlockPlugin.CODE_ELEMENT },
224
+ lines.map((line) =>
225
+ jsx('element', { type: CodeBlockPlugin.CODE_LINE_ELEMENT }, line),
226
+ ),
227
+ );
228
+ }
229
+ },
214
230
  },
215
231
  {
216
232
  type: CodeBlockPlugin.CODE_LINE_ELEMENT,
@@ -2,7 +2,6 @@ import isUrl from 'is-url';
2
2
  import { Editor, Element, Range, Transforms } from 'slate';
3
3
  import { jsx } from 'slate-hyperscript';
4
4
  import type { RenderElementProps } from 'slate-react';
5
- import { deserialize } from '../../core/deserialize';
6
5
  import type { IPlugin } from '../../types';
7
6
  import { LINK_ELEMENT } from './constants';
8
7
  import { Link } from './Link';
@@ -88,6 +88,33 @@ export class ListsPlugin implements IPlugin {
88
88
  }
89
89
  }
90
90
 
91
+ if (this.isList(editor, node as CustomElement)) {
92
+ const prevListItemPath = Editor.before(editor, path, {
93
+ unit: 'block',
94
+ });
95
+
96
+ const prevList = Editor.above(editor, {
97
+ at: prevListItemPath,
98
+ match: (n) => this.isList(editor, n as CustomElement),
99
+ });
100
+
101
+ if (prevList) {
102
+ const currentDepth = this.getListDepth(editor, path);
103
+ const prevDepth = this.getListDepth(editor, prevList[1]);
104
+
105
+ if (
106
+ this.isList(editor, prevList[0] as CustomElement) &&
107
+ currentDepth === prevDepth
108
+ ) {
109
+ try {
110
+ Transforms.mergeNodes(editor, {
111
+ match: (n) => this.isList(editor, n as CustomElement),
112
+ });
113
+ } catch (e) {}
114
+ }
115
+ }
116
+ }
117
+
91
118
  normalizeNode(entry);
92
119
  };
93
120
 
@@ -249,17 +276,25 @@ export class ListsPlugin implements IPlugin {
249
276
  return;
250
277
  }
251
278
 
252
- const prevListItem = getPrev(editor, currentListItem);
279
+ const prevListItemPath = Editor.before(editor, currentListItem[1], {
280
+ unit: 'block',
281
+ });
253
282
 
254
- if (!prevListItem) {
255
- return;
256
- }
283
+ if (currentListItem && prevListItemPath) {
284
+ const prevListItem = Editor.above(editor, {
285
+ at: prevListItemPath,
286
+ match: (n) => this.isListItem(editor, n as CustomElement),
287
+ });
288
+
289
+ if (!prevListItem) {
290
+ return;
291
+ }
257
292
 
258
- if (currentListItem && prevListItem) {
259
293
  const currentDepth = this.getListItemDepth(
260
294
  editor,
261
295
  currentListItem[1],
262
296
  );
297
+
263
298
  const prevDepth = this.getListItemDepth(editor, prevListItem[1]);
264
299
 
265
300
  if (prevListItem && prevDepth >= currentDepth) {
@@ -370,17 +405,37 @@ export class ListsPlugin implements IPlugin {
370
405
  if (item) {
371
406
  let count = 0;
372
407
  let parent = Editor.parent(editor, item[1]);
373
- while (
408
+ do {
409
+ parent = Editor.parent(editor, parent[1]);
410
+ count++;
411
+ } while (
374
412
  parent !== null &&
375
413
  this.isList(editor, parent[0] as CustomElement)
376
- ) {
377
- parent = Editor.parent(editor, parent[1]);
414
+ );
415
+
416
+ return count;
417
+ }
418
+
419
+ return 0;
420
+ };
421
+
422
+ private getListDepth = (editor: Editor, path?: Path) => {
423
+ if (path) {
424
+ const node = Node.get(editor, path) as CustomElement;
425
+ if (!this.isList(editor, node)) {
426
+ return 0;
427
+ }
428
+
429
+ let count = 0;
430
+ let parent = Editor.parent(editor, path);
431
+
432
+ while (parent && this.isList(editor, parent[0] as CustomElement)) {
378
433
  count++;
434
+ parent = Editor.parent(editor, parent[1]);
379
435
  }
380
436
 
381
437
  return count;
382
438
  }
383
-
384
439
  return 0;
385
440
  };
386
441
 
@@ -67,7 +67,7 @@ export class ShortcutsPlugin implements IPlugin {
67
67
  );
68
68
 
69
69
  // Check if the text before cursor matches the pattern
70
- matchBefore = new RegExp(shortcut.before.source, 'g').exec(
70
+ matchBefore = new RegExp(`${shortcut.before.source}`, 'g').exec(
71
71
  textBeforeCursor,
72
72
  );
73
73
  }
@@ -77,7 +77,7 @@ export class ShortcutsPlugin implements IPlugin {
77
77
  const currentPoint = Range.start(selection);
78
78
  const [currentNode] = Editor.node(editor, currentPoint.path);
79
79
  const textBetweenMatches = Node.string(currentNode).slice(
80
- matchBefore.index,
80
+ matchBefore.index + matchBefore[0].length,
81
81
  currentPoint.offset,
82
82
  );
83
83
 
@@ -110,13 +110,10 @@ export class ShortcutsPlugin implements IPlugin {
110
110
  shortcut.change(editor, {
111
111
  before: matchBefore,
112
112
  after: matchAfter,
113
- text: matchAfter.input,
113
+ text: matchBefore.input.slice(matchBefore.index),
114
114
  cleanText: matchBefore.input.slice(
115
115
  matchBefore.index + matchBefore[0].length,
116
- matchBefore.index +
117
- matchBefore[0].length +
118
- matchAfter.index -
119
- matchAfter[0].length,
116
+ matchBefore.input.length - matchAfter[0].length,
120
117
  ),
121
118
  });
122
119
  return;