@d3-maps/react 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENCE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright © 2026 Georgii Bukharov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -51,4 +51,4 @@ If you need strict stylesheet ordering, load your global reset/theme styles befo
51
51
 
52
52
  ## License
53
53
 
54
- MIT licensed. Copyright © 2020 Georgii Bukharov. See [LICENCE](../../LICENCE) for more details.
54
+ MIT licensed. Copyright © 2020 Georgii Bukharov. See [LICENSE](./LICENSE) for more details.
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import "@d3-maps/core/index.css";
2
2
  import * as react0 from "react";
3
- import { CSSProperties, FocusEventHandler, MouseEventHandler, ReactElement, ReactNode, SVGProps } from "react";
4
- import { ExtendedFeature, ExtendedFeatureCollection, GeoPath, GeoProjection } from "d3-geo";
5
- import { mesh } from "topojson-client";
3
+ import { CSSProperties, MouseEventHandler, ReactElement, ReactNode, SVGProps } from "react";
4
+ import { ExtendedFeature, ExtendedFeatureCollection, GeoGraticuleGenerator, GeoPath, GeoProjection } from "d3-geo";
5
+ import "topojson-client";
6
6
  import { D3ZoomEvent, ZoomBehavior } from "d3-zoom";
7
7
 
8
8
  //#region ../core/src/lib/mapObject.d.ts
@@ -12,7 +12,9 @@ import { D3ZoomEvent, ZoomBehavior } from "d3-zoom";
12
12
  */
13
13
  declare const mapObjectState: readonly ["default", "hover", "active"];
14
14
  type MapObjectState = typeof mapObjectState[number];
15
- type MapObjectStyles$1<TStyle> = Partial<Record<MapObjectState, TStyle>>;
15
+ interface MapObject<TStyle = unknown> {
16
+ styles?: Partial<Record<MapObjectState, TStyle>>;
17
+ }
16
18
  //#endregion
17
19
  //#region ../core/src/lib/feature.d.ts
18
20
  /**
@@ -24,11 +26,8 @@ type MapFeature$1 = (ExtendedFeature & Record<string, unknown>) | ExtendedFeatur
24
26
  /**
25
27
  * Shared props contract for a single rendered feature.
26
28
  */
