@use-kona/editor 0.1.25 → 0.1.26

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.
@@ -8,8 +8,9 @@ class ListsPlugin {
8
8
  static NUMBERED_LIST_ELEMENT = 'ol';
9
9
  static LIST_ITEM_ELEMENT = 'li';
10
10
  constructor(options = {}){
11
- const { listTypes = [], listItemTypes = [] } = options;
11
+ const { ignoreBackspaceTypes = [], listTypes = [], listItemTypes = [] } = options;
12
12
  this.options = {
13
+ ignoreBackspaceTypes,
13
14
  listTypes: [
14
15
  ListsPlugin.BULLETED_LIST_ELEMENT,
15
16
  ListsPlugin.NUMBERED_LIST_ELEMENT,
@@ -139,7 +140,8 @@ class ListsPlugin {
139
140
  match: (n)=>this.isListItem(editor, n)
140
141
  }));
141
142
  if ('Backspace' === event.key) {
142
- if (selection && match && 1 === this.getListItemDepth(editor, match[1]) && Editor.isStart(editor, selection.anchor, match[1])) {
143
+ if (selection && match && !this.options.ignoreBackspaceTypes?.includes(match[0].type) && 1 === this.getListItemDepth(editor, match[1]) && Editor.isStart(editor, selection.anchor, match[1])) {
144
+ event.preventDefault();
143
145
  Transforms.unwrapNodes(editor, {
144
146
  match: (n)=>this.isList(editor, n),
145
147
  split: true
@@ -5,6 +5,7 @@ import type { IPlugin } from '../../types';
5
5
  type Options = {
6
6
  listTypes?: string[];
7
7
  listItemTypes?: string[];
8
+ ignoreBackspaceTypes?: string[];
8
9
  };
9
10
  export declare class ListsPlugin implements IPlugin {
10
11
  options: Options;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@use-kona/editor",
3
- "version": "0.1.25",
3
+ "version": "0.1.26",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/index.ts"
@@ -1,35 +1,44 @@
1
1
  /** @jsxRuntime classic */
2
2
  /** @jsx jsx */
3
3
 
4
+ import type { KeyboardEvent, ReactNode } from 'react';
5
+ import { Editor } from 'slate';
4
6
  import {
5
7
  createHyperscript,
6
8
  createText,
7
9
  type HyperscriptShorthands,
8
10
  } from 'slate-hyperscript';
9
- import { describe, expect, it } from 'vitest';
10
- import { Editor } from 'slate';
11
+ import { describe, expect, it, vi } from 'vitest';
11
12
  import { createEditor } from '../../core/createEditor';
12
13
  import { ListsPlugin } from './ListsPlugin';
13
14
 
14
-
15
15
  const elements: HyperscriptShorthands = {
16
16
  paragraph: { type: 'paragraph' },
17
17
  numberedList: { type: ListsPlugin.NUMBERED_LIST_ELEMENT },
18
18
  bulletedList: { type: ListsPlugin.BULLETED_LIST_ELEMENT },
19
19
  listItem: { type: ListsPlugin.LIST_ITEM_ELEMENT },
20
+ taskItem: { type: 'task-item' },
20
21
  };
21
22
 
22
23
  const jsx = createHyperscript({ elements, creators: { text: createText } });
23
24
 
24
25
  jsx;
25
26
 
26
- const createEditorWithPlugin = (children: any) => {
27
+ const createEditorAndPlugin = (
28
+ children: ReactNode,
29
+ options: ConstructorParameters<typeof ListsPlugin>[0] = {},
30
+ ) => {
27
31
  const editorState = <editor>{children}</editor>;
28
- const editor = createEditor([new ListsPlugin()])();
32
+ const plugin = new ListsPlugin(options);
33
+ const editor = createEditor([plugin])();
29
34
  editor.children = editorState.children;
30
35
  editor.selection = editorState.selection;
31
36
 
32
- return editor;
37
+ return { editor, plugin };
38
+ };
39
+
40
+ const createEditorWithPlugin = (children: ReactNode) => {
41
+ return createEditorAndPlugin(children).editor;
33
42
  };
34
43
 
35
44
  describe('ListsPlugin', () => {
@@ -128,13 +137,19 @@ describe('ListsPlugin', () => {
128
137
  {
129
138
  type: ListsPlugin.NUMBERED_LIST_ELEMENT,
130
139
  children: [
131
- { type: ListsPlugin.LIST_ITEM_ELEMENT, children: [{ text: 'first' }] },
140
+ {
141
+ type: ListsPlugin.LIST_ITEM_ELEMENT,
142
+ children: [{ text: 'first' }],
143
+ },
132
144
  ],
133
145
  },
134
146
  {
135
147
  type: ListsPlugin.NUMBERED_LIST_ELEMENT,
136
148
  children: [
137
- { type: ListsPlugin.LIST_ITEM_ELEMENT, children: [{ text: 'second' }] },
149
+ {
150
+ type: ListsPlugin.LIST_ITEM_ELEMENT,
151
+ children: [{ text: 'second' }],
152
+ },
138
153
  ],
139
154
  },
140
155
  ];
@@ -146,8 +161,14 @@ describe('ListsPlugin', () => {
146
161
  {
147
162
  type: ListsPlugin.NUMBERED_LIST_ELEMENT,
148
163
  children: [
149
- { type: ListsPlugin.LIST_ITEM_ELEMENT, children: [{ text: 'first' }] },
150
- { type: ListsPlugin.LIST_ITEM_ELEMENT, children: [{ text: 'second' }] },
164
+ {
165
+ type: ListsPlugin.LIST_ITEM_ELEMENT,
166
+ children: [{ text: 'first' }],
167
+ },
168
+ {
169
+ type: ListsPlugin.LIST_ITEM_ELEMENT,
170
+ children: [{ text: 'second' }],
171
+ },
151
172
  ],
152
173
  },
153
174
  ];
@@ -166,13 +187,19 @@ describe('ListsPlugin', () => {
166
187
  {
167
188
  type: ListsPlugin.BULLETED_LIST_ELEMENT,
168
189
  children: [
169
- { type: ListsPlugin.LIST_ITEM_ELEMENT, children: [{ text: 'bullet' }] },
190
+ {
191
+ type: ListsPlugin.LIST_ITEM_ELEMENT,
192
+ children: [{ text: 'bullet' }],
193
+ },
170
194
  ],
171
195
  },
172
196
  {
173
197
  type: ListsPlugin.NUMBERED_LIST_ELEMENT,
174
198
  children: [
175
- { type: ListsPlugin.LIST_ITEM_ELEMENT, children: [{ text: 'number' }] },
199
+ {
200
+ type: ListsPlugin.LIST_ITEM_ELEMENT,
201
+ children: [{ text: 'number' }],
202
+ },
176
203
  ],
177
204
  },
178
205
  ];
@@ -184,13 +211,19 @@ describe('ListsPlugin', () => {
184
211
  {
185
212
  type: ListsPlugin.BULLETED_LIST_ELEMENT,
186
213
  children: [
187
- { type: ListsPlugin.LIST_ITEM_ELEMENT, children: [{ text: 'bullet' }] },
214
+ {
215
+ type: ListsPlugin.LIST_ITEM_ELEMENT,
216
+ children: [{ text: 'bullet' }],
217
+ },
188
218
  ],
189
219
  },
190
220
  {
191
221
  type: ListsPlugin.NUMBERED_LIST_ELEMENT,
192
222
  children: [
193
- { type: ListsPlugin.LIST_ITEM_ELEMENT, children: [{ text: 'number' }] },
223
+ {
224
+ type: ListsPlugin.LIST_ITEM_ELEMENT,
225
+ children: [{ text: 'number' }],
226
+ },
194
227
  ],
195
228
  },
196
229
  ];
@@ -242,4 +275,67 @@ describe('ListsPlugin', () => {
242
275
 
243
276
  expect(editor.children).toEqual(output);
244
277
  });
278
+
279
+ it('should turn first-level list item into paragraph on Backspace at start', () => {
280
+ const { editor, plugin } = createEditorAndPlugin(
281
+ <bulletedList>
282
+ <listItem>
283
+ <cursor />
284
+ <text>Hello world</text>
285
+ </listItem>
286
+ </bulletedList>,
287
+ );
288
+ const event = {
289
+ key: 'Backspace',
290
+ preventDefault: vi.fn(),
291
+ } as unknown as KeyboardEvent;
292
+
293
+ plugin.handlers.onKeyDown(event, editor);
294
+
295
+ const output = (
296
+ <editor>
297
+ <paragraph>
298
+ <text>Hello world</text>
299
+ </paragraph>
300
+ </editor>
301
+ );
302
+
303
+ expect(event.preventDefault).toHaveBeenCalledOnce();
304
+ expect(editor.children).toEqual(output.children);
305
+ });
306
+
307
+ it('should ignore configured list item types for Backspace conversion', () => {
308
+ const { editor, plugin } = createEditorAndPlugin(
309
+ <bulletedList>
310
+ <taskItem>
311
+ <cursor />
312
+ <text>Hello world</text>
313
+ </taskItem>
314
+ </bulletedList>,
315
+ {
316
+ ignoreBackspaceTypes: ['task-item'],
317
+ listItemTypes: ['task-item'],
318
+ },
319
+ );
320
+ const event = {
321
+ key: 'Backspace',
322
+ preventDefault: vi.fn(),
323
+ } as unknown as KeyboardEvent;
324
+
325
+ plugin.handlers.onKeyDown(event, editor);
326
+
327
+ const output = (
328
+ <editor>
329
+ <bulletedList>
330
+ <taskItem>
331
+ <text />
332
+ <text>Hello world</text>
333
+ </taskItem>
334
+ </bulletedList>
335
+ </editor>
336
+ );
337
+
338
+ expect(event.preventDefault).not.toHaveBeenCalled();
339
+ expect(editor.children).toEqual(output.children);
340
+ });
245
341
  });
@@ -17,6 +17,7 @@ import styles from './styles.module.css';
17
17
  type Options = {
18
18
  listTypes?: string[];
19
19
  listItemTypes?: string[];
20
+ ignoreBackspaceTypes?: string[];
20
21
  };
21
22
 
22
23
  export class ListsPlugin implements IPlugin {
@@ -27,9 +28,14 @@ export class ListsPlugin implements IPlugin {
27
28
  static LIST_ITEM_ELEMENT = 'li';
28
29
 
29
30
  constructor(options: Options = {}) {
30
- const { listTypes = [], listItemTypes = [] } = options;
31
+ const {
32
+ ignoreBackspaceTypes = [],
33
+ listTypes = [],
34
+ listItemTypes = [],
35
+ } = options;
31
36
 
32
37
  this.options = {
38
+ ignoreBackspaceTypes,
33
39
  listTypes: [
34
40
  ListsPlugin.BULLETED_LIST_ELEMENT,
35
41
  ListsPlugin.NUMBERED_LIST_ELEMENT,
@@ -239,9 +245,14 @@ export class ListsPlugin implements IPlugin {
239
245
  if (
240
246
  selection &&
241
247
  match &&
248
+ !this.options.ignoreBackspaceTypes?.includes(
249
+ (match[0] as CustomElement).type,
250
+ ) &&
242
251
  this.getListItemDepth(editor, match[1]) === 1 &&
243
252
  Editor.isStart(editor, selection.anchor, match[1])
244
253
  ) {
254
+ event.preventDefault();
255
+
245
256
  Transforms.unwrapNodes(editor, {
246
257
  match: (n) => this.isList(editor, n as CustomElement),
247
258
  split: true,