@lumir-company/editor 0.4.18 → 0.4.21
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 +47 -18
- package/dist/index.d.mts +12 -3
- package/dist/index.d.ts +12 -3
- package/dist/index.js +523 -57
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +510 -44
- package/dist/index.mjs.map +1 -1
- package/dist/style.css +45 -0
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1466,6 +1466,16 @@ var ColumnList = createStronglyTypedTiptapNode({
|
|
|
1466
1466
|
name: "columnList",
|
|
1467
1467
|
group: "childContainer bnBlock blockGroupChild",
|
|
1468
1468
|
content: "column column+",
|
|
1469
|
+
addAttributes() {
|
|
1470
|
+
return {
|
|
1471
|
+
// 블록별 중앙 세로 구분선 표시 여부(드래그핸들 메뉴에서 토글). data-divider로 렌더.
|
|
1472
|
+
showDivider: {
|
|
1473
|
+
default: false,
|
|
1474
|
+
parseHTML: (element) => element.getAttribute("data-divider") === "true",
|
|
1475
|
+
renderHTML: (attributes) => attributes.showDivider ? { "data-divider": "true" } : {}
|
|
1476
|
+
}
|
|
1477
|
+
};
|
|
1478
|
+
},
|
|
1469
1479
|
parseHTML() {
|
|
1470
1480
|
return [{ tag: 'div[data-node-type="columnList"]' }];
|
|
1471
1481
|
},
|
|
@@ -1914,7 +1924,8 @@ var HtmlPreviewBlock = createReactBlockSpec3(
|
|
|
1914
1924
|
);
|
|
1915
1925
|
var ColumnListBlock = createBlockSpecFromStronglyTypedTiptapNode(
|
|
1916
1926
|
ColumnList,
|
|
1917
|
-
|
|
1927
|
+
// showDivider를 블록 prop으로 등록 → onContentChange JSON 직렬화 + 재로드 라운드트립.
|
|
1928
|
+
{ showDivider: { default: false } }
|
|
1918
1929
|
);
|
|
1919
1930
|
var ColumnBlock = createBlockSpecFromStronglyTypedTiptapNode(Column, {});
|
|
1920
1931
|
var schema = BlockNoteSchema.create({
|
|
@@ -3412,6 +3423,8 @@ var ALLOWED_VIDEO_EXTENSIONS = [
|
|
|
3412
3423
|
];
|
|
3413
3424
|
var ROW_RESIZE_MIN_HEIGHT = 24;
|
|
3414
3425
|
var ROW_RESIZE_HANDLE_WIDTH = 5;
|
|
3426
|
+
var TABLE_SCALE_MIN_COL_WIDTH = 24;
|
|
3427
|
+
var TABLE_SCALE_MAX = 6;
|
|
3415
3428
|
|
|
3416
3429
|
// src/extensions/rowResizing.ts
|
|
3417
3430
|
var rowResizingPluginKey = new PluginKey3(
|
|
@@ -3690,9 +3703,165 @@ function handleDecorations(state, pluginState) {
|
|
|
3690
3703
|
return DecorationSet2.create(state.doc, decorations);
|
|
3691
3704
|
}
|
|
3692
3705
|
|
|
3693
|
-
// src/extensions/
|
|
3706
|
+
// src/extensions/tableScaling.ts
|
|
3694
3707
|
import { Plugin as Plugin4, PluginKey as PluginKey4 } from "prosemirror-state";
|
|
3695
|
-
|
|
3708
|
+
import { Decoration as Decoration3, DecorationSet as DecorationSet3 } from "prosemirror-view";
|
|
3709
|
+
import { TableMap as TableMap2 } from "prosemirror-tables";
|
|
3710
|
+
var tableScalingPluginKey = new PluginKey4(
|
|
3711
|
+
"lumirTableScaling"
|
|
3712
|
+
);
|
|
3713
|
+
function tableScaling() {
|
|
3714
|
+
return new Plugin4({
|
|
3715
|
+
key: tableScalingPluginKey,
|
|
3716
|
+
state: {
|
|
3717
|
+
init: () => null,
|
|
3718
|
+
apply(tr, prev) {
|
|
3719
|
+
const meta = tr.getMeta(tableScalingPluginKey);
|
|
3720
|
+
if (meta !== void 0) return meta.preview;
|
|
3721
|
+
if (prev && tr.docChanged) {
|
|
3722
|
+
return { ...prev, tablePos: tr.mapping.map(prev.tablePos, -1) };
|
|
3723
|
+
}
|
|
3724
|
+
return prev;
|
|
3725
|
+
}
|
|
3726
|
+
},
|
|
3727
|
+
props: {
|
|
3728
|
+
decorations(state) {
|
|
3729
|
+
const p = tableScalingPluginKey.getState(state);
|
|
3730
|
+
return p ? buildHeightDecorations(state, p) : null;
|
|
3731
|
+
}
|
|
3732
|
+
},
|
|
3733
|
+
view: (view) => ({
|
|
3734
|
+
update: () => {
|
|
3735
|
+
const p = tableScalingPluginKey.getState(view.state);
|
|
3736
|
+
if (p) applyColgroupPreview(view, p);
|
|
3737
|
+
}
|
|
3738
|
+
})
|
|
3739
|
+
});
|
|
3740
|
+
}
|
|
3741
|
+
function setTableScalePreview(view, preview) {
|
|
3742
|
+
view.dispatch(view.state.tr.setMeta(tableScalingPluginKey, { preview }));
|
|
3743
|
+
}
|
|
3744
|
+
function measureTableForScale(view, tablePos) {
|
|
3745
|
+
const tableEl = findTableEl(view.nodeDOM(tablePos));
|
|
3746
|
+
const node = view.state.doc.nodeAt(tablePos);
|
|
3747
|
+
if (!tableEl || !node || node.type.name !== "table") return null;
|
|
3748
|
+
const map = TableMap2.get(node);
|
|
3749
|
+
const rect = tableEl.getBoundingClientRect();
|
|
3750
|
+
const body = tableEl.tBodies[0];
|
|
3751
|
+
const rowHeights = body ? Array.from(body.rows).map((tr) => tr.getBoundingClientRect().height) : [];
|
|
3752
|
+
const colWidths = measureColWidths(tableEl, map.width);
|
|
3753
|
+
if (rowHeights.length !== map.height || colWidths.length !== map.width) {
|
|
3754
|
+
return null;
|
|
3755
|
+
}
|
|
3756
|
+
return {
|
|
3757
|
+
tablePos,
|
|
3758
|
+
colWidths,
|
|
3759
|
+
rowHeights,
|
|
3760
|
+
origW: rect.width,
|
|
3761
|
+
origH: rect.height,
|
|
3762
|
+
scale: 1
|
|
3763
|
+
};
|
|
3764
|
+
}
|
|
3765
|
+
function commitTableScale(view, preview) {
|
|
3766
|
+
const { state } = view;
|
|
3767
|
+
const { tablePos, colWidths, rowHeights, scale } = preview;
|
|
3768
|
+
const table = state.doc.nodeAt(tablePos);
|
|
3769
|
+
if (!table || table.type.name !== "table") return;
|
|
3770
|
+
const map = TableMap2.get(table);
|
|
3771
|
+
const start = tablePos + 1;
|
|
3772
|
+
const tr = state.tr;
|
|
3773
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3774
|
+
for (const relPos of map.map) {
|
|
3775
|
+
if (seen.has(relPos)) continue;
|
|
3776
|
+
seen.add(relPos);
|
|
3777
|
+
const node = table.nodeAt(relPos);
|
|
3778
|
+
if (!node) continue;
|
|
3779
|
+
const rect = map.findCell(relPos);
|
|
3780
|
+
const colwidth = [];
|
|
3781
|
+
for (let c = rect.left; c < rect.right; c++) {
|
|
3782
|
+
colwidth.push(Math.round((colWidths[c] ?? 0) * scale));
|
|
3783
|
+
}
|
|
3784
|
+
let h = 0;
|
|
3785
|
+
for (let r = rect.top; r < rect.bottom; r++) h += rowHeights[r] ?? 0;
|
|
3786
|
+
const rowHeight = Math.round(h * scale);
|
|
3787
|
+
tr.setNodeMarkup(start + relPos, void 0, {
|
|
3788
|
+
...node.attrs,
|
|
3789
|
+
colwidth: colwidth.some((w) => w > 0) ? colwidth : null,
|
|
3790
|
+
rowHeight: rowHeight > 0 ? rowHeight : null
|
|
3791
|
+
});
|
|
3792
|
+
}
|
|
3793
|
+
if (tr.docChanged) view.dispatch(tr);
|
|
3794
|
+
}
|
|
3795
|
+
function measureColWidths(tableEl, width) {
|
|
3796
|
+
const widths = new Array(width).fill(0);
|
|
3797
|
+
const colgroup = tableEl.querySelector("colgroup");
|
|
3798
|
+
if (colgroup && colgroup.children.length === width) {
|
|
3799
|
+
let allSet = true;
|
|
3800
|
+
for (let i = 0; i < width; i++) {
|
|
3801
|
+
const w = parseFloat(colgroup.children[i].style.width);
|
|
3802
|
+
if (Number.isFinite(w) && w > 0) widths[i] = w;
|
|
3803
|
+
else allSet = false;
|
|
3804
|
+
}
|
|
3805
|
+
if (allSet) return widths;
|
|
3806
|
+
}
|
|
3807
|
+
const firstRow = tableEl.tBodies[0]?.rows[0];
|
|
3808
|
+
if (firstRow) {
|
|
3809
|
+
let col = 0;
|
|
3810
|
+
for (const cell of Array.from(firstRow.cells)) {
|
|
3811
|
+
const span = cell.colSpan || 1;
|
|
3812
|
+
const w = cell.getBoundingClientRect().width / span;
|
|
3813
|
+
for (let s = 0; s < span && col < width; s++) widths[col++] = w;
|
|
3814
|
+
}
|
|
3815
|
+
}
|
|
3816
|
+
return widths;
|
|
3817
|
+
}
|
|
3818
|
+
function applyColgroupPreview(view, p) {
|
|
3819
|
+
const tableEl = findTableEl(view.nodeDOM(p.tablePos));
|
|
3820
|
+
const colgroup = tableEl?.querySelector("colgroup");
|
|
3821
|
+
if (!colgroup) return;
|
|
3822
|
+
const cols = colgroup.children;
|
|
3823
|
+
for (let i = 0; i < cols.length && i < p.colWidths.length; i++) {
|
|
3824
|
+
cols[i].style.width = Math.round(p.colWidths[i] * p.scale) + "px";
|
|
3825
|
+
}
|
|
3826
|
+
}
|
|
3827
|
+
function buildHeightDecorations(state, p) {
|
|
3828
|
+
const table = state.doc.nodeAt(p.tablePos);
|
|
3829
|
+
if (!table || table.type.name !== "table") return DecorationSet3.empty;
|
|
3830
|
+
const map = TableMap2.get(table);
|
|
3831
|
+
const start = p.tablePos + 1;
|
|
3832
|
+
const decorations = [];
|
|
3833
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3834
|
+
for (const relPos of map.map) {
|
|
3835
|
+
if (seen.has(relPos)) continue;
|
|
3836
|
+
seen.add(relPos);
|
|
3837
|
+
const node = table.nodeAt(relPos);
|
|
3838
|
+
if (!node) continue;
|
|
3839
|
+
const rect = map.findCell(relPos);
|
|
3840
|
+
let h = 0;
|
|
3841
|
+
for (let r = rect.top; r < rect.bottom; r++) h += p.rowHeights[r] ?? 0;
|
|
3842
|
+
const from = start + relPos;
|
|
3843
|
+
const to = from + node.nodeSize;
|
|
3844
|
+
decorations.push(
|
|
3845
|
+
Decoration3.node(from, to, {
|
|
3846
|
+
class: "lumir-table-scale-dragging",
|
|
3847
|
+
style: `height: ${Math.round(h * p.scale)}px`
|
|
3848
|
+
})
|
|
3849
|
+
);
|
|
3850
|
+
}
|
|
3851
|
+
return DecorationSet3.create(state.doc, decorations);
|
|
3852
|
+
}
|
|
3853
|
+
function findTableEl(dom) {
|
|
3854
|
+
if (!dom) return null;
|
|
3855
|
+
const el = dom;
|
|
3856
|
+
if (el.nodeName === "TABLE") return el;
|
|
3857
|
+
const inner = el.querySelector?.("table");
|
|
3858
|
+
if (inner) return inner;
|
|
3859
|
+
return el.closest?.("table") ?? null;
|
|
3860
|
+
}
|
|
3861
|
+
|
|
3862
|
+
// src/extensions/tableCellAttrPreserve.ts
|
|
3863
|
+
import { Plugin as Plugin5, PluginKey as PluginKey5 } from "prosemirror-state";
|
|
3864
|
+
var tableCellAttrPreserveKey = new PluginKey5(
|
|
3696
3865
|
"lumirTableCellAttrPreserve"
|
|
3697
3866
|
);
|
|
3698
3867
|
var PRESERVED_ATTRS = [
|
|
@@ -3749,7 +3918,7 @@ function collectCellAttrs(doc) {
|
|
|
3749
3918
|
return result;
|
|
3750
3919
|
}
|
|
3751
3920
|
function tableCellAttrPreserve() {
|
|
3752
|
-
return new
|
|
3921
|
+
return new Plugin5({
|
|
3753
3922
|
key: tableCellAttrPreserveKey,
|
|
3754
3923
|
appendTransaction(transactions, oldState, newState) {
|
|
3755
3924
|
if (!transactions.some((tr2) => tr2.docChanged)) {
|
|
@@ -3855,6 +4024,7 @@ var RowHeightExtension = Extension2.create({
|
|
|
3855
4024
|
const plugins = [tableCellAttrPreserve()];
|
|
3856
4025
|
if (this.options.resizable) {
|
|
3857
4026
|
plugins.push(rowResizing());
|
|
4027
|
+
plugins.push(tableScaling());
|
|
3858
4028
|
}
|
|
3859
4029
|
return plugins;
|
|
3860
4030
|
}
|
|
@@ -3862,11 +4032,11 @@ var RowHeightExtension = Extension2.create({
|
|
|
3862
4032
|
|
|
3863
4033
|
// src/extensions/TableAlignmentExtension.ts
|
|
3864
4034
|
import { Extension as Extension3 } from "@tiptap/core";
|
|
3865
|
-
import { Plugin as
|
|
3866
|
-
import { Decoration as
|
|
3867
|
-
var tableAlignmentDecoKey = new
|
|
4035
|
+
import { Plugin as Plugin6, PluginKey as PluginKey6 } from "prosemirror-state";
|
|
4036
|
+
import { Decoration as Decoration4, DecorationSet as DecorationSet4 } from "prosemirror-view";
|
|
4037
|
+
var tableAlignmentDecoKey = new PluginKey6("lumirTableAlignmentDeco");
|
|
3868
4038
|
function tableAlignmentDecorationPlugin() {
|
|
3869
|
-
return new
|
|
4039
|
+
return new Plugin6({
|
|
3870
4040
|
key: tableAlignmentDecoKey,
|
|
3871
4041
|
props: {
|
|
3872
4042
|
decorations(state) {
|
|
@@ -3876,7 +4046,7 @@ function tableAlignmentDecorationPlugin() {
|
|
|
3876
4046
|
const align = node.attrs.tableAlignment;
|
|
3877
4047
|
if (align && align !== "left") {
|
|
3878
4048
|
decorations.push(
|
|
3879
|
-
|
|
4049
|
+
Decoration4.node(pos, pos + node.nodeSize, {
|
|
3880
4050
|
"data-table-alignment": align
|
|
3881
4051
|
})
|
|
3882
4052
|
);
|
|
@@ -3885,7 +4055,7 @@ function tableAlignmentDecorationPlugin() {
|
|
|
3885
4055
|
}
|
|
3886
4056
|
return void 0;
|
|
3887
4057
|
});
|
|
3888
|
-
return
|
|
4058
|
+
return DecorationSet4.create(state.doc, decorations);
|
|
3889
4059
|
}
|
|
3890
4060
|
}
|
|
3891
4061
|
});
|
|
@@ -3916,9 +4086,56 @@ var TableAlignmentExtension = Extension3.create({
|
|
|
3916
4086
|
}
|
|
3917
4087
|
});
|
|
3918
4088
|
|
|
4089
|
+
// src/extensions/TableSelectAllExtension.ts
|
|
4090
|
+
import { Extension as Extension4 } from "@tiptap/core";
|
|
4091
|
+
import { CellSelection, TableMap as TableMap3 } from "prosemirror-tables";
|
|
4092
|
+
var TableSelectAllExtension = Extension4.create({
|
|
4093
|
+
name: "lumirTableSelectAll",
|
|
4094
|
+
priority: 1e3,
|
|
4095
|
+
addKeyboardShortcuts() {
|
|
4096
|
+
return {
|
|
4097
|
+
"Mod-a": () => {
|
|
4098
|
+
const view = this.editor.view;
|
|
4099
|
+
const { state } = view;
|
|
4100
|
+
const sel = state.selection;
|
|
4101
|
+
const $from = sel.$from;
|
|
4102
|
+
let depth = -1;
|
|
4103
|
+
for (let d = $from.depth; d > 0; d--) {
|
|
4104
|
+
if ($from.node(d).type.name === "table") {
|
|
4105
|
+
depth = d;
|
|
4106
|
+
break;
|
|
4107
|
+
}
|
|
4108
|
+
}
|
|
4109
|
+
if (depth < 0) return false;
|
|
4110
|
+
const table = $from.node(depth);
|
|
4111
|
+
const tableStart = $from.start(depth);
|
|
4112
|
+
const map = TableMap3.get(table);
|
|
4113
|
+
const firstRel = map.map[0];
|
|
4114
|
+
const lastRel = map.map[map.map.length - 1];
|
|
4115
|
+
if (sel instanceof CellSelection) {
|
|
4116
|
+
const a = sel.$anchorCell.pos - tableStart;
|
|
4117
|
+
const h = sel.$headCell.pos - tableStart;
|
|
4118
|
+
if (Math.min(a, h) === firstRel && Math.max(a, h) === lastRel) {
|
|
4119
|
+
return false;
|
|
4120
|
+
}
|
|
4121
|
+
}
|
|
4122
|
+
const tr = state.tr.setSelection(
|
|
4123
|
+
CellSelection.create(
|
|
4124
|
+
state.doc,
|
|
4125
|
+
tableStart + firstRel,
|
|
4126
|
+
tableStart + lastRel
|
|
4127
|
+
)
|
|
4128
|
+
);
|
|
4129
|
+
view.dispatch(tr);
|
|
4130
|
+
return true;
|
|
4131
|
+
}
|
|
4132
|
+
};
|
|
4133
|
+
}
|
|
4134
|
+
});
|
|
4135
|
+
|
|
3919
4136
|
// src/blocks/columns/insertColumns.ts
|
|
3920
4137
|
import { TextSelection } from "prosemirror-state";
|
|
3921
|
-
function insertTwoColumns(editor) {
|
|
4138
|
+
function insertTwoColumns(editor, showDivider = false) {
|
|
3922
4139
|
const tiptap = editor?._tiptapEditor;
|
|
3923
4140
|
if (!tiptap) {
|
|
3924
4141
|
return false;
|
|
@@ -3945,7 +4162,7 @@ function insertTwoColumns(editor) {
|
|
|
3945
4162
|
const insertPos = $from.after(depth);
|
|
3946
4163
|
const mkBlock = () => blockContainer.create(null, paragraph.create());
|
|
3947
4164
|
const mkColumn = () => column.create(null, mkBlock());
|
|
3948
|
-
const list = columnList.create(
|
|
4165
|
+
const list = columnList.create({ showDivider }, [mkColumn(), mkColumn()]);
|
|
3949
4166
|
try {
|
|
3950
4167
|
let tr = state.tr.insert(insertPos, list);
|
|
3951
4168
|
try {
|
|
@@ -4830,9 +5047,14 @@ import { useEffect as useEffect9, useMemo as useMemo6 } from "react";
|
|
|
4830
5047
|
function useFocusedCellHandlePositioning(cellEl, tbodyEl, orientation, show) {
|
|
4831
5048
|
const { refs, floatingStyles, context, update } = useFloating({
|
|
4832
5049
|
open: show,
|
|
4833
|
-
placement: orientation === "row" ? "left" : orientation === "col" ? "top" : "right",
|
|
4834
|
-
// col/row
|
|
4835
|
-
|
|
5050
|
+
placement: orientation === "row" ? "left" : orientation === "col" ? "top" : orientation === "corner" ? "bottom-start" : "right",
|
|
5051
|
+
// col/row/cell: 가장자리에 14px hit-area 중앙 정렬(-7).
|
|
5052
|
+
// corner: 18px hit-zone이 표 우하단 모서리에 걸치도록 위/좌로 살짝 당김(모서리 hover 자연).
|
|
5053
|
+
middleware: [
|
|
5054
|
+
offset(
|
|
5055
|
+
orientation === "corner" ? { mainAxis: -6, crossAxis: -6 } : -7
|
|
5056
|
+
)
|
|
5057
|
+
],
|
|
4836
5058
|
whileElementsMounted: autoUpdate
|
|
4837
5059
|
});
|
|
4838
5060
|
const { isMounted, styles } = useTransitionStyles(context);
|
|
@@ -4860,6 +5082,9 @@ function useFocusedCellHandlePositioning(cellEl, tbodyEl, orientation, show) {
|
|
|
4860
5082
|
if (orientation === "row") {
|
|
4861
5083
|
return new DOMRect(t.left, c.top, 0, c.height);
|
|
4862
5084
|
}
|
|
5085
|
+
if (orientation === "corner") {
|
|
5086
|
+
return new DOMRect(t.right, t.bottom, 0, 0);
|
|
5087
|
+
}
|
|
4863
5088
|
return c;
|
|
4864
5089
|
}
|
|
4865
5090
|
});
|
|
@@ -4877,6 +5102,33 @@ function useFocusedCellHandlePositioning(cellEl, tbodyEl, orientation, show) {
|
|
|
4877
5102
|
[floatingStyles, isMounted, refs.setFloating, styles]
|
|
4878
5103
|
);
|
|
4879
5104
|
}
|
|
5105
|
+
function useTableCornerPositioning(referencePosTable, show) {
|
|
5106
|
+
const { refs, floatingStyles, context } = useFloating({
|
|
5107
|
+
open: show,
|
|
5108
|
+
placement: "bottom-start",
|
|
5109
|
+
// 18px hit-zone을 모서리에서 안쪽(위/좌)으로 당겨 표 위에 걸치게 한다.
|
|
5110
|
+
middleware: [offset({ mainAxis: -12, crossAxis: -12 })],
|
|
5111
|
+
whileElementsMounted: autoUpdate
|
|
5112
|
+
});
|
|
5113
|
+
const { isMounted, styles } = useTransitionStyles(context);
|
|
5114
|
+
useEffect9(() => {
|
|
5115
|
+
if (!referencePosTable) {
|
|
5116
|
+
refs.setReference(null);
|
|
5117
|
+
return;
|
|
5118
|
+
}
|
|
5119
|
+
refs.setReference({
|
|
5120
|
+
getBoundingClientRect: () => new DOMRect(referencePosTable.right, referencePosTable.bottom, 0, 0)
|
|
5121
|
+
});
|
|
5122
|
+
}, [referencePosTable, refs]);
|
|
5123
|
+
return useMemo6(
|
|
5124
|
+
() => ({
|
|
5125
|
+
isMounted,
|
|
5126
|
+
ref: refs.setFloating,
|
|
5127
|
+
style: { ...styles, ...floatingStyles }
|
|
5128
|
+
}),
|
|
5129
|
+
[floatingStyles, isMounted, refs.setFloating, styles]
|
|
5130
|
+
);
|
|
5131
|
+
}
|
|
4880
5132
|
|
|
4881
5133
|
// src/components/LumirTableHandlesController.tsx
|
|
4882
5134
|
import { Fragment as Fragment7, jsx as jsx26, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
@@ -5027,6 +5279,10 @@ function LumirTableHandlesController() {
|
|
|
5027
5279
|
const onEndExtend = useCallback20(() => {
|
|
5028
5280
|
editor.tableHandles?.unfreezeHandles();
|
|
5029
5281
|
}, [editor]);
|
|
5282
|
+
const tableCorner = useTableCornerPositioning(
|
|
5283
|
+
coreState?.referencePosTable ?? null,
|
|
5284
|
+
!!coreState?.widgetContainer
|
|
5285
|
+
);
|
|
5030
5286
|
const menuHandlers = useMemo7(() => {
|
|
5031
5287
|
const mk = (kind) => ({
|
|
5032
5288
|
freeze: () => {
|
|
@@ -5076,6 +5332,48 @@ function LumirTableHandlesController() {
|
|
|
5076
5332
|
}, [editor, recompute]);
|
|
5077
5333
|
const noop = useCallback20(() => {
|
|
5078
5334
|
}, []);
|
|
5335
|
+
const onScaleStart = useCallback20(
|
|
5336
|
+
(e) => {
|
|
5337
|
+
e.preventDefault();
|
|
5338
|
+
e.stopPropagation();
|
|
5339
|
+
const view = editor.prosemirrorView;
|
|
5340
|
+
const blockId = coreState?.block?.id;
|
|
5341
|
+
if (!view || !blockId) return;
|
|
5342
|
+
const tablePos = findTableNodePos(editor._tiptapEditor, blockId);
|
|
5343
|
+
if (tablePos < 0) return;
|
|
5344
|
+
const base = measureTableForScale(view, tablePos);
|
|
5345
|
+
if (!base) return;
|
|
5346
|
+
const minScale = Math.max(
|
|
5347
|
+
TABLE_SCALE_MIN_COL_WIDTH / Math.max(1, Math.min(...base.colWidths)),
|
|
5348
|
+
ROW_RESIZE_MIN_HEIGHT / Math.max(1, Math.min(...base.rowHeights))
|
|
5349
|
+
);
|
|
5350
|
+
const startX = e.clientX;
|
|
5351
|
+
const startY = e.clientY;
|
|
5352
|
+
let current = base;
|
|
5353
|
+
editor.tableHandles?.freezeHandles();
|
|
5354
|
+
const onMove = (me) => {
|
|
5355
|
+
if (me.buttons === 0) return onUp();
|
|
5356
|
+
const newW = base.origW + (me.clientX - startX);
|
|
5357
|
+
const newH = base.origH + (me.clientY - startY);
|
|
5358
|
+
const scale = Math.min(
|
|
5359
|
+
TABLE_SCALE_MAX,
|
|
5360
|
+
Math.max(minScale, Math.max(newW / base.origW, newH / base.origH))
|
|
5361
|
+
);
|
|
5362
|
+
current = { ...base, scale };
|
|
5363
|
+
setTableScalePreview(view, current);
|
|
5364
|
+
};
|
|
5365
|
+
const onUp = () => {
|
|
5366
|
+
window.removeEventListener("pointermove", onMove);
|
|
5367
|
+
window.removeEventListener("pointerup", onUp);
|
|
5368
|
+
setTableScalePreview(view, null);
|
|
5369
|
+
commitTableScale(view, current);
|
|
5370
|
+
editor.tableHandles?.unfreezeHandles();
|
|
5371
|
+
};
|
|
5372
|
+
window.addEventListener("pointermove", onMove);
|
|
5373
|
+
window.addEventListener("pointerup", onUp);
|
|
5374
|
+
},
|
|
5375
|
+
[editor, coreState]
|
|
5376
|
+
);
|
|
5079
5377
|
return /* @__PURE__ */ jsxs18(Fragment7, { children: [
|
|
5080
5378
|
/* @__PURE__ */ jsx26("div", { ref: setMenuContainerRef }),
|
|
5081
5379
|
th && focused && menuContainerRef && /* @__PURE__ */ jsxs18(FloatingPortal, { root: focused.widgetContainer, children: [
|
|
@@ -5191,6 +5489,16 @@ function LumirTableHandlesController() {
|
|
|
5191
5489
|
}
|
|
5192
5490
|
)
|
|
5193
5491
|
}
|
|
5492
|
+
),
|
|
5493
|
+
tableCorner.isMounted && /* @__PURE__ */ jsx26(
|
|
5494
|
+
"div",
|
|
5495
|
+
{
|
|
5496
|
+
ref: tableCorner.ref,
|
|
5497
|
+
style: tableCorner.style,
|
|
5498
|
+
className: "lumir-tbl-scale-handle",
|
|
5499
|
+
title: "\uD45C \uD06C\uAE30 \uC870\uC808 (\uC885\uD6A1\uBE44 \uACE0\uC815)",
|
|
5500
|
+
onPointerDown: onScaleStart
|
|
5501
|
+
}
|
|
5194
5502
|
)
|
|
5195
5503
|
] })
|
|
5196
5504
|
] });
|
|
@@ -5447,7 +5755,7 @@ function liftFontSize(blocks) {
|
|
|
5447
5755
|
}
|
|
5448
5756
|
|
|
5449
5757
|
// src/utils/table-delete.ts
|
|
5450
|
-
import { CellSelection, TableMap as
|
|
5758
|
+
import { CellSelection as CellSelection2, TableMap as TableMap4, deleteRow } from "prosemirror-tables";
|
|
5451
5759
|
function measureRowHeights(view, tablePos) {
|
|
5452
5760
|
try {
|
|
5453
5761
|
const at = view?.domAtPos?.(tablePos + 1);
|
|
@@ -5466,7 +5774,7 @@ function measureRowHeights(view, tablePos) {
|
|
|
5466
5774
|
function buildDeleteColumnTr(state, tablePos, col, rowPx) {
|
|
5467
5775
|
const table = state.doc.nodeAt(tablePos);
|
|
5468
5776
|
if (!table || table.type.name !== "table") return null;
|
|
5469
|
-
const map =
|
|
5777
|
+
const map = TableMap4.get(table);
|
|
5470
5778
|
const W = map.width;
|
|
5471
5779
|
const H = map.height;
|
|
5472
5780
|
if (col < 0 || col >= W || W <= 1) return null;
|
|
@@ -5572,7 +5880,7 @@ function removeFocusedRowOrColumn(editor, index, direction) {
|
|
|
5572
5880
|
const rowStart = state.doc.resolve(tableInside.posAtIndex(index) + 1);
|
|
5573
5881
|
const cellPos = state.doc.resolve(rowStart.posAtIndex(0));
|
|
5574
5882
|
const selState = state.apply(
|
|
5575
|
-
state.tr.setSelection(new
|
|
5883
|
+
state.tr.setSelection(new CellSelection2(cellPos))
|
|
5576
5884
|
);
|
|
5577
5885
|
return deleteRow(selState, (tr) => tiptap.view.dispatch(tr));
|
|
5578
5886
|
} catch {
|
|
@@ -5724,6 +6032,20 @@ function normalizeAlign(ta) {
|
|
|
5724
6032
|
if (v === "justify") return "justify";
|
|
5725
6033
|
return "";
|
|
5726
6034
|
}
|
|
6035
|
+
function mapVerticalAlign(v) {
|
|
6036
|
+
const s = (v || "").trim().toLowerCase();
|
|
6037
|
+
if (s === "middle" || s === "center") return "middle";
|
|
6038
|
+
if (s === "bottom") return "bottom";
|
|
6039
|
+
return null;
|
|
6040
|
+
}
|
|
6041
|
+
function fontSizeToPx(raw) {
|
|
6042
|
+
if (!raw) return null;
|
|
6043
|
+
const m = String(raw).trim().match(/^([\d.]+)\s*(px|pt)?$/i);
|
|
6044
|
+
if (!m) return null;
|
|
6045
|
+
const v = parseFloat(m[1]);
|
|
6046
|
+
if (!Number.isFinite(v) || v <= 0) return null;
|
|
6047
|
+
return (m[2] || "px").toLowerCase() === "pt" ? v * (96 / 72) : v;
|
|
6048
|
+
}
|
|
5727
6049
|
function applyCellFormatting(el, fmt) {
|
|
5728
6050
|
if (fmt.bgRgb && !fmt.bgTransparent) {
|
|
5729
6051
|
const v = nearestBackgroundColorValue(fmt.bgRgb);
|
|
@@ -5743,6 +6065,13 @@ function applyCellFormatting(el, fmt) {
|
|
|
5743
6065
|
if (fmt.bold) inner = `<strong>${inner}</strong>`;
|
|
5744
6066
|
el.innerHTML = inner;
|
|
5745
6067
|
}
|
|
6068
|
+
if (fmt.verticalAlign) {
|
|
6069
|
+
el.setAttribute("data-vertical-alignment", fmt.verticalAlign);
|
|
6070
|
+
}
|
|
6071
|
+
if (fmt.fontSizePx && Math.abs(fmt.fontSizePx - 14) > 1 && el.innerHTML.trim()) {
|
|
6072
|
+
const v = `${Math.round(fmt.fontSizePx)}px`;
|
|
6073
|
+
el.innerHTML = `<span data-style-type="fontSize" data-value="${v}" style="font-size:${v}">` + el.innerHTML + `</span>`;
|
|
6074
|
+
}
|
|
5746
6075
|
}
|
|
5747
6076
|
function readComputedFormat(el) {
|
|
5748
6077
|
const cs = getComputedStyle(el);
|
|
@@ -5756,7 +6085,13 @@ function readComputedFormat(el) {
|
|
|
5756
6085
|
align: normalizeAlign(cs.textAlign),
|
|
5757
6086
|
bold: fw === "bold" || fw === "bolder" || !isNaN(fwNum) && fwNum >= 600,
|
|
5758
6087
|
italic: (cs.fontStyle || "").toLowerCase().includes("italic"),
|
|
5759
|
-
underline: decoration.toLowerCase().includes("underline")
|
|
6088
|
+
underline: decoration.toLowerCase().includes("underline"),
|
|
6089
|
+
fontSizePx: fontSizeToPx(cs.fontSize),
|
|
6090
|
+
// ⚠️ computed vertical-align은 td 기본값이 "middle"이라 셀마다 잘못 붙는다.
|
|
6091
|
+
// 명시적 inline style / valign 속성만 읽는다(기본값 노이즈 방지).
|
|
6092
|
+
verticalAlign: mapVerticalAlign(
|
|
6093
|
+
el.style?.verticalAlign || el.getAttribute("valign")
|
|
6094
|
+
)
|
|
5760
6095
|
};
|
|
5761
6096
|
}
|
|
5762
6097
|
function readInlineFormat(el) {
|
|
@@ -5772,7 +6107,11 @@ function readInlineFormat(el) {
|
|
|
5772
6107
|
align: normalizeAlign(sm["text-align"] || el.getAttribute("align")),
|
|
5773
6108
|
bold: fw === "bold" || fw === "bolder" || parseInt(fw, 10) >= 600,
|
|
5774
6109
|
italic: (sm["font-style"] || "").toLowerCase().includes("italic"),
|
|
5775
|
-
underline: decoration.toLowerCase().includes("underline")
|
|
6110
|
+
underline: decoration.toLowerCase().includes("underline"),
|
|
6111
|
+
fontSizePx: fontSizeToPx(sm["font-size"]),
|
|
6112
|
+
verticalAlign: mapVerticalAlign(
|
|
6113
|
+
sm["vertical-align"] || el.getAttribute("valign")
|
|
6114
|
+
)
|
|
5776
6115
|
};
|
|
5777
6116
|
}
|
|
5778
6117
|
function normalizeExcelTableHtml(html) {
|
|
@@ -5786,7 +6125,7 @@ function normalizeExcelTableHtml(html) {
|
|
|
5786
6125
|
try {
|
|
5787
6126
|
host = document.createElement("div");
|
|
5788
6127
|
host.setAttribute("aria-hidden", "true");
|
|
5789
|
-
host.style.cssText = "position:absolute;left:-99999px;top:0;width:0;height:0;overflow:hidden;opacity:0;pointer-events:none";
|
|
6128
|
+
host.style.cssText = "position:absolute;left:-99999px;top:0;width:0;height:0;overflow:hidden;opacity:0;pointer-events:none;font-size:14px";
|
|
5790
6129
|
const shadow = host.attachShadow({ mode: "open" });
|
|
5791
6130
|
const styles = Array.from(doc.querySelectorAll("style")).map((s) => s.outerHTML).join("");
|
|
5792
6131
|
shadow.innerHTML = styles + doc.body.innerHTML;
|
|
@@ -5809,6 +6148,100 @@ function normalizeExcelTableHtml(html) {
|
|
|
5809
6148
|
}
|
|
5810
6149
|
}
|
|
5811
6150
|
|
|
6151
|
+
// src/utils/table-paste-fit.ts
|
|
6152
|
+
var MIN_COL_PX = 24;
|
|
6153
|
+
function toPx(raw, maxWidth) {
|
|
6154
|
+
if (!raw) return null;
|
|
6155
|
+
const m = String(raw).trim().match(/^([\d.]+)\s*(pt|px|%)?$/i);
|
|
6156
|
+
if (!m) return null;
|
|
6157
|
+
const v = parseFloat(m[1]);
|
|
6158
|
+
if (!Number.isFinite(v) || v <= 0) return null;
|
|
6159
|
+
const unit = (m[2] || "px").toLowerCase();
|
|
6160
|
+
if (unit === "pt") return v * (96 / 72);
|
|
6161
|
+
if (unit === "%") return v / 100 * maxWidth;
|
|
6162
|
+
return v;
|
|
6163
|
+
}
|
|
6164
|
+
function elWidthPx(el, maxWidth) {
|
|
6165
|
+
const styleW = el.style?.width;
|
|
6166
|
+
return toPx(styleW, maxWidth) ?? toPx(el.getAttribute("width"), maxWidth);
|
|
6167
|
+
}
|
|
6168
|
+
function readColumnWidths(table, maxWidth) {
|
|
6169
|
+
const colEls = table.querySelector("colgroup")?.querySelectorAll("col");
|
|
6170
|
+
if (colEls && colEls.length > 0) {
|
|
6171
|
+
const widths2 = [];
|
|
6172
|
+
let ok2 = true;
|
|
6173
|
+
colEls.forEach((c) => {
|
|
6174
|
+
const span = parseInt(c.getAttribute("span") || "1", 10) || 1;
|
|
6175
|
+
const w = elWidthPx(c, maxWidth);
|
|
6176
|
+
if (w == null) ok2 = false;
|
|
6177
|
+
for (let i = 0; i < span; i++) widths2.push(w ?? 0);
|
|
6178
|
+
});
|
|
6179
|
+
if (ok2 && widths2.length > 0) return widths2;
|
|
6180
|
+
}
|
|
6181
|
+
const firstRow = table.querySelector("tr");
|
|
6182
|
+
if (!firstRow) return null;
|
|
6183
|
+
const widths = [];
|
|
6184
|
+
let ok = true;
|
|
6185
|
+
Array.from(firstRow.children).forEach((cell) => {
|
|
6186
|
+
if (cell.tagName !== "TD" && cell.tagName !== "TH") return;
|
|
6187
|
+
const span = parseInt(cell.getAttribute("colspan") || "1", 10) || 1;
|
|
6188
|
+
const w = elWidthPx(cell, maxWidth);
|
|
6189
|
+
if (w == null) ok = false;
|
|
6190
|
+
const per = (w ?? 0) / span;
|
|
6191
|
+
for (let i = 0; i < span; i++) widths.push(per);
|
|
6192
|
+
});
|
|
6193
|
+
return ok && widths.length > 0 ? widths : null;
|
|
6194
|
+
}
|
|
6195
|
+
function fitWidths(widths, maxWidth) {
|
|
6196
|
+
const total = widths.reduce((a, b) => a + b, 0);
|
|
6197
|
+
if (total <= 0) return widths;
|
|
6198
|
+
const scale = total > maxWidth ? maxWidth / total : 1;
|
|
6199
|
+
return widths.map((w) => Math.max(MIN_COL_PX, Math.round(w * scale)));
|
|
6200
|
+
}
|
|
6201
|
+
function computeFittedColumnWidthsPerTable(html, maxWidth) {
|
|
6202
|
+
if (!html || typeof DOMParser === "undefined" || !(maxWidth > 0)) return [];
|
|
6203
|
+
let doc;
|
|
6204
|
+
try {
|
|
6205
|
+
doc = new DOMParser().parseFromString(html, "text/html");
|
|
6206
|
+
} catch {
|
|
6207
|
+
return [];
|
|
6208
|
+
}
|
|
6209
|
+
return Array.from(doc.querySelectorAll("table")).map((t) => {
|
|
6210
|
+
const widths = readColumnWidths(t, maxWidth);
|
|
6211
|
+
return widths ? fitWidths(widths, maxWidth) : null;
|
|
6212
|
+
});
|
|
6213
|
+
}
|
|
6214
|
+
function collectTableBlocks(blocks) {
|
|
6215
|
+
const out = [];
|
|
6216
|
+
const walk = (bs) => {
|
|
6217
|
+
for (const b of bs) {
|
|
6218
|
+
if (b?.type === "table") out.push(b);
|
|
6219
|
+
if (b?.children?.length) walk(b.children);
|
|
6220
|
+
}
|
|
6221
|
+
};
|
|
6222
|
+
walk(blocks || []);
|
|
6223
|
+
return out;
|
|
6224
|
+
}
|
|
6225
|
+
function applyFittedWidthsToNewTables(editor, beforeIds, perTable) {
|
|
6226
|
+
if (!editor || perTable.length === 0) return;
|
|
6227
|
+
const newTables = collectTableBlocks(editor.document).filter(
|
|
6228
|
+
(b) => !beforeIds.has(b.id)
|
|
6229
|
+
);
|
|
6230
|
+
newTables.forEach((tb, i) => {
|
|
6231
|
+
const widths = perTable[i];
|
|
6232
|
+
const current = tb?.content?.columnWidths;
|
|
6233
|
+
if (widths && Array.isArray(current) && current.length === widths.length) {
|
|
6234
|
+
try {
|
|
6235
|
+
editor.updateBlock(tb, {
|
|
6236
|
+
type: "table",
|
|
6237
|
+
content: { ...tb.content, columnWidths: widths }
|
|
6238
|
+
});
|
|
6239
|
+
} catch {
|
|
6240
|
+
}
|
|
6241
|
+
}
|
|
6242
|
+
});
|
|
6243
|
+
}
|
|
6244
|
+
|
|
5812
6245
|
// src/components/LumirEditor.tsx
|
|
5813
6246
|
import { Fragment as Fragment8, jsx as jsx27, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
5814
6247
|
var DEBUG_LOG = (loc, msg, data) => {
|
|
@@ -6121,6 +6554,7 @@ function LumirEditor({
|
|
|
6121
6554
|
className = "",
|
|
6122
6555
|
placeholder,
|
|
6123
6556
|
sideMenuAddButton = false,
|
|
6557
|
+
columnDivider = false,
|
|
6124
6558
|
floatingMenu = false,
|
|
6125
6559
|
floatingMenuPosition = "sticky",
|
|
6126
6560
|
// callbacks / refs
|
|
@@ -6234,7 +6668,9 @@ function LumirEditor({
|
|
|
6234
6668
|
// tableHandles prop으로 게이트(기존 grip 컨트롤러와 동일 게이트).
|
|
6235
6669
|
RowHeightExtension.configure({ resizable: tableHandles }),
|
|
6236
6670
|
// 표 블록 정렬(좌/가운데/우) attr.
|
|
6237
|
-
TableAlignmentExtension
|
|
6671
|
+
TableAlignmentExtension,
|
|
6672
|
+
// 셀 포커스 시 Ctrl/Cmd+A → 표 전체 선택.
|
|
6673
|
+
TableSelectAllExtension
|
|
6238
6674
|
]
|
|
6239
6675
|
},
|
|
6240
6676
|
placeholders: placeholder ? { default: placeholder, emptyDocument: placeholder } : void 0,
|
|
@@ -6340,7 +6776,14 @@ function LumirEditor({
|
|
|
6340
6776
|
hasFiles: !!event?.clipboardData?.files?.length
|
|
6341
6777
|
});
|
|
6342
6778
|
event.preventDefault();
|
|
6779
|
+
const pmDom = editor2.prosemirrorView?.dom;
|
|
6780
|
+
const maxWidth = pmDom?.clientWidth ? pmDom.clientWidth - 8 : 0;
|
|
6781
|
+
const fittedWidths = computeFittedColumnWidthsPerTable(pastedHtml, maxWidth);
|
|
6782
|
+
const beforeTableIds = new Set(
|
|
6783
|
+
collectTableBlocks(editor2.document).map((b) => b.id)
|
|
6784
|
+
);
|
|
6343
6785
|
editor2.pasteHTML(normalizeExcelTableHtml(pastedHtml));
|
|
6786
|
+
applyFittedWidthsToNewTables(editor2, beforeTableIds, fittedWidths);
|
|
6344
6787
|
return true;
|
|
6345
6788
|
}
|
|
6346
6789
|
const fileList = event?.clipboardData?.files ?? null;
|
|
@@ -6635,7 +7078,11 @@ function LumirEditor({
|
|
|
6635
7078
|
return /* @__PURE__ */ jsxs19(
|
|
6636
7079
|
"div",
|
|
6637
7080
|
{
|
|
6638
|
-
className: cn(
|
|
7081
|
+
className: cn(
|
|
7082
|
+
"lumirEditor",
|
|
7083
|
+
columnDivider && "lumir-column-divider",
|
|
7084
|
+
className
|
|
7085
|
+
),
|
|
6639
7086
|
style: { position: "relative", display: "flex", flexDirection: "column" },
|
|
6640
7087
|
children: [
|
|
6641
7088
|
floatingMenu && editor && /* @__PURE__ */ jsxs19(Fragment8, { children: [
|
|
@@ -6832,33 +7279,52 @@ function LumirEditor({
|
|
|
6832
7279
|
),
|
|
6833
7280
|
subtext: "HTML \uD30C\uC77C\uC744 \uBBF8\uB9AC\uBCF4\uAE30\uB85C \uC0BD\uC785"
|
|
6834
7281
|
};
|
|
7282
|
+
const columnIcon = (withDivider) => /* @__PURE__ */ jsxs19(
|
|
7283
|
+
"svg",
|
|
7284
|
+
{
|
|
7285
|
+
width: "18",
|
|
7286
|
+
height: "18",
|
|
7287
|
+
viewBox: "0 0 24 24",
|
|
7288
|
+
fill: "none",
|
|
7289
|
+
stroke: "currentColor",
|
|
7290
|
+
strokeWidth: "2",
|
|
7291
|
+
strokeLinecap: "round",
|
|
7292
|
+
strokeLinejoin: "round",
|
|
7293
|
+
children: [
|
|
7294
|
+
/* @__PURE__ */ jsx27("rect", { x: "3", y: "4", width: "7", height: "16", rx: "1" }),
|
|
7295
|
+
/* @__PURE__ */ jsx27("rect", { x: "14", y: "4", width: "7", height: "16", rx: "1" }),
|
|
7296
|
+
withDivider && /* @__PURE__ */ jsx27("line", { x1: "12", y1: "3", x2: "12", y2: "21", strokeDasharray: "2 2" })
|
|
7297
|
+
]
|
|
7298
|
+
}
|
|
7299
|
+
);
|
|
6835
7300
|
const columnItem = {
|
|
6836
7301
|
title: "2\uB2E8 \uCEEC\uB7FC",
|
|
6837
|
-
onItemClick: () =>
|
|
6838
|
-
insertTwoColumns(editor);
|
|
6839
|
-
},
|
|
7302
|
+
onItemClick: () => insertTwoColumns(editor, false),
|
|
6840
7303
|
aliases: ["columns", "column", "2col", "\uB2E8", "\uCEEC\uB7FC", "\uB2E4\uB2E8", "\uBD84\uD560"],
|
|
6841
7304
|
group: "Basic blocks",
|
|
6842
|
-
icon:
|
|
6843
|
-
"svg",
|
|
6844
|
-
{
|
|
6845
|
-
width: "18",
|
|
6846
|
-
height: "18",
|
|
6847
|
-
viewBox: "0 0 24 24",
|
|
6848
|
-
fill: "none",
|
|
6849
|
-
stroke: "currentColor",
|
|
6850
|
-
strokeWidth: "2",
|
|
6851
|
-
strokeLinecap: "round",
|
|
6852
|
-
strokeLinejoin: "round",
|
|
6853
|
-
children: [
|
|
6854
|
-
/* @__PURE__ */ jsx27("rect", { x: "3", y: "4", width: "7", height: "16", rx: "1" }),
|
|
6855
|
-
/* @__PURE__ */ jsx27("rect", { x: "14", y: "4", width: "7", height: "16", rx: "1" })
|
|
6856
|
-
]
|
|
6857
|
-
}
|
|
6858
|
-
),
|
|
7305
|
+
icon: columnIcon(false),
|
|
6859
7306
|
subtext: "\uBE14\uB85D\uC744 \uC88C\uC6B0 2\uB2E8\uC73C\uB85C \uBC30\uCE58"
|
|
6860
7307
|
};
|
|
6861
|
-
const
|
|
7308
|
+
const columnDividerItem = {
|
|
7309
|
+
title: "2\uB2E8 \uCEEC\uB7FC (\uAD6C\uBD84\uC120)",
|
|
7310
|
+
onItemClick: () => insertTwoColumns(editor, true),
|
|
7311
|
+
aliases: [
|
|
7312
|
+
"columns divider",
|
|
7313
|
+
"\uAD6C\uBD84\uC120",
|
|
7314
|
+
"\uCEEC\uB7FC \uAD6C\uBD84\uC120",
|
|
7315
|
+
"2\uB2E8 \uAD6C\uBD84\uC120",
|
|
7316
|
+
"divider"
|
|
7317
|
+
],
|
|
7318
|
+
group: "Basic blocks",
|
|
7319
|
+
icon: columnIcon(true),
|
|
7320
|
+
subtext: "\uAC00\uC6B4\uB370 \uC138\uB85C \uAD6C\uBD84\uC120\uC774 \uC788\uB294 2\uB2E8 \uCEEC\uB7FC"
|
|
7321
|
+
};
|
|
7322
|
+
const allItems = [
|
|
7323
|
+
...filtered,
|
|
7324
|
+
htmlPreviewItem,
|
|
7325
|
+
columnItem,
|
|
7326
|
+
columnDividerItem
|
|
7327
|
+
];
|
|
6862
7328
|
if (linkPreview?.apiEndpoint) {
|
|
6863
7329
|
allItems.push({
|
|
6864
7330
|
title: "Link Preview",
|