Skip to content

Instantly share code, notes, and snippets.

@ruvasik
Created April 28, 2023 10:49
Show Gist options
  • Save ruvasik/ebd55d354ededfdd9655d3cc7d0478cf to your computer and use it in GitHub Desktop.
Save ruvasik/ebd55d354ededfdd9655d3cc7d0478cf to your computer and use it in GitHub Desktop.
SC & TS examples
export interface IMapPoint {
x: number;
y: number;
}
export interface IMapPointItem {
title?: string;
point: IMapPoint;
}
export interface IMapLineItem {
title?: string;
start: IMapPoint;
end: IMapPoint;
}
export interface IMapElements {
points?: IMapPointItem[];
lines?: IMapLineItem[];
}
interface IRegionMapProps {
ref?: React.RefObject<SVGSVGElement>;
region: TMapRegion;
id?: string;
elements?: IMapElements;
onClick?: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
}
const SvgRegion = forwardRef(function SvgRegion(
{ region, ...props }: ISvgRegionProps,
ref: ForwardedRef<SVGSVGElement>
) {
let Tag = SvgRussia;
if (region === "world") Tag = SvgWorld;
else if (region === "eurasia") Tag = SvgEurasia;
return <Tag ref={ref} {...props} />;
});
const StyledMap = styled(SvgRegion)<{ id: string }>`
& path[id] {
fill: ${theme.palette.map.background};
cursor: pointer;
outline: none;
stroke: ${theme.palette.map.border};
&.arc {
stroke: ${APP_RED};
fill: none;
}
}
& path[id]:hover {
fill: ${theme.palette.map.background}aa;
}
`;
const useMap = (refMap: RefObject<SVGSVGElement>, reg: string) => {
const mapInstance = useRef<Svg>();
const [region, setRegion] = useState("");
useEffect(() => {
if (refMap.current && region !== reg) {
mapInstance.current = SVG(refMap.current);
setRegion(reg);
}
}, [region, refMap.current]);
return mapInstance.current || SVG();
};
function RegionMap({
region = "russia",
id,
elements,
...props
}: IRegionMapProps) {
const ref = useRef<SVGSVGElement>(null);
const [elems, setElems] = useState<SVGElement[]>([]);
const map = useMap(ref, region);
const [popover, setPopover] = useState(null);
const [popoverContent, setPopoverContent] = useState(null);
const closePopover = useCallback((event) => {
if (event) {
event.stopPropagation();
event.preventDefault();
}
setPopover(null);
}, []);
const showPopover = useCallback(function (event) {
event.stopPropagation();
event.preventDefault();
setPopover(event.target);
const data = SVG(event.target).data();
setPopoverContent(data);
}, []);
const generateElems = useCallback(() => {
{
const elemsList: SVGElement[] = [];
elements?.lines?.forEach((elem, i) => {
const arc = map
.path(
generateArcPath(
elem.end.x,
elem.end.y,
elem.start.x,
elem.start.y,
ref.current?.clientWidth
)
)
.stroke({ color: "yellow", width: POINT_SIZE / 2 })
.fill("none")
.addClass("arc")
.id("circle" + (i + 1));
elemsList.push(arc);
elemsList.push(svgTrain(map as Element, arc, 0.5, APP_RED));
});
elements?.points?.forEach((elem) => {
const { point, title } = elem;
const popover = title ? { title, text: "text" } : undefined;
elemsList.push(
svgPin(map as Element, point.x, point.y, {
onShow: popover ? showPopover : undefined,
onHide: closePopover,
data: popover,
})
);
});
setElems(elemsList);
}
}, [map, elements]);
useEffect(() => {
elems.forEach((node) => node.remove());
generateElems();
}, [map, region, elements]);
return (
<>
<StyledMap ref={ref} id={id || region} region={region} {...props} />
<Popover
open={!!popover}
anchorEl={popover}
onClose={closePopover}
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
>
<Typography sx={{ fontWeight: 700 }}>
{popoverContent?.title || ""}
</Typography>
<Typography sx={{ fontSize: "0.9rem" }}>
{popoverContent?.text || ""}
</Typography>
</Popover>
</>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment