@underverse-ui/underverse 1.0.149 → 1.0.150
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 +583 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +583 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -25681,6 +25681,8 @@ import { Extension as Extension2 } from "@tiptap/core";
|
|
|
25681
25681
|
import { Plugin as Plugin2 } from "@tiptap/pm/state";
|
|
25682
25682
|
|
|
25683
25683
|
// src/components/UEditor/clipboard-tables.ts
|
|
25684
|
+
var DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR = "#ffffff";
|
|
25685
|
+
var DEFAULT_HTML_TABLE_TEXT_COLOR = "#000000";
|
|
25684
25686
|
function getClipboardData(dataTransfer, type) {
|
|
25685
25687
|
try {
|
|
25686
25688
|
return dataTransfer.getData(type) ?? "";
|
|
@@ -25701,6 +25703,411 @@ function extractClipboardHtmlFragment(html) {
|
|
|
25701
25703
|
function normalizeClipboardCellText(value) {
|
|
25702
25704
|
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();
|
|
25703
25705
|
}
|
|
25706
|
+
function parseStyleDeclarations(styleText) {
|
|
25707
|
+
const declarations = /* @__PURE__ */ new Map();
|
|
25708
|
+
if (!styleText) return declarations;
|
|
25709
|
+
for (const declaration of styleText.split(";")) {
|
|
25710
|
+
const separatorIndex = declaration.indexOf(":");
|
|
25711
|
+
if (separatorIndex <= 0) continue;
|
|
25712
|
+
const property = declaration.slice(0, separatorIndex).trim().toLowerCase();
|
|
25713
|
+
const value = cleanStyleValue(declaration.slice(separatorIndex + 1));
|
|
25714
|
+
if (!property || !value) continue;
|
|
25715
|
+
declarations.set(property, value);
|
|
25716
|
+
}
|
|
25717
|
+
return declarations;
|
|
25718
|
+
}
|
|
25719
|
+
function mergeStyleDeclarations(...sources) {
|
|
25720
|
+
const declarations = /* @__PURE__ */ new Map();
|
|
25721
|
+
for (const source of sources) {
|
|
25722
|
+
if (!source) continue;
|
|
25723
|
+
for (const [property, value] of source.entries()) {
|
|
25724
|
+
declarations.set(property, value);
|
|
25725
|
+
}
|
|
25726
|
+
}
|
|
25727
|
+
return declarations;
|
|
25728
|
+
}
|
|
25729
|
+
function extractCssClassNames(selectorText) {
|
|
25730
|
+
const classNames = /* @__PURE__ */ new Set();
|
|
25731
|
+
const classNamePattern = /\.([_a-zA-Z-][\w-]*)/g;
|
|
25732
|
+
let match;
|
|
25733
|
+
while ((match = classNamePattern.exec(selectorText)) !== null) {
|
|
25734
|
+
classNames.add(match[1]);
|
|
25735
|
+
}
|
|
25736
|
+
return classNames;
|
|
25737
|
+
}
|
|
25738
|
+
function parseClipboardCssClassStyles(doc) {
|
|
25739
|
+
const styleMap = /* @__PURE__ */ new Map();
|
|
25740
|
+
for (const styleElement of Array.from(doc.querySelectorAll("style"))) {
|
|
25741
|
+
const cssText = (styleElement.textContent ?? "").replace(/<!--|-->/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
25742
|
+
const rulePattern = /([^{}]+)\{([^{}]*)\}/g;
|
|
25743
|
+
let match;
|
|
25744
|
+
while ((match = rulePattern.exec(cssText)) !== null) {
|
|
25745
|
+
const classNames = extractCssClassNames(match[1]);
|
|
25746
|
+
if (classNames.size === 0) continue;
|
|
25747
|
+
const declarations = parseStyleDeclarations(match[2]);
|
|
25748
|
+
if (declarations.size === 0) continue;
|
|
25749
|
+
for (const className of classNames) {
|
|
25750
|
+
styleMap.set(className, mergeStyleDeclarations(styleMap.get(className), declarations));
|
|
25751
|
+
}
|
|
25752
|
+
}
|
|
25753
|
+
}
|
|
25754
|
+
return styleMap;
|
|
25755
|
+
}
|
|
25756
|
+
function getElementStyleDeclarations(element, styleMap) {
|
|
25757
|
+
const classDeclarations = Array.from(element.classList).map((className) => styleMap.get(className));
|
|
25758
|
+
const inlineDeclarations = parseStyleDeclarations(element.getAttribute("style"));
|
|
25759
|
+
return mergeStyleDeclarations(...classDeclarations, inlineDeclarations);
|
|
25760
|
+
}
|
|
25761
|
+
function cleanStyleValue(value) {
|
|
25762
|
+
const normalized = value?.trim();
|
|
25763
|
+
if (!normalized) return null;
|
|
25764
|
+
if (/[\0<>;{}]/.test(normalized)) return null;
|
|
25765
|
+
if (/\b(?:expression|url|(?:repeating-)?(?:linear|radial|conic)-gradient)\s*\(/i.test(normalized)) return null;
|
|
25766
|
+
return normalized;
|
|
25767
|
+
}
|
|
25768
|
+
function normalizeColorValue(value) {
|
|
25769
|
+
const normalized = cleanStyleValue(value);
|
|
25770
|
+
if (!normalized) return null;
|
|
25771
|
+
if (/^(?:auto|inherit|initial|none|transparent|unset)$/i.test(normalized)) return null;
|
|
25772
|
+
return normalized;
|
|
25773
|
+
}
|
|
25774
|
+
function normalizeTextColorValue(value) {
|
|
25775
|
+
const normalized = normalizeColorValue(value);
|
|
25776
|
+
if (!normalized) return null;
|
|
25777
|
+
if (/^(?:automatic|windowtext|black|#000|#000000|rgb\(\s*0\s*,\s*0\s*,\s*0\s*\))$/i.test(normalized)) {
|
|
25778
|
+
return DEFAULT_HTML_TABLE_TEXT_COLOR;
|
|
25779
|
+
}
|
|
25780
|
+
return normalized;
|
|
25781
|
+
}
|
|
25782
|
+
function isWhiteColor(value) {
|
|
25783
|
+
if (!value) return false;
|
|
25784
|
+
return /^(?:white|#fff|#ffffff|rgb\(\s*255\s*,\s*255\s*,\s*255\s*\))$/i.test(value.trim());
|
|
25785
|
+
}
|
|
25786
|
+
function parseCssColorRgb(value) {
|
|
25787
|
+
const normalized = normalizeColorValue(value);
|
|
25788
|
+
if (!normalized) return null;
|
|
25789
|
+
const lowerColor = normalized.toLowerCase();
|
|
25790
|
+
if (lowerColor === "white") return { r: 255, g: 255, b: 255 };
|
|
25791
|
+
if (lowerColor === "black") return { r: 0, g: 0, b: 0 };
|
|
25792
|
+
const hexMatch = lowerColor.match(/^#([\da-f]{3}|[\da-f]{6})$/i);
|
|
25793
|
+
if (hexMatch) {
|
|
25794
|
+
const hex = hexMatch[1];
|
|
25795
|
+
const fullHex = hex.length === 3 ? hex.split("").map((part) => part + part).join("") : hex;
|
|
25796
|
+
return {
|
|
25797
|
+
r: Number.parseInt(fullHex.slice(0, 2), 16),
|
|
25798
|
+
g: Number.parseInt(fullHex.slice(2, 4), 16),
|
|
25799
|
+
b: Number.parseInt(fullHex.slice(4, 6), 16)
|
|
25800
|
+
};
|
|
25801
|
+
}
|
|
25802
|
+
const rgbMatch = lowerColor.match(/^rgba?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)/);
|
|
25803
|
+
if (rgbMatch) {
|
|
25804
|
+
return {
|
|
25805
|
+
r: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[1]))),
|
|
25806
|
+
g: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[2]))),
|
|
25807
|
+
b: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[3])))
|
|
25808
|
+
};
|
|
25809
|
+
}
|
|
25810
|
+
return null;
|
|
25811
|
+
}
|
|
25812
|
+
function getRelativeLuminance(value) {
|
|
25813
|
+
const rgb = parseCssColorRgb(value);
|
|
25814
|
+
if (!rgb) return null;
|
|
25815
|
+
const toLinear = (channel) => {
|
|
25816
|
+
const normalized = channel / 255;
|
|
25817
|
+
return normalized <= 0.03928 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
|
|
25818
|
+
};
|
|
25819
|
+
return 0.2126 * toLinear(rgb.r) + 0.7152 * toLinear(rgb.g) + 0.0722 * toLinear(rgb.b);
|
|
25820
|
+
}
|
|
25821
|
+
function isLightTextColor(value) {
|
|
25822
|
+
const luminance = getRelativeLuminance(value);
|
|
25823
|
+
return luminance !== null && luminance >= 0.72;
|
|
25824
|
+
}
|
|
25825
|
+
function isDarkReadableBackground(value) {
|
|
25826
|
+
const luminance = getRelativeLuminance(value);
|
|
25827
|
+
return luminance !== null && luminance <= 0.45;
|
|
25828
|
+
}
|
|
25829
|
+
function splitCssTokens(value) {
|
|
25830
|
+
const tokens = [];
|
|
25831
|
+
let current = "";
|
|
25832
|
+
let depth = 0;
|
|
25833
|
+
for (const char of value) {
|
|
25834
|
+
if (char === "(") depth += 1;
|
|
25835
|
+
if (char === ")") depth = Math.max(0, depth - 1);
|
|
25836
|
+
if (/\s/.test(char) && depth === 0) {
|
|
25837
|
+
if (current) {
|
|
25838
|
+
tokens.push(current);
|
|
25839
|
+
current = "";
|
|
25840
|
+
}
|
|
25841
|
+
continue;
|
|
25842
|
+
}
|
|
25843
|
+
current += char;
|
|
25844
|
+
}
|
|
25845
|
+
if (current) tokens.push(current);
|
|
25846
|
+
return tokens;
|
|
25847
|
+
}
|
|
25848
|
+
function extractColorFromShorthand(value) {
|
|
25849
|
+
const normalized = cleanStyleValue(value);
|
|
25850
|
+
if (!normalized) return null;
|
|
25851
|
+
const explicitColor = normalized.match(/#[\da-f]{3,8}\b|rgba?\([^)]+\)|hsla?\([^)]+\)/i);
|
|
25852
|
+
if (explicitColor) return explicitColor[0];
|
|
25853
|
+
const ignoredKeywords = /* @__PURE__ */ new Set([
|
|
25854
|
+
"border-box",
|
|
25855
|
+
"center",
|
|
25856
|
+
"contain",
|
|
25857
|
+
"content-box",
|
|
25858
|
+
"cover",
|
|
25859
|
+
"fixed",
|
|
25860
|
+
"inherit",
|
|
25861
|
+
"initial",
|
|
25862
|
+
"left",
|
|
25863
|
+
"local",
|
|
25864
|
+
"none",
|
|
25865
|
+
"no-repeat",
|
|
25866
|
+
"padding-box",
|
|
25867
|
+
"repeat",
|
|
25868
|
+
"repeat-x",
|
|
25869
|
+
"repeat-y",
|
|
25870
|
+
"right",
|
|
25871
|
+
"scroll",
|
|
25872
|
+
"top",
|
|
25873
|
+
"transparent",
|
|
25874
|
+
"unset"
|
|
25875
|
+
]);
|
|
25876
|
+
return splitCssTokens(normalized).find((token) => !ignoredKeywords.has(token.toLowerCase())) ?? null;
|
|
25877
|
+
}
|
|
25878
|
+
function getBackgroundColor(styles) {
|
|
25879
|
+
return normalizeColorValue(styles.get("background-color")) ?? normalizeColorValue(extractColorFromShorthand(styles.get("background")));
|
|
25880
|
+
}
|
|
25881
|
+
var BORDER_STYLES = /* @__PURE__ */ new Set([
|
|
25882
|
+
"dashed",
|
|
25883
|
+
"dotted",
|
|
25884
|
+
"double",
|
|
25885
|
+
"groove",
|
|
25886
|
+
"hidden",
|
|
25887
|
+
"inset",
|
|
25888
|
+
"none",
|
|
25889
|
+
"outset",
|
|
25890
|
+
"ridge",
|
|
25891
|
+
"solid"
|
|
25892
|
+
]);
|
|
25893
|
+
var BORDER_WIDTH_KEYWORDS = /* @__PURE__ */ new Set(["medium", "thick", "thin"]);
|
|
25894
|
+
function normalizeBorderStyle(value) {
|
|
25895
|
+
const normalized = cleanStyleValue(value);
|
|
25896
|
+
if (!normalized) return null;
|
|
25897
|
+
const styles = splitCssTokens(normalized).filter((token) => BORDER_STYLES.has(token.toLowerCase()));
|
|
25898
|
+
const usefulStyles = styles.filter((style) => !/^(?:hidden|none)$/i.test(style));
|
|
25899
|
+
return usefulStyles.length > 0 ? usefulStyles.join(" ") : null;
|
|
25900
|
+
}
|
|
25901
|
+
function normalizeBorderWidth(value) {
|
|
25902
|
+
const normalized = cleanStyleValue(value);
|
|
25903
|
+
if (!normalized) return null;
|
|
25904
|
+
const widths = splitCssTokens(normalized).filter((token) => {
|
|
25905
|
+
const lowerToken = token.toLowerCase();
|
|
25906
|
+
return BORDER_WIDTH_KEYWORDS.has(lowerToken) || /^\d*\.?\d+(?:px|pt|pc|in|cm|mm|em|rem)?$/i.test(token);
|
|
25907
|
+
});
|
|
25908
|
+
return widths.length > 0 ? widths.join(" ") : null;
|
|
25909
|
+
}
|
|
25910
|
+
function parseBorderShorthand(value) {
|
|
25911
|
+
const normalized = cleanStyleValue(value);
|
|
25912
|
+
if (!normalized) return null;
|
|
25913
|
+
const tokens = splitCssTokens(normalized);
|
|
25914
|
+
let borderStyle = null;
|
|
25915
|
+
let borderWidth = null;
|
|
25916
|
+
const colorTokens = [];
|
|
25917
|
+
for (const token of tokens) {
|
|
25918
|
+
const lowerToken = token.toLowerCase();
|
|
25919
|
+
if (!borderStyle && BORDER_STYLES.has(lowerToken)) {
|
|
25920
|
+
borderStyle = lowerToken;
|
|
25921
|
+
continue;
|
|
25922
|
+
}
|
|
25923
|
+
if (!borderWidth && (BORDER_WIDTH_KEYWORDS.has(lowerToken) || /^\d*\.?\d+(?:px|pt|pc|in|cm|mm|em|rem)?$/i.test(token))) {
|
|
25924
|
+
borderWidth = token;
|
|
25925
|
+
continue;
|
|
25926
|
+
}
|
|
25927
|
+
colorTokens.push(token);
|
|
25928
|
+
}
|
|
25929
|
+
if (borderStyle && /^(?:hidden|none)$/i.test(borderStyle)) return null;
|
|
25930
|
+
return {
|
|
25931
|
+
borderColor: normalizeColorValue(colorTokens.join(" ")),
|
|
25932
|
+
borderStyle,
|
|
25933
|
+
borderWidth
|
|
25934
|
+
};
|
|
25935
|
+
}
|
|
25936
|
+
function getFirstParsedBorder(styles) {
|
|
25937
|
+
for (const property of ["border", "border-top", "border-right", "border-bottom", "border-left"]) {
|
|
25938
|
+
const border = parseBorderShorthand(styles.get(property));
|
|
25939
|
+
if (border) return border;
|
|
25940
|
+
}
|
|
25941
|
+
return null;
|
|
25942
|
+
}
|
|
25943
|
+
function getBorderAttrs(styles) {
|
|
25944
|
+
const parsedBorder = getFirstParsedBorder(styles);
|
|
25945
|
+
return {
|
|
25946
|
+
borderColor: normalizeColorValue(styles.get("border-color")) ?? parsedBorder?.borderColor ?? void 0,
|
|
25947
|
+
borderStyle: normalizeBorderStyle(styles.get("border-style")) ?? parsedBorder?.borderStyle ?? void 0,
|
|
25948
|
+
borderWidth: normalizeBorderWidth(styles.get("border-width")) ?? parsedBorder?.borderWidth ?? void 0
|
|
25949
|
+
};
|
|
25950
|
+
}
|
|
25951
|
+
function parsePositiveInteger(value, max = 100) {
|
|
25952
|
+
if (!value) return null;
|
|
25953
|
+
const parsed = Number.parseInt(value, 10);
|
|
25954
|
+
if (!Number.isFinite(parsed) || parsed < 1) return null;
|
|
25955
|
+
return Math.min(parsed, max);
|
|
25956
|
+
}
|
|
25957
|
+
function parseCssSize(value) {
|
|
25958
|
+
const normalized = cleanStyleValue(value);
|
|
25959
|
+
if (!normalized) return null;
|
|
25960
|
+
const match = normalized.match(/^(\d+(?:\.\d+)?)(px|pt)?$/i);
|
|
25961
|
+
if (!match) return null;
|
|
25962
|
+
const amount = Number.parseFloat(match[1]);
|
|
25963
|
+
if (!Number.isFinite(amount) || amount <= 0) return null;
|
|
25964
|
+
return Math.round(match[2]?.toLowerCase() === "pt" ? amount * (4 / 3) : amount);
|
|
25965
|
+
}
|
|
25966
|
+
function getCellWidth(cell, styles, colspan) {
|
|
25967
|
+
if (colspan !== 1) return null;
|
|
25968
|
+
const width = parseCssSize(cell.getAttribute("data-colwidth") ?? cell.getAttribute("width") ?? styles.get("width"));
|
|
25969
|
+
return width ? [width] : null;
|
|
25970
|
+
}
|
|
25971
|
+
function getTableRowAttrs(row, styles) {
|
|
25972
|
+
const rowHeight = parseCssSize(
|
|
25973
|
+
row.getAttribute("data-row-height") ?? row.getAttribute("height") ?? styles.get("height")
|
|
25974
|
+
);
|
|
25975
|
+
return rowHeight ? { rowHeight } : void 0;
|
|
25976
|
+
}
|
|
25977
|
+
function getTableCellAttrs(cell, styles, defaultBackgroundColor) {
|
|
25978
|
+
const colspan = parsePositiveInteger(cell.getAttribute("colspan")) ?? 1;
|
|
25979
|
+
const rowspan = parsePositiveInteger(cell.getAttribute("rowspan")) ?? 1;
|
|
25980
|
+
const backgroundColor = getBackgroundColor(styles) ?? normalizeColorValue(cell.getAttribute("data-background-color")) ?? normalizeColorValue(cell.getAttribute("bgcolor")) ?? defaultBackgroundColor;
|
|
25981
|
+
const borderAttrs = getBorderAttrs(styles);
|
|
25982
|
+
const colwidth = getCellWidth(cell, styles, colspan);
|
|
25983
|
+
const attrs = {};
|
|
25984
|
+
if (backgroundColor) attrs.backgroundColor = backgroundColor;
|
|
25985
|
+
if (borderAttrs.borderColor) attrs.borderColor = borderAttrs.borderColor;
|
|
25986
|
+
if (borderAttrs.borderStyle) attrs.borderStyle = borderAttrs.borderStyle;
|
|
25987
|
+
if (borderAttrs.borderWidth) attrs.borderWidth = borderAttrs.borderWidth;
|
|
25988
|
+
if (colspan > 1) attrs.colspan = colspan;
|
|
25989
|
+
if (rowspan > 1) attrs.rowspan = rowspan;
|
|
25990
|
+
if (colwidth) attrs.colwidth = colwidth;
|
|
25991
|
+
return Object.keys(attrs).length > 0 ? attrs : void 0;
|
|
25992
|
+
}
|
|
25993
|
+
function marksEqual(left, right) {
|
|
25994
|
+
return JSON.stringify(left ?? []) === JSON.stringify(right ?? []);
|
|
25995
|
+
}
|
|
25996
|
+
function mergeMarks(base2, additions) {
|
|
25997
|
+
const next = [...base2 ?? []];
|
|
25998
|
+
for (const addition of additions ?? []) {
|
|
25999
|
+
const existingIndex = next.findIndex((mark) => mark.type === addition.type);
|
|
26000
|
+
if (existingIndex >= 0) {
|
|
26001
|
+
const existingMark = next[existingIndex];
|
|
26002
|
+
next[existingIndex] = {
|
|
26003
|
+
...existingMark,
|
|
26004
|
+
attrs: {
|
|
26005
|
+
...existingMark.attrs ?? {},
|
|
26006
|
+
...addition.attrs ?? {}
|
|
26007
|
+
}
|
|
26008
|
+
};
|
|
26009
|
+
continue;
|
|
26010
|
+
}
|
|
26011
|
+
next.push(addition);
|
|
26012
|
+
}
|
|
26013
|
+
return next.length > 0 ? next : void 0;
|
|
26014
|
+
}
|
|
26015
|
+
function getMarkColor(marks, markType) {
|
|
26016
|
+
const mark = marks?.find((candidate) => candidate.type === markType);
|
|
26017
|
+
const color = mark?.attrs?.color;
|
|
26018
|
+
return typeof color === "string" ? color : null;
|
|
26019
|
+
}
|
|
26020
|
+
function replaceTextStyleColor(marks, color) {
|
|
26021
|
+
let replaced = false;
|
|
26022
|
+
const next = (marks ?? []).map((mark) => {
|
|
26023
|
+
if (mark.type !== "textStyle") return mark;
|
|
26024
|
+
replaced = true;
|
|
26025
|
+
return {
|
|
26026
|
+
...mark,
|
|
26027
|
+
attrs: {
|
|
26028
|
+
...mark.attrs ?? {},
|
|
26029
|
+
color
|
|
26030
|
+
}
|
|
26031
|
+
};
|
|
26032
|
+
});
|
|
26033
|
+
if (!replaced) {
|
|
26034
|
+
next.unshift({ type: "textStyle", attrs: { color } });
|
|
26035
|
+
}
|
|
26036
|
+
return next;
|
|
26037
|
+
}
|
|
26038
|
+
function ensureReadableSpreadsheetSegments(segments, cellBackgroundColor) {
|
|
26039
|
+
return segments.map((segment) => {
|
|
26040
|
+
const textColor = getMarkColor(segment.marks, "textStyle");
|
|
26041
|
+
if (!isLightTextColor(textColor)) return segment;
|
|
26042
|
+
const inlineBackgroundColor = getMarkColor(segment.marks, "highlight");
|
|
26043
|
+
if (isDarkReadableBackground(inlineBackgroundColor) || isDarkReadableBackground(cellBackgroundColor)) {
|
|
26044
|
+
return segment;
|
|
26045
|
+
}
|
|
26046
|
+
return {
|
|
26047
|
+
...segment,
|
|
26048
|
+
marks: replaceTextStyleColor(segment.marks, DEFAULT_HTML_TABLE_TEXT_COLOR)
|
|
26049
|
+
};
|
|
26050
|
+
});
|
|
26051
|
+
}
|
|
26052
|
+
function getElementInlineMarks(element, styles) {
|
|
26053
|
+
const marks = [];
|
|
26054
|
+
const tagName = element.tagName;
|
|
26055
|
+
const color = normalizeTextColorValue(styles.get("color") ?? element.getAttribute("color"));
|
|
26056
|
+
const backgroundColor = getBackgroundColor(styles);
|
|
26057
|
+
const fontWeight = styles.get("font-weight")?.toLowerCase();
|
|
26058
|
+
const fontStyle = styles.get("font-style")?.toLowerCase();
|
|
26059
|
+
const textDecoration = styles.get("text-decoration")?.toLowerCase();
|
|
26060
|
+
if (color) {
|
|
26061
|
+
marks.push({ type: "textStyle", attrs: { color } });
|
|
26062
|
+
}
|
|
26063
|
+
if (backgroundColor && !isWhiteColor(backgroundColor)) {
|
|
26064
|
+
marks.push({ type: "highlight", attrs: { color: backgroundColor } });
|
|
26065
|
+
}
|
|
26066
|
+
if (tagName === "B" || tagName === "STRONG" || fontWeight === "bold" || /^\d+$/.test(fontWeight ?? "") && Number(fontWeight) >= 600) {
|
|
26067
|
+
marks.push({ type: "bold" });
|
|
26068
|
+
}
|
|
26069
|
+
if (tagName === "I" || tagName === "EM" || fontStyle === "italic") {
|
|
26070
|
+
marks.push({ type: "italic" });
|
|
26071
|
+
}
|
|
26072
|
+
if (tagName === "U" || textDecoration?.includes("underline")) {
|
|
26073
|
+
marks.push({ type: "underline" });
|
|
26074
|
+
}
|
|
26075
|
+
return marks.length > 0 ? marks : void 0;
|
|
26076
|
+
}
|
|
26077
|
+
function appendTextSegment(segments, segment) {
|
|
26078
|
+
if (!segment.text) return;
|
|
26079
|
+
const lastSegment = segments[segments.length - 1];
|
|
26080
|
+
if (lastSegment && marksEqual(lastSegment.marks, segment.marks)) {
|
|
26081
|
+
lastSegment.text += segment.text;
|
|
26082
|
+
return;
|
|
26083
|
+
}
|
|
26084
|
+
segments.push(segment);
|
|
26085
|
+
}
|
|
26086
|
+
function segmentsEndWithNewline(segments) {
|
|
26087
|
+
return segments.length > 0 && segments[segments.length - 1].text.endsWith("\n");
|
|
26088
|
+
}
|
|
26089
|
+
function normalizeClipboardTextSegments(segments) {
|
|
26090
|
+
const normalizedSegments = [];
|
|
26091
|
+
for (const segment of segments) {
|
|
26092
|
+
appendTextSegment(normalizedSegments, {
|
|
26093
|
+
text: segment.text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\u00a0/g, " "),
|
|
26094
|
+
marks: segment.marks
|
|
26095
|
+
});
|
|
26096
|
+
}
|
|
26097
|
+
while (normalizedSegments.length > 0) {
|
|
26098
|
+
const firstSegment = normalizedSegments[0];
|
|
26099
|
+
firstSegment.text = firstSegment.text.replace(/^\s+/, "");
|
|
26100
|
+
if (firstSegment.text) break;
|
|
26101
|
+
normalizedSegments.shift();
|
|
26102
|
+
}
|
|
26103
|
+
while (normalizedSegments.length > 0) {
|
|
26104
|
+
const lastSegment = normalizedSegments[normalizedSegments.length - 1];
|
|
26105
|
+
lastSegment.text = lastSegment.text.replace(/\s+$/, "");
|
|
26106
|
+
if (lastSegment.text) break;
|
|
26107
|
+
normalizedSegments.pop();
|
|
26108
|
+
}
|
|
26109
|
+
return normalizedSegments;
|
|
26110
|
+
}
|
|
25704
26111
|
function getClipboardCellText(node) {
|
|
25705
26112
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
25706
26113
|
return node.textContent ?? "";
|
|
@@ -25718,57 +26125,205 @@ function getClipboardCellText(node) {
|
|
|
25718
26125
|
}
|
|
25719
26126
|
return childText;
|
|
25720
26127
|
}
|
|
25721
|
-
function
|
|
26128
|
+
function getClipboardCellSegments(node, styleMap, inheritedMarks) {
|
|
26129
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
26130
|
+
return [{ text: node.textContent ?? "", marks: inheritedMarks }];
|
|
26131
|
+
}
|
|
26132
|
+
if (!(node instanceof HTMLElement)) {
|
|
26133
|
+
return [];
|
|
26134
|
+
}
|
|
26135
|
+
if (node.tagName === "BR") {
|
|
26136
|
+
return [{ text: "\n", marks: inheritedMarks }];
|
|
26137
|
+
}
|
|
26138
|
+
const styles = getElementStyleDeclarations(node, styleMap);
|
|
26139
|
+
const marks = mergeMarks(inheritedMarks, getElementInlineMarks(node, styles));
|
|
26140
|
+
const segments = [];
|
|
26141
|
+
for (const childNode of Array.from(node.childNodes)) {
|
|
26142
|
+
for (const segment of getClipboardCellSegments(childNode, styleMap, marks)) {
|
|
26143
|
+
appendTextSegment(segments, segment);
|
|
26144
|
+
}
|
|
26145
|
+
}
|
|
26146
|
+
if ((node.tagName === "P" || node.tagName === "DIV" || node.tagName === "LI") && segments.length > 0 && !segmentsEndWithNewline(segments)) {
|
|
26147
|
+
appendTextSegment(segments, { text: "\n" });
|
|
26148
|
+
}
|
|
26149
|
+
return segments;
|
|
26150
|
+
}
|
|
26151
|
+
function getClipboardCellChildSegments(cell, styleMap, inheritedMarks) {
|
|
26152
|
+
const segments = [];
|
|
26153
|
+
for (const childNode of Array.from(cell.childNodes)) {
|
|
26154
|
+
for (const segment of getClipboardCellSegments(childNode, styleMap, inheritedMarks)) {
|
|
26155
|
+
appendTextSegment(segments, segment);
|
|
26156
|
+
}
|
|
26157
|
+
}
|
|
26158
|
+
return normalizeClipboardTextSegments(segments);
|
|
26159
|
+
}
|
|
26160
|
+
function getHtmlTableRows(table, styleMap) {
|
|
25722
26161
|
const rows = Array.from(table.querySelectorAll("tr")).map(
|
|
25723
|
-
(row) =>
|
|
25724
|
-
|
|
25725
|
-
|
|
25726
|
-
|
|
26162
|
+
(row) => ({
|
|
26163
|
+
attrs: getTableRowAttrs(row, getElementStyleDeclarations(row, styleMap)),
|
|
26164
|
+
cells: Array.from(row.children).filter((cell) => cell instanceof HTMLTableCellElement).map((cell) => {
|
|
26165
|
+
const styles = getElementStyleDeclarations(cell, styleMap);
|
|
26166
|
+
const textColor = normalizeTextColorValue(styles.get("color")) ?? DEFAULT_HTML_TABLE_TEXT_COLOR;
|
|
26167
|
+
const inheritedMarks = [{ type: "textStyle", attrs: { color: textColor } }];
|
|
26168
|
+
const attrs = getTableCellAttrs(cell, styles, DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR);
|
|
26169
|
+
const segments = ensureReadableSpreadsheetSegments(
|
|
26170
|
+
getClipboardCellChildSegments(cell, styleMap, inheritedMarks),
|
|
26171
|
+
attrs?.backgroundColor
|
|
26172
|
+
);
|
|
26173
|
+
return {
|
|
26174
|
+
text: normalizeClipboardCellText(getClipboardCellText(cell)),
|
|
26175
|
+
isHeader: cell.tagName === "TH",
|
|
26176
|
+
attrs,
|
|
26177
|
+
segments: segments.length > 0 ? segments : void 0,
|
|
26178
|
+
textColor
|
|
26179
|
+
};
|
|
26180
|
+
})
|
|
26181
|
+
})
|
|
25727
26182
|
);
|
|
25728
|
-
return rows.filter((row) => row.length > 0);
|
|
26183
|
+
return rows.filter((row) => row.cells.length > 0);
|
|
26184
|
+
}
|
|
26185
|
+
function createTextMarks(cell) {
|
|
26186
|
+
return cell.textColor ? [{ type: "textStyle", attrs: { color: cell.textColor } }] : void 0;
|
|
25729
26187
|
}
|
|
25730
|
-
function createParagraphContent(text) {
|
|
26188
|
+
function createParagraphContent(text, marks) {
|
|
25731
26189
|
return text ? {
|
|
25732
26190
|
type: "paragraph",
|
|
25733
|
-
content: [{ type: "text", text }]
|
|
26191
|
+
content: [{ type: "text", text, ...marks ? { marks } : {} }]
|
|
25734
26192
|
} : { type: "paragraph" };
|
|
25735
26193
|
}
|
|
26194
|
+
function createParagraphContentFromSegments(segments) {
|
|
26195
|
+
const paragraphs = [[]];
|
|
26196
|
+
for (const segment of segments) {
|
|
26197
|
+
const parts = segment.text.split("\n");
|
|
26198
|
+
parts.forEach((part, index) => {
|
|
26199
|
+
if (part) {
|
|
26200
|
+
paragraphs[paragraphs.length - 1].push({ text: part, marks: segment.marks });
|
|
26201
|
+
}
|
|
26202
|
+
if (index < parts.length - 1) {
|
|
26203
|
+
paragraphs.push([]);
|
|
26204
|
+
}
|
|
26205
|
+
});
|
|
26206
|
+
}
|
|
26207
|
+
return paragraphs.map((paragraphSegments) => {
|
|
26208
|
+
const content = paragraphSegments.map((segment) => ({
|
|
26209
|
+
type: "text",
|
|
26210
|
+
text: segment.text,
|
|
26211
|
+
...segment.marks ? { marks: segment.marks } : {}
|
|
26212
|
+
}));
|
|
26213
|
+
return content.length > 0 ? { type: "paragraph", content } : { type: "paragraph" };
|
|
26214
|
+
});
|
|
26215
|
+
}
|
|
25736
26216
|
function createTableCellContent(cell) {
|
|
25737
26217
|
const lines = cell.text.split("\n");
|
|
25738
|
-
const
|
|
26218
|
+
const marks = createTextMarks(cell);
|
|
26219
|
+
const paragraphs = cell.segments && cell.segments.length > 0 ? createParagraphContentFromSegments(cell.segments) : (lines.length > 0 ? lines : [""]).map((line) => createParagraphContent(line, marks));
|
|
25739
26220
|
return {
|
|
25740
26221
|
type: cell.isHeader ? "tableHeader" : "tableCell",
|
|
26222
|
+
...cell.attrs ? { attrs: cell.attrs } : {},
|
|
25741
26223
|
content: paragraphs.length > 0 ? paragraphs : [{ type: "paragraph" }]
|
|
25742
26224
|
};
|
|
25743
26225
|
}
|
|
25744
|
-
function
|
|
25745
|
-
const
|
|
26226
|
+
function getRowspanLimitedCell(cell, remainingRowCount) {
|
|
26227
|
+
const attrs = cell.attrs;
|
|
26228
|
+
if (!attrs?.rowspan || attrs.rowspan <= remainingRowCount) return cell;
|
|
26229
|
+
if (remainingRowCount <= 1) {
|
|
26230
|
+
const { rowspan: _rowspan, ...nextAttrs } = attrs;
|
|
26231
|
+
return {
|
|
26232
|
+
...cell,
|
|
26233
|
+
attrs: Object.keys(nextAttrs).length > 0 ? nextAttrs : void 0
|
|
26234
|
+
};
|
|
26235
|
+
}
|
|
26236
|
+
return {
|
|
26237
|
+
...cell,
|
|
26238
|
+
attrs: {
|
|
26239
|
+
...attrs,
|
|
26240
|
+
rowspan: remainingRowCount
|
|
26241
|
+
}
|
|
26242
|
+
};
|
|
26243
|
+
}
|
|
26244
|
+
function normalizeTableRows(rows) {
|
|
26245
|
+
const positionedRows = [];
|
|
26246
|
+
let rowspans = [];
|
|
26247
|
+
let columnCount = 0;
|
|
26248
|
+
rows.forEach((row, rowIndex) => {
|
|
26249
|
+
const coveredColumns = rowspans.map((span) => span > 0);
|
|
26250
|
+
const nextRowspans = rowspans.map((span) => Math.max(0, span - 1));
|
|
26251
|
+
const positionedCells = [];
|
|
26252
|
+
let columnIndex = 0;
|
|
26253
|
+
for (const rawCell of row.cells) {
|
|
26254
|
+
while (coveredColumns[columnIndex]) columnIndex += 1;
|
|
26255
|
+
const remainingRowCount = rows.length - rowIndex;
|
|
26256
|
+
const cell = getRowspanLimitedCell(rawCell, remainingRowCount);
|
|
26257
|
+
const colspan = Math.max(1, cell.attrs?.colspan ?? 1);
|
|
26258
|
+
const rowspan = Math.max(1, cell.attrs?.rowspan ?? 1);
|
|
26259
|
+
positionedCells.push({ startColumn: columnIndex, colspan, cell });
|
|
26260
|
+
if (rowspan > 1) {
|
|
26261
|
+
for (let offset = 0; offset < colspan; offset += 1) {
|
|
26262
|
+
const spannedColumn = columnIndex + offset;
|
|
26263
|
+
nextRowspans[spannedColumn] = Math.max(nextRowspans[spannedColumn] ?? 0, rowspan - 1);
|
|
26264
|
+
}
|
|
26265
|
+
}
|
|
26266
|
+
columnIndex += colspan;
|
|
26267
|
+
}
|
|
26268
|
+
const lastCoveredColumn = coveredColumns.reduce((lastIndex, covered, index) => covered ? index : lastIndex, -1);
|
|
26269
|
+
const lastFutureRowspanColumn = nextRowspans.reduce((lastIndex, span, index) => span > 0 ? index : lastIndex, -1);
|
|
26270
|
+
columnCount = Math.max(columnCount, columnIndex, lastCoveredColumn + 1, lastFutureRowspanColumn + 1);
|
|
26271
|
+
positionedRows.push({
|
|
26272
|
+
attrs: row.attrs,
|
|
26273
|
+
cells: positionedCells,
|
|
26274
|
+
coveredColumns
|
|
26275
|
+
});
|
|
26276
|
+
rowspans = nextRowspans;
|
|
26277
|
+
});
|
|
26278
|
+
return { positionedRows, columnCount };
|
|
26279
|
+
}
|
|
26280
|
+
function createNormalizedRowContent(row, columnCount, fillerCellAttrs) {
|
|
26281
|
+
const content = [];
|
|
26282
|
+
const cellByStartColumn = new Map(row.cells.map((cell) => [cell.startColumn, cell]));
|
|
26283
|
+
let columnIndex = 0;
|
|
26284
|
+
while (columnIndex < columnCount) {
|
|
26285
|
+
if (row.coveredColumns[columnIndex]) {
|
|
26286
|
+
columnIndex += 1;
|
|
26287
|
+
continue;
|
|
26288
|
+
}
|
|
26289
|
+
const positionedCell = cellByStartColumn.get(columnIndex);
|
|
26290
|
+
if (positionedCell) {
|
|
26291
|
+
content.push(createTableCellContent(positionedCell.cell));
|
|
26292
|
+
columnIndex += positionedCell.colspan;
|
|
26293
|
+
continue;
|
|
26294
|
+
}
|
|
26295
|
+
content.push(createTableCellContent({ text: "", isHeader: false, attrs: fillerCellAttrs }));
|
|
26296
|
+
columnIndex += 1;
|
|
26297
|
+
}
|
|
26298
|
+
return content;
|
|
26299
|
+
}
|
|
26300
|
+
function createTableContent(rows, minColumnCount = 1, fillerCellAttrs) {
|
|
26301
|
+
const tableRows = rows.filter((row) => row.cells.length > 0);
|
|
25746
26302
|
if (tableRows.length === 0) return null;
|
|
25747
|
-
const
|
|
26303
|
+
const { positionedRows, columnCount } = normalizeTableRows(tableRows);
|
|
25748
26304
|
if (columnCount < minColumnCount) return null;
|
|
25749
26305
|
return {
|
|
25750
26306
|
type: "table",
|
|
25751
|
-
content:
|
|
25752
|
-
|
|
25753
|
-
|
|
25754
|
-
|
|
25755
|
-
|
|
25756
|
-
return {
|
|
25757
|
-
type: "tableRow",
|
|
25758
|
-
content: normalizedRow.map(createTableCellContent)
|
|
25759
|
-
};
|
|
25760
|
-
})
|
|
26307
|
+
content: positionedRows.map((row) => ({
|
|
26308
|
+
type: "tableRow",
|
|
26309
|
+
...row.attrs ? { attrs: row.attrs } : {},
|
|
26310
|
+
content: createNormalizedRowContent(row, columnCount, fillerCellAttrs)
|
|
26311
|
+
}))
|
|
25761
26312
|
};
|
|
25762
26313
|
}
|
|
25763
26314
|
function getClipboardTableContent(dataTransfer) {
|
|
25764
26315
|
const html = getClipboardData(dataTransfer, "text/html");
|
|
25765
26316
|
if (!/<table(?:\s|>)/i.test(html)) return null;
|
|
25766
26317
|
if (typeof DOMParser === "undefined") return null;
|
|
26318
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
25767
26319
|
const fragment = extractClipboardHtmlFragment(html);
|
|
25768
|
-
const
|
|
25769
|
-
const
|
|
26320
|
+
const fragmentDoc = new DOMParser().parseFromString(fragment, "text/html");
|
|
26321
|
+
const styleMap = parseClipboardCssClassStyles(doc);
|
|
26322
|
+
const table = fragmentDoc.querySelector("table") ?? doc.querySelector("table");
|
|
25770
26323
|
if (!(table instanceof HTMLTableElement)) return null;
|
|
25771
|
-
return createTableContent(getHtmlTableRows(table)
|
|
26324
|
+
return createTableContent(getHtmlTableRows(table, styleMap), 1, {
|
|
26325
|
+
backgroundColor: DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR
|
|
26326
|
+
});
|
|
25772
26327
|
}
|
|
25773
26328
|
function parseClipboardTsvRows(text) {
|
|
25774
26329
|
const rows = [];
|
|
@@ -25825,7 +26380,9 @@ function getClipboardTsvTableContent(dataTransfer) {
|
|
|
25825
26380
|
if (!text.includes(" ")) return null;
|
|
25826
26381
|
const rows = parseClipboardTsvRows(text);
|
|
25827
26382
|
return createTableContent(
|
|
25828
|
-
rows.map((row) =>
|
|
26383
|
+
rows.map((row) => ({
|
|
26384
|
+
cells: row.map((cell) => ({ text: normalizeClipboardCellText(cell), isHeader: false }))
|
|
26385
|
+
})),
|
|
25829
26386
|
2
|
|
25830
26387
|
);
|
|
25831
26388
|
}
|