rift-ds 1.0.1 → 1.2.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/LICENSE +1 -1
- package/README.md +8 -2
- package/components/AppLayout/AppLayout.css +13 -3
- package/components/AppLayout/AppLayout.d.ts +5 -1
- package/components/AppLayout/AppLayout.js +4 -0
- package/components/AppSidebar/AppSidebar.css +237 -0
- package/components/AppSidebar/AppSidebar.d.ts +32 -3
- package/components/AppSidebar/AppSidebar.js +187 -16
- package/components/BottomNav/BottomNav.css +417 -0
- package/components/BottomNav/BottomNav.d.ts +73 -0
- package/components/BottomNav/BottomNav.js +100 -0
- package/components/Chart/AreaChart.js +67 -56
- package/components/Chart/BarChart.js +67 -56
- package/components/Chart/ComboChart.js +93 -82
- package/components/Chart/LineChart.js +59 -48
- package/components/Chart/PieChart.js +56 -45
- package/components/Chart/RadarChart.js +60 -49
- package/components/Chart/RadialChart.js +70 -59
- package/components/Chart/ScatterChart.js +61 -50
- package/components/Chart/StackedBarChart.js +64 -53
- package/components/Chart/Treemap.js +34 -23
- package/components/Inspector/Inspector.css +511 -0
- package/components/Inspector/InspectorDropdown.d.ts +39 -0
- package/components/Inspector/InspectorDropdown.js +68 -0
- package/components/Inspector/InspectorInput.d.ts +32 -0
- package/components/Inspector/InspectorInput.js +52 -0
- package/components/Inspector/InspectorSection.d.ts +32 -0
- package/components/Inspector/InspectorSection.js +70 -0
- package/components/Inspector/InspectorSegmentedControl.d.ts +46 -0
- package/components/Inspector/InspectorSegmentedControl.js +62 -0
- package/components/Inspector/InspectorSlider.d.ts +41 -0
- package/components/Inspector/InspectorSlider.js +81 -0
- package/components/Inspector/InspectorToggleSwitch.d.ts +29 -0
- package/components/Inspector/InspectorToggleSwitch.js +51 -0
- package/components/Nav/Nav.d.ts +11 -3
- package/components/Nav/Nav.js +3 -2
- package/components/SegmentedControl/SegmentedControl.css +7 -5
- package/components/SidebarPanel/SidebarPanel.css +291 -0
- package/components/SidebarPanel/SidebarPanel.d.ts +45 -0
- package/components/SidebarPanel/SidebarPanel.js +166 -0
- package/components/Stepper/Stepper.d.ts +3 -1
- package/components/Stepper/Stepper.js +1 -1
- package/components/TopAppBar/TopAppBar.css +464 -0
- package/components/TopAppBar/TopAppBar.d.ts +80 -0
- package/components/TopAppBar/TopAppBar.js +138 -0
- package/components/registry.json +84 -1
- package/components/registry.json.d.ts +84 -1
- package/components/registry.json.js +2 -2
- package/index.d.ts +9 -0
- package/index.js +18 -0
- package/package.json +5 -1
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
INSPECTOR FAMILY
|
|
3
|
+
One-row controls for tool panels and settings
|
|
4
|
+
rails: each control is one bar with its name
|
|
5
|
+
inside on the left and its state on the right.
|
|
6
|
+
The form controls (Slider, Input, ToggleSwitch,
|
|
7
|
+
SegmentedControl, Dropdown) keep their label
|
|
8
|
+
above; these are their counterparts for a
|
|
9
|
+
dense panel.
|
|
10
|
+
|
|
11
|
+
Shaped and sized like Button, not like Input:
|
|
12
|
+
the bar reads --radius-pill, and its height,
|
|
13
|
+
side inset and type are computed from the
|
|
14
|
+
Button's own tokens for the same size, so a
|
|
15
|
+
bar and a Button of one size sit level.
|
|
16
|
+
|
|
17
|
+
Colour climbs the neutral Button's ladder one
|
|
18
|
+
step per state, which holds in both themes:
|
|
19
|
+
bar passive-hover (neutral fill at
|
|
20
|
+
80%: a step off
|
|
21
|
+
a glass panel)
|
|
22
|
+
fill neutral-bg-hover (a slider's value,
|
|
23
|
+
a hovered bar, a
|
|
24
|
+
selected segment)
|
|
25
|
+
press neutral-bg-active
|
|
26
|
+
Nothing in the family takes the action colour
|
|
27
|
+
except the focus ring: a selected segment and
|
|
28
|
+
an on switch are neutral, the same call
|
|
29
|
+
SegmentedControl's `neutral` variant makes.
|
|
30
|
+
============================================ */
|
|
31
|
+
|
|
32
|
+
/* ---------- size ----------
|
|
33
|
+
default 8px + 24px line + 8px = 40px, 20px sides, paragraph-emphasis
|
|
34
|
+
compact 6px + 20px line + 6px = 32px, 12px sides, paragraph-sm-emphasis */
|
|
35
|
+
.ds-inspector-bar,
|
|
36
|
+
.ds-inspector-dropdown {
|
|
37
|
+
--ds-inspector-height: calc(2 * var(--padding-200) + var(--font-paragraph-emphasis-line-height));
|
|
38
|
+
--ds-inspector-inset: var(--padding-500);
|
|
39
|
+
--ds-inspector-font-family: var(--font-paragraph-emphasis-family);
|
|
40
|
+
--ds-inspector-font-size: var(--font-paragraph-emphasis-size);
|
|
41
|
+
--ds-inspector-line-height: var(--font-paragraph-emphasis-line-height);
|
|
42
|
+
--ds-inspector-letter-spacing: var(--font-paragraph-emphasis-letter-spacing);
|
|
43
|
+
--ds-inspector-label-weight: var(--font-paragraph-emphasis-weight);
|
|
44
|
+
--ds-inspector-value-weight: var(--font-paragraph-weight);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.ds-inspector-bar--compact,
|
|
48
|
+
.ds-inspector-dropdown--compact {
|
|
49
|
+
--ds-inspector-height: calc(2 * var(--padding-150) + var(--font-paragraph-sm-emphasis-line-height));
|
|
50
|
+
--ds-inspector-inset: var(--padding-300);
|
|
51
|
+
--ds-inspector-font-family: var(--font-paragraph-sm-emphasis-family);
|
|
52
|
+
--ds-inspector-font-size: var(--font-paragraph-sm-emphasis-size);
|
|
53
|
+
--ds-inspector-line-height: var(--font-paragraph-sm-emphasis-line-height);
|
|
54
|
+
--ds-inspector-letter-spacing: var(--font-paragraph-sm-emphasis-letter-spacing);
|
|
55
|
+
--ds-inspector-label-weight: var(--font-paragraph-sm-emphasis-weight);
|
|
56
|
+
--ds-inspector-value-weight: var(--font-paragraph-sm-weight);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* ============================================
|
|
60
|
+
THE BAR (shared by every control but the dropdown,
|
|
61
|
+
which dresses the library Dropdown's trigger instead)
|
|
62
|
+
============================================ */
|
|
63
|
+
|
|
64
|
+
.ds-inspector-bar {
|
|
65
|
+
position: relative;
|
|
66
|
+
display: flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
justify-content: space-between;
|
|
69
|
+
gap: var(--gap-300);
|
|
70
|
+
min-width: 0;
|
|
71
|
+
box-sizing: border-box;
|
|
72
|
+
height: var(--ds-inspector-height);
|
|
73
|
+
padding: 0 var(--ds-inspector-inset);
|
|
74
|
+
overflow: hidden;
|
|
75
|
+
border-radius: var(--radius-pill);
|
|
76
|
+
background: var(--color-action-passive-bg-hover);
|
|
77
|
+
font-family: var(--ds-inspector-font-family);
|
|
78
|
+
font-size: var(--ds-inspector-font-size);
|
|
79
|
+
line-height: var(--ds-inspector-line-height);
|
|
80
|
+
letter-spacing: var(--ds-inspector-letter-spacing);
|
|
81
|
+
font-weight: var(--ds-inspector-value-weight);
|
|
82
|
+
color: var(--color-text-secondary);
|
|
83
|
+
transition: background-color var(--motion-duration-fast) var(--motion-ease-standard);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.ds-inspector-bar:has(:focus-visible) {
|
|
87
|
+
outline: 2px solid var(--color-action-primary-bg);
|
|
88
|
+
outline-offset: 2px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.ds-inspector-bar__label {
|
|
92
|
+
position: relative;
|
|
93
|
+
flex-shrink: 0;
|
|
94
|
+
white-space: nowrap;
|
|
95
|
+
font-weight: var(--ds-inspector-label-weight);
|
|
96
|
+
color: var(--color-text-primary);
|
|
97
|
+
pointer-events: none;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.ds-inspector-bar__value {
|
|
101
|
+
position: relative;
|
|
102
|
+
min-width: 0;
|
|
103
|
+
overflow: hidden;
|
|
104
|
+
text-overflow: ellipsis;
|
|
105
|
+
white-space: nowrap;
|
|
106
|
+
font-variant-numeric: tabular-nums;
|
|
107
|
+
pointer-events: none;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* An invisible native control laid over the whole bar: the platform's
|
|
111
|
+
pointer, keyboard and screen-reader behaviour, none of its paint. */
|
|
112
|
+
.ds-inspector-bar__native {
|
|
113
|
+
position: absolute;
|
|
114
|
+
inset: 0;
|
|
115
|
+
width: 100%;
|
|
116
|
+
height: 100%;
|
|
117
|
+
margin: 0;
|
|
118
|
+
opacity: 0;
|
|
119
|
+
cursor: pointer;
|
|
120
|
+
appearance: none;
|
|
121
|
+
background: transparent;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Disabled: the system's 0.4 and not-allowed, on the whole bar. */
|
|
125
|
+
.ds-inspector-bar--disabled {
|
|
126
|
+
opacity: 0.4;
|
|
127
|
+
cursor: not-allowed;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.ds-inspector-bar--disabled * {
|
|
131
|
+
cursor: not-allowed;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* ============================================
|
|
135
|
+
SLIDER — the fill is the value
|
|
136
|
+
============================================ */
|
|
137
|
+
|
|
138
|
+
.ds-inspector-slider__fill {
|
|
139
|
+
position: absolute;
|
|
140
|
+
inset: 0 auto 0 0;
|
|
141
|
+
width: var(--ds-inspector-pct, 0%);
|
|
142
|
+
background: var(--color-action-neutral-bg-hover);
|
|
143
|
+
transition: background-color var(--motion-duration-fast) var(--motion-ease-standard);
|
|
144
|
+
pointer-events: none;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.ds-inspector-slider:not(.ds-inspector-bar--disabled):hover .ds-inspector-slider__fill,
|
|
148
|
+
.ds-inspector-slider:has(:active) .ds-inspector-slider__fill {
|
|
149
|
+
background: var(--color-action-neutral-bg-active);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* A short grip line just inside the fill's edge, held off both ends of the
|
|
153
|
+
bar so it never sits on the rounded corners. */
|
|
154
|
+
.ds-inspector-slider__grip {
|
|
155
|
+
position: absolute;
|
|
156
|
+
top: 30%;
|
|
157
|
+
bottom: 30%;
|
|
158
|
+
left: clamp(
|
|
159
|
+
var(--padding-200),
|
|
160
|
+
calc(var(--ds-inspector-pct, 0%) - var(--padding-200)),
|
|
161
|
+
calc(100% - var(--padding-200))
|
|
162
|
+
);
|
|
163
|
+
width: calc(var(--border-050) * 1.5);
|
|
164
|
+
border-radius: var(--radius-pill);
|
|
165
|
+
background: var(--color-text-secondary);
|
|
166
|
+
pointer-events: none;
|
|
167
|
+
/* Never `left`: the grip tracks the drag, and easing it would lag. */
|
|
168
|
+
transition:
|
|
169
|
+
top var(--motion-duration-fast) var(--motion-ease-standard),
|
|
170
|
+
bottom var(--motion-duration-fast) var(--motion-ease-standard),
|
|
171
|
+
background-color var(--motion-duration-fast) var(--motion-ease-standard);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.ds-inspector-slider:not(.ds-inspector-bar--disabled):hover .ds-inspector-slider__grip {
|
|
175
|
+
background: var(--color-text-primary);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* Held: the grip stands up taller, so the bar reads as gripped. */
|
|
179
|
+
.ds-inspector-slider:has(:active) .ds-inspector-slider__grip {
|
|
180
|
+
top: 22%;
|
|
181
|
+
bottom: 22%;
|
|
182
|
+
background: var(--color-text-primary);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.ds-inspector-slider .ds-inspector-bar__native {
|
|
186
|
+
cursor: ew-resize;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.ds-inspector-slider:has(:active) .ds-inspector-bar__native {
|
|
190
|
+
cursor: grabbing;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* The range's thumb is drawn one hairline-pair wide, so the input's
|
|
194
|
+
value-to-position mapping stays within a pixel of the painted fill. */
|
|
195
|
+
.ds-inspector-slider .ds-inspector-bar__native::-webkit-slider-thumb {
|
|
196
|
+
appearance: none;
|
|
197
|
+
width: var(--border-050);
|
|
198
|
+
height: 100%;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.ds-inspector-slider .ds-inspector-bar__native::-moz-range-thumb {
|
|
202
|
+
width: var(--border-050);
|
|
203
|
+
height: 100%;
|
|
204
|
+
border: none;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* ============================================
|
|
208
|
+
INPUT — name left, the field in the rest
|
|
209
|
+
============================================ */
|
|
210
|
+
|
|
211
|
+
.ds-inspector-input {
|
|
212
|
+
cursor: text;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.ds-inspector-input:not(.ds-inspector-bar--disabled):hover,
|
|
216
|
+
.ds-inspector-input:focus-within {
|
|
217
|
+
background: var(--color-action-neutral-bg-hover);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* The name is a real <label>, so a click on it focuses the field. */
|
|
221
|
+
.ds-inspector-input .ds-inspector-bar__label {
|
|
222
|
+
pointer-events: auto;
|
|
223
|
+
cursor: text;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.ds-inspector-input__field {
|
|
227
|
+
flex: 1;
|
|
228
|
+
/* width 0 + flex 1: the field takes the bar's spare room and never asks
|
|
229
|
+
for its intrinsic 20-character width, which would push its container. */
|
|
230
|
+
width: 0;
|
|
231
|
+
min-width: 0;
|
|
232
|
+
padding: 0;
|
|
233
|
+
border: none;
|
|
234
|
+
outline: none;
|
|
235
|
+
background: transparent;
|
|
236
|
+
font: inherit;
|
|
237
|
+
text-align: right;
|
|
238
|
+
color: var(--color-text-primary);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.ds-inspector-input__field::placeholder {
|
|
242
|
+
color: var(--color-text-tertiary);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* ============================================
|
|
246
|
+
TOGGLE SWITCH — the whole bar is the switch
|
|
247
|
+
============================================ */
|
|
248
|
+
|
|
249
|
+
.ds-inspector-toggle-switch:not(.ds-inspector-bar--disabled):hover {
|
|
250
|
+
background: var(--color-action-neutral-bg-hover);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.ds-inspector-toggle-switch:has(:active) {
|
|
254
|
+
background: var(--color-action-neutral-bg-active);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.ds-inspector-toggle-switch__track {
|
|
258
|
+
position: relative;
|
|
259
|
+
flex-shrink: 0;
|
|
260
|
+
width: calc(var(--padding-400) * 1.75);
|
|
261
|
+
height: var(--padding-400);
|
|
262
|
+
border-radius: var(--radius-pill);
|
|
263
|
+
background: var(--color-action-neutral-bg-active);
|
|
264
|
+
transition: background-color var(--motion-duration-fast) var(--motion-ease-standard);
|
|
265
|
+
pointer-events: none;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.ds-inspector-toggle-switch__knob {
|
|
269
|
+
position: absolute;
|
|
270
|
+
top: var(--border-050);
|
|
271
|
+
left: var(--border-050);
|
|
272
|
+
width: calc(var(--padding-400) - 2 * var(--border-050));
|
|
273
|
+
height: calc(var(--padding-400) - 2 * var(--border-050));
|
|
274
|
+
border-radius: var(--radius-pill);
|
|
275
|
+
background: var(--color-bg-container-primary);
|
|
276
|
+
transition: transform var(--motion-duration-base) var(--motion-ease-entrance);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/* On reads from the native checkbox itself, so the paint is right whether
|
|
280
|
+
the switch is controlled or not. Monochrome on purpose: on is the primary
|
|
281
|
+
text colour, not the action colour. */
|
|
282
|
+
.ds-inspector-toggle-switch:has(.ds-inspector-bar__native:checked) .ds-inspector-toggle-switch__track {
|
|
283
|
+
background: var(--color-text-primary);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.ds-inspector-toggle-switch:has(.ds-inspector-bar__native:checked) .ds-inspector-toggle-switch__knob {
|
|
287
|
+
transform: translateX(calc(var(--padding-400) * 0.75));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/* ============================================
|
|
291
|
+
SEGMENTED CONTROL — name left, choices right
|
|
292
|
+
============================================ */
|
|
293
|
+
|
|
294
|
+
/* The bar's own inset shrinks on the right, so the selected segment sits
|
|
295
|
+
as an inset chip in the bar's end rather than touching its edge. */
|
|
296
|
+
.ds-inspector-segmented-control {
|
|
297
|
+
padding-right: var(--padding-100);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/* Focus rings the segment, not the whole bar. */
|
|
301
|
+
.ds-inspector-segmented-control:has(:focus-visible) {
|
|
302
|
+
outline: none;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.ds-inspector-segmented-control__segments {
|
|
306
|
+
position: relative;
|
|
307
|
+
display: flex;
|
|
308
|
+
gap: var(--gap-050);
|
|
309
|
+
min-width: 0;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.ds-inspector-segmented-control__segment {
|
|
313
|
+
position: relative;
|
|
314
|
+
padding: var(--padding-050) var(--padding-200);
|
|
315
|
+
/* The chip nests in the bar's end, so it shares the bar's shape. */
|
|
316
|
+
border-radius: var(--radius-pill);
|
|
317
|
+
white-space: nowrap;
|
|
318
|
+
cursor: pointer;
|
|
319
|
+
transition:
|
|
320
|
+
background-color var(--motion-duration-fast) var(--motion-ease-standard),
|
|
321
|
+
color var(--motion-duration-fast) var(--motion-ease-standard);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.ds-inspector-segmented-control__segment:not(:has(:disabled)):hover {
|
|
325
|
+
color: var(--color-text-primary);
|
|
326
|
+
background: var(--color-action-passive-bg-active);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.ds-inspector-segmented-control__segment:has(:checked) {
|
|
330
|
+
color: var(--color-text-primary);
|
|
331
|
+
font-weight: var(--ds-inspector-label-weight);
|
|
332
|
+
background: var(--color-action-neutral-bg-hover);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.ds-inspector-segmented-control__segment:not(:has(:disabled)):active {
|
|
336
|
+
background: var(--color-action-neutral-bg-active);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.ds-inspector-segmented-control__segment:has(:focus-visible) {
|
|
340
|
+
outline: 2px solid var(--color-action-primary-bg);
|
|
341
|
+
outline-offset: 0;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.ds-inspector-segmented-control__segment:has(:disabled) {
|
|
345
|
+
opacity: 0.4;
|
|
346
|
+
cursor: not-allowed;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/* The radio itself is invisible; its segment is the target. */
|
|
350
|
+
.ds-inspector-segmented-control__radio {
|
|
351
|
+
position: absolute;
|
|
352
|
+
inset: 0;
|
|
353
|
+
margin: 0;
|
|
354
|
+
opacity: 0;
|
|
355
|
+
cursor: inherit;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/* ============================================
|
|
359
|
+
DROPDOWN — the library Dropdown, dressed as a bar
|
|
360
|
+
============================================ */
|
|
361
|
+
|
|
362
|
+
.ds-inspector-dropdown {
|
|
363
|
+
position: relative;
|
|
364
|
+
min-width: 0;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.ds-inspector-dropdown__label {
|
|
368
|
+
position: absolute;
|
|
369
|
+
top: 50%;
|
|
370
|
+
left: var(--ds-inspector-inset);
|
|
371
|
+
z-index: 1;
|
|
372
|
+
transform: translateY(-50%);
|
|
373
|
+
white-space: nowrap;
|
|
374
|
+
font-family: var(--ds-inspector-font-family);
|
|
375
|
+
font-size: var(--ds-inspector-font-size);
|
|
376
|
+
font-weight: var(--ds-inspector-label-weight);
|
|
377
|
+
line-height: var(--ds-inspector-line-height);
|
|
378
|
+
letter-spacing: var(--ds-inspector-letter-spacing);
|
|
379
|
+
color: var(--color-text-primary);
|
|
380
|
+
pointer-events: none;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.ds-inspector-dropdown .ds-dropdown__trigger {
|
|
384
|
+
box-sizing: border-box;
|
|
385
|
+
height: var(--ds-inspector-height);
|
|
386
|
+
padding: 0 calc(var(--ds-inspector-inset) - var(--padding-100)) 0
|
|
387
|
+
calc(var(--ds-inspector-inset) + var(--ds-inspector-label-width, 0px) + var(--gap-300));
|
|
388
|
+
border-color: transparent;
|
|
389
|
+
border-radius: var(--radius-pill);
|
|
390
|
+
background: var(--color-action-passive-bg-hover);
|
|
391
|
+
transition: background-color var(--motion-duration-fast) var(--motion-ease-standard);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.ds-inspector-dropdown .ds-dropdown__trigger:hover:not(:focus-visible),
|
|
395
|
+
.ds-inspector-dropdown .ds-dropdown--open .ds-dropdown__trigger {
|
|
396
|
+
border-color: transparent;
|
|
397
|
+
background: var(--color-action-neutral-bg-hover);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.ds-inspector-dropdown .ds-dropdown__trigger:active {
|
|
401
|
+
background: var(--color-action-neutral-bg-active);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/* The trigger's selected-border focus becomes the family's ring, so every
|
|
405
|
+
inspector control shows focus the same way. */
|
|
406
|
+
.ds-inspector-dropdown .ds-dropdown__trigger:focus-visible {
|
|
407
|
+
border-color: transparent;
|
|
408
|
+
outline: 2px solid var(--color-action-primary-bg);
|
|
409
|
+
outline-offset: 2px;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.ds-inspector-dropdown .ds-dropdown__value {
|
|
413
|
+
text-align: right;
|
|
414
|
+
font-family: var(--ds-dropdown-font, var(--ds-inspector-font-family));
|
|
415
|
+
font-size: var(--ds-inspector-font-size);
|
|
416
|
+
font-weight: var(--ds-inspector-value-weight);
|
|
417
|
+
line-height: var(--ds-inspector-line-height);
|
|
418
|
+
letter-spacing: var(--ds-inspector-letter-spacing);
|
|
419
|
+
color: var(--color-text-secondary);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.ds-inspector-dropdown .ds-dropdown__chevron {
|
|
423
|
+
--icon-size: var(--icon-size-500);
|
|
424
|
+
color: var(--color-icon-secondary);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/* ============================================
|
|
428
|
+
SECTION — a collapsible, titled run of controls
|
|
429
|
+
============================================ */
|
|
430
|
+
|
|
431
|
+
.ds-inspector-section {
|
|
432
|
+
display: flex;
|
|
433
|
+
flex-direction: column;
|
|
434
|
+
border-bottom: var(--border-025) solid var(--color-divider);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.ds-inspector-section:last-of-type {
|
|
438
|
+
border-bottom: none;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.ds-inspector-section__trigger {
|
|
442
|
+
display: flex;
|
|
443
|
+
align-items: center;
|
|
444
|
+
justify-content: space-between;
|
|
445
|
+
gap: var(--gap-200);
|
|
446
|
+
width: 100%;
|
|
447
|
+
padding: var(--padding-300) 0;
|
|
448
|
+
background: none;
|
|
449
|
+
border: none;
|
|
450
|
+
border-radius: var(--radius-200);
|
|
451
|
+
cursor: pointer;
|
|
452
|
+
text-align: left;
|
|
453
|
+
font-family: var(--font-paragraph-emphasis-family);
|
|
454
|
+
font-size: var(--font-paragraph-emphasis-size);
|
|
455
|
+
font-weight: var(--font-paragraph-emphasis-weight);
|
|
456
|
+
line-height: var(--font-paragraph-emphasis-line-height);
|
|
457
|
+
letter-spacing: var(--font-paragraph-emphasis-letter-spacing);
|
|
458
|
+
color: var(--color-text-primary);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.ds-inspector-section__trigger:focus-visible {
|
|
462
|
+
outline: 2px solid var(--color-action-primary-bg);
|
|
463
|
+
outline-offset: 2px;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.ds-inspector-section__icon {
|
|
467
|
+
flex-shrink: 0;
|
|
468
|
+
--icon-size: var(--icon-size-500);
|
|
469
|
+
color: var(--color-icon-secondary);
|
|
470
|
+
transition: transform var(--motion-duration-slow) var(--motion-ease-emphasized);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.ds-inspector-section--open .ds-inspector-section__icon {
|
|
474
|
+
transform: rotate(180deg);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.ds-inspector-section__panel {
|
|
478
|
+
display: grid;
|
|
479
|
+
/* minmax(0, 1fr), not auto: an auto column grows to its content's
|
|
480
|
+
intrinsic width, which would let a wide control push the panel. */
|
|
481
|
+
grid-template-columns: minmax(0, 1fr);
|
|
482
|
+
grid-template-rows: 0fr;
|
|
483
|
+
transition: grid-template-rows var(--motion-duration-slow) var(--motion-ease-emphasized);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
.ds-inspector-section--open .ds-inspector-section__panel {
|
|
487
|
+
grid-template-rows: 1fr;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/* Clipped while closed or moving; released once settled open, so a
|
|
491
|
+
Dropdown menu inside can open past the section's edge. */
|
|
492
|
+
.ds-inspector-section__body {
|
|
493
|
+
min-height: 0;
|
|
494
|
+
overflow: hidden;
|
|
495
|
+
display: flex;
|
|
496
|
+
flex-direction: column;
|
|
497
|
+
gap: var(--gap-200);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.ds-inspector-section--settled .ds-inspector-section__body {
|
|
501
|
+
overflow: visible;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/* The section's foot: an empty flex item sized so the gap above it plus
|
|
505
|
+
its own height equal the trigger's padding, so the content sits as far
|
|
506
|
+
from the divider as the title does. Padding instead would keep a closed
|
|
507
|
+
panel from reaching zero height. */
|
|
508
|
+
.ds-inspector-section__body::after {
|
|
509
|
+
content: "";
|
|
510
|
+
flex: 0 0 calc(var(--padding-300) - var(--gap-200));
|
|
511
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { DropdownOption, DropdownOptionGroup } from '../Dropdown/Dropdown';
|
|
3
|
+
/** Props owned by InspectorDropdown itself — everything else falls through to the wrapper. */
|
|
4
|
+
type InspectorDropdownOwnProps = {
|
|
5
|
+
/** The setting's name, drawn inside the bar on the left. Also the dropdown's accessible name. */
|
|
6
|
+
label: string;
|
|
7
|
+
/** Currently selected value. */
|
|
8
|
+
value?: string;
|
|
9
|
+
/** Available options (flat list). Each option's `font` previews the face, as in Dropdown. */
|
|
10
|
+
options: DropdownOption[];
|
|
11
|
+
/** Optional grouped options. When provided, renders groups with labels and separators. */
|
|
12
|
+
groups?: DropdownOptionGroup[];
|
|
13
|
+
/** Shown on the right when nothing is selected. */
|
|
14
|
+
placeholder?: string;
|
|
15
|
+
/** Whether the dropdown is disabled. */
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
/** Component size, matching Button: `default` is 40px tall, `compact` 32px. */
|
|
18
|
+
size?: 'default' | 'compact';
|
|
19
|
+
/** Called with the newly selected value. */
|
|
20
|
+
onValueChange?: (value: string) => void;
|
|
21
|
+
/** Additional CSS classes, applied to the wrapper. */
|
|
22
|
+
className?: string;
|
|
23
|
+
};
|
|
24
|
+
export interface InspectorDropdownProps extends InspectorDropdownOwnProps, Omit<React.ComponentPropsWithoutRef<'div'>, keyof InspectorDropdownOwnProps | 'onChange'> {
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A one-row select for inspector panels: the name on the left, the chosen
|
|
28
|
+
* option and a chevron on the right. It is the library Dropdown with its
|
|
29
|
+
* trigger dressed as an inspector bar, so the menu, its keyboard model and
|
|
30
|
+
* its per-option font previews are Dropdown's own. The form counterpart,
|
|
31
|
+
* with a label above, is Dropdown itself.
|
|
32
|
+
*
|
|
33
|
+
* The name is an overlay the trigger reserves room for. It is measured,
|
|
34
|
+
* since names differ in width and a web font can change one after load,
|
|
35
|
+
* and it lets clicks through, so the whole bar opens the menu. Forwards a
|
|
36
|
+
* ref to the wrapper and spreads unrecognised props onto it.
|
|
37
|
+
*/
|
|
38
|
+
export declare const InspectorDropdown: React.ForwardRefExoticComponent<InspectorDropdownProps & React.RefAttributes<HTMLDivElement>>;
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
|
+
import React, { useRef, useState, useLayoutEffect } from "react";
|
|
4
|
+
import { Dropdown } from "../Dropdown/Dropdown.js";
|
|
5
|
+
import "./Inspector.css";
|
|
6
|
+
const InspectorDropdown = React.forwardRef(
|
|
7
|
+
({
|
|
8
|
+
label,
|
|
9
|
+
value,
|
|
10
|
+
options,
|
|
11
|
+
groups,
|
|
12
|
+
placeholder,
|
|
13
|
+
disabled = false,
|
|
14
|
+
size = "default",
|
|
15
|
+
onValueChange,
|
|
16
|
+
className = "",
|
|
17
|
+
style,
|
|
18
|
+
...rest
|
|
19
|
+
}, ref) => {
|
|
20
|
+
const dropdownId = React.useId();
|
|
21
|
+
const labelRef = useRef(null);
|
|
22
|
+
const [labelWidth, setLabelWidth] = useState(0);
|
|
23
|
+
useLayoutEffect(() => {
|
|
24
|
+
const node = labelRef.current;
|
|
25
|
+
if (!node) return;
|
|
26
|
+
const measure = () => setLabelWidth(node.offsetWidth);
|
|
27
|
+
measure();
|
|
28
|
+
const observer = new ResizeObserver(measure);
|
|
29
|
+
observer.observe(node);
|
|
30
|
+
return () => observer.disconnect();
|
|
31
|
+
}, []);
|
|
32
|
+
const classes = [
|
|
33
|
+
"ds-inspector-dropdown",
|
|
34
|
+
size === "compact" ? "ds-inspector-dropdown--compact" : "",
|
|
35
|
+
className
|
|
36
|
+
].filter(Boolean).join(" ");
|
|
37
|
+
return /* @__PURE__ */ jsxs(
|
|
38
|
+
"div",
|
|
39
|
+
{
|
|
40
|
+
...rest,
|
|
41
|
+
ref,
|
|
42
|
+
className: classes,
|
|
43
|
+
style: { ...style, "--ds-inspector-label-width": `${labelWidth}px` },
|
|
44
|
+
children: [
|
|
45
|
+
/* @__PURE__ */ jsx("span", { ref: labelRef, className: "ds-inspector-dropdown__label", "aria-hidden": "true", children: label }),
|
|
46
|
+
/* @__PURE__ */ jsx(
|
|
47
|
+
Dropdown,
|
|
48
|
+
{
|
|
49
|
+
id: dropdownId,
|
|
50
|
+
"aria-label": label,
|
|
51
|
+
size,
|
|
52
|
+
value,
|
|
53
|
+
options,
|
|
54
|
+
groups,
|
|
55
|
+
placeholder,
|
|
56
|
+
disabled,
|
|
57
|
+
onValueChange
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
InspectorDropdown.displayName = "InspectorDropdown";
|
|
66
|
+
export {
|
|
67
|
+
InspectorDropdown
|
|
68
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/** Props owned by InspectorInput itself — everything else falls through to the <input>. */
|
|
3
|
+
type InspectorInputOwnProps = {
|
|
4
|
+
/** The setting's name, drawn inside the bar on the left, as the field's real <label>. */
|
|
5
|
+
label: string;
|
|
6
|
+
/** Current value. */
|
|
7
|
+
value?: string;
|
|
8
|
+
/** Input type: a curated subset of the single-line text types. */
|
|
9
|
+
type?: 'text' | 'email' | 'url' | 'search' | 'tel';
|
|
10
|
+
/** Component size, matching Button: `default` is 40px tall, `compact` 32px (not the native character-width `size` attribute). */
|
|
11
|
+
size?: 'default' | 'compact';
|
|
12
|
+
/**
|
|
13
|
+
* Convenience callback receiving the value directly.
|
|
14
|
+
* Fires alongside `onChange`, which keeps the standard React event signature
|
|
15
|
+
* so form libraries work unmodified.
|
|
16
|
+
*/
|
|
17
|
+
onValueChange?: (value: string) => void;
|
|
18
|
+
/** Additional CSS classes, applied to the bar rather than the <input>. */
|
|
19
|
+
className?: string;
|
|
20
|
+
};
|
|
21
|
+
export interface InspectorInputProps extends InspectorInputOwnProps, Omit<React.ComponentPropsWithoutRef<'input'>, keyof InspectorInputOwnProps> {
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A one-row text field for inspector panels: the name on the left, the
|
|
25
|
+
* typed value right-aligned in the rest of the bar. The form counterpart,
|
|
26
|
+
* with a label above and helper text below, is Input.
|
|
27
|
+
*
|
|
28
|
+
* Forwards a ref to the underlying <input> and spreads unrecognised props
|
|
29
|
+
* onto it, so `autoComplete`, `maxLength` and form registration all work.
|
|
30
|
+
*/
|
|
31
|
+
export declare const InspectorInput: React.ForwardRefExoticComponent<InspectorInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import "./Inspector.css";
|
|
5
|
+
const InspectorInput = React.forwardRef(
|
|
6
|
+
({
|
|
7
|
+
label,
|
|
8
|
+
value,
|
|
9
|
+
type = "text",
|
|
10
|
+
size = "default",
|
|
11
|
+
disabled = false,
|
|
12
|
+
onChange,
|
|
13
|
+
onValueChange,
|
|
14
|
+
className = "",
|
|
15
|
+
id,
|
|
16
|
+
...rest
|
|
17
|
+
}, ref) => {
|
|
18
|
+
const generatedId = React.useId();
|
|
19
|
+
const inputId = id ?? generatedId;
|
|
20
|
+
const classes = [
|
|
21
|
+
"ds-inspector-bar",
|
|
22
|
+
"ds-inspector-input",
|
|
23
|
+
size === "compact" ? "ds-inspector-bar--compact" : "",
|
|
24
|
+
disabled ? "ds-inspector-bar--disabled" : "",
|
|
25
|
+
className
|
|
26
|
+
].filter(Boolean).join(" ");
|
|
27
|
+
const handleChange = (e) => {
|
|
28
|
+
onChange?.(e);
|
|
29
|
+
onValueChange?.(e.target.value);
|
|
30
|
+
};
|
|
31
|
+
return /* @__PURE__ */ jsxs("div", { className: classes, children: [
|
|
32
|
+
/* @__PURE__ */ jsx("label", { htmlFor: inputId, className: "ds-inspector-bar__label", children: label }),
|
|
33
|
+
/* @__PURE__ */ jsx(
|
|
34
|
+
"input",
|
|
35
|
+
{
|
|
36
|
+
...rest,
|
|
37
|
+
ref,
|
|
38
|
+
id: inputId,
|
|
39
|
+
className: "ds-inspector-input__field",
|
|
40
|
+
type,
|
|
41
|
+
value,
|
|
42
|
+
disabled,
|
|
43
|
+
onChange: handleChange
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
] });
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
InspectorInput.displayName = "InspectorInput";
|
|
50
|
+
export {
|
|
51
|
+
InspectorInput
|
|
52
|
+
};
|