@design.estate/dees-catalog 9.5.0 → 9.6.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.
@@ -0,0 +1 @@
1
+ export declare const stepperStyles: import("@design.estate/dees-element").CSSResult;
@@ -0,0 +1,41 @@
1
+ import { css } from '@design.estate/dees-element';
2
+ export const stepperStyles = css `
3
+ :host { display: block; position: relative; width: 100%; height: 100%; min-height: 440px; min-width: 0; font-family: var(--dees-font-family); color: var(--dees-color-text-primary); }
4
+ :host([overlay]) { position: static; width: 0; height: 0; min-height: 0; }
5
+ .stepperContainer { position: absolute; inset: 0; width: 100%; height: 100%; overflow: hidden; overflow-anchor: none; box-sizing: border-box; container: stepper-surface / inline-size; }
6
+ .stepperContainer.overlay { position: fixed; width: 100%; height: 100dvh; max-width: none; max-height: none; border: 0; padding: 0; margin: 0; color: inherit; background: transparent; }
7
+ .stepperContainer::backdrop { background: var(--dees-color-scrim); }
8
+ dees-tile.step { position: relative; width: min(var(--dees-stepper-width, 520px), calc(100% - 40px)); max-height: calc(100% - 48px); min-height: min(280px, calc(100% - 48px)); margin: 0 auto 20px; pointer-events: none; opacity: .25; transition: opacity 240ms var(--dees-ease-standard); }
9
+ dees-tile.step::part(outer) { border-color: var(--dees-color-border-subtle); }
10
+ dees-tile.step.selected { pointer-events: auto; opacity: 1; }
11
+ dees-tile.step.selected::part(outer) { border-color: var(--dees-color-border-default); box-shadow: var(--dees-shadow-md); }
12
+ dees-tile.step.hiddenStep { opacity: 0; }
13
+ dees-tile.step:last-child { margin-bottom: 100dvh; }
14
+ .step-header { display: flex; align-items: center; gap: 16px; padding: 16px 24px; min-height: 48px; box-sizing: border-box; }
15
+ .step-rail { display: flex; flex: 1; gap: 5px; list-style: none; margin: 0; padding: 0; min-width: 0; }
16
+ .step-marker { flex: 1; height: 4px; border-radius: 2px; background: var(--dees-color-fill-secondary); }
17
+ .step-marker.complete { background: var(--dees-color-text-secondary); }
18
+ .step-marker.current { background: var(--dees-color-accent-primary); }
19
+ .step-counter { flex: none; font-size: 12px; font-weight: 500; font-variant-numeric: tabular-nums; color: var(--dees-color-text-secondary); }
20
+ .step-body { padding: 24px; }
21
+ .heading-row { display: flex; align-items: flex-start; gap: 16px; }
22
+ .title { margin: 0; font-size: 20px; line-height: 1.3; font-weight: 600; letter-spacing: -.025em; overflow-wrap: anywhere; outline: none; }
23
+ .description { margin: 8px 0 0; font-size: 13px; line-height: 1.55; color: var(--dees-color-text-secondary); }
24
+ .content { margin-top: 24px; font-size: 13px; line-height: 1.55; overflow-wrap: anywhere; }
25
+ .content:empty { display: none; }
26
+ .progressStep { margin: 20px -8px 0; }
27
+ .step-error { display: flex; align-items: flex-start; gap: 8px; padding: 12px; margin-top: 16px; border-radius: var(--dees-radius-md); background: var(--dees-color-badge-error-bg); color: var(--dees-color-text-error); font-size: 12px; line-height: 1.5; overflow-wrap: anywhere; }
28
+ .step-error dees-icon { flex: none; margin-top: 1px; }
29
+ .step-footer { padding: 12px 24px; }
30
+ .step-hint { flex: 0 0 120px; padding-top: 4px; margin-left: auto; color: var(--dees-color-text-secondary); font-size: 11px; line-height: 1.4; text-align: right; transition: opacity 160ms var(--dees-ease-standard), visibility 160ms; }
31
+ .step-hint[aria-hidden='true'] { opacity: 0; visibility: hidden; pointer-events: none; }
32
+ .bottomButtons { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
33
+ .back-action { margin-right: auto; }
34
+ .footer-spacer { margin-right: auto; }
35
+ .empty { display: grid; place-content: center; height: 100%; text-align: center; color: var(--dees-color-text-secondary); font-size: 13px; }
36
+ .predestroy .step.selected { animation: stepper-exit 160ms var(--dees-ease-standard) forwards; }
37
+ @keyframes stepper-exit { to { opacity: 0; transform: translateY(10px); } }
38
+ @media (prefers-reduced-motion: reduce) { dees-tile.step, .step-hint { transition: none; } .predestroy .step.selected { animation: none; } }
39
+ @container stepper-surface (max-width: 540px) { dees-tile.step { width: calc(100% - 24px); max-height: calc(100% - 24px); } .step-header { padding: 12px 20px; } .step-body { padding: 20px; } .step-footer { padding: 12px 20px; } .step-hint { flex-basis: 84px; } }
40
+ `;
41
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3R5bGVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vdHNfd2ViL2VsZW1lbnRzLzAwZ3JvdXAtbGF5b3V0L2RlZXMtc3RlcHBlci9zdHlsZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBRWxELE1BQU0sQ0FBQyxNQUFNLGFBQWEsR0FBRyxHQUFHLENBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0NBc0MvQixDQUFDIn0=
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@design.estate/dees-catalog",
3
- "version": "9.5.0",
3
+ "version": "9.6.0",
4
4
  "private": false,
