@d3-maps/react 0.1.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/README.md +54 -0
- package/dist/index.d.ts +599 -0
- package/dist/index.iife.js +1 -0
- package/dist/index.js +309 -0
- package/package.json +73 -0
package/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# @d3-maps/react
|
|
2
|
+
|
|
3
|
+
`@d3-maps/react` provides React bindings for `@d3-maps/core` to build SVG maps with React, [d3](https://github.com/d3/d3) and [TopoJSON-client](https://github.com/TopoJSON/TopoJSON-client).
|
|
4
|
+
|
|
5
|
+
[docs](https://souljorje.github.io/d3-maps)
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Requires React 19+
|
|
10
|
+
|
|
11
|
+
Next.js App Router: `@d3-maps/react` entrypoints are client-only (`'use client'`), so import them from Client Components
|
|
12
|
+
React Server Components entrypoints are planned
|
|
13
|
+
|
|
14
|
+
npm
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @d3-maps/react
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
pnpm
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pnpm add @d3-maps/react
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
bun
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
bun add @d3-maps/react
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { Map, MapFeatures } from '@d3-maps/react'
|
|
36
|
+
|
|
37
|
+
export function App({ mapData }: { mapData: unknown }) {
|
|
38
|
+
return (
|
|
39
|
+
<Map data={mapData}>
|
|
40
|
+
<MapFeatures />
|
|
41
|
+
</Map>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Styling
|
|
47
|
+
|
|
48
|
+
Importing `@d3-maps/react` automatically includes `@d3-maps/core/index.css`
|
|
49
|
+
|
|
50
|
+
If you need strict stylesheet ordering, load your global reset/theme styles before importing the adapter entry
|
|
51
|
+
|
|
52
|
+
## License
|
|
53
|
+
|
|
54
|
+
MIT licensed. Copyright © 2020 Georgii Bukharov. See [LICENCE](../../LICENCE) for more details.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
import "@d3-maps/core/index.css";
|
|
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";
|
|
6
|
+
import { D3ZoomEvent, ZoomBehavior } from "d3-zoom";
|
|
7
|
+
|
|
8
|
+
//#region ../core/src/lib/mapObject.d.ts
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Supported interaction states for map objects.
|
|
12
|
+
*/
|
|
13
|
+
declare const mapObjectState: readonly ["default", "hover", "active"];
|
|
14
|
+
type MapObjectState = typeof mapObjectState[number];
|
|
15
|
+
type MapObjectStyles$1<TStyle> = Partial<Record<MapObjectState, TStyle>>;
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region ../core/src/lib/feature.d.ts
|
|
18
|
+
/**
|
|
19
|
+
* A GeoJSON Feature used by d3-maps.
|
|
20
|
+
*
|
|
21
|
+
* This type allows extra top-level fields to be attached in `dataTransformer` (e.g. choropleth colors).
|
|
22
|
+
*/
|
|
23
|
+
type MapFeature$1 = (ExtendedFeature & Record<string, unknown>) | ExtendedFeature;
|
|
24
|
+
/**
|
|
25
|
+
* Shared props contract for a single rendered feature.
|
|
26
|
+
*/
|
|
27
|
+
interface MapFeatureProps$1<TStyle = unknown> {
|
|
28
|
+
data: MapFeature$1;
|
|
29
|
+
styles?: MapObjectStyles$1<TStyle>;
|
|
30
|
+
fill?: string;
|
|
31
|
+
stroke?: string;
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region ../../node_modules/.pnpm/@types+geojson@7946.0.16/node_modules/@types/geojson/index.d.ts
|
|
35
|
+
/**
|
|
36
|
+
* The valid values for the "type" property of GeoJSON geometry objects.
|
|
37
|
+
* https://tools.ietf.org/html/rfc7946#section-1.4
|
|
38
|
+
*/
|
|
39
|
+
type GeoJsonGeometryTypes = Geometry["type"];
|
|
40
|
+
/**
|
|
41
|
+
* The value values for the "type" property of GeoJSON Objects.
|
|
42
|
+
* https://tools.ietf.org/html/rfc7946#section-1.4
|
|
43
|
+
*/
|
|
44
|
+
type GeoJsonTypes = GeoJSON["type"];
|
|
45
|
+
/**
|
|
46
|
+
* Bounding box
|
|
47
|
+
* https://tools.ietf.org/html/rfc7946#section-5
|
|
48
|
+
*/
|
|
49
|
+
type BBox = [number, number, number, number] | [number, number, number, number, number, number];
|
|
50
|
+
/**
|
|
51
|
+
* A Position is an array of coordinates.
|
|
52
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.1
|
|
53
|
+
* Array should contain between two and three elements.
|
|
54
|
+
* The previous GeoJSON specification allowed more elements (e.g., which could be used to represent M values),
|
|
55
|
+
* but the current specification only allows X, Y, and (optionally) Z to be defined.
|
|
56
|
+
*
|
|
57
|
+
* Note: the type will not be narrowed down to `[number, number] | [number, number, number]` due to
|
|
58
|
+
* marginal benefits and the large impact of breaking change.
|
|
59
|
+
*
|
|
60
|
+
* See previous discussions on the type narrowing:
|
|
61
|
+
* - {@link https://github.com/DefinitelyTyped/DefinitelyTyped/pull/21590|Nov 2017}
|
|
62
|
+
* - {@link https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/67773|Dec 2023}
|
|
63
|
+
* - {@link https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/71441| Dec 2024}
|
|
64
|
+
*
|
|
65
|
+
* One can use a
|
|
66
|
+
* {@link https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates|user-defined type guard that returns a type predicate}
|
|
67
|
+
* to determine if a position is a 2D or 3D position.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* import type { Position } from 'geojson';
|
|
71
|
+
*
|
|
72
|
+
* type StrictPosition = [x: number, y: number] | [x: number, y: number, z: number]
|
|
73
|
+
*
|
|
74
|
+
* function isStrictPosition(position: Position): position is StrictPosition {
|
|
75
|
+
* return position.length === 2 || position.length === 3
|
|
76
|
+
* };
|
|
77
|
+
*
|
|
78
|
+
* let position: Position = [-116.91, 45.54];
|
|
79
|
+
*
|
|
80
|
+
* let x: number;
|
|
81
|
+
* let y: number;
|
|
82
|
+
* let z: number | undefined;
|
|
83
|
+
*
|
|
84
|
+
* if (isStrictPosition(position)) {
|
|
85
|
+
* // `tsc` would throw an error if we tried to destructure a fourth parameter
|
|
86
|
+
* [x, y, z] = position;
|
|
87
|
+
* } else {
|
|
88
|
+
* throw new TypeError("Position is not a 2D or 3D point");
|
|
89
|
+
* }
|
|
90
|
+
*/
|
|
91
|
+
type Position = number[];
|
|
92
|
+
/**
|
|
93
|
+
* The base GeoJSON object.
|
|
94
|
+
* https://tools.ietf.org/html/rfc7946#section-3
|
|
95
|
+
* The GeoJSON specification also allows foreign members
|
|
96
|
+
* (https://tools.ietf.org/html/rfc7946#section-6.1)
|
|
97
|
+
* Developers should use "&" type in TypeScript or extend the interface
|
|
98
|
+
* to add these foreign members.
|
|
99
|
+
*/
|
|
100
|
+
interface GeoJsonObject {
|
|
101
|
+
// Don't include foreign members directly into this type def.
|
|
102
|
+
// in order to preserve type safety.
|
|
103
|
+
// [key: string]: any;
|
|
104
|
+
/**
|
|
105
|
+
* Specifies the type of GeoJSON object.
|
|
106
|
+
*/
|
|
107
|
+
type: GeoJsonTypes;
|
|
108
|
+
/**
|
|
109
|
+
* Bounding box of the coordinate range of the object's Geometries, Features, or Feature Collections.
|
|
110
|
+
* The value of the bbox member is an array of length 2*n where n is the number of dimensions
|
|
111
|
+
* represented in the contained geometries, with all axes of the most southwesterly point
|
|
112
|
+
* followed by all axes of the more northeasterly point.
|
|
113
|
+
* The axes order of a bbox follows the axes order of geometries.
|
|
114
|
+
* https://tools.ietf.org/html/rfc7946#section-5
|
|
115
|
+
*/
|
|
116
|
+
bbox?: BBox | undefined;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Union of GeoJSON objects.
|
|
120
|
+
*/
|
|
121
|
+
type GeoJSON<G extends Geometry | null = Geometry, P$1 = GeoJsonProperties> = G | Feature<G, P$1> | FeatureCollection<G, P$1>;
|
|
122
|
+
/**
|
|
123
|
+
* Geometry object.
|
|
124
|
+
* https://tools.ietf.org/html/rfc7946#section-3
|
|
125
|
+
*/
|
|
126
|
+
type Geometry = Point$1 | MultiPoint$1 | LineString$1 | MultiLineString$1 | Polygon$1 | MultiPolygon$1 | GeometryCollection$1;
|
|
127
|
+
/**
|
|
128
|
+
* Point geometry object.
|
|
129
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.2
|
|
130
|
+
*/
|
|
131
|
+
interface Point$1 extends GeoJsonObject {
|
|
132
|
+
type: "Point";
|
|
133
|
+
coordinates: Position;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* MultiPoint geometry object.
|
|
137
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.3
|
|
138
|
+
*/
|
|
139
|
+
interface MultiPoint$1 extends GeoJsonObject {
|
|
140
|
+
type: "MultiPoint";
|
|
141
|
+
coordinates: Position[];
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* LineString geometry object.
|
|
145
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.4
|
|
146
|
+
*/
|
|
147
|
+
interface LineString$1 extends GeoJsonObject {
|
|
148
|
+
type: "LineString";
|
|
149
|
+
coordinates: Position[];
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* MultiLineString geometry object.
|
|
153
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.5
|
|
154
|
+
*/
|
|
155
|
+
interface MultiLineString$1 extends GeoJsonObject {
|
|
156
|
+
type: "MultiLineString";
|
|
157
|
+
coordinates: Position[][];
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Polygon geometry object.
|
|
161
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.6
|
|
162
|
+
*/
|
|
163
|
+
interface Polygon$1 extends GeoJsonObject {
|
|
164
|
+
type: "Polygon";
|
|
165
|
+
coordinates: Position[][];
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* MultiPolygon geometry object.
|
|
169
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.7
|
|
170
|
+
*/
|
|
171
|
+
interface MultiPolygon$1 extends GeoJsonObject {
|
|
172
|
+
type: "MultiPolygon";
|
|
173
|
+
coordinates: Position[][][];
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Geometry Collection
|
|
177
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.8
|
|
178
|
+
*/
|
|
179
|
+
interface GeometryCollection$1<G extends Geometry = Geometry> extends GeoJsonObject {
|
|
180
|
+
type: "GeometryCollection";
|
|
181
|
+
geometries: G[];
|
|
182
|
+
}
|
|
183
|
+
type GeoJsonProperties = {
|
|
184
|
+
[name: string]: any;
|
|
185
|
+
} | null;
|
|
186
|
+
/**
|
|
187
|
+
* A feature object which contains a geometry and associated properties.
|
|
188
|
+
* https://tools.ietf.org/html/rfc7946#section-3.2
|
|
189
|
+
*/
|
|
190
|
+
interface Feature<G extends Geometry | null = Geometry, P$1 = GeoJsonProperties> extends GeoJsonObject {
|
|
191
|
+
type: "Feature";
|
|
192
|
+
/**
|
|
193
|
+
* The feature's geometry
|
|
194
|
+
*/
|
|
195
|
+
geometry: G;
|
|
196
|
+
/**
|
|
197
|
+
* A value that uniquely identifies this feature in a
|
|
198
|
+
* https://tools.ietf.org/html/rfc7946#section-3.2.
|
|
199
|
+
*/
|
|
200
|
+
id?: string | number | undefined;
|
|
201
|
+
/**
|
|
202
|
+
* Properties associated with this feature.
|
|
203
|
+
*/
|
|
204
|
+
properties: P$1;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* A collection of feature objects.
|
|
208
|
+
* https://tools.ietf.org/html/rfc7946#section-3.3
|
|
209
|
+
*/
|
|
210
|
+
interface FeatureCollection<G extends Geometry | null = Geometry, P$1 = GeoJsonProperties> extends GeoJsonObject {
|
|
211
|
+
type: "FeatureCollection";
|
|
212
|
+
features: Array<Feature<G, P$1>>;
|
|
213
|
+
}
|
|
214
|
+
//#endregion
|
|
215
|
+
//#region ../../node_modules/.pnpm/@types+topojson-specification@1.0.5/node_modules/@types/topojson-specification/index.d.ts
|
|
216
|
+
// ---------------------------------------------------------------
|
|
217
|
+
// TopoJSON Format Specification
|
|
218
|
+
// ---------------------------------------------------------------
|
|
219
|
+
|
|
220
|
+
// See: https://github.com/topojson/topojson-specification/
|
|
221
|
+
|
|
222
|
+
// 2. TopoJSON Objects
|
|
223
|
+
interface TopoJSON {
|
|
224
|
+
type: "Topology" | GeoJsonGeometryTypes | null;
|
|
225
|
+
bbox?: BBox | undefined;
|
|
226
|
+
}
|
|
227
|
+
// 2.1. Topology Objects
|
|
228
|
+
interface Topology<T extends Objects<Properties> = Objects<Properties>> extends TopoJSON {
|
|
229
|
+
type: "Topology";
|
|
230
|
+
objects: T;
|
|
231
|
+
arcs: Arc[];
|
|
232
|
+
transform?: Transform | undefined;
|
|
233
|
+
}
|
|
234
|
+
// 2.1.1. Positions
|
|
235
|
+
type Positions = number[];
|
|
236
|
+
// at least two elements
|
|
237
|
+
|
|
238
|
+
// 2.1.2. Transforms
|
|
239
|
+
interface Transform {
|
|
240
|
+
scale: [number, number];
|
|
241
|
+
translate: [number, number];
|
|
242
|
+
}
|
|
243
|
+
// 2.1.3. Arcs
|
|
244
|
+
type Arc = Positions[];
|
|
245
|
+
// at least two elements
|
|
246
|
+
|
|
247
|
+
// 2.1.4. Arc Indexes
|
|
248
|
+
type ArcIndexes = number[];
|
|
249
|
+
// 2.1.5. Objects
|
|
250
|
+
type Properties = GeoJsonProperties;
|
|
251
|
+
interface Objects<P$1 extends Properties = {}> {
|
|
252
|
+
[key: string]: GeometryObject<P$1>;
|
|
253
|
+
}
|
|
254
|
+
// 2.2. Geometry Objects
|
|
255
|
+
interface GeometryObjectA<P$1 extends Properties = {}> extends TopoJSON {
|
|
256
|
+
type: GeoJsonGeometryTypes | null;
|
|
257
|
+
id?: number | string | undefined;
|
|
258
|
+
properties?: P$1 | undefined;
|
|
259
|
+
}
|
|
260
|
+
type GeometryObject<P$1 extends Properties = {}> = Point<P$1> | MultiPoint<P$1> | LineString<P$1> | MultiLineString<P$1> | Polygon<P$1> | MultiPolygon<P$1> | GeometryCollection<P$1> | NullObject;
|
|
261
|
+
// 2.2.1. Point
|
|
262
|
+
interface Point<P$1 extends Properties = {}> extends GeometryObjectA<P$1> {
|
|
263
|
+
type: "Point";
|
|
264
|
+
coordinates: Positions;
|
|
265
|
+
}
|
|
266
|
+
// 2.2.2. MultiPoint
|
|
267
|
+
interface MultiPoint<P$1 extends Properties = {}> extends GeometryObjectA<P$1> {
|
|
268
|
+
type: "MultiPoint";
|
|
269
|
+
coordinates: Positions[];
|
|
270
|
+
}
|
|
271
|
+
// 2.2.3. LineString
|
|
272
|
+
interface LineString<P$1 extends Properties = {}> extends GeometryObjectA<P$1> {
|
|
273
|
+
type: "LineString";
|
|
274
|
+
arcs: ArcIndexes;
|
|
275
|
+
}
|
|
276
|
+
// 2.2.4. MultiLineString
|
|
277
|
+
interface MultiLineString<P$1 extends Properties = {}> extends GeometryObjectA<P$1> {
|
|
278
|
+
type: "MultiLineString";
|
|
279
|
+
arcs: ArcIndexes[];
|
|
280
|
+
}
|
|
281
|
+
// 2.2.5. Polygon
|
|
282
|
+
interface Polygon<P$1 extends Properties = {}> extends GeometryObjectA<P$1> {
|
|
283
|
+
type: "Polygon";
|
|
284
|
+
arcs: ArcIndexes[];
|
|
285
|
+
}
|
|
286
|
+
// 2.2.6. MultiPolygon
|
|
287
|
+
interface MultiPolygon<P$1 extends Properties = {}> extends GeometryObjectA<P$1> {
|
|
288
|
+
type: "MultiPolygon";
|
|
289
|
+
arcs: ArcIndexes[][];
|
|
290
|
+
}
|
|
291
|
+
// 2.2.7. Geometry Collection
|
|
292
|
+
interface GeometryCollection<P$1 extends Properties = {}> extends GeometryObjectA<P$1> {
|
|
293
|
+
type: "GeometryCollection";
|
|
294
|
+
geometries: Array<GeometryObject<P$1>>;
|
|
295
|
+
}
|
|
296
|
+
// More
|
|
297
|
+
interface NullObject extends GeometryObjectA {
|
|
298
|
+
type: null;
|
|
299
|
+
}
|
|
300
|
+
//#endregion
|
|
301
|
+
//#region ../core/src/lib/utils.d.ts
|
|
302
|
+
type AnyFn = (...args: any) => any;
|
|
303
|
+
/**
|
|
304
|
+
* Extracts a union of parameter tuples from a (possibly overloaded) function type.
|
|
305
|
+
*
|
|
306
|
+
* TypeScript's built-in `Parameters<F>` only captures the *last* overload, which breaks typing
|
|
307
|
+
* for overloaded getter/setter APIs (common in d3), where the setter overload might not be last.
|
|
308
|
+
*
|
|
309
|
+
* Notes:
|
|
310
|
+
* - This helper supports up to 5 overload signatures (adjust if needed).
|
|
311
|
+
* - Getter overloads like `(): T` are filtered out later via `Exclude<..., []>` when we build
|
|
312
|
+
* setter-only config types.
|
|
313
|
+
*/
|
|
314
|
+
type OverloadedArgs<F> = F extends {
|
|
315
|
+
(...a: infer A1): any;
|
|
316
|
+
(...a: infer A2): any;
|
|
317
|
+
(...a: infer A3): any;
|
|
318
|
+
(...a: infer A4): any;
|
|
319
|
+
(...a: infer A5): any;
|
|
320
|
+
} ? A1 | A2 | A3 | A4 | A5 : F extends {
|
|
321
|
+
(...a: infer A1): any;
|
|
322
|
+
(...a: infer A2): any;
|
|
323
|
+
(...a: infer A3): any;
|
|
324
|
+
(...a: infer A4): any;
|
|
325
|
+
} ? A1 | A2 | A3 | A4 : F extends {
|
|
326
|
+
(...a: infer A1): any;
|
|
327
|
+
(...a: infer A2): any;
|
|
328
|
+
(...a: infer A3): any;
|
|
329
|
+
} ? A1 | A2 | A3 : F extends {
|
|
330
|
+
(...a: infer A1): any;
|
|
331
|
+
(...a: infer A2): any;
|
|
332
|
+
} ? A1 | A2 : F extends ((...a: infer A1) => any) ? A1 : never;
|
|
333
|
+
/**
|
|
334
|
+
* Removes 0-arg overloads (getters), leaving only setter-style overload argument tuples.
|
|
335
|
+
*/
|
|
336
|
+
type SetterArgs<F> = Exclude<OverloadedArgs<F>, []>;
|
|
337
|
+
/**
|
|
338
|
+
* True if the function has at least one overload that accepts arguments (i.e. a setter overload).
|
|
339
|
+
*/
|
|
340
|
+
type HasArgs<F> = [SetterArgs<F>] extends [never] ? false : true;
|
|
341
|
+
type OwnKeys<T> = T extends AnyFn ? Exclude<keyof T, keyof CallableFunction> : keyof T;
|
|
342
|
+
/**
|
|
343
|
+
* Converts method parameters to modifiers values
|
|
344
|
+
* - single non-array arg: `arg` | `[arg]`
|
|
345
|
+
* - multiple args/single array wrapped with array
|
|
346
|
+
*/
|
|
347
|
+
type ModifierArgs<P$1 extends unknown[]> = P$1 extends [infer Only] ? Only extends readonly unknown[] ? [Only] : Only | [Only] : P$1;
|
|
348
|
+
/**
|
|
349
|
+
* Maps methods with args to modifiers
|
|
350
|
+
*
|
|
351
|
+
* @example
|
|
352
|
+
* type X = {
|
|
353
|
+
* a: string; // not a function - will be ignored
|
|
354
|
+
* b(): void; // has no arguments - will be ignored
|
|
355
|
+
* c(x: number): void;
|
|
356
|
+
* d(x: number, y: string): void;
|
|
357
|
+
* e(xs: string[]): void;
|
|
358
|
+
* }
|
|
359
|
+
*
|
|
360
|
+
* type R = MethodsToModifiers<X>
|
|
361
|
+
* {
|
|
362
|
+
* c: number | [number];
|
|
363
|
+
* d: [number, string];
|
|
364
|
+
* e: [string[]]; // forced wrapper (arg is array)
|
|
365
|
+
* }
|
|
366
|
+
*/
|
|
367
|
+
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
|
+
//#endregion
|
|
369
|
+
//#region ../core/src/lib/map.d.ts
|
|
370
|
+
type MapMesh$1 = ReturnType<typeof mesh>;
|
|
371
|
+
type MapData = ExtendedFeatureCollection | Topology;
|
|
372
|
+
type DataTransformer = (features: MapFeature$1[]) => MapFeature$1[];
|
|
373
|
+
/**
|
|
374
|
+
* Extra projection method calls to apply before rendering.
|
|
375
|
+
*
|
|
376
|
+
* Use projection method names as keys and method arguments as values.
|
|
377
|
+
* Example: `{ center: [[0, 20]], rotate: [[0, 0, 0]], scale: 160 }`
|
|
378
|
+
*
|
|
379
|
+
* @see https://d3js.org/d3-geo/projection
|
|
380
|
+
*/
|
|
381
|
+
interface ProjectionConfig extends Omit<MethodsToModifiers<GeoProjection>, 'invert' | 'stream'> {}
|
|
382
|
+
/**
|
|
383
|
+
* Input configuration for creating a map context.
|
|
384
|
+
*
|
|
385
|
+
* In adapters, this is usually passed as component props.
|
|
386
|
+
*/
|
|
387
|
+
interface MapConfig {
|
|
388
|
+
width?: number;
|
|
389
|
+
height?: number;
|
|
390
|
+
aspectRatio?: number;
|
|
391
|
+
/**
|
|
392
|
+
* Projection factory from d3-geo (or a compatible implementation).
|
|
393
|
+
*
|
|
394
|
+
* Example: `geoNaturalEarth1`.
|
|
395
|
+
*/
|
|
396
|
+
projection?: () => GeoProjection;
|
|
397
|
+
/**
|
|
398
|
+
* Projection method arguments passed to the created projection
|
|
399
|
+
*/
|
|
400
|
+
projectionConfig?: ProjectionConfig;
|
|
401
|
+
/**
|
|
402
|
+
* TopoJSON or GeoJSON input.
|
|
403
|
+
*
|
|
404
|
+
* TopoJSON is automatically converted to GeoJSON features.
|
|
405
|
+
*/
|
|
406
|
+
data: MapData;
|
|
407
|
+
/**
|
|
408
|
+
* Optional feature transformer (filter/augment/normalize features).
|
|
409
|
+
*/
|
|
410
|
+
dataTransformer?: DataTransformer;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Fully computed, framework-agnostic map context.
|
|
414
|
+
*
|
|
415
|
+
* Adapters provide this context to child layers (features, markers, custom SVG).
|
|
416
|
+
*/
|
|
417
|
+
interface MapContext {
|
|
418
|
+
width: number;
|
|
419
|
+
height: number;
|
|
420
|
+
projection?: GeoProjection;
|
|
421
|
+
features: MapFeature$1[];
|
|
422
|
+
mesh?: MapMesh$1;
|
|
423
|
+
path: GeoPath;
|
|
424
|
+
renderPath: (feature: MapFeature$1) => ReturnType<GeoPath>;
|
|
425
|
+
renderMesh: () => ReturnType<GeoPath>;
|
|
426
|
+
}
|
|
427
|
+
//#endregion
|
|
428
|
+
//#region ../core/src/lib/marker.d.ts
|
|
429
|
+
type MapMarkerCoordinates = [number, number];
|
|
430
|
+
/**
|
|
431
|
+
* Shared props contract for marker layers.
|
|
432
|
+
*/
|
|
433
|
+
interface MapMarkerProps$1<TStyle = unknown> {
|
|
434
|
+
coordinates?: MapMarkerCoordinates;
|
|
435
|
+
styles?: MapObjectStyles$1<TStyle>;
|
|
436
|
+
}
|
|
437
|
+
//#endregion
|
|
438
|
+
//#region ../core/src/lib/zoom.d.ts
|
|
439
|
+
interface DefaultZoomBehavior extends ZoomBehavior<SVGSVGElement, unknown> {}
|
|
440
|
+
/**
|
|
441
|
+
* Extra zoom method calls to apply before rendering.
|
|
442
|
+
*
|
|
443
|
+
* Use zoom method names as keys and method arguments as values.
|
|
444
|
+
* Example: `{ scaleExtent: [[2, 9]], translateExtent: [[[0, 0], [10, 10]]] }`
|
|
445
|
+
*
|
|
446
|
+
* @see https://d3js.org/d3-zoom
|
|
447
|
+
*/
|
|
448
|
+
interface ZoomModifiers extends MethodsToModifiers<DefaultZoomBehavior> {}
|
|
449
|
+
interface ZoomProps {
|
|
450
|
+
center?: [number, number];
|
|
451
|
+
zoom?: number;
|
|
452
|
+
minZoom?: number;
|
|
453
|
+
maxZoom?: number;
|
|
454
|
+
config?: ZoomModifiers;
|
|
455
|
+
}
|
|
456
|
+
interface ZoomEvent extends D3ZoomEvent<SVGSVGElement, unknown> {}
|
|
457
|
+
//#endregion
|
|
458
|
+
//#region src/components/Map.d.ts
|
|
459
|
+
type MapRenderProp = (context: MapContext) => ReactNode;
|
|
460
|
+
interface MapProps extends MapConfig, Omit<SVGProps<SVGSVGElement>, keyof MapConfig | 'children'> {
|
|
461
|
+
children?: ReactNode | MapRenderProp;
|
|
462
|
+
}
|
|
463
|
+
declare function Map({
|
|
464
|
+
width,
|
|
465
|
+
height,
|
|
466
|
+
aspectRatio,
|
|
467
|
+
projection,
|
|
468
|
+
projectionConfig,
|
|
469
|
+
data,
|
|
470
|
+
dataTransformer,
|
|
471
|
+
children,
|
|
472
|
+
className,
|
|
473
|
+
...svgProps
|
|
474
|
+
}: MapProps): ReactElement;
|
|
475
|
+
//#endregion
|
|
476
|
+
//#region src/components/MapFeature.d.ts
|
|
477
|
+
interface MapFeatureProps extends MapFeatureProps$1<CSSProperties>, Omit<SVGProps<SVGPathElement>, 'children' | 'd' | 'style'> {}
|
|
478
|
+
declare function MapFeature({
|
|
479
|
+
data,
|
|
480
|
+
styles,
|
|
481
|
+
fill,
|
|
482
|
+
stroke,
|
|
483
|
+
onMouseEnter,
|
|
484
|
+
onMouseLeave,
|
|
485
|
+
onMouseDown,
|
|
486
|
+
onMouseUp,
|
|
487
|
+
onClick,
|
|
488
|
+
onFocus,
|
|
489
|
+
onBlur,
|
|
490
|
+
...pathProps
|
|
491
|
+
}: MapFeatureProps): ReactElement | null;
|
|
492
|
+
//#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
|
+
//#region src/components/MapFeatures.d.ts
|
|
519
|
+
interface MapFeaturesRenderProps {
|
|
520
|
+
features: MapFeature$1[];
|
|
521
|
+
}
|
|
522
|
+
type MapFeaturesChildren = ReactNode | ((props: MapFeaturesRenderProps) => ReactNode);
|
|
523
|
+
interface MapFeaturesProps {
|
|
524
|
+
idKey?: string;
|
|
525
|
+
fill?: string;
|
|
526
|
+
stroke?: string;
|
|
527
|
+
styles?: MapObjectStyles;
|
|
528
|
+
children?: MapFeaturesChildren;
|
|
529
|
+
}
|
|
530
|
+
declare function MapFeatures({
|
|
531
|
+
idKey,
|
|
532
|
+
fill,
|
|
533
|
+
stroke,
|
|
534
|
+
styles,
|
|
535
|
+
children
|
|
536
|
+
}: MapFeaturesProps): ReactElement;
|
|
537
|
+
//#endregion
|
|
538
|
+
//#region src/components/MapMarker.d.ts
|
|
539
|
+
interface MapMarkerProps extends MapMarkerProps$1<CSSProperties>, Omit<SVGProps<SVGGElement>, 'style'> {}
|
|
540
|
+
declare function MapMarker({
|
|
541
|
+
coordinates,
|
|
542
|
+
styles,
|
|
543
|
+
children,
|
|
544
|
+
onMouseEnter,
|
|
545
|
+
onMouseLeave,
|
|
546
|
+
onMouseDown,
|
|
547
|
+
onMouseUp,
|
|
548
|
+
onClick,
|
|
549
|
+
onFocus,
|
|
550
|
+
onBlur,
|
|
551
|
+
...groupProps
|
|
552
|
+
}: MapMarkerProps): ReactElement;
|
|
553
|
+
//#endregion
|
|
554
|
+
//#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
|
+
}
|
|
560
|
+
declare function MapMesh({
|
|
561
|
+
fill,
|
|
562
|
+
stroke,
|
|
563
|
+
styles,
|
|
564
|
+
onMouseEnter,
|
|
565
|
+
onMouseLeave,
|
|
566
|
+
onMouseDown,
|
|
567
|
+
onMouseUp,
|
|
568
|
+
onClick,
|
|
569
|
+
onFocus,
|
|
570
|
+
onBlur,
|
|
571
|
+
...pathProps
|
|
572
|
+
}: MapMeshProps): ReactElement | null;
|
|
573
|
+
//#endregion
|
|
574
|
+
//#region src/components/MapZoom.d.ts
|
|
575
|
+
interface MapZoomProps extends ZoomProps, Omit<SVGProps<SVGGElement>, 'children' | 'onZoom'> {
|
|
576
|
+
children?: ReactNode;
|
|
577
|
+
onZoomStart?: (event: ZoomEvent) => void;
|
|
578
|
+
onZoom?: (event: ZoomEvent) => void;
|
|
579
|
+
onZoomEnd?: (event: ZoomEvent) => void;
|
|
580
|
+
}
|
|
581
|
+
declare function MapZoom({
|
|
582
|
+
center,
|
|
583
|
+
zoom,
|
|
584
|
+
minZoom,
|
|
585
|
+
maxZoom,
|
|
586
|
+
config,
|
|
587
|
+
onZoomStart,
|
|
588
|
+
onZoom,
|
|
589
|
+
onZoomEnd,
|
|
590
|
+
children,
|
|
591
|
+
className,
|
|
592
|
+
...groupProps
|
|
593
|
+
}: MapZoomProps): ReactElement;
|
|
594
|
+
//#endregion
|
|
595
|
+
//#region src/hooks/useMapContext.d.ts
|
|
596
|
+
declare const MapContextValue: react0.Context<MapContext | undefined>;
|
|
597
|
+
declare function useMapContext(): MapContext | undefined;
|
|
598
|
+
//#endregion
|
|
599
|
+
export { Map, MapContextValue, MapFeature, MapFeatures, MapMarker, MapMesh, MapObjectStyle, MapObjectStyles, MapZoom, UseMapObjectOptions, UseMapObjectResult, useMapContext, useMapObject };
|
|
@@ -0,0 +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);
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import "@d3-maps/core/index.css";
|
|
4
|
+
import { ZOOM_DEFAULTS, applyZoomGroupTransform, applyZoomTransform, createZoomBehavior, getFeatureKey, getMarkerTransform, getObjectStateUpdate, makeMapContext, resolveObjectStyle, setupZoom } from "@d3-maps/core";
|
|
5
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
|
|
8
|
+
//#region src/hooks/useMapContext.ts
|
|
9
|
+
const MapContextValue = createContext(void 0);
|
|
10
|
+
function useMapContext() {
|
|
11
|
+
return useContext(MapContextValue);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/components/MapContext.tsx
|
|
16
|
+
function MapProvider({ context, children }) {
|
|
17
|
+
return /* @__PURE__ */ jsx(MapContextValue.Provider, {
|
|
18
|
+
value: context,
|
|
19
|
+
children
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/components/Map.tsx
|
|
25
|
+
function isRenderProp$1(children) {
|
|
26
|
+
return typeof children === "function";
|
|
27
|
+
}
|
|
28
|
+
function Map({ width, height, aspectRatio, projection, projectionConfig, data, dataTransformer, children, className, ...svgProps }) {
|
|
29
|
+
const context = useMemo(() => {
|
|
30
|
+
return makeMapContext({
|
|
31
|
+
width,
|
|
32
|
+
height,
|
|
33
|
+
aspectRatio,
|
|
34
|
+
projection,
|
|
35
|
+
projectionConfig,
|
|
36
|
+
data,
|
|
37
|
+
dataTransformer
|
|
38
|
+
});
|
|
39
|
+
}, [
|
|
40
|
+
width,
|
|
41
|
+
height,
|
|
42
|
+
aspectRatio,
|
|
43
|
+
projection,
|
|
44
|
+
projectionConfig,
|
|
45
|
+
data,
|
|
46
|
+
dataTransformer
|
|
47
|
+
]);
|
|
48
|
+
const resolvedChildren = isRenderProp$1(children) ? children(context) : children;
|
|
49
|
+
return /* @__PURE__ */ jsx(MapProvider, {
|
|
50
|
+
context,
|
|
51
|
+
children: /* @__PURE__ */ jsx("svg", {
|
|
52
|
+
...svgProps,
|
|
53
|
+
className: `d3-map ${className ?? ""}`,
|
|
54
|
+
viewBox: `0 0 ${context.width} ${context.height}`,
|
|
55
|
+
children: resolvedChildren
|
|
56
|
+
})
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/hooks/useLatest.ts
|
|
62
|
+
function useLatest(value) {
|
|
63
|
+
const ref = useRef(value);
|
|
64
|
+
ref.current = value;
|
|
65
|
+
return ref;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region src/hooks/useMapObject.ts
|
|
70
|
+
function useMapObject(options) {
|
|
71
|
+
const [state, setState] = useState("default");
|
|
72
|
+
const onMouseEnterRef = useLatest(options.onMouseEnter);
|
|
73
|
+
const onMouseLeaveRef = useLatest(options.onMouseLeave);
|
|
74
|
+
const onMouseDownRef = useLatest(options.onMouseDown);
|
|
75
|
+
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
|
+
});
|
|
84
|
+
}, []);
|
|
85
|
+
return {
|
|
86
|
+
computedStyle: useMemo(() => {
|
|
87
|
+
return resolveObjectStyle(state, options.styles);
|
|
88
|
+
}, [state, options.styles]),
|
|
89
|
+
onMouseEnter: useCallback((event) => {
|
|
90
|
+
setStateForEvent("mouseenter");
|
|
91
|
+
onMouseEnterRef.current?.(event);
|
|
92
|
+
}, [setStateForEvent]),
|
|
93
|
+
onMouseLeave: useCallback((event) => {
|
|
94
|
+
setStateForEvent("mouseleave");
|
|
95
|
+
onMouseLeaveRef.current?.(event);
|
|
96
|
+
}, [setStateForEvent]),
|
|
97
|
+
onMouseDown: useCallback((event) => {
|
|
98
|
+
setStateForEvent("mousedown");
|
|
99
|
+
onMouseDownRef.current?.(event);
|
|
100
|
+
}, [setStateForEvent]),
|
|
101
|
+
onMouseUp: useCallback((event) => {
|
|
102
|
+
setStateForEvent("mouseup");
|
|
103
|
+
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])
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/components/MapFeature.tsx
|
|
122
|
+
function MapFeature({ data, styles, fill, stroke, onMouseEnter, onMouseLeave, onMouseDown, onMouseUp, onClick, onFocus, onBlur, ...pathProps }) {
|
|
123
|
+
const context = useMapContext();
|
|
124
|
+
const path = useMemo(() => {
|
|
125
|
+
return context?.renderPath(data) ?? null;
|
|
126
|
+
}, [context, data]);
|
|
127
|
+
const { computedStyle, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, onClick: handleClick, onFocus: handleFocus, onBlur: handleBlur } = useMapObject({
|
|
128
|
+
styles,
|
|
129
|
+
onMouseEnter,
|
|
130
|
+
onMouseLeave,
|
|
131
|
+
onMouseDown,
|
|
132
|
+
onMouseUp,
|
|
133
|
+
onClick,
|
|
134
|
+
onFocus,
|
|
135
|
+
onBlur
|
|
136
|
+
});
|
|
137
|
+
return path ? /* @__PURE__ */ jsx("path", {
|
|
138
|
+
...pathProps,
|
|
139
|
+
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;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/components/MapFeatures.tsx
|
|
155
|
+
function isRenderProp(children) {
|
|
156
|
+
return typeof children === "function";
|
|
157
|
+
}
|
|
158
|
+
function MapFeatures({ idKey = "id", fill, stroke, styles, children }) {
|
|
159
|
+
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))) });
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region src/components/MapMarker.tsx
|
|
170
|
+
const DEFAULT_COORDINATES = [0, 0];
|
|
171
|
+
function MapMarker({ coordinates = DEFAULT_COORDINATES, styles, children, onMouseEnter, onMouseLeave, onMouseDown, onMouseUp, onClick, onFocus, onBlur, ...groupProps }) {
|
|
172
|
+
const context = useMapContext();
|
|
173
|
+
const transform = useMemo(() => {
|
|
174
|
+
return getMarkerTransform(context, coordinates);
|
|
175
|
+
}, [
|
|
176
|
+
context,
|
|
177
|
+
coordinates[0],
|
|
178
|
+
coordinates[1]
|
|
179
|
+
]);
|
|
180
|
+
const { computedStyle, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, onClick: handleClick, onFocus: handleFocus, onBlur: handleBlur } = useMapObject({
|
|
181
|
+
styles,
|
|
182
|
+
onMouseEnter,
|
|
183
|
+
onMouseLeave,
|
|
184
|
+
onMouseDown,
|
|
185
|
+
onMouseUp,
|
|
186
|
+
onClick,
|
|
187
|
+
onFocus,
|
|
188
|
+
onBlur
|
|
189
|
+
});
|
|
190
|
+
return /* @__PURE__ */ jsx("g", {
|
|
191
|
+
...groupProps,
|
|
192
|
+
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,
|
|
201
|
+
children
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
//#endregion
|
|
206
|
+
//#region src/components/MapMesh.tsx
|
|
207
|
+
function MapMesh({ fill = "none", stroke, styles, onMouseEnter, onMouseLeave, onMouseDown, onMouseUp, onClick, onFocus, onBlur, ...pathProps }) {
|
|
208
|
+
const context = useMapContext();
|
|
209
|
+
const path = useMemo(() => {
|
|
210
|
+
return context?.renderMesh() ?? null;
|
|
211
|
+
}, [context]);
|
|
212
|
+
const { computedStyle, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, onClick: handleClick, onFocus: handleFocus, onBlur: handleBlur } = useMapObject({
|
|
213
|
+
styles,
|
|
214
|
+
onMouseEnter,
|
|
215
|
+
onMouseLeave,
|
|
216
|
+
onMouseDown,
|
|
217
|
+
onMouseUp,
|
|
218
|
+
onClick,
|
|
219
|
+
onFocus,
|
|
220
|
+
onBlur
|
|
221
|
+
});
|
|
222
|
+
return path ? /* @__PURE__ */ jsx("path", {
|
|
223
|
+
...pathProps,
|
|
224
|
+
d: path,
|
|
225
|
+
style: computedStyle,
|
|
226
|
+
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;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
//#endregion
|
|
239
|
+
//#region src/components/MapZoom.tsx
|
|
240
|
+
function MapZoom({ center, zoom, minZoom = ZOOM_DEFAULTS.minZoom, maxZoom = ZOOM_DEFAULTS.maxZoom, config, onZoomStart, onZoom, onZoomEnd, children, className, ...groupProps }) {
|
|
241
|
+
const containerRef = useRef(null);
|
|
242
|
+
const skipNextTransformSyncRef = useRef(false);
|
|
243
|
+
const context = useMapContext();
|
|
244
|
+
const onZoomStartRef = useLatest(onZoomStart);
|
|
245
|
+
const onZoomRef = useLatest(onZoom);
|
|
246
|
+
const onZoomEndRef = useLatest(onZoomEnd);
|
|
247
|
+
const resolvedCenter = center ?? ZOOM_DEFAULTS.center;
|
|
248
|
+
const resolvedZoom = zoom ?? ZOOM_DEFAULTS.zoom;
|
|
249
|
+
const centerX = resolvedCenter[0];
|
|
250
|
+
const centerY = resolvedCenter[1];
|
|
251
|
+
const zoomBehavior = useMemo(() => {
|
|
252
|
+
return createZoomBehavior(context, {
|
|
253
|
+
minZoom,
|
|
254
|
+
maxZoom,
|
|
255
|
+
config,
|
|
256
|
+
onZoomStart: (event) => {
|
|
257
|
+
onZoomStartRef.current?.(event);
|
|
258
|
+
},
|
|
259
|
+
onZoom: (event) => {
|
|
260
|
+
applyZoomGroupTransform(containerRef.current, event.transform);
|
|
261
|
+
onZoomRef.current?.(event);
|
|
262
|
+
},
|
|
263
|
+
onZoomEnd: (event) => {
|
|
264
|
+
onZoomEndRef.current?.(event);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
}, [
|
|
268
|
+
context,
|
|
269
|
+
minZoom,
|
|
270
|
+
maxZoom,
|
|
271
|
+
config
|
|
272
|
+
]);
|
|
273
|
+
useEffect(() => {
|
|
274
|
+
skipNextTransformSyncRef.current = true;
|
|
275
|
+
setupZoom({
|
|
276
|
+
element: containerRef.current,
|
|
277
|
+
behavior: zoomBehavior,
|
|
278
|
+
center: resolvedCenter,
|
|
279
|
+
zoom: resolvedZoom
|
|
280
|
+
});
|
|
281
|
+
}, [zoomBehavior]);
|
|
282
|
+
useEffect(() => {
|
|
283
|
+
if (skipNextTransformSyncRef.current) {
|
|
284
|
+
skipNextTransformSyncRef.current = false;
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
applyZoomTransform({
|
|
288
|
+
element: containerRef.current,
|
|
289
|
+
behavior: zoomBehavior,
|
|
290
|
+
center: [centerX, centerY],
|
|
291
|
+
zoom: resolvedZoom
|
|
292
|
+
});
|
|
293
|
+
}, [
|
|
294
|
+
zoomBehavior,
|
|
295
|
+
centerX,
|
|
296
|
+
centerY,
|
|
297
|
+
resolvedZoom
|
|
298
|
+
]);
|
|
299
|
+
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
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
//#endregion
|
|
309
|
+
export { Map, MapContextValue, MapFeature, MapFeatures, MapMarker, MapMesh, MapZoom, useMapContext, useMapObject };
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@d3-maps/react",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"private": false,
|
|
6
|
+
"description": "React bindings for @d3-maps/core to build reactive D3 SVG maps",
|
|
7
|
+
"author": "Georgii Bukharov <souljorje@gmail.com>",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://souljorje.github.io/d3-maps",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/souljorje/d3-maps.git",
|
|
16
|
+
"directory": "packages/react"
|
|
17
|
+
},
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/souljorje/d3-maps/issues"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"react",
|
|
23
|
+
"d3",
|
|
24
|
+
"svg",
|
|
25
|
+
"map"
|
|
26
|
+
],
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js",
|
|
31
|
+
"browser": "./dist/index.iife.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"main": "dist/index.js",
|
|
35
|
+
"module": "dist/index.js",
|
|
36
|
+
"browser": "dist/index.iife.js",
|
|
37
|
+
"unpkg": "dist/index.iife.js",
|
|
38
|
+
"jsdelivr": "dist/index.iife.js",
|
|
39
|
+
"types": "dist/index.d.ts",
|
|
40
|
+
"files": [
|
|
41
|
+
"dist/*"
|
|
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
|
+
"peerDependencies": {
|
|
51
|
+
"react": ">=19 <20",
|
|
52
|
+
"react-dom": ">=19 <20"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@d3-maps/core": "workspace:*"
|
|
56
|
+
},
|
|
57
|
+
"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:"
|
|
72
|
+
}
|
|
73
|
+
}
|