strapi-plugin-map-box
Version:
418 lines (416 loc) • 12.6 kB
JavaScript
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