notra-editor 0.8.1 → 0.8.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 (217) hide show
  1. package/dist/components/blockquote-button/blockquote-button.cjs +5 -3
  2. package/dist/components/blockquote-button/blockquote-button.cjs.map +1 -1
  3. package/dist/components/blockquote-button/blockquote-button.mjs +5 -3
  4. package/dist/components/blockquote-button/blockquote-button.mjs.map +1 -1
  5. package/dist/components/code-block-button/code-block-button.cjs +5 -3
  6. package/dist/components/code-block-button/code-block-button.cjs.map +1 -1
  7. package/dist/components/code-block-button/code-block-button.mjs +5 -3
  8. package/dist/components/code-block-button/code-block-button.mjs.map +1 -1
  9. package/dist/components/code-block-view/code-block-shell.cjs +4 -2
  10. package/dist/components/code-block-view/code-block-shell.cjs.map +1 -1
  11. package/dist/components/code-block-view/code-block-shell.mjs +3 -2
  12. package/dist/components/code-block-view/code-block-shell.mjs.map +1 -1
  13. package/dist/components/code-block-view/code-block-view.cjs +6 -4
  14. package/dist/components/code-block-view/code-block-view.cjs.map +1 -1
  15. package/dist/components/code-block-view/code-block-view.mjs +6 -4
  16. package/dist/components/code-block-view/code-block-view.mjs.map +1 -1
  17. package/dist/components/code-block-view/language-select.cjs +9 -7
  18. package/dist/components/code-block-view/language-select.cjs.map +1 -1
  19. package/dist/components/code-block-view/language-select.mjs +9 -7
  20. package/dist/components/code-block-view/language-select.mjs.map +1 -1
  21. package/dist/components/copy-button.cjs +6 -4
  22. package/dist/components/copy-button.cjs.map +1 -1
  23. package/dist/components/copy-button.mjs +6 -4
  24. package/dist/components/copy-button.mjs.map +1 -1
  25. package/dist/components/heading-dropdown-menu/heading-dropdown-menu.cjs +8 -6
  26. package/dist/components/heading-dropdown-menu/heading-dropdown-menu.cjs.map +1 -1
  27. package/dist/components/heading-dropdown-menu/heading-dropdown-menu.mjs +8 -6
  28. package/dist/components/heading-dropdown-menu/heading-dropdown-menu.mjs.map +1 -1
  29. package/dist/components/heading-dropdown-menu/heading-menu-item.cjs +6 -4
  30. package/dist/components/heading-dropdown-menu/heading-menu-item.cjs.map +1 -1
  31. package/dist/components/heading-dropdown-menu/heading-menu-item.mjs +5 -4
  32. package/dist/components/heading-dropdown-menu/heading-menu-item.mjs.map +1 -1
  33. package/dist/components/heading-dropdown-menu/use-heading.cjs +4 -2
  34. package/dist/components/heading-dropdown-menu/use-heading.cjs.map +1 -1
  35. package/dist/components/heading-dropdown-menu/use-heading.mjs +3 -2
  36. package/dist/components/heading-dropdown-menu/use-heading.mjs.map +1 -1
  37. package/dist/components/image-popover/image-input-form.cjs +127 -0
  38. package/dist/components/image-popover/image-input-form.cjs.map +1 -0
  39. package/dist/components/image-popover/image-input-form.d.cts +16 -0
  40. package/dist/components/image-popover/image-input-form.d.ts +16 -0
  41. package/dist/components/image-popover/image-input-form.mjs +103 -0
  42. package/dist/components/image-popover/image-input-form.mjs.map +1 -0
  43. package/dist/components/image-popover/image-popover.cjs +35 -92
  44. package/dist/components/image-popover/image-popover.cjs.map +1 -1
  45. package/dist/components/image-popover/image-popover.mjs +36 -93
  46. package/dist/components/image-popover/image-popover.mjs.map +1 -1
  47. package/dist/components/image-popover/use-image-popover.cjs +2 -0
  48. package/dist/components/image-popover/use-image-popover.cjs.map +1 -1
  49. package/dist/components/image-popover/use-image-popover.mjs +1 -0
  50. package/dist/components/image-popover/use-image-popover.mjs.map +1 -1
  51. package/dist/components/link-popover/link-popover.cjs +9 -7
  52. package/dist/components/link-popover/link-popover.cjs.map +1 -1
  53. package/dist/components/link-popover/link-popover.mjs +9 -7
  54. package/dist/components/link-popover/link-popover.mjs.map +1 -1
  55. package/dist/components/link-popover/use-link-popover.cjs +2 -0
  56. package/dist/components/link-popover/use-link-popover.cjs.map +1 -1
  57. package/dist/components/link-popover/use-link-popover.mjs +1 -0
  58. package/dist/components/link-popover/use-link-popover.mjs.map +1 -1
  59. package/dist/components/list-dropdown-menu/list-dropdown-menu.cjs +8 -6
  60. package/dist/components/list-dropdown-menu/list-dropdown-menu.cjs.map +1 -1
  61. package/dist/components/list-dropdown-menu/list-dropdown-menu.mjs +8 -6
  62. package/dist/components/list-dropdown-menu/list-dropdown-menu.mjs.map +1 -1
  63. package/dist/components/list-dropdown-menu/list-menu-item.cjs +6 -4
  64. package/dist/components/list-dropdown-menu/list-menu-item.cjs.map +1 -1
  65. package/dist/components/list-dropdown-menu/list-menu-item.mjs +5 -4
  66. package/dist/components/list-dropdown-menu/list-menu-item.mjs.map +1 -1
  67. package/dist/components/list-dropdown-menu/use-list.cjs +5 -3
  68. package/dist/components/list-dropdown-menu/use-list.cjs.map +1 -1
  69. package/dist/components/list-dropdown-menu/use-list.mjs +4 -3
  70. package/dist/components/list-dropdown-menu/use-list.mjs.map +1 -1
  71. package/dist/components/mark-button/mark-button.cjs +6 -4
  72. package/dist/components/mark-button/mark-button.cjs.map +1 -1
  73. package/dist/components/mark-button/mark-button.mjs +6 -4
  74. package/dist/components/mark-button/mark-button.mjs.map +1 -1
  75. package/dist/components/mark-button/use-mark.cjs +4 -2
  76. package/dist/components/mark-button/use-mark.cjs.map +1 -1
  77. package/dist/components/mark-button/use-mark.mjs +3 -2
  78. package/dist/components/mark-button/use-mark.mjs.map +1 -1
  79. package/dist/components/slash-dropdown-menu/filter-slash-items.cjs +46 -0
  80. package/dist/components/slash-dropdown-menu/filter-slash-items.cjs.map +1 -0
  81. package/dist/components/slash-dropdown-menu/filter-slash-items.d.cts +15 -0
  82. package/dist/components/slash-dropdown-menu/filter-slash-items.d.ts +15 -0
  83. package/dist/components/slash-dropdown-menu/filter-slash-items.mjs +21 -0
  84. package/dist/components/slash-dropdown-menu/filter-slash-items.mjs.map +1 -0
  85. package/dist/components/slash-dropdown-menu/slash-dropdown-menu.cjs +294 -0
  86. package/dist/components/slash-dropdown-menu/slash-dropdown-menu.cjs.map +1 -0
  87. package/dist/components/slash-dropdown-menu/slash-dropdown-menu.d.cts +9 -0
  88. package/dist/components/slash-dropdown-menu/slash-dropdown-menu.d.ts +9 -0
  89. package/dist/components/slash-dropdown-menu/slash-dropdown-menu.mjs +292 -0
  90. package/dist/components/slash-dropdown-menu/slash-dropdown-menu.mjs.map +1 -0
  91. package/dist/components/slash-dropdown-menu/slash-image-popover.cjs +80 -0
  92. package/dist/components/slash-dropdown-menu/slash-image-popover.cjs.map +1 -0
  93. package/dist/components/slash-dropdown-menu/slash-image-popover.d.cts +11 -0
  94. package/dist/components/slash-dropdown-menu/slash-image-popover.d.ts +11 -0
  95. package/dist/components/slash-dropdown-menu/slash-image-popover.mjs +56 -0
  96. package/dist/components/slash-dropdown-menu/slash-image-popover.mjs.map +1 -0
  97. package/dist/components/slash-dropdown-menu/types.cjs +19 -0
  98. package/dist/components/slash-dropdown-menu/types.cjs.map +1 -0
  99. package/dist/components/slash-dropdown-menu/types.d.cts +22 -0
  100. package/dist/components/slash-dropdown-menu/types.d.ts +22 -0
  101. package/dist/components/slash-dropdown-menu/types.mjs +1 -0
  102. package/dist/components/slash-dropdown-menu/types.mjs.map +1 -0
  103. package/dist/components/slash-dropdown-menu/use-slash-items.cjs +138 -0
  104. package/dist/components/slash-dropdown-menu/use-slash-items.cjs.map +1 -0
  105. package/dist/components/slash-dropdown-menu/use-slash-items.d.cts +11 -0
  106. package/dist/components/slash-dropdown-menu/use-slash-items.d.ts +11 -0
  107. package/dist/components/slash-dropdown-menu/use-slash-items.mjs +125 -0
  108. package/dist/components/slash-dropdown-menu/use-slash-items.mjs.map +1 -0
  109. package/dist/components/toolbar/toolbar.cjs +4 -2
  110. package/dist/components/toolbar/toolbar.cjs.map +1 -1
  111. package/dist/components/toolbar/toolbar.mjs +3 -2
  112. package/dist/components/toolbar/toolbar.mjs.map +1 -1
  113. package/dist/components/ui/button.cjs +5 -3
  114. package/dist/components/ui/button.cjs.map +1 -1
  115. package/dist/components/ui/button.mjs +4 -3
  116. package/dist/components/ui/button.mjs.map +1 -1
  117. package/dist/components/ui/command.cjs +7 -6
  118. package/dist/components/ui/command.cjs.map +1 -1
  119. package/dist/components/ui/command.mjs +6 -6
  120. package/dist/components/ui/command.mjs.map +1 -1
  121. package/dist/components/ui/dialog.cjs +5 -4
  122. package/dist/components/ui/dialog.cjs.map +1 -1
  123. package/dist/components/ui/dialog.mjs +4 -4
  124. package/dist/components/ui/dialog.mjs.map +1 -1
  125. package/dist/components/ui/dropdown-menu.cjs +4 -3
  126. package/dist/components/ui/dropdown-menu.cjs.map +1 -1
  127. package/dist/components/ui/dropdown-menu.mjs +3 -3
  128. package/dist/components/ui/dropdown-menu.mjs.map +1 -1
  129. package/dist/components/ui/input-group.cjs +9 -7
  130. package/dist/components/ui/input-group.cjs.map +1 -1
  131. package/dist/components/ui/input-group.mjs +9 -7
  132. package/dist/components/ui/input-group.mjs.map +1 -1
  133. package/dist/components/ui/input.cjs +3 -1
  134. package/dist/components/ui/input.cjs.map +1 -1
  135. package/dist/components/ui/input.mjs +2 -1
  136. package/dist/components/ui/input.mjs.map +1 -1
  137. package/dist/components/ui/popover.cjs +4 -3
  138. package/dist/components/ui/popover.cjs.map +1 -1
  139. package/dist/components/ui/popover.mjs +3 -3
  140. package/dist/components/ui/popover.mjs.map +1 -1
  141. package/dist/components/ui/separator.cjs +4 -3
  142. package/dist/components/ui/separator.cjs.map +1 -1
  143. package/dist/components/ui/separator.mjs +3 -3
  144. package/dist/components/ui/separator.mjs.map +1 -1
  145. package/dist/components/ui/spacer.cjs +2 -0
  146. package/dist/components/ui/spacer.cjs.map +1 -1
  147. package/dist/components/ui/spacer.mjs +1 -0
  148. package/dist/components/ui/spacer.mjs.map +1 -1
  149. package/dist/components/ui/textarea.cjs +3 -1
  150. package/dist/components/ui/textarea.cjs.map +1 -1
  151. package/dist/components/ui/textarea.mjs +2 -1
  152. package/dist/components/ui/textarea.mjs.map +1 -1
  153. package/dist/components/undo-redo-button/undo-redo-button.cjs +6 -4
  154. package/dist/components/undo-redo-button/undo-redo-button.cjs.map +1 -1
  155. package/dist/components/undo-redo-button/undo-redo-button.mjs +6 -4
  156. package/dist/components/undo-redo-button/undo-redo-button.mjs.map +1 -1
  157. package/dist/components/undo-redo-button/use-undo-redo.cjs +4 -2
  158. package/dist/components/undo-redo-button/use-undo-redo.cjs.map +1 -1
  159. package/dist/components/undo-redo-button/use-undo-redo.mjs +3 -2
  160. package/dist/components/undo-redo-button/use-undo-redo.mjs.map +1 -1
  161. package/dist/extensions/code-block.cjs +8 -6
  162. package/dist/extensions/code-block.cjs.map +1 -1
  163. package/dist/extensions/code-block.mjs +7 -6
  164. package/dist/extensions/code-block.mjs.map +1 -1
  165. package/dist/extensions/editor.cjs +5 -3
  166. package/dist/extensions/editor.cjs.map +1 -1
  167. package/dist/extensions/editor.mjs +4 -3
  168. package/dist/extensions/editor.mjs.map +1 -1
  169. package/dist/extensions/index.cjs +4 -2
  170. package/dist/extensions/index.cjs.map +1 -1
  171. package/dist/extensions/index.mjs +3 -2
  172. package/dist/extensions/index.mjs.map +1 -1
  173. package/dist/extensions/shared.cjs +5 -3
  174. package/dist/extensions/shared.cjs.map +1 -1
  175. package/dist/extensions/shared.mjs +4 -3
  176. package/dist/extensions/shared.mjs.map +1 -1
  177. package/dist/hooks/use-copy-to-clipboard.cjs +3 -2
  178. package/dist/hooks/use-copy-to-clipboard.cjs.map +1 -1
  179. package/dist/hooks/use-copy-to-clipboard.mjs +2 -2
  180. package/dist/hooks/use-copy-to-clipboard.mjs.map +1 -1
  181. package/dist/hooks/use-markdown-editor.cjs +6 -1
  182. package/dist/hooks/use-markdown-editor.cjs.map +1 -1
  183. package/dist/hooks/use-markdown-editor.d.cts +1 -1
  184. package/dist/hooks/use-markdown-editor.d.ts +1 -1
  185. package/dist/hooks/use-markdown-editor.mjs +5 -1
  186. package/dist/hooks/use-markdown-editor.mjs.map +1 -1
  187. package/dist/index.cjs +18 -16
  188. package/dist/index.cjs.map +1 -1
  189. package/dist/index.mjs +17 -16
  190. package/dist/index.mjs.map +1 -1
  191. package/dist/lib/highlight-code-to-html.cjs +2 -0
  192. package/dist/lib/highlight-code-to-html.cjs.map +1 -1
  193. package/dist/lib/highlight-code-to-html.mjs +1 -0
  194. package/dist/lib/highlight-code-to-html.mjs.map +1 -1
  195. package/dist/lib/languages.cjs +4 -2
  196. package/dist/lib/languages.cjs.map +1 -1
  197. package/dist/lib/languages.mjs +3 -2
  198. package/dist/lib/languages.mjs.map +1 -1
  199. package/dist/lib/utils.cjs +2 -0
  200. package/dist/lib/utils.cjs.map +1 -1
  201. package/dist/lib/utils.mjs +1 -0
  202. package/dist/lib/utils.mjs.map +1 -1
  203. package/dist/notra-editor.cjs +17 -13
  204. package/dist/notra-editor.cjs.map +1 -1
  205. package/dist/notra-editor.mjs +17 -13
  206. package/dist/notra-editor.mjs.map +1 -1
  207. package/dist/notra-reader.cjs +9 -7
  208. package/dist/notra-reader.cjs.map +1 -1
  209. package/dist/notra-reader.mjs +8 -7
  210. package/dist/notra-reader.mjs.map +1 -1
  211. package/dist/styles/globals.css +4 -2
  212. package/dist/themes/default/editor.css +68 -6
  213. package/dist/utils/markdown-to-json.cjs +5 -3
  214. package/dist/utils/markdown-to-json.cjs.map +1 -1
  215. package/dist/utils/markdown-to-json.mjs +4 -3
  216. package/dist/utils/markdown-to-json.mjs.map +1 -1
  217. package/package.json +5 -1
