@use-kona/editor 0.1.2 → 0.1.3

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.
Files changed (57) hide show
  1. package/README.md +28 -17
  2. package/dist/examples/Emoji.d.ts +5 -0
  3. package/dist/examples/Emoji.js +8 -0
  4. package/dist/examples/EmojiSelector.d.ts +16 -0
  5. package/dist/examples/EmojiSelector.js +91 -0
  6. package/dist/examples/EmojiSelector.module.js +7 -0
  7. package/dist/examples/EmojiSelector_module.css +36 -0
  8. package/dist/examples/FloatingMenu.d.ts +2 -0
  9. package/dist/examples/Menu.js +16 -1
  10. package/dist/examples/getCommands.js +14 -1
  11. package/dist/examples/getPlugins.d.ts +2 -2
  12. package/dist/examples/getPlugins.js +18 -1
  13. package/dist/examples/icons/code.d.ts +2 -0
  14. package/dist/examples/icons/code.js +30 -0
  15. package/dist/examples/icons/drag.d.ts +1 -1
  16. package/dist/examples/store.d.ts +1 -0
  17. package/dist/examples/store.js +2 -1
  18. package/dist/examples/text.js +7 -2
  19. package/dist/plugins/CodeBlockPlugin/CodeBlockPlugin.js +38 -37
  20. package/dist/plugins/EmojiPlugin/EmojiPlugin.d.ts +28 -0
  21. package/dist/plugins/EmojiPlugin/EmojiPlugin.js +83 -0
  22. package/dist/plugins/EmojiPlugin/Menu.d.ts +7 -0
  23. package/dist/plugins/EmojiPlugin/Menu.js +51 -0
  24. package/dist/plugins/EmojiPlugin/index.d.ts +1 -0
  25. package/dist/plugins/EmojiPlugin/index.js +2 -0
  26. package/dist/plugins/EmojiPlugin/styles.module.js +7 -0
  27. package/dist/plugins/EmojiPlugin/styles_module.css +11 -0
  28. package/dist/plugins/EmojiPlugin/types.d.ts +7 -0
  29. package/dist/plugins/EmojiPlugin/types.js +0 -0
  30. package/dist/plugins/ListsPlugin/styles_module.css +4 -0
  31. package/dist/plugins/PlaceholderPlugin/PlaceholderPlugin.js +6 -3
  32. package/dist/plugins/PlaceholderPlugin/styles.module.js +1 -0
  33. package/dist/plugins/PlaceholderPlugin/styles_module.css +4 -0
  34. package/dist/plugins/index.d.ts +1 -0
  35. package/dist/plugins/index.js +2 -1
  36. package/package.json +4 -1
  37. package/src/examples/Emoji.tsx +9 -0
  38. package/src/examples/EmojiSelector.module.css +35 -0
  39. package/src/examples/EmojiSelector.tsx +115 -0
  40. package/src/examples/FloatingMenu.tsx +9 -5
  41. package/src/examples/Menu.tsx +15 -1
  42. package/src/examples/getCommands.tsx +17 -1
  43. package/src/examples/getPlugins.tsx +21 -0
  44. package/src/examples/icons/code.tsx +21 -0
  45. package/src/examples/icons/drag.tsx +1 -1
  46. package/src/examples/store.ts +2 -0
  47. package/src/examples/text.tsx +5 -0
  48. package/src/plugins/CodeBlockPlugin/CodeBlockPlugin.tsx +51 -48
  49. package/src/plugins/EmojiPlugin/EmojiPlugin.tsx +116 -0
  50. package/src/plugins/EmojiPlugin/Menu.tsx +74 -0
  51. package/src/plugins/EmojiPlugin/index.ts +1 -0
  52. package/src/plugins/EmojiPlugin/styles.module.css +10 -0
  53. package/src/plugins/EmojiPlugin/types.ts +5 -0
  54. package/src/plugins/ListsPlugin/styles.module.css +4 -0
  55. package/src/plugins/PlaceholderPlugin/PlaceholderPlugin.tsx +13 -3
  56. package/src/plugins/PlaceholderPlugin/styles.module.css +2 -2
  57. package/src/plugins/index.ts +1 -0
