@lumir-company/editor 0.4.11 → 0.4.13
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 +1511 -1511
- package/dist/api/link-preview.js.map +1 -1
- package/dist/api/link-preview.mjs.map +1 -1
- package/dist/index.js +473 -70
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +482 -51
- package/dist/index.mjs.map +1 -1
- package/dist/style.css +40 -4
- package/package.json +94 -93
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/components/LumirEditor.tsx
|
|
4
|
-
import { useEffect as useEffect8, useMemo, useCallback as
|
|
4
|
+
import { useEffect as useEffect8, useMemo as useMemo3, useCallback as useCallback16, useState as useState8, useRef as useRef9 } from "react";
|
|
5
5
|
import {
|
|
6
6
|
useCreateBlockNote,
|
|
7
7
|
SideMenu as BlockSideMenu,
|
|
@@ -10,8 +10,9 @@ import {
|
|
|
10
10
|
SuggestionMenuController,
|
|
11
11
|
getDefaultReactSlashMenuItems,
|
|
12
12
|
LinkToolbarController,
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
FormattingToolbarController,
|
|
14
|
+
useBlockNoteEditor as useBlockNoteEditor3,
|
|
15
|
+
useComponentsContext as useComponentsContext3,
|
|
15
16
|
EditLinkButton,
|
|
16
17
|
OpenLinkButton,
|
|
17
18
|
DeleteLinkButton
|
|
@@ -2810,6 +2811,426 @@ var LumirEditorError = class _LumirEditorError extends Error {
|
|
|
2810
2811
|
}
|
|
2811
2812
|
};
|
|
2812
2813
|
|
|
2814
|
+
// src/extensions/VerticalAlignmentExtension.ts
|
|
2815
|
+
import { Extension } from "@tiptap/core";
|
|
2816
|
+
var VerticalAlignmentExtension = Extension.create({
|
|
2817
|
+
name: "verticalAlignment",
|
|
2818
|
+
addGlobalAttributes() {
|
|
2819
|
+
return [
|
|
2820
|
+
{
|
|
2821
|
+
types: ["tableCell", "tableHeader"],
|
|
2822
|
+
attributes: {
|
|
2823
|
+
verticalAlignment: {
|
|
2824
|
+
default: "top",
|
|
2825
|
+
parseHTML: (element) => {
|
|
2826
|
+
return element.getAttribute("data-vertical-alignment") || "top";
|
|
2827
|
+
},
|
|
2828
|
+
renderHTML: (attributes) => {
|
|
2829
|
+
if (!attributes.verticalAlignment || attributes.verticalAlignment === "top") {
|
|
2830
|
+
return {};
|
|
2831
|
+
}
|
|
2832
|
+
return {
|
|
2833
|
+
"data-vertical-alignment": attributes.verticalAlignment
|
|
2834
|
+
};
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
];
|
|
2840
|
+
},
|
|
2841
|
+
addProseMirrorPlugins() {
|
|
2842
|
+
return [];
|
|
2843
|
+
}
|
|
2844
|
+
});
|
|
2845
|
+
|
|
2846
|
+
// src/components/CustomFormattingToolbar.tsx
|
|
2847
|
+
import {
|
|
2848
|
+
BasicTextStyleButton,
|
|
2849
|
+
BlockTypeSelect as BlockTypeSelect2,
|
|
2850
|
+
ColorStyleButton,
|
|
2851
|
+
CreateLinkButton,
|
|
2852
|
+
FileCaptionButton,
|
|
2853
|
+
FileDeleteButton,
|
|
2854
|
+
FileRenameButton,
|
|
2855
|
+
FileReplaceButton,
|
|
2856
|
+
FileDownloadButton,
|
|
2857
|
+
FilePreviewButton,
|
|
2858
|
+
FormattingToolbar,
|
|
2859
|
+
NestBlockButton,
|
|
2860
|
+
UnnestBlockButton,
|
|
2861
|
+
TableCellMergeButton
|
|
2862
|
+
} from "@blocknote/react";
|
|
2863
|
+
|
|
2864
|
+
// src/components/TextAlignButtonWithVA.tsx
|
|
2865
|
+
import {
|
|
2866
|
+
checkBlockHasDefaultProp,
|
|
2867
|
+
checkBlockTypeHasDefaultProp,
|
|
2868
|
+
mapTableCell
|
|
2869
|
+
} from "@blocknote/core";
|
|
2870
|
+
import { useCallback as useCallback14, useMemo } from "react";
|
|
2871
|
+
import {
|
|
2872
|
+
useComponentsContext,
|
|
2873
|
+
useBlockNoteEditor,
|
|
2874
|
+
useSelectedBlocks
|
|
2875
|
+
} from "@blocknote/react";
|
|
2876
|
+
|
|
2877
|
+
// src/utils/prosemirror-table-utils.ts
|
|
2878
|
+
function getSelectedCellPositions(editor) {
|
|
2879
|
+
const tiptap = editor._tiptapEditor;
|
|
2880
|
+
if (!tiptap) return [];
|
|
2881
|
+
const { state } = tiptap;
|
|
2882
|
+
const { selection } = state;
|
|
2883
|
+
if (typeof selection.forEachCell === "function") {
|
|
2884
|
+
const positions = [];
|
|
2885
|
+
selection.forEachCell((_node, pos) => {
|
|
2886
|
+
positions.push(pos);
|
|
2887
|
+
});
|
|
2888
|
+
return positions;
|
|
2889
|
+
}
|
|
2890
|
+
const $pos = selection.$from;
|
|
2891
|
+
for (let depth = $pos.depth; depth > 0; depth--) {
|
|
2892
|
+
const node = $pos.node(depth);
|
|
2893
|
+
if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
|
|
2894
|
+
return [$pos.before(depth)];
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
return [];
|
|
2898
|
+
}
|
|
2899
|
+
|
|
2900
|
+
// src/components/TextAlignButtonWithVA.tsx
|
|
2901
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
2902
|
+
var icons = {
|
|
2903
|
+
left: /* @__PURE__ */ jsx17("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx17("path", { d: "M15 15H3v2h12v-2zm0-8H3v2h12V7zM3 13h18v-2H3v2zm0 8h18v-2H3v2zM3 3v2h18V3H3z" }) }),
|
|
2904
|
+
center: /* @__PURE__ */ jsx17("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx17("path", { d: "M7 15v2h10v-2H7zm-4 6h18v-2H3v2zm0-8h18v-2H3v2zm4-6v2h10V7H7zM3 3v2h18V3H3z" }) }),
|
|
2905
|
+
right: /* @__PURE__ */ jsx17("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx17("path", { d: "M3 21h18v-2H3v2zm6-4h12v-2H9v2zm-6-4h18v-2H3v2zm6-4h12V7H9v2zM3 3v2h18V3H3z" }) }),
|
|
2906
|
+
justify: /* @__PURE__ */ jsx17("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx17("path", { d: "M3 21h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18V7H3v2zM3 3v2h18V3H3z" }) })
|
|
2907
|
+
};
|
|
2908
|
+
var tooltipMap = {
|
|
2909
|
+
left: "\uC67C\uCABD \uC815\uB82C",
|
|
2910
|
+
center: "\uAC00\uC6B4\uB370 \uC815\uB82C",
|
|
2911
|
+
right: "\uC624\uB978\uCABD \uC815\uB82C",
|
|
2912
|
+
justify: "\uC591\uCABD \uC815\uB82C"
|
|
2913
|
+
};
|
|
2914
|
+
var TextAlignButtonWithVA = (props) => {
|
|
2915
|
+
const Components = useComponentsContext();
|
|
2916
|
+
const editor = useBlockNoteEditor();
|
|
2917
|
+
const selectedBlocks = useSelectedBlocks(editor);
|
|
2918
|
+
const textAlignment = useMemo(() => {
|
|
2919
|
+
const block = selectedBlocks[0];
|
|
2920
|
+
if (checkBlockHasDefaultProp("textAlignment", block, editor)) {
|
|
2921
|
+
return block.props.textAlignment;
|
|
2922
|
+
}
|
|
2923
|
+
if (block.type === "table") {
|
|
2924
|
+
const cellSelection = editor.tableHandles?.getCellSelection();
|
|
2925
|
+
if (!cellSelection) {
|
|
2926
|
+
return;
|
|
2927
|
+
}
|
|
2928
|
+
const allCellsInTable = cellSelection.cells.map(
|
|
2929
|
+
({ row, col }) => mapTableCell(
|
|
2930
|
+
block.content.rows[row].cells[col]
|
|
2931
|
+
).props.textAlignment
|
|
2932
|
+
);
|
|
2933
|
+
const firstAlignment = allCellsInTable[0];
|
|
2934
|
+
if (allCellsInTable.every((alignment) => alignment === firstAlignment)) {
|
|
2935
|
+
return firstAlignment;
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
return;
|
|
2939
|
+
}, [editor, selectedBlocks]);
|
|
2940
|
+
const setTextAlignment = useCallback14(
|
|
2941
|
+
(newAlignment) => {
|
|
2942
|
+
editor.focus();
|
|
2943
|
+
for (const block of selectedBlocks) {
|
|
2944
|
+
if (block.type === "table") {
|
|
2945
|
+
const tiptap = editor._tiptapEditor;
|
|
2946
|
+
if (!tiptap) continue;
|
|
2947
|
+
const positions = getSelectedCellPositions(editor);
|
|
2948
|
+
if (positions.length === 0) continue;
|
|
2949
|
+
const { state } = tiptap;
|
|
2950
|
+
let tr = state.tr;
|
|
2951
|
+
for (const pos of positions) {
|
|
2952
|
+
const node = tr.doc.nodeAt(pos);
|
|
2953
|
+
if (node) {
|
|
2954
|
+
tr = tr.setNodeMarkup(pos, void 0, {
|
|
2955
|
+
...node.attrs,
|
|
2956
|
+
textAlignment: newAlignment
|
|
2957
|
+
});
|
|
2958
|
+
}
|
|
2959
|
+
}
|
|
2960
|
+
tiptap.view?.dispatch(tr);
|
|
2961
|
+
} else if (checkBlockTypeHasDefaultProp("textAlignment", block.type, editor)) {
|
|
2962
|
+
editor.updateBlock(block, {
|
|
2963
|
+
props: { textAlignment: newAlignment }
|
|
2964
|
+
});
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2967
|
+
},
|
|
2968
|
+
[editor, selectedBlocks]
|
|
2969
|
+
);
|
|
2970
|
+
const show = useMemo(() => {
|
|
2971
|
+
return !!selectedBlocks.find(
|
|
2972
|
+
(block) => "textAlignment" in block.props || block.type === "table" && block.children
|
|
2973
|
+
);
|
|
2974
|
+
}, [selectedBlocks]);
|
|
2975
|
+
if (!show || !editor.isEditable) {
|
|
2976
|
+
return null;
|
|
2977
|
+
}
|
|
2978
|
+
return /* @__PURE__ */ jsx17(
|
|
2979
|
+
Components.FormattingToolbar.Button,
|
|
2980
|
+
{
|
|
2981
|
+
className: "bn-button",
|
|
2982
|
+
"data-test": `alignText${props.textAlignment.slice(0, 1).toUpperCase() + props.textAlignment.slice(1)}`,
|
|
2983
|
+
onClick: () => setTextAlignment(props.textAlignment),
|
|
2984
|
+
isSelected: textAlignment === props.textAlignment,
|
|
2985
|
+
label: tooltipMap[props.textAlignment],
|
|
2986
|
+
mainTooltip: tooltipMap[props.textAlignment],
|
|
2987
|
+
icon: icons[props.textAlignment]
|
|
2988
|
+
}
|
|
2989
|
+
);
|
|
2990
|
+
};
|
|
2991
|
+
|
|
2992
|
+
// src/components/VerticalAlignButton.tsx
|
|
2993
|
+
import { useCallback as useCallback15, useMemo as useMemo2 } from "react";
|
|
2994
|
+
import {
|
|
2995
|
+
useBlockNoteEditor as useBlockNoteEditor2,
|
|
2996
|
+
useComponentsContext as useComponentsContext2,
|
|
2997
|
+
useSelectedBlocks as useSelectedBlocks2
|
|
2998
|
+
} from "@blocknote/react";
|
|
2999
|
+
import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3000
|
+
var icons2 = {
|
|
3001
|
+
top: /* @__PURE__ */ jsxs11("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
|
|
3002
|
+
/* @__PURE__ */ jsx18("rect", { x: "2", y: "2", width: "12", height: "12", rx: "1" }),
|
|
3003
|
+
/* @__PURE__ */ jsx18("line", { x1: "5", y1: "5", x2: "11", y2: "5" })
|
|
3004
|
+
] }),
|
|
3005
|
+
middle: /* @__PURE__ */ jsxs11("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
|
|
3006
|
+
/* @__PURE__ */ jsx18("rect", { x: "2", y: "2", width: "12", height: "12", rx: "1" }),
|
|
3007
|
+
/* @__PURE__ */ jsx18("line", { x1: "5", y1: "8", x2: "11", y2: "8" })
|
|
3008
|
+
] }),
|
|
3009
|
+
bottom: /* @__PURE__ */ jsxs11("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
|
|
3010
|
+
/* @__PURE__ */ jsx18("rect", { x: "2", y: "2", width: "12", height: "12", rx: "1" }),
|
|
3011
|
+
/* @__PURE__ */ jsx18("line", { x1: "5", y1: "11", x2: "11", y2: "11" })
|
|
3012
|
+
] })
|
|
3013
|
+
};
|
|
3014
|
+
var tooltips = {
|
|
3015
|
+
top: "\uC704\uCABD \uC815\uB82C",
|
|
3016
|
+
middle: "\uC138\uB85C \uAC00\uC6B4\uB370 \uC815\uB82C",
|
|
3017
|
+
bottom: "\uC544\uB798\uCABD \uC815\uB82C"
|
|
3018
|
+
};
|
|
3019
|
+
function getCurrentVerticalAlignment(editor) {
|
|
3020
|
+
const tiptap = editor._tiptapEditor;
|
|
3021
|
+
if (!tiptap) return void 0;
|
|
3022
|
+
const positions = getSelectedCellPositions(editor);
|
|
3023
|
+
if (positions.length === 0) return void 0;
|
|
3024
|
+
const { state } = tiptap;
|
|
3025
|
+
const alignments = positions.map((pos) => {
|
|
3026
|
+
const node = state.doc.nodeAt(pos);
|
|
3027
|
+
return node?.attrs?.verticalAlignment || "top";
|
|
3028
|
+
});
|
|
3029
|
+
const first = alignments[0];
|
|
3030
|
+
return alignments.every((a) => a === first) ? first : void 0;
|
|
3031
|
+
}
|
|
3032
|
+
var VerticalAlignButton = (props) => {
|
|
3033
|
+
const Components = useComponentsContext2();
|
|
3034
|
+
const editor = useBlockNoteEditor2();
|
|
3035
|
+
const selectedBlocks = useSelectedBlocks2(editor);
|
|
3036
|
+
const currentAlignment = useMemo2(() => {
|
|
3037
|
+
return getCurrentVerticalAlignment(editor);
|
|
3038
|
+
}, [editor, selectedBlocks]);
|
|
3039
|
+
const setVerticalAlignment = useCallback15(
|
|
3040
|
+
(alignment) => {
|
|
3041
|
+
const tiptap = editor._tiptapEditor;
|
|
3042
|
+
if (!tiptap) return;
|
|
3043
|
+
const positions = getSelectedCellPositions(editor);
|
|
3044
|
+
if (positions.length === 0) return;
|
|
3045
|
+
editor.focus();
|
|
3046
|
+
const { state } = tiptap;
|
|
3047
|
+
let tr = state.tr;
|
|
3048
|
+
for (const pos of positions) {
|
|
3049
|
+
const node = tr.doc.nodeAt(pos);
|
|
3050
|
+
if (node) {
|
|
3051
|
+
tr = tr.setNodeMarkup(pos, void 0, {
|
|
3052
|
+
...node.attrs,
|
|
3053
|
+
verticalAlignment: alignment
|
|
3054
|
+
});
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
tiptap.view?.dispatch(tr);
|
|
3058
|
+
},
|
|
3059
|
+
[editor]
|
|
3060
|
+
);
|
|
3061
|
+
const isInTable = useMemo2(() => {
|
|
3062
|
+
return selectedBlocks.some((block) => block.type === "table");
|
|
3063
|
+
}, [selectedBlocks]);
|
|
3064
|
+
if (!isInTable || !editor.isEditable) {
|
|
3065
|
+
return null;
|
|
3066
|
+
}
|
|
3067
|
+
return /* @__PURE__ */ jsx18(
|
|
3068
|
+
Components.FormattingToolbar.Button,
|
|
3069
|
+
{
|
|
3070
|
+
className: "bn-button",
|
|
3071
|
+
"data-test": `verticalAlign${props.verticalAlignment.charAt(0).toUpperCase() + props.verticalAlignment.slice(1)}`,
|
|
3072
|
+
onClick: () => setVerticalAlignment(props.verticalAlignment),
|
|
3073
|
+
isSelected: currentAlignment === props.verticalAlignment,
|
|
3074
|
+
label: tooltips[props.verticalAlignment],
|
|
3075
|
+
mainTooltip: tooltips[props.verticalAlignment],
|
|
3076
|
+
icon: icons2[props.verticalAlignment]
|
|
3077
|
+
}
|
|
3078
|
+
);
|
|
3079
|
+
};
|
|
3080
|
+
|
|
3081
|
+
// src/components/CustomFormattingToolbar.tsx
|
|
3082
|
+
import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3083
|
+
var CustomFormattingToolbar = () => {
|
|
3084
|
+
return /* @__PURE__ */ jsxs12(FormattingToolbar, { children: [
|
|
3085
|
+
/* @__PURE__ */ jsx19(BlockTypeSelect2, {}, "blockTypeSelect"),
|
|
3086
|
+
/* @__PURE__ */ jsx19(TableCellMergeButton, {}, "tableCellMergeButton"),
|
|
3087
|
+
/* @__PURE__ */ jsx19(FileCaptionButton, {}, "fileCaptionButton"),
|
|
3088
|
+
/* @__PURE__ */ jsx19(FileReplaceButton, {}, "replaceFileButton"),
|
|
3089
|
+
/* @__PURE__ */ jsx19(FileRenameButton, {}, "fileRenameButton"),
|
|
3090
|
+
/* @__PURE__ */ jsx19(FileDeleteButton, {}, "fileDeleteButton"),
|
|
3091
|
+
/* @__PURE__ */ jsx19(FileDownloadButton, {}, "fileDownloadButton"),
|
|
3092
|
+
/* @__PURE__ */ jsx19(FilePreviewButton, {}, "filePreviewButton"),
|
|
3093
|
+
/* @__PURE__ */ jsx19(BasicTextStyleButton, { basicTextStyle: "bold" }, "boldStyleButton"),
|
|
3094
|
+
/* @__PURE__ */ jsx19(
|
|
3095
|
+
BasicTextStyleButton,
|
|
3096
|
+
{
|
|
3097
|
+
basicTextStyle: "italic"
|
|
3098
|
+
},
|
|
3099
|
+
"italicStyleButton"
|
|
3100
|
+
),
|
|
3101
|
+
/* @__PURE__ */ jsx19(
|
|
3102
|
+
BasicTextStyleButton,
|
|
3103
|
+
{
|
|
3104
|
+
basicTextStyle: "underline"
|
|
3105
|
+
},
|
|
3106
|
+
"underlineStyleButton"
|
|
3107
|
+
),
|
|
3108
|
+
/* @__PURE__ */ jsx19(
|
|
3109
|
+
BasicTextStyleButton,
|
|
3110
|
+
{
|
|
3111
|
+
basicTextStyle: "strike"
|
|
3112
|
+
},
|
|
3113
|
+
"strikeStyleButton"
|
|
3114
|
+
),
|
|
3115
|
+
/* @__PURE__ */ jsx19(TextAlignButtonWithVA, { textAlignment: "left" }, "textAlignLeftButton"),
|
|
3116
|
+
/* @__PURE__ */ jsx19(
|
|
3117
|
+
TextAlignButtonWithVA,
|
|
3118
|
+
{
|
|
3119
|
+
textAlignment: "center"
|
|
3120
|
+
},
|
|
3121
|
+
"textAlignCenterButton"
|
|
3122
|
+
),
|
|
3123
|
+
/* @__PURE__ */ jsx19(TextAlignButtonWithVA, { textAlignment: "right" }, "textAlignRightButton"),
|
|
3124
|
+
/* @__PURE__ */ jsx19(
|
|
3125
|
+
VerticalAlignButton,
|
|
3126
|
+
{
|
|
3127
|
+
verticalAlignment: "top"
|
|
3128
|
+
},
|
|
3129
|
+
"verticalAlignTop"
|
|
3130
|
+
),
|
|
3131
|
+
/* @__PURE__ */ jsx19(
|
|
3132
|
+
VerticalAlignButton,
|
|
3133
|
+
{
|
|
3134
|
+
verticalAlignment: "middle"
|
|
3135
|
+
},
|
|
3136
|
+
"verticalAlignMiddle"
|
|
3137
|
+
),
|
|
3138
|
+
/* @__PURE__ */ jsx19(
|
|
3139
|
+
VerticalAlignButton,
|
|
3140
|
+
{
|
|
3141
|
+
verticalAlignment: "bottom"
|
|
3142
|
+
},
|
|
3143
|
+
"verticalAlignBottom"
|
|
3144
|
+
),
|
|
3145
|
+
/* @__PURE__ */ jsx19(ColorStyleButton, {}, "colorStyleButton"),
|
|
3146
|
+
/* @__PURE__ */ jsx19(NestBlockButton, {}, "nestBlockButton"),
|
|
3147
|
+
/* @__PURE__ */ jsx19(UnnestBlockButton, {}, "unnestBlockButton"),
|
|
3148
|
+
/* @__PURE__ */ jsx19(CreateLinkButton, {}, "createLinkButton")
|
|
3149
|
+
] });
|
|
3150
|
+
};
|
|
3151
|
+
|
|
3152
|
+
// src/utils/table-vertical-alignment.ts
|
|
3153
|
+
function injectVerticalAlignment(blocks, editor) {
|
|
3154
|
+
const tiptap = editor?._tiptapEditor;
|
|
3155
|
+
if (!tiptap) return blocks;
|
|
3156
|
+
const { doc } = tiptap.state;
|
|
3157
|
+
const tableVAMap = buildTableVerticalAlignmentMap(doc);
|
|
3158
|
+
if (tableVAMap.size === 0) return blocks;
|
|
3159
|
+
return patchBlocks(blocks, tableVAMap);
|
|
3160
|
+
}
|
|
3161
|
+
function buildTableVerticalAlignmentMap(doc) {
|
|
3162
|
+
const result = /* @__PURE__ */ new Map();
|
|
3163
|
+
doc.descendants((node) => {
|
|
3164
|
+
if (node.type.name === "blockContainer") {
|
|
3165
|
+
const blockId = node.attrs?.id;
|
|
3166
|
+
const contentNode = node.firstChild;
|
|
3167
|
+
if (blockId && contentNode?.type.name === "table") {
|
|
3168
|
+
const cells = [];
|
|
3169
|
+
let rowIndex = 0;
|
|
3170
|
+
contentNode.forEach((rowNode) => {
|
|
3171
|
+
if (rowNode.type.name === "tableRow") {
|
|
3172
|
+
let colIndex = 0;
|
|
3173
|
+
rowNode.forEach((cellNode) => {
|
|
3174
|
+
const va = cellNode.attrs?.verticalAlignment;
|
|
3175
|
+
if (va && va !== "top") {
|
|
3176
|
+
cells.push({ row: rowIndex, col: colIndex, va });
|
|
3177
|
+
}
|
|
3178
|
+
colIndex++;
|
|
3179
|
+
});
|
|
3180
|
+
rowIndex++;
|
|
3181
|
+
}
|
|
3182
|
+
});
|
|
3183
|
+
if (cells.length > 0) {
|
|
3184
|
+
result.set(blockId, cells);
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
return false;
|
|
3188
|
+
}
|
|
3189
|
+
});
|
|
3190
|
+
return result;
|
|
3191
|
+
}
|
|
3192
|
+
function patchBlocks(blocks, tableVAMap) {
|
|
3193
|
+
return blocks.map((block) => {
|
|
3194
|
+
if (block.type !== "table" || !block.id || !block.content) {
|
|
3195
|
+
if (block.children && block.children.length > 0) {
|
|
3196
|
+
return {
|
|
3197
|
+
...block,
|
|
3198
|
+
children: patchBlocks(block.children, tableVAMap)
|
|
3199
|
+
};
|
|
3200
|
+
}
|
|
3201
|
+
return block;
|
|
3202
|
+
}
|
|
3203
|
+
const cells = tableVAMap.get(block.id);
|
|
3204
|
+
if (!cells || cells.length === 0) {
|
|
3205
|
+
return block;
|
|
3206
|
+
}
|
|
3207
|
+
const content = block.content;
|
|
3208
|
+
if (content.type !== "tableContent" || !content.rows) {
|
|
3209
|
+
return block;
|
|
3210
|
+
}
|
|
3211
|
+
const newRows = content.rows.map((row, rowIndex) => {
|
|
3212
|
+
const newCells = row.cells.map((cell, colIndex) => {
|
|
3213
|
+
const match = cells.find(
|
|
3214
|
+
(c) => c.row === rowIndex && c.col === colIndex
|
|
3215
|
+
);
|
|
3216
|
+
if (!match) return cell;
|
|
3217
|
+
if (cell && typeof cell === "object" && cell.type === "tableCell") {
|
|
3218
|
+
return {
|
|
3219
|
+
...cell,
|
|
3220
|
+
props: { ...cell.props, verticalAlignment: match.va }
|
|
3221
|
+
};
|
|
3222
|
+
}
|
|
3223
|
+
return cell;
|
|
3224
|
+
});
|
|
3225
|
+
return { ...row, cells: newCells };
|
|
3226
|
+
});
|
|
3227
|
+
return {
|
|
3228
|
+
...block,
|
|
3229
|
+
content: { ...content, rows: newRows }
|
|
3230
|
+
};
|
|
3231
|
+
});
|
|
3232
|
+
}
|
|
3233
|
+
|
|
2813
3234
|
// src/constants/limits.ts
|
|
2814
3235
|
var MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
2815
3236
|
var MAX_VIDEO_FILE_SIZE = 100 * 1024 * 1024;
|
|
@@ -2829,7 +3250,7 @@ var ALLOWED_VIDEO_EXTENSIONS = [
|
|
|
2829
3250
|
];
|
|
2830
3251
|
|
|
2831
3252
|
// src/components/LumirEditor.tsx
|
|
2832
|
-
import { Fragment as Fragment5, jsx as
|
|
3253
|
+
import { Fragment as Fragment5, jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2833
3254
|
var DEBUG_LOG = (loc, msg, data) => {
|
|
2834
3255
|
const p = fetch("http://127.0.0.1:7686/ingest/1f8ee1c5-0cf0-4ae7-91ed-5ea7ed17130a", {
|
|
2835
3256
|
method: "POST",
|
|
@@ -3054,9 +3475,9 @@ var findBlockWithLink = (blocks, targetUrl) => {
|
|
|
3054
3475
|
return null;
|
|
3055
3476
|
};
|
|
3056
3477
|
var ConvertToPreviewButton = ({ url }) => {
|
|
3057
|
-
const editor =
|
|
3058
|
-
const Components =
|
|
3059
|
-
return /* @__PURE__ */
|
|
3478
|
+
const editor = useBlockNoteEditor3();
|
|
3479
|
+
const Components = useComponentsContext3();
|
|
3480
|
+
return /* @__PURE__ */ jsx20(
|
|
3060
3481
|
Components.LinkToolbar.Button,
|
|
3061
3482
|
{
|
|
3062
3483
|
className: "bn-button",
|
|
@@ -3075,29 +3496,29 @@ var ConvertToPreviewButton = ({ url }) => {
|
|
|
3075
3496
|
console.error("Convert to link preview failed:", err);
|
|
3076
3497
|
}
|
|
3077
3498
|
},
|
|
3078
|
-
icon: /* @__PURE__ */
|
|
3079
|
-
/* @__PURE__ */
|
|
3080
|
-
/* @__PURE__ */
|
|
3081
|
-
/* @__PURE__ */
|
|
3499
|
+
icon: /* @__PURE__ */ jsxs13("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
3500
|
+
/* @__PURE__ */ jsx20("rect", { x: "1", y: "3", width: "14", height: "10", rx: "2", stroke: "currentColor", strokeWidth: "1.5", fill: "none" }),
|
|
3501
|
+
/* @__PURE__ */ jsx20("line", { x1: "1", y1: "9", x2: "15", y2: "9", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
3502
|
+
/* @__PURE__ */ jsx20("circle", { cx: "5", cy: "6.5", r: "1.5", stroke: "currentColor", strokeWidth: "1", fill: "none" })
|
|
3082
3503
|
] })
|
|
3083
3504
|
}
|
|
3084
3505
|
);
|
|
3085
3506
|
};
|
|
3086
3507
|
var CustomLinkToolbar = (props) => {
|
|
3087
|
-
const editor =
|
|
3088
|
-
const Components =
|
|
3508
|
+
const editor = useBlockNoteEditor3();
|
|
3509
|
+
const Components = useComponentsContext3();
|
|
3089
3510
|
const hasLinkPreview = !!editor?._linkPreviewApiEndpoint;
|
|
3090
|
-
return /* @__PURE__ */
|
|
3511
|
+
return /* @__PURE__ */ jsxs13(
|
|
3091
3512
|
Components.LinkToolbar.Root,
|
|
3092
3513
|
{
|
|
3093
3514
|
className: "bn-toolbar bn-link-toolbar",
|
|
3094
3515
|
onMouseEnter: props.stopHideTimer,
|
|
3095
3516
|
onMouseLeave: props.startHideTimer,
|
|
3096
3517
|
children: [
|
|
3097
|
-
/* @__PURE__ */
|
|
3098
|
-
/* @__PURE__ */
|
|
3099
|
-
/* @__PURE__ */
|
|
3100
|
-
hasLinkPreview && /* @__PURE__ */
|
|
3518
|
+
/* @__PURE__ */ jsx20(EditLinkButton, { url: props.url, text: props.text, editLink: props.editLink }),
|
|
3519
|
+
/* @__PURE__ */ jsx20(OpenLinkButton, { url: props.url }),
|
|
3520
|
+
/* @__PURE__ */ jsx20(DeleteLinkButton, { deleteLink: props.deleteLink }),
|
|
3521
|
+
hasLinkPreview && /* @__PURE__ */ jsx20(ConvertToPreviewButton, { url: props.url })
|
|
3101
3522
|
]
|
|
3102
3523
|
}
|
|
3103
3524
|
);
|
|
@@ -3147,7 +3568,7 @@ function LumirEditor({
|
|
|
3147
3568
|
const floatingMenuFileInputRef = useRef9(null);
|
|
3148
3569
|
const floatingMenuBlockRef = useRef9(null);
|
|
3149
3570
|
const floatingMenuUploadStartTimeRef = useRef9(0);
|
|
3150
|
-
const handleError =
|
|
3571
|
+
const handleError = useCallback16(
|
|
3151
3572
|
(error) => {
|
|
3152
3573
|
onError?.(error);
|
|
3153
3574
|
setErrorMessage(error.getUserMessage());
|
|
@@ -3155,10 +3576,10 @@ function LumirEditor({
|
|
|
3155
3576
|
},
|
|
3156
3577
|
[onError]
|
|
3157
3578
|
);
|
|
3158
|
-
const validatedContent =
|
|
3579
|
+
const validatedContent = useMemo3(() => {
|
|
3159
3580
|
return ContentUtils.validateContent(initialContent, initialEmptyBlocks);
|
|
3160
3581
|
}, [initialContent, initialEmptyBlocks]);
|
|
3161
|
-
const tableConfig =
|
|
3582
|
+
const tableConfig = useMemo3(() => {
|
|
3162
3583
|
return EditorConfig.getDefaultTableConfig(tables);
|
|
3163
3584
|
}, [
|
|
3164
3585
|
tables?.splitCells,
|
|
@@ -3166,10 +3587,10 @@ function LumirEditor({
|
|
|
3166
3587
|
tables?.cellTextColor,
|
|
3167
3588
|
tables?.headers
|
|
3168
3589
|
]);
|
|
3169
|
-
const headingConfig =
|
|
3590
|
+
const headingConfig = useMemo3(() => {
|
|
3170
3591
|
return EditorConfig.getDefaultHeadingConfig(heading);
|
|
3171
3592
|
}, [heading?.levels?.join(",") ?? ""]);
|
|
3172
|
-
const disabledExtensions =
|
|
3593
|
+
const disabledExtensions = useMemo3(() => {
|
|
3173
3594
|
return EditorConfig.getDisabledExtensions(
|
|
3174
3595
|
disableExtensions,
|
|
3175
3596
|
allowVideoUpload,
|
|
@@ -3188,7 +3609,7 @@ function LumirEditor({
|
|
|
3188
3609
|
useEffect8(() => {
|
|
3189
3610
|
fileNameTransformRef.current = s3Upload?.fileNameTransform;
|
|
3190
3611
|
}, [s3Upload?.fileNameTransform]);
|
|
3191
|
-
const memoizedS3Upload =
|
|
3612
|
+
const memoizedS3Upload = useMemo3(() => {
|
|
3192
3613
|
if (!s3Upload) return void 0;
|
|
3193
3614
|
return {
|
|
3194
3615
|
apiEndpoint: s3Upload.apiEndpoint,
|
|
@@ -3229,6 +3650,9 @@ function LumirEditor({
|
|
|
3229
3650
|
defaultStyles,
|
|
3230
3651
|
// 확장 비활성: 비디오/오디오/파일 제어
|
|
3231
3652
|
disableExtensions: disabledExtensions,
|
|
3653
|
+
_tiptapOptions: {
|
|
3654
|
+
extensions: [VerticalAlignmentExtension]
|
|
3655
|
+
},
|
|
3232
3656
|
placeholders: placeholder ? { default: placeholder, emptyDocument: placeholder } : void 0,
|
|
3233
3657
|
tabBehavior,
|
|
3234
3658
|
trailingBlock,
|
|
@@ -3408,7 +3832,8 @@ function LumirEditor({
|
|
|
3408
3832
|
if (!editor || !onContentChange) return;
|
|
3409
3833
|
const handleContentChange = () => {
|
|
3410
3834
|
const blocks = editor.topLevelBlocks;
|
|
3411
|
-
|
|
3835
|
+
const patched = injectVerticalAlignment(blocks, editor);
|
|
3836
|
+
onContentChange(patched);
|
|
3412
3837
|
};
|
|
3413
3838
|
return editor.onEditorContentChange(handleContentChange);
|
|
3414
3839
|
}, [editor, onContentChange]);
|
|
@@ -3563,20 +3988,20 @@ function LumirEditor({
|
|
|
3563
3988
|
el.removeEventListener("drop", handleDrop, { capture: true });
|
|
3564
3989
|
};
|
|
3565
3990
|
}, [editor, allowVideoUpload]);
|
|
3566
|
-
const computedSideMenu =
|
|
3991
|
+
const computedSideMenu = useMemo3(() => {
|
|
3567
3992
|
return sideMenuAddButton ? sideMenu : false;
|
|
3568
3993
|
}, [sideMenuAddButton, sideMenu]);
|
|
3569
|
-
const DragHandleOnlySideMenu =
|
|
3570
|
-
return (props) => /* @__PURE__ */
|
|
3994
|
+
const DragHandleOnlySideMenu = useMemo3(() => {
|
|
3995
|
+
return (props) => /* @__PURE__ */ jsx20(BlockSideMenu, { ...props, children: /* @__PURE__ */ jsx20(DragHandleButton, { ...props }) });
|
|
3571
3996
|
}, []);
|
|
3572
|
-
return /* @__PURE__ */
|
|
3997
|
+
return /* @__PURE__ */ jsxs13(
|
|
3573
3998
|
"div",
|
|
3574
3999
|
{
|
|
3575
4000
|
className: cn("lumirEditor", className),
|
|
3576
4001
|
style: { position: "relative", display: "flex", flexDirection: "column" },
|
|
3577
4002
|
children: [
|
|
3578
|
-
floatingMenu && editor && /* @__PURE__ */
|
|
3579
|
-
/* @__PURE__ */
|
|
4003
|
+
floatingMenu && editor && /* @__PURE__ */ jsxs13(Fragment5, { children: [
|
|
4004
|
+
/* @__PURE__ */ jsx20(
|
|
3580
4005
|
"input",
|
|
3581
4006
|
{
|
|
3582
4007
|
ref: floatingMenuFileInputRef,
|
|
@@ -3647,7 +4072,7 @@ function LumirEditor({
|
|
|
3647
4072
|
}
|
|
3648
4073
|
}
|
|
3649
4074
|
),
|
|
3650
|
-
/* @__PURE__ */
|
|
4075
|
+
/* @__PURE__ */ jsx20(
|
|
3651
4076
|
FloatingMenu,
|
|
3652
4077
|
{
|
|
3653
4078
|
editor,
|
|
@@ -3679,13 +4104,13 @@ function LumirEditor({
|
|
|
3679
4104
|
}
|
|
3680
4105
|
)
|
|
3681
4106
|
] }),
|
|
3682
|
-
/* @__PURE__ */
|
|
4107
|
+
/* @__PURE__ */ jsxs13(
|
|
3683
4108
|
BlockNoteView,
|
|
3684
4109
|
{
|
|
3685
4110
|
editor,
|
|
3686
4111
|
editable,
|
|
3687
4112
|
theme,
|
|
3688
|
-
formattingToolbar,
|
|
4113
|
+
formattingToolbar: false,
|
|
3689
4114
|
linkToolbar: false,
|
|
3690
4115
|
sideMenu: computedSideMenu,
|
|
3691
4116
|
slashMenu: false,
|
|
@@ -3694,12 +4119,18 @@ function LumirEditor({
|
|
|
3694
4119
|
tableHandles,
|
|
3695
4120
|
onSelectionChange,
|
|
3696
4121
|
children: [
|
|
3697
|
-
|
|
3698
|
-
|
|
4122
|
+
formattingToolbar && /* @__PURE__ */ jsx20(
|
|
4123
|
+
FormattingToolbarController,
|
|
4124
|
+
{
|
|
4125
|
+
formattingToolbar: CustomFormattingToolbar
|
|
4126
|
+
}
|
|
4127
|
+
),
|
|
4128
|
+
linkToolbar && (linkPreview?.apiEndpoint ? /* @__PURE__ */ jsx20(LinkToolbarController, { linkToolbar: CustomLinkToolbar }) : /* @__PURE__ */ jsx20(LinkToolbarController, {})),
|
|
4129
|
+
/* @__PURE__ */ jsx20(
|
|
3699
4130
|
SuggestionMenuController,
|
|
3700
4131
|
{
|
|
3701
4132
|
triggerCharacter: "/",
|
|
3702
|
-
getItems:
|
|
4133
|
+
getItems: useCallback16(
|
|
3703
4134
|
async (query) => {
|
|
3704
4135
|
const items = getDefaultReactSlashMenuItems(editor);
|
|
3705
4136
|
const filtered = items.filter((item) => {
|
|
@@ -3743,7 +4174,7 @@ function LumirEditor({
|
|
|
3743
4174
|
},
|
|
3744
4175
|
aliases: ["html", "preview", "\uC6F9", "\uC6F9\uD398\uC774\uC9C0"],
|
|
3745
4176
|
group: "Embeds",
|
|
3746
|
-
icon: /* @__PURE__ */
|
|
4177
|
+
icon: /* @__PURE__ */ jsxs13(
|
|
3747
4178
|
"svg",
|
|
3748
4179
|
{
|
|
3749
4180
|
width: "18",
|
|
@@ -3755,8 +4186,8 @@ function LumirEditor({
|
|
|
3755
4186
|
strokeLinecap: "round",
|
|
3756
4187
|
strokeLinejoin: "round",
|
|
3757
4188
|
children: [
|
|
3758
|
-
/* @__PURE__ */
|
|
3759
|
-
/* @__PURE__ */
|
|
4189
|
+
/* @__PURE__ */ jsx20("polyline", { points: "16 18 22 12 16 6" }),
|
|
4190
|
+
/* @__PURE__ */ jsx20("polyline", { points: "8 6 2 12 8 18" })
|
|
3760
4191
|
]
|
|
3761
4192
|
}
|
|
3762
4193
|
),
|
|
@@ -3781,7 +4212,7 @@ function LumirEditor({
|
|
|
3781
4212
|
"\uD504\uB9AC\uBDF0"
|
|
3782
4213
|
],
|
|
3783
4214
|
group: "Embeds",
|
|
3784
|
-
icon: /* @__PURE__ */
|
|
4215
|
+
icon: /* @__PURE__ */ jsxs13(
|
|
3785
4216
|
"svg",
|
|
3786
4217
|
{
|
|
3787
4218
|
width: "18",
|
|
@@ -3793,8 +4224,8 @@ function LumirEditor({
|
|
|
3793
4224
|
strokeLinecap: "round",
|
|
3794
4225
|
strokeLinejoin: "round",
|
|
3795
4226
|
children: [
|
|
3796
|
-
/* @__PURE__ */
|
|
3797
|
-
/* @__PURE__ */
|
|
4227
|
+
/* @__PURE__ */ jsx20("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
|
|
4228
|
+
/* @__PURE__ */ jsx20("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
|
|
3798
4229
|
]
|
|
3799
4230
|
}
|
|
3800
4231
|
),
|
|
@@ -3813,21 +4244,21 @@ function LumirEditor({
|
|
|
3813
4244
|
)
|
|
3814
4245
|
}
|
|
3815
4246
|
),
|
|
3816
|
-
!sideMenuAddButton && /* @__PURE__ */
|
|
4247
|
+
!sideMenuAddButton && /* @__PURE__ */ jsx20(SideMenuController, { sideMenu: DragHandleOnlySideMenu })
|
|
3817
4248
|
]
|
|
3818
4249
|
}
|
|
3819
4250
|
),
|
|
3820
|
-
isUploading && /* @__PURE__ */
|
|
3821
|
-
/* @__PURE__ */
|
|
3822
|
-
uploadProgress !== null && /* @__PURE__ */
|
|
4251
|
+
isUploading && /* @__PURE__ */ jsxs13("div", { className: "lumirEditor-upload-overlay", children: [
|
|
4252
|
+
/* @__PURE__ */ jsx20("div", { className: "lumirEditor-spinner" }),
|
|
4253
|
+
uploadProgress !== null && /* @__PURE__ */ jsxs13("span", { className: "lumirEditor-upload-progress", children: [
|
|
3823
4254
|
uploadProgress,
|
|
3824
4255
|
"%"
|
|
3825
4256
|
] })
|
|
3826
4257
|
] }),
|
|
3827
|
-
errorMessage && /* @__PURE__ */
|
|
3828
|
-
/* @__PURE__ */
|
|
3829
|
-
/* @__PURE__ */
|
|
3830
|
-
/* @__PURE__ */
|
|
4258
|
+
errorMessage && /* @__PURE__ */ jsxs13("div", { className: "lumirEditor-error-toast", children: [
|
|
4259
|
+
/* @__PURE__ */ jsx20("span", { className: "lumirEditor-error-icon", children: "\u26A0\uFE0F" }),
|
|
4260
|
+
/* @__PURE__ */ jsx20("span", { className: "lumirEditor-error-message", children: errorMessage }),
|
|
4261
|
+
/* @__PURE__ */ jsx20(
|
|
3831
4262
|
"button",
|
|
3832
4263
|
{
|
|
3833
4264
|
className: "lumirEditor-error-close",
|