@morze/ui 0.2.12 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +171 -11
- package/dist/form/index.cjs +128 -0
- package/dist/form/index.cjs.map +1 -0
- package/dist/form/index.d.cts +58 -0
- package/dist/form/index.d.ts +58 -0
- package/dist/form/index.js +99 -0
- package/dist/form/index.js.map +1 -0
- package/dist/index.cjs +1652 -168
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +346 -8
- package/dist/index.d.ts +346 -8
- package/dist/index.js +1583 -169
- package/dist/index.js.map +1 -1
- package/dist/label-Bf37KoRc.d.cts +6 -0
- package/dist/label-Bf37KoRc.d.ts +6 -0
- package/dist/locales/en.cjs +10 -1
- package/dist/locales/en.cjs.map +1 -1
- package/dist/locales/en.d.cts +4 -2
- package/dist/locales/en.d.ts +4 -2
- package/dist/locales/en.js +10 -2
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/ru.cjs +10 -1
- package/dist/locales/ru.cjs.map +1 -1
- package/dist/locales/ru.d.cts +4 -2
- package/dist/locales/ru.d.ts +4 -2
- package/dist/locales/ru.js +10 -2
- package/dist/locales/ru.js.map +1 -1
- package/dist/morze-ui.css +1 -1
- package/dist/types-B3pfG-Kz.d.cts +164 -0
- package/dist/types-B3pfG-Kz.d.ts +164 -0
- package/package.json +15 -3
- package/dist/types-B8jz2FF9.d.cts +0 -75
- package/dist/types-B8jz2FF9.d.ts +0 -75
package/README.md
CHANGED
|
@@ -251,13 +251,13 @@ The full list lives in `dist/morze-ui-tokens.css`.
|
|
|
251
251
|
|
|
252
252
|
## Components
|
|
253
253
|
|
|
254
|
-
| Input and actions | Navigation
|
|
255
|
-
| --- | --- | --- |
|
|
256
|
-
| `Button` `Toggle` `ToggleGroup` | `Tabs` `Accordion` | `Dialog` |
|
|
257
|
-
| `Checkbox` `RadioGroup` `Switch` | `
|
|
258
|
-
| `Slider` `Input` `Textarea` | `
|
|
259
|
-
| `Label` `Field` `Select` | `
|
|
260
|
-
| `
|
|
254
|
+
| Input and actions | Navigation | Output | Overlays |
|
|
255
|
+
| --- | --- | --- | --- |
|
|
256
|
+
| `Button` `Toggle` `ToggleGroup` | `Tabs` `Accordion` | `Card` `Alert` `Badge` | `Dialog` `AlertDialog` |
|
|
257
|
+
| `Checkbox` `RadioGroup` `Switch` | `Sidebar` `Breadcrumb` | `Progress` `Avatar` | `DropdownMenu` `Menubar` |
|
|
258
|
+
| `Slider` `Input` `Textarea` | `Menubar` `NavigationMenu` | `Separator` `Skeleton` `Spinner` | `Popover` `Tooltip` |
|
|
259
|
+
| `Label` `Field` `Select` | `ScrollArea` | `Table` `Chart` | `Sheet` `Toast` |
|
|
260
|
+
| `Calendar` `DataTable` | | | |
|
|
261
261
|
|
|
262
262
|
The API mirrors shadcn/ui: same names, same sub-component composition,
|
|
263
263
|
`asChild`, a `data-slot` on every part — so examples from the shadcn docs work
|
|
@@ -286,6 +286,16 @@ What this kit adds on top:
|
|
|
286
286
|
`label={null}` makes it purely decorative.
|
|
287
287
|
- `Badge` — `variant`: `solid` `soft` `outline`.
|
|
288
288
|
- `Field` / `FieldHint` / `FieldError` — form scaffolding.
|
|
289
|
+
- `TabsList` — `overflow`: `wrap` (default), `scroll`, or `menu`, which
|
|
290
|
+
collapses whatever does not fit into a trailing menu. See [Tabs that run out
|
|
291
|
+
of room](#tabs-that-run-out-of-room).
|
|
292
|
+
- `AlertDialogAction` / `AlertDialogCancel` — `variant`, `size` and `tone` from
|
|
293
|
+
`Button`, so a destructive confirmation is `tone="danger"`.
|
|
294
|
+
- `Table` — brings its own horizontal scroll container, because a wide table
|
|
295
|
+
otherwise scrolls the page instead of itself; `containerRef` hands that
|
|
296
|
+
element back for a synchronised header or a virtualiser.
|
|
297
|
+
- `ScrollArea` — the kit's scrollbar in place of the platform's, so a pane
|
|
298
|
+
looks scrollable on macOS too. `viewportRef` reaches the scrolling element.
|
|
289
299
|
|
|
290
300
|
```tsx
|
|
291
301
|
<ToggleGroup type="single" defaultValue="week" appearance="segmented">
|
|
@@ -294,6 +304,149 @@ What this kit adds on top:
|
|
|
294
304
|
</ToggleGroup>
|
|
295
305
|
```
|
|
296
306
|
|
|
307
|
+
## Tabs that run out of room
|
|
308
|
+
|
|
309
|
+
`overflow="menu"` keeps a tab row on one line and folds the rest into a menu:
|
|
310
|
+
|
|
311
|
+
```tsx
|
|
312
|
+
<TabsList overflow="menu" overflowLabel="More">
|
|
313
|
+
{sections.map((s) => <TabsTrigger key={s.id} value={s.id}>{s.name}</TabsTrigger>)}
|
|
314
|
+
</TabsList>
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Every trigger stays mounted inside the list — Radix wires them into one
|
|
318
|
+
roving-focus group and a trigger outside it throws — so the collapsed ones are
|
|
319
|
+
hidden rather than unmounted, which also takes them out of the tab order. The
|
|
320
|
+
button lights up when the *selected* tab is one of the hidden ones, so the row
|
|
321
|
+
never looks as if nothing is chosen. Widths are measured off a mirror row of
|
|
322
|
+
inert spans: a hidden trigger measures zero, and a second row of real triggers
|
|
323
|
+
would duplicate every `value`.
|
|
324
|
+
|
|
325
|
+
## Calendar
|
|
326
|
+
|
|
327
|
+
A month grid with no date library behind it. Names come from `Intl`, so a
|
|
328
|
+
locale tag is the whole of the localisation story.
|
|
329
|
+
|
|
330
|
+
```tsx
|
|
331
|
+
<Calendar mode="single" selected={date} onSelect={setDate} locale="ru-RU" />
|
|
332
|
+
|
|
333
|
+
<Calendar
|
|
334
|
+
mode="range"
|
|
335
|
+
numberOfMonths={2}
|
|
336
|
+
selected={range}
|
|
337
|
+
onSelect={setRange}
|
|
338
|
+
disabled={{ before: new Date() }}
|
|
339
|
+
/>
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
`mode`: `single` (clicking the chosen day clears it unless `required`),
|
|
343
|
+
`multiple`, `range` (the ends are ordered for you, and the span under the
|
|
344
|
+
pointer previews before you commit). `disabled` takes a `Date`, a `Date[]`, a
|
|
345
|
+
`{ from, to }` span, an open `{ before, after }` bound, or a predicate.
|
|
346
|
+
`fromDate` / `toDate` bound both selection and paging; `captionLayout="dropdown"`
|
|
347
|
+
swaps the caption for month and year selects; `showWeekNumbers` adds the ISO
|
|
348
|
+
column; `weekStartsOn` overrides what the locale says.
|
|
349
|
+
|
|
350
|
+
Six week rows are always drawn, so paging never resizes the popover it sits in.
|
|
351
|
+
Arrows move a day, `Home`/`End` a week, `PageUp`/`PageDown` a month, and exactly
|
|
352
|
+
one day is ever in the tab order.
|
|
353
|
+
|
|
354
|
+
## Toast
|
|
355
|
+
|
|
356
|
+
`Toaster` mounts once; `toast()` is a plain function, callable from a fetch
|
|
357
|
+
handler or a store — the places a failure actually happens are rarely places
|
|
358
|
+
that can call a hook.
|
|
359
|
+
|
|
360
|
+
```tsx
|
|
361
|
+
// once, at the root
|
|
362
|
+
<Toaster position="bottom-right" />
|
|
363
|
+
|
|
364
|
+
// anywhere
|
|
365
|
+
toast({ title: 'Saved', tone: 'success' })
|
|
366
|
+
|
|
367
|
+
const t = toast({ title: 'Uploading…', duration: Infinity })
|
|
368
|
+
t.update({ title: 'Uploaded', tone: 'success', duration: 4000 })
|
|
369
|
+
t.dismiss()
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Three live at once; beyond that the oldest is dropped, because a taller stack
|
|
373
|
+
is a log and nobody reads a log that is covering the page. `useToast()` returns
|
|
374
|
+
the same `{ toasts, toast, dismiss }` for the rare case that needs to render
|
|
375
|
+
the queue itself.
|
|
376
|
+
|
|
377
|
+
## Chart
|
|
378
|
+
|
|
379
|
+
The kit does not draw charts — it dresses them. `ChartContainer` publishes each
|
|
380
|
+
series colour as a CSS custom property scoped to itself, so any library that
|
|
381
|
+
takes a CSS colour can consume it, and the tooltip and legend read like the
|
|
382
|
+
rest of the interface.
|
|
383
|
+
|
|
384
|
+
```tsx
|
|
385
|
+
const config = {
|
|
386
|
+
shipped: { label: 'Shipped', color: 'var(--mz-primary)' },
|
|
387
|
+
returned: { label: 'Returned', theme: { light: '#c2410c', dark: '#fb923c' } },
|
|
388
|
+
} satisfies ChartConfig
|
|
389
|
+
|
|
390
|
+
<ChartContainer config={config} className="h-64">
|
|
391
|
+
<ResponsiveContainer>
|
|
392
|
+
<BarChart data={rows}>
|
|
393
|
+
<Bar dataKey="shipped" fill="var(--color-shipped)" />
|
|
394
|
+
<Tooltip content={<ChartTooltipContent />} />
|
|
395
|
+
</BarChart>
|
|
396
|
+
</ResponsiveContainer>
|
|
397
|
+
</ChartContainer>
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
The responsive wrapper stays yours: sizing belongs to the charting library, and
|
|
401
|
+
a container that owned it would only ever work with one of them. Config values
|
|
402
|
+
are validated before they are interpolated into the stylesheet — a config is
|
|
403
|
+
data, and data ends up coming from a server.
|
|
404
|
+
|
|
405
|
+
`ChartTooltipContent` and `ChartLegendContent` take `nameKey` (and the tooltip
|
|
406
|
+
also `labelKey`) for the charts whose series key lives *inside* the datum
|
|
407
|
+
rather than in `dataKey` — a pie, where every slice comes from one series:
|
|
408
|
+
|
|
409
|
+
```tsx
|
|
410
|
+
<Tooltip content={<ChartTooltipContent nameKey="material" hideLabel />} />
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
`ChartTooltip` and `ChartLegend` are not exported: they were only ever
|
|
414
|
+
recharts' own `Tooltip` and `Legend`, so import those from recharts directly.
|
|
415
|
+
|
|
416
|
+
## Forms
|
|
417
|
+
|
|
418
|
+
React Hook Form bindings live behind their own entry point, because
|
|
419
|
+
`react-hook-form` is an *optional* peer dependency — an application that does
|
|
420
|
+
not use it should not carry it:
|
|
421
|
+
|
|
422
|
+
```tsx
|
|
423
|
+
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage }
|
|
424
|
+
from '@morze/ui/form'
|
|
425
|
+
|
|
426
|
+
<Form {...form}>
|
|
427
|
+
<FormField
|
|
428
|
+
control={form.control}
|
|
429
|
+
name="email"
|
|
430
|
+
render={({ field }) => (
|
|
431
|
+
<FormItem>
|
|
432
|
+
<FormLabel>Email</FormLabel>
|
|
433
|
+
<FormControl><Input {...field} /></FormControl>
|
|
434
|
+
<FormMessage />
|
|
435
|
+
</FormItem>
|
|
436
|
+
)}
|
|
437
|
+
/>
|
|
438
|
+
</Form>
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
What it adds over `Field` is the wiring nobody enjoys writing: one generated id
|
|
442
|
+
per field, `htmlFor` on the label, `aria-describedby` pointing at both the
|
|
443
|
+
description and the message, and `aria-invalid` flipped by the field's own
|
|
444
|
+
state. `FormMessage` renders nothing at all when there is no error, so a form
|
|
445
|
+
does not reserve a blank line under every input.
|
|
446
|
+
|
|
447
|
+
Without React Hook Form, `Field` / `FieldHint` / `FieldError` from the main
|
|
448
|
+
entry are the same layout with none of the binding.
|
|
449
|
+
|
|
297
450
|
## Sidebar
|
|
298
451
|
|
|
299
452
|
A navigation column: it collapses into an icon rail, turns into a sliding sheet
|
|
@@ -506,8 +659,13 @@ column pinned, it does not reflow into cards.
|
|
|
506
659
|
## Localisation
|
|
507
660
|
|
|
508
661
|
Component strings default to English and every one of them can be replaced.
|
|
509
|
-
`Dialog`/`Sheet` take `closeLabel`, `Spinner` takes `label`, `Sidebar`
|
|
510
|
-
`mobileTitle` / `mobileDescription
|
|
662
|
+
`Dialog`/`Sheet`/`Toaster` take `closeLabel`, `Spinner` takes `label`, `Sidebar`
|
|
663
|
+
takes `mobileTitle` / `mobileDescription`, `SidebarTrigger` takes `label`,
|
|
664
|
+
`BreadcrumbEllipsis` takes `label` and `TabsList` takes `overflowLabel`.
|
|
665
|
+
|
|
666
|
+
`Calendar` reads its month, weekday and day names from `Intl` — a `locale` tag
|
|
667
|
+
is enough — and takes a partial `labels` object for the four strings `Intl`
|
|
668
|
+
cannot supply (the nav buttons, the caption selects, the week-number column).
|
|
511
669
|
|
|
512
670
|
`DataTable` has more strings than the rest, so it takes a partial `labels`
|
|
513
671
|
object — anything omitted falls back to the English default:
|
|
@@ -537,14 +695,16 @@ does not hand-carry ~50 strings — and does not silently drift as the kit adds
|
|
|
537
695
|
them:
|
|
538
696
|
|
|
539
697
|
```tsx
|
|
540
|
-
import { dataTable as ru, common } from '@morze/ui/locales/ru'
|
|
698
|
+
import { dataTable as ru, calendar, common } from '@morze/ui/locales/ru'
|
|
541
699
|
|
|
542
700
|
<DataTable labels={ru} locale="ru-RU" … />
|
|
701
|
+
<Calendar labels={calendar} locale="ru-RU" … />
|
|
543
702
|
<Dialog><DialogContent closeLabel={common.close}>…</DialogContent></Dialog>
|
|
544
703
|
```
|
|
545
704
|
|
|
546
705
|
`common` carries the strings the rest of the kit takes as individual props
|
|
547
|
-
(`close`, `loading`, `sidebarNavigation`, `sidebarSections`,
|
|
706
|
+
(`close`, `loading`, `more`, `sidebarNavigation`, `sidebarSections`,
|
|
707
|
+
`toggleSidebar`), and `calendar` the four the month grid needs.
|
|
548
708
|
A bundle is plain data — no React, no styles — so it is safe to import from a
|
|
549
709
|
server component. `@morze/ui/locales/en` is the same shape for English, and
|
|
550
710
|
`MorzeLocale` types a language of your own.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var React2 = require('react');
|
|
5
|
+
var reactHookForm = require('react-hook-form');
|
|
6
|
+
var radixUi = require('radix-ui');
|
|
7
|
+
var clsx = require('clsx');
|
|
8
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
9
|
+
|
|
10
|
+
function _interopNamespace(e) {
|
|
11
|
+
if (e && e.__esModule) return e;
|
|
12
|
+
var n = Object.create(null);
|
|
13
|
+
if (e) {
|
|
14
|
+
Object.keys(e).forEach(function (k) {
|
|
15
|
+
if (k !== 'default') {
|
|
16
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
17
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
get: function () { return e[k]; }
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
n.default = e;
|
|
25
|
+
return Object.freeze(n);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var React2__namespace = /*#__PURE__*/_interopNamespace(React2);
|
|
29
|
+
|
|
30
|
+
function cn(...inputs) {
|
|
31
|
+
return clsx.clsx(inputs);
|
|
32
|
+
}
|
|
33
|
+
function Label({ className, ...props }) {
|
|
34
|
+
return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Label.Root, { "data-slot": "label", className: cn("mz-label", className), ...props });
|
|
35
|
+
}
|
|
36
|
+
var Form = reactHookForm.FormProvider;
|
|
37
|
+
var FormFieldContext = React2__namespace.createContext(null);
|
|
38
|
+
var FormItemContext = React2__namespace.createContext(null);
|
|
39
|
+
function FormField({ ...props }) {
|
|
40
|
+
return /* @__PURE__ */ jsxRuntime.jsx(FormFieldContext.Provider, { value: { name: props.name }, children: /* @__PURE__ */ jsxRuntime.jsx(reactHookForm.Controller, { ...props }) });
|
|
41
|
+
}
|
|
42
|
+
function useFormField() {
|
|
43
|
+
const fieldContext = React2__namespace.useContext(FormFieldContext);
|
|
44
|
+
const itemContext = React2__namespace.useContext(FormItemContext);
|
|
45
|
+
const { getFieldState } = reactHookForm.useFormContext();
|
|
46
|
+
if (!fieldContext) throw new Error("useFormField must be used inside a <FormField>");
|
|
47
|
+
if (!itemContext) throw new Error("useFormField must be used inside a <FormItem>");
|
|
48
|
+
const formState = reactHookForm.useFormState({ name: fieldContext.name });
|
|
49
|
+
const fieldState = getFieldState(fieldContext.name, formState);
|
|
50
|
+
const { id } = itemContext;
|
|
51
|
+
return {
|
|
52
|
+
id,
|
|
53
|
+
name: fieldContext.name,
|
|
54
|
+
formItemId: `${id}-form-item`,
|
|
55
|
+
formDescriptionId: `${id}-form-item-description`,
|
|
56
|
+
formMessageId: `${id}-form-item-message`,
|
|
57
|
+
...fieldState
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function FormItem({ className, ...props }) {
|
|
61
|
+
const id = React2__namespace.useId();
|
|
62
|
+
return /* @__PURE__ */ jsxRuntime.jsx(FormItemContext.Provider, { value: { id }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "form-item", className: cn("mz-field", className), ...props }) });
|
|
63
|
+
}
|
|
64
|
+
function FormLabel({ className, ...props }) {
|
|
65
|
+
const { error, formItemId } = useFormField();
|
|
66
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
67
|
+
Label,
|
|
68
|
+
{
|
|
69
|
+
"data-slot": "form-label",
|
|
70
|
+
"data-error": !!error,
|
|
71
|
+
className: cn(error && "mz-label--error", className),
|
|
72
|
+
htmlFor: formItemId,
|
|
73
|
+
...props
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
function FormControl({ ...props }) {
|
|
78
|
+
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
|
|
79
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
80
|
+
radixUi.Slot.Root,
|
|
81
|
+
{
|
|
82
|
+
"data-slot": "form-control",
|
|
83
|
+
id: formItemId,
|
|
84
|
+
"aria-describedby": error ? `${formDescriptionId} ${formMessageId}` : formDescriptionId,
|
|
85
|
+
"aria-invalid": !!error,
|
|
86
|
+
...props
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
function FormDescription({ className, ...props }) {
|
|
91
|
+
const { formDescriptionId } = useFormField();
|
|
92
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
93
|
+
"p",
|
|
94
|
+
{
|
|
95
|
+
"data-slot": "form-description",
|
|
96
|
+
id: formDescriptionId,
|
|
97
|
+
className: cn("mz-field__hint", className),
|
|
98
|
+
...props
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
function FormMessage({ className, children, ...props }) {
|
|
103
|
+
const { error, formMessageId } = useFormField();
|
|
104
|
+
const body = error ? String(error.message ?? "") : children;
|
|
105
|
+
if (!body) return null;
|
|
106
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
107
|
+
"p",
|
|
108
|
+
{
|
|
109
|
+
"data-slot": "form-message",
|
|
110
|
+
id: formMessageId,
|
|
111
|
+
role: "alert",
|
|
112
|
+
className: cn("mz-field__error", className),
|
|
113
|
+
...props,
|
|
114
|
+
children: body
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
exports.Form = Form;
|
|
120
|
+
exports.FormControl = FormControl;
|
|
121
|
+
exports.FormDescription = FormDescription;
|
|
122
|
+
exports.FormField = FormField;
|
|
123
|
+
exports.FormItem = FormItem;
|
|
124
|
+
exports.FormLabel = FormLabel;
|
|
125
|
+
exports.FormMessage = FormMessage;
|
|
126
|
+
exports.useFormField = useFormField;
|
|
127
|
+
//# sourceMappingURL=index.cjs.map
|
|
128
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/utils.ts","../../src/components/label.tsx","../../src/form/index.tsx"],"names":["clsx","jsx","LabelPrimitive","FormProvider","React2","Controller","useFormContext","useFormState","Slot"],"mappings":"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","file":"index.cjs","sourcesContent":["import { clsx, type ClassValue } from 'clsx'\n\n/**\n * Class name joiner. Morze UI ships plain, prefixed CSS rather than utility\n * classes, so there is nothing to merge — clsx is enough.\n */\nexport function cn(...inputs: ClassValue[]) {\n return clsx(inputs)\n}\n\nexport type Tone = 'primary' | 'accent' | 'danger' | 'warning' | 'info' | 'success'\n","'use client'\n\nimport * as React from 'react'\nimport { Label as LabelPrimitive } from 'radix-ui'\n\nimport { cn } from '../lib/utils'\n\nfunction Label({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {\n return <LabelPrimitive.Root data-slot=\"label\" className={cn('mz-label', className)} {...props} />\n}\n\nexport { Label }\n","'use client'\n\nimport * as React from 'react'\nimport {\n Controller,\n FormProvider,\n useFormContext,\n useFormState,\n type ControllerProps,\n type FieldPath,\n type FieldValues,\n} from 'react-hook-form'\nimport { Slot } from 'radix-ui'\n\nimport { cn } from '../lib/utils'\nimport { Label } from '../components/label'\n\n/**\n * React Hook Form bindings.\n *\n * import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage }\n * from '@morze/ui/form'\n *\n * They live behind their own entry point because `react-hook-form` is an\n * optional peer dependency: an application that does not use it should not\n * carry it, and the main bundle stays free of every form library.\n *\n * What this adds over `Field` is the wiring nobody enjoys writing by hand —\n * one generated id per field, `htmlFor` on the label, `aria-describedby`\n * pointing at the description and the message, and `aria-invalid` flipped by\n * the field's own validation state.\n */\nconst Form = FormProvider\n\ntype FormFieldContextValue<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> = { name: TName }\n\nconst FormFieldContext = React.createContext<FormFieldContextValue | null>(null)\nconst FormItemContext = React.createContext<{ id: string } | null>(null)\n\nfunction FormField<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({ ...props }: ControllerProps<TFieldValues, TName>) {\n return (\n <FormFieldContext.Provider value={{ name: props.name }}>\n <Controller {...props} />\n </FormFieldContext.Provider>\n )\n}\n\n/**\n * The ids and validation state of the field this is called inside.\n *\n * The state is read through `useFormState` scoped to this one name, so a\n * keystroke in one field does not re-render every other field's label.\n */\nfunction useFormField() {\n const fieldContext = React.useContext(FormFieldContext)\n const itemContext = React.useContext(FormItemContext)\n const { getFieldState } = useFormContext()\n\n if (!fieldContext) throw new Error('useFormField must be used inside a <FormField>')\n if (!itemContext) throw new Error('useFormField must be used inside a <FormItem>')\n\n const formState = useFormState({ name: fieldContext.name })\n const fieldState = getFieldState(fieldContext.name, formState)\n const { id } = itemContext\n\n return {\n id,\n name: fieldContext.name,\n formItemId: `${id}-form-item`,\n formDescriptionId: `${id}-form-item-description`,\n formMessageId: `${id}-form-item-message`,\n ...fieldState,\n }\n}\n\nfunction FormItem({ className, ...props }: React.ComponentProps<'div'>) {\n const id = React.useId()\n\n return (\n <FormItemContext.Provider value={{ id }}>\n <div data-slot=\"form-item\" className={cn('mz-field', className)} {...props} />\n </FormItemContext.Provider>\n )\n}\n\nfunction FormLabel({ className, ...props }: React.ComponentProps<typeof Label>) {\n const { error, formItemId } = useFormField()\n\n return (\n <Label\n data-slot=\"form-label\"\n data-error={!!error}\n className={cn(error && 'mz-label--error', className)}\n htmlFor={formItemId}\n {...props}\n />\n )\n}\n\n/**\n * Wraps the control itself — an `Input`, a `SelectTrigger`, anything. It\n * renders nothing of its own; it only stamps the id and the aria wiring onto\n * whatever child it is given.\n */\nfunction FormControl({ ...props }: React.ComponentProps<typeof Slot.Root>) {\n const { error, formItemId, formDescriptionId, formMessageId } = useFormField()\n\n return (\n <Slot.Root\n data-slot=\"form-control\"\n id={formItemId}\n aria-describedby={error ? `${formDescriptionId} ${formMessageId}` : formDescriptionId}\n aria-invalid={!!error}\n {...props}\n />\n )\n}\n\nfunction FormDescription({ className, ...props }: React.ComponentProps<'p'>) {\n const { formDescriptionId } = useFormField()\n\n return (\n <p\n data-slot=\"form-description\"\n id={formDescriptionId}\n className={cn('mz-field__hint', className)}\n {...props}\n />\n )\n}\n\n/**\n * The validation message. It renders the field's error when there is one and\n * `children` otherwise, and disappears entirely when there is neither — so a\n * form does not reserve a blank line under every input.\n */\nfunction FormMessage({ className, children, ...props }: React.ComponentProps<'p'>) {\n const { error, formMessageId } = useFormField()\n const body = error ? String(error.message ?? '') : children\n\n if (!body) return null\n\n return (\n <p\n data-slot=\"form-message\"\n id={formMessageId}\n role=\"alert\"\n className={cn('mz-field__error', className)}\n {...props}\n >\n {body}\n </p>\n )\n}\n\nexport {\n Form,\n FormField,\n FormItem,\n FormLabel,\n FormControl,\n FormDescription,\n FormMessage,\n useFormField,\n}\n"]}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as react_hook_form from 'react-hook-form';
|
|
2
|
+
import { FieldValues, FieldPath, ControllerProps } from 'react-hook-form';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { Slot } from 'radix-ui';
|
|
5
|
+
import { L as Label } from '../label-Bf37KoRc.cjs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* React Hook Form bindings.
|
|
9
|
+
*
|
|
10
|
+
* import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage }
|
|
11
|
+
* from '@morze/ui/form'
|
|
12
|
+
*
|
|
13
|
+
* They live behind their own entry point because `react-hook-form` is an
|
|
14
|
+
* optional peer dependency: an application that does not use it should not
|
|
15
|
+
* carry it, and the main bundle stays free of every form library.
|
|
16
|
+
*
|
|
17
|
+
* What this adds over `Field` is the wiring nobody enjoys writing by hand —
|
|
18
|
+
* one generated id per field, `htmlFor` on the label, `aria-describedby`
|
|
19
|
+
* pointing at the description and the message, and `aria-invalid` flipped by
|
|
20
|
+
* the field's own validation state.
|
|
21
|
+
*/
|
|
22
|
+
declare const Form: <TFieldValues extends FieldValues, TContext = any, TTransformedValues = TFieldValues>({ children, watch, getValues, getErrors, getFieldState, setError, clearErrors, setValue, setValues, trigger, formState, resetField, reset, resetDefaultValues, handleSubmit, unregister, control, register, setFocus, subscribe, }: react_hook_form.FormProviderProps<TFieldValues, TContext, TTransformedValues>) => React.JSX.Element;
|
|
23
|
+
declare function FormField<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({ ...props }: ControllerProps<TFieldValues, TName>): React.JSX.Element;
|
|
24
|
+
/**
|
|
25
|
+
* The ids and validation state of the field this is called inside.
|
|
26
|
+
*
|
|
27
|
+
* The state is read through `useFormState` scoped to this one name, so a
|
|
28
|
+
* keystroke in one field does not re-render every other field's label.
|
|
29
|
+
*/
|
|
30
|
+
declare function useFormField(): {
|
|
31
|
+
invalid: boolean;
|
|
32
|
+
isDirty: boolean;
|
|
33
|
+
isTouched: boolean;
|
|
34
|
+
isValidating: boolean;
|
|
35
|
+
error?: react_hook_form.FieldError | undefined;
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
formItemId: string;
|
|
39
|
+
formDescriptionId: string;
|
|
40
|
+
formMessageId: string;
|
|
41
|
+
};
|
|
42
|
+
declare function FormItem({ className, ...props }: React.ComponentProps<'div'>): React.JSX.Element;
|
|
43
|
+
declare function FormLabel({ className, ...props }: React.ComponentProps<typeof Label>): React.JSX.Element;
|
|
44
|
+
/**
|
|
45
|
+
* Wraps the control itself — an `Input`, a `SelectTrigger`, anything. It
|
|
46
|
+
* renders nothing of its own; it only stamps the id and the aria wiring onto
|
|
47
|
+
* whatever child it is given.
|
|
48
|
+
*/
|
|
49
|
+
declare function FormControl({ ...props }: React.ComponentProps<typeof Slot.Root>): React.JSX.Element;
|
|
50
|
+
declare function FormDescription({ className, ...props }: React.ComponentProps<'p'>): React.JSX.Element;
|
|
51
|
+
/**
|
|
52
|
+
* The validation message. It renders the field's error when there is one and
|
|
53
|
+
* `children` otherwise, and disappears entirely when there is neither — so a
|
|
54
|
+
* form does not reserve a blank line under every input.
|
|
55
|
+
*/
|
|
56
|
+
declare function FormMessage({ className, children, ...props }: React.ComponentProps<'p'>): React.JSX.Element | null;
|
|
57
|
+
|
|
58
|
+
export { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, useFormField };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as react_hook_form from 'react-hook-form';
|
|
2
|
+
import { FieldValues, FieldPath, ControllerProps } from 'react-hook-form';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { Slot } from 'radix-ui';
|
|
5
|
+
import { L as Label } from '../label-Bf37KoRc.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* React Hook Form bindings.
|
|
9
|
+
*
|
|
10
|
+
* import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage }
|
|
11
|
+
* from '@morze/ui/form'
|
|
12
|
+
*
|
|
13
|
+
* They live behind their own entry point because `react-hook-form` is an
|
|
14
|
+
* optional peer dependency: an application that does not use it should not
|
|
15
|
+
* carry it, and the main bundle stays free of every form library.
|
|
16
|
+
*
|
|
17
|
+
* What this adds over `Field` is the wiring nobody enjoys writing by hand —
|
|
18
|
+
* one generated id per field, `htmlFor` on the label, `aria-describedby`
|
|
19
|
+
* pointing at the description and the message, and `aria-invalid` flipped by
|
|
20
|
+
* the field's own validation state.
|
|
21
|
+
*/
|
|
22
|
+
declare const Form: <TFieldValues extends FieldValues, TContext = any, TTransformedValues = TFieldValues>({ children, watch, getValues, getErrors, getFieldState, setError, clearErrors, setValue, setValues, trigger, formState, resetField, reset, resetDefaultValues, handleSubmit, unregister, control, register, setFocus, subscribe, }: react_hook_form.FormProviderProps<TFieldValues, TContext, TTransformedValues>) => React.JSX.Element;
|
|
23
|
+
declare function FormField<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({ ...props }: ControllerProps<TFieldValues, TName>): React.JSX.Element;
|
|
24
|
+
/**
|
|
25
|
+
* The ids and validation state of the field this is called inside.
|
|
26
|
+
*
|
|
27
|
+
* The state is read through `useFormState` scoped to this one name, so a
|
|
28
|
+
* keystroke in one field does not re-render every other field's label.
|
|
29
|
+
*/
|
|
30
|
+
declare function useFormField(): {
|
|
31
|
+
invalid: boolean;
|
|
32
|
+
isDirty: boolean;
|
|
33
|
+
isTouched: boolean;
|
|
34
|
+
isValidating: boolean;
|
|
35
|
+
error?: react_hook_form.FieldError | undefined;
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
formItemId: string;
|
|
39
|
+
formDescriptionId: string;
|
|
40
|
+
formMessageId: string;
|
|
41
|
+
};
|
|
42
|
+
declare function FormItem({ className, ...props }: React.ComponentProps<'div'>): React.JSX.Element;
|
|
43
|
+
declare function FormLabel({ className, ...props }: React.ComponentProps<typeof Label>): React.JSX.Element;
|
|
44
|
+
/**
|
|
45
|
+
* Wraps the control itself — an `Input`, a `SelectTrigger`, anything. It
|
|
46
|
+
* renders nothing of its own; it only stamps the id and the aria wiring onto
|
|
47
|
+
* whatever child it is given.
|
|
48
|
+
*/
|
|
49
|
+
declare function FormControl({ ...props }: React.ComponentProps<typeof Slot.Root>): React.JSX.Element;
|
|
50
|
+
declare function FormDescription({ className, ...props }: React.ComponentProps<'p'>): React.JSX.Element;
|
|
51
|
+
/**
|
|
52
|
+
* The validation message. It renders the field's error when there is one and
|
|
53
|
+
* `children` otherwise, and disappears entirely when there is neither — so a
|
|
54
|
+
* form does not reserve a blank line under every input.
|
|
55
|
+
*/
|
|
56
|
+
declare function FormMessage({ className, children, ...props }: React.ComponentProps<'p'>): React.JSX.Element | null;
|
|
57
|
+
|
|
58
|
+
export { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, useFormField };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React2 from 'react';
|
|
3
|
+
import { FormProvider, Controller, useFormContext, useFormState } from 'react-hook-form';
|
|
4
|
+
import { Slot, Label as Label$1 } from 'radix-ui';
|
|
5
|
+
import { clsx } from 'clsx';
|
|
6
|
+
import { jsx } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
function cn(...inputs) {
|
|
9
|
+
return clsx(inputs);
|
|
10
|
+
}
|
|
11
|
+
function Label({ className, ...props }) {
|
|
12
|
+
return /* @__PURE__ */ jsx(Label$1.Root, { "data-slot": "label", className: cn("mz-label", className), ...props });
|
|
13
|
+
}
|
|
14
|
+
var Form = FormProvider;
|
|
15
|
+
var FormFieldContext = React2.createContext(null);
|
|
16
|
+
var FormItemContext = React2.createContext(null);
|
|
17
|
+
function FormField({ ...props }) {
|
|
18
|
+
return /* @__PURE__ */ jsx(FormFieldContext.Provider, { value: { name: props.name }, children: /* @__PURE__ */ jsx(Controller, { ...props }) });
|
|
19
|
+
}
|
|
20
|
+
function useFormField() {
|
|
21
|
+
const fieldContext = React2.useContext(FormFieldContext);
|
|
22
|
+
const itemContext = React2.useContext(FormItemContext);
|
|
23
|
+
const { getFieldState } = useFormContext();
|
|
24
|
+
if (!fieldContext) throw new Error("useFormField must be used inside a <FormField>");
|
|
25
|
+
if (!itemContext) throw new Error("useFormField must be used inside a <FormItem>");
|
|
26
|
+
const formState = useFormState({ name: fieldContext.name });
|
|
27
|
+
const fieldState = getFieldState(fieldContext.name, formState);
|
|
28
|
+
const { id } = itemContext;
|
|
29
|
+
return {
|
|
30
|
+
id,
|
|
31
|
+
name: fieldContext.name,
|
|
32
|
+
formItemId: `${id}-form-item`,
|
|
33
|
+
formDescriptionId: `${id}-form-item-description`,
|
|
34
|
+
formMessageId: `${id}-form-item-message`,
|
|
35
|
+
...fieldState
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function FormItem({ className, ...props }) {
|
|
39
|
+
const id = React2.useId();
|
|
40
|
+
return /* @__PURE__ */ jsx(FormItemContext.Provider, { value: { id }, children: /* @__PURE__ */ jsx("div", { "data-slot": "form-item", className: cn("mz-field", className), ...props }) });
|
|
41
|
+
}
|
|
42
|
+
function FormLabel({ className, ...props }) {
|
|
43
|
+
const { error, formItemId } = useFormField();
|
|
44
|
+
return /* @__PURE__ */ jsx(
|
|
45
|
+
Label,
|
|
46
|
+
{
|
|
47
|
+
"data-slot": "form-label",
|
|
48
|
+
"data-error": !!error,
|
|
49
|
+
className: cn(error && "mz-label--error", className),
|
|
50
|
+
htmlFor: formItemId,
|
|
51
|
+
...props
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
function FormControl({ ...props }) {
|
|
56
|
+
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
|
|
57
|
+
return /* @__PURE__ */ jsx(
|
|
58
|
+
Slot.Root,
|
|
59
|
+
{
|
|
60
|
+
"data-slot": "form-control",
|
|
61
|
+
id: formItemId,
|
|
62
|
+
"aria-describedby": error ? `${formDescriptionId} ${formMessageId}` : formDescriptionId,
|
|
63
|
+
"aria-invalid": !!error,
|
|
64
|
+
...props
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
function FormDescription({ className, ...props }) {
|
|
69
|
+
const { formDescriptionId } = useFormField();
|
|
70
|
+
return /* @__PURE__ */ jsx(
|
|
71
|
+
"p",
|
|
72
|
+
{
|
|
73
|
+
"data-slot": "form-description",
|
|
74
|
+
id: formDescriptionId,
|
|
75
|
+
className: cn("mz-field__hint", className),
|
|
76
|
+
...props
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
function FormMessage({ className, children, ...props }) {
|
|
81
|
+
const { error, formMessageId } = useFormField();
|
|
82
|
+
const body = error ? String(error.message ?? "") : children;
|
|
83
|
+
if (!body) return null;
|
|
84
|
+
return /* @__PURE__ */ jsx(
|
|
85
|
+
"p",
|
|
86
|
+
{
|
|
87
|
+
"data-slot": "form-message",
|
|
88
|
+
id: formMessageId,
|
|
89
|
+
role: "alert",
|
|
90
|
+
className: cn("mz-field__error", className),
|
|
91
|
+
...props,
|
|
92
|
+
children: body
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, useFormField };
|
|
98
|
+
//# sourceMappingURL=index.js.map
|
|
99
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/utils.ts","../../src/components/label.tsx","../../src/form/index.tsx"],"names":["LabelPrimitive","jsx"],"mappings":"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","file":"index.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx'\n\n/**\n * Class name joiner. Morze UI ships plain, prefixed CSS rather than utility\n * classes, so there is nothing to merge — clsx is enough.\n */\nexport function cn(...inputs: ClassValue[]) {\n return clsx(inputs)\n}\n\nexport type Tone = 'primary' | 'accent' | 'danger' | 'warning' | 'info' | 'success'\n","'use client'\n\nimport * as React from 'react'\nimport { Label as LabelPrimitive } from 'radix-ui'\n\nimport { cn } from '../lib/utils'\n\nfunction Label({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {\n return <LabelPrimitive.Root data-slot=\"label\" className={cn('mz-label', className)} {...props} />\n}\n\nexport { Label }\n","'use client'\n\nimport * as React from 'react'\nimport {\n Controller,\n FormProvider,\n useFormContext,\n useFormState,\n type ControllerProps,\n type FieldPath,\n type FieldValues,\n} from 'react-hook-form'\nimport { Slot } from 'radix-ui'\n\nimport { cn } from '../lib/utils'\nimport { Label } from '../components/label'\n\n/**\n * React Hook Form bindings.\n *\n * import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage }\n * from '@morze/ui/form'\n *\n * They live behind their own entry point because `react-hook-form` is an\n * optional peer dependency: an application that does not use it should not\n * carry it, and the main bundle stays free of every form library.\n *\n * What this adds over `Field` is the wiring nobody enjoys writing by hand —\n * one generated id per field, `htmlFor` on the label, `aria-describedby`\n * pointing at the description and the message, and `aria-invalid` flipped by\n * the field's own validation state.\n */\nconst Form = FormProvider\n\ntype FormFieldContextValue<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> = { name: TName }\n\nconst FormFieldContext = React.createContext<FormFieldContextValue | null>(null)\nconst FormItemContext = React.createContext<{ id: string } | null>(null)\n\nfunction FormField<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({ ...props }: ControllerProps<TFieldValues, TName>) {\n return (\n <FormFieldContext.Provider value={{ name: props.name }}>\n <Controller {...props} />\n </FormFieldContext.Provider>\n )\n}\n\n/**\n * The ids and validation state of the field this is called inside.\n *\n * The state is read through `useFormState` scoped to this one name, so a\n * keystroke in one field does not re-render every other field's label.\n */\nfunction useFormField() {\n const fieldContext = React.useContext(FormFieldContext)\n const itemContext = React.useContext(FormItemContext)\n const { getFieldState } = useFormContext()\n\n if (!fieldContext) throw new Error('useFormField must be used inside a <FormField>')\n if (!itemContext) throw new Error('useFormField must be used inside a <FormItem>')\n\n const formState = useFormState({ name: fieldContext.name })\n const fieldState = getFieldState(fieldContext.name, formState)\n const { id } = itemContext\n\n return {\n id,\n name: fieldContext.name,\n formItemId: `${id}-form-item`,\n formDescriptionId: `${id}-form-item-description`,\n formMessageId: `${id}-form-item-message`,\n ...fieldState,\n }\n}\n\nfunction FormItem({ className, ...props }: React.ComponentProps<'div'>) {\n const id = React.useId()\n\n return (\n <FormItemContext.Provider value={{ id }}>\n <div data-slot=\"form-item\" className={cn('mz-field', className)} {...props} />\n </FormItemContext.Provider>\n )\n}\n\nfunction FormLabel({ className, ...props }: React.ComponentProps<typeof Label>) {\n const { error, formItemId } = useFormField()\n\n return (\n <Label\n data-slot=\"form-label\"\n data-error={!!error}\n className={cn(error && 'mz-label--error', className)}\n htmlFor={formItemId}\n {...props}\n />\n )\n}\n\n/**\n * Wraps the control itself — an `Input`, a `SelectTrigger`, anything. It\n * renders nothing of its own; it only stamps the id and the aria wiring onto\n * whatever child it is given.\n */\nfunction FormControl({ ...props }: React.ComponentProps<typeof Slot.Root>) {\n const { error, formItemId, formDescriptionId, formMessageId } = useFormField()\n\n return (\n <Slot.Root\n data-slot=\"form-control\"\n id={formItemId}\n aria-describedby={error ? `${formDescriptionId} ${formMessageId}` : formDescriptionId}\n aria-invalid={!!error}\n {...props}\n />\n )\n}\n\nfunction FormDescription({ className, ...props }: React.ComponentProps<'p'>) {\n const { formDescriptionId } = useFormField()\n\n return (\n <p\n data-slot=\"form-description\"\n id={formDescriptionId}\n className={cn('mz-field__hint', className)}\n {...props}\n />\n )\n}\n\n/**\n * The validation message. It renders the field's error when there is one and\n * `children` otherwise, and disappears entirely when there is neither — so a\n * form does not reserve a blank line under every input.\n */\nfunction FormMessage({ className, children, ...props }: React.ComponentProps<'p'>) {\n const { error, formMessageId } = useFormField()\n const body = error ? String(error.message ?? '') : children\n\n if (!body) return null\n\n return (\n <p\n data-slot=\"form-message\"\n id={formMessageId}\n role=\"alert\"\n className={cn('mz-field__error', className)}\n {...props}\n >\n {body}\n </p>\n )\n}\n\nexport {\n Form,\n FormField,\n FormItem,\n FormLabel,\n FormControl,\n FormDescription,\n FormMessage,\n useFormField,\n}\n"]}
|