@rowsncolumns/utils 9.2.39 → 9.2.40

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.
@@ -1,5 +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
4
  /**
4
5
  * Pattern-based type inference (Excel parity). xlsx files routinely carry
5
6
  * a `numberFormat.pattern` without an accompanying `type` field — these
@@ -177,12 +178,145 @@ const localeForPattern = (pattern, locale) => {
177
178
  }
178
179
  return standalone ? (getStandaloneMonthLocale(locale) ?? locale) : locale;
179
180
  };
181
+ const shortAmPmLocales = new Map();
182
+ /**
183
+ * Excel's `A/P` renders one letter (`6:00 P`) in every locale, but numfmt
184
+ * honours the short form only when no locale is passed — under a locale it
185
+ * emits that locale's full designator (`6:00 PM`). Patterns with `A/P` render
186
+ * under a companion locale whose AM/PM designators are `A`/`P`. `locale` is a
187
+ * BCP 47 tag or an Excel LCID (`409`), as numfmt accepts both.
188
+ */
189
+ const getShortAmPmLocale = (locale) => {
190
+ const cached = shortAmPmLocales.get(locale);
191
+ if (cached !== undefined)
192
+ return cached;
193
+ let overlay = locale;
194
+ try {
195
+ overlay = `ampm_${locale.replace(/[^a-z\d]/gi, "")}`;
196
+ addLocale({ ...(getLocale(locale) ?? {}), ampm: ["A", "P"] }, overlay);
197
+ }
198
+ catch {
199
+ overlay = locale;
200
+ }
201
+ shortAmPmLocales.set(locale, overlay);
202
+ return overlay;
203
+ };
204
+ /**
205
+ * An Excel `[$-LCID]` / `[$symbol-LCID]` token pins the section's locale, and
206
+ * numfmt resolves it to that LCID's own table — ahead of the companion locale
207
+ * `ssfFormat` passes, so `[$-409]h:mm A/P` would still render `6:00 PM`.
208
+ * Re-point a known LCID or tag at its companion (`$-409` → `$-ampm_409`); an
209
+ * LCID numfmt does not know (`F400`) or one carrying calendar bits stays as
210
+ * written.
211
+ */
212
+ const shortAmPmLocaleToken = (token) => {
213
+ const dash = token.indexOf("-");
214
+ if (dash < 0)
215
+ return token;
216
+ const code = token.slice(dash + 1);
217
+ const wincode = Number.parseInt(code, 16);
218
+ if (Number.isFinite(wincode) && wincode & 0xff0000)
219
+ return token;
220
+ if (!getLocale(code))
221
+ return token;
222
+ return `${token.slice(0, dash + 1)}${getShortAmPmLocale(code)}`;
223
+ };
224
+ /**
225
+ * Does the pattern carry Excel's short `A/P` marker outside quoted literals,
226
+ * bracket directives and escapes? Returns the pattern with the marker
227
+ * upper-cased (Excel accepts `a/p` and renders it `A`/`P`; numfmt only
228
+ * tokenizes `A/P`) and its `[$-LCID]` tokens re-pointed at the companion
229
+ * `A`/`P` locale, or `undefined` when there is none.
230
+ */
231
+ export const withShortAmPmMarker = (pattern) => {
232
+ const chunks = [];
233
+ const localeChunks = [];
234
+ let found = false;
235
+ let inQuotes = false;
236
+ let inBrackets = false;
237
+ for (let i = 0; i < pattern.length; i++) {
238
+ const char = pattern[i];
239
+ if (inQuotes) {
240
+ inQuotes = char !== '"';
241
+ }
242
+ else if (inBrackets) {
243
+ inBrackets = char !== "]";
244
+ }
245
+ else if (char === '"') {
246
+ inQuotes = true;
247
+ }
248
+ else if (char === "[") {
249
+ const end = pattern[i + 1] === "$" ? pattern.indexOf("]", i) : -1;
250
+ if (end > 0) {
251
+ localeChunks.push(chunks.length);
252
+ chunks.push(pattern.slice(i + 1, end));
253
+ i = end;
254
+ continue;
255
+ }
256
+ inBrackets = true;
257
+ }
258
+ else if (char === "\\" || char === "_" || char === "*") {
259
+ chunks.push(char + (pattern[i + 1] ?? ""));
260
+ i++;
261
+ continue;
262
+ }
263
+ else if ((char === "A" || char === "a") &&
264
+ pattern[i + 1] === "/" &&
265
+ (pattern[i + 2] === "P" || pattern[i + 2] === "p")) {
266
+ chunks.push("A/P");
267
+ found = true;
268
+ i += 2;
269
+ continue;
270
+ }
271
+ chunks.push(char);
272
+ }
273
+ if (!found)
274
+ return undefined;
275
+ for (const index of localeChunks) {
276
+ chunks[index] = `[${shortAmPmLocaleToken(chunks[index])}]`;
277
+ }
278
+ return chunks.join("");
279
+ };
280
+ const shortAmPmPatternCache = new Map();
281
+ const DEFAULT_SEPARATORS = { decimal: ".", group: "," };
282
+ const separatorsFor = (locale) => {
283
+ if (!locale)
284
+ return DEFAULT_SEPARATORS;
285
+ const l10n = getLocale(locale);
286
+ if (!l10n)
287
+ return DEFAULT_SEPARATORS;
288
+ return {
289
+ decimal: l10n.decimal ?? ".",
290
+ group: l10n.group ?? ",",
291
+ };
292
+ };
180
293
  export const ssfFormat = (pattern, value, locale) => {
181
294
  try {
295
+ let shortAmPm = shortAmPmPatternCache.get(pattern);
296
+ if (shortAmPm === undefined && !shortAmPmPatternCache.has(pattern)) {
297
+ shortAmPm = withShortAmPmMarker(pattern);
298
+ shortAmPmPatternCache.set(pattern, shortAmPm);
299
+ }
300
+ const code = shortAmPm ?? pattern;
301
+ let patternLocale = localeForPattern(pattern, locale);
302
+ if (shortAmPm !== undefined && patternLocale) {
303
+ patternLocale = getShortAmPmLocale(patternLocale);
304
+ }
305
+ const options = { locale: patternLocale };
306
+ if (typeof value === "number") {
307
+ // Excel decides rounding, `General`, huge magnitudes and conditional
308
+ // sections from the 15-significant-digit decimal, not the binary
309
+ // double (#546 U8 / U21).
310
+ const pre = excelNumberPrePass(code, value, separatorsFor(locale), (section, sectionValue) => format(section, sectionValue, options));
311
+ if (pre.text !== undefined) {
312
+ return pre.text;
313
+ }
314
+ value = pre.value;
315
+ }
182
316
  const isNegativeElapsedTime = typeof value === "number" &&
183
317
  value < 0 &&
184
318
  isSingleSectionElapsedTimePattern(pattern);
185
- const formatted = format(pattern, isNegativeElapsedTime ? Math.abs(value) : value, { locale: localeForPattern(pattern, locale) });
319
+ const formatted = format(code, isNegativeElapsedTime ? Math.abs(value) : value, options);
186
320
  return isNegativeElapsedTime ? `-${formatted}` : formatted;
187
321
  }
188
322
  catch (err) {
@@ -9,6 +9,7 @@ export * from "./locale";
9
9
  export * from "./date";
10
10
  export * from "./log";
11
11
  export * from "./array";
12
+ export * from "./excel-number-format";
12
13
  export * from "./formatter";
13
14
  export * from "./memoize";
14
15
  export * from "./cell-data-helpers";
@@ -22,4 +23,5 @@ export * from "./data-validation";
22
23
  export * from "./content-hash";
23
24
  export * from "./shared-string-canonical";
24
25
  export * from "./keyboard";
26
+ export * from "./sheet-span";
25
27
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC;AACtB,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,QAAQ,CAAC;AACvB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC;AACtB,cAAc,SAAS,CAAC;AACxB,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,QAAQ,CAAC;AACvB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC"}
package/dist/esm/index.js CHANGED
@@ -9,6 +9,7 @@ export * from "./locale";
9
9
  export * from "./date";
10
10
  export * from "./log";
11
11
  export * from "./array";
12
+ export * from "./excel-number-format";
12
13
  export * from "./formatter";
13
14
  export * from "./memoize";
14
15
  export * from "./cell-data-helpers";
@@ -22,3 +23,4 @@ export * from "./data-validation";
22
23
  export * from "./content-hash";
23
24
  export * from "./shared-string-canonical";
24
25
  export * from "./keyboard";
26
+ export * from "./sheet-span";
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Sheets covered by a 3-D reference span `first:last` (`Sheet1:Sheet3!A1`):
3
+ * every name between the two endpoints in tab order, inclusive. Excel spans
4
+ * the tabs between the endpoints whichever one is written first and matches
5
+ * sheet names case-insensitively. Returns undefined when an endpoint is not a
6
+ * sheet (the caller reports #REF!).
7
+ * @param sheetNames sheet names in tab order
8
+ * @param first
9
+ * @param last
10
+ * @returns
11
+ */
12
+ export declare const resolveSheetSpan: (sheetNames: readonly string[], first: string, last: string) => string[] | undefined;
13
+ //# sourceMappingURL=sheet-span.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sheet-span.d.ts","sourceRoot":"","sources":["../../sheet-span.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,GAC3B,YAAY,SAAS,MAAM,EAAE,EAC7B,OAAO,MAAM,EACb,MAAM,MAAM,KACX,MAAM,EAAE,GAAG,SAQb,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Sheets covered by a 3-D reference span `first:last` (`Sheet1:Sheet3!A1`):
3
+ * every name between the two endpoints in tab order, inclusive. Excel spans
4
+ * the tabs between the endpoints whichever one is written first and matches
5
+ * sheet names case-insensitively. Returns undefined when an endpoint is not a
6
+ * sheet (the caller reports #REF!).
7
+ * @param sheetNames sheet names in tab order
8
+ * @param first
9
+ * @param last
10
+ * @returns
11
+ */
12
+ export const resolveSheetSpan = (sheetNames, first, last) => {
13
+ const lower = sheetNames.map((name) => name.toLowerCase());
14
+ const start = lower.indexOf(first.toLowerCase());
15
+ const end = lower.indexOf(last.toLowerCase());
16
+ if (start === -1 || end === -1) {
17
+ return undefined;
18
+ }
19
+ return sheetNames.slice(Math.min(start, end), Math.max(start, end) + 1);
20
+ };
@@ -1 +1 @@
1
- {"root":["../../array.ts","../../cell-data-helpers.ts","../../color.ts","../../colors.ts","../../conditional-format.ts","../../content-hash.ts","../../data-validation.ts","../../date.ts","../../dom.ts","../../formatter.ts","../../formula-refs.ts","../../formula.ts","../../index.ts","../../keyboard.ts","../../locale.ts","../../log.ts","../../memoize.ts","../../numbers.ts","../../selection.ts","../../shared-string-canonical.ts","../../sort.ts","../../string.ts","../../table-styles.ts","../../typeguards.ts","../../uuid.ts","../../vitest.config.ts"],"version":"5.8.3"}
1
+ {"root":["../../array.ts","../../cell-data-helpers.ts","../../color.ts","../../colors.ts","../../conditional-format.ts","../../content-hash.ts","../../data-validation.ts","../../date.ts","../../dom.ts","../../excel-number-format.ts","../../formatter.ts","../../formula-refs.ts","../../formula.ts","../../index.ts","../../keyboard.ts","../../locale.ts","../../log.ts","../../memoize.ts","../../numbers.ts","../../selection.ts","../../shared-string-canonical.ts","../../sheet-span.ts","../../sort.ts","../../string.ts","../../table-styles.ts","../../typeguards.ts","../../uuid.ts","../../vitest.config.ts"],"version":"5.8.3"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rowsncolumns/utils",
3
- "version": "9.2.39",
3
+ "version": "9.2.40",
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.30"
45
+ "@rowsncolumns/common-types": ">=5.2.31"
46
46
  },
47
47
  "devDependencies": {},
48
48
  "peerDependencies": {}