@premiate/strapi-plugin-maplibre-field 1.1.2 → 1.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.
Files changed (69) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/README.md +4 -4
  3. package/dist/_chunks/de-B0QBc7M2.js +27 -0
  4. package/dist/_chunks/de-BMK0skCB.mjs +27 -0
  5. package/dist/_chunks/en-C8iInpNv.mjs +27 -0
  6. package/dist/_chunks/en-CA0XtbN7.js +27 -0
  7. package/dist/_chunks/es-BU6JgkbZ.js +25 -0
  8. package/dist/_chunks/es-DZO4NHjt.mjs +25 -0
  9. package/dist/_chunks/fr-B8NDboqe.mjs +27 -0
  10. package/dist/_chunks/fr-DIBfzRbg.js +27 -0
  11. package/dist/_chunks/{index-DtFDFnhf.mjs → index-B0Nr95wz.mjs} +307 -306
  12. package/dist/_chunks/{index-Bih-3DHy.mjs → index-CBxydRKp.mjs} +14 -11
  13. package/dist/_chunks/{index-DG3FGfzf.js → index-CDY4Awnh.js} +13 -10
  14. package/dist/_chunks/{index-D9yb6SXN.js → index-ysvid6md.js} +312 -312
  15. package/dist/_chunks/it-B-lr3jhh.js +27 -0
  16. package/dist/_chunks/it-DFH6n6E1.mjs +27 -0
  17. package/dist/admin/de-B0QBc7M2.js +27 -0
  18. package/dist/admin/de-BMK0skCB.mjs +27 -0
  19. package/dist/admin/en-C8iInpNv.mjs +27 -0
  20. package/dist/admin/en-CA0XtbN7.js +27 -0
  21. package/dist/admin/es-BU6JgkbZ.js +25 -0
  22. package/dist/admin/es-DZO4NHjt.mjs +25 -0
  23. package/dist/admin/fr-B8NDboqe.mjs +27 -0
  24. package/dist/admin/fr-DIBfzRbg.js +27 -0
  25. package/dist/admin/index-B-3vqPUe.mjs +1836 -0
  26. package/dist/admin/index-B0LVK-NK.js +170 -0
  27. package/dist/admin/index-BaGweGhM.js +1856 -0
  28. package/dist/admin/index-DjL4deZ6.mjs +171 -0
  29. package/dist/admin/index.js +3 -2
  30. package/dist/admin/index.mjs +1 -1
  31. package/dist/admin/it-B-lr3jhh.js +27 -0
  32. package/dist/admin/it-DFH6n6E1.mjs +27 -0
  33. package/dist/server/index.js +11 -6
  34. package/dist/server/index.mjs +9 -5
  35. package/dist/server/src/index.d.ts +9 -4
  36. package/package.json +12 -10
  37. package/dist/_chunks/de-CGU2cyif.mjs +0 -16
  38. package/dist/_chunks/de-Dq_t3Z6M.js +0 -16
  39. package/dist/_chunks/en-BxxNWf9i.mjs +0 -16
  40. package/dist/_chunks/en-CgSPA-1L.js +0 -16
  41. package/dist/_chunks/es-B_cPv3G5.mjs +0 -16
  42. package/dist/_chunks/es-Sgja1XAa.js +0 -16
  43. package/dist/_chunks/fr-B3JIzyzo.js +0 -16
  44. package/dist/_chunks/fr-Dw5wEoDC.mjs +0 -16
  45. package/dist/_chunks/it-BgWDIXzn.js +0 -16
  46. package/dist/_chunks/it-CoUEVPt6.mjs +0 -16
  47. package/dist/admin/src/components/Initializer.d.ts +0 -6
  48. package/dist/admin/src/components/MapInput/basemap-control.d.ts +0 -8
  49. package/dist/admin/src/components/MapInput/credits-control.d.ts +0 -10
  50. package/dist/admin/src/components/MapInput/geocoder-control.d.ts +0 -20
  51. package/dist/admin/src/components/MapInput/index.d.ts +0 -19
  52. package/dist/admin/src/components/MapInput/layer-control.d.ts +0 -18
  53. package/dist/admin/src/components/PluginIcon.d.ts +0 -2
  54. package/dist/admin/src/hooks/usePluginConfig.d.ts +0 -2
  55. package/dist/admin/src/mutations/mutateEditViewHook.d.ts +0 -30
  56. package/dist/admin/src/services/poi-service.d.ts +0 -160
  57. package/dist/admin/src/utils/getTrad.d.ts +0 -2
  58. package/dist/admin/src/utils/pluginId.d.ts +0 -2
  59. package/dist/admin/src/utils/prefixPluginTranslations.d.ts +0 -3
  60. package/dist/server/src/bootstrap.d.ts +0 -2
  61. package/dist/server/src/config/index.d.ts +0 -62
  62. package/dist/server/src/config/schema.d.ts +0 -53
  63. package/dist/server/src/controllers/config.d.ts +0 -9
  64. package/dist/server/src/controllers/index.d.ts +0 -10
  65. package/dist/server/src/destroy.d.ts +0 -2
  66. package/dist/server/src/register.d.ts +0 -5
  67. package/dist/server/src/routes/admin.d.ts +0 -12
  68. package/dist/server/src/routes/index.d.ts +0 -14
  69. package/dist/server/src/types/config.d.ts +0 -26
