@farmzone/fz-react-ui 1.1.0 → 1.1.2
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/README.md +85 -58
- package/dist/index.cjs +1021 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +134 -1
- package/dist/index.d.ts +134 -1
- package/dist/index.js +1017 -3
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +18 -2
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,16 @@ var zustand = require('zustand');
|
|
|
30
30
|
var middleware = require('zustand/middleware');
|
|
31
31
|
var uuid = require('uuid');
|
|
32
32
|
var heic2any = require('heic2any');
|
|
33
|
+
var react = require('@tiptap/react');
|
|
34
|
+
var core = require('@tiptap/core');
|
|
35
|
+
var StarterKit = require('@tiptap/starter-kit');
|
|
36
|
+
var TextAlign = require('@tiptap/extension-text-align');
|
|
37
|
+
var extensionImage = require('@tiptap/extension-image');
|
|
38
|
+
var extensionTextStyle = require('@tiptap/extension-text-style');
|
|
39
|
+
var extensionHeading = require('@tiptap/extension-heading');
|
|
40
|
+
var extensionPlaceholder = require('@tiptap/extension-placeholder');
|
|
41
|
+
var Mention = require('@tiptap/extension-mention');
|
|
42
|
+
var extensionTable = require('@tiptap/extension-table');
|
|
33
43
|
|
|
34
44
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
35
45
|
|
|
@@ -63,6 +73,9 @@ var useEmblaCarousel__default = /*#__PURE__*/_interopDefault(useEmblaCarousel);
|
|
|
63
73
|
var dayjs__default = /*#__PURE__*/_interopDefault(dayjs);
|
|
64
74
|
var customParseFormat__default = /*#__PURE__*/_interopDefault(customParseFormat);
|
|
65
75
|
var heic2any__default = /*#__PURE__*/_interopDefault(heic2any);
|
|
76
|
+
var StarterKit__default = /*#__PURE__*/_interopDefault(StarterKit);
|
|
77
|
+
var TextAlign__default = /*#__PURE__*/_interopDefault(TextAlign);
|
|
78
|
+
var Mention__default = /*#__PURE__*/_interopDefault(Mention);
|
|
66
79
|
|
|
67
80
|
// src/lib/variants.ts
|
|
68
81
|
function cn(...inputs) {
|
|
@@ -9597,6 +9610,1010 @@ function Sidebar(props) {
|
|
|
9597
9610
|
}
|
|
9598
9611
|
);
|
|
9599
9612
|
}
|
|
9613
|
+
var LIGHT = {
|
|
9614
|
+
toolbar: {
|
|
9615
|
+
wrapper: "border-b border-gray-200 bg-white",
|
|
9616
|
+
btnIdle: "text-gray-700 hover:bg-gray-100",
|
|
9617
|
+
btnActive: "bg-indigo-100 text-indigo-600",
|
|
9618
|
+
btnDestructive: "text-red-400 hover:bg-red-50 hover:text-red-600",
|
|
9619
|
+
divider: "bg-gray-200",
|
|
9620
|
+
rText: "text-gray-700"
|
|
9621
|
+
},
|
|
9622
|
+
fileList: {
|
|
9623
|
+
wrapper: "border-t border-gray-100",
|
|
9624
|
+
label: "text-gray-400",
|
|
9625
|
+
item: "border border-gray-200 bg-white",
|
|
9626
|
+
itemName: "text-gray-700",
|
|
9627
|
+
itemSize: "text-gray-400",
|
|
9628
|
+
removeBtn: "text-gray-400 hover:bg-gray-100 hover:text-gray-600"
|
|
9629
|
+
},
|
|
9630
|
+
dropZone: {
|
|
9631
|
+
overlay: "border-blue-400 bg-blue-50/80 text-blue-600",
|
|
9632
|
+
subText: "text-blue-400"
|
|
9633
|
+
},
|
|
9634
|
+
mention: {
|
|
9635
|
+
container: "border-gray-200 bg-white",
|
|
9636
|
+
emptyText: "text-gray-400",
|
|
9637
|
+
btnIdle: "text-gray-700 hover:bg-gray-100 hover:text-gray-900",
|
|
9638
|
+
btnSelected: "bg-blue-100 text-blue-900",
|
|
9639
|
+
badgeIdle: "bg-gray-100 text-gray-500",
|
|
9640
|
+
badgeSelected: "bg-blue-200 text-blue-800"
|
|
9641
|
+
}
|
|
9642
|
+
};
|
|
9643
|
+
var DARK = {
|
|
9644
|
+
toolbar: {
|
|
9645
|
+
wrapper: "border-b border-gray-800 bg-gray-900",
|
|
9646
|
+
btnIdle: "text-gray-400 hover:bg-gray-800 hover:text-gray-200",
|
|
9647
|
+
btnActive: "bg-indigo-900/50 text-indigo-400",
|
|
9648
|
+
btnDestructive: "text-red-400 hover:bg-red-900/30 hover:text-red-300",
|
|
9649
|
+
divider: "bg-gray-800",
|
|
9650
|
+
rText: "text-gray-400"
|
|
9651
|
+
},
|
|
9652
|
+
fileList: {
|
|
9653
|
+
wrapper: "border-t border-gray-800",
|
|
9654
|
+
label: "text-gray-500",
|
|
9655
|
+
item: "border border-gray-700 bg-gray-900",
|
|
9656
|
+
itemName: "text-gray-300",
|
|
9657
|
+
itemSize: "text-gray-500",
|
|
9658
|
+
removeBtn: "text-gray-500 hover:bg-gray-800 hover:text-gray-300"
|
|
9659
|
+
},
|
|
9660
|
+
dropZone: {
|
|
9661
|
+
overlay: "border-blue-500 bg-blue-950/80 text-blue-400",
|
|
9662
|
+
subText: "text-blue-500"
|
|
9663
|
+
},
|
|
9664
|
+
mention: {
|
|
9665
|
+
container: "border-gray-700 bg-gray-900",
|
|
9666
|
+
emptyText: "text-gray-500",
|
|
9667
|
+
btnIdle: "text-gray-300 hover:bg-gray-800 hover:text-gray-100",
|
|
9668
|
+
btnSelected: "bg-blue-900/50 text-blue-200",
|
|
9669
|
+
badgeIdle: "bg-gray-800 text-gray-400",
|
|
9670
|
+
badgeSelected: "bg-blue-800 text-blue-200"
|
|
9671
|
+
}
|
|
9672
|
+
};
|
|
9673
|
+
var RICH_EDITOR_THEMES = {
|
|
9674
|
+
light: LIGHT,
|
|
9675
|
+
dark: DARK
|
|
9676
|
+
};
|
|
9677
|
+
var RichEditorThemeContext = React6.createContext(LIGHT);
|
|
9678
|
+
var RichEditorThemeProvider = RichEditorThemeContext.Provider;
|
|
9679
|
+
function useRichEditorTheme() {
|
|
9680
|
+
return React6.useContext(RichEditorThemeContext);
|
|
9681
|
+
}
|
|
9682
|
+
var MentionList = React6.forwardRef((props, ref) => {
|
|
9683
|
+
const { items, command, loading, className } = props;
|
|
9684
|
+
const theme = useRichEditorTheme();
|
|
9685
|
+
const [selectedIndex, setSelectedIndex] = React6.useState(0);
|
|
9686
|
+
const selectedRef = React6.useRef(null);
|
|
9687
|
+
const selectItem = (index) => {
|
|
9688
|
+
const item = items[index];
|
|
9689
|
+
if (item) {
|
|
9690
|
+
command({ id: item.id, label: item.label });
|
|
9691
|
+
}
|
|
9692
|
+
};
|
|
9693
|
+
React6.useEffect(() => setSelectedIndex(0), [items]);
|
|
9694
|
+
React6.useEffect(() => {
|
|
9695
|
+
selectedRef.current?.scrollIntoView({ block: "nearest" });
|
|
9696
|
+
}, [selectedIndex]);
|
|
9697
|
+
React6.useImperativeHandle(ref, () => ({
|
|
9698
|
+
onKeyDown: ({ event }) => {
|
|
9699
|
+
if (event.key === "ArrowUp") {
|
|
9700
|
+
setSelectedIndex((i) => (i + items.length - 1) % Math.max(items.length, 1));
|
|
9701
|
+
return true;
|
|
9702
|
+
}
|
|
9703
|
+
if (event.key === "ArrowDown") {
|
|
9704
|
+
setSelectedIndex((i) => (i + 1) % Math.max(items.length, 1));
|
|
9705
|
+
return true;
|
|
9706
|
+
}
|
|
9707
|
+
if (event.key === "Enter") {
|
|
9708
|
+
selectItem(selectedIndex);
|
|
9709
|
+
return true;
|
|
9710
|
+
}
|
|
9711
|
+
return false;
|
|
9712
|
+
}
|
|
9713
|
+
}));
|
|
9714
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("rich-editor-mention-list z-50 overflow-hidden rounded-md border shadow-md", theme.mention.container, className), children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("px-3 py-2 text-sm", theme.mention.emptyText), children: "\uAC80\uC0C9 \uC911..." }) : items.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("px-3 py-2 text-sm", theme.mention.emptyText), children: "\uAC80\uC0C9 \uACB0\uACFC \uC5C6\uC74C" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-48 overflow-y-auto p-1", children: items.map((item, index) => {
|
|
9715
|
+
const isSelected = index === selectedIndex;
|
|
9716
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9717
|
+
"button",
|
|
9718
|
+
{
|
|
9719
|
+
ref: isSelected ? selectedRef : null,
|
|
9720
|
+
type: "button",
|
|
9721
|
+
onClick: () => selectItem(index),
|
|
9722
|
+
className: cn(
|
|
9723
|
+
"flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pl-2 pr-4 text-sm text-left select-none transition-colors",
|
|
9724
|
+
isSelected ? theme.mention.btnSelected : theme.mention.btnIdle
|
|
9725
|
+
),
|
|
9726
|
+
children: [
|
|
9727
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9728
|
+
"span",
|
|
9729
|
+
{
|
|
9730
|
+
className: cn(
|
|
9731
|
+
"flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-[11px] font-semibold",
|
|
9732
|
+
isSelected ? theme.mention.badgeSelected : theme.mention.badgeIdle
|
|
9733
|
+
),
|
|
9734
|
+
children: item.label[0]?.toUpperCase()
|
|
9735
|
+
}
|
|
9736
|
+
),
|
|
9737
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: item.label })
|
|
9738
|
+
]
|
|
9739
|
+
},
|
|
9740
|
+
item.id
|
|
9741
|
+
);
|
|
9742
|
+
}) }) });
|
|
9743
|
+
});
|
|
9744
|
+
MentionList.displayName = "MentionList";
|
|
9745
|
+
|
|
9746
|
+
// src/components/RichEditor/config/extensions.ts
|
|
9747
|
+
function buildExtensions(config) {
|
|
9748
|
+
const { toolbar, mentionUsers = [], onMentionSearch, mentionClassName } = config;
|
|
9749
|
+
const exts = [extensionTextStyle.TextStyleKit, buildMarkResetOnEnter()];
|
|
9750
|
+
const headingLevels = toolbar.heading === false ? [] : Array.isArray(toolbar.heading) ? [...toolbar.heading] : [1, 2, 3, 4];
|
|
9751
|
+
exts.push(
|
|
9752
|
+
StarterKit__default.default.configure({
|
|
9753
|
+
heading: false,
|
|
9754
|
+
bold: toolbar.bold !== false ? {} : false,
|
|
9755
|
+
italic: toolbar.italic !== false ? {} : false,
|
|
9756
|
+
strike: toolbar.strike !== false ? {} : false,
|
|
9757
|
+
code: toolbar.code !== false ? {} : false,
|
|
9758
|
+
bulletList: toolbar.bulletList !== false ? { keepMarks: true, keepAttributes: false } : false,
|
|
9759
|
+
orderedList: toolbar.orderedList !== false ? { keepMarks: true, keepAttributes: false } : false,
|
|
9760
|
+
blockquote: toolbar.blockquote !== false ? {} : false,
|
|
9761
|
+
codeBlock: toolbar.codeBlock !== false ? {
|
|
9762
|
+
HTMLAttributes: {
|
|
9763
|
+
class: "rounded-lg p-4 font-mono text-sm my-4"
|
|
9764
|
+
}
|
|
9765
|
+
} : false
|
|
9766
|
+
})
|
|
9767
|
+
);
|
|
9768
|
+
if (headingLevels.length > 0) {
|
|
9769
|
+
exts.push(extensionHeading.Heading.configure({ levels: headingLevels }));
|
|
9770
|
+
}
|
|
9771
|
+
if (toolbar.textAlign !== false) {
|
|
9772
|
+
exts.push(buildCustomTextAlign());
|
|
9773
|
+
}
|
|
9774
|
+
if (toolbar.image) {
|
|
9775
|
+
exts.push(buildCustomImage());
|
|
9776
|
+
}
|
|
9777
|
+
if (toolbar.placeholder !== void 0) {
|
|
9778
|
+
exts.push(
|
|
9779
|
+
extensionPlaceholder.Placeholder.configure({
|
|
9780
|
+
placeholder: toolbar.placeholder,
|
|
9781
|
+
showOnlyWhenEditable: true
|
|
9782
|
+
})
|
|
9783
|
+
);
|
|
9784
|
+
}
|
|
9785
|
+
if (toolbar.mention) {
|
|
9786
|
+
const ALL_ITEM = { id: "__all__", label: "\uBAA8\uB450" };
|
|
9787
|
+
exts.push(
|
|
9788
|
+
Mention__default.default.configure({
|
|
9789
|
+
HTMLAttributes: { class: "rich-editor-mention" },
|
|
9790
|
+
suggestion: {
|
|
9791
|
+
items: async ({ query }) => {
|
|
9792
|
+
let items;
|
|
9793
|
+
if (onMentionSearch) {
|
|
9794
|
+
items = await onMentionSearch(query);
|
|
9795
|
+
} else {
|
|
9796
|
+
items = mentionUsers.filter(
|
|
9797
|
+
(u) => u.label.toLowerCase().includes(query.toLowerCase())
|
|
9798
|
+
);
|
|
9799
|
+
}
|
|
9800
|
+
return toolbar.mentionAll ? [ALL_ITEM, ...items] : items;
|
|
9801
|
+
},
|
|
9802
|
+
render: () => {
|
|
9803
|
+
let component;
|
|
9804
|
+
let unmount;
|
|
9805
|
+
return {
|
|
9806
|
+
onStart: (props) => {
|
|
9807
|
+
component = new react.ReactRenderer(MentionList, {
|
|
9808
|
+
props: mentionClassName ? { ...props, className: mentionClassName } : props,
|
|
9809
|
+
editor: props.editor
|
|
9810
|
+
});
|
|
9811
|
+
unmount = props.mount(component.element);
|
|
9812
|
+
},
|
|
9813
|
+
onUpdate: (props) => {
|
|
9814
|
+
component.updateProps(
|
|
9815
|
+
mentionClassName ? { ...props, className: mentionClassName } : props
|
|
9816
|
+
);
|
|
9817
|
+
},
|
|
9818
|
+
onKeyDown: ({ event }) => {
|
|
9819
|
+
return component.ref?.onKeyDown({ event }) ?? false;
|
|
9820
|
+
},
|
|
9821
|
+
onExit: () => {
|
|
9822
|
+
unmount?.();
|
|
9823
|
+
component.destroy();
|
|
9824
|
+
}
|
|
9825
|
+
};
|
|
9826
|
+
}
|
|
9827
|
+
}
|
|
9828
|
+
})
|
|
9829
|
+
);
|
|
9830
|
+
}
|
|
9831
|
+
if (toolbar.table) {
|
|
9832
|
+
exts.push(
|
|
9833
|
+
extensionTable.Table.configure({ resizable: true }),
|
|
9834
|
+
extensionTable.TableRow,
|
|
9835
|
+
extensionTable.TableHeader,
|
|
9836
|
+
extensionTable.TableCell
|
|
9837
|
+
);
|
|
9838
|
+
}
|
|
9839
|
+
return exts;
|
|
9840
|
+
}
|
|
9841
|
+
function buildCustomTextAlign() {
|
|
9842
|
+
const CustomTextAlign = TextAlign__default.default.extend({
|
|
9843
|
+
addKeyboardShortcuts() {
|
|
9844
|
+
return {
|
|
9845
|
+
Enter: ({ editor }) => {
|
|
9846
|
+
const isAligned = editor.isActive({ textAlign: "center" }) || editor.isActive({ textAlign: "right" });
|
|
9847
|
+
if (!isAligned) return false;
|
|
9848
|
+
const {
|
|
9849
|
+
state: {
|
|
9850
|
+
selection: { $from }
|
|
9851
|
+
}
|
|
9852
|
+
} = editor;
|
|
9853
|
+
const currentNode = $from.node($from.depth);
|
|
9854
|
+
if (currentNode.textContent.trim() === "") {
|
|
9855
|
+
editor.chain().focus().setTextAlign("left").run();
|
|
9856
|
+
return true;
|
|
9857
|
+
}
|
|
9858
|
+
return false;
|
|
9859
|
+
}
|
|
9860
|
+
};
|
|
9861
|
+
}
|
|
9862
|
+
});
|
|
9863
|
+
return CustomTextAlign.configure({
|
|
9864
|
+
types: ["heading", "paragraph"],
|
|
9865
|
+
alignments: ["left", "center", "right"],
|
|
9866
|
+
defaultAlignment: "left"
|
|
9867
|
+
});
|
|
9868
|
+
}
|
|
9869
|
+
function buildCustomImage() {
|
|
9870
|
+
const CustomImage = extensionImage.Image.extend({
|
|
9871
|
+
addAttributes() {
|
|
9872
|
+
return {
|
|
9873
|
+
...this.parent?.(),
|
|
9874
|
+
width: {
|
|
9875
|
+
default: null,
|
|
9876
|
+
renderHTML: (attrs) => attrs.width ? { width: attrs.width } : {}
|
|
9877
|
+
},
|
|
9878
|
+
height: {
|
|
9879
|
+
default: null,
|
|
9880
|
+
renderHTML: (attrs) => attrs.height ? { height: attrs.height } : {}
|
|
9881
|
+
},
|
|
9882
|
+
style: {
|
|
9883
|
+
default: null,
|
|
9884
|
+
renderHTML: (attrs) => attrs.style ? { style: attrs.style } : {}
|
|
9885
|
+
}
|
|
9886
|
+
};
|
|
9887
|
+
}
|
|
9888
|
+
});
|
|
9889
|
+
return CustomImage.configure({
|
|
9890
|
+
HTMLAttributes: { class: "max-w-full h-auto rounded-lg my-4" },
|
|
9891
|
+
inline: false,
|
|
9892
|
+
allowBase64: true
|
|
9893
|
+
});
|
|
9894
|
+
}
|
|
9895
|
+
function buildMarkResetOnEnter() {
|
|
9896
|
+
return core.Extension.create({
|
|
9897
|
+
name: "markResetOnEnter",
|
|
9898
|
+
priority: 200,
|
|
9899
|
+
addKeyboardShortcuts() {
|
|
9900
|
+
return {
|
|
9901
|
+
Enter: ({ editor }) => {
|
|
9902
|
+
if (editor.isActive("heading") || editor.isActive("bulletList") || editor.isActive("orderedList") || editor.isActive("blockquote") || editor.isActive("codeBlock")) {
|
|
9903
|
+
return false;
|
|
9904
|
+
}
|
|
9905
|
+
const { $from } = editor.state.selection;
|
|
9906
|
+
const currentNode = $from.node($from.depth);
|
|
9907
|
+
if (currentNode.textContent.trim() === "") return false;
|
|
9908
|
+
const hasMarks = editor.isActive("bold") || editor.isActive("italic") || editor.isActive("strike") || editor.isActive("code");
|
|
9909
|
+
const hasNonDefaultAlign = editor.isActive({ textAlign: "center" }) || editor.isActive({ textAlign: "right" });
|
|
9910
|
+
if (!hasMarks && !hasNonDefaultAlign) return false;
|
|
9911
|
+
editor.chain().splitBlock().unsetAllMarks().setTextAlign("left").run();
|
|
9912
|
+
return true;
|
|
9913
|
+
}
|
|
9914
|
+
};
|
|
9915
|
+
}
|
|
9916
|
+
});
|
|
9917
|
+
}
|
|
9918
|
+
|
|
9919
|
+
// src/components/RichEditor/config/keyboardHandlers.ts
|
|
9920
|
+
function createKeyboardHandler(codeBlockEmptyEnterRef, onSubmitRef) {
|
|
9921
|
+
return (view, event) => {
|
|
9922
|
+
const { editor } = view;
|
|
9923
|
+
if (event.key === "Enter" && (event.ctrlKey || event.metaKey)) {
|
|
9924
|
+
onSubmitRef?.current?.();
|
|
9925
|
+
return true;
|
|
9926
|
+
}
|
|
9927
|
+
if (event.key !== "Enter") return false;
|
|
9928
|
+
if (!editor) return false;
|
|
9929
|
+
const { state } = view;
|
|
9930
|
+
const { selection } = state;
|
|
9931
|
+
const { $from } = selection;
|
|
9932
|
+
if (editor.isActive("heading")) {
|
|
9933
|
+
event.preventDefault();
|
|
9934
|
+
editor.chain().focus().splitBlock().setParagraph().run();
|
|
9935
|
+
return true;
|
|
9936
|
+
}
|
|
9937
|
+
if (editor.isActive("blockquote")) {
|
|
9938
|
+
let blockquoteNode = null;
|
|
9939
|
+
for (let i = $from.depth; i >= 0; i--) {
|
|
9940
|
+
if ($from.node(i).type.name === "blockquote") {
|
|
9941
|
+
blockquoteNode = $from.node(i);
|
|
9942
|
+
break;
|
|
9943
|
+
}
|
|
9944
|
+
}
|
|
9945
|
+
const currentText = blockquoteNode?.textContent.trim() || "";
|
|
9946
|
+
if (currentText === "") {
|
|
9947
|
+
event.preventDefault();
|
|
9948
|
+
editor.chain().focus().toggleBlockquote().setParagraph().run();
|
|
9949
|
+
return true;
|
|
9950
|
+
}
|
|
9951
|
+
}
|
|
9952
|
+
if (editor.isActive("codeBlock")) {
|
|
9953
|
+
let codeBlockNode = null;
|
|
9954
|
+
for (let i = $from.depth; i >= 0; i--) {
|
|
9955
|
+
const node = $from.node(i);
|
|
9956
|
+
if (node.type.name === "codeBlock") {
|
|
9957
|
+
codeBlockNode = node;
|
|
9958
|
+
break;
|
|
9959
|
+
}
|
|
9960
|
+
}
|
|
9961
|
+
if (codeBlockNode) {
|
|
9962
|
+
const codeBlockText = codeBlockNode.textContent.trim();
|
|
9963
|
+
if (codeBlockText === "") {
|
|
9964
|
+
if (codeBlockEmptyEnterRef.current) {
|
|
9965
|
+
event.preventDefault();
|
|
9966
|
+
codeBlockEmptyEnterRef.current = false;
|
|
9967
|
+
editor.chain().focus().toggleCodeBlock().setParagraph().run();
|
|
9968
|
+
return true;
|
|
9969
|
+
}
|
|
9970
|
+
codeBlockEmptyEnterRef.current = true;
|
|
9971
|
+
return false;
|
|
9972
|
+
}
|
|
9973
|
+
codeBlockEmptyEnterRef.current = false;
|
|
9974
|
+
}
|
|
9975
|
+
} else {
|
|
9976
|
+
codeBlockEmptyEnterRef.current = false;
|
|
9977
|
+
}
|
|
9978
|
+
if (editor.isActive("bulletList")) {
|
|
9979
|
+
const currentNode = $from.node($from.depth);
|
|
9980
|
+
if (currentNode.textContent.trim() === "") {
|
|
9981
|
+
event.preventDefault();
|
|
9982
|
+
editor.chain().focus().lift("bulletList").setParagraph().run();
|
|
9983
|
+
return true;
|
|
9984
|
+
}
|
|
9985
|
+
}
|
|
9986
|
+
if (editor.isActive("orderedList")) {
|
|
9987
|
+
const currentNode = $from.node($from.depth);
|
|
9988
|
+
if (currentNode.textContent.trim() === "") {
|
|
9989
|
+
event.preventDefault();
|
|
9990
|
+
editor.chain().focus().lift("orderedList").setParagraph().run();
|
|
9991
|
+
return true;
|
|
9992
|
+
}
|
|
9993
|
+
}
|
|
9994
|
+
return false;
|
|
9995
|
+
};
|
|
9996
|
+
}
|
|
9997
|
+
|
|
9998
|
+
// src/components/RichEditor/hooks/useRichEditor.ts
|
|
9999
|
+
var DEFAULT_EDITOR_CLASS = "focus:outline-none";
|
|
10000
|
+
function useRichEditor(options = {}) {
|
|
10001
|
+
const {
|
|
10002
|
+
defaultValue,
|
|
10003
|
+
toolbar = {},
|
|
10004
|
+
mentionUsers = [],
|
|
10005
|
+
onMentionSearch,
|
|
10006
|
+
mentionClassName,
|
|
10007
|
+
editable = true,
|
|
10008
|
+
autofocus,
|
|
10009
|
+
editorClassName,
|
|
10010
|
+
additionalExtensions = [],
|
|
10011
|
+
onChange,
|
|
10012
|
+
onSubmit
|
|
10013
|
+
} = options;
|
|
10014
|
+
const codeBlockEmptyEnterRef = React6.useRef(false);
|
|
10015
|
+
const onChangeRef = React6.useRef(onChange);
|
|
10016
|
+
onChangeRef.current = onChange;
|
|
10017
|
+
const onSubmitRef = React6.useRef(onSubmit);
|
|
10018
|
+
onSubmitRef.current = onSubmit;
|
|
10019
|
+
const extensions = [
|
|
10020
|
+
...buildExtensions({
|
|
10021
|
+
toolbar,
|
|
10022
|
+
mentionUsers,
|
|
10023
|
+
onMentionSearch,
|
|
10024
|
+
mentionClassName
|
|
10025
|
+
}),
|
|
10026
|
+
...additionalExtensions
|
|
10027
|
+
];
|
|
10028
|
+
const editor = react.useEditor({
|
|
10029
|
+
extensions,
|
|
10030
|
+
autofocus: autofocus ?? false,
|
|
10031
|
+
editable,
|
|
10032
|
+
editorProps: {
|
|
10033
|
+
attributes: {
|
|
10034
|
+
class: cn(DEFAULT_EDITOR_CLASS, editorClassName)
|
|
10035
|
+
},
|
|
10036
|
+
handleKeyDown: createKeyboardHandler(codeBlockEmptyEnterRef, onSubmitRef)
|
|
10037
|
+
},
|
|
10038
|
+
content: defaultValue ?? "",
|
|
10039
|
+
onUpdate: ({ editor: editor2 }) => {
|
|
10040
|
+
onChangeRef.current?.(editor2.getHTML());
|
|
10041
|
+
}
|
|
10042
|
+
});
|
|
10043
|
+
React6.useEffect(() => {
|
|
10044
|
+
if (editor) editor.setEditable(editable);
|
|
10045
|
+
}, [editor, editable]);
|
|
10046
|
+
return {
|
|
10047
|
+
editor,
|
|
10048
|
+
getHTML: () => editor?.getHTML() ?? "",
|
|
10049
|
+
getJSON: () => editor?.getJSON() ?? {},
|
|
10050
|
+
setContent: (html) => {
|
|
10051
|
+
editor?.commands.setContent(html);
|
|
10052
|
+
},
|
|
10053
|
+
clearContent: () => {
|
|
10054
|
+
editor?.commands.clearContent();
|
|
10055
|
+
},
|
|
10056
|
+
focus: () => {
|
|
10057
|
+
editor?.commands.focus();
|
|
10058
|
+
},
|
|
10059
|
+
isEmpty: editor?.isEmpty ?? true
|
|
10060
|
+
// attachments,
|
|
10061
|
+
// removeAttachment,
|
|
10062
|
+
};
|
|
10063
|
+
}
|
|
10064
|
+
var MAX_COLS = 8;
|
|
10065
|
+
var MAX_ROWS = 8;
|
|
10066
|
+
function TableGridPicker({ editor, onClose }) {
|
|
10067
|
+
const [hover, setHover] = React6.useState({ col: 0, row: 0 });
|
|
10068
|
+
const handleSelect = (col, row) => {
|
|
10069
|
+
editor.chain().focus().insertTable({ rows: row, cols: col, withHeaderRow: true }).run();
|
|
10070
|
+
onClose();
|
|
10071
|
+
};
|
|
10072
|
+
const cols = hover.col || 1;
|
|
10073
|
+
const rows = hover.row || 1;
|
|
10074
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-1.5", children: [
|
|
10075
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10076
|
+
"div",
|
|
10077
|
+
{
|
|
10078
|
+
className: "grid gap-1",
|
|
10079
|
+
style: { gridTemplateColumns: `repeat(${MAX_COLS}, 1fr)` },
|
|
10080
|
+
onMouseLeave: () => setHover({ col: 0, row: 0 }),
|
|
10081
|
+
children: Array.from(
|
|
10082
|
+
{ length: MAX_ROWS },
|
|
10083
|
+
(_, rowIdx) => Array.from({ length: MAX_COLS }, (_2, colIdx) => {
|
|
10084
|
+
const row = rowIdx + 1;
|
|
10085
|
+
const col = colIdx + 1;
|
|
10086
|
+
const isHighlighted = col <= hover.col && row <= hover.row;
|
|
10087
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10088
|
+
"button",
|
|
10089
|
+
{
|
|
10090
|
+
type: "button",
|
|
10091
|
+
"aria-label": `${col}\uC5F4 ${row}\uD589 \uD14C\uC774\uBE14`,
|
|
10092
|
+
className: cn(
|
|
10093
|
+
"size-4 rounded border transition-colors",
|
|
10094
|
+
isHighlighted ? "border-blue-400 bg-blue-100" : "border-gray-200 bg-gray-50"
|
|
10095
|
+
),
|
|
10096
|
+
onMouseEnter: () => setHover({ col, row }),
|
|
10097
|
+
onClick: () => handleSelect(col, row)
|
|
10098
|
+
},
|
|
10099
|
+
`${row}-${col}`
|
|
10100
|
+
);
|
|
10101
|
+
})
|
|
10102
|
+
)
|
|
10103
|
+
}
|
|
10104
|
+
),
|
|
10105
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex items-center justify-center gap-1.5 text-xs text-gray-500", children: [
|
|
10106
|
+
/* @__PURE__ */ jsxRuntime.jsx(ColIcon, {}),
|
|
10107
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-4 text-center font-medium text-gray-700", children: cols }),
|
|
10108
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400", children: "\xD7" }),
|
|
10109
|
+
/* @__PURE__ */ jsxRuntime.jsx(RowIcon, {}),
|
|
10110
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-4 text-center font-medium text-gray-700", children: rows })
|
|
10111
|
+
] })
|
|
10112
|
+
] });
|
|
10113
|
+
}
|
|
10114
|
+
function ColIcon() {
|
|
10115
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", className: "text-gray-400", children: [
|
|
10116
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "1", y: "1", width: "5", height: "12", rx: "1", fill: "currentColor", opacity: "0.3" }),
|
|
10117
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "8", y: "1", width: "5", height: "12", rx: "1", fill: "currentColor", opacity: "0.15" })
|
|
10118
|
+
] });
|
|
10119
|
+
}
|
|
10120
|
+
function RowIcon() {
|
|
10121
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", className: "text-gray-400", children: [
|
|
10122
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "1", y: "1", width: "12", height: "5", rx: "1", fill: "currentColor", opacity: "0.3" }),
|
|
10123
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "1", y: "8", width: "12", height: "5", rx: "1", fill: "currentColor", opacity: "0.15" })
|
|
10124
|
+
] });
|
|
10125
|
+
}
|
|
10126
|
+
function ToolbarButton(props) {
|
|
10127
|
+
const { onMouseDown, active, disabled, title, tooltip, variant = "default", children } = props;
|
|
10128
|
+
const theme = useRichEditorTheme();
|
|
10129
|
+
const btn = /* @__PURE__ */ jsxRuntime.jsx(
|
|
10130
|
+
"button",
|
|
10131
|
+
{
|
|
10132
|
+
type: "button",
|
|
10133
|
+
onMouseDown,
|
|
10134
|
+
disabled,
|
|
10135
|
+
"aria-label": title,
|
|
10136
|
+
className: cn(
|
|
10137
|
+
"flex h-8 w-8 cursor-pointer items-center justify-center rounded text-sm transition-colors",
|
|
10138
|
+
"disabled:cursor-not-allowed disabled:opacity-40",
|
|
10139
|
+
variant === "destructive" ? theme.toolbar.btnDestructive : active ? theme.toolbar.btnActive : theme.toolbar.btnIdle
|
|
10140
|
+
),
|
|
10141
|
+
children
|
|
10142
|
+
}
|
|
10143
|
+
);
|
|
10144
|
+
if (!tooltip) return btn;
|
|
10145
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Tooltip2, { content: tooltip, position: "bottom", asChild: true, children: btn });
|
|
10146
|
+
}
|
|
10147
|
+
function Divider() {
|
|
10148
|
+
const theme = useRichEditorTheme();
|
|
10149
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("mx-1 h-6 w-px", theme.toolbar.divider) });
|
|
10150
|
+
}
|
|
10151
|
+
function cmd(fn) {
|
|
10152
|
+
return (e) => {
|
|
10153
|
+
e.preventDefault();
|
|
10154
|
+
fn();
|
|
10155
|
+
};
|
|
10156
|
+
}
|
|
10157
|
+
function RichEditorToolbar(props) {
|
|
10158
|
+
const { editor, toolbar = {}, className } = props;
|
|
10159
|
+
const theme = useRichEditorTheme();
|
|
10160
|
+
const [tablePickerOpen, setTablePickerOpen] = React6.useState(false);
|
|
10161
|
+
const stateOrNull = react.useEditorState({
|
|
10162
|
+
editor,
|
|
10163
|
+
selector: ({ editor: e }) => ({
|
|
10164
|
+
isH1: e?.isActive("heading", { level: 1 }) ?? false,
|
|
10165
|
+
isH2: e?.isActive("heading", { level: 2 }) ?? false,
|
|
10166
|
+
isH3: e?.isActive("heading", { level: 3 }) ?? false,
|
|
10167
|
+
isH4: e?.isActive("heading", { level: 4 }) ?? false,
|
|
10168
|
+
isBold: e?.isActive("bold") ?? false,
|
|
10169
|
+
isItalic: e?.isActive("italic") ?? false,
|
|
10170
|
+
isStrike: e?.isActive("strike") ?? false,
|
|
10171
|
+
isCode: e?.isActive("code") ?? false,
|
|
10172
|
+
isAlignLeft: e?.isActive({ textAlign: "left" }) ?? false,
|
|
10173
|
+
isAlignCenter: e?.isActive({ textAlign: "center" }) ?? false,
|
|
10174
|
+
isAlignRight: e?.isActive({ textAlign: "right" }) ?? false,
|
|
10175
|
+
isBulletList: e?.isActive("bulletList") ?? false,
|
|
10176
|
+
isOrderedList: e?.isActive("orderedList") ?? false,
|
|
10177
|
+
isBlockquote: e?.isActive("blockquote") ?? false,
|
|
10178
|
+
isCodeBlock: e?.isActive("codeBlock") ?? false,
|
|
10179
|
+
isTable: e?.isActive("table") ?? false
|
|
10180
|
+
})
|
|
10181
|
+
});
|
|
10182
|
+
if (!editor) return null;
|
|
10183
|
+
const state = stateOrNull ?? {
|
|
10184
|
+
isH1: false,
|
|
10185
|
+
isH2: false,
|
|
10186
|
+
isH3: false,
|
|
10187
|
+
isH4: false,
|
|
10188
|
+
isBold: false,
|
|
10189
|
+
isItalic: false,
|
|
10190
|
+
isStrike: false,
|
|
10191
|
+
isCode: false,
|
|
10192
|
+
isAlignLeft: false,
|
|
10193
|
+
isAlignCenter: false,
|
|
10194
|
+
isAlignRight: false,
|
|
10195
|
+
isBulletList: false,
|
|
10196
|
+
isOrderedList: false,
|
|
10197
|
+
isBlockquote: false,
|
|
10198
|
+
isCodeBlock: false,
|
|
10199
|
+
isTable: false
|
|
10200
|
+
};
|
|
10201
|
+
const showHeadingGroup = toolbar.heading !== false && (Array.isArray(toolbar.heading) ? toolbar.heading.length > 0 : true);
|
|
10202
|
+
const showInlineGroup = toolbar.bold !== false || toolbar.italic !== false || toolbar.strike !== false || toolbar.code !== false;
|
|
10203
|
+
const showAlignGroup = toolbar.textAlign !== false;
|
|
10204
|
+
const showListGroup = toolbar.bulletList !== false || toolbar.orderedList !== false || toolbar.blockquote !== false || toolbar.codeBlock !== false;
|
|
10205
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10206
|
+
"div",
|
|
10207
|
+
{
|
|
10208
|
+
className: cn(
|
|
10209
|
+
"flex flex-wrap items-center gap-0.5 p-2",
|
|
10210
|
+
theme.toolbar.wrapper,
|
|
10211
|
+
className
|
|
10212
|
+
),
|
|
10213
|
+
children: [
|
|
10214
|
+
showHeadingGroup && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10215
|
+
(!Array.isArray(toolbar.heading) || toolbar.heading.includes(1)) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10216
|
+
ToolbarButton,
|
|
10217
|
+
{
|
|
10218
|
+
onMouseDown: cmd(
|
|
10219
|
+
() => editor.chain().focus().toggleHeading({ level: 1 }).run()
|
|
10220
|
+
),
|
|
10221
|
+
active: state.isH1,
|
|
10222
|
+
title: "\uC81C\uBAA9 1 (H1)",
|
|
10223
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Heading1, { size: 16 })
|
|
10224
|
+
}
|
|
10225
|
+
),
|
|
10226
|
+
(!Array.isArray(toolbar.heading) || toolbar.heading.includes(2)) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10227
|
+
ToolbarButton,
|
|
10228
|
+
{
|
|
10229
|
+
onMouseDown: cmd(
|
|
10230
|
+
() => editor.chain().focus().toggleHeading({ level: 2 }).run()
|
|
10231
|
+
),
|
|
10232
|
+
active: state.isH2,
|
|
10233
|
+
title: "\uC81C\uBAA9 2 (H2)",
|
|
10234
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Heading2, { size: 16 })
|
|
10235
|
+
}
|
|
10236
|
+
),
|
|
10237
|
+
(!Array.isArray(toolbar.heading) || toolbar.heading.includes(3)) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10238
|
+
ToolbarButton,
|
|
10239
|
+
{
|
|
10240
|
+
onMouseDown: cmd(
|
|
10241
|
+
() => editor.chain().focus().toggleHeading({ level: 3 }).run()
|
|
10242
|
+
),
|
|
10243
|
+
active: state.isH3,
|
|
10244
|
+
title: "\uC81C\uBAA9 3 (H3)",
|
|
10245
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Heading3, { size: 16 })
|
|
10246
|
+
}
|
|
10247
|
+
),
|
|
10248
|
+
(!Array.isArray(toolbar.heading) || toolbar.heading.includes(4)) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10249
|
+
ToolbarButton,
|
|
10250
|
+
{
|
|
10251
|
+
onMouseDown: cmd(
|
|
10252
|
+
() => editor.chain().focus().toggleHeading({ level: 4 }).run()
|
|
10253
|
+
),
|
|
10254
|
+
active: state.isH4,
|
|
10255
|
+
title: "\uC81C\uBAA9 4 (H4)",
|
|
10256
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Heading4, { size: 16 })
|
|
10257
|
+
}
|
|
10258
|
+
),
|
|
10259
|
+
showInlineGroup && /* @__PURE__ */ jsxRuntime.jsx(Divider, {})
|
|
10260
|
+
] }),
|
|
10261
|
+
toolbar.bold !== false && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10262
|
+
ToolbarButton,
|
|
10263
|
+
{
|
|
10264
|
+
onMouseDown: cmd(() => editor.chain().focus().toggleBold().run()),
|
|
10265
|
+
active: state.isBold,
|
|
10266
|
+
title: "\uAD75\uAC8C (Ctrl+B)",
|
|
10267
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Bold, { size: 16 })
|
|
10268
|
+
}
|
|
10269
|
+
),
|
|
10270
|
+
toolbar.italic !== false && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10271
|
+
ToolbarButton,
|
|
10272
|
+
{
|
|
10273
|
+
onMouseDown: cmd(() => editor.chain().focus().toggleItalic().run()),
|
|
10274
|
+
active: state.isItalic,
|
|
10275
|
+
title: "\uAE30\uC6B8\uC784 (Ctrl+I)",
|
|
10276
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Italic, { size: 16 })
|
|
10277
|
+
}
|
|
10278
|
+
),
|
|
10279
|
+
toolbar.strike !== false && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10280
|
+
ToolbarButton,
|
|
10281
|
+
{
|
|
10282
|
+
onMouseDown: cmd(() => editor.chain().focus().toggleStrike().run()),
|
|
10283
|
+
active: state.isStrike,
|
|
10284
|
+
title: "\uCDE8\uC18C\uC120",
|
|
10285
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Strikethrough, { size: 16 })
|
|
10286
|
+
}
|
|
10287
|
+
),
|
|
10288
|
+
toolbar.code !== false && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10289
|
+
ToolbarButton,
|
|
10290
|
+
{
|
|
10291
|
+
onMouseDown: cmd(() => editor.chain().focus().toggleCode().run()),
|
|
10292
|
+
active: state.isCode,
|
|
10293
|
+
title: "\uC778\uB77C\uC778 \uCF54\uB4DC",
|
|
10294
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code, { size: 16 })
|
|
10295
|
+
}
|
|
10296
|
+
),
|
|
10297
|
+
showAlignGroup && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10298
|
+
showInlineGroup && /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
|
|
10299
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10300
|
+
ToolbarButton,
|
|
10301
|
+
{
|
|
10302
|
+
onMouseDown: cmd(
|
|
10303
|
+
() => editor.chain().focus().setTextAlign("left").run()
|
|
10304
|
+
),
|
|
10305
|
+
active: state.isAlignLeft,
|
|
10306
|
+
title: "\uC67C\uCABD \uC815\uB82C",
|
|
10307
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlignLeft, { size: 16 })
|
|
10308
|
+
}
|
|
10309
|
+
),
|
|
10310
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10311
|
+
ToolbarButton,
|
|
10312
|
+
{
|
|
10313
|
+
onMouseDown: cmd(
|
|
10314
|
+
() => editor.chain().focus().setTextAlign("center").run()
|
|
10315
|
+
),
|
|
10316
|
+
active: state.isAlignCenter,
|
|
10317
|
+
title: "\uAC00\uC6B4\uB370 \uC815\uB82C",
|
|
10318
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlignCenter, { size: 16 })
|
|
10319
|
+
}
|
|
10320
|
+
),
|
|
10321
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10322
|
+
ToolbarButton,
|
|
10323
|
+
{
|
|
10324
|
+
onMouseDown: cmd(
|
|
10325
|
+
() => editor.chain().focus().setTextAlign("right").run()
|
|
10326
|
+
),
|
|
10327
|
+
active: state.isAlignRight,
|
|
10328
|
+
title: "\uC624\uB978\uCABD \uC815\uB82C",
|
|
10329
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlignRight, { size: 16 })
|
|
10330
|
+
}
|
|
10331
|
+
)
|
|
10332
|
+
] }),
|
|
10333
|
+
showListGroup && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10334
|
+
/* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
|
|
10335
|
+
toolbar.bulletList !== false && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10336
|
+
ToolbarButton,
|
|
10337
|
+
{
|
|
10338
|
+
onMouseDown: cmd(
|
|
10339
|
+
() => editor.chain().focus().toggleBulletList().run()
|
|
10340
|
+
),
|
|
10341
|
+
active: state.isBulletList,
|
|
10342
|
+
title: "\uAE00\uBA38\uB9AC \uAE30\uD638",
|
|
10343
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.List, { size: 16 })
|
|
10344
|
+
}
|
|
10345
|
+
),
|
|
10346
|
+
toolbar.orderedList !== false && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10347
|
+
ToolbarButton,
|
|
10348
|
+
{
|
|
10349
|
+
onMouseDown: cmd(
|
|
10350
|
+
() => editor.chain().focus().toggleOrderedList().run()
|
|
10351
|
+
),
|
|
10352
|
+
active: state.isOrderedList,
|
|
10353
|
+
title: "\uBC88\uD638 \uBAA9\uB85D",
|
|
10354
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ListOrdered, { size: 16 })
|
|
10355
|
+
}
|
|
10356
|
+
),
|
|
10357
|
+
toolbar.blockquote !== false && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10358
|
+
ToolbarButton,
|
|
10359
|
+
{
|
|
10360
|
+
onMouseDown: cmd(
|
|
10361
|
+
() => editor.chain().focus().toggleBlockquote().run()
|
|
10362
|
+
),
|
|
10363
|
+
active: state.isBlockquote,
|
|
10364
|
+
title: "\uC778\uC6A9\uAD6C",
|
|
10365
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Quote, { size: 16 })
|
|
10366
|
+
}
|
|
10367
|
+
),
|
|
10368
|
+
toolbar.codeBlock !== false && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10369
|
+
ToolbarButton,
|
|
10370
|
+
{
|
|
10371
|
+
onMouseDown: cmd(
|
|
10372
|
+
() => editor.chain().focus().toggleCodeBlock().run()
|
|
10373
|
+
),
|
|
10374
|
+
active: state.isCodeBlock,
|
|
10375
|
+
title: "\uCF54\uB4DC \uBE14\uB85D",
|
|
10376
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileCode, { size: 16 })
|
|
10377
|
+
}
|
|
10378
|
+
)
|
|
10379
|
+
] }),
|
|
10380
|
+
toolbar.table && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10381
|
+
/* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
|
|
10382
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: tablePickerOpen, onOpenChange: setTablePickerOpen, children: [
|
|
10383
|
+
/* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
10384
|
+
"button",
|
|
10385
|
+
{
|
|
10386
|
+
type: "button",
|
|
10387
|
+
"aria-label": "\uD14C\uC774\uBE14 \uC0BD\uC785",
|
|
10388
|
+
className: cn(
|
|
10389
|
+
"flex h-8 w-8 cursor-pointer items-center justify-center rounded text-sm transition-colors",
|
|
10390
|
+
"disabled:cursor-not-allowed disabled:opacity-40",
|
|
10391
|
+
state.isTable ? theme.toolbar.btnActive : theme.toolbar.btnIdle
|
|
10392
|
+
),
|
|
10393
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Table, { size: 16 })
|
|
10394
|
+
}
|
|
10395
|
+
) }),
|
|
10396
|
+
/* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-auto p-0", align: "start", position: "bottom", children: editor && /* @__PURE__ */ jsxRuntime.jsx(TableGridPicker, { editor, onClose: () => setTablePickerOpen(false) }) })
|
|
10397
|
+
] }),
|
|
10398
|
+
state.isTable && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10399
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10400
|
+
ToolbarButton,
|
|
10401
|
+
{
|
|
10402
|
+
onMouseDown: cmd(() => editor.chain().focus().addRowAfter().run()),
|
|
10403
|
+
title: "\uC544\uB798\uC5D0 \uD589 \uCD94\uAC00",
|
|
10404
|
+
tooltip: "\uD589 \uCD94\uAC00",
|
|
10405
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 14 })
|
|
10406
|
+
}
|
|
10407
|
+
),
|
|
10408
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10409
|
+
ToolbarButton,
|
|
10410
|
+
{
|
|
10411
|
+
onMouseDown: cmd(() => {
|
|
10412
|
+
editor.chain().focus().command(({ tr, dispatch, state: state2 }) => {
|
|
10413
|
+
const { $anchor } = state2.selection;
|
|
10414
|
+
let tableNode = null;
|
|
10415
|
+
let tableStart = 0;
|
|
10416
|
+
for (let depth = $anchor.depth; depth > 0; depth--) {
|
|
10417
|
+
const node = $anchor.node(depth);
|
|
10418
|
+
if (node.type.name === "table") {
|
|
10419
|
+
tableNode = node;
|
|
10420
|
+
tableStart = $anchor.start(depth);
|
|
10421
|
+
break;
|
|
10422
|
+
}
|
|
10423
|
+
}
|
|
10424
|
+
if (!tableNode) return false;
|
|
10425
|
+
let lastRowStart = 0;
|
|
10426
|
+
let lastRowSize = 0;
|
|
10427
|
+
let rowCount = 0;
|
|
10428
|
+
tableNode.forEach((child, offset) => {
|
|
10429
|
+
if (child.type.name !== "tableRow") return;
|
|
10430
|
+
rowCount++;
|
|
10431
|
+
lastRowStart = tableStart + offset;
|
|
10432
|
+
lastRowSize = child.nodeSize;
|
|
10433
|
+
});
|
|
10434
|
+
if (rowCount <= 1) return false;
|
|
10435
|
+
if (dispatch) {
|
|
10436
|
+
dispatch(tr.delete(lastRowStart, lastRowStart + lastRowSize));
|
|
10437
|
+
}
|
|
10438
|
+
return true;
|
|
10439
|
+
}).run();
|
|
10440
|
+
}),
|
|
10441
|
+
title: "\uB9C8\uC9C0\uB9C9 \uD589 \uC0AD\uC81C",
|
|
10442
|
+
tooltip: "\uD589 \uC0AD\uC81C",
|
|
10443
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Minus, { size: 14 })
|
|
10444
|
+
}
|
|
10445
|
+
),
|
|
10446
|
+
/* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
|
|
10447
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10448
|
+
ToolbarButton,
|
|
10449
|
+
{
|
|
10450
|
+
onMouseDown: cmd(() => editor.chain().focus().addColumnAfter().run()),
|
|
10451
|
+
title: "\uC624\uB978\uCABD\uC5D0 \uC5F4 \uCD94\uAC00",
|
|
10452
|
+
tooltip: "\uC5F4 \uCD94\uAC00",
|
|
10453
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("text-xs font-bold leading-none", theme.toolbar.rText), children: "R+" })
|
|
10454
|
+
}
|
|
10455
|
+
),
|
|
10456
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10457
|
+
ToolbarButton,
|
|
10458
|
+
{
|
|
10459
|
+
onMouseDown: cmd(() => editor.chain().focus().deleteColumn().run()),
|
|
10460
|
+
title: "\uD604\uC7AC \uC5F4 \uC0AD\uC81C",
|
|
10461
|
+
tooltip: "\uC5F4 \uC0AD\uC81C",
|
|
10462
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("text-xs font-bold leading-none", theme.toolbar.rText), children: "R-" })
|
|
10463
|
+
}
|
|
10464
|
+
),
|
|
10465
|
+
/* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
|
|
10466
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10467
|
+
ToolbarButton,
|
|
10468
|
+
{
|
|
10469
|
+
onMouseDown: cmd(() => editor.chain().focus().deleteTable().run()),
|
|
10470
|
+
title: "\uD14C\uC774\uBE14 \uC0AD\uC81C",
|
|
10471
|
+
tooltip: "\uD14C\uC774\uBE14 \uC0AD\uC81C",
|
|
10472
|
+
variant: "destructive",
|
|
10473
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 14 })
|
|
10474
|
+
}
|
|
10475
|
+
)
|
|
10476
|
+
] })
|
|
10477
|
+
] })
|
|
10478
|
+
]
|
|
10479
|
+
}
|
|
10480
|
+
);
|
|
10481
|
+
}
|
|
10482
|
+
var DEFAULT_TOOLBAR = {
|
|
10483
|
+
heading: [1, 2, 3, 4],
|
|
10484
|
+
bold: true,
|
|
10485
|
+
italic: true,
|
|
10486
|
+
strike: true,
|
|
10487
|
+
textAlign: true,
|
|
10488
|
+
bulletList: true,
|
|
10489
|
+
orderedList: true,
|
|
10490
|
+
blockquote: true,
|
|
10491
|
+
code: true
|
|
10492
|
+
};
|
|
10493
|
+
function RichEditorContent(props) {
|
|
10494
|
+
const { editor, className, editorClassName, minHeight, maxHeight } = props;
|
|
10495
|
+
const style = {};
|
|
10496
|
+
if (minHeight) style.minHeight = typeof minHeight === "number" ? `${minHeight}px` : minHeight;
|
|
10497
|
+
if (maxHeight) {
|
|
10498
|
+
style.maxHeight = typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight;
|
|
10499
|
+
style.overflowY = "auto";
|
|
10500
|
+
}
|
|
10501
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("rich-editor-content", className), style, children: /* @__PURE__ */ jsxRuntime.jsx(react.EditorContent, { editor, className: cn("h-full", editorClassName) }) });
|
|
10502
|
+
}
|
|
10503
|
+
var RichEditor = React6.forwardRef(function RichEditor2({
|
|
10504
|
+
value,
|
|
10505
|
+
defaultValue,
|
|
10506
|
+
onChange,
|
|
10507
|
+
toolbar = true,
|
|
10508
|
+
mentionUsers,
|
|
10509
|
+
onMentionSearch,
|
|
10510
|
+
additionalExtensions,
|
|
10511
|
+
onSubmit,
|
|
10512
|
+
theme = "light",
|
|
10513
|
+
placeholder,
|
|
10514
|
+
minHeight,
|
|
10515
|
+
maxHeight,
|
|
10516
|
+
className,
|
|
10517
|
+
bodyClassName,
|
|
10518
|
+
editorClassName,
|
|
10519
|
+
mentionClassName,
|
|
10520
|
+
themeTokens,
|
|
10521
|
+
editable = true,
|
|
10522
|
+
autofocus
|
|
10523
|
+
}, ref) {
|
|
10524
|
+
const showToolbar = toolbar !== false;
|
|
10525
|
+
const toolbarOptions = typeof toolbar === "object" ? toolbar : {};
|
|
10526
|
+
const activeToolbar = {
|
|
10527
|
+
...DEFAULT_TOOLBAR,
|
|
10528
|
+
...toolbarOptions,
|
|
10529
|
+
...placeholder !== void 0 && !toolbarOptions.placeholder ? { placeholder } : {}
|
|
10530
|
+
};
|
|
10531
|
+
const isInternalChangeRef = React6.useRef(false);
|
|
10532
|
+
const handleChange = React6.useCallback(
|
|
10533
|
+
(html) => {
|
|
10534
|
+
isInternalChangeRef.current = true;
|
|
10535
|
+
onChange?.(html);
|
|
10536
|
+
},
|
|
10537
|
+
[onChange]
|
|
10538
|
+
);
|
|
10539
|
+
const { editor } = useRichEditor({
|
|
10540
|
+
defaultValue: value ?? defaultValue,
|
|
10541
|
+
toolbar: activeToolbar,
|
|
10542
|
+
mentionUsers,
|
|
10543
|
+
onMentionSearch,
|
|
10544
|
+
mentionClassName,
|
|
10545
|
+
additionalExtensions,
|
|
10546
|
+
editable,
|
|
10547
|
+
autofocus,
|
|
10548
|
+
editorClassName,
|
|
10549
|
+
onChange: handleChange,
|
|
10550
|
+
onSubmit
|
|
10551
|
+
});
|
|
10552
|
+
React6.useEffect(() => {
|
|
10553
|
+
if (!editor || value === void 0) return;
|
|
10554
|
+
if (isInternalChangeRef.current) {
|
|
10555
|
+
isInternalChangeRef.current = false;
|
|
10556
|
+
return;
|
|
10557
|
+
}
|
|
10558
|
+
if (editor.getHTML() !== value) {
|
|
10559
|
+
editor.commands.setContent(value, { emitUpdate: false });
|
|
10560
|
+
}
|
|
10561
|
+
}, [value, editor]);
|
|
10562
|
+
React6.useImperativeHandle(
|
|
10563
|
+
ref,
|
|
10564
|
+
() => ({
|
|
10565
|
+
getHTML: () => editor?.getHTML() ?? "",
|
|
10566
|
+
getJSON: () => editor?.getJSON() ?? {},
|
|
10567
|
+
setContent: (html) => {
|
|
10568
|
+
editor?.commands.setContent(html);
|
|
10569
|
+
},
|
|
10570
|
+
clearContent: () => {
|
|
10571
|
+
editor?.commands.clearContent();
|
|
10572
|
+
},
|
|
10573
|
+
focus: () => {
|
|
10574
|
+
editor?.commands.focus();
|
|
10575
|
+
},
|
|
10576
|
+
get isEmpty() {
|
|
10577
|
+
return editor?.isEmpty ?? true;
|
|
10578
|
+
},
|
|
10579
|
+
editor
|
|
10580
|
+
}),
|
|
10581
|
+
[editor]
|
|
10582
|
+
);
|
|
10583
|
+
const isToolbarVisible = showToolbar && editable;
|
|
10584
|
+
const baseTheme = RICH_EDITOR_THEMES[theme];
|
|
10585
|
+
const resolvedTheme = themeTokens ? {
|
|
10586
|
+
...baseTheme,
|
|
10587
|
+
...themeTokens.toolbar && { toolbar: { ...baseTheme.toolbar, ...themeTokens.toolbar } },
|
|
10588
|
+
...themeTokens.mention && { mention: { ...baseTheme.mention, ...themeTokens.mention } }
|
|
10589
|
+
} : baseTheme;
|
|
10590
|
+
return /* @__PURE__ */ jsxRuntime.jsx(RichEditorThemeProvider, { value: resolvedTheme, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10591
|
+
"div",
|
|
10592
|
+
{
|
|
10593
|
+
className: cn("overflow-hidden rounded-lg border border-gray-200 shadow-sm flex flex-col relative", className),
|
|
10594
|
+
"data-rich-editor-theme": theme,
|
|
10595
|
+
children: [
|
|
10596
|
+
isToolbarVisible && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10597
|
+
RichEditorToolbar,
|
|
10598
|
+
{
|
|
10599
|
+
editor,
|
|
10600
|
+
toolbar: activeToolbar
|
|
10601
|
+
}
|
|
10602
|
+
),
|
|
10603
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10604
|
+
RichEditorContent,
|
|
10605
|
+
{
|
|
10606
|
+
editor,
|
|
10607
|
+
className: cn("p-4", bodyClassName),
|
|
10608
|
+
minHeight,
|
|
10609
|
+
maxHeight
|
|
10610
|
+
}
|
|
10611
|
+
)
|
|
10612
|
+
]
|
|
10613
|
+
}
|
|
10614
|
+
) });
|
|
10615
|
+
});
|
|
10616
|
+
var RichEditor_default = RichEditor;
|
|
9600
10617
|
function useDetailController(params = {}) {
|
|
9601
10618
|
const { mode: controlledMode, onModeChange } = params;
|
|
9602
10619
|
const [internalMode, setInternalMode] = React6.useState("read");
|
|
@@ -10394,6 +11411,9 @@ exports.PopoverTitle = PopoverTitle;
|
|
|
10394
11411
|
exports.PopoverTrigger = PopoverTrigger;
|
|
10395
11412
|
exports.Radio = Radio;
|
|
10396
11413
|
exports.RadioGroup = RadioGroup;
|
|
11414
|
+
exports.RichEditor = RichEditor_default;
|
|
11415
|
+
exports.RichEditorContent = RichEditorContent;
|
|
11416
|
+
exports.RichEditorToolbar = RichEditorToolbar;
|
|
10397
11417
|
exports.ScrollArea = ScrollArea_default;
|
|
10398
11418
|
exports.ScrollBar = ScrollBar;
|
|
10399
11419
|
exports.SearchAddress = SearchAddress;
|
|
@@ -10445,6 +11465,7 @@ exports.useFilePreviewViewer = useFilePreviewViewer;
|
|
|
10445
11465
|
exports.useModal = useModal;
|
|
10446
11466
|
exports.useMultiTabStore = useMultiTabStore;
|
|
10447
11467
|
exports.useParams = useParams;
|
|
11468
|
+
exports.useRichEditor = useRichEditor;
|
|
10448
11469
|
exports.useScrollToTop = useScrollToTop;
|
|
10449
11470
|
exports.useStableImageSrc = useStableImageSrc;
|
|
10450
11471
|
exports.useToast = useToast;
|