@inshorts/glint-ui 0.5.4 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (340) hide show
  1. package/README.md +3 -1
  2. package/dist/alert.cjs +96 -0
  3. package/dist/alert.cjs.map +1 -0
  4. package/dist/alert.d.cts +29 -0
  5. package/dist/alert.d.ts +29 -0
  6. package/dist/alert.js +10 -0
  7. package/dist/alert.js.map +1 -0
  8. package/dist/autocomplete.cjs +578 -0
  9. package/dist/autocomplete.cjs.map +1 -0
  10. package/dist/autocomplete.d.cts +56 -0
  11. package/dist/autocomplete.d.ts +56 -0
  12. package/dist/autocomplete.js +11 -0
  13. package/dist/autocomplete.js.map +1 -0
  14. package/dist/badge.cjs +89 -0
  15. package/dist/badge.cjs.map +1 -0
  16. package/dist/badge.d.cts +25 -0
  17. package/dist/badge.d.ts +25 -0
  18. package/dist/badge.js +10 -0
  19. package/dist/badge.js.map +1 -0
  20. package/dist/breadcrumb.cjs +88 -0
  21. package/dist/breadcrumb.cjs.map +1 -0
  22. package/dist/breadcrumb.d.cts +14 -0
  23. package/dist/breadcrumb.d.ts +14 -0
  24. package/dist/breadcrumb.js +8 -0
  25. package/dist/breadcrumb.js.map +1 -0
  26. package/dist/button.cjs +143 -0
  27. package/dist/button.cjs.map +1 -0
  28. package/dist/button.d.cts +34 -0
  29. package/dist/button.d.ts +34 -0
  30. package/dist/button.js +9 -0
  31. package/dist/button.js.map +1 -0
  32. package/dist/card.cjs +111 -0
  33. package/dist/card.cjs.map +1 -0
  34. package/dist/card.d.cts +15 -0
  35. package/dist/card.d.ts +15 -0
  36. package/dist/card.js +18 -0
  37. package/dist/card.js.map +1 -0
  38. package/dist/checkbox.cjs +66 -0
  39. package/dist/checkbox.cjs.map +1 -0
  40. package/{src/checkbox.tsx → dist/checkbox.d.cts} +5 -22
  41. package/dist/checkbox.d.ts +25 -0
  42. package/dist/checkbox.js +10 -0
  43. package/dist/checkbox.js.map +1 -0
  44. package/dist/chunk-5MEOZHJA.js +513 -0
  45. package/dist/chunk-5MEOZHJA.js.map +1 -0
  46. package/dist/chunk-67ACNS42.js +29 -0
  47. package/dist/chunk-67ACNS42.js.map +1 -0
  48. package/dist/chunk-A6GRLKOO.js +85 -0
  49. package/dist/chunk-A6GRLKOO.js.map +1 -0
  50. package/dist/chunk-DS3QYIKL.js +90 -0
  51. package/dist/chunk-DS3QYIKL.js.map +1 -0
  52. package/dist/chunk-EWSBFU54.js +53 -0
  53. package/dist/chunk-EWSBFU54.js.map +1 -0
  54. package/dist/chunk-G4ZQUSAJ.js +38 -0
  55. package/dist/chunk-G4ZQUSAJ.js.map +1 -0
  56. package/dist/chunk-GMCUXEFI.js +67 -0
  57. package/dist/chunk-GMCUXEFI.js.map +1 -0
  58. package/dist/chunk-HBBOR43A.js +85 -0
  59. package/dist/chunk-HBBOR43A.js.map +1 -0
  60. package/dist/chunk-HCA5ODCD.js +11 -0
  61. package/dist/chunk-HCA5ODCD.js.map +1 -0
  62. package/dist/chunk-HYRJYUPI.js +26 -0
  63. package/dist/chunk-HYRJYUPI.js.map +1 -0
  64. package/dist/chunk-IRCTGJQB.js +26 -0
  65. package/dist/chunk-IRCTGJQB.js.map +1 -0
  66. package/dist/chunk-ISPICB4W.js +70 -0
  67. package/dist/chunk-ISPICB4W.js.map +1 -0
  68. package/dist/chunk-JDE5XYPT.js +30 -0
  69. package/dist/chunk-JDE5XYPT.js.map +1 -0
  70. package/dist/chunk-JSJEEKR3.js +59 -0
  71. package/dist/chunk-JSJEEKR3.js.map +1 -0
  72. package/dist/chunk-KR3BCFP7.js +85 -0
  73. package/dist/chunk-KR3BCFP7.js.map +1 -0
  74. package/dist/chunk-MLRSOTFI.js +81 -0
  75. package/dist/chunk-MLRSOTFI.js.map +1 -0
  76. package/dist/chunk-MQT5RAQV.js +202 -0
  77. package/dist/chunk-MQT5RAQV.js.map +1 -0
  78. package/dist/chunk-MUPW2TFJ.js +102 -0
  79. package/dist/chunk-MUPW2TFJ.js.map +1 -0
  80. package/dist/chunk-OUYT6SPU.js +76 -0
  81. package/dist/chunk-OUYT6SPU.js.map +1 -0
  82. package/dist/chunk-PM2VE7DJ.js +100 -0
  83. package/dist/chunk-PM2VE7DJ.js.map +1 -0
  84. package/dist/chunk-PPYR3O3L.js +44 -0
  85. package/dist/chunk-PPYR3O3L.js.map +1 -0
  86. package/dist/chunk-PUTC52SI.js +98 -0
  87. package/dist/chunk-PUTC52SI.js.map +1 -0
  88. package/dist/chunk-PWAKTEXF.js +18 -0
  89. package/dist/chunk-PWAKTEXF.js.map +1 -0
  90. package/dist/chunk-QTFWORA7.js +26 -0
  91. package/dist/chunk-QTFWORA7.js.map +1 -0
  92. package/dist/chunk-RUPL2H6V.js +49 -0
  93. package/dist/chunk-RUPL2H6V.js.map +1 -0
  94. package/dist/chunk-S7PVZAPL.js +28 -0
  95. package/dist/chunk-S7PVZAPL.js.map +1 -0
  96. package/dist/chunk-SQDV2AKJ.js +49 -0
  97. package/dist/chunk-SQDV2AKJ.js.map +1 -0
  98. package/dist/chunk-T2KXVH3O.js +297 -0
  99. package/dist/chunk-T2KXVH3O.js.map +1 -0
  100. package/dist/chunk-T4WCCHJQ.js +29 -0
  101. package/dist/chunk-T4WCCHJQ.js.map +1 -0
  102. package/dist/chunk-UC3ZIQPE.js +93 -0
  103. package/dist/chunk-UC3ZIQPE.js.map +1 -0
  104. package/dist/chunk-UE7JY7VR.js +28 -0
  105. package/dist/chunk-UE7JY7VR.js.map +1 -0
  106. package/dist/chunk-Y5WCT6XN.js +93 -0
  107. package/dist/chunk-Y5WCT6XN.js.map +1 -0
  108. package/dist/chunk-YDICVETX.js +151 -0
  109. package/dist/chunk-YDICVETX.js.map +1 -0
  110. package/dist/chunk-ZFSQVMLJ.js +126 -0
  111. package/dist/chunk-ZFSQVMLJ.js.map +1 -0
  112. package/dist/chunk-ZHATD73Y.js +46 -0
  113. package/dist/chunk-ZHATD73Y.js.map +1 -0
  114. package/dist/chunk-ZLAA4DGG.js +111 -0
  115. package/dist/chunk-ZLAA4DGG.js.map +1 -0
  116. package/dist/chunk-ZP252OJZ.js +56 -0
  117. package/dist/chunk-ZP252OJZ.js.map +1 -0
  118. package/dist/clipboard.cjs +125 -0
  119. package/dist/clipboard.cjs.map +1 -0
  120. package/dist/clipboard.d.cts +52 -0
  121. package/dist/clipboard.d.ts +52 -0
  122. package/dist/clipboard.js +23 -0
  123. package/dist/clipboard.js.map +1 -0
  124. package/dist/collapsible.cjs +94 -0
  125. package/dist/collapsible.cjs.map +1 -0
  126. package/dist/collapsible.d.cts +21 -0
  127. package/dist/collapsible.d.ts +21 -0
  128. package/dist/collapsible.js +9 -0
  129. package/dist/collapsible.js.map +1 -0
  130. package/dist/confirm-dialog.cjs +369 -0
  131. package/dist/confirm-dialog.cjs.map +1 -0
  132. package/dist/confirm-dialog.d.cts +16 -0
  133. package/dist/confirm-dialog.d.ts +16 -0
  134. package/dist/confirm-dialog.js +13 -0
  135. package/dist/confirm-dialog.js.map +1 -0
  136. package/dist/copy-button.cjs +102 -0
  137. package/dist/copy-button.cjs.map +1 -0
  138. package/dist/copy-button.d.cts +18 -0
  139. package/dist/copy-button.d.ts +18 -0
  140. package/dist/copy-button.js +10 -0
  141. package/dist/copy-button.js.map +1 -0
  142. package/dist/data-table.cjs +140 -0
  143. package/dist/data-table.cjs.map +1 -0
  144. package/dist/data-table.d.cts +81 -0
  145. package/dist/data-table.d.ts +81 -0
  146. package/dist/data-table.js +8 -0
  147. package/dist/data-table.js.map +1 -0
  148. package/dist/date-range-picker/dateRange.cjs +138 -0
  149. package/dist/date-range-picker/dateRange.cjs.map +1 -0
  150. package/dist/date-range-picker/dateRange.d.cts +57 -0
  151. package/dist/date-range-picker/dateRange.d.ts +57 -0
  152. package/dist/date-range-picker/dateRange.js +31 -0
  153. package/dist/date-range-picker/dateRange.js.map +1 -0
  154. package/dist/date-range-picker/index.cjs +375 -0
  155. package/dist/date-range-picker/index.cjs.map +1 -0
  156. package/dist/date-range-picker/index.d.cts +18 -0
  157. package/dist/date-range-picker/index.d.ts +18 -0
  158. package/dist/date-range-picker/index.js +10 -0
  159. package/dist/date-range-picker/index.js.map +1 -0
  160. package/dist/dialogLayer.cjs +121 -0
  161. package/dist/dialogLayer.cjs.map +1 -0
  162. package/dist/dialogLayer.d.cts +11 -0
  163. package/dist/dialogLayer.d.ts +11 -0
  164. package/dist/dialogLayer.js +8 -0
  165. package/dist/dialogLayer.js.map +1 -0
  166. package/dist/feature-access-gate.cjs +160 -0
  167. package/dist/feature-access-gate.cjs.map +1 -0
  168. package/dist/feature-access-gate.d.cts +14 -0
  169. package/dist/feature-access-gate.d.ts +14 -0
  170. package/dist/feature-access-gate.js +11 -0
  171. package/dist/feature-access-gate.js.map +1 -0
  172. package/dist/field.cjs +58 -0
  173. package/dist/field.cjs.map +1 -0
  174. package/dist/field.d.cts +23 -0
  175. package/dist/field.d.ts +23 -0
  176. package/dist/field.js +8 -0
  177. package/dist/field.js.map +1 -0
  178. package/dist/image-uploader.cjs +293 -0
  179. package/dist/image-uploader.cjs.map +1 -0
  180. package/dist/image-uploader.d.cts +34 -0
  181. package/dist/image-uploader.d.ts +34 -0
  182. package/dist/image-uploader.js +12 -0
  183. package/dist/image-uploader.js.map +1 -0
  184. package/dist/index.cjs +3008 -0
  185. package/dist/index.cjs.map +1 -0
  186. package/dist/index.d.cts +41 -0
  187. package/dist/index.d.ts +41 -0
  188. package/dist/index.js +232 -0
  189. package/dist/index.js.map +1 -0
  190. package/dist/input.cjs +58 -0
  191. package/dist/input.cjs.map +1 -0
  192. package/dist/input.d.cts +7 -0
  193. package/dist/input.d.ts +7 -0
  194. package/dist/input.js +10 -0
  195. package/dist/input.js.map +1 -0
  196. package/dist/label.cjs +67 -0
  197. package/dist/label.cjs.map +1 -0
  198. package/dist/label.d.cts +7 -0
  199. package/dist/label.d.ts +7 -0
  200. package/dist/label.js +9 -0
  201. package/dist/label.js.map +1 -0
  202. package/dist/modal.cjs +212 -0
  203. package/dist/modal.cjs.map +1 -0
  204. package/dist/modal.d.cts +39 -0
  205. package/dist/modal.d.ts +39 -0
  206. package/dist/modal.js +12 -0
  207. package/dist/modal.js.map +1 -0
  208. package/dist/page-header.cjs +78 -0
  209. package/dist/page-header.cjs.map +1 -0
  210. package/dist/page-header.d.cts +35 -0
  211. package/dist/page-header.d.ts +35 -0
  212. package/dist/page-header.js +8 -0
  213. package/dist/page-header.js.map +1 -0
  214. package/dist/pagination.cjs +237 -0
  215. package/dist/pagination.cjs.map +1 -0
  216. package/dist/pagination.d.cts +15 -0
  217. package/dist/pagination.d.ts +15 -0
  218. package/dist/pagination.js +11 -0
  219. package/dist/pagination.js.map +1 -0
  220. package/dist/panel-brand-mark.cjs +59 -0
  221. package/dist/panel-brand-mark.cjs.map +1 -0
  222. package/dist/panel-brand-mark.d.cts +13 -0
  223. package/dist/panel-brand-mark.d.ts +13 -0
  224. package/dist/panel-brand-mark.js +8 -0
  225. package/dist/panel-brand-mark.js.map +1 -0
  226. package/dist/panel-sign-in.cjs +256 -0
  227. package/dist/panel-sign-in.cjs.map +1 -0
  228. package/dist/panel-sign-in.d.cts +31 -0
  229. package/dist/panel-sign-in.d.ts +31 -0
  230. package/dist/panel-sign-in.js +14 -0
  231. package/dist/panel-sign-in.js.map +1 -0
  232. package/dist/segmented-control.cjs +122 -0
  233. package/dist/segmented-control.cjs.map +1 -0
  234. package/dist/segmented-control.d.cts +58 -0
  235. package/dist/segmented-control.d.ts +58 -0
  236. package/dist/segmented-control.js +9 -0
  237. package/dist/segmented-control.js.map +1 -0
  238. package/dist/select.cjs +648 -0
  239. package/dist/select.cjs.map +1 -0
  240. package/dist/select.d.cts +30 -0
  241. package/dist/select.d.ts +30 -0
  242. package/dist/select.js +12 -0
  243. package/dist/select.js.map +1 -0
  244. package/dist/separator.cjs +70 -0
  245. package/dist/separator.cjs.map +1 -0
  246. package/dist/separator.d.cts +13 -0
  247. package/dist/separator.d.ts +13 -0
  248. package/dist/separator.js +9 -0
  249. package/dist/separator.js.map +1 -0
  250. package/dist/side-drawer.cjs +321 -0
  251. package/dist/side-drawer.cjs.map +1 -0
  252. package/dist/side-drawer.d.cts +21 -0
  253. package/dist/side-drawer.d.ts +21 -0
  254. package/dist/side-drawer.js +13 -0
  255. package/dist/side-drawer.js.map +1 -0
  256. package/dist/spinner.cjs +57 -0
  257. package/dist/spinner.cjs.map +1 -0
  258. package/{src/spinner.tsx → dist/spinner.d.cts} +11 -22
  259. package/dist/spinner.d.ts +27 -0
  260. package/dist/spinner.js +8 -0
  261. package/dist/spinner.js.map +1 -0
  262. package/dist/spreadsheet-grid.cjs +238 -0
  263. package/dist/spreadsheet-grid.cjs.map +1 -0
  264. package/dist/spreadsheet-grid.d.cts +48 -0
  265. package/dist/spreadsheet-grid.d.ts +48 -0
  266. package/dist/spreadsheet-grid.js +8 -0
  267. package/dist/spreadsheet-grid.js.map +1 -0
  268. package/dist/state-panel.cjs +86 -0
  269. package/dist/state-panel.cjs.map +1 -0
  270. package/dist/state-panel.d.cts +23 -0
  271. package/dist/state-panel.d.ts +23 -0
  272. package/dist/state-panel.js +9 -0
  273. package/dist/state-panel.js.map +1 -0
  274. package/dist/switch.cjs +87 -0
  275. package/dist/switch.cjs.map +1 -0
  276. package/dist/switch.d.cts +13 -0
  277. package/dist/switch.d.ts +13 -0
  278. package/dist/switch.js +9 -0
  279. package/dist/switch.js.map +1 -0
  280. package/dist/table.cjs +139 -0
  281. package/dist/table.cjs.map +1 -0
  282. package/dist/table.d.cts +16 -0
  283. package/dist/table.d.ts +16 -0
  284. package/dist/table.js +22 -0
  285. package/dist/table.js.map +1 -0
  286. package/dist/tabs.cjs +168 -0
  287. package/dist/tabs.cjs.map +1 -0
  288. package/dist/tabs.d.cts +59 -0
  289. package/dist/tabs.d.ts +59 -0
  290. package/dist/tabs.js +11 -0
  291. package/dist/tabs.js.map +1 -0
  292. package/dist/textarea.cjs +66 -0
  293. package/dist/textarea.cjs.map +1 -0
  294. package/dist/textarea.d.cts +18 -0
  295. package/dist/textarea.d.ts +18 -0
  296. package/dist/textarea.js +10 -0
  297. package/dist/textarea.js.map +1 -0
  298. package/dist/utils.cjs +35 -0
  299. package/dist/utils.cjs.map +1 -0
  300. package/dist/utils.d.cts +5 -0
  301. package/dist/utils.d.ts +5 -0
  302. package/dist/utils.js +7 -0
  303. package/dist/utils.js.map +1 -0
  304. package/package.json +391 -14
  305. package/src/alert.tsx +0 -74
  306. package/src/autocomplete.tsx +0 -666
  307. package/src/badge.tsx +0 -60
  308. package/src/breadcrumb.tsx +0 -71
  309. package/src/button.tsx +0 -113
  310. package/src/card.tsx +0 -79
  311. package/src/clipboard.ts +0 -144
  312. package/src/collapsible.tsx +0 -63
  313. package/src/confirm-dialog.tsx +0 -84
  314. package/src/copy-button.tsx +0 -58
  315. package/src/data-table.tsx +0 -190
  316. package/src/date-range-picker/dateRange.ts +0 -142
  317. package/src/date-range-picker/index.tsx +0 -366
  318. package/src/dialogLayer.ts +0 -142
  319. package/src/feature-access-gate.tsx +0 -38
  320. package/src/field.tsx +0 -56
  321. package/src/image-uploader.tsx +0 -181
  322. package/src/index.ts +0 -37
  323. package/src/input.tsx +0 -16
  324. package/src/label.tsx +0 -23
  325. package/src/modal.tsx +0 -117
  326. package/src/page-header.tsx +0 -75
  327. package/src/pagination.tsx +0 -116
  328. package/src/panel-brand-mark.tsx +0 -32
  329. package/src/panel-sign-in.tsx +0 -134
  330. package/src/segmented-control.tsx +0 -155
  331. package/src/select.tsx +0 -105
  332. package/src/separator.tsx +0 -32
  333. package/src/side-drawer.tsx +0 -87
  334. package/src/spreadsheet-grid.tsx +0 -264
  335. package/src/state-panel.tsx +0 -46
  336. package/src/switch.tsx +0 -47
  337. package/src/table.tsx +0 -110
  338. package/src/tabs.tsx +0 -201
  339. package/src/textarea.tsx +0 -32
  340. package/src/utils.ts +0 -6
