@xenosystem/components 0.2.0 → 0.2.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.
@@ -3,7 +3,9 @@ import { ReactNode } from 'react';
3
3
  import { ButtonVariant } from '@xenosystem/elements-react';
4
4
 
5
5
  declare function ensureAuthStyles(): void;
6
+
6
7
  declare function XenoMark(): ReactNode;
8
+
7
9
  interface AuthHeroStat {
8
10
  value: string;
9
11
  label: string;
@@ -17,6 +19,7 @@ interface AuthHeroProps {
17
19
  compact?: boolean;
18
20
  }
19
21
  declare function AuthHero({ headline, lead, stats, imageSrc, compact }: AuthHeroProps): react.JSX.Element;
22
+
20
23
  interface SignInAction {
21
24
  id: string;
22
25
  label: string;
@@ -45,6 +48,7 @@ interface SignInFormProps {
45
48
  declare function SignInForm({ tag, title, body, actions, note, warning, identity, animate, phase }: SignInFormProps): react.JSX.Element;
46
49
  /** The licence plates are the same form with a tag; kept as a name so a panel reads honestly. */
47
50
  declare const LicencePlate: typeof SignInForm;
51
+
48
52
  interface AuthDoorProps {
49
53
  hero: Omit<AuthHeroProps, 'compact'>;
50
54
  /** Aria label of the dialog. */
@@ -59,9 +63,61 @@ interface AuthDoorProps {
59
63
  * was given.
60
64
  */
61
65
  declare function AuthDoor({ hero, label, children, rootAttributes }: AuthDoorProps): react.JSX.Element;
66
+
67
+ type CredentialMode = 'signin' | 'signup';
68
+ interface CredentialValues {
69
+ name: string;
70
+ email: string;
71
+ password: string;
72
+ }
73
+ interface CredentialErrors {
74
+ name?: string;
75
+ email?: string;
76
+ password?: string;
77
+ form?: string;
78
+ }
79
+ interface CredentialFormProps {
80
+ mode: CredentialMode;
81
+ onModeChange: (mode: CredentialMode) => void;
82
+ values: CredentialValues;
83
+ onChange: (values: CredentialValues) => void;
84
+ errors?: CredentialErrors;
85
+ submitting?: boolean;
86
+ onSubmit: () => void;
87
+ /** Rendered above the switch — the page's "Welcome back" / "Get started" copy. */
88
+ title?: ReactNode;
89
+ body?: ReactNode;
90
+ /** Rendered between the switch and the fields — `SocialSignIn` + `OrDivider`, when the page has them. */
91
+ before?: ReactNode;
92
+ /** Rendered under the submit — "Forgot password?", terms, the other-mode link. */
93
+ after?: ReactNode;
94
+ /** Copy overrides; defaults are the platform page's. */
95
+ labels?: Partial<Record<'name' | 'email' | 'password' | 'submitSignIn' | 'submitSignUp' | 'signin' | 'signup', string>>;
96
+ }
97
+ declare function CredentialForm({ mode, onModeChange, values, onChange, errors, submitting, onSubmit, title, body, before, after, labels }: CredentialFormProps): react.JSX.Element;
98
+
99
+ type SocialProvider = 'google' | 'github' | 'x';
100
+ interface SocialSignInProps {
101
+ providers?: readonly SocialProvider[];
102
+ onSelect: (provider: SocialProvider) => void;
103
+ disabled?: boolean;
104
+ }
105
+ declare function SocialSignIn({ providers, onSelect, disabled }: SocialSignInProps): react.JSX.Element;
106
+
107
+ /** The "or continue with email" rule between the social row and the fields. */
108
+ declare function OrDivider({ children }: {
109
+ children?: string;
110
+ }): react.JSX.Element;
111
+
62
112
  declare const STATES: {
63
113
  id: string;
64
114
  props: SignInFormProps;
65
115
  }[];
116
+ /** The origin page's states — everything but the callbacks, which the page supplies. */
117
+ type CredentialStateProps = Omit<CredentialFormProps, 'onModeChange' | 'onChange' | 'onSubmit'>;
118
+ declare const CREDENTIAL_STATES: {
119
+ id: string;
120
+ props: CredentialStateProps;
121
+ }[];
66
122
 
67
- export { AuthDoor, type AuthDoorProps, AuthHero, type AuthHeroProps, type AuthHeroStat, LicencePlate, STATES, type SignInAction, SignInForm, type SignInFormProps, XenoMark, ensureAuthStyles };
123
+ export { AuthDoor, type AuthDoorProps, AuthHero, type AuthHeroProps, type AuthHeroStat, CREDENTIAL_STATES, type CredentialErrors, CredentialForm, type CredentialFormProps, type CredentialMode, type CredentialValues, LicencePlate, OrDivider, STATES, type SignInAction, SignInForm, type SignInFormProps, type SocialProvider, SocialSignIn, type SocialSignInProps, XenoMark, ensureAuthStyles };
@@ -1,7 +1,4 @@
1
- // src/auth/index.tsx
2
- import { useEffect, useRef, useState } from "react";
3
- import { Button } from "@xenosystem/elements-react";
4
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
1
+ // src/auth/styles.ts
5
2
  var STYLE_ID = "xeno-components-auth";
6
3
  var CSS = `
7
4
  .xc-auth{position:absolute;inset:0;display:flex;background:var(--xeno-canvas);color:var(--xeno-text);font-family:var(--xeno-font-sans);-webkit-font-smoothing:antialiased;overflow:hidden}
@@ -42,6 +39,27 @@ var CSS = `
42
39
  .xc-form__warn{margin-top:16px;font-size:12px;line-height:1.6;color:var(--xeno-danger)}
43
40
  .xc-form__who{margin-top:24px;padding-top:16px;border-top:1px solid var(--xeno-border-subtle);font-size:12px;color:var(--xeno-muted)}
44
41
  .xc-form__who b{color:var(--xeno-text);font-weight:500}
42
+
43
+ .xc-cred{width:100%;max-width:400px;display:flex;flex-direction:column;gap:24px}
44
+ .xc-cred__head .xc-form__h2{font-size:30px}
45
+ .xc-cred__mode{width:100%}
46
+ .xc-cred__fields{display:flex;flex-direction:column;gap:16px}
47
+ .xc-cred__reveal{display:grid;grid-template-rows:0fr;opacity:0;transition:grid-template-rows var(--xeno-dur-entrance) var(--xeno-ease),opacity var(--xeno-dur-entrance) var(--xeno-ease)}
48
+ .xc-cred__reveal[data-open=true]{grid-template-rows:1fr;opacity:1}
49
+ .xc-cred__reveal > *{overflow:hidden;min-height:0}
50
+ .xc-cred__field{display:flex;flex-direction:column;gap:8px}
51
+ .xc-cred__label{font-size:13px;font-weight:500;color:var(--xeno-muted)}
52
+ .xc-cred__pw{display:grid;grid-template-columns:1fr auto;gap:8px;align-items:center}
53
+ .xc-cred__error{font-size:12px;color:var(--xeno-danger)}
54
+ .xc-cred__after{font-size:13px;color:var(--xeno-muted);text-align:center}
55
+ .xc-cred .xc-form__actions{margin-top:0}
56
+ .xc-social{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
57
+ .xc-social > *{width:100%}
58
+ .xc-social__mark{width:20px;height:20px;opacity:.75;transition:opacity var(--xeno-dur-fast) var(--xeno-ease)}
59
+ .xc-social button:hover .xc-social__mark{opacity:1}
60
+ .xc-or{position:relative;display:flex;justify-content:center;margin:4px 0}
61
+ .xc-or::before{content:'';position:absolute;inset:50% 0 auto 0;border-top:1px solid var(--xeno-border-subtle)}
62
+ .xc-or__label{position:relative;padding:0 16px;background:var(--xeno-canvas);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--xeno-muted)}
45
63
  `;
46
64
  function ensureAuthStyles() {
47
65
  if (typeof document === "undefined" || document.getElementById(STYLE_ID)) return;
@@ -50,6 +68,9 @@ function ensureAuthStyles() {
50
68
  style.textContent = CSS;
51
69
  document.head.appendChild(style);
52
70
  }
71
+
72
+ // src/auth/XenoMark.tsx
73
+ import { jsx, jsxs } from "react/jsx-runtime";
53
74
  function XenoMark() {
54
75
  return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 1082 1082", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [
55
76
  /* @__PURE__ */ jsx("path", { d: "M489.1 219.763L323.457 39.7072L101.649 30.4597C51.6926 28.3769 39.5494 67.5718 39.7224 87.4296L30.4124 310.735L347.816 655.757L475.833 537.987L241.73 283.514C207.644 246.462 222.019 240.156 233.467 241.634L455.275 250.881L489.1 219.763Z", fill: "currentColor" }),
@@ -58,30 +79,38 @@ function XenoMark() {
58
79
  /* @__PURE__ */ jsx("path", { d: "M220.763 592.907L40.7063 758.55L31.4588 980.358C29.3761 1030.31 68.5709 1042.46 88.4287 1042.28L311.735 1051.59L656.756 734.191L538.986 606.174L284.514 840.277C247.462 874.363 241.155 859.988 242.633 848.54L251.881 626.733L220.763 592.907Z", fill: "currentColor" })
59
80
  ] });
60
81
  }
82
+
83
+ // src/auth/AuthHero.tsx
84
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
61
85
  function AuthHero({ headline, lead, stats, imageSrc, compact }) {
62
- return /* @__PURE__ */ jsxs("div", { className: "xc-hero", "data-xc": "auth-hero", children: [
63
- imageSrc && /* @__PURE__ */ jsx("img", { className: "xc-hero__img", src: imageSrc, alt: "", draggable: false }),
64
- /* @__PURE__ */ jsxs("div", { className: "xc-hero__in", children: [
65
- /* @__PURE__ */ jsxs("div", { className: "xc-brand", children: [
66
- /* @__PURE__ */ jsx(XenoMark, {}),
67
- /* @__PURE__ */ jsx("span", { className: "xc-brand__word", children: "XENO" })
86
+ return /* @__PURE__ */ jsxs2("div", { className: "xc-hero", "data-xc": "auth-hero", children: [
87
+ imageSrc && /* @__PURE__ */ jsx2("img", { className: "xc-hero__img", src: imageSrc, alt: "", draggable: false }),
88
+ /* @__PURE__ */ jsxs2("div", { className: "xc-hero__in", children: [
89
+ /* @__PURE__ */ jsxs2("div", { className: "xc-brand", children: [
90
+ /* @__PURE__ */ jsx2(XenoMark, {}),
91
+ /* @__PURE__ */ jsx2("span", { className: "xc-brand__word", children: "XENO" })
68
92
  ] }),
69
- compact ? /* @__PURE__ */ jsx("h1", { className: "xc-hero__h1", children: headline }) : /* @__PURE__ */ jsxs(Fragment, { children: [
70
- /* @__PURE__ */ jsxs("div", { children: [
71
- /* @__PURE__ */ jsx("h1", { className: "xc-hero__h1", children: headline }),
72
- lead && /* @__PURE__ */ jsx("p", { className: "xc-hero__lead", children: lead })
93
+ compact ? /* @__PURE__ */ jsx2("h1", { className: "xc-hero__h1", children: headline }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
94
+ /* @__PURE__ */ jsxs2("div", { children: [
95
+ /* @__PURE__ */ jsx2("h1", { className: "xc-hero__h1", children: headline }),
96
+ lead && /* @__PURE__ */ jsx2("p", { className: "xc-hero__lead", children: lead })
73
97
  ] }),
74
- stats && stats.length > 0 && /* @__PURE__ */ jsx("div", { className: "xc-hero__stats", children: stats.map((s, i) => /* @__PURE__ */ jsxs("div", { style: { display: "contents" }, children: [
75
- i > 0 && /* @__PURE__ */ jsx("div", { className: "xc-hero__sep" }),
76
- /* @__PURE__ */ jsxs("div", { className: "xc-hero__stat", children: [
77
- /* @__PURE__ */ jsx("b", { children: s.value }),
78
- /* @__PURE__ */ jsx("span", { children: s.label })
98
+ stats && stats.length > 0 && /* @__PURE__ */ jsx2("div", { className: "xc-hero__stats", children: stats.map((s, i) => /* @__PURE__ */ jsxs2("div", { style: { display: "contents" }, children: [
99
+ i > 0 && /* @__PURE__ */ jsx2("div", { className: "xc-hero__sep" }),
100
+ /* @__PURE__ */ jsxs2("div", { className: "xc-hero__stat", children: [
101
+ /* @__PURE__ */ jsx2("b", { children: s.value }),
102
+ /* @__PURE__ */ jsx2("span", { children: s.label })
79
103
  ] })
80
104
  ] }, s.label)) })
81
105
  ] })
82
106
  ] })
83
107
  ] });
84
108
  }
109
+
110
+ // src/auth/SignInForm.tsx
111
+ import { useEffect, useState } from "react";
112
+ import { Button } from "@xenosystem/elements-react";
113
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
85
114
  function SignInForm({ tag, title, body, actions, note, warning, identity, animate = true, phase }) {
86
115
  const [visible, setVisible] = useState(!animate);
87
116
  useEffect(() => {
@@ -89,25 +118,29 @@ function SignInForm({ tag, title, body, actions, note, warning, identity, animat
89
118
  const t = window.setTimeout(() => setVisible(true), 100);
90
119
  return () => window.clearTimeout(t);
91
120
  }, [animate]);
92
- return /* @__PURE__ */ jsxs("div", { className: "xc-form", "data-xc": "sign-in-form", "data-visible": visible, "data-phase": phase, children: [
93
- tag && /* @__PURE__ */ jsx("span", { className: "xc-form__tag", children: tag }),
94
- /* @__PURE__ */ jsx("h2", { className: "xc-form__h2", children: title }),
95
- /* @__PURE__ */ jsx("p", { className: "xc-form__sub", children: body }),
96
- actions.length > 0 && /* @__PURE__ */ jsx("div", { className: "xc-form__actions", children: actions.map((a) => /* @__PURE__ */ jsx(Button, { variant: a.variant ?? "secondary", size: "lg", disabled: a.disabled, onClick: a.onClick, "data-intent": a.id, children: a.label }, a.id)) }),
97
- note && /* @__PURE__ */ jsx("p", { className: "xc-form__note", children: note }),
98
- warning && /* @__PURE__ */ jsx("p", { className: "xc-form__warn", children: warning }),
99
- identity && /* @__PURE__ */ jsxs("p", { className: "xc-form__who", children: [
121
+ return /* @__PURE__ */ jsxs3("div", { className: "xc-form", "data-xc": "sign-in-form", "data-visible": visible, "data-phase": phase, children: [
122
+ tag && /* @__PURE__ */ jsx3("span", { className: "xc-form__tag", children: tag }),
123
+ /* @__PURE__ */ jsx3("h2", { className: "xc-form__h2", children: title }),
124
+ /* @__PURE__ */ jsx3("p", { className: "xc-form__sub", children: body }),
125
+ actions.length > 0 && /* @__PURE__ */ jsx3("div", { className: "xc-form__actions", children: actions.map((a) => /* @__PURE__ */ jsx3(Button, { variant: a.variant ?? "secondary", size: "lg", disabled: a.disabled, onClick: a.onClick, "data-intent": a.id, children: a.label }, a.id)) }),
126
+ note && /* @__PURE__ */ jsx3("p", { className: "xc-form__note", children: note }),
127
+ warning && /* @__PURE__ */ jsx3("p", { className: "xc-form__warn", children: warning }),
128
+ identity && /* @__PURE__ */ jsxs3("p", { className: "xc-form__who", children: [
100
129
  "Signed in as ",
101
- /* @__PURE__ */ jsx("b", { children: identity })
130
+ /* @__PURE__ */ jsx3("b", { children: identity })
102
131
  ] })
103
132
  ] });
104
133
  }
105
134
  var LicencePlate = SignInForm;
135
+
136
+ // src/auth/AuthDoor.tsx
137
+ import { useEffect as useEffect2, useRef, useState as useState2 } from "react";
138
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
106
139
  var aspectOf = (w, h) => h <= 0 ? "landscape" : w / h > 1.2 ? "landscape" : w / h < 0.85 ? "portrait" : "square";
107
140
  function AuthDoor({ hero, label, children, rootAttributes }) {
108
141
  const ref = useRef(null);
109
- const [aspect, setAspect] = useState("landscape");
110
- useEffect(() => {
142
+ const [aspect, setAspect] = useState2("landscape");
143
+ useEffect2(() => {
111
144
  ensureAuthStyles();
112
145
  const el = ref.current;
113
146
  if (!el || typeof ResizeObserver === "undefined") return;
@@ -118,15 +151,105 @@ function AuthDoor({ hero, label, children, rootAttributes }) {
118
151
  ro.observe(el);
119
152
  return () => ro.disconnect();
120
153
  }, []);
121
- const brand = /* @__PURE__ */ jsxs("div", { className: "xc-brand", children: [
122
- /* @__PURE__ */ jsx(XenoMark, {}),
123
- /* @__PURE__ */ jsx("span", { className: "xc-brand__word", children: "XENO" })
154
+ const brand = /* @__PURE__ */ jsxs4("div", { className: "xc-brand", children: [
155
+ /* @__PURE__ */ jsx4(XenoMark, {}),
156
+ /* @__PURE__ */ jsx4("span", { className: "xc-brand__word", children: "XENO" })
157
+ ] });
158
+ return /* @__PURE__ */ jsxs4("div", { ref, className: "xc-auth", "data-xc": "auth-door", "data-aspect": aspect, role: "dialog", "aria-label": label, ...rootAttributes, children: [
159
+ aspect === "portrait" ? /* @__PURE__ */ jsx4("header", { className: "xc-auth__head", children: brand }) : /* @__PURE__ */ jsx4("div", { className: "xc-auth__heroWrap", children: /* @__PURE__ */ jsx4(AuthHero, { ...hero, compact: aspect === "square" }) }),
160
+ /* @__PURE__ */ jsx4("div", { className: "xc-auth__side", children: /* @__PURE__ */ jsx4("div", { className: "xc-auth__center", children }) })
161
+ ] });
162
+ }
163
+
164
+ // src/auth/CredentialForm.tsx
165
+ import { useState as useState3 } from "react";
166
+ import { Button as Button2, SegmentedControl, TextInput } from "@xenosystem/elements-react";
167
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
168
+ var L = {
169
+ name: "Full name",
170
+ email: "Email",
171
+ password: "Password",
172
+ submitSignIn: "Sign in",
173
+ submitSignUp: "Create account",
174
+ signin: "Sign In",
175
+ signup: "Sign Up"
176
+ };
177
+ function CredentialForm({ mode, onModeChange, values, onChange, errors = {}, submitting = false, onSubmit, title, body, before, after, labels }) {
178
+ const [showPassword, setShowPassword] = useState3(false);
179
+ const t = { ...L, ...labels };
180
+ const set = (k) => (e) => onChange({ ...values, [k]: e.target.value });
181
+ const submit = (e) => {
182
+ e.preventDefault();
183
+ if (!submitting) onSubmit();
184
+ };
185
+ return /* @__PURE__ */ jsxs5("form", { className: "xc-cred", "data-xc": "credential-form", "data-mode": mode, onSubmit: submit, noValidate: true, children: [
186
+ (title || body) && /* @__PURE__ */ jsxs5("div", { className: "xc-cred__head", children: [
187
+ title && /* @__PURE__ */ jsx5("h2", { className: "xc-form__h2", children: title }),
188
+ body && /* @__PURE__ */ jsx5("p", { className: "xc-form__sub", children: body })
189
+ ] }),
190
+ /* @__PURE__ */ jsx5(
191
+ SegmentedControl,
192
+ {
193
+ className: "xc-cred__mode",
194
+ value: mode,
195
+ onValueChange: (v) => onModeChange(v),
196
+ options: [{ value: "signin", label: t.signin }, { value: "signup", label: t.signup }],
197
+ size: "lg",
198
+ "aria-label": "Sign in or sign up"
199
+ }
200
+ ),
201
+ before,
202
+ /* @__PURE__ */ jsxs5("div", { className: "xc-cred__fields", children: [
203
+ /* @__PURE__ */ jsx5("div", { className: "xc-cred__reveal", "data-open": mode === "signup", "aria-hidden": mode !== "signup", children: /* @__PURE__ */ jsxs5("label", { className: "xc-cred__field", children: [
204
+ /* @__PURE__ */ jsx5("span", { className: "xc-cred__label", children: t.name }),
205
+ /* @__PURE__ */ jsx5(TextInput, { size: "lg", value: values.name, onChange: set("name"), placeholder: "Your name", autoComplete: "name", tabIndex: mode === "signup" ? 0 : -1, "aria-invalid": !!errors.name, "data-field": "name" }),
206
+ errors.name && /* @__PURE__ */ jsx5("span", { className: "xc-cred__error", role: "alert", children: errors.name })
207
+ ] }) }),
208
+ /* @__PURE__ */ jsxs5("label", { className: "xc-cred__field", children: [
209
+ /* @__PURE__ */ jsx5("span", { className: "xc-cred__label", children: t.email }),
210
+ /* @__PURE__ */ jsx5(TextInput, { size: "lg", type: "email", value: values.email, onChange: set("email"), placeholder: "you@example.com", autoComplete: "email", required: true, "aria-invalid": !!errors.email, "data-field": "email" }),
211
+ errors.email && /* @__PURE__ */ jsx5("span", { className: "xc-cred__error", role: "alert", children: errors.email })
212
+ ] }),
213
+ /* @__PURE__ */ jsxs5("label", { className: "xc-cred__field", children: [
214
+ /* @__PURE__ */ jsx5("span", { className: "xc-cred__label", children: t.password }),
215
+ /* @__PURE__ */ jsxs5("span", { className: "xc-cred__pw", children: [
216
+ /* @__PURE__ */ jsx5(TextInput, { size: "lg", type: showPassword ? "text" : "password", value: values.password, onChange: set("password"), placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", autoComplete: mode === "signup" ? "new-password" : "current-password", required: true, "aria-invalid": !!errors.password, "data-field": "password" }),
217
+ /* @__PURE__ */ jsx5(Button2, { type: "button", variant: "ghost", size: "sm", className: "xc-cred__eye", onClick: () => setShowPassword((v) => !v), "aria-label": showPassword ? "Hide password" : "Show password", "aria-pressed": showPassword, children: showPassword ? "Hide" : "Show" })
218
+ ] }),
219
+ errors.password && /* @__PURE__ */ jsx5("span", { className: "xc-cred__error", role: "alert", children: errors.password })
220
+ ] })
221
+ ] }),
222
+ errors.form && /* @__PURE__ */ jsx5("p", { className: "xc-form__warn", role: "alert", children: errors.form }),
223
+ /* @__PURE__ */ jsx5("div", { className: "xc-form__actions", children: /* @__PURE__ */ jsx5(Button2, { type: "submit", variant: "primary", size: "lg", busy: submitting, "data-intent": "submit", children: mode === "signin" ? t.submitSignIn : t.submitSignUp }) }),
224
+ after && /* @__PURE__ */ jsx5("div", { className: "xc-cred__after", children: after })
124
225
  ] });
125
- return /* @__PURE__ */ jsxs("div", { ref, className: "xc-auth", "data-xc": "auth-door", "data-aspect": aspect, role: "dialog", "aria-label": label, ...rootAttributes, children: [
126
- aspect === "portrait" ? /* @__PURE__ */ jsx("header", { className: "xc-auth__head", children: brand }) : /* @__PURE__ */ jsx("div", { className: "xc-auth__heroWrap", children: /* @__PURE__ */ jsx(AuthHero, { ...hero, compact: aspect === "square" }) }),
127
- /* @__PURE__ */ jsx("div", { className: "xc-auth__side", children: /* @__PURE__ */ jsx("div", { className: "xc-auth__center", children }) })
226
+ }
227
+
228
+ // src/auth/SocialSignIn.tsx
229
+ import { Button as Button3 } from "@xenosystem/elements-react";
230
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
231
+ var NAMES = { google: "Google", github: "GitHub", x: "X" };
232
+ function Mark({ p }) {
233
+ if (p === "google") return /* @__PURE__ */ jsxs6("svg", { className: "xc-social__mark", viewBox: "0 0 48 48", "aria-hidden": "true", children: [
234
+ /* @__PURE__ */ jsx6("path", { fill: "#FFC107", d: "M43.611,20.083H42V20H24v8h11.303c-1.649,4.657-6.08,8-11.303,8c-6.627,0-12-5.373-12-12c0-6.627,5.373-12,12-12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C12.955,4,4,12.955,4,24c0,11.045,8.955,20,20,20c11.045,0,20-8.955,20-20C44,22.659,43.862,21.35,43.611,20.083z" }),
235
+ /* @__PURE__ */ jsx6("path", { fill: "#FF3D00", d: "M6.306,14.691l6.571,4.819C14.655,15.108,18.961,12,24,12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C16.318,4,9.656,8.337,6.306,14.691z" }),
236
+ /* @__PURE__ */ jsx6("path", { fill: "#4CAF50", d: "M24,44c5.166,0,9.86-1.977,13.409-5.192l-6.19-5.238C29.211,35.091,26.715,36,24,36c-5.202,0-9.619-3.317-11.283-7.946l-6.522,5.025C9.505,39.556,16.227,44,24,44z" }),
237
+ /* @__PURE__ */ jsx6("path", { fill: "#1976D2", d: "M43.611,20.083H42V20H24v8h11.303c-0.792,2.237-2.231,4.166-4.087,5.571c0.001-0.001,0.002-0.001,0.003-0.002l6.19,5.238C36.971,39.205,44,34,44,24C44,22.659,43.862,21.35,43.611,20.083z" })
128
238
  ] });
239
+ if (p === "github") return /* @__PURE__ */ jsx6("svg", { className: "xc-social__mark", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx6("path", { d: "M12 .5C5.65.5.5 5.65.5 12c0 5.08 3.29 9.39 7.86 10.91.58.1.79-.25.79-.56v-2c-3.2.7-3.87-1.37-3.87-1.37-.52-1.33-1.28-1.69-1.28-1.69-1.05-.71.08-.7.08-.7 1.16.08 1.77 1.19 1.77 1.19 1.03 1.77 2.7 1.26 3.36.96.1-.75.4-1.26.73-1.55-2.55-.29-5.24-1.28-5.24-5.69 0-1.26.45-2.29 1.19-3.09-.12-.29-.52-1.46.11-3.05 0 0 .97-.31 3.17 1.18a11 11 0 0 1 5.77 0c2.2-1.49 3.17-1.18 3.17-1.18.63 1.59.23 2.76.11 3.05.74.8 1.19 1.83 1.19 3.09 0 4.42-2.69 5.39-5.25 5.68.41.36.78 1.06.78 2.13v3.16c0 .31.21.67.8.56A11.5 11.5 0 0 0 23.5 12C23.5 5.65 18.35.5 12 .5z" }) });
240
+ return /* @__PURE__ */ jsx6("svg", { className: "xc-social__mark", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx6("path", { d: "M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" }) });
129
241
  }
242
+ function SocialSignIn({ providers = ["google", "github", "x"], onSelect, disabled }) {
243
+ return /* @__PURE__ */ jsx6("div", { className: "xc-social", "data-xc": "social-sign-in", role: "group", "aria-label": "Continue with", children: providers.map((p) => /* @__PURE__ */ jsx6(Button3, { variant: "quiet", size: "lg", disabled, onClick: () => onSelect(p), "aria-label": `Continue with ${NAMES[p]}`, "data-provider": p, children: /* @__PURE__ */ jsx6(Mark, { p }) }, p)) });
244
+ }
245
+
246
+ // src/auth/OrDivider.tsx
247
+ import { jsx as jsx7 } from "react/jsx-runtime";
248
+ function OrDivider({ children = "or continue with email" }) {
249
+ return /* @__PURE__ */ jsx7("div", { className: "xc-or", role: "separator", "aria-label": children, children: /* @__PURE__ */ jsx7("span", { className: "xc-or__label", children }) });
250
+ }
251
+
252
+ // src/auth/states.ts
130
253
  var STATES = [
131
254
  { id: "sign-in", props: { title: "Welcome back", body: "Sign in to your XENO account to use XENO Canvas.", actions: [{ id: "signIn", label: "Sign in to XENO", variant: "primary" }], note: "Your browser opens at xenostudio.ai, where you can continue with Google, GitHub or email. XENO Canvas never sees your password.", animate: false } },
132
255
  { id: "pending", props: { title: "Welcome back", body: "Sign in to your XENO account to use XENO Canvas.", actions: [{ id: "pending", label: "Finish in your browser\u2026", variant: "primary", disabled: true }, { id: "cancelSignIn", label: "Cancel", variant: "quiet" }], animate: false } },
@@ -135,12 +258,25 @@ var STATES = [
135
258
  { id: "expired-offline", props: { tag: "Offline too long", title: "Your plan couldn\u2019t be confirmed", body: "XENO Canvas hasn\u2019t been able to reach XENO for 14 days \u2014 last confirmed 16 days ago. Your plan may be fine; connect once and it will pick up where it left off.", actions: [{ id: "refresh", label: "Check again", variant: "primary" }, { id: "signOut", label: "Sign out", variant: "quiet" }], identity: "emilian@bnkrsys.com", animate: false } },
136
259
  { id: "update-required", props: { tag: "Update required", title: "Update XENO Canvas to continue", body: "Versions before 0.39.0 are no longer supported. The latest version is a short download away.", actions: [{ id: "openDownload", label: "Download the latest XENO Canvas", variant: "primary" }, { id: "signOut", label: "Sign out", variant: "quiet" }], identity: "emilian@bnkrsys.com", animate: false } }
137
260
  ];
261
+ var empty = { name: "", email: "", password: "" };
262
+ var CREDENTIAL_STATES = [
263
+ { id: "signin", props: { mode: "signin", values: empty, title: "Welcome back", body: "Enter your credentials to access your account" } },
264
+ { id: "signup", props: { mode: "signup", values: empty, title: "Get started", body: "Create your account and start creating" } },
265
+ { id: "filled", props: { mode: "signin", values: { name: "", email: "you@example.com", password: "hunter22" }, title: "Welcome back", body: "Enter your credentials to access your account" } },
266
+ { id: "errors", props: { mode: "signup", values: { name: "", email: "not-an-email", password: "123" }, errors: { name: "Please enter your full name", email: "Please enter a valid email address", password: "Password must be at least 6 characters" }, title: "Get started", body: "Create your account and start creating" } },
267
+ { id: "submitting", props: { mode: "signin", values: { name: "", email: "you@example.com", password: "hunter22" }, submitting: true, title: "Welcome back", body: "Enter your credentials to access your account" } },
268
+ { id: "rejected", props: { mode: "signin", values: { name: "", email: "you@example.com", password: "hunter22" }, errors: { form: "Invalid email or password." }, title: "Welcome back", body: "Enter your credentials to access your account" } }
269
+ ];
138
270
  export {
139
271
  AuthDoor,
140
272
  AuthHero,
273
+ CREDENTIAL_STATES,
274
+ CredentialForm,
141
275
  LicencePlate,
276
+ OrDivider,
142
277
  STATES,
143
278
  SignInForm,
279
+ SocialSignIn,
144
280
  XenoMark,
145
281
  ensureAuthStyles
146
282
  };
package/dist/index.d.ts CHANGED
@@ -3,6 +3,6 @@
3
3
  * Families are subpaths: `@xenosystem/components/auth`, `/navigation`, `/feedback`, `/data`.
4
4
  * This root re-exports nothing on purpose: import the family you use, so a bundle carries only it.
5
5
  */
6
- declare const COMPONENTS_VERSION = "0.1.0";
6
+ declare const COMPONENTS_VERSION = "0.2.1";
7
7
 
8
8
  export { COMPONENTS_VERSION };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- var COMPONENTS_VERSION = "0.1.0";
2
+ var COMPONENTS_VERSION = "0.2.1";
3
3
  export {
4
4
  COMPONENTS_VERSION
5
5
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xenosystem/components",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "XENO Components — rung 2 of the front ladder: molecules composed from @xenosystem/elements, with no host contract. Families as subpaths: /auth, /navigation, /feedback, /data.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",