@carbonbits/sixr 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +139 -60
  2. package/dist/button.cjs +223 -0
  3. package/dist/button.cjs.map +1 -0
  4. package/dist/button.d.cts +34 -0
  5. package/dist/button.d.ts +34 -0
  6. package/dist/button.js +4 -0
  7. package/dist/button.js.map +1 -0
  8. package/dist/chunk-HDTW4FRT.js +111 -0
  9. package/dist/chunk-HDTW4FRT.js.map +1 -0
  10. package/dist/email-input.cjs +213 -170
  11. package/dist/email-input.cjs.map +1 -1
  12. package/dist/email-input.d.cts +4 -6
  13. package/dist/email-input.d.ts +4 -6
  14. package/dist/email-input.js +219 -170
  15. package/dist/email-input.js.map +1 -1
  16. package/dist/field.cjs +206 -0
  17. package/dist/field.cjs.map +1 -0
  18. package/dist/field.d.cts +80 -0
  19. package/dist/field.d.ts +80 -0
  20. package/dist/field.js +186 -0
  21. package/dist/field.js.map +1 -0
  22. package/dist/form.cjs +283 -42
  23. package/dist/form.cjs.map +1 -1
  24. package/dist/form.d.cts +79 -10
  25. package/dist/form.d.ts +79 -10
  26. package/dist/form.js +285 -45
  27. package/dist/form.js.map +1 -1
  28. package/dist/index-CdYkd6zz.d.cts +22 -0
  29. package/dist/index-CdYkd6zz.d.ts +22 -0
  30. package/dist/index.cjs +138 -34
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +116 -55
  33. package/dist/index.d.ts +116 -55
  34. package/dist/index.js +1 -0
  35. package/dist/password-input.cjs +217 -174
  36. package/dist/password-input.cjs.map +1 -1
  37. package/dist/password-input.d.cts +2 -3
  38. package/dist/password-input.d.ts +2 -3
  39. package/dist/password-input.js +222 -173
  40. package/dist/password-input.js.map +1 -1
  41. package/dist/phone-input.cjs +213 -170
  42. package/dist/phone-input.cjs.map +1 -1
  43. package/dist/phone-input.d.cts +5 -5
  44. package/dist/phone-input.d.ts +5 -5
  45. package/dist/phone-input.js +219 -170
  46. package/dist/phone-input.js.map +1 -1
  47. package/dist/styles.css +1 -1
  48. package/dist/text-input.cjs +213 -170
  49. package/dist/text-input.cjs.map +1 -1
  50. package/dist/text-input.d.cts +2 -3
  51. package/dist/text-input.d.ts +2 -3
  52. package/dist/text-input.js +219 -170
  53. package/dist/text-input.js.map +1 -1
  54. package/package.json +26 -2
  55. package/dist/index-DwjxC4l4.d.cts +0 -62
  56. package/dist/index-DwjxC4l4.d.ts +0 -62
