@synopackageland/ui 0.25.0 → 0.25.2
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/CHANGELOG.md +42 -0
- package/README.md +1 -1
- package/dist/cli.js +0 -0
- package/dist/components/composite/wizard/Wizard.vue.d.ts +9 -4
- package/dist/components/composite/wizard/WizardDialog.vue.d.ts +5 -2
- package/dist/components/composite/wizard/WizardStep.vue.d.ts +8 -2
- package/dist/components/composite/wizard/types.d.ts +80 -12
- package/dist/components/composite/wizard/wizardContext.d.ts +2 -0
- package/dist/components/primitives/dialog/Dialog.vue.d.ts +3 -0
- package/dist/grid.js +9 -0
- package/dist/i18n/text.d.ts +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +12288 -12152
- package/dist/json-preview/preview.js +2806 -2793
- package/dist/reka-runtime.json +1 -1
- package/dist/theme.css +1 -1
- package/dist/themes/dsm7-embedded-theme.css +1 -1
- package/dist/themes/dsm7-embedded.css +1 -1
- package/dist/themes/dsm7.css +1 -1
- package/docs/layout-rules.md +1 -1
- package/docs/mobile-native.md +18 -3
- package/docs/release-checks.md +1 -1
- package/docs/wizard.md +241 -10
- package/package.json +3 -89
package/docs/layout-rules.md
CHANGED
|
@@ -30,7 +30,7 @@ These rules keep pages built from `@synopackageland/ui` consistent. The componen
|
|
|
30
30
|
- In a dialog or a side `Sheet`, keep the default `direction="auto"`: as in DSM, the labels sit in a left column whenever the form is at least 400px wide (a 420–480px sheet) and stack above their controls only below that (a phone). Size the column with `Form label-width` (about 120px for short labels); a long label wraps inside it and stays level with its control's first line, whether that is a field, a select, a switch, a toggle group or a segment control.
|
|
31
31
|
- Size a field by its content: number fields are 128px; text fields use `fieldWidth`; in a narrow row that stacks its label (a phone), `md` and `lg` fill the column. Put two short fields side by side rather than stretching one across the page.
|
|
32
32
|
- A checkbox's text sits on the same line as its box. Do not apply `display: grid` to `label` in App CSS; `Checkbox` renders its own label.
|
|
33
|
-
- For 2–4 mutually exclusive settings, use `RadioGroup` (vertical by default, `orientation="horizontal"` when labels fit). In a wizard step, `variant="card"` draws the options as choice cards with an optional `icon` each. A checkbox enables an independent option; a switch turns a feature on or off immediately. Keep the circular radio with a dot, square checkbox with a tick, and pill switch with a moving thumb.
|
|
33
|
+
- For 2–4 mutually exclusive settings, use `RadioGroup` (vertical by default, `orientation="horizontal"` when labels fit). In a wizard step, `variant="card"` draws the options as choice cards with an optional `icon` each. The 2–4 count is a guide, not a limit: the cards wrap, but five or more short options (or longer ones, even three) read better as a vertical radio list. A checkbox enables an independent option; a switch turns a feature on or off immediately. Keep the circular radio with a dot, square checkbox with a tick, and pill switch with a moving thumb.
|
|
34
34
|
- Reserve `SegmentControl` for toolbar view switching: its selected item is a primary-filled capsule. `variant="sheet"` and `SectionTabs` are navigation tabs. Do not use either as a form value selector.
|
|
35
35
|
- Choices side by side are 34px apart (`--ui-choice-inline-gap`, DSM's Task Scheduler) and share one top whether checked or not. For a set of checkboxes use `CheckboxGroup` (`orientation="horizontal"` for a row, `v-model` an array of values), as `RadioGroup` for one choice; Checkboxes written one after another get the same gap. Do not add margins between them.
|
|
36
36
|
- A password field has a show/hide toggle; `Input type="password"` adds it.
|
package/docs/mobile-native.md
CHANGED
|
@@ -42,9 +42,9 @@ the commit footer behave as before.
|
|
|
42
42
|
| `--ui-radius-md` / `-lg` (and the 0.23.9 roles on them) | 4 / 6 | 12 / 14: md rounds the control, container and popover roles (fields, tool buttons, inset blocks, menus), lg the panel and nav roles |
|
|
43
43
|
| `--ui-radius-dialog` / `-window` / `-sheet` | 8 / 8 / 12 | 24 |
|
|
44
44
|
| Primary, danger and secondary buttons | capsule (action) or 4px (tool) | capsule; a primary's fill is accent step 10 (`#0075db`, 4.6:1 with white) |
|
|
45
|
-
| Segmented control | accent pill on a framed track, equal columns |
|
|
45
|
+
| Segmented control | accent pill on a framed track, equal columns | a pill in the primary button's fill (accent step 10, `#0075db`, with the accent's contrast colour, white: 4.6:1, light and dark) on a frameless mist track; 6px sides; up to three segments stay equal columns but one whose label is wider than its share grows to it, so a label is not cut (four ellipsize as before) (`check:phone-layout` checks it) |
|
|
46
46
|
| Phone bar | white with a hairline | the page's colour, no hairline, 17px bold title, round pressed tint |
|
|
47
|
-
| Tab bar | full-width white row with a top rule | a floating white pill inset 16px, 8px above the bottom (`--ui-tab-bar-float`); the current tab is
|
|
47
|
+
| Tab bar | full-width white row with a top rule | a floating white pill inset 16px, 8px above the bottom (`--ui-tab-bar-float`); the current tab's icon is white on a round pad in the primary's fill (accent step 10); a tab with a label and no icon takes accent step 10 on its label over the mist pad |
|
|
48
48
|
| `--ui-text-muted` | 40% grey (2:1) | 80% (4.8:1 on the mist; dark 62%) |
|
|
49
49
|
| Separators | `--ui-border-subtle` | `--ui-phone-separator` |
|
|
50
50
|
|
|
@@ -190,6 +190,17 @@ component rule overrides them.
|
|
|
190
190
|
IconButton draw their own pressed state (`data-variant`), so they're
|
|
191
191
|
skipped. A component that sets its own shadow or `active:` style also keeps
|
|
192
192
|
its own.
|
|
193
|
+
- Press feedback on a phone (`phone.css`, a coarse pointer in the mobile
|
|
194
|
+
presentation): a Button or an IconButton takes the `--ui-state-pressed`
|
|
195
|
+
layer over its own active colour and settles to `scale(0.98)` in 100ms
|
|
196
|
+
(`cubic-bezier(0.24, 1, 0.4, 1)`), springing back on release. The scale is
|
|
197
|
+
a transform, so nothing around the control moves. A ghost Button or
|
|
198
|
+
IconButton already fills with the layer and a link button fades, so neither
|
|
199
|
+
takes a second layer. A list row, a tab and a menu item keep the layer
|
|
200
|
+
alone: a row that shrank would pull away from its hairlines. A disabled
|
|
201
|
+
control takes nothing. Reduced motion and `data-ui-motion="off"` keep the
|
|
202
|
+
layer and drop the scale. A DSM window, a desktop page and a touch tablet
|
|
203
|
+
are unchanged.
|
|
193
204
|
- Hover: every hand-written `:hover` rule in the theme sits inside
|
|
194
205
|
`@media (hover: hover)`, so a tap leaves no stuck hover look. Tailwind's
|
|
195
206
|
`hover:` utilities already do this.
|
|
@@ -468,7 +479,11 @@ contain`, a pinned footer and safe-area padding. The top corners use
|
|
|
468
479
|
overrides that.
|
|
469
480
|
|
|
470
481
|
`Dialog` on a phone (an app 640px or narrower) renders as a `BottomSheet`:
|
|
471
|
-
`auto`, or `full` for `fullscreen` and for a form with more than 3 fields.
|
|
482
|
+
`auto`, or `full` for `fullscreen` and for a form with more than 3 fields.
|
|
483
|
+
`presentation="modal"` keeps a centred window instead, with the phone's
|
|
484
|
+
dialog corner and dim scrim, as tall as its content up to its `height` inside
|
|
485
|
+
the safe area (only a longer body scrolls); `WizardDialog` uses it by default, as a DSM
|
|
486
|
+
wizard opens, and `presentation="adaptive"` gives it the bottom sheet. The
|
|
472
487
|
CSS-only dialog-as-sheet in `adaptive.css` is gone. `Sheet side="bottom"` is
|
|
473
488
|
a `BottomSheet` everywhere; without `size` it fits its content. Left and right
|
|
474
489
|
sheets are unchanged, and their footer keeps clear of the home indicator.
|
package/docs/release-checks.md
CHANGED
|
@@ -8,7 +8,7 @@ checks through the optional build-isolation wrapper.
|
|
|
8
8
|
|
|
9
9
|
| Entry point | Default | Explicit full |
|
|
10
10
|
| --- | --- | --- |
|
|
11
|
-
| `npm run gates`, `scripts/gates.mjs` | Internal-content checks, build, lint, typecheck, unit tests, notices, isolated pack and tarball content audit | Adds Chromium and WebKit installation, responsive/adaptive/date-picker/JSON/host/switch/layout/InfoTip/motion/short-viewport/phone-layout/phone-bars (Chromium and WebKit)/focus-restore browser checks, Histoire build, exact-tarball registry consumer smoke |
|
|
11
|
+
| `npm run gates`, `scripts/gates.mjs` | Internal-content checks, build, lint, typecheck, unit tests, notices, isolated pack and tarball content audit | Adds Chromium and WebKit installation, responsive/adaptive/date-picker/JSON/host/switch/layout/InfoTip/motion/short-viewport/phone-layout/phone-bars (Chromium and WebKit)/press/focus-restore browser checks, Histoire build, exact-tarball registry consumer smoke |
|
|
12
12
|
| `pnpm check`, `check:fast` | Basic, with optional Docker host build isolation | `SPL_FULL_GATE=1` selects the same full plan |
|
|
13
13
|
| `fast-ci.yml`, composite verify action | Basic | No automatic extended checks |
|
|
14
14
|
| Disabled CI/release workflow templates | Basic, version/tag/ancestry checks, stage, pack and actual archive audit | No implicit browser or registry consumer smoke |
|
package/docs/wizard.md
CHANGED
|
@@ -57,15 +57,20 @@ const open = ref(false)
|
|
|
57
57
|
does not stack its children with a gap. Several blocks go in one `Form`
|
|
58
58
|
(fields one field gap apart) or a `<Space direction="column">`; a lone
|
|
59
59
|
Callout, paragraph or card grid needs no wrapper.
|
|
60
|
-
- **Choices.**
|
|
60
|
+
- **Choices.** Options that each need an icon and a line are choice cards:
|
|
61
61
|
`<RadioGroup variant="card" orientation="horizontal" :options>`. Each card
|
|
62
62
|
shows the option's `icon` in a tile, its label and one line, and keeps the
|
|
63
63
|
radio dot; the whole card picks it and the chosen card takes the primary
|
|
64
64
|
border and a soft fill. The cards share the row (180px at least) and wrap
|
|
65
|
-
in a narrow window.
|
|
65
|
+
in a narrow window. Cards usually suit 2–4 options; that is a guide, not a
|
|
66
|
+
limit, and nothing cuts a fifth. Five short options (symptoms, say) read
|
|
67
|
+
better as a vertical `RadioGroup` list, and long options do too even when
|
|
68
|
+
there are three. Pick by how the options read, not by their count.
|
|
66
69
|
- **Types.** `welcome` offers only Start; `done` drops Back and Cancel, fills
|
|
67
|
-
the progress and turns the main action into Finish
|
|
68
|
-
`
|
|
70
|
+
the progress and turns the main action into Finish; `result` ends the flow
|
|
71
|
+
with an outcome the app sets ([Result](#result)); `progress` shows work the
|
|
72
|
+
app runs ([Progress](#progress)). The last step without `next-step-key`
|
|
73
|
+
turns the main action into Apply.
|
|
69
74
|
- **Main action.** `next-label`, `next-disabled` and `:show-next="false"` (a step
|
|
70
75
|
that moves on by itself or through its own choices, such as a row of cards)
|
|
71
76
|
change it for one step. `:show-back="false"` hides Back.
|
|
@@ -98,9 +103,12 @@ const open = ref(false)
|
|
|
98
103
|
The `#footer` slot replaces the footer's buttons and receives `nextStep`,
|
|
99
104
|
`previousStep`, `finish`, `cancel`, `canGoPrevious`, `isLastStep`, `busy` and
|
|
100
105
|
`stepKey`. Events: `next-step`, `previous-step` (`{ from, to }`), `finished`,
|
|
101
|
-
`cancel
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
`cancel` and `navigation-error` ([Guards](#guards)). The step list does not
|
|
107
|
+
navigate, so a guard cannot be skipped.
|
|
108
|
+
|
|
109
|
+
`finished` means the user took Finish (or Apply); it does not mean the work
|
|
110
|
+
succeeded. Read the outcome from your own state, and do not run the work again
|
|
111
|
+
in a `finished` callback.
|
|
104
112
|
|
|
105
113
|
A wizard fills the height its container gives it (`WizardDialog` and
|
|
106
114
|
`<Dialog flush height="…">` give it one); on a page, set a height on its
|
|
@@ -117,6 +125,220 @@ there is no Back and nothing shifts. A welcome step offers Start alone; a done
|
|
|
117
125
|
step offers Finish (and its own `#actions`). There is no Close in the footer:
|
|
118
126
|
a `WizardDialog` closes from its header.
|
|
119
127
|
|
|
128
|
+
## Result
|
|
129
|
+
|
|
130
|
+
A `type="result"` step ends the flow with an outcome: `result-tone` is
|
|
131
|
+
`success`, `warning`, `danger` or `neutral` (the default, which claims neither
|
|
132
|
+
success nor failure). A partial import, a failed connection test or a scan
|
|
133
|
+
that finished with warnings is a result, never a `done` step: `done` always
|
|
134
|
+
shows the success mark and its burst.
|
|
135
|
+
|
|
136
|
+
```vue
|
|
137
|
+
<WizardStep
|
|
138
|
+
step-key="result"
|
|
139
|
+
type="result"
|
|
140
|
+
:result-tone="tone"
|
|
141
|
+
headline="Some items were not imported"
|
|
142
|
+
description="The imported items are kept; check why the others failed and try again"
|
|
143
|
+
>
|
|
144
|
+
<FindingList :items="findings" />
|
|
145
|
+
<template #actions>
|
|
146
|
+
<Button variant="ghost" kind="tool" @click="startAgain">Check again</Button>
|
|
147
|
+
</template>
|
|
148
|
+
</WizardStep>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
- The step shows its tone's mark (a tick, a warning sign, a cross or an info
|
|
152
|
+
mark) on its colour, read as "Succeeded", "Warning", "Failed" or "Done". It
|
|
153
|
+
pops in once, with no ring and no burst for any tone.
|
|
154
|
+
- Finish is the one main action, blue for every tone: a red mark does not
|
|
155
|
+
make the button red. Back and Cancel are gone; the header's close button and
|
|
156
|
+
the step's `#actions` stay. `next-label` renames Finish.
|
|
157
|
+
- The guide marks the step with the outcome: a warning or a failure is not
|
|
158
|
+
drawn or read as completed. The steps before it read as completed: that says
|
|
159
|
+
the user passed them, not that the backend did each part.
|
|
160
|
+
- A result step never moves on: `next-step-key` is ignored (with a warning
|
|
161
|
+
in development).
|
|
162
|
+
- Map the outcome in the app: a rollback that worked can be `neutral`, a
|
|
163
|
+
partial or unknown rollback `warning`, a failed one `danger`.
|
|
164
|
+
|
|
165
|
+
## Progress
|
|
166
|
+
|
|
167
|
+
A `type="progress"` step shows work the app runs: a progress bar and a status
|
|
168
|
+
line. The wizard only draws them; the app starts, watches and ends the work,
|
|
169
|
+
and moves the wizard on (`v-model:active-step-key`) when the backend says the
|
|
170
|
+
work ended.
|
|
171
|
+
|
|
172
|
+
```vue
|
|
173
|
+
<WizardStep
|
|
174
|
+
step-key="scanning"
|
|
175
|
+
type="progress"
|
|
176
|
+
headline="Checking"
|
|
177
|
+
:progress="total > 0
|
|
178
|
+
? { mode: 'determinate', label: 'Items checked', value: checked, max: total, status: statusText }
|
|
179
|
+
: { mode: 'indeterminate', label: 'Getting status', status: statusText }"
|
|
180
|
+
stop-label="Stop watching"
|
|
181
|
+
@stop="stopWatching"
|
|
182
|
+
/>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
- `progress` is `{ mode: 'indeterminate', label, status? }` while the total is
|
|
186
|
+
unknown, or `{ mode: 'determinate', label, value, max, valueLabel?, status? }`.
|
|
187
|
+
Without it the bar is indeterminate and named by the headline. `status` is
|
|
188
|
+
read politely when it changes.
|
|
189
|
+
- The bar is on its own tinted track (never grey) and has no stripes or
|
|
190
|
+
pattern; reduced motion stills it.
|
|
191
|
+
- Next and Back are hidden unless the step sets `:show-next="true"` or
|
|
192
|
+
`:show-back="true"` (for work that only reads). Back never undoes work that
|
|
193
|
+
changed data.
|
|
194
|
+
- `stop-label` adds a secondary button that emits `stop`; `stop-disabled`
|
|
195
|
+
disables it while the app waits for the backend. "Stop watching" only stops
|
|
196
|
+
the app looking; "Cancel task" is for a backend that can cancel, and the app
|
|
197
|
+
shows a cancelled result only once the backend confirms it.
|
|
198
|
+
- There is no timeout, retry count or auto-advance: time passing is not a
|
|
199
|
+
failure. `lazy` only mounts the step's content; it starts and stops nothing.
|
|
200
|
+
|
|
201
|
+
## Guards
|
|
202
|
+
|
|
203
|
+
`pre-enter-next-step` and `pre-enter-previous-step` run before a move. A guard
|
|
204
|
+
gets `{ from, direction, signal }` and returns:
|
|
205
|
+
|
|
206
|
+
| Return | Effect |
|
|
207
|
+
| --- | --- |
|
|
208
|
+
| nothing | the move goes on as the step says |
|
|
209
|
+
| `false` | the step stays |
|
|
210
|
+
| `{ goTo: 'key' }` | the move goes to that step instead, this time only |
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
async function checkAgain({ signal }: WizardGuardContext): Promise<WizardGuardResult> {
|
|
214
|
+
const status = await api.status({ signal })
|
|
215
|
+
if (status.needsAccess) return { goTo: 'access' }
|
|
216
|
+
return status.ready ? undefined : false
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
- The main action shows a spinner while a guard runs; presses meanwhile are
|
|
221
|
+
ignored, so the guard runs once.
|
|
222
|
+
- A `goTo` is checked once the app's update has rendered, so a step the
|
|
223
|
+
guard mounts with `v-if` can be its target. It moves through the usual
|
|
224
|
+
`next-step` or `previous-step` event, `update:active-step-key` and focus.
|
|
225
|
+
The target's own guard does not run.
|
|
226
|
+
- A `goTo` that names no registered step (`unknown-step`) or the step itself
|
|
227
|
+
(`same-step`) keeps the step and emits `navigation-error`. A guard that
|
|
228
|
+
throws or rejects keeps the step and emits `navigation-error` with
|
|
229
|
+
`guard-rejected`, without the error's text: show your own message.
|
|
230
|
+
- `signal` aborts when the move no longer matters: the wizard left the step,
|
|
231
|
+
the app reset `active-step-key`, the `WizardDialog` closed (not while "Keep
|
|
232
|
+
editing" is asked) or the wizard unmounted. The spinner stops at once, and
|
|
233
|
+
a reply that comes later moves nothing and emits nothing. Pass `signal` to
|
|
234
|
+
your requests so they stop too.
|
|
235
|
+
- A step key is not a permission. The target step and the backend still check
|
|
236
|
+
what the user may do.
|
|
237
|
+
|
|
238
|
+
A guard written as `() => false | void` still works as before.
|
|
239
|
+
|
|
240
|
+
## Recipes
|
|
241
|
+
|
|
242
|
+
These use the API above; none needs anything new.
|
|
243
|
+
|
|
244
|
+
### Branches the backend decides
|
|
245
|
+
|
|
246
|
+
Steps need not all be known up front. A guard loads the branch, and a
|
|
247
|
+
`next-step-key` function reads it; the wizard reads `next-step-key` after the
|
|
248
|
+
guard. A step only some branches need can be mounted with `v-if`; it takes its
|
|
249
|
+
place in document order.
|
|
250
|
+
|
|
251
|
+
```vue
|
|
252
|
+
<WizardStep step-key="inspect" headline="Check the target"
|
|
253
|
+
:pre-enter-next-step="loadBranch"
|
|
254
|
+
:next-step-key="() => branch.needsAccess ? 'access' : 'preview'" />
|
|
255
|
+
<WizardStep v-if="branch.needsAccess" step-key="access" headline="Allow access" next-step-key="preview" />
|
|
256
|
+
<WizardStep step-key="preview" headline="Confirm" />
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Map each backend kind to a step key in the app, and treat an unknown kind as
|
|
260
|
+
"cannot continue": never use a backend string as a key. The progress counts
|
|
261
|
+
the mounted steps, so mount the path the user takes, not every branch; when
|
|
262
|
+
the total is unknown, `progress-text` can say so.
|
|
263
|
+
|
|
264
|
+
### Two equal ways on
|
|
265
|
+
|
|
266
|
+
Keep or roll back, replace or keep: put the choices in one `RadioGroup` (cards
|
|
267
|
+
when they need an icon and a line) and let one main action confirm the chosen
|
|
268
|
+
one. Its `next-label` follows the choice; a choice never acts by itself, so
|
|
269
|
+
moving through the options with the arrow keys does nothing else. The footer
|
|
270
|
+
keeps one blue button. A choice that destroys data asks in a confirmation
|
|
271
|
+
dialog first, whose confirm button is the only red one.
|
|
272
|
+
|
|
273
|
+
```vue
|
|
274
|
+
<WizardStep step-key="decision" headline="Choose what to do"
|
|
275
|
+
:show-back="false" :next-disabled="!decision"
|
|
276
|
+
:next-label="decision === 'rollback' ? 'Roll back' : 'Keep changes'"
|
|
277
|
+
:next-step-key="() => decision === 'rollback' ? 'working' : 'result'"
|
|
278
|
+
:pre-enter-next-step="confirmDecision">
|
|
279
|
+
<RadioGroup v-model="decision" variant="card" orientation="horizontal" :options="allowedDecisions" />
|
|
280
|
+
</WizardStep>
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Offer only the choices the backend allows: an interrupted run has no "keep".
|
|
284
|
+
|
|
285
|
+
### Handing over to another wizard
|
|
286
|
+
|
|
287
|
+
The parent opens one wizard at a time. To hand over (a diagnosis opening a
|
|
288
|
+
fix): check what the source passes, stop the source's pending work, close the
|
|
289
|
+
source, then set the target's props and open it. Do not rely on two
|
|
290
|
+
`v-model:open` flips in one tick; mount the two exclusively (`v-if` on one
|
|
291
|
+
state) so only one dialog holds the focus. A handover is not the source's
|
|
292
|
+
Finish and not a rollback, and secrets do not travel in its props.
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
type WizardRequest = { kind: 'diagnose' } | { kind: 'setup'; intent: 'send' | 'receive' } | undefined
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Watching work
|
|
299
|
+
|
|
300
|
+
For a [progress](#progress) step the app watches the backend:
|
|
301
|
+
|
|
302
|
+
1. Watch only while the wizard is open on that step. Leaving, closing,
|
|
303
|
+
unmounting or reopening ends the round: clear its timer and abort its
|
|
304
|
+
request.
|
|
305
|
+
2. One request at a time: schedule the next when the last ends, not with
|
|
306
|
+
`setInterval`. Follow the backend's retry-after; on errors back off slowly
|
|
307
|
+
(from about a second to 15 or 30 seconds). These are a schedule, not a
|
|
308
|
+
deadline.
|
|
309
|
+
3. After every `await`, check that the round, the open state and the step are
|
|
310
|
+
still the same before writing anything.
|
|
311
|
+
4. A network error shows "Status not up to date" with a way to check again and
|
|
312
|
+
keeps the last progress. Only the backend's own failed or interrupted state
|
|
313
|
+
leads to a failure result.
|
|
314
|
+
5. Reopening reads the run from the backend and resumes; it does not apply
|
|
315
|
+
again.
|
|
316
|
+
|
|
317
|
+
### Opening another DSM app
|
|
318
|
+
|
|
319
|
+
When a step needs the user to finish something in another app, open it with
|
|
320
|
+
the platform SDK's app launcher (it knows the app's verified ID; a package
|
|
321
|
+
name is not an app ID), and always show the path in words as well, for a host
|
|
322
|
+
that cannot open it. A secondary button opens it; the step's main action is
|
|
323
|
+
"Check again", whose guard reads the state again. Coming back to the wizard
|
|
324
|
+
only triggers a check: it is never a success by itself.
|
|
325
|
+
|
|
326
|
+
```vue
|
|
327
|
+
<WizardStep step-key="external" headline="Finish in the other app"
|
|
328
|
+
next-label="Check again" :pre-enter-next-step="checkAgain">
|
|
329
|
+
<p>When you are done there, come back and check again</p>
|
|
330
|
+
<Button variant="secondary" kind="tool" @click="openApp">Open the app</Button>
|
|
331
|
+
<p v-if="launchUnavailable">Open it from the DSM main menu</p>
|
|
332
|
+
</WizardStep>
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Not provided
|
|
336
|
+
|
|
337
|
+
The wizard has no backend step schema or runner, no timeout or retry settings
|
|
338
|
+
for work, no two-primary footer, no card count option, no cross-wizard
|
|
339
|
+
navigation and no launcher of its own. These stay with the app (or the
|
|
340
|
+
platform SDK) on purpose.
|
|
341
|
+
|
|
120
342
|
## Modal
|
|
121
343
|
|
|
122
344
|
`WizardDialog` opens like a DSM modal window, in the app (app-local, see
|
|
@@ -131,8 +353,16 @@ a `WizardDialog` closes from its header.
|
|
|
131
353
|
step title) shows a ring only after a key, never after a click.
|
|
132
354
|
|
|
133
355
|
These are `Dialog` props (`scrim="light"`, `:close-on-outside="false"`,
|
|
134
|
-
`:header-divider="false"`), so a dialog of your own
|
|
135
|
-
open the same way.
|
|
356
|
+
`:header-divider="false"`, `presentation="modal"`), so a dialog of your own
|
|
357
|
+
around a `Wizard` can open the same way.
|
|
358
|
+
|
|
359
|
+
On a phone `WizardDialog` stays a centred window, as a DSM wizard does: the
|
|
360
|
+
phone's dialog corner, the phone's dim scrim, the same fixed header and
|
|
361
|
+
footer. It is as tall as its step, up to the wizard's height inside the
|
|
362
|
+
phone's safe area; only a longer step scrolls, between the header and the
|
|
363
|
+
footer. `presentation="adaptive"` keeps the bottom sheet instead, for an app
|
|
364
|
+
that wants its earlier phone look; other dialogs are bottom sheets on a phone
|
|
365
|
+
as before. The "Discard changes?" question is always a centred window.
|
|
136
366
|
|
|
137
367
|
## Leaving with input
|
|
138
368
|
|
|
@@ -164,6 +394,7 @@ in every built-in locale.
|
|
|
164
394
|
- **Motion.** A step fades in from 10px on the side it comes from and its
|
|
165
395
|
title fades in; the progress segments ease to their new state; the done mark
|
|
166
396
|
pops with a ring and a short burst. Reduced motion turns all of it off.
|
|
167
|
-
- **Size.** In a phone-width window the dialog
|
|
397
|
+
- **Size.** In a phone-width window the dialog stays a centred window with a
|
|
398
|
+
margin around it; `presentation="adaptive"` makes it a bottom sheet.
|
|
168
399
|
- **Theme.** Colours are the theme's roles, so the wizard follows the accent
|
|
169
400
|
and the dark theme.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synopackageland/ui",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.2",
|
|
4
4
|
"description": "Shared Vue 3 UI component library with a DSM 7 inspired application theme",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -79,34 +79,6 @@
|
|
|
79
79
|
"@tiptap/vue-3": "3.32.1",
|
|
80
80
|
"zod": "^4.6.5"
|
|
81
81
|
},
|
|
82
|
-
"devDependencies": {
|
|
83
|
-
"@eslint/js": "10.0.1",
|
|
84
|
-
"@histoire/plugin-vue": "1.0.0-beta.1",
|
|
85
|
-
"@tailwindcss/vite": "4.3.3",
|
|
86
|
-
"@tanstack/vue-virtual": "3.13.39",
|
|
87
|
-
"@vitejs/plugin-vue": "6.0.9",
|
|
88
|
-
"@vue/test-utils": "2.5.1",
|
|
89
|
-
"@vueuse/core": "14.4.0",
|
|
90
|
-
"eslint": "10.11.0",
|
|
91
|
-
"eslint-plugin-vue": "10.11.1",
|
|
92
|
-
"flexsearch": "0.8.212",
|
|
93
|
-
"globals": "17.12.0",
|
|
94
|
-
"histoire": "1.0.0-beta.1",
|
|
95
|
-
"jsdom": "30.1.1",
|
|
96
|
-
"playwright": "1.58.2",
|
|
97
|
-
"reka-ui": "2.11.0",
|
|
98
|
-
"tailwindcss": "4.3.3",
|
|
99
|
-
"typescript": "5.9.3",
|
|
100
|
-
"typescript-eslint": "8.71.0",
|
|
101
|
-
"vite": "7.3.6",
|
|
102
|
-
"vite-plugin-dts": "5.1.1",
|
|
103
|
-
"vitest": "5.0.2",
|
|
104
|
-
"vscode-oniguruma": "2.0.1",
|
|
105
|
-
"vscode-textmate": "9.3.2",
|
|
106
|
-
"vue": "3.5.43",
|
|
107
|
-
"vue-eslint-parser": "10.4.1",
|
|
108
|
-
"vue-tsc": "3.3.11"
|
|
109
|
-
},
|
|
110
82
|
"engines": {
|
|
111
83
|
"node": ">=20"
|
|
112
84
|
},
|
|
@@ -117,63 +89,5 @@
|
|
|
117
89
|
"reka-ui": "2.11.0"
|
|
118
90
|
}
|
|
119
91
|
},
|
|
120
|
-
"homepage": "https://www.varvis.me/docs/"
|
|
121
|
-
|
|
122
|
-
"check:docs": "node --test scripts/check-doc-versions.test.mjs && node scripts/check-doc-versions.mjs",
|
|
123
|
-
"dev": "vite build --watch",
|
|
124
|
-
"build": "vite build && vite build --mode json && vite build --mode rich-text && vite build --mode grid && vite build --mode cli && vite build --mode preview && vite build --mode embedded && node scripts/postbuild.mjs",
|
|
125
|
-
"lint": "eslint .",
|
|
126
|
-
"typecheck": "vue-tsc --noEmit -p tsconfig.json",
|
|
127
|
-
"test": "vitest run",
|
|
128
|
-
"test:watch": "vitest",
|
|
129
|
-
"test:changed": "echo \"test:changed skips the tests the changes since ${SYNOPKGLAND_GATE_BASE:-origin/master} do not reach (vitest --changed); pnpm check runs them all\" && vitest run --changed ${SYNOPKGLAND_GATE_BASE:-origin/master}",
|
|
130
|
-
"story": "histoire dev",
|
|
131
|
-
"story:build": "histoire build",
|
|
132
|
-
"check:host": "node scripts/dsm-host-shots.mjs",
|
|
133
|
-
"check:switch": "node scripts/check-switch.mjs",
|
|
134
|
-
"check:tabs": "node scripts/check-tabs.mjs",
|
|
135
|
-
"check:mobile": "node scripts/check-mobile-overflow.mjs",
|
|
136
|
-
"check:ui-patch": "node scripts/check-ui-patch.mjs",
|
|
137
|
-
"check:narrow": "node scripts/check-narrow-controls.mjs",
|
|
138
|
-
"check:drawer-form": "node scripts/check-drawer-form.mjs",
|
|
139
|
-
"check:responsive": "node scripts/check-responsive.mjs",
|
|
140
|
-
"check:adaptive": "node scripts/check-adaptive.mjs",
|
|
141
|
-
"check:sidebar-focus": "node scripts/check-sidebar-focus.mjs",
|
|
142
|
-
"check:json-render": "node scripts/check-json-render.mjs",
|
|
143
|
-
"check:motion": "node scripts/check-motion.mjs",
|
|
144
|
-
"check:topology": "node scripts/check-topology.mjs",
|
|
145
|
-
"check:wizard": "node scripts/check-wizard.mjs",
|
|
146
|
-
"check:edges": "node scripts/check-edges.mjs",
|
|
147
|
-
"check:data-grid": "node scripts/check-data-grid.mjs",
|
|
148
|
-
"check:truncation": "node scripts/check-truncation.mjs",
|
|
149
|
-
"check:link": "node scripts/check-link.mjs",
|
|
150
|
-
"check:text-select": "node scripts/check-text-select.mjs",
|
|
151
|
-
"check:date-picker": "node scripts/check-date-picker.mjs",
|
|
152
|
-
"check:full-page": "node scripts/check-full-page.mjs",
|
|
153
|
-
"check:short-viewport": "node scripts/check-short-viewport.mjs",
|
|
154
|
-
"check:phone-layout": "node scripts/check-phone-layout.mjs",
|
|
155
|
-
"check:phone-bars": "node scripts/check-phone-bars.mjs",
|
|
156
|
-
"check:text-zoom": "node scripts/check-text-zoom.mjs",
|
|
157
|
-
"check:focus-restore": "node scripts/check-focus-restore.mjs",
|
|
158
|
-
"check:detail-surface": "node scripts/check-detail-surface.mjs",
|
|
159
|
-
"check:internal": "node --test scripts/check-internal-content.test.mjs scripts/check-npm-metadata.test.mjs && node scripts/check-internal-content.mjs --repo .",
|
|
160
|
-
"check:npm-metadata": "node scripts/check-npm-metadata.mjs",
|
|
161
|
-
"check:package": "node scripts/check-package-content.mjs",
|
|
162
|
-
"check:public-api-consumer": "node scripts/check-public-api-consumer.mjs",
|
|
163
|
-
"notices:update": "node scripts/third-party-notices.mjs --write",
|
|
164
|
-
"notices:check": "node scripts/third-party-notices.mjs --check",
|
|
165
|
-
"check": "bash scripts/docker-check.sh \"npm run gates\"",
|
|
166
|
-
"check:fast": "bash scripts/docker-check.sh \"npm run gates\"",
|
|
167
|
-
"check:local-browser": "node scripts/app-local-browser.mjs",
|
|
168
|
-
"check:infotip": "node scripts/check-infotip.mjs",
|
|
169
|
-
"release:stage": "node scripts/stage-release.mjs",
|
|
170
|
-
"check:polish": "bash scripts/docker-check.sh --build --out .polish-shots 'node scripts/check-polish.mjs'",
|
|
171
|
-
"check:acceptance": "node scripts/check-acceptance.mjs",
|
|
172
|
-
"check:palette": "node scripts/check-palette.mjs",
|
|
173
|
-
"gates": "node scripts/gates.mjs",
|
|
174
|
-
"gates:full": "node scripts/gates.mjs --full",
|
|
175
|
-
"check:layout": "node scripts/check-layout.mjs",
|
|
176
|
-
"release:smoke": "node scripts/release-smoke.mjs",
|
|
177
|
-
"check:browser-install": "playwright install chromium webkit"
|
|
178
|
-
}
|
|
179
|
-
}
|
|
92
|
+
"homepage": "https://www.varvis.me/docs/"
|
|
93
|
+
}
|