@g4rcez/components 3.0.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/button.d.ts +2 -8
- package/dist/components/core/button.d.ts.map +1 -1
- package/dist/components/core/polymorph.d.ts.map +1 -1
- package/dist/components/core/slot.d.ts +1 -1
- package/dist/components/core/slot.d.ts.map +1 -1
- package/dist/components/core/tag.d.ts +2 -2
- package/dist/components/core/tag.d.ts.map +1 -1
- package/dist/components/core/typography.d.ts.map +1 -1
- package/dist/components/display/alert.d.ts.map +1 -1
- package/dist/components/display/calendar.d.ts.map +1 -1
- package/dist/components/display/card.d.ts.map +1 -1
- package/dist/components/display/list.d.ts.map +1 -1
- package/dist/components/display/notifications.d.ts +2 -0
- package/dist/components/display/notifications.d.ts.map +1 -1
- package/dist/components/display/progress.d.ts.map +1 -1
- package/dist/components/display/skeleton.d.ts.map +1 -1
- package/dist/components/display/step.d.ts.map +1 -1
- package/dist/components/display/tabs.d.ts.map +1 -1
- package/dist/components/floating/command-palette.d.ts +1 -0
- package/dist/components/floating/command-palette.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 +2 -2
- package/dist/components/floating/menu.d.ts.map +1 -1
- package/dist/components/floating/modal.d.ts +20 -53
- package/dist/components/floating/modal.d.ts.map +1 -1
- package/dist/components/floating/tooltip.d.ts.map +1 -1
- package/dist/components/floating/wizard.d.ts +1 -1
- package/dist/components/floating/wizard.d.ts.map +1 -1
- package/dist/components/form/autocomplete.d.ts.map +1 -1
- package/dist/components/form/date-picker.d.ts.map +1 -1
- package/dist/components/form/free-text.d.ts.map +1 -1
- package/dist/components/form/input-field.d.ts +3 -2
- package/dist/components/form/input-field.d.ts.map +1 -1
- package/dist/components/form/multi-select.d.ts.map +1 -1
- package/dist/components/form/select.d.ts.map +1 -1
- package/dist/components/form/slider.d.ts.map +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/page-calendar/calendar-header.d.ts +16 -0
- package/dist/components/page-calendar/calendar-header.d.ts.map +1 -0
- package/dist/components/page-calendar/day-view.d.ts +12 -0
- package/dist/components/page-calendar/day-view.d.ts.map +1 -0
- package/dist/components/page-calendar/event-pill.d.ts +9 -0
- package/dist/components/page-calendar/event-pill.d.ts.map +1 -0
- package/dist/components/page-calendar/index.d.ts +4 -0
- package/dist/components/page-calendar/index.d.ts.map +1 -0
- package/dist/components/page-calendar/month-view.d.ts +11 -0
- package/dist/components/page-calendar/month-view.d.ts.map +1 -0
- package/dist/components/page-calendar/page-calendar.d.ts +18 -0
- package/dist/components/page-calendar/page-calendar.d.ts.map +1 -0
- package/dist/components/page-calendar/page-calendar.types.d.ts +18 -0
- package/dist/components/page-calendar/page-calendar.types.d.ts.map +1 -0
- package/dist/components/page-calendar/page-calendar.utils.d.ts +18 -0
- package/dist/components/page-calendar/page-calendar.utils.d.ts.map +1 -0
- package/dist/components/page-calendar/week-view.d.ts +11 -0
- package/dist/components/page-calendar/week-view.d.ts.map +1 -0
- package/dist/components/table/index.d.ts.map +1 -1
- package/dist/components/table/inner-table.d.ts.map +1 -1
- package/dist/components/table/metadata.d.ts.map +1 -1
- package/dist/components/table/row.d.ts.map +1 -1
- package/dist/components/table/table-lib.d.ts.map +1 -1
- package/dist/components/table/thead.d.ts.map +1 -1
- package/dist/config/context.d.ts.map +1 -1
- package/dist/config/default-translations.d.ts +21 -4
- package/dist/config/default-translations.d.ts.map +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/hooks/use-components-provider.d.ts.map +1 -1
- package/dist/hooks/use-form.d.ts +11 -11
- package/dist/hooks/use-form.d.ts.map +1 -1
- package/dist/hooks/use-input-id.d.ts.map +1 -1
- package/dist/hooks/use-preferences.d.ts.map +1 -1
- package/dist/hooks/use-previous.d.ts.map +1 -1
- package/dist/hooks/use-reactive.d.ts.map +1 -1
- package/dist/hooks/use-resize-observer.d.ts.map +1 -1
- package/dist/hooks/use-stable-ref.d.ts.map +1 -1
- package/dist/hooks/use-swipe.d.ts.map +1 -1
- package/dist/hooks/use-translations.d.ts +21 -4
- package/dist/hooks/use-translations.d.ts.map +1 -1
- package/dist/index.css +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +13862 -12512
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +24 -17
- package/dist/index.umd.js.map +1 -1
- package/dist/lib/dom.d.ts +1 -0
- package/dist/lib/dom.d.ts.map +1 -1
- package/dist/lib/fns.d.ts.map +1 -1
- package/dist/preset/plugin.tailwind.d.ts +9 -0
- package/dist/preset/plugin.tailwind.d.ts.map +1 -0
- package/dist/preset/plugin.tailwind.js +27 -0
- package/dist/preset/preset.tailwind.d.ts +8 -0
- package/dist/preset/preset.tailwind.d.ts.map +1 -0
- package/dist/preset/preset.tailwind.js +54 -0
- package/dist/preset/src/styles/common.d.ts +2 -14
- package/dist/preset/src/styles/common.d.ts.map +1 -1
- package/dist/preset/src/styles/common.js +1 -0
- package/dist/preset/src/styles/dark.d.ts.map +1 -1
- package/dist/preset/src/styles/dark.js +119 -114
- package/dist/preset/src/styles/light.d.ts.map +1 -1
- package/dist/preset/src/styles/light.js +111 -106
- package/dist/preset/src/styles/theme.types.d.ts +17 -8
- package/dist/preset/src/styles/theme.types.d.ts.map +1 -1
- package/dist/styles/common.d.ts +2 -14
- package/dist/styles/common.d.ts.map +1 -1
- package/dist/styles/dark.d.ts.map +1 -1
- package/dist/styles/light.d.ts.map +1 -1
- package/dist/styles/theme.types.d.ts +17 -8
- package/dist/styles/theme.types.d.ts.map +1 -1
- package/package.json +299 -301
- package/dist/components/core/button.jsx +0 -86
- 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 -163
- package/dist/components/core/tag.jsx +0 -51
- package/dist/components/core/typography.jsx +0 -26
- package/dist/components/display/alert.jsx +0 -56
- package/dist/components/display/calendar.jsx +0 -301
- 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 -98
- package/dist/components/display/progress.jsx +0 -13
- package/dist/components/display/shortcut.jsx +0 -23
- package/dist/components/display/skeleton.jsx +0 -14
- 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 -100
- package/dist/components/display/timeline.jsx +0 -25
- package/dist/components/floating/command-palette.jsx +0 -172
- 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 -241
- package/dist/components/floating/toolbar.jsx +0 -5
- package/dist/components/floating/tooltip.jsx +0 -64
- package/dist/components/floating/wizard.jsx +0 -164
- package/dist/components/form/autocomplete.jsx +0 -275
- 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 -54
- 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 -43
- 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 -37
- 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 -84
- 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 -66
- 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 -16
- package/dist/hooks/use-debounce.js +0 -12
- package/dist/hooks/use-floating-ref.js +0 -6
- package/dist/hooks/use-form.js +0 -549
- 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 -8
- package/dist/hooks/use-reactive.js +0 -8
- 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 -8
- package/dist/hooks/use-swipe.js +0 -16
- 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 -44
- package/dist/lib/fns.js +0 -46
- package/dist/lib/fzf.js +0 -117
- package/dist/lib/keyboard-area.js +0 -14
- package/dist/preset/tailwindcssv4.d.ts +0 -3
- package/dist/preset/tailwindcssv4.d.ts.map +0 -1
- package/dist/preset/tailwindcssv4.js +0 -75
- package/dist/styles/common.js +0 -28
- package/dist/styles/dark.js +0 -209
- package/dist/styles/design-tokens.js +0 -69
- package/dist/styles/light.js +0 -209
- package/dist/styles/theme.js +0 -4
- package/dist/styles/theme.types.js +0 -1
- package/dist/types.js +0 -1
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Notifications
|
|
3
|
+
description: Toast notification system with themes, stacking, swipe-to-dismiss, and programmatic control.
|
|
4
|
+
package: "@g4rcez/components"
|
|
5
|
+
export: "{ Notifications }"
|
|
6
|
+
import: "import { Notifications, useNotification } from '@g4rcez/components/notifications'"
|
|
7
|
+
category: display
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Notifications
|
|
11
|
+
|
|
12
|
+
Toast notification system with themes, stacking, swipe-to-dismiss, and programmatic control.
|
|
13
|
+
|
|
14
|
+
## Import
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { Notifications, useNotification } from "@g4rcez/components/notifications";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Setup
|
|
21
|
+
|
|
22
|
+
Wrap your app (or the subtree that needs toasts) with `Notifications`:
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { Notifications } from "@g4rcez/components/notifications";
|
|
26
|
+
|
|
27
|
+
function App() {
|
|
28
|
+
return (
|
|
29
|
+
<Notifications max={5} timeout={5000}>
|
|
30
|
+
<YourApp />
|
|
31
|
+
</Notifications>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Props
|
|
37
|
+
|
|
38
|
+
### Notifications (Provider)
|
|
39
|
+
|
|
40
|
+
| Prop | Type | Default | Description |
|
|
41
|
+
|------|------|---------|-------------|
|
|
42
|
+
| `max` | `number` | `5` | Maximum notifications displayed at once |
|
|
43
|
+
| `timeout` | `number` | `5000` | Default auto-dismiss duration in milliseconds |
|
|
44
|
+
| `children` | `React.ReactNode` | — | Subtree that can use `useNotification` |
|
|
45
|
+
|
|
46
|
+
### useNotification — notify function
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
const notify = useNotification();
|
|
50
|
+
const subscription = notify(message, options);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Parameters**
|
|
54
|
+
|
|
55
|
+
| Parameter | Type | Description |
|
|
56
|
+
|-----------|------|-------------|
|
|
57
|
+
| `message` | `Label` | Notification body text (string or React node) |
|
|
58
|
+
| `options` | `NotificationOptions` | Optional configuration (see below) |
|
|
59
|
+
|
|
60
|
+
**NotificationOptions**
|
|
61
|
+
|
|
62
|
+
| Option | Type | Default | Description |
|
|
63
|
+
|--------|------|---------|-------------|
|
|
64
|
+
| `id` | `string` | — | Stable ID; if provided and a toast with this ID exists, it will be updated instead of duplicated |
|
|
65
|
+
| `title` | `Label` | — | Optional notification title |
|
|
66
|
+
| `theme` | `NotificationTheme` | `"default"` | Visual theme variant |
|
|
67
|
+
| `timeout` | `number` | Provider default | Override auto-dismiss duration in ms |
|
|
68
|
+
| `closable` | `boolean` | `true` | Show close button |
|
|
69
|
+
| `loading` | `boolean` | `false` | Replaces icon with spinning `Loader2Icon` |
|
|
70
|
+
|
|
71
|
+
**Return value — NotificationSubscriber**
|
|
72
|
+
|
|
73
|
+
| Method | Description |
|
|
74
|
+
|--------|-------------|
|
|
75
|
+
| `close()` | Dismiss this specific notification |
|
|
76
|
+
| `clear()` | Dismiss all visible notifications |
|
|
77
|
+
|
|
78
|
+
## Themes
|
|
79
|
+
|
|
80
|
+
| Value | Appearance |
|
|
81
|
+
|-------|-----------|
|
|
82
|
+
| `"default"` | Card background with foreground text |
|
|
83
|
+
| `"info"` | `bg-alert-info-bg` / `text-alert-info-text` / `border-alert-info-border` |
|
|
84
|
+
| `"success"` | `bg-alert-success-bg` / `text-alert-success-text` / `border-alert-success-border` |
|
|
85
|
+
| `"warn"` | `bg-alert-warn-bg` / `text-alert-warn-text` / `border-alert-warn-border` |
|
|
86
|
+
| `"danger"` | `bg-alert-danger-bg` / `text-alert-danger-text` / `border-alert-danger-border` |
|
|
87
|
+
| `"secondary"` | `bg-alert-secondary-bg` / `text-alert-secondary-text` / `border-alert-secondary-border` |
|
|
88
|
+
| `"muted"` | `bg-alert-muted-bg` / `text-alert-muted-text` / `border-alert-muted-border` |
|
|
89
|
+
|
|
90
|
+
## Design Tokens
|
|
91
|
+
|
|
92
|
+
Tokens this component reads. Customize by overriding these CSS variables in your theme.
|
|
93
|
+
|
|
94
|
+
| Token | CSS Variable | Purpose |
|
|
95
|
+
|-------|-------------|---------|
|
|
96
|
+
| `bg-card-background` | `--card-background` | Default notification background |
|
|
97
|
+
| `border-card-border` | `--card-border` | Default notification border |
|
|
98
|
+
| `text-foreground` | `--foreground` | Default notification text |
|
|
99
|
+
| `bg-alert-{theme}-bg` | `--alert-{theme}-bg` | Themed background |
|
|
100
|
+
| `text-alert-{theme}-text` | `--alert-{theme}-text` | Themed text |
|
|
101
|
+
| `border-alert-{theme}-border` | `--alert-{theme}-border` | Themed border |
|
|
102
|
+
|
|
103
|
+
## Examples
|
|
104
|
+
|
|
105
|
+
### Basic Notifications
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
function NotificationExamples() {
|
|
109
|
+
const notify = useNotification();
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<div className="space-y-2">
|
|
113
|
+
<button onClick={() => notify("Default notification")}>Default</button>
|
|
114
|
+
<button onClick={() => notify("Info message", { theme: "info" })}>Info</button>
|
|
115
|
+
<button onClick={() => notify("Success!", { theme: "success" })}>Success</button>
|
|
116
|
+
<button onClick={() => notify("Warning", { theme: "warn" })}>Warning</button>
|
|
117
|
+
<button onClick={() => notify("Error occurred", { theme: "danger" })}>Error</button>
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### With Title
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
function SaveButton() {
|
|
127
|
+
const notify = useNotification();
|
|
128
|
+
|
|
129
|
+
const handleSave = () => {
|
|
130
|
+
notify("Your changes have been saved to the server.", {
|
|
131
|
+
title: "Changes Saved",
|
|
132
|
+
theme: "success",
|
|
133
|
+
timeout: 3000,
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
return <button onClick={handleSave}>Save</button>;
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Persistent Notification with Manual Close
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
function ProcessButton() {
|
|
145
|
+
const notify = useNotification();
|
|
146
|
+
|
|
147
|
+
const startProcess = () => {
|
|
148
|
+
const subscription = notify("Processing your request…", {
|
|
149
|
+
title: "In Progress",
|
|
150
|
+
theme: "info",
|
|
151
|
+
timeout: Infinity,
|
|
152
|
+
closable: false,
|
|
153
|
+
loading: true,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
doWork().then(() => {
|
|
157
|
+
subscription.close();
|
|
158
|
+
notify("Process completed successfully!", { theme: "success" });
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
return <button onClick={startProcess}>Start</button>;
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Form Submission Feedback
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
function ContactForm() {
|
|
170
|
+
const notify = useNotification();
|
|
171
|
+
|
|
172
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
173
|
+
e.preventDefault();
|
|
174
|
+
try {
|
|
175
|
+
await submitForm();
|
|
176
|
+
notify("Message sent successfully.", { title: "Done", theme: "success" });
|
|
177
|
+
} catch {
|
|
178
|
+
notify("Failed to send. Please try again.", {
|
|
179
|
+
title: "Error",
|
|
180
|
+
theme: "danger",
|
|
181
|
+
timeout: 7000,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
return <form onSubmit={handleSubmit}>{/* fields */}</form>;
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Updating an Existing Toast
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
const SYNC_ID = "data-sync";
|
|
194
|
+
const notify = useNotification();
|
|
195
|
+
|
|
196
|
+
// Show initial state
|
|
197
|
+
notify("Syncing data…", { id: SYNC_ID, theme: "info", timeout: Infinity });
|
|
198
|
+
|
|
199
|
+
// Update in-place when done
|
|
200
|
+
notify("Sync complete.", { id: SYNC_ID, theme: "success", timeout: 3000 });
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Do
|
|
204
|
+
|
|
205
|
+
- Use the correct `theme` to convey message severity (`success`, `danger`, `warn`).
|
|
206
|
+
- Keep messages short — toasts are glanced at, not read.
|
|
207
|
+
- Use `title` for important notifications that need extra prominence.
|
|
208
|
+
- Set a longer `timeout` (or `Infinity`) for critical errors that need user attention.
|
|
209
|
+
- Use `loading: true` for in-progress operations to signal activity.
|
|
210
|
+
|
|
211
|
+
## Don't
|
|
212
|
+
|
|
213
|
+
- Don't pass raw Tailwind color classes (`bg-blue-500`, `text-white`) — use `theme` instead.
|
|
214
|
+
- Don't use arbitrary Tailwind values (`bg-[#abc]`) — override CSS variables in your `@theme` block.
|
|
215
|
+
- Don't use notifications for information that must be acknowledged before proceeding — use a `Modal` instead.
|
|
216
|
+
- Don't show more than 3–5 notifications at a time; configure `max` accordingly.
|
|
217
|
+
- Don't use notifications for persistent status indicators — use `Alert` or a status bar.
|
|
218
|
+
|
|
219
|
+
## Accessibility
|
|
220
|
+
|
|
221
|
+
- Built on Base UI Toast which manages ARIA live regions for screen reader announcements.
|
|
222
|
+
- Close buttons are keyboard accessible with visible focus states.
|
|
223
|
+
- Swipe-to-dismiss works on touch devices via the Base UI primitive.
|
|
224
|
+
- The viewport is positioned with `role` semantics handled by the underlying primitive.
|
|
225
|
+
|
|
226
|
+
## Notes
|
|
227
|
+
|
|
228
|
+
- Up to 3 notifications are visible by default; hovering the stack reveals all (up to `max`).
|
|
229
|
+
- Animations use Framer Motion (`motion/react`) with spring physics for enter/exit.
|
|
230
|
+
- The viewport is centered horizontally at the top of the viewport (`top-6`, `z-[100]`).
|
|
231
|
+
- When `max` is exceeded, a pill showing `+N more` appears below the visible toasts.
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: PageCalendar
|
|
3
|
+
description: Full-page calendar with month, week, and day views, event filtering, and custom event rendering.
|
|
4
|
+
package: "@g4rcez/components"
|
|
5
|
+
export: "{ PageCalendar }"
|
|
6
|
+
import: "import { PageCalendar } from '@g4rcez/components'"
|
|
7
|
+
category: calendar
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# PageCalendar
|
|
11
|
+
|
|
12
|
+
Full-page calendar with month, week, and day views, event filtering, and custom event rendering.
|
|
13
|
+
|
|
14
|
+
## Import
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { PageCalendar } from "@g4rcez/components";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Props
|
|
21
|
+
|
|
22
|
+
| Prop | Type | Default | Description |
|
|
23
|
+
|------|------|---------|-------------|
|
|
24
|
+
| `events` | `CalendarEvent<T>[]` | — | Array of event objects to display across all views. |
|
|
25
|
+
| `filters` | `CalendarFilter[]` | `[]` | Filter tag definitions. Each filter can be toggled to hide/show matching events. |
|
|
26
|
+
| `defaultView` | `"month" \| "week" \| "day"` | `"month"` | Initial view rendered when the component mounts. |
|
|
27
|
+
| `defaultDate` | `Date` | `new Date()` | Initial date the calendar focuses on. |
|
|
28
|
+
| `onEventClick` | `(event: CalendarEvent) => void` | — | Called when the user clicks an event pill. |
|
|
29
|
+
| `onSlotClick` | `(date: Date) => void` | — | Called when the user clicks an empty time slot (week and day views). |
|
|
30
|
+
| `onAddEvent` | `() => void` | — | Called when the "Add event" button in the header is clicked. Omit to hide the button. |
|
|
31
|
+
| `onChangeFilters` | `(filters: CalendarFilter[]) => void` | — | Called whenever a filter is toggled, receiving the updated filter array. |
|
|
32
|
+
| `renderEvent` | `(event: CalendarEvent<T>) => ReactNode` | — | Custom renderer for the selected event detail panel in day view. |
|
|
33
|
+
| `filterArea` | `ReactNode` | — | Replaces the default filter tag row in the header with custom content. |
|
|
34
|
+
| `getFilterId` | `() => void` | — | Custom accessor to extract a `filterId` from an event. Defaults to `event.filterId`. |
|
|
35
|
+
|
|
36
|
+
## Type Definitions
|
|
37
|
+
|
|
38
|
+
### CalendarEventBase
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
type CalendarEventBase = {
|
|
42
|
+
id: string;
|
|
43
|
+
date: Date;
|
|
44
|
+
};
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### CalendarEvent
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
type CalendarEvent<T extends CalendarEventBase = CalendarEventBase> = T & {
|
|
51
|
+
title: string;
|
|
52
|
+
filterId?: string;
|
|
53
|
+
className?: string;
|
|
54
|
+
};
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`className` is applied directly to the event pill element in all views.
|
|
58
|
+
|
|
59
|
+
### CalendarFilter
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
type CalendarFilter = {
|
|
63
|
+
id: string;
|
|
64
|
+
label: string;
|
|
65
|
+
enabled: boolean;
|
|
66
|
+
theme: TagProps["theme"]; // "primary" | "success" | "warn" | "danger" | "info" | "neutral" | "secondary" | "muted"
|
|
67
|
+
};
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### ViewMode
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
type ViewMode = "month" | "week" | "day";
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Design Tokens
|
|
77
|
+
|
|
78
|
+
Tokens this component reads. Customize by overriding these CSS variables in your theme.
|
|
79
|
+
|
|
80
|
+
| Token | CSS Variable | Purpose |
|
|
81
|
+
|-------|-------------|---------|
|
|
82
|
+
| `bg-primary` | `--primary` | Today indicator background, selected day highlight |
|
|
83
|
+
| `text-primary-foreground` | `--primary-foreground` | Text on today / selected day indicator |
|
|
84
|
+
| `bg-card` | `--card` | Non-today day indicator background in header |
|
|
85
|
+
| `text-foreground` | `--foreground` | Default text in day cells and header |
|
|
86
|
+
| `text-muted-foreground` | `--muted-foreground` | Week label, hour labels, secondary text |
|
|
87
|
+
| `border-border` | `--border` | Grid cell borders, day view hour-slot dividers |
|
|
88
|
+
| `border-card-border` | `--card-border` | Day and week view column borders |
|
|
89
|
+
| `bg-muted` | `--muted` | Hover background on time slots |
|
|
90
|
+
| `z-calendar` | `--z-calendar` | `z-index` for the column resizer handle (value: 2) |
|
|
91
|
+
| `z-floating` | `--z-floating` | `z-index` for floating overlays (value: 22) |
|
|
92
|
+
|
|
93
|
+
## Views
|
|
94
|
+
|
|
95
|
+
### Month view
|
|
96
|
+
|
|
97
|
+
Displays a 6-row grid of all days in the selected month. Each day cell shows event pills. Clicking a day navigates to that day in the day view.
|
|
98
|
+
|
|
99
|
+
### Week view
|
|
100
|
+
|
|
101
|
+
Displays the 7 days of the current week with an hourly time grid. Events are placed at their time position. Clicking an empty slot calls `onSlotClick`.
|
|
102
|
+
|
|
103
|
+
### Day view
|
|
104
|
+
|
|
105
|
+
Displays a single day with an hourly grid alongside a mini calendar and a detail panel. The detail panel shows a default summary or your `renderEvent` output when an event is clicked.
|
|
106
|
+
|
|
107
|
+
## Examples
|
|
108
|
+
|
|
109
|
+
### Minimal calendar
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
import { PageCalendar } from "@g4rcez/components";
|
|
113
|
+
|
|
114
|
+
type MyEvent = { id: string; date: Date; title: string };
|
|
115
|
+
|
|
116
|
+
const events: MyEvent[] = [
|
|
117
|
+
{ id: "1", date: new Date(), title: "Team standup" },
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
export function MyCalendar() {
|
|
121
|
+
return (
|
|
122
|
+
<PageCalendar
|
|
123
|
+
events={events}
|
|
124
|
+
onEventClick={(event) => console.log(event)}
|
|
125
|
+
/>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### With filters
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
import { PageCalendar } from "@g4rcez/components";
|
|
134
|
+
import type { CalendarFilter } from "@g4rcez/components";
|
|
135
|
+
|
|
136
|
+
const filters: CalendarFilter[] = [
|
|
137
|
+
{ id: "work", label: "Work", enabled: true, theme: "primary" },
|
|
138
|
+
{ id: "personal", label: "Personal", enabled: true, theme: "success" },
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
const events = [
|
|
142
|
+
{ id: "1", date: new Date(), title: "Sprint planning", filterId: "work" },
|
|
143
|
+
{ id: "2", date: new Date(), title: "Gym", filterId: "personal" },
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
export function FilteredCalendar() {
|
|
147
|
+
return (
|
|
148
|
+
<PageCalendar
|
|
149
|
+
events={events}
|
|
150
|
+
filters={filters}
|
|
151
|
+
onChangeFilters={(updated) => console.log(updated)}
|
|
152
|
+
/>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Custom event detail in day view
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
import { PageCalendar } from "@g4rcez/components";
|
|
161
|
+
import { MapPinIcon } from "lucide-react";
|
|
162
|
+
|
|
163
|
+
export function DetailCalendar() {
|
|
164
|
+
return (
|
|
165
|
+
<PageCalendar
|
|
166
|
+
events={events}
|
|
167
|
+
defaultView="day"
|
|
168
|
+
renderEvent={(event) => (
|
|
169
|
+
<div className="flex flex-col gap-1 p-2 bg-muted rounded-card">
|
|
170
|
+
<span className="font-semibold text-foreground">{event.title}</span>
|
|
171
|
+
<span className="flex items-center gap-1 text-sm text-muted-foreground">
|
|
172
|
+
<MapPinIcon size={12} />
|
|
173
|
+
{event.location}
|
|
174
|
+
</span>
|
|
175
|
+
</div>
|
|
176
|
+
)}
|
|
177
|
+
/>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Controlled add-event flow
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
import { useState } from "react";
|
|
186
|
+
import { PageCalendar } from "@g4rcez/components";
|
|
187
|
+
|
|
188
|
+
export function EditableCalendar() {
|
|
189
|
+
const [showForm, setShowForm] = useState(false);
|
|
190
|
+
const [slotDate, setSlotDate] = useState<Date | null>(null);
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<>
|
|
194
|
+
<PageCalendar
|
|
195
|
+
events={events}
|
|
196
|
+
onAddEvent={() => setShowForm(true)}
|
|
197
|
+
onSlotClick={(date) => {
|
|
198
|
+
setSlotDate(date);
|
|
199
|
+
setShowForm(true);
|
|
200
|
+
}}
|
|
201
|
+
/>
|
|
202
|
+
{showForm && <EventForm defaultDate={slotDate} onClose={() => setShowForm(false)} />}
|
|
203
|
+
</>
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Custom filter area
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
import { PageCalendar } from "@g4rcez/components";
|
|
212
|
+
import { Button } from "@g4rcez/components/button";
|
|
213
|
+
|
|
214
|
+
export function CustomFilterCalendar() {
|
|
215
|
+
return (
|
|
216
|
+
<PageCalendar
|
|
217
|
+
events={events}
|
|
218
|
+
filterArea={
|
|
219
|
+
<div className="flex items-center gap-2 rounded-card bg-muted px-3 py-1.5">
|
|
220
|
+
<Button theme="ghost-muted" size="small">All</Button>
|
|
221
|
+
<Button theme="primary" size="small">Mine</Button>
|
|
222
|
+
</div>
|
|
223
|
+
}
|
|
224
|
+
/>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Do
|
|
230
|
+
|
|
231
|
+
- Provide unique `id` values for every event and filter to ensure stable React keys and filter matching.
|
|
232
|
+
- Use `theme` on filters to give users a quick visual legend for event categories.
|
|
233
|
+
- Implement both `onAddEvent` and `onSlotClick` to give users two natural entry points for creating events.
|
|
234
|
+
- Use design-token classes in `renderEvent` output (`bg-muted`, `text-foreground`, `border-border`).
|
|
235
|
+
- Keep event `title` values short — they are truncated in month and week event pills.
|
|
236
|
+
|
|
237
|
+
## Don't
|
|
238
|
+
|
|
239
|
+
- Don't pass raw Tailwind color classes (`bg-blue-500`, `text-white`, `border-gray-300`) — use `theme` on filters/events or design tokens instead.
|
|
240
|
+
- Don't use arbitrary Tailwind values (`bg-[#abc]`, `bg-[--my-var]`) — override CSS variables in your `@theme` block instead.
|
|
241
|
+
- Don't pack more than a handful of events per day in month view — the grid will overflow; guide users toward week or day view for dense schedules.
|
|
242
|
+
- Don't use extremely long event titles; they are truncated inside the event pill.
|
|
243
|
+
- Don't supply both `filterArea` and `filters` expecting both to render — `filterArea` fully replaces the default filter tag row.
|
|
244
|
+
|
|
245
|
+
## Accessibility
|
|
246
|
+
|
|
247
|
+
- The root container has `role="application"` and `aria-label` from the i18n translation key `pageCalendarLabel`.
|
|
248
|
+
- The header month/year title uses `aria-live="polite"` and `aria-atomic="true"` so screen readers announce date changes after navigation.
|
|
249
|
+
- Navigation buttons (`Previous`, `Today`, `Next`) have `aria-label` attributes for screen reader descriptions.
|
|
250
|
+
- View toggle buttons use `aria-pressed` to communicate the active view.
|
|
251
|
+
- Filter tags are rendered as `<button>` elements with `aria-pressed` and an `aria-label` that includes the enabled/disabled state.
|
|
252
|
+
- Day view hour slots have `role="button"`, `tabIndex={0}`, and `aria-label` for keyboard-accessible slot selection.
|
|
253
|
+
- The day column header uses `aria-label` with the full formatted date string.
|
|
254
|
+
|
|
255
|
+
## Data Attributes
|
|
256
|
+
|
|
257
|
+
- `data-component="day-view-scroller"` — the scrolling container inside the day view. Useful for CSS height overrides:
|
|
258
|
+
|
|
259
|
+
```css
|
|
260
|
+
[data-component="day-view-scroller"] {
|
|
261
|
+
height: calc(100dvh - 200px);
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Notes
|
|
266
|
+
|
|
267
|
+
- Internally uses `date-fns` for all date arithmetic and locale-aware formatting. The locale is read from the `useLocale` hook; set it at the app root via the component provider.
|
|
268
|
+
- All labels (button text, ARIA strings, week number format) are driven by the i18n translation system. Override via the `translations` prop on the root provider.
|
|
269
|
+
- Event filtering is managed internally; `onChangeFilters` lets you mirror the filter state to an external store without controlling it.
|
|
270
|
+
- The day view mini-calendar renders dots beneath days that have events, matching the `bg-primary` token for visual consistency.
|
|
271
|
+
- The component grows to fill its container (`h-full flex-grow`). Place it inside a flex or grid container with an explicit height.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Polymorph
|
|
3
|
+
description: A polymorphic component that renders as any HTML element while maintaining full TypeScript type safety.
|
|
4
|
+
package: "@g4rcez/components"
|
|
5
|
+
export: "{ Polymorph }"
|
|
6
|
+
import: "import { Polymorph } from '@g4rcez/components'"
|
|
7
|
+
category: core
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Polymorph
|
|
11
|
+
|
|
12
|
+
A polymorphic component that renders as any HTML element while maintaining full TypeScript type safety. It is the foundational primitive used internally by `Button`, `Tag`, `Heading`, `RenderOnView`, and other components in the library.
|
|
13
|
+
|
|
14
|
+
## Import
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { Polymorph } from "@g4rcez/components";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Props
|
|
21
|
+
|
|
22
|
+
| Prop | Type | Default | Description |
|
|
23
|
+
|------|------|---------|-------------|
|
|
24
|
+
| `as` | `React.ElementType` | `"span"` | The HTML element or React component to render as |
|
|
25
|
+
| `ref` | `React.Ref` | - | Forwarded ref to the rendered element |
|
|
26
|
+
| `...props` | `React.ComponentPropsWithoutRef<T>` | - | All props valid for the chosen element type |
|
|
27
|
+
|
|
28
|
+
## Type Definitions
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
export type PolymorphicProps<Props, T extends React.ElementType> = Props & {
|
|
32
|
+
as?: T;
|
|
33
|
+
} & Omit<React.ComponentPropsWithoutRef<T>, keyof Props | "as" | "ref"> & {
|
|
34
|
+
ref?: React.ComponentProps<T>["ref"];
|
|
35
|
+
};
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Design Tokens
|
|
39
|
+
|
|
40
|
+
None — Polymorph itself applies no styles. It is a structural primitive only.
|
|
41
|
+
|
|
42
|
+
## Examples
|
|
43
|
+
|
|
44
|
+
### Basic Elements
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
<Polymorph>Default span</Polymorph>
|
|
48
|
+
|
|
49
|
+
<Polymorph as="button" onClick={() => void 0}>
|
|
50
|
+
Button
|
|
51
|
+
</Polymorph>
|
|
52
|
+
|
|
53
|
+
<Polymorph as="a" href="https://example.com" target="_blank">
|
|
54
|
+
External Link
|
|
55
|
+
</Polymorph>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### With Refs
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { useRef } from "react";
|
|
62
|
+
|
|
63
|
+
const MyComponent = () => {
|
|
64
|
+
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<Polymorph
|
|
68
|
+
as="button"
|
|
69
|
+
ref={buttonRef}
|
|
70
|
+
onClick={() => buttonRef.current?.focus()}
|
|
71
|
+
>
|
|
72
|
+
Focus Me
|
|
73
|
+
</Polymorph>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Conditional Element Type
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
const DynamicItem = ({ isLink, href }: { isLink: boolean; href?: string }) => (
|
|
82
|
+
<Polymorph
|
|
83
|
+
as={isLink ? "a" : "button"}
|
|
84
|
+
href={isLink ? href : undefined}
|
|
85
|
+
className="text-foreground"
|
|
86
|
+
>
|
|
87
|
+
{isLink ? "Visit Link" : "Click Button"}
|
|
88
|
+
</Polymorph>
|
|
89
|
+
);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Building Higher-Order Components
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
import { Polymorph, PolymorphicProps } from "@g4rcez/components";
|
|
96
|
+
|
|
97
|
+
type CardProps<T extends React.ElementType = "div"> = PolymorphicProps<
|
|
98
|
+
{ variant?: "default" | "muted" },
|
|
99
|
+
T
|
|
100
|
+
>;
|
|
101
|
+
|
|
102
|
+
const Card = <T extends React.ElementType = "div">({
|
|
103
|
+
as,
|
|
104
|
+
variant = "default",
|
|
105
|
+
className,
|
|
106
|
+
...props
|
|
107
|
+
}: CardProps<T>) => (
|
|
108
|
+
<Polymorph
|
|
109
|
+
as={as ?? "div"}
|
|
110
|
+
className={`rounded-card border border-border ${variant === "muted" ? "bg-muted" : "bg-card-background"} ${className ?? ""}`}
|
|
111
|
+
{...props}
|
|
112
|
+
/>
|
|
113
|
+
);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### TypeScript Safety
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
// Valid — button accepts type and disabled
|
|
120
|
+
<Polymorph as="button" type="submit" disabled />
|
|
121
|
+
|
|
122
|
+
// Valid — anchor accepts href and target
|
|
123
|
+
<Polymorph as="a" href="/link" target="_blank" />
|
|
124
|
+
|
|
125
|
+
// TypeScript error — href is not valid on button
|
|
126
|
+
// <Polymorph as="button" href="/link" />
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Do
|
|
130
|
+
|
|
131
|
+
- Use `Polymorph` as the base when building library components that need element flexibility
|
|
132
|
+
- Always provide a meaningful default for `as` in derived components
|
|
133
|
+
- Forward refs from your wrapper to `Polymorph` so consumers can access the DOM node
|
|
134
|
+
- Use design-token classes for any styling you apply (`text-foreground`, `bg-primary`, etc.)
|
|
135
|
+
|
|
136
|
+
## Don't
|
|
137
|
+
|
|
138
|
+
- Don't use `Polymorph` when the element type is fixed and will never change — use the native element directly
|
|
139
|
+
- Don't pass raw Tailwind color classes (`text-gray-800`, `bg-blue-500`) when building styled wrappers
|
|
140
|
+
- Don't use arbitrary Tailwind values (`text-[#333]`) — override CSS variables in your `@theme` block
|
|
141
|
+
- Don't pass props that are invalid for the target element (TypeScript will flag these at compile time)
|
|
142
|
+
|
|
143
|
+
## Accessibility
|
|
144
|
+
|
|
145
|
+
- `Polymorph` adds no ARIA attributes of its own — the rendered element's semantics are determined by the `as` prop
|
|
146
|
+
- When `as="button"` is omitted and you need interactive behavior, prefer `as="button"` over `as="div"` with an `onClick`
|
|
147
|
+
- Refs are forwarded correctly, enabling programmatic focus management
|
|
148
|
+
|
|
149
|
+
## Data Attributes
|
|
150
|
+
|
|
151
|
+
- No data attributes are set by `Polymorph` itself
|
|
152
|
+
- Components built on top of `Polymorph` (e.g., `Button`, `Tag`) add their own `data-component` attribute
|
|
153
|
+
|
|
154
|
+
## Notes
|
|
155
|
+
|
|
156
|
+
- The default element is `<span>` when no `as` prop is provided
|
|
157
|
+
- The `as` prop is stripped before forwarding to the DOM element to prevent unknown attribute warnings
|
|
158
|
+
- Refs are forwarded via `React.forwardRef`
|
|
159
|
+
- `PolymorphicProps` is exported and can be used to type custom components built on `Polymorph`
|