Created
April 13, 2018 02:33
-
-
Save AlexFrazer/aed3810407aaf23b23168449a7ef83bf to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as React from 'react'; | |
export interface Props { | |
selectableKey: string; | |
children?: React.ReactNode; | |
} | |
export interface ComponentProps { | |
selectableRef: React.Ref<HTMLElement>; | |
} | |
export default function createSelectable<TProps>( | |
Wrapped: React.ComponentType<ComponentProps & TProps> | |
) { | |
return class SelectableItem extends React.PureComponent<Props & TProps> { | |
static contextTypes = { | |
selectable: PropTypes.shape({ | |
insert: PropTypes.func, | |
remove: PropTypes.func, | |
}), | |
}; | |
onRef: React.Ref<HTMLElement> = (element) => { | |
if (element) { | |
this.context.selectable.insert(this.props.selectableKey, element); | |
} else { | |
this.context.selectable.remove(this.props.selectableKey, element); | |
} | |
} | |
render() { | |
return ( | |
<WrappedComponent | |
{...this.props} | |
selectableRef={this} | |
/> | |
); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as React from 'react'; | |
import * as rbush from 'rbush'; | |
import * as PropTypes from 'prop-types'; | |
export interface Props { | |
/* +/- how many pixels from mousedown */ | |
tolerance: number; | |
/* What to do on selection */ | |
onSelection(o: { selection: Set<string>, e: React.MouseEvent<HTMLDivElement> }): void; | |
/* What to do when selection has been completed */ | |
onSelectionDone: React.MouseEventHandler<HTMLDivElement>; | |
/* what to do after starting selection */ | |
onSelectionStart: React.MouseEventHandler<HTMLDivElement>; | |
} | |
export interface State { | |
minX: number; | |
maxX: number; | |
minY: number; | |
maxY: number; | |
} | |
export interface SelectableNode extends rbush.BBox { | |
key: string; // will be used to reference the node. | |
} | |
export default class SelectableGroup extends React.PureComponent<Props, State> { | |
static defaultProps = { | |
tolerance: 10, | |
}; | |
static childContextTypes = { | |
selectable: PropTypes.shape({ | |
insert: PropTypes.func, | |
remove: PropTypes.func, | |
}) | |
} | |
getChildContext() { | |
return { | |
selectable: { | |
insert: this.insert, | |
remove: this.remove, | |
} | |
} | |
} | |
initialX: number = 0; | |
initialY: number = 0; | |
tree: rbush.RBush<SelectableNode>(); | |
nodes: Map<string, HTMLElement> = new Map(); | |
insert = (key: string, node: HTMLElement) => { | |
this.nodes.set(key, node); | |
this.tree.insert(createTreeNode(node)); | |
} | |
remove = (key: string, node: HTMLElement) => { | |
this.nodes.delete(key); | |
this.tree.remove(createTreeNode(node), ({ key: a }, { key: b }) => a === b); | |
} | |
updateNode = (key: string, node: HTMLElement) => { | |
this.removeNode(node); | |
this.insertNode(node); | |
}; | |
onMouseDown: React.MouseEventHandler<HTMLDivElement> = ({ pageX, pageY }) => { | |
this.initialX = pageX; | |
this.initialY = pageY; | |
this.props.onSelectionStart(); | |
window.addEventListener('mouseup', this.onMouseUp); | |
window.addEventListener('mousemove', this.onMouseMove); | |
} | |
onMouseMove: MouseEventHandler = ({ pageX, pageY }) => { | |
this.setState({ | |
minX: Math.min(this.initialX, pageX), | |
maxX: Math.max(this.initialX, pageX), | |
minY: Math.min(this.initialY, pageY), | |
maxY: Math.max(this.initialY, pageY), | |
}, () => { | |
const collisions = this.tree.search(this.state); | |
this.props.onSelection(new Set(collisions)); | |
}) | |
} | |
onMouseUp: MouseEventHandler = () => { | |
this.props.onSelectionDone(); | |
window.removeEventListener('mousemove', this.onMouseMove); | |
window.removeEventListener('mouseup', this.onMouseUp); | |
} | |
render() { | |
return ( | |
<Container | |
{...props} | |
onMouseDown={this.onMouseDown} | |
> | |
{children} | |
</Container> | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment