@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.cjs
CHANGED
|
@@ -46,6 +46,10 @@ react = __toESM(react);
|
|
|
46
46
|
let _radix_ui_react_popover = require("@radix-ui/react-popover");
|
|
47
47
|
_radix_ui_react_popover = __toESM(_radix_ui_react_popover);
|
|
48
48
|
let _tiptap_react_menus = require("@tiptap/react/menus");
|
|
49
|
+
let _tiptap_suggestion = require("@tiptap/suggestion");
|
|
50
|
+
_tiptap_suggestion = __toESM(_tiptap_suggestion);
|
|
51
|
+
let tippy_js = require("tippy.js");
|
|
52
|
+
tippy_js = __toESM(tippy_js);
|
|
49
53
|
|
|
50
54
|
//#region src/core/email-node.ts
|
|
51
55
|
var EmailNode = class EmailNode extends _tiptap_core.Node {
|
|
@@ -2539,12 +2543,25 @@ function ButtonBubbleMenuToolbar({ children, ...rest }) {
|
|
|
2539
2543
|
});
|
|
2540
2544
|
}
|
|
2541
2545
|
|
|
2546
|
+
//#endregion
|
|
2547
|
+
//#region src/ui/button-bubble-menu/default.tsx
|
|
2548
|
+
function ButtonBubbleMenuDefault({ excludeItems = [], placement, offset, onHide, className }) {
|
|
2549
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ButtonBubbleMenuRoot, {
|
|
2550
|
+
placement,
|
|
2551
|
+
offset,
|
|
2552
|
+
onHide,
|
|
2553
|
+
className,
|
|
2554
|
+
children: !excludeItems.includes("edit-link") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ButtonBubbleMenuToolbar, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ButtonBubbleMenuEditLink, {}) })
|
|
2555
|
+
});
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2542
2558
|
//#endregion
|
|
2543
2559
|
//#region src/ui/button-bubble-menu/index.ts
|
|
2544
2560
|
const ButtonBubbleMenu = {
|
|
2545
2561
|
Root: ButtonBubbleMenuRoot,
|
|
2546
2562
|
Toolbar: ButtonBubbleMenuToolbar,
|
|
2547
|
-
EditLink: ButtonBubbleMenuEditLink
|
|
2563
|
+
EditLink: ButtonBubbleMenuEditLink,
|
|
2564
|
+
Default: ButtonBubbleMenuDefault
|
|
2548
2565
|
};
|
|
2549
2566
|
|
|
2550
2567
|
//#endregion
|
|
@@ -2621,12 +2638,25 @@ function ImageBubbleMenuToolbar({ children, ...rest }) {
|
|
|
2621
2638
|
});
|
|
2622
2639
|
}
|
|
2623
2640
|
|
|
2641
|
+
//#endregion
|
|
2642
|
+
//#region src/ui/image-bubble-menu/default.tsx
|
|
2643
|
+
function ImageBubbleMenuDefault({ excludeItems = [], placement, offset, onHide, className }) {
|
|
2644
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageBubbleMenuRoot, {
|
|
2645
|
+
placement,
|
|
2646
|
+
offset,
|
|
2647
|
+
onHide,
|
|
2648
|
+
className,
|
|
2649
|
+
children: !excludeItems.includes("edit-link") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageBubbleMenuToolbar, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageBubbleMenuEditLink, {}) })
|
|
2650
|
+
});
|
|
2651
|
+
}
|
|
2652
|
+
|
|
2624
2653
|
//#endregion
|
|
2625
2654
|
//#region src/ui/image-bubble-menu/index.ts
|
|
2626
2655
|
const ImageBubbleMenu = {
|
|
2627
2656
|
Root: ImageBubbleMenuRoot,
|
|
2628
2657
|
Toolbar: ImageBubbleMenuToolbar,
|
|
2629
|
-
EditLink: ImageBubbleMenuEditLink
|
|
2658
|
+
EditLink: ImageBubbleMenuEditLink,
|
|
2659
|
+
Default: ImageBubbleMenuDefault
|
|
2630
2660
|
};
|
|
2631
2661
|
|
|
2632
2662
|
//#endregion
|
|
@@ -2853,6 +2883,27 @@ function LinkBubbleMenuUnlink({ className, children, onClick, onMouseDown, ...re
|
|
|
2853
2883
|
});
|
|
2854
2884
|
}
|
|
2855
2885
|
|
|
2886
|
+
//#endregion
|
|
2887
|
+
//#region src/ui/link-bubble-menu/default.tsx
|
|
2888
|
+
function LinkBubbleMenuDefault({ excludeItems = [], placement, offset, onHide, className, validateUrl, onLinkApply, onLinkRemove }) {
|
|
2889
|
+
const has = (item) => !excludeItems.includes(item);
|
|
2890
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(LinkBubbleMenuRoot, {
|
|
2891
|
+
placement,
|
|
2892
|
+
offset,
|
|
2893
|
+
onHide,
|
|
2894
|
+
className,
|
|
2895
|
+
children: [(has("edit-link") || has("open-link") || has("unlink")) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(LinkBubbleMenuToolbar, { children: [
|
|
2896
|
+
has("edit-link") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LinkBubbleMenuEditLink, {}),
|
|
2897
|
+
has("open-link") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LinkBubbleMenuOpenLink, {}),
|
|
2898
|
+
has("unlink") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LinkBubbleMenuUnlink, {})
|
|
2899
|
+
] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LinkBubbleMenuForm, {
|
|
2900
|
+
validateUrl,
|
|
2901
|
+
onLinkApply,
|
|
2902
|
+
onLinkRemove
|
|
2903
|
+
})]
|
|
2904
|
+
});
|
|
2905
|
+
}
|
|
2906
|
+
|
|
2856
2907
|
//#endregion
|
|
2857
2908
|
//#region src/ui/link-bubble-menu/index.ts
|
|
2858
2909
|
const LinkBubbleMenu = {
|
|
@@ -2861,11 +2912,473 @@ const LinkBubbleMenu = {
|
|
|
2861
2912
|
Form: LinkBubbleMenuForm,
|
|
2862
2913
|
EditLink: LinkBubbleMenuEditLink,
|
|
2863
2914
|
Unlink: LinkBubbleMenuUnlink,
|
|
2864
|
-
OpenLink: LinkBubbleMenuOpenLink
|
|
2915
|
+
OpenLink: LinkBubbleMenuOpenLink,
|
|
2916
|
+
Default: LinkBubbleMenuDefault
|
|
2917
|
+
};
|
|
2918
|
+
|
|
2919
|
+
//#endregion
|
|
2920
|
+
//#region src/ui/slash-command/utils.ts
|
|
2921
|
+
function isInsideNode(editor, type) {
|
|
2922
|
+
const { $from } = editor.state.selection;
|
|
2923
|
+
for (let d = $from.depth; d > 0; d--) if ($from.node(d).type.name === type) return true;
|
|
2924
|
+
return false;
|
|
2925
|
+
}
|
|
2926
|
+
function isAtMaxColumnsDepth(editor) {
|
|
2927
|
+
const { from } = editor.state.selection;
|
|
2928
|
+
return getColumnsDepth(editor.state.doc, from) >= MAX_COLUMNS_DEPTH;
|
|
2929
|
+
}
|
|
2930
|
+
function updateScrollView(container, item) {
|
|
2931
|
+
const containerRect = container.getBoundingClientRect();
|
|
2932
|
+
const itemRect = item.getBoundingClientRect();
|
|
2933
|
+
if (itemRect.top < containerRect.top) container.scrollTop -= containerRect.top - itemRect.top;
|
|
2934
|
+
else if (itemRect.bottom > containerRect.bottom) container.scrollTop += itemRect.bottom - containerRect.bottom;
|
|
2935
|
+
}
|
|
2936
|
+
|
|
2937
|
+
//#endregion
|
|
2938
|
+
//#region src/ui/slash-command/command-list.tsx
|
|
2939
|
+
const CATEGORY_ORDER = [
|
|
2940
|
+
"Text",
|
|
2941
|
+
"Media",
|
|
2942
|
+
"Layout",
|
|
2943
|
+
"Utility"
|
|
2944
|
+
];
|
|
2945
|
+
function groupByCategory(items) {
|
|
2946
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2947
|
+
for (const item of items) {
|
|
2948
|
+
const existing = seen.get(item.category);
|
|
2949
|
+
if (existing) existing.push(item);
|
|
2950
|
+
else seen.set(item.category, [item]);
|
|
2951
|
+
}
|
|
2952
|
+
const ordered = [];
|
|
2953
|
+
for (const cat of CATEGORY_ORDER) {
|
|
2954
|
+
const group = seen.get(cat);
|
|
2955
|
+
if (group) {
|
|
2956
|
+
ordered.push({
|
|
2957
|
+
category: cat,
|
|
2958
|
+
items: group
|
|
2959
|
+
});
|
|
2960
|
+
seen.delete(cat);
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
for (const [category, group] of seen) ordered.push({
|
|
2964
|
+
category,
|
|
2965
|
+
items: group
|
|
2966
|
+
});
|
|
2967
|
+
return ordered;
|
|
2968
|
+
}
|
|
2969
|
+
function CommandItem({ item, selected, onSelect }) {
|
|
2970
|
+
const Icon = item.icon;
|
|
2971
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2972
|
+
"data-re-slash-command-item": "",
|
|
2973
|
+
"data-selected": selected || void 0,
|
|
2974
|
+
onClick: onSelect,
|
|
2975
|
+
type: "button",
|
|
2976
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { size: 20 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: item.title })]
|
|
2977
|
+
});
|
|
2978
|
+
}
|
|
2979
|
+
function CommandList({ items, command, query, ref }) {
|
|
2980
|
+
const [selectedIndex, setSelectedIndex] = (0, react.useState)(0);
|
|
2981
|
+
const containerRef = (0, react.useRef)(null);
|
|
2982
|
+
(0, react.useEffect)(() => {
|
|
2983
|
+
setSelectedIndex(0);
|
|
2984
|
+
}, [items]);
|
|
2985
|
+
(0, react.useLayoutEffect)(() => {
|
|
2986
|
+
const container = containerRef.current;
|
|
2987
|
+
if (!container) return;
|
|
2988
|
+
const selected = container.querySelector("[data-selected]");
|
|
2989
|
+
if (selected) updateScrollView(container, selected);
|
|
2990
|
+
}, [selectedIndex]);
|
|
2991
|
+
const selectItem = (0, react.useCallback)((index) => {
|
|
2992
|
+
const item = items[index];
|
|
2993
|
+
if (item) command(item);
|
|
2994
|
+
}, [items, command]);
|
|
2995
|
+
(0, react.useImperativeHandle)(ref, () => ({ onKeyDown: ({ event }) => {
|
|
2996
|
+
if (items.length === 0) return false;
|
|
2997
|
+
if (event.key === "ArrowUp") {
|
|
2998
|
+
setSelectedIndex((i) => (i + items.length - 1) % items.length);
|
|
2999
|
+
return true;
|
|
3000
|
+
}
|
|
3001
|
+
if (event.key === "ArrowDown") {
|
|
3002
|
+
setSelectedIndex((i) => (i + 1) % items.length);
|
|
3003
|
+
return true;
|
|
3004
|
+
}
|
|
3005
|
+
if (event.key === "Enter") {
|
|
3006
|
+
selectItem(selectedIndex);
|
|
3007
|
+
return true;
|
|
3008
|
+
}
|
|
3009
|
+
return false;
|
|
3010
|
+
} }), [
|
|
3011
|
+
items.length,
|
|
3012
|
+
selectItem,
|
|
3013
|
+
selectedIndex
|
|
3014
|
+
]);
|
|
3015
|
+
if (items.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3016
|
+
"data-re-slash-command": "",
|
|
3017
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3018
|
+
"data-re-slash-command-empty": "",
|
|
3019
|
+
children: "No results"
|
|
3020
|
+
})
|
|
3021
|
+
});
|
|
3022
|
+
if (query.trim().length > 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3023
|
+
"data-re-slash-command": "",
|
|
3024
|
+
ref: containerRef,
|
|
3025
|
+
children: items.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommandItem, {
|
|
3026
|
+
item,
|
|
3027
|
+
onSelect: () => selectItem(index),
|
|
3028
|
+
selected: index === selectedIndex
|
|
3029
|
+
}, item.title))
|
|
3030
|
+
});
|
|
3031
|
+
const groups = groupByCategory(items);
|
|
3032
|
+
let flatIndex = 0;
|
|
3033
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3034
|
+
"data-re-slash-command": "",
|
|
3035
|
+
ref: containerRef,
|
|
3036
|
+
children: groups.map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3037
|
+
"data-re-slash-command-category": "",
|
|
3038
|
+
children: group.category
|
|
3039
|
+
}), group.items.map((item) => {
|
|
3040
|
+
const currentIndex = flatIndex++;
|
|
3041
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommandItem, {
|
|
3042
|
+
item,
|
|
3043
|
+
onSelect: () => selectItem(currentIndex),
|
|
3044
|
+
selected: currentIndex === selectedIndex
|
|
3045
|
+
}, item.title);
|
|
3046
|
+
})] }, group.category))
|
|
3047
|
+
});
|
|
3048
|
+
}
|
|
3049
|
+
|
|
3050
|
+
//#endregion
|
|
3051
|
+
//#region src/ui/slash-command/commands.ts
|
|
3052
|
+
const TEXT = {
|
|
3053
|
+
title: "Text",
|
|
3054
|
+
description: "Plain text block",
|
|
3055
|
+
icon: lucide_react.Text,
|
|
3056
|
+
category: "Text",
|
|
3057
|
+
searchTerms: ["p", "paragraph"],
|
|
3058
|
+
command: ({ editor, range }) => {
|
|
3059
|
+
editor.chain().focus().deleteRange(range).toggleNode("paragraph", "paragraph").run();
|
|
3060
|
+
}
|
|
3061
|
+
};
|
|
3062
|
+
const H1 = {
|
|
3063
|
+
title: "Title",
|
|
3064
|
+
description: "Large heading",
|
|
3065
|
+
icon: lucide_react.Heading1,
|
|
3066
|
+
category: "Text",
|
|
3067
|
+
searchTerms: [
|
|
3068
|
+
"title",
|
|
3069
|
+
"big",
|
|
3070
|
+
"large",
|
|
3071
|
+
"h1"
|
|
3072
|
+
],
|
|
3073
|
+
command: ({ editor, range }) => {
|
|
3074
|
+
editor.chain().focus().deleteRange(range).setNode("heading", { level: 1 }).run();
|
|
3075
|
+
}
|
|
3076
|
+
};
|
|
3077
|
+
const H2 = {
|
|
3078
|
+
title: "Subtitle",
|
|
3079
|
+
description: "Medium heading",
|
|
3080
|
+
icon: lucide_react.Heading2,
|
|
3081
|
+
category: "Text",
|
|
3082
|
+
searchTerms: [
|
|
3083
|
+
"subtitle",
|
|
3084
|
+
"medium",
|
|
3085
|
+
"h2"
|
|
3086
|
+
],
|
|
3087
|
+
command: ({ editor, range }) => {
|
|
3088
|
+
editor.chain().focus().deleteRange(range).setNode("heading", { level: 2 }).run();
|
|
3089
|
+
}
|
|
3090
|
+
};
|
|
3091
|
+
const H3 = {
|
|
3092
|
+
title: "Heading",
|
|
3093
|
+
description: "Small heading",
|
|
3094
|
+
icon: lucide_react.Heading3,
|
|
3095
|
+
category: "Text",
|
|
3096
|
+
searchTerms: [
|
|
3097
|
+
"subtitle",
|
|
3098
|
+
"small",
|
|
3099
|
+
"h3"
|
|
3100
|
+
],
|
|
3101
|
+
command: ({ editor, range }) => {
|
|
3102
|
+
editor.chain().focus().deleteRange(range).setNode("heading", { level: 3 }).run();
|
|
3103
|
+
}
|
|
3104
|
+
};
|
|
3105
|
+
const BULLET_LIST = {
|
|
3106
|
+
title: "Bullet list",
|
|
3107
|
+
description: "Unordered list",
|
|
3108
|
+
icon: lucide_react.List,
|
|
3109
|
+
category: "Text",
|
|
3110
|
+
searchTerms: ["unordered", "point"],
|
|
3111
|
+
command: ({ editor, range }) => {
|
|
3112
|
+
editor.chain().focus().deleteRange(range).toggleBulletList().run();
|
|
3113
|
+
}
|
|
3114
|
+
};
|
|
3115
|
+
const NUMBERED_LIST = {
|
|
3116
|
+
title: "Numbered list",
|
|
3117
|
+
description: "Ordered list",
|
|
3118
|
+
icon: lucide_react.ListOrdered,
|
|
3119
|
+
category: "Text",
|
|
3120
|
+
searchTerms: ["ordered"],
|
|
3121
|
+
command: ({ editor, range }) => {
|
|
3122
|
+
editor.chain().focus().deleteRange(range).toggleOrderedList().run();
|
|
3123
|
+
}
|
|
3124
|
+
};
|
|
3125
|
+
const QUOTE = {
|
|
3126
|
+
title: "Quote",
|
|
3127
|
+
description: "Block quote",
|
|
3128
|
+
icon: lucide_react.TextQuote,
|
|
3129
|
+
category: "Text",
|
|
3130
|
+
searchTerms: ["blockquote"],
|
|
3131
|
+
command: ({ editor, range }) => {
|
|
3132
|
+
editor.chain().focus().deleteRange(range).toggleNode("paragraph", "paragraph").toggleBlockquote().run();
|
|
3133
|
+
}
|
|
3134
|
+
};
|
|
3135
|
+
const CODE = {
|
|
3136
|
+
title: "Code block",
|
|
3137
|
+
description: "Code snippet",
|
|
3138
|
+
icon: lucide_react.SquareCode,
|
|
3139
|
+
category: "Text",
|
|
3140
|
+
searchTerms: ["codeblock"],
|
|
3141
|
+
command: ({ editor, range }) => {
|
|
3142
|
+
editor.chain().focus().deleteRange(range).toggleCodeBlock().run();
|
|
3143
|
+
}
|
|
3144
|
+
};
|
|
3145
|
+
const BUTTON = {
|
|
3146
|
+
title: "Button",
|
|
3147
|
+
description: "Clickable button",
|
|
3148
|
+
icon: lucide_react.MousePointer,
|
|
3149
|
+
category: "Layout",
|
|
3150
|
+
searchTerms: ["button"],
|
|
3151
|
+
command: ({ editor, range }) => {
|
|
3152
|
+
editor.chain().focus().deleteRange(range).setButton().run();
|
|
3153
|
+
}
|
|
3154
|
+
};
|
|
3155
|
+
const DIVIDER = {
|
|
3156
|
+
title: "Divider",
|
|
3157
|
+
description: "Horizontal separator",
|
|
3158
|
+
icon: lucide_react.SplitSquareVertical,
|
|
3159
|
+
category: "Layout",
|
|
3160
|
+
searchTerms: [
|
|
3161
|
+
"hr",
|
|
3162
|
+
"divider",
|
|
3163
|
+
"separator"
|
|
3164
|
+
],
|
|
3165
|
+
command: ({ editor, range }) => {
|
|
3166
|
+
editor.chain().focus().deleteRange(range).setHorizontalRule().run();
|
|
3167
|
+
}
|
|
2865
3168
|
};
|
|
3169
|
+
const SECTION = {
|
|
3170
|
+
title: "Section",
|
|
3171
|
+
description: "Content section",
|
|
3172
|
+
icon: lucide_react.Rows2,
|
|
3173
|
+
category: "Layout",
|
|
3174
|
+
searchTerms: [
|
|
3175
|
+
"section",
|
|
3176
|
+
"row",
|
|
3177
|
+
"container"
|
|
3178
|
+
],
|
|
3179
|
+
command: ({ editor, range }) => {
|
|
3180
|
+
editor.chain().focus().deleteRange(range).insertSection().run();
|
|
3181
|
+
}
|
|
3182
|
+
};
|
|
3183
|
+
const TWO_COLUMNS = {
|
|
3184
|
+
title: "2 columns",
|
|
3185
|
+
description: "Two column layout",
|
|
3186
|
+
icon: lucide_react.Columns2,
|
|
3187
|
+
category: "Layout",
|
|
3188
|
+
searchTerms: [
|
|
3189
|
+
"columns",
|
|
3190
|
+
"column",
|
|
3191
|
+
"layout",
|
|
3192
|
+
"grid",
|
|
3193
|
+
"split",
|
|
3194
|
+
"side-by-side",
|
|
3195
|
+
"multi-column",
|
|
3196
|
+
"row",
|
|
3197
|
+
"two",
|
|
3198
|
+
"2"
|
|
3199
|
+
],
|
|
3200
|
+
command: ({ editor, range }) => {
|
|
3201
|
+
editor.chain().focus().deleteRange(range).insertColumns(2).run();
|
|
3202
|
+
}
|
|
3203
|
+
};
|
|
3204
|
+
const THREE_COLUMNS = {
|
|
3205
|
+
title: "3 columns",
|
|
3206
|
+
description: "Three column layout",
|
|
3207
|
+
icon: lucide_react.Columns3,
|
|
3208
|
+
category: "Layout",
|
|
3209
|
+
searchTerms: [
|
|
3210
|
+
"columns",
|
|
3211
|
+
"column",
|
|
3212
|
+
"layout",
|
|
3213
|
+
"grid",
|
|
3214
|
+
"split",
|
|
3215
|
+
"multi-column",
|
|
3216
|
+
"row",
|
|
3217
|
+
"three",
|
|
3218
|
+
"3"
|
|
3219
|
+
],
|
|
3220
|
+
command: ({ editor, range }) => {
|
|
3221
|
+
editor.chain().focus().deleteRange(range).insertColumns(3).run();
|
|
3222
|
+
}
|
|
3223
|
+
};
|
|
3224
|
+
const FOUR_COLUMNS = {
|
|
3225
|
+
title: "4 columns",
|
|
3226
|
+
description: "Four column layout",
|
|
3227
|
+
icon: lucide_react.Columns4,
|
|
3228
|
+
category: "Layout",
|
|
3229
|
+
searchTerms: [
|
|
3230
|
+
"columns",
|
|
3231
|
+
"column",
|
|
3232
|
+
"layout",
|
|
3233
|
+
"grid",
|
|
3234
|
+
"split",
|
|
3235
|
+
"multi-column",
|
|
3236
|
+
"row",
|
|
3237
|
+
"four",
|
|
3238
|
+
"4"
|
|
3239
|
+
],
|
|
3240
|
+
command: ({ editor, range }) => {
|
|
3241
|
+
editor.chain().focus().deleteRange(range).insertColumns(4).run();
|
|
3242
|
+
}
|
|
3243
|
+
};
|
|
3244
|
+
const defaultSlashCommands = [
|
|
3245
|
+
TEXT,
|
|
3246
|
+
H1,
|
|
3247
|
+
H2,
|
|
3248
|
+
H3,
|
|
3249
|
+
BULLET_LIST,
|
|
3250
|
+
NUMBERED_LIST,
|
|
3251
|
+
QUOTE,
|
|
3252
|
+
CODE,
|
|
3253
|
+
BUTTON,
|
|
3254
|
+
DIVIDER,
|
|
3255
|
+
SECTION,
|
|
3256
|
+
TWO_COLUMNS,
|
|
3257
|
+
THREE_COLUMNS,
|
|
3258
|
+
FOUR_COLUMNS
|
|
3259
|
+
];
|
|
3260
|
+
|
|
3261
|
+
//#endregion
|
|
3262
|
+
//#region src/ui/slash-command/extension.ts
|
|
3263
|
+
const SlashCommandExtension = _tiptap_core.Extension.create({
|
|
3264
|
+
name: "slash-command",
|
|
3265
|
+
addOptions() {
|
|
3266
|
+
return { suggestion: {
|
|
3267
|
+
char: "/",
|
|
3268
|
+
allow: ({ editor }) => !editor.isActive("codeBlock"),
|
|
3269
|
+
command: ({ editor, range, props }) => {
|
|
3270
|
+
props.command({
|
|
3271
|
+
editor,
|
|
3272
|
+
range
|
|
3273
|
+
});
|
|
3274
|
+
}
|
|
3275
|
+
} };
|
|
3276
|
+
},
|
|
3277
|
+
addProseMirrorPlugins() {
|
|
3278
|
+
return [(0, _tiptap_suggestion.default)({
|
|
3279
|
+
pluginKey: new _tiptap_pm_state.PluginKey("slash-command"),
|
|
3280
|
+
editor: this.editor,
|
|
3281
|
+
...this.options.suggestion
|
|
3282
|
+
})];
|
|
3283
|
+
}
|
|
3284
|
+
});
|
|
3285
|
+
|
|
3286
|
+
//#endregion
|
|
3287
|
+
//#region src/ui/slash-command/render.tsx
|
|
3288
|
+
function createRenderItems(component = CommandList) {
|
|
3289
|
+
return () => {
|
|
3290
|
+
let renderer = null;
|
|
3291
|
+
let popup = null;
|
|
3292
|
+
return {
|
|
3293
|
+
onStart: (props) => {
|
|
3294
|
+
renderer = new _tiptap_react.ReactRenderer(component, {
|
|
3295
|
+
props,
|
|
3296
|
+
editor: props.editor
|
|
3297
|
+
});
|
|
3298
|
+
if (!props.clientRect) return;
|
|
3299
|
+
popup = (0, tippy_js.default)("body", {
|
|
3300
|
+
getReferenceClientRect: props.clientRect,
|
|
3301
|
+
appendTo: () => document.body,
|
|
3302
|
+
content: renderer.element,
|
|
3303
|
+
showOnCreate: true,
|
|
3304
|
+
interactive: true,
|
|
3305
|
+
trigger: "manual",
|
|
3306
|
+
placement: "bottom-start"
|
|
3307
|
+
});
|
|
3308
|
+
},
|
|
3309
|
+
onUpdate: (props) => {
|
|
3310
|
+
if (!renderer) return;
|
|
3311
|
+
renderer.updateProps(props);
|
|
3312
|
+
if (popup?.[0] && props.clientRect) popup[0].setProps({ getReferenceClientRect: props.clientRect });
|
|
3313
|
+
},
|
|
3314
|
+
onKeyDown: (props) => {
|
|
3315
|
+
if (props.event.key === "Escape") {
|
|
3316
|
+
popup?.[0]?.hide();
|
|
3317
|
+
return true;
|
|
3318
|
+
}
|
|
3319
|
+
return renderer?.ref?.onKeyDown(props) ?? false;
|
|
3320
|
+
},
|
|
3321
|
+
onExit: () => {
|
|
3322
|
+
popup?.[0]?.destroy();
|
|
3323
|
+
renderer?.destroy();
|
|
3324
|
+
popup = null;
|
|
3325
|
+
renderer = null;
|
|
3326
|
+
}
|
|
3327
|
+
};
|
|
3328
|
+
};
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
//#endregion
|
|
3332
|
+
//#region src/ui/slash-command/search.ts
|
|
3333
|
+
function scoreItem(item, query) {
|
|
3334
|
+
if (!query) return 100;
|
|
3335
|
+
const q = query.toLowerCase();
|
|
3336
|
+
const title = item.title.toLowerCase();
|
|
3337
|
+
const description = item.description.toLowerCase();
|
|
3338
|
+
const terms = item.searchTerms?.map((t) => t.toLowerCase()) ?? [];
|
|
3339
|
+
if (title === q) return 100;
|
|
3340
|
+
if (title.startsWith(q)) return 90;
|
|
3341
|
+
if (title.split(/\s+/).some((w) => w.startsWith(q))) return 80;
|
|
3342
|
+
if (terms.some((t) => t === q)) return 70;
|
|
3343
|
+
if (terms.some((t) => t.startsWith(q))) return 60;
|
|
3344
|
+
if (title.includes(q)) return 40;
|
|
3345
|
+
if (terms.some((t) => t.includes(q))) return 30;
|
|
3346
|
+
if (description.includes(q)) return 20;
|
|
3347
|
+
return 0;
|
|
3348
|
+
}
|
|
3349
|
+
function filterAndRankItems(items, query) {
|
|
3350
|
+
const trimmed = query.trim();
|
|
3351
|
+
if (!trimmed) return items;
|
|
3352
|
+
const scored = items.map((item) => ({
|
|
3353
|
+
item,
|
|
3354
|
+
score: scoreItem(item, trimmed)
|
|
3355
|
+
})).filter(({ score }) => score > 0);
|
|
3356
|
+
scored.sort((a, b) => b.score - a.score);
|
|
3357
|
+
return scored.map(({ item }) => item);
|
|
3358
|
+
}
|
|
3359
|
+
|
|
3360
|
+
//#endregion
|
|
3361
|
+
//#region src/ui/slash-command/create-slash-command.ts
|
|
3362
|
+
function defaultFilterItems(items, query, editor) {
|
|
3363
|
+
return filterAndRankItems(isAtMaxColumnsDepth(editor) ? items.filter((item) => item.category !== "Layout" || !item.title.includes("column")) : items, query);
|
|
3364
|
+
}
|
|
3365
|
+
function createSlashCommand(options) {
|
|
3366
|
+
const items = options?.items ?? defaultSlashCommands;
|
|
3367
|
+
const filterFn = options?.filterItems ?? defaultFilterItems;
|
|
3368
|
+
return SlashCommandExtension.configure({ suggestion: {
|
|
3369
|
+
items: ({ query, editor }) => filterFn(items, query, editor),
|
|
3370
|
+
render: createRenderItems(options?.component)
|
|
3371
|
+
} });
|
|
3372
|
+
}
|
|
3373
|
+
|
|
3374
|
+
//#endregion
|
|
3375
|
+
//#region src/ui/slash-command/index.ts
|
|
3376
|
+
const SlashCommand = createSlashCommand();
|
|
2866
3377
|
|
|
2867
3378
|
//#endregion
|
|
2868
3379
|
exports.AlignmentAttribute = AlignmentAttribute;
|
|
3380
|
+
exports.BULLET_LIST = BULLET_LIST;
|
|
3381
|
+
exports.BUTTON = BUTTON;
|
|
2869
3382
|
exports.Body = Body;
|
|
2870
3383
|
exports.Bold = Bold;
|
|
2871
3384
|
exports.BubbleMenu = BubbleMenu;
|
|
@@ -2887,21 +3400,31 @@ exports.BubbleMenuUnderline = BubbleMenuUnderline;
|
|
|
2887
3400
|
exports.BubbleMenuUppercase = BubbleMenuUppercase;
|
|
2888
3401
|
exports.Button = Button;
|
|
2889
3402
|
exports.ButtonBubbleMenu = ButtonBubbleMenu;
|
|
3403
|
+
exports.ButtonBubbleMenuDefault = ButtonBubbleMenuDefault;
|
|
2890
3404
|
exports.ButtonBubbleMenuEditLink = ButtonBubbleMenuEditLink;
|
|
2891
3405
|
exports.ButtonBubbleMenuRoot = ButtonBubbleMenuRoot;
|
|
2892
3406
|
exports.ButtonBubbleMenuToolbar = ButtonBubbleMenuToolbar;
|
|
3407
|
+
exports.CODE = CODE;
|
|
2893
3408
|
exports.COLUMN_PARENT_TYPES = COLUMN_PARENT_TYPES;
|
|
2894
3409
|
exports.ClassAttribute = ClassAttribute;
|
|
2895
3410
|
exports.CodeBlockPrism = CodeBlockPrism;
|
|
2896
3411
|
exports.ColumnsColumn = ColumnsColumn;
|
|
3412
|
+
exports.CommandList = CommandList;
|
|
3413
|
+
exports.DIVIDER = DIVIDER;
|
|
2897
3414
|
exports.Div = Div;
|
|
2898
3415
|
exports.EmailNode = EmailNode;
|
|
3416
|
+
exports.FOUR_COLUMNS = FOUR_COLUMNS;
|
|
2899
3417
|
exports.FourColumns = FourColumns;
|
|
3418
|
+
exports.H1 = H1;
|
|
3419
|
+
exports.H2 = H2;
|
|
3420
|
+
exports.H3 = H3;
|
|
2900
3421
|
exports.ImageBubbleMenu = ImageBubbleMenu;
|
|
3422
|
+
exports.ImageBubbleMenuDefault = ImageBubbleMenuDefault;
|
|
2901
3423
|
exports.ImageBubbleMenuEditLink = ImageBubbleMenuEditLink;
|
|
2902
3424
|
exports.ImageBubbleMenuRoot = ImageBubbleMenuRoot;
|
|
2903
3425
|
exports.ImageBubbleMenuToolbar = ImageBubbleMenuToolbar;
|
|
2904
3426
|
exports.LinkBubbleMenu = LinkBubbleMenu;
|
|
3427
|
+
exports.LinkBubbleMenuDefault = LinkBubbleMenuDefault;
|
|
2905
3428
|
exports.LinkBubbleMenuEditLink = LinkBubbleMenuEditLink;
|
|
2906
3429
|
exports.LinkBubbleMenuForm = LinkBubbleMenuForm;
|
|
2907
3430
|
exports.LinkBubbleMenuOpenLink = LinkBubbleMenuOpenLink;
|
|
@@ -2910,15 +3433,22 @@ exports.LinkBubbleMenuToolbar = LinkBubbleMenuToolbar;
|
|
|
2910
3433
|
exports.LinkBubbleMenuUnlink = LinkBubbleMenuUnlink;
|
|
2911
3434
|
exports.MAX_COLUMNS_DEPTH = MAX_COLUMNS_DEPTH;
|
|
2912
3435
|
exports.MaxNesting = MaxNesting;
|
|
3436
|
+
exports.NUMBERED_LIST = NUMBERED_LIST;
|
|
2913
3437
|
exports.NodeSelectorContent = NodeSelectorContent;
|
|
2914
3438
|
exports.NodeSelectorRoot = NodeSelectorRoot;
|
|
2915
3439
|
exports.NodeSelectorTrigger = NodeSelectorTrigger;
|
|
2916
3440
|
exports.Placeholder = Placeholder;
|
|
2917
3441
|
exports.PreservedStyle = PreservedStyle;
|
|
2918
3442
|
exports.PreviewText = PreviewText;
|
|
3443
|
+
exports.QUOTE = QUOTE;
|
|
3444
|
+
exports.SECTION = SECTION;
|
|
2919
3445
|
exports.Section = Section;
|
|
3446
|
+
exports.SlashCommand = SlashCommand;
|
|
2920
3447
|
exports.StyleAttribute = StyleAttribute;
|
|
2921
3448
|
exports.Sup = Sup;
|
|
3449
|
+
exports.TEXT = TEXT;
|
|
3450
|
+
exports.THREE_COLUMNS = THREE_COLUMNS;
|
|
3451
|
+
exports.TWO_COLUMNS = TWO_COLUMNS;
|
|
2922
3452
|
exports.Table = Table;
|
|
2923
3453
|
exports.TableCell = TableCell;
|
|
2924
3454
|
exports.TableHeader = TableHeader;
|
|
@@ -2927,9 +3457,15 @@ exports.ThreeColumns = ThreeColumns;
|
|
|
2927
3457
|
exports.TwoColumns = TwoColumns;
|
|
2928
3458
|
exports.Uppercase = Uppercase;
|
|
2929
3459
|
exports.coreExtensions = coreExtensions;
|
|
3460
|
+
exports.createSlashCommand = createSlashCommand;
|
|
3461
|
+
exports.defaultSlashCommands = defaultSlashCommands;
|
|
2930
3462
|
exports.editorEventBus = editorEventBus;
|
|
3463
|
+
exports.filterAndRankItems = filterAndRankItems;
|
|
2931
3464
|
exports.getColumnsDepth = getColumnsDepth;
|
|
3465
|
+
exports.isAtMaxColumnsDepth = isAtMaxColumnsDepth;
|
|
3466
|
+
exports.isInsideNode = isInsideNode;
|
|
2932
3467
|
exports.processStylesForUnlink = processStylesForUnlink;
|
|
3468
|
+
exports.scoreItem = scoreItem;
|
|
2933
3469
|
exports.setTextAlignment = setTextAlignment;
|
|
2934
3470
|
exports.useButtonBubbleMenuContext = useButtonBubbleMenuContext;
|
|
2935
3471
|
exports.useImageBubbleMenuContext = useImageBubbleMenuContext;
|