UNPKG

react-beautiful-dnd

Version:

Beautiful, accessible drag and drop for lists with React.js

146 lines (124 loc) 3.66 kB
// @flow import { calculateBox, withScroll, type BoxModel, type Position, } from 'css-box-model'; import memoizeOne from 'memoize-one'; import PropTypes from 'prop-types'; import { Component, type Node } from 'react'; import invariant from 'tiny-invariant'; import { dimensionMarshalKey } from '../context-keys'; import type { DraggableDescriptor, DraggableDimension, Placeholder, DraggableId, DroppableId, TypeId, } from '../../types'; import type { DimensionMarshal } from '../../state/dimension-marshal/dimension-marshal-types'; type Props = {| draggableId: DraggableId, droppableId: DroppableId, type: TypeId, index: number, getDraggableRef: () => ?HTMLElement, children: Node, |}; export default class DraggableDimensionPublisher extends Component<Props> { /* eslint-disable react/sort-comp */ static contextTypes = { [dimensionMarshalKey]: PropTypes.object.isRequired, }; publishedDescriptor: ?DraggableDescriptor = null; componentDidMount() { this.publish(); } componentDidUpdate() { this.publish(); } componentWillUnmount() { this.unpublish(); } getMemoizedDescriptor = memoizeOne( ( id: DraggableId, index: number, droppableId: DroppableId, type: TypeId, ): DraggableDescriptor => ({ id, index, droppableId, type, }), ); publish = () => { const marshal: DimensionMarshal = this.context[dimensionMarshalKey]; const descriptor: DraggableDescriptor = this.getMemoizedDescriptor( this.props.draggableId, this.props.index, this.props.droppableId, this.props.type, ); if (!this.publishedDescriptor) { marshal.registerDraggable(descriptor, this.getDimension); this.publishedDescriptor = descriptor; return; } // No changes to the descriptor if (descriptor === this.publishedDescriptor) { return; } marshal.updateDraggable( this.publishedDescriptor, descriptor, this.getDimension, ); this.publishedDescriptor = descriptor; }; unpublish = () => { invariant( this.publishedDescriptor, 'Cannot unpublish descriptor when none is published', ); // Using the previously published id to unpublish. This is to guard // against the case where the id dynamically changes. This is not // supported during a drag - but it is good to guard against. const marshal: DimensionMarshal = this.context[dimensionMarshalKey]; marshal.unregisterDraggable(this.publishedDescriptor); this.publishedDescriptor = null; }; getDimension = (windowScroll: Position): DraggableDimension => { const targetRef: ?HTMLElement = this.props.getDraggableRef(); const descriptor: ?DraggableDescriptor = this.publishedDescriptor; invariant( targetRef, 'DraggableDimensionPublisher cannot calculate a dimension when not attached to the DOM', ); invariant(descriptor, 'Cannot get dimension for unpublished draggable'); const computedStyles: CSSStyleDeclaration = window.getComputedStyle( targetRef, ); const borderBox: ClientRect = targetRef.getBoundingClientRect(); const client: BoxModel = calculateBox(borderBox, computedStyles); const page: BoxModel = withScroll(client, windowScroll); const placeholder: Placeholder = { client, tagName: targetRef.tagName.toLowerCase(), display: computedStyles.display, }; const dimension: DraggableDimension = { descriptor, placeholder, client, page, }; return dimension; }; render() { return this.props.children; } }