@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.
@@ -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.code,
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: props.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,6 +1,7 @@
1
1
  import "./styles_module.css";
2
2
  const styles_module = {
3
- code: "code-j4FgXP",
4
- content: "content-HIkMJO"
3
+ root: "root-aY1XCj",
4
+ content: "content-HIkMJO",
5
+ line: "line-dwbtZS"
5
6
  };
6
7
  export { styles_module as default };
@@ -1,12 +1,31 @@
1
- .code-j4FgXP {
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
- padding: 8px;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@use-kona/editor",
3
- "version": "0.1.0-rc.5",
3
+ "version": "0.1.2-rc.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/index.ts"
@@ -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: Node | null = null;
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
- // Cast children to match the expected type in Deserialize interface
44
- const childrenAsDescendants = children as (string | Descendant)[];
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
- if (result) {
53
- return result;
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
  };
@@ -3,7 +3,6 @@
3
3
  height: calc(100vh - 128px * 2);
4
4
  overflow: hidden;
5
5
  max-height: 100vh;
6
- margin: 32px auto;
7
6
  border: 1px solid var(--kona-editor-border-color);
8
7
  border-radius: 8px;
9
8
  }
@@ -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
- export const ExampleEditor = () => {
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
- <KonaEditor
16
- initialValue={initialValue as CustomElement[]}
17
- plugins={getPlugins()}
18
- onChange={console.log}
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,3 +1,5 @@
1
+ export { deserialize } from './core/deserialize';
2
+ export { serialize } from './core/serialize';
1
3
  export { defaultValue } from './defaultValue';
2
4
  export * from './editor';
3
5
  export { ExampleEditor } from './examples/Editor';
@@ -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.code} spellCheck={false}>
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}>{props.children}</div>
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 {...props.attributes} style={{ position: 'relative' }}>
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
- .code {
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
- padding: 8px;
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 { RenderElementProps } from 'slate-react';
3
- import { IPlugin } from '../../types';
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
- ) => Node | undefined;
88
+ ) => CustomElement | CustomText[] | undefined;