@xenosystem/components 0.2.1 → 0.3.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/auth/index.d.ts +31 -2
- package/dist/auth/index.js +45 -8
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +18 -6
package/dist/auth/index.d.ts
CHANGED
|
@@ -91,10 +91,23 @@ interface CredentialFormProps {
|
|
|
91
91
|
before?: ReactNode;
|
|
92
92
|
/** Rendered under the submit — "Forgot password?", terms, the other-mode link. */
|
|
93
93
|
after?: ReactNode;
|
|
94
|
+
/** Live password rules (sign-up): shown while the field is focused or partly filled — never before,
|
|
95
|
+
* a list of things you have failed before you typed is a telling-off for a crime not yet committed. */
|
|
96
|
+
passwordRules?: {
|
|
97
|
+
label: string;
|
|
98
|
+
met: boolean;
|
|
99
|
+
}[];
|
|
100
|
+
/** Rendered beside the password label — the page's "Forgot?" link on sign-in. */
|
|
101
|
+
passwordAside?: ReactNode;
|
|
102
|
+
/** When false, the fields collapse (0fr grid) and leave the accessibility tree; the page's disclosure
|
|
103
|
+
* controls it. Default open. */
|
|
104
|
+
open?: boolean;
|
|
105
|
+
/** id for the collapsible region (matched by `EmailDisclosure.controls`). */
|
|
106
|
+
id?: string;
|
|
94
107
|
/** Copy overrides; defaults are the platform page's. */
|
|
95
108
|
labels?: Partial<Record<'name' | 'email' | 'password' | 'submitSignIn' | 'submitSignUp' | 'signin' | 'signup', string>>;
|
|
96
109
|
}
|
|
97
|
-
declare function CredentialForm({ mode, onModeChange, values, onChange, errors, submitting, onSubmit, title, body, before, after, labels }: CredentialFormProps): react.JSX.Element;
|
|
110
|
+
declare function CredentialForm({ mode, onModeChange, values, onChange, errors, submitting, onSubmit, title, body, before, after, labels, passwordRules, passwordAside, open, id }: CredentialFormProps): react.JSX.Element;
|
|
98
111
|
|
|
99
112
|
type SocialProvider = 'google' | 'github' | 'x';
|
|
100
113
|
interface SocialSignInProps {
|
|
@@ -109,6 +122,22 @@ declare function OrDivider({ children }: {
|
|
|
109
122
|
children?: string;
|
|
110
123
|
}): react.JSX.Element;
|
|
111
124
|
|
|
125
|
+
/**
|
|
126
|
+
* `EmailDisclosure` — the divider that is a BUTTON. On the origin page the email/password form starts
|
|
127
|
+
* collapsed because it is the minority path (most people press a provider), so the "or continue with
|
|
128
|
+
* email" rule opens it. A real button with `aria-expanded`/`aria-controls`, never a styled span, so it is
|
|
129
|
+
* keyboard-reachable and announced as what it is.
|
|
130
|
+
*/
|
|
131
|
+
interface EmailDisclosureProps {
|
|
132
|
+
open: boolean;
|
|
133
|
+
onToggle: () => void;
|
|
134
|
+
/** id of the form region it controls. */
|
|
135
|
+
controls: string;
|
|
136
|
+
openLabel?: string;
|
|
137
|
+
closedLabel?: string;
|
|
138
|
+
}
|
|
139
|
+
declare function EmailDisclosure({ open, onToggle, controls, openLabel, closedLabel }: EmailDisclosureProps): react.JSX.Element;
|
|
140
|
+
|
|
112
141
|
declare const STATES: {
|
|
113
142
|
id: string;
|
|
114
143
|
props: SignInFormProps;
|
|
@@ -120,4 +149,4 @@ declare const CREDENTIAL_STATES: {
|
|
|
120
149
|
props: CredentialStateProps;
|
|
121
150
|
}[];
|
|
122
151
|
|
|
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 };
|
|
152
|
+
export { AuthDoor, type AuthDoorProps, AuthHero, type AuthHeroProps, type AuthHeroStat, CREDENTIAL_STATES, type CredentialErrors, CredentialForm, type CredentialFormProps, type CredentialMode, type CredentialValues, EmailDisclosure, type EmailDisclosureProps, LicencePlate, OrDivider, STATES, type SignInAction, SignInForm, type SignInFormProps, type SocialProvider, SocialSignIn, type SocialSignInProps, XenoMark, ensureAuthStyles };
|
package/dist/auth/index.js
CHANGED
|
@@ -43,7 +43,23 @@ var CSS = `
|
|
|
43
43
|
.xc-cred{width:100%;max-width:400px;display:flex;flex-direction:column;gap:24px}
|
|
44
44
|
.xc-cred__head .xc-form__h2{font-size:30px}
|
|
45
45
|
.xc-cred__mode{width:100%}
|
|
46
|
+
.xc-cred__collapse{display:grid;grid-template-rows:1fr;opacity:1;transition:grid-template-rows var(--xeno-dur-entrance) var(--xeno-ease),opacity var(--xeno-dur-entrance) var(--xeno-ease)}
|
|
47
|
+
.xc-cred__collapse[data-open=false]{grid-template-rows:0fr;opacity:0}
|
|
48
|
+
.xc-cred__collapse > *{overflow:hidden;min-height:0}
|
|
46
49
|
.xc-cred__fields{display:flex;flex-direction:column;gap:16px}
|
|
50
|
+
.xc-cred__labelRow{display:flex;justify-content:space-between;align-items:baseline}
|
|
51
|
+
.xc-cred__labelRow a{font-size:12px;color:var(--xeno-muted);text-decoration:none}
|
|
52
|
+
.xc-cred__labelRow a:hover{color:var(--xeno-text)}
|
|
53
|
+
.xc-cred__rules{list-style:none;margin:0;padding:0;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)}
|
|
54
|
+
.xc-cred__rules[data-open=true]{grid-template-rows:1fr;opacity:1;margin-top:10px}
|
|
55
|
+
.xc-cred__rules > li{overflow:hidden;min-height:0}
|
|
56
|
+
.xc-cred__rule{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--xeno-muted);transition:color var(--xeno-dur-fast) var(--xeno-ease)}
|
|
57
|
+
.xc-cred__rule[data-met=true]{color:var(--xeno-text)}
|
|
58
|
+
.xc-cred__ruleMark{width:14px;height:14px;flex:none}
|
|
59
|
+
.xc-or__btn{border:0;cursor:pointer;display:inline-flex;align-items:center;gap:6px;font:inherit;font-size:11px;letter-spacing:.12em;text-transform:uppercase}
|
|
60
|
+
.xc-or__btn:hover{color:var(--xeno-text)}
|
|
61
|
+
.xc-or__caret{width:12px;height:12px;transition:transform var(--xeno-dur-fast) var(--xeno-ease)}
|
|
62
|
+
.xc-or__caret[data-open=true]{transform:rotate(180deg)}
|
|
47
63
|
.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
64
|
.xc-cred__reveal[data-open=true]{grid-template-rows:1fr;opacity:1}
|
|
49
65
|
.xc-cred__reveal > *{overflow:hidden;min-height:0}
|
|
@@ -174,8 +190,10 @@ var L = {
|
|
|
174
190
|
signin: "Sign In",
|
|
175
191
|
signup: "Sign Up"
|
|
176
192
|
};
|
|
177
|
-
function CredentialForm({ mode, onModeChange, values, onChange, errors = {}, submitting = false, onSubmit, title, body, before, after, labels }) {
|
|
193
|
+
function CredentialForm({ mode, onModeChange, values, onChange, errors = {}, submitting = false, onSubmit, title, body, before, after, labels, passwordRules, passwordAside, open = true, id = "xc-credential-fields" }) {
|
|
178
194
|
const [showPassword, setShowPassword] = useState3(false);
|
|
195
|
+
const [passwordFocused, setPasswordFocused] = useState3(false);
|
|
196
|
+
const showRules = mode === "signup" && !!passwordRules?.length && (passwordFocused || values.password.length > 0);
|
|
179
197
|
const t = { ...L, ...labels };
|
|
180
198
|
const set = (k) => (e) => onChange({ ...values, [k]: e.target.value });
|
|
181
199
|
const submit = (e) => {
|
|
@@ -199,26 +217,33 @@ function CredentialForm({ mode, onModeChange, values, onChange, errors = {}, sub
|
|
|
199
217
|
}
|
|
200
218
|
),
|
|
201
219
|
before,
|
|
202
|
-
/* @__PURE__ */ jsxs5("div", { className: "xc-cred__fields", children: [
|
|
220
|
+
/* @__PURE__ */ jsx5("div", { className: "xc-cred__collapse", "data-open": open, id, "aria-hidden": !open, children: /* @__PURE__ */ jsxs5("div", { className: "xc-cred__fields", children: [
|
|
203
221
|
/* @__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
222
|
/* @__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" }),
|
|
223
|
+
/* @__PURE__ */ jsx5(TextInput, { size: "lg", value: values.name, onChange: set("name"), placeholder: "Your name", autoComplete: "name", tabIndex: mode === "signup" && open ? 0 : -1, "aria-invalid": !!errors.name, "data-field": "name" }),
|
|
206
224
|
errors.name && /* @__PURE__ */ jsx5("span", { className: "xc-cred__error", role: "alert", children: errors.name })
|
|
207
225
|
] }) }),
|
|
208
226
|
/* @__PURE__ */ jsxs5("label", { className: "xc-cred__field", children: [
|
|
209
227
|
/* @__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" }),
|
|
228
|
+
/* @__PURE__ */ jsx5(TextInput, { size: "lg", type: "email", value: values.email, onChange: set("email"), placeholder: "you@example.com", autoComplete: "email", required: true, tabIndex: open ? 0 : -1, "aria-invalid": !!errors.email, "data-field": "email" }),
|
|
211
229
|
errors.email && /* @__PURE__ */ jsx5("span", { className: "xc-cred__error", role: "alert", children: errors.email })
|
|
212
230
|
] }),
|
|
213
231
|
/* @__PURE__ */ jsxs5("label", { className: "xc-cred__field", children: [
|
|
214
|
-
/* @__PURE__ */
|
|
232
|
+
/* @__PURE__ */ jsxs5("span", { className: "xc-cred__labelRow", children: [
|
|
233
|
+
/* @__PURE__ */ jsx5("span", { className: "xc-cred__label", children: t.password }),
|
|
234
|
+
passwordAside
|
|
235
|
+
] }),
|
|
215
236
|
/* @__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" }),
|
|
237
|
+
/* @__PURE__ */ jsx5(TextInput, { size: "lg", type: showPassword ? "text" : "password", value: values.password, onChange: set("password"), onFocus: () => setPasswordFocused(true), onBlur: () => setPasswordFocused(false), 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
238
|
/* @__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
239
|
] }),
|
|
219
|
-
errors.password && /* @__PURE__ */ jsx5("span", { className: "xc-cred__error", role: "alert", children: errors.password })
|
|
240
|
+
errors.password && /* @__PURE__ */ jsx5("span", { className: "xc-cred__error", role: "alert", children: errors.password }),
|
|
241
|
+
passwordRules && /* @__PURE__ */ jsx5("ul", { className: "xc-cred__rules", "data-open": showRules, "aria-live": "polite", children: passwordRules.map((r, i) => /* @__PURE__ */ jsxs5("li", { className: "xc-cred__rule", "data-met": r.met, style: { transitionDelay: `${i * 60}ms` }, children: [
|
|
242
|
+
/* @__PURE__ */ jsx5("svg", { className: "xc-cred__ruleMark", viewBox: "0 0 16 16", "aria-hidden": "true", children: r.met ? /* @__PURE__ */ jsx5("path", { d: "M3.5 8.5l3 3 6-6", fill: "none", stroke: "currentColor", strokeWidth: "1.6" }) : /* @__PURE__ */ jsx5("rect", { x: "4", y: "7.25", width: "8", height: "1.5", fill: "currentColor" }) }),
|
|
243
|
+
/* @__PURE__ */ jsx5("span", { children: r.label })
|
|
244
|
+
] }, r.label)) })
|
|
220
245
|
] })
|
|
221
|
-
] }),
|
|
246
|
+
] }) }),
|
|
222
247
|
errors.form && /* @__PURE__ */ jsx5("p", { className: "xc-form__warn", role: "alert", children: errors.form }),
|
|
223
248
|
/* @__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
249
|
after && /* @__PURE__ */ jsx5("div", { className: "xc-cred__after", children: after })
|
|
@@ -249,6 +274,15 @@ function OrDivider({ children = "or continue with email" }) {
|
|
|
249
274
|
return /* @__PURE__ */ jsx7("div", { className: "xc-or", role: "separator", "aria-label": children, children: /* @__PURE__ */ jsx7("span", { className: "xc-or__label", children }) });
|
|
250
275
|
}
|
|
251
276
|
|
|
277
|
+
// src/auth/EmailDisclosure.tsx
|
|
278
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
279
|
+
function EmailDisclosure({ open, onToggle, controls, openLabel = "hide email sign-in", closedLabel = "or continue with email" }) {
|
|
280
|
+
return /* @__PURE__ */ jsx8("div", { className: "xc-or", "data-xc": "email-disclosure", children: /* @__PURE__ */ jsxs7("button", { type: "button", className: "xc-or__label xc-or__btn", onClick: onToggle, "aria-expanded": open, "aria-controls": controls, children: [
|
|
281
|
+
open ? openLabel : closedLabel,
|
|
282
|
+
/* @__PURE__ */ jsx8("svg", { className: "xc-or__caret", "data-open": open, viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "M4 6l4 4 4-4", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }) })
|
|
283
|
+
] }) });
|
|
284
|
+
}
|
|
285
|
+
|
|
252
286
|
// src/auth/states.ts
|
|
253
287
|
var STATES = [
|
|
254
288
|
{ 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 } },
|
|
@@ -265,6 +299,8 @@ var CREDENTIAL_STATES = [
|
|
|
265
299
|
{ 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
300
|
{ 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
301
|
{ 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" } },
|
|
302
|
+
{ id: "rules", props: { mode: "signup", values: { name: "Ada", email: "ada@example.com", password: "abcdefgh1" }, passwordRules: [{ label: "Minimum 8 letters", met: true }, { label: "At least one number", met: true }, { label: "At least one special character", met: false }], title: "Get started", body: "Create your account and start creating" } },
|
|
303
|
+
{ id: "collapsed", props: { mode: "signin", values: empty, open: false, title: "Welcome back", body: "Enter your credentials to access your account" } },
|
|
268
304
|
{ 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
305
|
];
|
|
270
306
|
export {
|
|
@@ -272,6 +308,7 @@ export {
|
|
|
272
308
|
AuthHero,
|
|
273
309
|
CREDENTIAL_STATES,
|
|
274
310
|
CredentialForm,
|
|
311
|
+
EmailDisclosure,
|
|
275
312
|
LicencePlate,
|
|
276
313
|
OrDivider,
|
|
277
314
|
STATES,
|
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.
|
|
6
|
+
declare const COMPONENTS_VERSION = "0.3.0";
|
|
7
7
|
|
|
8
8
|
export { COMPONENTS_VERSION };
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xenosystem/components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
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",
|
|
7
7
|
"sideEffects": false,
|
|
8
8
|
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./auth": {
|
|
14
|
+
"types": "./dist/auth/index.d.ts",
|
|
15
|
+
"import": "./dist/auth/index.js"
|
|
16
|
+
}
|
|
11
17
|
},
|
|
12
|
-
"files": [
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
13
22
|
"scripts": {
|
|
14
23
|
"build": "tsup",
|
|
15
24
|
"typecheck": "tsc --noEmit",
|
|
16
|
-
"test": "vitest run"
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"prepublishOnly": "node scripts/verify-dist.mjs"
|
|
17
27
|
},
|
|
18
28
|
"peerDependencies": {
|
|
19
29
|
"@xenosystem/elements-react": ">=0.0.1",
|
|
@@ -31,5 +41,7 @@
|
|
|
31
41
|
"typescript": "^5.4.0",
|
|
32
42
|
"vitest": "^4.1.0"
|
|
33
43
|
},
|
|
34
|
-
"publishConfig": {
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
}
|
|
35
47
|
}
|