@firecms/ui 3.0.0-canary.16 → 3.0.0-canary.160
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 +60 -150
- package/dist/components/Avatar.d.ts +1 -0
- package/dist/components/BooleanSwitch.d.ts +1 -1
- package/dist/components/BooleanSwitchWithLabel.d.ts +4 -1
- package/dist/components/Button.d.ts +1 -1
- package/dist/components/CenteredView.d.ts +4 -2
- package/dist/components/Checkbox.d.ts +3 -2
- package/dist/components/Chip.d.ts +3 -2
- package/dist/components/DateTimeField.d.ts +5 -7
- package/dist/components/Dialog.d.ts +4 -1
- package/dist/components/DialogContent.d.ts +2 -1
- package/dist/components/DialogTitle.d.ts +10 -0
- package/dist/components/ExpandablePanel.d.ts +2 -1
- package/dist/components/FileUpload.d.ts +2 -2
- package/dist/components/InputLabel.d.ts +2 -2
- package/dist/components/Label.d.ts +4 -1
- package/dist/components/Markdown.d.ts +1 -0
- package/dist/components/Menu.d.ts +6 -2
- package/dist/components/Menubar.d.ts +79 -0
- package/dist/components/MultiSelect.d.ts +32 -16
- package/dist/components/Popover.d.ts +2 -1
- package/dist/components/RadioGroup.d.ts +26 -3
- package/dist/components/Select.d.ts +8 -11
- package/dist/components/Separator.d.ts +2 -1
- package/dist/components/Sheet.d.ts +6 -0
- package/dist/components/Slider.d.ts +21 -0
- package/dist/components/Table.d.ts +10 -10
- package/dist/components/Tabs.d.ts +4 -2
- package/dist/components/TextField.d.ts +1 -1
- package/dist/components/TextareaAutosize.d.ts +3 -34
- package/dist/components/Tooltip.d.ts +6 -2
- package/dist/components/Typography.d.ts +5 -4
- package/dist/components/index.d.ts +3 -1
- package/dist/hooks/index.d.ts +3 -0
- package/dist/icons/Icon.d.ts +3 -3
- package/dist/index.css +73 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +50174 -20590
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +50441 -857
- package/dist/index.umd.js.map +1 -1
- package/dist/styles.d.ts +9 -9
- package/dist/util/{cn.d.ts → cls.d.ts} +4 -0
- package/dist/util/index.d.ts +1 -3
- package/package.json +119 -118
- package/src/components/Alert.tsx +4 -4
- package/src/components/Autocomplete.tsx +7 -5
- package/src/components/Avatar.tsx +41 -26
- package/src/components/Badge.tsx +2 -2
- package/src/components/BooleanSwitch.tsx +14 -13
- package/src/components/BooleanSwitchWithLabel.tsx +17 -8
- package/src/components/Button.tsx +31 -23
- package/src/components/Card.tsx +4 -3
- package/src/components/CenteredView.tsx +26 -15
- package/src/components/Checkbox.tsx +16 -14
- package/src/components/Chip.tsx +13 -10
- package/src/components/CircularProgress.tsx +3 -3
- package/src/components/Collapse.tsx +4 -2
- package/src/components/Container.tsx +3 -2
- package/src/components/DateTimeField.tsx +144 -921
- package/src/components/DebouncedTextField.tsx +1 -0
- package/src/components/Dialog.tsx +17 -7
- package/src/components/DialogActions.tsx +3 -3
- package/src/components/DialogContent.tsx +7 -3
- package/src/components/DialogTitle.tsx +41 -0
- package/src/components/ExpandablePanel.tsx +20 -12
- package/src/components/FileUpload.tsx +11 -13
- package/src/components/IconButton.tsx +7 -11
- package/src/components/InfoLabel.tsx +2 -2
- package/src/components/InputLabel.tsx +12 -9
- package/src/components/Label.tsx +18 -4
- package/src/components/Markdown.tsx +15 -3
- package/src/components/Menu.tsx +50 -31
- package/src/components/Menubar.tsx +322 -0
- package/src/components/MultiSelect.tsx +341 -167
- package/src/components/Paper.tsx +2 -2
- package/src/components/Popover.tsx +19 -15
- package/src/components/RadioGroup.tsx +42 -9
- package/src/components/SearchBar.tsx +12 -11
- package/src/components/Select.tsx +142 -130
- package/src/components/Separator.tsx +10 -4
- package/src/components/Sheet.tsx +53 -31
- package/src/components/Skeleton.tsx +9 -6
- package/src/components/Slider.tsx +110 -0
- package/src/components/Table.tsx +54 -35
- package/src/components/Tabs.tsx +17 -15
- package/src/components/TextField.tsx +25 -23
- package/src/components/TextareaAutosize.tsx +4 -3
- package/src/components/Tooltip.tsx +33 -16
- package/src/components/Typography.tsx +42 -26
- package/src/components/common/SelectInputLabel.tsx +3 -3
- package/src/components/index.tsx +3 -1
- package/src/hooks/index.ts +3 -0
- package/src/{util → hooks}/useDebounceValue.tsx +2 -0
- package/src/{util → hooks}/useInjectStyles.tsx +1 -0
- package/src/{util → hooks}/useOutsideAlerter.tsx +2 -0
- package/src/icons/Icon.tsx +48 -43
- package/src/icons/icon_keys.ts +114 -1301
- package/src/index.css +73 -0
- package/src/index.ts +1 -0
- package/src/scripts/generateIconKeys.ts +20 -11
- package/src/styles.ts +9 -9
- package/src/util/cls.ts +14 -0
- package/src/util/index.ts +1 -3
- package/tailwind.config.js +42 -26
- package/dist/components/Spinner.d.ts +0 -1
- package/src/components/Spinner.tsx +0 -18
- package/src/util/cn.ts +0 -6
- /package/dist/{util → hooks}/useDebounceValue.d.ts +0 -0
- /package/dist/{util → hooks}/useInjectStyles.d.ts +0 -0
- /package/dist/{util → hooks}/useOutsideAlerter.d.ts +0 -0
@@ -1,192 +1,335 @@
|
|
1
|
+
"use client";
|
2
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
1
3
|
import * as React from "react";
|
2
|
-
import { useEffect } from "react";
|
3
|
-
import * as Dialog from "@radix-ui/react-dialog";
|
4
|
-
|
4
|
+
import { ChangeEvent, Children, useEffect } from "react";
|
5
5
|
import { Command as CommandPrimitive } from "cmdk";
|
6
|
-
|
7
|
-
import { ExpandMoreIcon } from "../icons";
|
8
|
-
import {
|
9
|
-
import {
|
6
|
+
import { cls } from "../util";
|
7
|
+
import { CloseIcon, ExpandMoreIcon, Icon } from "../icons";
|
8
|
+
import { Separator } from "./Separator";
|
9
|
+
import { Chip } from "./Chip";
|
10
10
|
import { SelectInputLabel } from "./common/SelectInputLabel";
|
11
|
+
import {
|
12
|
+
defaultBorderMixin,
|
13
|
+
fieldBackgroundDisabledMixin,
|
14
|
+
fieldBackgroundHoverMixin,
|
15
|
+
fieldBackgroundInvisibleMixin,
|
16
|
+
fieldBackgroundMixin,
|
17
|
+
focusedDisabled
|
18
|
+
} from "../styles";
|
19
|
+
import { useInjectStyles } from "../hooks";
|
20
|
+
|
21
|
+
interface MultiSelectContextProps {
|
22
|
+
fieldValue?: string[];
|
23
|
+
onItemClick: (v: string) => void;
|
24
|
+
}
|
25
|
+
|
26
|
+
export const MultiSelectContext = React.createContext<MultiSelectContextProps>({} as any);
|
27
|
+
|
28
|
+
/**
|
29
|
+
* Props for MultiSelect component
|
30
|
+
*/
|
31
|
+
interface MultiSelectProps {
|
32
|
+
|
33
|
+
/**
|
34
|
+
* The modality of the popover. When set to true, interaction with outside elements
|
35
|
+
* will be disabled and only popover content will be visible to screen readers.
|
36
|
+
* Optional, defaults to false.
|
37
|
+
*/
|
38
|
+
modalPopover?: boolean;
|
39
|
+
|
40
|
+
/**
|
41
|
+
* Additional class names to apply custom styles to the multi-select component.
|
42
|
+
* Optional, can be used to add custom styles.
|
43
|
+
*/
|
44
|
+
className?: string;
|
11
45
|
|
12
|
-
export type MultiSelectProps = {
|
13
46
|
open?: boolean,
|
14
47
|
name?: string,
|
15
48
|
id?: string,
|
16
49
|
onOpenChange?: (open: boolean) => void,
|
17
50
|
value?: string[],
|
18
|
-
containerClassName?: string,
|
19
|
-
className?: string,
|
20
51
|
inputClassName?: string,
|
21
|
-
|
52
|
+
onChange?: React.EventHandler<ChangeEvent<HTMLSelectElement>>,
|
53
|
+
onValueChange?: (updatedValue: string[]) => void,
|
22
54
|
placeholder?: React.ReactNode,
|
23
|
-
renderValue?: (values: string, index:number) => React.ReactNode,
|
24
|
-
renderValues?: (values: string[]) => React.ReactNode,
|
25
55
|
size?: "small" | "medium",
|
26
|
-
|
56
|
+
useChips?: boolean,
|
57
|
+
label?: React.ReactNode | string,
|
27
58
|
disabled?: boolean,
|
28
59
|
error?: boolean,
|
29
60
|
position?: "item-aligned" | "popper",
|
30
61
|
endAdornment?: React.ReactNode,
|
62
|
+
multiple?: boolean,
|
63
|
+
includeSelectAll?: boolean,
|
64
|
+
includeClear?: boolean,
|
31
65
|
inputRef?: React.RefObject<HTMLButtonElement>,
|
32
66
|
padding?: boolean,
|
33
|
-
|
34
|
-
children
|
35
|
-
|
36
|
-
|
37
|
-
interface MultiSelectContextProps {
|
38
|
-
fieldValue?: string[];
|
39
|
-
setInputValue: (v: string) => void;
|
40
|
-
onValueChangeInternal: (v: string) => void;
|
67
|
+
invisible?: boolean,
|
68
|
+
children: React.ReactNode;
|
69
|
+
renderValues?: (values: string[]) => React.ReactNode;
|
41
70
|
}
|
42
71
|
|
43
|
-
export const
|
72
|
+
export const MultiSelect = React.forwardRef<
|
73
|
+
HTMLButtonElement,
|
74
|
+
MultiSelectProps
|
75
|
+
>(
|
76
|
+
(
|
77
|
+
{
|
78
|
+
value,
|
79
|
+
size,
|
80
|
+
label,
|
81
|
+
error,
|
82
|
+
onValueChange,
|
83
|
+
invisible,
|
84
|
+
disabled,
|
85
|
+
placeholder,
|
86
|
+
modalPopover = true,
|
87
|
+
includeClear = true,
|
88
|
+
includeSelectAll = true,
|
89
|
+
useChips = true,
|
90
|
+
className,
|
91
|
+
children,
|
92
|
+
renderValues,
|
93
|
+
open,
|
94
|
+
onOpenChange,
|
95
|
+
},
|
96
|
+
ref
|
97
|
+
) => {
|
98
|
+
const [isPopoverOpen, setIsPopoverOpen] = React.useState(open ?? false);
|
99
|
+
const [selectedValues, setSelectedValues] = React.useState<string[]>(value ?? []);
|
44
100
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
onMultiValueChange,
|
49
|
-
size = "medium",
|
50
|
-
label,
|
51
|
-
disabled,
|
52
|
-
renderValue,
|
53
|
-
renderValues,
|
54
|
-
includeFocusOutline = true,
|
55
|
-
containerClassName,
|
56
|
-
className,
|
57
|
-
children,
|
58
|
-
error
|
59
|
-
}: MultiSelectProps) {
|
60
|
-
|
61
|
-
const containerRef = React.useRef<HTMLInputElement>(null);
|
62
|
-
const inputRef = React.useRef<HTMLInputElement>(null);
|
63
|
-
const listRef = React.useRef<HTMLDivElement>(null);
|
64
|
-
useOutsideAlerter(listRef, () => setOpenInternal(false));
|
65
|
-
|
66
|
-
const [openInternal, setOpenInternal] = React.useState(false);
|
67
|
-
useEffect(() => {
|
68
|
-
setOpenInternal(open ?? false);
|
69
|
-
}, [open]);
|
70
|
-
|
71
|
-
const onValueChangeInternal = React.useCallback((newValue: string) => {
|
72
|
-
if (Array.isArray(value) && value.includes(newValue)) {
|
73
|
-
onMultiValueChange?.(value.filter(v => v !== newValue));
|
74
|
-
} else {
|
75
|
-
onMultiValueChange?.([...(value ?? []), newValue]);
|
101
|
+
const onPopoverOpenChange = (open: boolean) => {
|
102
|
+
setIsPopoverOpen(open);
|
103
|
+
onOpenChange?.(open);
|
76
104
|
}
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
if (
|
87
|
-
|
88
|
-
newSelected.pop();
|
89
|
-
onMultiValueChange?.(newSelected);
|
105
|
+
|
106
|
+
useEffect(() => {
|
107
|
+
setIsPopoverOpen(open ?? false);
|
108
|
+
}, [open]);
|
109
|
+
|
110
|
+
const allValues = children
|
111
|
+
?
|
112
|
+
// @ts-ignore
|
113
|
+
Children.map(children, (child) => {
|
114
|
+
if (React.isValidElement(child)) {
|
115
|
+
return child.props.value;
|
90
116
|
}
|
117
|
+
return null;
|
118
|
+
}).filter(Boolean) as string[]
|
119
|
+
: [];
|
120
|
+
|
121
|
+
React.useEffect(() => {
|
122
|
+
setSelectedValues(value ?? []);
|
123
|
+
}, [value]);
|
124
|
+
|
125
|
+
function onItemClick(newValue: string) {
|
126
|
+
let newSelectedValues: string[];
|
127
|
+
if (selectedValues.includes(newValue)) {
|
128
|
+
newSelectedValues = selectedValues.filter((v) => v !== newValue);
|
129
|
+
} else {
|
130
|
+
newSelectedValues = [...selectedValues, newValue];
|
91
131
|
}
|
92
|
-
|
93
|
-
if (e.key === "Escape") {
|
94
|
-
input.blur();
|
95
|
-
setOpenInternal(false);
|
96
|
-
e.stopPropagation();
|
97
|
-
}
|
132
|
+
updateValues(newSelectedValues);
|
98
133
|
}
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
134
|
+
|
135
|
+
function updateValues(values: string[]) {
|
136
|
+
setSelectedValues(values);
|
137
|
+
onValueChange?.(values);
|
138
|
+
}
|
139
|
+
|
140
|
+
const handleInputKeyDown = (
|
141
|
+
event: React.KeyboardEvent<HTMLInputElement>
|
142
|
+
) => {
|
143
|
+
if (event.key === "Enter") {
|
144
|
+
onPopoverOpenChange(true);
|
145
|
+
} else if (event.key === "Backspace" && !event.currentTarget.value) {
|
146
|
+
const newSelectedValues = [...selectedValues];
|
147
|
+
newSelectedValues.pop();
|
148
|
+
updateValues(newSelectedValues);
|
149
|
+
}
|
150
|
+
};
|
151
|
+
|
152
|
+
const toggleOption = (value: string) => {
|
153
|
+
const newSelectedValues = selectedValues.includes(value)
|
154
|
+
? selectedValues.filter((v) => v !== value)
|
155
|
+
: [...selectedValues, value];
|
156
|
+
updateValues(newSelectedValues);
|
157
|
+
};
|
158
|
+
|
159
|
+
const handleClear = () => {
|
160
|
+
updateValues([]);
|
161
|
+
};
|
162
|
+
|
163
|
+
const handleTogglePopover = () => {
|
164
|
+
onPopoverOpenChange(!isPopoverOpen);
|
165
|
+
};
|
166
|
+
|
167
|
+
const toggleAll = () => {
|
168
|
+
if (selectedValues.length === allValues.length) {
|
169
|
+
handleClear();
|
170
|
+
} else {
|
171
|
+
updateValues(allValues);
|
172
|
+
}
|
173
|
+
onPopoverOpenChange(false);
|
174
|
+
};
|
175
|
+
|
176
|
+
useInjectStyles("MultiSelect", `
|
177
|
+
[cmdk-group] {
|
178
|
+
max-height: 45vh;
|
179
|
+
overflow-y: auto;
|
180
|
+
// width: 400px;
|
181
|
+
} `)
|
182
|
+
|
183
|
+
return (
|
184
|
+
<MultiSelectContext.Provider
|
185
|
+
value={{
|
186
|
+
fieldValue: selectedValues,
|
187
|
+
onItemClick
|
188
|
+
}}>
|
189
|
+
|
190
|
+
{typeof label === "string" ? <SelectInputLabel error={error}>{label}</SelectInputLabel> : label}
|
191
|
+
|
192
|
+
<PopoverPrimitive.Root
|
193
|
+
open={isPopoverOpen}
|
194
|
+
onOpenChange={onPopoverOpenChange}
|
195
|
+
modal={modalPopover}
|
133
196
|
>
|
134
|
-
<
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
<div
|
162
|
-
ref={listRef}
|
163
|
-
className={"z-50 absolute overflow-auto outline-none"}
|
164
|
-
style={{
|
165
|
-
pointerEvents: openInternal ? "auto" : "none",
|
166
|
-
top: (usedBoundingRect?.top ?? 0) + (usedBoundingRect?.height ?? 0),
|
167
|
-
left: usedBoundingRect?.left,
|
168
|
-
// right: boundingRect?.right,
|
169
|
-
width: usedBoundingRect?.width,
|
170
|
-
maxHeight: maxHeight,
|
171
|
-
|
172
|
-
}}>
|
173
|
-
|
174
|
-
<CommandPrimitive.Group
|
175
|
-
className="mt-2 text-slate-900 dark:text-white animate-in z-50 border border-slate-200 dark:border-slate-800 bg-white dark:bg-slate-800 p-2 rounded-lg shadow-lg flex flex-col outline-none w-full"
|
176
|
-
>
|
197
|
+
<PopoverPrimitive.Trigger asChild>
|
198
|
+
<button
|
199
|
+
ref={ref}
|
200
|
+
onClick={handleTogglePopover}
|
201
|
+
className={cls(
|
202
|
+
size === "small" ? "min-h-[42px]" : "min-h-[64px]",
|
203
|
+
"py-2",
|
204
|
+
"px-4",
|
205
|
+
"select-none rounded-md text-sm",
|
206
|
+
invisible ? fieldBackgroundInvisibleMixin : fieldBackgroundMixin,
|
207
|
+
disabled ? fieldBackgroundDisabledMixin : fieldBackgroundHoverMixin,
|
208
|
+
"relative flex items-center",
|
209
|
+
className
|
210
|
+
)}
|
211
|
+
>
|
212
|
+
{selectedValues.length > 0 ? (
|
213
|
+
<div className="flex justify-between items-center w-full">
|
214
|
+
<div className="flex flex-wrap items-center gap-1.5 text-start">
|
215
|
+
{renderValues && renderValues(selectedValues)}
|
216
|
+
{!renderValues && selectedValues.map((value) => {
|
217
|
+
|
218
|
+
// @ts-ignore
|
219
|
+
const childrenProps: MultiSelectItemProps[] = Children.map(children, (child) => {
|
220
|
+
if (React.isValidElement(child)) {
|
221
|
+
return child.props;
|
222
|
+
}
|
223
|
+
}).filter(Boolean);
|
177
224
|
|
225
|
+
const option = childrenProps.find((o) => o.value === value);
|
226
|
+
if (!useChips) {
|
227
|
+
return option?.children;
|
228
|
+
}
|
229
|
+
return (
|
230
|
+
<Chip
|
231
|
+
size={"medium"}
|
232
|
+
key={value}
|
233
|
+
className={cls("flex flex-row items-center p-1")}
|
234
|
+
>
|
235
|
+
{option?.children}
|
236
|
+
<CloseIcon
|
237
|
+
size={"smallest"}
|
238
|
+
onClick={(event) => {
|
239
|
+
event.stopPropagation();
|
240
|
+
toggleOption(value);
|
241
|
+
}}
|
242
|
+
/>
|
243
|
+
</Chip>
|
244
|
+
);
|
245
|
+
})}
|
246
|
+
</div>
|
247
|
+
<div className="flex items-center justify-between">
|
248
|
+
{includeClear && <CloseIcon
|
249
|
+
className={"ml-4"}
|
250
|
+
size={"small"}
|
251
|
+
onClick={(event) => {
|
252
|
+
event.stopPropagation();
|
253
|
+
handleClear();
|
254
|
+
}}
|
255
|
+
/>}
|
256
|
+
<div className={cls("px-2 h-full flex items-center")}>
|
257
|
+
<ExpandMoreIcon size={"small"}
|
258
|
+
className={cls("transition", isPopoverOpen ? "rotate-180" : "")}/>
|
259
|
+
</div>
|
260
|
+
</div>
|
261
|
+
</div>
|
262
|
+
) : (
|
263
|
+
<div className="flex items-center justify-between w-full mx-auto">
|
264
|
+
<span className="text-sm">
|
265
|
+
{placeholder}
|
266
|
+
</span>
|
267
|
+
<div className={cls("px-2 h-full flex items-center")}>
|
268
|
+
<ExpandMoreIcon size={"small"}
|
269
|
+
className={cls("transition", isPopoverOpen ? "rotate-180" : "")}/>
|
270
|
+
</div>
|
271
|
+
</div>
|
272
|
+
)}
|
273
|
+
</button>
|
274
|
+
</PopoverPrimitive.Trigger>
|
275
|
+
<PopoverPrimitive.Content
|
276
|
+
className={cls("z-50 relative overflow-hidden border bg-white dark:bg-surface-900 rounded-lg w-[400px]", defaultBorderMixin)}
|
277
|
+
align="start"
|
278
|
+
sideOffset={8}
|
279
|
+
onEscapeKeyDown={() => onPopoverOpenChange(false)}
|
280
|
+
>
|
281
|
+
<CommandPrimitive>
|
282
|
+
<div className={"flex flex-row items-center"}>
|
283
|
+
<CommandPrimitive.Input
|
284
|
+
className={cls(focusedDisabled, "bg-transparent outline-none flex-1 h-full w-full m-4 flex-grow ")}
|
285
|
+
placeholder="Search..."
|
286
|
+
onKeyDown={handleInputKeyDown}
|
287
|
+
/>
|
288
|
+
{selectedValues.length > 0 && (
|
289
|
+
<div
|
290
|
+
onClick={handleClear}
|
291
|
+
className="text-sm justify-center cursor-pointer py-3 px-4 text-text-secondary dark:text-text-secondary-dark">
|
292
|
+
Clear
|
293
|
+
</div>
|
294
|
+
)}
|
295
|
+
</div>
|
296
|
+
<Separator orientation={"horizontal"} className={"my-0"}/>
|
297
|
+
<CommandPrimitive.List>
|
298
|
+
<CommandPrimitive.Empty className={"px-4 py-2"}>
|
299
|
+
No results found.
|
300
|
+
</CommandPrimitive.Empty>
|
301
|
+
<CommandPrimitive.Group>
|
302
|
+
{includeSelectAll && <CommandPrimitive.Item
|
303
|
+
key="all"
|
304
|
+
onSelect={toggleAll}
|
305
|
+
className={
|
306
|
+
cls(
|
307
|
+
"flex flex-row items-center gap-1.5",
|
308
|
+
"cursor-pointer",
|
309
|
+
"m-1",
|
310
|
+
"ring-offset-transparent",
|
311
|
+
"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",
|
312
|
+
"aria-[selected=true]:bg-surface-accent-100 aria-[selected=true]:dark:bg-surface-accent-900",
|
313
|
+
"cursor-pointer p-2 rounded aria-[selected=true]:bg-surface-accent-100 aria-[selected=true]:dark:bg-surface-accent-900"
|
314
|
+
)
|
315
|
+
}
|
316
|
+
>
|
317
|
+
<InnerCheckBox checked={selectedValues.length === allValues.length}/>
|
318
|
+
<span className={"text-sm text-text-secondary dark:text-text-secondary-dark"}>(Select All)</span>
|
319
|
+
</CommandPrimitive.Item>}
|
178
320
|
{children}
|
179
321
|
</CommandPrimitive.Group>
|
180
322
|
|
181
|
-
</
|
182
|
-
</
|
183
|
-
</
|
184
|
-
</
|
185
|
-
</
|
323
|
+
</CommandPrimitive.List>
|
324
|
+
</CommandPrimitive>
|
325
|
+
</PopoverPrimitive.Content>
|
326
|
+
</PopoverPrimitive.Root>
|
327
|
+
</MultiSelectContext.Provider>
|
328
|
+
);
|
329
|
+
}
|
330
|
+
);
|
186
331
|
|
187
|
-
|
188
|
-
)
|
189
|
-
}
|
332
|
+
MultiSelect.displayName = "MultiSelect";
|
190
333
|
|
191
334
|
export interface MultiSelectItemProps {
|
192
335
|
value: string;
|
@@ -194,32 +337,63 @@ export interface MultiSelectItemProps {
|
|
194
337
|
className?: string;
|
195
338
|
}
|
196
339
|
|
197
|
-
export function MultiSelectItem({
|
340
|
+
export function MultiSelectItem({
|
341
|
+
children,
|
342
|
+
value,
|
343
|
+
className
|
344
|
+
}: MultiSelectItemProps) {
|
198
345
|
|
199
346
|
const context = React.useContext(MultiSelectContext);
|
200
347
|
if (!context) throw new Error("MultiSelectItem must be used inside a MultiSelect");
|
201
|
-
const {
|
348
|
+
const {
|
349
|
+
fieldValue,
|
350
|
+
onItemClick
|
351
|
+
} = context;
|
202
352
|
|
353
|
+
const isSelected = (fieldValue ?? []).includes(value);
|
203
354
|
return <CommandPrimitive.Item
|
355
|
+
// value={value}
|
204
356
|
onMouseDown={(e) => {
|
205
357
|
e.preventDefault();
|
206
358
|
e.stopPropagation();
|
207
359
|
}}
|
208
360
|
onSelect={(_) => {
|
209
|
-
|
210
|
-
onValueChangeInternal(value);
|
361
|
+
onItemClick(value);
|
211
362
|
}}
|
212
|
-
className={
|
213
|
-
|
363
|
+
className={cls(
|
364
|
+
"flex flex-row items-center gap-1.5",
|
365
|
+
isSelected ? "bg-surface-accent-200 dark:bg-surface-accent-950" : "",
|
214
366
|
"cursor-pointer",
|
215
367
|
"m-1",
|
216
368
|
"ring-offset-transparent",
|
217
|
-
"p-
|
218
|
-
"aria-[selected=true]:bg-
|
219
|
-
"cursor-pointer p-2 rounded aria-[selected=true]:bg-
|
369
|
+
"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",
|
370
|
+
"aria-[selected=true]:bg-surface-accent-100 aria-[selected=true]:dark:bg-surface-accent-900",
|
371
|
+
"cursor-pointer p-2 rounded aria-[selected=true]:bg-surface-accent-100 aria-[selected=true]:dark:bg-surface-accent-900",
|
220
372
|
className
|
221
373
|
)}
|
222
374
|
>
|
375
|
+
<InnerCheckBox checked={isSelected}/>
|
223
376
|
{children}
|
224
377
|
</CommandPrimitive.Item>;
|
378
|
+
|
225
379
|
}
|
380
|
+
|
381
|
+
function InnerCheckBox({ checked }: { checked: boolean }) {
|
382
|
+
return <div className={cls(
|
383
|
+
"p-2",
|
384
|
+
"w-8 h-8",
|
385
|
+
"inline-flex items-center justify-center text-sm font-medium focus:outline-none transition-colors ease-in-out duration-150",
|
386
|
+
)}>
|
387
|
+
<div
|
388
|
+
className={cls(
|
389
|
+
"border-2 relative transition-colors ease-in-out duration-150",
|
390
|
+
"w-4 h-4 rounded flex items-center justify-center",
|
391
|
+
(checked ? "bg-primary" : "bg-white dark:bg-surface-accent-900"),
|
392
|
+
(checked) ? "text-surface-accent-100 dark:text-surface-accent-900" : "",
|
393
|
+
(checked ? "border-transparent" : "border-surface-accent-800 dark:border-surface-accent-200")
|
394
|
+
)}>
|
395
|
+
{checked && <Icon iconKey={"check"} size={16} className={"absolute"}/>}
|
396
|
+
</div>
|
397
|
+
</div>
|
398
|
+
}
|
399
|
+
|
package/src/components/Paper.tsx
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import React from "react";
|
2
2
|
|
3
3
|
import { paperMixin } from "../styles";
|
4
|
-
import {
|
4
|
+
import { cls } from "../util";
|
5
5
|
|
6
6
|
export function Paper({
|
7
7
|
children,
|
@@ -15,7 +15,7 @@ export function Paper({
|
|
15
15
|
}) {
|
16
16
|
return (
|
17
17
|
<div
|
18
|
-
className={
|
18
|
+
className={cls(paperMixin, className)}
|
19
19
|
style={style}>
|
20
20
|
{children}
|
21
21
|
</div>
|
@@ -1,8 +1,10 @@
|
|
1
|
+
"use client";
|
1
2
|
import React from "react";
|
2
3
|
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
3
4
|
|
4
5
|
import { paperMixin } from "../styles";
|
5
|
-
import {
|
6
|
+
import { cls } from "../util";
|
7
|
+
import { useInjectStyles } from "../hooks";
|
6
8
|
|
7
9
|
export type PopoverSide = "top" | "right" | "bottom" | "left";
|
8
10
|
export type PopoverAlign = "start" | "center" | "end";
|
@@ -23,6 +25,7 @@ export interface PopoverProps {
|
|
23
25
|
enabled?: boolean;
|
24
26
|
modal?: boolean;
|
25
27
|
className?: string;
|
28
|
+
portalContainer?: HTMLElement | null;
|
26
29
|
}
|
27
30
|
|
28
31
|
export function Popover({
|
@@ -40,6 +43,7 @@ export function Popover({
|
|
40
43
|
avoidCollisions,
|
41
44
|
enabled = true,
|
42
45
|
modal = false,
|
46
|
+
portalContainer,
|
43
47
|
className
|
44
48
|
}: PopoverProps) {
|
45
49
|
|
@@ -50,25 +54,25 @@ export function Popover({
|
|
50
54
|
|
51
55
|
return <PopoverPrimitive.Root open={open}
|
52
56
|
onOpenChange={onOpenChange}
|
53
|
-
modal={modal}
|
54
|
-
>
|
57
|
+
modal={modal}>
|
55
58
|
<PopoverPrimitive.Trigger asChild>
|
56
59
|
{trigger}
|
57
60
|
</PopoverPrimitive.Trigger>
|
58
|
-
<PopoverPrimitive.Portal>
|
59
|
-
<PopoverPrimitive.Content
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
61
|
+
<PopoverPrimitive.Portal container={portalContainer}>
|
62
|
+
<PopoverPrimitive.Content
|
63
|
+
className={cls(paperMixin,
|
64
|
+
"PopoverContent z-40", className)}
|
65
|
+
side={side}
|
66
|
+
sideOffset={sideOffset}
|
67
|
+
align={align}
|
68
|
+
alignOffset={alignOffset}
|
69
|
+
arrowPadding={arrowPadding}
|
70
|
+
sticky={sticky}
|
71
|
+
hideWhenDetached={hideWhenDetached}
|
72
|
+
avoidCollisions={avoidCollisions}>
|
69
73
|
|
70
74
|
{children}
|
71
|
-
<PopoverPrimitive.Arrow className="fill-white dark:fill-
|
75
|
+
<PopoverPrimitive.Arrow className="fill-white dark:fill-surface-accent-950"/>
|
72
76
|
</PopoverPrimitive.Content>
|
73
77
|
</PopoverPrimitive.Portal>
|
74
78
|
</PopoverPrimitive.Root>;
|