@ponti-studios/ui 0.1.2 → 0.3.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 (114) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -7
  3. package/THIRD_PARTY_NOTICES.md +17 -0
  4. package/dist/index.js +1028 -638
  5. package/dist/index.js.map +1 -1
  6. package/package.json +30 -32
  7. package/src/animations.css +16 -434
  8. package/src/components/data-display/accordion.tsx +10 -7
  9. package/src/components/data-display/filter-chip.tsx +2 -2
  10. package/src/components/data-display/index.ts +5 -0
  11. package/src/components/data-display/metric-card.stories.tsx +48 -0
  12. package/src/components/data-display/metric-card.tsx +20 -0
  13. package/src/components/data-display/sort-controls.stories.tsx +45 -0
  14. package/src/components/data-display/sort-controls.tsx +99 -0
  15. package/src/components/data-display/sort-row.tsx +74 -0
  16. package/src/components/data-display/table.tsx +5 -5
  17. package/src/components/feedback/alert.tsx +4 -4
  18. package/src/components/feedback/empty-state.tsx +6 -6
  19. package/src/components/feedback/file-upload-status-badge.stories.tsx +28 -0
  20. package/src/components/feedback/file-upload-status-badge.tsx +33 -0
  21. package/src/components/feedback/file-upload-status.stories.tsx +57 -0
  22. package/src/components/feedback/file-upload-status.tsx +152 -0
  23. package/src/components/feedback/index.ts +9 -0
  24. package/src/components/feedback/progress-bar.stories.tsx +39 -0
  25. package/src/components/feedback/progress-bar.tsx +21 -0
  26. package/src/components/feedback/progress.stories.tsx +8 -6
  27. package/src/components/feedback/progress.tsx +1 -1
  28. package/src/components/feedback/skeleton.stories.tsx +36 -0
  29. package/src/components/feedback/skeleton.tsx +7 -0
  30. package/src/components/feedback/spinner.tsx +2 -2
  31. package/src/components/feedback/update-guard.stories.tsx +32 -0
  32. package/src/components/feedback/update-guard.tsx +280 -0
  33. package/src/components/forms/calendar.tsx +11 -11
  34. package/src/components/forms/date-month-select.stories.tsx +26 -0
  35. package/src/components/forms/date-month-select.tsx +70 -0
  36. package/src/components/forms/date-picker.tsx +3 -3
  37. package/src/components/forms/drop-zone.stories.tsx +56 -0
  38. package/src/components/forms/drop-zone.tsx +146 -0
  39. package/src/components/forms/entity-select.stories.tsx +65 -0
  40. package/src/components/forms/entity-select.tsx +79 -0
  41. package/src/components/forms/field.tsx +3 -3
  42. package/src/components/forms/group-by-select.stories.tsx +26 -0
  43. package/src/components/forms/group-by-select.tsx +47 -0
  44. package/src/components/forms/index.ts +10 -0
  45. package/src/components/forms/input.stories.tsx +4 -4
  46. package/src/components/forms/input.tsx +1 -1
  47. package/src/components/forms/passkey-management.stories.tsx +81 -0
  48. package/src/components/forms/passkey-management.tsx +138 -0
  49. package/src/components/forms/radio-group.tsx +14 -13
  50. package/src/components/forms/select.stories.tsx +4 -4
  51. package/src/components/forms/select.tsx +45 -53
  52. package/src/components/forms/slider.stories.tsx +5 -2
  53. package/src/components/forms/slider.tsx +27 -17
  54. package/src/components/forms/stepper.tsx +4 -4
  55. package/src/components/forms/switch.stories.tsx +1 -1
  56. package/src/components/forms/switch.tsx +2 -2
  57. package/src/components/forms/textarea.stories.tsx +2 -2
  58. package/src/components/forms/textarea.tsx +1 -1
  59. package/src/components/layout/index.ts +3 -0
  60. package/src/components/layout/scroll-area.stories.tsx +4 -4
  61. package/src/components/layout/scroll-area.tsx +23 -21
  62. package/src/components/layout/section-intro.stories.tsx +29 -0
  63. package/src/components/layout/section-intro.tsx +31 -0
  64. package/src/components/layout/surface-panel.stories.tsx +32 -0
  65. package/src/components/layout/surface-panel.tsx +15 -0
  66. package/src/components/navigation/app-navigation.stories.tsx +2 -2
  67. package/src/components/navigation/app-navigation.tsx +13 -13
  68. package/src/components/navigation/index.ts +4 -0
  69. package/src/components/navigation/pagination-controls.stories.tsx +41 -0
  70. package/src/components/navigation/pagination-controls.tsx +50 -0
  71. package/src/components/navigation/route-link.stories.tsx +30 -0
  72. package/src/components/navigation/route-link.tsx +42 -0
  73. package/src/components/navigation/tabs.stories.tsx +5 -5
  74. package/src/components/navigation/tabs.tsx +7 -7
  75. package/src/components/overlays/alert-dialog.tsx +21 -46
  76. package/src/components/overlays/command.tsx +51 -82
  77. package/src/components/overlays/dialog.tsx +5 -5
  78. package/src/components/overlays/dropdown-menu.stories.tsx +0 -5
  79. package/src/components/overlays/dropdown-menu.tsx +44 -142
  80. package/src/components/overlays/popover.stories.tsx +2 -2
  81. package/src/components/overlays/popover.tsx +34 -16
  82. package/src/components/overlays/sheet.stories.tsx +2 -2
  83. package/src/components/overlays/sheet.tsx +35 -17
  84. package/src/components/primitives/avatar.tsx +3 -3
  85. package/src/components/primitives/badge.tsx +7 -11
  86. package/src/components/primitives/button.tsx +41 -13
  87. package/src/components/primitives/card.stories.tsx +2 -2
  88. package/src/components/primitives/card.tsx +2 -2
  89. package/src/components/primitives/index.ts +2 -0
  90. package/src/components/primitives/label.stories.tsx +1 -1
  91. package/src/components/primitives/label.tsx +5 -6
  92. package/src/components/primitives/status-badge.stories.tsx +52 -0
  93. package/src/components/primitives/status-badge.tsx +33 -0
  94. package/src/constants/chart-colors.ts +5 -5
  95. package/src/hooks/index.ts +1 -0
  96. package/src/hooks/use-sort.ts +20 -0
  97. package/src/index.ts +30 -11
  98. package/src/styles.css +76 -1040
  99. package/src/tokens/color-systems.stories.tsx +9 -119
  100. package/src/tokens/foundations.stories.tsx +21 -113
  101. package/src/tokens/generated/dark.colors.generated.ts +56 -0
  102. package/src/tokens/generated/dark.css +54 -0
  103. package/src/tokens/generated/foundations.css +148 -0
  104. package/src/tokens/generated/foundations.generated.ts +443 -0
  105. package/src/tokens/generated/light.colors.generated.ts +56 -0
  106. package/src/tokens/generated/light.css +54 -0
  107. package/src/tokens/index.ts +74 -16
  108. package/src/tokens/layout.stories.tsx +3 -108
  109. package/src/tokens/colors.ts +0 -156
  110. package/src/tokens/motion.ts +0 -51
  111. package/src/tokens/radii.ts +0 -10
  112. package/src/tokens/shadows.ts +0 -34
  113. package/src/tokens/spacing.ts +0 -30
  114. package/src/tokens/typography.ts +0 -42
