shapes-ui 0.4.1 → 0.5.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 (149) 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 +68 -0
  6. package/.github/workflows/release.yml +8 -0
  7. package/.idea/compiler.xml +6 -0
  8. package/.idea/copilot.data.migration.ask2agent.xml +6 -0
  9. package/.idea/vcs.xml +6 -0
  10. package/CHANGELOG.md +31 -0
  11. package/CODE_OF_CONDUCT.md +41 -0
  12. package/CONTRIBUTING.md +52 -0
  13. package/README.md +5 -0
  14. package/SECURITY.md +0 -0
  15. package/content/components/field.mdx +2 -2
  16. package/content/components/fieldset.mdx +1 -1
  17. package/content/components/menubar.mdx +4 -2
  18. package/content/components/meter.mdx +13 -1
  19. package/content/components/navigation-menu.mdx +6 -0
  20. package/content/components/number-field.mdx +24 -0
  21. package/content/components/popover.mdx +20 -0
  22. package/content/components/preview-card.mdx +11 -0
  23. package/content/components/progress.mdx +2 -0
  24. package/content/components/radio.mdx +20 -0
  25. package/content/components/select.mdx +30 -0
  26. package/content/components/slider.mdx +48 -0
  27. package/content/components/switch.mdx +26 -0
  28. package/content/components/tabs.mdx +32 -0
  29. package/content/components/toast.mdx +60 -0
  30. package/content/components/toggle.mdx +34 -2
  31. package/content/components/toolbar.mdx +26 -0
  32. package/content/components/tooltip.mdx +25 -0
  33. package/content-collections.ts +1 -1
  34. package/examples/__index.tsx +231 -0
  35. package/examples/checkbox-demo.tsx +1 -1
  36. package/examples/checkbox-form.tsx +3 -3
  37. package/examples/field-custom-control.tsx +33 -9
  38. package/examples/form-demo.tsx +5 -10
  39. package/examples/menu-advanced.tsx +1 -3
  40. package/examples/menu-align.tsx +19 -16
  41. package/examples/menu-checkbox.tsx +2 -3
  42. package/examples/menu-demo.tsx +1 -3
  43. package/examples/menu-group.tsx +1 -3
  44. package/examples/menu-radio.tsx +1 -3
  45. package/examples/menu-submenu.tsx +2 -3
  46. package/examples/menubar-advanced.tsx +91 -0
  47. package/examples/meter-demo.tsx +8 -21
  48. package/examples/meter-flip.tsx +13 -0
  49. package/examples/meter-no-label.tsx +12 -0
  50. package/examples/meter-no-value.tsx +12 -0
  51. package/examples/navigation-menu-demo.tsx +113 -1
  52. package/examples/number-field-buttons-end.tsx +20 -0
  53. package/examples/number-field-demo.tsx +17 -1
  54. package/examples/number-field-scrub.tsx +38 -0
  55. package/examples/popover-demo.tsx +18 -1
  56. package/examples/popover-form.tsx +46 -0
  57. package/examples/popover-positions.tsx +54 -0
  58. package/examples/preview-card-demo.tsx +26 -1
  59. package/examples/preview-card-links.tsx +38 -0
  60. package/examples/progress-demo.tsx +33 -1
  61. package/examples/radio-card.tsx +28 -0
  62. package/examples/radio-demo.tsx +19 -1
  63. package/examples/radio-description.tsx +26 -0
  64. package/examples/radio-orientation.tsx +21 -0
  65. package/examples/select-alignment.tsx +51 -0
  66. package/examples/select-demo.tsx +36 -1
  67. package/examples/select-disabled.tsx +38 -0
  68. package/examples/select-groups.tsx +54 -0
  69. package/examples/select-invalid.tsx +41 -0
  70. package/examples/select-scrollable.tsx +112 -0
  71. package/examples/slider-controlled.tsx +28 -0
  72. package/examples/slider-demo.tsx +3 -1
  73. package/examples/slider-disabled.tsx +7 -0
  74. package/examples/slider-edge.tsx +13 -0
  75. package/examples/slider-multiple.tsx +7 -0
  76. package/examples/slider-range.tsx +5 -0
  77. package/examples/slider-vertical.tsx +10 -0
  78. package/examples/switch-demo.tsx +19 -1
  79. package/examples/switch-disabled.tsx +20 -0
  80. package/examples/switch-sizes.tsx +24 -0
  81. package/examples/switch-with-label.tsx +16 -0
  82. package/examples/tabs-demo.tsx +14 -1
  83. package/examples/tabs-disabled.tsx +21 -0
  84. package/examples/tabs-line.tsx +18 -0
  85. package/examples/tabs-vertical.tsx +13 -0
  86. package/examples/toast-action.tsx +39 -0
  87. package/examples/toast-anchored.tsx +36 -0
  88. package/examples/toast-demo.tsx +27 -1
  89. package/examples/toast-positions.tsx +54 -0
  90. package/examples/toast-promise.tsx +51 -0
  91. package/examples/toast-stacked.tsx +30 -0
  92. package/examples/toast-timeout.tsx +43 -0
  93. package/examples/toast-update.tsx +38 -0
  94. package/examples/toast-variants.tsx +54 -0
  95. package/examples/toggle-controlled.tsx +20 -0
  96. package/examples/toggle-demo.tsx +7 -51
  97. package/examples/toggle-group-demo.tsx +19 -0
  98. package/examples/toggle-group-multiple.tsx +19 -0
  99. package/examples/toggle-icon-fill.tsx +12 -0
  100. package/examples/toolbar-demo.tsx +45 -21
  101. package/examples/toolbar-input-link.tsx +35 -0
  102. package/examples/toolbar-menu.tsx +53 -0
  103. package/examples/tooltip-demo.tsx +48 -0
  104. package/examples/tooltip-positions.tsx +60 -0
  105. package/package.json +8 -8
  106. package/public/r/drawer.json +1 -1
  107. package/public/r/field.json +1 -1
  108. package/public/r/menubar.json +1 -1
  109. package/public/r/meter.json +1 -1
  110. package/public/r/navigation-menu.json +1 -1
  111. package/public/r/number-field.json +4 -2
  112. package/public/r/popover.json +1 -1
  113. package/public/r/preview-card.json +1 -1
  114. package/public/r/progress.json +1 -1
  115. package/public/r/radio.json +1 -1
  116. package/public/r/select.json +1 -1
  117. package/public/r/slider.json +1 -1
  118. package/public/r/switch.json +1 -1
  119. package/public/r/tabs.json +1 -1
  120. package/public/r/toast.json +2 -1
  121. package/public/r/toggle.json +1 -1
  122. package/public/r/toolbar.json +1 -1
  123. package/public/r/tooltip.json +15 -0
  124. package/src/components/docs/layout/header.tsx +4 -14
  125. package/src/components/docs/layout/mobile-menu.tsx +27 -78
  126. package/src/components/docs/layout/nav-list.tsx +27 -21
  127. package/src/components/docs/layout/split-layout.tsx +6 -3
  128. package/src/components/ui/badge.tsx +1 -1
  129. package/src/components/ui/checkbox.tsx +1 -1
  130. package/src/components/ui/drawer.tsx +1 -1
  131. package/src/components/ui/field.tsx +9 -28
  132. package/src/components/ui/form.tsx +1 -1
  133. package/src/components/ui/menubar.tsx +52 -18
  134. package/src/components/ui/meter.tsx +12 -24
  135. package/src/components/ui/navigation-menu.tsx +121 -38
  136. package/src/components/ui/number-field.tsx +42 -46
  137. package/src/components/ui/popover.tsx +7 -2
  138. package/src/components/ui/preview-card.tsx +4 -2
  139. package/src/components/ui/progress.tsx +7 -18
  140. package/src/components/ui/radio.tsx +32 -19
  141. package/src/components/ui/select.tsx +6 -6
  142. package/src/components/ui/slider.tsx +8 -5
  143. package/src/components/ui/switch.tsx +13 -17
  144. package/src/components/ui/tabs.tsx +23 -10
  145. package/src/components/ui/toast.tsx +190 -29
  146. package/src/components/ui/toggle.tsx +1 -1
  147. package/src/components/ui/toolbar.tsx +17 -4
  148. package/src/components/ui/tooltip.tsx +54 -0
  149. package/src/routes/__root.tsx +3 -5
