@biodiv-platform/naksha-maplibre-draw 5.0.6 → 5.0.8

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/dist/index.js CHANGED
@@ -49,10 +49,14 @@ module.exports = __toCommonJS(src_exports);
49
49
 
50
50
  // src/components/map.tsx
51
51
  var import_maplibre_gl2 = __toESM(require("maplibre-gl"));
52
+ var import_react3 = require("react");
52
53
 
53
54
  // src/components/setup-draw.tsx
54
55
  var import_terra_draw = require("terra-draw");
55
56
  var import_terra_draw_maplibre_gl_adapter = require("terra-draw-maplibre-gl-adapter");
57
+ var import_twind = require("twind");
58
+ var import_react = __toESM(require("react"));
59
+ var import_jsx_runtime = require("react/jsx-runtime");
56
60
  function setupDraw(map) {
57
61
  return new import_terra_draw.TerraDraw({
58
62
  tracked: true,
@@ -143,9 +147,50 @@ function setupDraw(map) {
143
147
  ]
144
148
  });
145
149
  }
146
-
147
- // src/components/map.tsx
148
- var import_react2 = require("react");
150
+ var ensureModeProperty = (features) => {
151
+ return features.map((feature) => {
152
+ var _a, _b;
153
+ if ((_a = feature.properties) == null ? void 0 : _a.mode)
154
+ return feature;
155
+ const type = (_b = feature.geometry) == null ? void 0 : _b.type;
156
+ const mode = type === "Point" ? "point" : type === "Polygon" ? "polygon" : type === "LineString" ? "linestring" : "unknown";
157
+ return __spreadProps(__spreadValues({}, feature), {
158
+ properties: __spreadProps(__spreadValues({}, feature.properties), { mode })
159
+ });
160
+ });
161
+ };
162
+ var IconButton = import_react.default.forwardRef(
163
+ (props, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", __spreadValues({
164
+ ref,
165
+ type: "button",
166
+ className: import_twind.tw`h-8 px-3 py-2 rounded-md focus:outline-none cursor-pointer text-md flex align-middle items-center gap-2 bg-gray-100 hover:bg-gray-200 text-gray-700 disabled:cursor-not-allowed disabled:bg-gray-100! disabled:opacity-50`
167
+ }, props))
168
+ );
169
+ var MapStyleSwitcher = ({
170
+ currentStyleIdx,
171
+ onStyleChange,
172
+ mapStyles
173
+ }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
174
+ style: {
175
+ position: "absolute",
176
+ left: 12,
177
+ bottom: 12,
178
+ background: "rgba(255,255,255,0.95)",
179
+ padding: "8px",
180
+ borderRadius: "6px",
181
+ zIndex: 1e3,
182
+ boxShadow: "0 2px 4px rgba(0,0,0,0.2)"
183
+ },
184
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", {
185
+ value: currentStyleIdx,
186
+ onChange: (e) => onStyleChange(Number(e.target.value)),
187
+ style: { fontSize: 14, padding: "4px" },
188
+ children: mapStyles.map((style, idx) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
189
+ value: idx,
190
+ children: style.text || `Style ${idx + 1}`
191
+ }, style.key || idx))
192
+ })
193
+ });
149
194
 
150
195
  // src/components/setup-maplibre.tsx
151
196
  var import_maplibre_gl = __toESM(require("maplibre-gl"));
@@ -174,9 +219,98 @@ function setupMaplibreMap({
174
219
  });
175
220
  }
176
221
 
222
+ // src/components/map-buttons/map-buttons.tsx
223
+ var import_react2 = require("react");
224
+
177
225
  // src/components/icon-buttons/icons.tsx
