@vvedantb/ui 0.17.1 → 0.18.1

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 (130) hide show
  1. package/README.md +251 -3
  2. package/package.json +13 -5
  3. package/shell/src/SidebarResize.tsx +1 -4
  4. package/src/ai-elements/__snapshots__/queue.test.tsx.snap +11 -0
  5. package/src/ai-elements/activity-question-utils.ts +26 -0
  6. package/src/ai-elements/activity-question.tsx +159 -0
  7. package/src/ai-elements/activity-shared.tsx +459 -0
  8. package/src/ai-elements/activity-step-detail.tsx +115 -0
  9. package/src/ai-elements/activity-step-label.ts +981 -0
  10. package/src/ai-elements/activity-tasks-utils.ts +199 -0
  11. package/src/ai-elements/activity-tasks.tsx +770 -0
  12. package/src/ai-elements/artifact.tsx +162 -52
  13. package/src/ai-elements/attachments.tsx +62 -32
  14. package/src/ai-elements/code-block.tsx +76 -22
  15. package/src/ai-elements/context.tsx +370 -0
  16. package/src/ai-elements/conversation.tsx +186 -62
  17. package/src/ai-elements/file-chip.tsx +53 -0
  18. package/src/ai-elements/message.tsx +161 -50
  19. package/src/ai-elements/model-picker-content.tsx +505 -0
  20. package/src/ai-elements/model-picker-types.ts +56 -0
  21. package/src/ai-elements/model-picker.tsx +301 -0
  22. package/src/ai-elements/model-selector.tsx +102 -21
  23. package/src/ai-elements/plan.tsx +130 -0
  24. package/src/ai-elements/prompt-input-settings.tsx +144 -0
  25. package/src/ai-elements/prompt-input-speech.tsx +189 -0
  26. package/src/ai-elements/prompt-input-traits.tsx +281 -0
  27. package/src/ai-elements/prompt-input.tsx +320 -134
  28. package/src/ai-elements/provider-icon.tsx +133 -0
  29. package/src/ai-elements/queue.tsx +293 -9
  30. package/src/ai-elements/shimmer.tsx +72 -1
  31. package/src/ai-elements/simple-model-ladder.tsx +108 -0
  32. package/src/ai-elements/speech-input.tsx +5 -3
  33. package/src/ai-elements/test-results.tsx +510 -0
  34. package/src/ai-elements/trace.tsx +243 -0
  35. package/src/ai-elements/use-streaming-disclosure.ts +50 -0
  36. package/src/ai-elements/web-preview.tsx +349 -0
  37. package/src/composer/input-area/ComposerSlashMenu.tsx +1 -1
  38. package/src/index.ts +66 -12
  39. package/src/kibo/avatar-stack.tsx +40 -0
  40. package/src/kibo/contribution-graph.tsx +539 -0
  41. package/src/kibo/cursor.tsx +72 -0
  42. package/src/kibo/gantt-features.tsx +439 -0
  43. package/src/kibo/gantt-header.tsx +181 -0
  44. package/src/kibo/gantt-provider.tsx +657 -0
  45. package/src/kibo/gantt-sidebar.tsx +157 -0
  46. package/src/kibo/gantt-timeline.tsx +154 -0
  47. package/src/kibo/index.ts +151 -0
  48. package/src/kibo/kanban.tsx +338 -0
  49. package/src/kibo/kanbanDropTarget.ts +25 -0
  50. package/src/kibo/list.tsx +162 -0
  51. package/src/kibo/theme-switcher.tsx +109 -0
  52. package/src/kibo/video-player.tsx +237 -0
  53. package/src/markdown/Markdown.tsx +112 -0
  54. package/src/markdown/MarkdownBlockquote.tsx +45 -0
  55. package/src/markdown/MarkdownCodeBlock.tsx +197 -0
  56. package/src/markdown/MarkdownInlineCode.tsx +40 -0
  57. package/src/markdown/MarkdownLink.tsx +92 -0
  58. package/src/markdown/codeFence.ts +57 -0
  59. package/src/markdown/codeHighlight.ts +138 -0
  60. package/src/markdown/incrementalBlocks.ts +182 -0
  61. package/src/markdown/index.ts +3 -0
  62. package/src/markdown/markdownClipboard.ts +178 -0
  63. package/src/markdown/mermaid.ts +155 -0
  64. package/src/markdown/rehypeGithubAlerts.ts +80 -0
  65. package/src/motion/presets.ts +24 -0
  66. package/src/ui/__snapshots__/alt-wrappers.test.tsx.snap +19 -0
  67. package/src/ui/__snapshots__/sonner-dom.test.tsx.snap +5 -0
  68. package/src/ui/_menu-parts.tsx +29 -0
  69. package/src/ui/accordion.alt.tsx +46 -53
  70. package/src/ui/accordion.tsx +59 -10
  71. package/src/ui/avatar.tsx +56 -0
  72. package/src/ui/badge.tsx +45 -21
  73. package/src/ui/border-beam.tsx +97 -0
  74. package/src/ui/button-group.tsx +50 -21
  75. package/src/ui/button.tsx +67 -45
  76. package/src/ui/calendar.tsx +48 -5
  77. package/src/ui/card.tsx +77 -8
  78. package/src/ui/carousel.tsx +60 -0
  79. package/src/ui/centered-spinner.tsx +30 -0
  80. package/src/ui/checkbox.tsx +34 -16
  81. package/src/ui/clear-input.alt.tsx +33 -75
  82. package/src/ui/clear-input.tsx +140 -15
  83. package/src/ui/collapsible.tsx +40 -2
  84. package/src/ui/command.tsx +143 -70
  85. package/src/ui/context-menu.tsx +159 -93
  86. package/src/ui/crossfade-icon.tsx +123 -0
  87. package/src/ui/dialog.tsx +154 -70
  88. package/src/ui/dropdown-menu.tsx +177 -117
  89. package/src/ui/hover-card.tsx +59 -17
  90. package/src/ui/input-group.alt.tsx +83 -83
  91. package/src/ui/input-group.tsx +129 -58
  92. package/src/ui/input.tsx +13 -4
  93. package/src/ui/label.tsx +11 -8
  94. package/src/ui/list-row.tsx +232 -0
  95. package/src/ui/loading-state.tsx +215 -0
  96. package/src/ui/popover.tsx +56 -23
  97. package/src/ui/progress.tsx +28 -16
  98. package/src/ui/refresh-spin-icon.tsx +41 -0
  99. package/src/ui/search-input.tsx +66 -15
  100. package/src/ui/select.tsx +171 -100
  101. package/src/ui/separator.tsx +23 -14
  102. package/src/ui/slider.tsx +21 -7
  103. package/src/ui/sonner.tsx +68 -30
  104. package/src/ui/spinner.tsx +229 -11
  105. package/src/ui/status-dot.tsx +84 -0
  106. package/src/ui/surface.tsx +49 -0
  107. package/src/ui/switch.tsx +29 -15
  108. package/src/ui/table.tsx +38 -16
  109. package/src/ui/tabs.tsx +123 -35
  110. package/src/ui/tabsSliding.ts +33 -1
  111. package/src/ui/textarea.tsx +13 -4
  112. package/src/ui/tooltip.tsx +50 -14
  113. package/src/ui/ui-config.tsx +149 -0
  114. package/src/utils/cat-color.ts +123 -0
  115. package/src/utils/dayjs.ts +6 -0
  116. package/src/utils/gesture.ts +59 -0
  117. package/src/utils/runtimeVisibility.ts +167 -0
  118. package/src/utils/sharedClock.ts +96 -0
  119. package/src/utils/surface-radius.ts +16 -0
  120. package/src/utils/useDragSensors.ts +58 -0
  121. package/tokens/examples/tokens-moved/Fixture.tsx +283 -0
  122. package/tokens/examples/tokens-moved/README.md +20 -0
  123. package/tokens/examples/tokens-moved/index.html +12 -0
  124. package/tokens/examples/tokens-moved/main.tsx +6 -0
  125. package/tokens/examples/tokens-moved/package.json +14 -0
  126. package/tokens/examples/tokens-moved/styles.css +12 -0
  127. package/tokens/examples/tokens-moved/tsconfig.json +14 -0
  128. package/tokens/examples/tokens-moved/vite.config.ts +11 -0
  129. package/tokens/src/css/border-beam.css +116 -0
  130. package/tokens/src/css/markdown.css +318 -0
