@carefully-built/cli 0.1.1 → 0.1.2

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 (213) hide show
  1. package/README.md +101 -80
  2. package/dist/index.mjs +8 -5
  3. package/dist/index.mjs.map +1 -1
  4. package/package.json +3 -3
  5. package/registry/ui/avatar/manifest.json +33 -0
  6. package/registry/ui/avatar/primitives/avatar.tsx +64 -0
  7. package/registry/ui/avatar/utils/cn.ts +6 -0
  8. package/registry/ui/button/manifest.json +24 -5
  9. package/registry/ui/button/utils/cn.ts +6 -0
  10. package/registry/ui/calendar/manifest.json +35 -0
  11. package/registry/ui/calendar/primitives/button.tsx +89 -0
  12. package/registry/ui/calendar/primitives/calendar.tsx +68 -0
  13. package/registry/ui/calendar/utils/cn.ts +6 -0
  14. package/registry/ui/card/manifest.json +36 -0
  15. package/registry/ui/card/primitives/card.tsx +80 -0
  16. package/registry/ui/card/utils/cn.ts +6 -0
  17. package/registry/ui/chip/manifest.json +36 -0
  18. package/registry/ui/chip/primitives/chip-utils.ts +10 -0
  19. package/registry/ui/chip/primitives/chip.tsx +74 -0
  20. package/registry/ui/chip/utils/cn.ts +6 -0
  21. package/registry/ui/chip-utils/manifest.json +33 -0
  22. package/registry/ui/chip-utils/primitives/chip-utils.ts +10 -0
  23. package/registry/ui/chip-utils/utils/cn.ts +6 -0
  24. package/registry/ui/date-display/manifest.json +33 -0
  25. package/registry/ui/date-display/utils/cn.ts +6 -0
  26. package/registry/ui/date-display/utils/date-display.ts +61 -0
  27. package/registry/ui/dialog/manifest.json +43 -0
  28. package/registry/ui/dialog/primitives/button.tsx +89 -0
  29. package/registry/ui/dialog/primitives/dialog.tsx +147 -0
  30. package/registry/ui/dialog/utils/cn.ts +6 -0
  31. package/registry/ui/display-date/manifest.json +36 -0
  32. package/registry/ui/display-date/primitives/display-date.tsx +20 -0
  33. package/registry/ui/display-date/utils/cn.ts +6 -0
  34. package/registry/ui/display-date/utils/date-display.ts +61 -0
  35. package/registry/ui/drawer/manifest.json +37 -0
  36. package/registry/ui/drawer/primitives/drawer.tsx +99 -0
  37. package/registry/ui/drawer/utils/cn.ts +6 -0
  38. package/registry/ui/dropdown-menu/manifest.json +37 -0
  39. package/registry/ui/dropdown-menu/primitives/dropdown-menu.tsx +140 -0
  40. package/registry/ui/dropdown-menu/utils/cn.ts +6 -0
  41. package/registry/ui/empty-state/empty-state/collection-empty-state.ts +29 -0
  42. package/registry/ui/empty-state/empty-state/empty-state-card.tsx +72 -0
  43. package/registry/ui/empty-state/empty-state/index.ts +8 -0
  44. package/registry/ui/empty-state/empty-state/initial-empty-state.tsx +36 -0
  45. package/registry/ui/empty-state/empty-state/no-results-state.tsx +20 -0
  46. package/registry/ui/empty-state/manifest.json +63 -0
  47. package/registry/ui/empty-state/primitives/button.tsx +89 -0
  48. package/registry/ui/empty-state/primitives/card.tsx +80 -0
  49. package/registry/ui/empty-state/utils/cn.ts +6 -0
  50. package/registry/ui/error-page/error-page/error-code.tsx +16 -0
  51. package/registry/ui/error-page/error-page/error-page-content.ts +75 -0
  52. package/registry/ui/error-page/error-page/index.ts +19 -0
  53. package/registry/ui/error-page/error-page/posthog-error-capture.ts +83 -0
  54. package/registry/ui/error-page/error-page/saas-error-page.tsx +146 -0
  55. package/registry/ui/error-page/manifest.json +64 -0
  56. package/registry/ui/error-page/primitives/button.tsx +89 -0
  57. package/registry/ui/error-page/utils/cn.ts +6 -0
  58. package/registry/ui/field-detail-row/manifest.json +32 -0
  59. package/registry/ui/field-detail-row/primitives/field-detail-row.tsx +28 -0
  60. package/registry/ui/field-detail-row/utils/cn.ts +6 -0
  61. package/registry/ui/file-dropzone/manifest.json +35 -0
  62. package/registry/ui/file-dropzone/primitives/button.tsx +89 -0
  63. package/registry/ui/file-dropzone/primitives/file-dropzone.tsx +236 -0
  64. package/registry/ui/file-dropzone/utils/cn.ts +6 -0
  65. package/registry/ui/help-info-button/manifest.json +72 -0
  66. package/registry/ui/help-info-button/overlays/responsive-sheet.footer.tsx +88 -0
  67. package/registry/ui/help-info-button/overlays/responsive-sheet.layouts.tsx +207 -0
  68. package/registry/ui/help-info-button/overlays/responsive-sheet.shortcuts.ts +103 -0
  69. package/registry/ui/help-info-button/overlays/responsive-sheet.tsx +132 -0
  70. package/registry/ui/help-info-button/primitives/button.tsx +89 -0
  71. package/registry/ui/help-info-button/primitives/drawer.tsx +99 -0
  72. package/registry/ui/help-info-button/primitives/help-info-button.tsx +63 -0
  73. package/registry/ui/help-info-button/primitives/keyboard-shortcut-hint.tsx +40 -0
  74. package/registry/ui/help-info-button/primitives/sheet.tsx +103 -0
  75. package/registry/ui/help-info-button/primitives/tooltip.tsx +57 -0
  76. package/registry/ui/help-info-button/utils/cn.ts +6 -0
  77. package/registry/ui/help-info-button/utils/use-media-query.ts +28 -0
  78. package/registry/ui/input/manifest.json +31 -0
  79. package/registry/ui/input/primitives/input.tsx +19 -0
  80. package/registry/ui/input/utils/cn.ts +6 -0
  81. package/registry/ui/keyboard-shortcut-hint/manifest.json +32 -0
  82. package/registry/ui/keyboard-shortcut-hint/primitives/keyboard-shortcut-hint.tsx +40 -0
  83. package/registry/ui/keyboard-shortcut-hint/utils/cn.ts +6 -0
  84. package/registry/ui/label/manifest.json +31 -0
  85. package/registry/ui/label/primitives/label.tsx +21 -0
  86. package/registry/ui/label/utils/cn.ts +6 -0
  87. package/registry/ui/pagination/manifest.json +36 -0
  88. package/registry/ui/pagination/primitives/button.tsx +89 -0
  89. package/registry/ui/pagination/primitives/pagination.tsx +143 -0
  90. package/registry/ui/pagination/utils/cn.ts +6 -0
  91. package/registry/ui/popover/manifest.json +33 -0
  92. package/registry/ui/popover/primitives/popover.tsx +46 -0
  93. package/registry/ui/popover/utils/cn.ts +6 -0
  94. package/registry/ui/responsive-sheet/manifest.json +66 -0
  95. package/registry/ui/responsive-sheet/overlays/responsive-sheet.footer.tsx +88 -0
  96. package/registry/ui/responsive-sheet/overlays/responsive-sheet.layouts.tsx +207 -0
  97. package/registry/ui/responsive-sheet/overlays/responsive-sheet.shortcuts.ts +103 -0
  98. package/registry/ui/responsive-sheet/overlays/responsive-sheet.tsx +132 -0
  99. package/registry/ui/responsive-sheet/primitives/button.tsx +89 -0
  100. package/registry/ui/responsive-sheet/primitives/drawer.tsx +99 -0
  101. package/registry/ui/responsive-sheet/primitives/keyboard-shortcut-hint.tsx +40 -0
  102. package/registry/ui/responsive-sheet/primitives/sheet.tsx +103 -0
  103. package/registry/ui/responsive-sheet/utils/cn.ts +6 -0
  104. package/registry/ui/responsive-sheet/utils/use-media-query.ts +28 -0
  105. package/registry/ui/responsive-sheet.footer/manifest.json +40 -0
  106. package/registry/ui/responsive-sheet.footer/overlays/responsive-sheet.footer.tsx +88 -0
  107. package/registry/ui/responsive-sheet.footer/primitives/button.tsx +89 -0
  108. package/registry/ui/responsive-sheet.footer/primitives/keyboard-shortcut-hint.tsx +40 -0
  109. package/registry/ui/responsive-sheet.footer/utils/cn.ts +6 -0
  110. package/registry/ui/responsive-sheet.shortcuts/manifest.json +34 -0
  111. package/registry/ui/responsive-sheet.shortcuts/overlays/responsive-sheet.shortcuts.ts +103 -0
  112. package/registry/ui/responsive-sheet.shortcuts/utils/cn.ts +6 -0
  113. package/registry/ui/scroll-fade-area/manifest.json +31 -0
  114. package/registry/ui/scroll-fade-area/primitives/scroll-fade-area.tsx +295 -0
  115. package/registry/ui/scroll-fade-area/utils/cn.ts +6 -0
  116. package/registry/ui/search/manifest.json +35 -0
  117. package/registry/ui/search/utils/cn.ts +6 -0
  118. package/registry/ui/search/utils/search.ts +227 -0
  119. package/registry/ui/searchable-select/manifest.json +48 -0
  120. package/registry/ui/searchable-select/primitives/input.tsx +19 -0
  121. package/registry/ui/searchable-select/search/searchable-select-position.ts +95 -0
  122. package/registry/ui/searchable-select/search/searchable-select.tsx +431 -0
  123. package/registry/ui/searchable-select/utils/cn.ts +6 -0
  124. package/registry/ui/searchable-select/utils/search.ts +227 -0
  125. package/registry/ui/searchable-select-position/manifest.json +32 -0
  126. package/registry/ui/searchable-select-position/search/searchable-select-position.ts +95 -0
  127. package/registry/ui/searchable-select-position/utils/cn.ts +6 -0
  128. package/registry/ui/segmented-toggle/manifest.json +41 -0
  129. package/registry/ui/segmented-toggle/primitives/scroll-fade-area.tsx +295 -0
  130. package/registry/ui/segmented-toggle/primitives/segmented-toggle.tsx +106 -0
  131. package/registry/ui/segmented-toggle/primitives/tabs.tsx +97 -0
  132. package/registry/ui/segmented-toggle/utils/cn.ts +6 -0
  133. package/registry/ui/select/manifest.json +37 -0
  134. package/registry/ui/select/primitives/select.tsx +142 -0
  135. package/registry/ui/select/utils/cn.ts +6 -0
  136. package/registry/ui/sheet/manifest.json +39 -0
  137. package/registry/ui/sheet/primitives/button.tsx +89 -0
  138. package/registry/ui/sheet/primitives/sheet.tsx +103 -0
  139. package/registry/ui/sheet/utils/cn.ts +6 -0
  140. package/registry/ui/skeleton/manifest.json +31 -0
  141. package/registry/ui/skeleton/primitives/skeleton.tsx +13 -0
  142. package/registry/ui/skeleton/utils/cn.ts +6 -0
  143. package/registry/ui/smart-table/manifest.json +115 -0
  144. package/registry/ui/smart-table/primitives/button.tsx +89 -0
  145. package/registry/ui/smart-table/primitives/card.tsx +80 -0
  146. package/registry/ui/smart-table/primitives/display-date.tsx +20 -0
  147. package/registry/ui/smart-table/primitives/pagination.tsx +143 -0
  148. package/registry/ui/smart-table/primitives/skeleton.tsx +13 -0
  149. package/registry/ui/smart-table/primitives/table.tsx +92 -0
  150. package/registry/ui/smart-table/primitives/tooltip.tsx +57 -0
  151. package/registry/ui/smart-table/smart-table/DesktopView.tsx +343 -0
  152. package/registry/ui/smart-table/smart-table/MobileView.tsx +170 -0
  153. package/registry/ui/smart-table/smart-table/SmartTable.tsx +85 -0
  154. package/registry/ui/smart-table/smart-table/SmartTableActions.tsx +71 -0
  155. package/registry/ui/smart-table/smart-table/TruncatedContent.tsx +147 -0
  156. package/registry/ui/smart-table/smart-table/index.ts +15 -0
  157. package/registry/ui/smart-table/smart-table/sorting.ts +148 -0
  158. package/registry/ui/smart-table/smart-table/truncated-content.utils.ts +22 -0
  159. package/registry/ui/smart-table/smart-table/types.ts +95 -0
  160. package/registry/ui/smart-table/smart-table/utils.ts +150 -0
  161. package/registry/ui/smart-table/utils/cn.ts +6 -0
  162. package/registry/ui/smart-table/utils/date-display.ts +61 -0
  163. package/registry/ui/smart-table/utils/use-media-query.ts +28 -0
  164. package/registry/ui/switch/manifest.json +31 -0
  165. package/registry/ui/switch/primitives/switch.tsx +31 -0
  166. package/registry/ui/switch/utils/cn.ts +6 -0
  167. package/registry/ui/table/manifest.json +38 -0
  168. package/registry/ui/table/primitives/table.tsx +92 -0
  169. package/registry/ui/table/utils/cn.ts +6 -0
  170. package/registry/ui/table-toolbar/manifest.json +93 -0
  171. package/registry/ui/table-toolbar/overlays/responsive-sheet.footer.tsx +88 -0
  172. package/registry/ui/table-toolbar/overlays/responsive-sheet.layouts.tsx +207 -0
  173. package/registry/ui/table-toolbar/overlays/responsive-sheet.shortcuts.ts +103 -0
  174. package/registry/ui/table-toolbar/overlays/responsive-sheet.tsx +132 -0
  175. package/registry/ui/table-toolbar/primitives/button.tsx +89 -0
  176. package/registry/ui/table-toolbar/primitives/drawer.tsx +99 -0
  177. package/registry/ui/table-toolbar/primitives/input.tsx +19 -0
  178. package/registry/ui/table-toolbar/primitives/keyboard-shortcut-hint.tsx +40 -0
  179. package/registry/ui/table-toolbar/primitives/sheet.tsx +103 -0
  180. package/registry/ui/table-toolbar/search/searchable-select-position.ts +95 -0
  181. package/registry/ui/table-toolbar/search/searchable-select.tsx +431 -0
  182. package/registry/ui/table-toolbar/table-toolbar/index.ts +9 -0
  183. package/registry/ui/table-toolbar/table-toolbar/table-toolbar.tsx +552 -0
  184. package/registry/ui/table-toolbar/utils/cn.ts +6 -0
  185. package/registry/ui/table-toolbar/utils/search.ts +227 -0
  186. package/registry/ui/table-toolbar/utils/use-media-query.ts +28 -0
  187. package/registry/ui/tabs/manifest.json +40 -0
  188. package/registry/ui/tabs/primitives/scroll-fade-area.tsx +295 -0
  189. package/registry/ui/tabs/primitives/tabs.tsx +97 -0
  190. package/registry/ui/tabs/utils/cn.ts +6 -0
  191. package/registry/ui/textarea/manifest.json +31 -0
  192. package/registry/ui/textarea/primitives/textarea.tsx +18 -0
  193. package/registry/ui/textarea/utils/cn.ts +6 -0
  194. package/registry/ui/tooltip/manifest.json +34 -0
  195. package/registry/ui/tooltip/primitives/tooltip.tsx +57 -0
  196. package/registry/ui/tooltip/utils/cn.ts +6 -0
  197. package/registry/ui/use-media-query/manifest.json +32 -0
  198. package/registry/ui/use-media-query/utils/cn.ts +6 -0
  199. package/registry/ui/use-media-query/utils/use-media-query.ts +28 -0
  200. package/registry/ui/user-picker/manifest.json +52 -0
  201. package/registry/ui/user-picker/primitives/avatar.tsx +64 -0
  202. package/registry/ui/user-picker/primitives/button.tsx +89 -0
  203. package/registry/ui/user-picker/primitives/input.tsx +19 -0
  204. package/registry/ui/user-picker/primitives/popover.tsx +46 -0
  205. package/registry/ui/user-picker/primitives/user-picker-utils.ts +113 -0
  206. package/registry/ui/user-picker/primitives/user-picker.tsx +226 -0
  207. package/registry/ui/user-picker/utils/cn.ts +6 -0
  208. package/registry/ui/user-picker-utils/manifest.json +38 -0
  209. package/registry/ui/user-picker-utils/primitives/user-picker-utils.ts +113 -0
  210. package/registry/ui/user-picker-utils/utils/cn.ts +6 -0
  211. package/assets/hero.png +0 -0
  212. package/registry/ui/button/cn.ts +0 -6
  213. /package/registry/ui/button/{button.tsx → primitives/button.tsx} +0 -0
