@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,166 @@
1
+ ---
2
+ title: Menu
3
+ description: Accessible floating menu system with nested submenus, keyboard navigation, hover support, and typeahead.
4
+ package: "@g4rcez/components"
5
+ export: "{ Menu, MenuItem }"
6
+ import: "import { Menu, MenuItem } from '@g4rcez/components/menu'"
7
+ category: floating
8
+ ---
9
+
10
+ # Menu
11
+
12
+ Accessible floating menu system with nested submenus, keyboard navigation, hover support, and typeahead.
13
+
14
+ ## Import
15
+
16
+ ```tsx
17
+ import { Menu, MenuItem } from "@g4rcez/components/menu";
18
+ ```
19
+
20
+ ## Props
21
+
22
+ ### Menu
23
+
24
+ | Prop | Type | Default | Description |
25
+ |------|------|---------|-------------|
26
+ | `label` | `string \| React.ReactElement` | — | Trigger content |
27
+ | `title` | `string` | — | Required for accessibility when `label` is an element; also used for typeahead |
28
+ | `hover` | `boolean` | `true` | Open on hover in addition to click |
29
+ | `open` | `boolean` | `false` | Initial open state |
30
+ | `asChild` | `boolean` | `false` | Use the `Slot` pattern — merge props onto the child element instead of wrapping in a `<button>` |
31
+ | `restoreFocus` | `boolean` | `false` | Restore focus to the trigger after the menu closes |
32
+ | `floatingClassName` | `string` | — | Additional CSS classes for the floating list container |
33
+ | `FloatingComponent` | `React.ElementType` | `"div"` | Element type for the floating container |
34
+
35
+ ### MenuItem
36
+
37
+ | Prop | Type | Default | Description |
38
+ |------|------|---------|-------------|
39
+ | `title` | `string` | — | Item text; used for typeahead matching and the `title` attribute |
40
+ | `children` | `React.ReactNode` | — | Visual content of the item |
41
+ | `disabled` | `boolean` | `false` | Removes item from keyboard navigation and typeahead |
42
+ | `Right` | `React.FC<LucideProps>` | — | Icon rendered on the right side |
43
+ | `onClick` | `function` | — | Click handler |
44
+
45
+ ## Design Tokens
46
+
47
+ Tokens this component reads. Customize by overriding these CSS variables in your theme.
48
+
49
+ | Token | CSS Variable | Purpose |
50
+ |-------|-------------|---------|
51
+ | `bg-floating-background` | `--floating-background` | Menu list surface background |
52
+ | `border-floating-border` | `--floating-border` | Menu list border |
53
+ | `shadow-shadow-floating` | `--shadow-floating` | Menu list drop shadow |
54
+ | `z-tooltip` | `--z-tooltip` | Z-index of the floating list |
55
+ | `bg-primary` | `--primary` | Active/focused item background |
56
+ | `text-primary-foreground` | `--primary-foreground` | Active/focused item text |
57
+
58
+ ## Examples
59
+
60
+ ### Basic Menu
61
+
62
+ ```tsx
63
+ import { Menu, MenuItem } from "@g4rcez/components/menu";
64
+
65
+ <Menu label="Actions">
66
+ <MenuItem title="Edit">Edit Profile</MenuItem>
67
+ <MenuItem title="Share">Share Profile</MenuItem>
68
+ </Menu>
69
+ ```
70
+
71
+ ### With Icons and Shortcuts
72
+
73
+ ```tsx
74
+ import { EditIcon, TrashIcon } from "lucide-react";
75
+ import { Menu, MenuItem } from "@g4rcez/components/menu";
76
+
77
+ <Menu label="Settings">
78
+ <MenuItem title="Edit" Right={EditIcon}>Edit</MenuItem>
79
+ <MenuItem title="Delete" Right={TrashIcon} className="text-danger">
80
+ Delete
81
+ </MenuItem>
82
+ </Menu>
83
+ ```
84
+
85
+ ### Nested Submenus
86
+
87
+ ```tsx
88
+ import { Menu, MenuItem } from "@g4rcez/components/menu";
89
+
90
+ <Menu label="Actions">
91
+ <MenuItem title="Edit">Edit Profile</MenuItem>
92
+ <MenuItem title="Share">Share Profile</MenuItem>
93
+ <Menu label="More Options" title="More Options">
94
+ <MenuItem title="Archive">Archive Account</MenuItem>
95
+ <MenuItem title="Delete" className="text-danger">
96
+ Delete Account
97
+ </MenuItem>
98
+ </Menu>
99
+ </Menu>
100
+ ```
101
+
102
+ ### Using asChild for Custom Triggers
103
+
104
+ ```tsx
105
+ import { Menu, MenuItem } from "@g4rcez/components/menu";
106
+ import { Button } from "@g4rcez/components/button";
107
+
108
+ <Menu
109
+ label={<Button theme="primary">Main Action</Button>}
110
+ asChild
111
+ title="Main Action"
112
+ >
113
+ <MenuItem title="Save">Save Version</MenuItem>
114
+ <MenuItem title="Publish">Publish Now</MenuItem>
115
+ </Menu>
116
+ ```
117
+
118
+ ### Disabled Items
119
+
120
+ ```tsx
121
+ import { Menu, MenuItem } from "@g4rcez/components/menu";
122
+
123
+ <Menu label="Options">
124
+ <MenuItem title="Export">Export Data</MenuItem>
125
+ <MenuItem title="Import" disabled>Import (unavailable)</MenuItem>
126
+ <MenuItem title="Delete" className="text-danger">Delete</MenuItem>
127
+ </Menu>
128
+ ```
129
+
130
+ ## Do
131
+
132
+ - Provide a `title` string whenever `label` is a React element — it enables typeahead and improves accessibility.
133
+ - Use nested `Menu` components (not just `MenuItem`) for logical groupings of secondary actions.
134
+ - Use `disabled` for actions temporarily unavailable, so users know the option exists.
135
+ - Use design-token classes (`text-danger`, `text-foreground`) for item text colors.
136
+
137
+ ## Don't
138
+
139
+ - Don't make menus deeper than 2–3 levels — deeply nested submenus are hard to navigate.
140
+ - Don't use raw Tailwind color classes (`text-red-600`, `hover:bg-gray-100`) on items — use design-token classes.
141
+ - Don't use arbitrary Tailwind values (`bg-[#abc]`) — override CSS variables in your `@theme` block.
142
+ - Don't use `Menu` for primary navigation that should be crawlable — use standard `<a>` links instead.
143
+
144
+ ## Accessibility
145
+
146
+ - Full keyboard navigation: arrow keys navigate items, `Enter`/`Space` select, `Escape` closes.
147
+ - Typeahead: typing the first characters of an item's `title` focuses it instantly (resets after inactivity).
148
+ - Correct ARIA roles: `menu` on the container, `menuitem` on each item, `aria-expanded` on nested triggers.
149
+ - `data-active` marks the currently focused item for CSS styling.
150
+ - `FloatingFocusManager` handles focus trapping and restoration.
151
+
152
+ ## Data Attributes
153
+
154
+ | Attribute | Applied to | Description |
155
+ |-----------|-----------|-------------|
156
+ | `data-open` | Menu trigger, MenuItem | Present when the menu/item is open |
157
+ | `data-nested` | Nested menu trigger | Present on triggers that open a submenu |
158
+ | `data-focus-inside` | Menu trigger | Present when focus is inside an open submenu |
159
+ | `data-active` | MenuItem | Present on the currently focused item |
160
+
161
+ ## Notes
162
+
163
+ - The root `Menu` wraps itself in a `FloatingTree`; nested `Menu` components detect this via `useFloatingParentNodeId` and behave as submenus.
164
+ - Hover delay is controlled by the `FLOATING_DELAY` constant. The safe-polygon handler keeps the menu open while the pointer moves toward the submenu.
165
+ - When `hover` is `false`, the menu opens only on click.
166
+ - The floating container default `max-h-80` prevents the list from growing taller than the viewport.
@@ -0,0 +1,280 @@
1
+ ---
2
+ title: Modal
3
+ description: Animated modal with dialog, drawer, and sheet variants; includes drag-to-resize and a programmatic confirm utility.
4
+ package: "@g4rcez/components"
5
+ export: "{ Modal, ModalConfirmProvider, useConfirm }"
6
+ import: "import { Modal } from '@g4rcez/components/modal'"
7
+ category: floating
8
+ ---
9
+
10
+ # Modal
11
+
12
+ Animated modal with dialog, drawer, and sheet variants; includes drag-to-resize and a programmatic confirm utility.
13
+
14
+ ## Import
15
+
16
+ ```tsx
17
+ import { Modal, ModalConfirmProvider, useConfirm } from "@g4rcez/components/modal";
18
+ ```
19
+
20
+ ## Props
21
+
22
+ | Prop | Type | Default | Description |
23
+ |------|------|---------|-------------|
24
+ | `open` | `boolean` | — | Controls modal visibility |
25
+ | `onChange` | `(nextState: boolean) => void` | — | Callback when modal state changes |
26
+ | `title` | `React.ReactNode` | — | Modal title; creates a `<h2>` header |
27
+ | `ariaTitle` | `string` | — | ARIA label used when no visible `title` is provided |
28
+ | `footer` | `React.ReactNode` | — | Footer content |
29
+ | `type` | `"dialog" \| "drawer" \| "sheet"` | `"dialog"` | Modal display variant |
30
+ | `position` | `"left" \| "right"` | `"right"` | Drawer slide-in side (drawer type only) |
31
+ | `animated` | `boolean` | `true` | Enable enter/exit animations |
32
+ | `closable` | `boolean` | `true` | Show the close button |
33
+ | `resizer` | `boolean` | `true` | Show the drag-to-resize handle (drawer and sheet) |
34
+ | `forceType` | `boolean` | `false` | Disable responsive behavior — keep `type` on all screen sizes |
35
+ | `overlayClickClose` | `boolean` | `false` | Close when clicking the backdrop |
36
+ | `trigger` | `React.ReactNode \| React.FC` | — | Element that toggles the modal when clicked |
37
+ | `asChild` | `boolean` | `false` | Merge trigger props onto the child element via `Slot` |
38
+ | `className` | `string` | — | Additional classes for the modal surface |
39
+ | `bodyClassName` | `string` | — | Additional classes for the scrollable body |
40
+ | `overlayClassName` | `string` | — | Additional classes for the backdrop overlay |
41
+ | `layoutId` | `string` | — | Framer Motion layout ID for shared-element transitions |
42
+ | `role` | `"dialog"` | `"dialog"` | ARIA role |
43
+ | `interactions` | `ElementProps[]` | `[]` | Extra Floating UI interaction hooks |
44
+
45
+ ## Modal Types
46
+
47
+ | Type | Behavior | Best for |
48
+ |------|----------|---------|
49
+ | `dialog` | Centered overlay with backdrop | Confirmations, forms |
50
+ | `drawer` | Slides in from left or right; full height | Navigation, detail panels |
51
+ | `sheet` | Slides up from bottom; full width | Mobile action sheets |
52
+
53
+ On mobile (`< 64 rem`) drawers automatically become sheets unless `forceType` is set.
54
+
55
+ ## Design Tokens
56
+
57
+ Tokens this component reads. Customize by overriding these CSS variables in your theme.
58
+
59
+ | Token | CSS Variable | Purpose |
60
+ |-------|-------------|---------|
61
+ | `bg-floating-background` | `--floating-background` | Modal surface background |
62
+ | `border-floating-border` | `--floating-border` | Modal border, header/footer dividers, resizer |
63
+ | `bg-floating-overlay` | `--floating-overlay` | Backdrop color (with `/70` opacity) |
64
+ | `z-overlay` | `--z-overlay` | Z-index of the backdrop |
65
+ | `z-floating` | `--z-floating` | Z-index of the modal surface and close button |
66
+ | `w-dialog` | `--dialog` | Default max-width for dialog type (`max-w-dialog`) |
67
+ | `text-foreground` | `--foreground` | Body text color |
68
+ | `text-danger` | `--danger` | Close button hover color |
69
+
70
+ ## Examples
71
+
72
+ ### Basic Dialog
73
+
74
+ ```tsx
75
+ import { useState } from "react";
76
+ import { Modal } from "@g4rcez/components/modal";
77
+ import { Button } from "@g4rcez/components/button";
78
+
79
+ function BasicDialog() {
80
+ const [open, setOpen] = useState(false);
81
+
82
+ return (
83
+ <>
84
+ <Button onClick={() => setOpen(true)}>Open Dialog</Button>
85
+
86
+ <Modal open={open} onChange={setOpen} title="Confirm Action">
87
+ <p className="text-foreground">Are you sure you want to proceed?</p>
88
+ <div className="flex gap-2 mt-4">
89
+ <Button theme="ghost-muted" onClick={() => setOpen(false)}>
90
+ Cancel
91
+ </Button>
92
+ <Button theme="danger" onClick={() => setOpen(false)}>
93
+ Confirm
94
+ </Button>
95
+ </div>
96
+ </Modal>
97
+ </>
98
+ );
99
+ }
100
+ ```
101
+
102
+ ### Modal with Footer
103
+
104
+ ```tsx
105
+ <Modal
106
+ open={open}
107
+ onChange={setOpen}
108
+ title="User Profile"
109
+ footer={
110
+ <div className="flex justify-end gap-2">
111
+ <Button theme="ghost-muted" onClick={() => setOpen(false)}>
112
+ Cancel
113
+ </Button>
114
+ <Button theme="primary">Save Changes</Button>
115
+ </div>
116
+ }
117
+ >
118
+ <form className="space-y-4">
119
+ <div>
120
+ <label className="text-sm font-medium text-foreground">Name</label>
121
+ <input type="text" className="w-full p-2 rounded-button border border-border bg-background text-foreground" />
122
+ </div>
123
+ </form>
124
+ </Modal>
125
+ ```
126
+
127
+ ### Drawer
128
+
129
+ ```tsx
130
+ <Modal
131
+ open={open}
132
+ onChange={setOpen}
133
+ type="drawer"
134
+ position="right"
135
+ title="Navigation"
136
+ >
137
+ <nav className="space-y-2">
138
+ <a href="/dashboard" className="block rounded-button p-2 text-foreground hover:bg-muted">
139
+ Dashboard
140
+ </a>
141
+ <a href="/profile" className="block rounded-button p-2 text-foreground hover:bg-muted">
142
+ Profile
143
+ </a>
144
+ <a href="/settings" className="block rounded-button p-2 text-foreground hover:bg-muted">
145
+ Settings
146
+ </a>
147
+ </nav>
148
+ </Modal>
149
+ ```
150
+
151
+ ### Sheet
152
+
153
+ ```tsx
154
+ <Modal open={open} onChange={setOpen} type="sheet" title="Quick Actions">
155
+ <div className="grid grid-cols-2 gap-4">
156
+ <Button theme="ghost-neutral" className="flex-col h-24">
157
+ Analytics
158
+ </Button>
159
+ <Button theme="ghost-neutral" className="flex-col h-24">
160
+ Revenue
161
+ </Button>
162
+ </div>
163
+ </Modal>
164
+ ```
165
+
166
+ ### Modal with Built-in Trigger
167
+
168
+ ```tsx
169
+ <Modal
170
+ open={open}
171
+ onChange={setOpen}
172
+ title="Settings"
173
+ trigger={<Button theme="primary">Open Settings</Button>}
174
+ >
175
+ <p className="text-foreground">Settings content here.</p>
176
+ </Modal>
177
+ ```
178
+
179
+ ### Non-closable Processing Modal
180
+
181
+ ```tsx
182
+ <Modal open={open} onChange={setOpen} title="Processing..." closable={false}>
183
+ <div className="text-center text-foreground">
184
+ <div className="animate-spin w-8 h-8 border-4 border-primary border-t-transparent rounded-full mx-auto mb-4" />
185
+ <p>Please wait while we process your request.</p>
186
+ </div>
187
+ </Modal>
188
+ ```
189
+
190
+ ### Programmatic Confirm Dialog
191
+
192
+ Wrap your app with `ModalConfirmProvider` once:
193
+
194
+ ```tsx
195
+ import { ModalConfirmProvider } from "@g4rcez/components/modal";
196
+
197
+ export default function App({ children }) {
198
+ return <ModalConfirmProvider>{children}</ModalConfirmProvider>;
199
+ }
200
+ ```
201
+
202
+ Then call `Modal.confirm` anywhere:
203
+
204
+ ```tsx
205
+ import { Modal } from "@g4rcez/components/modal";
206
+
207
+ async function handleDelete() {
208
+ const confirmed = await Modal.confirm({
209
+ title: "Delete item",
210
+ description: "This action cannot be undone.",
211
+ confirm: { text: "Delete", theme: "danger" },
212
+ cancel: { text: "Cancel" },
213
+ });
214
+
215
+ if (confirmed) {
216
+ // proceed with deletion
217
+ }
218
+ }
219
+ ```
220
+
221
+ Or use the `useConfirm` hook inside a `ModalConfirmProvider` subtree:
222
+
223
+ ```tsx
224
+ import { useConfirm } from "@g4rcez/components/modal";
225
+
226
+ function DeleteButton() {
227
+ const confirm = useConfirm();
228
+
229
+ const handleClick = async () => {
230
+ const ok = await confirm({
231
+ title: "Delete item",
232
+ description: "Are you sure?",
233
+ });
234
+ if (ok) {
235
+ // delete
236
+ }
237
+ };
238
+
239
+ return <Button theme="danger" onClick={handleClick}>Delete</Button>;
240
+ }
241
+ ```
242
+
243
+ ## Do
244
+
245
+ - Choose the correct `type` (`dialog`, `drawer`, `sheet`) for the content and context.
246
+ - Use `<Modal type="dialog" />` as the default — only switch to `drawer` or `sheet` when layout demands it.
247
+ - Always provide either `title` or `ariaTitle` so screen readers can announce the modal.
248
+ - Use `footer` for action buttons to keep them separated from body content.
249
+
250
+ ## Don't
251
+
252
+ - Don't pass custom `z-*`, `rounded-*`, or `p-*` overrides directly — use `type` to control layout variants.
253
+ - Don't use raw Tailwind color classes (`bg-blue-500`, `text-white`, `bg-gray-100`) — use design-token classes.
254
+ - Don't use arbitrary Tailwind values (`bg-[#abc]`, `z-[9999]`) — override CSS variables in your `@theme` block.
255
+ - Don't use `color-mix()` in `className` or `style` props.
256
+ - Don't nest modals inside each other — it creates broken focus and z-index behavior.
257
+ - Don't hide a modal's only close affordance (`closable={false}`) without providing another way to close.
258
+
259
+ ## Accessibility
260
+
261
+ - Focus is automatically trapped inside the modal via `FloatingFocusManager` with `guards` and `modal`.
262
+ - `Escape` closes the modal (via `useDismiss` with `escapeKey: true`).
263
+ - The modal surface receives `aria-labelledby` (when `title` is set) or `aria-label` (when `ariaTitle` is set).
264
+ - `aria-modal="true"` is applied to the floating surface.
265
+ - Scroll is locked on the body while the modal is open.
266
+
267
+ ## Data Attributes
268
+
269
+ | Attribute | Applied to | Description |
270
+ |-----------|-----------|-------------|
271
+ | `data-component="modal"` | Modal surface `<div>` | Identifies the modal root |
272
+ | `data-component="modal-body"` | Scrollable body `<section>` | Identifies the content area |
273
+
274
+ ## Notes
275
+
276
+ - Drawers auto-switch to sheets on viewports narrower than `64rem` unless `forceType={true}`.
277
+ - `resizer` adds a draggable handle: horizontal for drawers, vertical for sheets. Dragging a sheet past 60 % of screen height closes it.
278
+ - `layoutId` enables Framer Motion shared-element transitions between a trigger and the modal surface.
279
+ - `ModalConfirmProvider` sets a module-level `confirmGlobal` function so `Modal.confirm` works outside React trees (e.g., in event handlers).
280
+ - The confirm dialog uses `max-w-dialog` (`w-dialog` token, default `20rem`) and cannot be closed by clicking the overlay.
@@ -0,0 +1,196 @@
1
+ ---
2
+ title: MultiSelect
3
+ description: Dropdown that lets users select multiple options with fuzzy search and tag display.
4
+ package: "@g4rcez/components"
5
+ export: "{ MultiSelect }"
6
+ import: "import { MultiSelect } from '@g4rcez/components'"
7
+ category: form
8
+ ---
9
+
10
+ # MultiSelect
11
+
12
+ Dropdown that lets users select multiple options with fuzzy search and tag display.
13
+
14
+ ## Import
15
+
16
+ ```tsx
17
+ import { MultiSelect } from "@g4rcez/components";
18
+ ```
19
+
20
+ ## Props
21
+
22
+ The `MultiSelect` component inherits all props from `InputField`, plus:
23
+
24
+ | Prop | Type | Default | Description |
25
+ |------|------|---------|-------------|
26
+ | `options` | `MultiSelectItemProps[]` | — | Array of `{ value, label, Render? }` option objects. |
27
+ | `value` | `string[]` | — | Controlled selected values. |
28
+ | `defaultValue` | `string[]` | `[]` | Initial selected values for uncontrolled usage. |
29
+ | `onChangeOptions` | `(options: string[]) => void` | — | Called when the selection changes. |
30
+ | `dynamicOption` | `boolean` | `false` | Allows users to select their search query as a new option. |
31
+ | `emptyMessage` | `Label` | — | Message shown when no options match the search. |
32
+ | `selectedLabel` | `string` | — | Text shown in the overflow counter (e.g., "selected"). |
33
+
34
+ ### MultiSelectItemProps
35
+
36
+ Extends `OptionProps` with an optional custom renderer:
37
+
38
+ | Field | Type | Description |
39
+ |-------|------|-------------|
40
+ | `value` | `string` | Option value. |
41
+ | `label` | `string` | Option display text. |
42
+ | `Render` | `React.FC<OptionProps>` | Custom render component for the dropdown row. |
43
+ | `hidden` | `boolean` | Hides the option from the list. |
44
+
45
+ ## Design Tokens
46
+
47
+ Tokens this component reads. Customize by overriding these CSS variables in your theme.
48
+
49
+ | Token | CSS Variable | Purpose |
50
+ |-------|-------------|---------|
51
+ | `placeholder-input-mask` | `--input-mask` | Placeholder text color |
52
+ | `placeholder-input-mask-error` | `--input-mask-error` | Placeholder color in error state |
53
+ | `border-input-border` | `--input-border` | Search input bottom border in dropdown |
54
+ | `bg-floating-background` | `--floating-background` | Dropdown panel background |
55
+ | `border-floating-border` | `--floating-border` | Dropdown panel border |
56
+ | `bg-floating-hover` | `--floating-hover` | Option row hover/active background |
57
+ | `text-foreground` | `--foreground` | Option text color |
58
+ | `text-input-placeholder` | `--input-placeholder` | Placeholder li color |
59
+ | `text-disabled` | `--disabled` | Empty-state text color |
60
+ | `focus:ring-primary` | `--primary` | Keyboard focus ring |
61
+ | `h-input-height` | `--input-height` | Trigger element height (2.5 rem) |
62
+ | `px-input-x` | `--input-x` | Horizontal padding |
63
+ | `py-input-y` | `--input-y` | Vertical padding |
64
+
65
+ ## Examples
66
+
67
+ ### Basic usage
68
+
69
+ ```tsx
70
+ import { MultiSelect } from "@g4rcez/components";
71
+
72
+ const options = [
73
+ { value: "react", label: "React" },
74
+ { value: "vue", label: "Vue" },
75
+ { value: "angular", label: "Angular" },
76
+ ];
77
+
78
+ export default function FrameworkPicker() {
79
+ return (
80
+ <MultiSelect
81
+ title="Frameworks"
82
+ options={options}
83
+ onChangeOptions={(values) => console.log(values)}
84
+ />
85
+ );
86
+ }
87
+ ```
88
+
89
+ ### Controlled selection
90
+
91
+ ```tsx
92
+ import { useState } from "react";
93
+ import { MultiSelect } from "@g4rcez/components";
94
+
95
+ export default function ControlledMultiSelect() {
96
+ const [selected, setSelected] = useState<string[]>(["react"]);
97
+
98
+ return (
99
+ <MultiSelect
100
+ title="Frameworks"
101
+ options={[
102
+ { value: "react", label: "React" },
103
+ { value: "vue", label: "Vue" },
104
+ { value: "angular", label: "Angular" },
105
+ ]}
106
+ value={selected}
107
+ onChangeOptions={setSelected}
108
+ />
109
+ );
110
+ }
111
+ ```
112
+
113
+ ### Custom option rendering
114
+
115
+ ```tsx
116
+ import { ShieldIcon } from "lucide-react";
117
+ import { MultiSelect } from "@g4rcez/components";
118
+
119
+ const roleOptions = roles.map((r) => ({
120
+ value: r.id,
121
+ label: r.name,
122
+ Render: () => (
123
+ <span className="flex items-center gap-base">
124
+ <ShieldIcon size={14} className="text-primary" />
125
+ {r.name}
126
+ </span>
127
+ ),
128
+ }));
129
+
130
+ export default function RolePicker() {
131
+ return (
132
+ <MultiSelect
133
+ title="Roles"
134
+ options={roleOptions}
135
+ onChangeOptions={(ids) => console.log(ids)}
136
+ />
137
+ );
138
+ }
139
+ ```
140
+
141
+ ### Dynamic option creation
142
+
143
+ ```tsx
144
+ import { MultiSelect } from "@g4rcez/components";
145
+
146
+ export default function TagInput() {
147
+ return (
148
+ <MultiSelect
149
+ title="Tags"
150
+ dynamicOption
151
+ options={existingTags}
152
+ onChangeOptions={(tags) => console.log(tags)}
153
+ />
154
+ );
155
+ }
156
+ ```
157
+
158
+ ## Do
159
+
160
+ - Provide a descriptive `title` to label the trigger.
161
+ - Use `dynamicOption` when users may need to add items not in the predefined list.
162
+ - Use design-token classes for any wrapper elements (`bg-background`, `border-border`, `text-foreground`).
163
+ - Use `theme="<value>"` on nested elements — never hardcode colors in `className`.
164
+
165
+ ## Don't
166
+
167
+ - Don't use `MultiSelect` when only a single selection is needed — use `Select` or `Autocomplete` instead.
168
+ - Don't pass raw Tailwind color classes (`bg-blue-500`, `text-white`) — use theme props or design tokens instead.
169
+ - Don't use arbitrary Tailwind values (`bg-[#abc]`, `bg-[--my-var]`) — override CSS variables in your `@theme` block instead.
170
+ - Don't use `color-mix()` in className or style props for theming.
171
+
172
+ ## Accessibility
173
+
174
+ - The trigger uses `aria-autocomplete="list"` and `role="button"`.
175
+ - The dropdown list uses `role="listbox"` with individual items carrying `role="option"`, `aria-selected`, `aria-checked`, and `aria-current`.
176
+ - `FloatingFocusManager` traps focus inside the open dropdown.
177
+ - Arrow keys navigate the virtual list; Escape closes it; Enter selects the focused item.
178
+
179
+ ## Data Attributes
180
+
181
+ | Attribute | Element | Value | Description |
182
+ |-----------|---------|-------|-------------|
183
+ | `data-component` | trigger `ul` | `"autocomplete"` | Identifies the component type. |
184
+ | `data-shadow` | trigger `ul` | `"true"` | Marks the visual shadow trigger. |
185
+ | `data-value` | trigger `ul` | JSON string | Currently selected values as a JSON array. |
186
+ | `data-floating` | dropdown root | `"true"` | Marks the floating panel. |
187
+ | `data-dynamic` | option button | `"true"` | Marks options injected via `dynamicOption`. |
188
+ | `data-error` | trigger `ul` | boolean string | Reflects the error state. |
189
+
190
+ ## Notes
191
+
192
+ - Uses `react-virtuoso` for virtualized rendering — large option lists perform well.
193
+ - The overflow control collapses selected tags into a counter badge when they exceed the trigger width.
194
+ - The hidden `<input type="hidden">` stores the comma-separated selected values for native form submission.
195
+ - Floating position is computed with `@floating-ui/react` and updates automatically while open (`autoUpdate`).
196
+ - The dropdown search uses `fzf` (fuzzy matching) across both `value` and `label` fields.