@design.estate/dees-catalog 9.3.0 → 9.4.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.
Files changed (29) hide show
  1. package/dist_bundle/bundle.js +1372 -2727
  2. package/dist_bundle/bundle.js.map +1 -1
  3. package/dist_ts_web/00_commitinfo_data.js +1 -1
  4. package/dist_ts_web/elements/00group-button/dees-button/dees-button.js +2 -1
  5. package/dist_ts_web/elements/00group-input/dees-input-quantityselector/dees-input-quantityselector.js +4 -2
  6. package/dist_ts_web/elements/00group-simple/dees-shopping-productcard/dees-shopping-productcard.d.ts +11 -9
  7. package/dist_ts_web/elements/00group-simple/dees-shopping-productcard/dees-shopping-productcard.demo.d.ts +2 -2
  8. package/dist_ts_web/elements/00group-simple/dees-shopping-productcard/dees-shopping-productcard.demo.js +148 -319
  9. package/dist_ts_web/elements/00group-simple/dees-shopping-productcard/dees-shopping-productcard.js +60 -279
  10. package/dist_ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.d.ts +25 -10
  11. package/dist_ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.demo.d.ts +3 -4
  12. package/dist_ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.demo.js +229 -388
  13. package/dist_ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.js +253 -813
  14. package/dist_ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.demo.d.ts +2 -0
  15. package/dist_ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.demo.js +150 -389
  16. package/dist_ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.js +67 -11
  17. package/package.json +1 -1
  18. package/readme.hints.md +26 -0
  19. package/readme.md +76 -15
  20. package/scripts/bdtheme-baseline.json +0 -2
  21. package/ts_web/00_commitinfo_data.ts +1 -1
  22. package/ts_web/elements/00group-button/dees-button/dees-button.ts +1 -0
  23. package/ts_web/elements/00group-input/dees-input-quantityselector/dees-input-quantityselector.ts +3 -1
  24. package/ts_web/elements/00group-simple/dees-shopping-productcard/dees-shopping-productcard.demo.ts +67 -332
  25. package/ts_web/elements/00group-simple/dees-shopping-productcard/dees-shopping-productcard.ts +65 -322
  26. package/ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.demo.ts +136 -382
  27. package/ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.ts +224 -872
  28. package/ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.demo.ts +116 -421
  29. package/ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.ts +66 -10
@@ -1,436 +1,131 @@
1
- import { html, css, cssManager, customElement, state, DeesElement } from '@design.estate/dees-element';
1
+ import { html, css, cssManager, customElement, state, DeesElement, directives } from '@design.estate/dees-element';
2
2
  import { themeDefaultStyles } from '../../00theme.js';
3
- import type {
4
- DeesSimpleLogin,
5
- IDeesLoginPasskeyContext,
6
- IDeesLoginProvider,
7
- IDeesLoginProviderContext,
8
- TDeesLoginMethod,
9
- } from './dees-simple-login.js';
3
+ import { demoPage } from '../../../demos/templates.js';
4
+ import type { DeesSimpleLogin, IDeesLoginProvider, IDeesLoginProviderContext, TDeesLoginPasskeyIntent, TDeesLoginMethod } from './dees-simple-login.js';
5
+ import '../../00group-input/dees-input-dropdown/dees-input-dropdown.js';
6
+ import '../../00group-input/dees-input-multitoggle/dees-input-multitoggle.js';
10
7
 
