@rowsncolumns/utils 9.2.40 → 9.2.41

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.
@@ -31,6 +31,19 @@ export declare const getExtendedValueError: (extValue?: Record<string, any> | nu
31
31
  * `getDefaultHorizontalAlignment(sheetId, rowIndex, columnIndex)` so a toolbar
32
32
  * alignment selector reports the alignment the grid actually draws.
33
33
  */
34
+ /**
35
+ * Excel's `justify` and `distributed` alignments imply word wrap: the cell
36
+ * wraps inside its own width even without `wrapStrategy: "wrap"` (there is
37
+ * nothing to spread across otherwise) and the row auto-fits the wrapped
38
+ * lines. Shared by the canvas renderer (wrap + overflow decision) and the
39
+ * state hooks' row auto-height so both agree on which cells wrap.
40
+ */
41
+ export declare const alignmentImpliesWrap: (align: HorizontalAlign | string | null | undefined) => boolean;
42
+ /**
43
+ * Whether a cell format wraps its text — explicit `wrapStrategy: "wrap"` or
44
+ * an alignment that wraps implicitly ({@link alignmentImpliesWrap}).
45
+ */
46
+ export declare const cellFormatWrapsText: (format: Pick<CellFormat, "wrapStrategy" | "horizontalAlignment"> | null | undefined) => boolean;
34
47
  export declare const resolveDefaultHorizontalAlignment: ({ errorValue, userEnteredValue, effectiveValue, dataValidation, }: {
35
48
  errorValue: unknown;
36
49
  userEnteredValue: Record<string, any> | null | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"cell-data-helpers.d.ts","sourceRoot":"","sources":["../../cell-data-helpers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,QAAQ,EACR,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,cAAc,EACf,MAAM,4BAA4B,CAAC;AAEpC,eAAO,MAAM,uBAAuB,GAAI,CAAC,SAAS,QAAQ,EACxD,OAAO,CAAC,GAAG,IAAI,KACd,CAAC,CAAC,IAAI,CAER,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,CAAC,SAAS,QAAQ,EACtD,OAAO,CAAC,GAAG,IAAI,KACd,CAAC,CAAC,IAAI,CAER,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAI,CAAC,SAAS,QAAQ,EACzD,OAAO,CAAC,GAAG,IAAI,KACd,CAAC,CAAC,IAAI,CAER,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,CAAC,SAAS,QAAQ,EACvD,OAAO,CAAC,GAAG,IAAI,KACd,UAAU,GAAG,cAAc,GAAG,IAAI,GAAG,SAEvC,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,CAAC,SAAS,QAAQ,EACtD,OAAO,CAAC,GAAG,IAAI,KACd,MAAM,GAAG,SAAS,GAAG,IAEvB,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,UAAU,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAG,MAE1D,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,MAAM,GAAG,SAEX,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,MAAM,GAAG,SAEX,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,OAAO,GAAG,SAEZ,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,MAAM,GAAG,SAEX,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,GAAG,GAAG,SAER,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iCAAiC,GAAI,mEAK/C;IACD,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACzD,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACvD,cAAc,EAAE,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;CAC1E,KAAG,eAAe,GAAG,SASrB,CAAC"}
1
+ {"version":3,"file":"cell-data-helpers.d.ts","sourceRoot":"","sources":["../../cell-data-helpers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,QAAQ,EACR,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,cAAc,EACf,MAAM,4BAA4B,CAAC;AAEpC,eAAO,MAAM,uBAAuB,GAAI,CAAC,SAAS,QAAQ,EACxD,OAAO,CAAC,GAAG,IAAI,KACd,CAAC,CAAC,IAAI,CAER,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,CAAC,SAAS,QAAQ,EACtD,OAAO,CAAC,GAAG,IAAI,KACd,CAAC,CAAC,IAAI,CAER,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAI,CAAC,SAAS,QAAQ,EACzD,OAAO,CAAC,GAAG,IAAI,KACd,CAAC,CAAC,IAAI,CAER,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,CAAC,SAAS,QAAQ,EACvD,OAAO,CAAC,GAAG,IAAI,KACd,UAAU,GAAG,cAAc,GAAG,IAAI,GAAG,SAEvC,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,CAAC,SAAS,QAAQ,EACtD,OAAO,CAAC,GAAG,IAAI,KACd,MAAM,GAAG,SAAS,GAAG,IAEvB,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,UAAU,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAG,MAE1D,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,MAAM,GAAG,SAEX,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,MAAM,GAAG,SAEX,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,OAAO,GAAG,SAEZ,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,MAAM,GAAG,SAEX,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,GAAG,GAAG,SAER,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAC/B,OAAO,eAAe,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,KACjD,OAAyD,CAAC;AAE7D;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAC9B,QACI,IAAI,CAAC,UAAU,EAAE,cAAc,GAAG,qBAAqB,CAAC,GACxD,IAAI,GACJ,SAAS,KACZ,OAEgD,CAAC;AAEpD,eAAO,MAAM,iCAAiC,GAAI,mEAK/C;IACD,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACzD,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACvD,cAAc,EAAE,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;CAC1E,KAAG,eAAe,GAAG,SASrB,CAAC"}
@@ -6,7 +6,7 @@
6
6
  * Note: These functions use generic types to avoid circular dependencies
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.resolveDefaultHorizontalAlignment = exports.getExtendedValueError = exports.getExtendedValueFormula = exports.getExtendedValueBool = exports.getExtendedValueString = exports.getExtendedValueNumber = exports.getStyleId = exports.getCellFormattedValue = exports.getCellEffectiveFormat = exports.getCellUserEnteredFormat = exports.getCellEffectiveValue = exports.getCellUserEnteredValue = void 0;
9
+ exports.resolveDefaultHorizontalAlignment = exports.cellFormatWrapsText = exports.alignmentImpliesWrap = exports.getExtendedValueError = exports.getExtendedValueFormula = exports.getExtendedValueBool = exports.getExtendedValueString = exports.getExtendedValueNumber = exports.getStyleId = exports.getCellFormattedValue = exports.getCellEffectiveFormat = exports.getCellUserEnteredFormat = exports.getCellEffectiveValue = exports.getCellUserEnteredValue = void 0;
10
10
  var getCellUserEnteredValue = function (cell) {
11
11
  var _a;
12
12
  return (_a = cell === null || cell === void 0 ? void 0 : cell.ue) !== null && _a !== void 0 ? _a : cell === null || cell === void 0 ? void 0 : cell.userEnteredValue;
@@ -77,6 +77,24 @@ exports.getExtendedValueError = getExtendedValueError;
77
77
  * `getDefaultHorizontalAlignment(sheetId, rowIndex, columnIndex)` so a toolbar
78
78
  * alignment selector reports the alignment the grid actually draws.
79
79
  */
80
+ /**
81
+ * Excel's `justify` and `distributed` alignments imply word wrap: the cell
82
+ * wraps inside its own width even without `wrapStrategy: "wrap"` (there is
83
+ * nothing to spread across otherwise) and the row auto-fits the wrapped
84
+ * lines. Shared by the canvas renderer (wrap + overflow decision) and the
85
+ * state hooks' row auto-height so both agree on which cells wrap.
86
+ */
87
+ var alignmentImpliesWrap = function (align) { return align === "justify" || align === "distributed"; };
88
+ exports.alignmentImpliesWrap = alignmentImpliesWrap;
89
+ /**
90
+ * Whether a cell format wraps its text — explicit `wrapStrategy: "wrap"` or
91
+ * an alignment that wraps implicitly ({@link alignmentImpliesWrap}).
92
+ */
93
+ var cellFormatWrapsText = function (format) {
94
+ return (format === null || format === void 0 ? void 0 : format.wrapStrategy) === "wrap" ||
95
+ (0, exports.alignmentImpliesWrap)(format === null || format === void 0 ? void 0 : format.horizontalAlignment);
96
+ };
97
+ exports.cellFormatWrapsText = cellFormatWrapsText;
80
98
  var resolveDefaultHorizontalAlignment = function (_a) {
81
99
  var _b;
82
100
  var errorValue = _a.errorValue, userEnteredValue = _a.userEnteredValue, effectiveValue = _a.effectiveValue, dataValidation = _a.dataValidation;
@@ -31,6 +31,18 @@ export declare const isStandaloneMonthPattern: (pattern: string) => boolean;
31
31
  * `A`/`P` locale, or `undefined` when there is none.
32
32
  */
33
33
  export declare const withShortAmPmMarker: (pattern: string) => string | undefined;
34
+ /**
35
+ * Excel's `[$symbol-LCID]` token selects the currency symbol and the
36
+ * language of day/month names and AM/PM; digit grouping and the decimal
37
+ * separator follow the workbook locale (`[$€-407] #,##0.00` is
38
+ * `€ 1,234.50` on a US host). numfmt applies the token's locale to the whole
39
+ * section, separators included, so the locale part is dropped from every
40
+ * section that is not a date (number, scientific, fraction, text, …); a
41
+ * date section keeps the token for its names, re-pointed at a companion
42
+ * locale that carries the workbook's separators for fractional seconds.
43
+ * Returns `undefined` when nothing had to change.
44
+ */
45
+ export declare const withWorkbookSeparators: (pattern: string, locale?: string) => string | undefined;
34
46
  export declare const ssfFormat: (pattern: string, value: any, locale?: string) => string;
35
47
  export declare const ssfFormatColor: (pattern: string, value: any) => string | undefined;
36
48
  //# sourceMappingURL=formatter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../formatter.ts"],"names":[],"mappings":"AAaA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OASlE,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KACjC,OAWF,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OAWlE,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OAOlE,CAAC;AAMF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,MAAM,KAAG,OA4B1D,CAAC;AAuHF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,KAAG,MAAM,GAAG,SA4C9D,CAAC;AAgBF,eAAO,MAAM,SAAS,GACpB,SAAS,MAAM,EACf,OAAO,GAAG,EACV,SAAS,MAAM,KACd,MAyCF,CAAC;AAmLF,eAAO,MAAM,cAAc,GACzB,SAAS,MAAM,EACf,OAAO,GAAG,KACT,MAAM,GAAG,SAoBX,CAAC"}
1
+ {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../formatter.ts"],"names":[],"mappings":"AAiBA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OASlE,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KACjC,OAWF,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OAWlE,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OAOlE,CAAC;AAMF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,MAAM,KAAG,OA4B1D,CAAC;AAuHF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,KAAG,MAAM,GAAG,SA4C9D,CAAC;AAiIF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,GACjC,SAAS,MAAM,EACf,SAAS,MAAM,KACd,MAAM,GAAG,SAiBX,CAAC;AAiBF,eAAO,MAAM,SAAS,GACpB,SAAS,MAAM,EACf,OAAO,GAAG,EACV,SAAS,MAAM,KACd,MAyCF,CAAC;AAmLF,eAAO,MAAM,cAAc,GACzB,SAAS,MAAM,EACf,OAAO,GAAG,KACT,MAAM,GAAG,SAoBX,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ssfFormatColor = exports.ssfFormat = exports.withShortAmPmMarker = exports.isStandaloneMonthPattern = exports.isTextPattern = exports.isTimePattern = exports.isDateTimePattern = exports.isDatePattern = void 0;
3
+ exports.ssfFormatColor = exports.ssfFormat = exports.withWorkbookSeparators = exports.withShortAmPmMarker = exports.isStandaloneMonthPattern = exports.isTextPattern = exports.isTimePattern = exports.isDateTimePattern = exports.isDatePattern = void 0;
4
4
  var tslib_1 = require("tslib");
5
5
  var numfmt_1 = require("numfmt");
6
6
  var colors_1 = require("./colors");
@@ -316,6 +316,155 @@ var separatorsFor = function (locale) {
316
316
  group: (_b = l10n.group) !== null && _b !== void 0 ? _b : ",",
317
317
  };
318
318
  };
319
+ /**
320
+ * Rewrite the `[$symbol-LCID]` tokens of one section through `rewrite`,
321
+ * which receives the bracket content (`€-407`) and returns the new content,
322
+ * or an empty string to drop the token. Quoted literals, escapes and other
323
+ * bracket directives are left as written.
324
+ */
325
+ var rewriteSectionLocaleTokens = function (section, rewrite) {
326
+ var _a;
327
+ var out = "";
328
+ var inQuotes = false;
329
+ var inBrackets = false;
330
+ for (var i = 0; i < section.length; i++) {
331
+ var char = section[i];
332
+ if (inQuotes) {
333
+ inQuotes = char !== '"';
334
+ }
335
+ else if (inBrackets) {
336
+ inBrackets = char !== "]";
337
+ }
338
+ else if (char === '"') {
339
+ inQuotes = true;
340
+ }
341
+ else if (char === "[") {
342
+ var end = section[i + 1] === "$" ? section.indexOf("]", i) : -1;
343
+ if (end > 0) {
344
+ var token = rewrite(section.slice(i + 1, end));
345
+ if (token)
346
+ out += "[".concat(token, "]");
347
+ i = end;
348
+ continue;
349
+ }
350
+ inBrackets = true;
351
+ }
352
+ else if (char === "\\" || char === "_" || char === "*") {
353
+ out += char + ((_a = section[i + 1]) !== null && _a !== void 0 ? _a : "");
354
+ i++;
355
+ continue;
356
+ }
357
+ out += char;
358
+ }
359
+ return out;
360
+ };
361
+ /** `[$€-407]` → `[$€]`; a bare `[$-407]` goes away. */
362
+ var currencySymbolOnly = function (token) {
363
+ var dash = token.indexOf("-");
364
+ var symbol = dash < 0 ? token : token.slice(0, dash);
365
+ return symbol.length > 1 ? symbol : "";
366
+ };
367
+ var separatorLocales = new Map();
368
+ /**
369
+ * Companion numfmt locale carrying `locale`'s calendar names and AM/PM with
370
+ * the workbook's decimal / group separators, so a date section pinned to
371
+ * `[$-407]` renders German names but a workbook decimal mark in `ss.00`.
372
+ * Returns `locale` itself when its separators already match.
373
+ */
374
+ var getWorkbookSeparatorLocale = function (locale, separators) {
375
+ var key = "".concat(locale, "\0").concat(separators.decimal, "\0").concat(separators.group);
376
+ var cached = separatorLocales.get(key);
377
+ if (cached !== undefined)
378
+ return cached;
379
+ var overlay = locale;
380
+ try {
381
+ var base = (0, numfmt_1.getLocale)(locale);
382
+ if (base &&
383
+ (base.decimal !== separators.decimal || base.group !== separators.group)) {
384
+ // numfmt locale ids are `[a-z\d]+` with one optional `_` part.
385
+ var hex = function (text) {
386
+ return tslib_1.__spreadArray([], tslib_1.__read(text), false).map(function (c) { return c.codePointAt(0).toString(16); }).join("x");
387
+ };
388
+ overlay = "sep".concat(hex(separators.decimal), "x").concat(hex(separators.group), "_").concat(locale.replace(/[^a-z\d]/gi, ""));
389
+ (0, numfmt_1.addLocale)(tslib_1.__assign(tslib_1.__assign({}, base), { decimal: separators.decimal, group: separators.group }), overlay);
390
+ }
391
+ }
392
+ catch (_a) {
393
+ overlay = locale;
394
+ }
395
+ separatorLocales.set(key, overlay);
396
+ return overlay;
397
+ };
398
+ /**
399
+ * Re-point a date section's `[$symbol-LCID]` token at the companion locale
400
+ * with the workbook's separators (`$-407` → `$-sep2ex2c_407`). An LCID numfmt
401
+ * does not know or one carrying calendar bits stays as written.
402
+ */
403
+ var workbookSeparatorLocaleToken = function (separators) {
404
+ return function (token) {
405
+ var dash = token.indexOf("-");
406
+ if (dash < 0)
407
+ return token;
408
+ var code = token.slice(dash + 1);
409
+ var wincode = Number.parseInt(code, 16);
410
+ if (Number.isFinite(wincode) && wincode & 0xff0000)
411
+ return token;
412
+ if (!(0, numfmt_1.getLocale)(code))
413
+ return token;
414
+ return "".concat(token.slice(0, dash + 1)).concat(getWorkbookSeparatorLocale(code, separators));
415
+ };
416
+ };
417
+ var isDateSection = function (section) {
418
+ try {
419
+ return (0, numfmt_1.isDateFormat)(section);
420
+ }
421
+ catch (_a) {
422
+ return false;
423
+ }
424
+ };
425
+ /**
426
+ * Excel's `[$symbol-LCID]` token selects the currency symbol and the
427
+ * language of day/month names and AM/PM; digit grouping and the decimal
428
+ * separator follow the workbook locale (`[$€-407] #,##0.00` is
429
+ * `€ 1,234.50` on a US host). numfmt applies the token's locale to the whole
430
+ * section, separators included, so the locale part is dropped from every
431
+ * section that is not a date (number, scientific, fraction, text, …); a
432
+ * date section keeps the token for its names, re-pointed at a companion
433
+ * locale that carries the workbook's separators for fractional seconds.
434
+ * Returns `undefined` when nothing had to change.
435
+ */
436
+ var withWorkbookSeparators = function (pattern, locale) {
437
+ if (!pattern.includes("[$"))
438
+ return undefined;
439
+ var sections = (0, excel_number_format_1.splitFormatSections)(pattern);
440
+ var changed = false;
441
+ var dateTokens;
442
+ var rewritten = sections.map(function (section) {
443
+ if (!section.includes("[$"))
444
+ return section;
445
+ var rewrite = currencySymbolOnly;
446
+ if (isDateSection(section)) {
447
+ dateTokens !== null && dateTokens !== void 0 ? dateTokens : (dateTokens = workbookSeparatorLocaleToken(separatorsFor(locale)));
448
+ rewrite = dateTokens;
449
+ }
450
+ var stripped = rewriteSectionLocaleTokens(section, rewrite);
451
+ changed || (changed = stripped !== section);
452
+ return stripped;
453
+ });
454
+ return changed ? rewritten.join(";") : undefined;
455
+ };
456
+ exports.withWorkbookSeparators = withWorkbookSeparators;
457
+ var workbookSeparatorPatternCache = new Map();
458
+ var withWorkbookSeparatorsCached = function (code, locale) {
459
+ var _a;
460
+ var key = "".concat(locale !== null && locale !== void 0 ? locale : "", "\0").concat(code);
461
+ var rewritten = workbookSeparatorPatternCache.get(key);
462
+ if (rewritten === undefined) {
463
+ rewritten = (_a = (0, exports.withWorkbookSeparators)(code, locale)) !== null && _a !== void 0 ? _a : code;
464
+ workbookSeparatorPatternCache.set(key, rewritten);
465
+ }
466
+ return rewritten;
467
+ };
319
468
  var ssfFormat = function (pattern, value, locale) {
320
469
  try {
321
470
  var shortAmPm = shortAmPmPatternCache.get(pattern);
@@ -323,7 +472,7 @@ var ssfFormat = function (pattern, value, locale) {
323
472
  shortAmPm = (0, exports.withShortAmPmMarker)(pattern);
324
473
  shortAmPmPatternCache.set(pattern, shortAmPm);
325
474
  }
326
- var code = shortAmPm !== null && shortAmPm !== void 0 ? shortAmPm : pattern;
475
+ var code = withWorkbookSeparatorsCached(shortAmPm !== null && shortAmPm !== void 0 ? shortAmPm : pattern, locale);
327
476
  var patternLocale = localeForPattern(pattern, locale);
328
477
  if (shortAmPm !== undefined && patternLocale) {
329
478
  patternLocale = getShortAmPmLocale(patternLocale);
@@ -16,6 +16,21 @@ export declare const getLocaleDateParts: (locale: string) => [DatePartType, stri
16
16
  * de-DE → `d.m.yyyy`, hu-HU → `yyyy. mm. dd.`, en-CA → `yyyy-mm-dd`.
17
17
  */
18
18
  export declare const getDefaultDateFormat: (locale?: string) => string;
19
+ export interface DateFieldTokens {
20
+ day: "d" | "dd";
21
+ month: "m" | "mm";
22
+ year: "yy" | "yyyy";
23
+ }
24
+ /**
25
+ * The day / month / year tokens of {@link getDefaultDateFormat} for a
26
+ * locale, i.e. whether the locale zero-pads each numeric field — ru-RU
27
+ * (`dd.mm.yyyy`) → `{ day: "dd", month: "mm", year: "yyyy" }`, en-US
28
+ * (`m/d/yyyy`) → `{ day: "d", month: "m", year: "yyyy" }`. A date typed
29
+ * as digits is displayed with the locale's own padding (Excel re-renders
30
+ * `30.9.2025` as `30.09.2025` under ru-RU), so the date parser assigns
31
+ * these tokens rather than echoing the padding the user happened to type.
32
+ */
33
+ export declare const getDefaultDateFieldTokens: (locale?: string) => DateFieldTokens;
19
34
  /**
20
35
  * Default time pattern for a locale, honoring its 12/24-hour convention,
21
36
  * separators and hour padding — e.g. en-US → `h:mm:ss AM/PM`,
@@ -1 +1 @@
1
- {"version":3,"file":"locale.d.ts","sourceRoot":"","sources":["../../locale.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,gBAAgB,cAU5B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;AAEpD;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,GAAI,QAAQ,MAAM,qCAkC3D,CAAC;AAEF,eAAO,MAAM,kBAAkB,sDAAyC,CAAC;AAqFzE;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAQ,MAA2B,WAClC,CAAC;AAuCvC;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,SAAQ,MAA2B,EACnC,YAAW,QAAQ,GAAG,QAAmB,WACS,CAAC;AAErD,eAAO,MAAM,wBAAwB,GAAI,SAAS,MAAM,WAEvD,CAAC;AA2CF;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAI,SAAQ,MAA2B,WAClC,CAAC"}
1
+ {"version":3,"file":"locale.d.ts","sourceRoot":"","sources":["../../locale.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,gBAAgB,cAU5B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;AAEpD;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,GAAI,QAAQ,MAAM,qCAkC3D,CAAC;AAEF,eAAO,MAAM,kBAAkB,sDAAyC,CAAC;AAiGzE;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAQ,MAA2B,WAClC,CAAC;AAEvC,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC;IAChB,KAAK,EAAE,GAAG,GAAG,IAAI,CAAC;IAClB,IAAI,EAAE,IAAI,GAAG,MAAM,CAAC;CACrB;AAwBD;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,GACpC,SAAQ,MAA2B,KAClC,eAA4D,CAAC;AAuChE;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,SAAQ,MAA2B,EACnC,YAAW,QAAQ,GAAG,QAAmB,WACS,CAAC;AAErD,eAAO,MAAM,wBAAwB,GAAI,SAAS,MAAM,WAEvD,CAAC;AA2CF;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAI,SAAQ,MAA2B,WAClC,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getLongDateFormat = exports.getDefaultDateTimeFormat = exports.getDefaultTimeFormat = exports.getDefaultDateFormat = exports.getLocaleDateParts = exports.getLocaleDatePartsNotMemoized = exports.getCurrentLocale = void 0;
3
+ exports.getLongDateFormat = exports.getDefaultDateTimeFormat = exports.getDefaultTimeFormat = exports.getDefaultDateFieldTokens = exports.getDefaultDateFormat = exports.getLocaleDateParts = exports.getLocaleDatePartsNotMemoized = exports.getCurrentLocale = void 0;
4
4
  var tslib_1 = require("tslib");
5
5
  var memoize_1 = require("./memoize");
6
6
  var defaultLocale;
@@ -134,9 +134,15 @@ var buildPatternFromParts = function (parts, toToken) {
134
134
  // trailing fields we skipped (e.g. an era) — trim stray whitespace.
135
135
  return pattern.trim();
136
136
  };
137
- var getDefaultDateFormatMemoized = (0, memoize_1.memoize)(function (locale) {
137
+ /**
138
+ * `formatToParts` of the locale's numeric short date for the sample
139
+ * instant — the single source both the default pattern and its per-field
140
+ * tokens are read from, so they can never disagree. Empty when Intl has
141
+ * no data for the locale.
142
+ */
143
+ var getDefaultDateParts = (0, memoize_1.memoize)(function (locale) {
138
144
  try {
139
- var parts = new Intl.DateTimeFormat(locale, {
145
+ return new Intl.DateTimeFormat(locale, {
140
146
  calendar: "gregory",
141
147
  numberingSystem: "latn",
142
148
  year: "numeric",
@@ -144,13 +150,15 @@ var getDefaultDateFormatMemoized = (0, memoize_1.memoize)(function (locale) {
144
150
  day: "numeric",
145
151
  timeZone: "UTC",
146
152
  }).formatToParts(FORMAT_SAMPLE_DATE);
147
- var pattern = buildPatternFromParts(parts, dateFieldToken);
148
- return pattern || "m/d/yyyy";
149
153
  }
150
154
  catch (err) {
151
- return "m/d/yyyy";
155
+ return [];
152
156
  }
153
157
  });
158
+ var getDefaultDateFormatMemoized = (0, memoize_1.memoize)(function (locale) {
159
+ var pattern = buildPatternFromParts(getDefaultDateParts(locale), dateFieldToken);
160
+ return pattern || "m/d/yyyy";
161
+ });
154
162
  /**
155
163
  * Default numeric (short) date pattern for a locale, preserving the
156
164
  * locale's field order, separators, literal text and zero-padding as
@@ -162,6 +170,50 @@ var getDefaultDateFormat = function (locale) {
162
170
  return getDefaultDateFormatMemoized(locale);
163
171
  };
164
172
  exports.getDefaultDateFormat = getDefaultDateFormat;
173
+ var getDefaultDateFieldTokensMemoized = (0, memoize_1.memoize)(function (locale) {
174
+ var e_3, _a;
175
+ var tokens = { day: "d", month: "m", year: "yyyy" };
176
+ try {
177
+ for (var _b = tslib_1.__values(getDefaultDateParts(locale)), _c = _b.next(); !_c.done; _c = _b.next()) {
178
+ var part = _c.value;
179
+ switch (part.type) {
180
+ case "day":
181
+ tokens.day = part.value.length === 2 ? "dd" : "d";
182
+ break;
183
+ case "month":
184
+ tokens.month = part.value.length === 2 ? "mm" : "m";
185
+ break;
186
+ case "year":
187
+ tokens.year = part.value.length === 2 ? "yy" : "yyyy";
188
+ break;
189
+ default:
190
+ break;
191
+ }
192
+ }
193
+ }
194
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
195
+ finally {
196
+ try {
197
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
198
+ }
199
+ finally { if (e_3) throw e_3.error; }
200
+ }
201
+ return tokens;
202
+ });
203
+ /**
204
+ * The day / month / year tokens of {@link getDefaultDateFormat} for a
205
+ * locale, i.e. whether the locale zero-pads each numeric field — ru-RU
206
+ * (`dd.mm.yyyy`) → `{ day: "dd", month: "mm", year: "yyyy" }`, en-US
207
+ * (`m/d/yyyy`) → `{ day: "d", month: "m", year: "yyyy" }`. A date typed
208
+ * as digits is displayed with the locale's own padding (Excel re-renders
209
+ * `30.9.2025` as `30.09.2025` under ru-RU), so the date parser assigns
210
+ * these tokens rather than echoing the padding the user happened to type.
211
+ */
212
+ var getDefaultDateFieldTokens = function (locale) {
213
+ if (locale === void 0) { locale = (0, exports.getCurrentLocale)(); }
214
+ return getDefaultDateFieldTokensMemoized(locale);
215
+ };
216
+ exports.getDefaultDateFieldTokens = getDefaultDateFieldTokens;
165
217
  var getDefaultTimeFormatMemoized = (0, memoize_1.memoize)(function (locale, precision) {
166
218
  try {
167
219
  var parts = new Intl.DateTimeFormat(locale, tslib_1.__assign(tslib_1.__assign({ numberingSystem: "latn", hour: "numeric", minute: "numeric" }, (precision === "second" ? { second: "numeric" } : {})), { timeZone: "UTC" })).formatToParts(FORMAT_SAMPLE_DATE);
@@ -31,6 +31,19 @@ export declare const getExtendedValueError: (extValue?: Record<string, any> | nu
31
31
  * `getDefaultHorizontalAlignment(sheetId, rowIndex, columnIndex)` so a toolbar
32
32
  * alignment selector reports the alignment the grid actually draws.
33
33
  */
34
+ /**
35
+ * Excel's `justify` and `distributed` alignments imply word wrap: the cell
36
+ * wraps inside its own width even without `wrapStrategy: "wrap"` (there is
37
+ * nothing to spread across otherwise) and the row auto-fits the wrapped
38
+ * lines. Shared by the canvas renderer (wrap + overflow decision) and the
39
+ * state hooks' row auto-height so both agree on which cells wrap.
40
+ */
41
+ export declare const alignmentImpliesWrap: (align: HorizontalAlign | string | null | undefined) => boolean;
42
+ /**
43
+ * Whether a cell format wraps its text — explicit `wrapStrategy: "wrap"` or
44
+ * an alignment that wraps implicitly ({@link alignmentImpliesWrap}).
45
+ */
46
+ export declare const cellFormatWrapsText: (format: Pick<CellFormat, "wrapStrategy" | "horizontalAlignment"> | null | undefined) => boolean;
34
47
  export declare const resolveDefaultHorizontalAlignment: ({ errorValue, userEnteredValue, effectiveValue, dataValidation, }: {
35
48
  errorValue: unknown;
36
49
  userEnteredValue: Record<string, any> | null | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"cell-data-helpers.d.ts","sourceRoot":"","sources":["../../cell-data-helpers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,QAAQ,EACR,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,cAAc,EACf,MAAM,4BAA4B,CAAC;AAEpC,eAAO,MAAM,uBAAuB,GAAI,CAAC,SAAS,QAAQ,EACxD,OAAO,CAAC,GAAG,IAAI,KACd,CAAC,CAAC,IAAI,CAER,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,CAAC,SAAS,QAAQ,EACtD,OAAO,CAAC,GAAG,IAAI,KACd,CAAC,CAAC,IAAI,CAER,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAI,CAAC,SAAS,QAAQ,EACzD,OAAO,CAAC,GAAG,IAAI,KACd,CAAC,CAAC,IAAI,CAER,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,CAAC,SAAS,QAAQ,EACvD,OAAO,CAAC,GAAG,IAAI,KACd,UAAU,GAAG,cAAc,GAAG,IAAI,GAAG,SAEvC,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,CAAC,SAAS,QAAQ,EACtD,OAAO,CAAC,GAAG,IAAI,KACd,MAAM,GAAG,SAAS,GAAG,IAEvB,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,UAAU,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAG,MAE1D,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,MAAM,GAAG,SAEX,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,MAAM,GAAG,SAEX,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,OAAO,GAAG,SAEZ,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,MAAM,GAAG,SAEX,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,GAAG,GAAG,SAER,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iCAAiC,GAAI,mEAK/C;IACD,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACzD,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACvD,cAAc,EAAE,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;CAC1E,KAAG,eAAe,GAAG,SASrB,CAAC"}
1
+ {"version":3,"file":"cell-data-helpers.d.ts","sourceRoot":"","sources":["../../cell-data-helpers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,QAAQ,EACR,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,cAAc,EACf,MAAM,4BAA4B,CAAC;AAEpC,eAAO,MAAM,uBAAuB,GAAI,CAAC,SAAS,QAAQ,EACxD,OAAO,CAAC,GAAG,IAAI,KACd,CAAC,CAAC,IAAI,CAER,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,CAAC,SAAS,QAAQ,EACtD,OAAO,CAAC,GAAG,IAAI,KACd,CAAC,CAAC,IAAI,CAER,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAI,CAAC,SAAS,QAAQ,EACzD,OAAO,CAAC,GAAG,IAAI,KACd,CAAC,CAAC,IAAI,CAER,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,CAAC,SAAS,QAAQ,EACvD,OAAO,CAAC,GAAG,IAAI,KACd,UAAU,GAAG,cAAc,GAAG,IAAI,GAAG,SAEvC,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,CAAC,SAAS,QAAQ,EACtD,OAAO,CAAC,GAAG,IAAI,KACd,MAAM,GAAG,SAAS,GAAG,IAEvB,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,UAAU,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAG,MAE1D,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,MAAM,GAAG,SAEX,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,MAAM,GAAG,SAEX,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,OAAO,GAAG,SAEZ,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,MAAM,GAAG,SAEX,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,KACpC,GAAG,GAAG,SAER,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAC/B,OAAO,eAAe,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,KACjD,OAAyD,CAAC;AAE7D;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAC9B,QACI,IAAI,CAAC,UAAU,EAAE,cAAc,GAAG,qBAAqB,CAAC,GACxD,IAAI,GACJ,SAAS,KACZ,OAEgD,CAAC;AAEpD,eAAO,MAAM,iCAAiC,GAAI,mEAK/C;IACD,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACzD,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACvD,cAAc,EAAE,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;CAC1E,KAAG,eAAe,GAAG,SASrB,CAAC"}
@@ -52,6 +52,20 @@ export const getExtendedValueError = (extValue) => {
52
52
  * `getDefaultHorizontalAlignment(sheetId, rowIndex, columnIndex)` so a toolbar
53
53
  * alignment selector reports the alignment the grid actually draws.
54
54
  */
55
+ /**
56
+ * Excel's `justify` and `distributed` alignments imply word wrap: the cell
57
+ * wraps inside its own width even without `wrapStrategy: "wrap"` (there is
58
+ * nothing to spread across otherwise) and the row auto-fits the wrapped
59
+ * lines. Shared by the canvas renderer (wrap + overflow decision) and the
60
+ * state hooks' row auto-height so both agree on which cells wrap.
61
+ */
62
+ export const alignmentImpliesWrap = (align) => align === "justify" || align === "distributed";
63
+ /**
64
+ * Whether a cell format wraps its text — explicit `wrapStrategy: "wrap"` or
65
+ * an alignment that wraps implicitly ({@link alignmentImpliesWrap}).
66
+ */
67
+ export const cellFormatWrapsText = (format) => format?.wrapStrategy === "wrap" ||
68
+ alignmentImpliesWrap(format?.horizontalAlignment);
55
69
  export const resolveDefaultHorizontalAlignment = ({ errorValue, userEnteredValue, effectiveValue, dataValidation, }) => {
56
70
  const isBoolean = getExtendedValueBool(userEnteredValue) !== undefined;
57
71
  const isCheckbox = dataValidation?.condition?.type === "BOOLEAN";
@@ -31,6 +31,18 @@ export declare const isStandaloneMonthPattern: (pattern: string) => boolean;
31
31
  * `A`/`P` locale, or `undefined` when there is none.
32
32
  */
33
33
  export declare const withShortAmPmMarker: (pattern: string) => string | undefined;
34
+ /**
35
+ * Excel's `[$symbol-LCID]` token selects the currency symbol and the
36
+ * language of day/month names and AM/PM; digit grouping and the decimal
37
+ * separator follow the workbook locale (`[$€-407] #,##0.00` is
38
+ * `€ 1,234.50` on a US host). numfmt applies the token's locale to the whole
39
+ * section, separators included, so the locale part is dropped from every
40
+ * section that is not a date (number, scientific, fraction, text, …); a
41
+ * date section keeps the token for its names, re-pointed at a companion
42
+ * locale that carries the workbook's separators for fractional seconds.
43
+ * Returns `undefined` when nothing had to change.
44
+ */
45
+ export declare const withWorkbookSeparators: (pattern: string, locale?: string) => string | undefined;
34
46
  export declare const ssfFormat: (pattern: string, value: any, locale?: string) => string;
35
47
  export declare const ssfFormatColor: (pattern: string, value: any) => string | undefined;
36
48
  //# sourceMappingURL=formatter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../formatter.ts"],"names":[],"mappings":"AAaA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OASlE,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KACjC,OAWF,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OAWlE,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OAOlE,CAAC;AAMF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,MAAM,KAAG,OA4B1D,CAAC;AAuHF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,KAAG,MAAM,GAAG,SA4C9D,CAAC;AAgBF,eAAO,MAAM,SAAS,GACpB,SAAS,MAAM,EACf,OAAO,GAAG,EACV,SAAS,MAAM,KACd,MAyCF,CAAC;AAmLF,eAAO,MAAM,cAAc,GACzB,SAAS,MAAM,EACf,OAAO,GAAG,KACT,MAAM,GAAG,SAoBX,CAAC"}
1
+ {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../formatter.ts"],"names":[],"mappings":"AAiBA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OASlE,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KACjC,OAWF,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OAWlE,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OAOlE,CAAC;AAMF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,MAAM,KAAG,OA4B1D,CAAC;AAuHF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,KAAG,MAAM,GAAG,SA4C9D,CAAC;AAiIF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,GACjC,SAAS,MAAM,EACf,SAAS,MAAM,KACd,MAAM,GAAG,SAiBX,CAAC;AAiBF,eAAO,MAAM,SAAS,GACpB,SAAS,MAAM,EACf,OAAO,GAAG,EACV,SAAS,MAAM,KACd,MAyCF,CAAC;AAmLF,eAAO,MAAM,cAAc,GACzB,SAAS,MAAM,EACf,OAAO,GAAG,KACT,MAAM,GAAG,SAoBX,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { addLocale, format, formatColor, getFormatDateInfo, getLocale, isDateFormat, isTextFormat, } from "numfmt";
2
2
  import { IndexedColors } from "./colors";
3
- import { excelNumberPrePass } from "./excel-number-format";
3
+ import { excelNumberPrePass, splitFormatSections, } from "./excel-number-format";
4
4
  /**
5
5
  * Pattern-based type inference (Excel parity). xlsx files routinely carry
6
6
  * a `numberFormat.pattern` without an accompanying `type` field — these
@@ -290,6 +290,148 @@ const separatorsFor = (locale) => {
290
290
  group: l10n.group ?? ",",
291
291
  };
292
292
  };
293
+ /**
294
+ * Rewrite the `[$symbol-LCID]` tokens of one section through `rewrite`,
295
+ * which receives the bracket content (`€-407`) and returns the new content,
296
+ * or an empty string to drop the token. Quoted literals, escapes and other
297
+ * bracket directives are left as written.
298
+ */
299
+ const rewriteSectionLocaleTokens = (section, rewrite) => {
300
+ let out = "";
301
+ let inQuotes = false;
302
+ let inBrackets = false;
303
+ for (let i = 0; i < section.length; i++) {
304
+ const char = section[i];
305
+ if (inQuotes) {
306
+ inQuotes = char !== '"';
307
+ }
308
+ else if (inBrackets) {
309
+ inBrackets = char !== "]";
310
+ }
311
+ else if (char === '"') {
312
+ inQuotes = true;
313
+ }
314
+ else if (char === "[") {
315
+ const end = section[i + 1] === "$" ? section.indexOf("]", i) : -1;
316
+ if (end > 0) {
317
+ const token = rewrite(section.slice(i + 1, end));
318
+ if (token)
319
+ out += `[${token}]`;
320
+ i = end;
321
+ continue;
322
+ }
323
+ inBrackets = true;
324
+ }
325
+ else if (char === "\\" || char === "_" || char === "*") {
326
+ out += char + (section[i + 1] ?? "");
327
+ i++;
328
+ continue;
329
+ }
330
+ out += char;
331
+ }
332
+ return out;
333
+ };
334
+ /** `[$€-407]` → `[$€]`; a bare `[$-407]` goes away. */
335
+ const currencySymbolOnly = (token) => {
336
+ const dash = token.indexOf("-");
337
+ const symbol = dash < 0 ? token : token.slice(0, dash);
338
+ return symbol.length > 1 ? symbol : "";
339
+ };
340
+ const separatorLocales = new Map();
341
+ /**
342
+ * Companion numfmt locale carrying `locale`'s calendar names and AM/PM with
343
+ * the workbook's decimal / group separators, so a date section pinned to
344
+ * `[$-407]` renders German names but a workbook decimal mark in `ss.00`.
345
+ * Returns `locale` itself when its separators already match.
346
+ */
347
+ const getWorkbookSeparatorLocale = (locale, separators) => {
348
+ const key = `${locale}\u0000${separators.decimal}\u0000${separators.group}`;
349
+ const cached = separatorLocales.get(key);
350
+ if (cached !== undefined)
351
+ return cached;
352
+ let overlay = locale;
353
+ try {
354
+ const base = getLocale(locale);
355
+ if (base &&
356
+ (base.decimal !== separators.decimal || base.group !== separators.group)) {
357
+ // numfmt locale ids are `[a-z\d]+` with one optional `_` part.
358
+ const hex = (text) => [...text].map((c) => c.codePointAt(0).toString(16)).join("x");
359
+ overlay = `sep${hex(separators.decimal)}x${hex(separators.group)}_${locale.replace(/[^a-z\d]/gi, "")}`;
360
+ addLocale({ ...base, decimal: separators.decimal, group: separators.group }, overlay);
361
+ }
362
+ }
363
+ catch {
364
+ overlay = locale;
365
+ }
366
+ separatorLocales.set(key, overlay);
367
+ return overlay;
368
+ };
369
+ /**
370
+ * Re-point a date section's `[$symbol-LCID]` token at the companion locale
371
+ * with the workbook's separators (`$-407` → `$-sep2ex2c_407`). An LCID numfmt
372
+ * does not know or one carrying calendar bits stays as written.
373
+ */
374
+ const workbookSeparatorLocaleToken = (separators) => (token) => {
375
+ const dash = token.indexOf("-");
376
+ if (dash < 0)
377
+ return token;
378
+ const code = token.slice(dash + 1);
379
+ const wincode = Number.parseInt(code, 16);
380
+ if (Number.isFinite(wincode) && wincode & 0xff0000)
381
+ return token;
382
+ if (!getLocale(code))
383
+ return token;
384
+ return `${token.slice(0, dash + 1)}${getWorkbookSeparatorLocale(code, separators)}`;
385
+ };
386
+ const isDateSection = (section) => {
387
+ try {
388
+ return isDateFormat(section);
389
+ }
390
+ catch {
391
+ return false;
392
+ }
393
+ };
394
+ /**
395
+ * Excel's `[$symbol-LCID]` token selects the currency symbol and the
396
+ * language of day/month names and AM/PM; digit grouping and the decimal
397
+ * separator follow the workbook locale (`[$€-407] #,##0.00` is
398
+ * `€ 1,234.50` on a US host). numfmt applies the token's locale to the whole
399
+ * section, separators included, so the locale part is dropped from every
400
+ * section that is not a date (number, scientific, fraction, text, …); a
401
+ * date section keeps the token for its names, re-pointed at a companion
402
+ * locale that carries the workbook's separators for fractional seconds.
403
+ * Returns `undefined` when nothing had to change.
404
+ */
405
+ export const withWorkbookSeparators = (pattern, locale) => {
406
+ if (!pattern.includes("[$"))
407
+ return undefined;
408
+ const sections = splitFormatSections(pattern);
409
+ let changed = false;
410
+ let dateTokens;
411
+ const rewritten = sections.map((section) => {
412
+ if (!section.includes("[$"))
413
+ return section;
414
+ let rewrite = currencySymbolOnly;
415
+ if (isDateSection(section)) {
416
+ dateTokens ??= workbookSeparatorLocaleToken(separatorsFor(locale));
417
+ rewrite = dateTokens;
418
+ }
419
+ const stripped = rewriteSectionLocaleTokens(section, rewrite);
420
+ changed ||= stripped !== section;
421
+ return stripped;
422
+ });
423
+ return changed ? rewritten.join(";") : undefined;
424
+ };
425
+ const workbookSeparatorPatternCache = new Map();
426
+ const withWorkbookSeparatorsCached = (code, locale) => {
427
+ const key = `${locale ?? ""}\u0000${code}`;
428
+ let rewritten = workbookSeparatorPatternCache.get(key);
429
+ if (rewritten === undefined) {
430
+ rewritten = withWorkbookSeparators(code, locale) ?? code;
431
+ workbookSeparatorPatternCache.set(key, rewritten);
432
+ }
433
+ return rewritten;
434
+ };
293
435
  export const ssfFormat = (pattern, value, locale) => {
294
436
  try {
295
437
  let shortAmPm = shortAmPmPatternCache.get(pattern);
@@ -297,7 +439,7 @@ export const ssfFormat = (pattern, value, locale) => {
297
439
  shortAmPm = withShortAmPmMarker(pattern);
298
440
  shortAmPmPatternCache.set(pattern, shortAmPm);
299
441
  }
300
- const code = shortAmPm ?? pattern;
442
+ const code = withWorkbookSeparatorsCached(shortAmPm ?? pattern, locale);
301
443
  let patternLocale = localeForPattern(pattern, locale);
302
444
  if (shortAmPm !== undefined && patternLocale) {
303
445
  patternLocale = getShortAmPmLocale(patternLocale);
@@ -16,6 +16,21 @@ export declare const getLocaleDateParts: (locale: string) => [DatePartType, stri
16
16
  * de-DE → `d.m.yyyy`, hu-HU → `yyyy. mm. dd.`, en-CA → `yyyy-mm-dd`.
17
17
  */
18
18
  export declare const getDefaultDateFormat: (locale?: string) => string;
19
+ export interface DateFieldTokens {
20
+ day: "d" | "dd";
21
+ month: "m" | "mm";
22
+ year: "yy" | "yyyy";
23
+ }
24
+ /**
25
+ * The day / month / year tokens of {@link getDefaultDateFormat} for a
26
+ * locale, i.e. whether the locale zero-pads each numeric field — ru-RU
27
+ * (`dd.mm.yyyy`) → `{ day: "dd", month: "mm", year: "yyyy" }`, en-US
28
+ * (`m/d/yyyy`) → `{ day: "d", month: "m", year: "yyyy" }`. A date typed
29
+ * as digits is displayed with the locale's own padding (Excel re-renders
30
+ * `30.9.2025` as `30.09.2025` under ru-RU), so the date parser assigns
31
+ * these tokens rather than echoing the padding the user happened to type.
32
+ */
33
+ export declare const getDefaultDateFieldTokens: (locale?: string) => DateFieldTokens;
19
34
  /**
20
35
  * Default time pattern for a locale, honoring its 12/24-hour convention,
21
36
  * separators and hour padding — e.g. en-US → `h:mm:ss AM/PM`,
@@ -1 +1 @@
1
- {"version":3,"file":"locale.d.ts","sourceRoot":"","sources":["../../locale.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,gBAAgB,cAU5B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;AAEpD;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,GAAI,QAAQ,MAAM,qCAkC3D,CAAC;AAEF,eAAO,MAAM,kBAAkB,sDAAyC,CAAC;AAqFzE;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAQ,MAA2B,WAClC,CAAC;AAuCvC;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,SAAQ,MAA2B,EACnC,YAAW,QAAQ,GAAG,QAAmB,WACS,CAAC;AAErD,eAAO,MAAM,wBAAwB,GAAI,SAAS,MAAM,WAEvD,CAAC;AA2CF;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAI,SAAQ,MAA2B,WAClC,CAAC"}
1
+ {"version":3,"file":"locale.d.ts","sourceRoot":"","sources":["../../locale.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,gBAAgB,cAU5B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;AAEpD;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,GAAI,QAAQ,MAAM,qCAkC3D,CAAC;AAEF,eAAO,MAAM,kBAAkB,sDAAyC,CAAC;AAiGzE;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAQ,MAA2B,WAClC,CAAC;AAEvC,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC;IAChB,KAAK,EAAE,GAAG,GAAG,IAAI,CAAC;IAClB,IAAI,EAAE,IAAI,GAAG,MAAM,CAAC;CACrB;AAwBD;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,GACpC,SAAQ,MAA2B,KAClC,eAA4D,CAAC;AAuChE;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,SAAQ,MAA2B,EACnC,YAAW,QAAQ,GAAG,QAAmB,WACS,CAAC;AAErD,eAAO,MAAM,wBAAwB,GAAI,SAAS,MAAM,WAEvD,CAAC;AA2CF;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAI,SAAQ,MAA2B,WAClC,CAAC"}
@@ -105,9 +105,15 @@ const buildPatternFromParts = (parts, toToken) => {
105
105
  // trailing fields we skipped (e.g. an era) — trim stray whitespace.
106
106
  return pattern.trim();
107
107
  };
108
- const getDefaultDateFormatMemoized = memoize((locale) => {
108
+ /**
109
+ * `formatToParts` of the locale's numeric short date for the sample
110
+ * instant — the single source both the default pattern and its per-field
111
+ * tokens are read from, so they can never disagree. Empty when Intl has
112
+ * no data for the locale.
113
+ */
114
+ const getDefaultDateParts = memoize((locale) => {
109
115
  try {
110
- const parts = new Intl.DateTimeFormat(locale, {
116
+ return new Intl.DateTimeFormat(locale, {
111
117
  calendar: "gregory",
112
118
  numberingSystem: "latn",
113
119
  year: "numeric",
@@ -115,13 +121,15 @@ const getDefaultDateFormatMemoized = memoize((locale) => {
115
121
  day: "numeric",
116
122
  timeZone: "UTC",
117
123
  }).formatToParts(FORMAT_SAMPLE_DATE);
118
- const pattern = buildPatternFromParts(parts, dateFieldToken);
119
- return pattern || "m/d/yyyy";
120
124
  }
121
125
  catch (err) {
122
- return "m/d/yyyy";
126
+ return [];
123
127
  }
124
128
  });
129
+ const getDefaultDateFormatMemoized = memoize((locale) => {
130
+ const pattern = buildPatternFromParts(getDefaultDateParts(locale), dateFieldToken);
131
+ return pattern || "m/d/yyyy";
132
+ });
125
133
  /**
126
134
  * Default numeric (short) date pattern for a locale, preserving the
127
135
  * locale's field order, separators, literal text and zero-padding as
@@ -129,6 +137,35 @@ const getDefaultDateFormatMemoized = memoize((locale) => {
129
137
  * de-DE → `d.m.yyyy`, hu-HU → `yyyy. mm. dd.`, en-CA → `yyyy-mm-dd`.
130
138
  */
131
139
  export const getDefaultDateFormat = (locale = getCurrentLocale()) => getDefaultDateFormatMemoized(locale);
140
+ const getDefaultDateFieldTokensMemoized = memoize((locale) => {
141
+ const tokens = { day: "d", month: "m", year: "yyyy" };
142
+ for (const part of getDefaultDateParts(locale)) {
143
+ switch (part.type) {
144
+ case "day":
145
+ tokens.day = part.value.length === 2 ? "dd" : "d";
146
+ break;
147
+ case "month":
148
+ tokens.month = part.value.length === 2 ? "mm" : "m";
149
+ break;
150
+ case "year":
151
+ tokens.year = part.value.length === 2 ? "yy" : "yyyy";
152
+ break;
153
+ default:
154
+ break;
155
+ }
156
+ }
157
+ return tokens;
158
+ });
159
+ /**
160
+ * The day / month / year tokens of {@link getDefaultDateFormat} for a
161
+ * locale, i.e. whether the locale zero-pads each numeric field — ru-RU
162
+ * (`dd.mm.yyyy`) → `{ day: "dd", month: "mm", year: "yyyy" }`, en-US
163
+ * (`m/d/yyyy`) → `{ day: "d", month: "m", year: "yyyy" }`. A date typed
164
+ * as digits is displayed with the locale's own padding (Excel re-renders
165
+ * `30.9.2025` as `30.09.2025` under ru-RU), so the date parser assigns
166
+ * these tokens rather than echoing the padding the user happened to type.
167
+ */
168
+ export const getDefaultDateFieldTokens = (locale = getCurrentLocale()) => getDefaultDateFieldTokensMemoized(locale);
132
169
  const getDefaultTimeFormatMemoized = memoize((locale, precision) => {
133
170
  try {
134
171
  const parts = new Intl.DateTimeFormat(locale, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rowsncolumns/utils",
3
- "version": "9.2.40",
3
+ "version": "9.2.41",
4
4
  "author": "Vinay M",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "main": "./dist/cjs/index.js",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "numfmt": "^3.2.2",
45
- "@rowsncolumns/common-types": ">=5.2.31"
45
+ "@rowsncolumns/common-types": ">=5.2.32"
46
46
  },
47
47
  "devDependencies": {},
48
48
  "peerDependencies": {}