react-beautiful-dnd
Version:
Beautiful, accessible drag and drop for lists with React.js
86 lines (70 loc) • 2.31 kB
JavaScript
// @flow
import memoizeOne from 'memoize-one';
import invariant from 'tiny-invariant';
import getStyles, { type Styles } from './get-styles';
import { prefix } from '../data-attributes';
import type { StyleMarshal } from './style-marshal-types';
import type { DropReason } from '../../types';
let count: number = 0;
// Required for server side rendering as count is persisted across requests
export const resetStyleContext = () => {
count = 0;
};
const getHead = (): HTMLHeadElement => {
const head: ?HTMLHeadElement = document.querySelector('head');
invariant(head, 'Cannot find the head to append a style to');
return head;
};
export default () => {
const context: string = `${count++}`;
const styles: Styles = getStyles(context);
let el: ?HTMLStyleElement = null;
// using memoizeOne as a way of not updating the innerHTML
// unless there is a new value required
const setStyle = memoizeOne((proposed: string) => {
invariant(el, 'Cannot set style of style tag if not mounted');
// This technique works with ie11+ so no need for a nasty fallback as seen here:
// https://stackoverflow.com/a/22050778/1374236
el.innerHTML = proposed;
});
// exposing this as a seperate step so that it works nicely with
// server side rendering
const mount = () => {
invariant(!el, 'Style marshal already mounted');
el = document.createElement('style');
el.type = 'text/css';
// for easy identification
el.setAttribute(prefix, context);
// add style tag to head
getHead().appendChild(el);
// set initial style
setStyle(styles.resting);
};
const collecting = () => setStyle(styles.collecting);
const dragging = () => setStyle(styles.dragging);
const dropping = (reason: DropReason) => {
if (reason === 'DROP') {
setStyle(styles.dropAnimating);
return;
}
setStyle(styles.userCancel);
};
const resting = () => setStyle(styles.resting);
const unmount = (): void => {
invariant(el, 'Cannot unmount style marshal as it is already unmounted');
// Remove from head
getHead().removeChild(el);
// Unset
el = null;
};
const marshal: StyleMarshal = {
collecting,
dragging,
dropping,
resting,
styleContext: context,
mount,
unmount,
};
return marshal;
};