@@ -5,7 +5,6 @@ import {
5
5
  IconChevronRight,
6
6
  IconCircleFilled,
7
7
  } from "@tabler/icons-react";
8
- import { cn } from "../utils/cn";
9
8
  import {
10
9
  menuCheckboxRadioItemClass,
11
10
  menuContentClass,
@@ -17,14 +16,35 @@ import {
17
16
  } from "./_menu-classes";
18
17
  import {
19
18
  fitViewportClass,
19
+ type MenuConfig,
20
20
  MenuItemRow,
21
21
  SubTriggerRow,
22
22
  suppressAltKeyDown,
23
23
  } from "./_menu-parts";
24
24
  import { restoreModalScrollLock } from "./overlay-a11y";
25
+ import { useCn, useUiConfig } from "./ui-config";
26
+
27
+ /** App defaults for `DropdownMenu*` (see `UiConfigProvider`). */
28
+ export interface DropdownMenuConfig extends MenuConfig {
29
+ /** Default for `DropdownMenuContent` `sideOffset`. */
30
+ sideOffset?: number;
31
+ }
25
32
 
26
33
  const DropdownMenu = DropdownMenuPrimitive.Root;
27
- const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
34
+
35
+ const DropdownMenuTrigger = React.forwardRef<
36
+ React.ComponentRef<typeof DropdownMenuPrimitive.Trigger>,
37
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Trigger>
38
+ >((props, ref) => {
39
+ const trigger = useUiConfig("DropdownMenu")?.classes?.trigger;
40
+ const merge = useCn();
41
+ // Without config, pass props untouched: Radix's Slot turns an explicit
42
+ // `className={undefined}` into `class=""`.
43
+ if (trigger === undefined) return <DropdownMenuPrimitive.Trigger ref={ref} {...props} />;
44
+ const { className, ...rest } = props;
45
+ return <DropdownMenuPrimitive.Trigger ref={ref} className={merge(trigger, className)} {...rest} />;
46
+ });
47
+ DropdownMenuTrigger.displayName = DropdownMenuPrimitive.Trigger.displayName;
28
48
  const DropdownMenuGroup = DropdownMenuPrimitive.Group;
29
49
  const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
30
50
  const DropdownMenuSub = DropdownMenuPrimitive.Sub;
@@ -37,33 +57,41 @@ const DropdownMenuSubTrigger = React.forwardRef<
37
57
  /** Leading icon or mark. The label then fills the row. */
38
58
  startContent?: React.ReactNode;
39
59
  }
