@turtleclub/ui 0.7.0-beta.1 → 0.7.0-beta.10

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.
@@ -6,8 +6,10 @@ import { Chip } from "@/components/ui/chip";
6
6
  import { TokenSelector, type Token } from "./token-selector";
7
7
  import { BaseSelector } from "./widget/base-selector";
8
8
 
9
- interface SwapInputProps
10
- extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
9
+ interface SwapInputProps extends Omit<
10
+ React.HTMLAttributes<HTMLDivElement>,
11
+ "onChange"
12
+ > {
11
13
  value?: string;
12
14
  tokens: Token[];
13
15
  selectedToken?: string;
@@ -24,6 +26,8 @@ interface SwapInputProps
24
26
  useCustomTokenSelector?: boolean;
25
27
  selectedTokenData?: Token | null;
26
28
  onCustomTokenSelectorClick?: () => void;
29
+ showTokenSelectorBalance?: boolean;
30
+ showInputBalance?: boolean;
27
31
  }
28
32
 
29
33
  const SwapInput = React.forwardRef<HTMLDivElement, SwapInputProps>(
@@ -46,6 +50,8 @@ const SwapInput = React.forwardRef<HTMLDivElement, SwapInputProps>(
46
50
  useCustomTokenSelector = false,
47
51
  selectedTokenData,
48
52
  onCustomTokenSelectorClick,
53
+ showTokenSelectorBalance = true,
54
+ showInputBalance = true,
49
55
  ...props
50
56
  },
51
57
  ref,
@@ -127,40 +133,45 @@ const SwapInput = React.forwardRef<HTMLDivElement, SwapInputProps>(
127
133
  size="sm"
128
134
  variant="default"
129
135
  className="rounded-full"
136
+ showBalance={showTokenSelectorBalance}
130
137
  />
131
138
  ))}
132
139
  </div>
133
140
 
134
141
  {/* Bottom row: USD value and balance with MAX */}
135
- <div className="text-muted-foreground flex items-center justify-between text-sm">
136
- <div>
137
- {usdValue ? (
138
- <span>≈ {usdValue}</span>
139
- ) : value ? (
140
- <span className="text-destructive/50">Insufficient balance</span>
141
- ) : null}
142
+ {showInputBalance && (
143
+ <div className="text-muted-foreground flex items-center justify-between text-sm">
144
+ <div>
145
+ {usdValue ? (
146
+ <span>≈ {usdValue}</span>
147
+ ) : value ? (
148
+ <span className="text-destructive/50">
149
+ Insufficient balance
150
+ </span>
151
+ ) : null}
152
+ </div>
153
+ <div className="flex items-center gap-2">
154
+ {balance && (
155
+ <span className="text-sm">
156
+ Balance: {balance} {currentToken?.symbol || ""}
157
+ </span>
158
+ )}
159
+ {onMaxClick && (
160
+ <Chip
161
+ variant="default"
162
+ size="xs"
163
+ onClick={onMaxClick}
164
+ className={cn(
165
+ "hover:bg-primary hover:text-primary-foreground h-5 cursor-pointer px-2 py-0.5 text-xs transition-colors",
166
+ disabled && "cursor-not-allowed opacity-50",
167
+ )}
168
+ >
169
+ MAX
170
+ </Chip>
171
+ )}
172
+ </div>
142
173
  </div>
143
- <div className="flex items-center gap-2">
144
- {balance && (
145
- <span className="text-sm">
146
- Balance: {balance} {currentToken?.symbol || ""}
147
- </span>
148
- )}
149
- {onMaxClick && (
150
- <Chip
151
- variant="default"
152
- size="xs"
153
- onClick={onMaxClick}
154
- className={cn(
155
- "hover:bg-primary hover:text-primary-foreground h-5 cursor-pointer px-2 py-0.5 text-xs transition-colors",
156
- disabled && "cursor-not-allowed opacity-50",
157
- )}
158
- >
159
- MAX
160
- </Chip>
161
- )}
162
- </div>
163
- </div>
174
+ )}
164
175
  </Card>
