fix: luckyviewer formatting

This commit is contained in:
Saumya 2025-08-08 12:00:59 +05:30
parent 13fefdae77
commit 33739679b0
1 changed files with 296 additions and 35 deletions

View File

@ -31,28 +31,108 @@ function loadStyle(href: string): void {
document.head.appendChild(l);
}
function argbToHex(argb?: string): string | undefined {
if (!argb || typeof argb !== 'string') return undefined;
const v = argb.replace(/^#/, '');
if (v.length === 8) return `#${v.slice(2)}`;
if (v.length === 6) return `#${v}`;
function argbToHex(argb?: string | number): string | undefined {
if (!argb) return undefined;
if (typeof argb === 'number') {
const indexColors: Record<number, string> = {
0: '#000000', 1: '#FFFFFF', 2: '#FF0000', 3: '#00FF00',
4: '#0000FF', 5: '#FFFF00', 6: '#FF00FF', 7: '#00FFFF',
8: '#000000', 9: '#FFFFFF', 10: '#FF0000', 11: '#00FF00',
12: '#0000FF', 13: '#FFFF00', 14: '#FF00FF', 15: '#00FFFF',
16: '#800000', 17: '#008000', 18: '#000080', 19: '#808000',
20: '#800080', 21: '#008080', 22: '#C0C0C0', 23: '#808080',
24: '#9999FF', 25: '#993366', 26: '#FFFFCC', 27: '#CCFFFF',
28: '#660066', 29: '#FF8080', 30: '#0066CC', 31: '#CCCCFF',
32: '#000080', 33: '#FF00FF', 34: '#FFFF00', 35: '#00FFFF',
36: '#800080', 37: '#800000', 38: '#008080', 39: '#0000FF',
40: '#00CCFF', 41: '#CCFFFF', 42: '#CCFFCC', 43: '#FFFF99',
44: '#99CCFF', 45: '#FF99CC', 46: '#CC99FF', 47: '#FFCC99',
48: '#3366FF', 49: '#33CCCC', 50: '#99CC00', 51: '#FFCC00',
52: '#FF9900', 53: '#FF6600', 54: '#666699', 55: '#969696',
56: '#003366', 57: '#339966', 58: '#003300', 59: '#333300',
60: '#993300', 61: '#993366', 62: '#333399', 63: '#333333',
64: '#000000', 65: '#FFFFFF'
};
return indexColors[argb] || undefined;
}
const v = String(argb).replace(/^#/, '').toUpperCase();
if (v.length === 8) {
return `#${v.slice(2)}`;
}
if (v.length === 6) {
return `#${v}`;
}
if (v.startsWith('FF')) {
return `#${v.slice(2)}`;
}
return undefined;
}
function mapType(t: string | undefined): string {
switch (t) {
case 'n':
case 'd':
case 'b':
case 's':
case 'n':
case 'd':
case 'b':
case 's':
case 'str':
case 'e':
case 'e':
return t;
default:
return 'g';
return 'g';
}
}
function convertNumberFormat(fmt?: string): string {
if (!fmt) return 'General';
const formatMap: Record<string, string> = {
'0': '#,##0',
'0.00': '#,##0.00',
'#,##0': '#,##0',
'#,##0.00': '#,##0.00',
'$#,##0.00': '$#,##0.00',
'0%': '0%',
'0.00%': '0.00%',
'mm/dd/yyyy': 'MM/DD/YYYY',
'dd/mm/yyyy': 'DD/MM/YYYY',
'yyyy-mm-dd': 'YYYY-MM-DD',
};
return formatMap[fmt] || fmt;
}
function extractBorders(borders: any): any {
if (!borders) return null;
const borderConfig: any = {};
if (borders.top) {
borderConfig.t = {
style: borders.top.style === 'thin' ? 1 : 2,
color: argbToHex(borders.top.color?.rgb) || '#000000'
};
}
if (borders.bottom) {
borderConfig.b = {
style: borders.bottom.style === 'thin' ? 1 : 2,
color: argbToHex(borders.bottom.color?.rgb) || '#000000'
};
}
if (borders.left) {
borderConfig.l = {
style: borders.left.style === 'thin' ? 1 : 2,
color: argbToHex(borders.left.color?.rgb) || '#000000'
};
}
if (borders.right) {
borderConfig.r = {
style: borders.right.style === 'thin' ? 1 : 2,
color: argbToHex(borders.right.color?.rgb) || '#000000'
};
}
return Object.keys(borderConfig).length > 0 ? borderConfig : null;
}
export interface LuckysheetViewerProps {
xlsxPath: string;
sandboxId?: string;
@ -89,9 +169,11 @@ export function LuckysheetViewer({ xlsxPath, sandboxId, className, height }: Luc
try {
setLoading(true);
setError(null);
loadStyle('https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/plugins/css/pluginsCss.css');
loadStyle('https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/plugins/plugins.css');
loadStyle('https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/css/luckysheet.css');
loadStyle('https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/fonts/iconfont.css');
await loadScript('https://cdn.jsdelivr.net/npm/jquery@3.6.4/dist/jquery.min.js');
if (!window.$ && (window as any).jQuery) window.$ = (window as any).jQuery;
@ -117,7 +199,14 @@ export function LuckysheetViewer({ xlsxPath, sandboxId, className, height }: Luc
}
const XLSX = window.XLSX;
const wb = XLSX.read(ab, { type: 'array', cellStyles: true });
const wb = XLSX.read(ab, {
type: 'array',
cellStyles: true,
cellDates: true,
cellNF: true,
sheetStubs: true,
raw: false
});
const sheetsForLucky: any[] = [];
@ -132,32 +221,152 @@ export function LuckysheetViewer({ xlsxPath, sandboxId, className, height }: Luc
const addr = XLSX.utils.encode_cell({ r, c });
const cell = ws[addr];
if (!cell) continue;
const v: any = {
v: cell.v,
m: (cell.w ?? String(cell.v ?? '')),
ct: { t: mapType(cell.t), fa: cell.z || 'General' },
ct: {
t: mapType(cell.t),
fa: convertNumberFormat(cell.z) || 'General'
},
};
const s = (cell as any).s || {};
const font = s.font || {};
const fill = s.fill || {};
const alignment = s.alignment || {};
if (font.bold) v.bl = 1;
if (font.italic) v.it = 1;
if (font.sz) v.fs = Number(font.sz);
const fc = font.color?.rgb || font.color?.rgbColor || font.color;
const bg = fill.fgColor?.rgb || fill.bgColor?.rgb || fill.fgColor || fill.bgColor;
const fcHex = argbToHex(typeof fc === 'string' ? fc : undefined);
const bgHex = argbToHex(typeof bg === 'string' ? bg : undefined);
if (fcHex) v.fc = fcHex;
if (bgHex) v.bg = bgHex;
if (alignment) {
if (alignment.horizontal) v.ht = alignment.horizontal;
if (alignment.vertical) v.vt = alignment.vertical;
if (alignment.wrapText) v.tb = 1;
const cellStyle = cell.s || {};
const isHeader = r === 0;
if (cellStyle && typeof cellStyle === 'object') {
if (cellStyle.fgColor || cellStyle.bgColor || cellStyle.patternType) {
let bgColor = null;
if (cellStyle.fgColor?.rgb) {
bgColor = cellStyle.fgColor.rgb;
} else if (cellStyle.bgColor?.rgb) {
bgColor = cellStyle.bgColor.rgb;
}
if (bgColor) {
bgColor = bgColor.replace(/^#/, '').toUpperCase();
if (bgColor.length === 8) {
bgColor = '#' + bgColor.slice(2);
} else if (bgColor.length === 6) {
bgColor = '#' + bgColor;
}
if (bgColor.startsWith('#') && bgColor.length === 7) {
v.bg = bgColor;
}
}
}
if (cellStyle.font) {
if (cellStyle.font.bold) v.bl = 1;
if (cellStyle.font.italic) v.it = 1;
if (cellStyle.font.underline) v.un = 1;
if (cellStyle.font.strike) v.cl = 1;
if (cellStyle.font.sz) v.fs = Math.round(Number(cellStyle.font.sz));
if (cellStyle.font.name) v.ff = cellStyle.font.name;
if (cellStyle.font.color?.rgb) {
let fc = cellStyle.font.color.rgb;
fc = fc.replace(/^#/, '').toUpperCase();
if (fc.length === 8) {
fc = '#' + fc.slice(2);
} else if (fc.length === 6) {
fc = '#' + fc;
}
if (fc.startsWith('#') && fc.length === 7) {
v.fc = fc;
}
}
}
if (cellStyle.alignment) {
const htMap: Record<string, number> = {
'left': 1,
'center': 0,
'right': 2,
};
const vtMap: Record<string, number> = {
'top': 0,
'middle': 1,
'center': 1,
'bottom': 2,
};
if (cellStyle.alignment.horizontal) {
v.ht = htMap[cellStyle.alignment.horizontal] ?? 0;
}
if (cellStyle.alignment.vertical) {
v.vt = vtMap[cellStyle.alignment.vertical] ?? 1;
}
if (cellStyle.alignment.wrapText) v.tb = 2;
}
}
if (typeof cellStyle === 'number' && wb.Styles) {
const xfIndex = cellStyle;
if (wb.Styles.CellXf && wb.Styles.CellXf[xfIndex]) {
const xf = wb.Styles.CellXf[xfIndex];
if (xf.fontId !== undefined && wb.Styles.Fonts && wb.Styles.Fonts[xf.fontId]) {
const font = wb.Styles.Fonts[xf.fontId];
if (font.bold) v.bl = 1;
if (font.italic) v.it = 1;
if (font.underline) v.un = 1;
if (font.strike) v.cl = 1;
if (font.sz) v.fs = Math.round(Number(font.sz));
if (font.name) v.ff = font.name;
if (font.color?.rgb) {
let fc = font.color.rgb;
fc = fc.replace(/^#/, '').toUpperCase();
if (fc.length === 8) {
fc = '#' + fc.slice(2);
} else if (fc.length === 6) {
fc = '#' + fc;
}
if (fc.startsWith('#') && fc.length === 7) {
v.fc = fc;
}
}
}
if (xf.fillId !== undefined && xf.fillId > 0 && wb.Styles.Fills && wb.Styles.Fills[xf.fillId]) {
const fill = wb.Styles.Fills[xf.fillId];
if (fill.fgColor?.rgb || fill.bgColor?.rgb) {
let bg = fill.fgColor?.rgb || fill.bgColor?.rgb;
bg = bg.replace(/^#/, '').toUpperCase();
if (bg.length === 8) {
bg = '#' + bg.slice(2);
} else if (bg.length === 6) {
bg = '#' + bg;
}
if (bg.startsWith('#') && bg.length === 7) {
v.bg = bg;
}
}
}
if (xf.alignment) {
const htMap: Record<string, number> = {
'left': 1,
'center': 0,
'right': 2,
};
const vtMap: Record<string, number> = {
'top': 0,
'middle': 1,
'center': 1,
'bottom': 2,
};
if (xf.alignment.horizontal) {
v.ht = htMap[xf.alignment.horizontal] ?? 0;
}
if (xf.alignment.vertical) {
v.vt = vtMap[xf.alignment.vertical] ?? 1;
}
if (xf.alignment.wrapText) v.tb = 2;
}
}
}
if (isHeader) {
v.bl = 1;
}
celldata.push({ r, c, v });
}
}
@ -173,14 +382,14 @@ export function LuckysheetViewer({ xlsxPath, sandboxId, className, height }: Luc
const columnlen: Record<number, number> = {};
const cols = ws['!cols'] || [];
cols.forEach((col: any, i: number) => {
const wpx = col.wpx || (col.wch ? Math.round(col.wch * 7) : undefined);
const wpx = col.wpx || (col.wch ? Math.round(col.wch * 7.5) : col.width ? col.width * 7 : undefined);
if (wpx) columnlen[i] = wpx;
});
const rowlen: Record<number, number> = {};
const rows = ws['!rows'] || [];
rows.forEach((row: any, i: number) => {
const hpx = row.hpx || (row.hpt ? Math.round(row.hpt * 1.33) : undefined);
const hpx = row.hpx || (row.hpt ? Math.round(row.hpt * 1.33) : row.h ? row.h : undefined);
if (hpx) rowlen[i] = hpx;
});
@ -196,11 +405,29 @@ export function LuckysheetViewer({ xlsxPath, sandboxId, className, height }: Luc
order: idx,
celldata,
config,
row: Math.max(range.e.r + 1, 20),
column: Math.max(range.e.c + 1, 10),
luckysheet_select_save: [],
calcChain: [],
isPivotTable: false,
pivotTable: {},
filter_select: {},
filter: null,
luckysheet_alternateformat_save: [],
luckysheet_alternateformat_save_modelCustom: [],
luckysheet_conditionformat_save: {},
frozen: {},
chart: [],
zoomRatio: 1,
image: [],
showGridLines: 1,
dataVerification: {}
});
});
if (!containerRef.current) return;
containerRef.current.innerHTML = '';
window.luckysheet?.create({
container: containerIdRef.current,
data: sheetsForLucky,
@ -208,7 +435,41 @@ export function LuckysheetViewer({ xlsxPath, sandboxId, className, height }: Luc
showinfobar: false,
showsheetbar: true,
allowCopy: true,
showConfigWindowResize: true,
enableAddRow: false,
enableAddBackTop: false,
functionButton: true,
showRowBar: true,
showColumnBar: true,
sheetFormulaBar: true,
defaultFontSize: 11,
allowEdit: false,
editMode: false,
cellRightClickConfig: {
copy: true,
copyAs: true,
paste: true,
insertRow: false,
insertColumn: false,
deleteRow: false,
deleteColumn: false,
deleteCell: false,
hideRow: false,
hideColumn: false,
rowHeight: false,
columnWidth: false,
clear: false,
matrix: false,
sort: false,
filter: false,
chart: false,
image: false,
link: false,
data: false,
cellFormat: true
}
});
if (!disposed) setLoading(false);
} catch (e: any) {
if (!disposed) {