@draftlab/auth 0.1.5 → 0.2.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.
package/dist/ui/base.js CHANGED
@@ -6,293 +6,293 @@ import { jsx, jsxs } from "preact/jsx-runtime";
6
6
  const css = `@import url("https://unpkg.com/tailwindcss@3.4.15/src/css/preflight.css");
7
7
 
8
8
  :root {
9
- --color-background-dark: #0e0e11;
10
- --color-background-light: #ffffff;
11
- --color-primary-dark: #6772e5;
12
- --color-primary-light: #6772e5;
13
-
14
- --color-background-success-dark: oklch(0.3 0.04 172);
15
- --color-background-success-light: oklch(
16
- from var(--color-background-success-dark) 0.83 c h
17
- );
18
- --color-success-dark: oklch(
19
- from var(--color-background-success-dark) 0.92 c h
20
- );
21
- --color-success-light: oklch(
22
- from var(--color-background-success-dark) 0.25 c h
23
- );
24
-
25
- --color-background-error-dark: oklch(0.32 0.07 15);
26
- --color-background-error-light: oklch(
27
- from var(--color-background-error-dark) 0.92 c h
28
- );
29
- --color-error-dark: oklch(from var(--color-background-error-dark) 0.92 c h);
30
- --color-error-light: oklch(from var(--color-background-error-dark) 0.25 c h);
31
-
32
- --border-radius: 0;
33
-
34
- --color-background: var(--color-background-dark);
35
- --color-primary: var(--color-primary-dark);
36
-
37
- --color-background-success: var(--color-background-success-dark);
38
- --color-success: var(--color-success-dark);
39
- --color-background-error: var(--color-background-error-dark);
40
- --color-error: var(--color-error-dark);
41
-
42
- @media (prefers-color-scheme: light) {
43
- --color-background: var(--color-background-light);
44
- --color-primary: var(--color-primary-light);
45
-
46
- --color-background-success: var(--color-background-success-light);
47
- --color-success: var(--color-success-light);
48
- --color-background-error: var(--color-background-error-light);
49
- --color-error: var(--color-error-light);
50
- }
51
-
52
- --color-high: oklch(
53
- from var(--color-background) clamp(0, calc((l - 0.714) * -1000), 1) 0 0
54
- );
55
- --color-low: oklch(
56
- from var(--color-background) clamp(0, calc((l - 0.714) * 1000), 1) 0 0
57
- );
58
- --lightness-high: color-mix(
59
- in oklch,
60
- var(--color-high) 0%,
61
- oklch(var(--color-high) 0 0)
62
- );
63
- --lightness-low: color-mix(
64
- in oklch,
65
- var(--color-low) 0%,
66
- oklch(var(--color-low) 0 0)
67
- );
68
- --font-family:
69
- ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
70
- "Segoe UI Symbol", "Noto Color Emoji";
71
- --font-scale: 1;
72
-
73
- --font-size-xs: calc(0.75rem * var(--font-scale));
74
- --font-size-sm: calc(0.875rem * var(--font-scale));
75
- --font-size-md: calc(1rem * var(--font-scale));
76
- --font-size-lg: calc(1.125rem * var(--font-scale));
77
- --font-size-xl: calc(1.25rem * var(--font-scale));
78
- --font-size-2xl: calc(1.5rem * var(--font-scale));
9
+ --color-background-dark: #0e0e11;
10
+ --color-background-light: #ffffff;
11
+ --color-primary-dark: #6772e5;
12
+ --color-primary-light: #6772e5;
13
+
14
+ --color-background-success-dark: oklch(0.3 0.04 172);
15
+ --color-background-success-light: oklch(from var(--color-background-success-dark) 0.83 c h);
16
+ --color-success-dark: oklch(from var(--color-background-success-dark) 0.92 c h);
17
+ --color-success-light: oklch(from var(--color-background-success-dark) 0.25 c h);
18
+
19
+ --color-background-error-dark: oklch(0.32 0.07 15);
20
+ --color-background-error-light: oklch(from var(--color-background-error-dark) 0.92 c h);
21
+ --color-error-dark: oklch(from var(--color-background-error-dark) 0.92 c h);
22
+ --color-error-light: oklch(from var(--color-background-error-dark) 0.25 c h);
23
+
24
+ --border-radius: 0;
25
+
26
+ --color-background: var(--color-background-dark);
27
+ --color-primary: var(--color-primary-dark);
28
+
29
+ --color-background-success: var(--color-background-success-dark);
30
+ --color-success: var(--color-success-dark);
31
+ --color-background-error: var(--color-background-error-dark);
32
+ --color-error: var(--color-error-dark);
33
+
34
+ @media (prefers-color-scheme: light) {
35
+ --color-background: var(--color-background-light);
36
+ --color-primary: var(--color-primary-light);
37
+
38
+ --color-background-success: var(--color-background-success-light);
39
+ --color-success: var(--color-success-light);
40
+ --color-background-error: var(--color-background-error-light);
41
+ --color-error: var(--color-error-light);
42
+ }
43
+
44
+ --color-high: oklch(
45
+ from var(--color-background) clamp(0, calc((l - 0.714) * -1000), 1) 0 0
46
+ );
47
+ --color-low: oklch(from var(--color-background) clamp(0, calc((l - 0.714) * 1000), 1) 0 0);
48
+ --lightness-high: color-mix(
49
+ in oklch,
50
+ var(--color-high) 0%,
51
+ oklch(var(--color-high) 0 0)
52
+ );
53
+ --lightness-low: color-mix(
54
+ in oklch,
55
+ var(--color-low) 0%,
56
+ oklch(var(--color-low) 0 0)
57
+ );
58
+ --font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
59
+ "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
60
+ --font-scale: 1;
61
+
62
+ --font-size-xs: calc(0.75rem * var(--font-scale));
63
+ --font-size-sm: calc(0.875rem * var(--font-scale));
64
+ --font-size-md: calc(1rem * var(--font-scale));
65
+ --font-size-lg: calc(1.125rem * var(--font-scale));
66
+ --font-size-xl: calc(1.25rem * var(--font-scale));
67
+ --font-size-2xl: calc(1.5rem * var(--font-scale));
79
68
  }
80
69
 
81
70
  [data-component="root"] {
82
- font-family: var(--font-family);
83
- background-color: var(--color-background);
84
- padding: 1rem;
85
- color: white;
86
- position: absolute;
87
- inset: 0;
88
- display: flex;
89
- align-items: center;
90
- justify-content: center;
91
- flex-direction: column;
92
- user-select: none;
93
- color: var(--color-high);
71
+ font-family: var(--font-family);
72
+ background-color: var(--color-background);
73
+ padding: 1rem;
74
+ color: white;
75
+ position: absolute;
76
+ inset: 0;
77
+ display: flex;
78
+ align-items: center;
79
+ justify-content: center;
80
+ flex-direction: column;
81
+ user-select: none;
82
+ color: var(--color-high);
94
83
  }
95
84
 
96
85
  [data-component="center"] {
97
- width: 380px;
98
- display: flex;
99
- flex-direction: column;
100
- gap: 1.5rem;
86
+ width: 380px;
87
+ display: flex;
88
+ flex-direction: column;
89
+ gap: 1.5rem;
101
90
  }
102
91
 
103
92
  [data-component="center"][data-size="small"] {
104
- width: 300px;
93
+ width: 300px;
105
94
  }
106
95
 
107
- [data-component="button"] {
108
- height: 2.5rem;
109
- cursor: pointer;
110
- border: 0;
111
- font-weight: 500;
112
- font-size: var(--font-size-sm);
113
- border-radius: calc(var(--border-radius) * 0.25rem);
114
- display: flex;
115
- gap: 0.75rem;
116
- align-items: center;
117
- justify-content: center;
118
- background: var(--color-primary);
119
- color: oklch(
120
- from var(--color-primary) clamp(0, calc((l - 0.714) * -1000), 1) 0 0
121
- );
96
+ [data-component="link"] {
97
+ text-decoration: underline;
98
+ text-underline-offset: 0.125rem;
99
+ font-weight: 600;
122
100
  }
123
101
 
124
- [data-component="button"][data-color="ghost"] {
125
- background: transparent;
126
- color: var(--color-high);
127
- border: 1px solid
128
- oklch(
129
- from var(--color-background)
130
- calc(
131
- clamp(
132
- 0.22,
133
- l +
134
- (-0.12 * clamp(0, calc((l - 0.714) * 1000), 1) + 0.06),
135
- 0.88
136
- )
137
- )
138
- c h
139
- );
102
+ [data-component="label"] {
103
+ display: flex;
104
+ gap: 0.75rem;
105
+ flex-direction: column;
106
+ font-size: var(--font-size-xs);
140
107
  }
141
108
 
142
- [data-component="button"] [data-slot="icon"] {
143
- width: 16px;
144
- height: 16px;
109
+ [data-component="logo"] {
110
+ margin: 0 auto;
111
+ height: 2.5rem;
112
+ width: auto;
113
+ display: none;
145
114
  }
146
115
 
147
- [data-component="button"] [data-slot="icon"] svg {
148
- width: 100%;
149
- height: 100%;
116
+ [data-component="logo"][data-mode="light"] {
117
+ display: none;
150
118
  }
151
119
 
152
- [data-component="form"] {
153
- max-width: 100%;
154
- display: flex;
155
- flex-direction: column;
156
- gap: 1rem;
157
- margin: 0;
120
+ [data-component="logo"][data-mode="dark"] {
121
+ display: block;
158
122
  }
159
123
 
160
- [data-component="input"] {
161
- height: 2.5rem;
162
- border: 1px solid
163
- oklch(
164
- from var(--color-background)
165
- calc(
166
- clamp(
167
- 0.22,
168
- l +
169
- (-0.12 * clamp(0, calc((l - 0.714) * 1000), 1) + 0.06),
170
- 0.88
171
- )
172
- )
173
- c h
174
- );
175
- border-radius: calc(var(--border-radius) * 0.25rem);
176
- background: var(--color-background);
177
- color: var(--color-high);
178
- padding: 0 0.75rem;
179
- font-size: var(--font-size-sm);
180
- font-family: var(--font-family);
124
+ @media (prefers-color-scheme: light) {
125
+ [data-component="logo"][data-mode="light"] {
126
+ display: block;
127
+ }
128
+
129
+ [data-component="logo"][data-mode="dark"] {
130
+ display: none;
131
+ }
181
132
  }
182
133
 
183
- [data-component="input"]:focus {
184
- outline: none;
185
- border-color: var(--color-primary);
134
+ @media (prefers-color-scheme: dark) {
135
+ [data-component="logo"][data-mode="dark"] {
136
+ display: block;
137
+ }
186
138
  }
187
139
 
188
- [data-component="input"]::placeholder {
189
- color: oklch(
190
- from var(--color-high)
191
- l
192
- c
193
- h /
194
- 0.6
195
- );
140
+ [data-component="logo-default"] {
141
+ margin: 0 auto;
142
+ height: 2.5rem;
143
+ width: auto;
196
144
  }
197
145
 
198
- [data-component="form-alert"] {
199
- padding: 0.75rem;
200
- border-radius: calc(var(--border-radius) * 0.25rem);
201
- display: flex;
202
- align-items: center;
203
- gap: 0.75rem;
204
- font-size: var(--font-size-sm);
146
+ @media (prefers-color-scheme: light) {
147
+ [data-component="logo-default"] {
148
+ color: var(--color-high);
149
+ }
205
150
  }
206
151
 
207
- [data-component="form-alert"][data-color="danger"] {
208
- background: var(--color-background-error);
209
- color: var(--color-error);
210
- border: 1px solid var(--color-error);
152
+ @media (prefers-color-scheme: dark) {
153
+ [data-component="logo-default"] {
154
+ color: var(--color-high);
155
+ }
211
156
  }
212
157
 
213
- [data-component="form-alert"][data-color="success"] {
214
- background: var(--color-background-success);
215
- color: var(--color-success);
216
- border: 1px solid var(--color-success);
158
+ [data-component="input"] {
159
+ width: 100%;
160
+ height: 2.5rem;
161
+ padding: 0 1rem;
162
+ border: 1px solid transparent;
163
+ --background: oklch(
164
+ from var(--color-background) calc(l + (-0.06 * clamp(0, calc((l - 0.714) * 1000), 1) + 0.03)) c h
165
+ );
166
+ background: var(--background);
167
+ border-color: oklch(
168
+ from var(--color-background)
169
+ calc(clamp(0.22, l + (-0.12 * clamp(0, calc((l - 0.714) * 1000), 1) + 0.06), 0.88)) c h
170
+ );
171
+ border-radius: calc(var(--border-radius) * 0.25rem);
172
+ font-size: var(--font-size-sm);
173
+ outline: none;
217
174
  }
218
175
 
219
- [data-component="form-alert"] [data-slot="icon-success"] {
220
- display: none;
176
+ [data-component="input"]:focus {
177
+ border-color: oklch(
178
+ from var(--color-background)
179
+ calc(clamp(0.3, l + (-0.2 * clamp(0, calc((l - 0.714) * 1000), 1) + 0.1), 0.7)) c h
180
+ );
221
181
  }
222
182
 
223
- [data-component="form-alert"] [data-slot="icon-danger"] {
224
- display: none;
183
+ [data-component="input"]:user-invalid:not(:focus) {
184
+ border-color: oklch(0.4 0.09 7.91);
225
185
  }
226
186
 
227
- [data-component="form-alert"][data-color="success"] [data-slot="icon-success"] {
228
- display: block;
229
- width: 16px;
230
- height: 16px;
187
+ [data-component="button"] {
188
+ height: 2.5rem;
189
+ cursor: pointer;
190
+ border: 0;
191
+ font-weight: 500;
192
+ font-size: var(--font-size-sm);
193
+ border-radius: calc(var(--border-radius) * 0.25rem);
194
+ display: flex;
195
+ gap: 0.75rem;
196
+ align-items: center;
197
+ justify-content: center;
198
+ background: var(--color-primary);
199
+ color: oklch(from var(--color-primary) clamp(0, calc((l - 0.714) * -1000), 1) 0 0);
231
200
  }
232
201
 
233
- [data-component="form-alert"][data-color="danger"] [data-slot="icon-danger"] {
234
- display: block;
235
- width: 16px;
236
- height: 16px;
202
+ [data-component="button"][data-color="ghost"] {
203
+ background: transparent;
204
+ color: var(--color-high);
205
+ border: 1px solid
206
+ oklch(
207
+ from var(--color-background)
208
+ calc(clamp(0.22, l + (-0.12 * clamp(0, calc((l - 0.714) * 1000), 1) + 0.06), 0.88)) c h
209
+ );
237
210
  }
238
211
 
239
- [data-component="link"] {
240
- color: var(--color-primary);
241
- text-decoration: none;
242
- font-size: var(--font-size-sm);
212
+ [data-component="button"] [data-slot="icon"] {
213
+ width: 16px;
214
+ height: 16px;
243
215
  }
244
216
 
245
- [data-component="link"]:hover {
246
- text-decoration: underline;
217
+ [data-component="button"] [data-slot="icon"] svg {
218
+ width: 100%;
219
+ height: 100%;
247
220
  }
248
221
 
249
- [data-component="form-footer"] {
250
- display: flex;
251
- gap: 1rem;
252
- font-size: 0.75rem;
253
- align-items: center;
254
- justify-content: center;
222
+ [data-component="form"] {
223
+ max-width: 100%;
224
+ display: flex;
225
+ flex-direction: column;
226
+ gap: 1rem;
227
+ margin: 0;
255
228
  }
256
229
 
257
- [data-component="form-footer"]:has(> :nth-child(2)) {
258
- justify-content: space-between;
230
+ [data-component="form-alert"] {
231
+ height: 2.5rem;
232
+ display: flex;
233
+ align-items: center;
234
+ padding: 0 1rem;
235
+ border-radius: calc(var(--border-radius) * 0.25rem);
236
+ background: var(--color-background-error);
237
+ color: var(--color-error);
238
+ text-align: left;
239
+ font-size: 0.75rem;
240
+ gap: 0.5rem;
259
241
  }
260
242
 
261
- [data-component="logo-default"] {
262
- margin: 0 auto;
263
- height: 2.5rem;
264
- width: auto;
243
+ [data-component="form-alert"][data-color="success"] {
244
+ background: var(--color-background-success);
245
+ color: var(--color-success);
265
246
  }
266
247
 
267
- @media (prefers-color-scheme: light) {
268
- [data-component="logo-default"] {
269
- color: var(--color-high);
270
- }
248
+ [data-component="form-alert"][data-color="success"] [data-slot="icon-success"] {
249
+ display: block;
271
250
  }
272
251
 
273
- @media (prefers-color-scheme: dark) {
274
- [data-component="logo-default"] {
275
- color: var(--color-high);
276
- }
252
+ [data-component="form-alert"][data-color="success"] [data-slot="icon-danger"] {
253
+ display: none;
277
254
  }
278
255
 
279
- /* Logo theme switching */
280
- [data-component="logo"][data-mode="light"] {
281
- display: none;
256
+ [data-component="form-alert"]:has([data-slot="message"]:empty) {
257
+ display: none;
282
258
  }
283
259
 
284
- [data-component="logo"][data-mode="dark"] {
285
- display: block;
260
+ [data-component="form-alert"] [data-slot="icon-success"],
261
+ [data-component="form-alert"] [data-slot="icon-danger"] {
262
+ width: 1rem;
263
+ height: 1rem;
286
264
  }
287
265
 
288
- @media (prefers-color-scheme: light) {
289
- [data-component="logo"][data-mode="light"] {
290
- display: block;
291
- }
292
-
293
- [data-component="logo"][data-mode="dark"] {
294
- display: none;
295
- }
266
+ [data-component="form-alert"] [data-slot="icon-success"] {
267
+ display: none;
268
+ }
269
+
270
+ [data-component="form-footer"] {
271
+ display: flex;
272
+ gap: 1rem;
273
+ font-size: 0.75rem;
274
+ align-items: center;
275
+ justify-content: center;
276
+ }
277
+
278
+ [data-component="form-footer"]:has(> :nth-child(2)) {
279
+ justify-content: space-between;
280
+ }
281
+
282
+ [data-component="title"] {
283
+ font-size: var(--font-size-2xl);
284
+ font-weight: 600;
285
+ margin: 0 0 0.5rem 0;
286
+ color: var(--color-high);
287
+ text-align: center;
288
+ }
289
+
290
+ [data-component="description"] {
291
+ font-size: var(--font-size-sm);
292
+ color: var(--color-high);
293
+ margin: 0 0 1.5rem 0;
294
+ text-align: center;
295
+ opacity: 0.8;
296
296
  }
297
297
  `;
