shapes-ui 0.4.2 → 0.6.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 (180) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.yml +47 -0
  2. package/.github/ISSUE_TEMPLATE/config.yml +1 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.yml +31 -0
  4. package/.github/pull_request_template.md +14 -0
  5. package/.github/workflows/pr-preview.yml +75 -0
  6. package/.github/workflows/release.yml +8 -0
  7. package/CHANGELOG.md +30 -0
  8. package/CODE_OF_CONDUCT.md +41 -0
  9. package/CONTRIBUTING.md +52 -0
  10. package/README.md +18 -0
  11. package/SECURITY.md +0 -0
  12. package/content/components/accordion.mdx +13 -0
  13. package/content/components/alert-dialog.mdx +34 -0
  14. package/content/components/autocomplete.mdx +62 -0
  15. package/content/components/avatar.mdx +11 -0
  16. package/content/components/button.mdx +8 -0
  17. package/content/components/checkbox.mdx +11 -0
  18. package/content/components/collapsible.mdx +11 -0
  19. package/content/components/combobox.mdx +33 -0
  20. package/content/components/context-menu.mdx +29 -0
  21. package/content/components/dialog.mdx +33 -0
  22. package/content/components/drawer.mdx +38 -0
  23. package/content/components/field.mdx +23 -2
  24. package/content/components/fieldset.mdx +11 -1
  25. package/content/components/form.mdx +8 -0
  26. package/content/components/input.mdx +4 -0
  27. package/content/components/menu.mdx +27 -0
  28. package/content/components/menubar.mdx +31 -0
  29. package/content/components/meter.mdx +14 -0
  30. package/content/components/navigation-menu.mdx +28 -0
  31. package/content/components/number-field.mdx +25 -0
  32. package/content/components/popover.mdx +22 -0
  33. package/content/components/preview-card.mdx +14 -2
  34. package/content/components/progress.mdx +15 -1
  35. package/content/components/radio.mdx +31 -0
  36. package/content/components/scroll-area.mdx +23 -0
  37. package/content/components/select.mdx +57 -0
  38. package/content/components/separator.mdx +29 -0
  39. package/content/components/slider.mdx +52 -0
  40. package/content/components/switch.mdx +30 -0
  41. package/content/components/tabs.mdx +47 -0
  42. package/content/components/toast.mdx +70 -0
  43. package/content/components/toggle-group.mdx +37 -0
  44. package/content/components/toggle.mdx +46 -2
  45. package/content/components/toolbar.mdx +48 -0
  46. package/content/components/tooltip.mdx +38 -0
  47. package/content/docs/installation.mdx +30 -0
  48. package/content-collections.ts +65 -1
  49. package/dist/cli.js +947 -101
  50. package/examples/__index.tsx +320 -66
  51. package/examples/autocomplete-align.tsx +39 -0
  52. package/examples/autocomplete-controlled.tsx +44 -0
  53. package/examples/autocomplete-groups.tsx +65 -0
  54. package/examples/autocomplete-no-clear.tsx +40 -0
  55. package/examples/avatar-demo.tsx +3 -3
  56. package/examples/checkbox-demo.tsx +1 -1
  57. package/examples/checkbox-form.tsx +3 -3
  58. package/examples/field-custom-control.tsx +33 -9
  59. package/examples/form-demo.tsx +5 -10
  60. package/examples/input-group-with-button.tsx +1 -1
  61. package/examples/menu-advanced.tsx +1 -3
  62. package/examples/menu-align.tsx +19 -16
  63. package/examples/menu-checkbox.tsx +2 -3
  64. package/examples/menu-demo.tsx +1 -3
  65. package/examples/menu-group.tsx +1 -3
  66. package/examples/menu-radio.tsx +1 -3
  67. package/examples/menu-submenu.tsx +2 -3
  68. package/examples/meter-demo.tsx +10 -2
  69. package/examples/meter-flip.tsx +8 -8
  70. package/examples/meter-no-label.tsx +9 -2
  71. package/examples/meter-no-value.tsx +7 -8
  72. package/examples/radio-card.tsx +28 -0
  73. package/examples/radio-demo.tsx +19 -1
  74. package/examples/radio-description.tsx +26 -0
  75. package/examples/radio-orientation.tsx +21 -0
  76. package/examples/select-alignment.tsx +51 -0
  77. package/examples/select-demo.tsx +36 -1
  78. package/examples/select-disabled.tsx +38 -0
  79. package/examples/select-groups.tsx +54 -0
  80. package/examples/select-invalid.tsx +41 -0
  81. package/examples/select-scrollable.tsx +112 -0
  82. package/examples/separator-demo.tsx +13 -0
  83. package/examples/separator-horizontal.tsx +18 -0
  84. package/examples/slider-controlled.tsx +28 -0
  85. package/examples/slider-demo.tsx +3 -1
  86. package/examples/slider-disabled.tsx +7 -0
  87. package/examples/slider-edge.tsx +13 -0
  88. package/examples/slider-multiple.tsx +7 -0
  89. package/examples/slider-range.tsx +5 -0
  90. package/examples/slider-vertical.tsx +10 -0
  91. package/examples/switch-demo.tsx +19 -1
  92. package/examples/switch-disabled.tsx +20 -0
  93. package/examples/switch-sizes.tsx +24 -0
  94. package/examples/switch-with-label.tsx +16 -0
  95. package/examples/tabs-demo.tsx +14 -1
  96. package/examples/tabs-disabled.tsx +21 -0
  97. package/examples/tabs-line.tsx +18 -0
  98. package/examples/tabs-vertical.tsx +13 -0
  99. package/examples/toast-action.tsx +39 -0
  100. package/examples/toast-anchored.tsx +36 -0
  101. package/examples/toast-demo.tsx +27 -1
  102. package/examples/toast-positions.tsx +54 -0
  103. package/examples/toast-promise.tsx +51 -0
  104. package/examples/toast-stacked.tsx +30 -0
  105. package/examples/toast-timeout.tsx +43 -0
  106. package/examples/toast-update.tsx +38 -0
  107. package/examples/toast-variants.tsx +54 -0
  108. package/examples/toggle-controlled.tsx +20 -0
  109. package/examples/toggle-demo.tsx +7 -51
  110. package/examples/toggle-group-demo.tsx +19 -0
  111. package/examples/toggle-group-multiple.tsx +19 -0
  112. package/examples/toggle-icon-fill.tsx +12 -0
  113. package/examples/toolbar-demo.tsx +45 -21
  114. package/examples/toolbar-input-link.tsx +35 -0
  115. package/examples/toolbar-menu.tsx +53 -0
  116. package/examples/tooltip-demo.tsx +48 -0
  117. package/examples/tooltip-positions.tsx +60 -0
  118. package/package.json +19 -18
  119. package/public/base-ui.svg +1 -0
  120. package/public/r/drawer.json +1 -1
  121. package/public/r/field.json +1 -1
  122. package/public/r/meter.json +1 -1
  123. package/public/r/number-field.json +1 -1
  124. package/public/r/progress.json +1 -1
  125. package/public/r/radio.json +1 -1
  126. package/public/r/select.json +1 -1
  127. package/public/r/slider.json +1 -1
  128. package/public/r/switch.json +1 -1
  129. package/public/r/tabs.json +1 -1
  130. package/public/r/toast.json +2 -1
  131. package/public/r/toggle.json +1 -1
  132. package/public/r/toolbar.json +1 -1
  133. package/public/r/tooltip.json +15 -0
  134. package/src/assets/base-ui.svg +1 -0
  135. package/src/commands/add.ts +79 -38
  136. package/src/commands/cli.ts +50 -3
  137. package/src/commands/create.ts +262 -0
  138. package/src/commands/init.ts +45 -12
  139. package/src/commands/palette.ts +55 -0
  140. package/src/components/docs/layout/footer.tsx +2 -2
  141. package/src/components/docs/layout/header.tsx +7 -19
  142. package/src/components/docs/layout/mobile-menu.tsx +26 -78
  143. package/src/components/docs/layout/nav-list.tsx +27 -21
  144. package/src/components/docs/layout/page-header.tsx +52 -7
  145. package/src/components/docs/layout/split-layout.tsx +11 -9
  146. package/src/components/docs/layout/table-of-content.tsx +145 -0
  147. package/src/components/docs/markdown/components.tsx +142 -29
  148. package/src/components/docs/markdown/copy-button.tsx +41 -0
  149. package/src/components/docs/markdown/installation-block.tsx +5 -24
  150. package/src/components/docs/markdown/render-preview.tsx +1 -1
  151. package/src/components/ui/badge.tsx +1 -1
  152. package/src/components/ui/button-group.tsx +1 -1
  153. package/src/components/ui/checkbox.tsx +1 -1
  154. package/src/components/ui/drawer.tsx +1 -1
  155. package/src/components/ui/field.tsx +9 -28
  156. package/src/components/ui/form.tsx +1 -1
  157. package/src/components/ui/meter.tsx +12 -26
  158. package/src/components/ui/number-field.tsx +1 -1
  159. package/src/components/ui/radio.tsx +32 -19
  160. package/src/components/ui/scroll-area.tsx +11 -2
  161. package/src/components/ui/select.tsx +6 -6
  162. package/src/components/ui/slider.tsx +8 -5
  163. package/src/components/ui/switch.tsx +13 -17
  164. package/src/components/ui/tabs.tsx +23 -10
  165. package/src/components/ui/toast.tsx +190 -29
  166. package/src/components/ui/toggle.tsx +1 -1
  167. package/src/components/ui/toolbar.tsx +17 -4
  168. package/src/components/ui/tooltip.tsx +54 -0
  169. package/src/lib/docs-headings.ts +72 -0
  170. package/src/routeTree.gen.ts +60 -3
  171. package/src/routes/__root.tsx +3 -5
  172. package/src/routes/components.$slug.tsx +20 -4
  173. package/src/routes/docs.$slug.tsx +78 -0
  174. package/src/routes/docs.tsx +15 -0
  175. package/src/styles/styles.css +1 -1
  176. package/src/utils/cli-utils.ts +8 -8
  177. package/src/utils/dependency-installer.ts +30 -0
  178. package/src/utils/package-manager.ts +4 -4
  179. package/src/utils/palette.ts +666 -0
  180. package/src/utils/schema.ts +6 -0
