@cntyclub/ui-react 0.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.
Files changed (124) hide show
  1. package/dist/chunk-HDGMSYQS.js +26461 -0
  2. package/dist/chunk-HDGMSYQS.js.map +1 -0
  3. package/dist/chunk-PR4QN5HX.js +39 -0
  4. package/dist/chunk-PR4QN5HX.js.map +1 -0
  5. package/dist/form.d.ts +175 -0
  6. package/dist/form.js +5207 -0
  7. package/dist/form.js.map +1 -0
  8. package/dist/index.d.ts +1462 -0
  9. package/dist/index.js +81862 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/input-CZvh825j.d.ts +24 -0
  12. package/dist/qr-code-styling-3Y6LZH6V.js +1123 -0
  13. package/dist/qr-code-styling-3Y6LZH6V.js.map +1 -0
  14. package/package.json +79 -0
  15. package/src/components/form/checkbox-group-field.tsx +101 -0
  16. package/src/components/form/date-field.tsx +79 -0
  17. package/src/components/form/date-range-field.tsx +106 -0
  18. package/src/components/form/form-context.ts +10 -0
  19. package/src/components/form/form.tsx +54 -0
  20. package/src/components/form/number-field.tsx +69 -0
  21. package/src/components/form/select-field.tsx +76 -0
  22. package/src/components/form/submit-button.tsx +28 -0
  23. package/src/components/form/text-field.tsx +107 -0
  24. package/src/components/layout/dashboard-header.tsx +54 -0
  25. package/src/components/layout/dashboard-panel.tsx +34 -0
  26. package/src/components/theme-provider.tsx +403 -0
  27. package/src/components/ui/accordion.tsx +69 -0
  28. package/src/components/ui/alert-dialog.tsx +169 -0
  29. package/src/components/ui/alert.tsx +80 -0
  30. package/src/components/ui/animated-theme-toggler.tsx +265 -0
  31. package/src/components/ui/app-store-buttons.tsx +182 -0
  32. package/src/components/ui/aspect-ratio.tsx +23 -0
  33. package/src/components/ui/autocomplete.tsx +296 -0
  34. package/src/components/ui/avatar-group.tsx +95 -0
  35. package/src/components/ui/avatar.tsx +285 -0
  36. package/src/components/ui/badge-group.tsx +160 -0
  37. package/src/components/ui/badge.tsx +172 -0
  38. package/src/components/ui/breadcrumb.tsx +112 -0
  39. package/src/components/ui/button.tsx +77 -0
  40. package/src/components/ui/calendar.tsx +137 -0
  41. package/src/components/ui/card.tsx +244 -0
  42. package/src/components/ui/carousel.tsx +258 -0
  43. package/src/components/ui/chart.tsx +379 -0
  44. package/src/components/ui/checkbox-group.tsx +16 -0
  45. package/src/components/ui/checkbox.tsx +82 -0
  46. package/src/components/ui/collapsible.tsx +45 -0
  47. package/src/components/ui/combobox.tsx +411 -0
  48. package/src/components/ui/command.tsx +264 -0
  49. package/src/components/ui/context-menu.tsx +271 -0
  50. package/src/components/ui/credit-card.tsx +214 -0
  51. package/src/components/ui/dialog.tsx +196 -0
  52. package/src/components/ui/drawer.tsx +135 -0
  53. package/src/components/ui/empty.tsx +127 -0
  54. package/src/components/ui/featured-icon.tsx +149 -0
  55. package/src/components/ui/field.tsx +88 -0
  56. package/src/components/ui/fieldset.tsx +29 -0
  57. package/src/components/ui/form.tsx +17 -0
  58. package/src/components/ui/frame.tsx +82 -0
  59. package/src/components/ui/generic-empty.tsx +142 -0
  60. package/src/components/ui/group.tsx +97 -0
  61. package/src/components/ui/horizontal-scroll-fader.tsx +228 -0
  62. package/src/components/ui/input-group.tsx +102 -0
  63. package/src/components/ui/input-otp.tsx +96 -0
  64. package/src/components/ui/input.tsx +66 -0
  65. package/src/components/ui/item.tsx +198 -0
  66. package/src/components/ui/kbd.tsx +30 -0
  67. package/src/components/ui/label.tsx +28 -0
  68. package/src/components/ui/menu.tsx +312 -0
  69. package/src/components/ui/menubar.tsx +93 -0
  70. package/src/components/ui/meter.tsx +67 -0
  71. package/src/components/ui/multi-select.tsx +308 -0
  72. package/src/components/ui/navigation-menu.tsx +143 -0
  73. package/src/components/ui/number-field.tsx +160 -0
  74. package/src/components/ui/pagination-controls.tsx +74 -0
  75. package/src/components/ui/pagination.tsx +149 -0
  76. package/src/components/ui/popover.tsx +119 -0
  77. package/src/components/ui/preview-card.tsx +55 -0
  78. package/src/components/ui/progress.tsx +289 -0
  79. package/src/components/ui/qr-code.tsx +150 -0
  80. package/src/components/ui/radio-group.tsx +103 -0
  81. package/src/components/ui/resizable.tsx +56 -0
  82. package/src/components/ui/scroll-area.tsx +90 -0
  83. package/src/components/ui/scroller.tsx +38 -0
  84. package/src/components/ui/section-header.tsx +118 -0
  85. package/src/components/ui/select.tsx +181 -0
  86. package/src/components/ui/separator.tsx +23 -0
  87. package/src/components/ui/sheet.tsx +224 -0
  88. package/src/components/ui/sidebar.tsx +744 -0
  89. package/src/components/ui/skeleton.tsx +16 -0
  90. package/src/components/ui/slider.tsx +108 -0
  91. package/src/components/ui/smooth-scroll.tsx +143 -0
  92. package/src/components/ui/social-button.tsx +247 -0
  93. package/src/components/ui/spinner-on-demand.tsx +32 -0
  94. package/src/components/ui/spinner.tsx +18 -0
  95. package/src/components/ui/stat.tsx +187 -0
  96. package/src/components/ui/stepper.tsx +167 -0
  97. package/src/components/ui/switch.tsx +56 -0
  98. package/src/components/ui/table.tsx +126 -0
  99. package/src/components/ui/tabs.tsx +90 -0
  100. package/src/components/ui/tag.tsx +229 -0
  101. package/src/components/ui/target-countdown.tsx +46 -0
  102. package/src/components/ui/text-editor.tsx +313 -0
  103. package/src/components/ui/textarea.tsx +51 -0
  104. package/src/components/ui/timeline.tsx +116 -0
  105. package/src/components/ui/toast.tsx +268 -0
  106. package/src/components/ui/toggle-group.tsx +101 -0
  107. package/src/components/ui/toggle.tsx +45 -0
  108. package/src/components/ui/toolbar.tsx +89 -0
  109. package/src/components/ui/tooltip.tsx +102 -0
  110. package/src/components/ui/vertical-scroll-fader.tsx +250 -0
  111. package/src/components/ui/video-player.tsx +275 -0
  112. package/src/components/upload/avatar-upload-base.tsx +131 -0
  113. package/src/components/upload/image-upload-base.tsx +112 -0
  114. package/src/form.ts +17 -0
  115. package/src/index.ts +125 -0
  116. package/src/lib/hooks/use-callback-ref.ts +15 -0
  117. package/src/lib/hooks/use-first-render.ts +11 -0
  118. package/src/lib/hooks/use-hover.ts +53 -0
  119. package/src/lib/hooks/use-is-tab-active.ts +17 -0
  120. package/src/lib/hooks/use-media-query.ts +164 -0
  121. package/src/lib/utils/css.ts +6 -0
  122. package/src/styles.css +300 -0
  123. package/src/types/helpers.ts +24 -0
  124. package/src/types/react.d.ts +7 -0
