@slyxup/ui 0.1.0 → 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.
Files changed (58) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +105 -0
  3. package/dist/components/EmailVerification/EmailVerification.d.ts +9 -0
  4. package/dist/components/EmailVerification/EmailVerification.d.ts.map +1 -0
  5. package/dist/components/EmailVerification/EmailVerification.js +72 -0
  6. package/dist/components/EmailVerification/EmailVerification.js.map +1 -0
  7. package/dist/components/ForgotPassword/ForgotPassword.d.ts +8 -0
  8. package/dist/components/ForgotPassword/ForgotPassword.d.ts.map +1 -0
  9. package/dist/components/ForgotPassword/ForgotPassword.js +43 -0
  10. package/dist/components/ForgotPassword/ForgotPassword.js.map +1 -0
  11. package/dist/components/ResetPassword/ResetPassword.d.ts +9 -0
  12. package/dist/components/ResetPassword/ResetPassword.d.ts.map +1 -0
  13. package/dist/components/ResetPassword/ResetPassword.js +50 -0
  14. package/dist/components/ResetPassword/ResetPassword.js.map +1 -0
  15. package/dist/components/SignIn/SignIn.d.ts +11 -0
  16. package/dist/components/SignIn/SignIn.d.ts.map +1 -0
  17. package/dist/components/SignIn/SignIn.js +42 -0
  18. package/dist/components/SignIn/SignIn.js.map +1 -0
  19. package/dist/components/SignUp/SignUp.d.ts +8 -0
  20. package/dist/components/SignUp/SignUp.d.ts.map +1 -0
  21. package/dist/components/SignUp/SignUp.js +43 -0
  22. package/dist/components/SignUp/SignUp.js.map +1 -0
  23. package/dist/components/SocialButtons/SocialButtons.d.ts +9 -0
  24. package/dist/components/SocialButtons/SocialButtons.d.ts.map +1 -0
  25. package/dist/components/SocialButtons/SocialButtons.js +16 -0
  26. package/dist/components/SocialButtons/SocialButtons.js.map +1 -0
  27. package/dist/components/UserButton/UserButton.d.ts +3 -0
  28. package/dist/components/UserButton/UserButton.d.ts.map +1 -0
  29. package/dist/components/UserButton/UserButton.js +43 -0
  30. package/dist/components/UserButton/UserButton.js.map +1 -0
  31. package/dist/components/UserProfile/UserProfile.d.ts +3 -0
  32. package/dist/components/UserProfile/UserProfile.d.ts.map +1 -0
  33. package/dist/components/UserProfile/UserProfile.js +38 -0
  34. package/dist/components/UserProfile/UserProfile.js.map +1 -0
  35. package/dist/icons.d.ts +6 -0
  36. package/dist/icons.d.ts.map +1 -0
  37. package/dist/icons.js +15 -0
  38. package/dist/icons.js.map +1 -0
  39. package/dist/index.d.ts +15 -1
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +22 -1
  42. package/dist/index.js.map +1 -1
  43. package/dist/styles.d.ts +8 -0
  44. package/dist/styles.d.ts.map +1 -0
  45. package/dist/styles.js +198 -0
  46. package/dist/styles.js.map +1 -0
  47. package/package.json +30 -2
  48. package/src/components/EmailVerification/EmailVerification.tsx +180 -0
  49. package/src/components/ForgotPassword/ForgotPassword.tsx +122 -0
  50. package/src/components/ResetPassword/ResetPassword.tsx +120 -0
  51. package/src/components/SignIn/SignIn.tsx +143 -0
  52. package/src/components/SignUp/SignUp.tsx +156 -0
  53. package/src/components/SocialButtons/SocialButtons.tsx +39 -0
  54. package/src/components/UserButton/UserButton.tsx +89 -0
  55. package/src/components/UserProfile/UserProfile.tsx +93 -0
  56. package/src/icons.tsx +71 -0
  57. package/src/index.ts +37 -1
  58. package/src/styles.ts +199 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserProfile.d.ts","sourceRoot":"","sources":["../../../src/components/UserProfile/UserProfile.tsx"],"names":[],"mappings":"AAGA,yCAAyC;AACzC,wBAAgB,WAAW,gCAwF1B"}
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth, useUser } from '@slyxup/react';
3
+ import { useEffect, useState } from 'react';
4
+ /** Edit first/last name + avatar URL. */
5
+ export function UserProfile() {
6
+ const { isLoaded, user, reload } = useUser();
7
+ const { client } = useAuth();
8
+ const [firstName, setFirstName] = useState('');
9
+ const [lastName, setLastName] = useState('');
10
+ const [avatarUrl, setAvatarUrl] = useState('');
11
+ const [busy, setBusy] = useState(false);
12
+ const [saved, setSaved] = useState(false);
13
+ useEffect(() => {
14
+ if (user) {
15
+ setFirstName(user.firstName ?? '');
16
+ setLastName(user.lastName ?? '');
17
+ setAvatarUrl(user.avatarUrl ?? '');
18
+ }
19
+ }, [user]);
20
+ async function onSubmit(e) {
21
+ e.preventDefault();
22
+ setBusy(true);
23
+ setSaved(false);
24
+ try {
25
+ await client.users.update({ firstName, lastName, avatarUrl });
26
+ await reload();
27
+ setSaved(true);
28
+ setTimeout(() => setSaved(false), 2500);
29
+ }
30
+ finally {
31
+ setBusy(false);
32
+ }
33
+ }
34
+ if (!isLoaded)
35
+ return _jsx("div", { className: "slx-card", "aria-busy": "true" });
36
+ return (_jsxs("div", { className: "slx-card", children: [_jsx("h1", { className: "slx-title", children: "Profile" }), _jsxs("p", { className: "slx-subtitle", children: ["Signed in as ", user?.email] }), saved && (_jsx("p", { className: "slx-error-text", style: { color: '#34a853' }, children: "Profile saved." })), _jsxs("form", { onSubmit: onSubmit, children: [_jsxs("div", { className: "slx-field", children: [_jsx("label", { className: "slx-label", htmlFor: "slx-profile-first", children: "First name" }), _jsx("input", { id: "slx-profile-first", className: "slx-input", type: "text", value: firstName, onChange: (e) => setFirstName(e.target.value) })] }), _jsxs("div", { className: "slx-field", children: [_jsx("label", { className: "slx-label", htmlFor: "slx-profile-last", children: "Last name" }), _jsx("input", { id: "slx-profile-last", className: "slx-input", type: "text", value: lastName, onChange: (e) => setLastName(e.target.value) })] }), _jsxs("div", { className: "slx-field", children: [_jsx("label", { className: "slx-label", htmlFor: "slx-profile-avatar", children: "Avatar URL" }), _jsx("input", { id: "slx-profile-avatar", className: "slx-input", type: "url", placeholder: "https://\u2026", value: avatarUrl, onChange: (e) => setAvatarUrl(e.target.value) })] }), _jsx("button", { className: "slx-btn", type: "submit", disabled: busy, children: busy ? 'Saving…' : 'Save changes' })] })] }));
37
+ }
38
+ //# sourceMappingURL=UserProfile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserProfile.js","sourceRoot":"","sources":["../../../src/components/UserProfile/UserProfile.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /** Shared icons (stroke = currentColor) */
2
+ export declare function KeyholeMark(): import("react").JSX.Element;
3
+ export declare function GoogleIcon(): import("react").JSX.Element;
4
+ export declare function GitHubIcon(): import("react").JSX.Element;
5
+ export declare function CheckIcon(): import("react").JSX.Element;
6
+ //# sourceMappingURL=icons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../src/icons.tsx"],"names":[],"mappings":"AAAA,2CAA2C;AAE3C,wBAAgB,WAAW,gCAa1B;AAED,wBAAgB,UAAU,gCAqBzB;AAED,wBAAgB,UAAU,gCAYzB;AAED,wBAAgB,SAAS,gCAgBxB"}
package/dist/icons.js ADDED
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /** Shared icons (stroke = currentColor) */
3
+ export function KeyholeMark() {
4
+ return (_jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [_jsx("circle", { cx: "12", cy: "9", r: "3.2", fill: "white" }), _jsx("path", { d: "M10.6 11.5h2.8l.7 6h-4.2l.7-6z", fill: "white" })] }));
5
+ }
6
+ export function GoogleIcon() {
7
+ return (_jsxs("svg", { width: "17", height: "17", viewBox: "0 0 18 18", "aria-hidden": "true", children: [_jsx("path", { fill: "#4285F4", d: "M17.64 9.2c0-.63-.06-1.25-.16-1.84H9v3.48h4.84a4.14 4.14 0 0 1-1.8 2.72v2.26h2.92a8.78 8.78 0 0 0 2.68-6.62z" }), _jsx("path", { fill: "#34A853", d: "M9 18c2.43 0 4.47-.8 5.96-2.18l-2.91-2.26c-.81.54-1.84.86-3.05.86-2.34 0-4.33-1.58-5.04-3.71H.96v2.33A9 9 0 0 0 9 18z" }), _jsx("path", { fill: "#FBBC05", d: "M3.96 10.71a5.41 5.41 0 0 1 0-3.42V4.96H.96a9 9 0 0 0 0 8.08l3-2.33z" }), _jsx("path", { fill: "#EA4335", d: "M9 3.58c1.32 0 2.5.45 3.44 1.35l2.58-2.59A9 9 0 0 0 .96 4.96l3 2.33C4.67 5.16 6.66 3.58 9 3.58z" })] }));
8
+ }
9
+ export function GitHubIcon() {
10
+ return (_jsx("svg", { width: "17", height: "17", viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", children: _jsx("path", { d: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z" }) }));
11
+ }
12
+ export function CheckIcon() {
13
+ return (_jsx("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "M20 6L9 17l-5-5" }) }));
14
+ }
15
+ //# sourceMappingURL=icons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icons.js","sourceRoot":"","sources":["../src/icons.tsx"],"names":[],"mappings":";AAAA,2CAA2C;AAE3C,MAAM,UAAU,WAAW;IACzB,OAAO,CACL,eACE,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,iBACC,MAAM,aAElB,iBAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,GAAG,EAAC,CAAC,EAAC,KAAK,EAAC,IAAI,EAAC,OAAO,GAAG,EAC9C,eAAM,CAAC,EAAC,gCAAgC,EAAC,IAAI,EAAC,OAAO,GAAG,IACpD,CACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,CACL,eAAK,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,OAAO,EAAC,WAAW,iBAAa,MAAM,aAChE,eACE,IAAI,EAAC,SAAS,EACd,CAAC,EAAC,8GAA8G,GAChH,EACF,eACE,IAAI,EAAC,SAAS,EACd,CAAC,EAAC,uHAAuH,GACzH,EACF,eACE,IAAI,EAAC,SAAS,EACd,CAAC,EAAC,sEAAsE,GACxE,EACF,eACE,IAAI,EAAC,SAAS,EACd,CAAC,EAAC,iGAAiG,GACnG,IACE,CACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,CACL,cACE,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,cAAc,iBACP,MAAM,YAElB,eAAM,CAAC,EAAC,qjBAAqjB,GAAG,GAC5jB,CACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,CACL,cACE,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,KAAK,EACjB,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,iBACV,MAAM,YAElB,eAAM,CAAC,EAAC,iBAAiB,GAAG,GACxB,CACP,CAAC;AACJ,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,16 @@
1
- export declare const placeholder = "ui";
1
+ export { injectStyles, CSS } from './styles';
2
+ export { KeyholeMark, GoogleIcon, GitHubIcon, CheckIcon } from './icons';
3
+ export { SignIn, type SignInProps } from './components/SignIn/SignIn';
4
+ export { SignUp, type SignUpProps } from './components/SignUp/SignUp';
5
+ export { UserButton } from './components/UserButton/UserButton';
6
+ export { UserProfile } from './components/UserProfile/UserProfile';
7
+ export { ForgotPassword, type ForgotPasswordProps, } from './components/ForgotPassword/ForgotPassword';
8
+ export { ResetPassword, type ResetPasswordProps, } from './components/ResetPassword/ResetPassword';
9
+ export { EmailVerification, type EmailVerificationProps, } from './components/EmailVerification/EmailVerification';
10
+ export { SocialButtons, type SocialButtonsProps, } from './components/SocialButtons/SocialButtons';
11
+ /**
12
+ * Inject the SlyxUp stylesheet once. Render inside your app root
13
+ * (or rely on any component auto-injecting on mount).
14
+ */
15
+ export declare function SlyxUpStyles(): null;
2
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,OAAO,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzE,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EACL,cAAc,EACd,KAAK,mBAAmB,GACzB,MAAM,4CAA4C,CAAC;AACpD,OAAO,EACL,aAAa,EACb,KAAK,kBAAkB,GACxB,MAAM,0CAA0C,CAAC;AAClD,OAAO,EACL,iBAAiB,EACjB,KAAK,sBAAsB,GAC5B,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EACL,aAAa,EACb,KAAK,kBAAkB,GACxB,MAAM,0CAA0C,CAAC;AAKlD;;;GAGG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAKnC"}
package/dist/index.js CHANGED
@@ -1,2 +1,23 @@
1
- export const placeholder = 'ui';
1
+ export { injectStyles, CSS } from './styles';
2
+ export { KeyholeMark, GoogleIcon, GitHubIcon, CheckIcon } from './icons';
3
+ export { SignIn } from './components/SignIn/SignIn';
4
+ export { SignUp } from './components/SignUp/SignUp';
5
+ export { UserButton } from './components/UserButton/UserButton';
6
+ export { UserProfile } from './components/UserProfile/UserProfile';
7
+ export { ForgotPassword, } from './components/ForgotPassword/ForgotPassword';
8
+ export { ResetPassword, } from './components/ResetPassword/ResetPassword';
9
+ export { EmailVerification, } from './components/EmailVerification/EmailVerification';
10
+ export { SocialButtons, } from './components/SocialButtons/SocialButtons';
11
+ import { useEffect } from 'react';
12
+ import { injectStyles } from './styles';
13
+ /**
14
+ * Inject the SlyxUp stylesheet once. Render inside your app root
15
+ * (or rely on any component auto-injecting on mount).
16
+ */
17
+ export function SlyxUpStyles() {
18
+ useEffect(() => {
19
+ injectStyles();
20
+ }, []);
21
+ return null;
22
+ }
2
23
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzE,OAAO,EAAE,MAAM,EAAoB,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,MAAM,EAAoB,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EACL,cAAc,GAEf,MAAM,4CAA4C,CAAC;AACpD,OAAO,EACL,aAAa,GAEd,MAAM,0CAA0C,CAAC;AAClD,OAAO,EACL,iBAAiB,GAElB,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EACL,aAAa,GAEd,MAAM,0CAA0C,CAAC;AAElD,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC;;;GAGG;AACH,MAAM,UAAU,YAAY;IAC1B,SAAS,CAAC,GAAG,EAAE;QACb,YAAY,EAAE,CAAC;IACjB,CAAC,EAAE,EAAE,CAAC,CAAC;IACP,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * SlyxUp UI — design tokens + stylesheet.
3
+ * Injected once via <SlyxUpStyles />. Theme with CSS variables on :root or .slyxup-scope.
4
+ */
5
+ export declare const CSS = "\n.slyxup-root {\n --slx-accent: #5b5bd6;\n --slx-accent-hover: #4c4cc4;\n --slx-accent-soft: rgba(91, 91, 214, 0.1);\n --slx-bg: #ffffff;\n --slx-bg-page: #f7f7fa;\n --slx-ink: #16161d;\n --slx-muted: #6f6f7b;\n --slx-border: #e6e6ec;\n --slx-danger: #d64550;\n --slx-radius: 12px;\n --slx-font: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n --slx-mono: ui-monospace, SFMono-Regular, Menlo, monospace;\n\n font-family: var(--slx-font);\n color: var(--slx-ink);\n -webkit-font-smoothing: antialiased;\n}\n@media (prefers-color-scheme: dark) {\n .slyxup-root:not(.slyxup-light) {\n --slx-accent: #8484f2;\n --slx-accent-hover: #9696f5;\n --slx-accent-soft: rgba(132, 132, 242, 0.14);\n --slx-bg: #191920;\n --slx-bg-page: #121218;\n --slx-ink: #f2f2f5;\n --slx-muted: #9a9aa6;\n --slx-border: #2c2c36;\n --slx-danger: #f0737d;\n }\n}\n\n@keyframes slx-shake {\n 10%, 90% { transform: translateX(-1px); }\n 20%, 80% { transform: translateX(2px); }\n 30%, 50%, 70% { transform: translateX(-4px); }\n 40%, 60% { transform: translateX(4px); }\n}\n@keyframes slx-spin { to { transform: rotate(360deg); } }\n@media (prefers-reduced-motion: reduce) {\n .slyxup-root * { animation: none !important; transition: none !important; }\n}\n\n/* \u2500\u2500 Card \u2500\u2500 */\n.slx-card {\n width: 100%;\n max-width: 380px;\n background: var(--slx-bg);\n border: 1px solid var(--slx-border);\n border-radius: calc(var(--slx-radius) + 4px);\n padding: 32px;\n box-shadow: 0 1px 2px rgba(10,10,20,.04), 0 8px 24px rgba(10,10,20,.06);\n box-sizing: border-box;\n}\n.slx-card-error { animation: slx-shake .45s cubic-bezier(.36,.07,.19,.97) both; }\n\n/* \u2500\u2500 Header / keyhole mark \u2500\u2500 */\n.slx-mark {\n width: 44px; height: 44px; border-radius: 12px;\n display: flex; align-items: center; justify-content: center;\n background: linear-gradient(135deg, var(--slx-accent), #9a6cf0);\n margin-bottom: 18px;\n}\n.slx-mark svg { display: block; }\n.slx-title { font-size: 19px; font-weight: 650; letter-spacing: -0.02em; margin: 0 0 6px; }\n.slx-subtitle { font-size: 13.5px; color: var(--slx-muted); margin: 0 0 22px; line-height: 1.5; }\n\n/* \u2500\u2500 Fields \u2500\u2500 */\n.slx-field { margin-bottom: 14px; }\n.slx-label { display: block; font-size: 12.5px; font-weight: 550; margin-bottom: 6px; letter-spacing: 0.01em; }\n.slx-input {\n width: 100%; box-sizing: border-box;\n font: inherit; font-size: 14px; color: var(--slx-ink);\n background: transparent;\n border: 1px solid var(--slx-border);\n border-radius: var(--slx-radius);\n padding: 10px 12px;\n outline: none;\n transition: border-color .15s, box-shadow .15s;\n}\n.slx-input::placeholder { color: var(--slx-muted); opacity: .75; }\n.slx-input:focus-visible {\n border-color: var(--slx-accent);\n box-shadow: 0 0 0 3px var(--slx-accent-soft);\n}\n.slx-hint { font-size: 12px; color: var(--slx-muted); margin-top: 5px; }\n.slx-error-text {\n font-size: 13px; color: var(--slx-danger);\n background: color-mix(in srgb, var(--slx-danger) 9%, transparent);\n border-radius: 8px; padding: 9px 11px; margin: 0 0 14px; line-height: 1.4;\n}\n\n/* \u2500\u2500 Button \u2500\u2500 */\n.slx-btn {\n width: 100%; box-sizing: border-box;\n font: inherit; font-size: 14px; font-weight: 600; letter-spacing: 0.01em;\n color: #fff; background: var(--slx-accent);\n border: none; border-radius: var(--slx-radius);\n padding: 11px 14px; cursor: pointer;\n display: inline-flex; align-items: center; justify-content: center; gap: 8px;\n transition: background .15s, transform .06s;\n}\n.slx-btn:hover { background: var(--slx-accent-hover); }\n.slx-btn:active { transform: scale(.985); }\n.slx-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--slx-accent-soft), 0 0 0 1px var(--slx-accent); }\n.slx-btn[disabled] { opacity: .6; cursor: not-allowed; }\n.slx-spinner {\n width: 15px; height: 15px; flex: none;\n border: 2px solid rgba(255,255,255,.35); border-top-color: #fff;\n border-radius: 50%; animation: slx-spin .7s linear infinite;\n}\n\n/* \u2500\u2500 Social \u2500\u2500 */\n.slx-social { display: grid; gap: 10px; margin-bottom: 16px; }\n.slx-social-btn {\n width: 100%; box-sizing: border-box;\n font: inherit; font-size: 13.5px; font-weight: 550;\n color: var(--slx-ink); background: var(--slx-bg);\n border: 1px solid var(--slx-border); border-radius: var(--slx-radius);\n padding: 10px 14px; cursor: pointer;\n display: inline-flex; align-items: center; justify-content: center; gap: 10px;\n transition: background .15s, border-color .15s;\n}\n.slx-social-btn:hover { background: color-mix(in srgb, var(--slx-ink) 4%, var(--slx-bg)); }\n.slx-social-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--slx-accent-soft); }\n\n/* \u2500\u2500 Divider & footer \u2500\u2500 */\n.slx-divider {\n display: flex; align-items: center; gap: 12px;\n color: var(--slx-muted); font-size: 12px;\n margin: 18px 0;\n}\n.slx-divider::before, .slx-divider::after {\n content: \"\"; height: 1px; flex: 1; background: var(--slx-border);\n}\n.slx-footer { font-size: 13px; color: var(--slx-muted); margin-top: 20px; text-align: center; }\n.slx-link { color: var(--slx-accent); font-weight: 550; text-decoration: none; cursor: pointer; }\n.slx-link:hover { text-decoration: underline; }\n.slx-link:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--slx-accent-soft); border-radius: 4px; }\n\n/* \u2500\u2500 Success state \u2500\u2500 */\n.slx-success-icon {\n width: 46px; height: 46px; border-radius: 50%;\n display: flex; align-items: center; justify-content: center;\n background: color-mix(in srgb, #34a853 12%, transparent);\n color: #34a853; margin: 4px auto 18px;\n}\n\n/* \u2500\u2500 User button \u2500\u2500 */\n.slx-userbtn-wrap { position: relative; display: inline-block; }\n.slx-userbtn-avatar {\n width: 36px; height: 36px; border-radius: 50%;\n border: 1px solid var(--slx-border); cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n font-size: 14px; font-weight: 650; color: #fff;\n background: linear-gradient(135deg, var(--slx-accent), #9a6cf0);\n padding: 0; overflow: hidden;\n}\n.slx-userbtn-avatar img { width: 100%; height: 100%; object-fit: cover; }\n.slx-userbtn-avatar:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--slx-accent-soft); }\n.slx-menu {\n position: absolute; right: 0; top: calc(100% + 8px);\n min-width: 230px;\n background: var(--slx-bg);\n border: 1px solid var(--slx-border);\n border-radius: var(--slx-radius);\n box-shadow: 0 8px 30px rgba(10,10,20,.14);\n overflow: hidden; z-index: 1000;\n}\n.slx-menu-header { padding: 14px 16px; border-bottom: 1px solid var(--slx-border); }\n.slx-menu-name { font-size: 14px; font-weight: 600; margin: 0 0 2px; }\n.slx-menu-email { font-size: 12.5px; color: var(--slx-muted); margin: 0; word-break: break-all; }\n.slx-menu-item {\n display: block; width: 100%; text-align: left; box-sizing: border-box;\n font: inherit; font-size: 13.5px; color: var(--slx-ink);\n background: none; border: none; padding: 10px 16px; cursor: pointer;\n}\n.slx-menu-item:hover { background: color-mix(in srgb, var(--slx-ink) 5%, transparent); }\n.slx-menu-item:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--slx-accent-soft); }\n.slx-menu-item-danger { color: var(--slx-danger); }\n";
6
+ /** Inject the SlyxUp stylesheet once per document. */
7
+ export declare function injectStyles(): void;
8
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,GAAG,mxOAqLf,CAAC;AAIF,sDAAsD;AACtD,wBAAgB,YAAY,IAAI,IAAI,CAOnC"}
package/dist/styles.js ADDED
@@ -0,0 +1,198 @@
1
+ /**
2
+ * SlyxUp UI — design tokens + stylesheet.
3
+ * Injected once via <SlyxUpStyles />. Theme with CSS variables on :root or .slyxup-scope.
4
+ */
5
+ export const CSS = `
6
+ .slyxup-root {
7
+ --slx-accent: #5b5bd6;
8
+ --slx-accent-hover: #4c4cc4;
9
+ --slx-accent-soft: rgba(91, 91, 214, 0.1);
10
+ --slx-bg: #ffffff;
11
+ --slx-bg-page: #f7f7fa;
12
+ --slx-ink: #16161d;
13
+ --slx-muted: #6f6f7b;
14
+ --slx-border: #e6e6ec;
15
+ --slx-danger: #d64550;
16
+ --slx-radius: 12px;
17
+ --slx-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
18
+ --slx-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
19
+
20
+ font-family: var(--slx-font);
21
+ color: var(--slx-ink);
22
+ -webkit-font-smoothing: antialiased;
23
+ }
24
+ @media (prefers-color-scheme: dark) {
25
+ .slyxup-root:not(.slyxup-light) {
26
+ --slx-accent: #8484f2;
27
+ --slx-accent-hover: #9696f5;
28
+ --slx-accent-soft: rgba(132, 132, 242, 0.14);
29
+ --slx-bg: #191920;
30
+ --slx-bg-page: #121218;
31
+ --slx-ink: #f2f2f5;
32
+ --slx-muted: #9a9aa6;
33
+ --slx-border: #2c2c36;
34
+ --slx-danger: #f0737d;
35
+ }
36
+ }
37
+
38
+ @keyframes slx-shake {
39
+ 10%, 90% { transform: translateX(-1px); }
40
+ 20%, 80% { transform: translateX(2px); }
41
+ 30%, 50%, 70% { transform: translateX(-4px); }
42
+ 40%, 60% { transform: translateX(4px); }
43
+ }
44
+ @keyframes slx-spin { to { transform: rotate(360deg); } }
45
+ @media (prefers-reduced-motion: reduce) {
46
+ .slyxup-root * { animation: none !important; transition: none !important; }
47
+ }
48
+
49
+ /* ── Card ── */
50
+ .slx-card {
51
+ width: 100%;
52
+ max-width: 380px;
53
+ background: var(--slx-bg);
54
+ border: 1px solid var(--slx-border);
55
+ border-radius: calc(var(--slx-radius) + 4px);
56
+ padding: 32px;
57
+ box-shadow: 0 1px 2px rgba(10,10,20,.04), 0 8px 24px rgba(10,10,20,.06);
58
+ box-sizing: border-box;
59
+ }
60
+ .slx-card-error { animation: slx-shake .45s cubic-bezier(.36,.07,.19,.97) both; }
61
+
62
+ /* ── Header / keyhole mark ── */
63
+ .slx-mark {
64
+ width: 44px; height: 44px; border-radius: 12px;
65
+ display: flex; align-items: center; justify-content: center;
66
+ background: linear-gradient(135deg, var(--slx-accent), #9a6cf0);
67
+ margin-bottom: 18px;
68
+ }
69
+ .slx-mark svg { display: block; }
70
+ .slx-title { font-size: 19px; font-weight: 650; letter-spacing: -0.02em; margin: 0 0 6px; }
71
+ .slx-subtitle { font-size: 13.5px; color: var(--slx-muted); margin: 0 0 22px; line-height: 1.5; }
72
+
73
+ /* ── Fields ── */
74
+ .slx-field { margin-bottom: 14px; }
75
+ .slx-label { display: block; font-size: 12.5px; font-weight: 550; margin-bottom: 6px; letter-spacing: 0.01em; }
76
+ .slx-input {
77
+ width: 100%; box-sizing: border-box;
78
+ font: inherit; font-size: 14px; color: var(--slx-ink);
79
+ background: transparent;
80
+ border: 1px solid var(--slx-border);
81
+ border-radius: var(--slx-radius);
82
+ padding: 10px 12px;
83
+ outline: none;
84
+ transition: border-color .15s, box-shadow .15s;
85
+ }
86
+ .slx-input::placeholder { color: var(--slx-muted); opacity: .75; }
87
+ .slx-input:focus-visible {
88
+ border-color: var(--slx-accent);
89
+ box-shadow: 0 0 0 3px var(--slx-accent-soft);
90
+ }
91
+ .slx-hint { font-size: 12px; color: var(--slx-muted); margin-top: 5px; }
92
+ .slx-error-text {
93
+ font-size: 13px; color: var(--slx-danger);
94
+ background: color-mix(in srgb, var(--slx-danger) 9%, transparent);
95
+ border-radius: 8px; padding: 9px 11px; margin: 0 0 14px; line-height: 1.4;
96
+ }
97
+
98
+ /* ── Button ── */
99
+ .slx-btn {
100
+ width: 100%; box-sizing: border-box;
101
+ font: inherit; font-size: 14px; font-weight: 600; letter-spacing: 0.01em;
102
+ color: #fff; background: var(--slx-accent);
103
+ border: none; border-radius: var(--slx-radius);
104
+ padding: 11px 14px; cursor: pointer;
105
+ display: inline-flex; align-items: center; justify-content: center; gap: 8px;
106
+ transition: background .15s, transform .06s;
107
+ }
108
+ .slx-btn:hover { background: var(--slx-accent-hover); }
109
+ .slx-btn:active { transform: scale(.985); }
110
+ .slx-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--slx-accent-soft), 0 0 0 1px var(--slx-accent); }
111
+ .slx-btn[disabled] { opacity: .6; cursor: not-allowed; }
112
+ .slx-spinner {
113
+ width: 15px; height: 15px; flex: none;
114
+ border: 2px solid rgba(255,255,255,.35); border-top-color: #fff;
115
+ border-radius: 50%; animation: slx-spin .7s linear infinite;
116
+ }
117
+
118
+ /* ── Social ── */
119
+ .slx-social { display: grid; gap: 10px; margin-bottom: 16px; }
120
+ .slx-social-btn {
121
+ width: 100%; box-sizing: border-box;
122
+ font: inherit; font-size: 13.5px; font-weight: 550;
123
+ color: var(--slx-ink); background: var(--slx-bg);
124
+ border: 1px solid var(--slx-border); border-radius: var(--slx-radius);
125
+ padding: 10px 14px; cursor: pointer;
126
+ display: inline-flex; align-items: center; justify-content: center; gap: 10px;
127
+ transition: background .15s, border-color .15s;
128
+ }
129
+ .slx-social-btn:hover { background: color-mix(in srgb, var(--slx-ink) 4%, var(--slx-bg)); }
130
+ .slx-social-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--slx-accent-soft); }
131
+
132
+ /* ── Divider & footer ── */
133
+ .slx-divider {
134
+ display: flex; align-items: center; gap: 12px;
135
+ color: var(--slx-muted); font-size: 12px;
136
+ margin: 18px 0;
137
+ }
138
+ .slx-divider::before, .slx-divider::after {
139
+ content: ""; height: 1px; flex: 1; background: var(--slx-border);
140
+ }
141
+ .slx-footer { font-size: 13px; color: var(--slx-muted); margin-top: 20px; text-align: center; }
142
+ .slx-link { color: var(--slx-accent); font-weight: 550; text-decoration: none; cursor: pointer; }
143
+ .slx-link:hover { text-decoration: underline; }
144
+ .slx-link:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--slx-accent-soft); border-radius: 4px; }
145
+
146
+ /* ── Success state ── */
147
+ .slx-success-icon {
148
+ width: 46px; height: 46px; border-radius: 50%;
149
+ display: flex; align-items: center; justify-content: center;
150
+ background: color-mix(in srgb, #34a853 12%, transparent);
151
+ color: #34a853; margin: 4px auto 18px;
152
+ }
153
+
154
+ /* ── User button ── */
155
+ .slx-userbtn-wrap { position: relative; display: inline-block; }
156
+ .slx-userbtn-avatar {
157
+ width: 36px; height: 36px; border-radius: 50%;
158
+ border: 1px solid var(--slx-border); cursor: pointer;
159
+ display: flex; align-items: center; justify-content: center;
160
+ font-size: 14px; font-weight: 650; color: #fff;
161
+ background: linear-gradient(135deg, var(--slx-accent), #9a6cf0);
162
+ padding: 0; overflow: hidden;
163
+ }
164
+ .slx-userbtn-avatar img { width: 100%; height: 100%; object-fit: cover; }
165
+ .slx-userbtn-avatar:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--slx-accent-soft); }
166
+ .slx-menu {
167
+ position: absolute; right: 0; top: calc(100% + 8px);
168
+ min-width: 230px;
169
+ background: var(--slx-bg);
170
+ border: 1px solid var(--slx-border);
171
+ border-radius: var(--slx-radius);
172
+ box-shadow: 0 8px 30px rgba(10,10,20,.14);
173
+ overflow: hidden; z-index: 1000;
174
+ }
175
+ .slx-menu-header { padding: 14px 16px; border-bottom: 1px solid var(--slx-border); }
176
+ .slx-menu-name { font-size: 14px; font-weight: 600; margin: 0 0 2px; }
177
+ .slx-menu-email { font-size: 12.5px; color: var(--slx-muted); margin: 0; word-break: break-all; }
178
+ .slx-menu-item {
179
+ display: block; width: 100%; text-align: left; box-sizing: border-box;
180
+ font: inherit; font-size: 13.5px; color: var(--slx-ink);
181
+ background: none; border: none; padding: 10px 16px; cursor: pointer;
182
+ }
183
+ .slx-menu-item:hover { background: color-mix(in srgb, var(--slx-ink) 5%, transparent); }
184
+ .slx-menu-item:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--slx-accent-soft); }
185
+ .slx-menu-item-danger { color: var(--slx-danger); }
186
+ `;
187
+ let injected = false;
188
+ /** Inject the SlyxUp stylesheet once per document. */
189
+ export function injectStyles() {
190
+ if (injected || typeof document === 'undefined')
191
+ return;
192
+ const style = document.createElement('style');
193
+ style.setAttribute('data-slyxup', 'styles');
194
+ style.textContent = CSS;
195
+ document.head.appendChild(style);
196
+ injected = true;
197
+ }
198
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAC,MAAM,GAAG,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqLlB,CAAC;AAEF,IAAI,QAAQ,GAAG,KAAK,CAAC;AAErB,sDAAsD;AACtD,MAAM,UAAU,YAAY;IAC1B,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO;IACxD,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9C,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IAC5C,KAAK,CAAC,WAAW,GAAG,GAAG,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACjC,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC"}
package/package.json CHANGED
@@ -1,9 +1,37 @@
1
1
  {
2
2
  "name": "@slyxup/ui",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "@slyxup/ui — SlyxUp SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
- "scripts": { "build": "tsc -p tsconfig.json", "typecheck": "tsc --noEmit" }
8
+ "scripts": {
9
+ "build": "tsc -p tsconfig.json",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/slyxup/stack.git",
15
+ "directory": "packages/ui"
16
+ },
17
+ "homepage": "https://github.com/slyxup/stack/tree/main/packages/ui#readme",
18
+ "bugs": {
19
+ "url": "https://github.com/slyxup/stack/issues"
20
+ },
21
+ "author": "SlyxUp <hello@slyxup.online>",
22
+ "license": "MIT",
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "peerDependencies": {
27
+ "react": "^18 || ^19"
28
+ },
29
+ "dependencies": {
30
+ "@slyxup/core": "workspace:*",
31
+ "@slyxup/react": "workspace:*"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^26.2.0",
35
+ "@types/react": "^18"
36
+ }
9
37
  }
@@ -0,0 +1,180 @@
1
+ import { type FormEvent, useEffect, useState } from 'react';
2
+ import { CheckIcon, KeyholeMark } from '../../icons';
3
+
4
+ export interface EmailVerificationProps {
5
+ /** Verification token (from email link ?token=...). If absent, shows resend form. */
6
+ token?: string;
7
+ apiUrl?: string;
8
+ onSuccess?: () => void;
9
+ }
10
+
11
+ /** Verify an email with the emailed token, or request a new link. */
12
+ export function EmailVerification({
13
+ token,
14
+ apiUrl,
15
+ onSuccess,
16
+ }: EmailVerificationProps) {
17
+ const [status, setStatus] = useState<
18
+ 'verifying' | 'success' | 'error' | 'resend'
19
+ >(token ? 'verifying' : 'resend');
20
+ const [message, setMessage] = useState<string>('');
21
+ const [email, setEmail] = useState('');
22
+ const [busy, setBusy] = useState(false);
23
+
24
+ useEffect(() => {
25
+ if (!token) return;
26
+ let cancelled = false;
27
+ (async () => {
28
+ try {
29
+ const base = (
30
+ apiUrl ??
31
+ process.env.NEXT_PUBLIC_SLYXUP_API_URL ??
32
+ 'https://auth.slyxup.online'
33
+ ).replace(/\/$/, '');
34
+ const res = await fetch(`${base}/v1/verification/verify`, {
35
+ method: 'POST',
36
+ headers: { 'Content-Type': 'application/json' },
37
+ body: JSON.stringify({ token }),
38
+ });
39
+ if (cancelled) return;
40
+ if (res.ok) {
41
+ setStatus('success');
42
+ setTimeout(() => onSuccess?.(), 1800);
43
+ } else {
44
+ const data = await res
45
+ .json()
46
+ .catch(() => ({ error: 'Invalid or expired link' }));
47
+ setMessage(data.error ?? 'Invalid or expired link');
48
+ setStatus('error');
49
+ }
50
+ } catch {
51
+ if (!cancelled) {
52
+ setMessage('Network problem. Try again.');
53
+ setStatus('error');
54
+ }
55
+ }
56
+ })();
57
+ return () => {
58
+ cancelled = true;
59
+ };
60
+ }, [token, apiUrl, onSuccess]);
61
+
62
+ async function resend(e: FormEvent) {
63
+ e.preventDefault();
64
+ setBusy(true);
65
+ try {
66
+ const base = (
67
+ apiUrl ??
68
+ process.env.NEXT_PUBLIC_SLYXUP_API_URL ??
69
+ 'https://auth.slyxup.online'
70
+ ).replace(/\/$/, '');
71
+ await fetch(`${base}/v1/verification/resend`, {
72
+ method: 'POST',
73
+ headers: { 'Content-Type': 'application/json' },
74
+ body: JSON.stringify({ email }),
75
+ });
76
+ setMessage(
77
+ 'If that account exists, a new verification link is on its way.'
78
+ );
79
+ } catch {
80
+ setMessage('Network problem. Try again.');
81
+ } finally {
82
+ setBusy(false);
83
+ }
84
+ }
85
+
86
+ return (
87
+ <div className="slx-card">
88
+ <div className="slx-mark">
89
+ <KeyholeMark />
90
+ </div>
91
+
92
+ {status === 'verifying' && (
93
+ <>
94
+ <h1 className="slx-title">Verifying your email…</h1>
95
+ <p className="slx-subtitle">One moment while we confirm.</p>
96
+ </>
97
+ )}
98
+
99
+ {status === 'success' && (
100
+ <>
101
+ <div className="slx-success-icon">
102
+ <CheckIcon />
103
+ </div>
104
+ <h1 className="slx-title" style={{ textAlign: 'center' }}>
105
+ Email verified
106
+ </h1>
107
+ <p className="slx-subtitle" style={{ textAlign: 'center' }}>
108
+ You&apos;re all set. Redirecting…
109
+ </p>
110
+ </>
111
+ )}
112
+
113
+ {status === 'error' && (
114
+ <>
115
+ <h1 className="slx-title">Link didn&apos;t work</h1>
116
+ {message && (
117
+ <p className="slx-error-text" role="alert">
118
+ {message}
119
+ </p>
120
+ )}
121
+ <p className="slx-subtitle">Request a fresh link below.</p>
122
+ <form onSubmit={resend}>
123
+ <div className="slx-field">
124
+ <label className="slx-label" htmlFor="slx-verify-email">
125
+ Email
126
+ </label>
127
+ <input
128
+ id="slx-verify-email"
129
+ className="slx-input"
130
+ type="email"
131
+ placeholder="you@example.com"
132
+ value={email}
133
+ onChange={(e) => setEmail(e.target.value)}
134
+ required
135
+ />
136
+ </div>
137
+ <button className="slx-btn" type="submit" disabled={busy}>
138
+ {busy && <span className="slx-spinner" aria-hidden="true" />}
139
+ {busy ? 'Sending…' : 'Resend verification email'}
140
+ </button>
141
+ </form>
142
+ </>
143
+ )}
144
+
145
+ {status === 'resend' && (
146
+ <>
147
+ <h1 className="slx-title">Verify your email</h1>
148
+ <p className="slx-subtitle">
149
+ Enter your email and we&apos;ll send a verification link.
150
+ </p>
151
+ {message && (
152
+ <p className="slx-hint" style={{ marginBottom: 12 }}>
153
+ {message}
154
+ </p>
155
+ )}
156
+ <form onSubmit={resend}>
157
+ <div className="slx-field">
158
+ <label className="slx-label" htmlFor="slx-verify-email2">
159
+ Email
160
+ </label>
161
+ <input
162
+ id="slx-verify-email2"
163
+ className="slx-input"
164
+ type="email"
165
+ placeholder="you@example.com"
166
+ value={email}
167
+ onChange={(e) => setEmail(e.target.value)}
168
+ required
169
+ />
170
+ </div>
171
+ <button className="slx-btn" type="submit" disabled={busy}>
172
+ {busy && <span className="slx-spinner" aria-hidden="true" />}
173
+ {busy ? 'Sending…' : 'Send verification link'}
174
+ </button>
175
+ </form>
176
+ </>
177
+ )}
178
+ </div>
179
+ );
180
+ }