@firecms/ui 3.0.1 → 3.1.0-canary.9e89e98

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 (56) hide show
  1. package/README.md +9 -7
  2. package/dist/components/Card.d.ts +1 -1
  3. package/dist/components/ColorPicker.d.ts +30 -0
  4. package/dist/components/DateTimeField.d.ts +7 -0
  5. package/dist/components/Dialog.d.ts +2 -1
  6. package/dist/components/FileUpload.d.ts +1 -1
  7. package/dist/components/Menu.d.ts +2 -1
  8. package/dist/components/Menubar.d.ts +2 -1
  9. package/dist/components/MultiSelect.d.ts +1 -0
  10. package/dist/components/SearchBar.d.ts +11 -1
  11. package/dist/components/Select.d.ts +1 -0
  12. package/dist/components/Sheet.d.ts +1 -0
  13. package/dist/components/ToggleButtonGroup.d.ts +30 -0
  14. package/dist/components/index.d.ts +2 -0
  15. package/dist/hooks/PortalContainerContext.d.ts +31 -0
  16. package/dist/hooks/index.d.ts +1 -0
  17. package/dist/index.css +57 -6
  18. package/dist/index.es.js +1603 -935
  19. package/dist/index.es.js.map +1 -1
  20. package/dist/index.umd.js +1603 -935
  21. package/dist/index.umd.js.map +1 -1
  22. package/dist/styles.d.ts +11 -11
  23. package/package.json +3 -3
  24. package/src/components/BooleanSwitch.tsx +3 -3
  25. package/src/components/Button.tsx +5 -5
  26. package/src/components/Card.tsx +7 -7
  27. package/src/components/Checkbox.tsx +1 -1
  28. package/src/components/ColorPicker.tsx +134 -0
  29. package/src/components/DateTimeField.tsx +123 -34
  30. package/src/components/Dialog.tsx +25 -16
  31. package/src/components/DialogActions.tsx +1 -1
  32. package/src/components/ExpandablePanel.tsx +1 -1
  33. package/src/components/FileUpload.tsx +25 -24
  34. package/src/components/IconButton.tsx +3 -2
  35. package/src/components/Menu.tsx +44 -30
  36. package/src/components/Menubar.tsx +14 -3
  37. package/src/components/MultiSelect.tsx +87 -68
  38. package/src/components/Popover.tsx +11 -3
  39. package/src/components/SearchBar.tsx +37 -19
  40. package/src/components/Select.tsx +30 -17
  41. package/src/components/Separator.tsx +2 -2
  42. package/src/components/Sheet.tsx +12 -3
  43. package/src/components/Slider.tsx +4 -4
  44. package/src/components/Table.tsx +1 -1
  45. package/src/components/Tabs.tsx +14 -17
  46. package/src/components/TextField.tsx +19 -8
  47. package/src/components/ToggleButtonGroup.tsx +67 -0
  48. package/src/components/Tooltip.tsx +9 -2
  49. package/src/components/index.tsx +2 -0
  50. package/src/hooks/PortalContainerContext.tsx +48 -0
  51. package/src/hooks/index.ts +1 -0
  52. package/src/hooks/useInjectStyles.tsx +12 -3
  53. package/src/index.css +57 -6
  54. package/src/styles.ts +11 -11
  55. package/src/util/cls.ts +1 -1
  56. package/tailwind.config.js +2 -3
@@ -28,22 +28,22 @@ export type FileUploadProps = {
28
28
  title?: React.ReactNode;
29
29
  uploadDescription?: React.ReactNode;
30
30
  preventDropOnDocument?: boolean;
31
- size?: "medium" | "large";
31
+ size?: "small" | "medium" | "large";
32
32
  };
33
33
 