@@ -0,0 +1,111 @@
1
+ import { Typography } from './chunk-NZHHNVXZ.js';
2
+ import { Slot } from '@radix-ui/react-slot';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+
5
+ var BASE_CLASSES = "inline-flex shrink-0 items-center justify-center gap-2 rounded-field border font-[inherit] text-field font-semibold outline-none transition-colors focus-visible:ring-3 focus-visible:ring-accent-soft disabled:cursor-not-allowed disabled:opacity-45";
6
+ var VARIANT_CLASSES = {
7
+ primary: "border-transparent bg-accent text-on-accent enabled:hover:bg-accent-2",
8
+ secondary: "border-line bg-surface text-fg enabled:hover:bg-surface-2",
9
+ ghost: "border-transparent bg-transparent text-fg enabled:hover:bg-surface-2"
10
+ };
11
+ var SIZE_CLASSES = {
12
+ md: "h-10 px-4",
13
+ lg: "h-11 px-5"
14
+ };
15
+ var LABEL_TONE = {
16
+ primary: "on-accent",
17
+ secondary: "default",
18
+ ghost: "default"
19
+ };
20
+ function buttonClassName({
21
+ variant,
22
+ size,
23
+ fullWidth,
24
+ className
25
+ }) {
26
+ return [
27
+ BASE_CLASSES,
28
+ VARIANT_CLASSES[variant],
29
+ SIZE_CLASSES[size],
30
+ fullWidth ? "w-full" : "",
31
+ className ?? ""
32
+ ].filter(Boolean).join(" ");
33
+ }
34
+ function Spinner() {
35
+ return /* @__PURE__ */ jsxs(
36
+ "svg",
37
+ {
38
+ className: "h-4 w-4 animate-spin",
39
+ viewBox: "0 0 24 24",
40
+ fill: "none",
41
+ "aria-hidden": "true",
42
+ "data-testid": "button-spinner",
43
+ children: [
44
+ /* @__PURE__ */ jsx(
45
+ "circle",
46
+ {
47
+ className: "opacity-25",
48
+ cx: "12",
49
+ cy: "12",
50
+ r: "10",
51
+ stroke: "currentColor",
52
+ strokeWidth: "4"
53
+ }
54
+ ),
55
+ /* @__PURE__ */ jsx(
56
+ "path",
57
+ {
58
+ className: "opacity-75",
59
+ fill: "currentColor",
60
+ d: "M4 12a8 8 0 0 1 8-8V0C5.373 0 0 5.373 0 12H4Z"
61
+ }
62
+ )
63
+ ]
64
+ }
65
+ );
66
+ }
67
+ function Button({
68
+ variant = "primary",
69
+ size = "md",
70
+ fullWidth = false,
71
+ loading = false,
72
+ loadingLabel,
73
+ asChild = false,
74
+ type = "button",
75
+ disabled,
76
+ className,
77
+ children,
78
+ ...rest
79
+ }) {
80
+ const classes = buttonClassName({ variant, size, fullWidth, className });
81
+ if (asChild) {
82
+ return /* @__PURE__ */ jsx(Slot, { ...rest, className: classes, children });
83
+ }
84
+ return /* @__PURE__ */ jsx(
85
+ "button",
86
+ {
87
+ ...rest,
88
+ type,
89
+ disabled: disabled || loading,
90
+ "aria-busy": loading || void 0,
91
+ className: classes,
92
+ children: loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
93
+ /* @__PURE__ */ jsx(Spinner, {}),
94
+ loadingLabel
95
+ ] }) : /* @__PURE__ */ jsx(
96
+ Typography,
97
+ {
98
+ as: "span",
99
+ variant: "body",
100
+ weight: "semibold",
101
+ tone: LABEL_TONE[variant],
102
+ children
103
+ }
104
+ )
105
+ }
106
+ );
107
+ }
108
+
109
+ export { Button };
110
+ //# sourceMappingURL=chunk-HDTW4FRT.js.map
111
+ //# sourceMappingURL=chunk-HDTW4FRT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/button/index.tsx"],"names":[],"mappings":"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","file":"chunk-HDTW4FRT.js","sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport type { ButtonHTMLAttributes, ReactNode, Ref } from \"react\";\nimport { Typography, type TypographyTone } from \"@/components/typography\";\n\n/**\n * The design's primary action, as the auth screens use it: a 40px (or 44px)\n * accent-filled control with the field type scale, a 4px radius and the\n * shared 45% disabled opacity. `secondary` and `ghost` cover the quieter\n * actions that sit beside it.\n *\n * Server-safe: no state, no handlers of its own. `asChild` composes onto a\n * consumer's element — a `next/link` for a navigation styled as a button —\n * through Radix `Slot`, so the link keeps its own semantics. `loading` swaps\n * the label for a spinner and disables the control; it applies to the\n * rendered `<button>` only, since a child element owns its own content.\n */\n\nexport type ButtonVariant = \"primary\" | \"secondary\" | \"ghost\";\nexport type ButtonSize = \"md\" | \"lg\";\n\nexport type ButtonProps = Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n \"children\"\n> & {\n variant?: ButtonVariant;\n size?: ButtonSize;\n /** Stretch to the container, as the auth forms' submit does. */\n fullWidth?: boolean;\n /** Shows a spinner in place of the label and disables the control. */\n loading?: boolean;\n /** Shown next to the spinner while `loading`; nothing but the spinner otherwise. */\n loadingLabel?: ReactNode;\n /** Merge the styles onto the child element instead of rendering a button. */\n asChild?: boolean;\n ref?: Ref<HTMLButtonElement>;\n children: ReactNode;\n};\n\nconst BASE_CLASSES =\n \"inline-flex shrink-0 items-center justify-center gap-2 rounded-field border font-[inherit] text-field font-semibold outline-none transition-colors focus-visible:ring-3 focus-visible:ring-accent-soft disabled:cursor-not-allowed disabled:opacity-45\";\n\nconst VARIANT_CLASSES: Record<ButtonVariant, string> = {\n primary:\n \"border-transparent bg-accent text-on-accent enabled:hover:bg-accent-2\",\n secondary: \"border-line bg-surface text-fg enabled:hover:bg-surface-2\",\n ghost: \"border-transparent bg-transparent text-fg enabled:hover:bg-surface-2\",\n};\n\nconst SIZE_CLASSES: Record<ButtonSize, string> = {\n md: \"h-10 px-4\",\n lg: \"h-11 px-5\",\n};\n\nconst LABEL_TONE: Record<ButtonVariant, TypographyTone> = {\n primary: \"on-accent\",\n secondary: \"default\",\n ghost: \"default\",\n};\n\nfunction buttonClassName({\n variant,\n size,\n fullWidth,\n className,\n}: {\n variant: ButtonVariant;\n size: ButtonSize;\n fullWidth: boolean;\n className: string | undefined;\n}): string {\n return [\n BASE_CLASSES,\n VARIANT_CLASSES[variant],\n SIZE_CLASSES[size],\n fullWidth ? \"w-full\" : \"\",\n className ?? \"\",\n ]\n .filter(Boolean)\n .join(\" \");\n}\n\nfunction Spinner() {\n return (\n <svg\n className=\"h-4 w-4 animate-spin\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n data-testid=\"button-spinner\"\n >\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 0 1 8-8V0C5.373 0 0 5.373 0 12H4Z\"\n />\n </svg>\n );\n}\n\nexport function Button({\n variant = \"primary\",\n size = \"md\",\n fullWidth = false,\n loading = false,\n loadingLabel,\n asChild = false,\n type = \"button\",\n disabled,\n className,\n children,\n ...rest\n}: ButtonProps) {\n const classes = buttonClassName({ variant, size, fullWidth, className });\n\n if (asChild) {\n return (\n <Slot {...rest} className={classes}>\n {children}\n </Slot>\n );\n }\n\n return (\n <button\n {...rest}\n type={type}\n disabled={disabled || loading}\n aria-busy={loading || undefined}\n className={classes}\n >\n {loading ? (\n <>\n <Spinner />\n {loadingLabel}\n </>\n ) : (\n <Typography\n as=\"span\"\n variant=\"body\"\n weight=\"semibold\"\n tone={LABEL_TONE[variant]}\n >\n {children}\n </Typography>\n )}\n </button>\n );\n}\n"]}
@@ -28,81 +28,173 @@ module.exports = __toCommonJS(email_input_exports);
28
28
 
