Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 48 additions & 43 deletions packages/core/src/events/paste.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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" };
}
}

Expand Down
19 changes: 12 additions & 7 deletions packages/core/src/modules/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,20 +228,24 @@
ct.t = "d";

const map: Record<string, string> = {
"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";
Expand All @@ -265,6 +269,7 @@
}

export function is_date(fmt: string, v?: any) {
console.log(SSF.is_date(fmt, v), "is_date");

Check warning on line 272 in packages/core/src/modules/format.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected console statement

Check warning on line 272 in packages/core/src/modules/format.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected console statement

Check warning on line 272 in packages/core/src/modules/format.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected console statement

Check warning on line 272 in packages/core/src/modules/format.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected console statement
return SSF.is_date(fmt, v);
}

Expand Down
9 changes: 6 additions & 3 deletions packages/core/src/modules/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ const MONTH_NAME_MAP: Record<string, number> = {
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;
Expand Down Expand Up @@ -355,14 +356,15 @@ 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,
day: d,
hours: 0,
minutes: 0,
seconds: 0,
formatType: "named",
formatType: isAbbr ? "named-mdy-abbr" : "named-mdy-full",
};
}
}
Expand All @@ -376,14 +378,15 @@ 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,
day: d,
hours: 0,
minutes: 0,
seconds: 0,
formatType: "named",
formatType: isAbbr ? "named-dmy-abbr" : "named-dmy-full",
};
}
}
Expand All @@ -402,7 +405,7 @@ export function detectDateFormat(str: string): DateFormatInfo | null {
hours: 0,
minutes: 0,
seconds: 0,
formatType: "named",
formatType: "named-abbr-dashes",
};
}
}
Expand Down
18 changes: 13 additions & 5 deletions packages/react/src/components/Toolbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
Cell,
api,
getSheetIndex,
is_date,
} from "@fileverse-dev/fortune-core";
import _ from "lodash";
import {
Expand Down Expand Up @@ -758,14 +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)) {
currentFmt = hasTime ? "Date time" : "Date";
} else if (format != null) {
currentFmt = format.text;
} else {
currentFmt = defaultFormat[defaultFormat.length - 1].text;
}
Expand Down
15 changes: 15 additions & 0 deletions packages/react/src/components/Workbook/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
getFlowdata,
api,
handlePasteByClick,
update, // formatting helper
} from "@fileverse-dev/fortune-core";
import React, {
useMemo,
Expand Down Expand Up @@ -171,6 +172,20 @@ const Workbook = React.forwardRef<WorkbookInstance, Settings & AdditionalProps>(
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;
Expand Down
Loading