178
- var import_jsx_runtime = require("react/jsx-runtime");
179
- var SelectIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", {
226
+ var import_jsx_runtime2 = require("react/jsx-runtime");
227
+ var PencilOpenIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", {
228
+ xmlns: "http://www.w3.org/2000/svg",
229
+ width: "24",
230
+ height: "24",
231
+ viewBox: "0 0 24 24",
232
+ fill: "none",
233
+ stroke: "currentColor",
234
+ strokeWidth: "1.5",
235
+ strokeLinecap: "round",
236
+ strokeLinejoin: "round",
237
+ children: [
238
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", {
239
+ d: "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"
240
+ }),
241
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", {
242
+ d: "m15 5 4 4"
243
+ })
244
+ ]
245
+ });
246
+ var PencilCloseIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", {
247
+ xmlns: "http://www.w3.org/2000/svg",
248
+ width: "24",
249
+ height: "24",
250
+ viewBox: "0 0 24 24",
251
+ fill: "none",
252
+ stroke: "currentColor",
253
+ strokeWidth: "1.5",
254
+ strokeLinecap: "round",
255
+ strokeLinejoin: "round",
256
+ children: [
257
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", {
258
+ d: "m10 10-6.157 6.162a2 2 0 0 0-.5.833l-1.322 4.36a.5.5 0 0 0 .622.624l4.358-1.323a2 2 0 0 0 .83-.5L14 13.982"
259
+ }),
260
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", {
261
+ d: "m12.829 7.172 4.359-4.346a1 1 0 1 1 3.986 3.986l-4.353 4.353"
262
+ }),
263
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", {
264
+ d: "m15 5 4 4"
265
+ }),
266
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", {
267
+ d: "m2 2 20 20"
268
+ })
269
+ ]
270
+ });
271
+ var LocateIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", {
272
+ xmlns: "http://www.w3.org/2000/svg",
273
+ width: "24",
274
+ height: "24",
275
+ viewBox: "0 0 24 24",
276
+ fill: "none",
277
+ stroke: "currentColor",
278
+ strokeWidth: "1.5",
279
+ strokeLinecap: "round",
280
+ strokeLinejoin: "round",
281
+ children: [
282
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", {
283
+ x1: "2",
284
+ x2: "5",
285
+ y1: "12",
286
+ y2: "12"
287
+ }),
288
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", {
289
+ x1: "19",
290
+ x2: "22",
291
+ y1: "12",
292
+ y2: "12"
293
+ }),
294
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", {
295
+ x1: "12",
296
+ x2: "12",
297
+ y1: "2",
298
+ y2: "5"
299
+ }),
300
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", {
301
+ x1: "12",
302
+ x2: "12",
303
+ y1: "19",
304
+ y2: "22"
305
+ }),
306
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", {
307
+ cx: "12",
308
+ cy: "12",
309
+ r: "7"
310
+ })
311
+ ]
312
+ });
313
+ var SelectIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", {
180
314
  xmlns: "http://www.w3.org/2000/svg",
181
315
  width: "24",
182
316
  height: "24",
@@ -187,15 +321,15 @@ var SelectIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", {
187
321
  strokeLinecap: "round",
188
322
  strokeLinejoin: "round",
189
323
  children: [
190
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
324
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", {
191
325
  d: "M12.586 12.586 19 19"
192
326
  }),
193
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
327
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", {
194
328
  d: "M3.688 3.037a.497.497 0 0 0-.651.651l6.5 15.999a.501.501 0 0 0 .947-.062l1.569-6.083a2 2 0 0 1 1.448-1.479l6.124-1.579a.5.5 0 0 0 .063-.947z"
195
329
  })
196
330
  ]
197
331
  });
198
- var PointIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", {
332
+ var PointIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", {
199
333
  xmlns: "http://www.w3.org/2000/svg",
200
334
  width: "24",
201
335
  height: "24",
@@ -206,150 +340,143 @@ var PointIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", {
206
340
  strokeLinecap: "round",
207
341
  strokeLinejoin: "round",
208
342
  children: [
209
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
343
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", {
210
344
  d: "M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"
211
345
  }),
212
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", {
346
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", {
213
347
  cx: "12",
214
348
  cy: "10",
215
349
  r: "3"
216
350
  })
217
351
  ]
218
352
  });
219
- var LineIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
353
+ var LineIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", {
220
354
  width: "16",
