From fa616ad7bd0ab1d7ae977155749596cf90ce3f19 Mon Sep 17 00:00:00 2001 From: Maitra Khatri Date: Thu, 5 Mar 2026 18:24:43 +0530 Subject: [PATCH 1/2] fix: date format, m value generation --- packages/react/src/components/Toolbar/index.tsx | 9 +++++++++ packages/react/src/components/Workbook/index.tsx | 15 +++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/packages/react/src/components/Toolbar/index.tsx b/packages/react/src/components/Toolbar/index.tsx index 28aef356..1a8e2ae3 100644 --- a/packages/react/src/components/Toolbar/index.tsx +++ b/packages/react/src/components/Toolbar/index.tsx @@ -38,6 +38,7 @@ import { Cell, api, getSheetIndex, + is_date, } from "@fileverse-dev/fortune-core"; import _ from "lodash"; import { @@ -766,6 +767,14 @@ const Toolbar: React.FC<{ curr?.fa === "0.00" ) { currentFmt = "Number"; + } else if (is_date(curr.fa)) { + // Check if format contains time indicators + const hasTime = + curr.fa.includes("h") || + curr.fa.includes("H") || + curr.fa.includes("m") || + curr.fa.includes("s"); + currentFmt = hasTime ? "Date time" : "Date"; } else { currentFmt = defaultFormat[defaultFormat.length - 1].text; } diff --git a/packages/react/src/components/Workbook/index.tsx b/packages/react/src/components/Workbook/index.tsx index c6a5030a..0170da20 100644 --- a/packages/react/src/components/Workbook/index.tsx +++ b/packages/react/src/components/Workbook/index.tsx @@ -25,6 +25,7 @@ import { getFlowdata, api, handlePasteByClick, + update, // formatting helper } from "@fileverse-dev/fortune-core"; import React, { useMemo, @@ -171,6 +172,20 @@ const Workbook = React.forwardRef( celldata?.forEach((d) => { // TODO setCellValue(draftCtx, d.r, d.c, expandedData, d.v); expandedData[d.r][d.c] = d.v; + // if a date cell doesn't already have a formatted string, generate one + const cell = d.v as any; + if ( + cell && + cell.ct && + cell.ct.t === "d" && + (cell.m === undefined || cell.m === null) + ) { + try { + cell.m = update(cell.ct.fa || "General", cell.v); + } catch (e) { + // fallback silently + } + } }); draftCtx.luckysheetfile = produce(draftCtx.luckysheetfile, (d) => { d[index!].data = expandedData; From b7c236ac964031c68f81c4ef6c33d3350149d3f4 Mon Sep 17 00:00:00 2001 From: Maitra Khatri Date: Thu, 12 Mar 2026 12:37:59 +0530 Subject: [PATCH 2/2] fix: identify different date formats --- packages/core/src/events/paste.ts | 91 ++++++++++--------- packages/core/src/modules/format.ts | 19 ++-- packages/core/src/modules/validation.ts | 9 +- .../react/src/components/Toolbar/index.tsx | 21 ++--- 4 files changed, 76 insertions(+), 64 deletions(-) diff --git a/packages/core/src/events/paste.ts b/packages/core/src/events/paste.ts index ea357cba..1d209622 100644 --- a/packages/core/src/events/paste.ts +++ b/packages/core/src/events/paste.ts @@ -7,11 +7,11 @@ import { // execFunctionGroup, } from "../modules/formula"; import { getdatabyselection } from "../modules/cell"; -import { update, datenum_local } from "../modules/format"; +import { update, genarate } from "../modules/format"; import { normalizeSelection, selectionCache } from "../modules/selection"; import { Cell, CellMatrix } from "../types"; import { getSheetIndex, isAllowEdit } from "../utils"; -import { hasPartMC, isRealNum, detectDateFormat } from "../modules/validation"; +import { hasPartMC, isRealNum } from "../modules/validation"; import { getBorderInfoCompute } from "../modules/border"; import { expandRowsAndColumns, storeSheetParamALL } from "../modules/sheet"; import { jfrefreshgrid } from "../modules/refresh"; @@ -662,41 +662,48 @@ function pasteHandler(ctx: Context, data: any, borderInfo?: any) { } if (originCell) { - // If destination cell already has a date format, try to parse pasted string into a date serial - if (originCell.ct && originCell.ct.t === "d" && !isUrl) { - const df = detectDateFormat(originalValueStr); - if (df) { - const dateObj = new Date( - df.year, - df.month - 1, - df.day, - df.hours, - df.minutes, - df.seconds - ); - originCell.v = datenum_local(dateObj); - } else { - // Not a date: preserve original text so user can apply formats later - originCell.v = originalValueStr; - } - } else { - // Default: keep pasted value (numbers already parsed above) - originCell.v = isUrl ? originalValueStr : value; - } - - if (originCell.ct != null && originCell.ct.fa != null) { - // If value is not a numeric serial for date formats, avoid calling update - if (originCell.ct.t === "d" && typeof originCell.v !== "number") { - originCell.m = String(originCell.v); + if (!isUrl) { + const generated = genarate(originalValueStr); + if (generated) { + const [genM, genCt, genV] = generated; + if (genCt?.t === "d") { + // Pasted value is a date — always update ct so toolbar shows "Date" + originCell.v = genV; + originCell.m = genM ?? originalValueStr; + originCell.ct = genCt; + } else { + // Not a date: preserve destination format, just update value + originCell.v = value; + if (originCell.ct != null && originCell.ct.fa != null) { + if ( + originCell.ct.t === "d" && + typeof originCell.v !== "number" + ) { + originCell.m = String(originCell.v); + } else { + originCell.m = update(originCell.ct.fa, originCell.v); + } + } else { + originCell.m = + typeof originCell.v === "boolean" + ? String(originCell.v) + : originCell.v; + } + } } else { - originCell.m = update(originCell.ct.fa, originCell.v); + originCell.v = value; + if (originCell.ct != null && originCell.ct.fa != null) { + originCell.m = update(originCell.ct.fa, originCell.v); + } else { + originCell.m = + typeof originCell.v === "boolean" + ? String(originCell.v) + : originCell.v; + } } } else { - // Convert boolean to string if needed, since m only accepts string | number - originCell.m = - typeof originCell.v === "boolean" - ? String(originCell.v) - : originCell.v; + originCell.v = originalValueStr; + originCell.m = originalValueStr; } if (originCell.f != null && originCell.f.length > 0) { @@ -737,18 +744,16 @@ function pasteHandler(ctx: Context, data: any, borderInfo?: any) { t: "s", }; } else { - // Preserve original pasted text when creating new cells (automatic format) - cell.v = originalValueStr; - cell.m = originalValueStr; - cell.ct = { fa: "General", t: "g" }; // check if hex value to handle hex address if (/^0x?[a-fA-F0-9]+$/.test(value)) { - cell.m = value; - cell.ct = { - fa: "@", - t: "s", - }; cell.v = value; + cell.m = value; + cell.ct = { fa: "@", t: "s" }; + } else { + const [m, ct, v] = genarate(originalValueStr) ?? []; + cell.v = v ?? originalValueStr; + cell.m = m != null ? String(m) : originalValueStr; + cell.ct = ct ?? { fa: "General", t: "g" }; } } diff --git a/packages/core/src/modules/format.ts b/packages/core/src/modules/format.ts index f16269ff..793dfbc6 100644 --- a/packages/core/src/modules/format.ts +++ b/packages/core/src/modules/format.ts @@ -228,20 +228,24 @@ export function genarate(value: string | number | boolean) { ct.t = "d"; const map: Record = { - "yyyy-MM-dd": "dd/MM/yyyy", - "yyyy-MM-dd HH:mm": "dd/MM/yyyy", - "yyyy-MM-ddTHH:mm": "dd/MM/yyyy", - "yyyy/MM/dd": "dd/MM/yyyy", - "yyyy/MM/dd HH:mm": "dd/MM/yyyy", + "yyyy-MM-dd": "yyyy-MM-dd", + "yyyy-MM-dd HH:mm": "yyyy-MM-dd HH:mm", + "yyyy-MM-ddTHH:mm": "yyyy-MM-dd HH:mm", + "yyyy/MM/dd": "yyyy/MM/dd", + "yyyy/MM/dd HH:mm": "yyyy/MM/dd HH:mm", "yyyy.MM.dd": "yyyy.MM.dd", "MM/dd/yyyy h:mm AM/PM": "MM/dd/yyyy h:mm AM/PM", "MM/dd/yyyy": "MM/dd/yyyy", "M/d/yyyy": "M/d/yyyy", "MM/dd/yy": "MM/dd/yy", "dd/MM/yyyy": "dd/MM/yyyy", - "dd-MM-yyyy": "dd/MM/yyyy", + "dd-MM-yyyy": "dd-MM-yyyy", "dd.MM.yyyy": "dd.MM.yyyy", - named: "dd/MM/yyyy", + "named-mdy-full": "mmmm d, yyyy", + "named-mdy-abbr": "mmm d, yyyy", + "named-dmy-full": "d mmmm yyyy", + "named-dmy-abbr": "d mmm yyyy", + "named-abbr-dashes": "mmm-d-yyyy", }; ct.fa = map[df.formatType] || "dd/MM/yyyy"; @@ -265,6 +269,7 @@ export function update(fmt: string, v: any) { } export function is_date(fmt: string, v?: any) { + console.log(SSF.is_date(fmt, v), "is_date"); return SSF.is_date(fmt, v); } diff --git a/packages/core/src/modules/validation.ts b/packages/core/src/modules/validation.ts index eb5269c1..f8796607 100644 --- a/packages/core/src/modules/validation.ts +++ b/packages/core/src/modules/validation.ts @@ -109,6 +109,7 @@ const MONTH_NAME_MAP: Record = { const MONTH_NAMES_RE = "january|february|march|april|may|june|july|august|september|october|november|december|jan|feb|mar|apr|jun|jul|aug|sep|oct|nov|dec"; const MONTH_ABBR_RE = "jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec"; +const MONTH_ABBR_SET = new Set(MONTH_ABBR_RE.split("|")); function isValidDateParts(year: number, month: number, day: number): boolean { if (year < 1900) return false; @@ -355,6 +356,7 @@ export function detectDateFormat(str: string): DateFormatInfo | null { const d = +m[2]; const y = +m[3]; if (mo && isValidDateParts(y, mo, d)) { + const isAbbr = MONTH_ABBR_SET.has(m[1].toLowerCase()); return { year: y, month: mo, @@ -362,7 +364,7 @@ export function detectDateFormat(str: string): DateFormatInfo | null { hours: 0, minutes: 0, seconds: 0, - formatType: "named", + formatType: isAbbr ? "named-mdy-abbr" : "named-mdy-full", }; } } @@ -376,6 +378,7 @@ export function detectDateFormat(str: string): DateFormatInfo | null { const mo = MONTH_NAME_MAP[m[2].toLowerCase()]; const y = +m[3]; if (mo && isValidDateParts(y, mo, d)) { + const isAbbr = MONTH_ABBR_SET.has(m[2].toLowerCase()); return { year: y, month: mo, @@ -383,7 +386,7 @@ export function detectDateFormat(str: string): DateFormatInfo | null { hours: 0, minutes: 0, seconds: 0, - formatType: "named", + formatType: isAbbr ? "named-dmy-abbr" : "named-dmy-full", }; } } @@ -402,7 +405,7 @@ export function detectDateFormat(str: string): DateFormatInfo | null { hours: 0, minutes: 0, seconds: 0, - formatType: "named", + formatType: "named-abbr-dashes", }; } } diff --git a/packages/react/src/components/Toolbar/index.tsx b/packages/react/src/components/Toolbar/index.tsx index 1a8e2ae3..dfd2bd1b 100644 --- a/packages/react/src/components/Toolbar/index.tsx +++ b/packages/react/src/components/Toolbar/index.tsx @@ -759,22 +759,21 @@ const Toolbar: React.FC<{ const curr = normalizedCellAttr(cell, "ct"); const format = _.find(defaultFormat, (v) => v.value === curr?.fa); if (curr?.fa != null) { - if (format != null) { - currentFmt = format.text; + const hasTime = /[hH]:/.test(curr.fa); + + if (curr.t === "d") { + currentFmt = hasTime ? "Date time" : "Date"; } else if ( - curr?.fa?.includes("#,##0") || - curr?.fa === "0" || - curr?.fa === "0.00" + curr.t === "n" || + curr.fa.includes("#,##0") || + curr.fa === "0" || + curr.fa === "0.00" ) { currentFmt = "Number"; } else if (is_date(curr.fa)) { - // Check if format contains time indicators - const hasTime = - curr.fa.includes("h") || - curr.fa.includes("H") || - curr.fa.includes("m") || - curr.fa.includes("s"); currentFmt = hasTime ? "Date time" : "Date"; + } else if (format != null) { + currentFmt = format.text; } else { currentFmt = defaultFormat[defaultFormat.length - 1].text; }