react-x11 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +330 -81
- package/package.json +132 -14
- package/src/ClickToComponent.js +9 -2
- package/src/DevToolsIntegration.js +440 -32
- package/src/Reconciler.js +463 -165
- package/src/a11y.js +1299 -0
- package/src/acceleratorhooks.js +86 -0
- package/src/accelerators.js +128 -0
- package/src/activate.js +168 -0
- package/src/anchor.js +360 -0
- package/src/appcontext.js +161 -0
- package/src/appearance.js +748 -0
- package/src/appearancehooks.js +96 -0
- package/src/apphooks.js +73 -0
- package/src/application.js +748 -0
- package/src/atspi.js +1953 -0
- package/src/bus.js +545 -0
- package/src/bushooks.js +136 -0
- package/src/clientmessage.js +140 -0
- package/src/clipboard.js +121 -0
- package/src/components/Button.js +148 -33
- package/src/components/Checkbox.js +66 -32
- package/src/components/Dialog.js +96 -29
- package/src/components/FileDialog.js +491 -0
- package/src/components/Icon.js +329 -0
- package/src/components/Menu.js +797 -133
- package/src/components/PasswordInput.js +434 -0
- package/src/components/ProgressBar.js +91 -12
- package/src/components/Radio.js +78 -35
- package/src/components/Select.js +255 -65
- package/src/components/Slider.js +102 -41
- package/src/components/SplitPane.js +206 -0
- package/src/components/Switch.js +100 -22
- package/src/components/Table.js +529 -0
- package/src/components/Tabs.js +211 -0
- package/src/components/Tooltip.js +528 -54
- package/src/components/anchor.js +154 -107
- package/src/components/change.js +34 -0
- package/src/components/dnd.js +103 -0
- package/src/components/index.js +16 -3
- package/src/components/keys.js +17 -20
- package/src/components/scribble.js +247 -0
- package/src/components/theme.js +350 -42
- package/src/components/typeahead.js +12 -2
- package/src/compose.js +868 -0
- package/src/compositing.js +224 -0
- package/src/dbusmenu.js +384 -0
- package/src/debug.d.ts +63 -0
- package/src/debug.js +642 -0
- package/src/decorations.js +486 -0
- package/src/desktopsettings.js +209 -0
- package/src/desktopsettingshooks.js +62 -0
- package/src/dnd.js +1725 -0
- package/src/editmenu.js +272 -0
- package/src/errors.js +98 -0
- package/src/events.js +1406 -162
- package/src/extensions.js +45 -0
- package/src/filedialog.js +375 -0
- package/src/filedialoghooks.js +132 -0
- package/src/fonthooks.js +64 -0
- package/src/fonts.js +301 -0
- package/src/foreignnodes.js +519 -0
- package/src/frame/child.js +33 -0
- package/src/frame/childmain.js +274 -0
- package/src/frame/env.js +140 -0
- package/src/frame/index.js +444 -0
- package/src/frame/lifecycle.js +67 -0
- package/src/frame/protocol.js +179 -0
- package/src/frames.js +73 -0
- package/src/glbackend.js +114 -0
- package/src/glnodes.js +125 -67
- package/src/globalmenu.js +683 -0
- package/src/host.d.ts +88 -0
- package/src/host.js +42 -0
- package/src/idle.js +490 -0
- package/src/idlehooks.js +100 -0
- package/src/imagesource.js +349 -0
- package/src/index.d.ts +475 -0
- package/src/index.js +83 -9
- package/src/inputtime.js +206 -0
- package/src/jsx-dev-runtime.d.ts +21 -0
- package/src/jsx-dev-runtime.js +2 -0
- package/src/jsx-runtime.d.ts +39 -0
- package/src/jsx-runtime.js +6 -0
- package/src/keyboard.js +256 -0
- package/src/keyboardstate.js +278 -0
- package/src/keyboardstatehooks.js +58 -0
- package/src/keysyms.d.ts +149 -0
- package/src/keysyms.js +270 -0
- package/src/locale.js +170 -0
- package/src/localehooks.js +47 -0
- package/src/menuitem.js +223 -0
- package/src/node.d.ts +597 -0
- package/src/node.js +44 -0
- package/src/nodes.js +9546 -690
- package/src/ntk.d.ts +44 -0
- package/src/ntk.js +25 -0
- package/src/paintcache.js +366 -0
- package/src/palette.js +380 -0
- package/src/pastestate.js +66 -0
- package/src/portal.js +461 -0
- package/src/priority.js +26 -0
- package/src/refresh/index.d.ts +40 -0
- package/src/refresh/index.js +122 -0
- package/src/refresh/loader.d.ts +37 -0
- package/src/refresh/loader.js +401 -0
- package/src/refresh/register.d.ts +5 -0
- package/src/refresh/register.js +13 -0
- package/src/registry.js +232 -0
- package/src/scale.js +626 -0
- package/src/scalehooks.js +27 -0
- package/src/screencolor.js +640 -0
- package/src/screencolorhooks.js +101 -0
- package/src/screens.js +754 -0
- package/src/screenshooks.js +137 -0
- package/src/startup.js +302 -0
- package/src/style.d.ts +126 -0
- package/src/style.js +33 -0
- package/src/styles.js +1482 -6
- package/src/svgnodes.js +306 -0
- package/src/testing/a11y.js +484 -0
- package/src/testing/components.js +414 -0
- package/src/testing/events.js +407 -0
- package/src/testing/harness.js +455 -0
- package/src/testing/index.d.ts +558 -0
- package/src/testing/index.js +85 -0
- package/src/testing/mock-app.js +463 -0
- package/src/testing/pixels.js +152 -0
- package/src/testing/queries.js +224 -0
- package/src/textrange.js +83 -0
- package/src/textselection.js +439 -0
- package/src/trace-registry.js +63 -0
- package/src/transfer.js +93 -0
- package/src/types/appearance.d.ts +84 -0
- package/src/types/application.d.ts +173 -0
- package/src/types/components.d.ts +820 -0
- package/src/types/dbus.d.ts +177 -0
- package/src/types/elements.d.ts +897 -0
- package/src/types/events.d.ts +549 -0
- package/src/types/filedialog.d.ts +260 -0
- package/src/types/fonts.d.ts +124 -0
- package/src/types/frame.d.ts +146 -0
- package/src/types/globalmenu.d.ts +42 -0
- package/src/types/nodes.d.ts +199 -0
- package/src/types/screencolor.d.ts +84 -0
- package/src/types/style.d.ts +434 -0
- package/src/types/system.d.ts +287 -0
- package/src/windowid.js +151 -0
- package/src/windowstate.js +393 -0
- package/src/xsettings.js +336 -0
- package/src/yoga.d.ts +55 -0
- package/src/yoga.js +135 -0
- package/src/components/Canvas3D.js +0 -28
- package/src/geometry3d.js +0 -223
- package/src/pointer3d.js +0 -158
- package/src/raycast3d.js +0 -146
- package/src/richnodes.js +0 -436
- package/src/scene3d.js +0 -683
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
// The system icon set: the affordance glyphs core's own widgets are drawn
|
|
2
|
+
// with, and the same ones an application or a third-party widget can reach
|
|
3
|
+
// for so that a Select it did not write and a toolbar it did lines up.
|
|
4
|
+
//
|
|
5
|
+
// ## Affordances, not nouns
|
|
6
|
+
//
|
|
7
|
+
// The set holds glyphs that mean something about *the control*: there is
|
|
8
|
+
// more here, this one is chosen, this closes, this is hidden. It holds no
|
|
9
|
+
// nouns — no folder, no document, no save, no printer. Those belong to an
|
|
10
|
+
// icon theme (lucide, an XDG icon theme, the app's own art), they are
|
|
11
|
+
// unbounded in number, and a widget set that starts shipping them has taken
|
|
12
|
+
// on a design system. That line is what keeps this file small enough to
|
|
13
|
+
// read, and it is the answer to "should X be added?".
|
|
14
|
+
//
|
|
15
|
+
// ## One shape per idea
|
|
16
|
+
//
|
|
17
|
+
// Before this existed, core drew a chevron four ways: a filled triangle in
|
|
18
|
+
// Select and Table, and a `▸` text
|
|
19
|
+
// glyph in Menu — which is tofu on a machine without it, the very thing
|
|
20
|
+
// docs/components.md warns applications about. There is now one chevron and
|
|
21
|
+
// the filled caret is gone: a stroked chevron is what a modern control uses
|
|
22
|
+
// to say "more this way", and one idea drawn one way is the point of a set.
|
|
23
|
+
//
|
|
24
|
+
// ## How they are drawn, and why not a font
|
|
25
|
+
//
|
|
26
|
+
// A drawing per icon over `<canvas mono>`: the ink comes from `style.color`,
|
|
27
|
+
// so one drawing serves the resting row, the highlighted row, the disabled
|
|
28
|
+
// one and both colour schemes, and the paint cache keeps one rendered copy
|
|
29
|
+
// for every instance of it on screen. (A `mono` entry bakes its colour for
|
|
30
|
+
// now rather than caching coverage and tinting at composite time — see
|
|
31
|
+
// `CanvasNode.paintCachePlan` for why, and what it costs.) The draw
|
|
32
|
+
// functions are module-level, so their identity is stable across renders and
|
|
33
|
+
// a table of 500 twisties invalidates nothing when its rows re-render.
|
|
34
|
+
//
|
|
35
|
+
// A bundled icon *font* would work — ntk's FontManager takes font bytes, and
|
|
36
|
+
// glyphs composite through a solid source picture, so a whole column of
|
|
37
|
+
// chevrons would be one CompositeGlyphs. It loses on everything else: a
|
|
38
|
+
// binary artifact and a generation toolchain in a package that is otherwise
|
|
39
|
+
// pure JS, baseline rather than box alignment, unhinted mush at 12px, and an
|
|
40
|
+
// icon name routed through text layout and the accessibility tree. Revisit
|
|
41
|
+
// only if a profile of a large tree view shows composite count dominating.
|
|
42
|
+
//
|
|
43
|
+
// The drawings are also deliberately *not* themable. Colour and size are —
|
|
44
|
+
// see `Icon` — but the geometry is core's vocabulary, and an application
|
|
45
|
+
// that wants a different chevron wants an icon library, which is a separate
|
|
46
|
+
// thing it is welcome to bring.
|
|
47
|
+
|
|
48
|
+
import React from 'react';
|
|
49
|
+
import { useTheme } from './theme.js';
|
|
50
|
+
|
|
51
|
+
const h = React.createElement;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Stroke weight for a glyph in a `size` box.
|
|
55
|
+
*
|
|
56
|
+
* `size / 12` is the 2-on-a-24-grid weight every modern outline set uses,
|
|
57
|
+
* with a floor: below about 1.25 a stroke stops reading as a line at all,
|
|
58
|
+
* and a disclosure twisty is 10px.
|
|
59
|
+
*/
|
|
60
|
+
const weight = (size) => Math.max(1.25, size / 12);
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The live area: the ink runs corner to corner apart from this margin, so
|
|
64
|
+
* **`size` is the size of the mark** rather than of a grid it sits in.
|
|
65
|
+
*
|
|
66
|
+
* That is the one place this set departs from how lucide and its
|
|
67
|
+
* descendants draw, and it is deliberate. Their chevron spans half the width
|
|
68
|
+
* and a quarter the height of its box, so a "24px" lucide icon carries about
|
|
69
|
+
* 14px of ink and an author picks `size={20}` to sit beside 14px text. Every
|
|
70
|
+
* call site here is a small number chosen as _how big the mark should be_ —
|
|
71
|
+
* a `Select`'s chevron matches the capitals beside it, a `Checkbox`'s tick
|
|
72
|
+
* fits its 16px well — and the grid convention silently made all of them
|
|
73
|
+
* about 40% too small. The glyphs core drew before the set filled their
|
|
74
|
+
* boxes the same way (the tick was 8×5.5 of ink in a 10×8 canvas), which is
|
|
75
|
+
* what these proportions are matched against.
|
|
76
|
+
*
|
|
77
|
+
* The margin is half a stroke plus a hair: the stroke is centred on the
|
|
78
|
+
* path, so ink at 0.0 would paint outside the box and `<canvas>` clips to
|
|
79
|
+
* its bounds. 0.08 clears it at the smallest size anything is drawn at.
|
|
80
|
+
*/
|
|
81
|
+
const PAD = 0.08;
|
|
82
|
+
const END = 1 - PAD;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Stroke a polyline given in fractions of the box.
|
|
86
|
+
*
|
|
87
|
+
* Fractions rather than a fixed design grid because the box is the only
|
|
88
|
+
* thing the drawing is told about, and it is what the cache is keyed on: an
|
|
89
|
+
* icon at 10px and the same icon at 16px are two entries either way, and
|
|
90
|
+
* scaling here costs nothing while a transform would defeat the unscaled
|
|
91
|
+
* blit the cache goes out of its way to keep.
|
|
92
|
+
*
|
|
93
|
+
* Never sets a colour: under `mono` the ink is preset from `style.color`,
|
|
94
|
+
* and a drawing that named its own would collide with itself in the cache.
|
|
95
|
+
*/
|
|
96
|
+
function line(ctx, w, hgt, points) {
|
|
97
|
+
ctx.lineWidth = weight(Math.min(w, hgt));
|
|
98
|
+
ctx.lineCap = 'round';
|
|
99
|
+
ctx.lineJoin = 'round';
|
|
100
|
+
ctx.beginPath();
|
|
101
|
+
for (let i = 0; i < points.length; i++) {
|
|
102
|
+
const [x, y] = points[i];
|
|
103
|
+
if (i === 0) ctx.moveTo(x * w, y * hgt);
|
|
104
|
+
else ctx.lineTo(x * w, y * hgt);
|
|
105
|
+
}
|
|
106
|
+
ctx.stroke();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** A filled disc, in fractions of the box. */
|
|
110
|
+
function disc(ctx, w, hgt, cx, cy, r) {
|
|
111
|
+
ctx.beginPath();
|
|
112
|
+
ctx.arc(cx * w, cy * hgt, r * Math.min(w, hgt), 0, Math.PI * 2);
|
|
113
|
+
ctx.fill();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The eye's outline: two quadratics meeting at the corners, which is the
|
|
118
|
+
* almond every peek toggle is drawn as. A quadratic reaches only half way to
|
|
119
|
+
* its control point, so the lids are pushed past the box (`-0.15`, `1.15`)
|
|
120
|
+
* to arrive at 0.175 and 0.825 — and the curve stays flatter than a circle,
|
|
121
|
+
* which is what stops it reading as a lemon.
|
|
122
|
+
*/
|
|
123
|
+
function almond(ctx, w, hgt) {
|
|
124
|
+
ctx.lineWidth = weight(Math.min(w, hgt));
|
|
125
|
+
ctx.lineCap = 'round';
|
|
126
|
+
ctx.lineJoin = 'round';
|
|
127
|
+
ctx.beginPath();
|
|
128
|
+
ctx.moveTo(0.06 * w, 0.5 * hgt);
|
|
129
|
+
ctx.quadraticCurveTo(0.5 * w, -0.15 * hgt, 0.94 * w, 0.5 * hgt);
|
|
130
|
+
ctx.quadraticCurveTo(0.5 * w, 1.15 * hgt, 0.06 * w, 0.5 * hgt);
|
|
131
|
+
ctx.stroke();
|
|
132
|
+
ctx.beginPath();
|
|
133
|
+
ctx.arc(0.5 * w, 0.5 * hgt, 0.17 * Math.min(w, hgt), 0, Math.PI * 2);
|
|
134
|
+
ctx.stroke();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* The set. Each entry is an `onDraw` — `(ctx, { width, height })` — with the
|
|
139
|
+
* signature `<canvas>` already speaks, so an application can pass one
|
|
140
|
+
* straight to `<canvas mono>` without going through `<Icon>`:
|
|
141
|
+
*
|
|
142
|
+
* <canvas mono cacheKey="check" onDraw={icons.check} style={…} />
|
|
143
|
+
*
|
|
144
|
+
* Module-level and frozen: identity is the thing `CanvasNode` compares to
|
|
145
|
+
* decide whether a re-render changed the picture, so these must never be
|
|
146
|
+
* rebuilt per render.
|
|
147
|
+
*/
|
|
148
|
+
export const icons = Object.freeze({
|
|
149
|
+
// The four chevrons are four drawings rather than one rotated, because a
|
|
150
|
+
// rotation is a transform and the cache only composites unscaled,
|
|
151
|
+
// unrotated surfaces — and because a 10px chevron's crispness is a
|
|
152
|
+
// different problem per direction.
|
|
153
|
+
//
|
|
154
|
+
// Arms at 45°, so the ink is twice as wide as it is tall (or the other way
|
|
155
|
+
// round): the long axis takes the whole live area and the short one half
|
|
156
|
+
// of it, centred. A chevron is the flattest glyph in the set for that
|
|
157
|
+
// reason, and it is why `size` for one reads as its *width*.
|
|
158
|
+
chevronRight: (ctx, { width: w, height: hgt }) =>
|
|
159
|
+
line(ctx, w, hgt, [
|
|
160
|
+
[0.29, PAD],
|
|
161
|
+
[0.71, 0.5],
|
|
162
|
+
[0.29, END],
|
|
163
|
+
]),
|
|
164
|
+
chevronLeft: (ctx, { width: w, height: hgt }) =>
|
|
165
|
+
line(ctx, w, hgt, [
|
|
166
|
+
[0.71, PAD],
|
|
167
|
+
[0.29, 0.5],
|
|
168
|
+
[0.71, END],
|
|
169
|
+
]),
|
|
170
|
+
chevronDown: (ctx, { width: w, height: hgt }) =>
|
|
171
|
+
line(ctx, w, hgt, [
|
|
172
|
+
[PAD, 0.29],
|
|
173
|
+
[0.5, 0.71],
|
|
174
|
+
[END, 0.29],
|
|
175
|
+
]),
|
|
176
|
+
chevronUp: (ctx, { width: w, height: hgt }) =>
|
|
177
|
+
line(ctx, w, hgt, [
|
|
178
|
+
[PAD, 0.71],
|
|
179
|
+
[0.5, 0.29],
|
|
180
|
+
[END, 0.71],
|
|
181
|
+
]),
|
|
182
|
+
|
|
183
|
+
check: (ctx, { width: w, height: hgt }) =>
|
|
184
|
+
line(ctx, w, hgt, [
|
|
185
|
+
[PAD, 0.53],
|
|
186
|
+
[0.34, 0.79],
|
|
187
|
+
[END, 0.21],
|
|
188
|
+
]),
|
|
189
|
+
// The mixed state of a checkbox and a menu's `toggleState === -1`: a bar,
|
|
190
|
+
// because "some of these" is not "none of these" and an empty well says
|
|
191
|
+
// the second.
|
|
192
|
+
dash: (ctx, { width: w, height: hgt }) =>
|
|
193
|
+
line(ctx, w, hgt, [
|
|
194
|
+
[PAD, 0.5],
|
|
195
|
+
[END, 0.5],
|
|
196
|
+
]),
|
|
197
|
+
// A radio's mark, and a menu row standing in for one. Smaller than the
|
|
198
|
+
// live area on purpose: a solid disc carries far more ink per pixel than
|
|
199
|
+
// a stroke, and one drawn out to `PAD` reads as a blob beside a chevron
|
|
200
|
+
// rather than as the same size.
|
|
201
|
+
dot: (ctx, { width: w, height: hgt }) => disc(ctx, w, hgt, 0.5, 0.5, 0.26),
|
|
202
|
+
|
|
203
|
+
// Inset past the live area, for the mirror of the same reason: an X that
|
|
204
|
+
// reaches the corners measures longer on the diagonal than anything else
|
|
205
|
+
// in the set measures on its side, and looks it.
|
|
206
|
+
close: (ctx, { width: w, height: hgt }) => {
|
|
207
|
+
line(ctx, w, hgt, [
|
|
208
|
+
[0.13, 0.13],
|
|
209
|
+
[0.87, 0.87],
|
|
210
|
+
]);
|
|
211
|
+
line(ctx, w, hgt, [
|
|
212
|
+
[0.87, 0.13],
|
|
213
|
+
[0.13, 0.87],
|
|
214
|
+
]);
|
|
215
|
+
},
|
|
216
|
+
plus: (ctx, { width: w, height: hgt }) => {
|
|
217
|
+
line(ctx, w, hgt, [
|
|
218
|
+
[0.5, PAD],
|
|
219
|
+
[0.5, END],
|
|
220
|
+
]);
|
|
221
|
+
line(ctx, w, hgt, [
|
|
222
|
+
[PAD, 0.5],
|
|
223
|
+
[END, 0.5],
|
|
224
|
+
]);
|
|
225
|
+
},
|
|
226
|
+
// Overflow. Vertical because that is where it goes — at the end of a row,
|
|
227
|
+
// opening a menu below it.
|
|
228
|
+
moreVertical: (ctx, { width: w, height: hgt }) => {
|
|
229
|
+
disc(ctx, w, hgt, 0.5, 0.15, 0.115);
|
|
230
|
+
disc(ctx, w, hgt, 0.5, 0.5, 0.115);
|
|
231
|
+
disc(ctx, w, hgt, 0.5, 0.85, 0.115);
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
eye: (ctx, { width: w, height: hgt }) => almond(ctx, w, hgt),
|
|
235
|
+
eyeOff: (ctx, { width: w, height: hgt }) => {
|
|
236
|
+
almond(ctx, w, hgt);
|
|
237
|
+
line(ctx, w, hgt, [
|
|
238
|
+
[0.1, 0.9],
|
|
239
|
+
[0.9, 0.1],
|
|
240
|
+
]);
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
/** Every name in the set, for a runtime check or a gallery. */
|
|
245
|
+
export const iconNames = Object.freeze(Object.keys(icons));
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* The size an icon takes when nothing says otherwise: a shade under the
|
|
249
|
+
* text it sits beside, which at the default 14px body is the 12px the menu
|
|
250
|
+
* gutter was already built around.
|
|
251
|
+
*
|
|
252
|
+
* Derived from `fontSize` rather than given a palette token of its own, for
|
|
253
|
+
* the reason the popup radii are: a theme that scales the type wants the
|
|
254
|
+
* glyphs to scale with it, and a theme that does not should not have to
|
|
255
|
+
* remember a token to keep them in proportion. The per-call `size` prop is
|
|
256
|
+
* the seam for the one icon that needs to be bigger.
|
|
257
|
+
*/
|
|
258
|
+
export const iconSize = (fontSize) => Math.round(fontSize * 0.85);
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* <Icon name size color /> — one glyph from the system set.
|
|
262
|
+
*
|
|
263
|
+
* ```jsx
|
|
264
|
+
* <Icon name="chevronDown" />
|
|
265
|
+
* <Icon name="check" size={10} color={theme.accentText} />
|
|
266
|
+
* ```
|
|
267
|
+
*
|
|
268
|
+
* ## Colour inherits; size does not
|
|
269
|
+
*
|
|
270
|
+
* `color` travels down the tree, so an icon inside a row that dims itself
|
|
271
|
+
* dims with it, and a `:hover` block that sets `color` on the row reaches
|
|
272
|
+
* the icon exactly as it reaches the row's label — the ink is an inherited
|
|
273
|
+
* property, and the state block changes it on the row (docs/styling.md).
|
|
274
|
+
* The `color` prop here is for saying something the surrounding text does
|
|
275
|
+
* not: a destructive action's mark, a check drawn on an accent fill.
|
|
276
|
+
*
|
|
277
|
+
* **`size` does not inherit** and is deliberately not `fontSize`. A glyph is
|
|
278
|
+
* a drawing rather than a letter — it has no baseline to sit on and no
|
|
279
|
+
* ascent to be measured against — so it takes its default from the palette's
|
|
280
|
+
* `fontSize` (`iconSize`) rather than from whatever text happens to be
|
|
281
|
+
* around it, which keeps a chevron the same size in a row that shrank its
|
|
282
|
+
* label. Pass `size` for the one icon that has to be bigger.
|
|
283
|
+
*
|
|
284
|
+
* Everything else is a `<canvas>`: `style` merges last, and the rest of the
|
|
285
|
+
* props go straight through, so a clickable icon is `<Icon onClick focusable/>`.
|
|
286
|
+
* `aria-hidden` defaults to true because an affordance glyph is decoration —
|
|
287
|
+
* the meaning is already on the control, in its `role` and its
|
|
288
|
+
* `aria-expanded`. Name it (`aria-hidden={false} aria-label="Close"`) only
|
|
289
|
+
* when the icon *is* the control and nothing else says so.
|
|
290
|
+
*/
|
|
291
|
+
export function Icon({
|
|
292
|
+
name,
|
|
293
|
+
size,
|
|
294
|
+
color,
|
|
295
|
+
style,
|
|
296
|
+
'aria-hidden': ariaHidden = true,
|
|
297
|
+
...canvasProps
|
|
298
|
+
}) {
|
|
299
|
+
// Before the hook on purpose: a name outside the set never renders, so
|
|
300
|
+
// there is no hook order to keep, and this way the check is reachable
|
|
301
|
+
// without a tree — which is what makes it testable and what makes the
|
|
302
|
+
// message land on the call site rather than inside a paint.
|
|
303
|
+
const draw = icons[name];
|
|
304
|
+
if (!draw) {
|
|
305
|
+
throw new Error(
|
|
306
|
+
`<Icon name="${name}">: not a system icon. One of: ${iconNames.join(', ')}`,
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
const theme = useTheme();
|
|
310
|
+
const px = size ?? iconSize(theme.fontSize);
|
|
311
|
+
return h('canvas', {
|
|
312
|
+
mono: true,
|
|
313
|
+
// The size is already in the cache key — the plan carries the node's
|
|
314
|
+
// width and height — so the name is the whole of what this adds, and
|
|
315
|
+
// the colour is out of it on purpose: that is what `mono` buys.
|
|
316
|
+
cacheKey: name,
|
|
317
|
+
onDraw: draw,
|
|
318
|
+
'aria-hidden': ariaHidden,
|
|
319
|
+
...canvasProps,
|
|
320
|
+
// No `color` unless the call site named one: leaving it off the style is
|
|
321
|
+
// what lets it inherit, where `color: theme.text` would pin every icon to
|
|
322
|
+
// the palette and undo the row it sits in.
|
|
323
|
+
style: [
|
|
324
|
+
{ width: px, height: px, flexShrink: 0 },
|
|
325
|
+
color ? { color } : null,
|
|
326
|
+
style,
|
|
327
|
+
],
|
|
328
|
+
});
|
|
329
|
+
}
|