40
- >(({ className, inset, startContent, children, ...props }, ref) => (
41
- <DropdownMenuPrimitive.SubTrigger
42
- ref={ref}
43
- className={cn(menuSubTriggerClass, inset && "pl-8", className)}
44
- {...props}
45
- >
46
- {startContent === undefined ? children : (
47
- <SubTriggerRow startContent={startContent}>{children}</SubTriggerRow>
48
- )}
49
- <IconChevronRight className="ml-auto" />
50
- </DropdownMenuPrimitive.SubTrigger>
51
- ));
60
+ >(({ className, inset, startContent, children, ...props }, ref) => {
61
+ const classes = useUiConfig("DropdownMenu")?.classes;
62
+ const merge = useCn();
63
+ return (
64
+ <DropdownMenuPrimitive.SubTrigger
65
+ ref={ref}
66
+ className={merge(classes?.subTrigger ?? menuSubTriggerClass, inset && "pl-8", className)}
67
+ {...props}
68
+ >
69
+ {startContent === undefined ? children : (
70
+ <SubTriggerRow startContent={startContent}>{children}</SubTriggerRow>
71
+ )}
72
+ <IconChevronRight className="ml-auto" />
73
+ </DropdownMenuPrimitive.SubTrigger>
74
+ );
75
+ });
52
76
  DropdownMenuSubTrigger.displayName =
