1 | import { invariant } from '@react-dnd/invariant';
|
2 | import { addSource, addTarget, removeSource, removeTarget, } from '../actions/registry';
|
3 | import { getNextUniqueId } from '../utils/getNextUniqueId';
|
4 | import { HandlerRole, } from '../interfaces';
|
5 | import { validateSourceContract, validateTargetContract, validateType, } from '../contracts';
|
6 | import { asap } from '@react-dnd/asap';
|
7 | function getNextHandlerId(role) {
|
8 | const id = getNextUniqueId().toString();
|
9 | switch (role) {
|
10 | case HandlerRole.SOURCE:
|
11 | return `S${id}`;
|
12 | case HandlerRole.TARGET:
|
13 | return `T${id}`;
|
14 | default:
|
15 | throw new Error(`Unknown Handler Role: ${role}`);
|
16 | }
|
17 | }
|
18 | function parseRoleFromHandlerId(handlerId) {
|
19 | switch (handlerId[0]) {
|
20 | case 'S':
|
21 | return HandlerRole.SOURCE;
|
22 | case 'T':
|
23 | return HandlerRole.TARGET;
|
24 | default:
|
25 | invariant(false, `Cannot parse handler ID: ${handlerId}`);
|
26 | }
|
27 | }
|
28 | function mapContainsValue(map, searchValue) {
|
29 | const entries = map.entries();
|
30 | let isDone = false;
|
31 | do {
|
32 | const { done, value: [, value], } = entries.next();
|
33 | if (value === searchValue) {
|
34 | return true;
|
35 | }
|
36 | isDone = !!done;
|
37 | } while (!isDone);
|
38 | return false;
|
39 | }
|
40 | export class HandlerRegistryImpl {
|
41 | types = new Map();
|
42 | dragSources = new Map();
|
43 | dropTargets = new Map();
|
44 | pinnedSourceId = null;
|
45 | pinnedSource = null;
|
46 | store;
|
47 | constructor(store) {
|
48 | this.store = store;
|
49 | }
|
50 | addSource(type, source) {
|
51 | validateType(type);
|
52 | validateSourceContract(source);
|
53 | const sourceId = this.addHandler(HandlerRole.SOURCE, type, source);
|
54 | this.store.dispatch(addSource(sourceId));
|
55 | return sourceId;
|
56 | }
|
57 | addTarget(type, target) {
|
58 | validateType(type, true);
|
59 | validateTargetContract(target);
|
60 | const targetId = this.addHandler(HandlerRole.TARGET, type, target);
|
61 | this.store.dispatch(addTarget(targetId));
|
62 | return targetId;
|
63 | }
|
64 | containsHandler(handler) {
|
65 | return (mapContainsValue(this.dragSources, handler) ||
|
66 | mapContainsValue(this.dropTargets, handler));
|
67 | }
|
68 | getSource(sourceId, includePinned = false) {
|
69 | invariant(this.isSourceId(sourceId), 'Expected a valid source ID.');
|
70 | const isPinned = includePinned && sourceId === this.pinnedSourceId;
|
71 | const source = isPinned ? this.pinnedSource : this.dragSources.get(sourceId);
|
72 | return source;
|
73 | }
|
74 | getTarget(targetId) {
|
75 | invariant(this.isTargetId(targetId), 'Expected a valid target ID.');
|
76 | return this.dropTargets.get(targetId);
|
77 | }
|
78 | getSourceType(sourceId) {
|
79 | invariant(this.isSourceId(sourceId), 'Expected a valid source ID.');
|
80 | return this.types.get(sourceId);
|
81 | }
|
82 | getTargetType(targetId) {
|
83 | invariant(this.isTargetId(targetId), 'Expected a valid target ID.');
|
84 | return this.types.get(targetId);
|
85 | }
|
86 | isSourceId(handlerId) {
|
87 | const role = parseRoleFromHandlerId(handlerId);
|
88 | return role === HandlerRole.SOURCE;
|
89 | }
|
90 | isTargetId(handlerId) {
|
91 | const role = parseRoleFromHandlerId(handlerId);
|
92 | return role === HandlerRole.TARGET;
|
93 | }
|
94 | removeSource(sourceId) {
|
95 | invariant(this.getSource(sourceId), 'Expected an existing source.');
|
96 | this.store.dispatch(removeSource(sourceId));
|
97 | asap(() => {
|
98 | this.dragSources.delete(sourceId);
|
99 | this.types.delete(sourceId);
|
100 | });
|
101 | }
|
102 | removeTarget(targetId) {
|
103 | invariant(this.getTarget(targetId), 'Expected an existing target.');
|
104 | this.store.dispatch(removeTarget(targetId));
|
105 | this.dropTargets.delete(targetId);
|
106 | this.types.delete(targetId);
|
107 | }
|
108 | pinSource(sourceId) {
|
109 | const source = this.getSource(sourceId);
|
110 | invariant(source, 'Expected an existing source.');
|
111 | this.pinnedSourceId = sourceId;
|
112 | this.pinnedSource = source;
|
113 | }
|
114 | unpinSource() {
|
115 | invariant(this.pinnedSource, 'No source is pinned at the time.');
|
116 | this.pinnedSourceId = null;
|
117 | this.pinnedSource = null;
|
118 | }
|
119 | addHandler(role, type, handler) {
|
120 | const id = getNextHandlerId(role);
|
121 | this.types.set(id, type);
|
122 | if (role === HandlerRole.SOURCE) {
|
123 | this.dragSources.set(id, handler);
|
124 | }
|
125 | else if (role === HandlerRole.TARGET) {
|
126 | this.dropTargets.set(id, handler);
|
127 | }
|
128 | return id;
|
129 | }
|
130 | }
|