221
355
  height: "16",
222
356
  viewBox: "0 0 24 24",
223
357
  fill: "none",
224
358
  stroke: "currentColor",
225
359
  strokeWidth: "2",
226
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", {
360
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", {
227
361
  x1: "4",
228
362
  y1: "20",
229
363
  x2: "20",
230
364
  y2: "4"
231
365
  })
232
366
  });
233
- var PolygonIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
367
+ var PolygonIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", {
234
368
  width: "16",
235
369
  height: "16",
236
370
  viewBox: "0 0 24 24",
237
371
  fill: "none",
238
372
  stroke: "currentColor",
239
373
  strokeWidth: "2",
240
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polygon", {
374
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polygon", {
241
375
  points: "12,2 22,8 18,22 6,22 2,8"
242
376
  })
243
377
  });
244
- var DeleteIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
378
+ var RectangleIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", {
379
+ width: "16",
380
+ height: "16",
381
+ viewBox: "0 0 24 24",
382
+ fill: "none",
383
+ stroke: "currentColor",
384
+ strokeWidth: "2",
385
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", {
386
+ x: "4",
387
+ y: "4",
388
+ width: "16",
389
+ height: "16"
390
+ })
391
+ });
392
+ var CircleIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", {
393
+ width: "16",
394
+ height: "16",
395
+ viewBox: "0 0 24 24",
396
+ fill: "none",
397
+ stroke: "currentColor",
398
+ strokeWidth: "2",
399
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", {
400
+ cx: "12",
401
+ cy: "12",
402
+ r: "10"
403
+ })
404
+ });
405
+ var FreeHandIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", {
245
406
  width: "16",
246
407
  height: "16",
247
408
  viewBox: "0 0 24 24",
248
409
  fill: "none",
249
410
  stroke: "currentColor",
250
411
  strokeWidth: "2",
251
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
412
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", {
413
+ d: "M3 16c1-3 4-5 7-4 3 1 5 3 7 2 2-1 3-4 1-6-1-2-4-2-6-1"
414
+ })
415
+ });
416
+ var DeleteIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", {
417
+ width: "16",
418
+ height: "16",
419
+ viewBox: "0 0 24 24",
420
+ fill: "none",
421
+ stroke: "currentColor",
422
+ strokeWidth: "2",
423
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", {
252
424
  d: "M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"
253
425
  })
254
426
  });
255
427
 
