@idp.global/catalog 1.2.1 → 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 +21 -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
@@ -1,5 +1,8 @@
1
1
  import { DeesElement, html, property, customElement, css, type TemplateResult } from '@design.estate/dees-element';
2
2
  import { idpElementStyles } from './tokens.js';
3
+ import './idp-icon.js';
4
+
5
+ export type TIdpInputSize = 'md' | 'lg';
3
6
 
4
7
  declare global {
5
8
  interface HTMLElementTagNameMap {
@@ -9,8 +12,14 @@ declare global {
9
12
 
10
13
  @customElement('idp-input')
11
14
  export class IdpInput extends DeesElement {
12
- public static demo = () => html`<idp-input label="Email" placeholder="user@example.com"></idp-input>`;
13
- public static demoGroups = ['idp.global v3 primitives'];
15
+ public static demo = () => html`
16
+ <div style="display: flex; flex-direction: column; gap: 14px; max-width: 340px;">
17
+ <idp-input label="Email" placeholder="user@example.com"></idp-input>
18
+ <idp-input size="lg" icon="mail" placeholder="you@example.com"></idp-input>
19
+ <idp-input size="lg" icon="lock" type="password" placeholder="Password"></idp-input>
20
+ </div>
21
+ `;
22
+ public static demoGroups = ['idp.global v5 primitives'];
14
23
 
15
24
  @property({ type: String })
16
25
  public accessor label = '';
@@ -45,6 +54,12 @@ export class IdpInput extends DeesElement {
45
54
  @property({ type: Boolean, reflect: true })
46
55
  public accessor disabled = false;
47
56
 
57
+ @property({ type: String, reflect: true })
58
+ public accessor size: TIdpInputSize = 'md';
59
+
60
+ @property({ type: String })
61
+ public accessor icon = '';
62
+
48
63
  public static styles = [
49
64
  ...idpElementStyles,
50
65
  css`
@@ -85,6 +100,33 @@ export class IdpInput extends DeesElement {
85
100
  border-color: var(--idp-destructive);
86
101
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--idp-destructive), transparent 86%);
87
102
  }
103
+ .field {
104
+ position: relative;
105
+ display: flex;
106
+ align-items: center;
107
+ }
108
+ .field idp-icon {
109
+ position: absolute;
110
+ left: 13px;
111
+ color: var(--idp-fg-3);
112
+ pointer-events: none;
113
+ }
114
+ :host([size='lg']) input {
115
+ height: 44px;
116
+ border-width: 1.5px;
117
+ border-radius: 12px;
118
+ padding: 0 14px;
119
+ font-size: 14px;
120
+ }
121
+ :host([size='lg']) .field idp-icon + input {
122
+ padding-left: 40px;
123
+ }
124
+ :host([size='md']) .field idp-icon + input {
125
+ padding-left: 34px;
126
+ }
127
+ :host([size='lg']) input:focus {
128
+ box-shadow: 0 0 0 3px var(--sh-accent-ring);
129
+ }
88
130
  .hint,
89
131
  .error {
90
132
  color: var(--idp-muted-fg);
@@ -133,17 +175,20 @@ export class IdpInput extends DeesElement {
133
175
  return html`
134
176
  <label>
135
177
  ${this.label ? html`<span class="label">${this.label}</span>` : html``}
136
- <input
137
- .value=${this.value}
138
- name=${this.name || this.key}
139
- type=${this.type}
140
- placeholder=${this.placeholder}
141
- autocomplete=${this.autocomplete}
142
- ?required=${this.required}
143
- ?disabled=${this.disabled}
144
- aria-invalid=${this.error ? 'true' : 'false'}
145
- @input=${this.handleInput}
146
- />
178
+ <span class="field">
179
+ ${this.icon ? html`<idp-icon name=${this.icon as any} size=${this.size === 'lg' ? 17 : 14}></idp-icon>` : html``}
180
+ <input
181
+ .value=${this.value}
182
+ name=${this.name || this.key}
183
+ type=${this.type}
184
+ placeholder=${this.placeholder}
185
+ autocomplete=${this.autocomplete}
186
+ ?required=${this.required}
187
+ ?disabled=${this.disabled}
188
+ aria-invalid=${this.error ? 'true' : 'false'}
189
+ @input=${this.handleInput}
190
+ />
191
+ </span>
147
192
  ${this.error ? html`<span class="error">${this.error}</span>` : this.hint ? html`<span class="hint">${this.hint}</span>` : html``}
148
193
  </label>
149
194
  `;
@@ -0,0 +1,184 @@
1
+ import { DeesElement, html, property, customElement, css, type TemplateResult } from '@design.estate/dees-element';
2
+ import { idpElementStyles } from './tokens.js';
3
+ import './idp-icon.js';
4
+
5
+ export type TIdpOptionRowTrailing = 'chevron' | 'check' | 'none';
6
+ export type TIdpOptionRowTileTone = 'default' | 'accent';
7
+
8
+ declare global {
9
+ interface HTMLElementTagNameMap {
10
+ 'idp-option-row': IdpOptionRow;
11
+ }
12
+ }
13
+
14
+ @customElement('idp-option-row')
15
+ export class IdpOptionRow extends DeesElement {
16
+ public static demo = () => html`
17
+ <div style="display: flex; flex-direction: column; gap: 10px; max-width: 340px;">
18
+ <idp-option-row icon="fingerprint" label="Passkey · Face ID" description="Recommended — no password to remember" active trailing="none"></idp-option-row>
19
+ <idp-option-row icon="phone" label="Push approvals" description="Alex's iPhone 15 Pro" trailing="check"></idp-option-row>
20
+ <idp-option-row icon="user" label="Your name and email" description="alex@lossless.com" interactive="false" tileTone="accent" tileSize="30" trailing="check"></idp-option-row>
21
+ <idp-option-row label="Alex Mercer" description="alex@lossless.com">
22
+ <idp-avatar slot="leading" name="Alex Mercer"></idp-avatar>
23
+ </idp-option-row>
24
+ </div>
25
+ `;
26
+ public static demoGroups = ['idp.global v5 auth'];
27
+
28
+ @property({ type: String })
29
+ public accessor icon = '';
30
+
31
+ @property({ type: String })
32
+ public accessor label = '';
33
+
34
+ @property({ type: String })
35
+ public accessor description = '';
36
+
37
+ @property({ type: Boolean, reflect: true })
38
+ public accessor active = false;
39
+
40
+ @property({
41
+ converter: (valueArg) => valueArg !== 'false' && valueArg !== null,
42
+ })
43
+ public accessor interactive = true;
44
+
45
+ @property({ type: String })
46
+ public accessor trailing: TIdpOptionRowTrailing = 'chevron';
47
+
48
+ @property({ type: String })
49
+ public accessor tileTone: TIdpOptionRowTileTone = 'default';
50
+
51
+ @property({ type: Number })
52
+ public accessor tileSize = 34;
53
+
54
+ public static styles = [
55
+ ...idpElementStyles,
56
+ css`
57
+ :host {
58
+ display: block;
59
+ }
60
+ .row {
61
+ display: flex;
62
+ align-items: center;
63
+ gap: 12px;
64
+ padding: 11px 13px;
65
+ border-radius: 13px;
66
+ border: 1.5px solid var(--idp-border);
67
+ background: var(--idp-card);
68
+ font-family: var(--idp-font);
69
+ text-align: left;
70
+ width: 100%;
71
+ box-sizing: border-box;
72
+ }
73
+ .row.interactive {
74
+ cursor: pointer;
75
+ transition: border-color 120ms ease, background 120ms ease;
76
+ }
77
+ .row.interactive:hover {
78
+ background: var(--sh-hover);
79
+ }
80
+ .row.interactive:focus-visible {
81
+ outline: 2px solid color-mix(in srgb, var(--idp-accent), transparent 68%);
82
+ outline-offset: 2px;
83
+ }
84
+ :host([active]) .row {
85
+ border-color: var(--idp-accent);
86
+ background: var(--idp-accent-soft);
87
+ }
88
+ .tile {
89
+ display: flex;
90
+ align-items: center;
91
+ justify-content: center;
92
+ flex-shrink: 0;
93
+ border-radius: 9px;
94
+ background: var(--idp-muted);
95
+ color: var(--idp-fg-2);
96
+ }
97
+ .tile.accent {
98
+ background: var(--idp-accent-soft);
99
+ color: var(--sh-accent-soft-fg);
100
+ }
101
+ :host([active]) .tile {
102
+ background: var(--idp-card);
103
+ color: var(--idp-accent);
104
+ }
105
+ .body {
106
+ flex: 1;
107
+ min-width: 0;
108
+ }
109
+ .label {
110
+ font-size: 13.5px;
111
+ font-weight: 600;
112
+ color: var(--idp-fg);
113
+ }
114
+ .description {
115
+ font-size: 12px;
116
+ color: var(--idp-fg-3);
117
+ margin-top: 1px;
118
+ line-height: 1.4;
119
+ }
120
+ .trailing {
121
+ display: flex;
122
+ align-items: center;
123
+ flex-shrink: 0;
124
+ color: var(--sh-fg-4);
125
+ }
126
+ .trailing.check {
127
+ color: var(--idp-ok);
128
+ }
129
+ `,
130
+ ];
131
+
132
+ public render(): TemplateResult {
133
+ return html`
134
+ <div
135
+ class="row ${this.interactive ? 'interactive' : ''}"
136
+ role=${this.interactive ? 'button' : 'listitem'}
137
+ tabindex=${this.interactive ? '0' : '-1'}
138
+ @click=${this.handleSelect}
139
+ @keydown=${this.handleKeydown}
140
+ >
141
+ <slot name="leading">
142
+ ${this.icon
143
+ ? html`
144
+ <span
145
+ class="tile ${this.tileTone === 'accent' ? 'accent' : ''}"
146
+ style="width: ${this.tileSize}px; height: ${this.tileSize}px;"
147
+ >
148
+ <idp-icon name=${this.icon as any} size=${Math.round(this.tileSize / 2)}></idp-icon>
149
+ </span>
150
+ `
151
+ : html``}
152
+ </slot>
153
+ <div class="body">
154
+ <div class="label">${this.label}</div>
155
+ ${this.description ? html`<div class="description">${this.description}</div>` : html``}
156
+ </div>
157
+ <slot name="trailing">
158
+ ${this.trailing === 'chevron'
159
+ ? html`<span class="trailing"><idp-icon name="chevron-right" size="16"></idp-icon></span>`
160
+ : this.trailing === 'check'
161
+ ? html`<span class="trailing check"><idp-icon name="check" size="15"></idp-icon></span>`
162
+ : html``}
163
+ </slot>
164
+ </div>
165
+ `;
166
+ }
167
+
168
+ private handleSelect() {
169
+ if (!this.interactive) {
170
+ return;
171
+ }
172
+ this.dispatchEvent(new CustomEvent('idp-option-select', { bubbles: true, composed: true }));
173
+ }
174
+
175
+ private handleKeydown(eventArg: KeyboardEvent) {
176
+ if (!this.interactive) {
177
+ return;
178
+ }
179
+ if (eventArg.key === 'Enter' || eventArg.key === ' ') {
180
+ eventArg.preventDefault();
181
+ this.handleSelect();
182
+ }
183
+ }
184
+ }
@@ -0,0 +1,204 @@
1
+ import { DeesElement, html, property, customElement, css, type TemplateResult } from '@design.estate/dees-element';
2
+ import { idpElementStyles } from './tokens.js';
3
+
4
+ export interface IIdpOtpChangeEventDetail {
5
+ value: string;
6
+ complete: boolean;
7
+ }
8
+
9
+ declare global {
10
+ interface HTMLElementTagNameMap {
11
+ 'idp-otp-input': IdpOtpInput;
12
+ }
13
+ }
14
+
15
+ @customElement('idp-otp-input')
16
+ export class IdpOtpInput extends DeesElement {
17
+ public static demo = () => html`
18
+ <div style="display: flex; flex-direction: column; gap: 18px; max-width: 340px;">
19
+ <idp-otp-input></idp-otp-input>
20
+ <idp-otp-input length="4"></idp-otp-input>
21
+ <idp-otp-input error="That code didn't match. Try again."></idp-otp-input>
22
+ </div>
23
+ `;
24
+ public static demoGroups = ['idp.global v5 auth'];
25
+
26
+ @property({ type: Number })
27
+ public accessor length = 6;
28
+
29
+ @property({ type: String })
30
+ public accessor value = '';
31
+
32
+ @property({ type: String })
33
+ public accessor name = '';
34
+
35
+ @property({ type: String })
36
+ public accessor key = '';
37
+
38
+ @property({ type: Boolean, reflect: true })
39
+ public accessor disabled = false;
40
+
41
+ @property({ type: String })
42
+ public accessor error = '';
43
+
44
+ @property({ type: Boolean })
45
+ public accessor required = true;
46
+
47
+ public static styles = [
48
+ ...idpElementStyles,
49
+ css`
50
+ :host {
51
+ display: block;
52
+ font-family: var(--idp-font);
53
+ }
54
+ .boxes {
55
+ display: flex;
56
+ gap: 8px;
57
+ justify-content: center;
58
+ }
59
+ input {
60
+ width: 44px;
61
+ height: 52px;
62
+ border-radius: 12px;
63
+ background: var(--idp-card);
64
+ border: 1.5px solid var(--idp-border);
65
+ color: var(--idp-fg);
66
+ font-family: var(--idp-font);
67
+ font-size: 22px;
68
+ font-weight: 600;
69
+ text-align: center;
70
+ outline: none;
71
+ padding: 0;
72
+ caret-color: var(--idp-accent);
73
+ transition: border-color 120ms ease, box-shadow 120ms ease;
74
+ }
75
+ input:focus {
76
+ border-color: var(--idp-accent);
77
+ box-shadow: 0 0 0 3px var(--sh-accent-ring);
78
+ }
79
+ input:disabled {
80
+ opacity: 0.5;
81
+ }
82
+ .has-error input {
83
+ border-color: var(--idp-error);
84
+ }
85
+ .error {
86
+ margin: 8px 0 0;
87
+ font-size: 12px;
88
+ color: var(--idp-error);
89
+ text-align: center;
90
+ }
91
+ `,
92
+ ];
93
+
94
+ public render(): TemplateResult {
95
+ const digits = Array.from({ length: this.length }, (unused, indexArg) => this.value[indexArg] || '');
96
+ return html`
97
+ <div class="boxes ${this.error ? 'has-error' : ''}">
98
+ ${digits.map(
99
+ (digitArg, indexArg) => html`
100
+ <input
101
+ type="text"
102
+ inputmode="numeric"
103
+ maxlength="1"
104
+ autocomplete=${indexArg === 0 ? 'one-time-code' : 'off'}
105
+ .value=${digitArg}
106
+ ?disabled=${this.disabled}
107
+ aria-label="Digit ${indexArg + 1} of ${this.length}"
108
+ @input=${(eventArg: InputEvent) => this.handleInput(eventArg, indexArg)}
109
+ @keydown=${(eventArg: KeyboardEvent) => this.handleKeydown(eventArg, indexArg)}
110
+ @paste=${(eventArg: ClipboardEvent) => this.handlePaste(eventArg)}
111
+ />
112
+ `
113
+ )}
114
+ </div>
115
+ ${this.error ? html`<p class="error">${this.error}</p>` : html``}
116
+ `;
117
+ }
118
+
119
+ private get inputs(): HTMLInputElement[] {
120
+ return Array.from(this.shadowRoot?.querySelectorAll('input') || []);
121
+ }
122
+
123
+ private handleInput(eventArg: InputEvent, indexArg: number) {
124
+ const input = eventArg.target as HTMLInputElement;
125
+ const digits = input.value.replace(/\D/g, '');
126
+ // browser OTP autofill may drop the whole code into the first box
127
+ if (digits.length > 1) {
128
+ this.fillFrom(digits, indexArg);
129
+ return;
130
+ }
131
+ input.value = digits;
132
+ this.syncValueFromInputs();
133
+ if (digits && indexArg < this.length - 1) {
134
+ this.inputs[indexArg + 1]?.focus();
135
+ }
136
+ }
137
+
138
+ private handlePaste(eventArg: ClipboardEvent) {
139
+ eventArg.preventDefault();
140
+ const digits = (eventArg.clipboardData?.getData('text') || '').replace(/\D/g, '');
141
+ if (digits) {
142
+ this.fillFrom(digits, 0);
143
+ }
144
+ }
145
+
146
+ private fillFrom(digitsArg: string, startIndexArg: number) {
147
+ const inputs = this.inputs;
148
+ for (let offset = 0; offset < digitsArg.length && startIndexArg + offset < this.length; offset++) {
149
+ inputs[startIndexArg + offset].value = digitsArg[offset];
150
+ }
151
+ const nextIndex = Math.min(startIndexArg + digitsArg.length, this.length - 1);
152
+ inputs[nextIndex]?.focus();
153
+ this.syncValueFromInputs();
154
+ }
155
+
156
+ private handleKeydown(eventArg: KeyboardEvent, indexArg: number) {
157
+ const inputs = this.inputs;
158
+ if (eventArg.key === 'Backspace') {
159
+ eventArg.preventDefault();
160
+ if (inputs[indexArg].value) {
161
+ inputs[indexArg].value = '';
162
+ } else if (indexArg > 0) {
163
+ inputs[indexArg - 1].value = '';
164
+ inputs[indexArg - 1].focus();
165
+ }
166
+ this.syncValueFromInputs();
167
+ } else if (eventArg.key === 'ArrowLeft' && indexArg > 0) {
168
+ inputs[indexArg - 1].focus();
169
+ } else if (eventArg.key === 'ArrowRight' && indexArg < this.length - 1) {
170
+ inputs[indexArg + 1].focus();
171
+ }
172
+ }
173
+
174
+ private syncValueFromInputs() {
175
+ this.value = this.inputs.map((inputArg) => inputArg.value).join('');
176
+ this.error = '';
177
+ const complete = this.value.length === this.length;
178
+ const detail: IIdpOtpChangeEventDetail = { value: this.value, complete };
179
+ this.dispatchEvent(new CustomEvent('idp-otp-change', { detail, bubbles: true, composed: true }));
180
+ if (complete) {
181
+ this.dispatchEvent(new CustomEvent('idp-otp-complete', { detail, bubbles: true, composed: true }));
182
+ }
183
+ }
184
+
185
+ public focus() {
186
+ const firstEmpty = Math.min(this.value.length, this.length - 1);
187
+ this.inputs[firstEmpty]?.focus();
188
+ }
189
+
190
+ public clear() {
191
+ this.value = '';
192
+ this.error = '';
193
+ this.updateComplete.then(() => this.inputs[0]?.focus());
194
+ }
195
+
196
+ public validate(): boolean {
197
+ if (this.required && this.value.length !== this.length) {
198
+ this.error = `Enter the ${this.length}-digit code.`;
199
+ return false;
200
+ }
201
+ this.error = '';
202
+ return true;
203
+ }
204
+ }
@@ -0,0 +1,69 @@
1
+ import { DeesElement, html, property, customElement, css, type TemplateResult } from '@design.estate/dees-element';
2
+ import { idpElementStyles } from './tokens.js';
3
+
4
+ declare global {
5
+ interface HTMLElementTagNameMap {
6
+ 'idp-progress-bar': IdpProgressBar;
7
+ }
8
+ }
9
+
10
+ @customElement('idp-progress-bar')
11
+ export class IdpProgressBar extends DeesElement {
12
+ public static demo = () => html`
13
+ <div style="display: flex; flex-direction: column; gap: 14px; max-width: 320px;">
14
+ <idp-progress-bar value="35"></idp-progress-bar>
15
+ <idp-progress-bar value="82"></idp-progress-bar>
16
+ <idp-progress-bar indeterminate></idp-progress-bar>
17
+ </div>
18
+ `;
19
+ public static demoGroups = ['idp.global v5 primitives'];
20
+
21
+ @property({ type: Number })
22
+ public accessor value = 0;
23
+
24
+ @property({ type: Boolean, reflect: true })
25
+ public accessor indeterminate = false;
26
+
27
+ public static styles = [
28
+ ...idpElementStyles,
29
+ css`
30
+ :host {
31
+ display: block;
32
+ }
33
+ .track {
34
+ width: 100%;
35
+ height: 6px;
36
+ border-radius: var(--idp-radius-pill);
37
+ background: var(--idp-muted);
38
+ overflow: hidden;
39
+ }
40
+ .fill {
41
+ height: 100%;
42
+ border-radius: var(--idp-radius-pill);
43
+ background: var(--idp-accent);
44
+ transition: width 300ms ease;
45
+ }
46
+ :host([indeterminate]) .fill {
47
+ width: 40%;
48
+ animation: slide 1.2s ease-in-out infinite;
49
+ }
50
+ @keyframes slide {
51
+ 0% {
52
+ transform: translateX(-100%);
53
+ }
54
+ 100% {
55
+ transform: translateX(350%);
56
+ }
57
+ }
58
+ `,
59
+ ];
60
+
61
+ public render(): TemplateResult {
62
+ const clamped = Math.max(0, Math.min(100, this.value));
63
+ return html`
64
+ <div class="track" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow=${this.indeterminate ? '' : String(clamped)}>
65
+ <div class="fill" style=${this.indeterminate ? '' : `width: ${clamped}%;`}></div>
66
+ </div>
67
+ `;
68
+ }
69
+ }
@@ -0,0 +1,55 @@
1
+ import { DeesElement, html, property, customElement, css, type TemplateResult } from '@design.estate/dees-element';
2
+ import { idpElementStyles } from './tokens.js';
3
+ import './idp-avatar.js';
4
+
5
+ declare global {
6
+ interface HTMLElementTagNameMap {
7
+ 'idp-signed-in-pill': IdpSignedInPill;
8
+ }
9
+ }
10
+
11
+ @customElement('idp-signed-in-pill')
12
+ export class IdpSignedInPill extends DeesElement {
13
+ public static demo = () => html`
14
+ <idp-signed-in-pill name="Alex Mercer"></idp-signed-in-pill>
15
+ `;
16
+ public static demoGroups = ['idp.global v5 auth'];
17
+
18
+ @property({ type: String })
19
+ public accessor name = '';
20
+
21
+ @property({ type: String })
22
+ public accessor label = 'Signed in as';
23
+
24
+ public static styles = [
25
+ ...idpElementStyles,
26
+ css`
27
+ :host {
28
+ display: inline-flex;
29
+ }
30
+ .pill {
31
+ display: inline-flex;
32
+ align-items: center;
33
+ gap: 9px;
34
+ padding: 7px 12px 7px 8px;
35
+ border-radius: var(--idp-radius-pill);
36
+ background: var(--idp-card);
37
+ border: 1px solid var(--idp-border);
38
+ font-family: var(--idp-font);
39
+ font-size: 12.5px;
40
+ font-weight: 550;
41
+ color: var(--idp-fg-2);
42
+ white-space: nowrap;
43
+ }
44
+ `,
45
+ ];
46
+
47
+ public render(): TemplateResult {
48
+ return html`
49
+ <span class="pill">
50
+ <idp-avatar name=${this.name} size="22"></idp-avatar>
51
+ <span>${this.label} ${this.name}</span>
52
+ </span>
53
+ `;
54
+ }
55
+ }
@@ -0,0 +1,56 @@
1
+ import { DeesElement, html, property, customElement, css, type TemplateResult } from '@design.estate/dees-element';
2
+ import { idpElementStyles } from './tokens.js';
3
+
4
+ declare global {
5
+ interface HTMLElementTagNameMap {
6
+ 'idp-spinner': IdpSpinner;
7
+ }
8
+ }
9
+
10
+ @customElement('idp-spinner')
11
+ export class IdpSpinner extends DeesElement {
12
+ public static demo = () => html`
13
+ <div style="display: flex; gap: 16px; align-items: center;">
14
+ <idp-spinner size="16"></idp-spinner>
15
+ <idp-spinner></idp-spinner>
16
+ <idp-spinner size="34"></idp-spinner>
17
+ </div>
18
+ `;
19
+ public static demoGroups = ['idp.global v5 primitives'];
20
+
21
+ @property({ type: Number })
22
+ public accessor size = 28;
23
+
24
+ public static styles = [
25
+ ...idpElementStyles,
26
+ css`
27
+ :host {
28
+ display: inline-flex;
29
+ }
30
+ .spinner {
31
+ border-radius: 999px;
32
+ border-style: solid;
33
+ border-color: var(--idp-muted);
34
+ border-top-color: var(--idp-accent);
35
+ animation: spin 700ms linear infinite;
36
+ }
37
+ @keyframes spin {
38
+ to {
39
+ transform: rotate(360deg);
40
+ }
41
+ }
42
+ `,
43
+ ];
44
+
45
+ public render(): TemplateResult {
46
+ const borderWidth = Math.max(2, Math.round(this.size / 11));
47
+ return html`
48
+ <span
49
+ class="spinner"
50
+ role="status"
51
+ aria-label="Loading"
52
+ style="width: ${this.size}px; height: ${this.size}px; border-width: ${borderWidth}px;"
53
+ ></span>
54
+ `;
55
+ }
56
+ }