react-beautiful-dnd-next
Version:
Beautiful and accessible drag and drop for lists with React
79 lines (68 loc) • 2.58 kB
JavaScript
// @flow
import { useRef } from 'react';
import { type Position } from 'css-box-model';
import invariant from 'tiny-invariant';
import { useMemo, useCallback } from 'use-memo-one';
import type {
DraggableDescriptor,
DraggableDimension,
DraggableId,
} from '../../types';
import type { DimensionMarshal } from '../../state/dimension-marshal/dimension-marshal-types';
import useRequiredContext from '../use-required-context';
import AppContext, { type AppContextValue } from '../context/app-context';
import getDimension from './get-dimension';
import DroppableContext, {
type DroppableContextValue,
} from '../context/droppable-context';
import useLayoutEffect from '../use-isomorphic-layout-effect';
export type Args = {|
draggableId: DraggableId,
index: number,
getDraggableRef: () => ?HTMLElement,
|};
export default function useDraggableDimensionPublisher(args: Args) {
const { draggableId, index, getDraggableRef } = args;
// App context
const appContext: AppContextValue = useRequiredContext(AppContext);
const marshal: DimensionMarshal = appContext.marshal;
// Droppable context
const droppableContext: DroppableContextValue = useRequiredContext(
DroppableContext,
);
const { droppableId, type } = droppableContext;
const descriptor: DraggableDescriptor = useMemo(() => {
const result = {
id: draggableId,
droppableId,
type,
index,
};
return result;
}, [draggableId, droppableId, index, type]);
const publishedDescriptorRef = useRef<DraggableDescriptor>(descriptor);
const makeDimension = useCallback(
(windowScroll?: Position): DraggableDimension => {
const latest: DraggableDescriptor = publishedDescriptorRef.current;
const el: ?HTMLElement = getDraggableRef();
invariant(el, 'Cannot get dimension when no ref is set');
return getDimension(latest, el, windowScroll);
},
[getDraggableRef],
);
// handle mounting / unmounting
useLayoutEffect(() => {
marshal.registerDraggable(publishedDescriptorRef.current, makeDimension);
return () => marshal.unregisterDraggable(publishedDescriptorRef.current);
}, [makeDimension, marshal]);
// handle updates to descriptor
useLayoutEffect(() => {
// this will happen when mounting
if (publishedDescriptorRef.current === descriptor) {
return;
}
const previous: DraggableDescriptor = publishedDescriptorRef.current;
publishedDescriptorRef.current = descriptor;
marshal.updateDraggable(previous, descriptor, makeDimension);
}, [descriptor, makeDimension, marshal]);
}