@idp.global/catalog 1.1.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.
Files changed (88) hide show
  1. package/assets/v2/draw-41d55381-b162-433d-956c-fe4cd400212e.png +0 -0
  2. package/assets/v2/draw-bd386bf3-ce94-4452-88da-05ae372fae35.png +0 -0
  3. package/assets/v2/draw-be69231a-f491-4bd3-9a47-71561ce14c15.png +0 -0
  4. package/assets/v2/draw-d58859a8-0615-44ec-80fe-626033c888f7.png +0 -0
  5. package/assets/v2/draw-db67b502-d987-4226-a5f8-d57e3f8d7ba9.png +0 -0
  6. package/assets/v2/draw-e6ac3ef5-5a93-464d-9d3f-5ae7b7278bdd.png +0 -0
  7. package/changelog.md +26 -0
  8. package/dist_bundle/bundle.js +6385 -0
  9. package/dist_ts_web/00_commitinfo_data.d.ts +8 -0
  10. package/dist_ts_web/00_commitinfo_data.js +9 -0
  11. package/dist_ts_web/elements/idp-admin-shell.d.ts +292 -0
  12. package/dist_ts_web/elements/idp-admin-shell.js +2615 -0
  13. package/dist_ts_web/elements/idp-approval-card.d.ts +25 -0
  14. package/dist_ts_web/elements/idp-approval-card.js +274 -0
  15. package/dist_ts_web/elements/idp-badge.d.ts +14 -0
  16. package/dist_ts_web/elements/idp-badge.js +135 -0
  17. package/dist_ts_web/elements/idp-button.d.ts +21 -0
  18. package/dist_ts_web/elements/idp-button.js +234 -0
  19. package/dist_ts_web/elements/idp-card.d.ts +15 -0
  20. package/dist_ts_web/elements/idp-card.js +139 -0
  21. package/dist_ts_web/elements/idp-checkbox.d.ts +23 -0
  22. package/dist_ts_web/elements/idp-checkbox.js +233 -0
  23. package/dist_ts_web/elements/idp-dashboard-window.d.ts +23 -0
  24. package/dist_ts_web/elements/idp-dashboard-window.js +587 -0
  25. package/dist_ts_web/elements/idp-data-table.d.ts +45 -0
  26. package/dist_ts_web/elements/idp-data-table.js +472 -0
  27. package/dist_ts_web/elements/idp-form-submit.d.ts +20 -0
  28. package/dist_ts_web/elements/idp-form-submit.js +136 -0
  29. package/dist_ts_web/elements/idp-form.d.ts +31 -0
  30. package/dist_ts_web/elements/idp-form.js +197 -0
  31. package/dist_ts_web/elements/idp-icon.d.ts +18 -0
  32. package/dist_ts_web/elements/idp-icon.js +204 -0
  33. package/dist_ts_web/elements/idp-inbox-preview.d.ts +18 -0
  34. package/dist_ts_web/elements/idp-inbox-preview.js +245 -0
  35. package/dist_ts_web/elements/idp-input.d.ts +26 -0
  36. package/dist_ts_web/elements/idp-input.js +249 -0
  37. package/dist_ts_web/elements/idp-landing-hero.d.ts +15 -0
  38. package/dist_ts_web/elements/idp-landing-hero.js +209 -0
  39. package/dist_ts_web/elements/idp-landing-page.d.ts +21 -0
  40. package/dist_ts_web/elements/idp-landing-page.js +694 -0
  41. package/dist_ts_web/elements/idp-mobile-frame.d.ts +14 -0
  42. package/dist_ts_web/elements/idp-mobile-frame.js +195 -0
  43. package/dist_ts_web/elements/idp-mobile-showcase.d.ts +16 -0
  44. package/dist_ts_web/elements/idp-mobile-showcase.js +339 -0
  45. package/dist_ts_web/elements/idp-select.d.ts +28 -0
  46. package/dist_ts_web/elements/idp-select.js +179 -0
  47. package/dist_ts_web/elements/idp-toggle.d.ts +15 -0
  48. package/dist_ts_web/elements/idp-toggle.js +134 -0
  49. package/dist_ts_web/elements/index.d.ts +20 -0
  50. package/dist_ts_web/elements/index.js +21 -0
  51. package/dist_ts_web/elements/tokens.d.ts +21 -0
  52. package/dist_ts_web/elements/tokens.js +190 -0
  53. package/dist_ts_web/index.d.ts +1 -0
  54. package/dist_ts_web/index.js +2 -0
  55. package/dist_ts_web/pages/index.d.ts +5 -0
  56. package/dist_ts_web/pages/index.js +23 -0
  57. package/dist_watch/bundle.js +120699 -0
  58. package/dist_watch/bundle.js.map +7 -0
  59. package/dist_watch/index.html +29 -0
  60. package/html/index.html +29 -0
  61. package/html/index.ts +39 -0
  62. package/license +21 -0
  63. package/package.json +64 -0
  64. package/readme.md +103 -0
  65. package/ts_web/00_commitinfo_data.ts +8 -0
  66. package/ts_web/elements/idp-admin-shell.ts +2664 -0
  67. package/ts_web/elements/idp-approval-card.ts +182 -0
  68. package/ts_web/elements/idp-badge.ts +84 -0
  69. package/ts_web/elements/idp-button.ts +166 -0
  70. package/ts_web/elements/idp-card.ts +76 -0
  71. package/ts_web/elements/idp-checkbox.ts +143 -0
  72. package/ts_web/elements/idp-dashboard-window.ts +564 -0
  73. package/ts_web/elements/idp-data-table.ts +407 -0
  74. package/ts_web/elements/idp-form-submit.ts +65 -0
  75. package/ts_web/elements/idp-form.ts +159 -0
  76. package/ts_web/elements/idp-icon.ts +263 -0
  77. package/ts_web/elements/idp-inbox-preview.ts +192 -0
  78. package/ts_web/elements/idp-input.ts +151 -0
  79. package/ts_web/elements/idp-landing-hero.ts +165 -0
  80. package/ts_web/elements/idp-landing-page.ts +655 -0
  81. package/ts_web/elements/idp-mobile-frame.ts +137 -0
  82. package/ts_web/elements/idp-mobile-showcase.ts +295 -0
  83. package/ts_web/elements/idp-select.ts +119 -0
  84. package/ts_web/elements/idp-toggle.ts +77 -0
  85. package/ts_web/elements/index.ts +20 -0
  86. package/ts_web/elements/tokens.ts +193 -0
  87. package/ts_web/index.ts +1 -0
  88. package/ts_web/pages/index.ts +26 -0
