@arun-dev/headless 4.11.0 → 4.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/field/index.cjs +287 -0
- package/dist/field/index.d.cts +92 -0
- package/dist/field/index.d.ts +92 -0
- package/dist/field/index.js +176 -0
- package/package.json +11 -1
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/field/index.ts
|
|
21
|
+
var field_exports = {};
|
|
22
|
+
__export(field_exports, {
|
|
23
|
+
Field: () => index_parts_exports
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(field_exports);
|
|
26
|
+
|
|
27
|
+
// src/field/index.parts.ts
|
|
28
|
+
var index_parts_exports = {};
|
|
29
|
+
__export(index_parts_exports, {
|
|
30
|
+
Control: () => FieldControl,
|
|
31
|
+
Description: () => FieldDescription,
|
|
32
|
+
Error: () => FieldError,
|
|
33
|
+
Label: () => FieldLabel,
|
|
34
|
+
Root: () => FieldRoot
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// src/field/FieldRoot.tsx
|
|
38
|
+
var import_react3 = require("react");
|
|
39
|
+
|
|
40
|
+
// src/core/useRender.ts
|
|
41
|
+
var import_react = require("react");
|
|
42
|
+
|
|
43
|
+
// src/core/mergeProps.ts
|
|
44
|
+
function isEventHandler(key) {
|
|
45
|
+
return key.length > 2 && key.startsWith("on") && key[2] === key[2]?.toUpperCase();
|
|
46
|
+
}
|
|
47
|
+
function mergeRefs(...refs) {
|
|
48
|
+
return (node) => {
|
|
49
|
+
const cleanups = refs.map((ref) => {
|
|
50
|
+
if (typeof ref === "function") {
|
|
51
|
+
const result = ref(node);
|
|
52
|
+
return typeof result === "function" ? result : () => ref(null);
|
|
53
|
+
}
|
|
54
|
+
if (ref && typeof ref === "object") {
|
|
55
|
+
ref.current = node;
|
|
56
|
+
return () => {
|
|
57
|
+
ref.current = null;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return void 0;
|
|
61
|
+
});
|
|
62
|
+
return () => {
|
|
63
|
+
cleanups.forEach((cleanup) => {
|
|
64
|
+
if (typeof cleanup === "function") cleanup();
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function isSyntheticEvent(event) {
|
|
70
|
+
return typeof event === "object" && event !== null && "nativeEvent" in event;
|
|
71
|
+
}
|
|
72
|
+
function makeEventPreventable(event) {
|
|
73
|
+
if (Object.prototype.hasOwnProperty.call(event, "preventComponentHandler")) return;
|
|
74
|
+
const target = event;
|
|
75
|
+
target.preventComponentHandler = () => {
|
|
76
|
+
target.componentHandlerPrevented = true;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function isComponentHandlerPrevented(event) {
|
|
80
|
+
return typeof event === "object" && event !== null && event.componentHandlerPrevented === true;
|
|
81
|
+
}
|
|
82
|
+
function chainHandlers(earlier, value) {
|
|
83
|
+
return (...args) => {
|
|
84
|
+
const event = args[0];
|
|
85
|
+
if (isSyntheticEvent(event)) makeEventPreventable(event);
|
|
86
|
+
value(...args);
|
|
87
|
+
if (isComponentHandlerPrevented(event)) return;
|
|
88
|
+
earlier(...args);
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function mergeProps(...objects) {
|
|
92
|
+
const merged = {};
|
|
93
|
+
for (const props of objects) {
|
|
94
|
+
if (!props) continue;
|
|
95
|
+
for (const key of Object.keys(props)) {
|
|
96
|
+
const value = props[key];
|
|
97
|
+
if (value === void 0) continue;
|
|
98
|
+
if (isEventHandler(key) && typeof value === "function") {
|
|
99
|
+
const earlier = merged[key];
|
|
100
|
+
merged[key] = typeof earlier === "function" ? chainHandlers(earlier, value) : value;
|
|
101
|
+
} else if (key === "className") {
|
|
102
|
+
merged.className = merged.className ? `${String(merged.className)} ${String(value)}` : value;
|
|
103
|
+
} else if (key === "style") {
|
|
104
|
+
merged.style = { ...merged.style, ...value };
|
|
105
|
+
} else if (key === "ref") {
|
|
106
|
+
merged.ref = merged.ref ? mergeRefs(merged.ref, value) : value;
|
|
107
|
+
} else {
|
|
108
|
+
merged[key] = value;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return merged;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/core/useRender.ts
|
|
116
|
+
function useRender({
|
|
117
|
+
render,
|
|
118
|
+
defaultTagName,
|
|
119
|
+
props,
|
|
120
|
+
consumerProps
|
|
121
|
+
}) {
|
|
122
|
+
const merged = mergeProps(props, consumerProps);
|
|
123
|
+
if ((0, import_react.isValidElement)(render)) {
|
|
124
|
+
return (0, import_react.cloneElement)(render, mergeProps(merged, render.props));
|
|
125
|
+
}
|
|
126
|
+
return (0, import_react.createElement)(defaultTagName, merged);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// src/field/FieldRootContext.ts
|
|
130
|
+
var import_react2 = require("react");
|
|
131
|
+
var FieldRootContext = (0, import_react2.createContext)(null);
|
|
132
|
+
function useFieldRootContext(part) {
|
|
133
|
+
const context = (0, import_react2.useContext)(FieldRootContext);
|
|
134
|
+
if (context === null) {
|
|
135
|
+
throw new Error(`<Field.${part}> must be rendered inside <Field.Root>.`);
|
|
136
|
+
}
|
|
137
|
+
return context;
|
|
138
|
+
}
|
|
139
|
+
function fieldDataAttributes({ invalid, disabled, required }) {
|
|
140
|
+
return {
|
|
141
|
+
"data-invalid": invalid ? "" : void 0,
|
|
142
|
+
"data-disabled": disabled ? "" : void 0,
|
|
143
|
+
"data-required": required ? "" : void 0
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/field/FieldRoot.tsx
|
|
148
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
149
|
+
function usePresence() {
|
|
150
|
+
const [count, setCount] = (0, import_react3.useState)(0);
|
|
151
|
+
const register = (0, import_react3.useCallback)(() => {
|
|
152
|
+
setCount((n) => n + 1);
|
|
153
|
+
return () => setCount((n) => n - 1);
|
|
154
|
+
}, []);
|
|
155
|
+
return [count > 0, register];
|
|
156
|
+
}
|
|
157
|
+
function FieldRoot({
|
|
158
|
+
invalid = false,
|
|
159
|
+
disabled = false,
|
|
160
|
+
required = false,
|
|
161
|
+
className,
|
|
162
|
+
children,
|
|
163
|
+
render,
|
|
164
|
+
...rest
|
|
165
|
+
}) {
|
|
166
|
+
const id = (0, import_react3.useId)();
|
|
167
|
+
const [hasDescription, registerDescription] = usePresence();
|
|
168
|
+
const [hasError, registerError] = usePresence();
|
|
169
|
+
const context = (0, import_react3.useMemo)(
|
|
170
|
+
() => ({
|
|
171
|
+
invalid,
|
|
172
|
+
disabled,
|
|
173
|
+
required,
|
|
174
|
+
controlId: `${id}-control`,
|
|
175
|
+
descriptionId: `${id}-description`,
|
|
176
|
+
errorId: `${id}-error`,
|
|
177
|
+
hasDescription,
|
|
178
|
+
hasError,
|
|
179
|
+
registerDescription,
|
|
180
|
+
registerError
|
|
181
|
+
}),
|
|
182
|
+
[invalid, disabled, required, id, hasDescription, hasError, registerDescription, registerError]
|
|
183
|
+
);
|
|
184
|
+
const element = useRender({
|
|
185
|
+
render,
|
|
186
|
+
defaultTagName: "div",
|
|
187
|
+
props: { ...fieldDataAttributes({ invalid, disabled, required }), className, children },
|
|
188
|
+
consumerProps: rest
|
|
189
|
+
});
|
|
190
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FieldRootContext.Provider, { value: context, children: element });
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// src/field/FieldLabel.tsx
|
|
194
|
+
function FieldLabel({ className, children, render, ...rest }) {
|
|
195
|
+
const { controlId, ...state } = useFieldRootContext("Label");
|
|
196
|
+
return useRender({
|
|
197
|
+
render,
|
|
198
|
+
defaultTagName: "label",
|
|
199
|
+
props: { htmlFor: controlId, ...fieldDataAttributes(state), className, children },
|
|
200
|
+
consumerProps: rest
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// src/field/FieldControl.tsx
|
|
205
|
+
function FieldControl({
|
|
206
|
+
render,
|
|
207
|
+
"aria-describedby": describedBy,
|
|
208
|
+
...rest
|
|
209
|
+
}) {
|
|
210
|
+
const { controlId, descriptionId, errorId, hasDescription, hasError, ...state } = useFieldRootContext("Control");
|
|
211
|
+
const describedByIds = [
|
|
212
|
+
hasDescription ? descriptionId : void 0,
|
|
213
|
+
hasError ? errorId : void 0,
|
|
214
|
+
describedBy
|
|
215
|
+
].filter(Boolean).join(" ");
|
|
216
|
+
return useRender({
|
|
217
|
+
render,
|
|
218
|
+
defaultTagName: "input",
|
|
219
|
+
props: {
|
|
220
|
+
id: controlId,
|
|
221
|
+
"aria-describedby": describedByIds || void 0,
|
|
222
|
+
"aria-invalid": state.invalid || void 0,
|
|
223
|
+
disabled: state.disabled || void 0,
|
|
224
|
+
required: state.required || void 0,
|
|
225
|
+
...fieldDataAttributes(state)
|
|
226
|
+
},
|
|
227
|
+
consumerProps: rest
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/field/FieldDescription.tsx
|
|
232
|
+
var import_react4 = require("react");
|
|
233
|
+
function FieldDescription({ className, children, render, ...rest }) {
|
|
234
|
+
const context = useFieldRootContext("Description");
|
|
235
|
+
const { descriptionId, registerDescription, invalid, disabled, required } = context;
|
|
236
|
+
(0, import_react4.useLayoutEffect)(registerDescription, [registerDescription]);
|
|
237
|
+
return useRender({
|
|
238
|
+
render,
|
|
239
|
+
defaultTagName: "p",
|
|
240
|
+
props: {
|
|
241
|
+
id: descriptionId,
|
|
242
|
+
...fieldDataAttributes({ invalid, disabled, required }),
|
|
243
|
+
className,
|
|
244
|
+
children
|
|
245
|
+
},
|
|
246
|
+
consumerProps: rest
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/field/FieldError.tsx
|
|
251
|
+
var import_react5 = require("react");
|
|
252
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
253
|
+
function FieldError({ className, children, render, ...rest }) {
|
|
254
|
+
const context = useFieldRootContext("Error");
|
|
255
|
+
const { invalid, disabled, required } = context;
|
|
256
|
+
if (!invalid) return null;
|
|
257
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
258
|
+
ShownError,
|
|
259
|
+
{
|
|
260
|
+
className,
|
|
261
|
+
render,
|
|
262
|
+
state: { invalid, disabled, required },
|
|
263
|
+
...rest,
|
|
264
|
+
children
|
|
265
|
+
}
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
function ShownError({
|
|
269
|
+
className,
|
|
270
|
+
children,
|
|
271
|
+
render,
|
|
272
|
+
state,
|
|
273
|
+
...rest
|
|
274
|
+
}) {
|
|
275
|
+
const { errorId, registerError } = useFieldRootContext("Error");
|
|
276
|
+
(0, import_react5.useLayoutEffect)(registerError, [registerError]);
|
|
277
|
+
return useRender({
|
|
278
|
+
render,
|
|
279
|
+
defaultTagName: "p",
|
|
280
|
+
props: { id: errorId, ...fieldDataAttributes(state), className, children },
|
|
281
|
+
consumerProps: rest
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
285
|
+
0 && (module.exports = {
|
|
286
|
+
Field
|
|
287
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactElement, Ref, ComponentPropsWithRef } from 'react';
|
|
3
|
+
|
|
4
|
+
type FieldRootOwnProps = {
|
|
5
|
+
/** Shows the Error and marks the control `aria-invalid`. Yours to set — Field validates nothing. */
|
|
6
|
+
invalid?: boolean;
|
|
7
|
+
/** Disables the control and marks every part `data-disabled`. */
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
/** Marks the control `required`, so the browser's own validation reports it. */
|
|
10
|
+
required?: boolean;
|
|
11
|
+
/** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
|
|
12
|
+
render?: ReactElement;
|
|
13
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
14
|
+
ref?: Ref<HTMLElement>;
|
|
15
|
+
};
|
|
16
|
+
type FieldRootProps = FieldRootOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof FieldRootOwnProps>;
|
|
17
|
+
/**
|
|
18
|
+
* One control with its label, description and error (decision 15). Generates their ids and
|
|
19
|
+
* holds `invalid`, `disabled` and `required`; `Control` puts them on the control.
|
|
20
|
+
*
|
|
21
|
+
* Validity is yours: set `invalid` from react-hook-form's `fieldState`, or from what the
|
|
22
|
+
* browser reported. A group of controls under one label is a native `<fieldset>`.
|
|
23
|
+
*/
|
|
24
|
+
declare function FieldRoot({ invalid, disabled, required, className, children, render, ...rest }: FieldRootProps): react.JSX.Element;
|
|
25
|
+
|
|
26
|
+
type FieldLabelOwnProps = {
|
|
27
|
+
/** Element to render instead of the default `<label>`. Props and ref are merged onto it. */
|
|
28
|
+
render?: ReactElement;
|
|
29
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
30
|
+
ref?: Ref<HTMLElement>;
|
|
31
|
+
};
|
|
32
|
+
type FieldLabelProps = FieldLabelOwnProps & Omit<ComponentPropsWithRef<'label'>, keyof FieldLabelOwnProps>;
|
|
33
|
+
/** Names the control: a `<label>` whose `htmlFor` is the control's id, so a press focuses it. */
|
|
34
|
+
declare function FieldLabel({ className, children, render, ...rest }: FieldLabelProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
35
|
+
|
|
36
|
+
type FieldControlOwnProps = {
|
|
37
|
+
/**
|
|
38
|
+
* The control: `<Input />`, `<Select />`, `<Checkbox.Root />`, `<Combobox.Input />` or your
|
|
39
|
+
* own. Defaults to an `<input>`. The Field's `id`, `aria-describedby`, `aria-invalid`,
|
|
40
|
+
* `disabled` and `required` are merged onto it.
|
|
41
|
+
*/
|
|
42
|
+
render?: ReactElement;
|
|
43
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
44
|
+
ref?: Ref<HTMLElement>;
|
|
45
|
+
};
|
|
46
|
+
type FieldControlProps = FieldControlOwnProps & Omit<ComponentPropsWithRef<'input'>, keyof FieldControlOwnProps>;
|
|
47
|
+
/**
|
|
48
|
+
* Puts the Field on its control. `render` is the seam: Field does not reach into each control,
|
|
49
|
+
* it hands the control the attributes that tie it to the label, description and error.
|
|
50
|
+
*
|
|
51
|
+
* `aria-describedby` names the Description, then the Error — only those rendered — followed by
|
|
52
|
+
* any of your own. The id is the Field's: an `id` of your own breaks the Label's `htmlFor`.
|
|
53
|
+
*/
|
|
54
|
+
declare function FieldControl({ render, 'aria-describedby': describedBy, ...rest }: FieldControlProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
55
|
+
|
|
56
|
+
type FieldDescriptionOwnProps = {
|
|
57
|
+
/** Element to render instead of the default `<p>`. Props and ref are merged onto it. */
|
|
58
|
+
render?: ReactElement;
|
|
59
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
60
|
+
ref?: Ref<HTMLElement>;
|
|
61
|
+
};
|
|
62
|
+
type FieldDescriptionProps = FieldDescriptionOwnProps & Omit<ComponentPropsWithRef<'p'>, keyof FieldDescriptionOwnProps>;
|
|
63
|
+
/**
|
|
64
|
+
* Help text under the control, read out with it through `aria-describedby`.
|
|
65
|
+
*/
|
|
66
|
+
declare function FieldDescription({ className, children, render, ...rest }: FieldDescriptionProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
67
|
+
|
|
68
|
+
type FieldErrorOwnProps = {
|
|
69
|
+
/** Element to render instead of the default `<p>`. Props and ref are merged onto it. */
|
|
70
|
+
render?: ReactElement;
|
|
71
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
72
|
+
ref?: Ref<HTMLElement>;
|
|
73
|
+
};
|
|
74
|
+
type FieldErrorProps = FieldErrorOwnProps & Omit<ComponentPropsWithRef<'p'>, keyof FieldErrorOwnProps>;
|
|
75
|
+
/**
|
|
76
|
+
* What is wrong, read out with the control through `aria-describedby`. Rendered only while the
|
|
77
|
+
* Root is `invalid`, so it is named only while it is shown.
|
|
78
|
+
*/
|
|
79
|
+
declare function FieldError({ className, children, render, ...rest }: FieldErrorProps): react.JSX.Element | null;
|
|
80
|
+
|
|
81
|
+
declare namespace index_parts {
|
|
82
|
+
export { FieldControl as Control, FieldDescription as Description, FieldError as Error, FieldLabel as Label, FieldRoot as Root };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** The state Field.Root shares with its parts, and projects as `data-*` attributes. */
|
|
86
|
+
type FieldState = {
|
|
87
|
+
invalid: boolean;
|
|
88
|
+
disabled: boolean;
|
|
89
|
+
required: boolean;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export { index_parts as Field, type FieldControlProps, type FieldDescriptionProps, type FieldErrorProps, type FieldLabelProps, type FieldRootProps, type FieldState };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactElement, Ref, ComponentPropsWithRef } from 'react';
|
|
3
|
+
|
|
4
|
+
type FieldRootOwnProps = {
|
|
5
|
+
/** Shows the Error and marks the control `aria-invalid`. Yours to set — Field validates nothing. */
|
|
6
|
+
invalid?: boolean;
|
|
7
|
+
/** Disables the control and marks every part `data-disabled`. */
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
/** Marks the control `required`, so the browser's own validation reports it. */
|
|
10
|
+
required?: boolean;
|
|
11
|
+
/** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
|
|
12
|
+
render?: ReactElement;
|
|
13
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
14
|
+
ref?: Ref<HTMLElement>;
|
|
15
|
+
};
|
|
16
|
+
type FieldRootProps = FieldRootOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof FieldRootOwnProps>;
|
|
17
|
+
/**
|
|
18
|
+
* One control with its label, description and error (decision 15). Generates their ids and
|
|
19
|
+
* holds `invalid`, `disabled` and `required`; `Control` puts them on the control.
|
|
20
|
+
*
|
|
21
|
+
* Validity is yours: set `invalid` from react-hook-form's `fieldState`, or from what the
|
|
22
|
+
* browser reported. A group of controls under one label is a native `<fieldset>`.
|
|
23
|
+
*/
|
|
24
|
+
declare function FieldRoot({ invalid, disabled, required, className, children, render, ...rest }: FieldRootProps): react.JSX.Element;
|
|
25
|
+
|
|
26
|
+
type FieldLabelOwnProps = {
|
|
27
|
+
/** Element to render instead of the default `<label>`. Props and ref are merged onto it. */
|
|
28
|
+
render?: ReactElement;
|
|
29
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
30
|
+
ref?: Ref<HTMLElement>;
|
|
31
|
+
};
|
|
32
|
+
type FieldLabelProps = FieldLabelOwnProps & Omit<ComponentPropsWithRef<'label'>, keyof FieldLabelOwnProps>;
|
|
33
|
+
/** Names the control: a `<label>` whose `htmlFor` is the control's id, so a press focuses it. */
|
|
34
|
+
declare function FieldLabel({ className, children, render, ...rest }: FieldLabelProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
35
|
+
|
|
36
|
+
type FieldControlOwnProps = {
|
|
37
|
+
/**
|
|
38
|
+
* The control: `<Input />`, `<Select />`, `<Checkbox.Root />`, `<Combobox.Input />` or your
|
|
39
|
+
* own. Defaults to an `<input>`. The Field's `id`, `aria-describedby`, `aria-invalid`,
|
|
40
|
+
* `disabled` and `required` are merged onto it.
|
|
41
|
+
*/
|
|
42
|
+
render?: ReactElement;
|
|
43
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
44
|
+
ref?: Ref<HTMLElement>;
|
|
45
|
+
};
|
|
46
|
+
type FieldControlProps = FieldControlOwnProps & Omit<ComponentPropsWithRef<'input'>, keyof FieldControlOwnProps>;
|
|
47
|
+
/**
|
|
48
|
+
* Puts the Field on its control. `render` is the seam: Field does not reach into each control,
|
|
49
|
+
* it hands the control the attributes that tie it to the label, description and error.
|
|
50
|
+
*
|
|
51
|
+
* `aria-describedby` names the Description, then the Error — only those rendered — followed by
|
|
52
|
+
* any of your own. The id is the Field's: an `id` of your own breaks the Label's `htmlFor`.
|
|
53
|
+
*/
|
|
54
|
+
declare function FieldControl({ render, 'aria-describedby': describedBy, ...rest }: FieldControlProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
55
|
+
|
|
56
|
+
type FieldDescriptionOwnProps = {
|
|
57
|
+
/** Element to render instead of the default `<p>`. Props and ref are merged onto it. */
|
|
58
|
+
render?: ReactElement;
|
|
59
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
60
|
+
ref?: Ref<HTMLElement>;
|
|
61
|
+
};
|
|
62
|
+
type FieldDescriptionProps = FieldDescriptionOwnProps & Omit<ComponentPropsWithRef<'p'>, keyof FieldDescriptionOwnProps>;
|
|
63
|
+
/**
|
|
64
|
+
* Help text under the control, read out with it through `aria-describedby`.
|
|
65
|
+
*/
|
|
66
|
+
declare function FieldDescription({ className, children, render, ...rest }: FieldDescriptionProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
67
|
+
|
|
68
|
+
type FieldErrorOwnProps = {
|
|
69
|
+
/** Element to render instead of the default `<p>`. Props and ref are merged onto it. */
|
|
70
|
+
render?: ReactElement;
|
|
71
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
72
|
+
ref?: Ref<HTMLElement>;
|
|
73
|
+
};
|
|
74
|
+
type FieldErrorProps = FieldErrorOwnProps & Omit<ComponentPropsWithRef<'p'>, keyof FieldErrorOwnProps>;
|
|
75
|
+
/**
|
|
76
|
+
* What is wrong, read out with the control through `aria-describedby`. Rendered only while the
|
|
77
|
+
* Root is `invalid`, so it is named only while it is shown.
|
|
78
|
+
*/
|
|
79
|
+
declare function FieldError({ className, children, render, ...rest }: FieldErrorProps): react.JSX.Element | null;
|
|
80
|
+
|
|
81
|
+
declare namespace index_parts {
|
|
82
|
+
export { FieldControl as Control, FieldDescription as Description, FieldError as Error, FieldLabel as Label, FieldRoot as Root };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** The state Field.Root shares with its parts, and projects as `data-*` attributes. */
|
|
86
|
+
type FieldState = {
|
|
87
|
+
invalid: boolean;
|
|
88
|
+
disabled: boolean;
|
|
89
|
+
required: boolean;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export { index_parts as Field, type FieldControlProps, type FieldDescriptionProps, type FieldErrorProps, type FieldLabelProps, type FieldRootProps, type FieldState };
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__export,
|
|
3
|
+
useRender
|
|
4
|
+
} from "../chunk-OQTWWZ4B.js";
|
|
5
|
+
|
|
6
|
+
// src/field/index.parts.ts
|
|
7
|
+
var index_parts_exports = {};
|
|
8
|
+
__export(index_parts_exports, {
|
|
9
|
+
Control: () => FieldControl,
|
|
10
|
+
Description: () => FieldDescription,
|
|
11
|
+
Error: () => FieldError,
|
|
12
|
+
Label: () => FieldLabel,
|
|
13
|
+
Root: () => FieldRoot
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// src/field/FieldRoot.tsx
|
|
17
|
+
import { useCallback, useId, useMemo, useState } from "react";
|
|
18
|
+
|
|
19
|
+
// src/field/FieldRootContext.ts
|
|
20
|
+
import { createContext, useContext } from "react";
|
|
21
|
+
var FieldRootContext = createContext(null);
|
|
22
|
+
function useFieldRootContext(part) {
|
|
23
|
+
const context = useContext(FieldRootContext);
|
|
24
|
+
if (context === null) {
|
|
25
|
+
throw new Error(`<Field.${part}> must be rendered inside <Field.Root>.`);
|
|
26
|
+
}
|
|
27
|
+
return context;
|
|
28
|
+
}
|
|
29
|
+
function fieldDataAttributes({ invalid, disabled, required }) {
|
|
30
|
+
return {
|
|
31
|
+
"data-invalid": invalid ? "" : void 0,
|
|
32
|
+
"data-disabled": disabled ? "" : void 0,
|
|
33
|
+
"data-required": required ? "" : void 0
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/field/FieldRoot.tsx
|
|
38
|
+
import { jsx } from "react/jsx-runtime";
|
|
39
|
+
function usePresence() {
|
|
40
|
+
const [count, setCount] = useState(0);
|
|
41
|
+
const register = useCallback(() => {
|
|
42
|
+
setCount((n) => n + 1);
|
|
43
|
+
return () => setCount((n) => n - 1);
|
|
44
|
+
}, []);
|
|
45
|
+
return [count > 0, register];
|
|
46
|
+
}
|
|
47
|
+
function FieldRoot({
|
|
48
|
+
invalid = false,
|
|
49
|
+
disabled = false,
|
|
50
|
+
required = false,
|
|
51
|
+
className,
|
|
52
|
+
children,
|
|
53
|
+
render,
|
|
54
|
+
...rest
|
|
55
|
+
}) {
|
|
56
|
+
const id = useId();
|
|
57
|
+
const [hasDescription, registerDescription] = usePresence();
|
|
58
|
+
const [hasError, registerError] = usePresence();
|
|
59
|
+
const context = useMemo(
|
|
60
|
+
() => ({
|
|
61
|
+
invalid,
|
|
62
|
+
disabled,
|
|
63
|
+
required,
|
|
64
|
+
controlId: `${id}-control`,
|
|
65
|
+
descriptionId: `${id}-description`,
|
|
66
|
+
errorId: `${id}-error`,
|
|
67
|
+
hasDescription,
|
|
68
|
+
hasError,
|
|
69
|
+
registerDescription,
|
|
70
|
+
registerError
|
|
71
|
+
}),
|
|
72
|
+
[invalid, disabled, required, id, hasDescription, hasError, registerDescription, registerError]
|
|
73
|
+
);
|
|
74
|
+
const element = useRender({
|
|
75
|
+
render,
|
|
76
|
+
defaultTagName: "div",
|
|
77
|
+
props: { ...fieldDataAttributes({ invalid, disabled, required }), className, children },
|
|
78
|
+
consumerProps: rest
|
|
79
|
+
});
|
|
80
|
+
return /* @__PURE__ */ jsx(FieldRootContext.Provider, { value: context, children: element });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/field/FieldLabel.tsx
|
|
84
|
+
function FieldLabel({ className, children, render, ...rest }) {
|
|
85
|
+
const { controlId, ...state } = useFieldRootContext("Label");
|
|
86
|
+
return useRender({
|
|
87
|
+
render,
|
|
88
|
+
defaultTagName: "label",
|
|
89
|
+
props: { htmlFor: controlId, ...fieldDataAttributes(state), className, children },
|
|
90
|
+
consumerProps: rest
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/field/FieldControl.tsx
|
|
95
|
+
function FieldControl({
|
|
96
|
+
render,
|
|
97
|
+
"aria-describedby": describedBy,
|
|
98
|
+
...rest
|
|
99
|
+
}) {
|
|
100
|
+
const { controlId, descriptionId, errorId, hasDescription, hasError, ...state } = useFieldRootContext("Control");
|
|
101
|
+
const describedByIds = [
|
|
102
|
+
hasDescription ? descriptionId : void 0,
|
|
103
|
+
hasError ? errorId : void 0,
|
|
104
|
+
describedBy
|
|
105
|
+
].filter(Boolean).join(" ");
|
|
106
|
+
return useRender({
|
|
107
|
+
render,
|
|
108
|
+
defaultTagName: "input",
|
|
109
|
+
props: {
|
|
110
|
+
id: controlId,
|
|
111
|
+
"aria-describedby": describedByIds || void 0,
|
|
112
|
+
"aria-invalid": state.invalid || void 0,
|
|
113
|
+
disabled: state.disabled || void 0,
|
|
114
|
+
required: state.required || void 0,
|
|
115
|
+
...fieldDataAttributes(state)
|
|
116
|
+
},
|
|
117
|
+
consumerProps: rest
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/field/FieldDescription.tsx
|
|
122
|
+
import { useLayoutEffect } from "react";
|
|
123
|
+
function FieldDescription({ className, children, render, ...rest }) {
|
|
124
|
+
const context = useFieldRootContext("Description");
|
|
125
|
+
const { descriptionId, registerDescription, invalid, disabled, required } = context;
|
|
126
|
+
useLayoutEffect(registerDescription, [registerDescription]);
|
|
127
|
+
return useRender({
|
|
128
|
+
render,
|
|
129
|
+
defaultTagName: "p",
|
|
130
|
+
props: {
|
|
131
|
+
id: descriptionId,
|
|
132
|
+
...fieldDataAttributes({ invalid, disabled, required }),
|
|
133
|
+
className,
|
|
134
|
+
children
|
|
135
|
+
},
|
|
136
|
+
consumerProps: rest
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/field/FieldError.tsx
|
|
141
|
+
import { useLayoutEffect as useLayoutEffect2 } from "react";
|
|
142
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
143
|
+
function FieldError({ className, children, render, ...rest }) {
|
|
144
|
+
const context = useFieldRootContext("Error");
|
|
145
|
+
const { invalid, disabled, required } = context;
|
|
146
|
+
if (!invalid) return null;
|
|
147
|
+
return /* @__PURE__ */ jsx2(
|
|
148
|
+
ShownError,
|
|
149
|
+
{
|
|
150
|
+
className,
|
|
151
|
+
render,
|
|
152
|
+
state: { invalid, disabled, required },
|
|
153
|
+
...rest,
|
|
154
|
+
children
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
function ShownError({
|
|
159
|
+
className,
|
|
160
|
+
children,
|
|
161
|
+
render,
|
|
162
|
+
state,
|
|
163
|
+
...rest
|
|
164
|
+
}) {
|
|
165
|
+
const { errorId, registerError } = useFieldRootContext("Error");
|
|
166
|
+
useLayoutEffect2(registerError, [registerError]);
|
|
167
|
+
return useRender({
|
|
168
|
+
render,
|
|
169
|
+
defaultTagName: "p",
|
|
170
|
+
props: { id: errorId, ...fieldDataAttributes(state), className, children },
|
|
171
|
+
consumerProps: rest
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
export {
|
|
175
|
+
index_parts_exports as Field
|
|
176
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arun-dev/headless",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.12.0",
|
|
4
4
|
"description": "Unstyled React behaviour primitives — render engine, controlled/uncontrolled state, and data-* state projection. Ships no CSS and no class names.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -104,6 +104,16 @@
|
|
|
104
104
|
"default": "./dist/radio-group/index.cjs"
|
|
105
105
|
}
|
|
106
106
|
},
|
|
107
|
+
"./field": {
|
|
108
|
+
"import": {
|
|
109
|
+
"types": "./dist/field/index.d.ts",
|
|
110
|
+
"default": "./dist/field/index.js"
|
|
111
|
+
},
|
|
112
|
+
"require": {
|
|
113
|
+
"types": "./dist/field/index.d.cts",
|
|
114
|
+
"default": "./dist/field/index.cjs"
|
|
115
|
+
}
|
|
116
|
+
},
|
|
107
117
|
"./switch": {
|
|
108
118
|
"import": {
|
|
109
119
|
"types": "./dist/switch/index.d.ts",
|