@openeditor/ui 0.0.34 → 0.0.35

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/index.js CHANGED
@@ -2,7 +2,8 @@ import { Menu } from '@base-ui/react/menu';
2
2
  import { Toolbar } from '@base-ui/react/toolbar';
3
3
  import { useOpenEditorThemeStyle, OpenEditorTableBubbleMenu, OpenEditorBubbleMenu, sortSlashMenuItems, getDefaultSlashMenuItems, useOpenEditorBlockInteraction, OpenEditorBlockDragHandle, useOpenEditorController, OpenEditorThemeProvider, OpenEditorContent } from '@openeditor/react';
4
4
  export { OpenEditorContent, OpenEditorThemeProvider, createOpenEditorThemeStyle, openEditorThemeTokenNames, useOpenEditorController } from '@openeditor/react';
5
- import { Pilcrow, List, ListOrdered, ListTodo, Quote, Code, Heading1, Heading2, Heading3, Heading4, Heading5, Heading6, Bold, Italic, Underline, Strikethrough, Link, GripVertical, Rows3, BetweenHorizontalStart, BetweenHorizontalEnd, Trash2, Columns3, BetweenVerticalStart, BetweenVerticalEnd, PanelTop, PanelLeft, TableCellsSplit, TableCellsMerge, ChevronDown, ChevronRight, X, ArrowDown, ArrowUp, Upload, FileText, GitBranch, MessageSquare, Table, Columns2, Image, Minus, Type, Copy, CopyPlus } from 'lucide-react';
5
+ import { HugeiconsIcon } from '@hugeicons/react';
6
+ import { openEditorIcons } from '@openeditor/icons';
6
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
8
  export { OpenEditorEmojiPicker } from '@openeditor/emoji';
8
9
  import { Button } from '@base-ui/react/button';
@@ -11,7 +12,7 @@ import { Input } from '@base-ui/react/input';
11
12
  import { Popover } from '@base-ui/react/popover';
12
13
  import { Toggle } from '@base-ui/react/toggle';
13
14
  import { ToggleGroup } from '@base-ui/react/toggle-group';
14
- import { useState, useRef, useEffect, useId, useMemo, useCallback, Fragment as Fragment$1 } from 'react';
15
+ import { useState, useRef, useEffect, useId, useMemo, useCallback } from 'react';
15
16
 
16
17
  // src/table-menu.tsx
17
18
  var runTableCommandOnMouseDown = (event, controller, command) => {
@@ -26,7 +27,7 @@ var TableActionMenu = ({
26
27
  controller,
27
28
  container,
28
29
  label,
29
- Icon,
30
+ icon,
30
31
  actions
31
32
  }) => {
32
33
  const themeStyle = useOpenEditorThemeStyle();
@@ -39,9 +40,9 @@ var TableActionMenu = ({
39
40
  onMouseDown: (event) => event.preventDefault(),
40
41
  type: "button",
41
42
  children: [
42
- /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true", size: 15, strokeWidth: 2 }),
43
+ /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", icon, size: 15, strokeWidth: 2 }),
43
44
  /* @__PURE__ */ jsx("span", { children: label }),
44
- /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": "true", size: 12, strokeWidth: 2 })
45
+ /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", icon: openEditorIcons.chevronDown, size: 12, strokeWidth: 2 })
45
46
  ]
46
47
  }
47
48
  ),
@@ -63,7 +64,7 @@ var TableActionMenu = ({
63
64
  onClick: (event) => runTableCommandOnKeyboardClick(event, controller, action.command),
64
65
  onMouseDown: (event) => runTableCommandOnMouseDown(event, controller, action.command),
65
66
  children: [
66
- /* @__PURE__ */ jsx(action.Icon, { "aria-hidden": "true", size: 15, strokeWidth: 2 }),
67
+ /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", icon: action.icon, size: 15, strokeWidth: 2 }),
67
68
  /* @__PURE__ */ jsx("span", { children: action.label })
68
69
  ]
69
70
  },
@@ -96,14 +97,14 @@ var TableToolbarButton = ({
96
97
  }
97
98
  );
98
99
  var rowActions = [
99
- { command: "addRowBefore", label: "Insert row above", Icon: BetweenHorizontalStart },
100
- { command: "addRowAfter", label: "Insert row below", Icon: BetweenHorizontalEnd },
101
- { command: "deleteRow", label: "Delete row", Icon: Trash2, danger: true }
100
+ { command: "addRowBefore", label: "Insert row above", icon: openEditorIcons.tableRowInsertBefore },
101
+ { command: "addRowAfter", label: "Insert row below", icon: openEditorIcons.tableRowInsertAfter },
102
+ { command: "deleteRow", label: "Delete row", icon: openEditorIcons.tableRowDelete, danger: true }
102
103
  ];
103
104
  var columnActions = [
104
- { command: "addColumnBefore", label: "Insert column left", Icon: BetweenVerticalStart },
105
- { command: "addColumnAfter", label: "Insert column right", Icon: BetweenVerticalEnd },
106
- { command: "deleteColumn", label: "Delete column", Icon: Trash2, danger: true }
105
+ { command: "addColumnBefore", label: "Insert column left", icon: openEditorIcons.tableColumnInsertBefore },
106
+ { command: "addColumnAfter", label: "Insert column right", icon: openEditorIcons.tableColumnInsertAfter },
107
+ { command: "deleteColumn", label: "Delete column", icon: openEditorIcons.tableColumnDelete, danger: true }
107
108
  ];
108
109
  var TableToolbar = ({
109
110
  controller,
@@ -116,7 +117,7 @@ var TableToolbar = ({
116
117
  actions: rowActions,
117
118
  container,
118
119
  controller,
119
- Icon: Rows3,
120
+ icon: openEditorIcons.tableRow,
120
121
  label: "Row"
121
122
  }
122
123
  ),
@@ -126,7 +127,7 @@ var TableToolbar = ({
126
127
  actions: columnActions,
127
128
  container,
128
129
  controller,
129
- Icon: Columns3,
130
+ icon: openEditorIcons.tableColumn,
130
131
  label: "Column"
131
132
  }
132
133
  ),
@@ -138,7 +139,7 @@ var TableToolbar = ({
138
139
  command: "toggleHeaderRow",
139
140
  controller,
140
141
  label: state.headerRow ? "Remove header row" : "Make first row a header",
141
- children: /* @__PURE__ */ jsx(PanelTop, { "aria-hidden": "true", size: 15, strokeWidth: 2 })
142
+ children: /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", icon: openEditorIcons.tableHeaderRow, size: 15, strokeWidth: 2 })
142
143
  }
143
144
  ),
144
145
  /* @__PURE__ */ jsx(
@@ -148,12 +149,11 @@ var TableToolbar = ({
148
149
  command: "toggleHeaderColumn",
149
150
  controller,
150
151
  label: state.headerColumn ? "Remove header column" : "Make first column a header",
151
- children: /* @__PURE__ */ jsx(PanelLeft, { "aria-hidden": "true", size: 15, strokeWidth: 2 })
152
+ children: /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", icon: openEditorIcons.tableHeaderColumn, size: 15, strokeWidth: 2 })
152
153
  }
153
154
  ),
154
- state.canSplitCell ? /* @__PURE__ */ jsx(TableToolbarButton, { command: "splitCell", controller, label: "Split cell", children: /* @__PURE__ */ jsx(TableCellsSplit, { "aria-hidden": "true", size: 15, strokeWidth: 2 }) }) : /* @__PURE__ */ jsx(TableToolbarButton, { command: "mergeCells", controller, label: "Merge selected cells", children: /* @__PURE__ */ jsx(TableCellsMerge, { "aria-hidden": "true", size: 15, strokeWidth: 2 }) }),
155
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "oe-table-toolbar-separator" }),
156
- /* @__PURE__ */ jsx(TableToolbarButton, { command: "deleteTable", controller, danger: true, label: "Delete table", children: /* @__PURE__ */ jsx(Trash2, { "aria-hidden": "true", size: 15, strokeWidth: 2 }) })
155
+ state.canSplitCell ? /* @__PURE__ */ jsx(TableToolbarButton, { command: "splitCell", controller, label: "Split cell", children: /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", icon: openEditorIcons.splitCells, size: 15, strokeWidth: 2 }) }) : /* @__PURE__ */ jsx(TableToolbarButton, { command: "mergeCells", controller, label: "Merge selected cells", children: /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", icon: openEditorIcons.mergeCells, size: 15, strokeWidth: 2 }) }),
156
+ /* @__PURE__ */ jsx(TableToolbarButton, { command: "deleteTable", controller, danger: true, label: "Delete table", children: /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", icon: openEditorIcons.delete, size: 15, strokeWidth: 2 }) })
157
157
  ] }) });
