@draftlab/auth 0.0.4 → 0.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.
- package/dist/error.d.ts +1 -1
- package/dist/provider/code.d.ts +26 -10
- package/dist/provider/code.js +26 -13
- package/dist/provider/password.d.ts +2 -2
- package/dist/storage/memory.d.ts +1 -1
- package/dist/storage/unstorage.d.ts +1 -1
- package/dist/storage/unstorage.js +1 -1
- package/dist/themes/theme.d.ts +2 -2
- package/dist/ui/base.d.ts +22 -35
- package/dist/ui/base.js +163 -177
- package/dist/ui/code.d.ts +22 -135
- package/dist/ui/code.js +199 -160
- package/dist/ui/form.d.ts +8 -6
- package/dist/ui/form.js +44 -46
- package/dist/ui/icon.d.ts +7 -84
- package/dist/ui/icon.js +56 -178
- package/dist/ui/password.d.ts +29 -34
- package/dist/ui/password.js +340 -236
- package/dist/ui/select.d.ts +19 -218
- package/dist/ui/select.js +72 -259
- package/package.json +4 -2
package/dist/ui/code.d.ts
CHANGED
|
@@ -1,155 +1,42 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CodeProviderOptions } from "../provider/code.js";
|
|
2
2
|
|
|
3
3
|
//#region src/ui/code.d.ts
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
* All text can be customized via the copy prop.
|
|
6
|
+
* Type for customizable UI copy text
|
|
8
7
|
*/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
code_invalid: string;
|
|
22
|
-
/** Success message prefix when code is initially sent */
|
|
23
|
-
code_sent: string;
|
|
24
|
-
/** Success message prefix when code is resent */
|
|
25
|
-
code_resent: string;
|
|
26
|
-
/** Text asking if user didn't receive the code */
|
|
27
|
-
code_didnt_get: string;
|
|
28
|
-
/** Text for the resend code button */
|
|
29
|
-
code_resend: string;
|
|
30
|
-
};
|
|
31
|
-
/**
|
|
32
|
-
* Type for customizable UI copy text.
|
|
33
|
-
* All properties are optional to allow partial customization.
|
|
34
|
-
*/
|
|
35
|
-
type CodeUICopy = typeof DEFAULT_COPY;
|
|
8
|
+
interface CodeUICopy {
|
|
9
|
+
readonly email_placeholder: string;
|
|
10
|
+
readonly email_invalid: string;
|
|
11
|
+
readonly button_continue: string;
|
|
12
|
+
readonly code_info: string;
|
|
13
|
+
readonly code_placeholder: string;
|
|
14
|
+
readonly code_invalid: string;
|
|
15
|
+
readonly code_sent: string;
|
|
16
|
+
readonly code_resent: string;
|
|
17
|
+
readonly code_didnt_get: string;
|
|
18
|
+
readonly code_resend: string;
|
|
19
|
+
}
|
|
36
20
|
/**
|
|
37
|
-
* Input mode for the contact field
|
|
38
|
-
* Determines the input type and validation behavior.
|
|
21
|
+
* Input mode for the contact field
|
|
39
22
|
*/
|
|
40
23
|
type CodeUIMode = "email" | "phone";
|
|
41
24
|
/**
|
|
42
|
-
* Configuration options for the CodeUI component
|
|
25
|
+
* Configuration options for the CodeUI component
|
|
43
26
|
*/
|
|
44
|
-
interface CodeUIOptions {
|
|
27
|
+
interface CodeUIOptions extends Pick<CodeProviderOptions, "sendCode"> {
|
|
45
28
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* @param claims - User contact information (email, phone, etc.)
|
|
50
|
-
* @param code - The generated PIN code to send
|
|
51
|
-
* @returns Promise resolving to undefined on success, or error object on failure
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* ```ts
|
|
55
|
-
* sendCode: async (claims, code) => {
|
|
56
|
-
* if (claims.email) {
|
|
57
|
-
* await emailService.send({
|
|
58
|
-
* to: claims.email,
|
|
59
|
-
* subject: 'Your verification code',
|
|
60
|
-
* text: `Your PIN code is: ${code}`
|
|
61
|
-
* })
|
|
62
|
-
* } else if (claims.phone) {
|
|
63
|
-
* await smsService.send(claims.phone, `PIN: ${code}`)
|
|
64
|
-
* } else {
|
|
65
|
-
* return {
|
|
66
|
-
* type: "invalid_claim",
|
|
67
|
-
* key: "contact",
|
|
68
|
-
* value: "Email or phone required"
|
|
69
|
-
* }
|
|
70
|
-
* }
|
|
71
|
-
* }
|
|
72
|
-
* ```
|
|
29
|
+
* Input mode determining the type of contact information to collect
|
|
30
|
+
* @default "email"
|
|
73
31
|
*/
|
|
74
|
-
|
|
32
|
+
readonly mode?: CodeUIMode;
|
|
75
33
|
/**
|
|
76
|
-
* Custom text copy for UI labels and
|
|
77
|
-
* Allows full customization of all displayed text.
|
|
78
|
-
*
|
|
79
|
-
* @example
|
|
80
|
-
* ```ts
|
|
81
|
-
* copy: {
|
|
82
|
-
* email_placeholder: "Enter your email address",
|
|
83
|
-
* code_info: "Check your email for a 6-digit verification code",
|
|
84
|
-
* button_continue: "Verify",
|
|
85
|
-
* code_invalid: "The code you entered is incorrect"
|
|
86
|
-
* }
|
|
87
|
-
* ```
|
|
34
|
+
* Custom text copy for UI labels, messages, and errors
|
|
88
35
|
*/
|
|
89
36
|
readonly copy?: Partial<CodeUICopy>;
|
|
90
|
-
/**
|
|
91
|
-
* Input mode determining the type of contact information to collect.
|
|
92
|
-
*
|
|
93
|
-
* @default "email"
|
|
94
|
-
*
|
|
95
|
-
* @example
|
|
96
|
-
* ```ts
|
|
97
|
-
* mode: "phone" // Collect phone numbers instead of emails
|
|
98
|
-
* ```
|
|
99
|
-
*/
|
|
100
|
-
readonly mode?: CodeUIMode;
|
|
101
37
|
}
|
|
102
38
|
/**
|
|
103
|
-
* Creates a complete UI configuration for PIN code authentication
|
|
104
|
-
* Provides pre-built forms for collecting user contact info and verifying PIN codes.
|
|
105
|
-
*
|
|
106
|
-
* @param options - Configuration options for the UI
|
|
107
|
-
* @returns Complete CodeProvider configuration with UI handlers
|
|
108
|
-
*
|
|
109
|
-
* @example
|
|
110
|
-
* ```ts
|
|
111
|
-
* // Basic email-based PIN authentication
|
|
112
|
-
* const emailCodeUI = CodeUI({
|
|
113
|
-
* sendCode: async (claims, code) => {
|
|
114
|
-
* await emailService.send(claims.email, `Code: ${code}`)
|
|
115
|
-
* }
|
|
116
|
-
* })
|
|
117
|
-
*
|
|
118
|
-
* // Phone-based PIN authentication with custom copy
|
|
119
|
-
* const phoneCodeUI = CodeUI({
|
|
120
|
-
* mode: "phone",
|
|
121
|
-
* copy: {
|
|
122
|
-
* email_placeholder: "Phone number",
|
|
123
|
-
* code_info: "We'll send a verification code via SMS",
|
|
124
|
-
* email_invalid: "Please enter a valid phone number"
|
|
125
|
-
* },
|
|
126
|
-
* sendCode: async (claims, code) => {
|
|
127
|
-
* await smsService.send(claims.phone, `Verification: ${code}`)
|
|
128
|
-
* }
|
|
129
|
-
* })
|
|
130
|
-
*
|
|
131
|
-
* // Multi-mode authentication
|
|
132
|
-
* const flexibleCodeUI = CodeUI({
|
|
133
|
-
* copy: {
|
|
134
|
-
* email_placeholder: "Email or phone number",
|
|
135
|
-
* code_info: "We'll send a code to your email or phone"
|
|
136
|
-
* },
|
|
137
|
-
* sendCode: async (claims, code) => {
|
|
138
|
-
* if (claims.email && claims.email.includes('@')) {
|
|
139
|
-
* await emailService.send(claims.email, `Code: ${code}`)
|
|
140
|
-
* } else if (claims.email) {
|
|
141
|
-
* // Treat as phone number if no @ symbol
|
|
142
|
-
* await smsService.send(claims.email, `Code: ${code}`)
|
|
143
|
-
* } else {
|
|
144
|
-
* return {
|
|
145
|
-
* type: "invalid_claim",
|
|
146
|
-
* key: "contact",
|
|
147
|
-
* value: "Email or phone required"
|
|
148
|
-
* }
|
|
149
|
-
* }
|
|
150
|
-
* }
|
|
151
|
-
* })
|
|
152
|
-
* ```
|
|
39
|
+
* Creates a complete UI configuration for PIN code authentication
|
|
153
40
|
*/
|
|
154
41
|
declare const CodeUI: (options: CodeUIOptions) => CodeProviderOptions;
|
|
155
42
|
//#endregion
|
package/dist/ui/code.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { FormAlert } from "./form.js";
|
|
1
|
+
import { Layout, renderToHTML } from "./base.js";
|
|
2
|
+
import { jsx, jsxs } from "preact/jsx-runtime";
|
|
4
3
|
|
|
5
|
-
//#region src/ui/code.
|
|
4
|
+
//#region src/ui/code.tsx
|
|
6
5
|
/**
|
|
7
|
-
* Default text copy for the PIN code authentication UI
|
|
8
|
-
* All text can be customized via the copy prop.
|
|
6
|
+
* Default text copy for the PIN code authentication UI
|
|
9
7
|
*/
|
|
10
8
|
const DEFAULT_COPY = {
|
|
11
9
|
email_placeholder: "Email",
|
|
@@ -20,174 +18,215 @@ const DEFAULT_COPY = {
|
|
|
20
18
|
code_resend: "Resend"
|
|
21
19
|
};
|
|
22
20
|
/**
|
|
23
|
-
*
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
*
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
+
* Gets the appropriate error message for display
|
|
90
|
+
*/
|
|
91
|
+
const getErrorMessage = (error, copy) => {
|
|
92
|
+
if (!error?.type) return void 0;
|
|
93
|
+
switch (error.type) {
|
|
94
|
+
case "invalid_code": return copy.code_invalid;
|
|
95
|
+
case "invalid_claim": return copy.email_invalid;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Gets the appropriate success message for display
|
|
100
|
+
*/
|
|
101
|
+
const getSuccessMessage = (state, copy) => {
|
|
102
|
+
if (state.type === "start" || !state.claims) return void 0;
|
|
103
|
+
const contact = state.claims.email || state.claims.phone || "";
|
|
104
|
+
const prefix = state.resend ? copy.code_resent : copy.code_sent;
|
|
105
|
+
return {
|
|
106
|
+
message: `${prefix}${contact}`,
|
|
107
|
+
contact
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Creates a complete UI configuration for PIN code authentication
|
|
73
112
|
*/
|
|
74
113
|
const CodeUI = (options) => {
|
|
75
114
|
const copy = {
|
|
76
115
|
...DEFAULT_COPY,
|
|
77
116
|
...options.copy
|
|
78
117
|
};
|
|
79
|
-
const
|
|
118
|
+
const mode = options.mode || "email";
|
|
80
119
|
/**
|
|
81
|
-
*
|
|
120
|
+
* Renders the start form for collecting contact information
|
|
82
121
|
*/
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
122
|
+
const renderStart = (form, error, state) => {
|
|
123
|
+
const success = getSuccessMessage(state || { type: "start" }, copy);
|
|
124
|
+
return /* @__PURE__ */ jsx(Layout, { children: /* @__PURE__ */ jsxs("form", {
|
|
125
|
+
"data-component": "form",
|
|
126
|
+
method: "post",
|
|
127
|
+
children: [
|
|
128
|
+
success ? /* @__PURE__ */ jsx(FormAlert, {
|
|
129
|
+
message: success.message,
|
|
130
|
+
color: "success"
|
|
131
|
+
}) : /* @__PURE__ */ jsx(FormAlert, { message: getErrorMessage(error, copy) }),
|
|
132
|
+
/* @__PURE__ */ jsx(Input, {
|
|
133
|
+
type: mode === "email" ? "email" : "tel",
|
|
134
|
+
name: mode,
|
|
135
|
+
placeholder: copy.email_placeholder,
|
|
136
|
+
value: form?.get(mode)?.toString() || "",
|
|
137
|
+
autoComplete: mode,
|
|
138
|
+
required: true
|
|
139
|
+
}),
|
|
140
|
+
/* @__PURE__ */ jsx("input", {
|
|
141
|
+
type: "hidden",
|
|
142
|
+
name: "action",
|
|
143
|
+
value: "request"
|
|
144
|
+
}),
|
|
145
|
+
/* @__PURE__ */ jsx(Button, {
|
|
146
|
+
type: "submit",
|
|
147
|
+
children: copy.button_continue
|
|
148
|
+
}),
|
|
149
|
+
/* @__PURE__ */ jsx("p", {
|
|
150
|
+
style: {
|
|
151
|
+
fontSize: "0.875rem",
|
|
152
|
+
color: "var(--color-high)",
|
|
153
|
+
textAlign: "center",
|
|
154
|
+
margin: "1rem 0 0 0"
|
|
155
|
+
},
|
|
156
|
+
children: copy.code_info
|
|
157
|
+
})
|
|
158
|
+
]
|
|
159
|
+
}) });
|
|
98
160
|
};
|
|
99
161
|
/**
|
|
100
|
-
*
|
|
162
|
+
* Renders the code verification form
|
|
101
163
|
*/
|
|
102
|
-
const
|
|
103
|
-
|
|
164
|
+
const renderCode = (_form, error, state) => {
|
|
165
|
+
const success = getSuccessMessage(state, copy);
|
|
166
|
+
const contact = state.type === "code" ? state.claims?.[mode] || "" : "";
|
|
167
|
+
return /* @__PURE__ */ jsxs(Layout, { children: [/* @__PURE__ */ jsxs("form", {
|
|
168
|
+
"data-component": "form",
|
|
169
|
+
method: "post",
|
|
170
|
+
children: [
|
|
171
|
+
success ? /* @__PURE__ */ jsx(FormAlert, {
|
|
172
|
+
message: success.message,
|
|
173
|
+
color: "success"
|
|
174
|
+
}) : /* @__PURE__ */ jsx(FormAlert, { message: getErrorMessage(error, copy) }),
|
|
175
|
+
/* @__PURE__ */ jsx("input", {
|
|
176
|
+
name: "action",
|
|
177
|
+
type: "hidden",
|
|
178
|
+
value: "verify"
|
|
179
|
+
}),
|
|
180
|
+
/* @__PURE__ */ jsx(Input, {
|
|
181
|
+
type: "text",
|
|
182
|
+
name: "code",
|
|
183
|
+
placeholder: copy.code_placeholder,
|
|
184
|
+
"aria-label": "6-digit verification code",
|
|
185
|
+
autoComplete: "one-time-code",
|
|
186
|
+
inputMode: "numeric",
|
|
187
|
+
maxLength: 6,
|
|
188
|
+
minLength: 6,
|
|
189
|
+
pattern: "[0-9]{6}",
|
|
190
|
+
autoFocus: true,
|
|
191
|
+
required: true
|
|
192
|
+
}),
|
|
193
|
+
/* @__PURE__ */ jsx(Button, {
|
|
194
|
+
type: "submit",
|
|
195
|
+
children: copy.button_continue
|
|
196
|
+
})
|
|
197
|
+
]
|
|
198
|
+
}), /* @__PURE__ */ jsxs("form", {
|
|
199
|
+
method: "post",
|
|
200
|
+
children: [
|
|
201
|
+
/* @__PURE__ */ jsx("input", {
|
|
202
|
+
name: "action",
|
|
203
|
+
type: "hidden",
|
|
204
|
+
value: "resend"
|
|
205
|
+
}),
|
|
206
|
+
/* @__PURE__ */ jsx("input", {
|
|
207
|
+
name: mode,
|
|
208
|
+
type: "hidden",
|
|
209
|
+
value: contact
|
|
210
|
+
}),
|
|
211
|
+
/* @__PURE__ */ jsxs("div", {
|
|
212
|
+
"data-component": "form-footer",
|
|
213
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
214
|
+
style: { fontSize: "0.875rem" },
|
|
215
|
+
children: copy.code_didnt_get
|
|
216
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
217
|
+
type: "submit",
|
|
218
|
+
"data-component": "link",
|
|
219
|
+
children: copy.code_resend
|
|
220
|
+
})]
|
|
221
|
+
})
|
|
222
|
+
]
|
|
223
|
+
})] });
|
|
104
224
|
};
|
|
105
225
|
return {
|
|
106
226
|
sendCode: options.sendCode,
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const inputAttrs = getInputAttributes();
|
|
111
|
-
const formContent = `
|
|
112
|
-
<form data-component="form" method="post">
|
|
113
|
-
${error?.type === "invalid_claim" ? FormAlert({ message: copy.email_invalid }) : ""}
|
|
114
|
-
|
|
115
|
-
<input name="action" type="hidden" value="request" />
|
|
116
|
-
|
|
117
|
-
<input
|
|
118
|
-
autofocus
|
|
119
|
-
data-component="input"
|
|
120
|
-
placeholder="${copy.email_placeholder}"
|
|
121
|
-
required
|
|
122
|
-
type="${inputAttrs.type}"
|
|
123
|
-
name="${inputAttrs.name}"
|
|
124
|
-
inputmode="${inputAttrs.inputmode}"
|
|
125
|
-
autocomplete="${inputAttrs.autocomplete}"
|
|
126
|
-
/>
|
|
127
|
-
|
|
128
|
-
<button data-component="button" type="submit">
|
|
129
|
-
${copy.button_continue}
|
|
130
|
-
</button>
|
|
131
|
-
</form>
|
|
132
|
-
|
|
133
|
-
<p data-component="form-footer">${copy.code_info}</p>
|
|
134
|
-
`;
|
|
135
|
-
const html = Layout({ children: formContent });
|
|
136
|
-
return new Response(html, { headers: { "Content-Type": "text/html" } });
|
|
137
|
-
}
|
|
138
|
-
if (state.type === "code") {
|
|
139
|
-
const contactValue = getContactValue(state.claims);
|
|
140
|
-
const hiddenInputs = Object.entries(state.claims).map(([key, value]) => `<input name="${key}" type="hidden" value="${value}" />`).join("");
|
|
141
|
-
const formContent = `
|
|
142
|
-
<form data-component="form" method="post">
|
|
143
|
-
${error?.type === "invalid_code" ? FormAlert({ message: copy.code_invalid }) : ""}
|
|
144
|
-
|
|
145
|
-
${FormAlert({
|
|
146
|
-
color: "success",
|
|
147
|
-
message: (state.resend ? copy.code_resent : copy.code_sent) + contactValue
|
|
148
|
-
})}
|
|
149
|
-
|
|
150
|
-
<input name="action" type="hidden" value="verify" />
|
|
151
|
-
|
|
152
|
-
<input
|
|
153
|
-
aria-label="6-digit verification code"
|
|
154
|
-
autocomplete="one-time-code"
|
|
155
|
-
autofocus
|
|
156
|
-
data-component="input"
|
|
157
|
-
inputmode="numeric"
|
|
158
|
-
maxlength="6"
|
|
159
|
-
minlength="6"
|
|
160
|
-
name="code"
|
|
161
|
-
pattern="[0-9]{6}"
|
|
162
|
-
placeholder="${copy.code_placeholder}"
|
|
163
|
-
required
|
|
164
|
-
type="text"
|
|
165
|
-
/>
|
|
166
|
-
|
|
167
|
-
<button data-component="button" type="submit">
|
|
168
|
-
${copy.button_continue}
|
|
169
|
-
</button>
|
|
170
|
-
</form>
|
|
171
|
-
|
|
172
|
-
<form method="post">
|
|
173
|
-
${hiddenInputs}
|
|
174
|
-
|
|
175
|
-
<input name="action" type="hidden" value="resend" />
|
|
176
|
-
|
|
177
|
-
<div data-component="form-footer">
|
|
178
|
-
<span>
|
|
179
|
-
${copy.code_didnt_get}
|
|
180
|
-
<button data-component="link" type="submit">
|
|
181
|
-
${copy.code_resend}
|
|
182
|
-
</button>
|
|
183
|
-
</span>
|
|
184
|
-
</div>
|
|
185
|
-
</form>
|
|
186
|
-
`;
|
|
187
|
-
const html = Layout({ children: formContent });
|
|
188
|
-
return new Response(html, { headers: { "Content-Type": "text/html" } });
|
|
189
|
-
}
|
|
190
|
-
throw new UnknownStateError();
|
|
227
|
+
request: async (_req, state, form, error) => {
|
|
228
|
+
const html = renderToHTML(state.type === "start" ? renderStart(form, error, state) : renderCode(form, error, state));
|
|
229
|
+
return new Response(html, { headers: { "Content-Type": "text/html" } });
|
|
191
230
|
}
|
|
192
231
|
};
|
|
193
232
|
};
|
package/dist/ui/form.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
+
import { ComponentChildren } from "preact";
|
|
2
|
+
|
|
1
3
|
//#region src/ui/form.d.ts
|
|
2
|
-
|
|
3
|
-
* Form alert component for displaying success and error messages.
|
|
4
|
-
* Provides consistent styling and iconography for user feedback in authentication forms.
|
|
5
|
-
*/
|
|
4
|
+
|
|
6
5
|
/**
|
|
7
6
|
* Alert color variant determining the visual style and icon.
|
|
8
7
|
*/
|
|
@@ -24,8 +23,11 @@ interface FormAlertProps {
|
|
|
24
23
|
}
|
|
25
24
|
/**
|
|
26
25
|
* Form alert component that displays error or success messages.
|
|
27
|
-
* Returns
|
|
26
|
+
* Returns a Preact component or null if no message.
|
|
28
27
|
*/
|
|
29
|
-
declare const FormAlert: (
|
|
28
|
+
declare const FormAlert: ({
|
|
29
|
+
message,
|
|
30
|
+
color
|
|
31
|
+
}: FormAlertProps) => ComponentChildren;
|
|
30
32
|
//#endregion
|
|
31
33
|
export { FormAlert, FormAlertColor, FormAlertProps };
|