@braedonsaunders/appkit-ui 0.2.0

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 (300) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +11 -0
  3. package/account-menu.d.ts +49 -0
  4. package/account-menu.d.ts.map +1 -0
  5. package/account-menu.js +66 -0
  6. package/account-menu.js.map +1 -0
  7. package/alert.d.ts +13 -0
  8. package/alert.d.ts.map +1 -0
  9. package/alert.js +22 -0
  10. package/alert.js.map +1 -0
  11. package/animated-number.d.ts +13 -0
  12. package/animated-number.d.ts.map +1 -0
  13. package/animated-number.js +53 -0
  14. package/animated-number.js.map +1 -0
  15. package/api-reference.d.ts +44 -0
  16. package/api-reference.d.ts.map +1 -0
  17. package/api-reference.js +82 -0
  18. package/api-reference.js.map +1 -0
  19. package/app-shell.d.ts +39 -0
  20. package/app-shell.d.ts.map +1 -0
  21. package/app-shell.js +21 -0
  22. package/app-shell.js.map +1 -0
  23. package/app-sidebar.d.ts +22 -0
  24. package/app-sidebar.d.ts.map +1 -0
  25. package/app-sidebar.js +30 -0
  26. package/app-sidebar.js.map +1 -0
  27. package/avatar.d.ts +9 -0
  28. package/avatar.d.ts.map +1 -0
  29. package/avatar.js +18 -0
  30. package/avatar.js.map +1 -0
  31. package/badge.d.ts +9 -0
  32. package/badge.d.ts.map +1 -0
  33. package/badge.js +23 -0
  34. package/badge.js.map +1 -0
  35. package/button.d.ts +24 -0
  36. package/button.d.ts.map +1 -0
  37. package/button.js +50 -0
  38. package/button.js.map +1 -0
  39. package/card.d.ts +21 -0
  40. package/card.d.ts.map +1 -0
  41. package/card.js +36 -0
  42. package/card.js.map +1 -0
  43. package/checkbox.d.ts +4 -0
  44. package/checkbox.d.ts.map +1 -0
  45. package/checkbox.js +6 -0
  46. package/checkbox.js.map +1 -0
  47. package/confirm-dialog.d.ts +23 -0
  48. package/confirm-dialog.d.ts.map +1 -0
  49. package/confirm-dialog.js +67 -0
  50. package/confirm-dialog.js.map +1 -0
  51. package/context-menu.d.ts +47 -0
  52. package/context-menu.d.ts.map +1 -0
  53. package/context-menu.js +90 -0
  54. package/context-menu.js.map +1 -0
  55. package/dialog.d.ts +25 -0
  56. package/dialog.d.ts.map +1 -0
  57. package/dialog.js +89 -0
  58. package/dialog.js.map +1 -0
  59. package/document-title.d.ts +11 -0
  60. package/document-title.d.ts.map +1 -0
  61. package/document-title.js +17 -0
  62. package/document-title.js.map +1 -0
  63. package/drawer.d.ts +63 -0
  64. package/drawer.d.ts.map +1 -0
  65. package/drawer.js +188 -0
  66. package/drawer.js.map +1 -0
  67. package/empty-state.d.ts +10 -0
  68. package/empty-state.d.ts.map +1 -0
  69. package/empty-state.js +7 -0
  70. package/empty-state.js.map +1 -0
  71. package/file-uploader.d.ts +67 -0
  72. package/file-uploader.d.ts.map +1 -0
  73. package/file-uploader.js +156 -0
  74. package/file-uploader.js.map +1 -0
  75. package/filter-chips.d.ts +47 -0
  76. package/filter-chips.d.ts.map +1 -0
  77. package/filter-chips.js +62 -0
  78. package/filter-chips.js.map +1 -0
  79. package/global-search.d.ts +41 -0
  80. package/global-search.d.ts.map +1 -0
  81. package/global-search.js +116 -0
  82. package/global-search.js.map +1 -0
  83. package/index.d.ts +70 -0
  84. package/index.d.ts.map +1 -0
  85. package/index.js +70 -0
  86. package/index.js.map +1 -0
  87. package/input.d.ts +9 -0
  88. package/input.d.ts.map +1 -0
  89. package/input.js +11 -0
  90. package/input.js.map +1 -0
  91. package/label.d.ts +3 -0
  92. package/label.d.ts.map +1 -0
  93. package/label.js +6 -0
  94. package/label.js.map +1 -0
  95. package/line-grid.d.ts +48 -0
  96. package/line-grid.d.ts.map +1 -0
  97. package/line-grid.js +155 -0
  98. package/line-grid.js.map +1 -0
  99. package/link-context.d.ts +44 -0
  100. package/link-context.d.ts.map +1 -0
  101. package/link-context.js +32 -0
  102. package/link-context.js.map +1 -0
  103. package/list-nav.d.ts +28 -0
  104. package/list-nav.d.ts.map +1 -0
  105. package/list-nav.js +41 -0
  106. package/list-nav.js.map +1 -0
  107. package/list-pagination.d.ts +27 -0
  108. package/list-pagination.d.ts.map +1 -0
  109. package/list-pagination.js +43 -0
  110. package/list-pagination.js.map +1 -0
  111. package/list-params.d.ts +41 -0
  112. package/list-params.d.ts.map +1 -0
  113. package/list-params.js +84 -0
  114. package/list-params.js.map +1 -0
  115. package/list-search-input-state.d.ts +13 -0
  116. package/list-search-input-state.d.ts.map +1 -0
  117. package/list-search-input-state.js +27 -0
  118. package/list-search-input-state.js.map +1 -0
  119. package/list-search-input.d.ts +16 -0
  120. package/list-search-input.d.ts.map +1 -0
  121. package/list-search-input.js +51 -0
  122. package/list-search-input.js.map +1 -0
  123. package/mobile-tab-bar.d.ts +14 -0
  124. package/mobile-tab-bar.d.ts.map +1 -0
  125. package/mobile-tab-bar.js +25 -0
  126. package/mobile-tab-bar.js.map +1 -0
  127. package/multi-select.d.ts +32 -0
  128. package/multi-select.d.ts.map +1 -0
  129. package/multi-select.js +52 -0
  130. package/multi-select.js.map +1 -0
  131. package/nav-overflow.d.ts +12 -0
  132. package/nav-overflow.d.ts.map +1 -0
  133. package/nav-overflow.js +30 -0
  134. package/nav-overflow.js.map +1 -0
  135. package/navigation-config-editor.d.ts +26 -0
  136. package/navigation-config-editor.d.ts.map +1 -0
  137. package/navigation-config-editor.js +57 -0
  138. package/navigation-config-editor.js.map +1 -0
  139. package/navigation-config.d.ts +48 -0
  140. package/navigation-config.d.ts.map +1 -0
  141. package/navigation-config.js +75 -0
  142. package/navigation-config.js.map +1 -0
  143. package/navigation-mode.d.ts +16 -0
  144. package/navigation-mode.d.ts.map +1 -0
  145. package/navigation-mode.js +30 -0
  146. package/navigation-mode.js.map +1 -0
  147. package/notifications-bell.d.ts +22 -0
  148. package/notifications-bell.d.ts.map +1 -0
  149. package/notifications-bell.js +30 -0
  150. package/notifications-bell.js.map +1 -0
  151. package/org-chart.d.ts +85 -0
  152. package/org-chart.d.ts.map +1 -0
  153. package/org-chart.js +193 -0
  154. package/org-chart.js.map +1 -0
  155. package/package.json +61 -0
  156. package/page-header.d.ts +32 -0
  157. package/page-header.d.ts.map +1 -0
  158. package/page-header.js +14 -0
  159. package/page-header.js.map +1 -0
  160. package/page-layout.d.ts +29 -0
  161. package/page-layout.d.ts.map +1 -0
  162. package/page-layout.js +23 -0
  163. package/page-layout.js.map +1 -0
  164. package/page-transition.d.ts +13 -0
  165. package/page-transition.d.ts.map +1 -0
  166. package/page-transition.js +14 -0
  167. package/page-transition.js.map +1 -0
  168. package/paged-table.d.ts +54 -0
  169. package/paged-table.d.ts.map +1 -0
  170. package/paged-table.js +74 -0
  171. package/paged-table.js.map +1 -0
  172. package/popover.d.ts +16 -0
  173. package/popover.d.ts.map +1 -0
  174. package/popover.js +99 -0
  175. package/popover.js.map +1 -0
  176. package/progress.d.ts +7 -0
  177. package/progress.d.ts.map +1 -0
  178. package/progress.js +8 -0
  179. package/progress.js.map +1 -0
  180. package/prompt-dialog.d.ts +21 -0
  181. package/prompt-dialog.d.ts.map +1 -0
  182. package/prompt-dialog.js +109 -0
  183. package/prompt-dialog.js.map +1 -0
  184. package/record-list.d.ts +76 -0
  185. package/record-list.d.ts.map +1 -0
  186. package/record-list.js +51 -0
  187. package/record-list.js.map +1 -0
  188. package/search-select.d.ts +42 -0
  189. package/search-select.d.ts.map +1 -0
  190. package/search-select.js +194 -0
  191. package/search-select.js.map +1 -0
  192. package/select-options.d.ts +16 -0
  193. package/select-options.d.ts.map +1 -0
  194. package/select-options.js +119 -0
  195. package/select-options.js.map +1 -0
  196. package/select.d.ts +24 -0
  197. package/select.d.ts.map +1 -0
  198. package/select.js +65 -0
  199. package/select.js.map +1 -0
  200. package/separator.d.ts +6 -0
  201. package/separator.d.ts.map +1 -0
  202. package/separator.js +7 -0
  203. package/separator.js.map +1 -0
  204. package/settings-layout.d.ts +79 -0
  205. package/settings-layout.d.ts.map +1 -0
  206. package/settings-layout.js +74 -0
  207. package/settings-layout.js.map +1 -0
  208. package/sidebar-nav.d.ts +67 -0
  209. package/sidebar-nav.d.ts.map +1 -0
  210. package/sidebar-nav.js +291 -0
  211. package/sidebar-nav.js.map +1 -0
  212. package/signature-pad.d.ts +16 -0
  213. package/signature-pad.d.ts.map +1 -0
  214. package/signature-pad.js +355 -0
  215. package/signature-pad.js.map +1 -0
  216. package/skeleton.d.ts +9 -0
  217. package/skeleton.d.ts.map +1 -0
  218. package/skeleton.js +12 -0
  219. package/skeleton.js.map +1 -0
  220. package/slider.d.ts +13 -0
  221. package/slider.d.ts.map +1 -0
  222. package/slider.js +15 -0
  223. package/slider.js.map +1 -0
  224. package/sortable-th.d.ts +28 -0
  225. package/sortable-th.d.ts.map +1 -0
  226. package/sortable-th.js +35 -0
  227. package/sortable-th.js.map +1 -0
  228. package/sparkline.d.ts +16 -0
  229. package/sparkline.d.ts.map +1 -0
  230. package/sparkline.js +46 -0
  231. package/sparkline.js.map +1 -0
  232. package/spinner.d.ts +11 -0
  233. package/spinner.d.ts.map +1 -0
  234. package/spinner.js +11 -0
  235. package/spinner.js.map +1 -0
  236. package/styles.css +166 -0
  237. package/subtab-nav.d.ts +17 -0
  238. package/subtab-nav.d.ts.map +1 -0
  239. package/subtab-nav.js +11 -0
  240. package/subtab-nav.js.map +1 -0
  241. package/switch.d.ts +9 -0
  242. package/switch.d.ts.map +1 -0
  243. package/switch.js +11 -0
  244. package/switch.js.map +1 -0
  245. package/tab-content.d.ts +24 -0
  246. package/tab-content.d.ts.map +1 -0
  247. package/tab-content.js +26 -0
  248. package/tab-content.js.map +1 -0
  249. package/table.d.ts +28 -0
  250. package/table.d.ts.map +1 -0
  251. package/table.js +21 -0
  252. package/table.js.map +1 -0
  253. package/tabs.d.ts +18 -0
  254. package/tabs.d.ts.map +1 -0
  255. package/tabs.js +18 -0
  256. package/tabs.js.map +1 -0
  257. package/text-context.d.ts +9 -0
  258. package/text-context.d.ts.map +1 -0
  259. package/text-context.js +12 -0
  260. package/text-context.js.map +1 -0
  261. package/textarea.d.ts +4 -0
  262. package/textarea.d.ts.map +1 -0
  263. package/textarea.js +6 -0
  264. package/textarea.js.map +1 -0
  265. package/theme-script.d.ts +3 -0
  266. package/theme-script.d.ts.map +1 -0
  267. package/theme-script.js +6 -0
  268. package/theme-script.js.map +1 -0
  269. package/theme.d.ts +28 -0
  270. package/theme.d.ts.map +1 -0
  271. package/theme.js +92 -0
  272. package/theme.js.map +1 -0
  273. package/toast.d.ts +52 -0
  274. package/toast.d.ts.map +1 -0
  275. package/toast.js +178 -0
  276. package/toast.js.map +1 -0
  277. package/tooltip.d.ts +12 -0
  278. package/tooltip.d.ts.map +1 -0
  279. package/tooltip.js +14 -0
  280. package/tooltip.js.map +1 -0
  281. package/top-nav.d.ts +18 -0
  282. package/top-nav.d.ts.map +1 -0
  283. package/top-nav.js +160 -0
  284. package/top-nav.js.map +1 -0
  285. package/upload-limits.d.ts +4 -0
  286. package/upload-limits.d.ts.map +1 -0
  287. package/upload-limits.js +21 -0
  288. package/upload-limits.js.map +1 -0
  289. package/use-element-size.d.ts +7 -0
  290. package/use-element-size.d.ts.map +1 -0
  291. package/use-element-size.js +21 -0
  292. package/use-element-size.js.map +1 -0
  293. package/utils.d.ts +4 -0
  294. package/utils.d.ts.map +1 -0
  295. package/utils.js +7 -0
  296. package/utils.js.map +1 -0
  297. package/workspace-launcher.d.ts +50 -0
  298. package/workspace-launcher.d.ts.map +1 -0
  299. package/workspace-launcher.js +127 -0
  300. package/workspace-launcher.js.map +1 -0