@@ -3,31 +3,32 @@
3
3
  import { Menu as MenuPrimitive } from "@base-ui/react/menu";
4
4
  import { Menubar as MenubarPrimitive } from "@base-ui/react/menubar";
5
5
  import { ChevronRightIcon, CheckIcon } from "lucide-react";
6
+ import { ComponentProps } from "react";
6
7
 
7
8
  import { cn } from "@/lib/utils";
8
9
 
9
10
  function Menubar({ className, ...props }: MenubarPrimitive.Props) {
10
11
  return (
11
12
  <MenubarPrimitive
12
- className={cn("flex h-9 items-center gap-0.5 border bg-background p-1", className)}
13
+ data-slot="menubar"
14
+ className={cn("flex h-8 items-center gap-0.5 rounded-lg border bg-background p-1", className)}
13
15
  {...props}
14
16
  />
15
17
  );
16
18
  }
17
19
 
18
20
  function Menu(props: MenuPrimitive.Root.Props) {
19
- return <MenuPrimitive.Root {...props} />;
21
+ return <MenuPrimitive.Root data-slot="menu" {...props} />;
20
22
  }
21
23
 
22
24
  function MenuTrigger({ className, ...props }: MenuPrimitive.Trigger.Props) {
23
25
  return (
24
26
  <MenuPrimitive.Trigger
25
- className={cn("h-full px-4 text-sm hover:bg-accent", className)}
27
+ className={cn("h-full rounded px-4 text-sm hover:bg-accent", className)}
26
28
  {...props}
27
29
  />
28
30
  );
29
31
  }
