@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.
Files changed (274) hide show
  1. package/AGENTS.md +250 -0
  2. package/README.md +429 -0
  3. package/dist/assets/Service-applets-apps/audience-measurement.png +0 -0
  4. package/dist/assets/Service-applets-apps/coatro-app.png +0 -0
  5. package/dist/assets/Service-applets-apps/content-effectiveness.png +0 -0
  6. package/dist/assets/Service-applets-apps/hop-tracking.png +0 -0
  7. package/dist/assets/Service-applets-apps/index.ts +13 -0
  8. package/dist/assets/Service-applets-apps/journeys-app.png +0 -0
  9. package/dist/assets/Service-applets-apps/people-counting.png +0 -0
  10. package/dist/assets/Service-applets-apps/petra.png +0 -0
  11. package/dist/assets/Service-applets-apps/sam-generic.png +0 -0
  12. package/dist/assets/Service-applets-apps/sami-for-environment.png +0 -0
  13. package/dist/assets/Service-applets-apps/shelf-engagement.png +0 -0
  14. package/dist/assets/Service-applets-apps/traffic-management.png +0 -0
  15. package/dist/assets/Service-applets-apps/viana.png +0 -0
  16. package/dist/assets/Service-applets-apps/zone-engagement.png +0 -0
  17. package/dist/assets/icons/Dashboard-active.svg +29 -0
  18. package/dist/assets/icons/Dashboard.svg +25 -0
  19. package/dist/assets/icons/DashboardActiveIcon.tsx +35 -0
  20. package/dist/assets/icons/DashboardIcon.tsx +31 -0
  21. package/dist/assets/icons/Devices-active.svg +27 -0
  22. package/dist/assets/icons/Devices.svg +20 -0
  23. package/dist/assets/icons/DevicesActiveIcon.tsx +33 -0
  24. package/dist/assets/icons/DevicesIcon.tsx +26 -0
  25. package/dist/assets/icons/Installer-active.svg +29 -0
  26. package/dist/assets/icons/Installer.svg +17 -0
  27. package/dist/assets/icons/InstallerActiveIcon.tsx +35 -0
  28. package/dist/assets/icons/InstallerIcon.tsx +23 -0
  29. package/dist/assets/icons/Manifest-active.svg +49 -0
  30. package/dist/assets/icons/Manifest.svg +49 -0
  31. package/dist/assets/icons/ManifestActiveIcon.tsx +55 -0
  32. package/dist/assets/icons/ManifestIcon.tsx +55 -0
  33. package/dist/assets/icons/Sensor-active.svg +15 -0
  34. package/dist/assets/icons/Sensor.svg +11 -0
  35. package/dist/assets/icons/SensorActiveIcon.tsx +21 -0
  36. package/dist/assets/icons/SensorIcon.tsx +17 -0
  37. package/dist/assets/icons/Site-active.svg +14 -0
  38. package/dist/assets/icons/Site.svg +10 -0
  39. package/dist/assets/icons/SiteActiveIcon.tsx +20 -0
  40. package/dist/assets/icons/SiteIcon.tsx +16 -0
  41. package/dist/assets/icons/X-ray-active.svg +199 -0
  42. package/dist/assets/icons/X-ray.svg +47 -0
  43. package/dist/assets/icons/XrayActiveIcon.tsx +205 -0
  44. package/dist/assets/icons/XrayIcon.tsx +53 -0
  45. package/dist/assets/icons/index.ts +14 -0
  46. package/dist/assets/logos/DarkLogo.tsx +28 -0
  47. package/dist/assets/logos/DarkSymbol.tsx +9 -0
  48. package/dist/assets/logos/PrimaryLogo.tsx +28 -0
  49. package/dist/assets/logos/PrimarySymbol.tsx +9 -0
  50. package/dist/assets/logos/WhiteLogo.tsx +28 -0
  51. package/dist/assets/logos/WhiteSymbol.tsx +9 -0
  52. package/dist/assets/logos/dark_logo.svg +22 -0
  53. package/dist/assets/logos/dark_symbol.svg +3 -0
  54. package/dist/assets/logos/index.ts +6 -0
  55. package/dist/assets/logos/primary-logo.svg +22 -0
  56. package/dist/assets/logos/primary_logo.svg +22 -0
  57. package/dist/assets/logos/primary_symbol.svg +3 -0
  58. package/dist/assets/logos/white_symbol.svg +3 -0
  59. package/dist/index.cjs +160 -0
  60. package/dist/index.cjs.map +1 -0
  61. package/dist/index.js +45276 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/tokens-source.css +121 -0
  64. package/dist/types/assets/Service-applets-apps/index.d.ts +13 -0
  65. package/dist/types/assets/icons/DashboardActiveIcon.d.ts +2 -0
  66. package/dist/types/assets/icons/DashboardIcon.d.ts +2 -0
  67. package/dist/types/assets/icons/DevicesActiveIcon.d.ts +2 -0
  68. package/dist/types/assets/icons/DevicesIcon.d.ts +2 -0
  69. package/dist/types/assets/icons/InstallerActiveIcon.d.ts +2 -0
  70. package/dist/types/assets/icons/InstallerIcon.d.ts +2 -0
  71. package/dist/types/assets/icons/ManifestActiveIcon.d.ts +2 -0
  72. package/dist/types/assets/icons/ManifestIcon.d.ts +2 -0
  73. package/dist/types/assets/icons/SensorActiveIcon.d.ts +2 -0
  74. package/dist/types/assets/icons/SensorIcon.d.ts +2 -0
  75. package/dist/types/assets/icons/SiteActiveIcon.d.ts +2 -0
  76. package/dist/types/assets/icons/SiteIcon.d.ts +2 -0
  77. package/dist/types/assets/icons/XrayActiveIcon.d.ts +2 -0
  78. package/dist/types/assets/icons/XrayIcon.d.ts +2 -0
  79. package/dist/types/assets/icons/index.d.ts +14 -0
  80. package/dist/types/assets/logos/DarkLogo.d.ts +2 -0
  81. package/dist/types/assets/logos/DarkSymbol.d.ts +2 -0
  82. package/dist/types/assets/logos/PrimaryLogo.d.ts +2 -0
  83. package/dist/types/assets/logos/PrimarySymbol.d.ts +2 -0
  84. package/dist/types/assets/logos/WhiteLogo.d.ts +2 -0
  85. package/dist/types/assets/logos/WhiteSymbol.d.ts +2 -0
  86. package/dist/types/assets/logos/index.d.ts +6 -0
  87. package/dist/types/components/blocks/AppBentoMenu.d.ts +31 -0
  88. package/dist/types/components/blocks/AppDashboard.d.ts +121 -0
  89. package/dist/types/components/blocks/AppHeader.d.ts +198 -0
  90. package/dist/types/components/blocks/AppLocationTreeFilter.d.ts +44 -0
  91. package/dist/types/components/blocks/AppNotificationsMenu.d.ts +50 -0
  92. package/dist/types/components/blocks/AppPageTitle.d.ts +86 -0
  93. package/dist/types/components/blocks/AppProfile.d.ts +53 -0
  94. package/dist/types/components/blocks/TableManager.d.ts +194 -0
  95. package/dist/types/components/blocks/backgrounds/AppDashboardBackgroundDots.d.ts +14 -0
  96. package/dist/types/components/blocks/backgrounds/AppDashboardBackgroundWave.d.ts +14 -0
  97. package/dist/types/components/blocks/backgrounds/index.d.ts +3 -0
  98. package/dist/types/components/blocks/backgrounds/types.d.ts +13 -0
  99. package/dist/types/components/blocks/index.d.ts +9 -0
  100. package/dist/types/components/primitives/AppAccordion.d.ts +22 -0
  101. package/dist/types/components/primitives/AppAlert.d.ts +33 -0
  102. package/dist/types/components/primitives/AppAlertDialog.d.ts +79 -0
  103. package/dist/types/components/primitives/AppAspectRatio.d.ts +7 -0
  104. package/dist/types/components/primitives/AppAvatar.d.ts +17 -0
  105. package/dist/types/components/primitives/AppBadge.d.ts +9 -0
  106. package/dist/types/components/primitives/AppBreadcrumb.d.ts +45 -0
  107. package/dist/types/components/primitives/AppButton.d.ts +24 -0
  108. package/dist/types/components/primitives/AppButtonGroup.d.ts +10 -0
  109. package/dist/types/components/primitives/AppCalendar.d.ts +51 -0
  110. package/dist/types/components/primitives/AppCard.d.ts +41 -0
  111. package/dist/types/components/primitives/AppCarousel.d.ts +26 -0
  112. package/dist/types/components/primitives/AppChart.d.ts +27 -0
  113. package/dist/types/components/primitives/AppCheckbox.d.ts +9 -0
  114. package/dist/types/components/primitives/AppCollapsible.d.ts +20 -0
  115. package/dist/types/components/primitives/AppCombobox.d.ts +37 -0
  116. package/dist/types/components/primitives/AppCommand.d.ts +47 -0
  117. package/dist/types/components/primitives/AppContextMenu.d.ts +82 -0
  118. package/dist/types/components/primitives/AppDataTable.d.ts +23 -0
  119. package/dist/types/components/primitives/AppDatePicker.d.ts +26 -0
  120. package/dist/types/components/primitives/AppDialog.d.ts +40 -0
  121. package/dist/types/components/primitives/AppDrawer.d.ts +58 -0
  122. package/dist/types/components/primitives/AppDropdownMenu.d.ts +85 -0
  123. package/dist/types/components/primitives/AppEmpty.d.ts +35 -0
  124. package/dist/types/components/primitives/AppField.d.ts +65 -0
  125. package/dist/types/components/primitives/AppForm.d.ts +58 -0
  126. package/dist/types/components/primitives/AppHoverCard.d.ts +19 -0
  127. package/dist/types/components/primitives/AppInput.d.ts +21 -0
  128. package/dist/types/components/primitives/AppKbd.d.ts +18 -0
  129. package/dist/types/components/primitives/AppLabel.d.ts +8 -0
  130. package/dist/types/components/primitives/AppMenubar.d.ts +83 -0
  131. package/dist/types/components/primitives/AppNavigationMenu.d.ts +51 -0
  132. package/dist/types/components/primitives/AppPagination.d.ts +46 -0
  133. package/dist/types/components/primitives/AppPopover.d.ts +20 -0
  134. package/dist/types/components/primitives/AppProgress.d.ts +9 -0
  135. package/dist/types/components/primitives/AppRadioGroup.d.ts +15 -0
  136. package/dist/types/components/primitives/AppScrollArea.d.ts +12 -0
  137. package/dist/types/components/primitives/AppScrollText.d.ts +7 -0
  138. package/dist/types/components/primitives/AppSelect.d.ts +46 -0
  139. package/dist/types/components/primitives/AppSeparator.d.ts +8 -0
  140. package/dist/types/components/primitives/AppSheet.d.ts +32 -0
  141. package/dist/types/components/primitives/AppSidebar.d.ts +59 -0
  142. package/dist/types/components/primitives/AppSkeleton.d.ts +6 -0
  143. package/dist/types/components/primitives/AppSpinner.d.ts +13 -0
  144. package/dist/types/components/primitives/AppSwitch.d.ts +8 -0
  145. package/dist/types/components/primitives/AppTable.d.ts +53 -0
  146. package/dist/types/components/primitives/AppTabs.d.ts +10 -0
  147. package/dist/types/components/primitives/AppTextarea.d.ts +9 -0
  148. package/dist/types/components/primitives/AppThemeProvider.d.ts +46 -0
  149. package/dist/types/components/primitives/AppToaster.d.ts +7 -0
  150. package/dist/types/components/primitives/AppToggle.d.ts +9 -0
  151. package/dist/types/components/primitives/AppToggleGroup.d.ts +18 -0
  152. package/dist/types/components/primitives/AppTooltip.d.ts +26 -0
  153. package/dist/types/components/primitives/AppTypography.d.ts +80 -0
  154. package/dist/types/components/primitives/index.d.ts +57 -0
  155. package/dist/types/components/ui/accordion.d.ts +7 -0
  156. package/dist/types/components/ui/alert-dialog.d.ts +20 -0
  157. package/dist/types/components/ui/alert.d.ts +8 -0
  158. package/dist/types/components/ui/aspect-ratio.d.ts +4 -0
  159. package/dist/types/components/ui/avatar.d.ts +6 -0
  160. package/dist/types/components/ui/badge.d.ts +9 -0
  161. package/dist/types/components/ui/breadcrumb.d.ts +19 -0
  162. package/dist/types/components/ui/button.d.ts +13 -0
  163. package/dist/types/components/ui/calendar.d.ts +8 -0
  164. package/dist/types/components/ui/card.d.ts +11 -0
  165. package/dist/types/components/ui/carousel.d.ts +28 -0
  166. package/dist/types/components/ui/chart.d.ts +48 -0
  167. package/dist/types/components/ui/checkbox.d.ts +4 -0
  168. package/dist/types/components/ui/collapsible.d.ts +6 -0
  169. package/dist/types/components/ui/combobox.d.ts +83 -0
  170. package/dist/types/components/ui/command.d.ts +82 -0
  171. package/dist/types/components/ui/context-menu.d.ts +27 -0
  172. package/dist/types/components/ui/dialog.d.ts +19 -0
  173. package/dist/types/components/ui/drawer.d.ts +23 -0
  174. package/dist/types/components/ui/dropdown-menu.d.ts +27 -0
  175. package/dist/types/components/ui/empty.d.ts +14 -0
  176. package/dist/types/components/ui/field.d.ts +40 -0
  177. package/dist/types/components/ui/form.d.ts +22 -0
  178. package/dist/types/components/ui/hover-card.d.ts +6 -0
  179. package/dist/types/components/ui/input.d.ts +3 -0
  180. package/dist/types/components/ui/kbd.d.ts +4 -0
  181. package/dist/types/components/ui/label.d.ts +4 -0
  182. package/dist/types/components/ui/menubar.d.ts +30 -0
  183. package/dist/types/components/ui/navigation-menu.d.ts +12 -0
  184. package/dist/types/components/ui/pagination.d.ts +28 -0
  185. package/dist/types/components/ui/popover.d.ts +6 -0
  186. package/dist/types/components/ui/progress.d.ts +4 -0
  187. package/dist/types/components/ui/radio-group.d.ts +5 -0
  188. package/dist/types/components/ui/scroll-area.d.ts +5 -0
  189. package/dist/types/components/ui/scroll-text.d.ts +6 -0
  190. package/dist/types/components/ui/select.d.ts +13 -0
  191. package/dist/types/components/ui/separator.d.ts +4 -0
  192. package/dist/types/components/ui/sheet.d.ts +25 -0
  193. package/dist/types/components/ui/sidebar.d.ts +69 -0
  194. package/dist/types/components/ui/skeleton.d.ts +2 -0
  195. package/dist/types/components/ui/sonner.d.ts +2 -0
  196. package/dist/types/components/ui/switch.d.ts +4 -0
  197. package/dist/types/components/ui/table.d.ts +10 -0
  198. package/dist/types/components/ui/tabs.d.ts +19 -0
  199. package/dist/types/components/ui/textarea.d.ts +5 -0
  200. package/dist/types/components/ui/toggle-group.d.ts +15 -0
  201. package/dist/types/components/ui/toggle.d.ts +12 -0
  202. package/dist/types/components/ui/tooltip.d.ts +7 -0
  203. package/dist/types/hooks/use-mobile.d.ts +13 -0
  204. package/dist/types/hooks/use-page-title.d.ts +23 -0
  205. package/dist/types/index.d.ts +2 -0
  206. package/dist/types/lib/utils.d.ts +2 -0
  207. package/dist/ui-kit.css +1 -0
  208. package/package.json +117 -0
  209. package/rules/accordion.md +60 -0
  210. package/rules/alert-dialog.md +62 -0
  211. package/rules/alert.md +61 -0
  212. package/rules/aspect-ratio.md +47 -0
  213. package/rules/avatar.md +52 -0
  214. package/rules/badge.md +55 -0
  215. package/rules/bento-menu.md +86 -0
  216. package/rules/breadcrumb.md +80 -0
  217. package/rules/button-group.md +58 -0
  218. package/rules/button.md +66 -0
  219. package/rules/calendar.md +97 -0
  220. package/rules/card.md +84 -0
  221. package/rules/carousel.md +113 -0
  222. package/rules/chart.md +125 -0
  223. package/rules/checkbox.md +49 -0
  224. package/rules/collapsible.md +57 -0
  225. package/rules/combobox.md +133 -0
  226. package/rules/command.md +73 -0
  227. package/rules/context-menu.md +65 -0
  228. package/rules/dashboard.md +368 -0
  229. package/rules/data-table.md +91 -0
  230. package/rules/date-picker.md +65 -0
  231. package/rules/dialog.md +60 -0
  232. package/rules/drawer.md +65 -0
  233. package/rules/dropdown-menu.md +72 -0
  234. package/rules/empty.md +110 -0
  235. package/rules/field.md +149 -0
  236. package/rules/form.md +60 -0
  237. package/rules/header.md +245 -0
  238. package/rules/hover-card.md +53 -0
  239. package/rules/icons.md +141 -0
  240. package/rules/input.md +66 -0
  241. package/rules/kbd.md +58 -0
  242. package/rules/label.md +30 -0
  243. package/rules/location-tree-filter.md +165 -0
  244. package/rules/logo.md +58 -0
  245. package/rules/menubar.md +75 -0
  246. package/rules/navigation-menu.md +68 -0
  247. package/rules/notifications-menu.md +137 -0
  248. package/rules/pagetitle.md +160 -0
  249. package/rules/pagination.md +71 -0
  250. package/rules/popover.md +48 -0
  251. package/rules/profile.md +125 -0
  252. package/rules/progress.md +41 -0
  253. package/rules/radio-group.md +44 -0
  254. package/rules/scroll-area.md +32 -0
  255. package/rules/scroll-text.md +40 -0
  256. package/rules/select.md +86 -0
  257. package/rules/separator.md +43 -0
  258. package/rules/sheet.md +76 -0
  259. package/rules/sidebar.md +442 -0
  260. package/rules/skeleton.md +46 -0
  261. package/rules/spinner.md +43 -0
  262. package/rules/switch.md +46 -0
  263. package/rules/table-manager.md +306 -0
  264. package/rules/table.md +177 -0
  265. package/rules/tabs.md +82 -0
  266. package/rules/text.md +3 -0
  267. package/rules/textarea.md +38 -0
  268. package/rules/toaster.md +58 -0
  269. package/rules/toggle-group.md +50 -0
  270. package/rules/toggle.md +40 -0
  271. package/rules/tokens.md +88 -0
  272. package/rules/tooltip.md +78 -0
  273. package/rules/typography.md +81 -0
  274. package/scripts/scaffold.mjs +28 -0
