@boost33/ui 0.4.1 → 0.5.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/README.md +26 -1
- package/dist/components/AccountMenu/AccountMenu.d.ts +131 -0
- package/dist/components/AccountMenu/icons.d.ts +11 -0
- package/dist/components/AccountMenu/labels.d.ts +39 -0
- package/dist/components/AppShell/AppShell.d.ts +71 -18
- package/dist/components/AppShell/AppShell.js +33 -35
- package/dist/components/AppShell/AppShell.js.map +1 -1
- package/dist/components/AppShell/AppShell.module.css.js +11 -11
- package/dist/components/AppSwitcher/AppSwitcher.d.ts +23 -1
- package/dist/components/AppSwitcher/AppSwitcher.js +49 -43
- package/dist/components/AppSwitcher/AppSwitcher.js.map +1 -1
- package/dist/components/AppSwitcher/AppSwitcher.module.css.js +36 -36
- package/dist/components/AppSwitcher/labels.js.map +1 -1
- package/dist/components/CommandPalette/CommandPalette.d.ts +127 -0
- package/dist/components/CommandPalette/icons.d.ts +8 -0
- package/dist/components/CommandPalette/labels.d.ts +94 -0
- package/dist/components/DotMatrixLoader/DotMatrixLoader.d.ts +21 -0
- package/dist/components/DotMatrixLoader/DotMatrixLoader.js +18 -0
- package/dist/components/DotMatrixLoader/DotMatrixLoader.js.map +1 -0
- package/dist/components/DotMatrixLoader/DotMatrixLoader.module.css.js +12 -0
- package/dist/components/DotMatrixLoader/DotMatrixLoader.module.css.js.map +1 -0
- package/dist/components/LaunchLoader/LaunchLoader.d.ts +54 -0
- package/dist/components/LaunchLoader/LaunchLoader.js +57 -0
- package/dist/components/LaunchLoader/LaunchLoader.js.map +1 -0
- package/dist/components/LaunchLoader/LaunchLoader.module.css.js +24 -0
- package/dist/components/LaunchLoader/LaunchLoader.module.css.js.map +1 -0
- package/dist/components/NotificationCenter/NotificationCenter.d.ts +162 -0
- package/dist/components/NotificationCenter/icons.d.ts +34 -0
- package/dist/components/NotificationCenter/labels.d.ts +112 -0
- package/dist/components/ProductMark/ProductMark.d.ts +54 -1
- package/dist/components/ProductMark/ProductMark.js +23 -19
- package/dist/components/ProductMark/ProductMark.js.map +1 -1
- package/dist/components/Sidebar/Sidebar.d.ts +17 -3
- package/dist/components/Sidebar/Sidebar.js +5 -5
- package/dist/components/Sidebar/Sidebar.js.map +1 -1
- package/dist/components/WalletChip/WalletChip.d.ts +150 -27
- package/dist/components/WalletChip/WalletChip.js +38 -35
- package/dist/components/WalletChip/WalletChip.js.map +1 -1
- package/dist/components/WalletChip/WalletChip.module.css.js +36 -36
- package/dist/components/WalletChip/labels.d.ts +65 -47
- package/dist/components/WalletChip/labels.js +15 -20
- package/dist/components/WalletChip/labels.js.map +1 -1
- package/dist/fill-template.d.ts +10 -0
- package/dist/fill-template.js +10 -0
- package/dist/fill-template.js.map +1 -0
- package/dist/index.d.ts +10 -4
- package/dist/index.js +36 -39
- package/dist/index.js.map +1 -1
- package/dist/launch-gradient.d.ts +36 -0
- package/dist/launch-gradient.js +8 -0
- package/dist/launch-gradient.js.map +1 -0
- package/dist/nav-height-budget.d.ts +4 -0
- package/dist/nav-height-budget.js +18 -19
- package/dist/nav-height-budget.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/theme.css +40 -14
- package/package.json +2 -1
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
const t = "
|
|
1
|
+
const t = "_chip_xxga2_37", _ = "_header_xxga2_74", a = "_name_xxga2_83", n = "_pill_xxga2_162", s = "_figure_xxga2_171", e = "_amount_xxga2_186", o = "_unit_xxga2_195", x = "_retry_xxga2_200", r = "_bar_xxga2_209", i = "_segmentGranted_xxga2_230", c = "_segmentPurchased_xxga2_231", l = "_details_xxga2_275", g = "_split_xxga2_284", d = "_splitRow_xxga2_291", p = "_splitLabel_xxga2_297", u = "_splitAmount_xxga2_305", m = "_dotGranted_xxga2_313", h = "_dotPurchased_xxga2_314", b = "_footer_xxga2_328", f = "_burnNote_xxga2_336", y = "_topUp_xxga2_87", G = "_rail_xxga2_402", P = "_railIcon_xxga2_416", w = "_railFigure_xxga2_422", A = "_srOnly_xxga2_435", F = {
|
|
2
2
|
chip: t,
|
|
3
3
|
header: _,
|
|
4
|
-
name:
|
|
5
|
-
pill:
|
|
6
|
-
figure:
|
|
7
|
-
amount:
|
|
8
|
-
unit:
|
|
9
|
-
retry:
|
|
10
|
-
bar:
|
|
11
|
-
segmentGranted:
|
|
12
|
-
segmentPurchased:
|
|
13
|
-
details:
|
|
14
|
-
split:
|
|
4
|
+
name: a,
|
|
5
|
+
pill: n,
|
|
6
|
+
figure: s,
|
|
7
|
+
amount: e,
|
|
8
|
+
unit: o,
|
|
9
|
+
retry: x,
|
|
10
|
+
bar: r,
|
|
11
|
+
segmentGranted: i,
|
|
12
|
+
segmentPurchased: c,
|
|
13
|
+
details: l,
|
|
14
|
+
split: g,
|
|
15
15
|
splitRow: d,
|
|
16
16
|
splitLabel: p,
|
|
17
17
|
splitAmount: u,
|
|
18
18
|
dotGranted: m,
|
|
19
|
-
dotPurchased:
|
|
19
|
+
dotPurchased: h,
|
|
20
20
|
footer: b,
|
|
21
21
|
burnNote: f,
|
|
22
|
-
topUp:
|
|
23
|
-
rail:
|
|
24
|
-
railIcon:
|
|
25
|
-
railFigure:
|
|
26
|
-
srOnly:
|
|
22
|
+
topUp: y,
|
|
23
|
+
rail: G,
|
|
24
|
+
railIcon: P,
|
|
25
|
+
railFigure: w,
|
|
26
|
+
srOnly: A
|
|
27
27
|
};
|
|
28
28
|
export {
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
e as amount,
|
|
30
|
+
r as bar,
|
|
31
31
|
f as burnNote,
|
|
32
32
|
t as chip,
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
F as default,
|
|
34
|
+
l as details,
|
|
35
35
|
m as dotGranted,
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
h as dotPurchased,
|
|
37
|
+
s as figure,
|
|
38
38
|
b as footer,
|
|
39
39
|
_ as header,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
40
|
+
a as name,
|
|
41
|
+
n as pill,
|
|
42
|
+
G as rail,
|
|
43
|
+
w as railFigure,
|
|
44
|
+
P as railIcon,
|
|
45
|
+
x as retry,
|
|
46
|
+
i as segmentGranted,
|
|
47
|
+
c as segmentPurchased,
|
|
48
|
+
g as split,
|
|
49
49
|
u as splitAmount,
|
|
50
50
|
p as splitLabel,
|
|
51
51
|
d as splitRow,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
A as srOnly,
|
|
53
|
+
y as topUp,
|
|
54
|
+
o as unit
|
|
55
55
|
};
|
|
56
56
|
//# sourceMappingURL=WalletChip.module.css.js.map
|
|
@@ -1,34 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Every user-facing string the chip renders. Supplied whole -- the strings that
|
|
3
|
-
* carry a value take a
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* carry a value take a `%placeholder%` template rather than fragments, because
|
|
4
|
+
* word order is not a property of English and cannot be reassembled on the
|
|
5
|
+
* consumer's side.
|
|
6
6
|
*
|
|
7
|
-
* `grantExpiry`, `railFigure`, `railBalance` and
|
|
7
|
+
* **Every field is a `string`.** `grantExpiry`, `railFigure`, `railBalance` and
|
|
8
|
+
* `bar` are templates: plain strings marking each value's position with a
|
|
9
|
+
* `%placeholder%` -- `'Granted expires %date% · burns first'`. So the whole
|
|
10
|
+
* `labels` object is data all the way down, it survives the React Server Component
|
|
11
|
+
* boundary, and a Next App Router page can build it. There is no function form:
|
|
12
|
+
* it only ever worked from a Client Component and nothing in the type said so
|
|
13
|
+
* (B33-1278). A sentence needing logic a template cannot express -- a plural rule,
|
|
14
|
+
* an `Intl.ListFormat` call -- is computed in the consumer's own catalogue, where
|
|
15
|
+
* ICU already lives, and passed here finished.
|
|
8
16
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* the way down, so the whole `labels` object survives the React Server Component
|
|
12
|
-
* boundary and a Next App Router page can pass it. **This is the recommended
|
|
13
|
-
* form**, and the one the defaults use.
|
|
14
|
-
*
|
|
15
|
-
* A **function** of the same values still works unchanged. It is never
|
|
16
|
-
* serializable, so it only reaches the chip from a Client Component, but it is
|
|
17
|
-
* the escape hatch when the sentence needs logic a template cannot express --
|
|
18
|
-
* ICU plurals, an `Intl.ListFormat` call.
|
|
19
|
-
*
|
|
20
|
-
* In a template, a `{name}` the label does not define is left exactly as written
|
|
21
|
-
* -- a locale whose copy genuinely contains braces is not mangled, and a
|
|
17
|
+
* In a template, a `%name%` the label does not define is left exactly as written
|
|
18
|
+
* -- a locale whose copy genuinely pairs two percent signs is not mangled, and a
|
|
22
19
|
* misspelled placeholder shows up in the rendering rather than silently
|
|
23
20
|
* blanking. A placeholder a template omits is simply not rendered: a locale that
|
|
24
21
|
* does not need a figure is allowed not to name it.
|
|
22
|
+
*
|
|
23
|
+
* **The delimiter is `%name%`, not `{name}`** (B33-1277). A brace is ICU
|
|
24
|
+
* MessageFormat argument syntax, so a consumer storing these templates in its own
|
|
25
|
+
* next-intl catalogue could not read them back through its own formatter. It can
|
|
26
|
+
* now. `src/fill-template.ts` carries the reasoning, including why `%{name}` and
|
|
27
|
+
* `{{name}}` would both have kept the collision. A `{braced}` name in a template
|
|
28
|
+
* is ordinary text here and is passed through untouched.
|
|
25
29
|
*/
|
|
26
30
|
export interface WalletChipLabels {
|
|
27
31
|
/** Accessible name for the expanded chip's status region. Default "Wallet balance". */
|
|
28
32
|
region: string;
|
|
29
33
|
/** The chip header. Default "Wallet". */
|
|
30
34
|
name: string;
|
|
31
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* The unit rendered beside the figure. Default "tokens".
|
|
37
|
+
*
|
|
38
|
+
* A single string, and so not count-dependent: a balance of one renders "1
|
|
39
|
+
* tokens" in English. That has been true since B33-849 and B33-1278 did not
|
|
40
|
+
* change it -- `unit` never had the function form, so dropping that form took
|
|
41
|
+
* nothing away here. It is also a state the chip is unlikely to reach: a
|
|
42
|
+
* one-token balance is an all-but-empty wallet. If a locale needs the unit to
|
|
43
|
+
* agree with the figure, the shape to add is a count-aware field, which is a
|
|
44
|
+
* decision and not a refactor; nothing in the package invents one meanwhile.
|
|
45
|
+
*/
|
|
32
46
|
unit: string;
|
|
33
47
|
/** Screen-reader-only text in the figure when the balance is unknown. Default "Balance unknown". */
|
|
34
48
|
balanceUnknown: string;
|
|
@@ -40,10 +54,9 @@ export interface WalletChipLabels {
|
|
|
40
54
|
* The footer, around the preformatted expiry date. Supplied whole so the
|
|
41
55
|
* consumer owns word order.
|
|
42
56
|
*
|
|
43
|
-
* Placeholder:
|
|
44
|
-
* A function form `(date) => string` is also accepted.
|
|
57
|
+
* Placeholder: `%date%`. Default (en) `Granted expires %date% · burns first`.
|
|
45
58
|
*/
|
|
46
|
-
grantExpiry: string
|
|
59
|
+
grantExpiry: string;
|
|
47
60
|
/** The top-up button. Default "Top up". */
|
|
48
61
|
topUp: string;
|
|
49
62
|
/**
|
|
@@ -51,8 +64,7 @@ export interface WalletChipLabels {
|
|
|
51
64
|
* (e.g. "4.6"). Both the visible text in the 56px rail and the figure that
|
|
52
65
|
* `railBalance` is given.
|
|
53
66
|
*
|
|
54
|
-
* Placeholder:
|
|
55
|
-
* `(figure) => string` is also accepted.
|
|
67
|
+
* Placeholder: `%figure%`. Default (en) `%figure%k`.
|
|
56
68
|
*
|
|
57
69
|
* The figure itself is formatted for `locale` -- "4.6" in `en-US`, "4,6" in
|
|
58
70
|
* `fr-FR` -- but the mark after it is a word, and locales disagree about it:
|
|
@@ -61,14 +73,13 @@ export interface WalletChipLabels {
|
|
|
61
73
|
* template rather than a bare suffix, because a locale is entitled to put the
|
|
62
74
|
* mark somewhere other than the end (B33-876).
|
|
63
75
|
*/
|
|
64
|
-
railFigure: string
|
|
76
|
+
railFigure: string;
|
|
65
77
|
/**
|
|
66
78
|
* The rail group's accessible name, around the abbreviated balance (e.g. "4.6k").
|
|
67
79
|
*
|
|
68
|
-
* Placeholder:
|
|
69
|
-
* form `(balance) => string` is also accepted.
|
|
80
|
+
* Placeholder: `%balance%`. Default (en) `Wallet balance %balance%`.
|
|
70
81
|
*/
|
|
71
|
-
railBalance: string
|
|
82
|
+
railBalance: string;
|
|
72
83
|
/** The rail group's accessible name when the balance is unknown. Default "Wallet balance unknown". */
|
|
73
84
|
railBalanceUnknown: string;
|
|
74
85
|
/** Screen-reader-only marker on the rail when the balance is unknown. Default "unknown". */
|
|
@@ -78,22 +89,25 @@ export interface WalletChipLabels {
|
|
|
78
89
|
* whole rather than concatenated from fragments -- the shape that cannot be
|
|
79
90
|
* translated at all otherwise.
|
|
80
91
|
*
|
|
81
|
-
* Placeholders:
|
|
82
|
-
*
|
|
83
|
-
* function form `(granted, purchased, remaining) => string` is also accepted.
|
|
92
|
+
* Placeholders: `%granted%`, `%purchased%`, `%remaining%`. Default (en)
|
|
93
|
+
* `%granted% granted and %purchased% purchased of %remaining% remaining`.
|
|
84
94
|
*/
|
|
85
|
-
bar: string
|
|
95
|
+
bar: string;
|
|
86
96
|
}
|
|
87
97
|
/**
|
|
88
98
|
* The English defaults. A consumer that passes nothing renders exactly what the
|
|
89
99
|
* chip rendered before this prop existed, so the wallet baselines do not move.
|
|
90
100
|
*
|
|
91
|
-
* **Every field is a string.** That is what makes
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
101
|
+
* **Every field is a string, and now by construction.** That is what makes
|
|
102
|
+
* `labels={defaultWalletChipLabels}` and
|
|
103
|
+
* `labels={{ ...defaultWalletChipLabels, name: 'Portefeuille' }}` -- the pattern
|
|
104
|
+
* the specimen shows and this package's own advice recommends -- legal from a
|
|
105
|
+
* Server Component (B33-880). It used to be a runtime fact pinned by a test,
|
|
106
|
+
* because `WalletChipLabels` admitted functions on four fields and `satisfies`
|
|
107
|
+
* could not tell the two apart (B33-1029). With the union gone the compiler
|
|
108
|
+
* carries it: a function here is a type error at the `satisfies`, and
|
|
109
|
+
* `tests/wallet-chip-labels.type-test.ts` asserts the all-strings shape of the
|
|
110
|
+
* interface itself so the guarantee cannot be lost by widening one field.
|
|
97
111
|
*
|
|
98
112
|
* Frozen because it is a public export reaching four products on a version bump.
|
|
99
113
|
* `const` protects the binding, not the contents: without the freeze, a consumer
|
|
@@ -116,11 +130,15 @@ export declare const defaultWalletChipLabels: Readonly<{
|
|
|
116
130
|
bar: string;
|
|
117
131
|
}>;
|
|
118
132
|
/**
|
|
119
|
-
* The labels as the render consumes them: the four
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
133
|
+
* The labels as the render consumes them: the four templates presented as
|
|
134
|
+
* functions of their values, so the component writes `t.bar(…)` and never handles
|
|
135
|
+
* a template itself.
|
|
136
|
+
*
|
|
137
|
+
* This is internal -- it is not exported from `src/index.ts` -- and it stays a
|
|
138
|
+
* function shape now that `WalletChipLabels` holds only strings, for two reasons.
|
|
139
|
+
* The interpolation has to happen somewhere, and one site per label is the whole
|
|
140
|
+
* of it; and the call is where B33-875's laziness lives, so a label for an element
|
|
141
|
+
* that does not render is not interpolated at all.
|
|
124
142
|
*/
|
|
125
143
|
export interface ResolvedWalletChipLabels extends Omit<WalletChipLabels, 'grantExpiry' | 'railFigure' | 'railBalance' | 'bar'> {
|
|
126
144
|
grantExpiry: (date: string) => string;
|
|
@@ -130,14 +148,14 @@ export interface ResolvedWalletChipLabels extends Omit<WalletChipLabels, 'grantE
|
|
|
130
148
|
}
|
|
131
149
|
/**
|
|
132
150
|
* Merge a consumer's partial `labels` onto the English defaults, treating an
|
|
133
|
-
* explicitly-`undefined` field as unsupplied, and
|
|
134
|
-
*
|
|
151
|
+
* explicitly-`undefined` field as unsupplied, and present the four templates as
|
|
152
|
+
* functions of their values.
|
|
135
153
|
*
|
|
136
154
|
* A next-intl `t('...')` returns `undefined` on a missed key, so
|
|
137
155
|
* `{ name: undefined }` is what one missing translation out of thirteen looks like
|
|
138
156
|
* at the call site -- it must fall back to English, not overwrite the default
|
|
139
|
-
* with `undefined` and blank the header, unname the button, or
|
|
140
|
-
* `
|
|
141
|
-
* per-field skip does not.
|
|
157
|
+
* with `undefined` and blank the header, unname the button, or hand
|
|
158
|
+
* `fillTemplate` an `undefined` template (B33-873). A plain object spread copies
|
|
159
|
+
* the `undefined` across; a per-field skip does not.
|
|
142
160
|
*/
|
|
143
161
|
export declare function resolveWalletChipLabels(labels?: Partial<WalletChipLabels>): ResolvedWalletChipLabels;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fillTemplate as n } from "../../fill-template.js";
|
|
1
2
|
const c = Object.freeze({
|
|
2
3
|
region: "Wallet balance",
|
|
3
4
|
name: "Wallet",
|
|
@@ -5,34 +6,28 @@ const c = Object.freeze({
|
|
|
5
6
|
balanceUnknown: "Balance unknown",
|
|
6
7
|
granted: "Granted",
|
|
7
8
|
purchased: "Purchased",
|
|
8
|
-
grantExpiry: "Granted expires
|
|
9
|
+
grantExpiry: "Granted expires %date% · burns first",
|
|
9
10
|
topUp: "Top up",
|
|
10
|
-
railFigure: "
|
|
11
|
-
railBalance: "Wallet balance
|
|
11
|
+
railFigure: "%figure%k",
|
|
12
|
+
railBalance: "Wallet balance %balance%",
|
|
12
13
|
railBalanceUnknown: "Wallet balance unknown",
|
|
13
14
|
railUnknown: "unknown",
|
|
14
|
-
bar: "
|
|
15
|
+
bar: "%granted% granted and %purchased% purchased of %remaining% remaining"
|
|
15
16
|
});
|
|
16
|
-
function r
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
function o(t) {
|
|
23
|
-
const e = Object.fromEntries(
|
|
24
|
-
Object.entries(t ?? {}).filter(([, a]) => a !== void 0)
|
|
25
|
-
), n = { ...c, ...e };
|
|
17
|
+
function p(r) {
|
|
18
|
+
const l = Object.fromEntries(
|
|
19
|
+
Object.entries(r ?? {}).filter(([, e]) => e !== void 0)
|
|
20
|
+
), a = { ...c, ...l };
|
|
26
21
|
return {
|
|
27
|
-
...
|
|
28
|
-
grantExpiry: (
|
|
29
|
-
railFigure: (
|
|
30
|
-
railBalance: (
|
|
31
|
-
bar: (
|
|
22
|
+
...a,
|
|
23
|
+
grantExpiry: (e) => n(a.grantExpiry, { date: e }),
|
|
24
|
+
railFigure: (e) => n(a.railFigure, { figure: e }),
|
|
25
|
+
railBalance: (e) => n(a.railBalance, { balance: e }),
|
|
26
|
+
bar: (e, t, i) => n(a.bar, { granted: e, purchased: t, remaining: i })
|
|
32
27
|
};
|
|
33
28
|
}
|
|
34
29
|
export {
|
|
35
30
|
c as defaultWalletChipLabels,
|
|
36
|
-
|
|
31
|
+
p as resolveWalletChipLabels
|
|
37
32
|
};
|
|
38
33
|
//# sourceMappingURL=labels.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"labels.js","sources":["../../../src/components/WalletChip/labels.ts"],"sourcesContent":["// The chip is the only component in the package that renders words. Every one\n// of them lives here as a prop with an English default, so a French-default\n// consumer (the first two are) can translate the chip without the package\n// taking an i18n dependency -- CLAUDE.md, \"a component here renders what it is\n// given\". This is the same argument grantExpires and burnNote already made,\n// applied to the strings that were still English literals in the render.\n//\n// Three of those strings carry a value, and word order is not a property of\n// English: \"Granted expires 30 Sep\" puts the date where English puts it, and a\n// translation cannot be reassembled from fragments on the consumer's side. The\n// shape that solves that and nothing else is a function -- and a function is\n// the one thing that cannot be a prop of a Client Component rendered from a\n// React Server Component. React's Flight serializer walks the whole prop graph\n// and throws on any function it finds, nested or not, in production as well as\n// in development:\n//\n// Functions cannot be passed directly to Client Components unless you\n// explicitly expose it by marking it with \"use server\".\n//\n// `AppShell.wallet` is a ReactNode slot, so it is the *consumer's page* that\n// constructs `<WalletChip …/>`, and a Server Component page is the default way\n// the shell is meant to be used. A labels object that cannot cross that\n// boundary is a labels object most consumers cannot pass (B33-880).\n//\n// So each of the three takes either shape. The string form spells the value's\n// position with a `{placeholder}`, which is data all the way down and crosses\n// the boundary intact; it is the form the defaults use and the form the\n// specimen and the CHANGELOG demonstrate. The function form still works\n// unchanged, for a Client Component consumer that wants a real function --\n// next-intl's ICU plurals, an Intl.ListFormat call -- and that is a consumer\n// already on the client, where functions are ordinary props.\n//\n// B33-876 added a fourth of the same kind, `railFigure`. The rail's balance is\n// a number the `locale` prop formats -- \"4.6\" or \"4,6\" -- followed by a mark\n// that is a word and not a number: English \"k\", German \"Tsd.\", and French with\n// a space in front of it that English does not have. Intl cannot supply it (its\n// own compact notation renders German four-digit balances unabbreviated, which\n// does not fit a 56px rail -- see format.ts), so it belongs here, in the same\n// template shape, for the same reason: the consumer owns where the mark goes.\n\n/**\n * Every user-facing string the chip renders. Supplied whole -- the strings that\n * carry a value take a `{placeholder}` template (or a function) rather than\n * fragments, because word order is not a property of English and cannot be\n * reassembled on the consumer's side.\n *\n * `grantExpiry`, `railFigure`, `railBalance` and `bar` each accept two shapes.\n *\n * A **template** is a plain string marking the value's position with a\n * `{placeholder}` -- `'Granted expires {date} · burns first'`. It is data all\n * the way down, so the whole `labels` object survives the React Server Component\n * boundary and a Next App Router page can pass it. **This is the recommended\n * form**, and the one the defaults use.\n *\n * A **function** of the same values still works unchanged. It is never\n * serializable, so it only reaches the chip from a Client Component, but it is\n * the escape hatch when the sentence needs logic a template cannot express --\n * ICU plurals, an `Intl.ListFormat` call.\n *\n * In a template, a `{name}` the label does not define is left exactly as written\n * -- a locale whose copy genuinely contains braces is not mangled, and a\n * misspelled placeholder shows up in the rendering rather than silently\n * blanking. A placeholder a template omits is simply not rendered: a locale that\n * does not need a figure is allowed not to name it.\n */\nexport interface WalletChipLabels {\n /** Accessible name for the expanded chip's status region. Default \"Wallet balance\". */\n region: string\n /** The chip header. Default \"Wallet\". */\n name: string\n /** The unit rendered beside the figure. Default \"tokens\". */\n unit: string\n /** Screen-reader-only text in the figure when the balance is unknown. Default \"Balance unknown\". */\n balanceUnknown: string\n /** The granted side of the split -- granted burns first. Default \"Granted\". */\n granted: string\n /** The purchased side of the split -- purchased never expires. Default \"Purchased\". */\n purchased: string\n /**\n * The footer, around the preformatted expiry date. Supplied whole so the\n * consumer owns word order.\n *\n * Placeholder: `{date}`. Default (en) `Granted expires {date} · burns first`.\n * A function form `(date) => string` is also accepted.\n */\n grantExpiry: string | ((date: string) => string)\n /** The top-up button. Default \"Top up\". */\n topUp: string\n /**\n * The rail's abbreviated balance, around the one-decimal-thousand figure\n * (e.g. \"4.6\"). Both the visible text in the 56px rail and the figure that\n * `railBalance` is given.\n *\n * Placeholder: `{figure}`. Default (en) `{figure}k`. A function form\n * `(figure) => string` is also accepted.\n *\n * The figure itself is formatted for `locale` -- \"4.6\" in `en-US`, \"4,6\" in\n * `fr-FR` -- but the mark after it is a word, and locales disagree about it:\n * French writes \"4,6 k\" with a space, German abbreviates a thousand \"Tsd.\".\n * So the mark is a label like every other word the chip renders, and a\n * template rather than a bare suffix, because a locale is entitled to put the\n * mark somewhere other than the end (B33-876).\n */\n railFigure: string | ((figure: string) => string)\n /**\n * The rail group's accessible name, around the abbreviated balance (e.g. \"4.6k\").\n *\n * Placeholder: `{balance}`. Default (en) `Wallet balance {balance}`. A function\n * form `(balance) => string` is also accepted.\n */\n railBalance: string | ((balance: string) => string)\n /** The rail group's accessible name when the balance is unknown. Default \"Wallet balance unknown\". */\n railBalanceUnknown: string\n /** Screen-reader-only marker on the rail when the balance is unknown. Default \"unknown\". */\n railUnknown: string\n /**\n * The bar's accessible label, around the three preformatted figures. Assembled\n * whole rather than concatenated from fragments -- the shape that cannot be\n * translated at all otherwise.\n *\n * Placeholders: `{granted}`, `{purchased}`, `{remaining}`. Default (en)\n * `{granted} granted and {purchased} purchased of {remaining} remaining`. A\n * function form `(granted, purchased, remaining) => string` is also accepted.\n */\n bar: string | ((granted: string, purchased: string, remaining: string) => string)\n}\n\n/**\n * The English defaults. A consumer that passes nothing renders exactly what the\n * chip rendered before this prop existed, so the wallet baselines do not move.\n *\n * **Every field is a string.** That is what makes `labels={defaultWalletChipLabels}`\n * and `labels={{ ...defaultWalletChipLabels, name: 'Portefeuille' }}` -- the\n * pattern the specimen shows and this package's own advice recommends -- legal\n * from a Server Component. The four value-carrying defaults are templates, and\n * the rendered output is character-for-character what the function defaults\n * produced (B33-880).\n *\n * Frozen because it is a public export reaching four products on a version bump.\n * `const` protects the binding, not the contents: without the freeze, a consumer\n * aliasing it by reference -- `const mine = defaultWalletChipLabels; mine.name =\n * 'Portefeuille'` -- would silently rename every chip in the process (B33-877).\n */\nexport const defaultWalletChipLabels = Object.freeze({\n region: 'Wallet balance',\n name: 'Wallet',\n unit: 'tokens',\n balanceUnknown: 'Balance unknown',\n granted: 'Granted',\n purchased: 'Purchased',\n grantExpiry: 'Granted expires {date} · burns first',\n topUp: 'Top up',\n railFigure: '{figure}k',\n railBalance: 'Wallet balance {balance}',\n railBalanceUnknown: 'Wallet balance unknown',\n railUnknown: 'unknown',\n bar: '{granted} granted and {purchased} purchased of {remaining} remaining',\n} satisfies WalletChipLabels)\n\n/**\n * The labels as the render consumes them: the four value-carrying fields\n * normalised to functions, whichever shape the consumer supplied. The component\n * calls `t.bar(…)` either way and never learns which form it was given, so the\n * union costs the render nothing -- and, in particular, the lazy call that keeps\n * a consumer's `bar` off the degraded unknown path (B33-875) still works.\n */\nexport interface ResolvedWalletChipLabels\n extends Omit<WalletChipLabels, 'grantExpiry' | 'railFigure' | 'railBalance' | 'bar'> {\n grantExpiry: (date: string) => string\n railFigure: (figure: string) => string\n railBalance: (balance: string) => string\n bar: (granted: string, purchased: string, remaining: string) => string\n}\n\n/**\n * Fill a template's `{placeholder}`s from `values`.\n *\n * A name not in `values` is left verbatim, so misspelled or deliberate braces\n * survive to the rendering rather than disappearing. The replacement runs\n * through a callback, so a `$` in a formatted figure is a `$` and not a\n * `String.prototype.replace` substitution pattern.\n */\nfunction fillTemplate(template: string, values: Record<string, string>): string {\n return template.replace(/\\{(\\w+)\\}/g, (whole, key: string) =>\n Object.hasOwn(values, key) ? (values[key] ?? whole) : whole,\n )\n}\n\n/**\n * Merge a consumer's partial `labels` onto the English defaults, treating an\n * explicitly-`undefined` field as unsupplied, and normalise the four\n * value-carrying fields to functions.\n *\n * A next-intl `t('...')` returns `undefined` on a missed key, so\n * `{ name: undefined }` is what one missing translation out of thirteen looks like\n * at the call site -- it must fall back to English, not overwrite the default\n * with `undefined` and blank the header, unname the button, or crash on\n * `t.bar(...)` (B33-873). A plain object spread copies the `undefined` across; a\n * per-field skip does not.\n */\nexport function resolveWalletChipLabels(labels?: Partial<WalletChipLabels>): ResolvedWalletChipLabels {\n const supplied = Object.fromEntries(\n Object.entries(labels ?? {}).filter(([, value]) => value !== undefined),\n ) as Partial<WalletChipLabels>\n const merged = { ...defaultWalletChipLabels, ...supplied }\n return {\n ...merged,\n grantExpiry: (date) =>\n typeof merged.grantExpiry === 'function'\n ? merged.grantExpiry(date)\n : fillTemplate(merged.grantExpiry, { date }),\n railFigure: (figure) =>\n typeof merged.railFigure === 'function'\n ? merged.railFigure(figure)\n : fillTemplate(merged.railFigure, { figure }),\n railBalance: (balance) =>\n typeof merged.railBalance === 'function'\n ? merged.railBalance(balance)\n : fillTemplate(merged.railBalance, { balance }),\n bar: (granted, purchased, remaining) =>\n typeof merged.bar === 'function'\n ? merged.bar(granted, purchased, remaining)\n : fillTemplate(merged.bar, { granted, purchased, remaining }),\n }\n}\n"],"names":["defaultWalletChipLabels","fillTemplate","template","values","whole","key","resolveWalletChipLabels","labels","supplied","value","merged","date","figure","balance","granted","purchased","remaining"],"mappings":"AA+IO,MAAMA,IAA0B,OAAO,OAAO;AAAA,EACnD,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AAAA,EACN,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,WAAW;AAAA,EACX,aAAa;AAAA,EACb,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,oBAAoB;AAAA,EACpB,aAAa;AAAA,EACb,KAAK;AACP,CAA4B;AAyB5B,SAASC,EAAaC,GAAkBC,GAAwC;AAC9E,SAAOD,EAAS;AAAA,IAAQ;AAAA,IAAc,CAACE,GAAOC,MAC5C,OAAO,OAAOF,GAAQE,CAAG,IAAKF,EAAOE,CAAG,KAAKD,IAASA;AAAA,EAAA;AAE1D;AAcO,SAASE,EAAwBC,GAA8D;AACpG,QAAMC,IAAW,OAAO;AAAA,IACtB,OAAO,QAAQD,KAAU,EAAE,EAAE,OAAO,CAAC,CAAA,EAAGE,CAAK,MAAMA,MAAU,MAAS;AAAA,EAAA,GAElEC,IAAS,EAAE,GAAGV,GAAyB,GAAGQ,EAAA;AAChD,SAAO;AAAA,IACL,GAAGE;AAAA,IACH,aAAa,CAACC,MACZ,OAAOD,EAAO,eAAgB,aAC1BA,EAAO,YAAYC,CAAI,IACvBV,EAAaS,EAAO,aAAa,EAAE,MAAAC,GAAM;AAAA,IAC/C,YAAY,CAACC,MACX,OAAOF,EAAO,cAAe,aACzBA,EAAO,WAAWE,CAAM,IACxBX,EAAaS,EAAO,YAAY,EAAE,QAAAE,GAAQ;AAAA,IAChD,aAAa,CAACC,MACZ,OAAOH,EAAO,eAAgB,aAC1BA,EAAO,YAAYG,CAAO,IAC1BZ,EAAaS,EAAO,aAAa,EAAE,SAAAG,GAAS;AAAA,IAClD,KAAK,CAACC,GAASC,GAAWC,MACxB,OAAON,EAAO,OAAQ,aAClBA,EAAO,IAAII,GAASC,GAAWC,CAAS,IACxCf,EAAaS,EAAO,KAAK,EAAE,SAAAI,GAAS,WAAAC,GAAW,WAAAC,EAAA,CAAW;AAAA,EAAA;AAEpE;"}
|
|
1
|
+
{"version":3,"file":"labels.js","sources":["../../../src/components/WalletChip/labels.ts"],"sourcesContent":["// The chip is the only component in the package that renders words. Every one\n// of them lives here as a prop with an English default, so a French-default\n// consumer (the first two are) can translate the chip without the package\n// taking an i18n dependency -- CLAUDE.md, \"a component here renders what it is\n// given\". This is the same argument grantExpires and burnNote already made,\n// applied to the strings that were still English literals in the render.\n//\n// Three of those strings carry a value, and word order is not a property of\n// English: \"Granted expires 30 Sep\" puts the date where English puts it, and a\n// translation cannot be reassembled from fragments on the consumer's side. They\n// are supplied whole, as a template that spells the value's position with a\n// `%placeholder%`.\n//\n// ── Every field is a string, and that is the whole type (B33-1278) ───────────\n//\n// Each of those three was once `string | ((…) => string)`. The function half is\n// gone, and the reason is that it could not be used by the consumers this package\n// has. A function is the one thing that cannot be a prop of a Client Component\n// rendered from a React Server Component: React's Flight serializer walks the\n// whole prop graph and throws on any function it finds, nested or not, in\n// production as well as in development:\n//\n// Functions cannot be passed directly to Client Components unless you\n// explicitly expose it by marking it with \"use server\".\n//\n// `WalletChip` is a client module and `AppShell.wallet` is a ReactNode slot, so\n// it is the *consumer's page* that constructs `<WalletChip …/>` -- and under the\n// App Router that page is a Server Component by default, which is how both\n// consumers render it, because reading the balance is a server read. So the\n// function half was type-legal and fatal in production for the only way anyone\n// uses the component, and nothing in the type said so: `tsc` accepted it, the dev\n// server accepted it, and it threw at serialisation time (B33-880 for the first\n// half of this, B33-1278 for the rest). Score covered the hole from outside, with\n// a runtime `typeof raw === 'string'` guard and five tests walking the prop graph,\n// because -- in its own words -- neither `tsc` nor the dev server will.\n//\n// What the function form was *for* was logic a template cannot express: ICU\n// plurals, an `Intl.ListFormat` call. That capability is not lost, it moves to\n// where ICU already lives. A consumer selecting a plural form does the selection\n// in its own catalogue and passes this package the finished sentence, which is\n// what both consumers already do with next-intl. No default label in this package\n// is count-dependent, so no default needed it (see `unit` below).\n//\n// B33-876 added a fourth of the same kind, `railFigure`. The rail's balance is\n// a number the `locale` prop formats -- \"4.6\" or \"4,6\" -- followed by a mark\n// that is a word and not a number: English \"k\", German \"Tsd.\", and French with\n// a space in front of it that English does not have. Intl cannot supply it (its\n// own compact notation renders German four-digit balances unabbreviated, which\n// does not fit a 56px rail -- see format.ts), so it belongs here, in the same\n// template shape, for the same reason: the consumer owns where the mark goes.\n\nimport { fillTemplate } from '../../fill-template'\n\n/**\n * Every user-facing string the chip renders. Supplied whole -- the strings that\n * carry a value take a `%placeholder%` template rather than fragments, because\n * word order is not a property of English and cannot be reassembled on the\n * consumer's side.\n *\n * **Every field is a `string`.** `grantExpiry`, `railFigure`, `railBalance` and\n * `bar` are templates: plain strings marking each value's position with a\n * `%placeholder%` -- `'Granted expires %date% · burns first'`. So the whole\n * `labels` object is data all the way down, it survives the React Server Component\n * boundary, and a Next App Router page can build it. There is no function form:\n * it only ever worked from a Client Component and nothing in the type said so\n * (B33-1278). A sentence needing logic a template cannot express -- a plural rule,\n * an `Intl.ListFormat` call -- is computed in the consumer's own catalogue, where\n * ICU already lives, and passed here finished.\n *\n * In a template, a `%name%` the label does not define is left exactly as written\n * -- a locale whose copy genuinely pairs two percent signs is not mangled, and a\n * misspelled placeholder shows up in the rendering rather than silently\n * blanking. A placeholder a template omits is simply not rendered: a locale that\n * does not need a figure is allowed not to name it.\n *\n * **The delimiter is `%name%`, not `{name}`** (B33-1277). A brace is ICU\n * MessageFormat argument syntax, so a consumer storing these templates in its own\n * next-intl catalogue could not read them back through its own formatter. It can\n * now. `src/fill-template.ts` carries the reasoning, including why `%{name}` and\n * `{{name}}` would both have kept the collision. A `{braced}` name in a template\n * is ordinary text here and is passed through untouched.\n */\nexport interface WalletChipLabels {\n /** Accessible name for the expanded chip's status region. Default \"Wallet balance\". */\n region: string\n /** The chip header. Default \"Wallet\". */\n name: string\n /**\n * The unit rendered beside the figure. Default \"tokens\".\n *\n * A single string, and so not count-dependent: a balance of one renders \"1\n * tokens\" in English. That has been true since B33-849 and B33-1278 did not\n * change it -- `unit` never had the function form, so dropping that form took\n * nothing away here. It is also a state the chip is unlikely to reach: a\n * one-token balance is an all-but-empty wallet. If a locale needs the unit to\n * agree with the figure, the shape to add is a count-aware field, which is a\n * decision and not a refactor; nothing in the package invents one meanwhile.\n */\n unit: string\n /** Screen-reader-only text in the figure when the balance is unknown. Default \"Balance unknown\". */\n balanceUnknown: string\n /** The granted side of the split -- granted burns first. Default \"Granted\". */\n granted: string\n /** The purchased side of the split -- purchased never expires. Default \"Purchased\". */\n purchased: string\n /**\n * The footer, around the preformatted expiry date. Supplied whole so the\n * consumer owns word order.\n *\n * Placeholder: `%date%`. Default (en) `Granted expires %date% · burns first`.\n */\n grantExpiry: string\n /** The top-up button. Default \"Top up\". */\n topUp: string\n /**\n * The rail's abbreviated balance, around the one-decimal-thousand figure\n * (e.g. \"4.6\"). Both the visible text in the 56px rail and the figure that\n * `railBalance` is given.\n *\n * Placeholder: `%figure%`. Default (en) `%figure%k`.\n *\n * The figure itself is formatted for `locale` -- \"4.6\" in `en-US`, \"4,6\" in\n * `fr-FR` -- but the mark after it is a word, and locales disagree about it:\n * French writes \"4,6 k\" with a space, German abbreviates a thousand \"Tsd.\".\n * So the mark is a label like every other word the chip renders, and a\n * template rather than a bare suffix, because a locale is entitled to put the\n * mark somewhere other than the end (B33-876).\n */\n railFigure: string\n /**\n * The rail group's accessible name, around the abbreviated balance (e.g. \"4.6k\").\n *\n * Placeholder: `%balance%`. Default (en) `Wallet balance %balance%`.\n */\n railBalance: string\n /** The rail group's accessible name when the balance is unknown. Default \"Wallet balance unknown\". */\n railBalanceUnknown: string\n /** Screen-reader-only marker on the rail when the balance is unknown. Default \"unknown\". */\n railUnknown: string\n /**\n * The bar's accessible label, around the three preformatted figures. Assembled\n * whole rather than concatenated from fragments -- the shape that cannot be\n * translated at all otherwise.\n *\n * Placeholders: `%granted%`, `%purchased%`, `%remaining%`. Default (en)\n * `%granted% granted and %purchased% purchased of %remaining% remaining`.\n */\n bar: string\n}\n\n/**\n * The English defaults. A consumer that passes nothing renders exactly what the\n * chip rendered before this prop existed, so the wallet baselines do not move.\n *\n * **Every field is a string, and now by construction.** That is what makes\n * `labels={defaultWalletChipLabels}` and\n * `labels={{ ...defaultWalletChipLabels, name: 'Portefeuille' }}` -- the pattern\n * the specimen shows and this package's own advice recommends -- legal from a\n * Server Component (B33-880). It used to be a runtime fact pinned by a test,\n * because `WalletChipLabels` admitted functions on four fields and `satisfies`\n * could not tell the two apart (B33-1029). With the union gone the compiler\n * carries it: a function here is a type error at the `satisfies`, and\n * `tests/wallet-chip-labels.type-test.ts` asserts the all-strings shape of the\n * interface itself so the guarantee cannot be lost by widening one field.\n *\n * Frozen because it is a public export reaching four products on a version bump.\n * `const` protects the binding, not the contents: without the freeze, a consumer\n * aliasing it by reference -- `const mine = defaultWalletChipLabels; mine.name =\n * 'Portefeuille'` -- would silently rename every chip in the process (B33-877).\n */\nexport const defaultWalletChipLabels = Object.freeze({\n region: 'Wallet balance',\n name: 'Wallet',\n unit: 'tokens',\n balanceUnknown: 'Balance unknown',\n granted: 'Granted',\n purchased: 'Purchased',\n grantExpiry: 'Granted expires %date% · burns first',\n topUp: 'Top up',\n railFigure: '%figure%k',\n railBalance: 'Wallet balance %balance%',\n railBalanceUnknown: 'Wallet balance unknown',\n railUnknown: 'unknown',\n bar: '%granted% granted and %purchased% purchased of %remaining% remaining',\n} satisfies WalletChipLabels)\n\n/**\n * The labels as the render consumes them: the four templates presented as\n * functions of their values, so the component writes `t.bar(…)` and never handles\n * a template itself.\n *\n * This is internal -- it is not exported from `src/index.ts` -- and it stays a\n * function shape now that `WalletChipLabels` holds only strings, for two reasons.\n * The interpolation has to happen somewhere, and one site per label is the whole\n * of it; and the call is where B33-875's laziness lives, so a label for an element\n * that does not render is not interpolated at all.\n */\nexport interface ResolvedWalletChipLabels\n extends Omit<WalletChipLabels, 'grantExpiry' | 'railFigure' | 'railBalance' | 'bar'> {\n grantExpiry: (date: string) => string\n railFigure: (figure: string) => string\n railBalance: (balance: string) => string\n bar: (granted: string, purchased: string, remaining: string) => string\n}\n\n/**\n * Merge a consumer's partial `labels` onto the English defaults, treating an\n * explicitly-`undefined` field as unsupplied, and present the four templates as\n * functions of their values.\n *\n * A next-intl `t('...')` returns `undefined` on a missed key, so\n * `{ name: undefined }` is what one missing translation out of thirteen looks like\n * at the call site -- it must fall back to English, not overwrite the default\n * with `undefined` and blank the header, unname the button, or hand\n * `fillTemplate` an `undefined` template (B33-873). A plain object spread copies\n * the `undefined` across; a per-field skip does not.\n */\nexport function resolveWalletChipLabels(labels?: Partial<WalletChipLabels>): ResolvedWalletChipLabels {\n const supplied = Object.fromEntries(\n Object.entries(labels ?? {}).filter(([, value]) => value !== undefined),\n ) as Partial<WalletChipLabels>\n const merged = { ...defaultWalletChipLabels, ...supplied }\n return {\n ...merged,\n grantExpiry: (date) => fillTemplate(merged.grantExpiry, { date }),\n railFigure: (figure) => fillTemplate(merged.railFigure, { figure }),\n railBalance: (balance) => fillTemplate(merged.railBalance, { balance }),\n bar: (granted, purchased, remaining) => fillTemplate(merged.bar, { granted, purchased, remaining }),\n }\n}\n"],"names":["defaultWalletChipLabels","resolveWalletChipLabels","labels","supplied","value","merged","date","fillTemplate","figure","balance","granted","purchased","remaining"],"mappings":";AA0KO,MAAMA,IAA0B,OAAO,OAAO;AAAA,EACnD,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AAAA,EACN,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,WAAW;AAAA,EACX,aAAa;AAAA,EACb,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,oBAAoB;AAAA,EACpB,aAAa;AAAA,EACb,KAAK;AACP,CAA4B;AAiCrB,SAASC,EAAwBC,GAA8D;AACpG,QAAMC,IAAW,OAAO;AAAA,IACtB,OAAO,QAAQD,KAAU,EAAE,EAAE,OAAO,CAAC,CAAA,EAAGE,CAAK,MAAMA,MAAU,MAAS;AAAA,EAAA,GAElEC,IAAS,EAAE,GAAGL,GAAyB,GAAGG,EAAA;AAChD,SAAO;AAAA,IACL,GAAGE;AAAA,IACH,aAAa,CAACC,MAASC,EAAaF,EAAO,aAAa,EAAE,MAAAC,GAAM;AAAA,IAChE,YAAY,CAACE,MAAWD,EAAaF,EAAO,YAAY,EAAE,QAAAG,GAAQ;AAAA,IAClE,aAAa,CAACC,MAAYF,EAAaF,EAAO,aAAa,EAAE,SAAAI,GAAS;AAAA,IACtE,KAAK,CAACC,GAASC,GAAWC,MAAcL,EAAaF,EAAO,KAAK,EAAE,SAAAK,GAAS,WAAAC,GAAW,WAAAC,EAAA,CAAW;AAAA,EAAA;AAEtG;"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fill a template's `%placeholder%`s from `values`.
|
|
3
|
+
*
|
|
4
|
+
* A name not in `values` is left verbatim, so a misspelling, a deliberate pair
|
|
5
|
+
* of percent signs, or a `{braced}` name meant for someone else's formatter all
|
|
6
|
+
* survive to the rendering rather than disappearing. The replacement runs
|
|
7
|
+
* through a callback, so a `$` in a formatted figure is a `$` and not a
|
|
8
|
+
* `String.prototype.replace` substitution pattern.
|
|
9
|
+
*/
|
|
10
|
+
export declare function fillTemplate(template: string, values: Record<string, string>): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fill-template.js","sources":["../src/fill-template.ts"],"sourcesContent":["/*\n * The one `%placeholder%` filler, shared by every labels module.\n *\n * Flat at `src/` root beside `anchored-overlay.ts` and `nav-height-budget.ts`,\n * the precedent for logic shared across components, and absent from\n * `src/index.ts` -- consumers get components, not helpers.\n *\n * It lived privately inside `WalletChip/labels.ts` until B33-494 needed the same\n * shape for the NotificationCenter's `%source% · %time%` line. Copying it would\n * have been the third labels module re-deciding a contract two had already\n * settled, which is exactly what B33-1007 is open about: that ticket found\n * `AppSwitcher`'s labels diverging from `WalletChip`'s in three ways while its\n * own comment claimed parity, and `CLAUDE.md` names the outcome -- \"two\n * almost-identical components is the failure mode this whole repo exists to\n * end\". A second copy of this function would be a fourth dialect of the same\n * five lines, differing the day one of them is fixed.\n *\n * ── Why the delimiter is `%name%` and not `{name}` (B33-1277) ────────────────\n *\n * It was `{name}` until B33-1277, and `{\\w+}` is *exactly* ICU MessageFormat\n * argument syntax -- the syntax next-intl, FormatJS and every other ICU-backed\n * library in the Next.js ecosystem parses. So a consumer that stores these\n * default templates in the message catalogue it already has cannot read them\n * back through the function it already uses: ICU sees `{date}`, treats it as an\n * argument it was not given, and renders an error string where the sentence\n * should be. Score had to write a second accessor around its own formatter to\n * get the raw string out (`boostscore/components/app-shell/wallet-chip.tsx`) --\n * two accessors for one label bag, split on which fields happen to carry a\n * value, with the wrong choice showing formatter output in the sidebar.\n *\n * The fix is a delimiter holding no brace at all, which rules out both spellings\n * the ticket floated:\n *\n * - `%{date}` still holds `{date}`. ICU does not read `%` as an escape, so it\n * parses the argument and the failure is unchanged. The ticket's aside that\n * \"ICU ignores it\" is the one claim in it that does not hold.\n * - `{{date}}` is worse, as the ticket itself says: `{` is not a legal first\n * character of an ICU argument name, so such a catalogue fails to parse at\n * all rather than spoiling one sentence.\n *\n * `%name%` also clears every other formatter a Boost33 consumer might put copy\n * through, because all of them claim the double brace: i18next, Mustache and\n * Handlebars, and Vue's, Angular's and Svelte's template syntax. And it reads as\n * one unit in a translator's editor, being delimited at both ends -- gettext's\n * `%(name)s` leaves a bare `s` against the following word, and translators drop\n * it.\n *\n * No default in this package contains a `%`, so none of its own copy can be\n * mistaken for a placeholder; and an unknown name is left verbatim (below), so a\n * locale whose prose genuinely pairs two percent signs is not mangled, and\n * neither is an ICU `{argument}` a consumer means its own formatter to fill.\n */\n\n/**\n * Fill a template's `%placeholder%`s from `values`.\n *\n * A name not in `values` is left verbatim, so a misspelling, a deliberate pair\n * of percent signs, or a `{braced}` name meant for someone else's formatter all\n * survive to the rendering rather than disappearing. The replacement runs\n * through a callback, so a `$` in a formatted figure is a `$` and not a\n * `String.prototype.replace` substitution pattern.\n */\nexport function fillTemplate(template: string, values: Record<string, string>): string {\n return template.replace(/%(\\w+)%/g, (whole, key: string) =>\n Object.hasOwn(values, key) ? (values[key] ?? whole) : whole,\n )\n}\n"],"names":["fillTemplate","template","values","whole","key"],"mappings":"AA8DO,SAASA,EAAaC,GAAkBC,GAAwC;AACrF,SAAOD,EAAS;AAAA,IAAQ;AAAA,IAAY,CAACE,GAAOC,MAC1C,OAAO,OAAOF,GAAQE,CAAG,IAAKF,EAAOE,CAAG,KAAKD,IAASA;AAAA,EAAA;AAE1D;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,12 +6,12 @@ export { Wordmark } from './components/Wordmark/Wordmark';
|
|
|
6
6
|
export type { WordmarkProps } from './components/Wordmark/Wordmark';
|
|
7
7
|
export { Boost33Mark, BOOST33_MARK_MIN_LEGIBLE_PX } from './components/Boost33Mark/Boost33Mark';
|
|
8
8
|
export type { Boost33MarkProps } from './components/Boost33Mark/Boost33Mark';
|
|
9
|
-
export { ProductMark,
|
|
10
|
-
export type { ProductMarkProps
|
|
9
|
+
export { ProductMark, hasProductMark } from './components/ProductMark/ProductMark';
|
|
10
|
+
export type { ProductMarkProps } from './components/ProductMark/ProductMark';
|
|
11
11
|
export { NavItem } from './components/NavItem/NavItem';
|
|
12
12
|
export type { NavItemProps } from './components/NavItem/NavItem';
|
|
13
13
|
export { WalletChip } from './components/WalletChip/WalletChip';
|
|
14
|
-
export type { WalletChipProps } from './components/WalletChip/WalletChip';
|
|
14
|
+
export type { WalletChipProps, WalletChipKnownProps, WalletChipUnknownProps } from './components/WalletChip/WalletChip';
|
|
15
15
|
export { defaultWalletChipLabels } from './components/WalletChip/labels';
|
|
16
16
|
export type { WalletChipLabels } from './components/WalletChip/labels';
|
|
17
17
|
export { TopBar } from './components/TopBar/TopBar';
|
|
@@ -24,5 +24,11 @@ export { AppSwitcher } from './components/AppSwitcher/AppSwitcher';
|
|
|
24
24
|
export type { AppSwitcherProps, AppSwitcherProduct, AppSwitcherHub, AppSwitcherAccount, } from './components/AppSwitcher/AppSwitcher';
|
|
25
25
|
export { defaultAppSwitcherLabels } from './components/AppSwitcher/labels';
|
|
26
26
|
export type { AppSwitcherLabels } from './components/AppSwitcher/labels';
|
|
27
|
-
export {
|
|
27
|
+
export { DotMatrixLoader } from './components/DotMatrixLoader/DotMatrixLoader';
|
|
28
|
+
export type { DotMatrixLoaderProps } from './components/DotMatrixLoader/DotMatrixLoader';
|
|
29
|
+
export { LaunchLoader } from './components/LaunchLoader/LaunchLoader';
|
|
30
|
+
export type { LaunchLoaderProps } from './components/LaunchLoader/LaunchLoader';
|
|
31
|
+
export { launchBackdropCss } from './launch-gradient';
|
|
32
|
+
export type { LaunchGradientStops } from './launch-gradient';
|
|
33
|
+
export { checkNavHeightBudget, navContentHeight, navBudgetForViewport } from './nav-height-budget';
|
|
28
34
|
export type { NavHeightBudget, NavRowCosts, NavSectionShape } from './nav-height-budget';
|
package/dist/index.js
CHANGED
|
@@ -1,45 +1,42 @@
|
|
|
1
1
|
/* empty css */
|
|
2
2
|
import { Sidebar as t } from "./components/Sidebar/Sidebar.js";
|
|
3
|
-
import { NavSection as
|
|
4
|
-
import { Wordmark as
|
|
5
|
-
import { BOOST33_MARK_MIN_LEGIBLE_PX as
|
|
6
|
-
import {
|
|
7
|
-
import { NavItem as
|
|
8
|
-
import { WalletChip as
|
|
9
|
-
import { defaultWalletChipLabels as
|
|
10
|
-
import { TopBar as
|
|
11
|
-
import { PageHeader as
|
|
12
|
-
import { AppShell as
|
|
13
|
-
import { AppSwitcher as
|
|
14
|
-
import { defaultAppSwitcherLabels as
|
|
15
|
-
import {
|
|
3
|
+
import { NavSection as a } from "./components/NavSection/NavSection.js";
|
|
4
|
+
import { Wordmark as f } from "./components/Wordmark/Wordmark.js";
|
|
5
|
+
import { BOOST33_MARK_MIN_LEGIBLE_PX as d, Boost33Mark as h } from "./components/Boost33Mark/Boost33Mark.js";
|
|
6
|
+
import { ProductMark as l, hasProductMark as c } from "./components/ProductMark/ProductMark.js";
|
|
7
|
+
import { NavItem as n } from "./components/NavItem/NavItem.js";
|
|
8
|
+
import { WalletChip as L } from "./components/WalletChip/WalletChip.js";
|
|
9
|
+
import { defaultWalletChipLabels as s } from "./components/WalletChip/labels.js";
|
|
10
|
+
import { TopBar as S } from "./components/TopBar/TopBar.js";
|
|
11
|
+
import { PageHeader as v } from "./components/PageHeader/PageHeader.js";
|
|
12
|
+
import { AppShell as C } from "./components/AppShell/AppShell.js";
|
|
13
|
+
import { AppSwitcher as P } from "./components/AppSwitcher/AppSwitcher.js";
|
|
14
|
+
import { defaultAppSwitcherLabels as b } from "./components/AppSwitcher/labels.js";
|
|
15
|
+
import { DotMatrixLoader as H } from "./components/DotMatrixLoader/DotMatrixLoader.js";
|
|
16
|
+
import { LaunchLoader as W } from "./components/LaunchLoader/LaunchLoader.js";
|
|
17
|
+
import { launchBackdropCss as O } from "./launch-gradient.js";
|
|
18
|
+
import { checkNavHeightBudget as D, navBudgetForViewport as F, navContentHeight as G } from "./nav-height-budget.js";
|
|
16
19
|
export {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
R as NAV_SHELL_FIXED_PX,
|
|
28
|
-
k as NAV_SHELL_PACKAGE_FIXED_PX,
|
|
29
|
-
L as NavItem,
|
|
30
|
-
p as NavSection,
|
|
31
|
-
N as PRODUCT_SLUGS,
|
|
32
|
-
I as PageHeader,
|
|
33
|
-
x as ProductMark,
|
|
20
|
+
C as AppShell,
|
|
21
|
+
P as AppSwitcher,
|
|
22
|
+
d as BOOST33_MARK_MIN_LEGIBLE_PX,
|
|
23
|
+
h as Boost33Mark,
|
|
24
|
+
H as DotMatrixLoader,
|
|
25
|
+
W as LaunchLoader,
|
|
26
|
+
n as NavItem,
|
|
27
|
+
a as NavSection,
|
|
28
|
+
v as PageHeader,
|
|
29
|
+
l as ProductMark,
|
|
34
30
|
t as Sidebar,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
31
|
+
S as TopBar,
|
|
32
|
+
L as WalletChip,
|
|
33
|
+
f as Wordmark,
|
|
34
|
+
D as checkNavHeightBudget,
|
|
35
|
+
b as defaultAppSwitcherLabels,
|
|
36
|
+
s as defaultWalletChipLabels,
|
|
37
|
+
c as hasProductMark,
|
|
38
|
+
O as launchBackdropCss,
|
|
39
|
+
F as navBudgetForViewport,
|
|
40
|
+
G as navContentHeight
|
|
44
41
|
};
|
|
45
42
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated Ignored since 0.5.1 (B33-1401). The launch screen is the same
|
|
3
|
+
* Boost33 background for every product, built into `LaunchLoader`, so there are
|
|
4
|
+
* no per-product stops to pass. The type stays so a consumer on `^0.5.0` that
|
|
5
|
+
* still declares its colour map keeps compiling; drop the map and stop passing
|
|
6
|
+
* it.
|
|
7
|
+
*/
|
|
8
|
+
export type LaunchGradientStops = readonly [string, string, string, string];
|
|
9
|
+
/**
|
|
10
|
+
* The launch screen's ground as a raw CSS string, for a consumer to inline in a
|
|
11
|
+
* `<style>` block with no stylesheet request.
|
|
12
|
+
*
|
|
13
|
+
* LaunchLoader paints its own background -- black, with one orange glow --
|
|
14
|
+
* from the package stylesheet, identically on both sides of a hub-to-product
|
|
15
|
+
* navigation. This paints the one thing that can show before that stylesheet
|
|
16
|
+
* and the markup arrive: the root in the same black, so the first frame is
|
|
17
|
+
* already the screen's ground rather than white.
|
|
18
|
+
*
|
|
19
|
+
* `stops` is ignored and kept only so the 0.5.0 call shape,
|
|
20
|
+
* `launchBackdropCss(stops)` or `launchBackdropCss(stops, selector)`, still
|
|
21
|
+
* compiles; `launchBackdropCss()` is the call to write now. `selector` defaults
|
|
22
|
+
* to `html`.
|
|
23
|
+
*
|
|
24
|
+
* The `(prefers-contrast)` branch paints the same black the default does. It is
|
|
25
|
+
* redundant today and kept on purpose: it is the branch the component honours
|
|
26
|
+
* too, and keeping the output's shape means the day the two paths differ again
|
|
27
|
+
* is a value change here, not a new rule a consumer has never inlined.
|
|
28
|
+
*
|
|
29
|
+
* The selector is the one thing still interpolated. It is the caller's own
|
|
30
|
+
* literal, never served data -- the stops were the value that would have come
|
|
31
|
+
* from a registry, and they are no longer written anywhere -- so it is not
|
|
32
|
+
* checked. Do not feed it from a request.
|
|
33
|
+
*/
|
|
34
|
+
export declare function launchBackdropCss(
|
|
35
|
+
/** @deprecated Ignored since 0.5.1. Call `launchBackdropCss()`. */
|
|
36
|
+
_stops?: LaunchGradientStops, selector?: string): string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const r = "var(--color-neutral-1000,black)";
|
|
2
|
+
function n(c, o = "html") {
|
|
3
|
+
return `${o}{min-height:100%;background-color:${r}}@media (prefers-contrast){${o}{background-color:${r}}}`;
|
|
4
|
+
}
|
|
5
|
+
export {
|
|
6
|
+
n as launchBackdropCss
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=launch-gradient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launch-gradient.js","sources":["../src/launch-gradient.ts"],"sourcesContent":["/*\n * LaunchLoader's backdrop CSS, as text (B33-1389, B33-1401). Beside the\n * components rather than inside one, the way nav-height-budget.ts is: it\n * renders nothing, and what it assembles is CSS a consumer inlines, not copy a\n * person reads -- so it sits outside the no-hardcoded-strings scan, which reads\n * src/components for rendered words.\n */\n\n/**\n * @deprecated Ignored since 0.5.1 (B33-1401). The launch screen is the same\n * Boost33 background for every product, built into `LaunchLoader`, so there are\n * no per-product stops to pass. The type stays so a consumer on `^0.5.0` that\n * still declares its colour map keeps compiling; drop the map and stop passing\n * it.\n */\nexport type LaunchGradientStops = readonly [string, string, string, string]\n\n/*\n * The ground every launch screen is painted on: --color-neutral-1000, the\n * charte's primary black, which tokens.css reserves for \"the logo and dark\n * ground\". Written as the token with a keyword fallback, because this string is\n * inlined in the first HTML chunk precisely so it paints before any stylesheet\n * arrives -- and before one arrives, the token does not exist. `black` is the\n * same value tokens.css declares for it (tests/launch-loader.test.tsx holds the\n * two together); a keyword rather than a hex because no hex lives in src/\n * outside the token sheets (tests/guards.test.ts).\n */\nconst LAUNCH_GROUND = 'var(--color-neutral-1000,black)'\n\n/**\n * The launch screen's ground as a raw CSS string, for a consumer to inline in a\n * `<style>` block with no stylesheet request.\n *\n * LaunchLoader paints its own background -- black, with one orange glow --\n * from the package stylesheet, identically on both sides of a hub-to-product\n * navigation. This paints the one thing that can show before that stylesheet\n * and the markup arrive: the root in the same black, so the first frame is\n * already the screen's ground rather than white.\n *\n * `stops` is ignored and kept only so the 0.5.0 call shape,\n * `launchBackdropCss(stops)` or `launchBackdropCss(stops, selector)`, still\n * compiles; `launchBackdropCss()` is the call to write now. `selector` defaults\n * to `html`.\n *\n * The `(prefers-contrast)` branch paints the same black the default does. It is\n * redundant today and kept on purpose: it is the branch the component honours\n * too, and keeping the output's shape means the day the two paths differ again\n * is a value change here, not a new rule a consumer has never inlined.\n *\n * The selector is the one thing still interpolated. It is the caller's own\n * literal, never served data -- the stops were the value that would have come\n * from a registry, and they are no longer written anywhere -- so it is not\n * checked. Do not feed it from a request.\n */\nexport function launchBackdropCss(\n /** @deprecated Ignored since 0.5.1. Call `launchBackdropCss()`. */\n _stops?: LaunchGradientStops,\n selector = 'html',\n): string {\n return (\n `${selector}{min-height:100%;background-color:${LAUNCH_GROUND}}` +\n `@media (prefers-contrast){${selector}{background-color:${LAUNCH_GROUND}}}`\n )\n}\n"],"names":["LAUNCH_GROUND","launchBackdropCss","_stops","selector"],"mappings":"AA2BA,MAAMA,IAAgB;AA2Bf,SAASC,EAEdC,GACAC,IAAW,QACH;AACR,SACE,GAAGA,CAAQ,qCAAqCH,CAAa,8BAChCG,CAAQ,qBAAqBH,CAAa;AAE3E;"}
|