256
- // src/components/map-button/MapButton.tsx
257
- var import_jsx_runtime2 = require("react/jsx-runtime");
428
+ // src/components/map-button/map-button.tsx
429
+ var import_jsx_runtime3 = require("react/jsx-runtime");
430
+ var BUTTON_STYLE = {
431
+ display: "flex",
432
+ justifyContent: "center",
433
+ alignItems: "center",
434
+ width: 30,
435
+ height: 30,
436
+ padding: 0,
437
+ borderRadius: 4
438
+ };
258
439
  var MapButton = ({
259
440
  mode,
260
441
  currentMode,
261
- changeMode,
442
+ onClick,
262
443
  icon,
263
- tooltip
264
- }) => {
265
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
266
- onClick: () => changeMode(mode),
267
- title: tooltip,
268
- style: {
269
- display: "flex",
270
- justifyContent: "center",
271
- alignItems: "center",
272
- width: "30px",
273
- height: "30px",
274
- padding: "0",
275
- borderRadius: "4px",
276
- backgroundColor: currentMode === mode ? "#02cf87" : "#f0f0f0",
277
- cursor: "pointer"
278
- },
279
- children: icon
280
- });
281
- };
282
- var MapButton_default = MapButton;
283
-
284
- // src/components/map-buttons/MapButtons.tsx
285
- var import_jsx_runtime3 = require("react/jsx-runtime");
286
- var MapButtons = ({
287
- mode,
288
- changeMode
289
- }) => {
290
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", {
291
- style: {
292
- position: "absolute",
293
- top: "10px",
294
- left: "10px",
295
- display: "flex",
296
- flexDirection: "column",
297
- gap: "6px",
298
- zIndex: 1e3,
299
- backgroundColor: "rgba(255, 255, 255, 0.9)",
300
- padding: "8px",
301
- borderRadius: "6px",
302
- boxShadow: "0 2px 6px rgba(0,0,0,0.2)"
303
- },
304
- children: [
305
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MapButton_default, {
306
- mode: "select",
307
- icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SelectIcon, {}),
308
- currentMode: mode,
309
- changeMode,
310
- tooltip: "Select"
311
- }),
312
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MapButton_default, {
313
- mode: "point",
314
- icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PointIcon, {}),
315
- currentMode: mode,
316
- changeMode,
317
- tooltip: "Point"
318
- }),
319
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MapButton_default, {
320
- mode: "linestring",
321
- icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(LineIcon, {}),
322
- currentMode: mode,
323
- changeMode,
324
- tooltip: "Line"
325
- }),
326
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MapButton_default, {
327
- mode: "polygon",
328
- icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PolygonIcon, {}),
329
- currentMode: mode,
330
- changeMode,
331
- tooltip: "Polygon"
332
- })
333
- ]
334
- });
335
- };
336
- var MapButtons_default = MapButtons;
444
+ tooltip,
445
+ disabled
446
+ }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(IconButton, {
447
+ type: "button",
448
+ title: tooltip,
449
+ disabled,
450
+ onClick: () => !disabled && onClick(mode != null ? mode : ""),
451
+ style: __spreadProps(__spreadValues({}, BUTTON_STYLE), {
452
+ backgroundColor: disabled ? "#d3d3d3" : currentMode === mode ? "#02cf87" : "#f0f0f0",
453
+ cursor: disabled ? "not-allowed" : "pointer",
454
+ opacity: disabled ? 0.6 : 1
455
+ }),
456
+ children: icon
457
+ });
458
+ var map_button_default = MapButton;
337
459
 
338
- // src/components/icon-buttons/ClearButton.tsx
339
- var import_react = require("react");
460
+ // src/components/map-buttons/map-buttons.tsx
340
461
  var import_jsx_runtime4 = require("react/jsx-runtime");
