create-strayl-web-app 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 (78) hide show
  1. package/dist/index.js +59 -0
  2. package/dist/index.js.map +1 -0
  3. package/package.json +39 -0
  4. package/template/README.md +290 -0
  5. package/template/components.json +24 -0
  6. package/template/package.json +56 -0
  7. package/template/public/favicon.ico +0 -0
  8. package/template/public/google-logo.svg +6 -0
  9. package/template/public/logo-dark.ico +0 -0
  10. package/template/public/logo-dark.webp +0 -0
  11. package/template/public/logo-light.ico +0 -0
  12. package/template/public/logo-light.webp +0 -0
  13. package/template/public/manifest.json +16 -0
  14. package/template/public/robots.txt +3 -0
  15. package/template/src/components/Header.tsx +76 -0
  16. package/template/src/components/language-switcher.tsx +38 -0
  17. package/template/src/components/theme-provider.tsx +14 -0
  18. package/template/src/components/themed-logo.tsx +44 -0
  19. package/template/src/components/ui/accordion.tsx +69 -0
  20. package/template/src/components/ui/alert-dialog.tsx +169 -0
  21. package/template/src/components/ui/alert.tsx +80 -0
  22. package/template/src/components/ui/autocomplete.tsx +301 -0
  23. package/template/src/components/ui/avatar.tsx +46 -0
  24. package/template/src/components/ui/badge.tsx +60 -0
  25. package/template/src/components/ui/breadcrumb.tsx +112 -0
  26. package/template/src/components/ui/button.tsx +73 -0
  27. package/template/src/components/ui/card.tsx +244 -0
  28. package/template/src/components/ui/checkbox-group.tsx +16 -0
  29. package/template/src/components/ui/checkbox.tsx +60 -0
  30. package/template/src/components/ui/collapsible.tsx +45 -0
  31. package/template/src/components/ui/combobox.tsx +415 -0
  32. package/template/src/components/ui/command.tsx +264 -0
  33. package/template/src/components/ui/dialog.tsx +196 -0
  34. package/template/src/components/ui/empty.tsx +127 -0
  35. package/template/src/components/ui/field.tsx +74 -0
  36. package/template/src/components/ui/fieldset.tsx +29 -0
  37. package/template/src/components/ui/form.tsx +17 -0
  38. package/template/src/components/ui/frame.tsx +82 -0
  39. package/template/src/components/ui/group.tsx +97 -0
  40. package/template/src/components/ui/input-group.tsx +101 -0
  41. package/template/src/components/ui/input.tsx +66 -0
  42. package/template/src/components/ui/kbd.tsx +28 -0
  43. package/template/src/components/ui/label.tsx +28 -0
  44. package/template/src/components/ui/menu.tsx +310 -0
  45. package/template/src/components/ui/meter.tsx +67 -0
  46. package/template/src/components/ui/number-field.tsx +160 -0
  47. package/template/src/components/ui/pagination.tsx +136 -0
  48. package/template/src/components/ui/popover.tsx +104 -0
  49. package/template/src/components/ui/preview-card.tsx +55 -0
  50. package/template/src/components/ui/progress.tsx +81 -0
  51. package/template/src/components/ui/radio-group.tsx +36 -0
  52. package/template/src/components/ui/scroll-area.tsx +64 -0
  53. package/template/src/components/ui/select.tsx +180 -0
  54. package/template/src/components/ui/separator.tsx +23 -0
  55. package/template/src/components/ui/sheet.tsx +203 -0
  56. package/template/src/components/ui/sidebar.tsx +743 -0
  57. package/template/src/components/ui/skeleton.tsx +16 -0
  58. package/template/src/components/ui/slider.tsx +74 -0
  59. package/template/src/components/ui/spinner.tsx +18 -0
  60. package/template/src/components/ui/switch.tsx +27 -0
  61. package/template/src/components/ui/table.tsx +126 -0
  62. package/template/src/components/ui/tabs.tsx +87 -0
  63. package/template/src/components/ui/textarea.tsx +51 -0
  64. package/template/src/components/ui/toast.tsx +269 -0
  65. package/template/src/components/ui/toggle-group.tsx +102 -0
  66. package/template/src/components/ui/toggle.tsx +45 -0
  67. package/template/src/components/ui/toolbar.tsx +83 -0
  68. package/template/src/components/ui/tooltip.tsx +65 -0
  69. package/template/src/hooks/use-mobile.ts +21 -0
  70. package/template/src/lib/i18n.ts +70 -0
  71. package/template/src/lib/utils.ts +6 -0
  72. package/template/src/routeTree.gen.ts +68 -0
  73. package/template/src/router.tsx +17 -0
  74. package/template/src/routes/__root.tsx +62 -0
  75. package/template/src/routes/index.tsx +71 -0
  76. package/template/src/styles.css +121 -0
  77. package/template/tsconfig.json +28 -0
  78. package/template/vite.config.ts +30 -0
