Skip to content
This repository has been archived by the owner on Aug 6, 2024. It is now read-only.

feat: added item freeze functionality to draggable list #279

Merged
merged 18 commits into from
Feb 10, 2023
Merged
Changes from 3 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
946fd82
feat: added item freeze functionality to draggable list
keyurparalkar Dec 13, 2022
a4eac7e
fix: item dragging issue in draggable list
keyurparalkar Dec 14, 2022
f775824
chore: revert draggablelist.stories.tsx
keyurparalkar Dec 14, 2022
723e4b7
Merge branch 'release' of github.com:appsmithorg/design-system into f…
albinAppsmith Dec 14, 2022
eafebbd
Merge branch 'main' of github.com:appsmithorg/design-system into feat…
albinAppsmith Dec 14, 2022
cee9d38
Create olive-impalas-compare.md
albinAppsmith Dec 15, 2022
ff1c1d5
fix: addressed QA callouts
keyurparalkar Dec 16, 2022
c82b85c
fix: updated condition for unfrozenItems
keyurparalkar Dec 26, 2022
4ac1b3b
Merge branch 'release' of github.com:appsmithorg/design-system into f…
albinAppsmith Dec 26, 2022
9fb1903
Merge branch 'main' of github.com:appsmithorg/design-system into feat…
albinAppsmith Dec 26, 2022
6fdd488
fix: added new logic to disable drag for sticky items
keyurparalkar Jan 5, 2023
344c517
chore: added comments
keyurparalkar Jan 5, 2023
e3b22c5
Merge branch 'release' of github.com:appsmithorg/design-system into f…
albinAppsmith Jan 5, 2023
cfebac9
Merge branch 'main' of github.com:appsmithorg/design-system into feat…
albinAppsmith Jan 5, 2023
c1ca435
fix: renamed variable
keyurparalkar Jan 9, 2023
e0221b9
feat: added substract icon
keyurparalkar Jan 24, 2023
ce15b97
Merge branch 'release' of github.com:appsmithorg/design-system into f…
albinAppsmith Jan 24, 2023
c97264d
Merge branch 'release' into feat/feat-sticky-draggable-list
keyurparalkar Feb 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 94 additions & 16 deletions packages/design-system/src/DraggableList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useEffect, useRef } from "react";
import { clamp } from "lodash-es";
import { clamp, head, last } from "lodash-es";
import { useDrag } from "react-use-gesture";
import { useSprings, animated, to } from "react-spring";
import styled from "styled-components";
Expand Down Expand Up @@ -59,6 +59,12 @@ const DraggableListWrapper = styled.div`
}
`;

const FrozenListWrapper = styled.div<{
height?: number;
}>`
height: ${(props) => props.height}px;
`;

