@react-email/editor 0.0.0-experimental.6 → 0.0.0-experimental.8
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.d.mts +426 -93
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +427 -94
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1036 -188
- package/dist/index.mjs +1008 -189
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -3
package/dist/index.js
CHANGED
|
@@ -23,6 +23,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
//#endregion
|
|
24
24
|
let __tiptap_core = require("@tiptap/core");
|
|
25
25
|
__tiptap_core = __toESM(__tiptap_core);
|
|
26
|
+
let __tiptap_starter_kit = require("@tiptap/starter-kit");
|
|
27
|
+
__tiptap_starter_kit = __toESM(__tiptap_starter_kit);
|
|
26
28
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
27
29
|
react_jsx_runtime = __toESM(react_jsx_runtime);
|
|
28
30
|
let __react_email_components = require("@react-email/components");
|
|
@@ -39,6 +41,16 @@ let prismjs = require("prismjs");
|
|
|
39
41
|
prismjs = __toESM(prismjs);
|
|
40
42
|
let __tiptap_extension_placeholder = require("@tiptap/extension-placeholder");
|
|
41
43
|
__tiptap_extension_placeholder = __toESM(__tiptap_extension_placeholder);
|
|
44
|
+
let __tiptap_react = require("@tiptap/react");
|
|
45
|
+
__tiptap_react = __toESM(__tiptap_react);
|
|
46
|
+
let lucide_react = require("lucide-react");
|
|
47
|
+
lucide_react = __toESM(lucide_react);
|
|
48
|
+
let react = require("react");
|
|
49
|
+
react = __toESM(react);
|
|
50
|
+
let __radix_ui_react_popover = require("@radix-ui/react-popover");
|
|
51
|
+
__radix_ui_react_popover = __toESM(__radix_ui_react_popover);
|
|
52
|
+
let __tiptap_react_menus = require("@tiptap/react/menus");
|
|
53
|
+
__tiptap_react_menus = __toESM(__tiptap_react_menus);
|
|
42
54
|
|
|
43
55
|
//#region src/core/email-node.ts
|
|
44
56
|
var EmailNode = class EmailNode extends __tiptap_core.Node {
|
|
@@ -70,6 +82,48 @@ var EmailNode = class EmailNode extends __tiptap_core.Node {
|
|
|
70
82
|
}
|
|
71
83
|
};
|
|
72
84
|
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/core/event-bus.ts
|
|
87
|
+
const EVENT_PREFIX = "@react-email/editor:";
|
|
88
|
+
var EditorEventBus = class {
|
|
89
|
+
prefixEventName(eventName) {
|
|
90
|
+
return `${EVENT_PREFIX}${String(eventName)}`;
|
|
91
|
+
}
|
|
92
|
+
dispatch(eventName, payload, options) {
|
|
93
|
+
const target = options?.target ?? window;
|
|
94
|
+
const prefixedEventName = this.prefixEventName(eventName);
|
|
95
|
+
const event = new CustomEvent(prefixedEventName, {
|
|
96
|
+
detail: payload,
|
|
97
|
+
bubbles: false,
|
|
98
|
+
cancelable: false
|
|
99
|
+
});
|
|
100
|
+
target.dispatchEvent(event);
|
|
101
|
+
}
|
|
102
|
+
on(eventName, handler, options) {
|
|
103
|
+
const target = options?.target ?? window;
|
|
104
|
+
const prefixedEventName = this.prefixEventName(eventName);
|
|
105
|
+
const abortController = new AbortController();
|
|
106
|
+
const wrappedHandler = (event) => {
|
|
107
|
+
const customEvent = event;
|
|
108
|
+
const result = handler(customEvent.detail);
|
|
109
|
+
if (result instanceof Promise) result.catch((error) => {
|
|
110
|
+
console.error(`Error in async event handler for ${prefixedEventName}:`, {
|
|
111
|
+
event: customEvent.detail,
|
|
112
|
+
error
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
target.addEventListener(prefixedEventName, wrappedHandler, {
|
|
117
|
+
...options,
|
|
118
|
+
signal: abortController.signal
|
|
119
|
+
});
|
|
120
|
+
return { unsubscribe: () => {
|
|
121
|
+
abortController.abort();
|
|
122
|
+
} };
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const editorEventBus = new EditorEventBus();
|
|
126
|
+
|
|
73
127
|
//#endregion
|
|
74
128
|
//#region src/extensions/alignment-attribute.tsx
|
|
75
129
|
const AlignmentAttribute = __tiptap_core.Extension.create({
|
|
@@ -404,12 +458,12 @@ const Body = EmailNode.create({
|
|
|
404
458
|
0
|
|
405
459
|
];
|
|
406
460
|
},
|
|
407
|
-
renderToReactEmail({ children, node,
|
|
461
|
+
renderToReactEmail({ children, node, style }) {
|
|
408
462
|
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
409
463
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
410
464
|
className: node.attrs?.class || void 0,
|
|
411
465
|
style: {
|
|
412
|
-
...
|
|
466
|
+
...style,
|
|
413
467
|
...inlineStyles
|
|
414
468
|
},
|
|
415
469
|
children
|
|
@@ -565,7 +619,7 @@ const Button = EmailNode.create({
|
|
|
565
619
|
}
|
|
566
620
|
};
|
|
567
621
|
},
|
|
568
|
-
renderToReactEmail({ children, node,
|
|
622
|
+
renderToReactEmail({ children, node, style }) {
|
|
569
623
|
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
570
624
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Row, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Column, {
|
|
571
625
|
align: node.attrs?.align || node.attrs?.alignment,
|
|
@@ -573,8 +627,7 @@ const Button = EmailNode.create({
|
|
|
573
627
|
className: node.attrs?.class || void 0,
|
|
574
628
|
href: node.attrs?.href,
|
|
575
629
|
style: {
|
|
576
|
-
...
|
|
577
|
-
...styles.button,
|
|
630
|
+
...style,
|
|
578
631
|
...inlineStyles
|
|
579
632
|
},
|
|
580
633
|
children
|
|
@@ -803,6 +856,25 @@ const CodeBlockPrism = EmailNode.from(__tiptap_extension_code_block.default.exte
|
|
|
803
856
|
]
|
|
804
857
|
];
|
|
805
858
|
},
|
|
859
|
+
addKeyboardShortcuts() {
|
|
860
|
+
return {
|
|
861
|
+
...this.parent?.(),
|
|
862
|
+
"Mod-a": ({ editor }) => {
|
|
863
|
+
const { state } = editor;
|
|
864
|
+
const { selection } = state;
|
|
865
|
+
const { $from } = selection;
|
|
866
|
+
for (let depth = $from.depth; depth >= 1; depth--) if ($from.node(depth).type.name === this.name) {
|
|
867
|
+
const blockStart = $from.start(depth);
|
|
868
|
+
const blockEnd = $from.end(depth);
|
|
869
|
+
if (selection.from === blockStart && selection.to === blockEnd) return false;
|
|
870
|
+
const tr = state.tr.setSelection(__tiptap_pm_state.TextSelection.create(state.doc, blockStart, blockEnd));
|
|
871
|
+
editor.view.dispatch(tr);
|
|
872
|
+
return true;
|
|
873
|
+
}
|
|
874
|
+
return false;
|
|
875
|
+
}
|
|
876
|
+
};
|
|
877
|
+
},
|
|
806
878
|
addProseMirrorPlugins() {
|
|
807
879
|
return [...this.parent?.() || [], PrismPlugin({
|
|
808
880
|
name: this.name,
|
|
@@ -810,7 +882,7 @@ const CodeBlockPrism = EmailNode.from(__tiptap_extension_code_block.default.exte
|
|
|
810
882
|
defaultTheme: this.options.defaultTheme
|
|
811
883
|
})];
|
|
812
884
|
}
|
|
813
|
-
}), ({ node,
|
|
885
|
+
}), ({ node, style }) => {
|
|
814
886
|
const language = node.attrs?.language ? `${node.attrs.language}` : "javascript";
|
|
815
887
|
const userTheme = __react_email_components[node.attrs?.theme];
|
|
816
888
|
const theme = userTheme ? {
|
|
@@ -834,179 +906,9 @@ const CodeBlockPrism = EmailNode.from(__tiptap_extension_code_block.default.exte
|
|
|
834
906
|
theme,
|
|
835
907
|
style: {
|
|
836
908
|
width: "auto",
|
|
837
|
-
...
|
|
838
|
-
}
|
|
839
|
-
});
|
|
840
|
-
});
|
|
841
|
-
|
|
842
|
-
//#endregion
|
|
843
|
-
//#region src/extensions/columns.tsx
|
|
844
|
-
const COLUMN_PARENT_TYPES = [
|
|
845
|
-
"twoColumns",
|
|
846
|
-
"threeColumns",
|
|
847
|
-
"fourColumns"
|
|
848
|
-
];
|
|
849
|
-
const COLUMN_PARENT_SET = new Set(COLUMN_PARENT_TYPES);
|
|
850
|
-
const MAX_COLUMNS_DEPTH = 3;
|
|
851
|
-
function getColumnsDepth(doc, from) {
|
|
852
|
-
const $from = doc.resolve(from);
|
|
853
|
-
let depth = 0;
|
|
854
|
-
for (let d = $from.depth; d > 0; d--) if (COLUMN_PARENT_SET.has($from.node(d).type.name)) depth++;
|
|
855
|
-
return depth;
|
|
856
|
-
}
|
|
857
|
-
const VARIANTS = [
|
|
858
|
-
{
|
|
859
|
-
name: "twoColumns",
|
|
860
|
-
columnCount: 2,
|
|
861
|
-
content: "columnsColumn columnsColumn",
|
|
862
|
-
dataType: "two-columns"
|
|
863
|
-
},
|
|
864
|
-
{
|
|
865
|
-
name: "threeColumns",
|
|
866
|
-
columnCount: 3,
|
|
867
|
-
content: "columnsColumn columnsColumn columnsColumn",
|
|
868
|
-
dataType: "three-columns"
|
|
869
|
-
},
|
|
870
|
-
{
|
|
871
|
-
name: "fourColumns",
|
|
872
|
-
columnCount: 4,
|
|
873
|
-
content: "columnsColumn{4}",
|
|
874
|
-
dataType: "four-columns"
|
|
875
|
-
}
|
|
876
|
-
];
|
|
877
|
-
const NODE_TYPE_MAP = {
|
|
878
|
-
2: "twoColumns",
|
|
879
|
-
3: "threeColumns",
|
|
880
|
-
4: "fourColumns"
|
|
881
|
-
};
|
|
882
|
-
function createColumnsNode(config, includeCommands) {
|
|
883
|
-
return EmailNode.create({
|
|
884
|
-
name: config.name,
|
|
885
|
-
group: "block",
|
|
886
|
-
content: config.content,
|
|
887
|
-
isolating: true,
|
|
888
|
-
defining: true,
|
|
889
|
-
addAttributes() {
|
|
890
|
-
return createStandardAttributes([...LAYOUT_ATTRIBUTES, ...COMMON_HTML_ATTRIBUTES]);
|
|
891
|
-
},
|
|
892
|
-
parseHTML() {
|
|
893
|
-
return [{ tag: `div[data-type="${config.dataType}"]` }];
|
|
894
|
-
},
|
|
895
|
-
renderHTML({ HTMLAttributes }) {
|
|
896
|
-
return [
|
|
897
|
-
"div",
|
|
898
|
-
(0, __tiptap_core.mergeAttributes)({
|
|
899
|
-
"data-type": config.dataType,
|
|
900
|
-
class: "node-columns"
|
|
901
|
-
}, HTMLAttributes),
|
|
902
|
-
0
|
|
903
|
-
];
|
|
904
|
-
},
|
|
905
|
-
...includeCommands && { addCommands() {
|
|
906
|
-
return { insertColumns: (count) => ({ commands, state }) => {
|
|
907
|
-
if (getColumnsDepth(state.doc, state.selection.from) >= MAX_COLUMNS_DEPTH) return false;
|
|
908
|
-
const nodeType = NODE_TYPE_MAP[count];
|
|
909
|
-
const children = Array.from({ length: count }, () => ({
|
|
910
|
-
type: "columnsColumn",
|
|
911
|
-
content: [{
|
|
912
|
-
type: "paragraph",
|
|
913
|
-
content: []
|
|
914
|
-
}]
|
|
915
|
-
}));
|
|
916
|
-
return commands.insertContent({
|
|
917
|
-
type: nodeType,
|
|
918
|
-
content: children
|
|
919
|
-
});
|
|
920
|
-
} };
|
|
921
|
-
} },
|
|
922
|
-
renderToReactEmail({ children, node, styles }) {
|
|
923
|
-
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
924
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Row, {
|
|
925
|
-
className: node.attrs?.class || void 0,
|
|
926
|
-
style: {
|
|
927
|
-
...styles.reset,
|
|
928
|
-
...inlineStyles
|
|
929
|
-
},
|
|
930
|
-
children
|
|
931
|
-
});
|
|
909
|
+
...style
|
|
932
910
|
}
|
|
933
911
|
});
|
|
934
|
-
}
|
|
935
|
-
const TwoColumns = createColumnsNode(VARIANTS[0], true);
|
|
936
|
-
const ThreeColumns = createColumnsNode(VARIANTS[1], false);
|
|
937
|
-
const FourColumns = createColumnsNode(VARIANTS[2], false);
|
|
938
|
-
const ColumnsColumn = EmailNode.create({
|
|
939
|
-
name: "columnsColumn",
|
|
940
|
-
group: "columnsColumn",
|
|
941
|
-
content: "block+",
|
|
942
|
-
isolating: true,
|
|
943
|
-
addAttributes() {
|
|
944
|
-
return { ...createStandardAttributes([...LAYOUT_ATTRIBUTES, ...COMMON_HTML_ATTRIBUTES]) };
|
|
945
|
-
},
|
|
946
|
-
parseHTML() {
|
|
947
|
-
return [{ tag: "div[data-type=\"column\"]" }];
|
|
948
|
-
},
|
|
949
|
-
renderHTML({ HTMLAttributes }) {
|
|
950
|
-
return [
|
|
951
|
-
"div",
|
|
952
|
-
(0, __tiptap_core.mergeAttributes)({
|
|
953
|
-
"data-type": "column",
|
|
954
|
-
class: "node-column"
|
|
955
|
-
}, HTMLAttributes),
|
|
956
|
-
0
|
|
957
|
-
];
|
|
958
|
-
},
|
|
959
|
-
addKeyboardShortcuts() {
|
|
960
|
-
return {
|
|
961
|
-
Backspace: ({ editor }) => {
|
|
962
|
-
const { state } = editor;
|
|
963
|
-
const { selection } = state;
|
|
964
|
-
const { empty, $from } = selection;
|
|
965
|
-
if (!empty) return false;
|
|
966
|
-
for (let depth = $from.depth; depth >= 1; depth--) {
|
|
967
|
-
if ($from.pos !== $from.start(depth)) break;
|
|
968
|
-
const indexInParent = $from.index(depth - 1);
|
|
969
|
-
if (indexInParent === 0) continue;
|
|
970
|
-
const prevNode = $from.node(depth - 1).child(indexInParent - 1);
|
|
971
|
-
if (COLUMN_PARENT_SET.has(prevNode.type.name)) {
|
|
972
|
-
const deleteFrom = $from.before(depth) - prevNode.nodeSize;
|
|
973
|
-
const deleteTo = $from.before(depth);
|
|
974
|
-
editor.view.dispatch(state.tr.delete(deleteFrom, deleteTo));
|
|
975
|
-
return true;
|
|
976
|
-
}
|
|
977
|
-
break;
|
|
978
|
-
}
|
|
979
|
-
return false;
|
|
980
|
-
},
|
|
981
|
-
"Mod-a": ({ editor }) => {
|
|
982
|
-
const { state } = editor;
|
|
983
|
-
const { $from } = state.selection;
|
|
984
|
-
for (let d = $from.depth; d > 0; d--) {
|
|
985
|
-
if ($from.node(d).type.name !== "columnsColumn") continue;
|
|
986
|
-
const columnStart = $from.start(d);
|
|
987
|
-
const columnEnd = $from.end(d);
|
|
988
|
-
const { from, to } = state.selection;
|
|
989
|
-
if (from === columnStart && to === columnEnd) return false;
|
|
990
|
-
editor.view.dispatch(state.tr.setSelection(__tiptap_pm_state.TextSelection.create(state.doc, columnStart, columnEnd)));
|
|
991
|
-
return true;
|
|
992
|
-
}
|
|
993
|
-
return false;
|
|
994
|
-
}
|
|
995
|
-
};
|
|
996
|
-
},
|
|
997
|
-
renderToReactEmail({ children, node, styles }) {
|
|
998
|
-
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
999
|
-
const width = node.attrs?.width;
|
|
1000
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Column, {
|
|
1001
|
-
className: node.attrs?.class || void 0,
|
|
1002
|
-
style: {
|
|
1003
|
-
...styles.reset,
|
|
1004
|
-
...inlineStyles,
|
|
1005
|
-
...width ? { width } : {}
|
|
1006
|
-
},
|
|
1007
|
-
children
|
|
1008
|
-
});
|
|
1009
|
-
}
|
|
1010
912
|
});
|
|
1011
913
|
|
|
1012
914
|
//#endregion
|
|
@@ -1041,12 +943,12 @@ const Div = EmailNode.create({
|
|
|
1041
943
|
addAttributes() {
|
|
1042
944
|
return { ...createStandardAttributes([...COMMON_HTML_ATTRIBUTES, ...LAYOUT_ATTRIBUTES]) };
|
|
1043
945
|
},
|
|
1044
|
-
renderToReactEmail({ children, node,
|
|
946
|
+
renderToReactEmail({ children, node, style }) {
|
|
1045
947
|
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
1046
948
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1047
949
|
className: node.attrs?.class || void 0,
|
|
1048
950
|
style: {
|
|
1049
|
-
...
|
|
951
|
+
...style,
|
|
1050
952
|
...inlineStyles
|
|
1051
953
|
},
|
|
1052
954
|
children
|
|
@@ -1297,14 +1199,14 @@ const Section = EmailNode.create({
|
|
|
1297
1199
|
});
|
|
1298
1200
|
} };
|
|
1299
1201
|
},
|
|
1300
|
-
renderToReactEmail({ children, node,
|
|
1202
|
+
renderToReactEmail({ children, node, style }) {
|
|
1301
1203
|
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
1302
1204
|
const textAlign = node.attrs?.align || node.attrs?.alignment;
|
|
1303
1205
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Section, {
|
|
1304
1206
|
className: node.attrs?.class || void 0,
|
|
1305
1207
|
align: textAlign,
|
|
1306
1208
|
style: {
|
|
1307
|
-
...
|
|
1209
|
+
...style,
|
|
1308
1210
|
...inlineStyles,
|
|
1309
1211
|
...getTextAlignment(textAlign)
|
|
1310
1212
|
},
|
|
@@ -1437,7 +1339,7 @@ const Table = EmailNode.create({
|
|
|
1437
1339
|
]
|
|
1438
1340
|
];
|
|
1439
1341
|
},
|
|
1440
|
-
renderToReactEmail({ children, node,
|
|
1342
|
+
renderToReactEmail({ children, node, style }) {
|
|
1441
1343
|
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
1442
1344
|
const alignment = node.attrs?.align || node.attrs?.alignment;
|
|
1443
1345
|
const width = node.attrs?.width;
|
|
@@ -1448,7 +1350,7 @@ const Table = EmailNode.create({
|
|
|
1448
1350
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Section, {
|
|
1449
1351
|
className: node.attrs?.class || void 0,
|
|
1450
1352
|
align: alignment,
|
|
1451
|
-
style: resolveConflictingStyles(
|
|
1353
|
+
style: resolveConflictingStyles(style, {
|
|
1452
1354
|
...inlineStyles,
|
|
1453
1355
|
...centeringStyles
|
|
1454
1356
|
}),
|
|
@@ -1489,12 +1391,12 @@ const TableRow = EmailNode.create({
|
|
|
1489
1391
|
0
|
|
1490
1392
|
];
|
|
1491
1393
|
},
|
|
1492
|
-
renderToReactEmail({ children, node,
|
|
1394
|
+
renderToReactEmail({ children, node, style }) {
|
|
1493
1395
|
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
1494
1396
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", {
|
|
1495
1397
|
className: node.attrs?.class || void 0,
|
|
1496
1398
|
style: {
|
|
1497
|
-
...
|
|
1399
|
+
...style,
|
|
1498
1400
|
...inlineStyles
|
|
1499
1401
|
},
|
|
1500
1402
|
children
|
|
@@ -1534,13 +1436,13 @@ const TableCell = EmailNode.create({
|
|
|
1534
1436
|
0
|
|
1535
1437
|
];
|
|
1536
1438
|
},
|
|
1537
|
-
renderToReactEmail({ children, node,
|
|
1439
|
+
renderToReactEmail({ children, node, style }) {
|
|
1538
1440
|
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
1539
1441
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Column, {
|
|
1540
1442
|
className: node.attrs?.class || void 0,
|
|
1541
1443
|
align: node.attrs?.align || node.attrs?.alignment,
|
|
1542
1444
|
style: {
|
|
1543
|
-
...
|
|
1445
|
+
...style,
|
|
1544
1446
|
...inlineStyles
|
|
1545
1447
|
},
|
|
1546
1448
|
children
|
|
@@ -1583,10 +1485,949 @@ const TableHeader = __tiptap_core.Node.create({
|
|
|
1583
1485
|
}
|
|
1584
1486
|
});
|
|
1585
1487
|
|
|
1488
|
+
//#endregion
|
|
1489
|
+
//#region src/extensions/uppercase.ts
|
|
1490
|
+
const Uppercase = __tiptap_core.Mark.create({
|
|
1491
|
+
name: "uppercase",
|
|
1492
|
+
addOptions() {
|
|
1493
|
+
return { HTMLAttributes: {} };
|
|
1494
|
+
},
|
|
1495
|
+
parseHTML() {
|
|
1496
|
+
return [{
|
|
1497
|
+
tag: "span",
|
|
1498
|
+
getAttrs: (node) => {
|
|
1499
|
+
if (node.style.textTransform === "uppercase") return {};
|
|
1500
|
+
return false;
|
|
1501
|
+
}
|
|
1502
|
+
}];
|
|
1503
|
+
},
|
|
1504
|
+
renderHTML({ HTMLAttributes }) {
|
|
1505
|
+
return [
|
|
1506
|
+
"span",
|
|
1507
|
+
(0, __tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes, { style: "text-transform: uppercase" }),
|
|
1508
|
+
0
|
|
1509
|
+
];
|
|
1510
|
+
},
|
|
1511
|
+
addCommands() {
|
|
1512
|
+
return {
|
|
1513
|
+
setUppercase: () => ({ commands }) => {
|
|
1514
|
+
return commands.setMark(this.name);
|
|
1515
|
+
},
|
|
1516
|
+
toggleUppercase: () => ({ commands }) => {
|
|
1517
|
+
return commands.toggleMark(this.name);
|
|
1518
|
+
},
|
|
1519
|
+
unsetUppercase: () => ({ commands }) => {
|
|
1520
|
+
return commands.unsetMark(this.name);
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
});
|
|
1525
|
+
|
|
1526
|
+
//#endregion
|
|
1527
|
+
//#region src/extensions/columns.tsx
|
|
1528
|
+
const COLUMN_PARENT_TYPES = [
|
|
1529
|
+
"twoColumns",
|
|
1530
|
+
"threeColumns",
|
|
1531
|
+
"fourColumns"
|
|
1532
|
+
];
|
|
1533
|
+
const COLUMN_PARENT_SET = new Set(COLUMN_PARENT_TYPES);
|
|
1534
|
+
const MAX_COLUMNS_DEPTH = 3;
|
|
1535
|
+
function getColumnsDepth(doc, from) {
|
|
1536
|
+
const $from = doc.resolve(from);
|
|
1537
|
+
let depth = 0;
|
|
1538
|
+
for (let d = $from.depth; d > 0; d--) if (COLUMN_PARENT_SET.has($from.node(d).type.name)) depth++;
|
|
1539
|
+
return depth;
|
|
1540
|
+
}
|
|
1541
|
+
const VARIANTS = [
|
|
1542
|
+
{
|
|
1543
|
+
name: "twoColumns",
|
|
1544
|
+
columnCount: 2,
|
|
1545
|
+
content: "columnsColumn columnsColumn",
|
|
1546
|
+
dataType: "two-columns"
|
|
1547
|
+
},
|
|
1548
|
+
{
|
|
1549
|
+
name: "threeColumns",
|
|
1550
|
+
columnCount: 3,
|
|
1551
|
+
content: "columnsColumn columnsColumn columnsColumn",
|
|
1552
|
+
dataType: "three-columns"
|
|
1553
|
+
},
|
|
1554
|
+
{
|
|
1555
|
+
name: "fourColumns",
|
|
1556
|
+
columnCount: 4,
|
|
1557
|
+
content: "columnsColumn{4}",
|
|
1558
|
+
dataType: "four-columns"
|
|
1559
|
+
}
|
|
1560
|
+
];
|
|
1561
|
+
const NODE_TYPE_MAP = {
|
|
1562
|
+
2: "twoColumns",
|
|
1563
|
+
3: "threeColumns",
|
|
1564
|
+
4: "fourColumns"
|
|
1565
|
+
};
|
|
1566
|
+
function createColumnsNode(config, includeCommands) {
|
|
1567
|
+
return EmailNode.create({
|
|
1568
|
+
name: config.name,
|
|
1569
|
+
group: "block",
|
|
1570
|
+
content: config.content,
|
|
1571
|
+
isolating: true,
|
|
1572
|
+
defining: true,
|
|
1573
|
+
addAttributes() {
|
|
1574
|
+
return createStandardAttributes([...LAYOUT_ATTRIBUTES, ...COMMON_HTML_ATTRIBUTES]);
|
|
1575
|
+
},
|
|
1576
|
+
parseHTML() {
|
|
1577
|
+
return [{ tag: `div[data-type="${config.dataType}"]` }];
|
|
1578
|
+
},
|
|
1579
|
+
renderHTML({ HTMLAttributes }) {
|
|
1580
|
+
return [
|
|
1581
|
+
"div",
|
|
1582
|
+
(0, __tiptap_core.mergeAttributes)({
|
|
1583
|
+
"data-type": config.dataType,
|
|
1584
|
+
class: "node-columns"
|
|
1585
|
+
}, HTMLAttributes),
|
|
1586
|
+
0
|
|
1587
|
+
];
|
|
1588
|
+
},
|
|
1589
|
+
...includeCommands && { addCommands() {
|
|
1590
|
+
return { insertColumns: (count) => ({ commands, state }) => {
|
|
1591
|
+
if (getColumnsDepth(state.doc, state.selection.from) >= MAX_COLUMNS_DEPTH) return false;
|
|
1592
|
+
const nodeType = NODE_TYPE_MAP[count];
|
|
1593
|
+
const children = Array.from({ length: count }, () => ({
|
|
1594
|
+
type: "columnsColumn",
|
|
1595
|
+
content: [{
|
|
1596
|
+
type: "paragraph",
|
|
1597
|
+
content: []
|
|
1598
|
+
}]
|
|
1599
|
+
}));
|
|
1600
|
+
return commands.insertContent({
|
|
1601
|
+
type: nodeType,
|
|
1602
|
+
content: children
|
|
1603
|
+
});
|
|
1604
|
+
} };
|
|
1605
|
+
} },
|
|
1606
|
+
renderToReactEmail({ children, node, style }) {
|
|
1607
|
+
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
1608
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Row, {
|
|
1609
|
+
className: node.attrs?.class || void 0,
|
|
1610
|
+
style: {
|
|
1611
|
+
...style,
|
|
1612
|
+
...inlineStyles
|
|
1613
|
+
},
|
|
1614
|
+
children
|
|
1615
|
+
});
|
|
1616
|
+
}
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
const TwoColumns = createColumnsNode(VARIANTS[0], true);
|
|
1620
|
+
const ThreeColumns = createColumnsNode(VARIANTS[1], false);
|
|
1621
|
+
const FourColumns = createColumnsNode(VARIANTS[2], false);
|
|
1622
|
+
const ColumnsColumn = EmailNode.create({
|
|
1623
|
+
name: "columnsColumn",
|
|
1624
|
+
group: "columnsColumn",
|
|
1625
|
+
content: "block+",
|
|
1626
|
+
isolating: true,
|
|
1627
|
+
addAttributes() {
|
|
1628
|
+
return { ...createStandardAttributes([...LAYOUT_ATTRIBUTES, ...COMMON_HTML_ATTRIBUTES]) };
|
|
1629
|
+
},
|
|
1630
|
+
parseHTML() {
|
|
1631
|
+
return [{ tag: "div[data-type=\"column\"]" }];
|
|
1632
|
+
},
|
|
1633
|
+
renderHTML({ HTMLAttributes }) {
|
|
1634
|
+
return [
|
|
1635
|
+
"div",
|
|
1636
|
+
(0, __tiptap_core.mergeAttributes)({
|
|
1637
|
+
"data-type": "column",
|
|
1638
|
+
class: "node-column"
|
|
1639
|
+
}, HTMLAttributes),
|
|
1640
|
+
0
|
|
1641
|
+
];
|
|
1642
|
+
},
|
|
1643
|
+
addKeyboardShortcuts() {
|
|
1644
|
+
return {
|
|
1645
|
+
Backspace: ({ editor }) => {
|
|
1646
|
+
const { state } = editor;
|
|
1647
|
+
const { selection } = state;
|
|
1648
|
+
const { empty, $from } = selection;
|
|
1649
|
+
if (!empty) return false;
|
|
1650
|
+
for (let depth = $from.depth; depth >= 1; depth--) {
|
|
1651
|
+
if ($from.pos !== $from.start(depth)) break;
|
|
1652
|
+
const indexInParent = $from.index(depth - 1);
|
|
1653
|
+
if (indexInParent === 0) continue;
|
|
1654
|
+
const prevNode = $from.node(depth - 1).child(indexInParent - 1);
|
|
1655
|
+
if (COLUMN_PARENT_SET.has(prevNode.type.name)) {
|
|
1656
|
+
const deleteFrom = $from.before(depth) - prevNode.nodeSize;
|
|
1657
|
+
const deleteTo = $from.before(depth);
|
|
1658
|
+
editor.view.dispatch(state.tr.delete(deleteFrom, deleteTo));
|
|
1659
|
+
return true;
|
|
1660
|
+
}
|
|
1661
|
+
break;
|
|
1662
|
+
}
|
|
1663
|
+
return false;
|
|
1664
|
+
},
|
|
1665
|
+
"Mod-a": ({ editor }) => {
|
|
1666
|
+
const { state } = editor;
|
|
1667
|
+
const { $from } = state.selection;
|
|
1668
|
+
for (let d = $from.depth; d > 0; d--) {
|
|
1669
|
+
if ($from.node(d).type.name !== "columnsColumn") continue;
|
|
1670
|
+
const columnStart = $from.start(d);
|
|
1671
|
+
const columnEnd = $from.end(d);
|
|
1672
|
+
const { from, to } = state.selection;
|
|
1673
|
+
if (from === columnStart && to === columnEnd) return false;
|
|
1674
|
+
editor.view.dispatch(state.tr.setSelection(__tiptap_pm_state.TextSelection.create(state.doc, columnStart, columnEnd)));
|
|
1675
|
+
return true;
|
|
1676
|
+
}
|
|
1677
|
+
return false;
|
|
1678
|
+
}
|
|
1679
|
+
};
|
|
1680
|
+
},
|
|
1681
|
+
renderToReactEmail({ children, node, style }) {
|
|
1682
|
+
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
1683
|
+
const width = node.attrs?.width;
|
|
1684
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Column, {
|
|
1685
|
+
className: node.attrs?.class || void 0,
|
|
1686
|
+
style: {
|
|
1687
|
+
...style,
|
|
1688
|
+
...inlineStyles,
|
|
1689
|
+
...width ? { width } : {}
|
|
1690
|
+
},
|
|
1691
|
+
children
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
});
|
|
1695
|
+
|
|
1696
|
+
//#endregion
|
|
1697
|
+
//#region src/extensions/index.ts
|
|
1698
|
+
const coreExtensions = [
|
|
1699
|
+
__tiptap_starter_kit.StarterKit.configure({
|
|
1700
|
+
undoRedo: false,
|
|
1701
|
+
heading: false,
|
|
1702
|
+
link: false,
|
|
1703
|
+
underline: false,
|
|
1704
|
+
trailingNode: false,
|
|
1705
|
+
bold: false,
|
|
1706
|
+
gapcursor: false,
|
|
1707
|
+
listItem: {},
|
|
1708
|
+
bulletList: { HTMLAttributes: { class: "node-bulletList" } },
|
|
1709
|
+
paragraph: { HTMLAttributes: { class: "node-paragraph" } },
|
|
1710
|
+
orderedList: { HTMLAttributes: { class: "node-orderedList" } },
|
|
1711
|
+
blockquote: { HTMLAttributes: { class: "node-blockquote" } },
|
|
1712
|
+
codeBlock: false,
|
|
1713
|
+
code: { HTMLAttributes: {
|
|
1714
|
+
class: "node-inlineCode",
|
|
1715
|
+
spellcheck: "false"
|
|
1716
|
+
} },
|
|
1717
|
+
horizontalRule: false,
|
|
1718
|
+
dropcursor: {
|
|
1719
|
+
color: "#61a8f8",
|
|
1720
|
+
class: "rounded-full animate-[fade-in_300ms_ease-in-out] !z-40",
|
|
1721
|
+
width: 4
|
|
1722
|
+
}
|
|
1723
|
+
}),
|
|
1724
|
+
CodeBlockPrism.configure({
|
|
1725
|
+
defaultLanguage: "javascript",
|
|
1726
|
+
HTMLAttributes: { class: "prism node-codeBlock" }
|
|
1727
|
+
}),
|
|
1728
|
+
Placeholder,
|
|
1729
|
+
PreviewText,
|
|
1730
|
+
Bold,
|
|
1731
|
+
Sup,
|
|
1732
|
+
Uppercase,
|
|
1733
|
+
PreservedStyle,
|
|
1734
|
+
Table,
|
|
1735
|
+
TableRow,
|
|
1736
|
+
TableCell,
|
|
1737
|
+
TableHeader,
|
|
1738
|
+
Body,
|
|
1739
|
+
Div,
|
|
1740
|
+
Button,
|
|
1741
|
+
Section,
|
|
1742
|
+
AlignmentAttribute.configure({ types: [
|
|
1743
|
+
"heading",
|
|
1744
|
+
"paragraph",
|
|
1745
|
+
"image",
|
|
1746
|
+
"blockquote",
|
|
1747
|
+
"codeBlock",
|
|
1748
|
+
"bulletList",
|
|
1749
|
+
"orderedList",
|
|
1750
|
+
"listItem",
|
|
1751
|
+
"button",
|
|
1752
|
+
"youtube",
|
|
1753
|
+
"twitter",
|
|
1754
|
+
"table",
|
|
1755
|
+
"tableRow",
|
|
1756
|
+
"tableCell",
|
|
1757
|
+
"tableHeader",
|
|
1758
|
+
"columnsColumn"
|
|
1759
|
+
] }),
|
|
1760
|
+
StyleAttribute.configure({ types: [
|
|
1761
|
+
"heading",
|
|
1762
|
+
"paragraph",
|
|
1763
|
+
"image",
|
|
1764
|
+
"blockquote",
|
|
1765
|
+
"codeBlock",
|
|
1766
|
+
"bulletList",
|
|
1767
|
+
"orderedList",
|
|
1768
|
+
"listItem",
|
|
1769
|
+
"button",
|
|
1770
|
+
"youtube",
|
|
1771
|
+
"twitter",
|
|
1772
|
+
"horizontalRule",
|
|
1773
|
+
"footer",
|
|
1774
|
+
"section",
|
|
1775
|
+
"div",
|
|
1776
|
+
"body",
|
|
1777
|
+
"table",
|
|
1778
|
+
"tableRow",
|
|
1779
|
+
"tableCell",
|
|
1780
|
+
"tableHeader",
|
|
1781
|
+
"columnsColumn",
|
|
1782
|
+
"link"
|
|
1783
|
+
] }),
|
|
1784
|
+
ClassAttribute.configure({ types: [
|
|
1785
|
+
"heading",
|
|
1786
|
+
"paragraph",
|
|
1787
|
+
"image",
|
|
1788
|
+
"blockquote",
|
|
1789
|
+
"bulletList",
|
|
1790
|
+
"orderedList",
|
|
1791
|
+
"listItem",
|
|
1792
|
+
"button",
|
|
1793
|
+
"youtube",
|
|
1794
|
+
"twitter",
|
|
1795
|
+
"horizontalRule",
|
|
1796
|
+
"footer",
|
|
1797
|
+
"section",
|
|
1798
|
+
"div",
|
|
1799
|
+
"body",
|
|
1800
|
+
"table",
|
|
1801
|
+
"tableRow",
|
|
1802
|
+
"tableCell",
|
|
1803
|
+
"tableHeader",
|
|
1804
|
+
"columnsColumn",
|
|
1805
|
+
"link"
|
|
1806
|
+
] }),
|
|
1807
|
+
MaxNesting.configure({
|
|
1808
|
+
maxDepth: 50,
|
|
1809
|
+
nodeTypes: [
|
|
1810
|
+
"section",
|
|
1811
|
+
"bulletList",
|
|
1812
|
+
"orderedList"
|
|
1813
|
+
]
|
|
1814
|
+
})
|
|
1815
|
+
];
|
|
1816
|
+
|
|
1817
|
+
//#endregion
|
|
1818
|
+
//#region src/utils/set-text-alignment.ts
|
|
1819
|
+
function setTextAlignment(editor, alignment) {
|
|
1820
|
+
const { from, to } = editor.state.selection;
|
|
1821
|
+
const tr = editor.state.tr;
|
|
1822
|
+
editor.state.doc.nodesBetween(from, to, (node, pos) => {
|
|
1823
|
+
if (node.isTextblock) {
|
|
1824
|
+
const prop = "align" in node.attrs ? "align" : "alignment";
|
|
1825
|
+
tr.setNodeMarkup(pos, null, {
|
|
1826
|
+
...node.attrs,
|
|
1827
|
+
[prop]: alignment
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1830
|
+
});
|
|
1831
|
+
editor.view.dispatch(tr);
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
//#endregion
|
|
1835
|
+
//#region src/ui/bubble-menu/context.tsx
|
|
1836
|
+
const BubbleMenuContext = react.createContext(null);
|
|
1837
|
+
function useBubbleMenuContext() {
|
|
1838
|
+
const context = react.useContext(BubbleMenuContext);
|
|
1839
|
+
if (!context) throw new Error("BubbleMenu compound components must be used within <BubbleMenu.Root>");
|
|
1840
|
+
return context;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
//#endregion
|
|
1844
|
+
//#region src/ui/bubble-menu/item.tsx
|
|
1845
|
+
function BubbleMenuItem({ name, isActive, onCommand, className, children,...rest }) {
|
|
1846
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1847
|
+
type: "button",
|
|
1848
|
+
"aria-label": name,
|
|
1849
|
+
"aria-pressed": isActive,
|
|
1850
|
+
className,
|
|
1851
|
+
"data-re-bubble-menu-item": "",
|
|
1852
|
+
"data-item": name,
|
|
1853
|
+
...isActive ? { "data-active": "" } : {},
|
|
1854
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1855
|
+
onClick: onCommand,
|
|
1856
|
+
...rest,
|
|
1857
|
+
children
|
|
1858
|
+
});
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
//#endregion
|
|
1862
|
+
//#region src/ui/bubble-menu/align-center.tsx
|
|
1863
|
+
function BubbleMenuAlignCenter({ className, children }) {
|
|
1864
|
+
const { editor } = useBubbleMenuContext();
|
|
1865
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BubbleMenuItem, {
|
|
1866
|
+
name: "align-center",
|
|
1867
|
+
isActive: (0, __tiptap_react.useEditorState)({
|
|
1868
|
+
editor,
|
|
1869
|
+
selector: ({ editor: editor$1 }) => editor$1?.isActive({ alignment: "center" }) ?? false
|
|
1870
|
+
}),
|
|
1871
|
+
onCommand: () => setTextAlignment(editor, "center"),
|
|
1872
|
+
className,
|
|
1873
|
+
children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.AlignCenterIcon, {})
|
|
1874
|
+
});
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
//#endregion
|
|
1878
|
+
//#region src/ui/bubble-menu/align-left.tsx
|
|
1879
|
+
function BubbleMenuAlignLeft({ className, children }) {
|
|
1880
|
+
const { editor } = useBubbleMenuContext();
|
|
1881
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BubbleMenuItem, {
|
|
1882
|
+
name: "align-left",
|
|
1883
|
+
isActive: (0, __tiptap_react.useEditorState)({
|
|
1884
|
+
editor,
|
|
1885
|
+
selector: ({ editor: editor$1 }) => editor$1?.isActive({ alignment: "left" }) ?? false
|
|
1886
|
+
}),
|
|
1887
|
+
onCommand: () => setTextAlignment(editor, "left"),
|
|
1888
|
+
className,
|
|
1889
|
+
children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.AlignLeftIcon, {})
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
//#endregion
|
|
1894
|
+
//#region src/ui/bubble-menu/align-right.tsx
|
|
1895
|
+
function BubbleMenuAlignRight({ className, children }) {
|
|
1896
|
+
const { editor } = useBubbleMenuContext();
|
|
1897
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BubbleMenuItem, {
|
|
1898
|
+
name: "align-right",
|
|
1899
|
+
isActive: (0, __tiptap_react.useEditorState)({
|
|
1900
|
+
editor,
|
|
1901
|
+
selector: ({ editor: editor$1 }) => editor$1?.isActive({ alignment: "right" }) ?? false
|
|
1902
|
+
}),
|
|
1903
|
+
onCommand: () => setTextAlignment(editor, "right"),
|
|
1904
|
+
className,
|
|
1905
|
+
children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.AlignRightIcon, {})
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
//#endregion
|
|
1910
|
+
//#region src/ui/bubble-menu/create-mark-bubble-item.tsx
|
|
1911
|
+
function createMarkBubbleItem(config) {
|
|
1912
|
+
function MarkBubbleItem({ className, children }) {
|
|
1913
|
+
const { editor } = useBubbleMenuContext();
|
|
1914
|
+
const isActive = (0, __tiptap_react.useEditorState)({
|
|
1915
|
+
editor,
|
|
1916
|
+
selector: ({ editor: editor$1 }) => {
|
|
1917
|
+
if (config.activeParams) return editor$1?.isActive(config.activeName, config.activeParams) ?? false;
|
|
1918
|
+
return editor$1?.isActive(config.activeName) ?? false;
|
|
1919
|
+
}
|
|
1920
|
+
});
|
|
1921
|
+
const handleCommand = () => {
|
|
1922
|
+
const chain = editor.chain().focus();
|
|
1923
|
+
const method = chain[config.command];
|
|
1924
|
+
if (method) method.call(chain).run();
|
|
1925
|
+
};
|
|
1926
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BubbleMenuItem, {
|
|
1927
|
+
name: config.name,
|
|
1928
|
+
isActive,
|
|
1929
|
+
onCommand: handleCommand,
|
|
1930
|
+
className,
|
|
1931
|
+
children: children ?? config.icon
|
|
1932
|
+
});
|
|
1933
|
+
}
|
|
1934
|
+
MarkBubbleItem.displayName = `BubbleMenu${config.name.charAt(0).toUpperCase() + config.name.slice(1)}`;
|
|
1935
|
+
return MarkBubbleItem;
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
//#endregion
|
|
1939
|
+
//#region src/ui/bubble-menu/bold.tsx
|
|
1940
|
+
const BubbleMenuBold = createMarkBubbleItem({
|
|
1941
|
+
name: "bold",
|
|
1942
|
+
activeName: "bold",
|
|
1943
|
+
command: "toggleBold",
|
|
1944
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.BoldIcon, {})
|
|
1945
|
+
});
|
|
1946
|
+
|
|
1947
|
+
//#endregion
|
|
1948
|
+
//#region src/ui/bubble-menu/code.tsx
|
|
1949
|
+
const BubbleMenuCode = createMarkBubbleItem({
|
|
1950
|
+
name: "code",
|
|
1951
|
+
activeName: "code",
|
|
1952
|
+
command: "toggleCode",
|
|
1953
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.CodeIcon, {})
|
|
1954
|
+
});
|
|
1955
|
+
|
|
1956
|
+
//#endregion
|
|
1957
|
+
//#region src/ui/bubble-menu/group.tsx
|
|
1958
|
+
function BubbleMenuItemGroup({ className, children }) {
|
|
1959
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("fieldset", {
|
|
1960
|
+
className,
|
|
1961
|
+
"data-re-bubble-menu-group": "",
|
|
1962
|
+
children
|
|
1963
|
+
});
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
//#endregion
|
|
1967
|
+
//#region src/ui/bubble-menu/italic.tsx
|
|
1968
|
+
const BubbleMenuItalic = createMarkBubbleItem({
|
|
1969
|
+
name: "italic",
|
|
1970
|
+
activeName: "italic",
|
|
1971
|
+
command: "toggleItalic",
|
|
1972
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ItalicIcon, {})
|
|
1973
|
+
});
|
|
1974
|
+
|
|
1975
|
+
//#endregion
|
|
1976
|
+
//#region src/ui/bubble-menu/utils.ts
|
|
1977
|
+
const SAFE_PROTOCOLS = new Set([
|
|
1978
|
+
"http:",
|
|
1979
|
+
"https:",
|
|
1980
|
+
"mailto:",
|
|
1981
|
+
"tel:"
|
|
1982
|
+
]);
|
|
1983
|
+
/**
|
|
1984
|
+
* Basic URL validation and auto-prefixing.
|
|
1985
|
+
* Rejects dangerous schemes (javascript:, data:, vbscript:, etc.).
|
|
1986
|
+
* Returns the valid URL string or null.
|
|
1987
|
+
*/
|
|
1988
|
+
function getUrlFromString(str) {
|
|
1989
|
+
if (str === "#") return str;
|
|
1990
|
+
try {
|
|
1991
|
+
const url = new URL(str);
|
|
1992
|
+
if (SAFE_PROTOCOLS.has(url.protocol)) return str;
|
|
1993
|
+
return null;
|
|
1994
|
+
} catch {}
|
|
1995
|
+
try {
|
|
1996
|
+
if (str.includes(".") && !str.includes(" ")) return new URL(`https://${str}`).toString();
|
|
1997
|
+
} catch {}
|
|
1998
|
+
return null;
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
//#endregion
|
|
2002
|
+
//#region src/ui/bubble-menu/link-selector.tsx
|
|
2003
|
+
function BubbleMenuLinkSelector({ className, showToggle = true, validateUrl, onLinkApply, onLinkRemove, children }) {
|
|
2004
|
+
const { editor } = useBubbleMenuContext();
|
|
2005
|
+
const [isOpen, setIsOpen] = react.useState(false);
|
|
2006
|
+
const editorState = (0, __tiptap_react.useEditorState)({
|
|
2007
|
+
editor,
|
|
2008
|
+
selector: ({ editor: editor$1 }) => ({
|
|
2009
|
+
isLinkActive: editor$1?.isActive("link") ?? false,
|
|
2010
|
+
hasLink: Boolean(editor$1?.getAttributes("link").href),
|
|
2011
|
+
currentHref: editor$1?.getAttributes("link").href || ""
|
|
2012
|
+
})
|
|
2013
|
+
});
|
|
2014
|
+
react.useEffect(() => {
|
|
2015
|
+
const subscription = editorEventBus.on("bubble-menu:add-link", () => {
|
|
2016
|
+
setIsOpen(true);
|
|
2017
|
+
});
|
|
2018
|
+
return () => {
|
|
2019
|
+
setIsOpen(false);
|
|
2020
|
+
subscription.unsubscribe();
|
|
2021
|
+
};
|
|
2022
|
+
}, []);
|
|
2023
|
+
if (!editorState) return null;
|
|
2024
|
+
const handleOpenLink = () => {
|
|
2025
|
+
setIsOpen(!isOpen);
|
|
2026
|
+
};
|
|
2027
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2028
|
+
"data-re-link-selector": "",
|
|
2029
|
+
...isOpen ? { "data-open": "" } : {},
|
|
2030
|
+
...editorState.hasLink ? { "data-has-link": "" } : {},
|
|
2031
|
+
className,
|
|
2032
|
+
children: [showToggle && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2033
|
+
type: "button",
|
|
2034
|
+
"aria-expanded": isOpen,
|
|
2035
|
+
"aria-haspopup": "true",
|
|
2036
|
+
"aria-label": "Add link",
|
|
2037
|
+
"aria-pressed": editorState.isLinkActive && editorState.hasLink,
|
|
2038
|
+
"data-re-link-selector-trigger": "",
|
|
2039
|
+
onClick: handleOpenLink,
|
|
2040
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.LinkIcon, {})
|
|
2041
|
+
}), isOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LinkForm, {
|
|
2042
|
+
editor,
|
|
2043
|
+
currentHref: editorState.currentHref,
|
|
2044
|
+
validateUrl,
|
|
2045
|
+
onLinkApply,
|
|
2046
|
+
onLinkRemove,
|
|
2047
|
+
setIsOpen,
|
|
2048
|
+
children
|
|
2049
|
+
})]
|
|
2050
|
+
});
|
|
2051
|
+
}
|
|
2052
|
+
function LinkForm({ editor, currentHref, validateUrl, onLinkApply, onLinkRemove, setIsOpen, children }) {
|
|
2053
|
+
const inputRef = react.useRef(null);
|
|
2054
|
+
const formRef = react.useRef(null);
|
|
2055
|
+
const displayHref = currentHref === "#" ? "" : currentHref;
|
|
2056
|
+
const [inputValue, setInputValue] = react.useState(displayHref);
|
|
2057
|
+
react.useEffect(() => {
|
|
2058
|
+
const timeoutId = setTimeout(() => {
|
|
2059
|
+
inputRef.current?.focus();
|
|
2060
|
+
}, 0);
|
|
2061
|
+
return () => clearTimeout(timeoutId);
|
|
2062
|
+
}, []);
|
|
2063
|
+
react.useEffect(() => {
|
|
2064
|
+
const handleKeyDown = (event) => {
|
|
2065
|
+
if (event.key === "Escape") {
|
|
2066
|
+
if (editor.getAttributes("link").href === "#") editor.chain().unsetLink().run();
|
|
2067
|
+
setIsOpen(false);
|
|
2068
|
+
}
|
|
2069
|
+
};
|
|
2070
|
+
const handleClickOutside = (event) => {
|
|
2071
|
+
if (formRef.current && !formRef.current.contains(event.target)) {
|
|
2072
|
+
const form = formRef.current;
|
|
2073
|
+
const submitEvent = new Event("submit", {
|
|
2074
|
+
bubbles: true,
|
|
2075
|
+
cancelable: true
|
|
2076
|
+
});
|
|
2077
|
+
form.dispatchEvent(submitEvent);
|
|
2078
|
+
setIsOpen(false);
|
|
2079
|
+
}
|
|
2080
|
+
};
|
|
2081
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
2082
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
2083
|
+
return () => {
|
|
2084
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
2085
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
2086
|
+
};
|
|
2087
|
+
}, [editor, setIsOpen]);
|
|
2088
|
+
function handleSubmit(e) {
|
|
2089
|
+
e.preventDefault();
|
|
2090
|
+
const value = inputValue.trim();
|
|
2091
|
+
if (value === "") {
|
|
2092
|
+
setLinkHref(editor, "");
|
|
2093
|
+
setIsOpen(false);
|
|
2094
|
+
focusEditor(editor);
|
|
2095
|
+
onLinkRemove?.();
|
|
2096
|
+
return;
|
|
2097
|
+
}
|
|
2098
|
+
const finalValue = (validateUrl ?? getUrlFromString)(value);
|
|
2099
|
+
if (!finalValue) {
|
|
2100
|
+
setLinkHref(editor, "");
|
|
2101
|
+
setIsOpen(false);
|
|
2102
|
+
focusEditor(editor);
|
|
2103
|
+
onLinkRemove?.();
|
|
2104
|
+
return;
|
|
2105
|
+
}
|
|
2106
|
+
setLinkHref(editor, finalValue);
|
|
2107
|
+
setIsOpen(false);
|
|
2108
|
+
focusEditor(editor);
|
|
2109
|
+
onLinkApply?.(finalValue);
|
|
2110
|
+
}
|
|
2111
|
+
function handleUnlink(e) {
|
|
2112
|
+
e.stopPropagation();
|
|
2113
|
+
setLinkHref(editor, "");
|
|
2114
|
+
setIsOpen(false);
|
|
2115
|
+
focusEditor(editor);
|
|
2116
|
+
onLinkRemove?.();
|
|
2117
|
+
}
|
|
2118
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
2119
|
+
ref: formRef,
|
|
2120
|
+
"data-re-link-selector-form": "",
|
|
2121
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
2122
|
+
onClick: (e) => e.stopPropagation(),
|
|
2123
|
+
onKeyDown: (e) => e.stopPropagation(),
|
|
2124
|
+
onSubmit: handleSubmit,
|
|
2125
|
+
children: [
|
|
2126
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2127
|
+
ref: inputRef,
|
|
2128
|
+
"data-re-link-selector-input": "",
|
|
2129
|
+
value: inputValue,
|
|
2130
|
+
onFocus: (e) => e.stopPropagation(),
|
|
2131
|
+
onChange: (e) => setInputValue(e.target.value),
|
|
2132
|
+
placeholder: "Paste a link",
|
|
2133
|
+
type: "text"
|
|
2134
|
+
}),
|
|
2135
|
+
children,
|
|
2136
|
+
displayHref ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2137
|
+
type: "button",
|
|
2138
|
+
"aria-label": "Remove link",
|
|
2139
|
+
"data-re-link-selector-unlink": "",
|
|
2140
|
+
onClick: handleUnlink,
|
|
2141
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.UnlinkIcon, {})
|
|
2142
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2143
|
+
type: "submit",
|
|
2144
|
+
"aria-label": "Apply link",
|
|
2145
|
+
"data-re-link-selector-apply": "",
|
|
2146
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
2147
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, {})
|
|
2148
|
+
})
|
|
2149
|
+
]
|
|
2150
|
+
});
|
|
2151
|
+
}
|
|
2152
|
+
function setLinkHref(editor, href) {
|
|
2153
|
+
if (href.length === 0) {
|
|
2154
|
+
editor.chain().unsetLink().run();
|
|
2155
|
+
return;
|
|
2156
|
+
}
|
|
2157
|
+
const { from, to } = editor.state.selection;
|
|
2158
|
+
if (from === to) {
|
|
2159
|
+
editor.chain().extendMarkRange("link").setLink({ href }).setTextSelection({
|
|
2160
|
+
from,
|
|
2161
|
+
to
|
|
2162
|
+
}).run();
|
|
2163
|
+
return;
|
|
2164
|
+
}
|
|
2165
|
+
editor.chain().setLink({ href }).run();
|
|
2166
|
+
}
|
|
2167
|
+
function focusEditor(editor) {
|
|
2168
|
+
setTimeout(() => {
|
|
2169
|
+
editor.commands.focus();
|
|
2170
|
+
}, 0);
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
//#endregion
|
|
2174
|
+
//#region src/ui/bubble-menu/node-selector.tsx
|
|
2175
|
+
const NodeSelectorContext = react.createContext(null);
|
|
2176
|
+
function useNodeSelectorContext() {
|
|
2177
|
+
const context = react.useContext(NodeSelectorContext);
|
|
2178
|
+
if (!context) throw new Error("NodeSelector compound components must be used within <NodeSelector.Root>");
|
|
2179
|
+
return context;
|
|
2180
|
+
}
|
|
2181
|
+
function NodeSelectorRoot({ omit = [], open: controlledOpen, onOpenChange, className, children }) {
|
|
2182
|
+
const { editor } = useBubbleMenuContext();
|
|
2183
|
+
const [uncontrolledOpen, setUncontrolledOpen] = react.useState(false);
|
|
2184
|
+
const isControlled = controlledOpen !== void 0;
|
|
2185
|
+
const isOpen = isControlled ? controlledOpen : uncontrolledOpen;
|
|
2186
|
+
const setIsOpen = react.useCallback((value) => {
|
|
2187
|
+
if (!isControlled) setUncontrolledOpen(value);
|
|
2188
|
+
onOpenChange?.(value);
|
|
2189
|
+
}, [isControlled, onOpenChange]);
|
|
2190
|
+
const editorState = (0, __tiptap_react.useEditorState)({
|
|
2191
|
+
editor,
|
|
2192
|
+
selector: ({ editor: editor$1 }) => ({
|
|
2193
|
+
isParagraphActive: (editor$1?.isActive("paragraph") ?? false) && !editor$1?.isActive("bulletList") && !editor$1?.isActive("orderedList"),
|
|
2194
|
+
isHeading1Active: editor$1?.isActive("heading", { level: 1 }) ?? false,
|
|
2195
|
+
isHeading2Active: editor$1?.isActive("heading", { level: 2 }) ?? false,
|
|
2196
|
+
isHeading3Active: editor$1?.isActive("heading", { level: 3 }) ?? false,
|
|
2197
|
+
isBulletListActive: editor$1?.isActive("bulletList") ?? false,
|
|
2198
|
+
isOrderedListActive: editor$1?.isActive("orderedList") ?? false,
|
|
2199
|
+
isBlockquoteActive: editor$1?.isActive("blockquote") ?? false,
|
|
2200
|
+
isCodeBlockActive: editor$1?.isActive("codeBlock") ?? false
|
|
2201
|
+
})
|
|
2202
|
+
});
|
|
2203
|
+
const allItems = react.useMemo(() => [
|
|
2204
|
+
{
|
|
2205
|
+
name: "Text",
|
|
2206
|
+
icon: lucide_react.TextIcon,
|
|
2207
|
+
command: () => editor.chain().focus().clearNodes().toggleNode("paragraph", "paragraph").run(),
|
|
2208
|
+
isActive: editorState?.isParagraphActive ?? false
|
|
2209
|
+
},
|
|
2210
|
+
{
|
|
2211
|
+
name: "Title",
|
|
2212
|
+
icon: lucide_react.Heading1,
|
|
2213
|
+
command: () => editor.chain().focus().clearNodes().toggleHeading({ level: 1 }).run(),
|
|
2214
|
+
isActive: editorState?.isHeading1Active ?? false
|
|
2215
|
+
},
|
|
2216
|
+
{
|
|
2217
|
+
name: "Subtitle",
|
|
2218
|
+
icon: lucide_react.Heading2,
|
|
2219
|
+
command: () => editor.chain().focus().clearNodes().toggleHeading({ level: 2 }).run(),
|
|
2220
|
+
isActive: editorState?.isHeading2Active ?? false
|
|
2221
|
+
},
|
|
2222
|
+
{
|
|
2223
|
+
name: "Heading",
|
|
2224
|
+
icon: lucide_react.Heading3,
|
|
2225
|
+
command: () => editor.chain().focus().clearNodes().toggleHeading({ level: 3 }).run(),
|
|
2226
|
+
isActive: editorState?.isHeading3Active ?? false
|
|
2227
|
+
},
|
|
2228
|
+
{
|
|
2229
|
+
name: "Bullet List",
|
|
2230
|
+
icon: lucide_react.List,
|
|
2231
|
+
command: () => editor.chain().focus().clearNodes().toggleBulletList().run(),
|
|
2232
|
+
isActive: editorState?.isBulletListActive ?? false
|
|
2233
|
+
},
|
|
2234
|
+
{
|
|
2235
|
+
name: "Numbered List",
|
|
2236
|
+
icon: lucide_react.ListOrdered,
|
|
2237
|
+
command: () => editor.chain().focus().clearNodes().toggleOrderedList().run(),
|
|
2238
|
+
isActive: editorState?.isOrderedListActive ?? false
|
|
2239
|
+
},
|
|
2240
|
+
{
|
|
2241
|
+
name: "Quote",
|
|
2242
|
+
icon: lucide_react.TextQuote,
|
|
2243
|
+
command: () => editor.chain().focus().clearNodes().toggleNode("paragraph", "paragraph").toggleBlockquote().run(),
|
|
2244
|
+
isActive: editorState?.isBlockquoteActive ?? false
|
|
2245
|
+
},
|
|
2246
|
+
{
|
|
2247
|
+
name: "Code",
|
|
2248
|
+
icon: lucide_react.Code,
|
|
2249
|
+
command: () => editor.chain().focus().clearNodes().toggleCodeBlock().run(),
|
|
2250
|
+
isActive: editorState?.isCodeBlockActive ?? false
|
|
2251
|
+
}
|
|
2252
|
+
], [editor, editorState]);
|
|
2253
|
+
const items = react.useMemo(() => allItems.filter((item) => !omit.includes(item.name)), [allItems, omit]);
|
|
2254
|
+
const activeItem = react.useMemo(() => items.find((item) => item.isActive) ?? { name: "Multiple" }, [items]);
|
|
2255
|
+
const contextValue = react.useMemo(() => ({
|
|
2256
|
+
items,
|
|
2257
|
+
activeItem,
|
|
2258
|
+
isOpen,
|
|
2259
|
+
setIsOpen
|
|
2260
|
+
}), [
|
|
2261
|
+
items,
|
|
2262
|
+
activeItem,
|
|
2263
|
+
isOpen,
|
|
2264
|
+
setIsOpen
|
|
2265
|
+
]);
|
|
2266
|
+
if (!editorState || items.length === 0) return null;
|
|
2267
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NodeSelectorContext.Provider, {
|
|
2268
|
+
value: contextValue,
|
|
2269
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__radix_ui_react_popover.Root, {
|
|
2270
|
+
open: isOpen,
|
|
2271
|
+
onOpenChange: setIsOpen,
|
|
2272
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2273
|
+
"data-re-node-selector": "",
|
|
2274
|
+
...isOpen ? { "data-open": "" } : {},
|
|
2275
|
+
className,
|
|
2276
|
+
children
|
|
2277
|
+
})
|
|
2278
|
+
})
|
|
2279
|
+
});
|
|
2280
|
+
}
|
|
2281
|
+
function NodeSelectorTrigger({ className, children }) {
|
|
2282
|
+
const { activeItem, isOpen, setIsOpen } = useNodeSelectorContext();
|
|
2283
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__radix_ui_react_popover.Trigger, {
|
|
2284
|
+
"data-re-node-selector-trigger": "",
|
|
2285
|
+
className,
|
|
2286
|
+
onClick: () => setIsOpen(!isOpen),
|
|
2287
|
+
children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: activeItem.name }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronDown, {})] })
|
|
2288
|
+
});
|
|
2289
|
+
}
|
|
2290
|
+
function NodeSelectorContent({ className, align = "start", children }) {
|
|
2291
|
+
const { items, setIsOpen } = useNodeSelectorContext();
|
|
2292
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__radix_ui_react_popover.Content, {
|
|
2293
|
+
align,
|
|
2294
|
+
"data-re-node-selector-content": "",
|
|
2295
|
+
className,
|
|
2296
|
+
children: children ? children(items, () => setIsOpen(false)) : items.map((item) => {
|
|
2297
|
+
const Icon = item.icon;
|
|
2298
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2299
|
+
type: "button",
|
|
2300
|
+
"data-re-node-selector-item": "",
|
|
2301
|
+
...item.isActive ? { "data-active": "" } : {},
|
|
2302
|
+
onClick: () => {
|
|
2303
|
+
item.command();
|
|
2304
|
+
setIsOpen(false);
|
|
2305
|
+
},
|
|
2306
|
+
children: [
|
|
2307
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {}),
|
|
2308
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: item.name }),
|
|
2309
|
+
item.isActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, {})
|
|
2310
|
+
]
|
|
2311
|
+
}, item.name);
|
|
2312
|
+
})
|
|
2313
|
+
});
|
|
2314
|
+
}
|
|
2315
|
+
function BubbleMenuNodeSelector({ omit = [], className, triggerContent, open, onOpenChange }) {
|
|
2316
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(NodeSelectorRoot, {
|
|
2317
|
+
omit,
|
|
2318
|
+
open,
|
|
2319
|
+
onOpenChange,
|
|
2320
|
+
className,
|
|
2321
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(NodeSelectorTrigger, { children: triggerContent }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NodeSelectorContent, {})]
|
|
2322
|
+
});
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
//#endregion
|
|
2326
|
+
//#region src/ui/bubble-menu/root.tsx
|
|
2327
|
+
function BubbleMenuRoot({ excludeNodes = [], placement = "bottom", offset = 8, onHide, className, children }) {
|
|
2328
|
+
const { editor } = (0, __tiptap_react.useCurrentEditor)();
|
|
2329
|
+
if (!editor) return null;
|
|
2330
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tiptap_react_menus.BubbleMenu, {
|
|
2331
|
+
editor,
|
|
2332
|
+
"data-re-bubble-menu": "",
|
|
2333
|
+
shouldShow: ({ editor: editor$1, view }) => {
|
|
2334
|
+
for (const node of excludeNodes) if (editor$1.isActive(node)) return false;
|
|
2335
|
+
if (view.dom.classList.contains("dragging")) return false;
|
|
2336
|
+
return editor$1.view.state.selection.content().size > 0;
|
|
2337
|
+
},
|
|
2338
|
+
options: {
|
|
2339
|
+
placement,
|
|
2340
|
+
offset,
|
|
2341
|
+
onHide
|
|
2342
|
+
},
|
|
2343
|
+
className,
|
|
2344
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BubbleMenuContext.Provider, {
|
|
2345
|
+
value: { editor },
|
|
2346
|
+
children
|
|
2347
|
+
})
|
|
2348
|
+
});
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
//#endregion
|
|
2352
|
+
//#region src/ui/bubble-menu/separator.tsx
|
|
2353
|
+
function BubbleMenuSeparator({ className }) {
|
|
2354
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("hr", {
|
|
2355
|
+
className,
|
|
2356
|
+
"data-re-bubble-menu-separator": ""
|
|
2357
|
+
});
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
//#endregion
|
|
2361
|
+
//#region src/ui/bubble-menu/strike.tsx
|
|
2362
|
+
const BubbleMenuStrike = createMarkBubbleItem({
|
|
2363
|
+
name: "strike",
|
|
2364
|
+
activeName: "strike",
|
|
2365
|
+
command: "toggleStrike",
|
|
2366
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.StrikethroughIcon, {})
|
|
2367
|
+
});
|
|
2368
|
+
|
|
2369
|
+
//#endregion
|
|
2370
|
+
//#region src/ui/bubble-menu/underline.tsx
|
|
2371
|
+
const BubbleMenuUnderline = createMarkBubbleItem({
|
|
2372
|
+
name: "underline",
|
|
2373
|
+
activeName: "underline",
|
|
2374
|
+
command: "toggleUnderline",
|
|
2375
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.UnderlineIcon, {})
|
|
2376
|
+
});
|
|
2377
|
+
|
|
2378
|
+
//#endregion
|
|
2379
|
+
//#region src/ui/bubble-menu/uppercase.tsx
|
|
2380
|
+
const BubbleMenuUppercase = createMarkBubbleItem({
|
|
2381
|
+
name: "uppercase",
|
|
2382
|
+
activeName: "uppercase",
|
|
2383
|
+
command: "toggleUppercase",
|
|
2384
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.CaseUpperIcon, {})
|
|
2385
|
+
});
|
|
2386
|
+
|
|
2387
|
+
//#endregion
|
|
2388
|
+
//#region src/ui/bubble-menu/index.ts
|
|
2389
|
+
const BubbleMenu = {
|
|
2390
|
+
Root: BubbleMenuRoot,
|
|
2391
|
+
ItemGroup: BubbleMenuItemGroup,
|
|
2392
|
+
Separator: BubbleMenuSeparator,
|
|
2393
|
+
Item: BubbleMenuItem,
|
|
2394
|
+
Bold: BubbleMenuBold,
|
|
2395
|
+
Italic: BubbleMenuItalic,
|
|
2396
|
+
Underline: BubbleMenuUnderline,
|
|
2397
|
+
Strike: BubbleMenuStrike,
|
|
2398
|
+
Code: BubbleMenuCode,
|
|
2399
|
+
Uppercase: BubbleMenuUppercase,
|
|
2400
|
+
AlignLeft: BubbleMenuAlignLeft,
|
|
2401
|
+
AlignCenter: BubbleMenuAlignCenter,
|
|
2402
|
+
AlignRight: BubbleMenuAlignRight,
|
|
2403
|
+
NodeSelector: Object.assign(BubbleMenuNodeSelector, {
|
|
2404
|
+
Root: NodeSelectorRoot,
|
|
2405
|
+
Trigger: NodeSelectorTrigger,
|
|
2406
|
+
Content: NodeSelectorContent
|
|
2407
|
+
}),
|
|
2408
|
+
LinkSelector: BubbleMenuLinkSelector
|
|
2409
|
+
};
|
|
2410
|
+
|
|
1586
2411
|
//#endregion
|
|
1587
2412
|
exports.AlignmentAttribute = AlignmentAttribute;
|
|
1588
2413
|
exports.Body = Body;
|
|
1589
2414
|
exports.Bold = Bold;
|
|
2415
|
+
exports.BubbleMenu = BubbleMenu;
|
|
2416
|
+
exports.BubbleMenuAlignCenter = BubbleMenuAlignCenter;
|
|
2417
|
+
exports.BubbleMenuAlignLeft = BubbleMenuAlignLeft;
|
|
2418
|
+
exports.BubbleMenuAlignRight = BubbleMenuAlignRight;
|
|
2419
|
+
exports.BubbleMenuBold = BubbleMenuBold;
|
|
2420
|
+
exports.BubbleMenuCode = BubbleMenuCode;
|
|
2421
|
+
exports.BubbleMenuItalic = BubbleMenuItalic;
|
|
2422
|
+
exports.BubbleMenuItem = BubbleMenuItem;
|
|
2423
|
+
exports.BubbleMenuItemGroup = BubbleMenuItemGroup;
|
|
2424
|
+
exports.BubbleMenuLinkSelector = BubbleMenuLinkSelector;
|
|
2425
|
+
exports.BubbleMenuNodeSelector = BubbleMenuNodeSelector;
|
|
2426
|
+
exports.BubbleMenuRoot = BubbleMenuRoot;
|
|
2427
|
+
exports.BubbleMenuSeparator = BubbleMenuSeparator;
|
|
2428
|
+
exports.BubbleMenuStrike = BubbleMenuStrike;
|
|
2429
|
+
exports.BubbleMenuUnderline = BubbleMenuUnderline;
|
|
2430
|
+
exports.BubbleMenuUppercase = BubbleMenuUppercase;
|
|
1590
2431
|
exports.Button = Button;
|
|
1591
2432
|
exports.COLUMN_PARENT_TYPES = COLUMN_PARENT_TYPES;
|
|
1592
2433
|
exports.ClassAttribute = ClassAttribute;
|
|
@@ -1597,6 +2438,9 @@ exports.EmailNode = EmailNode;
|
|
|
1597
2438
|
exports.FourColumns = FourColumns;
|
|
1598
2439
|
exports.MAX_COLUMNS_DEPTH = MAX_COLUMNS_DEPTH;
|
|
1599
2440
|
exports.MaxNesting = MaxNesting;
|
|
2441
|
+
exports.NodeSelectorContent = NodeSelectorContent;
|
|
2442
|
+
exports.NodeSelectorRoot = NodeSelectorRoot;
|
|
2443
|
+
exports.NodeSelectorTrigger = NodeSelectorTrigger;
|
|
1600
2444
|
exports.Placeholder = Placeholder;
|
|
1601
2445
|
exports.PreservedStyle = PreservedStyle;
|
|
1602
2446
|
exports.PreviewText = PreviewText;
|
|
@@ -1609,5 +2453,9 @@ exports.TableHeader = TableHeader;
|
|
|
1609
2453
|
exports.TableRow = TableRow;
|
|
1610
2454
|
exports.ThreeColumns = ThreeColumns;
|
|
1611
2455
|
exports.TwoColumns = TwoColumns;
|
|
2456
|
+
exports.Uppercase = Uppercase;
|
|
2457
|
+
exports.coreExtensions = coreExtensions;
|
|
2458
|
+
exports.editorEventBus = editorEventBus;
|
|
1612
2459
|
exports.getColumnsDepth = getColumnsDepth;
|
|
1613
|
-
exports.processStylesForUnlink = processStylesForUnlink;
|
|
2460
|
+
exports.processStylesForUnlink = processStylesForUnlink;
|
|
2461
|
+
exports.setTextAlignment = setTextAlignment;
|