@atelic-action/ui 0.17.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +432 -45
- package/bin/atelic-check-base-path.ts +5 -0
- package/bin/atelic-clean-sitemap.ts +5 -0
- package/bin/atelic-contact-canary.ts +5 -0
- package/bin/atelic-print-sheets.sh +1 -1
- package/bin/atelic-seal-document.mjs +1 -1
- package/bin/atelic-serve-static.ts +1 -1
- package/dist/gate/index.d.ts +1 -1
- package/dist/gate/index.js +1 -1
- package/dist/gate/middleware.d.ts +4 -1
- package/dist/gate/middleware.js +14 -5
- package/dist/gate/wall.d.ts +25 -0
- package/dist/gate/wall.js +41 -2
- package/dist/routing/basePath.d.ts +24 -0
- package/dist/routing/basePath.js +54 -0
- package/dist/vite/index.d.ts +3 -2
- package/dist/vite/index.js +3 -2
- package/dist/vite/marketing-config.d.ts +50 -0
- package/dist/vite/marketing-config.js +83 -0
- package/package.json +164 -5
- package/src/app/AppProvider.tsx +41 -0
- package/src/app/AuthGuard.tsx +27 -0
- package/src/app/CardErrorBoundary.tsx +39 -0
- package/src/app/ErrorFallback.tsx +39 -0
- package/src/app/GoogleGlyph.tsx +22 -0
- package/src/app/config.ts +54 -0
- package/src/app/configure.ts +18 -0
- package/src/app/context/AppContext.tsx +52 -0
- package/src/app/context/SidebarContext.tsx +124 -0
- package/src/app/context/UserContext.tsx +139 -0
- package/src/app/hooks/useDebounce.ts +12 -0
- package/src/app/hooks/useIsDesktop.ts +31 -0
- package/src/app/hooks/useWakeLock.ts +49 -0
- package/src/app/index.ts +64 -0
- package/src/app/layout/ActionButton.tsx +67 -0
- package/src/app/layout/AppLayout.tsx +110 -0
- package/src/app/layout/AppPage.tsx +268 -0
- package/src/app/layout/Breadcrumbs.tsx +57 -0
- package/src/app/layout/DesktopRail.tsx +168 -0
- package/src/app/layout/FormActionButtons.tsx +69 -0
- package/src/app/layout/MobileMenu.tsx +156 -0
- package/src/app/layout/MobileTabBar.tsx +43 -0
- package/src/app/layout/PageError.tsx +30 -0
- package/src/app/layout/PageSkeleton.tsx +65 -0
- package/src/app/layout/SectionCards.tsx +33 -0
- package/src/app/layout/UserMenu.tsx +104 -0
- package/src/app/layout/identity.ts +22 -0
- package/src/app/layout/index.ts +20 -0
- package/src/app/layout/nav.ts +52 -0
- package/src/app/lib/dates.ts +57 -0
- package/src/app/lib/env.ts +37 -0
- package/src/app/lib/format.ts +61 -0
- package/src/app/lib/http.ts +205 -0
- package/src/app/lib/lastAuth.ts +45 -0
- package/src/app/lib/oauth.ts +32 -0
- package/src/app/lib/publicRoutes.ts +33 -0
- package/src/app/lib/queryClient.ts +38 -0
- package/src/app/lib/search.ts +17 -0
- package/src/app/lib/session.ts +36 -0
- package/src/app/lib/tokenRefresh.ts +137 -0
- package/src/app/lib/transformKeys.ts +60 -0
- package/src/app/lib/units.ts +228 -0
- package/src/app/lib/unwrap.ts +48 -0
- package/src/app/lib/utils.ts +10 -0
- package/src/app/stores/authStore.ts +80 -0
- package/src/app/stores/storage.ts +40 -0
- package/src/app/stores/unitsStore.ts +54 -0
- package/src/app/types/index.ts +72 -0
- package/src/app/types/query.ts +11 -0
- package/src/app/ui/accordion.tsx +54 -0
- package/src/app/ui/alert-dialog.tsx +114 -0
- package/src/app/ui/alert.tsx +49 -0
- package/src/app/ui/avatar.tsx +45 -0
- package/src/app/ui/badge-list.tsx +51 -0
- package/src/app/ui/badge.tsx +33 -0
- package/src/app/ui/button.tsx +49 -0
- package/src/app/ui/callout.tsx +22 -0
- package/src/app/ui/card.tsx +55 -0
- package/src/app/ui/checkbox.tsx +26 -0
- package/src/app/ui/clear-button.tsx +42 -0
- package/src/app/ui/command.tsx +140 -0
- package/src/app/ui/country-dropdown.tsx +184 -0
- package/src/app/ui/dialog.tsx +104 -0
- package/src/app/ui/dropdown-menu.tsx +185 -0
- package/src/app/ui/form.tsx +172 -0
- package/src/app/ui/index.ts +36 -0
- package/src/app/ui/input.tsx +22 -0
- package/src/app/ui/label.tsx +19 -0
- package/src/app/ui/option-select.tsx +56 -0
- package/src/app/ui/pagination.tsx +90 -0
- package/src/app/ui/popover.tsx +29 -0
- package/src/app/ui/progress.tsx +25 -0
- package/src/app/ui/search-input.tsx +63 -0
- package/src/app/ui/select.tsx +151 -0
- package/src/app/ui/separator.tsx +24 -0
- package/src/app/ui/sheet.tsx +119 -0
- package/src/app/ui/slider.tsx +23 -0
- package/src/app/ui/switch.tsx +27 -0
- package/src/app/ui/textarea.tsx +21 -0
- package/src/app/ui/tooltip.tsx +32 -0
- package/src/artifact/ArtifactShell.tsx +2 -2
- package/src/artifact/Gate.tsx +2 -2
- package/src/artifact/Note.tsx +9 -39
- package/src/artifact/Paragraph.tsx +65 -0
- package/src/artifact/Read.tsx +406 -0
- package/src/artifact/Writeup.tsx +7 -7
- package/src/artifact/index.ts +1 -0
- package/src/chrome/SiteShell.tsx +141 -0
- package/src/chrome/index.ts +2 -0
- package/src/components/Disclosure.tsx +80 -14
- package/src/components/GateLogin.tsx +1 -1
- package/src/components/Watermark.tsx +1 -1
- package/src/components/index.ts +6 -2
- package/src/gate/index.ts +1 -1
- package/src/gate/middleware.ts +17 -6
- package/src/gate/wall.ts +60 -2
- package/src/lib/basePathAudit.ts +96 -0
- package/src/lib/displayDate.ts +25 -0
- package/src/lib/honeypot.ts +11 -0
- package/src/lib/hours.ts +28 -0
- package/src/lib/sitemap.ts +34 -0
- package/src/lib/trackEvent.ts +26 -0
- package/src/lib/useOpenHeight.ts +28 -0
- package/src/primitives/Button.tsx +3 -1
- package/src/primitives/Chip.tsx +3 -1
- package/src/primitives/ImageFrame.tsx +48 -0
- package/src/primitives/ProseBlocks.tsx +67 -0
- package/src/primitives/QuoteCard.tsx +20 -0
- package/src/primitives/StarRow.tsx +37 -0
- package/src/primitives/TickList.tsx +24 -0
- package/src/primitives/index.ts +5 -0
- package/src/routing/BasePathProvider.tsx +47 -0
- package/src/routing/basePath.ts +57 -0
- package/src/routing/index.ts +8 -0
- package/src/scripts/check-base-path.ts +130 -0
- package/src/scripts/clean-sitemap.ts +27 -0
- package/src/scripts/contact-canary.ts +98 -0
- package/src/scripts/serve-static.ts +29 -3
- package/src/sections/BlogIndex.tsx +57 -0
- package/src/sections/BlogPost.tsx +65 -0
- package/src/sections/CTABanner.tsx +93 -0
- package/src/sections/CTASection.tsx +60 -0
- package/src/sections/ComparisonTable.tsx +114 -0
- package/src/sections/ContactForm.tsx +163 -0
- package/src/sections/ContactInfoCard.tsx +92 -0
- package/src/sections/CredentialList.tsx +28 -0
- package/src/sections/DeviceShowcase.tsx +39 -0
- package/src/sections/ExpandList.tsx +58 -0
- package/src/sections/FAQBrowser.tsx +283 -0
- package/src/sections/FeatureDetail.tsx +60 -0
- package/src/sections/FeatureGrid.tsx +83 -0
- package/src/sections/Gallery.tsx +84 -0
- package/src/sections/Hero.tsx +110 -0
- package/src/sections/HoursTable.tsx +23 -0
- package/src/sections/JumpRail.tsx +35 -0
- package/src/sections/LegalPage.tsx +43 -0
- package/src/sections/LocationSection.tsx +66 -0
- package/src/sections/Locations.tsx +71 -0
- package/src/sections/LogoMarquee.tsx +27 -0
- package/src/sections/MapEmbed.tsx +75 -0
- package/src/sections/MediaBand.tsx +20 -0
- package/src/sections/MenuList.tsx +44 -0
- package/src/sections/PageHero.tsx +25 -0
- package/src/sections/PressQuotes.tsx +51 -0
- package/src/sections/PricingBand.tsx +43 -0
- package/src/sections/PricingCard.tsx +36 -0
- package/src/sections/ProcessSteps.tsx +45 -0
- package/src/sections/ProfileCards.tsx +73 -0
- package/src/sections/RevealSlider.tsx +156 -0
- package/src/sections/ReviewCarousel.tsx +223 -0
- package/src/sections/ReviewWall.tsx +220 -0
- package/src/sections/SocialStrip.tsx +83 -0
- package/src/sections/SplitSection.tsx +56 -0
- package/src/sections/index.ts +76 -0
- package/src/sections/siteConfig.ts +201 -0
- package/src/styles/app.css +673 -0
- package/src/styles/artifact.css +759 -401
- package/src/styles/base.css +49 -47
- package/src/styles/chrome.css +71 -71
- package/src/styles/components.css +150 -140
- package/src/styles/gate.css +29 -29
- package/src/styles/primitives.css +190 -25
- package/src/styles/sections.css +2652 -0
- package/src/styles/star-rating.css +2 -2
- package/src/vite/index.ts +9 -2
- package/src/vite/marketing-config.ts +128 -0
- package/src/components/Collapsible.tsx +0 -43
- /package/src/scripts/{build-gate-payload.mjs → seal-document.mjs} +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
|
|
5
|
+
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
|
6
|
+
({ className, type, ...props }, ref) => {
|
|
7
|
+
return (
|
|
8
|
+
<input
|
|
9
|
+
type={type}
|
|
10
|
+
className={cn(
|
|
11
|
+
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
12
|
+
className,
|
|
13
|
+
)}
|
|
14
|
+
ref={ref}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
},
|
|
19
|
+
);
|
|
20
|
+
Input.displayName = "Input";
|
|
21
|
+
|
|
22
|
+
export { Input };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
|
|
7
|
+
const labelVariants = cva(
|
|
8
|
+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
const Label = React.forwardRef<
|
|
12
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
13
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
|
|
14
|
+
>(({ className, ...props }, ref) => (
|
|
15
|
+
<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />
|
|
16
|
+
));
|
|
17
|
+
Label.displayName = LabelPrimitive.Root.displayName;
|
|
18
|
+
|
|
19
|
+
export { Label };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { cn } from "../lib/utils";
|
|
2
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select";
|
|
3
|
+
|
|
4
|
+
export interface SelectOption {
|
|
5
|
+
value: string;
|
|
6
|
+
label: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface OptionSelectProps {
|
|
10
|
+
/** Currently selected option value. Omit (undefined) to show the placeholder. */
|
|
11
|
+
value?: string;
|
|
12
|
+
/** Fired with the newly selected option value. */
|
|
13
|
+
onValueChange: (value: string) => void;
|
|
14
|
+
/** Options to render, in display order. */
|
|
15
|
+
options: SelectOption[];
|
|
16
|
+
/** Accessible name for the trigger (also the combobox name in tests). */
|
|
17
|
+
ariaLabel: string;
|
|
18
|
+
/** Shown when no value matches (value is always set in most filters). */
|
|
19
|
+
placeholder?: string;
|
|
20
|
+
/** Width / layout classes for the trigger; the white surface is baked in. */
|
|
21
|
+
className?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Thin, reusable wrapper over the Radix Select primitives for the common case:
|
|
26
|
+
* a single-select driven by a flat `options` array. Bakes in the raised white
|
|
27
|
+
* surface used for filters/search so callers only pass width + the options.
|
|
28
|
+
* Reach for the raw Select primitives only when you need grouping, custom item
|
|
29
|
+
* rendering, or anything this does not express.
|
|
30
|
+
*
|
|
31
|
+
* Note: Radix forbids an empty-string option value. For an "all / none" option,
|
|
32
|
+
* use a sentinel value and map it back in the caller's onValueChange.
|
|
33
|
+
*/
|
|
34
|
+
export function OptionSelect({
|
|
35
|
+
value,
|
|
36
|
+
onValueChange,
|
|
37
|
+
options,
|
|
38
|
+
ariaLabel,
|
|
39
|
+
placeholder,
|
|
40
|
+
className,
|
|
41
|
+
}: OptionSelectProps) {
|
|
42
|
+
return (
|
|
43
|
+
<Select value={value} onValueChange={onValueChange}>
|
|
44
|
+
<SelectTrigger className={cn("bg-card shadow-sm", className)} aria-label={ariaLabel}>
|
|
45
|
+
<SelectValue placeholder={placeholder} />
|
|
46
|
+
</SelectTrigger>
|
|
47
|
+
<SelectContent>
|
|
48
|
+
{options.map((option) => (
|
|
49
|
+
<SelectItem key={option.value} value={option.value}>
|
|
50
|
+
{option.label}
|
|
51
|
+
</SelectItem>
|
|
52
|
+
))}
|
|
53
|
+
</SelectContent>
|
|
54
|
+
</Select>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { cn } from "../lib/utils";
|
|
2
|
+
import { Button } from "./button";
|
|
3
|
+
|
|
4
|
+
interface PaginationProps {
|
|
5
|
+
/** Current page, 1-indexed. */
|
|
6
|
+
page: number;
|
|
7
|
+
/** Total number of pages. */
|
|
8
|
+
totalPages: number;
|
|
9
|
+
/** Called with the requested page when a control is clicked. */
|
|
10
|
+
onPageChange: (page: number) => void;
|
|
11
|
+
className?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type PageItem = { type: "page"; value: number } | { type: "gap"; key: string };
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Windowed page list: always shows the first and last page plus the current
|
|
18
|
+
* page and its neighbors, eliding the rest with gaps. Keeps the control compact
|
|
19
|
+
* as the page count grows.
|
|
20
|
+
*/
|
|
21
|
+
function pageWindow(page: number, totalPages: number): PageItem[] {
|
|
22
|
+
const wanted = [1, totalPages, page, page - 1, page + 1];
|
|
23
|
+
const nums = [...new Set(wanted)].filter((p) => p >= 1 && p <= totalPages).sort((a, b) => a - b);
|
|
24
|
+
|
|
25
|
+
const items: PageItem[] = [];
|
|
26
|
+
let prev = 0;
|
|
27
|
+
let gapId = 0;
|
|
28
|
+
for (const value of nums) {
|
|
29
|
+
if (value - prev > 1) {
|
|
30
|
+
items.push({ type: "gap", key: `gap-${gapId++}` });
|
|
31
|
+
}
|
|
32
|
+
items.push({ type: "page", value });
|
|
33
|
+
prev = value;
|
|
34
|
+
}
|
|
35
|
+
return items;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Numbered pagination control. Renders nothing for a single page. Purely
|
|
40
|
+
* presentational: the caller owns the page state and slices its own data.
|
|
41
|
+
*/
|
|
42
|
+
export function Pagination({ page, totalPages, onPageChange, className }: PaginationProps) {
|
|
43
|
+
if (totalPages <= 1) return null;
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<nav
|
|
47
|
+
aria-label="Pagination"
|
|
48
|
+
className={cn("mt-8 flex flex-wrap items-center justify-center gap-1", className)}
|
|
49
|
+
>
|
|
50
|
+
<Button
|
|
51
|
+
variant="outline"
|
|
52
|
+
size="sm"
|
|
53
|
+
onClick={() => onPageChange(page - 1)}
|
|
54
|
+
disabled={page <= 1}
|
|
55
|
+
aria-label="Previous page"
|
|
56
|
+
>
|
|
57
|
+
Prev
|
|
58
|
+
</Button>
|
|
59
|
+
|
|
60
|
+
{pageWindow(page, totalPages).map((item) =>
|
|
61
|
+
item.type === "gap" ? (
|
|
62
|
+
<span key={item.key} className="px-2 text-muted-foreground" aria-hidden="true">
|
|
63
|
+
...
|
|
64
|
+
</span>
|
|
65
|
+
) : (
|
|
66
|
+
<Button
|
|
67
|
+
key={item.value}
|
|
68
|
+
variant={item.value === page ? "default" : "outline"}
|
|
69
|
+
size="sm"
|
|
70
|
+
onClick={() => onPageChange(item.value)}
|
|
71
|
+
aria-label={`Page ${item.value}`}
|
|
72
|
+
aria-current={item.value === page ? "page" : undefined}
|
|
73
|
+
>
|
|
74
|
+
{item.value}
|
|
75
|
+
</Button>
|
|
76
|
+
),
|
|
77
|
+
)}
|
|
78
|
+
|
|
79
|
+
<Button
|
|
80
|
+
variant="outline"
|
|
81
|
+
size="sm"
|
|
82
|
+
onClick={() => onPageChange(page + 1)}
|
|
83
|
+
disabled={page >= totalPages}
|
|
84
|
+
aria-label="Next page"
|
|
85
|
+
>
|
|
86
|
+
Next
|
|
87
|
+
</Button>
|
|
88
|
+
</nav>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import { cn } from "../lib/utils";
|
|
5
|
+
|
|
6
|
+
const Popover = PopoverPrimitive.Root;
|
|
7
|
+
|
|
8
|
+
const PopoverTrigger = PopoverPrimitive.Trigger;
|
|
9
|
+
|
|
10
|
+
const PopoverContent = React.forwardRef<
|
|
11
|
+
React.ElementRef<typeof PopoverPrimitive.Content>,
|
|
12
|
+
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
|
13
|
+
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
|
14
|
+
<PopoverPrimitive.Portal>
|
|
15
|
+
<PopoverPrimitive.Content
|
|
16
|
+
ref={ref}
|
|
17
|
+
align={align}
|
|
18
|
+
sideOffset={sideOffset}
|
|
19
|
+
className={cn(
|
|
20
|
+
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-popover-content-transform-origin)",
|
|
21
|
+
className,
|
|
22
|
+
)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
</PopoverPrimitive.Portal>
|
|
26
|
+
));
|
|
27
|
+
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
|
28
|
+
|
|
29
|
+
export { Popover, PopoverContent, PopoverTrigger };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as ProgressPrimitive from "@radix-ui/react-progress";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import { cn } from "../lib/utils";
|
|
5
|
+
|
|
6
|
+
const Progress = React.forwardRef<
|
|
7
|
+
React.ElementRef<typeof ProgressPrimitive.Root>,
|
|
8
|
+
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root> & {
|
|
9
|
+
indicatorClassName?: string;
|
|
10
|
+
}
|
|
11
|
+
>(({ className, value, indicatorClassName, ...props }, ref) => (
|
|
12
|
+
<ProgressPrimitive.Root
|
|
13
|
+
ref={ref}
|
|
14
|
+
className={cn("relative h-4 w-full overflow-hidden rounded-full bg-secondary", className)}
|
|
15
|
+
{...props}
|
|
16
|
+
>
|
|
17
|
+
<ProgressPrimitive.Indicator
|
|
18
|
+
className={cn("h-full w-full flex-1 bg-primary transition-all", indicatorClassName)}
|
|
19
|
+
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
|
20
|
+
/>
|
|
21
|
+
</ProgressPrimitive.Root>
|
|
22
|
+
));
|
|
23
|
+
Progress.displayName = ProgressPrimitive.Root.displayName;
|
|
24
|
+
|
|
25
|
+
export { Progress };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Search } from "lucide-react";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
import { ClearButton } from "./clear-button";
|
|
5
|
+
import { Input } from "./input";
|
|
6
|
+
|
|
7
|
+
export interface SearchInputProps extends Omit<React.ComponentProps<"input">, "type" | "onChange"> {
|
|
8
|
+
value: string;
|
|
9
|
+
onValueChange: (value: string) => void;
|
|
10
|
+
onClear?: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const SearchInput = React.forwardRef<HTMLInputElement, SearchInputProps>(
|
|
14
|
+
(
|
|
15
|
+
{ className, value, onValueChange, onClear, onKeyDown, placeholder = "Search...", ...props },
|
|
16
|
+
ref,
|
|
17
|
+
) => {
|
|
18
|
+
const handleClear = React.useCallback(() => {
|
|
19
|
+
onValueChange("");
|
|
20
|
+
onClear?.();
|
|
21
|
+
}, [onValueChange, onClear]);
|
|
22
|
+
|
|
23
|
+
const handleKeyDown = React.useCallback(
|
|
24
|
+
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
25
|
+
if (e.key === "Escape" && value) {
|
|
26
|
+
e.preventDefault();
|
|
27
|
+
handleClear();
|
|
28
|
+
}
|
|
29
|
+
onKeyDown?.(e);
|
|
30
|
+
},
|
|
31
|
+
[value, handleClear, onKeyDown],
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className="relative w-full">
|
|
36
|
+
<Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
|
37
|
+
<Input
|
|
38
|
+
ref={ref}
|
|
39
|
+
type="text"
|
|
40
|
+
value={value}
|
|
41
|
+
onChange={(e) => onValueChange(e.target.value)}
|
|
42
|
+
onKeyDown={handleKeyDown}
|
|
43
|
+
placeholder={placeholder}
|
|
44
|
+
// White surface + soft lift so the search bar stands off the warm
|
|
45
|
+
// paper background (--background) rather than blending into it.
|
|
46
|
+
className={cn("bg-card shadow-sm", "pl-10", value ? "pr-10" : "pr-3", className)}
|
|
47
|
+
aria-label="Search"
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
{value && (
|
|
51
|
+
<ClearButton
|
|
52
|
+
onClick={handleClear}
|
|
53
|
+
className="absolute right-3 top-1/2 -translate-y-1/2"
|
|
54
|
+
aria-label="Clear search"
|
|
55
|
+
/>
|
|
56
|
+
)}
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
SearchInput.displayName = "SearchInput";
|
|
62
|
+
|
|
63
|
+
export { SearchInput };
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
2
|
+
import { Check, ChevronDown, ChevronUp } from "lucide-react";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
|
|
7
|
+
const Select = SelectPrimitive.Root;
|
|
8
|
+
|
|
9
|
+
const SelectGroup = SelectPrimitive.Group;
|
|
10
|
+
|
|
11
|
+
const SelectValue = SelectPrimitive.Value;
|
|
12
|
+
|
|
13
|
+
const SelectTrigger = React.forwardRef<
|
|
14
|
+
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
|
15
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
|
16
|
+
>(({ className, children, ...props }, ref) => (
|
|
17
|
+
<SelectPrimitive.Trigger
|
|
18
|
+
ref={ref}
|
|
19
|
+
className={cn(
|
|
20
|
+
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
|
21
|
+
className,
|
|
22
|
+
)}
|
|
23
|
+
{...props}
|
|
24
|
+
>
|
|
25
|
+
{children}
|
|
26
|
+
<SelectPrimitive.Icon asChild>
|
|
27
|
+
<ChevronDown className="h-4 w-4 opacity-50" />
|
|
28
|
+
</SelectPrimitive.Icon>
|
|
29
|
+
</SelectPrimitive.Trigger>
|
|
30
|
+
));
|
|
31
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
32
|
+
|
|
33
|
+
const SelectScrollUpButton = React.forwardRef<
|
|
34
|
+
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
|
35
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
|
36
|
+
>(({ className, ...props }, ref) => (
|
|
37
|
+
<SelectPrimitive.ScrollUpButton
|
|
38
|
+
ref={ref}
|
|
39
|
+
className={cn("flex cursor-default items-center justify-center py-1", className)}
|
|
40
|
+
{...props}
|
|
41
|
+
>
|
|
42
|
+
<ChevronUp className="h-4 w-4" />
|
|
43
|
+
</SelectPrimitive.ScrollUpButton>
|
|
44
|
+
));
|
|
45
|
+
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
46
|
+
|
|
47
|
+
const SelectScrollDownButton = React.forwardRef<
|
|
48
|
+
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
|
49
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
|
50
|
+
>(({ className, ...props }, ref) => (
|
|
51
|
+
<SelectPrimitive.ScrollDownButton
|
|
52
|
+
ref={ref}
|
|
53
|
+
className={cn("flex cursor-default items-center justify-center py-1", className)}
|
|
54
|
+
{...props}
|
|
55
|
+
>
|
|
56
|
+
<ChevronDown className="h-4 w-4" />
|
|
57
|
+
</SelectPrimitive.ScrollDownButton>
|
|
58
|
+
));
|
|
59
|
+
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
60
|
+
|
|
61
|
+
const SelectContent = React.forwardRef<
|
|
62
|
+
React.ElementRef<typeof SelectPrimitive.Content>,
|
|
63
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
|
64
|
+
>(({ className, children, position = "popper", ...props }, ref) => (
|
|
65
|
+
<SelectPrimitive.Portal>
|
|
66
|
+
<SelectPrimitive.Content
|
|
67
|
+
ref={ref}
|
|
68
|
+
className={cn(
|
|
69
|
+
"relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-select-content-transform-origin)",
|
|
70
|
+
position === "popper" &&
|
|
71
|
+
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
72
|
+
className,
|
|
73
|
+
)}
|
|
74
|
+
position={position}
|
|
75
|
+
{...props}
|
|
76
|
+
>
|
|
77
|
+
<SelectScrollUpButton />
|
|
78
|
+
<SelectPrimitive.Viewport
|
|
79
|
+
className={cn(
|
|
80
|
+
"p-1",
|
|
81
|
+
position === "popper" &&
|
|
82
|
+
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
|
|
83
|
+
)}
|
|
84
|
+
>
|
|
85
|
+
{children}
|
|
86
|
+
</SelectPrimitive.Viewport>
|
|
87
|
+
<SelectScrollDownButton />
|
|
88
|
+
</SelectPrimitive.Content>
|
|
89
|
+
</SelectPrimitive.Portal>
|
|
90
|
+
));
|
|
91
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
92
|
+
|
|
93
|
+
const SelectLabel = React.forwardRef<
|
|
94
|
+
React.ElementRef<typeof SelectPrimitive.Label>,
|
|
95
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
|
96
|
+
>(({ className, ...props }, ref) => (
|
|
97
|
+
<SelectPrimitive.Label
|
|
98
|
+
ref={ref}
|
|
99
|
+
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
|
|
100
|
+
{...props}
|
|
101
|
+
/>
|
|
102
|
+
));
|
|
103
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
104
|
+
|
|
105
|
+
const SelectItem = React.forwardRef<
|
|
106
|
+
React.ElementRef<typeof SelectPrimitive.Item>,
|
|
107
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
|
108
|
+
>(({ className, children, ...props }, ref) => (
|
|
109
|
+
<SelectPrimitive.Item
|
|
110
|
+
ref={ref}
|
|
111
|
+
className={cn(
|
|
112
|
+
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
113
|
+
className,
|
|
114
|
+
)}
|
|
115
|
+
{...props}
|
|
116
|
+
>
|
|
117
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
118
|
+
<SelectPrimitive.ItemIndicator>
|
|
119
|
+
<Check className="h-4 w-4" />
|
|
120
|
+
</SelectPrimitive.ItemIndicator>
|
|
121
|
+
</span>
|
|
122
|
+
|
|
123
|
+
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
|
124
|
+
</SelectPrimitive.Item>
|
|
125
|
+
));
|
|
126
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
127
|
+
|
|
128
|
+
const SelectSeparator = React.forwardRef<
|
|
129
|
+
React.ElementRef<typeof SelectPrimitive.Separator>,
|
|
130
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
|
131
|
+
>(({ className, ...props }, ref) => (
|
|
132
|
+
<SelectPrimitive.Separator
|
|
133
|
+
ref={ref}
|
|
134
|
+
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
|
135
|
+
{...props}
|
|
136
|
+
/>
|
|
137
|
+
));
|
|
138
|
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
139
|
+
|
|
140
|
+
export {
|
|
141
|
+
Select,
|
|
142
|
+
SelectContent,
|
|
143
|
+
SelectGroup,
|
|
144
|
+
SelectItem,
|
|
145
|
+
SelectLabel,
|
|
146
|
+
SelectScrollDownButton,
|
|
147
|
+
SelectScrollUpButton,
|
|
148
|
+
SelectSeparator,
|
|
149
|
+
SelectTrigger,
|
|
150
|
+
SelectValue,
|
|
151
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as SeparatorPrimitive from "@radix-ui/react-separator";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import { cn } from "../lib/utils";
|
|
5
|
+
|
|
6
|
+
const Separator = React.forwardRef<
|
|
7
|
+
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
|
8
|
+
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
|
9
|
+
>(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (
|
|
10
|
+
<SeparatorPrimitive.Root
|
|
11
|
+
ref={ref}
|
|
12
|
+
decorative={decorative}
|
|
13
|
+
orientation={orientation}
|
|
14
|
+
className={cn(
|
|
15
|
+
"shrink-0 bg-border",
|
|
16
|
+
orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
|
|
17
|
+
className,
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
));
|
|
22
|
+
Separator.displayName = SeparatorPrimitive.Root.displayName;
|
|
23
|
+
|
|
24
|
+
export { Separator };
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as SheetPrimitive from "@radix-ui/react-dialog";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { X } from "lucide-react";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import { cn } from "../lib/utils";
|
|
7
|
+
|
|
8
|
+
const Sheet = SheetPrimitive.Root;
|
|
9
|
+
|
|
10
|
+
const SheetTrigger = SheetPrimitive.Trigger;
|
|
11
|
+
|
|
12
|
+
const SheetClose = SheetPrimitive.Close;
|
|
13
|
+
|
|
14
|
+
const SheetPortal = SheetPrimitive.Portal;
|
|
15
|
+
|
|
16
|
+
const SheetOverlay = React.forwardRef<
|
|
17
|
+
React.ElementRef<typeof SheetPrimitive.Overlay>,
|
|
18
|
+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
|
|
19
|
+
>(({ className, ...props }, ref) => (
|
|
20
|
+
<SheetPrimitive.Overlay
|
|
21
|
+
className={cn(
|
|
22
|
+
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
23
|
+
className,
|
|
24
|
+
)}
|
|
25
|
+
{...props}
|
|
26
|
+
ref={ref}
|
|
27
|
+
/>
|
|
28
|
+
));
|
|
29
|
+
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
|
30
|
+
|
|
31
|
+
const sheetVariants = cva(
|
|
32
|
+
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
|
33
|
+
{
|
|
34
|
+
variants: {
|
|
35
|
+
side: {
|
|
36
|
+
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
|
|
37
|
+
bottom:
|
|
38
|
+
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
|
39
|
+
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
|
|
40
|
+
right:
|
|
41
|
+
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
defaultVariants: {
|
|
45
|
+
side: "right",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
interface SheetContentProps
|
|
51
|
+
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
|
|
52
|
+
VariantProps<typeof sheetVariants> {}
|
|
53
|
+
|
|
54
|
+
const SheetContent = React.forwardRef<
|
|
55
|
+
React.ElementRef<typeof SheetPrimitive.Content>,
|
|
56
|
+
SheetContentProps
|
|
57
|
+
>(({ side = "right", className, children, ...props }, ref) => (
|
|
58
|
+
<SheetPortal>
|
|
59
|
+
<SheetOverlay />
|
|
60
|
+
<SheetPrimitive.Content ref={ref} className={cn(sheetVariants({ side }), className)} {...props}>
|
|
61
|
+
{children}
|
|
62
|
+
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
|
|
63
|
+
<X className="h-4 w-4" />
|
|
64
|
+
<span className="sr-only">Close</span>
|
|
65
|
+
</SheetPrimitive.Close>
|
|
66
|
+
</SheetPrimitive.Content>
|
|
67
|
+
</SheetPortal>
|
|
68
|
+
));
|
|
69
|
+
SheetContent.displayName = SheetPrimitive.Content.displayName;
|
|
70
|
+
|
|
71
|
+
const SheetHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
|
72
|
+
<div className={cn("flex flex-col space-y-2 text-center sm:text-left", className)} {...props} />
|
|
73
|
+
);
|
|
74
|
+
SheetHeader.displayName = "SheetHeader";
|
|
75
|
+
|
|
76
|
+
const SheetFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
|
77
|
+
<div
|
|
78
|
+
className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
|
|
79
|
+
{...props}
|
|
80
|
+
/>
|
|
81
|
+
);
|
|
82
|
+
SheetFooter.displayName = "SheetFooter";
|
|
83
|
+
|
|
84
|
+
const SheetTitle = React.forwardRef<
|
|
85
|
+
React.ElementRef<typeof SheetPrimitive.Title>,
|
|
86
|
+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
|
|
87
|
+
>(({ className, ...props }, ref) => (
|
|
88
|
+
<SheetPrimitive.Title
|
|
89
|
+
ref={ref}
|
|
90
|
+
className={cn("text-lg font-semibold text-foreground", className)}
|
|
91
|
+
{...props}
|
|
92
|
+
/>
|
|
93
|
+
));
|
|
94
|
+
SheetTitle.displayName = SheetPrimitive.Title.displayName;
|
|
95
|
+
|
|
96
|
+
const SheetDescription = React.forwardRef<
|
|
97
|
+
React.ElementRef<typeof SheetPrimitive.Description>,
|
|
98
|
+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
|
|
99
|
+
>(({ className, ...props }, ref) => (
|
|
100
|
+
<SheetPrimitive.Description
|
|
101
|
+
ref={ref}
|
|
102
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
103
|
+
{...props}
|
|
104
|
+
/>
|
|
105
|
+
));
|
|
106
|
+
SheetDescription.displayName = SheetPrimitive.Description.displayName;
|
|
107
|
+
|
|
108
|
+
export {
|
|
109
|
+
Sheet,
|
|
110
|
+
SheetClose,
|
|
111
|
+
SheetContent,
|
|
112
|
+
SheetDescription,
|
|
113
|
+
SheetFooter,
|
|
114
|
+
SheetHeader,
|
|
115
|
+
SheetOverlay,
|
|
116
|
+
SheetPortal,
|
|
117
|
+
SheetTitle,
|
|
118
|
+
SheetTrigger,
|
|
119
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as SliderPrimitive from "@radix-ui/react-slider";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import { cn } from "../lib/utils";
|
|
5
|
+
|
|
6
|
+
const Slider = React.forwardRef<
|
|
7
|
+
React.ElementRef<typeof SliderPrimitive.Root>,
|
|
8
|
+
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
|
|
9
|
+
>(({ className, ...props }, ref) => (
|
|
10
|
+
<SliderPrimitive.Root
|
|
11
|
+
ref={ref}
|
|
12
|
+
className={cn("relative flex w-full touch-none select-none items-center", className)}
|
|
13
|
+
{...props}
|
|
14
|
+
>
|
|
15
|
+
<SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary">
|
|
16
|
+
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
|
17
|
+
</SliderPrimitive.Track>
|
|
18
|
+
<SliderPrimitive.Thumb className="block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50" />
|
|
19
|
+
</SliderPrimitive.Root>
|
|
20
|
+
));
|
|
21
|
+
Slider.displayName = SliderPrimitive.Root.displayName;
|
|
22
|
+
|
|
23
|
+
export { Slider };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as SwitchPrimitives from "@radix-ui/react-switch";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import { cn } from "../lib/utils";
|
|
5
|
+
|
|
6
|
+
const Switch = React.forwardRef<
|
|
7
|
+
React.ElementRef<typeof SwitchPrimitives.Root>,
|
|
8
|
+
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
|
|
9
|
+
>(({ className, ...props }, ref) => (
|
|
10
|
+
<SwitchPrimitives.Root
|
|
11
|
+
className={cn(
|
|
12
|
+
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
|
|
13
|
+
className,
|
|
14
|
+
)}
|
|
15
|
+
{...props}
|
|
16
|
+
ref={ref}
|
|
17
|
+
>
|
|
18
|
+
<SwitchPrimitives.Thumb
|
|
19
|
+
className={cn(
|
|
20
|
+
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0",
|
|
21
|
+
)}
|
|
22
|
+
/>
|
|
23
|
+
</SwitchPrimitives.Root>
|
|
24
|
+
));
|
|
25
|
+
Switch.displayName = SwitchPrimitives.Root.displayName;
|
|
26
|
+
|
|
27
|
+
export { Switch };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
|
|
5
|
+
const Textarea = React.forwardRef<HTMLTextAreaElement, React.ComponentProps<"textarea">>(
|
|
6
|
+
({ className, ...props }, ref) => {
|
|
7
|
+
return (
|
|
8
|
+
<textarea
|
|
9
|
+
className={cn(
|
|
10
|
+
"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
11
|
+
className,
|
|
12
|
+
)}
|
|
13
|
+
ref={ref}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
},
|
|
18
|
+
);
|
|
19
|
+
Textarea.displayName = "Textarea";
|
|
20
|
+
|
|
21
|
+
export { Textarea };
|