165
176
  );
166
177
  },
@@ -54,7 +54,13 @@ const TokenSelector = ({
54
54
  const getTokenIcon = (token: Token) => {
55
55
  if (token.icon) return token.icon;
56
56
  if (token.logoUrl) {
57
- return <img src={token.logoUrl} alt={token.symbol} className="size-full rounded-full" />;
57
+ return (
58
+ <img
59
+ src={token.logoUrl}
60
+ alt={token.symbol}
61
+ className="size-full rounded-full"
62
+ />
63
+ );
58
64
  }
59
65
  return null;
60
66
  };
@@ -21,19 +21,13 @@ function Avatar({
21
21
  return (
22
22
  <AvatarPrimitive.Root
23
23
  data-slot="avatar"
24
- className={cn(
25
- "relative flex size-8 shrink-0 overflow-hidden rounded-full",
26
- className,
27
- )}
24
+ className={cn("relative flex size-8 shrink-0 overflow-hidden rounded-full", className)}
28
25
  {...props}
29
26
  />
30
27
  );
31
28
  }
32
29
 
33
- function AvatarImage({
34
- className,
35
- ...props
36
- }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
30
+ function AvatarImage({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
37
31
  return (
38
32
  <AvatarPrimitive.Image
39
33
  data-slot="avatar-image"
@@ -50,10 +44,7 @@ function AvatarFallback({
50
44
  return (
51
45
  <AvatarPrimitive.Fallback
52
46
  data-slot="avatar-fallback"
53
- className={cn(
54
- "bg-muted flex size-full items-center justify-center rounded-full",
55
- className,
56
- )}
47
+ className={cn("bg-muted flex size-full items-center justify-center rounded-full", className)}
57
48
  {...props}
58
49
  />
59
50
  );
@@ -65,14 +56,16 @@ function TurtleAvatar({
65
56
  fallback,
66
57
  ...rest
67
58
  }: AvatarPrimitive.AvatarProps & {
68
- src: string;
69
- alt: string;
59
+ src?: string;
60
+ alt?: string;
70
61
  fallback?: React.ReactNode;
71
62
  }) {
72
63
  return (
73
64
  <Avatar {...rest}>
74
65
  <AvatarImage src={src} alt={alt} />
75
- <AvatarFallback>{fallback}</AvatarFallback>
66
+ <AvatarFallback className="border-gradient-primary bg-primary/10 opacity-50">
67
+ {fallback}
68
+ </AvatarFallback>
76
69
  </Avatar>
77
70
  );
78
71
  }
@@ -12,7 +12,11 @@ import {
12
12
  CommandItem,
13
13
  CommandList,
14
14
  } from "@/components/ui/command";
15
- import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
15
+ import {
16
+ Popover,
17
+ PopoverContent,
18
+ PopoverTrigger,
19
+ } from "@/components/ui/popover";
16
20
  import { Separator } from "@/components/ui/separator";
17
21
  import { cn } from "@/lib/utils";
18
22
 
@@ -85,20 +89,43 @@ interface ComboboxOption<T = string> {
85
89
  * />
86
90
  */
87
91
  interface ComboboxProps<T = string>
88
- extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "animationConfig" | "defaultValue"> {
92
+ extends Omit<
93
+ React.ButtonHTMLAttributes<HTMLButtonElement>,
94
+ "animationConfig" | "defaultValue"
95
+ > {
89
96
  /**
90
97
  * An array of option objects or groups to be displayed in the multi-select component.
91
98
  */
92
99
  options: ComboboxOption<T>[];
100
+
93
101
  /**
94
102
  * Callback function triggered when the selected values change.
95
103
  * Receives an array of the new selected values.
96
104
  */
97
105
  onValueChange: (value: T) => void;
98
106
 
107
+ /**
108
+ * Custom renderer for the selected value displayed in the input.
109
+ * @param option The currently selected option object.
110
+ */
111
+ renderValue?: (option: ComboboxOption<T>) => React.ReactNode;
112
+
99
113
  /** The default selected values when the component mounts. */
100
114
  defaultValue?: T;
101
115
 
116
+ /**
117
+ * Callback function triggered when the search input value changes.
118
+ * Useful for server-side filtering or handling large datasets externally.
119
+ */
120
+ onInputValueChange?: (value: string) => void;
121
+
122
+ /**
123
+ * If true, disables the built-in filtering logic.
124
+ * Use this when you filter options externally (e.g. server-side search).
125
+ * Optional, defaults to false.
126
+ */
127
+ disableLocalFiltering?: boolean;
128
+
102
129
  /**
103
130
  * Placeholder text to be displayed when no values are selected.
104
131
  * Optional, defaults to "Select options".
@@ -137,6 +164,12 @@ interface ComboboxProps<T = string>
137
164
  */
138
165
  searchable?: boolean;
139
166
 
167
+ /**
168
+ * If true, searching will also check the 'value' field in addition to 'label'.
169
+ * Optional, defaults to false.
170
+ */
171
+ searchByValue?: boolean;
172
+
140
173
  /**
141
174
  * Custom empty state message when no options match search.
142
175
  * Optional, defaults to "No results found."
@@ -230,7 +263,10 @@ export interface ComboboxRef {
230
263
  focus: () => void;
231
264
  }
232
265
 
233
- const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<ComboboxRef>) => {
266
+ const ComboboxComponent = <T = string,>(
267
+ props: ComboboxProps<T>,
268
+ ref: React.Ref<ComboboxRef>,
269
+ ) => {
234
270
  const {
235
271
  options,
236
272
  onValueChange,
@@ -248,6 +284,10 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
248
284
  minWidth,
249
285
  maxWidth,
250
286
  closeOnSelect = false,
287
+ renderValue,
288
+ searchByValue = false,
289
+ onInputValueChange,
290
+ disableLocalFiltering = false,
251
291
  ...restProps
252
292
  } = props;
253
293
 
@@ -283,7 +323,7 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
283
323
  setTimeout(() => setPoliteMessage(""), 100);
284
324
  }
285
325
  },
286
- []
326
+ [],
287
327
  );
288
328
 
289
329
  const multiSelectId = React.useId();
@@ -324,10 +364,12 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
324
364
  }
325
365
  },
326
366
  }),
327
- [resetToDefault, selectedValue, onValueChange]
367
+ [resetToDefault, onValueChange],
328
368
  );
329
369
 
330
- const [screenSize, setScreenSize] = React.useState<"mobile" | "tablet" | "desktop">("desktop");
370
+ const [screenSize, setScreenSize] = React.useState<
371
+ "mobile" | "tablet" | "desktop"
372
+ >("desktop");
331
373
 
332
374
  React.useEffect(() => {
333
375
  if (typeof window === "undefined") return;
@@ -380,42 +422,53 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
380
422
 
381
423
  const getAllOptions = React.useCallback((): ComboboxOption<T>[] => {
382
424
  if (options.length === 0) return [];
383
- const allOptions: ComboboxOption<T>[] = options;
425
+
384
426
  const valueSet = new Set<T>();
385
- const duplicates: T[] = [];
386
427
  const uniqueOptions: ComboboxOption<T>[] = [];
387
- allOptions.forEach((option) => {
388
- if (valueSet.has(option.value)) {
389
- duplicates.push(option.value);
390
- } else {
428
+
429
+ options.forEach((option) => {
430
+ if (!valueSet.has(option.value)) {
391
431
  valueSet.add(option.value);
392
432
  uniqueOptions.push(option);
393
433
  }
394
434
  });
395
- return allOptions;
435
+
436
+ return uniqueOptions;
396
437
  }, [options]);
397
438
 
398
439
  const getOptionByValue = React.useCallback(
399
440
  (value: T): ComboboxOption<T> | undefined => {
400
441
  const option = getAllOptions().find((option) => option.value === value);
401
442
  if (!option && process.env.NODE_ENV === "development") {
402
- console.warn(`Combobox: Option with value "${value}" not found in options list`);
443
+ console.warn(
444
+ `Combobox: Option with value "${value}" not found in options list`,
445
+ );
403
446
  }
404
447
  return option;
405
448
  },
406
- [getAllOptions]
449
+ [getAllOptions],
407
450
  );
408
451
 
409
452
  const filteredOptions = React.useMemo(() => {
453
+ if (disableLocalFiltering) return options;
410
454
  if (!searchable || !searchValue) return options;
411
455
  if (options.length === 0) return [];
412
456
 
413
- return options.filter(
414
- (option) =>
415
- option.label.toLowerCase().includes(searchValue.toLowerCase()) ||
416
- String(option.value).toLowerCase().includes(searchValue.toLowerCase())
417
- );
418
- }, [options, searchValue, searchable]);
457
+ return options.filter((option) => {
458
+ const labelMatch = option.label
459
+ .toLowerCase()
460
+ .includes(searchValue.toLowerCase());
461
+ if (!searchByValue) return labelMatch;
462
+
463
+ const val = option.value;
464
+ const valueMatch =
465
+ typeof val === "string" || typeof val === "number"
466
+ ? String(val).toLowerCase().includes(searchValue.toLowerCase())
467
+ : false;
468
+
469
+ return labelMatch || valueMatch;
470
+ });
471
+ }, [disableLocalFiltering, options, searchable, searchValue, searchByValue]);
419
472
 
420
473
  // const handleInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
421
474
  // if (event.key === "Enter") {
@@ -464,7 +517,9 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
464
517
 
465
518
  if (isPopoverOpen !== prevIsOpen.current) {
466
519
  if (isPopoverOpen) {
467
- announce(`Dropdown opened. ${totalOptions} options available. Use arrow keys to navigate.`);
520
+ announce(
521
+ `Dropdown opened. ${totalOptions} options available. Use arrow keys to navigate.`,
522
+ );
468
523
  } else {
469
524
  announce("Dropdown closed.");
470
525
  }
@@ -476,11 +531,11 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
476
531
  const filteredCount = allOptions.filter(
477
532
  (opt) =>
478
533
  opt.label.toLowerCase().includes(searchValue.toLowerCase()) ||
479
- String(opt.value).toLowerCase().includes(searchValue.toLowerCase())
534
+ String(opt.value).toLowerCase().includes(searchValue.toLowerCase()),
480
535
  ).length;
481
536
 
482
537
  announce(
483
- `${filteredCount} option${filteredCount === 1 ? "" : "s"} found for "${searchValue}"`
538
+ `${filteredCount} option${filteredCount === 1 ? "" : "s"} found for "${searchValue}"`,
484
539
  );
485
540
  }
486
541
  prevSearchValue.current = searchValue;
@@ -498,9 +553,14 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
498
553
  </div>
499
554
  </div>
500
555
 
501
- <Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen} modal={modalPopover}>
556
+ <Popover
557
+ open={isPopoverOpen}
558
+ onOpenChange={setIsPopoverOpen}
559
+ modal={modalPopover}
560
+ >
502
561
  <div id={triggerDescriptionId} className="sr-only">
503
- Multi-select dropdown. Use arrow keys to navigate, Enter to select, and Escape to close.
562
+ Multi-select dropdown. Use arrow keys to navigate, Enter to select,
563
+ and Escape to close.
504
564
  </div>
505
565
  <PopoverTrigger asChild>
506
566
  <Button
@@ -521,7 +581,7 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
521
581
  responsiveSettings.compactMode && "h-8 text-sm",
522
582
  screenSize === "mobile" && "h-12 text-base",
523
583
  disabled && "cursor-not-allowed opacity-50",
524
- className
584
+ className,
525
585
  )}
526
586
  style={{
527
587
  ...widthConstraints,
@@ -532,7 +592,14 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
532
592
  <div className="mx-auto flex w-full items-center justify-between">
533
593
  <div className="flex items-center gap-2">
534
594
  {(() => {
535
- const selectedOption = options.find((op) => op.value === selectedValue);
595
+ const selectedOption = options.find(
596
+ (op) => op.value === selectedValue,
597
+ );
598
+
599
+ if (renderValue && selectedOption) {
600
+ return renderValue(selectedOption);
601
+ }
602
+
536
603
  return (
537
604
  <>
538
605
  {selectedOption?.icon && (
@@ -541,7 +608,9 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
541
608
  aria-hidden="true"
542
609
  />
543
610
  )}
544
- <span className="text-foreground text-sm">{selectedOption?.label}</span>
611
+ <span className="text-foreground text-sm">
612
+ {selectedOption?.label}
613
+ </span>
545
614
  </>
546
615
  );
547
616
  })()}
@@ -565,7 +634,10 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
565
634
  >
566
635
  <XIcon className="size-3.5" />
567
636
  </div>
568
- <Separator orientation="vertical" className="flex h-full min-h-6" />
637
+ <Separator
638
+ orientation="vertical"
639
+ className="flex h-full min-h-6"
640
+ />
569
641
  <ChevronDown
570
642
  className="text-muted-foreground mx-2 h-4 cursor-pointer"
571
643
  aria-hidden="true"
@@ -574,7 +646,9 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
574
646
  </div>
575
647
  ) : (
576
648
  <div className="mx-auto flex w-full items-center justify-between">
577
- <span className="text-muted-foreground text-sm">{placeholder}</span>
649
+ <span className="text-muted-foreground text-sm">
650
+ {placeholder}
651
+ </span>
578
652
  <ChevronDown className="text-muted-foreground mx-2 h-4 cursor-pointer" />
579
653
  </div>
580
654
  )}
@@ -590,7 +664,7 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
590
664
  screenSize === "mobile" && "w-[85vw] max-w-[280px]",
591
665
  screenSize === "tablet" && "w-[70vw] max-w-md",
592
666
  screenSize === "desktop" && "min-w-[300px]",
593
- popoverClassName
667
+ popoverClassName,
594
668
  )}
595
669
  style={{
596
670
  maxWidth: `min(${widthConstraints.maxWidth}, 85vw)`,
@@ -600,13 +674,16 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
600
674
  align="start"
601
675
  onEscapeKeyDown={() => setIsPopoverOpen(false)}
602
676
  >
603
- <Command>
677
+ <Command shouldFilter={false}>
604
678
  {searchable && (
605
679
  <CommandInput
606
680
  placeholder="Search options..."
607
681
  // onKeyDown={handleInputKeyDown}
608
682
  value={searchValue}
609
- onValueChange={setSearchValue}
683
+ onValueChange={(val) => {
684
+ setSearchValue(val);
685
+ onInputValueChange?.(val);
686
+ }}
610
687
  aria-label="Search through available options"
611
688
  aria-describedby={`${multiSelectId}-search-help`}
612
689
  />
@@ -620,10 +697,12 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
620
697
  className={cn(
621
698
  "multiselect-scrollbar max-h-[40vh] overflow-y-auto",
622
699
  screenSize === "mobile" && "max-h-[50vh]",
623
- "overscroll-behavior-y-contain"
700
+ "overscroll-behavior-y-contain",
624
701
  )}
625
702
  >
626
- <CommandEmpty>{emptyIndicator || "No results found."}</CommandEmpty>{" "}
703
+ <CommandEmpty>
704
+ {emptyIndicator || "No results found."}
705
+ </CommandEmpty>{" "}
627
706
  <CommandGroup>
628
707
  {filteredOptions.map((option) => {
629
708
  const isSelected = selectedValue === option.value;
@@ -639,7 +718,7 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
639
718
  }${option.disabled ? ", disabled" : ""}`}
640
719
  className={cn(
641
720
  "cursor-pointer",
642
- option.disabled && "cursor-not-allowed opacity-50"
721
+ option.disabled && "cursor-not-allowed opacity-50",
643
722
  )}
644
723
  disabled={option.disabled}
645
724
  >
@@ -650,7 +729,9 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
650
729
  />
651
730
  )}
652
731
  <span className="grow">{option.label}</span>
653
- {isSelected ? <CheckIcon className="text-muted-foreground size-3.5" /> : null}
732
+ {isSelected ? (
733
+ <CheckIcon className="text-muted-foreground size-3.5" />
734
+ ) : null}
654
735
  </CommandItem>
655
736
  );
656
737
  })}
@@ -662,7 +743,7 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
662
743
  <WandSparkles
663
744
  className={cn(
664
745
  "text-foreground bg-background my-2 h-3 w-3 cursor-pointer",
665
- isAnimating ? "" : "text-muted-foreground"
746
+ isAnimating ? "" : "text-muted-foreground",
666
747
  )}
667
748
  onClick={() => setIsAnimating(!isAnimating)}
668
749
  />
@@ -674,7 +755,7 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
674
755
 
675
756
  // Create the forwardRef wrapper with proper generic typing
676
757
  const ComboboxForwardRef = React.forwardRef(ComboboxComponent) as <T = string>(
677
- props: ComboboxProps<T> & { ref?: React.Ref<ComboboxRef> }
758
+ props: ComboboxProps<T> & { ref?: React.Ref<ComboboxRef> },
678
759
  ) => React.ReactElement;
679
760
 
680
761
  // Set displayName on a mutable object
@@ -6,27 +6,19 @@ import { XIcon } from "lucide-react";
6
6
 
7
7
  import { cn } from "@/lib/utils";
8
8
 
9
- function Dialog({
10
- ...props
11
- }: React.ComponentProps<typeof DialogPrimitive.Root>) {
9
+ function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
12
10
  return <DialogPrimitive.Root data-slot="dialog" {...props} />;
13
11
  }
14
12
 
15
- function DialogTrigger({
16
- ...props
17
- }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
13
+ function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
18
14
  return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
19
15
  }
20
16
 
21
- function DialogPortal({
22
- ...props
23
- }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
17
+ function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
24
18
  return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
25
19
  }
26
20
 
27
- function DialogClose({
28
- ...props
29
- }: React.ComponentProps<typeof DialogPrimitive.Close>) {
21
+ function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {
30
22
  return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
31
23
  }
32
24
 
@@ -38,8 +30,8 @@ function DialogOverlay({
38
30
  <DialogPrimitive.Overlay
39
31
  data-slot="dialog-overlay"
40
32
  className={cn(
41
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
42
- className,
33
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 grid place-items-center overflow-y-auto bg-black/50",
34
+ className
43
35
  )}
44
36
  {...props}
45
37
  />
@@ -61,7 +53,7 @@ function DialogContent({
61
53
  data-slot="dialog-content"
62
54
  className={cn(
63
55
  "bg-background 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 border-gradient-soft fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg p-6 shadow-lg duration-200 sm:max-w-lg",
64
- className,
56
+ className
65
57
  )}
66
58
  {...props}
67
59
  >
@@ -94,19 +86,13 @@ function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
94
86
  return (
95
87
  <div
96
88
  data-slot="dialog-footer"
97
- className={cn(
98
- "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
99
- className,
100
- )}
89
+ className={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)}
101
90
  {...props}
102
91
  />
103
92
  );
104
93
  }
105
94
 
106
- function DialogTitle({
107
- className,
108
- ...props
109
- }: React.ComponentProps<typeof DialogPrimitive.Title>) {
95
+ function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {
110
96
  return (
111
97
  <DialogPrimitive.Title
112
98
  data-slot="dialog-title"
@@ -50,7 +50,7 @@ function TooltipContent({
50
50
  data-slot="tooltip-content"
51
51
  sideOffset={sideOffset}
52
52
  className={cn(
53
- "animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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 bg-neutral-alpha-10 border-border z-50 origin-(--radix-tooltip-content-transform-origin) rounded-lg border p-2 backdrop-blur-lg transition-all",
53
+ "animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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 bg-neutral-alpha-10 border-border z-50 max-w-xs origin-(--radix-tooltip-content-transform-origin) rounded-lg border p-2 text-xs backdrop-blur-lg transition-all",
54
54
  gradient === "primary" && "border-gradient-primary",
55
55
  gradient === "white" && "border-gradient-white",
56
56
  className,