53
77
  DropdownMenuPrimitive.SubTrigger.displayName;
54
78
 
55
79
  const DropdownMenuSubContent = React.forwardRef<
56
80
  React.ComponentRef<typeof DropdownMenuPrimitive.SubContent>,
57
81
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
58
- >(({ className, ...props }, ref) => (
59
- <DropdownMenuPrimitive.Portal>
60
- <DropdownMenuPrimitive.SubContent
61
- ref={ref}
62
- className={cn(menuContentClass, className)}
63
- {...props}
64
- />
65
- </DropdownMenuPrimitive.Portal>
66
- ));
82
+ >(({ className, ...props }, ref) => {
83
+ const classes = useUiConfig("DropdownMenu")?.classes;
84
+ const merge = useCn();
85
+ return (
86
+ <DropdownMenuPrimitive.Portal>
87
+ <DropdownMenuPrimitive.SubContent
88
+ ref={ref}
89
+ className={merge(classes?.subContent ?? menuContentClass, className)}
90
+ {...props}
91
+ />
92
+ </DropdownMenuPrimitive.Portal>
93
+ );
94
+ });
67
95
  DropdownMenuSubContent.displayName =
68
96
  DropdownMenuPrimitive.SubContent.displayName;
69
97
 
@@ -81,7 +109,8 @@ const DropdownMenuContent = React.forwardRef<
81
109
  (
82
110
  {
83
111
  className,
84
- sideOffset = 4,
112
+ sideOffset,
113
+ collisionPadding,
85
114
  fitViewport,
86
115
  suppressAltKey,
87
116
  restoreScrollLockOnClose,
@@ -90,26 +119,35 @@ const DropdownMenuContent = React.forwardRef<
90
119
  ...props
91
120
  },
92
121
  ref,
93
- ) => (
94
- <DropdownMenuPrimitive.Portal>
95
- <DropdownMenuPrimitive.Content
96
- ref={ref}
97
- sideOffset={sideOffset}
98
- className={cn(menuContentClass, fitViewport && fitViewportClass, className)}
99
- onKeyDown={suppressAltKey ? suppressAltKeyDown(onKeyDown) : onKeyDown}
100
- onCloseAutoFocus={
101
- restoreScrollLockOnClose
102
- ? (event) => {
103
- restoreModalScrollLock();
104
- window.setTimeout(restoreModalScrollLock, 0);
105
- onCloseAutoFocus?.(event);
106
- }
107
- : onCloseAutoFocus
108
- }
109
- {...props}
110
- />
111
- </DropdownMenuPrimitive.Portal>
112
- ),
122
+ ) => {
123
+ const config = useUiConfig("DropdownMenu");
124
+ const merge = useCn();
125
+ return (
126
+ <DropdownMenuPrimitive.Portal>
127
+ <DropdownMenuPrimitive.Content
128
+ ref={ref}
129
+ sideOffset={sideOffset ?? config?.sideOffset ?? 4}
130
+ collisionPadding={collisionPadding ?? config?.collisionPadding}
131
+ className={merge(
132
+ config?.classes?.content ?? menuContentClass,
133
+ fitViewport && fitViewportClass,
134
+ className,
135
+ )}
136
+ onKeyDown={suppressAltKey ? suppressAltKeyDown(onKeyDown) : onKeyDown}
137
+ onCloseAutoFocus={
138
+ restoreScrollLockOnClose
139
+ ? (event) => {
140
+ restoreModalScrollLock();
141
+ window.setTimeout(restoreModalScrollLock, 0);
142
+ onCloseAutoFocus?.(event);
143
+ }
144
+ : onCloseAutoFocus
145
+ }
146
+ {...props}
147
+ />
148
+ </DropdownMenuPrimitive.Portal>
149
+ );
150
+ },
113
151
  );