341
- var ClearButton = ({
462
+ function MapButtons({
463
+ mode,
464
+ setMode,
465
+ onClick,
342
466
  draw,
343
- onFeaturesChange
344
- }) => {
345
- const [hasFeatures, setHasFeatures] = (0, import_react.useState)(false);
346
- (0, import_react.useEffect)(() => {
467
+ onFeaturesChange,
468
+ isMultiple = true,
469
+ autoFocus
470
+ }) {
471
+ const [expanded, setExpanded] = (0, import_react2.useState)(false);
472
+ const [hasFeatures, setHasFeatures] = (0, import_react2.useState)(false);
473
+ const updateHasFeatures = (0, import_react2.useCallback)(
474
+ () => setHasFeatures(draw.getSnapshot().length > 0),
475
+ [draw]
476
+ );
477
+ (0, import_react2.useEffect)(() => {
347
478
  if (!draw)
348
479
  return;
349
- const updateHasFeatures = () => {
350
- const snapshot = draw.getSnapshot();
351
- setHasFeatures(snapshot.length > 0);
352
- };
353
480
  draw.on("finish", updateHasFeatures);
354
481
  draw.on("change", updateHasFeatures);
355
482
  updateHasFeatures();
@@ -357,128 +484,242 @@ var ClearButton = ({
357
484
  draw.off("finish", updateHasFeatures);
358
485
  draw.off("change", updateHasFeatures);
359
486
  };
360
- }, [draw]);
361
- const handleClear = () => {
362
- if (!draw)
487
+ }, [draw, updateHasFeatures]);
488
+ const handleGeolocation = (0, import_react2.useCallback)(() => {
489
+ if (!("geolocation" in navigator)) {
490
+ alert("Geolocation is not supported in this browser.");
363
491
  return;
492
+ }
493
+ navigator.geolocation.getCurrentPosition(
494
+ (pos) => {
495
+ const coords = [
496
+ pos.coords.longitude,
497
+ pos.coords.latitude
498
+ ];
499
+ const pt = {
500
+ type: "Feature",
501
+ geometry: { type: "Point", coordinates: coords },
502
+ properties: { mode: "point", source: "geolocation" }
503
+ };
504
+ if (!isMultiple)
505
+ draw.clear();
506
+ draw.addFeatures([pt]);
507
+ onFeaturesChange == null ? void 0 : onFeaturesChange(draw.getSnapshot());
508
+ autoFocus == null ? void 0 : autoFocus([pt]);
509
+ },
510
+ (error) => {
511
+ alert("Error getting geolocation");
512
+ console.error(error);
513
+ }
514
+ );
515
+ setMode("current-location");
516
+ }, [draw, isMultiple, onFeaturesChange, autoFocus]);
517
+ const handleClear = (0, import_react2.useCallback)(() => {
364
518
  draw.clear();
365
519
  setHasFeatures(false);
366
- if (onFeaturesChange)
367
- onFeaturesChange([]);
368
- };
369
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", {
370
- onClick: handleClear,
371
- title: "Clear all features",
372
- disabled: !hasFeatures,
520
+ setMode("static");
521
+ onFeaturesChange == null ? void 0 : onFeaturesChange([]);
522
+ }, [draw, onFeaturesChange]);
523
+ const buttons = (0, import_react2.useMemo)(
524
+ () => [
525
+ {
526
+ icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(LocateIcon, {}),
527
+ mode: "current-location",
528
+ onClick: handleGeolocation,
529
+ tooltip: "Use current location"
530
+ },
531
+ {
532
+ icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SelectIcon, {}),
533
+ mode: "select",
534
+ onClick,
535
+ tooltip: "Select"
536
+ },
537
+ {
538
+ icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PointIcon, {}),
539
+ mode: "point",
540
+ onClick,
541
+ tooltip: "Point"
542
+ },
543
+ {
544
+ icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(LineIcon, {}),
545
+ mode: "linestring",
546
+ onClick,
547
+ tooltip: "Linestring"
548
+ },
549
+ {
550
+ icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PolygonIcon, {}),
551
+ mode: "polygon",
552
+ onClick,
553
+ tooltip: "Polygon"
554
+ },
555
+ {
556
+ icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RectangleIcon, {}),
557
+ mode: "rectangle",
558
+ onClick,
559
+ tooltip: "Reactangle"
560
+ },
561
+ {
562
+ icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CircleIcon, {}),
563
+ mode: "circle",
564
+ onClick,
565
+ tooltip: "Circle"
566
+ },
567
+ {
568
+ icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FreeHandIcon, {}),
569
+ mode: "freehand",
570
+ onClick,
571
+ tooltip: "Freehand"
572
+ },
573
+ {
574
+ icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DeleteIcon, {}),
575
+ mode: "clear",
576
+ onClick: handleClear,
577
+ tooltip: "Clear all features",
578
+ disabled: !hasFeatures
579
+ }
580
+ ],
581
+ [onClick, handleGeolocation, handleClear, hasFeatures]
582
+ );
583
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", {
373
584
  style: {
585
+ position: "absolute",
586
+ top: 10,
587
+ left: 10,
374
588
  display: "flex",
375
- justifyContent: "center",
376
- alignItems: "center",
377
- width: "30px",
378
- height: "30px",
379
- padding: "0",
380
- borderRadius: "4px",
381
- backgroundColor: hasFeatures ? "#f0f0f0" : "#e0e0e0",
382
- cursor: hasFeatures ? "pointer" : "not-allowed"
589
+ flexDirection: "column",
590
+ gap: 6,
591
+ zIndex: 1e3,
592
+ backgroundColor: "rgba(255,255,255,0.9)",
593
+ padding: 8,
594
+ borderRadius: 6,
595
+ boxShadow: "0 2px 6px rgba(0,0,0,0.2)"
383
596
  },