@@ -0,0 +1,263 @@
1
+ import {
2
+ DeesElement,
3
+ html,
4
+ property,
5
+ customElement,
6
+ css,
7
+ type TemplateResult,
8
+ } from '@design.estate/dees-element';
9
+ import * as lucideIcons from 'lucide';
10
+ import {
11
+ Activity,
12
+ ArrowUp,
13
+ Bell,
14
+ Bolt,
15
+ Box,
16
+ Building2,
17
+ Check,
18
+ ChevronDown,
19
+ ChevronRight,
20
+ Clock,
21
+ Cloud,
22
+ Copy,
23
+ CreditCard,
24
+ Fingerprint,
25
+ Globe,
26
+ Grid2x2,
27
+ Home,
28
+ Key,
29
+ Laptop,
30
+ Lock,
31
+ LogOut,
32
+ Mail,
33
+ MapPin,
34
+ Monitor,
35
+ MonitorSmartphone,
36
+ Nfc,
37
+ Phone,
38
+ Plus,
39
+ Power,
40
+ QrCode,
41
+ Search,
42
+ Settings,
43
+ Shield,
44
+ SmartphoneNfc,
45
+ SquarePen,
46
+ Trash2,
47
+ TriangleAlert,
48
+ User,
49
+ Users,
50
+ Wallet,
51
+ X,
52
+ createElement,
53
+ type IconNode,
54
+ } from 'lucide';
55
+ import { idpElementStyles } from './tokens.js';
56
+
57
+ export type TIdpIconName =
58
+ | 'activity'
59
+ | 'alert'
60
+ | 'alert-triangle'
61
+ | 'arrow-up'
62
+ | 'bell'
63
+ | 'bolt'
64
+ | 'box'
65
+ | 'building'
66
+ | 'building2'
67
+ | 'building-2'
68
+ | 'check'
69
+ | 'chevron'
70
+ | 'chevron-down'
71
+ | 'chevron-right'
72
+ | 'clock'
73
+ | 'cloud'
74
+ | 'copy'
75
+ | 'credit'
76
+ | 'device'
77
+ | 'edit'
78
+ | 'fingerprint'
79
+ | 'gear'
80
+ | 'globe'
81
+ | 'grid'
82
+ | 'home'
83
+ | 'key'
84
+ | 'laptop'
85
+ | 'location'
86
+ | 'lock'
87
+ | 'logout'
88
+ | 'mail'
89
+ | 'monitor'
90
+ | 'monitor-smartphone'
91
+ | 'nfc'
92
+ | 'phone'
93
+ | 'plus'
94
+ | 'power'
95
+ | 'qr'
96
+ | 'search'
97
+ | 'settings'
98
+ | 'shield'
99
+ | 'smartphone-nfc'
100
+ | 'trash'
101
+ | 'user'
102
+ | 'users'
103
+ | 'wallet'
104
+ | 'waveform'
105
+ | 'x'
106
+ | `lucide:${string}`;
107
+
108
+ declare global {
109
+ interface HTMLElementTagNameMap {
110
+ 'idp-icon': IdpIcon;
111
+ }
112
+ }
113
+
114
+ const iconNodes: Record<string, IconNode> = {
115
+ activity: Activity,
116
+ alert: TriangleAlert,
117
+ 'alert-triangle': TriangleAlert,
118
+ 'arrow-up': ArrowUp,
119
+ bell: Bell,
120
+ bolt: Bolt,
121
+ box: Box,
122
+ building: Building2,
123
+ building2: Building2,
124
+ 'building-2': Building2,
125
+ check: Check,
126
+ chevron: ChevronRight,
127
+ 'chevron-down': ChevronDown,
128
+ 'chevron-right': ChevronRight,
129
+ clock: Clock,
130
+ cloud: Cloud,
131
+ copy: Copy,
132
+ credit: CreditCard,
133
+ device: MonitorSmartphone,
134
+ edit: SquarePen,
135
+ fingerprint: Fingerprint,
136
+ gear: Settings,
137
+ globe: Globe,
138
+ grid: Grid2x2,
139
+ home: Home,
140
+ key: Key,
141
+ laptop: Laptop,
142
+ location: MapPin,
143
+ lock: Lock,
144
+ logout: LogOut,
145
+ mail: Mail,
146
+ monitor: Monitor,
147
+ 'monitor-smartphone': MonitorSmartphone,
148
+ nfc: Nfc,
149
+ phone: Phone,
150
+ plus: Plus,
151
+ power: Power,
152
+ qr: QrCode,
153
+ search: Search,
154
+ settings: Settings,
155
+ shield: Shield,
156
+ 'smartphone-nfc': SmartphoneNfc,
157
+ trash: Trash2,
158
+ user: User,
159
+ users: Users,
160
+ wallet: Wallet,
161
+ waveform: Activity,
162
+ x: X,
163
+ };
164
+
165
+ const toKebab = (valueArg: string): string => valueArg
166
+ .replace(/^lucide:/, '')
167
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
168
+ .toLowerCase();
169
+
170
+ const toLucideExportName = (valueArg: string): string => valueArg
171
+ .replace(/^lucide:/i, '')
172
+ .split(/[-_: ]+/)
173
+ .filter(Boolean)
174
+ .map((partArg) => `${partArg.charAt(0).toUpperCase()}${partArg.slice(1)}`)
175
+ .join('');
176
+
177
+ @customElement('idp-icon')
178
+ export class IdpIcon extends DeesElement {
179
+ public static demo = () => html`<idp-icon name="shield"></idp-icon>`;
180
+ public static demoGroups = ['idp.global v3 primitives'];
181
+
182
+ @property({ type: String })
183
+ public accessor name: TIdpIconName = 'shield';
184
+
185
+ @property({ type: Number })
186
+ public accessor size = 18;
187
+
188
+ private lastRenderKey = '';
189
+
190
+ public static styles = [
191
+ ...idpElementStyles,
192
+ css`
193
+ :host {
194
+ display: inline-flex;
195
+ align-items: center;
196
+ justify-content: center;
197
+ color: currentColor;
198
+ line-height: 0;
199
+ vertical-align: middle;
200
+ }
201
+ #iconContainer {
202
+ width: var(--icon-size);
203
+ height: var(--icon-size);
204
+ }
205
+ #iconContainer svg {
206
+ display: block;
207
+ width: 100%;
208
+ height: 100%;
209
+ fill: none;
210
+ stroke: currentColor;
211
+ stroke-width: 1.75;
212
+ stroke-linecap: round;
213
+ stroke-linejoin: round;
214
+ }
215
+ `,
216
+ ];
217
+
218
+ private resolveIconNode(): IconNode {
219
+ const rawName = String(this.name || 'shield');
220
+ const iconName = toKebab(rawName);
221
+ const aliasNode = iconNodes[iconName];
222
+ if (aliasNode) {
223
+ return aliasNode;
224
+ }
225
+
226
+ const exportName = toLucideExportName(rawName);
227
+ const lucideNode = (lucideIcons as Record<string, unknown>)[exportName];
228
+ if (Array.isArray(lucideNode)) {
229
+ return lucideNode as IconNode;
230
+ }
231
+
232
+ return Shield;
233
+ }
234
+
235
+ public render(): TemplateResult {
236
+ return html`
237
+ <div id="iconContainer" style="--icon-size: ${this.size}px"></div>
238
+ `;
239
+ }
240
+
241
+ public updated(): void {
242
+ const renderKey = `${this.name}:${this.size}`;
243
+ if (this.lastRenderKey === renderKey) {
244
+ return;
245
+ }
246
+
247
+ this.lastRenderKey = renderKey;
248
+
249
+ const container = this.shadowRoot?.querySelector('#iconContainer');
250
+ if (!container) {
251
+ return;
252
+ }
253
+
254
+ container.innerHTML = '';
255
+ const iconElement = createElement(this.resolveIconNode(), {
256
+ color: 'currentColor',
257
+ size: this.size,
258
+ strokeWidth: 1.75,
259
+ });
260
+ iconElement.setAttribute('aria-hidden', 'true');
261
+ container.appendChild(iconElement);
262
+ }
263
+ }
@@ -0,0 +1,192 @@
1
+ import { DeesElement, html, property, customElement, css, type TemplateResult } from '@design.estate/dees-element';
2
+ import { idpElementStyles } from './tokens.js';
3
+ import './idp-approval-card.js';
4
+ import './idp-badge.js';
5
+ import './idp-button.js';
6
+ import './idp-icon.js';
7
+ import './idp-mobile-frame.js';
8
+
9
+ declare global {
10
+ interface HTMLElementTagNameMap {
11
+ 'idp-inbox-preview': IdpInboxPreview;
12
+ }
13
+ }
14
+
15
+ @customElement('idp-inbox-preview')
16
+ export class IdpInboxPreview extends DeesElement {
17
+ public static demo = () => html`<idp-inbox-preview></idp-inbox-preview>`;
18
+ public static demoGroups = ['idp.global v3 approval surfaces'];
19
+
20
+ @property({ type: Boolean, reflect: true })
21
+ public accessor dark = false;
22
+
23
+ public static styles = [
24
+ ...idpElementStyles,
25
+ css`
26
+ :host {
27
+ display: block;
28
+ }
29
+ idp-mobile-frame {
30
+ --idp-bg: #ffffff;
31
+ }
32
+ :host([dark]) idp-mobile-frame {
33
+ --idp-bg: #09090b;
34
+ --idp-fg: #fafafa;
35
+ }
36
+ .screen {
37
+ height: 100%;
38
+ background: var(--idp-bg);
39
+ color: var(--idp-fg);
40
+ box-sizing: border-box;
41
+ padding-top: 58px;
42
+ display: flex;
43
+ flex-direction: column;
44
+ }
45
+ .header {
46
+ padding: 0 20px 14px;
47
+ border-bottom: 1px solid var(--idp-border);
48
+ }
49
+ .brandline {
50
+ display: flex;
51
+ justify-content: space-between;
52
+ align-items: center;
53
+ margin-bottom: 12px;
54
+ }
55
+ .brand {
56
+ display: flex;
57
+ align-items: center;
58
+ gap: 8px;
59
+ font-size: 14px;
60
+ font-weight: 650;
61
+ }
62
+ .brandmark {
63
+ width: 24px;
64
+ height: 24px;
65
+ display: flex;
66
+ align-items: center;
67
+ justify-content: center;
68
+ border-radius: 6px;
69
+ background: var(--idp-primary);
70
+ color: var(--idp-primary-fg);
71
+ }
72
+ h2 {
73
+ margin: 0;
74
+ font-size: 24px;
75
+ font-weight: 760;
76
+ letter-spacing: -0.04em;
77
+ }
78
+ .summary {
79
+ display: flex;
80
+ align-items: center;
81
+ gap: 8px;
82
+ margin-top: 6px;
83
+ color: var(--idp-muted-fg);
84
+ font-size: 13px;
85
+ }
86
+ .list {
87
+ display: grid;
88
+ gap: 10px;
89
+ padding: 14px 16px 120px;
90
+ overflow: auto;
91
+ }
92
+ .earlier {
93
+ margin: 10px 0 2px;
94
+ color: var(--idp-muted-fg);
95
+ font-size: 11px;
96
+ font-weight: 700;
97
+ letter-spacing: 0.08em;
98
+ text-transform: uppercase;
99
+ }
100
+ .row {
101
+ display: flex;
102
+ align-items: center;
103
+ gap: 10px;
104
+ padding: 10px 12px;
105
+ border: 1px solid var(--idp-border);
106
+ border-radius: 10px;
107
+ font-size: 13px;
108
+ }
109
+ .row-icon {
110
+ width: 26px;
111
+ height: 26px;
112
+ border-radius: 7px;
113
+ background: var(--idp-muted);
114
+ color: var(--idp-ok);
115
+ display: flex;
116
+ align-items: center;
117
+ justify-content: center;
118
+ }
119
+ .row-sub {
120
+ color: var(--idp-muted-fg);
121
+ font-size: 12px;
122
+ margin-top: 1px;
123
+ }
124
+ .tabbar {
125
+ position: absolute;
126
+ left: 12px;
127
+ right: 12px;
128
+ bottom: 24px;
129
+ z-index: 80;
130
+ display: grid;
131
+ grid-template-columns: repeat(4, 1fr);
132
+ gap: 4px;
133
+ padding: 8px;
134
+ border: 1px solid color-mix(in srgb, var(--idp-border), transparent 20%);
135
+ border-radius: 22px;
136
+ background: color-mix(in srgb, var(--idp-card), transparent 8%);
137
+ box-shadow: 0 16px 36px rgb(0 0 0 / 0.16);
138
+ backdrop-filter: blur(24px) saturate(170%);
139
+ }
140
+ .tab {
141
+ display: grid;
142
+ place-items: center;
143
+ gap: 3px;
144
+ min-height: 44px;
145
+ color: var(--idp-muted-fg);
146
+ font-size: 10px;
147
+ font-weight: 600;
148
+ }
149
+ .tab.active {
150
+ color: var(--idp-accent);
151
+ }
152
+ `,
153
+ ];
154
+
155
+ public render(): TemplateResult {
156
+ return html`
157
+ <idp-mobile-frame ?dark=${this.dark}>
158
+ <div class="screen">
159
+ <header class="header">
160
+ <div class="brandline">
161
+ <div class="brand"><span class="brandmark"><idp-icon name="shield" size="14"></idp-icon></span>idp.global</div>
162
+ <idp-button variant="ghost" size="sm" icon="search" aria-label="Search"></idp-button>
163
+ </div>
164
+ <h2>Inbox</h2>
165
+ <div class="summary"><idp-badge variant="ok">3 pending</idp-badge><span>oldest 8 min ago</span></div>
166
+ </header>
167
+ <div class="list">
168
+ <idp-approval-card primary app-name="GitHub" app-initials="GH" app-color="#24292F" request-text="Sign in to github.com" location="Berlin · DE" device="Safari · MBP" time-label="now"></idp-approval-card>
169
+ <idp-approval-card app-name="Lufthansa.com" app-initials="LH" app-color="#05164D" request-text="Verify identity" location="Berlin · DE" device="iPhone 15 Pro" time-label="2m"></idp-approval-card>
170
+ <idp-approval-card app-name="Hetzner Cloud" app-initials="HZ" app-color="#D50C2D" request-text="Sign in to console" location="Falkenstein · DE" device="Firefox · Windows" risk="warning" time-label="8m"></idp-approval-card>
171
+ <div class="earlier">Earlier today</div>
172
+ ${['Notion · Approved 11:42', 'Apple ID · Approved 09:18', 'reddit.com · Denied 08:57'].map((itemArg) => html`
173
+ <div class="row">
174
+ <div class="row-icon"><idp-icon name="check" size="15"></idp-icon></div>
175
+ <div>
176
+ <div>${itemArg.split(' · ')[0]}</div>
177
+ <div class="row-sub">${itemArg.split(' · ')[1]}</div>
178
+ </div>
179
+ </div>
180
+ `)}
181
+ </div>
182
+ <nav class="tabbar">
183
+ <div class="tab active"><idp-icon name="bell" size="18"></idp-icon>Inbox</div>
184
+ <div class="tab"><idp-icon name="clock" size="18"></idp-icon>History</div>
185
+ <div class="tab"><idp-icon name="device" size="18"></idp-icon>Devices</div>
186
+ <div class="tab"><idp-icon name="user" size="18"></idp-icon>Identity</div>
187
+ </nav>
188
+ </div>
189
+ </idp-mobile-frame>
190
+ `;
191
+ }
192
+ }
@@ -0,0 +1,151 @@
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-input': IdpInput;
7
+ }
8
+ }
9
+
10
+ @customElement('idp-input')
11
+ 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'];
14
+
15
+ @property({ type: String })
16
+ public accessor label = '';
17
+
18
+ @property({ type: String })
19
+ public accessor name = '';
20
+
21
+ @property({ type: String })
22
+ public accessor key = '';
23
+
24
+ @property({ type: String })
25
+ public accessor hint = '';
26
+
27
+ @property({ type: String })
28
+ public accessor value = '';
29
+
30
+ @property({ type: String })
31
+ public accessor placeholder = '';
32
+
33
+ @property({ type: String })
34
+ public accessor type = 'text';
35
+
36
+ @property({ type: String })
37
+ public accessor autocomplete = '';
38
+
39
+ @property({ type: String })
40
+ public accessor error = '';
41
+
42
+ @property({ type: Boolean, reflect: true })
43
+ public accessor required = false;
44
+
45
+ @property({ type: Boolean, reflect: true })
46
+ public accessor disabled = false;
47
+
48
+ public static styles = [
49
+ ...idpElementStyles,
50
+ css`
51
+ :host {
52
+ display: block;
53
+ }
54
+ label {
55
+ display: grid;
56
+ gap: 6px;
57
+ }
58
+ .label {
59
+ color: var(--idp-fg);
60
+ font-size: 13px;
61
+ font-weight: 500;
62
+ }
63
+ input {
64
+ width: 100%;
65
+ height: 36px;
66
+ box-sizing: border-box;
67
+ padding: 0 10px;
68
+ border: 1px solid var(--idp-border);
69
+ border-radius: 8px;
70
+ outline: none;
71
+ background: var(--idp-card);
72
+ color: var(--idp-fg);
73
+ font-family: var(--idp-font);
74
+ font-size: 13px;
75
+ transition: border-color 120ms ease, box-shadow 120ms ease;
76
+ }
77
+ input:focus {
78
+ border-color: var(--idp-accent);
79
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--idp-accent), transparent 86%);
80
+ }
81
+ input:disabled {
82
+ opacity: 0.5;
83
+ }
84
+ input[aria-invalid='true'] {
85
+ border-color: var(--idp-destructive);
86
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--idp-destructive), transparent 86%);
87
+ }
88
+ .hint,
89
+ .error {
90
+ color: var(--idp-muted-fg);
91
+ font-size: 12px;
92
+ line-height: 1.4;
93
+ }
94
+ .error {
95
+ color: var(--idp-destructive);
96
+ }
97
+ `,
98
+ ];
99
+
100
+ public focus() {
101
+ this.shadowRoot?.querySelector('input')?.focus();
102
+ }
103
+
104
+ public validate() {
105
+ const input = this.shadowRoot?.querySelector('input');
106
+ if (this.required && !this.value.trim()) {
107
+ this.error = `${this.label || this.name || 'This field'} is required.`;
108
+ return false;
109
+ }
110
+
111
+ if (input && !input.checkValidity()) {
112
+ this.error = input.validationMessage;
113
+ return false;
114
+ }
115
+
116
+ this.error = '';
117
+ return true;
118
+ }
119
+
120
+ private handleInput(eventArg: Event) {
121
+ this.value = (eventArg.target as HTMLInputElement).value;
122
+ if (this.error) {
123
+ this.validate();
124
+ }
125
+ this.dispatchEvent(new CustomEvent('idp-input-change', {
126
+ detail: { name: this.name || this.key, key: this.key || this.name, value: this.value },
127
+ bubbles: true,
128
+ composed: true,
129
+ }));
130
+ }
131
+
132
+ public render(): TemplateResult {
133
+ return html`
134
+ <label>
135
+ ${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
+ />
147
+ ${this.error ? html`<span class="error">${this.error}</span>` : this.hint ? html`<span class="hint">${this.hint}</span>` : html``}
148
+ </label>
149
+ `;
150
+ }
151
+ }