11
- /**
12
- * The catalog carries no third-party brand assets, so provider icons are always supplied by
13
- * the consumer. These are neutral Lucide stand-ins for what a real deployment would pass.
14
- */
15
- const demoProviders: IDeesLoginProvider[] = [
8
+ const providers: IDeesLoginProvider[] = [
9
+ { id: 'workspace', label: 'Workspace account', icon: 'lucide:building2' },
16
10
  { id: 'idp-global', label: 'idp.global', icon: 'lucide:shieldCheck' },
17
- { id: 'workspace', label: 'Workspace SSO', icon: 'lucide:building2' },
18
- { id: 'email-link', label: 'Email link', icon: 'lucide:mail' },
19
11
  ];
20
-
21
- const manyProviders: IDeesLoginProvider[] = [
22
- ...demoProviders,
23
- { id: 'security-key', label: 'Security key', icon: 'lucide:usb' },
24
- { id: 'smartcard', label: 'Smartcard', icon: 'lucide:idCard' },
25
- { id: 'legacy-ldap', label: 'LDAP', icon: 'lucide:network', disabled: true },
12
+ const variants = [
13
+ { key: 'full', option: 'All sign-in methods' }, { key: 'password', option: 'Password only' },
14
+ { key: 'passkey', option: 'Passkey only' }, { key: 'enroll', option: 'Passkey enrollment' },
15
+ { key: 'providers', option: 'Identity providers' }, { key: 'many', option: 'Six providers' },
16
+ { key: 'events', option: 'Consumer-managed events' }, { key: 'unavailable', option: 'Unavailable passkey' },
26
17
  ];
27
18
 
28
- const delay = (msArg: number) => new Promise<void>((resolve) => window.setTimeout(resolve, msArg));
29
-
30
- type TDemoVariant =
31
- | 'legacy'
32
- | 'full'
33
- | 'passkeyOnly'
34
- | 'enrollment'
35
- | 'providerOnly'
36
- | 'manyProviders'
37
- | 'eventMode';
38
-
39
- interface IDemoVariantConfig {
40
- key: TDemoVariant;
41
- title: string;
42
- description: string;
43
- }
44
-
45
- const variants: IDemoVariantConfig[] = [
46
- {
47
- key: 'legacy',
48
- title: 'Default (password only)',
49
- description:
50
- 'No new properties set. This is the pre-existing component: username, password, the ' +
51
- 'legacy `login` event and nothing else.',
52
- },
53
- {
54
- key: 'full',
55
- title: 'Passkey + providers + password',
56
- description:
57
- 'Handler mode. Every method has its own busy and error state — the passkey handler ' +
58
- 'below always rejects, and the password form stays untouched by it.',
59
- },
60
- {
61
- key: 'passkeyOnly',
62
- title: 'Passkey only',
63
- description: '`.passkey=${true} .password=${false}` — no credential fallback at all.',
64
- },
65
- {
66
- key: 'enrollment',
67
- title: 'Passkey enrollment + sign in',
68
- description:
69
- "`.passkeyIntents=${['authenticate', 'register']}` renders both ceremonies. " +
70
- 'This is what Onebox hand-rolls today.',
71
- },
72
- {
73
- key: 'providerOnly',
74
- title: 'Providers only',
75
- description: 'A pure OIDC surface. The provider handler resolves after a short delay.',
76
- },
77
- {
78
- key: 'manyProviders',
79
- title: 'Six providers',
80
- description:
81
- 'Four or more providers pair up into two columns instead of growing the card. The ' +
82
- 'last one is `disabled`.',
83
- },
84
- {
85
- key: 'eventMode',
86
- title: 'Event mode (no handlers)',
87
- description:
88
- 'No handler properties. The component dispatches `passkey-login` / `provider-login` ' +
89
- 'and the demo drives busy and error state through `setBusy()` and `reportError()`.',
90
- },
91
- ];
92
-
93
- @customElement('dees-simple-login-demo')
94
- class DeesSimpleLoginDemo extends DeesElement {
95
- @state()
96
- accessor variant: TDemoVariant = 'full';
97
-
98
- @state()
99
- accessor log: string[] = [];
100
-
101
- public static styles = [
102
- themeDefaultStyles,
103
- cssManager.defaultStyles,
104
- css`
105
- :host {
106
- display: block;
107
- position: absolute;
108
- inset: 0;
109
- font-family: var(--dees-font-family);
110
- color: var(--dees-color-text-primary);
111
- background: var(--dees-color-bg-canvas);
112
- }
113
-
114
- .layout {
115
- display: grid;
116
- grid-template-columns: 320px 1fr;
117
- height: 100%;
118
- }
119
-
120
- .sidebar {
121
- display: flex;
122
- flex-direction: column;
123
- gap: var(--dees-spacing-md);
124
- padding: var(--dees-spacing-xl);
125
- overflow-y: auto;
126
- border-right: 1px solid var(--dees-color-border-default);
127
- background: var(--dees-color-bg-secondary);
128
- }
129
-
130
- .sidebarTitle {
131
- font-size: var(--dees-font-control-size);
132
- font-weight: 600;
133
- color: var(--dees-color-text-muted);
134
- text-transform: uppercase;
135
- letter-spacing: 0.06em;
136
- }
137
-
138
- .variant {
139
- padding: var(--dees-spacing-md);
140
- border: 1px solid var(--dees-color-border-subtle);
141
- border-radius: var(--dees-radius-lg);
142
- corner-shape: var(--dees-corner-shape);
143
- background: var(--dees-color-bg-primary);
144
- cursor: pointer;
145
- transition: border-color var(--dees-transition-fast) var(--dees-ease-standard);
146
- }
147
-
148
- .variant:hover {
149
- border-color: var(--dees-color-border-strong);
150
- }
151
-
152
- .variant.selected {
153
- border-color: var(--dees-color-accent-primary);
154
- box-shadow: 0 0 0 1px var(--dees-color-accent-primary);
155
- }
156
-
157
- .variantTitle {
158
- font-size: var(--dees-font-control-size);
159
- font-weight: 600;
160
- }
161
-
162
- .variantDescription {
163
- margin-top: var(--dees-spacing-xs);
164
- font-size: var(--dees-font-control-size-sm);
165
- line-height: 1.45;
166
- color: var(--dees-color-text-muted);
167
- }
168
-
169
- .logTitle {
170
- margin-top: var(--dees-spacing-lg);
171
- }
172
-
173
- .logEntries {
174
- display: flex;
175
- flex-direction: column;
176
- gap: var(--dees-spacing-xs);
177
- font-family: var(--dees-font-family-mono);
178
- font-size: var(--dees-font-control-size-sm);
179
- color: var(--dees-color-text-secondary);
180
- user-select: text;
181
- }
182
-
183
- .stage {
184
- position: relative;
185
- overflow: hidden;
186
- }
187
-
188
- .authenticated {
189
- display: flex;
190
- flex-direction: column;
191
- gap: var(--dees-spacing-md);
192
- align-items: center;
193
- justify-content: center;
194
- height: 100%;
195
- text-align: center;
196
- }
197
-
198
- .authenticated h1 {
199
- margin: 0;
200
- font-size: var(--dees-font-title2-size);
201
- }
202
-
203
- .authenticated p {
204
- margin: 0;
205
- color: var(--dees-color-text-muted);
206
- }
207
- `,
208
- ];
209
-
210
- private get loginElement(): DeesSimpleLogin | null {
211
- return this.shadowRoot?.querySelector('dees-simple-login') || null;
19
+ @customElement('dees-demo-simple-login')
20
+ class DeesDemoSimpleLogin extends DeesElement {
21
+ @state() private accessor variant = 'full';
22
+ @state() private accessor outcome = 'success';
23
+ @state() private accessor revision = 0;
24
+ @state() private accessor result = 'Choose a sign-in method. These accounts and responses are local samples.';
25
+ private work = new AbortController();
26
+ private get login(): DeesSimpleLogin | null { return this.shadowRoot?.querySelector('dees-simple-login') || null; }
27
+
28
+ public static styles = [themeDefaultStyles, cssManager.defaultStyles, css`
29
+ :host { display: grid; gap: 16px; font-family: var(--dees-font-family); }
30
+ .controls { display: flex; align-items: end; flex-wrap: wrap; gap: 16px; }
31
+ .controls dees-input-dropdown { width: 250px; max-width: 100%; }
32
+ .controls dees-button { margin-left: auto; }
33
+ .stage { height: 720px; min-height: 0; position: relative; border: 1px solid var(--dees-color-border-subtle); border-radius: var(--dees-radius-2xl); corner-shape: var(--dees-corner-shape); overflow: hidden; }
34
+ .authenticated { display: grid; place-content: center; justify-items: center; text-align: center; gap: 16px; height: 100%; padding: 32px; background: var(--dees-color-bg-canvas); }
35
+ .authenticated > dees-icon { color: var(--dees-color-text-success); }
36
+ h2 { margin: 0; font-size: 28px; font-weight: 600; letter-spacing: -.03em; }
37
+ p, output { margin: 0; color: var(--dees-color-text-secondary); font-size: 13px; line-height: 1.5; }
38
+ output { min-height: 40px; padding: 12px 16px; background: var(--dees-color-bg-secondary); border-radius: var(--dees-radius-lg); }
39
+ @media (max-width: 480px) { .stage { height: 760px; } }
40
+ `];
41
+
42
+ private resetDemo(): void {
43
+ this.work.abort(); this.work = new AbortController(); this.revision++;
44
+ this.result = 'Ready. No account or personal information is required.';
212
45
  }
213
-
214
- private note(messageArg: string): void {
215
- this.log = [messageArg, ...this.log].slice(0, 12);
46
+ public async disconnectedCallback(): Promise<void> { this.work.abort(); await super.disconnectedCallback(); }
47
+ public async connectedCallback(): Promise<void> { if (this.work.signal.aborted) this.work = new AbortController(); await super.connectedCallback(); }
48
+
49
+ private async respond(method: string, eventTarget?: { method: TDeesLoginMethod; providerId?: string; intent?: TDeesLoginPasskeyIntent }): Promise<void> {
50
+ const login = this.login;
51
+ if (!login) return;
52
+ const signal = this.work.signal;
53
+ const outcome = this.outcome;
54
+ if (eventTarget) login.setBusy(eventTarget, true);
55
+ const form = eventTarget?.method === 'password' ? login.shadowRoot?.querySelector('dees-form') : null;
56
+ form?.setStatus('pending', 'Signing in…');
57
+ this.result = `Waiting for ${method}…`;
58
+ const completed = await new Promise<boolean>(resolve => {
59
+ const abort = () => { window.clearTimeout(timer); resolve(false); };
60
+ const timer = window.setTimeout(() => { signal.removeEventListener('abort', abort); resolve(true); }, 700);
61
+ signal.addEventListener('abort', abort, { once: true });
62
+ });
63
+ if (!completed || signal.aborted || !login.isConnected) return;
64
+ if (eventTarget) login.setBusy(eventTarget, false);
65
+ form?.setStatus('normal', 'Sign in');
66
+ if (outcome === 'error') {
67
+ this.result = `${method} was declined. The other sign-in methods remain available.`;
68
+ if (eventTarget) login.reportError(eventTarget, 'Could not sign in. Try again or choose another method.');
69
+ else throw new Error('Could not sign in. Try again or choose another method.');
70
+ return;
71
+ }
72
+ this.result = `Signed in with ${method}.`;
73
+ await login.switchToSlottedContent();
74
+ if (signal.aborted || !login.isConnected) return;
75
+ await this.updateComplete;
76
+ login.querySelector('dees-button')?.focus();
216
77
  }
217
78
 
218
- public render() {
219
- return html`
220
- <div class="layout">
221
- <div class="sidebar">
222
- <div class="sidebarTitle">Variants</div>
223
- ${variants.map(
224
- (variantArg) => html`
225
- <div
226
- class="variant ${this.variant === variantArg.key ? 'selected' : ''}"
227
- @click=${() => {
228
- this.variant = variantArg.key;
229
- this.log = [];
230
- }}
231
- >
232
- <div class="variantTitle">${variantArg.title}</div>
233
- <div class="variantDescription">${variantArg.description}</div>
234
- </div>
235
- `,
236
- )}
237
- <div class="sidebarTitle logTitle">Event log</div>
238
- <div class="logEntries">
239
- ${this.log.length === 0
240
- ? html`<div>— nothing yet —</div>`
241
- : this.log.map((entry) => html`<div>${entry}</div>`)}
242
- </div>
243
- </div>
244
- <div class="stage">${this.renderVariant()}</div>
245
- </div>
246
- `;
79
+ private signOut(): void {
80
+ const login = this.login;
81
+ if (!login) return;
82
+ login.reset();
83
+ const root = login.shadowRoot!;
84
+ root.querySelector('dees-form')?.setStatus('normal', 'Sign in');
85
+ Object.assign(root.querySelector<HTMLElement>('.login')!.style, { opacity: '1', transform: 'translateY(0px)' });
86
+ root.querySelector<HTMLElement>('.loginContainer')!.style.pointerEvents = 'all';
87
+ Object.assign(root.querySelector<HTMLElement>('.slotContainer')!.style, { opacity: '0', pointerEvents: 'none' });
88
+ this.result = 'Signed out. The same login surface is ready again.';
89
+ requestAnimationFrame(() => {
90
+ if (!login.isConnected) return;
91
+ const button = root.querySelector('dees-button');
92
+ const input = root.querySelector('dees-input-text');
93
+ (button || input)?.focus();
94
+ });
247
95
  }
248
96
 
249
- private renderVariant() {
250
- const shared = html`
251
- <div class="authenticated">
252
- <h1>Signed in</h1>
253
- <p>This is the slotted content that appears after login.</p>
97
+ public render() {
98
+ const v = this.variant;
99
+ const eventMode = v === 'events';
100
+ const passkey = ['full', 'passkey', 'enroll', 'events', 'unavailable'].includes(v);
101
+ const password = ['full', 'password', 'many', 'events'].includes(v);
102
+ const configuredProviders = ['full', 'providers', 'events'].includes(v) ? providers : v === 'many' ? [
103
+ ...providers, { id: 'email', label: 'Email link', icon: 'lucide:mail' }, { id: 'key', label: 'Security key', icon: 'lucide:keyRound' },
104
+ { id: 'card', label: 'Smartcard', icon: 'lucide:contact' }, { id: 'directory', label: 'Directory', icon: 'lucide:network', disabled: true },
105
+ ] : [];
106
+ return html`<div class="controls">
107
+ <dees-input-dropdown label="Configuration" .options=${variants} .selectedOption=${variants.find(variant => variant.key === v)!}
108
+ @selectedOption=${(event: CustomEvent) => { this.variant = event.detail.key; this.resetDemo(); }}></dees-input-dropdown>
109
+ <dees-input-multitoggle label="Demo response" .options=${[{ key: 'success', label: 'Success' }, { key: 'error', label: 'Error' }]} .value=${this.outcome}
110
+ @change=${(event: CustomEvent) => { this.outcome = event.detail.value; }}></dees-input-multitoggle>
111
+ <dees-button type="ghost" text="Reset demo" @clicked=${this.resetDemo}></dees-button>
112
+ </div>
113
+ <div class="stage">${directives.keyed(`${v}-${this.revision}`, html`<dees-simple-login name="Studio" .passkey=${passkey} .passkeyAvailable=${v !== 'unavailable'} .password=${password}
114
+ .providers=${configuredProviders} .passkeyIntents=${v === 'enroll' ? ['authenticate', 'register'] : ['authenticate']}
115
+ .labels=${{ heading: v === 'enroll' ? 'Your key to Studio' : 'Welcome to Studio', subheading: v === 'enroll' ? 'Sign in or create a passkey for this device.' : 'A little space for your next big idea.' }}
116
+ .passkeyLoginHandler=${eventMode ? undefined : () => this.respond('your passkey')}
117
+ .passkeyRegisterHandler=${() => this.respond('a new passkey')}
118
+ .providerLoginHandler=${eventMode ? undefined : (context: IDeesLoginProviderContext) => this.respond(context.provider.label)}
119
+ .passwordLoginHandler=${eventMode || v === 'password' ? undefined : () => this.respond('your password')}
120
+ @passkey-login=${() => { if (eventMode) void this.respond('your passkey', { method: 'passkey' }); }}
121
+ @provider-login=${(event: CustomEvent) => { if (eventMode) void this.respond(event.detail.provider.label, { method: 'provider', providerId: event.detail.providerId }); }}
122
+ @login=${() => { if (eventMode || v === 'password') void this.respond('your password', { method: 'password' }); }}>
123
+ <dees-icon slot="brand" icon="lucide:layers" iconSize="26"></dees-icon>
124
+ <div class="authenticated"><dees-icon icon="lucide:circleCheck" iconSize="36"></dees-icon><h2>You're in.</h2><p>Your Studio workspace is ready.</p>
125
+ <dees-button type="accent" text="Sign out" @clicked=${this.signOut}></dees-button>
254
126
  </div>
255
- `;
256
-
257
- if (this.variant === 'legacy') {
258
- return html`
259
- <dees-simple-login
260
- name="My Application"
261
- @login=${(eventArg: CustomEvent) => {
262
- const data = eventArg.detail?.data;
263
- this.note(`login: ${data?.username} / ${'*'.repeat(data?.password?.length || 0)}`);
264
- void this.loginElement?.switchToSlottedContent();
265
- }}
266
- >
267
- ${shared}
268
- </dees-simple-login>
269
- `;
270
- }
271
-
272
- if (this.variant === 'full') {
273
- return html`
274
- <dees-simple-login
275
- name="idp.global"
276
- .passkey=${true}
277
- .passkeyAvailable=${true}
278
- .providers=${demoProviders}
279
- .labels=${{ subheading: 'Choose how you want to continue' }}
280
- .passkeyLoginHandler=${async (contextArg: IDeesLoginPasskeyContext) => {
281
- this.note(`passkey handler (${contextArg.mediation})`);
282
- await delay(900);
283
- throw new Error('No passkey found for this account.');
284
- }}
285
- .providerLoginHandler=${async (contextArg: IDeesLoginProviderContext) => {
286
- this.note(`provider handler: ${contextArg.providerId}`);
287
- await delay(900);
288
- void this.loginElement?.switchToSlottedContent();
289
- }}
290
- .passwordLoginHandler=${async () => {
291
- this.note('password handler');
292
- await delay(900);
293
- void this.loginElement?.switchToSlottedContent();
294
- }}
295
- @login=${(eventArg: CustomEvent) => {
296
- this.note(`login event: ${eventArg.detail?.data?.username}`);
297
- }}
298
- >
299
- ${shared}
300
- </dees-simple-login>
301
- `;
302
- }
303
-
304
- if (this.variant === 'passkeyOnly') {
305
- return html`
306
- <dees-simple-login
307
- name="Onebox"
308
- .passkey=${true}
309
- .passkeyAvailable=${true}
310
- .password=${false}
311
- .labels=${{ subheading: 'Use a passkey registered for your Onebox account' }}
312
- .passkeyLoginHandler=${async () => {
313
- this.note('passkey sign in');
314
- await delay(900);
315
- void this.loginElement?.switchToSlottedContent();
316
- }}
317
- >
318
- ${shared}
319
- </dees-simple-login>
320
- `;
321
- }
322
-
323
- if (this.variant === 'enrollment') {
324
- return html`
325
- <dees-simple-login
326
- name="Onebox"
327
- .passkey=${true}
328
- .passkeyAvailable=${true}
329
- .password=${false}
330
- .passkeyIntents=${['authenticate', 'register']}
331
- .labels=${{ subheading: 'Sign in, or enroll this device' }}
332
- .passkeyLoginHandler=${async () => {
333
- this.note('passkey authenticate');
334
- await delay(900);
335
- throw new Error('This device has no passkey for Onebox yet.');
336
- }}
337
- .passkeyRegisterHandler=${async () => {
338
- this.note('passkey register');
339
- await delay(900);
340
- void this.loginElement?.switchToSlottedContent();
341
- }}
342
- >
343
- ${shared}
344
- </dees-simple-login>
345
- `;
346
- }
347
-
348
- if (this.variant === 'providerOnly') {
349
- return html`
350
- <dees-simple-login
351
- name="login.idp.global"
352
- .password=${false}
353
- .providers=${demoProviders}
354
- .labels=${{ subheading: 'Continue with one of your identity providers' }}
355
- .providerLoginHandler=${async (contextArg: IDeesLoginProviderContext) => {
356
- this.note(`provider handler: ${contextArg.providerId}`);
357
- await delay(900);
358
- void this.loginElement?.switchToSlottedContent();
359
- }}
360
- >
361
- ${shared}
362
- </dees-simple-login>
363
- `;
364
- }
365
-
366
- if (this.variant === 'manyProviders') {
367
- return html`
368
- <dees-simple-login
369
- name="login.idp.global"
370
- .providers=${manyProviders}
371
- .methodOrder=${['provider', 'password'] as TDeesLoginMethod[]}
372
- .providerLoginHandler=${async (contextArg: IDeesLoginProviderContext) => {
373
- this.note(`provider handler: ${contextArg.providerId}`);
374
- await delay(600);
375
- throw new Error('Discovery endpoint unreachable.');
376
- }}
377
- @login=${() => this.note('login event')}
378
- >
379
- ${shared}
380
- </dees-simple-login>
381
- `;
382
- }
383
-
384
- // eventMode
385
- return html`
386
- <dees-simple-login
387
- name="Cloudly"
388
- .passkey=${true}
389
- .passkeyAvailable=${true}
390
- .providers=${demoProviders}
391
- @passkey-login=${async (eventArg: CustomEvent) => {
392
- this.note(`passkey-login event (username: ${eventArg.detail.username || '—'})`);
393
- const element = this.loginElement;
394
- element?.setBusy({ method: 'passkey' }, true);
395
- await delay(900);
396
- element?.setBusy({ method: 'passkey' }, false);
397
- element?.reportError({ method: 'passkey' }, 'Consumer-reported: ceremony declined.');
398
- }}
399
- @provider-login=${async (eventArg: CustomEvent) => {
400
- this.note(`provider-login event: ${eventArg.detail.providerId}`);
401
- const element = this.loginElement;
402
- const target = { method: 'provider' as const, providerId: eventArg.detail.providerId };
403
- element?.setBusy(target, true);
404
- await delay(900);
405
- element?.setBusy(target, false);
406
- element?.reportError(target, 'Consumer-reported: redirect blocked.');
407
- }}
408
- @login=${(eventArg: CustomEvent) => {
409
- this.note(`login event: ${eventArg.detail?.data?.username}`);
410
- this.loginElement?.reportError('password', 'Consumer-reported: invalid credentials.');
411
- }}
412
- >
413
- ${shared}
414
- </dees-simple-login>
415
- `;
127
+ </dees-simple-login>`)}</div><output role="status">${this.result}</output>`;
416
128
  }
417
129
  }
418
130
 
419
- export const demoFunc = () => html`
420
- <style>
421
- body {
422
- margin: 0;
423
- padding: 0;
424
- }
425
- .demo-container {
426
- width: 100%;
427
- height: 100%;
428
- position: absolute;
429
- top: 0;
430
- left: 0;
431
- }
432
- </style>
433
- <div class="demo-container">
434
- <dees-simple-login-demo></dees-simple-login-demo>
435
- </div>
436
- `;
131
+ export const demoFunc = () => demoPage('Sign in', 'One welcoming surface. Passkeys, workspace accounts, and passwords working together.', html`<dees-demo-simple-login></dees-demo-simple-login>`);