@noya-app/noya-designsystem 0.1.64 → 0.1.65
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 +12 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +501 -228
- package/dist/index.d.ts +501 -228
- package/dist/index.js +6738 -2571
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6697 -2499
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
- package/src/components/ActivityIndicator.tsx +9 -4
- package/src/components/ActivityLog.tsx +197 -0
- package/src/components/AnimatePresence.tsx +37 -22
- package/src/components/Avatar.tsx +42 -25
- package/src/components/Banner.tsx +57 -11
- package/src/components/BaseToolbar.tsx +29 -7
- package/src/components/Breadcrumbs.tsx +2 -2
- package/src/components/Button.tsx +23 -24
- package/src/components/Checkbox.tsx +6 -35
- package/src/components/Chip.tsx +45 -33
- package/src/components/Collection.tsx +16 -11
- package/src/components/ColorSwatch.tsx +3 -3
- package/src/components/ColorSwatchControl.tsx +2 -2
- package/src/components/Combobox.tsx +6 -6
- package/src/components/ComboboxMenu.tsx +6 -5
- package/src/components/CommandPalette.tsx +1 -1
- package/src/components/Dialog.tsx +6 -12
- package/src/components/Divider.tsx +8 -8
- package/src/components/DraggableMenuButton.tsx +9 -5
- package/src/components/Drawer.tsx +5 -5
- package/src/components/DropdownMenu.tsx +1 -1
- package/src/components/EditableText.tsx +2 -2
- package/src/components/Fade.tsx +13 -13
- package/src/components/FileUploadIndicator.tsx +11 -11
- package/src/components/FloatingWindow.tsx +2 -2
- package/src/components/Grid.tsx +18 -11
- package/src/components/GridView.tsx +32 -25
- package/src/components/IVirtualizedList.tsx +5 -0
- package/src/components/Icons.tsx +24 -4
- package/src/components/InputField.tsx +19 -19
- package/src/components/InspectorContainer.tsx +2 -2
- package/src/components/InspectorPrimitives.tsx +15 -9
- package/src/components/Label.tsx +4 -3
- package/src/components/LabeledElementView.tsx +3 -3
- package/src/components/LabeledField.tsx +6 -6
- package/src/components/List.tsx +103 -45
- package/src/components/ListMenu.tsx +91 -0
- package/src/components/ListNavigator.tsx +106 -0
- package/src/components/ListView.tsx +20 -1158
- package/src/components/MediaThumbnail.tsx +56 -15
- package/src/components/Message.tsx +5 -5
- package/src/components/Navigator.tsx +461 -0
- package/src/components/NoyaLogo.tsx +1 -1
- package/src/components/Popover.tsx +9 -5
- package/src/components/Progress.tsx +19 -9
- package/src/components/ResizableContainer.tsx +187 -0
- package/src/components/RingProgress.tsx +128 -0
- package/src/components/ScrollArea.tsx +4 -4
- package/src/components/ScrollableSidebar.tsx +10 -0
- package/src/components/SearchCompletionMenu.tsx +3 -3
- package/src/components/Section.tsx +107 -57
- package/src/components/SegmentedControl.tsx +29 -29
- package/src/components/SelectMenu.tsx +13 -20
- package/src/components/SelectionToolbar.tsx +4 -3
- package/src/components/Slider.tsx +9 -9
- package/src/components/Spacer.tsx +2 -2
- package/src/components/StackNavigator.tsx +378 -0
- package/src/components/Stepper.tsx +88 -0
- package/src/components/Switch.tsx +4 -4
- package/src/components/Tabs.tsx +3 -3
- package/src/components/Text.tsx +23 -13
- package/src/components/TextArea.tsx +3 -3
- package/src/components/Toast.tsx +4 -4
- package/src/components/Toolbar.tsx +50 -9
- package/src/components/ToolbarDrawer.tsx +2 -2
- package/src/components/Tooltip.tsx +1 -1
- package/src/components/TreeView.tsx +11 -4
- package/src/components/UserPointer.tsx +4 -4
- package/src/components/ai-assistant/AIAssistantLayout.tsx +8 -8
- package/src/components/blocks/ImageBlockComponent.tsx +13 -7
- package/src/components/connected-users-menu/ConnectedUsersMenuLayout.tsx +4 -4
- package/src/components/file-explorer/FileExplorerLayout.tsx +20 -8
- package/src/components/internal/Checkmark.tsx +1 -1
- package/src/components/internal/Menu.tsx +22 -18
- package/src/components/internal/MenuViewport.tsx +13 -6
- package/src/components/internal/SelectItem.tsx +3 -3
- package/src/components/internal/TextInput.tsx +5 -1
- package/src/components/listView/ListViewContexts.tsx +68 -0
- package/src/components/listView/ListViewEditableRowTitle.tsx +55 -0
- package/src/components/listView/ListViewRoot.tsx +521 -0
- package/src/components/listView/ListViewRow.tsx +475 -0
- package/src/components/listView/ListViewRowTitle.tsx +21 -0
- package/src/components/listView/types.ts +11 -0
- package/src/components/pipeline/PipelineResultLayout.tsx +6 -6
- package/src/components/sorting/DragRegistration.tsx +2 -1
- package/src/components/sorting/SharedDragProvider.tsx +91 -9
- package/src/components/sorting/Sortable.tsx +71 -17
- package/src/components/workspace/DrawerWorkspaceLayout.tsx +5 -5
- package/src/components/workspace/PanelWorkspaceLayout.tsx +65 -49
- package/src/components/workspace/VerticalTabMenu.tsx +25 -15
- package/src/components/workspace/WorkspaceLayout.tsx +30 -21
- package/src/contexts/DialogContext.tsx +17 -5
- package/src/hooks/useIndent.ts +3 -8
- package/src/index.css +24 -10
- package/src/index.tsx +10 -5
- package/src/utils/classNames.ts +52 -6
- package/src/utils/editableBlockStyles.ts +2 -2
- package/src/utils/formatByteSize.ts +1 -0
- package/src/utils/inputs.ts +8 -6
- package/src/utils/sketchColor.ts +0 -34
- package/tailwind.config.ts +1 -0
- package/src/components/FillInputField.tsx +0 -37
- package/src/components/FillPreviewBackground.tsx +0 -134
- package/src/components/GradientPicker.tsx +0 -90
- package/src/utils/getGradientBackground.tsx +0 -31
|
@@ -1,1166 +1,28 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { composeRefs } from "@radix-ui/react-compose-refs";
|
|
7
|
-
import React, {
|
|
8
|
-
Children,
|
|
9
|
-
createContext,
|
|
10
|
-
CSSProperties,
|
|
11
|
-
ForwardedRef,
|
|
12
|
-
forwardRef,
|
|
13
|
-
HTMLAttributes,
|
|
14
|
-
isValidElement,
|
|
15
|
-
memo,
|
|
16
|
-
ReactElement,
|
|
17
|
-
ReactNode,
|
|
18
|
-
Ref,
|
|
19
|
-
useCallback,
|
|
20
|
-
useContext,
|
|
21
|
-
useImperativeHandle,
|
|
22
|
-
useLayoutEffect,
|
|
23
|
-
useMemo,
|
|
24
|
-
useRef,
|
|
25
|
-
} from "react";
|
|
26
|
-
import { WindowScroller, WindowScrollerChildProps } from "react-virtualized";
|
|
27
|
-
import { ListChildComponentProps, VariableSizeList } from "react-window";
|
|
28
|
-
import { mergeEventHandlers } from "../hooks/mergeEventHandlers";
|
|
29
|
-
import { useHover } from "../hooks/useHover";
|
|
30
|
-
import { cssVars, INPUT_HEIGHT } from "../theme";
|
|
31
|
-
import { cx, mergeConflictingClassNames } from "../utils/classNames";
|
|
32
|
-
import { ContextMenu } from "./ContextMenu";
|
|
33
|
-
import { InputField } from "./InputField";
|
|
34
|
-
import { MenuItem } from "./internal/Menu";
|
|
35
|
-
import { ScrollArea } from "./ScrollArea";
|
|
36
|
-
import { SharedDragProps } from "./sorting/DragRegistration";
|
|
37
|
-
import { Sortable } from "./sorting/Sortable";
|
|
3
|
+
import { IVirtualizedList } from "./IVirtualizedList";
|
|
4
|
+
import { ListRowContext } from "./listView/ListViewContexts";
|
|
5
|
+
import { ListViewEditableRowTitle } from "./listView/ListViewEditableRowTitle";
|
|
38
6
|
import {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
type ListColorScheme = "primary" | "secondary";
|
|
54
|
-
|
|
55
|
-
type PressEventName = "onClick" | "onPointerDown";
|
|
56
|
-
|
|
57
|
-
// TODO: Some of these could be passed in a single context value that's
|
|
58
|
-
// the same for all rows. But doing this seems to cause an infinite loop and
|
|
59
|
-
// prevents drag and drop from working.
|
|
60
|
-
type ListRowContextValue = {
|
|
61
|
-
marginType: ListRowMarginType;
|
|
62
|
-
selectedPosition: ListRowPosition;
|
|
63
|
-
sortable: boolean;
|
|
64
|
-
expandable: boolean;
|
|
65
|
-
divider: boolean;
|
|
66
|
-
gap: number;
|
|
67
|
-
variant: ListViewVariant;
|
|
68
|
-
sectionHeaderVariant: ListViewSectionHeaderVariant;
|
|
69
|
-
pressEventName: PressEventName;
|
|
70
|
-
isSectionHeader: boolean;
|
|
71
|
-
colorScheme: ListColorScheme;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
// The dragged item isn't passed a context value (doing so causes an infinite loop),
|
|
75
|
-
// so we pass this separately. Rows need the indentation to look correct when dragging,
|
|
76
|
-
// and the dragged row doesn't have a ListRowContextValue.
|
|
77
|
-
type ListViewDraggingContextValue = {
|
|
78
|
-
indentation: number;
|
|
79
|
-
isDragOverlay?: boolean;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const ListViewDraggingContext = createContext<ListViewDraggingContextValue>({
|
|
83
|
-
indentation: 12,
|
|
84
|
-
});
|
|
85
|
-
(ListViewDraggingContext as any).displayName = "ListViewDraggingContext";
|
|
86
|
-
|
|
87
|
-
const ListRowContext = createContext<ListRowContextValue>({
|
|
88
|
-
marginType: "none",
|
|
89
|
-
selectedPosition: "only",
|
|
90
|
-
sortable: false,
|
|
91
|
-
expandable: true,
|
|
92
|
-
divider: true,
|
|
93
|
-
gap: 0,
|
|
94
|
-
variant: "normal",
|
|
95
|
-
sectionHeaderVariant: "normal",
|
|
96
|
-
pressEventName: "onClick",
|
|
97
|
-
isSectionHeader: false,
|
|
98
|
-
colorScheme: "primary",
|
|
99
|
-
});
|
|
100
|
-
(ListRowContext as any).displayName = "ListRowContext";
|
|
101
|
-
|
|
102
|
-
/* ----------------------------------------------------------------------------
|
|
103
|
-
* RowTitle
|
|
104
|
-
* ------------------------------------------------------------------------- */
|
|
105
|
-
|
|
106
|
-
const ListViewRowTitle = ({
|
|
107
|
-
className,
|
|
108
|
-
children,
|
|
109
|
-
...props
|
|
110
|
-
}: {
|
|
111
|
-
className?: string;
|
|
112
|
-
children: React.ReactNode;
|
|
113
|
-
} & HTMLAttributes<HTMLSpanElement>) => (
|
|
114
|
-
<span
|
|
115
|
-
className={cx(
|
|
116
|
-
`flex-1 overflow-hidden text-ellipsis whitespace-pre `,
|
|
117
|
-
className
|
|
118
|
-
)}
|
|
119
|
-
{...props}
|
|
120
|
-
>
|
|
121
|
-
{children}
|
|
122
|
-
</span>
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
/* ----------------------------------------------------------------------------
|
|
126
|
-
* EditableRowTitle
|
|
127
|
-
* ------------------------------------------------------------------------- */
|
|
128
|
-
|
|
129
|
-
export interface EditableRowProps {
|
|
130
|
-
value: string;
|
|
131
|
-
onSubmitEditing: (value: string) => void;
|
|
132
|
-
autoFocus: boolean;
|
|
133
|
-
placeholder?: string;
|
|
134
|
-
className?: string;
|
|
135
|
-
onKeyDown?: (e: React.KeyboardEvent) => void;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function ListViewEditableRowTitle({
|
|
139
|
-
value,
|
|
140
|
-
onSubmitEditing,
|
|
141
|
-
autoFocus,
|
|
142
|
-
placeholder,
|
|
143
|
-
className,
|
|
144
|
-
onKeyDown,
|
|
145
|
-
}: EditableRowProps) {
|
|
146
|
-
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
147
|
-
|
|
148
|
-
useLayoutEffect(() => {
|
|
149
|
-
const element = inputRef.current;
|
|
150
|
-
|
|
151
|
-
if (!element || !autoFocus) return;
|
|
152
|
-
|
|
153
|
-
// Calling `focus` is necessary, in addition to `select`, to ensure
|
|
154
|
-
// the `onBlur` fires correctly.
|
|
155
|
-
element.focus();
|
|
156
|
-
|
|
157
|
-
// must be a delay of 1ms or more to ensure any radix ui components have finished any focus behaviors
|
|
158
|
-
setTimeout(() => {
|
|
159
|
-
element.select();
|
|
160
|
-
}, 1);
|
|
161
|
-
}, [autoFocus]);
|
|
162
|
-
|
|
163
|
-
return (
|
|
164
|
-
<InputField.Input
|
|
165
|
-
ref={inputRef}
|
|
166
|
-
value={value}
|
|
167
|
-
placeholder={placeholder}
|
|
168
|
-
onSubmit={onSubmitEditing}
|
|
169
|
-
allowSubmittingWithSameValue
|
|
170
|
-
className={cx("-mx-1.5 -my-1 bg-listview-editing-background", className)}
|
|
171
|
-
onKeyDown={onKeyDown}
|
|
172
|
-
/>
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/* ----------------------------------------------------------------------------
|
|
177
|
-
* Row
|
|
178
|
-
* ------------------------------------------------------------------------- */
|
|
179
|
-
|
|
180
|
-
const RowContainer = forwardRef<
|
|
181
|
-
HTMLDivElement,
|
|
182
|
-
React.HTMLAttributes<HTMLDivElement> & {
|
|
183
|
-
$marginType: ListRowMarginType;
|
|
184
|
-
$selected: boolean;
|
|
185
|
-
$selectedPosition: ListRowPosition;
|
|
186
|
-
$disabled: boolean;
|
|
187
|
-
$hovered: boolean;
|
|
188
|
-
$variant: ListViewVariant;
|
|
189
|
-
$divider: boolean;
|
|
190
|
-
$isSectionHeader: boolean;
|
|
191
|
-
$showsActiveState: boolean;
|
|
192
|
-
$sectionHeaderVariant: ListViewSectionHeaderVariant;
|
|
193
|
-
$colorScheme: ListColorScheme;
|
|
194
|
-
$gap: number;
|
|
195
|
-
$backgroundColor?: CSSProperties["backgroundColor"];
|
|
196
|
-
$clickable?: boolean;
|
|
197
|
-
href?: string;
|
|
198
|
-
as?: React.ElementType;
|
|
199
|
-
}
|
|
200
|
-
>(
|
|
201
|
-
(
|
|
202
|
-
{
|
|
203
|
-
as: Component = "div",
|
|
204
|
-
href,
|
|
205
|
-
className,
|
|
206
|
-
$marginType,
|
|
207
|
-
$selected,
|
|
208
|
-
$selectedPosition,
|
|
209
|
-
$disabled,
|
|
210
|
-
$hovered,
|
|
211
|
-
$variant,
|
|
212
|
-
$divider,
|
|
213
|
-
$isSectionHeader,
|
|
214
|
-
$showsActiveState,
|
|
215
|
-
$sectionHeaderVariant,
|
|
216
|
-
$colorScheme,
|
|
217
|
-
$gap,
|
|
218
|
-
$backgroundColor,
|
|
219
|
-
style,
|
|
220
|
-
$clickable,
|
|
221
|
-
"aria-describedby": _, // Causes hydration warning
|
|
222
|
-
role,
|
|
223
|
-
"aria-label": ariaLabel,
|
|
224
|
-
"aria-roledescription": ariaRoleDescription,
|
|
225
|
-
"aria-selected": ariaSelected,
|
|
226
|
-
...props
|
|
227
|
-
},
|
|
228
|
-
ref
|
|
229
|
-
) => {
|
|
230
|
-
return (
|
|
231
|
-
<Component
|
|
232
|
-
ref={ref}
|
|
233
|
-
href={href}
|
|
234
|
-
role={role}
|
|
235
|
-
aria-label={ariaLabel}
|
|
236
|
-
aria-roledescription={ariaRoleDescription}
|
|
237
|
-
aria-selected={ariaSelected ?? $selected}
|
|
238
|
-
className={cx(
|
|
239
|
-
$isSectionHeader && $sectionHeaderVariant === "label"
|
|
240
|
-
? "font-sans text-label uppercase leading-3 font-bold text-text-muted"
|
|
241
|
-
: "font-sans text-heading5 font-normal",
|
|
242
|
-
!$isSectionHeader &&
|
|
243
|
-
$selected &&
|
|
244
|
-
"text-selected-list-item-text active:bg-selected-list-item-background",
|
|
245
|
-
!$isSectionHeader && $disabled && "text-text-disabled",
|
|
246
|
-
$clickable ? "cursor-pointer" : "cursor-default",
|
|
247
|
-
$isSectionHeader && "bg-listview-raised-background",
|
|
248
|
-
"flex flex-[0_0_auto] select-none items-center relative",
|
|
249
|
-
className
|
|
250
|
-
)}
|
|
251
|
-
style={{
|
|
252
|
-
gap: $gap,
|
|
253
|
-
...($variant !== "bare" && {
|
|
254
|
-
paddingTop: $selected ? "4px" : "6px",
|
|
255
|
-
paddingRight: $selected ? "8px" : "12px",
|
|
256
|
-
paddingBottom: $selected ? "4px" : "6px",
|
|
257
|
-
paddingLeft: $selected ? "8px" : "12px",
|
|
258
|
-
borderRadius: $selected ? "4px" : "",
|
|
259
|
-
marginLeft: $selected ? "4px" : "",
|
|
260
|
-
marginRight: $selected ? "4px" : "",
|
|
261
|
-
marginTop: $selected ? "2px" : "",
|
|
262
|
-
marginBottom: $selected ? "2px" : "",
|
|
263
|
-
}),
|
|
264
|
-
...($selected &&
|
|
265
|
-
!$isSectionHeader && {
|
|
266
|
-
backgroundColor:
|
|
267
|
-
$colorScheme === "primary"
|
|
268
|
-
? cssVars.colors["selectedListItemBackground"]
|
|
269
|
-
: cssVars.colors[$colorScheme],
|
|
270
|
-
}),
|
|
271
|
-
...($selected &&
|
|
272
|
-
!$isSectionHeader &&
|
|
273
|
-
($selectedPosition === "middle" ||
|
|
274
|
-
$selectedPosition === "last") && {
|
|
275
|
-
borderTopRightRadius: "0px",
|
|
276
|
-
borderTopLeftRadius: "0px",
|
|
277
|
-
}),
|
|
278
|
-
...($selected &&
|
|
279
|
-
!$isSectionHeader &&
|
|
280
|
-
($selectedPosition === "middle" ||
|
|
281
|
-
$selectedPosition === "first") && {
|
|
282
|
-
borderBottomRightRadius: "0px",
|
|
283
|
-
borderBottomLeftRadius: "0px",
|
|
284
|
-
}),
|
|
285
|
-
...($hovered &&
|
|
286
|
-
!$selected && {
|
|
287
|
-
background: cssVars.colors.listViewHoverBackground,
|
|
288
|
-
}),
|
|
289
|
-
...($showsActiveState && {
|
|
290
|
-
"&:active": {
|
|
291
|
-
backgroundColor: $selected
|
|
292
|
-
? $colorScheme === "secondary"
|
|
293
|
-
? cssVars.colors.secondaryLight
|
|
294
|
-
: cssVars.colors.selectedListItemBackground
|
|
295
|
-
: cssVars.colors.activeBackground,
|
|
296
|
-
},
|
|
297
|
-
}),
|
|
298
|
-
...($divider && {
|
|
299
|
-
borderBottom: `1px solid ${cssVars.colors.dividerSubtle}`,
|
|
300
|
-
}),
|
|
301
|
-
...($backgroundColor && {
|
|
302
|
-
backgroundColor: $backgroundColor,
|
|
303
|
-
"&:hover": {
|
|
304
|
-
backgroundColor: $backgroundColor,
|
|
305
|
-
},
|
|
306
|
-
"&:active": {
|
|
307
|
-
backgroundColor: $backgroundColor,
|
|
308
|
-
},
|
|
309
|
-
}),
|
|
310
|
-
...style,
|
|
311
|
-
}}
|
|
312
|
-
{...props}
|
|
313
|
-
/>
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
);
|
|
317
|
-
|
|
318
|
-
const ListViewDragIndicatorElement = forwardRef<
|
|
319
|
-
HTMLDivElement,
|
|
320
|
-
React.HTMLAttributes<HTMLDivElement> & {
|
|
321
|
-
$relativeDropPosition?: RelativeDropPosition;
|
|
322
|
-
$gap: number;
|
|
323
|
-
$offsetLeft: number;
|
|
324
|
-
$colorScheme: ListColorScheme;
|
|
325
|
-
}
|
|
326
|
-
>(
|
|
327
|
-
(
|
|
328
|
-
{
|
|
329
|
-
className,
|
|
330
|
-
$relativeDropPosition,
|
|
331
|
-
$offsetLeft,
|
|
332
|
-
$colorScheme,
|
|
333
|
-
$gap,
|
|
334
|
-
style,
|
|
335
|
-
...props
|
|
336
|
-
},
|
|
337
|
-
ref
|
|
338
|
-
) => {
|
|
339
|
-
return (
|
|
340
|
-
<div
|
|
341
|
-
ref={ref}
|
|
342
|
-
{...props}
|
|
343
|
-
className={className}
|
|
344
|
-
style={{
|
|
345
|
-
zIndex: 1000,
|
|
346
|
-
position: "absolute",
|
|
347
|
-
borderRadius: "3px",
|
|
348
|
-
pointerEvents: "none",
|
|
349
|
-
...($relativeDropPosition === "inside"
|
|
350
|
-
? {
|
|
351
|
-
inset: 2,
|
|
352
|
-
boxShadow: `0 0 0 1px ${cssVars.colors.sidebarBackground}, 0 0 0 3px ${
|
|
353
|
-
$colorScheme === "secondary"
|
|
354
|
-
? cssVars.colors.secondary
|
|
355
|
-
: cssVars.colors.dragOutline
|
|
356
|
-
}`,
|
|
357
|
-
}
|
|
358
|
-
: {
|
|
359
|
-
top:
|
|
360
|
-
$relativeDropPosition === "above"
|
|
361
|
-
? -(3 + $gap / 2)
|
|
362
|
-
: undefined,
|
|
363
|
-
bottom:
|
|
364
|
-
$relativeDropPosition === "below"
|
|
365
|
-
? -(3 + $gap / 2)
|
|
366
|
-
: undefined,
|
|
367
|
-
left: $offsetLeft,
|
|
368
|
-
right: 0,
|
|
369
|
-
height: 6,
|
|
370
|
-
backgroundColor:
|
|
371
|
-
$colorScheme === "secondary"
|
|
372
|
-
? cssVars.colors.secondary
|
|
373
|
-
: cssVars.colors.dragOutline,
|
|
374
|
-
border: `2px solid white`,
|
|
375
|
-
boxShadow: "0 0 2px rgba(0,0,0,0.5)",
|
|
376
|
-
}),
|
|
377
|
-
...style,
|
|
378
|
-
}}
|
|
379
|
-
/>
|
|
380
|
-
);
|
|
381
|
-
}
|
|
382
|
-
);
|
|
383
|
-
|
|
384
|
-
interface ListViewClickInfo {
|
|
385
|
-
shiftKey: boolean;
|
|
386
|
-
altKey: boolean;
|
|
387
|
-
metaKey: boolean;
|
|
388
|
-
ctrlKey: boolean;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
export interface ListViewRowProps<MenuItemType extends string = string> {
|
|
392
|
-
id?: string;
|
|
393
|
-
tabIndex?: number;
|
|
394
|
-
role?: string;
|
|
395
|
-
selected?: boolean;
|
|
396
|
-
/** @default 0 */
|
|
397
|
-
depth?: number;
|
|
398
|
-
disabled?: boolean;
|
|
399
|
-
// draggable?: boolean;
|
|
400
|
-
hovered?: boolean;
|
|
401
|
-
sortable?: boolean;
|
|
402
|
-
gap?: number;
|
|
403
|
-
backgroundColor?: CSSProperties["backgroundColor"];
|
|
404
|
-
onPress?: (info: ListViewClickInfo) => void;
|
|
405
|
-
onDoubleClick?: () => void;
|
|
406
|
-
onHoverChange?: (isHovering: boolean) => void;
|
|
407
|
-
children?: ReactNode;
|
|
408
|
-
isSectionHeader?: boolean;
|
|
409
|
-
menuItems?: MenuItem<MenuItemType>[];
|
|
410
|
-
onSelectMenuItem?: (value: MenuItemType) => void;
|
|
411
|
-
onContextMenu?: () => void;
|
|
412
|
-
onMenuOpenChange?: (isOpen: boolean) => void;
|
|
413
|
-
onKeyDown?: (event: React.KeyboardEvent) => void;
|
|
414
|
-
style?: CSSProperties;
|
|
415
|
-
className?: string;
|
|
416
|
-
dragIndicatorStyle?:
|
|
417
|
-
| CSSProperties
|
|
418
|
-
| ((props: DragIndicatorStyleProps) => CSSProperties);
|
|
419
|
-
testRelativeDropPosition?: RelativeDropPosition;
|
|
420
|
-
href?: string;
|
|
421
|
-
as?: React.ElementType;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
export type DragIndicatorStyleProps = {
|
|
425
|
-
depth: number;
|
|
426
|
-
indentation: number;
|
|
427
|
-
position: RelativeDropPosition;
|
|
428
|
-
};
|
|
429
|
-
|
|
430
|
-
const ListViewRow = forwardRefGeneric(function ListViewRow<
|
|
431
|
-
MenuItemType extends string,
|
|
432
|
-
>(
|
|
433
|
-
{
|
|
434
|
-
id,
|
|
435
|
-
tabIndex = 0,
|
|
436
|
-
role,
|
|
437
|
-
gap,
|
|
438
|
-
backgroundColor,
|
|
439
|
-
selected = false,
|
|
440
|
-
depth = 0,
|
|
441
|
-
disabled = false,
|
|
442
|
-
hovered = false,
|
|
443
|
-
isSectionHeader = false,
|
|
444
|
-
sortable: overrideSortable,
|
|
445
|
-
onPress,
|
|
446
|
-
onDoubleClick,
|
|
447
|
-
onHoverChange,
|
|
448
|
-
children,
|
|
449
|
-
menuItems,
|
|
450
|
-
onContextMenu,
|
|
451
|
-
onSelectMenuItem,
|
|
452
|
-
onMenuOpenChange,
|
|
453
|
-
onKeyDown,
|
|
454
|
-
style,
|
|
455
|
-
dragIndicatorStyle,
|
|
456
|
-
className,
|
|
457
|
-
testRelativeDropPosition,
|
|
458
|
-
href,
|
|
459
|
-
as,
|
|
460
|
-
}: ListViewRowProps<MenuItemType>,
|
|
461
|
-
forwardedRef: ForwardedRef<HTMLElement>
|
|
462
|
-
) {
|
|
463
|
-
depth = Math.max(0, depth);
|
|
464
|
-
|
|
465
|
-
const {
|
|
466
|
-
marginType,
|
|
467
|
-
selectedPosition,
|
|
468
|
-
sortable,
|
|
469
|
-
pressEventName,
|
|
470
|
-
variant,
|
|
471
|
-
sectionHeaderVariant,
|
|
472
|
-
divider,
|
|
473
|
-
gap: listGap,
|
|
474
|
-
colorScheme,
|
|
475
|
-
} = useContext(ListRowContext);
|
|
476
|
-
const { hoverProps } = useHover({
|
|
477
|
-
onHoverChange,
|
|
478
|
-
});
|
|
479
|
-
const { indentation, isDragOverlay } = useContext(ListViewDraggingContext);
|
|
480
|
-
|
|
481
|
-
const handlePress = useCallback(
|
|
482
|
-
(event: React.MouseEvent) => {
|
|
483
|
-
// We use preventDefault as a hack to mark this event as handled. We check for
|
|
484
|
-
// this in the ListView.Root. We can't stopPropagation here or existing ContextMenus
|
|
485
|
-
// won't close (onPointerDownOutside won't fire).
|
|
486
|
-
// event.preventDefault();
|
|
487
|
-
|
|
488
|
-
// We only want to handle left clicks
|
|
489
|
-
if (event.button !== 0) return;
|
|
490
|
-
|
|
491
|
-
onPress?.(event);
|
|
492
|
-
},
|
|
493
|
-
[onPress]
|
|
494
|
-
);
|
|
495
|
-
|
|
496
|
-
const handleDoubleClick = useCallback(
|
|
497
|
-
(event: React.MouseEvent) => {
|
|
498
|
-
event.stopPropagation();
|
|
499
|
-
|
|
500
|
-
onDoubleClick?.();
|
|
501
|
-
},
|
|
502
|
-
[onDoubleClick]
|
|
503
|
-
);
|
|
504
|
-
|
|
505
|
-
const mergedStyle = useMemo(() => {
|
|
506
|
-
return isDragOverlay
|
|
507
|
-
? {
|
|
508
|
-
// TODO: Where do these offsets actually come from?
|
|
509
|
-
transform: `translate3d(-36px, -6px, 0)`,
|
|
510
|
-
width: `calc(100% + 48px)`,
|
|
511
|
-
...style,
|
|
512
|
-
}
|
|
513
|
-
: style;
|
|
514
|
-
}, [isDragOverlay, style]);
|
|
515
|
-
|
|
516
|
-
const renderContent = (
|
|
517
|
-
{
|
|
518
|
-
relativeDropPosition: relativeDropPositionProp,
|
|
519
|
-
...renderProps
|
|
520
|
-
}: React.ComponentProps<typeof RowContainer> & {
|
|
521
|
-
relativeDropPosition?: RelativeDropPosition;
|
|
522
|
-
},
|
|
523
|
-
ref: Ref<HTMLElement>
|
|
524
|
-
) => {
|
|
525
|
-
const Component = RowContainer;
|
|
526
|
-
const relativeDropPosition =
|
|
527
|
-
testRelativeDropPosition ?? relativeDropPositionProp;
|
|
528
|
-
|
|
529
|
-
const element = (
|
|
530
|
-
<Component
|
|
531
|
-
as={as}
|
|
532
|
-
href={href}
|
|
533
|
-
ref={ref as any}
|
|
534
|
-
$colorScheme={colorScheme}
|
|
535
|
-
onContextMenu={onContextMenu}
|
|
536
|
-
$isSectionHeader={isSectionHeader}
|
|
537
|
-
id={id}
|
|
538
|
-
role={role}
|
|
539
|
-
$gap={gap ?? 0}
|
|
540
|
-
$backgroundColor={backgroundColor}
|
|
541
|
-
{...hoverProps}
|
|
542
|
-
onDoubleClick={handleDoubleClick}
|
|
543
|
-
$marginType={marginType ?? "none"}
|
|
544
|
-
$disabled={disabled}
|
|
545
|
-
$hovered={hovered}
|
|
546
|
-
$selected={selected}
|
|
547
|
-
$variant={variant}
|
|
548
|
-
$sectionHeaderVariant={sectionHeaderVariant}
|
|
549
|
-
$selectedPosition={selectedPosition}
|
|
550
|
-
$showsActiveState={pressEventName === "onClick"}
|
|
551
|
-
aria-selected={selected}
|
|
552
|
-
$divider={!isDragOverlay && divider}
|
|
553
|
-
onKeyDown={onKeyDown}
|
|
554
|
-
style={mergedStyle}
|
|
555
|
-
{...(renderProps as Partial<React.ComponentProps<typeof RowContainer>>)}
|
|
556
|
-
{...mergeEventHandlers(
|
|
557
|
-
{ onPointerDown: renderProps.onPointerDown! },
|
|
558
|
-
{ [pressEventName]: handlePress }
|
|
559
|
-
)}
|
|
560
|
-
tabIndex={tabIndex}
|
|
561
|
-
className={className}
|
|
562
|
-
$clickable={!!onPress}
|
|
563
|
-
>
|
|
564
|
-
{relativeDropPosition && (
|
|
565
|
-
<ListViewDragIndicatorElement
|
|
566
|
-
key={relativeDropPosition}
|
|
567
|
-
$colorScheme={colorScheme}
|
|
568
|
-
$relativeDropPosition={relativeDropPosition}
|
|
569
|
-
$offsetLeft={depth * indentation}
|
|
570
|
-
$gap={listGap + (divider ? 1 : 0)}
|
|
571
|
-
style={
|
|
572
|
-
typeof dragIndicatorStyle === "function"
|
|
573
|
-
? dragIndicatorStyle({
|
|
574
|
-
depth,
|
|
575
|
-
indentation,
|
|
576
|
-
position: relativeDropPosition,
|
|
577
|
-
})
|
|
578
|
-
: dragIndicatorStyle
|
|
579
|
-
}
|
|
580
|
-
/>
|
|
581
|
-
)}
|
|
582
|
-
{depth > 0 && <Spacer.Horizontal size={depth * indentation} />}
|
|
583
|
-
{children}
|
|
584
|
-
</Component>
|
|
585
|
-
);
|
|
586
|
-
|
|
587
|
-
if (menuItems && onSelectMenuItem) {
|
|
588
|
-
return (
|
|
589
|
-
<ContextMenu<MenuItemType>
|
|
590
|
-
items={menuItems}
|
|
591
|
-
onSelect={onSelectMenuItem}
|
|
592
|
-
onOpenChange={onMenuOpenChange}
|
|
593
|
-
>
|
|
594
|
-
{element}
|
|
595
|
-
</ContextMenu>
|
|
596
|
-
);
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
return element;
|
|
600
|
-
};
|
|
601
|
-
|
|
602
|
-
if (sortable && id) {
|
|
603
|
-
return (
|
|
604
|
-
<Sortable.Item<HTMLElement> id={id} disabled={overrideSortable === false}>
|
|
605
|
-
{({ ref: sortableRef, ...sortableProps }) =>
|
|
606
|
-
renderContent(
|
|
607
|
-
sortableProps as any,
|
|
608
|
-
composeRefs(sortableRef, forwardedRef)
|
|
609
|
-
)
|
|
610
|
-
}
|
|
611
|
-
</Sortable.Item>
|
|
612
|
-
);
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
return renderContent({} as any, forwardedRef);
|
|
616
|
-
});
|
|
617
|
-
|
|
618
|
-
/* ----------------------------------------------------------------------------
|
|
619
|
-
* VirtualizedListRow
|
|
620
|
-
* ------------------------------------------------------------------------- */
|
|
621
|
-
|
|
622
|
-
const RenderItemContext = createContext<(index: number) => ReactNode>(
|
|
623
|
-
() => null
|
|
624
|
-
);
|
|
625
|
-
(RenderItemContext as any).displayName = "RenderItemContext";
|
|
626
|
-
|
|
627
|
-
const VirtualizedListRow = memo(function VirtualizedListRow({
|
|
628
|
-
index,
|
|
629
|
-
style,
|
|
630
|
-
}: ListChildComponentProps) {
|
|
631
|
-
const renderItem = useContext(RenderItemContext);
|
|
632
|
-
|
|
633
|
-
return (
|
|
634
|
-
<div key={index} style={style}>
|
|
635
|
-
{renderItem(index)}
|
|
636
|
-
</div>
|
|
637
|
-
);
|
|
638
|
-
});
|
|
639
|
-
|
|
640
|
-
/* ----------------------------------------------------------------------------
|
|
641
|
-
* VirtualizedList
|
|
642
|
-
* ------------------------------------------------------------------------- */
|
|
643
|
-
|
|
644
|
-
interface VirtualizedListProps<T> {
|
|
645
|
-
size: Size;
|
|
646
|
-
scrollElement: HTMLDivElement;
|
|
647
|
-
items: T[];
|
|
648
|
-
getItemHeight: (index: number) => number;
|
|
649
|
-
keyExtractor: (index: number) => string;
|
|
650
|
-
renderItem: (index: number) => ReactNode;
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
export interface IVirtualizedList {
|
|
654
|
-
scrollToIndex(index: number): void;
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
const VirtualizedListInner = forwardRefGeneric(function VirtualizedListInner<T>(
|
|
658
|
-
{
|
|
659
|
-
size,
|
|
660
|
-
scrollElement,
|
|
661
|
-
items,
|
|
662
|
-
getItemHeight,
|
|
663
|
-
keyExtractor,
|
|
664
|
-
renderItem,
|
|
665
|
-
}: VirtualizedListProps<T>,
|
|
666
|
-
ref: ForwardedRef<IVirtualizedList>
|
|
667
|
-
) {
|
|
668
|
-
const listRef = useRef<VariableSizeList<T> | null>(null);
|
|
669
|
-
|
|
670
|
-
useImperativeHandle(ref, () => ({
|
|
671
|
-
scrollToIndex(index) {
|
|
672
|
-
listRef.current?.scrollToItem(index);
|
|
673
|
-
},
|
|
674
|
-
}));
|
|
675
|
-
|
|
676
|
-
useLayoutEffect(() => {
|
|
677
|
-
listRef.current?.resetAfterIndex(0);
|
|
678
|
-
}, [
|
|
679
|
-
// When items change, we need to re-render the virtualized list,
|
|
680
|
-
// since it doesn't currently support row height changes
|
|
681
|
-
items,
|
|
682
|
-
]);
|
|
683
|
-
|
|
684
|
-
// Internally, react-virtualized updates these properties. We always want
|
|
685
|
-
// to use our custom scroll element, so we override them. It may update
|
|
686
|
-
// overflowX/Y individually in addition to `overflow`, so we include all 3.
|
|
687
|
-
const listStyle = useMemo(
|
|
688
|
-
(): CSSProperties => ({
|
|
689
|
-
overflowX: "initial",
|
|
690
|
-
overflowY: "initial",
|
|
691
|
-
overflow: "initial",
|
|
692
|
-
}),
|
|
693
|
-
[]
|
|
694
|
-
);
|
|
695
|
-
|
|
696
|
-
return (
|
|
697
|
-
<RenderItemContext.Provider value={renderItem}>
|
|
698
|
-
<WindowScroller
|
|
699
|
-
scrollElement={scrollElement}
|
|
700
|
-
style={useMemo(() => ({ flex: "1 1 auto" }), [])}
|
|
701
|
-
>
|
|
702
|
-
{useCallback(
|
|
703
|
-
({
|
|
704
|
-
registerChild,
|
|
705
|
-
onChildScroll,
|
|
706
|
-
scrollTop,
|
|
707
|
-
}: WindowScrollerChildProps & {
|
|
708
|
-
// Added when noya-designsystem moved to a separate package.
|
|
709
|
-
// I think this is a hack to get around the fact that react-virtualized
|
|
710
|
-
// doesn't update on scroll unless we force it to by changing ref.
|
|
711
|
-
// Either we're not using this the intended way or the types are wrong.
|
|
712
|
-
registerChild: (element: any) => void;
|
|
713
|
-
}) => (
|
|
714
|
-
<div ref={registerChild}>
|
|
715
|
-
<VariableSizeList<T>
|
|
716
|
-
ref={listRef}
|
|
717
|
-
// The list won't update on scroll unless we force it to by changing key
|
|
718
|
-
key={scrollTop}
|
|
719
|
-
style={listStyle}
|
|
720
|
-
itemKey={keyExtractor}
|
|
721
|
-
onScroll={({ scrollOffset }: { scrollOffset: number }) => {
|
|
722
|
-
onChildScroll({ scrollTop: scrollOffset });
|
|
723
|
-
}}
|
|
724
|
-
initialScrollOffset={scrollTop}
|
|
725
|
-
width={size.width}
|
|
726
|
-
height={size.height}
|
|
727
|
-
itemCount={items.length}
|
|
728
|
-
itemSize={getItemHeight}
|
|
729
|
-
estimatedItemSize={ROW_HEIGHT}
|
|
730
|
-
>
|
|
731
|
-
{VirtualizedListRow as any}
|
|
732
|
-
</VariableSizeList>
|
|
733
|
-
</div>
|
|
734
|
-
),
|
|
735
|
-
[
|
|
736
|
-
listStyle,
|
|
737
|
-
keyExtractor,
|
|
738
|
-
size.width,
|
|
739
|
-
size.height,
|
|
740
|
-
items.length,
|
|
741
|
-
getItemHeight,
|
|
742
|
-
]
|
|
743
|
-
)}
|
|
744
|
-
</WindowScroller>
|
|
745
|
-
</RenderItemContext.Provider>
|
|
746
|
-
);
|
|
747
|
-
});
|
|
748
|
-
|
|
749
|
-
const VirtualizedList = memo(
|
|
750
|
-
VirtualizedListInner
|
|
751
|
-
) as typeof VirtualizedListInner;
|
|
752
|
-
|
|
753
|
-
/* ----------------------------------------------------------------------------
|
|
754
|
-
* Root
|
|
755
|
-
* ------------------------------------------------------------------------- */
|
|
756
|
-
|
|
757
|
-
export type ListViewItemInfo = {
|
|
758
|
-
isDragOverlay: boolean;
|
|
759
|
-
activeDragIndex?: number;
|
|
760
|
-
};
|
|
761
|
-
|
|
762
|
-
type ChildrenProps = {
|
|
763
|
-
children: ReactNode;
|
|
764
|
-
};
|
|
765
|
-
|
|
766
|
-
type RenderProps<T> = {
|
|
767
|
-
data: T[];
|
|
768
|
-
renderItem: (item: T, index: number, info: ListViewItemInfo) => ReactNode;
|
|
769
|
-
keyExtractor: (item: T, index: number) => string;
|
|
770
|
-
/**
|
|
771
|
-
* Each item must have an `id` in order to be sortable.
|
|
772
|
-
*
|
|
773
|
-
* Additionally, the key extracted with `keyExtractor` must match the ListItem.Row `id`.
|
|
774
|
-
*/
|
|
775
|
-
sortable?: boolean;
|
|
776
|
-
virtualized?: Size;
|
|
777
|
-
};
|
|
778
|
-
|
|
779
|
-
type ListViewVariant = "normal" | "bare";
|
|
780
|
-
|
|
781
|
-
type ListViewSectionHeaderVariant = "normal" | "label";
|
|
782
|
-
|
|
783
|
-
export type ListViewRootProps = {
|
|
784
|
-
id?: string;
|
|
785
|
-
className?: string;
|
|
786
|
-
style?: CSSProperties;
|
|
787
|
-
role?: string;
|
|
788
|
-
"aria-labelledby"?: string;
|
|
789
|
-
"aria-orientation"?: "vertical" | "horizontal";
|
|
790
|
-
"aria-multiselectable"?: boolean;
|
|
791
|
-
onPress?: () => void;
|
|
792
|
-
scrollable?: boolean;
|
|
793
|
-
expandable?: boolean;
|
|
794
|
-
onMoveItem?: MoveDragItemHandler;
|
|
795
|
-
indentation?: number;
|
|
796
|
-
acceptsDrop?: DropValidator;
|
|
797
|
-
pressEventName?: PressEventName;
|
|
798
|
-
variant?: ListViewVariant;
|
|
799
|
-
sectionHeaderVariant?: ListViewSectionHeaderVariant;
|
|
800
|
-
divider?: boolean;
|
|
801
|
-
gap?: number;
|
|
802
|
-
colorScheme?: ListColorScheme;
|
|
803
|
-
containerRef?: React.RefObject<HTMLDivElement | null>;
|
|
804
|
-
onDragOver?: React.DragEventHandler<HTMLDivElement>;
|
|
805
|
-
onDragEnter?: React.DragEventHandler<HTMLDivElement>;
|
|
806
|
-
onDragLeave?: React.DragEventHandler<HTMLDivElement>;
|
|
807
|
-
onDrop?: React.DragEventHandler<HTMLDivElement>;
|
|
808
|
-
renderEmptyState?: () => ReactNode;
|
|
809
|
-
getDropTargetParentIndex?: SortableItemContextProps["getDropTargetParentIndex"];
|
|
810
|
-
sharedDragProps?: SharedDragProps;
|
|
811
|
-
sortableId?: string;
|
|
812
|
-
};
|
|
813
|
-
|
|
814
|
-
const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
815
|
-
{
|
|
816
|
-
id,
|
|
817
|
-
className,
|
|
818
|
-
style,
|
|
819
|
-
role,
|
|
820
|
-
"aria-labelledby": ariaLabelledby,
|
|
821
|
-
"aria-orientation": ariaOrientation,
|
|
822
|
-
"aria-multiselectable": ariaMultiselectable,
|
|
823
|
-
onPress,
|
|
824
|
-
scrollable = false,
|
|
825
|
-
expandable = true,
|
|
826
|
-
sortable = false,
|
|
827
|
-
divider = false,
|
|
828
|
-
onMoveItem,
|
|
829
|
-
indentation = 12,
|
|
830
|
-
acceptsDrop,
|
|
831
|
-
data,
|
|
832
|
-
renderItem,
|
|
833
|
-
keyExtractor,
|
|
834
|
-
virtualized,
|
|
835
|
-
variant = "normal",
|
|
836
|
-
sectionHeaderVariant = "normal",
|
|
837
|
-
pressEventName = "onClick",
|
|
838
|
-
colorScheme = "primary",
|
|
839
|
-
gap = 0,
|
|
840
|
-
containerRef,
|
|
841
|
-
onDragOver,
|
|
842
|
-
onDragEnter,
|
|
843
|
-
onDragLeave,
|
|
844
|
-
onDrop,
|
|
845
|
-
renderEmptyState,
|
|
846
|
-
getDropTargetParentIndex,
|
|
847
|
-
sharedDragProps,
|
|
848
|
-
sortableId,
|
|
849
|
-
}: RenderProps<T> & ListViewRootProps,
|
|
850
|
-
forwardedRef: ForwardedRef<IVirtualizedList>
|
|
851
|
-
) {
|
|
852
|
-
const handleClick = useCallback(
|
|
853
|
-
(event: React.MouseEvent) => {
|
|
854
|
-
if (
|
|
855
|
-
event.target instanceof HTMLElement &&
|
|
856
|
-
event.target.classList.contains("scroll-component")
|
|
857
|
-
)
|
|
858
|
-
return;
|
|
859
|
-
|
|
860
|
-
// As a hack, we call preventDefault in a row if the event was handled.
|
|
861
|
-
// If the event wasn't handled already, we call onPress here.
|
|
862
|
-
if (!event.isDefaultPrevented()) {
|
|
863
|
-
onPress?.();
|
|
864
|
-
}
|
|
865
|
-
},
|
|
866
|
-
[onPress]
|
|
867
|
-
);
|
|
868
|
-
|
|
869
|
-
const renderChild = useCallback(
|
|
870
|
-
(index: number) => renderItem(data[index], index, { isDragOverlay: false }),
|
|
871
|
-
[data, renderItem]
|
|
872
|
-
);
|
|
873
|
-
|
|
874
|
-
// TODO: Do we still use section header properties anywhere?
|
|
875
|
-
const defaultContextValue = useMemo(
|
|
876
|
-
(): ListRowContextValue => ({
|
|
877
|
-
colorScheme,
|
|
878
|
-
marginType: "none",
|
|
879
|
-
selectedPosition: "only",
|
|
880
|
-
sortable,
|
|
881
|
-
expandable,
|
|
882
|
-
divider,
|
|
883
|
-
pressEventName,
|
|
884
|
-
variant,
|
|
885
|
-
gap,
|
|
886
|
-
sectionHeaderVariant,
|
|
887
|
-
isSectionHeader: false,
|
|
888
|
-
}),
|
|
889
|
-
[
|
|
890
|
-
colorScheme,
|
|
891
|
-
sortable,
|
|
892
|
-
expandable,
|
|
893
|
-
divider,
|
|
894
|
-
pressEventName,
|
|
895
|
-
variant,
|
|
896
|
-
gap,
|
|
897
|
-
sectionHeaderVariant,
|
|
898
|
-
]
|
|
899
|
-
);
|
|
900
|
-
|
|
901
|
-
const getItemContextValue = useCallback(
|
|
902
|
-
(i: number): ListRowContextValue | undefined => {
|
|
903
|
-
if (variant === "bare" || variant === "normal")
|
|
904
|
-
return defaultContextValue;
|
|
905
|
-
|
|
906
|
-
const current = renderChild(i);
|
|
907
|
-
|
|
908
|
-
if (!isValidElement(current)) return;
|
|
909
|
-
|
|
910
|
-
const prevChild = i - 1 >= 0 && renderChild(i - 1);
|
|
911
|
-
const nextChild = i + 1 < data.length && renderChild(i + 1);
|
|
912
|
-
|
|
913
|
-
const next: ReactElement<any> | undefined = isValidElement(nextChild)
|
|
914
|
-
? nextChild
|
|
915
|
-
: undefined;
|
|
916
|
-
const prev: ReactElement<any> | undefined = isValidElement(prevChild)
|
|
917
|
-
? prevChild
|
|
918
|
-
: undefined;
|
|
919
|
-
|
|
920
|
-
const hasMarginTop = !prev;
|
|
921
|
-
const hasMarginBottom =
|
|
922
|
-
!next ||
|
|
923
|
-
(current as any).props.isSectionHeader ||
|
|
924
|
-
(next && (next as any).props.isSectionHeader);
|
|
925
|
-
|
|
926
|
-
let marginType: ListRowMarginType;
|
|
927
|
-
|
|
928
|
-
if (hasMarginTop && hasMarginBottom) {
|
|
929
|
-
marginType = "vertical";
|
|
930
|
-
} else if (hasMarginBottom) {
|
|
931
|
-
marginType = "bottom";
|
|
932
|
-
} else if (hasMarginTop) {
|
|
933
|
-
marginType = "top";
|
|
934
|
-
} else {
|
|
935
|
-
marginType = "none";
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
let selectedPosition: ListRowPosition = "only";
|
|
939
|
-
|
|
940
|
-
if ((current as any).props.selected) {
|
|
941
|
-
const nextSelected =
|
|
942
|
-
next && !next.props.isSectionHeader && (next as any).props.selected;
|
|
943
|
-
const prevSelected =
|
|
944
|
-
prev && !prev.props.isSectionHeader && (prev as any).props.selected;
|
|
945
|
-
|
|
946
|
-
if (nextSelected && prevSelected) {
|
|
947
|
-
selectedPosition = "middle";
|
|
948
|
-
} else if (nextSelected && !prevSelected) {
|
|
949
|
-
selectedPosition = "first";
|
|
950
|
-
} else if (!nextSelected && prevSelected) {
|
|
951
|
-
selectedPosition = "last";
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
return {
|
|
956
|
-
colorScheme,
|
|
957
|
-
marginType,
|
|
958
|
-
selectedPosition,
|
|
959
|
-
sortable,
|
|
960
|
-
expandable,
|
|
961
|
-
divider,
|
|
962
|
-
pressEventName,
|
|
963
|
-
variant,
|
|
964
|
-
sectionHeaderVariant,
|
|
965
|
-
isSectionHeader: (current as any).props.isSectionHeader,
|
|
966
|
-
gap,
|
|
967
|
-
};
|
|
968
|
-
},
|
|
969
|
-
[
|
|
970
|
-
variant,
|
|
971
|
-
defaultContextValue,
|
|
972
|
-
renderChild,
|
|
973
|
-
data.length,
|
|
974
|
-
colorScheme,
|
|
975
|
-
sortable,
|
|
976
|
-
expandable,
|
|
977
|
-
divider,
|
|
978
|
-
pressEventName,
|
|
979
|
-
sectionHeaderVariant,
|
|
980
|
-
gap,
|
|
981
|
-
]
|
|
982
|
-
);
|
|
983
|
-
|
|
984
|
-
const draggingContextOverlayValue = useMemo(
|
|
985
|
-
(): ListViewDraggingContextValue => ({ indentation, isDragOverlay: true }),
|
|
986
|
-
[indentation]
|
|
987
|
-
);
|
|
988
|
-
|
|
989
|
-
const renderOverlay = useCallback(
|
|
990
|
-
(index: number) => (
|
|
991
|
-
<div style={{ opacity: 0.25 }}>
|
|
992
|
-
<ListViewDraggingContext.Provider value={draggingContextOverlayValue}>
|
|
993
|
-
{renderItem(data[index], index, { isDragOverlay: true })}
|
|
994
|
-
</ListViewDraggingContext.Provider>
|
|
995
|
-
</div>
|
|
996
|
-
),
|
|
997
|
-
[draggingContextOverlayValue, renderItem, data]
|
|
998
|
-
);
|
|
999
|
-
|
|
1000
|
-
const renderWrappedChild = useCallback(
|
|
1001
|
-
(index: number) => {
|
|
1002
|
-
const contextValue = getItemContextValue(index);
|
|
1003
|
-
const current = renderChild(index);
|
|
1004
|
-
|
|
1005
|
-
if (!contextValue || !isValidElement(current)) return null;
|
|
1006
|
-
|
|
1007
|
-
return (
|
|
1008
|
-
<ListRowContext.Provider key={current.key} value={contextValue}>
|
|
1009
|
-
{current}
|
|
1010
|
-
</ListRowContext.Provider>
|
|
1011
|
-
);
|
|
1012
|
-
},
|
|
1013
|
-
[getItemContextValue, renderChild]
|
|
1014
|
-
);
|
|
1015
|
-
|
|
1016
|
-
const ids = useMemo(() => data.map(keyExtractor), [keyExtractor, data]);
|
|
1017
|
-
|
|
1018
|
-
const withSortable = (children: ReactNode) =>
|
|
1019
|
-
sortable ? (
|
|
1020
|
-
<Sortable.Root
|
|
1021
|
-
id={sortableId}
|
|
1022
|
-
onMoveItem={onMoveItem}
|
|
1023
|
-
keys={ids}
|
|
1024
|
-
renderOverlay={renderOverlay}
|
|
1025
|
-
acceptsDrop={acceptsDrop}
|
|
1026
|
-
getDropTargetParentIndex={getDropTargetParentIndex}
|
|
1027
|
-
sharedDragProps={sharedDragProps}
|
|
1028
|
-
>
|
|
1029
|
-
{children}
|
|
1030
|
-
</Sortable.Root>
|
|
1031
|
-
) : (
|
|
1032
|
-
children
|
|
1033
|
-
);
|
|
1034
|
-
|
|
1035
|
-
const withScrollable = (
|
|
1036
|
-
children: (scrollElementRef: HTMLDivElement | null) => ReactNode
|
|
1037
|
-
) => (scrollable ? <ScrollArea>{children}</ScrollArea> : children(null));
|
|
1038
|
-
|
|
1039
|
-
const getItemHeight = useCallback(
|
|
1040
|
-
(index: number) => {
|
|
1041
|
-
const child = getItemContextValue(index);
|
|
1042
|
-
const height =
|
|
1043
|
-
child?.isSectionHeader && child.sectionHeaderVariant === "label"
|
|
1044
|
-
? SECTION_HEADER_LABEL_HEIGHT
|
|
1045
|
-
: ROW_HEIGHT;
|
|
1046
|
-
return height;
|
|
1047
|
-
},
|
|
1048
|
-
[getItemContextValue]
|
|
1049
|
-
);
|
|
1050
|
-
|
|
1051
|
-
const getKey = useCallback(
|
|
1052
|
-
(index: number) => keyExtractor(data[index], index),
|
|
1053
|
-
[data, keyExtractor]
|
|
1054
|
-
);
|
|
1055
|
-
|
|
1056
|
-
const draggingContextValue = useMemo(() => ({ indentation }), [indentation]);
|
|
1057
|
-
const gapStyle = useMemo(() => ({ gap: `${gap}px` }), [gap]);
|
|
1058
|
-
|
|
1059
|
-
return (
|
|
1060
|
-
<ListViewDraggingContext.Provider value={draggingContextValue}>
|
|
1061
|
-
<div
|
|
1062
|
-
id={id}
|
|
1063
|
-
role={role}
|
|
1064
|
-
aria-labelledby={ariaLabelledby}
|
|
1065
|
-
aria-orientation={ariaOrientation}
|
|
1066
|
-
aria-multiselectable={ariaMultiselectable}
|
|
1067
|
-
className={mergeConflictingClassNames(
|
|
1068
|
-
[
|
|
1069
|
-
`flex flex-col text-text-muted ${scrollable ? "flex-1" : "flex-none"} `,
|
|
1070
|
-
className,
|
|
1071
|
-
],
|
|
1072
|
-
{
|
|
1073
|
-
categories: ["flex"],
|
|
1074
|
-
}
|
|
1075
|
-
)}
|
|
1076
|
-
style={{ ...gapStyle, ...style }}
|
|
1077
|
-
{...{
|
|
1078
|
-
[pressEventName]: handleClick,
|
|
1079
|
-
}}
|
|
1080
|
-
ref={containerRef}
|
|
1081
|
-
onDragOver={onDragOver}
|
|
1082
|
-
onDragEnter={onDragEnter}
|
|
1083
|
-
onDragLeave={onDragLeave}
|
|
1084
|
-
onDrop={onDrop}
|
|
1085
|
-
>
|
|
1086
|
-
{data.length === 0 && renderEmptyState
|
|
1087
|
-
? renderEmptyState()
|
|
1088
|
-
: withScrollable((scrollElementRef: HTMLDivElement | null) =>
|
|
1089
|
-
withSortable(
|
|
1090
|
-
virtualized ? (
|
|
1091
|
-
<VirtualizedList<T>
|
|
1092
|
-
ref={forwardedRef}
|
|
1093
|
-
scrollElement={scrollElementRef!}
|
|
1094
|
-
items={data}
|
|
1095
|
-
size={virtualized}
|
|
1096
|
-
getItemHeight={getItemHeight}
|
|
1097
|
-
keyExtractor={getKey}
|
|
1098
|
-
renderItem={renderWrappedChild}
|
|
1099
|
-
/>
|
|
1100
|
-
) : (
|
|
1101
|
-
range(0, data.length).map(renderWrappedChild)
|
|
1102
|
-
)
|
|
1103
|
-
)
|
|
1104
|
-
)}
|
|
1105
|
-
</div>
|
|
1106
|
-
</ListViewDraggingContext.Provider>
|
|
1107
|
-
);
|
|
1108
|
-
});
|
|
1109
|
-
|
|
1110
|
-
const ListViewRoot = memoGeneric(ListViewRootInner) as typeof ListViewRootInner;
|
|
1111
|
-
|
|
1112
|
-
const ChildrenListViewInner = forwardRef(function ChildrenListViewInner(
|
|
1113
|
-
{ children, ...rest }: ChildrenProps & ListViewRootProps,
|
|
1114
|
-
forwardedRef: ForwardedRef<IVirtualizedList>
|
|
1115
|
-
) {
|
|
1116
|
-
const items: ReactElement<any>[] = useMemo(
|
|
1117
|
-
() =>
|
|
1118
|
-
Children.toArray(children).flatMap((child) =>
|
|
1119
|
-
isValidElement(child) ? [child] : []
|
|
1120
|
-
),
|
|
1121
|
-
[children]
|
|
1122
|
-
);
|
|
1123
|
-
|
|
1124
|
-
return (
|
|
1125
|
-
<ListViewRoot
|
|
1126
|
-
ref={forwardedRef}
|
|
1127
|
-
{...rest}
|
|
1128
|
-
data={items}
|
|
1129
|
-
keyExtractor={useCallback(
|
|
1130
|
-
({ key }: { key: string | number | null }, index: number) =>
|
|
1131
|
-
typeof key === "string" ? key : (key ?? index).toString(),
|
|
1132
|
-
[]
|
|
1133
|
-
)}
|
|
1134
|
-
renderItem={useCallback((item: ReactElement<any>) => item, [])}
|
|
1135
|
-
/>
|
|
1136
|
-
);
|
|
1137
|
-
});
|
|
1138
|
-
|
|
1139
|
-
const ChildrenListView = memo(ChildrenListViewInner);
|
|
1140
|
-
|
|
1141
|
-
const SimpleListViewInner = forwardRefGeneric(function SimpleListViewInner<
|
|
1142
|
-
T = any,
|
|
1143
|
-
>(
|
|
1144
|
-
props: (ChildrenProps | RenderProps<T>) & ListViewRootProps,
|
|
1145
|
-
forwardedRef: ForwardedRef<IVirtualizedList>
|
|
1146
|
-
) {
|
|
1147
|
-
if ("children" in props) {
|
|
1148
|
-
return <ChildrenListView ref={forwardedRef} {...props} />;
|
|
1149
|
-
} else {
|
|
1150
|
-
return <ListViewRoot ref={forwardedRef} {...props} />;
|
|
1151
|
-
}
|
|
1152
|
-
});
|
|
1153
|
-
|
|
1154
|
-
/**
|
|
1155
|
-
* A ListView can be created either with `children` or render props
|
|
1156
|
-
*/
|
|
1157
|
-
const SimpleListView = memoGeneric(SimpleListViewInner);
|
|
7
|
+
ListViewItemInfo,
|
|
8
|
+
ListViewRoot,
|
|
9
|
+
ROW_HEIGHT,
|
|
10
|
+
SECTION_HEADER_LABEL_HEIGHT,
|
|
11
|
+
} from "./listView/ListViewRoot";
|
|
12
|
+
import {
|
|
13
|
+
ListViewClickInfo,
|
|
14
|
+
ListViewDragIndicatorElement,
|
|
15
|
+
ListViewRow,
|
|
16
|
+
ListViewRowProps,
|
|
17
|
+
} from "./listView/ListViewRow";
|
|
18
|
+
import { ListViewRowTitle } from "./listView/ListViewRowTitle";
|
|
19
|
+
import { normalizeListTargetIndex } from "./sorting/sorting";
|
|
1158
20
|
|
|
1159
21
|
export namespace ListView {
|
|
1160
|
-
export const RowTitle =
|
|
1161
|
-
export const EditableRowTitle =
|
|
1162
|
-
export const Row =
|
|
1163
|
-
export const Root =
|
|
22
|
+
export const RowTitle = ListViewRowTitle;
|
|
23
|
+
export const EditableRowTitle = ListViewEditableRowTitle;
|
|
24
|
+
export const Row = ListViewRow;
|
|
25
|
+
export const Root = ListViewRoot;
|
|
1164
26
|
export const RowContext = ListRowContext;
|
|
1165
27
|
export type ClickInfo = ListViewClickInfo;
|
|
1166
28
|
export type ItemInfo = ListViewItemInfo;
|