Skip to content

Commit

Permalink
Add stories for removable sortable items
Browse files Browse the repository at this point in the history
  • Loading branch information
Clauderic Demers committed Mar 24, 2021
1 parent 4bf585f commit fddb961
Show file tree
Hide file tree
Showing 22 changed files with 241 additions and 52 deletions.
16 changes: 16 additions & 0 deletions packages/sortable/src/components/SortableContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ interface ContextDescriptor {
useDragOverlay: boolean;
sortedRects: LayoutRect[];
strategy: SortingStrategy;
wasSorting: boolean;
}

export const Context = React.createContext<ContextDescriptor>({
Expand All @@ -35,6 +36,7 @@ export const Context = React.createContext<ContextDescriptor>({
useDragOverlay: false,
sortedRects: [],
strategy: rectSortingStrategy,
wasSorting: false,
});

export function SortableContext({
Expand All @@ -55,6 +57,8 @@ export function SortableContext({
const useDragOverlay = Boolean(overlayNode.rect !== null);
const activeIndex = active ? items.indexOf(active) : -1;
const isSorting = activeIndex !== -1;
const prevSorting = useRef(isSorting);
const wasSorting = !isSorting && prevSorting.current === true;
const overIndex = over ? items.indexOf(over.id) : -1;
const previousItemsRef = useRef(items);
const sortedRects = getSortedRects(items, droppableRects);
Expand All @@ -73,6 +77,16 @@ export function SortableContext({
previousItemsRef.current = items;
}, [items]);

useEffect(() => {
if (isSorting) {
prevSorting.current = isSorting;
} else {
requestAnimationFrame(() => {
prevSorting.current = isSorting;
});
}
}, [isSorting]);

const contextValue = useMemo(
(): ContextDescriptor => ({
activeIndex,
Expand All @@ -83,6 +97,7 @@ export function SortableContext({
useDragOverlay,
sortedRects,
strategy,
wasSorting,
}),
[
activeIndex,
Expand All @@ -93,6 +108,7 @@ export function SortableContext({
sortedRects,
useDragOverlay,
strategy,
wasSorting,
]
);

Expand Down
3 changes: 3 additions & 0 deletions packages/sortable/src/hooks/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import type {Transition} from '@dnd-kit/utilities';
export type SortableTransition = Pick<Transition, 'easing' | 'duration'>;

export type AnimateLayoutChanges = (args: {
active: UniqueIdentifier | null;
isDragging: boolean;
isSorting: boolean;
id: UniqueIdentifier;
index: number;
newIndex: number;
items: UniqueIdentifier[];
transition: SortableTransition | null;
wasSorting: boolean;
}) => boolean;
24 changes: 14 additions & 10 deletions packages/sortable/src/hooks/useSortable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export function useSortable({
overIndex,
useDragOverlay,
strategy: globalStrategy,
wasSorting,
} = useContext(Context);
const {
active,
Expand Down Expand Up @@ -94,15 +95,18 @@ export function useSortable({
: index;
const prevNewIndex = useRef(newIndex);
const shouldAnimateLayoutChanges = animateLayoutChanges({
active,
isDragging,
isSorting,
id,
index,
items,
newIndex: prevNewIndex.current,
transition,
wasSorting,
});
const derivedTransform = useDerivedTransform({
disabled: transition === null,
disabled: !shouldAnimateLayoutChanges,
index,
node,
rect,
Expand Down Expand Up @@ -138,17 +142,17 @@ export function useSortable({
return disabledTransition;
}

if (
shouldDisplaceDragSource ||
!shouldAnimateLayoutChanges ||
!transition
) {
if (shouldDisplaceDragSource || !transition) {
return null;
}

return CSS.Transition.toString({
...transition,
property: transitionProperty,
});
if (isSorting || shouldAnimateLayoutChanges) {
return CSS.Transition.toString({
...transition,
property: transitionProperty,
});
}

return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function useDerivedTransform({rect, disabled, index, node}: Arguments) {
if (index !== prevIndex.current) {
prevIndex.current = index;
}
}, [rect, disabled, index, node]);
}, [disabled, index, node, rect]);

useEffect(() => {
if (derivedTransform) {
Expand Down
25 changes: 23 additions & 2 deletions stories/2 - Presets/Sortable/1-Vertical.story.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import React from 'react';

import {LayoutMeasuringStrategy} from '@dnd-kit/core';
import {restrictToWindowEdges} from '@dnd-kit/modifiers';
import {verticalListSortingStrategy} from '@dnd-kit/sortable';
import {
AnimateLayoutChanges,
defaultAnimateLayoutChanges,
verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import {
restrictToVerticalAxis,
restrictToFirstScrollableAncestor,
Expand Down Expand Up @@ -142,3 +146,20 @@ export const RerenderBeforeSorting = () => {
/>
);
};

export const RemovableItems = () => {
const animateLayoutChanges: AnimateLayoutChanges = (args) =>
args.isSorting || args.wasSorting
? defaultAnimateLayoutChanges(args)
: true;

return (
<Sortable
{...props}
animateLayoutChanges={animateLayoutChanges}
layoutMeasuring={{strategy: LayoutMeasuringStrategy.Always}}
removable
handle
/>
);
};
24 changes: 23 additions & 1 deletion stories/2 - Presets/Sortable/2-Horizontal.story.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import React from 'react';
import {horizontalListSortingStrategy} from '@dnd-kit/sortable';
import {LayoutMeasuringStrategy} from '@dnd-kit/core';
import {
AnimateLayoutChanges,
defaultAnimateLayoutChanges,
horizontalListSortingStrategy,
} from '@dnd-kit/sortable';
import {restrictToHorizontalAxis} from '@dnd-kit/modifiers';

import {createRange} from '../../utilities';
Expand Down Expand Up @@ -107,3 +112,20 @@ export const MarginBetweenItems = () => {
/>
);
};

export const RemovableItems = () => {
const animateLayoutChanges: AnimateLayoutChanges = (args) =>
args.isSorting || args.wasSorting
? defaultAnimateLayoutChanges(args)
: true;

return (
<Sortable
{...props}
animateLayoutChanges={animateLayoutChanges}
layoutMeasuring={{strategy: LayoutMeasuringStrategy.Always}}
removable
handle
/>
);
};
25 changes: 23 additions & 2 deletions stories/2 - Presets/Sortable/3-Grid.story.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import React from 'react';

import {LayoutMeasuringStrategy} from '@dnd-kit/core';
import {restrictToWindowEdges} from '@dnd-kit/modifiers';
import {rectSortingStrategy} from '@dnd-kit/sortable';
import {
AnimateLayoutChanges,
defaultAnimateLayoutChanges,
rectSortingStrategy,
} from '@dnd-kit/sortable';

import {Sortable, Props as SortableProps} from './Sortable';
import {GridContainer} from '../../components';
Expand Down Expand Up @@ -120,3 +124,20 @@ export const MinimumDistance = () => (
}}
/>
);

export const RemovableItems = () => {
const animateLayoutChanges: AnimateLayoutChanges = (args) =>
args.isSorting || args.wasSorting
? defaultAnimateLayoutChanges(args)
: true;

return (
<Sortable
{...props}
animateLayoutChanges={animateLayoutChanges}
layoutMeasuring={{strategy: LayoutMeasuringStrategy.Always}}
removable
handle
/>
);
};
39 changes: 29 additions & 10 deletions stories/2 - Presets/Sortable/Sortable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
KeyboardSensor,
Modifiers,
MouseSensor,
LayoutMeasuring,
PointerActivationConstraint,
ScreenReaderInstructions,
TouchSensor,
Expand All @@ -24,21 +25,27 @@ import {
sortableKeyboardCoordinates,
SortingStrategy,
rectSortingStrategy,
AnimateLayoutChanges,
} from '@dnd-kit/sortable';

import {createRange} from '../../utilities';
import {Item, List, Wrapper} from '../../components';

export interface Props {
activationConstraint?: PointerActivationConstraint;
animateLayoutChanges?: AnimateLayoutChanges;
adjustScale?: boolean;
collisionDetection?: CollisionDetection;
Container?: any; // To-do: Fix me
strategy?: SortingStrategy;
itemCount?: number;
items?: string[];
renderItem?: any;
handle?: boolean;
layoutMeasuring?: Partial<LayoutMeasuring>;
modifiers?: Modifiers;
renderItem?: any;
removable?: boolean;
strategy?: SortingStrategy;
useDragOverlay?: boolean;
getItemStyles?(args: {
id: UniqueIdentifier;
index: number;
Expand All @@ -53,8 +60,6 @@ export interface Props {
id: string;
}): React.CSSProperties;
isDisabled?(id: UniqueIdentifier): boolean;
modifiers?: Modifiers;
useDragOverlay?: boolean;
}

const screenReaderInstructions: ScreenReaderInstructions = {
Expand All @@ -67,19 +72,22 @@ const screenReaderInstructions: ScreenReaderInstructions = {

export function Sortable({
activationConstraint,
animateLayoutChanges,
adjustScale = false,
Container = List,
collisionDetection = closestCenter,
strategy = rectSortingStrategy,
getItemStyles = () => ({}),
handle = false,
itemCount = 16,
items: initialItems,
renderItem,
handle = false,
getItemStyles = () => ({}),
wrapperStyle = () => ({}),
isDisabled = () => false,
layoutMeasuring,
modifiers,
removable,
renderItem,
strategy = rectSortingStrategy,
useDragOverlay = true,
wrapperStyle = () => ({}),
}: Props) {
const [items, setItems] = useState<string[]>(
() =>
Expand All @@ -101,7 +109,9 @@ export function Sortable({
const getIndex = items.indexOf.bind(items);
const getPosition = (id: string) => getIndex(id) + 1;
const activeIndex = activeId ? getIndex(activeId) : -1;

const handleRemove = removable
? (id: string) => setItems((items) => items.filter((item) => item !== id))
: undefined;
const announcements: Announcements = {
onDragStart(id) {
return `Picked up sortable item ${id}. Sortable item ${id} is in position ${getPosition(
Expand Down Expand Up @@ -155,6 +165,7 @@ export function Sortable({
}
}}
onDragCancel={() => setActiveId(null)}
layoutMeasuring={layoutMeasuring}
modifiers={modifiers}
>
<Wrapper center>
Expand All @@ -170,6 +181,8 @@ export function Sortable({
wrapperStyle={wrapperStyle}
disabled={isDisabled(value)}
renderItem={renderItem}
onRemove={handleRemove}
animateLayoutChanges={animateLayoutChanges}
useDragOverlay={useDragOverlay}
/>
))}
Expand Down Expand Up @@ -209,11 +222,13 @@ export function Sortable({
}

interface SortableItemProps {
animateLayoutChanges?: AnimateLayoutChanges;
disabled?: boolean;
id: string;
index: number;
handle: boolean;
useDragOverlay?: boolean;
onRemove?(id: string): void;
style(values: any): React.CSSProperties;
renderItem?(args: any): React.ReactElement;
wrapperStyle({
Expand All @@ -229,9 +244,11 @@ interface SortableItemProps {

export function SortableItem({
disabled,
animateLayoutChanges,
id,
index,
handle,
onRemove,
style,
renderItem,
useDragOverlay,
Expand All @@ -247,6 +264,7 @@ export function SortableItem({
transform,
transition,
} = useSortable({
animateLayoutChanges,
id,
disabled,
});
Expand All @@ -268,6 +286,7 @@ export function SortableItem({
isSorting,
overIndex,
})}
onRemove={onRemove ? () => onRemove(id) : undefined}
transform={transform}
transition={!useDragOverlay && isDragging ? 'none' : transition}
wrapperStyle={wrapperStyle({index, isDragging, id})}
Expand Down
Loading

0 comments on commit fddb961

Please sign in to comment.