diff --git a/.changeset/olive-impalas-compare.md b/.changeset/olive-impalas-compare.md new file mode 100644 index 000000000..99aa582cd --- /dev/null +++ b/.changeset/olive-impalas-compare.md @@ -0,0 +1,5 @@ +--- +"@appsmithorg/design-system": patch +--- + +feat: added item freeze functionality to draggable list diff --git a/packages/design-system-old/src/ControlIcons/index.tsx b/packages/design-system-old/src/ControlIcons/index.tsx index b9bd18f6b..306ffbbdd 100644 --- a/packages/design-system-old/src/ControlIcons/index.tsx +++ b/packages/design-system-old/src/ControlIcons/index.tsx @@ -65,6 +65,7 @@ import SettingsIcon from "remixicon-react/Settings5LineIcon"; import EyeIcon from "remixicon-react/EyeLineIcon"; import EyeOffIcon from "remixicon-react/EyeOffLineIcon"; import CloseIcon from "remixicon-react/CloseLineIcon"; +import SubtractIcon from "remixicon-react/SubtractFillIcon"; /* eslint-disable react/display-name */ @@ -400,6 +401,11 @@ export const ControlIcons: { ), + COLUMN_UNFREEZE: (props: IconProps) => ( + + + + ), }; export type ControlIconName = keyof typeof ControlIcons; diff --git a/packages/design-system-old/src/DraggableList/DraggableList.stories.tsx b/packages/design-system-old/src/DraggableList/DraggableList.stories.tsx index 8c3635bc8..ebc9e937a 100644 --- a/packages/design-system-old/src/DraggableList/DraggableList.stories.tsx +++ b/packages/design-system-old/src/DraggableList/DraggableList.stories.tsx @@ -52,11 +52,16 @@ DraggableList.args = { { id: 1, name: "Item 1", + isDragDisabled: true, }, { id: 2, name: "Item 2", }, + { + id: 3, + name: "Item 3", + }, ], keyAccessor: "id", onUpdate: (items) => { diff --git a/packages/design-system-old/src/DraggableList/index.tsx b/packages/design-system-old/src/DraggableList/index.tsx index b28bc28fa..ed57b5686 100644 --- a/packages/design-system-old/src/DraggableList/index.tsx +++ b/packages/design-system-old/src/DraggableList/index.tsx @@ -131,85 +131,118 @@ export function DraggableList(props: any) { const bind: any = useDrag((props: any) => { const originalIndex = props.args[0]; + const isDragDisabled = props.args[1]; const curIndex = order.current.indexOf(originalIndex); const pointerFromTop = props.xy[1]; - /** - * Checking for props.distance > 0 because: - * this container is the first recipient of all mouse events - * for self and children. Consequently, it treats a click as a drag request - * and updates the position of the list item. - * Checking for drag distance prevents this behavior. - */ - if (listRef && listRef.current && props?.distance > 0) { - const containerCoordinates = listRef?.current.getBoundingClientRect(); - const container = listRef.current; - if (containerCoordinates) { - const containerDistanceFromTop = containerCoordinates.top; - if (props.dragging) { - if (pointerFromTop < containerDistanceFromTop + itemHeight / 2) { - // Scroll inside container till first element in list is completely visible - if (container.scrollTop > 0) { - container.scrollTop -= itemHeight / 10; - } - } else if ( - pointerFromTop >= - containerDistanceFromTop + container.clientHeight - itemHeight / 2 - ) { - // Scroll inside container till container cannnot be scrolled more towards bottom - if ( - container.scrollTop <= - springs.length * itemHeight - - container.clientHeight - - itemHeight / 2 + + if (isDragDisabled) { + props.cancel(); + } else { + /** + * Checking for props.distance > 0 because: + * this container is the first recipient of all mouse events + * for self and children. Consequently, it treats a click as a drag request + * and updates the position of the list item. + * Checking for drag distance prevents this behavior. + */ + if (listRef && listRef.current && props?.distance > 0) { + const containerCoordinates = listRef?.current.getBoundingClientRect(); + const container = listRef.current; + if (containerCoordinates) { + const containerDistanceFromTop = containerCoordinates.top; + if (props.dragging) { + if (pointerFromTop < containerDistanceFromTop + itemHeight / 2) { + // Scroll inside container till first element in list is completely visible + if (container.scrollTop > 0) { + container.scrollTop -= itemHeight / 10; + } + } else if ( + pointerFromTop >= + containerDistanceFromTop + container.clientHeight - itemHeight / 2 ) { - container.scrollTop += itemHeight / 10; + // Scroll inside container till container cannnot be scrolled more towards bottom + if ( + container.scrollTop <= + springs.length * itemHeight - + container.clientHeight - + itemHeight / 2 + ) { + container.scrollTop += itemHeight / 10; + } } - } - // finding distance of current pointer from the top of the container to find the final position - // currIndex * itemHeight for the initial position - // subtraction formar with latter for displacement - displacement.current = - pointerFromTop - - containerDistanceFromTop + - container.scrollTop - - curIndex * itemHeight - - itemHeight / 2; + // finding distance of current pointer from the top of the container to find the final position + // currIndex * itemHeight for the initial position + // subtraction formar with latter for displacement + displacement.current = + pointerFromTop - + containerDistanceFromTop + + container.scrollTop - + curIndex * itemHeight - + itemHeight / 2; - if (!dragging.current && Math.abs(displacement.current) > 10) { - dragging.current = props.dragging; - updateDragging && updateDragging(dragging.current); - } - } else { - if (dragging.current) { - dragging.current = props.dragging; - updateDragging && updateDragging(dragging.current); + if (!dragging.current && Math.abs(displacement.current) > 10) { + dragging.current = props.dragging; + updateDragging && updateDragging(dragging.current); + } + } else { + if (dragging.current) { + dragging.current = props.dragging; + updateDragging && updateDragging(dragging.current); + } } - } - const curRow = clamp( - Math.round( - (curIndex * itemHeight + displacement.current) / itemHeight, - ), - 0, - items.length - 1, - ); - const newOrder = [...order.current]; - newOrder.splice(curRow, 0, newOrder.splice(curIndex, 1)[0]); - setSprings( - dragIdleSpringStyles(newOrder, { - down: props.down, - originalIndex, - curIndex, - y: Math.abs(displacement.current) > 10 ? displacement.current : 0, - itemHeight, - }), - ); - if (curRow !== curIndex) { - // Feed springs new style data, they'll animate the view without causing a single render - if (!props.down) { - order.current = newOrder; - setSprings(updateSpringStyles(order.current, itemHeight)); - debounce(onDrop, 400)(curIndex, curRow); + const curRow = clamp( + Math.round( + (curIndex * itemHeight + displacement.current) / itemHeight, + ), + 0, + items.length - 1, + ); + + /** + * We check if the final position's item's dragging is enabled. + */ + if ( + !items[curRow].hasOwnProperty("isDragDisabled") || + (items[curRow].hasOwnProperty("isDragDisabled") && + !items[curRow].isDragDisabled) + ) { + const newOrder = [...order.current]; + newOrder.splice(curRow, 0, newOrder.splice(curIndex, 1)[0]); + setSprings( + dragIdleSpringStyles(newOrder, { + down: props.down, + originalIndex, + curIndex, + y: + Math.abs(displacement.current) > 10 + ? displacement.current + : 0, + itemHeight, + }), + ); + if (curRow !== curIndex) { + // Feed springs new style data, they'll animate the view without causing a single render + if (!props.down) { + order.current = newOrder; + setSprings(updateSpringStyles(order.current, itemHeight)); + debounce(onDrop, 400)(curIndex, curRow); + } + } + } else { + // retain the order if an item's drag is disabled. + setSprings( + dragIdleSpringStyles(order.current, { + down: props.down, + originalIndex, + curIndex, + y: + Math.abs(displacement.current) > 10 + ? displacement.current + : 0, + itemHeight, + }), + ); } } } @@ -237,8 +270,15 @@ export function DraggableList(props: any) { }} > {springs.map(({ scale, y, zIndex }, i) => ( + /** + * Passing 2nd arg to bind is a boolean value that represents if the item's drag is disabled or not. + */