114
152
  DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
115
153
 
@@ -128,77 +166,89 @@ const DropdownMenuItem = React.forwardRef<
128
166
  (
129
167
  { className, inset, startContent, description, endContent, children, asChild, ...props },
130
168
  ref,
131
- ) => (
132
- <DropdownMenuPrimitive.Item
133
- ref={ref}
134
- asChild={asChild}
135
- className={cn(menuItemClass, inset && "pl-8", className)}
136
- {...props}
137
- >
138
- {asChild || (startContent === undefined && description === undefined && endContent === undefined) ? (
139
- children
140
- ) : endContent !== undefined ? (
141
- <MenuItemRow startContent={startContent} description={description} endContent={endContent}>
142
- {children}
143
- </MenuItemRow>
144
- ) : (
145
- <>
146
- {startContent}
147
- {description === undefined ? (
148
- children
149
- ) : (
150
- <span className="flex min-w-0 flex-col">
151
- <span>{children}</span>
152
- <span className="text-xs text-muted-foreground">
153
- {description}
169
+ ) => {
170
+ const item = useUiConfig("DropdownMenu")?.classes?.item;
171
+ const merge = useCn();
172
+ return (
173
+ <DropdownMenuPrimitive.Item
174
+ ref={ref}
175
+ asChild={asChild}
176
+ className={merge(item ?? menuItemClass, inset && "pl-8", className)}
177
+ {...props}
178
+ >
179
+ {asChild || (startContent === undefined && description === undefined && endContent === undefined) ? (
180
+ children
181
+ ) : endContent !== undefined ? (
182
+ <MenuItemRow startContent={startContent} description={description} endContent={endContent}>
183
+ {children}
184
+ </MenuItemRow>
185
+ ) : (
186
+ <>
187
+ {startContent}
188
+ {description === undefined ? (
189
+ children
190
+ ) : (
191
+ <span className="flex min-w-0 flex-col">
192
+ <span>{children}</span>
193
+ <span className="text-xs text-muted-foreground">
194
+ {description}
195
+ </span>
154
196
  </span>
155
- </span>
156
- )}
157
- </>
158
- )}
159
- </DropdownMenuPrimitive.Item>
160
- ),
197
+ )}
198
+ </>
199
+ )}
200
+ </DropdownMenuPrimitive.Item>
201
+ );
202
+ },
161
203
  );
162
204
  DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
163
205
 
164
206
  const DropdownMenuCheckboxItem = React.forwardRef<
165
207
  React.ComponentRef<typeof DropdownMenuPrimitive.CheckboxItem>,
166
208
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
167
- >(({ className, children, checked, ...props }, ref) => (
168
- <DropdownMenuPrimitive.CheckboxItem
169
- ref={ref}
170
- className={cn(menuCheckboxRadioItemClass, className)}
171
- checked={checked}
172
- {...props}
173
- >
174
- <span className="absolute left-2.5 flex h-3.5 w-3.5 items-center justify-center">
175
- <DropdownMenuPrimitive.ItemIndicator>
176
- <IconCheck className="h-4 w-4" />
177
- </DropdownMenuPrimitive.ItemIndicator>
178
- </span>
179
- {children}
180
- </DropdownMenuPrimitive.CheckboxItem>
181
- ));
209
+ >(({ className, children, checked, ...props }, ref) => {
210
+ const config = useUiConfig("DropdownMenu");
211
+ const merge = useCn();
212
+ return (
213
+ <DropdownMenuPrimitive.CheckboxItem
214
+ ref={ref}
215
+ className={merge(config?.classes?.checkboxItem ?? menuCheckboxRadioItemClass, className)}
216
+ checked={checked}
217
+ {...props}
218
+ >
219
+ <span className="absolute left-2.5 flex h-3.5 w-3.5 items-center justify-center">
220
+ <DropdownMenuPrimitive.ItemIndicator>
221
+ {config?.icons?.check ?? <IconCheck className="h-4 w-4" />}
222
+ </DropdownMenuPrimitive.ItemIndicator>
223
+ </span>
224
+ {children}
225
+ </DropdownMenuPrimitive.CheckboxItem>
226
+ );
227
+ });
182
228
  DropdownMenuCheckboxItem.displayName =
