@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,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Progress
|
|
3
|
+
description: Accessible progress bar built on Base UI Progress with optional label overlay and smooth transitions.
|
|
4
|
+
package: "@g4rcez/components"
|
|
5
|
+
export: "{ Progress }"
|
|
6
|
+
import: "import { Progress } from '@g4rcez/components'"
|
|
7
|
+
category: display
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Progress
|
|
11
|
+
|
|
12
|
+
Accessible progress bar built on Base UI Progress with optional label overlay and smooth transitions.
|
|
13
|
+
|
|
14
|
+
## Import
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { Progress } from "@g4rcez/components";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Props
|
|
21
|
+
|
|
22
|
+
| Prop | Type | Default | Description |
|
|
23
|
+
|------|------|---------|-------------|
|
|
24
|
+
| `percent` | `number` | — | Current progress value (0–100) |
|
|
25
|
+
| `max` | `number` | — | Maximum value (forwarded to Base UI `Progress.Root`) |
|
|
26
|
+
| `label` | `Label` | — | Custom text overlay; overrides the default `{percent} %` |
|
|
27
|
+
| `container` | `string` | — | Additional classes for the track element |
|
|
28
|
+
| `className` | `string` | — | Additional classes for the indicator (fill) element |
|
|
29
|
+
| `textClassName` | `string` | — | Additional classes for the label text overlay |
|
|
30
|
+
|
|
31
|
+
## Design Tokens
|
|
32
|
+
|
|
33
|
+
Tokens this component reads. Customize by overriding these CSS variables in your theme.
|
|
34
|
+
|
|
35
|
+
| Token | CSS Variable | Purpose |
|
|
36
|
+
|-------|-------------|---------|
|
|
37
|
+
| `bg-background` | `--background` | Track (unfilled) background |
|
|
38
|
+
| `bg-primary` | `--primary` | Indicator (fill) color |
|
|
39
|
+
| `text-primary-foreground` | `--primary-foreground` | Default label text color |
|
|
40
|
+
|
|
41
|
+
## Examples
|
|
42
|
+
|
|
43
|
+
### Basic Progress Bar
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<Progress percent={75} />
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Animated Progress
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
function AnimatedProgress() {
|
|
53
|
+
const [progress, setProgress] = useState(0);
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
const timer = setInterval(() => {
|
|
57
|
+
setProgress((prev) => (prev >= 100 ? 0 : prev + 10));
|
|
58
|
+
}, 500);
|
|
59
|
+
return () => clearInterval(timer);
|
|
60
|
+
}, []);
|
|
61
|
+
|
|
62
|
+
return <Progress percent={progress} />;
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Custom Label
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
<Progress percent={60} label="Uploading file… 60%" />
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Indeterminate / Unknown Duration
|
|
73
|
+
|
|
74
|
+
When `percent` is `undefined` the indicator is not rendered and the label is hidden, leaving only the track. Combine with a separate `Spinner` for unknown-duration operations.
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
{isLoading ? <Spinner /> : <Progress percent={uploadPercent} />}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Multi-Step Form Progress
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
function MultiStepForm() {
|
|
84
|
+
const [currentStep, setCurrentStep] = useState(1);
|
|
85
|
+
const totalSteps = 4;
|
|
86
|
+
const progress = (currentStep / totalSteps) * 100;
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<div className="space-y-4">
|
|
90
|
+
<div className="flex justify-between items-center text-sm text-muted-foreground">
|
|
91
|
+
<span>Step {currentStep} of {totalSteps}</span>
|
|
92
|
+
<span>{Math.round(progress)}% complete</span>
|
|
93
|
+
</div>
|
|
94
|
+
<Progress percent={progress} />
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Upload with Status Label
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
function FileUploadProgress({ fileName, percent }: { fileName: string; percent: number }) {
|
|
104
|
+
const isDone = percent >= 100;
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<div className="space-y-1">
|
|
108
|
+
<div className="flex justify-between text-sm">
|
|
109
|
+
<span className="text-foreground">{fileName}</span>
|
|
110
|
+
<span className="text-muted-foreground">{percent}%</span>
|
|
111
|
+
</div>
|
|
112
|
+
<Progress
|
|
113
|
+
percent={percent}
|
|
114
|
+
label={isDone ? "Complete" : undefined}
|
|
115
|
+
container={isDone ? "bg-success/20" : undefined}
|
|
116
|
+
className={isDone ? "bg-success" : undefined}
|
|
117
|
+
/>
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Do
|
|
124
|
+
|
|
125
|
+
- Use `Progress` when the duration of an operation is known or can be estimated.
|
|
126
|
+
- Provide a `label` when a percentage alone is not descriptive enough.
|
|
127
|
+
- Use `container` and `className` with design-token classes to change the track and fill colors (`bg-success`, `bg-warn`, etc.).
|
|
128
|
+
|
|
129
|
+
## Don't
|
|
130
|
+
|
|
131
|
+
- Don't pass raw Tailwind color classes (`bg-green-500`, `bg-blue-500`) in `container` or `className` — use design tokens (`bg-success`, `bg-primary`) instead.
|
|
132
|
+
- Don't use arbitrary Tailwind values (`bg-[#abc]`) — override CSS variables in your `@theme` block.
|
|
133
|
+
- Don't use `Progress` for operations with unknown durations — use `Spinner` instead.
|
|
134
|
+
- Don't update `percent` more frequently than needed; excessive updates cause jitter.
|
|
135
|
+
|
|
136
|
+
## Accessibility
|
|
137
|
+
|
|
138
|
+
- Built on Base UI `Progress.Root` which renders the correct `role="progressbar"` with `aria-valuenow`, `aria-valuemin`, and `aria-valuemax` automatically.
|
|
139
|
+
- The label overlay is a `<p>` element with `tabular-nums` for consistent digit rendering.
|
|
140
|
+
|
|
141
|
+
## Notes
|
|
142
|
+
|
|
143
|
+
- The indicator moves via a CSS `translateX` transform (`translateX(-${100 - percent}%)`) for GPU-accelerated animation.
|
|
144
|
+
- The 500 ms `transition-transform ease-in-out` is applied via the `className` on the indicator element and can be overridden.
|
|
145
|
+
- When `percent` is `undefined` or `null`, the label and fill are hidden; the track remains visible.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Radiobox
|
|
3
|
+
description: Styled radio button for selecting one option from a mutually exclusive set.
|
|
4
|
+
package: "@g4rcez/components"
|
|
5
|
+
export: "{ Radiobox }"
|
|
6
|
+
import: "import { Radiobox } from '@g4rcez/components/radiobox'"
|
|
7
|
+
category: form
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Radiobox
|
|
11
|
+
|
|
12
|
+
Styled radio button for selecting one option from a mutually exclusive set.
|
|
13
|
+
|
|
14
|
+
## Import
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { Radiobox } from "@g4rcez/components/radiobox";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Props
|
|
21
|
+
|
|
22
|
+
Accepts all standard HTML `input[type="radio"]` attributes, plus:
|
|
23
|
+
|
|
24
|
+
| Prop | Type | Default | Description |
|
|
25
|
+
|------|------|---------|-------------|
|
|
26
|
+
| `children` | `React.ReactNode` | — | Label text or element displayed next to the radio button. |
|
|
27
|
+
| `size` | `"medium" \| "large"` | `"medium"` | Visual size of the radio button. |
|
|
28
|
+
| `className` | `string` | — | Additional CSS classes for the `<input>` element. |
|
|
29
|
+
|
|
30
|
+
## Design Tokens
|
|
31
|
+
|
|
32
|
+
Tokens this component reads. Customize by overriding these CSS variables in your theme.
|
|
33
|
+
|
|
34
|
+
| Token | CSS Variable | Purpose |
|
|
35
|
+
|-------|-------------|---------|
|
|
36
|
+
| `border-card-border` | `--card-border` | Default border color of the radio circle |
|
|
37
|
+
| `text-primary` | `--primary` | Checked fill color (via `accent-color`) |
|
|
38
|
+
| `focus:ring-primary` | `--primary` | Focus ring color |
|
|
39
|
+
| `disabled:opacity-70` | — | Reduced opacity for disabled state |
|
|
40
|
+
|
|
41
|
+
## Examples
|
|
42
|
+
|
|
43
|
+
### Basic group
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { Radiobox } from "@g4rcez/components/radiobox";
|
|
47
|
+
|
|
48
|
+
export default function PlanSelector() {
|
|
49
|
+
return (
|
|
50
|
+
<div className="flex flex-col gap-sm">
|
|
51
|
+
<Radiobox name="plan" value="basic" defaultChecked>
|
|
52
|
+
Basic Plan
|
|
53
|
+
</Radiobox>
|
|
54
|
+
<Radiobox name="plan" value="pro">
|
|
55
|
+
Pro Plan
|
|
56
|
+
</Radiobox>
|
|
57
|
+
<Radiobox name="plan" value="enterprise">
|
|
58
|
+
Enterprise Plan
|
|
59
|
+
</Radiobox>
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Disabled state
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
import { Radiobox } from "@g4rcez/components/radiobox";
|
|
69
|
+
|
|
70
|
+
export default function DisabledOption() {
|
|
71
|
+
return (
|
|
72
|
+
<Radiobox disabled name="option" value="legacy">
|
|
73
|
+
Legacy (unavailable)
|
|
74
|
+
</Radiobox>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### In a grid layout
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
import { Radiobox } from "@g4rcez/components/radiobox";
|
|
83
|
+
|
|
84
|
+
export default function GenderSelector() {
|
|
85
|
+
return (
|
|
86
|
+
<div className="grid grid-cols-2 gap-base">
|
|
87
|
+
<Radiobox name="gender" value="male">Male</Radiobox>
|
|
88
|
+
<Radiobox name="gender" value="female">Female</Radiobox>
|
|
89
|
+
<Radiobox name="gender" value="non-binary">Non-binary</Radiobox>
|
|
90
|
+
<Radiobox name="gender" value="prefer-not">Prefer not to say</Radiobox>
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Do
|
|
97
|
+
|
|
98
|
+
- Group related `Radiobox` components by giving them the same `name` attribute.
|
|
99
|
+
- Provide a clear, concise label for each radio button via `children`.
|
|
100
|
+
- Use `Radiobox` when there are 2–7 mutually exclusive options; for more, prefer `Select`.
|
|
101
|
+
- Use design-token classes on wrapper elements (`bg-background`, `text-foreground`).
|
|
102
|
+
|
|
103
|
+
## Don't
|
|
104
|
+
|
|
105
|
+
- Don't use `Radiobox` for independent toggles — use `Checkbox` instead.
|
|
106
|
+
- Don't use `Radiobox` for a simple yes/no choice — consider `Switch`.
|
|
107
|
+
- Don't pass raw Tailwind color classes (`text-blue-500`, `border-gray-300`) — use design tokens instead.
|
|
108
|
+
- Don't use arbitrary Tailwind values (`text-[--my-var]`) — override CSS variables in your `@theme` block instead.
|
|
109
|
+
|
|
110
|
+
## Accessibility
|
|
111
|
+
|
|
112
|
+
- A semantic `<label>` wraps the `<input>`, associating label text with the radio button without extra `htmlFor` wiring.
|
|
113
|
+
- `aria-disabled` is set on the wrapper `<label>` when the input is disabled.
|
|
114
|
+
- Standard keyboard interaction: Arrow keys navigate between radios in the same group; Space selects.
|
|
115
|
+
- The `data-[disabled=true]:cursor-not-allowed` class gives a clear cursor signal for disabled items.
|
|
116
|
+
|
|
117
|
+
## Data Attributes
|
|
118
|
+
|
|
119
|
+
| Attribute | Element | Value | Description |
|
|
120
|
+
|-----------|---------|-------|-------------|
|
|
121
|
+
| `data-component` | `label` | `"radiobox"` | Identifies the component. |
|
|
122
|
+
| `data-disabled` | `label` | `true \| undefined` | Set when the radio is disabled. |
|
|
123
|
+
|
|
124
|
+
## Notes
|
|
125
|
+
|
|
126
|
+
- Uses the `form-radio` utility class for consistent cross-browser appearance.
|
|
127
|
+
- The `size` prop is accepted in the type definition but visual differentiation is handled via the `className` pass-through — you may add `h-5 w-5` or similar token-based size classes as needed.
|
|
128
|
+
- Render multiple `Radiobox` components inside a `<fieldset>` with a `<legend>` for full semantic grouping.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: RenderOnView
|
|
3
|
+
description: A performance wrapper that defers rendering children until the container enters the viewport.
|
|
4
|
+
package: "@g4rcez/components"
|
|
5
|
+
export: "{ RenderOnView }"
|
|
6
|
+
import: "import { RenderOnView } from '@g4rcez/components'"
|
|
7
|
+
category: core
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# RenderOnView
|
|
11
|
+
|
|
12
|
+
A performance wrapper that defers rendering its children until the container element enters the viewport. Uses the `IntersectionObserver` API and renders children exactly once — they stay mounted after first appearing.
|
|
13
|
+
|
|
14
|
+
## Import
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { RenderOnView } from "@g4rcez/components";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Props
|
|
21
|
+
|
|
22
|
+
| Prop | Type | Default | Description |
|
|
23
|
+
|------|------|---------|-------------|
|
|
24
|
+
| `onIntersection` | `() => void` | - | Callback fired once when the container first enters the viewport |
|
|
25
|
+
| `children` | `React.ReactNode` | - | Content to render when the container is visible |
|
|
26
|
+
| `as` | `React.ElementType` | `"div"` | HTML element to render the container as |
|
|
27
|
+
| `...props` | `React.ComponentPropsWithoutRef<T>` | - | All props valid for the chosen element type |
|
|
28
|
+
|
|
29
|
+
## Design Tokens
|
|
30
|
+
|
|
31
|
+
None — `RenderOnView` is a layout/performance primitive that applies no styles of its own.
|
|
32
|
+
|
|
33
|
+
## How It Works
|
|
34
|
+
|
|
35
|
+
1. **Initial state**: The container element renders immediately but children are not mounted.
|
|
36
|
+
2. **Intersection Observer**: A `useLayoutEffect` sets up an `IntersectionObserver` on the container.
|
|
37
|
+
3. **First intersection**: When the container enters the viewport, `shouldRender` flips to `true` and children mount.
|
|
38
|
+
4. **Stays mounted**: Children remain in the DOM after the first intersection — scrolling back out does not unmount them.
|
|
39
|
+
5. **Callback**: `onIntersection` fires once at the moment of the first intersection.
|
|
40
|
+
|
|
41
|
+
## Examples
|
|
42
|
+
|
|
43
|
+
### Basic Lazy Rendering
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<div>
|
|
47
|
+
<div className="h-screen flex items-center justify-center text-foreground">
|
|
48
|
+
Scroll down to see lazy content
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<RenderOnView>
|
|
52
|
+
<ExpensiveChart data={largeDataset} />
|
|
53
|
+
</RenderOnView>
|
|
54
|
+
</div>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### With Intersection Callback
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
const TrackableSection = ({ sectionName, children }: { sectionName: string; children: React.ReactNode }) => (
|
|
61
|
+
<RenderOnView
|
|
62
|
+
onIntersection={() => analytics.track("Section Viewed", { section: sectionName })}
|
|
63
|
+
>
|
|
64
|
+
{children}
|
|
65
|
+
</RenderOnView>
|
|
66
|
+
);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Multiple Deferred Sections
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
<main>
|
|
73
|
+
<HeroSection />
|
|
74
|
+
|
|
75
|
+
<RenderOnView>
|
|
76
|
+
<FeaturesSection />
|
|
77
|
+
</RenderOnView>
|
|
78
|
+
|
|
79
|
+
<RenderOnView>
|
|
80
|
+
<TestimonialsSection />
|
|
81
|
+
</RenderOnView>
|
|
82
|
+
|
|
83
|
+
<RenderOnView>
|
|
84
|
+
<ContactForm />
|
|
85
|
+
</RenderOnView>
|
|
86
|
+
</main>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### With Custom Element
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
<RenderOnView as="section" aria-label="Analytics charts">
|
|
93
|
+
<RevenueChart />
|
|
94
|
+
</RenderOnView>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### With React.lazy and Suspense
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
import { lazy, Suspense } from "react";
|
|
101
|
+
import { Spinner } from "@g4rcez/components";
|
|
102
|
+
|
|
103
|
+
const HeavyComponent = lazy(() => import("./HeavyComponent"));
|
|
104
|
+
|
|
105
|
+
<RenderOnView>
|
|
106
|
+
<Suspense fallback={<Spinner />}>
|
|
107
|
+
<HeavyComponent />
|
|
108
|
+
</Suspense>
|
|
109
|
+
</RenderOnView>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Do
|
|
113
|
+
|
|
114
|
+
- Use `RenderOnView` for components with significant rendering cost (charts, maps, rich editors)
|
|
115
|
+
- Combine with `React.lazy` and `Suspense` for maximum bundle and rendering savings
|
|
116
|
+
- Use `as="section"` or another semantic element when the container has meaningful structure
|
|
117
|
+
- Use design-token classes on wrapper divs inside children (`bg-background`, `border-border`)
|
|
118
|
+
|
|
119
|
+
## Don't
|
|
120
|
+
|
|
121
|
+
- Don't use `RenderOnView` for above-the-fold content — it adds an unnecessary observer
|
|
122
|
+
- Don't rely on it for critical content that must be in the initial HTML (SEO, LCP elements)
|
|
123
|
+
- Don't pass raw Tailwind color classes (`bg-white`, `text-gray-800`) to children or wrappers — use design tokens
|
|
124
|
+
- Don't use arbitrary Tailwind values (`bg-[#abc]`) — override CSS variables in your `@theme` block
|
|
125
|
+
|
|
126
|
+
## Accessibility
|
|
127
|
+
|
|
128
|
+
- The container element is always rendered and present in the DOM
|
|
129
|
+
- Children mount only when visible, so screen readers will encounter them when they scroll into view
|
|
130
|
+
- Ensure any interactive content inside has proper focus management after mounting
|
|
131
|
+
|
|
132
|
+
## Notes
|
|
133
|
+
|
|
134
|
+
- Children render exactly once and are never unmounted, regardless of subsequent scroll position
|
|
135
|
+
- `onIntersection` fires at most once per component lifetime
|
|
136
|
+
- The `IntersectionObserver` is disconnected on component unmount
|
|
137
|
+
- Uses `useLayoutEffect` for synchronous observer setup to avoid a flash of empty container
|
|
138
|
+
- SSR-safe: the initial `shouldRender` state starts as `false` since `ref.current` is `null` on the server
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Resizable
|
|
3
|
+
description: A wrapper that animates its height smoothly when content dimensions change.
|
|
4
|
+
package: "@g4rcez/components"
|
|
5
|
+
export: "{ Resizable }"
|
|
6
|
+
import: "import { Resizable } from '@g4rcez/components'"
|
|
7
|
+
category: core
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Resizable
|
|
11
|
+
|
|
12
|
+
A wrapper that automatically animates its height whenever the inner content size changes. Uses `ResizeObserver` to detect dimension changes and `motion/react` to drive smooth height transitions.
|
|
13
|
+
|
|
14
|
+
## Import
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { Resizable } from "@g4rcez/components";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Props
|
|
21
|
+
|
|
22
|
+
| Prop | Type | Default | Description |
|
|
23
|
+
|------|------|---------|-------------|
|
|
24
|
+
| `children` | `React.ReactNode` | - | Content rendered inside the animated container |
|
|
25
|
+
|
|
26
|
+
## Design Tokens
|
|
27
|
+
|
|
28
|
+
None — `Resizable` is a layout animation primitive that applies no color or spacing tokens.
|
|
29
|
+
|
|
30
|
+
## How It Works
|
|
31
|
+
|
|
32
|
+
1. An inner `<div>` holds the children and is observed by a `ResizeObserver`.
|
|
33
|
+
2. When the inner `div`'s height changes, the observed height value updates via a motion value.
|
|
34
|
+
3. An outer `motion.div` animates from the previous height to the new height using `motion/react`.
|
|
35
|
+
4. While the content has no measured height yet (`h === 0`), the container uses `height: "auto"`.
|
|
36
|
+
|
|
37
|
+
## Examples
|
|
38
|
+
|
|
39
|
+
### Collapsible Content
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { useState } from "react";
|
|
43
|
+
|
|
44
|
+
const CollapsiblePanel = () => {
|
|
45
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div className="border border-border rounded-card">
|
|
49
|
+
<button
|
|
50
|
+
type="button"
|
|
51
|
+
className="w-full px-4 py-3 text-left font-medium text-foreground"
|
|
52
|
+
onClick={() => setIsOpen((v) => !v)}
|
|
53
|
+
>
|
|
54
|
+
Toggle Details
|
|
55
|
+
</button>
|
|
56
|
+
<Resizable>
|
|
57
|
+
{isOpen ? (
|
|
58
|
+
<div className="px-4 pb-4 text-foreground">
|
|
59
|
+
<p>This content expands and collapses with a smooth height animation.</p>
|
|
60
|
+
<p>Additional paragraphs will also animate smoothly.</p>
|
|
61
|
+
</div>
|
|
62
|
+
) : null}
|
|
63
|
+
</Resizable>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Dynamic List
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import { useState } from "react";
|
|
73
|
+
import { Button } from "@g4rcez/components/button";
|
|
74
|
+
|
|
75
|
+
const GrowingList = () => {
|
|
76
|
+
const [items, setItems] = useState(["Item 1"]);
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<div className="flex flex-col gap-base">
|
|
80
|
+
<Button
|
|
81
|
+
theme="primary"
|
|
82
|
+
onClick={() => setItems((prev) => [...prev, `Item ${prev.length + 1}`])}
|
|
83
|
+
>
|
|
84
|
+
Add Item
|
|
85
|
+
</Button>
|
|
86
|
+
<Resizable>
|
|
87
|
+
<ul className="flex flex-col gap-sm">
|
|
88
|
+
{items.map((item) => (
|
|
89
|
+
<li key={item} className="text-foreground">
|
|
90
|
+
{item}
|
|
91
|
+
</li>
|
|
92
|
+
))}
|
|
93
|
+
</ul>
|
|
94
|
+
</Resizable>
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Animated Tab Panels
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
const TabbedContent = ({ activeTab }: { activeTab: string }) => (
|
|
104
|
+
<Resizable>
|
|
105
|
+
<div className="p-4 text-foreground">
|
|
106
|
+
{activeTab === "overview" && <OverviewPanel />}
|
|
107
|
+
{activeTab === "settings" && <SettingsPanel />}
|
|
108
|
+
{activeTab === "history" && <HistoryPanel />}
|
|
109
|
+
</div>
|
|
110
|
+
</Resizable>
|
|
111
|
+
);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Async Content Loading
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
const AsyncCard = ({ data }: { data: string[] | null }) => (
|
|
118
|
+
<div className="rounded-card border border-border bg-card-background shadow-shadow-card">
|
|
119
|
+
<Resizable>
|
|
120
|
+
{data === null ? (
|
|
121
|
+
<div className="p-4 text-muted-foreground">Loading…</div>
|
|
122
|
+
) : (
|
|
123
|
+
<ul className="p-4 flex flex-col gap-sm">
|
|
124
|
+
{data.map((item) => (
|
|
125
|
+
<li key={item} className="text-foreground">{item}</li>
|
|
126
|
+
))}
|
|
127
|
+
</ul>
|
|
128
|
+
)}
|
|
129
|
+
</Resizable>
|
|
130
|
+
</div>
|
|
131
|
+
);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Do
|
|
135
|
+
|
|
136
|
+
- Use `Resizable` around any content that changes height to provide a polished animation
|
|
137
|
+
- Wrap only the specific dynamic section — not large, stable portions of the page
|
|
138
|
+
- Apply visual styles (`bg-*`, `border-*`, `rounded-*`) to elements inside `Resizable`, not to `Resizable` itself
|
|
139
|
+
- Use design-token classes for all styling (`bg-background`, `border-border`, `rounded-card`)
|
|
140
|
+
|
|
141
|
+
## Don't
|
|
142
|
+
|
|
143
|
+
- Don't use `Resizable` on content that changes height at very high frequency (e.g., per-frame updates) — it may cause performance issues
|
|
144
|
+
- Don't wrap large, stable sections of a page — keep the observed area small
|
|
145
|
+
- Don't pass raw Tailwind color classes (`bg-white`, `border-gray-200`) inside `Resizable` wrappers — use design tokens
|
|
146
|
+
- Don't use arbitrary Tailwind values (`bg-[#fff]`) — override CSS variables in your `@theme` block
|
|
147
|
+
|
|
148
|
+
## Accessibility
|
|
149
|
+
|
|
150
|
+
- `Resizable` is a layout animation wrapper and introduces no semantic elements or ARIA attributes
|
|
151
|
+
- Ensure content inside maintains correct focus order and accessible roles
|
|
152
|
+
- For accordion/collapsible patterns, add `aria-expanded` and `aria-controls` to the trigger button
|
|
153
|
+
|
|
154
|
+
## Notes
|
|
155
|
+
|
|
156
|
+
- Requires `motion/react` as a peer dependency
|
|
157
|
+
- The outer `motion.div` drives the height animation; the inner `div` is the measured reference
|
|
158
|
+
- SSR-safe: `isSsr()` prevents `ResizeObserver` from being created on the server
|
|
159
|
+
- The component is a client component (`"use client"`)
|