= (props) => {
25 | const {
26 | target: propTarget,
27 | type,
28 | listener,
29 | } = props;
30 |
31 | const contextTarget = useEventTarget();
32 | const target = propTarget || contextTarget;
33 | if (!target) {
34 | throw new Error('react-naver-maps: No Target to add listener');
35 | }
36 |
37 | // TODO: FIX DefinitelyTyped
38 | useListener((target as unknown) as EventTarget, type, listener);
39 |
40 | return null;
41 | };
42 |
43 | export function getListenerKeys>(props: P) {
44 | return Object.keys(props).filter(key => /on[A-Z]\w+/.test(key)) as unknown as Array>;
45 | }
46 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/load-navermaps-script.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import type { ReactElement } from 'react';
3 |
4 | import type { ClientOptions } from './types/client';
5 | import { loadScript } from './utils/load-script';
6 |
7 | export function loadNavermapsScript(options: ClientOptions) {
8 | const url = makeUrl(options);
9 |
10 | // TODO: Caching Promise
11 |
12 | const promise = loadScript(url).then(() => {
13 | const navermaps = window.naver.maps;
14 |
15 | if (navermaps.jsContentLoaded) {
16 | return navermaps;
17 | }
18 |
19 | return new Promise(resolve => {
20 | navermaps.onJSContentLoaded = () => {
21 | resolve(navermaps);
22 | };
23 | });
24 | });
25 |
26 | return promise;
27 | }
28 |
29 | function makeUrl(options: ClientOptions) {
30 | const submodules = options.submodules;
31 |
32 | const clientIdQuery = 'ncpKeyId' in options ? `ncpKeyId=${options.ncpKeyId}` :
33 | 'ncpClientId' in options
34 | ? `ncpClientId=${options.ncpClientId}`
35 | : 'govClientId' in options
36 | ? `govClientId=${options.govClientId}`
37 | : 'finClientId' in options
38 | ? `finClientId=${options.finClientId}`
39 | : undefined;
40 |
41 | if (!clientIdQuery) {
42 | throw new Error('react-naver-maps: ncpKeyId, ncpClientId, govClientId or finClientId is required');
43 | }
44 |
45 | let url = `https://oapi.map.naver.com/openapi/v3/maps.js?${clientIdQuery}`;
46 |
47 | if (submodules) {
48 | url += `&submodules=${submodules.join(',')}`;
49 | }
50 |
51 | return url;
52 | }
53 |
54 |
55 | type Props = ClientOptions & {
56 | children: () => ReactElement;
57 | };
58 |
59 | export function LoadNavermapsScript({
60 | children: Children,
61 | ...options
62 | }: Props) {
63 | const [navermaps, setNavermaps] = useState();
64 |
65 | useEffect(() => {
66 | loadNavermapsScript(options).then((maps) => {
67 | setNavermaps(maps);
68 | });
69 | }, []);
70 |
71 | return (
72 | (navermaps && Children) ? : null
73 | );
74 | }
75 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/naver-map.tsx:
--------------------------------------------------------------------------------
1 | import pick from 'lodash.pick';
2 | import upperfirst from 'lodash.upperfirst';
3 | import { forwardRef, useImperativeHandle, useLayoutEffect, useRef, useState } from 'react';
4 | import type { ReactNode } from 'react';
5 |
6 | import { useContainerContext } from './contexts/container';
7 | import { EventTargetContext } from './contexts/event-target';
8 | import { NaverMapContext } from './contexts/naver-map';
9 | import { HandleEvents } from './helpers/event';
10 | import { usePrevious } from './hooks/use-previous';
11 | import { useNavermaps } from './use-navermaps';
12 |
13 | type MapPaddingOptions = {
14 | top?: number;
15 | right?: number;
16 | bottom?: number;
17 | left?: number;
18 | };
19 |
20 | type MapOptions = {
21 | background?: string;
22 | baseTileOpacity?: number;
23 | /**
24 | * @type naver.maps.Bounds | naver.maps.BoundsLiteral | null
25 | */
26 | bounds?: naver.maps.Bounds | naver.maps.BoundsLiteral | null;
27 | /**
28 | * @type naver.maps.Coord | naver.maps.CoordLiteral
29 | */
30 | center?: naver.maps.Coord | naver.maps.CoordLiteral;
31 | disableDoubleClickZoom?: boolean;
32 | disableDoubleTapZoom?: boolean;
33 | disableKineticPan?: boolean;
34 | disableTwoFingerTapZoom?: boolean;
35 | draggable?: boolean;
36 | keyboardShortcuts?: boolean;
37 | logoControl?: boolean;
38 | logoControlOptions?: naver.maps.LogoControlOptions;
39 | mapDataControl?: boolean;
40 | mapDataControlOptions?: naver.maps.MapDataControlOptions;
41 | mapTypeControl?: boolean;
42 | mapTypeControlOptions?: naver.maps.MapTypeControlOptions;
43 | mapTypeId?: string;
44 | mapTypes?: naver.maps.MapTypeRegistry;
45 | maxBounds?: naver.maps.Bounds | naver.maps.BoundsLiteral | null;
46 | maxZoom?: number;
47 | minZoom?: number;
48 | padding?: MapPaddingOptions;
49 | pinchZoom?: boolean;
50 | resizeOrigin?: naver.maps.Position;
51 | scaleControl?: boolean;
52 | scaleControlOptions?: naver.maps.ScaleControlOptions;
53 | scrollWheel?: boolean;
54 | size?: naver.maps.Size | naver.maps.SizeLiteral;
55 | overlayZoomEffect?: string | null;
56 | tileSpare?: number;
57 | tileTransition?: boolean;
58 | zoom?: number;
59 | zoomControl?: boolean;
60 | zoomControlOptions?: naver.maps.ZoomControlOptions;
61 | zoomOrigin?: naver.maps.Coord | naver.maps.CoordLiteral | null;
62 | blankTileImage?: string | null;
63 |
64 | // special.
65 | centerPoint?: naver.maps.Point | naver.maps.PointLiteral;
66 | };
67 |
68 | type Uncontrolled = {
69 | /**
70 | * Uncontrolled prop of mapTypeId
71 | */
72 | defaultMapTypeId?: MapOptions['mapTypeId'];
73 | /**
74 | * Uncontrolled prop of size
75 | * @type naver.maps.Coord | naver.maps.CoordLiteral
76 | */
77 | defaultSize?: MapOptions['size'];
78 | /**
79 | * Uncontrolled prop of bounds
80 | * @type naver.maps.Bounds | naver.maps.BoundsLiteral | null
81 | */
82 | defaultBounds?: MapOptions['bounds'];
83 | /**
84 | * Uncontrolled prop of center
85 | * @type naver.maps.Coord | naver.maps.CoordLiteral
86 | */
87 | defaultCenter?: MapOptions['center'];
88 | /**
89 | * Uncontrolled prop of zoom
90 | */
91 | defaultZoom?: MapOptions['zoom'];
92 | /**
93 | * Uncontrolled prop of centerPoint
94 | * @type naver.maps.Point | naver.maps.PointLiteral
95 | */
96 | defaultCenterPoint?: MapOptions['centerPoint'];
97 | };
98 |
99 | type MapEventCallbacks = {
100 | onMapTypeIdChanged?: (value: string) => void;
101 | onMapTypeChanged?: (value: naver.maps.MapType) => void;
102 | onSizeChanged?: (value: naver.maps.Size) => void;
103 | onBoundsChanged?: (value: naver.maps.Bounds) => void;
104 | onCenterChanged?: (value: naver.maps.Coord) => void;
105 | onCenterPointChanged?: (value: naver.maps.Point) => void;
106 | onZoomChanged?: (value: number) => void;
107 | };
108 |
109 | const basicMapOptionKeys: Array = [
110 | 'background',
111 | 'baseTileOpacity',
112 | // 'bounds',
113 | // 'center',
114 | 'disableDoubleClickZoom',
115 | 'disableDoubleTapZoom',
116 | 'disableKineticPan',
117 | 'disableTwoFingerTapZoom',
118 | 'draggable',
119 | 'keyboardShortcuts',
120 | 'logoControl',
121 | 'logoControlOptions',
122 | 'mapDataControl',
123 | 'mapDataControlOptions',
124 | 'mapTypeControl',
125 | 'mapTypeControlOptions',
126 | // 'mapTypeId',
127 | 'mapTypes',
128 | 'maxBounds',
129 | 'maxZoom',
130 | 'minZoom',
131 | 'padding',
132 | 'pinchZoom',
133 | 'resizeOrigin',
134 | 'scaleControl',
135 | 'scaleControlOptions',
136 | 'scrollWheel',
137 | // 'size',
138 | 'overlayZoomEffect',
139 | 'tileSpare',
140 | 'tileTransition',
141 | // 'zoom',
142 | 'zoomControl',
143 | 'zoomControlOptions',
144 | 'zoomOrigin',
145 | 'blankTileImage',
146 | ];
147 |
148 | const kvoKeys = [
149 | 'mapTypeId',
150 | 'size',
151 | 'bounds',
152 | 'center',
153 | 'zoom',
154 | 'centerPoint',
155 | ] as const;
156 |
157 | const kvoEvents = [
158 | ...kvoKeys.map(key => `${key}_changed`),
159 | 'mapType_changed', // special. https://navermaps.github.io/maps.js.ncp/docs/naver.maps.Map.html#event:mapType_changed__anchor
160 | ];
161 | const uiEvents = [
162 | 'mousedown',
163 | 'mouseup',
164 | 'click',
165 | 'dblclick',
166 | 'rightclick',
167 | 'mouseover',
168 | 'mouseout',
169 | 'mousemove',
170 | 'dragstart',
171 | 'drag',
172 | 'dragend',
173 | 'touchstart',
174 | 'touchmove',
175 | 'touchend',
176 | 'pinchstart',
177 | 'pinch',
178 | 'pinchend',
179 | 'tap',
180 | 'longtap',
181 | 'twofingertap',
182 | 'doubletap',
183 | ] as const;
184 | const mapOnlyEvents = [
185 | 'addLayer',
186 | 'idle',
187 | 'init',
188 | 'keydown',
189 | 'keyup',
190 | 'panning',
191 | 'projection_changed',
192 | 'removeLayer',
193 | 'resize',
194 | 'tilesloaded',
195 | 'zooming',
196 | ] as const;
197 | const events = [...uiEvents, ...kvoEvents, ...mapOnlyEvents];
198 |
199 | // type FunctionTypeChildren = (nmap: naver.maps.Map) => React.ReactNode;
200 |
201 | const defaultOptionKeyMap = {
202 | mapTypeId: 'defaultMapTypeId',
203 | size: 'defaultSize',
204 | bounds: 'defaultBounds',
205 | center: 'defaultCenter',
206 | zoom: 'defaultZoom',
207 | centerPoint: 'defaultCenterPoint',
208 | } as const;
209 |
210 | export type Props = Uncontrolled & {
211 | /**
212 | * Map 관련 components
213 | */
214 | children?: ReactNode;
215 | } & MapOptions & MapEventCallbacks;
216 |
217 | export const NaverMap = forwardRef(function NaverMap(props, ref) {
218 | const navermaps = useNavermaps();
219 | const { element: mapDiv } = useContainerContext();
220 | const [nmap, setNmap] = useState();
221 | const nmapRef = useRef();
222 |
223 | // https://github.com/facebook/react/issues/20090
224 | useLayoutEffect(() => {
225 | if (!mapDiv) {
226 | throw new Error('react-naver-maps: MapDiv is not found. Did you correctly wrap with `MapDiv`?');
227 | }
228 |
229 | const basicMapOptions = pick(props, basicMapOptionKeys);
230 | const kvos = kvoKeys.reduce((acc, key) => {
231 | // default kvo
232 | if (props[defaultOptionKeyMap[key]]) {
233 | return {
234 | ...acc,
235 | [key]: props[defaultOptionKeyMap[key]],
236 | };
237 | }
238 |
239 | // kvo
240 | if (props[key]) {
241 | return {
242 | ...acc,
243 | [key]: props[key],
244 | };
245 | }
246 |
247 | return acc;
248 | }, {});
249 |
250 | const _nmap = new navermaps.Map(mapDiv, { ...basicMapOptions, ...kvos });
251 | setNmap(_nmap);
252 | // for ref hack
253 | nmapRef.current = _nmap;
254 |
255 | return () => {
256 | _nmap.destroy();
257 | };
258 | }, []);
259 |
260 | const uncontrolledOmittedProps = (Object.keys(props) as Array).reduce((acc, key) => {
261 | // kvo key가 defaultKvo key와 함께 있을 경우 무시한다.
262 | if (key in defaultOptionKeyMap && props[defaultOptionKeyMap[key as keyof typeof defaultOptionKeyMap]]) {
263 | return acc;
264 | }
265 |
266 | return {
267 | ...acc,
268 | [key]: props[key],
269 | };
270 | }, {}) as Props;
271 |
272 | // nmap 이 layoutEffect에서 생성되므로 항상 Map이 존재한다.
273 | useImperativeHandle(ref, () => nmapRef.current);
274 |
275 | return (
276 | <>{nmap && }>
277 | );
278 | });
279 |
280 | function NaverMapCore({ nmap, children, ...mapProps }: Props & { nmap: naver.maps.Map }) {
281 | const basicMapOptions = pick(mapProps, basicMapOptionKeys);
282 | const {
283 | mapTypeId,
284 | size,
285 | bounds,
286 | center,
287 | centerPoint,
288 | zoom,
289 | } = mapProps;
290 |
291 | const prevKVOs = usePrevious({
292 | mapTypeId,
293 | size,
294 | bounds,
295 | center,
296 | centerPoint,
297 | zoom,
298 | }, [
299 | mapTypeId,
300 | size,
301 | bounds,
302 | center,
303 | centerPoint,
304 | zoom,
305 | ]);
306 |
307 | function getDirtyKVOs(keys: Array): Pick {
308 | return keys.reduce((acc, key) => {
309 | const currentValue = nmap[`get${upperfirst(key)}` as keyof naver.maps.Map]();
310 | const propValue = mapProps[key];
311 |
312 | if (!propValue || prevKVOs && prevKVOs[key] === propValue) {
313 | return acc;
314 | }
315 |
316 | const isEqual = typeof currentValue.equals === 'function' ? currentValue.equals(propValue) : currentValue === propValue;
317 |
318 | if (isEqual) {
319 | return acc;
320 | }
321 |
322 | return {
323 | ...acc,
324 | [key]: propValue,
325 | };
326 | }, {} as Pick);
327 | }
328 |
329 | useLayoutEffect(() => {
330 | nmap.setOptions(basicMapOptions);
331 | }, [Object.values(basicMapOptions)]);
332 |
333 | useLayoutEffect(() => {
334 | const updated = getDirtyKVOs(['size']).size;
335 | if (updated) {
336 | nmap.setSize(updated);
337 | }
338 | }, [size]);
339 |
340 | useLayoutEffect(() => {
341 | const updated = getDirtyKVOs(['mapTypeId']).mapTypeId;
342 | if (updated) {
343 | nmap.setMapTypeId(updated);
344 | }
345 | }, [mapTypeId]);
346 |
347 | useLayoutEffect(() => {
348 | const dirties = getDirtyKVOs(['bounds', 'center', 'centerPoint', 'zoom']);
349 |
350 | if (dirties.bounds) {
351 | // TODO
352 | nmap.fitBounds(dirties.bounds);
353 |
354 | // Ignore rest kvos
355 | return;
356 | }
357 |
358 | if (dirties.center && dirties.zoom) {
359 |
360 | nmap.morph(dirties.center, dirties.zoom);
361 |
362 | // Ignore rest kvos
363 | return;
364 | }
365 |
366 | if (dirties.centerPoint) {
367 | nmap.setCenterPoint(dirties.centerPoint);
368 | }
369 |
370 | if (dirties.center) {
371 | // TODO
372 | nmap.panTo(dirties.center, {});
373 | }
374 |
375 | if (dirties.zoom) {
376 | nmap.setZoom(dirties.zoom);
377 | }
378 | }, [bounds, center, centerPoint, zoom]);
379 |
380 | return (
381 |
382 |
383 | <>
384 |
388 | {children}
389 | >
390 |
391 |
392 | );
393 | }
394 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/overlay.spec.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 |
3 | import { NaverMapContext } from './contexts/naver-map';
4 | import { Overlay } from './overlay';
5 |
6 | describe('', () => {
7 | it('should currectly handle contexted map on render', () => {
8 | let m: any;
9 | const element = {
10 | setMap: jest.fn((map: any) => m = map),
11 | getMap: jest.fn(() => m),
12 | };
13 | const map = {} as naver.maps.Map;
14 |
15 | const { unmount, rerender } = render(
16 |
17 | );
18 |
19 | expect(element.setMap).not.toBeCalled();
20 | rerender(
21 |
22 | );
23 |
24 | expect(element.setMap).toHaveBeenLastCalledWith(map);
25 |
26 | unmount();
27 | expect(element.setMap).toHaveBeenLastCalledWith(null);
28 | });
29 |
30 | });
31 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/overlay.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import type { ReactNode } from 'react';
3 |
4 | import { EventTargetContext } from './contexts/event-target';
5 | import { useMap } from './contexts/naver-map';
6 |
7 | type MapElementType = {
8 | setMap(map: naver.maps.Map | null): void;
9 | getMap(): naver.maps.Map | null;
10 | };
11 |
12 | export type Props = {
13 | element: MapElementType;
14 | children?: ReactNode;
15 | autoMount?: boolean;
16 | };
17 |
18 | export function Overlay(props: Props) {
19 | const { element, children, autoMount = true } = props;
20 | const nmap = useMap();
21 |
22 | useEffect(() => {
23 | if (!autoMount) {
24 | return;
25 | }
26 |
27 | if (element.getMap() === nmap) {
28 | return;
29 | }
30 |
31 | element.setMap(nmap ? nmap : null);
32 | return () => {
33 | element.setMap(null);
34 | };
35 | }, [nmap]);
36 |
37 | return (
38 |
39 | {children}
40 |
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/overlays/circle.tsx:
--------------------------------------------------------------------------------
1 | import pick from 'lodash.pick';
2 | import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
3 |
4 | import { HandleEvents } from '../helpers/event';
5 | import { Overlay } from '../overlay';
6 | import type { UIEventHandlers } from '../types/event';
7 | import { useNavermaps } from '../use-navermaps';
8 | import { omitUndefined } from '../utils/omit-undefined';
9 |
10 | const primitiveKvoKeys = [
11 | 'radius',
12 | 'strokeWeight',
13 | 'strokeOpacity',
14 | 'strokeColor',
15 | 'strokeStyle',
16 | 'strokeLineCap',
17 | 'strokeLineJoin',
18 | 'fillColor',
19 | 'fillOpacity',
20 | 'clickable',
21 | 'visible',
22 | 'zIndex',
23 | ] as const;
24 | const kvoKeys = [
25 | ...primitiveKvoKeys,
26 | 'center',
27 | ] as const;
28 | const kvoEvents = kvoKeys.map(key => `${key}_changed`);
29 | const uiEvents = [
30 | 'mousedown',
31 | 'mouseup',
32 | 'click',
33 | 'dblclick',
34 | 'rightclick',
35 | 'mouseover',
36 | 'mouseout',
37 | 'mousemove',
38 | ] as const;
39 | const events = [...uiEvents, ...kvoEvents];
40 |
41 | type CircleOptions = {
42 | /**
43 | * center
44 | * @type naver.maps.Coord | naver.maps.CoordLiteral
45 | */
46 | center: naver.maps.Coord | naver.maps.CoordLiteral;
47 | radius?: number;
48 | strokeWeight?: number;
49 | strokeOpacity?: number;
50 | strokeColor?: string;
51 | strokeStyle?: naver.maps.strokeStyleType;
52 | strokeLineCap?: naver.maps.strokeLineCapType;
53 | strokeLineJoin?: naver.maps.strokeLineJoinType;
54 | fillColor?: string;
55 | fillOpacity?: number;
56 | clickable?: boolean;
57 | visible?: boolean;
58 | zIndex?: number;
59 | };
60 |
61 | export type Props = CircleOptions & {
62 | onCenterChanged?: (value: naver.maps.Coord) => void;
63 | onRadiusChanged?: (value: number) => void;
64 | onStrokeWeightChanged?: (value: number) => void;
65 | onStrokeOpacityChanged?: (value: number) => void;
66 | onStrokeColorChanged?: (value: string) => void;
67 | onStrokeStyleChanged?: (value: naver.maps.strokeStyleType) => void;
68 | onStrokeLineCapChanged?: (value: naver.maps.strokeLineCapType) => void;
69 | onStrokeLineJoinChanged?: (value: naver.maps.strokeLineJoinType) => void;
70 | onFillColorChanged?: (value: string) => void;
71 | onFillOpacityChanged?: (value: number) => void;
72 | onClickableChanged?: (event: boolean) => void;
73 | onVisibleChanged?: (event: boolean) => void;
74 | onZIndexChanged?: (event: number) => void;
75 | } & UIEventHandlers;
76 |
77 | export const Circle = forwardRef(function Circle(props, ref) {
78 | const { center } = props;
79 | const navermaps = useNavermaps();
80 | const [circle] = useState(() => new navermaps.Circle(omitUndefined(pick(props, [...kvoKeys])) as CircleOptions));
81 |
82 | useImperativeHandle(ref, () => circle);
83 |
84 | useEffect(() => {
85 | if (center && !circle.getCenter().equals(center as naver.maps.Point)) {
86 | circle.setCenter(center);
87 | }
88 | }, [center]);
89 |
90 | useEffect(() => {
91 | circle.setOptions(omitUndefined(pick(props, primitiveKvoKeys)) as CircleOptions);
92 | }, primitiveKvoKeys.map(key => props[key]));
93 |
94 | return (
95 |
96 |
97 |
98 | );
99 | });
100 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/overlays/ellipse.tsx:
--------------------------------------------------------------------------------
1 | import pick from 'lodash.pick';
2 | import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
3 |
4 | import { HandleEvents } from '../helpers/event';
5 | import { Overlay } from '../overlay';
6 | import type { UIEventHandlers } from '../types/event';
7 | import { useNavermaps } from '../use-navermaps';
8 | import { omitUndefined } from '../utils/omit-undefined';
9 |
10 | const primitiveKvoKeys = [
11 | 'strokeWeight',
12 | 'strokeOpacity',
13 | 'strokeColor',
14 | 'strokeStyle',
15 | 'strokeLineCap',
16 | 'strokeLineJoin',
17 | 'fillColor',
18 | 'fillOpacity',
19 | 'clickable',
20 | 'visible',
21 | 'zIndex',
22 | ] as const;
23 | const kvoKeys = [
24 | ...primitiveKvoKeys,
25 | 'bounds',
26 | ] as const;
27 | const kvoEvents = kvoKeys.map(key => `${key}_changed`);
28 | const uiEvents = [
29 | 'mousedown',
30 | 'mouseup',
31 | 'click',
32 | 'dblclick',
33 | 'rightclick',
34 | 'mouseover',
35 | 'mouseout',
36 | 'mousemove',
37 | ] as const;
38 | const events = [...uiEvents, ...kvoEvents];
39 |
40 | type EllipseOptions = {
41 | /**
42 | * bounds
43 | * @type naver.maps.Bounds | naver.maps.BoundsLiteral
44 | */
45 | bounds: naver.maps.Bounds | naver.maps.BoundsLiteral;
46 | strokeWeight?: number;
47 | strokeOpacity?: number;
48 | strokeColor?: string;
49 | strokeStyle?: naver.maps.strokeStyleType;
50 | strokeLineCap?: naver.maps.strokeLineCapType;
51 | strokeLineJoin?: naver.maps.strokeLineJoinType;
52 | fillColor?: string;
53 | fillOpacity?: number;
54 | clickable?: boolean;
55 | visible?: boolean;
56 | zIndex?: number;
57 | };
58 |
59 | export type Props = EllipseOptions & {
60 | onBoundsChanged?: (value: naver.maps.Bounds) => void;
61 | onStrokeWeightChanged?: (value: number) => void;
62 | onStrokeOpacityChanged?: (value: number) => void;
63 | onStrokeColorChanged?: (value: string) => void;
64 | onStrokeStyleChanged?: (value: naver.maps.strokeStyleType) => void;
65 | onStrokeLineCapChanged?: (value: naver.maps.strokeLineCapType) => void;
66 | onStrokeLineJoinChanged?: (value: naver.maps.strokeLineJoinType) => void;
67 | onFillColorChanged?: (value: string) => void;
68 | onFillOpacityChanged?: (value: number) => void;
69 | onClickableChanged?: (event: boolean) => void;
70 | onVisibleChanged?: (event: boolean) => void;
71 | onZIndexChanged?: (event: number) => void;
72 | } & UIEventHandlers;
73 |
74 | export const Ellipse = forwardRef(function Ellipse(props, ref) {
75 | const { bounds } = props;
76 | const navermaps = useNavermaps();
77 | const [ellipse] = useState(() => new navermaps.Ellipse(omitUndefined(pick(props, [...kvoKeys])) as EllipseOptions));
78 |
79 | useImperativeHandle(ref, () => ellipse);
80 |
81 | useEffect(() => {
82 | ellipse.setOptions(omitUndefined(pick(props, primitiveKvoKeys)) as EllipseOptions); // TODO: FIX DefinilyTyped. setOptions의 assign type 은 Partial 이어야 함
83 | }, primitiveKvoKeys.map(key => props[key]));
84 |
85 | useEffect(() => {
86 | if (bounds && ellipse.getBounds().equals(bounds as naver.maps.Bounds)) {
87 | ellipse.setBounds(bounds);
88 | }
89 | }, [bounds]);
90 |
91 | return (
92 |
93 |
94 |
95 | );
96 | });
97 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/overlays/ground-overlay.tsx:
--------------------------------------------------------------------------------
1 | import pick from 'lodash.pick';
2 | import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
3 |
4 | import { HandleEvents } from '../helpers/event';
5 | import { Overlay } from '../overlay';
6 | import type { UIEventHandlers } from '../types/event';
7 | import { useNavermaps } from '../use-navermaps';
8 |
9 | const kvoKeys = [
10 | 'clickable',
11 | 'opacity',
12 | ] as const;
13 | const kvoEvents = kvoKeys.map(key => `${key}_changed`);
14 | const uiEvents = [
15 | 'mousedown',
16 | 'mouseup',
17 | 'click',
18 | 'dblclick',
19 | 'rightclick',
20 | 'mouseover',
21 | 'mouseout',
22 | 'mousemove',
23 | ] as const;
24 | const events = [...uiEvents, ...kvoEvents];
25 |
26 | type GroundOverlayOptions = {
27 | clickable?: boolean;
28 | opacity?: number;
29 | };
30 |
31 | export type Props = GroundOverlayOptions & {
32 | url: string;
33 | /**
34 | * bounds
35 | * @type naver.maps.Bounds | naver.maps.BoundsLiteral
36 | */
37 | bounds: naver.maps.Bounds | naver.maps.BoundsLiteral;
38 | onOpacityChanged?: (value: number) => void;
39 | onClickableChanged?: (event: boolean) => void;
40 | } & UIEventHandlers;
41 |
42 | export const GroundOverlay = forwardRef(function GroundOverlay(props, ref) {
43 | const options = pick(props, kvoKeys);
44 | const { url, bounds } = props;
45 | const navermaps = useNavermaps();
46 | const [groundOverlay, setGroundOverlay] = useState(() => new navermaps.GroundOverlay(url, bounds, options));
47 |
48 | useImperativeHandle(ref, () => groundOverlay, [groundOverlay]);
49 |
50 | useEffect(() => {
51 | if (groundOverlay.getUrl() !== url || groundOverlay.getBounds().equals(bounds as naver.maps.Bounds)) {
52 | setGroundOverlay(new naver.maps.GroundOverlay(url, bounds, options));
53 | }
54 | }, [url, bounds]);
55 |
56 | useEffect(() => {
57 | kvoKeys.forEach(key => {
58 | if (options[key] && groundOverlay.get(key) !== options[key]) {
59 | groundOverlay.set(key, options[key]);
60 | }
61 | });
62 | }, kvoKeys.map(key => options[key]));
63 |
64 | return (
65 |
66 |
67 |
68 | );
69 | });
70 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/overlays/info-window.tsx:
--------------------------------------------------------------------------------
1 | import pick from 'lodash.pick';
2 | import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
3 |
4 | import { HandleEvents } from '../helpers/event';
5 | import { Overlay } from '../overlay';
6 | import type { UIEventHandlers } from '../types/event';
7 | import { useNavermaps } from '../use-navermaps';
8 | import { omitUndefined } from '../utils/omit-undefined';
9 |
10 | const primitiveKvoKeys = [
11 | 'content',
12 | 'zIndex',
13 | 'maxWidth',
14 | 'pixelOffset',
15 | 'backgroundColor',
16 | 'borderColor',
17 | 'borderWidth',
18 | 'disableAutoPan',
19 | 'disableAnchor',
20 | 'anchorSkew',
21 | 'anchorSize',
22 | 'anchorColor',
23 | ] as const;
24 | const kvoKeys = [
25 | ...primitiveKvoKeys,
26 | 'position',
27 | ] as const;
28 | const kvoEvents = kvoKeys.map(key => `${key}_changed`);
29 | const uiEvents = [
30 | 'mousedown',
31 | 'mouseup',
32 | 'click',
33 | 'dblclick',
34 | 'rightclick',
35 | 'mouseover',
36 | 'mouseout',
37 | 'mousemove',
38 | ] as const;
39 | const events = [...uiEvents, ...kvoEvents];
40 |
41 | type InfoWindowOptions = {
42 | /**
43 | * position
44 | * @type naver.maps.Coord | naver.maps.CoordLiteral
45 | */
46 | position?: naver.maps.Coord | naver.maps.CoordLiteral;
47 | content: string;
48 | zIndex?: number;
49 | maxWidth?: number;
50 | /**
51 | * @type naver.maps.Point | naver.maps.PointLiteral
52 | */
53 | pixelOffset?: naver.maps.Point | naver.maps.PointLiteral;
54 | backgroundColor?: string;
55 | borderColor?: string;
56 | borderWidth?: number;
57 | disableAutoPan?: boolean;
58 | disableAnchor?: boolean;
59 | anchorSkew?: boolean;
60 | /**
61 | * @type naver.maps.Size | naver.maps.SizeLiteral
62 | */
63 | anchorSize?: naver.maps.Size | naver.maps.SizeLiteral;
64 | anchorColor?: string;
65 | };
66 |
67 | export type Props = InfoWindowOptions & {
68 | onPositionChanged?: (value: naver.maps.Coord) => void;
69 | onContentChanged?: (value: HTMLElement) => void;
70 | onZIndexChanged?: (value: number) => void;
71 | onMaxWidthChanged?: (value: number) => void;
72 | onPixelOffsetChanged?: (value: naver.maps.Point) => void;
73 | onBackgroundColorChanged?: (value: string) => void;
74 | onBorderColorChanged?: (value: string) => void;
75 | onBorderWidthChanged?: (value: number) => void;
76 | onDisableAutoPanChanged?: (value: boolean) => void;
77 | onDisableAnchorChanged?: (value: boolean) => void;
78 | onAnchorSkewChanged?: (value: boolean) => void;
79 | onAnchorSizeChanged?: (value: naver.maps.Size) => void;
80 | onAnchorColorChanged?: (value: string) => void;
81 | } & UIEventHandlers;
82 |
83 | export const InfoWindow = forwardRef(function InfoWindow(props, ref) {
84 | const { position } = props;
85 | const navermaps = useNavermaps();
86 | const [infoWindow] = useState(() => new navermaps.InfoWindow(omitUndefined(pick(props, [...kvoKeys])) as InfoWindowOptions));
87 |
88 | useImperativeHandle(ref, () => infoWindow);
89 |
90 | useEffect(() => {
91 | infoWindow.setOptions(omitUndefined(pick(props, primitiveKvoKeys)) as InfoWindowOptions); // TODO: FIX DefinilyTyped
92 | }, primitiveKvoKeys.map(key => props[key]));
93 |
94 | useEffect(() => {
95 | if (position && infoWindow.getPosition().equals(position as naver.maps.Point)) {
96 | infoWindow.setPosition(position);
97 | }
98 | }, [position]);
99 |
100 | return (
101 |
102 |
103 |
104 | );
105 | });
106 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/overlays/marker.spec.tsx:
--------------------------------------------------------------------------------
1 | import { render, waitFor } from '@testing-library/react';
2 | import omit from 'lodash.omit';
3 | import { ReactElement, Suspense } from 'react';
4 |
5 | import { NaverMapContext } from '../contexts/naver-map';
6 | import { Marker } from './marker';
7 |
8 | const map = {} as naver.maps.Map;
9 | function renderOverlay(overlay: ReactElement) {
10 | return render(overlay, {
11 | wrapper: ({ children }) => (
12 |
13 | {children}
14 |
15 | ),
16 | });
17 | }
18 | const mockPosition = { equals: jest.fn(() => false) };
19 | let options = {} as naver.maps.MarkerOptions;
20 | const mockMethods = {
21 | getMap: jest.fn(),
22 | setMap: jest.fn(),
23 | getOptions: jest.fn((key: keyof naver.maps.MarkerOptions) => Object.assign({ [key]: options[key] })),
24 | setOptions: jest.fn((opt: any) => {
25 | Object.assign(options, opt, { position: mockPosition });
26 | }),
27 | getPosition: jest.fn(() => mockPosition),
28 | setPosition: jest.fn(),
29 | };
30 | const mockMarker = jest.fn().mockImplementation((opt) => {
31 | Object.assign(options, opt, { position: mockPosition });
32 | return mockMethods;
33 | });
34 |
35 | describe('', () => {
36 | beforeEach(() => {
37 | options = {};
38 | mockMarker.mockClear();
39 | Object.values(mockMethods).forEach(mock => mock.mockClear());
40 | // @ts-expect-error mocking navermaps client loader
41 | window.naver = { maps: { Marker: mockMarker } };
42 | });
43 |
44 | it('should currectly handle options without props', async () => {
45 | const { rerender, unmount } = renderOverlay();
46 | await waitFor(() => expect(window.naver.maps).toBeTruthy());
47 |
48 | expect(mockMethods.setMap).toBeCalledWith(map);
49 | expect(mockMethods.setPosition).not.toBeCalled();
50 | expect(mockMethods.setOptions).not.toBeCalled();
51 |
52 | const position = {} as naver.maps.Point;
53 | rerender();
54 | expect(mockMethods.setPosition).toBeCalledWith(position);
55 |
56 | unmount();
57 | expect(mockMethods.setMap).toBeCalledWith(null);
58 | });
59 |
60 | it('should currectly handle options with props', () => {
61 | const position = {} as naver.maps.Point;
62 | const animation = 0;
63 | const icon = '';
64 | const shape = {} as naver.maps.MarkerShape;
65 | const title = 'title';
66 | const cursor = '';
67 | const clickable = true;
68 | const draggable = true;
69 | const visible = true;
70 | const zIndex = 0;
71 |
72 | const { rerender } = renderOverlay();
84 |
85 | expect(mockMethods.setMap).toBeCalledWith(map);
86 | expect(omit(options, ['position'])).toEqual({
87 | animation,
88 | icon,
89 | shape,
90 | title,
91 | cursor,
92 | clickable,
93 | draggable,
94 | visible,
95 | zIndex,
96 | });
97 |
98 | const diffPosition1 = {} as naver.maps.Coord;
99 | const diffTitle1 = 'title1';
100 | const sameClickable1 = true;
101 |
102 | rerender();
103 | expect(mockMethods.setPosition).toBeCalledWith(diffPosition1);
104 | expect(mockMethods.setOptions).toBeCalledWith({ title: diffTitle1 });
105 | });
106 |
107 | it('should ignore change when uncontrolled props is set', () => {
108 | const position = {} as naver.maps.Point;
109 | const title = 'title';
110 |
111 | const { rerender } = renderOverlay();
115 |
116 | const position1 = {} as naver.maps.Coord;
117 | const title1 = 'title1';
118 |
119 | // position, defaultPosition 어느것이 변경되더라도 position이 변경되지 않아야한다.
120 | const prevCallCount = mockMethods.setPosition.mock.calls.length;
121 | rerender();
122 | expect(prevCallCount).toBe(mockMethods.setPosition.mock.calls.length);
123 | expect(mockMethods.setOptions).toBeCalledWith({ title: title1 });
124 | });
125 |
126 | it('should ignore position change when position is equal', () => {
127 | const position = {} as naver.maps.Point;
128 |
129 | const { rerender } = renderOverlay();
132 |
133 | const prevCallCount = mockMethods.setPosition.mock.calls.length;
134 | mockPosition.equals.mockImplementationOnce(() => true);
135 | rerender();
136 | expect(prevCallCount).toBe(mockMethods.setPosition.mock.calls.length);
137 | });
138 | });
139 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/overlays/marker.tsx:
--------------------------------------------------------------------------------
1 | import mapKeys from 'lodash.mapkeys';
2 | import pick from 'lodash.pick';
3 | import { forwardRef, useLayoutEffect, useImperativeHandle, useRef, useState } from 'react';
4 | import { useFirstMountState } from 'react-use';
5 |
6 | import { HandleEvents } from '../helpers/event';
7 | import { Overlay } from '../overlay';
8 | import type { UIEventHandlers } from '../types/event';
9 | import { useNavermaps } from '../use-navermaps';
10 | import { getKeys } from '../utils/get-keys';
11 | import { omitUndefined } from '../utils/omit-undefined';
12 | import { getUncontrolledKey, makeUncontrolledKeyMap, UncontrolledKey } from '../utils/uncontrolled';
13 |
14 | const primitiveKeys = [
15 | 'animation',
16 | 'icon',
17 | 'shape',
18 | 'title',
19 | 'cursor',
20 | 'clickable',
21 | 'draggable',
22 | 'visible',
23 | 'zIndex',
24 | ] as const;
25 | const locationalKeys = ['position'] as const;
26 | const uncontrolledKeyMap = makeUncontrolledKeyMap(locationalKeys);
27 | const kvoKeys = [
28 | ...primitiveKeys,
29 | ...locationalKeys,
30 | ] as const;
31 | const kvoEvents = kvoKeys.map(key => `${key}_changed`);
32 | const uiEvents = [
33 | 'mousedown',
34 | 'mouseup',
35 | 'click',
36 | 'dblclick',
37 | 'rightclick',
38 | 'mouseover',
39 | 'mouseout',
40 | 'dragstart',
41 | 'drag',
42 | 'dragend',
43 | ] as const;
44 | const events = [...uiEvents, ...kvoEvents];
45 |
46 | type MarkerKVO = {
47 | /** Animation??? */
48 | animation: naver.maps.Animation;
49 | position: naver.maps.Coord | naver.maps.CoordLiteral;
50 | icon: string | naver.maps.ImageIcon | naver.maps.SymbolIcon | naver.maps.HtmlIcon;
51 | shape: naver.maps.MarkerShape;
52 | title: string;
53 | cursor: string;
54 | clickable: boolean;
55 | draggable: boolean;
56 | visible: boolean;
57 | zIndex: number;
58 | };
59 |
60 | type UncontrolledProps = {
61 | [key in typeof locationalKeys[number] as UncontrolledKey]: MarkerKVO[key];
62 | };
63 |
64 | // TODO: Fix DefinitelyTyped
65 | type MarkerOptions = Partial;
66 |
67 | export type Props = MarkerOptions & Partial & UIEventHandlers & {
68 | onAnimationChanged?: (value: naver.maps.Animation) => void;
69 | onPositionChanged?: (value: naver.maps.Coord) => void;
70 | onIconChanged?: (value: string | naver.maps.ImageIcon | naver.maps.HtmlIcon | naver.maps.SymbolIcon) => void;
71 | onShapeChanged?: (event: naver.maps.MarkerShape) => void;
72 | onTitleChanged?: (event: string) => void;
73 | onCursorChanged?: (event: string) => void;
74 | onClickableChanged?: (event: boolean) => void;
75 | onDraggableChanged?: (event: boolean) => void;
76 | onVisibleChanged?: (event: boolean) => void;
77 | /**
78 | * hello yeah
79 | * @param event helo?
80 | * @returns
81 | */
82 | onZIndexChanged?: (event: number) => void;
83 | };
84 |
85 | function makeInitialOption(props: Props) {
86 | const uncontrolledProps = pick(props, getKeys(uncontrolledKeyMap));
87 | const prefixCleared = mapKeys(uncontrolledProps, (_, key) => uncontrolledKeyMap[key as keyof typeof uncontrolledKeyMap]);
88 | const kvoProps = pick(props, kvoKeys);
89 |
90 | return omitUndefined({ ...kvoProps, ...prefixCleared });
91 | }
92 |
93 | function isLocationalKey(key: string): key is typeof locationalKeys[number] {
94 | return locationalKeys.includes(key as typeof locationalKeys[number]);
95 | }
96 |
97 | function isEqualKvo(kvo: any, target: any) {
98 | if (kvo === undefined) {
99 | return false;
100 | }
101 |
102 | if (kvo === target) {
103 | return true;
104 | }
105 |
106 | try {
107 | return kvo.equals(target);
108 | } catch {
109 | return kvo === target;
110 | }
111 | }
112 |
113 | export const Marker = forwardRef(function Marker(props, ref) {
114 | const navermaps = useNavermaps();
115 | const [marker] = useState(() => new navermaps.Marker(makeInitialOption(props)));
116 | useImperativeHandle(ref, () => marker);
117 |
118 | // make dirties
119 | const isFirst = useFirstMountState();
120 | const dirtiesRef = useRef>({});
121 | dirtiesRef.current = getDirties();
122 |
123 | function getDirties() {
124 | // initialize의 option과 중복되지 않도록 첫 렌더시 제외한다.
125 | if (isFirst) {
126 | return {};
127 | }
128 |
129 | return kvoKeys.reduce((acc, key) => {
130 | if (props[key] === undefined) {
131 | return acc;
132 | }
133 |
134 | if (isLocationalKey(key) && props[getUncontrolledKey(key)] !== undefined) {
135 | return acc;
136 | }
137 |
138 | const kvos = marker.getOptions(key);
139 | if (isEqualKvo(kvos[key], props[key])) {
140 | return acc;
141 | }
142 |
143 | return {
144 | ...acc,
145 | [key]: props[key],
146 | };
147 | }, {});
148 | }
149 |
150 | function pickDirties(keys: readonly string[]) {
151 | return pick(dirtiesRef.current, keys);
152 | }
153 |
154 | // side effects
155 | useLayoutEffect(() => {
156 | const { position } = pickDirties(['position']);
157 | if (position) {
158 | marker.setPosition(position);
159 | }
160 | }, [dirtiesRef.current['position']]);
161 |
162 | useLayoutEffect(() => {
163 | const dirties = pickDirties(primitiveKeys);
164 | if (Object.values(dirties).length < 1) {
165 | return;
166 | }
167 |
168 | marker.setOptions(dirties);
169 | }, primitiveKeys.map(key => dirtiesRef.current[key]));
170 |
171 | return (
172 |
173 |
174 |
175 | );
176 | });
177 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/overlays/polygon.tsx:
--------------------------------------------------------------------------------
1 | import pick from 'lodash.pick';
2 | import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
3 |
4 | import { HandleEvents } from '../helpers/event';
5 | import { Overlay } from '../overlay';
6 | import type { UIEventHandlers } from '../types/event';
7 | import { useNavermaps } from '../use-navermaps';
8 | import { omitUndefined } from '../utils/omit-undefined';
9 |
10 | const kvoKeys = [
11 | 'paths',
12 | 'strokeWeight',
13 | 'strokeOpacity',
14 | 'strokeColor',
15 | 'strokeStyle',
16 | 'strokeLineCap',
17 | 'strokeLineJoin',
18 | 'fillColor',
19 | 'fillOpacity',
20 | 'clickable',
21 | 'visible',
22 | 'zIndex',
23 | ] as const;
24 | const kvoEvents = kvoKeys.map(key => `${key}_changed`);
25 | const uiEvents = [
26 | 'mousedown',
27 | 'mouseup',
28 | 'click',
29 | 'dblclick',
30 | 'rightclick',
31 | 'mouseover',
32 | 'mouseout',
33 | 'mousemove',
34 | ] as const;
35 | const events = [...uiEvents, ...kvoEvents];
36 |
37 | type PolygonOptions = {
38 | /**
39 | * @type naver.maps.ArrayOfCoords[] | naver.maps.KVOArrayOfCoords[] | naver.maps.ArrayOfCoordsLiteral[]
40 | */
41 | paths: naver.maps.ArrayOfCoords[] | naver.maps.KVOArrayOfCoords[] | naver.maps.ArrayOfCoordsLiteral[];
42 | strokeWeight?: number;
43 | strokeOpacity?: number;
44 | strokeColor?: string;
45 | strokeStyle?: naver.maps.strokeStyleType;
46 | strokeLineCap?: naver.maps.strokeLineCapType;
47 | strokeLineJoin?: naver.maps.strokeLineJoinType;
48 | fillColor?: string;
49 | fillOpacity?: number;
50 | clickable?: boolean;
51 | visible?: boolean;
52 | zIndex?: number;
53 | };
54 |
55 | export type Props = PolygonOptions & {
56 | onPathsChanged?: (value: Array) => void;
57 | onStrokeWeightChanged?: (value: number) => void;
58 | onStrokeOpacityChanged?: (value: number) => void;
59 | onStrokeColorChanged?: (value: string) => void;
60 | onStrokeStyleChanged?: (value: naver.maps.strokeStyleType) => void;
61 | onStrokeLineCapChanged?: (value: naver.maps.strokeLineCapType) => void;
62 | onStrokeLineJoinChanged?: (value: naver.maps.strokeLineJoinType) => void;
63 | onFillColorChanged?: (value: string) => void;
64 | onFillOpacityChanged?: (value: number) => void;
65 | onClickableChanged?: (event: boolean) => void;
66 | onVisibleChanged?: (event: boolean) => void;
67 | onZIndexChanged?: (event: number) => void;
68 | } & UIEventHandlers;
69 |
70 | export const Polygon = forwardRef(function Polygon(props, ref) {
71 | const options = pick(props, [...kvoKeys]);
72 | const navermaps = useNavermaps();
73 | const [polygon] = useState(() => new navermaps.Polygon(options));
74 |
75 | useImperativeHandle(ref, () => polygon);
76 |
77 | useEffect(() => {
78 | polygon.setOptions(omitUndefined(options) as PolygonOptions); // TODO: FIX DefinilyTyped. setOptions의 assign type 은 Partial 이어야 함
79 | }, kvoKeys.map(key => options[key]));
80 |
81 | return (
82 |
83 |
84 |
85 | );
86 | });
87 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/overlays/polyline.tsx:
--------------------------------------------------------------------------------
1 | import pick from 'lodash.pick';
2 | import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
3 |
4 | import { HandleEvents } from '../helpers/event';
5 | import { Overlay } from '../overlay';
6 | import type { UIEventHandlers } from '../types/event';
7 | import { useNavermaps } from '../use-navermaps';
8 | import { omitUndefined } from '../utils/omit-undefined';
9 |
10 | const kvoKeys = [
11 | 'path',
12 | 'strokeWeight',
13 | 'strokeOpacity',
14 | 'strokeColor',
15 | 'strokeStyle',
16 | 'strokeLineCap',
17 | 'strokeLineJoin',
18 | 'clickable',
19 | 'visible',
20 | 'zIndex',
21 | 'startIcon',
22 | 'startIconSize',
23 | 'endIcon',
24 | 'endIconSize',
25 | ] as const;
26 | const kvoEvents = kvoKeys.map(key => `${key}_changed`);
27 | const uiEvents = [
28 | 'mousedown',
29 | 'mouseup',
30 | 'click',
31 | 'dblclick',
32 | 'rightclick',
33 | 'mouseover',
34 | 'mouseout',
35 | 'mousemove',
36 | ] as const;
37 | const events = [...uiEvents, ...kvoEvents];
38 |
39 | type PolylineOptions = {
40 | /**
41 | * @type naver.maps.ArrayOfCoords | naver.maps.KVOArrayOfCoords | naver.maps.ArrayOfCoordsLiteral
42 | */
43 | path: naver.maps.ArrayOfCoords | naver.maps.KVOArrayOfCoords | naver.maps.ArrayOfCoordsLiteral;
44 | strokeWeight?: number;
45 | strokeOpacity?: number;
46 | strokeColor?: string;
47 | strokeStyle?: naver.maps.strokeStyleType;
48 | strokeLineCap?: naver.maps.strokeLineCapType;
49 | strokeLineJoin?: naver.maps.strokeLineJoinType;
50 | clickable?: boolean;
51 | visible?: boolean;
52 | zIndex?: number;
53 | startIcon?: naver.maps.PointingIcon;
54 | startIconSize?: number;
55 | endIcon?: naver.maps.PointingIcon;
56 | endIconSize?: number;
57 | };
58 |
59 | export type Props = PolylineOptions & {
60 | onPathChanged?: (value: naver.maps.ArrayOfCoords) => void;
61 | onStrokeWeightChanged?: (value: number) => void;
62 | onStrokeOpacityChanged?: (value: number) => void;
63 | onStrokeColorChanged?: (value: string) => void;
64 | onStrokeStyleChanged?: (value: naver.maps.strokeStyleType) => void;
65 | onStrokeLineCapChanged?: (value: naver.maps.strokeLineCapType) => void;
66 | onStrokeLineJoinChanged?: (value: naver.maps.strokeLineJoinType) => void;
67 | onClickableChanged?: (value: boolean) => void;
68 | onVisibleChanged?: (value: boolean) => void;
69 | onZIndexChanged?: (value: number) => void;
70 | onStartIconChanged?: (value: naver.maps.PointingIcon) => void;
71 | onStartIconSizeChanged?: (number: string) => void;
72 | onEndIconChanged?: (value: naver.maps.PointingIcon) => void;
73 | onEndIconSizeChanged?: (number: string) => void;
74 | } & UIEventHandlers;
75 |
76 | export const Polyline = forwardRef(function Polyline(props, ref) {
77 | const options = pick(props, [...kvoKeys]);
78 | const navermaps = useNavermaps();
79 | const [polyline] = useState(() => new navermaps.Polyline(options));
80 |
81 | useImperativeHandle(ref, () => polyline);
82 |
83 | useEffect(() => {
84 | polyline.setOptions(omitUndefined(options) as PolylineOptions); // TODO: FIX DefinilyTyped. setOptions의 assign type 은 Partial 이어야 함
85 | }, kvoKeys.map(key => options[key]));
86 |
87 | return (
88 |
89 |
90 |
91 | );
92 | });
93 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/overlays/rectangle.tsx:
--------------------------------------------------------------------------------
1 | import pick from 'lodash.pick';
2 | import { useEffect, useState, forwardRef, useImperativeHandle } from 'react';
3 |
4 | import { HandleEvents } from '../helpers/event';
5 | import { Overlay } from '../overlay';
6 | import type { UIEventHandlers } from '../types/event';
7 | import { useNavermaps } from '../use-navermaps';
8 | import { omitUndefined } from '../utils/omit-undefined';
9 |
10 | const optionKeys = [
11 | 'strokeWeight',
12 | 'strokeOpacity',
13 | 'strokeColor',
14 | 'strokeStyle',
15 | 'strokeLineCap',
16 | 'strokeLineJoin',
17 | 'fillColor',
18 | 'fillOpacity',
19 | ] as const;
20 | const kvoKeys = [
21 | 'bounds',
22 | 'clickable',
23 | 'visible',
24 | 'zIndex',
25 | ] as const;
26 | const kvoEvents = kvoKeys.map(key => `${key}_changed`);
27 | const uiEvents = [
28 | 'click',
29 | 'dblclick',
30 | 'mousedown',
31 | 'mouseout',
32 | 'mouseover',
33 | 'mouseup',
34 | ] as const;
35 | const events = [...uiEvents, ...kvoEvents];
36 |
37 | type RectangleOptions = {
38 | /**
39 | * @type naver.maps.Bounds | naver.maps.BoundsLiteral
40 | */
41 | bounds: naver.maps.Bounds | naver.maps.BoundsLiteral;
42 | strokeWeight?: number;
43 | strokeOpacity?: number;
44 | strokeColor?: string;
45 | strokeStyle?: naver.maps.strokeStyleType;
46 | strokeLineCap?: naver.maps.strokeLineCapType;
47 | strokeLineJoin?: naver.maps.strokeLineJoinType;
48 | fillColor?: string;
49 | fillOpacity?: number;
50 | clickable?: boolean;
51 | visible?: boolean;
52 | zIndex?: number;
53 | };
54 |
55 | export type Props = RectangleOptions & {
56 | onBoundsChanged?: (value: naver.maps.Bounds) => void;
57 | onClickableChanged?: (value: boolean) => void;
58 | onVisibleChanged?: (value: boolean) => void;
59 | onZIndexChanged?: (value: number) => void;
60 | } & UIEventHandlers;
61 |
62 | export const Rectangle = forwardRef(function Rectangle(props, ref) {
63 | const options = pick(props, [...optionKeys, ...kvoKeys]);
64 | const navermaps = useNavermaps();
65 | const [rectangle] = useState(() => new navermaps.Rectangle(options));
66 |
67 | useImperativeHandle(ref, () => rectangle);
68 |
69 | useEffect(() => {
70 | rectangle.setOptions(omitUndefined(options) as RectangleOptions); // TODO: FIX DefinilyTyped. setOptions의 assign type 은 Partial 이어야 함
71 | }, kvoKeys.map(key => options[key]));
72 |
73 | return (
74 |
75 |
76 |
77 | );
78 | });
79 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/provider.tsx:
--------------------------------------------------------------------------------
1 | import type { ReactNode } from 'react';
2 |
3 | import { ClientOptionsContext } from './contexts/client-options';
4 | import type { ClientOptions } from './types/client';
5 |
6 | export type Props = ClientOptions & { children?: ReactNode };
7 |
8 | export function NavermapsProvider({
9 | children,
10 | ...clientOptions
11 | }: Props) {
12 | return (
13 |
14 | {children}
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/types/case.ts:
--------------------------------------------------------------------------------
1 | export type SnakeToCamelCase =
2 | S extends `${infer T}_${infer U}` ?
3 | `${T}${Capitalize>}` :
4 | S;
5 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/types/client.ts:
--------------------------------------------------------------------------------
1 | export type CommonOptions = {
2 | submodules?: string[];
3 | /**
4 | * ncpKeyId로 대체됨
5 | * */
6 | ncpKeyId: string;
7 | };
8 |
9 | /** @deprecated */
10 | export type NcpOptions = {
11 | submodules?: string[];
12 | /**
13 | * @deprecated ncpKeyId로 대체
14 | * ncpClientId, govClientId, finClientId 중 선택
15 | */
16 | ncpClientId: string;
17 | };
18 |
19 | /** @deprecated */
20 | export type GovOptions = {
21 | submodules?: string[];
22 | /**
23 | * @deprecated ncpKeyId로 대체
24 | * ncpClientId, govClientId, finClientId 중 선택
25 | */
26 | govClientId: string;
27 | };
28 |
29 | /** @deprecated */
30 | export type finOptions = {
31 | submodules?: string[];
32 | /**
33 | * @deprecated ncpKeyId로 대체
34 | * ncpClientId, govClientId, finClientId 중 선택
35 | */
36 | finClientId: string;
37 | };
38 |
39 | export type ClientOptions = CommonOptions | NcpOptions | GovOptions | finOptions;
40 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/types/event.ts:
--------------------------------------------------------------------------------
1 | export type UIEventHandlers = Partial}`, (e: naver.maps.PointerEvent) => void>>;
2 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/types/utils.ts:
--------------------------------------------------------------------------------
1 |
2 | type FilterFlags = {
3 | [Key in keyof Base]: Key extends Condition ? Key : never
4 | };
5 |
6 | export type AllowedKey = FilterFlags[keyof Base];
7 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/use-navermaps.spec.tsx:
--------------------------------------------------------------------------------
1 | import { renderHook, fireEvent, waitFor } from '@testing-library/react';
2 | import { Suspense } from 'react';
3 |
4 | import { ClientOptionsContext } from './contexts/client-options';
5 | import { useNavermaps } from './use-navermaps';
6 |
7 | const testId = 'test-naver-cilent-id';
8 | const naverMock = { maps: { jsContentLoaded: true } };
9 |
10 | describe('useNavermaps()', () => {
11 | test('Suspense client fetching', async () => {
12 | const wrapper = ({ children }: { children: any }) => (
13 |
14 |
15 | {children}
16 |
17 |
18 | );
19 |
20 | const { result } = renderHook(() => useNavermaps(), { wrapper });
21 | expect(document.head.innerHTML).toMatch(new RegExp(`^. Instead of mock & fetch navermaps cdn client,
24 | // we just fire onload event to await loadNavermapsScript
25 | // @ts-expect-error mocking navermaps client loader
26 | window.naver = naverMock;
27 | fireEvent(
28 | document.getElementsByTagName('script')[0],
29 | new Event('load'),
30 | );
31 |
32 | await waitFor(() => expect(result.current).not.toBeNull());
33 |
34 | expect(result.current).toBe(naverMock.maps);
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/use-navermaps.ts:
--------------------------------------------------------------------------------
1 | import { suspend } from 'suspend-react';
2 |
3 | import { useClientOptions } from './contexts/client-options';
4 | import { loadNavermapsScript } from './load-navermaps-script';
5 | import type { ClientOptions } from './types/client';
6 |
7 | async function load(options?: ClientOptions): Promise {
8 | if (typeof window !== 'undefined' && window.naver?.maps) {
9 | return window.naver.maps;
10 | }
11 |
12 | if (!options) {
13 | throw new Error('react-naver-maps: set options with `useNavermaps.config`');
14 | }
15 |
16 | return await loadNavermapsScript(options);
17 | }
18 |
19 | export function useNavermaps() {
20 | if (typeof window === 'undefined') {
21 | throw new Error('react-naver-maps: browser');
22 | }
23 |
24 | /**
25 | * TODO: Provider option 이 변경될 경우 클리어하는 로직 필요
26 | * ex) submodule 에 파노라마 추가시 window.naver.maps가 존재하므로 새로 로드하지 않음
27 | */
28 | if (window.naver?.maps) {
29 | return window.naver.maps;
30 | }
31 |
32 | const options = useClientOptions();
33 |
34 | return suspend(load, [options, 'react-naver-maps/loadClient']);
35 | }
36 |
37 | // useNavermaps.preload = (options: any) => {
38 | // if (!window) {
39 | // return;
40 | // }
41 |
42 | // return preload(load, [options, 'react-naver-maps/loadClient']);
43 | // };
44 |
45 | // useNavermaps.clear = (options: any) => {
46 | // return clear([options, 'react-naver-maps/loadClient']);
47 | // };
48 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/utils/get-keys.ts:
--------------------------------------------------------------------------------
1 | export function getKeys>(obj: T): Array {
2 | return Object.keys(obj);
3 | }
4 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/utils/load-script.ts:
--------------------------------------------------------------------------------
1 | import _loadScript from 'load-script';
2 |
3 | export function loadScript(src: string): Promise {
4 | return new Promise((resolve, reject) => {
5 | _loadScript(src, (err, script) => {
6 | if (err) reject(err);
7 | else resolve(script);
8 | });
9 | });
10 | }
11 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/utils/omit-undefined.ts:
--------------------------------------------------------------------------------
1 | export function omitUndefined>(obj: T): Partial {
2 | return Object.keys(obj).reduce((acc, key) => {
3 | if (obj[key] === 'undefined') {
4 | return acc;
5 | }
6 | return {
7 | ...acc,
8 | [key]: obj[key],
9 | };
10 | }, {});
11 | }
12 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/src/utils/uncontrolled.ts:
--------------------------------------------------------------------------------
1 | import camelcase from 'camelcase';
2 |
3 | export type UncontrolledKey = `default${Capitalize}`;
4 |
5 | export function getUncontrolledKey(key: T): UncontrolledKey {
6 | return camelcase(`default_${key}`) as UncontrolledKey;
7 | }
8 |
9 | export function makeUncontrolledKeyMap(keys: T) {
10 | return keys.reduce((acc, key) => ({ ...acc, [getUncontrolledKey(key)]: key }), {}) as {
11 | [key in typeof keys[number] as UncontrolledKey]: key;
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/test/naver-map.test.tsx:
--------------------------------------------------------------------------------
1 | // import { render, fireEvent, waitFor } from '@testing-library/react';
2 | // import { renderHook, act } from '@testing-library/react-hooks';
3 | // import { MapDiv } from '../map-div';
4 | // import { NaverMap } from '../naver-map';
5 |
6 | // window.naver = {} as any;
7 | // window.naver.maps = {} as any;
8 | // window.naver.maps.Map = jest.fn().mockImplementation(() => {
9 | // return {
10 | // destroy: () => { console.log('naver.maps.Map destroyed'); },
11 | // getSize: () => new ObjectValue(),
12 | // getCenter: () => new ObjectValue(),
13 | // getBounds: () => new ObjectValue(),
14 | // getZoom: () => 10,
15 | // getMapTypeId: () => 'test',
16 | // getCenterPoint: () => new ObjectValue(),
17 | // setSize: mockSetSize,
18 | // panToBounds: mockPanToBounds,
19 | // setCenter: mockSetCenter,
20 | // setMapTypeId: mockSetMapTypeId,
21 | // setOptions: mockSetOptions,
22 | // };
23 | // });
24 |
25 | // const ObjectValue = jest.fn().mockImplementation(() => {
26 | // return { equals: (obj: any) => false };
27 | // });
28 | // const mockSetSize = jest.fn();
29 | // const mockPanToBounds = jest.fn();
30 | // const mockSetCenter = jest.fn();
31 | // const mockSetMapTypeId = jest.fn();
32 | // const mockSetOptions = jest.fn();
33 |
34 | // const mockMethods = [
35 | // mockSetSize,
36 | // mockSetMapTypeId,
37 | // mockPanToBounds,
38 | // mockSetCenter,
39 | // ];
40 |
41 | // beforeEach(() => {
42 | // (window.naver.maps.Map as any).mockClear();
43 | // mockMethods.forEach(f => f.mockClear());
44 | // });
45 |
46 | // async function waitMapRendered() {
47 | // await waitFor(() => {
48 | // expect(window.naver.maps.Map).toHaveBeenCalledTimes(1);
49 | // });
50 | // }
51 |
52 | // describe('Map', () => {
53 | // test('size', async () => {
54 | // const { rerender } = render(
55 | //
56 | // );
57 |
58 | // await waitMapRendered();
59 |
60 | // rerender(
61 | //
62 | // );
63 |
64 | // expect(mockSetSize).toBeCalledTimes(1);
65 | // });
66 |
67 | // test('mapTypeId', async () => {
68 | // const { rerender } = render(
69 | //
70 | // );
71 |
72 | // await waitMapRendered();
73 |
74 | // rerender(
75 | //
76 | // );
77 |
78 | // rerender(
79 | //
80 | // );
81 |
82 | // rerender(
83 | //
84 | // );
85 |
86 | // expect(mockSetMapTypeId).toBeCalledTimes(2);
87 | // });
88 |
89 | // test('bounds', async () => {
90 | // const { rerender } = render(
91 | //
92 | // );
93 |
94 | // await waitMapRendered();
95 |
96 | // rerender(
97 | //
101 | // );
102 |
103 | // expect(mockPanToBounds).toBeCalledTimes(1);
104 | // expect(mockSetCenter).toBeCalledTimes(0);
105 | // });
106 | // });
107 |
108 | describe('useNavermaps()', () => {
109 | test('hello', () => {
110 | expect(1).toBe(1);
111 | });
112 | });
113 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/test/setupTests.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zeakd/react-naver-maps/1de4aa19a58ef2a4044a3fd58448bba0134611e4/packages/react-naver-maps/test/setupTests.ts
--------------------------------------------------------------------------------
/packages/react-naver-maps/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */
4 |
5 | /* Basic Options */
6 | // "incremental": true, /* Enable incremental compilation */
7 | "target": "es2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
8 | "module": "es2020", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
9 | // "lib": [], /* Specify library files to be included in the compilation. */
10 | // "allowJs": true, /* Allow javascript files to be compiled. */
11 | // "checkJs": true, /* Report errors in .js files. */
12 | "jsx": "react-jsx", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
13 | "declaration": true, /* Generates corresponding '.d.ts' file. */
14 | "declarationDir": "./types",
15 | // "emitDeclarationOnly": true,
16 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
17 | // "sourceMap": true, /* Generates corresponding '.map' file. */
18 | // "outFile": "./", /* Concatenate and emit output to single file. */
19 | // "outDir": "./dist", /* Redirect output structure to the directory. */
20 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
21 | // "composite": true, /* Enable project compilation */
22 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
23 | // "removeComments": true, /* Do not emit comments to output. */
24 | // "noEmit": true, /* Do not emit outputs. */
25 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
26 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
27 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
28 |
29 | /* Strict Type-Checking Options */
30 | "strict": true, /* Enable all strict type-checking options. */
31 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
32 | // "strictNullChecks": true, /* Enable strict null checks. */
33 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
34 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
35 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
36 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
37 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
38 |
39 | /* Additional Checks */
40 | // "noUnusedLocals": true, /* Report errors on unused locals. */
41 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
42 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
43 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
44 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
45 |
46 | /* Module Resolution Options */
47 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
48 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
49 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
50 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
51 | // "typeRoots": [], /* List of folders to include type definitions from. */
52 | "types": ["navermaps", "jest"], /* Type declaration files to be included in compilation. */
53 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
54 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
55 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
56 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
57 |
58 | /* Source Map Options */
59 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
60 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
61 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
62 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
63 |
64 | /* Experimental Options */
65 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
66 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
67 |
68 | /* Advanced Options */
69 | // "skipLibCheck": true, /* Skip type checking of declaration files. */
70 | // "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
71 | },
72 | "include": ["src", "test"],
73 | "exclude": ["node_modules"],
74 | }
75 |
--------------------------------------------------------------------------------
/packages/react-naver-maps/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup';
2 | import pkgJson from './package.json';
3 |
4 | const external = [...Object.keys(pkgJson.dependencies || {}), ...Object.keys(pkgJson.peerDependencies || {})];
5 |
6 | export default defineConfig({
7 | entry: ['src/**/*.ts?(x)', '!src/**/*.(spec|test).ts?(x)'],
8 | format: ['cjs', 'esm'],
9 | external,
10 | dts: true,
11 | sourcemap: true,
12 | });
13 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - packages/*
3 | - website
--------------------------------------------------------------------------------
/website/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['../.eslintrc.js'],
3 | rules: { 'react/react-in-jsx-scope': 'off' },
4 | ignorePatterns: ['**/samples/*.js'],
5 | };
6 |
--------------------------------------------------------------------------------
/website/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | # vercel
34 | .vercel
35 |
36 | # typescript
37 | *.tsbuildinfo
38 |
--------------------------------------------------------------------------------
/website/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # website
2 |
3 | ## 0.1.0
4 |
5 | ### Minor Changes
6 |
7 | - 50c7d39: release v0.1.0
8 |
9 | ### Patch Changes
10 |
11 | - Updated dependencies [96c6bb0]
12 | - Updated dependencies [50c7d39]
13 | - Updated dependencies [894ccb1]
14 | - react-naver-maps@0.1.0
15 |
16 | ## 0.1.0-next.0
17 |
18 | ### Minor Changes
19 |
20 | - release v0.1.0
21 |
22 | ### Patch Changes
23 |
24 | - Updated dependencies
25 | - react-naver-maps@0.1.0-next.0
26 |
--------------------------------------------------------------------------------
/website/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | ```
12 |
13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
14 |
15 | You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
16 |
17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
18 |
19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
20 |
21 | ## Learn More
22 |
23 | To learn more about Next.js, take a look at the following resources:
24 |
25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
27 |
28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
29 |
30 | ## Deploy on Vercel
31 |
32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
33 |
34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
35 |
--------------------------------------------------------------------------------
/website/additional.d.ts:
--------------------------------------------------------------------------------
1 | // types/mdx.d.ts
2 | declare module '*.mdx' {
3 | let MDXComponent: (props) => JSX.Element;
4 | export default MDXComponent;
5 | }
6 |
--------------------------------------------------------------------------------
/website/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/website/next.config.mjs:
--------------------------------------------------------------------------------
1 | import { withDocz } from 'next-docz/config';
2 |
3 | /** @type {import('next').NextConfig} */
4 | export default withDocz({
5 | reactStrictMode: true,
6 | pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'],
7 | trailingSlash: true,
8 | basePath: process.env.NEXT_PUBLIC_WEBSITE_BASE_PATH,
9 | });
10 |
--------------------------------------------------------------------------------
/website/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "website",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "rm -rf .next && cd ../packages/react-naver-maps && pnpm link ../../website/node_modules/react; cd - && next dev",
7 | "build": "next build && next export",
8 | "start": "next start",
9 | "lint": "eslint src"
10 | },
11 | "dependencies": {
12 | "@emotion/react": "^11.10.4",
13 | "@emotion/styled": "^11.10.4",
14 | "@mdx-js/react": "^2.1.5",
15 | "@next/font": "13.1.1",
16 | "@radix-ui/react-navigation-menu": "^1.1.1",
17 | "@types/node": "16.11.10",
18 | "@types/react": "18.0.26",
19 | "@types/react-dom": "18.0.10",
20 | "@types/react-syntax-highlighter": "^13.5.2",
21 | "next": "^12.3.1",
22 | "next-docz": "workspace:^0.0.1",
23 | "normalize.css": "^8.0.1",
24 | "react": "^18.2.0",
25 | "react-dom": "^18.2.0",
26 | "react-icons": "^4.7.1",
27 | "react-naver-maps": "workspace:^0.1.0",
28 | "react-syntax-highlighter": "^15.5.0",
29 | "typescript": "^4.8.4",
30 | "use-asset": "^1.0.4"
31 | },
32 | "devDependencies": {
33 | "@types/gtag.js": "^0.0.12"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/website/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zeakd/react-naver-maps/1de4aa19a58ef2a4044a3fd58448bba0134611e4/website/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/website/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zeakd/react-naver-maps/1de4aa19a58ef2a4044a3fd58448bba0134611e4/website/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/website/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zeakd/react-naver-maps/1de4aa19a58ef2a4044a3fd58448bba0134611e4/website/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/website/public/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #da532c
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/website/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zeakd/react-naver-maps/1de4aa19a58ef2a4044a3fd58448bba0134611e4/website/public/favicon-16x16.png
--------------------------------------------------------------------------------
/website/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zeakd/react-naver-maps/1de4aa19a58ef2a4044a3fd58448bba0134611e4/website/public/favicon-32x32.png
--------------------------------------------------------------------------------
/website/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zeakd/react-naver-maps/1de4aa19a58ef2a4044a3fd58448bba0134611e4/website/public/favicon.ico
--------------------------------------------------------------------------------
/website/public/google8aa2ebba48557029.html:
--------------------------------------------------------------------------------
1 | google-site-verification: google8aa2ebba48557029.html
--------------------------------------------------------------------------------
/website/public/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zeakd/react-naver-maps/1de4aa19a58ef2a4044a3fd58448bba0134611e4/website/public/mstile-150x150.png
--------------------------------------------------------------------------------
/website/public/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
116 |
--------------------------------------------------------------------------------
/website/public/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "short_name": "",
4 | "icons": [
5 | {
6 | "src": "/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/website/src/components/layout.tsx:
--------------------------------------------------------------------------------
1 | import { css } from '@emotion/react';
2 | import * as NavMenu from '@radix-ui/react-navigation-menu';
3 | import Link from 'next/link';
4 | import type { ReactNode } from 'react';
5 | import { FiChevronDown } from 'react-icons/fi';
6 |
7 | import { menu } from '../menu';
8 |
9 | type Props = {
10 | children?: ReactNode;
11 | };
12 |
13 | export function Layout(props: Props) {
14 | return (
15 |
16 |
30 |
36 |
37 |
38 |
45 |
46 | {props.children}
47 |
48 |
49 |
50 |
51 |
52 | );
53 | }
54 |
55 | const navItemCss = css({
56 | background: 'transparent',
57 | border: 'none',
58 | padding: '8px 12px',
59 | outline: 'none',
60 | userSelect: 'none',
61 | fontWeight: '500',
62 | lineHeight: 1,
63 | borderRadius: '4px',
64 | fontSize: '15px',
65 |
66 | display: 'flex',
67 | alignItems: 'center',
68 | ':hover': { backgroundColor: '#fafafa' },
69 | // color: var(--violet11);
70 | });
71 |
72 | function Header() {
73 | return (
74 |
87 |
100 |
130 |
206 |
207 |
208 | );
209 | }
210 |
211 |
212 | function Sidebar() {
213 | return (
214 |
225 |
228 |
229 |
230 |
234 | {menu.map((item, idx) => {
235 | if (item.type === 'category') {
236 | return (
237 |
238 |
239 |
240 | {item.name}
241 |
242 |
243 |
247 | {item.contents.map(({ name, href }) => {
248 | return (
249 |
253 |
256 |
{name}
262 |
263 |
264 | );
265 | })}
266 |
267 |
)
268 | ;
269 | }
270 |
271 | return;
272 | })}
273 |
274 |
275 | );
276 | }
277 |
278 | type MainProps = {
279 | children?: ReactNode;
280 | };
281 |
282 | function Main(props: MainProps) {
283 | return (
284 |
290 | {props.children}
291 |
292 | );
293 | }
294 |
--------------------------------------------------------------------------------
/website/src/hooks/useIsDarkMode.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 |
3 | export function useIsDarkMode() {
4 | const [darkMode, setDarkMode] = useState();
5 |
6 | useEffect(() => {
7 | const matchDark = window.matchMedia('(prefers-color-scheme: dark)');
8 |
9 | setDarkMode(matchDark.matches);
10 |
11 | const darkHandler = (event: MediaQueryListEvent) => {
12 | setDarkMode(event.matches);
13 | };
14 | matchDark.addEventListener('change', darkHandler);
15 |
16 | return () => {
17 | matchDark.removeEventListener('change', darkHandler);
18 | };
19 | }, []);
20 |
21 | return darkMode;
22 | }
23 |
--------------------------------------------------------------------------------
/website/src/lib/gtag.ts:
--------------------------------------------------------------------------------
1 | export const GA_TRACKING_ID = process.env.NEXT_PUBLIC_GA_TRACKING_ID || '';
2 |
3 | type MappedTypeSnakeToCamel = {[K in keyof InputType as SnakeToCamelCase]: InputType[K]};
4 |
5 | type SnakeToCamelCase =
6 | S extends `${infer T}_${infer U}` ?
7 | `${T}${Capitalize>}` :
8 | S;
9 |
10 | // https://developers.google.com/analytics/devguides/collection/gtagjs/pages
11 | export const pageview = (url: string) => {
12 | window.gtag('config', GA_TRACKING_ID, { page_location: url });
13 | };
14 |
15 | // https://developers.google.com/analytics/devguides/collection/gtagjs/events
16 | export const event = (action: Gtag.EventNames, { eventCategory, eventLabel, value }: MappedTypeSnakeToCamel) => {
17 | window.gtag('event', action, {
18 | event_category: eventCategory,
19 | event_label: eventLabel,
20 | value: value,
21 | });
22 | };
23 |
--------------------------------------------------------------------------------
/website/src/menu.ts:
--------------------------------------------------------------------------------
1 |
2 | export const menu = [
3 | {
4 | type: 'category',
5 | name: 'Guides',
6 | contents: [
7 | {
8 | name: 'Introduction',
9 | href: '/',
10 | },
11 | {
12 | name: 'Quickstart',
13 | href: '/guides/quickstart',
14 | },
15 | {
16 | name: 'Core concepts',
17 | href: '/guides/core-concepts',
18 | },
19 | {
20 | name: 'Customize overlays',
21 | href: '/guides/customize-overlays',
22 | },
23 | {
24 | name: 'Suspensed useNavermaps',
25 | href: '/guides/suspensed-use-navermaps',
26 | },
27 | {
28 | name: 'Migration guide from v0.0',
29 | href: '/guides/migration-guide-from-0.0',
30 | },
31 | ],
32 | },
33 | {
34 | type: 'category',
35 | name: 'Examples',
36 | contents: [
37 | {
38 | name: '지도 기본 예제',
39 | href: '/examples/map-tutorial-1-simple',
40 | },
41 | {
42 | name: '지도 옵션 조정하기',
43 | href: '/examples/map-tutorial-2-options',
44 | },
45 | {
46 | name: '지도 유형 설정하기',
47 | href: '/examples/map-tutorial-3-types',
48 | },
49 | {
50 | name: '지도 좌표 경계 확인하기',
51 | href: '/examples/map-tutorial-4-bounds',
52 | },
53 | {
54 | name: '지도 이동하기',
55 | href: '/examples/map-tutorial-5-moves',
56 | },
57 | {
58 | name: 'HTML5 Geolocation API \n활용하기',
59 | href: '/examples/map-tutorial-6-geolocation',
60 | },
61 | {
62 | name: '마커 표시하기',
63 | href: '/examples/marker-tutorial-1-simple',
64 | },
65 | {
66 | name: '마커 클러스터화하기',
67 | href: '/examples/marker-cluster-tutorial',
68 | },
69 | {
70 | name: '사용자 정의 컨트롤 만들기',
71 | href: '/examples/control-tutorial-4-custom-p1',
72 | },
73 | ],
74 | },
75 | {
76 | type: 'category',
77 | name: 'API Reference',
78 | contents: [
79 | {
80 | name: 'NavermapsProvider',
81 | href: '/api-references/navermaps-provider',
82 | },
83 | {
84 | name: 'NaverMap',
85 | href: '/api-references/naver-map',
86 | },
87 | {
88 | name: 'Container',
89 | href: '/api-references/container',
90 | },
91 | {
92 | name: 'Circle',
93 | href: '/api-references/circle',
94 | },
95 | {
96 | name: 'Ellipse',
97 | href: '/api-references/ellipse',
98 | },
99 | {
100 | name: 'GroundOverlay',
101 | href: '/api-references/ground-overlay',
102 | },
103 | {
104 | name: 'InfoWindow',
105 | href: '/api-references/info-window',
106 | },
107 | {
108 | name: 'Marker',
109 | href: '/api-references/marker',
110 | },
111 | {
112 | name: 'Polygon',
113 | href: '/api-references/polygon',
114 | },
115 | {
116 | name: 'Polyline',
117 | href: '/api-references/polyline',
118 | },
119 | {
120 | name: 'Rectangle',
121 | href: '/api-references/rectangle',
122 | },
123 | {
124 | name: 'useNavermaps',
125 | href: '/api-references/use-navermaps',
126 | },
127 | {
128 | name: 'useListener',
129 | href: '/api-references/use-listener',
130 | },
131 | {
132 | name: 'Listener',
133 | href: '/api-references/listener',
134 | },
135 | {
136 | name: 'useMap',
137 | href: '/api-references/use-map',
138 | },
139 | {
140 | name: 'loadNavermapsScript',
141 | href: '/api-references/load-navermaps-script',
142 | },
143 | ],
144 | },
145 | ] as const;
146 |
--------------------------------------------------------------------------------
/website/src/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import 'normalize.css';
2 | import { css, Global } from '@emotion/react';
3 | import { MDXProvider } from '@mdx-js/react';
4 | import type { AppProps } from 'next/app';
5 | import Head from 'next/head';
6 | import Link from 'next/link';
7 | import { useRouter } from 'next/router';
8 | import Script from 'next/script';
9 | import { ComponentPropsWithoutRef, useEffect } from 'react';
10 | import { FiExternalLink } from 'react-icons/fi';
11 | import { NavermapsProvider } from 'react-naver-maps';
12 | import { Prism } from 'react-syntax-highlighter';
13 | import { materialLight } from 'react-syntax-highlighter/dist/cjs/styles/prism';
14 |
15 | import { Layout } from '../components/layout';
16 | import * as gtag from '../lib/gtag';
17 |
18 | const WEBSITE_BASE_PATH = process.env.NEXT_PUBLIC_WEBSITE_BASE_PATH || '';
19 |
20 | // import { useIsDarkMode } from '../hooks/useIsDarkMode';
21 |
22 | function Code({ className, ...props }: ComponentPropsWithoutRef<'code'>) {
23 | // const isDarkMode = useIsDarkMode();
24 |
25 | const match = /language-(\w+)/.exec(className || '');
26 | return match
27 | ? (
28 |
32 | )
33 | : (
34 |
43 |
46 |
47 | );
48 | }
49 |
50 | function Anchor({ href, ...restProps }: ComponentPropsWithoutRef<'a'>) {
51 | const isExternal = /https?:\/\//.test(href || '');
52 |
53 | if (isExternal) {
54 | return (
55 |
56 |
61 |
62 |
63 | );
64 | }
65 |
66 | return (
67 |
68 |
69 |
74 |
75 |
76 | );
77 | }
78 |
79 | function UL(props: ComponentPropsWithoutRef<'ul'>) {
80 | return (
81 |
82 | );
83 | }
84 |
85 | const mdxComponents = { code: Code, a: Anchor, ul: UL };
86 |
87 | function App({ Component, pageProps }: AppProps) {
88 | const router = useRouter();
89 | useEffect(() => {
90 | const handleRouteChange = (url: string) => {
91 | gtag.pageview(url);
92 | };
93 | router.events.on('routeChangeComplete', handleRouteChange);
94 | return () => {
95 | router.events.off('routeChangeComplete', handleRouteChange);
96 | };
97 | }, [router.events]);
98 |
99 | return (
100 | <>
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | React Naver Maps
111 |
112 | {/* Global Site Tag (gtag.js) - Google Analytics */}
113 |
117 |
128 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 | >
152 | );
153 | }
154 |
155 | export default App;
156 |
--------------------------------------------------------------------------------
/website/src/pages/api-references/circle.mdx:
--------------------------------------------------------------------------------
1 | import { Props } from 'next-docz';
2 | import { Circle } from 'react-naver-maps'
3 |
4 | # Circle
5 |
6 | [네이버 공식문서 Circle](https://navermaps.github.io/maps.js.ncp/docs/naver.maps.Circle.html)
7 |
8 | ## Props
9 |
10 |
--------------------------------------------------------------------------------
/website/src/pages/api-references/container.mdx:
--------------------------------------------------------------------------------
1 | import { Props } from 'next-docz';
2 | import { Container } from 'react-naver-maps'
3 |
4 | # Container
5 |
6 | 맵이나 파노라마가 렌더되는 컴포넌트입니다. div 역할을 하며 SSR처리와 Suspense가 내장되어있습니다.
7 |
8 | ## Props
9 |
10 |
15 |
--------------------------------------------------------------------------------
/website/src/pages/api-references/ellipse.mdx:
--------------------------------------------------------------------------------
1 | import { Props } from 'next-docz';
2 | import { Ellipse } from 'react-naver-maps'
3 |
4 | # Ellipse
5 |
6 | [네이버 공식문서 Ellipse](https://navermaps.github.io/maps.js.ncp/docs/naver.maps.Ellipse.html)
7 |
8 | ## Props
9 |
10 |
--------------------------------------------------------------------------------
/website/src/pages/api-references/ground-overlay.mdx:
--------------------------------------------------------------------------------
1 | import { Props } from 'next-docz';
2 | import { GroundOverlay } from 'react-naver-maps'
3 |
4 | # GroundOverlay
5 |
6 | [네이버 공식문서 GroundOverlay](https://navermaps.github.io/maps.js.ncp/docs/naver.maps.GroundOverlay.html)
7 |
8 | ## Props
9 |
10 |
--------------------------------------------------------------------------------
/website/src/pages/api-references/info-window.mdx:
--------------------------------------------------------------------------------
1 | import { Props } from 'next-docz';
2 | import { InfoWindow } from 'react-naver-maps'
3 |
4 | # InfoWindow
5 |
6 | [네이버 공식문서 InfoWindow](https://navermaps.github.io/maps.js.ncp/docs/naver.maps.InfoWindow.html)
7 |
8 | ## Props
9 |
10 |
--------------------------------------------------------------------------------
/website/src/pages/api-references/listener.mdx:
--------------------------------------------------------------------------------
1 | import { Props } from 'next-docz'
2 | import { Listener } from 'react-naver-maps'
3 |
4 | # Listener
5 |
6 | [Customize overlays](/guides/customize-overlays)를 참고해주세요
7 |
8 | ## Options
9 |
10 |
11 |
12 | ## Examples
13 |
14 | ``` jsx
15 |
16 | {console.log('click') }} />
17 |
18 |
19 | // standalone
20 | {console.log('click') }} />
21 | ```
--------------------------------------------------------------------------------
/website/src/pages/api-references/load-navermaps-script.mdx:
--------------------------------------------------------------------------------
1 | import { Props } from 'next-docz';
2 | import { loadNavermapsScript } from 'react-naver-maps'
3 |
4 | # loadNavermapsScript
5 |
6 | ## Options
7 |
8 |
--------------------------------------------------------------------------------
/website/src/pages/api-references/marker.mdx:
--------------------------------------------------------------------------------
1 | import { Props } from 'next-docz';
2 | import { Marker } from 'react-naver-maps'
3 |
4 | # Marker
5 |
6 | [네이버 공식문서 Marker](https://navermaps.github.io/maps.js.ncp/docs/naver.maps.Marker.html)
7 |
8 | ## Props
9 |
10 |
--------------------------------------------------------------------------------
/website/src/pages/api-references/naver-map.mdx:
--------------------------------------------------------------------------------
1 | import { Props } from 'next-docz';
2 | import { NaverMap } from 'react-naver-maps'
3 |
4 | # NaverMap
5 |
6 | [네이버 공식문서 Map](https://navermaps.github.io/maps.js.ncp/docs/naver.maps.Map.html)
7 |
8 | ## Props
9 |
10 |
--------------------------------------------------------------------------------
/website/src/pages/api-references/navermaps-provider.mdx:
--------------------------------------------------------------------------------
1 | import { Props } from 'next-docz';
2 | import { NavermapsProvider } from 'react-naver-maps'
3 |
4 | # NavermapsProvider
5 |
6 | [네이버 공식 문서 서브 모듈 시스템](https://navermaps.github.io/maps.js.ncp/docs/tutorial-4-Submodules.html)
7 |
8 | ## Props
9 |
10 |
11 |
12 | ## Example
13 |
14 | ``` jsx
15 |
19 |
20 |
21 | ```
--------------------------------------------------------------------------------
/website/src/pages/api-references/ploygon.mdx:
--------------------------------------------------------------------------------
1 | import { Props } from 'next-docz';
2 | import { Polygon } from 'react-naver-maps'
3 |
4 | # Polygon
5 |
6 | ## Props
7 |
8 |
--------------------------------------------------------------------------------
/website/src/pages/api-references/polygon.mdx:
--------------------------------------------------------------------------------
1 | import { Props } from 'next-docz';
2 | import { Polygon } from 'react-naver-maps'
3 |
4 | # Polygon
5 |
6 | [네이버 공식문서 Polygon](https://navermaps.github.io/maps.js.ncp/docs/naver.maps.Polygon.html)
7 |
8 | ## Props
9 |
10 |
--------------------------------------------------------------------------------
/website/src/pages/api-references/polyline.mdx:
--------------------------------------------------------------------------------
1 | import { Props } from 'next-docz';
2 | import { Polyline } from 'react-naver-maps'
3 |
4 | # Polyline
5 |
6 | [네이버 공식문서 Polyline](https://navermaps.github.io/maps.js.ncp/docs/naver.maps.Polyline.html)
7 |
8 | ## Props
9 |
10 |
--------------------------------------------------------------------------------
/website/src/pages/api-references/rectangle.mdx:
--------------------------------------------------------------------------------
1 | import { Props } from 'next-docz';
2 | import { Rectangle } from 'react-naver-maps'
3 |
4 | # Rectangle
5 |
6 | [네이버 공식문서 Rectangle](https://navermaps.github.io/maps.js.ncp/docs/naver.maps.Rectangle.html)
7 |
8 | ## Props
9 |
10 |
--------------------------------------------------------------------------------
/website/src/pages/api-references/use-listener.mdx:
--------------------------------------------------------------------------------
1 | import { Props } from 'next-docz'
2 | import { useListener } from 'react-naver-maps'
3 |
4 | # useListener
5 |
6 | [Customize overlays](/guides/customize-overlays)를 참고해주세요
7 |
8 | ## Example
9 |
10 | ``` jsx
11 | function MyMarker() {
12 | ...
13 | useListener(marker, 'click', () => console.log('click'))
14 | ...
15 | }
16 | ```
--------------------------------------------------------------------------------
/website/src/pages/api-references/use-map.mdx:
--------------------------------------------------------------------------------
1 | import { Props } from 'next-docz'
2 | import { useMap } from 'react-naver-maps'
3 |
4 | # useMap
5 |
6 | [Customize overlays](/guides/customize-overlays)를 참고해주세요
7 |
8 | ## Example
9 |
10 | ``` jsx
11 | function MyControls() {
12 | ...
13 | const map = useMap()
14 | ...
15 | }
16 | ```
--------------------------------------------------------------------------------
/website/src/pages/api-references/use-navermaps.mdx:
--------------------------------------------------------------------------------
1 | import { useNavermaps } from 'react-naver-maps'
2 |
3 | # useNavermaps
4 |
5 | [NavermapProvider](/api-references/navermaps-provider) 설정이 필요합니다.
6 |
7 | ## Example
8 |
9 | ``` jsx
10 | function MyComponent() {
11 | const navermaps = useNavermaps();
12 | // navermaps === window.naver.maps;
13 |
14 | return null;
15 | }
16 |
17 |
18 |
19 |
20 |
21 | ```
22 |
--------------------------------------------------------------------------------
/website/src/pages/examples/control-tutorial-4-custom-p1.mdx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react'
2 | import { Playground, Props } from 'next-docz'
3 | import { Container as MapDiv, NaverMap, useNavermaps, Overlay, useMap } from 'react-naver-maps'
4 |
5 | # 사용자 정의 컨트롤 만들기
6 |
7 | 네이버지도 공식 튜토리얼 [사용자 정의 컨트롤 만들기](https://navermaps.github.io/maps.js.ncp/docs/tutorial-4-control-custom-p1.example.html)의 구현 예시입니다.
8 |
9 |
16 | {() => {
17 | function MyCustomControl() {
18 | const locationBtnHtml = `
19 |
38 | NAVER 그린팩토리
54 |
55 | `;
56 | const navermaps = useNavermaps();
57 | const map = useMap();
58 | // customControl 객체 이용하기
59 | // Customize Overlay 참고
60 | // https://zeakd.github.io/react-naver-maps/guides/customize-overlays/
61 | const [customControl1] = useState(() => {
62 | return new navermaps.CustomControl(locationBtnHtml, {
63 | position: navermaps.Position.TOP_LEFT
64 | });
65 | })
66 |
67 | useEffect(() => {
68 | // naver.maps.Event.addDOMListener 사용할 필요 없이, native addEventListener를 사용합니다.
69 | // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
70 | const domElement = customControl1.getElement();
71 | const domListener = () => {
72 | map.setCenter(new navermaps.LatLng(37.3595953, 127.1053971));
73 | };
74 |
75 | domElement.addEventListener('click', domListener)
76 |
77 | return () => {
78 | domElement.removeEventListener('click', domListener);
79 | }
80 | }, [])
81 |
82 | useEffect(() => {
83 | // Map 객체의 controls 활용하기
84 | // Jquery 없이 생성하기
85 | // var $locationBtn = $(locationBtnHtml),
86 | // locationBtnEl = $locationBtn[0];
87 | const parent = document.createElement('div');
88 | parent.innerHTML = locationBtnHtml;
89 | const locationBtnEl = parent.children[0]
90 |
91 | map.controls[naver.maps.Position.LEFT_CENTER].push(locationBtnEl);
92 |
93 | // naver.maps.Event.addDOMListener 사용할 필요 없이, native addEventListener를 사용합니다.
94 | // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
95 | const domListener = () => {
96 | map.setCenter(new navermaps.LatLng(37.3595953, 127.1053971));
97 | };
98 |
99 | locationBtnEl.addEventListener('click', domListener)
100 |
101 | return () => {
102 | locationBtnEl.removeEventListener('click', domListener);
103 | }
104 | }, [])
105 |
106 | return (
107 |
108 | );
109 | }
110 |
111 | function MyMap() {
112 | const [init, setInit] = useState(false);
113 |
114 | return (
115 | {
118 | setInit(true);
119 | }}
120 | >
121 | {init && }
122 |
123 | )
124 | }
125 |
126 | return (
127 |
131 |
132 |
133 | )
134 | }}
135 |
136 |
--------------------------------------------------------------------------------
/website/src/pages/examples/map-tutorial-1-simple.mdx:
--------------------------------------------------------------------------------
1 | import { Suspense } from 'react'
2 | import { Playground, Props } from 'next-docz'
3 | import { Container as MapDiv, NaverMap } from 'react-naver-maps'
4 |
5 | # 지도 기본 예제
6 |
7 | 네이버지도 공식 튜토리얼 [지도 기본 예제](https://navermaps.github.io/maps.js.ncp/docs/tutorial-1-map-simple.example.html)의 구현 예시입니다.
8 |
9 |
15 |
19 |
20 |
21 |
22 |
23 | 3
--------------------------------------------------------------------------------
/website/src/pages/examples/map-tutorial-2-options.mdx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, useRef, useCallback } from 'react';
2 | import { Playground, Props } from 'next-docz'
3 | import { Container as MapDiv, NaverMap, useNavermaps } from 'react-naver-maps';
4 |
5 |
6 | # 지도 옵션 조정하기
7 |
8 | 네이버지도 공식 튜토리얼 [지도 옵션 조정하기](https://navermaps.github.io/maps.js.ncp/docs/tutorial-2-map-options.example.html)의 구현 예시입니다.
9 |
10 |
16 |
21 | {() => {
22 | const navermaps = useNavermaps();
23 |
24 | const [zoom, setZoom] = useState(13);
25 |
26 | const [draggable, setDraggable] = useState(true);
27 | const [disableKineticPan, setDisableKineticPan] = useState(true);
28 | const [tileTransition, setTileTransition] = useState(true);
29 | const [minZoom, setMinZoom] = useState(7);
30 | const [scaleControl, setScaleControl] = useState(true);
31 |
32 | const handleZoomChanged = useCallback((zoom) => {
33 | console.log(`zoom: ${zoom}`)
34 | }, [])
35 |
36 | const normalBtnStyle = {
37 | backgroundColor: '#fff',
38 | border: 'solid 1px #333',
39 | outline: '0 none',
40 | borderRadius: '5px',
41 | boxShadow: '2px 2px 1px 1px rgba(0, 0, 0, 0.5)',
42 | margin: '0 5px 5px 0',
43 | }
44 |
45 | const selectedBtnStyle = {
46 | ...normalBtnStyle,
47 | backgroundColor: '#2780E3',
48 | color: 'white',
49 | }
50 |
51 | return (
52 | <>
53 |
60 |
66 |
72 |
78 |
84 |
90 |
91 |
127 | >
128 | );
129 | }}
130 |
131 |
132 |
133 | https://navermaps.github.io/maps.js.ncp/docs/tutorial-2-map-options.example.html
--------------------------------------------------------------------------------
/website/src/pages/examples/map-tutorial-3-types.mdx:
--------------------------------------------------------------------------------
1 | import { Playground, Props } from 'next-docz';
2 | import { useState, useRef } from 'react';
3 | import { Container as MapDiv, NaverMap, useNavermaps } from 'react-naver-maps';
4 |
5 | # 지도 유형 설정하기
6 |
7 | 네이버지도 공식 튜토리얼 [지도 유형 설정하기](https://navermaps.github.io/maps.js.ncp/docs/tutorial-3-map-types.example.html)의 구현 예시입니다.
8 |
9 |
15 |
20 | {() => {
21 | const navermaps = useNavermaps();
22 | const [mapTypeId, setMapTypeId] = useState(navermaps.MapTypeId.NORMAL);
23 | const buttons = [
24 | {
25 | typeId: navermaps.MapTypeId.NORMAL,
26 | text: '일반지도'
27 | },
28 | {
29 | typeId: navermaps.MapTypeId.TERRAIN,
30 | text: '지형도'
31 | },
32 | {
33 | typeId: navermaps.MapTypeId.SATELLITE,
34 | text: '위성지도'
35 | },
36 | {
37 | typeId: navermaps.MapTypeId.HYBRID,
38 | text: '겹쳐보기'
39 | },
40 | ]
41 |
42 | return (
43 | <>
44 |
51 | {buttons.map(btn => {
52 | return
67 | })}
68 |
69 |
77 | >
78 | );
79 | }}
80 |
81 |
82 |
--------------------------------------------------------------------------------
/website/src/pages/examples/map-tutorial-4-bounds.mdx:
--------------------------------------------------------------------------------
1 | import { Playground, Props } from 'next-docz'
2 | import { useState, useEffect, useRef, useLayoutEffect } from 'react'
3 | import { Container as MapDiv, NaverMap, useNavermaps, Rectangle, useMap } from 'react-naver-maps'
4 |
5 | # 지도 좌표 경계 확인하기
6 |
7 | 네이버지도 공식 튜토리얼 [지도 유형 설정하기](https://navermaps.github.io/maps.js.ncp/docs/tutorial-1-map-simple.example.html)의 구현 예시입니다.
8 |
9 |
16 | {() => {
17 | const buttonStyle = {
18 | position: 'absolute',
19 | top: 10,
20 | left: 10,
21 | zIndex: 1000,
22 | backgroundColor: '#fff',
23 | border: 'solid 1px #333',
24 | outline: '0 none',
25 | borderRadius: '5px',
26 | boxShadow: '2px 2px 1px 1px rgba(0, 0, 0, 0.5)',
27 | margin: '0 5px 5px 0',
28 | }
29 |
30 | function MyMap() {
31 | const navermaps = useNavermaps();
32 | const center = new navermaps.LatLng(37.5666805, 126.9784147);
33 | const dokdo = new navermaps.LatLngBounds(
34 | new navermaps.LatLng(37.2380651, 131.8562652),
35 | new navermaps.LatLng(37.2444436, 131.8786475));
36 |
37 | const [map, setMap] = useState(null);
38 | const [rect, setRect] = useState(null);
39 |
40 | // map과 rect가 처음 mount되었을 때에만 동작합니다.
41 | useLayoutEffect(() => {
42 | if (map && rect) {
43 | rect.setBounds(map.getBounds());
44 | }
45 | }, [map, rect])
46 |
47 | return {
57 | if (rect) {
58 | window.setTimeout(function() {
59 | rect.setBounds(bounds);
60 | }, 500);
61 | }
62 | }}
63 | >
64 |
71 |
80 |
81 | }
82 |
83 | return (
84 |
89 |
90 |
91 | );
92 | }}
93 |
94 |
--------------------------------------------------------------------------------
/website/src/pages/examples/map-tutorial-5-moves.mdx:
--------------------------------------------------------------------------------
1 | import { Playground, Props } from 'next-docz';
2 | import { useState, useRef } from 'react';
3 | import { Container as MapDiv, NaverMap, useNavermaps } from 'react-naver-maps';
4 |
5 | # 지도 이동하기
6 |
7 | 네이버지도 공식 튜토리얼 [지도 이동하기](https://navermaps.github.io/maps.js.ncp/docs/tutorial-5-map-moves.example.html)의 구현 예시입니다.
8 |
9 |
16 | {() => {
17 |
18 | const buttonsStyle = {
19 | position: 'absolute',
20 | top: 0,
21 | left: 0,
22 | zIndex: 1000,
23 | padding: '5px',
24 | };
25 |
26 | const buttonStyle = {
27 | margin: '0 5px 5px 0',
28 | WebkitAppearance: 'button',
29 | cursor: 'pointer',
30 | color: '#555',
31 | padding: '2px 6px',
32 | background: '#fff',
33 | border: 'solid 1px #333',
34 | cursor: 'pointer',
35 | WebkitBorderRadius: '5px',
36 | outline: '0 none',
37 | borderRadius: '5px',
38 | boxShadow: '2px 2px 1px 1px rgba(0, 0, 0, 0.5)',
39 | };
40 |
41 | function MyMap() {
42 | const navermaps = useNavermaps();
43 |
44 | const jeju = new navermaps.LatLng(33.3590628, 126.534361);
45 | const busan = new navermaps.LatLng(35.1797865, 129.0750194);
46 | const dokdo = new navermaps.LatLngBounds(
47 | new navermaps.LatLng(37.2380651, 131.8562652),
48 | new navermaps.LatLng(37.2444436, 131.8786475));
49 | const seoul = new navermaps.LatLngBounds(
50 | new navermaps.LatLng(37.42829747263545, 126.76620435615891),
51 | new navermaps.LatLng(37.7010174173061, 127.18379493229875));
52 |
53 | const center = new navermaps.LatLng(37.5666805, 126.9784147);
54 |
55 | // useRef 대신 useState를 통해 ref를 가져옵니다.
56 | const [map, setMap] = useState(null);
57 |
58 | return
64 | {/* buttons */}
65 |
66 |
75 |
84 |
93 |
102 |
111 |
120 |
121 |
122 | }
123 |
124 | return (
125 |
130 |
131 |
132 | );
133 | }}
134 |
135 |
--------------------------------------------------------------------------------
/website/src/pages/examples/map-tutorial-6-geolocation.mdx:
--------------------------------------------------------------------------------
1 | import { Playground, Props } from 'next-docz';
2 | import { useState, useRef, useEffect } from 'react';
3 | import { Container as MapDiv, NaverMap, useNavermaps, InfoWindow } from 'react-naver-maps';
4 |
5 | # HTML5 Geolocation API 활용하기
6 |
7 | 네이버지도 공식 튜토리얼 [HTML5 Geolocation API 활용하기](https://navermaps.github.io/maps.js.ncp/docs/tutorial-6-map-geolocation.example.html)의 구현 예시입니다.
8 |
9 |
16 | {() => {
17 | function MyMap() {
18 | const navermaps = useNavermaps();
19 |
20 | // useRef 대신 useState를 통해 ref를 가져옵니다.
21 | const [map, setMap] = useState(null);
22 | const [infowindow, setInfoWindow] = useState(null);
23 |
24 |
25 | function onSuccessGeolocation(position) {
26 | if (!map || !infowindow) return
27 |
28 | const location = new navermaps.LatLng(position.coords.latitude,
29 | position.coords.longitude);
30 | map.setCenter(location);
31 | map.setZoom(10);
32 | infowindow.setContent('' + 'geolocation.getCurrentPosition() 위치' + '
');
33 | infowindow.open(map, location);
34 | console.log('Coordinates: ' + location.toString());
35 | }
36 |
37 | function onErrorGeolocation() {
38 | if (!map || !infowindow) return
39 |
40 | const center = map.getCenter();
41 | infowindow.setContent('' +
42 | '
Geolocation failed!
'+ "latitude: "+ center.lat() +"
longitude: "+ center.lng() +'
');
43 | infowindow.open(map, center);
44 |
45 | if (navigator.geolocation) {
46 | navigator.geolocation.getCurrentPosition(onSuccessGeolocation, onErrorGeolocation);
47 | } else {
48 | const center = map.getCenter();
49 | infowindow.setContent('Geolocation not supported
');
50 | infowindow.open(map, center);
51 | }
52 | }
53 |
54 | useEffect(() => {
55 | if (!map || !infowindow) {
56 | return;
57 | }
58 |
59 | if (navigator.geolocation) {
60 | navigator.geolocation.getCurrentPosition(onSuccessGeolocation, onErrorGeolocation);
61 | } else {
62 | var center = map.getCenter();
63 | infowindow.setContent('Geolocation not supported
');
64 | infowindow.open(map, center);
65 | }
66 | }, [map, infowindow]);
67 |
68 |
69 | return
76 |
77 |
78 | }
79 |
80 | return (
81 |
86 |
87 |
88 | );
89 | }}
90 |
91 |
--------------------------------------------------------------------------------
/website/src/pages/examples/marker-cluster-tutorial.mdx:
--------------------------------------------------------------------------------
1 | import { Suspense, useLayoutEffect, useEffect, useState } from 'react'
2 | import { Playground, Props } from 'next-docz'
3 | import { Container as MapDiv, NaverMap, Marker, useNavermaps, Overlay, useMap } from 'react-naver-maps'
4 | import { makeMarkerClustering } from '../../samples/marker-cluster'
5 | import { accidentDeath } from '../../samples/accident-death'
6 |
7 | # 마커 클러스터화하기
8 |
9 | 네이버지도 공식 튜토리얼 [마커 클러스터화하기](https://navermaps.github.io/maps.js.ncp/docs/tutorial-marker-cluster.example.html)의 구현 예시입니다.
10 |
11 |
16 | {() => {
17 | function MarkerCluster() {
18 | // https://github.com/navermaps/marker-tools.js/blob/master/marker-clustering/src/MarkerClustering.js
19 | // 예제에서 제공된 코드를 그대로 사용하되 naver 객체를 주입 받도록 간단히 makeMarkerClustering로 Wrapping 합니다.
20 |
21 | const navermaps = useNavermaps();
22 | const map = useMap();
23 |
24 | // https://github.com/zeakd/react-naver-maps/blob/main/website/src/samples/marker-cluster.js
25 | const MarkerClustering = makeMarkerClustering(window.naver);
26 |
27 | const htmlMarker1 = {
28 | content: '',
29 | size: navermaps.Size(40, 40),
30 | anchor: navermaps.Point(20, 20)
31 | }
32 | const htmlMarker2 = {
33 | content: '',
34 | size: navermaps.Size(40, 40),
35 | anchor: navermaps.Point(20, 20)
36 | }
37 | const htmlMarker3 = {
38 | content: '',
39 | size: navermaps.Size(40, 40),
40 | anchor: navermaps.Point(20, 20)
41 | }
42 | const htmlMarker4 = {
43 | content: '',
44 | size: navermaps.Size(40, 40),
45 | anchor: navermaps.Point(20, 20)
46 | }
47 | const htmlMarker5 = {
48 | content: '',
49 | size: navermaps.Size(40, 40),
50 | anchor: navermaps.Point(20, 20)
51 | };
52 |
53 | // https://navermaps.github.io/maps.js.ncp/docs/data/accidentdeath.js
54 | const data = accidentDeath.searchResult.accidentDeath;
55 |
56 | // Customize Overlay 참고
57 | // https://zeakd.github.io/react-naver-maps/guides/customize-overlays/
58 | const [cluster] = useState(() => {
59 | const markers = [];
60 |
61 | for (var i = 0, ii = data.length; i < ii; i++) {
62 | var spot = data[i],
63 | latlng = new naver.maps.LatLng(spot.grd_la, spot.grd_lo),
64 | marker = new naver.maps.Marker({
65 | position: latlng,
66 | draggable: true
67 | });
68 |
69 | markers.push(marker);
70 | }
71 |
72 | const cluster = new MarkerClustering({
73 | minClusterSize: 2,
74 | maxZoom: 8,
75 | map: map,
76 | markers: markers,
77 | disableClickZoom: false,
78 | gridSize: 120,
79 | icons: [htmlMarker1, htmlMarker2, htmlMarker3, htmlMarker4, htmlMarker5],
80 | indexGenerator: [10, 100, 200, 500, 1000],
81 | stylingFunction: function(clusterMarker, count) {
82 | // without jquery $(clusterMarker.getElement()).find('div:first-child').text(count)
83 | clusterMarker.getElement().querySelector('div:first-child').innerText = count;
84 | }
85 |
86 | });
87 |
88 | return cluster;
89 | })
90 |
91 | return (
92 |
93 | );
94 | }
95 |
96 | function MyMap() {
97 | const navermaps = useNavermaps();
98 |
99 | return (
100 |
109 |
110 |
111 | )
112 | }
113 |
114 | return (
115 |
121 |
122 |
123 | );
124 | }}
125 |
--------------------------------------------------------------------------------
/website/src/pages/examples/marker-tutorial-1-simple.mdx:
--------------------------------------------------------------------------------
1 | import { Suspense } from 'react'
2 | import { Playground, Props } from 'next-docz'
3 | import { Container as MapDiv, NaverMap, Marker, useNavermaps } from 'react-naver-maps'
4 |
5 | # 지도 기본 예제
6 |
7 | 네이버지도 공식 튜토리얼 [마커 표시하기](https://navermaps.github.io/maps.js.ncp/docs/tutorial-1-marker-simple.example.html)의 구현 예시입니다.
8 |
9 |
15 | {() => {
16 | function MyMap() {
17 | // instead of window.naver.maps
18 | const navermaps = useNavermaps();
19 |
20 | return (
21 |
25 |
28 |
29 | );
30 | }
31 |
32 | return (
33 |
37 |
38 |
39 | );
40 | }}
41 |
42 |
43 |
--------------------------------------------------------------------------------
/website/src/pages/guides/controlled-tutorial.mdx:
--------------------------------------------------------------------------------
1 | TODO
--------------------------------------------------------------------------------
/website/src/pages/guides/core-concepts.mdx:
--------------------------------------------------------------------------------
1 | # Core concepts
2 |
3 | React Naver Maps는 최대한 Naver Maps의 기본 기능을 유지하되 몇가지 React 에서 다루기 용이한 몇가지 기능을 제공합니다.
4 |
5 | ### React Component interface
6 |
7 | Naver Map의 요소들을 React Component형태로 mount/unmount 할 수 있으며 children을 통해 렌더여부를 결정할 수 있습니다.
8 |
9 | ``` jsx
10 | import { NaverMap, Marker } from 'react-naver-maps'
11 |
12 |
13 |
16 |
17 | ```
18 |
19 | ### Better event handler
20 |
21 | React의 EventHandler 방식을 사용하여 더 쉽게 Naver Maps 의 이벤트를 다룰 수 있습니다.
22 |
23 | ``` js
24 | // listener를 추가할 때
25 | map.addListener('zoom_changed', zoomListener); // 또는
26 | window.naver.maps.Event.addListener(map, 'zoom_changed', zoomListener)
27 |
28 | // listener를 제거할 때
29 | map.removeListener(zoomListener); // 또는
30 | window.naver.maps.Event.removeListener(zoomListener)
31 | ```
32 |
33 | ``` jsx
34 | // props로 전달됩니다.
35 |
38 |
39 | // 간단히 props에서 제거합니다.
40 |
43 | ```
44 |
45 | ### Controlled KVO Component
46 |
47 | [Controlled Component](https://reactjs.org/docs/forms.html#controlled-components) 방식을 사용하여 단방향 제어가 가능합니다.
48 |
49 | ``` jsx
50 | import { NaverMap } from 'react-naver-maps';
51 |
52 | function MyMap() {
53 | const [center, setCenter] = useState([37.3595704, 127.105399]);
54 |
55 | return (
56 | <>
57 | {
60 | setCenter(map.getCenter());
61 | }}
62 | />
63 |
66 | >
67 | )
68 | }
69 |
70 | ```
--------------------------------------------------------------------------------
/website/src/pages/guides/customize-overlays.mdx:
--------------------------------------------------------------------------------
1 | import { Playground } from 'next-docz';
2 | import { useState, useRef } from 'react';
3 | import {
4 | Container as MapDiv,
5 | NaverMap,
6 | useNavermaps,
7 | Overlay,
8 | useListener,
9 | Listener,
10 | useMap,
11 | } from 'react-naver-maps';
12 |
13 | # Customize Overlays
14 |
15 | 복잡한 로직을 다룰 수 있도록 직접 Overlay를 생성할 수 있는 여러 유틸리티를 제공하고 있습니다.
16 |
17 |
29 | {() => {
30 | function MyMarkers() {
31 | const navermaps = useNavermaps();
32 |
33 | // 마커를 한번만 생성하기 위해 useState lazy initialize 사용
34 | const [marker1] = useState(() => new navermaps.Marker({
35 | position: { lat: 37.5666103, lng: 126.9783882 },
36 | }));
37 |
38 | // 마커를 한번만 생성하기 위해 useRef 사용
39 | const marker2Ref = useRef(null);
40 | if (!marker2Ref.current) {
41 | marker2Ref.current = new navermaps.Marker({
42 | position: { lat: 37.5657259, lng: 126.97547 },
43 | })
44 | }
45 | const marker2 = marker2Ref.current;
46 |
47 | // hook 으로 이벤트 리스너 등록
48 | useListener(marker1, 'click', () => window.alert('서울시청 click'))
49 |
50 | return (
51 | <>
52 |
53 |
54 | {/* Component 로 이벤트 리스너 등록 */}
55 | window.alert('덕수궁 click')} />
56 |
57 | >
58 | );
59 | }
60 |
61 | function Buttons() {
62 | // Map의 instance를 가져옵니다.
63 | const naverMap = useMap();
64 |
65 | return (
66 |
71 |
77 |
84 |
85 | );
86 | }
87 |
88 | return (
89 |
95 |
96 |
97 |
98 |
99 |
100 | )
101 | }}
102 |
--------------------------------------------------------------------------------
/website/src/pages/guides/for-performance.mdx:
--------------------------------------------------------------------------------
1 | TODO
--------------------------------------------------------------------------------
/website/src/pages/guides/installation.mdx:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | ## Prerequisites
4 |
5 | 시작에 앞서 Naver Maps 클라이언트 아이디 발급이 필요합니다. [공식문서](https://navermaps.github.io/maps.js.ncp/docs/tutorial-1-Getting-Client-ID.html)를 참고하여
6 | 클라이언트 아이디 발급을 진행해주세요.
7 |
8 | ## Add React Naver Maps
9 |
10 | ``` bash
11 | npm install react-naver-maps@next
12 | ```
13 |
--------------------------------------------------------------------------------
/website/src/pages/guides/introduction.mdx:
--------------------------------------------------------------------------------
1 | import { Playground } from 'next-docz'
2 | import { Container as MapDiv, NaverMap, Marker } from 'react-naver-maps'
3 |
4 | # React Naver Maps
5 |
6 | React Naver Maps 는 [Naver Maps](https://navermaps.github.io/maps.js.ncp/)의 Non-Official React binding 라이브러리입니다.
7 |
8 |
9 |
15 |
20 |
21 |
24 |
25 |
26 |
27 |
28 | - [Quick Start](/guides/quickstart)
29 | - [Core Conepts](/guides/core-concepts)
30 | - [Migration guide from v0.0](/guides/migration-guide-from-0.0)
31 | - [Customize Overlays](/guides/customize-overlays)
32 |
--------------------------------------------------------------------------------
/website/src/pages/guides/migration-guide-from-0.0.mdx:
--------------------------------------------------------------------------------
1 | # Migration Guide from v0.0
2 |
3 | ## Removed ``
4 |
5 | 기존 RenderAfterNavermapsLoaded 는 SSR 문제와 컴포넌트 렌더 시의 모호함이 있었고 v0.1 에서 제거되었습니다.
6 | 대신, Application Root 에 ``를 추가해주세요
7 |
8 | ``` jsx
9 | // v0.0 (X)
10 | import { RenderAfterNavermapsLoaded } from 'react-naver-maps'
11 |
12 |
15 | ...
16 |
17 |
18 | // v0.1 (O)
19 | import { NavermapsProvider } from 'react-naver-maps'
20 |
21 |
24 |
25 |
26 | ```
27 |
28 | ## Add ``
29 |
30 | 기존 ``컴포넌트가 div의 역할과 navermaps 컴포넌트 역할을 동시에 수행하다보니 `HTMLDivElement`와 `naver.maps.Map` API가 섞여 있었고,
31 | SSR렌더가 되지 않는 등 분리하여 컨트롤하기 어려운 문제가 있었습니다.
32 |
33 | 이를 해결하기 위해 MapDiv 역할을 하는 Container 컴포넌트가 필수적으로 추가되었습니다.
34 |
35 | - `` 컴포넌트에서 id, style, className, HTML event handler를 제거해주세요.
36 | - ``를 감싸는 ``를 추가하고 위에서 제거한 요소를 추가해주세요.
37 |
38 | ``` jsx
39 | // v0.0 (X)
40 | import { NaverMap } from 'react-naver-maps'
41 |
42 | {}} // navermaps event? or html div event?
46 | />
47 |
48 | // v0.1 (O)
49 | import { Container as MapDiv, NaverMap } from 'react-naver-maps'
50 |
51 | // SSR Ready
52 | {}} // html div event
56 | >
57 | {}} // navermaps event
59 | />
60 |
61 | ```
62 |
63 |
64 | ## Suspensed `useNavermaps()`
65 |
66 | `window.naver.maps` 를 사용하고자 할 경우 `useNavermaps()`를 사용해주세요.
67 | React Naver Maps 는 내부적으로 Suspense 를 통해 navermaps client 를 가져오고 있습니다.
68 |
69 | ``는 Suspense를 내장하고 있으므로 ``내부에서 호출할 경우에는 ``를 사용하지 않아도 됩니다.
70 |
71 | ``` jsx
72 | // v0.1 (O)
73 | import { Container as MapDiv, useNavermaps } from 'react-naver-maps'
74 |
75 | <>
76 |
77 |
78 |
79 | // Suspense가 내장되어있습니다
80 |
81 |
82 | >
83 |
84 | function MyComponent() {
85 | const navermaps = useNavermaps(); // Suspensed
86 |
87 | return null;
88 | }
89 | ```
90 |
91 | ## Typescript 지원
92 |
93 | 타입스크립트가 내장되어 지원됩니다.
94 |
95 |
96 | ## 변경사항 종합
97 |
98 | ``` jsx
99 | // v0.0 (X)
100 | import { RenderAfterNavermapsLoaded, NaverMap, useNavermaps } from 'react-naver-maps'
101 |
102 | function App() {
103 | return
104 | }
105 |
106 | function MyMap() {
107 | ...
108 | return (
109 |
110 | <>
111 |
112 | {}}
116 | />
117 | >
118 |
119 | )
120 | }
121 |
122 | function Call() {
123 | const navermaps = useNavermaps()
124 | console.log(navermaps)
125 | return null;
126 | }
127 |
128 | ```
129 |
130 | ``` jsx
131 | // v0.1 (O)
132 | import { Suspense } from 'react'
133 | import { Container as MapDiv, NaverMap, useNavermaps, NavermapsProvider } from 'react-naver-maps'
134 |
135 | // Your App Root
136 | function App() {
137 | return (
138 |
139 |
140 |
141 | )
142 | }
143 |
144 | function MyMap() {
145 | ...
146 | return (
147 | <>
148 |
149 |
150 |
151 | {}}
155 | >
156 | {}}
158 | />
159 |
160 | >
161 | )
162 | }
163 |
164 | function Call() {
165 | const navermaps = useNavermaps()
166 | console.log(navermaps)
167 | return null;
168 | }
169 |
170 | ```
--------------------------------------------------------------------------------
/website/src/pages/guides/quickstart.mdx:
--------------------------------------------------------------------------------
1 | import { Playground } from 'next-docz'
2 | import { Suspense } from 'react'
3 | import { Container as MapDiv, NaverMap, Marker, useNavermaps } from 'react-naver-maps'
4 |
5 | # Quickstart
6 |
7 | Application 루트에 NaverMaps Provider를 제공해야합니다. 사용하시는 프레임워크에 맞춰 `` 를 감싸주세요.
8 |
9 | ``` jsx
10 | import { NavermapsProvider } from 'react-naver-maps';
11 |
12 | function App() {
13 | return (
14 |
18 |
19 |
20 | )
21 | }
22 | ```
23 |
24 |
30 | {() => {
31 | function MyMap() {
32 | // instead of window.naver.maps
33 | const navermaps = useNavermaps();
34 |
35 | return (
36 |
40 |
43 |
44 | );
45 | }
46 |
47 | return (
48 |
52 |
53 |
54 | );
55 | }}
56 |
57 |
--------------------------------------------------------------------------------
/website/src/pages/guides/suspensed-use-navermaps.mdx:
--------------------------------------------------------------------------------
1 | # Suspensed `useNavermaps()`
2 |
3 | `window.naver.maps`를 사용하고자 할 경우 `useNavermaps()`를 사용해주세요.
4 | React Naver Maps 는 내부적으로 Suspense 를 통해 navermaps client 를 가져오고 있습니다.
5 |
6 | ``는 Suspense를 내장하고 있으므로 ``내부에서 호출할 경우에는 ``를 사용하지 않아도 됩니다.
7 |
8 | ``` jsx
9 | import { Container as MapDiv, useNavermaps } from 'react-naver-maps'
10 |
11 | <>
12 |
13 |
14 |
15 | // Suspense가 내장되어있습니다
16 |
17 |
18 | >
19 | ```
--------------------------------------------------------------------------------
/website/src/pages/guides/without-component.mdx:
--------------------------------------------------------------------------------
1 | TODO
--------------------------------------------------------------------------------
/website/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import type { NextPage } from 'next';
2 |
3 | import Intro from './guides/introduction.mdx';
4 |
5 | const Home: NextPage = () => {
6 | return (
7 |
8 | );
9 | };
10 |
11 | export default Home;
12 |
--------------------------------------------------------------------------------
/website/src/samples/accident-death.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | // https://navermaps.github.io/maps.js.ncp/docs/data/accidentdeath.js
3 | var accidentDeath={searchResult:{accidentDeath:[{year:"2014",dt_006:"2014031411",dt_006_lv8:"40",cd_008:"1",cd_007:"6",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1335",cd_003_lv1:"1300",cd_014_lv1:"02",cd_014_lv2:"22",cd_014:"22",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"05",cd_036_1:"17",cd_036_1_lv2:"01",cd_036_2:"03",x_coord:"960824 ",y_coord:"1905922 ",grd_lo:"127.05882791",grd_la:"37.15120216"},{year:"2014",dt_006:"2014031313",dt_006_lv8:"23",cd_008:"1",cd_007:"5",no_010:1,injpsn_co:9,no_011:3,no_012:5,no_013:0,cd_003:"1909",cd_003_lv1:"1900",cd_014_lv1:"02",cd_014_lv2:"21",cd_014:"21",cd_027_1_lv1:"01",cd_027_1_lv2:"05",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"03",cd_036_1:"10",cd_036_1_lv2:"02",cd_036_2:"08",x_coord:"1045257 ",y_coord:"1843983 ",grd_lo:"128.01221855",grd_la:"36.58983435"},{year:"2014",dt_006:"2014031121",dt_006_lv8:"10",cd_008:"2",cd_007:"3",no_010:1,injpsn_co:3,no_011:0,no_012:2,no_013:0,cd_003:"2701",cd_003_lv1:"2700",cd_014_lv1:"01",cd_014_lv2:"15",cd_014:"05",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"01",cd_036_1:"02",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"974095 ",y_coord:"1828345 ",grd_lo:"127.21091471",grd_la:"36.45233748"},{year:"2014",dt_006:"2014031205",dt_006_lv8:"37",cd_008:"2",cd_007:"4",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1405",cd_003_lv1:"1400",cd_014_lv1:"03",cd_014_lv2:"31",cd_014:"41",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"02",cd_043:"07",cd_036_1_lv1:"05",cd_036_1:"17",cd_036_1_lv2:"00",cd_036_2:"00",x_coord:"1094818 ",y_coord:"2021380 ",grd_lo:"128.58271469",grd_la:"38.18770048"},{year:"2014",dt_006:"2014031209",dt_006_lv8:"50",cd_008:"1",cd_007:"4",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"2202",cd_003_lv1:"2200",cd_014_lv1:"01",cd_014_lv2:"14",cd_014:"04",cd_027_1_lv1:"01",cd_027_1_lv2:"14",cd_043_lv1:"01",cd_043:"01",cd_036_1_lv1:"04",cd_036_1:"16",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"1103199 ",y_coord:"1766794 ",grd_lo:"128.64345545",grd_la:"35.89231981"},{year:"2014",dt_006:"2014031102",dt_006_lv8:"20",cd_008:"2",cd_007:"3",no_010:1,injpsn_co:3,no_011:1,no_012:1,no_013:0,cd_003:"1517",cd_003_lv1:"1500",cd_014_lv1:"03",cd_014_lv2:"34",cd_014:"45",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"02",cd_043:"06",cd_036_1_lv1:"01",cd_036_1:"03",cd_036_1_lv2:"00",cd_036_2:"00",x_coord:"1016945 ",y_coord:"1880378 ",grd_lo:"127.70505351",grd_la:"36.94499381"},{year:"2014",dt_006:"2014032319",dt_006_lv8:"25",cd_008:"2",cd_007:"1",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1308",cd_003_lv1:"1300",cd_014_lv1:"01",cd_014_lv2:"15",cd_014:"05",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"02",cd_043:"07",cd_036_1_lv1:"01",cd_036_1:"03",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"960686 ",y_coord:"1897749 ",grd_lo:"127.05770261",grd_la:"37.07752506"},{year:"2014",dt_006:"2014021718",dt_006_lv8:"15",cd_008:"2",cd_007:"2",no_010:1,injpsn_co:2,no_011:1,no_012:0,no_013:0,cd_003:"1808",cd_003_lv1:"1800",cd_014_lv1:"03",cd_014_lv2:"31",cd_014:"41",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"02",cd_043:"06",cd_036_1_lv1:"03",cd_036_1:"09",cd_036_1_lv2:"00",cd_036_2:"00",x_coord:"1025531 ",y_coord:"1669535 ",grd_lo:"127.77985607",grd_la:"35.02048014"},{year:"2014",dt_006:"2014021919",dt_006_lv8:"40",cd_008:"2",cd_007:"4",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1902",cd_003_lv1:"1900",cd_014_lv1:"02",cd_014_lv2:"22",cd_014:"22",cd_027_1_lv1:"01",cd_027_1_lv2:"06",cd_043_lv1:"02",cd_043:"06",cd_036_1_lv1:"01",cd_036_1:"03",cd_036_1_lv2:"03",cd_036_2:"12",x_coord:"1169555 ",y_coord:"1780150 ",grd_lo:"129.38125275",grd_la:"36.00342549"},{year:"2014",dt_006:"2014022523",dt_006_lv8:"40",cd_008:"2",cd_007:"3",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1110",cd_003_lv1:"1100",cd_014_lv1:"01",cd_014_lv2:"15",cd_014:"05",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"01",cd_036_1:"03",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"949594 ",y_coord:"1950269 ",grd_lo:"126.92935115",grd_la:"37.55038341"},{year:"2014",dt_006:"2014022522",dt_006_lv8:"00",cd_008:"2",cd_007:"3",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"2402",cd_003_lv1:"2400",cd_014_lv1:"01",cd_014_lv2:"11",cd_014:"01",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"01",cd_036_1:"03",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"943638 ",y_coord:"1684345 ",grd_lo:"126.88119723",grd_la:"35.15276899"},{year:"2014",dt_006:"2014022702",dt_006_lv8:"05",cd_008:"2",cd_007:"5",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1324",cd_003_lv1:"1300",cd_014_lv1:"03",cd_014_lv2:"31",cd_014:"41",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"01",cd_036_1:"01",cd_036_1_lv2:"00",cd_036_2:"00",x_coord:"998124 ",y_coord:"1925390 ",grd_lo:"127.48762685",grd_la:"37.33953297"},{year:"2014",dt_006:"2014022506",dt_006_lv8:"05",cd_008:"1",cd_007:"3",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1902",cd_003_lv1:"1900",cd_014_lv1:"02",cd_014_lv2:"24",cd_014:"25",cd_027_1_lv1:"01",cd_027_1_lv2:"06",cd_043_lv1:"02",cd_043:"06",cd_036_1_lv1:"05",cd_036_1:"17",cd_036_1_lv2:"03",cd_036_2:"12",x_coord:"1168677 ",y_coord:"1784188 ",grd_lo:"129.37237675",grd_la:"36.03996462"},{year:"2014",dt_006:"2014030619",dt_006_lv8:"59",cd_008:"2",cd_007:"5",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1324",cd_003_lv1:"1300",cd_014_lv1:"02",cd_014_lv2:"22",cd_014:"22",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"01",cd_036_1:"03",cd_036_1_lv2:"99",cd_036_2:"99",x_coord:"996280 ",y_coord:"1919670 ",grd_lo:"127.45803888",grd_la:"37.27594171"},{year:"2014",dt_006:"2014030413",dt_006_lv8:"15",cd_008:"1",cd_007:"3",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1501",cd_003_lv1:"1500",cd_014_lv1:"01",cd_014_lv2:"12",cd_014:"02",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"01",cd_036_1:"03",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"996534 ",y_coord:"1847133 ",grd_lo:"127.46123671",grd_la:"36.62205754"},{year:"2014",dt_006:"2014032110",dt_006_lv8:"55",cd_008:"1",cd_007:"6",no_010:1,injpsn_co:2,no_011:0,no_012:1,no_013:0,cd_003:"2014",cd_003_lv1:"2000",cd_014_lv1:"02",cd_014_lv2:"23",cd_014:"23",cd_027_1_lv1:"01",cd_027_1_lv2:"04",cd_043_lv1:"02",cd_043:"06",cd_036_1_lv1:"06",cd_036_1:"20",cd_036_1_lv2:"05",cd_036_2:"27",x_coord:"1089070 ",y_coord:"1729025 ",grd_lo:"128.48273629",grd_la:"35.55321975"},{year:"2014",dt_006:"2014032305",dt_006_lv8:"42",cd_008:"2",cd_007:"1",no_010:2,injpsn_co:2,no_011:0,no_012:0,no_013:0,cd_003:"1319",cd_003_lv1:"1300",cd_014_lv1:"02",cd_014_lv2:"22",cd_014:"22",cd_027_1_lv1:"01",cd_027_1_lv2:"06",cd_043_lv1:"02",cd_043:"06",cd_036_1_lv1:"01",cd_036_1:"03",cd_036_1_lv2:"03",cd_036_2:"11",x_coord:"977787 ",y_coord:"1932802 ",grd_lo:"127.24904785",grd_la:"37.39405363"},{year:"2014",dt_006:"2014032014",dt_006_lv8:"30",cd_008:"1",cd_007:"5",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"2306",cd_003_lv1:"2300",cd_014_lv1:"01",cd_014_lv2:"15",cd_014:"05",cd_027_1_lv1:"01",cd_027_1_lv2:"19",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"01",cd_036_1:"02",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"925924 ",y_coord:"1956027 ",grd_lo:"126.63734058",grd_la:"37.59751713"},{year:"2014",dt_006:"2014032000",dt_006_lv8:"25",cd_008:"2",cd_007:"5",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1124",cd_003_lv1:"1100",cd_014_lv1:"01",cd_014_lv2:"11",cd_014:"01",cd_027_1_lv1:"01",cd_027_1_lv2:"02",cd_043_lv1:"02",cd_043:"07",cd_036_1_lv1:"01",cd_036_1:"03",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"959558 ",y_coord:"1959113 ",grd_lo:"127.0416618",grd_la:"37.63058777"},{year:"2014",dt_006:"2014032008",dt_006_lv8:"30",cd_008:"1",cd_007:"5",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1602",cd_003_lv1:"1600",cd_014_lv1:"01",cd_014_lv2:"15",cd_014:"05",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"01",cd_036_1:"04",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"967404 ",y_coord:"1866788 ",grd_lo:"127.134616",grd_la:"36.79868986"},{year:"2014",dt_006:"2014021710",dt_006_lv8:"30",cd_008:"1",cd_007:"2",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1316",cd_003_lv1:"1300",cd_014_lv1:"02",cd_014_lv2:"24",cd_014:"25",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"02",cd_043:"06",cd_036_1_lv1:"06",cd_036_1:"20",cd_036_1_lv2:"03",cd_036_2:"11",x_coord:"935683 ",y_coord:"1936996 ",grd_lo:"126.77303474",grd_la:"37.42988837"},{year:"2014",dt_006:"2014021711",dt_006_lv8:"40",cd_008:"1",cd_007:"2",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"2205",cd_003_lv1:"2200",cd_014_lv1:"02",cd_014_lv2:"22",cd_014:"22",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"08",cd_036_1:"22",cd_036_1_lv2:"07",cd_036_2:"21",x_coord:"1099252 ",y_coord:"1769117 ",grd_lo:"128.60001944",grd_la:"35.9136687"},{year:"2014",dt_006:"2014022500",dt_006_lv8:"50",cd_008:"2",cd_007:"3",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"2101",cd_003_lv1:"2100",cd_014_lv1:"01",cd_014_lv2:"11",cd_014:"01",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"01",cd_036_1:"03",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"912436 ",y_coord:"1502978 ",grd_lo:"126.55714252",grd_la:"33.51511658"},{year:"2014",dt_006:"2014022518",dt_006_lv8:"45",cd_008:"2",cd_007:"3",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1315",cd_003_lv1:"1300",cd_014_lv1:"03",cd_014_lv2:"34",cd_014:"45",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"05",cd_036_1:"27",cd_036_1_lv2:"00",cd_036_2:"00",x_coord:"948319 ",y_coord:"1902471 ",grd_lo:"126.91824957",grd_la:"37.11948787"},{year:"2014",dt_006:"2014022420",dt_006_lv8:"00",cd_008:"2",cd_007:"2",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1903",cd_003_lv1:"1900",cd_014_lv1:"03",cd_014_lv2:"34",cd_014:"45",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"05",cd_036_1:"17",cd_036_1_lv2:"00",cd_036_2:"00",x_coord:"1158224 ",y_coord:"1779193 ",grd_lo:"129.25539638",grd_la:"35.99670694"},{year:"2014",dt_006:"2014022814",dt_006_lv8:"20",cd_008:"1",cd_007:"6",no_010:1,injpsn_co:3,no_011:2,no_012:0,no_013:0,cd_003:"1706",cd_003_lv1:"1700",cd_014_lv1:"03",cd_014_lv2:"31",cd_014:"41",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"02",cd_036_1_lv1:"01",cd_036_1:"03",cd_036_1_lv2:"00",cd_036_2:"00",x_coord:"943546 ",y_coord:"1754962 ",grd_lo:"126.8752818",grd_la:"35.78946116"},{year:"2014",dt_006:"2014031020",dt_006_lv8:"05",cd_008:"2",cd_007:"2",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1501",cd_003_lv1:"1500",cd_014_lv1:"01",cd_014_lv2:"11",cd_014:"01",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"02",cd_036_1:"06",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"998976 ",y_coord:"1850825 ",grd_lo:"127.48854279",grd_la:"36.65534659"},{year:"2014",dt_006:"2014030913",dt_006_lv8:"00",cd_008:"1",cd_007:"1",no_010:1,injpsn_co:3,no_011:0,no_012:2,no_013:0,cd_003:"1403",cd_003_lv1:"1400",cd_014_lv1:"02",cd_014_lv2:"23",cd_014:"23",cd_027_1_lv1:"01",cd_027_1_lv2:"13",cd_043_lv1:"02",cd_043:"06",cd_036_1_lv1:"05",cd_036_1:"17",cd_036_1_lv2:"01",cd_036_2:"02",x_coord:"1141750 ",y_coord:"1948544 ",grd_lo:"129.10417404",grd_la:"37.52531751"},{year:"2014",dt_006:"2014031313",dt_006_lv8:"25",cd_008:"1",cd_007:"5",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1906",cd_003_lv1:"1900",cd_014_lv1:"01",cd_014_lv2:"15",cd_014:"05",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"02",cd_043:"07",cd_036_1_lv1:"02",cd_036_1:"06",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"1075254 ",y_coord:"1790209 ",grd_lo:"128.33608223",grd_la:"36.10595442"},{year:"2014",dt_006:"2014032122",dt_006_lv8:"50",cd_008:"2",cd_007:"6",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1334",cd_003_lv1:"1300",cd_014_lv1:"03",cd_014_lv2:"31",cd_014:"41",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"02",cd_043:"06",cd_036_1_lv1:"05",cd_036_1:"18",cd_036_1_lv2:"00",cd_036_2:"00",x_coord:"980954 ",y_coord:"1962002 ",grd_lo:"127.28406961",grd_la:"37.6573202"},{year:"2014",dt_006:"2014032022",dt_006_lv8:"40",cd_008:"2",cd_007:"5",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1116",cd_003_lv1:"1100",cd_014_lv1:"01",cd_014_lv2:"12",cd_014:"02",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"01",cd_036_1:"03",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"959077 ",y_coord:"1945776 ",grd_lo:"127.03695551",grd_la:"37.51035657"},{year:"2014",dt_006:"2014032020",dt_006_lv8:"12",cd_008:"2",cd_007:"5",no_010:2,injpsn_co:3,no_011:1,no_012:0,no_013:0,cd_003:"1515",cd_003_lv1:"1500",cd_014_lv1:"02",cd_014_lv2:"21",cd_014:"21",cd_027_1_lv1:"01",cd_027_1_lv2:"05",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"01",cd_036_1:"03",cd_036_1_lv2:"03",cd_036_2:"12",x_coord:"994979 ",y_coord:"1888570 ",grd_lo:"127.44109729",grd_la:"36.99899965"},{year:"2014",dt_006:"2014032401",dt_006_lv8:"50",cd_008:"2",cd_007:"2",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1902",cd_003_lv1:"1900",cd_014_lv1:"03",cd_014_lv2:"35",cd_014:"46",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"02",cd_043:"07",cd_036_1_lv1:"05",cd_036_1:"18",cd_036_1_lv2:"00",cd_036_2:"00",x_coord:"1169850 ",y_coord:"1776383 ",grd_lo:"129.34523617",grd_la:"35.9717673"},{year:"2014",dt_006:"2014032215",dt_006_lv8:"05",cd_008:"1",cd_007:"7",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1818",cd_003_lv1:"1800",cd_014_lv1:"03",cd_014_lv2:"34",cd_014:"45",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"09",cd_036_1:"23",cd_036_1_lv2:"00",cd_036_2:"00",x_coord:"971775 ",y_coord:"1617925 ",grd_lo:"127.19234731",grd_la:"34.55500878"},{year:"2014",dt_006:"2014032313",dt_006_lv8:"20",cd_008:"1",cd_007:"1",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1923",cd_003_lv1:"1900",cd_014_lv1:"01",cd_014_lv2:"15",cd_014:"05",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"03",cd_036_1:"11",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"1079990 ",y_coord:"1750319 ",grd_lo:"128.38467941",grd_la:"35.74596844"},{year:"2014",dt_006:"2014022421",dt_006_lv8:"47",cd_008:"2",cd_007:"2",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1307",cd_003_lv1:"1300",cd_014_lv1:"03",cd_014_lv2:"31",cd_014:"41",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"02",cd_043:"07",cd_036_1_lv1:"03",cd_036_1:"10",cd_036_1_lv2:"00",cd_036_2:"00",x_coord:"941212 ",y_coord:"1927839 ",grd_lo:"126.83625221",grd_la:"37.34772203"},{year:"2014",dt_006:"2014022615",dt_006_lv8:"04",cd_008:"1",cd_007:"4",no_010:1,injpsn_co:7,no_011:1,no_012:5,no_013:0,cd_003:"1720",cd_003_lv1:"1700",cd_014_lv1:"02",cd_014_lv2:"22",cd_014:"22",cd_027_1_lv1:"01",cd_027_1_lv2:"06",cd_043_lv1:"02",cd_043:"06",cd_036_1_lv1:"01",cd_036_1:"01",cd_036_1_lv2:"01",cd_036_2:"03",x_coord:"932324 ",y_coord:"1749150 ",grd_lo:"126.75159918",grd_la:"35.73635364"},{year:"2014",dt_006:"2014022613",dt_006_lv8:"25",cd_008:"1",cd_007:"4",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1720",cd_003_lv1:"1700",cd_014_lv1:"03",cd_014_lv2:"34",cd_014:"45",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"05",cd_036_1:"18",cd_036_1_lv2:"00",cd_036_2:"00",x_coord:"921352 ",y_coord:"1746589 ",grd_lo:"126.62620444",grd_la:"35.71042571"},{year:"2014",dt_006:"2014022617",dt_006_lv8:"00",cd_008:"1",cd_007:"4",no_010:1,injpsn_co:2,no_011:1,no_012:0,no_013:0,cd_003:"1336",cd_003_lv1:"1300",cd_014_lv1:"02",cd_014_lv2:"23",cd_014:"24",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"05",cd_036_1:"17",cd_036_1_lv2:"01",cd_036_2:"04",x_coord:"955518 ",y_coord:"1931773 ",grd_lo:"126.99753157",grd_la:"37.38397722"},{year:"2014",dt_006:"2014022812",dt_006_lv8:"25",cd_008:"1",cd_007:"6",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1602",cd_003_lv1:"1600",cd_014_lv1:"01",cd_014_lv2:"11",cd_014:"01",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"02",cd_043:"06",cd_036_1_lv1:"03",cd_036_1:"11",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"967640 ",y_coord:"1867055 ",grd_lo:"127.13725004",grd_la:"36.80110485"},{year:"2014",dt_006:"2014030404",dt_006_lv8:"45",cd_008:"2",cd_007:"3",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1321",cd_003_lv1:"1300",cd_014_lv1:"03",cd_014_lv2:"31",cd_014:"41",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"02",cd_043:"06",cd_036_1_lv1:"01",cd_036_1:"03",cd_036_1_lv2:"00",cd_036_2:"00",x_coord:"990184 ",y_coord:"2008157 ",grd_lo:"127.38808533",grd_la:"38.07346448"},{year:"2014",dt_006:"2014030908",dt_006_lv8:"00",cd_008:"1",cd_007:"1",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1303",cd_003_lv1:"1300",cd_014_lv1:"03",cd_014_lv2:"31",cd_014:"41",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"03",cd_036_1_lv1:"03",cd_036_1:"10",cd_036_1_lv2:"00",cd_036_2:"00",x_coord:"965734 ",y_coord:"1931973 ",grd_lo:"127.11291942",grd_la:"37.38621395"},{year:"2014",dt_006:"2014030412",dt_006_lv8:"30",cd_008:"1",cd_007:"3",no_010:1,injpsn_co:3,no_011:2,no_012:0,no_013:0,cd_003:"1604",cd_003_lv1:"1600",cd_014_lv1:"03",cd_014_lv2:"31",cd_014:"41",cd_027_1_lv1:"01",cd_027_1_lv2:"02",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"01",cd_036_1:"01",cd_036_1_lv2:"00",cd_036_2:"00",x_coord:"923293 ",y_coord:"1820676 ",grd_lo:"126.64478935",grd_la:"36.38048771"},{year:"2014",dt_006:"2014031619",dt_006_lv8:"00",cd_008:"2",cd_007:"1",no_010:2,injpsn_co:5,no_011:0,no_012:3,no_013:0,cd_003:"1803",cd_003_lv1:"1800",cd_014_lv1:"02",cd_014_lv2:"21",cd_014:"21",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"03",cd_036_1:"10",cd_036_1_lv2:"02",cd_036_2:"08",x_coord:"1012686 ",y_coord:"1643581 ",grd_lo:"127.63528585",grd_la:"34.79346657"},{year:"2014",dt_006:"2014031506",dt_006_lv8:"03",cd_008:"1",cd_007:"7",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1214",cd_003_lv1:"1200",cd_014_lv1:"01",cd_014_lv2:"11",cd_014:"01",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"01",cd_036_1_lv1:"05",cd_036_1:"17",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"1146622 ",y_coord:"1684355 ",grd_lo:"129.10953528",grd_la:"35.14374806"},{year:"2014",dt_006:"2014031612",dt_006_lv8:"36",cd_008:"1",cd_007:"1",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1819",cd_003_lv1:"1800",cd_014_lv1:"03",cd_014_lv2:"32",cd_014:"42",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"05",cd_036_1:"18",cd_036_1_lv2:"00",cd_036_2:"00",x_coord:"957984 ",y_coord:"1640641 ",grd_lo:"127.04090065",grd_la:"34.75938267"},{year:"2014",dt_006:"2014031509",dt_006_lv8:"17",cd_008:"1",cd_007:"7",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1315",cd_003_lv1:"1300",cd_014_lv1:"03",cd_014_lv2:"34",cd_014:"45",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"08",cd_036_1:"22",cd_036_1_lv2:"00",cd_036_2:"00",x_coord:"940451 ",y_coord:"1901457 ",grd_lo:"126.82964478",grd_la:"37.1098887"},{year:"2014",dt_006:"2014031623",dt_006_lv8:"45",cd_008:"2",cd_007:"1",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1723",cd_003_lv1:"1700",cd_014_lv1:"01",cd_014_lv2:"15",cd_014:"05",cd_027_1_lv1:"01",cd_027_1_lv2:"02",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"01",cd_036_1:"03",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"948275 ",y_coord:"1769033 ",grd_lo:"126.92669777",grd_la:"35.91658175"},{year:"2014",dt_006:"2014031500",dt_006_lv8:"30",cd_008:"2",cd_007:"7",no_010:2,injpsn_co:3,no_011:0,no_012:1,no_013:0,cd_003:"1906",cd_003_lv1:"1900",cd_014_lv1:"01",cd_014_lv2:"15",cd_014:"05",cd_027_1_lv1:"01",cd_027_1_lv2:"02",cd_043_lv1:"02",cd_043:"07",cd_036_1_lv1:"01",cd_036_1:"03",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"1075476 ",y_coord:"1791265 ",grd_lo:"128.33864965",grd_la:"36.11545683"},{year:"2014",dt_006:"2014031513",dt_006_lv8:"10",cd_008:"1",cd_007:"7",no_010:1,injpsn_co:2,no_011:1,no_012:0,no_013:0,cd_003:"1116",cd_003_lv1:"1100",cd_014_lv1:"01",cd_014_lv2:"11",cd_014:"01",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"01",cd_036_1:"03",cd_036_1_lv2:"11",cd_036_2:"25",x_coord:"959667 ",y_coord:"1945850 ",grd_lo:"127.04362711",grd_la:"37.51104954"},{year:"2014",dt_006:"2014031311",dt_006_lv8:"00",cd_008:"1",cd_007:"5",no_010:1,injpsn_co:1,no_011:0,no_012:0,no_013:0,cd_003:"1602",cd_003_lv1:"1600",cd_014_lv1:"02",cd_014_lv2:"24",cd_014:"25",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"07",cd_036_1:"21",cd_036_1_lv2:"03",cd_036_2:"11",x_coord:"969079 ",y_coord:"1866942 ",grd_lo:"127.15338529",grd_la:"36.80013431"},{year:"2014",dt_006:"2014031403",dt_006_lv8:"25",cd_008:"2",cd_007:"6",no_010:1,injpsn_co:3,no_011:0,no_012:2,no_013:0,cd_003:"2404",cd_003_lv1:"2400",cd_014_lv1:"02",cd_014_lv2:"21",cd_014:"21",cd_027_1_lv1:"01",cd_027_1_lv2:"05",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"01",cd_036_1:"03",cd_036_1_lv2:"01",cd_036_2:"03",x_coord:"938616 ",y_coord:"1687283 ",grd_lo:"126.82584497",grd_la:"35.17896554"},{year:"2014",dt_006:"2014031218",dt_006_lv8:"45",cd_008:"2",cd_007:"4",no_010:1,injpsn_co:2,no_011:0,no_012:1,no_013:0,cd_003:"1925",cd_003_lv1:"1900",cd_014_lv1:"02",cd_014_lv2:"24",cd_014:"25",cd_027_1_lv1:"01",cd_027_1_lv2:"12",cd_043_lv1:"01",cd_043:"05",cd_036_1_lv1:"01",cd_036_1:"02",cd_036_1_lv2:"01",cd_036_2:"03",x_coord:"1079831 ",y_coord:"1776561 ",grd_lo:"128.38554907",grd_la:"35.98255487"}]},resultCode:"Success"};
4 |
5 | export { accidentDeath }
6 | /* eslint-enable */
7 |
--------------------------------------------------------------------------------
/website/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true,
17 | "jsxImportSource": "@emotion/react"
18 | },
19 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
20 | "exclude": ["node_modules"]
21 | }
22 |
--------------------------------------------------------------------------------