183
229
  DropdownMenuPrimitive.CheckboxItem.displayName;
184
230
 
185
231
  const DropdownMenuRadioItem = React.forwardRef<
186
232
  React.ComponentRef<typeof DropdownMenuPrimitive.RadioItem>,
187
233
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
188
- >(({ className, children, ...props }, ref) => (
189
- <DropdownMenuPrimitive.RadioItem
190
- ref={ref}
191
- className={cn(menuCheckboxRadioItemClass, className)}
192
- {...props}
193
- >
194
- <span className="absolute left-2.5 flex h-3.5 w-3.5 items-center justify-center">
195
- <DropdownMenuPrimitive.ItemIndicator>
196
- <IconCircleFilled className="h-2 w-2 fill-current" />
197
- </DropdownMenuPrimitive.ItemIndicator>
198
- </span>
199
- {children}
200
- </DropdownMenuPrimitive.RadioItem>
201
- ));
234
+ >(({ className, children, ...props }, ref) => {
235
+ const config = useUiConfig("DropdownMenu");
236
+ const merge = useCn();
237
+ return (
238
+ <DropdownMenuPrimitive.RadioItem
239
+ ref={ref}
240
+ className={merge(config?.classes?.radioItem ?? menuCheckboxRadioItemClass, className)}
241
+ {...props}
242
+ >
243
+ <span className="absolute left-2.5 flex h-3.5 w-3.5 items-center justify-center">
244
+ <DropdownMenuPrimitive.ItemIndicator>
245
+ {config?.icons?.radio ?? <IconCircleFilled className="h-2 w-2 fill-current" />}
246
+ </DropdownMenuPrimitive.ItemIndicator>
247
+ </span>
248
+ {children}
249
+ </DropdownMenuPrimitive.RadioItem>
250
+ );
251
+ });
202
252
  DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
203
253
 
204
254
  const DropdownMenuLabel = React.forwardRef<
@@ -206,32 +256,42 @@ const DropdownMenuLabel = React.forwardRef<
206
256
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
207
257
  inset?: boolean;
208
258
  }
209
- >(({ className, inset, ...props }, ref) => (
210
- <DropdownMenuPrimitive.Label
211
- ref={ref}
212
- className={cn(menuLabelClass, inset && "pl-8", className)}
213
- {...props}
214
- />
215
- ));
259
+ >(({ className, inset, ...props }, ref) => {
260
+ const label = useUiConfig("DropdownMenu")?.classes?.label;
261
+ const merge = useCn();
262
+ return (
263
+ <DropdownMenuPrimitive.Label
264
+ ref={ref}
265
+ className={merge(label ?? menuLabelClass, inset && "pl-8", className)}
266
+ {...props}
267
+ />
268
+ );
269
+ });
216
270
  DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
217
271
 
218
272
  const DropdownMenuSeparator = React.forwardRef<
219
273
  React.ComponentRef<typeof DropdownMenuPrimitive.Separator>,
220
274
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
221
- >(({ className, ...props }, ref) => (
222
- <DropdownMenuPrimitive.Separator
223
- ref={ref}
224
- className={cn(menuSeparatorClass, className)}
225
- {...props}
226
- />
227
- ));
275
+ >(({ className, ...props }, ref) => {
276
+ const separator = useUiConfig("DropdownMenu")?.classes?.separator;
277
+ const merge = useCn();
278
+ return (
279
+ <DropdownMenuPrimitive.Separator
280
+ ref={ref}
281
+ className={merge(separator ?? menuSeparatorClass, className)}
282
+ {...props}
283
+ />
284
+ );
285
+ });
228
286
  DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
229
287
 
