@idp.global/catalog 2.0.0 → 2.1.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 (70) hide show
  1. package/changelog.md +15 -0
  2. package/dist_bundle/bundle.js +4428 -1496
  3. package/dist_ts_web/00_commitinfo_data.js +1 -1
  4. package/dist_ts_web/elements/idp-admin-overview.d.ts +28 -0
  5. package/dist_ts_web/elements/idp-admin-overview.js +460 -0
  6. package/dist_ts_web/elements/idp-admin-shared.d.ts +18 -0
  7. package/dist_ts_web/elements/idp-admin-shared.js +404 -0
  8. package/dist_ts_web/elements/idp-admin-shell.d.ts +96 -11
  9. package/dist_ts_web/elements/idp-admin-shell.js +442 -816
  10. package/dist_ts_web/elements/idp-admin-sidebar.d.ts +42 -0
  11. package/dist_ts_web/elements/idp-admin-sidebar.js +700 -0
  12. package/dist_ts_web/elements/idp-app-pair.d.ts +19 -0
  13. package/dist_ts_web/elements/idp-app-pair.js +187 -0
  14. package/dist_ts_web/elements/idp-auth-card.d.ts +17 -0
  15. package/dist_ts_web/elements/idp-auth-card.js +180 -0
  16. package/dist_ts_web/elements/idp-avatar.d.ts +17 -0
  17. package/dist_ts_web/elements/idp-avatar.js +151 -0
  18. package/dist_ts_web/elements/idp-badge.d.ts +2 -0
  19. package/dist_ts_web/elements/idp-badge.js +12 -3
  20. package/dist_ts_web/elements/idp-brand-mark.d.ts +16 -0
  21. package/dist_ts_web/elements/idp-brand-mark.js +139 -0
  22. package/dist_ts_web/elements/idp-button.d.ts +3 -0
  23. package/dist_ts_web/elements/idp-button.js +47 -3
  24. package/dist_ts_web/elements/idp-consent-card.d.ts +34 -0
  25. package/dist_ts_web/elements/idp-consent-card.js +217 -0
  26. package/dist_ts_web/elements/idp-delegation-section.d.ts +59 -0
  27. package/dist_ts_web/elements/idp-delegation-section.js +609 -0
  28. package/dist_ts_web/elements/idp-form-submit.d.ts +2 -1
  29. package/dist_ts_web/elements/idp-form-submit.js +11 -2
  30. package/dist_ts_web/elements/idp-form.js +2 -2
  31. package/dist_ts_web/elements/idp-input.d.ts +4 -0
  32. package/dist_ts_web/elements/idp-input.js +68 -15
  33. package/dist_ts_web/elements/idp-option-row.d.ts +25 -0
  34. package/dist_ts_web/elements/idp-option-row.js +267 -0
  35. package/dist_ts_web/elements/idp-otp-input.d.ts +32 -0
  36. package/dist_ts_web/elements/idp-otp-input.js +274 -0
  37. package/dist_ts_web/elements/idp-progress-bar.d.ts +14 -0
  38. package/dist_ts_web/elements/idp-progress-bar.js +127 -0
  39. package/dist_ts_web/elements/idp-signed-in-pill.d.ts +15 -0
  40. package/dist_ts_web/elements/idp-signed-in-pill.js +113 -0
  41. package/dist_ts_web/elements/idp-spinner.d.ts +13 -0
  42. package/dist_ts_web/elements/idp-spinner.js +109 -0
  43. package/dist_ts_web/elements/idp-status-icon.d.ts +18 -0
  44. package/dist_ts_web/elements/idp-status-icon.js +136 -0
  45. package/dist_ts_web/elements/index.d.ts +15 -0
  46. package/dist_ts_web/elements/index.js +16 -1
  47. package/package.json +1 -1
  48. package/ts_web/00_commitinfo_data.ts +1 -1
  49. package/ts_web/elements/idp-admin-overview.ts +388 -0
  50. package/ts_web/elements/idp-admin-shared.ts +419 -0
  51. package/ts_web/elements/idp-admin-shell.ts +514 -871
  52. package/ts_web/elements/idp-admin-sidebar.ts +646 -0
  53. package/ts_web/elements/idp-app-pair.ts +120 -0
  54. package/ts_web/elements/idp-auth-card.ts +113 -0
  55. package/ts_web/elements/idp-avatar.ts +84 -0
  56. package/ts_web/elements/idp-badge.ts +5 -1
  57. package/ts_web/elements/idp-brand-mark.ts +76 -0
  58. package/ts_web/elements/idp-button.ts +36 -1
  59. package/ts_web/elements/idp-consent-card.ts +146 -0
  60. package/ts_web/elements/idp-delegation-section.ts +576 -0
  61. package/ts_web/elements/idp-form-submit.ts +5 -1
  62. package/ts_web/elements/idp-form.ts +1 -1
  63. package/ts_web/elements/idp-input.ts +58 -13
  64. package/ts_web/elements/idp-option-row.ts +184 -0
  65. package/ts_web/elements/idp-otp-input.ts +204 -0
  66. package/ts_web/elements/idp-progress-bar.ts +69 -0
  67. package/ts_web/elements/idp-signed-in-pill.ts +55 -0
  68. package/ts_web/elements/idp-spinner.ts +56 -0
  69. package/ts_web/elements/idp-status-icon.ts +76 -0
  70. package/ts_web/elements/index.ts +15 -0