@@ -1,130 +1,99 @@
1
- import { Command as CommandPrimitive } from "cmdk";
1
+ import { Combobox as CommandPrimitive } from "@base-ui/react/combobox";
2
2
  import { SearchIcon } from "lucide-react";
3
3
  import * as React from "react";
4
4
 
5
5
  import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./dialog";
6
6
 
7
- function Command(props: React.ComponentProps<typeof CommandPrimitive>) {
7
+ function Command({ className, children, ...props }: Omit<React.ComponentProps<typeof CommandPrimitive.Root<string>>, "className"> & { className?: string }) {
8
8
  return (
9
- <CommandPrimitive
10
- data-slot="command"
11
- className="bg-raised text-primary flex h-full w-full flex-col overflow-hidden rounded-md"
12
- {...props}
13
- />
9
+ <div data-slot="command" className={`bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md ${className ?? ""}`}>
10
+ <CommandPrimitive.Root {...props}>{children}</CommandPrimitive.Root>
11
+ </div>
14
12
  );
15
13
  }
16
14
 
17
- function CommandDialog({
18
- title = "Command Palette",
19
- description = "Search for a command to run...",
20
- children,
21
- showCloseButton = true,
22
- ...props
23
- }: Omit<React.ComponentProps<typeof Dialog>, "children"> & {
24
- title?: string;
25
- description?: string;
26
- showCloseButton?: boolean;
27
- children?: React.ReactNode;
28
- }) {
15
+ function CommandDialog({ title = "Command Palette", description = "Search for a command to run...", children, showCloseButton = true, ...props }: Omit<React.ComponentProps<typeof Dialog>, "children"> & { title?: string; description?: string; showCloseButton?: boolean; children?: React.ReactNode }) {
29
16
  return (
30
17
  <Dialog {...props}>
31
- <DialogHeader className="sr-only">
32
- <DialogTitle>{title}</DialogTitle>
33
- <DialogDescription>{description}</DialogDescription>
34
- </DialogHeader>
35
- <DialogContent className="overflow-hidden p-0" showCloseButton={showCloseButton}>
36
- <Command>{children}</Command>
37
- </DialogContent>
18
+ <DialogHeader className="sr-only"><DialogTitle>{title}</DialogTitle><DialogDescription>{description}</DialogDescription></DialogHeader>
19
+ <DialogContent className="overflow-hidden p-0" showCloseButton={showCloseButton}><Command>{children}</Command></DialogContent>
38
20
  </Dialog>
39
21
  );
40
22
  }
41
23
 
42
- function CommandInput(props: React.ComponentProps<typeof CommandPrimitive.Input>) {
24
+ type CommandInputProps = React.ComponentProps<typeof CommandPrimitive.Input> & { onValueChange?: (value: string) => void };
25
+
26
+ function CommandInput({ className, onValueChange, onChange, "aria-label": ariaLabel = "Search commands", ...props }: CommandInputProps) {
43
27
  return (
44
28
  <div data-slot="command-input-wrapper" className="flex h-9 items-center gap-2 border-b px-3">
45
29
  <SearchIcon className="size-4 shrink-0 opacity-50" />
46
30
  <CommandPrimitive.Input
47
31
  data-slot="command-input"
48
- className="placeholder:text-secondary flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50"
32
+ aria-label={ariaLabel}
33
+ className={`placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50 ${className ?? ""}`}
34
+ onChange={(event) => { onChange?.(event); onValueChange?.(event.currentTarget.value); }}
49
35
  {...props}
50
36
  />
51
37
  </div>
52
38
  );
53
39
  }
54
40
 
55
- function CommandList(props: React.ComponentProps<typeof CommandPrimitive.List>) {
41
+ function CommandList({ children, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {
42
+ if (typeof children === "function") {
43
+ return (
44
+ <CommandPrimitive.List data-slot="command-list" aria-label="Command results" tabIndex={0} className="max-h-75 scroll-py-1 overflow-x-hidden overflow-y-auto" {...props}>
45
+ {children}
46
+ </CommandPrimitive.List>
47
+ );
48
+ }
49
+
50
+ const listChildren: React.ReactNode[] = [];
51
+ const emptyChildren: React.ReactNode[] = [];
52
+
53
+ React.Children.forEach(children, (child) => {
54
+ if (React.isValidElement(child) && child.type === CommandEmpty) emptyChildren.push(child);
55
+ else listChildren.push(child);
56
+ });
57
+
56
58
  return (
57
- <CommandPrimitive.List
58
- data-slot="command-list"
59
- className="max-h-75 scroll-py-1 overflow-x-hidden overflow-y-auto"
60
- {...props}
61
- />
59
+ <div data-slot="command-list-container" className="relative">
60
+ <CommandPrimitive.List data-slot="command-list" aria-label="Command results" tabIndex={0} className="max-h-75 scroll-py-1 overflow-x-hidden overflow-y-auto" {...props}>
61
+ {listChildren}
62
+ </CommandPrimitive.List>
63
+ {emptyChildren}
64
+ </div>
62
65
  );
63
66
  }
64
67
 
65
68
  function CommandEmpty({ ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
66
- return (
67
- <CommandPrimitive.Empty
68
- data-slot="command-empty"
69
- className="py-6 text-center text-sm"
70
- {...props}
71
- />
72
- );
69
+ return <CommandPrimitive.Empty data-slot="command-empty" className="py-6 text-center text-sm" {...props} />;
73
70
  }
74
71
 
75
- function CommandGroup(props: React.ComponentProps<typeof CommandPrimitive.Group>) {
72
+ function CommandGroup({ className, heading, children, ...props }: React.ComponentProps<typeof CommandPrimitive.Group> & { heading?: React.ReactNode }) {
76
73
  return (
77
- <CommandPrimitive.Group
78
- data-slot="command-group"
79
- className="text-primary **:[[cmdk-group-heading]]:text-secondary overflow-hidden p-1 **:[[cmdk-group-heading]]:px-2 **:[[cmdk-group-heading]]:py-1.5 **:[[cmdk-group-heading]]:text-xs **:[[cmdk-group-heading]]:font-medium"
80
- {...props}
81
- />
74
+ <CommandPrimitive.Group data-slot="command-group" className={`text-foreground overflow-hidden p-1 ${className ?? ""}`} {...props}>
75
+ {heading ? <CommandPrimitive.GroupLabel className="text-muted-foreground px-2 py-1.5 text-xs font-medium">{heading}</CommandPrimitive.GroupLabel> : null}
76
+ {children}
77
+ </CommandPrimitive.Group>
82
78
  );
83
79
  }
84
80
 
85
- function CommandSeparator(props: React.ComponentProps<typeof CommandPrimitive.Separator>) {
86
- return (
87
- <CommandPrimitive.Separator
88
- data-slot="command-separator"
89
- className="bg-border -mx-1 h-px"
90
- {...props}
91
- />
92
- );
81
+ type CommandItemProps = React.ComponentProps<typeof CommandPrimitive.Item> & { onSelect?: () => void };
82
+
83
+ function CommandItem({ className, onSelect, onClick, ...props }: CommandItemProps) {
84
+ return <CommandPrimitive.Item data-slot="command-item" className={`data-highlighted:bg-accent data-highlighted:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 ${className ?? ""}`} onClick={(event) => { onClick?.(event); onSelect?.(); }} {...props} />;
93
85
  }
94
86
 
95
- function CommandItem(props: React.ComponentProps<typeof CommandPrimitive.Item>) {
96
- return (
97
- <CommandPrimitive.Item
98
- data-slot="command-item"
99
- className="data-[selected=true]:bg-accent data-[selected=true]:text-on-accent [&_svg:not([class*='text-'])]:text-secondary relative flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
100
- {...props}
101
- />
102
- );
87
+ function CommandSeparator({ className, ...props }: React.ComponentProps<"div">) {
88
+ return <div data-slot="command-separator" aria-hidden="true" className={`bg-border -mx-1 h-px ${className ?? ""}`} {...props} />;
103
89
  }
104
90
 
105
91
  function CommandShortcut(props: React.ComponentProps<"span">) {
106
- return (
107
- <span
108
- data-slot="command-shortcut"
109
- className="text-secondary ml-auto text-xs tracking-widest"
110
- {...props}
111
- />
112
- );
92
+ return <span data-slot="command-shortcut" className="text-muted-foreground ml-auto text-xs tracking-widest" {...props} />;
113
93
  }
114
94
 
115
95
  function CommandListLoading(props: React.ComponentProps<"div">) {
116
96
  return <div data-slot="command-list-loading" className="py-6 text-center text-sm" {...props} />;
117
97
  }
118
98
 
119
- export {
120
- Command,
121
- CommandDialog,
122
- CommandEmpty,
123
- CommandGroup,
124
- CommandInput,
125
- CommandItem,
126
- CommandList,
127
- CommandListLoading,
128
- CommandSeparator,
129
- CommandShortcut,
130
- };
99
+ export { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandListLoading, CommandSeparator, CommandShortcut };
@@ -63,7 +63,7 @@ function DialogOverlay({ className, ...props }: React.ComponentProps<typeof Base
63
63
  <BaseDialog.Backdrop
64
64
  data-slot="dialog-overlay"
65
65
  className={cn(
66
- "overlay-backdrop data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 z-50 transition-opacity",
66
+ "bg-black/80 data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 z-50 transition-opacity",
67
67
  className,
68
68
  )}
69
69
  {...props}
@@ -85,7 +85,7 @@ function DialogContent({
85
85
  <BaseDialog.Popup
86
86
  data-slot="dialog-content"
87
87
  className={cn(
88
- "bg-raised text-primary border-subtle data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid max-h-[90dvh] w-[calc(100%-2rem)] max-w-[30rem] translate-x-[-50%] translate-y-[-50%] gap-6 overflow-y-auto rounded-xl border p-4 duration-200 outline-none sm:p-6",
88
+ "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid max-h-[90dvh] w-[calc(100%-2rem)] max-w-[30rem] translate-x-[-50%] translate-y-[-50%] gap-6 overflow-y-auto rounded-lg border p-4 duration-200 outline-none sm:p-6",
89
89
  className,
90
90
  )}
91
91
  {...props}
@@ -94,7 +94,7 @@ function DialogContent({
94
94
  {showCloseButton && (
95
95
  <BaseDialog.Close
96
96
  data-slot="dialog-close"
97
- className="text-secondary hover:bg-inset hover:text-primary focus-visible:outline-ring absolute top-3 right-3 inline-flex size-11 items-center justify-center rounded-md border border-transparent opacity-100 transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 disabled:pointer-events-none sm:top-4 sm:right-4 [&_svg]:pointer-events-none [&_svg]:size-4.5 [&_svg]:shrink-0"
97
+ className="text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-ring absolute top-3 right-3 inline-flex size-11 items-center justify-center rounded-md border border-transparent opacity-100 transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 disabled:pointer-events-none sm:top-4 sm:right-4 [&_svg]:pointer-events-none [&_svg]:size-4.5 [&_svg]:shrink-0"
98
98
  >
99
99
  <XIcon />
100
100
  <span className="sr-only">Close</span>
@@ -129,7 +129,7 @@ function DialogTitle({ className, ...props }: React.ComponentProps<typeof BaseDi
129
129
  return (
130
130
  <BaseDialog.Title
131
131
  data-slot="dialog-title"
132
- className={cn("heading-2 text-primary", className)}
132
+ className={cn("text-lg font-semibold leading-none tracking-tight", className)}
133
133
  {...props}
134
134
  />
135
135
  );
@@ -142,7 +142,7 @@ function DialogDescription({
142
142
  return (
143
143
  <BaseDialog.Description
144
144
  data-slot="dialog-description"
145
- className={cn("body-2 text-secondary max-w-prose", className)}
145
+ className={cn("max-w-prose text-sm text-muted-foreground", className)}
146
146
  {...props}
147
147
  />
148
148
  );
@@ -31,11 +31,6 @@ const meta = {
31
31
  description: "When true, interaction outside the menu is disabled while open",
32
32
  table: { defaultValue: { summary: "true" } },
33
33
  },
34
- dir: {
35
- control: "select",
36
- options: ["ltr", "rtl"],
37
- description: "Reading direction of the dropdown menu",
38
- },
39
34
  },
40
35
  } satisfies Meta<typeof DropdownMenu>;
41
36
 
@@ -1,4 +1,4 @@
1
- import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
1
+ import { Menu as DropdownMenuPrimitive } from "@base-ui/react/menu";
2
2
  import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
3
3
  import * as React from "react";
4
4
 
@@ -12,26 +12,33 @@ function DropdownMenuPortal(props: React.ComponentProps<typeof DropdownMenuPrimi
12
12
  return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
13
13
  }
14
14
 
15
- function DropdownMenuTrigger(props: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
16
- return <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;
15
+ function DropdownMenuTrigger({ asChild, children, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger> & { asChild?: boolean }) {
16
+ return (
17
+ <DropdownMenuPrimitive.Trigger
18
+ data-slot="dropdown-menu-trigger"
19
+ {...(asChild && React.isValidElement(children) ? { render: children as React.ReactElement } : {})}
20
+ {...props}
21
+ >
22
+ {!asChild && children}
23
+ </DropdownMenuPrimitive.Trigger>
24
+ );
17
25
  }
18
26
 
19
- function DropdownMenuContent({
20
- className,
21
- sideOffset = 4,
22
- ...props
23
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
27
+ function DropdownMenuContent({ className, sideOffset = 4, children, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Popup> & { sideOffset?: number }) {
24
28
  return (
25
29
  <DropdownMenuPrimitive.Portal>
26
- <DropdownMenuPrimitive.Content
27
- data-slot="dropdown-menu-content"
28
- sideOffset={sideOffset}
29
- className={cn(
30
- "bg-raised text-primary 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 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 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1",
31
- className,
32
- )}
33
- {...props}
34
- />
30
+ <DropdownMenuPrimitive.Positioner sideOffset={sideOffset}>
31
+ <DropdownMenuPrimitive.Popup
32
+ data-slot="dropdown-menu-content"
33
+ className={cn(
34
+ "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 z-50 max-h-(--available-height) min-w-[8rem] origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 outline-none",
35
+ className,
36
+ )}
37
+ {...props}
38
+ >
39
+ {children}
40
+ </DropdownMenuPrimitive.Popup>
41
+ </DropdownMenuPrimitive.Positioner>
35
42
  </DropdownMenuPrimitive.Portal>
36
43
  );
37
44
  }
@@ -40,159 +47,54 @@ function DropdownMenuGroup(props: React.ComponentProps<typeof DropdownMenuPrimit
40
47
  return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
41
48
  }
42
49
 
43
- function DropdownMenuLabel({
44
- className,
45
- inset,
46
- ...props
47
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & { inset?: boolean }) {
48
- return (
49
- <DropdownMenuPrimitive.Label
50
- data-slot="dropdown-menu-label"
51
- data-inset={inset}
52
- className={cn("px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className)}
53
- {...props}
54
- />
55
- );
50
+ function DropdownMenuLabel({ className, inset, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.GroupLabel> & { inset?: boolean }) {
51
+ return <DropdownMenuPrimitive.GroupLabel data-slot="dropdown-menu-label" data-inset={inset} className={cn("px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className)} {...props} />;
56
52
  }
57
53
 
58
- function DropdownMenuItem({
59
- className,
60
- inset,
61
- variant = "default",
62
- ...props
63
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
64
- inset?: boolean;
65
- variant?: "default" | "destructive";
66
- }) {
67
- return (
68
- <DropdownMenuPrimitive.Item
69
- data-slot="dropdown-menu-item"
70
- data-inset={inset}
71
- data-variant={variant}
72
- className={cn(
73
- "focus:bg-accent focus:text-on-accent data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
74
- className,
75
- )}
76
- {...props}
77
- />
78
- );
54
+ function DropdownMenuItem({ className, inset, variant = "default", ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & { inset?: boolean; variant?: "default" | "destructive" }) {
55
+ return <DropdownMenuPrimitive.Item data-slot="dropdown-menu-item" data-inset={inset} data-variant={variant} className={cn("data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:data-highlighted:bg-destructive/10 relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", className)} {...props} />;
79
56
  }
80
57
 
81
- function DropdownMenuCheckboxItem({
82
- className,
83
- children,
84
- checked,
85
- ...props
86
- }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
58
+ function DropdownMenuCheckboxItem({ className, children, checked, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
87
59
  return (
88
- <DropdownMenuPrimitive.CheckboxItem
89
- data-slot="dropdown-menu-checkbox-item"
90
- className={cn(
91
- "focus:bg-accent focus:text-on-accent relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
92
- className,
93
- )}
94
- checked={checked}
95
- {...props}
96
- >
97
- <span className="absolute left-2 flex size-3.5 items-center justify-center">
98
- <DropdownMenuPrimitive.ItemIndicator>
99
- <CheckIcon className="size-4" />
100
- </DropdownMenuPrimitive.ItemIndicator>
101
- </span>
60
+ <DropdownMenuPrimitive.CheckboxItem data-slot="dropdown-menu-checkbox-item" className={cn("data-highlighted:bg-accent data-highlighted:text-accent-foreground relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-disabled:pointer-events-none data-disabled:opacity-50", className)} checked={checked} {...props}>
61
+ <span className="absolute left-2 flex size-3.5 items-center justify-center"><DropdownMenuPrimitive.CheckboxItemIndicator><CheckIcon className="size-4" /></DropdownMenuPrimitive.CheckboxItemIndicator></span>
102
62
  {children}
103
63
  </DropdownMenuPrimitive.CheckboxItem>
104
64
  );
105
65
  }
106
66
 
107
- function DropdownMenuRadioGroup(
108
- props: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>,
109
- ) {
67
+ function DropdownMenuRadioGroup(props: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
110
68
  return <DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />;
111
69
  }
112
70
 
113
- function DropdownMenuRadioItem({
114
- className,
115
- children,
116
- ...props
117
- }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
71
+ function DropdownMenuRadioItem({ className, children, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
118
72
  return (
119
- <DropdownMenuPrimitive.RadioItem
120
- data-slot="dropdown-menu-radio-item"
121
- className={cn(
122
- "focus:bg-accent focus:text-on-accent relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
123
- className,
124
- )}
125
- {...props}
126
- >
127
- <span className="absolute left-2 flex size-3.5 items-center justify-center">
128
- <DropdownMenuPrimitive.ItemIndicator>
129
- <CircleIcon className="size-2 fill-current" />
130
- </DropdownMenuPrimitive.ItemIndicator>
131
- </span>
73
+ <DropdownMenuPrimitive.RadioItem data-slot="dropdown-menu-radio-item" className={cn("data-highlighted:bg-accent data-highlighted:text-accent-foreground relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-disabled:pointer-events-none data-disabled:opacity-50", className)} {...props}>
74
+ <span className="absolute left-2 flex size-3.5 items-center justify-center"><DropdownMenuPrimitive.RadioItemIndicator><CircleIcon className="size-2 fill-current" /></DropdownMenuPrimitive.RadioItemIndicator></span>
132
75
  {children}
133
76
  </DropdownMenuPrimitive.RadioItem>
134
77
  );
135
78
  }
136
79
 
137
- function DropdownMenuSeparator({
138
- className,
139
- ...props
140
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
141
- return (
142
- <DropdownMenuPrimitive.Separator
143
- data-slot="dropdown-menu-separator"
144
- className={cn("bg-border -mx-1 my-1 h-px", className)}
145
- {...props}
146
- />
147
- );
80
+ function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
81
+ return <DropdownMenuPrimitive.Separator data-slot="dropdown-menu-separator" className={cn("bg-border -mx-1 my-1 h-px", className)} {...props} />;
148
82
  }
149
83
 
150
84
  function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">) {
151
- return (
152
- <span className={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...props} />
153
- );
85
+ return <span className={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...props} />;
154
86
  }
155
87
 
156
- function DropdownMenuSub(props: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
157
- return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
88
+ function DropdownMenuSub(props: React.ComponentProps<typeof DropdownMenuPrimitive.SubmenuRoot>) {
89
+ return <DropdownMenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />;
158
90
  }
159
91
 
160
- function DropdownMenuSubTrigger({
161
- className,
162
- inset,
163
- children,
164
- ...props
165
- }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & { inset?: boolean }) {
166
- return (
167
- <DropdownMenuPrimitive.SubTrigger
168
- data-slot="dropdown-menu-sub-trigger"
169
- data-inset={inset}
170
- className={cn(
171
- "focus:bg-accent focus:text-on-accent data-[state=open]:bg-accent data-[state=open]:text-on-accent flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[inset]:pl-8",
172
- className,
173
- )}
174
- {...props}
175
- >
176
- {children}
177
- <ChevronRightIcon className="ml-auto size-4" />
178
- </DropdownMenuPrimitive.SubTrigger>
179
- );
92
+ function DropdownMenuSubTrigger({ className, inset, children, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.SubmenuTrigger> & { inset?: boolean }) {
93
+ return <DropdownMenuPrimitive.SubmenuTrigger data-slot="dropdown-menu-sub-trigger" data-inset={inset} className={cn("data-highlighted:bg-accent data-highlighted:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[inset]:pl-8", className)} {...props}>{children}<ChevronRightIcon className="ml-auto size-4" /></DropdownMenuPrimitive.SubmenuTrigger>;
180
94
  }
181
95
 
182
- function DropdownMenuSubContent({
183
- className,
184
- ...props
185
- }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
186
- return (
187
- <DropdownMenuPrimitive.SubContent
188
- data-slot="dropdown-menu-sub-content"
189
- className={cn(
190
- "bg-raised text-primary 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 z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-lg",
191
- className,
192
- )}
193
- {...props}
194
- />
195
- );
96
+ function DropdownMenuSubContent({ className, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Popup>) {
97
+ return <DropdownMenuPrimitive.Popup data-slot="dropdown-menu-sub-content" className={cn("bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-lg outline-none", className)} {...props} />;
196
98
  }
197
99
 
198
100
  export {
@@ -20,8 +20,8 @@ export const Default: Story = {
20
20
  </PopoverTrigger>
21
21
  <PopoverContent>
22
22
  <div className="grid gap-1.5">
23
- <h4 className="text-primary leading-none font-medium">Dimensions</h4>
24
- <p className="text-secondary text-sm">Set the dimensions for the layer.</p>
23
+ <h4 className="text-foreground leading-none font-medium">Dimensions</h4>
24
+ <p className="text-muted-foreground text-sm">Set the dimensions for the layer.</p>
25
25
  </div>
26
26
  <div className="grid gap-4 pt-4">
27
27
  <div className="grid grid-cols-3 items-center gap-4">
@@ -1,4 +1,4 @@
1
- import * as PopoverPrimitive from "@radix-ui/react-popover";
1
+ import { Popover as PopoverPrimitive } from "@base-ui/react/popover";
2
2
  import * as React from "react";
3
3
 
4
4
  import { cn } from "../../lib/utils";
@@ -7,34 +7,52 @@ function Popover({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root
7
7
  return <PopoverPrimitive.Root data-slot="popover" {...props} />;
8
8
  }
9
9
 
10
- function PopoverTrigger({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
11
- return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
10
+ function PopoverTrigger({ asChild, children, ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger> & { asChild?: boolean }) {
11
+ return (
12
+ <PopoverPrimitive.Trigger
13
+ data-slot="popover-trigger"
14
+ {...(asChild && React.isValidElement(children) ? { render: children as React.ReactElement } : {})}
15
+ {...props}
16
+ >
17
+ {!asChild && children}
18
+ </PopoverPrimitive.Trigger>
19
+ );
12
20
  }
13
21
 
14
22
  function PopoverContent({
15
23
  className,
16
24
  align = "center",
17
25
  sideOffset = 4,
26
+ children,
27
+ side = "bottom",
28
+ avoidCollisions = true,
18
29
  ...props
19
- }: React.ComponentProps<typeof PopoverPrimitive.Content>) {
30
+ }: React.ComponentProps<typeof PopoverPrimitive.Popup> & {
31
+ align?: "start" | "center" | "end";
32
+ sideOffset?: number;
33
+ side?: "top" | "right" | "bottom" | "left";
34
+ avoidCollisions?: boolean;
35
+ }) {
20
36
  return (
21
37
  <PopoverPrimitive.Portal>
22
- <PopoverPrimitive.Content
23
- data-slot="popover-content"
24
- align={align}
25
- sideOffset={sideOffset}
26
- className={cn(
27
- "bg-raised text-primary 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 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 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 outline-hidden",
28
- className,
29
- )}
30
- {...props}
31
- />
38
+ <PopoverPrimitive.Positioner align={align} side={side} sideOffset={sideOffset} collisionAvoidance={avoidCollisions ? undefined : { side: "none", align: "none" }}>
39
+ <PopoverPrimitive.Popup
40
+ data-slot="popover-content"
41
+ className={cn(
42
+ "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 z-50 w-72 origin-(--transform-origin) rounded-md border p-4 outline-hidden",
43
+ className,
44
+ )}
45
+ {...props}
46
+ >
47
+ {children}
48
+ </PopoverPrimitive.Popup>
49
+ </PopoverPrimitive.Positioner>
32
50
  </PopoverPrimitive.Portal>
33
51
  );
34
52
  }
35
53
 
36
- function PopoverAnchor({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
37
- return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />;
54
+ function PopoverAnchor({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
55
+ return <PopoverPrimitive.Trigger data-slot="popover-anchor" {...props} />;
38
56
  }
39
57
 
40
58
  export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };
@@ -38,14 +38,14 @@ const SheetDemo = () => (
38
38
  <div className="flex flex-col gap-2">
39
39
  <span className="text-sm font-medium">Name</span>
40
40
  <input
41
- className="border-default placeholder:text-secondary focus-visible:ring-focus flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:ring-1 focus-visible:outline-none"
41
+ className="border-input placeholder:text-muted-foreground focus-visible:ring-ring flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:ring-1 focus-visible:outline-none"
42
42
  placeholder="Jane Doe"
43
43
  />
44
44
  </div>
45
45
  <div className="flex flex-col gap-2">
46
46
  <span className="text-sm font-medium">Email</span>
47
47
  <input
48
- className="border-default placeholder:text-secondary focus-visible:ring-focus flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:ring-1 focus-visible:outline-none"
48
+ className="border-input placeholder:text-muted-foreground focus-visible:ring-ring flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:ring-1 focus-visible:outline-none"
49
49
  placeholder="jane@example.com"
50
50
  type="email"
51
51
  />