@react-email/editor 0.0.0-experimental.13 → 0.0.0-experimental.15
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 +539 -3
- package/dist/index.d.cts +182 -45
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +141 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +516 -6
- package/dist/index.mjs.map +1 -1
- package/dist/ui/bubble-menu/bubble-menu.css +32 -17
- package/dist/ui/slash-command/slash-command.css +44 -0
- package/dist/ui/themes/default.css +241 -31
- package/package.json +7 -2
package/dist/index.mjs
CHANGED
|
@@ -9,11 +9,14 @@ import { Decoration, DecorationSet } from "@tiptap/pm/view";
|
|
|
9
9
|
import { fromHtml } from "hast-util-from-html";
|
|
10
10
|
import Prism from "prismjs";
|
|
11
11
|
import TipTapPlaceholder from "@tiptap/extension-placeholder";
|
|
12
|
-
import { useCurrentEditor, useEditorState } from "@tiptap/react";
|
|
13
|
-
import { AlignCenterIcon, AlignLeftIcon, AlignRightIcon, BoldIcon, CaseUpperIcon, Check, ChevronDown, Code, CodeIcon, ExternalLinkIcon, Heading1, Heading2, Heading3, ItalicIcon, LinkIcon, List, ListOrdered, PencilIcon, StrikethroughIcon, TextIcon, TextQuote, UnderlineIcon, UnlinkIcon } from "lucide-react";
|
|
12
|
+
import { ReactRenderer, useCurrentEditor, useEditorState } from "@tiptap/react";
|
|
13
|
+
import { AlignCenterIcon, AlignLeftIcon, AlignRightIcon, BoldIcon, CaseUpperIcon, Check, ChevronDown, Code, 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";
|
|
14
14
|
import * as React from "react";
|
|
15
|
+
import { useCallback, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
|
|
15
16
|
import * as Popover from "@radix-ui/react-popover";
|
|
16
17
|
import { BubbleMenu as BubbleMenu$1 } from "@tiptap/react/menus";
|
|
18
|
+
import Suggestion from "@tiptap/suggestion";
|
|
19
|
+
import tippy from "tippy.js";
|
|
17
20
|
|
|
18
21
|
//#region src/core/email-node.ts
|
|
19
22
|
var EmailNode = class EmailNode extends Node {
|
|
@@ -2507,12 +2510,25 @@ function ButtonBubbleMenuToolbar({ children, ...rest }) {
|
|
|
2507
2510
|
});
|
|
2508
2511
|
}
|
|
2509
2512
|
|
|
2513
|
+
//#endregion
|
|
2514
|
+
//#region src/ui/button-bubble-menu/default.tsx
|
|
2515
|
+
function ButtonBubbleMenuDefault({ excludeItems = [], placement, offset, onHide, className }) {
|
|
2516
|
+
return /* @__PURE__ */ jsx(ButtonBubbleMenuRoot, {
|
|
2517
|
+
placement,
|
|
2518
|
+
offset,
|
|
2519
|
+
onHide,
|
|
2520
|
+
className,
|
|
2521
|
+
children: !excludeItems.includes("edit-link") && /* @__PURE__ */ jsx(ButtonBubbleMenuToolbar, { children: /* @__PURE__ */ jsx(ButtonBubbleMenuEditLink, {}) })
|
|
2522
|
+
});
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2510
2525
|
//#endregion
|
|
2511
2526
|
//#region src/ui/button-bubble-menu/index.ts
|
|
2512
2527
|
const ButtonBubbleMenu = {
|
|
2513
2528
|
Root: ButtonBubbleMenuRoot,
|
|
2514
2529
|
Toolbar: ButtonBubbleMenuToolbar,
|
|
2515
|
-
EditLink: ButtonBubbleMenuEditLink
|
|
2530
|
+
EditLink: ButtonBubbleMenuEditLink,
|
|
2531
|
+
Default: ButtonBubbleMenuDefault
|
|
2516
2532
|
};
|
|
2517
2533
|
|
|
2518
2534
|
//#endregion
|
|
@@ -2589,12 +2605,25 @@ function ImageBubbleMenuToolbar({ children, ...rest }) {
|
|
|
2589
2605
|
});
|
|
2590
2606
|
}
|
|
2591
2607
|
|
|
2608
|
+
//#endregion
|
|
2609
|
+
//#region src/ui/image-bubble-menu/default.tsx
|
|
2610
|
+
function ImageBubbleMenuDefault({ excludeItems = [], placement, offset, onHide, className }) {
|
|
2611
|
+
return /* @__PURE__ */ jsx(ImageBubbleMenuRoot, {
|
|
2612
|
+
placement,
|
|
2613
|
+
offset,
|
|
2614
|
+
onHide,
|
|
2615
|
+
className,
|
|
2616
|
+
children: !excludeItems.includes("edit-link") && /* @__PURE__ */ jsx(ImageBubbleMenuToolbar, { children: /* @__PURE__ */ jsx(ImageBubbleMenuEditLink, {}) })
|
|
2617
|
+
});
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2592
2620
|
//#endregion
|
|
2593
2621
|
//#region src/ui/image-bubble-menu/index.ts
|
|
2594
2622
|
const ImageBubbleMenu = {
|
|
2595
2623
|
Root: ImageBubbleMenuRoot,
|
|
2596
2624
|
Toolbar: ImageBubbleMenuToolbar,
|
|
2597
|
-
EditLink: ImageBubbleMenuEditLink
|
|
2625
|
+
EditLink: ImageBubbleMenuEditLink,
|
|
2626
|
+
Default: ImageBubbleMenuDefault
|
|
2598
2627
|
};
|
|
2599
2628
|
|
|
2600
2629
|
//#endregion
|
|
@@ -2821,6 +2850,27 @@ function LinkBubbleMenuUnlink({ className, children, onClick, onMouseDown, ...re
|
|
|
2821
2850
|
});
|
|
2822
2851
|
}
|
|
2823
2852
|
|
|
2853
|
+
//#endregion
|
|
2854
|
+
//#region src/ui/link-bubble-menu/default.tsx
|
|
2855
|
+
function LinkBubbleMenuDefault({ excludeItems = [], placement, offset, onHide, className, validateUrl, onLinkApply, onLinkRemove }) {
|
|
2856
|
+
const has = (item) => !excludeItems.includes(item);
|
|
2857
|
+
return /* @__PURE__ */ jsxs(LinkBubbleMenuRoot, {
|
|
2858
|
+
placement,
|
|
2859
|
+
offset,
|
|
2860
|
+
onHide,
|
|
2861
|
+
className,
|
|
2862
|
+
children: [(has("edit-link") || has("open-link") || has("unlink")) && /* @__PURE__ */ jsxs(LinkBubbleMenuToolbar, { children: [
|
|
2863
|
+
has("edit-link") && /* @__PURE__ */ jsx(LinkBubbleMenuEditLink, {}),
|
|
2864
|
+
has("open-link") && /* @__PURE__ */ jsx(LinkBubbleMenuOpenLink, {}),
|
|
2865
|
+
has("unlink") && /* @__PURE__ */ jsx(LinkBubbleMenuUnlink, {})
|
|
2866
|
+
] }), /* @__PURE__ */ jsx(LinkBubbleMenuForm, {
|
|
2867
|
+
validateUrl,
|
|
2868
|
+
onLinkApply,
|
|
2869
|
+
onLinkRemove
|
|
2870
|
+
})]
|
|
2871
|
+
});
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2824
2874
|
//#endregion
|
|
2825
2875
|
//#region src/ui/link-bubble-menu/index.ts
|
|
2826
2876
|
const LinkBubbleMenu = {
|
|
@@ -2829,9 +2879,469 @@ const LinkBubbleMenu = {
|
|
|
2829
2879
|
Form: LinkBubbleMenuForm,
|
|
2830
2880
|
EditLink: LinkBubbleMenuEditLink,
|
|
2831
2881
|
Unlink: LinkBubbleMenuUnlink,
|
|
2832
|
-
OpenLink: LinkBubbleMenuOpenLink
|
|
2882
|
+
OpenLink: LinkBubbleMenuOpenLink,
|
|
2883
|
+
Default: LinkBubbleMenuDefault
|
|
2884
|
+
};
|
|
2885
|
+
|
|
2886
|
+
//#endregion
|
|
2887
|
+
//#region src/ui/slash-command/utils.ts
|
|
2888
|
+
function isInsideNode(editor, type) {
|
|
2889
|
+
const { $from } = editor.state.selection;
|
|
2890
|
+
for (let d = $from.depth; d > 0; d--) if ($from.node(d).type.name === type) return true;
|
|
2891
|
+
return false;
|
|
2892
|
+
}
|
|
2893
|
+
function isAtMaxColumnsDepth(editor) {
|
|
2894
|
+
const { from } = editor.state.selection;
|
|
2895
|
+
return getColumnsDepth(editor.state.doc, from) >= MAX_COLUMNS_DEPTH;
|
|
2896
|
+
}
|
|
2897
|
+
function updateScrollView(container, item) {
|
|
2898
|
+
const containerRect = container.getBoundingClientRect();
|
|
2899
|
+
const itemRect = item.getBoundingClientRect();
|
|
2900
|
+
if (itemRect.top < containerRect.top) container.scrollTop -= containerRect.top - itemRect.top;
|
|
2901
|
+
else if (itemRect.bottom > containerRect.bottom) container.scrollTop += itemRect.bottom - containerRect.bottom;
|
|
2902
|
+
}
|
|
2903
|
+
|
|
2904
|
+
//#endregion
|
|
2905
|
+
//#region src/ui/slash-command/command-list.tsx
|
|
2906
|
+
const CATEGORY_ORDER = [
|
|
2907
|
+
"Text",
|
|
2908
|
+
"Media",
|
|
2909
|
+
"Layout",
|
|
2910
|
+
"Utility"
|
|
2911
|
+
];
|
|
2912
|
+
function groupByCategory(items) {
|
|
2913
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2914
|
+
for (const item of items) {
|
|
2915
|
+
const existing = seen.get(item.category);
|
|
2916
|
+
if (existing) existing.push(item);
|
|
2917
|
+
else seen.set(item.category, [item]);
|
|
2918
|
+
}
|
|
2919
|
+
const ordered = [];
|
|
2920
|
+
for (const cat of CATEGORY_ORDER) {
|
|
2921
|
+
const group = seen.get(cat);
|
|
2922
|
+
if (group) {
|
|
2923
|
+
ordered.push({
|
|
2924
|
+
category: cat,
|
|
2925
|
+
items: group
|
|
2926
|
+
});
|
|
2927
|
+
seen.delete(cat);
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
for (const [category, group] of seen) ordered.push({
|
|
2931
|
+
category,
|
|
2932
|
+
items: group
|
|
2933
|
+
});
|
|
2934
|
+
return ordered;
|
|
2935
|
+
}
|
|
2936
|
+
function CommandItem({ item, selected, onSelect }) {
|
|
2937
|
+
const Icon = item.icon;
|
|
2938
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
2939
|
+
"data-re-slash-command-item": "",
|
|
2940
|
+
"data-selected": selected || void 0,
|
|
2941
|
+
onClick: onSelect,
|
|
2942
|
+
type: "button",
|
|
2943
|
+
children: [/* @__PURE__ */ jsx(Icon, { size: 20 }), /* @__PURE__ */ jsx("span", { children: item.title })]
|
|
2944
|
+
});
|
|
2945
|
+
}
|
|
2946
|
+
function CommandList({ items, command, query, ref }) {
|
|
2947
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
2948
|
+
const containerRef = useRef(null);
|
|
2949
|
+
useEffect(() => {
|
|
2950
|
+
setSelectedIndex(0);
|
|
2951
|
+
}, [items]);
|
|
2952
|
+
useLayoutEffect(() => {
|
|
2953
|
+
const container = containerRef.current;
|
|
2954
|
+
if (!container) return;
|
|
2955
|
+
const selected = container.querySelector("[data-selected]");
|
|
2956
|
+
if (selected) updateScrollView(container, selected);
|
|
2957
|
+
}, [selectedIndex]);
|
|
2958
|
+
const selectItem = useCallback((index) => {
|
|
2959
|
+
const item = items[index];
|
|
2960
|
+
if (item) command(item);
|
|
2961
|
+
}, [items, command]);
|
|
2962
|
+
useImperativeHandle(ref, () => ({ onKeyDown: ({ event }) => {
|
|
2963
|
+
if (items.length === 0) return false;
|
|
2964
|
+
if (event.key === "ArrowUp") {
|
|
2965
|
+
setSelectedIndex((i) => (i + items.length - 1) % items.length);
|
|
2966
|
+
return true;
|
|
2967
|
+
}
|
|
2968
|
+
if (event.key === "ArrowDown") {
|
|
2969
|
+
setSelectedIndex((i) => (i + 1) % items.length);
|
|
2970
|
+
return true;
|
|
2971
|
+
}
|
|
2972
|
+
if (event.key === "Enter") {
|
|
2973
|
+
selectItem(selectedIndex);
|
|
2974
|
+
return true;
|
|
2975
|
+
}
|
|
2976
|
+
return false;
|
|
2977
|
+
} }), [
|
|
2978
|
+
items.length,
|
|
2979
|
+
selectItem,
|
|
2980
|
+
selectedIndex
|
|
2981
|
+
]);
|
|
2982
|
+
if (items.length === 0) return /* @__PURE__ */ jsx("div", {
|
|
2983
|
+
"data-re-slash-command": "",
|
|
2984
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
2985
|
+
"data-re-slash-command-empty": "",
|
|
2986
|
+
children: "No results"
|
|
2987
|
+
})
|
|
2988
|
+
});
|
|
2989
|
+
if (query.trim().length > 0) return /* @__PURE__ */ jsx("div", {
|
|
2990
|
+
"data-re-slash-command": "",
|
|
2991
|
+
ref: containerRef,
|
|
2992
|
+
children: items.map((item, index) => /* @__PURE__ */ jsx(CommandItem, {
|
|
2993
|
+
item,
|
|
2994
|
+
onSelect: () => selectItem(index),
|
|
2995
|
+
selected: index === selectedIndex
|
|
2996
|
+
}, item.title))
|
|
2997
|
+
});
|
|
2998
|
+
const groups = groupByCategory(items);
|
|
2999
|
+
let flatIndex = 0;
|
|
3000
|
+
return /* @__PURE__ */ jsx("div", {
|
|
3001
|
+
"data-re-slash-command": "",
|
|
3002
|
+
ref: containerRef,
|
|
3003
|
+
children: groups.map((group) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
|
|
3004
|
+
"data-re-slash-command-category": "",
|
|
3005
|
+
children: group.category
|
|
3006
|
+
}), group.items.map((item) => {
|
|
3007
|
+
const currentIndex = flatIndex++;
|
|
3008
|
+
return /* @__PURE__ */ jsx(CommandItem, {
|
|
3009
|
+
item,
|
|
3010
|
+
onSelect: () => selectItem(currentIndex),
|
|
3011
|
+
selected: currentIndex === selectedIndex
|
|
3012
|
+
}, item.title);
|
|
3013
|
+
})] }, group.category))
|
|
3014
|
+
});
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
//#endregion
|
|
3018
|
+
//#region src/ui/slash-command/commands.ts
|
|
3019
|
+
const TEXT = {
|
|
3020
|
+
title: "Text",
|
|
3021
|
+
description: "Plain text block",
|
|
3022
|
+
icon: Text,
|
|
3023
|
+
category: "Text",
|
|
3024
|
+
searchTerms: ["p", "paragraph"],
|
|
3025
|
+
command: ({ editor, range }) => {
|
|
3026
|
+
editor.chain().focus().deleteRange(range).toggleNode("paragraph", "paragraph").run();
|
|
3027
|
+
}
|
|
3028
|
+
};
|
|
3029
|
+
const H1 = {
|
|
3030
|
+
title: "Title",
|
|
3031
|
+
description: "Large heading",
|
|
3032
|
+
icon: Heading1,
|
|
3033
|
+
category: "Text",
|
|
3034
|
+
searchTerms: [
|
|
3035
|
+
"title",
|
|
3036
|
+
"big",
|
|
3037
|
+
"large",
|
|
3038
|
+
"h1"
|
|
3039
|
+
],
|
|
3040
|
+
command: ({ editor, range }) => {
|
|
3041
|
+
editor.chain().focus().deleteRange(range).setNode("heading", { level: 1 }).run();
|
|
3042
|
+
}
|
|
3043
|
+
};
|
|
3044
|
+
const H2 = {
|
|
3045
|
+
title: "Subtitle",
|
|
3046
|
+
description: "Medium heading",
|
|
3047
|
+
icon: Heading2,
|
|
3048
|
+
category: "Text",
|
|
3049
|
+
searchTerms: [
|
|
3050
|
+
"subtitle",
|
|
3051
|
+
"medium",
|
|
3052
|
+
"h2"
|
|
3053
|
+
],
|
|
3054
|
+
command: ({ editor, range }) => {
|
|
3055
|
+
editor.chain().focus().deleteRange(range).setNode("heading", { level: 2 }).run();
|
|
3056
|
+
}
|
|
3057
|
+
};
|
|
3058
|
+
const H3 = {
|
|
3059
|
+
title: "Heading",
|
|
3060
|
+
description: "Small heading",
|
|
3061
|
+
icon: Heading3,
|
|
3062
|
+
category: "Text",
|
|
3063
|
+
searchTerms: [
|
|
3064
|
+
"subtitle",
|
|
3065
|
+
"small",
|
|
3066
|
+
"h3"
|
|
3067
|
+
],
|
|
3068
|
+
command: ({ editor, range }) => {
|
|
3069
|
+
editor.chain().focus().deleteRange(range).setNode("heading", { level: 3 }).run();
|
|
3070
|
+
}
|
|
3071
|
+
};
|
|
3072
|
+
const BULLET_LIST = {
|
|
3073
|
+
title: "Bullet list",
|
|
3074
|
+
description: "Unordered list",
|
|
3075
|
+
icon: List,
|
|
3076
|
+
category: "Text",
|
|
3077
|
+
searchTerms: ["unordered", "point"],
|
|
3078
|
+
command: ({ editor, range }) => {
|
|
3079
|
+
editor.chain().focus().deleteRange(range).toggleBulletList().run();
|
|
3080
|
+
}
|
|
3081
|
+
};
|
|
3082
|
+
const NUMBERED_LIST = {
|
|
3083
|
+
title: "Numbered list",
|
|
3084
|
+
description: "Ordered list",
|
|
3085
|
+
icon: ListOrdered,
|
|
3086
|
+
category: "Text",
|
|
3087
|
+
searchTerms: ["ordered"],
|
|
3088
|
+
command: ({ editor, range }) => {
|
|
3089
|
+
editor.chain().focus().deleteRange(range).toggleOrderedList().run();
|
|
3090
|
+
}
|
|
3091
|
+
};
|
|
3092
|
+
const QUOTE = {
|
|
3093
|
+
title: "Quote",
|
|
3094
|
+
description: "Block quote",
|
|
3095
|
+
icon: TextQuote,
|
|
3096
|
+
category: "Text",
|
|
3097
|
+
searchTerms: ["blockquote"],
|
|
3098
|
+
command: ({ editor, range }) => {
|
|
3099
|
+
editor.chain().focus().deleteRange(range).toggleNode("paragraph", "paragraph").toggleBlockquote().run();
|
|
3100
|
+
}
|
|
3101
|
+
};
|
|
3102
|
+
const CODE = {
|
|
3103
|
+
title: "Code block",
|
|
3104
|
+
description: "Code snippet",
|
|
3105
|
+
icon: SquareCode,
|
|
3106
|
+
category: "Text",
|
|
3107
|
+
searchTerms: ["codeblock"],
|
|
3108
|
+
command: ({ editor, range }) => {
|
|
3109
|
+
editor.chain().focus().deleteRange(range).toggleCodeBlock().run();
|
|
3110
|
+
}
|
|
3111
|
+
};
|
|
3112
|
+
const BUTTON = {
|
|
3113
|
+
title: "Button",
|
|
3114
|
+
description: "Clickable button",
|
|
3115
|
+
icon: MousePointer,
|
|
3116
|
+
category: "Layout",
|
|
3117
|
+
searchTerms: ["button"],
|
|
3118
|
+
command: ({ editor, range }) => {
|
|
3119
|
+
editor.chain().focus().deleteRange(range).setButton().run();
|
|
3120
|
+
}
|
|
3121
|
+
};
|
|
3122
|
+
const DIVIDER = {
|
|
3123
|
+
title: "Divider",
|
|
3124
|
+
description: "Horizontal separator",
|
|
3125
|
+
icon: SplitSquareVertical,
|
|
3126
|
+
category: "Layout",
|
|
3127
|
+
searchTerms: [
|
|
3128
|
+
"hr",
|
|
3129
|
+
"divider",
|
|
3130
|
+
"separator"
|
|
3131
|
+
],
|
|
3132
|
+
command: ({ editor, range }) => {
|
|
3133
|
+
editor.chain().focus().deleteRange(range).setHorizontalRule().run();
|
|
3134
|
+
}
|
|
2833
3135
|
};
|
|
3136
|
+
const SECTION = {
|
|
3137
|
+
title: "Section",
|
|
3138
|
+
description: "Content section",
|
|
3139
|
+
icon: Rows2,
|
|
3140
|
+
category: "Layout",
|
|
3141
|
+
searchTerms: [
|
|
3142
|
+
"section",
|
|
3143
|
+
"row",
|
|
3144
|
+
"container"
|
|
3145
|
+
],
|
|
3146
|
+
command: ({ editor, range }) => {
|
|
3147
|
+
editor.chain().focus().deleteRange(range).insertSection().run();
|
|
3148
|
+
}
|
|
3149
|
+
};
|
|
3150
|
+
const TWO_COLUMNS = {
|
|
3151
|
+
title: "2 columns",
|
|
3152
|
+
description: "Two column layout",
|
|
3153
|
+
icon: Columns2,
|
|
3154
|
+
category: "Layout",
|
|
3155
|
+
searchTerms: [
|
|
3156
|
+
"columns",
|
|
3157
|
+
"column",
|
|
3158
|
+
"layout",
|
|
3159
|
+
"grid",
|
|
3160
|
+
"split",
|
|
3161
|
+
"side-by-side",
|
|
3162
|
+
"multi-column",
|
|
3163
|
+
"row",
|
|
3164
|
+
"two",
|
|
3165
|
+
"2"
|
|
3166
|
+
],
|
|
3167
|
+
command: ({ editor, range }) => {
|
|
3168
|
+
editor.chain().focus().deleteRange(range).insertColumns(2).run();
|
|
3169
|
+
}
|
|
3170
|
+
};
|
|
3171
|
+
const THREE_COLUMNS = {
|
|
3172
|
+
title: "3 columns",
|
|
3173
|
+
description: "Three column layout",
|
|
3174
|
+
icon: Columns3,
|
|
3175
|
+
category: "Layout",
|
|
3176
|
+
searchTerms: [
|
|
3177
|
+
"columns",
|
|
3178
|
+
"column",
|
|
3179
|
+
"layout",
|
|
3180
|
+
"grid",
|
|
3181
|
+
"split",
|
|
3182
|
+
"multi-column",
|
|
3183
|
+
"row",
|
|
3184
|
+
"three",
|
|
3185
|
+
"3"
|
|
3186
|
+
],
|
|
3187
|
+
command: ({ editor, range }) => {
|
|
3188
|
+
editor.chain().focus().deleteRange(range).insertColumns(3).run();
|
|
3189
|
+
}
|
|
3190
|
+
};
|
|
3191
|
+
const FOUR_COLUMNS = {
|
|
3192
|
+
title: "4 columns",
|
|
3193
|
+
description: "Four column layout",
|
|
3194
|
+
icon: Columns4,
|
|
3195
|
+
category: "Layout",
|
|
3196
|
+
searchTerms: [
|
|
3197
|
+
"columns",
|
|
3198
|
+
"column",
|
|
3199
|
+
"layout",
|
|
3200
|
+
"grid",
|
|
3201
|
+
"split",
|
|
3202
|
+
"multi-column",
|
|
3203
|
+
"row",
|
|
3204
|
+
"four",
|
|
3205
|
+
"4"
|
|
3206
|
+
],
|
|
3207
|
+
command: ({ editor, range }) => {
|
|
3208
|
+
editor.chain().focus().deleteRange(range).insertColumns(4).run();
|
|
3209
|
+
}
|
|
3210
|
+
};
|
|
3211
|
+
const defaultSlashCommands = [
|
|
3212
|
+
TEXT,
|
|
3213
|
+
H1,
|
|
3214
|
+
H2,
|
|
3215
|
+
H3,
|
|
3216
|
+
BULLET_LIST,
|
|
3217
|
+
NUMBERED_LIST,
|
|
3218
|
+
QUOTE,
|
|
3219
|
+
CODE,
|
|
3220
|
+
BUTTON,
|
|
3221
|
+
DIVIDER,
|
|
3222
|
+
SECTION,
|
|
3223
|
+
TWO_COLUMNS,
|
|
3224
|
+
THREE_COLUMNS,
|
|
3225
|
+
FOUR_COLUMNS
|
|
3226
|
+
];
|
|
3227
|
+
|
|
3228
|
+
//#endregion
|
|
3229
|
+
//#region src/ui/slash-command/extension.ts
|
|
3230
|
+
const SlashCommandExtension = Extension.create({
|
|
3231
|
+
name: "slash-command",
|
|
3232
|
+
addOptions() {
|
|
3233
|
+
return { suggestion: {
|
|
3234
|
+
char: "/",
|
|
3235
|
+
allow: ({ editor }) => !editor.isActive("codeBlock"),
|
|
3236
|
+
command: ({ editor, range, props }) => {
|
|
3237
|
+
props.command({
|
|
3238
|
+
editor,
|
|
3239
|
+
range
|
|
3240
|
+
});
|
|
3241
|
+
}
|
|
3242
|
+
} };
|
|
3243
|
+
},
|
|
3244
|
+
addProseMirrorPlugins() {
|
|
3245
|
+
return [Suggestion({
|
|
3246
|
+
pluginKey: new PluginKey("slash-command"),
|
|
3247
|
+
editor: this.editor,
|
|
3248
|
+
...this.options.suggestion
|
|
3249
|
+
})];
|
|
3250
|
+
}
|
|
3251
|
+
});
|
|
3252
|
+
|
|
3253
|
+
//#endregion
|
|
3254
|
+
//#region src/ui/slash-command/render.tsx
|
|
3255
|
+
function createRenderItems(component = CommandList) {
|
|
3256
|
+
return () => {
|
|
3257
|
+
let renderer = null;
|
|
3258
|
+
let popup = null;
|
|
3259
|
+
return {
|
|
3260
|
+
onStart: (props) => {
|
|
3261
|
+
renderer = new ReactRenderer(component, {
|
|
3262
|
+
props,
|
|
3263
|
+
editor: props.editor
|
|
3264
|
+
});
|
|
3265
|
+
if (!props.clientRect) return;
|
|
3266
|
+
popup = tippy("body", {
|
|
3267
|
+
getReferenceClientRect: props.clientRect,
|
|
3268
|
+
appendTo: () => document.body,
|
|
3269
|
+
content: renderer.element,
|
|
3270
|
+
showOnCreate: true,
|
|
3271
|
+
interactive: true,
|
|
3272
|
+
trigger: "manual",
|
|
3273
|
+
placement: "bottom-start"
|
|
3274
|
+
});
|
|
3275
|
+
},
|
|
3276
|
+
onUpdate: (props) => {
|
|
3277
|
+
if (!renderer) return;
|
|
3278
|
+
renderer.updateProps(props);
|
|
3279
|
+
if (popup?.[0] && props.clientRect) popup[0].setProps({ getReferenceClientRect: props.clientRect });
|
|
3280
|
+
},
|
|
3281
|
+
onKeyDown: (props) => {
|
|
3282
|
+
if (props.event.key === "Escape") {
|
|
3283
|
+
popup?.[0]?.hide();
|
|
3284
|
+
return true;
|
|
3285
|
+
}
|
|
3286
|
+
return renderer?.ref?.onKeyDown(props) ?? false;
|
|
3287
|
+
},
|
|
3288
|
+
onExit: () => {
|
|
3289
|
+
popup?.[0]?.destroy();
|
|
3290
|
+
renderer?.destroy();
|
|
3291
|
+
popup = null;
|
|
3292
|
+
renderer = null;
|
|
3293
|
+
}
|
|
3294
|
+
};
|
|
3295
|
+
};
|
|
3296
|
+
}
|
|
3297
|
+
|
|
3298
|
+
//#endregion
|
|
3299
|
+
//#region src/ui/slash-command/search.ts
|
|
3300
|
+
function scoreItem(item, query) {
|
|
3301
|
+
if (!query) return 100;
|
|
3302
|
+
const q = query.toLowerCase();
|
|
3303
|
+
const title = item.title.toLowerCase();
|
|
3304
|
+
const description = item.description.toLowerCase();
|
|
3305
|
+
const terms = item.searchTerms?.map((t) => t.toLowerCase()) ?? [];
|
|
3306
|
+
if (title === q) return 100;
|
|
3307
|
+
if (title.startsWith(q)) return 90;
|
|
3308
|
+
if (title.split(/\s+/).some((w) => w.startsWith(q))) return 80;
|
|
3309
|
+
if (terms.some((t) => t === q)) return 70;
|
|
3310
|
+
if (terms.some((t) => t.startsWith(q))) return 60;
|
|
3311
|
+
if (title.includes(q)) return 40;
|
|
3312
|
+
if (terms.some((t) => t.includes(q))) return 30;
|
|
3313
|
+
if (description.includes(q)) return 20;
|
|
3314
|
+
return 0;
|
|
3315
|
+
}
|
|
3316
|
+
function filterAndRankItems(items, query) {
|
|
3317
|
+
const trimmed = query.trim();
|
|
3318
|
+
if (!trimmed) return items;
|
|
3319
|
+
const scored = items.map((item) => ({
|
|
3320
|
+
item,
|
|
3321
|
+
score: scoreItem(item, trimmed)
|
|
3322
|
+
})).filter(({ score }) => score > 0);
|
|
3323
|
+
scored.sort((a, b) => b.score - a.score);
|
|
3324
|
+
return scored.map(({ item }) => item);
|
|
3325
|
+
}
|
|
3326
|
+
|
|
3327
|
+
//#endregion
|
|
3328
|
+
//#region src/ui/slash-command/create-slash-command.ts
|
|
3329
|
+
function defaultFilterItems(items, query, editor) {
|
|
3330
|
+
return filterAndRankItems(isAtMaxColumnsDepth(editor) ? items.filter((item) => item.category !== "Layout" || !item.title.includes("column")) : items, query);
|
|
3331
|
+
}
|
|
3332
|
+
function createSlashCommand(options) {
|
|
3333
|
+
const items = options?.items ?? defaultSlashCommands;
|
|
3334
|
+
const filterFn = options?.filterItems ?? defaultFilterItems;
|
|
3335
|
+
return SlashCommandExtension.configure({ suggestion: {
|
|
3336
|
+
items: ({ query, editor }) => filterFn(items, query, editor),
|
|
3337
|
+
render: createRenderItems(options?.component)
|
|
3338
|
+
} });
|
|
3339
|
+
}
|
|
3340
|
+
|
|
3341
|
+
//#endregion
|
|
3342
|
+
//#region src/ui/slash-command/index.ts
|
|
3343
|
+
const SlashCommand = createSlashCommand();
|
|
2834
3344
|
|
|
2835
3345
|
//#endregion
|
|
2836
|
-
export { AlignmentAttribute, Body, Bold, BubbleMenu, BubbleMenuAlignCenter, BubbleMenuAlignLeft, BubbleMenuAlignRight, BubbleMenuBold, BubbleMenuCode, BubbleMenuDefault, BubbleMenuItalic, BubbleMenuItem, BubbleMenuItemGroup, BubbleMenuLinkSelector, BubbleMenuNodeSelector, BubbleMenuRoot, BubbleMenuSeparator, BubbleMenuStrike, BubbleMenuUnderline, BubbleMenuUppercase, Button, ButtonBubbleMenu, ButtonBubbleMenuEditLink, ButtonBubbleMenuRoot, ButtonBubbleMenuToolbar, COLUMN_PARENT_TYPES, ClassAttribute, CodeBlockPrism, ColumnsColumn, Div, EmailNode, FourColumns, ImageBubbleMenu, ImageBubbleMenuEditLink, ImageBubbleMenuRoot, ImageBubbleMenuToolbar, LinkBubbleMenu, LinkBubbleMenuEditLink, LinkBubbleMenuForm, LinkBubbleMenuOpenLink, LinkBubbleMenuRoot, LinkBubbleMenuToolbar, LinkBubbleMenuUnlink, MAX_COLUMNS_DEPTH, MaxNesting, NodeSelectorContent, NodeSelectorRoot, NodeSelectorTrigger, Placeholder, PreservedStyle, PreviewText, Section, StyleAttribute, Sup, Table, TableCell, TableHeader, TableRow, ThreeColumns, TwoColumns, Uppercase, coreExtensions, editorEventBus, getColumnsDepth, processStylesForUnlink, setTextAlignment, useButtonBubbleMenuContext, useImageBubbleMenuContext, useLinkBubbleMenuContext };
|
|
3346
|
+
export { AlignmentAttribute, BULLET_LIST, BUTTON, Body, Bold, BubbleMenu, BubbleMenuAlignCenter, BubbleMenuAlignLeft, BubbleMenuAlignRight, BubbleMenuBold, BubbleMenuCode, BubbleMenuDefault, BubbleMenuItalic, BubbleMenuItem, BubbleMenuItemGroup, BubbleMenuLinkSelector, BubbleMenuNodeSelector, BubbleMenuRoot, BubbleMenuSeparator, BubbleMenuStrike, BubbleMenuUnderline, BubbleMenuUppercase, Button, ButtonBubbleMenu, ButtonBubbleMenuDefault, ButtonBubbleMenuEditLink, ButtonBubbleMenuRoot, ButtonBubbleMenuToolbar, CODE, COLUMN_PARENT_TYPES, ClassAttribute, CodeBlockPrism, ColumnsColumn, CommandList, DIVIDER, Div, EmailNode, FOUR_COLUMNS, FourColumns, H1, H2, H3, ImageBubbleMenu, ImageBubbleMenuDefault, ImageBubbleMenuEditLink, ImageBubbleMenuRoot, ImageBubbleMenuToolbar, LinkBubbleMenu, LinkBubbleMenuDefault, LinkBubbleMenuEditLink, LinkBubbleMenuForm, LinkBubbleMenuOpenLink, LinkBubbleMenuRoot, LinkBubbleMenuToolbar, LinkBubbleMenuUnlink, MAX_COLUMNS_DEPTH, MaxNesting, NUMBERED_LIST, NodeSelectorContent, NodeSelectorRoot, NodeSelectorTrigger, Placeholder, PreservedStyle, PreviewText, QUOTE, SECTION, Section, SlashCommand, StyleAttribute, Sup, TEXT, THREE_COLUMNS, TWO_COLUMNS, Table, TableCell, TableHeader, TableRow, ThreeColumns, TwoColumns, Uppercase, coreExtensions, createSlashCommand, defaultSlashCommands, editorEventBus, filterAndRankItems, getColumnsDepth, isAtMaxColumnsDepth, isInsideNode, processStylesForUnlink, scoreItem, setTextAlignment, useButtonBubbleMenuContext, useImageBubbleMenuContext, useLinkBubbleMenuContext };
|
|
2837
3347
|
//# sourceMappingURL=index.mjs.map
|