158
158
  var OpenEditorTableMenu = ({
159
159
  controller,
@@ -174,65 +174,81 @@ var OpenEditorTableMenu = ({
174
174
  );
175
175
  };
176
176
  var headingDefinitions = [
177
- { level: 1, Icon: Heading1 },
178
- { level: 2, Icon: Heading2 },
179
- { level: 3, Icon: Heading3 },
180
- { level: 4, Icon: Heading4 },
181
- { level: 5, Icon: Heading5 },
182
- { level: 6, Icon: Heading6 }
177
+ { level: 1, icon: openEditorIcons.heading1 },
178
+ { level: 2, icon: openEditorIcons.heading2 },
179
+ { level: 3, icon: openEditorIcons.heading3 },
180
+ { level: 4, icon: openEditorIcons.heading4 },
181
+ { level: 5, icon: openEditorIcons.heading5 },
182
+ { level: 6, icon: openEditorIcons.heading6 }
183
183
  ];
184
184
  var blockTransformActions = [
185
185
  {
186
186
  key: "paragraph",
187
- label: "Paragraph",
188
- Icon: Pilcrow,
187
+ label: "Text",
188
+ icon: openEditorIcons.text,
189
189
  isActive: (controller) => controller.isActive("paragraph"),
190
190
  run: (controller) => controller.setParagraph()
191
191
  },
192
- ...headingDefinitions.map(({ level, Icon }) => ({
192
+ ...headingDefinitions.map(({ level, icon }) => ({
193
193
  key: `heading${level}`,
194
194
  label: `Heading ${level}`,
195
- Icon,
195
+ icon,
196
196
  isActive: (controller) => controller.isActive("heading", { level }),
197
197
  run: (controller) => controller.toggleHeading(level)
198
198
  })),
199
199
  {
200
200
  key: "bulletList",
201
201
  label: "Bullet List",
202
- Icon: List,
202
+ icon: openEditorIcons.bulletList,
203
203
  isActive: (controller) => controller.isActive("bulletList"),
204
204
  run: (controller) => controller.toggleBulletList()
205
205
  },
206
206
  {
207
207
  key: "orderedList",
208
208
  label: "Numbered List",
209
- Icon: ListOrdered,
209
+ icon: openEditorIcons.orderedList,
210
210
  isActive: (controller) => controller.isActive("orderedList"),
211
211
  run: (controller) => controller.toggleOrderedList()
212
212
  },
213
213
  {
214
214
  key: "taskList",
215
215
  label: "Task List",
216
- Icon: ListTodo,
216
+ icon: openEditorIcons.taskList,
217
217
  isActive: (controller) => controller.isActive("taskList"),
218
218
  run: (controller) => controller.toggleTaskList()
219
219
  },
220
+ {
221
+ key: "toggleList",
222
+ label: "Toggle List",
223
+ icon: openEditorIcons.toggleList,
224
+ isActive: (controller) => controller.isActive("toggleList"),
225
+ run: (controller) => controller.toggleToggleList()
226
+ },
220
227
  {
221
228
  key: "blockquote",
222
229
  label: "Quote",
223
- Icon: Quote,
230
+ icon: openEditorIcons.blockquote,
224
231
  isActive: (controller) => controller.isActive("blockquote"),
225
232
  run: (controller) => controller.toggleBlockquote()
226
233
  },
234
+ {
235
+ key: "callout",
236
+ label: "Callout",
237
+ icon: openEditorIcons.callout,
238
+ isActive: (controller) => controller.isActive("callout"),
239
+ run: (controller) => controller.toggleCallout()
240
+ },
227
241
  {
228
242
  key: "codeBlock",
229
243
  label: "Code",
230
- Icon: Code,
244
+ icon: openEditorIcons.codeBlock,
231
245
  isActive: (controller) => controller.isActive("codeBlock"),
232
246
  run: (controller) => controller.toggleCodeBlock()
233
247
  }
234
248
  ];
235
249
  var getActiveBlockTransform = (controller) => [
250
+ "callout",
251
+ "toggleList",
236
252
  "taskList",
237
253
  "bulletList",
238
254
  "orderedList",
@@ -248,26 +264,30 @@ var getActiveBlockTransform = (controller) => [
248
264
  ].map((key) => blockTransformActions.find((action) => action.key === key)).find((action) => action?.isActive(controller)) ?? blockTransformActions[0];
249
265
  var isHeadingKey = (key) => /^heading[1-6]$/.test(key);
250
266
  var slashMenuIcons = {
251
- paragraph: Pilcrow,
252
- ...Object.fromEntries(headingDefinitions.map(({ level, Icon }) => [`heading${level}`, Icon])),
253
- bulletList: List,
254
- orderedList: ListOrdered,
255
- taskList: ListTodo,
256
- blockquote: Quote,
257
- codeBlock: Code,
258
- divider: Minus,
259
- image: Image,
260
- columns: Columns2,
261
- table: Table,
262
- toggleList: ChevronRight,
263
- callout: MessageSquare,
264
- diagram: GitBranch,
265
- page: FileText,
266
- attachment: Upload,
267
- moveBlockUp: ArrowUp,
268
- moveBlockDown: ArrowDown
267
+ paragraph: openEditorIcons.text,
268
+ ...Object.fromEntries(headingDefinitions.map(({ level, icon }) => [`heading${level}`, icon])),
269
+ bulletList: openEditorIcons.bulletList,
270
+ orderedList: openEditorIcons.orderedList,
271
+ taskList: openEditorIcons.taskList,
272
+ blockquote: openEditorIcons.blockquote,
273
+ codeBlock: openEditorIcons.codeBlock,
274
+ divider: openEditorIcons.divider,
275
+ image: openEditorIcons.image,
276
+ columns: openEditorIcons.columns,
277
+ table: openEditorIcons.table,
278
+ toggleList: openEditorIcons.toggleList,
279
+ callout: openEditorIcons.callout,
280
+ diagram: openEditorIcons.diagram,
281
+ page: openEditorIcons.page,
282
+ attachment: openEditorIcons.attachment
283
+ };
284
+ var isSlashMenuGroup = (item) => "children" in item;
285
+ var getSlashMenuIcon = (item) => slashMenuIcons[item.key] ?? (isSlashMenuGroup(item) ? openEditorIcons.heading1 : openEditorIcons.text);
286
+ var slashItemMatches = (item, query) => {
287
+ const normalized = query.toLowerCase();
288
+ const ownText = `${item.label} ${item.group} ${item.keywords?.join(" ") ?? ""}`.toLowerCase();
289
+ return ownText.includes(normalized) || isSlashMenuGroup(item) && item.children.some((child) => slashItemMatches(child, query));
269
290
  };
270
- var getSlashMenuIcon = (item) => item.icon ?? slashMenuIcons[item.key] ?? Type;
271
291
  var pointRect = (left, top) => ({
272
292
  left,
273
293
  right: left,
@@ -326,7 +346,7 @@ var OpenEditorLinkDialog = ({
326
346
  return /* @__PURE__ */ jsx(Dialog.Root, { open, onOpenChange, children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [
327
347
  /* @__PURE__ */ jsx(Dialog.Backdrop, { className: "oe-dialog-backdrop" }),
328
348
  /* @__PURE__ */ jsxs(Dialog.Popup, { className: "oe-dialog", initialFocus: false, style: themeStyle, children: [
329
- /* @__PURE__ */ jsx(Dialog.Close, { className: "oe-dialog-close", "aria-label": "Close link dialog", children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true", size: 15, strokeWidth: 2 }) }),
349
+ /* @__PURE__ */ jsx(Dialog.Close, { className: "oe-dialog-close", "aria-label": "Close link dialog", children: /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", icon: openEditorIcons.close, size: 15, strokeWidth: 2 }) }),
330
350
  /* @__PURE__ */ jsxs("form", { className: "oe-link-dialog-form", onSubmit: submitLink, children: [
331
351
  /* @__PURE__ */ jsx(Dialog.Title, { className: "oe-dialog-title", children: "Link" }),
332
352
  /* @__PURE__ */ jsx(
@@ -370,7 +390,7 @@ var SelectionButton = ({
370
390
  className: "oe-toolbar-button",
371
391
  "data-active": action.active ? "" : void 0,
372
392
  onClick: action.run,
373
- children: /* @__PURE__ */ jsx(action.Icon, { "aria-hidden": "true", className: "oe-toolbar-icon", size: 15, strokeWidth: 2 })
393
+ children: /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", className: "oe-toolbar-icon", icon: action.icon, size: 15, strokeWidth: 2 })
374
394
  }
375
395
  );
376
396
  var SelectionBlockTransformMenu = ({
@@ -400,9 +420,9 @@ var SelectionBlockTransformMenu = ({
400
420
  className: "oe-toolbar-button oe-block-transform-trigger",
401
421
  onMouseDown: (event) => event.preventDefault(),
402
422
  children: [
403
- /* @__PURE__ */ jsx(activeAction.Icon, { "aria-hidden": "true", className: "oe-toolbar-icon", size: 15, strokeWidth: 2 }),
423
+ /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", className: "oe-toolbar-icon", icon: activeAction.icon, size: 15, strokeWidth: 2 }),
404
424
  /* @__PURE__ */ jsx("span", { className: "oe-block-transform-trigger-label", children: activeAction.label }),
405
- /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": "true", className: "oe-toolbar-chevron", size: 12, strokeWidth: 2 })
425
+ /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", className: "oe-toolbar-chevron", icon: openEditorIcons.chevronDown, size: 12, strokeWidth: 2 })
406
426
  ]
407
427
  }
408
428
  ),
@@ -436,9 +456,9 @@ var SelectionBlockTransformMenu = ({
436
456
  onFocus: () => setHeadingPanelOpen(true),
437
457
  onMouseEnter: () => setHeadingPanelOpen(true),
438
458
  children: [
439
- /* @__PURE__ */ jsx(Heading1, { "aria-hidden": "true", className: "oe-block-transform-icon", size: 15, strokeWidth: 2 }),
459
+ /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", className: "oe-block-transform-icon", icon: openEditorIcons.heading1, size: 15, strokeWidth: 2 }),
440
460
  /* @__PURE__ */ jsx("span", { children: "Heading" }),
441
- /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": "true", className: "oe-submenu-chevron", size: 13, strokeWidth: 2 })
461
+ /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", className: "oe-submenu-chevron", icon: openEditorIcons.chevronRight, size: 13, strokeWidth: 2 })
442
462
  ]
443
463
  },
444
464
  "heading"
@@ -456,7 +476,7 @@ var SelectionBlockTransformMenu = ({
456
476
  if (!active) action.run(controller);
457
477
  },
458
478
  children: [
459
- /* @__PURE__ */ jsx(action.Icon, { "aria-hidden": "true", className: "oe-block-transform-icon", size: 15, strokeWidth: 2 }),
479
+ /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", className: "oe-block-transform-icon", icon: action.icon, size: 15, strokeWidth: 2 }),
460
480
  /* @__PURE__ */ jsx("span", { children: action.label })
461
481
  ]
462
482
  },
@@ -478,7 +498,7 @@ var SelectionBlockTransformMenu = ({
478
498
  },
479
499
  role: "menuitem",
480
500
  children: [
481
- /* @__PURE__ */ jsx(headingAction.Icon, { "aria-hidden": "true", className: "oe-block-transform-icon", size: 15, strokeWidth: 2 }),
501
+ /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", className: "oe-block-transform-icon", icon: headingAction.icon, size: 15, strokeWidth: 2 }),
482
502
  /* @__PURE__ */ jsx("span", { children: headingAction.label })
483
503
  ]
484
504
  },
@@ -508,35 +528,35 @@ var OpenEditorSelectionBubble = ({
508
528
  key: "bold",
509
529
  label: "Bold",
510
530
  active: controller.isActive("bold"),
511
- Icon: Bold,
531
+ icon: openEditorIcons.bold,
512
532
  run: () => controller.toggleBold()
513
533
  },
514
534
  {
515
535
  key: "italic",
516
536
  label: "Italic",
517
537
  active: controller.isActive("italic"),
518
- Icon: Italic,
538
+ icon: openEditorIcons.italic,
519
539
  run: () => controller.toggleItalic()
520
540
  },
521
541
  {
522
542
  key: "underline",
523
543
  label: "Underline",
524
544
  active: controller.isActive("underline"),
525
- Icon: Underline,
545
+ icon: openEditorIcons.underline,
526
546
  run: () => controller.toggleUnderline()
527
547
  },
528
548
  {
529
549
  key: "strike",
530
550
  label: "Strikethrough",
531
551
  active: controller.isActive("strike"),
532
- Icon: Strikethrough,
552
+ icon: openEditorIcons.strike,
533
553
  run: () => controller.toggleStrike()
534
554
  },
535
555
  {
536
556
  key: "code",
537
557
  label: "Inline code",
538
558
  active: controller.isActive("code"),
539
- Icon: Code,
559
+ icon: openEditorIcons.code,
540
560
  run: () => controller.toggleInlineCode()
541
561
  }
542
562
  ] : [];
@@ -592,7 +612,7 @@ var OpenEditorSelectionBubble = ({
592
612
  className: "oe-toolbar-button",
593
613
  "data-active": controller.isActive("link") ? "" : void 0,
594
614
  onClick: () => setLinkDialogOpen(true),
595
- children: /* @__PURE__ */ jsx(Link, { "aria-hidden": "true", className: "oe-toolbar-icon", size: 15, strokeWidth: 2 })
615
+ children: /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", className: "oe-toolbar-icon", icon: openEditorIcons.link, size: 15, strokeWidth: 2 })
596
616
  }
597
617
  )
598
618
  ]
@@ -622,6 +642,7 @@ var OpenEditorSlashMenu = ({
622
642
  const slashMenuItems = sortSlashMenuItems(items ?? getDefaultSlashMenuItems(controller));
623
643
  const slashState = controller.slashState;
624
644
  const [selectedIndex, setSelectedIndex] = useState(0);
645
+ const [openGroupKey, setOpenGroupKey] = useState(null);
625
646
  const selectedIndexRef = useRef(0);
626
647
  const menuId = useId();
627
648
  const menuRef = useRef(null);
@@ -633,19 +654,25 @@ var OpenEditorSlashMenu = ({
633
654
  ) : null,
634
655
  [controller, slashState]
635
656
  );
636
- const matchingItems = slashState ? slashMenuItems.filter((item) => {
637
- const haystack = `${item.label} ${item.group} ${item.keywords?.join(" ") ?? ""}`.toLowerCase();
638
- return haystack.includes(slashState.query.toLowerCase());
639
- }) : [];
657
+ const matchingItems = slashState ? slashMenuItems.filter((item) => slashItemMatches(item, slashState.query)) : [];
658
+ const openGroup = matchingItems.find(
659
+ (item) => isSlashMenuGroup(item) && item.key === openGroupKey
660
+ );
661
+ const matchingChildren = openGroup ? openGroup.children.filter((item) => slashItemMatches(item, slashState?.query ?? "")) : [];
662
+ const activeItems = openGroup ? matchingChildren : matchingItems;
640
663
  selectedIndexRef.current = selectedIndex;
641
664
  useEffect(() => {
642
665
  selectedIndexRef.current = 0;
643
666
  setSelectedIndex(0);
667
+ setOpenGroupKey(null);
644
668
  }, [slashState?.from, slashState?.query]);
645
669
  useEffect(() => {
646
- if (selectedIndex < matchingItems.length) return;
647
- setSelectedIndex(Math.max(0, matchingItems.length - 1));
648
- }, [matchingItems.length, selectedIndex]);
670
+ if (selectedIndex < activeItems.length) return;
671
+ setSelectedIndex(Math.max(0, activeItems.length - 1));
672
+ }, [activeItems.length, selectedIndex]);
673
+ useEffect(() => {
674
+ if (slashState && matchingItems.length === 0) controller.dismissSlashMenu();
675
+ }, [controller, matchingItems.length, slashState]);
649
676
  useEffect(() => {
650
677
  const editorElement = controller.getRootElement();
651
678
  if (!slashState || !editorElement) return;
@@ -654,7 +681,7 @@ var OpenEditorSlashMenu = ({
654
681
  expanded: editorElement.getAttribute("aria-expanded"),
655
682
  activeDescendant: editorElement.getAttribute("aria-activedescendant")
656
683
  };
657
- const selectedId = matchingItems[selectedIndex] ? `${menuId}-option-${selectedIndex}` : null;
684
+ const selectedId = activeItems[selectedIndex] ? `${menuId}-${openGroup ? openGroup.key : "root"}-option-${selectedIndex}` : null;
658
685
  editorElement.setAttribute("aria-controls", menuId);
659
686
  editorElement.setAttribute("aria-expanded", "true");
660
687
  if (selectedId) editorElement.setAttribute("aria-activedescendant", selectedId);
@@ -663,21 +690,43 @@ var OpenEditorSlashMenu = ({
663
690
  if (event.isComposing) return false;
664
691
  let nextIndex = null;
665
692
  const currentIndex = selectedIndexRef.current;
666
- if (event.key === "ArrowDown") nextIndex = (currentIndex + 1) % Math.max(1, matchingItems.length);
667
- if (event.key === "ArrowUp") nextIndex = (currentIndex - 1 + Math.max(1, matchingItems.length)) % Math.max(1, matchingItems.length);
693
+ if (event.key === "ArrowDown") nextIndex = (currentIndex + 1) % Math.max(1, activeItems.length);
694
+ if (event.key === "ArrowUp") nextIndex = (currentIndex - 1 + Math.max(1, activeItems.length)) % Math.max(1, activeItems.length);
668
695
  if (event.key === "Home" || event.key === "PageUp") nextIndex = 0;
669
- if (event.key === "End" || event.key === "PageDown") nextIndex = Math.max(0, matchingItems.length - 1);
696
+ if (event.key === "End" || event.key === "PageDown") nextIndex = Math.max(0, activeItems.length - 1);
670
697
  if (nextIndex !== null) {
671
698
  event.preventDefault();
672
699
  selectedIndexRef.current = nextIndex;
673
700
  setSelectedIndex(nextIndex);
674
701
  return true;
675
702
  }
703
+ if ((event.key === "ArrowLeft" || event.key === "Escape") && openGroup) {
704
+ event.preventDefault();
705
+ setOpenGroupKey(null);
706
+ selectedIndexRef.current = 0;
707
+ setSelectedIndex(0);
708
+ return true;
709
+ }
710
+ if (event.key === "ArrowRight") {
711
+ const item = activeItems[selectedIndexRef.current];
712
+ if (!item || !isSlashMenuGroup(item)) return false;
713
+ event.preventDefault();
714
+ setOpenGroupKey(item.key);
715
+ selectedIndexRef.current = 0;
716
+ setSelectedIndex(0);
717
+ return true;
718
+ }
676
719
  if (event.key === "Enter") {
677
- const item = matchingItems[selectedIndexRef.current];
720
+ const item = activeItems[selectedIndexRef.current];
678
721
  if (!item) return false;
679
722
  event.preventDefault();
680
723
  event.stopPropagation();
724
+ if (isSlashMenuGroup(item)) {
725
+ setOpenGroupKey(item.key);
726
+ selectedIndexRef.current = 0;
727
+ setSelectedIndex(0);
728
+ return true;
729
+ }
681
730
  item.execute({ controller, range: { from: slashState.from, to: slashState.to } });
682
731
  return true;
683
732
  }
@@ -695,11 +744,11 @@ var OpenEditorSlashMenu = ({
695
744
  else editorElement.setAttribute(attribute, value);
696
745
  }
697
746
  };
698
- }, [controller, matchingItems, menuId, selectedIndex, slashState]);
747
+ }, [activeItems, controller, menuId, openGroup, selectedIndex, slashState]);
699
748
  useEffect(() => {
700
749
  menuRef.current?.querySelector("[data-highlighted]")?.scrollIntoView({ block: "nearest" });
701
750
  }, [selectedIndex]);
702
- if (!slashState) return null;
751
+ if (!slashState || matchingItems.length === 0) return null;
703
752
  return /* @__PURE__ */ jsx(Popover.Root, { open: true, modal: false, children: /* @__PURE__ */ jsx(Popover.Portal, { container, children: /* @__PURE__ */ jsx(
704
753
  Popover.Positioner,
705
754
  {
@@ -711,99 +760,180 @@ var OpenEditorSlashMenu = ({
711
760
  sideOffset: 8,
712
761
  collisionPadding: 12,
713
762
  collisionAvoidance: { side: "flip", align: "shift", fallbackAxisSide: "none" },
714
- children: /* @__PURE__ */ jsx(
763
+ children: /* @__PURE__ */ jsxs(
715
764
  Popover.Popup,
716
765
  {
717
766
  className: "oe-cascade-menu",
718
767
  finalFocus: false,
719
768
  style: themeStyle,
720
769
  initialFocus: false,
721
- children: /* @__PURE__ */ jsx(
722
- "div",
723
- {
724
- "aria-label": "Insert block",
725
- className: `${className} oe-cascade-panel`,
726
- id: menuId,
727
- ref: menuRef,
728
- role: "listbox",
729
- children: /* @__PURE__ */ jsx("div", { className: "oe-slash-menu-scroll", children: matchingItems.map((item, index) => {
730
- const Icon = getSlashMenuIcon(item);
731
- return /* @__PURE__ */ jsxs(
732
- "button",
733
- {
734
- "aria-selected": index === selectedIndex,
735
- type: "button",
736
- className: "oe-slash-menu-item",
737
- "data-highlighted": index === selectedIndex ? "" : void 0,
738
- id: `${menuId}-option-${index}`,
739
- onMouseEnter: () => setSelectedIndex(index),
740
- onMouseDown: (event) => event.preventDefault(),
741
- onClick: () => {
742
- item.execute({ controller, range: { from: slashState.from, to: slashState.to } });
770
+ children: [
771
+ /* @__PURE__ */ jsx(
772
+ "div",
773
+ {
774
+ "aria-label": "Insert block",
775
+ className: `${className} oe-cascade-panel`,
776
+ id: menuId,
777
+ ref: menuRef,
778
+ role: "listbox",
779
+ children: /* @__PURE__ */ jsx("div", { className: "oe-slash-menu-scroll", children: matchingItems.map((item, index) => {
780
+ const CustomIcon = item.icon;
781
+ const icon = getSlashMenuIcon(item);
782
+ return /* @__PURE__ */ jsxs(
783
+ "button",
784
+ {
785
+ "aria-selected": !openGroup && index === selectedIndex,
786
+ type: "button",
787
+ className: "oe-slash-menu-item",
788
+ "data-highlighted": !openGroup && index === selectedIndex ? "" : void 0,
789
+ id: `${menuId}-root-option-${index}`,
790
+ onMouseEnter: () => {
791
+ selectedIndexRef.current = index;
792
+ setSelectedIndex(index);
793
+ setOpenGroupKey(isSlashMenuGroup(item) ? item.key : null);
794
+ },
795
+ onMouseDown: (event) => event.preventDefault(),
796
+ onClick: () => {
797
+ if (isSlashMenuGroup(item)) {
798
+ setOpenGroupKey(item.key);
799
+ selectedIndexRef.current = 0;
800
+ setSelectedIndex(0);
801
+ } else {
802
+ item.execute({ controller, range: { from: slashState.from, to: slashState.to } });
803
+ }
804
+ },
805
+ role: "option",
806
+ children: [
807
+ CustomIcon ? /* @__PURE__ */ jsx(CustomIcon, { "aria-hidden": true, className: "oe-slash-menu-icon", size: 15, strokeWidth: 2 }) : /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", className: "oe-slash-menu-icon", icon, size: 15, strokeWidth: 2 }),
808
+ /* @__PURE__ */ jsx("span", { children: item.label }),
809
+ isSlashMenuGroup(item) ? /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", className: "oe-submenu-chevron", icon: openEditorIcons.chevronRight, size: 13, strokeWidth: 2 }) : null
810
+ ]
743
811
  },
744
- role: "option",
745
- children: [
746
- /* @__PURE__ */ jsx(Icon, { "aria-hidden": true, className: "oe-slash-menu-icon", size: 15, strokeWidth: 2 }),
747
- /* @__PURE__ */ jsx("span", { children: item.label })
748
- ]
749
- },
750
- item.key
751
- );
752
- }) })
753
- }
754
- )
812
+ item.key
813
+ );
814
+ }) })
815
+ }
816
+ ),
817
+ openGroup ? /* @__PURE__ */ jsx(
818
+ "div",
819
+ {
820
+ "aria-label": openGroup.label,
821
+ className: `${className} oe-cascade-panel oe-block-transform-submenu`,
822
+ role: "listbox",
823
+ children: /* @__PURE__ */ jsx("div", { className: "oe-slash-menu-scroll", children: matchingChildren.map((item, index) => {
824
+ const CustomIcon = item.icon;
825
+ const icon = getSlashMenuIcon(item);
826
+ return /* @__PURE__ */ jsxs(
827
+ "button",
828
+ {
829
+ "aria-selected": index === selectedIndex,
830
+ className: "oe-slash-menu-item",
831
+ "data-highlighted": index === selectedIndex ? "" : void 0,
832
+ id: `${menuId}-${openGroup.key}-option-${index}`,
833
+ onClick: () => item.execute({ controller, range: { from: slashState.from, to: slashState.to } }),
834
+ onMouseDown: (event) => event.preventDefault(),
835
+ onMouseEnter: () => {
836
+ selectedIndexRef.current = index;
837
+ setSelectedIndex(index);
838
+ },
839
+ role: "option",
840
+ type: "button",
841
+ children: [
842
+ CustomIcon ? /* @__PURE__ */ jsx(CustomIcon, { "aria-hidden": true, className: "oe-slash-menu-icon", size: 15, strokeWidth: 2 }) : /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", className: "oe-slash-menu-icon", icon, size: 15, strokeWidth: 2 }),
843
+ /* @__PURE__ */ jsx("span", { children: item.label })
844
+ ]
845
+ },
846
+ item.key
847
+ );
848
+ }) })
849
+ }
850
+ ) : null
851
+ ]
755
852
  }
756
853
  )
757
854
  }
758
855
  ) }) });
759
856
  };
760
- var builtInBlockActions = (controller, block) => [
857
+ var createOpenEditorBuiltInBlockMenuActions = (handlers) => [
761
858
  {
762
859
  id: "move-up",
763
860
  label: "Move up",
764
861
  group: "move",
765
- Icon: ArrowUp,
766
- disabled: () => !controller.canMoveBlock(-1, block),
767
- run: () => {
768
- controller.moveBlock(-1, block);
769
- }
862
+ icon: openEditorIcons.moveUp,
863
+ disabled: !handlers.canMove(-1),
864
+ run: () => handlers.move(-1)
770
865
  },
771
866
  {
772
867
  id: "move-down",
773
868
  label: "Move down",
774
869
  group: "move",
775
- Icon: ArrowDown,
776
- disabled: () => !controller.canMoveBlock(1, block),
777
- run: () => {
778
- controller.moveBlock(1, block);
779
- }
870
+ icon: openEditorIcons.moveDown,
871
+ disabled: !handlers.canMove(1),
872
+ run: () => handlers.move(1)
780
873
  },
781
874
  {
782
875
  id: "copy",
783
876
  label: "Copy block",
784
877
  group: "edit",
785
- Icon: Copy,
786
- run: () => controller.copyBlock(block)
878
+ icon: openEditorIcons.copy,
879
+ run: handlers.copy
787
880
  },
788
881
  {
789
882
  id: "duplicate",
790
883
  label: "Duplicate",
791
884
  group: "edit",
792
- Icon: CopyPlus,
793
- run: () => {
794
- controller.duplicateBlock(block);
795
- }
885
+ icon: openEditorIcons.duplicate,
886
+ run: handlers.duplicate
796
887
  },
797
888
  {
798
889
  id: "delete",
799
890
  label: "Delete",
800
891
  group: "danger",
801
- Icon: Trash2,
802
- run: () => {
803
- controller.deleteBlock(block);
804
- }
892
+ icon: openEditorIcons.delete,
893
+ run: handlers.delete
805
894
  }
806
895
  ];
896
+ var OpenEditorBlockMenuPopup = ({
897
+ actions,
898
+ anchor,
899
+ ariaLabel,
900
+ container,
901
+ onOpenChange,
902
+ open,
903
+ style,
904
+ trigger
905
+ }) => /* @__PURE__ */ jsxs(Menu.Root, { modal: false, open, onOpenChange, children: [
906
+ trigger,
907
+ open ? /* @__PURE__ */ jsx(Menu.Portal, { container, children: /* @__PURE__ */ jsx(
908
+ Menu.Positioner,
909
+ {
910
+ align: "start",
911
+ anchor,
912
+ collisionAvoidance: { side: "flip", align: "shift", fallbackAxisSide: "none" },
913
+ collisionPadding: 12,
914
+ positionMethod: "fixed",
915
+ side: "bottom",
916
+ sideOffset: 8,
917
+ children: /* @__PURE__ */ jsx(Menu.Popup, { "aria-label": ariaLabel, className: "oe-block-menu", style, children: actions.map((action) => /* @__PURE__ */ jsxs(
918
+ Menu.Item,
919
+ {
920
+ className: "oe-block-menu-item",
921
+ "data-danger": action.group === "danger" ? "" : void 0,
922
+ disabled: action.disabled,
923
+ onClick: () => {
924
+ onOpenChange(false);
925
+ void action.run();
926
+ },
927
+ children: [
928
+ action.icon ? /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", icon: action.icon, size: 15, strokeWidth: 2 }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "oe-block-menu-icon-placeholder" }),
929
+ /* @__PURE__ */ jsx("span", { children: action.label })
930
+ ]
931
+ },
932
+ action.id
933
+ )) })
934
+ }
935
+ ) }) : null
936
+ ] });
807
937
  var OpenEditorBlockMenu = ({
808
938
  controller,
809
939
  className,
@@ -820,22 +950,15 @@ var OpenEditorBlockMenu = ({
820
950
  if (block) lastBlockRef.current = block;
821
951
  const activeBlock = block ?? (open ? lastBlockRef.current : null);
822
952
  const setMenuOpen = useCallback((nextOpen) => {
823
- const selected = lastBlockRef.current;
824
953
  if (nextOpen) {
825
- if (!selected || !controllerRef.current.selectBlock(selected)) {
954
+ if (!lastBlockRef.current) {
826
955
  setOpen(false);
827
- controllerRef.current.setBlockHandleLocked(false);
828
956
  return;
829
957
  }
830
- controllerRef.current.setBlockHandleLocked(true);
831
958
  setOpen(true);
832
959
  return;
833
960
  }
834
961
  setOpen(false);
835
- controllerRef.current.setBlockHandleLocked(false);
836
- }, []);
837
- useEffect(() => () => {
838
- controllerRef.current.setBlockHandleLocked(false);
839
962
  }, []);
840
963
  if (!controller.editable) return null;
841
964
  const handle = /* @__PURE__ */ jsx(
@@ -852,57 +975,47 @@ var OpenEditorBlockMenu = ({
852
975
  onDraggingChange: (dragging) => {
853
976
  if (dragging) setMenuOpen(false);
854
977
  },
855
- children: activeBlock ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cx("oe-block-handle", className), children: /* @__PURE__ */ jsx(GripVertical, { "aria-hidden": "true", size: 16, strokeWidth: 2 }) }) : null
978
+ children: activeBlock ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cx("oe-block-handle", className), children: /* @__PURE__ */ jsx(HugeiconsIcon, { "aria-hidden": "true", icon: openEditorIcons.dragHandle, size: 16, strokeWidth: 2 }) }) : null
856
979
  }
857
980
  );
858
- const actions = activeBlock ? [...builtInBlockActions(controller, activeBlock), ...controller.blockActions] : [];
859
- return /* @__PURE__ */ jsxs(Menu.Root, { modal: false, open, onOpenChange: setMenuOpen, children: [
860
- handle,
861
- open && activeBlock ? /* @__PURE__ */ jsx(Menu.Portal, { container, children: /* @__PURE__ */ jsx(
862
- Menu.Positioner,
863
- {
864
- align: "start",
865
- anchor: handleElementRef,
866
- collisionAvoidance: { side: "flip", align: "shift", fallbackAxisSide: "none" },
867
- collisionPadding: 12,
868
- positionMethod: "fixed",
869
- side: "bottom",
870
- sideOffset: 8,
871
- children: /* @__PURE__ */ jsx(
872
- Menu.Popup,
873
- {
874
- "aria-label": activeBlock ? `Actions for ${activeBlock.blockName ?? activeBlock.nodeType} block` : "Block actions",
875
- className: "oe-block-menu",
876
- style: themeStyle,
877
- children: actions.map((action, index) => {
878
- const previous = actions[index - 1];
879
- const Icon = action.Icon;
880
- const context = { controller, block: activeBlock };
881
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [
882
- previous && previous.group !== action.group ? /* @__PURE__ */ jsx(Menu.Separator, { className: "oe-block-menu-separator" }) : null,
883
- /* @__PURE__ */ jsxs(
884
- Menu.Item,
885
- {
886
- className: "oe-block-menu-item",
887
- "data-danger": action.group === "danger" ? "" : void 0,
888
- disabled: action.disabled?.(context),
889
- onClick: () => {
890
- setMenuOpen(false);
891
- void action.run(context);
892
- },
893
- children: [
894
- Icon ? /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true", size: 15, strokeWidth: 2 }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "oe-block-menu-icon-placeholder" }),
895
- /* @__PURE__ */ jsx("span", { children: action.label })
896
- ]
897
- }
898
- )
899
- ] }, action.id);
900
- })
901
- }
902
- )
981
+ const actions = activeBlock ? [
982
+ ...createOpenEditorBuiltInBlockMenuActions({
983
+ canMove: (direction) => controller.canMoveBlock(direction, activeBlock),
984
+ move: (direction) => {
985
+ controller.moveBlock(direction, activeBlock);
986
+ },
987
+ copy: async () => {
988
+ await controller.copyBlock(activeBlock);
989
+ },
990
+ duplicate: () => {
991
+ controller.duplicateBlock(activeBlock);
992
+ },
993
+ delete: () => {
994
+ controller.deleteBlock(activeBlock);
903
995
  }
904
- ) }) : null
905
- ] });
996
+ }),
997
+ ...controller.blockActions.map((action) => {
998
+ const context = { controller, block: activeBlock };
999
+ return {
1000
+ ...action,
1001
+ disabled: action.disabled?.(context),
1002
+ run: () => action.run(context)
1003
+ };
1004
+ })
1005
+ ] : [];
1006
+ return /* @__PURE__ */ jsx(
1007
+ OpenEditorBlockMenuPopup,
1008
+ {
1009
+ actions,
1010
+ anchor: handleElementRef,
1011
+ ariaLabel: activeBlock ? `Actions for ${activeBlock.blockName ?? activeBlock.nodeType} block` : "Block actions",
1012
+ container,
1013
+ onOpenChange: setMenuOpen,
1014
+ open: open && Boolean(activeBlock),
1015
+ style: themeStyle,
1016
+ trigger: handle
1017
+ }
1018
+ );
906
1019
  };
907
1020
  var OpenEditor = ({
908
1021
  className = "oe-editor-surface",
@@ -922,6 +1035,6 @@ var OpenEditor = ({
922
1035
  return theme ? /* @__PURE__ */ jsx(OpenEditorThemeProvider, { theme, children: editor }) : editor;
923
1036
  };
924
1037
 
925
- export { OpenEditor, OpenEditorBlockMenu, OpenEditorButton, OpenEditorInput, OpenEditorSelectionBubble, OpenEditorSlashMenu, OpenEditorTableMenu, OpenEditorToggle, OpenEditorToggleGroup };
1038
+ export { OpenEditor, OpenEditorBlockMenu, OpenEditorBlockMenuPopup, OpenEditorButton, OpenEditorInput, OpenEditorSelectionBubble, OpenEditorSlashMenu, OpenEditorTableMenu, OpenEditorToggle, OpenEditorToggleGroup, createOpenEditorBuiltInBlockMenuActions };
926
1039
  //# sourceMappingURL=index.js.map
927
1040
  //# sourceMappingURL=index.js.map