384
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DeleteIcon, {})
597
+ children: [
598
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(IconButton, {
599
+ onClick: () => setExpanded((v) => !v),
600
+ style: {
601
+ display: "flex",
602
+ justifyContent: "center",
603
+ alignItems: "center",
604
+ width: 30,
605
+ height: 30,
606
+ padding: 0,
607
+ borderRadius: 4,
608
+ backgroundColor: "#d3d3d3"
609
+ },
610
+ title: "Expand or collapse tools",
611
+ children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PencilCloseIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PencilOpenIcon, {})
612
+ }),
613
+ expanded && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", {
614
+ style: { display: "flex", flexDirection: "column", gap: 6 },
615
+ children: buttons.map((btn, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(map_button_default, {
616
+ icon: btn.icon,
617
+ mode: btn.mode,
618
+ currentMode: mode,
619
+ onClick: btn.onClick,
620
+ tooltip: btn.tooltip,
621
+ disabled: btn.disabled
622
+ }, i))
623
+ })
624
+ ]
385
625
  });
386
- };
387
- var ClearButton_default = ClearButton;
626
+ }
627
+ var map_buttons_default = MapButtons;
388
628
 
389
629
  // src/components/map.tsx
390
- var import_naksha_commons = require("@biodiv-platform/naksha-commons");
391
630
  var import_bbox = __toESM(require("@turf/bbox"));
631
+ var import_naksha_commons = require("@biodiv-platform/naksha-commons");
392
632
  var import_jsx_runtime5 = require("react/jsx-runtime");
