@noya-app/noya-designsystem 0.1.44 → 0.1.46
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 +18 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +1194 -878
- package/dist/index.d.ts +1194 -878
- package/dist/index.js +11432 -10219
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +11556 -10360
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/combobox.test.ts +137 -89
- package/src/components/AnimatePresence.tsx +10 -1
- package/src/components/Avatar.tsx +2 -0
- package/src/components/Breadcrumbs.tsx +29 -0
- package/src/components/Checkbox.tsx +6 -1
- package/src/components/Collection.tsx +74 -0
- package/src/components/Combobox.tsx +75 -56
- package/src/components/ComboboxMenu.tsx +61 -28
- package/src/components/CommandPalette.tsx +69 -0
- package/src/components/ContextMenu.tsx +33 -134
- package/src/components/DropdownMenu.tsx +46 -155
- package/src/components/EditableText.tsx +203 -0
- package/src/components/Fade.tsx +62 -0
- package/src/components/Grid.tsx +243 -0
- package/src/components/GridView.tsx +86 -96
- package/src/components/InputField.tsx +109 -133
- package/src/components/Label.tsx +81 -7
- package/src/components/LabeledField.tsx +112 -0
- package/src/components/List.tsx +268 -0
- package/src/components/ListView.tsx +65 -61
- package/src/components/Popover.tsx +12 -1
- package/src/components/SearchCompletionMenu.tsx +210 -0
- package/src/components/SegmentedControl.tsx +12 -6
- package/src/components/SelectMenu.tsx +110 -126
- 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 +9 -4
- package/src/components/TreeView.tsx +4 -0
- package/src/components/WorkspaceLayout.tsx +64 -20
- package/src/components/internal/Menu.tsx +107 -18
- package/src/components/internal/MenuViewport.tsx +152 -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 +15 -6
- package/src/utils/combobox.ts +116 -101
- package/src/utils/createSectionedMenu.ts +11 -14
- package/src/utils/fuzzyScorer.ts +11 -8
- package/src/utils/selection.ts +48 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import {
|
|
2
|
+
forwardRefGeneric,
|
|
3
|
+
memoGeneric,
|
|
4
|
+
useFileDropTarget,
|
|
5
|
+
} from "@noya-app/react-utils";
|
|
6
|
+
import React, { useImperativeHandle, useRef, useState } from "react";
|
|
7
|
+
import { cssVars } from "../theme";
|
|
8
|
+
import { cx } from "../utils/classNames";
|
|
9
|
+
import { updateSelection } from "../utils/selection";
|
|
10
|
+
import { CollectionProps, CollectionRef } from "./Collection";
|
|
11
|
+
import { ListView } from "./ListView";
|
|
12
|
+
import { TreeView } from "./TreeView";
|
|
13
|
+
|
|
14
|
+
const cssVarOverrides = {
|
|
15
|
+
"--icon-selected": cssVars.colors.primary,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const emptyArray: string[] = [];
|
|
19
|
+
|
|
20
|
+
export const List = memoGeneric(
|
|
21
|
+
forwardRefGeneric(function List<T, M extends string = string>(
|
|
22
|
+
props: CollectionProps<T, M> & { ref?: React.ForwardedRef<CollectionRef> },
|
|
23
|
+
ref: React.ForwardedRef<CollectionRef>
|
|
24
|
+
) {
|
|
25
|
+
const {
|
|
26
|
+
className,
|
|
27
|
+
items,
|
|
28
|
+
getId,
|
|
29
|
+
getName,
|
|
30
|
+
expandable,
|
|
31
|
+
getExpanded,
|
|
32
|
+
getRenamable,
|
|
33
|
+
getDepth,
|
|
34
|
+
setExpanded,
|
|
35
|
+
menuItems,
|
|
36
|
+
onSelectMenuItem,
|
|
37
|
+
onRename,
|
|
38
|
+
renderThumbnail,
|
|
39
|
+
renderAction,
|
|
40
|
+
renderDetail,
|
|
41
|
+
onSelectionChange,
|
|
42
|
+
itemRoleDescription = "clickable item",
|
|
43
|
+
detailPosition = "end",
|
|
44
|
+
size = "medium",
|
|
45
|
+
testHoveredId,
|
|
46
|
+
testRenamingId,
|
|
47
|
+
scrollable = false,
|
|
48
|
+
acceptsDrop,
|
|
49
|
+
onMoveItem,
|
|
50
|
+
onFilesDrop,
|
|
51
|
+
onDoubleClickItem,
|
|
52
|
+
renamable,
|
|
53
|
+
selectedIds = emptyArray,
|
|
54
|
+
} = props;
|
|
55
|
+
|
|
56
|
+
const [internalHoveredId, setHoveredId] = useState<string>();
|
|
57
|
+
const [internalRenamingId, setRenamingId] = useState<string>();
|
|
58
|
+
|
|
59
|
+
const hoveredId = internalHoveredId ?? testHoveredId;
|
|
60
|
+
const renamingId = testRenamingId ?? internalRenamingId;
|
|
61
|
+
|
|
62
|
+
const handleSelect = (itemId: string, event?: ListView.ClickInfo) => {
|
|
63
|
+
if (!onSelectionChange) return;
|
|
64
|
+
|
|
65
|
+
setRenamingId(undefined);
|
|
66
|
+
const newSelectedIds = updateSelection(
|
|
67
|
+
items.map(getId),
|
|
68
|
+
selectedIds,
|
|
69
|
+
itemId,
|
|
70
|
+
event
|
|
71
|
+
);
|
|
72
|
+
onSelectionChange(newSelectedIds, event);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const handleMenuAction = (action: M) => {
|
|
76
|
+
if (!onSelectMenuItem) return;
|
|
77
|
+
|
|
78
|
+
const selectedItems = items.filter((item) =>
|
|
79
|
+
selectedIds.includes(getId(item))
|
|
80
|
+
);
|
|
81
|
+
onSelectMenuItem(action, selectedItems);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const ViewComponent = expandable ? TreeView : ListView;
|
|
85
|
+
|
|
86
|
+
const fileDropTargetRef = useRef<HTMLDivElement>(null);
|
|
87
|
+
const { isDropTargetActive, dropTargetProps } = useFileDropTarget(
|
|
88
|
+
fileDropTargetRef,
|
|
89
|
+
onFilesDrop
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
useImperativeHandle(ref, () => ({
|
|
93
|
+
editName: (id: string) => {
|
|
94
|
+
setRenamingId(id);
|
|
95
|
+
},
|
|
96
|
+
}));
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<ViewComponent.Root
|
|
100
|
+
variant="bare"
|
|
101
|
+
className={cx(className, isDropTargetActive && "bg-primary-pastel")}
|
|
102
|
+
containerRef={fileDropTargetRef}
|
|
103
|
+
scrollable={scrollable}
|
|
104
|
+
data={items}
|
|
105
|
+
keyExtractor={getId}
|
|
106
|
+
role="listbox"
|
|
107
|
+
aria-orientation="vertical"
|
|
108
|
+
aria-multiselectable={true}
|
|
109
|
+
expandable={expandable}
|
|
110
|
+
sortable={expandable}
|
|
111
|
+
acceptsDrop={acceptsDrop}
|
|
112
|
+
onMoveItem={onMoveItem}
|
|
113
|
+
{...dropTargetProps}
|
|
114
|
+
renderItem={(
|
|
115
|
+
item: T,
|
|
116
|
+
_: number,
|
|
117
|
+
{ isDragging }: { isDragging: boolean }
|
|
118
|
+
) => {
|
|
119
|
+
const id = getId(item);
|
|
120
|
+
const isHovered = hoveredId === id && !isDragging;
|
|
121
|
+
const expanded = getExpanded?.(item);
|
|
122
|
+
const itemIsRenamable = getRenamable?.(item) ?? renamable;
|
|
123
|
+
const isSelected = selectedIds.includes(id);
|
|
124
|
+
const isRenaming = itemIsRenamable && onRename && renamingId === id;
|
|
125
|
+
const isTestingRenaming = testRenamingId === id;
|
|
126
|
+
|
|
127
|
+
const thumbnail = renderThumbnail?.(item, isSelected);
|
|
128
|
+
const action =
|
|
129
|
+
(isHovered || isSelected) &&
|
|
130
|
+
!isRenaming &&
|
|
131
|
+
renderAction?.(item, isSelected);
|
|
132
|
+
const end =
|
|
133
|
+
action ||
|
|
134
|
+
(detailPosition === "end" && renderDetail?.(item, isSelected));
|
|
135
|
+
const below =
|
|
136
|
+
detailPosition === "below" && renderDetail?.(item, isSelected);
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<ViewComponent.Row
|
|
140
|
+
id={id}
|
|
141
|
+
key={id}
|
|
142
|
+
role="option"
|
|
143
|
+
sortable={expandable}
|
|
144
|
+
aria-roledescription={itemRoleDescription}
|
|
145
|
+
aria-label={getName(item)}
|
|
146
|
+
aria-selected={isSelected}
|
|
147
|
+
className={cx("cursor-pointer p-1 rounded")}
|
|
148
|
+
backgroundColor={
|
|
149
|
+
isSelected ? cssVars.colors.primaryPastel : undefined
|
|
150
|
+
}
|
|
151
|
+
chevronClassName={cx(expandable && "relative left-2")}
|
|
152
|
+
style={cssVarOverrides as React.CSSProperties}
|
|
153
|
+
hovered={isHovered}
|
|
154
|
+
selected={isSelected}
|
|
155
|
+
expanded={expanded}
|
|
156
|
+
depth={getDepth?.(item)}
|
|
157
|
+
onHoverChange={(hovered: boolean) =>
|
|
158
|
+
setHoveredId(hovered ? id : undefined)
|
|
159
|
+
}
|
|
160
|
+
onPress={(e: ListView.ClickInfo) => {
|
|
161
|
+
handleSelect(id, e);
|
|
162
|
+
}}
|
|
163
|
+
onDoubleClick={() => {
|
|
164
|
+
onDoubleClickItem?.(id);
|
|
165
|
+
}}
|
|
166
|
+
onKeyDown={(e: React.KeyboardEvent) => {
|
|
167
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
168
|
+
e.preventDefault();
|
|
169
|
+
handleSelect(id, e);
|
|
170
|
+
if (e.key === "Enter") {
|
|
171
|
+
setRenamingId(id);
|
|
172
|
+
}
|
|
173
|
+
}}
|
|
174
|
+
menuItems={menuItems}
|
|
175
|
+
onSelectMenuItem={handleMenuAction}
|
|
176
|
+
onContextMenu={() => {
|
|
177
|
+
if (!isSelected) {
|
|
178
|
+
handleSelect(id, {
|
|
179
|
+
shiftKey: false,
|
|
180
|
+
altKey: false,
|
|
181
|
+
metaKey: false,
|
|
182
|
+
ctrlKey: false,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}}
|
|
186
|
+
tabIndex={0}
|
|
187
|
+
onClickChevron={() => {
|
|
188
|
+
if (!expandable) return;
|
|
189
|
+
setExpanded?.(item, !expanded);
|
|
190
|
+
}}
|
|
191
|
+
>
|
|
192
|
+
<div className="flex flex-1 items-center px-2 gap-3">
|
|
193
|
+
{thumbnail && (
|
|
194
|
+
<div
|
|
195
|
+
className={cx(
|
|
196
|
+
"rounded overflow-hidden flex-none flex items-center justify-center",
|
|
197
|
+
size === "medium" ? "w-8 h-8" : "w-16 h-16",
|
|
198
|
+
isSelected && "text-primary"
|
|
199
|
+
)}
|
|
200
|
+
style={{
|
|
201
|
+
backgroundColor: isSelected
|
|
202
|
+
? cssVars.colors.primaryPastel
|
|
203
|
+
: cssVars.colors.inputBackground,
|
|
204
|
+
}}
|
|
205
|
+
tabIndex={-1}
|
|
206
|
+
>
|
|
207
|
+
{thumbnail}
|
|
208
|
+
</div>
|
|
209
|
+
)}
|
|
210
|
+
<div className="flex flex-col flex-1 w-0">
|
|
211
|
+
<div className="flex items-center min-h-[27px] flex-1 gap-2">
|
|
212
|
+
<div className="flex-1 w-0 flex items-center" tabIndex={-1}>
|
|
213
|
+
{isRenaming ? (
|
|
214
|
+
<ViewComponent.EditableRowTitle
|
|
215
|
+
value={getName(item)}
|
|
216
|
+
placeholder="Enter name"
|
|
217
|
+
className="font-medium"
|
|
218
|
+
autoFocus={!isTestingRenaming}
|
|
219
|
+
onSubmitEditing={(value: string) => {
|
|
220
|
+
if (value !== getName(item)) {
|
|
221
|
+
onRename?.(item, value);
|
|
222
|
+
}
|
|
223
|
+
setRenamingId(undefined);
|
|
224
|
+
}}
|
|
225
|
+
/>
|
|
226
|
+
) : (
|
|
227
|
+
<ViewComponent.RowTitle
|
|
228
|
+
className={cx(
|
|
229
|
+
"font-medium",
|
|
230
|
+
isSelected && "text-primary"
|
|
231
|
+
)}
|
|
232
|
+
>
|
|
233
|
+
{getName(item)}
|
|
234
|
+
</ViewComponent.RowTitle>
|
|
235
|
+
)}
|
|
236
|
+
</div>
|
|
237
|
+
{end && (
|
|
238
|
+
<div
|
|
239
|
+
className={cx(
|
|
240
|
+
"flex items-center gap-2",
|
|
241
|
+
isSelected && "text-primary"
|
|
242
|
+
)}
|
|
243
|
+
tabIndex={-1}
|
|
244
|
+
>
|
|
245
|
+
{end}
|
|
246
|
+
</div>
|
|
247
|
+
)}
|
|
248
|
+
</div>
|
|
249
|
+
{below && (
|
|
250
|
+
<div
|
|
251
|
+
className={cx(
|
|
252
|
+
"flex items-center",
|
|
253
|
+
isSelected && "text-primary"
|
|
254
|
+
)}
|
|
255
|
+
tabIndex={-1}
|
|
256
|
+
>
|
|
257
|
+
{below}
|
|
258
|
+
</div>
|
|
259
|
+
)}
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
</ViewComponent.Row>
|
|
263
|
+
);
|
|
264
|
+
}}
|
|
265
|
+
/>
|
|
266
|
+
);
|
|
267
|
+
})
|
|
268
|
+
);
|
|
@@ -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
|
|
|
@@ -159,30 +147,24 @@ function ListViewEditableRowTitle({
|
|
|
159
147
|
// the `onBlur` fires correctly.
|
|
160
148
|
element.focus();
|
|
161
149
|
|
|
150
|
+
// must be a delay of 1ms or more to ensure any radix ui components have finished any focus behaviors
|
|
162
151
|
setTimeout(() => {
|
|
163
152
|
element.select();
|
|
164
|
-
},
|
|
153
|
+
}, 1);
|
|
165
154
|
}, [autoFocus]);
|
|
166
155
|
|
|
167
156
|
return (
|
|
168
|
-
<
|
|
157
|
+
<InputField.Input
|
|
169
158
|
ref={inputRef}
|
|
170
|
-
variant="bare"
|
|
171
159
|
value={value}
|
|
172
160
|
placeholder={placeholder}
|
|
173
161
|
onSubmit={onSubmitEditing}
|
|
174
162
|
allowSubmittingWithSameValue
|
|
163
|
+
className={cx("-mx-1.5 -my-1 bg-listview-editing-background", className)}
|
|
175
164
|
/>
|
|
176
165
|
);
|
|
177
166
|
}
|
|
178
167
|
|
|
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
168
|
/* ----------------------------------------------------------------------------
|
|
187
169
|
* Row
|
|
188
170
|
* ------------------------------------------------------------------------- */
|
|
@@ -225,18 +207,24 @@ const RowContainer = forwardRef<
|
|
|
225
207
|
style,
|
|
226
208
|
$clickable,
|
|
227
209
|
"aria-describedby": _, // Causes hydration warning
|
|
210
|
+
role,
|
|
211
|
+
"aria-label": ariaLabel,
|
|
212
|
+
"aria-roledescription": ariaRoleDescription,
|
|
213
|
+
"aria-selected": ariaSelected,
|
|
228
214
|
...props
|
|
229
215
|
},
|
|
230
216
|
ref
|
|
231
217
|
) => {
|
|
232
|
-
const margin = getPositionMargin($marginType);
|
|
233
|
-
|
|
234
218
|
return (
|
|
235
219
|
<div
|
|
236
220
|
ref={ref}
|
|
221
|
+
role={role}
|
|
222
|
+
aria-label={ariaLabel}
|
|
223
|
+
aria-roledescription={ariaRoleDescription}
|
|
224
|
+
aria-selected={ariaSelected ?? $selected}
|
|
237
225
|
className={cx(
|
|
238
226
|
$isSectionHeader && $sectionHeaderVariant === "label"
|
|
239
|
-
?
|
|
227
|
+
? "font-sans text-label uppercase leading-3 font-bold text-text-muted"
|
|
240
228
|
: "font-sans text-heading5 font-normal",
|
|
241
229
|
!$isSectionHeader &&
|
|
242
230
|
$selected &&
|
|
@@ -254,22 +242,11 @@ const RowContainer = forwardRef<
|
|
|
254
242
|
paddingRight: $selected ? "8px" : "12px",
|
|
255
243
|
paddingBottom: $selected ? "4px" : "6px",
|
|
256
244
|
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
|
-
: "",
|
|
245
|
+
borderRadius: $selected ? "4px" : "",
|
|
246
|
+
marginLeft: $selected ? "4px" : "",
|
|
247
|
+
marginRight: $selected ? "4px" : "",
|
|
248
|
+
marginTop: $selected ? "2px" : "",
|
|
249
|
+
marginBottom: $selected ? "2px" : "",
|
|
273
250
|
}),
|
|
274
251
|
...($selected &&
|
|
275
252
|
!$isSectionHeader && {
|
|
@@ -351,6 +328,7 @@ const ListViewDragIndicatorElement = forwardRef<
|
|
|
351
328
|
zIndex: 1000,
|
|
352
329
|
position: "absolute",
|
|
353
330
|
borderRadius: "3px",
|
|
331
|
+
pointerEvents: "none",
|
|
354
332
|
...($relativeDropPosition === "inside"
|
|
355
333
|
? {
|
|
356
334
|
inset: 2,
|
|
@@ -396,6 +374,7 @@ interface ListViewClickInfo {
|
|
|
396
374
|
interface ListViewRowProps<MenuItemType extends string = string> {
|
|
397
375
|
id?: string;
|
|
398
376
|
tabIndex?: number;
|
|
377
|
+
role?: string;
|
|
399
378
|
selected?: boolean;
|
|
400
379
|
/** @default 0 */
|
|
401
380
|
depth?: number;
|
|
@@ -432,6 +411,7 @@ const ListViewRow = forwardRefGeneric(function ListViewRow<
|
|
|
432
411
|
{
|
|
433
412
|
id,
|
|
434
413
|
tabIndex = 0,
|
|
414
|
+
role,
|
|
435
415
|
gap,
|
|
436
416
|
backgroundColor,
|
|
437
417
|
selected = false,
|
|
@@ -526,6 +506,7 @@ const ListViewRow = forwardRefGeneric(function ListViewRow<
|
|
|
526
506
|
onContextMenu={onContextMenu}
|
|
527
507
|
$isSectionHeader={isSectionHeader}
|
|
528
508
|
id={id}
|
|
509
|
+
role={role}
|
|
529
510
|
$gap={gap ?? 0}
|
|
530
511
|
$backgroundColor={backgroundColor}
|
|
531
512
|
{...hoverProps}
|
|
@@ -764,14 +745,18 @@ type RenderProps<T> = {
|
|
|
764
745
|
virtualized?: Size;
|
|
765
746
|
};
|
|
766
747
|
|
|
767
|
-
type ListViewVariant = "normal" | "
|
|
748
|
+
type ListViewVariant = "normal" | "bare";
|
|
768
749
|
|
|
769
750
|
type ListViewSectionHeaderVariant = "normal" | "label";
|
|
770
751
|
|
|
771
|
-
type ListViewRootProps = {
|
|
752
|
+
export type ListViewRootProps = {
|
|
772
753
|
id?: string;
|
|
773
754
|
className?: string;
|
|
774
755
|
style?: CSSProperties;
|
|
756
|
+
role?: string;
|
|
757
|
+
"aria-labelledby"?: string;
|
|
758
|
+
"aria-orientation"?: "vertical" | "horizontal";
|
|
759
|
+
"aria-multiselectable"?: boolean;
|
|
775
760
|
onPress?: () => void;
|
|
776
761
|
scrollable?: boolean;
|
|
777
762
|
expandable?: boolean;
|
|
@@ -788,6 +773,11 @@ type ListViewRootProps = {
|
|
|
788
773
|
divider?: boolean;
|
|
789
774
|
gap?: number;
|
|
790
775
|
colorScheme?: ListColorScheme;
|
|
776
|
+
containerRef?: React.RefObject<HTMLDivElement>;
|
|
777
|
+
onDragOver?: React.DragEventHandler<HTMLDivElement>;
|
|
778
|
+
onDragEnter?: React.DragEventHandler<HTMLDivElement>;
|
|
779
|
+
onDragLeave?: React.DragEventHandler<HTMLDivElement>;
|
|
780
|
+
onDrop?: React.DragEventHandler<HTMLDivElement>;
|
|
791
781
|
};
|
|
792
782
|
|
|
793
783
|
const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
@@ -795,6 +785,10 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
795
785
|
id,
|
|
796
786
|
className,
|
|
797
787
|
style,
|
|
788
|
+
role,
|
|
789
|
+
"aria-labelledby": ariaLabelledby,
|
|
790
|
+
"aria-orientation": ariaOrientation,
|
|
791
|
+
"aria-multiselectable": ariaMultiselectable,
|
|
798
792
|
onPress,
|
|
799
793
|
scrollable = false,
|
|
800
794
|
expandable = true,
|
|
@@ -812,6 +806,11 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
812
806
|
pressEventName = "onClick",
|
|
813
807
|
colorScheme = "primary",
|
|
814
808
|
gap = 0,
|
|
809
|
+
containerRef,
|
|
810
|
+
onDragOver,
|
|
811
|
+
onDragEnter,
|
|
812
|
+
onDragLeave,
|
|
813
|
+
onDrop,
|
|
815
814
|
}: RenderProps<T> & ListViewRootProps,
|
|
816
815
|
forwardedRef: ForwardedRef<IVirtualizedList>
|
|
817
816
|
) {
|
|
@@ -1002,17 +1001,13 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
1002
1001
|
const getItemHeight = useCallback(
|
|
1003
1002
|
(index: number) => {
|
|
1004
1003
|
const child = getItemContextValue(index);
|
|
1005
|
-
const margin = child?.marginType
|
|
1006
|
-
? getPositionMargin(child.marginType)
|
|
1007
|
-
: { top: 0, bottom: 0 };
|
|
1008
1004
|
const height =
|
|
1009
|
-
|
|
1005
|
+
child?.isSectionHeader && child.sectionHeaderVariant === "label"
|
|
1010
1006
|
? SECTION_HEADER_LABEL_HEIGHT
|
|
1011
|
-
: ROW_HEIGHT
|
|
1012
|
-
(variant === "padded" ? margin.top + margin.bottom : 0);
|
|
1007
|
+
: ROW_HEIGHT;
|
|
1013
1008
|
return height;
|
|
1014
1009
|
},
|
|
1015
|
-
[getItemContextValue
|
|
1010
|
+
[getItemContextValue]
|
|
1016
1011
|
);
|
|
1017
1012
|
|
|
1018
1013
|
const getKey = useCallback(
|
|
@@ -1027,6 +1022,10 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
1027
1022
|
<ListViewDraggingContext.Provider value={draggingContextValue}>
|
|
1028
1023
|
<div
|
|
1029
1024
|
id={id}
|
|
1025
|
+
role={role}
|
|
1026
|
+
aria-labelledby={ariaLabelledby}
|
|
1027
|
+
aria-orientation={ariaOrientation}
|
|
1028
|
+
aria-multiselectable={ariaMultiselectable}
|
|
1030
1029
|
className={cx(
|
|
1031
1030
|
`flex flex-col text-text-muted ${scrollable ? "flex-1" : "flex-none"} `,
|
|
1032
1031
|
className
|
|
@@ -1035,6 +1034,11 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
1035
1034
|
{...{
|
|
1036
1035
|
[pressEventName]: handleClick,
|
|
1037
1036
|
}}
|
|
1037
|
+
ref={containerRef}
|
|
1038
|
+
onDragOver={onDragOver}
|
|
1039
|
+
onDragEnter={onDragEnter}
|
|
1040
|
+
onDragLeave={onDragLeave}
|
|
1041
|
+
onDrop={onDrop}
|
|
1038
1042
|
>
|
|
1039
1043
|
{withScrollable((scrollElementRef: HTMLDivElement | null) =>
|
|
1040
1044
|
withSortable(
|
|
@@ -1121,15 +1125,15 @@ export namespace ListView {
|
|
|
1121
1125
|
export const DragIndicator = ListViewDragIndicatorElement;
|
|
1122
1126
|
export const rowHeight = ROW_HEIGHT;
|
|
1123
1127
|
export const sectionHeaderLabelHeight = SECTION_HEADER_LABEL_HEIGHT;
|
|
1124
|
-
export const calculateHeight = (
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1128
|
+
export const calculateHeight = ({
|
|
1129
|
+
menuItemsCount,
|
|
1130
|
+
sectionHeaderCount,
|
|
1131
|
+
}: {
|
|
1132
|
+
menuItemsCount: number;
|
|
1133
|
+
sectionHeaderCount: number;
|
|
1134
|
+
}) => {
|
|
1129
1135
|
return (
|
|
1130
|
-
|
|
1131
|
-
headerCount *
|
|
1132
|
-
(headerVariant === "label" ? sectionHeaderLabelHeight : rowHeight)
|
|
1136
|
+
menuItemsCount * rowHeight + sectionHeaderCount * sectionHeaderLabelHeight
|
|
1133
1137
|
);
|
|
1134
1138
|
};
|
|
1135
1139
|
export const normalizeDestinationIndex = normalizeListDestinationIndex;
|
|
@@ -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}
|