27
- interface MapFeatureProps$1<TStyle = unknown> {
29
+ interface MapFeatureProps$1<TStyle = unknown> extends MapObject<TStyle> {
28
30
  data: MapFeature$1;
29
- styles?: MapObjectStyles$1<TStyle>;
30
- fill?: string;
31
- stroke?: string;
32
31
  }
33
32
  //#endregion
34
33
  //#region ../../node_modules/.pnpm/@types+geojson@7946.0.16/node_modules/@types/geojson/index.d.ts
@@ -367,7 +366,6 @@ type ModifierArgs<P$1 extends unknown[]> = P$1 extends [infer Only] ? Only exten
367
366
  type MethodsToModifiers<T extends object> = { [K in OwnKeys<T> as Extract<T[K], AnyFn> extends never ? never : HasArgs<Extract<T[K], AnyFn>> extends true ? K : never]?: ModifierArgs<Extract<SetterArgs<Extract<T[K], AnyFn>>, unknown[]>> };
368
367
  //#endregion
369
368
  //#region ../core/src/lib/map.d.ts
370
- type MapMesh$1 = ReturnType<typeof mesh>;
371
369
  type MapData = ExtendedFeatureCollection | Topology;
372
370
  type DataTransformer = (features: MapFeature$1[]) => MapFeature$1[];
373
371
  /**
@@ -419,20 +417,28 @@ interface MapContext {
419
417
  height: number;
420
418
  projection?: GeoProjection;
421
419
  features: MapFeature$1[];
422
- mesh?: MapMesh$1;
423
420
  path: GeoPath;
424
- renderPath: (feature: MapFeature$1) => ReturnType<GeoPath>;
425
421
  renderMesh: () => ReturnType<GeoPath>;
426
422
  }
427
423
  //#endregion
424
+ //#region ../core/src/lib/graticule.d.ts
425
+ /**
426
+ * Extra graticule generator method calls to apply before rendering.
427
+ *
428
+ * Uses d3-geo `geoGraticule()` setter method names as keys.
429
+ * Example: `{ step: [[10, 10]], precision: 2.5 }`
430
+ *
431
+ * @see https://d3js.org/d3-geo/shape#geoGraticule
432
+ */
433
+ interface GraticuleConfig extends MethodsToModifiers<GeoGraticuleGenerator> {}
434
+ //#endregion
428
435
  //#region ../core/src/lib/marker.d.ts
429
436
  type MapMarkerCoordinates = [number, number];
430
437
  /**
431
438
  * Shared props contract for marker layers.
432
439
  */
433
- interface MapMarkerProps$1<TStyle = unknown> {
440
+ interface MapMarkerProps$1<TStyle = unknown> extends MapObject<TStyle> {
434
441
  coordinates?: MapMarkerCoordinates;
435
- styles?: MapObjectStyles$1<TStyle>;
436
442
  }
437
443
  //#endregion
438
444
  //#region ../core/src/lib/zoom.d.ts
@@ -478,63 +484,48 @@ interface MapFeatureProps extends MapFeatureProps$1<CSSProperties>, Omit<SVGProp
478
484
  declare function MapFeature({
479
485
  data,
480
486
  styles,
481
- fill,
482
- stroke,
483
487
  onMouseEnter,
484
488
  onMouseLeave,
485
489
  onMouseDown,
486
490
  onMouseUp,
487
- onClick,
488
- onFocus,
489
- onBlur,
490
491
  ...pathProps
491
492
  }: MapFeatureProps): ReactElement | null;
492
493
  //#endregion
493
- //#region src/hooks/useMapObject.d.ts
494
- type MapObjectStyle = CSSProperties;
495
- type MapObjectStyles = MapObjectStyles$1<MapObjectStyle>;
496
- interface UseMapObjectOptions<TElement extends Element> {
497
- styles?: MapObjectStyles;
498
- onMouseEnter?: MouseEventHandler<TElement>;
499
- onMouseLeave?: MouseEventHandler<TElement>;
500
- onMouseDown?: MouseEventHandler<TElement>;
501
- onMouseUp?: MouseEventHandler<TElement>;
502
- onClick?: MouseEventHandler<TElement>;
503
- onFocus?: FocusEventHandler<TElement>;
504
- onBlur?: FocusEventHandler<TElement>;
505
- }
506
- interface UseMapObjectResult<TElement extends Element> {
507
- computedStyle: MapObjectStyle | undefined;
508
- onMouseEnter: MouseEventHandler<TElement>;
509
- onMouseLeave: MouseEventHandler<TElement>;
510
- onMouseDown: MouseEventHandler<TElement>;
511
- onMouseUp: MouseEventHandler<TElement>;
512
- onClick: MouseEventHandler<TElement>;
513
- onFocus: FocusEventHandler<TElement>;
514
- onBlur: FocusEventHandler<TElement>;
515
- }
516
- declare function useMapObject<TElement extends Element>(options: UseMapObjectOptions<TElement>): UseMapObjectResult<TElement>;
517
- //#endregion
518
494
  //#region src/components/MapFeatures.d.ts
519
495
  interface MapFeaturesRenderProps {
520
496
  features: MapFeature$1[];
521
497
  }
522
498
  type MapFeaturesChildren = ReactNode | ((props: MapFeaturesRenderProps) => ReactNode);
523
- interface MapFeaturesProps {
499
+ type MapFeaturesElementProps = Omit<SVGProps<SVGGElement>, 'children'>;
500
+ interface MapFeaturesProps extends MapFeaturesElementProps, MapObject<CSSProperties> {
524
501
  idKey?: string;
525
- fill?: string;
526
- stroke?: string;
527
- styles?: MapObjectStyles;
528
502
  children?: MapFeaturesChildren;
529
503
  }
530
504
  declare function MapFeatures({
531
505
  idKey,
532
- fill,
533
- stroke,
534
506
  styles,
535
- children
507
+ children,
508
+ ...groupProps
536
509
  }: MapFeaturesProps): ReactElement;
537
510
  //#endregion
511
+ //#region src/components/MapGraticule.d.ts
512
+ interface MapGraticuleProps extends Omit<SVGProps<SVGPathElement>, 'children' | 'd' | 'style' | 'fill'>, MapObject<CSSProperties> {
513
+ config?: GraticuleConfig;
514
+ background?: boolean | string;
515
+ border?: boolean | string;
516
+ }
517
+ declare function MapGraticule({
518
+ config,
519
+ background,
520
+ border,
521
+ styles,
522
+ onMouseEnter,
523
+ onMouseLeave,
524
+ onMouseDown,
525
+ onMouseUp,
526
+ ...pathProps
527
+ }: MapGraticuleProps): ReactElement | null;
528
+ //#endregion
538
529
  //#region src/components/MapMarker.d.ts
539
530
  interface MapMarkerProps extends MapMarkerProps$1<CSSProperties>, Omit<SVGProps<SVGGElement>, 'style'> {}
540
531
  declare function MapMarker({
@@ -545,29 +536,17 @@ declare function MapMarker({
545
536
  onMouseLeave,
546
537
  onMouseDown,
547
538
  onMouseUp,
548
- onClick,
549
- onFocus,
550
- onBlur,
551
539
  ...groupProps
552
540
  }: MapMarkerProps): ReactElement;
553
541
  //#endregion
554
542
  //#region src/components/MapMesh.d.ts
555
- interface MapMeshProps extends Omit<SVGProps<SVGPathElement>, 'children' | 'd' | 'style'> {
556
- fill?: string;
557
- stroke?: string;
558
- styles?: MapObjectStyles;
559
- }
543
+ interface MapMeshProps extends Omit<SVGProps<SVGPathElement>, 'children' | 'd' | 'style'>, MapObject<CSSProperties> {}
560
544
  declare function MapMesh({
561
- fill,
562
- stroke,
563
545
  styles,
564
546
  onMouseEnter,
565
547
  onMouseLeave,
566
548
  onMouseDown,
567
549
  onMouseUp,
568
- onClick,
569
- onFocus,
570
- onBlur,
571
550
  ...pathProps
572
551
  }: MapMeshProps): ReactElement | null;
573
552
  //#endregion
@@ -596,4 +575,21 @@ declare function MapZoom({
596
575
  declare const MapContextValue: react0.Context<MapContext | undefined>;
597
576
  declare function useMapContext(): MapContext | undefined;
598
577
  //#endregion
599
- export { Map, MapContextValue, MapFeature, MapFeatures, MapMarker, MapMesh, MapObjectStyle, MapObjectStyles, MapZoom, UseMapObjectOptions, UseMapObjectResult, useMapContext, useMapObject };
578
+ //#region src/hooks/useMapObject.d.ts
579
+ type MapObjectStyle = CSSProperties;
580
+ interface UseMapObjectOptions<TElement extends Element> extends MapObject<MapObjectStyle> {
581
+ onMouseEnter?: MouseEventHandler<TElement>;
582
+ onMouseLeave?: MouseEventHandler<TElement>;
583
+ onMouseDown?: MouseEventHandler<TElement>;
584
+ onMouseUp?: MouseEventHandler<TElement>;
585
+ }
586
+ interface UseMapObjectResult<TElement extends Element> {
587
+ style: MapObjectStyle | undefined;
588
+ onMouseEnter: MouseEventHandler<TElement>;
589
+ onMouseLeave: MouseEventHandler<TElement>;
590
+ onMouseDown: MouseEventHandler<TElement>;
591
+ onMouseUp: MouseEventHandler<TElement>;
592
+ }
593
+ declare function useMapObject<TElement extends Element>(options: UseMapObjectOptions<TElement>): UseMapObjectResult<TElement>;
594
+ //#endregion
595
+ export { Map, MapContextValue, MapFeature, MapFeatures, MapGraticule, MapMarker, MapMesh, MapObjectStyle, MapZoom, UseMapObjectOptions, UseMapObjectResult, useMapContext, useMapObject };
@@ -1 +1 @@
1
- "use client";(function(e,t,n,r,i){let a=(0,r.createContext)(void 0);function o(){return(0,r.useContext)(a)}function s({context:e,children:t}){return(0,i.jsx)(a.Provider,{value:e,children:t})}function c(e){return typeof e==`function`}function l({width:e,height:t,aspectRatio:a,projection:o,projectionConfig:l,data:u,dataTransformer:d,children:f,className:p,...m}){let h=(0,r.useMemo)(()=>(0,n.makeMapContext)({width:e,height:t,aspectRatio:a,projection:o,projectionConfig:l,data:u,dataTransformer:d}),[e,t,a,o,l,u,d]),g=c(f)?f(h):f;return(0,i.jsx)(s,{context:h,children:(0,i.jsx)(`svg`,{...m,className:`d3-map ${p??``}`,viewBox:`0 0 ${h.width} ${h.height}`,children:g})})}function u(e){let t=(0,r.useRef)(e);return t.current=e,t}function d(e){let[t,i]=(0,r.useState)(`default`),a=u(e.onMouseEnter),o=u(e.onMouseLeave),s=u(e.onMouseDown),c=u(e.onMouseUp),l=u(e.onClick),d=u(e.onFocus),f=u(e.onBlur),p=(0,r.useCallback)(e=>{i(t=>{let r=(0,n.getObjectStateUpdate)(e);return t===r?t:r})},[]);return{computedStyle:(0,r.useMemo)(()=>(0,n.resolveObjectStyle)(t,e.styles),[t,e.styles]),onMouseEnter:(0,r.useCallback)(e=>{p(`mouseenter`),a.current?.(e)},[p]),onMouseLeave:(0,r.useCallback)(e=>{p(`mouseleave`),o.current?.(e)},[p]),onMouseDown:(0,r.useCallback)(e=>{p(`mousedown`),s.current?.(e)},[p]),onMouseUp:(0,r.useCallback)(e=>{p(`mouseup`),c.current?.(e)},[p]),onClick:(0,r.useCallback)(e=>{p(`mouseup`),l.current?.(e)},[p]),onFocus:(0,r.useCallback)(e=>{p(`focus`),d.current?.(e)},[p]),onBlur:(0,r.useCallback)(e=>{p(`blur`),f.current?.(e)},[p])}}function f({data:e,styles:t,fill:n,stroke:a,onMouseEnter:s,onMouseLeave:c,onMouseDown:l,onMouseUp:u,onClick:f,onFocus:p,onBlur:m,...h}){let g=o(),_=(0,r.useMemo)(()=>g?.renderPath(e)??null,[g,e]),{computedStyle:v,onMouseEnter:y,onMouseLeave:b,onMouseDown:x,onMouseUp:S,onClick:C,onFocus:w,onBlur:T}=d({styles:t,onMouseEnter:s,onMouseLeave:c,onMouseDown:l,onMouseUp:u,onClick:f,onFocus:p,onBlur:m});return _?(0,i.jsx)(`path`,{...h,d:_,style:v,fill:n,stroke:a,onMouseEnter:y,onMouseLeave:b,onMouseDown:x,onMouseUp:S,onClick:C,onFocus:w,onBlur:T}):null}function p(e){return typeof e==`function`}function m({idKey:e=`id`,fill:t,stroke:r,styles:a,children:s}){let c=o()?.features??[];return(0,i.jsx)(`g`,{children:(p(s)?s({features:c}):s)??c.map((o,s)=>(0,i.jsx)(f,{data:o,fill:t,stroke:r,styles:a},(0,n.getFeatureKey)(o,e,s)))})}let h=[0,0];function g({coordinates:e=h,styles:t,children:a,onMouseEnter:s,onMouseLeave:c,onMouseDown:l,onMouseUp:u,onClick:f,onFocus:p,onBlur:m,...g}){let _=o(),v=(0,r.useMemo)(()=>(0,n.getMarkerTransform)(_,e),[_,e[0],e[1]]),{computedStyle:y,onMouseEnter:b,onMouseLeave:x,onMouseDown:S,onMouseUp:C,onClick:w,onFocus:T,onBlur:E}=d({styles:t,onMouseEnter:s,onMouseLeave:c,onMouseDown:l,onMouseUp:u,onClick:f,onFocus:p,onBlur:m});return(0,i.jsx)(`g`,{...g,transform:v,style:y,onMouseEnter:b,onMouseLeave:x,onMouseDown:S,onMouseUp:C,onClick:w,onFocus:T,onBlur:E,children:a})}function _({fill:e=`none`,stroke:t,styles:n,onMouseEnter:a,onMouseLeave:s,onMouseDown:c,onMouseUp:l,onClick:u,onFocus:f,onBlur:p,...m}){let h=o(),g=(0,r.useMemo)(()=>h?.renderMesh()??null,[h]),{computedStyle:_,onMouseEnter:v,onMouseLeave:y,onMouseDown:b,onMouseUp:x,onClick:S,onFocus:C,onBlur:w}=d({styles:n,onMouseEnter:a,onMouseLeave:s,onMouseDown:c,onMouseUp:l,onClick:u,onFocus:f,onBlur:p});return g?(0,i.jsx)(`path`,{...m,d:g,style:_,fill:e,stroke:t,onMouseEnter:v,onMouseLeave:y,onMouseDown:b,onMouseUp:x,onClick:S,onFocus:C,onBlur:w}):null}function v({center:e,zoom:t,minZoom:a=n.ZOOM_DEFAULTS.minZoom,maxZoom:s=n.ZOOM_DEFAULTS.maxZoom,config:c,onZoomStart:l,onZoom:d,onZoomEnd:f,children:p,className:m,...h}){let g=(0,r.useRef)(null),_=(0,r.useRef)(!1),v=o(),y=u(l),b=u(d),x=u(f),S=e??n.ZOOM_DEFAULTS.center,C=t??n.ZOOM_DEFAULTS.zoom,w=S[0],T=S[1],E=(0,r.useMemo)(()=>(0,n.createZoomBehavior)(v,{minZoom:a,maxZoom:s,config:c,onZoomStart:e=>{y.current?.(e)},onZoom:e=>{(0,n.applyZoomGroupTransform)(g.current,e.transform),b.current?.(e)},onZoomEnd:e=>{x.current?.(e)}}),[v,a,s,c]);(0,r.useEffect)(()=>{_.current=!0,(0,n.setupZoom)({element:g.current,behavior:E,center:S,zoom:C})},[E]),(0,r.useEffect)(()=>{if(_.current){_.current=!1;return}(0,n.applyZoomTransform)({element:g.current,behavior:E,center:[w,T],zoom:C})},[E,w,T,C]);let D=m?`d3-map-zoom ${m}`:`d3-map-zoom`;return(0,i.jsx)(`g`,{...h,ref:g,className:D,children:p})}e.Map=l,e.MapContextValue=a,e.MapFeature=f,e.MapFeatures=m,e.MapMarker=g,e.MapMesh=_,e.MapZoom=v,e.useMapContext=o,e.useMapObject=d})(this.D3Maps=this.D3Maps||{},_d3_maps_core_index_css,D3Maps,React,ReactJSXRuntime);
1
+ "use client";(function(e,t,n,r,i){let a=(0,r.createContext)(void 0);function o(){return(0,r.useContext)(a)}function s({context:e,children:t}){return(0,i.jsx)(a.Provider,{value:e,children:t})}function c(e){return typeof e==`function`}function l({width:e,height:t,aspectRatio:a,projection:o,projectionConfig:l,data:u,dataTransformer:d,children:f,className:p,...m}){let h=(0,r.useMemo)(()=>(0,n.makeMapContext)({width:e,height:t,aspectRatio:a,projection:o,projectionConfig:l,data:u,dataTransformer:d}),[e,t,a,o,l,u,d]),g=c(f)?f(h):f;return(0,i.jsx)(s,{context:h,children:(0,i.jsx)(`svg`,{...m,className:`d3-map ${p??``}`,viewBox:`0 0 ${h.width} ${h.height}`,children:g})})}let u=(0,r.createContext)(!1);function d(){return(0,r.useContext)(u)}function f(e){let t=(0,r.useRef)(e);return t.current=e,t}function p(e){let[t,i]=(0,r.useState)(`default`),a=(0,r.useRef)(t),o=d(),s=f(e.onMouseEnter),c=f(e.onMouseLeave),l=f(e.onMouseDown),u=f(e.onMouseUp);(0,r.useEffect)(()=>{a.current=t},[t]);let p=(0,r.useCallback)(e=>{a.current!==e&&(a.current=e,i(e))},[]),m=(0,r.useMemo)(()=>(0,n.useMapObjectEvents)(p,o),[o,p]);return(0,r.useEffect)(()=>()=>{m.dispose()},[m]),{style:(0,r.useMemo)(()=>(0,n.resolveObjectStyle)(t,e.styles),[t,e.styles]),onMouseEnter:(0,r.useCallback)(e=>{m.onMouseenter(),s.current?.(e)},[m]),onMouseLeave:(0,r.useCallback)(e=>{m.onMouseleave(),c.current?.(e)},[m]),onMouseDown:(0,r.useCallback)(e=>{m.onMousedown(e),l.current?.(e)},[m]),onMouseUp:(0,r.useCallback)(e=>{m.onMouseup(),u.current?.(e)},[m])}}function m({data:e,styles:t,onMouseEnter:n,onMouseLeave:a,onMouseDown:s,onMouseUp:c,...l}){let u=o(),d=(0,r.useMemo)(()=>u?.path(e)??void 0,[u,e]),{style:f,...m}=p({styles:t,onMouseEnter:n,onMouseLeave:a,onMouseDown:s,onMouseUp:c});return(0,i.jsx)(`path`,{...l,d,style:f,name:`feature`,...m})}function h(e){return typeof e==`function`}function g({idKey:e=`id`,styles:t,children:r,...a}){let s=o()?.features??[],c=h(r)?r({features:s}):r;return(0,i.jsx)(`g`,{...a,name:`features`,children:c??s.map((r,a)=>(0,i.jsx)(m,{data:r,styles:t},(0,n.getFeatureKey)(r,e,a)))})}function _({config:e,background:t,border:a,styles:s,onMouseEnter:c,onMouseLeave:l,onMouseDown:u,onMouseUp:d,...f}){let m=o(),h=(0,r.useMemo)(()=>{if(m)return(0,n.renderGraticule)(m,e)??void 0},[m,e]),g=t===!0||typeof t==`string`,_=a===!0||typeof a==`string`,v=typeof t==`string`?t:void 0,y=typeof a==`string`?a:void 0,b=g||_,x=(0,r.useMemo)(()=>{if(!(!m||!b))return(0,n.renderOutline)(m)??void 0},[m,b]),{style:S,...C}=p({styles:s,onMouseEnter:c,onMouseLeave:l,onMouseDown:u,onMouseUp:d});return(0,i.jsxs)(`g`,{children:[g?(0,i.jsx)(`path`,{d:x??void 0,fill:v,name:`background`,pointerEvents:`none`}):null,(0,i.jsx)(`path`,{...f,d:h??void 0,style:S,fill:`none`,name:`graticule`,...C}),_?(0,i.jsx)(`path`,{d:x??void 0,fill:`none`,stroke:y,name:`border`,pointerEvents:`none`}):null]})}let v=[0,0];function y({coordinates:e=v,styles:t,children:a,onMouseEnter:s,onMouseLeave:c,onMouseDown:l,onMouseUp:u,...d}){let f=o(),m=(0,r.useMemo)(()=>(0,n.getMarkerTransform)(f,e),[f,e[0],e[1]]),{style:h,...g}=p({styles:t,onMouseEnter:s,onMouseLeave:c,onMouseDown:l,onMouseUp:u});return(0,i.jsx)(`g`,{...d,transform:m,style:h,name:`marker`,...g,children:a})}function b({styles:e,onMouseEnter:t,onMouseLeave:n,onMouseDown:a,onMouseUp:s,...c}){let l=o(),u=(0,r.useMemo)(()=>l?.renderMesh()??void 0,[l]),{style:d,...f}=p({styles:e,onMouseEnter:t,onMouseLeave:n,onMouseDown:a,onMouseUp:s}),m=c.fill??`none`;return(0,i.jsx)(`path`,{...c,d:u,style:d,fill:m,name:`mesh`,...f})}function x({center:e,zoom:t,minZoom:a=n.ZOOM_DEFAULTS.minZoom,maxZoom:s=n.ZOOM_DEFAULTS.maxZoom,config:c,onZoomStart:l,onZoom:d,onZoomEnd:p,children:m,className:h,...g}){let _=(0,r.useRef)(null),v=(0,r.useRef)(!1),y=o(),b=f(l),x=f(d),S=f(p),C=e??n.ZOOM_DEFAULTS.center,w=t??n.ZOOM_DEFAULTS.zoom,T=C[0],E=C[1],D=(0,r.useMemo)(()=>(0,n.createZoomBehavior)(y,{minZoom:a,maxZoom:s,config:c,onZoomStart:e=>{b.current?.(e)},onZoom:e=>{(0,n.applyZoomGroupTransform)(_.current,e.transform),x.current?.(e)},onZoomEnd:e=>{S.current?.(e)}}),[y,a,s,c]);(0,r.useEffect)(()=>{v.current=!0,(0,n.setupZoom)({element:_.current,behavior:D,center:C,zoom:w})},[D]),(0,r.useEffect)(()=>{if(v.current){v.current=!1;return}(0,n.applyZoomTransform)({element:_.current,behavior:D,center:[T,E],zoom:w})},[D,T,E,w]);let O=h?`d3-map-zoom ${h}`:`d3-map-zoom`;return(0,i.jsx)(u.Provider,{value:!0,children:(0,i.jsx)(`g`,{...g,ref:_,className:O,name:`zoom`,children:m})})}e.Map=l,e.MapContextValue=a,e.MapFeature=m,e.MapFeatures=g,e.MapGraticule=_,e.MapMarker=y,e.MapMesh=b,e.MapZoom=x,e.useMapContext=o,e.useMapObject=p})(this.D3Maps=this.D3Maps||{},_d3_maps_core_index_css,D3Maps,React,ReactJSXRuntime);
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  'use client';
2
2
 
3
3
  import "@d3-maps/core/index.css";
4
- import { ZOOM_DEFAULTS, applyZoomGroupTransform, applyZoomTransform, createZoomBehavior, getFeatureKey, getMarkerTransform, getObjectStateUpdate, makeMapContext, resolveObjectStyle, setupZoom } from "@d3-maps/core";
4
+ import { ZOOM_DEFAULTS, applyZoomGroupTransform, applyZoomTransform, createZoomBehavior, getFeatureKey, getMarkerTransform, makeMapContext, renderGraticule, renderOutline, resolveObjectStyle, setupZoom, useMapObjectEvents } from "@d3-maps/core";
5
5
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
6
- import { jsx } from "react/jsx-runtime";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
7
 
8
8
  //#region src/hooks/useMapContext.ts
9
9
  const MapContextValue = createContext(void 0);
@@ -57,6 +57,13 @@ function Map({ width, height, aspectRatio, projection, projectionConfig, data, d
57
57
  });
58
58
  }
59
59
 
60
+ //#endregion
61
+ //#region src/hooks/useInsideZoom.ts
62
+ const InsideZoomContext = createContext(false);
63
+ function useInsideZoom() {
64
+ return useContext(InsideZoomContext);
65
+ }
66
+
60
67
  //#endregion
61
68
  //#region src/hooks/useLatest.ts
62
69
  function useLatest(value) {
@@ -69,85 +76,70 @@ function useLatest(value) {
69
76
  //#region src/hooks/useMapObject.ts
70
77
  function useMapObject(options) {
71
78
  const [state, setState] = useState("default");
79
+ const stateRef = useRef(state);
80
+ const insideZoom = useInsideZoom();
72
81
  const onMouseEnterRef = useLatest(options.onMouseEnter);
73
82
  const onMouseLeaveRef = useLatest(options.onMouseLeave);
74
83
  const onMouseDownRef = useLatest(options.onMouseDown);
75
84
  const onMouseUpRef = useLatest(options.onMouseUp);
76
- const onClickRef = useLatest(options.onClick);
77
- const onFocusRef = useLatest(options.onFocus);
78
- const onBlurRef = useLatest(options.onBlur);
79
- const setStateForEvent = useCallback((eventName) => {
80
- setState((currentState) => {
81
- const nextState = getObjectStateUpdate(eventName);
82
- return currentState === nextState ? currentState : nextState;
83
- });
85
+ useEffect(() => {
86
+ stateRef.current = state;
87
+ }, [state]);
88
+ const syncState = useCallback((nextState) => {
89
+ if (stateRef.current === nextState) return;
90
+ stateRef.current = nextState;
91
+ setState(nextState);
84
92
  }, []);
93
+ const interactionController = useMemo(() => {
94
+ return useMapObjectEvents(syncState, insideZoom);
95
+ }, [insideZoom, syncState]);
96
+ useEffect(() => {
97
+ return () => {
98
+ interactionController.dispose();
99
+ };
100
+ }, [interactionController]);
85
101
  return {
86
- computedStyle: useMemo(() => {
87
- return resolveObjectStyle(state, options.styles);
88
- }, [state, options.styles]),
102
+ style: useMemo(() => resolveObjectStyle(state, options.styles), [state, options.styles]),
89
103
  onMouseEnter: useCallback((event) => {
90
- setStateForEvent("mouseenter");
104
+ interactionController.onMouseenter();
91
105
  onMouseEnterRef.current?.(event);
92
- }, [setStateForEvent]),
106
+ }, [interactionController]),
93
107
  onMouseLeave: useCallback((event) => {
94
- setStateForEvent("mouseleave");
108
+ interactionController.onMouseleave();
95
109
  onMouseLeaveRef.current?.(event);
96
- }, [setStateForEvent]),
110
+ }, [interactionController]),
97
111
  onMouseDown: useCallback((event) => {
98
- setStateForEvent("mousedown");
112
+ interactionController.onMousedown(event);
99
113
  onMouseDownRef.current?.(event);
100
- }, [setStateForEvent]),
114
+ }, [interactionController]),
101
115
  onMouseUp: useCallback((event) => {
102
- setStateForEvent("mouseup");
116
+ interactionController.onMouseup();
103
117
  onMouseUpRef.current?.(event);
104
- }, [setStateForEvent]),
105
- onClick: useCallback((event) => {
106
- setStateForEvent("mouseup");
107
- onClickRef.current?.(event);
108
- }, [setStateForEvent]),
109
- onFocus: useCallback((event) => {
110
- setStateForEvent("focus");
111
- onFocusRef.current?.(event);
112
- }, [setStateForEvent]),
113
- onBlur: useCallback((event) => {
114
- setStateForEvent("blur");
115
- onBlurRef.current?.(event);
116
- }, [setStateForEvent])
118
+ }, [interactionController])
117
119
  };
118
120
  }
119
121
 
120
122
  //#endregion
121
123
  //#region src/components/MapFeature.tsx
122
- function MapFeature({ data, styles, fill, stroke, onMouseEnter, onMouseLeave, onMouseDown, onMouseUp, onClick, onFocus, onBlur, ...pathProps }) {
124
+ function MapFeature({ data, styles, onMouseEnter, onMouseLeave, onMouseDown, onMouseUp, ...pathProps }) {
123
125
  const context = useMapContext();
124
126
  const path = useMemo(() => {
125
- return context?.renderPath(data) ?? null;
127
+ return context?.path(data) ?? void 0;
126
128
  }, [context, data]);
127
- const { computedStyle, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, onClick: handleClick, onFocus: handleFocus, onBlur: handleBlur } = useMapObject({
129
+ const { style, ...events } = useMapObject({
128
130
  styles,
129
131
  onMouseEnter,
130
132
  onMouseLeave,
131
133
  onMouseDown,
132
- onMouseUp,
133
- onClick,
134
- onFocus,
135
- onBlur
134
+ onMouseUp
136
135
  });
137
- return path ? /* @__PURE__ */ jsx("path", {
136
+ return /* @__PURE__ */ jsx("path", {
138
137
  ...pathProps,
139
138
  d: path,
140
- style: computedStyle,
141
- fill,
142
- stroke,
143
- onMouseEnter: handleMouseEnter,
144
- onMouseLeave: handleMouseLeave,
145
- onMouseDown: handleMouseDown,
146
- onMouseUp: handleMouseUp,
147
- onClick: handleClick,
148
- onFocus: handleFocus,
149
- onBlur: handleBlur
150
- }) : null;
139
+ style,
140
+ name: "feature",
141
+ ...events
142
+ });
151
143
  }
152
144
 
153
145
  //#endregion
@@ -155,20 +147,72 @@ function MapFeature({ data, styles, fill, stroke, onMouseEnter, onMouseLeave, on
155
147
  function isRenderProp(children) {
156
148
  return typeof children === "function";
157
149
  }
158
- function MapFeatures({ idKey = "id", fill, stroke, styles, children }) {
150
+ function MapFeatures({ idKey = "id", styles, children, ...groupProps }) {
159
151
  const features = useMapContext()?.features ?? [];
160
- return /* @__PURE__ */ jsx("g", { children: (isRenderProp(children) ? children({ features }) : children) ?? features.map((feature, index) => /* @__PURE__ */ jsx(MapFeature, {
161
- data: feature,
162
- fill,
163
- stroke,
164
- styles
165
- }, getFeatureKey(feature, idKey, index))) });
152
+ const resolvedChildren = isRenderProp(children) ? children({ features }) : children;
153
+ return /* @__PURE__ */ jsx("g", {
154
+ ...groupProps,
155
+ name: "features",
156
+ children: resolvedChildren ?? features.map((feature, index) => /* @__PURE__ */ jsx(MapFeature, {
157
+ data: feature,
158
+ styles
159
+ }, getFeatureKey(feature, idKey, index)))
160
+ });
161
+ }
162
+
163
+ //#endregion
164
+ //#region src/components/MapGraticule.tsx
165
+ function MapGraticule({ config, background, border, styles, onMouseEnter, onMouseLeave, onMouseDown, onMouseUp, ...pathProps }) {
166
+ const context = useMapContext();
167
+ const graticulePath = useMemo(() => {
168
+ if (!context) return void 0;
169
+ return renderGraticule(context, config) ?? void 0;
170
+ }, [context, config]);
171
+ const showBackground = background === true || typeof background === "string";
172
+ const showBorder = border === true || typeof border === "string";
173
+ const backgroundColor = typeof background === "string" ? background : void 0;
174
+ const borderColor = typeof border === "string" ? border : void 0;
175
+ const shouldRenderOutline = showBackground || showBorder;
176
+ const outlinePath = useMemo(() => {
177
+ if (!context || !shouldRenderOutline) return void 0;
178
+ return renderOutline(context) ?? void 0;
179
+ }, [context, shouldRenderOutline]);
180
+ const { style, ...events } = useMapObject({
181
+ styles,
182
+ onMouseEnter,
183
+ onMouseLeave,
184
+ onMouseDown,
185
+ onMouseUp
186
+ });
187
+ return /* @__PURE__ */ jsxs("g", { children: [
188
+ showBackground ? /* @__PURE__ */ jsx("path", {
189
+ d: outlinePath ?? void 0,
190
+ fill: backgroundColor,
191
+ name: "background",
192
+ pointerEvents: "none"
193
+ }) : null,
194
+ /* @__PURE__ */ jsx("path", {
195
+ ...pathProps,
196
+ d: graticulePath ?? void 0,
197
+ style,
198
+ fill: "none",
199
+ name: "graticule",
200
+ ...events
201
+ }),
202
+ showBorder ? /* @__PURE__ */ jsx("path", {
203
+ d: outlinePath ?? void 0,
204
+ fill: "none",
205
+ stroke: borderColor,
206
+ name: "border",
207
+ pointerEvents: "none"
208
+ }) : null
209
+ ] });
166
210
  }
167
211
 
168
212
  //#endregion
169
213
  //#region src/components/MapMarker.tsx
170
214
  const DEFAULT_COORDINATES = [0, 0];
171
- function MapMarker({ coordinates = DEFAULT_COORDINATES, styles, children, onMouseEnter, onMouseLeave, onMouseDown, onMouseUp, onClick, onFocus, onBlur, ...groupProps }) {
215
+ function MapMarker({ coordinates = DEFAULT_COORDINATES, styles, children, onMouseEnter, onMouseLeave, onMouseDown, onMouseUp, ...groupProps }) {
172
216
  const context = useMapContext();
173
217
  const transform = useMemo(() => {
174
218
  return getMarkerTransform(context, coordinates);
@@ -177,62 +221,46 @@ function MapMarker({ coordinates = DEFAULT_COORDINATES, styles, children, onMous
177
221
  coordinates[0],
178
222
  coordinates[1]
179
223
  ]);
180
- const { computedStyle, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, onClick: handleClick, onFocus: handleFocus, onBlur: handleBlur } = useMapObject({
224
+ const { style, ...events } = useMapObject({
181
225
  styles,
182
226
  onMouseEnter,
183
227
  onMouseLeave,
184
228
  onMouseDown,
185
- onMouseUp,
186
- onClick,
187
- onFocus,
188
- onBlur
229
+ onMouseUp
189
230
  });
190
231
  return /* @__PURE__ */ jsx("g", {
191
232
  ...groupProps,
192
233
  transform,
193
- style: computedStyle,
194
- onMouseEnter: handleMouseEnter,
195
- onMouseLeave: handleMouseLeave,
196
- onMouseDown: handleMouseDown,
197
- onMouseUp: handleMouseUp,
198
- onClick: handleClick,
199
- onFocus: handleFocus,
200
- onBlur: handleBlur,
234
+ style,
235
+ name: "marker",
236
+ ...events,
201
237
  children
202
238
  });
203
239
  }
204
240
 
205
241
  //#endregion
206
242
  //#region src/components/MapMesh.tsx
207
- function MapMesh({ fill = "none", stroke, styles, onMouseEnter, onMouseLeave, onMouseDown, onMouseUp, onClick, onFocus, onBlur, ...pathProps }) {
243
+ function MapMesh({ styles, onMouseEnter, onMouseLeave, onMouseDown, onMouseUp, ...pathProps }) {
208
244
  const context = useMapContext();
209
245
  const path = useMemo(() => {
210
- return context?.renderMesh() ?? null;
246
+ return context?.renderMesh() ?? void 0;
211
247
  }, [context]);
212
- const { computedStyle, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, onClick: handleClick, onFocus: handleFocus, onBlur: handleBlur } = useMapObject({
248
+ const { style, ...events } = useMapObject({
213
249
  styles,
214
250
  onMouseEnter,
215
251
  onMouseLeave,
216
252
  onMouseDown,
217
- onMouseUp,
218
- onClick,
219
- onFocus,
220
- onBlur
253
+ onMouseUp
221
254
  });
222
- return path ? /* @__PURE__ */ jsx("path", {
255
+ const fill = pathProps.fill ?? "none";
256
+ return /* @__PURE__ */ jsx("path", {
223
257
  ...pathProps,
224
258
  d: path,
225
- style: computedStyle,
259
+ style,
226
260
  fill,
227
- stroke,
228
- onMouseEnter: handleMouseEnter,
229
- onMouseLeave: handleMouseLeave,
230
- onMouseDown: handleMouseDown,
231
- onMouseUp: handleMouseUp,
232
- onClick: handleClick,
233
- onFocus: handleFocus,
234
- onBlur: handleBlur
235
- }) : null;
261
+ name: "mesh",
262
+ ...events
263
+ });
236
264
  }
237
265
 
238
266
  //#endregion
@@ -297,13 +325,17 @@ function MapZoom({ center, zoom, minZoom = ZOOM_DEFAULTS.minZoom, maxZoom = ZOOM
297
325
  resolvedZoom
298
326
  ]);
299
327
  const mergedClassName = className ? `d3-map-zoom ${className}` : "d3-map-zoom";
300
- return /* @__PURE__ */ jsx("g", {
301
- ...groupProps,
302
- ref: containerRef,
303
- className: mergedClassName,
304
- children
328
+ return /* @__PURE__ */ jsx(InsideZoomContext.Provider, {
329
+ value: true,
330
+ children: /* @__PURE__ */ jsx("g", {
331
+ ...groupProps,
332
+ ref: containerRef,
333
+ className: mergedClassName,
334
+ name: "zoom",
335
+ children
336
+ })
305
337
  });
306
338
  }
307
339
 
308
340
  //#endregion
309
- export { Map, MapContextValue, MapFeature, MapFeatures, MapMarker, MapMesh, MapZoom, useMapContext, useMapObject };
341
+ export { Map, MapContextValue, MapFeature, MapFeatures, MapGraticule, MapMarker, MapMesh, MapZoom, useMapContext, useMapObject };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@d3-maps/react",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "0.3.0",
5
5
  "private": false,
6
6
  "description": "React bindings for @d3-maps/core to build reactive D3 SVG maps",
7
7
  "author": "Georgii Bukharov <souljorje@gmail.com>",
@@ -40,34 +40,34 @@
40
40
  "files": [
41
41
  "dist/*"
42
42
  ],
43
- "scripts": {
44
- "typecheck": "tsc --noEmit",
45
- "typecheck:test": "tsc -p tsconfig.test.json --noEmit",
46
- "build": "pnpm run typecheck && tsdown",
47
- "test": "vitest run",
48
- "test:watch": "vitest"
49
- },
50
43
  "peerDependencies": {
51
44
  "react": ">=19 <20",
52
45
  "react-dom": ">=19 <20"
53
46
  },
54
47
  "dependencies": {
55
- "@d3-maps/core": "workspace:*"
48
+ "@d3-maps/core": "0.6.0"
56
49
  },
57
50
  "devDependencies": {
58
- "@testing-library/react": "catalog:",
59
- "@types/geojson": "catalog:",
60
- "@types/react": "catalog:",
61
- "@types/react-dom": "catalog:",
62
- "@types/topojson-client": "catalog:",
63
- "@types/topojson-specification": "catalog:",
64
- "@vitejs/plugin-react": "catalog:",
65
- "jsdom": "catalog:",
66
- "react": "catalog:",
67
- "react-dom": "catalog:",
68
- "tsdown": "catalog:",
69
- "typescript": "catalog:",
70
- "vite-tsconfig-paths": "catalog:",
71
- "vitest": "catalog:"
51
+ "@testing-library/react": "^16.3.0",
52
+ "@types/geojson": "^7946.0.16",
53
+ "@types/react": "^19.2.2",
54
+ "@types/react-dom": "^19.2.2",
55
+ "@types/topojson-client": "^3.1.5",
56
+ "@types/topojson-specification": "^1.0.5",
57
+ "@vitejs/plugin-react": "^5.1.0",
58
+ "jsdom": "^27.3.0",
59
+ "react": "^19.2.0",
60
+ "react-dom": "^19.2.0",
61
+ "tsdown": "0.19.0",
62
+ "typescript": "^5.9.3",
63
+ "vite-tsconfig-paths": "^6.1.1",
64
+ "vitest": "^4.0.15"
65
+ },
66
+ "scripts": {
67
+ "typecheck": "tsc --noEmit",
68
+ "typecheck:test": "tsc -p tsconfig.test.json --noEmit",
69
+ "build": "pnpm run typecheck && tsdown",
70
+ "test": "vitest run",
71
+ "test:watch": "vitest"
72
72
  }
73
- }
73
+ }