diff --git a/src/app/[lang]/crag/[cragSlug]/info/page.tsx b/src/app/[lang]/crag/[cragSlug]/info/page.tsx index e39b33c..5e8f87c 100644 --- a/src/app/[lang]/crag/[cragSlug]/info/page.tsx +++ b/src/app/[lang]/crag/[cragSlug]/info/page.tsx @@ -28,9 +28,9 @@ import Map from "@/components/map/map"; import Button from "@/components/ui/button"; import IconMissing from "@/components/ui/icons/missing"; import Link from "@/components/ui/link"; -import { TMarker } from "@/components/map/lazy-map"; import { IconSize } from "@/components/ui/icons/icon-size"; import IconMore from "@/components/ui/icons/more"; +import { TMarker } from "@/components/map/map-marker"; type TCragInfoPageParams = { cragSlug: string; diff --git a/src/components/map/lazy-map.tsx b/src/components/map/lazy-map.tsx index 0825207..1498802 100644 --- a/src/components/map/lazy-map.tsx +++ b/src/components/map/lazy-map.tsx @@ -1,18 +1,11 @@ "use client"; -import { MapContainer, Marker, Popup, TileLayer } from "react-leaflet"; +import { MapContainer, TileLayer } from "react-leaflet"; import L, { FitBoundsOptions } from "leaflet"; import "leaflet/dist/leaflet.css"; -import ReactDOMServer from "react-dom/server"; -import IconMarker from "../ui/icons/marker"; import { ReactNode } from "react"; import "./map.css"; - -type TMarker = { - type: "parking" | "wall"; - position: [number, number]; - popupContent?: ReactNode; -}; +import MapMarker, { TMarker } from "./map-marker"; type TLazyMapProps = { children?: ReactNode; @@ -64,21 +57,7 @@ function LazyMap({ /> {markers?.map((marker, index) => ( - - ), - iconSize: [52, 52], - iconAnchor: [26, 52], - popupAnchor: [0, -46], - })} - position={marker.position} - > - {marker.popupContent} - + ))} {children} @@ -86,5 +65,5 @@ function LazyMap({ ); } -export type { TLazyMapProps, TMarker }; +export type { TLazyMapProps }; export default LazyMap; diff --git a/src/components/map/map-marker.tsx b/src/components/map/map-marker.tsx new file mode 100644 index 0000000..8cca2d6 --- /dev/null +++ b/src/components/map/map-marker.tsx @@ -0,0 +1,39 @@ +import { Marker, Popup } from "react-leaflet"; +import L from "leaflet"; +import IconMarker from "../ui/icons/marker"; +import { ReactNode } from "react"; +import { useClientRenderToString } from "@/hooks/useClientRenderToString"; + +type TMapMarkerProps = { + marker: TMarker; + index: number; +}; + +type TMarker = { + type: "parking" | "wall"; + position: [number, number]; + popupContent?: ReactNode; +}; + +function MapMarker({ marker, index }: TMapMarkerProps) { + const [icon] = useClientRenderToString(); + + return ( + + {marker.popupContent} + + ); +} + +export type { TMarker }; +export default MapMarker; diff --git a/src/components/map/map.css b/src/components/map/map.css index 909a9d7..b347b47 100644 --- a/src/components/map/map.css +++ b/src/components/map/map.css @@ -1,6 +1,6 @@ .leaflet-popup-content-wrapper, .leaflet-popup-tip { - @apply bg-white shadow-lg; + @apply bg-white shadow-lg; } .leaflet-popup-content-wrapper { diff --git a/src/hooks/useClientRenderToString.tsx b/src/hooks/useClientRenderToString.tsx new file mode 100644 index 0000000..7dabb73 --- /dev/null +++ b/src/hooks/useClientRenderToString.tsx @@ -0,0 +1,41 @@ +import { useEffect, useState } from "react"; +import { createRoot } from "react-dom/client"; + +type TUseClientRenderToString = ( + input: React.ReactElement | React.ReactElement[], + deps?: any[] +) => string[]; + +const clientRenderToString = (element: React.ReactElement): Promise => + new Promise((resolve) => { + const container = document.createElement("div"); + const renderCallback = () => { + resolve(container.firstElementChild?.innerHTML || ""); + }; + + createRoot(container).render(
{element}
); + }); + +export const useClientRenderToString: TUseClientRenderToString = ( + input, + deps = [] +) => { + const [htmlStringList, setHtmlStringList] = useState([]); + const elementList = Array.isArray(input) ? input : [input]; + + useEffect(() => { + (async () => { + const markupPromises = elementList.map(clientRenderToString); + const markup: string[] = await Promise.all(markupPromises); + + if (!setHtmlStringList) { + return; + } + + setHtmlStringList(markup); + })(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, deps); + + return htmlStringList; +};