@crewlethq/ui 0.49.1 → 0.50.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 +14 -0
- package/dist/ContrastBadge/ContrastBadge.d.ts +25 -3
- package/dist/ContrastBadge/index.d.ts +1 -1
- package/dist/ContrastBadge/index.js +1 -1
- package/dist/EditingStatus/EditingStatus.d.ts +15 -1
- package/dist/EditingStatus/index.js +3 -1
- package/dist/InlineEditor/index.js +4 -2
- package/dist/LogoSlot/index.js +2 -2
- package/dist/RichEditor/index.js +1 -1
- package/dist/SetupWizard/SetupWizard.d.ts +77 -0
- package/dist/SetupWizard/index.d.ts +3 -0
- package/dist/SetupWizard/index.js +11 -0
- package/dist/SetupWizard-Y5BTXZX5.css +197 -0
- package/dist/Stepper/Stepper.d.ts +60 -11
- package/dist/Stepper/index.d.ts +1 -1
- package/dist/Stepper/index.js +1 -1
- package/dist/Stepper-QEJI5RGX.css +355 -0
- package/dist/{chunk-ITI2Z44C.js → chunk-BEAXJARF.js} +1 -1
- package/dist/{chunk-7PCDKRNB.js → chunk-JWNYI7UD.js} +1 -1
- package/dist/chunk-KKHCUHWS.js +107 -0
- package/dist/chunk-LDGQIDIZ.js +129 -0
- package/dist/{chunk-MULOZYHL.js → chunk-MMOXWAUG.js} +16 -4
- package/dist/{chunk-B235ANDO.js → chunk-NSSOWQCJ.js} +20 -5
- package/dist/{chunk-7P336RA5.js → chunk-QGRA2RHC.js} +1 -1
- package/dist/generated/index.d.ts +1 -0
- package/dist/generated/index.js +24 -23
- package/dist/index.js +24 -23
- package/dist/styles.css +391 -12
- package/package.json +3 -3
- package/dist/Stepper-HW4JM3YH.css +0 -176
- package/dist/chunk-5QWIJSQM.js +0 -73
- package/dist/{Editor-ZYDXHGJE.js → Editor-MLGK3KOG.js} +1 -1
- /package/dist/{chunk-3WMOEGEL.js → chunk-HNQ2EIMR.js} +0 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The stepper: where a sequence has got to (Stepper.tsx).
|
|
3
|
+
*
|
|
4
|
+
* THE APPROVED DESIGN'S STEP ROW. Each step is a 20px pill inside the default
|
|
5
|
+
* hairline, the 11px step at the medium weight, 8px inside each end and 5px
|
|
6
|
+
* between a mark and the word; two pills are joined by a 10px rule in the
|
|
7
|
+
* strong hairline's colour with 6px either side of it. A finished step is the
|
|
8
|
+
* secondary ink behind a check, the current step its tone's soft tint under
|
|
9
|
+
* its ink behind its dot, and a step still to come the tertiary ink.
|
|
10
|
+
*
|
|
11
|
+
* EVERY INK IS A MEASURED PAIR. The secondary and tertiary steps are held to
|
|
12
|
+
* 4.5:1 on every opaque rung, and each tone's ink to 4.5:1 on its own soft
|
|
13
|
+
* tint over every rung, by the palette suite in @crewlethq/tokens; the pill
|
|
14
|
+
* draws nothing else. The current pill is the design's `work-soft` under
|
|
15
|
+
* `work-ink` in the default tone, and a Tag's colours in every tone, which is
|
|
16
|
+
* what a chip naming a state is in this kit.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
.crewlet-stepper {
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-wrap: wrap;
|
|
22
|
+
align-items: center;
|
|
23
|
+
/* The design's 6px between a pill and the rule beside it: a step and a half. */
|
|
24
|
+
gap: calc(var(--spacing-1) * 1.5);
|
|
25
|
+
min-width: 0;
|
|
26
|
+
margin: 0;
|
|
27
|
+
padding: 0;
|
|
28
|
+
list-style: none;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.crewlet-stepper__step {
|
|
32
|
+
display: inline-flex;
|
|
33
|
+
align-items: center;
|
|
34
|
+
gap: calc(var(--spacing-1) * 1.5);
|
|
35
|
+
min-width: 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/*
|
|
39
|
+
* THE RULE BETWEEN TWO STEPS is drawn by the second of them, so a sequence of
|
|
40
|
+
* one draws none and nothing trails the last. A pseudo-element, because it is
|
|
41
|
+
* furniture: it carries nothing a reader has to be told, and the list already
|
|
42
|
+
* says the steps are in order. 10px by a hairline, the measure of this one
|
|
43
|
+
* mark, and fixed at every density as a hairline is.
|
|
44
|
+
*/
|
|
45
|
+
.crewlet-stepper__step + .crewlet-stepper__step::before {
|
|
46
|
+
content: '';
|
|
47
|
+
flex: none;
|
|
48
|
+
width: 10px;
|
|
49
|
+
height: 1px;
|
|
50
|
+
background: var(--color-border-strong);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.crewlet-stepper__chip {
|
|
54
|
+
display: inline-flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
/* The design's 5px between the check or the dot and the word. */
|
|
57
|
+
gap: calc(var(--spacing-1) * 1.25);
|
|
58
|
+
box-sizing: border-box;
|
|
59
|
+
/* The design's 20px pill, which follows the density with every other step
|
|
60
|
+
height in the kit. It labels and takes no press, so the 24px target floor
|
|
61
|
+
is not its to clear. */
|
|
62
|
+
height: calc(20px * var(--density, 1));
|
|
63
|
+
max-width: 100%;
|
|
64
|
+
padding: 0 var(--spacing-2);
|
|
65
|
+
border: 1px solid var(--color-border-default);
|
|
66
|
+
border-radius: var(--radius-pill);
|
|
67
|
+
color: var(--color-text-tertiary);
|
|
68
|
+
font-family: var(--font-family-sans);
|
|
69
|
+
font-size: var(--font-size-2xs);
|
|
70
|
+
font-weight: var(--font-weight-medium);
|
|
71
|
+
line-height: 1;
|
|
72
|
+
white-space: nowrap;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.crewlet-stepper__label {
|
|
76
|
+
min-width: 0;
|
|
77
|
+
overflow: hidden;
|
|
78
|
+
text-overflow: ellipsis;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.crewlet-stepper__check {
|
|
82
|
+
flex: none;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.crewlet-stepper__step--done > .crewlet-stepper__chip {
|
|
86
|
+
color: var(--color-text-secondary);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/*
|
|
90
|
+
* THE CURRENT STEP, in its tone: the pill in a row, the mark in a column. The
|
|
91
|
+
* hairline goes transparent rather than away, so the pill keeps its box and
|
|
92
|
+
* the row does not move by a pixel as the current step advances along it, and
|
|
93
|
+
* so forced-colors mode, which draws a transparent border in the system's own
|
|
94
|
+
* colour, still outlines it.
|
|
95
|
+
*/
|
|
96
|
+
.crewlet-stepper--info .crewlet-stepper__step--current > .crewlet-stepper__chip,
|
|
97
|
+
.crewlet-stepper--info .crewlet-stepper__step--current .crewlet-stepper__mark {
|
|
98
|
+
border-color: transparent;
|
|
99
|
+
background: var(--color-feedback-info-soft);
|
|
100
|
+
color: var(--color-feedback-info-ink);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.crewlet-stepper--success .crewlet-stepper__step--current > .crewlet-stepper__chip,
|
|
104
|
+
.crewlet-stepper--success .crewlet-stepper__step--current .crewlet-stepper__mark {
|
|
105
|
+
border-color: transparent;
|
|
106
|
+
background: var(--color-feedback-success-soft);
|
|
107
|
+
color: var(--color-feedback-success-ink);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.crewlet-stepper--warning .crewlet-stepper__step--current > .crewlet-stepper__chip,
|
|
111
|
+
.crewlet-stepper--warning .crewlet-stepper__step--current .crewlet-stepper__mark {
|
|
112
|
+
border-color: transparent;
|
|
113
|
+
background: var(--color-feedback-warning-soft);
|
|
114
|
+
color: var(--color-feedback-warning-ink);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.crewlet-stepper--danger .crewlet-stepper__step--current > .crewlet-stepper__chip,
|
|
118
|
+
.crewlet-stepper--danger .crewlet-stepper__step--current .crewlet-stepper__mark {
|
|
119
|
+
border-color: transparent;
|
|
120
|
+
background: var(--color-feedback-danger-soft);
|
|
121
|
+
color: var(--color-feedback-danger-ink);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.crewlet-stepper--brand .crewlet-stepper__step--current > .crewlet-stepper__chip,
|
|
125
|
+
.crewlet-stepper--brand .crewlet-stepper__step--current .crewlet-stepper__mark {
|
|
126
|
+
border-color: transparent;
|
|
127
|
+
background: var(--color-brand-accent-soft);
|
|
128
|
+
color: var(--color-brand-accent-ink);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/*
|
|
132
|
+
* A STEP THAT NEEDS ATTENTION is the warning tint under the warning ink, the
|
|
133
|
+
* pair the palette suite holds to 4.5:1 over every rung, with the warning
|
|
134
|
+
* glyph: the pill of a step that is not current, and the mark of any step in
|
|
135
|
+
* a column. A CURRENT pill keeps its tone, so the row still says which step
|
|
136
|
+
* the reader is on, and carries the glyph in that tone's ink.
|
|
137
|
+
*/
|
|
138
|
+
.crewlet-stepper .crewlet-stepper__step--attention:not(.crewlet-stepper__step--current) > .crewlet-stepper__chip,
|
|
139
|
+
.crewlet-stepper .crewlet-stepper__step--attention .crewlet-stepper__mark {
|
|
140
|
+
border-color: transparent;
|
|
141
|
+
background: var(--color-feedback-warning-soft);
|
|
142
|
+
color: var(--color-feedback-warning-ink);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.crewlet-stepper__warning {
|
|
146
|
+
flex: none;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/*
|
|
150
|
+
* INSIDE THE PILL THE DOT IS THE PILL'S OWN INK, as a Tag's is, and for the
|
|
151
|
+
* Tag's reason: StatusDot paints the fill step, measured as a mark on the
|
|
152
|
+
* opaque rungs, and here it stands on the tone's soft tint instead, where
|
|
153
|
+
* nothing measures it. The ink is the step that IS measured on that tint, at
|
|
154
|
+
* 4.5:1, and the colour of the word beside it. A pulsing dot keeps
|
|
155
|
+
* StatusDot's halo, the same soft step drawn once more over the pill, which
|
|
156
|
+
* stands dE 3.92 off it at the least (the light warning pill on the frame).
|
|
157
|
+
*
|
|
158
|
+
* Two classes deep, so it beats StatusDot's own tone rule whichever order the
|
|
159
|
+
* bundle writes the two files in.
|
|
160
|
+
*/
|
|
161
|
+
.crewlet-stepper .crewlet-stepper__dot {
|
|
162
|
+
background: currentColor;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/*
|
|
166
|
+
* FORCED COLORS repaints every background to Canvas, which takes the current
|
|
167
|
+
* step's tint and its dot, so the one step the row is about would read like
|
|
168
|
+
* any other. The current pill is drawn as the system draws a selection,
|
|
169
|
+
* HighlightText on Highlight, with its dot in the same ink, and the rule
|
|
170
|
+
* between two steps in GrayText; the chips' hairlines are borders, which the
|
|
171
|
+
* mode keeps in its own colour.
|
|
172
|
+
*
|
|
173
|
+
* THE CURRENT PILL OPTS OUT OF THE ADJUSTMENT, which is safe only because
|
|
174
|
+
* every colour it draws in that mode is a system colour. Left to it, Chromium
|
|
175
|
+
* paints a Canvas backplate behind every run of text, and HighlightText is
|
|
176
|
+
* written for Highlight, not for Canvas: in a dark high-contrast theme the
|
|
177
|
+
* step's words drew black on black (measured in Chromium 153 with the mode
|
|
178
|
+
* emulated). Opting out also keeps shadows, so the pulsing dot's halo, which
|
|
179
|
+
* is the tone's soft tint and no system colour, is held still inside it.
|
|
180
|
+
*/
|
|
181
|
+
/*
|
|
182
|
+
* A STEP THAT CAN BE PICKED is a button with no look of its own: the pill or
|
|
183
|
+
* the row it carries draws it, and a hover lays the kit's hover overlay over
|
|
184
|
+
* a step at rest. The current step keeps its tint under the pointer, since a
|
|
185
|
+
* tone rule weighs more than the hover. The ring sits inside the step's own
|
|
186
|
+
* box, over the pill's hairline rather than a second line beside it.
|
|
187
|
+
*/
|
|
188
|
+
.crewlet-stepper__target {
|
|
189
|
+
appearance: none;
|
|
190
|
+
margin: 0;
|
|
191
|
+
background: transparent;
|
|
192
|
+
font: inherit;
|
|
193
|
+
text-align: start;
|
|
194
|
+
cursor: pointer;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.crewlet-stepper__chip.crewlet-stepper__target {
|
|
198
|
+
font-family: var(--font-family-sans);
|
|
199
|
+
font-size: var(--font-size-2xs);
|
|
200
|
+
font-weight: var(--font-weight-medium);
|
|
201
|
+
line-height: 1;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.crewlet-stepper__target:hover {
|
|
205
|
+
background: var(--color-surface-hover);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.crewlet-stepper__target:focus-visible {
|
|
209
|
+
outline: 2px solid var(--color-focus);
|
|
210
|
+
outline-offset: var(--size-focus-ring-inset-offset);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/*
|
|
214
|
+
* THE COLUMN, a setup wizard's rail. Each step is a row: a 20px mark (the
|
|
215
|
+
* check, the dot, the warning glyph, or the step's number for one still to
|
|
216
|
+
* come) and beside it the label, with the description and the status words
|
|
217
|
+
* under it. A hairline in the strong hairline's colour joins one mark to the
|
|
218
|
+
* next, 4px clear of each, drawn by the step it leaves so nothing trails the
|
|
219
|
+
* last. The mark is the pills' 20px, following the density as they do, and
|
|
220
|
+
* the label's line is the mark's height so the two stand level. The row's words follow the same three inks as the pills: the current
|
|
221
|
+
* step primary and medium, a finished one secondary, the rest tertiary.
|
|
222
|
+
*/
|
|
223
|
+
.crewlet-stepper--vertical {
|
|
224
|
+
flex-direction: column;
|
|
225
|
+
flex-wrap: nowrap;
|
|
226
|
+
align-items: stretch;
|
|
227
|
+
gap: 0;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.crewlet-stepper--vertical .crewlet-stepper__step {
|
|
231
|
+
position: relative;
|
|
232
|
+
display: block;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.crewlet-stepper--vertical .crewlet-stepper__step + .crewlet-stepper__step::before {
|
|
236
|
+
content: none;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.crewlet-stepper--vertical .crewlet-stepper__step:not(:last-child)::after {
|
|
240
|
+
content: '';
|
|
241
|
+
position: absolute;
|
|
242
|
+
top: calc(var(--spacing-2) + 20px * var(--density, 1) + 4px);
|
|
243
|
+
bottom: calc(4px - var(--spacing-2));
|
|
244
|
+
left: calc(var(--spacing-2) + 10px * var(--density, 1));
|
|
245
|
+
width: 1px;
|
|
246
|
+
background: var(--color-border-strong);
|
|
247
|
+
pointer-events: none;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.crewlet-stepper__row {
|
|
251
|
+
display: flex;
|
|
252
|
+
align-items: flex-start;
|
|
253
|
+
gap: var(--spacing-3);
|
|
254
|
+
box-sizing: border-box;
|
|
255
|
+
width: 100%;
|
|
256
|
+
padding: var(--spacing-2);
|
|
257
|
+
border: 0;
|
|
258
|
+
border-radius: var(--radius-md);
|
|
259
|
+
color: var(--color-text-tertiary);
|
|
260
|
+
font-family: var(--font-family-sans);
|
|
261
|
+
font-size: var(--font-size-sm);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.crewlet-stepper__mark {
|
|
265
|
+
display: inline-flex;
|
|
266
|
+
flex: none;
|
|
267
|
+
align-items: center;
|
|
268
|
+
justify-content: center;
|
|
269
|
+
box-sizing: border-box;
|
|
270
|
+
width: calc(20px * var(--density, 1));
|
|
271
|
+
height: calc(20px * var(--density, 1));
|
|
272
|
+
border: 1px solid var(--color-border-default);
|
|
273
|
+
border-radius: var(--radius-pill);
|
|
274
|
+
font-size: var(--font-size-2xs);
|
|
275
|
+
font-weight: var(--font-weight-medium);
|
|
276
|
+
font-variant-numeric: tabular-nums;
|
|
277
|
+
line-height: 1;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.crewlet-stepper__text {
|
|
281
|
+
display: flex;
|
|
282
|
+
flex-direction: column;
|
|
283
|
+
gap: var(--spacing-1);
|
|
284
|
+
min-width: 0;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.crewlet-stepper__title {
|
|
288
|
+
line-height: calc(20px * var(--density, 1));
|
|
289
|
+
overflow-wrap: anywhere;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.crewlet-stepper__row .crewlet-stepper__label {
|
|
293
|
+
overflow: visible;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.crewlet-stepper__description,
|
|
297
|
+
.crewlet-stepper__note {
|
|
298
|
+
color: var(--color-text-tertiary);
|
|
299
|
+
font-size: var(--font-size-xs);
|
|
300
|
+
line-height: var(--font-line-height-snug);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.crewlet-stepper__step--attention .crewlet-stepper__note {
|
|
304
|
+
color: var(--color-text-secondary);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.crewlet-stepper__step--done > .crewlet-stepper__row {
|
|
308
|
+
color: var(--color-text-secondary);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/*
|
|
312
|
+
* THE CURRENT ROW is lifted as the rail's current row is (SidebarNav): the
|
|
313
|
+
* elevated rung inside a one-pixel hairline drawn as an outline, so forced
|
|
314
|
+
* colors keeps it, and it does not move the row by a pixel.
|
|
315
|
+
*/
|
|
316
|
+
.crewlet-stepper__step--current > .crewlet-stepper__row {
|
|
317
|
+
outline: 1px solid var(--color-border-default);
|
|
318
|
+
outline-offset: -1px;
|
|
319
|
+
background: var(--color-surface-elevated);
|
|
320
|
+
color: var(--color-text-primary);
|
|
321
|
+
font-weight: var(--font-weight-medium);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.crewlet-stepper__step--current .crewlet-stepper__description,
|
|
325
|
+
.crewlet-stepper__step--current .crewlet-stepper__note {
|
|
326
|
+
font-weight: var(--font-weight-regular);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
@media (forced-colors: active) {
|
|
330
|
+
.crewlet-stepper .crewlet-stepper__step--current > .crewlet-stepper__chip,
|
|
331
|
+
.crewlet-stepper .crewlet-stepper__step--current .crewlet-stepper__mark {
|
|
332
|
+
forced-color-adjust: none;
|
|
333
|
+
border-color: Highlight;
|
|
334
|
+
background: Highlight;
|
|
335
|
+
color: HighlightText;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.crewlet-stepper .crewlet-stepper__dot {
|
|
339
|
+
background: HighlightText;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.crewlet-stepper .crewlet-stepper__dot.is-pulsing::after {
|
|
343
|
+
animation: none;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.crewlet-stepper__step + .crewlet-stepper__step::before,
|
|
347
|
+
.crewlet-stepper--vertical .crewlet-stepper__step:not(:last-child)::after {
|
|
348
|
+
background: GrayText;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.crewlet-stepper__target:hover {
|
|
352
|
+
outline: 1px solid Highlight;
|
|
353
|
+
outline-offset: var(--size-focus-ring-inset-offset);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FileDrop, FilePickButton } from './chunk-THDGBD6Y.js';
|
|
2
|
-
import { contrastRatio } from './chunk-
|
|
2
|
+
import { contrastRatio } from './chunk-NSSOWQCJ.js';
|
|
3
3
|
import { normalizeHex } from './chunk-UZMNR366.js';
|
|
4
4
|
import { Button } from './chunk-OG6CL3EB.js';
|
|
5
5
|
import { VisuallyHidden } from './chunk-BZFWDIV6.js';
|
|
@@ -9,7 +9,7 @@ import { jsx } from 'react/jsx-runtime';
|
|
|
9
9
|
var engine = null;
|
|
10
10
|
var loading = null;
|
|
11
11
|
function loadEngine() {
|
|
12
|
-
loading ??= import('./Editor-
|
|
12
|
+
loading ??= import('./Editor-MLGK3KOG.js').then(
|
|
13
13
|
(module) => {
|
|
14
14
|
engine = module.Editor;
|
|
15
15
|
return engine;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Stepper } from './chunk-LDGQIDIZ.js';
|
|
2
|
+
import { PageHeader } from './chunk-DKB73KZI.js';
|
|
3
|
+
import { Disclosure } from './chunk-OGXHZHRX.js';
|
|
4
|
+
import { useHeadingLevel, nextHeadingLevel, headingTag, HeadingLevelProvider } from './chunk-PQADV6P6.js';
|
|
5
|
+
import { useMediaMatch } from './chunk-RTPQDNW3.js';
|
|
6
|
+
import { cx } from './chunk-ODEQTPFM.js';
|
|
7
|
+
import './SetupWizard-Y5BTXZX5.css';
|
|
8
|
+
import { useId, useRef, useEffect, createElement } from 'react';
|
|
9
|
+
import { breakpoint } from '@crewlethq/tokens';
|
|
10
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
11
|
+
|
|
12
|
+
var LABELS = {
|
|
13
|
+
progress: (position, total) => `Step ${position} of ${total}`,
|
|
14
|
+
asideToggle: "Preview"
|
|
15
|
+
};
|
|
16
|
+
var PHONE = `(width < ${breakpoint.phone})`;
|
|
17
|
+
function SetupWizard({
|
|
18
|
+
title,
|
|
19
|
+
headingLevel,
|
|
20
|
+
trail,
|
|
21
|
+
status,
|
|
22
|
+
actions,
|
|
23
|
+
steps,
|
|
24
|
+
current,
|
|
25
|
+
reached,
|
|
26
|
+
onSelect,
|
|
27
|
+
stepsLabel,
|
|
28
|
+
heading,
|
|
29
|
+
lead,
|
|
30
|
+
children,
|
|
31
|
+
aside,
|
|
32
|
+
asideLabel,
|
|
33
|
+
footer,
|
|
34
|
+
labels,
|
|
35
|
+
className,
|
|
36
|
+
...rest
|
|
37
|
+
}) {
|
|
38
|
+
const text = { ...LABELS, ...labels };
|
|
39
|
+
const phone = useMediaMatch(PHONE);
|
|
40
|
+
const context = useHeadingLevel();
|
|
41
|
+
const titleLevel = headingLevel ?? context;
|
|
42
|
+
const stepLevel = nextHeadingLevel(titleLevel);
|
|
43
|
+
const contentLevel = nextHeadingLevel(stepLevel);
|
|
44
|
+
const headingId = useId();
|
|
45
|
+
const headingRef = useRef(null);
|
|
46
|
+
const shown = useRef(current);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (shown.current === current) return;
|
|
49
|
+
shown.current = current;
|
|
50
|
+
headingRef.current?.focus();
|
|
51
|
+
}, [current]);
|
|
52
|
+
if (aside !== void 0 && aside !== null && !asideLabel) {
|
|
53
|
+
throw new TypeError("SetupWizard needs an asideLabel with an aside: it names the aside\u2019s landmark");
|
|
54
|
+
}
|
|
55
|
+
const position = steps.findIndex((step2) => step2.id === current);
|
|
56
|
+
const step = steps[position];
|
|
57
|
+
const stepper = /* @__PURE__ */ jsx(
|
|
58
|
+
Stepper,
|
|
59
|
+
{
|
|
60
|
+
label: stepsLabel,
|
|
61
|
+
steps,
|
|
62
|
+
current,
|
|
63
|
+
reached,
|
|
64
|
+
onSelect,
|
|
65
|
+
tone: "brand",
|
|
66
|
+
orientation: phone ? "horizontal" : "vertical"
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
const asideBlock = aside === void 0 || aside === null ? null : /* @__PURE__ */ jsx("aside", { className: "crewlet-setup-wizard__aside", "aria-label": asideLabel, children: /* @__PURE__ */ jsx(HeadingLevelProvider, { level: contentLevel, children: aside }) });
|
|
70
|
+
return /* @__PURE__ */ jsx("div", { ...rest, className: cx("crewlet-setup-wizard", className), children: /* @__PURE__ */ jsxs("div", { className: "crewlet-setup-wizard__frame", children: [
|
|
71
|
+
/* @__PURE__ */ jsxs("div", { className: "crewlet-setup-wizard__head", children: [
|
|
72
|
+
trail ? /* @__PURE__ */ jsx("div", { className: "crewlet-setup-wizard__trail", children: trail }) : null,
|
|
73
|
+
/* @__PURE__ */ jsx(
|
|
74
|
+
PageHeader,
|
|
75
|
+
{
|
|
76
|
+
title,
|
|
77
|
+
headingLevel: titleLevel,
|
|
78
|
+
actions: status || actions ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
79
|
+
status ? /* @__PURE__ */ jsx("span", { className: "crewlet-setup-wizard__status", children: status }) : null,
|
|
80
|
+
actions
|
|
81
|
+
] }) : void 0
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
] }),
|
|
85
|
+
/* @__PURE__ */ jsxs("nav", { className: "crewlet-setup-wizard__rail", "aria-label": stepsLabel, children: [
|
|
86
|
+
phone && step ? /* @__PURE__ */ jsxs("p", { className: "crewlet-setup-wizard__position", children: [
|
|
87
|
+
text.progress(position + 1, steps.length),
|
|
88
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: " \xB7 " }),
|
|
89
|
+
/* @__PURE__ */ jsx("span", { className: "crewlet-setup-wizard__position-step", children: step.label })
|
|
90
|
+
] }) : null,
|
|
91
|
+
stepper
|
|
92
|
+
] }),
|
|
93
|
+
/* @__PURE__ */ jsxs("section", { className: "crewlet-setup-wizard__content", "aria-labelledby": headingId, children: [
|
|
94
|
+
createElement(
|
|
95
|
+
headingTag(stepLevel),
|
|
96
|
+
{ id: headingId, ref: headingRef, tabIndex: -1, className: "crewlet-setup-wizard__heading" },
|
|
97
|
+
heading
|
|
98
|
+
),
|
|
99
|
+
lead ? /* @__PURE__ */ jsx("p", { className: "crewlet-setup-wizard__lead", children: lead }) : null,
|
|
100
|
+
/* @__PURE__ */ jsx("div", { className: "crewlet-setup-wizard__body", children: /* @__PURE__ */ jsx(HeadingLevelProvider, { level: contentLevel, children }) })
|
|
101
|
+
] }),
|
|
102
|
+
asideBlock === null ? null : phone ? /* @__PURE__ */ jsx(Disclosure, { className: "crewlet-setup-wizard__preview", title: text.asideToggle, headingLevel: stepLevel, children: asideBlock }) : asideBlock,
|
|
103
|
+
/* @__PURE__ */ jsx("footer", { className: "crewlet-setup-wizard__footer", children: footer })
|
|
104
|
+
] }) });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export { SetupWizard };
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { StatusDot } from './chunk-WIATRXEJ.js';
|
|
2
|
+
import { VisuallyHidden } from './chunk-BZFWDIV6.js';
|
|
3
|
+
import { cx } from './chunk-ODEQTPFM.js';
|
|
4
|
+
import './Stepper-QEJI5RGX.css';
|
|
5
|
+
import { CheckGlyph, TriangleAlertGlyph } from '@crewlethq/icons/glyphs';
|
|
6
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
function Stepper({
|
|
9
|
+
steps,
|
|
10
|
+
current,
|
|
11
|
+
label,
|
|
12
|
+
tone = "info",
|
|
13
|
+
pulse = false,
|
|
14
|
+
orientation = "horizontal",
|
|
15
|
+
onSelect,
|
|
16
|
+
reached,
|
|
17
|
+
doneLabel = "Done",
|
|
18
|
+
optionalLabel = "Optional",
|
|
19
|
+
attentionLabel = "Needs attention",
|
|
20
|
+
className,
|
|
21
|
+
...rest
|
|
22
|
+
}) {
|
|
23
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24
|
+
for (const step of steps) {
|
|
25
|
+
if (seen.has(step.id)) throw new RangeError(`Stepper step ids must be unique; "${step.id}" is used twice`);
|
|
26
|
+
seen.add(step.id);
|
|
27
|
+
}
|
|
28
|
+
const ids = () => steps.map((step) => `"${step.id}"`).join(", ");
|
|
29
|
+
const at = current === null ? steps.length : steps.findIndex((step) => step.id === current);
|
|
30
|
+
if (at < 0) {
|
|
31
|
+
throw new RangeError(
|
|
32
|
+
`Stepper current must be the id of one of its steps, or null once every step is done; "${current}" is not one of ${ids()}`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
const furthest = reached === void 0 ? at : steps.findIndex((step) => step.id === reached);
|
|
36
|
+
if (furthest < 0) throw new RangeError(`Stepper reached must be the id of one of its steps; "${reached}" is not one of ${ids()}`);
|
|
37
|
+
for (const [index, step] of steps.entries()) {
|
|
38
|
+
if (step.status === "current" && index !== at) {
|
|
39
|
+
throw new RangeError(`Stepper step "${step.id}" says it is current, but current is ${current === null ? "null" : `"${current}"`}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (steps.length === 0) return null;
|
|
43
|
+
const visitedTo = Math.max(at, furthest);
|
|
44
|
+
const vertical = orientation === "vertical";
|
|
45
|
+
const drawnOf = (step, index) => {
|
|
46
|
+
if (index === at) return "current";
|
|
47
|
+
if (step.status !== void 0 && step.status !== "current") return step.status;
|
|
48
|
+
return index < at ? "done" : "upcoming";
|
|
49
|
+
};
|
|
50
|
+
return (
|
|
51
|
+
/*
|
|
52
|
+
* `role="list"` although an `ol` already has one, which is why the lint
|
|
53
|
+
* rule that calls it redundant is silenced here rather than obeyed, as
|
|
54
|
+
* `Legend` and `DiffList` silence it: the stylesheet takes the markers off,
|
|
55
|
+
* and WebKit answers a marker-less list as a group of paragraphs, so a
|
|
56
|
+
* reader would no longer be told how many steps there are before hearing
|
|
57
|
+
* them. The role puts that back.
|
|
58
|
+
*/
|
|
59
|
+
// eslint-disable-next-line jsx-a11y/no-redundant-roles
|
|
60
|
+
/* @__PURE__ */ jsx(
|
|
61
|
+
"ol",
|
|
62
|
+
{
|
|
63
|
+
...rest,
|
|
64
|
+
role: "list",
|
|
65
|
+
className: cx(
|
|
66
|
+
"crewlet-stepper",
|
|
67
|
+
`crewlet-stepper--${tone}`,
|
|
68
|
+
vertical && "crewlet-stepper--vertical",
|
|
69
|
+
onSelect && "crewlet-stepper--selectable",
|
|
70
|
+
className
|
|
71
|
+
),
|
|
72
|
+
"aria-label": label,
|
|
73
|
+
children: steps.map((step, index) => {
|
|
74
|
+
const drawn = drawnOf(step, index);
|
|
75
|
+
const isCurrent = drawn === "current";
|
|
76
|
+
const attention = drawn === "attention" || isCurrent && step.status === "attention";
|
|
77
|
+
const optional = drawn === "optional" || isCurrent && step.status === "optional";
|
|
78
|
+
const note = attention ? attentionLabel : optional ? optionalLabel : null;
|
|
79
|
+
const selectable = onSelect !== void 0 && index <= visitedTo;
|
|
80
|
+
const mark = drawn === "done" ? /* @__PURE__ */ jsx(CheckGlyph, { size: "xs", className: "crewlet-stepper__check" }) : attention ? /* @__PURE__ */ jsx(TriangleAlertGlyph, { size: "xs", className: "crewlet-stepper__warning" }) : isCurrent ? /* @__PURE__ */ jsx(StatusDot, { tone, pulse, className: "crewlet-stepper__dot" }) : vertical ? /* @__PURE__ */ jsx("span", { className: "crewlet-stepper__number", "aria-hidden": "true", children: index + 1 }) : null;
|
|
81
|
+
const words = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
82
|
+
drawn === "done" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
83
|
+
/* @__PURE__ */ jsx(VisuallyHidden, { children: doneLabel }),
|
|
84
|
+
" "
|
|
85
|
+
] }) : null,
|
|
86
|
+
/* @__PURE__ */ jsx("span", { className: "crewlet-stepper__label", children: step.label }),
|
|
87
|
+
note !== null && !vertical ? /* @__PURE__ */ jsx(VisuallyHidden, { children: `, ${note}` }) : null
|
|
88
|
+
] });
|
|
89
|
+
const content = vertical ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
90
|
+
/* @__PURE__ */ jsx("span", { className: "crewlet-stepper__mark", children: mark }),
|
|
91
|
+
/* @__PURE__ */ jsxs("span", { className: "crewlet-stepper__text", children: [
|
|
92
|
+
/* @__PURE__ */ jsx("span", { className: "crewlet-stepper__title", children: words }),
|
|
93
|
+
step.description ? /* @__PURE__ */ jsx("span", { className: "crewlet-stepper__description", children: step.description }) : null,
|
|
94
|
+
note !== null ? /* @__PURE__ */ jsx("span", { className: "crewlet-stepper__note", children: note }) : null
|
|
95
|
+
] })
|
|
96
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
97
|
+
mark,
|
|
98
|
+
words
|
|
99
|
+
] });
|
|
100
|
+
const surface = vertical ? "crewlet-stepper__row" : "crewlet-stepper__chip";
|
|
101
|
+
return /* @__PURE__ */ jsx(
|
|
102
|
+
"li",
|
|
103
|
+
{
|
|
104
|
+
className: cx(
|
|
105
|
+
"crewlet-stepper__step",
|
|
106
|
+
`crewlet-stepper__step--${drawn}`,
|
|
107
|
+
attention && drawn !== "attention" && "crewlet-stepper__step--attention"
|
|
108
|
+
),
|
|
109
|
+
"aria-current": isCurrent && !selectable ? "step" : void 0,
|
|
110
|
+
children: selectable ? /* @__PURE__ */ jsx(
|
|
111
|
+
"button",
|
|
112
|
+
{
|
|
113
|
+
type: "button",
|
|
114
|
+
className: cx(surface, "crewlet-stepper__target"),
|
|
115
|
+
"aria-current": isCurrent ? "step" : void 0,
|
|
116
|
+
onClick: () => onSelect(step.id),
|
|
117
|
+
children: content
|
|
118
|
+
}
|
|
119
|
+
) : /* @__PURE__ */ jsx("span", { className: surface, children: content })
|
|
120
|
+
},
|
|
121
|
+
step.id
|
|
122
|
+
);
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export { Stepper };
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
+
import { useNow, formatRelative } from './chunk-3CZ7XAVY.js';
|
|
1
2
|
import { VisuallyHidden } from './chunk-BZFWDIV6.js';
|
|
2
3
|
import { cx } from './chunk-ODEQTPFM.js';
|
|
3
4
|
import './EditingStatus-NNA2CKIL.css';
|
|
4
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
5
6
|
|
|
6
7
|
var LABELS = {
|
|
7
8
|
editing: "Editing",
|
|
8
9
|
unsaved: "Unsaved changes",
|
|
9
|
-
saving: "Saving"
|
|
10
|
+
saving: "Saving",
|
|
11
|
+
saved: (ago) => `Saved ${ago}`,
|
|
12
|
+
savedSpoken: "Saved"
|
|
10
13
|
};
|
|
11
|
-
function EditingStatus({ dirty, saving = false, labels, className }) {
|
|
14
|
+
function EditingStatus({ dirty, saving = false, savedAt, labels, className }) {
|
|
12
15
|
const text = { ...LABELS, ...labels };
|
|
13
|
-
const state = saving ? text.saving : dirty ? text.unsaved : null;
|
|
16
|
+
const state = saving ? text.saving : dirty ? text.unsaved : savedAt !== void 0 && savedAt !== null ? /* @__PURE__ */ jsx(SavedAgo, { at: savedAt, labels: text }) : null;
|
|
14
17
|
return /* @__PURE__ */ jsxs(
|
|
15
18
|
"span",
|
|
16
19
|
{
|
|
@@ -26,5 +29,14 @@ function EditingStatus({ dirty, saving = false, labels, className }) {
|
|
|
26
29
|
}
|
|
27
30
|
);
|
|
28
31
|
}
|
|
32
|
+
function SavedAgo({ at, labels }) {
|
|
33
|
+
const now = useNow();
|
|
34
|
+
const ago = formatRelative(at, now);
|
|
35
|
+
if (ago === null) return null;
|
|
36
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
37
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: labels.saved(ago) }),
|
|
38
|
+
/* @__PURE__ */ jsx(VisuallyHidden, { children: labels.savedSpoken })
|
|
39
|
+
] });
|
|
40
|
+
}
|
|
29
41
|
|
|
30
42
|
export { EditingStatus };
|