@@ -0,0 +1,165 @@
1
+ # AppLocationTreeFilter
2
+
3
+ > Docs: https://ui-kit.vercel.app/docs/blocks/location-tree-filter
4
+
5
+ **This is a block — import from `blocks/`, not `primitives/`.**
6
+
7
+ ```tsx
8
+ import { AppLocationTreeFilter } from "@/components/blocks/AppLocationTreeFilter"
9
+ ```
10
+
11
+ Also available from the barrel:
12
+
13
+ ```tsx
14
+ import { AppLocationTreeFilter } from "@/components/blocks"
15
+ ```
16
+
17
+ ## Hard stop
18
+
19
+ > ❌ **Never compose a location tree filter manually using `AppCollapsible`, raw `<details>`, or any other approach.**
20
+ >
21
+ > `AppLocationTreeFilter` handles the full tree rendering, recursive collapsible structure, search/filter, depth indentation, selection state, and unallocated row. Building this from scratch will produce inconsistent behaviour and visual drift. Use this block.
22
+
23
+ ## Types
24
+
25
+ ```tsx
26
+ type TreeNode = {
27
+ label: string // Display label and the value emitted when this node is selected
28
+ count: number // Badge count shown alongside the label
29
+ children?: TreeNode[] // If present, renders as a collapsible branch
30
+ }
31
+
32
+ type UnallocatedConfig = {
33
+ label?: string // Row label. Defaults to "Unallocated"
34
+ count: number // Badge count
35
+ value?: string // Sentinel value emitted on click. Defaults to "__unallocated__"
36
+ }
37
+ ```
38
+
39
+ ## Props
40
+
41
+ | Prop | Type | Default | Description |
42
+ |------|------|---------|-------------|
43
+ | `data` | `TreeNode[]` | required | Tree data. Top-level nodes with optional nested children. |
44
+ | `selected` | `string \| null` | required | Current selection value. `null` = root/all state. |
45
+ | `onSelect` | `(value: string \| null) => void` | required | Fired when user clicks a node or the unallocated row. |
46
+ | `title` | `string` | `"Filter by Location"` | Label shown above the search input. |
47
+ | `showHelp` | `boolean` | `false` | Shows a help icon next to the title. |
48
+ | `searchPlaceholder` | `string` | `"Search locations..."` | Placeholder for the search input. |
49
+ | `unallocated` | `UnallocatedConfig` | `undefined` | Pinned row above the tree for unallocated records. Omit to hide. |
50
+ | `className` | `string` | `—` | Extra classes on the root container (default width: `w-56`). |
51
+
52
+ ## Selection model
53
+
54
+ The block is **uncontrolled for tree state** (collapse/expand), but **controlled for selection** — your component owns `selected`.
55
+
56
+ | `onSelect` emits | Meaning |
57
+ |-----------------|---------|
58
+ | `null` | Top-level branch node clicked — "show all" / root state |
59
+ | `node.label` | A mid-level or leaf node was clicked — filter by that label |
60
+ | `unallocated.value` (default: `"__unallocated__"`) | The unallocated row was clicked |
61
+
62
+ ```tsx
63
+ const [selected, setSelected] = React.useState<string | null>(null)
64
+
65
+ <AppLocationTreeFilter
66
+ data={tree}
67
+ selected={selected}
68
+ onSelect={setSelected}
69
+ />
70
+ ```
71
+
72
+ ## Search behavior
73
+
74
+ Typing in the search input filters visible tree nodes by label. Branches that contain a matching descendant are automatically expanded. Clearing the search restores the original collapsed state.
75
+
76
+ ## Usage
77
+
78
+ ### Basic
79
+
80
+ ```tsx
81
+ const tree: TreeNode[] = [
82
+ {
83
+ label: "All",
84
+ count: 65,
85
+ children: [
86
+ {
87
+ label: "Philippines",
88
+ count: 65,
89
+ children: [
90
+ { label: "Davao City", count: 1 },
91
+ { label: "Cagayan de Oro", count: 64 },
92
+ ],
93
+ },
94
+ ],
95
+ },
96
+ ]
97
+
98
+ export default function Page() {
99
+ const [selected, setSelected] = React.useState<string | null>(null)
100
+
101
+ return (
102
+ <AppLocationTreeFilter
103
+ data={tree}
104
+ selected={selected}
105
+ onSelect={setSelected}
106
+ />
107
+ )
108
+ }
109
+ ```
110
+
111
+ ### With unallocated row
112
+
113
+ ```tsx
114
+ <AppLocationTreeFilter
115
+ data={tree}
116
+ title="Filter by Location"
117
+ showHelp
118
+ unallocated={{ label: "Unallocated Devices", count: 135 }}
119
+ selected={selected}
120
+ onSelect={setSelected}
121
+ />
122
+ ```
123
+
124
+ ### URL search params
125
+
126
+ The `unallocated.value` sentinel (default `"__unallocated__"`) must be encoded when storing in URL params:
127
+
128
+ ```tsx
129
+ // Writing to URL
130
+ const params = new URLSearchParams()
131
+ if (selected !== null) params.set("location", encodeURIComponent(selected))
132
+
133
+ // Reading from URL
134
+ const raw = params.get("location")
135
+ const selected = raw ? decodeURIComponent(raw) : null
136
+ ```
137
+
138
+ ### Custom width
139
+
140
+ Override the default `w-56` via `className`:
141
+
142
+ ```tsx
143
+ <AppLocationTreeFilter
144
+ data={tree}
145
+ selected={selected}
146
+ onSelect={setSelected}
147
+ className="w-64"
148
+ />
149
+ ```
150
+
151
+ ## Rules
152
+
153
+ - ✅ Always use `AppLocationTreeFilter` for hierarchical location filtering — never compose manually
154
+ - ✅ Provide a top-level node (e.g. "All") as the root branch — it emits `null` on click and represents the "show all" state
155
+ - ✅ Own the `selected` state in your page or parent component — the block does not manage selection internally
156
+ - ✅ Use `unallocated` whenever records can exist without an assigned location
157
+ - ✅ Set a custom `unallocated.value` if `"__unallocated__"` conflicts with any real location label in your dataset
158
+ - ❌ Never import `AppCollapsible` to build a tree structure — use this block
159
+ - ❌ Never manage tree expand/collapse state externally — the block handles it internally
160
+ - ❌ Never pass a flat list as `data` — the tree structure must be nested via `children`
161
+ - ❌ If a prop you need is missing, stop and inform the design team
162
+
163
+ ## Responsive
164
+
165
+ The root container uses `w-full sm:w-56 sm:shrink-0` — on mobile it expands to full width and stacks vertically with surrounding content; on desktop it returns to its fixed `14rem` width and does not shrink. A drawer-based mobile trigger is not implemented — wrap in `AppSheet` at the page level if a collapsible filter panel is needed.
package/rules/logo.md ADDED
@@ -0,0 +1,58 @@
1
+ # Logo Assets
2
+
3
+ Assets ship as raw files in `dist/assets/` (requires `@skunkworksai/ui-kit@>=0.1.5`). They are scaffolded into your project during setup — import from your own files, never from the package directly:
4
+
5
+ ```tsx
6
+ import { PrimaryLogo, DarkLogo, WhiteLogo } from "./assets/logos"
7
+ import { PrimarySymbol, DarkSymbol, WhiteSymbol } from "./assets/logos"
8
+ ```
9
+
10
+ ## Variants
11
+
12
+ | Component | Fill color | Use on |
13
+ |-----------|-----------|--------|
14
+ | `PrimaryLogo` | Brand blue | Light backgrounds, white surfaces |
15
+ | `DarkLogo` | Dark | Light backgrounds when primary blue is too prominent |
16
+ | `WhiteLogo` | White | Dark backgrounds, colored sidebars, dark-mode |
17
+ | `PrimarySymbol` | Brand blue | Icon-only contexts on light backgrounds |
18
+ | `DarkSymbol` | Dark | Icon-only on light |
19
+ | `WhiteSymbol` | White | Icon-only on dark backgrounds or colored fills |
20
+
21
+ ## Default dimensions
22
+
23
+ All logos share the same `265 × 83` viewBox. Pass `width` and `height` to scale proportionally:
24
+
25
+ ```tsx
26
+ <PrimaryLogo width={120} height={38} /> {/* standard header size */}
27
+ <WhiteLogo width={160} height={50} /> {/* larger marketing use */}
28
+ <WhiteSymbol width={32} height={32} /> {/* icon-only mark */}
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ### Light + dark mode pair
34
+
35
+ ```tsx
36
+ <PrimaryLogo className="block dark:hidden" width={120} height={38} />
37
+ <WhiteLogo className="hidden dark:block" width={120} height={38} />
38
+ ```
39
+
40
+ ### Symbol only
41
+
42
+ ```tsx
43
+ <WhiteSymbol width={28} height={28} />
44
+ ```
45
+
46
+ ## Rules
47
+
48
+ - ✅ Import from `./assets/logos` — never from `@skunkworksai/ui-kit`
49
+ - ✅ Use `PrimaryLogo` on light backgrounds (docs, marketing pages, cards)
50
+ - ✅ Use `WhiteLogo` or `WhiteSymbol` on dark backgrounds
51
+ - ✅ Support dark mode with `block dark:hidden` / `hidden dark:block` pair
52
+ - ❌ Do not hardcode logo colors with Tailwind color classes — use the correct variant instead
53
+ - ❌ Do not distort the logo — always scale both `width` and `height` proportionally from the `265 × 83` viewBox ratio
54
+ - ❌ If you need a logo variant that doesn't exist, inform the design team — do not create ad-hoc SVGs
55
+
56
+ ## On package upgrade
57
+
58
+ Re-run the asset scaffold step and diff `src/assets/logos/` against the new `dist/assets/logos/` if logos changed.
@@ -0,0 +1,75 @@
1
+ # AppMenubar
2
+
3
+ > Docs: https://ui-kit.vercel.app/docs/components/menubar
4
+
5
+ ```tsx
6
+ import {
7
+ AppMenubar,
8
+ AppMenubarMenu,
9
+ AppMenubarTrigger,
10
+ AppMenubarContent,
11
+ AppMenubarItem,
12
+ AppMenubarSeparator,
13
+ AppMenubarLabel,
14
+ AppMenubarCheckboxItem,
15
+ AppMenubarRadioGroup,
16
+ AppMenubarRadioItem,
17
+ AppMenubarPortal,
18
+ AppMenubarGroup,
19
+ AppMenubarSub,
20
+ AppMenubarSubContent,
21
+ AppMenubarSubTrigger,
22
+ AppMenubarShortcut,
23
+ } from "@/components/primitives/AppMenubar"
24
+ ```
25
+
26
+ ## AppMenubar
27
+
28
+ | Prop | Type | Default | Description |
29
+ | :--- | :--- | :--- | :--- |
30
+ | `defaultValue` | `string` | - | The value of the menu that should be open by default. |
31
+
32
+ ## AppMenubarTrigger
33
+
34
+ | Prop | Type | Default | Description |
35
+ | :--- | :--- | :--- | :--- |
36
+ | `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child. |
37
+
38
+ ## Usage
39
+
40
+ ### Desktop-style Menubar
41
+ ```tsx
42
+ <AppMenubar>
43
+ <AppMenubarMenu>
44
+ <AppMenubarTrigger>File</AppMenubarTrigger>
45
+ <AppMenubarContent>
46
+ <AppMenubarItem>
47
+ New Tab <AppMenubarShortcut>⌘T</AppMenubarShortcut>
48
+ </AppMenubarItem>
49
+ <AppMenubarItem>
50
+ New Window <AppMenubarShortcut>⌘N</AppMenubarShortcut>
51
+ </AppMenubarItem>
52
+ <AppMenubarSeparator />
53
+ <AppMenubarItem>Print</AppMenubarItem>
54
+ </AppMenubarContent>
55
+ </AppMenubarMenu>
56
+ <AppMenubarMenu>
57
+ <AppMenubarTrigger>Edit</AppMenubarTrigger>
58
+ <AppMenubarContent>
59
+ <AppMenubarItem>Undo</AppMenubarItem>
60
+ <AppMenubarItem>Redo</AppMenubarItem>
61
+ </AppMenubarContent>
62
+ </AppMenubarMenu>
63
+ </AppMenubar>
64
+ ```
65
+
66
+ ## Rules
67
+
68
+ - **Do** use `AppMenubar` for application-wide persistent menus.
69
+ - **Do** use `AppMenubarShortcut` to display keyboard shortcuts.
70
+ - **Don't** use `AppMenubar` for simple navigation; use `AppNavigationMenu` for website headers.
71
+ - If a prop you need is missing, stop and inform the design team.
72
+
73
+ ## Responsive
74
+
75
+ `AppMenubar` scrolls horizontally on mobile via an `overflow-x-auto` wrapper with `min-w-max` on the inner bar. A Sheet-based mobile menu is not implemented — compose one at the page level using `AppSheet` if a collapsing menu is required.
@@ -0,0 +1,68 @@
1
+ # AppNavigationMenu
2
+
3
+ > Docs: https://ui-kit.vercel.app/docs/components/navigation-menu
4
+
5
+ ```tsx
6
+ import {
7
+ AppNavigationMenu,
8
+ AppNavigationMenuList,
9
+ AppNavigationMenuItem,
10
+ AppNavigationMenuTrigger,
11
+ AppNavigationMenuContent,
12
+ AppNavigationMenuLink,
13
+ AppNavigationMenuIndicator,
14
+ AppNavigationMenuViewport,
15
+ navigationMenuTriggerStyle,
16
+ } from "@/components/primitives/AppNavigationMenu"
17
+ ```
18
+
19
+ ## AppNavigationMenu
20
+
21
+ | Prop | Type | Default | Description |
22
+ | :--- | :--- | :--- | :--- |
23
+ | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | The orientation of the menu. |
24
+
25
+ ## AppNavigationMenuLink
26
+
27
+ | Prop | Type | Default | Description |
28
+ | :--- | :--- | :--- | :--- |
29
+ | `active` | `boolean` | - | Whether the link is currently active. |
30
+ | `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child. |
31
+
32
+ ## Usage
33
+
34
+ ### Standard Navigation Menu
35
+ ```tsx
36
+ <AppNavigationMenu>
37
+ <AppNavigationMenuList>
38
+ <AppNavigationMenuItem>
39
+ <AppNavigationMenuTrigger>Components</AppNavigationMenuTrigger>
40
+ <AppNavigationMenuContent>
41
+ <ul className="grid w-[400px] gap-3 p-4">
42
+ <li>
43
+ <AppNavigationMenuLink asChild>
44
+ <a href="/docs/alert">Alert</a>
45
+ </AppNavigationMenuLink>
46
+ </li>
47
+ </ul>
48
+ </AppNavigationMenuContent>
49
+ </AppNavigationMenuItem>
50
+ <AppNavigationMenuItem>
51
+ <AppNavigationMenuLink className={navigationMenuTriggerStyle()} href="/docs">
52
+ Documentation
53
+ </AppNavigationMenuLink>
54
+ </AppNavigationMenuItem>
55
+ </AppNavigationMenuList>
56
+ </AppNavigationMenu>
57
+ ```
58
+
59
+ ## Rules
60
+
61
+ - **Do** use `AppNavigationMenu` for primary website navigation.
62
+ - **Do** use `navigationMenuTriggerStyle()` for consistent styling on top-level links.
63
+ - **Don't** nest `AppNavigationMenu` components.
64
+ - If a prop you need is missing, stop and inform the design team.
65
+
66
+ ## Responsive
67
+
68
+ `AppNavigationMenuList` scrolls horizontally on mobile via an `overflow-x-auto` wrapper with `min-w-max` on items so they never wrap. A hamburger-to-Sheet pattern is not implemented — if a collapsing nav is needed at mobile widths, build it at the page level using `AppSheet`.
@@ -0,0 +1,137 @@
1
+ # AppNotificationsMenu
2
+
3
+ ## When to use
4
+
5
+ `AppNotificationsMenu` is a bell-icon trigger that opens a popover panel listing notifications. It is rendered automatically inside `AppHeader` — you do not need to import it directly unless building a layout outside `AppDashboard`.
6
+
7
+ To show notifications inside `AppDashboard`, pass notification props via the `header` prop on `AppDashboard`:
8
+
9
+ ```tsx
10
+ <AppDashboard
11
+ header={{
12
+ notifications: myNotifications,
13
+ notificationsTotal: 1433,
14
+ onShowMoreNotifications: handleShowMore,
15
+ onMarkAllNotificationsRead: handleMarkAll,
16
+ }}
17
+ >
18
+ ...
19
+ </AppDashboard>
20
+ ```
21
+
22
+ ## Props
23
+
24
+ | Prop | Type | Default | Description |
25
+ |------|------|---------|-------------|
26
+ | `notifications` | `AppNotification[]` | `[]` | Notification items to display. Component is data-dumb — caller owns the array. |
27
+ | `totalCount` | `number` | `notifications.length` | Total count on the server. Used for "Show More (N of TOTAL)" footer label. |
28
+ | `pageSize` | `number` | `10` | Initial number of visible items. |
29
+ | `onShowMore` | `() => void` | — | Called when "Show More" is clicked. Append more items to `notifications` in the caller. |
30
+ | `onMarkAllAsRead` | `() => void` | — | Called when "Mark all as read" is selected from the meatball menu. |
31
+ | `open` | `boolean` | — | Controlled open state for the popover. |
32
+ | `onOpenChange` | `(open: boolean) => void` | — | Called when the panel open state changes. |
33
+ | `trigger` | `ReactNode` | Bell icon button | Replaces the default bell trigger entirely. |
34
+ | `className` | `string` | — | Extra classes applied to the popover content panel. |
35
+
36
+ ## AppNotification type
37
+
38
+ ```ts
39
+ type AppNotification = {
40
+ id: string
41
+ title: string
42
+ description?: string
43
+ /** ISO string or Date. Rendered as relative time ("20d ago"). */
44
+ timestamp: string | Date
45
+ variant?: "info" | "success" | "warning" | "destructive" // default "info"
46
+ /** When false, renders an unread dot before the title. */
47
+ read?: boolean
48
+ /** Override the default variant icon. */
49
+ icon?: React.ComponentType<React.SVGProps<SVGSVGElement>>
50
+ href?: string
51
+ onClick?: () => void
52
+ }
53
+ ```
54
+
55
+ ## Variant → icon map
56
+
57
+ | Variant | Icon | Container colors |
58
+ |---------|------|-----------------|
59
+ | `info` | `Info` | `bg-info text-info-foreground` |
60
+ | `success` | `CheckCircle2` | `bg-success text-success-foreground` |
61
+ | `warning` | `AlertTriangle` | `bg-warning text-warning-foreground` |
62
+ | `destructive` | `AlertCircle` | `bg-destructive text-destructive-foreground` |
63
+
64
+ ## Usage
65
+
66
+ ### Inside AppDashboard (standard)
67
+
68
+ ```tsx
69
+ <AppDashboard
70
+ nav={nav}
71
+ header={{
72
+ notifications: items,
73
+ notificationsTotal: 1433,
74
+ onShowMoreNotifications: () => fetchMore().then(more => setItems(prev => [...prev, ...more])),
75
+ onMarkAllNotificationsRead: () => setItems(prev => prev.map(n => ({ ...n, read: true }))),
76
+ }}
77
+ >
78
+ <PageContent />
79
+ </AppDashboard>
80
+ ```
81
+
82
+ ### Hide the bell
83
+
84
+ ```tsx
85
+ <AppDashboard header={{ showNotifications: false }}>...</AppDashboard>
86
+ ```
87
+
88
+ ### Custom bell trigger
89
+
90
+ ```tsx
91
+ <AppNotificationsMenu
92
+ notifications={items}
93
+ trigger={<AppButton variant="outline" size="sm">Alerts</AppButton>}
94
+ />
95
+ ```
96
+
97
+ ### Server-paged Show More
98
+
99
+ The component is intentionally dumb about data. "Show More" fires `onShowMore` — caller fetches the next page and appends to `notifications`. The footer auto-hides when `notifications.length >= totalCount`.
100
+
101
+ ```tsx
102
+ const [items, setItems] = useState<AppNotification[]>(initialPage)
103
+
104
+ <AppNotificationsMenu
105
+ notifications={items}
106
+ totalCount={serverTotal}
107
+ pageSize={10}
108
+ onShowMore={async () => {
109
+ const next = await fetchPage(items.length)
110
+ setItems(prev => [...prev, ...next])
111
+ }}
112
+ />
113
+ ```
114
+
115
+ ## Default header action order
116
+
117
+ ```
118
+ network combobox → bell → bento → theme toggle → avatar
119
+ ```
120
+
121
+ The bell is positioned between the network combobox and the bento icon. This order is fixed in `AppHeader` default actions and cannot be changed via props (use the `actions` slot override on `AppHeader` to reorder).
122
+
123
+ ## Rules
124
+
125
+ - **Do** keep the component data-dumb — never mutate `notifications` inside the component. Caller owns the array.
126
+ - **Do** pass `totalCount` when data is server-paged so the footer shows the correct "N of TOTAL" label.
127
+ - **Do** set `read: false` on new items to render the unread blue dot before the title.
128
+ - **Do** provide an `href` or `onClick` on each row that should be navigable.
129
+ - **Don't** add an unread badge count to the bell trigger — deferred to a future `unreadCount` prop.
130
+ - **Don't** sort or filter notifications inside this component — do it in the caller before passing the array.
131
+ - **Don't** use `AppScrollArea` inside this component — the list uses a plain overflow div; Radix ScrollArea requires a fixed height which conflicts with dynamic list heights.
132
+
133
+ If a prop you need is missing, stop and inform the design team.
134
+
135
+ ## Responsive
136
+
137
+ On mobile (<768px) the panel renders as a side `AppDrawer` with the header row showing title, meatball menu, and close button together. On desktop (≥768px) it renders as a floating `AppPopover`. The component uses `useIsMobile` to branch between these two paths internally — the public API is unchanged across all viewport sizes. The bell trigger remains visible at all viewport widths.
@@ -0,0 +1,160 @@
1
+ # AppPageTitle
2
+
3
+ > **Default page header block** — present on most pages. Suppress it explicitly with `pageTitle={false}`.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { AppPageTitle } from "@/components/blocks/AppPageTitle"
9
+ ```
10
+
11
+ Or via `AppDashboard` (recommended):
12
+
13
+ ```tsx
14
+ import { AppDashboard } from "@/components/blocks/AppDashboard"
15
+ ```
16
+
17
+ ## Props
18
+
19
+ | Prop | Type | Default | Description |
20
+ |------|------|---------|-------------|
21
+ | `title` | `string` | auto | Page heading, rendered as an `<h1>`. Auto-derived from the last pathname segment when omitted. |
22
+ | `subtitle` | `string` | — | Optional description rendered below the heading. |
23
+ | `breadcrumbs` | `AppPageTitleBreadcrumb[]` | auto | Explicit breadcrumb trail. Auto-generated from `window.location.pathname` when omitted. |
24
+ | `actions` | `ReactNode` | — | Right-side action slot. Accepts any elements — buttons, selects, text, etc. Empty by default. |
25
+ | `icon` | `ReactNode` | — | Optional icon rendered to the left of the title stack. Accepts `<img>`, SVG, or Lucide icons. Omit to hide. |
26
+ | `titleColor` | `"default" \| "primary"` | `"default"` | Controls the h1 color. `"default"` uses `text-foreground`; `"primary"` uses the brand blue. |
27
+ | `hidden` | `boolean` | `false` | Hides the entire block. |
28
+ | `className` | `string` | — | Extra classes on the root element. |
29
+
30
+ ### AppPageTitleBreadcrumb
31
+
32
+ ```ts
33
+ type AppPageTitleBreadcrumb = {
34
+ label: string
35
+ href?: string // Omit for the last (current-page) segment
36
+ }
37
+ ```
38
+
39
+ ## Usage via AppDashboard
40
+
41
+ ### Default (title + breadcrumbs auto-derived from pathname)
42
+
43
+ ```tsx
44
+ <AppDashboard nav={nav}>
45
+ <PageContent />
46
+ </AppDashboard>
47
+ ```
48
+
49
+ ### With explicit title and subtitle
50
+
51
+ ```tsx
52
+ <AppDashboard
53
+ nav={nav}
54
+ pageTitle={{ title: "Site", subtitle: "Stay up to date to everything in your network" }}
55
+ >
56
+ <PageContent />
57
+ </AppDashboard>
58
+ ```
59
+
60
+ ### With actions
61
+
62
+ ```tsx
63
+ <AppDashboard
64
+ nav={nav}
65
+ pageTitle={{
66
+ title: "Site",
67
+ subtitle: "Stay up to date to everything in your network",
68
+ actions: <AppButton>+ Add Site</AppButton>,
69
+ }}
70
+ >
71
+ <PageContent />
72
+ </AppDashboard>
73
+ ```
74
+
75
+ ### Hidden
76
+
77
+ ```tsx
78
+ <AppDashboard nav={nav} pageTitle={false}>
79
+ <PageContent />
80
+ </AppDashboard>
81
+ ```
82
+
83
+ ### Manual breadcrumbs
84
+
85
+ ```tsx
86
+ <AppDashboard
87
+ nav={nav}
88
+ pageTitle={{
89
+ title: "Site Details",
90
+ breadcrumbs: [
91
+ { label: "Manage", href: "/manage" },
92
+ { label: "Site", href: "/manage/site" },
93
+ { label: "Site Details" },
94
+ ],
95
+ }}
96
+ >
97
+ <PageContent />
98
+ </AppDashboard>
99
+ ```
100
+
101
+ ## Breadcrumb auto-generation
102
+
103
+ When `breadcrumbs` is omitted, the component reads `window.location.pathname` on mount and converts each path segment into a readable label:
104
+
105
+ - Segments are split on `-` and `_`, then title-cased (`site-settings` → `Site Settings`)
106
+ - The final segment has no `href` (current page, non-linked)
107
+ - All preceding segments get `href` values pointing to their cumulative path
108
+
109
+ ## With icon
110
+
111
+ ```tsx
112
+ <AppDashboard
113
+ nav={nav}
114
+ pageTitle={{
115
+ title: "Hi Kevin! Welcome to Skunkworks",
116
+ subtitle: "Stay up to date to everything in your network",
117
+ breadcrumbs: [{ label: "Dashboard" }],
118
+ titleColor: "primary",
119
+ icon: (
120
+ <img
121
+ src="/persistent-icon.png"
122
+ alt="Dashboard"
123
+ className="size-16 rounded-xl"
124
+ />
125
+ ),
126
+ }}
127
+ >
128
+ <PageContent />
129
+ </AppDashboard>
130
+ ```
131
+
132
+ ## With primary title color
133
+
134
+ ```tsx
135
+ <AppDashboard
136
+ nav={nav}
137
+ pageTitle={{
138
+ title: "Dashboard",
139
+ titleColor: "primary",
140
+ }}
141
+ >
142
+ <PageContent />
143
+ </AppDashboard>
144
+ ```
145
+
146
+ ## Rules
147
+
148
+ - **Do** use `pageTitle` on `AppDashboard` rather than placing `AppPageTitle` manually inside the page content.
149
+ - **Do** pass `pageTitle={false}` to explicitly suppress the block — do not conditionally omit it based on state.
150
+ - **Do** put action buttons in the `actions` slot — do not place them above the title.
151
+ - **Do** size and style the `icon` yourself — pass `className="size-16 rounded-xl"` on the `<img>` or wrapper. The component does not enforce icon dimensions.
152
+ - **Don't** hardcode breadcrumbs when auto-generation is correct — only override when the URL structure doesn't match the desired labels.
153
+ - **Don't** add additional margin-top to page content — `AppPageTitle` already provides `mb-6` spacing.
154
+ - **Don't** use raw color values for `titleColor` overrides — use the `titleColor` prop, not a `className` on the heading.
155
+
156
+ If a prop you need is missing, stop and inform the design team.
157
+
158
+ ## Responsive
159
+
160
+ On mobile (<768px) the title row stacks vertically (`flex-col`) with actions rendering below the title. On desktop (≥768px) title and actions are inline (`flex-row justify-between`). When a breadcrumb trail has more than 2 segments, middle items are automatically hidden on mobile using `hideOnMobile` — no manual configuration required.