@multiplatform.one/forms 7.88.6 → 7.88.8
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/dist/cjs/Button/index.cjs +40 -18
- package/dist/cjs/Button/index.native.js +47 -25
- package/dist/cjs/Button/index.native.js.map +1 -1
- package/dist/esm/Button/index.mjs +40 -18
- package/dist/esm/Button/index.mjs.map +1 -1
- package/dist/esm/Button/index.native.js +47 -25
- package/dist/esm/Button/index.native.js.map +1 -1
- package/dist/jsx/Button/index.mjs +40 -18
- package/dist/jsx/Button/index.mjs.map +1 -1
- package/dist/jsx/Button/index.native.js +47 -25
- package/dist/jsx/Button/index.native.js.map +1 -1
- package/package.json +8 -8
- package/src/Button/Button.accentInk.spec.tsx +17 -3
- package/src/Button/Button.iconButton.spec.tsx +55 -14
- package/src/Button/Button.outlinedAccentInk.spec.tsx +13 -1
- package/src/Button/Button.outlinedInkFlip.spec.tsx +305 -0
- package/src/Button/Button.scopeFlip.native.spec.tsx +208 -0
- package/src/Button/Button.scopeFlip.spec.tsx +350 -0
- package/src/Button/index.tsx +43 -20
- package/types/Button/index.d.ts.map +1 -1
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vitest-environment jsdom
|
|
3
|
+
*
|
|
4
|
+
* FZ-9617: a Button read some of its paints from the host scope's theme at
|
|
5
|
+
* render and painted the literal: the keyboard ring, a framed circle's plate
|
|
6
|
+
* ink, an icon button's hover and press washes and the bar glyph tint. A
|
|
7
|
+
* light/dark flip that moves only the root's t_light/t_dark class never
|
|
8
|
+
* re-renders the Button, so each kept the old scheme's colour. On web they now
|
|
9
|
+
* paint `var(--mp-host-*)` aliases that a span outside the intent and
|
|
10
|
+
* component themes declares.
|
|
11
|
+
*
|
|
12
|
+
* jsdom cascades no Tamagui CSS, so this reads what each node asks for: the
|
|
13
|
+
* atomic class, the injected rule behind it, the `--mp-host-*` alias on the
|
|
14
|
+
* ancestor that declares it, and for a `var(--token)` the house theme table of
|
|
15
|
+
* the scope that declares it.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { PlusIcon } from "@phosphor-icons/react";
|
|
19
|
+
import { cleanup, render, screen } from "@testing-library/react";
|
|
20
|
+
import {
|
|
21
|
+
createDefaultThemeConfig,
|
|
22
|
+
createThemesBuilder,
|
|
23
|
+
defaultAccentTheme,
|
|
24
|
+
defaultBaseTheme,
|
|
25
|
+
defaultBuilderOptions,
|
|
26
|
+
type Knobs,
|
|
27
|
+
Preset,
|
|
28
|
+
Surface,
|
|
29
|
+
} from "@multiplatform.one/theme";
|
|
30
|
+
import type { ReactElement } from "react";
|
|
31
|
+
import { TamaguiProvider, YStack } from "tamagui";
|
|
32
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
33
|
+
import { Button } from "./index";
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
cleanup();
|
|
37
|
+
document.documentElement.className = "";
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const houseConfig = createDefaultThemeConfig();
|
|
41
|
+
const houseThemes = createThemesBuilder(
|
|
42
|
+
defaultBaseTheme,
|
|
43
|
+
defaultAccentTheme,
|
|
44
|
+
defaultBuilderOptions,
|
|
45
|
+
).themes() as Record<string, Record<string, string>>;
|
|
46
|
+
|
|
47
|
+
const FLIPS = [
|
|
48
|
+
["light", "dark"],
|
|
49
|
+
["dark", "light"],
|
|
50
|
+
] as const;
|
|
51
|
+
type Scheme = (typeof FLIPS)[number][number];
|
|
52
|
+
|
|
53
|
+
function* styleRules(rules: CSSRuleList): Generator<CSSStyleRule> {
|
|
54
|
+
for (const rule of Array.from(rules)) {
|
|
55
|
+
if (rule instanceof CSSStyleRule) yield rule;
|
|
56
|
+
else if ("cssRules" in rule) yield* styleRules((rule as CSSGroupingRule).cssRules);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function injectedRuleValue(className: string, property: string): string {
|
|
61
|
+
const selector = new RegExp(`\\.${className.replace(/[^\w-]/g, "\\$&")}(?![\\w-])`);
|
|
62
|
+
for (const sheet of Array.from(document.styleSheets)) {
|
|
63
|
+
for (const rule of styleRules(sheet.cssRules)) {
|
|
64
|
+
if (!selector.test(rule.selectorText)) continue;
|
|
65
|
+
const value = rule.style.getPropertyValue(property);
|
|
66
|
+
if (value) return value.trim();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
throw new Error(`no injected ${property} rule for .${className}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** The theme a node's custom properties resolve in: its scheme and sub-theme path. */
|
|
73
|
+
function themeOf(el: Element): string {
|
|
74
|
+
let sub: string | null = null;
|
|
75
|
+
for (let node: Element | null = el; node; node = node.parentElement) {
|
|
76
|
+
const classes = Array.from(node.classList);
|
|
77
|
+
const scheme = classes.includes("t_dark")
|
|
78
|
+
? "dark"
|
|
79
|
+
: classes.includes("t_light")
|
|
80
|
+
? "light"
|
|
81
|
+
: null;
|
|
82
|
+
const names = classes
|
|
83
|
+
.filter((c) => c.startsWith("t_") && !["t_sub_theme", "t_light", "t_dark"].includes(c))
|
|
84
|
+
.map((c) => c.slice(2));
|
|
85
|
+
if (sub === null && names.length) sub = names.sort((a, b) => b.length - a.length)[0];
|
|
86
|
+
if (scheme) return sub ? `${scheme}_${sub}` : scheme;
|
|
87
|
+
}
|
|
88
|
+
throw new Error("no scheme above this node");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function tokenIn(themeName: string, token: string): string {
|
|
92
|
+
const parts = themeName.split("_");
|
|
93
|
+
for (let end = parts.length; end > 0; end--) {
|
|
94
|
+
const hit = houseThemes[parts.slice(0, end).join("_")]?.[token];
|
|
95
|
+
if (hit) return hit;
|
|
96
|
+
}
|
|
97
|
+
throw new Error(`--${token} resolves nowhere in ${themeName}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** What `value` paints on `el`, following aliases to the scope that declares them. */
|
|
101
|
+
function resolveOn(el: Element, value: string): string {
|
|
102
|
+
const name = value.match(/^var\((--[\w-]+)\)$/)?.[1];
|
|
103
|
+
if (!name) return value;
|
|
104
|
+
for (let node: Element | null = el; node; node = node.parentElement) {
|
|
105
|
+
const declared = (node as HTMLElement).style?.getPropertyValue(name);
|
|
106
|
+
if (declared) return resolveOn(node, declared.trim());
|
|
107
|
+
}
|
|
108
|
+
return tokenIn(themeOf(el), name.slice(2));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const PSEUDO = /^_[a-zA-Z]+-0(hover|active|focus|press|focus-visible)-/;
|
|
112
|
+
|
|
113
|
+
/** What `el` paints for one atomic property, at rest or under `pseudo`. */
|
|
114
|
+
function painted(el: Element, prefix: string, property: string, pseudo?: string): string | null {
|
|
115
|
+
const cls = Array.from(el.classList).find((c) =>
|
|
116
|
+
pseudo ? c.startsWith(`${prefix}0${pseudo}-`) : c.startsWith(prefix) && !PSEUDO.test(c),
|
|
117
|
+
);
|
|
118
|
+
return cls ? resolveOn(el, injectedRuleValue(cls, property)) : null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** An svg's fill, following `currentColor` to the CSS colour it inherits. */
|
|
122
|
+
function glyphFill(svg: Element): string | null {
|
|
123
|
+
const fill = svg.getAttribute("fill");
|
|
124
|
+
if (fill !== "currentColor") return fill;
|
|
125
|
+
for (let node = svg.parentElement; node; node = node.parentElement) {
|
|
126
|
+
const ink = painted(node, "_col-", "color");
|
|
127
|
+
if (ink) return ink;
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function frameOf(name: string): HTMLElement {
|
|
133
|
+
return (screen.queryByRole("button", { name }) ??
|
|
134
|
+
screen.getByText(name).closest("[role=button]")) as HTMLElement;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function swapRootScheme(from: Scheme, to: Scheme) {
|
|
138
|
+
const root = document.querySelector("[data-scope-root]");
|
|
139
|
+
for (const el of Array.from(document.querySelectorAll(`.t_${from}`))) {
|
|
140
|
+
if (root && !el.contains(root)) continue;
|
|
141
|
+
el.classList.replace(`t_${from}`, `t_${to}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function mount(scheme: Scheme, tree: ReactElement) {
|
|
146
|
+
return render(
|
|
147
|
+
<TamaguiProvider config={houseConfig.tamagui} defaultTheme={scheme} disableInjectCSS>
|
|
148
|
+
<YStack backgroundColor="$background" data-scope-root="">
|
|
149
|
+
{tree}
|
|
150
|
+
</YStack>
|
|
151
|
+
</TamaguiProvider>,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function expectFlipFollows<T>(from: Scheme, to: Scheme, tree: ReactElement, read: () => T): T {
|
|
156
|
+
mount(to, tree);
|
|
157
|
+
const fresh = read();
|
|
158
|
+
cleanup();
|
|
159
|
+
mount(from, tree);
|
|
160
|
+
swapRootScheme(from, to);
|
|
161
|
+
try {
|
|
162
|
+
expect(read()).toEqual(fresh);
|
|
163
|
+
} finally {
|
|
164
|
+
swapRootScheme(to, from);
|
|
165
|
+
}
|
|
166
|
+
return fresh;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const RING_BUTTONS: [string, ReactElement][] = [
|
|
170
|
+
["plain", <Button key="b">Go</Button>],
|
|
171
|
+
[
|
|
172
|
+
"outlined",
|
|
173
|
+
<Button key="b" outlined>
|
|
174
|
+
Go
|
|
175
|
+
</Button>,
|
|
176
|
+
],
|
|
177
|
+
[
|
|
178
|
+
"chromeless",
|
|
179
|
+
<Button key="b" chromeless>
|
|
180
|
+
Go
|
|
181
|
+
</Button>,
|
|
182
|
+
],
|
|
183
|
+
[
|
|
184
|
+
"accent",
|
|
185
|
+
<Button key="b" accent>
|
|
186
|
+
Go
|
|
187
|
+
</Button>,
|
|
188
|
+
],
|
|
189
|
+
[
|
|
190
|
+
"error",
|
|
191
|
+
<Button key="b" error>
|
|
192
|
+
Go
|
|
193
|
+
</Button>,
|
|
194
|
+
],
|
|
195
|
+
[
|
|
196
|
+
'theme="accent" outlined',
|
|
197
|
+
<Button key="b" theme="accent" outlined>
|
|
198
|
+
Go
|
|
199
|
+
</Button>,
|
|
200
|
+
],
|
|
201
|
+
["icon-only", <Button key="b" icon={PlusIcon} aria-label="Go" />],
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
describe("FZ-9617 DG-A11Y-01: a focused Button's ring follows a class-only root flip", () => {
|
|
205
|
+
for (const [from, to] of FLIPS) {
|
|
206
|
+
for (const [name, button] of RING_BUTTONS) {
|
|
207
|
+
it(`${from} -> ${to} ${name}: the ring is the host scope's $outlineColor`, () => {
|
|
208
|
+
const ring = expectFlipFollows(from, to, button, () => {
|
|
209
|
+
const frame = frameOf("Go");
|
|
210
|
+
frame.focus();
|
|
211
|
+
return painted(frame, "_outlineColor-", "outline-color", "focus-visible");
|
|
212
|
+
});
|
|
213
|
+
expect(ring).toBe(houseThemes[to]?.outlineColor);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
describe("FZ-9617 LC-176: a framed circle with no intent inks its plate on the host scope's $color12", () => {
|
|
220
|
+
for (const [from, to] of FLIPS) {
|
|
221
|
+
it(`${from} -> ${to}`, () => {
|
|
222
|
+
const ink = expectFlipFollows(
|
|
223
|
+
from,
|
|
224
|
+
to,
|
|
225
|
+
<Button circular icon={PlusIcon} aria-label="Add" />,
|
|
226
|
+
() => {
|
|
227
|
+
const frame = frameOf("Add");
|
|
228
|
+
return {
|
|
229
|
+
frame: painted(frame, "_col-", "color"),
|
|
230
|
+
glyph: glyphFill(frame.querySelector("svg") as Element),
|
|
231
|
+
};
|
|
232
|
+
},
|
|
233
|
+
);
|
|
234
|
+
expect(ink).toEqual({ frame: houseThemes[to]?.color12, glyph: houseThemes[to]?.color12 });
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const ICON_BUTTONS: [string, ReactElement][] = [
|
|
240
|
+
["plain", <Button key="b" icon={PlusIcon} aria-label="Add" />],
|
|
241
|
+
["accent", <Button key="b" accent icon={PlusIcon} aria-label="Add" />],
|
|
242
|
+
["error", <Button key="b" error icon={PlusIcon} aria-label="Add" />],
|
|
243
|
+
];
|
|
244
|
+
|
|
245
|
+
describe("FZ-9617 LC-176: an icon-only Button's hover and press washes follow a class-only root flip", () => {
|
|
246
|
+
for (const [from, to] of FLIPS) {
|
|
247
|
+
for (const [name, button] of ICON_BUTTONS) {
|
|
248
|
+
it(`${from} -> ${to} ${name}: the page scope's $backgroundHover and $backgroundPress`, () => {
|
|
249
|
+
const washes = expectFlipFollows(from, to, button, () => {
|
|
250
|
+
const frame = frameOf("Add");
|
|
251
|
+
return {
|
|
252
|
+
hover: painted(frame, "_bg-", "background-color", "hover"),
|
|
253
|
+
press: painted(frame, "_bg-", "background-color", "active"),
|
|
254
|
+
};
|
|
255
|
+
});
|
|
256
|
+
expect(washes).toEqual({
|
|
257
|
+
hover: houseThemes[to]?.backgroundHover,
|
|
258
|
+
press: houseThemes[to]?.backgroundPress,
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
function barRead(name: string) {
|
|
266
|
+
return () => {
|
|
267
|
+
const frame = frameOf(name);
|
|
268
|
+
return {
|
|
269
|
+
frame: painted(frame, "_col-", "color"),
|
|
270
|
+
glyph: glyphFill(frame.querySelector("svg") as Element),
|
|
271
|
+
};
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const BAR_BUTTONS: [string, string, ReactElement][] = [
|
|
276
|
+
["at the root", "Add", <Button key="b" chromeless accentInk icon={PlusIcon} aria-label="Add" />],
|
|
277
|
+
[
|
|
278
|
+
"in a bar Surface",
|
|
279
|
+
"Add",
|
|
280
|
+
<Surface key="b" bar>
|
|
281
|
+
<Button chromeless accentInk icon={PlusIcon} aria-label="Add" />
|
|
282
|
+
</Surface>,
|
|
283
|
+
],
|
|
284
|
+
[
|
|
285
|
+
"with a label",
|
|
286
|
+
"Back",
|
|
287
|
+
<Button key="b" chromeless accentInk edge="start" icon={PlusIcon}>
|
|
288
|
+
Back
|
|
289
|
+
</Button>,
|
|
290
|
+
],
|
|
291
|
+
];
|
|
292
|
+
|
|
293
|
+
describe("FZ-9617 LC-176: a chromeless accentInk bar glyph follows a class-only root flip", () => {
|
|
294
|
+
for (const [from, to] of FLIPS) {
|
|
295
|
+
for (const [where, name, button] of BAR_BUTTONS) {
|
|
296
|
+
it(`${from} -> ${to} ${where}: the glyph paints the host scope's identity ink`, () => {
|
|
297
|
+
const ink = expectFlipFollows(from, to, button, barRead(name));
|
|
298
|
+
expect(ink.glyph).toBe(ink.frame);
|
|
299
|
+
expect(ink.glyph).toMatch(/^#|^rgb|^hsl/);
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
it("the glyph's svg never carries a var() or a token, only currentColor (LC-84)", () => {
|
|
305
|
+
mount("light", <Button chromeless accentInk icon={PlusIcon} aria-label="Add" />);
|
|
306
|
+
const svg = frameOf("Add").querySelector("svg") as Element;
|
|
307
|
+
expect(svg.getAttribute("fill")).toBe("currentColor");
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it.each(["light", "dark"] as const)(
|
|
311
|
+
"%s: the house identity ink is the theme's $color12",
|
|
312
|
+
(scheme) => {
|
|
313
|
+
expect(houseThemes[scheme]?.identityInk).toBe(houseThemes[scheme]?.color12);
|
|
314
|
+
mount(scheme, <Button chromeless accentInk icon={PlusIcon} aria-label="Add" />);
|
|
315
|
+
expect(barRead("Add")()).toEqual({
|
|
316
|
+
frame: houseThemes[scheme]?.identityInk,
|
|
317
|
+
glyph: houseThemes[scheme]?.identityInk,
|
|
318
|
+
});
|
|
319
|
+
},
|
|
320
|
+
);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
const WASH_KNOBS: Partial<Knobs> = {
|
|
324
|
+
disabledStyle: "keepLabel",
|
|
325
|
+
fillStyle: "filled",
|
|
326
|
+
borderWidth: "medium",
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
describe("FZ-9617: a disabled accent media plate washes from the dark scope on web", () => {
|
|
330
|
+
for (const scheme of ["light", "dark"] as const) {
|
|
331
|
+
it(`${scheme} page: fill, edge and ink are the dark scope's $color3, $color6 and $color11`, () => {
|
|
332
|
+
mount(
|
|
333
|
+
scheme,
|
|
334
|
+
<Preset overrides={WASH_KNOBS}>
|
|
335
|
+
<Button accent disabled plate="media" icon={PlusIcon} aria-label="Play" />
|
|
336
|
+
</Preset>,
|
|
337
|
+
);
|
|
338
|
+
const frame = frameOf("Play");
|
|
339
|
+
expect({
|
|
340
|
+
fill: painted(frame, "_bg-", "background-color"),
|
|
341
|
+
edge: painted(frame, "_btc-", "border-top-color"),
|
|
342
|
+
ink: painted(frame, "_col-", "color"),
|
|
343
|
+
}).toEqual({
|
|
344
|
+
fill: houseThemes.dark?.color3,
|
|
345
|
+
edge: houseThemes.dark?.color6,
|
|
346
|
+
ink: houseThemes.dark?.color11,
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
});
|
package/src/Button/index.tsx
CHANGED
|
@@ -781,12 +781,17 @@ export function Button({
|
|
|
781
781
|
const tierPaint = tier ? controlTierPaint(tier, darkScheme, elevatedGround) : undefined;
|
|
782
782
|
const tierDisabled =
|
|
783
783
|
tier && disabledState.style !== "dimWhole" ? controlTierDisabled(tier) : undefined;
|
|
784
|
+
const pageTheme = parentScopeTheme as unknown as Record<string, { val?: unknown } | undefined>;
|
|
785
|
+
const hostTheme =
|
|
786
|
+
(mediaPlate
|
|
787
|
+
? (getConfig().themes as Record<string, Record<string, { val?: unknown }>>).dark
|
|
788
|
+
: undefined) ?? pageTheme;
|
|
784
789
|
const hostScoped = <T extends object>(fragment: T): T =>
|
|
785
790
|
Object.fromEntries(
|
|
786
791
|
Object.entries(fragment).map(([key, value]) => [
|
|
787
792
|
key,
|
|
788
793
|
typeof value === "string" && value.startsWith("$")
|
|
789
|
-
? (
|
|
794
|
+
? (hostTheme[value.slice(1)]?.val ?? value)
|
|
790
795
|
: value,
|
|
791
796
|
]),
|
|
792
797
|
) as T;
|
|
@@ -881,7 +886,11 @@ export function Button({
|
|
|
881
886
|
const outlinedOnHost = Boolean(
|
|
882
887
|
onPage && activeThemeName && solidPaletteThemes.has(activeThemeName),
|
|
883
888
|
);
|
|
884
|
-
const outlinedInk = outlinedOnHost
|
|
889
|
+
const outlinedInk = outlinedOnHost
|
|
890
|
+
? hostWash({ color: hostAccentInk }).color
|
|
891
|
+
: outlinedIntentInk
|
|
892
|
+
? "$color11"
|
|
893
|
+
: undefined;
|
|
885
894
|
|
|
886
895
|
const resolvedRole: ButtonActionRole | undefined =
|
|
887
896
|
actionRole ?? (accent || confirmPlate ? "primary" : undefined);
|
|
@@ -905,6 +914,11 @@ export function Button({
|
|
|
905
914
|
? glyphTint.label
|
|
906
915
|
: glyphTint.glyph
|
|
907
916
|
: null;
|
|
917
|
+
const barInkPaint =
|
|
918
|
+
isWeb && barInk && !mediaPlate && pageTheme.identityInk?.val === barInk
|
|
919
|
+
? hostWash({ color: "$identityInk" }).color
|
|
920
|
+
: barInk;
|
|
921
|
+
const barGlyphInk = barInk && isWeb && props.color == null ? "currentColor" : barInk;
|
|
908
922
|
const inlineGlyph = iconButton && inline === true && !nested;
|
|
909
923
|
// LC-176: back's caret beside its label is a bar glyph too, and so is the
|
|
910
924
|
// glyph on a plate (media, or the overlay's confirm).
|
|
@@ -953,7 +967,7 @@ export function Button({
|
|
|
953
967
|
: confirmPlate
|
|
954
968
|
? confirmInk
|
|
955
969
|
: framedCircle && !activeThemeName
|
|
956
|
-
?
|
|
970
|
+
? hostWash({ color: "$color12" }).color
|
|
957
971
|
: undefined;
|
|
958
972
|
const framedCirclePx =
|
|
959
973
|
touch && typeof sizeProp !== "number"
|
|
@@ -983,7 +997,7 @@ export function Button({
|
|
|
983
997
|
const barGlyphFloor = barGlyph && !framedCircle && !inlineGlyph && !nested;
|
|
984
998
|
const glyph =
|
|
985
999
|
barGlyph || inlineGlyph || framedCircle
|
|
986
|
-
? withGlyphWeight(icon, iconSize, resolvedGlyphWeight, barGlyphFloor,
|
|
1000
|
+
? withGlyphWeight(icon, iconSize, resolvedGlyphWeight, barGlyphFloor, barGlyphInk)
|
|
987
1001
|
: icon;
|
|
988
1002
|
|
|
989
1003
|
// LC-158: the frame keeps its recipe height under Dynamic Type, so the
|
|
@@ -1013,12 +1027,12 @@ export function Button({
|
|
|
1013
1027
|
{...(nested ? knobProps.label : { fontSize: recipe.fontSize })}
|
|
1014
1028
|
{...(textColor
|
|
1015
1029
|
? outlinedOnHost
|
|
1016
|
-
?
|
|
1030
|
+
? hostWash({ color: textColor })
|
|
1017
1031
|
: { color: textColor }
|
|
1018
1032
|
: outlinedInk
|
|
1019
1033
|
? { color: outlinedInk }
|
|
1020
1034
|
: undefined)}
|
|
1021
|
-
{...(
|
|
1035
|
+
{...(barInkPaint ? { color: barInkPaint } : undefined)}
|
|
1022
1036
|
// LC-40: on the washed disabled chrome the label leaves the on-fill
|
|
1023
1037
|
// foreground for the wash's AA ink (keepLabel keeps >=4.5:1).
|
|
1024
1038
|
{...(unavailable ? disabledText : undefined)}
|
|
@@ -1052,7 +1066,7 @@ export function Button({
|
|
|
1052
1066
|
plateInk ??
|
|
1053
1067
|
(textColor
|
|
1054
1068
|
? outlinedOnHost
|
|
1055
|
-
?
|
|
1069
|
+
? hostWash({ color: textColor }).color
|
|
1056
1070
|
: textColor
|
|
1057
1071
|
: outlinedInk) ??
|
|
1058
1072
|
"$color";
|
|
@@ -1075,7 +1089,7 @@ export function Button({
|
|
|
1075
1089
|
) : null;
|
|
1076
1090
|
const glyphAfter =
|
|
1077
1091
|
iconAfter && (barGlyph || inlineGlyph || framedCircle)
|
|
1078
|
-
? withGlyphWeight(iconAfter, iconSize, resolvedGlyphWeight, barGlyphFloor,
|
|
1092
|
+
? withGlyphWeight(iconAfter, iconSize, resolvedGlyphWeight, barGlyphFloor, barGlyphInk)
|
|
1079
1093
|
: iconAfter;
|
|
1080
1094
|
const iconAfterElement = glyphAfter ? (
|
|
1081
1095
|
<View flexShrink={0}>
|
|
@@ -1176,14 +1190,14 @@ export function Button({
|
|
|
1176
1190
|
const glyphState = iconButton && !selectedFill;
|
|
1177
1191
|
const iconHover = glyphState
|
|
1178
1192
|
? isWeb
|
|
1179
|
-
?
|
|
1193
|
+
? hostWash({ backgroundColor: "$backgroundHover" })
|
|
1180
1194
|
: { backgroundColor: "transparent" }
|
|
1181
1195
|
: mediaPlate
|
|
1182
1196
|
? { backgroundColor: "$shadow5" }
|
|
1183
1197
|
: undefined;
|
|
1184
1198
|
const iconPress = glyphState
|
|
1185
1199
|
? isWeb
|
|
1186
|
-
?
|
|
1200
|
+
? hostWash({ backgroundColor: "$backgroundPress" })
|
|
1187
1201
|
: GlassView || glassGroup
|
|
1188
1202
|
? { backgroundColor: "transparent" }
|
|
1189
1203
|
: { backgroundColor: "transparent", opacity: ICON_BUTTON_PRESS_OPACITY }
|
|
@@ -1273,7 +1287,7 @@ export function Button({
|
|
|
1273
1287
|
// (LC-05); outlined frames sit on the page, so re-anchor inherited
|
|
1274
1288
|
// text/icon color (currentColor) to the hue's readable text tier.
|
|
1275
1289
|
{...(outlinedInk ? { color: outlinedInk } : undefined)}
|
|
1276
|
-
{...(
|
|
1290
|
+
{...(barInkPaint ? { color: barInkPaint } : undefined)}
|
|
1277
1291
|
{...(plateInk ? { color: plateInk } : undefined)}
|
|
1278
1292
|
// LC-40 DISABLED-VISIBLE: keepLabel washes the chrome (muted fill/
|
|
1279
1293
|
// border) while label/glyphs re-anchor to the lowest AA text tier
|
|
@@ -1316,13 +1330,13 @@ export function Button({
|
|
|
1316
1330
|
focusStyle={{ outlineWidth: 0 }}
|
|
1317
1331
|
// DG-A11Y-01 / LC-71: house keyboard ring (≥2px `$outlineColor`) on the
|
|
1318
1332
|
// whole painted frame. Rest and mouse-focus paint none. Parent-scope
|
|
1319
|
-
// `$outlineColor
|
|
1333
|
+
// `$outlineColor`: intent buttons re-theme the frame, and those
|
|
1320
1334
|
// sub-themes derive a ring for their own inverted surfaces — invisible
|
|
1321
1335
|
// on the page the ring actually draws on (measured 1.17:1 on the accent
|
|
1322
1336
|
// pagination button).
|
|
1323
1337
|
focusVisibleStyle={ensureFocusVisibleRing({
|
|
1324
1338
|
...control.focusVisibleKnobProps,
|
|
1325
|
-
...(pageOutlineColor ? { outlineColor:
|
|
1339
|
+
...(pageOutlineColor ? hostWash({ outlineColor: "$outlineColor" }) : undefined),
|
|
1326
1340
|
})}
|
|
1327
1341
|
onPress={handlePress}
|
|
1328
1342
|
{...(isWeb ? { onKeyDown: handleKeyDown as unknown as () => void } : undefined)}
|
|
@@ -1461,17 +1475,26 @@ export function Button({
|
|
|
1461
1475
|
buttonElement
|
|
1462
1476
|
);
|
|
1463
1477
|
const hostWashScope = Object.fromEntries(
|
|
1464
|
-
[
|
|
1478
|
+
[
|
|
1479
|
+
disabledState.surfaceKnobProps,
|
|
1480
|
+
disabledState.textKnobProps,
|
|
1481
|
+
{ color: hostAccentInk },
|
|
1482
|
+
{ color: textColor },
|
|
1483
|
+
{ outlineColor: "$outlineColor" },
|
|
1484
|
+
{ backgroundColor: "$backgroundHover" },
|
|
1485
|
+
{ backgroundColor: "$backgroundPress" },
|
|
1486
|
+
{ color: "$color12" },
|
|
1487
|
+
{ color: "$identityInk" },
|
|
1488
|
+
]
|
|
1465
1489
|
.flatMap((fragment) => Object.values(fragment))
|
|
1466
1490
|
.filter((value): value is string => typeof value === "string" && value.startsWith("$"))
|
|
1467
1491
|
.map((value) => [hostWashVar(value.slice(1)), `var(--${value.slice(1)})`]),
|
|
1468
1492
|
);
|
|
1469
|
-
const hostedButton =
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
);
|
|
1493
|
+
const hostedButton = isWeb ? (
|
|
1494
|
+
<span style={{ display: "contents", ...hostWashScope } as CSSProperties}>{intentButton}</span>
|
|
1495
|
+
) : (
|
|
1496
|
+
intentButton
|
|
1497
|
+
);
|
|
1475
1498
|
// The media plate reads the same over any picture in either scheme: a dark
|
|
1476
1499
|
// material with a light glyph.
|
|
1477
1500
|
const themedButton = mediaPlate ? <Theme name="dark">{hostedButton}</Theme> : hostedButton;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Button/index.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Button/index.tsx"],"names":[],"mappings":"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"}
|