You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
130 lines
4.4 KiB
130 lines
4.4 KiB
import { invariant } from '@react-dnd/invariant';
|
|
import { addSource, addTarget, removeSource, removeTarget, } from './actions/registry';
|
|
import getNextUniqueId from './utils/getNextUniqueId';
|
|
import { HandlerRole, } from './interfaces';
|
|
import { validateSourceContract, validateTargetContract, validateType, } from './contracts';
|
|
import { asap } from '@react-dnd/asap';
|
|
function getNextHandlerId(role) {
|
|
const id = getNextUniqueId().toString();
|
|
switch (role) {
|
|
case HandlerRole.SOURCE:
|
|
return `S${id}`;
|
|
case HandlerRole.TARGET:
|
|
return `T${id}`;
|
|
default:
|
|
throw new Error(`Unknown Handler Role: ${role}`);
|
|
}
|
|
}
|
|
function parseRoleFromHandlerId(handlerId) {
|
|
switch (handlerId[0]) {
|
|
case 'S':
|
|
return HandlerRole.SOURCE;
|
|
case 'T':
|
|
return HandlerRole.TARGET;
|
|
default:
|
|
invariant(false, `Cannot parse handler ID: ${handlerId}`);
|
|
}
|
|
}
|
|
function mapContainsValue(map, searchValue) {
|
|
const entries = map.entries();
|
|
let isDone = false;
|
|
do {
|
|
const { done, value: [, value], } = entries.next();
|
|
if (value === searchValue) {
|
|
return true;
|
|
}
|
|
isDone = !!done;
|
|
} while (!isDone);
|
|
return false;
|
|
}
|
|
export default class HandlerRegistryImpl {
|
|
constructor(store) {
|
|
this.types = new Map();
|
|
this.dragSources = new Map();
|
|
this.dropTargets = new Map();
|
|
this.pinnedSourceId = null;
|
|
this.pinnedSource = null;
|
|
this.store = store;
|
|
}
|
|
addSource(type, source) {
|
|
validateType(type);
|
|
validateSourceContract(source);
|
|
const sourceId = this.addHandler(HandlerRole.SOURCE, type, source);
|
|
this.store.dispatch(addSource(sourceId));
|
|
return sourceId;
|
|
}
|
|
addTarget(type, target) {
|
|
validateType(type, true);
|
|
validateTargetContract(target);
|
|
const targetId = this.addHandler(HandlerRole.TARGET, type, target);
|
|
this.store.dispatch(addTarget(targetId));
|
|
return targetId;
|
|
}
|
|
containsHandler(handler) {
|
|
return (mapContainsValue(this.dragSources, handler) ||
|
|
mapContainsValue(this.dropTargets, handler));
|
|
}
|
|
getSource(sourceId, includePinned = false) {
|
|
invariant(this.isSourceId(sourceId), 'Expected a valid source ID.');
|
|
const isPinned = includePinned && sourceId === this.pinnedSourceId;
|
|
const source = isPinned ? this.pinnedSource : this.dragSources.get(sourceId);
|
|
return source;
|
|
}
|
|
getTarget(targetId) {
|
|
invariant(this.isTargetId(targetId), 'Expected a valid target ID.');
|
|
return this.dropTargets.get(targetId);
|
|
}
|
|
getSourceType(sourceId) {
|
|
invariant(this.isSourceId(sourceId), 'Expected a valid source ID.');
|
|
return this.types.get(sourceId);
|
|
}
|
|
getTargetType(targetId) {
|
|
invariant(this.isTargetId(targetId), 'Expected a valid target ID.');
|
|
return this.types.get(targetId);
|
|
}
|
|
isSourceId(handlerId) {
|
|
const role = parseRoleFromHandlerId(handlerId);
|
|
return role === HandlerRole.SOURCE;
|
|
}
|
|
isTargetId(handlerId) {
|
|
const role = parseRoleFromHandlerId(handlerId);
|
|
return role === HandlerRole.TARGET;
|
|
}
|
|
removeSource(sourceId) {
|
|
invariant(this.getSource(sourceId), 'Expected an existing source.');
|
|
this.store.dispatch(removeSource(sourceId));
|
|
asap(() => {
|
|
this.dragSources.delete(sourceId);
|
|
this.types.delete(sourceId);
|
|
});
|
|
}
|
|
removeTarget(targetId) {
|
|
invariant(this.getTarget(targetId), 'Expected an existing target.');
|
|
this.store.dispatch(removeTarget(targetId));
|
|
this.dropTargets.delete(targetId);
|
|
this.types.delete(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;
|
|
}
|
|
addHandler(role, type, handler) {
|
|
const id = getNextHandlerId(role);
|
|
this.types.set(id, type);
|
|
if (role === HandlerRole.SOURCE) {
|
|
this.dragSources.set(id, handler);
|
|
}
|
|
else if (role === HandlerRole.TARGET) {
|
|
this.dropTargets.set(id, handler);
|
|
}
|
|
return id;
|
|
}
|
|
}
|