@carefully-built/cli 0.1.0 → 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.
- package/README.md +148 -7
- package/dist/index.mjs +71 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -3
- package/registry/ui/avatar/manifest.json +33 -0
- package/registry/ui/avatar/primitives/avatar.tsx +64 -0
- package/registry/ui/avatar/utils/cn.ts +6 -0
- package/registry/ui/button/manifest.json +24 -5
- package/registry/ui/button/utils/cn.ts +6 -0
- package/registry/ui/calendar/manifest.json +35 -0
- package/registry/ui/calendar/primitives/button.tsx +89 -0
- package/registry/ui/calendar/primitives/calendar.tsx +68 -0
- package/registry/ui/calendar/utils/cn.ts +6 -0
- package/registry/ui/card/manifest.json +36 -0
- package/registry/ui/card/primitives/card.tsx +80 -0
- package/registry/ui/card/utils/cn.ts +6 -0
- package/registry/ui/chip/manifest.json +36 -0
- package/registry/ui/chip/primitives/chip-utils.ts +10 -0
- package/registry/ui/chip/primitives/chip.tsx +74 -0
- package/registry/ui/chip/utils/cn.ts +6 -0
- package/registry/ui/chip-utils/manifest.json +33 -0
- package/registry/ui/chip-utils/primitives/chip-utils.ts +10 -0
- package/registry/ui/chip-utils/utils/cn.ts +6 -0
- package/registry/ui/date-display/manifest.json +33 -0
- package/registry/ui/date-display/utils/cn.ts +6 -0
- package/registry/ui/date-display/utils/date-display.ts +61 -0
- package/registry/ui/dialog/manifest.json +43 -0
- package/registry/ui/dialog/primitives/button.tsx +89 -0
- package/registry/ui/dialog/primitives/dialog.tsx +147 -0
- package/registry/ui/dialog/utils/cn.ts +6 -0
- package/registry/ui/display-date/manifest.json +36 -0
- package/registry/ui/display-date/primitives/display-date.tsx +20 -0
- package/registry/ui/display-date/utils/cn.ts +6 -0
- package/registry/ui/display-date/utils/date-display.ts +61 -0
- package/registry/ui/drawer/manifest.json +37 -0
- package/registry/ui/drawer/primitives/drawer.tsx +99 -0
- package/registry/ui/drawer/utils/cn.ts +6 -0
- package/registry/ui/dropdown-menu/manifest.json +37 -0
- package/registry/ui/dropdown-menu/primitives/dropdown-menu.tsx +140 -0
- package/registry/ui/dropdown-menu/utils/cn.ts +6 -0
- package/registry/ui/empty-state/empty-state/collection-empty-state.ts +29 -0
- package/registry/ui/empty-state/empty-state/empty-state-card.tsx +72 -0
- package/registry/ui/empty-state/empty-state/index.ts +8 -0
- package/registry/ui/empty-state/empty-state/initial-empty-state.tsx +36 -0
- package/registry/ui/empty-state/empty-state/no-results-state.tsx +20 -0
- package/registry/ui/empty-state/manifest.json +63 -0
- package/registry/ui/empty-state/primitives/button.tsx +89 -0
- package/registry/ui/empty-state/primitives/card.tsx +80 -0
- package/registry/ui/empty-state/utils/cn.ts +6 -0
- package/registry/ui/error-page/error-page/error-code.tsx +16 -0
- package/registry/ui/error-page/error-page/error-page-content.ts +75 -0
- package/registry/ui/error-page/error-page/index.ts +19 -0
- package/registry/ui/error-page/error-page/posthog-error-capture.ts +83 -0
- package/registry/ui/error-page/error-page/saas-error-page.tsx +146 -0
- package/registry/ui/error-page/manifest.json +64 -0
- package/registry/ui/error-page/primitives/button.tsx +89 -0
- package/registry/ui/error-page/utils/cn.ts +6 -0
- package/registry/ui/field-detail-row/manifest.json +32 -0
- package/registry/ui/field-detail-row/primitives/field-detail-row.tsx +28 -0
- package/registry/ui/field-detail-row/utils/cn.ts +6 -0
- package/registry/ui/file-dropzone/manifest.json +35 -0
- package/registry/ui/file-dropzone/primitives/button.tsx +89 -0
- package/registry/ui/file-dropzone/primitives/file-dropzone.tsx +236 -0
- package/registry/ui/file-dropzone/utils/cn.ts +6 -0
- package/registry/ui/help-info-button/manifest.json +72 -0
- package/registry/ui/help-info-button/overlays/responsive-sheet.footer.tsx +88 -0
- package/registry/ui/help-info-button/overlays/responsive-sheet.layouts.tsx +207 -0
- package/registry/ui/help-info-button/overlays/responsive-sheet.shortcuts.ts +103 -0
- package/registry/ui/help-info-button/overlays/responsive-sheet.tsx +132 -0
- package/registry/ui/help-info-button/primitives/button.tsx +89 -0
- package/registry/ui/help-info-button/primitives/drawer.tsx +99 -0
- package/registry/ui/help-info-button/primitives/help-info-button.tsx +63 -0
- package/registry/ui/help-info-button/primitives/keyboard-shortcut-hint.tsx +40 -0
- package/registry/ui/help-info-button/primitives/sheet.tsx +103 -0
- package/registry/ui/help-info-button/primitives/tooltip.tsx +57 -0
- package/registry/ui/help-info-button/utils/cn.ts +6 -0
- package/registry/ui/help-info-button/utils/use-media-query.ts +28 -0
- package/registry/ui/input/manifest.json +31 -0
- package/registry/ui/input/primitives/input.tsx +19 -0
- package/registry/ui/input/utils/cn.ts +6 -0
- package/registry/ui/keyboard-shortcut-hint/manifest.json +32 -0
- package/registry/ui/keyboard-shortcut-hint/primitives/keyboard-shortcut-hint.tsx +40 -0
- package/registry/ui/keyboard-shortcut-hint/utils/cn.ts +6 -0
- package/registry/ui/label/manifest.json +31 -0
- package/registry/ui/label/primitives/label.tsx +21 -0
- package/registry/ui/label/utils/cn.ts +6 -0
- package/registry/ui/pagination/manifest.json +36 -0
- package/registry/ui/pagination/primitives/button.tsx +89 -0
- package/registry/ui/pagination/primitives/pagination.tsx +143 -0
- package/registry/ui/pagination/utils/cn.ts +6 -0
- package/registry/ui/popover/manifest.json +33 -0
- package/registry/ui/popover/primitives/popover.tsx +46 -0
- package/registry/ui/popover/utils/cn.ts +6 -0
- package/registry/ui/responsive-sheet/manifest.json +66 -0
- package/registry/ui/responsive-sheet/overlays/responsive-sheet.footer.tsx +88 -0
- package/registry/ui/responsive-sheet/overlays/responsive-sheet.layouts.tsx +207 -0
- package/registry/ui/responsive-sheet/overlays/responsive-sheet.shortcuts.ts +103 -0
- package/registry/ui/responsive-sheet/overlays/responsive-sheet.tsx +132 -0
- package/registry/ui/responsive-sheet/primitives/button.tsx +89 -0
- package/registry/ui/responsive-sheet/primitives/drawer.tsx +99 -0
- package/registry/ui/responsive-sheet/primitives/keyboard-shortcut-hint.tsx +40 -0
- package/registry/ui/responsive-sheet/primitives/sheet.tsx +103 -0
- package/registry/ui/responsive-sheet/utils/cn.ts +6 -0
- package/registry/ui/responsive-sheet/utils/use-media-query.ts +28 -0
- package/registry/ui/responsive-sheet.footer/manifest.json +40 -0
- package/registry/ui/responsive-sheet.footer/overlays/responsive-sheet.footer.tsx +88 -0
- package/registry/ui/responsive-sheet.footer/primitives/button.tsx +89 -0
- package/registry/ui/responsive-sheet.footer/primitives/keyboard-shortcut-hint.tsx +40 -0
- package/registry/ui/responsive-sheet.footer/utils/cn.ts +6 -0
- package/registry/ui/responsive-sheet.shortcuts/manifest.json +34 -0
- package/registry/ui/responsive-sheet.shortcuts/overlays/responsive-sheet.shortcuts.ts +103 -0
- package/registry/ui/responsive-sheet.shortcuts/utils/cn.ts +6 -0
- package/registry/ui/scroll-fade-area/manifest.json +31 -0
- package/registry/ui/scroll-fade-area/primitives/scroll-fade-area.tsx +295 -0
- package/registry/ui/scroll-fade-area/utils/cn.ts +6 -0
- package/registry/ui/search/manifest.json +35 -0
- package/registry/ui/search/utils/cn.ts +6 -0
- package/registry/ui/search/utils/search.ts +227 -0
- package/registry/ui/searchable-select/manifest.json +48 -0
- package/registry/ui/searchable-select/primitives/input.tsx +19 -0
- package/registry/ui/searchable-select/search/searchable-select-position.ts +95 -0
- package/registry/ui/searchable-select/search/searchable-select.tsx +431 -0
- package/registry/ui/searchable-select/utils/cn.ts +6 -0
- package/registry/ui/searchable-select/utils/search.ts +227 -0
- package/registry/ui/searchable-select-position/manifest.json +32 -0
- package/registry/ui/searchable-select-position/search/searchable-select-position.ts +95 -0
- package/registry/ui/searchable-select-position/utils/cn.ts +6 -0
- package/registry/ui/segmented-toggle/manifest.json +41 -0
- package/registry/ui/segmented-toggle/primitives/scroll-fade-area.tsx +295 -0
- package/registry/ui/segmented-toggle/primitives/segmented-toggle.tsx +106 -0
- package/registry/ui/segmented-toggle/primitives/tabs.tsx +97 -0
- package/registry/ui/segmented-toggle/utils/cn.ts +6 -0
- package/registry/ui/select/manifest.json +37 -0
- package/registry/ui/select/primitives/select.tsx +142 -0
- package/registry/ui/select/utils/cn.ts +6 -0
- package/registry/ui/sheet/manifest.json +39 -0
- package/registry/ui/sheet/primitives/button.tsx +89 -0
- package/registry/ui/sheet/primitives/sheet.tsx +103 -0
- package/registry/ui/sheet/utils/cn.ts +6 -0
- package/registry/ui/skeleton/manifest.json +31 -0
- package/registry/ui/skeleton/primitives/skeleton.tsx +13 -0
- package/registry/ui/skeleton/utils/cn.ts +6 -0
- package/registry/ui/smart-table/manifest.json +115 -0
- package/registry/ui/smart-table/primitives/button.tsx +89 -0
- package/registry/ui/smart-table/primitives/card.tsx +80 -0
- package/registry/ui/smart-table/primitives/display-date.tsx +20 -0
- package/registry/ui/smart-table/primitives/pagination.tsx +143 -0
- package/registry/ui/smart-table/primitives/skeleton.tsx +13 -0
- package/registry/ui/smart-table/primitives/table.tsx +92 -0
- package/registry/ui/smart-table/primitives/tooltip.tsx +57 -0
- package/registry/ui/smart-table/smart-table/DesktopView.tsx +343 -0
- package/registry/ui/smart-table/smart-table/MobileView.tsx +170 -0
- package/registry/ui/smart-table/smart-table/SmartTable.tsx +85 -0
- package/registry/ui/smart-table/smart-table/SmartTableActions.tsx +71 -0
- package/registry/ui/smart-table/smart-table/TruncatedContent.tsx +147 -0
- package/registry/ui/smart-table/smart-table/index.ts +15 -0
- package/registry/ui/smart-table/smart-table/sorting.ts +148 -0
- package/registry/ui/smart-table/smart-table/truncated-content.utils.ts +22 -0
- package/registry/ui/smart-table/smart-table/types.ts +95 -0
- package/registry/ui/smart-table/smart-table/utils.ts +150 -0
- package/registry/ui/smart-table/utils/cn.ts +6 -0
- package/registry/ui/smart-table/utils/date-display.ts +61 -0
- package/registry/ui/smart-table/utils/use-media-query.ts +28 -0
- package/registry/ui/switch/manifest.json +31 -0
- package/registry/ui/switch/primitives/switch.tsx +31 -0
- package/registry/ui/switch/utils/cn.ts +6 -0
- package/registry/ui/table/manifest.json +38 -0
- package/registry/ui/table/primitives/table.tsx +92 -0
- package/registry/ui/table/utils/cn.ts +6 -0
- package/registry/ui/table-toolbar/manifest.json +93 -0
- package/registry/ui/table-toolbar/overlays/responsive-sheet.footer.tsx +88 -0
- package/registry/ui/table-toolbar/overlays/responsive-sheet.layouts.tsx +207 -0
- package/registry/ui/table-toolbar/overlays/responsive-sheet.shortcuts.ts +103 -0
- package/registry/ui/table-toolbar/overlays/responsive-sheet.tsx +132 -0
- package/registry/ui/table-toolbar/primitives/button.tsx +89 -0
- package/registry/ui/table-toolbar/primitives/drawer.tsx +99 -0
- package/registry/ui/table-toolbar/primitives/input.tsx +19 -0
- package/registry/ui/table-toolbar/primitives/keyboard-shortcut-hint.tsx +40 -0
- package/registry/ui/table-toolbar/primitives/sheet.tsx +103 -0
- package/registry/ui/table-toolbar/search/searchable-select-position.ts +95 -0
- package/registry/ui/table-toolbar/search/searchable-select.tsx +431 -0
- package/registry/ui/table-toolbar/table-toolbar/index.ts +9 -0
- package/registry/ui/table-toolbar/table-toolbar/table-toolbar.tsx +552 -0
- package/registry/ui/table-toolbar/utils/cn.ts +6 -0
- package/registry/ui/table-toolbar/utils/search.ts +227 -0
- package/registry/ui/table-toolbar/utils/use-media-query.ts +28 -0
- package/registry/ui/tabs/manifest.json +40 -0
- package/registry/ui/tabs/primitives/scroll-fade-area.tsx +295 -0
- package/registry/ui/tabs/primitives/tabs.tsx +97 -0
- package/registry/ui/tabs/utils/cn.ts +6 -0
- package/registry/ui/textarea/manifest.json +31 -0
- package/registry/ui/textarea/primitives/textarea.tsx +18 -0
- package/registry/ui/textarea/utils/cn.ts +6 -0
- package/registry/ui/tooltip/manifest.json +34 -0
- package/registry/ui/tooltip/primitives/tooltip.tsx +57 -0
- package/registry/ui/tooltip/utils/cn.ts +6 -0
- package/registry/ui/use-media-query/manifest.json +32 -0
- package/registry/ui/use-media-query/utils/cn.ts +6 -0
- package/registry/ui/use-media-query/utils/use-media-query.ts +28 -0
- package/registry/ui/user-picker/manifest.json +52 -0
- package/registry/ui/user-picker/primitives/avatar.tsx +64 -0
- package/registry/ui/user-picker/primitives/button.tsx +89 -0
- package/registry/ui/user-picker/primitives/input.tsx +19 -0
- package/registry/ui/user-picker/primitives/popover.tsx +46 -0
- package/registry/ui/user-picker/primitives/user-picker-utils.ts +113 -0
- package/registry/ui/user-picker/primitives/user-picker.tsx +226 -0
- package/registry/ui/user-picker/utils/cn.ts +6 -0
- package/registry/ui/user-picker-utils/manifest.json +38 -0
- package/registry/ui/user-picker-utils/primitives/user-picker-utils.ts +113 -0
- package/registry/ui/user-picker-utils/utils/cn.ts +6 -0
- package/registry/ui/button/cn.ts +0 -6
- /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,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,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
|
+
}
|