@design.estate/dees-catalog 3.97.1 → 3.98.1
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/dist_bundle/bundle.js +4915 -3518
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/00group-button/dees-button/dees-button.d.ts +35 -0
- package/dist_ts_web/elements/00group-button/dees-button/dees-button.js +128 -5
- package/dist_ts_web/elements/00group-chart/dees-chart-area/component.d.ts +14 -0
- package/dist_ts_web/elements/00group-chart/dees-chart-area/component.js +271 -117
- package/dist_ts_web/elements/00group-chart/dees-chart-area/realtime-updates.d.ts +21 -0
- package/dist_ts_web/elements/00group-chart/dees-chart-area/realtime-updates.js +79 -0
- package/dist_ts_web/elements/00group-feedback/dees-spinner/dees-spinner.js +12 -3
- package/dist_ts_web/elements/00group-form/dees-form-submit/dees-form-submit.d.ts +9 -0
- package/dist_ts_web/elements/00group-form/dees-form-submit/dees-form-submit.js +25 -2
- package/dist_ts_web/elements/00group-input/dees-input-text/dees-input-text.d.ts +15 -0
- package/dist_ts_web/elements/00group-input/dees-input-text/dees-input-text.js +36 -2
- package/dist_ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.d.ts +246 -0
- package/dist_ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.demo.js +439 -21
- package/dist_ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.js +857 -23
- package/package.json +3 -3
- package/readme.hints.md +35 -0
- package/readme.md +80 -11
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/00group-button/dees-button/dees-button.ts +128 -2
- package/ts_web/elements/00group-chart/dees-chart-area/component.ts +276 -116
- package/ts_web/elements/00group-chart/dees-chart-area/realtime-updates.ts +110 -0
- package/ts_web/elements/00group-feedback/dees-spinner/dees-spinner.ts +11 -2
- package/ts_web/elements/00group-form/dees-form-submit/dees-form-submit.ts +18 -0
- package/ts_web/elements/00group-input/dees-input-text/dees-input-text.ts +29 -0
- package/ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.demo.ts +418 -19
- package/ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.ts +969 -20
|
@@ -55,6 +55,10 @@ export class DeesSpinner extends DeesElement {
|
|
|
55
55
|
display: block;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/* --dees-spinner-color lets a spinner on a coloured surface (an accent button face,
|
|
59
|
+
a toast) match that surface's foreground. It is read as an inherited fallback
|
|
60
|
+
rather than declared on :host, because a :host declaration would outrank the
|
|
61
|
+
value an ancestor sets. Unset consumers keep the previous token. */
|
|
58
62
|
#loading {
|
|
59
63
|
position: relative;
|
|
60
64
|
transition: none;
|
|
@@ -62,9 +66,14 @@ export class DeesSpinner extends DeesElement {
|
|
|
62
66
|
justify-content: center;
|
|
63
67
|
align-content: center;
|
|
64
68
|
background: transparent;
|
|
65
|
-
border: 3px solid
|
|
69
|
+
border: 3px solid
|
|
70
|
+
color-mix(
|
|
71
|
+
in srgb,
|
|
72
|
+
var(--dees-spinner-color, var(--dees-color-text-primary)) 20%,
|
|
73
|
+
transparent
|
|
74
|
+
);
|
|
66
75
|
border-radius: 50%;
|
|
67
|
-
border-top-color: var(--dees-color-text-primary);
|
|
76
|
+
border-top-color: var(--dees-spinner-color, var(--dees-color-text-primary));
|
|
68
77
|
animation: spin 1s ease-in-out infinite;
|
|
69
78
|
-webkit-animation: spin 1s ease-in-out infinite;
|
|
70
79
|
}
|
|
@@ -47,6 +47,10 @@ export class DeesFormSubmit extends DeesElement {
|
|
|
47
47
|
@property({ type: String })
|
|
48
48
|
accessor iconPosition: 'left' | 'right' = 'left';
|
|
49
49
|
|
|
50
|
+
/** stretches the button face to the full host width — see dees-button.fullWidth */
|
|
51
|
+
@property({ type: Boolean, reflect: true, attribute: 'full-width' })
|
|
52
|
+
accessor fullWidth: boolean = false;
|
|
53
|
+
|
|
50
54
|
constructor() {
|
|
51
55
|
super();
|
|
52
56
|
}
|
|
@@ -58,6 +62,12 @@ export class DeesFormSubmit extends DeesElement {
|
|
|
58
62
|
:host {
|
|
59
63
|
display: inline-block;
|
|
60
64
|
}
|
|
65
|
+
/* The host has to stretch too, or forwarding fullWidth only widens the face inside a
|
|
66
|
+
still shrink-wrapped host and the property looks broken. */
|
|
67
|
+
:host([full-width]) {
|
|
68
|
+
display: block;
|
|
69
|
+
width: 100%;
|
|
70
|
+
}
|
|
61
71
|
dees-button {
|
|
62
72
|
width: 100%;
|
|
63
73
|
}
|
|
@@ -73,6 +83,7 @@ export class DeesFormSubmit extends DeesElement {
|
|
|
73
83
|
.icon=${this.icon}
|
|
74
84
|
.iconPosition=${this.iconPosition}
|
|
75
85
|
.text=${this.text}
|
|
86
|
+
.fullWidth=${this.fullWidth}
|
|
76
87
|
?disabled=${this.disabled}
|
|
77
88
|
@clicked=${this.submit}
|
|
78
89
|
></dees-button>
|
|
@@ -103,6 +114,13 @@ export class DeesFormSubmit extends DeesElement {
|
|
|
103
114
|
}
|
|
104
115
|
}
|
|
105
116
|
|
|
117
|
+
/**
|
|
118
|
+
* NOT a focus method despite the name — it submits. `dees-form.addBehaviours()` calls
|
|
119
|
+
* `getSubmitButton()?.focus()` when Enter is pressed in the last field, and that is the
|
|
120
|
+
* catalog's "Enter submits the form" behaviour. Renaming it would break every form, so it
|
|
121
|
+
* stays; keyboard *focus* reaches the submit through the inner dees-button face, which
|
|
122
|
+
* carries its own tabindex.
|
|
123
|
+
*/
|
|
106
124
|
public async focus() {
|
|
107
125
|
const domtools = await this.domtoolsPromise;
|
|
108
126
|
if (!this.disabled) {
|
|
@@ -67,6 +67,16 @@ export class DeesInputText extends DeesInputBase {
|
|
|
67
67
|
@property({ attribute: false })
|
|
68
68
|
accessor validConfirmed: boolean = false;
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* autofill hint forwarded to the inner input element.
|
|
72
|
+
* Empty by default, which renders no `autocomplete` attribute at all — byte-identical to
|
|
73
|
+
* this component before the property existed. Set it to a concrete token list to help
|
|
74
|
+
* password managers (`'username'`, `'current-password'`, `'one-time-code'`) or to opt an
|
|
75
|
+
* input into WebAuthn conditional mediation (`'username webauthn'`).
|
|
76
|
+
*/
|
|
77
|
+
@property({ type: String })
|
|
78
|
+
accessor autocomplete: string = '';
|
|
79
|
+
|
|
70
80
|
private validTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
71
81
|
|
|
72
82
|
public static styles = [
|
|
@@ -324,6 +334,25 @@ export class DeesInputText extends DeesInputBase {
|
|
|
324
334
|
`;
|
|
325
335
|
}
|
|
326
336
|
|
|
337
|
+
/**
|
|
338
|
+
* `autocomplete` is applied imperatively rather than bound in the template so that the
|
|
339
|
+
* default renders no attribute at all. Binding it would emit `autocomplete=""` on every
|
|
340
|
+
* text input in the catalog, and an empty or `on` value is not universally equivalent to
|
|
341
|
+
* an absent one across browser and password-manager heuristics.
|
|
342
|
+
*/
|
|
343
|
+
public updated(_changedProperties: Map<string, any>): void {
|
|
344
|
+
super.updated(_changedProperties);
|
|
345
|
+
const input = this.shadowRoot?.querySelector('input');
|
|
346
|
+
if (!input) {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
if (this.autocomplete) {
|
|
350
|
+
input.setAttribute('autocomplete', this.autocomplete);
|
|
351
|
+
} else {
|
|
352
|
+
input.removeAttribute('autocomplete');
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
327
356
|
firstUpdated() {
|
|
328
357
|
if (this.validationFunction && this.value) {
|
|
329
358
|
const result = this.validationFunction(this.value);
|
|
@@ -1,4 +1,420 @@
|
|
|
1
|
-
import { html } from '@design.estate/dees-element';
|
|
1
|
+
import { html, css, cssManager, customElement, state, DeesElement } from '@design.estate/dees-element';
|
|
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';
|
|
10
|
+
|
|
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[] = [
|
|
16
|
+
{ 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
|
+
];
|
|
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 },
|
|
26
|
+
];
|
|
27
|
+
|
|
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;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private note(messageArg: string): void {
|
|
215
|
+
this.log = [messageArg, ...this.log].slice(0, 12);
|
|
216
|
+
}
|
|
217
|
+
|
|
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
|
+
`;
|
|
247
|
+
}
|
|
248
|
+
|
|
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>
|
|
254
|
+
</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
|
+
`;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
2
418
|
|
|
3
419
|
export const demoFunc = () => html`
|
|
4
420
|
<style>
|
|
@@ -15,23 +431,6 @@ export const demoFunc = () => html`
|
|
|
15
431
|
}
|
|
16
432
|
</style>
|
|
17
433
|
<div class="demo-container">
|
|
18
|
-
<dees-simple-login
|
|
19
|
-
name="My Application"
|
|
20
|
-
@login=${(e: CustomEvent) => {
|
|
21
|
-
console.log('Login event received:', e.detail);
|
|
22
|
-
const loginData = e.detail?.data || e.detail;
|
|
23
|
-
if (loginData?.username && loginData?.password) {
|
|
24
|
-
alert(`Login attempted with:\nUsername: ${loginData.username}\nPassword: ${loginData.password}`);
|
|
25
|
-
// Here you would typically validate credentials and show the slotted content
|
|
26
|
-
} else {
|
|
27
|
-
console.error('Invalid login data structure:', e.detail);
|
|
28
|
-
}
|
|
29
|
-
}}
|
|
30
|
-
>
|
|
31
|
-
<div style="padding: 40px; text-align: center;">
|
|
32
|
-
<h1>Welcome!</h1>
|
|
33
|
-
<p>This is the slotted content that appears after login.</p>
|
|
34
|
-
</div>
|
|
35
|
-
</dees-simple-login>
|
|
434
|
+
<dees-simple-login-demo></dees-simple-login-demo>
|
|
36
435
|
</div>
|
|
37
436
|
`;
|