29
29
  // src/components/field/index.tsx
30
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
31
+ var import_react_slot = require("@radix-ui/react-slot");
34
32
  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 };
33
+ var import_react_hook_form = require("react-hook-form");
34
+ var import_jsx_runtime = require("react/jsx-runtime");
35
+ var FieldContext = (0, import_react.createContext)(null);
36
+ function useFieldContext() {
37
+ const context = (0, import_react.useContext)(FieldContext);
38
+ if (!context) {
39
+ throw new Error("Field primitives must be rendered inside FieldRoot");
43
40
  }
41
+ return context;
42
+ }
43
+ function useFieldIds(id) {
44
+ const generated = (0, import_react.useId)();
45
+ const base = id ?? generated;
44
46
  return {
45
- valid: false,
46
- error: result.error.issues[0]?.message ?? FALLBACK_MESSAGE
47
+ id: base,
48
+ descriptionId: `${base}-description`,
49
+ messageId: `${base}-message`
47
50
  };
48
51
  }
49
- function useFieldBehaviour({
52
+ function FieldShell({
50
53
  value,
51
- defaultValue,
52
- error,
53
- onChange,
54
- onBlur,
55
- schema,
56
- validateOn = "blur",
57
- onValidationChange
54
+ asChild = false,
55
+ children,
56
+ ...rest
58
57
  }) {
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]
58
+ const [hasDescription, setHasDescription] = (0, import_react.useState)(false);
59
+ const Component = asChild ? import_react_slot.Slot : "div";
60
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
61
+ FieldContext.Provider,
62
+ {
63
+ value: { ...value, hasDescription, setHasDescription },
64
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
65
+ Component,
66
+ {
67
+ ...rest,
68
+ "data-invalid": value.invalid ? "" : void 0,
69
+ "data-disabled": value.disabled ? "" : void 0,
70
+ children
71
+ }
72
+ )
73
+ }
71
74
  );
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);
75
+ }
76
+ function ConnectedFieldRoot({
77
+ name,
78
+ error,
79
+ required = false,
80
+ disabled = false,
81
+ id,
82
+ ...rest
83
+ }) {
84
+ const ids = useFieldIds(id);
85
+ const { field, fieldState } = (0, import_react_hook_form.useController)({ name, disabled });
86
+ const shownError = error ?? fieldState.error?.message;
87
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
88
+ FieldShell,
89
+ {
90
+ ...rest,
91
+ value: {
92
+ ...ids,
93
+ name,
94
+ error: shownError,
95
+ invalid: Boolean(shownError),
96
+ required,
97
+ disabled: Boolean(field.disabled ?? disabled),
98
+ bindings: {
99
+ name: field.name,
100
+ value: field.value ?? "",
101
+ onChange: field.onChange,
102
+ onBlur: field.onBlur,
103
+ ref: field.ref
104
+ }
81
105
  }
82
- },
83
- [isControlled, onChange, validateOn, schemaError, validate]
106
+ }
84
107
  );
