@bento/input 0.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/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 GoDaddy Operating Company, LLC.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.mdx ADDED
@@ -0,0 +1,148 @@
1
+ import {
2
+ Meta,
3
+ Story,
4
+ ArgTypes,
5
+ Controls,
6
+ Source,
7
+ } from '@storybook/addon-docs/blocks';
8
+ import * as Stories from './input.stories.tsx';
9
+
10
+ import SourceControlled from './examples/controlled.tsx?raw';
11
+ import SourceForm from './examples/basic-form.tsx?raw';
12
+ import SourceUncontrolled from './examples/uncontrolled.tsx?raw';
13
+
14
+ <Meta of={Stories} name="Overview" />
15
+
16
+ # Input
17
+
18
+ The `@bento/input` package provides a universal input primitive component that renders an `<input>` element with React Aria interactions. It supports all HTML input types with proper accessibility, hover, and focus management.
19
+
20
+ This primitive is built with accessibility and flexibility in mind, providing state-based render props, comprehensive data attributes, and integration with React Aria's focus and hover hooks. It can be used for text inputs, checkboxes, radio buttons, file uploads, and any other input type supported by HTML.
21
+
22
+ ## Installation
23
+
24
+ ```shell
25
+ npm install --save @bento/input
26
+ ```
27
+
28
+ ## Props
29
+
30
+ The `@bento/input` package exports the `Input` component:
31
+
32
+ ```jsx
33
+ import { Input } from '@bento/input';
34
+
35
+ <Input type="text" placeholder="Enter text" />
36
+ ```
37
+
38
+ The following properties are available to be used on the `Input` component:
39
+
40
+ <ArgTypes of={Stories.Props} />
41
+
42
+ For all other properties specified on the `Input` component, the component
43
+ will pass them down to the underlying `<input>` element. This includes properties
44
+ such as `id`, `data-*` attributes, or additional ARIA attributes that you might
45
+ need for specialized use cases.
46
+
47
+ ## Examples
48
+
49
+ ### Controlled Input
50
+
51
+ The most common pattern for the `Input` component is to use it as a controlled component, where the value is managed by React state. This allows you to easily read and update the input value.
52
+
53
+ <Source language='tsx' code={SourceControlled} />
54
+ <Story of={Stories.Controlled} inline />
55
+ <Controls of={Stories.Controlled} />
56
+
57
+ In this example, the input value is stored in state and updated via the `onChange` handler. This is the recommended pattern for most use cases where you need to read or validate the input value.
58
+
59
+ ### Uncontrolled Input
60
+
61
+ For simpler use cases where you don't need to track the input value in React state, you can use an uncontrolled input with a `defaultValue`. The DOM will manage the input's value internally.
62
+
63
+ <Source language='tsx' code={SourceUncontrolled} />
64
+ <Story of={Stories.Uncontrolled} inline />
65
+ <Controls of={Stories.Uncontrolled} />
66
+
67
+ Uncontrolled inputs are useful when you only need to read the value on form submission, or when integrating with form libraries that manage values through refs.
68
+
69
+ ### Form Example
70
+
71
+ The `Input` component supports all HTML input types, making it versatile for building complete forms. This example demonstrates multiple input types working together in a single form.
72
+
73
+ <Source language='tsx' code={SourceForm} />
74
+ <Story of={Stories.BasicForm} inline collapsed />
75
+ <Controls of={Stories.BasicForm} />
76
+
77
+ The component intelligently handles type-specific props, ensuring that only relevant attributes are applied to each input type. For example, `min` and `max` are only applied to number and range inputs, while `checked` is only applied to checkbox and radio inputs.
78
+
79
+ ## Usage Guidelines
80
+
81
+ The `Input` component is a low-level primitive that provides the foundation for building input fields. Understanding when and how to use it will help you create accessible, user-friendly forms.
82
+
83
+ ### Supported Input Types
84
+
85
+ The `Input` component supports all HTML input types:
86
+
87
+ **Text-based inputs:** `text`, `email`, `password`, `url`, `tel`, `search`
88
+ **Numeric inputs:** `number`, `range`
89
+ **Date/time inputs:** `date`, `datetime-local`, `time`, `month`, `week`
90
+ **Choice inputs:** `checkbox`, `radio`
91
+ **File input:** `file`
92
+ **Color input:** `color`
93
+ **Hidden input:** `hidden`
94
+
95
+ Each input type receives appropriate type-specific props automatically. You don't need to worry about which props are valid for which types—the component handles this for you.
96
+
97
+ ### Accessibility Best Practices
98
+
99
+ Always provide labels for your inputs using the `<label>` element with a matching `htmlFor` attribute, or by using `aria-label` or `aria-labelledby`. Never rely solely on `placeholder` text for labeling, as it disappears when the user starts typing.
100
+
101
+ For invalid inputs, set `aria-invalid` to provide semantic meaning to assistive technologies. Combine this with `aria-describedby` to reference error messages that explain what went wrong.
102
+
103
+ Use appropriate input types to enable better mobile keyboards and native browser features. For example, `type="email"` shows an email-optimized keyboard on mobile devices, while `type="date"` shows a native date picker.
104
+
105
+ ## Accessibility
106
+
107
+ The `Input` component is built with accessibility as a core requirement. It provides comprehensive keyboard support, ARIA attributes, and integration with React Aria's focus management system.
108
+
109
+ **Keyboard Navigation**
110
+
111
+ All input types are fully keyboard accessible. Text inputs can be focused with Tab and navigated with arrow keys. Checkboxes and radio buttons can be toggled with Space. The component integrates with React Aria's `useFocusRing` hook to provide intelligent focus indicators that only appear during keyboard navigation, not mouse clicks.
112
+
113
+ **Focus Management**
114
+
115
+ The component distinguishes between mouse focus and keyboard focus through the `isFocused` and `isFocusVisible` states. This allows you to style focus rings appropriately—showing them only when the user is navigating with a keyboard, which reduces visual noise for mouse users while maintaining accessibility for keyboard users.
116
+
117
+ **ARIA Support**
118
+
119
+ The component passes through all ARIA attributes you provide, including `aria-label`, `aria-labelledby`, `aria-describedby`, and `aria-invalid`. These attributes are essential for screen reader users to understand the purpose and state of form fields.
120
+
121
+ **State Communication**
122
+
123
+ The component provides comprehensive data attributes that communicate its state to both CSS selectors and assistive technologies. These attributes make it easy to style inputs based on their state and ensure consistent visual feedback.
124
+
125
+ ## Customization
126
+
127
+ The `Input` component is built using the `@bento/slots` package, allowing you to customize styling based on component state through render props, slots, and data attributes.
128
+
129
+ ### Data Attributes
130
+
131
+ The component automatically applies data attributes that correspond to its state, allowing you to style with CSS selectors:
132
+
133
+ | Attribute | Description | Example Values |
134
+ | ---------------------- | ---------------------------------------------- | ----------------- |
135
+ | `data-focused` | Whether the input is focused | "true" / "false" |
136
+ | `data-focus-visible` | Whether keyboard focus ring should be visible | "true" / "false" |
137
+ | `data-hovered` | Whether the input is hovered | "true" / "false" |
138
+ | `data-disabled` | Whether the input is disabled | "true" / "false" |
139
+ | `data-invalid` | Whether the input is invalid | "true" / "false" |
140
+ | `data-readonly` | Whether the input is read-only | "true" / "false" |
141
+ | `data-required` | Whether the input is required | "true" / "false" |
142
+ | `data-empty` | Whether the input has no value | "true" / "false" |
143
+ | `data-checked` | Whether checkbox/radio is checked | "true" / "false" |
144
+
145
+ ### Slots
146
+
147
+ The component is registered as `BentoInput` in the slots system. While the base Input component doesn't introduce additional slots, it can be extended with slot-based composition for building higher-level components.
148
+
package/dist/index.cjs ADDED
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var useDataAttributes = require('@bento/use-data-attributes');
5
+ var useProps = require('@bento/use-props');
6
+ var slots = require('@bento/slots');
7
+ var reactAria = require('react-aria');
8
+ var utils = require('@react-aria/utils');
9
+
10
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
+
12
+ var React__default = /*#__PURE__*/_interopDefault(React);
13
+
14
+ // src/index.tsx
15
+ var Input = slots.withSlots("BentoInput", function Input2(...args) {
16
+ const { props, apply } = useProps.useProps(args);
17
+ const { autoFocus } = props;
18
+ const { isFocused, isFocusVisible, focusProps } = reactAria.useFocusRing({
19
+ isTextInput: true,
20
+ autoFocus
21
+ });
22
+ const { hoverProps, isHovered } = reactAria.useHover(props);
23
+ const mergedProps = utils.mergeProps(props, focusProps, hoverProps);
24
+ return /* @__PURE__ */ React__default.default.createElement(
25
+ "input",
26
+ {
27
+ ...apply(mergedProps),
28
+ ...useDataAttributes.useDataAttributes({
29
+ focused: isFocused,
30
+ hovered: isHovered,
31
+ focusVisible: isFocusVisible,
32
+ disabled: props.disabled || false,
33
+ invalid: !!props["aria-invalid"] && props["aria-invalid"] !== "false",
34
+ readonly: props.readOnly || false,
35
+ required: props.required || false,
36
+ empty: props.value === "" || props.value === void 0 || props.value === null,
37
+ checked: props.type === "checkbox" || props.type === "radio" ? !!props.checked : void 0
38
+ })
39
+ }
40
+ );
41
+ });
42
+
43
+ exports.Input = Input;
44
+ //# sourceMappingURL=index.cjs.map
45
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.tsx"],"names":["withSlots","Input","useProps","useFocusRing","useHover","mergeProps","React","useDataAttributes"],"mappings":";;;;;;;;;;;;;;AA0CO,IAAM,KAAA,GAAQA,eAAA,CAAU,YAAA,EAAc,SAASC,UAAS,IAAA,EAAkD;AAC/G,EAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAM,GAAIC,kBAAS,IAAI,CAAA;AACtC,EAAA,MAAM,EAAE,WAAU,GAAI,KAAA;AAEtB,EAAA,MAAM,EAAE,SAAA,EAAW,cAAA,EAAgB,UAAA,KAAeC,sBAAA,CAAa;AAAA,IAC7D,WAAA,EAAa,IAAA;AAAA,IACb;AAAA,GACD,CAAA;AACD,EAAA,MAAM,EAAE,UAAA,EAAY,SAAA,EAAU,GAAIC,mBAAS,KAAK,CAAA;AAEhD,EAAA,MAAM,WAAA,GAAcC,gBAAA,CAAW,KAAA,EAAO,UAAA,EAAY,UAAU,CAAA;AAE5D,EAAA,uBACEC,sBAAA,CAAA,aAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACE,GAAG,MAAM,WAAW,CAAA;AAAA,MACpB,GAAGC,mCAAA,CAAkB;AAAA,QACpB,OAAA,EAAS,SAAA;AAAA,QACT,OAAA,EAAS,SAAA;AAAA,QACT,YAAA,EAAc,cAAA;AAAA,QACd,QAAA,EAAU,MAAM,QAAA,IAAY,KAAA;AAAA,QAC5B,OAAA,EAAS,CAAC,CAAC,KAAA,CAAM,cAAc,CAAA,IAAK,KAAA,CAAM,cAAc,CAAA,KAAM,OAAA;AAAA,QAC9D,QAAA,EAAU,MAAM,QAAA,IAAY,KAAA;AAAA,QAC5B,QAAA,EAAU,MAAM,QAAA,IAAY,KAAA;AAAA,QAC5B,KAAA,EAAO,MAAM,KAAA,KAAU,EAAA,IAAM,MAAM,KAAA,KAAU,MAAA,IAAa,MAAM,KAAA,KAAU,IAAA;AAAA,QAC1E,OAAA,EAAS,KAAA,CAAM,IAAA,KAAS,UAAA,IAAc,KAAA,CAAM,SAAS,OAAA,GAAU,CAAC,CAAC,KAAA,CAAM,OAAA,GAAU;AAAA,OAClF;AAAA;AAAA,GACH;AAEJ,CAAC","file":"index.cjs","sourcesContent":["/* v8 ignore next */\nimport React from 'react';\nimport type { InputHTMLAttributes } from 'react';\nimport { useDataAttributes } from '@bento/use-data-attributes';\nimport { useProps } from '@bento/use-props';\nimport { withSlots } from '@bento/slots';\nimport { useFocusRing, useHover } from 'react-aria';\nimport { mergeProps } from '@react-aria/utils';\nimport type { HoverEvents } from 'react-aria';\n\n/**\n * Props for the Input component.\n */\nexport interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, keyof HoverEvents> {\n /** Whether the input should be focused on mount. */\n autoFocus?: boolean;\n}\n\n/**\n * A universal input primitive component that renders an `<input>` element with React Aria interactions.\n * Supports all HTML input types with proper accessibility, hover, and focus management.\n *\n * @component\n * @param {InputProps} props - The properties passed to the Input component.\n *\n * @example\n * ```tsx\n * // Text input\n * <Input type=\"text\" placeholder=\"Enter text\" />\n *\n * // Checkbox\n * <Input type=\"checkbox\" />\n *\n * // With render props\n * <Input\n * type=\"email\"\n * className={(state) => state.isFocusVisible ? 'focused' : ''}\n * />\n * ```\n *\n * @public\n */\nexport const Input = withSlots('BentoInput', function Input(...args: [InputProps, React.Ref<HTMLInputElement>?]) {\n const { props, apply } = useProps(args);\n const { autoFocus } = props;\n\n const { isFocused, isFocusVisible, focusProps } = useFocusRing({\n isTextInput: true,\n autoFocus: autoFocus\n });\n const { hoverProps, isHovered } = useHover(props);\n\n const mergedProps = mergeProps(props, focusProps, hoverProps);\n\n return (\n <input\n {...apply(mergedProps)}\n {...useDataAttributes({\n focused: isFocused,\n hovered: isHovered,\n focusVisible: isFocusVisible,\n disabled: props.disabled || false,\n invalid: !!props['aria-invalid'] && props['aria-invalid'] !== 'false',\n readonly: props.readOnly || false,\n required: props.required || false,\n empty: props.value === '' || props.value === undefined || props.value === null,\n checked: props.type === 'checkbox' || props.type === 'radio' ? !!props.checked : undefined\n })}\n />\n );\n}) as React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;\n"]}
@@ -0,0 +1,37 @@
1
+ import React, { InputHTMLAttributes } from 'react';
2
+ import { HoverEvents } from 'react-aria';
3
+
4
+ /**
5
+ * Props for the Input component.
6
+ */
7
+ interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, keyof HoverEvents> {
8
+ /** Whether the input should be focused on mount. */
9
+ autoFocus?: boolean;
10
+ }
11
+ /**
12
+ * A universal input primitive component that renders an `<input>` element with React Aria interactions.
13
+ * Supports all HTML input types with proper accessibility, hover, and focus management.
14
+ *
15
+ * @component
16
+ * @param {InputProps} props - The properties passed to the Input component.
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * // Text input
21
+ * <Input type="text" placeholder="Enter text" />
22
+ *
23
+ * // Checkbox
24
+ * <Input type="checkbox" />
25
+ *
26
+ * // With render props
27
+ * <Input
28
+ * type="email"
29
+ * className={(state) => state.isFocusVisible ? 'focused' : ''}
30
+ * />
31
+ * ```
32
+ *
33
+ * @public
34
+ */
35
+ declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
36
+
37
+ export { Input, type InputProps };
@@ -0,0 +1,37 @@
1
+ import React, { InputHTMLAttributes } from 'react';
2
+ import { HoverEvents } from 'react-aria';
3
+
4
+ /**
5
+ * Props for the Input component.
6
+ */
7
+ interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, keyof HoverEvents> {
8
+ /** Whether the input should be focused on mount. */
9
+ autoFocus?: boolean;
10
+ }
11
+ /**
12
+ * A universal input primitive component that renders an `<input>` element with React Aria interactions.
13
+ * Supports all HTML input types with proper accessibility, hover, and focus management.
14
+ *
15
+ * @component
16
+ * @param {InputProps} props - The properties passed to the Input component.
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * // Text input
21
+ * <Input type="text" placeholder="Enter text" />
22
+ *
23
+ * // Checkbox
24
+ * <Input type="checkbox" />
25
+ *
26
+ * // With render props
27
+ * <Input
28
+ * type="email"
29
+ * className={(state) => state.isFocusVisible ? 'focused' : ''}
30
+ * />
31
+ * ```
32
+ *
33
+ * @public
34
+ */
35
+ declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
36
+
37
+ export { Input, type InputProps };
package/dist/index.js ADDED
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import { useDataAttributes } from '@bento/use-data-attributes';
3
+ import { useProps } from '@bento/use-props';
4
+ import { withSlots } from '@bento/slots';
5
+ import { useFocusRing, useHover } from 'react-aria';
6
+ import { mergeProps } from '@react-aria/utils';
7
+
8
+ // src/index.tsx
9
+ var Input = withSlots("BentoInput", function Input2(...args) {
10
+ const { props, apply } = useProps(args);
11
+ const { autoFocus } = props;
12
+ const { isFocused, isFocusVisible, focusProps } = useFocusRing({
13
+ isTextInput: true,
14
+ autoFocus
15
+ });
16
+ const { hoverProps, isHovered } = useHover(props);
17
+ const mergedProps = mergeProps(props, focusProps, hoverProps);
18
+ return /* @__PURE__ */ React.createElement(
19
+ "input",
20
+ {
21
+ ...apply(mergedProps),
22
+ ...useDataAttributes({
23
+ focused: isFocused,
24
+ hovered: isHovered,
25
+ focusVisible: isFocusVisible,
26
+ disabled: props.disabled || false,
27
+ invalid: !!props["aria-invalid"] && props["aria-invalid"] !== "false",
28
+ readonly: props.readOnly || false,
29
+ required: props.required || false,
30
+ empty: props.value === "" || props.value === void 0 || props.value === null,
31
+ checked: props.type === "checkbox" || props.type === "radio" ? !!props.checked : void 0
32
+ })
33
+ }
34
+ );
35
+ });
36
+
37
+ export { Input };
38
+ //# sourceMappingURL=index.js.map
39
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.tsx"],"names":["Input"],"mappings":";;;;;;;;AA0CO,IAAM,KAAA,GAAQ,SAAA,CAAU,YAAA,EAAc,SAASA,UAAS,IAAA,EAAkD;AAC/G,EAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAM,GAAI,SAAS,IAAI,CAAA;AACtC,EAAA,MAAM,EAAE,WAAU,GAAI,KAAA;AAEtB,EAAA,MAAM,EAAE,SAAA,EAAW,cAAA,EAAgB,UAAA,KAAe,YAAA,CAAa;AAAA,IAC7D,WAAA,EAAa,IAAA;AAAA,IACb;AAAA,GACD,CAAA;AACD,EAAA,MAAM,EAAE,UAAA,EAAY,SAAA,EAAU,GAAI,SAAS,KAAK,CAAA;AAEhD,EAAA,MAAM,WAAA,GAAc,UAAA,CAAW,KAAA,EAAO,UAAA,EAAY,UAAU,CAAA;AAE5D,EAAA,uBACE,KAAA,CAAA,aAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACE,GAAG,MAAM,WAAW,CAAA;AAAA,MACpB,GAAG,iBAAA,CAAkB;AAAA,QACpB,OAAA,EAAS,SAAA;AAAA,QACT,OAAA,EAAS,SAAA;AAAA,QACT,YAAA,EAAc,cAAA;AAAA,QACd,QAAA,EAAU,MAAM,QAAA,IAAY,KAAA;AAAA,QAC5B,OAAA,EAAS,CAAC,CAAC,KAAA,CAAM,cAAc,CAAA,IAAK,KAAA,CAAM,cAAc,CAAA,KAAM,OAAA;AAAA,QAC9D,QAAA,EAAU,MAAM,QAAA,IAAY,KAAA;AAAA,QAC5B,QAAA,EAAU,MAAM,QAAA,IAAY,KAAA;AAAA,QAC5B,KAAA,EAAO,MAAM,KAAA,KAAU,EAAA,IAAM,MAAM,KAAA,KAAU,MAAA,IAAa,MAAM,KAAA,KAAU,IAAA;AAAA,QAC1E,OAAA,EAAS,KAAA,CAAM,IAAA,KAAS,UAAA,IAAc,KAAA,CAAM,SAAS,OAAA,GAAU,CAAC,CAAC,KAAA,CAAM,OAAA,GAAU;AAAA,OAClF;AAAA;AAAA,GACH;AAEJ,CAAC","file":"index.js","sourcesContent":["/* v8 ignore next */\nimport React from 'react';\nimport type { InputHTMLAttributes } from 'react';\nimport { useDataAttributes } from '@bento/use-data-attributes';\nimport { useProps } from '@bento/use-props';\nimport { withSlots } from '@bento/slots';\nimport { useFocusRing, useHover } from 'react-aria';\nimport { mergeProps } from '@react-aria/utils';\nimport type { HoverEvents } from 'react-aria';\n\n/**\n * Props for the Input component.\n */\nexport interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, keyof HoverEvents> {\n /** Whether the input should be focused on mount. */\n autoFocus?: boolean;\n}\n\n/**\n * A universal input primitive component that renders an `<input>` element with React Aria interactions.\n * Supports all HTML input types with proper accessibility, hover, and focus management.\n *\n * @component\n * @param {InputProps} props - The properties passed to the Input component.\n *\n * @example\n * ```tsx\n * // Text input\n * <Input type=\"text\" placeholder=\"Enter text\" />\n *\n * // Checkbox\n * <Input type=\"checkbox\" />\n *\n * // With render props\n * <Input\n * type=\"email\"\n * className={(state) => state.isFocusVisible ? 'focused' : ''}\n * />\n * ```\n *\n * @public\n */\nexport const Input = withSlots('BentoInput', function Input(...args: [InputProps, React.Ref<HTMLInputElement>?]) {\n const { props, apply } = useProps(args);\n const { autoFocus } = props;\n\n const { isFocused, isFocusVisible, focusProps } = useFocusRing({\n isTextInput: true,\n autoFocus: autoFocus\n });\n const { hoverProps, isHovered } = useHover(props);\n\n const mergedProps = mergeProps(props, focusProps, hoverProps);\n\n return (\n <input\n {...apply(mergedProps)}\n {...useDataAttributes({\n focused: isFocused,\n hovered: isHovered,\n focusVisible: isFocusVisible,\n disabled: props.disabled || false,\n invalid: !!props['aria-invalid'] && props['aria-invalid'] !== 'false',\n readonly: props.readOnly || false,\n required: props.required || false,\n empty: props.value === '' || props.value === undefined || props.value === null,\n checked: props.type === 'checkbox' || props.type === 'radio' ? !!props.checked : undefined\n })}\n />\n );\n}) as React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;\n"]}
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@bento/input",
3
+ "version": "0.1.0",
4
+ "description": "Universal input primitive component supporting all HTML input types",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "scripts": {
9
+ "build": "tsup-node",
10
+ "lint": "biome lint && tsc",
11
+ "posttest": "npm run lint",
12
+ "pretest": "npm run build",
13
+ "test": "vitest --run",
14
+ "test:watch": "vitest"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/godaddy/bento.git"
19
+ },
20
+ "keywords": [
21
+ "accessibility",
22
+ "aria",
23
+ "bento",
24
+ "component",
25
+ "input",
26
+ "library",
27
+ "react"
28
+ ],
29
+ "author": "GoDaddy Operating Company, LLC",
30
+ "license": "MIT",
31
+ "bugs": {
32
+ "url": "https://github.com/godaddy/bento/issues"
33
+ },
34
+ "homepage": "https://github.com/godaddy/bento#readme",
35
+ "files": [
36
+ "dist",
37
+ "src",
38
+ "package.json"
39
+ ],
40
+ "dependencies": {
41
+ "@bento/container": "^0.2.1",
42
+ "@bento/slots": "^0.3.0",
43
+ "@bento/text": "^0.1.4",
44
+ "@bento/use-data-attributes": "^0.1.1",
45
+ "@bento/use-props": "^0.2.1",
46
+ "@react-aria/utils": "^3.30.0",
47
+ "react-aria": "^3.44.0"
48
+ },
49
+ "peerDependencies": {
50
+ "react": "18.x || 19.x",
51
+ "react-dom": "18.x || 19.x"
52
+ },
53
+ "exports": {
54
+ ".": {
55
+ "import": {
56
+ "types": "./dist/index.d.ts",
57
+ "default": "./dist/index.js"
58
+ },
59
+ "require": {
60
+ "types": "./dist/index.d.cts",
61
+ "default": "./dist/index.cjs"
62
+ }
63
+ },
64
+ "./package.json": "./package.json"
65
+ },
66
+ "publishConfig": {
67
+ "access": "public",
68
+ "registry": "https://registry.npmjs.org/"
69
+ }
70
+ }
package/src/index.tsx ADDED
@@ -0,0 +1,71 @@
1
+ /* v8 ignore next */
2
+ import React from 'react';
3
+ import type { InputHTMLAttributes } from 'react';
4
+ import { useDataAttributes } from '@bento/use-data-attributes';
5
+ import { useProps } from '@bento/use-props';
6
+ import { withSlots } from '@bento/slots';
7
+ import { useFocusRing, useHover } from 'react-aria';
8
+ import { mergeProps } from '@react-aria/utils';
9
+ import type { HoverEvents } from 'react-aria';
10
+
11
+ /**
12
+ * Props for the Input component.
13
+ */
14
+ export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, keyof HoverEvents> {
15
+ /** Whether the input should be focused on mount. */
16
+ autoFocus?: boolean;
17
+ }
18
+
19
+ /**
20
+ * A universal input primitive component that renders an `<input>` element with React Aria interactions.
21
+ * Supports all HTML input types with proper accessibility, hover, and focus management.
22
+ *
23
+ * @component
24
+ * @param {InputProps} props - The properties passed to the Input component.
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * // Text input
29
+ * <Input type="text" placeholder="Enter text" />
30
+ *
31
+ * // Checkbox
32
+ * <Input type="checkbox" />
33
+ *
34
+ * // With render props
35
+ * <Input
36
+ * type="email"
37
+ * className={(state) => state.isFocusVisible ? 'focused' : ''}
38
+ * />
39
+ * ```
40
+ *
41
+ * @public
42
+ */
43
+ export const Input = withSlots('BentoInput', function Input(...args: [InputProps, React.Ref<HTMLInputElement>?]) {
44
+ const { props, apply } = useProps(args);
45
+ const { autoFocus } = props;
46
+
47
+ const { isFocused, isFocusVisible, focusProps } = useFocusRing({
48
+ isTextInput: true,
49
+ autoFocus: autoFocus
50
+ });
51
+ const { hoverProps, isHovered } = useHover(props);
52
+
53
+ const mergedProps = mergeProps(props, focusProps, hoverProps);
54
+
55
+ return (
56
+ <input
57
+ {...apply(mergedProps)}
58
+ {...useDataAttributes({
59
+ focused: isFocused,
60
+ hovered: isHovered,
61
+ focusVisible: isFocusVisible,
62
+ disabled: props.disabled || false,
63
+ invalid: !!props['aria-invalid'] && props['aria-invalid'] !== 'false',
64
+ readonly: props.readOnly || false,
65
+ required: props.required || false,
66
+ empty: props.value === '' || props.value === undefined || props.value === null,
67
+ checked: props.type === 'checkbox' || props.type === 'radio' ? !!props.checked : undefined
68
+ })}
69
+ />
70
+ );
71
+ }) as React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;