@nexub/react 6.0.0 → 6.1.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/accessibility.json +34 -1
- package/dist/components/Fieldset/Fieldset.d.ts +11 -0
- package/dist/components/Fieldset/Fieldset.js +23 -0
- package/dist/components/Fieldset/index.d.ts +2 -0
- package/dist/components/Fieldset/index.js +4 -0
- package/dist/components/Form/Form.d.ts +33 -0
- package/dist/components/Form/Form.js +79 -0
- package/dist/components/Form/index.d.ts +2 -0
- package/dist/components/Form/index.js +5 -0
- package/dist/components/FormField/FormField.d.ts +2 -0
- package/dist/components/FormField/FormField.js +10 -5
- package/dist/components/InlineEdit/InlineEdit.d.ts +21 -0
- package/dist/components/InlineEdit/InlineEdit.js +103 -0
- package/dist/components/InlineEdit/index.d.ts +2 -0
- package/dist/components/InlineEdit/index.js +4 -0
- package/dist/hooks/useUnsavedChanges.d.ts +6 -0
- package/dist/hooks/useUnsavedChanges.js +15 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +10 -1
- package/dist/styles.css +1 -1
- package/package.json +6 -6
package/accessibility.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "6.
|
|
2
|
+
"version": "6.1.0",
|
|
3
3
|
"standard": "WCAG 2.2 Level AA",
|
|
4
4
|
"method": {
|
|
5
5
|
"automated": "axe-core rules tagged wcag2a, wcag2aa, wcag21a, wcag21aa and wcag22aa, run on every Storybook story in light and dark, and on open popups (menus, listboxes, dialogs), on every pull request; a new violation fails the build.",
|
|
@@ -410,6 +410,15 @@
|
|
|
410
410
|
"keyboard": [],
|
|
411
411
|
"screenReader": []
|
|
412
412
|
},
|
|
413
|
+
{
|
|
414
|
+
"slug": "fieldset",
|
|
415
|
+
"name": "Fieldset",
|
|
416
|
+
"package": "@nexub/react",
|
|
417
|
+
"automated": "pass",
|
|
418
|
+
"exceptions": [],
|
|
419
|
+
"keyboard": [],
|
|
420
|
+
"screenReader": []
|
|
421
|
+
},
|
|
413
422
|
{
|
|
414
423
|
"slug": "filter-builder",
|
|
415
424
|
"name": "Filter Builder",
|
|
@@ -459,6 +468,17 @@
|
|
|
459
468
|
"keyboard": [],
|
|
460
469
|
"screenReader": []
|
|
461
470
|
},
|
|
471
|
+
{
|
|
472
|
+
"slug": "form",
|
|
473
|
+
"name": "Form",
|
|
474
|
+
"package": "@nexub/react",
|
|
475
|
+
"automated": "pass",
|
|
476
|
+
"exceptions": [],
|
|
477
|
+
"keyboard": [
|
|
478
|
+
"Enter: Submits from any text field; the first invalid field takes focus."
|
|
479
|
+
],
|
|
480
|
+
"screenReader": []
|
|
481
|
+
},
|
|
462
482
|
{
|
|
463
483
|
"slug": "funnel",
|
|
464
484
|
"name": "Funnel",
|
|
@@ -546,6 +566,19 @@
|
|
|
546
566
|
"keyboard": [],
|
|
547
567
|
"screenReader": []
|
|
548
568
|
},
|
|
569
|
+
{
|
|
570
|
+
"slug": "inline-edit",
|
|
571
|
+
"name": "Inline Edit",
|
|
572
|
+
"package": "@nexub/react",
|
|
573
|
+
"automated": "pass",
|
|
574
|
+
"exceptions": [],
|
|
575
|
+
"keyboard": [
|
|
576
|
+
"Enter / Space: On the text: starts editing.",
|
|
577
|
+
"Enter: In the input: saves.",
|
|
578
|
+
"Escape: Reverts and returns focus to the text."
|
|
579
|
+
],
|
|
580
|
+
"screenReader": []
|
|
581
|
+
},
|
|
549
582
|
{
|
|
550
583
|
"slug": "input",
|
|
551
584
|
"name": "Input",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface FieldsetProps extends React.FieldsetHTMLAttributes<HTMLFieldSetElement> {
|
|
3
|
+
/** The group's name, read before each field inside it. */
|
|
4
|
+
legend: React.ReactNode;
|
|
5
|
+
description?: React.ReactNode;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Groups related fields under a heading (`<fieldset>` + `<legend>`). `disabled`
|
|
9
|
+
* disables every control inside it, natively.
|
|
10
|
+
*/
|
|
11
|
+
export declare const Fieldset: React.ForwardRefExoticComponent<FieldsetProps & React.RefAttributes<HTMLFieldSetElement>>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "../../utils/cn.js";
|
|
4
|
+
const Fieldset = React.forwardRef(function Fieldset2({ legend, description, className, children, ...props }, ref) {
|
|
5
|
+
const descriptionId = React.useId();
|
|
6
|
+
return /* @__PURE__ */ jsxs(
|
|
7
|
+
"fieldset",
|
|
8
|
+
{
|
|
9
|
+
ref,
|
|
10
|
+
"aria-describedby": description ? descriptionId : void 0,
|
|
11
|
+
className: cn("m-0 flex min-w-0 flex-col gap-4 border-0 p-0", className),
|
|
12
|
+
...props,
|
|
13
|
+
children: [
|
|
14
|
+
/* @__PURE__ */ jsx("legend", { className: "mb-1 p-0 text-title-sm font-semibold text-[var(--color-on-surface)]", children: legend }),
|
|
15
|
+
description && /* @__PURE__ */ jsx("p", { id: descriptionId, className: "text-body-sm text-[var(--color-on-surface-muted)]", children: description }),
|
|
16
|
+
children
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
export {
|
|
22
|
+
Fieldset
|
|
23
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface FormErrorItem {
|
|
3
|
+
/** The field's `name`. */
|
|
4
|
+
name: string;
|
|
5
|
+
/** The field's DOM id, or its name when it has none (ErrorSummary link target). */
|
|
6
|
+
id: string;
|
|
7
|
+
message: string;
|
|
8
|
+
}
|
|
9
|
+
export type FormErrors = Record<string, string>;
|
|
10
|
+
export interface FormContextValue {
|
|
11
|
+
errors: FormErrors;
|
|
12
|
+
submitting: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** Errors and submitting state of the closest `Form`, or null outside one. */
|
|
15
|
+
export declare function useForm(): FormContextValue | null;
|
|
16
|
+
export interface FormProps extends Omit<React.FormHTMLAttributes<HTMLFormElement>, 'onSubmit' | 'onInvalid'> {
|
|
17
|
+
/**
|
|
18
|
+
* Extra checks after the browser's own (`required`, `type`, `pattern`, `min`…).
|
|
19
|
+
* Return `{ fieldName: message }`; empty values mean valid. Plug zod or any
|
|
20
|
+
* schema library in here.
|
|
21
|
+
*/
|
|
22
|
+
validate?: (data: FormData) => Record<string, string | null | undefined | false> | void;
|
|
23
|
+
/** Called with the form data only when every field is valid. A promise sets `submitting`. */
|
|
24
|
+
onSubmit?: (data: FormData, event: React.FormEvent<HTMLFormElement>) => void | Promise<unknown>;
|
|
25
|
+
/** Called after a failed submit, in field order; pass the list to ErrorSummary. */
|
|
26
|
+
onInvalidSubmit?: (errors: FormErrorItem[]) => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A `<form>` that validates on submit with the browser's constraint validation
|
|
30
|
+
* plus your `validate`, shows the messages through FormField, and focuses the
|
|
31
|
+
* first invalid field. A field's error clears as soon as it is edited.
|
|
32
|
+
*/
|
|
33
|
+
export declare const Form: React.ForwardRefExoticComponent<FormProps & React.RefAttributes<HTMLFormElement>>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../../utils/cn.js";
|
|
5
|
+
const FormContext = React.createContext(null);
|
|
6
|
+
function useForm() {
|
|
7
|
+
return React.useContext(FormContext);
|
|
8
|
+
}
|
|
9
|
+
const isControl = (el) => "validity" in el && "name" in el && Boolean(el.name);
|
|
10
|
+
const Form = React.forwardRef(function Form2({ validate, onSubmit, onInvalidSubmit, onInput, className, children, ...props }, ref) {
|
|
11
|
+
const [errors, setErrors] = React.useState({});
|
|
12
|
+
const [submitting, setSubmitting] = React.useState(false);
|
|
13
|
+
const handleSubmit = async (event) => {
|
|
14
|
+
event.preventDefault();
|
|
15
|
+
if (submitting) return;
|
|
16
|
+
const form = event.currentTarget;
|
|
17
|
+
const data = new FormData(form);
|
|
18
|
+
const controls = Array.from(form.elements).filter(isControl).filter((el) => !el.disabled);
|
|
19
|
+
const next = {};
|
|
20
|
+
for (const el of controls) {
|
|
21
|
+
if (!el.validity.valid && !next[el.name]) next[el.name] = el.validationMessage;
|
|
22
|
+
}
|
|
23
|
+
for (const [name, message] of Object.entries(validate?.(data) ?? {})) {
|
|
24
|
+
if (message && !next[name]) next[name] = message;
|
|
25
|
+
}
|
|
26
|
+
setErrors(next);
|
|
27
|
+
if (Object.keys(next).length) {
|
|
28
|
+
const seen = /* @__PURE__ */ new Set();
|
|
29
|
+
const items = [];
|
|
30
|
+
for (const el of controls) {
|
|
31
|
+
if (next[el.name] && !seen.has(el.name)) {
|
|
32
|
+
seen.add(el.name);
|
|
33
|
+
items.push({ name: el.name, id: el.id || el.name, message: next[el.name] });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
for (const name of Object.keys(next)) {
|
|
37
|
+
if (!seen.has(name)) items.push({ name, id: name, message: next[name] });
|
|
38
|
+
}
|
|
39
|
+
onInvalidSubmit?.(items);
|
|
40
|
+
const first = controls.find((el) => next[el.name] && el.type !== "hidden");
|
|
41
|
+
first?.focus();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const result = onSubmit?.(data, event);
|
|
45
|
+
if (result instanceof Promise) {
|
|
46
|
+
setSubmitting(true);
|
|
47
|
+
try {
|
|
48
|
+
await result;
|
|
49
|
+
} finally {
|
|
50
|
+
setSubmitting(false);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const handleInput = (event) => {
|
|
55
|
+
onInput?.(event);
|
|
56
|
+
const name = event.target.name;
|
|
57
|
+
if (name && errors[name]) {
|
|
58
|
+
setErrors(({ [name]: _cleared, ...rest }) => rest);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const value = React.useMemo(() => ({ errors, submitting }), [errors, submitting]);
|
|
62
|
+
return /* @__PURE__ */ jsx(FormContext.Provider, { value, children: /* @__PURE__ */ jsx(
|
|
63
|
+
"form",
|
|
64
|
+
{
|
|
65
|
+
ref,
|
|
66
|
+
noValidate: true,
|
|
67
|
+
"aria-busy": submitting || void 0,
|
|
68
|
+
className: cn("flex flex-col gap-5", className),
|
|
69
|
+
onSubmit: handleSubmit,
|
|
70
|
+
onInput: handleInput,
|
|
71
|
+
...props,
|
|
72
|
+
children
|
|
73
|
+
}
|
|
74
|
+
) });
|
|
75
|
+
});
|
|
76
|
+
export {
|
|
77
|
+
Form,
|
|
78
|
+
useForm
|
|
79
|
+
};
|
|
@@ -10,6 +10,8 @@ export interface FormFieldContextValue {
|
|
|
10
10
|
disabled?: boolean;
|
|
11
11
|
descriptionId: string;
|
|
12
12
|
errorId: string;
|
|
13
|
+
/** The message from the enclosing Form for this field's `name`. */
|
|
14
|
+
formError?: string;
|
|
13
15
|
}
|
|
14
16
|
export interface FormFieldRootProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
15
17
|
id?: string;
|
|
@@ -5,6 +5,7 @@ import { cn } from "../../utils/cn.js";
|
|
|
5
5
|
import { Label } from "../Label/Label.js";
|
|
6
6
|
import { HelperText } from "../HelperText/HelperText.js";
|
|
7
7
|
import { FormError } from "../FormError/FormError.js";
|
|
8
|
+
import { useForm } from "../Form/Form.js";
|
|
8
9
|
const FormFieldContext = React.createContext(null);
|
|
9
10
|
function useFormField() {
|
|
10
11
|
const ctx = React.useContext(FormFieldContext);
|
|
@@ -13,9 +14,12 @@ function useFormField() {
|
|
|
13
14
|
}
|
|
14
15
|
return ctx;
|
|
15
16
|
}
|
|
16
|
-
const Root = React.forwardRef(function Root2({ id: idProp, name, invalid, required, disabled, className, children, ...props }, ref) {
|
|
17
|
+
const Root = React.forwardRef(function Root2({ id: idProp, name, invalid: invalidProp, required, disabled, className, children, ...props }, ref) {
|
|
17
18
|
const generatedId = React.useId();
|
|
18
19
|
const id = idProp ?? generatedId;
|
|
20
|
+
const form = useForm();
|
|
21
|
+
const formError = name ? form?.errors[name] : void 0;
|
|
22
|
+
const invalid = invalidProp ?? (formError ? true : void 0);
|
|
19
23
|
const value = React.useMemo(
|
|
20
24
|
() => ({
|
|
21
25
|
id,
|
|
@@ -24,9 +28,10 @@ const Root = React.forwardRef(function Root2({ id: idProp, name, invalid, requir
|
|
|
24
28
|
required,
|
|
25
29
|
disabled,
|
|
26
30
|
descriptionId: `${id}-description`,
|
|
27
|
-
errorId: `${id}-error
|
|
31
|
+
errorId: `${id}-error`,
|
|
32
|
+
formError
|
|
28
33
|
}),
|
|
29
|
-
[id, name, invalid, required, disabled]
|
|
34
|
+
[id, name, invalid, required, disabled, formError]
|
|
30
35
|
);
|
|
31
36
|
return /* @__PURE__ */ jsx(FormFieldContext.Provider, { value, children: /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-col gap-1.5", className), ...props, children }) });
|
|
32
37
|
});
|
|
@@ -79,9 +84,9 @@ function FieldError({
|
|
|
79
84
|
children,
|
|
80
85
|
...props
|
|
81
86
|
}) {
|
|
82
|
-
const { errorId, invalid } = useFormField();
|
|
87
|
+
const { errorId, invalid, formError } = useFormField();
|
|
83
88
|
if (!invalid) return null;
|
|
84
|
-
return /* @__PURE__ */ jsx(FormError, { id: errorId, className, ...props, children });
|
|
89
|
+
return /* @__PURE__ */ jsx(FormError, { id: errorId, className, ...props, children: children ?? formError });
|
|
85
90
|
}
|
|
86
91
|
FieldError.displayName = "FormField.Error";
|
|
87
92
|
const FormField = { Root, Label: FieldLabel, Control, Description, Error: FieldError };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface InlineEditProps {
|
|
3
|
+
value: string;
|
|
4
|
+
/**
|
|
5
|
+
* Called with the new text on Enter or blur (only when it changed). Return a
|
|
6
|
+
* promise to keep the field open and busy until it settles; a rejection keeps
|
|
7
|
+
* it open and marks it invalid so the person can retry or press Escape.
|
|
8
|
+
*/
|
|
9
|
+
onSave: (value: string) => void | Promise<unknown>;
|
|
10
|
+
/** Names the field: the input's label and "Edit ‹label›" on the button. */
|
|
11
|
+
label: string;
|
|
12
|
+
/** Shown when `value` is empty. */
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Text that turns into an input on click. Enter or blur saves, Escape reverts,
|
|
19
|
+
* and focus returns to the text afterwards.
|
|
20
|
+
*/
|
|
21
|
+
export declare function InlineEdit({ value, onSave, label, placeholder, disabled, className, }: InlineEditProps): React.JSX.Element;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../../utils/cn.js";
|
|
5
|
+
import { Input } from "../Input/Input.js";
|
|
6
|
+
import { useNexubMessages } from "../NexubProvider/NexubProvider.js";
|
|
7
|
+
function InlineEdit({
|
|
8
|
+
value,
|
|
9
|
+
onSave,
|
|
10
|
+
label,
|
|
11
|
+
placeholder,
|
|
12
|
+
disabled,
|
|
13
|
+
className
|
|
14
|
+
}) {
|
|
15
|
+
const t = useNexubMessages();
|
|
16
|
+
const [editing, setEditing] = React.useState(false);
|
|
17
|
+
const [draft, setDraft] = React.useState(value);
|
|
18
|
+
const [saving, setSaving] = React.useState(false);
|
|
19
|
+
const [failed, setFailed] = React.useState(false);
|
|
20
|
+
const buttonRef = React.useRef(null);
|
|
21
|
+
const returnFocus = React.useRef(false);
|
|
22
|
+
React.useEffect(() => {
|
|
23
|
+
if (!editing && returnFocus.current) {
|
|
24
|
+
returnFocus.current = false;
|
|
25
|
+
buttonRef.current?.focus();
|
|
26
|
+
}
|
|
27
|
+
}, [editing]);
|
|
28
|
+
const close = (refocus) => {
|
|
29
|
+
returnFocus.current = refocus;
|
|
30
|
+
setFailed(false);
|
|
31
|
+
setEditing(false);
|
|
32
|
+
};
|
|
33
|
+
const save = async (refocus) => {
|
|
34
|
+
if (saving) return;
|
|
35
|
+
if (draft === value) return close(refocus);
|
|
36
|
+
const result = onSave(draft);
|
|
37
|
+
if (!(result instanceof Promise)) return close(refocus);
|
|
38
|
+
setSaving(true);
|
|
39
|
+
try {
|
|
40
|
+
await result;
|
|
41
|
+
close(refocus);
|
|
42
|
+
} catch {
|
|
43
|
+
setFailed(true);
|
|
44
|
+
} finally {
|
|
45
|
+
setSaving(false);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
if (editing) {
|
|
49
|
+
return /* @__PURE__ */ jsx(
|
|
50
|
+
Input,
|
|
51
|
+
{
|
|
52
|
+
autoFocus: true,
|
|
53
|
+
"aria-label": label,
|
|
54
|
+
value: draft,
|
|
55
|
+
invalid: failed,
|
|
56
|
+
"aria-busy": saving || void 0,
|
|
57
|
+
readOnly: saving,
|
|
58
|
+
wrapperClassName: className,
|
|
59
|
+
onChange: (e) => {
|
|
60
|
+
setDraft(e.target.value);
|
|
61
|
+
setFailed(false);
|
|
62
|
+
},
|
|
63
|
+
onKeyDown: (e) => {
|
|
64
|
+
if (e.key === "Enter") {
|
|
65
|
+
e.preventDefault();
|
|
66
|
+
void save(true);
|
|
67
|
+
} else if (e.key === "Escape") {
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
e.stopPropagation();
|
|
70
|
+
close(true);
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
onBlur: () => {
|
|
74
|
+
if (!failed) void save(false);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
return /* @__PURE__ */ jsx(
|
|
80
|
+
"button",
|
|
81
|
+
{
|
|
82
|
+
ref: buttonRef,
|
|
83
|
+
type: "button",
|
|
84
|
+
disabled,
|
|
85
|
+
"aria-label": `${t.editField(label)}, ${value || placeholder || ""}`,
|
|
86
|
+
onClick: () => {
|
|
87
|
+
setDraft(value);
|
|
88
|
+
setEditing(true);
|
|
89
|
+
},
|
|
90
|
+
className: cn(
|
|
91
|
+
"-mx-2 inline-flex min-h-control-sm max-w-full items-center rounded-shape-control px-2 text-start",
|
|
92
|
+
"text-body-md text-[var(--color-on-surface)] hover:bg-[var(--color-surface-container-high)]",
|
|
93
|
+
"disabled:cursor-not-allowed disabled:hover:bg-transparent",
|
|
94
|
+
!value && "text-[var(--color-on-surface-muted)]",
|
|
95
|
+
className
|
|
96
|
+
),
|
|
97
|
+
children: /* @__PURE__ */ jsx("span", { className: "truncate", children: value || placeholder })
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
export {
|
|
102
|
+
InlineEdit
|
|
103
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asks the browser to confirm before the page unloads (reload, close, external
|
|
3
|
+
* link) while `dirty` is true. In-app navigation belongs to your router: block
|
|
4
|
+
* it there with the same flag.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useUnsavedChanges(dirty: boolean): void;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
function useUnsavedChanges(dirty) {
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
if (!dirty) return;
|
|
6
|
+
const onBeforeUnload = (event) => {
|
|
7
|
+
event.preventDefault();
|
|
8
|
+
};
|
|
9
|
+
window.addEventListener("beforeunload", onBeforeUnload);
|
|
10
|
+
return () => window.removeEventListener("beforeunload", onBeforeUnload);
|
|
11
|
+
}, [dirty]);
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
useUnsavedChanges
|
|
15
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { useDisclosure } from './hooks/useDisclosure.js';
|
|
|
7
7
|
export { useIsomorphicLayoutEffect } from './hooks/useIsomorphicLayoutEffect.js';
|
|
8
8
|
export { useMediaQuery } from './hooks/useMediaQuery.js';
|
|
9
9
|
export { useMergedRefs } from './hooks/useMergedRefs.js';
|
|
10
|
+
export { useUnsavedChanges } from './hooks/useUnsavedChanges.js';
|
|
10
11
|
export { usePointerSheen } from './hooks/usePointerSheen.js';
|
|
11
12
|
export { PromptInput } from './components/PromptInput/index.js';
|
|
12
13
|
export type { PromptInputProps, PromptInputMessages, PromptInputStatus } from './components/PromptInput/index.js';
|
|
@@ -213,6 +214,12 @@ export type { ErrorStateProps } from './components/ErrorState/index.js';
|
|
|
213
214
|
export { FileUpload, fileAccepted } from './components/FileUpload/index.js';
|
|
214
215
|
export type { FileUploadRootProps, FileUploadItemProps, FileRejection } from './components/FileUpload/index.js';
|
|
215
216
|
export { OTPInput } from './components/OTPInput/index.js';
|
|
217
|
+
export { Form, useForm } from './components/Form/index.js';
|
|
218
|
+
export type { FormProps, FormErrorItem, FormErrors, FormContextValue } from './components/Form/index.js';
|
|
219
|
+
export { Fieldset } from './components/Fieldset/index.js';
|
|
220
|
+
export type { FieldsetProps } from './components/Fieldset/index.js';
|
|
221
|
+
export { InlineEdit } from './components/InlineEdit/index.js';
|
|
222
|
+
export type { InlineEditProps } from './components/InlineEdit/index.js';
|
|
216
223
|
export type { OTPInputProps } from './components/OTPInput/index.js';
|
|
217
224
|
export { DatePicker } from './components/DatePicker/index.js';
|
|
218
225
|
export type { DatePickerProps } from './components/DatePicker/index.js';
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { useDisclosure } from "./hooks/useDisclosure.js";
|
|
|
6
6
|
import { useIsomorphicLayoutEffect } from "./hooks/useIsomorphicLayoutEffect.js";
|
|
7
7
|
import { useMediaQuery } from "./hooks/useMediaQuery.js";
|
|
8
8
|
import { useMergedRefs } from "./hooks/useMergedRefs.js";
|
|
9
|
+
import { useUnsavedChanges } from "./hooks/useUnsavedChanges.js";
|
|
9
10
|
import { usePointerSheen } from "./hooks/usePointerSheen.js";
|
|
10
11
|
import { PromptInput } from "./components/PromptInput/index.js";
|
|
11
12
|
import { ThinkingIndicator } from "./components/ThinkingIndicator/index.js";
|
|
@@ -120,6 +121,9 @@ import { EmptyState, EmptyStatePreset } from "./components/EmptyState/index.js";
|
|
|
120
121
|
import { ErrorState } from "./components/ErrorState/index.js";
|
|
121
122
|
import { FileUpload, fileAccepted } from "./components/FileUpload/index.js";
|
|
122
123
|
import { OTPInput } from "./components/OTPInput/index.js";
|
|
124
|
+
import { Form, useForm } from "./components/Form/index.js";
|
|
125
|
+
import { Fieldset } from "./components/Fieldset/index.js";
|
|
126
|
+
import { InlineEdit } from "./components/InlineEdit/index.js";
|
|
123
127
|
import { DatePicker } from "./components/DatePicker/index.js";
|
|
124
128
|
import { DateRangePicker } from "./components/DateRangePicker/index.js";
|
|
125
129
|
import { todayIn } from "./components/DateRangePicker/index.js";
|
|
@@ -175,6 +179,7 @@ export {
|
|
|
175
179
|
ErrorState,
|
|
176
180
|
EyebrowChip,
|
|
177
181
|
Fade,
|
|
182
|
+
Fieldset,
|
|
178
183
|
FileUpload,
|
|
179
184
|
FilterBuilder,
|
|
180
185
|
FilterChip,
|
|
@@ -182,6 +187,7 @@ export {
|
|
|
182
187
|
Flex,
|
|
183
188
|
Float,
|
|
184
189
|
FloatingInput,
|
|
190
|
+
Form,
|
|
185
191
|
FormError,
|
|
186
192
|
FormField,
|
|
187
193
|
Funnel,
|
|
@@ -193,6 +199,7 @@ export {
|
|
|
193
199
|
HelperText,
|
|
194
200
|
HoverCard,
|
|
195
201
|
IconButton,
|
|
202
|
+
InlineEdit,
|
|
196
203
|
Input,
|
|
197
204
|
Kbd,
|
|
198
205
|
KeyboardShortcut,
|
|
@@ -312,6 +319,7 @@ export {
|
|
|
312
319
|
transitionName,
|
|
313
320
|
useControllableState,
|
|
314
321
|
useDisclosure,
|
|
322
|
+
useForm,
|
|
315
323
|
useIsomorphicLayoutEffect,
|
|
316
324
|
useLocale,
|
|
317
325
|
useMediaQuery,
|
|
@@ -321,5 +329,6 @@ export {
|
|
|
321
329
|
usePrefersReducedMotion,
|
|
322
330
|
useScopedSeed,
|
|
323
331
|
useTheme,
|
|
324
|
-
useToastStore
|
|
332
|
+
useToastStore,
|
|
333
|
+
useUnsavedChanges
|
|
325
334
|
};
|