UNPKG

strapi-plugin-map-box

Version:
418 lines (416 loc) 12.6 kB
import { useRef, useEffect, useState } from "react"; import { PinMap } from "@strapi/icons"; import { jsx, jsxs } from "react/jsx-runtime"; import { Field, JSONInput } from "@strapi/design-system"; import Map, { FullscreenControl, NavigationControl, GeolocateControl, Marker } from "react-map-gl/mapbox"; import "mapbox-gl/dist/mapbox-gl.css"; import { useFetchClient } from "@strapi/strapi/admin"; import styled from "styled-components"; const __variableDynamicImportRuntimeHelper = (glob, path, segs) => { const v = glob[path]; if (v) { return typeof v === "function" ? v() : Promise.resolve(v); } return new Promise((_, reject) => { (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)( reject.bind( null, new Error( "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "") ) ) ); }); }; const PLUGIN_ID = "map-box"; const Initializer = ({ setPlugin }) => { const ref = useRef(setPlugin); useEffect(() => { ref.current(PLUGIN_ID); }, []); return null; }; const ControlsContainer = styled.div` position: absolute; top: 1rem; left: 1rem; z-index: 1; width: 300px; `; const ControlsWrapper = styled.div` display: flex; gap: 0.5rem; `; const SearchInput = styled.input` flex: 1; padding: 0.5rem; border: 1px solid #dcdce4; border-radius: 4px; font-size: 14px; `; const SearchButton = styled.button` padding: 0.5rem 1rem; background-color: #4945ff; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; &:hover { background-color: #3832e0; } `; const MapSearch = ({ onSearch, searchQuery, setSearchQuery, handleKeyDown }) => { return /* @__PURE__ */ jsx(ControlsContainer, { children: /* @__PURE__ */ jsxs(ControlsWrapper, { children: [ /* @__PURE__ */ jsx( SearchInput, { type: "text", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), onKeyDown: handleKeyDown, placeholder: "Search for a location..." } ), /* @__PURE__ */ jsx( SearchButton, { type: "button", onClick: onSearch, children: "Search" } ) ] }) }); }; const DEFAULT_VIEW_STATE = { longitude: -122.4194, latitude: 37.7749, zoom: 13, pitch: 0, bearing: 0, padding: { top: 0, bottom: 0, left: 0, right: 0 } }; const useMapBoxSettings = () => { const { get } = useFetchClient(); const [config, setConfig] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchSettings = async () => { try { setIsLoading(true); const { data } = await get("/strapi-plugin-map-box/get-settings"); console.log("data from getSettings", data); setConfig(data); setError(null); } catch (err) { setError(err instanceof Error ? err.message : "Failed to fetch MapBox settings"); } finally { setIsLoading(false); } }; fetchSettings(); }, []); return { config, isLoading, error }; }; const useMapLocationHook = (initialValue) => { const [viewState, setViewState] = useState(DEFAULT_VIEW_STATE); const [markerPosition, setMarkerPosition] = useState({ longitude: DEFAULT_VIEW_STATE.longitude, latitude: DEFAULT_VIEW_STATE.latitude }); useEffect(() => { if (initialValue) { console.log("Initializing from previous value:", initialValue); const previousValue = initialValue; setViewState((prev) => ({ ...prev, longitude: previousValue.longitude, latitude: previousValue.latitude, zoom: previousValue.zoom, pitch: previousValue.pitch, bearing: previousValue.bearing })); setMarkerPosition({ longitude: previousValue.longitude, latitude: previousValue.latitude }); } }, []); return { viewState, setViewState, markerPosition, setMarkerPosition }; }; function DebugInfo({ searchResults, searchError, viewState, markerPosition, searchQuery, value }) { return /* @__PURE__ */ jsxs(DebugContainer, { children: [ /* @__PURE__ */ jsx("h4", { children: "Debug Information:" }), /* @__PURE__ */ jsxs(DebugSection, { children: [ /* @__PURE__ */ jsx("strong", { children: "Search Results:" }), /* @__PURE__ */ jsx(DebugPre, { children: searchResults ? JSON.stringify(searchResults, null, 2) : "No search results yet" }) ] }), searchError && /* @__PURE__ */ jsxs(ErrorMessage, { children: [ /* @__PURE__ */ jsx("strong", { children: "Error:" }), " ", searchError ] }), /* @__PURE__ */ jsxs(DebugSection, { children: [ /* @__PURE__ */ jsx("strong", { children: "Current View State:" }), /* @__PURE__ */ jsx(DebugPre, { children: JSON.stringify(viewState, null, 2) }) ] }), /* @__PURE__ */ jsxs(DebugSection, { children: [ /* @__PURE__ */ jsx("strong", { children: "Marker Position:" }), /* @__PURE__ */ jsx(DebugPre, { children: JSON.stringify(markerPosition, null, 2) }) ] }), /* @__PURE__ */ jsxs(DebugSection, { children: [ /* @__PURE__ */ jsx("strong", { children: "Search Query:" }), /* @__PURE__ */ jsx(DebugPre, { children: searchQuery || "No search query" }) ] }), /* @__PURE__ */ jsxs(DebugSection, { children: [ /* @__PURE__ */ jsx("strong", { children: "Current Value:" }), /* @__PURE__ */ jsx(DebugPre, { children: value ? JSON.stringify(value, null, 2) : "No value set" }) ] }) ] }); } const DebugContainer = styled.div` margin-top: 20px; padding: 1rem; background-color: #f5f5f5; border-radius: 4px; `; const DebugSection = styled.div` margin-bottom: 10px; `; const DebugPre = styled.pre` background: #ffffff; padding: 10px; border-radius: 4px; max-height: 200px; overflow: auto; margin: 5px 0; border: 1px solid #dcdce4; `; const ErrorMessage = styled.div` color: #d02b20; margin-bottom: 10px; padding: 10px; background-color: #fff5f5; border: 1px solid #ffd7d5; border-radius: 4px; `; function MapBoxField({ name, onChange, value, intlLabel, required }) { const { get } = useFetchClient(); const { config, isLoading, error } = useMapBoxSettings(); const { viewState, markerPosition, setViewState, setMarkerPosition } = useMapLocationHook(value); const { accessToken, debugMode } = config || {}; const [searchQuery, setSearchQuery] = useState(""); const [searchResults, setSearchResults] = useState(null); const [searchError, setSearchError] = useState(null); const updateMarkerPosition = (lng, lat, address) => { setMarkerPosition({ longitude: lng, latitude: lat }); const newValue = { longitude: lng, latitude: lat, address: address || "Selected location", zoom: viewState.zoom, pitch: viewState.pitch, bearing: viewState.bearing }; onChange({ target: { name, value: newValue, type: "json" } }); }; const handleSearch = async () => { if (!searchQuery.trim()) return; try { setSearchError(null); const encodedQuery = encodeURIComponent(searchQuery.trim()); const url = `/strapi-plugin-map-box/location-search/${encodedQuery}`; const { data } = await get(url); setSearchResults(data); if (data.features && data.features[0]) { const [longitude, latitude] = data.features[0].center; setViewState((prev) => ({ ...prev, longitude, latitude, zoom: 14, transitionDuration: 1e3 })); updateMarkerPosition(longitude, latitude, data.features[0].place_name); } else if (data.error) { setSearchError(data.error); } else { setSearchError("No results found"); } } catch (error2) { console.error("Error searching location:", error2); setSearchError(error2 instanceof Error ? error2.message : "An error occurred"); } }; const handleKeyDown = (e) => { if (e.key === "Enter") { e.preventDefault(); handleSearch(); } }; const handleMapClick = (event) => { const { lngLat } = event; updateMarkerPosition(lngLat.lng, lngLat.lat); }; const handleMapMove = (evt) => { setViewState(evt.viewState); }; const handleMarkerDragEnd = (event) => { const { lngLat } = event; updateMarkerPosition(lngLat.lng, lngLat.lat); }; const handlePositionChange = (input) => { try { const value2 = JSON.parse(input); setViewState((prev) => ({ ...prev, longitude: value2.longitude, latitude: value2.latitude, zoom: value2.zoom || prev.zoom, pitch: value2.pitch || prev.pitch, bearing: value2.bearing || prev.bearing })); setMarkerPosition({ longitude: value2.longitude, latitude: value2.latitude }); onChange({ target: { name, value: value2, type: "json" } }); } catch { } }; const finalValue = { longitude: markerPosition.longitude, latitude: markerPosition.latitude, zoom: viewState.zoom, pitch: viewState.pitch, bearing: viewState.bearing, address: value?.address || "Selected location" }; const strValue = JSON.stringify(value || finalValue, null, 2); if (!accessToken || isLoading) { return /* @__PURE__ */ jsx("div", { children: "Loading..." }); } if (error) { return /* @__PURE__ */ jsxs("div", { children: [ "Error: ", error ] }); } return /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsxs("div", { style: { position: "relative", height: "500px", width: "100%" }, children: [ /* @__PURE__ */ jsx( MapSearch, { onSearch: handleSearch, searchQuery, setSearchQuery, handleKeyDown } ), /* @__PURE__ */ jsxs( Map, { ...viewState, onMove: handleMapMove, onClick: handleMapClick, mapStyle: "mapbox://styles/mapbox/streets-v12", mapboxAccessToken: accessToken, attributionControl: false, style: { height: "100%", width: "100%" }, children: [ /* @__PURE__ */ jsx(FullscreenControl, {}), /* @__PURE__ */ jsx(NavigationControl, {}), /* @__PURE__ */ jsx(GeolocateControl, {}), /* @__PURE__ */ jsx( Marker, { longitude: markerPosition.longitude, latitude: markerPosition.latitude, color: "#4945ff", draggable: true, onDragEnd: handleMarkerDragEnd } ) ] } ) ] }), debugMode && /* @__PURE__ */ jsxs(Field.Root, { name, required, children: [ /* @__PURE__ */ jsx(Field.Label, { children: intlLabel?.defaultMessage ?? "Location" }), /* @__PURE__ */ jsx(JSONInput, { value: strValue, onChange: handlePositionChange }), /* @__PURE__ */ jsx(Field.Error, {}), /* @__PURE__ */ jsx(Field.Hint, {}) ] }), debugMode && /* @__PURE__ */ jsx( DebugInfo, { viewState, searchResults, searchError, markerPosition, searchQuery, value } ) ] }); } const index = { register(app) { app.customFields.register({ name: "map-box", type: "json", icon: PinMap, intlLabel: { id: "custom.fields.map-box.label", defaultMessage: "Map Box" }, intlDescription: { id: "custom.fields.map-box.description", defaultMessage: "Enter geographic coordinates" }, components: { Input: () => ({ default: MapBoxField }) } }); app.registerPlugin({ id: PLUGIN_ID, initializer: Initializer, isReady: false, name: PLUGIN_ID }); }, async registerTrads({ locales }) { return Promise.all( locales.map(async (locale) => { try { const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("../_chunks/en-Byx4XI2L.mjs") }), `./translations/${locale}.json`, 3); return { data, locale }; } catch { return { data: {}, locale }; } }) ); } }; export { index as default }; //# sourceMappingURL=index.mjs.map