@@ -41,7 +41,7 @@ function SelectTrigger({
41
41
  data-slot="select-trigger"
42
42
  data-size={size}
43
43
  className={cn(
44
- "flex w-fit items-center justify-between gap-1.5 border border-input bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-[3px] aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
44
+ "flex w-fit items-center justify-between gap-1.5 rounded-lg border border-input bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
45
45
  className,
46
46
  )}
47
47
  {...props}
@@ -79,10 +79,10 @@ function SelectPopup({
79
79
  className="isolate z-50"
80
80
  >
81
81
  <SelectPrimitive.Popup
82
- data-slot="select-content"
82
+ data-slot="select-popup"
83
83
  data-align-trigger={alignItemWithTrigger}
84
84
  className={cn(
85
- "text-popover-foreground relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto bg-popup p-1 shadow-md ring-1 ring-foreground/10 duration-100 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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",
85
+ "relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popup text-popup-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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",
86
86
  className,
87
87
  )}
88
88
  {...props}
@@ -111,7 +111,7 @@ function SelectItem({ className, children, ...props }: SelectPrimitive.Item.Prop
111
111
  <SelectPrimitive.Item
112
112
  data-slot="select-item"
113
113
  className={cn(
114
- "relative flex w-full cursor-default items-center gap-1.5 py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
114
+ "relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
115
115
  className,
116
116
  )}
117
117
  {...props}
@@ -148,7 +148,7 @@ function SelectScrollUpButton({
148
148
  <SelectPrimitive.ScrollUpArrow
149
149
  data-slot="select-scroll-up-button"
150
150
  className={cn(
151
- "top-0 z-10 flex w-full cursor-default items-center justify-center bg-popup py-1 [&_svg:not([class*='size-'])]:size-3",
151
+ "top-0 z-10 flex w-full cursor-default items-center justify-center bg-popup py-1 [&_svg:not([class*='size-'])]:size-4",
152
152
  className,
153
153
  )}
154
154
  {...props}
@@ -166,7 +166,7 @@ function SelectScrollDownButton({
166
166
  <SelectPrimitive.ScrollDownArrow
167
167
  data-slot="select-scroll-down-button"
168
168
  className={cn(
169
- "bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-popup py-1 [&_svg:not([class*='size-'])]:size-3",
169
+ "bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-popup py-1 [&_svg:not([class*='size-'])]:size-4",
170
170
  className,
171
171
  )}
172
172
  {...props}
@@ -20,7 +20,10 @@ function Slider({
20
20
 
21
21
  return (
22
22
  <SliderPrimitive.Root
23
- className={cn("data-horizontal:w-full data-vertical:h-full", className)}
23
+ className={cn(
24
+ "data-horizontal:w-full data-vertical:h-full data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full",
25
+ className,
26
+ )}
24
27
  data-slot="slider"
25
28
  defaultValue={defaultValue}
26
29
  value={value}
@@ -29,14 +32,14 @@ function Slider({
29
32
  thumbAlignment="edge"
30
33
  {...props}
31
34
  >
32
- <SliderPrimitive.Control className="relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-col">
33
- <SliderPrimitive.Track className="relative h-1.5 grow overflow-hidden border bg-muted select-none data-horizontal:h-1 data-horizontal:w-full data-vertical:h-full data-vertical:w-1">
34
- <SliderPrimitive.Indicator className="bg-primary select-none data-horizontal:h-full data-vertical:w-full" />
35
+ <SliderPrimitive.Control className="relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-col data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-40 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col">
36
+ <SliderPrimitive.Track className="relative h-1.5 grow overflow-hidden rounded-full border bg-muted select-none data-horizontal:h-1 data-horizontal:w-full data-vertical:h-full data-vertical:w-1 data-[orientation=horizontal]:h-1 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1">
37
+ <SliderPrimitive.Indicator className="bg-primary select-none data-horizontal:h-full data-vertical:w-full data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full" />
35
38
  </SliderPrimitive.Track>
36
39
  {Array.from({ length: _values.length }, (_, index) => (
37
40
  <SliderPrimitive.Thumb
38
41
  key={index}
39
- className="relative block size-3.5 shrink-0 border border-ring bg-white ring-ring/50 transition-[color,box-shadow] select-none after:absolute after:-inset-2 hover:ring-[3px] focus-visible:ring-[3px] focus-visible:outline-hidden active:ring-[3px] disabled:pointer-events-none disabled:opacity-50"
42
+ className="relative block size-3.5 shrink-0 rounded-full border border-ring bg-primary ring-ring/50 transition-[color,box-shadow] select-none after:absolute after:-inset-2 hover:ring-[3px] focus-visible:ring-[3px] focus-visible:outline-hidden active:ring-[3px] disabled:pointer-events-none disabled:opacity-50"
40
43
  />
41
44
  ))}
42
45
  </SliderPrimitive.Control>
@@ -5,7 +5,6 @@ import { Switch as SwitchPrimitive } from "@base-ui/react/switch";
5
5
  import { cn } from "@/lib/utils";
6
6
 
7
7
  function Switch({
8
- children,
9
8
  className,
10
9
  size = "default",
11
10
  ...props
@@ -13,23 +12,20 @@ function Switch({
13
12
  size?: "sm" | "default";
14
13
  }) {
15
14
  return (
16
- <label
17
- id={props.id}
18
- htmlFor={props.id}
19
- className={cn("flex items-center gap-1.5 text-sm", className)}
15
+ <SwitchPrimitive.Root
16
+ data-slot="switch"
17
+ data-size={size}
18
+ className={cn(
19
+ "peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-checked:bg-primary data-disabled:cursor-not-allowed data-disabled:opacity-50 data-unchecked:bg-input data-[size=default]:h-[18.4px] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 dark:data-unchecked:bg-input/80",
20
+ className,
21
+ )}
22
+ {...props}
20
23
  >
21
- <SwitchPrimitive.Root
22
- data-size={size}
23
- className={cn(
24
- "peer group/switch relative inline-flex w-fit shrink-0 items-center border border-transparent transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-[3px] aria-invalid:ring-destructive/20 data-checked:bg-primary data-disabled:cursor-not-allowed data-disabled:opacity-50 data-unchecked:bg-input data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 dark:data-unchecked:bg-input/80",
25
- className,
26
- )}
27
- {...props}
28
- >
29
- <SwitchPrimitive.Thumb className="pointer-events-none block bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-checked:bg-primary-foreground dark:data-unchecked:bg-foreground" />
30
- </SwitchPrimitive.Root>
31
- {children}
32
- </label>
24
+ <SwitchPrimitive.Thumb
25
+ data-slot="switch-thumb"
26
+ className="pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-checked:bg-primary-foreground dark:data-unchecked:bg-foreground"
27
+ />
28
+ </SwitchPrimitive.Root>
33
29
  );
34
30
  }
35
31
 
@@ -10,14 +10,17 @@ function Tabs({ className, orientation = "horizontal", ...props }: TabsPrimitive
10
10
  <TabsPrimitive.Root
11
11
  data-slot="tabs"
12
12
  data-orientation={orientation}
13
- className={cn("group/tabs flex gap-2 data-horizontal:flex-col", className)}
13
+ className={cn(
14
+ "group/tabs flex gap-2 data-[orientation=horizontal]:flex-col data-[orientation=vertical]:flex-row data-[orientation=horizontal]:**:data-[slot=tabs-list]:h-8 data-[orientation=horizontal]:**:data-[slot=tabs-list]:flex-row data-[orientation=vertical]:**:data-[slot=tabs-list]:h-fit data-[orientation=vertical]:**:data-[slot=tabs-list]:flex-col",
15
+ className,
16
+ )}
14
17
  {...props}
15
18
  />
16
19
  );
17
20
  }
18
21
 
19
22
  const tabsListVariants = cva(
20
- "group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-8 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none",
23
+ "group/tabs-list relative inline-flex w-max overflow-hidden rounded-2xl p-0.5 text-muted-foreground group-data-[orientation=horizontal]/tabs:items-center group-data-[orientation=horizontal]/tabs:justify-center group-data-[orientation=vertical]/tabs:items-stretch group-data-[orientation=vertical]/tabs:justify-start data-[orientation=horizontal]:items-center data-[orientation=horizontal]:justify-center data-[orientation=vertical]:items-stretch data-[orientation=vertical]:justify-start data-[variant=line]:rounded-none",
21
24
  {
22
25
  variants: {
23
26
  variant: {
@@ -32,6 +35,7 @@ const tabsListVariants = cva(
32
35
  );
33
36
 
34
37
  function TabsList({
38
+ children,
35
39
  className,
36
40
  variant = "default",
37
41
  ...props
@@ -42,7 +46,19 @@ function TabsList({
42
46
  data-variant={variant}
43
47
  className={cn(tabsListVariants({ variant }), className)}
44
48
  {...props}
45
- />
49
+ >
50
+ {children}
51
+ <TabsPrimitive.Indicator
52
+ className={cn(
53
+ "pointer-events-none absolute rounded-xl border border-input bg-card shadow-sm transition-[left,right,top,bottom,width,height,transform] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]",
54
+ "data-[orientation=horizontal]:top-(--active-tab-top) data-[orientation=horizontal]:left-(--active-tab-left) data-[orientation=horizontal]:h-(--active-tab-height) data-[orientation=horizontal]:w-(--active-tab-width)",
55
+ "data-[orientation=vertical]:top-(--active-tab-top) data-[orientation=vertical]:left-(--active-tab-left) data-[orientation=vertical]:h-(--active-tab-height) data-[orientation=vertical]:w-(--active-tab-width)",
56
+ "group-data-[variant=line]/tabs-list:rounded-none group-data-[variant=line]/tabs-list:border-0 group-data-[variant=line]/tabs-list:bg-foreground group-data-[variant=line]/tabs-list:shadow-none",
57
+ "group-data-[variant=line]/tabs-list:data-[orientation=horizontal]:top-auto group-data-[variant=line]/tabs-list:data-[orientation=horizontal]:bottom-0 group-data-[variant=line]/tabs-list:data-[orientation=horizontal]:h-0.5",
58
+ "group-data-[variant=line]/tabs-list:data-[orientation=vertical]:right-auto group-data-[variant=line]/tabs-list:data-[orientation=vertical]:left-0 group-data-[variant=line]/tabs-list:data-[orientation=vertical]:w-0.5",
59
+ )}
60
+ />
61
+ </TabsPrimitive.List>
46
62
  );
47
63
  }
48
64
 
@@ -51,10 +67,7 @@ function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {
51
67
  <TabsPrimitive.Tab
52
68
  data-slot="tabs-trigger"
53
69
  className={cn(
54
- "relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none dark:text-muted-foreground dark:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
55
- "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
56
- "data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground",
57
- "after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
70
+ "relative z-10 inline-flex h-7 flex-none items-center justify-center rounded-xl border border-transparent px-2 py-0.5 text-center text-sm font-medium whitespace-nowrap text-muted-foreground transition-[color,transform,opacity] duration-200 ease-out group-data-[orientation=vertical]/tabs:w-full group-data-[variant=line]/tabs-list:rounded-none group-data-[variant=line]/tabs-list:border-0 group-data-[variant=line]/tabs-list:bg-transparent hover:text-foreground focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring group-data-[variant=line]/tabs-list:focus-visible:border-transparent group-data-[variant=line]/tabs-list:focus-visible:ring-0 group-data-[variant=line]/tabs-list:focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-active:text-card-foreground data-[orientation=vertical]:h-fit data-[orientation=vertical]:w-full [&_svg]:pointer-events-none [&_svg]:shrink-0",
58
71
  className,
59
72
  )}
60
73
  {...props}
@@ -62,14 +75,14 @@ function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {
62
75
  );
63
76
  }
64
77
 
65
- function TabsContent({ className, ...props }: TabsPrimitive.Panel.Props) {
78
+ function TabsPanel({ className, ...props }: TabsPrimitive.Panel.Props) {
66
79
  return (
67
80
  <TabsPrimitive.Panel
68
- data-slot="tabs-content"
81
+ data-slot="tabs-panel"
69
82
  className={cn("flex-1 text-sm outline-none", className)}
70
83
  {...props}
71
84
  />
72
85
  );
73
86
  }
74
87
 
75
- export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants };
88
+ export { Tabs, TabsList, TabsTrigger, TabsPanel, tabsListVariants };
@@ -1,48 +1,191 @@
1
1
  "use client";
2
2
 
3
3
  import { Toast as ToastPrimitive } from "@base-ui/react/toast";
4
+ import { cva, type VariantProps } from "class-variance-authority";
4
5
  import { AlertCircle, CheckCircle2 } from "lucide-react";
6
+ import * as React from "react";
5
7
 
6
8
  import { cn } from "@/lib/utils";
7
9
 
8
10
  type ToastTypes = "default" | "success" | "error";
9
11
 
12
+ const toastViewportVariants = cva("fixed z-50 w-80 outline-none", {
13
+ variants: {
14
+ position: {
15
+ "top-left": "top-4 right-auto bottom-auto left-4",
16
+ "top-center": "top-4 right-0 bottom-auto left-0 mx-auto",
17
+ "top-right": "top-4 right-4 bottom-auto left-auto",
18
+ "bottom-left": "top-auto right-auto bottom-4 left-4",
19
+ "bottom-center": "top-auto right-0 bottom-4 left-0 mx-auto",
20
+ "bottom-right": "top-auto right-4 bottom-4 left-auto",
21
+ },
22
+ },
23
+ defaultVariants: {
24
+ position: "bottom-right",
25
+ },
26
+ });
27
+
28
+ const stackedToastVariants = cva(
29
+ "absolute z-[calc(1000-var(--toast-index))] h-(--height) w-full rounded-lg border bg-popup bg-clip-padding p-4 select-none [--gap:0.75rem] [--height:var(--toast-frontmost-height,var(--toast-height))] [--peek:0.75rem] [--scale:calc(max(0,1-(var(--toast-index)*0.1)))] [--shrink:calc(1-var(--scale))] [transition:transform_0.5s_cubic-bezier(0.22,1,0.36,1),opacity_0.5s,height_0.15s] after:absolute after:left-0 after:h-[calc(var(--gap)+1px)] after:w-full after:content-[''] data-ending-style:opacity-0 data-expanded:h-(--toast-height) data-limited:opacity-0 data-ending-style:data-[swipe-direction=down]:transform-[translateY(calc(var(--toast-swipe-movement-y)+150%))] data-expanded:data-ending-style:data-[swipe-direction=down]:transform-[translateY(calc(var(--toast-swipe-movement-y)+150%))] data-ending-style:data-[swipe-direction=left]:transform-[translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))] data-expanded:data-ending-style:data-[swipe-direction=left]:transform-[translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))] data-ending-style:data-[swipe-direction=right]:transform-[translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))] data-expanded:data-ending-style:data-[swipe-direction=right]:transform-[translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))] data-ending-style:data-[swipe-direction=up]:transform-[translateY(calc(var(--toast-swipe-movement-y)-150%))] data-expanded:data-ending-style:data-[swipe-direction=up]:transform-[translateY(calc(var(--toast-swipe-movement-y)-150%))]",
30
+ {
31
+ variants: {
32
+ vertical: {
33
+ top: "top-0 origin-top [transform:translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)+(var(--toast-index)*var(--peek))+(var(--shrink)*var(--height))))_scale(var(--scale))] [--offset-y:calc(var(--toast-offset-y)+(var(--toast-index)*var(--gap))+var(--toast-swipe-movement-y))] after:bottom-full data-expanded:transform-[translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--offset-y)))] data-starting-style:transform-[translateY(-150%)] [&[data-ending-style]:not([data-limited]):not([data-swipe-direction])]:transform-[translateY(-150%)]",
34
+ bottom:
35
+ "bottom-0 origin-bottom [transform:translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)-(var(--toast-index)*var(--peek))-(var(--shrink)*var(--height))))_scale(var(--scale))] [--offset-y:calc(var(--toast-offset-y)*-1+calc(var(--toast-index)*var(--gap)*-1)+var(--toast-swipe-movement-y))] after:top-full data-expanded:transform-[translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--offset-y)))] data-starting-style:transform-[translateY(150%)] [&[data-ending-style]:not([data-limited]):not([data-swipe-direction])]:transform-[translateY(150%)]",
36
+ },
37
+ horizontal: {
38
+ left: "right-auto left-0",
39
+ center: "right-0 left-0 mx-auto",
40
+ right: "right-0 left-auto",
41
+ },
42
+ },
43
+ defaultVariants: {
44
+ vertical: "bottom",
45
+ horizontal: "right",
46
+ },
47
+ },
48
+ );
49
+
50
+ const anchoredToastVariants = cva(
51
+ "pointer-events-auto w-80 rounded-lg border bg-popup bg-clip-padding p-4 data-ending-style:opacity-0 data-starting-style:opacity-0",
52
+ );
53
+
54
+ const toastContentVariants = cva(
55
+ "overflow-hidden transition-opacity duration-250 data-behind:pointer-events-none data-behind:opacity-0 data-expanded:pointer-events-auto data-expanded:opacity-100",
56
+ );
57
+
58
+ const toastBodyVariants = cva("flex items-center gap-3");
59
+
60
+ type ToastPosition = NonNullable<VariantProps<typeof toastViewportVariants>["position"]>;
61
+ type StackVertical = NonNullable<VariantProps<typeof stackedToastVariants>["vertical"]>;
62
+ type StackHorizontal = NonNullable<VariantProps<typeof stackedToastVariants>["horizontal"]>;
63
+ type ToastManager = ReturnType<typeof ToastPrimitive.createToastManager>;
64
+
65
+ interface ToastProviderProps {
66
+ children: React.ReactNode;
67
+ position?: ToastPosition;
68
+ limit?: number;
69
+ timeout?: number;
70
+ toastManager?: ToastManager;
71
+ }
72
+
10
73
  const globalToastManager = ToastPrimitive.createToastManager();
11
- export const toast = globalToastManager;
74
+ export const globalToast = globalToastManager;
75
+
76
+ export function ToastProvider({
77
+ children,
78
+ position = "bottom-right",
79
+ limit = 5,
80
+ timeout = 5000,
81
+ toastManager,
82
+ }: ToastProviderProps) {
83
+ const localToastManagerRef = React.useRef<ToastManager | null>(null);
84
+
85
+ if (!localToastManagerRef.current) {
86
+ localToastManagerRef.current = ToastPrimitive.createToastManager();
87
+ }
88
+
89
+ const manager = toastManager ?? localToastManagerRef.current;
12
90
 
13
- export function ToastProvider({ children }: { children: React.ReactNode }) {
14
91
  return (
15
- <ToastPrimitive.Provider toastManager={globalToastManager} limit={5} timeout={5000}>
92
+ <ToastPrimitive.Provider toastManager={manager} limit={limit} timeout={timeout}>
16
93
  {children}
17
94
  <ToastPrimitive.Portal>
18
- <ToastPrimitive.Viewport className="fixed right-4 bottom-4 z-50 flex flex-col gap-2">
19
- <ToastViewportConsumer />
95
+ <ToastPrimitive.Viewport className={cn(toastViewportVariants({ position }))}>
96
+ <ToastStackConsumer position={position} />
97
+ </ToastPrimitive.Viewport>
98
+ <ToastPrimitive.Viewport className="pointer-events-none fixed inset-0 z-50 outline-none">
99
+ <ToastAnchorConsumer />
20
100
  </ToastPrimitive.Viewport>
21
101
  </ToastPrimitive.Portal>
22
102
  </ToastPrimitive.Provider>
23
103
  );
24
104
  }
25
105
 
26
- function ToastViewportConsumer() {
106
+ function ToastStackConsumer({ position }: { position: ToastPosition }) {
27
107
  const { toasts } = ToastPrimitive.useToastManager();
108
+ const stackToasts = toasts.filter((currentToast) => !currentToast.positionerProps?.anchor);
109
+
28
110
  return (
29
111
  <>
30
- {toasts.map((toast) => (
31
- <Toast key={toast.id} toast={toast} />
112
+ {stackToasts.map((currentToast) => (
113
+ <Toast key={currentToast.id} toast={currentToast} position={position} />
32
114
  ))}
33
115
  </>
34
116
  );
35
117
  }
36
118
 
37
- function Toast({ className, toast, ...props }: ToastPrimitive.Root.Props) {
119
+ function ToastAnchorConsumer() {
120
+ const { toasts } = ToastPrimitive.useToastManager();
121
+ const anchorToasts = toasts.filter((currentToast) =>
122
+ Boolean(currentToast.positionerProps?.anchor),
123
+ );
124
+
125
+ return (
126
+ <>
127
+ {anchorToasts.map((currentToast) => {
128
+ const { className, ...positionerProps } = currentToast.positionerProps ?? {};
129
+
130
+ return (
131
+ <ToastPrimitive.Positioner
132
+ key={currentToast.id}
133
+ toast={currentToast}
134
+ className={cn("pointer-events-none z-[calc(1000-var(--toast-index))]", className)}
135
+ {...positionerProps}
136
+ >
137
+ <AnchoredToast toast={currentToast} />
138
+ </ToastPrimitive.Positioner>
139
+ );
140
+ })}
141
+ </>
142
+ );
143
+ }
144
+
145
+ function Toast({
146
+ className,
147
+ toast,
148
+ position,
149
+ ...props
150
+ }: ToastPrimitive.Root.Props & { position: ToastPosition }) {
151
+ const { vertical, horizontal } = getStackPlacement(position);
152
+
153
+ return (
154
+ <ToastPrimitive.Root
155
+ toast={toast}
156
+ className={cn(stackedToastVariants({ vertical, horizontal, className }))}
157
+ {...props}
158
+ >
159
+ <ToastPrimitive.Content className={cn(toastContentVariants())}>
160
+ <ToastBody toast={toast} />
161
+ </ToastPrimitive.Content>
162
+ </ToastPrimitive.Root>
163
+ );
164
+ }
165
+
166
+ function AnchoredToast({ className, toast, ...props }: ToastPrimitive.Root.Props) {
167
+ return (
168
+ <ToastPrimitive.Root
169
+ toast={toast}
170
+ className={cn(anchoredToastVariants({ className }))}
171
+ {...props}
172
+ >
173
+ <ToastPrimitive.Content className={cn(toastContentVariants())}>
174
+ <ToastBody toast={toast} />
175
+ </ToastPrimitive.Content>
176
+ </ToastPrimitive.Root>
177
+ );
178
+ }
179
+
180
+ function ToastBody({ toast }: { toast: ToastPrimitive.Root.Props["toast"] }) {
38
181
  let icon;
39
182
 
40
183
  switch (toast.type as ToastTypes) {
41
184
  case "success":
42
- icon = <CheckCircle2 className="h-4 w-4 text-green-600" />;
185
+ icon = <CheckCircle2 className="h-4 w-4 text-success" />;
43
186
  break;
44
187
  case "error":
45
- icon = <AlertCircle className="h-4 w-4 text-red-600" />;
188
+ icon = <AlertCircle className="h-4 w-4 text-destructive" />;
46
189
  break;
47
190
  default:
48
191
  icon = null;
@@ -50,27 +193,45 @@ function Toast({ className, toast, ...props }: ToastPrimitive.Root.Props) {
50
193
  }
51
194
 
52
195
  return (
53
- <ToastPrimitive.Root
54
- toast={toast}
55
- className={cn(
56
- "flex w-80 items-center gap-3 rounded-lg border bg-popup p-4 shadow-md",
57
- className,
58
- )}
59
- {...props}
60
- >
61
- {icon && <div>{icon}</div>}
196
+ <div className={cn(toastBodyVariants())}>
197
+ {icon ? <div>{icon}</div> : null}
62
198
  <div className="flex flex-1 flex-col">
63
- {toast.title && (
64
- <ToastPrimitive.Title className="text-sm font-medium">{toast.title}</ToastPrimitive.Title>
65
- )}
66
- {toast.description && (
67
- <ToastPrimitive.Description className="text-sm text-muted-foreground">
68
- {toast.description}
69
- </ToastPrimitive.Description>
70
- )}
199
+ {toast.title ? <ToastPrimitive.Title className="text-sm font-medium" /> : null}
200
+ {toast.description ? (
201
+ <ToastPrimitive.Description className="text-sm text-muted-foreground" />
202
+ ) : null}
203
+ {toast.actionProps ? (
204
+ <ToastPrimitive.Action className="mt-2 inline-flex h-7 items-center justify-center self-start rounded-md border px-2.5 text-xs font-medium hover:bg-muted" />
205
+ ) : null}
71
206
  </div>
72
- </ToastPrimitive.Root>
207
+ </div>
73
208
  );
74
209
  }
75
210
 
211
+ function getStackPlacement(position: ToastPosition): {
212
+ vertical: StackVertical;
213
+ horizontal: StackHorizontal;
214
+ } {
215
+ const vertical: StackVertical = position.startsWith("top") ? "top" : "bottom";
216
+
217
+ let horizontal: StackHorizontal = "center";
218
+ if (position.endsWith("left")) {
219
+ horizontal = "left";
220
+ }
221
+ if (position.endsWith("right")) {
222
+ horizontal = "right";
223
+ }
224
+
225
+ return {
226
+ vertical,
227
+ horizontal,
228
+ };
229
+ }
230
+
231
+ function useToast<Data extends object = any>() {
232
+ return ToastPrimitive.useToastManager<Data>();
233
+ }
234
+
76
235
  export { ToastProvider as Toast };
236
+ export { useToast };
237
+ export type { ToastPosition, ToastManager };
@@ -7,7 +7,7 @@ import { cva, type VariantProps } from "class-variance-authority";
7
7
  import { cn } from "@/lib/utils";
8
8
 
9
9
  const toggleVariants = cva(
10
- "group/toggle inline-flex items-center justify-center gap-1 rounded text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-pressed:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
10
+ "group/toggle inline-flex items-center justify-center gap-1 rounded text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-pressed:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_[data-slot=fill]_*]:fill-transparent data-[pressed]:[&_[data-slot=fill]_*]:fill-current",
11
11
  {
12
12
  variants: {
13
13
  variant: {
@@ -4,10 +4,12 @@ import { Toolbar as ToolbarPrimitive } from "@base-ui/react";
4
4
 
5
5
  import { cn } from "@/lib/utils";
6
6
 
7
+ import { Input } from "./input";
8
+
7
9
  function Toolbar({ className, ...props }: ToolbarPrimitive.Root.Props) {
8
10
  return (
9
11
  <ToolbarPrimitive.Root
10
- className={cn("flex h-9 items-center justify-evenly gap-1 border bg-muted p-1", className)}
12
+ className={cn("flex h-9 items-center justify-evenly gap-1 rounded-xl border p-1", className)}
11
13
  {...props}
12
14
  />
13
15
  );
@@ -22,18 +24,29 @@ function ToolbarLink({ className, ...props }: ToolbarPrimitive.Link.Props) {
22
24
  }
23
25
 
24
26
  function ToolbarInput({ className, ...props }: ToolbarPrimitive.Input.Props) {
25
- return <ToolbarPrimitive.Input className={cn(className)} {...props} />;
27
+ return (
28
+ <ToolbarPrimitive.Input
29
+ className={cn(
30
+ "rounded-none border-0 ring-0 outline-0 focus:ring-0 focus-visible:ring-0 focus-visible:outline-0",
31
+ className,
32
+ )}
33
+ render={<Input />}
34
+ {...props}
35
+ />
36
+ );
26
37
  }
27
38
 
28
39
  function ToolbarGroup({ className, ...props }: ToolbarPrimitive.Group.Props) {
29
- return <ToolbarPrimitive.Group className={cn(className)} {...props} />;
40
+ return (
41
+ <ToolbarPrimitive.Group className={cn("flex items-center gap-0.5", className)} {...props} />
42
+ );
30
43
  }
31
44
 
32
45
  function ToolbarSeparator({ className, ...props }: ToolbarPrimitive.Separator.Props) {
33
46
  return (
34
47
  <ToolbarPrimitive.Separator
35
48
  className={cn(
36
- "shrink-0 bg-primary/40 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:self-stretch",
49
+ "shrink-0 bg-primary/30 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:self-stretch",
37
50
  className,
38
51
  )}
39
52
  {...props}
@@ -0,0 +1,54 @@
1
+ "use client";
2
+
3
+ import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip";
4
+
5
+ import { cn } from "@/lib/utils";
6
+
7
+ function TooltipProvider({ delay = 0, ...props }: TooltipPrimitive.Provider.Props) {
8
+ return <TooltipPrimitive.Provider data-slot="tooltip-provider" delay={delay} {...props} />;
9
+ }
10
+
11
+ function Tooltip({ ...props }: TooltipPrimitive.Root.Props) {
12
+ return <TooltipPrimitive.Root data-slot="tooltip" {...props} />;
13
+ }
14
+
15
+ function TooltipTrigger({ ...props }: TooltipPrimitive.Trigger.Props) {
16
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
17
+ }
18
+
19
+ function TooltipPopup({
20
+ className,
21
+ side = "top",
22
+ sideOffset = 4,
23
+ align = "center",
24
+ alignOffset = 0,
25
+ children,
26
+ ...props
27
+ }: TooltipPrimitive.Popup.Props &
28
+ Pick<TooltipPrimitive.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset">) {
29
+ return (
30
+ <TooltipPrimitive.Portal>
31
+ <TooltipPrimitive.Positioner
32
+ align={align}
33
+ alignOffset={alignOffset}
34
+ side={side}
35
+ sideOffset={sideOffset}
36
+ className="isolate z-50"
37
+ >
38
+ <TooltipPrimitive.Popup
39
+ data-slot="tooltip-popup"
40
+ className={cn(
41
+ "z-50 w-fit max-w-xs origin-(--transform-origin) rounded-md bg-foreground px-3 py-1.5 text-xs text-background data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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 data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95",
42
+ className,
43
+ )}
44
+ {...props}
45
+ >
46
+ {children}
47
+ <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" />
48
+ </TooltipPrimitive.Popup>
49
+ </TooltipPrimitive.Positioner>
50
+ </TooltipPrimitive.Portal>
51
+ );
52
+ }
53
+
54
+ export { Tooltip, TooltipTrigger, TooltipPopup, TooltipProvider };