5
5
  "description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.",
6
6
  "main": "dist_ts_web/index.js",
package/readme.md CHANGED
@@ -2173,13 +2173,43 @@ const layer = await DeesWindowLayer.createAndShow({
2173
2173
  ### Navigation Components
2174
2174
 
2175
2175
  #### `DeesStepper`
2176
- Multi-step navigation component for guided user flows, including optional auto-advancing progress steps that can render `dees-progressbar` status output between form steps.
2176
+ Guided form and background-work flows with a compact progress strip, shared action
2177
+ buttons, and a vertical transition between steps. Use it inline or open the same
2178
+ steps as a modal with `DeesStepper.createAndShow({ steps, cancelable: true })`.
2179
+
2180
+ - `IStep` accepts `title`, optional `description`, `content`, `menuOptions`,
2181
+ `allowBack`, `validationFunc`, `onReturnToStepFunc`, and `progressStep`.
2182
+ - The last menu action is primary. A nested `dees-form` gates that action on its
2183
+ required fields and dispatches collected `formData` before the action runs.
2184
+ The required-fields hint fades beside the heading without changing its layout.
2185
+ - Async actions prevent duplicate activation and display failures in the current
2186
+ step. Progress failures expose **Try again**; `retryStep()` reruns validation.
2187
+ Keep external work abortable using the callback's `AbortSignal`.
2188
+ - Progress steps use `dees-progressbar` and advance after successful validation
2189
+ unless `progressStep.autoAdvance` is `false`. Use `updateProgressStep()`,
2190
+ `appendProgressStepLine()`, and `resetProgressStep()` to report work.
2191
+ - `goNext()` and `goBack()` preserve mounted form contents. Back honors
2192
+ `allowBack: false`. Inactive steps are inert. Transitions respect reduced motion
2193
+ and adjust to changing content and container sizes without snapping.
2194
+ - Overlay mode uses native dialog focus containment and restoration. Cancel,
2195
+ Escape, and backdrop clicks share a confirmation flow; `cancelable: false`
2196
+ disables those dismissal paths. `destroy()` closes the flow; `stepper-close`
2197
+ fires when it disconnects. Disconnecting also aborts work and releases observers.
2198
+ - Inline steppers fill a bounded area (440px minimum height by default). Set a
2199
+ height appropriate to the composition; `--dees-stepper-width` controls the card's
2200
+ maximum width (520px by default).
2201
+
2202
+ The catalog demo combines setup fields, preferences, a review, background work,
2203
+ and completion. **Retry example** fails the first background attempt and preserves
2204
+ the choices for recovery. It also shows optional and required-completion overlays.
2177
2205
 
2178
2206
  ```typescript
2179
2207
  <dees-stepper
2208
+ style="height: 540px"
2180
2209
  .steps=${[
2181
2210
  {
2182
2211
  title: 'Account Setup',
2212
+ description: 'Choose the owner of your workspace.',
2183
2213
  content: html`<dees-form>...</dees-form>`,
2184
2214
  menuOptions: [{ name: 'Continue', action: async (stepper) => stepper?.goNext() }]
2185
2215
  },
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@design.estate/dees-catalog',
6
- version: '9.5.0',
6
+ version: '9.6.0',
7
7
  description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
8
8
  }
@@ -1,212 +1,154 @@
1
- import { html } from '@design.estate/dees-element';
1
+ import { DeesElement, customElement, property, state, html, css, cssManager } from '@design.estate/dees-element';
2
2
  import { DeesStepper, type IStep } from './dees-stepper.js';
3
+ import { themeDefaultStyles } from '../../00theme.js';
4
+ import { demoPage } from '../../../demos/templates.js';
5
+ import '../../00group-form/dees-form/dees-form.js';
6
+ import '../../00group-input/dees-input-text/dees-input-text.js';
7
+ import '../../00group-input/dees-input-multitoggle/dees-input-multitoggle.js';
8
+ import '../../00group-input/dees-input-toggle/dees-input-toggle.js';
3
9
 
4
- const waitForProgressTick = async (timeoutArg: number, signal?: AbortSignal): Promise<boolean> => {
5
- return new Promise((resolve) => {
6
- let completed = false;
10
+ const waitForProgressTick = (duration: number, signal?: AbortSignal): Promise<boolean> => new Promise(resolve => {
11
+ if (signal?.aborted) { resolve(false); return; }
12
+ const abort = () => { clearTimeout(timer); resolve(false); };
13
+ const timer = setTimeout(() => { signal?.removeEventListener('abort', abort); resolve(true); }, duration);
14
+ signal?.addEventListener('abort', abort, { once: true });
15
+ });
7
16
 
8
- const finish = (result: boolean) => {
9
- if (completed) {
10
- return;
11
- }
17
+ const contentStyles = html`<style>
18
+ .setup-summary { margin: 0; display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 12px 24px; font-size: 13px; }
19
+ .setup-summary dt { color: var(--dees-color-text-secondary); }
20
+ .setup-summary dd { margin: 0; text-align: right; overflow-wrap: anywhere; font-weight: 500; }
21
+ .setup-note { color: var(--dees-color-text-secondary); font-size: 12px; line-height: 1.6; margin: 20px 0 0; }
22
+ .setup-success { display: flex; align-items: center; gap: 10px; margin: 0 0 20px; color: var(--dees-color-text-success); }
23
+ </style>`;
12
24
 
13
- completed = true;
14
- if (signal) {
15
- signal.removeEventListener('abort', handleAbort);
16
- }
17
- resolve(result);
18
- };
19
-
20
- const handleAbort = () => {
21
- window.clearTimeout(timeoutId);
22
- finish(false);
23
- };
24
-
25
- const timeoutId = window.setTimeout(() => {
26
- finish(true);
27
- }, timeoutArg);
28
-
29
- if (signal) {
30
- signal.addEventListener('abort', handleAbort, { once: true });
31
- }
32
- });
33
- };
34
-
35
- const createContinueMenuOptions = (labelArg = 'Continue') => [
36
- {
37
- name: labelArg,
38
- action: async (stepper?: DeesStepper) => stepper?.goNext(),
39
- },
40
- ];
41
-
42
- const createDemoSteps = (): IStep[] => [
43
- {
44
- title: 'Account Setup',
45
- content: html`
46
- <dees-form>
47
- <dees-input-text key="email" label="Work Email" required></dees-input-text>
48
- <dees-input-text key="password" label="Create Password" type="password" required></dees-input-text>
49
- </dees-form>
50
- `,
51
- menuOptions: createContinueMenuOptions(),
52
- },
53
- {
54
- title: 'Profile Details',
55
- content: html`
56
- <dees-form>
57
- <dees-input-text key="firstName" label="First Name" required></dees-input-text>
58
- <dees-input-text key="lastName" label="Last Name" required></dees-input-text>
59
- </dees-form>
60
- `,
61
- menuOptions: createContinueMenuOptions(),
62
- },
63
- {
64
- title: 'Contact Information',
65
- content: html`
66
- <dees-form>
67
- <dees-input-phone key="phone" label="Mobile Number" required></dees-input-phone>
68
- <dees-input-text key="company" label="Company"></dees-input-text>
69
- </dees-form>
70
- `,
71
- menuOptions: createContinueMenuOptions(),
72
- },
73
- {
74
- title: 'Provision Workspace',
75
- content: html`
76
- <dees-panel>
77
- <p>
78
- We are creating your starter workspace, applying your onboarding choices,
79
- and preparing a live preview. This step moves forward automatically when
80
- the environment is ready.
81
- </p>
82
- </dees-panel>
83
- `,
84
- progressStep: {
85
- label: 'Workspace setup',
86
- percentage: 8,
87
- indeterminate: true,
88
- statusRows: 4,
89
- statusText: 'Allocating a clean workspace...',
90
- terminalLines: ['Allocating a clean workspace'],
25
+ const createDemoSteps = (failFirst: boolean, finish: (name: string) => void): IStep[] => {
26
+ const draft = { name: '', email: '', purpose: 'Product', updates: true };
27
+ let attempts = 0;
28
+ const summary = () => html`${contentStyles}<dl class="setup-summary">
29
+ <dt>Workspace</dt><dd>${draft.name}</dd><dt>Owner</dt><dd>${draft.email}</dd>
30
+ <dt>Built for</dt><dd>${draft.purpose}</dd><dt>Weekly digest</dt><dd>${draft.updates ? 'On' : 'Off'}</dd>
31
+ </dl>`;
32
+ const collect = (event: CustomEvent<{ data: Partial<typeof draft> }>) => {
33
+ Object.assign(draft, event.detail.data);
34
+ steps[2].content = summary();
35
+ steps[4].content = html`${contentStyles}<p class="setup-success"><dees-icon icon="lucide:CircleCheck" iconSize="20"></dees-icon>Everything is in place.</p>${summary()}`;
36
+ };
37
+ const next = [{ name: 'Continue', action: async (stepper?: DeesStepper) => stepper?.goNext() }];
38
+ const steps: IStep[] = [
39
+ {
40
+ title: 'Create a workspace.',
41
+ description: 'Give your workspace a name and choose who will own it.',
42
+ content: html`<dees-form @formData=${collect}>
43
+ <dees-input-text key="name" label="Workspace name" placeholder="Studio Notes" required></dees-input-text>
44
+ <dees-input-text key="email" label="Work email" placeholder="alex@example.com" type="email" required></dees-input-text>
45
+ </dees-form>`,
46
+ menuOptions: next,
91
47
  },
92
- validationFunc: async (stepper, _htmlElement, signal) => {
93
- const progressFrames = [
94
- { line: 'Allocating a clean workspace', percentage: 8, delay: 500 },
95
- { line: 'Syncing account preferences', percentage: 24, delay: 650 },
96
- { line: 'Installing selected integrations', percentage: 47, delay: 700 },
97
- { line: 'Generating starter project files', percentage: 71, delay: 650 },
98
- { line: 'Booting the live preview environment', percentage: 92, delay: 700 },
99
- ];
100
-
101
- stepper.resetProgressStep();
102
-
103
- for (const [index, progressFrame] of progressFrames.entries()) {
104
- if (signal?.aborted) {
105
- return;
48
+ {
49
+ title: 'Make it yours.',
50
+ description: 'Start with a focus. You can change these choices later.',
51
+ content: html`<dees-form @formData=${collect}>
52
+ <dees-input-multitoggle key="purpose" label="What are you working on?" .options=${['Product', 'Operations', 'Research']}></dees-input-multitoggle>
53
+ <dees-input-toggle key="updates" label="Weekly workspace digest" .value=${true}></dees-input-toggle>
54
+ </dees-form>`,
55
+ menuOptions: next,
56
+ },
57
+ {
58
+ title: 'Ready to create?',
59
+ description: 'Take a moment to check your choices.',
60
+ content: summary(),
61
+ menuOptions: [{ name: 'Create workspace', action: async (stepper) => stepper?.goNext() }],
62
+ },
63
+ {
64
+ title: 'Bringing it together.',
65
+ description: 'Your workspace is taking shape. This usually takes a few seconds.',
66
+ content: html`${contentStyles}<p class="setup-note">You can go back to adjust your choices, or cancel while setup is running.</p>`,
67
+ progressStep: { label: 'Creating workspace', percentage: 0, statusRows: 3 },
68
+ validationFunc: async (stepper, _element, signal) => {
69
+ attempts++;
70
+ const messages = ['Creating your space', 'Applying preferences', 'Preparing starter files', 'Workspace ready'];
71
+ for (const [index, message] of messages.entries()) {
72
+ if (!await waitForProgressTick(650, signal)) return;
73
+ if (failFirst && attempts === 1 && index === 2) throw new Error('Starter files could not be copied. Your choices are saved. Try again.');
74
+ stepper.appendProgressStepLine(message);
75
+ stepper.updateProgressStep({ percentage: (index + 1) * 25, statusText: message, indeterminate: false });
106
76
  }
77
+ await waitForProgressTick(450, signal);
78
+ },
79
+ },
80
+ {
81
+ title: 'Your workspace is ready.',
82
+ description: 'A fresh start, with all the details you chose.',
83
+ allowBack: false,
84
+ content: summary(),
85
+ menuOptions: [{ name: 'Done', action: async stepper => { finish(draft.name); await stepper?.destroy(); } }],
86
+ },
87
+ ];
88
+ return steps;
89
+ };
107
90
 
108
- if (index === 0) {
109
- stepper.updateProgressStep({
110
- percentage: progressFrame.percentage,
111
- indeterminate: true,
112
- statusText: `${progressFrame.line}...`,
113
- terminalLines: [progressFrame.line],
114
- });
115
- } else {
116
- stepper.appendProgressStepLine(progressFrame.line);
117
- stepper.updateProgressStep({
118
- percentage: progressFrame.percentage,
119
- indeterminate: true,
120
- statusText: `${progressFrame.line}...`,
121
- });
122
- }
91
+ @customElement('dees-demo-stepper')
92
+ class DeesDemoStepper extends DeesElement {
93
+ @property() accessor scenario = 'Standard setup';
94
+ @state() private accessor closed = false;
95
+ @state() private accessor result = '';
96
+ private steps = createDemoSteps(false, name => { this.result = `${name} is ready.`; });
97
+ private overlayStepper?: DeesStepper;
123
98
 
124
- const completed = await waitForProgressTick(progressFrame.delay, signal);
125
- if (!completed) {
126
- return;
127
- }
128
- }
99
+ public static styles = [themeDefaultStyles, cssManager.defaultStyles, css`
100
+ :host { display: block; min-width: 0; }
101
+ .toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 20px; }
102
+ .actions { display: flex; gap: 8px; flex-wrap: wrap; }
103
+ .stage { position: relative; height: 540px; border: 1px solid var(--dees-color-border-subtle); border-radius: 18px; background: var(--dees-color-bg-secondary); overflow: hidden; }
104
+ .finished { display: grid; place-content: center; justify-items: center; gap: 16px; height: 100%; padding: 24px; box-sizing: border-box; text-align: center; }
105
+ .finished h2 { margin: 0; font-size: 20px; letter-spacing: -.025em; font-weight: 600; }
106
+ .finished p, .caption { font-size: 12px; line-height: 1.6; color: var(--dees-color-text-secondary); }
107
+ .caption { margin: 16px 0 0; }
108
+ summary.caption { cursor: pointer; font-size: 13px; font-weight: 500; }
109
+ .finished dees-icon { color: var(--dees-color-text-success); }
110
+ @media (max-width: 540px) { .stage { height: 560px; } }
111
+ `];
129
112
 
130
- stepper.appendProgressStepLine('Workspace ready');
131
- stepper.updateProgressStep({
132
- percentage: 100,
133
- indeterminate: false,
134
- statusText: 'Workspace ready.',
135
- });
113
+ private reset() {
114
+ this.result = '';
115
+ this.closed = false;
116
+ this.steps = createDemoSteps(this.scenario === 'Retry example', name => { this.result = `${name} is ready.`; });
117
+ this.requestUpdate();
118
+ }
136
119
 
137
- await waitForProgressTick(350, signal);
138
- },
139
- },
140
- {
141
- title: 'Team Size',
142
- content: html`
143
- <dees-form>
144
- <dees-input-dropdown
145
- key="teamSize"
146
- label="How big is your team?"
147
- .options=${[
148
- { label: '1-5', value: '1-5' },
149
- { label: '6-20', value: '6-20' },
150
- { label: '21-50', value: '21-50' },
151
- { label: '51+', value: '51+' },
152
- ]}
153
- required
154
- ></dees-input-dropdown>
155
- </dees-form>
156
- `,
157
- menuOptions: createContinueMenuOptions(),
158
- },
159
- {
160
- title: 'Goals',
161
- content: html`
162
- <dees-form>
163
- <dees-input-multitoggle
164
- key="goal"
165
- label="Main objective"
166
- .options=${[
167
- { label: 'Onboarding', key: 'onboarding' },
168
- { label: 'Analytics', key: 'analytics' },
169
- { label: 'Automation', key: 'automation' },
170
- ]}
171
- ></dees-input-multitoggle>
172
- </dees-form>
173
- `,
174
- menuOptions: createContinueMenuOptions(),
175
- },
176
- {
177
- title: 'Review & Launch',
178
- content: html`
179
- <dees-panel>
180
- <p>
181
- Your workspace is ready. Review the collected details and launch when
182
- you are ready to start.
183
- </p>
184
- </dees-panel>
185
- `,
186
- menuOptions: [
187
- {
188
- name: 'Launch',
189
- action: async (stepper?: DeesStepper) => {
190
- if (stepper?.overlay) {
191
- await stepper.destroy();
192
- }
193
- },
194
- },
195
- ],
196
- },
197
- ];
120
+ private async openOverlay(cancelable = true) {
121
+ if (this.overlayStepper?.isConnected) return;
122
+ this.overlayStepper = await DeesStepper.createAndShow({
123
+ cancelable,
124
+ steps: createDemoSteps(this.scenario === 'Retry example', name => { this.result = `${name} is ready.`; }),
125
+ });
126
+ }
198
127
 
199
- export const stepperDemo = () => html`
200
- <div style="position: absolute; inset: 0;">
201
- <div
202
- style="position: absolute; top: 16px; left: 50%; transform: translateX(-50%); z-index: 10;"
203
- >
204
- <dees-button
205
- @click=${async () => {
206
- await DeesStepper.createAndShow({ steps: createDemoSteps() });
207
- }}
208
- >Open stepper as overlay</dees-button>
128
+ public render() {
129
+ return html`<div class="toolbar">
130
+ <dees-input-multitoggle size="sm" label="Demo scenario" labelPosition="none"
131
+ .options=${['Standard setup', 'Retry example']} .selectedOption=${this.scenario}
132
+ @change=${(event: CustomEvent<{ value: string }>) => { this.scenario = event.detail.value; this.reset(); }}></dees-input-multitoggle>
133
+ <div class="actions"><dees-button type="secondary" text="Open overlay" @clicked=${() => this.openOverlay()}></dees-button>
134
+ <dees-button type="ghost" text="Reset" @clicked=${this.reset}></dees-button></div>
209
135
  </div>
210
- <dees-stepper .steps=${createDemoSteps()}></dees-stepper>
211
- </div>
212
- `;
136
+ <div class="stage">${this.closed ? html`<div class="finished">
137
+ <dees-icon icon=${this.result ? 'lucide:CircleCheck' : 'lucide:Layers'} iconSize="32"></dees-icon>
138
+ <h2>${this.result || 'Setup cancelled.'}</h2><p>This example uses local sample data.</p>
139
+ <dees-button type="secondary" text="Start again" @clicked=${this.reset}></dees-button>
140
+ </div>` : html`<dees-stepper .steps=${this.steps} @stepper-close=${() => { this.closed = true; }}></dees-stepper>`}</div>
141
+ <p class="caption">${this.scenario === 'Retry example' ? 'The first setup attempt pauses with an error. Try again to continue with your saved choices.' : 'Move forward, go back, and review your choices. Changes stay in this demo.'}</p>
142
+ <details><summary class="caption">Completion required</summary>
143
+ <p class="caption">For a flow that must finish, such as a required update, cancellation can be disabled.</p>
144
+ <dees-button type="secondary" text="Open required setup" @clicked=${() => this.openOverlay(false)}></dees-button>
145
+ </details>`;
146
+ }
147
+
148
+ public async disconnectedCallback() {
149
+ if (this.overlayStepper?.isConnected) await this.overlayStepper.destroy();
150
+ await super.disconnectedCallback();
151
+ }
152
+ }
153
+
154
+ export const stepperDemo = () => demoPage('Stepper', 'A clear path from the first detail to the finished result.', html`<dees-demo-stepper></dees-demo-stepper>`);