import * as React from 'react' import { DragDropManager, createDragDropManager, Identifier } from '../dnd-core' import { DndComponentClass, DndComponent } from './interfaces' // import { Consumer } from './DragDropContext' import {manager as dragDropManager} from './DragDropContext'; import { Disposable, CompositeDisposable, SerialDisposable, } from './utils/disposables' import createHTML5Backend from '../react-dnd-html5-backend' const isClassComponent = require('recompose/isClassComponent').default const isPlainObject = require('lodash/isPlainObject') const invariant = require('invariant') const hoistStatics = require('hoist-non-react-statics') const shallowEqual = require('shallowequal') // export const dragDropManager = createDragDropManager(createHTML5Backend, {}) export interface DecorateHandlerArgs { DecoratedComponent: | React.ComponentClass | React.StatelessComponent createHandler: any createMonitor: any createConnector: any registerHandler: any containerDisplayName: string getType: (props: Props) => ItemIdType collect: any options: any } export default function decorateHandler({ DecoratedComponent, createHandler, createMonitor, createConnector, registerHandler, containerDisplayName, getType, collect, options, }: DecorateHandlerArgs): TargetClass & DndComponentClass { const { arePropsEqual = shallowEqual } = options const Decorated: any = DecoratedComponent const displayName = DecoratedComponent.displayName || DecoratedComponent.name || 'Component' interface HandlerReceiver { receiveHandlerId: (handlerId: Identifier | null) => void } interface Handler { ref: React.RefObject receiveProps(props: Props): void } interface HandlerConnector extends HandlerReceiver { hooks: any[] } class DragDropContainer extends React.Component implements DndComponent { public static DecoratedComponent = DecoratedComponent public static displayName = `${containerDisplayName}(${displayName})` private handlerId: string | undefined private manager: DragDropManager | undefined private handlerMonitor: HandlerReceiver | undefined private handlerConnector: HandlerConnector | undefined private handler: Handler | undefined private disposable: any private isCurrentlyMounted: boolean = false private currentType: any constructor(props: Props) { super(props) this.handleChange = this.handleChange.bind(this) this.manager = dragDropManager this.handlerMonitor = createMonitor(dragDropManager) this.handlerConnector = createConnector(dragDropManager.getBackend()) this.handler = createHandler(this.handlerMonitor) this.disposable = new SerialDisposable() this.receiveProps(props) this.dispose() } public getHandlerId() { return this.handlerId as string } public getDecoratedComponentInstance() { if (!this.handler) { return null } return this.handler.ref.current as any } public shouldComponentUpdate(nextProps: any, nextState: any) { return ( !arePropsEqual(nextProps, this.props) || !shallowEqual(nextState, this.state) ) } public componentDidMount() { this.isCurrentlyMounted = true this.disposable = new SerialDisposable() this.currentType = undefined this.receiveProps(this.props) this.handleChange() } public componentDidUpdate(prevProps: Props) { if (!arePropsEqual(this.props, prevProps)) { this.receiveProps(this.props) this.handleChange() } } public componentWillUnmount() { this.dispose() this.isCurrentlyMounted = false } public receiveProps(props: any) { if (!this.handler) { return } this.handler.receiveProps(props) this.receiveType(getType(props)) } public receiveType(type: any) { if (!this.handlerMonitor || !this.manager || !this.handlerConnector) { return } if (type === this.currentType) { return } this.currentType = type const { handlerId, unregister } = registerHandler( type, this.handler, this.manager, ) this.handlerId = handlerId this.handlerMonitor.receiveHandlerId(handlerId) this.handlerConnector.receiveHandlerId(handlerId) const globalMonitor = this.manager.getMonitor() const unsubscribe = globalMonitor.subscribeToStateChange( this.handleChange, { handlerIds: [handlerId] }, ) this.disposable.setDisposable( new CompositeDisposable( new Disposable(unsubscribe), new Disposable(unregister), ), ) } public handleChange() { if (!this.isCurrentlyMounted) { return } const nextState = this.getCurrentState() if (!shallowEqual(nextState, this.state)) { this.setState(nextState) } } public dispose() { this.disposable.dispose() if (this.handlerConnector) { this.handlerConnector.receiveHandlerId(null) } } public getCurrentState() { if (!this.handlerConnector) { return {} } const nextState = collect( this.handlerConnector.hooks, this.handlerMonitor, ) if (process.env.NODE_ENV !== 'production') { invariant( isPlainObject(nextState), 'Expected `collect` specified as the second argument to ' + '%s for %s to return a plain object of props to inject. ' + 'Instead, received %s.', containerDisplayName, displayName, nextState, ) } return nextState } public render() { // Let componentDidMount fire to initialize the collected state if (!this.isCurrentlyMounted) { return null } return ( ) } // private receiveDragDropManager(dragDropManager: DragDropManager) { // if (this.manager !== undefined) { // return // } // this.manager = dragDropManager // invariant( // typeof dragDropManager === 'object', // 'Could not find the drag and drop manager in the context of %s. ' + // 'Make sure to wrap the top-level component of your app with DragDropContext. ' + // 'Read more: http://react-dnd.github.io/react-dnd/docs-troubleshooting.html#could-not-find-the-drag-and-drop-manager-in-the-context', // displayName, // displayName, // ) // this.handlerMonitor = createMonitor(dragDropManager) // this.handlerConnector = createConnector(dragDropManager.getBackend()) // this.handler = createHandler(this.handlerMonitor) // } } return hoistStatics(DragDropContainer, DecoratedComponent) as TargetClass & DndComponentClass }