@use-kona/editor 0.1.0-rc.5 → 0.1.2-rc.0
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/plugins/CodeBlockPlugin/CodeBlock.js +5 -2
- package/dist/plugins/CodeBlockPlugin/CodeBlockLine.d.ts +1 -1
- package/dist/plugins/CodeBlockPlugin/CodeBlockLine.js +2 -0
- package/dist/plugins/CodeBlockPlugin/styles.module.js +3 -2
- package/dist/plugins/CodeBlockPlugin/styles_module.css +21 -2
- package/package.json +1 -1
- package/src/core/deserialize.ts +26 -5
- package/src/examples/Editor.module.css +0 -1
- package/src/examples/Editor.tsx +36 -6
- package/src/index.ts +2 -0
- package/src/plugins/BasicFormattingPlugin/BasicFormattingPlugin.tsx +31 -0
- package/src/plugins/CodeBlockPlugin/CodeBlock.tsx +4 -2
- package/src/plugins/CodeBlockPlugin/CodeBlockLine.tsx +7 -2
- package/src/plugins/CodeBlockPlugin/styles.module.css +21 -2
- package/src/plugins/HeadingsPlugin/HeadingsPlugin.tsx +36 -2
- package/src/plugins/LinksPlugin/LinksPlugin.tsx +15 -0
- package/src/plugins/ListsPlugin/ListsPlugin.tsx +31 -0
- package/src/types.ts +2 -1
|
@@ -2,7 +2,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import styles_module from "./styles.module.js";
|
|
3
3
|
const CodeBlock = (props)=>/*#__PURE__*/ jsxs("div", {
|
|
4
4
|
...props.attributes,
|
|
5
|
-
className: styles_module.
|
|
5
|
+
className: styles_module.root,
|
|
6
6
|
spellCheck: false,
|
|
7
7
|
children: [
|
|
8
8
|
/*#__PURE__*/ jsx("div", {
|
|
@@ -11,7 +11,10 @@ const CodeBlock = (props)=>/*#__PURE__*/ jsxs("div", {
|
|
|
11
11
|
}),
|
|
12
12
|
/*#__PURE__*/ jsx("div", {
|
|
13
13
|
className: styles_module.content,
|
|
14
|
-
children:
|
|
14
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
15
|
+
className: styles_module.code,
|
|
16
|
+
children: props.children
|
|
17
|
+
})
|
|
15
18
|
})
|
|
16
19
|
]
|
|
17
20
|
});
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { RenderElementProps } from 'slate-react';
|
|
1
|
+
import type { RenderElementProps } from 'slate-react';
|
|
2
2
|
export declare const CodeBlockLine: (props: RenderElementProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import styles_module from "./styles.module.js";
|
|
2
3
|
const CodeBlockLine = (props)=>/*#__PURE__*/ jsx("div", {
|
|
3
4
|
...props.attributes,
|
|
4
5
|
style: {
|
|
5
6
|
position: 'relative'
|
|
6
7
|
},
|
|
8
|
+
className: styles_module.line,
|
|
7
9
|
children: props.children
|
|
8
10
|
});
|
|
9
11
|
export { CodeBlockLine };
|
|
@@ -1,12 +1,31 @@
|
|
|
1
|
-
.
|
|
1
|
+
.root-aY1XCj {
|
|
2
2
|
border: 1px solid var(--kona-editor-border-color, #eee);
|
|
3
3
|
border-radius: 4px;
|
|
4
|
+
flex-direction: column;
|
|
4
5
|
font-family: monospace;
|
|
6
|
+
display: flex;
|
|
5
7
|
overflow: hidden;
|
|
6
8
|
}
|
|
7
9
|
|
|
8
10
|
.content-HIkMJO {
|
|
9
11
|
background-color: var(--kona-editor-background-color, #fff);
|
|
10
|
-
|
|
12
|
+
display: flex;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.line-dwbtZS {
|
|
16
|
+
counter-increment: line;
|
|
17
|
+
padding-left: 50px;
|
|
18
|
+
|
|
19
|
+
&:before {
|
|
20
|
+
content: counter(line);
|
|
21
|
+
text-align: left;
|
|
22
|
+
width: max-content;
|
|
23
|
+
color: var(--kona-editor-text-color, #444);
|
|
24
|
+
opacity: .65;
|
|
25
|
+
padding-left: 16px;
|
|
26
|
+
display: inline-block;
|
|
27
|
+
position: absolute;
|
|
28
|
+
left: 0;
|
|
29
|
+
}
|
|
11
30
|
}
|
|
12
31
|
|
package/package.json
CHANGED
package/src/core/deserialize.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Descendant, Element, type Node } from 'slate';
|
|
2
2
|
import { jsx } from 'slate-hyperscript';
|
|
3
|
+
import { CustomElement, CustomText } from '../../types';
|
|
3
4
|
import type { IPlugin } from '../types';
|
|
4
5
|
|
|
5
6
|
export const deserialize =
|
|
@@ -35,13 +36,16 @@ export const deserialize =
|
|
|
35
36
|
return jsx('element', { type: 'paragraph' }, children);
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
let result:
|
|
39
|
+
let result: CustomElement | CustomText[] | null = null;
|
|
39
40
|
for (const plugin of plugins) {
|
|
40
41
|
if (plugin.blocks?.some((b) => b.deserialize)) {
|
|
41
42
|
plugin.blocks.forEach((e) => {
|
|
42
43
|
if (e.deserialize) {
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
const childrenAsDescendants = (result || children) as (
|
|
45
|
+
| string
|
|
46
|
+
| Descendant
|
|
47
|
+
)[];
|
|
48
|
+
|
|
45
49
|
const newResult = e.deserialize(element, childrenAsDescendants);
|
|
46
50
|
if (newResult) {
|
|
47
51
|
result = newResult;
|
|
@@ -49,9 +53,26 @@ export const deserialize =
|
|
|
49
53
|
}
|
|
50
54
|
});
|
|
51
55
|
}
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
|
|
57
|
+
if (plugin.leafs?.some((b) => b.deserialize)) {
|
|
58
|
+
plugin.leafs.forEach((e) => {
|
|
59
|
+
if (e.deserialize) {
|
|
60
|
+
const childrenAsDescendants = (result || children) as (
|
|
61
|
+
| string
|
|
62
|
+
| Descendant
|
|
63
|
+
)[];
|
|
64
|
+
const newResult = e.deserialize(element, childrenAsDescendants);
|
|
65
|
+
if (newResult) {
|
|
66
|
+
result = newResult;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
54
70
|
}
|
|
55
71
|
}
|
|
72
|
+
|
|
73
|
+
if (result) {
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
|
|
56
77
|
return children.filter((child) => child !== null);
|
|
57
78
|
};
|
package/src/examples/Editor.tsx
CHANGED
|
@@ -1,22 +1,52 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
1
2
|
import { DndProvider } from 'react-dnd';
|
|
2
3
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
|
4
|
+
import type { Descendant } from 'slate';
|
|
3
5
|
import type { CustomElement } from '../../types';
|
|
6
|
+
import { deserialize } from '../core/deserialize';
|
|
4
7
|
import { KonaEditor } from '../editor';
|
|
8
|
+
import type { EditorRef } from '../types';
|
|
5
9
|
import styles from './Editor.module.css';
|
|
6
10
|
import { getPlugins } from './getPlugins';
|
|
7
11
|
import { text } from './text';
|
|
8
12
|
|
|
9
13
|
const initialValue = text;
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
type Props = {
|
|
16
|
+
initialValueType?: 'kona-editor' | 'html';
|
|
17
|
+
value?: any;
|
|
18
|
+
onChange?: (value: Descendant[]) => void;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const ExampleEditor = (props: Props) => {
|
|
22
|
+
const { initialValueType = 'kona-editor' } = props;
|
|
23
|
+
const [plugins] = useState(getPlugins());
|
|
24
|
+
const [value, setValue] = useState<Descendant[] | null>(null);
|
|
25
|
+
|
|
26
|
+
const ref = useRef<EditorRef>(null);
|
|
27
|
+
|
|
28
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: only on init
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (initialValueType === 'kona-editor') {
|
|
31
|
+
setValue(props.value);
|
|
32
|
+
} else {
|
|
33
|
+
const parsed = deserialize(plugins)(props.value);
|
|
34
|
+
parsed && setValue(parsed as Descendant[]);
|
|
35
|
+
console.log(parsed);
|
|
36
|
+
}
|
|
37
|
+
}, []);
|
|
38
|
+
|
|
12
39
|
return (
|
|
13
40
|
<DndProvider backend={HTML5Backend}>
|
|
14
41
|
<div className={[styles.root].join(' ')}>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
42
|
+
{value && (
|
|
43
|
+
<KonaEditor
|
|
44
|
+
ref={ref}
|
|
45
|
+
initialValue={value || (initialValue as CustomElement[])}
|
|
46
|
+
plugins={plugins}
|
|
47
|
+
onChange={props.onChange || console.log}
|
|
48
|
+
/>
|
|
49
|
+
)}
|
|
20
50
|
</div>
|
|
21
51
|
</DndProvider>
|
|
22
52
|
);
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Editor } from 'slate';
|
|
2
|
+
import { jsx } from 'slate-hyperscript';
|
|
2
3
|
import type { CustomElement, CustomText } from '../../../types';
|
|
3
4
|
import type { IPlugin } from '../../types';
|
|
4
5
|
|
|
@@ -63,6 +64,36 @@ export class BasicFormattingPlugin
|
|
|
63
64
|
|
|
64
65
|
return <span {...attributes}>{content}</span>;
|
|
65
66
|
},
|
|
67
|
+
deserialize: (element: HTMLElement, children) => {
|
|
68
|
+
const { nodeName } = element;
|
|
69
|
+
|
|
70
|
+
let attrs: Record<string, boolean> | null = null;
|
|
71
|
+
switch (nodeName) {
|
|
72
|
+
case 'EM':
|
|
73
|
+
case 'I': {
|
|
74
|
+
attrs = { italic: true };
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
case 'STRONG': {
|
|
78
|
+
attrs = { bold: true };
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case 'U': {
|
|
82
|
+
attrs = { underline: true };
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
case 'S': {
|
|
86
|
+
attrs = { strikethrough: true };
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (attrs) {
|
|
92
|
+
return children?.map((child) => jsx('text', attrs, child));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return undefined;
|
|
96
|
+
},
|
|
66
97
|
},
|
|
67
98
|
];
|
|
68
99
|
|
|
@@ -10,11 +10,13 @@ type Props = RenderElementProps & {
|
|
|
10
10
|
|
|
11
11
|
export const CodeBlock = (props: Props) => {
|
|
12
12
|
return (
|
|
13
|
-
<div {...props.attributes} className={styles.
|
|
13
|
+
<div {...props.attributes} className={styles.root} spellCheck={false}>
|
|
14
14
|
<div contentEditable={false}>
|
|
15
15
|
{props.renderLanguageSelector(props.element)}
|
|
16
16
|
</div>
|
|
17
|
-
<div className={styles.content}>
|
|
17
|
+
<div className={styles.content}>
|
|
18
|
+
<div className={styles.code}>{props.children}</div>
|
|
19
|
+
</div>
|
|
18
20
|
</div>
|
|
19
21
|
);
|
|
20
22
|
};
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { RenderElementProps } from 'slate-react';
|
|
1
|
+
import type { RenderElementProps } from 'slate-react';
|
|
2
|
+
import styles from './styles.module.css';
|
|
2
3
|
|
|
3
4
|
export const CodeBlockLine = (props: RenderElementProps) => (
|
|
4
|
-
<div
|
|
5
|
+
<div
|
|
6
|
+
{...props.attributes}
|
|
7
|
+
style={{ position: 'relative' }}
|
|
8
|
+
className={styles.line}
|
|
9
|
+
>
|
|
5
10
|
{props.children}
|
|
6
11
|
</div>
|
|
7
12
|
);
|
|
@@ -1,11 +1,30 @@
|
|
|
1
|
-
.
|
|
1
|
+
.root {
|
|
2
2
|
border: 1px solid var(--kona-editor-border-color, #eee);
|
|
3
3
|
border-radius: 4px;
|
|
4
4
|
overflow: hidden;
|
|
5
5
|
font-family: monospace;
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
6
8
|
}
|
|
7
9
|
|
|
8
10
|
.content {
|
|
9
|
-
|
|
11
|
+
display: flex;
|
|
10
12
|
background-color: var(--kona-editor-background-color, #fff);
|
|
11
13
|
}
|
|
14
|
+
|
|
15
|
+
.line {
|
|
16
|
+
counter-increment: line;
|
|
17
|
+
padding-left: 50px;
|
|
18
|
+
|
|
19
|
+
&::before {
|
|
20
|
+
left: 0;
|
|
21
|
+
position: absolute;
|
|
22
|
+
content: counter(line);
|
|
23
|
+
display: inline-block;
|
|
24
|
+
width: max-content;
|
|
25
|
+
text-align: left;
|
|
26
|
+
padding-left: 16px;
|
|
27
|
+
color: var(--kona-editor-text-color, #444);
|
|
28
|
+
opacity: 0.65;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Editor, Element, Transforms } from 'slate';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { jsx } from 'slate-hyperscript';
|
|
3
|
+
import type { RenderElementProps } from 'slate-react';
|
|
4
|
+
import type { IPlugin } from '../../types';
|
|
4
5
|
|
|
5
6
|
export class HeadingsPlugin implements IPlugin {
|
|
6
7
|
static HeadingLevel1 = 'h1';
|
|
@@ -13,18 +14,51 @@ export class HeadingsPlugin implements IPlugin {
|
|
|
13
14
|
render: (props: RenderElementProps) => {
|
|
14
15
|
return <h1 {...props.attributes}>{props.children}</h1>;
|
|
15
16
|
},
|
|
17
|
+
deserialize: (element: HTMLElement, children) => {
|
|
18
|
+
const { nodeName } = element;
|
|
19
|
+
|
|
20
|
+
if (nodeName === 'H1') {
|
|
21
|
+
return jsx(
|
|
22
|
+
'element',
|
|
23
|
+
{ type: HeadingsPlugin.HeadingLevel1 },
|
|
24
|
+
children,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
16
28
|
},
|
|
17
29
|
{
|
|
18
30
|
type: HeadingsPlugin.HeadingLevel2,
|
|
19
31
|
render: (props: RenderElementProps) => {
|
|
20
32
|
return <h2 {...props.attributes}>{props.children}</h2>;
|
|
21
33
|
},
|
|
34
|
+
deserialize: (element: HTMLElement, children) => {
|
|
35
|
+
const { nodeName } = element;
|
|
36
|
+
|
|
37
|
+
if (nodeName === 'H2') {
|
|
38
|
+
return jsx(
|
|
39
|
+
'element',
|
|
40
|
+
{ type: HeadingsPlugin.HeadingLevel2 },
|
|
41
|
+
children,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
},
|
|
22
45
|
},
|
|
23
46
|
{
|
|
24
47
|
type: HeadingsPlugin.HeadingLevel3,
|
|
25
48
|
render: (props: RenderElementProps) => {
|
|
26
49
|
return <h3 {...props.attributes}>{props.children}</h3>;
|
|
27
50
|
},
|
|
51
|
+
deserialize: (element: HTMLElement, children) => {
|
|
52
|
+
const { nodeName } = element;
|
|
53
|
+
|
|
54
|
+
if (nodeName === 'H3') {
|
|
55
|
+
return jsx(
|
|
56
|
+
'element',
|
|
57
|
+
{ type: HeadingsPlugin.HeadingLevel3 },
|
|
58
|
+
children,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
28
62
|
},
|
|
29
63
|
];
|
|
30
64
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import isUrl from 'is-url';
|
|
2
2
|
import { Editor, Element, Range, Transforms } from 'slate';
|
|
3
|
+
import { jsx } from 'slate-hyperscript';
|
|
3
4
|
import type { RenderElementProps } from 'slate-react';
|
|
5
|
+
import { deserialize } from '../../core/deserialize';
|
|
4
6
|
import type { IPlugin } from '../../types';
|
|
5
7
|
import { LINK_ELEMENT } from './constants';
|
|
6
8
|
import { Link } from './Link';
|
|
@@ -54,6 +56,19 @@ export class LinksPlugin implements IPlugin {
|
|
|
54
56
|
/>
|
|
55
57
|
);
|
|
56
58
|
},
|
|
59
|
+
deserialize: (element: HTMLElement, children) => {
|
|
60
|
+
if (element.tagName === 'A') {
|
|
61
|
+
const url = element.getAttribute('href') || '';
|
|
62
|
+
return jsx(
|
|
63
|
+
'element',
|
|
64
|
+
{
|
|
65
|
+
type: LinksPlugin.LINK_TYPE,
|
|
66
|
+
url,
|
|
67
|
+
},
|
|
68
|
+
children,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
},
|
|
57
72
|
},
|
|
58
73
|
];
|
|
59
74
|
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type Path,
|
|
9
9
|
Transforms,
|
|
10
10
|
} from 'slate';
|
|
11
|
+
import { jsx } from 'slate-hyperscript';
|
|
11
12
|
import type { RenderElementProps } from 'slate-react';
|
|
12
13
|
import type { CustomElement } from '../../../types';
|
|
13
14
|
import { getPrev } from '../../core/queries';
|
|
@@ -133,6 +134,16 @@ export class ListsPlugin implements IPlugin {
|
|
|
133
134
|
</ul>
|
|
134
135
|
);
|
|
135
136
|
},
|
|
137
|
+
deserialize: (element: HTMLElement, children) => {
|
|
138
|
+
const { nodeName } = element;
|
|
139
|
+
if (nodeName === 'UL') {
|
|
140
|
+
return jsx(
|
|
141
|
+
'element',
|
|
142
|
+
{ type: ListsPlugin.BULLETED_LIST_ELEMENT },
|
|
143
|
+
children,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
},
|
|
136
147
|
},
|
|
137
148
|
{
|
|
138
149
|
type: ListsPlugin.NUMBERED_LIST_ELEMENT,
|
|
@@ -143,6 +154,16 @@ export class ListsPlugin implements IPlugin {
|
|
|
143
154
|
</ol>
|
|
144
155
|
);
|
|
145
156
|
},
|
|
157
|
+
deserialize: (element: HTMLElement, children) => {
|
|
158
|
+
const { nodeName } = element;
|
|
159
|
+
if (nodeName === 'OL') {
|
|
160
|
+
return jsx(
|
|
161
|
+
'element',
|
|
162
|
+
{ type: ListsPlugin.NUMBERED_LIST_ELEMENT },
|
|
163
|
+
children,
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
},
|
|
146
167
|
},
|
|
147
168
|
{
|
|
148
169
|
type: ListsPlugin.LIST_ITEM_ELEMENT,
|
|
@@ -153,6 +174,16 @@ export class ListsPlugin implements IPlugin {
|
|
|
153
174
|
</li>
|
|
154
175
|
);
|
|
155
176
|
},
|
|
177
|
+
deserialize: (element: HTMLElement, children) => {
|
|
178
|
+
const { nodeName } = element;
|
|
179
|
+
if (nodeName === 'LI') {
|
|
180
|
+
return jsx(
|
|
181
|
+
'element',
|
|
182
|
+
{ type: ListsPlugin.LIST_ITEM_ELEMENT },
|
|
183
|
+
children,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
},
|
|
156
187
|
},
|
|
157
188
|
];
|
|
158
189
|
|
package/src/types.ts
CHANGED
|
@@ -60,6 +60,7 @@ export type Leaf<T extends Editor, TLeaf extends CustomText = CustomText> = {
|
|
|
60
60
|
editor: T,
|
|
61
61
|
) => ReactElement | null;
|
|
62
62
|
isVoid?: boolean;
|
|
63
|
+
deserialize?: Deserialize;
|
|
63
64
|
};
|
|
64
65
|
|
|
65
66
|
type Hotkey = readonly [string, (event: KeyboardEvent, editor: Editor) => void];
|
|
@@ -84,4 +85,4 @@ export type Serialize = (node: Node, children?: string) => string | undefined;
|
|
|
84
85
|
export type Deserialize = (
|
|
85
86
|
element: HTMLElement,
|
|
86
87
|
children?: (string | Descendant)[],
|
|
87
|
-
) =>
|
|
88
|
+
) => CustomElement | CustomText[] | undefined;
|