@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 +9 -0
- package/README.mdx +148 -0
- package/dist/index.cjs +45 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +37 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/package.json +70 -0
- package/src/index.tsx +71 -0
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"]}
|
package/dist/index.d.cts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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>>;
|