85
- const handleBlur = (0, import_react.useCallback)(
86
- (event) => {
87
- if (validateOn === "blur") {
88
- validate(event.target.value);
108
+ }
109
+ function StandaloneFieldRoot({
110
+ name,
111
+ error,
112
+ required = false,
113
+ disabled = false,
114
+ id,
115
+ ...rest
116
+ }) {
117
+ const ids = useFieldIds(id);
118
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
119
+ FieldShell,
120
+ {
121
+ ...rest,
122
+ value: {
123
+ ...ids,
124
+ name,
125
+ error,
126
+ invalid: Boolean(error),
127
+ required,
128
+ disabled,
129
+ bindings: { name }
89
130
  }
90
- onBlur?.(event);
91
- },
92
- [validateOn, validate, onBlur]
131
+ }
93
132
  );
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
133
+ }
134
+ function FieldRoot(props) {
135
+ const form = (0, import_react_hook_form.useFormContext)();
136
+ return form ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConnectedFieldRoot, { ...props }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StandaloneFieldRoot, { ...props });
137
+ }
138
+ function FieldLabel(props) {
139
+ const { id } = useFieldContext();
140
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_label.Label, { ...props, htmlFor: id });
141
+ }
142
+ function describedBy({
143
+ error,
144
+ messageId,
145
+ hasDescription,
146
+ descriptionId
147
+ }) {
148
+ if (error) {
149
+ return messageId;
150
+ }
151
+ return hasDescription ? descriptionId : void 0;
152
+ }
153
+ function FieldControl(props) {
154
+ const context = useFieldContext();
155
+ const { id, invalid, required, disabled, bindings } = context;
156
+ const controlProps = {
157
+ ...bindings,
158
+ id,
159
+ "aria-invalid": invalid || void 0,
160
+ "aria-required": required || void 0,
161
+ "aria-describedby": describedBy(context),
162
+ disabled: disabled || void 0
100
163
  };
164
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_slot.Slot, { ...props, ...controlProps });
165
+ }
166
+ function FieldDescription({
167
+ asChild = false,
168
+ ...props
169
+ }) {
170
+ const { descriptionId, error, setHasDescription } = useFieldContext();
171
+ (0, import_react.useEffect)(() => {
172
+ setHasDescription(true);
173
+ return () => setHasDescription(false);
174
+ }, [setHasDescription]);
175
+ if (error) {
176
+ return null;
177
+ }
178
+ const Component = asChild ? import_react_slot.Slot : "p";
179
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, { ...props, id: descriptionId });
180
+ }
181
+ function FieldMessage({
182
+ asChild = false,
183
+ children,
184
+ ...props
185
+ }) {
186
+ const { messageId, error } = useFieldContext();
187
+ const content = error ?? children;
188
+ if (!content) {
189
+ return null;
190
+ }
191
+ const Component = asChild ? import_react_slot.Slot : "p";
192
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, { ...props, id: messageId, role: "alert", children: content });
101
193
  }
102
194
 
103
195
  // src/components/typography/index.tsx
104
- var import_react_slot = require("@radix-ui/react-slot");
105
- var import_jsx_runtime = require("react/jsx-runtime");
196
+ var import_react_slot2 = require("@radix-ui/react-slot");
197
+ var import_jsx_runtime2 = require("react/jsx-runtime");
106
198
  var DEFAULT_ELEMENT = {
107
199
  display: "h1",
108
200
  heading: "h2",
@@ -189,14 +281,14 @@ function Typography({
189
281
  ...rest
190
282
  }) {
191
283
  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: [
284
+ const Component = asChild ? import_react_slot2.Slot : as ?? DEFAULT_ELEMENT[variant];
285
+ const iconNode = hasIcon ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { "aria-hidden": "true", className: ICON_CLASSES, children: icon }) : null;
286
+ const content = hasIcon ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
195
287
  iconPosition === "leading" ? iconNode : null,
196
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: truncate ? "min-w-0 truncate" : void 0, children }),
288
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: truncate ? "min-w-0 truncate" : void 0, children }),
197
289
  iconPosition === "trailing" ? iconNode : null