package/README.md CHANGED
@@ -1,23 +1,34 @@
1
- # Rslib project
1
+ # Kona Editor
2
2
 
3
- ## Setup
4
-
5
- Install the dependencies:
6
-
7
- ```bash
8
- pnpm install
9
- ```
10
-
11
- ## Get started
12
-
13
- Build the library:
3
+ ## Installing
14
4
 
15
5
  ```bash
16
- pnpm build
6
+ npm install @use-kona/editor
17
7
  ```
18
8
 
19
- Build the library in watch mode:
20
-
21
- ```bash
22
- pnpm dev
9
+ ## Using
10
+
11
+ ```tsx
12
+ import {
13
+ KonaEditor,
14
+ BasicFormattingPlugin,
15
+ /* rest of the plugins */
16
+ } from "@use-kona/editor";
17
+
18
+ const defaultValue = [
19
+ { type: 'paragraph', children: [{ text: '' }]}
20
+ ];
21
+
22
+ export const Editor = () => {
23
+ return (
24
+ <KonaEditor
25
+ plugins={[
26
+ new BasicFormattingPlugin(),
27
+ // rest of the plugins
28
+ ]}
29
+ initialValue={defaultValue}
30
+ onChange={console.log}
31
+ />
32
+ )
33
+ }
23
34
  ```
@@ -0,0 +1,5 @@
1
+ type Props = {
2
+ id: string;
3
+ };
4
+ export declare const Emoji: (props: Props) => import("react/jsx-runtime").JSX.Element;
5
+ export {};
@@ -0,0 +1,8 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import EmojiSelector_module from "./EmojiSelector.module.js";
3
+ const Emoji = (props)=>/*#__PURE__*/ jsx("em-emoji", {
4
+ class: EmojiSelector_module.emoji,
5
+ id: props.id,
6
+ size: "16px"
7
+ });
8
+ export { Emoji };
@@ -0,0 +1,16 @@
1
+ declare module 'react/jsx-runtime' {
2
+ namespace JSX {
3
+ interface IntrinsicElements {
4
+ 'em-emoji': {
5
+ id: string;
6
+ size: string | number;
7
+ class?: string;
8
+ };
9
+ }
10
+ }
11
+ }
12
+ type Props = {
13
+ onConfirm: (emoji: any, query: any, editor: any) => void;
14
+ };
15
+ export declare const EmojiSelector: (props: Props) => import("react/jsx-runtime").JSX.Element | null;
16
+ export {};
@@ -0,0 +1,91 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import data from "@emoji-mart/data";
3
+ import { useStore } from "@nanostores/react";
4
+ import clsx from "clsx";
5
+ import { SearchIndex, init } from "emoji-mart";
6
+ import { useEffect, useState } from "react";
7
+ import { useSlateStatic } from "slate-react";
8
+ import EmojiSelector_module from "./EmojiSelector.module.js";
9
+ import { $store } from "./store.js";
10
+ init({
11
+ data: data,
12
+ set: 'apple',
13
+ custom: [
14
+ {
15
+ id: 'custom',
16
+ name: 'Custom',
17
+ emojis: [
18
+ {
19
+ id: 'kona',
20
+ name: 'Kona',
21
+ keywords: [
22
+ 'kona'
23
+ ],
24
+ skins: [
25
+ {
26
+ src: '/kona.svg'
27
+ }
28
+ ]
29
+ }
30
+ ]
31
+ }
32
+ ]
33
+ });
34
+ const EmojiSelector = (props)=>{
35
+ const { onConfirm } = props;
36
+ const editor = useSlateStatic();
37
+ const { emojiSearch } = useStore($store);
38
+ const [index, setIndex] = useState(-1);
39
+ const [items, setItems] = useState([]);
40
+ useEffect(()=>{
41
+ const search = async ()=>{
42
+ const emojis = await SearchIndex.search(emojiSearch);
43
+ setItems(emojis.slice(0, 10));
44
+ setIndex(0);
45
+ };
46
+ search();
47
+ }, [
48
+ emojiSearch
49
+ ]);
50
+ useEffect(()=>{
51
+ const handleKeyDown = (event)=>{
52
+ switch(event.key){
53
+ case 'ArrowRight':
54
+ event.preventDefault();
55
+ setIndex((index)=>(index + 1) % items.length);
56
+ break;
57
+ case 'ArrowLeft':
58
+ event.preventDefault();
59
+ setIndex((index)=>(index - 1 + items.length) % items.length);
60
+ break;
61
+ case 'Enter':
62
+ event.preventDefault();
63
+ onConfirm(items[index]?.id, emojiSearch, editor);
64
+ break;
65
+ }
66
+ };
67
+ document.addEventListener('keydown', handleKeyDown, true);
68
+ return ()=>{
69
+ document.removeEventListener('keydown', handleKeyDown, true);
70
+ };
71
+ }, [
72
+ items,
73
+ index,
74
+ emojiSearch
75
+ ]);
76
+ if (!items.length) return null;
77
+ return /*#__PURE__*/ jsx("div", {
78
+ className: EmojiSelector_module.root,
79
+ children: items.map((item, idx)=>/*#__PURE__*/ jsx("span", {
80
+ className: clsx(EmojiSelector_module.emoji, {
81
+ [EmojiSelector_module.selected]: idx === index
82
+ }),
83
+ children: /*#__PURE__*/ jsx("em-emoji", {
84
+ class: EmojiSelector_module.emoji,
85
+ id: item.id,
86
+ size: "20px"
87
+ })
88
+ }, item.id))
89
+ });
90
+ };
91
+ export { EmojiSelector };
@@ -0,0 +1,7 @@
1
+ import "./EmojiSelector_module.css";
2
+ const EmojiSelector_module = {
3
+ root: "root-f7KjwY",
4
+ emoji: "emoji-kNlwVt",
5
+ selected: "selected-ONnfU2"
6
+ };
7
+ export { EmojiSelector_module as default };
@@ -0,0 +1,36 @@
1
+ .root-f7KjwY {
2
+ background-color: var(--kona-editor-background-color);
3
+ border: 1px solid var(--kona-editor-border-color);
4
+ border-radius: 8px;
5
+ gap: 4px;
6
+ padding: 4px;
7
+ display: flex;
8
+ }
9
+
10
+ .emoji-kNlwVt {
11
+ cursor: pointer;
12
+ vertical-align: text-bottom;
13
+ border-radius: 4px;
14
+ justify-content: center;
15
+ align-items: center;
16
+ width: 24px;
17
+ height: 24px;
18
+ display: inline-flex;
19
+
20
+ & span {
21
+ justify-content: center;
22
+ align-items: center;
23
+ width: 20px;
24
+ height: 20px;
25
+ display: inline-flex;
26
+ }
27
+
28
+ &:hover {
29
+ background-color: var(--kona-editor-alt-background-color);
30
+ }
31
+ }
32
+
33
+ .selected-ONnfU2 {
34
+ background-color: var(--kona-editor-alt-background-color);
35
+ }
36
+
@@ -6,6 +6,8 @@ type Props = {
6
6
  isFloatingMenuOpen: boolean;
7
7
  floatingMenuMode: Mode;
8
8
  url?: string;
9
+ } & {
10
+ [key: string]: unknown;
9
11
  }>;
10
12
  editor: Editor;
11
13
  commands: {
@@ -1,5 +1,6 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { HeadingsPlugin, ListsPlugin } from "../plugins/index.js";
2
+ import { CodeBlockPlugin, HeadingsPlugin, ListsPlugin } from "../plugins/index.js";
3
+ import { CodeIcon } from "./icons/code.js";
3
4
  import { Heading1Icon } from "./icons/heading1.js";
4
5
  import { Heading2Icon } from "./icons/heading2.js";
5
6
  import { Heading3Icon } from "./icons/heading3.js";
@@ -68,6 +69,20 @@ const Menu = (props)=>{
68
69
  children: /*#__PURE__*/ jsx(OlIcon, {
69
70
  size: 16
70
71
  })
72
+ }),
73
+ /*#__PURE__*/ jsx("span", {
74
+ className: Menu_module.divider
75
+ }),
76
+ /*#__PURE__*/ jsx("button", {
77
+ type: "button",
78
+ className: getButtonClassName(CodeBlockPlugin.isCodeBlockActive(editor)),
79
+ onMouseDown: (event)=>{
80
+ event.preventDefault();
81
+ CodeBlockPlugin.toggleCodeBlock(editor);
82
+ },
83
+ children: /*#__PURE__*/ jsx(CodeIcon, {
84
+ size: 16
85
+ })
71
86
  })
72
87
  ]
73
88
  });
@@ -1,5 +1,6 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- import { HeadingsPlugin, ListsPlugin } from "../plugins/index.js";
2
+ import { CodeBlockPlugin, HeadingsPlugin, ListsPlugin } from "../plugins/index.js";
3
+ import { CodeIcon } from "./icons/code.js";
3
4
  import { Heading1Icon } from "./icons/heading1.js";
4
5
  import { Heading2Icon } from "./icons/heading2.js";
5
6
  import { Heading3Icon } from "./icons/heading3.js";
@@ -82,6 +83,18 @@ const getCommands = ()=>[
82
83
  actions.removeCommand();
83
84
  ListsPlugin.toggleList(editor, ListsPlugin.NUMBERED_LIST_ELEMENT, ListsPlugin.LIST_ITEM_ELEMENT);
84
85
  }
86
+ },
87
+ {
88
+ name: 'code',
89
+ title: 'Code',
90
+ commandName: 'code',
91
+ icon: /*#__PURE__*/ jsx(CodeIcon, {
92
+ size: 16
93
+ }),
94
+ action: (actions, editor)=>{
95
+ actions.removeCommand();
96
+ CodeBlockPlugin.toggleCodeBlock(editor);
97
+ }
85
98
  }
86
99
  ];
87
100
  export { getCommands };
@@ -1,2 +1,2 @@
1
- import { BasicFormattingPlugin, BreaksPlugin, CodeBlockPlugin, CommandsPlugin, DnDPlugin, FloatingMenuPlugin, HeadingsPlugin, HighlightsPlugin, LinksPlugin, ListsPlugin, MenuPlugin, NodeIdPlugin, PlaceholderPlugin, ShortcutsPlugin, TableOfContentsPlugin } from '../plugins';
2
- export declare const getPlugins: () => (BasicFormattingPlugin | BreaksPlugin | CodeBlockPlugin | CommandsPlugin | DnDPlugin | FloatingMenuPlugin | HeadingsPlugin | HighlightsPlugin | LinksPlugin | ListsPlugin | MenuPlugin | NodeIdPlugin | PlaceholderPlugin | ShortcutsPlugin | TableOfContentsPlugin)[];
1
+ import { BasicFormattingPlugin, BreaksPlugin, CodeBlockPlugin, CommandsPlugin, DnDPlugin, EmojiPlugin, FloatingMenuPlugin, HeadingsPlugin, HighlightsPlugin, LinksPlugin, ListsPlugin, MenuPlugin, NodeIdPlugin, PlaceholderPlugin, ShortcutsPlugin, TableOfContentsPlugin } from '../plugins';
2
+ export declare const getPlugins: () => (BasicFormattingPlugin | BreaksPlugin | CodeBlockPlugin | CommandsPlugin | DnDPlugin | EmojiPlugin | FloatingMenuPlugin | HeadingsPlugin | HighlightsPlugin | LinksPlugin | ListsPlugin | MenuPlugin | NodeIdPlugin | PlaceholderPlugin | ShortcutsPlugin | TableOfContentsPlugin)[];
@@ -1,9 +1,11 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- import { BasicFormattingPlugin, BreaksPlugin, CodeBlockPlugin, CommandsPlugin, DnDPlugin, FloatingMenuPlugin, HeadingsPlugin, HighlightsPlugin, LinksPlugin, ListsPlugin, MenuPlugin, NodeIdPlugin, PlaceholderPlugin, ShortcutsPlugin, TableOfContentsPlugin } from "../plugins/index.js";
2
+ import { BasicFormattingPlugin, BreaksPlugin, CodeBlockPlugin, CommandsPlugin, DnDPlugin, EmojiPlugin, FloatingMenuPlugin, HeadingsPlugin, HighlightsPlugin, LinksPlugin, ListsPlugin, MenuPlugin, NodeIdPlugin, PlaceholderPlugin, ShortcutsPlugin, TableOfContentsPlugin } from "../plugins/index.js";
3
3
  import { Backdrop } from "./Backdrop.js";
4
4
  import { CodeBlock } from "./CodeBlock.js";
5
5
  import { colors } from "./colors.js";
6
6
  import { DragBlock } from "./DragBlock.js";
7
+ import { Emoji } from "./Emoji.js";
8
+ import { EmojiSelector } from "./EmojiSelector.js";
7
9
  import { FloatingMenu } from "./FloatingMenu.js";
8
10
  import { getCommands } from "./getCommands.js";
9
11
  import { getShortcuts } from "./getShortcuts.js";
@@ -44,6 +46,21 @@ const getPlugins = ()=>{
44
46
  });
45
47
  return [
46
48
  new BasicFormattingPlugin(),
49
+ new EmojiPlugin({
50
+ ignoreNodes: [
51
+ CodeBlockPlugin.CODE_LINE_ELEMENT
52
+ ],
53
+ onSearch: (query)=>$store.setKey('emojiSearch', query),
54
+ renderMenu: ({ insertEmoji })=>/*#__PURE__*/ jsx(EmojiSelector, {
55
+ onConfirm: (...params)=>{
56
+ insertEmoji(...params);
57
+ $store.setKey('emojiSearch', '');
58
+ }
59
+ }),
60
+ renderEmoji: (emoji)=>/*#__PURE__*/ jsx(Emoji, {
61
+ id: emoji
62
+ })
63
+ }),
47
64
  new PlaceholderPlugin({
48
65
  focused: 'Write / to open command menu',
49
66
  unfocused: 'Write / to open command menu',
@@ -0,0 +1,2 @@
1
+ import type { IconProps } from './types';
2
+ export declare const CodeIcon: ({ size }: IconProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,30 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ const CodeIcon = ({ size })=>/*#__PURE__*/ jsxs("svg", {
3
+ xmlns: "http://www.w3.org/2000/svg",
4
+ width: size,
5
+ height: size,
6
+ viewBox: "0 0 24 24",
7
+ fill: "none",
8
+ stroke: "currentColor",
9
+ strokeWidth: 1.5,
10
+ strokeLinecap: "round",
11
+ strokeLinejoin: "round",
12
+ className: "icon icon-tabler icons-tabler-outline icon-tabler-code",
13
+ children: [
14
+ /*#__PURE__*/ jsx("path", {
15
+ stroke: "none",
16
+ d: "M0 0h24v24H0z",
17
+ fill: "none"
18
+ }),
19
+ /*#__PURE__*/ jsx("path", {
20
+ d: "M7 8l-4 4l4 4"
21
+ }),
22
+ /*#__PURE__*/ jsx("path", {
23
+ d: "M17 8l4 4l-4 4"
24
+ }),
25
+ /*#__PURE__*/ jsx("path", {
26
+ d: "M14 4l-4 16"
27
+ })
28
+ ]
29
+ });
30
+ export { CodeIcon };
@@ -1,2 +1,2 @@
1
- import { IconProps } from './types';
1
+ import type { IconProps } from './types';
2
2
  export declare const DragIcon: ({ size }: IconProps) => import("react/jsx-runtime").JSX.Element;
@@ -3,6 +3,7 @@ type Store = {
3
3
  isFloatingMenuOpen: boolean;
4
4
  floatingMenuMode: FloatingMenuMode;
5
5
  url?: string;
6
+ emojiSearch: string;
6
7
  };
7
8
  export declare const $store: import("nanostores").PreinitializedMapStore<Store> & object;
8
9
  export {};
@@ -1,6 +1,7 @@
1
1
  import { map } from "nanostores";
2
2
  const $store = map({
3
3
  isFloatingMenuOpen: false,
4
- floatingMenuMode: 'main'
4
+ floatingMenuMode: 'main',
5
+ emojiSearch: ''
5
6
  });
6
7
  export { $store };
@@ -30,6 +30,9 @@ const text_elements = {
30
30
  },
31
31
  hlink: {
32
32
  type: LinksPlugin.LINK_TYPE
33
+ },
34
+ hemoji: {
35
+ type: 'emoji'
33
36
  }
34
37
  };
35
38
  const creators = {
@@ -42,7 +45,9 @@ const jsx = createHyperscript({
42
45
  const text_text = /*#__PURE__*/ jsx("fragment", null, /*#__PURE__*/ jsx("heading1", null, "About"), /*#__PURE__*/ jsx("paragraph", null, /*#__PURE__*/ jsx("htext", {
43
46
  bold: true,
44
47
  italic: true
45
- }, "Kona Editor"), ' ', "is a text editor based on Slate.js that I use in", ' ', /*#__PURE__*/ jsx("hlink", {
48
+ }, "Kona Editor"), ' ', "is a text editor based on Slate.js that I use in", ' ', /*#__PURE__*/ jsx("hemoji", {
49
+ emoji: "kona"
50
+ }, "'"), /*#__PURE__*/ jsx("hlink", {
46
51
  url: "https://kona.to"
47
52
  }, "Kona calendar"), " for notes and event descriptions. I decided to open-source the editor for a few reasons:"), /*#__PURE__*/ jsx("numberedList", null, /*#__PURE__*/ jsx("listItem", null, "I had a lot of ", /*#__PURE__*/ jsx("htext", {
48
53
  strikethrough: true
@@ -56,5 +61,5 @@ const text_text = /*#__PURE__*/ jsx("fragment", null, /*#__PURE__*/ jsx("heading
56
61
  underline: true
57
62
  }, "underlined"), " and", ' ', /*#__PURE__*/ jsx("htext", {
58
63
  strikethrough: true
59
- }, "strikethrough"), " text"), /*#__PURE__*/ jsx("heading3", null, "BreaksPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Overrides default Slate behavior by breaking custom block types when user presses Enter."), /*#__PURE__*/ jsx("heading3", null, "CodeBlockPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Adds support for code blocks."), /*#__PURE__*/ jsx("heading3", null, "CommandsPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Adds Notion-style menu with custom list of commands"), /*#__PURE__*/ jsx("heading3", null, "DnDPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Allows reordering blocks by drag'n'dropping them"), /*#__PURE__*/ jsx("heading3", null, "FloatingMenuPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Adds menu which is shown when user selects a text"), /*#__PURE__*/ jsx("heading3", null, "HeadingsPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Adds three levels of headings"), /*#__PURE__*/ jsx("heading3", null, "HighlightsPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Allows highlighting selected text with a custom color"), /*#__PURE__*/ jsx("heading3", null, "LinksPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Gives user ability to convert the selected text into a link"), /*#__PURE__*/ jsx("heading3", null, "MenuPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Adds pinned to the top menu with custom commands"), /*#__PURE__*/ jsx("heading3", null, "NodeIdPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Assigns a unique id to each block"), /*#__PURE__*/ jsx("heading3", null, "PlaceholderPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Allows to setup a custom placeholder for the selected line"), /*#__PURE__*/ jsx("heading3", null, "ShortcutsPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Converts some popular markdown shortcuts to a custom node"), /*#__PURE__*/ jsx("heading3", null, "TableOfContentsPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Shows a quick map of headings inside the document"));
64
+ }, "strikethrough"), " text"), /*#__PURE__*/ jsx("heading3", null, "BreaksPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Overrides default Slate behavior by breaking custom block types when user presses Enter."), /*#__PURE__*/ jsx("heading3", null, "CodeBlockPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Adds support for code blocks."), /*#__PURE__*/ jsx("heading3", null, "CommandsPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Adds Notion-style menu with custom list of commands"), /*#__PURE__*/ jsx("heading3", null, "DnDPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Allows reordering blocks by drag'n'dropping them"), /*#__PURE__*/ jsx("heading3", null, "FloatingMenuPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Adds menu which is shown when user selects a text"), /*#__PURE__*/ jsx("heading3", null, "HeadingsPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Adds three levels of headings"), /*#__PURE__*/ jsx("heading3", null, "HighlightsPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Allows highlighting selected text with a custom color"), /*#__PURE__*/ jsx("heading3", null, "LinksPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Gives user ability to convert the selected text into a link"), /*#__PURE__*/ jsx("heading3", null, "MenuPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Adds pinned to the top menu with custom commands"), /*#__PURE__*/ jsx("heading3", null, "NodeIdPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Assigns a unique id to each block"), /*#__PURE__*/ jsx("heading3", null, "PlaceholderPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Allows to setup a custom placeholder for the selected line"), /*#__PURE__*/ jsx("heading3", null, "ShortcutsPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Converts some popular markdown shortcuts to a custom node"), /*#__PURE__*/ jsx("heading3", null, "TableOfContentsPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Shows a quick map of headings inside the document"), /*#__PURE__*/ jsx("heading3", null, "EmojiPlugin"), /*#__PURE__*/ jsx("paragraph", null, "Allows paste emoji to the text"));
60
65
  export { text_text as text };
@@ -185,44 +185,45 @@ class CodeBlockPlugin {
185
185
  return !!match;
186
186
  }
187
187
  static toggleCodeBlock = (editor)=>{
188
- Editor.withoutNormalizing(editor, ()=>{
189
- const node = Editor.above(editor, {
190
- match: (n)=>!Editor.isEditor(n) && n.type === CodeBlockPlugin.CODE_ELEMENT,
191
- mode: 'highest'
192
- });
193
- if (node) {
194
- Transforms.setNodes(editor, {
195
- type: 'paragraph'
196
- }, {
197
- at: node[1],
198
- match: (n)=>n.type === CodeBlockPlugin.CODE_LINE_ELEMENT,
199
- mode: 'lowest'
200
- });
201
- Transforms.unwrapNodes(editor, {
202
- at: node[1],
203
- match: (n)=>n.type === CodeBlockPlugin.CODE_ELEMENT,
204
- split: true
205
- });
206
- } else {
207
- Transforms.setNodes(editor, {
208
- type: CodeBlockPlugin.CODE_LINE_ELEMENT,
209
- children: [
210
- {
211
- text: ''
212
- }
213
- ]
214
- }, {
215
- match: (n)=>Editor.isBlock(editor, n) && 'paragraph' === n.type
216
- });
217
- Transforms.wrapNodes(editor, {
218
- type: CodeBlockPlugin.CODE_ELEMENT,
219
- language: "javascript",
220
- children: []
221
- }, {
222
- match: (n)=>n.type === CodeBlockPlugin.CODE_LINE_ELEMENT
223
- });
224
- }
188
+ if (!editor.selection) return;
189
+ const node = Editor.above(editor, {
190
+ match: (n)=>!Editor.isEditor(n) && n.type === CodeBlockPlugin.CODE_ELEMENT,
191
+ mode: 'highest'
225
192
  });
193
+ if (node) {
194
+ Transforms.setNodes(editor, {
195
+ type: 'paragraph'
196
+ }, {
197
+ at: node[1],
198
+ match: (n)=>n.type === CodeBlockPlugin.CODE_LINE_ELEMENT,
199
+ mode: 'lowest'
200
+ });
201
+ Transforms.unwrapNodes(editor, {
202
+ at: node[1],
203
+ match: (n)=>n.type === CodeBlockPlugin.CODE_ELEMENT,
204
+ split: true
205
+ });
206
+ } else {
207
+ Transforms.setNodes(editor, {
208
+ type: CodeBlockPlugin.CODE_LINE_ELEMENT,
209
+ children: [
210
+ {
211
+ text: ''
212
+ }
213
+ ]
214
+ }, {
215
+ mode: 'lowest'
216
+ });
217
+ Transforms.wrapNodes(editor, {
218
+ type: CodeBlockPlugin.CODE_ELEMENT,
219
+ language: "javascript",
220
+ children: []
221
+ }, {
222
+ mode: 'highest',
223
+ match: (n)=>n.type === CodeBlockPlugin.CODE_LINE_ELEMENT,
224
+ at: editor.selection.focus
225
+ });
226
+ }
226
227
  };
227
228
  }
228
229
  export { CodeBlockPlugin };
@@ -0,0 +1,28 @@
1
+ import { type MapStore } from 'nanostores';
2
+ import type { ReactNode } from 'react';
3
+ import { Editor } from 'slate';
4
+ import type { IPlugin } from '../../types';
5
+ type Options = {
6
+ onSearch: (query: string) => void;
7
+ renderMenu: (actions: {
8
+ insertEmoji: (emoji: string, query: string, editor: Editor) => void;
9
+ }) => ReactNode;
10
+ renderEmoji: (emoji: string) => ReactNode;
11
+ ignoreNodes?: string[];
12
+ };
13
+ export declare class EmojiPlugin implements IPlugin {
14
+ options: Options;
15
+ $store: MapStore<{
16
+ isOpen: boolean;
17
+ }>;
18
+ constructor(options: Options);
19
+ blocks: {
20
+ isInline: boolean;
21
+ isVoid: boolean;
22
+ type: string;
23
+ render: (props: any) => import("react/jsx-runtime").JSX.Element;
24
+ }[];
25
+ init(editor: any): any;
26
+ ui(): import("react/jsx-runtime").JSX.Element | null;
27
+ }
28
+ export {};
@@ -0,0 +1,83 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useStore } from "@nanostores/react";
3
+ import { map } from "nanostores";
4
+ import { Editor, Node, Transforms } from "slate";
5
+ import { Menu } from "./Menu.js";
6
+ import styles_module from "./styles.module.js";
7
+ class EmojiPlugin {
8
+ options;
9
+ $store;
10
+ constructor(options){
11
+ this.options = options;
12
+ this.$store = map({
13
+ isOpen: false
14
+ });
15
+ }
16
+ blocks = [
17
+ {
18
+ isInline: true,
19
+ isVoid: true,
20
+ type: 'emoji',
21
+ render: (props)=>{
22
+ const children = this.options.renderEmoji(props.element.emoji);
23
+ return /*#__PURE__*/ jsx("span", {
24
+ ...props.attributes,
25
+ className: styles_module.emojiRoot,
26
+ children: children
27
+ });
28
+ }
29
+ }
30
+ ];
31
+ init(editor) {
32
+ const { insertText, onChange } = editor;
33
+ editor.insertText = (text)=>{
34
+ if (':' === text) this.$store.setKey('isOpen', true);
35
+ insertText(text);
36
+ };
37
+ editor.onChange = (...args)=>{
38
+ if (this.$store.get().isOpen) {
39
+ const query = /:([^:]*$)/.exec(getCurrentText(editor));
40
+ if (query?.[0]) this.options.onSearch(query[1]);
41
+ else this.$store.setKey('isOpen', false);
42
+ }
43
+ return onChange(...args);
44
+ };
45
+ return editor;
46
+ }
47
+ ui() {
48
+ const { isOpen } = useStore(this.$store);
49
+ if (!isOpen) return null;
50
+ return /*#__PURE__*/ jsx(Menu, {
51
+ isOpen: isOpen,
52
+ children: this.options.renderMenu({
53
+ insertEmoji: (emoji, query, editor)=>{
54
+ this.$store.setKey('isOpen', false);
55
+ Transforms["delete"](editor, {
56
+ at: editor.selection?.focus,
57
+ distance: query.length + 1,
58
+ reverse: true,
59
+ unit: 'character'
60
+ });
61
+ Transforms.insertNodes(editor, {
62
+ type: 'emoji',
63
+ emoji,
64
+ children: [
65
+ {
66
+ text: ''
67
+ }
68
+ ]
69
+ });
70
+ Transforms.move(editor);
71
+ }
72
+ })
73
+ });
74
+ }
75
+ }
76
+ const getCurrentText = (editor)=>{
77
+ const entry = Editor.above(editor, {
78
+ match: (n)=>Editor.isBlock(editor, n)
79
+ });
80
+ if (entry) return Node.string(entry[0]);
81
+ return '';
82
+ };
83
+ export { EmojiPlugin };
@@ -0,0 +1,7 @@
1
+ import { type ReactNode } from 'react';
2
+ type Props = {
3
+ isOpen: boolean;
4
+ children: ReactNode;
5
+ };
6
+ export declare const Menu: (props: Props) => import("react").ReactPortal;
7
+ export {};