@g4rcez/components 3.0.0 → 3.0.1
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/ai/SKILL.md +266 -0
- package/dist/ai/docs/Alert.md +167 -0
- package/dist/ai/docs/AnimatedList.md +205 -0
- package/dist/ai/docs/Autocomplete.md +225 -0
- package/dist/ai/docs/Button.md +182 -0
- package/dist/ai/docs/Calendar.md +219 -0
- package/dist/ai/docs/Card.md +174 -0
- package/dist/ai/docs/Checkbox.md +199 -0
- package/dist/ai/docs/CommandPalette.md +293 -0
- package/dist/ai/docs/DatePicker.md +171 -0
- package/dist/ai/docs/Dropdown.md +223 -0
- package/dist/ai/docs/Empty.md +163 -0
- package/dist/ai/docs/Expand.md +143 -0
- package/dist/ai/docs/FileUpload.md +225 -0
- package/dist/ai/docs/Form.md +107 -0
- package/dist/ai/docs/FormReset.md +117 -0
- package/dist/ai/docs/Heading.md +88 -0
- package/dist/ai/docs/Input.md +237 -0
- package/dist/ai/docs/InputField.md +170 -0
- package/dist/ai/docs/List.md +205 -0
- package/dist/ai/docs/Menu.md +166 -0
- package/dist/ai/docs/Modal.md +280 -0
- package/dist/ai/docs/MultiSelect.md +196 -0
- package/dist/ai/docs/Notifications.md +231 -0
- package/dist/ai/docs/PageCalendar.md +271 -0
- package/dist/ai/docs/Polymorph.md +159 -0
- package/dist/ai/docs/Progress.md +145 -0
- package/dist/ai/docs/Radiobox.md +128 -0
- package/dist/ai/docs/RenderOnView.md +138 -0
- package/dist/ai/docs/Resizable.md +159 -0
- package/dist/ai/docs/Select.md +284 -0
- package/dist/ai/docs/Shortcut.md +105 -0
- package/dist/ai/docs/Skeleton.md +166 -0
- package/dist/ai/docs/Slider.md +144 -0
- package/dist/ai/docs/Slot.md +173 -0
- package/dist/ai/docs/Spinner.md +118 -0
- package/dist/ai/docs/Stats.md +137 -0
- package/dist/ai/docs/Step.md +159 -0
- package/dist/ai/docs/Switch.md +167 -0
- package/dist/ai/docs/Table.md +298 -0
- package/dist/ai/docs/Tabs.md +191 -0
- package/dist/ai/docs/Tag.md +224 -0
- package/dist/ai/docs/TaskList.md +144 -0
- package/dist/ai/docs/Textarea.md +167 -0
- package/dist/ai/docs/Timeline.md +210 -0
- package/dist/ai/docs/Toolbar.md +132 -0
- package/dist/ai/docs/Tooltip.md +231 -0
- package/dist/ai/docs/TransferList.md +142 -0
- package/dist/ai/docs/Typography.md +187 -0
- package/dist/ai/docs/Wizard.md +213 -0
- package/dist/ai/docs/index.md +183 -0
- package/dist/components/core/tag.d.ts +1 -1
- package/dist/components/core/tag.d.ts.map +1 -1
- package/dist/components/display/list.d.ts.map +1 -1
- package/dist/components/floating/dropdown.d.ts +1 -0
- package/dist/components/floating/dropdown.d.ts.map +1 -1
- package/dist/components/floating/menu.d.ts.map +1 -1
- package/dist/config/default-translations.d.ts +4 -4
- package/dist/hooks/use-translations.d.ts +4 -4
- package/dist/hooks/use-translations.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.js +28 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2463 -2458
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +12 -12
- package/dist/index.umd.js.map +1 -1
- package/package.json +4 -4
- package/dist/components/core/button.jsx +0 -79
- package/dist/components/core/heading.jsx +0 -4
- package/dist/components/core/polymorph.jsx +0 -5
- package/dist/components/core/render-on-view.jsx +0 -31
- package/dist/components/core/resizable.jsx +0 -51
- package/dist/components/core/slot.jsx +0 -156
- package/dist/components/core/tag.jsx +0 -51
- package/dist/components/core/typography.jsx +0 -22
- package/dist/components/display/alert.jsx +0 -58
- package/dist/components/display/calendar.jsx +0 -299
- package/dist/components/display/card.jsx +0 -43
- package/dist/components/display/empty.jsx +0 -11
- package/dist/components/display/list.jsx +0 -81
- package/dist/components/display/notifications.jsx +0 -126
- package/dist/components/display/progress.jsx +0 -11
- package/dist/components/display/shortcut.jsx +0 -23
- package/dist/components/display/skeleton.jsx +0 -12
- package/dist/components/display/spinner.jsx +0 -7
- package/dist/components/display/stats.jsx +0 -20
- package/dist/components/display/step.jsx +0 -131
- package/dist/components/display/tabs.jsx +0 -98
- package/dist/components/display/timeline.jsx +0 -25
- package/dist/components/floating/command-palette.jsx +0 -194
- package/dist/components/floating/dropdown.jsx +0 -53
- package/dist/components/floating/expand.jsx +0 -44
- package/dist/components/floating/menu.jsx +0 -147
- package/dist/components/floating/modal.jsx +0 -299
- package/dist/components/floating/toolbar.jsx +0 -5
- package/dist/components/floating/tooltip.jsx +0 -58
- package/dist/components/floating/wizard.jsx +0 -161
- package/dist/components/form/autocomplete.jsx +0 -279
- package/dist/components/form/checkbox.jsx +0 -12
- package/dist/components/form/date-picker.jsx +0 -115
- package/dist/components/form/file-upload.jsx +0 -133
- package/dist/components/form/form.jsx +0 -10
- package/dist/components/form/formReset.jsx +0 -17
- package/dist/components/form/free-text.jsx +0 -41
- package/dist/components/form/input-field.jsx +0 -56
- package/dist/components/form/input.jsx +0 -36
- package/dist/components/form/multi-select.jsx +0 -328
- package/dist/components/form/radiobox.jsx +0 -6
- package/dist/components/form/select.jsx +0 -42
- package/dist/components/form/slider.jsx +0 -45
- package/dist/components/form/switch.jsx +0 -46
- package/dist/components/form/task-list.jsx +0 -26
- package/dist/components/form/textarea.jsx +0 -12
- package/dist/components/form/transfer-list.jsx +0 -39
- package/dist/components/index.js +0 -45
- package/dist/components/page-calendar/calendar-header.jsx +0 -81
- package/dist/components/page-calendar/day-view.jsx +0 -87
- package/dist/components/page-calendar/event-pill.jsx +0 -25
- package/dist/components/page-calendar/index.js +0 -2
- package/dist/components/page-calendar/month-view.jsx +0 -47
- package/dist/components/page-calendar/page-calendar.jsx +0 -41
- package/dist/components/page-calendar/page-calendar.types.js +0 -1
- package/dist/components/page-calendar/page-calendar.utils.js +0 -71
- package/dist/components/page-calendar/week-view.jsx +0 -64
- package/dist/components/table/filter.jsx +0 -141
- package/dist/components/table/group.jsx +0 -68
- package/dist/components/table/index.jsx +0 -60
- package/dist/components/table/inner-table.jsx +0 -104
- package/dist/components/table/metadata.jsx +0 -36
- package/dist/components/table/pagination.jsx +0 -73
- package/dist/components/table/row.jsx +0 -58
- package/dist/components/table/sort.jsx +0 -105
- package/dist/components/table/table-lib.js +0 -83
- package/dist/components/table/table.context.jsx +0 -4
- package/dist/components/table/thead.jsx +0 -103
- package/dist/config/context.js +0 -12
- package/dist/config/default-translations.jsx +0 -83
- package/dist/config/default-tweaks.js +0 -4
- package/dist/constants.js +0 -2
- package/dist/hooks/use-click-outside.js +0 -17
- package/dist/hooks/use-color-parser.js +0 -9
- package/dist/hooks/use-components-provider.jsx +0 -19
- package/dist/hooks/use-debounce.js +0 -12
- package/dist/hooks/use-floating-ref.js +0 -6
- package/dist/hooks/use-form.js +0 -550
- package/dist/hooks/use-hover.js +0 -18
- package/dist/hooks/use-input-id.js +0 -5
- package/dist/hooks/use-is-coarse-device.js +0 -12
- package/dist/hooks/use-locale.js +0 -10
- package/dist/hooks/use-media-query.js +0 -25
- package/dist/hooks/use-on-event.js +0 -7
- package/dist/hooks/use-parent.js +0 -21
- package/dist/hooks/use-preferences.js +0 -23
- package/dist/hooks/use-previous.js +0 -9
- package/dist/hooks/use-reactive.js +0 -9
- package/dist/hooks/use-remove-scroll.js +0 -61
- package/dist/hooks/use-resize-observer.js +0 -17
- package/dist/hooks/use-stable-ref.js +0 -9
- package/dist/hooks/use-swipe.js +0 -17
- package/dist/hooks/use-translations.js +0 -9
- package/dist/hooks/use-tweaks.js +0 -9
- package/dist/hooks/use-window-size.js +0 -14
- package/dist/lib/combi-keys.js +0 -60
- package/dist/lib/dict.js +0 -39
- package/dist/lib/dom.js +0 -62
- package/dist/lib/fns.js +0 -46
- package/dist/lib/fzf.js +0 -117
- package/dist/lib/keyboard-area.js +0 -14
- package/dist/styles/common.js +0 -29
- package/dist/styles/dark.js +0 -214
- package/dist/styles/design-tokens.js +0 -69
- package/dist/styles/light.js +0 -214
- package/dist/styles/theme.js +0 -4
- package/dist/styles/theme.types.js +0 -1
- package/dist/types.js +0 -1
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Input
|
|
3
|
+
description: Text input with optional masking for currencies, percentages, phone numbers, dates, and custom regex patterns.
|
|
4
|
+
package: "@g4rcez/components"
|
|
5
|
+
export: "{ Input }"
|
|
6
|
+
import: "import { Input } from '@g4rcez/components/input'"
|
|
7
|
+
category: form
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Input
|
|
11
|
+
|
|
12
|
+
Text input with optional masking for currencies, percentages, phone numbers, dates, and custom regex patterns.
|
|
13
|
+
|
|
14
|
+
## Import
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { Input } from "@g4rcez/components/input";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Props
|
|
21
|
+
|
|
22
|
+
`Input` extends all standard HTML `<input>` attributes plus `InputField` layout props:
|
|
23
|
+
|
|
24
|
+
| Prop | Type | Default | Description |
|
|
25
|
+
|------|------|---------|-------------|
|
|
26
|
+
| `mask` | `AllMasks \| Array<string \| RegExp> \| ((value: string) => AllMasks)` | - | Input mask pattern |
|
|
27
|
+
| `locale` | `Locales` | - | Locale for currency/number formatting |
|
|
28
|
+
| `currency` | `CurrencyCode` | - | Currency code when using `mask="currency"` |
|
|
29
|
+
| `error` | `string` | - | Error message shown below the field |
|
|
30
|
+
| `title` | `string` | - | Field label |
|
|
31
|
+
| `feedback` | `Label` | - | Success or neutral feedback text below the field |
|
|
32
|
+
| `left` | `Label` | - | Content rendered on the left inside the field border |
|
|
33
|
+
| `right` | `Label` | - | Content rendered on the right inside the field border |
|
|
34
|
+
| `required` | `boolean` | `false` | Marks field as required; hides "Optional" label text |
|
|
35
|
+
| `disabled` | `boolean` | `false` | Disabled state |
|
|
36
|
+
| `loading` | `boolean` | `false` | Loading state |
|
|
37
|
+
| `container` | `string` | - | Extra CSS classes for the outer `fieldset` |
|
|
38
|
+
| `labelClassName` | `string` | - | Extra CSS classes for the label/border wrapper |
|
|
39
|
+
| `next` | `string` | - | `id` of the next field to focus when Enter is pressed with `enterKeyHint="next"` |
|
|
40
|
+
| `hiddenLabel` | `boolean` | `false` | Visually hides the label but keeps it for screen readers |
|
|
41
|
+
| `...props` | `React.InputHTMLAttributes<HTMLInputElement>` | - | All standard input attributes |
|
|
42
|
+
|
|
43
|
+
### Mask patterns
|
|
44
|
+
|
|
45
|
+
| Pattern char | Matches |
|
|
46
|
+
|---|---|
|
|
47
|
+
| `9` | Digit (0–9) |
|
|
48
|
+
| `A` | Letter (a–z, A–Z) |
|
|
49
|
+
| `S` | Alphanumeric |
|
|
50
|
+
| `*` | Any character |
|
|
51
|
+
|
|
52
|
+
### Special mask strings
|
|
53
|
+
|
|
54
|
+
| Value | Description |
|
|
55
|
+
|---|---|
|
|
56
|
+
| `"currency"` | Currency formatting (requires `locale` and `currency`) |
|
|
57
|
+
| `"percentage"` | Percentage input with `%` symbol |
|
|
58
|
+
| `"decimal"` | Decimal number formatting |
|
|
59
|
+
|
|
60
|
+
## Design Tokens
|
|
61
|
+
|
|
62
|
+
Tokens this component reads. Customize by overriding these CSS variables in your theme.
|
|
63
|
+
|
|
64
|
+
| Token | CSS Variable | Purpose |
|
|
65
|
+
|-------|-------------|---------|
|
|
66
|
+
| `h-input-height` | `--input-height` | Input height |
|
|
67
|
+
| `px-input-x` | `--input-x` | Horizontal padding |
|
|
68
|
+
| `py-input-y` | `--input-y` | Vertical padding |
|
|
69
|
+
| `mt-input-gap` | `--input-gap` | Gap between field border and error/feedback text |
|
|
70
|
+
| `border-input-border` | `--input-border` | Default border color |
|
|
71
|
+
| `text-field-label` | `--field-label` | Label text color |
|
|
72
|
+
| `text-foreground` | `--foreground` | Input text color |
|
|
73
|
+
| `text-primary` | `--primary` | Focus ring, focus/hover border color |
|
|
74
|
+
| `text-danger` | `--danger` | Error state border, text, and label color |
|
|
75
|
+
| `text-disabled` | `--disabled` | Disabled text and border color |
|
|
76
|
+
| `placeholder-input-mask` | `--input-mask` | Placeholder text color |
|
|
77
|
+
| `placeholder-input-mask-error` | `--input-mask-error` | Placeholder color in error state |
|
|
78
|
+
|
|
79
|
+
## Examples
|
|
80
|
+
|
|
81
|
+
### Basic text input
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
import { Input } from "@g4rcez/components/input";
|
|
85
|
+
|
|
86
|
+
<Input name="name" title="Full name" placeholder="Jane Smith" required />
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Phone number mask
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
<Input
|
|
93
|
+
name="phone"
|
|
94
|
+
title="Phone"
|
|
95
|
+
mask="(99) 99999-9999"
|
|
96
|
+
placeholder="(00) 00000-0000"
|
|
97
|
+
/>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Currency input
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
<Input
|
|
104
|
+
name="price"
|
|
105
|
+
title="Price"
|
|
106
|
+
mask="currency"
|
|
107
|
+
currency="USD"
|
|
108
|
+
locale="en-US"
|
|
109
|
+
placeholder="0.00"
|
|
110
|
+
/>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Dynamic mask (CPF / CNPJ)
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
const docMask = (value: string) =>
|
|
117
|
+
value.replace(/\D/g, "").length <= 11
|
|
118
|
+
? "999.999.999-99"
|
|
119
|
+
: "99.999.999/9999-99";
|
|
120
|
+
|
|
121
|
+
<Input
|
|
122
|
+
name="document"
|
|
123
|
+
title="CPF or CNPJ"
|
|
124
|
+
mask={docMask}
|
|
125
|
+
placeholder="000.000.000-00"
|
|
126
|
+
/>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Input with inline left/right slots
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
import { SearchIcon } from "lucide-react";
|
|
133
|
+
|
|
134
|
+
<Input
|
|
135
|
+
name="search"
|
|
136
|
+
title="Search"
|
|
137
|
+
left={<SearchIcon size={16} className="text-muted-foreground" />}
|
|
138
|
+
placeholder="Type to search..."
|
|
139
|
+
/>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Input with error and feedback
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
const [email, setEmail] = useState("");
|
|
146
|
+
const error = email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
|
|
147
|
+
? "Enter a valid email address"
|
|
148
|
+
: undefined;
|
|
149
|
+
|
|
150
|
+
<Input
|
|
151
|
+
name="email"
|
|
152
|
+
type="email"
|
|
153
|
+
title="Email"
|
|
154
|
+
value={email}
|
|
155
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
156
|
+
error={error}
|
|
157
|
+
feedback={!error && email ? "Looks good!" : undefined}
|
|
158
|
+
/>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Enter-to-advance between fields
|
|
162
|
+
|
|
163
|
+
```tsx
|
|
164
|
+
<Input
|
|
165
|
+
name="first"
|
|
166
|
+
title="First name"
|
|
167
|
+
enterKeyHint="next"
|
|
168
|
+
next="last"
|
|
169
|
+
/>
|
|
170
|
+
<Input
|
|
171
|
+
name="last"
|
|
172
|
+
id="last"
|
|
173
|
+
title="Last name"
|
|
174
|
+
enterKeyHint="done"
|
|
175
|
+
/>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Inside a form
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
import { Form } from "@g4rcez/components/form";
|
|
182
|
+
import { Button } from "@g4rcez/components/button";
|
|
183
|
+
|
|
184
|
+
function SignUpForm() {
|
|
185
|
+
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
186
|
+
const data = new FormData(e.currentTarget);
|
|
187
|
+
console.log(Object.fromEntries(data));
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
<Form onSubmit={handleSubmit} className="flex flex-col gap-base max-w-md">
|
|
192
|
+
<Input name="name" title="Full name" required />
|
|
193
|
+
<Input name="email" type="email" title="Email" required />
|
|
194
|
+
<Input name="phone" title="Phone" mask="(99) 99999-9999" />
|
|
195
|
+
<Button theme="primary" type="submit">Sign up</Button>
|
|
196
|
+
</Form>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Do
|
|
202
|
+
|
|
203
|
+
- Use `mask` patterns that match user expectations for the data type.
|
|
204
|
+
- Provide a `placeholder` that shows the expected format (e.g., `"(00) 00000-0000"`).
|
|
205
|
+
- Use the `error` prop to surface validation messages from your form library or manual validation.
|
|
206
|
+
- Use the `next` prop with `enterKeyHint="next"` to create smooth keyboard flows on mobile.
|
|
207
|
+
- Use design-token classes for wrapper elements (`bg-background`, `text-foreground`, `border-border`).
|
|
208
|
+
|
|
209
|
+
## Don't
|
|
210
|
+
|
|
211
|
+
- Don't pass raw Tailwind color classes (`bg-blue-500`, `text-white`, `border-gray-300`) — use theme props or design tokens instead.
|
|
212
|
+
- Don't use arbitrary Tailwind values (`bg-[#abc]`, `bg-[--my-var]`) — override CSS variables in your `@theme` block instead.
|
|
213
|
+
- Don't use a mask that blocks valid input variations — prefer dynamic masks for formats with variable length.
|
|
214
|
+
- Don't forget that the mask formats the display value; if your backend expects raw data, strip formatting before sending.
|
|
215
|
+
- Don't use `Input` for long-form text — use `Textarea` instead.
|
|
216
|
+
|
|
217
|
+
## Accessibility
|
|
218
|
+
|
|
219
|
+
- Renders a semantic `<input>` wrapped in a `<fieldset>` with an associated `<label>` (via `InputField`).
|
|
220
|
+
- `aria-disabled`, `aria-readonly`, and `aria-busy` are set automatically from props.
|
|
221
|
+
- Error messages appear as a visible `<p>` below the field after the user has interacted with it (`data-initialized="true"`).
|
|
222
|
+
- Focus ring uses `focus:ring-primary` for consistent, visible keyboard indication.
|
|
223
|
+
- The `hiddenLabel` prop keeps the label in the accessibility tree while hiding it visually.
|
|
224
|
+
|
|
225
|
+
## Data Attributes
|
|
226
|
+
|
|
227
|
+
- `data-initialized` — managed by `initializeInputDataset`; switches from `"false"` to `"true"` after first user interaction, enabling validation display.
|
|
228
|
+
- `data-next` — set from the `next` prop; used to focus the next field on Enter.
|
|
229
|
+
- `data-component="input"` — set on the outer `fieldset` by `InputField`.
|
|
230
|
+
- `data-error` — on the `fieldset`: `"true"` when an `error` string is present.
|
|
231
|
+
|
|
232
|
+
## Notes
|
|
233
|
+
|
|
234
|
+
- Built on top of `the-mask-input`. All mask features from that library are available.
|
|
235
|
+
- The component automatically manages cursor positioning during masked input.
|
|
236
|
+
- Works with both controlled (`value` + `onChange`) and uncontrolled (`defaultValue`) patterns.
|
|
237
|
+
- `Input` is created via the `createFreeText` factory, which also powers `Textarea`. Both share the same styling tokens.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: InputField
|
|
3
|
+
description: Low-level field wrapper that provides a label, tooltip, error message, feedback text, and left/right slots for any form control.
|
|
4
|
+
package: "@g4rcez/components"
|
|
5
|
+
export: "{ InputField }"
|
|
6
|
+
import: "import { InputField } from '@g4rcez/components'"
|
|
7
|
+
category: form
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# InputField
|
|
11
|
+
|
|
12
|
+
Low-level field wrapper that provides a label, tooltip, error message, feedback text, and left/right slots for any form control. Most higher-level components (`Input`, `Select`, `Autocomplete`, `DatePicker`, `Textarea`) use `InputField` internally and forward its props automatically.
|
|
13
|
+
|
|
14
|
+
## Import
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { InputField } from "@g4rcez/components";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Props
|
|
21
|
+
|
|
22
|
+
| Prop | Type | Default | Description |
|
|
23
|
+
|------|------|---------|-------------|
|
|
24
|
+
| `title` | `Label` | - | Main label text for the field |
|
|
25
|
+
| `info` | `Label` | - | Informational text shown in a `Tooltip` icon next to the label |
|
|
26
|
+
| `error` | `string` | - | Error message rendered below the field border |
|
|
27
|
+
| `feedback` | `Label` | - | Success or neutral feedback message (hidden when `error` is present) |
|
|
28
|
+
| `left` | `Label` | - | Content rendered to the left inside the field border |
|
|
29
|
+
| `right` | `Label` | - | Content rendered to the right inside the field border |
|
|
30
|
+
| `rightLabel` | `Label` | - | Content rendered to the right of the label text |
|
|
31
|
+
| `required` | `boolean` | `false` | If `true`, hides the "Optional" badge |
|
|
32
|
+
| `optionalText` | `string` | `"Optional"` | Text shown for optional fields (translatable) |
|
|
33
|
+
| `disabled` | `boolean` | `false` | Applies disabled styling to the wrapper and label |
|
|
34
|
+
| `interactive` | `boolean` | `false` | Sets `data-interactive` on the fieldset |
|
|
35
|
+
| `container` | `string` | - | Extra CSS classes for the outer `<fieldset>` |
|
|
36
|
+
| `labelClassName` | `string` | - | Extra CSS classes for the inner label/border wrapper `<div>` |
|
|
37
|
+
| `hiddenLabel` | `boolean` | `false` | Visually hides the label row while keeping it accessible |
|
|
38
|
+
| `reportStatus` | `boolean` | - | Show `CheckCircle`/`XCircle` icons alongside the label based on validity |
|
|
39
|
+
| `componentName` | `string` | - | Sets `data-component` on the fieldset (e.g., `"input"`, `"select"`) |
|
|
40
|
+
| `id` | `string` | - | `id` linked to the inner control via `htmlFor` on the label |
|
|
41
|
+
| `name` | `string` | - | Fallback for `id` when `id` is not provided |
|
|
42
|
+
| `children` | `React.ReactNode` | - | The actual form control (input, select, etc.) |
|
|
43
|
+
|
|
44
|
+
## Design Tokens
|
|
45
|
+
|
|
46
|
+
Tokens this component reads. Customize by overriding these CSS variables in your theme.
|
|
47
|
+
|
|
48
|
+
| Token | CSS Variable | Purpose |
|
|
49
|
+
|-------|-------------|---------|
|
|
50
|
+
| `border-input-border` | `--input-border` | Default field border color |
|
|
51
|
+
| `text-field-label` | `--field-label` | Label text color |
|
|
52
|
+
| `text-primary` | `--primary` | Label and border color on focus/hover |
|
|
53
|
+
| `text-danger` | `--danger` | Label, border, and error text color in error state |
|
|
54
|
+
| `text-disabled` | `--disabled` | Label and border color when disabled |
|
|
55
|
+
| `mt-input-gap` | `--input-gap` | Gap between border and error/feedback text |
|
|
56
|
+
|
|
57
|
+
## Examples
|
|
58
|
+
|
|
59
|
+
### Wrapping a native input
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
import { InputField } from "@g4rcez/components";
|
|
63
|
+
|
|
64
|
+
<InputField title="Username" name="username" required>
|
|
65
|
+
<input
|
|
66
|
+
id="username"
|
|
67
|
+
name="username"
|
|
68
|
+
className="h-input-height w-full flex-1 bg-transparent px-input-x text-foreground outline-none"
|
|
69
|
+
/>
|
|
70
|
+
</InputField>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### With info tooltip and error
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import { SearchIcon, CheckIcon } from "lucide-react";
|
|
77
|
+
|
|
78
|
+
<InputField
|
|
79
|
+
title="API Key"
|
|
80
|
+
name="api_key"
|
|
81
|
+
info="Your secret API key from the developer portal."
|
|
82
|
+
error={apiKeyError}
|
|
83
|
+
left={<SearchIcon size={16} className="text-muted-foreground" />}
|
|
84
|
+
right={isValid ? <CheckIcon size={16} className="text-success" /> : null}
|
|
85
|
+
required
|
|
86
|
+
>
|
|
87
|
+
<input
|
|
88
|
+
id="api_key"
|
|
89
|
+
name="api_key"
|
|
90
|
+
type="password"
|
|
91
|
+
className="h-input-height w-full flex-1 bg-transparent px-input-x text-foreground outline-none"
|
|
92
|
+
/>
|
|
93
|
+
</InputField>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Optional vs required
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
{/* Required — "Optional" badge is hidden */}
|
|
100
|
+
<InputField title="Email" name="email" required>
|
|
101
|
+
<input id="email" name="email" type="email" className="h-input-height w-full flex-1 bg-transparent px-input-x text-foreground outline-none" />
|
|
102
|
+
</InputField>
|
|
103
|
+
|
|
104
|
+
{/* Optional — shows "Optional" badge */}
|
|
105
|
+
<InputField title="Website" name="website">
|
|
106
|
+
<input id="website" name="website" type="url" className="h-input-height w-full flex-1 bg-transparent px-input-x text-foreground outline-none" />
|
|
107
|
+
</InputField>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Hidden label (accessible)
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
<InputField title="Search" name="search" hiddenLabel>
|
|
114
|
+
<input
|
|
115
|
+
id="search"
|
|
116
|
+
name="search"
|
|
117
|
+
placeholder="Search..."
|
|
118
|
+
className="h-input-height w-full flex-1 bg-transparent px-input-x text-foreground outline-none"
|
|
119
|
+
/>
|
|
120
|
+
</InputField>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Feedback after validation
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
<InputField
|
|
127
|
+
title="Slug"
|
|
128
|
+
name="slug"
|
|
129
|
+
feedback={isAvailable ? "This slug is available." : undefined}
|
|
130
|
+
error={!isAvailable ? "Slug is already taken." : undefined}
|
|
131
|
+
required
|
|
132
|
+
>
|
|
133
|
+
<input id="slug" name="slug" className="h-input-height w-full flex-1 bg-transparent px-input-x text-foreground outline-none" />
|
|
134
|
+
</InputField>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Do
|
|
138
|
+
|
|
139
|
+
- Use `InputField` when building new form controls to ensure consistent label, error, and feedback layout across the application.
|
|
140
|
+
- Always provide a `title` for every field so screen readers can identify the control.
|
|
141
|
+
- Use the `info` prop for fields that need supplemental explanation without cluttering the label.
|
|
142
|
+
- Match the `id` on the inner control to the `name` (or `id`) on `InputField` so the `<label htmlFor>` association works correctly.
|
|
143
|
+
|
|
144
|
+
## Don't
|
|
145
|
+
|
|
146
|
+
- Don't pass raw Tailwind color classes (`bg-blue-500`, `text-white`, `border-gray-300`) to `InputField` wrappers — use theme props or design tokens instead.
|
|
147
|
+
- Don't use arbitrary Tailwind values (`bg-[#abc]`, `bg-[--my-var]`) — override CSS variables in your `@theme` block instead.
|
|
148
|
+
- Don't use `InputField` for non-form content; it is specifically designed for labeled input controls.
|
|
149
|
+
- Don't skip the `title` prop — omitting it hides the label but may break screen reader associations.
|
|
150
|
+
|
|
151
|
+
## Accessibility
|
|
152
|
+
|
|
153
|
+
- Renders a `<fieldset>` element as the outer wrapper, with `aria-disabled` when disabled.
|
|
154
|
+
- The inner `<label>` uses `htmlFor` linked to `id` (or `name`) to associate with the control.
|
|
155
|
+
- Error messages render as a `<p>` below the field; they are shown after the user interacts with the input (`data-initialized="true"`) or when `error` is set explicitly.
|
|
156
|
+
- Feedback messages render as a separate `<p>` and are hidden when an error is present.
|
|
157
|
+
- The `info` tooltip uses `Tooltip` with `aria-label` / `aria-describedby` for screen reader support.
|
|
158
|
+
- `reportStatus` icons use `aria-hidden="true"` since they are supplemental visual indicators.
|
|
159
|
+
|
|
160
|
+
## Data Attributes
|
|
161
|
+
|
|
162
|
+
- `data-component` — reflects the `componentName` prop (e.g., `"input"`, `"select"`, `"autocomplete"`).
|
|
163
|
+
- `data-error` — `"true"` when an `error` string is present; drives CSS group variants.
|
|
164
|
+
- `data-interactive` — `"true"` when `interactive` is set; used for custom interactive state styling.
|
|
165
|
+
|
|
166
|
+
## Notes
|
|
167
|
+
|
|
168
|
+
- `InputField` uses Tailwind `group` classes to synchronize hover and focus states between the outer wrapper and inner elements. The `group-focus-within:border-primary` and `group-hover:border-primary` patterns rely on this.
|
|
169
|
+
- The optional text label ("Optional") is sourced from the translation system via `useTranslations`, making it localizable.
|
|
170
|
+
- `reportStatus` defaults to the value configured in `useTweaks().input.iconFeedback`, allowing a global default.
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: List
|
|
3
|
+
description: Animated list that expands each item into a focused floating detail overlay when clicked.
|
|
4
|
+
package: "@g4rcez/components"
|
|
5
|
+
export: "{ AnimatedList, AnimatedListItem }"
|
|
6
|
+
import: "import { AnimatedList, AnimatedListItem } from '@g4rcez/components/list'"
|
|
7
|
+
category: display
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# List
|
|
11
|
+
|
|
12
|
+
Animated list that expands each item into a focused floating detail overlay when clicked.
|
|
13
|
+
|
|
14
|
+
`AnimatedList` and `AnimatedListItem` are exported from the `@g4rcez/components/list` subpath. Each row in the list is clickable; selecting a row opens a `motion/react`-animated card overlay with the item's full content.
|
|
15
|
+
|
|
16
|
+
## Import
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
import { AnimatedList, AnimatedListItem } from "@g4rcez/components/list";
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Props
|
|
23
|
+
|
|
24
|
+
### AnimatedList
|
|
25
|
+
|
|
26
|
+
| Prop | Type | Default | Description |
|
|
27
|
+
|------|------|---------|-------------|
|
|
28
|
+
| `children` | `React.ReactNode` | — | One or more `AnimatedListItem` elements. |
|
|
29
|
+
|
|
30
|
+
### AnimatedListItem
|
|
31
|
+
|
|
32
|
+
| Prop | Type | Default | Description |
|
|
33
|
+
|------|------|---------|-------------|
|
|
34
|
+
| `title` | `Label` | — | Primary heading shown in the list row and in the overlay header. |
|
|
35
|
+
| `description` | `Label` | — | Secondary text rendered below the title in both the row and the overlay. |
|
|
36
|
+
| `children` | `Label` | — | Content rendered inside the expanded overlay below the header section. |
|
|
37
|
+
| `avatar` | `Label` | — | Optional leading node (image, icon, or element) displayed before the title in the row. |
|
|
38
|
+
| `leading` | `React.FC<{ open: () => void }>` | — | Optional render-prop component at the trailing end of the row. Receives an `open` callback to open the overlay programmatically. |
|
|
39
|
+
|
|
40
|
+
`Label` is `string | number | ReactNode`.
|
|
41
|
+
|
|
42
|
+
## Design Tokens
|
|
43
|
+
|
|
44
|
+
Tokens this component reads. Customize by overriding these CSS variables in your theme.
|
|
45
|
+
|
|
46
|
+
| Token | CSS Variable | Purpose |
|
|
47
|
+
|-------|-------------|---------|
|
|
48
|
+
| `border-card-border` | `--card-border` | Row separator and overlay card border |
|
|
49
|
+
| `bg-card-background` | `--card-background` | Overlay card background color |
|
|
50
|
+
| `rounded-card` | `--radius-card` | Overlay card corner radius |
|
|
51
|
+
| `shadow-shadow-card` | `--shadow-card` | Overlay card drop shadow |
|
|
52
|
+
| `text-foreground` | `--foreground` | Default text color for row and overlay content |
|
|
53
|
+
| `text-secondary` | `--secondary` | Description text color |
|
|
54
|
+
| `text-primary` | `--primary` | Row title hover color and avatar focus ring |
|
|
55
|
+
| `text-danger` | `--danger` | Close button hover color in the overlay |
|
|
56
|
+
| `bg-floating-overlay` | `--floating-overlay` | Semi-transparent backdrop behind the overlay (used at 70 % opacity) |
|
|
57
|
+
| `z-floating` | `--z-floating` | `z-index` for the overlay card (value: 22) |
|
|
58
|
+
| `z-overlay` | `--z-overlay` | `z-index` for the backdrop scrim |
|
|
59
|
+
|
|
60
|
+
## Examples
|
|
61
|
+
|
|
62
|
+
### Basic usage
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
import { AnimatedList, AnimatedListItem } from "@g4rcez/components/list";
|
|
66
|
+
|
|
67
|
+
const members = [
|
|
68
|
+
{ id: "1", name: "Alice Johnson", role: "Engineering" },
|
|
69
|
+
{ id: "2", name: "Bob Smith", role: "Design" },
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
export function TeamList() {
|
|
73
|
+
return (
|
|
74
|
+
<AnimatedList>
|
|
75
|
+
{members.map((m) => (
|
|
76
|
+
<AnimatedListItem
|
|
77
|
+
key={m.id}
|
|
78
|
+
title={m.name}
|
|
79
|
+
description={m.role}
|
|
80
|
+
>
|
|
81
|
+
<p className="text-foreground">Full profile for {m.name}.</p>
|
|
82
|
+
</AnimatedListItem>
|
|
83
|
+
))}
|
|
84
|
+
</AnimatedList>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### With avatar
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
import { AnimatedList, AnimatedListItem } from "@g4rcez/components/list";
|
|
93
|
+
import { UserCircleIcon } from "lucide-react";
|
|
94
|
+
|
|
95
|
+
export function UserDirectory() {
|
|
96
|
+
return (
|
|
97
|
+
<AnimatedList>
|
|
98
|
+
<AnimatedListItem
|
|
99
|
+
title="Carol White"
|
|
100
|
+
description="Product Manager"
|
|
101
|
+
avatar={<UserCircleIcon size={40} className="text-muted-foreground" />}
|
|
102
|
+
>
|
|
103
|
+
<div className="flex flex-col gap-2 text-foreground">
|
|
104
|
+
<span>Department: Product</span>
|
|
105
|
+
<span>Location: San Francisco</span>
|
|
106
|
+
</div>
|
|
107
|
+
</AnimatedListItem>
|
|
108
|
+
</AnimatedList>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### With trailing action using `leading`
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
import { AnimatedList, AnimatedListItem } from "@g4rcez/components/list";
|
|
117
|
+
import { Button } from "@g4rcez/components/button";
|
|
118
|
+
|
|
119
|
+
export function OrderList({ orders }: { orders: Order[] }) {
|
|
120
|
+
return (
|
|
121
|
+
<AnimatedList>
|
|
122
|
+
{orders.map((order) => (
|
|
123
|
+
<AnimatedListItem
|
|
124
|
+
key={order.id}
|
|
125
|
+
title={`Order #${order.id}`}
|
|
126
|
+
description={`Total: ${order.total}`}
|
|
127
|
+
leading={({ open }) => (
|
|
128
|
+
<Button size="small" theme="ghost-muted" onClick={open}>
|
|
129
|
+
View details
|
|
130
|
+
</Button>
|
|
131
|
+
)}
|
|
132
|
+
>
|
|
133
|
+
<OrderDetailContent order={order} />
|
|
134
|
+
</AnimatedListItem>
|
|
135
|
+
))}
|
|
136
|
+
</AnimatedList>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Notification / activity feed
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
import { AnimatedList, AnimatedListItem } from "@g4rcez/components/list";
|
|
145
|
+
import { CheckCircleIcon, AlertTriangleIcon } from "lucide-react";
|
|
146
|
+
|
|
147
|
+
const feed = [
|
|
148
|
+
{ id: "n1", icon: <CheckCircleIcon size={20} className="text-success" />, title: "Deployment succeeded", time: "2 min ago", detail: "All 3 services are healthy." },
|
|
149
|
+
{ id: "n2", icon: <AlertTriangleIcon size={20} className="text-warn" />, title: "High CPU usage", time: "10 min ago", detail: "Instance i-0ab2 is at 94 %." },
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
export function NotificationFeed() {
|
|
153
|
+
return (
|
|
154
|
+
<AnimatedList>
|
|
155
|
+
{feed.map((n) => (
|
|
156
|
+
<AnimatedListItem
|
|
157
|
+
key={n.id}
|
|
158
|
+
title={n.title}
|
|
159
|
+
description={n.time}
|
|
160
|
+
avatar={n.icon}
|
|
161
|
+
>
|
|
162
|
+
<p className="text-sm text-muted-foreground">{n.detail}</p>
|
|
163
|
+
</AnimatedListItem>
|
|
164
|
+
))}
|
|
165
|
+
</AnimatedList>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Do
|
|
171
|
+
|
|
172
|
+
- Keep `title` and `description` short — they share a single row and are not truncated automatically.
|
|
173
|
+
- Provide meaningful `children` content in the overlay to justify clicking the row.
|
|
174
|
+
- Use `leading` to expose a primary call-to-action alongside the title without forcing the user to open the overlay.
|
|
175
|
+
- Use design-token classes inside the overlay content (`bg-card-background`, `text-foreground`, `border-card-border`).
|
|
176
|
+
|
|
177
|
+
## Don't
|
|
178
|
+
|
|
179
|
+
- Don't pass raw Tailwind color classes (`bg-blue-500`, `text-white`, `border-gray-300`) — use design tokens instead.
|
|
180
|
+
- Don't use arbitrary Tailwind values (`bg-[#abc]`, `bg-[--my-var]`) — override CSS variables in your `@theme` block instead.
|
|
181
|
+
- Don't use this component for purely informational lists where no detail overlay is needed — use a plain `<ul>` instead.
|
|
182
|
+
- Don't embed full applications or heavy forms inside the overlay `children`; use a `Modal` for complex workflows.
|
|
183
|
+
- Don't render `AnimatedListItem` outside of `AnimatedList` — the item component returns a `Fragment` and relies entirely on the parent list for rendering.
|
|
184
|
+
|
|
185
|
+
## Accessibility
|
|
186
|
+
|
|
187
|
+
- The list renders as `<ul role="list">`.
|
|
188
|
+
- Each row title/description area is a `<button>`, making it keyboard focusable and activatable with `Enter` or `Space`.
|
|
189
|
+
- The overlay uses `FloatingFocusManager` to trap focus and `FloatingOverlay` with `lockScroll` to block background interaction.
|
|
190
|
+
- Pressing `Escape`, clicking outside, or clicking the close button dismisses the overlay.
|
|
191
|
+
- The close button inside the overlay is a focusable `<button>` with an `XIcon`.
|
|
192
|
+
- `MotionConfig reducedMotion="user"` honours the system-level reduced-motion preference, disabling animations when requested.
|
|
193
|
+
|
|
194
|
+
## Data Attributes
|
|
195
|
+
|
|
196
|
+
- `layoutId="item-{id}"` — shared between the list row `<li>` and the overlay card `<motion.div>` to drive the shared-element expand/collapse animation.
|
|
197
|
+
- `layoutId="toast-{id}"` — inner content wrapper for coordinated layout transitions.
|
|
198
|
+
|
|
199
|
+
## Notes
|
|
200
|
+
|
|
201
|
+
- Only one item can be open at a time; selecting a new item dismisses the previous overlay automatically.
|
|
202
|
+
- `AnimatedListItem` is a shell — it returns a `Fragment` directly and holds no state. All rendering logic lives inside `AnimatedList`, which reads child props via `React.Children.toArray`.
|
|
203
|
+
- The overlay is rendered in a `FloatingPortal` (outside the DOM tree) to avoid stacking-context or overflow clipping issues.
|
|
204
|
+
- Animations use `motion/react` shared-layout (`layoutId`) so the item card expands smoothly from its row position to the center of the viewport.
|
|
205
|
+
- The animation transition is `tween` with a 0.3 s duration; `stiffness` is set low (25) for a relaxed feel.
|