@optilogic/core 1.0.0-beta.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 (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +107 -0
  3. package/dist/index.cjs +6003 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +2310 -0
  6. package/dist/index.d.ts +2310 -0
  7. package/dist/index.js +5828 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/styles.css +96 -0
  10. package/dist/tailwind-preset.cjs +106 -0
  11. package/dist/tailwind-preset.cjs.map +1 -0
  12. package/dist/tailwind-preset.d.cts +23 -0
  13. package/dist/tailwind-preset.d.ts +23 -0
  14. package/dist/tailwind-preset.js +101 -0
  15. package/dist/tailwind-preset.js.map +1 -0
  16. package/package.json +154 -0
  17. package/src/components/accordion.tsx +187 -0
  18. package/src/components/alert-dialog.tsx +143 -0
  19. package/src/components/autocomplete.tsx +271 -0
  20. package/src/components/badge.tsx +62 -0
  21. package/src/components/button.tsx +85 -0
  22. package/src/components/calendar.tsx +235 -0
  23. package/src/components/card.tsx +94 -0
  24. package/src/components/checkbox.tsx +77 -0
  25. package/src/components/chip.tsx +77 -0
  26. package/src/components/confirmation-modal.tsx +195 -0
  27. package/src/components/context-menu.tsx +406 -0
  28. package/src/components/copy-button.tsx +84 -0
  29. package/src/components/data-grid/DataGrid.tsx +1027 -0
  30. package/src/components/data-grid/components/CellEditor.tsx +346 -0
  31. package/src/components/data-grid/components/FilterPopover.tsx +459 -0
  32. package/src/components/data-grid/components/HeaderCell.tsx +207 -0
  33. package/src/components/data-grid/components/index.ts +14 -0
  34. package/src/components/data-grid/hooks/index.ts +28 -0
  35. package/src/components/data-grid/hooks/useColumnResize.ts +378 -0
  36. package/src/components/data-grid/hooks/useDataGridState.ts +346 -0
  37. package/src/components/data-grid/hooks/useKeyboardNavigation.ts +361 -0
  38. package/src/components/data-grid/index.ts +71 -0
  39. package/src/components/data-grid/types.ts +478 -0
  40. package/src/components/data-grid/utils/dataProcessing.ts +277 -0
  41. package/src/components/data-grid/utils/index.ts +12 -0
  42. package/src/components/date-picker.tsx +366 -0
  43. package/src/components/dropdown-menu.tsx +230 -0
  44. package/src/components/icon-button.tsx +157 -0
  45. package/src/components/input.tsx +40 -0
  46. package/src/components/label.tsx +37 -0
  47. package/src/components/loading-spinner.tsx +113 -0
  48. package/src/components/modal.tsx +207 -0
  49. package/src/components/popover.tsx +62 -0
  50. package/src/components/progress.tsx +41 -0
  51. package/src/components/resizable-panel.tsx +434 -0
  52. package/src/components/resize-handle.tsx +187 -0
  53. package/src/components/select.tsx +160 -0
  54. package/src/components/separator.tsx +50 -0
  55. package/src/components/skeleton.tsx +37 -0
  56. package/src/components/switch.tsx +59 -0
  57. package/src/components/table.tsx +136 -0
  58. package/src/components/tabs.tsx +102 -0
  59. package/src/components/textarea.tsx +36 -0
  60. package/src/components/theme-picker.tsx +245 -0
  61. package/src/components/toaster.tsx +84 -0
  62. package/src/components/tooltip.tsx +199 -0
  63. package/src/index.ts +318 -0
  64. package/src/styles.css +96 -0
  65. package/src/tailwind-preset.ts +129 -0
  66. package/src/theme/index.ts +41 -0
  67. package/src/theme/presets.ts +502 -0
  68. package/src/theme/types.ts +164 -0
  69. package/src/theme/utils.ts +309 -0
  70. package/src/utils/cn.ts +14 -0
@@ -0,0 +1,85 @@
1
+ import * as React from "react";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+
5
+ import { cn } from "../utils/cn";
6
+
7
+ /**
8
+ * Button variant styles using class-variance-authority.
9
+ * Provides consistent button styling across the application.
10
+ */
11
+ const buttonVariants = cva(
12
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
13
+ {
14
+ variants: {
15
+ variant: {
16
+ default:
17
+ "bg-muted text-muted-foreground hover:bg-accent hover:text-accent-foreground",
18
+ primary: "bg-accent text-accent-foreground shadow hover:shadow-md",
19
+ destructive:
20
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90 hover:shadow-md",
21
+ outline:
22
+ "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
23
+ secondary:
24
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
25
+ ghost: "hover:bg-accent hover:text-accent-foreground",
26
+ link: "text-primary underline-offset-4 hover:underline",
27
+ },
28
+ size: {
29
+ default: "h-9 px-4 py-2",
30
+ sm: "h-8 rounded-md px-3 text-xs",
31
+ lg: "h-10 rounded-md px-8",
32
+ icon: "h-9 w-9",
33
+ },
34
+ },
35
+ defaultVariants: {
36
+ variant: "default",
37
+ size: "default",
38
+ },
39
+ }
40
+ );
41
+
42
+ export interface ButtonProps
43
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
44
+ VariantProps<typeof buttonVariants> {
45
+ /**
46
+ * If true, the button will render as its child element (Slot pattern).
47
+ * Useful for wrapping other components like links.
48
+ */
49
+ asChild?: boolean;
50
+ }
51
+
52
+ /**
53
+ * Button Component
54
+ *
55
+ * A versatile button component with multiple variants and sizes.
56
+ * Supports the Slot pattern for composing with other elements.
57
+ *
58
+ * @example
59
+ * <Button variant="primary">Click me</Button>
60
+ *
61
+ * @example
62
+ * <Button variant="ghost" size="icon">
63
+ * <Icon />
64
+ * </Button>
65
+ *
66
+ * @example
67
+ * <Button asChild>
68
+ * <a href="/link">Link Button</a>
69
+ * </Button>
70
+ */
71
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
72
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
73
+ const Comp = asChild ? Slot : "button";
74
+ return (
75
+ <Comp
76
+ className={cn(buttonVariants({ variant, size, className }))}
77
+ ref={ref}
78
+ {...props}
79
+ />
80
+ );
81
+ }
82
+ );
83
+ Button.displayName = "Button";
84
+
85
+ export { Button, buttonVariants };
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Calendar Component
3
+ *
4
+ * A styled calendar component built on react-day-picker.
5
+ * Matches the library's design system with proper theming.
6
+ *
7
+ * Features:
8
+ * - Single date, range, and multiple selection modes
9
+ * - Keyboard navigation
10
+ * - Min/max date constraints
11
+ * - Disabled dates
12
+ * - Theme-aware styling
13
+ * - Month/year dropdown selectors for quick navigation
14
+ */
15
+
16
+ import * as React from "react";
17
+ import { DayPicker, type DayPickerProps } from "react-day-picker";
18
+ import { ChevronLeft, ChevronRight } from "lucide-react";
19
+ import { cn } from "../utils/cn";
20
+ import { buttonVariants } from "./button";
21
+ import {
22
+ Select,
23
+ SelectContent,
24
+ SelectItem,
25
+ SelectTrigger,
26
+ SelectValue,
27
+ } from "./select";
28
+
29
+ export type CalendarProps = DayPickerProps;
30
+
31
+ const MONTHS = [
32
+ "January",
33
+ "February",
34
+ "March",
35
+ "April",
36
+ "May",
37
+ "June",
38
+ "July",
39
+ "August",
40
+ "September",
41
+ "October",
42
+ "November",
43
+ "December",
44
+ ];
45
+
46
+ /**
47
+ * Generate an array of years for the year selector
48
+ * Defaults to 100 years before and 20 years after current year
49
+ */
50
+ function getYearRange(fromYear?: number, toYear?: number): number[] {
51
+ const currentYear = new Date().getFullYear();
52
+ const start = fromYear ?? currentYear - 100;
53
+ const end = toYear ?? currentYear + 20;
54
+ const years: number[] = [];
55
+ for (let year = start; year <= end; year++) {
56
+ years.push(year);
57
+ }
58
+ return years;
59
+ }
60
+
61
+ /**
62
+ * Calendar Component
63
+ *
64
+ * A fully styled calendar using react-day-picker with library design tokens.
65
+ *
66
+ * @example Single date selection
67
+ * ```tsx
68
+ * const [date, setDate] = useState<Date>();
69
+ * <Calendar mode="single" selected={date} onSelect={setDate} />
70
+ * ```
71
+ *
72
+ * @example Date range selection
73
+ * ```tsx
74
+ * const [range, setRange] = useState<DateRange>();
75
+ * <Calendar mode="range" selected={range} onSelect={setRange} />
76
+ * ```
77
+ *
78
+ * @example Quick navigation with month/year selectors
79
+ * ```tsx
80
+ * <Calendar
81
+ * mode="single"
82
+ * selected={date}
83
+ * onSelect={setDate}
84
+ * fromYear={2020}
85
+ * toYear={2030}
86
+ * />
87
+ * ```
88
+ */
89
+ function Calendar({
90
+ className,
91
+ classNames,
92
+ showOutsideDays = true,
93
+ month: controlledMonth,
94
+ defaultMonth,
95
+ onMonthChange,
96
+ ...props
97
+ }: CalendarProps) {
98
+ const startMonth = props.startMonth ?? props.fromDate;
99
+ const endMonth = props.endMonth ?? props.toDate;
100
+ const fromYear = startMonth?.getFullYear();
101
+ const toYear = endMonth?.getFullYear();
102
+ const years = React.useMemo(() => getYearRange(fromYear, toYear), [fromYear, toYear]);
103
+
104
+ const [internalMonth, setInternalMonth] = React.useState<Date>(
105
+ defaultMonth ?? new Date()
106
+ );
107
+
108
+ const displayedMonth = controlledMonth ?? internalMonth;
109
+
110
+ const handleMonthChange = React.useCallback(
111
+ (newMonth: Date) => {
112
+ if (controlledMonth === undefined) {
113
+ setInternalMonth(newMonth);
114
+ }
115
+ onMonthChange?.(newMonth);
116
+ },
117
+ [controlledMonth, onMonthChange]
118
+ );
119
+
120
+ return (
121
+ <DayPicker
122
+ showOutsideDays={showOutsideDays}
123
+ month={displayedMonth}
124
+ onMonthChange={handleMonthChange}
125
+ className={cn("p-3", className)}
126
+ classNames={{
127
+ months: "flex flex-col sm:flex-row gap-4",
128
+ month: "flex flex-col gap-4",
129
+ month_caption: "flex justify-center pt-1 relative items-center h-7 px-10",
130
+ caption_label: "text-sm font-medium hidden",
131
+ nav: "flex items-center gap-1",
132
+ button_previous: cn(
133
+ buttonVariants({ variant: "outline" }),
134
+ "absolute left-1 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 z-10"
135
+ ),
136
+ button_next: cn(
137
+ buttonVariants({ variant: "outline" }),
138
+ "absolute right-1 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 z-10"
139
+ ),
140
+ month_grid: "w-full border-collapse px-1",
141
+ weekdays: "flex",
142
+ weekday:
143
+ "text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]",
144
+ week: "flex w-full mt-2",
145
+ day: cn(
146
+ "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md",
147
+ props.mode === "range"
148
+ ? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
149
+ : "[&:has([aria-selected])]:rounded-md"
150
+ ),
151
+ day_button: cn(
152
+ buttonVariants({ variant: "ghost" }),
153
+ "h-9 w-9 p-0 font-normal aria-selected:opacity-100"
154
+ ),
155
+ range_start: "day-range-start rounded-l-md",
156
+ range_end: "day-range-end rounded-r-md",
157
+ selected:
158
+ "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
159
+ today: "bg-accent text-accent-foreground",
160
+ outside:
161
+ "day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30",
162
+ disabled: "text-muted-foreground opacity-50",
163
+ range_middle:
164
+ "aria-selected:bg-accent aria-selected:text-accent-foreground",
165
+ hidden: "invisible",
166
+ ...classNames,
167
+ }}
168
+ components={{
169
+ Chevron: ({ orientation }) => {
170
+ const Icon = orientation === "left" ? ChevronLeft : ChevronRight;
171
+ return <Icon className="h-4 w-4" />;
172
+ },
173
+ MonthCaption: ({ calendarMonth }) => {
174
+ const month = calendarMonth.date.getMonth();
175
+ const year = calendarMonth.date.getFullYear();
176
+
177
+ return (
178
+ <div className="flex items-center justify-center gap-1 px-10">
179
+ <Select
180
+ value={month.toString()}
181
+ onValueChange={(value) => {
182
+ const newDate = new Date(calendarMonth.date);
183
+ newDate.setMonth(parseInt(value, 10));
184
+ handleMonthChange(newDate);
185
+ }}
186
+ >
187
+ <SelectTrigger
188
+ className="h-7 w-[110px] text-sm font-medium border-none shadow-none hover:bg-accent focus:ring-0 px-2"
189
+ aria-label="Select month"
190
+ >
191
+ <SelectValue>{MONTHS[month]}</SelectValue>
192
+ </SelectTrigger>
193
+ <SelectContent>
194
+ {MONTHS.map((monthName, index) => (
195
+ <SelectItem key={monthName} value={index.toString()}>
196
+ {monthName}
197
+ </SelectItem>
198
+ ))}
199
+ </SelectContent>
200
+ </Select>
201
+
202
+ <Select
203
+ value={year.toString()}
204
+ onValueChange={(value) => {
205
+ const newDate = new Date(calendarMonth.date);
206
+ newDate.setFullYear(parseInt(value, 10));
207
+ handleMonthChange(newDate);
208
+ }}
209
+ >
210
+ <SelectTrigger
211
+ className="h-7 w-[70px] text-sm font-medium border-none shadow-none hover:bg-accent focus:ring-0 px-2"
212
+ aria-label="Select year"
213
+ >
214
+ <SelectValue>{year}</SelectValue>
215
+ </SelectTrigger>
216
+ <SelectContent className="max-h-[200px]">
217
+ {years.map((y) => (
218
+ <SelectItem key={y} value={y.toString()}>
219
+ {y}
220
+ </SelectItem>
221
+ ))}
222
+ </SelectContent>
223
+ </Select>
224
+ </div>
225
+ );
226
+ },
227
+ }}
228
+ {...props}
229
+ />
230
+ );
231
+ }
232
+
233
+ Calendar.displayName = "Calendar";
234
+
235
+ export { Calendar };
@@ -0,0 +1,94 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "../utils/cn";
4
+
5
+ export interface CardProps extends React.HTMLAttributes<HTMLDivElement> {}
6
+
7
+ /**
8
+ * Card - Container component for grouped content
9
+ */
10
+ const Card = React.forwardRef<HTMLDivElement, CardProps>(
11
+ ({ className, ...props }, ref) => (
12
+ <div
13
+ ref={ref}
14
+ className={cn(
15
+ "rounded-xl border bg-card text-card-foreground shadow",
16
+ className
17
+ )}
18
+ {...props}
19
+ />
20
+ )
21
+ );
22
+ Card.displayName = "Card";
23
+
24
+ export interface CardHeaderProps extends React.HTMLAttributes<HTMLDivElement> {}
25
+
26
+ const CardHeader = React.forwardRef<HTMLDivElement, CardHeaderProps>(
27
+ ({ className, ...props }, ref) => (
28
+ <div
29
+ ref={ref}
30
+ className={cn("flex flex-col space-y-1.5 p-6", className)}
31
+ {...props}
32
+ />
33
+ )
34
+ );
35
+ CardHeader.displayName = "CardHeader";
36
+
37
+ export interface CardTitleProps extends React.HTMLAttributes<HTMLDivElement> {}
38
+
39
+ const CardTitle = React.forwardRef<HTMLDivElement, CardTitleProps>(
40
+ ({ className, ...props }, ref) => (
41
+ <div
42
+ ref={ref}
43
+ className={cn("font-semibold leading-none tracking-tight", className)}
44
+ {...props}
45
+ />
46
+ )
47
+ );
48
+ CardTitle.displayName = "CardTitle";
49
+
50
+ export interface CardDescriptionProps
51
+ extends React.HTMLAttributes<HTMLDivElement> {}
52
+
53
+ const CardDescription = React.forwardRef<HTMLDivElement, CardDescriptionProps>(
54
+ ({ className, ...props }, ref) => (
55
+ <div
56
+ ref={ref}
57
+ className={cn("text-sm text-muted-foreground", className)}
58
+ {...props}
59
+ />
60
+ )
61
+ );
62
+ CardDescription.displayName = "CardDescription";
63
+
64
+ export interface CardContentProps
65
+ extends React.HTMLAttributes<HTMLDivElement> {}
66
+
67
+ const CardContent = React.forwardRef<HTMLDivElement, CardContentProps>(
68
+ ({ className, ...props }, ref) => (
69
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
70
+ )
71
+ );
72
+ CardContent.displayName = "CardContent";
73
+
74
+ export interface CardFooterProps extends React.HTMLAttributes<HTMLDivElement> {}
75
+
76
+ const CardFooter = React.forwardRef<HTMLDivElement, CardFooterProps>(
77
+ ({ className, ...props }, ref) => (
78
+ <div
79
+ ref={ref}
80
+ className={cn("flex items-center p-6 pt-0", className)}
81
+ {...props}
82
+ />
83
+ )
84
+ );
85
+ CardFooter.displayName = "CardFooter";
86
+
87
+ export {
88
+ Card,
89
+ CardHeader,
90
+ CardFooter,
91
+ CardTitle,
92
+ CardDescription,
93
+ CardContent,
94
+ };
@@ -0,0 +1,77 @@
1
+ import * as React from "react";
2
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
3
+ import { Check } from "lucide-react";
4
+
5
+ import { cn } from "../utils/cn";
6
+
7
+ export interface CheckboxProps
8
+ extends React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root> {
9
+ /**
10
+ * Whether to show a visible border around the checkbox.
11
+ * @default true
12
+ */
13
+ showBorder?: boolean;
14
+ }
15
+
16
+ /**
17
+ * Checkbox Component
18
+ *
19
+ * An accessible checkbox input built on Radix UI primitives.
20
+ * Supports controlled and uncontrolled modes.
21
+ *
22
+ * @example
23
+ * <Checkbox id="terms" />
24
+ * <Label htmlFor="terms">Accept terms</Label>
25
+ *
26
+ * @example
27
+ * <Checkbox checked={isChecked} onCheckedChange={setIsChecked} />
28
+ */
29
+ const Checkbox = React.forwardRef<
30
+ React.ElementRef<typeof CheckboxPrimitive.Root>,
31
+ CheckboxProps
32
+ >(({ className, showBorder = true, ...props }, ref) => (
33
+ <CheckboxPrimitive.Root
34
+ ref={ref}
35
+ className={cn(
36
+ // Base styles
37
+ "grid place-content-center peer shrink-0",
38
+ "h-4 w-4 rounded-sm",
39
+ "transition-colors duration-150",
40
+ // Focus styles
41
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
42
+ // Disabled styles
43
+ "disabled:cursor-not-allowed disabled:opacity-50",
44
+ // Border styles (conditional)
45
+ showBorder
46
+ ? [
47
+ // Visible border for better contrast
48
+ "border-2 border-foreground/40",
49
+ // Hover state
50
+ "hover:border-foreground/60",
51
+ // Checked state - use accent background with contrasting border
52
+ "data-[state=checked]:bg-accent data-[state=checked]:border-accent",
53
+ ]
54
+ : [
55
+ // No border variant (for inline uses)
56
+ "border border-transparent",
57
+ // Checked state with subtle background
58
+ "data-[state=checked]:bg-accent/80",
59
+ ],
60
+ className
61
+ )}
62
+ {...props}
63
+ >
64
+ <CheckboxPrimitive.Indicator
65
+ className={cn(
66
+ "grid place-content-center",
67
+ // Use foreground color that contrasts with accent
68
+ "text-accent-foreground"
69
+ )}
70
+ >
71
+ <Check className="h-3.5 w-3.5 stroke-[3]" />
72
+ </CheckboxPrimitive.Indicator>
73
+ </CheckboxPrimitive.Root>
74
+ ));
75
+ Checkbox.displayName = CheckboxPrimitive.Root.displayName;
76
+
77
+ export { Checkbox };
@@ -0,0 +1,77 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "../utils/cn";
4
+
5
+ export interface ChipProps {
6
+ /**
7
+ * Chip label text
8
+ */
9
+ label: string;
10
+
11
+ /**
12
+ * Click handler
13
+ */
14
+ onClick?: () => void;
15
+
16
+ /**
17
+ * Whether the chip is disabled
18
+ */
19
+ disabled?: boolean;
20
+
21
+ /**
22
+ * Additional class names
23
+ */
24
+ className?: string;
25
+ }
26
+
27
+ /**
28
+ * Chip component
29
+ *
30
+ * A clickable pill-shaped component for tags, filters, or suggested actions.
31
+ * Uses the theme chip colors.
32
+ *
33
+ * @example
34
+ * <Chip label="React" onClick={() => console.log('clicked')} />
35
+ *
36
+ * @example
37
+ * <Chip label="Disabled" disabled />
38
+ */
39
+ export function Chip({
40
+ label,
41
+ onClick,
42
+ disabled = false,
43
+ className,
44
+ }: ChipProps) {
45
+ const isClickable = !!onClick && !disabled;
46
+
47
+ return (
48
+ <button
49
+ onClick={onClick}
50
+ disabled={!isClickable}
51
+ className={cn(
52
+ "inline-flex items-center justify-center",
53
+ "px-4 py-2 rounded-full",
54
+ "text-sm font-medium",
55
+ "transition-all duration-150",
56
+ // Default styling with chip colors
57
+ "bg-chip text-chip-foreground",
58
+ "border border-chip-foreground/20",
59
+ // Clickable state
60
+ isClickable && [
61
+ "cursor-pointer",
62
+ "hover:bg-accent hover:text-accent-foreground",
63
+ "hover:border-accent",
64
+ "hover:shadow-md",
65
+ ],
66
+ // Non-clickable state
67
+ !isClickable && "cursor-default",
68
+ // Disabled state
69
+ disabled && "opacity-50 cursor-not-allowed",
70
+ className
71
+ )}
72
+ type="button"
73
+ >
74
+ {label}
75
+ </button>
76
+ );
77
+ }