first commit
This commit is contained in:
163
docs/ui/react/react-ui/src/components/input-group/input-group.md
Normal file
163
docs/ui/react/react-ui/src/components/input-group/input-group.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# InputGroup
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - input-group.variants — `inputGroupVariants`, `input…
|
||||
- [input-group.variants](./input-group.variants.md) — `inputGroupVariants`, `inputGroupAddonVariants`, `inputGroupInputVariants`, `inputGroupTextareaVariants`, and `inputGro…
|
||||
- input-group.variants — `inputGroupVariants`, `input…
|
||||
|
||||
- [input-group.variants](./input-group.variants.md) — `inputGroupVariants`, `inputGroupAddonVariants`, `inputGroupInputVariants`, `inputGroupTextareaVariants`, and `inputGro…
|
||||
|
||||
- input-group.variants — `inputGroupVariants`, `input…
|
||||
|
||||
- [input-group.variants](./input-group.variants.md) — `inputGroupVariants`, `inputGroupAddonVariants`, `inputGroupInputVariants`, `inputGroupTextareaVariants`, and `inputGro…
|
||||
|
||||
- input-group.variants — `inputGroupVariants`, `input…
|
||||
|
||||
- [input-group.variants](./input-group.variants.md) — `inputGroupVariants`, `inputGroupAddonVariants`, `inputGroupInputVariants`, `inputGroupTextareaVariants`, and `inputGro…
|
||||
- [input-group.variants](./input-group.variants.md) — `inputGroupVariants`, `inputGroupAddonVariants`, `inputGroupInputVariants`, `inputGroupTextareaVariants`, and `inputGro…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<InputGroup>` is the chassis container that wraps an input alongside leading/trailing addons (icons, prefix/suffix text, inline buttons). It hoists the focus ring and invalid border onto the wrapper itself so the inner control can drop its own ring; the dedicated `<InputGroupInput>` and `<InputGroupTextarea>` primitives strip the standard border / shadow / focus ring so the wrapper carries them.
|
||||
|
||||
Per the shadcn skill rule, NEVER drop a raw `<Input>` or `<Textarea>` inside `<InputGroup>` — the inner control needs the wrapped surface variants to remove its own border and focus ring. The chassis provides `<InputGroupInput>` and `<InputGroupTextarea>` for the input case. Inline buttons inside inputs use `<InputGroupAddon position="none">` + `<InputGroupButton>` (renders an icon-button without a separator border).
|
||||
|
||||
## Features
|
||||
|
||||
- Wrapper container shares focus ring + invalid border with every child via `focus-within:` and `data-[invalid=true]:`.
|
||||
- `<InputGroupAddon>` with three positions (`start` / `end` / `none`) controlling which separator border is drawn.
|
||||
- `<InputGroupInput>` strips the standard `<Input>` border / shadow / focus ring.
|
||||
- `<InputGroupTextarea>` strips the standard `<Textarea>` border / shadow / focus ring.
|
||||
- `<InputGroupButton>` for inline icon-buttons (toggle password visibility, clear input, attach file).
|
||||
- `placeholder` on `<InputGroupInput>` / `<InputGroupTextarea>` typed as `TranslationKey | undefined` and resolved via active i18n.
|
||||
- `data-invalid="true"` and `data-disabled="true"` on the wrapper drive cva selectors.
|
||||
- Refs forwarded on every primitive.
|
||||
- `motion-reduce:transition-none` collapses transitions for `prefers-reduced-motion: reduce`.
|
||||
- Theme-driven variant overrides via `useThemedVariants("InputGroup" | "InputGroupAddon" | "InputGroupInput" | "InputGroupTextarea" | "InputGroupButton")`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
InputGroup,
|
||||
InputGroupAddon,
|
||||
InputGroupInput,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function EmailField() {
|
||||
return (
|
||||
<InputGroup>
|
||||
<InputGroupAddon>@</InputGroupAddon>
|
||||
<InputGroupInput type="email" />
|
||||
</InputGroup>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
InputGroup,
|
||||
InputGroupAddon,
|
||||
InputGroupButton,
|
||||
InputGroupInput,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { EyeIcon, EyeOffIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
export function PasswordWithReveal() {
|
||||
const [show, setShow] = useState(false);
|
||||
const [pwd, setPwd] = useState("");
|
||||
const tooShort = pwd.length > 0 && pwd.length < 8;
|
||||
|
||||
return (
|
||||
<InputGroup invalid={tooShort}>
|
||||
<InputGroupAddon>Pwd</InputGroupAddon>
|
||||
<InputGroupInput
|
||||
type={show ? "text" : "password"}
|
||||
value={pwd}
|
||||
aria-invalid={tooShort}
|
||||
onChange={(e) => setPwd(e.currentTarget.value)}
|
||||
/>
|
||||
<InputGroupAddon position="none">
|
||||
<InputGroupButton
|
||||
aria-label={show ? "Hide password" : "Show password"}
|
||||
onClick={() => setShow((s) => !s)}
|
||||
>
|
||||
{show ? <EyeOffIcon /> : <EyeIcon />}
|
||||
</InputGroupButton>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<InputGroup>`
|
||||
|
||||
`forwardRef<HTMLDivElement, InputGroupProps>` — renders a `<div>` wrapper.
|
||||
|
||||
#### `InputGroupProps`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `invalid` | `boolean` | `false` | Drives `data-invalid="true"` on the wrapper so border + focus ring flip to destructive. |
|
||||
| `disabled` | `boolean` | `false` | Drives `data-disabled="true"` for opacity decay. Pass to inner `<InputGroupInput>` separately for native semantics. |
|
||||
| _all native_ `HTMLAttributes<HTMLDivElement>` | _native_ | _native_ | Forwarded as-is. |
|
||||
|
||||
### `<InputGroupAddon>`
|
||||
|
||||
`forwardRef<HTMLDivElement, InputGroupAddonProps>` — renders a `<div>` for decorative content.
|
||||
|
||||
#### `InputGroupAddonProps`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `position` | `"start" \| "end" \| "none"` | `"start"` | Which side the addon hugs and which separator border is drawn (`none` = no separator, used for inline buttons). |
|
||||
|
||||
### `<InputGroupInput>`
|
||||
|
||||
`forwardRef<HTMLInputElement, InputGroupInputProps>` — renders an `<input>` with the standard border / shadow / focus ring stripped.
|
||||
|
||||
#### `InputGroupInputProps`
|
||||
|
||||
Inherits every prop from `InputHTMLAttributes<HTMLInputElement>` minus `size` / `placeholder`. Adds:
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `placeholder` | `TranslationKey` | _none_ | Resolved via active i18n. |
|
||||
|
||||
### `<InputGroupTextarea>`
|
||||
|
||||
`forwardRef<HTMLTextAreaElement, InputGroupTextareaProps>` — renders a `<textarea>` with the standard border / shadow / focus ring stripped.
|
||||
|
||||
#### `InputGroupTextareaProps`
|
||||
|
||||
Inherits every prop from `TextareaHTMLAttributes<HTMLTextAreaElement>` minus `placeholder`. Adds:
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `placeholder` | `TranslationKey` | _none_ | Resolved via active i18n. |
|
||||
|
||||
### `<InputGroupButton>`
|
||||
|
||||
`forwardRef<HTMLButtonElement, InputGroupButtonProps>` — renders a small icon-button with `type="button"` by default so it does not accidentally submit the surrounding form.
|
||||
|
||||
#### `InputGroupButtonProps`
|
||||
|
||||
Inherits every prop from `ButtonHTMLAttributes<HTMLButtonElement>`. The default `type="button"` can be overridden if intentional.
|
||||
|
||||
#### Cva resolvers
|
||||
|
||||
`inputGroupVariants`, `inputGroupAddonVariants`, `inputGroupInputVariants`, `inputGroupTextareaVariants`, and `inputGroupButtonVariants` are all exported as bare cva resolvers for external composition.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Per the shadcn skill rule, the chassis enforces the `<InputGroupInput>` / `<InputGroupTextarea>` wrappers — raw `<Input>` / `<Textarea>` inside `<InputGroup>` carries an inner border + focus ring that double up with the wrapper's own.
|
||||
- The `position="none"` value on `<InputGroupAddon>` is the supported way to render an inline button without a separator border. The button supplies its own padding so the addon does not need one.
|
||||
- `<InputGroupButton>` defaults to `type="button"` so a button rendered inside an input group does not submit the surrounding form when clicked. Override with `type="submit"` only when intentional.
|
||||
- The wrapper carries the focus ring via `focus-within:` so any inner element gaining focus surfaces the ring on the wrapper. The inner `<InputGroupInput>` removes its own ring via `focus:ring-0` to avoid the visual double-ring.
|
||||
- Refs target the underlying DOM element on every primitive — consumers can `.focus()` the input, button, addon, or wrapper programmatically.
|
||||
Reference in New Issue
Block a user