298
298
  /**
package/dist/ui/code.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Layout, renderToHTML } from "./base.js";
2
+ import { FormAlert } from "./form.js";
2
3
  import { jsx, jsxs } from "preact/jsx-runtime";
3
4
 
4
5
  //#region src/ui/code.tsx
@@ -18,74 +19,6 @@ const DEFAULT_COPY = {
18
19
  code_resend: "Resend"
19
20
  };
20
21
  /**
21
- * FormAlert component for displaying messages
22
- */
23
- const FormAlert = ({ message, color = "danger" }) => {
24
- if (!message) return null;
25
- return /* @__PURE__ */ jsxs("div", {
26
- "data-component": "form-alert",
27
- "data-color": color,
28
- children: [/* @__PURE__ */ jsx("i", {
29
- "data-slot": color === "success" ? "icon-success" : "icon-danger",
30
- children: color === "success" ? /* @__PURE__ */ jsx("svg", {
31
- fill: "none",
32
- stroke: "currentColor",
33
- viewBox: "0 0 24 24",
34
- xmlns: "http://www.w3.org/2000/svg",
35
- "aria-label": "Success",
36
- role: "img",
37
- children: /* @__PURE__ */ jsx("path", {
38
- strokeLinecap: "round",
39
- strokeLinejoin: "round",
40
- strokeWidth: 2,
41
- d: "M5 13l4 4L19 7"
42
- })
43
- }) : /* @__PURE__ */ jsx("svg", {
44
- fill: "none",
45
- stroke: "currentColor",
46
- viewBox: "0 0 24 24",
47
- xmlns: "http://www.w3.org/2000/svg",
48
- "aria-label": "Error",
49
- role: "img",
50
- children: /* @__PURE__ */ jsx("path", {
51
- strokeLinecap: "round",
52
- strokeLinejoin: "round",
53
- strokeWidth: 2,
54
- d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.232 16.5c-.77.833.192 2.5 1.732 2.5z"
55
- })
56
- })
57
- }), /* @__PURE__ */ jsx("span", {
58
- "data-slot": "message",
59
- children: message
60
- })]
61
- });
62
- };
63
- /**
64
- * Input component with consistent styling
65
- */
66
- const Input = ({ type, name, placeholder, value, required, autoComplete, autoFocus,...props }) => /* @__PURE__ */ jsx("input", {
67
- type,
68
- name,
69
- placeholder,
70
- value,
71
- required,
72
- autoComplete,
73
- "data-component": "input",
74
- ...props
75
- });
76
- /**
77
- * Button component with consistent styling
78
- */
79
- const Button = ({ type = "submit", children,...props }) => /* @__PURE__ */ jsx("button", {
80
- type,
81
- "data-component": "button",
82
- ...props,
83
- children
84
- });
85
- /**
86
- * Link component with consistent styling
87
- */
88
- /**
89
22
  * Gets the appropriate error message for display
90
23
  */
