@noya-app/noya-designsystem 0.0.14 → 0.0.16
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/.eslintrc.js +11 -0
- package/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-lint.log +3 -0
- package/CHANGELOG.md +24 -0
- package/dist/index.d.mts +133 -111
- package/dist/index.d.ts +133 -111
- package/dist/index.js +7832 -646
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7819 -601
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -7
- package/src/components/Chip.tsx +101 -82
- package/src/components/Dialog.tsx +51 -26
- package/src/components/DraggableMenuButton.tsx +116 -0
- package/src/components/InputField.tsx +196 -132
- package/src/components/InputFieldWithCompletions.tsx +43 -6
- package/src/components/InspectorPrimitives.tsx +1 -1
- package/src/components/ListView.tsx +77 -38
- package/src/components/Select.tsx +2 -0
- package/src/components/Sortable.tsx +45 -36
- package/src/components/Stack.tsx +63 -62
- package/src/components/Switch.tsx +25 -25
- package/src/components/Text.tsx +3 -0
- package/src/contexts/DialogContext.tsx +3 -0
- package/src/index.tsx +3 -2
- package/src/theme/light.ts +1 -1
- package/src/utils/getGradientBackground.tsx +8 -10
|
@@ -7,6 +7,7 @@ import React, {
|
|
|
7
7
|
memo,
|
|
8
8
|
useCallback,
|
|
9
9
|
useEffect,
|
|
10
|
+
useImperativeHandle,
|
|
10
11
|
useLayoutEffect,
|
|
11
12
|
useMemo,
|
|
12
13
|
useRef,
|
|
@@ -163,6 +164,12 @@ type Props = {
|
|
|
163
164
|
hideMenuWhenEmptyValue?: boolean;
|
|
164
165
|
};
|
|
165
166
|
|
|
167
|
+
export interface IInputFieldWithCompletions {
|
|
168
|
+
focus(): void;
|
|
169
|
+
setValue(value: string): void;
|
|
170
|
+
selectAllInputText(): void;
|
|
171
|
+
}
|
|
172
|
+
|
|
166
173
|
export const InputFieldWithCompletions = memo(
|
|
167
174
|
forwardRef(function InputFieldWithCompletions(
|
|
168
175
|
{
|
|
@@ -183,10 +190,9 @@ export const InputFieldWithCompletions = memo(
|
|
|
183
190
|
hideChildrenWhenFocused = false,
|
|
184
191
|
hideMenuWhenEmptyValue = false,
|
|
185
192
|
}: Props,
|
|
186
|
-
forwardedRef: ForwardedRef<
|
|
193
|
+
forwardedRef: ForwardedRef<IInputFieldWithCompletions>
|
|
187
194
|
) {
|
|
188
|
-
const
|
|
189
|
-
const ref = forwardedRef || defaultRef;
|
|
195
|
+
const ref = useRef<HTMLInputElement>(null);
|
|
190
196
|
|
|
191
197
|
const [isFocused, setIsFocused] = useState(false);
|
|
192
198
|
const [state, _setState] = useState({
|
|
@@ -338,12 +344,25 @@ export const InputFieldWithCompletions = memo(
|
|
|
338
344
|
|
|
339
345
|
handled = true;
|
|
340
346
|
break;
|
|
341
|
-
case "Enter":
|
|
347
|
+
case "Enter": {
|
|
342
348
|
const item = filteredItems[selectedIndex];
|
|
343
|
-
|
|
349
|
+
if (item) {
|
|
350
|
+
selectItem(item);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
handled = true;
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
case "Tab": {
|
|
357
|
+
// Update the filter to the selected item
|
|
358
|
+
const item = filteredItems[selectedIndex];
|
|
359
|
+
if (item) {
|
|
360
|
+
handleChange(item.name);
|
|
361
|
+
}
|
|
344
362
|
|
|
345
363
|
handled = true;
|
|
346
364
|
break;
|
|
365
|
+
}
|
|
347
366
|
case "Escape":
|
|
348
367
|
if (ref && typeof ref === "object") {
|
|
349
368
|
ref.current?.blur();
|
|
@@ -370,8 +389,9 @@ export const InputFieldWithCompletions = memo(
|
|
|
370
389
|
handleIndexChange,
|
|
371
390
|
filteredItems,
|
|
372
391
|
selectedIndex,
|
|
373
|
-
selectItem,
|
|
374
392
|
ref,
|
|
393
|
+
selectItem,
|
|
394
|
+
handleChange,
|
|
375
395
|
filter,
|
|
376
396
|
onDeleteWhenEmpty,
|
|
377
397
|
]
|
|
@@ -379,6 +399,20 @@ export const InputFieldWithCompletions = memo(
|
|
|
379
399
|
|
|
380
400
|
const display = !filter && hideMenuWhenEmptyValue ? "none" : "flex";
|
|
381
401
|
|
|
402
|
+
const typeaheadValue = filteredItems[selectedIndex]?.name;
|
|
403
|
+
|
|
404
|
+
useImperativeHandle(forwardedRef, () => ({
|
|
405
|
+
focus: () => {
|
|
406
|
+
ref.current?.focus();
|
|
407
|
+
},
|
|
408
|
+
setValue: (value: string) => {
|
|
409
|
+
handleChange(value);
|
|
410
|
+
},
|
|
411
|
+
selectAllInputText: () => {
|
|
412
|
+
ref.current?.setSelectionRange(0, ref.current.value.length);
|
|
413
|
+
},
|
|
414
|
+
}));
|
|
415
|
+
|
|
382
416
|
return (
|
|
383
417
|
<InputField.Root
|
|
384
418
|
size={size}
|
|
@@ -434,6 +468,9 @@ export const InputFieldWithCompletions = memo(
|
|
|
434
468
|
aria-expanded={isFocused && display !== "none"}
|
|
435
469
|
aria-controls="component-listbox"
|
|
436
470
|
/>
|
|
471
|
+
{filter && typeaheadValue && typeaheadValue.startsWith(filter) && (
|
|
472
|
+
<InputField.Typeahead value={typeaheadValue} />
|
|
473
|
+
)}
|
|
437
474
|
{(!isFocused || !hideChildrenWhenFocused) && children}
|
|
438
475
|
{loading && isFocused && (
|
|
439
476
|
<InputField.Label>
|
|
@@ -102,7 +102,7 @@ export const LabeledRow = memo(function LabeledRow({
|
|
|
102
102
|
}: {
|
|
103
103
|
id?: string;
|
|
104
104
|
children: ReactNode;
|
|
105
|
-
label:
|
|
105
|
+
label: ReactNode;
|
|
106
106
|
labelTextStyle?: "small" | "heading5" | "heading4" | "heading3";
|
|
107
107
|
gap?: CSSProperties["gap"];
|
|
108
108
|
right?: ReactNode;
|
|
@@ -31,7 +31,7 @@ import { MenuItem } from "./internal/Menu";
|
|
|
31
31
|
import { ScrollArea } from "./ScrollArea";
|
|
32
32
|
import {
|
|
33
33
|
DropValidator,
|
|
34
|
-
|
|
34
|
+
normalizeListDestinationIndex,
|
|
35
35
|
RelativeDropPosition,
|
|
36
36
|
Sortable,
|
|
37
37
|
} from "./Sortable";
|
|
@@ -47,6 +47,9 @@ type ListColorScheme = "primary" | "secondary";
|
|
|
47
47
|
|
|
48
48
|
type PressEventName = "onClick" | "onPointerDown";
|
|
49
49
|
|
|
50
|
+
// TODO: Some of these could be passed in a single context value that's
|
|
51
|
+
// the same for all rows. But doing this seems to cause an infinite loop and
|
|
52
|
+
// prevents drag and drop from working.
|
|
50
53
|
type ListRowContextValue = {
|
|
51
54
|
marginType: ListRowMarginType;
|
|
52
55
|
selectedPosition: ListRowPosition;
|
|
@@ -56,12 +59,23 @@ type ListRowContextValue = {
|
|
|
56
59
|
gap: number;
|
|
57
60
|
variant: ListViewVariant;
|
|
58
61
|
sectionHeaderVariant: ListViewSectionHeaderVariant;
|
|
59
|
-
indentation: number;
|
|
60
62
|
pressEventName: PressEventName;
|
|
61
63
|
isSectionHeader: boolean;
|
|
62
64
|
colorScheme: ListColorScheme;
|
|
63
65
|
};
|
|
64
66
|
|
|
67
|
+
// The dragged item isn't passed a context value (doing so causes an infinite loop),
|
|
68
|
+
// so we pass this separately. Rows need the indentation to look correct when dragging,
|
|
69
|
+
// and the dragged row doesn't have a ListRowContextValue.
|
|
70
|
+
type DraggingContextValue = {
|
|
71
|
+
indentation: number;
|
|
72
|
+
isDragging?: boolean;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const DraggingContext = createContext<DraggingContextValue>({
|
|
76
|
+
indentation: 12,
|
|
77
|
+
});
|
|
78
|
+
|
|
65
79
|
const ListRowContext = createContext<ListRowContextValue>({
|
|
66
80
|
marginType: "none",
|
|
67
81
|
selectedPosition: "only",
|
|
@@ -71,7 +85,6 @@ const ListRowContext = createContext<ListRowContextValue>({
|
|
|
71
85
|
gap: 0,
|
|
72
86
|
variant: "normal",
|
|
73
87
|
sectionHeaderVariant: "normal",
|
|
74
|
-
indentation: 12,
|
|
75
88
|
pressEventName: "onClick",
|
|
76
89
|
isSectionHeader: false,
|
|
77
90
|
colorScheme: "primary",
|
|
@@ -305,7 +318,7 @@ interface ListViewRowProps<MenuItemType extends string = string> {
|
|
|
305
318
|
selected?: boolean;
|
|
306
319
|
depth?: number;
|
|
307
320
|
disabled?: boolean;
|
|
308
|
-
draggable?: boolean;
|
|
321
|
+
// draggable?: boolean;
|
|
309
322
|
hovered?: boolean;
|
|
310
323
|
sortable?: boolean;
|
|
311
324
|
gap?: number;
|
|
@@ -348,11 +361,12 @@ const ListViewRow = forwardRef(function ListViewRow<
|
|
|
348
361
|
}: ListViewRowProps<MenuItemType>,
|
|
349
362
|
forwardedRef: ForwardedRef<HTMLElement>
|
|
350
363
|
) {
|
|
364
|
+
depth = Math.max(0, depth);
|
|
365
|
+
|
|
351
366
|
const {
|
|
352
367
|
marginType,
|
|
353
368
|
selectedPosition,
|
|
354
369
|
sortable,
|
|
355
|
-
indentation,
|
|
356
370
|
pressEventName,
|
|
357
371
|
variant,
|
|
358
372
|
sectionHeaderVariant,
|
|
@@ -363,6 +377,7 @@ const ListViewRow = forwardRef(function ListViewRow<
|
|
|
363
377
|
const { hoverProps } = useHover({
|
|
364
378
|
onHoverChange,
|
|
365
379
|
});
|
|
380
|
+
const { indentation, isDragging } = useContext(DraggingContext);
|
|
366
381
|
|
|
367
382
|
const handlePress = useCallback(
|
|
368
383
|
(event: React.MouseEvent) => {
|
|
@@ -387,6 +402,16 @@ const ListViewRow = forwardRef(function ListViewRow<
|
|
|
387
402
|
[onDoubleClick]
|
|
388
403
|
);
|
|
389
404
|
|
|
405
|
+
const isDraggingStyle = useMemo(() => {
|
|
406
|
+
return isDragging
|
|
407
|
+
? {
|
|
408
|
+
// TODO: Where do these offsets actually come from?
|
|
409
|
+
transform: `translate3d(-36px, -6px, 0)`,
|
|
410
|
+
width: `calc(100% + 48px)`,
|
|
411
|
+
}
|
|
412
|
+
: undefined;
|
|
413
|
+
}, [isDragging]);
|
|
414
|
+
|
|
390
415
|
const renderContent = (
|
|
391
416
|
{
|
|
392
417
|
relativeDropPosition,
|
|
@@ -416,8 +441,9 @@ const ListViewRow = forwardRef(function ListViewRow<
|
|
|
416
441
|
selectedPosition={selectedPosition}
|
|
417
442
|
showsActiveState={pressEventName === "onClick"}
|
|
418
443
|
aria-selected={selected}
|
|
419
|
-
divider={divider}
|
|
444
|
+
divider={!isDragging && divider}
|
|
420
445
|
onKeyDown={onKeyDown}
|
|
446
|
+
style={isDraggingStyle}
|
|
421
447
|
{...renderProps}
|
|
422
448
|
{...mergeEventHandlers(
|
|
423
449
|
{ onPointerDown: renderProps.onPointerDown },
|
|
@@ -702,11 +728,6 @@ const ListViewRootInner = forwardRef(function ListViewRootInner<T>(
|
|
|
702
728
|
[data, renderItem]
|
|
703
729
|
);
|
|
704
730
|
|
|
705
|
-
const renderOverlay = useCallback(
|
|
706
|
-
(index: number) => renderItem(data[index], index, { isDragging: true }),
|
|
707
|
-
[renderItem, data]
|
|
708
|
-
);
|
|
709
|
-
|
|
710
731
|
const getItemContextValue = useCallback(
|
|
711
732
|
(i: number): ListRowContextValue | undefined => {
|
|
712
733
|
const current = renderChild(i);
|
|
@@ -765,7 +786,6 @@ const ListViewRootInner = forwardRef(function ListViewRootInner<T>(
|
|
|
765
786
|
sortable,
|
|
766
787
|
expandable,
|
|
767
788
|
divider,
|
|
768
|
-
indentation,
|
|
769
789
|
pressEventName,
|
|
770
790
|
variant,
|
|
771
791
|
sectionHeaderVariant,
|
|
@@ -780,7 +800,6 @@ const ListViewRootInner = forwardRef(function ListViewRootInner<T>(
|
|
|
780
800
|
sortable,
|
|
781
801
|
expandable,
|
|
782
802
|
divider,
|
|
783
|
-
indentation,
|
|
784
803
|
pressEventName,
|
|
785
804
|
variant,
|
|
786
805
|
sectionHeaderVariant,
|
|
@@ -788,6 +807,22 @@ const ListViewRootInner = forwardRef(function ListViewRootInner<T>(
|
|
|
788
807
|
]
|
|
789
808
|
);
|
|
790
809
|
|
|
810
|
+
const draggingContextOverlayValue = useMemo(
|
|
811
|
+
() => ({ indentation, isDragging: true }),
|
|
812
|
+
[indentation]
|
|
813
|
+
);
|
|
814
|
+
|
|
815
|
+
const renderOverlay = useCallback(
|
|
816
|
+
(index: number) => (
|
|
817
|
+
<div style={{ opacity: 0.25 }}>
|
|
818
|
+
<DraggingContext.Provider value={draggingContextOverlayValue}>
|
|
819
|
+
{renderItem(data[index], index, { isDragging: true })}
|
|
820
|
+
</DraggingContext.Provider>
|
|
821
|
+
</div>
|
|
822
|
+
),
|
|
823
|
+
[draggingContextOverlayValue, renderItem, data]
|
|
824
|
+
);
|
|
825
|
+
|
|
791
826
|
const renderWrappedChild = useCallback(
|
|
792
827
|
(index: number) => {
|
|
793
828
|
const contextValue = getItemContextValue(index);
|
|
@@ -845,32 +880,36 @@ const ListViewRootInner = forwardRef(function ListViewRootInner<T>(
|
|
|
845
880
|
[data, keyExtractor]
|
|
846
881
|
);
|
|
847
882
|
|
|
883
|
+
const draggingContextValue = useMemo(() => ({ indentation }), [indentation]);
|
|
884
|
+
|
|
848
885
|
return (
|
|
849
|
-
<
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
886
|
+
<DraggingContext.Provider value={draggingContextValue}>
|
|
887
|
+
<RootContainer
|
|
888
|
+
{...{
|
|
889
|
+
[pressEventName]: handleClick,
|
|
890
|
+
}}
|
|
891
|
+
gap={gap}
|
|
892
|
+
scrollable={scrollable}
|
|
893
|
+
>
|
|
894
|
+
{withScrollable((scrollElementRef: HTMLDivElement | null) =>
|
|
895
|
+
withSortable(
|
|
896
|
+
virtualized ? (
|
|
897
|
+
<VirtualizedList<T>
|
|
898
|
+
ref={forwardedRef}
|
|
899
|
+
scrollElement={scrollElementRef!}
|
|
900
|
+
items={data}
|
|
901
|
+
size={virtualized}
|
|
902
|
+
getItemHeight={getItemHeight}
|
|
903
|
+
keyExtractor={getKey}
|
|
904
|
+
renderItem={renderWrappedChild}
|
|
905
|
+
/>
|
|
906
|
+
) : (
|
|
907
|
+
range(0, data.length).map(renderWrappedChild)
|
|
908
|
+
)
|
|
870
909
|
)
|
|
871
|
-
)
|
|
872
|
-
|
|
873
|
-
</
|
|
910
|
+
)}
|
|
911
|
+
</RootContainer>
|
|
912
|
+
</DraggingContext.Provider>
|
|
874
913
|
);
|
|
875
914
|
});
|
|
876
915
|
|
|
@@ -946,5 +985,5 @@ export namespace ListView {
|
|
|
946
985
|
(headerVariant === "label" ? sectionHeaderLabelHeight : rowHeight)
|
|
947
986
|
);
|
|
948
987
|
};
|
|
949
|
-
export const
|
|
988
|
+
export const normalizeDestinationIndex = normalizeListDestinationIndex;
|
|
950
989
|
}
|
|
@@ -56,6 +56,7 @@ const SelectElement = styled.select<{ flex: CSSProperties["flex"] }>(
|
|
|
56
56
|
appearance: "none",
|
|
57
57
|
...theme.textStyles.small,
|
|
58
58
|
color: theme.colors.text,
|
|
59
|
+
lineHeight: "19px",
|
|
59
60
|
width: "0px", // Reset intrinsic width
|
|
60
61
|
flex: flex ?? "1 1 0px",
|
|
61
62
|
position: "relative",
|
|
@@ -144,6 +145,7 @@ export const Select = memo(function Select<T extends string>({
|
|
|
144
145
|
listeners.current.get(event.target.value)?.(),
|
|
145
146
|
[listeners]
|
|
146
147
|
)}
|
|
148
|
+
onPointerDown={(event) => event.stopPropagation()}
|
|
147
149
|
>
|
|
148
150
|
{optionElements}
|
|
149
151
|
</SelectElement>
|
|
@@ -9,12 +9,12 @@ import {
|
|
|
9
9
|
Translate,
|
|
10
10
|
useSensor,
|
|
11
11
|
useSensors,
|
|
12
|
-
} from
|
|
12
|
+
} from "@dnd-kit/core";
|
|
13
13
|
import {
|
|
14
14
|
SortableContext,
|
|
15
15
|
useSortable,
|
|
16
16
|
verticalListSortingStrategy,
|
|
17
|
-
} from
|
|
17
|
+
} from "@dnd-kit/sortable";
|
|
18
18
|
import React, {
|
|
19
19
|
createContext,
|
|
20
20
|
memo,
|
|
@@ -25,32 +25,32 @@ import React, {
|
|
|
25
25
|
useMemo,
|
|
26
26
|
useRef,
|
|
27
27
|
useState,
|
|
28
|
-
} from
|
|
29
|
-
import { createPortal } from
|
|
28
|
+
} from "react";
|
|
29
|
+
import { createPortal } from "react-dom";
|
|
30
30
|
|
|
31
|
-
export type RelativeDropPosition =
|
|
31
|
+
export type RelativeDropPosition = "above" | "below" | "inside";
|
|
32
32
|
|
|
33
33
|
export type DropValidator = (
|
|
34
34
|
sourceIndex: number,
|
|
35
35
|
destinationIndex: number,
|
|
36
|
-
position: RelativeDropPosition
|
|
36
|
+
position: RelativeDropPosition
|
|
37
37
|
) => boolean;
|
|
38
38
|
|
|
39
|
-
export const
|
|
39
|
+
export const normalizeListDestinationIndex = (
|
|
40
40
|
index: number,
|
|
41
|
-
position:
|
|
41
|
+
position: "above" | "below"
|
|
42
42
|
): number => {
|
|
43
|
-
return position ===
|
|
43
|
+
return position === "above" ? index : index + 1;
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
const defaultAcceptsDrop: DropValidator = (
|
|
47
47
|
sourceIndex,
|
|
48
48
|
destinationIndex,
|
|
49
|
-
position
|
|
49
|
+
position
|
|
50
50
|
) => {
|
|
51
|
-
if (position ===
|
|
51
|
+
if (position === "inside") return false;
|
|
52
52
|
|
|
53
|
-
const normalized =
|
|
53
|
+
const normalized = normalizeListDestinationIndex(destinationIndex, position);
|
|
54
54
|
|
|
55
55
|
if (sourceIndex === normalized || sourceIndex + 1 === normalized) {
|
|
56
56
|
return false;
|
|
@@ -64,11 +64,13 @@ const SortableItemContext = createContext<{
|
|
|
64
64
|
position: Translate;
|
|
65
65
|
acceptsDrop: DropValidator;
|
|
66
66
|
setActivatorEvent: (event: PointerEvent) => void;
|
|
67
|
+
axis: "x" | "y";
|
|
67
68
|
}>({
|
|
68
69
|
keys: [],
|
|
69
70
|
position: { x: 0, y: 0 },
|
|
70
71
|
acceptsDrop: defaultAcceptsDrop,
|
|
71
72
|
setActivatorEvent: () => {},
|
|
73
|
+
axis: "y",
|
|
72
74
|
});
|
|
73
75
|
|
|
74
76
|
function validateDropIndicator(
|
|
@@ -76,33 +78,33 @@ function validateDropIndicator(
|
|
|
76
78
|
keys: string[],
|
|
77
79
|
activeId: string,
|
|
78
80
|
overId: string,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
offsetStart: number,
|
|
82
|
+
elementStart: number,
|
|
83
|
+
elementSize: number
|
|
82
84
|
): RelativeDropPosition | undefined {
|
|
83
85
|
const activeIndex = keys.indexOf(activeId);
|
|
84
86
|
const overIndex = keys.indexOf(overId);
|
|
85
87
|
|
|
86
|
-
const acceptsDropInside = acceptsDrop(activeIndex, overIndex,
|
|
88
|
+
const acceptsDropInside = acceptsDrop(activeIndex, overIndex, "inside");
|
|
87
89
|
|
|
88
90
|
// If we're in the center of the element, prefer dropping inside
|
|
89
91
|
if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
offsetStart >= elementStart + elementSize / 3 &&
|
|
93
|
+
offsetStart <= elementStart + (elementSize * 2) / 3 &&
|
|
92
94
|
acceptsDropInside
|
|
93
95
|
)
|
|
94
|
-
return
|
|
96
|
+
return "inside";
|
|
95
97
|
|
|
96
98
|
// Are we over the top or bottom half of the element?
|
|
97
99
|
const indicator =
|
|
98
|
-
|
|
100
|
+
offsetStart < elementStart + elementSize / 2 ? "above" : "below";
|
|
99
101
|
|
|
100
102
|
// Drop above or below if possible, falling back to inside
|
|
101
103
|
return acceptsDrop(activeIndex, overIndex, indicator)
|
|
102
104
|
? indicator
|
|
103
105
|
: acceptsDropInside
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
? "inside"
|
|
107
|
+
: undefined;
|
|
106
108
|
}
|
|
107
109
|
|
|
108
110
|
/* ----------------------------------------------------------------------------
|
|
@@ -119,7 +121,7 @@ interface ItemProps<T> {
|
|
|
119
121
|
ref: Ref<T>;
|
|
120
122
|
relativeDropPosition?: RelativeDropPosition;
|
|
121
123
|
[key: string]: any;
|
|
122
|
-
} & UseSortableReturnType[
|
|
124
|
+
} & UseSortableReturnType["attributes"]
|
|
123
125
|
) => JSX.Element;
|
|
124
126
|
}
|
|
125
127
|
|
|
@@ -128,7 +130,7 @@ function SortableItem<T extends HTMLElement>({
|
|
|
128
130
|
disabled,
|
|
129
131
|
children,
|
|
130
132
|
}: ItemProps<T>) {
|
|
131
|
-
const { keys, position, acceptsDrop, setActivatorEvent } =
|
|
133
|
+
const { keys, position, acceptsDrop, setActivatorEvent, axis } =
|
|
132
134
|
useContext(SortableItemContext);
|
|
133
135
|
const sortable = useSortable({ id, disabled });
|
|
134
136
|
|
|
@@ -148,7 +150,9 @@ function SortableItem<T extends HTMLElement>({
|
|
|
148
150
|
setActivatorEvent(activatorEvent as PointerEvent);
|
|
149
151
|
}
|
|
150
152
|
|
|
153
|
+
const eventX = (activatorEvent as PointerEvent | null)?.clientX ?? 0;
|
|
151
154
|
const eventY = (activatorEvent as PointerEvent | null)?.clientY ?? 0;
|
|
155
|
+
const offsetLeft = eventX + position.x;
|
|
152
156
|
const offsetTop = eventY + position.y;
|
|
153
157
|
|
|
154
158
|
const ref = useCallback((node: T) => setNodeRef(node), [setNodeRef]);
|
|
@@ -164,9 +168,9 @@ function SortableItem<T extends HTMLElement>({
|
|
|
164
168
|
keys,
|
|
165
169
|
active.id,
|
|
166
170
|
over.id,
|
|
167
|
-
offsetTop,
|
|
168
|
-
over.rect.offsetTop,
|
|
169
|
-
over.rect.height
|
|
171
|
+
axis === "x" ? offsetLeft : offsetTop,
|
|
172
|
+
axis === "x" ? over.rect.offsetLeft : over.rect.offsetTop,
|
|
173
|
+
axis === "x" ? over.rect.width : over.rect.height
|
|
170
174
|
)
|
|
171
175
|
: undefined,
|
|
172
176
|
});
|
|
@@ -183,9 +187,10 @@ interface RootProps {
|
|
|
183
187
|
onMoveItem?: (
|
|
184
188
|
sourceIndex: number,
|
|
185
189
|
destinationIndex: number,
|
|
186
|
-
position: RelativeDropPosition
|
|
190
|
+
position: RelativeDropPosition
|
|
187
191
|
) => void;
|
|
188
192
|
acceptsDrop?: DropValidator;
|
|
193
|
+
axis?: "x" | "y";
|
|
189
194
|
}
|
|
190
195
|
|
|
191
196
|
function SortableRoot({
|
|
@@ -194,13 +199,14 @@ function SortableRoot({
|
|
|
194
199
|
onMoveItem,
|
|
195
200
|
renderOverlay,
|
|
196
201
|
acceptsDrop = defaultAcceptsDrop,
|
|
202
|
+
axis = "y",
|
|
197
203
|
}: RootProps) {
|
|
198
204
|
const sensors = useSensors(
|
|
199
205
|
useSensor(PointerSensor, {
|
|
200
206
|
activationConstraint: {
|
|
201
207
|
distance: 4,
|
|
202
208
|
},
|
|
203
|
-
})
|
|
209
|
+
})
|
|
204
210
|
);
|
|
205
211
|
|
|
206
212
|
const [activeIndex, setActiveIndex] = useState<number | undefined>();
|
|
@@ -216,7 +222,7 @@ function SortableRoot({
|
|
|
216
222
|
(event: DragStartEvent) => {
|
|
217
223
|
setActiveIndex(keys.indexOf(event.active.id));
|
|
218
224
|
},
|
|
219
|
-
[keys]
|
|
225
|
+
[keys]
|
|
220
226
|
);
|
|
221
227
|
|
|
222
228
|
const handleDragMove = useCallback((event: DragMoveEvent) => {
|
|
@@ -233,7 +239,9 @@ function SortableRoot({
|
|
|
233
239
|
const oldIndex = keys.indexOf(active.id);
|
|
234
240
|
const newIndex = keys.indexOf(over.id);
|
|
235
241
|
|
|
242
|
+
const eventX = activatorEvent.current?.clientX ?? 0;
|
|
236
243
|
const eventY = activatorEvent.current?.clientY ?? 0;
|
|
244
|
+
const offsetLeft = eventX + position.x;
|
|
237
245
|
const offsetTop = eventY + position.y;
|
|
238
246
|
|
|
239
247
|
const indicator = validateDropIndicator(
|
|
@@ -241,9 +249,9 @@ function SortableRoot({
|
|
|
241
249
|
keys,
|
|
242
250
|
active.id,
|
|
243
251
|
over.id,
|
|
244
|
-
offsetTop,
|
|
245
|
-
over.rect.offsetTop,
|
|
246
|
-
over.rect.height
|
|
252
|
+
axis === "x" ? offsetLeft : offsetTop,
|
|
253
|
+
axis === "x" ? over.rect.offsetLeft : over.rect.offsetTop,
|
|
254
|
+
axis === "x" ? over.rect.width : over.rect.height
|
|
247
255
|
);
|
|
248
256
|
|
|
249
257
|
if (!indicator) return;
|
|
@@ -251,7 +259,7 @@ function SortableRoot({
|
|
|
251
259
|
onMoveItem?.(oldIndex, newIndex, indicator);
|
|
252
260
|
}
|
|
253
261
|
},
|
|
254
|
-
[acceptsDrop, keys, onMoveItem, position.y]
|
|
262
|
+
[acceptsDrop, axis, keys, onMoveItem, position.x, position.y]
|
|
255
263
|
);
|
|
256
264
|
|
|
257
265
|
return (
|
|
@@ -262,8 +270,9 @@ function SortableRoot({
|
|
|
262
270
|
acceptsDrop,
|
|
263
271
|
position,
|
|
264
272
|
setActivatorEvent,
|
|
273
|
+
axis,
|
|
265
274
|
}),
|
|
266
|
-
[acceptsDrop, keys, position, setActivatorEvent]
|
|
275
|
+
[acceptsDrop, axis, keys, position, setActivatorEvent]
|
|
267
276
|
)}
|
|
268
277
|
>
|
|
269
278
|
<DndContext
|
|
@@ -283,7 +292,7 @@ function SortableRoot({
|
|
|
283
292
|
activeIndex >= 0 &&
|
|
284
293
|
renderOverlay(activeIndex)}
|
|
285
294
|
</DragOverlay>,
|
|
286
|
-
document.body
|
|
295
|
+
document.body
|
|
287
296
|
)}
|
|
288
297
|
</DndContext>
|
|
289
298
|
</SortableItemContext.Provider>
|