@@ -0,0 +1,294 @@
1
+ "use strict";
2
+ "use client";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/components/slash-dropdown-menu/slash-dropdown-menu.tsx
22
+ var slash_dropdown_menu_exports = {};
23
+ __export(slash_dropdown_menu_exports, {
24
+ SlashDropdownMenu: () => SlashDropdownMenu
25
+ });
26
+ module.exports = __toCommonJS(slash_dropdown_menu_exports);
27
+ var import_react = require("@floating-ui/react");
28
+ var import_state = require("@tiptap/pm/state");
29
+ var import_suggestion = require("@tiptap/suggestion");
30
+ var import_react2 = require("react");
31
+ var import_filter_slash_items = require("./filter-slash-items.cjs");
32
+ var import_slash_image_popover = require("./slash-image-popover.cjs");
33
+ var import_use_slash_items = require("./use-slash-items.cjs");
34
+ var import_command = require("../ui/command.cjs");
35
+ var import_jsx_runtime = require("react/jsx-runtime");
36
+ var FLOATING_Z_INDEX = 1e3;
37
+ var MAX_HEIGHT = 384;
38
+ function SlashDropdownMenu({ editor }) {
39
+ const [imageAnchor, setImageAnchor] = (0, import_react2.useState)(null);
40
+ const handleImageRequest = (0, import_react2.useCallback)(() => {
41
+ if (!editor) return;
42
+ const { from } = editor.state.selection;
43
+ const rect = editor.view.coordsAtPos(from);
44
+ const domRect = new DOMRect(
45
+ rect.left,
46
+ rect.top,
47
+ rect.right - rect.left,
48
+ rect.bottom - rect.top
49
+ );
50
+ setImageAnchor(domRect);
51
+ }, [editor]);
52
+ const getSlashItems = (0, import_use_slash_items.useSlashItems)({ onImageRequest: handleImageRequest });
53
+ const itemsCallback = (0, import_react2.useCallback)(
54
+ ({ query, editor: editor2 }) => (0, import_filter_slash_items.filterSlashItems)(getSlashItems(editor2), query),
55
+ [getSlashItems]
56
+ );
57
+ const [open, setOpen] = (0, import_react2.useState)(false);
58
+ const [decorationNode, setDecorationNode] = (0, import_react2.useState)(
59
+ null
60
+ );
61
+ const [filteredItems, setFilteredItems] = (0, import_react2.useState)([]);
62
+ const [selectedValue, setSelectedValue] = (0, import_react2.useState)("");
63
+ const close = (0, import_react2.useCallback)(() => {
64
+ setOpen(false);
65
+ }, []);
66
+ const middleware = (0, import_react2.useMemo)(
67
+ () => [
68
+ (0, import_react.offset)(8),
69
+ (0, import_react.flip)({ mainAxis: true, crossAxis: false }),
70
+ (0, import_react.shift)({ padding: 8 }),
71
+ (0, import_react.size)({
72
+ apply({ availableHeight, elements }) {
73
+ const next = Math.min(availableHeight, MAX_HEIGHT);
74
+ elements.floating.style.setProperty(
75
+ "--suggestion-menu-max-height",
76
+ `${next}px`
77
+ );
78
+ }
79
+ })
80
+ ],
81
+ []
82
+ );
83
+ const { refs, floatingStyles, context } = (0, import_react.useFloating)({
84
+ open,
85
+ strategy: "absolute",
86
+ placement: "bottom-start",
87
+ whileElementsMounted: import_react.autoUpdate,
88
+ middleware,
89
+ onOpenChange: (next) => {
90
+ if (!next) close();
91
+ }
92
+ });
93
+ (0, import_react2.useEffect)(() => {
94
+ refs.setReference(decorationNode);
95
+ }, [refs, decorationNode]);
96
+ const dismiss = (0, import_react.useDismiss)(context);
97
+ const { getFloatingProps } = (0, import_react.useInteractions)([dismiss]);
98
+ const itemsCallbackRef = (0, import_react2.useRef)(itemsCallback);
99
+ const filteredItemsRef = (0, import_react2.useRef)([]);
100
+ const selectedValueRef = (0, import_react2.useRef)("");
101
+ const commandRef = (0, import_react2.useRef)(null);
102
+ (0, import_react2.useEffect)(() => {
103
+ itemsCallbackRef.current = itemsCallback;
104
+ }, [itemsCallback]);
105
+ (0, import_react2.useEffect)(() => {
106
+ filteredItemsRef.current = filteredItems;
107
+ }, [filteredItems]);
108
+ (0, import_react2.useEffect)(() => {
109
+ selectedValueRef.current = selectedValue;
110
+ }, [selectedValue]);
111
+ const floatingDivRef = (0, import_react2.useRef)(null);
112
+ (0, import_react2.useEffect)(() => {
113
+ if (!selectedValue) return;
114
+ const root = floatingDivRef.current;
115
+ if (!root) return;
116
+ const target = root.querySelector(
117
+ '[data-selected="true"]'
118
+ );
119
+ target?.scrollIntoView({ block: "nearest" });
120
+ }, [selectedValue]);
121
+ const setFloatingNode = (0, import_react2.useCallback)(
122
+ (node) => {
123
+ floatingDivRef.current = node;
124
+ refs.setFloating(node);
125
+ },
126
+ [refs]
127
+ );
128
+ (0, import_react2.useEffect)(() => {
129
+ if (!editor || editor.isDestroyed) return;
130
+ const pluginKey = new import_state.PluginKey("slashDropdownMenu");
131
+ const plugin = (0, import_suggestion.Suggestion)({
132
+ editor,
133
+ char: "/",
134
+ pluginKey,
135
+ decorationClass: "notra-slash-decoration",
136
+ decorationContent: "Filter...",
137
+ items: ({ query, editor: editor2 }) => itemsCallbackRef.current({ query, editor: editor2 }),
138
+ allow: ({ state, range }) => {
139
+ const $from = state.doc.resolve(range.from);
140
+ for (let depth = $from.depth; depth > 0; depth--) {
141
+ const name = $from.node(depth).type.name;
142
+ if (name === "image" || name === "codeBlock") return false;
143
+ }
144
+ return true;
145
+ },
146
+ command: ({ editor: editor2, range, props }) => {
147
+ editor2.chain().focus().deleteRange(range).run();
148
+ props.onSelect({ editor: editor2, range });
149
+ },
150
+ render: () => ({
151
+ onStart: (props) => {
152
+ setDecorationNode(props.decorationNode ?? null);
153
+ setFilteredItems(props.items);
154
+ setSelectedValue(props.items[0]?.title ?? "");
155
+ commandRef.current = (item) => props.command(item);
156
+ setOpen(true);
157
+ },
158
+ onUpdate: (props) => {
159
+ setDecorationNode(props.decorationNode ?? null);
160
+ setFilteredItems(props.items);
161
+ setSelectedValue(
162
+ (prev) => props.items.some((i) => i.title === prev) ? prev : props.items[0]?.title ?? ""
163
+ );
164
+ commandRef.current = (item) => props.command(item);
165
+ },
166
+ onKeyDown: ({ event }) => {
167
+ const list = filteredItemsRef.current;
168
+ if (list.length === 0 && event.key !== "Escape") return false;
169
+ if (event.key === "ArrowDown") {
170
+ const currentIndex = list.findIndex(
171
+ (i) => i.title === selectedValueRef.current
172
+ );
173
+ const nextIndex = (currentIndex + 1) % list.length;
174
+ setSelectedValue(list[nextIndex].title);
175
+ return true;
176
+ }
177
+ if (event.key === "ArrowUp") {
178
+ const currentIndex = list.findIndex(
179
+ (i) => i.title === selectedValueRef.current
180
+ );
181
+ const prevIndex = (currentIndex - 1 + list.length) % list.length;
182
+ setSelectedValue(list[prevIndex].title);
183
+ return true;
184
+ }
185
+ if (event.key === "Enter") {
186
+ const item = list.find((i) => i.title === selectedValueRef.current);
187
+ if (item && commandRef.current) {
188
+ commandRef.current(item);
189
+ }
190
+ return true;
191
+ }
192
+ if (event.key === "Escape") {
193
+ close();
194
+ return true;
195
+ }
196
+ return false;
197
+ },
198
+ onExit: () => {
199
+ setDecorationNode(null);
200
+ setFilteredItems([]);
201
+ setSelectedValue("");
202
+ commandRef.current = null;
203
+ setOpen(false);
204
+ }
205
+ })
206
+ });
207
+ editor.registerPlugin(plugin, (newPlugin, currentPlugins) => [
208
+ newPlugin,
209
+ ...currentPlugins
210
+ ]);
211
+ return () => {
212
+ if (!editor.isDestroyed) {
213
+ editor.unregisterPlugin(pluginKey);
214
+ }
215
+ };
216
+ }, [editor, close]);
217
+ const grouped = (0, import_react2.useMemo)(() => groupByLabel(filteredItems), [filteredItems]);
218
+ const isMounted = open && decorationNode !== null;
219
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
220
+ isMounted && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
221
+ "div",
222
+ {
223
+ ref: setFloatingNode,
224
+ className: "nt:rounded-xl nt:bg-popover nt:text-popover-foreground nt:shadow-md nt:ring-1 nt:ring-foreground/10 nt:outline-hidden",
225
+ "data-selector": "notra-slash-dropdown-menu",
226
+ style: { ...floatingStyles, zIndex: FLOATING_Z_INDEX },
227
+ ...getFloatingProps(),
228
+ onPointerDown: (e) => e.preventDefault(),
229
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
230
+ import_command.Command,
231
+ {
232
+ disablePointerSelection: true,
233
+ label: "Slash command menu",
234
+ shouldFilter: false,
235
+ value: selectedValue,
236
+ onValueChange: setSelectedValue,
237
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
238
+ import_command.CommandList,
239
+ {
240
+ style: {
241
+ maxHeight: "var(--suggestion-menu-max-height)"
242
+ },
243
+ children: grouped.map((group, groupIndex) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react2.Fragment, { children: [
244
+ groupIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_command.CommandSeparator, {}),
245
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_command.CommandGroup, { heading: group.label || void 0, children: group.items.map((item) => {
246
+ const Badge = item.badge;
247
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
248
+ import_command.CommandItem,
249
+ {
250
+ value: item.title,
251
+ onSelect: () => commandRef.current?.(item),
252
+ children: [
253
+ Badge && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, { className: "nt:size-4" }),
254
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "nt:flex-1 nt:text-left", children: item.title })
255
+ ]
256
+ },
257
+ item.title
258
+ );
259
+ }) })
260
+ ] }, `${group.label}-${groupIndex}`))
261
+ }
262
+ )
263
+ }
264
+ )
265
+ }
266
+ ),
267
+ imageAnchor && editor && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
268
+ import_slash_image_popover.SlashImagePopover,
269
+ {
270
+ anchorRect: imageAnchor,
271
+ editor,
272
+ onClose: () => setImageAnchor(null)
273
+ }
274
+ )
275
+ ] });
276
+ }
277
+ function groupByLabel(items) {
278
+ const result = [];
279
+ for (const item of items) {
280
+ const label = item.group ?? "";
281
+ const last = result[result.length - 1];
282
+ if (last && last.label === label) {
283
+ last.items.push(item);
284
+ } else {
285
+ result.push({ label, items: [item] });
286
+ }
287
+ }
288
+ return result;
289
+ }
290
+ // Annotate the CommonJS export names for ESM import in node:
291
+ 0 && (module.exports = {
292
+ SlashDropdownMenu
293
+ });
294
+ //# sourceMappingURL=slash-dropdown-menu.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/slash-dropdown-menu/slash-dropdown-menu.tsx"],"sourcesContent":["'use client';\n\nimport {\n\tautoUpdate,\n\tflip,\n\toffset,\n\tshift,\n\tsize,\n\tuseDismiss,\n\tuseFloating,\n\tuseInteractions\n} from '@floating-ui/react';\nimport { PluginKey } from '@tiptap/pm/state';\nimport { Suggestion } from '@tiptap/suggestion';\nimport {\n\tFragment,\n\tuseCallback,\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState\n} from 'react';\n\nimport { filterSlashItems } from './filter-slash-items.js';\nimport { SlashImagePopover } from './slash-image-popover.js';\nimport { useSlashItems } from './use-slash-items.js';\nimport {\n\tCommand,\n\tCommandGroup,\n\tCommandItem,\n\tCommandList,\n\tCommandSeparator\n} from '../ui/command.js';\n\nimport type { SlashItem } from './types.js';\nimport type { Editor } from '@tiptap/core';\nimport type {\n\tSuggestionKeyDownProps,\n\tSuggestionProps\n} from '@tiptap/suggestion';\n\nexport interface SlashDropdownMenuProps {\n\teditor: Editor | null;\n}\n\nconst FLOATING_Z_INDEX = 1000;\nconst MAX_HEIGHT = 384;\n\nexport function SlashDropdownMenu({ editor }: SlashDropdownMenuProps) {\n\tconst [imageAnchor, setImageAnchor] = useState<DOMRect | null>(null);\n\n\tconst handleImageRequest = useCallback(() => {\n\t\tif (!editor) return;\n\n\t\tconst { from } = editor.state.selection;\n\t\tconst rect = editor.view.coordsAtPos(from);\n\t\tconst domRect = new DOMRect(\n\t\t\trect.left,\n\t\t\trect.top,\n\t\t\trect.right - rect.left,\n\t\t\trect.bottom - rect.top\n\t\t);\n\n\t\tsetImageAnchor(domRect);\n\t}, [editor]);\n\n\tconst getSlashItems = useSlashItems({ onImageRequest: handleImageRequest });\n\n\tconst itemsCallback = useCallback(\n\t\t({ query, editor }: { query: string; editor: Editor }) =>\n\t\t\tfilterSlashItems(getSlashItems(editor), query),\n\t\t[getSlashItems]\n\t);\n\n\tconst [open, setOpen] = useState(false);\n\tconst [decorationNode, setDecorationNode] = useState<HTMLElement | null>(\n\t\tnull\n\t);\n\tconst [filteredItems, setFilteredItems] = useState<SlashItem[]>([]);\n\tconst [selectedValue, setSelectedValue] = useState<string>('');\n\n\tconst close = useCallback(() => {\n\t\tsetOpen(false);\n\t}, []);\n\n\tconst middleware = useMemo(\n\t\t() => [\n\t\t\toffset(8),\n\t\t\tflip({ mainAxis: true, crossAxis: false }),\n\t\t\tshift({ padding: 8 }),\n\t\t\tsize({\n\t\t\t\tapply({ availableHeight, elements }) {\n\t\t\t\t\tconst next = Math.min(availableHeight, MAX_HEIGHT);\n\n\t\t\t\t\telements.floating.style.setProperty(\n\t\t\t\t\t\t'--suggestion-menu-max-height',\n\t\t\t\t\t\t`${next}px`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t})\n\t\t],\n\t\t[]\n\t);\n\n\tconst { refs, floatingStyles, context } = useFloating({\n\t\topen,\n\t\tstrategy: 'absolute',\n\t\tplacement: 'bottom-start',\n\t\twhileElementsMounted: autoUpdate,\n\t\tmiddleware,\n\t\tonOpenChange: (next) => {\n\t\t\tif (!next) close();\n\t\t}\n\t});\n\n\tuseEffect(() => {\n\t\trefs.setReference(decorationNode);\n\t}, [refs, decorationNode]);\n\n\tconst dismiss = useDismiss(context);\n\tconst { getFloatingProps } = useInteractions([dismiss]);\n\n\t// Stable refs for the plugin, which is registered once per editor instance.\n\tconst itemsCallbackRef = useRef(itemsCallback);\n\tconst filteredItemsRef = useRef<SlashItem[]>([]);\n\tconst selectedValueRef = useRef('');\n\tconst commandRef = useRef<((item: SlashItem) => void) | null>(null);\n\n\tuseEffect(() => {\n\t\titemsCallbackRef.current = itemsCallback;\n\t}, [itemsCallback]);\n\n\tuseEffect(() => {\n\t\tfilteredItemsRef.current = filteredItems;\n\t}, [filteredItems]);\n\n\tuseEffect(() => {\n\t\tselectedValueRef.current = selectedValue;\n\t}, [selectedValue]);\n\n\t// Keep highlighted item visible when arrow keys move past the viewport.\n\tconst floatingDivRef = useRef<HTMLDivElement | null>(null);\n\n\tuseEffect(() => {\n\t\tif (!selectedValue) return;\n\n\t\tconst root = floatingDivRef.current;\n\n\t\tif (!root) return;\n\n\t\tconst target = root.querySelector(\n\t\t\t'[data-selected=\"true\"]'\n\t\t) as HTMLElement | null;\n\n\t\ttarget?.scrollIntoView({ block: 'nearest' });\n\t}, [selectedValue]);\n\n\tconst setFloatingNode = useCallback(\n\t\t(node: HTMLDivElement | null) => {\n\t\t\tfloatingDivRef.current = node;\n\t\t\trefs.setFloating(node);\n\t\t},\n\t\t[refs]\n\t);\n\n\tuseEffect(() => {\n\t\tif (!editor || editor.isDestroyed) return;\n\n\t\tconst pluginKey = new PluginKey('slashDropdownMenu');\n\n\t\tconst plugin = Suggestion<SlashItem>({\n\t\t\teditor,\n\t\t\tchar: '/',\n\t\t\tpluginKey,\n\t\t\tdecorationClass: 'notra-slash-decoration',\n\t\t\tdecorationContent: 'Filter...',\n\n\t\t\titems: ({ query, editor }) => itemsCallbackRef.current({ query, editor }),\n\n\t\t\tallow: ({ state, range }) => {\n\t\t\t\tconst $from = state.doc.resolve(range.from);\n\n\t\t\t\tfor (let depth = $from.depth; depth > 0; depth--) {\n\t\t\t\t\tconst name = $from.node(depth).type.name;\n\n\t\t\t\t\tif (name === 'image' || name === 'codeBlock') return false;\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t},\n\n\t\t\tcommand: ({ editor, range, props }) => {\n\t\t\t\teditor.chain().focus().deleteRange(range).run();\n\t\t\t\tprops.onSelect({ editor, range });\n\t\t\t},\n\n\t\t\trender: () => ({\n\t\t\t\tonStart: (props: SuggestionProps<SlashItem>) => {\n\t\t\t\t\tsetDecorationNode((props.decorationNode as HTMLElement) ?? null);\n\t\t\t\t\tsetFilteredItems(props.items);\n\t\t\t\t\tsetSelectedValue(props.items[0]?.title ?? '');\n\t\t\t\t\tcommandRef.current = (item) => props.command(item);\n\t\t\t\t\tsetOpen(true);\n\t\t\t\t},\n\t\t\t\tonUpdate: (props: SuggestionProps<SlashItem>) => {\n\t\t\t\t\tsetDecorationNode((props.decorationNode as HTMLElement) ?? null);\n\t\t\t\t\tsetFilteredItems(props.items);\n\t\t\t\t\tsetSelectedValue((prev) =>\n\t\t\t\t\t\tprops.items.some((i) => i.title === prev)\n\t\t\t\t\t\t\t? prev\n\t\t\t\t\t\t\t: (props.items[0]?.title ?? '')\n\t\t\t\t\t);\n\t\t\t\t\tcommandRef.current = (item) => props.command(item);\n\t\t\t\t},\n\t\t\t\tonKeyDown: ({ event }: SuggestionKeyDownProps) => {\n\t\t\t\t\tconst list = filteredItemsRef.current;\n\n\t\t\t\t\tif (list.length === 0 && event.key !== 'Escape') return false;\n\n\t\t\t\t\tif (event.key === 'ArrowDown') {\n\t\t\t\t\t\tconst currentIndex = list.findIndex(\n\t\t\t\t\t\t\t(i) => i.title === selectedValueRef.current\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst nextIndex = (currentIndex + 1) % list.length;\n\n\t\t\t\t\t\tsetSelectedValue(list[nextIndex].title);\n\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (event.key === 'ArrowUp') {\n\t\t\t\t\t\tconst currentIndex = list.findIndex(\n\t\t\t\t\t\t\t(i) => i.title === selectedValueRef.current\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst prevIndex = (currentIndex - 1 + list.length) % list.length;\n\n\t\t\t\t\t\tsetSelectedValue(list[prevIndex].title);\n\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (event.key === 'Enter') {\n\t\t\t\t\t\tconst item = list.find((i) => i.title === selectedValueRef.current);\n\n\t\t\t\t\t\tif (item && commandRef.current) {\n\t\t\t\t\t\t\tcommandRef.current(item);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (event.key === 'Escape') {\n\t\t\t\t\t\tclose();\n\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn false;\n\t\t\t\t},\n\t\t\t\tonExit: () => {\n\t\t\t\t\tsetDecorationNode(null);\n\t\t\t\t\tsetFilteredItems([]);\n\t\t\t\t\tsetSelectedValue('');\n\t\t\t\t\tcommandRef.current = null;\n\t\t\t\t\tsetOpen(false);\n\t\t\t\t}\n\t\t\t})\n\t\t});\n\n\t\t// Prepend so our handleKeyDown runs before the base keymap; otherwise\n\t\t// Enter would be consumed by the paragraph-split handler before our\n\t\t// suggestion onKeyDown could see it.\n\t\teditor.registerPlugin(plugin, (newPlugin, currentPlugins) => [\n\t\t\tnewPlugin,\n\t\t\t...currentPlugins\n\t\t]);\n\n\t\treturn () => {\n\t\t\tif (!editor.isDestroyed) {\n\t\t\t\teditor.unregisterPlugin(pluginKey);\n\t\t\t}\n\t\t};\n\t}, [editor, close]);\n\n\tconst grouped = useMemo(() => groupByLabel(filteredItems), [filteredItems]);\n\n\tconst isMounted = open && decorationNode !== null;\n\n\treturn (\n\t\t<>\n\t\t\t{isMounted && (\n\t\t\t\t<div\n\t\t\t\t\tref={setFloatingNode}\n\t\t\t\t\tclassName=\"nt:rounded-xl nt:bg-popover nt:text-popover-foreground nt:shadow-md nt:ring-1 nt:ring-foreground/10 nt:outline-hidden\"\n\t\t\t\t\tdata-selector=\"notra-slash-dropdown-menu\"\n\t\t\t\t\tstyle={{ ...floatingStyles, zIndex: FLOATING_Z_INDEX }}\n\t\t\t\t\t{...getFloatingProps()}\n\t\t\t\t\tonPointerDown={(e) => e.preventDefault()}\n\t\t\t\t>\n\t\t\t\t\t<Command\n\t\t\t\t\t\tdisablePointerSelection\n\t\t\t\t\t\tlabel=\"Slash command menu\"\n\t\t\t\t\t\tshouldFilter={false}\n\t\t\t\t\t\tvalue={selectedValue}\n\t\t\t\t\t\tonValueChange={setSelectedValue}\n\t\t\t\t\t>\n\t\t\t\t\t\t<CommandList\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\tmaxHeight: 'var(--suggestion-menu-max-height)'\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{grouped.map((group, groupIndex) => (\n\t\t\t\t\t\t\t\t<Fragment key={`${group.label}-${groupIndex}`}>\n\t\t\t\t\t\t\t\t\t{groupIndex > 0 && <CommandSeparator />}\n\t\t\t\t\t\t\t\t\t<CommandGroup heading={group.label || undefined}>\n\t\t\t\t\t\t\t\t\t\t{group.items.map((item) => {\n\t\t\t\t\t\t\t\t\t\t\tconst Badge = item.badge;\n\n\t\t\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t\t\t<CommandItem\n\t\t\t\t\t\t\t\t\t\t\t\t\tkey={item.title}\n\t\t\t\t\t\t\t\t\t\t\t\t\tvalue={item.title}\n\t\t\t\t\t\t\t\t\t\t\t\t\tonSelect={() => commandRef.current?.(item)}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{Badge && <Badge className=\"nt:size-4\" />}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"nt:flex-1 nt:text-left\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{item.title}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t</CommandItem>\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t</CommandGroup>\n\t\t\t\t\t\t\t\t</Fragment>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</CommandList>\n\t\t\t\t\t</Command>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t{imageAnchor && editor && (\n\t\t\t\t<SlashImagePopover\n\t\t\t\t\tanchorRect={imageAnchor}\n\t\t\t\t\teditor={editor}\n\t\t\t\t\tonClose={() => setImageAnchor(null)}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</>\n\t);\n}\n\ninterface GroupedSlashItems {\n\tlabel: string;\n\titems: SlashItem[];\n}\n\nfunction groupByLabel(items: SlashItem[]): GroupedSlashItems[] {\n\tconst result: GroupedSlashItems[] = [];\n\n\tfor (const item of items) {\n\t\tconst label = item.group ?? '';\n\t\tconst last = result[result.length - 1];\n\n\t\tif (last && last.label === label) {\n\t\t\tlast.items.push(item);\n\t\t} else {\n\t\t\tresult.push({ label, items: [item] });\n\t\t}\n\t}\n\n\treturn result;\n}\n"],"mappings":"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","names":["import_react","editor"]}
@@ -0,0 +1,9 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { Editor } from '@tiptap/core';
3
+
4
+ interface SlashDropdownMenuProps {
5
+ editor: Editor | null;
6
+ }
7
+ declare function SlashDropdownMenu({ editor }: SlashDropdownMenuProps): react_jsx_runtime.JSX.Element;
8
+
9
+ export { SlashDropdownMenu, type SlashDropdownMenuProps };
@@ -0,0 +1,9 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { Editor } from '@tiptap/core';
3
+
4
+ interface SlashDropdownMenuProps {
5
+ editor: Editor | null;
6
+ }
7
+ declare function SlashDropdownMenu({ editor }: SlashDropdownMenuProps): react_jsx_runtime.JSX.Element;
8
+
9
+ export { SlashDropdownMenu, type SlashDropdownMenuProps };
@@ -0,0 +1,292 @@
1
+ "use client";
2
+
3
+ // src/components/slash-dropdown-menu/slash-dropdown-menu.tsx
4
+ import {
5
+ autoUpdate,
6
+ flip,
7
+ offset,
8
+ shift,
9
+ size,
10
+ useDismiss,
11
+ useFloating,
12
+ useInteractions
13
+ } from "@floating-ui/react";
14
+ import { PluginKey } from "@tiptap/pm/state";
15
+ import { Suggestion } from "@tiptap/suggestion";
16
+ import {
17
+ Fragment,
18
+ useCallback,
19
+ useEffect,
20
+ useMemo,
21
+ useRef,
22
+ useState
23
+ } from "react";
24
+ import { filterSlashItems } from "./filter-slash-items.mjs";
25
+ import { SlashImagePopover } from "./slash-image-popover.mjs";
26
+ import { useSlashItems } from "./use-slash-items.mjs";
27
+ import {
28
+ Command,
29
+ CommandGroup,
30
+ CommandItem,
31
+ CommandList,
32
+ CommandSeparator
33
+ } from "../ui/command.mjs";
34
+ import { Fragment as Fragment2, jsx, jsxs } from "react/jsx-runtime";
35
+ var FLOATING_Z_INDEX = 1e3;
36
+ var MAX_HEIGHT = 384;
37
+ function SlashDropdownMenu({ editor }) {
38
+ const [imageAnchor, setImageAnchor] = useState(null);
39
+ const handleImageRequest = useCallback(() => {
40
+ if (!editor) return;
41
+ const { from } = editor.state.selection;
42
+ const rect = editor.view.coordsAtPos(from);
43
+ const domRect = new DOMRect(
44
+ rect.left,
45
+ rect.top,
46
+ rect.right - rect.left,
47
+ rect.bottom - rect.top
48
+ );
49
+ setImageAnchor(domRect);
50
+ }, [editor]);
51
+ const getSlashItems = useSlashItems({ onImageRequest: handleImageRequest });
52
+ const itemsCallback = useCallback(
53
+ ({ query, editor: editor2 }) => filterSlashItems(getSlashItems(editor2), query),
54
+ [getSlashItems]
55
+ );
56
+ const [open, setOpen] = useState(false);
57
+ const [decorationNode, setDecorationNode] = useState(
58
+ null
59
+ );
60
+ const [filteredItems, setFilteredItems] = useState([]);
61
+ const [selectedValue, setSelectedValue] = useState("");
62
+ const close = useCallback(() => {
63
+ setOpen(false);
64
+ }, []);
65
+ const middleware = useMemo(
66
+ () => [
67
+ offset(8),
68
+ flip({ mainAxis: true, crossAxis: false }),
69
+ shift({ padding: 8 }),
70
+ size({
71
+ apply({ availableHeight, elements }) {
72
+ const next = Math.min(availableHeight, MAX_HEIGHT);
73
+ elements.floating.style.setProperty(
74
+ "--suggestion-menu-max-height",
75
+ `${next}px`
76
+ );
77
+ }
78
+ })
79
+ ],
80
+ []
81
+ );
82
+ const { refs, floatingStyles, context } = useFloating({
83
+ open,
84
+ strategy: "absolute",
85
+ placement: "bottom-start",
86
+ whileElementsMounted: autoUpdate,
87
+ middleware,
88
+ onOpenChange: (next) => {
89
+ if (!next) close();
90
+ }
91
+ });
92
+ useEffect(() => {
93
+ refs.setReference(decorationNode);
94
+ }, [refs, decorationNode]);
95
+ const dismiss = useDismiss(context);
96
+ const { getFloatingProps } = useInteractions([dismiss]);
97
+ const itemsCallbackRef = useRef(itemsCallback);
98
+ const filteredItemsRef = useRef([]);
99
+ const selectedValueRef = useRef("");
100
+ const commandRef = useRef(null);
101
+ useEffect(() => {
102
+ itemsCallbackRef.current = itemsCallback;
103
+ }, [itemsCallback]);
104
+ useEffect(() => {
105
+ filteredItemsRef.current = filteredItems;
106
+ }, [filteredItems]);
107
+ useEffect(() => {
108
+ selectedValueRef.current = selectedValue;
109
+ }, [selectedValue]);
110
+ const floatingDivRef = useRef(null);
111
+ useEffect(() => {
112
+ if (!selectedValue) return;
113
+ const root = floatingDivRef.current;
114
+ if (!root) return;
115
+ const target = root.querySelector(
116
+ '[data-selected="true"]'
117
+ );
118
+ target?.scrollIntoView({ block: "nearest" });
119
+ }, [selectedValue]);
120
+ const setFloatingNode = useCallback(
121
+ (node) => {
122
+ floatingDivRef.current = node;
123
+ refs.setFloating(node);
124
+ },
125
+ [refs]
126
+ );
127
+ useEffect(() => {
128
+ if (!editor || editor.isDestroyed) return;
129
+ const pluginKey = new PluginKey("slashDropdownMenu");
130
+ const plugin = Suggestion({
131
+ editor,
132
+ char: "/",
133
+ pluginKey,
134
+ decorationClass: "notra-slash-decoration",
135
+ decorationContent: "Filter...",
136
+ items: ({ query, editor: editor2 }) => itemsCallbackRef.current({ query, editor: editor2 }),
137
+ allow: ({ state, range }) => {
138
+ const $from = state.doc.resolve(range.from);
139
+ for (let depth = $from.depth; depth > 0; depth--) {
140
+ const name = $from.node(depth).type.name;
141
+ if (name === "image" || name === "codeBlock") return false;
142
+ }
143
+ return true;
144
+ },
145
+ command: ({ editor: editor2, range, props }) => {
146
+ editor2.chain().focus().deleteRange(range).run();
147
+ props.onSelect({ editor: editor2, range });
148
+ },
149
+ render: () => ({
150
+ onStart: (props) => {
151
+ setDecorationNode(props.decorationNode ?? null);
152
+ setFilteredItems(props.items);
153
+ setSelectedValue(props.items[0]?.title ?? "");
154
+ commandRef.current = (item) => props.command(item);
155
+ setOpen(true);
156
+ },
157
+ onUpdate: (props) => {
158
+ setDecorationNode(props.decorationNode ?? null);
159
+ setFilteredItems(props.items);
160
+ setSelectedValue(
161
+ (prev) => props.items.some((i) => i.title === prev) ? prev : props.items[0]?.title ?? ""
162
+ );
163
+ commandRef.current = (item) => props.command(item);
164
+ },
165
+ onKeyDown: ({ event }) => {
166
+ const list = filteredItemsRef.current;
167
+ if (list.length === 0 && event.key !== "Escape") return false;
168
+ if (event.key === "ArrowDown") {
169
+ const currentIndex = list.findIndex(
170
+ (i) => i.title === selectedValueRef.current
171
+ );
172
+ const nextIndex = (currentIndex + 1) % list.length;
173
+ setSelectedValue(list[nextIndex].title);
174
+ return true;
175
+ }
176
+ if (event.key === "ArrowUp") {
177
+ const currentIndex = list.findIndex(
178
+ (i) => i.title === selectedValueRef.current
179
+ );
180
+ const prevIndex = (currentIndex - 1 + list.length) % list.length;
181
+ setSelectedValue(list[prevIndex].title);
182
+ return true;
183
+ }
184
+ if (event.key === "Enter") {
185
+ const item = list.find((i) => i.title === selectedValueRef.current);
186
+ if (item && commandRef.current) {
187
+ commandRef.current(item);
188
+ }
189
+ return true;
190
+ }
191
+ if (event.key === "Escape") {
192
+ close();
193
+ return true;
194
+ }
195
+ return false;
196
+ },
197
+ onExit: () => {
198
+ setDecorationNode(null);
199
+ setFilteredItems([]);
200
+ setSelectedValue("");
201
+ commandRef.current = null;
202
+ setOpen(false);
203
+ }
204
+ })
205
+ });
206
+ editor.registerPlugin(plugin, (newPlugin, currentPlugins) => [
207
+ newPlugin,
208
+ ...currentPlugins
209
+ ]);
210
+ return () => {
211
+ if (!editor.isDestroyed) {
212
+ editor.unregisterPlugin(pluginKey);
213
+ }
214
+ };
215
+ }, [editor, close]);
216
+ const grouped = useMemo(() => groupByLabel(filteredItems), [filteredItems]);
217
+ const isMounted = open && decorationNode !== null;
218
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
219
+ isMounted && /* @__PURE__ */ jsx(
220
+ "div",
221
+ {
222
+ ref: setFloatingNode,
223
+ className: "nt:rounded-xl nt:bg-popover nt:text-popover-foreground nt:shadow-md nt:ring-1 nt:ring-foreground/10 nt:outline-hidden",
224
+ "data-selector": "notra-slash-dropdown-menu",
225
+ style: { ...floatingStyles, zIndex: FLOATING_Z_INDEX },
226
+ ...getFloatingProps(),
227
+ onPointerDown: (e) => e.preventDefault(),
228
+ children: /* @__PURE__ */ jsx(
229
+ Command,
230
+ {
231
+ disablePointerSelection: true,
232
+ label: "Slash command menu",
233
+ shouldFilter: false,
234
+ value: selectedValue,
235
+ onValueChange: setSelectedValue,
236
+ children: /* @__PURE__ */ jsx(
237
+ CommandList,
238
+ {
239
+ style: {
240
+ maxHeight: "var(--suggestion-menu-max-height)"
241
+ },
242
+ children: grouped.map((group, groupIndex) => /* @__PURE__ */ jsxs(Fragment, { children: [
243
+ groupIndex > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
244
+ /* @__PURE__ */ jsx(CommandGroup, { heading: group.label || void 0, children: group.items.map((item) => {
245
+ const Badge = item.badge;
246
+ return /* @__PURE__ */ jsxs(
247
+ CommandItem,
248
+ {
249
+ value: item.title,
250
+ onSelect: () => commandRef.current?.(item),
251
+ children: [
252
+ Badge && /* @__PURE__ */ jsx(Badge, { className: "nt:size-4" }),
253
+ /* @__PURE__ */ jsx("span", { className: "nt:flex-1 nt:text-left", children: item.title })
254
+ ]
255
+ },
256
+ item.title
257
+ );
258
+ }) })
259
+ ] }, `${group.label}-${groupIndex}`))
260
+ }
261
+ )
262
+ }
263
+ )
264
+ }
265
+ ),
266
+ imageAnchor && editor && /* @__PURE__ */ jsx(
267
+ SlashImagePopover,
268
+ {
269
+ anchorRect: imageAnchor,
270
+ editor,
271
+ onClose: () => setImageAnchor(null)
272
+ }
273
+ )
274
+ ] });
275
+ }
276
+ function groupByLabel(items) {
277
+ const result = [];
278
+ for (const item of items) {
279
+ const label = item.group ?? "";
280
+ const last = result[result.length - 1];
281
+ if (last && last.label === label) {
282
+ last.items.push(item);
283
+ } else {
284
+ result.push({ label, items: [item] });
285
+ }
286
+ }
287
+ return result;
288
+ }
289
+ export {
290
+ SlashDropdownMenu
291
+ };
292
+ //# sourceMappingURL=slash-dropdown-menu.mjs.map