@react-email/editor 0.0.0-experimental.20 → 0.0.0-experimental.22

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.cjs CHANGED
@@ -37,6 +37,8 @@ let _tiptap_starter_kit = require("@tiptap/starter-kit");
37
37
  _tiptap_starter_kit = __toESM(_tiptap_starter_kit);
38
38
  let _tiptap_extension_blockquote = require("@tiptap/extension-blockquote");
39
39
  _tiptap_extension_blockquote = __toESM(_tiptap_extension_blockquote);
40
+ let _tiptap_extension_bold = require("@tiptap/extension-bold");
41
+ _tiptap_extension_bold = __toESM(_tiptap_extension_bold);
40
42
  let _tiptap_extension_bullet_list = require("@tiptap/extension-bullet-list");
41
43
  _tiptap_extension_bullet_list = __toESM(_tiptap_extension_bullet_list);
42
44
  let _tiptap_extension_code = require("@tiptap/extension-code");
@@ -67,15 +69,19 @@ let _tiptap_extension_placeholder = require("@tiptap/extension-placeholder");
67
69
  _tiptap_extension_placeholder = __toESM(_tiptap_extension_placeholder);
68
70
  let _tiptap_extension_strike = require("@tiptap/extension-strike");
69
71
  _tiptap_extension_strike = __toESM(_tiptap_extension_strike);
72
+ let _tiptap_extension_superscript = require("@tiptap/extension-superscript");
73
+ _tiptap_extension_superscript = __toESM(_tiptap_extension_superscript);
74
+ let _tiptap_extension_underline = require("@tiptap/extension-underline");
75
+ _tiptap_extension_underline = __toESM(_tiptap_extension_underline);
70
76
  let _tiptap_html = require("@tiptap/html");
71
77
  let lucide_react = require("lucide-react");
72
78
  let _radix_ui_react_popover = require("@radix-ui/react-popover");
73
79
  _radix_ui_react_popover = __toESM(_radix_ui_react_popover);
74
80
  let _tiptap_react_menus = require("@tiptap/react/menus");
81
+ let _floating_ui_react_dom = require("@floating-ui/react-dom");
75
82
  let _tiptap_suggestion = require("@tiptap/suggestion");
76
83
  _tiptap_suggestion = __toESM(_tiptap_suggestion);
77
- let tippy_js = require("tippy.js");
78
- tippy_js = __toESM(tippy_js);
84
+ let react_dom = require("react-dom");
79
85
 
80
86
  //#region src/core/event-bus.ts
81
87
  const EVENT_PREFIX = "@react-email/editor:";
