UNPKG

consensys-ui

Version:

Consensys UI component library and design system

399 lines (299 loc) 10.9 kB
# Input Component The `<Input />` and `<Textarea />` components provide cross-platform text input fields that adapt to both web and React Native environments while providing platform-native APIs for each. ## Installation ```bash pnpm add @consensys/ui ``` ## Usage Examples Both components support web and native APIs through our [Dual API](#6--dual-api) and offer flexibility through our [Compound Components](#2--compound-components). ### Web-Specific Usage ```tsx import { Input, Textarea } from '@consensys/ui'; import type { WebChangeEvent } from '@consensys/ui'; function WebInputs() { // Using web-specific APIs return ( <> <Input type="email" onChange={(e: WebChangeEvent) => console.log('Changed!', e.target.value)} placeholder="Email address" /> <Textarea onChange={(e: WebChangeEvent) => console.log('Changed!', e.target.value)} placeholder="Enter description" rows={5} /> </> ); } ``` ### Native/Hybrid Usage ```tsx import { Input, Textarea } from '@consensys/ui'; function NativeInputs() { // Using React Native APIs return ( <> <Input onChangeText={(text) => console.log('Changed!', text)} keyboardType="email-address" placeholder="Email address" /> <Textarea onChangeText={(text) => console.log('Changed!', text)} placeholder="Enter description" numberOfLines={5} /> </> ); } ``` > **Note**: Choose one API style and stick with it throughout your codebase. See the [Guiding Philosophy](../../../README.md#guiding-philosophy) for more details. ## Component API ### Simple Usage ```tsx // Single-line input <Input variant="outline" color="primary" placeholder="Enter text" /> // Multi-line input <Textarea variant="outline" color="primary" placeholder="Enter text" numberOfLines={4} /> ``` ### Compound Usage ```tsx // Input with icon and text <Input variant="soft" color="primary" className="rounded-lg"> <Input.Icon icon={Mail} /> <Input.Field /> <Input.Text>*</Input.Text> <Input.Spinner /> </Input> // Textarea with icon <Textarea variant="soft" color="primary" className="rounded-lg"> <Input.Icon icon={DocumentText} /> <Input.Field /> <Input.Spinner /> </Textarea> ``` ### `<Input />` and `<Textarea />` The main components with various styling options. #### Props Both Input and Textarea share the following props: | Prop | Type | Default | Description | |------|------|---------|-------------| | `variant` | `'soft' \| 'outline' \| 'underline' \| 'ghost'` | `'outline'` | The visual style of the input | | `color` | `'neutral' \| 'primary' \| 'secondary' \| 'error' \| 'warning' \| 'success'` | `'neutral'` | The color scheme of the input | | `accentColor` | Same as `color` | - | Color to switch to on focus/hover | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of the input | | `disabled` | `boolean` | `false` | Whether the input is disabled | | `loading` | `boolean` | `false` | Whether the input is in loading state | | `readOnly` | `boolean` | `false` | Whether the input is read-only | | `asChild` | `boolean` | `false` | Whether to replace the input with a different component | | `className` | `string` | - | Additional class names | Textarea-specific props: | Prop | Type | Default | Description | |------|------|---------|-------------| | `numberOfLines` | `number` | `4` | Number of lines to display (approximate height) | | `rows` | `number` | - | **(Web only)** Number of visible text lines | Input-specific props: | Prop | Type | Default | Description | |------|------|---------|-------------| | `multiline` | `boolean` | `false` | Whether the input should support multiple lines (consider using Textarea instead) | Plus additional props based on platform: - Web: All [`HTMLInputElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement) props ([`onChange`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event), [`type`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types), etc.) - Native: All [`TextInput`](https://reactnative.dev/docs/textinput) props ([`onChangeText`](https://reactnative.dev/docs/textinput#onchangetext), [`keyboardType`](https://reactnative.dev/docs/textinput#keyboardtype), etc.) ### `<Input.Field />` Child component for the actual input field. Allows you to construct inputs with other parts. ```tsx <Input variant="outline" color="primary" placeholder="Enter text"> <Input.Field /> </Input> ``` #### Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `className` | `string` | - | Additional class names for the input field | ### `<Input.Text />` Used to add text elements before or after the input field. ```tsx // Adding a prefix <Input variant="outline" color="primary"> <Input.Text>$</Input.Text> <Input.Field /> </Input> // Adding a suffix <Input variant="outline" color="primary"> <Input.Field /> <Input.Text>.00</Input.Text> </Input> ``` #### Props For props and styling options, see the [Text Component API](/packages/ui/src/components/text). ### `<Input.Icon />` An icon for the input. Can be placed before and/or after the field. ```tsx import { Search, Eye } from 'lucide-react-native'; // Simple usage with search icon <Input variant="outline" color="primary"> <Input.Icon icon={Search} /> <Input.Field /> </Input> // Password input with toggleable visibility <Input variant="outline" color="primary" secureTextEntry> <Input.Field /> <Input.Icon icon={Eye} /> </Input> ``` #### Props For props and styling options, see the [Icon Component API](/packages/ui/src/components/icon). ### `<Input.Spinner />` A loading spinner that automatically appears when the input's `loading` prop is true. ```tsx import { Search, Loader } from 'lucide-react-native'; // Simple usage <Input variant="outline" color="primary" loading> <Input.Field placeholder="Loading..." /> </Input> // Custom loading icon <Input variant="soft" color="primary" loading> <Input.Spinner loadingIcon={Loader} /> <Input.Field placeholder="Loading..." /> </Input> // With fallback icon <Input variant="outline" color="primary" loading> <Input.Spinner icon={Search} /> <Input.Field placeholder="Loading..." /> </Input> ``` #### Props For props and styling options, see the [Spinner Component API](/packages/ui/src/components/spinner). ## Styling Options ### Variants ```tsx <Input variant="soft">Soft</Input> <Input variant="outline">Outline</Input> <Input variant="underline">Underline</Input> <Input variant="ghost">Ghost</Input> ``` ### Colors ```tsx <Input color="neutral">Neutral</Input> <Input color="primary">Primary</Input> <Input color="secondary">Secondary</Input> <Input color="error">Error</Input> <Input color="warning">Warning</Input> <Input color="success">Success</Input> ``` ### Sizes ```tsx <Input size="sm">Small</Input> <Input size="md">Medium</Input> <Input size="lg">Large</Input> ``` ### Accent Colors Change the color on focus/hover: ```tsx // Web example with focus effect <Input variant="soft" color="primary" accentColor="secondary" > <Input.Field placeholder="Focus me" /> </Input> // React Native example <Input variant="soft" color="primary" accentColor="secondary" > <Input.Field placeholder="Tap me" /> </Input> ``` ### Disabled State ```tsx // Web example <Input disabled> <Input.Field placeholder="Disabled Input" /> </Input> // React Native example <Input disabled> <Input.Field placeholder="Disabled Input" /> </Input> ``` ### Read-Only State ```tsx <Input readOnly value="Read-only value"> <Input.Field /> </Input> <Textarea readOnly value="Read-only multiline text"> <Input.Field /> </Textarea> ``` ### Loading State ```tsx <Input loading> <Input.Field placeholder="Loading..." /> </Input> <Input loading> <Input.Spinner /> <Input.Field placeholder="Loading..." /> </Input> ``` ### Events The component supports platform-specific event handling: ```tsx import type { WebChangeEvent, WebFocusEvent, NativeFocusEvent } from '@consensys/ui'; // Web events <Input onChange={(e: WebChangeEvent) => console.log('Changed', e.target.value)} onFocus={(e: WebFocusEvent) => console.log('Focused', e)} onBlur={(e: WebFocusEvent) => console.log('Blurred', e)} > <Input.Field placeholder="Web Events" /> </Input> // Native events <Input onChangeText={(text: string) => console.log('Changed', text)} onFocus={(e: NativeFocusEvent) => console.log('Focused', e)} onBlur={(e: NativeFocusEvent) => console.log('Blurred', e)} > <Input.Field placeholder="Native Events" /> </Input> ``` The component exports typed event interfaces that match their platform equivalents: - Web: [`HTMLInputElement` events](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement) like `onChange`, `onFocus`, `onBlur` - Native: [`TextInput` events](https://reactnative.dev/docs/textinput) like `onChangeText`, `onChange`, `onFocus`, `onBlur` For details on typed events and the dual-API approach, see the [Dual API](#6--dual-api). ### Using `asChild` for Custom Elements The Input component supports the [Slot Pattern](../../../README.md#the-slot-pattern-aschild) via the `asChild` prop, allowing you to replace the input with a custom component: ```tsx // Custom input implementation <Input variant="outline" color="primary" asChild> <CustomInput placeholder="Custom input component" /> </Input> ``` ## Prop Mapping Input implements automatic prop mapping to support cross-platform development while maintaining platform-specific behavior: | Native Prop | Web Prop | Description | |-------------|----------|-------------| | `onChangeText` | `onChange` | Triggered when input value changes | | `secureTextEntry` | `type="password"` | For password fields | | `numberOfLines` | `rows` | For multiline inputs/textareas | | `autoCorrect` | `autoCorrect` | Spelling autocorrection | > While native props can be used in web environments (automatically converted), web props should never be used in native environments. For more details on our cross-platform approach, see the [Dual API](#6--dual-api). ## Accessibility The Input component automatically implements proper accessibility attributes for each platform: ### Accessibility Prop Mappings | Property | Web Implementation | Native Implementation | |----------|-------------------|----------------------| | Role | `role="input"` or `role="textbox"` | `accessibilityRole="textbox"` or `"adjustable"` | | Disabled | `aria-disabled={disabled}` | `accessibilityState={{ disabled }}` | | Loading | `aria-busy={loading}` | `accessibilityState={{ busy: loading }}` | | ReadOnly | `aria-readonly={readOnly}` | `accessibilityState={{ readonly: readOnly }}` | | Multiline | `aria-multiline={multiline}` | Sets `accessibilityRole="adjustable"` |