@underverse-ui/underverse 1.0.149 → 1.0.151
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/api-reference.json +1 -1
- package/dist/index.cjs +1516 -322
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +1517 -322
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1029,6 +1029,10 @@ var en_default = {
|
|
|
1029
1029
|
borderWidth: "Border Width",
|
|
1030
1030
|
borderColor: "Border Color",
|
|
1031
1031
|
clearBorder: "Clear Border",
|
|
1032
|
+
formula: "Formula",
|
|
1033
|
+
apply: "Apply",
|
|
1034
|
+
clear: "Clear",
|
|
1035
|
+
recalculate: "Recalculate",
|
|
1032
1036
|
done: "Done"
|
|
1033
1037
|
},
|
|
1034
1038
|
callout: {
|
|
@@ -1405,6 +1409,10 @@ var vi_default = {
|
|
|
1405
1409
|
borderWidth: "\u0110\u1ED9 d\xE0y vi\u1EC1n",
|
|
1406
1410
|
borderColor: "M\xE0u vi\u1EC1n",
|
|
1407
1411
|
clearBorder: "X\xF3a vi\u1EC1n",
|
|
1412
|
+
formula: "C\xF4ng th\u1EE9c",
|
|
1413
|
+
apply: "\xC1p d\u1EE5ng",
|
|
1414
|
+
clear: "X\xF3a",
|
|
1415
|
+
recalculate: "T\xEDnh l\u1EA1i",
|
|
1408
1416
|
done: "Xong"
|
|
1409
1417
|
},
|
|
1410
1418
|
callout: {
|
|
@@ -25836,6 +25844,8 @@ var import_core5 = require("@tiptap/core");
|
|
|
25836
25844
|
var import_state2 = require("@tiptap/pm/state");
|
|
25837
25845
|
|
|
25838
25846
|
// src/components/UEditor/clipboard-tables.ts
|
|
25847
|
+
var DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR = "#ffffff";
|
|
25848
|
+
var DEFAULT_HTML_TABLE_TEXT_COLOR = "#000000";
|
|
25839
25849
|
function getClipboardData(dataTransfer, type) {
|
|
25840
25850
|
try {
|
|
25841
25851
|
return dataTransfer.getData(type) ?? "";
|
|
@@ -25856,6 +25866,411 @@ function extractClipboardHtmlFragment(html) {
|
|
|
25856
25866
|
function normalizeClipboardCellText(value) {
|
|
25857
25867
|
return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\u00a0/g, " ").replace(/[ \t]+\n/g, "\n").replace(/\n[ \t]+/g, "\n").replace(/\n+$/g, "").replace(/^\n+/g, "").trim();
|
|
25858
25868
|
}
|
|
25869
|
+
function parseStyleDeclarations(styleText) {
|
|
25870
|
+
const declarations = /* @__PURE__ */ new Map();
|
|
25871
|
+
if (!styleText) return declarations;
|
|
25872
|
+
for (const declaration of styleText.split(";")) {
|
|
25873
|
+
const separatorIndex = declaration.indexOf(":");
|
|
25874
|
+
if (separatorIndex <= 0) continue;
|
|
25875
|
+
const property = declaration.slice(0, separatorIndex).trim().toLowerCase();
|
|
25876
|
+
const value = cleanStyleValue(declaration.slice(separatorIndex + 1));
|
|
25877
|
+
if (!property || !value) continue;
|
|
25878
|
+
declarations.set(property, value);
|
|
25879
|
+
}
|
|
25880
|
+
return declarations;
|
|
25881
|
+
}
|
|
25882
|
+
function mergeStyleDeclarations(...sources) {
|
|
25883
|
+
const declarations = /* @__PURE__ */ new Map();
|
|
25884
|
+
for (const source of sources) {
|
|
25885
|
+
if (!source) continue;
|
|
25886
|
+
for (const [property, value] of source.entries()) {
|
|
25887
|
+
declarations.set(property, value);
|
|
25888
|
+
}
|
|
25889
|
+
}
|
|
25890
|
+
return declarations;
|
|
25891
|
+
}
|
|
25892
|
+
function extractCssClassNames(selectorText) {
|
|
25893
|
+
const classNames = /* @__PURE__ */ new Set();
|
|
25894
|
+
const classNamePattern = /\.([_a-zA-Z-][\w-]*)/g;
|
|
25895
|
+
let match;
|
|
25896
|
+
while ((match = classNamePattern.exec(selectorText)) !== null) {
|
|
25897
|
+
classNames.add(match[1]);
|
|
25898
|
+
}
|
|
25899
|
+
return classNames;
|
|
25900
|
+
}
|
|
25901
|
+
function parseClipboardCssClassStyles(doc) {
|
|
25902
|
+
const styleMap = /* @__PURE__ */ new Map();
|
|
25903
|
+
for (const styleElement of Array.from(doc.querySelectorAll("style"))) {
|
|
25904
|
+
const cssText = (styleElement.textContent ?? "").replace(/<!--|-->/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
25905
|
+
const rulePattern = /([^{}]+)\{([^{}]*)\}/g;
|
|
25906
|
+
let match;
|
|
25907
|
+
while ((match = rulePattern.exec(cssText)) !== null) {
|
|
25908
|
+
const classNames = extractCssClassNames(match[1]);
|
|
25909
|
+
if (classNames.size === 0) continue;
|
|
25910
|
+
const declarations = parseStyleDeclarations(match[2]);
|
|
25911
|
+
if (declarations.size === 0) continue;
|
|
25912
|
+
for (const className of classNames) {
|
|
25913
|
+
styleMap.set(className, mergeStyleDeclarations(styleMap.get(className), declarations));
|
|
25914
|
+
}
|
|
25915
|
+
}
|
|
25916
|
+
}
|
|
25917
|
+
return styleMap;
|
|
25918
|
+
}
|
|
25919
|
+
function getElementStyleDeclarations(element, styleMap) {
|
|
25920
|
+
const classDeclarations = Array.from(element.classList).map((className) => styleMap.get(className));
|
|
25921
|
+
const inlineDeclarations = parseStyleDeclarations(element.getAttribute("style"));
|
|
25922
|
+
return mergeStyleDeclarations(...classDeclarations, inlineDeclarations);
|
|
25923
|
+
}
|
|
25924
|
+
function cleanStyleValue(value) {
|
|
25925
|
+
const normalized = value?.trim();
|
|
25926
|
+
if (!normalized) return null;
|
|
25927
|
+
if (/[\0<>;{}]/.test(normalized)) return null;
|
|
25928
|
+
if (/\b(?:expression|url|(?:repeating-)?(?:linear|radial|conic)-gradient)\s*\(/i.test(normalized)) return null;
|
|
25929
|
+
return normalized;
|
|
25930
|
+
}
|
|
25931
|
+
function normalizeColorValue(value) {
|
|
25932
|
+
const normalized = cleanStyleValue(value);
|
|
25933
|
+
if (!normalized) return null;
|
|
25934
|
+
if (/^(?:auto|inherit|initial|none|transparent|unset)$/i.test(normalized)) return null;
|
|
25935
|
+
return normalized;
|
|
25936
|
+
}
|
|
25937
|
+
function normalizeTextColorValue(value) {
|
|
25938
|
+
const normalized = normalizeColorValue(value);
|
|
25939
|
+
if (!normalized) return null;
|
|
25940
|
+
if (/^(?:automatic|windowtext|black|#000|#000000|rgb\(\s*0\s*,\s*0\s*,\s*0\s*\))$/i.test(normalized)) {
|
|
25941
|
+
return DEFAULT_HTML_TABLE_TEXT_COLOR;
|
|
25942
|
+
}
|
|
25943
|
+
return normalized;
|
|
25944
|
+
}
|
|
25945
|
+
function isWhiteColor(value) {
|
|
25946
|
+
if (!value) return false;
|
|
25947
|
+
return /^(?:white|#fff|#ffffff|rgb\(\s*255\s*,\s*255\s*,\s*255\s*\))$/i.test(value.trim());
|
|
25948
|
+
}
|
|
25949
|
+
function parseCssColorRgb(value) {
|
|
25950
|
+
const normalized = normalizeColorValue(value);
|
|
25951
|
+
if (!normalized) return null;
|
|
25952
|
+
const lowerColor = normalized.toLowerCase();
|
|
25953
|
+
if (lowerColor === "white") return { r: 255, g: 255, b: 255 };
|
|
25954
|
+
if (lowerColor === "black") return { r: 0, g: 0, b: 0 };
|
|
25955
|
+
const hexMatch = lowerColor.match(/^#([\da-f]{3}|[\da-f]{6})$/i);
|
|
25956
|
+
if (hexMatch) {
|
|
25957
|
+
const hex = hexMatch[1];
|
|
25958
|
+
const fullHex = hex.length === 3 ? hex.split("").map((part) => part + part).join("") : hex;
|
|
25959
|
+
return {
|
|
25960
|
+
r: Number.parseInt(fullHex.slice(0, 2), 16),
|
|
25961
|
+
g: Number.parseInt(fullHex.slice(2, 4), 16),
|
|
25962
|
+
b: Number.parseInt(fullHex.slice(4, 6), 16)
|
|
25963
|
+
};
|
|
25964
|
+
}
|
|
25965
|
+
const rgbMatch = lowerColor.match(/^rgba?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)/);
|
|
25966
|
+
if (rgbMatch) {
|
|
25967
|
+
return {
|
|
25968
|
+
r: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[1]))),
|
|
25969
|
+
g: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[2]))),
|
|
25970
|
+
b: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[3])))
|
|
25971
|
+
};
|
|
25972
|
+
}
|
|
25973
|
+
return null;
|
|
25974
|
+
}
|
|
25975
|
+
function getRelativeLuminance(value) {
|
|
25976
|
+
const rgb = parseCssColorRgb(value);
|
|
25977
|
+
if (!rgb) return null;
|
|
25978
|
+
const toLinear = (channel) => {
|
|
25979
|
+
const normalized = channel / 255;
|
|
25980
|
+
return normalized <= 0.03928 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
|
|
25981
|
+
};
|
|
25982
|
+
return 0.2126 * toLinear(rgb.r) + 0.7152 * toLinear(rgb.g) + 0.0722 * toLinear(rgb.b);
|
|
25983
|
+
}
|
|
25984
|
+
function isLightTextColor(value) {
|
|
25985
|
+
const luminance = getRelativeLuminance(value);
|
|
25986
|
+
return luminance !== null && luminance >= 0.72;
|
|
25987
|
+
}
|
|
25988
|
+
function isDarkReadableBackground(value) {
|
|
25989
|
+
const luminance = getRelativeLuminance(value);
|
|
25990
|
+
return luminance !== null && luminance <= 0.45;
|
|
25991
|
+
}
|
|
25992
|
+
function splitCssTokens(value) {
|
|
25993
|
+
const tokens = [];
|
|
25994
|
+
let current = "";
|
|
25995
|
+
let depth = 0;
|
|
25996
|
+
for (const char of value) {
|
|
25997
|
+
if (char === "(") depth += 1;
|
|
25998
|
+
if (char === ")") depth = Math.max(0, depth - 1);
|
|
25999
|
+
if (/\s/.test(char) && depth === 0) {
|
|
26000
|
+
if (current) {
|
|
26001
|
+
tokens.push(current);
|
|
26002
|
+
current = "";
|
|
26003
|
+
}
|
|
26004
|
+
continue;
|
|
26005
|
+
}
|
|
26006
|
+
current += char;
|
|
26007
|
+
}
|
|
26008
|
+
if (current) tokens.push(current);
|
|
26009
|
+
return tokens;
|
|
26010
|
+
}
|
|
26011
|
+
function extractColorFromShorthand(value) {
|
|
26012
|
+
const normalized = cleanStyleValue(value);
|
|
26013
|
+
if (!normalized) return null;
|
|
26014
|
+
const explicitColor = normalized.match(/#[\da-f]{3,8}\b|rgba?\([^)]+\)|hsla?\([^)]+\)/i);
|
|
26015
|
+
if (explicitColor) return explicitColor[0];
|
|
26016
|
+
const ignoredKeywords = /* @__PURE__ */ new Set([
|
|
26017
|
+
"border-box",
|
|
26018
|
+
"center",
|
|
26019
|
+
"contain",
|
|
26020
|
+
"content-box",
|
|
26021
|
+
"cover",
|
|
26022
|
+
"fixed",
|
|
26023
|
+
"inherit",
|
|
26024
|
+
"initial",
|
|
26025
|
+
"left",
|
|
26026
|
+
"local",
|
|
26027
|
+
"none",
|
|
26028
|
+
"no-repeat",
|
|
26029
|
+
"padding-box",
|
|
26030
|
+
"repeat",
|
|
26031
|
+
"repeat-x",
|
|
26032
|
+
"repeat-y",
|
|
26033
|
+
"right",
|
|
26034
|
+
"scroll",
|
|
26035
|
+
"top",
|
|
26036
|
+
"transparent",
|
|
26037
|
+
"unset"
|
|
26038
|
+
]);
|
|
26039
|
+
return splitCssTokens(normalized).find((token) => !ignoredKeywords.has(token.toLowerCase())) ?? null;
|
|
26040
|
+
}
|
|
26041
|
+
function getBackgroundColor(styles) {
|
|
26042
|
+
return normalizeColorValue(styles.get("background-color")) ?? normalizeColorValue(extractColorFromShorthand(styles.get("background")));
|
|
26043
|
+
}
|
|
26044
|
+
var BORDER_STYLES = /* @__PURE__ */ new Set([
|
|
26045
|
+
"dashed",
|
|
26046
|
+
"dotted",
|
|
26047
|
+
"double",
|
|
26048
|
+
"groove",
|
|
26049
|
+
"hidden",
|
|
26050
|
+
"inset",
|
|
26051
|
+
"none",
|
|
26052
|
+
"outset",
|
|
26053
|
+
"ridge",
|
|
26054
|
+
"solid"
|
|
26055
|
+
]);
|
|
26056
|
+
var BORDER_WIDTH_KEYWORDS = /* @__PURE__ */ new Set(["medium", "thick", "thin"]);
|
|
26057
|
+
function normalizeBorderStyle(value) {
|
|
26058
|
+
const normalized = cleanStyleValue(value);
|
|
26059
|
+
if (!normalized) return null;
|
|
26060
|
+
const styles = splitCssTokens(normalized).filter((token) => BORDER_STYLES.has(token.toLowerCase()));
|
|
26061
|
+
const usefulStyles = styles.filter((style) => !/^(?:hidden|none)$/i.test(style));
|
|
26062
|
+
return usefulStyles.length > 0 ? usefulStyles.join(" ") : null;
|
|
26063
|
+
}
|
|
26064
|
+
function normalizeBorderWidth(value) {
|
|
26065
|
+
const normalized = cleanStyleValue(value);
|
|
26066
|
+
if (!normalized) return null;
|
|
26067
|
+
const widths = splitCssTokens(normalized).filter((token) => {
|
|
26068
|
+
const lowerToken = token.toLowerCase();
|
|
26069
|
+
return BORDER_WIDTH_KEYWORDS.has(lowerToken) || /^\d*\.?\d+(?:px|pt|pc|in|cm|mm|em|rem)?$/i.test(token);
|
|
26070
|
+
});
|
|
26071
|
+
return widths.length > 0 ? widths.join(" ") : null;
|
|
26072
|
+
}
|
|
26073
|
+
function parseBorderShorthand(value) {
|
|
26074
|
+
const normalized = cleanStyleValue(value);
|
|
26075
|
+
if (!normalized) return null;
|
|
26076
|
+
const tokens = splitCssTokens(normalized);
|
|
26077
|
+
let borderStyle = null;
|
|
26078
|
+
let borderWidth = null;
|
|
26079
|
+
const colorTokens = [];
|
|
26080
|
+
for (const token of tokens) {
|
|
26081
|
+
const lowerToken = token.toLowerCase();
|
|
26082
|
+
if (!borderStyle && BORDER_STYLES.has(lowerToken)) {
|
|
26083
|
+
borderStyle = lowerToken;
|
|
26084
|
+
continue;
|
|
26085
|
+
}
|
|
26086
|
+
if (!borderWidth && (BORDER_WIDTH_KEYWORDS.has(lowerToken) || /^\d*\.?\d+(?:px|pt|pc|in|cm|mm|em|rem)?$/i.test(token))) {
|
|
26087
|
+
borderWidth = token;
|
|
26088
|
+
continue;
|
|
26089
|
+
}
|
|
26090
|
+
colorTokens.push(token);
|
|
26091
|
+
}
|
|
26092
|
+
if (borderStyle && /^(?:hidden|none)$/i.test(borderStyle)) return null;
|
|
26093
|
+
return {
|
|
26094
|
+
borderColor: normalizeColorValue(colorTokens.join(" ")),
|
|
26095
|
+
borderStyle,
|
|
26096
|
+
borderWidth
|
|
26097
|
+
};
|
|
26098
|
+
}
|
|
26099
|
+
function getFirstParsedBorder(styles) {
|
|
26100
|
+
for (const property of ["border", "border-top", "border-right", "border-bottom", "border-left"]) {
|
|
26101
|
+
const border = parseBorderShorthand(styles.get(property));
|
|
26102
|
+
if (border) return border;
|
|
26103
|
+
}
|
|
26104
|
+
return null;
|
|
26105
|
+
}
|
|
26106
|
+
function getBorderAttrs(styles) {
|
|
26107
|
+
const parsedBorder = getFirstParsedBorder(styles);
|
|
26108
|
+
return {
|
|
26109
|
+
borderColor: normalizeColorValue(styles.get("border-color")) ?? parsedBorder?.borderColor ?? void 0,
|
|
26110
|
+
borderStyle: normalizeBorderStyle(styles.get("border-style")) ?? parsedBorder?.borderStyle ?? void 0,
|
|
26111
|
+
borderWidth: normalizeBorderWidth(styles.get("border-width")) ?? parsedBorder?.borderWidth ?? void 0
|
|
26112
|
+
};
|
|
26113
|
+
}
|
|
26114
|
+
function parsePositiveInteger(value, max = 100) {
|
|
26115
|
+
if (!value) return null;
|
|
26116
|
+
const parsed = Number.parseInt(value, 10);
|
|
26117
|
+
if (!Number.isFinite(parsed) || parsed < 1) return null;
|
|
26118
|
+
return Math.min(parsed, max);
|
|
26119
|
+
}
|
|
26120
|
+
function parseCssSize(value) {
|
|
26121
|
+
const normalized = cleanStyleValue(value);
|
|
26122
|
+
if (!normalized) return null;
|
|
26123
|
+
const match = normalized.match(/^(\d+(?:\.\d+)?)(px|pt)?$/i);
|
|
26124
|
+
if (!match) return null;
|
|
26125
|
+
const amount = Number.parseFloat(match[1]);
|
|
26126
|
+
if (!Number.isFinite(amount) || amount <= 0) return null;
|
|
26127
|
+
return Math.round(match[2]?.toLowerCase() === "pt" ? amount * (4 / 3) : amount);
|
|
26128
|
+
}
|
|
26129
|
+
function getCellWidth(cell, styles, colspan) {
|
|
26130
|
+
if (colspan !== 1) return null;
|
|
26131
|
+
const width = parseCssSize(cell.getAttribute("data-colwidth") ?? cell.getAttribute("width") ?? styles.get("width"));
|
|
26132
|
+
return width ? [width] : null;
|
|
26133
|
+
}
|
|
26134
|
+
function getTableRowAttrs(row, styles) {
|
|
26135
|
+
const rowHeight = parseCssSize(
|
|
26136
|
+
row.getAttribute("data-row-height") ?? row.getAttribute("height") ?? styles.get("height")
|
|
26137
|
+
);
|
|
26138
|
+
return rowHeight ? { rowHeight } : void 0;
|
|
26139
|
+
}
|
|
26140
|
+
function getTableCellAttrs(cell, styles, defaultBackgroundColor) {
|
|
26141
|
+
const colspan = parsePositiveInteger(cell.getAttribute("colspan")) ?? 1;
|
|
26142
|
+
const rowspan = parsePositiveInteger(cell.getAttribute("rowspan")) ?? 1;
|
|
26143
|
+
const backgroundColor = getBackgroundColor(styles) ?? normalizeColorValue(cell.getAttribute("data-background-color")) ?? normalizeColorValue(cell.getAttribute("bgcolor")) ?? defaultBackgroundColor;
|
|
26144
|
+
const borderAttrs = getBorderAttrs(styles);
|
|
26145
|
+
const colwidth = getCellWidth(cell, styles, colspan);
|
|
26146
|
+
const attrs = {};
|
|
26147
|
+
if (backgroundColor) attrs.backgroundColor = backgroundColor;
|
|
26148
|
+
if (borderAttrs.borderColor) attrs.borderColor = borderAttrs.borderColor;
|
|
26149
|
+
if (borderAttrs.borderStyle) attrs.borderStyle = borderAttrs.borderStyle;
|
|
26150
|
+
if (borderAttrs.borderWidth) attrs.borderWidth = borderAttrs.borderWidth;
|
|
26151
|
+
if (colspan > 1) attrs.colspan = colspan;
|
|
26152
|
+
if (rowspan > 1) attrs.rowspan = rowspan;
|
|
26153
|
+
if (colwidth) attrs.colwidth = colwidth;
|
|
26154
|
+
return Object.keys(attrs).length > 0 ? attrs : void 0;
|
|
26155
|
+
}
|
|
26156
|
+
function marksEqual(left, right) {
|
|
26157
|
+
return JSON.stringify(left ?? []) === JSON.stringify(right ?? []);
|
|
26158
|
+
}
|
|
26159
|
+
function mergeMarks(base2, additions) {
|
|
26160
|
+
const next = [...base2 ?? []];
|
|
26161
|
+
for (const addition of additions ?? []) {
|
|
26162
|
+
const existingIndex = next.findIndex((mark) => mark.type === addition.type);
|
|
26163
|
+
if (existingIndex >= 0) {
|
|
26164
|
+
const existingMark = next[existingIndex];
|
|
26165
|
+
next[existingIndex] = {
|
|
26166
|
+
...existingMark,
|
|
26167
|
+
attrs: {
|
|
26168
|
+
...existingMark.attrs ?? {},
|
|
26169
|
+
...addition.attrs ?? {}
|
|
26170
|
+
}
|
|
26171
|
+
};
|
|
26172
|
+
continue;
|
|
26173
|
+
}
|
|
26174
|
+
next.push(addition);
|
|
26175
|
+
}
|
|
26176
|
+
return next.length > 0 ? next : void 0;
|
|
26177
|
+
}
|
|
26178
|
+
function getMarkColor(marks, markType) {
|
|
26179
|
+
const mark = marks?.find((candidate) => candidate.type === markType);
|
|
26180
|
+
const color = mark?.attrs?.color;
|
|
26181
|
+
return typeof color === "string" ? color : null;
|
|
26182
|
+
}
|
|
26183
|
+
function replaceTextStyleColor(marks, color) {
|
|
26184
|
+
let replaced = false;
|
|
26185
|
+
const next = (marks ?? []).map((mark) => {
|
|
26186
|
+
if (mark.type !== "textStyle") return mark;
|
|
26187
|
+
replaced = true;
|
|
26188
|
+
return {
|
|
26189
|
+
...mark,
|
|
26190
|
+
attrs: {
|
|
26191
|
+
...mark.attrs ?? {},
|
|
26192
|
+
color
|
|
26193
|
+
}
|
|
26194
|
+
};
|
|
26195
|
+
});
|
|
26196
|
+
if (!replaced) {
|
|
26197
|
+
next.unshift({ type: "textStyle", attrs: { color } });
|
|
26198
|
+
}
|
|
26199
|
+
return next;
|
|
26200
|
+
}
|
|
26201
|
+
function ensureReadableSpreadsheetSegments(segments, cellBackgroundColor) {
|
|
26202
|
+
return segments.map((segment) => {
|
|
26203
|
+
const textColor = getMarkColor(segment.marks, "textStyle");
|
|
26204
|
+
if (!isLightTextColor(textColor)) return segment;
|
|
26205
|
+
const inlineBackgroundColor = getMarkColor(segment.marks, "highlight");
|
|
26206
|
+
if (isDarkReadableBackground(inlineBackgroundColor) || isDarkReadableBackground(cellBackgroundColor)) {
|
|
26207
|
+
return segment;
|
|
26208
|
+
}
|
|
26209
|
+
return {
|
|
26210
|
+
...segment,
|
|
26211
|
+
marks: replaceTextStyleColor(segment.marks, DEFAULT_HTML_TABLE_TEXT_COLOR)
|
|
26212
|
+
};
|
|
26213
|
+
});
|
|
26214
|
+
}
|
|
26215
|
+
function getElementInlineMarks(element, styles) {
|
|
26216
|
+
const marks = [];
|
|
26217
|
+
const tagName = element.tagName;
|
|
26218
|
+
const color = normalizeTextColorValue(styles.get("color") ?? element.getAttribute("color"));
|
|
26219
|
+
const backgroundColor = getBackgroundColor(styles);
|
|
26220
|
+
const fontWeight = styles.get("font-weight")?.toLowerCase();
|
|
26221
|
+
const fontStyle = styles.get("font-style")?.toLowerCase();
|
|
26222
|
+
const textDecoration = styles.get("text-decoration")?.toLowerCase();
|
|
26223
|
+
if (color) {
|
|
26224
|
+
marks.push({ type: "textStyle", attrs: { color } });
|
|
26225
|
+
}
|
|
26226
|
+
if (backgroundColor && !isWhiteColor(backgroundColor)) {
|
|
26227
|
+
marks.push({ type: "highlight", attrs: { color: backgroundColor } });
|
|
26228
|
+
}
|
|
26229
|
+
if (tagName === "B" || tagName === "STRONG" || fontWeight === "bold" || /^\d+$/.test(fontWeight ?? "") && Number(fontWeight) >= 600) {
|
|
26230
|
+
marks.push({ type: "bold" });
|
|
26231
|
+
}
|
|
26232
|
+
if (tagName === "I" || tagName === "EM" || fontStyle === "italic") {
|
|
26233
|
+
marks.push({ type: "italic" });
|
|
26234
|
+
}
|
|
26235
|
+
if (tagName === "U" || textDecoration?.includes("underline")) {
|
|
26236
|
+
marks.push({ type: "underline" });
|
|
26237
|
+
}
|
|
26238
|
+
return marks.length > 0 ? marks : void 0;
|
|
26239
|
+
}
|
|
26240
|
+
function appendTextSegment(segments, segment) {
|
|
26241
|
+
if (!segment.text) return;
|
|
26242
|
+
const lastSegment = segments[segments.length - 1];
|
|
26243
|
+
if (lastSegment && marksEqual(lastSegment.marks, segment.marks)) {
|
|
26244
|
+
lastSegment.text += segment.text;
|
|
26245
|
+
return;
|
|
26246
|
+
}
|
|
26247
|
+
segments.push(segment);
|
|
26248
|
+
}
|
|
26249
|
+
function segmentsEndWithNewline(segments) {
|
|
26250
|
+
return segments.length > 0 && segments[segments.length - 1].text.endsWith("\n");
|
|
26251
|
+
}
|
|
26252
|
+
function normalizeClipboardTextSegments(segments) {
|
|
26253
|
+
const normalizedSegments = [];
|
|
26254
|
+
for (const segment of segments) {
|
|
26255
|
+
appendTextSegment(normalizedSegments, {
|
|
26256
|
+
text: segment.text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\u00a0/g, " "),
|
|
26257
|
+
marks: segment.marks
|
|
26258
|
+
});
|
|
26259
|
+
}
|
|
26260
|
+
while (normalizedSegments.length > 0) {
|
|
26261
|
+
const firstSegment = normalizedSegments[0];
|
|
26262
|
+
firstSegment.text = firstSegment.text.replace(/^\s+/, "");
|
|
26263
|
+
if (firstSegment.text) break;
|
|
26264
|
+
normalizedSegments.shift();
|
|
26265
|
+
}
|
|
26266
|
+
while (normalizedSegments.length > 0) {
|
|
26267
|
+
const lastSegment = normalizedSegments[normalizedSegments.length - 1];
|
|
26268
|
+
lastSegment.text = lastSegment.text.replace(/\s+$/, "");
|
|
26269
|
+
if (lastSegment.text) break;
|
|
26270
|
+
normalizedSegments.pop();
|
|
26271
|
+
}
|
|
26272
|
+
return normalizedSegments;
|
|
26273
|
+
}
|
|
25859
26274
|
function getClipboardCellText(node) {
|
|
25860
26275
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
25861
26276
|
return node.textContent ?? "";
|
|
@@ -25873,57 +26288,205 @@ function getClipboardCellText(node) {
|
|
|
25873
26288
|
}
|
|
25874
26289
|
return childText;
|
|
25875
26290
|
}
|
|
25876
|
-
function
|
|
26291
|
+
function getClipboardCellSegments(node, styleMap, inheritedMarks) {
|
|
26292
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
26293
|
+
return [{ text: node.textContent ?? "", marks: inheritedMarks }];
|
|
26294
|
+
}
|
|
26295
|
+
if (!(node instanceof HTMLElement)) {
|
|
26296
|
+
return [];
|
|
26297
|
+
}
|
|
26298
|
+
if (node.tagName === "BR") {
|
|
26299
|
+
return [{ text: "\n", marks: inheritedMarks }];
|
|
26300
|
+
}
|
|
26301
|
+
const styles = getElementStyleDeclarations(node, styleMap);
|
|
26302
|
+
const marks = mergeMarks(inheritedMarks, getElementInlineMarks(node, styles));
|
|
26303
|
+
const segments = [];
|
|
26304
|
+
for (const childNode of Array.from(node.childNodes)) {
|
|
26305
|
+
for (const segment of getClipboardCellSegments(childNode, styleMap, marks)) {
|
|
26306
|
+
appendTextSegment(segments, segment);
|
|
26307
|
+
}
|
|
26308
|
+
}
|
|
26309
|
+
if ((node.tagName === "P" || node.tagName === "DIV" || node.tagName === "LI") && segments.length > 0 && !segmentsEndWithNewline(segments)) {
|
|
26310
|
+
appendTextSegment(segments, { text: "\n" });
|
|
26311
|
+
}
|
|
26312
|
+
return segments;
|
|
26313
|
+
}
|
|
26314
|
+
function getClipboardCellChildSegments(cell, styleMap, inheritedMarks) {
|
|
26315
|
+
const segments = [];
|
|
26316
|
+
for (const childNode of Array.from(cell.childNodes)) {
|
|
26317
|
+
for (const segment of getClipboardCellSegments(childNode, styleMap, inheritedMarks)) {
|
|
26318
|
+
appendTextSegment(segments, segment);
|
|
26319
|
+
}
|
|
26320
|
+
}
|
|
26321
|
+
return normalizeClipboardTextSegments(segments);
|
|
26322
|
+
}
|
|
26323
|
+
function getHtmlTableRows(table, styleMap) {
|
|
25877
26324
|
const rows = Array.from(table.querySelectorAll("tr")).map(
|
|
25878
|
-
(row) =>
|
|
25879
|
-
|
|
25880
|
-
|
|
25881
|
-
|
|
26325
|
+
(row) => ({
|
|
26326
|
+
attrs: getTableRowAttrs(row, getElementStyleDeclarations(row, styleMap)),
|
|
26327
|
+
cells: Array.from(row.children).filter((cell) => cell instanceof HTMLTableCellElement).map((cell) => {
|
|
26328
|
+
const styles = getElementStyleDeclarations(cell, styleMap);
|
|
26329
|
+
const textColor = normalizeTextColorValue(styles.get("color")) ?? DEFAULT_HTML_TABLE_TEXT_COLOR;
|
|
26330
|
+
const inheritedMarks = [{ type: "textStyle", attrs: { color: textColor } }];
|
|
26331
|
+
const attrs = getTableCellAttrs(cell, styles, DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR);
|
|
26332
|
+
const segments = ensureReadableSpreadsheetSegments(
|
|
26333
|
+
getClipboardCellChildSegments(cell, styleMap, inheritedMarks),
|
|
26334
|
+
attrs?.backgroundColor
|
|
26335
|
+
);
|
|
26336
|
+
return {
|
|
26337
|
+
text: normalizeClipboardCellText(getClipboardCellText(cell)),
|
|
26338
|
+
isHeader: cell.tagName === "TH",
|
|
26339
|
+
attrs,
|
|
26340
|
+
segments: segments.length > 0 ? segments : void 0,
|
|
26341
|
+
textColor
|
|
26342
|
+
};
|
|
26343
|
+
})
|
|
26344
|
+
})
|
|
25882
26345
|
);
|
|
25883
|
-
return rows.filter((row) => row.length > 0);
|
|
26346
|
+
return rows.filter((row) => row.cells.length > 0);
|
|
26347
|
+
}
|
|
26348
|
+
function createTextMarks(cell) {
|
|
26349
|
+
return cell.textColor ? [{ type: "textStyle", attrs: { color: cell.textColor } }] : void 0;
|
|
25884
26350
|
}
|
|
25885
|
-
function createParagraphContent(text) {
|
|
26351
|
+
function createParagraphContent(text, marks) {
|
|
25886
26352
|
return text ? {
|
|
25887
26353
|
type: "paragraph",
|
|
25888
|
-
content: [{ type: "text", text }]
|
|
26354
|
+
content: [{ type: "text", text, ...marks ? { marks } : {} }]
|
|
25889
26355
|
} : { type: "paragraph" };
|
|
25890
26356
|
}
|
|
26357
|
+
function createParagraphContentFromSegments(segments) {
|
|
26358
|
+
const paragraphs = [[]];
|
|
26359
|
+
for (const segment of segments) {
|
|
26360
|
+
const parts = segment.text.split("\n");
|
|
26361
|
+
parts.forEach((part, index) => {
|
|
26362
|
+
if (part) {
|
|
26363
|
+
paragraphs[paragraphs.length - 1].push({ text: part, marks: segment.marks });
|
|
26364
|
+
}
|
|
26365
|
+
if (index < parts.length - 1) {
|
|
26366
|
+
paragraphs.push([]);
|
|
26367
|
+
}
|
|
26368
|
+
});
|
|
26369
|
+
}
|
|
26370
|
+
return paragraphs.map((paragraphSegments) => {
|
|
26371
|
+
const content = paragraphSegments.map((segment) => ({
|
|
26372
|
+
type: "text",
|
|
26373
|
+
text: segment.text,
|
|
26374
|
+
...segment.marks ? { marks: segment.marks } : {}
|
|
26375
|
+
}));
|
|
26376
|
+
return content.length > 0 ? { type: "paragraph", content } : { type: "paragraph" };
|
|
26377
|
+
});
|
|
26378
|
+
}
|
|
25891
26379
|
function createTableCellContent(cell) {
|
|
25892
26380
|
const lines = cell.text.split("\n");
|
|
25893
|
-
const
|
|
26381
|
+
const marks = createTextMarks(cell);
|
|
26382
|
+
const paragraphs = cell.segments && cell.segments.length > 0 ? createParagraphContentFromSegments(cell.segments) : (lines.length > 0 ? lines : [""]).map((line) => createParagraphContent(line, marks));
|
|
25894
26383
|
return {
|
|
25895
26384
|
type: cell.isHeader ? "tableHeader" : "tableCell",
|
|
26385
|
+
...cell.attrs ? { attrs: cell.attrs } : {},
|
|
25896
26386
|
content: paragraphs.length > 0 ? paragraphs : [{ type: "paragraph" }]
|
|
25897
26387
|
};
|
|
25898
26388
|
}
|
|
25899
|
-
function
|
|
25900
|
-
const
|
|
26389
|
+
function getRowspanLimitedCell(cell, remainingRowCount) {
|
|
26390
|
+
const attrs = cell.attrs;
|
|
26391
|
+
if (!attrs?.rowspan || attrs.rowspan <= remainingRowCount) return cell;
|
|
26392
|
+
if (remainingRowCount <= 1) {
|
|
26393
|
+
const { rowspan: _rowspan, ...nextAttrs } = attrs;
|
|
26394
|
+
return {
|
|
26395
|
+
...cell,
|
|
26396
|
+
attrs: Object.keys(nextAttrs).length > 0 ? nextAttrs : void 0
|
|
26397
|
+
};
|
|
26398
|
+
}
|
|
26399
|
+
return {
|
|
26400
|
+
...cell,
|
|
26401
|
+
attrs: {
|
|
26402
|
+
...attrs,
|
|
26403
|
+
rowspan: remainingRowCount
|
|
26404
|
+
}
|
|
26405
|
+
};
|
|
26406
|
+
}
|
|
26407
|
+
function normalizeTableRows(rows) {
|
|
26408
|
+
const positionedRows = [];
|
|
26409
|
+
let rowspans = [];
|
|
26410
|
+
let columnCount = 0;
|
|
26411
|
+
rows.forEach((row, rowIndex) => {
|
|
26412
|
+
const coveredColumns = rowspans.map((span) => span > 0);
|
|
26413
|
+
const nextRowspans = rowspans.map((span) => Math.max(0, span - 1));
|
|
26414
|
+
const positionedCells = [];
|
|
26415
|
+
let columnIndex = 0;
|
|
26416
|
+
for (const rawCell of row.cells) {
|
|
26417
|
+
while (coveredColumns[columnIndex]) columnIndex += 1;
|
|
26418
|
+
const remainingRowCount = rows.length - rowIndex;
|
|
26419
|
+
const cell = getRowspanLimitedCell(rawCell, remainingRowCount);
|
|
26420
|
+
const colspan = Math.max(1, cell.attrs?.colspan ?? 1);
|
|
26421
|
+
const rowspan = Math.max(1, cell.attrs?.rowspan ?? 1);
|
|
26422
|
+
positionedCells.push({ startColumn: columnIndex, colspan, cell });
|
|
26423
|
+
if (rowspan > 1) {
|
|
26424
|
+
for (let offset = 0; offset < colspan; offset += 1) {
|
|
26425
|
+
const spannedColumn = columnIndex + offset;
|
|
26426
|
+
nextRowspans[spannedColumn] = Math.max(nextRowspans[spannedColumn] ?? 0, rowspan - 1);
|
|
26427
|
+
}
|
|
26428
|
+
}
|
|
26429
|
+
columnIndex += colspan;
|
|
26430
|
+
}
|
|
26431
|
+
const lastCoveredColumn = coveredColumns.reduce((lastIndex, covered, index) => covered ? index : lastIndex, -1);
|
|
26432
|
+
const lastFutureRowspanColumn = nextRowspans.reduce((lastIndex, span, index) => span > 0 ? index : lastIndex, -1);
|
|
26433
|
+
columnCount = Math.max(columnCount, columnIndex, lastCoveredColumn + 1, lastFutureRowspanColumn + 1);
|
|
26434
|
+
positionedRows.push({
|
|
26435
|
+
attrs: row.attrs,
|
|
26436
|
+
cells: positionedCells,
|
|
26437
|
+
coveredColumns
|
|
26438
|
+
});
|
|
26439
|
+
rowspans = nextRowspans;
|
|
26440
|
+
});
|
|
26441
|
+
return { positionedRows, columnCount };
|
|
26442
|
+
}
|
|
26443
|
+
function createNormalizedRowContent(row, columnCount, fillerCellAttrs) {
|
|
26444
|
+
const content = [];
|
|
26445
|
+
const cellByStartColumn = new Map(row.cells.map((cell) => [cell.startColumn, cell]));
|
|
26446
|
+
let columnIndex = 0;
|
|
26447
|
+
while (columnIndex < columnCount) {
|
|
26448
|
+
if (row.coveredColumns[columnIndex]) {
|
|
26449
|
+
columnIndex += 1;
|
|
26450
|
+
continue;
|
|
26451
|
+
}
|
|
26452
|
+
const positionedCell = cellByStartColumn.get(columnIndex);
|
|
26453
|
+
if (positionedCell) {
|
|
26454
|
+
content.push(createTableCellContent(positionedCell.cell));
|
|
26455
|
+
columnIndex += positionedCell.colspan;
|
|
26456
|
+
continue;
|
|
26457
|
+
}
|
|
26458
|
+
content.push(createTableCellContent({ text: "", isHeader: false, attrs: fillerCellAttrs }));
|
|
26459
|
+
columnIndex += 1;
|
|
26460
|
+
}
|
|
26461
|
+
return content;
|
|
26462
|
+
}
|
|
26463
|
+
function createTableContent(rows, minColumnCount = 1, fillerCellAttrs) {
|
|
26464
|
+
const tableRows = rows.filter((row) => row.cells.length > 0);
|
|
25901
26465
|
if (tableRows.length === 0) return null;
|
|
25902
|
-
const
|
|
26466
|
+
const { positionedRows, columnCount } = normalizeTableRows(tableRows);
|
|
25903
26467
|
if (columnCount < minColumnCount) return null;
|
|
25904
26468
|
return {
|
|
25905
26469
|
type: "table",
|
|
25906
|
-
content:
|
|
25907
|
-
|
|
25908
|
-
|
|
25909
|
-
|
|
25910
|
-
|
|
25911
|
-
return {
|
|
25912
|
-
type: "tableRow",
|
|
25913
|
-
content: normalizedRow.map(createTableCellContent)
|
|
25914
|
-
};
|
|
25915
|
-
})
|
|
26470
|
+
content: positionedRows.map((row) => ({
|
|
26471
|
+
type: "tableRow",
|
|
26472
|
+
...row.attrs ? { attrs: row.attrs } : {},
|
|
26473
|
+
content: createNormalizedRowContent(row, columnCount, fillerCellAttrs)
|
|
26474
|
+
}))
|
|
25916
26475
|
};
|
|
25917
26476
|
}
|
|
25918
26477
|
function getClipboardTableContent(dataTransfer) {
|
|
25919
26478
|
const html = getClipboardData(dataTransfer, "text/html");
|
|
25920
26479
|
if (!/<table(?:\s|>)/i.test(html)) return null;
|
|
25921
26480
|
if (typeof DOMParser === "undefined") return null;
|
|
26481
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
25922
26482
|
const fragment = extractClipboardHtmlFragment(html);
|
|
25923
|
-
const
|
|
25924
|
-
const
|
|
26483
|
+
const fragmentDoc = new DOMParser().parseFromString(fragment, "text/html");
|
|
26484
|
+
const styleMap = parseClipboardCssClassStyles(doc);
|
|
26485
|
+
const table = fragmentDoc.querySelector("table") ?? doc.querySelector("table");
|
|
25925
26486
|
if (!(table instanceof HTMLTableElement)) return null;
|
|
25926
|
-
return createTableContent(getHtmlTableRows(table)
|
|
26487
|
+
return createTableContent(getHtmlTableRows(table, styleMap), 1, {
|
|
26488
|
+
backgroundColor: DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR
|
|
26489
|
+
});
|
|
25927
26490
|
}
|
|
25928
26491
|
function parseClipboardTsvRows(text) {
|
|
25929
26492
|
const rows = [];
|
|
@@ -25980,7 +26543,9 @@ function getClipboardTsvTableContent(dataTransfer) {
|
|
|
25980
26543
|
if (!text.includes(" ")) return null;
|
|
25981
26544
|
const rows = parseClipboardTsvRows(text);
|
|
25982
26545
|
return createTableContent(
|
|
25983
|
-
rows.map((row) =>
|
|
26546
|
+
rows.map((row) => ({
|
|
26547
|
+
cells: row.map((cell) => ({ text: normalizeClipboardCellText(cell), isHeader: false }))
|
|
26548
|
+
})),
|
|
25984
26549
|
2
|
|
25985
26550
|
);
|
|
25986
26551
|
}
|
|
@@ -27255,6 +27820,46 @@ var CustomTableCell = import_extension_table_cell.default.extend({
|
|
|
27255
27820
|
"data-border-width": attributes.borderWidth
|
|
27256
27821
|
};
|
|
27257
27822
|
}
|
|
27823
|
+
},
|
|
27824
|
+
cellId: {
|
|
27825
|
+
default: null,
|
|
27826
|
+
parseHTML: (element) => element.getAttribute("data-cell-id") || null,
|
|
27827
|
+
renderHTML: (attributes) => {
|
|
27828
|
+
if (!attributes.cellId) return {};
|
|
27829
|
+
return {
|
|
27830
|
+
"data-cell-id": attributes.cellId
|
|
27831
|
+
};
|
|
27832
|
+
}
|
|
27833
|
+
},
|
|
27834
|
+
numberFormat: {
|
|
27835
|
+
default: null,
|
|
27836
|
+
parseHTML: (element) => element.getAttribute("data-number-format") || null,
|
|
27837
|
+
renderHTML: (attributes) => {
|
|
27838
|
+
if (!attributes.numberFormat) return {};
|
|
27839
|
+
return {
|
|
27840
|
+
"data-number-format": attributes.numberFormat
|
|
27841
|
+
};
|
|
27842
|
+
}
|
|
27843
|
+
},
|
|
27844
|
+
formula: {
|
|
27845
|
+
default: null,
|
|
27846
|
+
parseHTML: (element) => element.getAttribute("data-formula") || null,
|
|
27847
|
+
renderHTML: (attributes) => {
|
|
27848
|
+
if (!attributes.formula) return {};
|
|
27849
|
+
return {
|
|
27850
|
+
"data-formula": attributes.formula
|
|
27851
|
+
};
|
|
27852
|
+
}
|
|
27853
|
+
},
|
|
27854
|
+
computedValue: {
|
|
27855
|
+
default: null,
|
|
27856
|
+
parseHTML: (element) => element.getAttribute("data-computed-value") || null,
|
|
27857
|
+
renderHTML: (attributes) => {
|
|
27858
|
+
if (!attributes.computedValue) return {};
|
|
27859
|
+
return {
|
|
27860
|
+
"data-computed-value": attributes.computedValue
|
|
27861
|
+
};
|
|
27862
|
+
}
|
|
27258
27863
|
}
|
|
27259
27864
|
};
|
|
27260
27865
|
},
|
|
@@ -27322,6 +27927,46 @@ var CustomTableHeader = import_extension_table_header.default.extend({
|
|
|
27322
27927
|
"data-border-width": attributes.borderWidth
|
|
27323
27928
|
};
|
|
27324
27929
|
}
|
|
27930
|
+
},
|
|
27931
|
+
cellId: {
|
|
27932
|
+
default: null,
|
|
27933
|
+
parseHTML: (element) => element.getAttribute("data-cell-id") || null,
|
|
27934
|
+
renderHTML: (attributes) => {
|
|
27935
|
+
if (!attributes.cellId) return {};
|
|
27936
|
+
return {
|
|
27937
|
+
"data-cell-id": attributes.cellId
|
|
27938
|
+
};
|
|
27939
|
+
}
|
|
27940
|
+
},
|
|
27941
|
+
numberFormat: {
|
|
27942
|
+
default: null,
|
|
27943
|
+
parseHTML: (element) => element.getAttribute("data-number-format") || null,
|
|
27944
|
+
renderHTML: (attributes) => {
|
|
27945
|
+
if (!attributes.numberFormat) return {};
|
|
27946
|
+
return {
|
|
27947
|
+
"data-number-format": attributes.numberFormat
|
|
27948
|
+
};
|
|
27949
|
+
}
|
|
27950
|
+
},
|
|
27951
|
+
formula: {
|
|
27952
|
+
default: null,
|
|
27953
|
+
parseHTML: (element) => element.getAttribute("data-formula") || null,
|
|
27954
|
+
renderHTML: (attributes) => {
|
|
27955
|
+
if (!attributes.formula) return {};
|
|
27956
|
+
return {
|
|
27957
|
+
"data-formula": attributes.formula
|
|
27958
|
+
};
|
|
27959
|
+
}
|
|
27960
|
+
},
|
|
27961
|
+
computedValue: {
|
|
27962
|
+
default: null,
|
|
27963
|
+
parseHTML: (element) => element.getAttribute("data-computed-value") || null,
|
|
27964
|
+
renderHTML: (attributes) => {
|
|
27965
|
+
if (!attributes.computedValue) return {};
|
|
27966
|
+
return {
|
|
27967
|
+
"data-computed-value": attributes.computedValue
|
|
27968
|
+
};
|
|
27969
|
+
}
|
|
27325
27970
|
}
|
|
27326
27971
|
};
|
|
27327
27972
|
},
|
|
@@ -28034,6 +28679,34 @@ function collectChildren(node) {
|
|
|
28034
28679
|
function createEmptyCellNode(cellNode) {
|
|
28035
28680
|
return cellNode.type.createAndFill(cellNode.attrs) ?? cellNode;
|
|
28036
28681
|
}
|
|
28682
|
+
function createCellCopyForColumnDuplicate(cellNode) {
|
|
28683
|
+
return cellNode.type.create(cellNode.attrs, cellNode.content);
|
|
28684
|
+
}
|
|
28685
|
+
function getTableRows(tableNode) {
|
|
28686
|
+
const rows = [];
|
|
28687
|
+
tableNode.forEach((rowNode, rowOffset) => {
|
|
28688
|
+
const cells = [];
|
|
28689
|
+
rowNode.forEach((cellNode, cellOffset, index) => {
|
|
28690
|
+
cells.push({
|
|
28691
|
+
index,
|
|
28692
|
+
node: cellNode,
|
|
28693
|
+
relativePos: rowOffset + 1 + cellOffset
|
|
28694
|
+
});
|
|
28695
|
+
});
|
|
28696
|
+
rows.push({
|
|
28697
|
+
node: rowNode,
|
|
28698
|
+
cells
|
|
28699
|
+
});
|
|
28700
|
+
});
|
|
28701
|
+
return rows;
|
|
28702
|
+
}
|
|
28703
|
+
function safeFindCell(map, relativePos) {
|
|
28704
|
+
try {
|
|
28705
|
+
return map.findCell(relativePos);
|
|
28706
|
+
} catch {
|
|
28707
|
+
return null;
|
|
28708
|
+
}
|
|
28709
|
+
}
|
|
28037
28710
|
function getSelectedTableRect(editor) {
|
|
28038
28711
|
const cellSelection = getCellSelectionPositions(editor.state.selection);
|
|
28039
28712
|
if (cellSelection) {
|
|
@@ -28159,34 +28832,49 @@ function duplicateTableRowAt(editor, rowIndex, cellPos) {
|
|
|
28159
28832
|
}
|
|
28160
28833
|
function clearTableRowAt(editor, rowIndex, cellPos) {
|
|
28161
28834
|
return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
|
|
28162
|
-
const
|
|
28163
|
-
|
|
28164
|
-
|
|
28165
|
-
|
|
28166
|
-
|
|
28835
|
+
const map = import_tables.TableMap.get(tableNode);
|
|
28836
|
+
if (rowIndex < 0 || rowIndex >= map.height) return null;
|
|
28837
|
+
const rows = getTableRows(tableNode).map((rowInfo) => {
|
|
28838
|
+
const cells = collectChildren(rowInfo.node);
|
|
28839
|
+
for (const entry of rowInfo.cells) {
|
|
28840
|
+
const rect = safeFindCell(map, entry.relativePos);
|
|
28841
|
+
if (!rect || rect.top > rowIndex || rowIndex >= rect.bottom) continue;
|
|
28842
|
+
cells[entry.index] = createEmptyCellNode(entry.node);
|
|
28843
|
+
}
|
|
28844
|
+
return rowInfo.node.type.create(rowInfo.node.attrs, cells);
|
|
28845
|
+
});
|
|
28167
28846
|
return tableNode.type.create(tableNode.attrs, rows);
|
|
28168
28847
|
});
|
|
28169
28848
|
}
|
|
28170
28849
|
function duplicateTableColumnAt(editor, columnIndex, cellPos) {
|
|
28171
28850
|
return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
|
|
28172
|
-
const
|
|
28173
|
-
|
|
28174
|
-
|
|
28175
|
-
|
|
28176
|
-
|
|
28177
|
-
|
|
28851
|
+
const map = import_tables.TableMap.get(tableNode);
|
|
28852
|
+
if (columnIndex < 0 || columnIndex >= map.width) return null;
|
|
28853
|
+
const rows = getTableRows(tableNode).map((rowInfo, rowIndex) => {
|
|
28854
|
+
const cells = collectChildren(rowInfo.node);
|
|
28855
|
+
const sourceCell = rowInfo.cells.find((entry) => {
|
|
28856
|
+
const rect = safeFindCell(map, entry.relativePos);
|
|
28857
|
+
return rect && rect.top === rowIndex && rect.left <= columnIndex && columnIndex < rect.right;
|
|
28858
|
+
});
|
|
28859
|
+
if (!sourceCell) return rowInfo.node;
|
|
28860
|
+
cells.splice(sourceCell.index + 1, 0, createCellCopyForColumnDuplicate(sourceCell.node));
|
|
28861
|
+
return rowInfo.node.type.create(rowInfo.node.attrs, cells);
|
|
28178
28862
|
});
|
|
28179
28863
|
return tableNode.type.create(tableNode.attrs, rows);
|
|
28180
28864
|
});
|
|
28181
28865
|
}
|
|
28182
28866
|
function clearTableColumnAt(editor, columnIndex, cellPos) {
|
|
28183
28867
|
return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
|
|
28184
|
-
const
|
|
28185
|
-
|
|
28186
|
-
|
|
28187
|
-
|
|
28188
|
-
|
|
28189
|
-
|
|
28868
|
+
const map = import_tables.TableMap.get(tableNode);
|
|
28869
|
+
if (columnIndex < 0 || columnIndex >= map.width) return null;
|
|
28870
|
+
const rows = getTableRows(tableNode).map((rowInfo) => {
|
|
28871
|
+
const cells = collectChildren(rowInfo.node);
|
|
28872
|
+
for (const entry of rowInfo.cells) {
|
|
28873
|
+
const rect = safeFindCell(map, entry.relativePos);
|
|
28874
|
+
if (!rect || rect.left > columnIndex || columnIndex >= rect.right) continue;
|
|
28875
|
+
cells[entry.index] = createEmptyCellNode(entry.node);
|
|
28876
|
+
}
|
|
28877
|
+
return rowInfo.node.type.create(rowInfo.node.attrs, cells);
|
|
28190
28878
|
});
|
|
28191
28879
|
return tableNode.type.create(tableNode.attrs, rows);
|
|
28192
28880
|
});
|
|
@@ -29238,14 +29926,410 @@ var EditorToolbar = ({
|
|
|
29238
29926
|
// src/components/UEditor/menus.tsx
|
|
29239
29927
|
var import_react62 = require("react");
|
|
29240
29928
|
var import_react63 = require("@tiptap/react");
|
|
29241
|
-
var
|
|
29929
|
+
var import_tables3 = require("@tiptap/pm/tables");
|
|
29242
29930
|
var import_react_dom8 = require("react-dom");
|
|
29243
29931
|
var import_lucide_react49 = require("lucide-react");
|
|
29932
|
+
|
|
29933
|
+
// src/components/UEditor/table-formula-commands.ts
|
|
29934
|
+
var import_tables2 = require("@tiptap/pm/tables");
|
|
29935
|
+
|
|
29936
|
+
// src/components/UEditor/table-formula.ts
|
|
29937
|
+
var CELL_ADDRESS_RE = /^([A-Z]+)([1-9]\d*)$/i;
|
|
29938
|
+
var CELL_RANGE_RE = /^([A-Z]+[1-9]\d*):([A-Z]+[1-9]\d*)$/i;
|
|
29939
|
+
var SUPPORTED_FUNCTIONS = /* @__PURE__ */ new Set(["SUM", "AVG", "MIN", "MAX", "COUNT"]);
|
|
29940
|
+
function columnNameToIndex(columnName) {
|
|
29941
|
+
const normalized = columnName.trim().toUpperCase();
|
|
29942
|
+
if (!/^[A-Z]+$/.test(normalized)) {
|
|
29943
|
+
return -1;
|
|
29944
|
+
}
|
|
29945
|
+
let index = 0;
|
|
29946
|
+
for (const char of normalized) {
|
|
29947
|
+
index = index * 26 + char.charCodeAt(0) - 64;
|
|
29948
|
+
}
|
|
29949
|
+
return index - 1;
|
|
29950
|
+
}
|
|
29951
|
+
function indexToColumnName(index) {
|
|
29952
|
+
if (!Number.isInteger(index) || index < 0) {
|
|
29953
|
+
return "";
|
|
29954
|
+
}
|
|
29955
|
+
let value = index + 1;
|
|
29956
|
+
let name = "";
|
|
29957
|
+
while (value > 0) {
|
|
29958
|
+
const remainder = (value - 1) % 26;
|
|
29959
|
+
name = String.fromCharCode(65 + remainder) + name;
|
|
29960
|
+
value = Math.floor((value - 1) / 26);
|
|
29961
|
+
}
|
|
29962
|
+
return name;
|
|
29963
|
+
}
|
|
29964
|
+
function parseTableCellAddress(input) {
|
|
29965
|
+
const match = input.trim().match(CELL_ADDRESS_RE);
|
|
29966
|
+
if (!match) {
|
|
29967
|
+
return null;
|
|
29968
|
+
}
|
|
29969
|
+
const column = columnNameToIndex(match[1] ?? "");
|
|
29970
|
+
const row = Number.parseInt(match[2] ?? "", 10) - 1;
|
|
29971
|
+
if (column < 0 || row < 0) {
|
|
29972
|
+
return null;
|
|
29973
|
+
}
|
|
29974
|
+
return {
|
|
29975
|
+
column,
|
|
29976
|
+
row,
|
|
29977
|
+
label: `${indexToColumnName(column)}${row + 1}`
|
|
29978
|
+
};
|
|
29979
|
+
}
|
|
29980
|
+
function parseTableCellRange(input) {
|
|
29981
|
+
const match = input.trim().match(CELL_RANGE_RE);
|
|
29982
|
+
if (!match) {
|
|
29983
|
+
return null;
|
|
29984
|
+
}
|
|
29985
|
+
const from = parseTableCellAddress(match[1] ?? "");
|
|
29986
|
+
const to = parseTableCellAddress(match[2] ?? "");
|
|
29987
|
+
if (!from || !to) {
|
|
29988
|
+
return null;
|
|
29989
|
+
}
|
|
29990
|
+
return { from, to };
|
|
29991
|
+
}
|
|
29992
|
+
function getTableCellRangeLabels(range) {
|
|
29993
|
+
const startColumn = Math.min(range.from.column, range.to.column);
|
|
29994
|
+
const endColumn = Math.max(range.from.column, range.to.column);
|
|
29995
|
+
const startRow = Math.min(range.from.row, range.to.row);
|
|
29996
|
+
const endRow = Math.max(range.from.row, range.to.row);
|
|
29997
|
+
const labels = [];
|
|
29998
|
+
for (let row = startRow; row <= endRow; row += 1) {
|
|
29999
|
+
for (let column = startColumn; column <= endColumn; column += 1) {
|
|
30000
|
+
labels.push(`${indexToColumnName(column)}${row + 1}`);
|
|
30001
|
+
}
|
|
30002
|
+
}
|
|
30003
|
+
return labels;
|
|
30004
|
+
}
|
|
30005
|
+
function normalizeTableFormula(formula) {
|
|
30006
|
+
return formula.trim().replace(/^=/, "").trim();
|
|
30007
|
+
}
|
|
30008
|
+
function evaluateBasicTableFormula(formula, getCellValue) {
|
|
30009
|
+
const normalized = normalizeTableFormula(formula);
|
|
30010
|
+
if (!normalized) {
|
|
30011
|
+
return { value: null, error: "empty" };
|
|
30012
|
+
}
|
|
30013
|
+
const tokens = tokenizeFormula(normalized);
|
|
30014
|
+
if (!tokens) {
|
|
30015
|
+
return { value: null, error: "invalid-formula" };
|
|
30016
|
+
}
|
|
30017
|
+
const parser = new FormulaParser(tokens, getCellValue);
|
|
30018
|
+
const result = parser.parseExpression();
|
|
30019
|
+
if (result.error) {
|
|
30020
|
+
return result;
|
|
30021
|
+
}
|
|
30022
|
+
if (!parser.isComplete()) {
|
|
30023
|
+
return { value: null, error: "invalid-formula" };
|
|
30024
|
+
}
|
|
30025
|
+
return result;
|
|
30026
|
+
}
|
|
30027
|
+
function tokenizeFormula(formula) {
|
|
30028
|
+
const tokens = [];
|
|
30029
|
+
let index = 0;
|
|
30030
|
+
while (index < formula.length) {
|
|
30031
|
+
const char = formula[index];
|
|
30032
|
+
if (!char) break;
|
|
30033
|
+
if (/\s/.test(char)) {
|
|
30034
|
+
index += 1;
|
|
30035
|
+
continue;
|
|
30036
|
+
}
|
|
30037
|
+
if (char === "," || char === "+" || char === "-" || char === "*" || char === "/" || char === "(" || char === ")") {
|
|
30038
|
+
if (char === ",") tokens.push({ type: "comma", value: char });
|
|
30039
|
+
else if (char === "(" || char === ")") tokens.push({ type: "paren", value: char });
|
|
30040
|
+
else tokens.push({ type: "operator", value: char });
|
|
30041
|
+
index += 1;
|
|
30042
|
+
continue;
|
|
30043
|
+
}
|
|
30044
|
+
const numberMatch = formula.slice(index).match(/^\d+(?:\.\d+)?/);
|
|
30045
|
+
if (numberMatch?.[0]) {
|
|
30046
|
+
tokens.push({ type: "number", value: Number.parseFloat(numberMatch[0]) });
|
|
30047
|
+
index += numberMatch[0].length;
|
|
30048
|
+
continue;
|
|
30049
|
+
}
|
|
30050
|
+
const identifierMatch = formula.slice(index).match(/^[A-Z]+[1-9]\d*(?::[A-Z]+[1-9]\d*)?|^[A-Z]+/i);
|
|
30051
|
+
if (identifierMatch?.[0]) {
|
|
30052
|
+
const value = identifierMatch[0].toUpperCase();
|
|
30053
|
+
if (CELL_RANGE_RE.test(value)) tokens.push({ type: "range", value });
|
|
30054
|
+
else if (parseTableCellAddress(value)) tokens.push({ type: "cell", value });
|
|
30055
|
+
else if (SUPPORTED_FUNCTIONS.has(value)) tokens.push({ type: "function", value });
|
|
30056
|
+
else return null;
|
|
30057
|
+
index += identifierMatch[0].length;
|
|
30058
|
+
continue;
|
|
30059
|
+
}
|
|
30060
|
+
return null;
|
|
30061
|
+
}
|
|
30062
|
+
return tokens;
|
|
30063
|
+
}
|
|
30064
|
+
var FormulaParser = class {
|
|
30065
|
+
constructor(tokens, getCellValue) {
|
|
30066
|
+
this.tokens = tokens;
|
|
30067
|
+
this.getCellValue = getCellValue;
|
|
30068
|
+
this.index = 0;
|
|
30069
|
+
}
|
|
30070
|
+
isComplete() {
|
|
30071
|
+
return this.index >= this.tokens.length;
|
|
30072
|
+
}
|
|
30073
|
+
parseExpression() {
|
|
30074
|
+
let left = this.parseTerm();
|
|
30075
|
+
while (!left.error) {
|
|
30076
|
+
const operator = this.peekOperator(["+", "-"]);
|
|
30077
|
+
if (!operator) break;
|
|
30078
|
+
this.index += 1;
|
|
30079
|
+
const right = this.parseTerm();
|
|
30080
|
+
if (right.error) return right;
|
|
30081
|
+
left = {
|
|
30082
|
+
value: operator.value === "+" ? left.value + right.value : left.value - right.value,
|
|
30083
|
+
error: null
|
|
30084
|
+
};
|
|
30085
|
+
}
|
|
30086
|
+
return left;
|
|
30087
|
+
}
|
|
30088
|
+
parseTerm() {
|
|
30089
|
+
let left = this.parseFactor();
|
|
30090
|
+
while (!left.error) {
|
|
30091
|
+
const operator = this.peekOperator(["*", "/"]);
|
|
30092
|
+
if (!operator) break;
|
|
30093
|
+
this.index += 1;
|
|
30094
|
+
const right = this.parseFactor();
|
|
30095
|
+
if (right.error) return right;
|
|
30096
|
+
if (operator.value === "/" && right.value === 0) {
|
|
30097
|
+
return { value: null, error: "division-by-zero" };
|
|
30098
|
+
}
|
|
30099
|
+
left = {
|
|
30100
|
+
value: operator.value === "*" ? left.value * right.value : left.value / right.value,
|
|
30101
|
+
error: null
|
|
30102
|
+
};
|
|
30103
|
+
}
|
|
30104
|
+
return left;
|
|
30105
|
+
}
|
|
30106
|
+
parseFactor() {
|
|
30107
|
+
const token = this.tokens[this.index];
|
|
30108
|
+
if (!token) {
|
|
30109
|
+
return { value: null, error: "invalid-formula" };
|
|
30110
|
+
}
|
|
30111
|
+
if (token.type === "operator" && token.value === "-") {
|
|
30112
|
+
this.index += 1;
|
|
30113
|
+
const value = this.parseFactor();
|
|
30114
|
+
if (value.error) return value;
|
|
30115
|
+
return { value: -value.value, error: null };
|
|
30116
|
+
}
|
|
30117
|
+
if (token.type === "number") {
|
|
30118
|
+
this.index += 1;
|
|
30119
|
+
return { value: token.value, error: null };
|
|
30120
|
+
}
|
|
30121
|
+
if (token.type === "cell") {
|
|
30122
|
+
this.index += 1;
|
|
30123
|
+
return this.readCellNumber(token.value);
|
|
30124
|
+
}
|
|
30125
|
+
if (token.type === "function") {
|
|
30126
|
+
return this.parseFunction(token.value);
|
|
30127
|
+
}
|
|
30128
|
+
if (token.type === "paren" && token.value === "(") {
|
|
30129
|
+
this.index += 1;
|
|
30130
|
+
const value = this.parseExpression();
|
|
30131
|
+
if (value.error) return value;
|
|
30132
|
+
if (!this.consumeParen(")")) {
|
|
30133
|
+
return { value: null, error: "invalid-formula" };
|
|
30134
|
+
}
|
|
30135
|
+
return value;
|
|
30136
|
+
}
|
|
30137
|
+
return { value: null, error: "invalid-formula" };
|
|
30138
|
+
}
|
|
30139
|
+
parseFunction(name) {
|
|
30140
|
+
this.index += 1;
|
|
30141
|
+
if (!this.consumeParen("(")) {
|
|
30142
|
+
return { value: null, error: "invalid-formula" };
|
|
30143
|
+
}
|
|
30144
|
+
const values = [];
|
|
30145
|
+
while (true) {
|
|
30146
|
+
const token = this.tokens[this.index];
|
|
30147
|
+
if (!token) {
|
|
30148
|
+
return { value: null, error: "invalid-formula" };
|
|
30149
|
+
}
|
|
30150
|
+
if (token.type === "range") {
|
|
30151
|
+
this.index += 1;
|
|
30152
|
+
const range = parseTableCellRange(token.value);
|
|
30153
|
+
if (!range) return { value: null, error: "invalid-reference" };
|
|
30154
|
+
for (const label of getTableCellRangeLabels(range)) {
|
|
30155
|
+
const cellValue = this.readCellNumber(label);
|
|
30156
|
+
if (cellValue.error) return cellValue;
|
|
30157
|
+
values.push(cellValue.value);
|
|
30158
|
+
}
|
|
30159
|
+
} else {
|
|
30160
|
+
const value = this.parseExpression();
|
|
30161
|
+
if (value.error) return value;
|
|
30162
|
+
values.push(value.value);
|
|
30163
|
+
}
|
|
30164
|
+
if (this.consumeComma()) {
|
|
30165
|
+
continue;
|
|
30166
|
+
}
|
|
30167
|
+
if (this.consumeParen(")")) {
|
|
30168
|
+
break;
|
|
30169
|
+
}
|
|
30170
|
+
return { value: null, error: "invalid-formula" };
|
|
30171
|
+
}
|
|
30172
|
+
if (values.length === 0) {
|
|
30173
|
+
return { value: null, error: "invalid-formula" };
|
|
30174
|
+
}
|
|
30175
|
+
if (name === "SUM") return { value: values.reduce((sum, value) => sum + value, 0), error: null };
|
|
30176
|
+
if (name === "AVG") return { value: values.reduce((sum, value) => sum + value, 0) / values.length, error: null };
|
|
30177
|
+
if (name === "MIN") return { value: Math.min(...values), error: null };
|
|
30178
|
+
if (name === "MAX") return { value: Math.max(...values), error: null };
|
|
30179
|
+
if (name === "COUNT") return { value: values.length, error: null };
|
|
30180
|
+
return { value: null, error: "invalid-formula" };
|
|
30181
|
+
}
|
|
30182
|
+
readCellNumber(label) {
|
|
30183
|
+
const value = this.getCellValue(label);
|
|
30184
|
+
const parsed = typeof value === "number" ? value : Number.parseFloat(String(value ?? "").trim());
|
|
30185
|
+
if (!Number.isFinite(parsed)) {
|
|
30186
|
+
return { value: null, error: "invalid-reference" };
|
|
30187
|
+
}
|
|
30188
|
+
return { value: parsed, error: null };
|
|
30189
|
+
}
|
|
30190
|
+
peekOperator(operators) {
|
|
30191
|
+
const token = this.tokens[this.index];
|
|
30192
|
+
return token?.type === "operator" && operators.includes(token.value) ? token : null;
|
|
30193
|
+
}
|
|
30194
|
+
consumeComma() {
|
|
30195
|
+
if (this.tokens[this.index]?.type !== "comma") {
|
|
30196
|
+
return false;
|
|
30197
|
+
}
|
|
30198
|
+
this.index += 1;
|
|
30199
|
+
return true;
|
|
30200
|
+
}
|
|
30201
|
+
consumeParen(value) {
|
|
30202
|
+
const token = this.tokens[this.index];
|
|
30203
|
+
if (token?.type !== "paren" || token.value !== value) {
|
|
30204
|
+
return false;
|
|
30205
|
+
}
|
|
30206
|
+
this.index += 1;
|
|
30207
|
+
return true;
|
|
30208
|
+
}
|
|
30209
|
+
};
|
|
30210
|
+
|
|
30211
|
+
// src/components/UEditor/table-formula-commands.ts
|
|
30212
|
+
function collectChildren2(node) {
|
|
30213
|
+
const children = [];
|
|
30214
|
+
node.forEach((child) => children.push(child));
|
|
30215
|
+
return children;
|
|
30216
|
+
}
|
|
30217
|
+
function getTableRows2(tableNode) {
|
|
30218
|
+
const rows = [];
|
|
30219
|
+
tableNode.forEach((rowNode, rowOffset) => {
|
|
30220
|
+
const cells = [];
|
|
30221
|
+
rowNode.forEach((cellNode, cellOffset, index) => {
|
|
30222
|
+
cells.push({
|
|
30223
|
+
index,
|
|
30224
|
+
node: cellNode,
|
|
30225
|
+
relativePos: rowOffset + 1 + cellOffset
|
|
30226
|
+
});
|
|
30227
|
+
});
|
|
30228
|
+
rows.push({ node: rowNode, cells });
|
|
30229
|
+
});
|
|
30230
|
+
return rows;
|
|
30231
|
+
}
|
|
30232
|
+
function safeFindCell2(map, relativePos) {
|
|
30233
|
+
try {
|
|
30234
|
+
return map.findCell(relativePos);
|
|
30235
|
+
} catch {
|
|
30236
|
+
return null;
|
|
30237
|
+
}
|
|
30238
|
+
}
|
|
30239
|
+
function getCellText(cellNode) {
|
|
30240
|
+
return cellNode.textBetween(0, cellNode.content.size, "\n").trim();
|
|
30241
|
+
}
|
|
30242
|
+
function buildTableValueGetter(tableNode) {
|
|
30243
|
+
const map = import_tables2.TableMap.get(tableNode);
|
|
30244
|
+
const values = /* @__PURE__ */ new Map();
|
|
30245
|
+
for (const rowInfo of getTableRows2(tableNode)) {
|
|
30246
|
+
for (const entry of rowInfo.cells) {
|
|
30247
|
+
const rect = safeFindCell2(map, entry.relativePos);
|
|
30248
|
+
if (!rect) continue;
|
|
30249
|
+
const label = `${indexToColumnName(rect.left)}${rect.top + 1}`;
|
|
30250
|
+
const computedValue = entry.node.attrs.computedValue;
|
|
30251
|
+
values.set(label, typeof computedValue === "string" && computedValue.trim() ? computedValue : getCellText(entry.node));
|
|
30252
|
+
}
|
|
30253
|
+
}
|
|
30254
|
+
return (label) => values.get(label.toUpperCase());
|
|
30255
|
+
}
|
|
30256
|
+
function getFormulaComputedValue(formula, tableNode) {
|
|
30257
|
+
const result = evaluateBasicTableFormula(formula, buildTableValueGetter(tableNode));
|
|
30258
|
+
return result.error ? `#${result.error.toUpperCase()}` : String(result.value);
|
|
30259
|
+
}
|
|
30260
|
+
function normalizeFormulaInput(formula) {
|
|
30261
|
+
const trimmed = formula.trim();
|
|
30262
|
+
if (!trimmed) return "";
|
|
30263
|
+
return trimmed.startsWith("=") ? trimmed : `=${trimmed}`;
|
|
30264
|
+
}
|
|
30265
|
+
function setSelectedTableCellFormula(editor, formula) {
|
|
30266
|
+
const normalized = normalizeFormulaInput(formula);
|
|
30267
|
+
const { state, view } = editor;
|
|
30268
|
+
if (!normalized) {
|
|
30269
|
+
const clearedFormula = (0, import_tables2.setCellAttr)("formula", null)(state, view.dispatch.bind(view));
|
|
30270
|
+
const clearedValue = (0, import_tables2.setCellAttr)("computedValue", null)(editor.state, view.dispatch.bind(view));
|
|
30271
|
+
if (clearedFormula || clearedValue) {
|
|
30272
|
+
view.focus();
|
|
30273
|
+
dispatchTableLayoutChange(editor);
|
|
30274
|
+
return true;
|
|
30275
|
+
}
|
|
30276
|
+
return false;
|
|
30277
|
+
}
|
|
30278
|
+
const rect = (0, import_tables2.selectedRect)(state);
|
|
30279
|
+
const computedValue = getFormulaComputedValue(normalized, rect.table);
|
|
30280
|
+
const appliedFormula = (0, import_tables2.setCellAttr)("formula", normalized)(state, view.dispatch.bind(view));
|
|
30281
|
+
const appliedValue = (0, import_tables2.setCellAttr)("computedValue", computedValue)(editor.state, view.dispatch.bind(view));
|
|
30282
|
+
if (appliedFormula || appliedValue) {
|
|
30283
|
+
view.focus();
|
|
30284
|
+
dispatchTableLayoutChange(editor);
|
|
30285
|
+
return true;
|
|
30286
|
+
}
|
|
30287
|
+
return false;
|
|
30288
|
+
}
|
|
30289
|
+
function clearSelectedTableCellFormula(editor) {
|
|
30290
|
+
return setSelectedTableCellFormula(editor, "");
|
|
30291
|
+
}
|
|
30292
|
+
function recalculateSelectedTable(editor) {
|
|
30293
|
+
const rect = (0, import_tables2.selectedRect)(editor.state);
|
|
30294
|
+
const tableNode = rect.table;
|
|
30295
|
+
const map = import_tables2.TableMap.get(tableNode);
|
|
30296
|
+
const getCellValue = buildTableValueGetter(tableNode);
|
|
30297
|
+
let changed = false;
|
|
30298
|
+
const rows = getTableRows2(tableNode).map((rowInfo) => {
|
|
30299
|
+
const cells = collectChildren2(rowInfo.node);
|
|
30300
|
+
for (const entry of rowInfo.cells) {
|
|
30301
|
+
const formula = typeof entry.node.attrs.formula === "string" ? entry.node.attrs.formula.trim() : "";
|
|
30302
|
+
if (!formula) continue;
|
|
30303
|
+
const rectForCell = safeFindCell2(map, entry.relativePos);
|
|
30304
|
+
if (!rectForCell) continue;
|
|
30305
|
+
const result = evaluateBasicTableFormula(formula, getCellValue);
|
|
30306
|
+
const computedValue = result.error ? `#${result.error.toUpperCase()}` : String(result.value);
|
|
30307
|
+
if (entry.node.attrs.computedValue === computedValue) continue;
|
|
30308
|
+
cells[entry.index] = entry.node.type.create(
|
|
30309
|
+
{
|
|
30310
|
+
...entry.node.attrs,
|
|
30311
|
+
computedValue
|
|
30312
|
+
},
|
|
30313
|
+
entry.node.content,
|
|
30314
|
+
entry.node.marks
|
|
30315
|
+
);
|
|
30316
|
+
changed = true;
|
|
30317
|
+
}
|
|
30318
|
+
return rowInfo.node.type.create(rowInfo.node.attrs, cells);
|
|
30319
|
+
});
|
|
30320
|
+
if (!changed) return false;
|
|
30321
|
+
const nextTable = tableNode.type.create(tableNode.attrs, rows);
|
|
30322
|
+
editor.view.dispatch(editor.state.tr.replaceWith(rect.tableStart - 1, rect.tableStart - 1 + tableNode.nodeSize, nextTable));
|
|
30323
|
+
dispatchTableLayoutChange(editor);
|
|
30324
|
+
return true;
|
|
30325
|
+
}
|
|
30326
|
+
|
|
30327
|
+
// src/components/UEditor/menus.tsx
|
|
29244
30328
|
var import_jsx_runtime86 = require("react/jsx-runtime");
|
|
29245
30329
|
function applyTableCellBackground(editor, color) {
|
|
29246
30330
|
const value = color || null;
|
|
29247
30331
|
const { state, view } = editor;
|
|
29248
|
-
const applied = (0,
|
|
30332
|
+
const applied = (0, import_tables3.setCellAttr)("backgroundColor", value)(state, view.dispatch.bind(view));
|
|
29249
30333
|
if (applied) {
|
|
29250
30334
|
view.focus();
|
|
29251
30335
|
return;
|
|
@@ -29271,6 +30355,8 @@ var BubbleMenuContent = ({
|
|
|
29271
30355
|
setShowLinkInput(initialShowLinkInput);
|
|
29272
30356
|
}, [initialShowLinkInput]);
|
|
29273
30357
|
const [showTypographyPanel, setShowTypographyPanel] = (0, import_react62.useState)(false);
|
|
30358
|
+
const [showFormulaPanel, setShowFormulaPanel] = (0, import_react62.useState)(false);
|
|
30359
|
+
const [formulaDraft, setFormulaDraft] = (0, import_react62.useState)("");
|
|
29274
30360
|
const [showFontSizeOptions, setShowFontSizeOptions] = (0, import_react62.useState)(false);
|
|
29275
30361
|
const [fontSizeDraft, setFontSizeDraft] = (0, import_react62.useState)("");
|
|
29276
30362
|
const isImageSelected = editor.isActive("image");
|
|
@@ -29281,7 +30367,8 @@ var BubbleMenuContent = ({
|
|
|
29281
30367
|
const currentTextColor = normalizeStyleValue(textStyleAttrs.color) || "inherit";
|
|
29282
30368
|
const currentHighlightColor = normalizeStyleValue(editor.getAttributes("highlight").color) || "";
|
|
29283
30369
|
const currentCellBgColor = normalizeStyleValue(editor.getAttributes("tableCell").backgroundColor || editor.getAttributes("tableHeader").backgroundColor) || "";
|
|
29284
|
-
const
|
|
30370
|
+
const currentCellFormula = normalizeStyleValue(editor.getAttributes("tableCell").formula || editor.getAttributes("tableHeader").formula) || "";
|
|
30371
|
+
const isInTable2 = (0, import_tables3.isInTable)(editor.state);
|
|
29285
30372
|
const canMergeCells = isInTable2 && editor.can().mergeCells();
|
|
29286
30373
|
const canSplitCell = isInTable2 && editor.can().splitCell();
|
|
29287
30374
|
const currentFontSize = normalizeStyleValue(textStyleAttrs.fontSize);
|
|
@@ -29297,6 +30384,9 @@ var BubbleMenuContent = ({
|
|
|
29297
30384
|
(0, import_react62.useEffect)(() => {
|
|
29298
30385
|
setFontSizeDraft(currentFontSize.replace(/px$/i, ""));
|
|
29299
30386
|
}, [currentFontSize]);
|
|
30387
|
+
(0, import_react62.useEffect)(() => {
|
|
30388
|
+
setFormulaDraft(currentCellFormula);
|
|
30389
|
+
}, [currentCellFormula]);
|
|
29300
30390
|
const applyFontSizeDraft = () => {
|
|
29301
30391
|
const normalized = fontSizeDraft.trim();
|
|
29302
30392
|
if (!normalized) {
|
|
@@ -29499,6 +30589,82 @@ var BubbleMenuContent = ({
|
|
|
29499
30589
|
)
|
|
29500
30590
|
] });
|
|
29501
30591
|
}
|
|
30592
|
+
if (showFormulaPanel && isInTable2) {
|
|
30593
|
+
return /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "w-72 p-2", children: [
|
|
30594
|
+
/* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
|
|
30595
|
+
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { className: "px-1 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: t("tableMenu.formula") || "Formula" }),
|
|
30596
|
+
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
|
|
30597
|
+
"button",
|
|
30598
|
+
{
|
|
30599
|
+
type: "button",
|
|
30600
|
+
onClick: () => {
|
|
30601
|
+
setShowFormulaPanel(false);
|
|
30602
|
+
onKeepOpenChange?.(false);
|
|
30603
|
+
},
|
|
30604
|
+
className: "rounded-md px-2 py-1 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
30605
|
+
children: t("colors.done")
|
|
30606
|
+
}
|
|
30607
|
+
)
|
|
30608
|
+
] }),
|
|
30609
|
+
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)("div", { className: "flex h-9 items-center overflow-hidden rounded-md border border-border/60 bg-muted/40", children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
|
|
30610
|
+
"input",
|
|
30611
|
+
{
|
|
30612
|
+
value: formulaDraft,
|
|
30613
|
+
onChange: (event) => setFormulaDraft(event.target.value),
|
|
30614
|
+
onMouseDown: (event) => event.stopPropagation(),
|
|
30615
|
+
onClick: (event) => event.stopPropagation(),
|
|
30616
|
+
onKeyDown: (event) => {
|
|
30617
|
+
event.stopPropagation();
|
|
30618
|
+
if (event.key === "Enter") {
|
|
30619
|
+
event.preventDefault();
|
|
30620
|
+
setSelectedTableCellFormula(editor, formulaDraft);
|
|
30621
|
+
setShowFormulaPanel(false);
|
|
30622
|
+
onKeepOpenChange?.(false);
|
|
30623
|
+
}
|
|
30624
|
+
},
|
|
30625
|
+
"aria-label": t("tableMenu.formula") || "Formula",
|
|
30626
|
+
placeholder: "=SUM(A1:A3)",
|
|
30627
|
+
className: "h-full min-w-0 flex-1 bg-transparent px-2 text-sm font-medium text-foreground outline-none"
|
|
30628
|
+
}
|
|
30629
|
+
) }),
|
|
30630
|
+
/* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "mt-2 grid grid-cols-3 gap-1", children: [
|
|
30631
|
+
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
|
|
30632
|
+
"button",
|
|
30633
|
+
{
|
|
30634
|
+
type: "button",
|
|
30635
|
+
onClick: () => {
|
|
30636
|
+
setSelectedTableCellFormula(editor, formulaDraft);
|
|
30637
|
+
setShowFormulaPanel(false);
|
|
30638
|
+
onKeepOpenChange?.(false);
|
|
30639
|
+
},
|
|
30640
|
+
className: "h-8 rounded-md bg-primary/10 text-xs font-semibold text-primary transition-colors hover:bg-primary/15",
|
|
30641
|
+
children: t("tableMenu.apply") || "Apply"
|
|
30642
|
+
}
|
|
30643
|
+
),
|
|
30644
|
+
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
|
|
30645
|
+
"button",
|
|
30646
|
+
{
|
|
30647
|
+
type: "button",
|
|
30648
|
+
onClick: () => {
|
|
30649
|
+
clearSelectedTableCellFormula(editor);
|
|
30650
|
+
setFormulaDraft("");
|
|
30651
|
+
},
|
|
30652
|
+
className: "h-8 rounded-md bg-muted/40 text-xs font-semibold text-foreground transition-colors hover:bg-muted",
|
|
30653
|
+
children: t("tableMenu.clear") || "Clear"
|
|
30654
|
+
}
|
|
30655
|
+
),
|
|
30656
|
+
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
|
|
30657
|
+
"button",
|
|
30658
|
+
{
|
|
30659
|
+
type: "button",
|
|
30660
|
+
onClick: () => recalculateSelectedTable(editor),
|
|
30661
|
+
className: "h-8 rounded-md bg-muted/40 text-xs font-semibold text-foreground transition-colors hover:bg-muted",
|
|
30662
|
+
children: t("tableMenu.recalculate") || "Recalc"
|
|
30663
|
+
}
|
|
30664
|
+
)
|
|
30665
|
+
] })
|
|
30666
|
+
] });
|
|
30667
|
+
}
|
|
29502
30668
|
if (showTypographyPanel) {
|
|
29503
30669
|
return /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "w-72 p-2", children: [
|
|
29504
30670
|
/* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
|
|
@@ -29712,6 +30878,21 @@ var BubbleMenuContent = ({
|
|
|
29712
30878
|
)
|
|
29713
30879
|
}
|
|
29714
30880
|
),
|
|
30881
|
+
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
|
|
30882
|
+
ToolbarButton,
|
|
30883
|
+
{
|
|
30884
|
+
onMouseDown: () => {
|
|
30885
|
+
onKeepOpenChange?.(true);
|
|
30886
|
+
},
|
|
30887
|
+
onClick: () => {
|
|
30888
|
+
setFormulaDraft(currentCellFormula);
|
|
30889
|
+
setShowFormulaPanel(true);
|
|
30890
|
+
},
|
|
30891
|
+
active: Boolean(currentCellFormula),
|
|
30892
|
+
title: t("tableMenu.formula") || "Formula",
|
|
30893
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_lucide_react49.Sigma, { className: "w-4 h-4" })
|
|
30894
|
+
}
|
|
30895
|
+
),
|
|
29715
30896
|
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
|
|
29716
30897
|
ToolbarButton,
|
|
29717
30898
|
{
|
|
@@ -33553,7 +34734,7 @@ if (typeof WeakMap != "undefined") {
|
|
|
33553
34734
|
return cache[cachePos++] = value;
|
|
33554
34735
|
};
|
|
33555
34736
|
}
|
|
33556
|
-
var
|
|
34737
|
+
var TableMap3 = class {
|
|
33557
34738
|
constructor(width, height, map, problems) {
|
|
33558
34739
|
this.width = width;
|
|
33559
34740
|
this.height = height;
|
|
@@ -33690,7 +34871,7 @@ function computeMap(table) {
|
|
|
33690
34871
|
pos++;
|
|
33691
34872
|
}
|
|
33692
34873
|
if (width === 0 || height === 0) (problems || (problems = [])).push({ type: "zero_sized" });
|
|
33693
|
-
const tableMap = new
|
|
34874
|
+
const tableMap = new TableMap3(width, height, map, problems);
|
|
33694
34875
|
let badWidths = false;
|
|
33695
34876
|
for (let i = 0; !badWidths && i < colWidths.length; i += 2) if (colWidths[i] != null && colWidths[i + 1] < height) badWidths = true;
|
|
33696
34877
|
if (badWidths) findBadColWidths(tableMap, colWidths, table);
|
|
@@ -33794,7 +34975,7 @@ function inSameTable($cellA, $cellB) {
|
|
|
33794
34975
|
}
|
|
33795
34976
|
function nextCell($pos, axis, dir) {
|
|
33796
34977
|
const table = $pos.node(-1);
|
|
33797
|
-
const map =
|
|
34978
|
+
const map = TableMap3.get(table);
|
|
33798
34979
|
const tableStart = $pos.start(-1);
|
|
33799
34980
|
const moved = map.nextCell($pos.pos - tableStart, axis, dir);
|
|
33800
34981
|
return moved == null ? null : $pos.node(0).resolve(tableStart + moved);
|
|
@@ -33814,7 +34995,7 @@ function removeColSpan(attrs, pos, n = 1) {
|
|
|
33814
34995
|
var CellSelection = class CellSelection2 extends Selection {
|
|
33815
34996
|
constructor($anchorCell, $headCell = $anchorCell) {
|
|
33816
34997
|
const table = $anchorCell.node(-1);
|
|
33817
|
-
const map =
|
|
34998
|
+
const map = TableMap3.get(table);
|
|
33818
34999
|
const tableStart = $anchorCell.start(-1);
|
|
33819
35000
|
const rect = map.rectBetween($anchorCell.pos - tableStart, $headCell.pos - tableStart);
|
|
33820
35001
|
const doc = $anchorCell.node(0);
|
|
@@ -33843,7 +35024,7 @@ var CellSelection = class CellSelection2 extends Selection {
|
|
|
33843
35024
|
}
|
|
33844
35025
|
content() {
|
|
33845
35026
|
const table = this.$anchorCell.node(-1);
|
|
33846
|
-
const map =
|
|
35027
|
+
const map = TableMap3.get(table);
|
|
33847
35028
|
const tableStart = this.$anchorCell.start(-1);
|
|
33848
35029
|
const rect = map.rectBetween(this.$anchorCell.pos - tableStart, this.$headCell.pos - tableStart);
|
|
33849
35030
|
const seen = {};
|
|
@@ -33897,7 +35078,7 @@ var CellSelection = class CellSelection2 extends Selection {
|
|
|
33897
35078
|
}
|
|
33898
35079
|
forEachCell(f) {
|
|
33899
35080
|
const table = this.$anchorCell.node(-1);
|
|
33900
|
-
const map =
|
|
35081
|
+
const map = TableMap3.get(table);
|
|
33901
35082
|
const tableStart = this.$anchorCell.start(-1);
|
|
33902
35083
|
const cells = map.cellsInRect(map.rectBetween(this.$anchorCell.pos - tableStart, this.$headCell.pos - tableStart));
|
|
33903
35084
|
for (let i = 0; i < cells.length; i++) f(table.nodeAt(cells[i]), tableStart + cells[i]);
|
|
@@ -33912,7 +35093,7 @@ var CellSelection = class CellSelection2 extends Selection {
|
|
|
33912
35093
|
}
|
|
33913
35094
|
static colSelection($anchorCell, $headCell = $anchorCell) {
|
|
33914
35095
|
const table = $anchorCell.node(-1);
|
|
33915
|
-
const map =
|
|
35096
|
+
const map = TableMap3.get(table);
|
|
33916
35097
|
const tableStart = $anchorCell.start(-1);
|
|
33917
35098
|
const anchorRect = map.findCell($anchorCell.pos - tableStart);
|
|
33918
35099
|
const headRect = map.findCell($headCell.pos - tableStart);
|
|
@@ -33928,7 +35109,7 @@ var CellSelection = class CellSelection2 extends Selection {
|
|
|
33928
35109
|
}
|
|
33929
35110
|
isRowSelection() {
|
|
33930
35111
|
const table = this.$anchorCell.node(-1);
|
|
33931
|
-
const map =
|
|
35112
|
+
const map = TableMap3.get(table);
|
|
33932
35113
|
const tableStart = this.$anchorCell.start(-1);
|
|
33933
35114
|
const anchorLeft = map.colCount(this.$anchorCell.pos - tableStart);
|
|
33934
35115
|
const headLeft = map.colCount(this.$headCell.pos - tableStart);
|
|
@@ -33942,7 +35123,7 @@ var CellSelection = class CellSelection2 extends Selection {
|
|
|
33942
35123
|
}
|
|
33943
35124
|
static rowSelection($anchorCell, $headCell = $anchorCell) {
|
|
33944
35125
|
const table = $anchorCell.node(-1);
|
|
33945
|
-
const map =
|
|
35126
|
+
const map = TableMap3.get(table);
|
|
33946
35127
|
const tableStart = $anchorCell.start(-1);
|
|
33947
35128
|
const anchorRect = map.findCell($anchorCell.pos - tableStart);
|
|
33948
35129
|
const headRect = map.findCell($headCell.pos - tableStart);
|
|
@@ -33991,7 +35172,7 @@ var CellBookmark = class CellBookmark2 {
|
|
|
33991
35172
|
};
|
|
33992
35173
|
var fixTablesKey = new PluginKey4("fix-tables");
|
|
33993
35174
|
function convertTableNodeToArrayOfRows(tableNode) {
|
|
33994
|
-
const map =
|
|
35175
|
+
const map = TableMap3.get(tableNode);
|
|
33995
35176
|
const rows = [];
|
|
33996
35177
|
const rowCount = map.height;
|
|
33997
35178
|
const colCount$1 = map.width;
|
|
@@ -34022,7 +35203,7 @@ function convertTableNodeToArrayOfRows(tableNode) {
|
|
|
34022
35203
|
}
|
|
34023
35204
|
function convertArrayOfRowsToTableNode(tableNode, arrayOfNodes) {
|
|
34024
35205
|
const newRows = [];
|
|
34025
|
-
const map =
|
|
35206
|
+
const map = TableMap3.get(tableNode);
|
|
34026
35207
|
const rowCount = map.height;
|
|
34027
35208
|
const colCount$1 = map.width;
|
|
34028
35209
|
for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
|
|
@@ -34071,7 +35252,7 @@ function findParentNode(predicate, $pos) {
|
|
|
34071
35252
|
function getCellsInColumn(columnIndex, selection) {
|
|
34072
35253
|
const table = findTable(selection.$from);
|
|
34073
35254
|
if (!table) return;
|
|
34074
|
-
const map =
|
|
35255
|
+
const map = TableMap3.get(table.node);
|
|
34075
35256
|
if (columnIndex < 0 || columnIndex > map.width - 1) return;
|
|
34076
35257
|
return map.cellsInRect({
|
|
34077
35258
|
left: columnIndex,
|
|
@@ -34092,7 +35273,7 @@ function getCellsInColumn(columnIndex, selection) {
|
|
|
34092
35273
|
function getCellsInRow(rowIndex, selection) {
|
|
34093
35274
|
const table = findTable(selection.$from);
|
|
34094
35275
|
if (!table) return;
|
|
34095
|
-
const map =
|
|
35276
|
+
const map = TableMap3.get(table.node);
|
|
34096
35277
|
if (rowIndex < 0 || rowIndex > map.height - 1) return;
|
|
34097
35278
|
return map.cellsInRect({
|
|
34098
35279
|
left: 0,
|
|
@@ -34221,7 +35402,7 @@ function moveColumn(moveColParams) {
|
|
|
34221
35402
|
const newTable = moveTableColumn$1(table.node, indexesOriginColumn, indexesTargetColumn, 0);
|
|
34222
35403
|
tr.replaceWith(table.pos, table.pos + table.node.nodeSize, newTable);
|
|
34223
35404
|
if (!select) return true;
|
|
34224
|
-
const map =
|
|
35405
|
+
const map = TableMap3.get(newTable);
|
|
34225
35406
|
const start = table.start;
|
|
34226
35407
|
const index = targetIndex;
|
|
34227
35408
|
const lastCell = map.positionAt(map.height - 1, index, newTable);
|
|
@@ -34249,7 +35430,7 @@ function moveRow(moveRowParams) {
|
|
|
34249
35430
|
const newTable = moveTableRow$1(table.node, indexesOriginRow, indexesTargetRow, 0);
|
|
34250
35431
|
tr.replaceWith(table.pos, table.pos + table.node.nodeSize, newTable);
|
|
34251
35432
|
if (!select) return true;
|
|
34252
|
-
const map =
|
|
35433
|
+
const map = TableMap3.get(newTable);
|
|
34253
35434
|
const start = table.start;
|
|
34254
35435
|
const index = targetIndex;
|
|
34255
35436
|
const lastCell = map.positionAt(index, map.width - 1, newTable);
|
|
@@ -34264,12 +35445,12 @@ function moveTableRow$1(table, indexesOrigin, indexesTarget, direction) {
|
|
|
34264
35445
|
rows = moveRowInArrayOfRows(rows, indexesOrigin, indexesTarget, direction);
|
|
34265
35446
|
return convertArrayOfRowsToTableNode(table, rows);
|
|
34266
35447
|
}
|
|
34267
|
-
function
|
|
35448
|
+
function selectedRect3(state) {
|
|
34268
35449
|
const sel = state.selection;
|
|
34269
35450
|
const $pos = selectionCell(state);
|
|
34270
35451
|
const table = $pos.node(-1);
|
|
34271
35452
|
const tableStart = $pos.start(-1);
|
|
34272
|
-
const map =
|
|
35453
|
+
const map = TableMap3.get(table);
|
|
34273
35454
|
return {
|
|
34274
35455
|
...sel instanceof CellSelection ? map.rectBetween(sel.$anchorCell.pos - tableStart, sel.$headCell.pos - tableStart) : map.findCell($pos.pos - tableStart),
|
|
34275
35456
|
tableStart,
|
|
@@ -34282,7 +35463,7 @@ function deprecated_toggleHeader(type) {
|
|
|
34282
35463
|
if (!isInTable(state)) return false;
|
|
34283
35464
|
if (dispatch) {
|
|
34284
35465
|
const types = tableNodeTypes(state.schema);
|
|
34285
|
-
const rect =
|
|
35466
|
+
const rect = selectedRect3(state), tr = state.tr;
|
|
34286
35467
|
const cells = rect.map.cellsInRect(type == "column" ? {
|
|
34287
35468
|
left: rect.left,
|
|
34288
35469
|
top: 0,
|
|
@@ -34322,7 +35503,7 @@ function toggleHeader(type, options) {
|
|
|
34322
35503
|
if (!isInTable(state)) return false;
|
|
34323
35504
|
if (dispatch) {
|
|
34324
35505
|
const types = tableNodeTypes(state.schema);
|
|
34325
|
-
const rect =
|
|
35506
|
+
const rect = selectedRect3(state), tr = state.tr;
|
|
34326
35507
|
const isHeaderRowEnabled = isHeaderEnabledByType("row", rect, types);
|
|
34327
35508
|
const isHeaderColumnEnabled = isHeaderEnabledByType("column", rect, types);
|
|
34328
35509
|
const selectionStartsAt = (type === "column" ? isHeaderRowEnabled : type === "row" ? isHeaderColumnEnabled : false) ? 1 : 0;
|
|
@@ -34472,6 +35653,262 @@ var columnResizingPluginKey = new PluginKey4("tableColumnResizing");
|
|
|
34472
35653
|
// src/components/UEditor/table-controls.tsx
|
|
34473
35654
|
var import_lucide_react52 = require("lucide-react");
|
|
34474
35655
|
|
|
35656
|
+
// src/components/UEditor/table-layout-model.ts
|
|
35657
|
+
var FALLBACK_TABLE_ROW_HEIGHT = 44;
|
|
35658
|
+
var FALLBACK_TABLE_COLUMN_WIDTH = 160;
|
|
35659
|
+
function getVisibleTableBounds(layout) {
|
|
35660
|
+
const left = Math.max(layout.tableLeft, layout.wrapperLeft);
|
|
35661
|
+
const top = Math.max(layout.tableTop, layout.wrapperTop);
|
|
35662
|
+
const right = Math.min(layout.tableLeft + layout.tableWidth, layout.wrapperLeft + layout.viewportWidth);
|
|
35663
|
+
const bottom = Math.min(layout.tableTop + layout.tableHeight, layout.wrapperTop + layout.viewportHeight);
|
|
35664
|
+
return {
|
|
35665
|
+
left,
|
|
35666
|
+
top,
|
|
35667
|
+
right,
|
|
35668
|
+
bottom,
|
|
35669
|
+
width: Math.max(0, right - left),
|
|
35670
|
+
height: Math.max(0, bottom - top)
|
|
35671
|
+
};
|
|
35672
|
+
}
|
|
35673
|
+
function metricOrFallback(value, fallback) {
|
|
35674
|
+
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
35675
|
+
}
|
|
35676
|
+
function parsePixelMetric(value) {
|
|
35677
|
+
if (!value) return null;
|
|
35678
|
+
const parsed = Number.parseFloat(value);
|
|
35679
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
35680
|
+
}
|
|
35681
|
+
function getPrimaryCell(table) {
|
|
35682
|
+
const cell = table.querySelector("th,td");
|
|
35683
|
+
return cell instanceof HTMLTableCellElement ? cell : null;
|
|
35684
|
+
}
|
|
35685
|
+
function getLastCell(table) {
|
|
35686
|
+
const lastRow = table.rows.item(table.rows.length - 1);
|
|
35687
|
+
if (!(lastRow instanceof HTMLTableRowElement)) return null;
|
|
35688
|
+
const cell = lastRow.cells.item(lastRow.cells.length - 1);
|
|
35689
|
+
return cell instanceof HTMLTableCellElement ? cell : null;
|
|
35690
|
+
}
|
|
35691
|
+
function getCellFromTarget(target) {
|
|
35692
|
+
const element = resolveEventElement(target);
|
|
35693
|
+
if (!element) return null;
|
|
35694
|
+
const directCell = element.closest("th,td");
|
|
35695
|
+
if (directCell instanceof HTMLTableCellElement) {
|
|
35696
|
+
return directCell;
|
|
35697
|
+
}
|
|
35698
|
+
const table = element.closest("table");
|
|
35699
|
+
if (table instanceof HTMLTableElement) {
|
|
35700
|
+
return getPrimaryCell(table);
|
|
35701
|
+
}
|
|
35702
|
+
return null;
|
|
35703
|
+
}
|
|
35704
|
+
function findTableInfo(editor, pos) {
|
|
35705
|
+
const $pos = editor.state.doc.resolve(pos);
|
|
35706
|
+
for (let depth = $pos.depth; depth > 0; depth -= 1) {
|
|
35707
|
+
const node = $pos.node(depth);
|
|
35708
|
+
if (node.type.name === "table") {
|
|
35709
|
+
return {
|
|
35710
|
+
node,
|
|
35711
|
+
pos: $pos.before(depth),
|
|
35712
|
+
start: $pos.start(depth)
|
|
35713
|
+
};
|
|
35714
|
+
}
|
|
35715
|
+
}
|
|
35716
|
+
return null;
|
|
35717
|
+
}
|
|
35718
|
+
function getCellRelativePosFromDomPos(map, tableStart, domPos) {
|
|
35719
|
+
const relativeDomPos = domPos - tableStart;
|
|
35720
|
+
const seen = /* @__PURE__ */ new Set();
|
|
35721
|
+
for (const relativeCellPos of map.map) {
|
|
35722
|
+
if (seen.has(relativeCellPos)) continue;
|
|
35723
|
+
seen.add(relativeCellPos);
|
|
35724
|
+
if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
|
|
35725
|
+
return relativeCellPos;
|
|
35726
|
+
}
|
|
35727
|
+
}
|
|
35728
|
+
return null;
|
|
35729
|
+
}
|
|
35730
|
+
function buildLogicalColumnMetrics({
|
|
35731
|
+
editor,
|
|
35732
|
+
surface,
|
|
35733
|
+
surfaceRect,
|
|
35734
|
+
tableElement,
|
|
35735
|
+
tableInfo,
|
|
35736
|
+
tableLeft,
|
|
35737
|
+
tableWidth
|
|
35738
|
+
}) {
|
|
35739
|
+
const map = TableMap3.get(tableInfo.node);
|
|
35740
|
+
const fallbackWidth = metricOrFallback(tableWidth / map.width, FALLBACK_TABLE_COLUMN_WIDTH);
|
|
35741
|
+
const firstRow = tableElement.rows.item(0);
|
|
35742
|
+
const visualColumns = [];
|
|
35743
|
+
if (firstRow) {
|
|
35744
|
+
for (const tableCell of Array.from(firstRow.cells)) {
|
|
35745
|
+
if (!(tableCell instanceof HTMLTableCellElement)) continue;
|
|
35746
|
+
const cellPos = editor.view.posAtDOM(tableCell, 0);
|
|
35747
|
+
const relativeCellPos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
|
|
35748
|
+
if (relativeCellPos == null) continue;
|
|
35749
|
+
const cellMapRect = map.findCell(relativeCellPos);
|
|
35750
|
+
const cellRect = tableCell.getBoundingClientRect();
|
|
35751
|
+
const cellStart = cellRect.width > 0 ? cellRect.left - surfaceRect.left + surface.scrollLeft : tableLeft + cellMapRect.left * fallbackWidth;
|
|
35752
|
+
const size = metricOrFallback(cellRect.width, fallbackWidth * Math.max(1, cellMapRect.right - cellMapRect.left));
|
|
35753
|
+
visualColumns.push({
|
|
35754
|
+
index: cellMapRect.left,
|
|
35755
|
+
cellPos: tableInfo.start + relativeCellPos,
|
|
35756
|
+
start: cellStart,
|
|
35757
|
+
size,
|
|
35758
|
+
center: cellStart + size / 2
|
|
35759
|
+
});
|
|
35760
|
+
}
|
|
35761
|
+
}
|
|
35762
|
+
if (visualColumns.length > 0) {
|
|
35763
|
+
return visualColumns.sort((a, b) => a.index - b.index);
|
|
35764
|
+
}
|
|
35765
|
+
const cols = Array.from(tableElement.querySelectorAll("colgroup > col"));
|
|
35766
|
+
const parsedWidths = cols.slice(0, map.width).map((col) => parsePixelMetric(col.style.width) ?? parsePixelMetric(col.getAttribute("width")));
|
|
35767
|
+
const hasCompleteColWidths = parsedWidths.length >= map.width && parsedWidths.every((width) => typeof width === "number");
|
|
35768
|
+
let cursor = tableLeft;
|
|
35769
|
+
return Array.from({ length: map.width }, (_, index) => {
|
|
35770
|
+
const size = hasCompleteColWidths ? parsedWidths[index] : fallbackWidth;
|
|
35771
|
+
const start = hasCompleteColWidths ? cursor : tableLeft + index * fallbackWidth;
|
|
35772
|
+
cursor += size;
|
|
35773
|
+
return {
|
|
35774
|
+
index,
|
|
35775
|
+
cellPos: tableInfo.start + map.positionAt(0, index, tableInfo.node),
|
|
35776
|
+
start,
|
|
35777
|
+
size,
|
|
35778
|
+
center: start + size / 2
|
|
35779
|
+
};
|
|
35780
|
+
});
|
|
35781
|
+
}
|
|
35782
|
+
function buildLogicalRowMetrics({
|
|
35783
|
+
editor,
|
|
35784
|
+
surface,
|
|
35785
|
+
surfaceRect,
|
|
35786
|
+
tableInfo,
|
|
35787
|
+
rows,
|
|
35788
|
+
tableTop,
|
|
35789
|
+
tableHeight,
|
|
35790
|
+
cornerCell
|
|
35791
|
+
}) {
|
|
35792
|
+
const map = TableMap3.get(tableInfo.node);
|
|
35793
|
+
const fallbackHeight = metricOrFallback(tableHeight / map.height, FALLBACK_TABLE_ROW_HEIGHT);
|
|
35794
|
+
const visualRows = [];
|
|
35795
|
+
const seenCellPositions = /* @__PURE__ */ new Set();
|
|
35796
|
+
for (let rowIndex = 0; rowIndex < map.height; rowIndex += 1) {
|
|
35797
|
+
const relativeCellPos = map.map[rowIndex * map.width];
|
|
35798
|
+
if (seenCellPositions.has(relativeCellPos)) continue;
|
|
35799
|
+
seenCellPositions.add(relativeCellPos);
|
|
35800
|
+
const cellMapRect = map.findCell(relativeCellPos);
|
|
35801
|
+
const cellDom = editor.view.nodeDOM(tableInfo.start + relativeCellPos);
|
|
35802
|
+
const tableCell = cellDom instanceof HTMLTableCellElement ? cellDom : null;
|
|
35803
|
+
if (tableCell) {
|
|
35804
|
+
const cellRect = tableCell.getBoundingClientRect();
|
|
35805
|
+
const start = cellRect.height > 0 ? cellRect.top - surfaceRect.top + surface.scrollTop : tableTop + cellMapRect.top * fallbackHeight;
|
|
35806
|
+
const size = metricOrFallback(cellRect.height, fallbackHeight * Math.max(1, cellMapRect.bottom - cellMapRect.top));
|
|
35807
|
+
visualRows.push({
|
|
35808
|
+
index: cellMapRect.top,
|
|
35809
|
+
cellPos: tableInfo.start + relativeCellPos,
|
|
35810
|
+
start,
|
|
35811
|
+
size,
|
|
35812
|
+
center: start + size / 2
|
|
35813
|
+
});
|
|
35814
|
+
}
|
|
35815
|
+
}
|
|
35816
|
+
if (visualRows.length > 0) {
|
|
35817
|
+
return visualRows.sort((a, b) => a.index - b.index);
|
|
35818
|
+
}
|
|
35819
|
+
return rows.map((tableRow, index) => {
|
|
35820
|
+
const rowRect = tableRow.getBoundingClientRect();
|
|
35821
|
+
const anchorCell = tableRow.cells.item(0) ?? cornerCell;
|
|
35822
|
+
const start = rowRect.height > 0 ? rowRect.top - surfaceRect.top + surface.scrollTop : tableTop + index * fallbackHeight;
|
|
35823
|
+
const size = metricOrFallback(rowRect.height, fallbackHeight);
|
|
35824
|
+
return {
|
|
35825
|
+
index,
|
|
35826
|
+
cellPos: editor.view.posAtDOM(anchorCell, 0),
|
|
35827
|
+
start,
|
|
35828
|
+
size,
|
|
35829
|
+
center: start + size / 2
|
|
35830
|
+
};
|
|
35831
|
+
});
|
|
35832
|
+
}
|
|
35833
|
+
function buildTableControlLayout(editor, surface, cell) {
|
|
35834
|
+
const row = cell.closest("tr");
|
|
35835
|
+
const table = cell.closest("table");
|
|
35836
|
+
if (!(row instanceof HTMLTableRowElement) || !(table instanceof HTMLTableElement)) {
|
|
35837
|
+
return null;
|
|
35838
|
+
}
|
|
35839
|
+
const rows = Array.from(table.rows).filter((item) => item instanceof HTMLTableRowElement);
|
|
35840
|
+
const cornerCell = getLastCell(table);
|
|
35841
|
+
const cellPos = editor.view.posAtDOM(cell, 0);
|
|
35842
|
+
const tableInfo = findTableInfo(editor, cellPos);
|
|
35843
|
+
if (rows.length === 0 || !tableInfo || !(cornerCell instanceof HTMLTableCellElement)) {
|
|
35844
|
+
return null;
|
|
35845
|
+
}
|
|
35846
|
+
const map = TableMap3.get(tableInfo.node);
|
|
35847
|
+
const surfaceRect = surface.getBoundingClientRect();
|
|
35848
|
+
const tableRect = table.getBoundingClientRect();
|
|
35849
|
+
const wrapperElement = table.closest(".tableWrapper");
|
|
35850
|
+
const wrapper = wrapperElement instanceof HTMLElement ? wrapperElement : null;
|
|
35851
|
+
const wrapperRect = wrapper?.getBoundingClientRect() ?? tableRect;
|
|
35852
|
+
const tableLeft = tableRect.left - surfaceRect.left + surface.scrollLeft;
|
|
35853
|
+
const tableTop = tableRect.top - surfaceRect.top + surface.scrollTop;
|
|
35854
|
+
const avgRowHeight = metricOrFallback(tableRect.height / rows.length, FALLBACK_TABLE_ROW_HEIGHT);
|
|
35855
|
+
const avgColumnWidth = metricOrFallback(tableRect.width / map.width, FALLBACK_TABLE_COLUMN_WIDTH);
|
|
35856
|
+
const tableWidth = metricOrFallback(tableRect.width, avgColumnWidth * map.width);
|
|
35857
|
+
const tableHeight = metricOrFallback(tableRect.height, avgRowHeight * rows.length);
|
|
35858
|
+
const wrapperLeft = wrapperRect.left - surfaceRect.left + surface.scrollLeft;
|
|
35859
|
+
const wrapperTop = wrapperRect.top - surfaceRect.top + surface.scrollTop;
|
|
35860
|
+
const wrapperWidth = metricOrFallback(wrapperRect.width, tableWidth);
|
|
35861
|
+
const wrapperHeight = metricOrFallback(wrapperRect.height, tableHeight);
|
|
35862
|
+
const viewportWidth = metricOrFallback(wrapper?.clientWidth ?? wrapperRect.width, tableWidth);
|
|
35863
|
+
const viewportHeight = metricOrFallback(wrapper?.clientHeight ?? wrapperRect.height, tableHeight);
|
|
35864
|
+
const verticalScrollbarWidth = Math.max(0, Math.round(wrapperWidth - viewportWidth));
|
|
35865
|
+
const horizontalScrollbarHeight = Math.max(0, Math.round(wrapperHeight - viewportHeight));
|
|
35866
|
+
const rowHandles = buildLogicalRowMetrics({
|
|
35867
|
+
editor,
|
|
35868
|
+
surface,
|
|
35869
|
+
surfaceRect,
|
|
35870
|
+
tableInfo,
|
|
35871
|
+
rows,
|
|
35872
|
+
tableTop,
|
|
35873
|
+
tableHeight,
|
|
35874
|
+
cornerCell
|
|
35875
|
+
});
|
|
35876
|
+
const columnHandles = buildLogicalColumnMetrics({
|
|
35877
|
+
editor,
|
|
35878
|
+
surface,
|
|
35879
|
+
surfaceRect,
|
|
35880
|
+
tableElement: table,
|
|
35881
|
+
tableInfo,
|
|
35882
|
+
tableLeft,
|
|
35883
|
+
tableWidth
|
|
35884
|
+
});
|
|
35885
|
+
const activeCellRelativePos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
|
|
35886
|
+
const activeCellRect = activeCellRelativePos != null ? map.findCell(activeCellRelativePos) : { left: cell.cellIndex, top: row.rowIndex };
|
|
35887
|
+
const normalizedCellPos = activeCellRelativePos != null ? tableInfo.start + activeCellRelativePos : cellPos;
|
|
35888
|
+
return {
|
|
35889
|
+
cellPos: normalizedCellPos,
|
|
35890
|
+
cornerCellPos: tableInfo.start + map.positionAt(map.height - 1, map.width - 1, tableInfo.node),
|
|
35891
|
+
activeRowIndex: activeCellRect.top,
|
|
35892
|
+
activeColumnIndex: activeCellRect.left,
|
|
35893
|
+
tableLeft,
|
|
35894
|
+
tableTop,
|
|
35895
|
+
tableWidth,
|
|
35896
|
+
tableHeight,
|
|
35897
|
+
wrapperLeft,
|
|
35898
|
+
wrapperTop,
|
|
35899
|
+
wrapperWidth,
|
|
35900
|
+
wrapperHeight,
|
|
35901
|
+
viewportWidth,
|
|
35902
|
+
viewportHeight,
|
|
35903
|
+
horizontalScrollbarHeight,
|
|
35904
|
+
verticalScrollbarWidth,
|
|
35905
|
+
avgRowHeight,
|
|
35906
|
+
avgColumnWidth,
|
|
35907
|
+
rowHandles,
|
|
35908
|
+
columnHandles
|
|
35909
|
+
};
|
|
35910
|
+
}
|
|
35911
|
+
|
|
34475
35912
|
// src/components/UEditor/table-hover-state.ts
|
|
34476
35913
|
var MENU_HOVER_PADDING = 18;
|
|
34477
35914
|
var ROW_HANDLE_HOVER_WIDTH = 28;
|
|
@@ -34505,18 +35942,18 @@ function buildTableHoverState({
|
|
|
34505
35942
|
const directAddRow = targetElement?.closest?.("[data-table-control='add-row']");
|
|
34506
35943
|
const directRowHandleIndex = directRowHandle instanceof HTMLElement ? Number.parseInt(directRowHandle.dataset.rowHandleIndex ?? "", 10) : Number.NaN;
|
|
34507
35944
|
const directColumnHandleIndex = directColumnHandle instanceof HTMLElement ? Number.parseInt(directColumnHandle.dataset.columnHandleIndex ?? "", 10) : Number.NaN;
|
|
34508
|
-
const
|
|
34509
|
-
const
|
|
35945
|
+
const visibleBounds = getVisibleTableBounds(layout);
|
|
35946
|
+
const rowRailTop = layout.wrapperTop + layout.wrapperHeight;
|
|
34510
35947
|
const isMouseInTable = relativeX >= layout.tableLeft && relativeX <= layout.tableLeft + layout.tableWidth && relativeY >= layout.tableTop && relativeY <= layout.tableTop + layout.tableHeight;
|
|
34511
35948
|
const rowHandleIndex = Number.isFinite(directRowHandleIndex) ? directRowHandleIndex : layout.rowHandles.find((rowHandle) => relativeX >= layout.tableLeft - ROW_HANDLE_HOVER_WIDTH && relativeX <= layout.tableLeft && Math.abs(relativeY - rowHandle.center) <= HANDLE_HOVER_RADIUS)?.index ?? null;
|
|
34512
35949
|
const columnHandleIndex = Number.isFinite(directColumnHandleIndex) ? directColumnHandleIndex : layout.columnHandles.find((columnHandle) => relativeY >= layout.tableTop - COLUMN_HANDLE_HOVER_HEIGHT && relativeY <= layout.tableTop && Math.abs(relativeX - columnHandle.center) <= HANDLE_HOVER_RADIUS)?.index ?? null;
|
|
34513
35950
|
const menuVisible = Boolean(directTableMenu) || isMouseInTable || relativeX >= layout.tableLeft - MENU_HOVER_PADDING && relativeX <= layout.tableLeft + 42 && relativeY >= layout.tableTop - COLUMN_HANDLE_HOVER_HEIGHT && relativeY <= layout.tableTop + MENU_HOVER_PADDING;
|
|
34514
35951
|
const lastRow = layout.rowHandles[layout.rowHandles.length - 1];
|
|
34515
35952
|
const lastCol = layout.columnHandles[layout.columnHandles.length - 1];
|
|
34516
|
-
const isMouseInLastColumn = lastCol ? relativeX >= lastCol.start && relativeX <= lastCol.start + lastCol.size && relativeY >=
|
|
34517
|
-
const addColumnVisible = Boolean(directAddColumn) || relativeX >=
|
|
34518
|
-
const isMouseInLastRow = lastRow ? relativeY >= lastRow.start && relativeY <= lastRow.start + lastRow.size && relativeX >=
|
|
34519
|
-
const addRowVisible = Boolean(directAddRow) || relativeY >=
|
|
35953
|
+
const isMouseInLastColumn = lastCol ? relativeX >= lastCol.start && relativeX <= lastCol.start + lastCol.size && relativeY >= visibleBounds.top && relativeY <= visibleBounds.bottom : false;
|
|
35954
|
+
const addColumnVisible = Boolean(directAddColumn) || relativeX >= visibleBounds.right && relativeX <= visibleBounds.right + ADD_COLUMN_HOVER_WIDTH && relativeY >= visibleBounds.top && relativeY <= visibleBounds.bottom || isMouseInLastColumn;
|
|
35955
|
+
const isMouseInLastRow = lastRow ? relativeY >= lastRow.start && relativeY <= lastRow.start + lastRow.size && relativeX >= visibleBounds.left && relativeX <= visibleBounds.right : false;
|
|
35956
|
+
const addRowVisible = Boolean(directAddRow) || relativeY >= rowRailTop && relativeY <= rowRailTop + ADD_ROW_HOVER_HEIGHT && relativeX >= visibleBounds.left && relativeX <= visibleBounds.right || isMouseInLastRow;
|
|
34520
35957
|
return {
|
|
34521
35958
|
menuVisible,
|
|
34522
35959
|
addColumnVisible,
|
|
@@ -34638,12 +36075,11 @@ function TableAddRails({
|
|
|
34638
36075
|
quickAddColumnLabel,
|
|
34639
36076
|
quickAddRowLabel
|
|
34640
36077
|
}) {
|
|
34641
|
-
const
|
|
34642
|
-
const
|
|
34643
|
-
const
|
|
34644
|
-
const columnRailLeft = layout.tableLeft + visibleTableWidth + ADD_COLUMN_RAIL_GAP;
|
|
36078
|
+
const visibleBounds = getVisibleTableBounds(layout);
|
|
36079
|
+
const columnRailTop = visibleBounds.top;
|
|
36080
|
+
const columnRailLeft = visibleBounds.right + ADD_COLUMN_RAIL_GAP;
|
|
34645
36081
|
const rowRailTop = layout.wrapperTop + layout.wrapperHeight + ADD_ROW_RAIL_GAP;
|
|
34646
|
-
const rowRailLeft =
|
|
36082
|
+
const rowRailLeft = visibleBounds.left;
|
|
34647
36083
|
const showColumnRail = controlsVisible || addColumnVisible;
|
|
34648
36084
|
const showRowRail = controlsVisible || addRowVisible;
|
|
34649
36085
|
return /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(import_jsx_runtime89.Fragment, { children: [
|
|
@@ -34671,10 +36107,10 @@ function TableAddRails({
|
|
|
34671
36107
|
"transition-[opacity,transform,colors] duration-150 hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed"
|
|
34672
36108
|
),
|
|
34673
36109
|
style: {
|
|
34674
|
-
top: showColumnRail ? columnRailTop : columnRailTop + Math.max(0,
|
|
36110
|
+
top: showColumnRail ? columnRailTop : columnRailTop + Math.max(0, visibleBounds.height / 2 - 24),
|
|
34675
36111
|
left: columnRailLeft,
|
|
34676
36112
|
width: showColumnRail ? 18 : 12,
|
|
34677
|
-
height: showColumnRail ?
|
|
36113
|
+
height: showColumnRail ? visibleBounds.height : 48,
|
|
34678
36114
|
opacity: showColumnRail ? 1 : 0,
|
|
34679
36115
|
transform: showColumnRail ? "scale(1)" : "scale(0.92)",
|
|
34680
36116
|
pointerEvents: showColumnRail ? "auto" : "none"
|
|
@@ -34709,8 +36145,8 @@ function TableAddRails({
|
|
|
34709
36145
|
),
|
|
34710
36146
|
style: {
|
|
34711
36147
|
top: rowRailTop,
|
|
34712
|
-
left: showRowRail ? rowRailLeft : rowRailLeft + Math.max(0,
|
|
34713
|
-
width: showRowRail ?
|
|
36148
|
+
left: showRowRail ? rowRailLeft : rowRailLeft + Math.max(0, visibleBounds.width / 2 - 24),
|
|
36149
|
+
width: showRowRail ? visibleBounds.width : 48,
|
|
34714
36150
|
height: showRowRail ? 16 : 12,
|
|
34715
36151
|
opacity: showRowRail ? 1 : 0,
|
|
34716
36152
|
transform: showRowRail ? "scale(1)" : "scale(0.92)",
|
|
@@ -34935,248 +36371,6 @@ function TableColumnHandles({
|
|
|
34935
36371
|
}) });
|
|
34936
36372
|
}
|
|
34937
36373
|
|
|
34938
|
-
// src/components/UEditor/table-layout-model.ts
|
|
34939
|
-
var FALLBACK_TABLE_ROW_HEIGHT = 44;
|
|
34940
|
-
var FALLBACK_TABLE_COLUMN_WIDTH = 160;
|
|
34941
|
-
function metricOrFallback(value, fallback) {
|
|
34942
|
-
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
34943
|
-
}
|
|
34944
|
-
function parsePixelMetric(value) {
|
|
34945
|
-
if (!value) return null;
|
|
34946
|
-
const parsed = Number.parseFloat(value);
|
|
34947
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
34948
|
-
}
|
|
34949
|
-
function getPrimaryCell(table) {
|
|
34950
|
-
const cell = table.querySelector("th,td");
|
|
34951
|
-
return cell instanceof HTMLTableCellElement ? cell : null;
|
|
34952
|
-
}
|
|
34953
|
-
function getLastCell(table) {
|
|
34954
|
-
const lastRow = table.rows.item(table.rows.length - 1);
|
|
34955
|
-
if (!(lastRow instanceof HTMLTableRowElement)) return null;
|
|
34956
|
-
const cell = lastRow.cells.item(lastRow.cells.length - 1);
|
|
34957
|
-
return cell instanceof HTMLTableCellElement ? cell : null;
|
|
34958
|
-
}
|
|
34959
|
-
function getCellFromTarget(target) {
|
|
34960
|
-
const element = resolveEventElement(target);
|
|
34961
|
-
if (!element) return null;
|
|
34962
|
-
const directCell = element.closest("th,td");
|
|
34963
|
-
if (directCell instanceof HTMLTableCellElement) {
|
|
34964
|
-
return directCell;
|
|
34965
|
-
}
|
|
34966
|
-
const table = element.closest("table");
|
|
34967
|
-
if (table instanceof HTMLTableElement) {
|
|
34968
|
-
return getPrimaryCell(table);
|
|
34969
|
-
}
|
|
34970
|
-
return null;
|
|
34971
|
-
}
|
|
34972
|
-
function findTableInfo(editor, pos) {
|
|
34973
|
-
const $pos = editor.state.doc.resolve(pos);
|
|
34974
|
-
for (let depth = $pos.depth; depth > 0; depth -= 1) {
|
|
34975
|
-
const node = $pos.node(depth);
|
|
34976
|
-
if (node.type.name === "table") {
|
|
34977
|
-
return {
|
|
34978
|
-
node,
|
|
34979
|
-
pos: $pos.before(depth),
|
|
34980
|
-
start: $pos.start(depth)
|
|
34981
|
-
};
|
|
34982
|
-
}
|
|
34983
|
-
}
|
|
34984
|
-
return null;
|
|
34985
|
-
}
|
|
34986
|
-
function getCellRelativePosFromDomPos(map, tableStart, domPos) {
|
|
34987
|
-
const relativeDomPos = domPos - tableStart;
|
|
34988
|
-
const seen = /* @__PURE__ */ new Set();
|
|
34989
|
-
for (const relativeCellPos of map.map) {
|
|
34990
|
-
if (seen.has(relativeCellPos)) continue;
|
|
34991
|
-
seen.add(relativeCellPos);
|
|
34992
|
-
if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
|
|
34993
|
-
return relativeCellPos;
|
|
34994
|
-
}
|
|
34995
|
-
}
|
|
34996
|
-
return null;
|
|
34997
|
-
}
|
|
34998
|
-
function buildLogicalColumnMetrics({
|
|
34999
|
-
editor,
|
|
35000
|
-
surface,
|
|
35001
|
-
surfaceRect,
|
|
35002
|
-
tableElement,
|
|
35003
|
-
tableInfo,
|
|
35004
|
-
tableLeft,
|
|
35005
|
-
tableWidth
|
|
35006
|
-
}) {
|
|
35007
|
-
const map = TableMap2.get(tableInfo.node);
|
|
35008
|
-
const fallbackWidth = metricOrFallback(tableWidth / map.width, FALLBACK_TABLE_COLUMN_WIDTH);
|
|
35009
|
-
const firstRow = tableElement.rows.item(0);
|
|
35010
|
-
const visualColumns = [];
|
|
35011
|
-
if (firstRow) {
|
|
35012
|
-
for (const tableCell of Array.from(firstRow.cells)) {
|
|
35013
|
-
if (!(tableCell instanceof HTMLTableCellElement)) continue;
|
|
35014
|
-
const cellPos = editor.view.posAtDOM(tableCell, 0);
|
|
35015
|
-
const relativeCellPos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
|
|
35016
|
-
if (relativeCellPos == null) continue;
|
|
35017
|
-
const cellMapRect = map.findCell(relativeCellPos);
|
|
35018
|
-
const cellRect = tableCell.getBoundingClientRect();
|
|
35019
|
-
const cellStart = cellRect.width > 0 ? cellRect.left - surfaceRect.left + surface.scrollLeft : tableLeft + cellMapRect.left * fallbackWidth;
|
|
35020
|
-
const size = metricOrFallback(cellRect.width, fallbackWidth * Math.max(1, cellMapRect.right - cellMapRect.left));
|
|
35021
|
-
visualColumns.push({
|
|
35022
|
-
index: cellMapRect.left,
|
|
35023
|
-
cellPos: tableInfo.start + relativeCellPos,
|
|
35024
|
-
start: cellStart,
|
|
35025
|
-
size,
|
|
35026
|
-
center: cellStart + size / 2
|
|
35027
|
-
});
|
|
35028
|
-
}
|
|
35029
|
-
}
|
|
35030
|
-
if (visualColumns.length > 0) {
|
|
35031
|
-
return visualColumns.sort((a, b) => a.index - b.index);
|
|
35032
|
-
}
|
|
35033
|
-
const cols = Array.from(tableElement.querySelectorAll("colgroup > col"));
|
|
35034
|
-
const parsedWidths = cols.slice(0, map.width).map((col) => parsePixelMetric(col.style.width) ?? parsePixelMetric(col.getAttribute("width")));
|
|
35035
|
-
const hasCompleteColWidths = parsedWidths.length >= map.width && parsedWidths.every((width) => typeof width === "number");
|
|
35036
|
-
let cursor = tableLeft;
|
|
35037
|
-
return Array.from({ length: map.width }, (_, index) => {
|
|
35038
|
-
const size = hasCompleteColWidths ? parsedWidths[index] : fallbackWidth;
|
|
35039
|
-
const start = hasCompleteColWidths ? cursor : tableLeft + index * fallbackWidth;
|
|
35040
|
-
cursor += size;
|
|
35041
|
-
return {
|
|
35042
|
-
index,
|
|
35043
|
-
cellPos: tableInfo.start + map.positionAt(0, index, tableInfo.node),
|
|
35044
|
-
start,
|
|
35045
|
-
size,
|
|
35046
|
-
center: start + size / 2
|
|
35047
|
-
};
|
|
35048
|
-
});
|
|
35049
|
-
}
|
|
35050
|
-
function buildLogicalRowMetrics({
|
|
35051
|
-
editor,
|
|
35052
|
-
surface,
|
|
35053
|
-
surfaceRect,
|
|
35054
|
-
tableInfo,
|
|
35055
|
-
rows,
|
|
35056
|
-
tableTop,
|
|
35057
|
-
tableHeight,
|
|
35058
|
-
cornerCell
|
|
35059
|
-
}) {
|
|
35060
|
-
const map = TableMap2.get(tableInfo.node);
|
|
35061
|
-
const fallbackHeight = metricOrFallback(tableHeight / map.height, FALLBACK_TABLE_ROW_HEIGHT);
|
|
35062
|
-
const visualRows = [];
|
|
35063
|
-
const seenCellPositions = /* @__PURE__ */ new Set();
|
|
35064
|
-
for (let rowIndex = 0; rowIndex < map.height; rowIndex += 1) {
|
|
35065
|
-
const relativeCellPos = map.map[rowIndex * map.width];
|
|
35066
|
-
if (seenCellPositions.has(relativeCellPos)) continue;
|
|
35067
|
-
seenCellPositions.add(relativeCellPos);
|
|
35068
|
-
const cellMapRect = map.findCell(relativeCellPos);
|
|
35069
|
-
const cellDom = editor.view.nodeDOM(tableInfo.start + relativeCellPos);
|
|
35070
|
-
const tableCell = cellDom instanceof HTMLTableCellElement ? cellDom : null;
|
|
35071
|
-
if (tableCell) {
|
|
35072
|
-
const cellRect = tableCell.getBoundingClientRect();
|
|
35073
|
-
const start = cellRect.height > 0 ? cellRect.top - surfaceRect.top + surface.scrollTop : tableTop + cellMapRect.top * fallbackHeight;
|
|
35074
|
-
const size = metricOrFallback(cellRect.height, fallbackHeight * Math.max(1, cellMapRect.bottom - cellMapRect.top));
|
|
35075
|
-
visualRows.push({
|
|
35076
|
-
index: cellMapRect.top,
|
|
35077
|
-
cellPos: tableInfo.start + relativeCellPos,
|
|
35078
|
-
start,
|
|
35079
|
-
size,
|
|
35080
|
-
center: start + size / 2
|
|
35081
|
-
});
|
|
35082
|
-
}
|
|
35083
|
-
}
|
|
35084
|
-
if (visualRows.length > 0) {
|
|
35085
|
-
return visualRows.sort((a, b) => a.index - b.index);
|
|
35086
|
-
}
|
|
35087
|
-
return rows.map((tableRow, index) => {
|
|
35088
|
-
const rowRect = tableRow.getBoundingClientRect();
|
|
35089
|
-
const anchorCell = tableRow.cells.item(0) ?? cornerCell;
|
|
35090
|
-
const start = rowRect.height > 0 ? rowRect.top - surfaceRect.top + surface.scrollTop : tableTop + index * fallbackHeight;
|
|
35091
|
-
const size = metricOrFallback(rowRect.height, fallbackHeight);
|
|
35092
|
-
return {
|
|
35093
|
-
index,
|
|
35094
|
-
cellPos: editor.view.posAtDOM(anchorCell, 0),
|
|
35095
|
-
start,
|
|
35096
|
-
size,
|
|
35097
|
-
center: start + size / 2
|
|
35098
|
-
};
|
|
35099
|
-
});
|
|
35100
|
-
}
|
|
35101
|
-
function buildTableControlLayout(editor, surface, cell) {
|
|
35102
|
-
const row = cell.closest("tr");
|
|
35103
|
-
const table = cell.closest("table");
|
|
35104
|
-
if (!(row instanceof HTMLTableRowElement) || !(table instanceof HTMLTableElement)) {
|
|
35105
|
-
return null;
|
|
35106
|
-
}
|
|
35107
|
-
const rows = Array.from(table.rows).filter((item) => item instanceof HTMLTableRowElement);
|
|
35108
|
-
const cornerCell = getLastCell(table);
|
|
35109
|
-
const cellPos = editor.view.posAtDOM(cell, 0);
|
|
35110
|
-
const tableInfo = findTableInfo(editor, cellPos);
|
|
35111
|
-
if (rows.length === 0 || !tableInfo || !(cornerCell instanceof HTMLTableCellElement)) {
|
|
35112
|
-
return null;
|
|
35113
|
-
}
|
|
35114
|
-
const map = TableMap2.get(tableInfo.node);
|
|
35115
|
-
const surfaceRect = surface.getBoundingClientRect();
|
|
35116
|
-
const tableRect = table.getBoundingClientRect();
|
|
35117
|
-
const wrapperElement = table.closest(".tableWrapper");
|
|
35118
|
-
const wrapper = wrapperElement instanceof HTMLElement ? wrapperElement : null;
|
|
35119
|
-
const wrapperRect = wrapper?.getBoundingClientRect() ?? tableRect;
|
|
35120
|
-
const tableLeft = tableRect.left - surfaceRect.left + surface.scrollLeft;
|
|
35121
|
-
const tableTop = tableRect.top - surfaceRect.top + surface.scrollTop;
|
|
35122
|
-
const avgRowHeight = metricOrFallback(tableRect.height / rows.length, FALLBACK_TABLE_ROW_HEIGHT);
|
|
35123
|
-
const avgColumnWidth = metricOrFallback(tableRect.width / map.width, FALLBACK_TABLE_COLUMN_WIDTH);
|
|
35124
|
-
const tableWidth = metricOrFallback(tableRect.width, avgColumnWidth * map.width);
|
|
35125
|
-
const tableHeight = metricOrFallback(tableRect.height, avgRowHeight * rows.length);
|
|
35126
|
-
const wrapperLeft = wrapperRect.left - surfaceRect.left + surface.scrollLeft;
|
|
35127
|
-
const wrapperTop = wrapperRect.top - surfaceRect.top + surface.scrollTop;
|
|
35128
|
-
const wrapperWidth = metricOrFallback(wrapperRect.width, tableWidth);
|
|
35129
|
-
const wrapperHeight = metricOrFallback(wrapperRect.height, tableHeight);
|
|
35130
|
-
const viewportWidth = metricOrFallback(wrapper?.clientWidth ?? wrapperRect.width, tableWidth);
|
|
35131
|
-
const viewportHeight = metricOrFallback(wrapper?.clientHeight ?? wrapperRect.height, tableHeight);
|
|
35132
|
-
const verticalScrollbarWidth = Math.max(0, Math.round(wrapperWidth - viewportWidth));
|
|
35133
|
-
const horizontalScrollbarHeight = Math.max(0, Math.round(wrapperHeight - viewportHeight));
|
|
35134
|
-
const rowHandles = buildLogicalRowMetrics({
|
|
35135
|
-
editor,
|
|
35136
|
-
surface,
|
|
35137
|
-
surfaceRect,
|
|
35138
|
-
tableInfo,
|
|
35139
|
-
rows,
|
|
35140
|
-
tableTop,
|
|
35141
|
-
tableHeight,
|
|
35142
|
-
cornerCell
|
|
35143
|
-
});
|
|
35144
|
-
const columnHandles = buildLogicalColumnMetrics({
|
|
35145
|
-
editor,
|
|
35146
|
-
surface,
|
|
35147
|
-
surfaceRect,
|
|
35148
|
-
tableElement: table,
|
|
35149
|
-
tableInfo,
|
|
35150
|
-
tableLeft,
|
|
35151
|
-
tableWidth
|
|
35152
|
-
});
|
|
35153
|
-
const activeCellRelativePos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
|
|
35154
|
-
const activeCellRect = activeCellRelativePos != null ? map.findCell(activeCellRelativePos) : { left: cell.cellIndex, top: row.rowIndex };
|
|
35155
|
-
const normalizedCellPos = activeCellRelativePos != null ? tableInfo.start + activeCellRelativePos : cellPos;
|
|
35156
|
-
return {
|
|
35157
|
-
cellPos: normalizedCellPos,
|
|
35158
|
-
cornerCellPos: tableInfo.start + map.positionAt(map.height - 1, map.width - 1, tableInfo.node),
|
|
35159
|
-
activeRowIndex: activeCellRect.top,
|
|
35160
|
-
activeColumnIndex: activeCellRect.left,
|
|
35161
|
-
tableLeft,
|
|
35162
|
-
tableTop,
|
|
35163
|
-
tableWidth,
|
|
35164
|
-
tableHeight,
|
|
35165
|
-
wrapperLeft,
|
|
35166
|
-
wrapperTop,
|
|
35167
|
-
wrapperWidth,
|
|
35168
|
-
wrapperHeight,
|
|
35169
|
-
viewportWidth,
|
|
35170
|
-
viewportHeight,
|
|
35171
|
-
horizontalScrollbarHeight,
|
|
35172
|
-
verticalScrollbarWidth,
|
|
35173
|
-
avgRowHeight,
|
|
35174
|
-
avgColumnWidth,
|
|
35175
|
-
rowHandles,
|
|
35176
|
-
columnHandles
|
|
35177
|
-
};
|
|
35178
|
-
}
|
|
35179
|
-
|
|
35180
36374
|
// src/components/UEditor/table-controls.tsx
|
|
35181
36375
|
var import_jsx_runtime92 = require("react/jsx-runtime");
|
|
35182
36376
|
var TABLE_MENU_TOP_OFFSET = 10;
|