91
24
  const getErrorMessage = (error, copy) => {
@@ -129,7 +62,8 @@ const CodeUI = (options) => {
129
62
  message: success.message,
130
63
  color: "success"
131
64
  }) : /* @__PURE__ */ jsx(FormAlert, { message: getErrorMessage(error, copy) }),
132
- /* @__PURE__ */ jsx(Input, {
65
+ /* @__PURE__ */ jsx("input", {
66
+ "data-component": "input",
133
67
  type: mode === "email" ? "email" : "tel",
134
68
  name: mode,
135
69
  placeholder: copy.email_placeholder,
@@ -142,17 +76,13 @@ const CodeUI = (options) => {
142
76
  name: "action",
143
77
  value: "request"
144
78
  }),
145
- /* @__PURE__ */ jsx(Button, {
79
+ /* @__PURE__ */ jsx("button", {
80
+ "data-component": "button",
146
81
  type: "submit",
147
82
  children: copy.button_continue
148
83
  }),
149
84
  /* @__PURE__ */ jsx("p", {
150
- style: {
151
- fontSize: "0.875rem",
152
- color: "var(--color-high)",
153
- textAlign: "center",
154
- margin: "1rem 0 0 0"
155
- },
85
+ "data-component": "description",
156
86
  children: copy.code_info
157
87
  })
158
88
  ]
@@ -177,7 +107,8 @@ const CodeUI = (options) => {
177
107
  type: "hidden",
178
108
  value: "verify"
179
109
  }),
180
- /* @__PURE__ */ jsx(Input, {
110
+ /* @__PURE__ */ jsx("input", {
111
+ "data-component": "input",
181
112
  type: "text",
182
113
  name: "code",
183
114
  placeholder: copy.code_placeholder,
@@ -187,10 +118,10 @@ const CodeUI = (options) => {
187
118
  maxLength: 6,
188
119
  minLength: 6,
189
120
  pattern: "[0-9]{6}",
190
- autoFocus: true,
191
121
  required: true
192
122
  }),
193
- /* @__PURE__ */ jsx(Button, {
123
+ /* @__PURE__ */ jsx("button", {
124
+ "data-component": "button",
194
125
  type: "submit",
195
126
  children: copy.button_continue
196
127
  })
@@ -210,12 +141,9 @@ const CodeUI = (options) => {
210
141
  }),
211
142
  /* @__PURE__ */ jsxs("div", {
212
143
  "data-component": "form-footer",
213
- children: [/* @__PURE__ */ jsx("span", {
214
- style: { fontSize: "0.875rem" },
215
- children: copy.code_didnt_get
216
- }), /* @__PURE__ */ jsx(Button, {
144
+ children: [/* @__PURE__ */ jsx("span", { children: copy.code_didnt_get }), /* @__PURE__ */ jsx("button", {
145
+ "data-component": "button",
217
146
  type: "submit",
218
- "data-component": "link",
219
147
  children: copy.code_resend
220
148
  })]
221
149
  })
package/dist/ui/form.d.ts CHANGED
@@ -23,7 +23,6 @@ interface FormAlertProps {
23
23
  }
24
24
  /**
25
25
  * Form alert component that displays error or success messages.
26
- * Returns a Preact component or null if no message.
27
26
  */
28
27
  declare const FormAlert: ({
29
28
  message,
package/dist/ui/form.js CHANGED
@@ -2,56 +2,46 @@ import { jsx, jsxs } from "preact/jsx-runtime";
2
2
 
3
3
  //#region src/ui/form.tsx
4
4
  /**
5
- * Success icon component showing a checkmark in a circle.
6
- * Used for positive feedback messages.
7
- */
8
- const SuccessIcon = () => /* @__PURE__ */ jsx("svg", {
9
- "aria-hidden": "true",
10
- "data-slot": "icon-success",
11
- fill: "none",
12
- stroke: "currentColor",
13
- strokeWidth: "1.5",
14
- viewBox: "0 0 24 24",
15
- xmlns: "http://www.w3.org/2000/svg",
16
- children: /* @__PURE__ */ jsx("path", {
17
- d: "M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z",
18
- strokeLinecap: "round",
19
- strokeLinejoin: "round"
20
- })
21
- });
22
- /**
23
- * Danger icon component showing an exclamation mark in a circle.
24
- * Used for error and warning messages.
25
- */
26
- const DangerIcon = () => /* @__PURE__ */ jsx("svg", {
27
- "aria-hidden": "true",
28
- "data-slot": "icon-danger",
29
- fill: "none",
30
- stroke: "currentColor",
31
- strokeWidth: "1.5",
32
- viewBox: "0 0 24 24",
33
- xmlns: "http://www.w3.org/2000/svg",
34
- children: /* @__PURE__ */ jsx("path", {
35
- d: "M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z",
36
- strokeLinecap: "round",
37
- strokeLinejoin: "round"
38
- })
39
- });
40
- /**
41
5
  * Form alert component that displays error or success messages.
42
- * Returns a Preact component or null if no message.
43
6
  */
44
7
  const FormAlert = ({ message, color = "danger" }) => {
45
- if (!message) return null;
46
8
  return /* @__PURE__ */ jsxs("div", {
47
- "aria-live": "polite",
48
- "data-color": color,
49
9
  "data-component": "form-alert",
50
- role: "alert",
51
- children: [color === "success" ? /* @__PURE__ */ jsx(SuccessIcon, {}) : /* @__PURE__ */ jsx(DangerIcon, {}), /* @__PURE__ */ jsx("span", {
52
- "data-slot": "message",
53
- children: message
54
- })]
10
+ "data-color": color,
11
+ children: [
12
+ /* @__PURE__ */ jsx("svg", {
13
+ "aria-hidden": "true",
14
+ "data-slot": "icon-success",
15
+ xmlns: "http://www.w3.org/2000/svg",
16
+ fill: "none",
17
+ viewBox: "0 0 24 24",
18
+ "stroke-width": "1.5",
19
+ stroke: "currentColor",
20
+ children: /* @__PURE__ */ jsx("path", {
21
+ "stroke-linecap": "round",
22
+ "stroke-linejoin": "round",
23
+ d: "M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
24
+ })
25
+ }),
26
+ /* @__PURE__ */ jsx("svg", {
27
+ "aria-hidden": "true",
28
+ "data-slot": "icon-danger",
29
+ xmlns: "http://www.w3.org/2000/svg",
30
+ fill: "none",
31
+ viewBox: "0 0 24 24",
32
+ "stroke-width": "1.5",
33
+ stroke: "currentColor",
34
+ children: /* @__PURE__ */ jsx("path", {
35
+ "stroke-linecap": "round",
36
+ "stroke-linejoin": "round",
37
+ d: "M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z"
38
+ })
39
+ }),
40
+ /* @__PURE__ */ jsx("span", {
41
+ "data-slot": "message",
42
+ children: message
43
+ })
44
+ ]
55
45
  });
56
46
  };
57
47
 
@@ -33,48 +33,27 @@ const PasskeyUI = (options) => {
33
33
  window.addEventListener("load", async () => {
34
34
  const { startAuthentication } = SimpleWebAuthnBrowser;
35
35
  const authorizeForm = document.getElementById("authorizeForm");
36
+ const message = document.querySelector("[data-slot='message']");
36
37
  const origin = window.location.origin;
37
38
  const rpID = window.location.hostname;
38
39
 
39
- const showMessage = (msg) => {
40
- const messageEl = document.querySelector("[data-slot='message']");
41
- if (messageEl) {
42
- messageEl.innerHTML = msg;
43
- } else {
44
- // Create alert if it doesn't exist
45
- const alertDiv = document.createElement("div");
46
- alertDiv.setAttribute("data-component", "form-alert");
47
- alertDiv.setAttribute("role", "alert");
48
- alertDiv.setAttribute("aria-live", "polite");
49
- alertDiv.setAttribute("data-color", "error");
50
- alertDiv.innerHTML = '<span data-slot="message">' + msg + '</span>';
51
- authorizeForm.insertBefore(alertDiv, authorizeForm.firstChild);
52
- }
53
- };
54
-
55
- const clearMessage = () => {
56
- const alertDiv = document.querySelector("[data-component='form-alert']");
57
- if (alertDiv) {
58
- alertDiv.remove();
59
- }
60
- };
61
-
62
40
  authorizeForm.addEventListener("submit", async (e) => {
63
41
  e.preventDefault();
64
42
  const formData = new FormData(authorizeForm);
65
43
  const email = formData.get("email");
66
- clearMessage();
44
+
45
+ message.textContent = "";
67
46
 
68
47
  // GET authentication options from the endpoint that calls
69
48
  // @simplewebauthn/server -> generateAuthenticationOptions()
70
49
  const resp = await fetch(
71
- "./passkey/authenticate-options?userId=" + email + "&rpID=" + rpID
50
+ "./authenticate-options?userId=" + email + "&rpID=" + rpID
72
51
  );
73
52
 
74
53
  const optionsJSON = await resp.json();
75
54
 
76
55
  if (optionsJSON.error) {
77
- showMessage(optionsJSON.error);
56
+ message.textContent = optionsJSON.error;
78
57
  return;
79
58
  }
80
59
 
@@ -83,12 +62,12 @@ const PasskeyUI = (options) => {
83
62
  // Pass the options to the authenticator and wait for a response
84
63
  attResp = await startAuthentication({ optionsJSON });
85
64
  } catch (error) {
86
- showMessage(error);
65
+ message.textContent = error;
87
66
  throw error;
88
67
  }
89
68
 
90
69
  const verificationResp = await fetch(
91
- "./passkey/authenticate-verify?userId=" +
70
+ "./authenticate-verify?userId=" +
92
71
  email +
93
72
  "&rpID=" +
94
73
  rpID +
@@ -116,25 +95,35 @@ const PasskeyUI = (options) => {
116
95
  );
117
96
  try {
118
97
  const errorData = await verificationResp.json();
119
- showMessage(errorData.error);
98
+ message.textContent = errorData.error;
120
99
  } catch (error) {
121
- showMessage("Something went wrong");
100
+ message.textContent = "Something went wrong";
122
101
  }
123
102
  }
124
103
  });
125
104
  });
126
105
  ` } }),
127
- /* @__PURE__ */ jsx("h1", { children: copy.authorize_title }),
128
- /* @__PURE__ */ jsx("p", { children: copy.authorize_description }),
106
+ /* @__PURE__ */ jsx("h1", {
107
+ "data-component": "title",
108
+ children: copy.authorize_title
109
+ }),
110
+ /* @__PURE__ */ jsx("p", {
111
+ "data-component": "description",
112
+ children: copy.authorize_description
113
+ }),
129
114
  /* @__PURE__ */ jsxs("form", {
130
115
  id: "authorizeForm",
131
116
  "data-component": "form",
132
117
  children: [
133
118
  /* @__PURE__ */ jsx(FormAlert, {}),
134
119
  /* @__PURE__ */ jsx("input", {
120
+ id: "auth-email",
135
121
  "data-component": "input",
136
122
  type: "email",
137
123
  name: "email",
124
+ "aria-required": "true",
125
+ "aria-describedby": "auth-email-help",
126
+ autoComplete: "email",
138
127
  required: true,
139
128
  placeholder: copy.input_email
140
129
  }),
@@ -171,54 +160,32 @@ const PasskeyUI = (options) => {
171
160
  window.addEventListener("load", async () => {
172
161
  const { startRegistration } = SimpleWebAuthnBrowser;
173
162
  const registerForm = document.getElementById("registerForm");
163
+ const message = document.querySelector("[data-slot='message']");
174
164
  const origin = window.location.origin;
175
165
  const rpID = window.location.hostname;
176
166
 
177
- const showMessage = (msg) => {
178
- const messageEl = document.querySelector("[data-slot='message']");
179
- if (messageEl) {
180
- messageEl.innerHTML = msg;
181
- } else {
182
- // Create alert if it doesn't exist
183
- const alertDiv = document.createElement("div");
184
- alertDiv.setAttribute("data-component", "form-alert");
185
- alertDiv.setAttribute("role", "alert");
186
- alertDiv.setAttribute("aria-live", "polite");
187
- alertDiv.setAttribute("data-color", "error");
188
- alertDiv.innerHTML = '<span data-slot="message">' + msg + '</span>';
189
- registerForm.insertBefore(alertDiv, registerForm.firstChild);
190
- }
191
- };
192
-
193
- const clearMessage = () => {
194
- const alertDiv = document.querySelector("[data-component='form-alert']");
195
- if (alertDiv) {
196
- alertDiv.remove();
197
- }
198
- };
199
-
200
167
  // Start registration when the user clicks a button
201
168
  const register = async (otherDevice = false) => {
202
169
  const formData = new FormData(registerForm);
203
170
  const email = formData.get("email");
204
- clearMessage();
171
+ message.textContent = "";
205
172
 
206
173
  // GET registration options from the endpoint that calls
207
174
  // @simplewebauthn/server -> generateRegistrationOptions()
208
175
  const resp = await fetch(
209
- "./passkey/register-request?userId=" +
176
+ "./register-request?userId=" +
210
177
  email +
211
178
  "&origin=" +
212
179
  origin +
213
180
  "&rpID=" +
214
181
  rpID +
215
182
  "&otherDevice=" +
216
- otherDevice,
183
+ otherDevice
217
184
  );
218
185
  const optionsJSON = await resp.json();
219
186
 
220
187
  if (optionsJSON.error) {
221
- showMessage(optionsJSON.error);
188
+ message.textContent = optionsJSON.error;
222
189
  return;
223
190
  }
224
191
 
@@ -227,7 +194,7 @@ const PasskeyUI = (options) => {
227
194
  // Pass the options to the authenticator and wait for a response
228
195
  attResp = await startRegistration({ optionsJSON });
229
196
  } catch (error) {
230
- showMessage(error);
197
+ message.textContent = error;
231
198
  throw error;
232
199
  }
233
200
 
@@ -235,7 +202,7 @@ const PasskeyUI = (options) => {
235
202
  // @simplewebauthn/server -> verifyRegistrationResponse()
236
203
  try {
237
204
  const verificationResp = await fetch(
238
- "./passkey/register-verify?userId=" +
205
+ "./register-verify?userId=" +
239
206
  email +
240
207
  "&origin=" +
241
208
  origin +
@@ -263,14 +230,14 @@ const PasskeyUI = (options) => {
263
230
  );
264
231
  try {
265
232
  const errorData = await verificationResp.json();
266
- showMessage(errorData.error);
233
+ message.textContent = errorData.error;
267
234
  } catch (error) {
268
- showMessage("Something went wrong");
235
+ message.textContent = "Something went wrong";
269
236
  }
270
237
  }
271
238
  } catch (error) {
272
239
  console.error(error);
273
- showMessage("Something went wrong");
240
+ message.textContent = "Something went wrong";
274
241
  }
275
242
  };
276
243
 
@@ -280,17 +247,27 @@ const PasskeyUI = (options) => {
280
247
  });
281
248
  });
282
249
  ` } }),
283
- /* @__PURE__ */ jsx("h1", { children: copy.register_title }),
284
- /* @__PURE__ */ jsx("p", { children: copy.register_description }),
250
+ /* @__PURE__ */ jsx("h1", {
251
+ "data-component": "title",
252
+ children: copy.register_title
253
+ }),
254
+ /* @__PURE__ */ jsx("p", {
255
+ "data-component": "description",
256
+ children: copy.register_description
257
+ }),
285
258
  /* @__PURE__ */ jsxs("form", {
286
259
  id: "registerForm",
287
260
  "data-component": "form",
288
261
  children: [
289
262
  /* @__PURE__ */ jsx(FormAlert, {}),
290
263
  /* @__PURE__ */ jsx("input", {
264
+ id: "reg-email",
291
265
  "data-component": "input",
292
266
  type: "email",
293
267
  name: "email",
268
+ "aria-required": "true",
269
+ "aria-describedby": "reg-email-help",
270
+ autoComplete: "email",
294
271
  required: true,
295
272
  placeholder: copy.input_email
296
273
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@draftlab/auth",
3
- "version": "0.1.5",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "Core implementation for @draftlab/auth",
6
6
  "author": "Matheus Pergoli",