@xenosystem/components 0.3.0 → 0.4.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 +22 -8
- package/dist/auth/index.js +84 -29
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode } from 'react';
|
|
2
|
+
import { ReactNode, ReactElement } from 'react';
|
|
3
3
|
import { ButtonVariant } from '@xenosystem/elements-react';
|
|
4
4
|
|
|
5
5
|
declare function ensureAuthStyles(): void;
|
|
@@ -122,12 +122,6 @@ declare function OrDivider({ children }: {
|
|
|
122
122
|
children?: string;
|
|
123
123
|
}): react.JSX.Element;
|
|
124
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
125
|
interface EmailDisclosureProps {
|
|
132
126
|
open: boolean;
|
|
133
127
|
onToggle: () => void;
|
|
@@ -149,4 +143,24 @@ declare const CREDENTIAL_STATES: {
|
|
|
149
143
|
props: CredentialStateProps;
|
|
150
144
|
}[];
|
|
151
145
|
|
|
152
|
-
|
|
146
|
+
/**
|
|
147
|
+
* `gallery()` — every unit of the family in every state, as renderable entries. This is what the
|
|
148
|
+
* workshop (rungs 1–2, from source) and xeno-apps' Library mode (from the published package) both
|
|
149
|
+
* consume, so neither hand-lists a state: `XENO FRONT-END HIERARCHY.md` — "states are derived, never
|
|
150
|
+
* hand-listed, IN the rung packages, so the workshop and the Library render the same list."
|
|
151
|
+
*
|
|
152
|
+
* `render` is pure: fixture props in, element out, callbacks inert. A gallery entry never reaches a
|
|
153
|
+
* network or a host.
|
|
154
|
+
*/
|
|
155
|
+
|
|
156
|
+
interface GalleryEntry {
|
|
157
|
+
family: 'auth';
|
|
158
|
+
unit: string;
|
|
159
|
+
state: string;
|
|
160
|
+
/** How the entry wants to be staged: `form` (a 400 px column) or `door` (fills a box, `inset: 0`). */
|
|
161
|
+
stage: 'form' | 'door';
|
|
162
|
+
render: () => ReactElement;
|
|
163
|
+
}
|
|
164
|
+
declare function gallery(): GalleryEntry[];
|
|
165
|
+
|
|
166
|
+
export { AuthDoor, type AuthDoorProps, AuthHero, type AuthHeroProps, type AuthHeroStat, CREDENTIAL_STATES, type CredentialErrors, CredentialForm, type CredentialFormProps, type CredentialMode, type CredentialValues, EmailDisclosure, type EmailDisclosureProps, type GalleryEntry, LicencePlate, OrDivider, STATES, type SignInAction, SignInForm, type SignInFormProps, type SocialProvider, SocialSignIn, type SocialSignInProps, XenoMark, ensureAuthStyles, gallery };
|
package/dist/auth/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/auth/styles.ts
|
|
2
|
+
import { useInsertionEffect } from "react";
|
|
2
3
|
var STYLE_ID = "xeno-components-auth";
|
|
3
4
|
var CSS = `
|
|
4
5
|
.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}
|
|
@@ -45,7 +46,7 @@ var CSS = `
|
|
|
45
46
|
.xc-cred__mode{width:100%}
|
|
46
47
|
.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
48
|
.xc-cred__collapse[data-open=false]{grid-template-rows:0fr;opacity:0}
|
|
48
|
-
.xc-
|
|
49
|
+
.xc-cred__collapseIn{overflow:hidden;min-height:0;display:flex;flex-direction:column;gap:24px}
|
|
49
50
|
.xc-cred__fields{display:flex;flex-direction:column;gap:16px}
|
|
50
51
|
.xc-cred__labelRow{display:flex;justify-content:space-between;align-items:baseline}
|
|
51
52
|
.xc-cred__labelRow a{font-size:12px;color:var(--xeno-muted);text-decoration:none}
|
|
@@ -84,6 +85,11 @@ function ensureAuthStyles() {
|
|
|
84
85
|
style.textContent = CSS;
|
|
85
86
|
document.head.appendChild(style);
|
|
86
87
|
}
|
|
88
|
+
function useAuthStyles() {
|
|
89
|
+
useInsertionEffect(() => {
|
|
90
|
+
ensureAuthStyles();
|
|
91
|
+
}, []);
|
|
92
|
+
}
|
|
87
93
|
|
|
88
94
|
// src/auth/XenoMark.tsx
|
|
89
95
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -99,6 +105,7 @@ function XenoMark() {
|
|
|
99
105
|
// src/auth/AuthHero.tsx
|
|
100
106
|
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
101
107
|
function AuthHero({ headline, lead, stats, imageSrc, compact }) {
|
|
108
|
+
useAuthStyles();
|
|
102
109
|
return /* @__PURE__ */ jsxs2("div", { className: "xc-hero", "data-xc": "auth-hero", children: [
|
|
103
110
|
imageSrc && /* @__PURE__ */ jsx2("img", { className: "xc-hero__img", src: imageSrc, alt: "", draggable: false }),
|
|
104
111
|
/* @__PURE__ */ jsxs2("div", { className: "xc-hero__in", children: [
|
|
@@ -128,6 +135,7 @@ import { useEffect, useState } from "react";
|
|
|
128
135
|
import { Button } from "@xenosystem/elements-react";
|
|
129
136
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
130
137
|
function SignInForm({ tag, title, body, actions, note, warning, identity, animate = true, phase }) {
|
|
138
|
+
useAuthStyles();
|
|
131
139
|
const [visible, setVisible] = useState(!animate);
|
|
132
140
|
useEffect(() => {
|
|
133
141
|
if (!animate) return;
|
|
@@ -191,6 +199,7 @@ var L = {
|
|
|
191
199
|
signup: "Sign Up"
|
|
192
200
|
};
|
|
193
201
|
function CredentialForm({ mode, onModeChange, values, onChange, errors = {}, submitting = false, onSubmit, title, body, before, after, labels, passwordRules, passwordAside, open = true, id = "xc-credential-fields" }) {
|
|
202
|
+
useAuthStyles();
|
|
194
203
|
const [showPassword, setShowPassword] = useState3(false);
|
|
195
204
|
const [passwordFocused, setPasswordFocused] = useState3(false);
|
|
196
205
|
const showRules = mode === "signup" && !!passwordRules?.length && (passwordFocused || values.password.length > 0);
|
|
@@ -217,35 +226,37 @@ function CredentialForm({ mode, onModeChange, values, onChange, errors = {}, sub
|
|
|
217
226
|
}
|
|
218
227
|
),
|
|
219
228
|
before,
|
|
220
|
-
/* @__PURE__ */ jsx5("div", { className: "xc-cred__collapse", "data-open": open, id, "aria-hidden": !open, children: /* @__PURE__ */ jsxs5("div", { className: "xc-
|
|
221
|
-
/* @__PURE__ */
|
|
222
|
-
/* @__PURE__ */ jsx5("
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
/* @__PURE__ */
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
/* @__PURE__ */ jsxs5("label", { className: "xc-cred__field", children: [
|
|
232
|
-
/* @__PURE__ */ jsxs5("span", { className: "xc-cred__labelRow", children: [
|
|
233
|
-
/* @__PURE__ */ jsx5("span", { className: "xc-cred__label", children: t.password }),
|
|
234
|
-
passwordAside
|
|
229
|
+
/* @__PURE__ */ jsx5("div", { className: "xc-cred__collapse", "data-open": open, id, "aria-hidden": !open, children: /* @__PURE__ */ jsxs5("div", { className: "xc-cred__collapseIn", children: [
|
|
230
|
+
/* @__PURE__ */ jsxs5("div", { className: "xc-cred__fields", children: [
|
|
231
|
+
/* @__PURE__ */ jsx5("div", { className: "xc-cred__reveal", "data-open": mode === "signup", "aria-hidden": mode !== "signup", children: /* @__PURE__ */ jsxs5("label", { className: "xc-cred__field", children: [
|
|
232
|
+
/* @__PURE__ */ jsx5("span", { className: "xc-cred__label", children: t.name }),
|
|
233
|
+
/* @__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" }),
|
|
234
|
+
errors.name && /* @__PURE__ */ jsx5("span", { className: "xc-cred__error", role: "alert", children: errors.name })
|
|
235
|
+
] }) }),
|
|
236
|
+
/* @__PURE__ */ jsxs5("label", { className: "xc-cred__field", children: [
|
|
237
|
+
/* @__PURE__ */ jsx5("span", { className: "xc-cred__label", children: t.email }),
|
|
238
|
+
/* @__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" }),
|
|
239
|
+
errors.email && /* @__PURE__ */ jsx5("span", { className: "xc-cred__error", role: "alert", children: errors.email })
|
|
235
240
|
] }),
|
|
236
|
-
/* @__PURE__ */ jsxs5("
|
|
237
|
-
/* @__PURE__ */
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
241
|
+
/* @__PURE__ */ jsxs5("label", { className: "xc-cred__field", children: [
|
|
242
|
+
/* @__PURE__ */ jsxs5("span", { className: "xc-cred__labelRow", children: [
|
|
243
|
+
/* @__PURE__ */ jsx5("span", { className: "xc-cred__label", children: t.password }),
|
|
244
|
+
passwordAside
|
|
245
|
+
] }),
|
|
246
|
+
/* @__PURE__ */ jsxs5("span", { className: "xc-cred__pw", children: [
|
|
247
|
+
/* @__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", tabIndex: open ? 0 : -1, autoComplete: mode === "signup" ? "new-password" : "current-password", required: true, "aria-invalid": !!errors.password, "data-field": "password" }),
|
|
248
|
+
/* @__PURE__ */ jsx5(Button2, { type: "button", variant: "ghost", size: "sm", className: "xc-cred__eye", tabIndex: open ? 0 : -1, onClick: () => setShowPassword((v) => !v), "aria-label": showPassword ? "Hide password" : "Show password", "aria-pressed": showPassword, children: showPassword ? "Hide" : "Show" })
|
|
249
|
+
] }),
|
|
250
|
+
errors.password && /* @__PURE__ */ jsx5("span", { className: "xc-cred__error", role: "alert", children: errors.password }),
|
|
251
|
+
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: [
|
|
252
|
+
/* @__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" }) }),
|
|
253
|
+
/* @__PURE__ */ jsx5("span", { children: r.label })
|
|
254
|
+
] }, r.label)) })
|
|
255
|
+
] })
|
|
256
|
+
] }),
|
|
257
|
+
errors.form && /* @__PURE__ */ jsx5("p", { className: "xc-form__warn", role: "alert", children: errors.form }),
|
|
258
|
+
/* @__PURE__ */ jsx5("div", { className: "xc-form__actions", children: /* @__PURE__ */ jsx5(Button2, { type: "submit", variant: "primary", size: "lg", busy: submitting, tabIndex: open ? 0 : -1, "data-intent": "submit", children: mode === "signin" ? t.submitSignIn : t.submitSignUp }) })
|
|
246
259
|
] }) }),
|
|
247
|
-
errors.form && /* @__PURE__ */ jsx5("p", { className: "xc-form__warn", role: "alert", children: errors.form }),
|
|
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 }) }),
|
|
249
260
|
after && /* @__PURE__ */ jsx5("div", { className: "xc-cred__after", children: after })
|
|
250
261
|
] });
|
|
251
262
|
}
|
|
@@ -265,18 +276,21 @@ function Mark({ p }) {
|
|
|
265
276
|
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" }) });
|
|
266
277
|
}
|
|
267
278
|
function SocialSignIn({ providers = ["google", "github", "x"], onSelect, disabled }) {
|
|
279
|
+
useAuthStyles();
|
|
268
280
|
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)) });
|
|
269
281
|
}
|
|
270
282
|
|
|
271
283
|
// src/auth/OrDivider.tsx
|
|
272
284
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
273
285
|
function OrDivider({ children = "or continue with email" }) {
|
|
286
|
+
useAuthStyles();
|
|
274
287
|
return /* @__PURE__ */ jsx7("div", { className: "xc-or", role: "separator", "aria-label": children, children: /* @__PURE__ */ jsx7("span", { className: "xc-or__label", children }) });
|
|
275
288
|
}
|
|
276
289
|
|
|
277
290
|
// src/auth/EmailDisclosure.tsx
|
|
278
291
|
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
279
292
|
function EmailDisclosure({ open, onToggle, controls, openLabel = "hide email sign-in", closedLabel = "or continue with email" }) {
|
|
293
|
+
useAuthStyles();
|
|
280
294
|
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
295
|
open ? openLabel : closedLabel,
|
|
282
296
|
/* @__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" }) })
|
|
@@ -303,6 +317,46 @@ var CREDENTIAL_STATES = [
|
|
|
303
317
|
{ id: "collapsed", props: { mode: "signin", values: empty, open: false, title: "Welcome back", body: "Enter your credentials to access your account" } },
|
|
304
318
|
{ 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" } }
|
|
305
319
|
];
|
|
320
|
+
|
|
321
|
+
// src/auth/gallery.tsx
|
|
322
|
+
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
323
|
+
var noop = () => {
|
|
324
|
+
};
|
|
325
|
+
function gallery() {
|
|
326
|
+
const out = [];
|
|
327
|
+
const hero = {
|
|
328
|
+
headline: /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
329
|
+
"One account.",
|
|
330
|
+
/* @__PURE__ */ jsx9("br", {}),
|
|
331
|
+
"Every XENO product."
|
|
332
|
+
] }),
|
|
333
|
+
lead: "Sign in once and XENO Canvas \u2014 and every other XENO app \u2014 runs under your plan, your credits and your files.",
|
|
334
|
+
stats: [{ value: "1", label: "Account" }, { value: "Yours", label: "Files stay put" }, { value: "Browser", label: "Sign-in, never in-app" }]
|
|
335
|
+
};
|
|
336
|
+
for (const s of STATES) {
|
|
337
|
+
out.push({ family: "auth", unit: "SignInForm", state: s.id, stage: "form", render: () => /* @__PURE__ */ jsx9(SignInForm, { ...s.props, phase: s.id }) });
|
|
338
|
+
out.push({ family: "auth", unit: "AuthDoor", state: s.id, stage: "door", render: () => /* @__PURE__ */ jsx9(AuthDoor, { label: `XENO Canvas sign-in \u2014 ${s.id}`, hero, rootAttributes: { "data-xeno-auth-gate": s.id }, children: /* @__PURE__ */ jsx9(SignInForm, { ...s.props, phase: s.id }) }) });
|
|
339
|
+
}
|
|
340
|
+
for (const s of CREDENTIAL_STATES) {
|
|
341
|
+
out.push({ family: "auth", unit: "CredentialForm", state: s.id, stage: "form", render: () => /* @__PURE__ */ jsx9(CredentialForm, { ...s.props, id: `gallery-${s.id}`, onModeChange: noop, onChange: noop, onSubmit: noop, before: /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
342
|
+
/* @__PURE__ */ jsx9(SocialSignIn, { onSelect: noop }),
|
|
343
|
+
/* @__PURE__ */ jsx9(EmailDisclosure, { open: s.props.open !== false, onToggle: noop, controls: `gallery-${s.id}` })
|
|
344
|
+
] }) }) });
|
|
345
|
+
}
|
|
346
|
+
out.push({ family: "auth", unit: "SocialSignIn", state: "default", stage: "form", render: () => /* @__PURE__ */ jsx9(SocialSignIn, { onSelect: noop }) });
|
|
347
|
+
out.push({ family: "auth", unit: "SocialSignIn", state: "disabled", stage: "form", render: () => /* @__PURE__ */ jsx9(SocialSignIn, { onSelect: noop, disabled: true }) });
|
|
348
|
+
const region = (id, open) => /* @__PURE__ */ jsx9("div", { id, hidden: !open, className: "xc-form__note", children: "the email form" });
|
|
349
|
+
out.push({ family: "auth", unit: "EmailDisclosure", state: "closed", stage: "form", render: () => /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
350
|
+
/* @__PURE__ */ jsx9(EmailDisclosure, { open: false, onToggle: noop, controls: "gallery-disclosure-closed" }),
|
|
351
|
+
region("gallery-disclosure-closed", false)
|
|
352
|
+
] }) });
|
|
353
|
+
out.push({ family: "auth", unit: "EmailDisclosure", state: "open", stage: "form", render: () => /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
354
|
+
/* @__PURE__ */ jsx9(EmailDisclosure, { open: true, onToggle: noop, controls: "gallery-disclosure-open" }),
|
|
355
|
+
region("gallery-disclosure-open", true)
|
|
356
|
+
] }) });
|
|
357
|
+
out.push({ family: "auth", unit: "OrDivider", state: "default", stage: "form", render: () => /* @__PURE__ */ jsx9(OrDivider, {}) });
|
|
358
|
+
return out;
|
|
359
|
+
}
|
|
306
360
|
export {
|
|
307
361
|
AuthDoor,
|
|
308
362
|
AuthHero,
|
|
@@ -315,5 +369,6 @@ export {
|
|
|
315
369
|
SignInForm,
|
|
316
370
|
SocialSignIn,
|
|
317
371
|
XenoMark,
|
|
318
|
-
ensureAuthStyles
|
|
372
|
+
ensureAuthStyles,
|
|
373
|
+
gallery
|
|
319
374
|
};
|
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.4.0";
|
|
7
7
|
|
|
8
8
|
export { COMPONENTS_VERSION };
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xenosystem/components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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",
|