adt-js-components 1.9.3 → 1.9.5

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/Map/index.js +162 -66
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adt-js-components",
3
- "version": "1.9.3",
3
+ "version": "1.9.5",
4
4
  "description": "JavaScript components for Nette framework",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/src/Map/index.js CHANGED
@@ -7,6 +7,7 @@ import {RouteSetting, defaultRouteSettings} from './route.types.js';
7
7
 
8
8
  let siteKey;
9
9
  let markerImg;
10
+ const svgCache = new Map();
10
11
  const mapInstances = new WeakMap();
11
12
  const selectedMarkers = new WeakMap();
12
13
  const selectionOrder = new WeakMap();
@@ -16,6 +17,8 @@ const routeSettingsMap = new WeakMap();
16
17
  const markersDataMap = new WeakMap();
17
18
  const markerClusters = new WeakMap();
18
19
  const onSelectionChangeMap = new WeakMap();
20
+ const onBeforeRouteCalculationMap = new WeakMap();
21
+ const onAfterRouteCalculationMap = new WeakMap();
19
22
 
20
23
  const DEPOT_TYPE = {
21
24
  START: 'depot-start',
@@ -40,6 +43,8 @@ async function run(options) {
40
43
  customMarkers = {},
41
44
  showSelectionOrder = false,
42
45
  markerInfoCallback = null,
46
+ onBeforeRouteCalculation = null,
47
+ onAfterRouteCalculation = null,
43
48
  } = JSON.parse(el.dataset.adtMap);
44
49
 
45
50
  /** @type {RouteSetting} */
@@ -63,6 +68,8 @@ async function run(options) {
63
68
  routeSettingsMap.set(map, routeSettings);
64
69
  markersDataMap.set(map, markers);
65
70
  onSelectionChangeMap.set(map, onSelectionChange);
71
+ onBeforeRouteCalculationMap.set(map, onBeforeRouteCalculation);
72
+ onAfterRouteCalculationMap.set(map, onAfterRouteCalculation);
66
73
 
67
74
  L.tileLayer("https://api.mapy.cz/v1/maptiles/basic/256/{z}/{x}/{y}?apikey=" + siteKey, {
68
75
  minZoom: 0,
@@ -157,9 +164,7 @@ async function run(options) {
157
164
  }
158
165
 
159
166
  if (routeSettings.enabled) {
160
- setTimeout(() => {
161
- calculateRoute(map);
162
- }, 500);
167
+ calculateRoute(map);
163
168
  }
164
169
 
165
170
  if (callback) {
@@ -175,11 +180,10 @@ async function run(options) {
175
180
  if (mutation.type === "childList") {
176
181
  mutation.addedNodes.forEach(node => {
177
182
  if (node.nodeType === 1) {
178
- // Zkontroluj samotný node
179
183
  if (node.hasAttribute("data-adt-map")) {
180
184
  applyEventHandlers(node);
181
185
  }
182
- // Zkontroluj všechny potomky
186
+
183
187
  node.querySelectorAll?.('[data-adt-map]').forEach(el => {
184
188
  applyEventHandlers(el);
185
189
  });
@@ -344,9 +348,7 @@ function addMarkers(map, markers, options, selectedOptions, position, selectable
344
348
  map.addLayer(cluster);
345
349
 
346
350
  if (loadedCount === totalMarkers) {
347
- setTimeout(() => {
348
- checkAndApplyPreselection(map, markers, showSelectionOrder, onSelectionChange, selectable);
349
- }, 200);
351
+ checkAndApplyPreselection(map, markers, showSelectionOrder, onSelectionChange, selectable);
350
352
  }
351
353
  }
352
354
 
@@ -371,7 +373,7 @@ function createMarker(marker, options, selectedOptions, cluster = null, selectab
371
373
  const markers = markerInstances.get(map);
372
374
  markers.set(marker.id, mapMarker);
373
375
 
374
- mapMarker.on('add', function() {
376
+ mapMarker.on('add', function () {
375
377
  const el = mapMarker.getElement();
376
378
  if (el) {
377
379
  el.style.cursor = 'pointer';
@@ -512,22 +514,29 @@ async function updateMarkerOrderDisplay(map, marker, orderNumber, isSelected, co
512
514
 
513
515
  async function createMarkerIcon(map, iconUrl, orderNumber, color = null) {
514
516
  let inlineStyle = null;
515
- const response = await fetch(iconUrl);
516
- const svg = await response.text();
517
517
  const settings = routeSettingsMap.get(map);
518
518
 
519
519
  if ((settings && settings.enabled) || color) {
520
520
  inlineStyle = `--marker-fill: ${color ?? settings.color};`;
521
521
  }
522
522
 
523
+ let svg;
524
+ if (svgCache.has(iconUrl)) {
525
+ svg = svgCache.get(iconUrl);
526
+ } else {
527
+ const response = await fetch(iconUrl);
528
+ svg = await response.text();
529
+ svgCache.set(iconUrl, svg);
530
+ }
531
+
523
532
  return L.divIcon({
524
533
  className: 'marker-icon-wrapper',
525
534
  html: `
526
- <div class="marker-icon" style="${inlineStyle}">
527
- ${svg}
528
- ${orderNumber ? `<span class="selection-order-label">${orderNumber}</span>` : ''}
529
- </div>
530
- `,
535
+ <div class="marker-icon" style="${inlineStyle}">
536
+ ${svg}
537
+ ${orderNumber ? `<span class="selection-order-label">${orderNumber}</span>` : ''}
538
+ </div>
539
+ `,
531
540
  iconSize: [43, 58],
532
541
  iconAnchor: [21, 58]
533
542
  });
@@ -581,68 +590,134 @@ async function calculateRoute(map) {
581
590
  return;
582
591
  }
583
592
 
584
- if (selectedSet && selectedSet.size > 0) {
585
- const orderedIds = Array.from(order.entries())
586
- .sort((a, b) => a[1] - b[1])
587
- .map(entry => entry[0]);
588
- const routeMarkers = orderedIds
589
- .map(id => markers.find(m => m.id === id))
590
- .filter(m => m !== undefined);
593
+ const hasCustomStart = routeSettings.startPoint !== null && routeSettings.startPoint !== undefined;
594
+ const hasCustomEnd = routeSettings.endPoint !== null && routeSettings.endPoint !== undefined;
591
595
 
592
- if (routeMarkers.length < 2) {
593
- return;
594
- }
596
+ if (hasCustomStart) addDepotMarker(map, routeSettings.startPoint, DEPOT_TYPE.START);
597
+ if (hasCustomEnd) addDepotMarker(map, routeSettings.endPoint, DEPOT_TYPE.END);
595
598
 
596
- let startPoint = routeMarkers[0].position;
597
- const hasCustomStart = routeSettings.startPoint !== null && routeSettings.startPoint !== undefined;
598
- if (hasCustomStart) {
599
- startPoint = routeSettings.startPoint;
600
- }
599
+ if (!selectedSet || selectedSet.size === 0) return;
601
600
 
602
- let endPoint = routeMarkers[routeMarkers.length - 1].position;
603
- const hasCustomEnd = routeSettings.endPoint !== null && routeSettings.endPoint !== undefined;
604
- if (hasCustomEnd) {
605
- endPoint = routeSettings.endPoint;
606
- }
601
+ const orderedIds = Array.from(order.entries())
602
+ .sort((a, b) => a[1] - b[1])
603
+ .map(entry => entry[0]);
604
+ const routeMarkers = orderedIds
605
+ .map(id => markers.find(m => m.id === id))
606
+ .filter(m => m !== undefined);
607
607
 
608
- if (hasCustomStart) {
609
- addDepotMarker(map, startPoint, DEPOT_TYPE.START);
610
- }
611
- if (hasCustomEnd) {
612
- addDepotMarker(map, endPoint, DEPOT_TYPE.END);
613
- }
608
+ if (routeMarkers.length < 2) return;
614
609
 
615
- const params = new URLSearchParams({
616
- start: `${startPoint.lon},${startPoint.lat}`,
617
- end: `${endPoint.lon},${endPoint.lat}`,
618
- routeType: routeSettings.routeType,
619
- apikey: siteKey
620
- });
610
+ const beforeCallback = onBeforeRouteCalculationMap.get(map);
611
+ if (beforeCallback && window[beforeCallback]) {
612
+ window[beforeCallback]();
613
+ }
621
614
 
622
- routeMarkers.forEach(m => {
623
- params.append('waypoints', `${m.position.lon},${m.position.lat}`);
624
- });
615
+ let startPoint = hasCustomStart ? routeSettings.startPoint : routeMarkers[0].position;
616
+ let endPoint = hasCustomEnd ? routeSettings.endPoint : routeMarkers[routeMarkers.length - 1].position;
617
+
618
+ const WAYPOINTS_LIMIT = 15;
619
+ const allCoords = [];
620
+ const allParts = [];
621
+ let totalDuration = 0;
622
+ let totalLength = 0;
623
+
624
+ try {
625
+ if (routeMarkers.length <= WAYPOINTS_LIMIT) {
626
+ const params = new URLSearchParams({
627
+ start: `${startPoint.lon},${startPoint.lat}`,
628
+ end: `${endPoint.lon},${endPoint.lat}`,
629
+ routeType: routeSettings.routeType,
630
+ apikey: siteKey
631
+ });
632
+ routeMarkers.forEach(m => params.append('waypoints', `${m.position.lon},${m.position.lat}`));
625
633
 
626
- try {
627
- const response = await fetch(
628
- `https://api.mapy.cz/v1/routing/route?${params.toString()}`
629
- );
634
+ const response = await fetch(`https://api.mapy.cz/v1/routing/route?${params.toString()}`);
630
635
  const data = await response.json();
631
636
 
632
637
  if (data.geometry?.geometry?.coordinates) {
633
- const coords = data.geometry.geometry.coordinates.map(c => [c[1], c[0]]);
634
- const polyline = L.polyline(coords, {
635
- color: routeSettings.color,
636
- weight: routeSettings.weight,
637
- opacity: routeSettings.opacity
638
- }).addTo(map);
639
-
640
- routePolylines.set(map, polyline);
638
+ allCoords.push(...data.geometry.geometry.coordinates.map(c => [c[1], c[0]]));
639
+ }
640
+ if (data.parts) {
641
+ allParts.push(...data.parts);
642
+ }
643
+ if (data.duration) {
644
+ totalDuration += data.duration;
645
+ }
646
+ if (data.length) {
647
+ totalLength += data.length;
648
+ }
649
+ } else {
650
+ const chunks = [];
651
+ for (let i = 0; i < routeMarkers.length; i += WAYPOINTS_LIMIT) {
652
+ chunks.push(routeMarkers.slice(i, i + WAYPOINTS_LIMIT));
653
+ }
654
+
655
+ for (let i = 0; i < chunks.length; i++) {
656
+ const chunk = chunks[i];
657
+ const isFirstChunk = i === 0;
658
+ const isLastChunk = i === chunks.length - 1;
659
+
660
+ const chunkStart = isFirstChunk ? startPoint : chunks[i - 1][chunks[i - 1].length - 1].position;
661
+ const chunkEnd = isLastChunk ? endPoint : chunk[chunk.length - 1].position;
662
+
663
+ const params = new URLSearchParams({
664
+ start: `${chunkStart.lon},${chunkStart.lat}`,
665
+ end: `${chunkEnd.lon},${chunkEnd.lat}`,
666
+ routeType: routeSettings.routeType,
667
+ apikey: siteKey
668
+ });
669
+
670
+ const markersToAdd = isFirstChunk ? chunk : chunk.slice(1);
671
+ markersToAdd.forEach(m => params.append('waypoints', `${m.position.lon},${m.position.lat}`));
672
+
673
+ const response = await fetch(`https://api.mapy.cz/v1/routing/route?${params.toString()}`);
674
+ const data = await response.json();
675
+
676
+ if (data.geometry?.geometry?.coordinates) {
677
+ allCoords.push(...data.geometry.geometry.coordinates.map(c => [c[1], c[0]]));
678
+ }
679
+ if (data.parts) {
680
+ allParts.push(...data.parts);
681
+ }
682
+ if (data.duration) {
683
+ totalDuration += data.duration;
684
+ }
685
+ if (data.length) {
686
+ totalLength += data.length;
687
+ }
641
688
  }
642
- } catch (error) {
643
- console.error('Error calculating route:', error);
644
689
  }
690
+ } catch (error) {
691
+ console.error('Error calculating route:', error);
692
+ const afterCallback = onAfterRouteCalculationMap.get(map);
693
+ if (afterCallback && window[afterCallback]) window[afterCallback]({}, 0, 0);
694
+
695
+ return;
696
+ }
697
+
698
+ if (allCoords.length > 0) {
699
+ const polyline = L.polyline(allCoords, {
700
+ color: routeSettings.color,
701
+ weight: routeSettings.weight,
702
+ opacity: routeSettings.opacity
703
+ }).addTo(map);
704
+ routePolylines.set(map, polyline);
645
705
  }
706
+
707
+ // @type {TRouteParts}
708
+ const routeParts = {};
709
+ allParts.forEach((part, index) => {
710
+ if (index < orderedIds.length) {
711
+ const markerId = orderedIds[index];
712
+ routeParts[markerId] = {
713
+ duration: part.duration,
714
+ length: part.length,
715
+ };
716
+ }
717
+ });
718
+
719
+ const afterCallback = onAfterRouteCalculationMap.get(map);
720
+ if (afterCallback && window[afterCallback]) window[afterCallback](routeParts, totalLength, totalDuration);
646
721
  }
647
722
 
648
723
  function addDepotMarker(map, position, depotType) {
@@ -659,7 +734,7 @@ function addDepotMarker(map, position, depotType) {
659
734
  iconAnchor: [img.width / 2, img.height]
660
735
  });
661
736
 
662
- L.marker(position, { icon: icon }).addTo(map);
737
+ L.marker(position, {icon: icon}).addTo(map);
663
738
  };
664
739
  }
665
740
 
@@ -740,10 +815,31 @@ function setOrder(mapElement, markerId, orderNumber) {
740
815
  updateMarkerOrderDisplay(mapElement, marker, orderNumber, true, color);
741
816
  }
742
817
 
818
+ function onBeforeRouteCalculation(mapElement, callbackName) {
819
+ const map = mapInstances.get(mapElement);
820
+ if (!map) return;
821
+ onBeforeRouteCalculationMap.set(map, callbackName);
822
+ }
823
+
824
+ function onAfterRouteCalculation(mapElement, callbackName) {
825
+ const map = mapInstances.get(mapElement);
826
+ if (!map) return;
827
+ onAfterRouteCalculationMap.set(map, callbackName);
828
+ }
829
+
830
+ function recalculateRoute(mapElement) {
831
+ const map = mapInstances.get(mapElement);
832
+ if (!map) return;
833
+ calculateRoute(map);
834
+ }
835
+
743
836
  export default {
744
837
  run,
745
838
  getSelectedMarkers,
746
839
  clearSelection,
747
840
  toggleMarker,
748
841
  setOrder,
842
+ onBeforeRouteCalculation,
843
+ onAfterRouteCalculation,
844
+ recalculateRoute,
749
845
  }