@@ -0,0 +1,160 @@
1
+ "use client";
2
+
3
+ import { NumberField as NumberFieldPrimitive } from "@base-ui/react/number-field";
4
+ import { MinusIcon, PlusIcon } from "lucide-react";
5
+ import * as React from "react";
6
+
7
+ import { cn } from "@/lib/utils";
8
+ import { Label } from "@/components/ui/label";
9
+
10
+ const NumberFieldContext = React.createContext<{
11
+ fieldId: string;
12
+ } | null>(null);
13
+
14
+ function NumberField({
15
+ id,
16
+ className,
17
+ size = "default",
18
+ ...props
19
+ }: NumberFieldPrimitive.Root.Props & {
20
+ size?: "sm" | "default" | "lg";
21
+ }) {
22
+ const generatedId = React.useId();
23
+ const fieldId = id ?? generatedId;
24
+
25
+ return (
26
+ <NumberFieldContext.Provider value={{ fieldId }}>
27
+ <NumberFieldPrimitive.Root
28
+ className={cn("flex w-full flex-col items-start gap-2", className)}
29
+ data-size={size}
30
+ data-slot="number-field"
31
+ id={fieldId}
32
+ {...props}
33
+ />
34
+ </NumberFieldContext.Provider>
35
+ );
36
+ }
37
+
38
+ function NumberFieldGroup({
39
+ className,
40
+ ...props
41
+ }: NumberFieldPrimitive.Group.Props) {
42
+ return (
43
+ <NumberFieldPrimitive.Group
44
+ className={cn(
45
+ "relative flex w-full justify-between rounded-lg border border-input bg-background not-dark:bg-clip-padding text-base text-foreground shadow-xs/5 ring-ring/24 transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-data-disabled:not-focus-within:not-aria-invalid:before:shadow-[0_1px_--theme(--color-black/6%)] focus-within:border-ring focus-within:ring-[3px] has-aria-invalid:border-destructive/36 focus-within:has-aria-invalid:border-destructive/64 focus-within:has-aria-invalid:ring-destructive/48 data-disabled:pointer-events-none data-disabled:opacity-64 sm:text-sm dark:bg-input/32 dark:has-aria-invalid:ring-destructive/24 dark:not-data-disabled:not-focus-within:not-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)] [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0 [[data-disabled],:focus-within,[aria-invalid]]:shadow-none",
46
+ className,
47
+ )}
48
+ data-slot="number-field-group"
49
+ {...props}
50
+ />
51
+ );
52
+ }
53
+
54
+ function NumberFieldDecrement({
55
+ className,
56
+ ...props
57
+ }: NumberFieldPrimitive.Decrement.Props) {
58
+ return (
59
+ <NumberFieldPrimitive.Decrement
60
+ className={cn(
61
+ "relative flex shrink-0 cursor-pointer items-center justify-center rounded-s-[calc(var(--radius-lg)-1px)] in-data-[size=sm]:px-[calc(--spacing(2.5)-1px)] px-[calc(--spacing(3)-1px)] transition-colors pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:bg-accent",
62
+ className,
63
+ )}
64
+ data-slot="number-field-decrement"
65
+ {...props}
66
+ >
67
+ <MinusIcon />
68
+ </NumberFieldPrimitive.Decrement>
69
+ );
70
+ }
71
+
72
+ function NumberFieldIncrement({
73
+ className,
74
+ ...props
75
+ }: NumberFieldPrimitive.Increment.Props) {
76
+ return (
77
+ <NumberFieldPrimitive.Increment
78
+ className={cn(
79
+ "relative flex shrink-0 cursor-pointer items-center justify-center rounded-e-[calc(var(--radius-lg)-1px)] in-data-[size=sm]:px-[calc(--spacing(2.5)-1px)] px-[calc(--spacing(3)-1px)] transition-colors pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:bg-accent",
80
+ className,
81
+ )}
82
+ data-slot="number-field-increment"
83
+ {...props}
84
+ >
85
+ <PlusIcon />
86
+ </NumberFieldPrimitive.Increment>
87
+ );
88
+ }
89
+
90
+ function NumberFieldInput({
91
+ className,
92
+ ...props
93
+ }: NumberFieldPrimitive.Input.Props) {
94
+ return (
95
+ <NumberFieldPrimitive.Input
96
+ className={cn(
97
+ "h-8.5 in-data-[size=lg]:h-9.5 in-data-[size=sm]:h-7.5 w-full min-w-0 grow bg-transparent in-data-[size=sm]:px-[calc(--spacing(2.5)-1px)] px-[calc(--spacing(3)-1px)] text-center tabular-nums in-data-[size=lg]:leading-9.5 in-data-[size=sm]:leading-7.5 leading-8.5 outline-none sm:h-7.5 sm:in-data-[size=lg]:h-8.5 sm:in-data-[size=sm]:h-6.5 sm:in-data-[size=lg]:leading-8.5 sm:in-data-[size=sm]:leading-8.5 sm:leading-7.5",
98
+ className,
99
+ )}
100
+ data-slot="number-field-input"
101
+ {...props}
102
+ />
103
+ );
104
+ }
105
+
106
+ function NumberFieldScrubArea({
107
+ className,
108
+ label,
109
+ ...props
110
+ }: NumberFieldPrimitive.ScrubArea.Props & {
111
+ label: string;
112
+ }) {
113
+ const context = React.useContext(NumberFieldContext);
114
+
115
+ if (!context) {
116
+ throw new Error(
117
+ "NumberFieldScrubArea must be used within a NumberField component for accessibility.",
118
+ );
119
+ }
120
+
121
+ return (
122
+ <NumberFieldPrimitive.ScrubArea
123
+ className={cn("flex cursor-ew-resize", className)}
124
+ data-slot="number-field-scrub-area"
125
+ {...props}
126
+ >
127
+ <Label className="cursor-ew-resize" htmlFor={context.fieldId}>
128
+ {label}
129
+ </Label>
130
+ <NumberFieldPrimitive.ScrubAreaCursor className="drop-shadow-[0_1px_1px_#0008] filter">
131
+ <CursorGrowIcon />
132
+ </NumberFieldPrimitive.ScrubAreaCursor>
133
+ </NumberFieldPrimitive.ScrubArea>
134
+ );
135
+ }
136
+
137
+ function CursorGrowIcon(props: React.ComponentProps<"svg">) {
138
+ return (
139
+ <svg
140
+ fill="black"
141
+ height="14"
142
+ stroke="white"
143
+ viewBox="0 0 24 14"
144
+ width="26"
145
+ xmlns="http://www.w3.org/2000/svg"
146
+ {...props}
147
+ >
148
+ <path d="M19.5 5.5L6.49737 5.51844V2L1 6.9999L6.5 12L6.49737 8.5L19.5 8.5V12L25 6.9999L19.5 2V5.5Z" />
149
+ </svg>
150
+ );
151
+ }
152
+
153
+ export {
154
+ NumberField,
155
+ NumberFieldScrubArea,
156
+ NumberFieldDecrement,
157
+ NumberFieldIncrement,
158
+ NumberFieldGroup,
159
+ NumberFieldInput,
160
+ };
@@ -0,0 +1,136 @@
1
+ "use client";
2
+
3
+ import { mergeProps } from "@base-ui/react/merge-props";
4
+ import { useRender } from "@base-ui/react/use-render";
5
+ import {
6
+ ChevronLeftIcon,
7
+ ChevronRightIcon,
8
+ MoreHorizontalIcon,
9
+ } from "lucide-react";
10
+ import type * as React from "react";
11
+
12
+ import { cn } from "@/lib/utils";
13
+ import { type Button, buttonVariants } from "@/components/ui/button";
14
+
15
+ function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
16
+ return (
17
+ <nav
18
+ aria-label="pagination"
19
+ className={cn("mx-auto flex w-full justify-center", className)}
20
+ data-slot="pagination"
21
+ {...props}
22
+ />
23
+ );
24
+ }
25
+
26
+ function PaginationContent({
27
+ className,
28
+ ...props
29
+ }: React.ComponentProps<"ul">) {
30
+ return (
31
+ <ul
32
+ className={cn("flex flex-row items-center gap-1", className)}
33
+ data-slot="pagination-content"
34
+ {...props}
35
+ />
36
+ );
37
+ }
38
+
39
+ function PaginationItem({ ...props }: React.ComponentProps<"li">) {
40
+ return <li data-slot="pagination-item" {...props} />;
41
+ }
42
+
43
+ type PaginationLinkProps = {
44
+ isActive?: boolean;
45
+ size?: React.ComponentProps<typeof Button>["size"];
46
+ } & useRender.ComponentProps<"a">;
47
+
48
+ function PaginationLink({
49
+ className,
50
+ isActive,
51
+ size = "icon",
52
+ render,
53
+ ...props
54
+ }: PaginationLinkProps) {
55
+ const defaultProps = {
56
+ "aria-current": isActive ? ("page" as const) : undefined,
57
+ className: render
58
+ ? className
59
+ : cn(
60
+ buttonVariants({
61
+ size,
62
+ variant: isActive ? "outline" : "ghost",
63
+ }),
64
+ className,
65
+ ),
66
+ "data-active": isActive,
67
+ "data-slot": "pagination-link",
68
+ };
69
+
70
+ return useRender({
71
+ defaultTagName: "a",
72
+ props: mergeProps<"a">(defaultProps, props),
73
+ render,
74
+ });
75
+ }
76
+
77
+ function PaginationPrevious({
78
+ className,
79
+ ...props
80
+ }: React.ComponentProps<typeof PaginationLink>) {
81
+ return (
82
+ <PaginationLink
83
+ aria-label="Go to previous page"
84
+ className={cn("max-sm:aspect-square max-sm:p-0", className)}
85
+ size="default"
86
+ {...props}
87
+ >
88
+ <ChevronLeftIcon className="sm:-ms-1" />
89
+ <span className="max-sm:hidden">Previous</span>
90
+ </PaginationLink>
91
+ );
92
+ }
93
+
94
+ function PaginationNext({
95
+ className,
96
+ ...props
97
+ }: React.ComponentProps<typeof PaginationLink>) {
98
+ return (
99
+ <PaginationLink
100
+ aria-label="Go to next page"
101
+ className={cn("max-sm:aspect-square max-sm:p-0", className)}
102
+ size="default"
103
+ {...props}
104
+ >
105
+ <span className="max-sm:hidden">Next</span>
106
+ <ChevronRightIcon className="sm:-me-1" />
107
+ </PaginationLink>
108
+ );
109
+ }
110
+
111
+ function PaginationEllipsis({
112
+ className,
113
+ ...props
114
+ }: React.ComponentProps<"span">) {
115
+ return (
116
+ <span
117
+ aria-hidden
118
+ className={cn("flex min-w-7 justify-center", className)}
119
+ data-slot="pagination-ellipsis"
120
+ {...props}
121
+ >
122
+ <MoreHorizontalIcon className="size-5 sm:size-4" />
123
+ <span className="sr-only">More pages</span>
124
+ </span>
125
+ );
126
+ }
127
+
128
+ export {
129
+ Pagination,
130
+ PaginationContent,
131
+ PaginationLink,
132
+ PaginationItem,
133
+ PaginationPrevious,
134
+ PaginationNext,
135
+ PaginationEllipsis,
136
+ };
@@ -0,0 +1,104 @@
1
+ "use client";
2
+
3
+ import { Popover as PopoverPrimitive } from "@base-ui/react/popover";
4
+
5
+ import { cn } from "@/lib/utils";
6
+
7
+ const PopoverCreateHandle = PopoverPrimitive.createHandle;
8
+
9
+ const Popover = PopoverPrimitive.Root;
10
+
11
+ function PopoverTrigger(props: PopoverPrimitive.Trigger.Props) {
12
+ return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
13
+ }
14
+
15
+ function PopoverPopup({
16
+ children,
17
+ className,
18
+ side = "bottom",
19
+ align = "center",
20
+ sideOffset = 4,
21
+ alignOffset = 0,
22
+ tooltipStyle = false,
23
+ ...props
24
+ }: PopoverPrimitive.Popup.Props & {
25
+ side?: PopoverPrimitive.Positioner.Props["side"];
26
+ align?: PopoverPrimitive.Positioner.Props["align"];
27
+ sideOffset?: PopoverPrimitive.Positioner.Props["sideOffset"];
28
+ alignOffset?: PopoverPrimitive.Positioner.Props["alignOffset"];
29
+ tooltipStyle?: boolean;
30
+ }) {
31
+ return (
32
+ <PopoverPrimitive.Portal>
33
+ <PopoverPrimitive.Positioner
34
+ align={align}
35
+ alignOffset={alignOffset}
36
+ className="z-50 h-(--positioner-height) w-(--positioner-width) max-w-(--available-width) transition-[top,left,right,bottom,transform] data-instant:transition-none"
37
+ data-slot="popover-positioner"
38
+ side={side}
39
+ sideOffset={sideOffset}
40
+ >
41
+ <PopoverPrimitive.Popup
42
+ className={cn(
43
+ "relative flex h-(--popup-height,auto) w-(--popup-width,auto) origin-(--transform-origin) rounded-lg border bg-popover not-dark:bg-clip-padding text-popover-foreground shadow-lg/5 transition-[width,height,scale,opacity] before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] before:shadow-[0_1px_--theme(--color-black/6%)] data-starting-style:scale-98 data-starting-style:opacity-0 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]",
44
+ tooltipStyle &&
45
+ "w-fit text-balance rounded-md text-xs shadow-md/5 before:rounded-[calc(var(--radius-md)-1px)]",
46
+ className,
47
+ )}
48
+ data-slot="popover-popup"
49
+ {...props}
50
+ >
51
+ <PopoverPrimitive.Viewport
52
+ className={cn(
53
+ "relative size-full max-h-(--available-height) overflow-clip px-(--viewport-inline-padding) py-4 outline-none [--viewport-inline-padding:--spacing(4)] data-instant:transition-none **:data-current:data-ending-style:opacity-0 **:data-current:data-starting-style:opacity-0 **:data-previous:data-ending-style:opacity-0 **:data-previous:data-starting-style:opacity-0 **:data-current:w-[calc(var(--popup-width)-2*var(--viewport-inline-padding)-2px)] **:data-previous:w-[calc(var(--popup-width)-2*var(--viewport-inline-padding)-2px)] **:data-current:opacity-100 **:data-previous:opacity-100 **:data-current:transition-opacity **:data-previous:transition-opacity",
54
+ tooltipStyle
55
+ ? "py-1 [--viewport-inline-padding:--spacing(2)]"
56
+ : "not-data-transitioning:overflow-y-auto",
57
+ )}
58
+ data-slot="popover-viewport"
59
+ >
60
+ {children}
61
+ </PopoverPrimitive.Viewport>
62
+ </PopoverPrimitive.Popup>
63
+ </PopoverPrimitive.Positioner>
64
+ </PopoverPrimitive.Portal>
65
+ );
66
+ }
67
+
68
+ function PopoverClose({ ...props }: PopoverPrimitive.Close.Props) {
69
+ return <PopoverPrimitive.Close data-slot="popover-close" {...props} />;
70
+ }
71
+
72
+ function PopoverTitle({ className, ...props }: PopoverPrimitive.Title.Props) {
73
+ return (
74
+ <PopoverPrimitive.Title
75
+ className={cn("font-semibold text-lg leading-none", className)}
76
+ data-slot="popover-title"
77
+ {...props}
78
+ />
79
+ );
80
+ }
81
+
82
+ function PopoverDescription({
83
+ className,
84
+ ...props
85
+ }: PopoverPrimitive.Description.Props) {
86
+ return (
87
+ <PopoverPrimitive.Description
88
+ className={cn("text-muted-foreground text-sm", className)}
89
+ data-slot="popover-description"
90
+ {...props}
91
+ />
92
+ );
93
+ }
94
+
95
+ export {
96
+ PopoverCreateHandle,
97
+ Popover,
98
+ PopoverTrigger,
99
+ PopoverPopup,
100
+ PopoverPopup as PopoverContent,
101
+ PopoverTitle,
102
+ PopoverDescription,
103
+ PopoverClose,
104
+ };
@@ -0,0 +1,55 @@
1
+ "use client";
2
+
3
+ import { PreviewCard as PreviewCardPrimitive } from "@base-ui/react/preview-card";
4
+
5
+ import { cn } from "@/lib/utils";
6
+
7
+ const PreviewCard = PreviewCardPrimitive.Root;
8
+
9
+ function PreviewCardTrigger({ ...props }: PreviewCardPrimitive.Trigger.Props) {
10
+ return (
11
+ <PreviewCardPrimitive.Trigger data-slot="preview-card-trigger" {...props} />
12
+ );
13
+ }
14
+
15
+ function PreviewCardPopup({
16
+ className,
17
+ children,
18
+ align = "center",
19
+ sideOffset = 4,
20
+ ...props
21
+ }: PreviewCardPrimitive.Popup.Props & {
22
+ align?: PreviewCardPrimitive.Positioner.Props["align"];
23
+ sideOffset?: PreviewCardPrimitive.Positioner.Props["sideOffset"];
24
+ }) {
25
+ return (
26
+ <PreviewCardPrimitive.Portal>
27
+ <PreviewCardPrimitive.Positioner
28
+ align={align}
29
+ className="z-50"
30
+ data-slot="preview-card-positioner"
31
+ sideOffset={sideOffset}
32
+ >
33
+ <PreviewCardPrimitive.Popup
34
+ className={cn(
35
+ "relative flex w-64 origin-(--transform-origin) text-balance rounded-lg border bg-popover not-dark:bg-clip-padding p-4 text-popover-foreground text-sm shadow-lg/5 transition-[scale,opacity] before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] before:shadow-[0_1px_--theme(--color-black/6%)] data-ending-style:scale-98 data-starting-style:scale-98 data-ending-style:opacity-0 data-starting-style:opacity-0 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]",
36
+ className,
37
+ )}
38
+ data-slot="preview-card-content"
39
+ {...props}
40
+ >
41
+ {children}
42
+ </PreviewCardPrimitive.Popup>
43
+ </PreviewCardPrimitive.Positioner>
44
+ </PreviewCardPrimitive.Portal>
45
+ );
46
+ }
47
+
48
+ export {
49
+ PreviewCard,
50
+ PreviewCard as HoverCard,
51
+ PreviewCardTrigger,
52
+ PreviewCardTrigger as HoverCardTrigger,
53
+ PreviewCardPopup,
54
+ PreviewCardPopup as HoverCardContent,
55
+ };
@@ -0,0 +1,81 @@
1
+ "use client";
2
+
3
+ import { Progress as ProgressPrimitive } from "@base-ui/react/progress";
4
+
5
+ import { cn } from "@/lib/utils";
6
+
7
+ function Progress({
8
+ className,
9
+ children,
10
+ ...props
11
+ }: ProgressPrimitive.Root.Props) {
12
+ return (
13
+ <ProgressPrimitive.Root
14
+ className={cn("flex w-full flex-col gap-2", className)}
15
+ data-slot="progress"
16
+ {...props}
17
+ >
18
+ {children ? (
19
+ children
20
+ ) : (
21
+ <ProgressTrack>
22
+ <ProgressIndicator />
23
+ </ProgressTrack>
24
+ )}
25
+ </ProgressPrimitive.Root>
26
+ );
27
+ }
28
+
29
+ function ProgressLabel({ className, ...props }: ProgressPrimitive.Label.Props) {
30
+ return (
31
+ <ProgressPrimitive.Label
32
+ className={cn("font-medium text-sm", className)}
33
+ data-slot="progress-label"
34
+ {...props}
35
+ />
36
+ );
37
+ }
38
+
39
+ function ProgressTrack({ className, ...props }: ProgressPrimitive.Track.Props) {
40
+ return (
41
+ <ProgressPrimitive.Track
42
+ className={cn(
43
+ "block h-1.5 w-full overflow-hidden rounded-full bg-input",
44
+ className,
45
+ )}
46
+ data-slot="progress-track"
47
+ {...props}
48
+ />
49
+ );
50
+ }
51
+
52
+ function ProgressIndicator({
53
+ className,
54
+ ...props
55
+ }: ProgressPrimitive.Indicator.Props) {
56
+ return (
57
+ <ProgressPrimitive.Indicator
58
+ className={cn("bg-primary transition-all duration-500", className)}
59
+ data-slot="progress-indicator"
60
+ {...props}
61
+ />
62
+ );
63
+ }
64
+
65
+ function ProgressValue({ className, ...props }: ProgressPrimitive.Value.Props) {
66
+ return (
67
+ <ProgressPrimitive.Value
68
+ className={cn("text-sm tabular-nums", className)}
69
+ data-slot="progress-value"
70
+ {...props}
71
+ />
72
+ );
73
+ }
74
+
75
+ export {
76
+ Progress,
77
+ ProgressLabel,
78
+ ProgressTrack,
79
+ ProgressIndicator,
80
+ ProgressValue,
81
+ };
@@ -0,0 +1,36 @@
1
+ "use client";
2
+
3
+ import { Radio as RadioPrimitive } from "@base-ui/react/radio";
4
+ import { RadioGroup as RadioGroupPrimitive } from "@base-ui/react/radio-group";
5
+
6
+ import { cn } from "@/lib/utils";
7
+
8
+ function RadioGroup({ className, ...props }: RadioGroupPrimitive.Props) {
9
+ return (
10
+ <RadioGroupPrimitive
11
+ className={cn("flex flex-col gap-3", className)}
12
+ data-slot="radio-group"
13
+ {...props}
14
+ />
15
+ );
16
+ }
17
+
18
+ function Radio({ className, ...props }: RadioPrimitive.Root.Props) {
19
+ return (
20
+ <RadioPrimitive.Root
21
+ className={cn(
22
+ "relative inline-flex size-4.5 shrink-0 items-center justify-center rounded-full border border-input bg-background not-dark:bg-clip-padding shadow-xs/5 outline-none transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-full not-data-disabled:not-data-checked:not-aria-invalid:before:shadow-[0_1px_--theme(--color-black/6%)] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background aria-invalid:border-destructive/36 focus-visible:aria-invalid:border-destructive/64 focus-visible:aria-invalid:ring-destructive/48 data-disabled:opacity-64 sm:size-4 dark:not-data-checked:bg-input/32 dark:aria-invalid:ring-destructive/24 dark:not-data-disabled:not-data-checked:not-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)] [[data-disabled],[data-checked],[aria-invalid]]:shadow-none",
23
+ className,
24
+ )}
25
+ data-slot="radio"
26
+ {...props}
27
+ >
28
+ <RadioPrimitive.Indicator
29
+ className="-inset-px absolute flex size-4.5 items-center justify-center rounded-full before:size-2 before:rounded-full before:bg-primary-foreground data-unchecked:hidden data-checked:bg-primary sm:size-4 sm:before:size-1.5"
30
+ data-slot="radio-indicator"
31
+ />
32
+ </RadioPrimitive.Root>
33
+ );
34
+ }
35
+
36
+ export { RadioGroup, Radio, Radio as RadioGroupItem };
@@ -0,0 +1,64 @@
1
+ "use client";
2
+
3
+ import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react/scroll-area";
4
+
5
+ import { cn } from "@/lib/utils";
6
+
7
+ function ScrollArea({
8
+ className,
9
+ children,
10
+ scrollFade = false,
11
+ scrollbarGutter = false,
12
+ ...props
13
+ }: ScrollAreaPrimitive.Root.Props & {
14
+ scrollFade?: boolean;
15
+ scrollbarGutter?: boolean;
16
+ }) {
17
+ return (
18
+ <ScrollAreaPrimitive.Root
19
+ className={cn("size-full min-h-0", className)}
20
+ {...props}
21
+ >
22
+ <ScrollAreaPrimitive.Viewport
23
+ className={cn(
24
+ "h-full rounded-[inherit] outline-none transition-shadows focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-has-overflow-x:overscroll-x-contain",
25
+ scrollFade &&
26
+ "mask-t-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-start)))] mask-b-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-end)))] mask-l-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-start)))] mask-r-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-end)))] [--fade-size:1.5rem]",
27
+ scrollbarGutter &&
28
+ "data-has-overflow-y:pe-2.5 data-has-overflow-x:pb-2.5",
29
+ )}
30
+ data-slot="scroll-area-viewport"
31
+ >
32
+ {children}
33
+ </ScrollAreaPrimitive.Viewport>
34
+ <ScrollBar orientation="vertical" />
35
+ <ScrollBar orientation="horizontal" />
36
+ <ScrollAreaPrimitive.Corner data-slot="scroll-area-corner" />
37
+ </ScrollAreaPrimitive.Root>
38
+ );
39
+ }
40
+
41
+ function ScrollBar({
42
+ className,
43
+ orientation = "vertical",
44
+ ...props
45
+ }: ScrollAreaPrimitive.Scrollbar.Props) {
46
+ return (
47
+ <ScrollAreaPrimitive.Scrollbar
48
+ className={cn(
49
+ "m-1 flex opacity-0 transition-opacity delay-300 data-[orientation=horizontal]:h-1.5 data-[orientation=vertical]:w-1.5 data-[orientation=horizontal]:flex-col data-hovering:opacity-100 data-scrolling:opacity-100 data-hovering:delay-0 data-scrolling:delay-0 data-hovering:duration-100 data-scrolling:duration-100",
50
+ className,
51
+ )}
52
+ data-slot="scroll-area-scrollbar"
53
+ orientation={orientation}
54
+ {...props}
55
+ >
56
+ <ScrollAreaPrimitive.Thumb
57
+ className="relative flex-1 rounded-full bg-foreground/20"
58
+ data-slot="scroll-area-thumb"
59
+ />
60
+ </ScrollAreaPrimitive.Scrollbar>
61
+ );
62
+ }
63
+
64
+ export { ScrollArea, ScrollBar };