@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.
- package/README.md +9 -7
- package/dist/components/Card.d.ts +1 -1
- package/dist/components/ColorPicker.d.ts +30 -0
- package/dist/components/DateTimeField.d.ts +7 -0
- package/dist/components/Dialog.d.ts +2 -1
- package/dist/components/FileUpload.d.ts +1 -1
- package/dist/components/Menu.d.ts +2 -1
- package/dist/components/Menubar.d.ts +2 -1
- package/dist/components/MultiSelect.d.ts +1 -0
- package/dist/components/SearchBar.d.ts +11 -1
- package/dist/components/Select.d.ts +1 -0
- package/dist/components/Sheet.d.ts +1 -0
- package/dist/components/ToggleButtonGroup.d.ts +30 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/hooks/PortalContainerContext.d.ts +31 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/index.css +57 -6
- package/dist/index.es.js +1603 -935
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1603 -935
- package/dist/index.umd.js.map +1 -1
- package/dist/styles.d.ts +11 -11
- package/package.json +3 -3
- package/src/components/BooleanSwitch.tsx +3 -3
- package/src/components/Button.tsx +5 -5
- package/src/components/Card.tsx +7 -7
- package/src/components/Checkbox.tsx +1 -1
- package/src/components/ColorPicker.tsx +134 -0
- package/src/components/DateTimeField.tsx +123 -34
- package/src/components/Dialog.tsx +25 -16
- package/src/components/DialogActions.tsx +1 -1
- package/src/components/ExpandablePanel.tsx +1 -1
- package/src/components/FileUpload.tsx +25 -24
- package/src/components/IconButton.tsx +3 -2
- package/src/components/Menu.tsx +44 -30
- package/src/components/Menubar.tsx +14 -3
- package/src/components/MultiSelect.tsx +87 -68
- package/src/components/Popover.tsx +11 -3
- package/src/components/SearchBar.tsx +37 -19
- package/src/components/Select.tsx +30 -17
- package/src/components/Separator.tsx +2 -2
- package/src/components/Sheet.tsx +12 -3
- package/src/components/Slider.tsx +4 -4
- package/src/components/Table.tsx +1 -1
- package/src/components/Tabs.tsx +14 -17
- package/src/components/TextField.tsx +19 -8
- package/src/components/ToggleButtonGroup.tsx +67 -0
- package/src/components/Tooltip.tsx +9 -2
- package/src/components/index.tsx +2 -0
- package/src/hooks/PortalContainerContext.tsx +48 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useInjectStyles.tsx +12 -3
- package/src/index.css +57 -6
- package/src/styles.ts +11 -11
- package/src/util/cls.ts +1 -1
- 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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
93
|
-
|
|
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,
|
package/src/components/Menu.tsx
CHANGED
|
@@ -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
|
-
|
|
37
|
-
}, ref) =>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
92
|
-
const [isPopoverOpen, setIsPopoverOpen] =
|
|
93
|
-
const [selectedValues, setSelectedValues] =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
295
|
+
className={cls("transition", isPopoverOpen ? "rotate-180" : "")} />
|
|
279
296
|
</div>
|
|
280
297
|
</div>
|
|
281
298
|
)}
|
|
282
299
|
</button>
|
|
283
300
|
</PopoverPrimitive.Trigger>
|
|
284
|
-
<PopoverPrimitive.
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
<
|
|
292
|
-
<
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
<CommandPrimitive.
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
-
|
|
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;
|