@@ -1,116 +0,0 @@
1
- "use client";
2
-
3
- import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react";
4
- import * as React from "react";
5
-
6
- import { Button } from "./button";
7
-
8
- export type PaginationProps = {
9
- currentPage: number;
10
- totalPages: number;
11
- onPageChange: (page: number) => void;
12
- loading?: boolean;
13
- showPageInfo?: boolean;
14
- };
15
-
16
- /**
17
- * Numbered pager for list panels. Hides itself when there is only one page.
18
- */
19
- export function Pagination({
20
- currentPage,
21
- totalPages,
22
- onPageChange,
23
- loading = false,
24
- showPageInfo = true,
25
- }: PaginationProps) {
26
- const visiblePages = React.useMemo(() => {
27
- const delta = 2;
28
- const range: number[] = [];
29
- const rangeWithDots: Array<number | "..."> = [];
30
-
31
- for (
32
- let i = Math.max(2, currentPage - delta);
33
- i <= Math.min(totalPages - 1, currentPage + delta);
34
- i++
35
- ) {
36
- range.push(i);
37
- }
38
-
39
- if (currentPage - delta > 2) {
40
- rangeWithDots.push(1, "...");
41
- } else {
42
- rangeWithDots.push(1);
43
- }
44
-
45
- rangeWithDots.push(...range);
46
-
47
- if (currentPage + delta < totalPages - 1) {
48
- rangeWithDots.push("...", totalPages);
49
- } else if (totalPages > 1) {
50
- rangeWithDots.push(totalPages);
51
- }
52
-
53
- return rangeWithDots;
54
- }, [currentPage, totalPages]);
55
-
56
- if (totalPages <= 1) return null;
57
-
58
- return (
59
- <div className="flex flex-col items-center justify-between gap-4 sm:flex-row">
60
- {showPageInfo ? (
61
- <div className="text-muted-foreground order-2 text-sm sm:order-1">
62
- Page {currentPage} of {totalPages}
63
- </div>
64
- ) : null}
65
-
66
- <div className="order-1 flex items-center space-x-1 sm:order-2 sm:space-x-2">
67
- <Button
68
- variant="outline"
69
- size="sm"
70
- onClick={() => onPageChange(currentPage - 1)}
71
- disabled={currentPage <= 1 || loading}
72
- className="h-8 min-w-8 px-2"
73
- aria-label="Previous page"
74
- >
75
- <ChevronLeft className="h-4 w-4" />
76
- </Button>
77
-
78
- <div className="flex max-w-[200px] items-center space-x-1 overflow-x-auto sm:max-w-none">
79
- {visiblePages.map((page, index) =>
80
- page === "..." ? (
81
- <div
82
- key={`dots-${index}`}
83
- className="flex h-8 w-8 shrink-0 items-center justify-center"
84
- >
85
- <MoreHorizontal className="h-4 w-4" />
86
- </div>
87
- ) : (
88
- <Button
89
- key={page}
90
- variant={page === currentPage ? "default" : "outline"}
91
- size="sm"
92
- onClick={() => onPageChange(page)}
93
- disabled={loading}
94
- className="h-8 min-w-8 shrink-0 px-2"
95
- aria-current={page === currentPage ? "page" : undefined}
96
- >
97
- {page}
98
- </Button>
99
- ),
100
- )}
101
- </div>
102
-
103
- <Button
104
- variant="outline"
105
- size="sm"
106
- onClick={() => onPageChange(currentPage + 1)}
107
- disabled={currentPage >= totalPages || loading}
108
- className="h-8 min-w-8 px-2"
109
- aria-label="Next page"
110
- >
111
- <ChevronRight className="h-4 w-4" />
112
- </Button>
113
- </div>
114
- </div>
115
- );
116
- }
@@ -1,32 +0,0 @@
1
- import * as React from "react";
2
-
3
- import { cn } from "./utils";
4
-
5
- export type PanelBrandMarkProps = React.HTMLAttributes<HTMLDivElement> & {
6
- /** Icon inside the mark (lucide, etc.). */
7
- children: React.ReactNode;
8
- size?: "sm" | "md";
9
- };
10
-
11
- /**
12
- * Quiet brand tile for sidebar / sign-in — solid primary, no decorative gradients.
13
- */
14
- export function PanelBrandMark({
15
- children,
16
- size = "md",
17
- className,
18
- ...props
19
- }: PanelBrandMarkProps) {
20
- return (
21
- <div
22
- className={cn(
23
- "bg-primary text-primary-foreground grid shrink-0 place-items-center rounded-lg",
24
- size === "sm" ? "h-9 w-9" : "h-10 w-10",
25
- className,
26
- )}
27
- {...props}
28
- >
29
- {children}
30
- </div>
31
- );
32
- }
@@ -1,134 +0,0 @@
1
- "use client";
2
-
3
- import { Loader2 } from "lucide-react";
4
- import * as React from "react";
5
-
6
- import { Button } from "./button";
7
- import { PanelBrandMark } from "./panel-brand-mark";
8
- import { cn } from "./utils";
9
-
10
- export type PanelSignInProps = {
11
- appName: string;
12
- /** Short line under the brand (e.g. "Secure Google sign-in"). */
13
- eyebrow?: string;
14
- /** One supporting sentence under the headline. */
15
- description?: string;
16
- /** Optional feature bullets — keep to ≤3; DESIGN.md: one job per surface. */
17
- features?: string[];
18
- brandIcon?: React.ReactNode;
19
- error?: string | null;
20
- /** Google signed in but panel access refused. */
21
- refused?: boolean;
22
- refusedTitle?: string;
23
- refusedMessage?: string;
24
- isSessionLoading?: boolean;
25
- isSigningIn?: boolean;
26
- onSignIn: () => void;
27
- signInLabel?: string;
28
- className?: string;
29
- };
30
-
31
- /**
32
- * DESIGN.md-compliant panel login: brand-first, ink neutrals, no purple glow.
33
- */
34
- export function PanelSignIn({
35
- appName,
36
- eyebrow = "Secure Google sign-in",
37
- description = "Use your work Google account to continue.",
38
- features,
39
- brandIcon,
40
- error,
41
- refused = false,
42
- refusedTitle = "Access denied",
43
- refusedMessage = "This Google account is signed in, but it is not provisioned for this panel.",
44
- isSessionLoading = false,
45
- isSigningIn = false,
46
- onSignIn,
47
- signInLabel = "Continue with Google",
48
- className,
49
- }: PanelSignInProps) {
50
- const busy = isSessionLoading || isSigningIn;
51
-
52
- return (
53
- <main
54
- className={cn(
55
- "bg-background text-foreground grid min-h-screen place-items-center p-4 sm:p-8",
56
- className,
57
- )}
58
- >
59
- <section className="border-border bg-card w-full max-w-md space-y-6 rounded-lg border p-8 shadow-sm">
60
- <div className="flex items-center gap-3">
61
- <PanelBrandMark>
62
- {brandIcon ?? (
63
- <span className="text-sm font-semibold" aria-hidden>
64
- {appName.slice(0, 1).toUpperCase()}
65
- </span>
66
- )}
67
- </PanelBrandMark>
68
- <div className="min-w-0">
69
- <p className="text-title truncate font-semibold">{appName}</p>
70
- <p className="text-muted-foreground text-meta">{eyebrow}</p>
71
- </div>
72
- </div>
73
-
74
- <div className="space-y-2">
75
- <h1 className="text-display font-semibold tracking-tight">
76
- {refused ? refusedTitle : "Sign in"}
77
- </h1>
78
- <p className="text-muted-foreground text-body">
79
- {refused ? refusedMessage : description}
80
- </p>
81
- </div>
82
-
83
- {features && features.length > 0 && !refused ? (
84
- <ul className="text-muted-foreground text-body space-y-1.5">
85
- {features.slice(0, 3).map((feature) => (
86
- <li key={feature} className="flex gap-2">
87
- <span className="text-foreground mt-1.5 h-1 w-1 shrink-0 rounded-full bg-current" />
88
- <span>{feature}</span>
89
- </li>
90
- ))}
91
- </ul>
92
- ) : null}
93
-
94
- {error ? (
95
- <p
96
- role="alert"
97
- className="border-destructive/30 bg-destructive/5 text-destructive rounded-md border px-3 py-2 text-sm"
98
- >
99
- {error}
100
- </p>
101
- ) : null}
102
-
103
- <Button
104
- className="w-full"
105
- disabled={busy}
106
- onClick={onSignIn}
107
- aria-busy={busy}
108
- >
109
- {busy ? (
110
- <Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden />
111
- ) : null}
112
- {isSigningIn ? "Redirecting…" : signInLabel}
113
- </Button>
114
- </section>
115
- </main>
116
- );
117
- }
118
-
119
- export function PanelSignInSkeleton({ className }: { className?: string }) {
120
- return (
121
- <main
122
- className={cn(
123
- "bg-background grid min-h-screen place-items-center p-4 sm:p-8",
124
- className,
125
- )}
126
- >
127
- <div className="border-border bg-card w-full max-w-md space-y-4 rounded-lg border p-8 shadow-sm">
128
- <div className="bg-muted h-3 w-24 animate-pulse rounded" />
129
- <div className="bg-muted h-8 w-48 animate-pulse rounded" />
130
- <div className="bg-muted mt-7 h-10 w-full animate-pulse rounded-md" />
131
- </div>
132
- </main>
133
- );
134
- }
@@ -1,155 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
-
5
- import { cn } from "./utils";
6
-
7
- /**
8
- * The one-of-N picker seven panels were each drawing by hand.
9
- *
10
- * Not `Tabs`, which is a bottom-bordered strip sitting above the content it
11
- * switches. This is the inline version — a bordered track with the selected
12
- * option filled in — used for a filter or a setting that sits among other
13
- * controls rather than heading a panel.
14
- *
15
- * **Roles are the reason to use this.** The seven copies had three different
16
- * ideas about what they were: one said `tablist`/`tab`, one said
17
- * `group`/`aria-pressed`, and the other five said nothing at all — so on five
18
- * of seven a screen reader heard a row of unlabelled buttons with no
19
- * indication which one was active. None of the three was right. `tablist`
20
- * without a `tabpanel` is a promise the markup does not keep, and
21
- * `aria-pressed` describes independent toggles, not a choice of one. Picking a
22
- * value out of a fixed set is a radio group, so that is what this is, with the
23
- * roving tabindex and arrow keys that come with the role.
24
- *
25
- * `tone` keeps both active colours the panels had drifted into: `emphasis` is
26
- * the near-black/near-white fill five of them used, `primary` the violet
27
- * accent the other two did. They are genuinely different colours rather than
28
- * one spelled two ways, so collapsing them is a visual change to two panels
29
- * and belongs in its own reviewable diff — the same call `DataTable` makes
30
- * about its two header variants.
31
- */
32
- export interface SegmentedOption<Value extends string> {
33
- value: Value;
34
- label: React.ReactNode;
35
- disabled?: boolean;
36
- /**
37
- * Hover text. Worth setting on a disabled option — "Everyone" going grey
38
- * says nothing about which permission would bring it back.
39
- */
40
- title?: string;
41
- }
42
-
43
- export interface SegmentedControlProps<Value extends string> {
44
- options: ReadonlyArray<SegmentedOption<Value>>;
45
- value: Value;
46
- onChange: (value: Value) => void;
47
- /**
48
- * Required: the group never has a visible label — the options name the
49
- * choice between them, which tells you nothing about what is being chosen.
50
- */
51
- ariaLabel: string;
52
- /**
53
- * `contained` — one bordered track around the options (five panels).
54
- * `loose` — free-standing pills with a gap between them (two panels).
55
- */
56
- variant?: "contained" | "loose";
57
- /** See the note above: two real colours, not one spelled twice. */
58
- tone?: "emphasis" | "primary";
59
- /** Options share the width equally, for a control that fills its column. */
60
- fill?: boolean;
61
- className?: string;
62
- }
63
-
64
- const TONES = {
65
- emphasis: {
66
- on: "bg-slate-950 text-white dark:bg-white dark:text-slate-950",
67
- off: "text-muted-foreground hover:bg-muted",
68
- },
69
- primary: {
70
- on: "bg-primary text-primary-foreground",
71
- off: "text-muted-foreground hover:bg-muted",
72
- },
73
- } as const;
74
-
75
- const TRACKS = {
76
- contained: "border-border inline-flex rounded-md border p-0.5",
77
- loose: "flex flex-wrap gap-2",
78
- } as const;
79
-
80
- export function SegmentedControl<Value extends string>({
81
- options,
82
- value,
83
- onChange,
84
- ariaLabel,
85
- variant = "contained",
86
- tone = "emphasis",
87
- fill = false,
88
- className,
89
- }: SegmentedControlProps<Value>) {
90
- const refs = React.useRef(new Map<Value, HTMLButtonElement | null>());
91
-
92
- function onKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
93
- const step =
94
- event.key === "ArrowRight" || event.key === "ArrowDown"
95
- ? 1
96
- : event.key === "ArrowLeft" || event.key === "ArrowUp"
97
- ? -1
98
- : 0;
99
- if (step === 0) return;
100
-
101
- const selectable = options.filter((option) => !option.disabled);
102
- if (selectable.length < 2) return;
103
-
104
- const current = selectable.findIndex((option) => option.value === value);
105
- // Wraps, which is what a radio group is expected to do.
106
- const next =
107
- selectable[(current + step + selectable.length) % selectable.length];
108
-
109
- event.preventDefault();
110
- onChange(next.value);
111
- // The newly checked option has to take focus, or the next arrow press is
112
- // still measured from the old one.
113
- refs.current.get(next.value)?.focus();
114
- }
115
-
116
- return (
117
- <div
118
- role="radiogroup"
119
- aria-label={ariaLabel}
120
- onKeyDown={onKeyDown}
121
- className={cn(TRACKS[variant], className)}
122
- >
123
- {options.map((option) => {
124
- const checked = option.value === value;
125
- return (
126
- <button
127
- key={option.value}
128
- ref={(node) => {
129
- refs.current.set(option.value, node);
130
- }}
131
- type="button"
132
- role="radio"
133
- aria-checked={checked}
134
- disabled={option.disabled}
135
- title={option.title}
136
- // Only the checked option is reachable by Tab; arrows move within
137
- // the group. Without this a keyboard user pages through every
138
- // option to get past the control.
139
- tabIndex={checked ? 0 : -1}
140
- onClick={() => onChange(option.value)}
141
- className={cn(
142
- "cursor-pointer rounded px-3 py-1 text-sm font-medium transition",
143
- "focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none",
144
- "disabled:cursor-not-allowed disabled:opacity-50",
145
- fill && "flex-1",
146
- checked ? TONES[tone].on : TONES[tone].off,
147
- )}
148
- >
149
- {option.label}
150
- </button>
151
- );
152
- })}
153
- </div>
154
- );
155
- }
package/src/select.tsx DELETED
@@ -1,105 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
-
5
- import { Autocomplete, type AutocompleteOption } from "./autocomplete";
6
-
7
- type SelectChange = {
8
- target: { value: string };
9
- currentTarget: { value: string };
10
- };
11
-
12
- export type SelectProps = {
13
- id?: string;
14
- className?: string;
15
- disabled?: boolean;
16
- /** Accessible name when the control is not wrapped in a label. */
17
- "aria-label"?: string;
18
- /** `compact` fits a spreadsheet cell. Typing still filters the list. */
19
- size?: "default" | "compact";
20
- value?: string | number | readonly string[];
21
- placeholder?: string;
22
- children?: React.ReactNode;
23
- onChange?: (event: SelectChange) => void;
24
- };
25
-
26
- function optionText(node: React.ReactNode): string {
27
- if (node == null || typeof node === "boolean") return "";
28
- if (typeof node === "string" || typeof node === "number") return String(node);
29
- if (Array.isArray(node)) return node.map(optionText).join("");
30
- if (React.isValidElement<{ children?: React.ReactNode }>(node)) {
31
- return optionText(node.props.children);
32
- }
33
- return "";
34
- }
35
-
36
- function optionsFromChildren(children: React.ReactNode): AutocompleteOption[] {
37
- const options: AutocompleteOption[] = [];
38
- React.Children.forEach(children, (child) => {
39
- if (!React.isValidElement(child)) return;
40
- if (child.type === React.Fragment) {
41
- options.push(
42
- ...optionsFromChildren(
43
- (child.props as { children?: React.ReactNode }).children,
44
- ),
45
- );
46
- return;
47
- }
48
- const props = child.props as {
49
- value?: string | number;
50
- disabled?: boolean;
51
- children?: React.ReactNode;
52
- };
53
- const value = props.value == null ? "" : String(props.value);
54
- options.push({
55
- value,
56
- label: optionText(props.children).trim() || value,
57
- disabled: Boolean(props.disabled),
58
- });
59
- });
60
- return options;
61
- }
62
-
63
- /**
64
- * The dropdown. It is the searchable autocomplete, not a native `<select>`,
65
- * so a long list can be typed into and an empty list still opens.
66
- */
67
- export function Select({
68
- id,
69
- className,
70
- disabled,
71
- value,
72
- placeholder,
73
- children,
74
- onChange,
75
- size = "default",
76
- "aria-label": ariaLabel,
77
- }: SelectProps) {
78
- const options = optionsFromChildren(children);
79
- const current = Array.isArray(value)
80
- ? String(value[0] ?? "")
81
- : value == null
82
- ? ""
83
- : String(value);
84
- const blank = options.find((option) => option.value === "");
85
-
86
- return (
87
- <Autocomplete
88
- id={id}
89
- className={className}
90
- disabled={Boolean(disabled)}
91
- ariaLabel={ariaLabel}
92
- size={size}
93
- options={options}
94
- value={current}
95
- clearable={false}
96
- placeholder={placeholder ?? blank?.label ?? "Search…"}
97
- onChange={(next) =>
98
- onChange?.({
99
- target: { value: next },
100
- currentTarget: { value: next },
101
- })
102
- }
103
- />
104
- );
105
- }
package/src/separator.tsx DELETED
@@ -1,32 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
-
5
- import { cn } from "./utils";
6
-
7
- export type SeparatorProps = React.HTMLAttributes<HTMLDivElement> & {
8
- orientation?: "horizontal" | "vertical";
9
- decorative?: boolean;
10
- };
11
-
12
- /** Thin rule between sections. */
13
- export const Separator = React.forwardRef<HTMLDivElement, SeparatorProps>(
14
- (
15
- { className, orientation = "horizontal", decorative = true, ...props },
16
- ref,
17
- ) => (
18
- <div
19
- ref={ref}
20
- role={decorative ? "none" : "separator"}
21
- aria-orientation={orientation}
22
- className={cn(
23
- "bg-border shrink-0",
24
- orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
25
- className,
26
- )}
27
- {...props}
28
- />
29
- ),
30
- );
31
-
32
- Separator.displayName = "Separator";
@@ -1,87 +0,0 @@
1
- "use client";
2
-
3
- import { X } from "lucide-react";
4
- import * as React from "react";
5
- import { createPortal } from "react-dom";
6
-
7
- import { Button } from "./button";
8
- import { useDialogLayer } from "./dialogLayer";
9
- import { useHasMounted } from "./modal";
10
- import { cn } from "./utils";
11
-
12
- interface SideDrawerProps {
13
- isOpen: boolean;
14
- title: string;
15
- onClose: () => void;
16
- children: React.ReactNode;
17
- /** Tailwind width class for the panel. Defaults to a reading-width column. */
18
- panelClassName?: string;
19
- }
20
-
21
- /**
22
- * A panel that slides in from the right.
23
- *
24
- * For content that belongs beside the page rather than on top of it — a queue
25
- * you glance at and dismiss, not a decision you have to answer. Unlike
26
- * `Modal`, it does not centre or dim the whole page to a stop: the table
27
- * underneath stays readable, which is the point of looking at both.
28
- */
29
- export function SideDrawer({
30
- isOpen,
31
- title,
32
- onClose,
33
- children,
34
- panelClassName,
35
- }: SideDrawerProps) {
36
- const mounted = useHasMounted();
37
- const headingId = React.useId();
38
- const panelRef = React.useRef<HTMLElement>(null);
39
-
40
- useDialogLayer({
41
- // Gated on `mounted` too: before that the panel is not rendered, so there
42
- // would be nothing to move focus into.
43
- active: isOpen && mounted,
44
- containerRef: panelRef,
45
- onEscape: onClose,
46
- });
47
-
48
- if (!mounted || !isOpen) return null;
49
-
50
- return createPortal(
51
- <>
52
- <div
53
- aria-hidden
54
- onClick={onClose}
55
- className="fixed inset-0 z-[90] bg-slate-950/30"
56
- />
57
- <aside
58
- ref={panelRef}
59
- role="dialog"
60
- aria-modal="true"
61
- aria-labelledby={headingId}
62
- tabIndex={-1}
63
- className={cn(
64
- "border-border bg-background fixed inset-y-0 right-0 z-[95] flex flex-col border-l shadow-2xl outline-none",
65
- panelClassName ?? "w-full max-w-md",
66
- )}
67
- >
68
- <div className="border-border flex items-center justify-between gap-3 border-b px-4 py-3">
69
- <h2 id={headingId} className="truncate text-base font-semibold">
70
- {title}
71
- </h2>
72
- <Button
73
- variant="ghost"
74
- size="icon"
75
- aria-label="Close panel"
76
- onClick={onClose}
77
- >
78
- <X className="h-4 w-4" aria-hidden />
79
- </Button>
80
- </div>
81
-
82
- <div className="min-h-0 flex-1 overflow-y-auto p-4">{children}</div>
83
- </aside>
84
- </>,
85
- document.body,
86
- );
87
- }