@noya-app/noya-designsystem 0.1.48 → 0.1.49
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 +10 -10
- package/CHANGELOG.md +8 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +213 -164
- package/dist/index.d.ts +213 -164
- package/dist/index.js +3583 -3290
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3617 -3329
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
- package/src/__tests__/validateDropIndicator.test.ts +4 -4
- package/src/components/ActionMenu.tsx +15 -4
- package/src/components/BaseToolbar.tsx +2 -2
- package/src/components/Checkbox.tsx +2 -2
- package/src/components/Chip.tsx +7 -6
- package/src/components/Collection.tsx +19 -1
- package/src/components/Combobox.tsx +22 -23
- package/src/components/ComboboxMenu.tsx +1 -1
- package/src/components/DimensionInput.tsx +14 -12
- package/src/components/EditableText.tsx +3 -1
- package/src/components/FillInputField.tsx +2 -2
- package/src/components/Grid.tsx +133 -113
- package/src/components/GridView.tsx +36 -18
- package/src/components/InputField.tsx +116 -171
- package/src/components/Label.tsx +4 -68
- package/src/components/LabeledField.tsx +7 -1
- package/src/components/List.tsx +103 -44
- package/src/components/ListView.tsx +42 -26
- package/src/components/MediaThumbnail.tsx +3 -3
- package/src/components/Message.tsx +8 -8
- package/src/components/NoyaLogo.tsx +41 -0
- package/src/components/SegmentedControl.tsx +1 -1
- package/src/components/SelectMenu.tsx +9 -4
- package/src/components/Slider.tsx +16 -7
- package/src/components/Sortable.tsx +62 -25
- package/src/components/internal/Menu.tsx +8 -3
- package/src/components/internal/SelectItem.tsx +3 -2
- package/src/index.css +7 -1
- package/src/index.tsx +2 -0
- package/src/theme/index.ts +2 -0
- package/src/utils/classNames.ts +51 -3
- package/src/utils/inputs.ts +21 -0
- package/tailwind.config.ts +7 -0
package/src/components/Label.tsx
CHANGED
|
@@ -1,72 +1,9 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
CSSProperties,
|
|
3
|
-
LabelHTMLAttributes,
|
|
4
|
-
forwardRef,
|
|
5
|
-
memo,
|
|
6
|
-
useMemo,
|
|
7
|
-
} from "react";
|
|
1
|
+
import React, { LabelHTMLAttributes, forwardRef, memo } from "react";
|
|
8
2
|
import { cx } from "../utils/classNames";
|
|
9
|
-
import { InputFieldSize } from "./InputField";
|
|
10
3
|
|
|
11
4
|
export type LabelPosition = "inset" | "start" | "end" | "above";
|
|
12
5
|
|
|
13
|
-
export interface LabelProps extends LabelHTMLAttributes<HTMLLabelElement> {
|
|
14
|
-
/** @default start */
|
|
15
|
-
position?: Exclude<LabelPosition, "inset">;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export type InsetLabelProps = {
|
|
19
|
-
/** @default medium */
|
|
20
|
-
size?: InputFieldSize;
|
|
21
|
-
/** when used with an InputField.Root and label position is inset, it will add padding to the right of the label to account for the button */
|
|
22
|
-
buttonWidth?: number;
|
|
23
|
-
} & LabelHTMLAttributes<HTMLLabelElement>;
|
|
24
|
-
|
|
25
|
-
// apply to <label
|
|
26
|
-
export const insetLabelTextStyles = `font-sans text-label uppercase text-text-disabled leading-[19px] font-bold text-[60%] truncate pointer-events-none text-right`;
|
|
27
|
-
|
|
28
|
-
// InsetLabelContainer
|
|
29
|
-
export const insetLabelStyles = `flex items-center absolute top-0 bottom-0 z-label`;
|
|
30
|
-
const getInsetLabelPosition = ({
|
|
31
|
-
buttonWidth,
|
|
32
|
-
}: {
|
|
33
|
-
buttonWidth?: number;
|
|
34
|
-
}): CSSProperties => ({
|
|
35
|
-
right: `${6 + (buttonWidth ?? 0)}px`,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
export const InsetLabel = memo(
|
|
39
|
-
forwardRef<HTMLLabelElement, InsetLabelProps>(function InsetLabel(
|
|
40
|
-
{ children, buttonWidth, size = "medium", className, style, ...props },
|
|
41
|
-
ref
|
|
42
|
-
) {
|
|
43
|
-
const styles = useMemo(
|
|
44
|
-
() => ({
|
|
45
|
-
...getInsetLabelPosition({
|
|
46
|
-
buttonWidth,
|
|
47
|
-
}),
|
|
48
|
-
...style,
|
|
49
|
-
}),
|
|
50
|
-
[buttonWidth, style]
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<label
|
|
55
|
-
ref={ref}
|
|
56
|
-
style={styles}
|
|
57
|
-
className={cx(
|
|
58
|
-
insetLabelStyles,
|
|
59
|
-
insetLabelTextStyles,
|
|
60
|
-
size === "small" ? "min-h-[19px]" : "min-h-[27px]",
|
|
61
|
-
className
|
|
62
|
-
)}
|
|
63
|
-
{...props}
|
|
64
|
-
>
|
|
65
|
-
{children}
|
|
66
|
-
</label>
|
|
67
|
-
);
|
|
68
|
-
})
|
|
69
|
-
);
|
|
6
|
+
export interface LabelProps extends LabelHTMLAttributes<HTMLLabelElement> {}
|
|
70
7
|
|
|
71
8
|
const labelStyles = "flex items-center select-none z-label relative";
|
|
72
9
|
const labelTextStyles =
|
|
@@ -74,7 +11,7 @@ const labelTextStyles =
|
|
|
74
11
|
|
|
75
12
|
export const Label = memo(
|
|
76
13
|
forwardRef<HTMLLabelElement, LabelProps>(function Label(
|
|
77
|
-
{ className,
|
|
14
|
+
{ className, children, ...props },
|
|
78
15
|
ref
|
|
79
16
|
) {
|
|
80
17
|
return (
|
|
@@ -83,8 +20,7 @@ export const Label = memo(
|
|
|
83
20
|
className={cx(
|
|
84
21
|
labelStyles,
|
|
85
22
|
labelTextStyles,
|
|
86
|
-
"text-left min-h-
|
|
87
|
-
(position === "start" || position === "end") && "self-start",
|
|
23
|
+
"text-left min-h-input-height",
|
|
88
24
|
className
|
|
89
25
|
)}
|
|
90
26
|
{...props}
|
|
@@ -91,7 +91,13 @@ export const LabeledField = memo(function LabeledField({
|
|
|
91
91
|
<div className={labeledFieldClasses}>
|
|
92
92
|
<Label
|
|
93
93
|
htmlFor={fieldId}
|
|
94
|
-
position
|
|
94
|
+
// we do this so presence avatars are aligned to the right when position is above
|
|
95
|
+
// and when start/end position with textarea it aligns label to the top of the field
|
|
96
|
+
className={
|
|
97
|
+
labelPosition === "start" || labelPosition === "end"
|
|
98
|
+
? "self-start"
|
|
99
|
+
: undefined
|
|
100
|
+
}
|
|
95
101
|
style={labelStyle}
|
|
96
102
|
{...props}
|
|
97
103
|
>
|
package/src/components/List.tsx
CHANGED
|
@@ -3,14 +3,16 @@ import {
|
|
|
3
3
|
memoGeneric,
|
|
4
4
|
useFileDropTarget,
|
|
5
5
|
} from "@noya-app/react-utils";
|
|
6
|
-
import React, { useImperativeHandle, useRef, useState } from "react";
|
|
6
|
+
import React, { memo, useImperativeHandle, useRef, useState } from "react";
|
|
7
7
|
import { cssVars } from "../theme";
|
|
8
8
|
import { cx } from "../utils/classNames";
|
|
9
9
|
import { updateSelection } from "../utils/selection";
|
|
10
10
|
import { ActionMenu } from "./ActionMenu";
|
|
11
11
|
import { CollectionProps, CollectionRef } from "./Collection";
|
|
12
|
-
import {
|
|
12
|
+
import { GridViewSize } from "./Grid";
|
|
13
|
+
import { ListView, ListViewItemInfo } from "./ListView";
|
|
13
14
|
import { TreeView } from "./TreeView";
|
|
15
|
+
|
|
14
16
|
const cssVarOverrides = {
|
|
15
17
|
"--icon-selected": cssVars.colors.primary,
|
|
16
18
|
};
|
|
@@ -42,8 +44,10 @@ export const List = memoGeneric(
|
|
|
42
44
|
itemRoleDescription = "clickable item",
|
|
43
45
|
detailPosition = "end",
|
|
44
46
|
size = "medium",
|
|
47
|
+
thumbnailSize = "auto",
|
|
45
48
|
testHoveredId,
|
|
46
49
|
testRenamingId,
|
|
50
|
+
testShowDropIndicatorId,
|
|
47
51
|
scrollable = false,
|
|
48
52
|
acceptsDrop,
|
|
49
53
|
onMoveItem,
|
|
@@ -52,6 +56,10 @@ export const List = memoGeneric(
|
|
|
52
56
|
renamable,
|
|
53
57
|
selectedIds = emptyArray,
|
|
54
58
|
renderEmptyState,
|
|
59
|
+
sortable,
|
|
60
|
+
getDropTargetParentIndex,
|
|
61
|
+
getPlaceholder,
|
|
62
|
+
onClickItem,
|
|
55
63
|
} = props;
|
|
56
64
|
|
|
57
65
|
const [internalHoveredId, setHoveredId] = useState<string>();
|
|
@@ -116,19 +124,21 @@ export const List = memoGeneric(
|
|
|
116
124
|
aria-orientation="vertical"
|
|
117
125
|
aria-multiselectable={true}
|
|
118
126
|
expandable={expandable}
|
|
119
|
-
sortable={
|
|
127
|
+
sortable={sortable}
|
|
120
128
|
acceptsDrop={acceptsDrop}
|
|
121
129
|
onMoveItem={onMoveItem}
|
|
122
130
|
renderEmptyState={renderEmptyState}
|
|
131
|
+
getDropTargetParentIndex={getDropTargetParentIndex}
|
|
123
132
|
{...dropTargetProps}
|
|
124
133
|
renderItem={(
|
|
125
134
|
item: T,
|
|
126
135
|
_: number,
|
|
127
|
-
{
|
|
136
|
+
{ isDragOverlay }: ListViewItemInfo
|
|
128
137
|
) => {
|
|
129
138
|
const id = getId(item);
|
|
130
139
|
const isDropdownOpen = dropdownOpenId === id;
|
|
131
|
-
const isHovered =
|
|
140
|
+
const isHovered =
|
|
141
|
+
(hoveredId === id || isDropdownOpen) && !isDragOverlay;
|
|
132
142
|
const expanded = getExpanded?.(item);
|
|
133
143
|
const itemIsRenamable = getRenamable?.(item) ?? renamable;
|
|
134
144
|
const isSelected = selectedIds.includes(id);
|
|
@@ -139,24 +149,25 @@ export const List = memoGeneric(
|
|
|
139
149
|
item,
|
|
140
150
|
selected: isSelected,
|
|
141
151
|
});
|
|
152
|
+
const onOpenChange = (open: boolean) => {
|
|
153
|
+
setDropdownOpenId(open ? id : undefined);
|
|
154
|
+
|
|
155
|
+
if (open && !isSelected) {
|
|
156
|
+
handleSelect(id);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
142
159
|
const action =
|
|
143
160
|
(isHovered || isSelected) &&
|
|
144
161
|
!isRenaming &&
|
|
145
162
|
(typeof renderAction === "function"
|
|
146
|
-
? renderAction(item, isSelected)
|
|
163
|
+
? renderAction({ item, selected: isSelected, onOpenChange })
|
|
147
164
|
: renderAction === "menu" &&
|
|
148
165
|
menuItems && (
|
|
149
166
|
<ActionMenu
|
|
150
167
|
menuItems={menuItems}
|
|
151
168
|
onSelect={(action) => handleMenuAction(action, item)}
|
|
152
169
|
selected={isSelected}
|
|
153
|
-
onOpenChange={
|
|
154
|
-
setDropdownOpenId(open ? id : undefined);
|
|
155
|
-
|
|
156
|
-
if (open && !isSelected) {
|
|
157
|
-
handleSelect(id);
|
|
158
|
-
}
|
|
159
|
-
}}
|
|
170
|
+
onOpenChange={onOpenChange}
|
|
160
171
|
/>
|
|
161
172
|
));
|
|
162
173
|
|
|
@@ -171,14 +182,17 @@ export const List = memoGeneric(
|
|
|
171
182
|
id={id}
|
|
172
183
|
key={id}
|
|
173
184
|
role="option"
|
|
174
|
-
sortable={
|
|
185
|
+
sortable={sortable}
|
|
175
186
|
aria-roledescription={itemRoleDescription}
|
|
176
187
|
aria-label={getName(item)}
|
|
177
188
|
aria-selected={isSelected}
|
|
178
|
-
className={cx("cursor-pointer
|
|
189
|
+
className={cx("cursor-pointer rounded px-1", pyMap[size])}
|
|
179
190
|
backgroundColor={
|
|
180
191
|
isSelected ? cssVars.colors.primaryPastel : undefined
|
|
181
192
|
}
|
|
193
|
+
testRelativeDropPosition={
|
|
194
|
+
testShowDropIndicatorId === id ? "inside" : undefined
|
|
195
|
+
}
|
|
182
196
|
chevronClassName={cx(expandable && "relative left-2")}
|
|
183
197
|
style={cssVarOverrides as React.CSSProperties}
|
|
184
198
|
hovered={isHovered}
|
|
@@ -190,6 +204,7 @@ export const List = memoGeneric(
|
|
|
190
204
|
}
|
|
191
205
|
onPress={(e: ListView.ClickInfo) => {
|
|
192
206
|
handleSelect(id, e);
|
|
207
|
+
onClickItem?.(id);
|
|
193
208
|
}}
|
|
194
209
|
onDoubleClick={() => {
|
|
195
210
|
onDoubleClickItem?.(id);
|
|
@@ -215,44 +230,44 @@ export const List = memoGeneric(
|
|
|
215
230
|
setExpanded?.(item, !expanded);
|
|
216
231
|
}}
|
|
217
232
|
>
|
|
218
|
-
<div
|
|
233
|
+
<div
|
|
234
|
+
className={cx("flex flex-1 items-center px-2", rowGapMap[size])}
|
|
235
|
+
>
|
|
219
236
|
{thumbnail && (
|
|
220
237
|
<div
|
|
221
238
|
className={cx(
|
|
222
239
|
"rounded overflow-hidden flex-none flex items-center justify-center",
|
|
223
|
-
|
|
240
|
+
thumbnailSize === "auto" && thumbnailSizeMap[size],
|
|
224
241
|
isSelected && "text-primary"
|
|
225
242
|
)}
|
|
226
|
-
style={{
|
|
227
|
-
backgroundColor: isSelected
|
|
228
|
-
? cssVars.colors.primaryPastel
|
|
229
|
-
: cssVars.colors.inputBackground,
|
|
230
|
-
}}
|
|
231
243
|
tabIndex={-1}
|
|
232
244
|
>
|
|
233
245
|
{thumbnail}
|
|
234
246
|
</div>
|
|
235
247
|
)}
|
|
236
248
|
<div className="flex flex-col flex-1 w-0">
|
|
237
|
-
<div className="flex items-center min-h-
|
|
249
|
+
<div className="flex items-center min-h-input-height flex-1 gap-2">
|
|
238
250
|
<div className="flex-1 w-0 flex items-center" tabIndex={-1}>
|
|
239
251
|
{isRenaming ? (
|
|
240
252
|
<ViewComponent.EditableRowTitle
|
|
241
253
|
value={getName(item)}
|
|
242
|
-
placeholder="Enter name"
|
|
243
|
-
className=
|
|
254
|
+
placeholder={getPlaceholder?.(item) ?? "Enter name"}
|
|
255
|
+
className={fontStyleMap[size]}
|
|
244
256
|
autoFocus={!isTestingRenaming}
|
|
245
257
|
onSubmitEditing={(value: string) => {
|
|
246
|
-
if (value !== getName(item)) {
|
|
258
|
+
if (value !== getName(item) && value !== "") {
|
|
247
259
|
onRename?.(item, value);
|
|
248
260
|
}
|
|
249
261
|
setRenamingId(undefined);
|
|
250
262
|
}}
|
|
263
|
+
onKeyDown={(e: React.KeyboardEvent) => {
|
|
264
|
+
e.stopPropagation();
|
|
265
|
+
}}
|
|
251
266
|
/>
|
|
252
267
|
) : (
|
|
253
268
|
<ViewComponent.RowTitle
|
|
254
269
|
className={cx(
|
|
255
|
-
|
|
270
|
+
fontStyleMap[size],
|
|
256
271
|
isSelected && "text-primary"
|
|
257
272
|
)}
|
|
258
273
|
>
|
|
@@ -260,30 +275,21 @@ export const List = memoGeneric(
|
|
|
260
275
|
</ViewComponent.RowTitle>
|
|
261
276
|
)}
|
|
262
277
|
</div>
|
|
263
|
-
{end && (
|
|
264
|
-
<
|
|
265
|
-
className={cx(
|
|
266
|
-
"flex items-center gap-2",
|
|
267
|
-
isSelected && "text-primary"
|
|
268
|
-
)}
|
|
269
|
-
tabIndex={-1}
|
|
270
|
-
>
|
|
278
|
+
{end && detailPosition === "end" && (
|
|
279
|
+
<DetailContainer selected={isSelected}>
|
|
271
280
|
{end}
|
|
272
|
-
</
|
|
281
|
+
</DetailContainer>
|
|
273
282
|
)}
|
|
274
283
|
</div>
|
|
275
284
|
{below && (
|
|
276
|
-
<
|
|
277
|
-
className={cx(
|
|
278
|
-
"flex items-center",
|
|
279
|
-
isSelected && "text-primary"
|
|
280
|
-
)}
|
|
281
|
-
tabIndex={-1}
|
|
282
|
-
>
|
|
285
|
+
<DetailContainer selected={isSelected}>
|
|
283
286
|
{below}
|
|
284
|
-
</
|
|
287
|
+
</DetailContainer>
|
|
285
288
|
)}
|
|
286
289
|
</div>
|
|
290
|
+
{end && detailPosition === "below" && (
|
|
291
|
+
<DetailContainer selected={isSelected}>{end}</DetailContainer>
|
|
292
|
+
)}
|
|
287
293
|
</div>
|
|
288
294
|
</ViewComponent.Row>
|
|
289
295
|
);
|
|
@@ -292,3 +298,56 @@ export const List = memoGeneric(
|
|
|
292
298
|
);
|
|
293
299
|
})
|
|
294
300
|
);
|
|
301
|
+
|
|
302
|
+
const DetailContainer = memo(function DetailContainer({
|
|
303
|
+
children,
|
|
304
|
+
selected,
|
|
305
|
+
}: {
|
|
306
|
+
children: React.ReactNode;
|
|
307
|
+
selected: boolean;
|
|
308
|
+
}) {
|
|
309
|
+
return (
|
|
310
|
+
<div
|
|
311
|
+
className={cx("flex items-center gap-2", selected && "text-primary")}
|
|
312
|
+
tabIndex={-1}
|
|
313
|
+
>
|
|
314
|
+
{children}
|
|
315
|
+
</div>
|
|
316
|
+
);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const pyMap: Record<GridViewSize, string> = {
|
|
320
|
+
xxs: "py-1",
|
|
321
|
+
xs: "py-1",
|
|
322
|
+
small: "py-1",
|
|
323
|
+
medium: "py-1",
|
|
324
|
+
large: "py-2",
|
|
325
|
+
xl: "py-3",
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const thumbnailSizeMap: Record<GridViewSize, string> = {
|
|
329
|
+
xxs: "w-4 h-4",
|
|
330
|
+
xs: "w-4 h-4",
|
|
331
|
+
small: "w-6 h-6",
|
|
332
|
+
medium: "w-8 h-8",
|
|
333
|
+
large: "w-16 h-16",
|
|
334
|
+
xl: "w-24 h-24",
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const rowGapMap: Record<GridViewSize, string> = {
|
|
338
|
+
xxs: "gap-3",
|
|
339
|
+
xs: "gap-3",
|
|
340
|
+
small: "gap-3",
|
|
341
|
+
medium: "gap-3",
|
|
342
|
+
large: "gap-3",
|
|
343
|
+
xl: "gap-4",
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
const fontStyleMap: Record<GridViewSize, string> = {
|
|
347
|
+
xxs: "font-medium",
|
|
348
|
+
xs: "font-medium",
|
|
349
|
+
small: "font-medium",
|
|
350
|
+
medium: "font-medium",
|
|
351
|
+
large: "font-medium",
|
|
352
|
+
xl: "font-medium text-heading4 text-text",
|
|
353
|
+
};
|
|
@@ -25,7 +25,7 @@ import { WindowScroller, WindowScrollerChildProps } from "react-virtualized";
|
|
|
25
25
|
import { ListChildComponentProps, VariableSizeList } from "react-window";
|
|
26
26
|
import { mergeEventHandlers } from "../hooks/mergeEventHandlers";
|
|
27
27
|
import { useHover } from "../hooks/useHover";
|
|
28
|
-
import { cssVars } from "../theme";
|
|
28
|
+
import { cssVars, INPUT_HEIGHT } from "../theme";
|
|
29
29
|
import { cx } from "../utils/classNames";
|
|
30
30
|
import { ContextMenu } from "./ContextMenu";
|
|
31
31
|
import { InputField } from "./InputField";
|
|
@@ -33,9 +33,10 @@ import { MenuItem } from "./internal/Menu";
|
|
|
33
33
|
import { ScrollArea } from "./ScrollArea";
|
|
34
34
|
import {
|
|
35
35
|
DropValidator,
|
|
36
|
-
|
|
36
|
+
normalizeListTargetIndex,
|
|
37
37
|
RelativeDropPosition,
|
|
38
38
|
Sortable,
|
|
39
|
+
type SortableItemContextProps,
|
|
39
40
|
} from "./Sortable";
|
|
40
41
|
import { Spacer } from "./Spacer";
|
|
41
42
|
|
|
@@ -43,7 +44,7 @@ export type ListRowMarginType = "none" | "top" | "bottom" | "vertical";
|
|
|
43
44
|
export type ListRowPosition = "only" | "first" | "middle" | "last";
|
|
44
45
|
|
|
45
46
|
const ROW_HEIGHT = 31;
|
|
46
|
-
const SECTION_HEADER_LABEL_HEIGHT =
|
|
47
|
+
const SECTION_HEADER_LABEL_HEIGHT = INPUT_HEIGHT;
|
|
47
48
|
|
|
48
49
|
type ListColorScheme = "primary" | "secondary";
|
|
49
50
|
|
|
@@ -71,7 +72,7 @@ type ListRowContextValue = {
|
|
|
71
72
|
// and the dragged row doesn't have a ListRowContextValue.
|
|
72
73
|
type ListViewDraggingContextValue = {
|
|
73
74
|
indentation: number;
|
|
74
|
-
|
|
75
|
+
isDragOverlay?: boolean;
|
|
75
76
|
};
|
|
76
77
|
|
|
77
78
|
const ListViewDraggingContext = createContext<ListViewDraggingContextValue>({
|
|
@@ -127,6 +128,7 @@ export interface EditableRowProps {
|
|
|
127
128
|
autoFocus: boolean;
|
|
128
129
|
placeholder?: string;
|
|
129
130
|
className?: string;
|
|
131
|
+
onKeyDown?: (e: React.KeyboardEvent) => void;
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
function ListViewEditableRowTitle({
|
|
@@ -135,6 +137,7 @@ function ListViewEditableRowTitle({
|
|
|
135
137
|
autoFocus,
|
|
136
138
|
placeholder,
|
|
137
139
|
className,
|
|
140
|
+
onKeyDown,
|
|
138
141
|
}: EditableRowProps) {
|
|
139
142
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
140
143
|
|
|
@@ -161,6 +164,7 @@ function ListViewEditableRowTitle({
|
|
|
161
164
|
onSubmit={onSubmitEditing}
|
|
162
165
|
allowSubmittingWithSameValue
|
|
163
166
|
className={cx("-mx-1.5 -my-1 bg-listview-editing-background", className)}
|
|
167
|
+
onKeyDown={onKeyDown}
|
|
164
168
|
/>
|
|
165
169
|
);
|
|
166
170
|
}
|
|
@@ -266,9 +270,10 @@ const RowContainer = forwardRef<
|
|
|
266
270
|
borderBottomRightRadius: "0px",
|
|
267
271
|
borderBottomLeftRadius: "0px",
|
|
268
272
|
}),
|
|
269
|
-
...($hovered &&
|
|
270
|
-
|
|
271
|
-
|
|
273
|
+
...($hovered &&
|
|
274
|
+
!$selected && {
|
|
275
|
+
background: cssVars.colors.listViewHoverBackground,
|
|
276
|
+
}),
|
|
272
277
|
...($showsActiveState && {
|
|
273
278
|
"&:active": {
|
|
274
279
|
backgroundColor: $selected
|
|
@@ -301,7 +306,7 @@ const RowContainer = forwardRef<
|
|
|
301
306
|
const ListViewDragIndicatorElement = forwardRef<
|
|
302
307
|
HTMLDivElement,
|
|
303
308
|
React.HTMLAttributes<HTMLDivElement> & {
|
|
304
|
-
$relativeDropPosition
|
|
309
|
+
$relativeDropPosition?: RelativeDropPosition;
|
|
305
310
|
$gap: number;
|
|
306
311
|
$offsetLeft: number;
|
|
307
312
|
$colorScheme: ListColorScheme;
|
|
@@ -398,13 +403,16 @@ interface ListViewRowProps<MenuItemType extends string = string> {
|
|
|
398
403
|
className?: string;
|
|
399
404
|
dragIndicatorStyle?:
|
|
400
405
|
| CSSProperties
|
|
401
|
-
| ((props:
|
|
402
|
-
|
|
403
|
-
indentation: number;
|
|
404
|
-
position: RelativeDropPosition;
|
|
405
|
-
}) => CSSProperties);
|
|
406
|
+
| ((props: DragIndicatorStyleProps) => CSSProperties);
|
|
407
|
+
testRelativeDropPosition?: RelativeDropPosition;
|
|
406
408
|
}
|
|
407
409
|
|
|
410
|
+
export type DragIndicatorStyleProps = {
|
|
411
|
+
depth: number;
|
|
412
|
+
indentation: number;
|
|
413
|
+
position: RelativeDropPosition;
|
|
414
|
+
};
|
|
415
|
+
|
|
408
416
|
const ListViewRow = forwardRefGeneric(function ListViewRow<
|
|
409
417
|
MenuItemType extends string,
|
|
410
418
|
>(
|
|
@@ -432,6 +440,7 @@ const ListViewRow = forwardRefGeneric(function ListViewRow<
|
|
|
432
440
|
style,
|
|
433
441
|
dragIndicatorStyle,
|
|
434
442
|
className,
|
|
443
|
+
testRelativeDropPosition,
|
|
435
444
|
}: ListViewRowProps<MenuItemType>,
|
|
436
445
|
forwardedRef: ForwardedRef<HTMLElement>
|
|
437
446
|
) {
|
|
@@ -451,7 +460,7 @@ const ListViewRow = forwardRefGeneric(function ListViewRow<
|
|
|
451
460
|
const { hoverProps } = useHover({
|
|
452
461
|
onHoverChange,
|
|
453
462
|
});
|
|
454
|
-
const { indentation,
|
|
463
|
+
const { indentation, isDragOverlay } = useContext(ListViewDraggingContext);
|
|
455
464
|
|
|
456
465
|
const handlePress = useCallback(
|
|
457
466
|
(event: React.MouseEvent) => {
|
|
@@ -478,7 +487,7 @@ const ListViewRow = forwardRefGeneric(function ListViewRow<
|
|
|
478
487
|
);
|
|
479
488
|
|
|
480
489
|
const mergedStyle = useMemo(() => {
|
|
481
|
-
return
|
|
490
|
+
return isDragOverlay
|
|
482
491
|
? {
|
|
483
492
|
// TODO: Where do these offsets actually come from?
|
|
484
493
|
transform: `translate3d(-36px, -6px, 0)`,
|
|
@@ -486,11 +495,11 @@ const ListViewRow = forwardRefGeneric(function ListViewRow<
|
|
|
486
495
|
...style,
|
|
487
496
|
}
|
|
488
497
|
: style;
|
|
489
|
-
}, [
|
|
498
|
+
}, [isDragOverlay, style]);
|
|
490
499
|
|
|
491
500
|
const renderContent = (
|
|
492
501
|
{
|
|
493
|
-
relativeDropPosition,
|
|
502
|
+
relativeDropPosition: relativeDropPositionProp,
|
|
494
503
|
...renderProps
|
|
495
504
|
}: React.ComponentProps<typeof RowContainer> & {
|
|
496
505
|
relativeDropPosition?: RelativeDropPosition;
|
|
@@ -498,6 +507,8 @@ const ListViewRow = forwardRefGeneric(function ListViewRow<
|
|
|
498
507
|
ref: Ref<HTMLElement>
|
|
499
508
|
) => {
|
|
500
509
|
const Component = RowContainer;
|
|
510
|
+
const relativeDropPosition =
|
|
511
|
+
testRelativeDropPosition ?? relativeDropPositionProp;
|
|
501
512
|
|
|
502
513
|
const element = (
|
|
503
514
|
<Component
|
|
@@ -520,7 +531,7 @@ const ListViewRow = forwardRefGeneric(function ListViewRow<
|
|
|
520
531
|
$selectedPosition={selectedPosition}
|
|
521
532
|
$showsActiveState={pressEventName === "onClick"}
|
|
522
533
|
aria-selected={selected}
|
|
523
|
-
$divider={!
|
|
534
|
+
$divider={!isDragOverlay && divider}
|
|
524
535
|
onKeyDown={onKeyDown}
|
|
525
536
|
style={mergedStyle}
|
|
526
537
|
{...(renderProps as Partial<React.ComponentProps<typeof RowContainer>>)}
|
|
@@ -534,9 +545,10 @@ const ListViewRow = forwardRefGeneric(function ListViewRow<
|
|
|
534
545
|
>
|
|
535
546
|
{relativeDropPosition && (
|
|
536
547
|
<ListViewDragIndicatorElement
|
|
548
|
+
key={relativeDropPosition}
|
|
537
549
|
$colorScheme={colorScheme}
|
|
538
550
|
$relativeDropPosition={relativeDropPosition}
|
|
539
|
-
$offsetLeft={
|
|
551
|
+
$offsetLeft={depth * indentation}
|
|
540
552
|
$gap={listGap + (divider ? 1 : 0)}
|
|
541
553
|
style={
|
|
542
554
|
typeof dragIndicatorStyle === "function"
|
|
@@ -724,8 +736,9 @@ const VirtualizedList = memo(
|
|
|
724
736
|
* Root
|
|
725
737
|
* ------------------------------------------------------------------------- */
|
|
726
738
|
|
|
727
|
-
type ListViewItemInfo = {
|
|
728
|
-
|
|
739
|
+
export type ListViewItemInfo = {
|
|
740
|
+
isDragOverlay: boolean;
|
|
741
|
+
activeDragIndex?: number;
|
|
729
742
|
};
|
|
730
743
|
|
|
731
744
|
type ChildrenProps = {
|
|
@@ -762,7 +775,7 @@ export type ListViewRootProps = {
|
|
|
762
775
|
expandable?: boolean;
|
|
763
776
|
onMoveItem?: (
|
|
764
777
|
sourceIndex: number,
|
|
765
|
-
|
|
778
|
+
targetIndex: number,
|
|
766
779
|
position: RelativeDropPosition
|
|
767
780
|
) => void;
|
|
768
781
|
indentation?: number;
|
|
@@ -779,6 +792,7 @@ export type ListViewRootProps = {
|
|
|
779
792
|
onDragLeave?: React.DragEventHandler<HTMLDivElement>;
|
|
780
793
|
onDrop?: React.DragEventHandler<HTMLDivElement>;
|
|
781
794
|
renderEmptyState?: () => ReactNode;
|
|
795
|
+
getDropTargetParentIndex?: SortableItemContextProps["getDropTargetParentIndex"];
|
|
782
796
|
};
|
|
783
797
|
|
|
784
798
|
const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
@@ -813,6 +827,7 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
813
827
|
onDragLeave,
|
|
814
828
|
onDrop,
|
|
815
829
|
renderEmptyState,
|
|
830
|
+
getDropTargetParentIndex,
|
|
816
831
|
}: RenderProps<T> & ListViewRootProps,
|
|
817
832
|
forwardedRef: ForwardedRef<IVirtualizedList>
|
|
818
833
|
) {
|
|
@@ -834,7 +849,7 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
834
849
|
);
|
|
835
850
|
|
|
836
851
|
const renderChild = useCallback(
|
|
837
|
-
(index: number) => renderItem(data[index], index, {
|
|
852
|
+
(index: number) => renderItem(data[index], index, { isDragOverlay: false }),
|
|
838
853
|
[data, renderItem]
|
|
839
854
|
);
|
|
840
855
|
|
|
@@ -949,7 +964,7 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
949
964
|
);
|
|
950
965
|
|
|
951
966
|
const draggingContextOverlayValue = useMemo(
|
|
952
|
-
() => ({ indentation,
|
|
967
|
+
(): ListViewDraggingContextValue => ({ indentation, isDragOverlay: true }),
|
|
953
968
|
[indentation]
|
|
954
969
|
);
|
|
955
970
|
|
|
@@ -957,7 +972,7 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
957
972
|
(index: number) => (
|
|
958
973
|
<div style={{ opacity: 0.25 }}>
|
|
959
974
|
<ListViewDraggingContext.Provider value={draggingContextOverlayValue}>
|
|
960
|
-
{renderItem(data[index], index, {
|
|
975
|
+
{renderItem(data[index], index, { isDragOverlay: true })}
|
|
961
976
|
</ListViewDraggingContext.Provider>
|
|
962
977
|
</div>
|
|
963
978
|
),
|
|
@@ -989,6 +1004,7 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
|
|
|
989
1004
|
keys={ids}
|
|
990
1005
|
renderOverlay={renderOverlay}
|
|
991
1006
|
acceptsDrop={acceptsDrop}
|
|
1007
|
+
getDropTargetParentIndex={getDropTargetParentIndex}
|
|
992
1008
|
>
|
|
993
1009
|
{children}
|
|
994
1010
|
</Sortable.Root>
|
|
@@ -1140,5 +1156,5 @@ export namespace ListView {
|
|
|
1140
1156
|
menuItemsCount * rowHeight + sectionHeaderCount * sectionHeaderLabelHeight
|
|
1141
1157
|
);
|
|
1142
1158
|
};
|
|
1143
|
-
export const
|
|
1159
|
+
export const normalizeTargetIndex = normalizeListTargetIndex;
|
|
1144
1160
|
}
|
|
@@ -43,18 +43,18 @@ const getColors = (colorScheme: ColorScheme, selected: boolean) => {
|
|
|
43
43
|
case "primary":
|
|
44
44
|
return {
|
|
45
45
|
icon: cssVars.colors.primary,
|
|
46
|
-
background: cssVars.colors.
|
|
46
|
+
background: cssVars.colors.listViewThumbnailBackground,
|
|
47
47
|
};
|
|
48
48
|
case "warning":
|
|
49
49
|
return {
|
|
50
50
|
icon: cssVars.colors.warning,
|
|
51
|
-
background: cssVars.colors.
|
|
51
|
+
background: cssVars.colors.listViewThumbnailBackground,
|
|
52
52
|
};
|
|
53
53
|
case "icon":
|
|
54
54
|
default:
|
|
55
55
|
return {
|
|
56
56
|
icon: cssVars.colors.icon,
|
|
57
|
-
background: cssVars.colors.
|
|
57
|
+
background: cssVars.colors.listViewThumbnailBackground,
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
};
|