@@ -0,0 +1,22 @@
1
+ import * as React from 'react';
2
+ export type NotificationItem = {
3
+ id: string;
4
+ title: string;
5
+ body?: string | null;
6
+ href?: string | null;
7
+ readAt?: string | null;
8
+ createdAt: string;
9
+ };
10
+ export type NotificationsBellLabels = {
11
+ ariaLabel: string;
12
+ title: string;
13
+ markAllRead: string;
14
+ empty: string;
15
+ };
16
+ export declare function NotificationsBell({ items, onOpenItem, onMarkRead, labels, }: {
17
+ items: NotificationItem[];
18
+ onOpenItem?: (item: NotificationItem) => void;
19
+ onMarkRead?: (ids: string[] | 'all') => void | Promise<void>;
20
+ labels?: NotificationsBellLabels;
21
+ }): React.JSX.Element;
22
+ //# sourceMappingURL=notifications-bell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifications-bell.d.ts","sourceRoot":"","sources":["../src/notifications-bell.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAeD,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,UAAU,EACV,UAAU,EACV,MAAuB,GACxB,EAAE;IACD,KAAK,EAAE,gBAAgB,EAAE,CAAA;IACzB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAA;IAC7C,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5D,MAAM,CAAC,EAAE,uBAAuB,CAAA;CACjC,qBAgBA"}
@@ -0,0 +1,30 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from 'react';
4
+ import { Bell } from 'lucide-react';
5
+ import { Popover } from './popover.js';
6
+ import { cn } from './utils.js';
7
+ const DEFAULT_LABELS = {
8
+ ariaLabel: 'Notifications',
9
+ title: 'Notifications',
10
+ markAllRead: 'Mark all read',
11
+ empty: 'You’re all caught up.',
12
+ };
13
+ function timeLabel(value) {
14
+ const date = new Date(value);
15
+ if (Number.isNaN(date.getTime()))
16
+ return '';
17
+ return date.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
18
+ }
19
+ export function NotificationsBell({ items, onOpenItem, onMarkRead, labels = DEFAULT_LABELS, }) {
20
+ const [open, setOpen] = React.useState(false);
21
+ const unread = items.filter((item) => !item.readAt).length;
22
+ function openItem(item) {
23
+ setOpen(false);
24
+ if (!item.readAt)
25
+ void onMarkRead?.([item.id]);
26
+ onOpenItem?.(item);
27
+ }
28
+ return (_jsxs(Popover, { open: open, onOpenChange: setOpen, align: "end", className: "w-80", trigger: _jsxs("button", { type: "button", onClick: () => setOpen((value) => !value), "aria-label": labels.ariaLabel, "aria-expanded": open, "aria-haspopup": "menu", className: "relative grid size-8 shrink-0 place-items-center rounded-md text-fg-muted transition-colors hover:bg-surface-hover hover:text-fg", children: [_jsx(Bell, { size: 17 }), unread > 0 ? _jsx("span", { className: "absolute -right-0.5 -top-0.5 grid h-4 min-w-4 place-items-center rounded-full bg-danger px-1 text-[10px] font-semibold leading-none text-danger-fg", children: unread > 99 ? '99+' : unread }) : null] }), children: [_jsxs("div", { className: "flex items-center justify-between border-b border-border-subtle px-3 py-2.5", children: [_jsx("span", { className: "text-sm font-medium text-fg", children: labels.title }), unread > 0 && onMarkRead ? _jsx("button", { type: "button", onClick: () => void onMarkRead('all'), className: "text-xs font-medium text-fg-muted hover:text-fg", children: labels.markAllRead }) : null] }), _jsx("div", { className: "max-h-96 overflow-y-auto", children: items.length === 0 ? _jsx("p", { className: "px-3 py-6 text-center text-sm text-fg-muted", children: labels.empty }) : items.map((item) => _jsx("button", { type: "button", onClick: () => openItem(item), className: cn('flex w-full flex-col gap-0.5 border-b border-border-subtle px-3 py-2.5 text-left last:border-b-0 hover:bg-surface-hover', !item.readAt && 'bg-bg-subtle'), children: _jsxs("span", { className: "flex items-start gap-2", children: [_jsx("span", { className: cn('mt-1.5 size-1.5 shrink-0 rounded-full', item.readAt ? 'bg-transparent' : 'bg-info') }), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: cn('block truncate text-sm text-fg', !item.readAt && 'font-medium'), children: item.title }), item.body ? _jsx("span", { className: "block truncate text-xs text-fg-muted", children: item.body }) : null, _jsx("span", { className: "block text-[11px] text-fg-subtle", children: timeLabel(item.createdAt) })] })] }) }, item.id)) })] }));
29
+ }
30
+ //# sourceMappingURL=notifications-bell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifications-bell.js","sourceRoot":"","sources":["../src/notifications-bell.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { Bell } from 'lucide-react'\nimport { Popover } from './popover'\nimport { cn } from './utils'\n\nexport type NotificationItem = {\n id: string\n title: string\n body?: string | null\n href?: string | null\n readAt?: string | null\n createdAt: string\n}\n\nexport type NotificationsBellLabels = {\n ariaLabel: string\n title: string\n markAllRead: string\n empty: string\n}\n\nconst DEFAULT_LABELS: NotificationsBellLabels = {\n ariaLabel: 'Notifications',\n title: 'Notifications',\n markAllRead: 'Mark all read',\n empty: 'You’re all caught up.',\n}\n\nfunction timeLabel(value: string): string {\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return ''\n return date.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })\n}\n\nexport function NotificationsBell({\n items,\n onOpenItem,\n onMarkRead,\n labels = DEFAULT_LABELS,\n}: {\n items: NotificationItem[]\n onOpenItem?: (item: NotificationItem) => void\n onMarkRead?: (ids: string[] | 'all') => void | Promise<void>\n labels?: NotificationsBellLabels\n}) {\n const [open, setOpen] = React.useState(false)\n const unread = items.filter((item) => !item.readAt).length\n\n function openItem(item: NotificationItem) {\n setOpen(false)\n if (!item.readAt) void onMarkRead?.([item.id])\n onOpenItem?.(item)\n }\n\n return (\n <Popover open={open} onOpenChange={setOpen} align=\"end\" className=\"w-80\" trigger={<button type=\"button\" onClick={() => setOpen((value) => !value)} aria-label={labels.ariaLabel} aria-expanded={open} aria-haspopup=\"menu\" className=\"relative grid size-8 shrink-0 place-items-center rounded-md text-fg-muted transition-colors hover:bg-surface-hover hover:text-fg\"><Bell size={17} />{unread > 0 ? <span className=\"absolute -right-0.5 -top-0.5 grid h-4 min-w-4 place-items-center rounded-full bg-danger px-1 text-[10px] font-semibold leading-none text-danger-fg\">{unread > 99 ? '99+' : unread}</span> : null}</button>}>\n <div className=\"flex items-center justify-between border-b border-border-subtle px-3 py-2.5\"><span className=\"text-sm font-medium text-fg\">{labels.title}</span>{unread > 0 && onMarkRead ? <button type=\"button\" onClick={() => void onMarkRead('all')} className=\"text-xs font-medium text-fg-muted hover:text-fg\">{labels.markAllRead}</button> : null}</div>\n <div className=\"max-h-96 overflow-y-auto\">{items.length === 0 ? <p className=\"px-3 py-6 text-center text-sm text-fg-muted\">{labels.empty}</p> : items.map((item) => <button key={item.id} type=\"button\" onClick={() => openItem(item)} className={cn('flex w-full flex-col gap-0.5 border-b border-border-subtle px-3 py-2.5 text-left last:border-b-0 hover:bg-surface-hover', !item.readAt && 'bg-bg-subtle')}><span className=\"flex items-start gap-2\"><span className={cn('mt-1.5 size-1.5 shrink-0 rounded-full', item.readAt ? 'bg-transparent' : 'bg-info')} /><span className=\"min-w-0 flex-1\"><span className={cn('block truncate text-sm text-fg', !item.readAt && 'font-medium')}>{item.title}</span>{item.body ? <span className=\"block truncate text-xs text-fg-muted\">{item.body}</span> : null}<span className=\"block text-[11px] text-fg-subtle\">{timeLabel(item.createdAt)}</span></span></span></button>)}</div>\n </Popover>\n )\n}\n"]}
package/org-chart.d.ts ADDED
@@ -0,0 +1,85 @@
1
+ import * as React from 'react';
2
+ import { type BadgeProps } from './badge.js';
3
+ /** One record in a reporting hierarchy. `parentId` is the formal manager. */
4
+ export type OrgChartNode = {
5
+ id: string;
6
+ /** The record this one reports to. Null/undefined/unknown ⇒ top level. */
7
+ parentId?: string | null;
8
+ name: string;
9
+ /** Job title or role, shown under the name. */
10
+ subtitle?: string;
11
+ /** Third line — email, extension, department. */
12
+ meta?: string;
13
+ avatarSrc?: string;
14
+ /**
15
+ * A rendered avatar to use instead of `avatarSrc` — for records whose
16
+ * likeness is composed at render time rather than stored as one image. It
17
+ * replaces the card's 44px avatar, so size it to match.
18
+ */
19
+ avatar?: React.ReactNode;
20
+ badge?: {
21
+ label: string;
22
+ variant?: BadgeProps['variant'];
23
+ };
24
+ /** Render the card muted (inactive, offboarded, archived…). */
25
+ muted?: boolean;
26
+ };
27
+ export type OrgChartTreeNode = OrgChartNode & {
28
+ depth: number;
29
+ children: OrgChartTreeNode[];
30
+ };
31
+ /**
32
+ * Resolve a flat parent-pointer list into a forest.
33
+ *
34
+ * Real hierarchies arrive dirty: dangling parents, self-references, and — when
35
+ * two records are edited concurrently — cycles. None of those may lose a
36
+ * record or hang the renderer, so anything that cannot be reached from a
37
+ * genuine root is promoted to a root itself, in input order. Every input node
38
+ * appears exactly once in the output.
39
+ */
40
+ export declare function buildOrgTree(nodes: OrgChartNode[]): OrgChartTreeNode[];
41
+ /** Every record beneath `id`, exclusive of `id` itself. */
42
+ export declare function orgChartDescendantIds(nodes: OrgChartNode[], id: string): Set<string>;
43
+ /**
44
+ * May `childId` be moved under `parentId` without breaking the tree? Guards
45
+ * self-reference, no-op moves, and the cycle a manager-under-own-report makes.
46
+ * Callers must re-check server-side — this is the UI's fast path, not the rule.
47
+ */
48
+ export declare function canReparent(nodes: OrgChartNode[], childId: string, parentId: string | null): boolean;
49
+ export type OrgChartLabels = {
50
+ collapse?: string;
51
+ expand?: string;
52
+ zoomIn?: string;
53
+ zoomOut?: string;
54
+ resetZoom?: string;
55
+ topLevel?: string;
56
+ topLevelHint?: string;
57
+ /** Suffix for the collapsed-branch count, e.g. "4 reports". */
58
+ reports?: string;
59
+ };
60
+ export type OrgChartProps = {
61
+ nodes: OrgChartNode[];
62
+ selectedId?: string | null;
63
+ onSelect?: (id: string) => void;
64
+ /**
65
+ * Enables drag-to-reparent. Called with the moved record and its new manager
66
+ * (`null` = top level). Only fired for moves `canReparent` accepts; persist
67
+ * with the same guard on the server.
68
+ */
69
+ onReparent?: (childId: string, parentId: string | null) => void;
70
+ /** Extra controls rendered beside the zoom buttons. */
71
+ toolbar?: React.ReactNode;
72
+ /** Shown instead of the tree when `nodes` is empty. */
73
+ empty?: React.ReactNode;
74
+ labels?: OrgChartLabels;
75
+ className?: string;
76
+ };
77
+ /**
78
+ * A formal reporting hierarchy: cards connected top-down, pan by scrolling,
79
+ * branches collapsible, and — when `onReparent` is supplied — records dragged
80
+ * onto a new manager. Connectors are drawn with borders rather than measured
81
+ * geometry, so the chart renders identically on the server and never needs a
82
+ * layout pass.
83
+ */
84
+ export declare function OrgChart({ nodes, selectedId, onSelect, onReparent, toolbar, empty, labels, className, }: OrgChartProps): React.JSX.Element;
85
+ //# sourceMappingURL=org-chart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"org-chart.d.ts","sourceRoot":"","sources":["../src/org-chart.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,SAAS,CAAA;AAKhD,6EAA6E;AAC7E,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iDAAiD;IACjD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACxB,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAA;KAAE,CAAA;IAC1D,+DAA+D;IAC/D,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG;IAC5C,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,gBAAgB,EAAE,CAAA;CAC7B,CAAA;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,gBAAgB,EAAE,CAiCtE;AAED,2DAA2D;AAC3D,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAiBpF;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,YAAY,EAAE,EACrB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GAAG,IAAI,GACtB,OAAO,CAST;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAgBD,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,YAAY,EAAE,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAC/D,uDAAuD;IACvD,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,uDAAuD;IACvD,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB,MAAM,CAAC,EAAE,cAAc,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,UAAU,EACV,QAAQ,EACR,UAAU,EACV,OAAO,EACP,KAAK,EACL,MAAM,EACN,SAAS,GACV,EAAE,aAAa,qBA6Mf"}
package/org-chart.js ADDED
@@ -0,0 +1,193 @@
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from 'react';
4
+ import { ChevronDown, ChevronRight, Minus, Plus, RotateCcw } from 'lucide-react';
5
+ import { Avatar } from './avatar.js';
6
+ import { Badge } from './badge.js';
7
+ import { Button } from './button.js';
8
+ import { EmptyState } from './empty-state.js';
9
+ import { cn } from './utils.js';
10
+ /**
11
+ * Resolve a flat parent-pointer list into a forest.
12
+ *
13
+ * Real hierarchies arrive dirty: dangling parents, self-references, and — when
14
+ * two records are edited concurrently — cycles. None of those may lose a
15
+ * record or hang the renderer, so anything that cannot be reached from a
16
+ * genuine root is promoted to a root itself, in input order. Every input node
17
+ * appears exactly once in the output.
18
+ */
19
+ export function buildOrgTree(nodes) {
20
+ const byId = new Map();
21
+ for (const node of nodes)
22
+ byId.set(node.id, node);
23
+ const childrenOf = new Map();
24
+ const roots = [];
25
+ for (const node of nodes) {
26
+ const parentId = node.parentId;
27
+ const hasParent = typeof parentId === 'string' && parentId !== node.id && byId.has(parentId);
28
+ if (!hasParent) {
29
+ roots.push(node);
30
+ continue;
31
+ }
32
+ const siblings = childrenOf.get(parentId);
33
+ if (siblings)
34
+ siblings.push(node);
35
+ else
36
+ childrenOf.set(parentId, [node]);
37
+ }
38
+ const placed = new Set();
39
+ const expand = (node, depth) => {
40
+ placed.add(node.id);
41
+ const children = (childrenOf.get(node.id) ?? [])
42
+ .filter((child) => !placed.has(child.id))
43
+ .map((child) => expand(child, depth + 1));
44
+ return { ...node, depth, children };
45
+ };
46
+ const forest = roots.map((root) => expand(root, 0));
47
+ // Whatever is left is inside a cycle: promote in input order until drained.
48
+ for (const node of nodes) {
49
+ if (!placed.has(node.id))
50
+ forest.push(expand(node, 0));
51
+ }
52
+ return forest;
53
+ }
54
+ /** Every record beneath `id`, exclusive of `id` itself. */
55
+ export function orgChartDescendantIds(nodes, id) {
56
+ const childrenOf = new Map();
57
+ for (const node of nodes) {
58
+ if (typeof node.parentId !== 'string' || node.parentId === node.id)
59
+ continue;
60
+ const siblings = childrenOf.get(node.parentId);
61
+ if (siblings)
62
+ siblings.push(node.id);
63
+ else
64
+ childrenOf.set(node.parentId, [node.id]);
65
+ }
66
+ const found = new Set();
67
+ const queue = [...(childrenOf.get(id) ?? [])];
68
+ while (queue.length > 0) {
69
+ const next = queue.pop();
70
+ if (found.has(next) || next === id)
71
+ continue;
72
+ found.add(next);
73
+ queue.push(...(childrenOf.get(next) ?? []));
74
+ }
75
+ return found;
76
+ }
77
+ /**
78
+ * May `childId` be moved under `parentId` without breaking the tree? Guards
79
+ * self-reference, no-op moves, and the cycle a manager-under-own-report makes.
80
+ * Callers must re-check server-side — this is the UI's fast path, not the rule.
81
+ */
82
+ export function canReparent(nodes, childId, parentId) {
83
+ const child = nodes.find((node) => node.id === childId);
84
+ if (!child)
85
+ return false;
86
+ const currentParent = typeof child.parentId === 'string' ? child.parentId : null;
87
+ if (currentParent === parentId)
88
+ return false;
89
+ if (parentId === null)
90
+ return true;
91
+ if (parentId === childId)
92
+ return false;
93
+ if (!nodes.some((node) => node.id === parentId))
94
+ return false;
95
+ return !orgChartDescendantIds(nodes, childId).has(parentId);
96
+ }
97
+ const DEFAULT_LABELS = {
98
+ collapse: 'Collapse branch',
99
+ expand: 'Expand branch',
100
+ zoomIn: 'Zoom in',
101
+ zoomOut: 'Zoom out',
102
+ resetZoom: 'Reset zoom',
103
+ topLevel: 'Top level',
104
+ topLevelHint: 'Drop here to remove the reporting line',
105
+ reports: 'reports',
106
+ };
107
+ const ZOOM_STEPS = [0.5, 0.65, 0.8, 1, 1.15];
108
+ const DEFAULT_ZOOM_INDEX = 3;
109
+ /**
110
+ * A formal reporting hierarchy: cards connected top-down, pan by scrolling,
111
+ * branches collapsible, and — when `onReparent` is supplied — records dragged
112
+ * onto a new manager. Connectors are drawn with borders rather than measured
113
+ * geometry, so the chart renders identically on the server and never needs a
114
+ * layout pass.
115
+ */
116
+ export function OrgChart({ nodes, selectedId, onSelect, onReparent, toolbar, empty, labels, className, }) {
117
+ const text = { ...DEFAULT_LABELS, ...labels };
118
+ const forest = React.useMemo(() => buildOrgTree(nodes), [nodes]);
119
+ const [collapsed, setCollapsed] = React.useState(() => new Set());
120
+ const [zoomIndex, setZoomIndex] = React.useState(DEFAULT_ZOOM_INDEX);
121
+ const [dragging, setDragging] = React.useState(null);
122
+ const [dropTarget, setDropTarget] = React.useState(null);
123
+ const [topDrop, setTopDrop] = React.useState(false);
124
+ // A record removed from the data must not keep a stale branch folded shut.
125
+ React.useEffect(() => {
126
+ setCollapsed((current) => {
127
+ const live = new Set(nodes.map((node) => node.id));
128
+ const next = new Set([...current].filter((id) => live.has(id)));
129
+ return next.size === current.size ? current : next;
130
+ });
131
+ }, [nodes]);
132
+ const toggle = (id) => setCollapsed((current) => {
133
+ const next = new Set(current);
134
+ if (next.has(id))
135
+ next.delete(id);
136
+ else
137
+ next.add(id);
138
+ return next;
139
+ });
140
+ const draggable = typeof onReparent === 'function';
141
+ const endDrag = () => {
142
+ setDragging(null);
143
+ setDropTarget(null);
144
+ setTopDrop(false);
145
+ };
146
+ const drop = (parentId) => {
147
+ if (dragging && canReparent(nodes, dragging, parentId))
148
+ onReparent?.(dragging, parentId);
149
+ endDrag();
150
+ };
151
+ if (nodes.length === 0) {
152
+ return (_jsx(_Fragment, { children: empty ?? _jsx(EmptyState, { title: "Nothing to chart yet", description: "Records appear here once they exist." }) }));
153
+ }
154
+ const zoom = ZOOM_STEPS[zoomIndex] ?? 1;
155
+ const renderNode = (node, isRoot) => {
156
+ const isCollapsed = collapsed.has(node.id);
157
+ const hasChildren = node.children.length > 0;
158
+ const isDragging = dragging === node.id;
159
+ const isDropTarget = dropTarget === node.id;
160
+ const invalidTarget = dragging !== null && !isDragging && !canReparent(nodes, dragging, node.id);
161
+ return (_jsxs("li", { className: cn('appkit-org-item', isRoot && 'appkit-org-item--root'), children: [isRoot ? null : _jsx("span", { "aria-hidden": true, className: "appkit-org-stem" }), _jsxs("div", { className: cn('appkit-org-card reveal group relative flex w-56 flex-col items-center gap-2 rounded-xl border bg-surface px-4 py-3 text-center shadow-sm transition-colors', selectedId === node.id ? 'border-primary ring-1 ring-primary' : 'border-border', isDropTarget && 'border-primary bg-primary-subtle', isDragging && 'opacity-50', node.muted && 'opacity-60'), draggable: draggable, onDragStart: draggable
162
+ ? (event) => {
163
+ event.dataTransfer.effectAllowed = 'move';
164
+ event.dataTransfer.setData('text/plain', node.id);
165
+ setDragging(node.id);
166
+ }
167
+ : undefined, onDragEnd: draggable ? endDrag : undefined, onDragOver: draggable
168
+ ? (event) => {
169
+ if (!dragging || invalidTarget || isDragging)
170
+ return;
171
+ event.preventDefault();
172
+ event.dataTransfer.dropEffect = 'move';
173
+ setDropTarget(node.id);
174
+ }
175
+ : undefined, onDragLeave: draggable ? () => setDropTarget((current) => (current === node.id ? null : current)) : undefined, onDrop: draggable
176
+ ? (event) => {
177
+ event.preventDefault();
178
+ drop(node.id);
179
+ }
180
+ : undefined, children: [_jsxs("button", { type: "button", onClick: () => onSelect?.(node.id), className: "flex flex-col items-center gap-2 focus-visible:outline-none", "aria-current": selectedId === node.id ? 'true' : undefined, children: [node.avatar ?? (_jsx(Avatar, { name: node.name, size: 44, ...(node.avatarSrc ? { src: node.avatarSrc } : {}) })), _jsxs("span", { className: "flex flex-col items-center gap-1", children: [_jsx("span", { className: "text-sm font-semibold text-fg group-hover:text-primary", children: node.name }), node.subtitle ? _jsx("span", { className: "text-xs text-fg-muted", children: node.subtitle }) : null, node.badge ? (_jsx(Badge, { variant: node.badge.variant ?? 'secondary', children: node.badge.label })) : null, node.meta ? _jsx("span", { className: "max-w-full truncate text-[11px] text-fg-subtle", children: node.meta }) : null] })] }), hasChildren ? (_jsxs("button", { type: "button", onClick: () => toggle(node.id), "aria-expanded": !isCollapsed, "aria-label": isCollapsed ? text.expand : text.collapse, title: `${node.children.length} ${text.reports}`, className: "absolute -bottom-3 left-1/2 z-1 flex -translate-x-1/2 items-center gap-1 rounded-full border border-border bg-surface px-2 py-0.5 text-[11px] text-fg-muted shadow-sm transition-colors hover:border-primary hover:text-primary", children: [isCollapsed ? _jsx(ChevronRight, { className: "size-3" }) : _jsx(ChevronDown, { className: "size-3" }), node.children.length] })) : null] }), hasChildren && !isCollapsed ? (_jsx("ul", { className: "appkit-org-children", children: node.children.map((child) => renderNode(child, false)) })) : null] }, node.id));
181
+ };
182
+ return (_jsxs("div", { className: cn('space-y-3', className), children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Button, { type: "button", variant: "outline", size: "sm", className: "w-8 px-0", "aria-label": text.zoomOut, disabled: zoomIndex === 0, onClick: () => setZoomIndex((index) => Math.max(0, index - 1)), children: _jsx(Minus, { className: "size-4" }) }), _jsx(Button, { type: "button", variant: "outline", size: "sm", className: "w-8 px-0", "aria-label": text.zoomIn, disabled: zoomIndex === ZOOM_STEPS.length - 1, onClick: () => setZoomIndex((index) => Math.min(ZOOM_STEPS.length - 1, index + 1)), children: _jsx(Plus, { className: "size-4" }) }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "w-8 px-0", "aria-label": text.resetZoom, onClick: () => setZoomIndex(DEFAULT_ZOOM_INDEX), children: _jsx(RotateCcw, { className: "size-4" }) }), _jsxs("span", { className: "ml-1 text-xs tabular-nums text-fg-subtle", children: [Math.round(zoom * 100), "%"] })] }), toolbar] }), _jsx("div", { className: "overflow-x-auto rounded-xl border border-border bg-bg-subtle p-6", children: _jsxs("div", { className: "appkit-org-canvas", style: { zoom }, children: [draggable ? (_jsx("div", { onDragOver: (event) => {
183
+ if (!dragging || !canReparent(nodes, dragging, null))
184
+ return;
185
+ event.preventDefault();
186
+ event.dataTransfer.dropEffect = 'move';
187
+ setTopDrop(true);
188
+ }, onDragLeave: () => setTopDrop(false), onDrop: (event) => {
189
+ event.preventDefault();
190
+ drop(null);
191
+ }, className: cn('mx-auto mb-4 w-fit rounded-full border border-dashed px-4 py-1.5 text-xs transition-colors', topDrop ? 'border-primary bg-primary-subtle text-primary' : 'border-border text-fg-subtle'), children: dragging ? text.topLevelHint : text.topLevel })) : null, _jsx("ul", { className: "appkit-org-children appkit-org-roots", children: forest.map((root) => renderNode(root, true)) })] }) })] }));
192
+ }
193
+ //# sourceMappingURL=org-chart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"org-chart.js","sourceRoot":"","sources":["../src/org-chart.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { ChevronDown, ChevronRight, Minus, Plus, RotateCcw } from 'lucide-react'\nimport { Avatar } from './avatar'\nimport { Badge, type BadgeProps } from './badge'\nimport { Button } from './button'\nimport { EmptyState } from './empty-state'\nimport { cn } from './utils'\n\n/** One record in a reporting hierarchy. `parentId` is the formal manager. */\nexport type OrgChartNode = {\n id: string\n /** The record this one reports to. Null/undefined/unknown ⇒ top level. */\n parentId?: string | null\n name: string\n /** Job title or role, shown under the name. */\n subtitle?: string\n /** Third line — email, extension, department. */\n meta?: string\n avatarSrc?: string\n /**\n * A rendered avatar to use instead of `avatarSrc` — for records whose\n * likeness is composed at render time rather than stored as one image. It\n * replaces the card's 44px avatar, so size it to match.\n */\n avatar?: React.ReactNode\n badge?: { label: string; variant?: BadgeProps['variant'] }\n /** Render the card muted (inactive, offboarded, archived…). */\n muted?: boolean\n}\n\nexport type OrgChartTreeNode = OrgChartNode & {\n depth: number\n children: OrgChartTreeNode[]\n}\n\n/**\n * Resolve a flat parent-pointer list into a forest.\n *\n * Real hierarchies arrive dirty: dangling parents, self-references, and — when\n * two records are edited concurrently — cycles. None of those may lose a\n * record or hang the renderer, so anything that cannot be reached from a\n * genuine root is promoted to a root itself, in input order. Every input node\n * appears exactly once in the output.\n */\nexport function buildOrgTree(nodes: OrgChartNode[]): OrgChartTreeNode[] {\n const byId = new Map<string, OrgChartNode>()\n for (const node of nodes) byId.set(node.id, node)\n\n const childrenOf = new Map<string, OrgChartNode[]>()\n const roots: OrgChartNode[] = []\n for (const node of nodes) {\n const parentId = node.parentId\n const hasParent = typeof parentId === 'string' && parentId !== node.id && byId.has(parentId)\n if (!hasParent) {\n roots.push(node)\n continue\n }\n const siblings = childrenOf.get(parentId as string)\n if (siblings) siblings.push(node)\n else childrenOf.set(parentId as string, [node])\n }\n\n const placed = new Set<string>()\n const expand = (node: OrgChartNode, depth: number): OrgChartTreeNode => {\n placed.add(node.id)\n const children = (childrenOf.get(node.id) ?? [])\n .filter((child) => !placed.has(child.id))\n .map((child) => expand(child, depth + 1))\n return { ...node, depth, children }\n }\n\n const forest = roots.map((root) => expand(root, 0))\n // Whatever is left is inside a cycle: promote in input order until drained.\n for (const node of nodes) {\n if (!placed.has(node.id)) forest.push(expand(node, 0))\n }\n return forest\n}\n\n/** Every record beneath `id`, exclusive of `id` itself. */\nexport function orgChartDescendantIds(nodes: OrgChartNode[], id: string): Set<string> {\n const childrenOf = new Map<string, string[]>()\n for (const node of nodes) {\n if (typeof node.parentId !== 'string' || node.parentId === node.id) continue\n const siblings = childrenOf.get(node.parentId)\n if (siblings) siblings.push(node.id)\n else childrenOf.set(node.parentId, [node.id])\n }\n const found = new Set<string>()\n const queue = [...(childrenOf.get(id) ?? [])]\n while (queue.length > 0) {\n const next = queue.pop() as string\n if (found.has(next) || next === id) continue\n found.add(next)\n queue.push(...(childrenOf.get(next) ?? []))\n }\n return found\n}\n\n/**\n * May `childId` be moved under `parentId` without breaking the tree? Guards\n * self-reference, no-op moves, and the cycle a manager-under-own-report makes.\n * Callers must re-check server-side — this is the UI's fast path, not the rule.\n */\nexport function canReparent(\n nodes: OrgChartNode[],\n childId: string,\n parentId: string | null,\n): boolean {\n const child = nodes.find((node) => node.id === childId)\n if (!child) return false\n const currentParent = typeof child.parentId === 'string' ? child.parentId : null\n if (currentParent === parentId) return false\n if (parentId === null) return true\n if (parentId === childId) return false\n if (!nodes.some((node) => node.id === parentId)) return false\n return !orgChartDescendantIds(nodes, childId).has(parentId)\n}\n\nexport type OrgChartLabels = {\n collapse?: string\n expand?: string\n zoomIn?: string\n zoomOut?: string\n resetZoom?: string\n topLevel?: string\n topLevelHint?: string\n /** Suffix for the collapsed-branch count, e.g. \"4 reports\". */\n reports?: string\n}\n\nconst DEFAULT_LABELS: Required<OrgChartLabels> = {\n collapse: 'Collapse branch',\n expand: 'Expand branch',\n zoomIn: 'Zoom in',\n zoomOut: 'Zoom out',\n resetZoom: 'Reset zoom',\n topLevel: 'Top level',\n topLevelHint: 'Drop here to remove the reporting line',\n reports: 'reports',\n}\n\nconst ZOOM_STEPS = [0.5, 0.65, 0.8, 1, 1.15] as const\nconst DEFAULT_ZOOM_INDEX = 3\n\nexport type OrgChartProps = {\n nodes: OrgChartNode[]\n selectedId?: string | null\n onSelect?: (id: string) => void\n /**\n * Enables drag-to-reparent. Called with the moved record and its new manager\n * (`null` = top level). Only fired for moves `canReparent` accepts; persist\n * with the same guard on the server.\n */\n onReparent?: (childId: string, parentId: string | null) => void\n /** Extra controls rendered beside the zoom buttons. */\n toolbar?: React.ReactNode\n /** Shown instead of the tree when `nodes` is empty. */\n empty?: React.ReactNode\n labels?: OrgChartLabels\n className?: string\n}\n\n/**\n * A formal reporting hierarchy: cards connected top-down, pan by scrolling,\n * branches collapsible, and — when `onReparent` is supplied — records dragged\n * onto a new manager. Connectors are drawn with borders rather than measured\n * geometry, so the chart renders identically on the server and never needs a\n * layout pass.\n */\nexport function OrgChart({\n nodes,\n selectedId,\n onSelect,\n onReparent,\n toolbar,\n empty,\n labels,\n className,\n}: OrgChartProps) {\n const text = { ...DEFAULT_LABELS, ...labels }\n const forest = React.useMemo(() => buildOrgTree(nodes), [nodes])\n const [collapsed, setCollapsed] = React.useState<ReadonlySet<string>>(() => new Set<string>())\n const [zoomIndex, setZoomIndex] = React.useState(DEFAULT_ZOOM_INDEX)\n const [dragging, setDragging] = React.useState<string | null>(null)\n const [dropTarget, setDropTarget] = React.useState<string | null>(null)\n const [topDrop, setTopDrop] = React.useState(false)\n\n // A record removed from the data must not keep a stale branch folded shut.\n React.useEffect(() => {\n setCollapsed((current) => {\n const live = new Set(nodes.map((node) => node.id))\n const next = new Set([...current].filter((id) => live.has(id)))\n return next.size === current.size ? current : next\n })\n }, [nodes])\n\n const toggle = (id: string) =>\n setCollapsed((current) => {\n const next = new Set(current)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n return next\n })\n\n const draggable = typeof onReparent === 'function'\n const endDrag = () => {\n setDragging(null)\n setDropTarget(null)\n setTopDrop(false)\n }\n const drop = (parentId: string | null) => {\n if (dragging && canReparent(nodes, dragging, parentId)) onReparent?.(dragging, parentId)\n endDrag()\n }\n\n if (nodes.length === 0) {\n return (\n <>{empty ?? <EmptyState title=\"Nothing to chart yet\" description=\"Records appear here once they exist.\" />}</>\n )\n }\n\n const zoom = ZOOM_STEPS[zoomIndex] ?? 1\n\n const renderNode = (node: OrgChartTreeNode, isRoot: boolean): React.ReactNode => {\n const isCollapsed = collapsed.has(node.id)\n const hasChildren = node.children.length > 0\n const isDragging = dragging === node.id\n const isDropTarget = dropTarget === node.id\n const invalidTarget = dragging !== null && !isDragging && !canReparent(nodes, dragging, node.id)\n\n return (\n <li key={node.id} className={cn('appkit-org-item', isRoot && 'appkit-org-item--root')}>\n {isRoot ? null : <span aria-hidden className=\"appkit-org-stem\" />}\n <div\n className={cn(\n 'appkit-org-card reveal group relative flex w-56 flex-col items-center gap-2 rounded-xl border bg-surface px-4 py-3 text-center shadow-sm transition-colors',\n selectedId === node.id ? 'border-primary ring-1 ring-primary' : 'border-border',\n isDropTarget && 'border-primary bg-primary-subtle',\n isDragging && 'opacity-50',\n node.muted && 'opacity-60',\n )}\n draggable={draggable}\n onDragStart={\n draggable\n ? (event) => {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', node.id)\n setDragging(node.id)\n }\n : undefined\n }\n onDragEnd={draggable ? endDrag : undefined}\n onDragOver={\n draggable\n ? (event) => {\n if (!dragging || invalidTarget || isDragging) return\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n setDropTarget(node.id)\n }\n : undefined\n }\n onDragLeave={\n draggable ? () => setDropTarget((current) => (current === node.id ? null : current)) : undefined\n }\n onDrop={\n draggable\n ? (event) => {\n event.preventDefault()\n drop(node.id)\n }\n : undefined\n }\n >\n <button\n type=\"button\"\n onClick={() => onSelect?.(node.id)}\n className=\"flex flex-col items-center gap-2 focus-visible:outline-none\"\n aria-current={selectedId === node.id ? 'true' : undefined}\n >\n {node.avatar ?? (\n <Avatar name={node.name} size={44} {...(node.avatarSrc ? { src: node.avatarSrc } : {})} />\n )}\n <span className=\"flex flex-col items-center gap-1\">\n <span className=\"text-sm font-semibold text-fg group-hover:text-primary\">{node.name}</span>\n {node.subtitle ? <span className=\"text-xs text-fg-muted\">{node.subtitle}</span> : null}\n {node.badge ? (\n <Badge variant={node.badge.variant ?? 'secondary'}>{node.badge.label}</Badge>\n ) : null}\n {node.meta ? <span className=\"max-w-full truncate text-[11px] text-fg-subtle\">{node.meta}</span> : null}\n </span>\n </button>\n {hasChildren ? (\n <button\n type=\"button\"\n onClick={() => toggle(node.id)}\n aria-expanded={!isCollapsed}\n aria-label={isCollapsed ? text.expand : text.collapse}\n title={`${node.children.length} ${text.reports}`}\n className=\"absolute -bottom-3 left-1/2 z-1 flex -translate-x-1/2 items-center gap-1 rounded-full border border-border bg-surface px-2 py-0.5 text-[11px] text-fg-muted shadow-sm transition-colors hover:border-primary hover:text-primary\"\n >\n {isCollapsed ? <ChevronRight className=\"size-3\" /> : <ChevronDown className=\"size-3\" />}\n {node.children.length}\n </button>\n ) : null}\n </div>\n {hasChildren && !isCollapsed ? (\n <ul className=\"appkit-org-children\">{node.children.map((child) => renderNode(child, false))}</ul>\n ) : null}\n </li>\n )\n }\n\n return (\n <div className={cn('space-y-3', className)}>\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"flex items-center gap-1\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"w-8 px-0\"\n aria-label={text.zoomOut}\n disabled={zoomIndex === 0}\n onClick={() => setZoomIndex((index) => Math.max(0, index - 1))}\n >\n <Minus className=\"size-4\" />\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"w-8 px-0\"\n aria-label={text.zoomIn}\n disabled={zoomIndex === ZOOM_STEPS.length - 1}\n onClick={() => setZoomIndex((index) => Math.min(ZOOM_STEPS.length - 1, index + 1))}\n >\n <Plus className=\"size-4\" />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"w-8 px-0\"\n aria-label={text.resetZoom}\n onClick={() => setZoomIndex(DEFAULT_ZOOM_INDEX)}\n >\n <RotateCcw className=\"size-4\" />\n </Button>\n <span className=\"ml-1 text-xs tabular-nums text-fg-subtle\">{Math.round(zoom * 100)}%</span>\n </div>\n {toolbar}\n </div>\n <div className=\"overflow-x-auto rounded-xl border border-border bg-bg-subtle p-6\">\n <div className=\"appkit-org-canvas\" style={{ zoom }}>\n {draggable ? (\n <div\n onDragOver={(event) => {\n if (!dragging || !canReparent(nodes, dragging, null)) return\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n setTopDrop(true)\n }}\n onDragLeave={() => setTopDrop(false)}\n onDrop={(event) => {\n event.preventDefault()\n drop(null)\n }}\n className={cn(\n 'mx-auto mb-4 w-fit rounded-full border border-dashed px-4 py-1.5 text-xs transition-colors',\n topDrop ? 'border-primary bg-primary-subtle text-primary' : 'border-border text-fg-subtle',\n )}\n >\n {dragging ? text.topLevelHint : text.topLevel}\n </div>\n ) : null}\n <ul className=\"appkit-org-children appkit-org-roots\">\n {forest.map((root) => renderNode(root, true))}\n </ul>\n </div>\n </div>\n </div>\n )\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@braedonsaunders/appkit-ui",
3
+ "version": "0.2.0",
4
+ "description": "Tokenized, animated UI primitives — the feel of the ecosystem.",
5
+ "license": "AGPL-3.0-or-later",
6
+ "type": "module",
7
+ "sideEffects": [
8
+ "*.css"
9
+ ],
10
+ "exports": {
11
+ ".": {
12
+ "types": "./index.d.ts",
13
+ "import": "./index.js",
14
+ "default": "./index.js"
15
+ },
16
+ "./page-transition": {
17
+ "types": "./page-transition.d.ts",
18
+ "import": "./page-transition.js",
19
+ "default": "./page-transition.js"
20
+ },
21
+ "./use-element-size": {
22
+ "types": "./use-element-size.d.ts",
23
+ "import": "./use-element-size.js",
24
+ "default": "./use-element-size.js"
25
+ },
26
+ "./styles.css": "./styles.css",
27
+ "./package.json": "./package.json"
28
+ },
29
+ "main": "./index.js",
30
+ "types": "./index.d.ts",
31
+ "dependencies": {
32
+ "@braedonsaunders/appkit-tokens": "^0.1.2",
33
+ "class-variance-authority": "^0.7.1",
34
+ "clsx": "^2.1.1",
35
+ "framer-motion": "^12.42.2",
36
+ "lucide-react": "^1.24.0",
37
+ "tailwind-merge": "^3.6.0"
38
+ },
39
+ "peerDependencies": {
40
+ "react": "^19.0.0",
41
+ "react-dom": "^19.0.0"
42
+ },
43
+ "author": "Braedon Saunders",
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/braedonsaunders/appkit.git",
47
+ "directory": "packages/ui"
48
+ },
49
+ "homepage": "https://github.com/braedonsaunders/appkit/tree/main/packages/ui#readme",
50
+ "bugs": {
51
+ "url": "https://github.com/braedonsaunders/appkit/issues"
52
+ },
53
+ "engines": {
54
+ "node": ">=22"
55
+ },
56
+ "keywords": [
57
+ "appkit",
58
+ "application-framework",
59
+ "typescript"
60
+ ]
61
+ }
@@ -0,0 +1,32 @@
1
+ import * as React from 'react';
2
+ export type PageHeaderProps = {
3
+ title: string;
4
+ description?: string;
5
+ actions?: React.ReactNode;
6
+ /** Optional back link. `render` lets an app supply its own <Link>. */
7
+ back?: {
8
+ href: string;
9
+ label: string;
10
+ render?: (props: {
11
+ href: string;
12
+ children: React.ReactNode;
13
+ className: string;
14
+ }) => React.ReactNode;
15
+ };
16
+ className?: string;
17
+ };
18
+ export declare function PageHeader({ title, description, actions, back, className }: PageHeaderProps): React.JSX.Element;
19
+ export type DetailHeaderProps = {
20
+ back?: {
21
+ href: string;
22
+ label: string;
23
+ };
24
+ title: string;
25
+ subtitle?: string;
26
+ badge?: React.ReactNode;
27
+ actions?: React.ReactNode;
28
+ className?: string;
29
+ };
30
+ /** Source-compatible record-detail heading. */
31
+ export declare function DetailHeader({ back, title, subtitle, badge, actions, className, }: DetailHeaderProps): React.JSX.Element;
32
+ //# sourceMappingURL=page-header.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-header.d.ts","sourceRoot":"","sources":["../src/page-header.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,sEAAsE;IACtE,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAE,KAAK,KAAK,CAAC,SAAS,CAAA;KAAE,CAAA;IAC3I,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,eAAe,qBAuB3F;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IACtC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,+CAA+C;AAC/C,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,KAAK,EACL,OAAO,EACP,SAAS,GACV,EAAE,iBAAiB,qBAqBnB"}
package/page-header.js ADDED
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from 'react';
3
+ import { DocumentTitle } from './document-title.js';
4
+ import { UiBackLink } from './link-context.js';
5
+ import { cn } from './utils.js';
6
+ export function PageHeader({ title, description, actions, back, className }) {
7
+ const backClass = 'inline-flex items-center gap-1 text-xs text-fg-muted transition-colors hover:text-primary';
8
+ return (_jsxs("div", { className: cn('space-y-2', className), children: [_jsx(DocumentTitle, { title: title }), back ? (back.render ? (back.render({ href: back.href, className: backClass, children: _jsxs(_Fragment, { children: ["\u2190 ", back.label] }) })) : _jsx(UiBackLink, { href: back.href, label: back.label, className: backClass })) : null, _jsxs("header", { className: "flex items-center justify-between gap-3 sm:items-end sm:gap-4", children: [_jsxs("div", { className: "min-w-0 space-y-1", children: [_jsx("h1", { className: "truncate text-xl font-semibold text-fg sm:text-2xl", children: title }), description ? (_jsx("p", { className: "hidden text-sm text-fg-muted sm:block", children: description })) : null] }), actions ? (_jsx("div", { className: "flex shrink-0 flex-wrap items-center justify-end gap-2", children: actions })) : null] })] }));
9
+ }
10
+ /** Source-compatible record-detail heading. */
11
+ export function DetailHeader({ back, title, subtitle, badge, actions, className, }) {
12
+ return (_jsxs("header", { className: cn('space-y-2', className), children: [_jsx(DocumentTitle, { title: title }), back ? (_jsx(UiBackLink, { href: back.href, label: back.label, className: "text-sm text-primary hover:underline" })) : null, _jsxs("div", { className: "flex flex-col gap-2.5 sm:flex-row sm:items-center sm:justify-between sm:gap-4", children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1.5", children: [_jsx("h1", { className: "text-xl font-semibold text-fg sm:truncate sm:text-2xl", children: title }), badge] }), actions ? _jsx("div", { className: "flex flex-wrap items-center gap-2", children: actions }) : null] }), subtitle ? _jsx("p", { className: "text-sm text-fg-muted", children: subtitle }) : null] }));
13
+ }
14
+ //# sourceMappingURL=page-header.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-header.js","sourceRoot":"","sources":["../src/page-header.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAW5B,MAAM,UAAU,UAAU,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAmB;IAC1F,MAAM,SAAS,GAAG,2FAA2F,CAAA;IAC7G,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,WAAW,EAAE,SAAS,CAAC,aACxC,KAAC,aAAa,IAAC,KAAK,EAAE,KAAK,GAAI,EAC9B,IAAI,CAAC,CAAC,CAAC,CACN,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CACZ,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,yCAAK,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CACtF,CAAC,CAAC,CAAC,KAAC,UAAU,IAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,GAAI,CAC7E,CAAC,CAAC,CAAC,IAAI,EACR,kBAAQ,SAAS,EAAC,+DAA+D,aAC/E,eAAK,SAAS,EAAC,mBAAmB,aAChC,aAAI,SAAS,EAAC,oDAAoD,YAAE,KAAK,GAAM,EAC9E,WAAW,CAAC,CAAC,CAAC,CACb,YAAG,SAAS,EAAC,uCAAuC,YAAE,WAAW,GAAK,CACvE,CAAC,CAAC,CAAC,IAAI,IACJ,EACL,OAAO,CAAC,CAAC,CAAC,CACT,cAAK,SAAS,EAAC,wDAAwD,YAAE,OAAO,GAAO,CACxF,CAAC,CAAC,CAAC,IAAI,IACD,IACL,CACP,CAAA;AACH,CAAC;AAWD,+CAA+C;AAC/C,MAAM,UAAU,YAAY,CAAC,EAC3B,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,KAAK,EACL,OAAO,EACP,SAAS,GACS;IAClB,OAAO,CACL,kBAAQ,SAAS,EAAE,EAAE,CAAC,WAAW,EAAE,SAAS,CAAC,aAC3C,KAAC,aAAa,IAAC,KAAK,EAAE,KAAK,GAAI,EAC9B,IAAI,CAAC,CAAC,CAAC,CACN,KAAC,UAAU,IACT,IAAI,EAAE,IAAI,CAAC,IAAI,EACf,KAAK,EAAE,IAAI,CAAC,KAAK,EACjB,SAAS,EAAC,sCAAsC,GAChD,CACH,CAAC,CAAC,CAAC,IAAI,EACR,eAAK,SAAS,EAAC,+EAA+E,aAC5F,eAAK,SAAS,EAAC,uDAAuD,aACpE,aAAI,SAAS,EAAC,uDAAuD,YAAE,KAAK,GAAM,EACjF,KAAK,IACF,EACL,OAAO,CAAC,CAAC,CAAC,cAAK,SAAS,EAAC,mCAAmC,YAAE,OAAO,GAAO,CAAC,CAAC,CAAC,IAAI,IAChF,EACL,QAAQ,CAAC,CAAC,CAAC,YAAG,SAAS,EAAC,uBAAuB,YAAE,QAAQ,GAAK,CAAC,CAAC,CAAC,IAAI,IAC/D,CACV,CAAA;AACH,CAAC","sourcesContent":["import * as React from 'react'\nimport { DocumentTitle } from './document-title'\nimport { UiBackLink } from './link-context'\nimport { cn } from './utils'\n\nexport type PageHeaderProps = {\n title: string\n description?: string\n actions?: React.ReactNode\n /** Optional back link. `render` lets an app supply its own <Link>. */\n back?: { href: string; label: string; render?: (props: { href: string; children: React.ReactNode; className: string }) => React.ReactNode }\n className?: string\n}\n\nexport function PageHeader({ title, description, actions, back, className }: PageHeaderProps) {\n const backClass = 'inline-flex items-center gap-1 text-xs text-fg-muted transition-colors hover:text-primary'\n return (\n <div className={cn('space-y-2', className)}>\n <DocumentTitle title={title} />\n {back ? (\n back.render ? (\n back.render({ href: back.href, className: backClass, children: <>← {back.label}</> })\n ) : <UiBackLink href={back.href} label={back.label} className={backClass} />\n ) : null}\n <header className=\"flex items-center justify-between gap-3 sm:items-end sm:gap-4\">\n <div className=\"min-w-0 space-y-1\">\n <h1 className=\"truncate text-xl font-semibold text-fg sm:text-2xl\">{title}</h1>\n {description ? (\n <p className=\"hidden text-sm text-fg-muted sm:block\">{description}</p>\n ) : null}\n </div>\n {actions ? (\n <div className=\"flex shrink-0 flex-wrap items-center justify-end gap-2\">{actions}</div>\n ) : null}\n </header>\n </div>\n )\n}\n\nexport type DetailHeaderProps = {\n back?: { href: string; label: string }\n title: string\n subtitle?: string\n badge?: React.ReactNode\n actions?: React.ReactNode\n className?: string\n}\n\n/** Source-compatible record-detail heading. */\nexport function DetailHeader({\n back,\n title,\n subtitle,\n badge,\n actions,\n className,\n}: DetailHeaderProps) {\n return (\n <header className={cn('space-y-2', className)}>\n <DocumentTitle title={title} />\n {back ? (\n <UiBackLink\n href={back.href}\n label={back.label}\n className=\"text-sm text-primary hover:underline\"\n />\n ) : null}\n <div className=\"flex flex-col gap-2.5 sm:flex-row sm:items-center sm:justify-between sm:gap-4\">\n <div className=\"flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1.5\">\n <h1 className=\"text-xl font-semibold text-fg sm:truncate sm:text-2xl\">{title}</h1>\n {badge}\n </div>\n {actions ? <div className=\"flex flex-wrap items-center gap-2\">{actions}</div> : null}\n </div>\n {subtitle ? <p className=\"text-sm text-fg-muted\">{subtitle}</p> : null}\n </header>\n )\n}\n"]}
@@ -0,0 +1,29 @@
1
+ import * as React from 'react';
2
+ /** Content-driven pages (dashboards, forms): the whole body scrolls. */
3
+ export declare function PageContainer({ className, children }: {
4
+ className?: string;
5
+ children: React.ReactNode;
6
+ }): React.JSX.Element;
7
+ /** List pages: a sticky header (title/actions/search/filters); only the body scrolls. */
8
+ export declare function ListPageLayout({ header, children, className, }: {
9
+ header: React.ReactNode;
10
+ children: React.ReactNode;
11
+ className?: string;
12
+ }): React.JSX.Element;
13
+ /** Detail pages: fixed header + optional alerts + subtab strip; content scrolls. */
14
+ export declare function DetailPageLayout({ header, alerts, subtabs, children, className, }: {
15
+ header: React.ReactNode;
16
+ alerts?: React.ReactNode;
17
+ subtabs?: React.ReactNode;
18
+ children: React.ReactNode;
19
+ className?: string;
20
+ }): React.JSX.Element;
21
+ /** Wizard/form pages: sticky header, scrolling body, optional sticky footer. */
22
+ export declare function WizardLayout({ header, footer, children, className, wide, }: {
23
+ header: React.ReactNode;
24
+ footer?: React.ReactNode;
25
+ children: React.ReactNode;
26
+ className?: string;
27
+ wide?: boolean;
28
+ }): React.JSX.Element;
29
+ //# sourceMappingURL=page-layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-layout.d.ts","sourceRoot":"","sources":["../src/page-layout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,wEAAwE;AACxE,wBAAgB,aAAa,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,qBAQvG;AAED,yFAAyF;AACzF,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,QAAQ,EACR,SAAS,GACV,EAAE;IACD,MAAM,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,qBAWA;AAED,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,MAAM,EACN,OAAO,EACP,QAAQ,EACR,SAAS,GACV,EAAE;IACD,MAAM,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACxB,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,qBAeA;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,MAAM,EACN,QAAQ,EACR,SAAS,EACT,IAAY,GACb,EAAE;IACD,MAAM,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACxB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,OAAO,CAAA;CACf,qBAiBA"}
package/page-layout.js ADDED
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from 'react';
3
+ import { cn } from './utils.js';
4
+ // The page shells that fill an AppShell's <main>. The fade-in is the CSS
5
+ // `.reveal` (visible-by-default @starting-style), never a JS animation.
6
+ /** Content-driven pages (dashboards, forms): the whole body scrolls. */
7
+ export function PageContainer({ className, children }) {
8
+ return (_jsx("div", { className: "app-scroll flex-1 overflow-y-auto", children: _jsx("div", { className: cn('reveal mx-auto w-full max-w-(--breakpoint-2xl) p-4 sm:p-6', className), children: children }) }));
9
+ }
10
+ /** List pages: a sticky header (title/actions/search/filters); only the body scrolls. */
11
+ export function ListPageLayout({ header, children, className, }) {
12
+ return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsx("div", { className: "border-b border-border bg-surface px-3 pb-2.5 pt-3 sm:px-6 sm:pb-3 sm:pt-4", children: _jsx("div", { className: "reveal mx-auto max-w-(--breakpoint-2xl) space-y-2 sm:space-y-2.5", children: header }) }), _jsx("div", { className: "app-scroll min-h-0 flex-1 overflow-y-auto", children: _jsx("div", { className: cn('reveal mx-auto max-w-(--breakpoint-2xl) p-3 sm:p-6', className), children: children }) })] }));
13
+ }
14
+ /** Detail pages: fixed header + optional alerts + subtab strip; content scrolls. */
15
+ export function DetailPageLayout({ header, alerts, subtabs, children, className, }) {
16
+ return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsx("div", { className: "border-b border-border bg-surface", children: _jsxs("div", { className: "reveal mx-auto max-w-(--breakpoint-2xl) px-3 pt-3 sm:px-6 sm:pt-5", children: [header, alerts ? _jsx("div", { className: "mt-2.5 space-y-2 sm:mt-3", children: alerts }) : null, subtabs ? _jsx("div", { className: "mt-2.5 sm:mt-4", children: subtabs }) : null] }) }), _jsx("div", { className: "app-scroll min-h-0 flex-1 overflow-y-auto", children: _jsx("div", { className: cn('reveal mx-auto max-w-(--breakpoint-2xl) p-3 sm:p-6', className), children: children }) })] }));
17
+ }
18
+ /** Wizard/form pages: sticky header, scrolling body, optional sticky footer. */
19
+ export function WizardLayout({ header, footer, children, className, wide = false, }) {
20
+ const maxW = wide ? 'max-w-(--breakpoint-2xl)' : 'max-w-3xl';
21
+ return (_jsxs("div", { className: cn('flex h-full min-h-0 flex-col', className), children: [_jsx("div", { className: "border-b border-border bg-surface", children: _jsx("div", { className: cn('reveal mx-auto px-4 py-4 sm:px-6', maxW), children: header }) }), _jsx("div", { className: "app-scroll min-h-0 flex-1 overflow-y-auto", children: _jsx("div", { className: cn('reveal mx-auto space-y-5 p-4 sm:p-6', maxW), children: children }) }), footer != null ? (_jsx("div", { className: "border-t border-border bg-surface", children: _jsx("div", { className: cn('mx-auto px-4 py-3 sm:px-6', maxW), children: footer }) })) : null] }));
22
+ }
23
+ //# sourceMappingURL=page-layout.js.map