@@ -690,95 +696,23 @@ const Body = EmailNode.create({
690
696
 
691
697
  //#endregion
692
698
  //#region src/extensions/bold.tsx
693
- /**
694
- * Matches bold text via `**` as input.
695
- */
696
- const starInputRegex = /(?:^|\s)(\*\*(?!\s+\*\*)((?:[^*]+))\*\*(?!\s+\*\*))$/;
697
- /**
698
- * Matches bold text via `**` while pasting.
699
- */
700
- const starPasteRegex = /(?:^|\s)(\*\*(?!\s+\*\*)((?:[^*]+))\*\*(?!\s+\*\*))/g;
701
- /**
702
- * Matches bold text via `__` as input.
703
- */
704
- const underscoreInputRegex = /(?:^|\s)(__(?!\s+__)((?:[^_]+))__(?!\s+__))$/;
705
- /**
706
- * Matches bold text via `__` while pasting.
707
- */
708
- const underscorePasteRegex = /(?:^|\s)(__(?!\s+__)((?:[^_]+))__(?!\s+__))/g;
709
- /**
710
- * This extension allows you to mark text as bold.
711
- * @see https://tiptap.dev/api/marks/bold
712
- */
713
- const Bold = EmailMark.create({
714
- name: "bold",
715
- addOptions() {
716
- return { HTMLAttributes: {} };
717
- },
718
- parseHTML() {
719
- return [
720
- { tag: "strong" },
721
- {
722
- tag: "b",
723
- getAttrs: (node) => node.style.fontWeight !== "normal" && null
724
- },
725
- {
726
- style: "font-weight=400",
727
- clearMark: (mark) => mark.type.name === this.name
728
- }
729
- ];
730
- },
731
- renderHTML({ HTMLAttributes }) {
732
- return [
733
- "strong",
734
- (0, _tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes),
735
- 0
736
- ];
737
- },
738
- renderToReactEmail({ children, style }) {
739
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
740
- style,
741
- children
742
- });
743
- },
744
- addCommands() {
745
- return {
746
- setBold: () => ({ commands }) => {
747
- return commands.setMark(this.name);
748
- },
749
- toggleBold: () => ({ commands }) => {
750
- return commands.toggleMark(this.name);
751
- },
752
- unsetBold: () => ({ commands }) => {
753
- return commands.unsetMark(this.name);
754
- }
755
- };
756
- },
757
- addKeyboardShortcuts() {
758
- return {
759
- "Mod-b": () => this.editor.commands.toggleBold(),
760
- "Mod-B": () => this.editor.commands.toggleBold()
761
- };
762
- },
763
- addInputRules() {
764
- return [(0, _tiptap_core.markInputRule)({
765
- find: starInputRegex,
766
- type: this.type
767
- }), (0, _tiptap_core.markInputRule)({
768
- find: underscoreInputRegex,
769
- type: this.type
770
- })];
771
- },
772
- addPasteRules() {
773
- return [(0, _tiptap_core.markPasteRule)({
774
- find: starPasteRegex,
775
- type: this.type
776
- }), (0, _tiptap_core.markPasteRule)({
777
- find: underscorePasteRegex,
778
- type: this.type
779
- })];
780
- }
781
- });
699
+ const BoldWithoutFontWeightInference = _tiptap_extension_bold.default.extend({ parseHTML() {
700
+ return [
701
+ { tag: "strong" },
702
+ {
703
+ tag: "b",
704
+ getAttrs: (node) => node.style.fontWeight !== "normal" && null
705
+ },
706
+ {
707
+ style: "font-weight=400",
708
+ clearMark: (mark) => mark.type.name === this.name
709
+ }
710
+ ];
711
+ } });
712
+ const Bold = EmailMark.from(BoldWithoutFontWeightInference, ({ children, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
713
+ style,
714
+ children
715
+ }));
782
716
 
783
717
  //#endregion
784
718
  //#region src/extensions/bullet-list.tsx
