@accelint/map-toolkit 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/catalog-info.yaml +5 -5
- package/dist/cursor-coordinates/index.d.ts +14 -3
- package/dist/cursor-coordinates/index.js +16 -3
- package/dist/cursor-coordinates/use-cursor-coordinates.d.ts +20 -6
- package/dist/cursor-coordinates/use-cursor-coordinates.js +247 -128
- package/dist/cursor-coordinates/use-cursor-coordinates.js.map +1 -1
- package/dist/deckgl/base-map/constants.d.ts +14 -12
- package/dist/deckgl/base-map/constants.js +26 -12
- package/dist/deckgl/base-map/constants.js.map +1 -1
- package/dist/deckgl/base-map/events.d.ts +6 -4
- package/dist/deckgl/base-map/events.js +18 -4
- package/dist/deckgl/base-map/events.js.map +1 -1
- package/dist/deckgl/base-map/index.d.ts +45 -18
- package/dist/deckgl/base-map/index.js +216 -148
- package/dist/deckgl/base-map/index.js.map +1 -1
- package/dist/deckgl/base-map/provider.d.ts +48 -32
- package/dist/deckgl/base-map/provider.js +122 -11
- package/dist/deckgl/base-map/provider.js.map +1 -1
- package/dist/deckgl/base-map/types.d.ts +49 -39
- package/dist/deckgl/base-map/types.js +11 -2
- package/dist/deckgl/index.d.ts +18 -13
- package/dist/deckgl/index.js +19 -6
- package/dist/deckgl/symbol-layer/fiber.d.ts +21 -10
- package/dist/deckgl/symbol-layer/fiber.js +18 -3
- package/dist/deckgl/symbol-layer/fiber.js.map +1 -1
- package/dist/deckgl/symbol-layer/index.d.ts +68 -54
- package/dist/deckgl/symbol-layer/index.js +105 -85
- package/dist/deckgl/symbol-layer/index.js.map +1 -1
- package/dist/deckgl/text-layer/character-sets.d.ts +19 -17
- package/dist/deckgl/text-layer/character-sets.js +40 -19
- package/dist/deckgl/text-layer/character-sets.js.map +1 -1
- package/dist/deckgl/text-layer/default-settings.d.ts +16 -2
- package/dist/deckgl/text-layer/default-settings.js +42 -18
- package/dist/deckgl/text-layer/default-settings.js.map +1 -1
- package/dist/deckgl/text-layer/fiber.d.ts +38 -27
- package/dist/deckgl/text-layer/fiber.js +18 -3
- package/dist/deckgl/text-layer/fiber.js.map +1 -1
- package/dist/deckgl/text-layer/index.d.ts +39 -25
- package/dist/deckgl/text-layer/index.js +47 -29
- package/dist/deckgl/text-layer/index.js.map +1 -1
- package/dist/decorators/deckgl.d.ts +16 -2
- package/dist/decorators/deckgl.js +25 -7
- package/dist/decorators/deckgl.js.map +1 -1
- package/dist/map-cursor/events.d.ts +16 -0
- package/dist/map-cursor/events.js +27 -0
- package/dist/map-cursor/events.js.map +1 -0
- package/dist/map-cursor/index.d.ts +17 -0
- package/dist/map-cursor/index.js +18 -0
- package/dist/map-cursor/store.d.ts +93 -0
- package/dist/map-cursor/store.js +351 -0
- package/dist/map-cursor/store.js.map +1 -0
- package/dist/map-cursor/types.d.ts +81 -0
- package/dist/map-cursor/types.js +12 -0
- package/dist/map-cursor/use-map-cursor.d.ts +99 -0
- package/dist/map-cursor/use-map-cursor.js +116 -0
- package/dist/map-cursor/use-map-cursor.js.map +1 -0
- package/dist/map-mode/events.d.ts +11 -9
- package/dist/map-mode/events.js +43 -9
- package/dist/map-mode/events.js.map +1 -1
- package/dist/map-mode/index.d.ts +17 -6
- package/dist/map-mode/index.js +18 -5
- package/dist/map-mode/store.d.ts +26 -3
- package/dist/map-mode/store.js +329 -265
- package/dist/map-mode/store.js.map +1 -1
- package/dist/map-mode/types.d.ts +49 -35
- package/dist/map-mode/types.js +11 -2
- package/dist/map-mode/use-map-mode.d.ts +21 -7
- package/dist/map-mode/use-map-mode.js +66 -23
- package/dist/map-mode/use-map-mode.js.map +1 -1
- package/dist/maplibre/constants.d.ts +10 -8
- package/dist/maplibre/constants.js +22 -8
- package/dist/maplibre/constants.js.map +1 -1
- package/dist/maplibre/hooks/use-maplibre.d.ts +17 -2
- package/dist/maplibre/hooks/use-maplibre.js +77 -31
- package/dist/maplibre/hooks/use-maplibre.js.map +1 -1
- package/dist/maplibre/index.d.ts +15 -3
- package/dist/maplibre/index.js +17 -4
- package/dist/viewport/constants.d.ts +8 -6
- package/dist/viewport/constants.js +20 -6
- package/dist/viewport/constants.js.map +1 -1
- package/dist/viewport/index.d.ts +18 -13
- package/dist/viewport/index.js +19 -6
- package/dist/viewport/types.d.ts +27 -17
- package/dist/viewport/types.js +11 -2
- package/dist/viewport/use-viewport-state.d.ts +29 -14
- package/dist/viewport/use-viewport-state.js +200 -87
- package/dist/viewport/use-viewport-state.js.map +1 -1
- package/dist/viewport/utils.d.ts +25 -10
- package/dist/viewport/utils.js +67 -37
- package/dist/viewport/utils.js.map +1 -1
- package/dist/viewport/viewport-size.d.ts +27 -15
- package/dist/viewport/viewport-size.js +54 -11
- package/dist/viewport/viewport-size.js.map +1 -1
- package/package.json +54 -27
- package/dist/cursor-coordinates/index.js.map +0 -1
- package/dist/deckgl/base-map/types.js.map +0 -1
- package/dist/deckgl/index.js.map +0 -1
- package/dist/map-mode/index.js.map +0 -1
- package/dist/map-mode/types.js.map +0 -1
- package/dist/maplibre/index.js.map +0 -1
- package/dist/metafile-esm.json +0 -1
- package/dist/viewport/index.js.map +0 -1
- package/dist/viewport/types.js.map +0 -1
package/dist/map-mode/store.js
CHANGED
|
@@ -1,306 +1,370 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
4
12
|
|
|
13
|
+
|
|
14
|
+
import { MapModeEvents } from "./events.js";
|
|
15
|
+
import { Broadcast } from "@accelint/bus";
|
|
16
|
+
import { uuid } from "@accelint/core";
|
|
17
|
+
|
|
18
|
+
//#region src/map-mode/store.ts
|
|
5
19
|
const DEFAULT_MODE = "default";
|
|
20
|
+
/**
|
|
21
|
+
* Typed event bus instance for map mode events.
|
|
22
|
+
* Provides type-safe event emission and listening for all map mode state changes.
|
|
23
|
+
*/
|
|
6
24
|
const mapModeBus = Broadcast.getInstance();
|
|
25
|
+
/**
|
|
26
|
+
* Store for map mode state keyed by instanceId
|
|
27
|
+
*/
|
|
7
28
|
const modeStore = /* @__PURE__ */ new Map();
|
|
29
|
+
/**
|
|
30
|
+
* Track React component subscribers per instanceId (for fan-out notifications).
|
|
31
|
+
* Each Set contains onStoreChange callbacks from useSyncExternalStore.
|
|
32
|
+
*/
|
|
8
33
|
const componentSubscribers = /* @__PURE__ */ new Map();
|
|
34
|
+
/**
|
|
35
|
+
* Cache of bus unsubscribe functions (1 per instanceId).
|
|
36
|
+
* This ensures we only have one bus listener per map mode instance, regardless of
|
|
37
|
+
* how many React components subscribe to it.
|
|
38
|
+
*/
|
|
9
39
|
const busUnsubscribers = /* @__PURE__ */ new Map();
|
|
40
|
+
/**
|
|
41
|
+
* Cache of subscription functions per instanceId to avoid recreating on every render
|
|
42
|
+
*/
|
|
10
43
|
const subscriptionCache = /* @__PURE__ */ new Map();
|
|
44
|
+
/**
|
|
45
|
+
* Cache of snapshot functions per instanceId to maintain referential stability
|
|
46
|
+
*/
|
|
11
47
|
const snapshotCache = /* @__PURE__ */ new Map();
|
|
48
|
+
/**
|
|
49
|
+
* Cache of server snapshot functions per instanceId to maintain referential stability.
|
|
50
|
+
* Server snapshots always return default mode since mode state is client-only.
|
|
51
|
+
*/
|
|
52
|
+
const serverSnapshotCache = /* @__PURE__ */ new Map();
|
|
53
|
+
/**
|
|
54
|
+
* Cache of requestModeChange functions per instanceId to maintain referential stability
|
|
55
|
+
*/
|
|
12
56
|
const requestModeChangeCache = /* @__PURE__ */ new Map();
|
|
57
|
+
/**
|
|
58
|
+
* Get or create mode state for a given instanceId
|
|
59
|
+
*/
|
|
13
60
|
function getOrCreateState(instanceId) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
return modeStore.get(instanceId);
|
|
61
|
+
if (!modeStore.has(instanceId)) modeStore.set(instanceId, {
|
|
62
|
+
mode: DEFAULT_MODE,
|
|
63
|
+
modeOwners: /* @__PURE__ */ new Map(),
|
|
64
|
+
pendingRequests: /* @__PURE__ */ new Map()
|
|
65
|
+
});
|
|
66
|
+
return modeStore.get(instanceId);
|
|
22
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Notify all React subscribers for a given instanceId
|
|
70
|
+
*/
|
|
23
71
|
function notifySubscribers(instanceId) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
for (const onStoreChange of subscribers) {
|
|
27
|
-
onStoreChange();
|
|
28
|
-
}
|
|
29
|
-
}
|
|
72
|
+
const subscribers = componentSubscribers.get(instanceId);
|
|
73
|
+
if (subscribers) for (const onStoreChange of subscribers) onStoreChange();
|
|
30
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Determine if a mode change request should be auto-accepted without authorization
|
|
77
|
+
*/
|
|
31
78
|
function shouldAutoAcceptRequest(state, desiredMode, requestOwner) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
if (!(currentModeOwner || desiredModeOwner)) {
|
|
41
|
-
return true;
|
|
42
|
-
}
|
|
43
|
-
if (state.mode === DEFAULT_MODE && requestOwner === desiredModeOwner) {
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
46
|
-
return false;
|
|
79
|
+
const currentModeOwner = state.modeOwners.get(state.mode);
|
|
80
|
+
const desiredModeOwner = state.modeOwners.get(desiredMode);
|
|
81
|
+
if (desiredMode === DEFAULT_MODE && requestOwner === currentModeOwner) return true;
|
|
82
|
+
if (requestOwner === currentModeOwner) return true;
|
|
83
|
+
if (!(currentModeOwner || desiredModeOwner)) return true;
|
|
84
|
+
if (state.mode === DEFAULT_MODE && requestOwner === desiredModeOwner) return true;
|
|
85
|
+
return false;
|
|
47
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Set mode and emit change event
|
|
89
|
+
*/
|
|
48
90
|
function setMode(instanceId, state, newMode) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
91
|
+
const previousMode = state.mode;
|
|
92
|
+
state.mode = newMode;
|
|
93
|
+
mapModeBus.emit(MapModeEvents.changed, {
|
|
94
|
+
previousMode,
|
|
95
|
+
currentMode: newMode,
|
|
96
|
+
id: instanceId
|
|
97
|
+
});
|
|
98
|
+
notifySubscribers(instanceId);
|
|
57
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Approve a request and reject all others
|
|
102
|
+
*/
|
|
58
103
|
function approveRequestAndRejectOthers(instanceId, state, approvedRequest, excludeAuthId, decisionOwner, reason, emitApproval) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
reason,
|
|
79
|
-
id: instanceId
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
for (const request of requestsToReject) {
|
|
83
|
-
mapModeBus.emit(MapModeEvents.changeDecision, {
|
|
84
|
-
authId: request.authId,
|
|
85
|
-
approved: false,
|
|
86
|
-
owner: decisionOwner,
|
|
87
|
-
reason: "Request auto-rejected because another request was approved",
|
|
88
|
-
id: instanceId
|
|
89
|
-
});
|
|
90
|
-
}
|
|
104
|
+
const requestsToReject = [];
|
|
105
|
+
for (const request of state.pendingRequests.values()) if (request.authId !== excludeAuthId) requestsToReject.push(request);
|
|
106
|
+
state.pendingRequests.clear();
|
|
107
|
+
setMode(instanceId, state, approvedRequest.desiredMode);
|
|
108
|
+
if (approvedRequest.desiredMode !== DEFAULT_MODE) state.modeOwners.set(approvedRequest.desiredMode, approvedRequest.requestOwner);
|
|
109
|
+
if (emitApproval) mapModeBus.emit(MapModeEvents.changeDecision, {
|
|
110
|
+
authId: approvedRequest.authId,
|
|
111
|
+
approved: true,
|
|
112
|
+
owner: decisionOwner,
|
|
113
|
+
reason,
|
|
114
|
+
id: instanceId
|
|
115
|
+
});
|
|
116
|
+
for (const request of requestsToReject) mapModeBus.emit(MapModeEvents.changeDecision, {
|
|
117
|
+
authId: request.authId,
|
|
118
|
+
approved: false,
|
|
119
|
+
owner: decisionOwner,
|
|
120
|
+
reason: "Request auto-rejected because another request was approved",
|
|
121
|
+
id: instanceId
|
|
122
|
+
});
|
|
91
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Handle pending requests when returning to default mode
|
|
126
|
+
*/
|
|
92
127
|
function handlePendingRequestsOnDefaultMode(instanceId, state, previousMode) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
owner: previousModeOwner,
|
|
109
|
-
reason: "Request rejected - already in requested mode",
|
|
110
|
-
id: instanceId
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
} else {
|
|
114
|
-
approveRequestAndRejectOthers(
|
|
115
|
-
instanceId,
|
|
116
|
-
state,
|
|
117
|
-
firstEntry,
|
|
118
|
-
firstEntry.authId,
|
|
119
|
-
previousModeOwner,
|
|
120
|
-
"Auto-accepted when mode owner returned to default",
|
|
121
|
-
true
|
|
122
|
-
);
|
|
123
|
-
}
|
|
128
|
+
const firstEntry = Array.from(state.pendingRequests.values())[0];
|
|
129
|
+
if (!firstEntry) return;
|
|
130
|
+
const previousModeOwner = state.modeOwners.get(previousMode);
|
|
131
|
+
if (!previousModeOwner) return;
|
|
132
|
+
if (firstEntry.desiredMode === DEFAULT_MODE) {
|
|
133
|
+
const allRequests = Array.from(state.pendingRequests.values());
|
|
134
|
+
state.pendingRequests.clear();
|
|
135
|
+
for (const request of allRequests) mapModeBus.emit(MapModeEvents.changeDecision, {
|
|
136
|
+
authId: request.authId,
|
|
137
|
+
approved: false,
|
|
138
|
+
owner: previousModeOwner,
|
|
139
|
+
reason: "Request rejected - already in requested mode",
|
|
140
|
+
id: instanceId
|
|
141
|
+
});
|
|
142
|
+
} else approveRequestAndRejectOthers(instanceId, state, firstEntry, firstEntry.authId, previousModeOwner, "Auto-accepted when mode owner returned to default", true);
|
|
124
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Handle authorization decision
|
|
146
|
+
*
|
|
147
|
+
* Processes approval/rejection decisions from mode owners. Only the current mode's owner
|
|
148
|
+
* can make authorization decisions. If a decision comes from a non-owner, a warning is
|
|
149
|
+
* logged and the decision is ignored to prevent unauthorized mode changes.
|
|
150
|
+
*
|
|
151
|
+
* @param instanceId - The unique identifier for this map instance
|
|
152
|
+
* @param state - The mode state for this instance
|
|
153
|
+
* @param payload - The authorization decision containing authId, approved status, and owner
|
|
154
|
+
*/
|
|
125
155
|
function handleAuthorizationDecision(instanceId, state, payload) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
if (!(matchingRequest && matchingRequestOwner)) {
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
if (approved) {
|
|
147
|
-
approveRequestAndRejectOthers(
|
|
148
|
-
instanceId,
|
|
149
|
-
state,
|
|
150
|
-
matchingRequest,
|
|
151
|
-
authId,
|
|
152
|
-
decisionOwner,
|
|
153
|
-
"",
|
|
154
|
-
false
|
|
155
|
-
);
|
|
156
|
-
} else {
|
|
157
|
-
state.pendingRequests.delete(matchingRequestOwner);
|
|
158
|
-
}
|
|
156
|
+
const { approved, authId, owner: decisionOwner } = payload;
|
|
157
|
+
const currentModeOwner = state.modeOwners.get(state.mode);
|
|
158
|
+
if (decisionOwner !== currentModeOwner) {
|
|
159
|
+
console.warn(`[MapMode] Authorization decision from "${decisionOwner}" ignored - not the owner of mode "${state.mode}" (owner: ${currentModeOwner || "none"})`);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
let matchingRequestOwner = null;
|
|
163
|
+
let matchingRequest = null;
|
|
164
|
+
for (const [requestOwner, request] of state.pendingRequests.entries()) if (request.authId === authId) {
|
|
165
|
+
matchingRequestOwner = requestOwner;
|
|
166
|
+
matchingRequest = request;
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
if (!(matchingRequest && matchingRequestOwner)) return;
|
|
170
|
+
if (approved) approveRequestAndRejectOthers(instanceId, state, matchingRequest, authId, decisionOwner, "", false);
|
|
171
|
+
else state.pendingRequests.delete(matchingRequestOwner);
|
|
159
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Handle mode change request logic
|
|
175
|
+
*/
|
|
160
176
|
function handleModeChangeRequest(instanceId, state, desiredMode, requestOwner) {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
id: instanceId
|
|
182
|
-
});
|
|
177
|
+
const desiredModeOwner = state.modeOwners.get(desiredMode);
|
|
178
|
+
if (shouldAutoAcceptRequest(state, desiredMode, requestOwner)) {
|
|
179
|
+
setMode(instanceId, state, desiredMode);
|
|
180
|
+
if (desiredMode !== DEFAULT_MODE && !desiredModeOwner) state.modeOwners.set(desiredMode, requestOwner);
|
|
181
|
+
state.pendingRequests.delete(requestOwner);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const authId = uuid();
|
|
185
|
+
state.pendingRequests.set(requestOwner, {
|
|
186
|
+
authId,
|
|
187
|
+
desiredMode,
|
|
188
|
+
currentMode: state.mode,
|
|
189
|
+
requestOwner
|
|
190
|
+
});
|
|
191
|
+
mapModeBus.emit(MapModeEvents.changeAuthorization, {
|
|
192
|
+
authId,
|
|
193
|
+
desiredMode,
|
|
194
|
+
currentMode: state.mode,
|
|
195
|
+
id: instanceId
|
|
196
|
+
});
|
|
183
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* Ensures a single bus listener exists for the given instanceId.
|
|
200
|
+
* All React subscribers will be notified via fan-out when the bus events fire.
|
|
201
|
+
* This prevents creating N bus listeners for N React components.
|
|
202
|
+
*
|
|
203
|
+
* @param instanceId - The unique identifier for the map mode instance
|
|
204
|
+
*/
|
|
184
205
|
function ensureBusListener(instanceId) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
});
|
|
212
|
-
busUnsubscribers.set(instanceId, () => {
|
|
213
|
-
unsubRequest();
|
|
214
|
-
unsubDecision();
|
|
215
|
-
unsubChanged();
|
|
216
|
-
});
|
|
206
|
+
if (busUnsubscribers.has(instanceId)) return;
|
|
207
|
+
const state = getOrCreateState(instanceId);
|
|
208
|
+
const unsubRequest = mapModeBus.on(MapModeEvents.changeRequest, (event) => {
|
|
209
|
+
const { desiredMode, owner: requestOwner, id } = event.payload;
|
|
210
|
+
if (id !== instanceId || desiredMode === state.mode) return;
|
|
211
|
+
handleModeChangeRequest(instanceId, state, desiredMode, requestOwner);
|
|
212
|
+
});
|
|
213
|
+
const unsubDecision = mapModeBus.on(MapModeEvents.changeDecision, (event) => {
|
|
214
|
+
const { id, approved, authId, owner } = event.payload;
|
|
215
|
+
if (id !== instanceId) return;
|
|
216
|
+
handleAuthorizationDecision(instanceId, state, {
|
|
217
|
+
approved,
|
|
218
|
+
authId,
|
|
219
|
+
owner
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
const unsubChanged = mapModeBus.on(MapModeEvents.changed, (event) => {
|
|
223
|
+
const { currentMode, previousMode, id } = event.payload;
|
|
224
|
+
if (id !== instanceId) return;
|
|
225
|
+
if (currentMode === DEFAULT_MODE && state.pendingRequests.size > 0) handlePendingRequestsOnDefaultMode(instanceId, state, previousMode);
|
|
226
|
+
});
|
|
227
|
+
busUnsubscribers.set(instanceId, () => {
|
|
228
|
+
unsubRequest();
|
|
229
|
+
unsubDecision();
|
|
230
|
+
unsubChanged();
|
|
231
|
+
});
|
|
217
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Cleans up the bus listener if no React subscribers remain.
|
|
235
|
+
*
|
|
236
|
+
* @param instanceId - The unique identifier for the map mode instance
|
|
237
|
+
*/
|
|
218
238
|
function cleanupBusListenerIfNeeded(instanceId) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
239
|
+
const subscribers = componentSubscribers.get(instanceId);
|
|
240
|
+
if (!subscribers || subscribers.size === 0) {
|
|
241
|
+
const unsub = busUnsubscribers.get(instanceId);
|
|
242
|
+
if (unsub) {
|
|
243
|
+
unsub();
|
|
244
|
+
busUnsubscribers.delete(instanceId);
|
|
245
|
+
}
|
|
246
|
+
modeStore.delete(instanceId);
|
|
247
|
+
componentSubscribers.delete(instanceId);
|
|
248
|
+
subscriptionCache.delete(instanceId);
|
|
249
|
+
snapshotCache.delete(instanceId);
|
|
250
|
+
serverSnapshotCache.delete(instanceId);
|
|
251
|
+
requestModeChangeCache.delete(instanceId);
|
|
252
|
+
}
|
|
232
253
|
}
|
|
254
|
+
/**
|
|
255
|
+
* Creates or retrieves a cached subscription function for a given instanceId.
|
|
256
|
+
* Uses a fan-out pattern: 1 bus listener -> N React subscribers.
|
|
257
|
+
* Automatically cleans up map mode state when the last subscriber unsubscribes.
|
|
258
|
+
*
|
|
259
|
+
* @param instanceId - The unique identifier for the map mode instance
|
|
260
|
+
* @returns A subscription function for useSyncExternalStore
|
|
261
|
+
*/
|
|
233
262
|
function getOrCreateSubscription(instanceId) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
subscriptionCache.set(instanceId, subscription);
|
|
252
|
-
return subscription;
|
|
263
|
+
const subscription = subscriptionCache.get(instanceId) ?? ((onStoreChange) => {
|
|
264
|
+
getOrCreateState(instanceId);
|
|
265
|
+
ensureBusListener(instanceId);
|
|
266
|
+
let subscriberSet = componentSubscribers.get(instanceId);
|
|
267
|
+
if (!subscriberSet) {
|
|
268
|
+
subscriberSet = /* @__PURE__ */ new Set();
|
|
269
|
+
componentSubscribers.set(instanceId, subscriberSet);
|
|
270
|
+
}
|
|
271
|
+
subscriberSet.add(onStoreChange);
|
|
272
|
+
return () => {
|
|
273
|
+
const currentSubscriberSet = componentSubscribers.get(instanceId);
|
|
274
|
+
if (currentSubscriberSet) currentSubscriberSet.delete(onStoreChange);
|
|
275
|
+
cleanupBusListenerIfNeeded(instanceId);
|
|
276
|
+
};
|
|
277
|
+
});
|
|
278
|
+
subscriptionCache.set(instanceId, subscription);
|
|
279
|
+
return subscription;
|
|
253
280
|
}
|
|
281
|
+
/**
|
|
282
|
+
* Creates or retrieves a cached snapshot function for a given instanceId.
|
|
283
|
+
* The string returned gets equality checked, so it needs to be stable or React re-renders unnecessarily.
|
|
284
|
+
*
|
|
285
|
+
* @param instanceId - The unique identifier for the map mode instance
|
|
286
|
+
* @returns A snapshot function for useSyncExternalStore
|
|
287
|
+
*/
|
|
254
288
|
function getOrCreateSnapshot(instanceId) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
289
|
+
const snapshot = snapshotCache.get(instanceId) ?? (() => {
|
|
290
|
+
const state = modeStore.get(instanceId);
|
|
291
|
+
if (!state) return DEFAULT_MODE;
|
|
292
|
+
return state.mode;
|
|
293
|
+
});
|
|
294
|
+
snapshotCache.set(instanceId, snapshot);
|
|
295
|
+
return snapshot;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Creates or retrieves a cached server snapshot function for a given instanceId.
|
|
299
|
+
* Server snapshots always return the default mode since mode state is client-only.
|
|
300
|
+
* Required for SSR/RSC compatibility with useSyncExternalStore.
|
|
301
|
+
*
|
|
302
|
+
* @param instanceId - The unique identifier for the map mode instance
|
|
303
|
+
* @returns A server snapshot function for useSyncExternalStore
|
|
304
|
+
*/
|
|
305
|
+
function getOrCreateServerSnapshot(instanceId) {
|
|
306
|
+
const serverSnapshot = serverSnapshotCache.get(instanceId) ?? (() => DEFAULT_MODE);
|
|
307
|
+
serverSnapshotCache.set(instanceId, serverSnapshot);
|
|
308
|
+
return serverSnapshot;
|
|
264
309
|
}
|
|
310
|
+
/**
|
|
311
|
+
* Creates or retrieves a cached requestModeChange function for a given instanceId.
|
|
312
|
+
* This maintains referential stability for the function reference.
|
|
313
|
+
*
|
|
314
|
+
* @param instanceId - The unique identifier for the map mode instance
|
|
315
|
+
* @returns A requestModeChange function for this instance
|
|
316
|
+
*/
|
|
265
317
|
function getOrCreateRequestModeChange(instanceId) {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
});
|
|
280
|
-
});
|
|
281
|
-
requestModeChangeCache.set(instanceId, requestModeChange);
|
|
282
|
-
return requestModeChange;
|
|
318
|
+
const requestModeChange = requestModeChangeCache.get(instanceId) ?? ((desiredMode, requestOwner) => {
|
|
319
|
+
const trimmedDesiredMode = desiredMode.trim();
|
|
320
|
+
const trimmedRequestOwner = requestOwner.trim();
|
|
321
|
+
if (!trimmedDesiredMode) throw new Error("requestModeChange requires non-empty desiredMode");
|
|
322
|
+
if (!trimmedRequestOwner) throw new Error("requestModeChange requires non-empty requestOwner");
|
|
323
|
+
mapModeBus.emit(MapModeEvents.changeRequest, {
|
|
324
|
+
desiredMode: trimmedDesiredMode,
|
|
325
|
+
owner: trimmedRequestOwner,
|
|
326
|
+
id: instanceId
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
requestModeChangeCache.set(instanceId, requestModeChange);
|
|
330
|
+
return requestModeChange;
|
|
283
331
|
}
|
|
332
|
+
/**
|
|
333
|
+
* Get the owner of the current mode for a given map instance
|
|
334
|
+
* @internal - For internal map-toolkit use only
|
|
335
|
+
*/
|
|
284
336
|
function getCurrentModeOwner(instanceId) {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
return state.modeOwners.get(state.mode);
|
|
337
|
+
const state = modeStore.get(instanceId);
|
|
338
|
+
if (!state) return;
|
|
339
|
+
return state.modeOwners.get(state.mode);
|
|
290
340
|
}
|
|
341
|
+
/**
|
|
342
|
+
* Manually clear map mode state for a specific instanceId.
|
|
343
|
+
* This is typically not needed as cleanup happens automatically when all subscribers unmount.
|
|
344
|
+
* Use this only in advanced scenarios where manual cleanup is required.
|
|
345
|
+
*
|
|
346
|
+
* @param instanceId - The unique identifier for the map mode instance to clear
|
|
347
|
+
*
|
|
348
|
+
* @example
|
|
349
|
+
* ```tsx
|
|
350
|
+
* // Manual cleanup (rarely needed)
|
|
351
|
+
* clearMapModeState('my-map-instance');
|
|
352
|
+
* ```
|
|
353
|
+
*/
|
|
291
354
|
function clearMapModeState(instanceId) {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
355
|
+
const unsub = busUnsubscribers.get(instanceId);
|
|
356
|
+
if (unsub) {
|
|
357
|
+
unsub();
|
|
358
|
+
busUnsubscribers.delete(instanceId);
|
|
359
|
+
}
|
|
360
|
+
modeStore.delete(instanceId);
|
|
361
|
+
componentSubscribers.delete(instanceId);
|
|
362
|
+
subscriptionCache.delete(instanceId);
|
|
363
|
+
snapshotCache.delete(instanceId);
|
|
364
|
+
serverSnapshotCache.delete(instanceId);
|
|
365
|
+
requestModeChangeCache.delete(instanceId);
|
|
302
366
|
}
|
|
303
367
|
|
|
304
|
-
|
|
305
|
-
|
|
368
|
+
//#endregion
|
|
369
|
+
export { clearMapModeState, getCurrentModeOwner, getOrCreateRequestModeChange, getOrCreateServerSnapshot, getOrCreateSnapshot, getOrCreateSubscription };
|
|
306
370
|
//# sourceMappingURL=store.js.map
|