@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,225 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: FileUpload
|
|
3
|
+
description: Drag-and-drop file upload area with file preview, modal viewer, and per-file deletion.
|
|
4
|
+
package: "@g4rcez/components"
|
|
5
|
+
export: "{ FileUpload }"
|
|
6
|
+
import: "import { FileUpload } from '@g4rcez/components/file-upload'"
|
|
7
|
+
category: form
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# FileUpload
|
|
11
|
+
|
|
12
|
+
Drag-and-drop file upload area with file preview, modal viewer, and per-file deletion.
|
|
13
|
+
|
|
14
|
+
## Import
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { FileUpload } from "@g4rcez/components/file-upload";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Props
|
|
21
|
+
|
|
22
|
+
| Prop | Type | Default | Description |
|
|
23
|
+
|------|------|---------|-------------|
|
|
24
|
+
| `files` | `File[]` | - | Controlled files array |
|
|
25
|
+
| `onDrop` | `(files: File[]) => void` | - | Called when files are dropped or selected |
|
|
26
|
+
| `onDeleteFile` | `(file: File) => void` | - | Called when the delete button on a file item is clicked |
|
|
27
|
+
| `idle` | `React.ReactElement` | Default idle UI | Content to show when no files are present and the area is not active |
|
|
28
|
+
| `File` | `React.FC<{ file: File }>` | - | Custom component rendered below each file's name/size row |
|
|
29
|
+
| `accept` | `string \| Record<string, string[]>` | - | Accepted file types (forwarded to `react-dropzone`) |
|
|
30
|
+
| `multiple` | `boolean` | `false` | Allow selecting more than one file |
|
|
31
|
+
| `maxFiles` | `number` | - | Maximum number of files |
|
|
32
|
+
| `maxSize` | `number` | - | Maximum file size in bytes |
|
|
33
|
+
| `disabled` | `boolean` | `false` | Disable the drop zone |
|
|
34
|
+
| `name` | `string` | - | Name for the underlying `<input>` |
|
|
35
|
+
| `...DropzoneProps` | | | All `react-dropzone` props are supported |
|
|
36
|
+
|
|
37
|
+
## Design Tokens
|
|
38
|
+
|
|
39
|
+
Tokens this component reads. Customize by overriding these CSS variables in your theme.
|
|
40
|
+
|
|
41
|
+
| Token | CSS Variable | Purpose |
|
|
42
|
+
|-------|-------------|---------|
|
|
43
|
+
| `border-card-border` | `--card-border` | Border between file list items and default drop zone border |
|
|
44
|
+
| `bg-card-background` | `--card-background` | Drop zone background when files are present |
|
|
45
|
+
| `text-foreground` | `--foreground` | General text color |
|
|
46
|
+
| `text-primary` | `--primary` | Folder icon color and "browse" link color in default idle state |
|
|
47
|
+
| `text-danger` | `--danger` | Delete button hover color |
|
|
48
|
+
|
|
49
|
+
## Drag and Drop States
|
|
50
|
+
|
|
51
|
+
| State | Description |
|
|
52
|
+
|-------|-------------|
|
|
53
|
+
| Idle (empty) | Displays the `idle` prop or the default folder icon with an upload prompt |
|
|
54
|
+
| Drag active | Replaces the idle UI with an open folder icon while dragging |
|
|
55
|
+
| Files present | Renders the file list; drop zone border becomes solid and background fills |
|
|
56
|
+
|
|
57
|
+
## File Type Rendering
|
|
58
|
+
|
|
59
|
+
The component automatically selects an icon based on file extension:
|
|
60
|
+
|
|
61
|
+
| Extension(s) | Icon |
|
|
62
|
+
|---|---|
|
|
63
|
+
| csv, xls, xlsx | `SheetIcon` |
|
|
64
|
+
| pdf, txt | `FileTextIcon` |
|
|
65
|
+
| json | `FileJsonIcon` |
|
|
66
|
+
| mp3 | `AudioLinesIcon` |
|
|
67
|
+
| mp4, mov | `FileVideo2` |
|
|
68
|
+
| Images | Inline `<img>` thumbnail |
|
|
69
|
+
| Other | Generic `FileIcon` |
|
|
70
|
+
|
|
71
|
+
Clicking a file thumbnail opens a `Modal` viewer with full-size preview for images, `<video>` player for videos, and `<audio>` player for audio files.
|
|
72
|
+
|
|
73
|
+
## Examples
|
|
74
|
+
|
|
75
|
+
### Controlled image upload
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import { FileUpload } from "@g4rcez/components/file-upload";
|
|
79
|
+
|
|
80
|
+
function ImageUpload() {
|
|
81
|
+
const [images, setImages] = useState<File[]>([]);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<FileUpload
|
|
85
|
+
files={images}
|
|
86
|
+
onDrop={(added) => setImages((prev) => [...prev, ...added])}
|
|
87
|
+
onDeleteFile={(file) => setImages((prev) => prev.filter((f) => f !== file))}
|
|
88
|
+
accept="image/*"
|
|
89
|
+
multiple
|
|
90
|
+
maxFiles={10}
|
|
91
|
+
maxSize={5 * 1024 * 1024}
|
|
92
|
+
/>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Single file upload
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
function AvatarUpload() {
|
|
101
|
+
const [file, setFile] = useState<File | null>(null);
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<FileUpload
|
|
105
|
+
files={file ? [file] : []}
|
|
106
|
+
onDrop={(files) => setFile(files[0])}
|
|
107
|
+
onDeleteFile={() => setFile(null)}
|
|
108
|
+
accept="image/jpeg,image/png"
|
|
109
|
+
multiple={false}
|
|
110
|
+
maxSize={2 * 1024 * 1024}
|
|
111
|
+
/>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Custom idle state
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
import { UploadCloudIcon } from "lucide-react";
|
|
120
|
+
|
|
121
|
+
const CustomIdle = () => (
|
|
122
|
+
<div className="flex flex-col items-center gap-2 py-10">
|
|
123
|
+
<UploadCloudIcon size={48} className="text-primary" />
|
|
124
|
+
<p className="text-foreground font-medium">Drop files here</p>
|
|
125
|
+
<p className="text-muted-foreground text-sm">PDF, DOCX, up to 10 MB each</p>
|
|
126
|
+
</div>
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
<FileUpload
|
|
130
|
+
files={[]}
|
|
131
|
+
onDrop={setFiles}
|
|
132
|
+
idle={<CustomIdle />}
|
|
133
|
+
accept="application/pdf,.docx"
|
|
134
|
+
multiple
|
|
135
|
+
/>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Custom per-file renderer
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
const ProgressRow = ({ file }: { file: File }) => {
|
|
142
|
+
const progress = useUploadProgress(file);
|
|
143
|
+
return (
|
|
144
|
+
<div className="flex flex-col gap-1 pb-2">
|
|
145
|
+
<div className="flex justify-between text-xs text-muted-foreground">
|
|
146
|
+
<span>{progress < 100 ? "Uploading..." : "Done"}</span>
|
|
147
|
+
<span>{progress}%</span>
|
|
148
|
+
</div>
|
|
149
|
+
<div className="h-1.5 rounded-full bg-muted overflow-hidden">
|
|
150
|
+
<div
|
|
151
|
+
className="h-full rounded-full bg-primary transition-all"
|
|
152
|
+
style={{ width: `${progress}%` }}
|
|
153
|
+
/>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
<FileUpload files={files} onDrop={setFiles} File={ProgressRow} multiple />
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Inside a form
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
import { Form } from "@g4rcez/components/form";
|
|
166
|
+
import { Button } from "@g4rcez/components/button";
|
|
167
|
+
|
|
168
|
+
function SubmissionForm() {
|
|
169
|
+
const [attachments, setAttachments] = useState<File[]>([]);
|
|
170
|
+
|
|
171
|
+
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
172
|
+
const data = new FormData(e.currentTarget);
|
|
173
|
+
attachments.forEach((f) => data.append("attachments", f));
|
|
174
|
+
submitToServer(data);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
return (
|
|
178
|
+
<Form onSubmit={handleSubmit} className="flex flex-col gap-base">
|
|
179
|
+
<FileUpload
|
|
180
|
+
name="attachments"
|
|
181
|
+
files={attachments}
|
|
182
|
+
onDrop={(added) => setAttachments((prev) => [...prev, ...added])}
|
|
183
|
+
onDeleteFile={(f) => setAttachments((prev) => prev.filter((x) => x !== f))}
|
|
184
|
+
accept="image/*,.pdf"
|
|
185
|
+
multiple
|
|
186
|
+
maxSize={10 * 1024 * 1024}
|
|
187
|
+
/>
|
|
188
|
+
<Button theme="primary" type="submit">Submit</Button>
|
|
189
|
+
</Form>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Do
|
|
195
|
+
|
|
196
|
+
- Always set `accept` to restrict uploads to the file types your server expects.
|
|
197
|
+
- Set `maxSize` to provide immediate client-side feedback for oversized files.
|
|
198
|
+
- Use `multiple` when batch uploads are expected.
|
|
199
|
+
- Use the `File` prop to add progress bars, metadata, or custom actions below each file row.
|
|
200
|
+
- Use design-token classes for wrapper elements (`bg-card-background`, `text-foreground`).
|
|
201
|
+
|
|
202
|
+
## Don't
|
|
203
|
+
|
|
204
|
+
- Don't pass raw Tailwind color classes (`bg-blue-500`, `text-white`, `border-gray-300`) — use theme props or design tokens instead.
|
|
205
|
+
- Don't use arbitrary Tailwind values (`bg-[#abc]`, `bg-[--my-var]`) — override CSS variables in your `@theme` block instead.
|
|
206
|
+
- Don't rely solely on client-side `maxSize`/`accept` — validate files server-side as well.
|
|
207
|
+
- Don't skip `onDeleteFile` when `files` is controlled — without it, users cannot remove files they added.
|
|
208
|
+
|
|
209
|
+
## Accessibility
|
|
210
|
+
|
|
211
|
+
- The drop zone uses `react-dropzone`'s `getRootProps` and `getInputProps`, which include keyboard support (Enter/Space to open the file browser).
|
|
212
|
+
- The hidden `<input type="file">` is accessible by assistive technologies.
|
|
213
|
+
- Delete buttons include a `type="button"` to prevent accidental form submission.
|
|
214
|
+
- The modal viewer opened for file preview is managed by the `Modal` component, which handles focus trapping and Escape to close.
|
|
215
|
+
|
|
216
|
+
## Data Attributes
|
|
217
|
+
|
|
218
|
+
- `data-active="true"` — on the drop zone wrapper when at least one file is present; used to switch between dashed (empty) and solid (filled) border styles.
|
|
219
|
+
|
|
220
|
+
## Notes
|
|
221
|
+
|
|
222
|
+
- When `files` is not provided, the component manages its own internal file state. Pass `files` for fully controlled behavior.
|
|
223
|
+
- Object URLs created for previews are revoked on cleanup via `useEffect` to prevent memory leaks.
|
|
224
|
+
- The `File` prop (custom renderer) is rendered inside the file list item, below the file name and size row, alongside the built-in delete button.
|
|
225
|
+
- The component depends on `react-dropzone` and `pretty-bytes` as peer dependencies.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Form
|
|
3
|
+
description: Thin wrapper around the HTML form element that calls preventDefault automatically before invoking onSubmit.
|
|
4
|
+
package: "@g4rcez/components"
|
|
5
|
+
export: "{ Form }"
|
|
6
|
+
import: "import { Form } from '@g4rcez/components/form'"
|
|
7
|
+
category: form
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Form
|
|
11
|
+
|
|
12
|
+
Thin wrapper around the HTML `<form>` element that calls `preventDefault` automatically before invoking `onSubmit`.
|
|
13
|
+
|
|
14
|
+
## Import
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { Form } from "@g4rcez/components/form";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Props
|
|
21
|
+
|
|
22
|
+
`Form` accepts all standard HTML `<form>` attributes.
|
|
23
|
+
|
|
24
|
+
| Prop | Type | Default | Description |
|
|
25
|
+
|------|------|---------|-------------|
|
|
26
|
+
| `onSubmit` | `(event: React.FormEvent<HTMLFormElement>) => void` | - | Submit handler. `preventDefault()` is called before this. |
|
|
27
|
+
| `children` | `React.ReactNode` | - | Form fields and other content. |
|
|
28
|
+
| `className` | `string` | - | CSS classes for the `<form>` element. |
|
|
29
|
+
| `...props` | `React.ComponentProps<"form">` | - | All standard form attributes (`action`, `method`, `noValidate`, etc.). |
|
|
30
|
+
|
|
31
|
+
## Design Tokens
|
|
32
|
+
|
|
33
|
+
`Form` has no direct token usage. Tokens are applied by the child components (`Input`, `Button`, etc.) placed inside it.
|
|
34
|
+
|
|
35
|
+
## Examples
|
|
36
|
+
|
|
37
|
+
### Login form
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { Form } from "@g4rcez/components/form";
|
|
41
|
+
import { Input } from "@g4rcez/components/input";
|
|
42
|
+
import { Button } from "@g4rcez/components/button";
|
|
43
|
+
|
|
44
|
+
function LoginForm() {
|
|
45
|
+
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
46
|
+
const data = new FormData(e.currentTarget);
|
|
47
|
+
console.log(Object.fromEntries(data));
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Form onSubmit={handleSubmit} className="flex flex-col gap-base max-w-sm">
|
|
52
|
+
<Input name="email" type="email" title="Email" required />
|
|
53
|
+
<Input name="password" type="password" title="Password" required />
|
|
54
|
+
<Button theme="primary" type="submit">Log in</Button>
|
|
55
|
+
</Form>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Reading FormData on submit
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
64
|
+
const formData = new FormData(e.currentTarget);
|
|
65
|
+
const payload = Object.fromEntries(formData);
|
|
66
|
+
await fetch("/api/contact", { method: "POST", body: JSON.stringify(payload) });
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
<Form onSubmit={handleSubmit} className="flex flex-col gap-base">
|
|
70
|
+
<Input name="name" title="Name" required />
|
|
71
|
+
<Input name="message" title="Message" />
|
|
72
|
+
<Button theme="primary" type="submit">Send</Button>
|
|
73
|
+
</Form>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Multi-column layout
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
<Form className="grid grid-cols-2 gap-base max-w-2xl">
|
|
80
|
+
<Input name="firstName" title="First name" required />
|
|
81
|
+
<Input name="lastName" title="Last name" required />
|
|
82
|
+
<Input name="email" type="email" title="Email" container="col-span-2" required />
|
|
83
|
+
<Button type="submit" theme="primary" container="col-start-2">Save</Button>
|
|
84
|
+
</Form>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Do
|
|
88
|
+
|
|
89
|
+
- Use `Form` instead of a raw `<form>` to avoid calling `e.preventDefault()` in every submit handler.
|
|
90
|
+
- Always include a `<Button type="submit">` or `<button type="submit">` so keyboard users can submit with Enter.
|
|
91
|
+
- Use design-token layout utilities (`gap-base`, `gap-sm`) for consistent spacing between fields.
|
|
92
|
+
|
|
93
|
+
## Don't
|
|
94
|
+
|
|
95
|
+
- Don't pass raw Tailwind color classes (`bg-blue-500`, `text-white`, `border-gray-300`) to `Form` — use theme props or design tokens instead.
|
|
96
|
+
- Don't use arbitrary Tailwind values (`bg-[#abc]`) — override CSS variables in your `@theme` block instead.
|
|
97
|
+
- Don't use `Form` when you need a traditional server-rendered full-page reload; use a plain `<form>` with `action` and `method` instead.
|
|
98
|
+
|
|
99
|
+
## Accessibility
|
|
100
|
+
|
|
101
|
+
- Renders a semantic `<form>` element, correctly identified by assistive technologies.
|
|
102
|
+
- Ensure every field inside the form has an associated label (the `Input`, `Select`, and other components in this library do this automatically via `InputField`).
|
|
103
|
+
|
|
104
|
+
## Notes
|
|
105
|
+
|
|
106
|
+
- `Form` is intentionally minimal — it has no state, no validation logic, and no styling. All field-level behavior is delegated to child components.
|
|
107
|
+
- The `e.persist()` call inside the wrapper ensures the synthetic event remains accessible in async submit handlers.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: formReset
|
|
3
|
+
description: Utility function that resets all inputs and selects in a form to their default values and clears their initialization state.
|
|
4
|
+
package: "@g4rcez/components"
|
|
5
|
+
export: "{ formReset }"
|
|
6
|
+
import: "import { formReset } from '@g4rcez/components'"
|
|
7
|
+
category: form
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# formReset
|
|
11
|
+
|
|
12
|
+
Utility function that resets all inputs and selects in a form to their default values and clears their initialization state.
|
|
13
|
+
|
|
14
|
+
## Import
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { formReset } from "@g4rcez/components";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Signature
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
function formReset(form?: HTMLFormElement | null): void
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Parameters
|
|
27
|
+
|
|
28
|
+
| Parameter | Type | Description |
|
|
29
|
+
|-----------|------|-------------|
|
|
30
|
+
| `form` | `HTMLFormElement \| null \| undefined` | The form element to reset. Safely no-ops if `null` or `undefined`. |
|
|
31
|
+
|
|
32
|
+
## How It Works
|
|
33
|
+
|
|
34
|
+
1. Iterates over all elements in the form (`form.elements`).
|
|
35
|
+
2. For `<input>` elements: sets `input.value` to `input.defaultValue`.
|
|
36
|
+
3. For `<select>` elements: sets `select.value` to `""`.
|
|
37
|
+
4. On every processed field: sets `data-initialized="false"`, which tells components like `Input` and `Autocomplete` to treat the field as untouched (suppressing validation messages).
|
|
38
|
+
|
|
39
|
+
## Examples
|
|
40
|
+
|
|
41
|
+
### Reset after successful submission
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import { useRef } from "react";
|
|
45
|
+
import { Form } from "@g4rcez/components/form";
|
|
46
|
+
import { Input } from "@g4rcez/components/input";
|
|
47
|
+
import { Button } from "@g4rcez/components/button";
|
|
48
|
+
import { formReset } from "@g4rcez/components";
|
|
49
|
+
|
|
50
|
+
function ContactForm() {
|
|
51
|
+
const formRef = useRef<HTMLFormElement>(null);
|
|
52
|
+
|
|
53
|
+
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
|
54
|
+
const data = new FormData(e.currentTarget);
|
|
55
|
+
await submitToServer(Object.fromEntries(data));
|
|
56
|
+
formReset(formRef.current);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<Form ref={formRef} onSubmit={handleSubmit} className="flex flex-col gap-base">
|
|
61
|
+
<Input name="name" title="Name" required />
|
|
62
|
+
<Input name="email" type="email" title="Email" required />
|
|
63
|
+
<Button theme="primary" type="submit">Send</Button>
|
|
64
|
+
</Form>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Cancel button that resets the form
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
function EditForm({ defaultValues }: { defaultValues: Record<string, string> }) {
|
|
73
|
+
const formRef = useRef<HTMLFormElement>(null);
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<Form ref={formRef} onSubmit={handleSave} className="flex flex-col gap-base">
|
|
77
|
+
<Input name="title" title="Title" defaultValue={defaultValues.title} />
|
|
78
|
+
<Input name="description" title="Description" defaultValue={defaultValues.description} />
|
|
79
|
+
<div className="flex gap-sm">
|
|
80
|
+
<Button theme="primary" type="submit">Save</Button>
|
|
81
|
+
<Button
|
|
82
|
+
theme="ghost"
|
|
83
|
+
type="button"
|
|
84
|
+
onClick={() => formReset(formRef.current)}
|
|
85
|
+
>
|
|
86
|
+
Cancel
|
|
87
|
+
</Button>
|
|
88
|
+
</div>
|
|
89
|
+
</Form>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Do
|
|
95
|
+
|
|
96
|
+
- Use `formReset` (instead of `form.reset()`) when the form contains library components — it also resets the `data-initialized` flag that controls validation UI.
|
|
97
|
+
- Pass a valid `HTMLFormElement` ref.
|
|
98
|
+
|
|
99
|
+
## Don't
|
|
100
|
+
|
|
101
|
+
- Don't expect `formReset` to fire `onChange` events — it modifies `value` directly in the DOM without dispatching React synthetic events.
|
|
102
|
+
- Don't use `formReset` to reset a single field; use standard DOM methods or React state for that.
|
|
103
|
+
- Don't use `formReset` as a substitute for React-controlled state management in complex forms.
|
|
104
|
+
|
|
105
|
+
## Accessibility
|
|
106
|
+
|
|
107
|
+
No direct accessibility implications. After a reset, validation error messages will disappear because `data-initialized="false"` prevents them from being shown again until the user interacts with each field.
|
|
108
|
+
|
|
109
|
+
## Data Attributes
|
|
110
|
+
|
|
111
|
+
- Sets `data-initialized="false"` on every `INPUT` and `SELECT` element in the form. This attribute is read by `InputField` to decide whether to display validation feedback.
|
|
112
|
+
|
|
113
|
+
## Notes
|
|
114
|
+
|
|
115
|
+
- Safe to call with `null` or `undefined` — the function returns early without throwing.
|
|
116
|
+
- Only processes elements with `tagName === "INPUT"` or `tagName === "SELECT"`. `<textarea>` elements are not currently reset by this utility.
|
|
117
|
+
- This is a plain function, not a React component or hook. It works on DOM references directly.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Heading
|
|
3
|
+
description: A minimal heading wrapper that renders as h2 by default, delegating to Polymorph for element flexibility.
|
|
4
|
+
package: "@g4rcez/components"
|
|
5
|
+
export: "{ Heading }"
|
|
6
|
+
import: "import { Heading } from '@g4rcez/components'"
|
|
7
|
+
category: core
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Heading
|
|
11
|
+
|
|
12
|
+
A minimal heading wrapper that renders as `h2` by default. It delegates to `Polymorph` internally, so all children are forwarded without additional styling or tokens.
|
|
13
|
+
|
|
14
|
+
## Import
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { Heading } from "@g4rcez/components";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Props
|
|
21
|
+
|
|
22
|
+
| Prop | Type | Default | Description |
|
|
23
|
+
|------|------|---------|-------------|
|
|
24
|
+
| `children` | `React.ReactNode` | - | The content of the heading |
|
|
25
|
+
|
|
26
|
+
## Design Tokens
|
|
27
|
+
|
|
28
|
+
None — `Heading` itself applies no styles. Style via `className` passed to the component or global heading rules in your theme.
|
|
29
|
+
|
|
30
|
+
## Examples
|
|
31
|
+
|
|
32
|
+
### Default Usage
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
<Heading>Section Title</Heading>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Custom Heading Level
|
|
39
|
+
|
|
40
|
+
The current implementation always renders as `h2`. Use the `Polymorph` component directly if you need a different heading level.
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import { Polymorph } from "@g4rcez/components";
|
|
44
|
+
|
|
45
|
+
<Polymorph as="h1" className="text-3xl font-bold tracking-tight text-foreground">
|
|
46
|
+
Page Title
|
|
47
|
+
</Polymorph>
|
|
48
|
+
|
|
49
|
+
<Polymorph as="h3" className="text-lg font-semibold text-foreground">
|
|
50
|
+
Subsection Title
|
|
51
|
+
</Polymorph>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Applying Typography Styles
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
<Heading className="text-2xl font-bold text-foreground">
|
|
58
|
+
Dashboard Overview
|
|
59
|
+
</Heading>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Do
|
|
63
|
+
|
|
64
|
+
- Use `Heading` for `h2`-level section titles throughout your app to ensure a consistent element
|
|
65
|
+
- Use design-token classes for color (`text-foreground`, `text-muted-foreground`) — never raw color utilities
|
|
66
|
+
- Pair with `PageHeader` or `PageTitle` from the Typography components for full page structure
|
|
67
|
+
|
|
68
|
+
## Don't
|
|
69
|
+
|
|
70
|
+
- Don't pass raw Tailwind color classes (`text-gray-900`, `text-black`) — use `text-foreground` instead
|
|
71
|
+
- Don't use arbitrary Tailwind values (`text-[#333]`) — override CSS variables in your `@theme` block
|
|
72
|
+
- Don't skip heading hierarchy — if you need `h1` or `h3`, use `Polymorph as="h1"` rather than misusing `Heading`
|
|
73
|
+
- Don't use `Heading` for non-heading content purely for visual sizing
|
|
74
|
+
|
|
75
|
+
## Accessibility
|
|
76
|
+
|
|
77
|
+
- Renders as `<h2>` by default, providing semantic structure for screen readers and document outline tools
|
|
78
|
+
- Pass `className` for visual styling without altering the semantic element
|
|
79
|
+
- Ensure each page has a single `h1` (use `Polymorph as="h1"`) before using `Heading` (`h2`) for section titles
|
|
80
|
+
|
|
81
|
+
## Data Attributes
|
|
82
|
+
|
|
83
|
+
- `data-component="polymorph"`: Inherited from the underlying `Polymorph` component
|
|
84
|
+
|
|
85
|
+
## Notes
|
|
86
|
+
|
|
87
|
+
- `Heading` is a thin wrapper — it accepts `children` only; all other styling is done via `className`
|
|
88
|
+
- If you need an `as` prop for dynamic heading levels, use `Polymorph` directly
|