@cloudparse/up-miniapps-sdk 0.1.6 → 0.2.1
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/README.md +167 -2
- package/dist/chunk-S4UHRBSM.js +252 -0
- package/dist/chunk-S4UHRBSM.js.map +1 -0
- package/dist/host/index.d.cts +2 -0
- package/dist/host/index.d.ts +2 -0
- package/dist/host.cjs +279 -0
- package/dist/host.cjs.map +1 -0
- package/dist/host.js +11 -0
- package/dist/host.js.map +1 -0
- package/dist/index-B9dDciJq.d.cts +65 -0
- package/dist/index-B9dDciJq.d.ts +65 -0
- package/dist/index.browser.min.js +879 -1
- package/dist/index.browser.min.js.map +1 -1
- package/dist/index.cjs +3859 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +2 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.cts +1146 -0
- package/dist/index.d.ts +770 -152
- package/dist/index.js +3257 -57
- package/dist/index.js.map +1 -1
- package/dist/style.css +2291 -0
- package/package.json +55 -13
- package/dist/index.d.mts +0 -528
- package/dist/index.mjs +0 -348
- package/dist/index.mjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,31 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/index.ts
|
|
21
|
-
var index_exports = {};
|
|
22
|
-
__export(index_exports, {
|
|
23
|
-
MiniAppSDK: () => MiniAppSDK,
|
|
24
|
-
UIEvent: () => UIEvent,
|
|
25
|
-
default: () => index_default,
|
|
26
|
-
sdk: () => sdk
|
|
27
|
-
});
|
|
28
|
-
module.exports = __toCommonJS(index_exports);
|
|
1
|
+
import {
|
|
2
|
+
RoutingService,
|
|
3
|
+
handleClearRoute,
|
|
4
|
+
handleDrawRoute,
|
|
5
|
+
handleRouteRequest
|
|
6
|
+
} from "./chunk-S4UHRBSM.js";
|
|
29
7
|
|
|
30
8
|
// src/bridge.ts
|
|
31
9
|
var Bridge = class {
|
|
@@ -41,16 +19,19 @@ var Bridge = class {
|
|
|
41
19
|
const bridgeReceiver = Object.freeze({
|
|
42
20
|
onNativeMessage: (envelope) => this.handleNativeMessage(envelope)
|
|
43
21
|
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
22
|
+
if (typeof window.__MiniAppBridge === "undefined") {
|
|
23
|
+
Object.defineProperty(window, "__MiniAppBridge", {
|
|
24
|
+
configurable: true,
|
|
25
|
+
// MUST be true for React StrictMode / HMR unmount/remount cycles
|
|
26
|
+
value: bridgeReceiver,
|
|
27
|
+
writable: false
|
|
28
|
+
});
|
|
29
|
+
}
|
|
49
30
|
window.addEventListener("message", (event) => {
|
|
50
31
|
if (!event.data) return;
|
|
51
32
|
try {
|
|
52
33
|
const data = typeof event.data === "string" ? JSON.parse(event.data) : event.data;
|
|
53
|
-
if (data && typeof data === "object" && (data.callbackId || data.event)) {
|
|
34
|
+
if (data && typeof data === "object" && (data.callbackId || data.event || data.eventName)) {
|
|
54
35
|
this.handleNativeMessage(data);
|
|
55
36
|
}
|
|
56
37
|
} catch (_e) {
|
|
@@ -98,6 +79,9 @@ var Bridge = class {
|
|
|
98
79
|
* @returns A promise that resolves with the native response.
|
|
99
80
|
*/
|
|
100
81
|
send(type, payload, timeoutMs = 3e4) {
|
|
82
|
+
if (!this.isNative()) {
|
|
83
|
+
return this.handleMockMessage(type, payload);
|
|
84
|
+
}
|
|
101
85
|
return new Promise((resolve, reject) => {
|
|
102
86
|
const callbackId = `${type}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
103
87
|
const timeout = setTimeout(() => {
|
|
@@ -150,21 +134,2478 @@ var Bridge = class {
|
|
|
150
134
|
return;
|
|
151
135
|
}
|
|
152
136
|
}
|
|
153
|
-
const eventName = envelope.event;
|
|
154
|
-
if (eventName
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
137
|
+
const eventName = envelope.eventName || envelope.event;
|
|
138
|
+
if (eventName) {
|
|
139
|
+
if (envelope.type === "EVENT" || !envelope.type || envelope.event) {
|
|
140
|
+
const listeners = this.eventListeners.get(eventName);
|
|
141
|
+
if (listeners) {
|
|
142
|
+
for (const listener of listeners) {
|
|
143
|
+
try {
|
|
144
|
+
listener(envelope.payload || envelope.value);
|
|
145
|
+
} catch (e) {
|
|
146
|
+
console.error(`Error in event listener for ${eventName}:`, e);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Internal handler for mock message processing, overrideable in test environments
|
|
155
|
+
*/
|
|
156
|
+
handleMockMessage(type, payload) {
|
|
157
|
+
console.warn(`[Bridge] Mock mode active: Unhandled message type ${type}`);
|
|
158
|
+
return Promise.resolve();
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Determine environment. Check flag or cache result.
|
|
162
|
+
*/
|
|
163
|
+
isNative() {
|
|
164
|
+
if (typeof window === "undefined") return false;
|
|
165
|
+
return window.__UP_REACT_NATIVE_APP__ === true || !!window.ReactNativeWebView;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// src/components/Map/components/MapDrawer.tsx
|
|
170
|
+
import {
|
|
171
|
+
forwardRef,
|
|
172
|
+
useEffect,
|
|
173
|
+
useImperativeHandle,
|
|
174
|
+
useRef,
|
|
175
|
+
useState
|
|
176
|
+
} from "react";
|
|
177
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
178
|
+
var resolveHeightToPixels = (height, containerHeight) => {
|
|
179
|
+
if (typeof height === "number") {
|
|
180
|
+
return height;
|
|
181
|
+
}
|
|
182
|
+
if (height.endsWith("%")) {
|
|
183
|
+
return parseFloat(height) / 100 * containerHeight;
|
|
184
|
+
}
|
|
185
|
+
return parseFloat(height);
|
|
186
|
+
};
|
|
187
|
+
var MapDrawer = forwardRef(
|
|
188
|
+
({
|
|
189
|
+
menuItems,
|
|
190
|
+
state,
|
|
191
|
+
onChangeState,
|
|
192
|
+
side,
|
|
193
|
+
etaBarHeight = 0,
|
|
194
|
+
etaData,
|
|
195
|
+
touchStartY,
|
|
196
|
+
setTouchStartY,
|
|
197
|
+
currentTouchY,
|
|
198
|
+
setCurrentTouchY,
|
|
199
|
+
isDragging,
|
|
200
|
+
clampedDragOffset,
|
|
201
|
+
drawerPeekHeight,
|
|
202
|
+
drawerFullHeight,
|
|
203
|
+
containerHeight,
|
|
204
|
+
menuContent,
|
|
205
|
+
enableTouchDrag = false,
|
|
206
|
+
showScrollToTopButton = true,
|
|
207
|
+
scrollToTopThreshold = 300,
|
|
208
|
+
safeAreaInsets = { bottom: 0, left: 0, right: 0, top: 0 }
|
|
209
|
+
}, ref) => {
|
|
210
|
+
const scrollContainerRef = useRef(null);
|
|
211
|
+
const containerRef = useRef(null);
|
|
212
|
+
useImperativeHandle(
|
|
213
|
+
ref,
|
|
214
|
+
() => scrollContainerRef.current
|
|
215
|
+
);
|
|
216
|
+
const handleHandleClick = () => {
|
|
217
|
+
if (side === "left") {
|
|
218
|
+
onChangeState(state === "closed" ? "full" : "closed");
|
|
219
|
+
} else {
|
|
220
|
+
if (state === "closed") {
|
|
221
|
+
onChangeState("peek");
|
|
222
|
+
} else if (state === "peek") {
|
|
223
|
+
onChangeState("full");
|
|
224
|
+
} else {
|
|
225
|
+
onChangeState("peek");
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
const [showScrollTop, setShowScrollTop] = useState(false);
|
|
230
|
+
const handleScroll = (e) => {
|
|
231
|
+
const scrollTop = e.currentTarget.scrollTop;
|
|
232
|
+
setShowScrollTop(state !== "closed" && scrollTop > scrollToTopThreshold);
|
|
233
|
+
};
|
|
234
|
+
useEffect(() => {
|
|
235
|
+
if (state === "closed") {
|
|
236
|
+
setShowScrollTop(false);
|
|
237
|
+
} else {
|
|
238
|
+
const scrollTop = scrollContainerRef.current?.scrollTop || 0;
|
|
239
|
+
setShowScrollTop(scrollTop > scrollToTopThreshold);
|
|
240
|
+
}
|
|
241
|
+
}, [state, scrollToTopThreshold]);
|
|
242
|
+
const isDraggingSheet = useRef(false);
|
|
243
|
+
const hasDeterminedDirection = useRef(false);
|
|
244
|
+
const isTouchOnContent = useRef(false);
|
|
245
|
+
const stateRef = useRef(state);
|
|
246
|
+
const touchStartYRef = useRef(touchStartY);
|
|
247
|
+
const currentTouchYRef = useRef(currentTouchY);
|
|
248
|
+
useEffect(() => {
|
|
249
|
+
stateRef.current = state;
|
|
250
|
+
}, [state]);
|
|
251
|
+
useEffect(() => {
|
|
252
|
+
touchStartYRef.current = touchStartY;
|
|
253
|
+
}, [touchStartY]);
|
|
254
|
+
useEffect(() => {
|
|
255
|
+
currentTouchYRef.current = currentTouchY;
|
|
256
|
+
}, [currentTouchY]);
|
|
257
|
+
useEffect(() => {
|
|
258
|
+
const container = containerRef.current;
|
|
259
|
+
if (!container || side !== "bottom" || !enableTouchDrag) return;
|
|
260
|
+
const handleNativeTouchStart = (e) => {
|
|
261
|
+
const clientY = e.targetTouches[0].clientY;
|
|
262
|
+
setTouchStartY(clientY);
|
|
263
|
+
setCurrentTouchY(clientY);
|
|
264
|
+
isDraggingSheet.current = false;
|
|
265
|
+
hasDeterminedDirection.current = false;
|
|
266
|
+
const target = e.target;
|
|
267
|
+
isTouchOnContent.current = scrollContainerRef.current ? scrollContainerRef.current.contains(target) : false;
|
|
268
|
+
};
|
|
269
|
+
const handleNativeTouchMove = (e) => {
|
|
270
|
+
const startY = touchStartYRef.current;
|
|
271
|
+
if (startY === null) return;
|
|
272
|
+
const currentY = e.targetTouches[0].clientY;
|
|
273
|
+
const deltaY = startY - currentY;
|
|
274
|
+
const scrollTop = scrollContainerRef.current ? scrollContainerRef.current.scrollTop : 0;
|
|
275
|
+
const clientHeight = scrollContainerRef.current ? scrollContainerRef.current.clientHeight : 0;
|
|
276
|
+
const scrollHeight = scrollContainerRef.current ? scrollContainerRef.current.scrollHeight : 0;
|
|
277
|
+
const isAtBottom = scrollTop + clientHeight >= scrollHeight - 1;
|
|
278
|
+
const currentState = stateRef.current;
|
|
279
|
+
if (!hasDeterminedDirection.current) {
|
|
280
|
+
if (!isTouchOnContent.current) {
|
|
281
|
+
isDraggingSheet.current = true;
|
|
282
|
+
hasDeterminedDirection.current = true;
|
|
283
|
+
} else if (currentState !== "full") {
|
|
284
|
+
if (deltaY > 0 && isAtBottom) {
|
|
285
|
+
isDraggingSheet.current = true;
|
|
286
|
+
hasDeterminedDirection.current = true;
|
|
287
|
+
} else if (deltaY < 0 && scrollTop <= 0) {
|
|
288
|
+
isDraggingSheet.current = true;
|
|
289
|
+
hasDeterminedDirection.current = true;
|
|
290
|
+
} else {
|
|
291
|
+
isDraggingSheet.current = false;
|
|
292
|
+
hasDeterminedDirection.current = true;
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
if (deltaY < 0 && scrollTop <= 0) {
|
|
296
|
+
isDraggingSheet.current = true;
|
|
297
|
+
hasDeterminedDirection.current = true;
|
|
298
|
+
} else {
|
|
299
|
+
isDraggingSheet.current = false;
|
|
300
|
+
hasDeterminedDirection.current = true;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
} else if (!isDraggingSheet.current && isTouchOnContent.current) {
|
|
304
|
+
if (deltaY < 0 && scrollTop <= 0) {
|
|
305
|
+
isDraggingSheet.current = true;
|
|
306
|
+
setTouchStartY(currentY);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (deltaY > 0 && isAtBottom && currentState !== "full") {
|
|
310
|
+
isDraggingSheet.current = true;
|
|
311
|
+
setTouchStartY(currentY);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (isDraggingSheet.current) {
|
|
316
|
+
if (e.cancelable) {
|
|
317
|
+
e.preventDefault();
|
|
318
|
+
}
|
|
319
|
+
const parentContainer = container.parentElement;
|
|
320
|
+
if (parentContainer) {
|
|
321
|
+
const containerH2 = containerHeight || parentContainer.getBoundingClientRect().height;
|
|
322
|
+
const fullPx2 = resolveHeightToPixels(
|
|
323
|
+
drawerFullHeight || "85%",
|
|
324
|
+
containerH2
|
|
325
|
+
);
|
|
326
|
+
const peekPx2 = resolveHeightToPixels(
|
|
327
|
+
drawerPeekHeight || "15%",
|
|
328
|
+
containerH2
|
|
329
|
+
);
|
|
330
|
+
const closedPx = 32 + (currentState === "closed" ? safeAreaInsets.bottom : 0);
|
|
331
|
+
const dragOffset = currentY - startY;
|
|
332
|
+
let localClamped = dragOffset;
|
|
333
|
+
if (currentState === "full") {
|
|
334
|
+
localClamped = Math.max(
|
|
335
|
+
0,
|
|
336
|
+
Math.min(fullPx2 - closedPx, dragOffset)
|
|
337
|
+
);
|
|
338
|
+
} else if (currentState === "peek") {
|
|
339
|
+
localClamped = Math.max(
|
|
340
|
+
-(fullPx2 - peekPx2),
|
|
341
|
+
Math.min(peekPx2 - closedPx, dragOffset)
|
|
342
|
+
);
|
|
343
|
+
} else if (currentState === "closed") {
|
|
344
|
+
localClamped = Math.min(
|
|
345
|
+
0,
|
|
346
|
+
Math.max(-(fullPx2 - closedPx), dragOffset)
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
parentContainer.style.setProperty(
|
|
350
|
+
"--drag-offset",
|
|
351
|
+
`${localClamped}px`
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
const handleNativeTouchEnd = (e) => {
|
|
357
|
+
const startY = touchStartYRef.current;
|
|
358
|
+
const currentY = currentTouchYRef.current;
|
|
359
|
+
const parentContainer = container.parentElement;
|
|
360
|
+
if (startY === null || currentY === null) {
|
|
361
|
+
if (parentContainer) {
|
|
362
|
+
parentContainer.style.setProperty("--drag-offset", "0px");
|
|
363
|
+
}
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
if (!isDraggingSheet.current) {
|
|
367
|
+
setTouchStartY(null);
|
|
368
|
+
setCurrentTouchY(null);
|
|
369
|
+
if (parentContainer) {
|
|
370
|
+
parentContainer.style.setProperty("--drag-offset", "0px");
|
|
371
|
+
}
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
const touchEndY = e.changedTouches[0].clientY;
|
|
375
|
+
const dragDistance = Math.abs(touchEndY - startY);
|
|
376
|
+
if (dragDistance < 10) {
|
|
377
|
+
setTouchStartY(null);
|
|
378
|
+
setCurrentTouchY(null);
|
|
379
|
+
if (parentContainer) {
|
|
380
|
+
parentContainer.style.setProperty("--drag-offset", "0px");
|
|
381
|
+
}
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const containerH2 = containerHeight || (parentContainer ? parentContainer.getBoundingClientRect().height : window.innerHeight);
|
|
385
|
+
const containerTop = parentContainer ? parentContainer.getBoundingClientRect().top : 0;
|
|
386
|
+
const touchYInContainer = touchEndY - containerTop;
|
|
387
|
+
const drawerHeightFromBottom = containerH2 - touchYInContainer;
|
|
388
|
+
const fullPx2 = resolveHeightToPixels(
|
|
389
|
+
drawerFullHeight || "85%",
|
|
390
|
+
containerH2
|
|
391
|
+
);
|
|
392
|
+
const peekPx2 = resolveHeightToPixels(
|
|
393
|
+
drawerPeekHeight || "15%",
|
|
394
|
+
containerH2
|
|
395
|
+
);
|
|
396
|
+
const currentState = stateRef.current;
|
|
397
|
+
const closedPx = 32 + (currentState === "closed" ? safeAreaInsets.bottom : 0);
|
|
398
|
+
const distToFull = Math.abs(drawerHeightFromBottom - fullPx2);
|
|
399
|
+
const distToPeek = Math.abs(drawerHeightFromBottom - peekPx2);
|
|
400
|
+
const distToClosed = Math.abs(drawerHeightFromBottom - closedPx);
|
|
401
|
+
const minDist = Math.min(distToFull, distToPeek, distToClosed);
|
|
402
|
+
let targetState = "closed";
|
|
403
|
+
if (minDist === distToFull) {
|
|
404
|
+
targetState = "full";
|
|
405
|
+
} else if (minDist === distToPeek) {
|
|
406
|
+
targetState = "peek";
|
|
407
|
+
} else {
|
|
408
|
+
targetState = "closed";
|
|
409
|
+
}
|
|
410
|
+
setTouchStartY(null);
|
|
411
|
+
setCurrentTouchY(null);
|
|
412
|
+
onChangeState(targetState);
|
|
413
|
+
requestAnimationFrame(() => {
|
|
414
|
+
if (parentContainer) {
|
|
415
|
+
parentContainer.style.setProperty("--drag-offset", "0px");
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
};
|
|
419
|
+
container.addEventListener("touchstart", handleNativeTouchStart, {
|
|
420
|
+
passive: true
|
|
421
|
+
});
|
|
422
|
+
container.addEventListener("touchmove", handleNativeTouchMove, {
|
|
423
|
+
passive: false
|
|
424
|
+
});
|
|
425
|
+
container.addEventListener("touchend", handleNativeTouchEnd, {
|
|
426
|
+
passive: true
|
|
427
|
+
});
|
|
428
|
+
return () => {
|
|
429
|
+
container.removeEventListener("touchstart", handleNativeTouchStart);
|
|
430
|
+
container.removeEventListener("touchmove", handleNativeTouchMove);
|
|
431
|
+
container.removeEventListener("touchend", handleNativeTouchEnd);
|
|
432
|
+
};
|
|
433
|
+
}, [
|
|
434
|
+
side,
|
|
435
|
+
enableTouchDrag,
|
|
436
|
+
containerHeight,
|
|
437
|
+
drawerFullHeight,
|
|
438
|
+
drawerPeekHeight,
|
|
439
|
+
safeAreaInsets,
|
|
440
|
+
onChangeState,
|
|
441
|
+
setTouchStartY,
|
|
442
|
+
setCurrentTouchY
|
|
443
|
+
]);
|
|
444
|
+
const isBackdropActive = state === "full";
|
|
445
|
+
const backdropStyles = isBackdropActive ? "opacity-100 pointer-events-auto" : "opacity-0 pointer-events-none";
|
|
446
|
+
const containerH = containerHeight || window.innerHeight;
|
|
447
|
+
const fullPx = resolveHeightToPixels(drawerFullHeight || "85%", containerH);
|
|
448
|
+
const peekPx = resolveHeightToPixels(drawerPeekHeight || "15%", containerH);
|
|
449
|
+
const shiftPx = fullPx - peekPx;
|
|
450
|
+
const getBottomDrawerTransform = () => {
|
|
451
|
+
if (state === "full") {
|
|
452
|
+
return "translateY(var(--drag-offset, 0px))";
|
|
453
|
+
}
|
|
454
|
+
if (state === "peek") {
|
|
455
|
+
return `translateY(calc(${shiftPx}px + var(--drag-offset, 0px)))`;
|
|
456
|
+
}
|
|
457
|
+
const closedVisibleHeight = 32;
|
|
458
|
+
const closedShiftPx = fullPx - closedVisibleHeight;
|
|
459
|
+
return `translateY(calc(${closedShiftPx}px + var(--drag-offset, 0px)))`;
|
|
460
|
+
};
|
|
461
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
462
|
+
/* @__PURE__ */ jsx("style", { children: `
|
|
463
|
+
.no-scrollbar {
|
|
464
|
+
-ms-overflow-style: none;
|
|
465
|
+
scrollbar-width: none;
|
|
466
|
+
}
|
|
467
|
+
.no-scrollbar::-webkit-scrollbar {
|
|
468
|
+
display: none;
|
|
469
|
+
}
|
|
470
|
+
` }),
|
|
471
|
+
/* @__PURE__ */ jsx(
|
|
472
|
+
"div",
|
|
473
|
+
{
|
|
474
|
+
className: `absolute inset-0 bg-black/45 backdrop-blur-[1px] transition-opacity duration-300 z-30 ${backdropStyles}`,
|
|
475
|
+
onClick: () => onChangeState("closed")
|
|
476
|
+
}
|
|
477
|
+
),
|
|
478
|
+
side === "left" ? (
|
|
479
|
+
/* Left Drawer Panel */
|
|
480
|
+
/* @__PURE__ */ jsxs(
|
|
481
|
+
"div",
|
|
482
|
+
{
|
|
483
|
+
className: `absolute top-4 left-0 w-80 bg-zinc-950/85 backdrop-blur-md border-r border-y border-white/10 rounded-r-2xl shadow-2xl flex z-40 transition-transform duration-300 ease-out select-none ${state !== "closed" ? "translate-x-0" : "translate-x-[calc(-100%+24px)]"}`,
|
|
484
|
+
ref: containerRef,
|
|
485
|
+
style: {
|
|
486
|
+
bottom: `calc(1rem + ${etaBarHeight}px)`
|
|
487
|
+
},
|
|
488
|
+
children: [
|
|
489
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col h-full overflow-hidden pl-4 py-4 pr-1 relative", children: [
|
|
490
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between pb-3 border-b border-white/10 mb-2", children: /* @__PURE__ */ jsx("span", { className: "text-sm font-bold tracking-wider text-zinc-400 uppercase", children: "Menu" }) }),
|
|
491
|
+
/* @__PURE__ */ jsx(
|
|
492
|
+
"div",
|
|
493
|
+
{
|
|
494
|
+
className: "flex-1 overflow-y-auto no-scrollbar pr-2",
|
|
495
|
+
onScroll: handleScroll,
|
|
496
|
+
ref: scrollContainerRef,
|
|
497
|
+
children: menuContent ? menuContent : /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1.5", children: menuItems.map((item) => /* @__PURE__ */ jsxs(
|
|
498
|
+
"button",
|
|
499
|
+
{
|
|
500
|
+
className: `flex items-center gap-3 w-full py-3 px-3.5 rounded-xl text-left cursor-pointer transition-all duration-150 ${item.disabled ? "opacity-45 cursor-not-allowed pointer-events-none" : "hover:bg-white/5 active:bg-white/10 text-white"}`,
|
|
501
|
+
disabled: item.disabled,
|
|
502
|
+
onClick: () => {
|
|
503
|
+
if (!item.disabled) {
|
|
504
|
+
item.onPress();
|
|
505
|
+
onChangeState("closed");
|
|
506
|
+
}
|
|
507
|
+
},
|
|
508
|
+
children: [
|
|
509
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center w-8 h-8 rounded-lg bg-white/5 text-zinc-300", children: [
|
|
510
|
+
item.icon,
|
|
511
|
+
item.badge !== void 0 && /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-1 bg-cyan-500 text-black text-[9px] font-extrabold min-w-[15px] h-3.5 rounded-full flex items-center justify-center px-1 border border-zinc-950 shadow", children: item.badge })
|
|
512
|
+
] }),
|
|
513
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium tracking-wide flex-1", children: item.label })
|
|
514
|
+
]
|
|
515
|
+
},
|
|
516
|
+
item.id
|
|
517
|
+
)) })
|
|
518
|
+
}
|
|
519
|
+
),
|
|
520
|
+
showScrollToTopButton && showScrollTop && /* @__PURE__ */ jsx(
|
|
521
|
+
"button",
|
|
522
|
+
{
|
|
523
|
+
className: "absolute bottom-6 right-6 z-50 flex items-center justify-center w-10 h-10 rounded-full bg-cyan-500 hover:bg-cyan-400 active:scale-95 text-black shadow-lg shadow-cyan-500/25 transition-all duration-200 cursor-pointer",
|
|
524
|
+
onClick: () => {
|
|
525
|
+
scrollContainerRef.current?.scrollTo({
|
|
526
|
+
behavior: "smooth",
|
|
527
|
+
top: 0
|
|
528
|
+
});
|
|
529
|
+
},
|
|
530
|
+
children: /* @__PURE__ */ jsx(
|
|
531
|
+
"svg",
|
|
532
|
+
{
|
|
533
|
+
fill: "none",
|
|
534
|
+
height: "20",
|
|
535
|
+
stroke: "currentColor",
|
|
536
|
+
strokeLinecap: "round",
|
|
537
|
+
strokeLinejoin: "round",
|
|
538
|
+
strokeWidth: "2.5",
|
|
539
|
+
viewBox: "0 0 24 24",
|
|
540
|
+
width: "20",
|
|
541
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
542
|
+
children: /* @__PURE__ */ jsx("polyline", { points: "18 15 12 9 6 15" })
|
|
543
|
+
}
|
|
544
|
+
)
|
|
545
|
+
}
|
|
546
|
+
)
|
|
547
|
+
] }),
|
|
548
|
+
/* @__PURE__ */ jsx(
|
|
549
|
+
"div",
|
|
550
|
+
{
|
|
551
|
+
className: "w-6 flex items-center justify-center cursor-pointer hover:bg-white/5 active:bg-white/10 transition-colors border-l border-white/5 rounded-r-2xl",
|
|
552
|
+
onClick: handleHandleClick,
|
|
553
|
+
children: /* @__PURE__ */ jsx("div", { className: "w-1.5 h-12 bg-white/20 rounded-full transition-colors group-hover:bg-white/40" })
|
|
554
|
+
}
|
|
555
|
+
)
|
|
556
|
+
]
|
|
557
|
+
}
|
|
558
|
+
)
|
|
559
|
+
) : (
|
|
560
|
+
/* Bottom Sheet Panel - Hardware accelerated translateY transitions */
|
|
561
|
+
/* @__PURE__ */ jsxs(
|
|
562
|
+
"div",
|
|
563
|
+
{
|
|
564
|
+
className: `absolute w-full bg-zinc-950/85 backdrop-blur-md border border-white/10 rounded-t-2xl shadow-2xl flex flex-col z-40 select-none ${isDragging ? "transition-none" : "transition-transform duration-300 ease-out"}`,
|
|
565
|
+
ref: containerRef,
|
|
566
|
+
style: {
|
|
567
|
+
bottom: "0px",
|
|
568
|
+
height: typeof drawerFullHeight === "number" ? `${drawerFullHeight}px` : drawerFullHeight || "85%",
|
|
569
|
+
marginBottom: state === "closed" ? `${safeAreaInsets.bottom}px` : "0px",
|
|
570
|
+
transform: getBottomDrawerTransform(),
|
|
571
|
+
transition: isDragging ? "none" : "transform 300ms ease-out, margin-bottom 250ms ease-in-out"
|
|
572
|
+
},
|
|
573
|
+
children: [
|
|
574
|
+
/* @__PURE__ */ jsx(
|
|
575
|
+
"div",
|
|
576
|
+
{
|
|
577
|
+
className: "w-full h-8 flex flex-col items-center justify-center cursor-pointer hover:bg-white/5 rounded-t-2xl transition-colors border-b border-white/5",
|
|
578
|
+
onClick: handleHandleClick,
|
|
579
|
+
children: /* @__PURE__ */ jsx("div", { className: "w-12 h-1 bg-white/25 rounded-full" })
|
|
580
|
+
}
|
|
581
|
+
),
|
|
582
|
+
/* @__PURE__ */ jsxs(
|
|
583
|
+
"div",
|
|
584
|
+
{
|
|
585
|
+
className: "flex-1 flex flex-col overflow-hidden px-4 pb-4 pt-2 transition-[margin-top] duration-250 ease-in-out relative",
|
|
586
|
+
style: {
|
|
587
|
+
marginTop: state === "closed" ? `${safeAreaInsets.bottom}px` : "0px"
|
|
588
|
+
},
|
|
589
|
+
children: [
|
|
590
|
+
/* @__PURE__ */ jsx(
|
|
591
|
+
"div",
|
|
592
|
+
{
|
|
593
|
+
className: "flex-1 overflow-y-auto no-scrollbar",
|
|
594
|
+
onScroll: handleScroll,
|
|
595
|
+
ref: scrollContainerRef,
|
|
596
|
+
style: {
|
|
597
|
+
paddingBottom: state === "peek" ? `${shiftPx}px` : "0px"
|
|
598
|
+
},
|
|
599
|
+
children: menuContent ? menuContent : /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1", children: menuItems.map((item) => /* @__PURE__ */ jsxs(
|
|
600
|
+
"button",
|
|
601
|
+
{
|
|
602
|
+
className: `flex items-center gap-3 w-full py-3 px-3.5 rounded-xl text-left cursor-pointer transition-all duration-150 ${item.disabled ? "opacity-45 cursor-not-allowed pointer-events-none" : "hover:bg-white/5 active:bg-white/10 text-white"}`,
|
|
603
|
+
disabled: item.disabled,
|
|
604
|
+
onClick: () => {
|
|
605
|
+
if (!item.disabled) {
|
|
606
|
+
item.onPress();
|
|
607
|
+
onChangeState("closed");
|
|
608
|
+
}
|
|
609
|
+
},
|
|
610
|
+
children: [
|
|
611
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center w-8 h-8 rounded-lg bg-white/5 text-zinc-300", children: [
|
|
612
|
+
item.icon,
|
|
613
|
+
item.badge !== void 0 && /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-1 bg-cyan-500 text-black text-[9px] font-extrabold min-w-[15px] h-3.5 rounded-full flex items-center justify-center px-1 border border-zinc-950 shadow", children: item.badge })
|
|
614
|
+
] }),
|
|
615
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium tracking-wide", children: item.label })
|
|
616
|
+
]
|
|
617
|
+
},
|
|
618
|
+
item.id
|
|
619
|
+
)) })
|
|
620
|
+
}
|
|
621
|
+
),
|
|
622
|
+
showScrollToTopButton && showScrollTop && /* @__PURE__ */ jsx(
|
|
623
|
+
"button",
|
|
624
|
+
{
|
|
625
|
+
className: "absolute right-6 z-50 flex items-center justify-center w-10 h-10 rounded-full bg-cyan-500 hover:bg-cyan-400 active:scale-95 text-black shadow-lg shadow-cyan-500/25 transition-all duration-200 cursor-pointer",
|
|
626
|
+
onClick: () => {
|
|
627
|
+
scrollContainerRef.current?.scrollTo({
|
|
628
|
+
behavior: "smooth",
|
|
629
|
+
top: 0
|
|
630
|
+
});
|
|
631
|
+
},
|
|
632
|
+
style: {
|
|
633
|
+
bottom: state === "peek" ? `calc(${24 + shiftPx}px + var(--drag-offset, 0px))` : `calc(24px + var(--drag-offset, 0px))`
|
|
634
|
+
},
|
|
635
|
+
children: /* @__PURE__ */ jsx(
|
|
636
|
+
"svg",
|
|
637
|
+
{
|
|
638
|
+
fill: "none",
|
|
639
|
+
height: "20",
|
|
640
|
+
stroke: "currentColor",
|
|
641
|
+
strokeLinecap: "round",
|
|
642
|
+
strokeLinejoin: "round",
|
|
643
|
+
strokeWidth: "2.5",
|
|
644
|
+
viewBox: "0 0 24 24",
|
|
645
|
+
width: "20",
|
|
646
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
647
|
+
children: /* @__PURE__ */ jsx("polyline", { points: "18 15 12 9 6 15" })
|
|
648
|
+
}
|
|
649
|
+
)
|
|
650
|
+
}
|
|
651
|
+
)
|
|
652
|
+
]
|
|
653
|
+
}
|
|
654
|
+
)
|
|
655
|
+
]
|
|
656
|
+
}
|
|
657
|
+
)
|
|
658
|
+
)
|
|
659
|
+
] });
|
|
660
|
+
}
|
|
661
|
+
);
|
|
662
|
+
|
|
663
|
+
// src/components/Map/components/MapEtaPanel.tsx
|
|
664
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
665
|
+
var MapEtaPanel = ({
|
|
666
|
+
etaData,
|
|
667
|
+
layoutMode,
|
|
668
|
+
onHeaderClick,
|
|
669
|
+
style,
|
|
670
|
+
isDragging = false
|
|
671
|
+
}) => {
|
|
672
|
+
if (layoutMode === "closed-banner") {
|
|
673
|
+
return /* @__PURE__ */ jsxs2("div", { className: "w-full h-10 flex items-center justify-between px-4 pb-2 pt-1 border-b border-white/5 bg-zinc-950/15 relative overflow-hidden", children: [
|
|
674
|
+
/* @__PURE__ */ jsxs2(
|
|
675
|
+
"div",
|
|
676
|
+
{
|
|
677
|
+
className: "flex items-center gap-2 cursor-pointer flex-1",
|
|
678
|
+
onClick: onHeaderClick,
|
|
679
|
+
children: [
|
|
680
|
+
/* @__PURE__ */ jsx2("span", { className: "w-2 h-2 rounded-full bg-emerald-500 animate-pulse" }),
|
|
681
|
+
/* @__PURE__ */ jsxs2("span", { className: "text-xs font-bold text-emerald-400", children: [
|
|
682
|
+
etaData.etaMins,
|
|
683
|
+
" min (",
|
|
684
|
+
etaData.distanceRemainingKm.toFixed(1),
|
|
685
|
+
" km)"
|
|
686
|
+
] })
|
|
687
|
+
]
|
|
688
|
+
}
|
|
689
|
+
),
|
|
690
|
+
etaData.onAbort && /* @__PURE__ */ jsx2(
|
|
691
|
+
"button",
|
|
692
|
+
{
|
|
693
|
+
className: "px-1 py-1 text-[10px] font-black bg-red-500/20 text-red-400 hover:bg-red-500/30 border border-red-500/25 active:scale-95 rounded-md transition-all cursor-pointer pointer-events-auto",
|
|
694
|
+
onClick: (e) => {
|
|
695
|
+
e.stopPropagation();
|
|
696
|
+
etaData.onAbort?.();
|
|
697
|
+
},
|
|
698
|
+
children: /* @__PURE__ */ jsxs2(
|
|
699
|
+
"svg",
|
|
700
|
+
{
|
|
701
|
+
className: "lucide lucide-x",
|
|
702
|
+
fill: "none",
|
|
703
|
+
height: "18",
|
|
704
|
+
stroke: "currentColor",
|
|
705
|
+
"stroke-linecap": "round",
|
|
706
|
+
"stroke-linejoin": "round",
|
|
707
|
+
"stroke-width": "2.5",
|
|
708
|
+
viewBox: "0 0 24 24",
|
|
709
|
+
width: "18",
|
|
710
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
711
|
+
children: [
|
|
712
|
+
/* @__PURE__ */ jsx2("path", { d: "M18 6 6 18" }),
|
|
713
|
+
/* @__PURE__ */ jsx2("path", { d: "m6 6 12 12" })
|
|
714
|
+
]
|
|
715
|
+
}
|
|
716
|
+
)
|
|
717
|
+
}
|
|
718
|
+
),
|
|
719
|
+
etaData.progress !== void 0 && /* @__PURE__ */ jsx2("div", { className: "absolute bottom-0 left-0 right-0 h-[2px] bg-white/5", children: /* @__PURE__ */ jsx2(
|
|
720
|
+
"div",
|
|
721
|
+
{
|
|
722
|
+
className: "h-full bg-cyan-400",
|
|
723
|
+
style: { width: `${etaData.progress}%` }
|
|
724
|
+
}
|
|
725
|
+
) })
|
|
726
|
+
] });
|
|
727
|
+
}
|
|
728
|
+
if (layoutMode === "open-card") {
|
|
729
|
+
return /* @__PURE__ */ jsxs2("div", { className: "mx-4 mt-2 p-4 bg-white/5 border border-white/10 rounded-xl flex flex-col", children: [
|
|
730
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between mb-2", children: [
|
|
731
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
|
|
732
|
+
/* @__PURE__ */ jsx2("span", { className: "w-2 h-2 rounded-full bg-emerald-500 animate-pulse" }),
|
|
733
|
+
/* @__PURE__ */ jsx2("span", { className: "text-[10px] font-bold text-emerald-400 uppercase tracking-wider", children: etaData.label || "Journey Active" })
|
|
734
|
+
] }),
|
|
735
|
+
etaData.onAbort && /* @__PURE__ */ jsx2(
|
|
736
|
+
"button",
|
|
737
|
+
{
|
|
738
|
+
className: "px-1 px-1 text-xs font-bold bg-red-500/20 text-red-400 hover:bg-red-500/30 border border-red-500/30 active:scale-95 rounded-lg transition-all cursor-pointer pointer-events-auto",
|
|
739
|
+
onClick: etaData.onAbort,
|
|
740
|
+
children: /* @__PURE__ */ jsxs2(
|
|
741
|
+
"svg",
|
|
742
|
+
{
|
|
743
|
+
className: "lucide lucide-x",
|
|
744
|
+
fill: "none",
|
|
745
|
+
height: "18",
|
|
746
|
+
stroke: "currentColor",
|
|
747
|
+
"stroke-linecap": "round",
|
|
748
|
+
"stroke-linejoin": "round",
|
|
749
|
+
"stroke-width": "2.5",
|
|
750
|
+
viewBox: "0 0 24 24",
|
|
751
|
+
width: "18",
|
|
752
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
753
|
+
children: [
|
|
754
|
+
/* @__PURE__ */ jsx2("path", { d: "M18 6 6 18" }),
|
|
755
|
+
/* @__PURE__ */ jsx2("path", { d: "m6 6 12 12" })
|
|
756
|
+
]
|
|
757
|
+
}
|
|
758
|
+
)
|
|
759
|
+
}
|
|
760
|
+
)
|
|
761
|
+
] }),
|
|
762
|
+
/* @__PURE__ */ jsxs2("div", { className: "grid grid-cols-2 divide-x divide-white/10 py-1.5 mb-2.5", children: [
|
|
763
|
+
/* @__PURE__ */ jsxs2("div", { className: "text-center", children: [
|
|
764
|
+
/* @__PURE__ */ jsxs2("div", { className: "text-xl font-black text-white leading-none", children: [
|
|
765
|
+
etaData.etaMins,
|
|
766
|
+
/* @__PURE__ */ jsx2("span", { className: "text-xs text-zinc-400 font-normal", children: " min" })
|
|
767
|
+
] }),
|
|
768
|
+
/* @__PURE__ */ jsx2("div", { className: "text-[8px] text-zinc-500 font-bold uppercase tracking-wider mt-1", children: "Time Remaining" })
|
|
769
|
+
] }),
|
|
770
|
+
/* @__PURE__ */ jsxs2("div", { className: "text-center", children: [
|
|
771
|
+
/* @__PURE__ */ jsxs2("div", { className: "text-xl font-black text-white leading-none", children: [
|
|
772
|
+
etaData.distanceRemainingKm.toFixed(1),
|
|
773
|
+
/* @__PURE__ */ jsx2("span", { className: "text-xs text-zinc-400 font-normal", children: " km" })
|
|
774
|
+
] }),
|
|
775
|
+
/* @__PURE__ */ jsx2("div", { className: "text-[8px] text-zinc-500 font-bold uppercase tracking-wider mt-1", children: "Distance Remaining" })
|
|
776
|
+
] })
|
|
777
|
+
] }),
|
|
778
|
+
etaData.progress !== void 0 && /* @__PURE__ */ jsxs2("div", { className: "mb-3", children: [
|
|
779
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex justify-between text-[9px] font-bold text-zinc-450 mb-1", children: [
|
|
780
|
+
/* @__PURE__ */ jsx2("span", { children: "Progress" }),
|
|
781
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
782
|
+
Math.round(etaData.progress),
|
|
783
|
+
"%"
|
|
784
|
+
] })
|
|
785
|
+
] }),
|
|
786
|
+
/* @__PURE__ */ jsx2("div", { className: "h-1 bg-white/10 rounded-full overflow-hidden", children: /* @__PURE__ */ jsx2(
|
|
787
|
+
"div",
|
|
788
|
+
{
|
|
789
|
+
className: "h-full bg-cyan-400 rounded-full",
|
|
790
|
+
style: { width: `${etaData.progress}%` }
|
|
791
|
+
}
|
|
792
|
+
) })
|
|
793
|
+
] }),
|
|
794
|
+
etaData.mode === "preview" && etaData.onStart && /* @__PURE__ */ jsx2(
|
|
795
|
+
"button",
|
|
796
|
+
{
|
|
797
|
+
className: "w-full py-2.5 text-xs font-bold bg-cyan-500 text-black hover:bg-cyan-400 active:scale-95 rounded-lg transition-all cursor-pointer",
|
|
798
|
+
onClick: etaData.onStart,
|
|
799
|
+
children: "Start Guidance"
|
|
800
|
+
}
|
|
801
|
+
)
|
|
802
|
+
] });
|
|
803
|
+
}
|
|
804
|
+
return /* @__PURE__ */ jsxs2(
|
|
805
|
+
"div",
|
|
806
|
+
{
|
|
807
|
+
className: `absolute left-1/2 -translate-x-1/2 z-40 w-[calc(100%-2rem)] max-w-[320px] sm:max-w-[380px] bg-zinc-950/85 backdrop-blur-md border border-white/10 rounded-2xl shadow-2xl p-3 sm:p-4 flex flex-col pointer-events-auto select-none ${isDragging ? "transition-none" : "transition-all duration-300 ease-out"}`,
|
|
808
|
+
style,
|
|
809
|
+
children: [
|
|
810
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between mb-2 sm:mb-3", children: [
|
|
811
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
|
|
812
|
+
/* @__PURE__ */ jsx2("span", { className: "w-1.5 h-1.5 sm:w-2 sm:h-2 rounded-full bg-emerald-500 animate-pulse" }),
|
|
813
|
+
/* @__PURE__ */ jsx2("span", { className: "text-[9px] sm:text-[10px] font-bold text-emerald-400 uppercase tracking-wider", children: etaData.label || "Guidance Active" })
|
|
814
|
+
] }),
|
|
815
|
+
etaData.onAbort && /* @__PURE__ */ jsx2(
|
|
816
|
+
"button",
|
|
817
|
+
{
|
|
818
|
+
className: "px-1 py-1 text-[10px] sm:text-xs font-bold bg-red-500/20 text-red-400 hover:bg-red-500/30 border border-red-500/30 active:scale-95 rounded-lg transition-all cursor-pointer",
|
|
819
|
+
onClick: etaData.onAbort,
|
|
820
|
+
children: /* @__PURE__ */ jsxs2(
|
|
821
|
+
"svg",
|
|
822
|
+
{
|
|
823
|
+
className: "lucide lucide-x",
|
|
824
|
+
fill: "none",
|
|
825
|
+
height: "18",
|
|
826
|
+
stroke: "currentColor",
|
|
827
|
+
"stroke-linecap": "round",
|
|
828
|
+
"stroke-linejoin": "round",
|
|
829
|
+
"stroke-width": "2.5",
|
|
830
|
+
viewBox: "0 0 24 24",
|
|
831
|
+
width: "18",
|
|
832
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
833
|
+
children: [
|
|
834
|
+
/* @__PURE__ */ jsx2("path", { d: "M18 6 6 18" }),
|
|
835
|
+
/* @__PURE__ */ jsx2("path", { d: "m6 6 12 12" })
|
|
836
|
+
]
|
|
837
|
+
}
|
|
838
|
+
)
|
|
839
|
+
}
|
|
840
|
+
)
|
|
841
|
+
] }),
|
|
842
|
+
/* @__PURE__ */ jsxs2("div", { className: "grid grid-cols-2 divide-x divide-white/10 py-0.5 sm:py-1 mb-2 sm:mb-3", children: [
|
|
843
|
+
/* @__PURE__ */ jsx2("div", { className: "text-center", children: /* @__PURE__ */ jsxs2("div", { className: "text-base sm:text-lg font-black text-white leading-none", children: [
|
|
844
|
+
etaData.etaMins,
|
|
845
|
+
/* @__PURE__ */ jsxs2("span", { className: "text-[10px] sm:text-xs text-zinc-400 font-normal", children: [
|
|
846
|
+
" ",
|
|
847
|
+
"min"
|
|
848
|
+
] })
|
|
849
|
+
] }) }),
|
|
850
|
+
/* @__PURE__ */ jsx2("div", { className: "text-center", children: /* @__PURE__ */ jsxs2("div", { className: "text-base sm:text-lg font-black text-white leading-none", children: [
|
|
851
|
+
etaData.distanceRemainingKm.toFixed(1),
|
|
852
|
+
/* @__PURE__ */ jsxs2("span", { className: "text-[10px] sm:text-xs text-zinc-400 font-normal", children: [
|
|
853
|
+
" ",
|
|
854
|
+
"km"
|
|
855
|
+
] })
|
|
856
|
+
] }) })
|
|
857
|
+
] }),
|
|
858
|
+
etaData.progress !== void 0 && /* @__PURE__ */ jsxs2("div", { children: [
|
|
859
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex justify-between text-[8px] sm:text-[9px] font-bold text-zinc-400 mb-1", children: [
|
|
860
|
+
/* @__PURE__ */ jsx2("span", { children: "Progress" }),
|
|
861
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
862
|
+
Math.round(etaData.progress),
|
|
863
|
+
"%"
|
|
864
|
+
] })
|
|
865
|
+
] }),
|
|
866
|
+
/* @__PURE__ */ jsx2("div", { className: "h-1 sm:h-1.5 bg-white/10 rounded-full overflow-hidden", children: /* @__PURE__ */ jsx2(
|
|
867
|
+
"div",
|
|
868
|
+
{
|
|
869
|
+
className: "h-full bg-cyan-400 rounded-full",
|
|
870
|
+
style: { width: `${etaData.progress}%` }
|
|
871
|
+
}
|
|
872
|
+
) })
|
|
873
|
+
] }),
|
|
874
|
+
etaData.mode === "preview" && etaData.onStart && /* @__PURE__ */ jsx2(
|
|
875
|
+
"button",
|
|
876
|
+
{
|
|
877
|
+
className: "mt-2.5 sm:mt-3 py-1.5 sm:py-2 text-[11px] sm:text-xs font-bold bg-cyan-500 text-black hover:bg-cyan-400 active:scale-95 rounded-lg transition-all cursor-pointer",
|
|
878
|
+
onClick: etaData.onStart,
|
|
879
|
+
children: "Start Guidance"
|
|
880
|
+
}
|
|
881
|
+
)
|
|
882
|
+
]
|
|
883
|
+
}
|
|
884
|
+
);
|
|
885
|
+
};
|
|
886
|
+
|
|
887
|
+
// src/components/Map/components/MapRightControls.tsx
|
|
888
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
889
|
+
var MapRightControls = ({
|
|
890
|
+
customControls = [],
|
|
891
|
+
etaBarHeight,
|
|
892
|
+
onRecenter,
|
|
893
|
+
isMapCentered,
|
|
894
|
+
mapBearing,
|
|
895
|
+
mapPitch,
|
|
896
|
+
onResetBearing,
|
|
897
|
+
initialViewState,
|
|
898
|
+
drawerState,
|
|
899
|
+
side,
|
|
900
|
+
etaData,
|
|
901
|
+
isDragging,
|
|
902
|
+
clampedDragOffset,
|
|
903
|
+
customRecenterButton,
|
|
904
|
+
customCompassButton,
|
|
905
|
+
drawerPeekHeight,
|
|
906
|
+
insets = { bottom: 0, left: 0, right: 0, top: 0 }
|
|
907
|
+
}) => {
|
|
908
|
+
const aboveControls = customControls.filter(
|
|
909
|
+
(c) => c.position === "above-recenter"
|
|
910
|
+
);
|
|
911
|
+
const belowControls = customControls.filter(
|
|
912
|
+
(c) => c.position === "below-recenter"
|
|
913
|
+
);
|
|
914
|
+
const isCompassVisible = Math.abs(mapBearing) >= 0.5 || Math.abs(mapPitch) >= 0.5;
|
|
915
|
+
const closedHeightOffset = "32px";
|
|
916
|
+
const activeEtaHeight = etaData ? etaBarHeight : drawerState === "closed" ? insets.bottom : 0;
|
|
917
|
+
const peekStyle = typeof drawerPeekHeight === "number" ? `${drawerPeekHeight}px` : drawerPeekHeight || "15%";
|
|
918
|
+
const controlsBottom = side === "bottom" ? drawerState === "closed" ? `calc(${closedHeightOffset} + 1rem + ${activeEtaHeight}px - var(--drag-offset, 0px))` : `calc(${peekStyle} + 1rem + ${etaData ? etaBarHeight : 0}px - var(--drag-offset, 0px))` : `calc(1rem + ${insets.bottom}px)`;
|
|
919
|
+
const isControlsVisible = side !== "bottom" || drawerState !== "full";
|
|
920
|
+
return /* @__PURE__ */ jsxs3(
|
|
921
|
+
"div",
|
|
922
|
+
{
|
|
923
|
+
className: `absolute right-4 z-30 flex flex-col items-center gap-3 select-none pointer-events-none ${isDragging ? "transition-none" : "transition-all duration-300 ease-out"} ${isControlsVisible ? "opacity-100 scale-100 pointer-events-auto" : "opacity-0 scale-75 pointer-events-none"}`,
|
|
924
|
+
style: {
|
|
925
|
+
bottom: controlsBottom
|
|
926
|
+
},
|
|
927
|
+
children: [
|
|
928
|
+
aboveControls.length > 0 && /* @__PURE__ */ jsx3("div", { className: "flex flex-col gap-2 pointer-events-auto", children: aboveControls.map((control) => /* @__PURE__ */ jsxs3("div", { className: "relative group flex items-center", children: [
|
|
929
|
+
control.tooltip && /* @__PURE__ */ jsx3("div", { className: "absolute right-14 pointer-events-none opacity-0 scale-95 origin-right group-hover:opacity-100 group-hover:scale-100 transition-all duration-150 bg-zinc-900/95 border border-white/10 text-white text-xs px-2.5 py-1.5 rounded-lg shadow-xl whitespace-nowrap", children: control.tooltip }),
|
|
930
|
+
/* @__PURE__ */ jsx3(
|
|
931
|
+
"button",
|
|
932
|
+
{
|
|
933
|
+
className: `w-12 h-12 rounded-full bg-zinc-950/80 backdrop-blur-md border border-white/10 shadow-lg flex items-center justify-center text-white active:scale-95 transition-all cursor-pointer ${control.activeState ? "ring-2 ring-cyan-400 ring-offset-2 ring-offset-zinc-950" : "hover:bg-zinc-900"}`,
|
|
934
|
+
onClick: control.onPress,
|
|
935
|
+
children: control.icon
|
|
936
|
+
}
|
|
937
|
+
)
|
|
938
|
+
] }, control.id)) }),
|
|
939
|
+
isCompassVisible && (customCompassButton ? /* @__PURE__ */ jsx3("div", { className: "pointer-events-auto", children: customCompassButton }) : /* @__PURE__ */ jsx3("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx3(
|
|
940
|
+
"button",
|
|
941
|
+
{
|
|
942
|
+
className: `w-12 h-12 rounded-full bg-zinc-950/80 backdrop-blur-md border border-white/10 shadow-lg flex items-center justify-center text-white active:scale-95 transition-all duration-300 ${isCompassVisible ? "opacity-100 pointer-events-auto" : "opacity-0 pointer-events-none h-0 w-0 border-none m-0 p-0 overflow-hidden"}`,
|
|
943
|
+
onClick: () => onResetBearing({ pitch: initialViewState?.pitch ?? 0 }),
|
|
944
|
+
style: {
|
|
945
|
+
transform: isCompassVisible ? "scale(1) rotate(0deg)" : "scale(0.7) rotate(0deg)"
|
|
946
|
+
},
|
|
947
|
+
title: "Reset bearing to North",
|
|
948
|
+
children: /* @__PURE__ */ jsxs3(
|
|
949
|
+
"svg",
|
|
950
|
+
{
|
|
951
|
+
className: "w-7 h-7 transition-transform duration-75 ease-out",
|
|
952
|
+
fill: "none",
|
|
953
|
+
style: {
|
|
954
|
+
transform: `rotate(${-mapBearing}deg)`
|
|
955
|
+
},
|
|
956
|
+
viewBox: "0 0 24 24",
|
|
957
|
+
children: [
|
|
958
|
+
/* @__PURE__ */ jsx3(
|
|
959
|
+
"circle",
|
|
960
|
+
{
|
|
961
|
+
cx: "12",
|
|
962
|
+
cy: "12",
|
|
963
|
+
r: "9.5",
|
|
964
|
+
stroke: "white",
|
|
965
|
+
strokeOpacity: "0.2",
|
|
966
|
+
strokeWidth: "1"
|
|
967
|
+
}
|
|
968
|
+
),
|
|
969
|
+
/* @__PURE__ */ jsx3("path", { d: "M12 4.5L15 12H9L12 4.5Z", fill: "#ef4444" }),
|
|
970
|
+
/* @__PURE__ */ jsx3("path", { d: "M12 19.5L9 12H15L12 19.5Z", fill: "#f4f4f5" }),
|
|
971
|
+
/* @__PURE__ */ jsx3("circle", { cx: "12", cy: "12", fill: "#18181b", r: "1.5" })
|
|
972
|
+
]
|
|
973
|
+
}
|
|
974
|
+
)
|
|
975
|
+
}
|
|
976
|
+
) })),
|
|
977
|
+
customRecenterButton ? /* @__PURE__ */ jsx3("div", { className: "pointer-events-auto", children: customRecenterButton }) : /* @__PURE__ */ jsx3("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx3(
|
|
978
|
+
"button",
|
|
979
|
+
{
|
|
980
|
+
className: `w-12 h-12 rounded-full bg-zinc-950/80 backdrop-blur-md border border-white/10 shadow-lg flex items-center justify-center text-white active:scale-95 transition-all cursor-pointer ${isMapCentered ? "ring-2 ring-cyan-400 ring-offset-2 ring-offset-zinc-950" : "hover:bg-zinc-900"}`,
|
|
981
|
+
onClick: () => onRecenter(),
|
|
982
|
+
title: "Recenter Map",
|
|
983
|
+
children: isMapCentered ? (
|
|
984
|
+
/* Filled active lock state */
|
|
985
|
+
/* @__PURE__ */ jsxs3(
|
|
986
|
+
"svg",
|
|
987
|
+
{
|
|
988
|
+
className: "w-6 h-6 text-cyan-400",
|
|
989
|
+
fill: "none",
|
|
990
|
+
stroke: "currentColor",
|
|
991
|
+
strokeWidth: "2.2",
|
|
992
|
+
viewBox: "0 0 24 24",
|
|
993
|
+
children: [
|
|
994
|
+
/* @__PURE__ */ jsx3("circle", { cx: "12", cy: "12", r: "8" }),
|
|
995
|
+
/* @__PURE__ */ jsx3("circle", { cx: "12", cy: "12", fill: "currentColor", r: "3" }),
|
|
996
|
+
/* @__PURE__ */ jsx3("line", { x1: "12", x2: "12", y1: "2", y2: "4" }),
|
|
997
|
+
/* @__PURE__ */ jsx3("line", { x1: "12", x2: "12", y1: "20", y2: "22" }),
|
|
998
|
+
/* @__PURE__ */ jsx3("line", { x1: "2", x2: "4", y1: "12", y2: "12" }),
|
|
999
|
+
/* @__PURE__ */ jsx3("line", { x1: "20", x2: "22", y1: "12", y2: "12" })
|
|
1000
|
+
]
|
|
1001
|
+
}
|
|
1002
|
+
)
|
|
1003
|
+
) : (
|
|
1004
|
+
/* Outlined drifted state */
|
|
1005
|
+
/* @__PURE__ */ jsxs3(
|
|
1006
|
+
"svg",
|
|
1007
|
+
{
|
|
1008
|
+
className: "w-6 h-6 text-zinc-300",
|
|
1009
|
+
fill: "none",
|
|
1010
|
+
stroke: "currentColor",
|
|
1011
|
+
strokeWidth: "2",
|
|
1012
|
+
viewBox: "0 0 24 24",
|
|
1013
|
+
children: [
|
|
1014
|
+
/* @__PURE__ */ jsx3("circle", { cx: "12", cy: "12", r: "8" }),
|
|
1015
|
+
/* @__PURE__ */ jsx3("line", { x1: "12", x2: "12", y1: "2", y2: "4" }),
|
|
1016
|
+
/* @__PURE__ */ jsx3("line", { x1: "12", x2: "12", y1: "20", y2: "22" }),
|
|
1017
|
+
/* @__PURE__ */ jsx3("line", { x1: "2", x2: "4", y1: "12", y2: "12" }),
|
|
1018
|
+
/* @__PURE__ */ jsx3("line", { x1: "20", x2: "22", y1: "12", y2: "12" })
|
|
1019
|
+
]
|
|
1020
|
+
}
|
|
1021
|
+
)
|
|
1022
|
+
)
|
|
1023
|
+
}
|
|
1024
|
+
) }),
|
|
1025
|
+
belowControls.length > 0 && /* @__PURE__ */ jsx3("div", { className: "flex flex-col gap-2 pointer-events-auto", children: belowControls.map((control) => /* @__PURE__ */ jsxs3("div", { className: "relative group flex items-center", children: [
|
|
1026
|
+
control.tooltip && /* @__PURE__ */ jsx3("div", { className: "absolute right-14 pointer-events-none opacity-0 scale-95 origin-right group-hover:opacity-100 group-hover:scale-100 transition-all duration-150 bg-zinc-900/95 border border-white/10 text-white text-xs px-2.5 py-1.5 rounded-lg shadow-xl whitespace-nowrap", children: control.tooltip }),
|
|
1027
|
+
/* @__PURE__ */ jsx3(
|
|
1028
|
+
"button",
|
|
1029
|
+
{
|
|
1030
|
+
className: `w-12 h-12 rounded-full bg-zinc-950/80 backdrop-blur-md border border-white/10 shadow-lg flex items-center justify-center text-white active:scale-95 transition-all cursor-pointer ${control.activeState ? "ring-2 ring-cyan-400 ring-offset-2 ring-offset-zinc-950" : "hover:bg-zinc-900"}`,
|
|
1031
|
+
onClick: control.onPress,
|
|
1032
|
+
children: control.icon
|
|
1033
|
+
}
|
|
1034
|
+
)
|
|
1035
|
+
] }, control.id)) })
|
|
1036
|
+
]
|
|
1037
|
+
}
|
|
1038
|
+
);
|
|
1039
|
+
};
|
|
1040
|
+
|
|
1041
|
+
// src/components/Map/MapContext.tsx
|
|
1042
|
+
import {
|
|
1043
|
+
createContext,
|
|
1044
|
+
useCallback,
|
|
1045
|
+
useContext,
|
|
1046
|
+
useMemo,
|
|
1047
|
+
useRef as useRef2,
|
|
1048
|
+
useState as useState2
|
|
1049
|
+
} from "react";
|
|
1050
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
1051
|
+
var MapControlsContext = createContext(
|
|
1052
|
+
null
|
|
1053
|
+
);
|
|
1054
|
+
var SDKMapProvider = ({
|
|
1055
|
+
children
|
|
1056
|
+
}) => {
|
|
1057
|
+
const [isFollowing, setIsFollowing] = useState2(true);
|
|
1058
|
+
const [bearing, setBearingState] = useState2(0);
|
|
1059
|
+
const [pitch, setPitchState] = useState2(0);
|
|
1060
|
+
const [drawerState, setDrawerStateInternal] = useState2("closed");
|
|
1061
|
+
const drawerContentRef = useRef2(null);
|
|
1062
|
+
const setDrawerState = useCallback((val) => {
|
|
1063
|
+
setDrawerStateInternal(val);
|
|
1064
|
+
}, []);
|
|
1065
|
+
const [hasControls, setHasControlsState] = useState2(false);
|
|
1066
|
+
const setHasControls = useCallback((val) => {
|
|
1067
|
+
setHasControlsState((prev) => prev === val ? prev : val);
|
|
1068
|
+
}, []);
|
|
1069
|
+
const [etaData, setEtaDataState] = useState2(void 0);
|
|
1070
|
+
const setEtaData = useCallback((newData) => {
|
|
1071
|
+
setEtaDataState((prev) => {
|
|
1072
|
+
if (!prev && !newData) return prev;
|
|
1073
|
+
if (!prev || !newData) return newData;
|
|
1074
|
+
if (prev.etaMins === newData.etaMins && prev.distanceRemainingKm === newData.distanceRemainingKm && prev.label === newData.label && prev.progress === newData.progress && prev.mode === newData.mode && prev.isCalculating === newData.isCalculating && prev.error === newData.error) {
|
|
1075
|
+
return prev;
|
|
1076
|
+
}
|
|
1077
|
+
return newData;
|
|
1078
|
+
});
|
|
1079
|
+
}, []);
|
|
1080
|
+
const [insets, setInsetsState] = useState2({
|
|
1081
|
+
bottom: 0,
|
|
1082
|
+
left: 0,
|
|
1083
|
+
right: 0,
|
|
1084
|
+
top: 0
|
|
1085
|
+
});
|
|
1086
|
+
const setInsets = useCallback((newInsets) => {
|
|
1087
|
+
setInsetsState((prev) => ({ ...prev, ...newInsets }));
|
|
1088
|
+
}, []);
|
|
1089
|
+
const centerCallbackRef = useRef2(null);
|
|
1090
|
+
const resetBearingCallbackRef = useRef2(null);
|
|
1091
|
+
const centerOnUser = useCallback((params) => {
|
|
1092
|
+
if (centerCallbackRef.current) {
|
|
1093
|
+
centerCallbackRef.current(params);
|
|
1094
|
+
}
|
|
1095
|
+
}, []);
|
|
1096
|
+
const resetBearing = useCallback((params) => {
|
|
1097
|
+
if (resetBearingCallbackRef.current) {
|
|
1098
|
+
resetBearingCallbackRef.current(params);
|
|
1099
|
+
}
|
|
1100
|
+
}, []);
|
|
1101
|
+
const _registerCenterCallback = useCallback(
|
|
1102
|
+
(cb) => {
|
|
1103
|
+
centerCallbackRef.current = cb;
|
|
1104
|
+
},
|
|
1105
|
+
[]
|
|
1106
|
+
);
|
|
1107
|
+
const _registerResetBearingCallback = useCallback(
|
|
1108
|
+
(cb) => {
|
|
1109
|
+
resetBearingCallbackRef.current = cb;
|
|
1110
|
+
},
|
|
1111
|
+
[]
|
|
1112
|
+
);
|
|
1113
|
+
const setBearing = useCallback((b) => {
|
|
1114
|
+
setBearingState(b);
|
|
1115
|
+
}, []);
|
|
1116
|
+
const setPitch = useCallback((p) => {
|
|
1117
|
+
setPitchState(p);
|
|
1118
|
+
}, []);
|
|
1119
|
+
const contextValue = useMemo(
|
|
1120
|
+
() => ({
|
|
1121
|
+
_registerCenterCallback,
|
|
1122
|
+
_registerResetBearingCallback,
|
|
1123
|
+
bearing,
|
|
1124
|
+
centerOnUser,
|
|
1125
|
+
drawerContentRef,
|
|
1126
|
+
drawerState,
|
|
1127
|
+
etaData,
|
|
1128
|
+
hasControls,
|
|
1129
|
+
insets,
|
|
1130
|
+
isFollowing,
|
|
1131
|
+
pitch,
|
|
1132
|
+
resetBearing,
|
|
1133
|
+
setBearing,
|
|
1134
|
+
setDrawerState,
|
|
1135
|
+
setEtaData,
|
|
1136
|
+
setHasControls,
|
|
1137
|
+
setInsets,
|
|
1138
|
+
setIsFollowing,
|
|
1139
|
+
setPitch
|
|
1140
|
+
}),
|
|
1141
|
+
[
|
|
1142
|
+
_registerCenterCallback,
|
|
1143
|
+
_registerResetBearingCallback,
|
|
1144
|
+
bearing,
|
|
1145
|
+
centerOnUser,
|
|
1146
|
+
drawerState,
|
|
1147
|
+
etaData,
|
|
1148
|
+
hasControls,
|
|
1149
|
+
isFollowing,
|
|
1150
|
+
pitch,
|
|
1151
|
+
resetBearing,
|
|
1152
|
+
setBearing,
|
|
1153
|
+
setDrawerState,
|
|
1154
|
+
setEtaData,
|
|
1155
|
+
setHasControls,
|
|
1156
|
+
setPitch,
|
|
1157
|
+
insets,
|
|
1158
|
+
setInsets
|
|
1159
|
+
]
|
|
1160
|
+
);
|
|
1161
|
+
return /* @__PURE__ */ jsx4(MapControlsContext.Provider, { value: contextValue, children });
|
|
1162
|
+
};
|
|
1163
|
+
var useMapControls = () => {
|
|
1164
|
+
const context = useContext(MapControlsContext);
|
|
1165
|
+
if (!context) {
|
|
1166
|
+
throw new Error("useMapControls must be used within a MapControlsProvider");
|
|
1167
|
+
}
|
|
1168
|
+
return context;
|
|
1169
|
+
};
|
|
1170
|
+
|
|
1171
|
+
// src/components/Map/components/MapControls.tsx
|
|
1172
|
+
import { useCallback as useCallback2, useContext as useContext2, useEffect as useEffect2, useRef as useRef3, useState as useState3 } from "react";
|
|
1173
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1174
|
+
var resolveHeightToPixels2 = (height, containerHeight) => {
|
|
1175
|
+
if (typeof height === "number") {
|
|
1176
|
+
return height;
|
|
1177
|
+
}
|
|
1178
|
+
if (height.endsWith("%")) {
|
|
1179
|
+
return parseFloat(height) / 100 * containerHeight;
|
|
1180
|
+
}
|
|
1181
|
+
return parseFloat(height);
|
|
1182
|
+
};
|
|
1183
|
+
var MapControls = ({
|
|
1184
|
+
menuItems = [],
|
|
1185
|
+
drawerSide,
|
|
1186
|
+
customControls = [],
|
|
1187
|
+
etaBarHeight = 100,
|
|
1188
|
+
onRecenter: propRecenter,
|
|
1189
|
+
isMapCentered: propMapCentered,
|
|
1190
|
+
mapBearing: propMapBearing,
|
|
1191
|
+
onResetBearing: propResetBearing,
|
|
1192
|
+
etaData,
|
|
1193
|
+
customRecenterButton,
|
|
1194
|
+
defaultState,
|
|
1195
|
+
initialViewState,
|
|
1196
|
+
customCompassButton,
|
|
1197
|
+
drawerPeekHeight,
|
|
1198
|
+
drawerFullHeight,
|
|
1199
|
+
drawerContentRef,
|
|
1200
|
+
menuContent,
|
|
1201
|
+
children,
|
|
1202
|
+
enableTouchDrag = false,
|
|
1203
|
+
showScrollToTopButton = true,
|
|
1204
|
+
scrollToTopThreshold = 300,
|
|
1205
|
+
isInternal = false
|
|
1206
|
+
}) => {
|
|
1207
|
+
const mapContext = useContext2(MapControlsContext);
|
|
1208
|
+
const contextRef = mapContext?.drawerContentRef;
|
|
1209
|
+
const mergedRef = useCallback2(
|
|
1210
|
+
(node) => {
|
|
1211
|
+
if (drawerContentRef) {
|
|
1212
|
+
if (typeof drawerContentRef === "function") {
|
|
1213
|
+
drawerContentRef(node);
|
|
1214
|
+
} else {
|
|
1215
|
+
drawerContentRef.current = node;
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
if (contextRef) {
|
|
1219
|
+
contextRef.current = node;
|
|
1220
|
+
}
|
|
1221
|
+
},
|
|
1222
|
+
[drawerContentRef, contextRef]
|
|
1223
|
+
);
|
|
1224
|
+
const [localDrawerState, setLocalDrawerState] = useState3(
|
|
1225
|
+
defaultState ?? "closed"
|
|
1226
|
+
);
|
|
1227
|
+
const drawerState = mapContext ? mapContext.drawerState : localDrawerState;
|
|
1228
|
+
const setDrawerState = useCallback2(
|
|
1229
|
+
(state) => {
|
|
1230
|
+
if (mapContext) {
|
|
1231
|
+
mapContext.setDrawerState(state);
|
|
1232
|
+
} else {
|
|
1233
|
+
setLocalDrawerState(state);
|
|
1234
|
+
}
|
|
1235
|
+
},
|
|
1236
|
+
[mapContext]
|
|
1237
|
+
);
|
|
1238
|
+
useEffect2(() => {
|
|
1239
|
+
if (defaultState && mapContext) {
|
|
1240
|
+
mapContext.setDrawerState(defaultState);
|
|
1241
|
+
}
|
|
1242
|
+
}, []);
|
|
1243
|
+
const [side, setSide] = useState3("left");
|
|
1244
|
+
const containerRef = useRef3(null);
|
|
1245
|
+
const [mapHeight, setMapHeight] = useState3(600);
|
|
1246
|
+
useEffect2(() => {
|
|
1247
|
+
const parent = containerRef.current?.parentElement;
|
|
1248
|
+
if (!parent) return;
|
|
1249
|
+
const updateHeight = () => {
|
|
1250
|
+
setMapHeight(parent.getBoundingClientRect().height);
|
|
1251
|
+
};
|
|
1252
|
+
updateHeight();
|
|
1253
|
+
const observer = new ResizeObserver(updateHeight);
|
|
1254
|
+
observer.observe(parent);
|
|
1255
|
+
return () => {
|
|
1256
|
+
observer.disconnect();
|
|
1257
|
+
};
|
|
1258
|
+
}, []);
|
|
1259
|
+
const setHasControls = mapContext?.setHasControls;
|
|
1260
|
+
useEffect2(() => {
|
|
1261
|
+
if (setHasControls && !isInternal) {
|
|
1262
|
+
setHasControls(true);
|
|
1263
|
+
return () => setHasControls(false);
|
|
1264
|
+
}
|
|
1265
|
+
}, [setHasControls, isInternal]);
|
|
1266
|
+
const isMapCentered = propMapCentered !== void 0 ? propMapCentered : mapContext ? mapContext.isFollowing : true;
|
|
1267
|
+
const mapBearing = propMapBearing !== void 0 ? propMapBearing : mapContext ? mapContext.bearing : 0;
|
|
1268
|
+
const onRecenter = (params) => {
|
|
1269
|
+
if (propRecenter) {
|
|
1270
|
+
propRecenter(params);
|
|
1271
|
+
} else if (mapContext) {
|
|
1272
|
+
mapContext.centerOnUser(params);
|
|
1273
|
+
}
|
|
1274
|
+
};
|
|
1275
|
+
const onResetBearing = (params) => {
|
|
1276
|
+
if (propResetBearing) {
|
|
1277
|
+
propResetBearing(params);
|
|
1278
|
+
} else if (mapContext) {
|
|
1279
|
+
mapContext.resetBearing(params);
|
|
1280
|
+
}
|
|
1281
|
+
};
|
|
1282
|
+
const mapPitch = mapContext ? mapContext.pitch : 0;
|
|
1283
|
+
const resolvedEtaData = etaData !== void 0 ? etaData : mapContext?.etaData;
|
|
1284
|
+
const [touchStartY, setTouchStartY] = useState3(null);
|
|
1285
|
+
const [currentTouchY, setCurrentTouchY] = useState3(null);
|
|
1286
|
+
useEffect2(() => {
|
|
1287
|
+
if (typeof window === "undefined") return;
|
|
1288
|
+
const checkLayout = () => {
|
|
1289
|
+
if (drawerSide) {
|
|
1290
|
+
setSide(drawerSide);
|
|
1291
|
+
} else {
|
|
1292
|
+
if (window.innerWidth < 768) {
|
|
1293
|
+
setSide("bottom");
|
|
1294
|
+
} else {
|
|
1295
|
+
setSide("left");
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
};
|
|
1299
|
+
checkLayout();
|
|
1300
|
+
window.addEventListener("resize", checkLayout);
|
|
1301
|
+
return () => window.removeEventListener("resize", checkLayout);
|
|
1302
|
+
}, [drawerSide]);
|
|
1303
|
+
const isDragging = touchStartY !== null && currentTouchY !== null;
|
|
1304
|
+
const dragOffset = isDragging ? currentTouchY - touchStartY : 0;
|
|
1305
|
+
const fullPx = resolveHeightToPixels2(drawerFullHeight || "85%", mapHeight);
|
|
1306
|
+
const peekPx = resolveHeightToPixels2(drawerPeekHeight || "15%", mapHeight);
|
|
1307
|
+
const bottomInset = mapContext?.insets.bottom || 0;
|
|
1308
|
+
const closedPx = 32 + (drawerState === "closed" ? bottomInset : 0);
|
|
1309
|
+
let clampedDragOffset = dragOffset;
|
|
1310
|
+
if (drawerState === "full") {
|
|
1311
|
+
clampedDragOffset = Math.max(0, Math.min(fullPx - closedPx, dragOffset));
|
|
1312
|
+
} else if (drawerState === "peek") {
|
|
1313
|
+
clampedDragOffset = Math.max(
|
|
1314
|
+
-(fullPx - peekPx),
|
|
1315
|
+
// up to full
|
|
1316
|
+
Math.min(peekPx - closedPx, dragOffset)
|
|
1317
|
+
// down to closed
|
|
1318
|
+
);
|
|
1319
|
+
} else if (drawerState === "closed") {
|
|
1320
|
+
clampedDragOffset = Math.min(0, Math.max(-(fullPx - closedPx), dragOffset));
|
|
1321
|
+
}
|
|
1322
|
+
let baseHeight = 32;
|
|
1323
|
+
if (drawerState === "peek") {
|
|
1324
|
+
baseHeight = peekPx;
|
|
1325
|
+
} else if (drawerState === "full") {
|
|
1326
|
+
baseHeight = fullPx;
|
|
1327
|
+
}
|
|
1328
|
+
const currentDrawerHeight = baseHeight - clampedDragOffset;
|
|
1329
|
+
const startFadeHeight = peekPx + 20;
|
|
1330
|
+
const endFadeHeight = peekPx + (fullPx - peekPx) * 0.35;
|
|
1331
|
+
let etaOpacity = 1;
|
|
1332
|
+
let etaBottomStyle = "1rem";
|
|
1333
|
+
if (side === "bottom") {
|
|
1334
|
+
if (currentDrawerHeight <= startFadeHeight) {
|
|
1335
|
+
etaOpacity = 1;
|
|
1336
|
+
} else if (currentDrawerHeight >= endFadeHeight) {
|
|
1337
|
+
etaOpacity = 0;
|
|
1338
|
+
} else {
|
|
1339
|
+
const fraction = (currentDrawerHeight - startFadeHeight) / (endFadeHeight - startFadeHeight);
|
|
1340
|
+
etaOpacity = 1 - fraction;
|
|
1341
|
+
}
|
|
1342
|
+
const effectiveInset = drawerState === "closed" ? bottomInset : 0;
|
|
1343
|
+
etaBottomStyle = `calc(${baseHeight + effectiveInset + 16}px - var(--drag-offset, 0px))`;
|
|
1344
|
+
} else {
|
|
1345
|
+
etaOpacity = drawerState === "full" ? 0 : 1;
|
|
1346
|
+
etaBottomStyle = `calc(1rem + ${bottomInset}px)`;
|
|
1347
|
+
}
|
|
1348
|
+
return /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1349
|
+
/* @__PURE__ */ jsx5("div", { ref: containerRef, style: { display: "none" } }),
|
|
1350
|
+
/* @__PURE__ */ jsx5(
|
|
1351
|
+
MapDrawer,
|
|
1352
|
+
{
|
|
1353
|
+
clampedDragOffset,
|
|
1354
|
+
containerHeight: mapHeight,
|
|
1355
|
+
currentTouchY,
|
|
1356
|
+
drawerFullHeight,
|
|
1357
|
+
drawerPeekHeight,
|
|
1358
|
+
enableTouchDrag,
|
|
1359
|
+
etaBarHeight,
|
|
1360
|
+
etaData: resolvedEtaData,
|
|
1361
|
+
isDragging,
|
|
1362
|
+
menuContent: children || menuContent,
|
|
1363
|
+
menuItems,
|
|
1364
|
+
onChangeState: setDrawerState,
|
|
1365
|
+
ref: mergedRef,
|
|
1366
|
+
safeAreaInsets: mapContext?.insets,
|
|
1367
|
+
scrollToTopThreshold,
|
|
1368
|
+
setCurrentTouchY,
|
|
1369
|
+
setTouchStartY,
|
|
1370
|
+
showScrollToTopButton,
|
|
1371
|
+
side,
|
|
1372
|
+
state: drawerState,
|
|
1373
|
+
touchStartY
|
|
1374
|
+
}
|
|
1375
|
+
),
|
|
1376
|
+
/* @__PURE__ */ jsx5(
|
|
1377
|
+
MapRightControls,
|
|
1378
|
+
{
|
|
1379
|
+
clampedDragOffset,
|
|
1380
|
+
customCompassButton,
|
|
1381
|
+
customControls,
|
|
1382
|
+
customRecenterButton,
|
|
1383
|
+
drawerPeekHeight,
|
|
1384
|
+
drawerState,
|
|
1385
|
+
etaBarHeight: etaBarHeight + (drawerState === "closed" ? bottomInset : 0),
|
|
1386
|
+
etaData: resolvedEtaData,
|
|
1387
|
+
initialViewState,
|
|
1388
|
+
insets: mapContext?.insets,
|
|
1389
|
+
isDragging,
|
|
1390
|
+
isMapCentered,
|
|
1391
|
+
mapBearing,
|
|
1392
|
+
mapPitch,
|
|
1393
|
+
onRecenter: () => onRecenter(),
|
|
1394
|
+
onResetBearing,
|
|
1395
|
+
side
|
|
1396
|
+
}
|
|
1397
|
+
),
|
|
1398
|
+
resolvedEtaData && /* @__PURE__ */ jsx5(
|
|
1399
|
+
MapEtaPanel,
|
|
1400
|
+
{
|
|
1401
|
+
etaData: resolvedEtaData,
|
|
1402
|
+
isDragging,
|
|
1403
|
+
layoutMode: "floating-card",
|
|
1404
|
+
style: {
|
|
1405
|
+
bottom: etaBottomStyle,
|
|
1406
|
+
opacity: etaOpacity,
|
|
1407
|
+
pointerEvents: etaOpacity > 0.1 ? "auto" : "none",
|
|
1408
|
+
transition: isDragging ? "none" : "bottom 250ms ease-in-out, opacity 300ms ease-out"
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
)
|
|
1412
|
+
] });
|
|
1413
|
+
};
|
|
1414
|
+
|
|
1415
|
+
// src/components/Map/components/MapMenu.tsx
|
|
1416
|
+
import { useContext as useContext3 } from "react";
|
|
1417
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1418
|
+
var MapMenu = ({ children }) => {
|
|
1419
|
+
return /* @__PURE__ */ jsx6("div", { className: "flex flex-col gap-1.5", children });
|
|
1420
|
+
};
|
|
1421
|
+
var MapMenuItem = ({
|
|
1422
|
+
id,
|
|
1423
|
+
label,
|
|
1424
|
+
icon,
|
|
1425
|
+
onPress,
|
|
1426
|
+
badge,
|
|
1427
|
+
disabled = false,
|
|
1428
|
+
children
|
|
1429
|
+
}) => {
|
|
1430
|
+
const context = useContext3(MapControlsContext);
|
|
1431
|
+
const handleClick = (e) => {
|
|
1432
|
+
if (disabled) return;
|
|
1433
|
+
if (onPress) onPress();
|
|
1434
|
+
if (context && context.setDrawerState) {
|
|
1435
|
+
context.setDrawerState("closed");
|
|
1436
|
+
}
|
|
1437
|
+
};
|
|
1438
|
+
return /* @__PURE__ */ jsxs5(
|
|
1439
|
+
"button",
|
|
1440
|
+
{
|
|
1441
|
+
className: `flex items-center gap-3 w-full py-3 px-3.5 rounded-xl text-left cursor-pointer transition-all duration-150 ${disabled ? "opacity-45 cursor-not-allowed pointer-events-none" : "hover:bg-white/5 active:bg-white/10 text-white"}`,
|
|
1442
|
+
disabled,
|
|
1443
|
+
onClick: handleClick,
|
|
1444
|
+
type: "button",
|
|
1445
|
+
children: [
|
|
1446
|
+
icon && /* @__PURE__ */ jsxs5("div", { className: "relative flex items-center justify-center w-8 h-8 rounded-lg bg-white/5 text-zinc-300", children: [
|
|
1447
|
+
icon,
|
|
1448
|
+
badge !== void 0 && /* @__PURE__ */ jsx6("span", { className: "absolute -top-1 -right-1 bg-cyan-500 text-black text-[9px] font-extrabold min-w-[15px] h-3.5 rounded-full flex items-center justify-center px-1 border border-zinc-950 shadow", children: badge })
|
|
1449
|
+
] }),
|
|
1450
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex-1 flex flex-col justify-center", children: [
|
|
1451
|
+
/* @__PURE__ */ jsx6("span", { className: "text-sm font-medium tracking-wide flex-1", children: label }),
|
|
1452
|
+
children
|
|
1453
|
+
] })
|
|
1454
|
+
]
|
|
1455
|
+
}
|
|
1456
|
+
);
|
|
1457
|
+
};
|
|
1458
|
+
MapMenu.displayName = "MapMenu";
|
|
1459
|
+
MapMenuItem.displayName = "MapMenuItem";
|
|
1460
|
+
var MapMenuTitle = ({
|
|
1461
|
+
children,
|
|
1462
|
+
showDivider = true,
|
|
1463
|
+
className = ""
|
|
1464
|
+
}) => {
|
|
1465
|
+
return /* @__PURE__ */ jsx6(
|
|
1466
|
+
"div",
|
|
1467
|
+
{
|
|
1468
|
+
className: `flex items-center justify-between pb-3 first:mt-0 px-3.5 ${className} ${showDivider ? "border-b border-white/10 mb-2 mt-4" : ""}`,
|
|
1469
|
+
children
|
|
1470
|
+
}
|
|
1471
|
+
);
|
|
1472
|
+
};
|
|
1473
|
+
MapMenuTitle.displayName = "MapMenuTitle";
|
|
1474
|
+
var MapMenuDivider = ({
|
|
1475
|
+
className = ""
|
|
1476
|
+
}) => {
|
|
1477
|
+
return /* @__PURE__ */ jsx6("div", { className: `h-[1px] bg-white/10 my-3 mx-3.5 ${className}` });
|
|
1478
|
+
};
|
|
1479
|
+
MapMenuDivider.displayName = "MapMenuDivider";
|
|
1480
|
+
var MapMenuFooter = ({
|
|
1481
|
+
children,
|
|
1482
|
+
className = ""
|
|
1483
|
+
}) => {
|
|
1484
|
+
return /* @__PURE__ */ jsx6(
|
|
1485
|
+
"div",
|
|
1486
|
+
{
|
|
1487
|
+
className: `mt-auto pt-4 border-t border-white/10 px-3.5 text-xs text-zinc-500 font-medium ${className}`,
|
|
1488
|
+
children
|
|
1489
|
+
}
|
|
1490
|
+
);
|
|
1491
|
+
};
|
|
1492
|
+
MapMenuFooter.displayName = "MapMenuFooter";
|
|
1493
|
+
|
|
1494
|
+
// src/types/types.ts
|
|
1495
|
+
var UIEvent = /* @__PURE__ */ ((UIEvent2) => {
|
|
1496
|
+
UIEvent2["TOP_SCROLL_MORE"] = "TOP_SCROLL_MORE";
|
|
1497
|
+
UIEvent2["BOTTOM_SCROLL_MORE"] = "BOTTOM_SCROLL_MORE";
|
|
1498
|
+
UIEvent2["BACK_GESTURE"] = "BACK_GESTURE";
|
|
1499
|
+
return UIEvent2;
|
|
1500
|
+
})(UIEvent || {});
|
|
1501
|
+
var GeolocationEvent = /* @__PURE__ */ ((GeolocationEvent2) => {
|
|
1502
|
+
GeolocationEvent2["LOCATION_UPDATE"] = "LOCATION_UPDATE";
|
|
1503
|
+
GeolocationEvent2["LOCATION_STREAM_UPDATE"] = "LOCATION_STREAM_UPDATE";
|
|
1504
|
+
return GeolocationEvent2;
|
|
1505
|
+
})(GeolocationEvent || {});
|
|
1506
|
+
var GlobalEvent = /* @__PURE__ */ ((GlobalEvent2) => {
|
|
1507
|
+
GlobalEvent2["UPDATE_CONTEXT"] = "UPDATE_CONTEXT";
|
|
1508
|
+
return GlobalEvent2;
|
|
1509
|
+
})(GlobalEvent || {});
|
|
1510
|
+
|
|
1511
|
+
// src/components/Map/hooks/useUserLocation.ts
|
|
1512
|
+
import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef4, useState as useState4 } from "react";
|
|
1513
|
+
var LERP_FACTOR = 0.12;
|
|
1514
|
+
var useUserLocation = () => {
|
|
1515
|
+
const [locationState, setLocationState] = useState4({
|
|
1516
|
+
accuracy: null,
|
|
1517
|
+
coords: null,
|
|
1518
|
+
heading: null
|
|
1519
|
+
});
|
|
1520
|
+
const targetRef = useRef4({
|
|
1521
|
+
accuracy: null,
|
|
1522
|
+
coords: null,
|
|
1523
|
+
heading: null
|
|
1524
|
+
});
|
|
1525
|
+
const currentRef = useRef4({
|
|
1526
|
+
accuracy: null,
|
|
1527
|
+
coords: null,
|
|
1528
|
+
heading: null
|
|
1529
|
+
});
|
|
1530
|
+
const mapPayload = useCallback3((payload) => {
|
|
1531
|
+
const coords = payload.location?.coords || payload.coords;
|
|
1532
|
+
const headingData = payload.heading || payload.coords;
|
|
1533
|
+
const newCoords = coords ? [coords.longitude, coords.latitude] : null;
|
|
1534
|
+
let heading = null;
|
|
1535
|
+
if (headingData?.trueHeading !== void 0 && headingData.trueHeading >= 0) {
|
|
1536
|
+
heading = headingData.trueHeading;
|
|
1537
|
+
} else if (headingData?.magneticHeading !== void 0 && headingData.magneticHeading >= 0) {
|
|
1538
|
+
heading = headingData.magneticHeading;
|
|
1539
|
+
} else if (headingData?.heading !== null && headingData.heading >= 0) {
|
|
1540
|
+
heading = headingData.heading;
|
|
1541
|
+
}
|
|
1542
|
+
return {
|
|
1543
|
+
accuracy: coords?.accuracy || null,
|
|
1544
|
+
coords: newCoords,
|
|
1545
|
+
heading
|
|
1546
|
+
};
|
|
1547
|
+
}, []);
|
|
1548
|
+
useEffect3(() => {
|
|
1549
|
+
let rafId;
|
|
1550
|
+
const animate = () => {
|
|
1551
|
+
let changed = false;
|
|
1552
|
+
if (targetRef.current.coords) {
|
|
1553
|
+
if (!currentRef.current.coords) {
|
|
1554
|
+
currentRef.current.coords = [...targetRef.current.coords];
|
|
1555
|
+
changed = true;
|
|
1556
|
+
} else {
|
|
1557
|
+
const [tLng, tLat] = targetRef.current.coords;
|
|
1558
|
+
const [cLng, cLat] = currentRef.current.coords;
|
|
1559
|
+
const nLng = cLng + (tLng - cLng) * LERP_FACTOR;
|
|
1560
|
+
const nLat = cLat + (tLat - cLat) * LERP_FACTOR;
|
|
1561
|
+
if (Math.abs(nLng - cLng) > 1e-7 || Math.abs(nLat - cLat) > 1e-7) {
|
|
1562
|
+
currentRef.current.coords = [nLng, nLat];
|
|
1563
|
+
changed = true;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
if (targetRef.current.heading !== null) {
|
|
1568
|
+
if (currentRef.current.heading === null) {
|
|
1569
|
+
currentRef.current.heading = targetRef.current.heading;
|
|
1570
|
+
changed = true;
|
|
1571
|
+
} else {
|
|
1572
|
+
const target = targetRef.current.heading;
|
|
1573
|
+
const current = currentRef.current.heading;
|
|
1574
|
+
let delta = target - current;
|
|
1575
|
+
if (delta > 180) delta -= 360;
|
|
1576
|
+
if (delta < -180) delta += 360;
|
|
1577
|
+
const nHeading = current + delta * LERP_FACTOR;
|
|
1578
|
+
const normalized = (nHeading % 360 + 360) % 360;
|
|
1579
|
+
if (Math.abs(delta) > 0.1) {
|
|
1580
|
+
currentRef.current.heading = normalized;
|
|
1581
|
+
changed = true;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
if (changed) {
|
|
1586
|
+
setLocationState({
|
|
1587
|
+
accuracy: targetRef.current.accuracy,
|
|
1588
|
+
coords: currentRef.current.coords ? [...currentRef.current.coords] : null,
|
|
1589
|
+
heading: currentRef.current.heading
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
rafId = requestAnimationFrame(animate);
|
|
1593
|
+
};
|
|
1594
|
+
rafId = requestAnimationFrame(animate);
|
|
1595
|
+
sdk.location.getCurrentPosition().then((data) => {
|
|
1596
|
+
const payload = mapPayload(data);
|
|
1597
|
+
targetRef.current = payload;
|
|
1598
|
+
}).catch((e) => console.warn("Initial location fetch failed", e));
|
|
1599
|
+
const unsubscribeStream = sdk.on(
|
|
1600
|
+
"LOCATION_STREAM_UPDATE" /* LOCATION_STREAM_UPDATE */,
|
|
1601
|
+
(payload) => {
|
|
1602
|
+
targetRef.current = mapPayload(payload);
|
|
1603
|
+
}
|
|
1604
|
+
);
|
|
1605
|
+
const unsubscribeLegacy = sdk.on(
|
|
1606
|
+
"LOCATION_UPDATE" /* LOCATION_UPDATE */,
|
|
1607
|
+
(payload) => {
|
|
1608
|
+
targetRef.current = mapPayload(payload);
|
|
1609
|
+
}
|
|
1610
|
+
);
|
|
1611
|
+
sdk.location.startStream({
|
|
1612
|
+
distanceFilter: 1,
|
|
1613
|
+
headingThreshold: 2
|
|
1614
|
+
}).catch((e) => console.warn("Failed to start location stream", e));
|
|
1615
|
+
return () => {
|
|
1616
|
+
cancelAnimationFrame(rafId);
|
|
1617
|
+
unsubscribeStream();
|
|
1618
|
+
unsubscribeLegacy();
|
|
1619
|
+
sdk.location.stopStream().catch(() => {
|
|
1620
|
+
});
|
|
1621
|
+
};
|
|
1622
|
+
}, [mapPayload]);
|
|
1623
|
+
return locationState;
|
|
1624
|
+
};
|
|
1625
|
+
|
|
1626
|
+
// src/components/Map/hooks/useLiveRouting.ts
|
|
1627
|
+
import { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef5, useState as useState5 } from "react";
|
|
1628
|
+
var useLiveRouting = ({
|
|
1629
|
+
origin: manualOrigin,
|
|
1630
|
+
destination,
|
|
1631
|
+
profile = "driving",
|
|
1632
|
+
recalculateThreshold = 50,
|
|
1633
|
+
enabled = true,
|
|
1634
|
+
initialMode = "preview"
|
|
1635
|
+
}) => {
|
|
1636
|
+
const [route, setRoute] = useState5(null);
|
|
1637
|
+
const [totalDistance, setTotalDistance] = useState5(null);
|
|
1638
|
+
const [isCalculating, setIsCalculating] = useState5(false);
|
|
1639
|
+
const [error, setError] = useState5(null);
|
|
1640
|
+
const [mode, setMode] = useState5(initialMode);
|
|
1641
|
+
const userLocationState = useUserLocation();
|
|
1642
|
+
const userLocation = manualOrigin || userLocationState.coords;
|
|
1643
|
+
const lastCalcOrigin = useRef5(null);
|
|
1644
|
+
const lastCalcDest = useRef5(null);
|
|
1645
|
+
const isRequestInProgress = useRef5(false);
|
|
1646
|
+
const getDistance = useCallback4(
|
|
1647
|
+
(p1, p2) => {
|
|
1648
|
+
const R = 6371e3;
|
|
1649
|
+
const \u03C61 = p1[1] * Math.PI / 180;
|
|
1650
|
+
const \u03C62 = p2[1] * Math.PI / 180;
|
|
1651
|
+
const \u0394\u03C6 = (p2[1] - p1[1]) * Math.PI / 180;
|
|
1652
|
+
const \u0394\u03BB = (p2[0] - p1[0]) * Math.PI / 180;
|
|
1653
|
+
const a = Math.sin(\u0394\u03C6 / 2) * Math.sin(\u0394\u03C6 / 2) + Math.cos(\u03C61) * Math.cos(\u03C62) * Math.sin(\u0394\u03BB / 2) * Math.sin(\u0394\u03BB / 2);
|
|
1654
|
+
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
1655
|
+
return R * c;
|
|
1656
|
+
},
|
|
1657
|
+
[]
|
|
1658
|
+
);
|
|
1659
|
+
const currentDistanceMeters = useMemo2(() => {
|
|
1660
|
+
if (!userLocation || !destination) return null;
|
|
1661
|
+
return getDistance(userLocation, destination);
|
|
1662
|
+
}, [userLocation, destination, getDistance]);
|
|
1663
|
+
const calculate = useCallback4(
|
|
1664
|
+
async (origin, dest, isNewDest) => {
|
|
1665
|
+
if (isRequestInProgress.current) return;
|
|
1666
|
+
isRequestInProgress.current = true;
|
|
1667
|
+
setIsCalculating(true);
|
|
1668
|
+
try {
|
|
1669
|
+
const data = await sdk.routing.getRoute({
|
|
1670
|
+
destination: dest,
|
|
1671
|
+
origin,
|
|
1672
|
+
profile
|
|
1673
|
+
});
|
|
1674
|
+
setRoute(data);
|
|
1675
|
+
setError(null);
|
|
1676
|
+
if (isNewDest) {
|
|
1677
|
+
setTotalDistance(data.distanceMeters);
|
|
1678
|
+
}
|
|
1679
|
+
lastCalcOrigin.current = origin;
|
|
1680
|
+
lastCalcDest.current = dest;
|
|
1681
|
+
} catch (err) {
|
|
1682
|
+
setError(err.message || "Failed to fetch route");
|
|
1683
|
+
} finally {
|
|
1684
|
+
setIsCalculating(false);
|
|
1685
|
+
isRequestInProgress.current = false;
|
|
1686
|
+
}
|
|
1687
|
+
},
|
|
1688
|
+
[profile]
|
|
1689
|
+
);
|
|
1690
|
+
useEffect4(() => {
|
|
1691
|
+
if (!enabled || !destination || !userLocation) {
|
|
1692
|
+
if (!enabled || !destination) {
|
|
1693
|
+
setRoute(null);
|
|
1694
|
+
setTotalDistance(null);
|
|
1695
|
+
lastCalcOrigin.current = null;
|
|
1696
|
+
lastCalcDest.current = null;
|
|
1697
|
+
}
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
const destChanged = !lastCalcDest.current || lastCalcDest.current[0] !== destination[0] || lastCalcDest.current[1] !== destination[1];
|
|
1701
|
+
if (destChanged) {
|
|
1702
|
+
calculate(userLocation, destination, true);
|
|
1703
|
+
} else if (lastCalcOrigin.current) {
|
|
1704
|
+
const dist = getDistance(userLocation, lastCalcOrigin.current);
|
|
1705
|
+
if (dist > recalculateThreshold) {
|
|
1706
|
+
calculate(userLocation, destination, false);
|
|
1707
|
+
}
|
|
1708
|
+
} else {
|
|
1709
|
+
calculate(userLocation, destination, true);
|
|
1710
|
+
}
|
|
1711
|
+
}, [
|
|
1712
|
+
userLocation,
|
|
1713
|
+
destination,
|
|
1714
|
+
enabled,
|
|
1715
|
+
recalculateThreshold,
|
|
1716
|
+
calculate,
|
|
1717
|
+
getDistance
|
|
1718
|
+
]);
|
|
1719
|
+
return {
|
|
1720
|
+
currentDistanceMeters,
|
|
1721
|
+
error,
|
|
1722
|
+
isCalculating,
|
|
1723
|
+
mode,
|
|
1724
|
+
recalculate: () => userLocation && destination && calculate(userLocation, destination, false),
|
|
1725
|
+
route,
|
|
1726
|
+
start: () => setMode("navigation"),
|
|
1727
|
+
totalDistance
|
|
1728
|
+
};
|
|
1729
|
+
};
|
|
1730
|
+
var useRoute = (origin, destination, profile = "driving") => {
|
|
1731
|
+
const [route, setRoute] = useState5(null);
|
|
1732
|
+
const [isCalculating, setIsCalculating] = useState5(false);
|
|
1733
|
+
const [error, setError] = useState5(null);
|
|
1734
|
+
useEffect4(() => {
|
|
1735
|
+
if (!origin || !destination) {
|
|
1736
|
+
setRoute(null);
|
|
1737
|
+
return;
|
|
1738
|
+
}
|
|
1739
|
+
const fetchRoute = async () => {
|
|
1740
|
+
setIsCalculating(true);
|
|
1741
|
+
try {
|
|
1742
|
+
const data = await sdk.routing.getRoute({
|
|
1743
|
+
destination,
|
|
1744
|
+
origin,
|
|
1745
|
+
profile
|
|
1746
|
+
});
|
|
1747
|
+
setRoute(data);
|
|
1748
|
+
setError(null);
|
|
1749
|
+
} catch (err) {
|
|
1750
|
+
setError(err.message || "Failed to fetch route");
|
|
1751
|
+
} finally {
|
|
1752
|
+
setIsCalculating(false);
|
|
1753
|
+
}
|
|
1754
|
+
};
|
|
1755
|
+
fetchRoute();
|
|
1756
|
+
}, [origin, destination, profile]);
|
|
1757
|
+
return { error, isCalculating, route };
|
|
1758
|
+
};
|
|
1759
|
+
|
|
1760
|
+
// src/components/Map/hooks/useTrimmedRoute.ts
|
|
1761
|
+
import { useMemo as useMemo3 } from "react";
|
|
1762
|
+
var useTrimmedRoute = (route, userLocation) => {
|
|
1763
|
+
return useMemo3(() => {
|
|
1764
|
+
if (!route || !userLocation || route.coordinates.length < 2) return route;
|
|
1765
|
+
const coords = route.coordinates;
|
|
1766
|
+
let minDistance = Infinity;
|
|
1767
|
+
let closestIndex = 0;
|
|
1768
|
+
const getDistSq = (p1, p2) => {
|
|
1769
|
+
return (p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2;
|
|
1770
|
+
};
|
|
1771
|
+
for (let i = 0; i < coords.length; i++) {
|
|
1772
|
+
const dist = getDistSq(userLocation, coords[i]);
|
|
1773
|
+
if (dist < minDistance) {
|
|
1774
|
+
minDistance = dist;
|
|
1775
|
+
closestIndex = i;
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
const remainingCoords = coords.slice(closestIndex + 1);
|
|
1779
|
+
return {
|
|
1780
|
+
coordinates: [userLocation, ...remainingCoords],
|
|
1781
|
+
type: "LineString"
|
|
1782
|
+
};
|
|
1783
|
+
}, [route, userLocation]);
|
|
1784
|
+
};
|
|
1785
|
+
|
|
1786
|
+
// src/components/Map/layers/RouteLayer.tsx
|
|
1787
|
+
import { Layer, Source } from "@vis.gl/react-maplibre";
|
|
1788
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1789
|
+
var RouteLayer = ({
|
|
1790
|
+
geometry,
|
|
1791
|
+
color = "#3b82f6",
|
|
1792
|
+
width = 5,
|
|
1793
|
+
opacity = 0.8
|
|
1794
|
+
}) => {
|
|
1795
|
+
if (!geometry) return null;
|
|
1796
|
+
const data = {
|
|
1797
|
+
geometry,
|
|
1798
|
+
properties: {},
|
|
1799
|
+
type: "Feature"
|
|
1800
|
+
};
|
|
1801
|
+
return /* @__PURE__ */ jsxs6(Source, { data, id: "sdk-route-source", type: "geojson", children: [
|
|
1802
|
+
/* @__PURE__ */ jsx7(
|
|
1803
|
+
Layer,
|
|
1804
|
+
{
|
|
1805
|
+
id: "sdk-route-layer-casing",
|
|
1806
|
+
paint: {
|
|
1807
|
+
"line-blur": 1,
|
|
1808
|
+
"line-color": "#ffffff",
|
|
1809
|
+
"line-opacity": opacity,
|
|
1810
|
+
"line-width": width + 2
|
|
1811
|
+
},
|
|
1812
|
+
type: "line"
|
|
1813
|
+
}
|
|
1814
|
+
),
|
|
1815
|
+
/* @__PURE__ */ jsx7(
|
|
1816
|
+
Layer,
|
|
1817
|
+
{
|
|
1818
|
+
id: "sdk-route-layer",
|
|
1819
|
+
layout: {
|
|
1820
|
+
"line-cap": "round",
|
|
1821
|
+
"line-join": "round"
|
|
1822
|
+
},
|
|
1823
|
+
paint: {
|
|
1824
|
+
"line-color": color,
|
|
1825
|
+
"line-opacity": opacity,
|
|
1826
|
+
"line-width": width
|
|
1827
|
+
},
|
|
1828
|
+
type: "line"
|
|
1829
|
+
}
|
|
1830
|
+
)
|
|
1831
|
+
] });
|
|
1832
|
+
};
|
|
1833
|
+
|
|
1834
|
+
// src/components/Map/Map.tsx
|
|
1835
|
+
import { Map as MapLibre, MapProvider } from "@vis.gl/react-maplibre";
|
|
1836
|
+
import React, {
|
|
1837
|
+
useCallback as useCallback5,
|
|
1838
|
+
useContext as useContext4,
|
|
1839
|
+
useEffect as useEffect6,
|
|
1840
|
+
useMemo as useMemo5,
|
|
1841
|
+
useRef as useRef6,
|
|
1842
|
+
useState as useState7
|
|
1843
|
+
} from "react";
|
|
1844
|
+
import "maplibre-gl/dist/maplibre-gl.css";
|
|
1845
|
+
|
|
1846
|
+
// src/components/Map/layers/POILayers.tsx
|
|
1847
|
+
import { Layer as Layer2, Source as Source2, useMap } from "@vis.gl/react-maplibre";
|
|
1848
|
+
import { useEffect as useEffect5, useMemo as useMemo4, useState as useState6 } from "react";
|
|
1849
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1850
|
+
function createEmojiIcon(emoji, size = 64) {
|
|
1851
|
+
const canvas = document.createElement("canvas");
|
|
1852
|
+
canvas.width = size;
|
|
1853
|
+
canvas.height = size;
|
|
1854
|
+
const ctx = canvas.getContext("2d");
|
|
1855
|
+
ctx.font = `${size * 0.75}px serif`;
|
|
1856
|
+
ctx.textAlign = "center";
|
|
1857
|
+
ctx.textBaseline = "middle";
|
|
1858
|
+
ctx.fillText(emoji, size / 2, size / 2);
|
|
1859
|
+
return ctx.getImageData(0, 0, size, size);
|
|
1860
|
+
}
|
|
1861
|
+
var POILayers = ({ pois }) => {
|
|
1862
|
+
const [loadedImages, setLoadedImages] = useState6(0);
|
|
1863
|
+
const { current: map } = useMap();
|
|
1864
|
+
const glPois = useMemo4(
|
|
1865
|
+
() => pois.filter((p) => !p.displayMode || p.displayMode === "gl"),
|
|
1866
|
+
[pois]
|
|
1867
|
+
);
|
|
1868
|
+
useEffect5(() => {
|
|
1869
|
+
if (!map) return;
|
|
1870
|
+
const glMap = map.getMap();
|
|
1871
|
+
const defaults = [
|
|
1872
|
+
{ emoji: "\u{1F37A}", name: "sdk-default-bar" },
|
|
1873
|
+
{ emoji: "\u{1F37D}\uFE0F", name: "sdk-default-restaurant" },
|
|
1874
|
+
{ emoji: "\u{1F4CD}", name: "sdk-default-poi" }
|
|
1875
|
+
];
|
|
1876
|
+
defaults.forEach((def) => {
|
|
1877
|
+
if (!glMap.hasImage(def.name)) {
|
|
1878
|
+
glMap.addImage(def.name, createEmojiIcon(def.emoji), { pixelRatio: 2 });
|
|
1879
|
+
}
|
|
1880
|
+
});
|
|
1881
|
+
let updates = 0;
|
|
1882
|
+
glPois.forEach((poi) => {
|
|
1883
|
+
if (poi.imageUrl) {
|
|
1884
|
+
const name = `custom-url-${poi.id}`;
|
|
1885
|
+
if (!glMap.hasImage(name)) {
|
|
1886
|
+
glMap.loadImage(poi.imageUrl, (error, image) => {
|
|
1887
|
+
if (!error && image) {
|
|
1888
|
+
if (!glMap.hasImage(name)) {
|
|
1889
|
+
glMap.addImage(name, image);
|
|
1890
|
+
setLoadedImages((prev) => prev + 1);
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
});
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
if (poi.emoji) {
|
|
1897
|
+
const name = `custom-emoji-${poi.emoji}`;
|
|
1898
|
+
if (!glMap.hasImage(name)) {
|
|
1899
|
+
glMap.addImage(name, createEmojiIcon(poi.emoji), { pixelRatio: 2 });
|
|
1900
|
+
updates++;
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
});
|
|
1904
|
+
if (updates > 0) {
|
|
1905
|
+
setLoadedImages((prev) => prev + updates);
|
|
1906
|
+
}
|
|
1907
|
+
}, [map, glPois]);
|
|
1908
|
+
const geojson = useMemo4(
|
|
1909
|
+
() => ({
|
|
1910
|
+
features: glPois.map((poi) => ({
|
|
1911
|
+
geometry: {
|
|
1912
|
+
coordinates: [poi.longitude, poi.latitude],
|
|
1913
|
+
type: "Point"
|
|
1914
|
+
},
|
|
1915
|
+
properties: {
|
|
1916
|
+
...poi,
|
|
1917
|
+
// Selection logic for the icon in GL
|
|
1918
|
+
"sdk-icon": poi.imageUrl ? `custom-url-${poi.id}` : poi.spriteName ? poi.spriteName : poi.emoji ? `custom-emoji-${poi.emoji}` : poi.type === "bar" ? "sdk-default-bar" : poi.type === "restaurant" ? "sdk-default-restaurant" : "sdk-default-poi"
|
|
1919
|
+
},
|
|
1920
|
+
type: "Feature"
|
|
1921
|
+
})),
|
|
1922
|
+
type: "FeatureCollection"
|
|
1923
|
+
}),
|
|
1924
|
+
[glPois]
|
|
1925
|
+
);
|
|
1926
|
+
return /* @__PURE__ */ jsx8(Source2, { data: geojson, id: "sdk-pois", type: "geojson", children: /* @__PURE__ */ jsx8(
|
|
1927
|
+
Layer2,
|
|
1928
|
+
{
|
|
1929
|
+
id: "sdk-poi-layer",
|
|
1930
|
+
layout: {
|
|
1931
|
+
"icon-allow-overlap": true,
|
|
1932
|
+
"icon-image": ["get", "sdk-icon"],
|
|
1933
|
+
"icon-size": 1,
|
|
1934
|
+
"text-anchor": "top",
|
|
1935
|
+
"text-field": [
|
|
1936
|
+
"step",
|
|
1937
|
+
["zoom"],
|
|
1938
|
+
"",
|
|
1939
|
+
// Show nothing when zoomed out
|
|
1940
|
+
14,
|
|
1941
|
+
["get", "name"]
|
|
1942
|
+
// Show name when zoomed in
|
|
1943
|
+
],
|
|
1944
|
+
"text-font": ["Open Sans Bold"],
|
|
1945
|
+
"text-offset": [0, 1.5],
|
|
1946
|
+
"text-optional": true,
|
|
1947
|
+
"text-size": 11
|
|
1948
|
+
},
|
|
1949
|
+
paint: {
|
|
1950
|
+
"icon-opacity": ["interpolate", ["linear"], ["zoom"], 11, 0, 12, 1],
|
|
1951
|
+
"text-color": "#ffffff",
|
|
1952
|
+
"text-halo-color": "#000000",
|
|
1953
|
+
"text-halo-width": 1,
|
|
1954
|
+
"text-opacity": ["interpolate", ["linear"], ["zoom"], 13.5, 0, 14, 1]
|
|
1955
|
+
},
|
|
1956
|
+
type: "symbol"
|
|
1957
|
+
}
|
|
1958
|
+
) });
|
|
1959
|
+
};
|
|
1960
|
+
|
|
1961
|
+
// src/components/Map/layers/POIMarkers.tsx
|
|
1962
|
+
import { Marker } from "@vis.gl/react-maplibre";
|
|
1963
|
+
import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1964
|
+
var POIMarkers = ({ pois, onClick }) => {
|
|
1965
|
+
const markerPois = pois.filter((p) => p.displayMode === "marker");
|
|
1966
|
+
if (markerPois.length === 0) return null;
|
|
1967
|
+
return /* @__PURE__ */ jsx9(Fragment3, { children: markerPois.map((poi) => /* @__PURE__ */ jsx9(
|
|
1968
|
+
Marker,
|
|
1969
|
+
{
|
|
1970
|
+
anchor: "bottom",
|
|
1971
|
+
latitude: poi.latitude,
|
|
1972
|
+
longitude: poi.longitude,
|
|
1973
|
+
onClick: (e) => {
|
|
1974
|
+
e.originalEvent.stopPropagation();
|
|
1975
|
+
if (onClick) onClick(poi);
|
|
1976
|
+
},
|
|
1977
|
+
children: poi.markerComponent ? poi.markerComponent : /* @__PURE__ */ jsxs7(
|
|
1978
|
+
"div",
|
|
1979
|
+
{
|
|
1980
|
+
style: {
|
|
1981
|
+
cursor: "pointer",
|
|
1982
|
+
filter: "drop-shadow(0 4px 6px rgba(0,0,0,0.1))",
|
|
1983
|
+
transform: "transition-transform duration-200 hover:scale-110"
|
|
1984
|
+
},
|
|
1985
|
+
children: [
|
|
1986
|
+
/* @__PURE__ */ jsx9(
|
|
1987
|
+
"div",
|
|
1988
|
+
{
|
|
1989
|
+
style: {
|
|
1990
|
+
alignItems: "center",
|
|
1991
|
+
backgroundColor: poi.type === "bar" ? "#f97316" : "#10b981",
|
|
1992
|
+
border: "2px solid white",
|
|
1993
|
+
borderRadius: "50%",
|
|
1994
|
+
display: "flex",
|
|
1995
|
+
height: "32px",
|
|
1996
|
+
justifyContent: "center",
|
|
1997
|
+
width: "32px"
|
|
1998
|
+
},
|
|
1999
|
+
children: poi.emoji ? /* @__PURE__ */ jsx9("span", { style: { fontSize: "16px" }, children: poi.emoji }) : /* @__PURE__ */ jsx9(
|
|
2000
|
+
"div",
|
|
2001
|
+
{
|
|
2002
|
+
style: {
|
|
2003
|
+
backgroundColor: "white",
|
|
2004
|
+
borderRadius: "50%",
|
|
2005
|
+
height: "8px",
|
|
2006
|
+
width: "8px"
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
)
|
|
2010
|
+
}
|
|
2011
|
+
),
|
|
2012
|
+
/* @__PURE__ */ jsx9(
|
|
2013
|
+
"div",
|
|
2014
|
+
{
|
|
2015
|
+
style: {
|
|
2016
|
+
borderLeft: "6px solid transparent",
|
|
2017
|
+
borderRight: "6px solid transparent",
|
|
2018
|
+
borderTop: `8px solid ${poi.type === "bar" ? "#f97316" : "#10b981"}`,
|
|
2019
|
+
height: 0,
|
|
2020
|
+
margin: "-2px auto 0",
|
|
2021
|
+
width: 0
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
)
|
|
2025
|
+
]
|
|
2026
|
+
}
|
|
2027
|
+
)
|
|
2028
|
+
},
|
|
2029
|
+
poi.id
|
|
2030
|
+
)) });
|
|
2031
|
+
};
|
|
2032
|
+
|
|
2033
|
+
// src/components/Map/layers/UserLocationLayer.tsx
|
|
2034
|
+
import { Marker as Marker2 } from "@vis.gl/react-maplibre";
|
|
2035
|
+
import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2036
|
+
var UserLocationLayer = ({
|
|
2037
|
+
forcedLocation,
|
|
2038
|
+
heading
|
|
2039
|
+
}) => {
|
|
2040
|
+
if (!forcedLocation) return null;
|
|
2041
|
+
return /* @__PURE__ */ jsx10(
|
|
2042
|
+
Marker2,
|
|
2043
|
+
{
|
|
2044
|
+
anchor: "center",
|
|
2045
|
+
latitude: forcedLocation[1],
|
|
2046
|
+
longitude: forcedLocation[0],
|
|
2047
|
+
children: /* @__PURE__ */ jsxs8(
|
|
2048
|
+
"div",
|
|
2049
|
+
{
|
|
2050
|
+
style: {
|
|
2051
|
+
alignItems: "center",
|
|
2052
|
+
display: "flex",
|
|
2053
|
+
justifyContent: "center",
|
|
2054
|
+
position: "relative"
|
|
2055
|
+
},
|
|
2056
|
+
children: [
|
|
2057
|
+
/* @__PURE__ */ jsx10(
|
|
2058
|
+
"div",
|
|
2059
|
+
{
|
|
2060
|
+
style: {
|
|
2061
|
+
animation: "pulse 2s infinite",
|
|
2062
|
+
backgroundColor: "rgba(59, 130, 246, 0.4)",
|
|
2063
|
+
borderRadius: "50%",
|
|
2064
|
+
height: "40px",
|
|
2065
|
+
position: "absolute",
|
|
2066
|
+
width: "40px"
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
),
|
|
2070
|
+
heading !== void 0 && heading !== null && /* @__PURE__ */ jsx10(
|
|
2071
|
+
"div",
|
|
2072
|
+
{
|
|
2073
|
+
style: {
|
|
2074
|
+
height: "60px",
|
|
2075
|
+
position: "absolute",
|
|
2076
|
+
transform: `rotate(${heading}deg)`,
|
|
2077
|
+
width: "60px",
|
|
2078
|
+
zIndex: 0
|
|
2079
|
+
},
|
|
2080
|
+
children: /* @__PURE__ */ jsxs8(
|
|
2081
|
+
"svg",
|
|
2082
|
+
{
|
|
2083
|
+
style: { height: "100%", width: "100%" },
|
|
2084
|
+
viewBox: "0 0 100 100",
|
|
2085
|
+
children: [
|
|
2086
|
+
/* @__PURE__ */ jsx10("defs", { children: /* @__PURE__ */ jsxs8(
|
|
2087
|
+
"radialGradient",
|
|
2088
|
+
{
|
|
2089
|
+
cx: "50%",
|
|
2090
|
+
cy: "50%",
|
|
2091
|
+
fx: "50%",
|
|
2092
|
+
fy: "50%",
|
|
2093
|
+
id: "grad1",
|
|
2094
|
+
r: "50%",
|
|
2095
|
+
children: [
|
|
2096
|
+
/* @__PURE__ */ jsx10(
|
|
2097
|
+
"stop",
|
|
2098
|
+
{
|
|
2099
|
+
offset: "0%",
|
|
2100
|
+
style: {
|
|
2101
|
+
stopColor: "rgba(37, 99, 235, 0.6)",
|
|
2102
|
+
stopOpacity: 1
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
),
|
|
2106
|
+
/* @__PURE__ */ jsx10(
|
|
2107
|
+
"stop",
|
|
2108
|
+
{
|
|
2109
|
+
offset: "100%",
|
|
2110
|
+
style: {
|
|
2111
|
+
stopColor: "rgba(37, 99, 235, 0)",
|
|
2112
|
+
stopOpacity: 0
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
)
|
|
2116
|
+
]
|
|
2117
|
+
}
|
|
2118
|
+
) }),
|
|
2119
|
+
/* @__PURE__ */ jsx10(
|
|
2120
|
+
"path",
|
|
2121
|
+
{
|
|
2122
|
+
d: "M 50 50 L 35 0 A 50 50 0 0 1 65 0 Z",
|
|
2123
|
+
fill: "url(#grad1)"
|
|
2124
|
+
}
|
|
2125
|
+
)
|
|
2126
|
+
]
|
|
2127
|
+
}
|
|
2128
|
+
)
|
|
2129
|
+
}
|
|
2130
|
+
),
|
|
2131
|
+
/* @__PURE__ */ jsx10(
|
|
2132
|
+
"div",
|
|
2133
|
+
{
|
|
2134
|
+
style: {
|
|
2135
|
+
alignItems: "center",
|
|
2136
|
+
backgroundColor: "white",
|
|
2137
|
+
borderRadius: "50%",
|
|
2138
|
+
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(37, 99, 235, 0.2)",
|
|
2139
|
+
display: "flex",
|
|
2140
|
+
height: "20px",
|
|
2141
|
+
justifyContent: "center",
|
|
2142
|
+
width: "20px",
|
|
2143
|
+
zIndex: 1
|
|
2144
|
+
},
|
|
2145
|
+
children: /* @__PURE__ */ jsx10(
|
|
2146
|
+
"div",
|
|
2147
|
+
{
|
|
2148
|
+
style: {
|
|
2149
|
+
backgroundColor: "#2563eb",
|
|
2150
|
+
border: "2px solid white",
|
|
2151
|
+
borderRadius: "50%",
|
|
2152
|
+
height: "12px",
|
|
2153
|
+
width: "12px"
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
)
|
|
2157
|
+
}
|
|
2158
|
+
),
|
|
2159
|
+
/* @__PURE__ */ jsx10("style", { children: `@keyframes pulse { 0% { transform: scale(0.95); opacity: 0.7; } 70% { transform: scale(1.5); opacity: 0; } 100% { transform: scale(0.95); opacity: 0; } }` })
|
|
2160
|
+
]
|
|
2161
|
+
}
|
|
2162
|
+
)
|
|
2163
|
+
}
|
|
2164
|
+
);
|
|
2165
|
+
};
|
|
2166
|
+
|
|
2167
|
+
// src/components/Map/Map.tsx
|
|
2168
|
+
import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2169
|
+
var MapInternal = ({
|
|
2170
|
+
initialViewState = {
|
|
2171
|
+
latitude: 45.90583599233422,
|
|
2172
|
+
longitude: 6.12589018919138,
|
|
2173
|
+
zoom: 12
|
|
2174
|
+
},
|
|
2175
|
+
style = {},
|
|
2176
|
+
mapStyle = "https://tiles.cloudparse.fr/style/up_dark_matter",
|
|
2177
|
+
className = "",
|
|
2178
|
+
maxBounds,
|
|
2179
|
+
children,
|
|
2180
|
+
onClick,
|
|
2181
|
+
onMapClickMiss,
|
|
2182
|
+
onContextMenu,
|
|
2183
|
+
interactiveLayerIds = [],
|
|
2184
|
+
onLoad,
|
|
2185
|
+
showUserLocation = true,
|
|
2186
|
+
followUser: initialFollowUser = false,
|
|
2187
|
+
showUserLocationButton = true,
|
|
2188
|
+
renderUserLocation,
|
|
2189
|
+
pois = [],
|
|
2190
|
+
onPoiClick,
|
|
2191
|
+
route: externalRoute,
|
|
2192
|
+
showNavigationUI: externalShowNavigationUI,
|
|
2193
|
+
navigationData: externalNavigationData,
|
|
2194
|
+
onNavigationAbort,
|
|
2195
|
+
onNavigationModeChange,
|
|
2196
|
+
navigation,
|
|
2197
|
+
showControls = true,
|
|
2198
|
+
menuItems,
|
|
2199
|
+
menuContent,
|
|
2200
|
+
drawerState: propDrawerState,
|
|
2201
|
+
drawerContentRef,
|
|
2202
|
+
controlProps = {},
|
|
2203
|
+
enableTouchDrag = false
|
|
2204
|
+
}) => {
|
|
2205
|
+
const mapControls = useContext4(MapControlsContext);
|
|
2206
|
+
const mapRef = useRef6(null);
|
|
2207
|
+
const userLocationState = useUserLocation();
|
|
2208
|
+
const [progNav, setProgNav] = useState7(null);
|
|
2209
|
+
useEffect6(() => {
|
|
2210
|
+
const unsubCreate = sdk.on("SDK_NAVIGATION_CREATE", (payload) => {
|
|
2211
|
+
setProgNav(payload);
|
|
2212
|
+
});
|
|
2213
|
+
const unsubStart = sdk.on("SDK_NAVIGATION_START", () => {
|
|
2214
|
+
setProgNav((prev) => prev ? { ...prev, mode: "navigation" } : null);
|
|
2215
|
+
});
|
|
2216
|
+
const unsubAbort = sdk.on("SDK_NAVIGATION_ABORT", () => {
|
|
2217
|
+
setProgNav(null);
|
|
2218
|
+
});
|
|
2219
|
+
return () => {
|
|
2220
|
+
unsubCreate();
|
|
2221
|
+
unsubStart();
|
|
2222
|
+
unsubAbort();
|
|
2223
|
+
};
|
|
2224
|
+
}, []);
|
|
2225
|
+
const effectiveNavOptions = progNav || navigation;
|
|
2226
|
+
const internalNav = useLiveRouting({
|
|
2227
|
+
destination: effectiveNavOptions?.destination || null,
|
|
2228
|
+
enabled: !!effectiveNavOptions?.destination,
|
|
2229
|
+
initialMode: effectiveNavOptions?.mode || "preview",
|
|
2230
|
+
origin: effectiveNavOptions?.origin,
|
|
2231
|
+
profile: effectiveNavOptions?.profile || "driving",
|
|
2232
|
+
recalculateThreshold: effectiveNavOptions?.recalculateThreshold || 50
|
|
2233
|
+
});
|
|
2234
|
+
useEffect6(() => {
|
|
2235
|
+
if (onNavigationModeChange && navigation) {
|
|
2236
|
+
onNavigationModeChange(internalNav.mode);
|
|
2237
|
+
}
|
|
2238
|
+
}, [internalNav.mode, onNavigationModeChange, navigation]);
|
|
2239
|
+
const navigationData = effectiveNavOptions ? internalNav : externalNavigationData;
|
|
2240
|
+
const route = effectiveNavOptions ? internalNav.route?.geometry : externalRoute;
|
|
2241
|
+
const showNavigationUI = effectiveNavOptions?.showUI !== false && (effectiveNavOptions ? !!effectiveNavOptions.destination : externalShowNavigationUI);
|
|
2242
|
+
const mode = navigationData?.mode || "preview";
|
|
2243
|
+
const resolvedInitialFollow = mapControls ? mapControls.isFollowing : initialFollowUser;
|
|
2244
|
+
const isFollowingRef = useRef6(resolvedInitialFollow);
|
|
2245
|
+
const isFlyingRef = useRef6(false);
|
|
2246
|
+
const [localIsFollowingUI, setLocalIsFollowingUI] = useState7(
|
|
2247
|
+
resolvedInitialFollow
|
|
2248
|
+
);
|
|
2249
|
+
const isFollowingUI = mapControls ? mapControls.isFollowing : localIsFollowingUI;
|
|
2250
|
+
const setIsFollowingCtx = mapControls?.setIsFollowing;
|
|
2251
|
+
const setFollowMode = useCallback5(
|
|
2252
|
+
(follow) => {
|
|
2253
|
+
isFollowingRef.current = follow;
|
|
2254
|
+
setLocalIsFollowingUI(follow);
|
|
2255
|
+
if (setIsFollowingCtx) {
|
|
2256
|
+
setIsFollowingCtx(follow);
|
|
2257
|
+
}
|
|
2258
|
+
},
|
|
2259
|
+
[setIsFollowingCtx]
|
|
2260
|
+
);
|
|
2261
|
+
useEffect6(() => {
|
|
2262
|
+
if (mapControls && mapControls.isFollowing !== isFollowingRef.current) {
|
|
2263
|
+
isFollowingRef.current = mapControls.isFollowing;
|
|
2264
|
+
}
|
|
2265
|
+
}, [mapControls?.isFollowing, mapControls]);
|
|
2266
|
+
const mergedInteractiveLayerIds = [...interactiveLayerIds];
|
|
2267
|
+
if (pois.length > 0 && !mergedInteractiveLayerIds.includes("sdk-poi-layer")) {
|
|
2268
|
+
mergedInteractiveLayerIds.push("sdk-poi-layer");
|
|
2269
|
+
}
|
|
2270
|
+
const prevInitialFollow = useRef6(initialFollowUser);
|
|
2271
|
+
useEffect6(() => {
|
|
2272
|
+
if (initialFollowUser !== prevInitialFollow.current) {
|
|
2273
|
+
setFollowMode(initialFollowUser);
|
|
2274
|
+
prevInitialFollow.current = initialFollowUser;
|
|
2275
|
+
if (initialFollowUser && userLocationState.coords && mapRef.current) {
|
|
2276
|
+
isFlyingRef.current = true;
|
|
2277
|
+
mapRef.current.flyTo({
|
|
2278
|
+
center: userLocationState.coords,
|
|
2279
|
+
duration: 1e3,
|
|
2280
|
+
essential: true,
|
|
2281
|
+
zoom: Math.max(mapRef.current.getZoom(), 16)
|
|
2282
|
+
});
|
|
2283
|
+
setTimeout(() => {
|
|
2284
|
+
isFlyingRef.current = false;
|
|
2285
|
+
}, 1050);
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
}, [initialFollowUser, setFollowMode, userLocationState.coords]);
|
|
2289
|
+
const lastAutoLockedRouteRef = useRef6(null);
|
|
2290
|
+
useEffect6(() => {
|
|
2291
|
+
if (!route || !mapRef.current) {
|
|
2292
|
+
if (!route) lastAutoLockedRouteRef.current = null;
|
|
2293
|
+
return;
|
|
2294
|
+
}
|
|
2295
|
+
const isNewRoute = route !== lastAutoLockedRouteRef.current;
|
|
2296
|
+
if (!isNewRoute && mode === "navigation") return;
|
|
2297
|
+
if (mode === "navigation") {
|
|
2298
|
+
setFollowMode(true);
|
|
2299
|
+
} else if (mode === "preview" && isNewRoute) {
|
|
2300
|
+
setFollowMode(false);
|
|
2301
|
+
const coords = route.coordinates;
|
|
2302
|
+
if (coords.length > 0) {
|
|
2303
|
+
const bounds = coords.reduce(
|
|
2304
|
+
(acc, coord) => {
|
|
2305
|
+
return [
|
|
2306
|
+
[Math.min(acc[0][0], coord[0]), Math.min(acc[0][1], coord[1])],
|
|
2307
|
+
[Math.max(acc[1][0], coord[0]), Math.max(acc[1][1], coord[1])]
|
|
2308
|
+
];
|
|
2309
|
+
},
|
|
2310
|
+
[
|
|
2311
|
+
[coords[0][0], coords[0][1]],
|
|
2312
|
+
[coords[0][0], coords[0][1]]
|
|
2313
|
+
]
|
|
2314
|
+
);
|
|
2315
|
+
mapRef.current.fitBounds(
|
|
2316
|
+
[bounds[0][0], bounds[0][1], bounds[1][0], bounds[1][1]],
|
|
2317
|
+
{ padding: 100, speed: 1.2 }
|
|
2318
|
+
);
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
lastAutoLockedRouteRef.current = route;
|
|
2322
|
+
}, [route, mode, setFollowMode]);
|
|
2323
|
+
useEffect6(() => {
|
|
2324
|
+
if (isFollowingRef.current && !isFlyingRef.current && userLocationState.coords && mapRef.current) {
|
|
2325
|
+
mapRef.current.easeTo({
|
|
2326
|
+
center: userLocationState.coords,
|
|
2327
|
+
duration: 50,
|
|
2328
|
+
// Ultra-short for high-frequency interpolated stream
|
|
2329
|
+
easing: (t) => t,
|
|
2330
|
+
essential: true
|
|
2331
|
+
});
|
|
2332
|
+
}
|
|
2333
|
+
}, [userLocationState.coords]);
|
|
2334
|
+
const displayRoute = useTrimmedRoute(
|
|
2335
|
+
route,
|
|
2336
|
+
showUserLocation ? userLocationState.coords : null
|
|
2337
|
+
);
|
|
2338
|
+
const remainingMeters = navigationData?.currentDistanceMeters ?? navigationData?.route?.distanceMeters ?? 0;
|
|
2339
|
+
const etaMins = navigationData?.route?.durationSeconds ? Math.ceil(navigationData.route.durationSeconds / 60) : 0;
|
|
2340
|
+
const kmRemaining = remainingMeters / 1e3;
|
|
2341
|
+
const totalDist = navigationData?.totalDistance ?? 0;
|
|
2342
|
+
const progressValue = totalDist > 0 ? (totalDist - remainingMeters) / totalDist * 100 : 0;
|
|
2343
|
+
const showEtaData = useMemo5(() => {
|
|
2344
|
+
if (!navigationData || !route) return void 0;
|
|
2345
|
+
return {
|
|
2346
|
+
distanceRemainingKm: kmRemaining,
|
|
2347
|
+
etaMins,
|
|
2348
|
+
label: mode === "navigation" ? "Walking Guidance Active" : "Route Preview",
|
|
2349
|
+
mode,
|
|
2350
|
+
onAbort: onNavigationAbort || (() => {
|
|
2351
|
+
setProgNav(null);
|
|
2352
|
+
}),
|
|
2353
|
+
onStart: navigationData.start,
|
|
2354
|
+
progress: progressValue
|
|
2355
|
+
};
|
|
2356
|
+
}, [
|
|
2357
|
+
navigationData,
|
|
2358
|
+
route,
|
|
2359
|
+
etaMins,
|
|
2360
|
+
kmRemaining,
|
|
2361
|
+
mode,
|
|
2362
|
+
progressValue,
|
|
2363
|
+
onNavigationAbort
|
|
2364
|
+
]);
|
|
2365
|
+
const setEtaDataCtx = mapControls?.setEtaData;
|
|
2366
|
+
useEffect6(() => {
|
|
2367
|
+
if (setEtaDataCtx) {
|
|
2368
|
+
setEtaDataCtx(showEtaData);
|
|
2369
|
+
}
|
|
2370
|
+
}, [showEtaData, setEtaDataCtx]);
|
|
2371
|
+
const setDrawerStateCtx = mapControls?.setDrawerState;
|
|
2372
|
+
useEffect6(() => {
|
|
2373
|
+
if (setDrawerStateCtx && propDrawerState !== void 0) {
|
|
2374
|
+
setDrawerStateCtx(propDrawerState);
|
|
2375
|
+
}
|
|
2376
|
+
}, [propDrawerState, setDrawerStateCtx]);
|
|
2377
|
+
const defaultMenuItems = useMemo5(
|
|
2378
|
+
() => [
|
|
2379
|
+
{
|
|
2380
|
+
icon: /* @__PURE__ */ jsx11(
|
|
2381
|
+
"svg",
|
|
2382
|
+
{
|
|
2383
|
+
className: "w-5 h-5",
|
|
2384
|
+
fill: "none",
|
|
2385
|
+
stroke: "currentColor",
|
|
2386
|
+
strokeWidth: "2",
|
|
2387
|
+
viewBox: "0 0 24 24",
|
|
2388
|
+
children: /* @__PURE__ */ jsx11(
|
|
2389
|
+
"path",
|
|
2390
|
+
{
|
|
2391
|
+
d: "M12 11c0 3.517-1.009 6.799-2.753 9.571m-3.44-2.04l.054-.09A13.916 13.916 0 008 11a4 4 0 118 0c0 1.017-.07 2.019-.203 3m-2.118 6.844A21.88 21.88 0 0015.171 17m3.839 1.132c.645-2.266.99-4.659.99-7.132A8 8 0 008 4.07M3 15.364c.64-1.319 1-2.8 1-4.364 0-1.457.39-2.823 1.07-4",
|
|
2392
|
+
strokeLinecap: "round",
|
|
2393
|
+
strokeLinejoin: "round"
|
|
2394
|
+
}
|
|
2395
|
+
)
|
|
162
2396
|
}
|
|
2397
|
+
),
|
|
2398
|
+
id: "walking",
|
|
2399
|
+
label: "Walking Mode",
|
|
2400
|
+
onPress: () => {
|
|
163
2401
|
}
|
|
164
2402
|
}
|
|
2403
|
+
],
|
|
2404
|
+
[]
|
|
2405
|
+
);
|
|
2406
|
+
const resolvedMenuItems = menuItems || defaultMenuItems;
|
|
2407
|
+
const { mapLibreChildren, menuContentFromChildren } = useMemo5(() => {
|
|
2408
|
+
const mapLibreChildren2 = [];
|
|
2409
|
+
const extractedMenuItems = [];
|
|
2410
|
+
React.Children.forEach(children, (child) => {
|
|
2411
|
+
if (React.isValidElement(child)) {
|
|
2412
|
+
const type = child.type;
|
|
2413
|
+
const displayName = type?.displayName || type?.name;
|
|
2414
|
+
const isMenu = type === MapMenu || displayName === "MapMenu";
|
|
2415
|
+
const isMenuComponent = isMenu || type === MapMenuItem || type === MapMenuTitle || type === MapMenuDivider || type === MapMenuFooter || [
|
|
2416
|
+
"MapMenuItem",
|
|
2417
|
+
"MapMenuTitle",
|
|
2418
|
+
"MapMenuDivider",
|
|
2419
|
+
"MapMenuFooter"
|
|
2420
|
+
].includes(displayName);
|
|
2421
|
+
if (isMenu) {
|
|
2422
|
+
React.Children.forEach(
|
|
2423
|
+
child.props.children,
|
|
2424
|
+
(nestedChild) => {
|
|
2425
|
+
if (nestedChild) {
|
|
2426
|
+
extractedMenuItems.push(nestedChild);
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
);
|
|
2430
|
+
} else if (isMenuComponent) {
|
|
2431
|
+
extractedMenuItems.push(child);
|
|
2432
|
+
} else {
|
|
2433
|
+
mapLibreChildren2.push(child);
|
|
2434
|
+
}
|
|
2435
|
+
} else {
|
|
2436
|
+
mapLibreChildren2.push(child);
|
|
2437
|
+
}
|
|
2438
|
+
});
|
|
2439
|
+
const menuContentFromChildren2 = extractedMenuItems.length > 0 ? /* @__PURE__ */ jsx11(MapMenu, { children: extractedMenuItems }) : null;
|
|
2440
|
+
return { mapLibreChildren: mapLibreChildren2, menuContentFromChildren: menuContentFromChildren2 };
|
|
2441
|
+
}, [children]);
|
|
2442
|
+
const handleMapClick = (e) => {
|
|
2443
|
+
const clickedFeatures = e.features || [];
|
|
2444
|
+
if (onPoiClick) {
|
|
2445
|
+
const poiFeature = clickedFeatures.find(
|
|
2446
|
+
(f) => f.layer.id === "sdk-poi-layer"
|
|
2447
|
+
);
|
|
2448
|
+
if (poiFeature) {
|
|
2449
|
+
onPoiClick(poiFeature.properties);
|
|
2450
|
+
return;
|
|
2451
|
+
}
|
|
165
2452
|
}
|
|
2453
|
+
const hitInteractive = clickedFeatures.some(
|
|
2454
|
+
(f) => mergedInteractiveLayerIds.includes(f.layer.id)
|
|
2455
|
+
);
|
|
2456
|
+
if (!hitInteractive) {
|
|
2457
|
+
if (onMapClickMiss) {
|
|
2458
|
+
onMapClickMiss(e);
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
if (onClick) onClick(e);
|
|
2462
|
+
};
|
|
2463
|
+
const handleCenterOnUser = useCallback5(
|
|
2464
|
+
(payload) => {
|
|
2465
|
+
if (userLocationState.coords && mapRef.current) {
|
|
2466
|
+
setFollowMode(true);
|
|
2467
|
+
isFlyingRef.current = true;
|
|
2468
|
+
mapRef.current.flyTo({
|
|
2469
|
+
center: userLocationState.coords,
|
|
2470
|
+
duration: 1e3,
|
|
2471
|
+
essential: true,
|
|
2472
|
+
pitch: payload?.pitch || 0,
|
|
2473
|
+
zoom: Math.max(mapRef.current.getZoom(), 16)
|
|
2474
|
+
});
|
|
2475
|
+
setTimeout(() => {
|
|
2476
|
+
isFlyingRef.current = false;
|
|
2477
|
+
}, 1050);
|
|
2478
|
+
}
|
|
2479
|
+
},
|
|
2480
|
+
[userLocationState.coords, setFollowMode]
|
|
2481
|
+
);
|
|
2482
|
+
const handleResetBearing = useCallback5((params) => {
|
|
2483
|
+
if (mapRef.current) {
|
|
2484
|
+
mapRef.current.easeTo({
|
|
2485
|
+
bearing: 0,
|
|
2486
|
+
duration: 500,
|
|
2487
|
+
essential: true,
|
|
2488
|
+
pitch: params?.pitch ?? 0
|
|
2489
|
+
});
|
|
2490
|
+
}
|
|
2491
|
+
}, []);
|
|
2492
|
+
useEffect6(() => {
|
|
2493
|
+
if (mapControls) {
|
|
2494
|
+
mapControls._registerCenterCallback(handleCenterOnUser);
|
|
2495
|
+
mapControls._registerResetBearingCallback(handleResetBearing);
|
|
2496
|
+
}
|
|
2497
|
+
}, [mapControls, handleCenterOnUser, handleResetBearing]);
|
|
2498
|
+
const handleMove = useCallback5(() => {
|
|
2499
|
+
if (mapRef.current && mapControls) {
|
|
2500
|
+
mapControls.setBearing(mapRef.current.getBearing());
|
|
2501
|
+
mapControls.setPitch(mapRef.current.getPitch());
|
|
2502
|
+
}
|
|
2503
|
+
}, [mapControls]);
|
|
2504
|
+
const handleUserInteraction = () => {
|
|
2505
|
+
if (isFollowingRef.current) {
|
|
2506
|
+
console.log("[SDK:Map] Manual interaction detected. Halting tracking.");
|
|
2507
|
+
setFollowMode(false);
|
|
2508
|
+
if (mapRef.current) {
|
|
2509
|
+
mapRef.current.stop();
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
};
|
|
2513
|
+
return /* @__PURE__ */ jsxs9(
|
|
2514
|
+
"div",
|
|
2515
|
+
{
|
|
2516
|
+
className: `w-full h-full overflow-hidden relative ${className}`,
|
|
2517
|
+
style,
|
|
2518
|
+
children: [
|
|
2519
|
+
/* @__PURE__ */ jsxs9(
|
|
2520
|
+
MapLibre,
|
|
2521
|
+
{
|
|
2522
|
+
attributionControl: false,
|
|
2523
|
+
initialViewState,
|
|
2524
|
+
interactiveLayerIds: mergedInteractiveLayerIds,
|
|
2525
|
+
mapStyle,
|
|
2526
|
+
maxBounds,
|
|
2527
|
+
onClick: handleMapClick,
|
|
2528
|
+
onContextMenu,
|
|
2529
|
+
onDragStart: handleUserInteraction,
|
|
2530
|
+
onLoad: (e) => {
|
|
2531
|
+
if (onLoad) onLoad(e);
|
|
2532
|
+
if (initialFollowUser && userLocationState.coords && mapRef.current) {
|
|
2533
|
+
mapRef.current.jumpTo({
|
|
2534
|
+
center: userLocationState.coords,
|
|
2535
|
+
zoom: 16
|
|
2536
|
+
});
|
|
2537
|
+
}
|
|
2538
|
+
},
|
|
2539
|
+
onMouseDown: handleUserInteraction,
|
|
2540
|
+
onMove: handleMove,
|
|
2541
|
+
onTouchStart: handleUserInteraction,
|
|
2542
|
+
onWheel: handleUserInteraction,
|
|
2543
|
+
ref: mapRef,
|
|
2544
|
+
children: [
|
|
2545
|
+
showUserLocation && userLocationState.coords && (renderUserLocation ? renderUserLocation({
|
|
2546
|
+
coords: userLocationState.coords,
|
|
2547
|
+
heading: userLocationState.heading
|
|
2548
|
+
}) : /* @__PURE__ */ jsx11(
|
|
2549
|
+
UserLocationLayer,
|
|
2550
|
+
{
|
|
2551
|
+
forcedLocation: userLocationState.coords,
|
|
2552
|
+
heading: userLocationState.heading
|
|
2553
|
+
}
|
|
2554
|
+
)),
|
|
2555
|
+
displayRoute && /* @__PURE__ */ jsx11(RouteLayer, { geometry: displayRoute }),
|
|
2556
|
+
pois.length > 0 && /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
2557
|
+
/* @__PURE__ */ jsx11(POILayers, { pois }),
|
|
2558
|
+
/* @__PURE__ */ jsx11(POIMarkers, { onClick: onPoiClick, pois })
|
|
2559
|
+
] }),
|
|
2560
|
+
mapLibreChildren
|
|
2561
|
+
]
|
|
2562
|
+
}
|
|
2563
|
+
),
|
|
2564
|
+
showControls && mapControls && !mapControls.hasControls && /* @__PURE__ */ jsx11(
|
|
2565
|
+
MapControls,
|
|
2566
|
+
{
|
|
2567
|
+
drawerContentRef,
|
|
2568
|
+
enableTouchDrag,
|
|
2569
|
+
etaData: showEtaData,
|
|
2570
|
+
initialViewState,
|
|
2571
|
+
isInternal: true,
|
|
2572
|
+
menuContent: menuContent || menuContentFromChildren,
|
|
2573
|
+
menuItems: resolvedMenuItems,
|
|
2574
|
+
...controlProps
|
|
2575
|
+
}
|
|
2576
|
+
)
|
|
2577
|
+
]
|
|
2578
|
+
}
|
|
2579
|
+
);
|
|
2580
|
+
};
|
|
2581
|
+
var SDKMapProviderWrapper = ({
|
|
2582
|
+
children
|
|
2583
|
+
}) => {
|
|
2584
|
+
const context = useContext4(MapControlsContext);
|
|
2585
|
+
if (context) {
|
|
2586
|
+
return /* @__PURE__ */ jsx11(Fragment4, { children });
|
|
166
2587
|
}
|
|
2588
|
+
return /* @__PURE__ */ jsx11(SDKMapProvider, { children });
|
|
167
2589
|
};
|
|
2590
|
+
var Map2 = (props) => {
|
|
2591
|
+
return /* @__PURE__ */ jsx11(SDKMapProviderWrapper, { children: /* @__PURE__ */ jsx11(MapProvider, { children: /* @__PURE__ */ jsx11(MapInternal, { ...props }) }) });
|
|
2592
|
+
};
|
|
2593
|
+
|
|
2594
|
+
// src/components/MapWrapper.tsx
|
|
2595
|
+
import React2 from "react";
|
|
2596
|
+
import { createRoot } from "react-dom/client";
|
|
2597
|
+
function renderMap(containerId, props = {}) {
|
|
2598
|
+
const container = document.getElementById(containerId);
|
|
2599
|
+
if (!container) {
|
|
2600
|
+
throw new Error(`Container with id "${containerId}" not found.`);
|
|
2601
|
+
}
|
|
2602
|
+
const root = createRoot(container);
|
|
2603
|
+
root.render(React2.createElement(Map2, props));
|
|
2604
|
+
return root;
|
|
2605
|
+
}
|
|
2606
|
+
|
|
2607
|
+
// package.json
|
|
2608
|
+
var version = "0.2.1";
|
|
168
2609
|
|
|
169
2610
|
// src/sdk.ts
|
|
170
2611
|
function deepFreeze(obj) {
|
|
@@ -178,7 +2619,19 @@ function deepFreeze(obj) {
|
|
|
178
2619
|
}
|
|
179
2620
|
return Object.freeze(obj);
|
|
180
2621
|
}
|
|
2622
|
+
var globalRoutingConfig = null;
|
|
181
2623
|
var MiniAppSDK = class {
|
|
2624
|
+
/**
|
|
2625
|
+
* Map services.
|
|
2626
|
+
* Provides access to MapLibre-based mapping components.
|
|
2627
|
+
*/
|
|
2628
|
+
maps = Object.freeze({
|
|
2629
|
+
/**
|
|
2630
|
+
* Renders a MapLibre map into a DOM element.
|
|
2631
|
+
* Useful for non-React environments.
|
|
2632
|
+
*/
|
|
2633
|
+
render: renderMap
|
|
2634
|
+
});
|
|
182
2635
|
bridge;
|
|
183
2636
|
/**
|
|
184
2637
|
* Local SQLite Storage.
|
|
@@ -216,8 +2669,19 @@ var MiniAppSDK = class {
|
|
|
216
2669
|
* Device Location services.
|
|
217
2670
|
*/
|
|
218
2671
|
location = Object.freeze({
|
|
219
|
-
/**
|
|
220
|
-
|
|
2672
|
+
/**
|
|
2673
|
+
* Retrieves the current GPS position of the device.
|
|
2674
|
+
*/
|
|
2675
|
+
getCurrentPosition: () => this.bridge.send("GET_CURRENT_POSITION"),
|
|
2676
|
+
/**
|
|
2677
|
+
* Starts the continuous location and sensor stream.
|
|
2678
|
+
* @param config Optional configuration for thresholds and intervals.
|
|
2679
|
+
*/
|
|
2680
|
+
startStream: (config) => this.bridge.send("START_LOCATION_STREAM", config),
|
|
2681
|
+
/**
|
|
2682
|
+
* Stops the continuous location and sensor stream.
|
|
2683
|
+
*/
|
|
2684
|
+
stopStream: () => this.bridge.send("STOP_LOCATION_STREAM")
|
|
221
2685
|
});
|
|
222
2686
|
/**
|
|
223
2687
|
* Identity & Profile management.
|
|
@@ -285,7 +2749,44 @@ var MiniAppSDK = class {
|
|
|
285
2749
|
* Performs a network request via the host shell.
|
|
286
2750
|
* @param options Request method, URL, headers, and body.
|
|
287
2751
|
*/
|
|
288
|
-
request: (options) =>
|
|
2752
|
+
request: async (options) => {
|
|
2753
|
+
return this.bridge.send("NETWORK_REQUEST", options);
|
|
2754
|
+
}
|
|
2755
|
+
});
|
|
2756
|
+
/**
|
|
2757
|
+
* Navigation and Journey controls.
|
|
2758
|
+
* Allows programmatically starting itineraries and managing navigation state.
|
|
2759
|
+
*/
|
|
2760
|
+
navigation = Object.freeze({
|
|
2761
|
+
/**
|
|
2762
|
+
* Aborts the current navigation journey.
|
|
2763
|
+
*/
|
|
2764
|
+
abort: () => {
|
|
2765
|
+
this.bridge.handleNativeMessage({
|
|
2766
|
+
eventName: "SDK_NAVIGATION_ABORT",
|
|
2767
|
+
type: "EVENT"
|
|
2768
|
+
});
|
|
2769
|
+
},
|
|
2770
|
+
/**
|
|
2771
|
+
* Creates a new itinerary preview.
|
|
2772
|
+
* @param options Destination and routing options.
|
|
2773
|
+
*/
|
|
2774
|
+
createJourney: (options) => {
|
|
2775
|
+
this.bridge.handleNativeMessage({
|
|
2776
|
+
eventName: "SDK_NAVIGATION_CREATE",
|
|
2777
|
+
payload: { ...options, mode: "preview" },
|
|
2778
|
+
type: "EVENT"
|
|
2779
|
+
});
|
|
2780
|
+
},
|
|
2781
|
+
/**
|
|
2782
|
+
* Starts the active navigation (active guidance).
|
|
2783
|
+
*/
|
|
2784
|
+
start: () => {
|
|
2785
|
+
this.bridge.handleNativeMessage({
|
|
2786
|
+
eventName: "SDK_NAVIGATION_START",
|
|
2787
|
+
type: "EVENT"
|
|
2788
|
+
});
|
|
2789
|
+
}
|
|
289
2790
|
});
|
|
290
2791
|
/**
|
|
291
2792
|
* Fidelity & Rewards program integration.
|
|
@@ -298,6 +2799,10 @@ var MiniAppSDK = class {
|
|
|
298
2799
|
* @param params Filtering and pagination parameters.
|
|
299
2800
|
*/
|
|
300
2801
|
getFidelityHistory: (params) => this.bridge.send("GET_FIDELITY_HISTORY", params),
|
|
2802
|
+
/**
|
|
2803
|
+
* Triggers the native fidelity history view.
|
|
2804
|
+
*/
|
|
2805
|
+
showHistory: () => this.bridge.send("SHOW_FIDELITY_HISTORY"),
|
|
301
2806
|
/**
|
|
302
2807
|
* Authorize spending fidelity points (FPoints) via Two-Phase Authorization.
|
|
303
2808
|
* @param intentUuid UUID generated by your backend for the transaction.
|
|
@@ -326,12 +2831,84 @@ var MiniAppSDK = class {
|
|
|
326
2831
|
*/
|
|
327
2832
|
log: (...message) => this.bridge.send("DEBUG_LOG", { message })
|
|
328
2833
|
});
|
|
2834
|
+
/**
|
|
2835
|
+
* Routing integration (OSRM / Valhalla).
|
|
2836
|
+
* Provides access to route calculation and rendering on the host map.
|
|
2837
|
+
*/
|
|
2838
|
+
routing = Object.freeze({
|
|
2839
|
+
/**
|
|
2840
|
+
* Clears the currently rendered route.
|
|
2841
|
+
*/
|
|
2842
|
+
clearRoute: () => {
|
|
2843
|
+
const requestId = crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(2);
|
|
2844
|
+
return this.bridge.send("SDK_ROUTING_CLEARROUTE", {
|
|
2845
|
+
requestId,
|
|
2846
|
+
sdkVersion: version
|
|
2847
|
+
});
|
|
2848
|
+
},
|
|
2849
|
+
/**
|
|
2850
|
+
* Draws the calculated route on the host map.
|
|
2851
|
+
* @param routeData The route data returned by getRoute.
|
|
2852
|
+
*/
|
|
2853
|
+
drawRoute: (routeData) => {
|
|
2854
|
+
const requestId = crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(2);
|
|
2855
|
+
return this.bridge.send("SDK_ROUTING_DRAWROUTE", {
|
|
2856
|
+
payload: { routeData },
|
|
2857
|
+
requestId,
|
|
2858
|
+
sdkVersion: version
|
|
2859
|
+
});
|
|
2860
|
+
},
|
|
2861
|
+
/**
|
|
2862
|
+
* Calculates a route between points.
|
|
2863
|
+
* @param payload Request payload containing origin, destination, and options.
|
|
2864
|
+
*/
|
|
2865
|
+
getRoute: async (payload) => {
|
|
2866
|
+
const requestId = crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(2);
|
|
2867
|
+
const request = {
|
|
2868
|
+
payload,
|
|
2869
|
+
requestId,
|
|
2870
|
+
sdkVersion: version,
|
|
2871
|
+
type: "SDK_ROUTING_GETROUTE"
|
|
2872
|
+
};
|
|
2873
|
+
if (globalRoutingConfig) {
|
|
2874
|
+
const response = await RoutingService.fetchRoute(
|
|
2875
|
+
request,
|
|
2876
|
+
globalRoutingConfig
|
|
2877
|
+
);
|
|
2878
|
+
if (response.success && response.data) {
|
|
2879
|
+
return response.data;
|
|
2880
|
+
}
|
|
2881
|
+
throw new Error(response.error?.message || "Routing failed");
|
|
2882
|
+
}
|
|
2883
|
+
return this.bridge.send("SDK_ROUTING_GETROUTE", {
|
|
2884
|
+
payload,
|
|
2885
|
+
requestId,
|
|
2886
|
+
sdkVersion: version
|
|
2887
|
+
});
|
|
2888
|
+
}
|
|
2889
|
+
});
|
|
329
2890
|
constructor() {
|
|
330
2891
|
this.bridge = new Bridge();
|
|
331
2892
|
this.initialize = this.initialize.bind(this);
|
|
332
2893
|
this.on = this.on.bind(this);
|
|
333
2894
|
this.off = this.off.bind(this);
|
|
334
2895
|
}
|
|
2896
|
+
/**
|
|
2897
|
+
* Configures global SDK settings.
|
|
2898
|
+
*/
|
|
2899
|
+
config(options) {
|
|
2900
|
+
if (options.routingBackend) {
|
|
2901
|
+
globalRoutingConfig = options.routingBackend;
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
/**
|
|
2905
|
+
* Updates the global context shared between the Mini-App and the native shell.
|
|
2906
|
+
* Useful for synchronizing state like theme preferences, UI flags, or custom app-specific context.
|
|
2907
|
+
* @param context Partial context object to merge into the existing shell context.
|
|
2908
|
+
*/
|
|
2909
|
+
updateContext(context) {
|
|
2910
|
+
return this.bridge.send("UPDATE_CONTEXT", context);
|
|
2911
|
+
}
|
|
335
2912
|
/**
|
|
336
2913
|
* The starting point for the Mini-App. Must be called before any other SDK methods.
|
|
337
2914
|
* @param options The options to provide to the shell app
|
|
@@ -359,19 +2936,642 @@ var MiniAppSDK = class {
|
|
|
359
2936
|
};
|
|
360
2937
|
var sdk = deepFreeze(new MiniAppSDK());
|
|
361
2938
|
|
|
362
|
-
// src/
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
2939
|
+
// src/components/BridgeMocker.tsx
|
|
2940
|
+
import { useCallback as useCallback6, useEffect as useEffect7, useRef as useRef7, useState as useState8 } from "react";
|
|
2941
|
+
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2942
|
+
var getBearing = (p1, p2) => {
|
|
2943
|
+
const lat1 = p1[1] * Math.PI / 180;
|
|
2944
|
+
const lat2 = p2[1] * Math.PI / 180;
|
|
2945
|
+
const dLon = (p2[0] - p1[0]) * Math.PI / 180;
|
|
2946
|
+
const y = Math.sin(dLon) * Math.cos(lat2);
|
|
2947
|
+
const x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
|
|
2948
|
+
return (Math.atan2(y, x) * 180 / Math.PI + 360) % 360;
|
|
2949
|
+
};
|
|
2950
|
+
var BridgeMocker = ({
|
|
2951
|
+
appId,
|
|
2952
|
+
initialLocation = [6.1258, 45.9058],
|
|
2953
|
+
// Annecy default
|
|
2954
|
+
isMoving = false,
|
|
2955
|
+
simulationPath,
|
|
2956
|
+
simulationSpeed = 1,
|
|
2957
|
+
mockJWTToken,
|
|
2958
|
+
children
|
|
2959
|
+
}) => {
|
|
2960
|
+
const [autoMove, setAutoMove] = useState8(isMoving);
|
|
2961
|
+
const [location, setLocation] = useState8(initialLocation);
|
|
2962
|
+
const [heading, setHeading] = useState8(0);
|
|
2963
|
+
const [speedMultiplier, setSpeedMultiplier] = useState8(simulationSpeed);
|
|
2964
|
+
const [isReady, setIsReady] = useState8(false);
|
|
2965
|
+
const [isMinimized, setIsMinimized] = useState8(true);
|
|
2966
|
+
const streamIntervalRef = useRef7(null);
|
|
2967
|
+
const pathIndexRef = useRef7(0);
|
|
2968
|
+
const activeConfigRef = useRef7(null);
|
|
2969
|
+
const stateRef = useRef7({
|
|
2970
|
+
autoMove,
|
|
2971
|
+
heading,
|
|
2972
|
+
location,
|
|
2973
|
+
mockJWTToken,
|
|
2974
|
+
pushLocationUpdate: () => {
|
|
2975
|
+
},
|
|
2976
|
+
setHeading,
|
|
2977
|
+
setLocation,
|
|
2978
|
+
simulationPath,
|
|
2979
|
+
speedMultiplier
|
|
2980
|
+
});
|
|
2981
|
+
const pushLocationUpdate = useCallback6(() => {
|
|
2982
|
+
const currentState = stateRef.current;
|
|
2983
|
+
const payload = {
|
|
2984
|
+
accelerometer: null,
|
|
2985
|
+
heading: {
|
|
2986
|
+
accuracy: 3,
|
|
2987
|
+
magneticHeading: currentState.heading,
|
|
2988
|
+
trueHeading: currentState.heading
|
|
2989
|
+
},
|
|
2990
|
+
location: {
|
|
2991
|
+
coords: {
|
|
2992
|
+
accuracy: 10,
|
|
2993
|
+
altitude: 0,
|
|
2994
|
+
altitudeAccuracy: null,
|
|
2995
|
+
heading: currentState.heading,
|
|
2996
|
+
latitude: currentState.location[1],
|
|
2997
|
+
longitude: currentState.location[0],
|
|
2998
|
+
speed: currentState.autoMove ? 1.5 * currentState.speedMultiplier : 0
|
|
2999
|
+
},
|
|
3000
|
+
timestamp: Date.now()
|
|
3001
|
+
},
|
|
3002
|
+
timestamp: Date.now()
|
|
3003
|
+
};
|
|
3004
|
+
window.__MiniAppBridge?.onNativeMessage({
|
|
3005
|
+
eventName: "LOCATION_STREAM_UPDATE" /* LOCATION_STREAM_UPDATE */,
|
|
3006
|
+
payload,
|
|
3007
|
+
type: "EVENT"
|
|
3008
|
+
});
|
|
3009
|
+
}, []);
|
|
3010
|
+
useEffect7(() => {
|
|
3011
|
+
stateRef.current = {
|
|
3012
|
+
autoMove,
|
|
3013
|
+
heading,
|
|
3014
|
+
location,
|
|
3015
|
+
mockJWTToken,
|
|
3016
|
+
pushLocationUpdate,
|
|
3017
|
+
setHeading,
|
|
3018
|
+
setLocation,
|
|
3019
|
+
simulationPath,
|
|
3020
|
+
speedMultiplier
|
|
3021
|
+
};
|
|
3022
|
+
}, [
|
|
3023
|
+
location,
|
|
3024
|
+
heading,
|
|
3025
|
+
autoMove,
|
|
3026
|
+
pushLocationUpdate,
|
|
3027
|
+
simulationPath,
|
|
3028
|
+
speedMultiplier,
|
|
3029
|
+
mockJWTToken
|
|
3030
|
+
]);
|
|
3031
|
+
useEffect7(() => {
|
|
3032
|
+
setAutoMove(isMoving);
|
|
3033
|
+
}, [isMoving]);
|
|
3034
|
+
useEffect7(() => {
|
|
3035
|
+
setSpeedMultiplier(simulationSpeed);
|
|
3036
|
+
}, [simulationSpeed]);
|
|
3037
|
+
useEffect7(() => {
|
|
3038
|
+
if (simulationPath && simulationPath.length > 0) {
|
|
3039
|
+
pathIndexRef.current = 0;
|
|
3040
|
+
setLocation(simulationPath[0]);
|
|
3041
|
+
}
|
|
3042
|
+
}, [simulationPath]);
|
|
3043
|
+
useEffect7(() => {
|
|
3044
|
+
if (streamIntervalRef.current && activeConfigRef.current) {
|
|
3045
|
+
sdk.bridge.send(
|
|
3046
|
+
"START_LOCATION_STREAM",
|
|
3047
|
+
activeConfigRef.current
|
|
3048
|
+
);
|
|
3049
|
+
}
|
|
3050
|
+
}, [speedMultiplier]);
|
|
3051
|
+
useEffect7(() => {
|
|
3052
|
+
const bridge = sdk.bridge;
|
|
3053
|
+
const originalSend = bridge.send.bind(bridge);
|
|
3054
|
+
const stopStream = () => {
|
|
3055
|
+
if (streamIntervalRef.current) {
|
|
3056
|
+
clearInterval(streamIntervalRef.current);
|
|
3057
|
+
streamIntervalRef.current = null;
|
|
3058
|
+
}
|
|
3059
|
+
activeConfigRef.current = null;
|
|
3060
|
+
};
|
|
3061
|
+
const startStream = (config) => {
|
|
3062
|
+
stopStream();
|
|
3063
|
+
activeConfigRef.current = config || {};
|
|
3064
|
+
const baseInterval = config?.interval || 500;
|
|
3065
|
+
const interval = Math.max(
|
|
3066
|
+
10,
|
|
3067
|
+
baseInterval / stateRef.current.speedMultiplier
|
|
3068
|
+
);
|
|
3069
|
+
streamIntervalRef.current = setInterval(() => {
|
|
3070
|
+
const state = stateRef.current;
|
|
3071
|
+
if (state.autoMove) {
|
|
3072
|
+
if (state.simulationPath && state.simulationPath.length > 0) {
|
|
3073
|
+
if (pathIndexRef.current < state.simulationPath.length - 1) {
|
|
3074
|
+
const currentPoint = state.simulationPath[pathIndexRef.current];
|
|
3075
|
+
const nextPoint = state.simulationPath[pathIndexRef.current + 1];
|
|
3076
|
+
pathIndexRef.current += 1;
|
|
3077
|
+
const newHeading = getBearing(currentPoint, nextPoint);
|
|
3078
|
+
state.setHeading(newHeading);
|
|
3079
|
+
state.setLocation(nextPoint);
|
|
3080
|
+
} else {
|
|
3081
|
+
state.setLocation(
|
|
3082
|
+
state.simulationPath[state.simulationPath.length - 1]
|
|
3083
|
+
);
|
|
3084
|
+
}
|
|
3085
|
+
} else {
|
|
3086
|
+
state.setLocation((prev) => [
|
|
3087
|
+
prev[0] + (Math.random() - 0.5) * 1e-4,
|
|
3088
|
+
prev[1] + (Math.random() - 0.5) * 1e-4
|
|
3089
|
+
]);
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
state.pushLocationUpdate();
|
|
3093
|
+
}, interval);
|
|
3094
|
+
};
|
|
3095
|
+
const simulateNetworkRequest = async (payload) => {
|
|
3096
|
+
const {
|
|
3097
|
+
url,
|
|
3098
|
+
method = "GET",
|
|
3099
|
+
headers: incomingHeaders = {},
|
|
3100
|
+
body
|
|
3101
|
+
} = payload;
|
|
3102
|
+
const state = stateRef.current;
|
|
3103
|
+
const headers = new Headers(incomingHeaders);
|
|
3104
|
+
if (state.mockJWTToken) {
|
|
3105
|
+
headers.set(
|
|
3106
|
+
"Authorization",
|
|
3107
|
+
`Bearer ${state.mockJWTToken.replace("Bearer ", "")}`
|
|
3108
|
+
);
|
|
3109
|
+
}
|
|
3110
|
+
if (body && typeof body === "object" && !headers.has("Content-Type")) {
|
|
3111
|
+
headers.set("Content-Type", "application/json");
|
|
3112
|
+
}
|
|
3113
|
+
try {
|
|
3114
|
+
const response = await fetch(url, {
|
|
3115
|
+
body: body ? typeof body === "string" ? body : JSON.stringify(body) : void 0,
|
|
3116
|
+
headers,
|
|
3117
|
+
method
|
|
3118
|
+
});
|
|
3119
|
+
const contentType = response.headers.get("content-type");
|
|
3120
|
+
let responseData;
|
|
3121
|
+
if (contentType?.includes("application/json")) {
|
|
3122
|
+
responseData = await response.json();
|
|
3123
|
+
} else {
|
|
3124
|
+
responseData = await response.text();
|
|
3125
|
+
}
|
|
3126
|
+
const headersObj = {};
|
|
3127
|
+
response.headers.forEach((value, key) => {
|
|
3128
|
+
headersObj[key] = value;
|
|
3129
|
+
});
|
|
3130
|
+
return {
|
|
3131
|
+
data: responseData,
|
|
3132
|
+
headers: headersObj,
|
|
3133
|
+
status: response.status,
|
|
3134
|
+
statusText: response.statusText
|
|
3135
|
+
};
|
|
3136
|
+
} catch (error) {
|
|
3137
|
+
throw {
|
|
3138
|
+
error: error.message || "Network request failed",
|
|
3139
|
+
status: 0
|
|
3140
|
+
};
|
|
3141
|
+
}
|
|
3142
|
+
};
|
|
3143
|
+
bridge.send = async (type, payload) => {
|
|
3144
|
+
if (type === "INITIALIZE") {
|
|
3145
|
+
return Promise.resolve({
|
|
3146
|
+
appId,
|
|
3147
|
+
// Legacy support
|
|
3148
|
+
context: { theme: "dark" },
|
|
3149
|
+
// Legacy support
|
|
3150
|
+
description: "Mocked for Dev",
|
|
3151
|
+
env: {},
|
|
3152
|
+
insets: { bottom: 0, left: 0, right: 0, top: 0 },
|
|
3153
|
+
name: "Vite Dev (Mock)",
|
|
3154
|
+
params: {},
|
|
3155
|
+
profile: {
|
|
3156
|
+
countryCode: "FR",
|
|
3157
|
+
email: "caveman@up.app",
|
|
3158
|
+
fullName: "Smart Caveman",
|
|
3159
|
+
localNumber: "0600000000",
|
|
3160
|
+
username: "smart_caveman",
|
|
3161
|
+
uuid: "mock-user-42"
|
|
3162
|
+
},
|
|
3163
|
+
success: true,
|
|
3164
|
+
userData: {},
|
|
3165
|
+
uuid: appId || "mock-app-id",
|
|
3166
|
+
version: "1.0.0"
|
|
3167
|
+
});
|
|
3168
|
+
}
|
|
3169
|
+
if (type === "GET_CURRENT_POSITION") {
|
|
3170
|
+
const state = stateRef.current;
|
|
3171
|
+
return Promise.resolve({
|
|
3172
|
+
coords: {
|
|
3173
|
+
accuracy: 10,
|
|
3174
|
+
altitude: 0,
|
|
3175
|
+
altitudeAccuracy: 0,
|
|
3176
|
+
heading: state.heading,
|
|
3177
|
+
latitude: state.location[1],
|
|
3178
|
+
longitude: state.location[0],
|
|
3179
|
+
speed: state.autoMove ? 1.5 * state.speedMultiplier : 0
|
|
3180
|
+
},
|
|
3181
|
+
timestamp: Date.now()
|
|
3182
|
+
});
|
|
3183
|
+
}
|
|
3184
|
+
if (type === "START_LOCATION_STREAM") {
|
|
3185
|
+
startStream(payload);
|
|
3186
|
+
return Promise.resolve();
|
|
3187
|
+
}
|
|
3188
|
+
if (type === "STOP_LOCATION_STREAM") {
|
|
3189
|
+
stopStream();
|
|
3190
|
+
return Promise.resolve();
|
|
3191
|
+
}
|
|
3192
|
+
if (type === "NETWORK_REQUEST") {
|
|
3193
|
+
return simulateNetworkRequest(payload);
|
|
3194
|
+
}
|
|
3195
|
+
if (type === "STORAGE_GET" || type === "SECURE_GET") {
|
|
3196
|
+
return Promise.resolve(localStorage.getItem(`mock_sdk_${payload.key}`));
|
|
3197
|
+
}
|
|
3198
|
+
if (type === "STORAGE_SET" || type === "SECURE_SET") {
|
|
3199
|
+
localStorage.setItem(`mock_sdk_${payload.key}`, payload.value);
|
|
3200
|
+
return Promise.resolve();
|
|
3201
|
+
}
|
|
3202
|
+
if (type === "GET_USER_PROFILE") {
|
|
3203
|
+
return Promise.resolve({
|
|
3204
|
+
countryCode: "FR",
|
|
3205
|
+
email: "caveman@up.app",
|
|
3206
|
+
fullName: "Smart Caveman",
|
|
3207
|
+
localNumber: "0600000000",
|
|
3208
|
+
username: "smart_caveman",
|
|
3209
|
+
uuid: "mock-user-42"
|
|
3210
|
+
});
|
|
3211
|
+
}
|
|
3212
|
+
if (type === "GET_FIDELITY_BALANCE") {
|
|
3213
|
+
return Promise.resolve({ balance: 1337, currency: "FPoints" });
|
|
3214
|
+
}
|
|
3215
|
+
if (type === "PAYMENT_REQUEST") {
|
|
3216
|
+
return Promise.resolve({
|
|
3217
|
+
success: true,
|
|
3218
|
+
transactionId: `tx_${Math.random().toString(36).slice(2)}`
|
|
3219
|
+
});
|
|
3220
|
+
}
|
|
3221
|
+
if (type === "TRIGGER_HAPTIC" || type === "EXIT" || type === "UPDATE_CONTEXT") {
|
|
3222
|
+
return Promise.resolve({ success: true });
|
|
3223
|
+
}
|
|
3224
|
+
return originalSend(type, payload);
|
|
3225
|
+
};
|
|
3226
|
+
setIsReady(true);
|
|
3227
|
+
return () => {
|
|
3228
|
+
stopStream();
|
|
3229
|
+
bridge.send = originalSend;
|
|
3230
|
+
};
|
|
3231
|
+
}, [appId]);
|
|
3232
|
+
if (!isReady) return null;
|
|
3233
|
+
return /* @__PURE__ */ jsxs10("div", { className: "relative w-full h-full", children: [
|
|
3234
|
+
/* @__PURE__ */ jsx12("div", { className: "w-full h-full", children }),
|
|
3235
|
+
isMinimized ? /* @__PURE__ */ jsx12(
|
|
3236
|
+
"button",
|
|
3237
|
+
{
|
|
3238
|
+
className: "fixed bottom-4 right-4 w-8 h-8 rounded-full bg-slate-900/60 backdrop-blur-sm border border-slate-700/50 flex items-center justify-center text-slate-400 hover:text-blue-400 z-[9999] transition-all shadow-lg group",
|
|
3239
|
+
onClick: () => setIsMinimized(false),
|
|
3240
|
+
title: "Open Simulation Controls",
|
|
3241
|
+
type: "button",
|
|
3242
|
+
children: /* @__PURE__ */ jsxs10(
|
|
3243
|
+
"svg",
|
|
3244
|
+
{
|
|
3245
|
+
className: "group-hover:scale-110 transition-transform",
|
|
3246
|
+
fill: "none",
|
|
3247
|
+
height: "14",
|
|
3248
|
+
stroke: "currentColor",
|
|
3249
|
+
strokeLinecap: "round",
|
|
3250
|
+
strokeLinejoin: "round",
|
|
3251
|
+
strokeWidth: "2",
|
|
3252
|
+
viewBox: "0 0 24 24",
|
|
3253
|
+
width: "14",
|
|
3254
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3255
|
+
children: [
|
|
3256
|
+
/* @__PURE__ */ jsx12("path", { d: "M21 4H3" }),
|
|
3257
|
+
/* @__PURE__ */ jsx12("path", { d: "M10 8H3" }),
|
|
3258
|
+
/* @__PURE__ */ jsx12("path", { d: "M21 12H3" }),
|
|
3259
|
+
/* @__PURE__ */ jsx12("path", { d: "M10 16H3" }),
|
|
3260
|
+
/* @__PURE__ */ jsx12("path", { d: "M21 20H3" }),
|
|
3261
|
+
/* @__PURE__ */ jsx12("circle", { cx: "14", cy: "8", r: "2" }),
|
|
3262
|
+
/* @__PURE__ */ jsx12("circle", { cx: "14", cy: "16", r: "2" })
|
|
3263
|
+
]
|
|
3264
|
+
}
|
|
3265
|
+
)
|
|
3266
|
+
}
|
|
3267
|
+
) : /* @__PURE__ */ jsxs10(
|
|
3268
|
+
"div",
|
|
3269
|
+
{
|
|
3270
|
+
className: "fixed bottom-4 right-4 bg-slate-900 text-slate-300 font-mono text-xs rounded-xl shadow-2xl border border-slate-700 pointer-events-auto flex flex-col w-80 overflow-hidden animate-in slide-in-from-right duration-300",
|
|
3271
|
+
style: { zIndex: 9998 },
|
|
3272
|
+
children: [
|
|
3273
|
+
/* @__PURE__ */ jsxs10(
|
|
3274
|
+
"div",
|
|
3275
|
+
{
|
|
3276
|
+
className: "flex justify-between items-center p-3 border-b border-slate-800 cursor-pointer bg-slate-800/50 hover:bg-slate-800 transition-colors",
|
|
3277
|
+
onClick: () => setIsMinimized(true),
|
|
3278
|
+
children: [
|
|
3279
|
+
/* @__PURE__ */ jsxs10("span", { className: "font-bold text-slate-100 flex items-center gap-2", children: [
|
|
3280
|
+
/* @__PURE__ */ jsx12("span", { className: "text-lg", children: "\u{1F6E0}\uFE0F" }),
|
|
3281
|
+
" Simulation Controls"
|
|
3282
|
+
] }),
|
|
3283
|
+
/* @__PURE__ */ jsx12("button", { className: "text-slate-500 hover:text-white", type: "button", children: "\u25BC" })
|
|
3284
|
+
]
|
|
3285
|
+
}
|
|
3286
|
+
),
|
|
3287
|
+
/* @__PURE__ */ jsxs10("div", { className: "p-4 flex flex-col gap-3", children: [
|
|
3288
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex justify-between items-center", children: [
|
|
3289
|
+
/* @__PURE__ */ jsxs10("label", { className: "flex items-center gap-2 cursor-pointer", children: [
|
|
3290
|
+
/* @__PURE__ */ jsx12(
|
|
3291
|
+
"input",
|
|
3292
|
+
{
|
|
3293
|
+
checked: autoMove,
|
|
3294
|
+
className: "accent-blue-500 w-4 h-4",
|
|
3295
|
+
onChange: (e) => setAutoMove(e.target.checked),
|
|
3296
|
+
type: "checkbox"
|
|
3297
|
+
}
|
|
3298
|
+
),
|
|
3299
|
+
/* @__PURE__ */ jsxs10("span", { className: "text-slate-200 font-bold", children: [
|
|
3300
|
+
"Auto-Move ",
|
|
3301
|
+
simulationPath ? "(Path)" : "(Random)"
|
|
3302
|
+
] })
|
|
3303
|
+
] }),
|
|
3304
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2", children: [
|
|
3305
|
+
/* @__PURE__ */ jsx12("span", { className: "text-[10px] text-slate-500", children: "Speed:" }),
|
|
3306
|
+
/* @__PURE__ */ jsx12(
|
|
3307
|
+
"select",
|
|
3308
|
+
{
|
|
3309
|
+
className: "bg-slate-800 border border-slate-700 rounded px-1 py-0.5 text-white focus:outline-none focus:border-blue-500 text-[10px]",
|
|
3310
|
+
onChange: (e) => setSpeedMultiplier(Number(e.target.value)),
|
|
3311
|
+
value: speedMultiplier,
|
|
3312
|
+
children: [1, 2, 5, 10, 50, 100, 250].map((s) => /* @__PURE__ */ jsxs10("option", { value: s, children: [
|
|
3313
|
+
s,
|
|
3314
|
+
"x"
|
|
3315
|
+
] }, s))
|
|
3316
|
+
}
|
|
3317
|
+
)
|
|
3318
|
+
] })
|
|
3319
|
+
] }),
|
|
3320
|
+
/* @__PURE__ */ jsxs10("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
3321
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-1", children: [
|
|
3322
|
+
/* @__PURE__ */ jsx12("span", { className: "text-[10px] text-slate-500 text-left", children: "Lng" }),
|
|
3323
|
+
/* @__PURE__ */ jsx12(
|
|
3324
|
+
"input",
|
|
3325
|
+
{
|
|
3326
|
+
className: "bg-slate-800 border border-slate-700 rounded px-2 py-1 text-white focus:outline-none focus:border-blue-500",
|
|
3327
|
+
onChange: (e) => {
|
|
3328
|
+
const lng = parseFloat(e.target.value) || 0;
|
|
3329
|
+
setLocation([lng, location[1]]);
|
|
3330
|
+
pushLocationUpdate();
|
|
3331
|
+
},
|
|
3332
|
+
step: "0.001",
|
|
3333
|
+
type: "number",
|
|
3334
|
+
value: location[0]
|
|
3335
|
+
}
|
|
3336
|
+
)
|
|
3337
|
+
] }),
|
|
3338
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-1", children: [
|
|
3339
|
+
/* @__PURE__ */ jsx12("span", { className: "text-[10px] text-slate-500 text-left", children: "Lat" }),
|
|
3340
|
+
/* @__PURE__ */ jsx12(
|
|
3341
|
+
"input",
|
|
3342
|
+
{
|
|
3343
|
+
className: "bg-slate-800 border border-slate-700 rounded px-2 py-1 text-white focus:outline-none focus:border-blue-500",
|
|
3344
|
+
onChange: (e) => {
|
|
3345
|
+
const lat = parseFloat(e.target.value) || 0;
|
|
3346
|
+
setLocation([location[0], lat]);
|
|
3347
|
+
pushLocationUpdate();
|
|
3348
|
+
},
|
|
3349
|
+
step: "0.001",
|
|
3350
|
+
type: "number",
|
|
3351
|
+
value: location[1]
|
|
3352
|
+
}
|
|
3353
|
+
)
|
|
3354
|
+
] }),
|
|
3355
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-1 col-span-2", children: [
|
|
3356
|
+
/* @__PURE__ */ jsx12("span", { className: "text-[10px] text-slate-500", children: "Heading (Degrees)" }),
|
|
3357
|
+
/* @__PURE__ */ jsx12(
|
|
3358
|
+
"input",
|
|
3359
|
+
{
|
|
3360
|
+
className: "w-full accent-blue-500",
|
|
3361
|
+
max: "360",
|
|
3362
|
+
min: "0",
|
|
3363
|
+
onChange: (e) => {
|
|
3364
|
+
setHeading(parseInt(e.target.value, 10));
|
|
3365
|
+
pushLocationUpdate();
|
|
3366
|
+
},
|
|
3367
|
+
type: "range",
|
|
3368
|
+
value: heading
|
|
3369
|
+
}
|
|
3370
|
+
),
|
|
3371
|
+
/* @__PURE__ */ jsxs10("div", { className: "text-right text-slate-400", children: [
|
|
3372
|
+
heading,
|
|
3373
|
+
"\xB0"
|
|
3374
|
+
] })
|
|
3375
|
+
] })
|
|
3376
|
+
] })
|
|
3377
|
+
] })
|
|
3378
|
+
]
|
|
3379
|
+
}
|
|
3380
|
+
)
|
|
3381
|
+
] });
|
|
3382
|
+
};
|
|
3383
|
+
|
|
3384
|
+
// src/components/DebugConsole.tsx
|
|
3385
|
+
import { useCallback as useCallback7, useEffect as useEffect8, useRef as useRef8, useState as useState9 } from "react";
|
|
3386
|
+
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3387
|
+
var DebugConsole = ({
|
|
3388
|
+
isOpen: initialOpen = false,
|
|
3389
|
+
onClose
|
|
3390
|
+
}) => {
|
|
3391
|
+
const [showConsole, setShowConsole] = useState9(initialOpen);
|
|
3392
|
+
const [logs, setLogs] = useState9([]);
|
|
3393
|
+
const scrollRef = useRef8(null);
|
|
3394
|
+
useEffect8(() => {
|
|
3395
|
+
setShowConsole(initialOpen);
|
|
3396
|
+
}, [initialOpen]);
|
|
3397
|
+
const clearLogs = () => setLogs([]);
|
|
3398
|
+
const addLog = useCallback7((label, data) => {
|
|
3399
|
+
if (label.includes("LOCATION_STREAM_UPDATE" /* LOCATION_STREAM_UPDATE */)) return;
|
|
3400
|
+
const entry = {
|
|
3401
|
+
content: typeof data === "string" ? data : JSON.stringify(data, null, 2),
|
|
3402
|
+
id: Math.random().toString(36).slice(2),
|
|
3403
|
+
label,
|
|
3404
|
+
time: (/* @__PURE__ */ new Date()).toLocaleTimeString()
|
|
3405
|
+
};
|
|
3406
|
+
setLogs((prev) => [entry, ...prev].slice(0, 100));
|
|
3407
|
+
}, []);
|
|
3408
|
+
useEffect8(() => {
|
|
3409
|
+
if (typeof window === "undefined") return;
|
|
3410
|
+
const bridge = sdk.bridge;
|
|
3411
|
+
const originalSend = bridge.send.bind(bridge);
|
|
3412
|
+
bridge.send = async (type, payload) => {
|
|
3413
|
+
addLog(`OUT: ${type}`, payload);
|
|
3414
|
+
return originalSend(type, payload);
|
|
3415
|
+
};
|
|
3416
|
+
const originalHandle = bridge.handleNativeMessage.bind(bridge);
|
|
3417
|
+
bridge.handleNativeMessage = (envelope) => {
|
|
3418
|
+
const type = envelope.eventName || envelope.event || envelope.type || "MSG";
|
|
3419
|
+
if (typeof type === "string") {
|
|
3420
|
+
addLog(`IN: ${type}`, envelope.payload || envelope.value || envelope);
|
|
3421
|
+
originalHandle(envelope);
|
|
3422
|
+
}
|
|
3423
|
+
};
|
|
3424
|
+
return () => {
|
|
3425
|
+
bridge.send = originalSend;
|
|
3426
|
+
bridge.handleNativeMessage = originalHandle;
|
|
3427
|
+
};
|
|
3428
|
+
}, [addLog]);
|
|
3429
|
+
if (!showConsole) {
|
|
3430
|
+
return /* @__PURE__ */ jsx13(
|
|
3431
|
+
"button",
|
|
3432
|
+
{
|
|
3433
|
+
className: "fixed bottom-4 left-4 w-8 h-8 rounded-full bg-slate-900/60 backdrop-blur-sm border border-slate-700/50 flex items-center justify-center text-slate-400 hover:text-emerald-400 transition-all z-[9998] shadow-lg group",
|
|
3434
|
+
onClick: () => setShowConsole(true),
|
|
3435
|
+
title: "Open Debug Console",
|
|
3436
|
+
type: "button",
|
|
3437
|
+
children: /* @__PURE__ */ jsxs11(
|
|
3438
|
+
"svg",
|
|
3439
|
+
{
|
|
3440
|
+
className: "group-hover:scale-110 transition-transform",
|
|
3441
|
+
fill: "none",
|
|
3442
|
+
height: "14",
|
|
3443
|
+
stroke: "currentColor",
|
|
3444
|
+
strokeLinecap: "round",
|
|
3445
|
+
strokeLinejoin: "round",
|
|
3446
|
+
strokeWidth: "2",
|
|
3447
|
+
viewBox: "0 0 24 24",
|
|
3448
|
+
width: "14",
|
|
3449
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3450
|
+
children: [
|
|
3451
|
+
/* @__PURE__ */ jsx13("path", { d: "m8 2 1.88 1.88" }),
|
|
3452
|
+
/* @__PURE__ */ jsx13("path", { d: "M14.12 3.88 16 2" }),
|
|
3453
|
+
/* @__PURE__ */ jsx13("path", { d: "M9 7.13v-1a3.003 3.003 0 1 1 6 0v1" }),
|
|
3454
|
+
/* @__PURE__ */ jsx13("path", { d: "M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6" }),
|
|
3455
|
+
/* @__PURE__ */ jsx13("path", { d: "M12 20v-9" }),
|
|
3456
|
+
/* @__PURE__ */ jsx13("path", { d: "M6.53 9C4.6 8.8 3 7.1 3 5" }),
|
|
3457
|
+
/* @__PURE__ */ jsx13("path", { d: "M6 13H2" }),
|
|
3458
|
+
/* @__PURE__ */ jsx13("path", { d: "M3 21c0-2.1 1.7-3.9 3.8-4" }),
|
|
3459
|
+
/* @__PURE__ */ jsx13("path", { d: "M20.97 5c0 2.1-1.6 3.8-3.5 4" }),
|
|
3460
|
+
/* @__PURE__ */ jsx13("path", { d: "M22 13h-4" }),
|
|
3461
|
+
/* @__PURE__ */ jsx13("path", { d: "M17.2 17c2.1.1 3.8 1.9 3.8 4" })
|
|
3462
|
+
]
|
|
3463
|
+
}
|
|
3464
|
+
)
|
|
3465
|
+
}
|
|
3466
|
+
);
|
|
3467
|
+
}
|
|
3468
|
+
return /* @__PURE__ */ jsxs11("div", { className: "fixed inset-x-0 bottom-0 w-full bg-[#0a080f] border-t border-slate-800 z-[9998] p-4 shadow-2xl flex flex-col animate-in slide-in-from-bottom duration-300", children: [
|
|
3469
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-between mb-2", children: [
|
|
3470
|
+
/* @__PURE__ */ jsxs11("span", { className: "text-[10px] font-bold uppercase tracking-widest text-slate-400 flex items-center gap-2", children: [
|
|
3471
|
+
/* @__PURE__ */ jsx13("span", { className: "w-2 h-2 rounded-full bg-emerald-500 animate-pulse" }),
|
|
3472
|
+
"UP SDK Debug Console"
|
|
3473
|
+
] }),
|
|
3474
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex gap-4", children: [
|
|
3475
|
+
/* @__PURE__ */ jsx13(
|
|
3476
|
+
"button",
|
|
3477
|
+
{
|
|
3478
|
+
className: "text-[10px] text-slate-500 font-bold uppercase hover:text-slate-300 transition-colors",
|
|
3479
|
+
onClick: clearLogs,
|
|
3480
|
+
type: "button",
|
|
3481
|
+
children: "Clear"
|
|
3482
|
+
}
|
|
3483
|
+
),
|
|
3484
|
+
/* @__PURE__ */ jsx13(
|
|
3485
|
+
"button",
|
|
3486
|
+
{
|
|
3487
|
+
className: "text-[10px] text-pink-500 font-bold uppercase hover:underline transition-colors",
|
|
3488
|
+
onClick: () => {
|
|
3489
|
+
setShowConsole(false);
|
|
3490
|
+
if (onClose) onClose();
|
|
3491
|
+
},
|
|
3492
|
+
type: "button",
|
|
3493
|
+
children: "Close"
|
|
3494
|
+
}
|
|
3495
|
+
)
|
|
3496
|
+
] })
|
|
3497
|
+
] }),
|
|
3498
|
+
/* @__PURE__ */ jsx13(
|
|
3499
|
+
"div",
|
|
3500
|
+
{
|
|
3501
|
+
className: "bg-black/80 rounded-xl p-3 font-mono text-[10px] text-emerald-400 min-h-[120px] max-h-[240px] overflow-y-auto border border-slate-900 custom-scrollbar",
|
|
3502
|
+
ref: scrollRef,
|
|
3503
|
+
children: logs.length === 0 ? /* @__PURE__ */ jsx13("div", { className: "text-slate-600 italic text-center py-8", children: "No logs yet. Interacting with the SDK will show events here." }) : logs.map((log) => /* @__PURE__ */ jsxs11(
|
|
3504
|
+
"div",
|
|
3505
|
+
{
|
|
3506
|
+
className: "mb-3 pb-3 border-b border-slate-900/50 last:border-0 last:mb-0 last:pb-0",
|
|
3507
|
+
children: [
|
|
3508
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex gap-2 text-slate-500 mb-1 items-center", children: [
|
|
3509
|
+
/* @__PURE__ */ jsx13("span", { className: "text-[8px]", children: log.time }),
|
|
3510
|
+
/* @__PURE__ */ jsx13("span", { className: "font-bold text-white uppercase text-[8px] bg-slate-800 px-1.5 py-0.5 rounded leading-none", children: log.label })
|
|
3511
|
+
] }),
|
|
3512
|
+
/* @__PURE__ */ jsx13("pre", { className: "whitespace-pre-wrap break-all opacity-90", children: log.content })
|
|
3513
|
+
]
|
|
3514
|
+
},
|
|
3515
|
+
log.id
|
|
3516
|
+
))
|
|
3517
|
+
}
|
|
3518
|
+
)
|
|
3519
|
+
] });
|
|
3520
|
+
};
|
|
3521
|
+
|
|
3522
|
+
// src/components/SDKProvider.tsx
|
|
3523
|
+
import { createContext as createContext2, useContext as useContext5, useMemo as useMemo6 } from "react";
|
|
3524
|
+
import { Fragment as Fragment5, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3525
|
+
var SDKContext = createContext2({ isMocked: false });
|
|
3526
|
+
var useIsMocked = () => useContext5(SDKContext).isMocked;
|
|
3527
|
+
var SDKProvider = ({
|
|
3528
|
+
children,
|
|
3529
|
+
debug = false,
|
|
3530
|
+
...mockerProps
|
|
3531
|
+
}) => {
|
|
3532
|
+
const isMocked = typeof window !== "undefined" && !(window.__UP_REACT_NATIVE_APP__ === true || !!window.ReactNativeWebView);
|
|
3533
|
+
const value = useMemo6(() => ({ isMocked }), [isMocked]);
|
|
3534
|
+
const content = /* @__PURE__ */ jsxs12(Fragment5, { children: [
|
|
3535
|
+
children,
|
|
3536
|
+
debug && /* @__PURE__ */ jsx14(DebugConsole, {})
|
|
3537
|
+
] });
|
|
3538
|
+
if (isMocked) {
|
|
3539
|
+
return /* @__PURE__ */ jsx14(SDKContext.Provider, { value, children: /* @__PURE__ */ jsx14(BridgeMocker, { ...mockerProps, children: content }) });
|
|
3540
|
+
}
|
|
3541
|
+
return /* @__PURE__ */ jsx14(SDKContext.Provider, { value, children: content });
|
|
3542
|
+
};
|
|
368
3543
|
|
|
369
3544
|
// src/index.ts
|
|
370
|
-
var
|
|
371
|
-
|
|
372
|
-
|
|
3545
|
+
var src_default = sdk;
|
|
3546
|
+
export {
|
|
3547
|
+
BridgeMocker,
|
|
3548
|
+
DebugConsole,
|
|
3549
|
+
GeolocationEvent,
|
|
3550
|
+
GlobalEvent,
|
|
3551
|
+
Map2 as Map,
|
|
3552
|
+
MapControls,
|
|
3553
|
+
MapControlsContext,
|
|
3554
|
+
MapMenu,
|
|
3555
|
+
MapMenuDivider,
|
|
3556
|
+
MapMenuFooter,
|
|
3557
|
+
MapMenuItem,
|
|
3558
|
+
MapMenuTitle,
|
|
373
3559
|
MiniAppSDK,
|
|
3560
|
+
RouteLayer,
|
|
3561
|
+
SDKMapProvider,
|
|
3562
|
+
SDKProvider,
|
|
374
3563
|
UIEvent,
|
|
375
|
-
|
|
376
|
-
|
|
3564
|
+
src_default as default,
|
|
3565
|
+
handleClearRoute,
|
|
3566
|
+
handleDrawRoute,
|
|
3567
|
+
handleRouteRequest,
|
|
3568
|
+
renderMap,
|
|
3569
|
+
sdk,
|
|
3570
|
+
useIsMocked,
|
|
3571
|
+
useLiveRouting,
|
|
3572
|
+
useMapControls,
|
|
3573
|
+
useRoute,
|
|
3574
|
+
useTrimmedRoute,
|
|
3575
|
+
useUserLocation
|
|
3576
|
+
};
|
|
377
3577
|
//# sourceMappingURL=index.js.map
|