@@ -1778,33 +1712,11 @@ const StyleAttribute = _tiptap_core.Extension.create({
1778
1712
 
1779
1713
  //#endregion
1780
1714
  //#region src/extensions/sup.tsx
1781
- /**
1782
- * This extension allows you to mark text as superscript.
1783
- * @see https://tiptap.dev/api/marks/superscript
1784
- */
1785
- const Sup = EmailMark.create({
1715
+ const SupBase = _tiptap_extension_superscript.default.extend({
1786
1716
  name: "sup",
1787
- addOptions() {
1788
- return { HTMLAttributes: {} };
1789
- },
1790
- parseHTML() {
1791
- return [{ tag: "sup" }];
1792
- },
1793
- renderHTML({ HTMLAttributes }) {
1794
- return [
1795
- "sup",
1796
- (0, _tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes),
1797
- 0
1798
- ];
1799
- },
1800
- renderToReactEmail({ children, style }) {
1801
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("sup", {
1802
- style,
1803
- children
1804
- });
1805
- },
1806
1717
  addCommands() {
1807
1718
  return {
1719
+ ...this.parent?.(),
1808
1720
  setSup: () => ({ commands }) => {
1809
1721
  return commands.setMark(this.name);
1810
1722
  },
@@ -1817,6 +1729,10 @@ const Sup = EmailMark.create({
1817
1729
  };
1818
1730
  }
1819
1731
  });
1732
+ const Sup = EmailMark.from(SupBase, ({ children, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("sup", {
1733
+ style,
1734
+ children
1735
+ }));
1820
1736
 
1821
1737
  //#endregion
1822
1738
  //#region src/extensions/table.tsx
@@ -2004,6 +1920,13 @@ const TableHeader = _tiptap_core.Node.create({
2004
1920
  }
2005
1921
  });
2006
1922
 
1923
+ //#endregion
1924
+ //#region src/extensions/underline.tsx
1925
+ const Underline = EmailMark.from(_tiptap_extension_underline.default, ({ children, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("u", {
1926
+ style,
1927
+ children
1928
+ }));
1929
+
2007
1930
  //#endregion
2008
1931
  //#region src/extensions/uppercase.tsx
2009
1932
  const Uppercase = EmailMark.create({
@@ -2241,6 +2164,7 @@ const starterKitExtensions = {
2241
2164
  Divider,
2242
2165
  Link,
2243
2166
  Sup,
2167
+ Underline,
2244
2168
  Uppercase,
2245
2169
  PreservedStyle,
2246
2170
  Table,
@@ -2285,6 +2209,7 @@ const StarterKit = _tiptap_core.Extension.create({
2285
2209
  Divider: {},
2286
2210
  Link: {},
2287
2211
  Sup: {},
2212
+ Underline: {},
2288
2213
  Uppercase: {},
2289
2214
  PreservedStyle: {},
2290
2215
  Table: {},
@@ -3122,7 +3047,7 @@ function BubbleMenuNodeSelector({ omit = [], className, triggerContent, open, on
3122
3047
 
3123
3048
  //#endregion
3124
3049
  //#region src/ui/bubble-menu/root.tsx
3125
- function BubbleMenuRoot({ excludeNodes = [], placement = "bottom", offset = 8, onHide, className, children }) {
3050
+ function BubbleMenuRoot({ excludeNodes = [], placement = "bottom", offset: offset$1 = 8, onHide, className, children }) {
3126
3051
  const { editor } = (0, _tiptap_react.useCurrentEditor)();
3127
3052
  if (!editor) return null;
3128
3053
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tiptap_react_menus.BubbleMenu, {
@@ -3135,7 +3060,7 @@ function BubbleMenuRoot({ excludeNodes = [], placement = "bottom", offset = 8, o
3135
3060
  },
3136
3061
  options: {
3137
3062
  placement,
3138
- offset,
3063
+ offset: offset$1,
3139
3064
  onHide
3140
3065
  },
3141
3066
  className,
@@ -3175,7 +3100,7 @@ const BubbleMenuUppercase = createMarkBubbleItem({
3175
3100
 
3176
3101
  //#endregion
3177
3102
  //#region src/ui/bubble-menu/default.tsx
3178
- function BubbleMenuDefault({ excludeItems = [], excludeNodes, placement, offset, onHide, className }) {
3103
+ function BubbleMenuDefault({ excludeItems = [], excludeNodes, placement, offset: offset$1, onHide, className }) {
3179
3104
  const [isNodeSelectorOpen, setIsNodeSelectorOpen] = react.useState(false);
3180
3105
  const [isLinkSelectorOpen, setIsLinkSelectorOpen] = react.useState(false);
3181
3106
  const has = (item) => !excludeItems.includes(item);
@@ -3197,7 +3122,7 @@ function BubbleMenuDefault({ excludeItems = [], excludeNodes, placement, offset,
3197
3122
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(BubbleMenuRoot, {
3198
3123
  excludeNodes,
3199
3124
  placement,
3200
- offset,
3125
+ offset: offset$1,
3201
3126
  onHide: handleHide,
3202
3127
  className,
3203
3128
  children: [
@@ -3294,7 +3219,7 @@ function ButtonBubbleMenuEditLink({ className, children, onClick, onMouseDown, .
3294
3219
 
3295
3220
  //#endregion
3296
3221
  //#region src/ui/button-bubble-menu/root.tsx
3297
- function ButtonBubbleMenuRoot({ onHide, placement = "top", offset = 8, className, children }) {
3222
+ function ButtonBubbleMenuRoot({ onHide, placement = "top", offset: offset$1 = 8, className, children }) {
3298
3223
  const { editor } = (0, _tiptap_react.useCurrentEditor)();
3299
3224
  const [isEditing, setIsEditing] = react.useState(false);
3300
3225
  if (!editor) return null;
@@ -3304,7 +3229,7 @@ function ButtonBubbleMenuRoot({ onHide, placement = "top", offset = 8, className
3304
3229
  shouldShow: ({ editor: e, view }) => e.isActive("button") && !view.dom.classList.contains("dragging"),
3305
3230
  options: {
3306
3231
  placement,
3307
- offset,
3232
+ offset: offset$1,
3308
3233
  onHide: () => {
3309
3234
  setIsEditing(false);
3310
3235
  onHide?.();
@@ -3336,10 +3261,10 @@ function ButtonBubbleMenuToolbar({ children, ...rest }) {
3336
3261
 
3337
3262
  //#endregion
3338
3263
  //#region src/ui/button-bubble-menu/default.tsx
3339
- function ButtonBubbleMenuDefault({ excludeItems = [], placement, offset, onHide, className }) {
3264
+ function ButtonBubbleMenuDefault({ excludeItems = [], placement, offset: offset$1, onHide, className }) {
3340
3265
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ButtonBubbleMenuRoot, {
3341
3266
  placement,
3342
- offset,
3267
+ offset: offset$1,
3343
3268
  onHide,
3344
3269
  className,
3345
3270
  children: !excludeItems.includes("edit-link") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ButtonBubbleMenuToolbar, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ButtonBubbleMenuEditLink, {}) })
@@ -3389,7 +3314,7 @@ function ImageBubbleMenuEditLink({ className, children, onClick, onMouseDown, ..
3389
3314
 
3390
3315
  //#endregion
3391
3316
  //#region src/ui/image-bubble-menu/root.tsx
3392
- function ImageBubbleMenuRoot({ onHide, placement = "top", offset = 8, className, children }) {
3317
+ function ImageBubbleMenuRoot({ onHide, placement = "top", offset: offset$1 = 8, className, children }) {
3393
3318
  const { editor } = (0, _tiptap_react.useCurrentEditor)();
3394
3319
  const [isEditing, setIsEditing] = react.useState(false);
3395
3320
  if (!editor) return null;
@@ -3399,7 +3324,7 @@ function ImageBubbleMenuRoot({ onHide, placement = "top", offset = 8, className,
3399
3324
  shouldShow: ({ editor: e, view }) => e.isActive("image") && !view.dom.classList.contains("dragging"),
3400
3325
  options: {
3401
3326
  placement,
3402
- offset,
3327
+ offset: offset$1,
3403
3328
  onHide: () => {
3404
3329
  setIsEditing(false);
3405
3330
  onHide?.();
@@ -3431,10 +3356,10 @@ function ImageBubbleMenuToolbar({ children, ...rest }) {
3431
3356
 
3432
3357
  //#endregion
3433
3358
  //#region src/ui/image-bubble-menu/default.tsx
3434
- function ImageBubbleMenuDefault({ excludeItems = [], placement, offset, onHide, className }) {
3359
+ function ImageBubbleMenuDefault({ excludeItems = [], placement, offset: offset$1, onHide, className }) {
3435
3360
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageBubbleMenuRoot, {
3436
3361
  placement,
3437
- offset,
3362
+ offset: offset$1,
3438
3363
  onHide,
3439
3364
  className,
3440
3365
  children: !excludeItems.includes("edit-link") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageBubbleMenuToolbar, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageBubbleMenuEditLink, {}) })
@@ -3606,7 +3531,7 @@ function LinkBubbleMenuOpenLink({ className, children, ...rest }) {
3606
3531
 
3607
3532
  //#endregion
3608
3533
  //#region src/ui/link-bubble-menu/root.tsx
3609
- function LinkBubbleMenuRoot({ onHide, placement = "top", offset = 8, className, children }) {
3534
+ function LinkBubbleMenuRoot({ onHide, placement = "top", offset: offset$1 = 8, className, children }) {
3610
3535
  const { editor } = (0, _tiptap_react.useCurrentEditor)();
3611
3536
  const [isEditing, setIsEditing] = react.useState(false);
3612
3537
  const linkHref = (0, _tiptap_react.useEditorState)({
@@ -3620,7 +3545,7 @@ function LinkBubbleMenuRoot({ onHide, placement = "top", offset = 8, className,
3620
3545
  shouldShow: ({ editor: e }) => e.isActive("link") && e.view.state.selection.content().size === 0,
3621
3546
  options: {
3622
3547
  placement,
3623
- offset,
3548
+ offset: offset$1,
3624
3549
  onHide: () => {
3625
3550
  setIsEditing(false);
3626
3551
  onHide?.();
@@ -3676,11 +3601,11 @@ function LinkBubbleMenuUnlink({ className, children, onClick, onMouseDown, ...re
3676
3601
 
3677
3602
  //#endregion
3678
3603
  //#region src/ui/link-bubble-menu/default.tsx
3679
- function LinkBubbleMenuDefault({ excludeItems = [], placement, offset, onHide, className, validateUrl, onLinkApply, onLinkRemove }) {
3604
+ function LinkBubbleMenuDefault({ excludeItems = [], placement, offset: offset$1, onHide, className, validateUrl, onLinkApply, onLinkRemove }) {
3680
3605
  const has = (item) => !excludeItems.includes(item);
3681
3606
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(LinkBubbleMenuRoot, {
3682
3607
  placement,
3683
- offset,
3608
+ offset: offset$1,
3684
3609
  onHide,
3685
3610
  className,
3686
3611
  children: [(has("edit-link") || has("open-link") || has("unlink")) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(LinkBubbleMenuToolbar, { children: [
@@ -3766,42 +3691,14 @@ function CommandItem({ item, selected, onSelect }) {
3766
3691
  children: [item.icon, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: item.title })]
3767
3692
  });
3768
3693
  }
3769
- function CommandList({ items, command, query, ref }) {
3770
- const [selectedIndex, setSelectedIndex] = (0, react.useState)(0);
3694
+ function CommandList({ items, query, selectedIndex, onSelect }) {
3771
3695
  const containerRef = (0, react.useRef)(null);
3772
- (0, react.useEffect)(() => {
3773
- setSelectedIndex(0);
3774
- }, [items]);
3775
3696
  (0, react.useLayoutEffect)(() => {
3776
3697
  const container = containerRef.current;
3777
3698
  if (!container) return;
3778
3699
  const selected = container.querySelector("[data-selected]");
3779
3700
  if (selected) updateScrollView(container, selected);
3780
3701
  }, [selectedIndex]);
3781
- const selectItem = (0, react.useCallback)((index) => {
3782
- const item = items[index];
3783
- if (item) command(item);
3784
- }, [items, command]);
3785
- (0, react.useImperativeHandle)(ref, () => ({ onKeyDown: ({ event }) => {
3786
- if (items.length === 0) return false;
3787
- if (event.key === "ArrowUp") {
3788
- setSelectedIndex((i) => (i + items.length - 1) % items.length);
3789
- return true;
3790
- }
3791
- if (event.key === "ArrowDown") {
3792
- setSelectedIndex((i) => (i + 1) % items.length);
3793
- return true;
3794
- }
3795
- if (event.key === "Enter") {
3796
- selectItem(selectedIndex);
3797
- return true;
3798
- }
3799
- return false;
3800
- } }), [
3801
- items.length,
3802
- selectItem,
3803
- selectedIndex
3804
- ]);
3805
3702
  if (items.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3806
3703
  "data-re-slash-command": "",
3807
3704
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -3814,7 +3711,7 @@ function CommandList({ items, command, query, ref }) {
3814
3711
  ref: containerRef,
3815
3712
  children: items.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommandItem, {
3816
3713
  item,
3817
- onSelect: () => selectItem(index),
3714
+ onSelect: () => onSelect(index),
3818
3715
  selected: index === selectedIndex
3819
3716
  }, item.title))
3820
3717
  });
@@ -3830,7 +3727,7 @@ function CommandList({ items, command, query, ref }) {
3830
3727
  const currentIndex = flatIndex++;
3831
3728
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommandItem, {
3832
3729
  item,
3833
- onSelect: () => selectItem(currentIndex),
3730
+ onSelect: () => onSelect(currentIndex),
3834
3731
  selected: currentIndex === selectedIndex
3835
3732
  }, item.title);
3836
3733
  })] }, group.category))
@@ -4048,76 +3945,6 @@ const defaultSlashCommands = [
4048
3945
  FOUR_COLUMNS
4049
3946
  ];
4050
3947
 
4051
- //#endregion
4052
- //#region src/ui/slash-command/extension.ts
4053
- const SlashCommandExtension = _tiptap_core.Extension.create({
4054
- name: "slash-command",
4055
- addOptions() {
4056
- return { suggestion: {
4057
- char: "/",
4058
- allow: ({ editor }) => !editor.isActive("codeBlock"),
4059
- command: ({ editor, range, props }) => {
4060
- props.command({
4061
- editor,
4062
- range
4063
- });
4064
- }
4065
- } };
4066
- },
4067
- addProseMirrorPlugins() {
4068
- return [(0, _tiptap_suggestion.default)({
4069
- pluginKey: new _tiptap_pm_state.PluginKey("slash-command"),
4070
- editor: this.editor,
4071
- ...this.options.suggestion
4072
- })];
4073
- }
4074
- });
4075
-
4076
- //#endregion
4077
- //#region src/ui/slash-command/render.tsx
4078
- function createRenderItems(component = CommandList) {
4079
- return () => {
4080
- let renderer = null;
4081
- let popup = null;
4082
- return {
4083
- onStart: (props) => {
4084
- renderer = new _tiptap_react.ReactRenderer(component, {
4085
- props,
4086
- editor: props.editor
4087
- });
4088
- if (!props.clientRect) return;
4089
- popup = (0, tippy_js.default)("body", {
4090
- getReferenceClientRect: props.clientRect,
4091
- appendTo: () => document.body,
4092
- content: renderer.element,
4093
- showOnCreate: true,
4094
- interactive: true,
4095
- trigger: "manual",
4096
- placement: "bottom-start"
4097
- });
4098
- },
4099
- onUpdate: (props) => {
4100
- if (!renderer) return;
4101
- renderer.updateProps(props);
4102
- if (popup?.[0] && props.clientRect) popup[0].setProps({ getReferenceClientRect: props.clientRect });
4103
- },
4104
- onKeyDown: (props) => {
4105
- if (props.event.key === "Escape") {
4106
- popup?.[0]?.hide();
4107
- return true;
4108
- }
4109
- return renderer?.ref?.onKeyDown(props) ?? false;
4110
- },
4111
- onExit: () => {
4112
- popup?.[0]?.destroy();
4113
- renderer?.destroy();
4114
- popup = null;
4115
- renderer = null;
4116
- }
4117
- };
4118
- };
4119
- }
4120
-
4121
3948
  //#endregion
4122
3949
  //#region src/ui/slash-command/search.ts
4123
3950
  function scoreItem(item, query) {
@@ -4148,22 +3975,144 @@ function filterAndRankItems(items, query) {
4148
3975
  }
4149
3976
 
4150
3977
  //#endregion
4151
- //#region src/ui/slash-command/create-slash-command.ts
3978
+ //#region src/ui/slash-command/root.tsx
3979
+ const pluginKey = new _tiptap_pm_state.PluginKey("slash-command");
3980
+ const INITIAL_STATE = {
3981
+ active: false,
3982
+ query: "",
3983
+ items: [],
3984
+ clientRect: null
3985
+ };
4152
3986
  function defaultFilterItems(items, query, editor) {
4153
3987
  return filterAndRankItems(isAtMaxColumnsDepth(editor) ? items.filter((item) => item.category !== "Layout" || !item.title.includes("column")) : items, query);
4154
3988
  }
4155
- function createSlashCommand(options) {
4156
- const items = options?.items ?? defaultSlashCommands;
4157
- const filterFn = options?.filterItems ?? defaultFilterItems;
4158
- return SlashCommandExtension.configure({ suggestion: {
4159
- items: ({ query, editor }) => filterFn(items, query, editor),
4160
- render: createRenderItems(options?.component)
4161
- } });
3989
+ function SlashCommandRoot({ items: itemsProp, filterItems: filterItemsProp, char = "/", allow: allowProp, children }) {
3990
+ const { editor } = (0, _tiptap_react.useCurrentEditor)();
3991
+ const [state, setState] = (0, react.useState)(INITIAL_STATE);
3992
+ const [selectedIndex, setSelectedIndex] = (0, react.useState)(0);
3993
+ const itemsRef = (0, react.useRef)(itemsProp ?? defaultSlashCommands);
3994
+ const filterRef = (0, react.useRef)(filterItemsProp ?? defaultFilterItems);
3995
+ const allowRef = (0, react.useRef)(allowProp ?? (({ editor: e }) => !e.isActive("codeBlock")));
3996
+ itemsRef.current = itemsProp ?? defaultSlashCommands;
3997
+ filterRef.current = filterItemsProp ?? defaultFilterItems;
3998
+ allowRef.current = allowProp ?? (({ editor: e }) => !e.isActive("codeBlock"));
3999
+ const commandRef = (0, react.useRef)(null);
4000
+ const suggestionItemsRef = (0, react.useRef)([]);
4001
+ const selectedIndexRef = (0, react.useRef)(0);
4002
+ suggestionItemsRef.current = state.items;
4003
+ selectedIndexRef.current = selectedIndex;
4004
+ const { refs, floatingStyles } = (0, _floating_ui_react_dom.useFloating)({
4005
+ open: state.active,
4006
+ placement: "bottom-start",
4007
+ middleware: [
4008
+ (0, _floating_ui_react_dom.offset)(8),
4009
+ (0, _floating_ui_react_dom.flip)(),
4010
+ (0, _floating_ui_react_dom.shift)({ padding: 8 })
4011
+ ],
4012
+ whileElementsMounted: _floating_ui_react_dom.autoUpdate
4013
+ });
4014
+ (0, react.useEffect)(() => {
4015
+ if (!state.clientRect) return;
4016
+ refs.setReference({ getBoundingClientRect: state.clientRect });
4017
+ }, [state.clientRect, refs]);
4018
+ (0, react.useEffect)(() => {
4019
+ setSelectedIndex(0);
4020
+ }, [state.items]);
4021
+ const onSelect = (0, react.useCallback)((index) => {
4022
+ const item = suggestionItemsRef.current[index];
4023
+ if (item && commandRef.current) commandRef.current(item);
4024
+ }, []);
4025
+ (0, react.useEffect)(() => {
4026
+ if (!editor) return;
4027
+ const plugin = (0, _tiptap_suggestion.default)({
4028
+ pluginKey,
4029
+ editor,
4030
+ char,
4031
+ allow: ({ editor: e }) => allowRef.current({ editor: e }),
4032
+ command: ({ editor: e, range, props }) => {
4033
+ props.command({
4034
+ editor: e,
4035
+ range
4036
+ });
4037
+ },
4038
+ items: ({ query, editor: e }) => filterRef.current(itemsRef.current, query, e),
4039
+ render: () => ({
4040
+ onStart: (props) => {
4041
+ commandRef.current = props.command;
4042
+ setState({
4043
+ active: true,
4044
+ query: props.query,
4045
+ items: props.items,
4046
+ clientRect: props.clientRect ?? null
4047
+ });
4048
+ },
4049
+ onUpdate: (props) => {
4050
+ commandRef.current = props.command;
4051
+ setState({
4052
+ active: true,
4053
+ query: props.query,
4054
+ items: props.items,
4055
+ clientRect: props.clientRect ?? null
4056
+ });
4057
+ },
4058
+ onKeyDown: ({ event }) => {
4059
+ if (event.key === "Escape") {
4060
+ setState(INITIAL_STATE);
4061
+ return true;
4062
+ }
4063
+ const items = suggestionItemsRef.current;
4064
+ if (items.length === 0) return false;
4065
+ if (event.key === "ArrowUp") {
4066
+ setSelectedIndex((i) => (i + items.length - 1) % items.length);
4067
+ return true;
4068
+ }
4069
+ if (event.key === "ArrowDown") {
4070
+ setSelectedIndex((i) => (i + 1) % items.length);
4071
+ return true;
4072
+ }
4073
+ if (event.key === "Enter") {
4074
+ const item = items[selectedIndexRef.current];
4075
+ if (item && commandRef.current) commandRef.current(item);
4076
+ return true;
4077
+ }
4078
+ return false;
4079
+ },
4080
+ onExit: () => {
4081
+ setState(INITIAL_STATE);
4082
+ requestAnimationFrame(() => {
4083
+ commandRef.current = null;
4084
+ });
4085
+ }
4086
+ })
4087
+ });
4088
+ editor.registerPlugin(plugin, (newPlugin, plugins) => [newPlugin, ...plugins]);
4089
+ return () => {
4090
+ editor.unregisterPlugin(pluginKey);
4091
+ };
4092
+ }, [editor, char]);
4093
+ if (!editor || !state.active) return null;
4094
+ const renderProps = {
4095
+ items: state.items,
4096
+ query: state.query,
4097
+ selectedIndex,
4098
+ onSelect
4099
+ };
4100
+ let content;
4101
+ if (children) content = children(renderProps);
4102
+ else content = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommandList, { ...renderProps });
4103
+ return (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4104
+ ref: refs.setFloating,
4105
+ style: floatingStyles,
4106
+ children: content
4107
+ }), document.body);
4162
4108
  }
4163
4109
 
4164
4110
  //#endregion
4165
4111
  //#region src/ui/slash-command/index.ts
4166
- const SlashCommand = createSlashCommand();
4112
+ const SlashCommand = {
4113
+ Root: SlashCommandRoot,
4114
+ CommandList
4115
+ };
4167
4116
 
4168
4117
  //#endregion
4169
4118
  exports.AlignmentAttribute = AlignmentAttribute;
@@ -4259,9 +4208,9 @@ exports.TableHeader = TableHeader;
4259
4208
  exports.TableRow = TableRow;
4260
4209
  exports.ThreeColumns = ThreeColumns;
4261
4210
  exports.TwoColumns = TwoColumns;
4211
+ exports.Underline = Underline;
4262
4212
  exports.Uppercase = Uppercase;
4263
4213
  exports.composeReactEmail = composeReactEmail;
4264
- exports.createSlashCommand = createSlashCommand;
4265
4214
  exports.defaultSlashCommands = defaultSlashCommands;
4266
4215
  exports.editorEventBus = editorEventBus;
4267
4216
  exports.filterAndRankItems = filterAndRankItems;