@firecms/ui 3.0.1 → 3.1.0-canary.24c8270
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 +2 -1
- package/dist/components/SearchBar.d.ts +11 -1
- package/dist/components/Select.d.ts +2 -1
- 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/hooks/useOutsideAlerter.d.ts +1 -1
- package/dist/index.css +57 -6
- package/dist/index.es.js +1731 -949
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1731 -949
- package/dist/index.umd.js.map +1 -1
- package/dist/styles.d.ts +11 -11
- package/package.json +7 -7
- 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/DebouncedTextField.tsx +3 -3
- 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 +91 -74
- package/src/components/Popover.tsx +11 -3
- package/src/components/SearchBar.tsx +37 -19
- package/src/components/Select.tsx +86 -73
- 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 +121 -36
- 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/hooks/useOutsideAlerter.tsx +1 -1
- 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
|
|
|
@@ -54,11 +55,12 @@ interface MultiSelectProps<T extends MultiSelectValue = string> {
|
|
|
54
55
|
multiple?: boolean,
|
|
55
56
|
includeSelectAll?: boolean,
|
|
56
57
|
includeClear?: boolean,
|
|
57
|
-
inputRef?: React.RefObject<HTMLButtonElement>,
|
|
58
|
+
inputRef?: React.RefObject<HTMLButtonElement | null>,
|
|
58
59
|
padding?: boolean,
|
|
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);
|
|
@@ -103,20 +118,18 @@ export const MultiSelect = React.forwardRef<
|
|
|
103
118
|
|
|
104
119
|
const allValues = React.useMemo(() => children
|
|
105
120
|
?
|
|
106
|
-
// @ts-ignore
|
|
107
121
|
Children.map(children, (child) => {
|
|
108
|
-
if (React.isValidElement(child)) {
|
|
122
|
+
if (React.isValidElement<MultiSelectItemProps>(child)) {
|
|
109
123
|
return child.props.value;
|
|
110
124
|
}
|
|
111
125
|
return null;
|
|
112
|
-
})
|
|
126
|
+
})?.filter(Boolean) ?? []
|
|
113
127
|
: [], [children]);
|
|
114
128
|
|
|
115
129
|
const optionsMap = React.useMemo(() => {
|
|
116
130
|
const map = new Map<string, React.ReactNode>();
|
|
117
131
|
Children.forEach(children, (child) => {
|
|
118
|
-
if (React.isValidElement(child)) {
|
|
119
|
-
// @ts-ignore
|
|
132
|
+
if (React.isValidElement<MultiSelectItemProps>(child)) {
|
|
120
133
|
map.set(String(child.props.value), child.props.children);
|
|
121
134
|
}
|
|
122
135
|
});
|
|
@@ -195,13 +208,13 @@ export const MultiSelect = React.forwardRef<
|
|
|
195
208
|
{typeof label === "string" ? <SelectInputLabel error={error}>{label}</SelectInputLabel> : label}
|
|
196
209
|
|
|
197
210
|
<PopoverPrimitive.Root
|
|
198
|
-
open={isPopoverOpen}
|
|
211
|
+
open={isMounted && isPopoverOpen}
|
|
199
212
|
onOpenChange={onPopoverOpenChange}
|
|
200
213
|
modal={modalPopover}
|
|
201
214
|
>
|
|
202
215
|
<PopoverPrimitive.Trigger asChild>
|
|
203
216
|
<button
|
|
204
|
-
ref={ref}
|
|
217
|
+
ref={inputRef ?? ref}
|
|
205
218
|
onClick={handleTogglePopover}
|
|
206
219
|
className={cls(
|
|
207
220
|
{
|
|
@@ -219,10 +232,12 @@ export const MultiSelect = React.forwardRef<
|
|
|
219
232
|
"px-4": size === "medium" || size === "large",
|
|
220
233
|
},
|
|
221
234
|
"select-none rounded-md text-sm",
|
|
235
|
+
"focus:ring-0 focus-visible:ring-0 outline-none focus:outline-none focus-visible:outline-none",
|
|
222
236
|
invisible ? fieldBackgroundInvisibleMixin : fieldBackgroundMixin,
|
|
223
237
|
disabled ? fieldBackgroundDisabledMixin : fieldBackgroundHoverMixin,
|
|
224
238
|
"relative flex items-center",
|
|
225
|
-
className
|
|
239
|
+
className,
|
|
240
|
+
inputClassName
|
|
226
241
|
)}
|
|
227
242
|
>
|
|
228
243
|
{selectedValues.length > 0 ? (
|
|
@@ -264,73 +279,75 @@ export const MultiSelect = React.forwardRef<
|
|
|
264
279
|
/>}
|
|
265
280
|
<div className={cls("px-2 h-full flex items-center")}>
|
|
266
281
|
<KeyboardArrowDownIcon size={size === "large" ? "medium" : "small"}
|
|
267
|
-
|
|
282
|
+
className={cls("transition", isPopoverOpen ? "rotate-180" : "")} />
|
|
268
283
|
</div>
|
|
269
284
|
</div>
|
|
270
285
|
</div>
|
|
271
286
|
) : (
|
|
272
287
|
<div className="flex items-center justify-between w-full mx-auto">
|
|
273
288
|
<span className="text-sm">
|
|
274
|
-
|
|
289
|
+
{placeholder}
|
|
275
290
|
</span>
|
|
276
291
|
<div className={cls("px-2 h-full flex items-center")}>
|
|
277
292
|
<KeyboardArrowDownIcon size={size === "large" ? "medium" : "small"}
|
|
278
|
-
|
|
293
|
+
className={cls("transition", isPopoverOpen ? "rotate-180" : "")} />
|
|
279
294
|
</div>
|
|
280
295
|
</div>
|
|
281
296
|
)}
|
|
282
297
|
</button>
|
|
283
298
|
</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
|
-
|
|
299
|
+
<PopoverPrimitive.Portal container={finalContainer}>
|
|
300
|
+
<PopoverPrimitive.Content
|
|
301
|
+
className={cls("z-50 overflow-hidden border bg-white dark:bg-surface-900 rounded-lg w-[400px]", defaultBorderMixin)}
|
|
302
|
+
align="start"
|
|
303
|
+
sideOffset={8}
|
|
304
|
+
onEscapeKeyDown={() => onPopoverOpenChange(false)}
|
|
305
|
+
>
|
|
306
|
+
<CommandPrimitive>
|
|
307
|
+
<div className={"flex flex-row items-center"}>
|
|
308
|
+
<CommandPrimitive.Input
|
|
309
|
+
className={cls(focusedDisabled, "bg-transparent outline-none flex-1 h-full w-full m-4 flex-grow text-surface-accent-900 dark:text-white")}
|
|
310
|
+
placeholder="Search..."
|
|
311
|
+
onKeyDown={handleInputKeyDown}
|
|
312
|
+
/>
|
|
313
|
+
{selectedValues.length > 0 && (
|
|
314
|
+
<div
|
|
315
|
+
onClick={handleClear}
|
|
316
|
+
className="text-sm justify-center cursor-pointer py-3 px-4 text-text-secondary dark:text-text-secondary-dark">
|
|
317
|
+
Clear
|
|
318
|
+
</div>
|
|
319
|
+
)}
|
|
320
|
+
</div>
|
|
321
|
+
<Separator orientation={"horizontal"} className={"my-0"} />
|
|
322
|
+
<CommandPrimitive.List>
|
|
323
|
+
<CommandPrimitive.Empty className={"px-4 py-2 text-sm text-text-secondary dark:text-text-secondary-dark"}>
|
|
324
|
+
No results found.
|
|
325
|
+
</CommandPrimitive.Empty>
|
|
326
|
+
<CommandPrimitive.Group>
|
|
327
|
+
{includeSelectAll && <CommandPrimitive.Item
|
|
328
|
+
key="all"
|
|
329
|
+
onSelect={toggleAll}
|
|
330
|
+
className={
|
|
331
|
+
cls(
|
|
332
|
+
"flex flex-row items-center gap-1.5",
|
|
333
|
+
"cursor-pointer",
|
|
334
|
+
"m-1",
|
|
335
|
+
"ring-offset-transparent",
|
|
336
|
+
"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",
|
|
337
|
+
"aria-[selected=true]:bg-surface-accent-100 aria-[selected=true]:dark:bg-surface-accent-900",
|
|
338
|
+
"cursor-pointer p-2 rounded aria-[selected=true]:bg-surface-accent-100 aria-[selected=true]:dark:bg-surface-accent-900"
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
>
|
|
342
|
+
<InnerCheckBox checked={selectedValues.length === allValues.length} />
|
|
343
|
+
<span className={"text-sm text-text-secondary dark:text-text-secondary-dark"}>(Select All)</span>
|
|
344
|
+
</CommandPrimitive.Item>}
|
|
345
|
+
{children}
|
|
346
|
+
</CommandPrimitive.Group>
|
|
347
|
+
</CommandPrimitive.List>
|
|
348
|
+
</CommandPrimitive>
|
|
349
|
+
</PopoverPrimitive.Content>
|
|
350
|
+
</PopoverPrimitive.Portal>
|
|
334
351
|
</PopoverPrimitive.Root>
|
|
335
352
|
</MultiSelectContext.Provider>
|
|
336
353
|
);
|
|
@@ -346,10 +363,10 @@ export interface MultiSelectItemProps<T extends MultiSelectValue = string> {
|
|
|
346
363
|
}
|
|
347
364
|
|
|
348
365
|
export const MultiSelectItem = React.memo(function MultiSelectItem<T extends MultiSelectValue = string>({
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
366
|
+
children,
|
|
367
|
+
value,
|
|
368
|
+
className
|
|
369
|
+
}: MultiSelectItemProps<T>) {
|
|
353
370
|
const context = React.useContext(MultiSelectContext);
|
|
354
371
|
if (!context) throw new Error("MultiSelectItem must be used inside a MultiSelect");
|
|
355
372
|
const {
|
|
@@ -373,13 +390,14 @@ export const MultiSelectItem = React.memo(function MultiSelectItem<T extends Mul
|
|
|
373
390
|
"cursor-pointer",
|
|
374
391
|
"m-1",
|
|
375
392
|
"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",
|
|
393
|
+
"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
394
|
"aria-[selected=true]:bg-surface-accent-100 aria-[selected=true]:dark:bg-surface-accent-900",
|
|
378
395
|
"cursor-pointer p-2 rounded aria-[selected=true]:bg-surface-accent-100 aria-[selected=true]:dark:bg-surface-accent-900",
|
|
396
|
+
"text-surface-accent-700 dark:text-surface-accent-300",
|
|
379
397
|
className
|
|
380
398
|
)}
|
|
381
399
|
>
|
|
382
|
-
<InnerCheckBox checked={isSelected}/>
|
|
400
|
+
<InnerCheckBox checked={isSelected} />
|
|
383
401
|
{children}
|
|
384
402
|
</CommandPrimitive.Item>;
|
|
385
403
|
});
|
|
@@ -398,8 +416,7 @@ const InnerCheckBox = React.memo(function InnerCheckBox({ checked }: { checked:
|
|
|
398
416
|
(checked) ? "text-surface-accent-100 dark:text-surface-accent-900" : "",
|
|
399
417
|
(checked ? "border-transparent" : "border-surface-accent-800 dark:border-surface-accent-200")
|
|
400
418
|
)}>
|
|
401
|
-
{checked && <CheckIcon size={16} className={"absolute"}/>}
|
|
419
|
+
{checked && <CheckIcon size={16} className={"absolute"} />}
|
|
402
420
|
</div>
|
|
403
421
|
</div>
|
|
404
422
|
});
|
|
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;
|