@@ -0,0 +1,31 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Switch as SwitchPrimitive } from "radix-ui"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ function Switch({
9
+ className,
10
+ ...props
11
+ }: React.ComponentProps<typeof SwitchPrimitive.Root>) {
12
+ return (
13
+ <SwitchPrimitive.Root
14
+ data-slot="switch"
15
+ className={cn(
16
+ "peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input inline-flex h-6 w-11 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50",
17
+ className
18
+ )}
19
+ {...props}
20
+ >
21
+ <SwitchPrimitive.Thumb
22
+ data-slot="switch-thumb"
23
+ className={cn(
24
+ "bg-background pointer-events-none block size-5 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[22px] data-[state=unchecked]:translate-x-0.5"
25
+ )}
26
+ />
27
+ </SwitchPrimitive.Root>
28
+ )
29
+ }
30
+
31
+ export { Switch }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]): string {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "table",
3
+ "description": "Editable source registry entry for table.",
4
+ "importPath": "@carefully-built/ui",
5
+ "exports": [
6
+ "Table",
7
+ "TableBody",
8
+ "TableCaption",
9
+ "TableCell",
10
+ "TableFooter",
11
+ "TableHead",
12
+ "TableHeader",
13
+ "TableRow"
14
+ ],
15
+ "dependencies": [
16
+ "class-variance-authority",
17
+ "clsx",
18
+ "tailwind-merge"
19
+ ],
20
+ "peerDependencies": [
21
+ "react",
22
+ "react-dom",
23
+ "radix-ui",
24
+ "lucide-react",
25
+ "react-day-picker",
26
+ "vaul"
27
+ ],
28
+ "files": [
29
+ {
30
+ "source": "primitives/table.tsx",
31
+ "target": "components/ui/table.tsx"
32
+ },
33
+ {
34
+ "source": "utils/cn.ts",
35
+ "target": "lib/utils.ts"
36
+ }
37
+ ]
38
+ }
@@ -0,0 +1,92 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+
5
+ import { cn } from '@/lib/utils';
6
+
7
+ function Table({ className, ...props }: React.ComponentProps<'table'>) {
8
+ return (
9
+ <div data-slot="table-container" className="relative w-full min-w-0 overflow-x-auto">
10
+ <table
11
+ data-slot="table"
12
+ className={cn('w-full min-w-0 caption-bottom text-sm', className)}
13
+ {...props}
14
+ />
15
+ </div>
16
+ );
17
+ }
18
+
19
+ function TableHeader({ className, ...props }: React.ComponentProps<'thead'>) {
20
+ return <thead data-slot="table-header" className={cn('[&_tr]:border-b', className)} {...props} />;
21
+ }
22
+
23
+ function TableBody({ className, ...props }: React.ComponentProps<'tbody'>) {
24
+ return (
25
+ <tbody
26
+ data-slot="table-body"
27
+ className={cn('[&_tr:last-child]:border-0', className)}
28
+ {...props}
29
+ />
30
+ );
31
+ }
32
+
33
+ function TableFooter({ className, ...props }: React.ComponentProps<'tfoot'>) {
34
+ return (
35
+ <tfoot
36
+ data-slot="table-footer"
37
+ className={cn('bg-muted/50 border-t font-medium [&>tr]:last:border-b-0', className)}
38
+ {...props}
39
+ />
40
+ );
41
+ }
42
+
43
+ function TableRow({ className, ...props }: React.ComponentProps<'tr'>) {
44
+ return (
45
+ <tr
46
+ data-slot="table-row"
47
+ className={cn(
48
+ 'hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors',
49
+ className,
50
+ )}
51
+ {...props}
52
+ />
53
+ );
54
+ }
55
+
56
+ function TableHead({ className, ...props }: React.ComponentProps<'th'>) {
57
+ return (
58
+ <th
59
+ data-slot="table-head"
60
+ className={cn(
61
+ 'text-foreground h-10 overflow-hidden px-2 text-left align-middle font-medium text-ellipsis whitespace-nowrap [&:has([role=checkbox])]:pr-0',
62
+ className,
63
+ )}
64
+ {...props}
65
+ />
66
+ );
67
+ }
68
+
69
+ function TableCell({ className, ...props }: React.ComponentProps<'td'>) {
70
+ return (
71
+ <td
72
+ data-slot="table-cell"
73
+ className={cn(
74
+ 'overflow-hidden p-2 align-middle text-ellipsis whitespace-nowrap [&:has([role=checkbox])]:pr-0',
75
+ className,
76
+ )}
77
+ {...props}
78
+ />
79
+ );
80
+ }
81
+
82
+ function TableCaption({ className, ...props }: React.ComponentProps<'caption'>) {
83
+ return (
84
+ <caption
85
+ data-slot="table-caption"
86
+ className={cn('text-muted-foreground mt-4 text-sm', className)}
87
+ {...props}
88
+ />
89
+ );
90
+ }
91
+
92
+ export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption };
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]): string {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -0,0 +1,93 @@
1
+ {
2
+ "name": "table-toolbar",
3
+ "description": "Editable source registry entry for table-toolbar.",
4
+ "importPath": "@carefully-built/ui",
5
+ "exports": [
6
+ "CustomTableToolbarFilter",
7
+ "FilterConfig",
8
+ "FilterDropdown",
9
+ "FilterOption",
10
+ "SearchInput",
11
+ "TableToolbar",
12
+ "TableToolbarProps"
13
+ ],
14
+ "dependencies": [
15
+ "class-variance-authority",
16
+ "clsx",
17
+ "tailwind-merge"
18
+ ],
19
+ "peerDependencies": [
20
+ "react",
21
+ "react-dom",
22
+ "radix-ui",
23
+ "lucide-react",
24
+ "react-day-picker",
25
+ "vaul"
26
+ ],
27
+ "files": [
28
+ {
29
+ "source": "overlays/responsive-sheet.footer.tsx",
30
+ "target": "components/ui/responsive-sheet.footer.tsx"
31
+ },
32
+ {
33
+ "source": "overlays/responsive-sheet.layouts.tsx",
34
+ "target": "components/ui/responsive-sheet.layouts.tsx"
35
+ },
36
+ {
37
+ "source": "overlays/responsive-sheet.shortcuts.ts",
38
+ "target": "components/ui/responsive-sheet.shortcuts.ts"
39
+ },
40
+ {
41
+ "source": "overlays/responsive-sheet.tsx",
42
+ "target": "components/ui/responsive-sheet.tsx"
43
+ },
44
+ {
45
+ "source": "primitives/button.tsx",
46
+ "target": "components/ui/button.tsx"
47
+ },
48
+ {
49
+ "source": "primitives/drawer.tsx",
50
+ "target": "components/ui/drawer.tsx"
51
+ },
52
+ {
53
+ "source": "primitives/input.tsx",
54
+ "target": "components/ui/input.tsx"
55
+ },
56
+ {
57
+ "source": "primitives/keyboard-shortcut-hint.tsx",
58
+ "target": "components/ui/keyboard-shortcut-hint.tsx"
59
+ },
60
+ {
61
+ "source": "primitives/sheet.tsx",
62
+ "target": "components/ui/sheet.tsx"
63
+ },
64
+ {
65
+ "source": "search/searchable-select-position.ts",
66
+ "target": "components/ui/search/searchable-select-position.ts"
67
+ },
68
+ {
69
+ "source": "search/searchable-select.tsx",
70
+ "target": "components/ui/search/searchable-select.tsx"
71
+ },
72
+ {
73
+ "source": "table-toolbar/index.ts",
74
+ "target": "components/ui/table-toolbar/index.ts"
75
+ },
76
+ {
77
+ "source": "table-toolbar/table-toolbar.tsx",
78
+ "target": "components/ui/table-toolbar/table-toolbar.tsx"
79
+ },
80
+ {
81
+ "source": "utils/cn.ts",
82
+ "target": "lib/utils.ts"
83
+ },
84
+ {
85
+ "source": "utils/search.ts",
86
+ "target": "components/ui/search.ts"
87
+ },
88
+ {
89
+ "source": "utils/use-media-query.ts",
90
+ "target": "components/ui/use-media-query.ts"
91
+ }
92
+ ]
93
+ }
@@ -0,0 +1,88 @@
1
+ 'use client';
2
+
3
+ import { CornerDownLeft } from 'lucide-react';
4
+
5
+ import type { ReactNode } from 'react';
6
+
7
+ import { Button } from '@/components/ui/button';
8
+ import { KeyboardKeycap, ShortcutModifierKeycap } from '@/components/ui/keyboard-shortcut-hint';
9
+
10
+ export function DesktopConfirmShortcutHint({
11
+ desktopModifierLabel,
12
+ }: {
13
+ readonly desktopModifierLabel: string;
14
+ }): React.ReactElement {
15
+ const keycapClassName = 'border-primary-foreground/20 bg-primary-foreground/10';
16
+
17
+ return (
18
+ <span className="text-primary-foreground/70 inline-flex items-center gap-1">
19
+ <ShortcutModifierKeycap modifierLabel={desktopModifierLabel} className={keycapClassName} />
20
+ <KeyboardKeycap className={keycapClassName}>
21
+ <CornerDownLeft className="size-[10px]" />
22
+ </KeyboardKeycap>
23
+ </span>
24
+ );
25
+ }
26
+
27
+ interface SheetActionFooterProps {
28
+ readonly footer?: ReactNode;
29
+ readonly onCancel?: () => void;
30
+ readonly cancelLabel: ReactNode;
31
+ readonly onConfirm?: () => void;
32
+ readonly confirmLabel: ReactNode;
33
+ readonly confirmDisabled: boolean;
34
+ readonly confirmLoading: boolean;
35
+ readonly desktopConfirmShortcutEnabled?: boolean;
36
+ readonly desktopModifierLabel?: string | null;
37
+ }
38
+
39
+ export function SheetActionFooter({
40
+ footer,
41
+ onCancel,
42
+ cancelLabel,
43
+ onConfirm,
44
+ confirmLabel,
45
+ confirmDisabled,
46
+ confirmLoading,
47
+ desktopConfirmShortcutEnabled = false,
48
+ desktopModifierLabel = null,
49
+ }: SheetActionFooterProps): React.ReactNode {
50
+ if (footer) return footer;
51
+
52
+ if (!onCancel && !onConfirm) return null;
53
+
54
+ const actionCount = Number(Boolean(onCancel)) + Number(Boolean(onConfirm));
55
+ const footerButtonClassName = actionCount === 1 ? 'w-full' : 'min-w-0 flex-1';
56
+
57
+ return (
58
+ <div className="flex w-full flex-nowrap items-center gap-2">
59
+ {onCancel ? (
60
+ <Button
61
+ type="button"
62
+ variant="outline"
63
+ onClick={onCancel}
64
+ className={footerButtonClassName}
65
+ >
66
+ {cancelLabel}
67
+ </Button>
68
+ ) : null}
69
+ {onConfirm ? (
70
+ <Button
71
+ type="button"
72
+ onClick={onConfirm}
73
+ disabled={confirmDisabled}
74
+ className={`${footerButtonClassName} relative`}
75
+ >
76
+ <span className="inline-flex w-full items-center justify-center">
77
+ <span>{confirmLoading ? 'Saving...' : confirmLabel}</span>
78
+ {desktopConfirmShortcutEnabled && desktopModifierLabel ? (
79
+ <span className="absolute top-1/2 right-2 -translate-y-1/2">
80
+ <DesktopConfirmShortcutHint desktopModifierLabel={desktopModifierLabel} />
81
+ </span>
82
+ ) : null}
83
+ </span>
84
+ </Button>
85
+ ) : null}
86
+ </div>
87
+ );
88
+ }
@@ -0,0 +1,207 @@
1
+ 'use client';
2
+
3
+ import type { ReactNode } from 'react';
4
+
5
+ import type { SheetOutsideInteractionGuard } from '@/components/ui/responsive-sheet';
6
+ import type { ResponsiveSheetClassNames } from '@/components/ui/responsive-sheet';
7
+
8
+ import {
9
+ Drawer,
10
+ DrawerContent,
11
+ DrawerDescription,
12
+ DrawerHeader,
13
+ DrawerTitle,
14
+ } from '@/components/ui/drawer';
15
+ import {
16
+ Sheet,
17
+ SheetContent,
18
+ SheetDescription,
19
+ SheetHeader,
20
+ SheetTitle,
21
+ } from '@/components/ui/sheet';
22
+ import { cn } from '@/lib/utils';
23
+
24
+ interface SheetDescriptionBlockProps {
25
+ readonly title: ReactNode;
26
+ readonly description?: ReactNode;
27
+ readonly classes?: ResponsiveSheetClassNames;
28
+ }
29
+
30
+ function SheetDescriptionBlock({
31
+ title,
32
+ description,
33
+ classes,
34
+ }: SheetDescriptionBlockProps): React.ReactElement {
35
+ return (
36
+ <>
37
+ <SheetTitle className={classes?.title}>{title}</SheetTitle>
38
+ {description ? (
39
+ <SheetDescription className={classes?.description}>{description}</SheetDescription>
40
+ ) : null}
41
+ </>
42
+ );
43
+ }
44
+
45
+ interface SharedSheetLayoutProps {
46
+ readonly open: boolean;
47
+ readonly onOpenChange: (open: boolean) => void;
48
+ readonly modal: boolean;
49
+ readonly outsideInteractionGuard?: SheetOutsideInteractionGuard;
50
+ readonly title: ReactNode;
51
+ readonly description?: ReactNode;
52
+ readonly children: ReactNode;
53
+ readonly footer: ReactNode;
54
+ readonly mobileDrawerContentClassName?: string;
55
+ readonly contentClassName?: string;
56
+ readonly footerClassName?: string;
57
+ readonly classes?: ResponsiveSheetClassNames;
58
+ }
59
+
60
+ function shouldPreventOutsideInteraction(
61
+ target: EventTarget | null,
62
+ guard?: SheetOutsideInteractionGuard,
63
+ ): boolean {
64
+ const element =
65
+ target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
66
+
67
+ if (!element) {
68
+ return false;
69
+ }
70
+
71
+ if (element.closest('[data-searchable-select-content]')) {
72
+ return true;
73
+ }
74
+
75
+ return guard?.selectors.some((selector) => element.closest(selector)) ?? false;
76
+ }
77
+
78
+ type MobileSheetLayoutProps = SharedSheetLayoutProps;
79
+
80
+ export function MobileSheetLayout({
81
+ open,
82
+ onOpenChange,
83
+ modal,
84
+ outsideInteractionGuard,
85
+ title,
86
+ description,
87
+ children,
88
+ footer,
89
+ mobileDrawerContentClassName,
90
+ contentClassName,
91
+ footerClassName,
92
+ classes,
93
+ }: MobileSheetLayoutProps): React.ReactElement {
94
+ return (
95
+ <Drawer open={open} onOpenChange={onOpenChange} modal={modal}>
96
+ <DrawerContent
97
+ aria-describedby={description ? undefined : 'responsive-sheet-description-empty'}
98
+ className={cn(
99
+ 'px-4 pb-[calc(env(safe-area-inset-bottom)+20px)]',
100
+ mobileDrawerContentClassName,
101
+ classes?.mobileContent,
102
+ )}
103
+ onInteractOutside={(event) => {
104
+ if (shouldPreventOutsideInteraction(event.target, outsideInteractionGuard)) {
105
+ event.preventDefault();
106
+ }
107
+ }}
108
+ onPointerDownOutside={(event) => {
109
+ if (shouldPreventOutsideInteraction(event.target, outsideInteractionGuard)) {
110
+ event.preventDefault();
111
+ }
112
+ }}
113
+ >
114
+ <DrawerHeader className={cn('px-0 pb-4', classes?.header)}>
115
+ <DrawerTitle className={classes?.title}>{title}</DrawerTitle>
116
+ {description ? (
117
+ <DrawerDescription className={classes?.description}>{description}</DrawerDescription>
118
+ ) : (
119
+ <DrawerDescription id="responsive-sheet-description-empty" className="sr-only">
120
+ Dialog
121
+ </DrawerDescription>
122
+ )}
123
+ </DrawerHeader>
124
+ <div className="flex min-h-0 flex-1 flex-col gap-4">
125
+ <div
126
+ className={cn(
127
+ '-mx-1 flex-1 overflow-y-auto px-1 pb-3 [scrollbar-gutter:stable]',
128
+ contentClassName,
129
+ classes?.body,
130
+ )}
131
+ >
132
+ {children}
133
+ </div>
134
+ {footer ? (
135
+ <div className={cn('shrink-0 border-t pt-4 pb-3', footerClassName, classes?.footer)}>
136
+ {footer}
137
+ </div>
138
+ ) : null}
139
+ </div>
140
+ </DrawerContent>
141
+ </Drawer>
142
+ );
143
+ }
144
+
145
+ interface DesktopSheetLayoutProps extends SharedSheetLayoutProps {
146
+ readonly width: number;
147
+ }
148
+
149
+ export function DesktopSheetLayout({
150
+ open,
151
+ onOpenChange,
152
+ modal,
153
+ outsideInteractionGuard,
154
+ title,
155
+ description,
156
+ children,
157
+ footer,
158
+ width,
159
+ contentClassName,
160
+ footerClassName,
161
+ classes,
162
+ }: DesktopSheetLayoutProps): React.ReactElement {
163
+ return (
164
+ <Sheet open={open} onOpenChange={onOpenChange} modal={modal}>
165
+ <SheetContent
166
+ aria-describedby={description ? undefined : 'responsive-sheet-description-empty'}
167
+ style={{ width: `${String(width)}px`, maxWidth: '85vw' }}
168
+ className={cn('flex flex-col gap-0 p-0', classes?.desktopContent)}
169
+ onInteractOutside={(event) => {
170
+ if (shouldPreventOutsideInteraction(event.target, outsideInteractionGuard)) {
171
+ event.preventDefault();
172
+ }
173
+ }}
174
+ onPointerDownOutside={(event) => {
175
+ if (shouldPreventOutsideInteraction(event.target, outsideInteractionGuard)) {
176
+ event.preventDefault();
177
+ }
178
+ }}
179
+ >
180
+ <SheetHeader className={cn('border-b px-4 py-4', classes?.header)}>
181
+ <SheetDescriptionBlock title={title} description={description} classes={classes} />
182
+ </SheetHeader>
183
+ {description ? null : (
184
+ <SheetDescription id="responsive-sheet-description-empty" className="sr-only">
185
+ Dialog
186
+ </SheetDescription>
187
+ )}
188
+ <div className="flex min-h-0 flex-1 flex-col">
189
+ <div
190
+ className={cn(
191
+ 'flex-1 overflow-x-visible overflow-y-auto px-4 pt-4 pb-6 [scrollbar-gutter:stable]',
192
+ contentClassName,
193
+ classes?.body,
194
+ )}
195
+ >
196
+ {children}
197
+ </div>
198
+ {footer ? (
199
+ <div className={cn('border-t px-4 py-4', footerClassName, classes?.footer)}>
200
+ {footer}
201
+ </div>
202
+ ) : null}
203
+ </div>
204
+ </SheetContent>
205
+ </Sheet>
206
+ );
207
+ }
@@ -0,0 +1,103 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ interface NavigatorWithUserAgentData extends Navigator {
4
+ readonly userAgentData?: {
5
+ readonly platform?: string;
6
+ };
7
+ }
8
+
9
+ export function getDesktopShortcutModifierLabel(): string {
10
+ if (typeof navigator === "undefined") {
11
+ return "Ctrl";
12
+ }
13
+
14
+ const navigatorWithUserAgentData = navigator as NavigatorWithUserAgentData;
15
+ const platform =
16
+ navigatorWithUserAgentData.userAgentData?.platform ?? navigator.userAgent;
17
+
18
+ return /Mac|iPhone|iPad|iPod/i.test(platform) ? "Cmd" : "Ctrl";
19
+ }
20
+
21
+ export function isAllowedConfirmShortcutEvent(
22
+ event: KeyboardEvent,
23
+ desktopModifierLabel: string,
24
+ ): boolean {
25
+ if (event.key !== "Enter" || event.repeat || event.isComposing) {
26
+ return false;
27
+ }
28
+
29
+ const expectsMetaKey = desktopModifierLabel === "Cmd";
30
+ const usedExpectedModifier = expectsMetaKey ? event.metaKey : event.ctrlKey;
31
+ const usedOtherModifier = expectsMetaKey ? event.ctrlKey : event.metaKey;
32
+ const usedShiftModifier = event.shiftKey;
33
+ const usedAltModifier = event.altKey;
34
+
35
+ if (
36
+ !usedExpectedModifier ||
37
+ usedOtherModifier ||
38
+ usedShiftModifier ||
39
+ usedAltModifier
40
+ ) {
41
+ return false;
42
+ }
43
+
44
+ return true;
45
+ }
46
+
47
+ export function useDesktopShortcutModifierLabel(
48
+ enabled: boolean,
49
+ ): string | null {
50
+ const [desktopModifierLabel, setDesktopModifierLabel] = useState<
51
+ string | null
52
+ >(null);
53
+
54
+ useEffect(() => {
55
+ if (!enabled) {
56
+ setDesktopModifierLabel(null);
57
+ return;
58
+ }
59
+
60
+ setDesktopModifierLabel(getDesktopShortcutModifierLabel());
61
+ }, [enabled]);
62
+
63
+ return desktopModifierLabel;
64
+ }
65
+
66
+ interface UseDesktopConfirmShortcutOptions {
67
+ readonly open: boolean;
68
+ readonly enabled: boolean;
69
+ readonly confirmDisabled: boolean;
70
+ readonly confirmLoading: boolean;
71
+ readonly onConfirm?: () => void;
72
+ }
73
+
74
+ export function useDesktopConfirmShortcut({
75
+ open,
76
+ enabled,
77
+ confirmDisabled,
78
+ confirmLoading,
79
+ onConfirm,
80
+ }: UseDesktopConfirmShortcutOptions): void {
81
+ useEffect(() => {
82
+ if (!open || !enabled || !onConfirm || confirmDisabled || confirmLoading) {
83
+ return;
84
+ }
85
+
86
+ const desktopModifierLabel = getDesktopShortcutModifierLabel();
87
+
88
+ const handleKeyDown = (event: KeyboardEvent): void => {
89
+ if (!isAllowedConfirmShortcutEvent(event, desktopModifierLabel)) {
90
+ return;
91
+ }
92
+
93
+ event.preventDefault();
94
+ onConfirm();
95
+ };
96
+
97
+ window.addEventListener("keydown", handleKeyDown);
98
+
99
+ return () => {
100
+ window.removeEventListener("keydown", handleKeyDown);
101
+ };
102
+ }, [confirmDisabled, confirmLoading, enabled, onConfirm, open]);
103
+ }