@@ -3,13 +3,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  const React = require("react");
5
5
  const admin = require("@strapi/strapi/admin");
6
- const MapLibreGeocoder = require("@maplibre/maplibre-gl-geocoder");
7
- const maplibregl = require("maplibre-gl");
8
- require("@maplibre/maplibre-gl-geocoder/dist/maplibre-gl-geocoder.css");
9
- const Map = require("react-map-gl/maplibre");
10
6
  const designSystem = require("@strapi/design-system");
11
- const index = require("./index-DG3FGfzf.js");
7
+ const index = require("./index-CDY4Awnh.js");
8
+ const Map = require("@vis.gl/react-maplibre");
12
9
  const pmtiles = require("pmtiles");
10
+ const maplibregl = require("maplibre-gl");
13
11
  require("maplibre-gl/dist/maplibre-gl.css");
14
12
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
15
13
  function _interopNamespace(e) {
@@ -30,9 +28,8 @@ function _interopNamespace(e) {
30
28
  return Object.freeze(n);
31
29
  }
32
30
  const React__namespace = /* @__PURE__ */ _interopNamespace(React);
33
- const MapLibreGeocoder__default = /* @__PURE__ */ _interopDefault(MapLibreGeocoder);
34
- const maplibregl__default = /* @__PURE__ */ _interopDefault(maplibregl);
35
31
  const Map__default = /* @__PURE__ */ _interopDefault(Map);
32
+ const maplibregl__default = /* @__PURE__ */ _interopDefault(maplibregl);
36
33
  var __assign = function() {
37
34
  __assign = Object.assign || function __assign2(t) {
38
35
  for (var s, i = 1, n = arguments.length; i < n; i++) {
@@ -440,38 +437,6 @@ createFormattedComponent("formatList");
440
437
  createFormattedComponent("formatDisplayName");
441
438
  createFormattedDateTimePartsComponent("formatDate");
442
439
  createFormattedDateTimePartsComponent("formatTime");
443
- const DEFAULT_CONFIG = {
444
- mapStyles: [
445
- {
446
- id: "default",
447
- name: "Default",
448
- url: "https://demotiles.maplibre.org/style.json",
449
- isDefault: true
450
- }
451
- ],
452
- defaultZoom: 4.5,
453
- defaultCenter: [0, 0],
454
- // Null Island - fallback if not configured
455
- geocodingProvider: "nominatim",
456
- nominatimUrl: "https://nominatim.openstreetmap.org"
457
- };
458
- const usePluginConfig = () => {
459
- const [config, setConfig] = React.useState(DEFAULT_CONFIG);
460
- const { get } = admin.useFetchClient();
461
- React.useEffect(() => {
462
- const fetchConfig = async () => {
463
- try {
464
- const response = await get("/maplibre-field/config");
465
- if (response.data) {
466
- setConfig({ ...DEFAULT_CONFIG, ...response.data });
467
- }
468
- } catch {
469
- }
470
- };
471
- fetchConfig();
472
- }, [get]);
473
- return config;
474
- };
475
440
  const USER_AGENT$2 = "strapi-plugin-maplibre-field (Strapi CMS)";
476
441
  function createLocationFeature(coordinates, properties = {}) {
477
442
  const cleanProperties = Object.fromEntries(
@@ -498,7 +463,6 @@ function calculateDistance(coord1, coord2) {
498
463
  }
499
464
  async function queryCustomAPI(apiUrl, searchQuery) {
500
465
  try {
501
- console.log(`[POI Service] Fetching from: ${apiUrl}`);
502
466
  const response = await fetch(apiUrl);
503
467
  if (!response.ok) {
504
468
  console.error(
@@ -511,7 +475,6 @@ async function queryCustomAPI(apiUrl, searchQuery) {
511
475
  console.error(`[POI Service] Invalid GeoJSON from ${apiUrl}`);
512
476
  throw new Error("Invalid GeoJSON response format");
513
477
  }
514
- console.log(`[POI Service] Loaded ${data.features.length} features from ${apiUrl}`);
515
478
  let features = data.features;
516
479
  if (searchQuery && searchQuery.trim()) {
517
480
  const query = searchQuery.toLowerCase();
@@ -529,7 +492,6 @@ async function queryCustomAPI(apiUrl, searchQuery) {
529
492
  async function queryNominatim(lat, lng, radius, nominatimUrl) {
530
493
  try {
531
494
  const url = `${nominatimUrl}/reverse?format=jsonv2&lat=${lat}&lon=${lng}&zoom=18&addressdetails=1`;
532
- console.log("[Nominatim] Querying:", url);
533
495
  const response = await fetch(url, {
534
496
  headers: {
535
497
  "User-Agent": USER_AGENT$2
@@ -539,24 +501,18 @@ async function queryNominatim(lat, lng, radius, nominatimUrl) {
539
501
  throw new Error(`HTTP error! status: ${response.status}`);
540
502
  }
541
503
  const data = await response.json();
542
- console.log("[Nominatim] Response:", data);
543
504
  if (!data) {
544
- console.log("[Nominatim] No data returned");
545
505
  return [];
546
506
  }
547
507
  const coordinates = [parseFloat(data.lon), parseFloat(data.lat)];
548
508
  const distance = calculateDistance([lng, lat], coordinates);
549
- console.log("[Nominatim] Distance calculated:", distance, "meters (radius:", radius, "m)");
550
- console.log("[Nominatim] Click coords:", [lng, lat], "Result coords:", coordinates);
551
509
  if (distance > radius) {
552
- console.log("[Nominatim] POI outside radius, rejecting");
553
510
  return [];
554
511
  }
555
512
  let poiName = data.name || data.display_name || "Unknown Location";
556
513
  if (data.namedetails) {
557
514
  poiName = data.namedetails.name || poiName;
558
515
  }
559
- console.log("[Nominatim] POI found:", poiName, "type:", data.type || data.class);
560
516
  const poi = {
561
517
  id: `nominatim-${data.place_id || Date.now()}`,
562
518
  name: poiName,
@@ -704,161 +660,265 @@ async function queryPOIsForViewport(bounds, center, maxDisplay, config) {
704
660
  }
705
661
  }
706
662
  const USER_AGENT$1 = "strapi-plugin-maplibre-field/1.0.0 (Strapi CMS)";
707
- const GeocoderControl = ({
708
- mapRef,
709
- position = "top-left",
710
- onResult
711
- }) => {
712
- const geocoderRef = React.useRef(null);
713
- const config = usePluginConfig();
714
- React.useEffect(() => {
715
- if (!mapRef.current) return;
716
- const map = mapRef.current.getMap();
717
- if (!map || geocoderRef.current) return;
718
- const geocoderApi = {
719
- forwardGeocode: async (geocoderConfig) => {
720
- const query = geocoderConfig.query;
721
- if (typeof query !== "string" || !query.trim()) {
722
- return { type: "FeatureCollection", features: [] };
723
- }
724
- try {
725
- const results = [];
726
- if (config.poiSources && config.poiSearchEnabled) {
727
- const enabledSources = config.poiSources.filter((source) => source.enabled !== false);
728
- for (const source of enabledSources) {
729
- try {
730
- const customPOIs = await searchPOIsForGeocoder(query, {
731
- nominatimUrl: config.nominatimUrl || "https://nominatim.openstreetmap.org",
732
- customApiUrl: source.apiUrl,
733
- mapName: source.name,
734
- radius: 100,
735
- categories: []
736
- });
737
- const customFeatures = customPOIs.filter((poi) => poi.source === "custom").slice(0, 5).map((poi) => ({
738
- type: "Feature",
739
- geometry: {
740
- type: "Point",
741
- coordinates: poi.coordinates
742
- },
743
- place_name: poi.name,
744
- properties: {
745
- ...poi,
746
- source: "custom",
747
- poi_source_id: source.id
748
- // For color lookup in render function
749
- },
750
- text: poi.name,
751
- place_type: ["poi"],
752
- center: poi.coordinates
753
- }));
754
- results.push(...customFeatures);
755
- } catch (error) {
756
- console.warn(`Custom API search failed for ${source.name}, continuing:`, error);
757
- }
758
- }
759
- }
760
- const nominatimResponse = await fetch(
761
- `${config.nominatimUrl}/search?q=${encodeURIComponent(
762
- query
763
- )}&format=json&addressdetails=1&limit=5`,
764
- {
765
- headers: {
766
- "User-Agent": USER_AGENT$1
767
- }
768
- }
769
- );
770
- if (nominatimResponse.ok) {
771
- const nominatimData = await nominatimResponse.json();
772
- const nominatimFeatures = nominatimData.map((result) => ({
663
+ async function performSearch(query, config) {
664
+ const results = [];
665
+ if (config.poiSearchEnabled && config.poiSources) {
666
+ const enabledSources = config.poiSources.filter((source) => source.enabled !== false);
667
+ for (const source of enabledSources) {
668
+ try {
669
+ const poiResults = await searchPOIsForGeocoder(query, {
670
+ nominatimUrl: config.nominatimUrl,
671
+ customApiUrl: source.apiUrl,
672
+ mapName: source.name,
673
+ radius: 100,
674
+ categories: []
675
+ });
676
+ results.push(
677
+ ...poiResults.map((poi, idx) => ({
678
+ id: `poi-${source.id}-${idx}`,
679
+ place_name: poi.name,
680
+ feature: {
773
681
  type: "Feature",
774
682
  geometry: {
775
683
  type: "Point",
776
- coordinates: [parseFloat(result.lon), parseFloat(result.lat)]
684
+ coordinates: poi.coordinates
777
685
  },
778
- place_name: result.display_name,
779
- properties: { ...result, source: "nominatim" },
780
- text: result.display_name,
781
- place_type: ["place"],
782
- center: [parseFloat(result.lon), parseFloat(result.lat)]
783
- }));
784
- results.push(...nominatimFeatures);
785
- }
786
- return {
787
- type: "FeatureCollection",
788
- features: results
789
- };
790
- } catch (error) {
791
- console.error("Geocoding error:", error);
792
- return { type: "FeatureCollection", features: [] };
793
- }
794
- },
795
- maplibregl: maplibregl__default.default
796
- };
797
- const geocoderOptions = {
798
- marker: false,
799
- showResultsWhileTyping: false,
800
- // Require Enter key for Nominatim policy compliance
801
- render: (item) => {
802
- const properties = "properties" in item ? item.properties : void 0;
803
- const source = properties?.source;
804
- let dotColor = "#6c757d";
805
- if (source === "custom") {
806
- const poiSourceId = properties?.poi_source_id;
807
- const poiSource = config.poiSources?.find((s) => s.id === poiSourceId);
808
- dotColor = poiSource?.color || "#cc0000";
686
+ properties: {
687
+ name: poi.name,
688
+ address: poi.address,
689
+ source: "custom",
690
+ sourceId: source.id,
691
+ sourceLayer: source.name,
692
+ category: poi.type,
693
+ // POI.type is the category
694
+ inputMethod: "search"
695
+ }
696
+ },
697
+ source: "custom"
698
+ }))
699
+ );
700
+ } catch (error) {
701
+ console.warn(`POI search failed for ${source.name}:`, error);
702
+ }
703
+ }
704
+ }
705
+ try {
706
+ const response = await fetch(
707
+ `${config.nominatimUrl}/search?q=${encodeURIComponent(query)}&format=json&addressdetails=1&limit=5`,
708
+ {
709
+ headers: {
710
+ "User-Agent": USER_AGENT$1
809
711
  }
810
- const escapeHtml = (str) => {
811
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
812
- };
813
- const placeName = "place_name" in item ? item.place_name : void 0;
814
- const displayText = escapeHtml(item.text || placeName || "");
815
- return `
816
- <div class="maplibregl-ctrl-geocoder--suggestion">
817
- <div class="maplibregl-ctrl-geocoder--suggestion-icon">
818
- <span style="display:inline-block;width:10px;height:10px;border-radius:50%;background-color:${dotColor};border:2px solid #ffffff;box-shadow:0 0 0 1px rgba(0,0,0,0.1);"></span>
819
- </div>
820
- <div class="maplibregl-ctrl-geocoder--suggestion-info">
821
- <div class="maplibregl-ctrl-geocoder--suggestion-title">
822
- ${displayText}
823
- </div>
824
- </div>
825
- </div>
826
- `;
827
712
  }
828
- };
829
- const geocoder = new MapLibreGeocoder__default.default(geocoderApi, geocoderOptions);
830
- if (onResult) {
831
- geocoder.on("result", (evt) => {
832
- const feature = evt.result;
833
- const geometry = feature.geometry;
834
- const coords = geometry && "coordinates" in geometry ? geometry.coordinates : void 0;
835
- const result = {
836
- result: {
837
- center: Array.isArray(coords) && coords.length >= 2 ? [coords[0], coords[1]] : void 0,
838
- place_name: feature.place_name || "",
839
- geometry: geometry?.type === "Point" && Array.isArray(coords) && coords.length >= 2 ? {
713
+ );
714
+ if (response.ok) {
715
+ const data = await response.json();
716
+ results.push(
717
+ ...data.map((result, idx) => ({
718
+ id: `nominatim-${idx}`,
719
+ place_name: result.display_name,
720
+ feature: {
721
+ type: "Feature",
722
+ geometry: {
840
723
  type: "Point",
841
- coordinates: [coords[0], coords[1]]
842
- } : void 0
843
- }
844
- };
845
- onResult(result);
846
- });
724
+ coordinates: [parseFloat(result.lon), parseFloat(result.lat)]
725
+ },
726
+ properties: {
727
+ name: result.display_name,
728
+ address: result.display_name,
729
+ source: "nominatim",
730
+ inputMethod: "search"
731
+ }
732
+ },
733
+ source: "nominatim"
734
+ }))
735
+ );
847
736
  }
848
- map.addControl(geocoder, position);
849
- geocoderRef.current = geocoder;
850
- return () => {
851
- if (geocoderRef.current && map) {
852
- try {
853
- map.removeControl(geocoderRef.current);
854
- } catch (error) {
855
- console.warn("Error removing geocoder control:", error);
856
- }
857
- geocoderRef.current = null;
737
+ } catch (error) {
738
+ console.error("Nominatim search error:", error);
739
+ }
740
+ return results;
741
+ }
742
+ const SearchBox = ({
743
+ onSelectResult,
744
+ nominatimUrl,
745
+ poiSearchEnabled,
746
+ poiSources
747
+ }) => {
748
+ const { formatMessage } = useIntl();
749
+ const [query, setQuery] = React.useState("");
750
+ const [results, setResults] = React.useState([]);
751
+ const [isLoading, setIsLoading] = React.useState(false);
752
+ const [isOpen, setIsOpen] = React.useState(false);
753
+ const [hoveredId, setHoveredId] = React.useState(null);
754
+ const dropdownRef = React.useRef(null);
755
+ const containerRef = React.useRef(null);
756
+ React.useEffect(() => {
757
+ const handleClickOutside = (event) => {
758
+ if (containerRef.current && !containerRef.current.contains(event.target)) {
759
+ setIsOpen(false);
858
760
  }
859
761
  };
860
- }, [mapRef, position, onResult, config.nominatimUrl]);
861
- return null;
762
+ if (isOpen) {
763
+ document.addEventListener("mousedown", handleClickOutside);
764
+ return () => {
765
+ document.removeEventListener("mousedown", handleClickOutside);
766
+ };
767
+ }
768
+ }, [isOpen]);
769
+ const handleSearch = async () => {
770
+ if (!query.trim()) {
771
+ setResults([]);
772
+ setIsOpen(false);
773
+ return;
774
+ }
775
+ setIsLoading(true);
776
+ try {
777
+ const searchResults = await performSearch(query, {
778
+ nominatimUrl,
779
+ poiSearchEnabled,
780
+ poiSources
781
+ });
782
+ setResults(searchResults);
783
+ setIsOpen(searchResults.length > 0);
784
+ } catch (error) {
785
+ console.error("Search error:", error);
786
+ setResults([]);
787
+ setIsOpen(false);
788
+ } finally {
789
+ setIsLoading(false);
790
+ }
791
+ };
792
+ const handleKeyDown = (e) => {
793
+ if (e.key === "Enter") {
794
+ e.preventDefault();
795
+ handleSearch();
796
+ } else if (e.key === "Escape") {
797
+ setIsOpen(false);
798
+ setQuery("");
799
+ }
800
+ };
801
+ const handleSelectResult = (result) => {
802
+ onSelectResult(result.feature);
803
+ setIsOpen(false);
804
+ setQuery("");
805
+ setResults([]);
806
+ };
807
+ const getResultColor = (result) => {
808
+ if (result.source === "nominatim") {
809
+ return "#6c757d";
810
+ }
811
+ const sourceId = result.feature.properties?.sourceId;
812
+ if (sourceId && poiSources) {
813
+ const source = poiSources.find((s) => s.id === sourceId);
814
+ if (source?.color) {
815
+ return source.color;
816
+ }
817
+ }
818
+ return "#cc0000";
819
+ };
820
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { ref: containerRef, style: { position: "relative", width: "100%" }, children: [
821
+ /* @__PURE__ */ jsxRuntime.jsx(
822
+ designSystem.TextInput,
823
+ {
824
+ placeholder: formatMessage({ id: index.getTrad("search.placeholder") }),
825
+ value: query,
826
+ onChange: (e) => setQuery(e.target.value),
827
+ onKeyDown: handleKeyDown,
828
+ "aria-label": "Location search"
829
+ }
830
+ ),
831
+ isOpen && results.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
832
+ designSystem.Box,
833
+ {
834
+ ref: dropdownRef,
835
+ background: "neutral0",
836
+ shadow: "filterShadow",
837
+ borderColor: "neutral150",
838
+ hasRadius: true,
839
+ padding: 2,
840
+ style: {
841
+ position: "absolute",
842
+ top: "calc(100% + 4px)",
843
+ left: 0,
844
+ right: 0,
845
+ zIndex: 1e3,
846
+ maxHeight: "300px",
847
+ overflowY: "auto"
848
+ },
849
+ children: results.map((result) => {
850
+ const dotColor = getResultColor(result);
851
+ return /* @__PURE__ */ jsxRuntime.jsx(
852
+ designSystem.Box,
853
+ {
854
+ paddingTop: 2,
855
+ paddingBottom: 2,
856
+ paddingLeft: 3,
857
+ paddingRight: 3,
858
+ hasRadius: true,
859
+ background: hoveredId === result.id ? "primary100" : "neutral0",
860
+ onMouseEnter: () => setHoveredId(result.id),
861
+ onMouseLeave: () => setHoveredId(null),
862
+ onClick: () => handleSelectResult(result),
863
+ style: {
864
+ cursor: "pointer"
865
+ },
866
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
867
+ /* @__PURE__ */ jsxRuntime.jsx(
868
+ "div",
869
+ {
870
+ style: {
871
+ width: "10px",
872
+ height: "10px",
873
+ borderRadius: "50%",
874
+ backgroundColor: dotColor,
875
+ flexShrink: 0
876
+ }
877
+ }
878
+ ),
879
+ /* @__PURE__ */ jsxRuntime.jsx(
880
+ designSystem.Typography,
881
+ {
882
+ variant: "omega",
883
+ textColor: "neutral800",
884
+ style: {
885
+ overflow: "hidden",
886
+ textOverflow: "ellipsis",
887
+ whiteSpace: "nowrap"
888
+ },
889
+ children: result.place_name
890
+ }
891
+ )
892
+ ] })
893
+ },
894
+ result.id
895
+ );
896
+ })
897
+ }
898
+ ),
899
+ isOpen && results.length === 0 && !isLoading && query.trim() && /* @__PURE__ */ jsxRuntime.jsx(
900
+ designSystem.Box,
901
+ {
902
+ background: "neutral0",
903
+ shadow: "filterShadow",
904
+ borderColor: "neutral150",
905
+ hasRadius: true,
906
+ padding: 4,
907
+ style: {
908
+ position: "absolute",
909
+ top: "calc(100% + 4px)",
910
+ left: 0,
911
+ right: 0,
912
+ zIndex: 1e3
913
+ },
914
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "omega", textColor: "neutral600", children: [
915
+ 'No results found for "',
916
+ query,
917
+ '"'
918
+ ] })
919
+ }
920
+ )
921
+ ] });
862
922
  };
863
923
  class BasemapControl {
864
924
  _map;
@@ -1100,88 +1160,38 @@ const LayerControl = ({ mapRef, layers, onLayerToggle }) => {
1100
1160
  }, [layers]);
1101
1161
  return null;
1102
1162
  };
1103
- class CreditsControlImpl {
1104
- _map;
1105
- _container;
1106
- onAdd(map) {
1107
- this._map = map;
1108
- this._container = document.createElement("div");
1109
- this._container.className = "maplibregl-ctrl maplibregl-ctrl-attrib maplibregl-compact";
1110
- this._render();
1111
- return this._container;
1112
- }
1113
- onRemove() {
1114
- if (this._container && this._container.parentNode) {
1115
- this._container.parentNode.removeChild(this._container);
1163
+ const DEFAULT_CONFIG = {
1164
+ mapStyles: [
1165
+ {
1166
+ id: "default",
1167
+ name: "Default",
1168
+ url: "https://tiles.openfreemap.org/styles/liberty",
1169
+ isDefault: true
1116
1170
  }
1117
- this._map = void 0;
1118
- }
1119
- _render() {
1120
- if (!this._container) return;
1121
- this._container.innerHTML = "";
1122
- const button = document.createElement("button");
1123
- button.type = "button";
1124
- button.className = "maplibregl-ctrl-attrib-button";
1125
- button.title = "Toggle attribution";
1126
- button.setAttribute("aria-label", "Toggle attribution");
1127
- button.setAttribute("type", "button");
1128
- button.setAttribute("aria-pressed", "false");
1129
- const panel = document.createElement("div");
1130
- panel.className = "maplibregl-ctrl-attrib maplibregl-compact";
1131
- panel.style.display = "none";
1132
- panel.style.background = "rgba(255, 255, 255, 0.5)";
1133
- panel.style.fontSize = "11px";
1134
- panel.style.padding = "0 5px";
1135
- panel.style.margin = "0";
1136
- panel.style.lineHeight = "20px";
1137
- panel.style.color = "rgba(0, 0, 0, 0.75)";
1138
- const content = document.createElement("div");
1139
- content.innerHTML = `
1140
- <a href="https://www.openstreetmap.org/copyright" target="_blank" rel="noopener noreferrer">© OpenStreetMap</a> |
1141
- <a href="https://maplibre.org/" target="_blank" rel="noopener noreferrer">MapLibre</a>
1142
- `;
1143
- content.querySelectorAll("a").forEach((link) => {
1144
- link.style.color = "rgba(0, 0, 0, 0.75)";
1145
- link.style.textDecoration = "none";
1146
- });
1147
- panel.appendChild(content);
1148
- let isPanelOpen = false;
1149
- const togglePanel = (open) => {
1150
- isPanelOpen = open;
1151
- panel.style.display = isPanelOpen ? "block" : "none";
1152
- button.setAttribute("aria-pressed", isPanelOpen ? "true" : "false");
1153
- };
1154
- button.addEventListener("click", (e) => {
1155
- e.stopPropagation();
1156
- togglePanel(!isPanelOpen);
1157
- });
1158
- document.addEventListener("click", (e) => {
1159
- if (isPanelOpen && this._container && !this._container.contains(e.target)) {
1160
- togglePanel(false);
1161
- }
1162
- });
1163
- this._container.appendChild(button);
1164
- this._container.appendChild(panel);
1165
- }
1166
- }
1167
- const CreditsControl = ({ mapRef }) => {
1168
- const controlRef = React.useRef(null);
1171
+ ],
1172
+ defaultZoom: 4.5,
1173
+ defaultCenter: [0, 0],
1174
+ // Null Island - fallback if not configured
1175
+ geocodingProvider: "nominatim",
1176
+ nominatimUrl: "https://nominatim.openstreetmap.org"
1177
+ };
1178
+ const usePluginConfig = () => {
1179
+ const [config, setConfig] = React.useState(DEFAULT_CONFIG);
1180
+ const { get } = admin.useFetchClient();
1169
1181
  React.useEffect(() => {
1170
- if (!mapRef.current) return;
1171
- const map = mapRef.current.getMap();
1172
- const control = new CreditsControlImpl();
1173
- controlRef.current = control;
1174
- map.addControl(control, "bottom-right");
1175
- return () => {
1176
- if (controlRef.current) {
1177
- map.removeControl(controlRef.current);
1178
- controlRef.current = null;
1182
+ const fetchConfig = async () => {
1183
+ try {
1184
+ const response = await get("/maplibre-field/config");
1185
+ if (response.data) {
1186
+ setConfig({ ...DEFAULT_CONFIG, ...response.data });
1187
+ }
1188
+ } catch {
1179
1189
  }
1180
1190
  };
1181
- }, [mapRef]);
1182
- return null;
1191
+ fetchConfig();
1192
+ }, [get]);
1193
+ return config;
1183
1194
  };
1184
- const getTrad = (id) => `${index.pluginId}.${id}`;
1185
1195
  const USER_AGENT = "strapi-plugin-maplibre-field/1.0.0 (Strapi CMS)";
1186
1196
  const protocol = new pmtiles.Protocol();
1187
1197
  maplibregl__default.default.addProtocol("pmtiles", protocol.tile);
@@ -1426,7 +1436,7 @@ const MapField = ({ intlLabel, name, onChange, value }) => {
1426
1436
  );
1427
1437
  toggleNotification({
1428
1438
  type: "success",
1429
- message: poi.mapName ? `${poi.mapName} ${poi.name}` : `Selected ${poi.name}`
1439
+ message: poi.mapName ? `${poi.mapName} ${poi.name}` : `Selected ${poi.name}`
1430
1440
  });
1431
1441
  };
1432
1442
  const handleMapClick = (evt) => {
@@ -1507,8 +1517,8 @@ const MapField = ({ intlLabel, name, onChange, value }) => {
1507
1517
  toggleNotification({
1508
1518
  type: "info",
1509
1519
  message: formatMessage({
1510
- id: getTrad("coordinates-saved"),
1511
- defaultMessage: "Coordinates saved"
1520
+ id: index.getTrad("coordinates-saved"),
1521
+ defaultMessage: "Coordinates set"
1512
1522
  })
1513
1523
  });
1514
1524
  }
@@ -1522,8 +1532,8 @@ const MapField = ({ intlLabel, name, onChange, value }) => {
1522
1532
  toggleNotification({
1523
1533
  type: "info",
1524
1534
  message: formatMessage({
1525
- id: getTrad("coordinates-saved"),
1526
- defaultMessage: "Coordinates saved"
1535
+ id: index.getTrad("coordinates-saved"),
1536
+ defaultMessage: "Coordinates set"
1527
1537
  })
1528
1538
  });
1529
1539
  }
@@ -1536,36 +1546,20 @@ const MapField = ({ intlLabel, name, onChange, value }) => {
1536
1546
  setLatitude(feature.geometry.coordinates[1]);
1537
1547
  onChange({ target: { name, value: value2, type: "json" } });
1538
1548
  };
1539
- const handleGeocoderResult = (evt) => {
1540
- const { result: result2 } = evt;
1541
- if (result2?.center) {
1542
- const properties = result2.properties || {};
1543
- const isCustomSource = properties.source === "custom";
1544
- const getString = (val) => typeof val === "string" ? val : void 0;
1545
- let name2 = result2.place_name || "";
1546
- const displayName = getString(properties.display_name);
1547
- if (!isCustomSource && displayName) {
1548
- name2 = getString(properties.name) || displayName.split(",")[0].trim();
1549
- }
1550
- updateValues(
1551
- createLocationFeature(result2.center, {
1552
- name: isCustomSource ? result2.place_name : name2,
1553
- address: isCustomSource ? void 0 : result2.place_name,
1554
- // Full address for Nominatim
1555
- source: getString(isCustomSource ? properties.poi_source_id : "nominatim"),
1556
- sourceId: getString(isCustomSource ? properties.id : `nominatim-${properties.place_id}`),
1557
- sourceLayer: getString(isCustomSource ? properties.mapName : void 0),
1558
- category: getString(properties.type) || getString(properties.class),
1559
- inputMethod: "search",
1560
- metadata: isCustomSource ? properties.metadata : {
1561
- osm_id: properties.osm_id,
1562
- osm_type: properties.osm_type,
1563
- place_id: properties.place_id,
1564
- addresstype: properties.addresstype
1565
- }
1566
- })
1567
- );
1549
+ const handleSearchResult = (feature) => {
1550
+ if (feature.geometry?.coordinates) {
1551
+ const [lng, lat] = feature.geometry.coordinates;
1552
+ mapRef.current?.flyTo({
1553
+ center: [lng, lat],
1554
+ zoom: 16,
1555
+ essential: true
1556
+ });
1568
1557
  }
1558
+ updateValues(feature);
1559
+ toggleNotification({
1560
+ type: "success",
1561
+ message: `Location set to: ${feature.properties.name || feature.properties.address}`
1562
+ });
1569
1563
  };
1570
1564
  React.useEffect(() => {
1571
1565
  if (!isDefaultViewState && mapRef.current) {
@@ -1621,6 +1615,15 @@ const MapField = ({ intlLabel, name, onChange, value }) => {
1621
1615
  }, [config.poiDisplayEnabled]);
1622
1616
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
1623
1617
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", variant: "pi", fontWeight: "bold", children: formatMessage(label) }),
1618
+ /* @__PURE__ */ jsxRuntime.jsx(
1619
+ SearchBox,
1620
+ {
1621
+ onSelectResult: handleSearchResult,
1622
+ nominatimUrl: config.nominatimUrl || "https://nominatim.openstreetmap.org",
1623
+ poiSearchEnabled: config.poiSearchEnabled,
1624
+ poiSources: config.poiSources
1625
+ }
1626
+ ),
1624
1627
  /* @__PURE__ */ jsxRuntime.jsx(
1625
1628
  designSystem.Flex,
1626
1629
  {
@@ -1641,12 +1644,10 @@ const MapField = ({ intlLabel, name, onChange, value }) => {
1641
1644
  onClick: handleMapClick,
1642
1645
  onDblClick: handleMapDoubleClick,
1643
1646
  mapStyle: currentStyleUrl,
1644
- attributionControl: false,
1645
1647
  children: [
1646
1648
  /* @__PURE__ */ jsxRuntime.jsx(Map.FullscreenControl, {}),
1647
1649
  /* @__PURE__ */ jsxRuntime.jsx(Map.NavigationControl, {}),
1648
1650
  /* @__PURE__ */ jsxRuntime.jsx(Map.GeolocateControl, {}),
1649
- /* @__PURE__ */ jsxRuntime.jsx(GeocoderControl, { mapRef, position: "top-left", onResult: handleGeocoderResult }),
1650
1651
  config.mapStyles && config.mapStyles.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(
1651
1652
  BasemapControlComponent,
1652
1653
  {
@@ -1656,7 +1657,6 @@ const MapField = ({ intlLabel, name, onChange, value }) => {
1656
1657
  }
1657
1658
  ),
1658
1659
  poiLayers.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(LayerControl, { mapRef, layers: poiLayers, onLayerToggle: handleLayerToggle }),
1659
- /* @__PURE__ */ jsxRuntime.jsx(CreditsControl, { mapRef }),
1660
1660
  config.poiDisplayEnabled && displayedPOIs.length > 0 && (() => {
1661
1661
  const layerColorMap = {};
1662
1662
  poiLayers.forEach((layer) => {
@@ -1752,7 +1752,7 @@ const MapField = ({ intlLabel, name, onChange, value }) => {
1752
1752
  `poi-source-${displayedPOIs.length}-${selectedPOI?.id || "none"}`
1753
1753
  );
1754
1754
  })(),
1755
- /* @__PURE__ */ jsxRuntime.jsx(Map.Marker, { longitude, latitude, color: "#1da1f2" })
1755
+ /* @__PURE__ */ jsxRuntime.jsx(Map.Marker, { longitude, latitude, color: "#4945ff" })
1756
1756
  ]
1757
1757
  }
1758
1758
  )
@@ -1761,28 +1761,28 @@ const MapField = ({ intlLabel, name, onChange, value }) => {
1761
1761
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { children: [
1762
1762
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { padding: 1, col: 8, xs: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
1763
1763
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: formatMessage({
1764
- id: result?.properties?.sourceId ? getTrad("fields.poi-name") : getTrad("fields.address"),
1764
+ id: result?.properties?.sourceId ? index.getTrad("fields.poi-name") : index.getTrad("fields.address"),
1765
1765
  defaultMessage: result?.properties?.sourceId ? "POI Name" : "Address"
1766
1766
  }) }),
1767
1767
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Input, { name: "place_name", value: address, disabled: true })
1768
1768
  ] }) }),
1769
1769
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { padding: 1, col: 2, xs: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
1770
1770
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: formatMessage({
1771
- id: getTrad("fields.longitude"),
1771
+ id: index.getTrad("fields.longitude"),
1772
1772
  defaultMessage: "Longitude"
1773
1773
  }) }),
1774
1774
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Input, { name: "longitude", value: longitude, disabled: true })
1775
1775
  ] }) }),
1776
1776
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { padding: 1, col: 2, xs: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
1777
1777
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: formatMessage({
1778
- id: getTrad("fields.latitude"),
1778
+ id: index.getTrad("fields.latitude"),
1779
1779
  defaultMessage: "Latitude"
1780
1780
  }) }),
1781
1781
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Input, { name: "latitude", value: latitude, disabled: true })
1782
1782
  ] }) }),
1783
1783
  result?.properties?.sourceId && result?.properties?.address && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { padding: 1, col: 12, xs: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
1784
1784
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: formatMessage({
1785
- id: getTrad("fields.poi-address"),
1785
+ id: index.getTrad("fields.poi-address"),
1786
1786
  defaultMessage: "Full Address"
1787
1787
  }) }),
1788
1788
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Input, { name: "poi_address", value: result.properties.address, disabled: true })