@weasel-js/quantity 1.6.1
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/LICENSE +21 -0
- package/README.md +83 -0
- package/dist/index.d.ts +430 -0
- package/dist/index.js +765 -0
- package/dist/index.js.map +1 -0
- package/package.json +43 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 orochi235
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# @weasel-js/quantity
|
|
2
|
+
|
|
3
|
+
Numbers with a unit and a display — how they show, how a screen reader says
|
|
4
|
+
them, and how typed text reads back. No React, no DOM.
|
|
5
|
+
|
|
6
|
+
Part of [weasel](https://github.com/orochi235/weasel), a domain-agnostic 2D
|
|
7
|
+
scene-graph canvas kit for React. See the
|
|
8
|
+
[API reference](https://orochi235.github.io/weasel/api/).
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npm install @weasel-js/quantity
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## A quantity is a number, or a number with a tag
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { qty, tag, retag, fraction } from '@weasel-js/quantity';
|
|
18
|
+
|
|
19
|
+
const seam = tag(1 / 12, fraction()); // { value: 0.0833…, display: { kind: 'fraction' } }
|
|
20
|
+
qty(seam).text // '1/12'
|
|
21
|
+
qty(seam).spoken // '1 over 12'
|
|
22
|
+
qty(seam).html // '<data value="0.0833…"><span data-part="numerator">1</span>/<span data-part="denominator">12</span></data>'
|
|
23
|
+
qty(seam).mathml // '<math><mfrac><mn>1</mn><mn>12</mn></mfrac></math>'
|
|
24
|
+
|
|
25
|
+
retag(seam, 1 / 6) // still tagged, still a fraction
|
|
26
|
+
retag(0.5, 0.25) // a bare number stays bare
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`Quantity` is `number | { value, unit?, display? }`. A bare number costs
|
|
30
|
+
nothing; a tagged one keeps its presentation as it moves, because code that
|
|
31
|
+
changes a value writes it back with `retag`. A display is plain data, so a
|
|
32
|
+
tagged value survives JSON, history snapshots and `structuredClone`.
|
|
33
|
+
|
|
34
|
+
`qty(q, fallback)` wraps a quantity for display. `fallback` is the display for a
|
|
35
|
+
value that carries none of its own — a value's own tag wins. Nothing about the
|
|
36
|
+
wrapper is stored.
|
|
37
|
+
|
|
38
|
+
## Displays
|
|
39
|
+
|
|
40
|
+
| Builder | Shows | Spoken |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `decimal({ places })` | `1,234.57` | `1,234.57` |
|
|
43
|
+
| `integer()` | `42` | `42` |
|
|
44
|
+
| `compact()` | `2.00M` | `2 million` |
|
|
45
|
+
| `percent()` | `25%` | `25 percent` |
|
|
46
|
+
| `fraction({ maxDenominator, mixed })` | `1/12`, `1 1/2` | `1 over 12`, `1 and 1 over 2` |
|
|
47
|
+
| `ratio()` | `1:12` | `1 to 12` |
|
|
48
|
+
| `multiplier()` | `2.5×` | `2.5 times` |
|
|
49
|
+
| `zoom()` | `150%`, `2.5x` | `150 percent`, `2.5 times` |
|
|
50
|
+
| `unit('mm', { accepts })` | `12mm` | `12 millimeters` |
|
|
51
|
+
| `currency('USD')` | `$12.50` | `12.50 US dollars` |
|
|
52
|
+
| `duration({ style })` | `1:02:03`, `2h 5m 3s` | `1 hour, 2 minutes, 3 seconds` |
|
|
53
|
+
| `bytes({ base })` | `1.2 MB`, `1 KiB` | `1.2 megabytes`, `1 kibibyte` |
|
|
54
|
+
| `roman()` | `XII` | `12` |
|
|
55
|
+
| `ordinal()` | `22nd` | `22nd` |
|
|
56
|
+
|
|
57
|
+
Every display parses its own text back (`parseAs('1 1/2', fraction())` is
|
|
58
|
+
1.5), and NaN is the answer for text it cannot read. A display whose kind is
|
|
59
|
+
not registered shows as `decimal` and keeps its tag, so a document saved by an
|
|
60
|
+
app with a custom kind still opens in one without it.
|
|
61
|
+
|
|
62
|
+
## Adding a kind
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import { registerDisplayKind, fractionKind } from '@weasel-js/quantity';
|
|
66
|
+
|
|
67
|
+
registerDisplayKind({ kind: 'hex', format: (v) => [{ type: 'number', value: `0x${v.toString(16)}` }] });
|
|
68
|
+
registerDisplayKind({ ...fractionKind, speak: (v) => toWords(v) }); // replaces the built-in
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
A kind returns parts — `{ type, value }` runs, the way `Intl`'s
|
|
72
|
+
`formatToParts` does. The text is the parts joined; the HTML wraps each
|
|
73
|
+
non-literal part in a `<span data-part>`, which is the whole styling surface.
|
|
74
|
+
|
|
75
|
+
## Units
|
|
76
|
+
|
|
77
|
+
`UnitSystem` tables convert between units of one dimension (`METRIC_MM`,
|
|
78
|
+
`IMPERIAL_INCHES`, `ANGLE_RADIANS`, `PIXELS`). `qty(tag(12, unit(), 'mm')).to('cm', METRIC_MM)`
|
|
79
|
+
is `1.2cm`. `parseNumber('5ft 3in', { in: 1, ft: 12 })` is 63.
|
|
80
|
+
|
|
81
|
+
## License
|
|
82
|
+
|
|
83
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Units — a tiny customizable unit system.
|
|
3
|
+
*
|
|
4
|
+
* The kit stores all coordinates as bare numbers in a single base unit
|
|
5
|
+
* chosen by the consumer app. To make API call sites self-documenting,
|
|
6
|
+
* the public surface accepts `UnitValue` — either a bare number (interpreted
|
|
7
|
+
* as base units) or a `{ value, unit }` tag that's resolved against a
|
|
8
|
+
* `UnitSystem` at the API boundary. Internals never see units.
|
|
9
|
+
*
|
|
10
|
+
* An entry is affine — `base = value * factor + offset` — so a scale that
|
|
11
|
+
* puts zero somewhere else (degC against K) is expressible. No per-axis
|
|
12
|
+
* units. No mixed-unit arithmetic.
|
|
13
|
+
*/
|
|
14
|
+
/** A unit name (e.g. `'in'`, `'ft'`, `'mm'`). Looked up in a `UnitSystem`. */
|
|
15
|
+
type Unit = string;
|
|
16
|
+
/** What one unit is worth in base units: `base = value * factor + offset`. */
|
|
17
|
+
interface UnitScale {
|
|
18
|
+
factor: number;
|
|
19
|
+
/** Where this unit puts zero, in base units. Absent is 0 — a pure scale. */
|
|
20
|
+
offset?: number;
|
|
21
|
+
}
|
|
22
|
+
/** One unit's conversion. A bare number is the `{ factor }` shorthand. */
|
|
23
|
+
type UnitEntry = number | UnitScale;
|
|
24
|
+
/** Conversion table mapping unit names to their scale against a base unit. */
|
|
25
|
+
interface UnitSystem {
|
|
26
|
+
/** Name of the base unit, e.g. 'in'. All conversions resolve to this. */
|
|
27
|
+
base: Unit;
|
|
28
|
+
/** How to reach base units from each unit. The base unit's entry is 1. */
|
|
29
|
+
units: Record<Unit, UnitEntry>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* One unit's scale, with the bare-number shorthand widened and the offset
|
|
33
|
+
* defaulted — what every conversion in the kit reads. Throws if the system
|
|
34
|
+
* does not carry the unit.
|
|
35
|
+
*/
|
|
36
|
+
declare function unitScale(unitSystem: UnitSystem, unit: Unit): Required<UnitScale>;
|
|
37
|
+
/** An entry's scale, with the bare-number shorthand widened and the offset defaulted. */
|
|
38
|
+
declare function entryScale(entry: UnitEntry): Required<UnitScale>;
|
|
39
|
+
/** Value at a unit-aware API boundary: bare number (in base units) or `{ value, unit }` tag. */
|
|
40
|
+
type UnitValue = number | {
|
|
41
|
+
value: number;
|
|
42
|
+
unit: Unit;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Resolve a UnitValue to a number in base units.
|
|
46
|
+
* - bare number, or a quantity tagged with no unit: its value, as base
|
|
47
|
+
* - tagged: looks up factor; throws if unit not in unit system
|
|
48
|
+
*/
|
|
49
|
+
declare function resolveUnit(v: UnitValue | Quantity, unitSystem?: UnitSystem): number;
|
|
50
|
+
/**
|
|
51
|
+
* Format a base-unit number as a string in the named display unit.
|
|
52
|
+
* e.g. formatUnit(36, 'ft', IMPERIAL_INCHES) => '3ft'
|
|
53
|
+
* Default precision: 2. Trailing zeros trimmed.
|
|
54
|
+
*/
|
|
55
|
+
declare function formatUnit(baseValue: number, displayUnit: Unit, unitSystem: UnitSystem, opts?: {
|
|
56
|
+
precision?: number;
|
|
57
|
+
suffix?: boolean;
|
|
58
|
+
}): string;
|
|
59
|
+
/** Imperial unit system with base 'in'. */
|
|
60
|
+
declare const IMPERIAL_INCHES: UnitSystem;
|
|
61
|
+
/** Metric unit system with base 'mm'. */
|
|
62
|
+
declare const METRIC_MM: UnitSystem;
|
|
63
|
+
/** Angle unit system with base 'rad'. */
|
|
64
|
+
declare const ANGLE_RADIANS: UnitSystem;
|
|
65
|
+
/** Pixel unit system — sole unit is the base. */
|
|
66
|
+
declare const PIXELS: UnitSystem;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* How a number shows, speaks and reads back, as plain data: `{ kind: 'fraction',
|
|
70
|
+
* maxDenominator: 64 }`. Data rather than a function so a tagged value keeps its
|
|
71
|
+
* presentation through JSON, history snapshots and `structuredClone`. The
|
|
72
|
+
* behavior lives on the registered {@link DisplayKind} of the same `kind`.
|
|
73
|
+
*/
|
|
74
|
+
interface Display {
|
|
75
|
+
readonly kind: string;
|
|
76
|
+
readonly [option: string]: unknown;
|
|
77
|
+
}
|
|
78
|
+
/** A number that carries its unit, its display, or both. */
|
|
79
|
+
interface Tagged {
|
|
80
|
+
value: number;
|
|
81
|
+
/** The unit `value` is measured in. Absent is the base unit. */
|
|
82
|
+
unit?: Unit;
|
|
83
|
+
display?: Display;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A number anywhere the engine takes one: bare, which costs nothing, or
|
|
87
|
+
* {@link Tagged}, which keeps its unit and display as it moves. Every reader
|
|
88
|
+
* goes through {@link amount}, and every writer through {@link retag}, so a
|
|
89
|
+
* value leaves in the shape it arrived in.
|
|
90
|
+
*/
|
|
91
|
+
type Quantity = number | Tagged;
|
|
92
|
+
/** The number a quantity holds. */
|
|
93
|
+
declare function amount(q: Quantity): number;
|
|
94
|
+
/**
|
|
95
|
+
* `next` in the shape of `q`: a bare number stays bare, and a tagged one keeps
|
|
96
|
+
* its unit and display. What an editor calls when it changes a value it was
|
|
97
|
+
* handed, so the presentation survives the edit.
|
|
98
|
+
*/
|
|
99
|
+
declare function retag<Q extends Quantity>(q: Q, next: number): Q;
|
|
100
|
+
/** A tagged quantity. `display` and `unit` are left off when absent, so the
|
|
101
|
+
* JSON carries only what was set. */
|
|
102
|
+
declare function tag(value: number, display?: Display, unit?: Unit): Tagged;
|
|
103
|
+
/** The display `q` carries, else `fallback`. A value's own tag wins over the
|
|
104
|
+
* default of whatever shows it. */
|
|
105
|
+
declare function displayOf(q: Quantity, fallback?: Display): Display | undefined;
|
|
106
|
+
/** The unit `q` is measured in, when it says. */
|
|
107
|
+
declare function unitOf(q: Quantity): Unit | undefined;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* One run of a formatted value, named for what it is: `number`, `unit`,
|
|
111
|
+
* `currency`, `numerator`, `literal`. The HTML serialization marks each
|
|
112
|
+
* non-literal run with `data-part`, which is the whole styling surface.
|
|
113
|
+
*/
|
|
114
|
+
interface Part {
|
|
115
|
+
type: string;
|
|
116
|
+
value: string;
|
|
117
|
+
}
|
|
118
|
+
/** What formatting and parsing need beyond the value and its display. */
|
|
119
|
+
interface FormatContext {
|
|
120
|
+
/** BCP 47 locale. Every kind parses `en-US` text, so it is the default. */
|
|
121
|
+
locale: string;
|
|
122
|
+
/** The unit the value is measured in, from its tag. */
|
|
123
|
+
unit?: Unit;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The behavior behind one `Display.kind`. `format` is required; `speak`
|
|
127
|
+
* defaults to the formatted text, `parse` to `parseNumber`, and `mathml` to
|
|
128
|
+
* nothing, which means the kind has no MathML form.
|
|
129
|
+
*/
|
|
130
|
+
interface DisplayKind<D extends Display = Display> {
|
|
131
|
+
kind: D['kind'];
|
|
132
|
+
format(value: number, display: D, ctx: FormatContext): Part[];
|
|
133
|
+
speak?(value: number, display: D, ctx: FormatContext): string;
|
|
134
|
+
parse?(text: string, display: D, ctx: FormatContext): number;
|
|
135
|
+
mathml?(value: number, display: D, ctx: FormatContext): string | undefined;
|
|
136
|
+
}
|
|
137
|
+
/** Parts joined into plain text. */
|
|
138
|
+
declare function textOf(parts: readonly Part[]): string;
|
|
139
|
+
/** A number run from `Intl`, with the real minus sign. */
|
|
140
|
+
declare function numberPart(value: number, locale: string, options?: Intl.NumberFormatOptions): Part;
|
|
141
|
+
/** `-` swapped for {@link MINUS_SIGN} at the front of already-formatted text. */
|
|
142
|
+
declare function signed(text: string): string;
|
|
143
|
+
/** A leading minus sign read aloud: `−3` is spoken `minus 3`, which a screen
|
|
144
|
+
* reader otherwise renders as a dash or drops. */
|
|
145
|
+
declare function spokenSign(text: string): string;
|
|
146
|
+
/** `singular` or `plural` for `value`, by the locale's plural rules. */
|
|
147
|
+
declare function plural(value: number, locale: string, singular: string, pluralForm: string): string;
|
|
148
|
+
/**
|
|
149
|
+
* `Intl.NumberFormat`'s parts, with the digit runs — sign, integer, group,
|
|
150
|
+
* decimal, fraction — merged into one `number` part, so a styling layer sees
|
|
151
|
+
* the number as one thing and its unit or symbol as another.
|
|
152
|
+
*/
|
|
153
|
+
declare function intlParts(value: number, locale: string, options: Intl.NumberFormatOptions): Part[];
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Adds a display kind, or replaces a built-in one — the way to swap in a words
|
|
157
|
+
* library for `fraction`'s spoken form (`{ ...fractionKind, speak }`). Returns
|
|
158
|
+
* a function that removes it again.
|
|
159
|
+
*/
|
|
160
|
+
declare function registerDisplayKind<D extends Display>(kind: DisplayKind<D>): () => void;
|
|
161
|
+
/**
|
|
162
|
+
* The kind behind `display`. An unknown kind resolves to `decimal` rather than
|
|
163
|
+
* throwing: a document saved by an app with a custom kind still opens in one
|
|
164
|
+
* without it, and its tag survives untouched.
|
|
165
|
+
*/
|
|
166
|
+
declare function displayKindOf(display: Display | undefined): DisplayKind<Display>;
|
|
167
|
+
/** Options for presenting or parsing a quantity. */
|
|
168
|
+
interface PresentOptions {
|
|
169
|
+
/** BCP 47 locale. Default `en-US`, which every kind parses. */
|
|
170
|
+
locale?: string;
|
|
171
|
+
}
|
|
172
|
+
/** A quantity as text, spoken text, HTML and, where its kind has one, MathML. */
|
|
173
|
+
interface Presentation {
|
|
174
|
+
text: string;
|
|
175
|
+
spoken: string;
|
|
176
|
+
/** Unstyled: a `<data value>` wrapping one `<span data-part>` per named part. */
|
|
177
|
+
html: string;
|
|
178
|
+
mathml?: string;
|
|
179
|
+
}
|
|
180
|
+
/** Parts as the unstyled HTML fragment {@link Presentation.html} describes. */
|
|
181
|
+
declare function partsToHtml(value: number, parts: readonly Part[]): string;
|
|
182
|
+
/** Parts of `q` under its own display, else `fallback`, else `decimal`. */
|
|
183
|
+
declare function partsOf(q: Quantity, fallback?: Display, options?: PresentOptions): Part[];
|
|
184
|
+
/** `q` as text, spoken text, HTML and MathML. See {@link qty} for one at a time. */
|
|
185
|
+
declare function present(q: Quantity, fallback?: Display, options?: PresentOptions): Presentation;
|
|
186
|
+
/** Typed text read through `display`: NaN when it does not read. */
|
|
187
|
+
declare function parseAs(text: string, display?: Display, options?: PresentOptions & {
|
|
188
|
+
unit?: Unit;
|
|
189
|
+
}): number;
|
|
190
|
+
/**
|
|
191
|
+
* A short-lived view of a quantity with its presentation as methods. Nothing
|
|
192
|
+
* about it is stored — keep the quantity, and wrap it where it is shown.
|
|
193
|
+
*/
|
|
194
|
+
declare class QuantityView<Q extends Quantity = Quantity> {
|
|
195
|
+
/** The quantity as given. */
|
|
196
|
+
readonly raw: Q;
|
|
197
|
+
private readonly fallback?;
|
|
198
|
+
private readonly options?;
|
|
199
|
+
constructor(
|
|
200
|
+
/** The quantity as given. */
|
|
201
|
+
raw: Q, fallback?: Display | undefined, options?: PresentOptions | undefined);
|
|
202
|
+
get value(): number;
|
|
203
|
+
get unit(): Unit | undefined;
|
|
204
|
+
/** The display in effect: the value's own, else the fallback. */
|
|
205
|
+
get display(): Display | undefined;
|
|
206
|
+
get parts(): Part[];
|
|
207
|
+
get text(): string;
|
|
208
|
+
get spoken(): string;
|
|
209
|
+
get html(): string;
|
|
210
|
+
get mathml(): string | undefined;
|
|
211
|
+
/** `next` in this quantity's shape — see {@link retag}. */
|
|
212
|
+
with(next: number): QuantityView<Q>;
|
|
213
|
+
/** Typed text read through this quantity's display; NaN when it does not read. */
|
|
214
|
+
parse(text: string): number;
|
|
215
|
+
/** The same amount in `target`, converted through `system`, tagged with it. */
|
|
216
|
+
to(target: Unit, system: UnitSystem): QuantityView;
|
|
217
|
+
toJSON(): Q;
|
|
218
|
+
}
|
|
219
|
+
/** Wraps `q` for display: `qty(band.from, fraction()).text`. `fallback` is the
|
|
220
|
+
* display for a value that carries none of its own. */
|
|
221
|
+
declare function qty<Q extends Quantity>(q: Q, fallback?: Display, options?: PresentOptions): QuantityView<Q>;
|
|
222
|
+
|
|
223
|
+
/** A plain number. `places` fixes the decimals, trailing zeros included;
|
|
224
|
+
* otherwise up to `maxPlaces` (default 3) show and trailing zeros drop. */
|
|
225
|
+
type DecimalDisplay = {
|
|
226
|
+
kind: 'decimal';
|
|
227
|
+
places?: number;
|
|
228
|
+
maxPlaces?: number;
|
|
229
|
+
/** Thousands separators. Default true. */
|
|
230
|
+
grouping?: boolean;
|
|
231
|
+
};
|
|
232
|
+
declare function decimal(options?: Omit<DecimalDisplay, 'kind'>): DecimalDisplay;
|
|
233
|
+
declare const decimalKind: DisplayKind<DecimalDisplay>;
|
|
234
|
+
/** A whole number, rounded. */
|
|
235
|
+
type IntegerDisplay = {
|
|
236
|
+
kind: 'integer';
|
|
237
|
+
grouping?: boolean;
|
|
238
|
+
};
|
|
239
|
+
declare function integer(options?: Omit<IntegerDisplay, 'kind'>): IntegerDisplay;
|
|
240
|
+
declare const integerKind: DisplayKind<IntegerDisplay>;
|
|
241
|
+
/**
|
|
242
|
+
* Below 1,000 at `places` decimals, from 1,000 up at three significant figures
|
|
243
|
+
* with a magnitude suffix (`40.0K`, `2.00M`), so a readout holds one width.
|
|
244
|
+
* Spoken long: `2 million`.
|
|
245
|
+
*/
|
|
246
|
+
type CompactDisplay = {
|
|
247
|
+
kind: 'compact';
|
|
248
|
+
places?: number;
|
|
249
|
+
};
|
|
250
|
+
declare function compact(options?: Omit<CompactDisplay, 'kind'>): CompactDisplay;
|
|
251
|
+
declare const compactKind: DisplayKind<CompactDisplay>;
|
|
252
|
+
|
|
253
|
+
/** A share of one as a percentage: 0.25 shows `25%`. Typed text reads as
|
|
254
|
+
* percent with or without the sign. */
|
|
255
|
+
type PercentDisplay = {
|
|
256
|
+
kind: 'percent';
|
|
257
|
+
/** Most decimals shown. Default 0. */
|
|
258
|
+
places?: number;
|
|
259
|
+
};
|
|
260
|
+
declare function percent(options?: Omit<PercentDisplay, 'kind'>): PercentDisplay;
|
|
261
|
+
declare const percentKind: DisplayKind<PercentDisplay>;
|
|
262
|
+
/**
|
|
263
|
+
* The nearest fraction whose denominator is at most `maxDenominator`, as
|
|
264
|
+
* `[numerator, denominator]` in lowest terms with the sign on the numerator.
|
|
265
|
+
* Walks the continued fraction and settles the last step on the best
|
|
266
|
+
* semiconvergent, which is the closest any denominator in range can get.
|
|
267
|
+
*/
|
|
268
|
+
declare function nearestFraction(x: number, maxDenominator: number): [number, number];
|
|
269
|
+
/**
|
|
270
|
+
* A value as the nearest fraction: 1/12 shows `1/12` and is spoken `1 over 12`.
|
|
271
|
+
* `mixed` splits a whole part off (`1 1/2`); a whole value shows as an integer.
|
|
272
|
+
* Has a MathML form, as `<mfrac>`.
|
|
273
|
+
*/
|
|
274
|
+
type FractionDisplay = {
|
|
275
|
+
kind: 'fraction';
|
|
276
|
+
/** Default 64. */
|
|
277
|
+
maxDenominator?: number;
|
|
278
|
+
mixed?: boolean;
|
|
279
|
+
};
|
|
280
|
+
declare function fraction(options?: Omit<FractionDisplay, 'kind'>): FractionDisplay;
|
|
281
|
+
declare const fractionKind: DisplayKind<FractionDisplay>;
|
|
282
|
+
/** A value as a ratio `a:b` — 1/12 shows `1:12`, spoken `1 to 12`. */
|
|
283
|
+
type RatioDisplay = {
|
|
284
|
+
kind: 'ratio';
|
|
285
|
+
/** Largest second term. Default 64. */
|
|
286
|
+
maxDenominator?: number;
|
|
287
|
+
};
|
|
288
|
+
declare function ratio(options?: Omit<RatioDisplay, 'kind'>): RatioDisplay;
|
|
289
|
+
declare const ratioKind: DisplayKind<RatioDisplay>;
|
|
290
|
+
/** A factor with a times sign: `2.5×`, spoken `2.5 times`. Typed `x` reads too. */
|
|
291
|
+
type MultiplierDisplay = {
|
|
292
|
+
kind: 'multiplier';
|
|
293
|
+
/** Most decimals. Default 2. */
|
|
294
|
+
places?: number;
|
|
295
|
+
/** Default `×`. */
|
|
296
|
+
symbol?: string;
|
|
297
|
+
};
|
|
298
|
+
declare function multiplier(options?: Omit<MultiplierDisplay, 'kind'>): MultiplierDisplay;
|
|
299
|
+
declare const multiplierKind: DisplayKind<MultiplierDisplay>;
|
|
300
|
+
/**
|
|
301
|
+
* A zoom factor. At 2x and below a percentage reads naturally; past it a
|
|
302
|
+
* multiplier is what people say, so 2.5 shows `2.5x`. Past 100x a tenth is
|
|
303
|
+
* noise: `1009.74` shows `1,010x`. Typed text without a sign reads as percent.
|
|
304
|
+
*/
|
|
305
|
+
type ZoomDisplay = {
|
|
306
|
+
kind: 'zoom';
|
|
307
|
+
};
|
|
308
|
+
declare function zoom(): ZoomDisplay;
|
|
309
|
+
declare const zoomKind: DisplayKind<ZoomDisplay>;
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Display-formatter for numbers. Use this anywhere a number is shown to
|
|
313
|
+
* a user. The whole point: negative values get prefixed with the real
|
|
314
|
+
* MINUS SIGN (U+2212) instead of the ASCII HYPHEN-MINUS (U+002D) that
|
|
315
|
+
* `toLocaleString` and template literals produce by default.
|
|
316
|
+
*
|
|
317
|
+
* U+2212 is the same visual width as `+` and reads as a sign rather
|
|
318
|
+
* than a hyphen — columns of signed numbers align cleanly and the
|
|
319
|
+
* glyph doesn't get confused with a bullet or list dash.
|
|
320
|
+
*/
|
|
321
|
+
declare const MINUS_SIGN = "\u2212";
|
|
322
|
+
/**
|
|
323
|
+
* Formats a number for display, substituting {@link MINUS_SIGN} for the ASCII
|
|
324
|
+
* hyphen `toLocaleString` emits. Non-finite values stringify as-is.
|
|
325
|
+
*/
|
|
326
|
+
declare function formatNumber(value: number, options?: Intl.NumberFormatOptions): string;
|
|
327
|
+
/**
|
|
328
|
+
* Parses a string that may carry {@link MINUS_SIGN} in place of the ASCII
|
|
329
|
+
* hyphen. The inverse of {@link formatNumber} for any editing surface that
|
|
330
|
+
* renders its value through it and reads the edited text back.
|
|
331
|
+
*/
|
|
332
|
+
declare function parseSignedNumber(text: string): number;
|
|
333
|
+
/**
|
|
334
|
+
* Formats a number the way a `compact` readout shows it: below 1,000 at
|
|
335
|
+
* `decimals` places, from 1,000 up at three significant figures with a magnitude
|
|
336
|
+
* suffix (`40.0K`, `294K`, `2.00M`), so the readout holds one width whatever the
|
|
337
|
+
* value. Always `en-US`, so {@link parseNumber} reads it back.
|
|
338
|
+
*/
|
|
339
|
+
declare function formatCompact(value: number, decimals?: number): string;
|
|
340
|
+
/**
|
|
341
|
+
* Reads a typed number: anything {@link parseSignedNumber} reads, plus
|
|
342
|
+
* thousands commas in the `40,000` shape and a `k`/`m`/`b`/`t` suffix in either
|
|
343
|
+
* case (`2.5m` is 2,500,000). Empty text is NaN rather than zero.
|
|
344
|
+
*
|
|
345
|
+
* Given `units` — a suffix mapped to the factor it scales by — a trailing unit
|
|
346
|
+
* name is read first: the longest match wins, exact case before any case, and
|
|
347
|
+
* a unit beats a magnitude suffix, so with `m` accepted `2m` is `2 * units.m`.
|
|
348
|
+
*/
|
|
349
|
+
declare function parseNumber(text: string, units?: Readonly<UnitTable>): number;
|
|
350
|
+
/** Suffixes a person may type, each mapped to its conversion into the shown unit. */
|
|
351
|
+
type UnitTable = Record<string, UnitEntry>;
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* A number with its unit: `12mm`, spoken `12 millimeters`. The unit is the
|
|
355
|
+
* display's, else the one the value is tagged with. `accepts` is what a person
|
|
356
|
+
* may type, each unit mapped to its conversion into the shown one; compound
|
|
357
|
+
* text such as `5ft 3in` reads when every term's unit is accepted.
|
|
358
|
+
*/
|
|
359
|
+
type UnitDisplay = {
|
|
360
|
+
kind: 'unit';
|
|
361
|
+
unit?: string;
|
|
362
|
+
/** Most decimals, trailing zeros dropped. Default 2. */
|
|
363
|
+
places?: number;
|
|
364
|
+
/** A space between number and unit. Default false. */
|
|
365
|
+
space?: boolean;
|
|
366
|
+
accepts?: UnitTable;
|
|
367
|
+
/** Singular and plural names, for a unit `Intl` cannot name. */
|
|
368
|
+
spoken?: readonly [string, string];
|
|
369
|
+
};
|
|
370
|
+
declare function unit(name?: string, options?: Omit<UnitDisplay, 'kind' | 'unit'>): UnitDisplay;
|
|
371
|
+
declare const unitKind: DisplayKind<UnitDisplay>;
|
|
372
|
+
/** Money in an ISO 4217 currency: `$12.50`, spoken `12.50 US dollars`. */
|
|
373
|
+
type CurrencyDisplay = {
|
|
374
|
+
kind: 'currency';
|
|
375
|
+
currency: string;
|
|
376
|
+
/** Fixed decimals. Defaults to the currency's own (2 for USD, 0 for JPY). */
|
|
377
|
+
places?: number;
|
|
378
|
+
};
|
|
379
|
+
declare function currency(code: string, options?: Omit<CurrencyDisplay, 'kind' | 'currency'>): CurrencyDisplay;
|
|
380
|
+
declare const currencyKind: DisplayKind<CurrencyDisplay>;
|
|
381
|
+
/**
|
|
382
|
+
* A span of time, the value in seconds. `clock` shows `1:02:03` or `4:05.5`;
|
|
383
|
+
* `units` shows `2h 5m 3s`. Spoken `2 hours, 5 minutes, 3 seconds`. Typed text
|
|
384
|
+
* reads in either form.
|
|
385
|
+
*/
|
|
386
|
+
type DurationDisplay = {
|
|
387
|
+
kind: 'duration';
|
|
388
|
+
/** Default `'clock'`. */
|
|
389
|
+
style?: 'clock' | 'units';
|
|
390
|
+
/** Decimals on the seconds. Default 0. */
|
|
391
|
+
places?: number;
|
|
392
|
+
};
|
|
393
|
+
declare function duration(options?: Omit<DurationDisplay, 'kind'>): DurationDisplay;
|
|
394
|
+
declare const durationKind: DisplayKind<DurationDisplay>;
|
|
395
|
+
/** A byte count in the largest unit that keeps it at or above 1: `1.2 MB`,
|
|
396
|
+
* spoken `1.2 megabytes`. `base: 1024` uses `KiB`, `MiB`, … */
|
|
397
|
+
type BytesDisplay = {
|
|
398
|
+
kind: 'bytes';
|
|
399
|
+
/** Default 1000. */
|
|
400
|
+
base?: 1000 | 1024;
|
|
401
|
+
/** Most decimals. Default 1. */
|
|
402
|
+
places?: number;
|
|
403
|
+
};
|
|
404
|
+
declare function bytes(options?: Omit<BytesDisplay, 'kind'>): BytesDisplay;
|
|
405
|
+
declare const bytesKind: DisplayKind<BytesDisplay>;
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* A whole number in Roman numerals, `XII`. Spoken as the plain number, since a
|
|
409
|
+
* screen reader otherwise spells the letters out. Outside 1–3999, which the
|
|
410
|
+
* numerals cannot write, it shows as a plain number.
|
|
411
|
+
*/
|
|
412
|
+
type RomanDisplay = {
|
|
413
|
+
kind: 'roman';
|
|
414
|
+
lower?: boolean;
|
|
415
|
+
};
|
|
416
|
+
declare function roman(options?: Omit<RomanDisplay, 'kind'>): RomanDisplay;
|
|
417
|
+
/** `n` in Roman numerals, or undefined outside 1–3999. */
|
|
418
|
+
declare function toRoman(n: number): string | undefined;
|
|
419
|
+
/** The value of well-formed Roman numerals, either case; NaN otherwise. */
|
|
420
|
+
declare function fromRoman(text: string): number;
|
|
421
|
+
declare const romanKind: DisplayKind<RomanDisplay>;
|
|
422
|
+
/** A whole number as an ordinal: `1st`, `22nd`, `113th`. English suffixes;
|
|
423
|
+
* in any other locale the number shows alone. */
|
|
424
|
+
type OrdinalDisplay = {
|
|
425
|
+
kind: 'ordinal';
|
|
426
|
+
};
|
|
427
|
+
declare function ordinal(): OrdinalDisplay;
|
|
428
|
+
declare const ordinalKind: DisplayKind<OrdinalDisplay>;
|
|
429
|
+
|
|
430
|
+
export { ANGLE_RADIANS, type BytesDisplay, type CompactDisplay, type CurrencyDisplay, type DecimalDisplay, type Display, type DisplayKind, type DurationDisplay, type FormatContext, type FractionDisplay, IMPERIAL_INCHES, type IntegerDisplay, METRIC_MM, MINUS_SIGN, type MultiplierDisplay, type OrdinalDisplay, PIXELS, type Part, type PercentDisplay, type PresentOptions, type Presentation, type Quantity, QuantityView, type RatioDisplay, type RomanDisplay, type Tagged, type Unit, type UnitDisplay, type UnitEntry, type UnitScale, type UnitSystem, type UnitTable, type UnitValue, type ZoomDisplay, amount, bytes, bytesKind, compact, compactKind, currency, currencyKind, decimal, decimalKind, displayKindOf, displayOf, duration, durationKind, entryScale, formatCompact, formatNumber, formatUnit, fraction, fractionKind, fromRoman, integer, integerKind, intlParts, multiplier, multiplierKind, nearestFraction, numberPart, ordinal, ordinalKind, parseAs, parseNumber, parseSignedNumber, partsOf, partsToHtml, percent, percentKind, plural, present, qty, ratio, ratioKind, registerDisplayKind, resolveUnit, retag, roman, romanKind, signed, spokenSign, tag, textOf, toRoman, unit, unitKind, unitOf, unitScale, zoom, zoomKind };
|