import React , { useEffect , useState , Dispatch , SetStateAction } from "react" ; import styled from "styled-components" ; import COLOR from "@styles/Color" ; import { flexRowCenterAlign } from "@styles/StyledComponents" ; import { useSelector , useDispatch } from "react-redux" ; import { RootState } from "@src/reducer" ; import Marker from "@components/Map/Markers" ; import MapLayerPostModal from "./Modal" ; import SetClustering from "@components/Map/SetClustering" ; import ReactDOM from "react-dom" ; import PostListModal from "@components/Modal/PostListModal" ; import { MapAction , ModalAction } from "@src/action" ; import { AddressAction } from "@src/action" ; import { modal } from "@src/constants" ; declare const MarkerClustering ; declare global { interface Window { kakao : any ; } } type LatLng = any ; type Feature = any ; type Event = any ; type Coord = Point | LatLng ; type DOMEvent = Event ; interface Point { x : number ; y : number ; } interface PointerEvent { coord : Coord ; point : Point ; offset : Point ; pointerEvent : DOMEvent ; feature : Feature ; latlng : naver . maps . LatLng ; } interface PostType { postId : number ; postTitle : string ; postLatitude : number ; postLongitude : number ; } interface IPodo { content : string ; size : naver . maps . Size ; origin : naver . maps . Point ; } interface IMarkerClustering { minClusterSize : number ; maxZoom : number ; map : naver . maps . Map ; markers : Array < naver . maps . Marker > ; disableClickZoom : boolean ; gridSize : number ; icons : Array < IPodo > ; indexGenerator : Array < number > ; setMap : Function ; } interface IClustredMember extends naver . maps . MarkerOptions { postId : number ; } interface IPostsList { postsList : PostType [ ] ; } const convertWidth = ( width ) => { if ( width <= 768 ) return 4 ; if ( width <= 1100 ) return 6 ; if ( width <= 1440 ) return 8 ; if ( width <= 2000 ) return 10 ; if ( width <= 3000 ) return 12 ; if ( width <= 6000 ) return 24 ; return 30 ; } ; const setMap = ( INIT_X , INIT_Y , ZOOM_SIZE , setNaverMap , dispatch ) => { const pos = new naver . maps . LatLng ( INIT_X , INIT_Y ) ; const map = new naver . maps . Map ( "map" , { center : pos , zoom : ZOOM_SIZE , minZoom : 8 , maxZoom : 17 , } ) ; setNaverMap ( map ) ; const newClusteringWindow = new naver . maps . InfoWindow ( { content : "" , borderColor : COLOR . GRAY , borderWidth : 2 , disableAnchor : true , } ) ; const newPostCreateWindow = new naver . maps . InfoWindow ( { content : "" , maxWidth : 200 , disableAnchor : true , borderColor : "transparent" , backgroundColor : "transparent" , pixelOffset : { x : 5 * convertWidth ( window . innerWidth ) , y : 5 * convertWidth ( window . innerWidth ) } , } ) ; dispatch ( MapAction . setClusteringWindowAction ( { clusteringWindow : newClusteringWindow } ) ) ; dispatch ( MapAction . setPostCreateWindowAction ( { postCreateWindow : newPostCreateWindow } ) ) ; naver . maps . Event . addListener ( map , "rightclick" , ( e ) => { newPostCreateWindow . setContent ( [ '<div id="createPostWindow" style="border: none; width: 11rem; height: 4rem;">' ] . join ( "" ) ) ; newPostCreateWindow . open ( map , e . latlng ) ; const modalOpen = ( x , y ) => { newPostCreateWindow . close ( ) ; dispatch ( AddressAction . setAddressAction ( "" ) ) ; dispatch ( AddressAction . setPositionAction ( { x , y } ) ) ; dispatch ( ModalAction . openModalAction ( modal . UploadAddressModal ) ) ; } ; ReactDOM . render ( < React . StrictMode > < MapLayerPostModal latLng = { e . latlng } clickInfo = { e } rightPosition = { { x : e . pointerEvent . pageX , y : e . pointerEvent . pageY } } modalOpen = { modalOpen } dispatch = { dispatch } / > < /React.StrictMode>, document.getElementById("createPostWindow")); dispatch ( MapAction . setPostCreateWindowOpenedAction ( { isPostCreateWindowOpened : true } ) ) ; } ) ; naver . maps . Event . addListener ( map , "zoom_changed" , ( e ) => { newClusteringWindow . close ( ) ; } ) ; naver . maps . Event . addListener ( map , "mousedown" , ( e ) => { newClusteringWindow . close ( ) ; newPostCreateWindow . close ( ) ; } ) ; } ; const Map = ( ) => { const dispatch = useDispatch ( ) ; const { clusteringWindow , postCreateWindow , } = useSelector ( ( state ) => state . map ) ; const [ naverMap , setNaverMap ] = useState < naver . maps . Map > ( ) ; const [ currentMarkers , setCurrentMarkers ] = useState < Array < naver . maps . Marker > > ( [ ] ) ; const [ currentClustering , setCurrentClustering ] = useState < IMarkerClustering | undefined > ( undefined ) ; const { postsList } = useSelector ( ( state ) => state . groups ) ; const { selectedPost } = useSelector ( ( state ) => state . modal ) ; const { nowTheme } = useSelector ( ( state ) => state . theme ) ; const { isPostCreateWindowOpened , isClusteringWindowOpened } = useSelector ( ( state ) => state . map ) ; const closePostCreateWindow = ( ) => { if ( ! isPostCreateWindowOpened ) return ; dispatch ( MapAction . setPostCreateWindowOpenedAction ( { isPostCreateWindowOpened : false } ) ) ; } ; useEffect ( ( ) => { const initMap = ( ) => { const INIT_X = 37.511337 ; const INIT_Y = 127.012084 ; const ZOOM_SIZE = 13 ; setMap ( INIT_X , INIT_Y , ZOOM_SIZE , setNaverMap , dispatch ) ; } ; initMap ( ) ; } , [ ] ) ; useEffect ( ( ) => { if ( ! postsList ) return ; const setMarker = ( ) => { const handleClickMarker = ( clickedPostID ) => { if ( clusteringWindow ) clusteringWindow . close ( ) ; if ( postCreateWindow ) postCreateWindow . close ( ) ; closePostCreateWindow ( ) ; dispatch ( ModalAction . selectPostRequestAction ( clickedPostID ) ) ; } ; const markers = postsList . map ( ( post ) => { const pos = new naver . maps . LatLng ( post . postLatitude , post . postLongitude ) ; const marker = new naver . maps . Marker ( Marker ( naverMap as naver . maps . Map , pos , post . postTitle , post . postId ) ) ; naver . maps . Event . addListener ( marker , "click" , ( ) => handleClickMarker ( post . postId ) ) ; return marker ; } ) ; const handleClickClustering = ( members , LatLng ) => { closePostCreateWindow ( ) ; const clusteredMarker = members . map ( ( member ) => { return { postId : member . postId , postTitle : member . title } ; } ) ; if ( ! clusteringWindow ) return ; clusteringWindow . setContent ( [ '<div id="clustredMarkerList" style="width:20rem;height:10rem;">' ] . join ( "" ) ) ; if ( ! naverMap ) return ; clusteringWindow . open ( naverMap , LatLng ) ; const clustredMarkerList = document . getElementById ( "clustredMarkerList" ) as HTMLElement ; const parent = clustredMarkerList . parentNode as HTMLElement ; parent . style . width = "20rem" ; dispatch ( MapAction . setClusteringWindowOpenedAction ( { isClusteringWindowOpened : true } ) ) ; ReactDOM . render ( < React . StrictMode > < PostListModal clusteredMarker = { clusteredMarker } handleClickMarker = { handleClickMarker } nowTheme = { nowTheme } / > < /React.StrictMode>, document.getElementById("clustredMarkerList")); } ; const markerClustering = new MarkerClustering ( SetClustering ( naverMap as naver . maps . Map , markers , handleClickClustering ) ) ; setCurrentMarkers ( markers ) ; setCurrentClustering ( markerClustering ) ; } ; setMarker ( ) ; } , [ postsList ] ) ; useEffect ( ( ) => { return ( ) => { currentMarkers . forEach ( ( marker ) => { marker . setVisible ( false ) ; marker . setMap ( null ) ; } ) ; if ( ! currentClustering ) return ; currentClustering . setMap ( null ) ; } ; } , [ currentMarkers ] ) ; useEffect ( ( ) => { if ( naverMap ) { const coord = { lat : selectedPost . postLatitude , lng : selectedPost . postLongitude } ; const zoom = 15 ; const transitionOptions = { duration : 1000 , easing : "linear" } ; naverMap . morph ( coord , zoom , transitionOptions ) ; } } , [ selectedPost ] ) ; const modalOpen = ( ) => { dispatch ( AddressAction . setAddressAction ( "" ) ) ; if ( clusteringWindow ) clusteringWindow . close ( ) ; if ( postCreateWindow ) postCreateWindow . close ( ) ; dispatch ( ModalAction . openModalAction ( modal . PostCreateModal ) ) ; } ; return ( < React . Fragment > < Maps id = "map" / > < FloatActionBtn onClick = { modalOpen } > + < /FloatActionBtn> < /React.Fragment>); } ; const Maps = styled . div `
  position: absolute;
  width: 100%;
  height: 95vh;
  z-index: 1;
  &:focus-visible {
    outline: none;
  }
` ; const FloatActionBtn = styled . button `
  background-color: ${ ( props ) => props . theme . SECONDARY };
  position: absolute;
  z-index: 2;
  border-radius: 50%;
  border: none;
  height: 8vh;
  width: 8vh;
  bottom: 8vh;
  right: 8vh;
  ${ flexRowCenterAlign }
  color: ${ COLOR . WHITE };
  font-size: 7vh;
  box-shadow: 0.2vh 0.2vh 1vh 0.2vh ${ ( props ) => props . theme . PRIMARY };
  &:hover {
    cursor: pointer;
    background-color: ${ ( props ) => props . theme . PRIMARY };
  }
` ; export default Map ;