@@ -0,0 +1,576 @@
1
+ import { DeesElement, html, property, state, customElement, css, type TemplateResult } from '@design.estate/dees-element';
2
+ import { idpElementStyles } from './tokens.js';
3
+ import './idp-avatar.js';
4
+ import './idp-button.js';
5
+ import './idp-icon.js';
6
+
7
+ export interface IIdpDelegationPermission {
8
+ id: string;
9
+ label: string;
10
+ hint: string;
11
+ }
12
+
13
+ export interface IIdpDelegationApp {
14
+ id: string;
15
+ name: string;
16
+ perms: IIdpDelegationPermission[];
17
+ }
18
+
19
+ export interface IIdpDelegation {
20
+ id: string;
21
+ account: {
22
+ name: string;
23
+ email: string;
24
+ };
25
+ grants: Record<string, string[]>;
26
+ expiresAt: number | null;
27
+ createdAt: number;
28
+ }
29
+
30
+ export interface IIdpDelegationGrantEventDetail {
31
+ email: string;
32
+ grants: Record<string, string[]>;
33
+ expiryDays: number | null;
34
+ }
35
+
36
+ export interface IIdpDelegationRevokeEventDetail {
37
+ delegationId: string;
38
+ }
39
+
40
+ const demoApps: IIdpDelegationApp[] = [
41
+ {
42
+ id: 'foss',
43
+ name: 'foss.global',
44
+ perms: [
45
+ { id: 'projects:read', label: 'View projects', hint: 'Read-only access to your projects' },
46
+ { id: 'projects:write', label: 'Manage projects', hint: 'Create, edit and archive projects' },
47
+ { id: 'billing:read', label: 'View billing', hint: 'See invoices and payment history' },
48
+ ],
49
+ },
50
+ {
51
+ id: 'task',
52
+ name: 'task.vc',
53
+ perms: [
54
+ { id: 'tasks:read', label: 'View tasks', hint: 'See your tasks and boards' },
55
+ { id: 'tasks:write', label: 'Act on tasks', hint: 'Create, assign and complete tasks as you' },
56
+ ],
57
+ },
58
+ ];
59
+
60
+ const demoDelegations: IIdpDelegation[] = [
61
+ {
62
+ id: 'd1',
63
+ account: { name: 'Jordan Kim', email: 'jordan@lossless.com' },
64
+ grants: { task: ['tasks:read', 'tasks:write'] },
65
+ expiresAt: Date.now() + 86400000 * 24,
66
+ createdAt: Date.now() - 86400000 * 6,
67
+ },
68
+ {
69
+ id: 'd2',
70
+ account: { name: 'Sam Rivera', email: 'sam@lossless.com' },
71
+ grants: { foss: ['billing:read'] },
72
+ expiresAt: null,
73
+ createdAt: Date.now() - 86400000 * 40,
74
+ },
75
+ ];
76
+
77
+ declare global {
78
+ interface HTMLElementTagNameMap {
79
+ 'idp-delegation-section': IdpDelegationSection;
80
+ }
81
+ }
82
+
83
+ @customElement('idp-delegation-section')
84
+ export class IdpDelegationSection extends DeesElement {
85
+ public static demo = () => html`
86
+ <div style="max-width: 640px;">
87
+ <idp-delegation-section .apps=${demoApps} .delegations=${demoDelegations}></idp-delegation-section>
88
+ </div>
89
+ `;
90
+ public static demoGroups = ['idp.global v5 composed surfaces'];
91
+
92
+ @property({ type: Array })
93
+ public accessor apps: IIdpDelegationApp[] = [];
94
+
95
+ @property({ type: Array })
96
+ public accessor delegations: IIdpDelegation[] = [];
97
+
98
+ @state()
99
+ private accessor grantOpen = false;
100
+
101
+ @state()
102
+ private accessor grantEmail = '';
103
+
104
+ @state()
105
+ private accessor grantSelections: Record<string, string[]> = {};
106
+
107
+ @state()
108
+ private accessor grantExpiry = '30';
109
+
110
+ public static styles = [
111
+ ...idpElementStyles,
112
+ css`
113
+ :host {
114
+ display: block;
115
+ font-family: var(--idp-font);
116
+ color: var(--idp-fg);
117
+ }
118
+ .section {
119
+ background: var(--idp-card);
120
+ border: 1px solid var(--idp-border);
121
+ border-radius: var(--idp-radius);
122
+ padding: 20px;
123
+ }
124
+ .headline {
125
+ display: flex;
126
+ align-items: flex-start;
127
+ justify-content: space-between;
128
+ gap: 16px;
129
+ margin-bottom: 16px;
130
+ }
131
+ .title {
132
+ font-size: 14px;
133
+ font-weight: 600;
134
+ }
135
+ .muted {
136
+ font-size: 12.5px;
137
+ color: var(--idp-muted-fg);
138
+ line-height: 1.5;
139
+ margin-top: 3px;
140
+ }
141
+ .row {
142
+ padding: 12px 0;
143
+ border-bottom: 1px solid var(--idp-border);
144
+ }
145
+ .row:last-of-type {
146
+ border-bottom: none;
147
+ }
148
+ .row-head {
149
+ display: flex;
150
+ align-items: center;
151
+ gap: 10px;
152
+ }
153
+ .row-name {
154
+ font-size: 13px;
155
+ font-weight: 500;
156
+ }
157
+ .row-email {
158
+ font-size: 11px;
159
+ color: var(--idp-muted-fg);
160
+ }
161
+ .row-expiry {
162
+ font-size: 11px;
163
+ color: var(--idp-muted-fg);
164
+ white-space: nowrap;
165
+ }
166
+ .revoke {
167
+ background: none;
168
+ border: none;
169
+ cursor: pointer;
170
+ font-family: inherit;
171
+ font-size: 11px;
172
+ font-weight: 500;
173
+ color: var(--idp-destructive);
174
+ padding: 4px 8px;
175
+ border-radius: 6px;
176
+ }
177
+ .revoke:hover {
178
+ background: var(--idp-error-bg);
179
+ }
180
+ .grant-chips {
181
+ display: flex;
182
+ flex-wrap: wrap;
183
+ gap: 6px;
184
+ margin: 8px 0 0 40px;
185
+ }
186
+ .grant-chip {
187
+ display: flex;
188
+ align-items: center;
189
+ gap: 6px;
190
+ border: 1px solid var(--idp-border);
191
+ border-radius: 7px;
192
+ padding: 3px 8px 3px 4px;
193
+ background: var(--idp-muted);
194
+ }
195
+ .grant-chip-tile {
196
+ width: 18px;
197
+ height: 18px;
198
+ border-radius: 4px;
199
+ background: var(--idp-accent-soft);
200
+ display: flex;
201
+ align-items: center;
202
+ justify-content: center;
203
+ font-size: 8px;
204
+ font-weight: 700;
205
+ color: var(--idp-accent);
206
+ flex-shrink: 0;
207
+ }
208
+ .grant-chip-name {
209
+ font-size: 11px;
210
+ font-weight: 500;
211
+ }
212
+ .grant-chip-perms {
213
+ font-size: 10.5px;
214
+ color: var(--idp-muted-fg);
215
+ font-family: var(--idp-mono);
216
+ }
217
+ .empty {
218
+ font-size: 12.5px;
219
+ color: var(--idp-muted-fg);
220
+ padding: 4px 0;
221
+ }
222
+ .backdrop {
223
+ position: fixed;
224
+ inset: 0;
225
+ background: rgba(0, 0, 0, 0.4);
226
+ display: flex;
227
+ align-items: center;
228
+ justify-content: center;
229
+ z-index: 9990;
230
+ }
231
+ .modal {
232
+ width: 460px;
233
+ max-width: calc(100vw - 32px);
234
+ max-height: calc(100vh - 80px);
235
+ overflow: auto;
236
+ background: var(--idp-card);
237
+ border: 1px solid var(--idp-border);
238
+ border-radius: 12px;
239
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
240
+ }
241
+ .modal-head {
242
+ display: flex;
243
+ align-items: center;
244
+ justify-content: space-between;
245
+ padding: 16px 20px;
246
+ border-bottom: 1px solid var(--idp-border);
247
+ font-size: 15px;
248
+ font-weight: 600;
249
+ }
250
+ .modal-close {
251
+ background: none;
252
+ border: none;
253
+ cursor: pointer;
254
+ color: var(--idp-muted-fg);
255
+ display: flex;
256
+ padding: 2px;
257
+ }
258
+ .modal-body {
259
+ padding: 20px;
260
+ }
261
+ .field-label {
262
+ font-size: 12px;
263
+ font-weight: 500;
264
+ margin-bottom: 4px;
265
+ }
266
+ .field-label .req {
267
+ color: var(--idp-destructive);
268
+ }
269
+ input[type='email'] {
270
+ width: 100%;
271
+ height: 34px;
272
+ box-sizing: border-box;
273
+ padding: 0 10px;
274
+ font-size: 13px;
275
+ font-family: inherit;
276
+ color: var(--idp-fg);
277
+ background: var(--idp-bg);
278
+ border: 1px solid var(--idp-border);
279
+ border-radius: 8px;
280
+ outline: none;
281
+ }
282
+ input[type='email']:focus {
283
+ border-color: var(--idp-accent);
284
+ }
285
+ .apps-box {
286
+ border: 1px solid var(--idp-border);
287
+ border-radius: 10px;
288
+ overflow: hidden;
289
+ margin-bottom: 14px;
290
+ }
291
+ .app-row {
292
+ border-bottom: 1px solid var(--idp-border);
293
+ }
294
+ .app-row:last-child {
295
+ border-bottom: none;
296
+ }
297
+ .app-head {
298
+ display: flex;
299
+ align-items: center;
300
+ gap: 10px;
301
+ padding: 10px 12px;
302
+ cursor: pointer;
303
+ }
304
+ .app-head.enabled {
305
+ background: var(--idp-muted);
306
+ }
307
+ .app-tile {
308
+ width: 22px;
309
+ height: 22px;
310
+ border-radius: 6px;
311
+ background: var(--idp-accent-soft);
312
+ border: 1px solid var(--idp-border);
313
+ display: flex;
314
+ align-items: center;
315
+ justify-content: center;
316
+ font-size: 9px;
317
+ font-weight: 700;
318
+ color: var(--idp-accent);
319
+ flex-shrink: 0;
320
+ }
321
+ .app-name {
322
+ flex: 1;
323
+ font-size: 13px;
324
+ font-weight: 500;
325
+ }
326
+ .app-count {
327
+ font-size: 10.5px;
328
+ color: var(--idp-muted-fg);
329
+ }
330
+ .perm-list {
331
+ display: flex;
332
+ flex-direction: column;
333
+ gap: 2px;
334
+ padding: 2px 12px 10px 36px;
335
+ }
336
+ .perm-row {
337
+ display: flex;
338
+ align-items: flex-start;
339
+ gap: 9px;
340
+ padding: 5px 0;
341
+ cursor: pointer;
342
+ }
343
+ .perm-label {
344
+ font-size: 12.5px;
345
+ }
346
+ .perm-hint {
347
+ display: block;
348
+ font-size: 11px;
349
+ color: var(--idp-muted-fg);
350
+ }
351
+ .perm-id {
352
+ font-size: 10px;
353
+ color: var(--idp-muted-fg);
354
+ font-family: var(--idp-mono);
355
+ margin-top: 2px;
356
+ }
357
+ input[type='checkbox'] {
358
+ accent-color: var(--idp-accent);
359
+ margin: 2px 0 0;
360
+ cursor: pointer;
361
+ }
362
+ .modal-actions {
363
+ display: flex;
364
+ gap: 8px;
365
+ justify-content: flex-end;
366
+ margin-top: 18px;
367
+ }
368
+ .expiry-select {
369
+ width: 100%;
370
+ height: 34px;
371
+ padding: 0 10px;
372
+ font-size: 13px;
373
+ font-family: inherit;
374
+ color: var(--idp-fg);
375
+ background: var(--idp-bg);
376
+ border: 1px solid var(--idp-border);
377
+ border-radius: 8px;
378
+ outline: none;
379
+ cursor: pointer;
380
+ }
381
+ `,
382
+ ];
383
+
384
+ private expiryLabel(expiresAtArg: number | null): string {
385
+ if (!expiresAtArg) {
386
+ return 'Until revoked';
387
+ }
388
+ const days = Math.max(0, Math.round((expiresAtArg - Date.now()) / 86400000));
389
+ return days === 0 ? 'Expires today' : `Expires in ${days}d`;
390
+ }
391
+
392
+ public render(): TemplateResult {
393
+ return html`
394
+ <section class="section">
395
+ <div class="headline">
396
+ <div>
397
+ <div class="title">Delegated access</div>
398
+ <div class="muted">Let other idp.global accounts act on your behalf — scoped to specific apps and app-specific permissions.</div>
399
+ </div>
400
+ <idp-button variant="outline" size="sm" @click=${() => this.openGrant()}>Delegate access</idp-button>
401
+ </div>
402
+ ${this.delegations.length
403
+ ? this.delegations.map((delegationArg) => this.renderDelegationRow(delegationArg))
404
+ : html`<div class="empty">No delegations yet. Access you grant here can be revoked at any time.</div>`}
405
+ </section>
406
+ ${this.grantOpen ? this.renderGrantModal() : html``}
407
+ `;
408
+ }
409
+
410
+ private renderDelegationRow(delegationArg: IIdpDelegation): TemplateResult {
411
+ const grantedApps = Object.keys(delegationArg.grants);
412
+ return html`
413
+ <div class="row">
414
+ <div class="row-head">
415
+ <idp-avatar name=${delegationArg.account.name} size="30"></idp-avatar>
416
+ <div style="flex:1;min-width:0">
417
+ <div class="row-name">${delegationArg.account.name}</div>
418
+ <div class="row-email">${delegationArg.account.email}</div>
419
+ </div>
420
+ <span class="row-expiry">${this.expiryLabel(delegationArg.expiresAt)}</span>
421
+ <button class="revoke" @click=${() => this.revoke(delegationArg.id)}>Revoke</button>
422
+ </div>
423
+ <div class="grant-chips">
424
+ ${grantedApps.map((appIdArg) => {
425
+ const app = this.apps.find((appArg) => appArg.id === appIdArg);
426
+ if (!app) {
427
+ return html``;
428
+ }
429
+ return html`
430
+ <span class="grant-chip">
431
+ <span class="grant-chip-tile">${app.name.slice(0, 2).toUpperCase()}</span>
432
+ <span class="grant-chip-name">${app.name}</span>
433
+ <span class="grant-chip-perms">${delegationArg.grants[appIdArg].join(' · ')}</span>
434
+ </span>
435
+ `;
436
+ })}
437
+ </div>
438
+ </div>
439
+ `;
440
+ }
441
+
442
+ private renderGrantModal(): TemplateResult {
443
+ const valid = /.+@.+\..+/.test(this.grantEmail)
444
+ && Object.values(this.grantSelections).some((permsArg) => permsArg.length > 0);
445
+ return html`
446
+ <div class="backdrop" @click=${(eventArg: Event) => { if (eventArg.target === eventArg.currentTarget) this.closeGrant(); }}>
447
+ <div class="modal">
448
+ <div class="modal-head">
449
+ <span>Delegate access</span>
450
+ <button class="modal-close" @click=${() => this.closeGrant()}><idp-icon name="x" size="16"></idp-icon></button>
451
+ </div>
452
+ <div class="modal-body">
453
+ <div class="muted" style="margin: 0 0 14px;">
454
+ The delegate signs in with their own idp.global account and can act on your behalf — only in the apps and permissions you pick here. They never see your credentials.
455
+ </div>
456
+ <div style="margin-bottom: 14px;">
457
+ <div class="field-label">Account email <span class="req">*</span></div>
458
+ <input
459
+ type="email"
460
+ placeholder="colleague@company.com"
461
+ .value=${this.grantEmail}
462
+ @input=${(eventArg: Event) => { this.grantEmail = (eventArg.target as HTMLInputElement).value; }}
463
+ />
464
+ </div>
465
+ <div class="field-label">Apps &amp; permissions <span class="req">*</span></div>
466
+ <div class="apps-box">
467
+ ${this.apps.map((appArg) => this.renderGrantAppRow(appArg))}
468
+ </div>
469
+ <div style="margin-bottom: 18px;">
470
+ <div class="field-label">Access expires</div>
471
+ <select class="expiry-select" .value=${this.grantExpiry} @change=${(eventArg: Event) => { this.grantExpiry = (eventArg.target as HTMLSelectElement).value; }}>
472
+ <option value="7">7 days</option>
473
+ <option value="30">30 days</option>
474
+ <option value="90">90 days</option>
475
+ <option value="">Until revoked</option>
476
+ </select>
477
+ </div>
478
+ <div class="modal-actions">
479
+ <idp-button variant="outline" size="sm" @click=${() => this.closeGrant()}>Cancel</idp-button>
480
+ <idp-button variant="accent" size="sm" ?disabled=${!valid} @click=${() => this.submitGrant()}>Grant access</idp-button>
481
+ </div>
482
+ </div>
483
+ </div>
484
+ </div>
485
+ `;
486
+ }
487
+
488
+ private renderGrantAppRow(appArg: IIdpDelegationApp): TemplateResult {
489
+ const enabled = !!this.grantSelections[appArg.id];
490
+ const selected = this.grantSelections[appArg.id] || [];
491
+ return html`
492
+ <div class="app-row">
493
+ <label class="app-head ${enabled ? 'enabled' : ''}">
494
+ <input type="checkbox" .checked=${enabled} @change=${() => this.toggleGrantApp(appArg.id)} />
495
+ <span class="app-tile">${appArg.name.slice(0, 2).toUpperCase()}</span>
496
+ <span class="app-name">${appArg.name}</span>
497
+ ${enabled ? html`<span class="app-count">${selected.length} of ${appArg.perms.length} permissions</span>` : html``}
498
+ </label>
499
+ ${enabled
500
+ ? html`
501
+ <div class="perm-list">
502
+ ${appArg.perms.map(
503
+ (permArg) => html`
504
+ <label class="perm-row">
505
+ <input
506
+ type="checkbox"
507
+ .checked=${selected.includes(permArg.id)}
508
+ @change=${() => this.toggleGrantPerm(appArg.id, permArg.id)}
509
+ />
510
+ <span style="flex:1">
511
+ <span class="perm-label">${permArg.label}</span>
512
+ <span class="perm-hint">${permArg.hint}</span>
513
+ </span>
514
+ <code class="perm-id">${permArg.id}</code>
515
+ </label>
516
+ `
517
+ )}
518
+ </div>
519
+ `
520
+ : html``}
521
+ </div>
522
+ `;
523
+ }
524
+
525
+ private openGrant() {
526
+ this.grantEmail = '';
527
+ this.grantSelections = {};
528
+ this.grantExpiry = '30';
529
+ this.grantOpen = true;
530
+ }
531
+
532
+ private closeGrant() {
533
+ this.grantOpen = false;
534
+ }
535
+
536
+ private toggleGrantApp(appIdArg: string) {
537
+ const next = { ...this.grantSelections };
538
+ if (next[appIdArg]) {
539
+ delete next[appIdArg];
540
+ } else {
541
+ next[appIdArg] = [];
542
+ }
543
+ this.grantSelections = next;
544
+ }
545
+
546
+ private toggleGrantPerm(appIdArg: string, permIdArg: string) {
547
+ const current = this.grantSelections[appIdArg] || [];
548
+ this.grantSelections = {
549
+ ...this.grantSelections,
550
+ [appIdArg]: current.includes(permIdArg)
551
+ ? current.filter((permArg) => permArg !== permIdArg)
552
+ : [...current, permIdArg],
553
+ };
554
+ }
555
+
556
+ private submitGrant() {
557
+ const grants: Record<string, string[]> = {};
558
+ for (const [appId, perms] of Object.entries(this.grantSelections)) {
559
+ if (perms.length) {
560
+ grants[appId] = perms;
561
+ }
562
+ }
563
+ const detail: IIdpDelegationGrantEventDetail = {
564
+ email: this.grantEmail,
565
+ grants,
566
+ expiryDays: this.grantExpiry ? parseInt(this.grantExpiry, 10) : null,
567
+ };
568
+ this.dispatchEvent(new CustomEvent('idp-delegation-grant', { detail, bubbles: true, composed: true }));
569
+ this.closeGrant();
570
+ }
571
+
572
+ private revoke(delegationIdArg: string) {
573
+ const detail: IIdpDelegationRevokeEventDetail = { delegationId: delegationIdArg };
574
+ this.dispatchEvent(new CustomEvent('idp-delegation-revoke', { detail, bubbles: true, composed: true }));
575
+ }
576
+ }
@@ -1,7 +1,7 @@
1
1
  import { DeesElement, html, property, customElement, css, type TemplateResult } from '@design.estate/dees-element';
