@forthtilliath/ts-kit 0.8.0 → 0.10.0
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.
- package/README.md +12 -0
- package/dist/array/createColumnStorage.d.ts +30 -0
- package/dist/array/createColumnStorage.d.ts.map +1 -0
- package/dist/array/createColumnStorage.js +51 -0
- package/dist/array/index.d.ts +2 -0
- package/dist/array/index.d.ts.map +1 -1
- package/dist/array/index.js +2 -0
- package/dist/array/sortUpcomingFirst.d.ts +30 -0
- package/dist/array/sortUpcomingFirst.d.ts.map +1 -0
- package/dist/array/sortUpcomingFirst.js +51 -0
- package/dist/files/index.d.ts +1 -0
- package/dist/files/index.d.ts.map +1 -1
- package/dist/files/index.js +1 -0
- package/dist/files/sanitizeFileName.d.ts +14 -0
- package/dist/files/sanitizeFileName.d.ts.map +1 -0
- package/dist/files/sanitizeFileName.js +18 -0
- package/dist/number/index.d.ts +1 -0
- package/dist/number/index.d.ts.map +1 -1
- package/dist/number/index.js +1 -0
- package/dist/number/padNumber.d.ts +15 -0
- package/dist/number/padNumber.d.ts.map +1 -0
- package/dist/number/padNumber.js +16 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,6 +36,10 @@ import { chunk } from "@forthtilliath/ts-kit/array/chunk";
|
|
|
36
36
|
### `array`
|
|
37
37
|
|
|
38
38
|
- `chunk(array, size)` — splits an array into chunks of a given size.
|
|
39
|
+
- `createColumnStorage(defaults)` — `{ serialize, deserialize }` pair to
|
|
40
|
+
persist a user-customized column list (order + visibility, e.g. in
|
|
41
|
+
`localStorage`). Only `key`/`visible` are stored; on read, unknown keys are
|
|
42
|
+
dropped, new columns appended, and invalid input falls back to `defaults`.
|
|
39
43
|
- `flattenDeep(array)` — recursively flattens nested arrays.
|
|
40
44
|
- `getMostRecentIds(rows, limit?)` — most recently occurring distinct ids
|
|
41
45
|
(`{ id, occurredAt }[]`), most recent first, deduplicated, limited
|
|
@@ -46,6 +50,10 @@ import { chunk } from "@forthtilliath/ts-kit/array/chunk";
|
|
|
46
50
|
- `rankByNameMatch(items, query, getName)` — ranks `items` by relevance to
|
|
47
51
|
`query`: earlier match position first, then shorter name — for a
|
|
48
52
|
search-as-you-type list.
|
|
53
|
+
- `sortUpcomingFirst(items, getDates, { now? })` — upcoming items first
|
|
54
|
+
(nearest first), then past ones (most recent first), undated last — for a
|
|
55
|
+
list of events. An item may carry several dates: it stays upcoming while
|
|
56
|
+
one is ahead (ranked by its next date), then is ranked by its latest date.
|
|
49
57
|
- `swapItems(items, i, j)` — copy of `items` with the elements at `i` and `j`
|
|
50
58
|
swapped (e.g. moving an item up/down) — a plain copy if either position is
|
|
51
59
|
out of bounds.
|
|
@@ -110,6 +118,8 @@ import { chunk } from "@forthtilliath/ts-kit/array/chunk";
|
|
|
110
118
|
via a `data:` URI.
|
|
111
119
|
- `downloadTextBlob(filename, text)` — same, but via a `Blob` + object URL
|
|
112
120
|
(better for larger content).
|
|
121
|
+
- `sanitizeFileName(name)` — replaces characters forbidden in file names
|
|
122
|
+
(`\ / : * ? " < > |`) with hyphens, collapses whitespace and trims.
|
|
113
123
|
|
|
114
124
|
### `id`
|
|
115
125
|
|
|
@@ -144,6 +154,8 @@ import { chunk } from "@forthtilliath/ts-kit/array/chunk";
|
|
|
144
154
|
- `formatDuration(ms)` — human-readable duration ("1d 1h 1m 1s").
|
|
145
155
|
- `formatCsvNumber(value, decimals?)` — formats a number with a comma
|
|
146
156
|
decimal separator (French-locale spreadsheets) instead of JS's dot.
|
|
157
|
+
- `padNumber(n, total, minWidth?)` — zero-pads `n` to the width of `total`
|
|
158
|
+
(at least `minWidth`, default 2) so numbered file names sort correctly.
|
|
147
159
|
|
|
148
160
|
### `object`
|
|
149
161
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Minimal shape of a toggleable, reorderable column. */
|
|
2
|
+
export interface StorableColumn {
|
|
3
|
+
key: string;
|
|
4
|
+
visible: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface ColumnStorage<T extends StorableColumn> {
|
|
7
|
+
serialize: (columns: readonly T[]) => string;
|
|
8
|
+
deserialize: (raw: string) => T[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Creates a (de)serializer for a user-customized list of columns (order and
|
|
12
|
+
* visibility), e.g. to persist a table layout in `localStorage`. Only `key`
|
|
13
|
+
* and `visible` are stored; other properties (labels…) always come from
|
|
14
|
+
* `defaults`. On read, unknown keys are dropped, columns missing from the
|
|
15
|
+
* stored value are appended in their default order, and invalid JSON falls
|
|
16
|
+
* back to `defaults`.
|
|
17
|
+
*
|
|
18
|
+
* @param defaults - The default columns, in their default order.
|
|
19
|
+
* @returns A `{ serialize, deserialize }` pair.
|
|
20
|
+
* @example
|
|
21
|
+
* const storage = createColumnStorage([
|
|
22
|
+
* { key: "name", label: "Name", visible: true },
|
|
23
|
+
* { key: "email", label: "Email", visible: true },
|
|
24
|
+
* ]);
|
|
25
|
+
* storage.deserialize('[{"key":"email","visible":false}]');
|
|
26
|
+
* // => [{ key: "email", label: "Email", visible: false },
|
|
27
|
+
* // { key: "name", label: "Name", visible: true }]
|
|
28
|
+
*/
|
|
29
|
+
export declare function createColumnStorage<T extends StorableColumn>(defaults: readonly T[]): ColumnStorage<T>;
|
|
30
|
+
//# sourceMappingURL=createColumnStorage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createColumnStorage.d.ts","sourceRoot":"","sources":["../../src/array/createColumnStorage.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,cAAc;IACrD,SAAS,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,KAAK,MAAM,CAAC;IAC7C,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC,EAAE,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,cAAc,EAC1D,QAAQ,EAAE,SAAS,CAAC,EAAE,GACrB,aAAa,CAAC,CAAC,CAAC,CAgClB"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a (de)serializer for a user-customized list of columns (order and
|
|
3
|
+
* visibility), e.g. to persist a table layout in `localStorage`. Only `key`
|
|
4
|
+
* and `visible` are stored; other properties (labels…) always come from
|
|
5
|
+
* `defaults`. On read, unknown keys are dropped, columns missing from the
|
|
6
|
+
* stored value are appended in their default order, and invalid JSON falls
|
|
7
|
+
* back to `defaults`.
|
|
8
|
+
*
|
|
9
|
+
* @param defaults - The default columns, in their default order.
|
|
10
|
+
* @returns A `{ serialize, deserialize }` pair.
|
|
11
|
+
* @example
|
|
12
|
+
* const storage = createColumnStorage([
|
|
13
|
+
* { key: "name", label: "Name", visible: true },
|
|
14
|
+
* { key: "email", label: "Email", visible: true },
|
|
15
|
+
* ]);
|
|
16
|
+
* storage.deserialize('[{"key":"email","visible":false}]');
|
|
17
|
+
* // => [{ key: "email", label: "Email", visible: false },
|
|
18
|
+
* // { key: "name", label: "Name", visible: true }]
|
|
19
|
+
*/
|
|
20
|
+
export function createColumnStorage(defaults) {
|
|
21
|
+
function serialize(columns) {
|
|
22
|
+
return JSON.stringify(columns.map(({ key, visible }) => ({ key, visible })));
|
|
23
|
+
}
|
|
24
|
+
function deserialize(raw) {
|
|
25
|
+
try {
|
|
26
|
+
const stored = JSON.parse(raw);
|
|
27
|
+
if (!Array.isArray(stored))
|
|
28
|
+
return [...defaults];
|
|
29
|
+
const result = [];
|
|
30
|
+
for (const entry of stored) {
|
|
31
|
+
const { key, visible } = (entry ?? {});
|
|
32
|
+
const column = defaults.find((c) => c.key === key);
|
|
33
|
+
if (!column || result.some((c) => c.key === column.key))
|
|
34
|
+
continue;
|
|
35
|
+
result.push({
|
|
36
|
+
...column,
|
|
37
|
+
visible: typeof visible === "boolean" ? visible : column.visible,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
for (const column of defaults) {
|
|
41
|
+
if (!result.some((c) => c.key === column.key))
|
|
42
|
+
result.push(column);
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return [...defaults];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return { serialize, deserialize };
|
|
51
|
+
}
|
package/dist/array/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export * from "./chunk.js";
|
|
2
|
+
export * from "./createColumnStorage.js";
|
|
2
3
|
export * from "./flattenDeep.js";
|
|
3
4
|
export * from "./getMostRecentIds.js";
|
|
4
5
|
export * from "./nextInCycle.js";
|
|
5
6
|
export * from "./rankByNameMatch.js";
|
|
7
|
+
export * from "./sortUpcomingFirst.js";
|
|
6
8
|
export * from "./swapItems.js";
|
|
7
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/array/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/array/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,0BAA0B,CAAC;AACzC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC"}
|
package/dist/array/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export * from "./chunk.js";
|
|
2
|
+
export * from "./createColumnStorage.js";
|
|
2
3
|
export * from "./flattenDeep.js";
|
|
3
4
|
export * from "./getMostRecentIds.js";
|
|
4
5
|
export * from "./nextInCycle.js";
|
|
5
6
|
export * from "./rankByNameMatch.js";
|
|
7
|
+
export * from "./sortUpcomingFirst.js";
|
|
6
8
|
export * from "./swapItems.js";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type DateInput = Date | string | number;
|
|
2
|
+
export interface SortUpcomingFirstOptions {
|
|
3
|
+
/** Instant separating upcoming from past items. Defaults to now. */
|
|
4
|
+
now?: DateInput;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Sorts items around a reference instant: upcoming items first, nearest
|
|
8
|
+
* first, then past items, most recent first — the usual order for a list of
|
|
9
|
+
* events (concerts, meetings, bookings…). Items without a valid date go last.
|
|
10
|
+
* Ties keep their original order.
|
|
11
|
+
*
|
|
12
|
+
* An item may carry several dates (e.g. an event with several sessions): it
|
|
13
|
+
* stays upcoming as long as one of its dates is still ahead and is ranked by
|
|
14
|
+
* its next date; once all are past, it is ranked by its latest date.
|
|
15
|
+
*
|
|
16
|
+
* @param items - The items to sort (left untouched).
|
|
17
|
+
* @param getDates - Reads the date(s) off an item (`Date`, ISO string or
|
|
18
|
+
* timestamp); `null`/`undefined` or invalid dates are ignored.
|
|
19
|
+
* @param options - `now`: the reference instant (a date equal to it counts as
|
|
20
|
+
* upcoming). Defaults to now.
|
|
21
|
+
* @returns A sorted copy of `items`.
|
|
22
|
+
* @example
|
|
23
|
+
* sortUpcomingFirst(
|
|
24
|
+
* [{ d: "2026-01-01" }, { d: "2026-12-01" }, { d: "2026-03-01" }, { d: "2026-10-01" }],
|
|
25
|
+
* (item) => item.d,
|
|
26
|
+
* { now: "2026-06-01" },
|
|
27
|
+
* ); // => 2026-10-01, 2026-12-01, 2026-03-01, 2026-01-01
|
|
28
|
+
*/
|
|
29
|
+
export declare function sortUpcomingFirst<T>(items: readonly T[], getDates: (item: T) => DateInput | readonly DateInput[] | null | undefined, { now }?: SortUpcomingFirstOptions): T[];
|
|
30
|
+
//# sourceMappingURL=sortUpcomingFirst.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sortUpcomingFirst.d.ts","sourceRoot":"","sources":["../../src/array/sortUpcomingFirst.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;AAE/C,MAAM,WAAW,wBAAwB;IACvC,oEAAoE;IACpE,GAAG,CAAC,EAAE,SAAS,CAAC;CACjB;AAiBD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,GAAG,SAAS,SAAS,EAAE,GAAG,IAAI,GAAG,SAAS,EAC1E,EAAE,GAAgB,EAAE,GAAE,wBAA6B,GAClD,CAAC,EAAE,CAaL"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Upcoming items first, then past items, then items without a usable date.
|
|
2
|
+
const UPCOMING = 0;
|
|
3
|
+
const PAST = 1;
|
|
4
|
+
const UNDATED = 2;
|
|
5
|
+
function toTimes(dates) {
|
|
6
|
+
if (dates == null)
|
|
7
|
+
return [];
|
|
8
|
+
const list = Array.isArray(dates)
|
|
9
|
+
? dates
|
|
10
|
+
: [dates];
|
|
11
|
+
return list
|
|
12
|
+
.map((date) => new Date(date).getTime())
|
|
13
|
+
.filter((time) => !Number.isNaN(time));
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Sorts items around a reference instant: upcoming items first, nearest
|
|
17
|
+
* first, then past items, most recent first — the usual order for a list of
|
|
18
|
+
* events (concerts, meetings, bookings…). Items without a valid date go last.
|
|
19
|
+
* Ties keep their original order.
|
|
20
|
+
*
|
|
21
|
+
* An item may carry several dates (e.g. an event with several sessions): it
|
|
22
|
+
* stays upcoming as long as one of its dates is still ahead and is ranked by
|
|
23
|
+
* its next date; once all are past, it is ranked by its latest date.
|
|
24
|
+
*
|
|
25
|
+
* @param items - The items to sort (left untouched).
|
|
26
|
+
* @param getDates - Reads the date(s) off an item (`Date`, ISO string or
|
|
27
|
+
* timestamp); `null`/`undefined` or invalid dates are ignored.
|
|
28
|
+
* @param options - `now`: the reference instant (a date equal to it counts as
|
|
29
|
+
* upcoming). Defaults to now.
|
|
30
|
+
* @returns A sorted copy of `items`.
|
|
31
|
+
* @example
|
|
32
|
+
* sortUpcomingFirst(
|
|
33
|
+
* [{ d: "2026-01-01" }, { d: "2026-12-01" }, { d: "2026-03-01" }, { d: "2026-10-01" }],
|
|
34
|
+
* (item) => item.d,
|
|
35
|
+
* { now: "2026-06-01" },
|
|
36
|
+
* ); // => 2026-10-01, 2026-12-01, 2026-03-01, 2026-01-01
|
|
37
|
+
*/
|
|
38
|
+
export function sortUpcomingFirst(items, getDates, { now = Date.now() } = {}) {
|
|
39
|
+
const nowTime = new Date(now).getTime();
|
|
40
|
+
const keyed = items.map((item) => {
|
|
41
|
+
const times = toTimes(getDates(item));
|
|
42
|
+
const upcoming = times.filter((time) => time >= nowTime);
|
|
43
|
+
if (upcoming.length > 0)
|
|
44
|
+
return { item, group: UPCOMING, key: Math.min(...upcoming) };
|
|
45
|
+
if (times.length > 0)
|
|
46
|
+
return { item, group: PAST, key: -Math.max(...times) };
|
|
47
|
+
return { item, group: UNDATED, key: 0 };
|
|
48
|
+
});
|
|
49
|
+
keyed.sort((a, b) => a.group - b.group || a.key - b.key);
|
|
50
|
+
return keyed.map((k) => k.item);
|
|
51
|
+
}
|
package/dist/files/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/files/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/files/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC"}
|
package/dist/files/index.js
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Makes a string safe to use as a file name: characters forbidden on Windows
|
|
3
|
+
* (`\ / : * ? " < > |`) become hyphens, whitespace runs collapse into a single
|
|
4
|
+
* space and the result is trimmed. Also prevents a `/` from creating a
|
|
5
|
+
* sub-folder inside a ZIP archive.
|
|
6
|
+
*
|
|
7
|
+
* @param name - The raw file name.
|
|
8
|
+
* @returns The sanitized file name.
|
|
9
|
+
* @example
|
|
10
|
+
* sanitizeFileName("AC/DC: Live?"); // => "AC-DC- Live-"
|
|
11
|
+
* sanitizeFileName(" My file "); // => "My file"
|
|
12
|
+
*/
|
|
13
|
+
export declare function sanitizeFileName(name: string): string;
|
|
14
|
+
//# sourceMappingURL=sanitizeFileName.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizeFileName.d.ts","sourceRoot":"","sources":["../../src/files/sanitizeFileName.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKrD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Makes a string safe to use as a file name: characters forbidden on Windows
|
|
3
|
+
* (`\ / : * ? " < > |`) become hyphens, whitespace runs collapse into a single
|
|
4
|
+
* space and the result is trimmed. Also prevents a `/` from creating a
|
|
5
|
+
* sub-folder inside a ZIP archive.
|
|
6
|
+
*
|
|
7
|
+
* @param name - The raw file name.
|
|
8
|
+
* @returns The sanitized file name.
|
|
9
|
+
* @example
|
|
10
|
+
* sanitizeFileName("AC/DC: Live?"); // => "AC-DC- Live-"
|
|
11
|
+
* sanitizeFileName(" My file "); // => "My file"
|
|
12
|
+
*/
|
|
13
|
+
export function sanitizeFileName(name) {
|
|
14
|
+
return name
|
|
15
|
+
.replace(/[\\/:*?"<>|]/g, "-")
|
|
16
|
+
.replace(/\s+/g, " ")
|
|
17
|
+
.trim();
|
|
18
|
+
}
|
package/dist/number/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/number/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/number/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC"}
|
package/dist/number/index.js
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Left-pads a number with zeros so that file names sort correctly in a file
|
|
3
|
+
* explorer (`"01"`, `"02"`, …, `"10"`). The width adapts to `total` (3 digits
|
|
4
|
+
* from 100 items on) and never goes below `minWidth`.
|
|
5
|
+
*
|
|
6
|
+
* @param n - The number to pad.
|
|
7
|
+
* @param total - The total count, used to compute the width.
|
|
8
|
+
* @param minWidth - The minimum width (default: 2).
|
|
9
|
+
* @returns The zero-padded number.
|
|
10
|
+
* @example
|
|
11
|
+
* padNumber(1, 12); // => "01"
|
|
12
|
+
* padNumber(7, 120); // => "007"
|
|
13
|
+
*/
|
|
14
|
+
export declare function padNumber(n: number, total: number, minWidth?: number): string;
|
|
15
|
+
//# sourceMappingURL=padNumber.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"padNumber.d.ts","sourceRoot":"","sources":["../../src/number/padNumber.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAI,GAAG,MAAM,CAExE"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Left-pads a number with zeros so that file names sort correctly in a file
|
|
3
|
+
* explorer (`"01"`, `"02"`, …, `"10"`). The width adapts to `total` (3 digits
|
|
4
|
+
* from 100 items on) and never goes below `minWidth`.
|
|
5
|
+
*
|
|
6
|
+
* @param n - The number to pad.
|
|
7
|
+
* @param total - The total count, used to compute the width.
|
|
8
|
+
* @param minWidth - The minimum width (default: 2).
|
|
9
|
+
* @returns The zero-padded number.
|
|
10
|
+
* @example
|
|
11
|
+
* padNumber(1, 12); // => "01"
|
|
12
|
+
* padNumber(7, 120); // => "007"
|
|
13
|
+
*/
|
|
14
|
+
export function padNumber(n, total, minWidth = 2) {
|
|
15
|
+
return String(n).padStart(Math.max(minWidth, String(total).length), "0");
|
|
16
|
+
}
|