34
34
  export function FileUpload({
35
- accept,
36
- onFilesAdded,
37
- onFilesRejected,
38
- maxSize,
39
- disabled,
40
- maxFiles,
41
- title,
42
- uploadDescription,
43
- children,
44
- preventDropOnDocument = true,
45
- size
46
- }: React.PropsWithChildren<FileUploadProps>) {
35
+ accept,
36
+ onFilesAdded,
37
+ onFilesRejected,
38
+ maxSize,
39
+ disabled,
40
+ maxFiles,
41
+ title,
42
+ uploadDescription,
43
+ children,
44
+ preventDropOnDocument = true,
45
+ size
46
+ }: React.PropsWithChildren<FileUploadProps>) {
47
47
 
48
48
  const {
49
49
  getRootProps,
@@ -52,15 +52,15 @@ export function FileUpload({
52
52
  isDragAccept,
53
53
  isDragReject
54
54
  } = useDropzone({
55
- accept,
56
- noDragEventsBubbling: true,
57
- maxSize,
58
- onDrop: onFilesAdded,
59
- onDropRejected: onFilesRejected,
60
- disabled,
61
- maxFiles,
62
- preventDropOnDocument
63
- }
55
+ accept,
56
+ noDragEventsBubbling: true,
57
+ maxSize,
58
+ onDrop: onFilesAdded,
59
+ onDropRejected: onFilesRejected,
60
+ disabled,
61
+ maxFiles,
62
+ preventDropOnDocument
63
+ }
64
64
  );
65
65
  return <div
66
66
  {...getRootProps()}
@@ -71,6 +71,7 @@ export function FileUpload({
71
71
  {
72
72
  "h-44": size === "large",
73
73
  "h-28": size === "medium",
74
+ "h-16": size === "small",
74
75
  "cursor-pointer": !disabled,
75
76
  [fieldBackgroundHoverMixin]: !isDragActive,
76
77
  "transition-colors duration-200 ease-[cubic-bezier(0,0,0.2,1)] border-red-500": isDragReject,
@@ -89,8 +90,8 @@ export function FileUpload({
89
90
  <div
90
91
  className="flex-grow h-28 box-border flex flex-col items-center justify-center text-center">
91
92
  <Typography align={"center"}
92
- variant={"label"}
93
- className={"flex flex-row gap-2 justify-center"}>
93
+ variant={"label"}
94
+ className={"flex flex-row gap-2 justify-center"}>
94
95
  {uploadDescription}
95
96
  </Typography>
96
97
  </div>
@@ -14,7 +14,7 @@ export type IconButtonProps<C extends React.ElementType> =
14
14
  onClick?: React.MouseEventHandler<any>
15
15
  }
16
16
 
17
- const buttonClasses = "hover:bg-surface-accent-200 hover:bg-opacity-75 dark:hover:bg-surface-accent-800 hover:scale-105 transition-transform";
17
+ const buttonClasses = "hover:bg-surface-accent-200 hover:bg-opacity-75 hover:bg-surface-accent-200/75 dark:hover:bg-surface-accent-800 hover:scale-105 transition-transform";
18
18
  const baseClasses = "inline-flex items-center justify-center p-2 text-sm font-medium focus:outline-none transition-colors ease-in-out duration-150";
19
19
  const colorClasses = "text-surface-accent-600 visited:text-surface-accent-600 dark:text-surface-accent-300 dark:visited:text-surface-300";
20
20
  const sizeClasses = {
@@ -40,7 +40,7 @@ const IconButtonInner = <C extends React.ElementType = "button">({
40
40
  ...props
41
41
  }: IconButtonProps<C>, ref: React.ForwardedRef<HTMLButtonElement>) => {
42
42
 
43
- const bgClasses = variant === "ghost" ? "bg-transparent" : "bg-surface-accent-200 bg-opacity-50 dark:bg-surface-950 dark:bg-opacity-50";
43
+ const bgClasses = variant === "ghost" ? "bg-transparent" : "bg-surface-accent-200 bg-opacity-50 bg-surface-accent-200/50 dark:bg-surface-950 dark:bg-opacity-50 dark:bg-surface-950/50";
44
44
  const Component: React.ElementType<any> = component || "button";
45
45
  return (
46
46
  <Component
@@ -50,6 +50,7 @@ const IconButtonInner = <C extends React.ElementType = "button">({
50
50
  className={cls(
51
51
  disabled ? "opacity-50 pointer-events-none" : "cursor-pointer",
52
52
  toggled ? "outline outline-2 outline-primary" : "",
53
+ "text-inherit dark:text-inherit",
53
54
  colorClasses,
54
55
  bgClasses,
55
56
  baseClasses,
@@ -3,6 +3,7 @@ import React from "react";
3
3
  import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
4
4
  import { focusedDisabled, paperMixin } from "../styles";
5
5
  import { cls } from "../util";
6
+ import { usePortalContainer } from "../hooks/PortalContainerContext";
6
7
 
7
8
  export type MenuProps = {
8
9
  children: React.ReactNode;
@@ -33,28 +34,36 @@ const Menu = React.forwardRef<
33
34
  onOpenChange,
34
35
  portalContainer,
35
36
  sideOffset = 4,
36
- className
37
- }, ref) => (
38
- <DropdownMenu.Root
39
- open={open}
40
- defaultOpen={defaultOpen}
41
- onOpenChange={onOpenChange}>
42
- <DropdownMenu.Trigger
43
- ref={ref}
44
- asChild>
45
- {trigger}
46
- </DropdownMenu.Trigger>
47
- <DropdownMenu.Portal container={portalContainer}>
48
- <DropdownMenu.Content
49
- side={side}
50
- sideOffset={sideOffset}
51
- align={align}
52
- className={cls(paperMixin, focusedDisabled, "py-2 z-30", className)}>
53
- {children}
54
- </DropdownMenu.Content>
55
- </DropdownMenu.Portal>
56
- </DropdownMenu.Root>
57
- ))
37
+ className
38
+ }, ref) => {
39
+ // Get the portal container from context
40
+ const contextContainer = usePortalContainer();
41
+
42
+ // Prioritize manual prop, fallback to context container
43
+ const finalContainer = (portalContainer ?? contextContainer ?? undefined) as HTMLElement | undefined;
44
+
45
+ return (
46
+ <DropdownMenu.Root
47
+ open={open}
48
+ defaultOpen={defaultOpen}
49
+ onOpenChange={onOpenChange}>
50
+ <DropdownMenu.Trigger
51
+ ref={ref}
52
+ asChild>
53
+ {trigger}
54
+ </DropdownMenu.Trigger>
55
+ <DropdownMenu.Portal container={finalContainer}>
56
+ <DropdownMenu.Content
57
+ side={side}
58
+ sideOffset={sideOffset}
59
+ align={align}
60
+ className={cls(paperMixin, focusedDisabled, "py-2 z-30", className)}>
61
+ {children}
62
+ </DropdownMenu.Content>
63
+ </DropdownMenu.Portal>
64
+ </DropdownMenu.Root>
65
+ );
66
+ })
58
67
  Menu.displayName = "Menu"
59
68
 
60
69
  export { Menu }
@@ -62,20 +71,24 @@ export { Menu }
62
71
  export type MenuItemProps = {
63
72
  children: React.ReactNode;
64
73
  dense?: boolean;
74
+ disabled?: boolean;
65
75
  onClick?: (event: React.MouseEvent) => void;
66
76
  className?: string;
67
77
  };
68
78
 
69
79
  export const MenuItem = React.memo(({
70
- children,
71
- dense = false, // Default value is false if not provided
72
- onClick,
73
- className
74
- }: MenuItemProps) => {
80
+ children,
81
+ dense = false, // Default value is false if not provided
82
+ disabled = false,
83
+ onClick,
84
+ className
85
+ }: MenuItemProps) => {
75
86
  // Dynamically adjusting the class based on the "dense" prop
76
87
  const classNames = cls(
77
- onClick && "cursor-pointer",
78
- "rounded-md text-sm font-medium text-surface-accent-700 dark:text-surface-accent-300 hover:bg-surface-accent-100 dark:hover:bg-surface-accent-900 flex items-center gap-4",
88
+ onClick && !disabled && "cursor-pointer",
89
+ disabled && "opacity-50 cursor-not-allowed",
90
+ "rounded-md text-sm font-medium text-surface-accent-700 dark:text-surface-accent-300 flex items-center gap-4",
91
+ !disabled && "hover:bg-surface-accent-100 dark:hover:bg-surface-accent-900",
79
92
  dense ? "px-4 py-1.5" : "px-4 py-2",
80
93
  className
81
94
  );
@@ -83,7 +96,8 @@ export const MenuItem = React.memo(({
83
96
  return (
84
97
  <DropdownMenu.Item
85
98
  className={classNames}
86
- onClick={onClick}>
99
+ disabled={disabled}
100
+ onClick={disabled ? undefined : onClick}>
87
101
  {children}
88
102
  </DropdownMenu.Item>
89
103
  );
@@ -3,6 +3,7 @@ import * as React from "react";
3
3
  import * as MenubarPrimitive from "@radix-ui/react-menubar";
4
4
  import { cls } from "../util";
5
5
  import { CheckIcon, ChevronRightIcon } from "../icons";
6
+ import { usePortalContainer } from "../hooks/PortalContainerContext";
6
7
 
7
8
  export function Menubar({
8
9
  children,
@@ -44,7 +45,7 @@ export function MenubarTrigger({
44
45
  return (
45
46
  <MenubarPrimitive.Trigger
46
47
  onSelect={onSelect}
47
- className={cls("py-2 px-3 outline-none select-none font-medium leading-none rounded text-text-primary dark:text-text-primary-dark text-[13px] flex items-center justify-between gap-[2px] data-[highlighted]:bg-surface-accent-100 data-[highlighted]:dark:bg-surface-800 data-[state=open]:bg-surface-accent-100 data-[state=open]:dark:bg-surface-800 hover:bg-surface-accent-200 hover:bg-opacity-75 dark:hover:bg-surface-accent-800",
48
+ className={cls("py-2 px-3 outline-none select-none font-medium leading-none rounded text-text-primary dark:text-text-primary-dark text-[13px] flex items-center justify-between gap-[2px] data-[highlighted]:bg-surface-accent-100 data-[highlighted]:dark:bg-surface-800 data-[state=open]:bg-surface-accent-100 data-[state=open]:dark:bg-surface-800 hover:bg-surface-accent-200 hover:bg-opacity-75 hover:bg-surface-accent-200/75 dark:hover:bg-surface-accent-800",
48
49
  className)}>
49
50
  {children}
50
51
  </MenubarPrimitive.Trigger>
@@ -53,9 +54,19 @@ export function MenubarTrigger({
53
54
 
54
55
  export function MenubarPortal({
55
56
  children,
56
- }: { children: React.ReactNode }) {
57
+ portalContainer,
58
+ }: {
59
+ children: React.ReactNode;
60
+ portalContainer?: HTMLElement | null;
61
+ }) {
62
+ // Get the portal container from context
63
+ const contextContainer = usePortalContainer();
64
+
65
+ // Prioritize manual prop, fallback to context container
66
+ const finalContainer = (portalContainer ?? contextContainer ?? undefined) as HTMLElement | undefined;
67
+
57
68
  return (
58
- <MenubarPrimitive.Portal>
69
+ <MenubarPrimitive.Portal container={finalContainer}>
59
70
  {children}
60
71
  </MenubarPrimitive.Portal>
61
72
  )
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import * as PopoverPrimitive from "@radix-ui/react-popover";
3
3
  import * as React from "react";
4
- import { ChangeEvent, Children, useEffect } from "react";
4
+ import { ChangeEvent, Children, useEffect, useState } from "react";
5
5
  import { Command as CommandPrimitive } from "cmdk";
6
6
  import { cls } from "../util";
7
7
  import { CheckIcon, CloseIcon, KeyboardArrowDownIcon } from "../icons";
@@ -17,6 +17,7 @@ import {
17
17
  focusedDisabled
18
18
  } from "../styles";
19
19
  import { useInjectStyles } from "../hooks";
20
+ import { usePortalContainer } from "../hooks/PortalContainerContext";
20
21
 
21
22
  export type MultiSelectValue = string | number | boolean;
22
23
 
@@ -59,6 +60,7 @@ interface MultiSelectProps<T extends MultiSelectValue = string> {
59
60
  invisible?: boolean,
60
61
  children: React.ReactNode;
61
62
  renderValues?: (values: T[]) => React.ReactNode;
63
+ portalContainer?: HTMLElement | null;
62
64
  }
63
65
 
64
66
  // Use generic type for the forwarded ref
@@ -81,16 +83,29 @@ export const MultiSelect = React.forwardRef<
81
83
  includeSelectAll = true,
82
84
  useChips = true,
83
85
  className,
86
+ inputClassName,
87
+ inputRef,
84
88
  children,
85
89
  renderValues,
86
90
  open,
87
91
  onOpenChange,
92
+ portalContainer,
88
93
  },
89
94
  ref
90
95
  ) => {
91
- // Properly type the state variables to match the generic props
92
- const [isPopoverOpen, setIsPopoverOpen] = React.useState(open ?? false);
93
- const [selectedValues, setSelectedValues] = React.useState<any[]>(value ?? []);
96
+ const [isMounted, setIsMounted] = useState(false);
97
+ const [isPopoverOpen, setIsPopoverOpen] = useState(open ?? false);
98
+ const [selectedValues, setSelectedValues] = useState<any[]>(value ?? []);
99
+
100
+ // Get the portal container from context
101
+ const contextContainer = usePortalContainer();
102
+
103
+ // Prioritize manual prop, fallback to context container
104
+ const finalContainer = (portalContainer ?? contextContainer ?? undefined) as HTMLElement | undefined;
105
+
106
+ useEffect(() => {
107
+ setIsMounted(true);
108
+ }, []);
94
109
 
95
110
  const onPopoverOpenChange = (open: boolean) => {
96
111
  setIsPopoverOpen(open);
@@ -195,13 +210,13 @@ export const MultiSelect = React.forwardRef<
195
210
  {typeof label === "string" ? <SelectInputLabel error={error}>{label}</SelectInputLabel> : label}
196
211
 
197
212
  <PopoverPrimitive.Root
198
- open={isPopoverOpen}
213
+ open={isMounted && isPopoverOpen}
199
214
  onOpenChange={onPopoverOpenChange}
200
215
  modal={modalPopover}
201
216
  >
202
217
  <PopoverPrimitive.Trigger asChild>
203
218
  <button
204
- ref={ref}
219
+ ref={inputRef ?? ref}
205
220
  onClick={handleTogglePopover}
206
221
  className={cls(
207
222
  {
@@ -219,10 +234,12 @@ export const MultiSelect = React.forwardRef<
219
234
  "px-4": size === "medium" || size === "large",
220
235
  },
221
236
  "select-none rounded-md text-sm",
237
+ "focus:ring-0 focus-visible:ring-0 outline-none focus:outline-none focus-visible:outline-none",
222
238
  invisible ? fieldBackgroundInvisibleMixin : fieldBackgroundMixin,
223
239
  disabled ? fieldBackgroundDisabledMixin : fieldBackgroundHoverMixin,
224
240
  "relative flex items-center",
225
- className
241
+ className,
242
+ inputClassName
226
243
  )}
227
244
  >
228
245
  {selectedValues.length > 0 ? (
@@ -264,73 +281,75 @@ export const MultiSelect = React.forwardRef<
264
281
  />}
265
282
  <div className={cls("px-2 h-full flex items-center")}>
266
283
  <KeyboardArrowDownIcon size={size === "large" ? "medium" : "small"}
267
- className={cls("transition", isPopoverOpen ? "rotate-180" : "")}/>
284
+ className={cls("transition", isPopoverOpen ? "rotate-180" : "")} />
268
285
  </div>
269
286
  </div>
270
287
  </div>
271
288
  ) : (
272
289
  <div className="flex items-center justify-between w-full mx-auto">
273
290
  <span className="text-sm">
274
- {placeholder}
291
+ {placeholder}
275
292
  </span>
276
293
  <div className={cls("px-2 h-full flex items-center")}>
277
294
  <KeyboardArrowDownIcon size={size === "large" ? "medium" : "small"}
278
- className={cls("transition", isPopoverOpen ? "rotate-180" : "")}/>
295
+ className={cls("transition", isPopoverOpen ? "rotate-180" : "")} />
279
296
  </div>
280
297
  </div>
281
298
  )}
282
299
  </button>
283
300
  </PopoverPrimitive.Trigger>
284
- <PopoverPrimitive.Content
285
- className={cls("z-50 relative overflow-hidden border bg-white dark:bg-surface-900 rounded-lg w-[400px]", defaultBorderMixin)}
286
- align="start"
287
- sideOffset={8}
288
- onEscapeKeyDown={() => onPopoverOpenChange(false)}
289
- >
290
- <CommandPrimitive>
291
- <div className={"flex flex-row items-center"}>
292
- <CommandPrimitive.Input
293
- className={cls(focusedDisabled, "bg-transparent outline-none flex-1 h-full w-full m-4 flex-grow ")}
294
- placeholder="Search..."
295
- onKeyDown={handleInputKeyDown}
296
- />
297
- {selectedValues.length > 0 && (
298
- <div
299
- onClick={handleClear}
300
- className="text-sm justify-center cursor-pointer py-3 px-4 text-text-secondary dark:text-text-secondary-dark">
301
- Clear
302
- </div>
303
- )}
304
- </div>
305
- <Separator orientation={"horizontal"} className={"my-0"}/>
306
- <CommandPrimitive.List>
307
- <CommandPrimitive.Empty className={"px-4 py-2"}>
308
- No results found.
309
- </CommandPrimitive.Empty>
310
- <CommandPrimitive.Group>
311
- {includeSelectAll && <CommandPrimitive.Item
312
- key="all"
313
- onSelect={toggleAll}
314
- className={
315
- cls(
316
- "flex flex-row items-center gap-1.5",
317
- "cursor-pointer",
318
- "m-1",
319
- "ring-offset-transparent",
320
- "p-1 rounded aria-[selected=true]:outline-none aria-[selected=true]:ring-2 aria-[selected=true]:ring-primary aria-[selected=true]:ring-opacity-75 aria-[selected=true]:ring-offset-2",
321
- "aria-[selected=true]:bg-surface-accent-100 aria-[selected=true]:dark:bg-surface-accent-900",
322
- "cursor-pointer p-2 rounded aria-[selected=true]:bg-surface-accent-100 aria-[selected=true]:dark:bg-surface-accent-900"
323
- )
324
- }
325
- >
326
- <InnerCheckBox checked={selectedValues.length === allValues.length}/>
327
- <span className={"text-sm text-text-secondary dark:text-text-secondary-dark"}>(Select All)</span>
328
- </CommandPrimitive.Item>}
329
- {children}
330
- </CommandPrimitive.Group>
331
- </CommandPrimitive.List>
332
- </CommandPrimitive>
333
- </PopoverPrimitive.Content>
301
+ <PopoverPrimitive.Portal container={finalContainer}>
302
+ <PopoverPrimitive.Content
303
+ className={cls("z-50 overflow-hidden border bg-white dark:bg-surface-900 rounded-lg w-[400px]", defaultBorderMixin)}
304
+ align="start"
305
+ sideOffset={8}
306
+ onEscapeKeyDown={() => onPopoverOpenChange(false)}
307
+ >
308
+ <CommandPrimitive>
309
+ <div className={"flex flex-row items-center"}>
310
+ <CommandPrimitive.Input
311
+ className={cls(focusedDisabled, "bg-transparent outline-none flex-1 h-full w-full m-4 flex-grow text-surface-accent-900 dark:text-white")}
312
+ placeholder="Search..."
313
+ onKeyDown={handleInputKeyDown}
314
+ />
315
+ {selectedValues.length > 0 && (
316
+ <div
317
+ onClick={handleClear}
318
+ className="text-sm justify-center cursor-pointer py-3 px-4 text-text-secondary dark:text-text-secondary-dark">
319
+ Clear
320
+ </div>
321
+ )}
322
+ </div>
323
+ <Separator orientation={"horizontal"} className={"my-0"} />
324
+ <CommandPrimitive.List>
325
+ <CommandPrimitive.Empty className={"px-4 py-2 text-sm text-text-secondary dark:text-text-secondary-dark"}>
326
+ No results found.
327
+ </CommandPrimitive.Empty>
328
+ <CommandPrimitive.Group>
329
+ {includeSelectAll && <CommandPrimitive.Item
330
+ key="all"
331
+ onSelect={toggleAll}
332
+ className={
333
+ cls(
334
+ "flex flex-row items-center gap-1.5",
335
+ "cursor-pointer",
336
+ "m-1",
337
+ "ring-offset-transparent",
338
+ "p-1 rounded aria-[selected=true]:outline-none aria-[selected=true]:ring-2 aria-[selected=true]:ring-primary aria-[selected=true]:ring-opacity-75 aria-[selected=true]:ring-primary/75 aria-[selected=true]:ring-offset-2",
339
+ "aria-[selected=true]:bg-surface-accent-100 aria-[selected=true]:dark:bg-surface-accent-900",
340
+ "cursor-pointer p-2 rounded aria-[selected=true]:bg-surface-accent-100 aria-[selected=true]:dark:bg-surface-accent-900"
341
+ )
342
+ }
343
+ >
344
+ <InnerCheckBox checked={selectedValues.length === allValues.length} />
345
+ <span className={"text-sm text-text-secondary dark:text-text-secondary-dark"}>(Select All)</span>
346
+ </CommandPrimitive.Item>}
347
+ {children}
348
+ </CommandPrimitive.Group>
349
+ </CommandPrimitive.List>
350
+ </CommandPrimitive>
351
+ </PopoverPrimitive.Content>
352
+ </PopoverPrimitive.Portal>
334
353
  </PopoverPrimitive.Root>
335
354
  </MultiSelectContext.Provider>
336
355
  );
@@ -346,10 +365,10 @@ export interface MultiSelectItemProps<T extends MultiSelectValue = string> {
346
365
  }
347
366
 
348
367
  export const MultiSelectItem = React.memo(function MultiSelectItem<T extends MultiSelectValue = string>({
349
- children,
350
- value,
351
- className
352
- }: MultiSelectItemProps<T>) {
368
+ children,
369
+ value,
370
+ className
371
+ }: MultiSelectItemProps<T>) {
353
372
  const context = React.useContext(MultiSelectContext);
354
373
  if (!context) throw new Error("MultiSelectItem must be used inside a MultiSelect");
355
374
  const {
@@ -373,13 +392,14 @@ export const MultiSelectItem = React.memo(function MultiSelectItem<T extends Mul
373
392
  "cursor-pointer",
374
393
  "m-1",
375
394
  "ring-offset-transparent",
376
- "p-1 rounded aria-[selected=true]:outline-none aria-[selected=true]:ring-2 aria-[selected=true]:ring-primary aria-[selected=true]:ring-opacity-75 aria-[selected=true]:ring-offset-2",
395
+ "p-1 rounded aria-[selected=true]:outline-none aria-[selected=true]:ring-2 aria-[selected=true]:ring-primary aria-[selected=true]:ring-opacity-75 aria-[selected=true]:ring-primary/75 aria-[selected=true]:ring-offset-2",
377
396
  "aria-[selected=true]:bg-surface-accent-100 aria-[selected=true]:dark:bg-surface-accent-900",
378
397
  "cursor-pointer p-2 rounded aria-[selected=true]:bg-surface-accent-100 aria-[selected=true]:dark:bg-surface-accent-900",
398
+ "text-surface-accent-700 dark:text-surface-accent-300",
379
399
  className
380
400
  )}
381
401
  >
382
- <InnerCheckBox checked={isSelected}/>
402
+ <InnerCheckBox checked={isSelected} />
383
403
  {children}
384
404
  </CommandPrimitive.Item>;
385
405
  });
@@ -398,8 +418,7 @@ const InnerCheckBox = React.memo(function InnerCheckBox({ checked }: { checked:
398
418
  (checked) ? "text-surface-accent-100 dark:text-surface-accent-900" : "",
399
419
  (checked ? "border-transparent" : "border-surface-accent-800 dark:border-surface-accent-200")
400
420
  )}>
401
- {checked && <CheckIcon size={16} className={"absolute"}/>}
421
+ {checked && <CheckIcon size={16} className={"absolute"} />}
402
422
  </div>
403
423
  </div>
404
424
  });
405
-
@@ -5,6 +5,7 @@ import * as PopoverPrimitive from "@radix-ui/react-popover";
5
5
  import { paperMixin } from "../styles";
6
6
  import { cls } from "../util";
7
7
  import { useInjectStyles } from "../hooks";
8
+ import { usePortalContainer } from "../hooks/PortalContainerContext";
8
9
 
9
10
  export type PopoverSide = "top" | "right" | "bottom" | "left";
10
11
  export type PopoverAlign = "start" | "center" | "end";
@@ -49,16 +50,24 @@ export function Popover({
49
50
 
50
51
  useInjectStyles("Popover", popoverStyles);
51
52
 
53
+ // Get the portal container from context
54
+ const contextContainer = usePortalContainer();
55
+
56
+ // Prioritize manual prop, fallback to context container
57
+ const finalContainer = (portalContainer ?? contextContainer ?? undefined) as HTMLElement | undefined;
58
+
52
59
  if (!enabled)
53
60
  return <>{trigger}</>;
54
61
 
55
62
  return <PopoverPrimitive.Root open={open}
56
63
  onOpenChange={onOpenChange}
57
64
  modal={modal}>
65
+
58
66
  <PopoverPrimitive.Trigger asChild>
59
67
  {trigger}
60
68
  </PopoverPrimitive.Trigger>
61
- <PopoverPrimitive.Portal container={portalContainer}>
69
+
70
+ <PopoverPrimitive.Portal container={finalContainer}>
62
71
  <PopoverPrimitive.Content
63
72
  className={cls(paperMixin,
64
73
  "PopoverContent z-40", className)}
@@ -79,7 +88,7 @@ export function Popover({
79
88
  }
80
89
 
81
90
  const popoverStyles = `
82
-
91
+ /* ... (styles remain unchanged) ... */
83
92
  .PopoverContent {
84
93
  animation-duration: 400ms;
85
94
  animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
@@ -98,7 +107,6 @@ const popoverStyles = `
98
107
  animation-name: slideRightAndFade;
99
108
  }
100
109
 
101
-
102
110
  @keyframes slideUpAndFade {
103
111
  from {
104
112
  opacity: 0;