UNPKG

qtsd-fork

Version:

Do not use this please

196 lines (162 loc) 4.35 kB
import invariant from 'invariant' import isArray from 'lodash/isArray' import asap from 'asap' import { addSource, addTarget, removeSource, removeTarget, } from './actions/registry' import getNextUniqueId from './utils/getNextUniqueId' const HandlerRoles = { SOURCE: 'SOURCE', TARGET: 'TARGET', } function validateSourceContract(source) { invariant( typeof source.canDrag === 'function', 'Expected canDrag to be a function.', ) invariant( typeof source.beginDrag === 'function', 'Expected beginDrag to be a function.', ) invariant( typeof source.endDrag === 'function', 'Expected endDrag to be a function.', ) } function validateTargetContract(target) { invariant( typeof target.canDrop === 'function', 'Expected canDrop to be a function.', ) invariant( typeof target.hover === 'function', 'Expected hover to be a function.', ) invariant( typeof target.drop === 'function', 'Expected beginDrag to be a function.', ) } function validateType(type, allowArray) { if (allowArray && isArray(type)) { type.forEach(t => validateType(t, false)) return } invariant( typeof type === 'string' || typeof type === 'symbol', allowArray ? 'Type can only be a string, a symbol, or an array of either.' : 'Type can only be a string or a symbol.', ) } function getNextHandlerId(role) { const id = getNextUniqueId().toString() switch (role) { case HandlerRoles.SOURCE: return `S${id}` case HandlerRoles.TARGET: return `T${id}` default: invariant(false, `Unknown role: ${role}`) } } function parseRoleFromHandlerId(handlerId) { switch (handlerId[0]) { case 'S': return HandlerRoles.SOURCE case 'T': return HandlerRoles.TARGET default: invariant(false, `Cannot parse handler ID: ${handlerId}`) } } export default class HandlerRegistry { constructor(store) { this.store = store this.types = {} this.handlers = {} this.pinnedSourceId = null this.pinnedSource = null } addSource(type, source) { validateType(type) validateSourceContract(source) const sourceId = this.addHandler(HandlerRoles.SOURCE, type, source) this.store.dispatch(addSource(sourceId)) return sourceId } addTarget(type, target) { validateType(type, true) validateTargetContract(target) const targetId = this.addHandler(HandlerRoles.TARGET, type, target) this.store.dispatch(addTarget(targetId)) return targetId } addHandler(role, type, handler) { const id = getNextHandlerId(role) this.types[id] = type this.handlers[id] = handler return id } containsHandler(handler) { return Object.keys(this.handlers).some( key => this.handlers[key] === handler, ) } getSource(sourceId, includePinned) { invariant(this.isSourceId(sourceId), 'Expected a valid source ID.') const isPinned = includePinned && sourceId === this.pinnedSourceId const source = isPinned ? this.pinnedSource : this.handlers[sourceId] return source } getTarget(targetId) { invariant(this.isTargetId(targetId), 'Expected a valid target ID.') return this.handlers[targetId] } getSourceType(sourceId) { invariant(this.isSourceId(sourceId), 'Expected a valid source ID.') return this.types[sourceId] } getTargetType(targetId) { invariant(this.isTargetId(targetId), 'Expected a valid target ID.') return this.types[targetId] } isSourceId(handlerId) { const role = parseRoleFromHandlerId(handlerId) return role === HandlerRoles.SOURCE } isTargetId(handlerId) { const role = parseRoleFromHandlerId(handlerId) return role === HandlerRoles.TARGET } removeSource(sourceId) { invariant(this.getSource(sourceId), 'Expected an existing source.') this.store.dispatch(removeSource(sourceId)) asap(() => { delete this.handlers[sourceId] delete this.types[sourceId] }) } removeTarget(targetId) { invariant(this.getTarget(targetId), 'Expected an existing target.') this.store.dispatch(removeTarget(targetId)) asap(() => { delete this.handlers[targetId] delete this.types[targetId] }) } pinSource(sourceId) { const source = this.getSource(sourceId) invariant(source, 'Expected an existing source.') this.pinnedSourceId = sourceId this.pinnedSource = source } unpinSource() { invariant(this.pinnedSource, 'No source is pinned at the time.') this.pinnedSourceId = null this.pinnedSource = null } }