@skunkworksai/ui-kit 0.1.18
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/AGENTS.md +250 -0
- package/README.md +429 -0
- package/dist/assets/Service-applets-apps/audience-measurement.png +0 -0
- package/dist/assets/Service-applets-apps/coatro-app.png +0 -0
- package/dist/assets/Service-applets-apps/content-effectiveness.png +0 -0
- package/dist/assets/Service-applets-apps/hop-tracking.png +0 -0
- package/dist/assets/Service-applets-apps/index.ts +13 -0
- package/dist/assets/Service-applets-apps/journeys-app.png +0 -0
- package/dist/assets/Service-applets-apps/people-counting.png +0 -0
- package/dist/assets/Service-applets-apps/petra.png +0 -0
- package/dist/assets/Service-applets-apps/sam-generic.png +0 -0
- package/dist/assets/Service-applets-apps/sami-for-environment.png +0 -0
- package/dist/assets/Service-applets-apps/shelf-engagement.png +0 -0
- package/dist/assets/Service-applets-apps/traffic-management.png +0 -0
- package/dist/assets/Service-applets-apps/viana.png +0 -0
- package/dist/assets/Service-applets-apps/zone-engagement.png +0 -0
- package/dist/assets/icons/Dashboard-active.svg +29 -0
- package/dist/assets/icons/Dashboard.svg +25 -0
- package/dist/assets/icons/DashboardActiveIcon.tsx +35 -0
- package/dist/assets/icons/DashboardIcon.tsx +31 -0
- package/dist/assets/icons/Devices-active.svg +27 -0
- package/dist/assets/icons/Devices.svg +20 -0
- package/dist/assets/icons/DevicesActiveIcon.tsx +33 -0
- package/dist/assets/icons/DevicesIcon.tsx +26 -0
- package/dist/assets/icons/Installer-active.svg +29 -0
- package/dist/assets/icons/Installer.svg +17 -0
- package/dist/assets/icons/InstallerActiveIcon.tsx +35 -0
- package/dist/assets/icons/InstallerIcon.tsx +23 -0
- package/dist/assets/icons/Manifest-active.svg +49 -0
- package/dist/assets/icons/Manifest.svg +49 -0
- package/dist/assets/icons/ManifestActiveIcon.tsx +55 -0
- package/dist/assets/icons/ManifestIcon.tsx +55 -0
- package/dist/assets/icons/Sensor-active.svg +15 -0
- package/dist/assets/icons/Sensor.svg +11 -0
- package/dist/assets/icons/SensorActiveIcon.tsx +21 -0
- package/dist/assets/icons/SensorIcon.tsx +17 -0
- package/dist/assets/icons/Site-active.svg +14 -0
- package/dist/assets/icons/Site.svg +10 -0
- package/dist/assets/icons/SiteActiveIcon.tsx +20 -0
- package/dist/assets/icons/SiteIcon.tsx +16 -0
- package/dist/assets/icons/X-ray-active.svg +199 -0
- package/dist/assets/icons/X-ray.svg +47 -0
- package/dist/assets/icons/XrayActiveIcon.tsx +205 -0
- package/dist/assets/icons/XrayIcon.tsx +53 -0
- package/dist/assets/icons/index.ts +14 -0
- package/dist/assets/logos/DarkLogo.tsx +28 -0
- package/dist/assets/logos/DarkSymbol.tsx +9 -0
- package/dist/assets/logos/PrimaryLogo.tsx +28 -0
- package/dist/assets/logos/PrimarySymbol.tsx +9 -0
- package/dist/assets/logos/WhiteLogo.tsx +28 -0
- package/dist/assets/logos/WhiteSymbol.tsx +9 -0
- package/dist/assets/logos/dark_logo.svg +22 -0
- package/dist/assets/logos/dark_symbol.svg +3 -0
- package/dist/assets/logos/index.ts +6 -0
- package/dist/assets/logos/primary-logo.svg +22 -0
- package/dist/assets/logos/primary_logo.svg +22 -0
- package/dist/assets/logos/primary_symbol.svg +3 -0
- package/dist/assets/logos/white_symbol.svg +3 -0
- package/dist/index.cjs +160 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +45276 -0
- package/dist/index.js.map +1 -0
- package/dist/tokens-source.css +121 -0
- package/dist/types/assets/Service-applets-apps/index.d.ts +13 -0
- package/dist/types/assets/icons/DashboardActiveIcon.d.ts +2 -0
- package/dist/types/assets/icons/DashboardIcon.d.ts +2 -0
- package/dist/types/assets/icons/DevicesActiveIcon.d.ts +2 -0
- package/dist/types/assets/icons/DevicesIcon.d.ts +2 -0
- package/dist/types/assets/icons/InstallerActiveIcon.d.ts +2 -0
- package/dist/types/assets/icons/InstallerIcon.d.ts +2 -0
- package/dist/types/assets/icons/ManifestActiveIcon.d.ts +2 -0
- package/dist/types/assets/icons/ManifestIcon.d.ts +2 -0
- package/dist/types/assets/icons/SensorActiveIcon.d.ts +2 -0
- package/dist/types/assets/icons/SensorIcon.d.ts +2 -0
- package/dist/types/assets/icons/SiteActiveIcon.d.ts +2 -0
- package/dist/types/assets/icons/SiteIcon.d.ts +2 -0
- package/dist/types/assets/icons/XrayActiveIcon.d.ts +2 -0
- package/dist/types/assets/icons/XrayIcon.d.ts +2 -0
- package/dist/types/assets/icons/index.d.ts +14 -0
- package/dist/types/assets/logos/DarkLogo.d.ts +2 -0
- package/dist/types/assets/logos/DarkSymbol.d.ts +2 -0
- package/dist/types/assets/logos/PrimaryLogo.d.ts +2 -0
- package/dist/types/assets/logos/PrimarySymbol.d.ts +2 -0
- package/dist/types/assets/logos/WhiteLogo.d.ts +2 -0
- package/dist/types/assets/logos/WhiteSymbol.d.ts +2 -0
- package/dist/types/assets/logos/index.d.ts +6 -0
- package/dist/types/components/blocks/AppBentoMenu.d.ts +31 -0
- package/dist/types/components/blocks/AppDashboard.d.ts +121 -0
- package/dist/types/components/blocks/AppHeader.d.ts +198 -0
- package/dist/types/components/blocks/AppLocationTreeFilter.d.ts +44 -0
- package/dist/types/components/blocks/AppNotificationsMenu.d.ts +50 -0
- package/dist/types/components/blocks/AppPageTitle.d.ts +86 -0
- package/dist/types/components/blocks/AppProfile.d.ts +53 -0
- package/dist/types/components/blocks/TableManager.d.ts +194 -0
- package/dist/types/components/blocks/backgrounds/AppDashboardBackgroundDots.d.ts +14 -0
- package/dist/types/components/blocks/backgrounds/AppDashboardBackgroundWave.d.ts +14 -0
- package/dist/types/components/blocks/backgrounds/index.d.ts +3 -0
- package/dist/types/components/blocks/backgrounds/types.d.ts +13 -0
- package/dist/types/components/blocks/index.d.ts +9 -0
- package/dist/types/components/primitives/AppAccordion.d.ts +22 -0
- package/dist/types/components/primitives/AppAlert.d.ts +33 -0
- package/dist/types/components/primitives/AppAlertDialog.d.ts +79 -0
- package/dist/types/components/primitives/AppAspectRatio.d.ts +7 -0
- package/dist/types/components/primitives/AppAvatar.d.ts +17 -0
- package/dist/types/components/primitives/AppBadge.d.ts +9 -0
- package/dist/types/components/primitives/AppBreadcrumb.d.ts +45 -0
- package/dist/types/components/primitives/AppButton.d.ts +24 -0
- package/dist/types/components/primitives/AppButtonGroup.d.ts +10 -0
- package/dist/types/components/primitives/AppCalendar.d.ts +51 -0
- package/dist/types/components/primitives/AppCard.d.ts +41 -0
- package/dist/types/components/primitives/AppCarousel.d.ts +26 -0
- package/dist/types/components/primitives/AppChart.d.ts +27 -0
- package/dist/types/components/primitives/AppCheckbox.d.ts +9 -0
- package/dist/types/components/primitives/AppCollapsible.d.ts +20 -0
- package/dist/types/components/primitives/AppCombobox.d.ts +37 -0
- package/dist/types/components/primitives/AppCommand.d.ts +47 -0
- package/dist/types/components/primitives/AppContextMenu.d.ts +82 -0
- package/dist/types/components/primitives/AppDataTable.d.ts +23 -0
- package/dist/types/components/primitives/AppDatePicker.d.ts +26 -0
- package/dist/types/components/primitives/AppDialog.d.ts +40 -0
- package/dist/types/components/primitives/AppDrawer.d.ts +58 -0
- package/dist/types/components/primitives/AppDropdownMenu.d.ts +85 -0
- package/dist/types/components/primitives/AppEmpty.d.ts +35 -0
- package/dist/types/components/primitives/AppField.d.ts +65 -0
- package/dist/types/components/primitives/AppForm.d.ts +58 -0
- package/dist/types/components/primitives/AppHoverCard.d.ts +19 -0
- package/dist/types/components/primitives/AppInput.d.ts +21 -0
- package/dist/types/components/primitives/AppKbd.d.ts +18 -0
- package/dist/types/components/primitives/AppLabel.d.ts +8 -0
- package/dist/types/components/primitives/AppMenubar.d.ts +83 -0
- package/dist/types/components/primitives/AppNavigationMenu.d.ts +51 -0
- package/dist/types/components/primitives/AppPagination.d.ts +46 -0
- package/dist/types/components/primitives/AppPopover.d.ts +20 -0
- package/dist/types/components/primitives/AppProgress.d.ts +9 -0
- package/dist/types/components/primitives/AppRadioGroup.d.ts +15 -0
- package/dist/types/components/primitives/AppScrollArea.d.ts +12 -0
- package/dist/types/components/primitives/AppScrollText.d.ts +7 -0
- package/dist/types/components/primitives/AppSelect.d.ts +46 -0
- package/dist/types/components/primitives/AppSeparator.d.ts +8 -0
- package/dist/types/components/primitives/AppSheet.d.ts +32 -0
- package/dist/types/components/primitives/AppSidebar.d.ts +59 -0
- package/dist/types/components/primitives/AppSkeleton.d.ts +6 -0
- package/dist/types/components/primitives/AppSpinner.d.ts +13 -0
- package/dist/types/components/primitives/AppSwitch.d.ts +8 -0
- package/dist/types/components/primitives/AppTable.d.ts +53 -0
- package/dist/types/components/primitives/AppTabs.d.ts +10 -0
- package/dist/types/components/primitives/AppTextarea.d.ts +9 -0
- package/dist/types/components/primitives/AppThemeProvider.d.ts +46 -0
- package/dist/types/components/primitives/AppToaster.d.ts +7 -0
- package/dist/types/components/primitives/AppToggle.d.ts +9 -0
- package/dist/types/components/primitives/AppToggleGroup.d.ts +18 -0
- package/dist/types/components/primitives/AppTooltip.d.ts +26 -0
- package/dist/types/components/primitives/AppTypography.d.ts +80 -0
- package/dist/types/components/primitives/index.d.ts +57 -0
- package/dist/types/components/ui/accordion.d.ts +7 -0
- package/dist/types/components/ui/alert-dialog.d.ts +20 -0
- package/dist/types/components/ui/alert.d.ts +8 -0
- package/dist/types/components/ui/aspect-ratio.d.ts +4 -0
- package/dist/types/components/ui/avatar.d.ts +6 -0
- package/dist/types/components/ui/badge.d.ts +9 -0
- package/dist/types/components/ui/breadcrumb.d.ts +19 -0
- package/dist/types/components/ui/button.d.ts +13 -0
- package/dist/types/components/ui/calendar.d.ts +8 -0
- package/dist/types/components/ui/card.d.ts +11 -0
- package/dist/types/components/ui/carousel.d.ts +28 -0
- package/dist/types/components/ui/chart.d.ts +48 -0
- package/dist/types/components/ui/checkbox.d.ts +4 -0
- package/dist/types/components/ui/collapsible.d.ts +6 -0
- package/dist/types/components/ui/combobox.d.ts +83 -0
- package/dist/types/components/ui/command.d.ts +82 -0
- package/dist/types/components/ui/context-menu.d.ts +27 -0
- package/dist/types/components/ui/dialog.d.ts +19 -0
- package/dist/types/components/ui/drawer.d.ts +23 -0
- package/dist/types/components/ui/dropdown-menu.d.ts +27 -0
- package/dist/types/components/ui/empty.d.ts +14 -0
- package/dist/types/components/ui/field.d.ts +40 -0
- package/dist/types/components/ui/form.d.ts +22 -0
- package/dist/types/components/ui/hover-card.d.ts +6 -0
- package/dist/types/components/ui/input.d.ts +3 -0
- package/dist/types/components/ui/kbd.d.ts +4 -0
- package/dist/types/components/ui/label.d.ts +4 -0
- package/dist/types/components/ui/menubar.d.ts +30 -0
- package/dist/types/components/ui/navigation-menu.d.ts +12 -0
- package/dist/types/components/ui/pagination.d.ts +28 -0
- package/dist/types/components/ui/popover.d.ts +6 -0
- package/dist/types/components/ui/progress.d.ts +4 -0
- package/dist/types/components/ui/radio-group.d.ts +5 -0
- package/dist/types/components/ui/scroll-area.d.ts +5 -0
- package/dist/types/components/ui/scroll-text.d.ts +6 -0
- package/dist/types/components/ui/select.d.ts +13 -0
- package/dist/types/components/ui/separator.d.ts +4 -0
- package/dist/types/components/ui/sheet.d.ts +25 -0
- package/dist/types/components/ui/sidebar.d.ts +69 -0
- package/dist/types/components/ui/skeleton.d.ts +2 -0
- package/dist/types/components/ui/sonner.d.ts +2 -0
- package/dist/types/components/ui/switch.d.ts +4 -0
- package/dist/types/components/ui/table.d.ts +10 -0
- package/dist/types/components/ui/tabs.d.ts +19 -0
- package/dist/types/components/ui/textarea.d.ts +5 -0
- package/dist/types/components/ui/toggle-group.d.ts +15 -0
- package/dist/types/components/ui/toggle.d.ts +12 -0
- package/dist/types/components/ui/tooltip.d.ts +7 -0
- package/dist/types/hooks/use-mobile.d.ts +13 -0
- package/dist/types/hooks/use-page-title.d.ts +23 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/lib/utils.d.ts +2 -0
- package/dist/ui-kit.css +1 -0
- package/package.json +117 -0
- package/rules/accordion.md +60 -0
- package/rules/alert-dialog.md +62 -0
- package/rules/alert.md +61 -0
- package/rules/aspect-ratio.md +47 -0
- package/rules/avatar.md +52 -0
- package/rules/badge.md +55 -0
- package/rules/bento-menu.md +86 -0
- package/rules/breadcrumb.md +80 -0
- package/rules/button-group.md +58 -0
- package/rules/button.md +66 -0
- package/rules/calendar.md +97 -0
- package/rules/card.md +84 -0
- package/rules/carousel.md +113 -0
- package/rules/chart.md +125 -0
- package/rules/checkbox.md +49 -0
- package/rules/collapsible.md +57 -0
- package/rules/combobox.md +133 -0
- package/rules/command.md +73 -0
- package/rules/context-menu.md +65 -0
- package/rules/dashboard.md +368 -0
- package/rules/data-table.md +91 -0
- package/rules/date-picker.md +65 -0
- package/rules/dialog.md +60 -0
- package/rules/drawer.md +65 -0
- package/rules/dropdown-menu.md +72 -0
- package/rules/empty.md +110 -0
- package/rules/field.md +149 -0
- package/rules/form.md +60 -0
- package/rules/header.md +245 -0
- package/rules/hover-card.md +53 -0
- package/rules/icons.md +141 -0
- package/rules/input.md +66 -0
- package/rules/kbd.md +58 -0
- package/rules/label.md +30 -0
- package/rules/location-tree-filter.md +165 -0
- package/rules/logo.md +58 -0
- package/rules/menubar.md +75 -0
- package/rules/navigation-menu.md +68 -0
- package/rules/notifications-menu.md +137 -0
- package/rules/pagetitle.md +160 -0
- package/rules/pagination.md +71 -0
- package/rules/popover.md +48 -0
- package/rules/profile.md +125 -0
- package/rules/progress.md +41 -0
- package/rules/radio-group.md +44 -0
- package/rules/scroll-area.md +32 -0
- package/rules/scroll-text.md +40 -0
- package/rules/select.md +86 -0
- package/rules/separator.md +43 -0
- package/rules/sheet.md +76 -0
- package/rules/sidebar.md +442 -0
- package/rules/skeleton.md +46 -0
- package/rules/spinner.md +43 -0
- package/rules/switch.md +46 -0
- package/rules/table-manager.md +306 -0
- package/rules/table.md +177 -0
- package/rules/tabs.md +82 -0
- package/rules/text.md +3 -0
- package/rules/textarea.md +38 -0
- package/rules/toaster.md +58 -0
- package/rules/toggle-group.md +50 -0
- package/rules/toggle.md +40 -0
- package/rules/tokens.md +88 -0
- package/rules/tooltip.md +78 -0
- package/rules/typography.md +81 -0
- package/scripts/scaffold.mjs +28 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# AppCalendar
|
|
2
|
+
|
|
3
|
+
> Docs: https://ui-kit.vercel.app/docs/components/calendar
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { AppCalendar } from "@/components/primitives/AppCalendar"
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Props
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Default | Description |
|
|
12
|
+
|------|------|---------|-------------|
|
|
13
|
+
| `mode` | `"single" \| "range"` | `"single"` | Selection mode |
|
|
14
|
+
| `value` | `Date \| DateRange` | — | Controlled selected date(s) |
|
|
15
|
+
| `onChange` | `(date) => void` | — | Change handler |
|
|
16
|
+
| `popover` | `boolean` | `false` | Render as a popover trigger instead of inline |
|
|
17
|
+
| `presets` | `{ label: string; days: number }[]` | `[]` | Quick-select preset buttons (popover only) |
|
|
18
|
+
| `placeholder` | `string` | — | Trigger button label when no date selected (popover only) |
|
|
19
|
+
| `numberOfMonths` | `number` | `1` | Months to display side by side |
|
|
20
|
+
| `showWeekNumber` | `boolean` | `false` | Show ISO week numbers |
|
|
21
|
+
| `disabled` | `boolean` | `false` | Disable the trigger button (popover only) |
|
|
22
|
+
| `disabledDates` | `Matcher \| Matcher[]` | — | Disable specific dates in the calendar |
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Inline (basic)
|
|
27
|
+
```tsx
|
|
28
|
+
<AppCalendar
|
|
29
|
+
mode="single"
|
|
30
|
+
value={date}
|
|
31
|
+
onChange={setDate}
|
|
32
|
+
/>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Inline range
|
|
36
|
+
```tsx
|
|
37
|
+
<AppCalendar
|
|
38
|
+
mode="range"
|
|
39
|
+
value={range}
|
|
40
|
+
onChange={setRange}
|
|
41
|
+
numberOfMonths={2}
|
|
42
|
+
/>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Popover date picker (built-in)
|
|
46
|
+
```tsx
|
|
47
|
+
<AppCalendar
|
|
48
|
+
mode="single"
|
|
49
|
+
popover
|
|
50
|
+
value={date}
|
|
51
|
+
onChange={setDate}
|
|
52
|
+
placeholder="Pick a date"
|
|
53
|
+
className="w-64"
|
|
54
|
+
/>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Popover with presets
|
|
58
|
+
```tsx
|
|
59
|
+
<AppCalendar
|
|
60
|
+
mode="single"
|
|
61
|
+
popover
|
|
62
|
+
value={date}
|
|
63
|
+
onChange={setDate}
|
|
64
|
+
presets={[
|
|
65
|
+
{ label: "Today", days: 0 },
|
|
66
|
+
{ label: "Tomorrow", days: 1 },
|
|
67
|
+
{ label: "In a week", days: 7 },
|
|
68
|
+
]}
|
|
69
|
+
className="w-64"
|
|
70
|
+
/>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Popover range picker
|
|
74
|
+
```tsx
|
|
75
|
+
<AppCalendar
|
|
76
|
+
mode="range"
|
|
77
|
+
popover
|
|
78
|
+
value={range}
|
|
79
|
+
onChange={setRange}
|
|
80
|
+
numberOfMonths={2}
|
|
81
|
+
className="w-64"
|
|
82
|
+
/>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Rules
|
|
86
|
+
- ✅ Always use `mode` to specify the selection behavior
|
|
87
|
+
- ✅ Use `popover` prop for a built-in dropdown date picker — no need to manually wrap in `AppPopover`
|
|
88
|
+
- ✅ Use `presets` for quick-select shortcuts (popover mode only)
|
|
89
|
+
- ✅ Use `numberOfMonths={2}` for range selection to improve usability
|
|
90
|
+
- ✅ Set trigger width via `className` when using `popover` (e.g. `className="w-64"`) — no default width
|
|
91
|
+
- ❌ Do not wrap `AppCalendar` in `AppPopover` manually — use the `popover` prop instead
|
|
92
|
+
- ❌ Do not use `AppCalendar` for simple month/year selection — it is optimized for date selection
|
|
93
|
+
- ❌ If a prop you need is missing, stop and inform the design team
|
|
94
|
+
|
|
95
|
+
## Responsive
|
|
96
|
+
|
|
97
|
+
On mobile (<768px) `numberOfMonths` is forced to `1` regardless of the prop value; on desktop the prop value is used (default `2` for range mode). Pass `numberOfMonths={1}` explicitly to opt out of the 2-month default on desktop without affecting mobile behavior.
|
package/rules/card.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# AppCard
|
|
2
|
+
|
|
3
|
+
> Docs: https://ui-kit.vercel.app/docs/components/card
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import {
|
|
7
|
+
AppCard,
|
|
8
|
+
AppCardHeader,
|
|
9
|
+
AppCardTitle,
|
|
10
|
+
AppCardDescription,
|
|
11
|
+
AppCardAction,
|
|
12
|
+
AppCardContent,
|
|
13
|
+
AppCardFooter,
|
|
14
|
+
} from "@/components/primitives/AppCard"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## AppCard
|
|
18
|
+
|
|
19
|
+
| Prop | Type | Default | Description |
|
|
20
|
+
|------|------|---------|-------------|
|
|
21
|
+
| `size` | `"default" \| "sm"` | `"default"` | Padding and spacing size (shadcn base prop) |
|
|
22
|
+
| `padding` | `"default" \| "none" \| "sm" \| "md" \| "lg"` | `"default"` | Override internal padding for dense/media layouts |
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Basic
|
|
27
|
+
```tsx
|
|
28
|
+
<AppCard>
|
|
29
|
+
<AppCardHeader>
|
|
30
|
+
<AppCardTitle>Project Update</AppCardTitle>
|
|
31
|
+
<AppCardDescription>Deploying to production tomorrow.</AppCardDescription>
|
|
32
|
+
</AppCardHeader>
|
|
33
|
+
<AppCardContent>
|
|
34
|
+
<p>Everything is looking good. All tests passed.</p>
|
|
35
|
+
</AppCardContent>
|
|
36
|
+
<AppCardFooter>
|
|
37
|
+
<AppButton>Dismiss</AppButton>
|
|
38
|
+
</AppCardFooter>
|
|
39
|
+
</AppCard>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### With Action
|
|
43
|
+
```tsx
|
|
44
|
+
<AppCardHeader>
|
|
45
|
+
<AppCardTitle>Settings</AppCardTitle>
|
|
46
|
+
<AppCardDescription>Manage your account settings.</AppCardDescription>
|
|
47
|
+
<AppCardAction>
|
|
48
|
+
<AppButton variant="outline" size="sm">Edit</AppButton>
|
|
49
|
+
</AppCardAction>
|
|
50
|
+
</AppCardHeader>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Small Size
|
|
54
|
+
```tsx
|
|
55
|
+
<AppCard size="sm">
|
|
56
|
+
<AppCardHeader>
|
|
57
|
+
<AppCardTitle>Summary</AppCardTitle>
|
|
58
|
+
</AppCardHeader>
|
|
59
|
+
<AppCardContent>
|
|
60
|
+
<p>Compact card content.</p>
|
|
61
|
+
</AppCardContent>
|
|
62
|
+
</AppCard>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Dense / media layout
|
|
66
|
+
```tsx
|
|
67
|
+
<AppCard padding="none">
|
|
68
|
+
<img src="/feed.jpg" alt="Camera feed" className="h-40 w-full object-cover" />
|
|
69
|
+
<AppCardHeader>
|
|
70
|
+
<AppCardTitle>Camera 01</AppCardTitle>
|
|
71
|
+
</AppCardHeader>
|
|
72
|
+
</AppCard>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`padding` values: `"default"` (24px), `"none"` (0), `"sm"` (12px), `"md"` (16px), `"lg"` (32px).
|
|
76
|
+
|
|
77
|
+
## Rules
|
|
78
|
+
- ✅ Use `AppCardAction` for actions that should live in the header (aligned top-right)
|
|
79
|
+
- ✅ Use `AppCardHeader` to group Title, Description, and Action together
|
|
80
|
+
- ✅ Use `AppCard` as the standard container for grouped information
|
|
81
|
+
- ✅ Use `padding="none"` for flush media cards (camera feeds, image-first layouts)
|
|
82
|
+
- ❌ Do not nest multiple `AppCard` components unless visually distinct
|
|
83
|
+
- ❌ Do not use `!important` overrides on Card children — use the `padding` prop instead
|
|
84
|
+
- ❌ If a prop you need is missing, stop and inform the design team
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# AppCarousel
|
|
2
|
+
|
|
3
|
+
> Docs: https://ui-kit.vercel.app/docs/components/carousel
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import {
|
|
7
|
+
AppCarousel,
|
|
8
|
+
AppCarouselContent,
|
|
9
|
+
AppCarouselItem,
|
|
10
|
+
AppCarouselPrevious,
|
|
11
|
+
AppCarouselNext,
|
|
12
|
+
type AppCarouselApi,
|
|
13
|
+
useAppCarousel,
|
|
14
|
+
} from "@skunkworksai/ui-kit"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## AppCarousel
|
|
18
|
+
|
|
19
|
+
| Prop | Type | Default | Description |
|
|
20
|
+
|------|------|---------|-------------|
|
|
21
|
+
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Scroll axis. |
|
|
22
|
+
| `opts` | `EmblaOptionsType` | — | Embla configuration object (align, loop, etc.). |
|
|
23
|
+
| `setApi` | `(api: AppCarouselApi) => void` | — | Callback to receive the Embla API instance for programmatic control. |
|
|
24
|
+
| `plugins` | `EmblaPluginType[]` | — | Embla plugins (e.g. autoplay). |
|
|
25
|
+
|
|
26
|
+
## AppCarouselContent
|
|
27
|
+
|
|
28
|
+
Accepts all `div` props. The scrollable slide container — wrap `AppCarouselItem` children inside.
|
|
29
|
+
|
|
30
|
+
## AppCarouselItem
|
|
31
|
+
|
|
32
|
+
Accepts all `div` props. Use `className="basis-1/2"` (or `basis-1/3`, etc.) to show multiple slides at once.
|
|
33
|
+
|
|
34
|
+
## AppCarouselPrevious / AppCarouselNext
|
|
35
|
+
|
|
36
|
+
Accepts all `button` props. Positioned absolutely — ensure the parent (`AppCarousel`) has enough horizontal padding (e.g. `px-14`) so buttons don't overlap slide content.
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
### Basic
|
|
41
|
+
```tsx
|
|
42
|
+
<div className="px-14 max-w-sm">
|
|
43
|
+
<AppCarousel>
|
|
44
|
+
<AppCarouselContent>
|
|
45
|
+
{items.map((item, i) => (
|
|
46
|
+
<AppCarouselItem key={i}>
|
|
47
|
+
<AppCard>
|
|
48
|
+
<AppCardContent className="flex aspect-square items-center justify-center p-6">
|
|
49
|
+
{item}
|
|
50
|
+
</AppCardContent>
|
|
51
|
+
</AppCard>
|
|
52
|
+
</AppCarouselItem>
|
|
53
|
+
))}
|
|
54
|
+
</AppCarouselContent>
|
|
55
|
+
<AppCarouselPrevious />
|
|
56
|
+
<AppCarouselNext />
|
|
57
|
+
</AppCarousel>
|
|
58
|
+
</div>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Multiple slides visible
|
|
62
|
+
```tsx
|
|
63
|
+
<AppCarousel opts={{ align: "start" }}>
|
|
64
|
+
<AppCarouselContent>
|
|
65
|
+
{items.map((item, i) => (
|
|
66
|
+
<AppCarouselItem key={i} className="basis-1/3">
|
|
67
|
+
...
|
|
68
|
+
</AppCarouselItem>
|
|
69
|
+
))}
|
|
70
|
+
</AppCarouselContent>
|
|
71
|
+
<AppCarouselPrevious />
|
|
72
|
+
<AppCarouselNext />
|
|
73
|
+
</AppCarousel>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Vertical orientation
|
|
77
|
+
```tsx
|
|
78
|
+
<AppCarousel orientation="vertical" className="max-h-64">
|
|
79
|
+
<AppCarouselContent className="-mt-1 h-64">
|
|
80
|
+
{items.map((item, i) => (
|
|
81
|
+
<AppCarouselItem key={i} className="pt-1 basis-1/3">
|
|
82
|
+
...
|
|
83
|
+
</AppCarouselItem>
|
|
84
|
+
))}
|
|
85
|
+
</AppCarouselContent>
|
|
86
|
+
<AppCarouselPrevious />
|
|
87
|
+
<AppCarouselNext />
|
|
88
|
+
</AppCarousel>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Programmatic API access
|
|
92
|
+
```tsx
|
|
93
|
+
const [api, setApi] = React.useState<AppCarouselApi>()
|
|
94
|
+
const [current, setCurrent] = React.useState(0)
|
|
95
|
+
|
|
96
|
+
React.useEffect(() => {
|
|
97
|
+
if (!api) return
|
|
98
|
+
setCurrent(api.selectedScrollSnap() + 1)
|
|
99
|
+
api.on("select", () => setCurrent(api.selectedScrollSnap() + 1))
|
|
100
|
+
}, [api])
|
|
101
|
+
|
|
102
|
+
<AppCarousel setApi={setApi}>...</AppCarousel>
|
|
103
|
+
<p>Slide {current} of {api?.scrollSnapList().length}</p>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Rules
|
|
107
|
+
- ✅ Always wrap `AppCarouselPrevious` / `AppCarouselNext` inside `AppCarousel` — they use carousel context
|
|
108
|
+
- ✅ Add horizontal padding (e.g. `px-14`) to the container when using nav buttons in horizontal mode
|
|
109
|
+
- ✅ Add vertical padding (e.g. `py-14`) when using nav buttons in vertical mode
|
|
110
|
+
- ✅ Use `opts={{ loop: true }}` for infinite looping
|
|
111
|
+
- ✅ Use `setApi` to get the Embla instance for programmatic scroll, event listeners, or slide counters
|
|
112
|
+
- ❌ Do not use `AppCarouselPrevious` / `AppCarouselNext` outside an `AppCarousel` — they require its context
|
|
113
|
+
- ❌ If a prop you need is missing, stop and inform the design team
|
package/rules/chart.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# AppChart
|
|
2
|
+
|
|
3
|
+
> Docs: https://ui-kit.vercel.app/docs/components/chart
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import {
|
|
7
|
+
AppChartContainer,
|
|
8
|
+
AppChartTooltip,
|
|
9
|
+
AppChartTooltipContent,
|
|
10
|
+
AppChartLegend,
|
|
11
|
+
AppChartLegendContent,
|
|
12
|
+
AppChartStyle,
|
|
13
|
+
type AppChartConfig,
|
|
14
|
+
// Recharts chart + shape components re-exported:
|
|
15
|
+
BarChart, Bar, LineChart, Line, AreaChart, Area,
|
|
16
|
+
PieChart, Pie, Cell, CartesianGrid, XAxis, YAxis,
|
|
17
|
+
} from "@skunkworksai/ui-kit"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## AppChartContainer
|
|
21
|
+
|
|
22
|
+
| Prop | Type | Default | Description |
|
|
23
|
+
|------|------|---------|-------------|
|
|
24
|
+
| `config` | `AppChartConfig` | required | Color and label mappings for each data key. |
|
|
25
|
+
| `className` | `string` | — | Must include a height constraint (`min-h-*` or `aspect-*`). |
|
|
26
|
+
| `children` | `ReactElement` | required | A single Recharts chart component (BarChart, LineChart, etc.). |
|
|
27
|
+
|
|
28
|
+
## AppChartTooltipContent
|
|
29
|
+
|
|
30
|
+
| Prop | Type | Default | Description |
|
|
31
|
+
|------|------|---------|-------------|
|
|
32
|
+
| `indicator` | `"dot" \| "line" \| "dashed"` | `"dot"` | Color swatch shape. |
|
|
33
|
+
| `nameKey` | `string` | — | Config key to look up labels. Set when `dataKey` ≠ config key (e.g. Pie charts). |
|
|
34
|
+
| `hideLabel` | `boolean` | `false` | Hides the series label row. |
|
|
35
|
+
|
|
36
|
+
## AppChartLegendContent
|
|
37
|
+
|
|
38
|
+
| Prop | Type | Default | Description |
|
|
39
|
+
|------|------|---------|-------------|
|
|
40
|
+
| `nameKey` | `string` | — | Config key for label lookup. Set when `dataKey` ≠ config key (e.g. Pie charts). |
|
|
41
|
+
|
|
42
|
+
## AppChartConfig
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
type AppChartConfig = {
|
|
46
|
+
[key: string]: {
|
|
47
|
+
label: string
|
|
48
|
+
color?: string // CSS variable: "var(--chart-1)"
|
|
49
|
+
theme?: { // or per-theme colors
|
|
50
|
+
light: string
|
|
51
|
+
dark: string
|
|
52
|
+
}
|
|
53
|
+
icon?: React.ComponentType
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Inside Recharts, reference colors as `var(--color-{key})` — e.g. `var(--color-desktop)`.
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
### Bar chart
|
|
63
|
+
```tsx
|
|
64
|
+
const chartConfig = {
|
|
65
|
+
desktop: { label: "Desktop", color: "var(--chart-1)" },
|
|
66
|
+
mobile: { label: "Mobile", color: "var(--chart-2)" },
|
|
67
|
+
} satisfies AppChartConfig
|
|
68
|
+
|
|
69
|
+
<AppChartContainer config={chartConfig} className="min-h-[250px] w-full">
|
|
70
|
+
<BarChart data={data} accessibilityLayer>
|
|
71
|
+
<CartesianGrid vertical={false} />
|
|
72
|
+
<XAxis dataKey="month" tickLine={false} axisLine={false} tickMargin={8} />
|
|
73
|
+
<AppChartTooltip content={<AppChartTooltipContent />} />
|
|
74
|
+
<AppChartLegend content={<AppChartLegendContent />} />
|
|
75
|
+
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
|
|
76
|
+
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
|
|
77
|
+
</BarChart>
|
|
78
|
+
</AppChartContainer>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Line chart
|
|
82
|
+
```tsx
|
|
83
|
+
<AppChartContainer config={chartConfig} className="min-h-[250px] w-full">
|
|
84
|
+
<LineChart data={data} accessibilityLayer>
|
|
85
|
+
<CartesianGrid vertical={false} />
|
|
86
|
+
<XAxis dataKey="month" tickLine={false} axisLine={false} tickMargin={8} />
|
|
87
|
+
<YAxis tickLine={false} axisLine={false} tickMargin={8} />
|
|
88
|
+
<AppChartTooltip content={<AppChartTooltipContent />} />
|
|
89
|
+
<AppChartLegend content={<AppChartLegendContent />} />
|
|
90
|
+
<Line dataKey="desktop" stroke="var(--color-desktop)" strokeWidth={2} dot={false} />
|
|
91
|
+
<Line dataKey="mobile" stroke="var(--color-mobile)" strokeWidth={2} dot={false} />
|
|
92
|
+
</LineChart>
|
|
93
|
+
</AppChartContainer>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Pie chart
|
|
97
|
+
```tsx
|
|
98
|
+
const pieConfig = {
|
|
99
|
+
Chrome: { label: "Chrome", color: "var(--chart-1)" },
|
|
100
|
+
Firefox: { label: "Firefox", color: "var(--chart-2)" },
|
|
101
|
+
} satisfies AppChartConfig
|
|
102
|
+
|
|
103
|
+
<AppChartContainer config={pieConfig} className="min-h-[250px] w-full">
|
|
104
|
+
<PieChart accessibilityLayer>
|
|
105
|
+
<AppChartTooltip content={<AppChartTooltipContent nameKey="name" />} />
|
|
106
|
+
<AppChartLegend content={<AppChartLegendContent nameKey="name" />} />
|
|
107
|
+
<Pie data={pieData} dataKey="value" nameKey="name" outerRadius={80}>
|
|
108
|
+
{pieData.map((entry) => (
|
|
109
|
+
<Cell key={entry.name} fill={pieConfig[entry.name as keyof typeof pieConfig]?.color} />
|
|
110
|
+
))}
|
|
111
|
+
</Pie>
|
|
112
|
+
</PieChart>
|
|
113
|
+
</AppChartContainer>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Rules
|
|
117
|
+
- ✅ Always set a height constraint on `AppChartContainer` (`min-h-[250px]`, `aspect-video`, etc.)
|
|
118
|
+
- ✅ Add `accessibilityLayer` to every Recharts chart component
|
|
119
|
+
- ✅ Use `var(--chart-1)` through `var(--chart-5)` as color values in config — never hardcode colors
|
|
120
|
+
- ✅ Reference colors inside the chart as `var(--color-{key})` (e.g. `var(--color-desktop)`)
|
|
121
|
+
- ✅ For Pie charts, set `nameKey` on both `AppChartTooltipContent` and `AppChartLegendContent` so labels resolve correctly
|
|
122
|
+
- ✅ Use `satisfies AppChartConfig` on your config object for type safety
|
|
123
|
+
- ❌ Do not import from `recharts` directly — import Recharts components from `@skunkworksai/ui-kit`
|
|
124
|
+
- ❌ Do not hardcode color values in `fill` or `stroke` — always use `var(--color-{key})`
|
|
125
|
+
- ❌ If a prop you need is missing, stop and inform the design team
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# AppCheckbox
|
|
2
|
+
|
|
3
|
+
> Docs: https://ui-kit.vercel.app/docs/components/checkbox
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { AppCheckbox } from "@/components/primitives/AppCheckbox"
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## AppCheckbox
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Default | Description |
|
|
12
|
+
| :--- | :--- | :--- | :--- |
|
|
13
|
+
| `checked` | `boolean \| "indeterminate"` | - | The controlled checked state of the checkbox. |
|
|
14
|
+
| `defaultChecked` | `boolean` | - | The checked state of the checkbox when it is initially rendered. |
|
|
15
|
+
| `onCheckedChange` | `(checked: boolean \| "indeterminate") => void` | - | Event handler called when the checked state of the checkbox changes. |
|
|
16
|
+
| `disabled` | `boolean` | - | When true, prevents the user from interacting with the checkbox. |
|
|
17
|
+
| `required` | `boolean` | - | When true, indicates that the user must check the checkbox before the owning form can be submitted. |
|
|
18
|
+
| `name` | `string` | - | The name of the checkbox. Submitted with its owning form as part of a name/value pair. |
|
|
19
|
+
| `value` | `string` | `"on"` | The value given as data to the checkbox when submitted in a form. |
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
### Default Checkbox
|
|
24
|
+
```tsx
|
|
25
|
+
<div className="flex items-center gap-2">
|
|
26
|
+
<AppCheckbox id="terms" />
|
|
27
|
+
<label htmlFor="terms" className="text-sm font-medium">
|
|
28
|
+
Accept terms and conditions
|
|
29
|
+
</label>
|
|
30
|
+
</div>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Disabled State
|
|
34
|
+
```tsx
|
|
35
|
+
<div className="flex items-center gap-2">
|
|
36
|
+
<AppCheckbox id="disabled-terms" disabled />
|
|
37
|
+
<label htmlFor="disabled-terms" className="text-sm font-medium opacity-50">
|
|
38
|
+
Disabled checkbox
|
|
39
|
+
</label>
|
|
40
|
+
</div>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Rules
|
|
44
|
+
|
|
45
|
+
- **Do** use `AppCheckbox` for all checkbox needs.
|
|
46
|
+
- **Do** always provide a corresponding label for accessibility.
|
|
47
|
+
- **Don't** use the raw HTML `<input type="checkbox">`.
|
|
48
|
+
- **Don't** manually style the checkbox unless absolutely necessary.
|
|
49
|
+
- If a prop you need is missing, stop and inform the design team.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# AppCollapsible
|
|
2
|
+
|
|
3
|
+
> Docs: https://ui-kit.vercel.app/docs/components/collapsible
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import {
|
|
7
|
+
AppCollapsible,
|
|
8
|
+
AppCollapsibleTrigger,
|
|
9
|
+
AppCollapsibleContent
|
|
10
|
+
} from "@/components/primitives/AppCollapsible"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## AppCollapsible
|
|
14
|
+
|
|
15
|
+
| Prop | Type | Default | Description |
|
|
16
|
+
| :--- | :--- | :--- | :--- |
|
|
17
|
+
| `open` | `boolean` | - | The controlled open state of the collapsible. |
|
|
18
|
+
| `defaultOpen` | `boolean` | - | The open state of the collapsible when it is initially rendered. |
|
|
19
|
+
| `onOpenChange` | `(open: boolean) => void` | - | Event handler called when the open state of the collapsible changes. |
|
|
20
|
+
| `disabled` | `boolean` | - | When true, prevents the user from interacting with the collapsible. |
|
|
21
|
+
|
|
22
|
+
## AppCollapsibleTrigger
|
|
23
|
+
|
|
24
|
+
| Prop | Type | Default | Description |
|
|
25
|
+
| :--- | :--- | :--- | :--- |
|
|
26
|
+
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior. |
|
|
27
|
+
|
|
28
|
+
## AppCollapsibleContent
|
|
29
|
+
|
|
30
|
+
| Prop | Type | Default | Description |
|
|
31
|
+
| :--- | :--- | :--- | :--- |
|
|
32
|
+
| `forceMount` | `boolean` | - | Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. |
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
### Simple Collapsible
|
|
37
|
+
```tsx
|
|
38
|
+
const [isOpen, setIsOpen] = React.useState(false)
|
|
39
|
+
|
|
40
|
+
<AppCollapsible open={isOpen} onOpenChange={setIsOpen}>
|
|
41
|
+
<AppCollapsibleTrigger asChild>
|
|
42
|
+
<AppButton variant="ghost">Toggle Content</AppButton>
|
|
43
|
+
</AppCollapsibleTrigger>
|
|
44
|
+
<AppCollapsibleContent>
|
|
45
|
+
<div className="p-4 border rounded-md">
|
|
46
|
+
Hidden information revealed!
|
|
47
|
+
</div>
|
|
48
|
+
</AppCollapsibleContent>
|
|
49
|
+
</AppCollapsible>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Rules
|
|
53
|
+
|
|
54
|
+
- **Do** use `AppCollapsible` for showing/hiding additional content.
|
|
55
|
+
- **Do** use `asChild` on the trigger if you want to use a custom component like `AppButton`.
|
|
56
|
+
- **Don't** use `AppCollapsible` for complex menus; use `AppAccordion` or `AppNavigationMenu` instead.
|
|
57
|
+
- If a prop you need is missing, stop and inform the design team.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# AppCombobox
|
|
2
|
+
|
|
3
|
+
> Docs: https://ui-kit.vercel.app/docs/components/combobox
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import {
|
|
7
|
+
AppCombobox,
|
|
8
|
+
AppComboboxInput,
|
|
9
|
+
AppComboboxContent,
|
|
10
|
+
AppComboboxList,
|
|
11
|
+
AppComboboxItem,
|
|
12
|
+
AppComboboxEmpty,
|
|
13
|
+
AppComboboxSeparator,
|
|
14
|
+
} from "@skunkworksai/ui-kit"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## AppCombobox
|
|
18
|
+
|
|
19
|
+
Root provider. Manages open state, search state, selection, and filtering.
|
|
20
|
+
|
|
21
|
+
| Prop | Type | Default | Description |
|
|
22
|
+
|------|------|---------|-------------|
|
|
23
|
+
| `items` | `readonly string[]` | — | **Required.** Flat list of option values. |
|
|
24
|
+
| `value` | `string` \| `string[]` | — | Controlled selected value(s). |
|
|
25
|
+
| `onValueChange` | `(value: string) => void` \| `(value: string[]) => void` | — | Selection change handler. |
|
|
26
|
+
| `multiple` | `boolean` | `false` | Enables multi-select. `value` becomes `string[]`. |
|
|
27
|
+
| `filter` | `(item: string, search: string) => boolean` | case-insensitive substring | Custom filter function. |
|
|
28
|
+
| `getDisplayValue` | `(value: string \| string[]) => string \| null` | — | Maps selected value(s) to display text in the trigger. |
|
|
29
|
+
| `disabled` | `boolean` | `false` | Disables the entire combobox. |
|
|
30
|
+
|
|
31
|
+
## AppComboboxInput
|
|
32
|
+
|
|
33
|
+
Trigger button. Shows current selection or placeholder.
|
|
34
|
+
|
|
35
|
+
| Prop | Type | Default | Description |
|
|
36
|
+
|------|------|---------|-------------|
|
|
37
|
+
| `placeholder` | `string` | `"Select..."` | Shown when nothing is selected. |
|
|
38
|
+
| `className` | `string` | — | Additional Tailwind classes. |
|
|
39
|
+
|
|
40
|
+
## AppComboboxContent
|
|
41
|
+
|
|
42
|
+
Popover panel. Contains search input and item list.
|
|
43
|
+
|
|
44
|
+
| Prop | Type | Default | Description |
|
|
45
|
+
|------|------|---------|-------------|
|
|
46
|
+
| `searchPlaceholder` | `string` | `"Search..."` | Placeholder inside the search input. |
|
|
47
|
+
| `align` | `"start"` \| `"center"` \| `"end"` | `"start"` | Popover alignment relative to trigger. |
|
|
48
|
+
| `sideOffset` | `number` | `4` | Vertical gap between trigger and popover (px). |
|
|
49
|
+
|
|
50
|
+
## AppComboboxList
|
|
51
|
+
|
|
52
|
+
Renders filtered items via a render-prop function.
|
|
53
|
+
|
|
54
|
+
| Prop | Type | Default | Description |
|
|
55
|
+
|------|------|---------|-------------|
|
|
56
|
+
| `children` | `(item: string) => ReactNode` | — | **Required.** Render function per item value. |
|
|
57
|
+
|
|
58
|
+
## AppComboboxItem
|
|
59
|
+
|
|
60
|
+
Single selectable option. Shows a checkmark when selected.
|
|
61
|
+
|
|
62
|
+
| Prop | Type | Default | Description |
|
|
63
|
+
|------|------|---------|-------------|
|
|
64
|
+
| `value` | `string` | — | **Required.** Must match a value in `items`. |
|
|
65
|
+
|
|
66
|
+
## AppComboboxEmpty
|
|
67
|
+
|
|
68
|
+
Shown when no items match the current search.
|
|
69
|
+
|
|
70
|
+
| Prop | Type | Default | Description |
|
|
71
|
+
|------|------|---------|-------------|
|
|
72
|
+
| `children` | `ReactNode` | `"No results found."` | Empty-state message. |
|
|
73
|
+
|
|
74
|
+
## AppComboboxSeparator
|
|
75
|
+
|
|
76
|
+
Visual divider between groups of items.
|
|
77
|
+
|
|
78
|
+
## Usage
|
|
79
|
+
|
|
80
|
+
### Single select with label mapping
|
|
81
|
+
```tsx
|
|
82
|
+
const options = [
|
|
83
|
+
{ value: "next", label: "Next.js" },
|
|
84
|
+
{ value: "remix", label: "Remix" },
|
|
85
|
+
]
|
|
86
|
+
const values = options.map((o) => o.value)
|
|
87
|
+
const labelOf = (v: string) => options.find((o) => o.value === v)?.label ?? v
|
|
88
|
+
|
|
89
|
+
<AppCombobox
|
|
90
|
+
items={values}
|
|
91
|
+
value={selected}
|
|
92
|
+
onValueChange={setSelected}
|
|
93
|
+
getDisplayValue={(v) => (v ? labelOf(v as string) : null)}
|
|
94
|
+
filter={(item, search) => labelOf(item).toLowerCase().includes(search.toLowerCase())}
|
|
95
|
+
>
|
|
96
|
+
<AppComboboxInput placeholder="Select framework..." />
|
|
97
|
+
<AppComboboxContent searchPlaceholder="Search...">
|
|
98
|
+
<AppComboboxEmpty>No framework found.</AppComboboxEmpty>
|
|
99
|
+
<AppComboboxList>
|
|
100
|
+
{(item) => <AppComboboxItem value={item}>{labelOf(item)}</AppComboboxItem>}
|
|
101
|
+
</AppComboboxList>
|
|
102
|
+
</AppComboboxContent>
|
|
103
|
+
</AppCombobox>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Multi-select
|
|
107
|
+
```tsx
|
|
108
|
+
<AppCombobox
|
|
109
|
+
multiple
|
|
110
|
+
items={values}
|
|
111
|
+
value={selected}
|
|
112
|
+
onValueChange={setSelected}
|
|
113
|
+
getDisplayValue={(v) => {
|
|
114
|
+
const arr = (v as string[]) ?? []
|
|
115
|
+
return arr.length ? arr.map(labelOf).join(", ") : null
|
|
116
|
+
}}
|
|
117
|
+
>
|
|
118
|
+
<AppComboboxInput placeholder="Select frameworks..." />
|
|
119
|
+
<AppComboboxContent>
|
|
120
|
+
<AppComboboxEmpty>No results.</AppComboboxEmpty>
|
|
121
|
+
<AppComboboxList>
|
|
122
|
+
{(item) => <AppComboboxItem value={item}>{labelOf(item)}</AppComboboxItem>}
|
|
123
|
+
</AppComboboxList>
|
|
124
|
+
</AppComboboxContent>
|
|
125
|
+
</AppCombobox>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Rules
|
|
129
|
+
- ✅ Always pass `items` as `string[]` — use `getDisplayValue` + `filter` for label mapping
|
|
130
|
+
- ✅ Always render `AppComboboxEmpty` inside `AppComboboxContent`
|
|
131
|
+
- ✅ Always use `AppComboboxList` render-prop to map items to `AppComboboxItem`
|
|
132
|
+
- ❌ Never use a raw `<select>` or custom dropdown — use `AppCombobox`
|
|
133
|
+
- ❌ If a prop you need is missing, stop and inform the design team
|