@rowsncolumns/utils 9.2.40 → 9.2.42

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;AA+LF;;;;;;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,MA0CF,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");
@@ -144,6 +144,69 @@ var intlMonthNames = function (locale, options) {
144
144
  .find(function (part) { return part.type === "month"; })) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "";
145
145
  });
146
146
  };
147
+ var intlRegisteredLocales = new Set();
148
+ /**
149
+ * numfmt ships name tables for a fixed set of languages; a tag it does not know
150
+ * (et, ms, nn, …) silently renders English month/weekday names and en-US
151
+ * separators. Excel and `Intl.DateTimeFormat` localize every locale, so an
152
+ * unknown tag gets a numfmt locale built from Intl on first use: separators
153
+ * from `Intl.NumberFormat`, the in-date month names (numfmt's single table is
154
+ * the form used beside a day number — see `getStandaloneMonthLocale`), the
155
+ * in-date weekday names (the form `dateStyle: "full"` shows) and the AM/PM
156
+ * designators. Tags Intl itself does not know keep numfmt's fallback.
157
+ */
158
+ var ensureNumfmtLocale = function (locale) {
159
+ var _a, _b;
160
+ if (intlRegisteredLocales.has(locale))
161
+ return;
162
+ intlRegisteredLocales.add(locale);
163
+ try {
164
+ if ((0, numfmt_1.getLocale)(locale))
165
+ return;
166
+ var canonical = Intl.DateTimeFormat.supportedLocalesOf([locale])[0];
167
+ if (!canonical)
168
+ return;
169
+ var numberParts_1 = new Intl.NumberFormat(locale).formatToParts(1234567.5);
170
+ var part = function (type) { var _a; return (_a = numberParts_1.find(function (p) { return p.type === type; })) === null || _a === void 0 ? void 0 : _a.value; };
171
+ var weekdayNames = function (weekday) {
172
+ // 2024-01-07 was a Sunday; numfmt indexes `dddd[0]` as Sunday.
173
+ var fmt = new Intl.DateTimeFormat(locale, {
174
+ weekday: weekday,
175
+ day: "numeric",
176
+ month: "long",
177
+ year: "numeric",
178
+ timeZone: "UTC",
179
+ });
180
+ return Array.from({ length: 7 }, function (_, i) {
181
+ var _a, _b;
182
+ return (_b = (_a = fmt
183
+ .formatToParts(new Date(Date.UTC(2024, 0, 7 + i)))
184
+ .find(function (p) { return p.type === "weekday"; })) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "";
185
+ });
186
+ };
187
+ var dayPeriod = function (hour) {
188
+ var _a;
189
+ return (_a = new Intl.DateTimeFormat(locale, {
190
+ hour: "numeric",
191
+ hour12: true,
192
+ timeZone: "UTC",
193
+ })
194
+ .formatToParts(new Date(Date.UTC(2024, 0, 7, hour)))
195
+ .find(function (p) { return p.type === "dayPeriod"; })) === null || _a === void 0 ? void 0 : _a.value;
196
+ };
197
+ var dateParts = new Intl.DateTimeFormat(locale, {
198
+ timeZone: "UTC",
199
+ }).formatToParts(new Date(Date.UTC(2024, 0, 7)));
200
+ var monthIndex = dateParts.findIndex(function (p) { return p.type === "month"; });
201
+ var dayIndex = dateParts.findIndex(function (p) { return p.type === "day"; });
202
+ var am = dayPeriod(9);
203
+ var pm = dayPeriod(21);
204
+ (0, numfmt_1.addLocale)(tslib_1.__assign(tslib_1.__assign({ group: (_a = part("group")) !== null && _a !== void 0 ? _a : "\u00a0", decimal: (_b = part("decimal")) !== null && _b !== void 0 ? _b : ".", mmmm: intlMonthNames(locale, { day: "numeric", month: "long" }), mmm: intlMonthNames(locale, { day: "numeric", month: "short" }), dddd: weekdayNames("long"), ddd: weekdayNames("short") }, (am && pm && am !== pm ? { ampm: [am, pm] } : {})), { preferMDY: monthIndex >= 0 && dayIndex >= 0 && monthIndex < dayIndex }), locale);
205
+ }
206
+ catch (_c) {
207
+ // Malformed tag: numfmt keeps its own fallback.
208
+ }
209
+ };
147
210
  var standaloneMonthLocales = new Map();
148
211
  /**
149
212
  * numfmt ships one month-name table per locale: the form used beside a day
@@ -316,14 +379,165 @@ var separatorsFor = function (locale) {
316
379
  group: (_b = l10n.group) !== null && _b !== void 0 ? _b : ",",
317
380
  };
318
381
  };
382
+ /**
383
+ * Rewrite the `[$symbol-LCID]` tokens of one section through `rewrite`,
384
+ * which receives the bracket content (`€-407`) and returns the new content,
385
+ * or an empty string to drop the token. Quoted literals, escapes and other
386
+ * bracket directives are left as written.
387
+ */
388
+ var rewriteSectionLocaleTokens = function (section, rewrite) {
389
+ var _a;
390
+ var out = "";
391
+ var inQuotes = false;
392
+ var inBrackets = false;
393
+ for (var i = 0; i < section.length; i++) {
394
+ var char = section[i];
395
+ if (inQuotes) {
396
+ inQuotes = char !== '"';
397
+ }
398
+ else if (inBrackets) {
399
+ inBrackets = char !== "]";
400
+ }
401
+ else if (char === '"') {
402
+ inQuotes = true;
403
+ }
404
+ else if (char === "[") {
405
+ var end = section[i + 1] === "$" ? section.indexOf("]", i) : -1;
406
+ if (end > 0) {
407
+ var token = rewrite(section.slice(i + 1, end));
408
+ if (token)
409
+ out += "[".concat(token, "]");
410
+ i = end;
411
+ continue;
412
+ }
413
+ inBrackets = true;
414
+ }
415
+ else if (char === "\\" || char === "_" || char === "*") {
416
+ out += char + ((_a = section[i + 1]) !== null && _a !== void 0 ? _a : "");
417
+ i++;
418
+ continue;
419
+ }
420
+ out += char;
421
+ }
422
+ return out;
423
+ };
424
+ /** `[$€-407]` → `[$€]`; a bare `[$-407]` goes away. */
425
+ var currencySymbolOnly = function (token) {
426
+ var dash = token.indexOf("-");
427
+ var symbol = dash < 0 ? token : token.slice(0, dash);
428
+ return symbol.length > 1 ? symbol : "";
429
+ };
430
+ var separatorLocales = new Map();
431
+ /**
432
+ * Companion numfmt locale carrying `locale`'s calendar names and AM/PM with
433
+ * the workbook's decimal / group separators, so a date section pinned to
434
+ * `[$-407]` renders German names but a workbook decimal mark in `ss.00`.
435
+ * Returns `locale` itself when its separators already match.
436
+ */
437
+ var getWorkbookSeparatorLocale = function (locale, separators) {
438
+ var key = "".concat(locale, "\0").concat(separators.decimal, "\0").concat(separators.group);
439
+ var cached = separatorLocales.get(key);
440
+ if (cached !== undefined)
441
+ return cached;
442
+ var overlay = locale;
443
+ try {
444
+ var base = (0, numfmt_1.getLocale)(locale);
445
+ if (base &&
446
+ (base.decimal !== separators.decimal || base.group !== separators.group)) {
447
+ // numfmt locale ids are `[a-z\d]+` with one optional `_` part.
448
+ var hex = function (text) {
449
+ return tslib_1.__spreadArray([], tslib_1.__read(text), false).map(function (c) { return c.codePointAt(0).toString(16); }).join("x");
450
+ };
451
+ overlay = "sep".concat(hex(separators.decimal), "x").concat(hex(separators.group), "_").concat(locale.replace(/[^a-z\d]/gi, ""));
452
+ (0, numfmt_1.addLocale)(tslib_1.__assign(tslib_1.__assign({}, base), { decimal: separators.decimal, group: separators.group }), overlay);
453
+ }
454
+ }
455
+ catch (_a) {
456
+ overlay = locale;
457
+ }
458
+ separatorLocales.set(key, overlay);
459
+ return overlay;
460
+ };
461
+ /**
462
+ * Re-point a date section's `[$symbol-LCID]` token at the companion locale
463
+ * with the workbook's separators (`$-407` → `$-sep2ex2c_407`). An LCID numfmt
464
+ * does not know or one carrying calendar bits stays as written.
465
+ */
466
+ var workbookSeparatorLocaleToken = function (separators) {
467
+ return function (token) {
468
+ var dash = token.indexOf("-");
469
+ if (dash < 0)
470
+ return token;
471
+ var code = token.slice(dash + 1);
472
+ var wincode = Number.parseInt(code, 16);
473
+ if (Number.isFinite(wincode) && wincode & 0xff0000)
474
+ return token;
475
+ if (!(0, numfmt_1.getLocale)(code))
476
+ return token;
477
+ return "".concat(token.slice(0, dash + 1)).concat(getWorkbookSeparatorLocale(code, separators));
478
+ };
479
+ };
480
+ var isDateSection = function (section) {
481
+ try {
482
+ return (0, numfmt_1.isDateFormat)(section);
483
+ }
484
+ catch (_a) {
485
+ return false;
486
+ }
487
+ };
488
+ /**
489
+ * Excel's `[$symbol-LCID]` token selects the currency symbol and the
490
+ * language of day/month names and AM/PM; digit grouping and the decimal
491
+ * separator follow the workbook locale (`[$€-407] #,##0.00` is
492
+ * `€ 1,234.50` on a US host). numfmt applies the token's locale to the whole
493
+ * section, separators included, so the locale part is dropped from every
494
+ * section that is not a date (number, scientific, fraction, text, …); a
495
+ * date section keeps the token for its names, re-pointed at a companion
496
+ * locale that carries the workbook's separators for fractional seconds.
497
+ * Returns `undefined` when nothing had to change.
498
+ */
499
+ var withWorkbookSeparators = function (pattern, locale) {
500
+ if (!pattern.includes("[$"))
501
+ return undefined;
502
+ var sections = (0, excel_number_format_1.splitFormatSections)(pattern);
503
+ var changed = false;
504
+ var dateTokens;
505
+ var rewritten = sections.map(function (section) {
506
+ if (!section.includes("[$"))
507
+ return section;
508
+ var rewrite = currencySymbolOnly;
509
+ if (isDateSection(section)) {
510
+ dateTokens !== null && dateTokens !== void 0 ? dateTokens : (dateTokens = workbookSeparatorLocaleToken(separatorsFor(locale)));
511
+ rewrite = dateTokens;
512
+ }
513
+ var stripped = rewriteSectionLocaleTokens(section, rewrite);
514
+ changed || (changed = stripped !== section);
515
+ return stripped;
516
+ });
517
+ return changed ? rewritten.join(";") : undefined;
518
+ };
519
+ exports.withWorkbookSeparators = withWorkbookSeparators;
520
+ var workbookSeparatorPatternCache = new Map();
521
+ var withWorkbookSeparatorsCached = function (code, locale) {
522
+ var _a;
523
+ var key = "".concat(locale !== null && locale !== void 0 ? locale : "", "\0").concat(code);
524
+ var rewritten = workbookSeparatorPatternCache.get(key);
525
+ if (rewritten === undefined) {
526
+ rewritten = (_a = (0, exports.withWorkbookSeparators)(code, locale)) !== null && _a !== void 0 ? _a : code;
527
+ workbookSeparatorPatternCache.set(key, rewritten);
528
+ }
529
+ return rewritten;
530
+ };
319
531
  var ssfFormat = function (pattern, value, locale) {
320
532
  try {
533
+ if (locale)
534
+ ensureNumfmtLocale(locale);
321
535
  var shortAmPm = shortAmPmPatternCache.get(pattern);
322
536
  if (shortAmPm === undefined && !shortAmPmPatternCache.has(pattern)) {
323
537
  shortAmPm = (0, exports.withShortAmPmMarker)(pattern);
324
538
  shortAmPmPatternCache.set(pattern, shortAmPm);
325
539
  }
326
- var code = shortAmPm !== null && shortAmPm !== void 0 ? shortAmPm : pattern;
540
+ var code = withWorkbookSeparatorsCached(shortAmPm !== null && shortAmPm !== void 0 ? shortAmPm : pattern, locale);
327
541
  var patternLocale = localeForPattern(pattern, locale);
328
542
  if (shortAmPm !== undefined && patternLocale) {
329
543
  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;AA+LF;;;;;;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,MA0CF,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
@@ -132,6 +132,71 @@ const intlMonthNames = (locale, options) => {
132
132
  .formatToParts(new Date(Date.UTC(2020, month, 1)))
133
133
  .find((part) => part.type === "month")?.value ?? "");
134
134
  };
135
+ const intlRegisteredLocales = new Set();
136
+ /**
137
+ * numfmt ships name tables for a fixed set of languages; a tag it does not know
138
+ * (et, ms, nn, …) silently renders English month/weekday names and en-US
139
+ * separators. Excel and `Intl.DateTimeFormat` localize every locale, so an
140
+ * unknown tag gets a numfmt locale built from Intl on first use: separators
141
+ * from `Intl.NumberFormat`, the in-date month names (numfmt's single table is
142
+ * the form used beside a day number — see `getStandaloneMonthLocale`), the
143
+ * in-date weekday names (the form `dateStyle: "full"` shows) and the AM/PM
144
+ * designators. Tags Intl itself does not know keep numfmt's fallback.
145
+ */
146
+ const ensureNumfmtLocale = (locale) => {
147
+ if (intlRegisteredLocales.has(locale))
148
+ return;
149
+ intlRegisteredLocales.add(locale);
150
+ try {
151
+ if (getLocale(locale))
152
+ return;
153
+ const canonical = Intl.DateTimeFormat.supportedLocalesOf([locale])[0];
154
+ if (!canonical)
155
+ return;
156
+ const numberParts = new Intl.NumberFormat(locale).formatToParts(1234567.5);
157
+ const part = (type) => numberParts.find((p) => p.type === type)?.value;
158
+ const weekdayNames = (weekday) => {
159
+ // 2024-01-07 was a Sunday; numfmt indexes `dddd[0]` as Sunday.
160
+ const fmt = new Intl.DateTimeFormat(locale, {
161
+ weekday,
162
+ day: "numeric",
163
+ month: "long",
164
+ year: "numeric",
165
+ timeZone: "UTC",
166
+ });
167
+ return Array.from({ length: 7 }, (_, i) => fmt
168
+ .formatToParts(new Date(Date.UTC(2024, 0, 7 + i)))
169
+ .find((p) => p.type === "weekday")?.value ?? "");
170
+ };
171
+ const dayPeriod = (hour) => new Intl.DateTimeFormat(locale, {
172
+ hour: "numeric",
173
+ hour12: true,
174
+ timeZone: "UTC",
175
+ })
176
+ .formatToParts(new Date(Date.UTC(2024, 0, 7, hour)))
177
+ .find((p) => p.type === "dayPeriod")?.value;
178
+ const dateParts = new Intl.DateTimeFormat(locale, {
179
+ timeZone: "UTC",
180
+ }).formatToParts(new Date(Date.UTC(2024, 0, 7)));
181
+ const monthIndex = dateParts.findIndex((p) => p.type === "month");
182
+ const dayIndex = dateParts.findIndex((p) => p.type === "day");
183
+ const am = dayPeriod(9);
184
+ const pm = dayPeriod(21);
185
+ addLocale({
186
+ group: part("group") ?? "\u00a0",
187
+ decimal: part("decimal") ?? ".",
188
+ mmmm: intlMonthNames(locale, { day: "numeric", month: "long" }),
189
+ mmm: intlMonthNames(locale, { day: "numeric", month: "short" }),
190
+ dddd: weekdayNames("long"),
191
+ ddd: weekdayNames("short"),
192
+ ...(am && pm && am !== pm ? { ampm: [am, pm] } : {}),
193
+ preferMDY: monthIndex >= 0 && dayIndex >= 0 && monthIndex < dayIndex,
194
+ }, locale);
195
+ }
196
+ catch {
197
+ // Malformed tag: numfmt keeps its own fallback.
198
+ }
199
+ };
135
200
  const standaloneMonthLocales = new Map();
136
201
  /**
137
202
  * numfmt ships one month-name table per locale: the form used beside a day
@@ -290,14 +355,158 @@ const separatorsFor = (locale) => {
290
355
  group: l10n.group ?? ",",
291
356
  };
292
357
  };
358
+ /**
359
+ * Rewrite the `[$symbol-LCID]` tokens of one section through `rewrite`,
360
+ * which receives the bracket content (`€-407`) and returns the new content,
361
+ * or an empty string to drop the token. Quoted literals, escapes and other
362
+ * bracket directives are left as written.
363
+ */
364
+ const rewriteSectionLocaleTokens = (section, rewrite) => {
365
+ let out = "";
366
+ let inQuotes = false;
367
+ let inBrackets = false;
368
+ for (let i = 0; i < section.length; i++) {
369
+ const char = section[i];
370
+ if (inQuotes) {
371
+ inQuotes = char !== '"';
372
+ }
373
+ else if (inBrackets) {
374
+ inBrackets = char !== "]";
375
+ }
376
+ else if (char === '"') {
377
+ inQuotes = true;
378
+ }
379
+ else if (char === "[") {
380
+ const end = section[i + 1] === "$" ? section.indexOf("]", i) : -1;
381
+ if (end > 0) {
382
+ const token = rewrite(section.slice(i + 1, end));
383
+ if (token)
384
+ out += `[${token}]`;
385
+ i = end;
386
+ continue;
387
+ }
388
+ inBrackets = true;
389
+ }
390
+ else if (char === "\\" || char === "_" || char === "*") {
391
+ out += char + (section[i + 1] ?? "");
392
+ i++;
393
+ continue;
394
+ }
395
+ out += char;
396
+ }
397
+ return out;
398
+ };
399
+ /** `[$€-407]` → `[$€]`; a bare `[$-407]` goes away. */
400
+ const currencySymbolOnly = (token) => {
401
+ const dash = token.indexOf("-");
402
+ const symbol = dash < 0 ? token : token.slice(0, dash);
403
+ return symbol.length > 1 ? symbol : "";
404
+ };
405
+ const separatorLocales = new Map();
406
+ /**
407
+ * Companion numfmt locale carrying `locale`'s calendar names and AM/PM with
408
+ * the workbook's decimal / group separators, so a date section pinned to
409
+ * `[$-407]` renders German names but a workbook decimal mark in `ss.00`.
410
+ * Returns `locale` itself when its separators already match.
411
+ */
412
+ const getWorkbookSeparatorLocale = (locale, separators) => {
413
+ const key = `${locale}\u0000${separators.decimal}\u0000${separators.group}`;
414
+ const cached = separatorLocales.get(key);
415
+ if (cached !== undefined)
416
+ return cached;
417
+ let overlay = locale;
418
+ try {
419
+ const base = getLocale(locale);
420
+ if (base &&
421
+ (base.decimal !== separators.decimal || base.group !== separators.group)) {
422
+ // numfmt locale ids are `[a-z\d]+` with one optional `_` part.
423
+ const hex = (text) => [...text].map((c) => c.codePointAt(0).toString(16)).join("x");
424
+ overlay = `sep${hex(separators.decimal)}x${hex(separators.group)}_${locale.replace(/[^a-z\d]/gi, "")}`;
425
+ addLocale({ ...base, decimal: separators.decimal, group: separators.group }, overlay);
426
+ }
427
+ }
428
+ catch {
429
+ overlay = locale;
430
+ }
431
+ separatorLocales.set(key, overlay);
432
+ return overlay;
433
+ };
434
+ /**
435
+ * Re-point a date section's `[$symbol-LCID]` token at the companion locale
436
+ * with the workbook's separators (`$-407` → `$-sep2ex2c_407`). An LCID numfmt
437
+ * does not know or one carrying calendar bits stays as written.
438
+ */
439
+ const workbookSeparatorLocaleToken = (separators) => (token) => {
440
+ const dash = token.indexOf("-");
441
+ if (dash < 0)
442
+ return token;
443
+ const code = token.slice(dash + 1);
444
+ const wincode = Number.parseInt(code, 16);
445
+ if (Number.isFinite(wincode) && wincode & 0xff0000)
446
+ return token;
447
+ if (!getLocale(code))
448
+ return token;
449
+ return `${token.slice(0, dash + 1)}${getWorkbookSeparatorLocale(code, separators)}`;
450
+ };
451
+ const isDateSection = (section) => {
452
+ try {
453
+ return isDateFormat(section);
454
+ }
455
+ catch {
456
+ return false;
457
+ }
458
+ };
459
+ /**
460
+ * Excel's `[$symbol-LCID]` token selects the currency symbol and the
461
+ * language of day/month names and AM/PM; digit grouping and the decimal
462
+ * separator follow the workbook locale (`[$€-407] #,##0.00` is
463
+ * `€ 1,234.50` on a US host). numfmt applies the token's locale to the whole
464
+ * section, separators included, so the locale part is dropped from every
465
+ * section that is not a date (number, scientific, fraction, text, …); a
466
+ * date section keeps the token for its names, re-pointed at a companion
467
+ * locale that carries the workbook's separators for fractional seconds.
468
+ * Returns `undefined` when nothing had to change.
469
+ */
470
+ export const withWorkbookSeparators = (pattern, locale) => {
471
+ if (!pattern.includes("[$"))
472
+ return undefined;
473
+ const sections = splitFormatSections(pattern);
474
+ let changed = false;
475
+ let dateTokens;
476
+ const rewritten = sections.map((section) => {
477
+ if (!section.includes("[$"))
478
+ return section;
479
+ let rewrite = currencySymbolOnly;
480
+ if (isDateSection(section)) {
481
+ dateTokens ??= workbookSeparatorLocaleToken(separatorsFor(locale));
482
+ rewrite = dateTokens;
483
+ }
484
+ const stripped = rewriteSectionLocaleTokens(section, rewrite);
485
+ changed ||= stripped !== section;
486
+ return stripped;
487
+ });
488
+ return changed ? rewritten.join(";") : undefined;
489
+ };
490
+ const workbookSeparatorPatternCache = new Map();
491
+ const withWorkbookSeparatorsCached = (code, locale) => {
492
+ const key = `${locale ?? ""}\u0000${code}`;
493
+ let rewritten = workbookSeparatorPatternCache.get(key);
494
+ if (rewritten === undefined) {
495
+ rewritten = withWorkbookSeparators(code, locale) ?? code;
496
+ workbookSeparatorPatternCache.set(key, rewritten);
497
+ }
498
+ return rewritten;
499
+ };
293
500
  export const ssfFormat = (pattern, value, locale) => {
294
501
  try {
502
+ if (locale)
503
+ ensureNumfmtLocale(locale);
295
504
  let shortAmPm = shortAmPmPatternCache.get(pattern);
296
505
  if (shortAmPm === undefined && !shortAmPmPatternCache.has(pattern)) {
297
506
  shortAmPm = withShortAmPmMarker(pattern);
298
507
  shortAmPmPatternCache.set(pattern, shortAmPm);
299
508
  }
300
- const code = shortAmPm ?? pattern;
509
+ const code = withWorkbookSeparatorsCached(shortAmPm ?? pattern, locale);
301
510
  let patternLocale = localeForPattern(pattern, locale);
302
511
  if (shortAmPm !== undefined && patternLocale) {
303
512
  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.42",
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": {}