198
290
  ] }) : children;
199
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
291
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
200
292
  Component,
201
293
  {
202
294
  ...rest,
@@ -214,38 +306,26 @@ function Typography({
214
306
  );
215
307
  }
216
308
 
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";
309
+ // src/components/input/index.tsx
310
+ var import_jsx_runtime3 = require("react/jsx-runtime");
311
+ var CONTROL_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
312
  var VALID_CLASSES = "border-line focus:border-accent focus:ring-3 focus:ring-accent-soft";
221
313
  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,
314
+ function InputLabel({
232
315
  label,
233
316
  optional,
234
317
  action
235
318
  }) {
236
- if (!label) {
237
- return null;
238
- }
239
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
319
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
240
320
  Typography,
241
321
  {
242
322
  variant: "label",
243
323
  tone: "muted",
244
324
  className: "mb-1.5 flex items-center",
245
325
  asChild: true,
246
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_label.Label, { htmlFor, children: [
326
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(FieldLabel, { children: [
247
327
  label,
248
- optional ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
328
+ optional ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
249
329
  Typography,
250
330
  {
251
331
  as: "span",
@@ -256,114 +336,77 @@ function FieldLabel({
256
336
  children: "Optional"
257
337
  }
258
338
  ) : null,
259
- action ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "ml-auto", children: action }) : null
339
+ action ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "ml-auto", children: action }) : null
260
340
  ] })
261
341
  }
262
342
  );
263
343
  }
264
- function FieldMessage({
265
- id,
266
- error,
267
- hint
344
+ function InputControl({
345
+ hasTrailing,
346
+ className,
347
+ ...rest
268
348
  }) {
349
+ const { invalid } = useFieldContext();
350
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FieldControl, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
351
+ "input",
352
+ {
353
+ ...rest,
354
+ className: [
355
+ CONTROL_CLASSES,
356
+ invalid ? INVALID_CLASSES : VALID_CLASSES,
357
+ hasTrailing ? "pr-16" : "",
358
+ className ?? ""
359
+ ].filter(Boolean).join(" ")
360
+ }
361
+ ) });
362
+ }
363
+ function InputMessages({ hint }) {
364
+ const { error } = useFieldContext();
269
365
  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
- );
366
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Typography, { as: "p", variant: "hint", tone: "danger", asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FieldMessage, { className: "mt-1.5" }) });
282
367
  }
283
368
  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
- );
369
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Typography, { as: "p", variant: "hint", tone: "subtle", asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FieldDescription, { className: "mt-1.5", children: hint }) });
295
370
  }
296
371
  return null;
297
372
  }
298
- function Field({
373
+ function InputField({
374
+ name,
299
375
  label,
300
376
  hint,
301
377
  error,
302
378
  optional = false,
303
379
  action,
304
380
  trailing,
305
- type,
306
- value,
307
- defaultValue,
308
- onChange,
309
- schema,
310
- validateOn = "blur",
311
- onValidationChange,
312
381
  id,
313
- className,
314
- onBlur,
382
+ disabled,
383
+ required,
384
+ type,
315
385
  ...rest
316
386
  }) {
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
- ] });
387
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
388
+ FieldRoot,
389
+ {
390
+ name,
391
+ id,
392
+ error,
393
+ disabled,
394
+ required,
395
+ className: "flex flex-col",
396
+ children: [
397
+ label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(InputLabel, { label, optional, action }) : null,
398
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: trailing ? "relative" : void 0, children: [
399
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(InputControl, { ...rest, type, hasTrailing: Boolean(trailing) }),
400
+ trailing ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "absolute top-1/2 right-[9px] flex -translate-y-1/2 items-center font-semibold text-fg-subtle text-hint", children: trailing }) : null
401
+ ] }),
402
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(InputMessages, { hint })
403
+ ]
404
+ }
405
+ );
363
406
  }
364
407
 
365
408
  // src/components/email-input/index.tsx
366
- var import_jsx_runtime3 = require("react/jsx-runtime");
409
+ var import_jsx_runtime4 = require("react/jsx-runtime");
367
410
  function EmailInput({
368
411
  autoComplete = "email",
369
412
  inputMode = "email",
@@ -371,8 +414,8 @@ function EmailInput({
371
414
  spellCheck = false,
372
415
  ...rest
373
416
  }) {
374
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
375
- Field,
417
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
418
+ InputField,
376
419
  {
377
420
  ...rest,
378
421
  type: "email",