qtsd-fork
Version:
Do not use this please
196 lines (162 loc) • 4.35 kB
JavaScript
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
}
}