@atelic-action/ui 0.17.2 → 1.0.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.
Files changed (184) hide show
  1. package/README.md +428 -44
  2. package/bin/atelic-check-base-path.ts +5 -0
  3. package/bin/atelic-clean-sitemap.ts +5 -0
  4. package/bin/atelic-contact-canary.ts +5 -0
  5. package/bin/atelic-print-sheets.sh +1 -1
  6. package/bin/atelic-seal-document.mjs +1 -1
  7. package/bin/atelic-serve-static.ts +1 -1
  8. package/dist/gate/index.d.ts +1 -1
  9. package/dist/gate/index.js +1 -1
  10. package/dist/gate/middleware.d.ts +4 -1
  11. package/dist/gate/middleware.js +14 -5
  12. package/dist/gate/wall.d.ts +25 -0
  13. package/dist/gate/wall.js +41 -2
  14. package/dist/routing/basePath.d.ts +24 -0
  15. package/dist/routing/basePath.js +54 -0
  16. package/dist/vite/index.d.ts +3 -2
  17. package/dist/vite/index.js +3 -2
  18. package/dist/vite/marketing-config.d.ts +50 -0
  19. package/dist/vite/marketing-config.js +83 -0
  20. package/package.json +164 -5
  21. package/src/app/AppProvider.tsx +41 -0
  22. package/src/app/AuthGuard.tsx +27 -0
  23. package/src/app/CardErrorBoundary.tsx +39 -0
  24. package/src/app/ErrorFallback.tsx +39 -0
  25. package/src/app/GoogleGlyph.tsx +22 -0
  26. package/src/app/config.ts +54 -0
  27. package/src/app/configure.ts +18 -0
  28. package/src/app/context/AppContext.tsx +52 -0
  29. package/src/app/context/SidebarContext.tsx +124 -0
  30. package/src/app/context/UserContext.tsx +139 -0
  31. package/src/app/hooks/useDebounce.ts +12 -0
  32. package/src/app/hooks/useIsDesktop.ts +31 -0
  33. package/src/app/hooks/useWakeLock.ts +49 -0
  34. package/src/app/index.ts +64 -0
  35. package/src/app/layout/ActionButton.tsx +67 -0
  36. package/src/app/layout/AppLayout.tsx +110 -0
  37. package/src/app/layout/AppPage.tsx +268 -0
  38. package/src/app/layout/Breadcrumbs.tsx +57 -0
  39. package/src/app/layout/DesktopRail.tsx +168 -0
  40. package/src/app/layout/FormActionButtons.tsx +69 -0
  41. package/src/app/layout/MobileMenu.tsx +156 -0
  42. package/src/app/layout/MobileTabBar.tsx +43 -0
  43. package/src/app/layout/PageError.tsx +30 -0
  44. package/src/app/layout/PageSkeleton.tsx +65 -0
  45. package/src/app/layout/SectionCards.tsx +33 -0
  46. package/src/app/layout/UserMenu.tsx +104 -0
  47. package/src/app/layout/identity.ts +22 -0
  48. package/src/app/layout/index.ts +20 -0
  49. package/src/app/layout/nav.ts +52 -0
  50. package/src/app/lib/dates.ts +57 -0
  51. package/src/app/lib/env.ts +37 -0
  52. package/src/app/lib/format.ts +61 -0
  53. package/src/app/lib/http.ts +205 -0
  54. package/src/app/lib/lastAuth.ts +45 -0
  55. package/src/app/lib/oauth.ts +32 -0
  56. package/src/app/lib/publicRoutes.ts +33 -0
  57. package/src/app/lib/queryClient.ts +38 -0
  58. package/src/app/lib/search.ts +17 -0
  59. package/src/app/lib/session.ts +36 -0
  60. package/src/app/lib/tokenRefresh.ts +137 -0
  61. package/src/app/lib/transformKeys.ts +60 -0
  62. package/src/app/lib/units.ts +228 -0
  63. package/src/app/lib/unwrap.ts +48 -0
  64. package/src/app/lib/utils.ts +10 -0
  65. package/src/app/stores/authStore.ts +80 -0
  66. package/src/app/stores/storage.ts +40 -0
  67. package/src/app/stores/unitsStore.ts +54 -0
  68. package/src/app/types/index.ts +72 -0
  69. package/src/app/types/query.ts +11 -0
  70. package/src/app/ui/accordion.tsx +54 -0
  71. package/src/app/ui/alert-dialog.tsx +114 -0
  72. package/src/app/ui/alert.tsx +49 -0
  73. package/src/app/ui/avatar.tsx +45 -0
  74. package/src/app/ui/badge-list.tsx +51 -0
  75. package/src/app/ui/badge.tsx +33 -0
  76. package/src/app/ui/button.tsx +49 -0
  77. package/src/app/ui/callout.tsx +22 -0
  78. package/src/app/ui/card.tsx +55 -0
  79. package/src/app/ui/checkbox.tsx +26 -0
  80. package/src/app/ui/clear-button.tsx +42 -0
  81. package/src/app/ui/command.tsx +140 -0
  82. package/src/app/ui/country-dropdown.tsx +184 -0
  83. package/src/app/ui/dialog.tsx +104 -0
  84. package/src/app/ui/dropdown-menu.tsx +185 -0
  85. package/src/app/ui/form.tsx +172 -0
  86. package/src/app/ui/index.ts +36 -0
  87. package/src/app/ui/input.tsx +22 -0
  88. package/src/app/ui/label.tsx +19 -0
  89. package/src/app/ui/option-select.tsx +56 -0
  90. package/src/app/ui/pagination.tsx +90 -0
  91. package/src/app/ui/popover.tsx +29 -0
  92. package/src/app/ui/progress.tsx +25 -0
  93. package/src/app/ui/search-input.tsx +63 -0
  94. package/src/app/ui/select.tsx +151 -0
  95. package/src/app/ui/separator.tsx +24 -0
  96. package/src/app/ui/sheet.tsx +119 -0
  97. package/src/app/ui/slider.tsx +23 -0
  98. package/src/app/ui/switch.tsx +27 -0
  99. package/src/app/ui/textarea.tsx +21 -0
  100. package/src/app/ui/tooltip.tsx +32 -0
  101. package/src/artifact/ArtifactShell.tsx +2 -2
  102. package/src/artifact/Gate.tsx +2 -2
  103. package/src/artifact/Writeup.tsx +7 -7
  104. package/src/chrome/SiteShell.tsx +141 -0
  105. package/src/chrome/index.ts +2 -0
  106. package/src/components/Disclosure.tsx +80 -14
  107. package/src/components/GateLogin.tsx +1 -1
  108. package/src/components/Watermark.tsx +1 -1
  109. package/src/components/index.ts +6 -2
  110. package/src/gate/index.ts +1 -1
  111. package/src/gate/middleware.ts +17 -6
  112. package/src/gate/wall.ts +60 -2
  113. package/src/lib/basePathAudit.ts +96 -0
  114. package/src/lib/displayDate.ts +25 -0
  115. package/src/lib/honeypot.ts +11 -0
  116. package/src/lib/hours.ts +28 -0
  117. package/src/lib/sitemap.ts +34 -0
  118. package/src/lib/trackEvent.ts +26 -0
  119. package/src/lib/useOpenHeight.ts +28 -0
  120. package/src/primitives/Button.tsx +3 -1
  121. package/src/primitives/Chip.tsx +3 -1
  122. package/src/primitives/ImageFrame.tsx +48 -0
  123. package/src/primitives/ProseBlocks.tsx +67 -0
  124. package/src/primitives/QuoteCard.tsx +20 -0
  125. package/src/primitives/StarRow.tsx +37 -0
  126. package/src/primitives/TickList.tsx +24 -0
  127. package/src/primitives/index.ts +5 -0
  128. package/src/routing/BasePathProvider.tsx +47 -0
  129. package/src/routing/basePath.ts +57 -0
  130. package/src/routing/index.ts +8 -0
  131. package/src/scripts/check-base-path.ts +130 -0
  132. package/src/scripts/clean-sitemap.ts +27 -0
  133. package/src/scripts/contact-canary.ts +98 -0
  134. package/src/scripts/serve-static.ts +29 -3
  135. package/src/sections/BlogIndex.tsx +57 -0
  136. package/src/sections/BlogPost.tsx +65 -0
  137. package/src/sections/CTABanner.tsx +93 -0
  138. package/src/sections/CTASection.tsx +60 -0
  139. package/src/sections/ComparisonTable.tsx +114 -0
  140. package/src/sections/ContactForm.tsx +163 -0
  141. package/src/sections/ContactInfoCard.tsx +92 -0
  142. package/src/sections/CredentialList.tsx +28 -0
  143. package/src/sections/DeviceShowcase.tsx +39 -0
  144. package/src/sections/ExpandList.tsx +58 -0
  145. package/src/sections/FAQBrowser.tsx +283 -0
  146. package/src/sections/FeatureDetail.tsx +60 -0
  147. package/src/sections/FeatureGrid.tsx +83 -0
  148. package/src/sections/Gallery.tsx +84 -0
  149. package/src/sections/Hero.tsx +110 -0
  150. package/src/sections/HoursTable.tsx +23 -0
  151. package/src/sections/JumpRail.tsx +35 -0
  152. package/src/sections/LegalPage.tsx +43 -0
  153. package/src/sections/LocationSection.tsx +66 -0
  154. package/src/sections/Locations.tsx +71 -0
  155. package/src/sections/LogoMarquee.tsx +27 -0
  156. package/src/sections/MapEmbed.tsx +75 -0
  157. package/src/sections/MediaBand.tsx +20 -0
  158. package/src/sections/MenuList.tsx +44 -0
  159. package/src/sections/PageHero.tsx +25 -0
  160. package/src/sections/PressQuotes.tsx +51 -0
  161. package/src/sections/PricingBand.tsx +43 -0
  162. package/src/sections/PricingCard.tsx +36 -0
  163. package/src/sections/ProcessSteps.tsx +45 -0
  164. package/src/sections/ProfileCards.tsx +73 -0
  165. package/src/sections/RevealSlider.tsx +156 -0
  166. package/src/sections/ReviewCarousel.tsx +223 -0
  167. package/src/sections/ReviewWall.tsx +220 -0
  168. package/src/sections/SocialStrip.tsx +83 -0
  169. package/src/sections/SplitSection.tsx +56 -0
  170. package/src/sections/index.ts +76 -0
  171. package/src/sections/siteConfig.ts +201 -0
  172. package/src/styles/app.css +673 -0
  173. package/src/styles/artifact.css +472 -399
  174. package/src/styles/base.css +49 -47
  175. package/src/styles/chrome.css +71 -71
  176. package/src/styles/components.css +150 -140
  177. package/src/styles/gate.css +29 -29
  178. package/src/styles/primitives.css +190 -25
  179. package/src/styles/sections.css +2652 -0
  180. package/src/styles/star-rating.css +2 -2
  181. package/src/vite/index.ts +9 -2
  182. package/src/vite/marketing-config.ts +128 -0
  183. package/src/components/Collapsible.tsx +0 -43
  184. /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 };