2
2
  import { idpElementStyles } from './tokens.js';
3
3
  import './idp-button.js';
4
- import type { TIdpButtonSize, TIdpButtonVariant } from './idp-button.js';
4
+ import type { TIdpButtonShape, TIdpButtonSize, TIdpButtonVariant } from './idp-button.js';
5
5
 
6
6
  declare global {
7
7
  interface HTMLElementTagNameMap {
@@ -23,6 +23,9 @@ export class IdpFormSubmit extends DeesElement {
23
23
  @property({ type: String })
24
24
  public accessor size: TIdpButtonSize = 'md';
25
25
 
26
+ @property({ type: String })
27
+ public accessor shape: TIdpButtonShape = 'pill';
28
+
26
29
  @property({ type: Boolean, reflect: true })
27
30
  public accessor disabled = false;
28
31
 
@@ -56,6 +59,7 @@ export class IdpFormSubmit extends DeesElement {
56
59
  <idp-button
57
60
  variant=${this.variant}
58
61
  size=${this.size}
62
+ shape=${this.shape}
59
63
  .disabled=${this.disabled || this.loading}
60
64
  .loading=${this.loading}
61
65
  @click=${this.handleClick}
@@ -96,7 +96,7 @@ export class IdpForm extends DeesElement {
96
96
  }
97
97
 
98
98
  private getControls() {
99
- return Array.from(this.querySelectorAll('idp-input, idp-checkbox')) as TFormControl[];
99
+ return Array.from(this.querySelectorAll('idp-input, idp-checkbox, idp-otp-input')) as TFormControl[];
100
100
  }
101
101
 
102
102
  private validateControls() {