393
- var ensureModeProperty = (features) => {
394
- return features.map((feature) => {
395
- var _a, _b;
396
- if ((_a = feature.properties) == null ? void 0 : _a.mode)
397
- return feature;
398
- const geometryType = (_b = feature.geometry) == null ? void 0 : _b.type;
399
- const mode = geometryType === "Point" ? "point" : geometryType === "Polygon" ? "polygon" : geometryType === "LineString" ? "linestring" : "unknown";
400
- return __spreadProps(__spreadValues({}, feature), {
401
- properties: __spreadProps(__spreadValues({}, feature.properties), { mode })
402
- });
403
- });
404
- };
405
633
  var Map = (props) => {
406
- const mapRef = (0, import_react2.useRef)(null);
407
- const [map, setMap] = (0, import_react2.useState)();
408
- const [mode, setMode] = (0, import_react2.useState)("static");
409
- const [draw, setDraw] = (0, import_react2.useState)();
410
- const mapStyle = import_naksha_commons.defaultMapStyles[(props == null ? void 0 : props.mapStyle) || 0].style;
411
- const { isMultiple = true } = props;
412
- (0, import_react2.useEffect)(() => {
413
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
634
+ var _a;
635
+ console.info("Rendering Naksha Maplibre Draw Map Component");
636
+ const mapRef = (0, import_react3.useRef)(null);
637
+ const [map, setMap] = (0, import_react3.useState)();
638
+ const [mode, setMode] = (0, import_react3.useState)("static");
639
+ const [draw, setDraw] = (0, import_react3.useState)();
640
+ const { isMultiple = true, features = [], onFeaturesChange } = props;
641
+ const [selectedMapStyleIdx, setSelectedMapStyleIdx] = (0, import_react3.useState)(
642
+ (props == null ? void 0 : props.mapStyle) || 0
643
+ );
644
+ const mapTile = props.mapStyles ? props.mapStyles : import_naksha_commons.defaultMapStyles;
645
+ const mapStyle = (_a = mapTile[selectedMapStyleIdx]) == null ? void 0 : _a.style;
646
+ const autoFocus = (0, import_react3.useCallback)(
647
+ (features2) => {
648
+ if (!(features2 == null ? void 0 : features2.length) || !map)
649
+ return;
650
+ const _bbox = (0, import_bbox.default)({ type: "FeatureCollection", features: features2 });
651
+ map.fitBounds(_bbox, {
652
+ padding: 40,
653
+ duration: 1e3,
654
+ maxZoom: 15
655
+ });
656
+ },
657
+ [map]
658
+ );
659
+ const mapStyleConfig = mapTile[selectedMapStyleIdx];
660
+ (0, import_react3.useEffect)(() => {
661
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
414
662
  const maplibreMap = setupMaplibreMap({
415
663
  containerId: "maplibre-map",
416
- latitude: (_b = (_a = props.defaultViewState) == null ? void 0 : _a.latitude) != null ? _b : 0,
664
+ latitude: (_b = (_a2 = props.defaultViewState) == null ? void 0 : _a2.latitude) != null ? _b : 0,
417
665
  longitude: (_d = (_c = props.defaultViewState) == null ? void 0 : _c.longitude) != null ? _d : 0,
418
666
  zoom: (_f = (_e = props.defaultViewState) == null ? void 0 : _e.zoom) != null ? _f : 0,
419
667
  pitch: (_h = (_g = props.defaultViewState) == null ? void 0 : _g.pitch) != null ? _h : 0,
420
668
  bearing: (_j = (_i = props.defaultViewState) == null ? void 0 : _i.bearing) != null ? _j : 0,
421
669
  mapstyle: mapStyle
422
670
  });
671
+ if ("maxZoom" in mapStyleConfig && mapStyleConfig.maxZoom) {
672
+ maplibreMap.setMaxZoom(mapStyleConfig.maxZoom);
673
+ }
423
674
  const navControl = new import_maplibre_gl2.default.NavigationControl({
424
675
  showCompass: true,
425
676
  showZoom: true,
426
677
  visualizePitch: true
427
678
  });
679
+ const fullscreen = new import_maplibre_gl2.default.FullscreenControl({});
428
680
  maplibreMap.addControl(navControl, "bottom-right");
681
+ maplibreMap.addControl(fullscreen, "bottom-right");
429
682
  maplibreMap.once("style.load", () => setMap(maplibreMap));
430
683
  return () => {
431
- if (navControl && maplibreMap) {
432
- maplibreMap.removeControl(navControl);
433
- }
684
+ maplibreMap.removeControl(navControl);
434
685
  };
435
- }, []);
436
- const autoFocus = (0, import_react2.useCallback)(
437
- (features) => {
438
- if (!(features == null ? void 0 : features.length) || !map)
439
- return;
440
- const _bbox = (0, import_bbox.default)({
441
- type: "FeatureCollection",
442
- features
443
- });
444
- map.fitBounds(_bbox, { padding: 40, duration: 1e3 });
445
- },
446
- [map]
686
+ }, [mapStyle, props.defaultViewState]);
687
+ const filterUserFeatures = (features2) => features2.filter(
688
+ (f) => f.geometry.type !== "Point" || f.properties.mode !== "static" && f.properties.mode !== "select"
447
689
  );
448
- (0, import_react2.useEffect)(() => {
449
- var _a;
690
+ (0, import_react3.useEffect)(() => {
450
691
  if (!map)
451
692
  return;
452
693
  const terraDraw = setupDraw(map);
453
694
  terraDraw.start();
454
695
  setDraw(terraDraw);
455
696
  const handleChange = () => {
456
- var _a2;
457
- const snapshot = terraDraw.getSnapshot();
458
- let filteredFeatures = snapshot;
459
- if (!isMultiple && snapshot.length > 1) {
460
- const latestFeature = snapshot[snapshot.length - 1];
461
- filteredFeatures = [latestFeature];
462
- const featuresToRemove = snapshot.slice(0, -1);
463
- const featureIds = featuresToRemove.map((f) => f.id).filter((id) => id !== void 0);
464
- if (featureIds.length > 0) {
465
- terraDraw.removeFeatures(featureIds);
697
+ let snapshot = terraDraw.getSnapshot();
698
+ let userFeatures = filterUserFeatures(snapshot);
699
+ if (!isMultiple && userFeatures.length > 1) {
700
+ const lastFeature = userFeatures.at(-1);
701
+ const toRemove = userFeatures.slice(0, -1).map((f) => f.id).filter((id) => Boolean(id));
702
+ if (toRemove.length > 0) {
703
+ try {
704
+ terraDraw.removeFeatures(toRemove);
705
+ } catch (e) {
706
+ console.warn("removeFeatures failed", e);
707
+ }
466
708
  }
709
+ userFeatures = [lastFeature];
467
710
  }
468
- (_a2 = props.onFeaturesChange) == null ? void 0 : _a2.call(props, snapshot);
469
- autoFocus(filteredFeatures);
711
+ onFeaturesChange == null ? void 0 : onFeaturesChange(userFeatures);
712
+ autoFocus(userFeatures);
713
+ setMode("static");
470
714
  };
471
715
  terraDraw.on("finish", handleChange);
472
- if ((_a = props.features) == null ? void 0 : _a.length) {
716
+ if (features.length) {
473
717
  setTimeout(() => {
474
718
  if (terraDraw.getSnapshot().length === 0) {
475
- const featuresWithMode = ensureModeProperty(props.features);
476
- let initialFeatures = featuresWithMode;
477
- if (!isMultiple && featuresWithMode.length > 1) {
478
- initialFeatures = [featuresWithMode[featuresWithMode.length - 1]];
479
- }
480
- terraDraw.addFeatures(initialFeatures);
481
- autoFocus(initialFeatures);
719
+ let withMode = ensureModeProperty(features);
720
+ let initial = !isMultiple && withMode.length > 1 ? [withMode.at(-1)] : withMode;
721
+ terraDraw.addFeatures(initial);
722
+ autoFocus(initial);
482
723
  }
483
724
  });
484
725
  }
@@ -486,45 +727,37 @@ var Map = (props) => {
486
727
  terraDraw.off("finish", handleChange);
487
728
  terraDraw.stop();
488
729
  };
489
- }, [map, isMultiple, autoFocus]);
490
- const changeMode = (0, import_react2.useCallback)(
730
+ }, [map, isMultiple, autoFocus, features, onFeaturesChange]);
731
+ const changeMode = (0, import_react3.useCallback)(
491
732
  (newMode) => {
492
733
  draw == null ? void 0 : draw.setMode(newMode);
493
734
  setMode(newMode);
494
735
  },
495
736
  [draw]
496
737
  );
497
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", {
738
+ const handleStyleChange = (idx) => {
739
+ setSelectedMapStyleIdx(idx);
740
+ };
741
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", {
498
742
  ref: mapRef,
499
743
  id: "maplibre-map",
500
- style: { width: "100%", height: "100%" },
501
- children: draw && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, {
502
- children: [
503
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MapButtons_default, {
504
- mode,
505
- changeMode
506
- }),
507
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", {
508
- style: {
509
- position: "absolute",
510
- top: "180px",
511
- left: "10px",
512
- display: "flex",
513
- flexDirection: "column",
514
- gap: "6px",
515
- zIndex: 1e3,
516
- backgroundColor: "rgba(255, 255, 255, 0.9)",
517
- padding: "8px",
518
- borderRadius: "6px",
519
- boxShadow: "0 2px 6px rgba(0,0,0,0.2)"
520
- },
521
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ClearButton_default, {
522
- draw,
523
- onFeaturesChange: props.onFeaturesChange
524
- })
525
- })
526
- ]
527
- })
744
+ style: { width: "100%", height: "100%", position: "relative" },
745
+ children: [
746
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MapStyleSwitcher, {
747
+ currentStyleIdx: selectedMapStyleIdx,
748
+ onStyleChange: handleStyleChange,
749
+ mapStyles: mapTile
750
+ }),
751
+ draw && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(map_buttons_default, {
752
+ mode,
753
+ setMode,
754
+ onClick: changeMode,
755
+ draw,
756
+ onFeaturesChange,
757
+ isMultiple,
758
+ autoFocus
759
+ })
760
+ ]
528
761
  });
529
762
  };
530
763
  var map_default = Map;