export function DraggableList(props: any) {
const {
className,
Expand All @@ -72,34 +78,83 @@ export function DraggableList(props: any) {
updateDragging,
} = props;

const topFrozenItems = items.filter((item: any) => item?.sticky === "left");
const bottomFrozenItems = items.filter(
(item: any) => item?.sticky === "right",
);
const unfrozenItems =
topFrozenItems.length > 0 || bottomFrozenItems.length > 0
? items.filter((item: any) => item.sticky === undefined)
: items;

const topFrozenContainerHeight = topFrozenItems.length * itemHeight;
const bottomFrozenContainerHeight = bottomFrozenItems.length * itemHeight;
const listContainerHeight =
fixedHeight && fixedHeight < items.length * itemHeight
? fixedHeight
: items.length * itemHeight;

const shouldReRender = get(props, "shouldReRender", true);
// order of items in the list
const order = useRef<any>(items.map((_: any, index: any) => index));
const order = useRef<any>(
unfrozenItems.map((_: any, index: number) => index),
);
const displacement = useRef<number>(0);
const dragging = useRef<boolean>(false);

const listRef = useRef<HTMLDivElement | null>(null);

const onDrop = (originalIndex: number, newIndex: number) => {
onUpdate(order.current, originalIndex, newIndex);
const onDrop = (
itemOrder: number[],
originalIndex: number,
newIndex: number,
) => {
/**
* On drop we have the itemOrder of the unfrozen items.
* We need to map this order with respect to the indices present in the actual column order.
* For example,
* itemOrder = [2, 0, 1];
* orderMapping = [unfrozenItems[2].index, unfrozenItems[0].index, unfrozenItems[1].index] // The actual column order.
*/
const orderMapping = itemOrder.map(
(rowIdx: any) => unfrozenItems[rowIdx].index,
);

const updatedOrder = [
...topFrozenItems.map((item: any) => item.index),
...orderMapping,
...bottomFrozenItems.map((item: any) => item.index),
];

const columnOrderOrigIndex = orderMapping[originalIndex];
const columnOrderNewIndex = orderMapping[newIndex];
onUpdate(
updatedOrder,
updatedOrder.indexOf(columnOrderOrigIndex) !== 0
? updatedOrder.indexOf(columnOrderOrigIndex)
: originalIndex,
updatedOrder.indexOf(columnOrderNewIndex) !== 0
? updatedOrder.indexOf(columnOrderNewIndex)
: newIndex,
);
order.current = itemOrder;

if (shouldReRender) {
order.current = items.map((_: any, index: any) => index);
order.current = unfrozenItems.map((_: any, index: any) => index);
setSprings(updateSpringStyles(order.current, itemHeight));
}
};

useEffect(() => {
// when items are updated(added/removed/updated) reassign order and animate springs.
if (items.length !== order.current.length || shouldReRender === false) {
order.current = items.map((_: any, index: any) => index);
if (
unfrozenItems.length !== order.current.length ||
shouldReRender === false
) {
order.current = unfrozenItems.map((_: any, index: any) => index);
setSprings(updateSpringStyles(order.current, itemHeight));
}
}, [items]);
}, [unfrozenItems]);

useEffect(() => {
if (focusedIndex && listRef && listRef.current) {
Expand All @@ -125,7 +180,7 @@ export function DraggableList(props: any) {
}, [focusedIndex]);

const [springs, setSprings] = useSprings<any>(
items.length,
unfrozenItems.length,
updateSpringStyles(order.current, itemHeight),
);

Expand Down Expand Up @@ -191,8 +246,9 @@ export function DraggableList(props: any) {
(curIndex * itemHeight + displacement.current) / itemHeight,
),
0,
items.length - 1,
unfrozenItems.length - 1,
);

const newOrder = [...order.current];
newOrder.splice(curRow, 0, newOrder.splice(curIndex, 1)[0]);
setSprings(
Expand All @@ -209,7 +265,7 @@ export function DraggableList(props: any) {
if (!props.down) {
order.current = newOrder;
setSprings(updateSpringStyles(order.current, itemHeight));
debounce(onDrop, 400)(curIndex, curRow);
debounce(onDrop, 400)(newOrder, curIndex, curRow);
}
}
}
Expand All @@ -218,31 +274,41 @@ export function DraggableList(props: any) {
return (
<div
className={className}
ref={listRef}
style={{
height: listContainerHeight,
overflowY: "auto",
zIndex: 1,
}}
>
{topFrozenItems.length > 0 && (
<FrozenListWrapper height={topFrozenContainerHeight}>
{topFrozenItems.map((item: any) => (
<div>
<ItemRenderer index={item.index} item={item} />
</div>
))}
</FrozenListWrapper>
)}
<DraggableListWrapper
className="content"
ref={listRef}
onMouseDown={() => {
// set events to null to stop other parent draggable elements execution(ex: Property pane)
document.onmouseup = null;
document.onmousemove = null;
}}
style={{
height: "100%",
height: `${listContainerHeight -
(topFrozenContainerHeight + bottomFrozenContainerHeight)}px`,
}}
>
{springs.map(({ scale, y, zIndex }, i) => (
<animated.div
{...bind(i)}
data-rbd-draggable-id={items[i].id}
data-rbd-draggable-id={unfrozenItems[i].id}
//having a key of items[i].id will break in few places,
//eg, primary columns in propertyPane of Table widget
key={items[i][keyAccessor] || i}
key={unfrozenItems[i][keyAccessor] || i}
style={{
zIndex,
width: "100%",
Expand All @@ -253,11 +319,23 @@ export function DraggableList(props: any) {
}}
>
<div>
<ItemRenderer index={i} item={items[i]} />
<ItemRenderer
index={unfrozenItems[i]?.index || i}
item={unfrozenItems[i]}
/>
</div>
</animated.div>
))}
</DraggableListWrapper>
{bottomFrozenItems.length > 0 && (
<FrozenListWrapper height={bottomFrozenContainerHeight}>
{bottomFrozenItems.map((item: any) => (
<div>
<ItemRenderer index={item.index} item={item} />
</div>
))}
</FrozenListWrapper>
)}
</div>
);
}
Expand Down