@noya-app/noya-designsystem 0.1.44 → 0.1.45
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/.turbo/turbo-build.log +13 -10
- package/CHANGELOG.md +9 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +320 -78
- package/dist/index.d.ts +320 -78
- package/dist/index.js +2113 -1374
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2000 -1274
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/components/AnimatePresence.tsx +10 -1
- package/src/components/Avatar.tsx +2 -0
- package/src/components/Checkbox.tsx +6 -1
- package/src/components/Combobox.tsx +6 -4
- package/src/components/ComboboxMenu.tsx +30 -15
- package/src/components/CommandPalette.tsx +69 -0
- package/src/components/ContextMenu.tsx +33 -134
- package/src/components/DropdownMenu.tsx +47 -155
- package/src/components/Fade.tsx +62 -0
- package/src/components/InputField.tsx +109 -133
- package/src/components/Label.tsx +81 -7
- package/src/components/LabeledField.tsx +112 -0
- package/src/components/ListView.tsx +55 -52
- package/src/components/Popover.tsx +12 -1
- package/src/components/SearchCompletionMenu.tsx +206 -0
- package/src/components/SegmentedControl.tsx +5 -2
- package/src/components/SelectMenu.tsx +104 -124
- package/src/components/SidebarList.tsx +252 -0
- package/src/components/Slider.tsx +18 -26
- package/src/components/Text.tsx +1 -0
- package/src/components/TextArea.tsx +4 -1
- package/src/components/Toolbar.tsx +6 -2
- package/src/components/TreeView.tsx +3 -0
- package/src/components/WorkspaceLayout.tsx +38 -18
- package/src/components/internal/Menu.tsx +58 -10
- package/src/components/internal/MenuViewport.tsx +151 -0
- package/src/components/internal/SelectItem.tsx +111 -0
- package/src/hooks/useIndent.ts +12 -0
- package/src/hooks/useLabel.ts +51 -0
- package/src/hooks/usePreservePanelSize.tsx +50 -19
- package/src/index.tsx +12 -5
- package/src/utils/combobox.ts +4 -1
- package/src/utils/createSectionedMenu.ts +11 -8
- package/src/utils/selection.ts +36 -0
|
@@ -28,7 +28,7 @@ import { useHover } from "../hooks/useHover";
|
|
|
28
28
|
import { cssVars } from "../theme";
|
|
29
29
|
import { cx } from "../utils/classNames";
|
|
30
30
|
import { ContextMenu } from "./ContextMenu";
|
|
31
|
-
import { InputField
|
|
31
|
+
import { InputField } from "./InputField";
|
|
32
32
|
import { MenuItem } from "./internal/Menu";
|
|
33
33
|
import { ScrollArea } from "./ScrollArea";
|
|
34
34
|
import {
|
|
@@ -38,7 +38,6 @@ import {
|
|
|
38
38
|
Sortable,
|
|
39
39
|
} from "./Sortable";
|
|
40
40
|
import { Spacer } from "./Spacer";
|
|
41
|
-
import { labelTextStyles } from "./SelectMenu";
|
|
42
41
|
|
|
43
42
|
export type ListRowMarginType = "none" | "top" | "bottom" | "vertical";
|
|
44
43
|
export type ListRowPosition = "only" | "first" | "middle" | "last";
|
|
@@ -122,24 +121,12 @@ const ListViewRowTitle = ({
|
|
|
122
121
|
* EditableRowTitle
|
|
123
122
|
* ------------------------------------------------------------------------- */
|
|
124
123
|
|
|
125
|
-
const ListViewEditableRowTitleElement = forwardRef(
|
|
126
|
-
(
|
|
127
|
-
{ className, ...props }: InputFieldInputProps,
|
|
128
|
-
forwardedRef: ForwardedRef<HTMLInputElement>
|
|
129
|
-
) => (
|
|
130
|
-
<InputField.Input
|
|
131
|
-
ref={forwardedRef}
|
|
132
|
-
className={cx(`bg-listview-editing-background `, className)}
|
|
133
|
-
{...props}
|
|
134
|
-
/>
|
|
135
|
-
)
|
|
136
|
-
);
|
|
137
|
-
|
|
138
124
|
export interface EditableRowProps {
|
|
139
125
|
value: string;
|
|
140
126
|
onSubmitEditing: (value: string) => void;
|
|
141
127
|
autoFocus: boolean;
|
|
142
128
|
placeholder?: string;
|
|
129
|
+
className?: string;
|
|
143
130
|
}
|
|
144
131
|
|
|
145
132
|
function ListViewEditableRowTitle({
|
|
@@ -147,6 +134,7 @@ function ListViewEditableRowTitle({
|
|
|
147
134
|
onSubmitEditing,
|
|
148
135
|
autoFocus,
|
|
149
136
|
placeholder,
|
|
137
|
+
className,
|
|
150
138
|
}: EditableRowProps) {
|
|
151
139
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
152
140
|
|
|
@@ -165,24 +153,17 @@ function ListViewEditableRowTitle({
|
|
|
165
153
|
}, [autoFocus]);
|
|
166
154
|
|
|
167
155
|
return (
|
|
168
|
-
<
|
|
156
|
+
<InputField.Input
|
|
169
157
|
ref={inputRef}
|
|
170
|
-
variant="bare"
|
|
171
158
|
value={value}
|
|
172
159
|
placeholder={placeholder}
|
|
173
160
|
onSubmit={onSubmitEditing}
|
|
174
161
|
allowSubmittingWithSameValue
|
|
162
|
+
className={cx("-mx-1.5 -my-1 bg-listview-editing-background", className)}
|
|
175
163
|
/>
|
|
176
164
|
);
|
|
177
165
|
}
|
|
178
166
|
|
|
179
|
-
function getPositionMargin(marginType: ListRowMarginType) {
|
|
180
|
-
return {
|
|
181
|
-
top: marginType === "top" || marginType === "vertical" ? 8 : 0,
|
|
182
|
-
bottom: marginType === "bottom" || marginType === "vertical" ? 8 : 0,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
167
|
/* ----------------------------------------------------------------------------
|
|
187
168
|
* Row
|
|
188
169
|
* ------------------------------------------------------------------------- */
|
|
@@ -225,18 +206,24 @@ const RowContainer = forwardRef<
|
|
|
225
206
|
style,
|
|
226
207
|
$clickable,
|
|
227
208
|
"aria-describedby": _, // Causes hydration warning
|
|
209
|
+
role,
|
|
210
|
+
"aria-label": ariaLabel,
|
|
211
|
+
"aria-roledescription": ariaRoleDescription,
|
|
212
|
+
"aria-selected": ariaSelected,
|
|
228
213
|
...props
|
|
229
214
|
},
|
|
230
215
|
ref
|
|
231
216
|
) => {
|
|
232
|
-
const margin = getPositionMargin($marginType);
|
|
233
|
-
|
|
234
217
|
return (
|
|
235
218
|
<div
|
|
236
219
|
ref={ref}
|
|
220
|
+
role={role}
|
|
221
|
+
aria-label={ariaLabel}
|
|
222
|
+
aria-roledescription={ariaRoleDescription}
|
|
223
|
+
aria-selected={ariaSelected ?? $selected}
|
|
237
224
|
className={cx(
|
|
238
225
|
$isSectionHeader && $sectionHeaderVariant === "label"
|
|
239
|
-
?
|
|
226
|
+
? "font-sans text-label uppercase leading-3 font-bold text-text-muted"
|
|
240
227
|
: "font-sans text-heading5 font-normal",
|
|
241
228
|
!$isSectionHeader &&
|
|
242
229
|
$selected &&
|
|
@@ -254,22 +241,11 @@ const RowContainer = forwardRef<
|
|
|
254
241
|
paddingRight: $selected ? "8px" : "12px",
|
|
255
242
|
paddingBottom: $selected ? "4px" : "6px",
|
|
256
243
|
paddingLeft: $selected ? "8px" : "12px",
|
|
257
|
-
borderRadius:
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
$variant === "padded"
|
|
263
|
-
? `${margin.top}px`
|
|
264
|
-
: $selected
|
|
265
|
-
? "2px"
|
|
266
|
-
: "",
|
|
267
|
-
marginBottom:
|
|
268
|
-
$variant === "padded"
|
|
269
|
-
? `${margin.bottom}px`
|
|
270
|
-
: $selected
|
|
271
|
-
? "2px"
|
|
272
|
-
: "",
|
|
244
|
+
borderRadius: $selected ? "4px" : "",
|
|
245
|
+
marginLeft: $selected ? "4px" : "",
|
|
246
|
+
marginRight: $selected ? "4px" : "",
|
|
247
|
+
marginTop: $selected ? "2px" : "",
|
|
248
|
+
marginBottom: $selected ? "2px" : "",
|
|
273
249
|
}),
|
|
274
250
|
...($selected &&
|
|
275
251
|
!$isSectionHeader && {
|
|
@@ -351,6 +327,7 @@ const ListViewDragIndicatorElement = forwardRef<
|
|
|
351
327
|
zIndex: 1000,
|
|
352
328
|
position: "absolute",
|
|
353
329
|
borderRadius: "3px",
|
|
330
|
+
pointerEvents: "none",
|
|
354
331
|
...($relativeDropPosition === "inside"
|
|
355
332
|
? {
|
|
356
333
|
inset: 2,
|
|
@@ -396,6 +373,7 @@ interface ListViewClickInfo {
|
|
|
396
373
|
interface ListViewRowProps<MenuItemType extends string = string> {
|
|
397
374
|
id?: string;
|
|
398
375
|
tabIndex?: number;
|
|
376
|
+
role?: string;
|
|
399
377
|
selected?: boolean;
|
|
400
378
|
/** @default 0 */
|
|
401
379
|
depth?: number;
|
|
@@ -432,6 +410,7 @@ const ListViewRow = forwardRefGeneric(function ListViewRow<
|
|
|
432
410
|
{
|
|
433
411
|
id,
|
|
434
412
|
tabIndex = 0,
|
|
413
|
+
role,
|
|
435
414
|
gap,
|
|
436
415
|
backgroundColor,
|
|
437
416
|
selected = false,
|
|
@@ -526,6 +505,7 @@ const ListViewRow = forwardRefGeneric(function ListViewRow<
|
|
|
526
505
|
onContextMenu={onContextMenu}
|
|
527
506
|
$isSectionHeader={isSectionHeader}
|
|
528
507
|
id={id}
|
|
508
|
+
role={role}
|
|
529
509
|
$gap={gap ?? 0}
|
|
530
510
|
$backgroundColor={backgroundColor}
|
|
531
511
|
{...hoverProps}
|
|
@@ -764,14 +744,18 @@ type RenderProps<T> = {
|
|
|
764
744
|
virtualized?: Size;
|
|
765
745
|
};
|
|
766
746
|
|
|
767
|
-
type ListViewVariant = "normal" | "
|
|
747
|
+
type ListViewVariant = "normal" | "bare";
|
|
768
748
|
|
|
769
749
|
type ListViewSectionHeaderVariant = "normal" | "label";
|
|
770
750
|
|
|
771
|
-
type ListViewRootProps = {
|
|
751
|
+
export type ListViewRootProps = {
|
|
772
752
|
id?: string;
|
|
773
753
|
className?: string;
|
|
774
754
|
style?: CSSProperties;
|
|
755
|
+
role?: string;
|
|
756
|
+
"aria-labelledby"?: string;
|
|
757
|
+
"aria-orientation"?: "vertical" | "horizontal";
|
|
758
|
+
"aria-multiselectable"?: boolean;
|
|
775
759
|
onPress?: () => void;
|
|
776
760
|
scrollable?: boolean;
|
|
777
761
|
expandable?: boolean;
|
|
@@ -788,6 +772,11 @@ type ListViewRootProps = {
|
|
|
788
772
|
divider?: boolean;
|
|
789
773
|
gap?: number;
|
|
790
774
|
colorScheme?: ListColorScheme;
|
|
775
|
+
containerRef?: React.RefObject<HTMLDivElement>;
|
|
776
|
+
onDragOver?: React.DragEventHandler<HTMLDivElement>;
|
|
777
|
+
onDragEnter?: React.DragEventHandler<HTMLDivElement>;
|
|
778
|
+
onDragLeave?: React.DragEventHandler<HTMLDivElement>;
|
|
779
|
+
onDrop?: React.DragEventHandler<HTMLDivElement>;
|
|
791
780
|
};
|
|
792
781
|
|
|
793
782
|
const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
@@ -795,6 +784,10 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
795
784
|
id,
|
|
796
785
|
className,
|
|
797
786
|
style,
|
|
787
|
+
role,
|
|
788
|
+
"aria-labelledby": ariaLabelledby,
|
|
789
|
+
"aria-orientation": ariaOrientation,
|
|
790
|
+
"aria-multiselectable": ariaMultiselectable,
|
|
798
791
|
onPress,
|
|
799
792
|
scrollable = false,
|
|
800
793
|
expandable = true,
|
|
@@ -812,6 +805,11 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
812
805
|
pressEventName = "onClick",
|
|
813
806
|
colorScheme = "primary",
|
|
814
807
|
gap = 0,
|
|
808
|
+
containerRef,
|
|
809
|
+
onDragOver,
|
|
810
|
+
onDragEnter,
|
|
811
|
+
onDragLeave,
|
|
812
|
+
onDrop,
|
|
815
813
|
}: RenderProps<T> & ListViewRootProps,
|
|
816
814
|
forwardedRef: ForwardedRef<IVirtualizedList>
|
|
817
815
|
) {
|
|
@@ -1002,17 +1000,13 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
1002
1000
|
const getItemHeight = useCallback(
|
|
1003
1001
|
(index: number) => {
|
|
1004
1002
|
const child = getItemContextValue(index);
|
|
1005
|
-
const margin = child?.marginType
|
|
1006
|
-
? getPositionMargin(child.marginType)
|
|
1007
|
-
: { top: 0, bottom: 0 };
|
|
1008
1003
|
const height =
|
|
1009
|
-
|
|
1004
|
+
child?.isSectionHeader && child.sectionHeaderVariant === "label"
|
|
1010
1005
|
? SECTION_HEADER_LABEL_HEIGHT
|
|
1011
|
-
: ROW_HEIGHT
|
|
1012
|
-
(variant === "padded" ? margin.top + margin.bottom : 0);
|
|
1006
|
+
: ROW_HEIGHT;
|
|
1013
1007
|
return height;
|
|
1014
1008
|
},
|
|
1015
|
-
[getItemContextValue
|
|
1009
|
+
[getItemContextValue]
|
|
1016
1010
|
);
|
|
1017
1011
|
|
|
1018
1012
|
const getKey = useCallback(
|
|
@@ -1027,6 +1021,10 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
1027
1021
|
<ListViewDraggingContext.Provider value={draggingContextValue}>
|
|
1028
1022
|
<div
|
|
1029
1023
|
id={id}
|
|
1024
|
+
role={role}
|
|
1025
|
+
aria-labelledby={ariaLabelledby}
|
|
1026
|
+
aria-orientation={ariaOrientation}
|
|
1027
|
+
aria-multiselectable={ariaMultiselectable}
|
|
1030
1028
|
className={cx(
|
|
1031
1029
|
`flex flex-col text-text-muted ${scrollable ? "flex-1" : "flex-none"} `,
|
|
1032
1030
|
className
|
|
@@ -1035,6 +1033,11 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
1035
1033
|
{...{
|
|
1036
1034
|
[pressEventName]: handleClick,
|
|
1037
1035
|
}}
|
|
1036
|
+
ref={containerRef}
|
|
1037
|
+
onDragOver={onDragOver}
|
|
1038
|
+
onDragEnter={onDragEnter}
|
|
1039
|
+
onDragLeave={onDragLeave}
|
|
1040
|
+
onDrop={onDrop}
|
|
1038
1041
|
>
|
|
1039
1042
|
{withScrollable((scrollElementRef: HTMLDivElement | null) =>
|
|
1040
1043
|
withSortable(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
2
2
|
import React, { ComponentProps } from "react";
|
|
3
|
+
import { cx } from "../utils/classNames";
|
|
3
4
|
import { IconButton } from "./IconButton";
|
|
4
5
|
|
|
5
6
|
const closeStyles: React.CSSProperties = {
|
|
@@ -28,6 +29,7 @@ interface Props
|
|
|
28
29
|
sideOffset?: number;
|
|
29
30
|
onClickClose?: () => void;
|
|
30
31
|
showArrow?: boolean;
|
|
32
|
+
className?: string;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
export function Popover({
|
|
@@ -46,13 +48,22 @@ export function Popover({
|
|
|
46
48
|
onInteractOutside,
|
|
47
49
|
onFocusOutside,
|
|
48
50
|
onClickClose,
|
|
51
|
+
className,
|
|
49
52
|
}: Props) {
|
|
50
53
|
return (
|
|
51
54
|
<PopoverPrimitive.Root open={open} onOpenChange={onOpenChange}>
|
|
52
55
|
<PopoverPrimitive.Trigger asChild>{trigger}</PopoverPrimitive.Trigger>
|
|
53
56
|
<PopoverPrimitive.Portal>
|
|
54
57
|
<PopoverPrimitive.Content
|
|
55
|
-
className={
|
|
58
|
+
className={cx(
|
|
59
|
+
"rounded font-[14px] bg-popover-background shadow-[0_2px_4px_rgba(0,0,0,0.2),0_0_12px_rgba(0,0,0,0.1)] max-h-[600px] overflow-y-auto text-text-muted z-[1000]",
|
|
60
|
+
variant === "large"
|
|
61
|
+
? "w-[680px]"
|
|
62
|
+
: variant === "normal"
|
|
63
|
+
? "w-[240px]"
|
|
64
|
+
: "",
|
|
65
|
+
className
|
|
66
|
+
)}
|
|
56
67
|
side={side}
|
|
57
68
|
align="center"
|
|
58
69
|
sideOffset={sideOffset}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ComboboxMenu,
|
|
3
|
+
ComboboxOption,
|
|
4
|
+
Divider,
|
|
5
|
+
IScoredItem,
|
|
6
|
+
InputField,
|
|
7
|
+
ListView,
|
|
8
|
+
Small,
|
|
9
|
+
fuzzyFilter,
|
|
10
|
+
getNextIndex,
|
|
11
|
+
} from "@noya-app/noya-designsystem";
|
|
12
|
+
import { getCurrentPlatform, handleKeyboardEvent } from "@noya-app/noya-keymap";
|
|
13
|
+
import React, {
|
|
14
|
+
forwardRef,
|
|
15
|
+
useCallback,
|
|
16
|
+
useEffect,
|
|
17
|
+
useImperativeHandle,
|
|
18
|
+
useMemo,
|
|
19
|
+
useRef,
|
|
20
|
+
useState,
|
|
21
|
+
} from "react";
|
|
22
|
+
|
|
23
|
+
export interface ISearchCompletionMenu {
|
|
24
|
+
focus: () => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const SearchCompletionMenu = forwardRef(function SearchCompletionMenu(
|
|
28
|
+
{
|
|
29
|
+
onSelect,
|
|
30
|
+
onHover,
|
|
31
|
+
onClose,
|
|
32
|
+
items,
|
|
33
|
+
width = 200,
|
|
34
|
+
}: {
|
|
35
|
+
onSelect: (item: ComboboxOption) => void;
|
|
36
|
+
onHover?: (item: ComboboxOption) => void;
|
|
37
|
+
onClose: () => void;
|
|
38
|
+
items: ComboboxOption[];
|
|
39
|
+
width?: number;
|
|
40
|
+
},
|
|
41
|
+
forwardedRef: React.ForwardedRef<ISearchCompletionMenu>
|
|
42
|
+
) {
|
|
43
|
+
const [state, setState] = useState({
|
|
44
|
+
search: "",
|
|
45
|
+
index: 0,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const { index, search } = state;
|
|
49
|
+
|
|
50
|
+
const listRef = React.useRef<ListView.VirtualizedList>(null);
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
listRef.current?.scrollToIndex(index);
|
|
54
|
+
}, [index]);
|
|
55
|
+
|
|
56
|
+
const results = useMemo(
|
|
57
|
+
() =>
|
|
58
|
+
fuzzyFilter({
|
|
59
|
+
items: items.map((item) => item.name),
|
|
60
|
+
query: search,
|
|
61
|
+
}).map((item) => ({
|
|
62
|
+
...item,
|
|
63
|
+
...items[item.index],
|
|
64
|
+
})) as (ComboboxOption & IScoredItem)[],
|
|
65
|
+
[items, search]
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const setSearch = useCallback((search: string) => {
|
|
69
|
+
setState((state) => ({ ...state, search, index: 0 }));
|
|
70
|
+
}, []);
|
|
71
|
+
|
|
72
|
+
const setIndex = useCallback((index: number) => {
|
|
73
|
+
setState((state) => ({ ...state, index }));
|
|
74
|
+
}, []);
|
|
75
|
+
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
onHover?.(results[index]!);
|
|
78
|
+
}, [index, onHover, results]);
|
|
79
|
+
|
|
80
|
+
const searchHeight = 31;
|
|
81
|
+
|
|
82
|
+
const listSize =
|
|
83
|
+
results.length > 0
|
|
84
|
+
? {
|
|
85
|
+
width,
|
|
86
|
+
height: Math.min(results.length * 31, 31 * 6.5),
|
|
87
|
+
}
|
|
88
|
+
: {
|
|
89
|
+
width,
|
|
90
|
+
height: 60,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const elementSize = {
|
|
94
|
+
width: listSize.width,
|
|
95
|
+
height: searchHeight + listSize.height,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const selectAndClose = useCallback(
|
|
99
|
+
(item: ComboboxOption) => {
|
|
100
|
+
onSelect(item);
|
|
101
|
+
onClose();
|
|
102
|
+
},
|
|
103
|
+
[onSelect, onClose]
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const handleKeyDown = useCallback(
|
|
107
|
+
(event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
108
|
+
handleKeyboardEvent(event.nativeEvent, getCurrentPlatform(navigator), {
|
|
109
|
+
ArrowUp: () => {
|
|
110
|
+
const nextIndex = getNextIndex(
|
|
111
|
+
results,
|
|
112
|
+
index,
|
|
113
|
+
"previous",
|
|
114
|
+
(item) => item.disabled ?? false
|
|
115
|
+
);
|
|
116
|
+
setIndex(nextIndex);
|
|
117
|
+
},
|
|
118
|
+
ArrowDown: () => {
|
|
119
|
+
const nextIndex = getNextIndex(
|
|
120
|
+
results,
|
|
121
|
+
index,
|
|
122
|
+
"next",
|
|
123
|
+
(item) => item.disabled ?? false
|
|
124
|
+
);
|
|
125
|
+
setIndex(nextIndex);
|
|
126
|
+
},
|
|
127
|
+
Tab: () => selectAndClose(results[index]!),
|
|
128
|
+
Enter: () => selectAndClose(results[index]!),
|
|
129
|
+
Escape: () => onClose(),
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
[index, onClose, selectAndClose, results, setIndex]
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
136
|
+
|
|
137
|
+
useImperativeHandle(forwardedRef, () => ({
|
|
138
|
+
focus: () => {
|
|
139
|
+
inputRef.current?.focus();
|
|
140
|
+
},
|
|
141
|
+
}));
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<div {...elementSize} className="flex flex-col overflow-hidden">
|
|
145
|
+
<InputField.Root className="flex flex-0">
|
|
146
|
+
<InputField.Input
|
|
147
|
+
ref={inputRef}
|
|
148
|
+
value={search}
|
|
149
|
+
placeholder="Filter..."
|
|
150
|
+
style={{
|
|
151
|
+
height: searchHeight,
|
|
152
|
+
borderRadius: 0,
|
|
153
|
+
outline: "none",
|
|
154
|
+
boxShadow: "none",
|
|
155
|
+
backgroundColor: "white",
|
|
156
|
+
padding: "4px 12px",
|
|
157
|
+
}}
|
|
158
|
+
onChange={setSearch}
|
|
159
|
+
onKeyDown={handleKeyDown}
|
|
160
|
+
// onBlur={(event) => {
|
|
161
|
+
// const isWithinPopover =
|
|
162
|
+
// event.relatedTarget &&
|
|
163
|
+
// event.relatedTarget instanceof HTMLElement &&
|
|
164
|
+
// event.relatedTarget.role === 'dialog';
|
|
165
|
+
|
|
166
|
+
// if (isWithinPopover) {
|
|
167
|
+
// inputRef.current?.focus();
|
|
168
|
+
|
|
169
|
+
// event.stopPropagation();
|
|
170
|
+
// event.preventDefault();
|
|
171
|
+
// }
|
|
172
|
+
// }}
|
|
173
|
+
// onPointerDown={(event) => {
|
|
174
|
+
// event.stopPropagation();
|
|
175
|
+
// event.preventDefault();
|
|
176
|
+
// }}
|
|
177
|
+
// onFocusCapture={(event) => {
|
|
178
|
+
// event.stopPropagation();
|
|
179
|
+
// event.preventDefault();
|
|
180
|
+
// }}
|
|
181
|
+
/>
|
|
182
|
+
</InputField.Root>
|
|
183
|
+
<Divider />
|
|
184
|
+
{results.length > 0 ? (
|
|
185
|
+
<ComboboxMenu
|
|
186
|
+
ref={listRef}
|
|
187
|
+
listSize={listSize}
|
|
188
|
+
items={results}
|
|
189
|
+
selectedIndex={index}
|
|
190
|
+
onSelectItem={(item) => {
|
|
191
|
+
if (item.type === "sectionHeader") return;
|
|
192
|
+
selectAndClose(item);
|
|
193
|
+
}}
|
|
194
|
+
onHoverIndex={setIndex}
|
|
195
|
+
/>
|
|
196
|
+
) : (
|
|
197
|
+
<div
|
|
198
|
+
{...listSize}
|
|
199
|
+
className="flex flex-col p-20 items-center justify-center"
|
|
200
|
+
>
|
|
201
|
+
<Small color="textDisabled">No results</Small>
|
|
202
|
+
</div>
|
|
203
|
+
)}
|
|
204
|
+
</div>
|
|
205
|
+
);
|
|
206
|
+
});
|
|
@@ -8,6 +8,7 @@ import React, {
|
|
|
8
8
|
useContext,
|
|
9
9
|
useMemo,
|
|
10
10
|
} from "react";
|
|
11
|
+
import { useLabel } from "../hooks/useLabel";
|
|
11
12
|
import { cx } from "../utils/classNames";
|
|
12
13
|
import { renderIcon } from "./Icons";
|
|
13
14
|
import { RegularMenuItem } from "./internal/Menu";
|
|
@@ -77,6 +78,7 @@ const SegmentedControlItem = forwardRef(function SegmentedControlItem<
|
|
|
77
78
|
: "aria-checked:bg-background aria-checked:text-text",
|
|
78
79
|
"focus:z-interactable",
|
|
79
80
|
"aria-checked:shadow-[0_1px_1px_rgba(0,0,0,0.1)]",
|
|
81
|
+
"whitespace-pre",
|
|
80
82
|
className
|
|
81
83
|
)}
|
|
82
84
|
{...props}
|
|
@@ -105,7 +107,7 @@ const Separator = ({ transparent }: { transparent: boolean }) => (
|
|
|
105
107
|
export const SegmentedControl = memo(function SegmentedControl<
|
|
106
108
|
T extends string,
|
|
107
109
|
>({
|
|
108
|
-
id,
|
|
110
|
+
id: idProp,
|
|
109
111
|
value,
|
|
110
112
|
onValueChange,
|
|
111
113
|
colorScheme,
|
|
@@ -116,6 +118,7 @@ export const SegmentedControl = memo(function SegmentedControl<
|
|
|
116
118
|
style,
|
|
117
119
|
}: SegmentedControlProps<T>) {
|
|
118
120
|
const contextValue = useMemo(() => ({ colorScheme }), [colorScheme]);
|
|
121
|
+
const { fieldId: id } = useLabel({ fieldId: idProp });
|
|
119
122
|
|
|
120
123
|
const handleValueChange = useCallback(
|
|
121
124
|
(value: T) => {
|
|
@@ -135,7 +138,7 @@ export const SegmentedControl = memo(function SegmentedControl<
|
|
|
135
138
|
value={value}
|
|
136
139
|
onValueChange={handleValueChange}
|
|
137
140
|
className={cx(
|
|
138
|
-
`flex items-stretch
|
|
141
|
+
`flex items-stretch flex-auto appearance-none relative outline-none min-h-[27px] rounded bg-input-background py-[2px] px-[2px]`,
|
|
139
142
|
className
|
|
140
143
|
)}
|
|
141
144
|
style={style}
|