@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.
- package/README.md +101 -80
- package/dist/index.mjs +8 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -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/assets/hero.png +0 -0
- package/registry/ui/button/cn.ts +0 -6
- /package/registry/ui/button/{button.tsx → primitives/button.tsx} +0 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
import { SheetActionFooter } from '@/components/ui/responsive-sheet.footer';
|
|
6
|
+
import { DesktopSheetLayout, MobileSheetLayout } from '@/components/ui/responsive-sheet.layouts';
|
|
7
|
+
import {
|
|
8
|
+
useDesktopConfirmShortcut,
|
|
9
|
+
useDesktopShortcutModifierLabel,
|
|
10
|
+
} from '@/components/ui/responsive-sheet.shortcuts';
|
|
11
|
+
import { cn } from '@/lib/utils';
|
|
12
|
+
import { useIsMobile } from '@/components/ui/use-media-query';
|
|
13
|
+
|
|
14
|
+
export interface SheetOutsideInteractionGuard {
|
|
15
|
+
readonly selectors: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ResponsiveSheetClassNames {
|
|
19
|
+
readonly desktopContent?: string;
|
|
20
|
+
readonly mobileContent?: string;
|
|
21
|
+
readonly header?: string;
|
|
22
|
+
readonly body?: string;
|
|
23
|
+
readonly footer?: string;
|
|
24
|
+
readonly title?: string;
|
|
25
|
+
readonly description?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ResponsiveSheetProps {
|
|
29
|
+
readonly open: boolean;
|
|
30
|
+
readonly onOpenChange: (open: boolean) => void;
|
|
31
|
+
readonly title: ReactNode;
|
|
32
|
+
readonly description?: ReactNode;
|
|
33
|
+
readonly children: ReactNode;
|
|
34
|
+
readonly footer?: ReactNode;
|
|
35
|
+
readonly onCancel?: () => void;
|
|
36
|
+
readonly cancelLabel?: ReactNode;
|
|
37
|
+
readonly onConfirm?: () => void;
|
|
38
|
+
readonly confirmLabel?: ReactNode;
|
|
39
|
+
readonly confirmDisabled?: boolean;
|
|
40
|
+
readonly confirmLoading?: boolean;
|
|
41
|
+
readonly width?: number;
|
|
42
|
+
readonly modal?: boolean;
|
|
43
|
+
readonly outsideInteractionGuard?: SheetOutsideInteractionGuard;
|
|
44
|
+
readonly enableDesktopConfirmShortcut?: boolean;
|
|
45
|
+
readonly mobileDrawerContentClassName?: string;
|
|
46
|
+
readonly className?: string;
|
|
47
|
+
readonly contentClassName?: string;
|
|
48
|
+
readonly footerClassName?: string;
|
|
49
|
+
readonly classes?: ResponsiveSheetClassNames;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function ResponsiveSheet({
|
|
53
|
+
open,
|
|
54
|
+
onOpenChange,
|
|
55
|
+
title,
|
|
56
|
+
description,
|
|
57
|
+
children,
|
|
58
|
+
footer,
|
|
59
|
+
onCancel,
|
|
60
|
+
cancelLabel = 'Cancel',
|
|
61
|
+
onConfirm,
|
|
62
|
+
confirmLabel = 'Save',
|
|
63
|
+
confirmDisabled = false,
|
|
64
|
+
confirmLoading = false,
|
|
65
|
+
width = 550,
|
|
66
|
+
modal = true,
|
|
67
|
+
outsideInteractionGuard,
|
|
68
|
+
enableDesktopConfirmShortcut = true,
|
|
69
|
+
mobileDrawerContentClassName,
|
|
70
|
+
className,
|
|
71
|
+
contentClassName,
|
|
72
|
+
footerClassName,
|
|
73
|
+
classes,
|
|
74
|
+
}: ResponsiveSheetProps): React.ReactElement {
|
|
75
|
+
const isMobile = useIsMobile();
|
|
76
|
+
const desktopConfirmShortcutEnabled =
|
|
77
|
+
!isMobile && enableDesktopConfirmShortcut && Boolean(onConfirm);
|
|
78
|
+
const desktopModifierLabel = useDesktopShortcutModifierLabel(desktopConfirmShortcutEnabled);
|
|
79
|
+
|
|
80
|
+
useDesktopConfirmShortcut({
|
|
81
|
+
open,
|
|
82
|
+
enabled: desktopConfirmShortcutEnabled,
|
|
83
|
+
confirmDisabled,
|
|
84
|
+
confirmLoading,
|
|
85
|
+
onConfirm: onConfirm
|
|
86
|
+
? () => {
|
|
87
|
+
onConfirm();
|
|
88
|
+
}
|
|
89
|
+
: undefined,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const resolvedFooter = (
|
|
93
|
+
<SheetActionFooter
|
|
94
|
+
footer={footer}
|
|
95
|
+
onCancel={onCancel}
|
|
96
|
+
cancelLabel={cancelLabel}
|
|
97
|
+
onConfirm={onConfirm}
|
|
98
|
+
confirmLabel={confirmLabel}
|
|
99
|
+
confirmDisabled={confirmDisabled}
|
|
100
|
+
confirmLoading={confirmLoading}
|
|
101
|
+
desktopConfirmShortcutEnabled={desktopConfirmShortcutEnabled}
|
|
102
|
+
desktopModifierLabel={desktopModifierLabel}
|
|
103
|
+
/>
|
|
104
|
+
);
|
|
105
|
+
const hasFooter = [footer, onCancel, onConfirm].some(
|
|
106
|
+
(value) => value !== null && value !== undefined,
|
|
107
|
+
);
|
|
108
|
+
const sharedLayoutProps = {
|
|
109
|
+
open,
|
|
110
|
+
onOpenChange,
|
|
111
|
+
modal,
|
|
112
|
+
outsideInteractionGuard,
|
|
113
|
+
title,
|
|
114
|
+
description,
|
|
115
|
+
footer: hasFooter ? resolvedFooter : null,
|
|
116
|
+
children,
|
|
117
|
+
contentClassName,
|
|
118
|
+
footerClassName,
|
|
119
|
+
mobileDrawerContentClassName,
|
|
120
|
+
classes: {
|
|
121
|
+
...classes,
|
|
122
|
+
desktopContent: cn(className, classes?.desktopContent),
|
|
123
|
+
mobileContent: cn(className, mobileDrawerContentClassName, classes?.mobileContent),
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
return isMobile ? (
|
|
128
|
+
<MobileSheetLayout {...sharedLayoutProps} />
|
|
129
|
+
) : (
|
|
130
|
+
<DesktopSheetLayout {...sharedLayoutProps} width={width} />
|
|
131
|
+
);
|
|
132
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva } from "class-variance-authority";
|
|
3
|
+
import { Slot } from "radix-ui";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
"group/button inline-flex shrink-0 cursor-pointer items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default:
|
|
13
|
+
"bg-primary text-primary-foreground hover:brightness-90 [a]:hover:bg-primary/80",
|
|
14
|
+
outline:
|
|
15
|
+
"border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
16
|
+
secondary:
|
|
17
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
18
|
+
ghost:
|
|
19
|
+
"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
|
|
20
|
+
destructive:
|
|
21
|
+
"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
|
|
22
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
23
|
+
},
|
|
24
|
+
size: {
|
|
25
|
+
default:
|
|
26
|
+
"h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
27
|
+
xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
28
|
+
sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
|
29
|
+
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
30
|
+
icon: "size-8",
|
|
31
|
+
"icon-xs":
|
|
32
|
+
"size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
|
|
33
|
+
"icon-sm":
|
|
34
|
+
"size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
|
|
35
|
+
"icon-lg": "size-9",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
defaultVariants: {
|
|
39
|
+
variant: "default",
|
|
40
|
+
size: "default",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
type ButtonVariant =
|
|
46
|
+
| "default"
|
|
47
|
+
| "outline"
|
|
48
|
+
| "secondary"
|
|
49
|
+
| "ghost"
|
|
50
|
+
| "destructive"
|
|
51
|
+
| "link";
|
|
52
|
+
type ButtonSize =
|
|
53
|
+
| "default"
|
|
54
|
+
| "xs"
|
|
55
|
+
| "sm"
|
|
56
|
+
| "lg"
|
|
57
|
+
| "icon"
|
|
58
|
+
| "icon-xs"
|
|
59
|
+
| "icon-sm"
|
|
60
|
+
| "icon-lg";
|
|
61
|
+
|
|
62
|
+
interface ButtonProps extends React.ComponentProps<"button"> {
|
|
63
|
+
readonly asChild?: boolean;
|
|
64
|
+
readonly size?: ButtonSize;
|
|
65
|
+
readonly variant?: ButtonVariant;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function Button({
|
|
69
|
+
className,
|
|
70
|
+
variant = "default",
|
|
71
|
+
size = "default",
|
|
72
|
+
asChild = false,
|
|
73
|
+
...props
|
|
74
|
+
}: ButtonProps) {
|
|
75
|
+
const Comp = asChild ? Slot.Root : "button";
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<Comp
|
|
79
|
+
data-slot="button"
|
|
80
|
+
data-variant={variant}
|
|
81
|
+
data-size={size}
|
|
82
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
83
|
+
{...props}
|
|
84
|
+
/>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export { Button, buttonVariants };
|
|
89
|
+
export type { ButtonProps, ButtonSize, ButtonVariant };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { Drawer as DrawerPrimitive } from 'vaul';
|
|
5
|
+
|
|
6
|
+
import { cn } from '@/lib/utils';
|
|
7
|
+
|
|
8
|
+
function Drawer({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Root>) {
|
|
9
|
+
return <DrawerPrimitive.Root data-slot="drawer" {...props} />;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function DrawerPortal({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
|
|
13
|
+
return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function DrawerOverlay({
|
|
17
|
+
className,
|
|
18
|
+
...props
|
|
19
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
|
|
20
|
+
return (
|
|
21
|
+
<DrawerPrimitive.Overlay
|
|
22
|
+
data-slot="drawer-overlay"
|
|
23
|
+
className={cn(
|
|
24
|
+
'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 z-50 bg-black/10 backdrop-blur-xs',
|
|
25
|
+
className,
|
|
26
|
+
)}
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function DrawerContent({
|
|
33
|
+
className,
|
|
34
|
+
children,
|
|
35
|
+
...props
|
|
36
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Content>) {
|
|
37
|
+
return (
|
|
38
|
+
<DrawerPortal data-slot="drawer-portal">
|
|
39
|
+
<DrawerOverlay />
|
|
40
|
+
<DrawerPrimitive.Content
|
|
41
|
+
data-slot="drawer-content"
|
|
42
|
+
className={cn(
|
|
43
|
+
'bg-background group/drawer-content fixed z-50 flex h-auto flex-col overflow-visible text-sm data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-xl data-[vaul-drawer-direction=bottom]:border-t data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:rounded-r-xl data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:rounded-l-xl data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-xl data-[vaul-drawer-direction=top]:border-b data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm',
|
|
44
|
+
className,
|
|
45
|
+
)}
|
|
46
|
+
{...props}
|
|
47
|
+
>
|
|
48
|
+
<div className="bg-muted mx-auto mt-4 hidden h-1 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
|
|
49
|
+
{children}
|
|
50
|
+
</DrawerPrimitive.Content>
|
|
51
|
+
</DrawerPortal>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function DrawerHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
|
56
|
+
return (
|
|
57
|
+
<div
|
|
58
|
+
data-slot="drawer-header"
|
|
59
|
+
className={cn(
|
|
60
|
+
'flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-0.5 md:text-left',
|
|
61
|
+
className,
|
|
62
|
+
)}
|
|
63
|
+
{...props}
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function DrawerTitle({ className, ...props }: React.ComponentProps<typeof DrawerPrimitive.Title>) {
|
|
69
|
+
return (
|
|
70
|
+
<DrawerPrimitive.Title
|
|
71
|
+
data-slot="drawer-title"
|
|
72
|
+
className={cn('text-foreground text-base font-medium', className)}
|
|
73
|
+
{...props}
|
|
74
|
+
/>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function DrawerDescription({
|
|
79
|
+
className,
|
|
80
|
+
...props
|
|
81
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Description>) {
|
|
82
|
+
return (
|
|
83
|
+
<DrawerPrimitive.Description
|
|
84
|
+
data-slot="drawer-description"
|
|
85
|
+
className={cn('text-muted-foreground text-sm', className)}
|
|
86
|
+
{...props}
|
|
87
|
+
/>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export {
|
|
92
|
+
Drawer,
|
|
93
|
+
DrawerContent,
|
|
94
|
+
DrawerDescription,
|
|
95
|
+
DrawerHeader,
|
|
96
|
+
DrawerOverlay,
|
|
97
|
+
DrawerPortal,
|
|
98
|
+
DrawerTitle,
|
|
99
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '@/lib/utils';
|
|
4
|
+
|
|
5
|
+
function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
|
|
6
|
+
return (
|
|
7
|
+
<input
|
|
8
|
+
type={type}
|
|
9
|
+
data-slot="input"
|
|
10
|
+
className={cn(
|
|
11
|
+
'dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-8 rounded-lg border bg-transparent px-2.5 py-1 text-base transition-colors file:h-6 file:text-sm file:font-medium focus-visible:ring-3 aria-invalid:ring-3 md:text-sm file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
|
|
12
|
+
className,
|
|
13
|
+
)}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { Input };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Command } from 'lucide-react';
|
|
4
|
+
|
|
5
|
+
import type { ReactNode } from 'react';
|
|
6
|
+
|
|
7
|
+
import { cn } from '@/lib/utils';
|
|
8
|
+
|
|
9
|
+
export function KeyboardKeycap({
|
|
10
|
+
children,
|
|
11
|
+
className,
|
|
12
|
+
}: {
|
|
13
|
+
readonly children: ReactNode;
|
|
14
|
+
readonly className?: string;
|
|
15
|
+
}): React.ReactElement {
|
|
16
|
+
return (
|
|
17
|
+
<span
|
|
18
|
+
className={cn(
|
|
19
|
+
'inline-flex h-4 min-w-4 items-center justify-center rounded-[4px] border px-1 text-[9px] font-semibold leading-none',
|
|
20
|
+
className,
|
|
21
|
+
)}
|
|
22
|
+
>
|
|
23
|
+
{children}
|
|
24
|
+
</span>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function ShortcutModifierKeycap({
|
|
29
|
+
modifierLabel,
|
|
30
|
+
className,
|
|
31
|
+
}: {
|
|
32
|
+
readonly modifierLabel: string;
|
|
33
|
+
readonly className?: string;
|
|
34
|
+
}): React.ReactElement {
|
|
35
|
+
return (
|
|
36
|
+
<KeyboardKeycap className={className}>
|
|
37
|
+
{modifierLabel === 'Cmd' ? <Command className="size-[10px]" /> : 'Ctrl'}
|
|
38
|
+
</KeyboardKeycap>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { X } from 'lucide-react';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { Dialog as SheetPrimitive } from 'radix-ui';
|
|
6
|
+
|
|
7
|
+
import { Button } from '@/components/ui/button';
|
|
8
|
+
import { cn } from '@/lib/utils';
|
|
9
|
+
|
|
10
|
+
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
|
|
11
|
+
return <SheetPrimitive.Root data-slot="sheet" {...props} />;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function SheetPortal({ ...props }: React.ComponentProps<typeof SheetPrimitive.Portal>) {
|
|
15
|
+
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function SheetOverlay({
|
|
19
|
+
className,
|
|
20
|
+
...props
|
|
21
|
+
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
|
|
22
|
+
return (
|
|
23
|
+
<SheetPrimitive.Overlay
|
|
24
|
+
data-slot="sheet-overlay"
|
|
25
|
+
className={cn(
|
|
26
|
+
'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 z-50 bg-black/10 backdrop-blur-xs duration-100 data-ending-style:opacity-0 data-starting-style:opacity-0',
|
|
27
|
+
className,
|
|
28
|
+
)}
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function SheetContent({
|
|
35
|
+
className,
|
|
36
|
+
children,
|
|
37
|
+
side = 'right',
|
|
38
|
+
showCloseButton = true,
|
|
39
|
+
...props
|
|
40
|
+
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
|
|
41
|
+
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
42
|
+
showCloseButton?: boolean;
|
|
43
|
+
}) {
|
|
44
|
+
return (
|
|
45
|
+
<SheetPortal>
|
|
46
|
+
<SheetOverlay />
|
|
47
|
+
<SheetPrimitive.Content
|
|
48
|
+
data-slot="sheet-content"
|
|
49
|
+
data-side={side}
|
|
50
|
+
className={cn(
|
|
51
|
+
'bg-background data-open:animate-in data-closed:animate-out data-[side=right]:data-closed:slide-out-to-right-10 data-[side=right]:data-open:slide-in-from-right-10 data-[side=left]:data-closed:slide-out-to-left-10 data-[side=left]:data-open:slide-in-from-left-10 data-[side=top]:data-closed:slide-out-to-top-10 data-[side=top]:data-open:slide-in-from-top-10 data-closed:fade-out-0 data-open:fade-in-0 data-[side=bottom]:data-closed:slide-out-to-bottom-10 data-[side=bottom]:data-open:slide-in-from-bottom-10 fixed z-50 flex flex-col gap-4 overflow-visible bg-clip-padding text-sm shadow-lg transition duration-200 ease-in-out data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:rounded-r-xl data-[side=left]:border-r data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:rounded-l-xl data-[side=right]:border-l data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm',
|
|
52
|
+
className,
|
|
53
|
+
)}
|
|
54
|
+
{...props}
|
|
55
|
+
>
|
|
56
|
+
{children}
|
|
57
|
+
{showCloseButton ? (
|
|
58
|
+
<SheetPrimitive.Close data-slot="sheet-close" asChild>
|
|
59
|
+
<Button variant="ghost" className="absolute top-3 right-3" size="icon-sm">
|
|
60
|
+
<X className="size-4" />
|
|
61
|
+
<span className="sr-only">Close</span>
|
|
62
|
+
</Button>
|
|
63
|
+
</SheetPrimitive.Close>
|
|
64
|
+
) : null}
|
|
65
|
+
</SheetPrimitive.Content>
|
|
66
|
+
</SheetPortal>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function SheetHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
|
71
|
+
return (
|
|
72
|
+
<div
|
|
73
|
+
data-slot="sheet-header"
|
|
74
|
+
className={cn('flex flex-col gap-0.5 p-4', className)}
|
|
75
|
+
{...props}
|
|
76
|
+
/>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function SheetTitle({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Title>) {
|
|
81
|
+
return (
|
|
82
|
+
<SheetPrimitive.Title
|
|
83
|
+
data-slot="sheet-title"
|
|
84
|
+
className={cn('text-foreground text-base font-medium', className)}
|
|
85
|
+
{...props}
|
|
86
|
+
/>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function SheetDescription({
|
|
91
|
+
className,
|
|
92
|
+
...props
|
|
93
|
+
}: React.ComponentProps<typeof SheetPrimitive.Description>) {
|
|
94
|
+
return (
|
|
95
|
+
<SheetPrimitive.Description
|
|
96
|
+
data-slot="sheet-description"
|
|
97
|
+
className={cn('text-muted-foreground text-sm', className)}
|
|
98
|
+
{...props}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
export interface SearchableSelectRect {
|
|
2
|
+
readonly top: number;
|
|
3
|
+
readonly left: number;
|
|
4
|
+
readonly right: number;
|
|
5
|
+
readonly bottom: number;
|
|
6
|
+
readonly width: number;
|
|
7
|
+
readonly height: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface ResolveSearchableSelectDropdownPositionArgs {
|
|
11
|
+
readonly triggerRect: SearchableSelectRect;
|
|
12
|
+
readonly boundaryRect?: SearchableSelectRect;
|
|
13
|
+
readonly portalRect?: SearchableSelectRect;
|
|
14
|
+
readonly contentWidth: number;
|
|
15
|
+
readonly contentHeight: number;
|
|
16
|
+
readonly viewportWidth: number;
|
|
17
|
+
readonly viewportHeight: number;
|
|
18
|
+
readonly offset?: number;
|
|
19
|
+
readonly padding?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface SearchableSelectDropdownPosition {
|
|
23
|
+
readonly top: number;
|
|
24
|
+
readonly left: number;
|
|
25
|
+
readonly width: number;
|
|
26
|
+
readonly maxHeight: number;
|
|
27
|
+
readonly direction: 'up' | 'down';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function clamp(value: number, min: number, max: number): number {
|
|
31
|
+
if (max < min) {
|
|
32
|
+
return min;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return Math.min(Math.max(value, min), max);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function resolveSearchableSelectDropdownPosition({
|
|
39
|
+
triggerRect,
|
|
40
|
+
boundaryRect,
|
|
41
|
+
portalRect,
|
|
42
|
+
contentWidth,
|
|
43
|
+
contentHeight,
|
|
44
|
+
viewportWidth,
|
|
45
|
+
viewportHeight,
|
|
46
|
+
offset = 8,
|
|
47
|
+
padding = 8,
|
|
48
|
+
}: ResolveSearchableSelectDropdownPositionArgs): SearchableSelectDropdownPosition {
|
|
49
|
+
const boundaryLeftEdge = boundaryRect?.left ?? 0;
|
|
50
|
+
const boundaryRightEdge = boundaryRect?.right ?? viewportWidth;
|
|
51
|
+
const boundaryTopEdge = boundaryRect?.top ?? 0;
|
|
52
|
+
const boundaryBottomEdge = boundaryRect?.bottom ?? viewportHeight;
|
|
53
|
+
const boundaryLeft = Math.max(padding, boundaryLeftEdge + padding);
|
|
54
|
+
const boundaryRight = Math.min(
|
|
55
|
+
viewportWidth - padding,
|
|
56
|
+
boundaryRightEdge - padding,
|
|
57
|
+
);
|
|
58
|
+
const boundaryTop = Math.max(padding, boundaryTopEdge + padding);
|
|
59
|
+
const boundaryBottom = Math.min(
|
|
60
|
+
viewportHeight - padding,
|
|
61
|
+
boundaryBottomEdge - padding,
|
|
62
|
+
);
|
|
63
|
+
const availableWidth = Math.max(0, boundaryRight - boundaryLeft);
|
|
64
|
+
const width = Math.min(contentWidth, availableWidth);
|
|
65
|
+
const alignedRightLeft = triggerRect.right - width;
|
|
66
|
+
const defaultLeft = triggerRect.left;
|
|
67
|
+
const shouldAlignRight =
|
|
68
|
+
defaultLeft + width > boundaryRight && alignedRightLeft >= boundaryLeft;
|
|
69
|
+
const left = clamp(
|
|
70
|
+
shouldAlignRight ? alignedRightLeft : defaultLeft,
|
|
71
|
+
boundaryLeft,
|
|
72
|
+
boundaryRight - width,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const spaceAbove = triggerRect.top - boundaryTop;
|
|
76
|
+
const spaceBelow = boundaryBottom - triggerRect.bottom;
|
|
77
|
+
const shouldOpenUp = spaceBelow < contentHeight + offset && spaceAbove > spaceBelow;
|
|
78
|
+
const direction = shouldOpenUp ? 'up' : 'down';
|
|
79
|
+
const maxHeight = Math.max(
|
|
80
|
+
120,
|
|
81
|
+
Math.floor((shouldOpenUp ? spaceAbove : spaceBelow) - offset),
|
|
82
|
+
);
|
|
83
|
+
const renderedHeight = Math.min(contentHeight, maxHeight);
|
|
84
|
+
const viewportTop = shouldOpenUp
|
|
85
|
+
? Math.max(boundaryTop, triggerRect.top - offset - renderedHeight)
|
|
86
|
+
: Math.min(boundaryBottom - renderedHeight, triggerRect.bottom + offset);
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
top: viewportTop - (portalRect?.top ?? 0),
|
|
90
|
+
left: left - (portalRect?.left ?? 0),
|
|
91
|
+
width,
|
|
92
|
+
maxHeight,
|
|
93
|
+
direction,
|
|
94
|
+
};
|
|
95
|
+
}
|