@@ -0,0 +1,187 @@
1
+ "use client";
2
+
3
+ import { mergeProps } from "@base-ui/react/merge-props";
4
+ import { useRender } from "@base-ui/react/use-render";
5
+ import { cva, type VariantProps } from "class-variance-authority";
6
+ import { TrendingDownIcon, TrendingUpIcon } from "lucide-react";
7
+ import type * as React from "react";
8
+
9
+ import { cn } from "../../lib/utils/css";
10
+
11
+ const statVariants = cva("flex flex-col gap-1.5", {
12
+ defaultVariants: { variant: "default" },
13
+ variants: {
14
+ variant: {
15
+ // Bare — no chrome. Use inside a StatGroup or a Card.
16
+ default: "",
17
+ // A subtle tonal tile.
18
+ muted: "rounded-xl border bg-secondary p-4",
19
+ // A bordered surface tile that reads as its own card.
20
+ card: "rounded-xl border bg-card not-dark:bg-clip-padding p-4 shadow-xs/5",
21
+ },
22
+ },
23
+ });
24
+
25
+ interface StatProps extends useRender.ComponentProps<"div"> {
26
+ variant?: VariantProps<typeof statVariants>["variant"];
27
+ }
28
+
29
+ /**
30
+ * A single headline metric (KPI). Compose it from `StatLabel`, `StatValue`,
31
+ * `StatDescription` and an optional `StatTrend`. Drop bare `Stat`s into a
32
+ * `StatGroup` for a divided row, or use `variant="muted" | "card"` for a
33
+ * standalone tile.
34
+ */
35
+ function Stat({ className, variant, render, ...props }: StatProps) {
36
+ const defaultProps = {
37
+ className: cn(statVariants({ className, variant })),
38
+ "data-slot": "stat",
39
+ };
40
+
41
+ return useRender({
42
+ defaultTagName: "div",
43
+ props: mergeProps<"div">(defaultProps, props),
44
+ render,
45
+ });
46
+ }
47
+
48
+ /** The metric's caption, e.g. "Active members". */
49
+ function StatLabel({
50
+ className,
51
+ render,
52
+ ...props
53
+ }: useRender.ComponentProps<"div">) {
54
+ const defaultProps = {
55
+ className: cn("font-medium text-muted-foreground text-sm leading-none", className),
56
+ "data-slot": "stat-label",
57
+ };
58
+
59
+ return useRender({
60
+ defaultTagName: "div",
61
+ props: mergeProps<"div">(defaultProps, props),
62
+ render,
63
+ });
64
+ }
65
+
66
+ /**
67
+ * The headline number. Sans + tabular so digits stay aligned across a row;
68
+ * drop `font-sans` from `className` to render it in the serif display face.
69
+ */
70
+ function StatValue({
71
+ className,
72
+ render,
73
+ ...props
74
+ }: useRender.ComponentProps<"div">) {
75
+ const defaultProps = {
76
+ className: cn(
77
+ "font-sans font-semibold text-2xl text-foreground leading-none tabular-nums tracking-tight",
78
+ className,
79
+ ),
80
+ "data-slot": "stat-value",
81
+ };
82
+
83
+ return useRender({
84
+ defaultTagName: "div",
85
+ props: mergeProps<"div">(defaultProps, props),
86
+ render,
87
+ });
88
+ }
89
+
90
+ /** Supporting copy under the value, e.g. "vs. 1,204 last month". */
91
+ function StatDescription({
92
+ className,
93
+ render,
94
+ ...props
95
+ }: useRender.ComponentProps<"p">) {
96
+ const defaultProps = {
97
+ className: cn("text-muted-foreground text-sm leading-snug", className),
98
+ "data-slot": "stat-description",
99
+ };
100
+
101
+ return useRender({
102
+ defaultTagName: "p",
103
+ props: mergeProps<"p">(defaultProps, props),
104
+ render,
105
+ });
106
+ }
107
+
108
+ const statTrendVariants = cva(
109
+ "inline-flex items-center gap-0.5 font-medium text-sm tabular-nums [&_svg:not([class*='size-'])]:size-3.5 [&_svg]:pointer-events-none [&_svg]:shrink-0",
110
+ {
111
+ defaultVariants: { direction: "up" },
112
+ variants: {
113
+ direction: {
114
+ up: "text-success-foreground",
115
+ down: "text-destructive-foreground",
116
+ neutral: "text-muted-foreground",
117
+ },
118
+ },
119
+ },
120
+ );
121
+
122
+ interface StatTrendProps extends React.ComponentProps<"span"> {
123
+ direction?: VariantProps<typeof statTrendVariants>["direction"];
124
+ /** Show the directional arrow before the children. Defaults to `true`. */
125
+ showIcon?: boolean;
126
+ }
127
+
128
+ /**
129
+ * A small delta chip — e.g. "+12.5%". Picks an up/down arrow from `direction`
130
+ * and colors itself success/destructive/neutral.
131
+ */
132
+ function StatTrend({
133
+ className,
134
+ direction = "up",
135
+ showIcon = true,
136
+ children,
137
+ ...props
138
+ }: StatTrendProps) {
139
+ const Icon = direction === "down" ? TrendingDownIcon : TrendingUpIcon;
140
+ return (
141
+ <span
142
+ className={cn(statTrendVariants({ direction }), className)}
143
+ data-direction={direction}
144
+ data-slot="stat-trend"
145
+ {...props}
146
+ >
147
+ {showIcon && direction !== "neutral" ? <Icon /> : null}
148
+ {children}
149
+ </span>
150
+ );
151
+ }
152
+
153
+ /**
154
+ * Lays a set of bare `Stat`s out as a divided row (stacked on narrow screens),
155
+ * wrapped in a bordered card surface with even padding per metric.
156
+ */
157
+ function StatGroup({
158
+ className,
159
+ render,
160
+ ...props
161
+ }: useRender.ComponentProps<"div">) {
162
+ const defaultProps = {
163
+ className: cn(
164
+ "grid auto-cols-fr grid-flow-row divide-y rounded-xl border bg-card not-dark:bg-clip-padding shadow-xs/5 sm:grid-flow-col sm:divide-x sm:divide-y-0 [&>[data-slot=stat]]:p-4",
165
+ className,
166
+ ),
167
+ "data-slot": "stat-group",
168
+ role: "list",
169
+ };
170
+
171
+ return useRender({
172
+ defaultTagName: "div",
173
+ props: mergeProps<"div">(defaultProps, props),
174
+ render,
175
+ });
176
+ }
177
+
178
+ export {
179
+ Stat,
180
+ StatLabel,
181
+ StatValue,
182
+ StatDescription,
183
+ StatTrend,
184
+ StatGroup,
185
+ statVariants,
186
+ statTrendVariants,
187
+ };
@@ -0,0 +1,167 @@
1
+ "use client";
2
+
3
+ import { CheckIcon } from "lucide-react";
4
+ import * as React from "react";
5
+
6
+ import { cn } from "../../lib/utils/css";
7
+
8
+ type Orientation = "horizontal" | "vertical";
9
+ type StepState = "active" | "completed" | "inactive";
10
+
11
+ const StepperContext = React.createContext<{
12
+ value: number;
13
+ orientation: Orientation;
14
+ }>({ orientation: "horizontal", value: 1 });
15
+
16
+ const StepperItemContext = React.createContext<{
17
+ step: number;
18
+ state: StepState;
19
+ }>({ state: "inactive", step: 1 });
20
+
21
+ function Stepper({
22
+ value = 1,
23
+ orientation = "horizontal",
24
+ className,
25
+ ...props
26
+ }: React.ComponentProps<"div"> & {
27
+ /** 1-based index of the active step. */
28
+ value?: number;
29
+ orientation?: Orientation;
30
+ }) {
31
+ return (
32
+ <StepperContext.Provider value={{ orientation, value }}>
33
+ <div
34
+ className={cn(
35
+ "group/stepper flex w-full gap-2",
36
+ orientation === "vertical" ? "flex-col" : "items-center",
37
+ className,
38
+ )}
39
+ data-orientation={orientation}
40
+ data-slot="stepper"
41
+ {...props}
42
+ />
43
+ </StepperContext.Provider>
44
+ );
45
+ }
46
+
47
+ function StepperItem({
48
+ step,
49
+ completed,
50
+ className,
51
+ ...props
52
+ }: React.ComponentProps<"div"> & { step: number; completed?: boolean }) {
53
+ const { value } = React.useContext(StepperContext);
54
+ const state: StepState =
55
+ completed || value > step
56
+ ? "completed"
57
+ : value === step
58
+ ? "active"
59
+ : "inactive";
60
+
61
+ return (
62
+ <StepperItemContext.Provider value={{ state, step }}>
63
+ <div
64
+ className={cn(
65
+ "group/step flex gap-2.5",
66
+ "group-data-[orientation=horizontal]/stepper:items-center group-data-[orientation=horizontal]/stepper:not-last:flex-1",
67
+ "group-data-[orientation=vertical]/stepper:flex-col",
68
+ className,
69
+ )}
70
+ data-slot="stepper-item"
71
+ data-state={state}
72
+ {...props}
73
+ />
74
+ </StepperItemContext.Provider>
75
+ );
76
+ }
77
+
78
+ function StepperTrigger({
79
+ className,
80
+ ...props
81
+ }: React.ComponentProps<"button">) {
82
+ return (
83
+ <button
84
+ className={cn(
85
+ "flex items-center gap-2.5 rounded-lg text-left outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64",
86
+ className,
87
+ )}
88
+ data-slot="stepper-trigger"
89
+ type="button"
90
+ {...props}
91
+ />
92
+ );
93
+ }
94
+
95
+ function StepperIndicator({
96
+ className,
97
+ children,
98
+ ...props
99
+ }: React.ComponentProps<"span">) {
100
+ const { step } = React.useContext(StepperItemContext);
101
+ return (
102
+ <span
103
+ className={cn(
104
+ "relative flex size-8 shrink-0 items-center justify-center rounded-full border border-transparent bg-muted font-medium text-muted-foreground text-sm transition-colors group-data-[state=active]/step:bg-primary group-data-[state=active]/step:text-primary-foreground group-data-[state=completed]/step:bg-primary group-data-[state=completed]/step:text-primary-foreground",
105
+ className,
106
+ )}
107
+ data-slot="stepper-indicator"
108
+ {...props}
109
+ >
110
+ <span className="group-data-[state=completed]/step:hidden">
111
+ {children ?? step}
112
+ </span>
113
+ <CheckIcon className="hidden size-4 group-data-[state=completed]/step:block" />
114
+ </span>
115
+ );
116
+ }
117
+
118
+ function StepperTitle({ className, ...props }: React.ComponentProps<"div">) {
119
+ return (
120
+ <div
121
+ className={cn(
122
+ "font-medium text-sm transition-colors group-data-[state=inactive]/step:text-muted-foreground",
123
+ className,
124
+ )}
125
+ data-slot="stepper-title"
126
+ {...props}
127
+ />
128
+ );
129
+ }
130
+
131
+ function StepperDescription({
132
+ className,
133
+ ...props
134
+ }: React.ComponentProps<"div">) {
135
+ return (
136
+ <div
137
+ className={cn("text-muted-foreground text-xs", className)}
138
+ data-slot="stepper-description"
139
+ {...props}
140
+ />
141
+ );
142
+ }
143
+
144
+ function StepperSeparator({ className, ...props }: React.ComponentProps<"div">) {
145
+ return (
146
+ <div
147
+ className={cn(
148
+ "rounded-full bg-border transition-colors group-data-[state=completed]/step:bg-primary",
149
+ "group-data-[orientation=horizontal]/stepper:h-0.5 group-data-[orientation=horizontal]/stepper:flex-1",
150
+ "group-data-[orientation=vertical]/stepper:ms-[calc(--spacing(4)-1px)] group-data-[orientation=vertical]/stepper:h-6 group-data-[orientation=vertical]/stepper:w-0.5",
151
+ className,
152
+ )}
153
+ data-slot="stepper-separator"
154
+ {...props}
155
+ />
156
+ );
157
+ }
158
+
159
+ export {
160
+ Stepper,
161
+ StepperItem,
162
+ StepperTrigger,
163
+ StepperIndicator,
164
+ StepperTitle,
165
+ StepperDescription,
166
+ StepperSeparator,
167
+ };
@@ -0,0 +1,56 @@
1
+ "use client";
2
+
3
+ import { Switch as SwitchPrimitive } from "@base-ui/react/switch";
4
+
5
+ import { cn } from "../../lib/utils/css";
6
+
7
+ type SwitchProps = SwitchPrimitive.Root.Props & {
8
+ size?: "sm" | "md" | "lg";
9
+ slim?: boolean;
10
+ };
11
+
12
+ function Switch({ className, size = "md", slim = false, ...props }: SwitchProps) {
13
+ return (
14
+ <SwitchPrimitive.Root
15
+ className={cn(
16
+ "inline-flex shrink-0 items-center rounded-full outline-none transition-[background-color,box-shadow] duration-200",
17
+ "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
18
+ "data-checked:bg-primary data-unchecked:bg-input data-disabled:opacity-64",
19
+ size === "sm" && "[--thumb-size:--spacing(4)] sm:[--thumb-size:--spacing(3.5)]",
20
+ size === "md" && "[--thumb-size:--spacing(5)] sm:[--thumb-size:--spacing(4)]",
21
+ size === "lg" && "[--thumb-size:--spacing(6)] sm:[--thumb-size:--spacing(5)]",
22
+ "w-[calc(var(--thumb-size)*2-2px)]",
23
+ !slim && "h-[calc(var(--thumb-size)+2px)] p-px",
24
+ slim && "relative h-[calc(var(--thumb-size)*0.4)] overflow-visible p-0",
25
+ className,
26
+ )}
27
+ data-slot="switch"
28
+ {...props}
29
+ >
30
+ <SwitchPrimitive.Thumb
31
+ className={cn(
32
+ "pointer-events-none block aspect-square bg-background shadow-sm/5 will-change-transform",
33
+ "[transition:translate_.15s,border-radius_.15s,scale_.1s_.1s,transform-origin_.15s]",
34
+ !slim && [
35
+ "h-full origin-left",
36
+ "in-[[role=switch]:active,[data-slot=label]:active]:not-data-disabled:scale-x-110",
37
+ "in-[[role=switch]:active,[data-slot=label]:active]:rounded-[var(--thumb-size)/calc(var(--thumb-size)*1.1)]",
38
+ "rounded-(--thumb-size)",
39
+ "data-checked:origin-[var(--thumb-size)_50%] data-checked:translate-x-[calc(var(--thumb-size)-4px)]",
40
+ ],
41
+ slim && [
42
+ "absolute top-1/2 -translate-y-1/2",
43
+ "h-[var(--thumb-size)] w-[var(--thumb-size)]",
44
+ "rounded-full origin-left",
45
+ "in-[[role=switch]:active,[data-slot=label]:active]:not-data-disabled:scale-x-110",
46
+ "in-[[role=switch]:active,[data-slot=label]:active]:rounded-[var(--thumb-size)/calc(var(--thumb-size)*1.1)]",
47
+ "data-checked:origin-[var(--thumb-size)_50%] data-checked:translate-x-[calc(var(--thumb-size)-2px)]",
48
+ ],
49
+ )}
50
+ data-slot="switch-thumb"
51
+ />
52
+ </SwitchPrimitive.Root>
53
+ );
54
+ }
55
+
56
+ export { Switch };
@@ -0,0 +1,126 @@
1
+ import type * as React from "react";
2
+
3
+ import { cn } from "../../lib/utils/css";
4
+
5
+ function Table({ className, ...props }: React.ComponentProps<"table">) {
6
+ return (
7
+ <div
8
+ className="relative w-full overflow-x-auto"
9
+ data-slot="table-container"
10
+ >
11
+ <table
12
+ className={cn(
13
+ "w-full caption-bottom in-data-[slot=frame]:border-separate in-data-[slot=frame]:border-spacing-0 text-sm",
14
+ className,
15
+ )}
16
+ data-slot="table"
17
+ {...props}
18
+ />
19
+ </div>
20
+ );
21
+ }
22
+
23
+ function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
24
+ return (
25
+ <thead
26
+ className={cn(
27
+ "[&_tr]:border-b in-data-[slot=frame]:**:[th]:h-9 in-data-[slot=frame]:*:[tr]:border-none in-data-[slot=frame]:*:[tr]:hover:bg-transparent",
28
+ className,
29
+ )}
30
+ data-slot="table-header"
31
+ {...props}
32
+ />
33
+ );
34
+ }
35
+
36
+ function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
37
+ return (
38
+ <tbody
39
+ className={cn(
40
+ "relative in-data-[slot=frame]:rounded-xl in-data-[slot=frame]:shadow-xs/5 before:pointer-events-none before:absolute before:inset-px not-in-data-[slot=frame]:before:hidden before:rounded-[calc(var(--radius-xl)-1px)] before:shadow-[0_1px_--theme(--color-black/6%)] dark:before:shadow-[0_-1px_--theme(--color-white/8%)] [&_tr:last-child]:border-0 in-data-[slot=frame]:*:[tr]:border-0 in-data-[slot=frame]:*:[tr]:*:[td]:border-b in-data-[slot=frame]:*:[tr]:*:[td]:bg-background in-data-[slot=frame]:*:[tr]:*:[td]:bg-clip-padding in-data-[slot=frame]:*:[tr]:first:*:[td]:first:rounded-ss-xl in-data-[slot=frame]:*:[tr]:*:[td]:first:border-s in-data-[slot=frame]:*:[tr]:first:*:[td]:border-t in-data-[slot=frame]:*:[tr]:last:*:[td]:last:rounded-ee-xl in-data-[slot=frame]:*:[tr]:*:[td]:last:border-e in-data-[slot=frame]:*:[tr]:first:*:[td]:last:rounded-se-xl in-data-[slot=frame]:*:[tr]:last:*:[td]:first:rounded-es-xl in-data-[slot=frame]:*:[tr]:hover:*:[td]:bg-transparent in-data-[slot=frame]:*:[tr]:data-[state=selected]:*:[td]:bg-muted/72",
41
+ className,
42
+ )}
43
+ data-slot="table-body"
44
+ {...props}
45
+ />
46
+ );
47
+ }
48
+
49
+ function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
50
+ return (
51
+ <tfoot
52
+ className={cn(
53
+ "border-t in-data-[slot=frame]:border-none bg-muted/72 in-data-[slot=frame]:bg-transparent font-medium [&>tr]:last:border-b-0 in-data-[slot=frame]:*:[tr]:hover:bg-transparent",
54
+ className,
55
+ )}
56
+ data-slot="table-footer"
57
+ {...props}
58
+ />
59
+ );
60
+ }
61
+
62
+ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
63
+ return (
64
+ <tr
65
+ className={cn(
66
+ "border-b transition-colors hover:bg-muted/72 in-data-[slot=frame]:hover:bg-transparent data-[state=selected]:bg-muted/72 in-data-[slot=frame]:data-[state=selected]:bg-transparent",
67
+ className,
68
+ )}
69
+ data-slot="table-row"
70
+ {...props}
71
+ />
72
+ );
73
+ }
74
+
75
+ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
76
+ return (
77
+ <th
78
+ className={cn(
79
+ "h-10 whitespace-nowrap px-2.5 text-left align-middle font-medium text-muted-foreground leading-none has-[[role=checkbox]]:w-px has-[[role=checkbox]]:pe-0",
80
+ className,
81
+ )}
82
+ data-slot="table-head"
83
+ {...props}
84
+ />
85
+ );
86
+ }
87
+
88
+ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
89
+ return (
90
+ <td
91
+ className={cn(
92
+ "whitespace-nowrap p-2.5 align-middle leading-none in-data-[slot=frame]:first:p-[calc(--spacing(2.5)-1px)] in-data-[slot=frame]:last:p-[calc(--spacing(2.5)-1px)] has-[[role=checkbox]]:pe-0",
93
+ className,
94
+ )}
95
+ data-slot="table-cell"
96
+ {...props}
97
+ />
98
+ );
99
+ }
100
+
101
+ function TableCaption({
102
+ className,
103
+ ...props
104
+ }: React.ComponentProps<"caption">) {
105
+ return (
106
+ <caption
107
+ className={cn(
108
+ "in-data-[slot=frame]:my-4 mt-4 text-muted-foreground text-sm",
109
+ className,
110
+ )}
111
+ data-slot="table-caption"
112
+ {...props}
113
+ />
114
+ );
115
+ }
116
+
117
+ export {
118
+ Table,
119
+ TableHeader,
120
+ TableBody,
121
+ TableFooter,
122
+ TableHead,
123
+ TableRow,
124
+ TableCell,
125
+ TableCaption,
126
+ };
@@ -0,0 +1,90 @@
1
+ "use client";
2
+
3
+ import { Tabs as TabsPrimitive } from "@base-ui/react/tabs";
4
+
5
+ import { cn } from "../../lib/utils/css";
6
+
7
+ type TabsVariant = "default" | "underline";
8
+
9
+ function Tabs({ className, ...props }: TabsPrimitive.Root.Props) {
10
+ return (
11
+ <TabsPrimitive.Root
12
+ className={cn(
13
+ "flex flex-col gap-2 data-[orientation=vertical]:flex-row",
14
+ className,
15
+ )}
16
+ data-slot="tabs"
17
+ {...props}
18
+ />
19
+ );
20
+ }
21
+
22
+ function TabsList({
23
+ variant = "default",
24
+ className,
25
+ children,
26
+ ...props
27
+ }: TabsPrimitive.List.Props & {
28
+ variant?: TabsVariant;
29
+ }) {
30
+ return (
31
+ <TabsPrimitive.List
32
+ className={cn(
33
+ "relative z-0 flex w-fit items-center justify-center gap-x-0.5 text-muted-foreground",
34
+ "data-[orientation=vertical]:flex-col",
35
+ variant === "default"
36
+ ? "rounded-lg bg-muted p-0.5 text-muted-foreground/72"
37
+ : "data-[orientation=vertical]:px-1 data-[orientation=horizontal]:py-1 *:data-[slot=tabs-tab]:hover:bg-accent",
38
+ className,
39
+ )}
40
+ data-slot="tabs-list"
41
+ {...props}
42
+ >
43
+ {children}
44
+ <TabsPrimitive.Indicator
45
+ className={cn(
46
+ "-translate-y-(--active-tab-bottom) absolute bottom-0 left-0 h-(--active-tab-height) w-(--active-tab-width) translate-x-(--active-tab-left) transition-[width,translate] duration-200 ease-in-out",
47
+ variant === "underline"
48
+ ? "data-[orientation=vertical]:-translate-x-px z-10 bg-primary data-[orientation=horizontal]:h-0.5 data-[orientation=vertical]:w-0.5 data-[orientation=horizontal]:translate-y-px"
49
+ : "-z-1 rounded-md bg-background shadow-sm/5 dark:bg-input",
50
+ )}
51
+ data-slot="tab-indicator"
52
+ />
53
+ </TabsPrimitive.List>
54
+ );
55
+ }
56
+
57
+ function TabsTab({ className, ...props }: TabsPrimitive.Tab.Props) {
58
+ return (
59
+ <TabsPrimitive.Tab
60
+ className={cn(
61
+ "[&_svg]:-mx-0.5 relative flex h-9 shrink-0 grow cursor-pointer items-center justify-center gap-1.5 whitespace-nowrap rounded-md border border-transparent px-[calc(--spacing(2.5)-1px)] font-medium text-base outline-none transition-[color,background-color,box-shadow] hover:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring data-disabled:pointer-events-none data-[orientation=vertical]:w-full data-[orientation=vertical]:justify-start data-active:text-foreground data-disabled:opacity-64 sm:h-8 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
62
+ className,
63
+ )}
64
+ data-slot="tabs-tab"
65
+ {...props}
66
+ />
67
+ );
68
+ }
69
+
70
+ function TabsPanel({ className, ...props }: TabsPrimitive.Panel.Props) {
71
+ return (
72
+ <TabsPrimitive.Panel
73
+ className={cn(
74
+ "flex-1 outline-none data-[activation-direction=down]:animate-in data-[activation-direction=left]:animate-in data-[activation-direction=right]:animate-in data-[activation-direction=up]:animate-in fade-in-0 duration-200 ease-out data-[activation-direction=down]:slide-in-from-bottom-1 data-[activation-direction=left]:slide-in-from-left-1 data-[activation-direction=right]:slide-in-from-right-1 data-[activation-direction=up]:slide-in-from-top-1",
75
+ className,
76
+ )}
77
+ data-slot="tabs-content"
78
+ {...props}
79
+ />
80
+ );
81
+ }
82
+
83
+ export {
84
+ Tabs,
85
+ TabsList,
86
+ TabsTab,
87
+ TabsTab as TabsTrigger,
88
+ TabsPanel,
89
+ TabsPanel as TabsContent,
90
+ };