shelving 1.89.5 → 1.90.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/package.json +1 -1
- package/state/DataState.d.ts +15 -3
- package/state/DataState.js +28 -1
- package/state/DictionaryState.d.ts +7 -1
- package/state/DictionaryState.js +15 -1
- package/util/duration.d.ts +4 -4
- package/util/duration.js +8 -6
- package/util/number.d.ts +18 -35
- package/util/number.js +17 -57
- package/util/units.d.ts +4 -10
- package/util/units.js +6 -11
package/package.json
CHANGED
package/state/DataState.d.ts
CHANGED
|
@@ -1,21 +1,33 @@
|
|
|
1
|
-
import { Data,
|
|
2
|
-
import { Transformers } from "../util/transform.js";
|
|
1
|
+
import { Data, DataKey } from "../util/data.js";
|
|
2
|
+
import { Transformers, Transformer } from "../util/transform.js";
|
|
3
3
|
import { State } from "./State.js";
|
|
4
4
|
/** State that stores a data object and has additional methods to help with that. */
|
|
5
5
|
export declare class DataState<T extends Data> extends State<T> {
|
|
6
6
|
/** Get the data value of this state. */
|
|
7
7
|
get data(): T;
|
|
8
|
+
/** Update a single named prop in this data. */
|
|
9
|
+
getProp<K extends DataKey<T>>(name: K): T[K];
|
|
10
|
+
/** Update a single named prop in this data. */
|
|
11
|
+
setProp<K extends DataKey<T>>(name: K, value: T[K]): void;
|
|
12
|
+
/** Update a single named prop in this data. */
|
|
13
|
+
updateProp<K extends DataKey<T>>(name: K, update: Transformer<T[K], T[K]>): void;
|
|
8
14
|
/** Update several props in this data. */
|
|
9
15
|
update(updates: Transformers<T>): void;
|
|
10
16
|
}
|
|
11
17
|
/** State that stores an optional data object and has additional methods to help with that. */
|
|
12
|
-
export declare class OptionalDataState<T extends Data> extends State<
|
|
18
|
+
export declare class OptionalDataState<T extends Data> extends State<T | null> {
|
|
13
19
|
/** Get current data value of this state (or throw `Promise` that resolves to the next required value). */
|
|
14
20
|
get data(): T;
|
|
15
21
|
/** Does the data exist or not? */
|
|
16
22
|
get exists(): boolean;
|
|
17
23
|
/** Update several props in this data. */
|
|
18
24
|
update(updates: Transformers<T>): void;
|
|
25
|
+
/** Update a single named prop in this data. */
|
|
26
|
+
getProp<K extends DataKey<T>>(name: K): T[K];
|
|
27
|
+
/** Update a single named prop in this data. */
|
|
28
|
+
setProp<K extends DataKey<T>>(name: K, value: T[K]): void;
|
|
29
|
+
/** Update a single named prop in this data. */
|
|
30
|
+
updateProp<K extends DataKey<T>>(name: K, update: Transformer<T[K], T[K]>): void;
|
|
19
31
|
/** Set the data to `null`. */
|
|
20
32
|
unset(): void;
|
|
21
33
|
}
|
package/state/DataState.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { withProp } from "../util/object.js";
|
|
1
2
|
import { getData } from "../util/data.js";
|
|
2
|
-
import { transformObject } from "../util/transform.js";
|
|
3
|
+
import { transform, transformObject } from "../util/transform.js";
|
|
3
4
|
import { State } from "./State.js";
|
|
4
5
|
/** State that stores a data object and has additional methods to help with that. */
|
|
5
6
|
export class DataState extends State {
|
|
@@ -7,6 +8,19 @@ export class DataState extends State {
|
|
|
7
8
|
get data() {
|
|
8
9
|
return this.value;
|
|
9
10
|
}
|
|
11
|
+
/** Update a single named prop in this data. */
|
|
12
|
+
getProp(name) {
|
|
13
|
+
return this.data[name];
|
|
14
|
+
}
|
|
15
|
+
/** Update a single named prop in this data. */
|
|
16
|
+
setProp(name, value) {
|
|
17
|
+
this.set(withProp(this.data, name, value));
|
|
18
|
+
}
|
|
19
|
+
/** Update a single named prop in this data. */
|
|
20
|
+
updateProp(name, update) {
|
|
21
|
+
const data = this.data;
|
|
22
|
+
this.set(withProp(data, name, transform(data[name], update)));
|
|
23
|
+
}
|
|
10
24
|
/** Update several props in this data. */
|
|
11
25
|
update(updates) {
|
|
12
26
|
this.set(transformObject(this.data, updates));
|
|
@@ -26,6 +40,19 @@ export class OptionalDataState extends State {
|
|
|
26
40
|
update(updates) {
|
|
27
41
|
this.set(transformObject(this.data, updates));
|
|
28
42
|
}
|
|
43
|
+
/** Update a single named prop in this data. */
|
|
44
|
+
getProp(name) {
|
|
45
|
+
return this.data[name];
|
|
46
|
+
}
|
|
47
|
+
/** Update a single named prop in this data. */
|
|
48
|
+
setProp(name, value) {
|
|
49
|
+
this.set(withProp(this.data, name, value));
|
|
50
|
+
}
|
|
51
|
+
/** Update a single named prop in this data. */
|
|
52
|
+
updateProp(name, update) {
|
|
53
|
+
const data = this.data;
|
|
54
|
+
this.set(withProp(data, name, transform(data[name], update)));
|
|
55
|
+
}
|
|
29
56
|
/** Set the data to `null`. */
|
|
30
57
|
unset() {
|
|
31
58
|
this.set(null);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DictionaryItem, ImmutableDictionary } from "../util/dictionary.js";
|
|
2
|
-
import { Transformers } from "../util/transform.js";
|
|
2
|
+
import { Transformer, Transformers } from "../util/transform.js";
|
|
3
3
|
import { State } from "./State.js";
|
|
4
4
|
/** State that stores a dictionary object and has additional methods to help with that. */
|
|
5
5
|
export declare class DictionaryState<T> extends State<ImmutableDictionary<T>> implements Iterable<DictionaryItem<T>> {
|
|
@@ -10,6 +10,12 @@ export declare class DictionaryState<T> extends State<ImmutableDictionary<T>> im
|
|
|
10
10
|
update(updates: Transformers<ImmutableDictionary<T>>): void;
|
|
11
11
|
/** Remove a named entry from this object. */
|
|
12
12
|
delete(...keys: string[]): void;
|
|
13
|
+
/** Update a single named prop in this data. */
|
|
14
|
+
getItem(name: string): T | undefined;
|
|
15
|
+
/** Update a single named prop in this data. */
|
|
16
|
+
setItem(name: string, value: T): void;
|
|
17
|
+
/** Update a single named prop in this data. */
|
|
18
|
+
updateItem(name: string, update: Transformer<T | undefined, T>): void;
|
|
13
19
|
/** Iterate over the entries of the object. */
|
|
14
20
|
[Symbol.iterator](): Iterator<DictionaryItem<T>>;
|
|
15
21
|
}
|
package/state/DictionaryState.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { withProp } from "../util/object.js";
|
|
1
2
|
import { omitDictionaryItems } from "../util/dictionary.js";
|
|
2
|
-
import { transformDictionary } from "../util/transform.js";
|
|
3
|
+
import { transform, transformDictionary } from "../util/transform.js";
|
|
3
4
|
import { State } from "./State.js";
|
|
4
5
|
/** State that stores a dictionary object and has additional methods to help with that. */
|
|
5
6
|
export class DictionaryState extends State {
|
|
@@ -18,6 +19,19 @@ export class DictionaryState extends State {
|
|
|
18
19
|
delete(...keys) {
|
|
19
20
|
this.set(omitDictionaryItems(this.value, ...keys));
|
|
20
21
|
}
|
|
22
|
+
/** Update a single named prop in this data. */
|
|
23
|
+
getItem(name) {
|
|
24
|
+
return this.value[name];
|
|
25
|
+
}
|
|
26
|
+
/** Update a single named prop in this data. */
|
|
27
|
+
setItem(name, value) {
|
|
28
|
+
this.set(withProp(this.value, name, value));
|
|
29
|
+
}
|
|
30
|
+
/** Update a single named prop in this data. */
|
|
31
|
+
updateItem(name, update) {
|
|
32
|
+
const value = this.value;
|
|
33
|
+
this.set(withProp(value, name, transform(value[name], update)));
|
|
34
|
+
}
|
|
21
35
|
/** Iterate over the entries of the object. */
|
|
22
36
|
[Symbol.iterator]() {
|
|
23
37
|
return Object.entries(this.value)[Symbol.iterator]();
|
package/util/duration.d.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { PossibleDate } from "./date.js";
|
|
2
2
|
/** Format a full format of a duration of time using the most reasonable units e.g. `5 years` or `1 week` or `4 minutes` or `12 milliseconds`. */
|
|
3
|
-
export declare function
|
|
3
|
+
export declare function pluralizeDuration(ms: number): string;
|
|
4
4
|
/** Format a description of a duration of time using the most reasonable units e.g. `5y` or `4m` or `12ms`. */
|
|
5
5
|
export declare function formatDuration(ms: number): string;
|
|
6
6
|
/** Full when a date happens/happened, e.g. `in 10 days` or `2 hours ago` */
|
|
7
|
-
export declare const
|
|
7
|
+
export declare const pluralizeWhen: (target: PossibleDate, current?: PossibleDate) => string;
|
|
8
8
|
/** Compact when a date happens/happened, e.g. `in 10d` or `2h ago` or `in 1w` */
|
|
9
9
|
export declare const formatWhen: (target: PossibleDate, current?: PossibleDate) => string;
|
|
10
10
|
/** Full when a date happens, e.g. `10 days` or `2 hours` or `-1 week` */
|
|
11
|
-
export declare const
|
|
11
|
+
export declare const pluralizeUntil: (target: PossibleDate, current?: PossibleDate) => string;
|
|
12
12
|
/** Compact when a date happens, e.g. `10d` or `2h` or `-1w` */
|
|
13
13
|
export declare const formatUntil: (target: PossibleDate, current?: PossibleDate) => string;
|
|
14
14
|
/** Full when a date happened, e.g. `10 days` or `2 hours` or `-1 week` */
|
|
15
|
-
export declare const
|
|
15
|
+
export declare const pluralizeAgo: (target: PossibleDate, current?: PossibleDate) => string;
|
|
16
16
|
/** Compact when a date will happen, e.g. `10d` or `2h` or `-1w` */
|
|
17
17
|
export declare const formatAgo: (target: PossibleDate, current?: PossibleDate) => string;
|
package/util/duration.js
CHANGED
|
@@ -20,15 +20,17 @@ function _getTimeUnit(ms) {
|
|
|
20
20
|
return TIME_UNITS.unit("second");
|
|
21
21
|
return TIME_UNITS.unit("millisecond");
|
|
22
22
|
}
|
|
23
|
+
/** Default number options for duration (no decimal places and rounding down). */
|
|
24
|
+
const NUMBER_OPTIONS = { maximumFractionDigits: 0, roundingMode: "trunc" };
|
|
23
25
|
/** Format a full format of a duration of time using the most reasonable units e.g. `5 years` or `1 week` or `4 minutes` or `12 milliseconds`. */
|
|
24
|
-
export function
|
|
26
|
+
export function pluralizeDuration(ms) {
|
|
25
27
|
const unit = _getTimeUnit(ms);
|
|
26
|
-
return unit.
|
|
28
|
+
return unit.pluralize(unit.from(ms), NUMBER_OPTIONS);
|
|
27
29
|
}
|
|
28
30
|
/** Format a description of a duration of time using the most reasonable units e.g. `5y` or `4m` or `12ms`. */
|
|
29
31
|
export function formatDuration(ms) {
|
|
30
32
|
const unit = _getTimeUnit(ms);
|
|
31
|
-
return unit.format(unit.from(ms),
|
|
33
|
+
return unit.format(unit.from(ms), NUMBER_OPTIONS);
|
|
32
34
|
}
|
|
33
35
|
/** format when a data happens/happened. */
|
|
34
36
|
function _formatWhen(formatter, target, current) {
|
|
@@ -38,14 +40,14 @@ function _formatWhen(formatter, target, current) {
|
|
|
38
40
|
return abs < 10 * SECOND ? "just now" : ms > 0 ? `in ${duration}` : `${duration} ago`;
|
|
39
41
|
}
|
|
40
42
|
/** Full when a date happens/happened, e.g. `in 10 days` or `2 hours ago` */
|
|
41
|
-
export const
|
|
43
|
+
export const pluralizeWhen = (target, current) => _formatWhen(pluralizeDuration, target, current);
|
|
42
44
|
/** Compact when a date happens/happened, e.g. `in 10d` or `2h ago` or `in 1w` */
|
|
43
45
|
export const formatWhen = (target, current) => _formatWhen(formatDuration, target, current);
|
|
44
46
|
/** Full when a date happens, e.g. `10 days` or `2 hours` or `-1 week` */
|
|
45
|
-
export const
|
|
47
|
+
export const pluralizeUntil = (target, current) => pluralizeDuration(getDuration(target, current));
|
|
46
48
|
/** Compact when a date happens, e.g. `10d` or `2h` or `-1w` */
|
|
47
49
|
export const formatUntil = (target, current) => formatDuration(getDuration(target, current));
|
|
48
50
|
/** Full when a date happened, e.g. `10 days` or `2 hours` or `-1 week` */
|
|
49
|
-
export const
|
|
51
|
+
export const pluralizeAgo = (target, current) => pluralizeDuration(getDuration(current, target));
|
|
50
52
|
/** Compact when a date will happen, e.g. `10d` or `2h` or `-1w` */
|
|
51
53
|
export const formatAgo = (target, current) => formatDuration(getDuration(current, target));
|
package/util/number.d.ts
CHANGED
|
@@ -59,8 +59,7 @@ export declare const roundNumber: (num: number, precision?: number) => number;
|
|
|
59
59
|
* - Better than `Math.trunc()` because it allows a `precision` argument.
|
|
60
60
|
*
|
|
61
61
|
* @param num The number to truncate.
|
|
62
|
-
* @param precision Maximum number of digits shown after the decimal point (defaults to
|
|
63
|
-
*
|
|
62
|
+
* @param precision Maximum number of digits shown after the decimal point (defaults to 0).
|
|
64
63
|
* @returns The number truncated to the specified precision.
|
|
65
64
|
*/
|
|
66
65
|
export declare const truncateNumber: (num: number, precision?: number) => number;
|
|
@@ -68,51 +67,35 @@ export declare const truncateNumber: (num: number, precision?: number) => number
|
|
|
68
67
|
export declare function boundNumber(num: number, min: number, max: number): number;
|
|
69
68
|
/** Wrap a number so it fits between two values. */
|
|
70
69
|
export declare function wrapNumber(num: number, min: number, max: number): number;
|
|
70
|
+
/** Options for `formatNumber()` and `formatRange()`. */
|
|
71
|
+
export interface NumberOptions extends Intl.NumberFormatOptions {
|
|
72
|
+
roundingMode?: "ceil" | "floor" | "expand" | "trunc" | "halfCeil" | "halfFloor" | "halfExpand" | "halfTrunc" | "halfEven" | undefined;
|
|
73
|
+
roundingPriority?: "morePrecision" | "lessPrecision" | undefined;
|
|
74
|
+
}
|
|
71
75
|
/** Format a number (based on the user's browser language settings). */
|
|
72
|
-
export declare function formatNumber(num: number,
|
|
76
|
+
export declare function formatNumber(num: number, options?: NumberOptions): string;
|
|
73
77
|
/** Format a number range (based on the user's browser language settings). */
|
|
74
|
-
export declare function formatRange(min: number, max: number,
|
|
78
|
+
export declare function formatRange(min: number, max: number, options?: NumberOptions): string;
|
|
75
79
|
/** Format a number with a short suffix, e.g. `1,000 kg` */
|
|
76
|
-
export declare const formatQuantity: (num: number, abbr: string,
|
|
80
|
+
export declare const formatQuantity: (num: number, abbr: string, options?: NumberOptions) => string;
|
|
77
81
|
/** Format a number with a longer full-word suffix. */
|
|
78
|
-
export declare function
|
|
82
|
+
export declare function pluralizeQuantity(num: number, singular: string, plural: string, options?: NumberOptions): string;
|
|
79
83
|
/**
|
|
80
|
-
*
|
|
81
|
-
* - Improves glanceability.
|
|
82
|
-
* - Keeps number of characters under five if possible.
|
|
83
|
-
*
|
|
84
|
-
* - Numbers over 100 trillion: `157T`
|
|
85
|
-
* - Numbers over 10 trillion: `15.7T` (includes zero e.g. `40.0T` for consistency).
|
|
86
|
-
* - Numbers over 1 trillion: `1.57T` (includes zeros e.g. `4.00T` for consistency).
|
|
87
|
-
* - Numbers over 100 billion: `157B`
|
|
88
|
-
* - Numbers over 10 billion: `15.7B` (includes zero e.g. `40.0B` for consistency).
|
|
89
|
-
* - Numbers over 1 billion: `1.57B` (includes zeros e.g. `4.00B` for consistency).
|
|
90
|
-
* - Numbers over 100 million: `157M`
|
|
91
|
-
* - Numbers over 10 million: `15.7M` (includes zero e.g. `40.0M` for consistency).
|
|
92
|
-
* - Numbers over 1 million: `1.57M` (includes zeros e.g. `4.00M` for consistency).
|
|
93
|
-
* - Numbers over 100,000: `157K`
|
|
94
|
-
* - Numbers over 10,000: `15.7K` (includes zero e.g. `14.0K` for consistency).
|
|
95
|
-
* - Smaller numbers: `1570` and `157` and `15.7` and `1.6`
|
|
96
|
-
*
|
|
97
|
-
* @param num The number to format.
|
|
98
|
-
* @param precision Maximum number of digits shown after the decimal point (defaults to 10, only used for numbers under 10,000).
|
|
84
|
+
* Get a number as a percentage of another number.
|
|
99
85
|
*
|
|
100
|
-
* @
|
|
86
|
+
* @param numerator Number representing the amount of progress.
|
|
87
|
+
* @param denumerator The number representing the whole amount.
|
|
101
88
|
*/
|
|
102
|
-
export declare
|
|
103
|
-
/** Cram a number with a short suffix, e.g. `1.02M kg` */
|
|
104
|
-
export declare const cramQuantity: (num: number, suffix: string) => string;
|
|
105
|
-
/** Cram a number with a longer full-word suffix. */
|
|
106
|
-
export declare function cramFullQuantity(num: number, singular: string, plural: string): string;
|
|
89
|
+
export declare const getPercent: (numerator: number, denumerator: number) => number;
|
|
107
90
|
/**
|
|
108
|
-
*
|
|
91
|
+
* Format a percentage (combines `getPercent()` and `formatQuantity()` for convenience).
|
|
92
|
+
* - Defaults to showing no decimal places.
|
|
93
|
+
* - Defaults to rounding closer to zero (so that 99.99% is shown as 99%).
|
|
109
94
|
*
|
|
110
95
|
* @param numerator Number representing the amount of progress.
|
|
111
96
|
* @param denumerator The number representing the whole amount.
|
|
112
97
|
*/
|
|
113
|
-
export declare const
|
|
114
|
-
/** Format a percentage (combines `getPercent()` and `formatQuantity()` for convenience). */
|
|
115
|
-
export declare const formatPercent: (numerator: number, denumerator: number, precision?: number) => string;
|
|
98
|
+
export declare const formatPercent: (numerator: number, denumerator: number, options?: NumberOptions) => string;
|
|
116
99
|
/** Sum an iterable set of numbers and return the total. */
|
|
117
100
|
export declare function sumNumbers(nums: Iterable<number>): number;
|
|
118
101
|
/** Find the number that's closest to a target in an iterable set of numbers. */
|
package/util/number.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AssertionError } from "../error/AssertionError.js";
|
|
2
|
-
import {
|
|
2
|
+
import { NNBSP } from "./constants.js";
|
|
3
3
|
/** Is a value a number? */
|
|
4
4
|
export const isNumber = (value) => typeof value === "number";
|
|
5
5
|
/** Assert that a value is a number. */
|
|
@@ -89,8 +89,7 @@ export const roundNumber = (num, precision = 0) => Math.round(num * 10 ** precis
|
|
|
89
89
|
* - Better than `Math.trunc()` because it allows a `precision` argument.
|
|
90
90
|
*
|
|
91
91
|
* @param num The number to truncate.
|
|
92
|
-
* @param precision Maximum number of digits shown after the decimal point (defaults to
|
|
93
|
-
*
|
|
92
|
+
* @param precision Maximum number of digits shown after the decimal point (defaults to 0).
|
|
94
93
|
* @returns The number truncated to the specified precision.
|
|
95
94
|
*/
|
|
96
95
|
export const truncateNumber = (num, precision = 0) => Math.trunc(num * 10 ** precision) / 10 ** precision;
|
|
@@ -109,79 +108,40 @@ export function wrapNumber(num, min, max) {
|
|
|
109
108
|
return num;
|
|
110
109
|
}
|
|
111
110
|
/** Format a number (based on the user's browser language settings). */
|
|
112
|
-
export function formatNumber(num,
|
|
111
|
+
export function formatNumber(num, options = { maximumFractionDigits: 2 }) {
|
|
113
112
|
if (Number.isNaN(num))
|
|
114
113
|
return "None";
|
|
115
114
|
if (!Number.isFinite(num))
|
|
116
115
|
return "∞";
|
|
117
|
-
return new Intl.NumberFormat(undefined,
|
|
116
|
+
return new Intl.NumberFormat(undefined, options).format(num);
|
|
118
117
|
}
|
|
119
118
|
/** Format a number range (based on the user's browser language settings). */
|
|
120
|
-
export function formatRange(min, max,
|
|
121
|
-
return `${formatNumber(min,
|
|
119
|
+
export function formatRange(min, max, options) {
|
|
120
|
+
return `${formatNumber(min, options)}–${formatNumber(max, options)}`;
|
|
122
121
|
}
|
|
123
122
|
/** Format a number with a short suffix, e.g. `1,000 kg` */
|
|
124
|
-
export const formatQuantity = (num, abbr,
|
|
123
|
+
export const formatQuantity = (num, abbr, options) => `${formatNumber(num, options)}${NNBSP}${abbr}`;
|
|
125
124
|
/** Format a number with a longer full-word suffix. */
|
|
126
|
-
export function
|
|
127
|
-
const qty = formatNumber(num,
|
|
125
|
+
export function pluralizeQuantity(num, singular, plural, options) {
|
|
126
|
+
const qty = formatNumber(num, options);
|
|
128
127
|
return `${qty} ${qty === "1" ? singular : plural}`;
|
|
129
128
|
}
|
|
130
129
|
/**
|
|
131
|
-
*
|
|
132
|
-
* - Improves glanceability.
|
|
133
|
-
* - Keeps number of characters under five if possible.
|
|
134
|
-
*
|
|
135
|
-
* - Numbers over 100 trillion: `157T`
|
|
136
|
-
* - Numbers over 10 trillion: `15.7T` (includes zero e.g. `40.0T` for consistency).
|
|
137
|
-
* - Numbers over 1 trillion: `1.57T` (includes zeros e.g. `4.00T` for consistency).
|
|
138
|
-
* - Numbers over 100 billion: `157B`
|
|
139
|
-
* - Numbers over 10 billion: `15.7B` (includes zero e.g. `40.0B` for consistency).
|
|
140
|
-
* - Numbers over 1 billion: `1.57B` (includes zeros e.g. `4.00B` for consistency).
|
|
141
|
-
* - Numbers over 100 million: `157M`
|
|
142
|
-
* - Numbers over 10 million: `15.7M` (includes zero e.g. `40.0M` for consistency).
|
|
143
|
-
* - Numbers over 1 million: `1.57M` (includes zeros e.g. `4.00M` for consistency).
|
|
144
|
-
* - Numbers over 100,000: `157K`
|
|
145
|
-
* - Numbers over 10,000: `15.7K` (includes zero e.g. `14.0K` for consistency).
|
|
146
|
-
* - Smaller numbers: `1570` and `157` and `15.7` and `1.6`
|
|
147
|
-
*
|
|
148
|
-
* @param num The number to format.
|
|
149
|
-
* @param precision Maximum number of digits shown after the decimal point (defaults to 10, only used for numbers under 10,000).
|
|
130
|
+
* Get a number as a percentage of another number.
|
|
150
131
|
*
|
|
151
|
-
* @
|
|
132
|
+
* @param numerator Number representing the amount of progress.
|
|
133
|
+
* @param denumerator The number representing the whole amount.
|
|
152
134
|
*/
|
|
153
|
-
export
|
|
154
|
-
const abs = Math.abs(num);
|
|
155
|
-
if (abs >= TRILLION)
|
|
156
|
-
return `${_significance(num / TRILLION)}T`;
|
|
157
|
-
if (abs >= BILLION)
|
|
158
|
-
return `${_significance(num / BILLION)}B`;
|
|
159
|
-
if (abs >= MILLION)
|
|
160
|
-
return `${_significance(num / MILLION)}M`;
|
|
161
|
-
if (abs >= TEN_THOUSAND)
|
|
162
|
-
return `${_significance(num / THOUSAND)}K`;
|
|
163
|
-
return truncateNumber(num, 2).toString();
|
|
164
|
-
}
|
|
165
|
-
function _significance(num) {
|
|
166
|
-
const digits = num >= 100 ? 0 : num >= 10 ? 1 : 2;
|
|
167
|
-
return truncateNumber(num, digits).toFixed(digits);
|
|
168
|
-
}
|
|
169
|
-
/** Cram a number with a short suffix, e.g. `1.02M kg` */
|
|
170
|
-
export const cramQuantity = (num, suffix) => `${cramNumber(num)}${NNBSP}${suffix}`;
|
|
171
|
-
/** Cram a number with a longer full-word suffix. */
|
|
172
|
-
export function cramFullQuantity(num, singular, plural) {
|
|
173
|
-
const qty = cramNumber(num);
|
|
174
|
-
return `${qty} ${qty === "1" ? singular : plural}`;
|
|
175
|
-
}
|
|
135
|
+
export const getPercent = (numerator, denumerator) => Math.max(0, Math.min(100, (100 / denumerator) * numerator));
|
|
176
136
|
/**
|
|
177
|
-
*
|
|
137
|
+
* Format a percentage (combines `getPercent()` and `formatQuantity()` for convenience).
|
|
138
|
+
* - Defaults to showing no decimal places.
|
|
139
|
+
* - Defaults to rounding closer to zero (so that 99.99% is shown as 99%).
|
|
178
140
|
*
|
|
179
141
|
* @param numerator Number representing the amount of progress.
|
|
180
142
|
* @param denumerator The number representing the whole amount.
|
|
181
143
|
*/
|
|
182
|
-
export const
|
|
183
|
-
/** Format a percentage (combines `getPercent()` and `formatQuantity()` for convenience). */
|
|
184
|
-
export const formatPercent = (numerator, denumerator, precision) => formatQuantity(getPercent(numerator, denumerator), "%", precision);
|
|
144
|
+
export const formatPercent = (numerator, denumerator, options) => formatQuantity(getPercent(numerator, denumerator), "%", { maximumFractionDigits: 0, roundingMode: "trunc", ...options });
|
|
185
145
|
/** Sum an iterable set of numbers and return the total. */
|
|
186
146
|
export function sumNumbers(nums) {
|
|
187
147
|
let sum = 0;
|
package/util/units.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { ImmutableObject } from "./object.js";
|
|
2
2
|
import { MapKey, ImmutableMap } from "./map.js";
|
|
3
|
+
import { NumberOptions } from "./number.js";
|
|
3
4
|
/** Conversion from one unit to another (either an amount to multiple by, or a function to convert). */
|
|
4
5
|
type Conversion = number | ((num: number) => number);
|
|
5
6
|
/** Set of possible conversions for a set of items. */
|
|
6
7
|
type Conversions<T extends string> = {
|
|
7
8
|
readonly [K in T]?: Conversion;
|
|
8
9
|
};
|
|
9
|
-
/** Get a `Unit` instance from a `UnitList` instance */
|
|
10
10
|
type UnitProps<T extends string> = {
|
|
11
11
|
/** Short abbreviation for this unit, e.g. `km` (defaults to first letter of `id`). */
|
|
12
12
|
readonly abbr?: string;
|
|
@@ -14,8 +14,6 @@ type UnitProps<T extends string> = {
|
|
|
14
14
|
readonly singular?: string;
|
|
15
15
|
/** Plural name for this unit, e.g. `kilometers` (defaults to `id`). */
|
|
16
16
|
readonly plural?: string;
|
|
17
|
-
/** Default precision for this unit (defaults to `null`). */
|
|
18
|
-
readonly precision?: number | null;
|
|
19
17
|
/** Conversions to other units (typically needs at least the base conversion, unless it's already the base unit). */
|
|
20
18
|
readonly to?: Conversions<T>;
|
|
21
19
|
};
|
|
@@ -32,8 +30,6 @@ export declare class Unit<K extends string> {
|
|
|
32
30
|
readonly singular: string;
|
|
33
31
|
/** Plural name for this unit, e.g. `kilometers` (defaults to `singular` + "s"). */
|
|
34
32
|
readonly plural: string;
|
|
35
|
-
/** Default precision for this unit (defaults to `null`). */
|
|
36
|
-
readonly precision: number | null;
|
|
37
33
|
/** Title for this unit (uses format `abbr (plural)`, e.g. `fl oz (US fluid ounces)`) */
|
|
38
34
|
get title(): string;
|
|
39
35
|
constructor(
|
|
@@ -42,7 +38,7 @@ export declare class Unit<K extends string> {
|
|
|
42
38
|
/** String key for this unit, e.g. `kilometer` */
|
|
43
39
|
key: K,
|
|
44
40
|
/** Props to configure this unit. */
|
|
45
|
-
{ abbr, singular, plural,
|
|
41
|
+
{ abbr, singular, plural, to }: UnitProps<K>);
|
|
46
42
|
/** Convert an amount from this unit to another unit. */
|
|
47
43
|
to(amount: number, unit?: K | Unit<K>): number;
|
|
48
44
|
/** Convert an amount from another unit to this unit. */
|
|
@@ -50,11 +46,9 @@ export declare class Unit<K extends string> {
|
|
|
50
46
|
/** Convert an amount from this unit to another unit (must specify another `Unit` instance). */
|
|
51
47
|
private _toUnit;
|
|
52
48
|
/** Format an amount with a given unit of measure, e.g. `12 kg` or `29.5 l` */
|
|
53
|
-
format(amount: number,
|
|
49
|
+
format(amount: number, options?: NumberOptions): string;
|
|
54
50
|
/** Format an amount with a given unit of measure, e.g. `12 kilograms` or `29.5 liters` or `1 degree` */
|
|
55
|
-
|
|
56
|
-
/** Cram an amount with a given unit of measure, e.g. `1.25M mi` */
|
|
57
|
-
cram(amount: number): string;
|
|
51
|
+
pluralize(amount: number, options?: NumberOptions): string;
|
|
58
52
|
}
|
|
59
53
|
/**
|
|
60
54
|
* Represent a list of units.
|
package/util/units.js
CHANGED
|
@@ -3,7 +3,7 @@ import { RequiredError } from "../error/RequiredError.js";
|
|
|
3
3
|
import { DAY, HOUR, MILLION, MINUTE, MONTH, NNBSP, SECOND, WEEK, YEAR } from "./constants.js";
|
|
4
4
|
import { getProps } from "./object.js";
|
|
5
5
|
import { ImmutableMap } from "./map.js";
|
|
6
|
-
import {
|
|
6
|
+
import { pluralizeQuantity, formatQuantity } from "./number.js";
|
|
7
7
|
/** Convert an amount using a `Conversion. */
|
|
8
8
|
const _convert = (amount, conversion) => (typeof conversion === "function" ? conversion(amount) : conversion === 1 ? amount : amount * conversion);
|
|
9
9
|
/** Represent a unit. */
|
|
@@ -18,13 +18,12 @@ export class Unit {
|
|
|
18
18
|
/** String key for this unit, e.g. `kilometer` */
|
|
19
19
|
key,
|
|
20
20
|
/** Props to configure this unit. */
|
|
21
|
-
{ abbr = key.slice(0, 1), singular = key.replace(/-/, " "), plural = `${singular}s`,
|
|
21
|
+
{ abbr = key.slice(0, 1), singular = key.replace(/-/, " "), plural = `${singular}s`, to }) {
|
|
22
22
|
this.list = list;
|
|
23
23
|
this.key = key;
|
|
24
24
|
this.abbr = abbr;
|
|
25
25
|
this.singular = singular;
|
|
26
26
|
this.plural = plural;
|
|
27
|
-
this.precision = precision;
|
|
28
27
|
this._to = to;
|
|
29
28
|
}
|
|
30
29
|
/** Convert an amount from this unit to another unit. */
|
|
@@ -58,16 +57,12 @@ export class Unit {
|
|
|
58
57
|
throw new ConditionError(`Cannot convert "${this.key}" to "${unit.key}"`);
|
|
59
58
|
}
|
|
60
59
|
/** Format an amount with a given unit of measure, e.g. `12 kg` or `29.5 l` */
|
|
61
|
-
format(amount,
|
|
62
|
-
return formatQuantity(amount, this.abbr,
|
|
60
|
+
format(amount, options) {
|
|
61
|
+
return formatQuantity(amount, this.abbr, options);
|
|
63
62
|
}
|
|
64
63
|
/** Format an amount with a given unit of measure, e.g. `12 kilograms` or `29.5 liters` or `1 degree` */
|
|
65
|
-
|
|
66
|
-
return
|
|
67
|
-
}
|
|
68
|
-
/** Cram an amount with a given unit of measure, e.g. `1.25M mi` */
|
|
69
|
-
cram(amount) {
|
|
70
|
-
return cramQuantity(amount, this.abbr);
|
|
64
|
+
pluralize(amount, options) {
|
|
65
|
+
return pluralizeQuantity(amount, this.singular, this.plural, options);
|
|
71
66
|
}
|
|
72
67
|
}
|
|
73
68
|
/**
|