@use-kona/editor 0.1.24 → 0.1.25
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 +146 -15
- package/dist/core/createEditor.js +1 -1
- package/dist/core/styles.module.js +3 -1
- package/dist/core/styles_module.css +13 -0
- package/dist/elements/Block.js +25 -0
- package/dist/index.js +2 -1
- package/dist/plugins/CodeBlockPlugin/CodeBlockPlugin.js +1 -1
- package/dist/plugins/DnDPlugin/DnDPlugin.js +93 -45
- package/dist/plugins/DnDPlugin/types.js +2 -0
- package/dist/plugins/HeadingsPlugin/HeadingsPlugin.js +1 -1
- package/dist/plugins/ListsPlugin/ListsPlugin.js +1 -1
- package/dist/plugins/NodeIdPlugin/NodeIdPlugin.js +6 -4
- package/dist/provider.js +15 -0
- package/dist/src/core/createEditable.d.ts +1 -1
- package/dist/src/elements/Block.d.ts +2 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/plugins/DnDPlugin/DnDPlugin.d.ts +0 -7
- package/dist/src/plugins/DnDPlugin/types.d.ts +29 -15
- package/dist/src/plugins/NodeIdPlugin/NodeIdPlugin.d.ts +1 -0
- package/dist/src/provider.d.ts +8 -0
- package/dist/src/types.d.ts +4 -0
- package/package.json +9 -8
- package/src/core/createEditable.tsx +170 -18
- package/src/core/createEditor.ts +1 -1
- package/src/core/styles.module.css +12 -0
- package/src/editor.tsx +4 -3
- package/src/elements/Block.tsx +31 -0
- package/src/index.ts +1 -0
- package/src/plugins/CodeBlockPlugin/CodeBlockPlugin.tsx +1 -3
- package/src/plugins/DnDPlugin/DnDPlugin.tsx +147 -62
- package/src/plugins/DnDPlugin/types.ts +35 -20
- package/src/plugins/HeadingsPlugin/HeadingsPlugin.tsx +1 -2
- package/src/plugins/ListsPlugin/ListsPlugin.tsx +5 -5
- package/src/plugins/NodeIdPlugin/NodeIdPlugin.ts +9 -7
- package/src/provider.tsx +28 -0
- package/src/types.ts +5 -0
|
@@ -2,9 +2,16 @@ import type React from 'react';
|
|
|
2
2
|
import type { ConnectDragPreview, ConnectDragSource, ConnectDropTarget } from 'react-dnd';
|
|
3
3
|
import type { Editor, Path } from 'slate';
|
|
4
4
|
import type { RenderElementProps } from 'slate-react';
|
|
5
|
-
import type { CustomElement } from '../../../types';
|
|
6
|
-
|
|
5
|
+
import type { CustomElement, CustomText } from '../../../types';
|
|
6
|
+
import type { NodeIdPlugin } from '../NodeIdPlugin';
|
|
7
|
+
export type NodeWithId = Omit<CustomElement, 'children'> & {
|
|
7
8
|
nodeId: string;
|
|
9
|
+
children: (NodeWithId | CustomText)[];
|
|
10
|
+
};
|
|
11
|
+
export declare const isNodeWithId: (node: unknown) => node is NodeWithId;
|
|
12
|
+
export type CustomDropHandler<T = unknown> = {
|
|
13
|
+
type: string | symbol;
|
|
14
|
+
onDrop: (context: CustomDropContext<T>) => DropResult;
|
|
8
15
|
};
|
|
9
16
|
export type Options = {
|
|
10
17
|
editorId?: string;
|
|
@@ -16,8 +23,8 @@ export type Options = {
|
|
|
16
23
|
dropRef: ConnectDropTarget;
|
|
17
24
|
previewRef: ConnectDragPreview;
|
|
18
25
|
position: 'top' | 'bottom' | null;
|
|
19
|
-
|
|
20
|
-
onToggleSelected:
|
|
26
|
+
onNativeDrop?: (event: React.DragEvent<HTMLDivElement>) => void;
|
|
27
|
+
onToggleSelected?: (event: React.MouseEvent<HTMLDivElement>) => void;
|
|
21
28
|
}) => React.ReactNode;
|
|
22
29
|
ignoreNodes?: string[];
|
|
23
30
|
customTypes?: {
|
|
@@ -27,25 +34,23 @@ export type Options = {
|
|
|
27
34
|
};
|
|
28
35
|
};
|
|
29
36
|
customDropHandlers?: {
|
|
30
|
-
[type: string | symbol]:
|
|
31
|
-
type: string | symbol;
|
|
32
|
-
onDrop: <T = unknown>(context: CustomDropContext<T>) => DropResult;
|
|
33
|
-
};
|
|
37
|
+
[type: string | symbol]: CustomDropHandler;
|
|
34
38
|
};
|
|
39
|
+
nodeIdPlugin?: NodeIdPlugin;
|
|
35
40
|
};
|
|
36
|
-
type CustomDropContext<
|
|
41
|
+
export type CustomDropContext<TItem = unknown> = {
|
|
37
42
|
editor: Editor;
|
|
38
|
-
|
|
43
|
+
data: EditorOuterDragItem<TItem>;
|
|
39
44
|
insertAt: Path;
|
|
40
45
|
};
|
|
41
46
|
type DropResult = 'handled' | 'ignored';
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
*/
|
|
45
|
-
export type EditorDragItem = {
|
|
47
|
+
export type EditorInnerDragItem<TItem = unknown> = {
|
|
48
|
+
kind: 'inner';
|
|
46
49
|
nodes: NodeWithId[];
|
|
47
|
-
|
|
50
|
+
item?: TItem;
|
|
51
|
+
items?: TItem[];
|
|
48
52
|
source: {
|
|
53
|
+
editor: Editor;
|
|
49
54
|
/**
|
|
50
55
|
* Different editors have different unique identifiers.
|
|
51
56
|
*/
|
|
@@ -59,4 +64,13 @@ export type EditorDragItem = {
|
|
|
59
64
|
};
|
|
60
65
|
data?: Record<string, unknown>;
|
|
61
66
|
};
|
|
67
|
+
export type EditorOuterDragItem<TItem = unknown> = {
|
|
68
|
+
kind: 'outer';
|
|
69
|
+
item: TItem;
|
|
70
|
+
items: TItem[];
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* We're using this type to represent a drag item that is being dragged from the editor.
|
|
74
|
+
*/
|
|
75
|
+
export type EditorDragItem<TItem = unknown> = EditorInnerDragItem<TItem> | EditorOuterDragItem<TItem>;
|
|
62
76
|
export {};
|
|
@@ -9,6 +9,7 @@ type Options = {
|
|
|
9
9
|
};
|
|
10
10
|
export declare class NodeIdPlugin implements IPlugin<Editor, NodeIdBlock> {
|
|
11
11
|
private options;
|
|
12
|
+
generateId: () => string;
|
|
12
13
|
constructor(options: Options);
|
|
13
14
|
init(editor: Editor): import("slate").BaseEditor & import("slate-react").ReactEditor & {
|
|
14
15
|
getCommands: <T extends IPlugin>(plugin: new () => T) => T["commands"];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { StoreValue } from 'nanostores';
|
|
2
|
+
import type React from 'react';
|
|
3
|
+
import type { EditorContext } from './types';
|
|
4
|
+
export declare const EditorProvider: ({ children, selectedNodes, }: {
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
selectedNodes: StoreValue<string[]>;
|
|
7
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export declare const useEditorContext: () => EditorContext;
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { StoreValue } from 'nanostores';
|
|
1
2
|
import type { KeyboardEvent, ReactElement, ReactNode } from 'react';
|
|
2
3
|
import type { DecoratedRange, Descendant, Editor, Location, NodeEntry, NodeMatch } from 'slate';
|
|
3
4
|
import type { RenderElementProps, RenderLeafProps } from 'slate-react';
|
|
@@ -57,4 +58,7 @@ export type EditorRef = {
|
|
|
57
58
|
};
|
|
58
59
|
export type Serialize = (node: CustomElement | CustomText, children?: string) => string | undefined;
|
|
59
60
|
export type Deserialize = (element: HTMLElement, children?: (string | Descendant)[]) => CustomElement | CustomText[] | undefined;
|
|
61
|
+
export type EditorContext = {
|
|
62
|
+
selectedNodes: StoreValue<string[]>;
|
|
63
|
+
};
|
|
60
64
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@use-kona/editor",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./src/index.ts"
|
|
@@ -43,10 +43,11 @@
|
|
|
43
43
|
"react-dnd": "^16.0.1",
|
|
44
44
|
"react-dnd-html5-backend": "^16.0.1",
|
|
45
45
|
"react-dom": "18",
|
|
46
|
-
"slate": "^0.
|
|
46
|
+
"slate": "^0.126.0",
|
|
47
|
+
"slate-dom": "^0.126.0",
|
|
47
48
|
"slate-history": "^0.113.1",
|
|
48
|
-
"slate-hyperscript": "^0.
|
|
49
|
-
"slate-react": "^0.
|
|
49
|
+
"slate-hyperscript": "^0.125.0",
|
|
50
|
+
"slate-react": "^0.126.0",
|
|
50
51
|
"storybook": "^9.0.13",
|
|
51
52
|
"storybook-addon-rslib": "^2.0.2",
|
|
52
53
|
"storybook-react-rsbuild": "^2.0.2",
|
|
@@ -58,10 +59,11 @@
|
|
|
58
59
|
"react-dnd": "^16.0.1",
|
|
59
60
|
"react-dnd-html5-backend": "^16.0.1",
|
|
60
61
|
"react-dom": "18",
|
|
61
|
-
"slate": "^0.
|
|
62
|
+
"slate": "^0.126.0",
|
|
63
|
+
"slate-dom": "^0.126.0",
|
|
62
64
|
"slate-history": "^0.113.1",
|
|
63
|
-
"slate-hyperscript": "^0.
|
|
64
|
-
"slate-react": "^0.
|
|
65
|
+
"slate-hyperscript": "^0.125.0",
|
|
66
|
+
"slate-react": "^0.126.0"
|
|
65
67
|
},
|
|
66
68
|
"dependencies": {
|
|
67
69
|
"@nanostores/react": "^1.0.0",
|
|
@@ -69,7 +71,6 @@
|
|
|
69
71
|
"escape-html": "^1.0.3",
|
|
70
72
|
"is-hotkey": "^0.2.0",
|
|
71
73
|
"is-url": "^1.2.4",
|
|
72
|
-
"nanoid": "^5.1.16",
|
|
73
74
|
"nanostores": "^1.0.1",
|
|
74
75
|
"prismjs": "^1.30.0"
|
|
75
76
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { useStore } from '@nanostores/react';
|
|
1
2
|
import isHotkey from 'is-hotkey';
|
|
3
|
+
import { atom } from 'nanostores';
|
|
2
4
|
import React, {
|
|
3
5
|
type ClipboardEvent,
|
|
4
6
|
isValidElement,
|
|
@@ -7,21 +9,28 @@ import React, {
|
|
|
7
9
|
type ReactElement,
|
|
8
10
|
type ReactNode,
|
|
9
11
|
useCallback,
|
|
12
|
+
useState,
|
|
10
13
|
} from 'react';
|
|
11
14
|
import {
|
|
12
15
|
type DecoratedRange,
|
|
13
16
|
type Descendant,
|
|
14
|
-
|
|
17
|
+
Editor,
|
|
18
|
+
Element,
|
|
15
19
|
type NodeEntry,
|
|
20
|
+
Path,
|
|
16
21
|
Transforms,
|
|
17
22
|
} from 'slate';
|
|
18
23
|
import {
|
|
19
24
|
Editable,
|
|
25
|
+
ReactEditor,
|
|
20
26
|
type RenderElementProps,
|
|
21
27
|
type RenderLeafProps,
|
|
22
28
|
useReadOnly,
|
|
23
29
|
} from 'slate-react';
|
|
30
|
+
import type { CustomElement } from '../../types';
|
|
24
31
|
import { BaseElement } from '../elements/BaseElement';
|
|
32
|
+
import { Block } from '../elements/Block';
|
|
33
|
+
import { EditorProvider } from '../provider';
|
|
25
34
|
import type { IPlugin } from '../types';
|
|
26
35
|
import { deserialize } from './deserialize';
|
|
27
36
|
import styles from './styles.module.css';
|
|
@@ -35,6 +44,10 @@ type Props = {
|
|
|
35
44
|
export const createEditable =
|
|
36
45
|
(editor: Editor, plugins: IPlugin[]) =>
|
|
37
46
|
({ readOnly }: Props) => {
|
|
47
|
+
const [selectedNodes] = useState(() => atom<CustomElement[]>([]));
|
|
48
|
+
|
|
49
|
+
const $selectedNodes = useStore(selectedNodes);
|
|
50
|
+
|
|
38
51
|
const renderElement = (props: RenderElementProps): ReactElement => {
|
|
39
52
|
let result: ReactElement | null = null;
|
|
40
53
|
|
|
@@ -56,13 +69,21 @@ export const createEditable =
|
|
|
56
69
|
}
|
|
57
70
|
}
|
|
58
71
|
|
|
59
|
-
|
|
72
|
+
if (Editor.isInline(editor, props.element)) {
|
|
73
|
+
return result || <BaseElement {...props} />;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<Block element={props.element} attributes={props.attributes}>
|
|
78
|
+
{result || <BaseElement {...props} />}
|
|
79
|
+
</Block>
|
|
80
|
+
);
|
|
60
81
|
};
|
|
61
82
|
|
|
62
83
|
const renderLeaf = (props: RenderLeafProps): JSX.Element => {
|
|
63
84
|
let result: ReactNode;
|
|
64
85
|
for (const plugin of plugins) {
|
|
65
|
-
plugin.leafs?.
|
|
86
|
+
plugin.leafs?.forEach((element) => {
|
|
66
87
|
const newElement = element.render(
|
|
67
88
|
{
|
|
68
89
|
...props,
|
|
@@ -84,6 +105,132 @@ export const createEditable =
|
|
|
84
105
|
event.preventDefault();
|
|
85
106
|
}
|
|
86
107
|
|
|
108
|
+
if (event.key === 'Escape') {
|
|
109
|
+
const currentBlock = Editor.above<CustomElement>(editor, {
|
|
110
|
+
at: editor.selection?.focus,
|
|
111
|
+
match: (n) => Element.isElement(n) && Editor.isBlock(editor, n),
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (!currentBlock) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if ($selectedNodes.length === 0) {
|
|
119
|
+
selectedNodes.set([currentBlock[0]]);
|
|
120
|
+
} else {
|
|
121
|
+
selectedNodes.set([]);
|
|
122
|
+
ReactEditor.blur(editor);
|
|
123
|
+
}
|
|
124
|
+
} else if (event.key === 'ArrowDown') {
|
|
125
|
+
const currentBlock = Editor.above(editor, {
|
|
126
|
+
at: editor.selection?.focus,
|
|
127
|
+
match: (n) => Element.isElement(n),
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
if (!currentBlock) return;
|
|
131
|
+
|
|
132
|
+
const nextBlock = Editor.next(editor, {
|
|
133
|
+
at: currentBlock[1],
|
|
134
|
+
match: (n) => Element.isElement(n),
|
|
135
|
+
voids: true,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
if (nextBlock) {
|
|
139
|
+
if (Editor.isVoid(editor, nextBlock[0])) {
|
|
140
|
+
event.preventDefault();
|
|
141
|
+
selectedNodes.set([nextBlock[0]]);
|
|
142
|
+
} else {
|
|
143
|
+
selectedNodes.set([]);
|
|
144
|
+
}
|
|
145
|
+
if (
|
|
146
|
+
Editor.isVoid(editor, currentBlock[0]) ||
|
|
147
|
+
Editor.isVoid(editor, nextBlock[0])
|
|
148
|
+
) {
|
|
149
|
+
event.preventDefault();
|
|
150
|
+
Transforms.select(editor, nextBlock[1]);
|
|
151
|
+
Transforms.collapse(editor, { edge: 'start' });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
} else if (event.key === 'ArrowUp') {
|
|
155
|
+
const currentBlock = Editor.above(editor, {
|
|
156
|
+
at: editor.selection?.focus,
|
|
157
|
+
match: (n) => Element.isElement(n),
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
if (!currentBlock) return;
|
|
161
|
+
|
|
162
|
+
const prevBlock = Editor.previous(editor, {
|
|
163
|
+
at: currentBlock[1],
|
|
164
|
+
match: (n) => Element.isElement(n),
|
|
165
|
+
voids: true,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
if (prevBlock) {
|
|
169
|
+
if (Editor.isVoid(editor, prevBlock[0])) {
|
|
170
|
+
event.preventDefault();
|
|
171
|
+
selectedNodes.set([prevBlock[0]]);
|
|
172
|
+
} else {
|
|
173
|
+
selectedNodes.set([]);
|
|
174
|
+
}
|
|
175
|
+
if (
|
|
176
|
+
Editor.isVoid(editor, currentBlock[0]) ||
|
|
177
|
+
Editor.isVoid(editor, prevBlock[0])
|
|
178
|
+
) {
|
|
179
|
+
event.preventDefault();
|
|
180
|
+
Transforms.select(editor, prevBlock[1]);
|
|
181
|
+
Transforms.collapse(editor, { edge: 'end' });
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
} else if (event.key === 'Backspace') {
|
|
185
|
+
const currentBlock = Editor.above(editor, {
|
|
186
|
+
at: editor.selection?.focus,
|
|
187
|
+
match: (n) => Element.isElement(n) && Editor.isBlock(editor, n),
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
if (!currentBlock) return;
|
|
191
|
+
if (editor.selection?.focus.offset !== 0) return;
|
|
192
|
+
|
|
193
|
+
const prevBlock = Editor.previous(editor, {
|
|
194
|
+
at: currentBlock[1],
|
|
195
|
+
match: (n) => Element.isElement(n),
|
|
196
|
+
voids: true,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
if (prevBlock && Editor.isVoid(editor, prevBlock[0])) {
|
|
200
|
+
event.preventDefault();
|
|
201
|
+
Transforms.removeNodes(editor, {
|
|
202
|
+
at: currentBlock[1],
|
|
203
|
+
});
|
|
204
|
+
Transforms.select(editor, prevBlock[1]);
|
|
205
|
+
selectedNodes.set([prevBlock[0]]);
|
|
206
|
+
Transforms.collapse(editor, { edge: 'start' });
|
|
207
|
+
}
|
|
208
|
+
} else if (event.key === 'Enter') {
|
|
209
|
+
const currentBlock = Editor.above(editor, {
|
|
210
|
+
at: editor.selection?.focus,
|
|
211
|
+
match: (n) => Element.isElement(n),
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
if (!currentBlock) return;
|
|
215
|
+
|
|
216
|
+
if (Editor.isVoid(editor, currentBlock[0])) {
|
|
217
|
+
event.preventDefault();
|
|
218
|
+
Transforms.insertNodes(
|
|
219
|
+
editor,
|
|
220
|
+
{
|
|
221
|
+
type: 'paragraph',
|
|
222
|
+
children: [{ text: '' }],
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
at: Path.next(currentBlock[1]),
|
|
226
|
+
},
|
|
227
|
+
);
|
|
228
|
+
selectedNodes.set([]);
|
|
229
|
+
Transforms.select(editor, Path.next(currentBlock[1]));
|
|
230
|
+
Transforms.collapse(editor, { edge: 'start' });
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
87
234
|
for (const plugin of plugins) {
|
|
88
235
|
if (plugin.hotkeys) {
|
|
89
236
|
for (const hotkey of plugin.hotkeys) {
|
|
@@ -154,12 +301,15 @@ export const createEditable =
|
|
|
154
301
|
}
|
|
155
302
|
|
|
156
303
|
return (
|
|
157
|
-
<div
|
|
158
|
-
|
|
159
|
-
|
|
304
|
+
<div
|
|
305
|
+
className={styles.ui}
|
|
306
|
+
onClick={(event) => {
|
|
307
|
+
event.stopPropagation();
|
|
308
|
+
}}
|
|
309
|
+
>
|
|
160
310
|
{ui.map((p, index) => (
|
|
161
311
|
<React.Fragment key={index}>
|
|
162
|
-
{p.ui
|
|
312
|
+
{p.ui?.({ readOnly, children, editor })}
|
|
163
313
|
</React.Fragment>
|
|
164
314
|
))}
|
|
165
315
|
<div className={styles.editor}>{children}</div>
|
|
@@ -170,16 +320,18 @@ export const createEditable =
|
|
|
170
320
|
);
|
|
171
321
|
|
|
172
322
|
return (
|
|
173
|
-
<
|
|
174
|
-
<
|
|
175
|
-
<
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
323
|
+
<EditorProvider selectedNodes={selectedNodes}>
|
|
324
|
+
<div className={styles.root}>
|
|
325
|
+
<Ui>
|
|
326
|
+
<Editable
|
|
327
|
+
decorate={decorate}
|
|
328
|
+
renderElement={renderElement}
|
|
329
|
+
renderLeaf={renderLeaf}
|
|
330
|
+
readOnly={readOnly}
|
|
331
|
+
{...handlers}
|
|
332
|
+
/>
|
|
333
|
+
</Ui>
|
|
334
|
+
</div>
|
|
335
|
+
</EditorProvider>
|
|
184
336
|
);
|
|
185
337
|
};
|
package/src/core/createEditor.ts
CHANGED
|
@@ -219,7 +219,7 @@ export const createEditor = (plugins: IPlugin[]) => () => {
|
|
|
219
219
|
const hasOnBeforeDelete = match?.onBeforeDelete;
|
|
220
220
|
|
|
221
221
|
if (hasOnBeforeDelete) {
|
|
222
|
-
const result = await match.onBeforeDelete
|
|
222
|
+
const result = await match.onBeforeDelete?.([node]);
|
|
223
223
|
|
|
224
224
|
if (result) {
|
|
225
225
|
Transforms.removeNodes(editorWithPlugins, {
|
|
@@ -32,3 +32,15 @@
|
|
|
32
32
|
outline: none;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
+
|
|
36
|
+
.block {
|
|
37
|
+
transition: background-color 0.2s ease-in-out;
|
|
38
|
+
|
|
39
|
+
&.selected {
|
|
40
|
+
background-color: var(--kona-editor-selected-color);
|
|
41
|
+
& > * {
|
|
42
|
+
transition: transform 0.2s ease-in-out;
|
|
43
|
+
transform: scale(0.98);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/editor.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { atom } from 'nanostores';
|
|
1
2
|
import { forwardRef, useCallback, useImperativeHandle, useState } from 'react';
|
|
2
|
-
import {type Descendant, Editor, type NodeMatch, Transforms} from 'slate';
|
|
3
|
+
import { type Descendant, Editor, type NodeMatch, Transforms } from 'slate';
|
|
3
4
|
import { ReactEditor, Slate } from 'slate-react';
|
|
4
5
|
import type { CustomElement } from '../types';
|
|
5
6
|
import { createEditable } from './core/createEditable';
|
|
@@ -54,8 +55,8 @@ export const KonaEditor = forwardRef<EditorRef, KonaEditorProps>(
|
|
|
54
55
|
},
|
|
55
56
|
focus: (mode?: 'end') => {
|
|
56
57
|
if (mode === 'end') {
|
|
57
|
-
const endPoint = Editor.end(editor, [])
|
|
58
|
-
Transforms.select(editor, endPoint)
|
|
58
|
+
const endPoint = Editor.end(editor, []);
|
|
59
|
+
Transforms.select(editor, endPoint);
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
ReactEditor.focus(editor);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useStore } from '@nanostores/react';
|
|
2
|
+
import { Editor } from 'slate';
|
|
3
|
+
import { type RenderElementProps, useSlateStatic } from 'slate-react';
|
|
4
|
+
import styles from '../core/styles.module.css';
|
|
5
|
+
import { useEditorContext } from '../provider';
|
|
6
|
+
|
|
7
|
+
export const Block = (props: RenderElementProps) => {
|
|
8
|
+
const editor = useSlateStatic();
|
|
9
|
+
const { selectedNodes } = useEditorContext();
|
|
10
|
+
const $selectedNodes = useStore(selectedNodes);
|
|
11
|
+
|
|
12
|
+
const isSelected = $selectedNodes.some((node) => node === props.element);
|
|
13
|
+
|
|
14
|
+
const classes = isSelected
|
|
15
|
+
? `${styles.block} ${styles.selected}`
|
|
16
|
+
: styles.block;
|
|
17
|
+
|
|
18
|
+
const handleClick = () => {
|
|
19
|
+
if (Editor.isVoid(editor, props.element)) {
|
|
20
|
+
selectedNodes.set([props.element]);
|
|
21
|
+
} else {
|
|
22
|
+
selectedNodes.set([]);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className={classes} onClick={handleClick}>
|
|
28
|
+
{props.children}
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -4,5 +4,6 @@ export { serialize } from './core/serialize';
|
|
|
4
4
|
export { defaultValue } from './defaultValue';
|
|
5
5
|
export * from './editor';
|
|
6
6
|
export * from './plugins';
|
|
7
|
+
export { useEditorContext } from './provider';
|
|
7
8
|
export type { EditorRef, IPlugin } from './types';
|
|
8
9
|
export { isEmpty } from './utils';
|
|
@@ -117,9 +117,7 @@ export class CodeBlockPlugin implements IPlugin {
|
|
|
117
117
|
// Find the code block node and its path
|
|
118
118
|
const entry = Editor.above(editor, {
|
|
119
119
|
match: (n) =>
|
|
120
|
-
|
|
121
|
-
Element.isElement(n) &&
|
|
122
|
-
n.type === CodeBlockPlugin.CODE_ELEMENT,
|
|
120
|
+
Element.isElement(n) && n.type === CodeBlockPlugin.CODE_ELEMENT,
|
|
123
121
|
});
|
|
124
122
|
|
|
125
123
|
if (entry) {
|