30
-
31
32
  function MenuPopup({
32
33
  className,
33
34
  align = "start",
@@ -46,8 +47,9 @@ function MenuPopup({
46
47
  alignOffset={alignOffset}
47
48
  >
48
49
  <MenuPrimitive.Popup
50
+ data-slot="menu-popup"
49
51
  className={cn(
50
- "text-popover-foreground z-50 max-h-(--available-height) 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 outline-none 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",
52
+ "text-popover-foreground z-50 max-h-(--available-height) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-xl bg-popup p-1 shadow-md ring-1 ring-foreground/10 duration-100 outline-none 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",
51
53
  className,
52
54
  )}
53
55
  {...props}
@@ -61,7 +63,7 @@ function MenuGroup({ ...props }: MenuPrimitive.Group.Props) {
61
63
  return <MenuPrimitive.Group data-slot="-menu-group" {...props} />;
62
64
  }
63
65
 
64
- function MenuGroupLabel({
66
+ function MenuLabel({
65
67
  className,
66
68
  inset,
67
69
  ...props
@@ -70,10 +72,10 @@ function MenuGroupLabel({
70
72
  }) {
71
73
  return (
72
74
  <MenuPrimitive.GroupLabel
73
- data-slot="-menu-label"
75
+ data-slot="menu-label"
74
76
  data-inset={inset}
75
77
  className={cn(
76
- "px-1.5 py-1 text-xs font-medium text-muted-foreground data-[inset]:pl-8",
78
+ "px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-8",
77
79
  className,
78
80
  )}
79
81
  {...props}
@@ -88,15 +90,15 @@ function MenuItem({
88
90
  ...props
89
91
  }: MenuPrimitive.Item.Props & {
90
92
  inset?: boolean;
91
- variant?: "default" | "destructive";
93
+ variant?: "default" | "destructive" | "success" | "warning";
92
94
  }) {
93
95
  return (
94
96
  <MenuPrimitive.Item
95
- data-slot="-menu-item"
97
+ data-slot="menu-item"
96
98
  data-inset={inset}
97
99
  data-variant={variant}
98
100
  className={cn(
99
- "group/-menu-item relative flex cursor-default items-center gap-1.5 px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 focus:*:[svg]:text-accent-foreground data-[variant=destructive]:*:[svg]:text-destructive",
101
+ "group/-menu-item relative flex cursor-default items-center gap-1.5 rounded px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive data-[variant=success]:text-success data-[variant=success]:focus:bg-success/10 data-[variant=success]:focus:text-success data-[variant=warning]:text-warning data-[variant=warning]:focus:bg-warning/10 data-[variant=warning]:focus:text-warning dark:data-[variant=destructive]:focus:bg-destructive/20 dark:data-[variant=success]:focus:bg-success/20 dark:data-[variant=warning]:focus:bg-warning/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 focus:*:[svg]:text-accent-foreground data-[variant=destructive]:*:[svg]:text-destructive",
100
102
  className,
101
103
  )}
102
104
  {...props}
@@ -108,6 +110,30 @@ function MenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
108
110
  return <MenuPrimitive.SubmenuRoot data-slot="-menu-sub" {...props} />;
109
111
  }
110
112
 
113
+ function MenuSubPopup({
114
+ align = "start",
115
+ alignOffset = -3,
116
+ side = "right",
117
+ sideOffset = 0,
118
+ className,
119
+ ...props
120
+ }: React.ComponentProps<typeof MenuPopup>) {
121
+ return (
122
+ <MenuPopup
123
+ data-slot="menu-sub-content"
124
+ className={cn(
125
+ "text-popover-foreground w-auto min-w-24 rounded-md bg-popup p-1 shadow-lg 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=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
126
+ className,
127
+ )}
128
+ align={align}
129
+ alignOffset={alignOffset}
130
+ side={side}
131
+ sideOffset={sideOffset}
132
+ {...props}
133
+ />
134
+ );
135
+ }
136
+
111
137
  function MenuSubTrigger({
112
138
  className,
113
139
  inset,
@@ -118,7 +144,7 @@ function MenuSubTrigger({
118
144
  }) {
119
145
  return (
120
146
  <MenuPrimitive.SubmenuTrigger
121
- data-slot="-menu-sub-trigger"
147
+ data-slot="menu-sub-trigger"
122
148
  data-inset={inset}
123
149
  className={cn(
124
150
  "flex cursor-default items-center gap-1.5 px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-8 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
@@ -140,7 +166,7 @@ function MenuCheckboxItem({
140
166
  }: MenuPrimitive.CheckboxItem.Props) {
141
167
  return (
142
168
  <MenuPrimitive.CheckboxItem
143
- data-slot="-menu-checkbox-item"
169
+ data-slot="menu-checkbox-item"
144
170
  className={cn(
145
171
  "relative flex 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",
146
172
  className,
@@ -159,13 +185,13 @@ function MenuCheckboxItem({
159
185
  }
160
186
 
161
187
  function MenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
162
- return <MenuPrimitive.RadioGroup data-slot="-menu-radio-group" {...props} />;
188
+ return <MenuPrimitive.RadioGroup data-slot="menu-radio-group" {...props} />;
163
189
  }
164
190
 
165
191
  function MenuRadioItem({ className, children, ...props }: MenuPrimitive.RadioItem.Props) {
166
192
  return (
167
193
  <MenuPrimitive.RadioItem
168
- data-slot="-menu-radio-item"
194
+ data-slot="menu-radio-item"
169
195
  className={cn(
170
196
  "relative flex 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",
171
197
  className,
@@ -185,25 +211,33 @@ function MenuRadioItem({ className, children, ...props }: MenuPrimitive.RadioIte
185
211
  function MenuSeparator({ className, ...props }: MenuPrimitive.Separator.Props) {
186
212
  return (
187
213
  <MenuPrimitive.Separator
188
- data-slot="-menu-separator"
189
- className={cn("-mx-1 my-1 h-full w-px bg-border", className)}
214
+ data-slot="menu-separator"
215
+ className={cn("-mx-1 my-1 h-px bg-border", className)}
190
216
  {...props}
191
217
  />
192
218
  );
193
219
  }
194
220
 
221
+ function MenuShortcut({ className, ...props }: ComponentProps<"span">) {
222
+ return (
223
+ <span className={cn("ml-auto text-xs font-thin text-muted-foreground", className)} {...props} />
224
+ );
225
+ }
226
+
195
227
  export {
196
228
  Menubar,
197
229
  Menu,
198
230
  MenuTrigger,
199
231
  MenuPopup,
200
232
  MenuGroup,
201
- MenuGroupLabel,
233
+ MenuLabel,
202
234
  MenuItem,
203
235
  MenuSub,
236
+ MenuSubPopup,
204
237
  MenuSubTrigger,
205
238
  MenuCheckboxItem,
206
239
  MenuRadioGroup,
207
240
  MenuRadioItem,
208
241
  MenuSeparator,
242
+ MenuShortcut,
209
243
  };
@@ -4,44 +4,32 @@ import { Meter as MeterPrimitive } from "@base-ui/react/meter";
4
4
 
5
5
  import { cn } from "@/lib/utils";
6
6
 
7
- function Meter({
8
- label,
9
- showLabel,
10
- showValue,
11
- className,
12
- ...props
13
- }: MeterPrimitive.Root.Props & {
14
- showValue?: boolean;
15
- showLabel?: boolean;
16
- label?: string;
17
- }) {
18
- return (
19
- <MeterPrimitive.Root className={cn("grid w-80 grid-cols-2 gap-1", className)} {...props}>
20
- {showLabel && label && <MeterLabel>{label}</MeterLabel>}
21
- {showValue && <MeterValue />}
22
- <MeterTrack>
23
- <MeterIndicator />
24
- </MeterTrack>
25
- </MeterPrimitive.Root>
26
- );
7
+ function Meter({ className, ...props }: MeterPrimitive.Root.Props) {
8
+ return <MeterPrimitive.Root className={cn("grid grid-cols-2 gap-1", className)} {...props} />;
27
9
  }
28
10
 
29
11
  function MeterLabel({ className, ...props }: MeterPrimitive.Label.Props) {
30
12
  return (
31
- <MeterPrimitive.Label className={cn("text-xs font-bold text-primary", className)} {...props} />
13
+ <MeterPrimitive.Label
14
+ className={cn("col-span-1 text-xs font-bold text-primary", className)}
15
+ {...props}
16
+ />
32
17
  );
33
18
  }
34
19
 
35
20
  function MeterValue({ className, ...props }: MeterPrimitive.Value.Props) {
36
21
  return (
37
- <MeterPrimitive.Value className={cn("text-right text-xs text-primary", className)} {...props} />
22
+ <MeterPrimitive.Value
23
+ className={cn("col-span-1 text-right text-xs text-primary", className)}
24
+ {...props}
25
+ />
38
26
  );
39
27
  }
40
28
 
41
29
  function MeterTrack({ className, ...props }: MeterPrimitive.Track.Props) {
42
30
  return (
43
31
  <MeterPrimitive.Track
44
- className={cn("col-span-2 block h-2 overflow-hidden bg-primary/20", className)}
32
+ className={cn("col-span-2 h-2 w-full overflow-hidden rounded-full bg-primary/20", className)}
45
33
  {...props}
46
34
  />
47
35
  );
@@ -50,7 +38,7 @@ function MeterTrack({ className, ...props }: MeterPrimitive.Track.Props) {
50
38
  function MeterIndicator({ className, ...props }: MeterPrimitive.Indicator.Props) {
51
39
  return (
52
40
  <MeterPrimitive.Indicator
53
- className={cn("block rounded-r-xl bg-primary", className)}
41
+ className={cn("block rounded-full bg-primary", className)}
54
42
  {...props}
55
43
  />
56
44
  );
@@ -2,31 +2,49 @@
2
2
 
3
3
  import { NavigationMenu as NavigationMenuPrimitive } from "@base-ui/react/navigation-menu";
4
4
  import { ChevronDownIcon } from "lucide-react";
5
- import type React from "react";
6
5
 
7
6
  import { cn } from "@/lib/utils";
8
7
 
9
- function NavigationMenu(props: NavigationMenuPrimitive.Root.Props) {
10
- return <NavigationMenuPrimitive.Root {...props} />;
11
- }
12
-
13
- function NavigationMenuList({
8
+ function NavigationMenu({
14
9
  className,
10
+ delay = 50,
11
+ closeDelay = 50,
12
+ orientation = "horizontal",
15
13
  ...props
16
- }: React.ComponentProps<typeof NavigationMenuPrimitive.List>) {
14
+ }: NavigationMenuPrimitive.Root.Props) {
15
+ return (
16
+ <NavigationMenuPrimitive.Root
17
+ data-slot="navigation-menu"
18
+ delay={delay}
19
+ closeDelay={closeDelay}
20
+ orientation={orientation}
21
+ className={cn(
22
+ "group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
23
+ className,
24
+ )}
25
+ {...props}
26
+ />
27
+ );
28
+ }
29
+
30
+ function NavigationMenuList({ className, ...props }: NavigationMenuPrimitive.List.Props) {
17
31
  return (
18
32
  <NavigationMenuPrimitive.List
19
- className={cn("flex items-center gap-0.5", className)}
33
+ data-slot="navigation-menu-list"
34
+ className={cn("group flex flex-1 list-none items-center justify-center gap-1", className)}
20
35
  {...props}
21
36
  />
22
37
  );
23
38
  }
24
39
 
25
- function NavigationMenuItem({
26
- className,
27
- ...props
28
- }: React.ComponentProps<typeof NavigationMenuPrimitive.Item>) {
29
- return <NavigationMenuPrimitive.Item className={cn(className)} {...props} />;
40
+ function NavigationMenuItem({ className, ...props }: NavigationMenuPrimitive.Item.Props) {
41
+ return (
42
+ <NavigationMenuPrimitive.Item
43
+ data-slot="navigation-menu-item"
44
+ className={cn("relative", className)}
45
+ {...props}
46
+ />
47
+ );
30
48
  }
31
49
 
32
50
  function NavigationMenuTrigger({
@@ -36,61 +54,125 @@ function NavigationMenuTrigger({
36
54
  }: NavigationMenuPrimitive.Trigger.Props) {
37
55
  return (
38
56
  <NavigationMenuPrimitive.Trigger
39
- className={cn("group h-full px-3 py-1.5 text-sm font-medium hover:bg-accent/60", className)}
57
+ data-slot="navigation-menu-trigger"
58
+ className={cn(
59
+ "group/navigation-menu-trigger relative inline-flex h-9 w-max items-center justify-center rounded-lg bg-background px-2.5 py-1.5 text-sm font-medium transition-all outline-none hover:bg-muted focus:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[popup-open]:bg-muted/50 data-[popup-open]:hover:bg-muted",
60
+ className,
61
+ )}
40
62
  {...props}
41
63
  >
42
64
  {children}
43
- <ChevronDownIcon className="ml-1 inline-block size-3 transition-all ease-in group-data-popup-open:rotate-180" />
65
+ <NavigationMenuPrimitive.Icon
66
+ render={
67
+ <ChevronDownIcon
68
+ className="relative top-px ml-1 size-3 transition-transform duration-300 group-data-[popup-open]/navigation-menu-trigger:rotate-180"
69
+ aria-hidden="true"
70
+ />
71
+ }
72
+ />
44
73
  </NavigationMenuPrimitive.Trigger>
45
74
  );
46
75
  }
47
76
 
48
- function NavigationMenuContent(props: NavigationMenuPrimitive.Content.Props) {
49
- return <NavigationMenuPrimitive.Content {...props} />;
50
- }
51
-
52
- function NavigationMenuLink(props: NavigationMenuPrimitive.Link.Props) {
53
- return <NavigationMenuPrimitive.Link {...props} />;
77
+ function NavigationMenuContent({ className, ...props }: NavigationMenuPrimitive.Content.Props) {
78
+ return (
79
+ <NavigationMenuPrimitive.Content
80
+ data-slot="navigation-menu-content"
81
+ className={cn(
82
+ "flex h-full w-auto p-1 transition-[opacity,transform,translate] duration-[350ms] ease-[cubic-bezier(0.22,1,0.36,1)] data-[ending-style]:opacity-0 data-[ending-style]:data-[activation-direction=left]:translate-x-1/2 data-[ending-style]:data-[activation-direction=right]:-translate-x-1/2 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none data-[starting-style]:opacity-0 data-[starting-style]:data-[activation-direction=left]:-translate-x-1/2 data-[starting-style]:data-[activation-direction=right]:translate-x-1/2",
83
+ className,
84
+ )}
85
+ {...props}
86
+ />
87
+ );
54
88
  }
55
89
 
56
90
  function NavigationMenuPopup({
91
+ className,
92
+ sideOffset = 10,
57
93
  align = "start",
58
- alignOffset,
94
+ collisionPadding = 10,
59
95
  side = "bottom",
60
- sideOffset = 8,
61
- className,
62
- renderViewport = true,
96
+ anchor,
63
97
  children,
64
98
  ...props
65
99
  }: NavigationMenuPrimitive.Popup.Props &
66
100
  Pick<
67
101
  NavigationMenuPrimitive.Positioner.Props,
68
- "sideOffset" | "alignOffset" | "align" | "side"
69
- > & {
70
- renderViewport?: boolean;
71
- }) {
102
+ "sideOffset" | "align" | "collisionPadding" | "side" | "anchor"
103
+ >) {
72
104
  return (
73
- <NavigationMenuPrimitive.Portal keepMounted>
105
+ <NavigationMenuPrimitive.Portal>
74
106
  <NavigationMenuPrimitive.Positioner
75
- align={align}
76
- alignOffset={alignOffset}
77
107
  side={side}
78
108
  sideOffset={sideOffset}
109
+ align={align}
110
+ collisionPadding={collisionPadding}
111
+ anchor={anchor}
112
+ className={
113
+ "isolate z-50 h-(--positioner-height) w-(--positioner-width) max-w-(--available-width) transition-[top,left,right,bottom] duration-[350ms] ease-[cubic-bezier(0.22,1,0.36,1)] data-[instant]:transition-none"
114
+ }
79
115
  >
80
116
  <NavigationMenuPrimitive.Popup
81
- className={cn("bg-popup p-2 shadow outline outline-border", className)}
82
117
  {...props}
118
+ data-slot="navigation-menu-popup"
119
+ className={cn(
120
+ "relative h-(--popup-height) w-(--popup-width) origin-(--transform-origin) overflow-hidden rounded-lg bg-popup text-popup-foreground shadow ring-1 ring-foreground/10 transition-[opacity,transform,width,height,scale,translate] duration-[350ms] ease-[cubic-bezier(0.22,1,0.36,1)] outline-none data-[ending-style]:scale-95 data-[ending-style]:opacity-0 data-[ending-style]:duration-150 data-[ending-style]:ease-in data-[starting-style]:scale-95 data-[starting-style]:opacity-0",
121
+ className,
122
+ )}
83
123
  >
84
- {children}
85
- {renderViewport && <NavigationMenuViewPort />}
124
+ {children || <NavigationMenuViewport />}
86
125
  </NavigationMenuPrimitive.Popup>
87
126
  </NavigationMenuPrimitive.Positioner>
88
127
  </NavigationMenuPrimitive.Portal>
89
128
  );
90
129
  }
91
130
 
92
- function NavigationMenuViewPort(props: NavigationMenuPrimitive.Viewport.Props) {
93
- return <NavigationMenuPrimitive.Viewport {...props} />;
131
+ function NavigationMenuViewport({ className, ...props }: NavigationMenuPrimitive.Viewport.Props) {
132
+ return (
133
+ <NavigationMenuPrimitive.Viewport
134
+ data-slot="navigation-menu-viewport"
135
+ className={cn("relative size-full overflow-hidden", className)}
136
+ {...props}
137
+ />
138
+ );
139
+ }
140
+
141
+ function NavigationMenuLink({ className, ...props }: NavigationMenuPrimitive.Link.Props) {
142
+ return (
143
+ <NavigationMenuPrimitive.Link
144
+ data-slot="navigation-menu-link"
145
+ className={cn(
146
+ "flex flex-col items-start gap-1 rounded-md p-2 text-sm font-medium transition-all outline-none hover:bg-muted focus:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 data-[active]:bg-muted/50 data-[active]:hover:bg-muted data-[active]:focus:bg-muted [&_svg:not([class*='size-'])]:size-4",
147
+ className,
148
+ )}
149
+ {...props}
150
+ />
151
+ );
152
+ }
153
+
154
+ function NavigationMenuArrow({ className, ...props }: NavigationMenuPrimitive.Arrow.Props) {
155
+ return (
156
+ <NavigationMenuPrimitive.Arrow
157
+ data-slot="navigation-menu-arrow"
158
+ className={cn(
159
+ "transition-transform duration-[350ms] ease-[cubic-bezier(0.22,1,0.36,1)] data-[side=bottom]:top-[-8px] data-[side=left]:right-[-13px] data-[side=left]:rotate-90 data-[side=right]:left-[-13px] data-[side=right]:-rotate-90 data-[side=top]:bottom-[-8px] data-[side=top]:rotate-180",
160
+ className,
161
+ )}
162
+ {...props}
163
+ >
164
+ <svg width="20" height="10" viewBox="0 0 20 10" fill="none">
165
+ <path
166
+ d="M9.66437 2.60207L4.80758 6.97318C4.07308 7.63423 3.11989 8 2.13172 8H0V10H20V8H18.5349C17.5468 8 16.5936 7.63423 15.8591 6.97318L11.0023 2.60207C10.622 2.2598 10.0447 2.25979 9.66437 2.60207Z"
167
+ className="fill-popup"
168
+ />
169
+ <path
170
+ d="M8.99542 1.85876C9.75604 1.17425 10.9106 1.17422 11.6713 1.85878L16.5281 6.22989C17.0789 6.72568 17.7938 7.00001 18.5349 7.00001L15.89 7L11.0023 2.60207C10.622 2.2598 10.0447 2.2598 9.66436 2.60207L4.77734 7L2.13171 7.00001C2.87284 7.00001 3.58774 6.72568 4.13861 6.22989L8.99542 1.85876Z"
171
+ className="fill-foreground/10"
172
+ />
173
+ </svg>
174
+ </NavigationMenuPrimitive.Arrow>
175
+ );
94
176
  }
95
177
 
96
178
  export {
@@ -101,5 +183,6 @@ export {
101
183
  NavigationMenuContent,
102
184
  NavigationMenuLink,
103
185
  NavigationMenuPopup,
104
- NavigationMenuViewPort,
186
+ NavigationMenuViewport,
187
+ NavigationMenuArrow,
105
188
  };
@@ -1,44 +1,11 @@
1
1
  "use client";
2
2
 
3
3
  import { NumberField as NumberFieldPrimitive } from "@base-ui/react/number-field";
4
- import { ChevronsLeftRightEllipsis, MinusIcon, PlusIcon } from "lucide-react";
4
+ import { MinusIcon, PlusIcon } from "lucide-react";
5
5
 
6
6
  import { cn } from "@/lib/utils";
7
7
 
8
- function NumberField({
9
- label,
10
- hasScrubber = false,
11
- className,
12
- ...props
13
- }: NumberFieldPrimitive.Root.Props & {
14
- label?: string;
15
- hasScrubber?: boolean;
16
- }) {
17
- return (
18
- <NumberFieldPrimitive.Root
19
- className={cn("flex w-fit flex-col bg-transparent text-foreground", className)}
20
- {...props}
21
- >
22
- {hasScrubber && (
23
- <NumberFieldScrubArea>
24
- {label && (
25
- <label htmlFor={props.id} className="text-sm font-medium">
26
- {label}
27
- </label>
28
- )}
29
- <NumberFieldScrubAreaCursor>
30
- <ChevronsLeftRightEllipsis className="size-4" />
31
- </NumberFieldScrubAreaCursor>
32
- </NumberFieldScrubArea>
33
- )}
34
- <NumberFieldGroup>
35
- <NumberFieldDecrement />
36
- <NumberFieldInput />
37
- <NumberFieldIncrement />
38
- </NumberFieldGroup>
39
- </NumberFieldPrimitive.Root>
40
- );
41
- }
8
+ import { Input } from "./input";
42
9
 
43
10
  function NumberFieldScrubArea({ className, ...props }: NumberFieldPrimitive.ScrubArea.Props) {
44
11
  return <NumberFieldPrimitive.ScrubArea className={cn("", className)} {...props} />;
@@ -53,36 +20,66 @@ function NumberFieldScrubAreaCursor({
53
20
  );
54
21
  }
55
22
 
23
+ function NumberField({ className, ...props }: NumberFieldPrimitive.Root.Props) {
24
+ return (
25
+ <NumberFieldPrimitive.Root
26
+ data-slot="number-field"
27
+ className={cn("group/number-field flex flex-col gap-1.5", className)}
28
+ {...props}
29
+ />
30
+ );
31
+ }
32
+
56
33
  function NumberFieldGroup({ className, ...props }: NumberFieldPrimitive.Group.Props) {
57
34
  return (
58
35
  <NumberFieldPrimitive.Group
59
- className={cn("flex overflow-hidden border bg-transparent", className)}
36
+ data-slot="number-field-group"
37
+ className={cn(
38
+ "relative inline-flex h-9 w-full items-stretch overflow-hidden rounded-lg border border-input focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50 has-[>[data-slot=number-field-decrement]]:*:data-[slot=number-field-input]:rounded-none has-[>[data-slot=number-field-decrement]]:*:data-[slot=number-field-input]:border-0 has-[>[data-slot=number-field-decrement]]:*:data-[slot=number-field-input]:outline-none has-[>[data-slot=number-field-decrement]]:*:data-[slot=number-field-input]:focus:ring-0 has-[>[data-slot=number-field-decrement]]:*:data-[slot=number-field-input]:focus:outline-none",
39
+ "has-[>[data-slot=number-field-decrement][data-align=end]]:*:data-[slot=number-field-decrement]:border-l has-[>[data-slot=number-field-decrement][data-align=start]]:*:data-[slot=number-field-decrement]:border-r has-[>[data-slot=number-field-increment][data-align=end]]:*:data-[slot=number-field-increment]:border-l ",
40
+ className,
41
+ )}
60
42
  {...props}
61
43
  />
62
44
  );
63
45
  }
64
46
 
65
- function NumberFieldDecrement({ className, ...props }: NumberFieldPrimitive.Decrement.Props) {
47
+ function NumberFieldDecrement({
48
+ className,
49
+ align,
50
+ ...props
51
+ }: NumberFieldPrimitive.Decrement.Props & { align?: "start" | "end" }) {
66
52
  return (
67
53
  <NumberFieldPrimitive.Decrement
54
+ data-slot="number-field-decrement"
55
+ data-align={align}
68
56
  className={cn(
69
- "flex size-9 items-center justify-center rounded-l-lg border-r hover:bg-accent",
57
+ "px-2.5 transition-colors hover:bg-muted active:bg-accent disabled:opacity-50",
70
58
  className,
71
59
  )}
72
60
  {...props}
73
61
  >
74
- <MinusIcon className="inline-flex size-4" />
62
+ <MinusIcon className="size-4" />
75
63
  </NumberFieldPrimitive.Decrement>
76
64
  );
77
65
  }
78
66
 
79
- function NumberFieldIncrement({ className, ...props }: NumberFieldPrimitive.Increment.Props) {
67
+ function NumberFieldIncrement({
68
+ className,
69
+ align,
70
+ ...props
71
+ }: NumberFieldPrimitive.Increment.Props & { align?: "start" | "end" }) {
80
72
  return (
81
73
  <NumberFieldPrimitive.Increment
82
- className={cn("flex size-9 items-center justify-center border-l hover:bg-accent", className)}
74
+ data-slot="number-field-increment"
75
+ data-align={align}
76
+ className={cn(
77
+ "px-2.5 transition-colors hover:bg-muted active:bg-accent disabled:opacity-50",
78
+ className,
79
+ )}
83
80
  {...props}
84
81
  >
85
- <PlusIcon className="inline-flex size-4" />
82
+ <PlusIcon className="size-4" />
86
83
  </NumberFieldPrimitive.Increment>
87
84
  );
88
85
  }
@@ -90,10 +87,9 @@ function NumberFieldIncrement({ className, ...props }: NumberFieldPrimitive.Incr
90
87
  function NumberFieldInput({ className, ...props }: NumberFieldPrimitive.Input.Props) {
91
88
  return (
92
89
  <NumberFieldPrimitive.Input
93
- className={cn(
94
- "h-9 rounded-none text-center text-sm ring-accent outline-0 data-scrubbing:border-0 data-scrubbing:outline-0",
95
- className,
96
- )}
90
+ data-slot="number-field-input"
91
+ className={cn("text-center", className)}
92
+ render={<Input />}
97
93
  {...props}
98
94
  />
99
95
  );
@@ -18,9 +18,13 @@ function PopoverPopup({
18
18
  alignOffset = 0,
19
19
  side = "bottom",
20
20
  sideOffset = 8,
21
+ anchor,
21
22
  ...props
22
23
  }: PopoverPrimitive.Popup.Props &
23
- Pick<PopoverPrimitive.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset">) {
24
+ Pick<
25
+ PopoverPrimitive.Positioner.Props,
26
+ "align" | "alignOffset" | "side" | "sideOffset" | "anchor"
27
+ >) {
24
28
  return (
25
29
  <PopoverPrimitive.Portal>
26
30
  <PopoverPrimitive.Positioner
@@ -28,11 +32,12 @@ function PopoverPopup({
28
32
  alignOffset={alignOffset}
29
33
  side={side}
30
34
  sideOffset={sideOffset}
35
+ anchor={anchor}
31
36
  className="isolate z-50"
32
37
  >
33
38
  <PopoverPrimitive.Popup
34
39
  className={cn(
35
- "text-popover-foreground z-50 flex w-72 flex-col gap-2.5 bg-popup p-4 text-sm ring-1 ring-foreground/10 outline-hidden 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",
40
+ "text-popover-foreground z-50 flex w-72 flex-col gap-2.5 rounded-lg bg-popup p-4 text-sm ring-1 ring-foreground/10 outline-hidden 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",
36
41
  className,
37
42
  )}
38
43
  {...props}
@@ -26,9 +26,10 @@ function PreviewCardPopup({
26
26
  side = "bottom",
27
27
  sideOffset = 6,
28
28
  alignOffset,
29
+ anchor,
29
30
  ...props
30
31
  }: PreviewCardPrimitive.Popup.Props &
31
- Pick<PreviewCardPrimitive.Positioner.Props, "align" | "sideOffset" | "side" | "alignOffset">) {
32
+ Pick<PreviewCardPrimitive.Positioner.Props, "align" | "sideOffset" | "side" | "alignOffset" | "anchor">) {
32
33
  return (
33
34
  <PreviewCardPrimitive.Portal>
34
35
  <PreviewCardPrimitive.Positioner
@@ -36,10 +37,11 @@ function PreviewCardPopup({
36
37
  sideOffset={sideOffset}
37
38
  side={side}
38
39
  alignOffset={alignOffset}
40
+ anchor={anchor}
39
41
  >
40
42
  <PreviewCardPrimitive.Popup
41
43
  className={cn(
42
- "text-popover-foreground overflow-hidden border bg-popup p-1 text-sm outline-hidden duration-25 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",
44
+ "text-popover-foreground overflow-hidden rounded-xl border bg-popup p-1 text-sm outline-hidden duration-25 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",
43
45
  className,
44
46
  )}
45
47
  {...props}