@carbonbits/sixr 1.3.0 → 1.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/README.md +77 -18
- package/dist/chunk-NZHHNVXZ.js +118 -0
- package/dist/chunk-NZHHNVXZ.js.map +1 -0
- package/dist/email-input.cjs +390 -0
- package/dist/email-input.cjs.map +1 -0
- package/dist/email-input.d.cts +14 -0
- package/dist/email-input.d.ts +14 -0
- package/dist/email-input.js +367 -0
- package/dist/email-input.js.map +1 -0
- package/dist/index-DwjxC4l4.d.cts +62 -0
- package/dist/index-DwjxC4l4.d.ts +62 -0
- package/dist/index.cjs +112 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -9
- package/dist/index.d.ts +48 -9
- package/dist/index.js +1 -0
- package/dist/password-input.cjs +406 -0
- package/dist/password-input.cjs.map +1 -0
- package/dist/password-input.d.cts +20 -0
- package/dist/password-input.d.ts +20 -0
- package/dist/password-input.js +385 -0
- package/dist/password-input.js.map +1 -0
- package/dist/phone-input.cjs +386 -0
- package/dist/phone-input.cjs.map +1 -0
- package/dist/phone-input.d.cts +12 -0
- package/dist/phone-input.d.ts +12 -0
- package/dist/phone-input.js +363 -0
- package/dist/phone-input.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/text-input.cjs +228 -59
- package/dist/text-input.cjs.map +1 -1
- package/dist/text-input.d.cts +10 -58
- package/dist/text-input.d.ts +10 -58
- package/dist/text-input.js +226 -60
- package/dist/text-input.js.map +1 -1
- package/dist/typography.cjs +120 -0
- package/dist/typography.cjs.map +1 -0
- package/dist/typography.d.cts +46 -0
- package/dist/typography.d.ts +46 -0
- package/dist/typography.js +3 -0
- package/dist/typography.js.map +1 -0
- package/package.json +45 -1
package/README.md
CHANGED
|
@@ -70,43 +70,99 @@ type CartProduct = {
|
|
|
70
70
|
Pass an `id` whenever a cart can hold two lines with the same name. Prices are
|
|
71
71
|
formatted with `Intl.NumberFormat`, and the total is summed from `price`.
|
|
72
72
|
|
|
73
|
-
### `
|
|
73
|
+
### `Typography`
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import { Typography } from "@carbonbits/sixr/typography";
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The type scale. Every piece of text the library renders goes through it, and it is
|
|
80
|
+
the component to reach for when yours does too. Server-safe.
|
|
81
|
+
|
|
82
|
+
| Prop | Type | Default | Description |
|
|
83
|
+
| -------------- | ------------------------------------------------------------------------- | ------------------ | -------------------------------------------------------- |
|
|
84
|
+
| `variant` | `"display" \| "heading" \| "subheading" \| "body" \| "label" \| "hint" \| "code"` | `"body"` | Size, default element and default weight. |
|
|
85
|
+
| `tone` | `"default" \| "muted" \| "subtle" \| "accent" \| "on-accent" \| "success" \| "warning" \| "info" \| "danger"` | `"default"` | Colour role from the design tokens. |
|
|
86
|
+
| `weight` | `"regular" \| "medium" \| "semibold" \| "bold"` | per variant | Overrides the variant's weight. |
|
|
87
|
+
| `align` | `"start" \| "center" \| "end"` | — | Text alignment. |
|
|
88
|
+
| `truncate` | `boolean` | `false` | One line, clipped with an ellipsis. |
|
|
89
|
+
| `icon` | `ReactNode` | — | An icon beside the text, sized to it, `aria-hidden`. |
|
|
90
|
+
| `iconPosition` | `"leading" \| "trailing"` | `"leading"` | Which side the icon sits on. |
|
|
91
|
+
| `as` | `"p" \| "span" \| "div" \| "h1"…"h6" \| "label" \| "code" \| …` | per variant | The element to render. |
|
|
92
|
+
| `asChild` | `boolean` | `false` | Merge the styles onto the child element instead. |
|
|
93
|
+
|
|
94
|
+
Default elements: `display` → `h1`, `heading` → `h2`, `subheading` → `h3`, `body` and
|
|
95
|
+
`hint` → `p`, `label` → `span`, `code` → `code`. Any other HTML attribute passes through.
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
<Typography variant="heading">Welcome back</Typography>
|
|
99
|
+
<Typography tone="muted">Sign in to your Carbonbits workspace.</Typography>
|
|
100
|
+
<Typography variant="hint" tone="success" icon={<CheckIcon />}>
|
|
101
|
+
Verification code accepted
|
|
102
|
+
</Typography>
|
|
103
|
+
|
|
104
|
+
// Style your own element — a Next.js Link, a Radix Label — with `asChild`.
|
|
105
|
+
<Typography variant="label" tone="accent" asChild>
|
|
106
|
+
<Link href="/reset">Forgot your password?</Link>
|
|
107
|
+
</Typography>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Inputs: `TextInput`, `EmailInput`, `PasswordInput`, `PhoneInput`
|
|
74
111
|
|
|
75
112
|
```tsx
|
|
76
113
|
import { TextInput } from "@carbonbits/sixr/text-input";
|
|
114
|
+
import { EmailInput } from "@carbonbits/sixr/email-input";
|
|
115
|
+
import { PasswordInput } from "@carbonbits/sixr/password-input";
|
|
116
|
+
import { PhoneInput } from "@carbonbits/sixr/phone-input";
|
|
77
117
|
```
|
|
78
118
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
119
|
+
Labelled single-line fields with optional schema validation. Each one is for a single
|
|
120
|
+
kind of value and fixes the input type, keyboard and autofill hints that value needs:
|
|
121
|
+
|
|
122
|
+
| Component | Value | Fixes |
|
|
123
|
+
| --------------- | ---------------- | ---------------------------------------------------------------------- |
|
|
124
|
+
| `TextInput` | Free text | `type="text"`. Adds `mono` for the monospace face (verification codes). |
|
|
125
|
+
| `EmailInput` | An email address | `type="email"`, `inputMode="email"`, `autoComplete="email"`, no autocapitalise or spellcheck. |
|
|
126
|
+
| `PasswordInput` | A secret | `type="password"`, `autoComplete="current-password"`, and a built-in Show/Hide toggle. |
|
|
127
|
+
| `PhoneInput` | A phone number | `type="tel"`, `inputMode="tel"`, `autoComplete="tel"`. |
|
|
128
|
+
|
|
129
|
+
Interactive, so each ships its own `"use client"` directive and is reachable only at
|
|
130
|
+
its subpath — none is on the root barrel, which stays server-safe.
|
|
131
|
+
|
|
132
|
+
They share these props:
|
|
82
133
|
|
|
83
134
|
| Prop | Type | Default | Description |
|
|
84
135
|
| -------------------- | ------------------------------------ | -------- | ----------------------------------------------------- |
|
|
85
136
|
| `label` | `string` | — | Field label, associated with the input. |
|
|
86
137
|
| `hint` | `ReactNode` | — | Guidance below the field. Replaced by an error. |
|
|
87
138
|
| `error` | `string` | — | Caller-supplied error. Outranks the schema. |
|
|
88
|
-
| `schema` | `
|
|
139
|
+
| `schema` | `FieldSchema` | — | Any zod schema. Only `safeParse` is called. |
|
|
89
140
|
| `validateOn` | `"blur" \| "change" \| "never"` | `"blur"` | When the schema runs. |
|
|
90
|
-
| `onValidationChange` | `(state:
|
|
141
|
+
| `onValidationChange` | `(state: FieldValidation) => void` | — | Fires on each validation pass. |
|
|
91
142
|
| `onChange` | `(value: string) => void` | — | Receives the value, not the event. |
|
|
92
143
|
| `value` | `string` | — | Makes the field controlled. |
|
|
93
144
|
| `defaultValue` | `string` | `""` | Initial value when uncontrolled. |
|
|
94
145
|
| `optional` | `boolean` | `false` | Shows an "Optional" marker on the label. |
|
|
95
146
|
| `action` | `ReactNode` | — | Trailing label slot, e.g. a "Forgot?" link. |
|
|
96
|
-
| `trailing` | `ReactNode` | — | Trailing in-field slot
|
|
97
|
-
| `mono` | `boolean` | `false` | Renders the value in the monospace face. |
|
|
98
|
-
| `type` | `"text" \| "email" \| "password" \| "tel" \| "url" \| "search"` | `"text"` | Input type. |
|
|
147
|
+
| `trailing` | `ReactNode` | — | Trailing in-field slot. Not on `PasswordInput`, which owns it. |
|
|
99
148
|
|
|
100
|
-
|
|
101
|
-
|
|
149
|
+
And their own:
|
|
150
|
+
|
|
151
|
+
| Component | Prop | Type | Default | Description |
|
|
152
|
+
| --------------- | -------------- | -------------------------------- | -------------------------------- | ------------------------------------ |
|
|
153
|
+
| `TextInput` | `mono` | `boolean` | `false` | Renders the value in the monospace face. |
|
|
154
|
+
| `PasswordInput` | `revealable` | `boolean` | `true` | Shows the Show/Hide toggle. |
|
|
155
|
+
| `PasswordInput` | `revealLabels` | `{ show: string; hide: string }` | `{ show: "Show", hide: "Hide" }` | Toggle text, for localisation. |
|
|
156
|
+
|
|
157
|
+
Any other `<input>` attribute — `placeholder`, `maxLength`, `disabled`, `autoComplete` —
|
|
158
|
+
is passed straight through, and overrides the component's default where there is one.
|
|
102
159
|
|
|
103
160
|
```tsx
|
|
104
|
-
import {
|
|
161
|
+
import { EmailInput } from "@carbonbits/sixr/email-input";
|
|
105
162
|
import { emailSchema } from "@carbonbits/sixr/validation";
|
|
106
163
|
|
|
107
|
-
<
|
|
164
|
+
<EmailInput
|
|
108
165
|
label="Work email"
|
|
109
|
-
type="email"
|
|
110
166
|
placeholder="you@company.com"
|
|
111
167
|
schema={emailSchema}
|
|
112
168
|
onChange={setEmail}
|
|
@@ -119,7 +175,9 @@ is shown, the input gets `aria-invalid`, and the message is announced via `role=
|
|
|
119
175
|
and wired up with `aria-describedby`.
|
|
120
176
|
|
|
121
177
|
`schema` is structurally typed, so any zod schema satisfies it without zod appearing
|
|
122
|
-
in this package's types — you only need zod installed if you use it.
|
|
178
|
+
in this package's types — you only need zod installed if you use it. The inputs never
|
|
179
|
+
default to a schema for the same reason; pair them with the ready-made ones under
|
|
180
|
+
"Validation schemas".
|
|
123
181
|
|
|
124
182
|
### `Form` / `FormError` / `SubmitButton`
|
|
125
183
|
|
|
@@ -140,8 +198,9 @@ is reachable only at this subpath.
|
|
|
140
198
|
| `SubmitButton`| `loadingLabel` | `ReactNode` | Shown next to the spinner in place of `children` while submitting. |
|
|
141
199
|
|
|
142
200
|
```tsx
|
|
201
|
+
import { EmailInput } from "@carbonbits/sixr/email-input";
|
|
143
202
|
import { Form, FormError, SubmitButton } from "@carbonbits/sixr/form";
|
|
144
|
-
import {
|
|
203
|
+
import { PasswordInput } from "@carbonbits/sixr/password-input";
|
|
145
204
|
|
|
146
205
|
function LoginForm() {
|
|
147
206
|
const [email, setEmail] = useState("");
|
|
@@ -156,8 +215,8 @@ function LoginForm() {
|
|
|
156
215
|
|
|
157
216
|
return (
|
|
158
217
|
<Form onSubmit={handleSubmit}>
|
|
159
|
-
<
|
|
160
|
-
<
|
|
218
|
+
<EmailInput label="Work email" onChange={setEmail} />
|
|
219
|
+
<PasswordInput label="Password" onChange={setPassword} />
|
|
161
220
|
<FormError>{error}</FormError>
|
|
162
221
|
<SubmitButton loadingLabel="Signing in…">Sign in</SubmitButton>
|
|
163
222
|
</Form>
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
2
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
// src/components/typography/index.tsx
|
|
5
|
+
var DEFAULT_ELEMENT = {
|
|
6
|
+
display: "h1",
|
|
7
|
+
heading: "h2",
|
|
8
|
+
subheading: "h3",
|
|
9
|
+
body: "p",
|
|
10
|
+
label: "span",
|
|
11
|
+
hint: "p",
|
|
12
|
+
code: "code"
|
|
13
|
+
};
|
|
14
|
+
var DEFAULT_WEIGHT = {
|
|
15
|
+
display: "semibold",
|
|
16
|
+
heading: "semibold",
|
|
17
|
+
subheading: "medium",
|
|
18
|
+
body: "regular",
|
|
19
|
+
label: "semibold",
|
|
20
|
+
hint: "regular",
|
|
21
|
+
code: "regular"
|
|
22
|
+
};
|
|
23
|
+
var VARIANT_CLASSES = {
|
|
24
|
+
display: "text-display leading-tight tracking-tight",
|
|
25
|
+
heading: "text-heading leading-tight tracking-tight",
|
|
26
|
+
subheading: "text-subheading leading-snug",
|
|
27
|
+
body: "text-field leading-normal",
|
|
28
|
+
label: "text-label leading-normal",
|
|
29
|
+
hint: "text-hint leading-normal",
|
|
30
|
+
code: "font-code text-field leading-normal"
|
|
31
|
+
};
|
|
32
|
+
var TONE_CLASSES = {
|
|
33
|
+
default: "text-fg",
|
|
34
|
+
muted: "text-fg-muted",
|
|
35
|
+
subtle: "text-fg-subtle",
|
|
36
|
+
accent: "text-accent",
|
|
37
|
+
"on-accent": "text-on-accent",
|
|
38
|
+
success: "text-success",
|
|
39
|
+
warning: "text-warning",
|
|
40
|
+
info: "text-info",
|
|
41
|
+
danger: "text-danger"
|
|
42
|
+
};
|
|
43
|
+
var WEIGHT_CLASSES = {
|
|
44
|
+
regular: "font-normal",
|
|
45
|
+
medium: "font-medium",
|
|
46
|
+
semibold: "font-semibold",
|
|
47
|
+
bold: "font-bold"
|
|
48
|
+
};
|
|
49
|
+
var ALIGN_CLASSES = {
|
|
50
|
+
start: "text-start",
|
|
51
|
+
center: "text-center",
|
|
52
|
+
end: "text-end"
|
|
53
|
+
};
|
|
54
|
+
var ICON_CLASSES = "inline-flex shrink-0 items-center [&>svg]:h-[1em] [&>svg]:w-[1em]";
|
|
55
|
+
function typographyClassName({
|
|
56
|
+
variant,
|
|
57
|
+
tone,
|
|
58
|
+
weight,
|
|
59
|
+
align,
|
|
60
|
+
truncate,
|
|
61
|
+
hasIcon,
|
|
62
|
+
className
|
|
63
|
+
}) {
|
|
64
|
+
return [
|
|
65
|
+
VARIANT_CLASSES[variant],
|
|
66
|
+
TONE_CLASSES[tone],
|
|
67
|
+
WEIGHT_CLASSES[weight],
|
|
68
|
+
align ? ALIGN_CLASSES[align] : "",
|
|
69
|
+
hasIcon ? "inline-flex items-center gap-1.5" : "",
|
|
70
|
+
// With an icon the root is a flex container, and text-overflow only
|
|
71
|
+
// clips block text, so the text span carries the truncation instead.
|
|
72
|
+
truncate && !hasIcon ? "truncate" : "",
|
|
73
|
+
className ?? ""
|
|
74
|
+
].filter(Boolean).join(" ");
|
|
75
|
+
}
|
|
76
|
+
function Typography({
|
|
77
|
+
variant = "body",
|
|
78
|
+
tone = "default",
|
|
79
|
+
weight,
|
|
80
|
+
align,
|
|
81
|
+
truncate = false,
|
|
82
|
+
icon,
|
|
83
|
+
iconPosition = "leading",
|
|
84
|
+
as,
|
|
85
|
+
asChild = false,
|
|
86
|
+
className,
|
|
87
|
+
children,
|
|
88
|
+
...rest
|
|
89
|
+
}) {
|
|
90
|
+
const hasIcon = icon !== void 0 && icon !== null;
|
|
91
|
+
const Component = asChild ? Slot : as ?? DEFAULT_ELEMENT[variant];
|
|
92
|
+
const iconNode = hasIcon ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: ICON_CLASSES, children: icon }) : null;
|
|
93
|
+
const content = hasIcon ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
94
|
+
iconPosition === "leading" ? iconNode : null,
|
|
95
|
+
/* @__PURE__ */ jsx("span", { className: truncate ? "min-w-0 truncate" : void 0, children }),
|
|
96
|
+
iconPosition === "trailing" ? iconNode : null
|
|
97
|
+
] }) : children;
|
|
98
|
+
return /* @__PURE__ */ jsx(
|
|
99
|
+
Component,
|
|
100
|
+
{
|
|
101
|
+
...rest,
|
|
102
|
+
className: typographyClassName({
|
|
103
|
+
variant,
|
|
104
|
+
tone,
|
|
105
|
+
weight: weight ?? DEFAULT_WEIGHT[variant],
|
|
106
|
+
align,
|
|
107
|
+
truncate,
|
|
108
|
+
hasIcon,
|
|
109
|
+
className
|
|
110
|
+
}),
|
|
111
|
+
children: content
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export { Typography };
|
|
117
|
+
//# sourceMappingURL=chunk-NZHHNVXZ.js.map
|
|
118
|
+
//# sourceMappingURL=chunk-NZHHNVXZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/typography/index.tsx"],"names":[],"mappings":"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","file":"chunk-NZHHNVXZ.js","sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport type { ElementType, HTMLAttributes, ReactNode, Ref } from \"react\";\n\n/**\n * The library's type scale. Everything that renders text — field labels, hints,\n * form errors, headings — goes through here, so a size or colour lives in one\n * place and the tokens in `src/styles/index.css` stay the single source of\n * truth for what \"hint\" or \"muted\" means.\n *\n * Server-safe: no state, no handlers, no directive. It composes Radix `Slot`\n * for `asChild`, which is how a consumer puts these styles on their own\n * element (a `next/link`, a Radix `Label`) without an extra wrapper.\n */\n\nexport type TypographyVariant =\n | \"display\"\n | \"heading\"\n | \"subheading\"\n | \"body\"\n | \"label\"\n | \"hint\"\n | \"code\";\n\nexport type TypographyTone =\n | \"default\"\n | \"muted\"\n | \"subtle\"\n | \"accent\"\n | \"on-accent\"\n | \"success\"\n | \"warning\"\n | \"info\"\n | \"danger\";\n\nexport type TypographyWeight = \"regular\" | \"medium\" | \"semibold\" | \"bold\";\n\nexport type TypographyElement =\n | \"p\"\n | \"span\"\n | \"div\"\n | \"h1\"\n | \"h2\"\n | \"h3\"\n | \"h4\"\n | \"h5\"\n | \"h6\"\n | \"label\"\n | \"code\"\n | \"strong\"\n | \"em\"\n | \"small\";\n\nexport type TypographyProps = Omit<HTMLAttributes<HTMLElement>, \"color\"> & {\n /** Size and default element. `body` unless told otherwise. */\n variant?: TypographyVariant;\n /** Colour role, from the design tokens. */\n tone?: TypographyTone;\n /** Overrides the variant's default weight. */\n weight?: TypographyWeight;\n align?: \"start\" | \"center\" | \"end\";\n /** Clip to one line with an ellipsis. */\n truncate?: boolean;\n /**\n * An icon rendered beside the text, hidden from assistive tech. Sized to the\n * text with `1em`, so the same SVG fits every variant.\n */\n icon?: ReactNode;\n iconPosition?: \"leading\" | \"trailing\";\n /** The element to render. Defaults per variant (`h2` for a heading, and so on). */\n as?: TypographyElement;\n /**\n * Radix-style composition: render the child element itself, with these\n * styles merged onto it, instead of a wrapper. Takes precedence over `as`.\n */\n asChild?: boolean;\n ref?: Ref<HTMLElement>;\n children?: ReactNode;\n};\n\nconst DEFAULT_ELEMENT: Record<TypographyVariant, TypographyElement> = {\n display: \"h1\",\n heading: \"h2\",\n subheading: \"h3\",\n body: \"p\",\n label: \"span\",\n hint: \"p\",\n code: \"code\",\n};\n\nconst DEFAULT_WEIGHT: Record<TypographyVariant, TypographyWeight> = {\n display: \"semibold\",\n heading: \"semibold\",\n subheading: \"medium\",\n body: \"regular\",\n label: \"semibold\",\n hint: \"regular\",\n code: \"regular\",\n};\n\nconst VARIANT_CLASSES: Record<TypographyVariant, string> = {\n display: \"text-display leading-tight tracking-tight\",\n heading: \"text-heading leading-tight tracking-tight\",\n subheading: \"text-subheading leading-snug\",\n body: \"text-field leading-normal\",\n label: \"text-label leading-normal\",\n hint: \"text-hint leading-normal\",\n code: \"font-code text-field leading-normal\",\n};\n\nconst TONE_CLASSES: Record<TypographyTone, string> = {\n default: \"text-fg\",\n muted: \"text-fg-muted\",\n subtle: \"text-fg-subtle\",\n accent: \"text-accent\",\n \"on-accent\": \"text-on-accent\",\n success: \"text-success\",\n warning: \"text-warning\",\n info: \"text-info\",\n danger: \"text-danger\",\n};\n\nconst WEIGHT_CLASSES: Record<TypographyWeight, string> = {\n regular: \"font-normal\",\n medium: \"font-medium\",\n semibold: \"font-semibold\",\n bold: \"font-bold\",\n};\n\nconst ALIGN_CLASSES = {\n start: \"text-start\",\n center: \"text-center\",\n end: \"text-end\",\n} as const;\n\nconst ICON_CLASSES =\n \"inline-flex shrink-0 items-center [&>svg]:h-[1em] [&>svg]:w-[1em]\";\n\nfunction typographyClassName({\n variant,\n tone,\n weight,\n align,\n truncate,\n hasIcon,\n className,\n}: {\n variant: TypographyVariant;\n tone: TypographyTone;\n weight: TypographyWeight;\n align: \"start\" | \"center\" | \"end\" | undefined;\n truncate: boolean;\n hasIcon: boolean;\n className: string | undefined;\n}): string {\n return [\n VARIANT_CLASSES[variant],\n TONE_CLASSES[tone],\n WEIGHT_CLASSES[weight],\n align ? ALIGN_CLASSES[align] : \"\",\n hasIcon ? \"inline-flex items-center gap-1.5\" : \"\",\n // With an icon the root is a flex container, and text-overflow only\n // clips block text, so the text span carries the truncation instead.\n truncate && !hasIcon ? \"truncate\" : \"\",\n className ?? \"\",\n ]\n .filter(Boolean)\n .join(\" \");\n}\n\nexport function Typography({\n variant = \"body\",\n tone = \"default\",\n weight,\n align,\n truncate = false,\n icon,\n iconPosition = \"leading\",\n as,\n asChild = false,\n className,\n children,\n ...rest\n}: TypographyProps) {\n const hasIcon = icon !== undefined && icon !== null;\n // Widened to `ElementType`: the union of intrinsic tags would otherwise\n // demand a `ref` typed to one specific element.\n const Component: ElementType = asChild\n ? Slot\n : (as ?? DEFAULT_ELEMENT[variant]);\n\n const iconNode = hasIcon ? (\n <span aria-hidden=\"true\" className={ICON_CLASSES}>\n {icon}\n </span>\n ) : null;\n\n const content = hasIcon ? (\n <>\n {iconPosition === \"leading\" ? iconNode : null}\n <span className={truncate ? \"min-w-0 truncate\" : undefined}>\n {children}\n </span>\n {iconPosition === \"trailing\" ? iconNode : null}\n </>\n ) : (\n children\n );\n\n return (\n <Component\n {...rest}\n className={typographyClassName({\n variant,\n tone,\n weight: weight ?? DEFAULT_WEIGHT[variant],\n align,\n truncate,\n hasIcon,\n className,\n })}\n >\n {content}\n </Component>\n );\n}\n"]}
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
"use client";
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
// src/components/email-input/index.tsx
|
|
23
|
+
var email_input_exports = {};
|
|
24
|
+
__export(email_input_exports, {
|
|
25
|
+
EmailInput: () => EmailInput
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(email_input_exports);
|
|
28
|
+
|
|
29
|
+
// src/components/field/index.tsx
|
|
30
|
+
var import_react_label = require("@radix-ui/react-label");
|
|
31
|
+
var import_react2 = require("react");
|
|
32
|
+
|
|
33
|
+
// src/components/field/use-field-behaviour.ts
|
|
34
|
+
var import_react = require("react");
|
|
35
|
+
var FALLBACK_MESSAGE = "Invalid value";
|
|
36
|
+
function runSchema(schema, value) {
|
|
37
|
+
if (!schema) {
|
|
38
|
+
return { valid: true };
|
|
39
|
+
}
|
|
40
|
+
const result = schema.safeParse(value);
|
|
41
|
+
if (result.success) {
|
|
42
|
+
return { valid: true };
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
valid: false,
|
|
46
|
+
error: result.error.issues[0]?.message ?? FALLBACK_MESSAGE
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function useFieldBehaviour({
|
|
50
|
+
value,
|
|
51
|
+
defaultValue,
|
|
52
|
+
error,
|
|
53
|
+
onChange,
|
|
54
|
+
onBlur,
|
|
55
|
+
schema,
|
|
56
|
+
validateOn = "blur",
|
|
57
|
+
onValidationChange
|
|
58
|
+
}) {
|
|
59
|
+
const [uncontrolledValue, setUncontrolledValue] = (0, import_react.useState)(
|
|
60
|
+
defaultValue ?? ""
|
|
61
|
+
);
|
|
62
|
+
const [schemaError, setSchemaError] = (0, import_react.useState)(void 0);
|
|
63
|
+
const isControlled = value !== void 0;
|
|
64
|
+
const validate = (0, import_react.useCallback)(
|
|
65
|
+
(next) => {
|
|
66
|
+
const state = runSchema(schema, next);
|
|
67
|
+
setSchemaError(state.error);
|
|
68
|
+
onValidationChange?.(state);
|
|
69
|
+
},
|
|
70
|
+
[schema, onValidationChange]
|
|
71
|
+
);
|
|
72
|
+
const handleChange = (0, import_react.useCallback)(
|
|
73
|
+
(event) => {
|
|
74
|
+
const next = event.target.value;
|
|
75
|
+
if (!isControlled) {
|
|
76
|
+
setUncontrolledValue(next);
|
|
77
|
+
}
|
|
78
|
+
onChange?.(next);
|
|
79
|
+
if (validateOn === "change" || validateOn === "blur" && schemaError) {
|
|
80
|
+
validate(next);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
[isControlled, onChange, validateOn, schemaError, validate]
|
|
84
|
+
);
|
|
85
|
+
const handleBlur = (0, import_react.useCallback)(
|
|
86
|
+
(event) => {
|
|
87
|
+
if (validateOn === "blur") {
|
|
88
|
+
validate(event.target.value);
|
|
89
|
+
}
|
|
90
|
+
onBlur?.(event);
|
|
91
|
+
},
|
|
92
|
+
[validateOn, validate, onBlur]
|
|
93
|
+
);
|
|
94
|
+
return {
|
|
95
|
+
currentValue: isControlled ? value : uncontrolledValue,
|
|
96
|
+
// A caller-supplied error outranks the schema's verdict.
|
|
97
|
+
shownError: error ?? schemaError,
|
|
98
|
+
handleChange,
|
|
99
|
+
handleBlur
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/components/typography/index.tsx
|
|
104
|
+
var import_react_slot = require("@radix-ui/react-slot");
|
|
105
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
106
|
+
var DEFAULT_ELEMENT = {
|
|
107
|
+
display: "h1",
|
|
108
|
+
heading: "h2",
|
|
109
|
+
subheading: "h3",
|
|
110
|
+
body: "p",
|
|
111
|
+
label: "span",
|
|
112
|
+
hint: "p",
|
|
113
|
+
code: "code"
|
|
114
|
+
};
|
|
115
|
+
var DEFAULT_WEIGHT = {
|
|
116
|
+
display: "semibold",
|
|
117
|
+
heading: "semibold",
|
|
118
|
+
subheading: "medium",
|
|
119
|
+
body: "regular",
|
|
120
|
+
label: "semibold",
|
|
121
|
+
hint: "regular",
|
|
122
|
+
code: "regular"
|
|
123
|
+
};
|
|
124
|
+
var VARIANT_CLASSES = {
|
|
125
|
+
display: "text-display leading-tight tracking-tight",
|
|
126
|
+
heading: "text-heading leading-tight tracking-tight",
|
|
127
|
+
subheading: "text-subheading leading-snug",
|
|
128
|
+
body: "text-field leading-normal",
|
|
129
|
+
label: "text-label leading-normal",
|
|
130
|
+
hint: "text-hint leading-normal",
|
|
131
|
+
code: "font-code text-field leading-normal"
|
|
132
|
+
};
|
|
133
|
+
var TONE_CLASSES = {
|
|
134
|
+
default: "text-fg",
|
|
135
|
+
muted: "text-fg-muted",
|
|
136
|
+
subtle: "text-fg-subtle",
|
|
137
|
+
accent: "text-accent",
|
|
138
|
+
"on-accent": "text-on-accent",
|
|
139
|
+
success: "text-success",
|
|
140
|
+
warning: "text-warning",
|
|
141
|
+
info: "text-info",
|
|
142
|
+
danger: "text-danger"
|
|
143
|
+
};
|
|
144
|
+
var WEIGHT_CLASSES = {
|
|
145
|
+
regular: "font-normal",
|
|
146
|
+
medium: "font-medium",
|
|
147
|
+
semibold: "font-semibold",
|
|
148
|
+
bold: "font-bold"
|
|
149
|
+
};
|
|
150
|
+
var ALIGN_CLASSES = {
|
|
151
|
+
start: "text-start",
|
|
152
|
+
center: "text-center",
|
|
153
|
+
end: "text-end"
|
|
154
|
+
};
|
|
155
|
+
var ICON_CLASSES = "inline-flex shrink-0 items-center [&>svg]:h-[1em] [&>svg]:w-[1em]";
|
|
156
|
+
function typographyClassName({
|
|
157
|
+
variant,
|
|
158
|
+
tone,
|
|
159
|
+
weight,
|
|
160
|
+
align,
|
|
161
|
+
truncate,
|
|
162
|
+
hasIcon,
|
|
163
|
+
className
|
|
164
|
+
}) {
|
|
165
|
+
return [
|
|
166
|
+
VARIANT_CLASSES[variant],
|
|
167
|
+
TONE_CLASSES[tone],
|
|
168
|
+
WEIGHT_CLASSES[weight],
|
|
169
|
+
align ? ALIGN_CLASSES[align] : "",
|
|
170
|
+
hasIcon ? "inline-flex items-center gap-1.5" : "",
|
|
171
|
+
// With an icon the root is a flex container, and text-overflow only
|
|
172
|
+
// clips block text, so the text span carries the truncation instead.
|
|
173
|
+
truncate && !hasIcon ? "truncate" : "",
|
|
174
|
+
className ?? ""
|
|
175
|
+
].filter(Boolean).join(" ");
|
|
176
|
+
}
|
|
177
|
+
function Typography({
|
|
178
|
+
variant = "body",
|
|
179
|
+
tone = "default",
|
|
180
|
+
weight,
|
|
181
|
+
align,
|
|
182
|
+
truncate = false,
|
|
183
|
+
icon,
|
|
184
|
+
iconPosition = "leading",
|
|
185
|
+
as,
|
|
186
|
+
asChild = false,
|
|
187
|
+
className,
|
|
188
|
+
children,
|
|
189
|
+
...rest
|
|
190
|
+
}) {
|
|
191
|
+
const hasIcon = icon !== void 0 && icon !== null;
|
|
192
|
+
const Component = asChild ? import_react_slot.Slot : as ?? DEFAULT_ELEMENT[variant];
|
|
193
|
+
const iconNode = hasIcon ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": "true", className: ICON_CLASSES, children: icon }) : null;
|
|
194
|
+
const content = hasIcon ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
195
|
+
iconPosition === "leading" ? iconNode : null,
|
|
196
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: truncate ? "min-w-0 truncate" : void 0, children }),
|
|
197
|
+
iconPosition === "trailing" ? iconNode : null
|
|
198
|
+
] }) : children;
|
|
199
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
200
|
+
Component,
|
|
201
|
+
{
|
|
202
|
+
...rest,
|
|
203
|
+
className: typographyClassName({
|
|
204
|
+
variant,
|
|
205
|
+
tone,
|
|
206
|
+
weight: weight ?? DEFAULT_WEIGHT[variant],
|
|
207
|
+
align,
|
|
208
|
+
truncate,
|
|
209
|
+
hasIcon,
|
|
210
|
+
className
|
|
211
|
+
}),
|
|
212
|
+
children: content
|
|
213
|
+
}
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/components/field/index.tsx
|
|
218
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
219
|
+
var FIELD_CLASSES = "h-10 w-full rounded-field border bg-surface px-3 font-[inherit] text-field text-fg outline-none placeholder:text-fg-subtle disabled:cursor-not-allowed disabled:opacity-45";
|
|
220
|
+
var VALID_CLASSES = "border-line focus:border-accent focus:ring-3 focus:ring-accent-soft";
|
|
221
|
+
var INVALID_CLASSES = "border-danger focus:border-danger focus:ring-3 focus:ring-danger-soft";
|
|
222
|
+
function fieldClassName(invalid, hasTrailing, className) {
|
|
223
|
+
return [
|
|
224
|
+
FIELD_CLASSES,
|
|
225
|
+
invalid ? INVALID_CLASSES : VALID_CLASSES,
|
|
226
|
+
hasTrailing ? "pr-16" : "",
|
|
227
|
+
className ?? ""
|
|
228
|
+
].filter(Boolean).join(" ");
|
|
229
|
+
}
|
|
230
|
+
function FieldLabel({
|
|
231
|
+
htmlFor,
|
|
232
|
+
label,
|
|
233
|
+
optional,
|
|
234
|
+
action
|
|
235
|
+
}) {
|
|
236
|
+
if (!label) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
240
|
+
Typography,
|
|
241
|
+
{
|
|
242
|
+
variant: "label",
|
|
243
|
+
tone: "muted",
|
|
244
|
+
className: "mb-1.5 flex items-center",
|
|
245
|
+
asChild: true,
|
|
246
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_label.Label, { htmlFor, children: [
|
|
247
|
+
label,
|
|
248
|
+
optional ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
249
|
+
Typography,
|
|
250
|
+
{
|
|
251
|
+
as: "span",
|
|
252
|
+
variant: "hint",
|
|
253
|
+
tone: "subtle",
|
|
254
|
+
weight: "semibold",
|
|
255
|
+
className: "ml-auto",
|
|
256
|
+
children: "Optional"
|
|
257
|
+
}
|
|
258
|
+
) : null,
|
|
259
|
+
action ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "ml-auto", children: action }) : null
|
|
260
|
+
] })
|
|
261
|
+
}
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
function FieldMessage({
|
|
265
|
+
id,
|
|
266
|
+
error,
|
|
267
|
+
hint
|
|
268
|
+
}) {
|
|
269
|
+
if (error) {
|
|
270
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
271
|
+
Typography,
|
|
272
|
+
{
|
|
273
|
+
as: "p",
|
|
274
|
+
variant: "hint",
|
|
275
|
+
tone: "danger",
|
|
276
|
+
id,
|
|
277
|
+
role: "alert",
|
|
278
|
+
className: "mt-1.5",
|
|
279
|
+
children: error
|
|
280
|
+
}
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
if (hint) {
|
|
284
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
285
|
+
Typography,
|
|
286
|
+
{
|
|
287
|
+
as: "p",
|
|
288
|
+
variant: "hint",
|
|
289
|
+
tone: "subtle",
|
|
290
|
+
id,
|
|
291
|
+
className: "mt-1.5",
|
|
292
|
+
children: hint
|
|
293
|
+
}
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
function Field({
|
|
299
|
+
label,
|
|
300
|
+
hint,
|
|
301
|
+
error,
|
|
302
|
+
optional = false,
|
|
303
|
+
action,
|
|
304
|
+
trailing,
|
|
305
|
+
type,
|
|
306
|
+
value,
|
|
307
|
+
defaultValue,
|
|
308
|
+
onChange,
|
|
309
|
+
schema,
|
|
310
|
+
validateOn = "blur",
|
|
311
|
+
onValidationChange,
|
|
312
|
+
id,
|
|
313
|
+
className,
|
|
314
|
+
onBlur,
|
|
315
|
+
...rest
|
|
316
|
+
}) {
|
|
317
|
+
const generatedId = (0, import_react2.useId)();
|
|
318
|
+
const inputId = id ?? generatedId;
|
|
319
|
+
const describedById = `${inputId}-description`;
|
|
320
|
+
const { currentValue, shownError, handleChange, handleBlur } = useFieldBehaviour({
|
|
321
|
+
value,
|
|
322
|
+
defaultValue,
|
|
323
|
+
error,
|
|
324
|
+
onChange,
|
|
325
|
+
onBlur,
|
|
326
|
+
schema,
|
|
327
|
+
validateOn,
|
|
328
|
+
onValidationChange
|
|
329
|
+
});
|
|
330
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col", children: [
|
|
331
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
332
|
+
FieldLabel,
|
|
333
|
+
{
|
|
334
|
+
htmlFor: inputId,
|
|
335
|
+
label,
|
|
336
|
+
optional,
|
|
337
|
+
action
|
|
338
|
+
}
|
|
339
|
+
),
|
|
340
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: trailing ? "relative" : void 0, children: [
|
|
341
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
342
|
+
"input",
|
|
343
|
+
{
|
|
344
|
+
...rest,
|
|
345
|
+
id: inputId,
|
|
346
|
+
type,
|
|
347
|
+
value: currentValue,
|
|
348
|
+
"aria-invalid": shownError ? true : void 0,
|
|
349
|
+
"aria-describedby": shownError || hint ? describedById : void 0,
|
|
350
|
+
onChange: handleChange,
|
|
351
|
+
onBlur: handleBlur,
|
|
352
|
+
className: fieldClassName(
|
|
353
|
+
Boolean(shownError),
|
|
354
|
+
Boolean(trailing),
|
|
355
|
+
className
|
|
356
|
+
)
|
|
357
|
+
}
|
|
358
|
+
),
|
|
359
|
+
trailing ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "absolute top-1/2 right-[9px] flex -translate-y-1/2 items-center text-hint font-semibold text-fg-subtle", children: trailing }) : null
|
|
360
|
+
] }),
|
|
361
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FieldMessage, { id: describedById, error: shownError, hint })
|
|
362
|
+
] });
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// src/components/email-input/index.tsx
|
|
366
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
367
|
+
function EmailInput({
|
|
368
|
+
autoComplete = "email",
|
|
369
|
+
inputMode = "email",
|
|
370
|
+
autoCapitalize = "none",
|
|
371
|
+
spellCheck = false,
|
|
372
|
+
...rest
|
|
373
|
+
}) {
|
|
374
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
375
|
+
Field,
|
|
376
|
+
{
|
|
377
|
+
...rest,
|
|
378
|
+
type: "email",
|
|
379
|
+
autoComplete,
|
|
380
|
+
inputMode,
|
|
381
|
+
autoCapitalize,
|
|
382
|
+
spellCheck
|
|
383
|
+
}
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
387
|
+
0 && (module.exports = {
|
|
388
|
+
EmailInput
|
|
389
|
+
});
|
|
390
|
+
//# sourceMappingURL=email-input.cjs.map
|