230
288
  const DropdownMenuShortcut = ({
231
289
  className,
232
290
  ...props
233
291
  }: React.HTMLAttributes<HTMLSpanElement>) => {
234
- return <span className={cn(menuShortcutClass, className)} {...props} />;
292
+ const shortcut = useUiConfig("DropdownMenu")?.classes?.shortcut;
293
+ const merge = useCn();
294
+ return <span className={merge(shortcut ?? menuShortcutClass, className)} {...props} />;
235
295
  };
236
296
  DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
237
297
 
@@ -1,31 +1,73 @@
1
1
  import * as React from "react";
2
2
  import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
3
- import { cn } from "../utils/cn";
4
3
  import {
5
4
  floatingSurfaceAnimateClass,
6
5
  floatingSurfaceClass,
7
6
  } from "./_menu-classes";
7
+ import { useCn, useUiConfig } from "./ui-config";
8
+
9
+ /** App defaults for `HoverCard*` (see `UiConfigProvider`). */
10
+ export interface HoverCardConfig {
11
+ /** Replaces the built-in classes of a part. */
12
+ classes?: { content?: string };
13
+ /** Default for `HoverCard` `openDelay` (Radix: 700ms). */
14
+ openDelay?: number;
15
+ /** Default for `HoverCard` `closeDelay` (Radix: 300ms). */
16
+ closeDelay?: number;
17
+ /** Default for `HoverCardContent` `sideOffset`. */
18
+ sideOffset?: number;
19
+ /** Default for `HoverCardContent` `portal`. */
20
+ portal?: boolean;
21
+ }
22
+
23
+ function HoverCard({
24
+ openDelay,
25
+ closeDelay,
26
+ ...props
27
+ }: React.ComponentProps<typeof HoverCardPrimitive.Root>) {
28
+ const config = useUiConfig("HoverCard");
29
+ return (
30
+ <HoverCardPrimitive.Root
31
+ openDelay={openDelay ?? config?.openDelay}
32
+ closeDelay={closeDelay ?? config?.closeDelay}
33
+ {...props}
34
+ />
35
+ );
36
+ }
8
37
 
9
- const HoverCard = HoverCardPrimitive.Root;
10
38
  const HoverCardTrigger = HoverCardPrimitive.Trigger;
11
39
 
12
40
  const HoverCardContent = React.forwardRef<
13
41
  React.ComponentRef<typeof HoverCardPrimitive.Content>,
14
- React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
15
- >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
16
- <HoverCardPrimitive.Content
17
- ref={ref}
18
- align={align}
19
- sideOffset={sideOffset}
20
- className={cn(
21
- "z-50 w-72 max-sm:max-w-[calc(100vw-2rem)] max-sm:max-h-[85dvh] max-sm:overflow-y-auto p-4 outline-none",
22
- floatingSurfaceClass,
23
- floatingSurfaceAnimateClass,
24
- className,
25
- )}
26
- {...props}
27
- />
28
- ));
42
+ React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content> & {
43
+ /** Render into `document.body` so a clipping parent cannot cut it off. */
44
+ portal?: boolean;
45
+ }
46
+ >(({ className, align = "center", sideOffset, portal, ...props }, ref) => {
47
+ const merge = useCn();
48
+ const config = useUiConfig("HoverCard");
49
+ const content = (
50
+ <HoverCardPrimitive.Content
51
+ ref={ref}
52
+ align={align}
53
+ sideOffset={sideOffset ?? config?.sideOffset ?? 4}
54
+ className={merge(
55
+ config?.classes?.content ?? [
56
+ "z-50 w-72 max-sm:max-w-[calc(100vw-2rem)] max-sm:max-h-[85dvh] max-sm:overflow-y-auto p-4 outline-none",
57
+ floatingSurfaceClass,
58
+ floatingSurfaceAnimateClass,
59
+ ],
60
+ className,
61
+ )}
62
+ {...props}
63
+ />
64
+ );
65
+ return (portal ?? config?.portal) ? (
66
+ <HoverCardPrimitive.Portal>{content}</HoverCardPrimitive.Portal>
67
+ ) : (
68
+ content
69
+ );
70
+ });
29
71
  HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;
30
72
 
31
73
  export { HoverCard, HoverCardTrigger, HoverCardContent };