@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 +191 -242
- package/dist/index.d.cts +86 -107
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +86 -107
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +191 -244
- package/dist/index.mjs.map +1 -1
- package/dist/ui/themes/default.css +20 -1
- package/package.json +9 -5
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import * as ReactEmailComponents from "@react-email/components";
|
|
2
2
|
import { Body as Body$1, Button as Button$1, CodeBlock, Column, Head, Heading as Heading$1, Hr, Html, Link as Link$1, Preview, Row, Section as Section$1, pretty, render, toPlainText } from "@react-email/components";
|
|
3
3
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
-
import { Extension, InputRule, Mark, Node as Node$1, findChildren,
|
|
4
|
+
import { Extension, InputRule, Mark, Node as Node$1, findChildren, mergeAttributes } from "@tiptap/core";
|
|
5
5
|
import { UndoRedo } from "@tiptap/extensions";
|
|
6
|
-
import { NodeViewContent, NodeViewWrapper, ReactNodeViewRenderer,
|
|
6
|
+
import { NodeViewContent, NodeViewWrapper, ReactNodeViewRenderer, useCurrentEditor, useEditor as useEditor$1, useEditorState } from "@tiptap/react";
|
|
7
7
|
import * as React from "react";
|
|
8
|
-
import { useCallback, useEffect,
|
|
8
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
9
9
|
import TipTapStarterKit from "@tiptap/starter-kit";
|
|
10
10
|
import BlockquoteBase from "@tiptap/extension-blockquote";
|
|
11
|
+
import BoldBase from "@tiptap/extension-bold";
|
|
11
12
|
import BulletListBase from "@tiptap/extension-bullet-list";
|
|
12
13
|
import CodeBase from "@tiptap/extension-code";
|
|
13
14
|
import CodeBlock$1 from "@tiptap/extension-code-block";
|
|
@@ -25,12 +26,15 @@ import OrderedListBase from "@tiptap/extension-ordered-list";
|
|
|
25
26
|
import ParagraphBase from "@tiptap/extension-paragraph";
|
|
26
27
|
import TipTapPlaceholder from "@tiptap/extension-placeholder";
|
|
27
28
|
import StrikeBase from "@tiptap/extension-strike";
|
|
29
|
+
import SuperscriptBase from "@tiptap/extension-superscript";
|
|
30
|
+
import UnderlineBase from "@tiptap/extension-underline";
|
|
28
31
|
import { generateJSON } from "@tiptap/html";
|
|
29
32
|
import { AlignCenterIcon, AlignLeftIcon, AlignRightIcon, BoldIcon, CaseUpperIcon, Check, ChevronDown, Code as Code$1, CodeIcon, Columns2, Columns3, Columns4, ExternalLinkIcon, Heading1, Heading2, Heading3, ItalicIcon, LinkIcon, List, ListOrdered, MousePointer, PencilIcon, Rows2, SplitSquareVertical, SquareCode, StrikethroughIcon, Text, TextIcon, TextQuote, UnderlineIcon, UnlinkIcon } from "lucide-react";
|
|
30
33
|
import * as Popover from "@radix-ui/react-popover";
|
|
31
34
|
import { BubbleMenu as BubbleMenu$1 } from "@tiptap/react/menus";
|
|
35
|
+
import { autoUpdate, flip, offset, shift, useFloating } from "@floating-ui/react-dom";
|
|
32
36
|
import Suggestion from "@tiptap/suggestion";
|
|
33
|
-
import
|
|
37
|
+
import { createPortal } from "react-dom";
|
|
34
38
|
|
|
35
39
|
//#region src/core/event-bus.ts
|
|
36
40
|
const EVENT_PREFIX = "@react-email/editor:";
|
|
@@ -645,95 +649,23 @@ const Body = EmailNode.create({
|
|
|
645
649
|
|
|
646
650
|
//#endregion
|
|
647
651
|
//#region src/extensions/bold.tsx
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
* This extension allows you to mark text as bold.
|
|
666
|
-
* @see https://tiptap.dev/api/marks/bold
|
|
667
|
-
*/
|
|
668
|
-
const Bold = EmailMark.create({
|
|
669
|
-
name: "bold",
|
|
670
|
-
addOptions() {
|
|
671
|
-
return { HTMLAttributes: {} };
|
|
672
|
-
},
|
|
673
|
-
parseHTML() {
|
|
674
|
-
return [
|
|
675
|
-
{ tag: "strong" },
|
|
676
|
-
{
|
|
677
|
-
tag: "b",
|
|
678
|
-
getAttrs: (node) => node.style.fontWeight !== "normal" && null
|
|
679
|
-
},
|
|
680
|
-
{
|
|
681
|
-
style: "font-weight=400",
|
|
682
|
-
clearMark: (mark) => mark.type.name === this.name
|
|
683
|
-
}
|
|
684
|
-
];
|
|
685
|
-
},
|
|
686
|
-
renderHTML({ HTMLAttributes }) {
|
|
687
|
-
return [
|
|
688
|
-
"strong",
|
|
689
|
-
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
|
|
690
|
-
0
|
|
691
|
-
];
|
|
692
|
-
},
|
|
693
|
-
renderToReactEmail({ children, style }) {
|
|
694
|
-
return /* @__PURE__ */ jsx("strong", {
|
|
695
|
-
style,
|
|
696
|
-
children
|
|
697
|
-
});
|
|
698
|
-
},
|
|
699
|
-
addCommands() {
|
|
700
|
-
return {
|
|
701
|
-
setBold: () => ({ commands }) => {
|
|
702
|
-
return commands.setMark(this.name);
|
|
703
|
-
},
|
|
704
|
-
toggleBold: () => ({ commands }) => {
|
|
705
|
-
return commands.toggleMark(this.name);
|
|
706
|
-
},
|
|
707
|
-
unsetBold: () => ({ commands }) => {
|
|
708
|
-
return commands.unsetMark(this.name);
|
|
709
|
-
}
|
|
710
|
-
};
|
|
711
|
-
},
|
|
712
|
-
addKeyboardShortcuts() {
|
|
713
|
-
return {
|
|
714
|
-
"Mod-b": () => this.editor.commands.toggleBold(),
|
|
715
|
-
"Mod-B": () => this.editor.commands.toggleBold()
|
|
716
|
-
};
|
|
717
|
-
},
|
|
718
|
-
addInputRules() {
|
|
719
|
-
return [markInputRule({
|
|
720
|
-
find: starInputRegex,
|
|
721
|
-
type: this.type
|
|
722
|
-
}), markInputRule({
|
|
723
|
-
find: underscoreInputRegex,
|
|
724
|
-
type: this.type
|
|
725
|
-
})];
|
|
726
|
-
},
|
|
727
|
-
addPasteRules() {
|
|
728
|
-
return [markPasteRule({
|
|
729
|
-
find: starPasteRegex,
|
|
730
|
-
type: this.type
|
|
731
|
-
}), markPasteRule({
|
|
732
|
-
find: underscorePasteRegex,
|
|
733
|
-
type: this.type
|
|
734
|
-
})];
|
|
735
|
-
}
|
|
736
|
-
});
|
|
652
|
+
const BoldWithoutFontWeightInference = BoldBase.extend({ parseHTML() {
|
|
653
|
+
return [
|
|
654
|
+
{ tag: "strong" },
|
|
655
|
+
{
|
|
656
|
+
tag: "b",
|
|
657
|
+
getAttrs: (node) => node.style.fontWeight !== "normal" && null
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
style: "font-weight=400",
|
|
661
|
+
clearMark: (mark) => mark.type.name === this.name
|
|
662
|
+
}
|
|
663
|
+
];
|
|
664
|
+
} });
|
|
665
|
+
const Bold = EmailMark.from(BoldWithoutFontWeightInference, ({ children, style }) => /* @__PURE__ */ jsx("strong", {
|
|
666
|
+
style,
|
|
667
|
+
children
|
|
668
|
+
}));
|
|
737
669
|
|
|
738
670
|
//#endregion
|
|
739
671
|
//#region src/extensions/bullet-list.tsx
|
|
@@ -1733,33 +1665,11 @@ const StyleAttribute = Extension.create({
|
|
|
1733
1665
|
|
|
1734
1666
|
//#endregion
|
|
1735
1667
|
//#region src/extensions/sup.tsx
|
|
1736
|
-
|
|
1737
|
-
* This extension allows you to mark text as superscript.
|
|
1738
|
-
* @see https://tiptap.dev/api/marks/superscript
|
|
1739
|
-
*/
|
|
1740
|
-
const Sup = EmailMark.create({
|
|
1668
|
+
const SupBase = SuperscriptBase.extend({
|
|
1741
1669
|
name: "sup",
|
|
1742
|
-
addOptions() {
|
|
1743
|
-
return { HTMLAttributes: {} };
|
|
1744
|
-
},
|
|
1745
|
-
parseHTML() {
|
|
1746
|
-
return [{ tag: "sup" }];
|
|
1747
|
-
},
|
|
1748
|
-
renderHTML({ HTMLAttributes }) {
|
|
1749
|
-
return [
|
|
1750
|
-
"sup",
|
|
1751
|
-
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
|
|
1752
|
-
0
|
|
1753
|
-
];
|
|
1754
|
-
},
|
|
1755
|
-
renderToReactEmail({ children, style }) {
|
|
1756
|
-
return /* @__PURE__ */ jsx("sup", {
|
|
1757
|
-
style,
|
|
1758
|
-
children
|
|
1759
|
-
});
|
|
1760
|
-
},
|
|
1761
1670
|
addCommands() {
|
|
1762
1671
|
return {
|
|
1672
|
+
...this.parent?.(),
|
|
1763
1673
|
setSup: () => ({ commands }) => {
|
|
1764
1674
|
return commands.setMark(this.name);
|
|
1765
1675
|
},
|
|
@@ -1772,6 +1682,10 @@ const Sup = EmailMark.create({
|
|
|
1772
1682
|
};
|
|
1773
1683
|
}
|
|
1774
1684
|
});
|
|
1685
|
+
const Sup = EmailMark.from(SupBase, ({ children, style }) => /* @__PURE__ */ jsx("sup", {
|
|
1686
|
+
style,
|
|
1687
|
+
children
|
|
1688
|
+
}));
|
|
1775
1689
|
|
|
1776
1690
|
//#endregion
|
|
1777
1691
|
//#region src/extensions/table.tsx
|
|
@@ -1959,6 +1873,13 @@ const TableHeader = Node$1.create({
|
|
|
1959
1873
|
}
|
|
1960
1874
|
});
|
|
1961
1875
|
|
|
1876
|
+
//#endregion
|
|
1877
|
+
//#region src/extensions/underline.tsx
|
|
1878
|
+
const Underline = EmailMark.from(UnderlineBase, ({ children, style }) => /* @__PURE__ */ jsx("u", {
|
|
1879
|
+
style,
|
|
1880
|
+
children
|
|
1881
|
+
}));
|
|
1882
|
+
|
|
1962
1883
|
//#endregion
|
|
1963
1884
|
//#region src/extensions/uppercase.tsx
|
|
1964
1885
|
const Uppercase = EmailMark.create({
|
|
@@ -2196,6 +2117,7 @@ const starterKitExtensions = {
|
|
|
2196
2117
|
Divider,
|
|
2197
2118
|
Link,
|
|
2198
2119
|
Sup,
|
|
2120
|
+
Underline,
|
|
2199
2121
|
Uppercase,
|
|
2200
2122
|
PreservedStyle,
|
|
2201
2123
|
Table,
|
|
@@ -2240,6 +2162,7 @@ const StarterKit = Extension.create({
|
|
|
2240
2162
|
Divider: {},
|
|
2241
2163
|
Link: {},
|
|
2242
2164
|
Sup: {},
|
|
2165
|
+
Underline: {},
|
|
2243
2166
|
Uppercase: {},
|
|
2244
2167
|
PreservedStyle: {},
|
|
2245
2168
|
Table: {},
|
|
@@ -3077,7 +3000,7 @@ function BubbleMenuNodeSelector({ omit = [], className, triggerContent, open, on
|
|
|
3077
3000
|
|
|
3078
3001
|
//#endregion
|
|
3079
3002
|
//#region src/ui/bubble-menu/root.tsx
|
|
3080
|
-
function BubbleMenuRoot({ excludeNodes = [], placement = "bottom", offset = 8, onHide, className, children }) {
|
|
3003
|
+
function BubbleMenuRoot({ excludeNodes = [], placement = "bottom", offset: offset$1 = 8, onHide, className, children }) {
|
|
3081
3004
|
const { editor } = useCurrentEditor();
|
|
3082
3005
|
if (!editor) return null;
|
|
3083
3006
|
return /* @__PURE__ */ jsx(BubbleMenu$1, {
|
|
@@ -3090,7 +3013,7 @@ function BubbleMenuRoot({ excludeNodes = [], placement = "bottom", offset = 8, o
|
|
|
3090
3013
|
},
|
|
3091
3014
|
options: {
|
|
3092
3015
|
placement,
|
|
3093
|
-
offset,
|
|
3016
|
+
offset: offset$1,
|
|
3094
3017
|
onHide
|
|
3095
3018
|
},
|
|
3096
3019
|
className,
|
|
@@ -3130,7 +3053,7 @@ const BubbleMenuUppercase = createMarkBubbleItem({
|
|
|
3130
3053
|
|
|
3131
3054
|
//#endregion
|
|
3132
3055
|
//#region src/ui/bubble-menu/default.tsx
|
|
3133
|
-
function BubbleMenuDefault({ excludeItems = [], excludeNodes, placement, offset, onHide, className }) {
|
|
3056
|
+
function BubbleMenuDefault({ excludeItems = [], excludeNodes, placement, offset: offset$1, onHide, className }) {
|
|
3134
3057
|
const [isNodeSelectorOpen, setIsNodeSelectorOpen] = React.useState(false);
|
|
3135
3058
|
const [isLinkSelectorOpen, setIsLinkSelectorOpen] = React.useState(false);
|
|
3136
3059
|
const has = (item) => !excludeItems.includes(item);
|
|
@@ -3152,7 +3075,7 @@ function BubbleMenuDefault({ excludeItems = [], excludeNodes, placement, offset,
|
|
|
3152
3075
|
return /* @__PURE__ */ jsxs(BubbleMenuRoot, {
|
|
3153
3076
|
excludeNodes,
|
|
3154
3077
|
placement,
|
|
3155
|
-
offset,
|
|
3078
|
+
offset: offset$1,
|
|
3156
3079
|
onHide: handleHide,
|
|
3157
3080
|
className,
|
|
3158
3081
|
children: [
|
|
@@ -3249,7 +3172,7 @@ function ButtonBubbleMenuEditLink({ className, children, onClick, onMouseDown, .
|
|
|
3249
3172
|
|
|
3250
3173
|
//#endregion
|
|
3251
3174
|
//#region src/ui/button-bubble-menu/root.tsx
|
|
3252
|
-
function ButtonBubbleMenuRoot({ onHide, placement = "top", offset = 8, className, children }) {
|
|
3175
|
+
function ButtonBubbleMenuRoot({ onHide, placement = "top", offset: offset$1 = 8, className, children }) {
|
|
3253
3176
|
const { editor } = useCurrentEditor();
|
|
3254
3177
|
const [isEditing, setIsEditing] = React.useState(false);
|
|
3255
3178
|
if (!editor) return null;
|
|
@@ -3259,7 +3182,7 @@ function ButtonBubbleMenuRoot({ onHide, placement = "top", offset = 8, className
|
|
|
3259
3182
|
shouldShow: ({ editor: e, view }) => e.isActive("button") && !view.dom.classList.contains("dragging"),
|
|
3260
3183
|
options: {
|
|
3261
3184
|
placement,
|
|
3262
|
-
offset,
|
|
3185
|
+
offset: offset$1,
|
|
3263
3186
|
onHide: () => {
|
|
3264
3187
|
setIsEditing(false);
|
|
3265
3188
|
onHide?.();
|
|
@@ -3291,10 +3214,10 @@ function ButtonBubbleMenuToolbar({ children, ...rest }) {
|
|
|
3291
3214
|
|
|
3292
3215
|
//#endregion
|
|
3293
3216
|
//#region src/ui/button-bubble-menu/default.tsx
|
|
3294
|
-
function ButtonBubbleMenuDefault({ excludeItems = [], placement, offset, onHide, className }) {
|
|
3217
|
+
function ButtonBubbleMenuDefault({ excludeItems = [], placement, offset: offset$1, onHide, className }) {
|
|
3295
3218
|
return /* @__PURE__ */ jsx(ButtonBubbleMenuRoot, {
|
|
3296
3219
|
placement,
|
|
3297
|
-
offset,
|
|
3220
|
+
offset: offset$1,
|
|
3298
3221
|
onHide,
|
|
3299
3222
|
className,
|
|
3300
3223
|
children: !excludeItems.includes("edit-link") && /* @__PURE__ */ jsx(ButtonBubbleMenuToolbar, { children: /* @__PURE__ */ jsx(ButtonBubbleMenuEditLink, {}) })
|
|
@@ -3344,7 +3267,7 @@ function ImageBubbleMenuEditLink({ className, children, onClick, onMouseDown, ..
|
|
|
3344
3267
|
|
|
3345
3268
|
//#endregion
|
|
3346
3269
|
//#region src/ui/image-bubble-menu/root.tsx
|
|
3347
|
-
function ImageBubbleMenuRoot({ onHide, placement = "top", offset = 8, className, children }) {
|
|
3270
|
+
function ImageBubbleMenuRoot({ onHide, placement = "top", offset: offset$1 = 8, className, children }) {
|
|
3348
3271
|
const { editor } = useCurrentEditor();
|
|
3349
3272
|
const [isEditing, setIsEditing] = React.useState(false);
|
|
3350
3273
|
if (!editor) return null;
|
|
@@ -3354,7 +3277,7 @@ function ImageBubbleMenuRoot({ onHide, placement = "top", offset = 8, className,
|
|
|
3354
3277
|
shouldShow: ({ editor: e, view }) => e.isActive("image") && !view.dom.classList.contains("dragging"),
|
|
3355
3278
|
options: {
|
|
3356
3279
|
placement,
|
|
3357
|
-
offset,
|
|
3280
|
+
offset: offset$1,
|
|
3358
3281
|
onHide: () => {
|
|
3359
3282
|
setIsEditing(false);
|
|
3360
3283
|
onHide?.();
|
|
@@ -3386,10 +3309,10 @@ function ImageBubbleMenuToolbar({ children, ...rest }) {
|
|
|
3386
3309
|
|
|
3387
3310
|
//#endregion
|
|
3388
3311
|
//#region src/ui/image-bubble-menu/default.tsx
|
|
3389
|
-
function ImageBubbleMenuDefault({ excludeItems = [], placement, offset, onHide, className }) {
|
|
3312
|
+
function ImageBubbleMenuDefault({ excludeItems = [], placement, offset: offset$1, onHide, className }) {
|
|
3390
3313
|
return /* @__PURE__ */ jsx(ImageBubbleMenuRoot, {
|
|
3391
3314
|
placement,
|
|
3392
|
-
offset,
|
|
3315
|
+
offset: offset$1,
|
|
3393
3316
|
onHide,
|
|
3394
3317
|
className,
|
|
3395
3318
|
children: !excludeItems.includes("edit-link") && /* @__PURE__ */ jsx(ImageBubbleMenuToolbar, { children: /* @__PURE__ */ jsx(ImageBubbleMenuEditLink, {}) })
|
|
@@ -3561,7 +3484,7 @@ function LinkBubbleMenuOpenLink({ className, children, ...rest }) {
|
|
|
3561
3484
|
|
|
3562
3485
|
//#endregion
|
|
3563
3486
|
//#region src/ui/link-bubble-menu/root.tsx
|
|
3564
|
-
function LinkBubbleMenuRoot({ onHide, placement = "top", offset = 8, className, children }) {
|
|
3487
|
+
function LinkBubbleMenuRoot({ onHide, placement = "top", offset: offset$1 = 8, className, children }) {
|
|
3565
3488
|
const { editor } = useCurrentEditor();
|
|
3566
3489
|
const [isEditing, setIsEditing] = React.useState(false);
|
|
3567
3490
|
const linkHref = useEditorState({
|
|
@@ -3575,7 +3498,7 @@ function LinkBubbleMenuRoot({ onHide, placement = "top", offset = 8, className,
|
|
|
3575
3498
|
shouldShow: ({ editor: e }) => e.isActive("link") && e.view.state.selection.content().size === 0,
|
|
3576
3499
|
options: {
|
|
3577
3500
|
placement,
|
|
3578
|
-
offset,
|
|
3501
|
+
offset: offset$1,
|
|
3579
3502
|
onHide: () => {
|
|
3580
3503
|
setIsEditing(false);
|
|
3581
3504
|
onHide?.();
|
|
@@ -3631,11 +3554,11 @@ function LinkBubbleMenuUnlink({ className, children, onClick, onMouseDown, ...re
|
|
|
3631
3554
|
|
|
3632
3555
|
//#endregion
|
|
3633
3556
|
//#region src/ui/link-bubble-menu/default.tsx
|
|
3634
|
-
function LinkBubbleMenuDefault({ excludeItems = [], placement, offset, onHide, className, validateUrl, onLinkApply, onLinkRemove }) {
|
|
3557
|
+
function LinkBubbleMenuDefault({ excludeItems = [], placement, offset: offset$1, onHide, className, validateUrl, onLinkApply, onLinkRemove }) {
|
|
3635
3558
|
const has = (item) => !excludeItems.includes(item);
|
|
3636
3559
|
return /* @__PURE__ */ jsxs(LinkBubbleMenuRoot, {
|
|
3637
3560
|
placement,
|
|
3638
|
-
offset,
|
|
3561
|
+
offset: offset$1,
|
|
3639
3562
|
onHide,
|
|
3640
3563
|
className,
|
|
3641
3564
|
children: [(has("edit-link") || has("open-link") || has("unlink")) && /* @__PURE__ */ jsxs(LinkBubbleMenuToolbar, { children: [
|
|
@@ -3721,42 +3644,14 @@ function CommandItem({ item, selected, onSelect }) {
|
|
|
3721
3644
|
children: [item.icon, /* @__PURE__ */ jsx("span", { children: item.title })]
|
|
3722
3645
|
});
|
|
3723
3646
|
}
|
|
3724
|
-
function CommandList({ items,
|
|
3725
|
-
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
3647
|
+
function CommandList({ items, query, selectedIndex, onSelect }) {
|
|
3726
3648
|
const containerRef = useRef(null);
|
|
3727
|
-
useEffect(() => {
|
|
3728
|
-
setSelectedIndex(0);
|
|
3729
|
-
}, [items]);
|
|
3730
3649
|
useLayoutEffect(() => {
|
|
3731
3650
|
const container = containerRef.current;
|
|
3732
3651
|
if (!container) return;
|
|
3733
3652
|
const selected = container.querySelector("[data-selected]");
|
|
3734
3653
|
if (selected) updateScrollView(container, selected);
|
|
3735
3654
|
}, [selectedIndex]);
|
|
3736
|
-
const selectItem = useCallback((index) => {
|
|
3737
|
-
const item = items[index];
|
|
3738
|
-
if (item) command(item);
|
|
3739
|
-
}, [items, command]);
|
|
3740
|
-
useImperativeHandle(ref, () => ({ onKeyDown: ({ event }) => {
|
|
3741
|
-
if (items.length === 0) return false;
|
|
3742
|
-
if (event.key === "ArrowUp") {
|
|
3743
|
-
setSelectedIndex((i) => (i + items.length - 1) % items.length);
|
|
3744
|
-
return true;
|
|
3745
|
-
}
|
|
3746
|
-
if (event.key === "ArrowDown") {
|
|
3747
|
-
setSelectedIndex((i) => (i + 1) % items.length);
|
|
3748
|
-
return true;
|
|
3749
|
-
}
|
|
3750
|
-
if (event.key === "Enter") {
|
|
3751
|
-
selectItem(selectedIndex);
|
|
3752
|
-
return true;
|
|
3753
|
-
}
|
|
3754
|
-
return false;
|
|
3755
|
-
} }), [
|
|
3756
|
-
items.length,
|
|
3757
|
-
selectItem,
|
|
3758
|
-
selectedIndex
|
|
3759
|
-
]);
|
|
3760
3655
|
if (items.length === 0) return /* @__PURE__ */ jsx("div", {
|
|
3761
3656
|
"data-re-slash-command": "",
|
|
3762
3657
|
children: /* @__PURE__ */ jsx("div", {
|
|
@@ -3769,7 +3664,7 @@ function CommandList({ items, command, query, ref }) {
|
|
|
3769
3664
|
ref: containerRef,
|
|
3770
3665
|
children: items.map((item, index) => /* @__PURE__ */ jsx(CommandItem, {
|
|
3771
3666
|
item,
|
|
3772
|
-
onSelect: () =>
|
|
3667
|
+
onSelect: () => onSelect(index),
|
|
3773
3668
|
selected: index === selectedIndex
|
|
3774
3669
|
}, item.title))
|
|
3775
3670
|
});
|
|
@@ -3785,7 +3680,7 @@ function CommandList({ items, command, query, ref }) {
|
|
|
3785
3680
|
const currentIndex = flatIndex++;
|
|
3786
3681
|
return /* @__PURE__ */ jsx(CommandItem, {
|
|
3787
3682
|
item,
|
|
3788
|
-
onSelect: () =>
|
|
3683
|
+
onSelect: () => onSelect(currentIndex),
|
|
3789
3684
|
selected: currentIndex === selectedIndex
|
|
3790
3685
|
}, item.title);
|
|
3791
3686
|
})] }, group.category))
|
|
@@ -4003,76 +3898,6 @@ const defaultSlashCommands = [
|
|
|
4003
3898
|
FOUR_COLUMNS
|
|
4004
3899
|
];
|
|
4005
3900
|
|
|
4006
|
-
//#endregion
|
|
4007
|
-
//#region src/ui/slash-command/extension.ts
|
|
4008
|
-
const SlashCommandExtension = Extension.create({
|
|
4009
|
-
name: "slash-command",
|
|
4010
|
-
addOptions() {
|
|
4011
|
-
return { suggestion: {
|
|
4012
|
-
char: "/",
|
|
4013
|
-
allow: ({ editor }) => !editor.isActive("codeBlock"),
|
|
4014
|
-
command: ({ editor, range, props }) => {
|
|
4015
|
-
props.command({
|
|
4016
|
-
editor,
|
|
4017
|
-
range
|
|
4018
|
-
});
|
|
4019
|
-
}
|
|
4020
|
-
} };
|
|
4021
|
-
},
|
|
4022
|
-
addProseMirrorPlugins() {
|
|
4023
|
-
return [Suggestion({
|
|
4024
|
-
pluginKey: new PluginKey("slash-command"),
|
|
4025
|
-
editor: this.editor,
|
|
4026
|
-
...this.options.suggestion
|
|
4027
|
-
})];
|
|
4028
|
-
}
|
|
4029
|
-
});
|
|
4030
|
-
|
|
4031
|
-
//#endregion
|
|
4032
|
-
//#region src/ui/slash-command/render.tsx
|
|
4033
|
-
function createRenderItems(component = CommandList) {
|
|
4034
|
-
return () => {
|
|
4035
|
-
let renderer = null;
|
|
4036
|
-
let popup = null;
|
|
4037
|
-
return {
|
|
4038
|
-
onStart: (props) => {
|
|
4039
|
-
renderer = new ReactRenderer(component, {
|
|
4040
|
-
props,
|
|
4041
|
-
editor: props.editor
|
|
4042
|
-
});
|
|
4043
|
-
if (!props.clientRect) return;
|
|
4044
|
-
popup = tippy("body", {
|
|
4045
|
-
getReferenceClientRect: props.clientRect,
|
|
4046
|
-
appendTo: () => document.body,
|
|
4047
|
-
content: renderer.element,
|
|
4048
|
-
showOnCreate: true,
|
|
4049
|
-
interactive: true,
|
|
4050
|
-
trigger: "manual",
|
|
4051
|
-
placement: "bottom-start"
|
|
4052
|
-
});
|
|
4053
|
-
},
|
|
4054
|
-
onUpdate: (props) => {
|
|
4055
|
-
if (!renderer) return;
|
|
4056
|
-
renderer.updateProps(props);
|
|
4057
|
-
if (popup?.[0] && props.clientRect) popup[0].setProps({ getReferenceClientRect: props.clientRect });
|
|
4058
|
-
},
|
|
4059
|
-
onKeyDown: (props) => {
|
|
4060
|
-
if (props.event.key === "Escape") {
|
|
4061
|
-
popup?.[0]?.hide();
|
|
4062
|
-
return true;
|
|
4063
|
-
}
|
|
4064
|
-
return renderer?.ref?.onKeyDown(props) ?? false;
|
|
4065
|
-
},
|
|
4066
|
-
onExit: () => {
|
|
4067
|
-
popup?.[0]?.destroy();
|
|
4068
|
-
renderer?.destroy();
|
|
4069
|
-
popup = null;
|
|
4070
|
-
renderer = null;
|
|
4071
|
-
}
|
|
4072
|
-
};
|
|
4073
|
-
};
|
|
4074
|
-
}
|
|
4075
|
-
|
|
4076
3901
|
//#endregion
|
|
4077
3902
|
//#region src/ui/slash-command/search.ts
|
|
4078
3903
|
function scoreItem(item, query) {
|
|
@@ -4103,23 +3928,145 @@ function filterAndRankItems(items, query) {
|
|
|
4103
3928
|
}
|
|
4104
3929
|
|
|
4105
3930
|
//#endregion
|
|
4106
|
-
//#region src/ui/slash-command/
|
|
3931
|
+
//#region src/ui/slash-command/root.tsx
|
|
3932
|
+
const pluginKey = new PluginKey("slash-command");
|
|
3933
|
+
const INITIAL_STATE = {
|
|
3934
|
+
active: false,
|
|
3935
|
+
query: "",
|
|
3936
|
+
items: [],
|
|
3937
|
+
clientRect: null
|
|
3938
|
+
};
|
|
4107
3939
|
function defaultFilterItems(items, query, editor) {
|
|
4108
3940
|
return filterAndRankItems(isAtMaxColumnsDepth(editor) ? items.filter((item) => item.category !== "Layout" || !item.title.includes("column")) : items, query);
|
|
4109
3941
|
}
|
|
4110
|
-
function
|
|
4111
|
-
const
|
|
4112
|
-
const
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
3942
|
+
function SlashCommandRoot({ items: itemsProp, filterItems: filterItemsProp, char = "/", allow: allowProp, children }) {
|
|
3943
|
+
const { editor } = useCurrentEditor();
|
|
3944
|
+
const [state, setState] = useState(INITIAL_STATE);
|
|
3945
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
3946
|
+
const itemsRef = useRef(itemsProp ?? defaultSlashCommands);
|
|
3947
|
+
const filterRef = useRef(filterItemsProp ?? defaultFilterItems);
|
|
3948
|
+
const allowRef = useRef(allowProp ?? (({ editor: e }) => !e.isActive("codeBlock")));
|
|
3949
|
+
itemsRef.current = itemsProp ?? defaultSlashCommands;
|
|
3950
|
+
filterRef.current = filterItemsProp ?? defaultFilterItems;
|
|
3951
|
+
allowRef.current = allowProp ?? (({ editor: e }) => !e.isActive("codeBlock"));
|
|
3952
|
+
const commandRef = useRef(null);
|
|
3953
|
+
const suggestionItemsRef = useRef([]);
|
|
3954
|
+
const selectedIndexRef = useRef(0);
|
|
3955
|
+
suggestionItemsRef.current = state.items;
|
|
3956
|
+
selectedIndexRef.current = selectedIndex;
|
|
3957
|
+
const { refs, floatingStyles } = useFloating({
|
|
3958
|
+
open: state.active,
|
|
3959
|
+
placement: "bottom-start",
|
|
3960
|
+
middleware: [
|
|
3961
|
+
offset(8),
|
|
3962
|
+
flip(),
|
|
3963
|
+
shift({ padding: 8 })
|
|
3964
|
+
],
|
|
3965
|
+
whileElementsMounted: autoUpdate
|
|
3966
|
+
});
|
|
3967
|
+
useEffect(() => {
|
|
3968
|
+
if (!state.clientRect) return;
|
|
3969
|
+
refs.setReference({ getBoundingClientRect: state.clientRect });
|
|
3970
|
+
}, [state.clientRect, refs]);
|
|
3971
|
+
useEffect(() => {
|
|
3972
|
+
setSelectedIndex(0);
|
|
3973
|
+
}, [state.items]);
|
|
3974
|
+
const onSelect = useCallback((index) => {
|
|
3975
|
+
const item = suggestionItemsRef.current[index];
|
|
3976
|
+
if (item && commandRef.current) commandRef.current(item);
|
|
3977
|
+
}, []);
|
|
3978
|
+
useEffect(() => {
|
|
3979
|
+
if (!editor) return;
|
|
3980
|
+
const plugin = Suggestion({
|
|
3981
|
+
pluginKey,
|
|
3982
|
+
editor,
|
|
3983
|
+
char,
|
|
3984
|
+
allow: ({ editor: e }) => allowRef.current({ editor: e }),
|
|
3985
|
+
command: ({ editor: e, range, props }) => {
|
|
3986
|
+
props.command({
|
|
3987
|
+
editor: e,
|
|
3988
|
+
range
|
|
3989
|
+
});
|
|
3990
|
+
},
|
|
3991
|
+
items: ({ query, editor: e }) => filterRef.current(itemsRef.current, query, e),
|
|
3992
|
+
render: () => ({
|
|
3993
|
+
onStart: (props) => {
|
|
3994
|
+
commandRef.current = props.command;
|
|
3995
|
+
setState({
|
|
3996
|
+
active: true,
|
|
3997
|
+
query: props.query,
|
|
3998
|
+
items: props.items,
|
|
3999
|
+
clientRect: props.clientRect ?? null
|
|
4000
|
+
});
|
|
4001
|
+
},
|
|
4002
|
+
onUpdate: (props) => {
|
|
4003
|
+
commandRef.current = props.command;
|
|
4004
|
+
setState({
|
|
4005
|
+
active: true,
|
|
4006
|
+
query: props.query,
|
|
4007
|
+
items: props.items,
|
|
4008
|
+
clientRect: props.clientRect ?? null
|
|
4009
|
+
});
|
|
4010
|
+
},
|
|
4011
|
+
onKeyDown: ({ event }) => {
|
|
4012
|
+
if (event.key === "Escape") {
|
|
4013
|
+
setState(INITIAL_STATE);
|
|
4014
|
+
return true;
|
|
4015
|
+
}
|
|
4016
|
+
const items = suggestionItemsRef.current;
|
|
4017
|
+
if (items.length === 0) return false;
|
|
4018
|
+
if (event.key === "ArrowUp") {
|
|
4019
|
+
setSelectedIndex((i) => (i + items.length - 1) % items.length);
|
|
4020
|
+
return true;
|
|
4021
|
+
}
|
|
4022
|
+
if (event.key === "ArrowDown") {
|
|
4023
|
+
setSelectedIndex((i) => (i + 1) % items.length);
|
|
4024
|
+
return true;
|
|
4025
|
+
}
|
|
4026
|
+
if (event.key === "Enter") {
|
|
4027
|
+
const item = items[selectedIndexRef.current];
|
|
4028
|
+
if (item && commandRef.current) commandRef.current(item);
|
|
4029
|
+
return true;
|
|
4030
|
+
}
|
|
4031
|
+
return false;
|
|
4032
|
+
},
|
|
4033
|
+
onExit: () => {
|
|
4034
|
+
setState(INITIAL_STATE);
|
|
4035
|
+
requestAnimationFrame(() => {
|
|
4036
|
+
commandRef.current = null;
|
|
4037
|
+
});
|
|
4038
|
+
}
|
|
4039
|
+
})
|
|
4040
|
+
});
|
|
4041
|
+
editor.registerPlugin(plugin, (newPlugin, plugins) => [newPlugin, ...plugins]);
|
|
4042
|
+
return () => {
|
|
4043
|
+
editor.unregisterPlugin(pluginKey);
|
|
4044
|
+
};
|
|
4045
|
+
}, [editor, char]);
|
|
4046
|
+
if (!editor || !state.active) return null;
|
|
4047
|
+
const renderProps = {
|
|
4048
|
+
items: state.items,
|
|
4049
|
+
query: state.query,
|
|
4050
|
+
selectedIndex,
|
|
4051
|
+
onSelect
|
|
4052
|
+
};
|
|
4053
|
+
let content;
|
|
4054
|
+
if (children) content = children(renderProps);
|
|
4055
|
+
else content = /* @__PURE__ */ jsx(CommandList, { ...renderProps });
|
|
4056
|
+
return createPortal(/* @__PURE__ */ jsx("div", {
|
|
4057
|
+
ref: refs.setFloating,
|
|
4058
|
+
style: floatingStyles,
|
|
4059
|
+
children: content
|
|
4060
|
+
}), document.body);
|
|
4117
4061
|
}
|
|
4118
4062
|
|
|
4119
4063
|
//#endregion
|
|
4120
4064
|
//#region src/ui/slash-command/index.ts
|
|
4121
|
-
const SlashCommand =
|
|
4065
|
+
const SlashCommand = {
|
|
4066
|
+
Root: SlashCommandRoot,
|
|
4067
|
+
CommandList
|
|
4068
|
+
};
|
|
4122
4069
|
|
|
4123
4070
|
//#endregion
|
|
4124
|
-
export { AlignmentAttribute, BULLET_LIST, BUTTON, Blockquote, Body, Bold, BubbleMenu, BubbleMenuAlignCenter, BubbleMenuAlignLeft, BubbleMenuAlignRight, BubbleMenuBold, BubbleMenuCode, BubbleMenuDefault, BubbleMenuItalic, BubbleMenuItem, BubbleMenuItemGroup, BubbleMenuLinkSelector, BubbleMenuNodeSelector, BubbleMenuRoot, BubbleMenuSeparator, BubbleMenuStrike, BubbleMenuUnderline, BubbleMenuUppercase, BulletList, Button, ButtonBubbleMenu, ButtonBubbleMenuDefault, ButtonBubbleMenuEditLink, ButtonBubbleMenuRoot, ButtonBubbleMenuToolbar, CODE, COLUMN_PARENT_TYPES, ClassAttribute, Code, CodeBlockPrism, ColumnsColumn, CommandList, DIVIDER, Div, Divider, EmailNode, FOUR_COLUMNS, FourColumns, GlobalContent, H1, H2, H3, HardBreak, Heading, ImageBubbleMenu, ImageBubbleMenuDefault, ImageBubbleMenuEditLink, ImageBubbleMenuRoot, ImageBubbleMenuToolbar, Italic, Link, LinkBubbleMenu, LinkBubbleMenuDefault, LinkBubbleMenuEditLink, LinkBubbleMenuForm, LinkBubbleMenuOpenLink, LinkBubbleMenuRoot, LinkBubbleMenuToolbar, LinkBubbleMenuUnlink, ListItem, MAX_COLUMNS_DEPTH, MaxNesting, NUMBERED_LIST, NodeSelectorContent, NodeSelectorRoot, NodeSelectorTrigger, OrderedList, Paragraph, Placeholder, PreservedStyle, PreviewText, QUOTE, SECTION, Section, SlashCommand, StarterKit, Strike, StyleAttribute, Sup, TEXT, THREE_COLUMNS, TWO_COLUMNS, Table, TableCell, TableHeader, TableRow, ThreeColumns, TwoColumns, Uppercase, composeReactEmail,
|
|
4071
|
+
export { AlignmentAttribute, BULLET_LIST, BUTTON, Blockquote, Body, Bold, BubbleMenu, BubbleMenuAlignCenter, BubbleMenuAlignLeft, BubbleMenuAlignRight, BubbleMenuBold, BubbleMenuCode, BubbleMenuDefault, BubbleMenuItalic, BubbleMenuItem, BubbleMenuItemGroup, BubbleMenuLinkSelector, BubbleMenuNodeSelector, BubbleMenuRoot, BubbleMenuSeparator, BubbleMenuStrike, BubbleMenuUnderline, BubbleMenuUppercase, BulletList, Button, ButtonBubbleMenu, ButtonBubbleMenuDefault, ButtonBubbleMenuEditLink, ButtonBubbleMenuRoot, ButtonBubbleMenuToolbar, CODE, COLUMN_PARENT_TYPES, ClassAttribute, Code, CodeBlockPrism, ColumnsColumn, CommandList, DIVIDER, Div, Divider, EmailNode, FOUR_COLUMNS, FourColumns, GlobalContent, H1, H2, H3, HardBreak, Heading, ImageBubbleMenu, ImageBubbleMenuDefault, ImageBubbleMenuEditLink, ImageBubbleMenuRoot, ImageBubbleMenuToolbar, Italic, Link, LinkBubbleMenu, LinkBubbleMenuDefault, LinkBubbleMenuEditLink, LinkBubbleMenuForm, LinkBubbleMenuOpenLink, LinkBubbleMenuRoot, LinkBubbleMenuToolbar, LinkBubbleMenuUnlink, ListItem, MAX_COLUMNS_DEPTH, MaxNesting, NUMBERED_LIST, NodeSelectorContent, NodeSelectorRoot, NodeSelectorTrigger, OrderedList, Paragraph, Placeholder, PreservedStyle, PreviewText, QUOTE, SECTION, Section, SlashCommand, StarterKit, Strike, StyleAttribute, Sup, TEXT, THREE_COLUMNS, TWO_COLUMNS, Table, TableCell, TableHeader, TableRow, ThreeColumns, TwoColumns, Underline, Uppercase, composeReactEmail, defaultSlashCommands, editorEventBus, filterAndRankItems, getColumnsDepth, getGlobalContent, isAtMaxColumnsDepth, isDocumentVisuallyEmpty, isInsideNode, processStylesForUnlink, scoreItem, setTextAlignment, useButtonBubbleMenuContext, useEditor, useImageBubbleMenuContext, useLinkBubbleMenuContext };
|
|
4125
4072
|
//# sourceMappingURL=index.mjs.map
|