@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.
Files changed (176) hide show
  1. package/dist/ai/SKILL.md +266 -0
  2. package/dist/ai/docs/Alert.md +167 -0
  3. package/dist/ai/docs/AnimatedList.md +205 -0
  4. package/dist/ai/docs/Autocomplete.md +225 -0
  5. package/dist/ai/docs/Button.md +182 -0
  6. package/dist/ai/docs/Calendar.md +219 -0
  7. package/dist/ai/docs/Card.md +174 -0
  8. package/dist/ai/docs/Checkbox.md +199 -0
  9. package/dist/ai/docs/CommandPalette.md +293 -0
  10. package/dist/ai/docs/DatePicker.md +171 -0
  11. package/dist/ai/docs/Dropdown.md +223 -0
  12. package/dist/ai/docs/Empty.md +163 -0
  13. package/dist/ai/docs/Expand.md +143 -0
  14. package/dist/ai/docs/FileUpload.md +225 -0
  15. package/dist/ai/docs/Form.md +107 -0
  16. package/dist/ai/docs/FormReset.md +117 -0
  17. package/dist/ai/docs/Heading.md +88 -0
  18. package/dist/ai/docs/Input.md +237 -0
  19. package/dist/ai/docs/InputField.md +170 -0
  20. package/dist/ai/docs/List.md +205 -0
  21. package/dist/ai/docs/Menu.md +166 -0
  22. package/dist/ai/docs/Modal.md +280 -0
  23. package/dist/ai/docs/MultiSelect.md +196 -0
  24. package/dist/ai/docs/Notifications.md +231 -0
  25. package/dist/ai/docs/PageCalendar.md +271 -0
  26. package/dist/ai/docs/Polymorph.md +159 -0
  27. package/dist/ai/docs/Progress.md +145 -0
  28. package/dist/ai/docs/Radiobox.md +128 -0
  29. package/dist/ai/docs/RenderOnView.md +138 -0
  30. package/dist/ai/docs/Resizable.md +159 -0
  31. package/dist/ai/docs/Select.md +284 -0
  32. package/dist/ai/docs/Shortcut.md +105 -0
  33. package/dist/ai/docs/Skeleton.md +166 -0
  34. package/dist/ai/docs/Slider.md +144 -0
  35. package/dist/ai/docs/Slot.md +173 -0
  36. package/dist/ai/docs/Spinner.md +118 -0
  37. package/dist/ai/docs/Stats.md +137 -0
  38. package/dist/ai/docs/Step.md +159 -0
  39. package/dist/ai/docs/Switch.md +167 -0
  40. package/dist/ai/docs/Table.md +298 -0
  41. package/dist/ai/docs/Tabs.md +191 -0
  42. package/dist/ai/docs/Tag.md +224 -0
  43. package/dist/ai/docs/TaskList.md +144 -0
  44. package/dist/ai/docs/Textarea.md +167 -0
  45. package/dist/ai/docs/Timeline.md +210 -0
  46. package/dist/ai/docs/Toolbar.md +132 -0
  47. package/dist/ai/docs/Tooltip.md +231 -0
  48. package/dist/ai/docs/TransferList.md +142 -0
  49. package/dist/ai/docs/Typography.md +187 -0
  50. package/dist/ai/docs/Wizard.md +213 -0
  51. package/dist/ai/docs/index.md +183 -0
  52. package/dist/components/core/tag.d.ts +1 -1
  53. package/dist/components/core/tag.d.ts.map +1 -1
  54. package/dist/components/display/list.d.ts.map +1 -1
  55. package/dist/components/floating/dropdown.d.ts +1 -0
  56. package/dist/components/floating/dropdown.d.ts.map +1 -1
  57. package/dist/components/floating/menu.d.ts.map +1 -1
  58. package/dist/config/default-translations.d.ts +4 -4
  59. package/dist/hooks/use-translations.d.ts +4 -4
  60. package/dist/hooks/use-translations.d.ts.map +1 -1
  61. package/dist/index.css +1 -1
  62. package/dist/index.js +28 -20
  63. package/dist/index.js.map +1 -1
  64. package/dist/index.mjs +2463 -2458
  65. package/dist/index.mjs.map +1 -1
  66. package/dist/index.umd.js +12 -12
  67. package/dist/index.umd.js.map +1 -1
  68. package/package.json +4 -4
  69. package/dist/components/core/button.jsx +0 -79
  70. package/dist/components/core/heading.jsx +0 -4
  71. package/dist/components/core/polymorph.jsx +0 -5
  72. package/dist/components/core/render-on-view.jsx +0 -31
  73. package/dist/components/core/resizable.jsx +0 -51
  74. package/dist/components/core/slot.jsx +0 -156
  75. package/dist/components/core/tag.jsx +0 -51
  76. package/dist/components/core/typography.jsx +0 -22
  77. package/dist/components/display/alert.jsx +0 -58
  78. package/dist/components/display/calendar.jsx +0 -299
  79. package/dist/components/display/card.jsx +0 -43
  80. package/dist/components/display/empty.jsx +0 -11
  81. package/dist/components/display/list.jsx +0 -81
  82. package/dist/components/display/notifications.jsx +0 -126
  83. package/dist/components/display/progress.jsx +0 -11
  84. package/dist/components/display/shortcut.jsx +0 -23
  85. package/dist/components/display/skeleton.jsx +0 -12
  86. package/dist/components/display/spinner.jsx +0 -7
  87. package/dist/components/display/stats.jsx +0 -20
  88. package/dist/components/display/step.jsx +0 -131
  89. package/dist/components/display/tabs.jsx +0 -98
  90. package/dist/components/display/timeline.jsx +0 -25
  91. package/dist/components/floating/command-palette.jsx +0 -194
  92. package/dist/components/floating/dropdown.jsx +0 -53
  93. package/dist/components/floating/expand.jsx +0 -44
  94. package/dist/components/floating/menu.jsx +0 -147
  95. package/dist/components/floating/modal.jsx +0 -299
  96. package/dist/components/floating/toolbar.jsx +0 -5
  97. package/dist/components/floating/tooltip.jsx +0 -58
  98. package/dist/components/floating/wizard.jsx +0 -161
  99. package/dist/components/form/autocomplete.jsx +0 -279
  100. package/dist/components/form/checkbox.jsx +0 -12
  101. package/dist/components/form/date-picker.jsx +0 -115
  102. package/dist/components/form/file-upload.jsx +0 -133
  103. package/dist/components/form/form.jsx +0 -10
  104. package/dist/components/form/formReset.jsx +0 -17
  105. package/dist/components/form/free-text.jsx +0 -41
  106. package/dist/components/form/input-field.jsx +0 -56
  107. package/dist/components/form/input.jsx +0 -36
  108. package/dist/components/form/multi-select.jsx +0 -328
  109. package/dist/components/form/radiobox.jsx +0 -6
  110. package/dist/components/form/select.jsx +0 -42
  111. package/dist/components/form/slider.jsx +0 -45
  112. package/dist/components/form/switch.jsx +0 -46
  113. package/dist/components/form/task-list.jsx +0 -26
  114. package/dist/components/form/textarea.jsx +0 -12
  115. package/dist/components/form/transfer-list.jsx +0 -39
  116. package/dist/components/index.js +0 -45
  117. package/dist/components/page-calendar/calendar-header.jsx +0 -81
  118. package/dist/components/page-calendar/day-view.jsx +0 -87
  119. package/dist/components/page-calendar/event-pill.jsx +0 -25
  120. package/dist/components/page-calendar/index.js +0 -2
  121. package/dist/components/page-calendar/month-view.jsx +0 -47
  122. package/dist/components/page-calendar/page-calendar.jsx +0 -41
  123. package/dist/components/page-calendar/page-calendar.types.js +0 -1
  124. package/dist/components/page-calendar/page-calendar.utils.js +0 -71
  125. package/dist/components/page-calendar/week-view.jsx +0 -64
  126. package/dist/components/table/filter.jsx +0 -141
  127. package/dist/components/table/group.jsx +0 -68
  128. package/dist/components/table/index.jsx +0 -60
  129. package/dist/components/table/inner-table.jsx +0 -104
  130. package/dist/components/table/metadata.jsx +0 -36
  131. package/dist/components/table/pagination.jsx +0 -73
  132. package/dist/components/table/row.jsx +0 -58
  133. package/dist/components/table/sort.jsx +0 -105
  134. package/dist/components/table/table-lib.js +0 -83
  135. package/dist/components/table/table.context.jsx +0 -4
  136. package/dist/components/table/thead.jsx +0 -103
  137. package/dist/config/context.js +0 -12
  138. package/dist/config/default-translations.jsx +0 -83
  139. package/dist/config/default-tweaks.js +0 -4
  140. package/dist/constants.js +0 -2
  141. package/dist/hooks/use-click-outside.js +0 -17
  142. package/dist/hooks/use-color-parser.js +0 -9
  143. package/dist/hooks/use-components-provider.jsx +0 -19
  144. package/dist/hooks/use-debounce.js +0 -12
  145. package/dist/hooks/use-floating-ref.js +0 -6
  146. package/dist/hooks/use-form.js +0 -550
  147. package/dist/hooks/use-hover.js +0 -18
  148. package/dist/hooks/use-input-id.js +0 -5
  149. package/dist/hooks/use-is-coarse-device.js +0 -12
  150. package/dist/hooks/use-locale.js +0 -10
  151. package/dist/hooks/use-media-query.js +0 -25
  152. package/dist/hooks/use-on-event.js +0 -7
  153. package/dist/hooks/use-parent.js +0 -21
  154. package/dist/hooks/use-preferences.js +0 -23
  155. package/dist/hooks/use-previous.js +0 -9
  156. package/dist/hooks/use-reactive.js +0 -9
  157. package/dist/hooks/use-remove-scroll.js +0 -61
  158. package/dist/hooks/use-resize-observer.js +0 -17
  159. package/dist/hooks/use-stable-ref.js +0 -9
  160. package/dist/hooks/use-swipe.js +0 -17
  161. package/dist/hooks/use-translations.js +0 -9
  162. package/dist/hooks/use-tweaks.js +0 -9
  163. package/dist/hooks/use-window-size.js +0 -14
  164. package/dist/lib/combi-keys.js +0 -60
  165. package/dist/lib/dict.js +0 -39
  166. package/dist/lib/dom.js +0 -62
  167. package/dist/lib/fns.js +0 -46
  168. package/dist/lib/fzf.js +0 -117
  169. package/dist/lib/keyboard-area.js +0 -14
  170. package/dist/styles/common.js +0 -29
  171. package/dist/styles/dark.js +0 -214
  172. package/dist/styles/design-tokens.js +0 -69
  173. package/dist/styles/light.js +0 -214
  174. package/dist/styles/theme.js +0 -4
  175. package/dist/styles/theme.types.js +0 -1
  176. 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`