@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 @@
1
+ {"version":3,"file":"page-layout.js","sourceRoot":"","sources":["../src/page-layout.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAE5B,yEAAyE;AACzE,wEAAwE;AAExE,wEAAwE;AACxE,MAAM,UAAU,aAAa,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAqD;IACtG,OAAO,CACL,cAAK,SAAS,EAAC,mCAAmC,YAChD,cAAK,SAAS,EAAE,EAAE,CAAC,2DAA2D,EAAE,SAAS,CAAC,YACvF,QAAQ,GACL,GACF,CACP,CAAA;AACH,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,cAAc,CAAC,EAC7B,MAAM,EACN,QAAQ,EACR,SAAS,GAKV;IACC,OAAO,CACL,eAAK,SAAS,EAAC,8BAA8B,aAC3C,cAAK,SAAS,EAAC,4EAA4E,YACzF,cAAK,SAAS,EAAC,kEAAkE,YAAE,MAAM,GAAO,GAC5F,EACN,cAAK,SAAS,EAAC,2CAA2C,YACxD,cAAK,SAAS,EAAE,EAAE,CAAC,oDAAoD,EAAE,SAAS,CAAC,YAAG,QAAQ,GAAO,GACjG,IACF,CACP,CAAA;AACH,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,gBAAgB,CAAC,EAC/B,MAAM,EACN,MAAM,EACN,OAAO,EACP,QAAQ,EACR,SAAS,GAOV;IACC,OAAO,CACL,eAAK,SAAS,EAAC,8BAA8B,aAC3C,cAAK,SAAS,EAAC,mCAAmC,YAChD,eAAK,SAAS,EAAC,mEAAmE,aAC/E,MAAM,EACN,MAAM,CAAC,CAAC,CAAC,cAAK,SAAS,EAAC,0BAA0B,YAAE,MAAM,GAAO,CAAC,CAAC,CAAC,IAAI,EACxE,OAAO,CAAC,CAAC,CAAC,cAAK,SAAS,EAAC,gBAAgB,YAAE,OAAO,GAAO,CAAC,CAAC,CAAC,IAAI,IAC7D,GACF,EACN,cAAK,SAAS,EAAC,2CAA2C,YACxD,cAAK,SAAS,EAAE,EAAE,CAAC,oDAAoD,EAAE,SAAS,CAAC,YAAG,QAAQ,GAAO,GACjG,IACF,CACP,CAAA;AACH,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,YAAY,CAAC,EAC3B,MAAM,EACN,MAAM,EACN,QAAQ,EACR,SAAS,EACT,IAAI,GAAG,KAAK,GAOb;IACC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,WAAW,CAAA;IAC5D,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,8BAA8B,EAAE,SAAS,CAAC,aAC3D,cAAK,SAAS,EAAC,mCAAmC,YAChD,cAAK,SAAS,EAAE,EAAE,CAAC,kCAAkC,EAAE,IAAI,CAAC,YAAG,MAAM,GAAO,GACxE,EACN,cAAK,SAAS,EAAC,2CAA2C,YACxD,cAAK,SAAS,EAAE,EAAE,CAAC,qCAAqC,EAAE,IAAI,CAAC,YAAG,QAAQ,GAAO,GAC7E,EACL,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,CAChB,cAAK,SAAS,EAAC,mCAAmC,YAChD,cAAK,SAAS,EAAE,EAAE,CAAC,2BAA2B,EAAE,IAAI,CAAC,YAAG,MAAM,GAAO,GACjE,CACP,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAA;AACH,CAAC","sourcesContent":["import * as React from 'react'\nimport { cn } from './utils'\n\n// The page shells that fill an AppShell's <main>. The fade-in is the CSS\n// `.reveal` (visible-by-default @starting-style), never a JS animation.\n\n/** Content-driven pages (dashboards, forms): the whole body scrolls. */\nexport function PageContainer({ className, children }: { className?: string; children: React.ReactNode }) {\n return (\n <div className=\"app-scroll flex-1 overflow-y-auto\">\n <div className={cn('reveal mx-auto w-full max-w-(--breakpoint-2xl) p-4 sm:p-6', className)}>\n {children}\n </div>\n </div>\n )\n}\n\n/** List pages: a sticky header (title/actions/search/filters); only the body scrolls. */\nexport function ListPageLayout({\n header,\n children,\n className,\n}: {\n header: React.ReactNode\n children: React.ReactNode\n className?: string\n}) {\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <div className=\"border-b border-border bg-surface px-3 pb-2.5 pt-3 sm:px-6 sm:pb-3 sm:pt-4\">\n <div className=\"reveal mx-auto max-w-(--breakpoint-2xl) space-y-2 sm:space-y-2.5\">{header}</div>\n </div>\n <div className=\"app-scroll min-h-0 flex-1 overflow-y-auto\">\n <div className={cn('reveal mx-auto max-w-(--breakpoint-2xl) p-3 sm:p-6', className)}>{children}</div>\n </div>\n </div>\n )\n}\n\n/** Detail pages: fixed header + optional alerts + subtab strip; content scrolls. */\nexport function DetailPageLayout({\n header,\n alerts,\n subtabs,\n children,\n className,\n}: {\n header: React.ReactNode\n alerts?: React.ReactNode\n subtabs?: React.ReactNode\n children: React.ReactNode\n className?: string\n}) {\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <div className=\"border-b border-border bg-surface\">\n <div className=\"reveal mx-auto max-w-(--breakpoint-2xl) px-3 pt-3 sm:px-6 sm:pt-5\">\n {header}\n {alerts ? <div className=\"mt-2.5 space-y-2 sm:mt-3\">{alerts}</div> : null}\n {subtabs ? <div className=\"mt-2.5 sm:mt-4\">{subtabs}</div> : null}\n </div>\n </div>\n <div className=\"app-scroll min-h-0 flex-1 overflow-y-auto\">\n <div className={cn('reveal mx-auto max-w-(--breakpoint-2xl) p-3 sm:p-6', className)}>{children}</div>\n </div>\n </div>\n )\n}\n\n/** Wizard/form pages: sticky header, scrolling body, optional sticky footer. */\nexport function WizardLayout({\n header,\n footer,\n children,\n className,\n wide = false,\n}: {\n header: React.ReactNode\n footer?: React.ReactNode\n children: React.ReactNode\n className?: string\n wide?: boolean\n}) {\n const maxW = wide ? 'max-w-(--breakpoint-2xl)' : 'max-w-3xl'\n return (\n <div className={cn('flex h-full min-h-0 flex-col', className)}>\n <div className=\"border-b border-border bg-surface\">\n <div className={cn('reveal mx-auto px-4 py-4 sm:px-6', maxW)}>{header}</div>\n </div>\n <div className=\"app-scroll min-h-0 flex-1 overflow-y-auto\">\n <div className={cn('reveal mx-auto space-y-5 p-4 sm:p-6', maxW)}>{children}</div>\n </div>\n {footer != null ? (\n <div className=\"border-t border-border bg-surface\">\n <div className={cn('mx-auto px-4 py-3 sm:px-6', maxW)}>{footer}</div>\n </div>\n ) : null}\n </div>\n )\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import * as React from 'react';
2
+ export interface PageTransitionProps {
3
+ /** A stable value that changes when the routed page changes, usually usePathname(). */
4
+ navigationKey: React.Key;
5
+ children: React.ReactNode;
6
+ className?: string;
7
+ }
8
+ /**
9
+ * Keeps the application shell live while the browser hands the changing page
10
+ * canvas between routes. Requires Next.js `experimental.viewTransition`.
11
+ */
12
+ export declare function PageTransition({ navigationKey, children, className, }: PageTransitionProps): React.JSX.Element;
13
+ //# sourceMappingURL=page-transition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-transition.d.ts","sourceRoot":"","sources":["../src/page-transition.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,MAAM,WAAW,mBAAmB;IAClC,uFAAuF;IACvF,aAAa,EAAE,KAAK,CAAC,GAAG,CAAA;IACxB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC7B,aAAa,EACb,QAAQ,EACR,SAAS,GACV,EAAE,mBAAmB,qBAoBrB"}
@@ -0,0 +1,14 @@
1
+ /// <reference types="react/canary" />
2
+ 'use client';
3
+ import { jsx as _jsx } from "react/jsx-runtime";
4
+ import * as React from 'react';
5
+ import { ViewTransition } from 'react';
6
+ import { cn } from './utils.js';
7
+ /**
8
+ * Keeps the application shell live while the browser hands the changing page
9
+ * canvas between routes. Requires Next.js `experimental.viewTransition`.
10
+ */
11
+ export function PageTransition({ navigationKey, children, className, }) {
12
+ return (_jsx(ViewTransition, { name: "appkit-page", share: "appkit-page-transition", enter: "appkit-page-transition", exit: "appkit-page-transition", default: "none", children: _jsx("div", { className: cn('appkit-page-frame flex min-h-0 flex-1 flex-col overflow-hidden', className), children: children }) }, navigationKey));
13
+ }
14
+ //# sourceMappingURL=page-transition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-transition.js","sourceRoot":"","sources":["../src/page-transition.tsx"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC,YAAY,CAAA;;AAEZ,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAS5B;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,EAC7B,aAAa,EACb,QAAQ,EACR,SAAS,GACW;IACpB,OAAO,CACL,KAAC,cAAc,IAEb,IAAI,EAAC,aAAa,EAClB,KAAK,EAAC,wBAAwB,EAC9B,KAAK,EAAC,wBAAwB,EAC9B,IAAI,EAAC,wBAAwB,EAC7B,OAAO,EAAC,MAAM,YAEd,cACE,SAAS,EAAE,EAAE,CACX,gEAAgE,EAChE,SAAS,CACV,YAEA,QAAQ,GACL,IAdD,aAAa,CAeH,CAClB,CAAA;AACH,CAAC","sourcesContent":["/// <reference types=\"react/canary\" />\n\n'use client'\n\nimport * as React from 'react'\nimport { ViewTransition } from 'react'\nimport { cn } from './utils'\n\nexport interface PageTransitionProps {\n /** A stable value that changes when the routed page changes, usually usePathname(). */\n navigationKey: React.Key\n children: React.ReactNode\n className?: string\n}\n\n/**\n * Keeps the application shell live while the browser hands the changing page\n * canvas between routes. Requires Next.js `experimental.viewTransition`.\n */\nexport function PageTransition({\n navigationKey,\n children,\n className,\n}: PageTransitionProps) {\n return (\n <ViewTransition\n key={navigationKey}\n name=\"appkit-page\"\n share=\"appkit-page-transition\"\n enter=\"appkit-page-transition\"\n exit=\"appkit-page-transition\"\n default=\"none\"\n >\n <div\n className={cn(\n 'appkit-page-frame flex min-h-0 flex-1 flex-col overflow-hidden',\n className,\n )}\n >\n {children}\n </div>\n </ViewTransition>\n )\n}\n"]}
@@ -0,0 +1,54 @@
1
+ import * as React from 'react';
2
+ /** Comparable value a column exposes for client-side ordering. */
3
+ export type PagedSortValue = string | number | Date | null | undefined;
4
+ export interface PagedColumn<T> {
5
+ key: string;
6
+ header: React.ReactNode;
7
+ align?: 'left' | 'right';
8
+ cell: (row: T) => React.ReactNode;
9
+ /** Text used for client-side search matching. */
10
+ search?: (row: T) => string;
11
+ /**
12
+ * Comparable value used for client-side ordering. Supplying it turns the
13
+ * column header into a sort control; omit it for columns that can't be
14
+ * ordered (actions, rendered composites without a natural key).
15
+ */
16
+ sortValue?: (row: T) => PagedSortValue;
17
+ }
18
+ export interface PagedTableSort {
19
+ key: string;
20
+ dir: 'asc' | 'desc';
21
+ }
22
+ export interface PagedTableLabels {
23
+ searchPlaceholder: string;
24
+ searchLabel: string;
25
+ showing: (from: number, to: number, total: number) => React.ReactNode;
26
+ prev: string;
27
+ next: string;
28
+ pageOf: (page: number, pages: number) => React.ReactNode;
29
+ /** Shown in place of rows when a search matches nothing. */
30
+ noResults: (query: string) => React.ReactNode;
31
+ /** Accessible name for a column's sort control. */
32
+ sortBy: (column: string) => string;
33
+ }
34
+ export interface PagedTableProps<T> {
35
+ rows: T[];
36
+ columns: PagedColumn<T>[];
37
+ pageSize?: number;
38
+ searchable?: boolean;
39
+ empty: React.ReactNode;
40
+ rowKey: (row: T, index: number) => string;
41
+ rowClassName?: (row: T) => string | undefined;
42
+ /** Initial ordering; the header controls take over from there. */
43
+ defaultSort?: PagedTableSort;
44
+ /** Row activation — makes rows clickable (open a drawer, navigate, select). */
45
+ onRowClick?: (row: T) => void;
46
+ labels?: Partial<PagedTableLabels>;
47
+ }
48
+ /**
49
+ * Client-side searched, sorted, and paginated table for bounded data already
50
+ * loaded into a page or drawer. The call surface matches the production
51
+ * reference; only localized copy and semantic tokens are injected/generalized.
52
+ */
53
+ export declare function PagedTable<T>({ rows, columns, pageSize, searchable, empty, rowKey, rowClassName, defaultSort, onRowClick, labels: labelOverrides, }: PagedTableProps<T>): React.JSX.Element;
54
+ //# sourceMappingURL=paged-table.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paged-table.d.ts","sourceRoot":"","sources":["../src/paged-table.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAc9B,kEAAkE;AAClE,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,SAAS,CAAA;AAEtE,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACxB,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,KAAK,CAAC,SAAS,CAAA;IACjC,iDAAiD;IACjD,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,CAAA;IAC3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,cAAc,CAAA;CACvC;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,KAAK,GAAG,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,iBAAiB,EAAE,MAAM,CAAA;IACzB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAA;IACrE,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAA;IACxD,4DAA4D;IAC5D,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAA;IAC7C,mDAAmD;IACnD,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAA;CACnC;AAiBD,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAA;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IACzC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,CAAA;IAC7C,kEAAkE;IAClE,WAAW,CAAC,EAAE,cAAc,CAAA;IAC5B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAA;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;CACnC;AAYD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,EAC5B,IAAI,EACJ,OAAO,EACP,QAAa,EACb,UAAkB,EAClB,KAAK,EACL,MAAM,EACN,YAAY,EACZ,WAAW,EACX,UAAU,EACV,MAAM,EAAE,cAAc,GACvB,EAAE,eAAe,CAAC,CAAC,CAAC,qBA+FpB"}
package/paged-table.js ADDED
@@ -0,0 +1,74 @@
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 { ArrowDown, ArrowUp } from 'lucide-react';
5
+ import { Button } from './button.js';
6
+ import { Input } from './input.js';
7
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from './table.js';
8
+ import { cn } from './utils.js';
9
+ const DEFAULT_LABELS = {
10
+ searchPlaceholder: 'Search…',
11
+ searchLabel: 'Search',
12
+ showing: (from, to, total) => _jsxs(_Fragment, { children: ["Showing ", _jsx("strong", { className: "font-semibold text-fg", children: from }), "\u2013", _jsx("strong", { className: "font-semibold text-fg", children: to }), " of", ' ', _jsx("strong", { className: "font-semibold text-fg", children: total })] }),
13
+ prev: 'Prev',
14
+ next: 'Next',
15
+ pageOf: (page, pages) => _jsxs(_Fragment, { children: ["Page ", page, " of ", pages] }),
16
+ noResults: (query) => _jsxs(_Fragment, { children: ["No matches for \u201C", query, "\u201D"] }),
17
+ sortBy: (column) => `Sort by ${column}`,
18
+ };
19
+ /** Blanks order last ascending, so a sorted column leads with real values. */
20
+ function compareValues(a, b) {
21
+ const aBlank = a == null || a === '';
22
+ const bBlank = b == null || b === '';
23
+ if (aBlank || bBlank)
24
+ return aBlank && bBlank ? 0 : aBlank ? 1 : -1;
25
+ if (a instanceof Date && b instanceof Date)
26
+ return a.getTime() - b.getTime();
27
+ if (typeof a === 'number' && typeof b === 'number')
28
+ return a - b;
29
+ return String(a).localeCompare(String(b), undefined, { numeric: true, sensitivity: 'base' });
30
+ }
31
+ /**
32
+ * Client-side searched, sorted, and paginated table for bounded data already
33
+ * loaded into a page or drawer. The call surface matches the production
34
+ * reference; only localized copy and semantic tokens are injected/generalized.
35
+ */
36
+ export function PagedTable({ rows, columns, pageSize = 10, searchable = false, empty, rowKey, rowClassName, defaultSort, onRowClick, labels: labelOverrides, }) {
37
+ const labels = { ...DEFAULT_LABELS, ...labelOverrides };
38
+ const [query, setQuery] = React.useState('');
39
+ const [page, setPage] = React.useState(0);
40
+ const [sort, setSort] = React.useState(defaultSort ?? null);
41
+ const filtered = React.useMemo(() => {
42
+ const normalized = query.trim().toLocaleLowerCase();
43
+ if (!normalized)
44
+ return rows;
45
+ return rows.filter((row) => columns.some((column) => column.search?.(row).toLocaleLowerCase().includes(normalized)));
46
+ }, [columns, query, rows]);
47
+ const ordered = React.useMemo(() => {
48
+ const read = sort ? columns.find((column) => column.key === sort.key)?.sortValue : undefined;
49
+ if (!read || !sort)
50
+ return filtered;
51
+ const direction = sort.dir === 'asc' ? 1 : -1;
52
+ return [...filtered].sort((a, b) => compareValues(read(a), read(b)) * direction);
53
+ }, [columns, filtered, sort]);
54
+ const boundedPageSize = Math.max(1, Math.trunc(pageSize));
55
+ const pageCount = Math.max(1, Math.ceil(ordered.length / boundedPageSize));
56
+ const clamped = Math.min(page, pageCount - 1);
57
+ const start = clamped * boundedPageSize;
58
+ const visible = ordered.slice(start, start + boundedPageSize);
59
+ const toggleSort = (key) => {
60
+ setSort((current) => current?.key === key ? { key, dir: current.dir === 'asc' ? 'desc' : 'asc' } : { key, dir: 'asc' });
61
+ setPage(0);
62
+ };
63
+ if (rows.length === 0)
64
+ return _jsx(_Fragment, { children: empty });
65
+ return _jsxs("div", { className: "space-y-3", children: [searchable ? _jsx(Input, { type: "search", "aria-label": labels.searchLabel, value: query, onChange: (event) => {
66
+ setQuery(event.target.value);
67
+ setPage(0);
68
+ }, placeholder: labels.searchPlaceholder, className: "max-w-xs" }) : null, _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsx(TableRow, { noAnimate: true, children: columns.map((column) => {
69
+ const active = sort?.key === column.key;
70
+ const alignRight = column.align === 'right';
71
+ return _jsx(TableHead, { align: alignRight ? 'right' : undefined, className: alignRight ? 'text-right' : undefined, "aria-sort": column.sortValue ? (active ? (sort.dir === 'asc' ? 'ascending' : 'descending') : 'none') : undefined, children: column.sortValue ? _jsxs("button", { type: "button", onClick: () => toggleSort(column.key), "aria-label": typeof column.header === 'string' ? labels.sortBy(column.header) : undefined, className: cn('inline-flex items-center gap-1 uppercase tracking-wide transition-colors hover:text-fg', alignRight && 'flex-row-reverse', active && 'text-fg'), children: [column.header, active ? (sort.dir === 'asc' ? _jsx(ArrowUp, { className: "size-3" }) : _jsx(ArrowDown, { className: "size-3" })) : null] }) : column.header }, column.key);
72
+ }) }) }), _jsx(TableBody, { children: visible.length === 0 ? _jsx(TableRow, { children: _jsx(TableCell, { colSpan: columns.length, className: "py-8 text-center text-sm text-fg-muted", children: labels.noResults(query.trim()) }) }) : visible.map((row, index) => _jsx(TableRow, { onClick: onRowClick ? () => onRowClick(row) : undefined, className: cn(onRowClick && 'cursor-pointer', rowClassName?.(row)), children: columns.map((column) => _jsx(TableCell, { className: column.align === 'right' ? 'text-right tabular-nums' : undefined, children: column.cell(row) }, column.key)) }, rowKey(row, start + index))) })] }), ordered.length > boundedPageSize ? _jsxs("div", { className: "flex items-center justify-between text-xs text-fg-muted", children: [_jsx("span", { children: labels.showing(start + 1, Math.min(start + boundedPageSize, ordered.length), ordered.length) }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Button, { variant: "outline", size: "sm", disabled: clamped === 0, onClick: () => setPage(clamped - 1), children: labels.prev }), _jsx("span", { className: "tabular-nums", children: labels.pageOf(clamped + 1, pageCount) }), _jsx(Button, { variant: "outline", size: "sm", disabled: clamped >= pageCount - 1, onClick: () => setPage(clamped + 1), children: labels.next })] })] }) : null] });
73
+ }
74
+ //# sourceMappingURL=paged-table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paged-table.js","sourceRoot":"","sources":["../src/paged-table.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { ArrowDown, ArrowUp } from 'lucide-react'\nimport { Button } from './button'\nimport { Input } from './input'\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from './table'\nimport { cn } from './utils'\n\n/** Comparable value a column exposes for client-side ordering. */\nexport type PagedSortValue = string | number | Date | null | undefined\n\nexport interface PagedColumn<T> {\n key: string\n header: React.ReactNode\n align?: 'left' | 'right'\n cell: (row: T) => React.ReactNode\n /** Text used for client-side search matching. */\n search?: (row: T) => string\n /**\n * Comparable value used for client-side ordering. Supplying it turns the\n * column header into a sort control; omit it for columns that can't be\n * ordered (actions, rendered composites without a natural key).\n */\n sortValue?: (row: T) => PagedSortValue\n}\n\nexport interface PagedTableSort {\n key: string\n dir: 'asc' | 'desc'\n}\n\nexport interface PagedTableLabels {\n searchPlaceholder: string\n searchLabel: string\n showing: (from: number, to: number, total: number) => React.ReactNode\n prev: string\n next: string\n pageOf: (page: number, pages: number) => React.ReactNode\n /** Shown in place of rows when a search matches nothing. */\n noResults: (query: string) => React.ReactNode\n /** Accessible name for a column's sort control. */\n sortBy: (column: string) => string\n}\n\nconst DEFAULT_LABELS: PagedTableLabels = {\n searchPlaceholder: 'Search…',\n searchLabel: 'Search',\n showing: (from, to, total) => <>\n Showing <strong className=\"font-semibold text-fg\">{from}</strong>–\n <strong className=\"font-semibold text-fg\">{to}</strong> of{' '}\n <strong className=\"font-semibold text-fg\">{total}</strong>\n </>,\n prev: 'Prev',\n next: 'Next',\n pageOf: (page, pages) => <>Page {page} of {pages}</>,\n noResults: (query) => <>No matches for “{query}”</>,\n sortBy: (column) => `Sort by ${column}`,\n}\n\nexport interface PagedTableProps<T> {\n rows: T[]\n columns: PagedColumn<T>[]\n pageSize?: number\n searchable?: boolean\n empty: React.ReactNode\n rowKey: (row: T, index: number) => string\n rowClassName?: (row: T) => string | undefined\n /** Initial ordering; the header controls take over from there. */\n defaultSort?: PagedTableSort\n /** Row activation — makes rows clickable (open a drawer, navigate, select). */\n onRowClick?: (row: T) => void\n labels?: Partial<PagedTableLabels>\n}\n\n/** Blanks order last ascending, so a sorted column leads with real values. */\nfunction compareValues(a: PagedSortValue, b: PagedSortValue): number {\n const aBlank = a == null || a === ''\n const bBlank = b == null || b === ''\n if (aBlank || bBlank) return aBlank && bBlank ? 0 : aBlank ? 1 : -1\n if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime()\n if (typeof a === 'number' && typeof b === 'number') return a - b\n return String(a).localeCompare(String(b), undefined, { numeric: true, sensitivity: 'base' })\n}\n\n/**\n * Client-side searched, sorted, and paginated table for bounded data already\n * loaded into a page or drawer. The call surface matches the production\n * reference; only localized copy and semantic tokens are injected/generalized.\n */\nexport function PagedTable<T>({\n rows,\n columns,\n pageSize = 10,\n searchable = false,\n empty,\n rowKey,\n rowClassName,\n defaultSort,\n onRowClick,\n labels: labelOverrides,\n}: PagedTableProps<T>) {\n const labels = { ...DEFAULT_LABELS, ...labelOverrides }\n const [query, setQuery] = React.useState('')\n const [page, setPage] = React.useState(0)\n const [sort, setSort] = React.useState<PagedTableSort | null>(defaultSort ?? null)\n\n const filtered = React.useMemo(() => {\n const normalized = query.trim().toLocaleLowerCase()\n if (!normalized) return rows\n return rows.filter((row) => columns.some((column) =>\n column.search?.(row).toLocaleLowerCase().includes(normalized),\n ))\n }, [columns, query, rows])\n\n const ordered = React.useMemo(() => {\n const read = sort ? columns.find((column) => column.key === sort.key)?.sortValue : undefined\n if (!read || !sort) return filtered\n const direction = sort.dir === 'asc' ? 1 : -1\n return [...filtered].sort((a, b) => compareValues(read(a), read(b)) * direction)\n }, [columns, filtered, sort])\n\n const boundedPageSize = Math.max(1, Math.trunc(pageSize))\n const pageCount = Math.max(1, Math.ceil(ordered.length / boundedPageSize))\n const clamped = Math.min(page, pageCount - 1)\n const start = clamped * boundedPageSize\n const visible = ordered.slice(start, start + boundedPageSize)\n\n const toggleSort = (key: string) => {\n setSort((current) =>\n current?.key === key ? { key, dir: current.dir === 'asc' ? 'desc' : 'asc' } : { key, dir: 'asc' },\n )\n setPage(0)\n }\n\n if (rows.length === 0) return <>{empty}</>\n\n return <div className=\"space-y-3\">\n {searchable ? <Input\n type=\"search\"\n aria-label={labels.searchLabel}\n value={query}\n onChange={(event) => {\n setQuery(event.target.value)\n setPage(0)\n }}\n placeholder={labels.searchPlaceholder}\n className=\"max-w-xs\"\n /> : null}\n <Table>\n <TableHeader>\n <TableRow noAnimate>{columns.map((column) => {\n const active = sort?.key === column.key\n const alignRight = column.align === 'right'\n return <TableHead\n key={column.key}\n align={alignRight ? 'right' : undefined}\n className={alignRight ? 'text-right' : undefined}\n aria-sort={column.sortValue ? (active ? (sort!.dir === 'asc' ? 'ascending' : 'descending') : 'none') : undefined}\n >{column.sortValue ? <button\n type=\"button\"\n onClick={() => toggleSort(column.key)}\n aria-label={typeof column.header === 'string' ? labels.sortBy(column.header) : undefined}\n className={cn(\n 'inline-flex items-center gap-1 uppercase tracking-wide transition-colors hover:text-fg',\n alignRight && 'flex-row-reverse',\n active && 'text-fg',\n )}\n >\n {column.header}\n {active ? (sort!.dir === 'asc' ? <ArrowUp className=\"size-3\" /> : <ArrowDown className=\"size-3\" />) : null}\n </button> : column.header}</TableHead>\n })}</TableRow>\n </TableHeader>\n <TableBody>{visible.length === 0 ? <TableRow>\n <TableCell colSpan={columns.length} className=\"py-8 text-center text-sm text-fg-muted\">\n {labels.noResults(query.trim())}\n </TableCell>\n </TableRow> : visible.map((row, index) => <TableRow\n key={rowKey(row, start + index)}\n onClick={onRowClick ? () => onRowClick(row) : undefined}\n className={cn(onRowClick && 'cursor-pointer', rowClassName?.(row))}\n >{columns.map((column) => <TableCell\n key={column.key}\n className={column.align === 'right' ? 'text-right tabular-nums' : undefined}\n >{column.cell(row)}</TableCell>)}</TableRow>)}</TableBody>\n </Table>\n {ordered.length > boundedPageSize ? <div className=\"flex items-center justify-between text-xs text-fg-muted\">\n <span>{labels.showing(start + 1, Math.min(start + boundedPageSize, ordered.length), ordered.length)}</span>\n <div className=\"flex items-center gap-1.5\">\n <Button variant=\"outline\" size=\"sm\" disabled={clamped === 0} onClick={() => setPage(clamped - 1)}>{labels.prev}</Button>\n <span className=\"tabular-nums\">{labels.pageOf(clamped + 1, pageCount)}</span>\n <Button variant=\"outline\" size=\"sm\" disabled={clamped >= pageCount - 1} onClick={() => setPage(clamped + 1)}>{labels.next}</Button>\n </div>\n </div> : null}\n </div>\n}\n"]}
package/popover.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * Portal-based popover that escapes any overflow-hidden ancestor. The trigger
4
+ * stays in place; the floating panel renders into <body> at a fixed position
5
+ * computed from the trigger's rect and re-measured on scroll/resize.
6
+ */
7
+ export declare function Popover({ trigger, open, onOpenChange, align, side, className, children, }: {
8
+ trigger: React.ReactElement;
9
+ open: boolean;
10
+ onOpenChange: (open: boolean) => void;
11
+ align?: 'start' | 'end';
12
+ side?: 'top' | 'bottom' | 'left' | 'right';
13
+ className?: string;
14
+ children: React.ReactNode;
15
+ }): React.JSX.Element;
16
+ //# sourceMappingURL=popover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"popover.d.ts","sourceRoot":"","sources":["../src/popover.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,EACtB,OAAO,EACP,IAAI,EACJ,YAAY,EACZ,KAAa,EACb,IAAe,EACf,SAAS,EACT,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,KAAK,CAAC,YAAY,CAAA;IAC3B,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,CAAA;IACvB,IAAI,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAA;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B,qBAyHA"}
package/popover.js ADDED
@@ -0,0 +1,99 @@
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 { createPortal } from 'react-dom';
5
+ import { AnimatePresence, motion } from 'framer-motion';
6
+ import { cn } from './utils.js';
7
+ /**
8
+ * Portal-based popover that escapes any overflow-hidden ancestor. The trigger
9
+ * stays in place; the floating panel renders into <body> at a fixed position
10
+ * computed from the trigger's rect and re-measured on scroll/resize.
11
+ */
12
+ export function Popover({ trigger, open, onOpenChange, align = 'end', side = 'bottom', className, children, }) {
13
+ const triggerRef = React.useRef(null);
14
+ const panelRef = React.useRef(null);
15
+ const [rect, setRect] = React.useState(null);
16
+ const [mounted, setMounted] = React.useState(false);
17
+ React.useEffect(() => setMounted(true), []);
18
+ React.useEffect(() => {
19
+ if (!open)
20
+ return;
21
+ const t = triggerRef.current?.firstElementChild;
22
+ if (!t)
23
+ return;
24
+ function measure() {
25
+ const r = t.getBoundingClientRect();
26
+ setRect({ top: r.top, left: r.left, width: r.width, height: r.height });
27
+ }
28
+ measure();
29
+ window.addEventListener('resize', measure);
30
+ window.addEventListener('scroll', measure, true);
31
+ return () => {
32
+ window.removeEventListener('resize', measure);
33
+ window.removeEventListener('scroll', measure, true);
34
+ };
35
+ }, [open]);
36
+ React.useEffect(() => {
37
+ if (!open)
38
+ return;
39
+ function onClick(e) {
40
+ const target = e.target;
41
+ if (panelRef.current?.contains(target))
42
+ return;
43
+ if (triggerRef.current?.contains(target))
44
+ return;
45
+ // Nested overlays are portaled siblings, not descendants — treat as inside.
46
+ if (target instanceof Element && target.closest('[data-ui-overlay]'))
47
+ return;
48
+ onOpenChange(false);
49
+ }
50
+ function onKey(e) {
51
+ if (e.key === 'Escape')
52
+ onOpenChange(false);
53
+ }
54
+ const t = setTimeout(() => {
55
+ document.addEventListener('mousedown', onClick);
56
+ document.addEventListener('keydown', onKey);
57
+ }, 0);
58
+ return () => {
59
+ clearTimeout(t);
60
+ document.removeEventListener('mousedown', onClick);
61
+ document.removeEventListener('keydown', onKey);
62
+ };
63
+ }, [open, onOpenChange]);
64
+ return (_jsxs(_Fragment, { children: [_jsx("div", { ref: triggerRef, className: "contents", children: trigger }), mounted && rect && typeof document !== 'undefined'
65
+ ? createPortal(_jsx(AnimatePresence, { children: open ? (_jsx(motion.div, { ref: panelRef, "data-ui-overlay": true, initial: {
66
+ opacity: 0,
67
+ x: side === 'right' ? -4 : side === 'left' ? 4 : 0,
68
+ y: side === 'bottom' ? -4 : side === 'top' ? 4 : 0,
69
+ scale: 0.97,
70
+ }, animate: { opacity: 1, x: 0, y: 0, scale: 1 }, exit: {
71
+ opacity: 0,
72
+ x: side === 'right' ? -4 : side === 'left' ? 4 : 0,
73
+ y: side === 'bottom' ? -4 : side === 'top' ? 4 : 0,
74
+ scale: 0.97,
75
+ }, transition: { duration: 0.14, ease: [0.16, 1, 0.3, 1] }, className: cn('fixed z-[60] min-w-[12rem] origin-top rounded-md border border-border bg-elevated shadow-lg', className), style: {
76
+ top: side === 'bottom'
77
+ ? rect.top + rect.height + 4
78
+ : (side === 'left' || side === 'right') && align === 'start'
79
+ ? rect.top
80
+ : undefined,
81
+ bottom: side === 'top'
82
+ ? window.innerHeight - rect.top + 4
83
+ : (side === 'left' || side === 'right') && align === 'end'
84
+ ? window.innerHeight - (rect.top + rect.height)
85
+ : undefined,
86
+ left: side === 'right'
87
+ ? rect.left + rect.width + 4
88
+ : (side === 'top' || side === 'bottom') && align === 'start'
89
+ ? rect.left
90
+ : undefined,
91
+ right: side === 'left'
92
+ ? window.innerWidth - rect.left + 4
93
+ : (side === 'top' || side === 'bottom') && align === 'end'
94
+ ? window.innerWidth - (rect.left + rect.width)
95
+ : undefined,
96
+ }, role: "dialog", children: children })) : null }), document.body)
97
+ : null] }));
98
+ }
99
+ //# sourceMappingURL=popover.js.map
package/popover.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"popover.js","sourceRoot":"","sources":["../src/popover.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { createPortal } from 'react-dom'\nimport { AnimatePresence, motion } from 'framer-motion'\nimport { cn } from './utils'\n\n/**\n * Portal-based popover that escapes any overflow-hidden ancestor. The trigger\n * stays in place; the floating panel renders into <body> at a fixed position\n * computed from the trigger's rect and re-measured on scroll/resize.\n */\nexport function Popover({\n trigger,\n open,\n onOpenChange,\n align = 'end',\n side = 'bottom',\n className,\n children,\n}: {\n trigger: React.ReactElement\n open: boolean\n onOpenChange: (open: boolean) => void\n align?: 'start' | 'end'\n side?: 'top' | 'bottom' | 'left' | 'right'\n className?: string\n children: React.ReactNode\n}) {\n const triggerRef = React.useRef<HTMLDivElement>(null)\n const panelRef = React.useRef<HTMLDivElement>(null)\n const [rect, setRect] = React.useState<{\n top: number\n left: number\n width: number\n height: number\n } | null>(null)\n const [mounted, setMounted] = React.useState(false)\n\n React.useEffect(() => setMounted(true), [])\n\n React.useEffect(() => {\n if (!open) return\n const t = triggerRef.current?.firstElementChild as HTMLElement | null\n if (!t) return\n function measure() {\n const r = t!.getBoundingClientRect()\n setRect({ top: r.top, left: r.left, width: r.width, height: r.height })\n }\n measure()\n window.addEventListener('resize', measure)\n window.addEventListener('scroll', measure, true)\n return () => {\n window.removeEventListener('resize', measure)\n window.removeEventListener('scroll', measure, true)\n }\n }, [open])\n\n React.useEffect(() => {\n if (!open) return\n function onClick(e: MouseEvent) {\n const target = e.target as Node\n if (panelRef.current?.contains(target)) return\n if (triggerRef.current?.contains(target)) return\n // Nested overlays are portaled siblings, not descendants — treat as inside.\n if (target instanceof Element && target.closest('[data-ui-overlay]')) return\n onOpenChange(false)\n }\n function onKey(e: KeyboardEvent) {\n if (e.key === 'Escape') onOpenChange(false)\n }\n const t = setTimeout(() => {\n document.addEventListener('mousedown', onClick)\n document.addEventListener('keydown', onKey)\n }, 0)\n return () => {\n clearTimeout(t)\n document.removeEventListener('mousedown', onClick)\n document.removeEventListener('keydown', onKey)\n }\n }, [open, onOpenChange])\n\n return (\n <>\n <div ref={triggerRef} className=\"contents\">\n {trigger}\n </div>\n {mounted && rect && typeof document !== 'undefined'\n ? createPortal(\n <AnimatePresence>\n {open ? (\n <motion.div\n ref={panelRef}\n data-ui-overlay\n initial={{\n opacity: 0,\n x: side === 'right' ? -4 : side === 'left' ? 4 : 0,\n y: side === 'bottom' ? -4 : side === 'top' ? 4 : 0,\n scale: 0.97,\n }}\n animate={{ opacity: 1, x: 0, y: 0, scale: 1 }}\n exit={{\n opacity: 0,\n x: side === 'right' ? -4 : side === 'left' ? 4 : 0,\n y: side === 'bottom' ? -4 : side === 'top' ? 4 : 0,\n scale: 0.97,\n }}\n transition={{ duration: 0.14, ease: [0.16, 1, 0.3, 1] }}\n className={cn(\n 'fixed z-[60] min-w-[12rem] origin-top rounded-md border border-border bg-elevated shadow-lg',\n className,\n )}\n style={{\n top:\n side === 'bottom'\n ? rect.top + rect.height + 4\n : (side === 'left' || side === 'right') && align === 'start'\n ? rect.top\n : undefined,\n bottom:\n side === 'top'\n ? window.innerHeight - rect.top + 4\n : (side === 'left' || side === 'right') && align === 'end'\n ? window.innerHeight - (rect.top + rect.height)\n : undefined,\n left:\n side === 'right'\n ? rect.left + rect.width + 4\n : (side === 'top' || side === 'bottom') && align === 'start'\n ? rect.left\n : undefined,\n right:\n side === 'left'\n ? window.innerWidth - rect.left + 4\n : (side === 'top' || side === 'bottom') && align === 'end'\n ? window.innerWidth - (rect.left + rect.width)\n : undefined,\n }}\n role=\"dialog\"\n >\n {children}\n </motion.div>\n ) : null}\n </AnimatePresence>,\n document.body,\n )\n : null}\n </>\n )\n}\n"]}
package/progress.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import * as React from 'react';
2
+ export type ProgressProps = React.HTMLAttributes<HTMLDivElement> & {
3
+ /** 0–100. Omit for an indeterminate bar. */
4
+ value?: number;
5
+ };
6
+ export declare function Progress({ className, value, ...props }: ProgressProps): React.JSX.Element;
7
+ //# sourceMappingURL=progress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../src/progress.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,GAAG;IACjE,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,wBAAgB,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,qBAqBrE"}
package/progress.js ADDED
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from 'react';
3
+ import { cn } from './utils.js';
4
+ export function Progress({ className, value, ...props }) {
5
+ const clamped = value == null ? null : Math.max(0, Math.min(100, value));
6
+ return (_jsx("div", { role: "progressbar", "aria-valuenow": clamped ?? undefined, "aria-valuemin": 0, "aria-valuemax": 100, className: cn('h-2 w-full overflow-hidden rounded-full bg-border-subtle', className), ...props, children: clamped == null ? (_jsx("div", { className: "h-full w-2/5 rounded-full bg-primary/80 [animation:appkit-indeterminate_1.2s_ease-in-out_infinite] motion-reduce:animate-none" })) : (_jsx("div", { className: "h-full rounded-full bg-primary transition-[width] duration-500 ease-out", style: { width: `${clamped}%` } })) }));
7
+ }
8
+ //# sourceMappingURL=progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.js","sourceRoot":"","sources":["../src/progress.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAO5B,MAAM,UAAU,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,KAAK,EAAiB;IACpE,MAAM,OAAO,GAAG,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAA;IACxE,OAAO,CACL,cACE,IAAI,EAAC,aAAa,mBACH,OAAO,IAAI,SAAS,mBACpB,CAAC,mBACD,GAAG,EAClB,SAAS,EAAE,EAAE,CAAC,0DAA0D,EAAE,SAAS,CAAC,KAChF,KAAK,YAER,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,CACjB,cAAK,SAAS,EAAC,+HAA+H,GAAG,CAClJ,CAAC,CAAC,CAAC,CACF,cACE,SAAS,EAAC,yEAAyE,EACnF,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,OAAO,GAAG,EAAE,GAC/B,CACH,GACG,CACP,CAAA;AACH,CAAC","sourcesContent":["import * as React from 'react'\nimport { cn } from './utils'\n\nexport type ProgressProps = React.HTMLAttributes<HTMLDivElement> & {\n /** 0–100. Omit for an indeterminate bar. */\n value?: number\n}\n\nexport function Progress({ className, value, ...props }: ProgressProps) {\n const clamped = value == null ? null : Math.max(0, Math.min(100, value))\n return (\n <div\n role=\"progressbar\"\n aria-valuenow={clamped ?? undefined}\n aria-valuemin={0}\n aria-valuemax={100}\n className={cn('h-2 w-full overflow-hidden rounded-full bg-border-subtle', className)}\n {...props}\n >\n {clamped == null ? (\n <div className=\"h-full w-2/5 rounded-full bg-primary/80 [animation:appkit-indeterminate_1.2s_ease-in-out_infinite] motion-reduce:animate-none\" />\n ) : (\n <div\n className=\"h-full rounded-full bg-primary transition-[width] duration-500 ease-out\"\n style={{ width: `${clamped}%` }}\n />\n )}\n </div>\n )\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import * as React from 'react';
2
+ export type PromptDialogOptions = {
3
+ title: string;
4
+ /** Field label above the input. */
5
+ label?: string;
6
+ /** Pre-filled and selected value. */
7
+ initialValue?: string;
8
+ placeholder?: string;
9
+ confirmLabel?: string;
10
+ cancelLabel?: string;
11
+ };
12
+ /** Opens the shared text-input modal and resolves to a trimmed value or null. */
13
+ export declare function promptDialog(options: PromptDialogOptions): Promise<string | null>;
14
+ /** Cancels the active prompt, if one exists. */
15
+ export declare function cancelPromptDialog(): void;
16
+ /** Mount once beside the application's toaster. */
17
+ export declare function PromptRoot({ defaultConfirmLabel, defaultCancelLabel, }: {
18
+ defaultConfirmLabel?: string;
19
+ defaultCancelLabel?: string;
20
+ }): React.ReactPortal | null;
21
+ //# sourceMappingURL=prompt-dialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-dialog.d.ts","sourceRoot":"","sources":["../src/prompt-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAO9B,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qCAAqC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAeD,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMjF;AAED,gDAAgD;AAChD,wBAAgB,kBAAkB,SAEjC;AAeD,mDAAmD;AACnD,wBAAgB,UAAU,CAAC,EACzB,mBAA4B,EAC5B,kBAA6B,GAC9B,EAAE;IACD,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,4BAmIA"}
@@ -0,0 +1,109 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from 'react';
4
+ import { createPortal } from 'react-dom';
5
+ import { AnimatePresence, motion, useReducedMotion } from 'framer-motion';
6
+ import { Button } from './button.js';
7
+ import { Input } from './input.js';
8
+ import { Label } from './label.js';
9
+ let current = null;
10
+ let counter = 0;
11
+ const listeners = new Set();
12
+ function emit() {
13
+ for (const listener of listeners)
14
+ listener();
15
+ }
16
+ /** Opens the shared text-input modal and resolves to a trimmed value or null. */
17
+ export function promptDialog(options) {
18
+ return new Promise((resolve) => {
19
+ current?.resolve(null);
20
+ current = { ...options, id: ++counter, resolve };
21
+ emit();
22
+ });
23
+ }
24
+ /** Cancels the active prompt, if one exists. */
25
+ export function cancelPromptDialog() {
26
+ settle(null);
27
+ }
28
+ function settle(value) {
29
+ const request = current;
30
+ if (!request)
31
+ return;
32
+ current = null;
33
+ request.resolve(value);
34
+ emit();
35
+ }
36
+ function subscribe(listener) {
37
+ listeners.add(listener);
38
+ return () => listeners.delete(listener);
39
+ }
40
+ /** Mount once beside the application's toaster. */
41
+ export function PromptRoot({ defaultConfirmLabel = 'Save', defaultCancelLabel = 'Cancel', }) {
42
+ const request = React.useSyncExternalStore(subscribe, () => current, () => null);
43
+ const [value, setValue] = React.useState('');
44
+ const inputRef = React.useRef(null);
45
+ const panelRef = React.useRef(null);
46
+ const reduceMotion = useReducedMotion();
47
+ const titleId = React.useId();
48
+ const inputId = React.useId();
49
+ React.useEffect(() => {
50
+ if (!request)
51
+ return;
52
+ setValue(request.initialValue ?? '');
53
+ const frame = requestAnimationFrame(() => {
54
+ inputRef.current?.focus();
55
+ inputRef.current?.select();
56
+ });
57
+ const previousOverflow = document.body.style.overflow;
58
+ const previouslyFocused = document.activeElement;
59
+ document.body.style.overflow = 'hidden';
60
+ const focusable = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
61
+ function onKeyDown(event) {
62
+ if (event.key === 'Escape') {
63
+ event.preventDefault();
64
+ settle(null);
65
+ return;
66
+ }
67
+ if (event.key !== 'Tab')
68
+ return;
69
+ const panel = panelRef.current;
70
+ if (!panel)
71
+ return;
72
+ const items = Array.from(panel.querySelectorAll(focusable)).filter((element) => element.offsetParent !== null || element === document.activeElement);
73
+ if (items.length === 0) {
74
+ event.preventDefault();
75
+ panel.focus();
76
+ return;
77
+ }
78
+ const first = items[0];
79
+ const last = items.at(-1);
80
+ if (event.shiftKey && document.activeElement === first) {
81
+ event.preventDefault();
82
+ last.focus();
83
+ }
84
+ else if (!event.shiftKey && document.activeElement === last) {
85
+ event.preventDefault();
86
+ first.focus();
87
+ }
88
+ }
89
+ document.addEventListener('keydown', onKeyDown);
90
+ return () => {
91
+ cancelAnimationFrame(frame);
92
+ document.removeEventListener('keydown', onKeyDown);
93
+ document.body.style.overflow = previousOverflow;
94
+ if (previouslyFocused && document.contains(previouslyFocused))
95
+ previouslyFocused.focus();
96
+ };
97
+ }, [request?.id]);
98
+ if (typeof document === 'undefined')
99
+ return null;
100
+ function submit() {
101
+ const trimmed = value.trim();
102
+ settle(trimmed || null);
103
+ }
104
+ return createPortal(_jsx(AnimatePresence, { children: request ? (_jsxs("div", { "data-ui-overlay": "prompt", className: "fixed inset-0 z-[60] flex items-center justify-center p-4", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: [_jsx(motion.div, { ref: panelRef, tabIndex: -1, initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, className: "absolute inset-0 bg-overlay/50 backdrop-blur-[2px]", onClick: () => settle(null), "aria-hidden": "true" }), _jsx(motion.div, { initial: reduceMotion ? { opacity: 0 } : { opacity: 0, scale: 0.96, y: 8 }, animate: reduceMotion ? { opacity: 1 } : { opacity: 1, scale: 1, y: 0 }, exit: reduceMotion ? { opacity: 0 } : { opacity: 0, scale: 0.96, y: 8 }, transition: { type: 'spring', damping: 26, stiffness: 340, mass: 0.7 }, className: "relative w-full max-w-md overflow-hidden rounded-xl border border-border bg-surface shadow-lg", children: _jsxs("form", { onSubmit: (event) => {
105
+ event.preventDefault();
106
+ submit();
107
+ }, children: [_jsxs("div", { className: "space-y-3 p-6", children: [_jsx("h2", { id: titleId, className: "text-base font-semibold text-fg", children: request.title }), request.label ? (_jsx(Label, { htmlFor: inputId, className: "text-xs text-fg-muted", children: request.label })) : null, _jsx(Input, { id: inputId, ref: inputRef, value: value, placeholder: request.placeholder, onChange: (event) => setValue(event.target.value) })] }), _jsxs("div", { className: "flex justify-end gap-2 border-t border-border bg-bg-subtle px-6 py-4", children: [_jsx(Button, { type: "button", variant: "outline", onClick: () => settle(null), children: request.cancelLabel ?? defaultCancelLabel }), _jsx(Button, { type: "submit", disabled: !value.trim(), children: request.confirmLabel ?? defaultConfirmLabel })] })] }) })] }, request.id)) : null }), document.body);
108
+ }
109
+ //# sourceMappingURL=prompt-dialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-dialog.js","sourceRoot":"","sources":["../src/prompt-dialog.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { createPortal } from 'react-dom'\nimport { AnimatePresence, motion, useReducedMotion } from 'framer-motion'\nimport { Button } from './button'\nimport { Input } from './input'\nimport { Label } from './label'\n\nexport type PromptDialogOptions = {\n title: string\n /** Field label above the input. */\n label?: string\n /** Pre-filled and selected value. */\n initialValue?: string\n placeholder?: string\n confirmLabel?: string\n cancelLabel?: string\n}\n\ntype PromptRequest = PromptDialogOptions & {\n id: number\n resolve: (value: string | null) => void\n}\n\nlet current: PromptRequest | null = null\nlet counter = 0\nconst listeners = new Set<() => void>()\n\nfunction emit() {\n for (const listener of listeners) listener()\n}\n\n/** Opens the shared text-input modal and resolves to a trimmed value or null. */\nexport function promptDialog(options: PromptDialogOptions): Promise<string | null> {\n return new Promise((resolve) => {\n current?.resolve(null)\n current = { ...options, id: ++counter, resolve }\n emit()\n })\n}\n\n/** Cancels the active prompt, if one exists. */\nexport function cancelPromptDialog() {\n settle(null)\n}\n\nfunction settle(value: string | null) {\n const request = current\n if (!request) return\n current = null\n request.resolve(value)\n emit()\n}\n\nfunction subscribe(listener: () => void) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n}\n\n/** Mount once beside the application's toaster. */\nexport function PromptRoot({\n defaultConfirmLabel = 'Save',\n defaultCancelLabel = 'Cancel',\n}: {\n defaultConfirmLabel?: string\n defaultCancelLabel?: string\n}) {\n const request = React.useSyncExternalStore(subscribe, () => current, () => null)\n const [value, setValue] = React.useState('')\n const inputRef = React.useRef<HTMLInputElement>(null)\n const panelRef = React.useRef<HTMLDivElement>(null)\n const reduceMotion = useReducedMotion()\n const titleId = React.useId()\n const inputId = React.useId()\n\n React.useEffect(() => {\n if (!request) return\n setValue(request.initialValue ?? '')\n const frame = requestAnimationFrame(() => {\n inputRef.current?.focus()\n inputRef.current?.select()\n })\n const previousOverflow = document.body.style.overflow\n const previouslyFocused = document.activeElement as HTMLElement | null\n document.body.style.overflow = 'hidden'\n const focusable =\n 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n function onKeyDown(event: KeyboardEvent) {\n if (event.key === 'Escape') {\n event.preventDefault()\n settle(null)\n return\n }\n if (event.key !== 'Tab') return\n const panel = panelRef.current\n if (!panel) return\n const items = Array.from(panel.querySelectorAll<HTMLElement>(focusable)).filter(\n (element) => element.offsetParent !== null || element === document.activeElement,\n )\n if (items.length === 0) {\n event.preventDefault()\n panel.focus()\n return\n }\n const first = items[0]!\n const last = items.at(-1)!\n if (event.shiftKey && document.activeElement === first) {\n event.preventDefault()\n last.focus()\n } else if (!event.shiftKey && document.activeElement === last) {\n event.preventDefault()\n first.focus()\n }\n }\n document.addEventListener('keydown', onKeyDown)\n return () => {\n cancelAnimationFrame(frame)\n document.removeEventListener('keydown', onKeyDown)\n document.body.style.overflow = previousOverflow\n if (previouslyFocused && document.contains(previouslyFocused)) previouslyFocused.focus()\n }\n }, [request?.id])\n\n if (typeof document === 'undefined') return null\n\n function submit() {\n const trimmed = value.trim()\n settle(trimmed || null)\n }\n\n return createPortal(\n <AnimatePresence>\n {request ? (\n <div\n key={request.id}\n data-ui-overlay=\"prompt\"\n className=\"fixed inset-0 z-[60] flex items-center justify-center p-4\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n >\n <motion.div\n ref={panelRef}\n tabIndex={-1}\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.15 }}\n className=\"absolute inset-0 bg-overlay/50 backdrop-blur-[2px]\"\n onClick={() => settle(null)}\n aria-hidden=\"true\"\n />\n <motion.div\n initial={reduceMotion ? { opacity: 0 } : { opacity: 0, scale: 0.96, y: 8 }}\n animate={reduceMotion ? { opacity: 1 } : { opacity: 1, scale: 1, y: 0 }}\n exit={reduceMotion ? { opacity: 0 } : { opacity: 0, scale: 0.96, y: 8 }}\n transition={{ type: 'spring', damping: 26, stiffness: 340, mass: 0.7 }}\n className=\"relative w-full max-w-md overflow-hidden rounded-xl border border-border bg-surface shadow-lg\"\n >\n <form\n onSubmit={(event) => {\n event.preventDefault()\n submit()\n }}\n >\n <div className=\"space-y-3 p-6\">\n <h2 id={titleId} className=\"text-base font-semibold text-fg\">\n {request.title}\n </h2>\n {request.label ? (\n <Label htmlFor={inputId} className=\"text-xs text-fg-muted\">\n {request.label}\n </Label>\n ) : null}\n <Input\n id={inputId}\n ref={inputRef}\n value={value}\n placeholder={request.placeholder}\n onChange={(event) => setValue(event.target.value)}\n />\n </div>\n <div className=\"flex justify-end gap-2 border-t border-border bg-bg-subtle px-6 py-4\">\n <Button type=\"button\" variant=\"outline\" onClick={() => settle(null)}>\n {request.cancelLabel ?? defaultCancelLabel}\n </Button>\n <Button type=\"submit\" disabled={!value.trim()}>\n {request.confirmLabel ?? defaultConfirmLabel}\n </Button>\n </div>\n </form>\n </motion.div>\n </div>\n ) : null}\n </AnimatePresence>,\n document.body,\n )\n}\n"]}