@d3-maps/core 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/LICENCE +21 -0
- package/dist/index.css +9 -0
- package/dist/index.d.ts +501 -0
- package/dist/index.iife.js +1 -0
- package/dist/index.js +256 -0
- package/package.json +60 -0
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/dist/index.css
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
import { GeoPath, GeoProjection } from "d3-geo";
|
|
2
|
+
import { D3ZoomEvent, D3ZoomEvent as D3ZoomEvent$1, ZoomBehavior, ZoomBehavior as ZoomBehavior$1, ZoomTransform, ZoomTransform as ZoomTransform$1 } from "d3-zoom";
|
|
3
|
+
|
|
4
|
+
//#region ../../node_modules/.pnpm/@types+geojson@7946.0.16/node_modules/@types/geojson/index.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The valid values for the "type" property of GeoJSON geometry objects.
|
|
8
|
+
* https://tools.ietf.org/html/rfc7946#section-1.4
|
|
9
|
+
*/
|
|
10
|
+
type GeoJsonGeometryTypes = Geometry["type"];
|
|
11
|
+
/**
|
|
12
|
+
* The value values for the "type" property of GeoJSON Objects.
|
|
13
|
+
* https://tools.ietf.org/html/rfc7946#section-1.4
|
|
14
|
+
*/
|
|
15
|
+
type GeoJsonTypes = GeoJSON["type"];
|
|
16
|
+
/**
|
|
17
|
+
* Bounding box
|
|
18
|
+
* https://tools.ietf.org/html/rfc7946#section-5
|
|
19
|
+
*/
|
|
20
|
+
type BBox = [number, number, number, number] | [number, number, number, number, number, number];
|
|
21
|
+
/**
|
|
22
|
+
* A Position is an array of coordinates.
|
|
23
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.1
|
|
24
|
+
* Array should contain between two and three elements.
|
|
25
|
+
* The previous GeoJSON specification allowed more elements (e.g., which could be used to represent M values),
|
|
26
|
+
* but the current specification only allows X, Y, and (optionally) Z to be defined.
|
|
27
|
+
*
|
|
28
|
+
* Note: the type will not be narrowed down to `[number, number] | [number, number, number]` due to
|
|
29
|
+
* marginal benefits and the large impact of breaking change.
|
|
30
|
+
*
|
|
31
|
+
* See previous discussions on the type narrowing:
|
|
32
|
+
* - {@link https://github.com/DefinitelyTyped/DefinitelyTyped/pull/21590|Nov 2017}
|
|
33
|
+
* - {@link https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/67773|Dec 2023}
|
|
34
|
+
* - {@link https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/71441| Dec 2024}
|
|
35
|
+
*
|
|
36
|
+
* One can use a
|
|
37
|
+
* {@link https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates|user-defined type guard that returns a type predicate}
|
|
38
|
+
* to determine if a position is a 2D or 3D position.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* import type { Position } from 'geojson';
|
|
42
|
+
*
|
|
43
|
+
* type StrictPosition = [x: number, y: number] | [x: number, y: number, z: number]
|
|
44
|
+
*
|
|
45
|
+
* function isStrictPosition(position: Position): position is StrictPosition {
|
|
46
|
+
* return position.length === 2 || position.length === 3
|
|
47
|
+
* };
|
|
48
|
+
*
|
|
49
|
+
* let position: Position = [-116.91, 45.54];
|
|
50
|
+
*
|
|
51
|
+
* let x: number;
|
|
52
|
+
* let y: number;
|
|
53
|
+
* let z: number | undefined;
|
|
54
|
+
*
|
|
55
|
+
* if (isStrictPosition(position)) {
|
|
56
|
+
* // `tsc` would throw an error if we tried to destructure a fourth parameter
|
|
57
|
+
* [x, y, z] = position;
|
|
58
|
+
* } else {
|
|
59
|
+
* throw new TypeError("Position is not a 2D or 3D point");
|
|
60
|
+
* }
|
|
61
|
+
*/
|
|
62
|
+
type Position = number[];
|
|
63
|
+
/**
|
|
64
|
+
* The base GeoJSON object.
|
|
65
|
+
* https://tools.ietf.org/html/rfc7946#section-3
|
|
66
|
+
* The GeoJSON specification also allows foreign members
|
|
67
|
+
* (https://tools.ietf.org/html/rfc7946#section-6.1)
|
|
68
|
+
* Developers should use "&" type in TypeScript or extend the interface
|
|
69
|
+
* to add these foreign members.
|
|
70
|
+
*/
|
|
71
|
+
interface GeoJsonObject {
|
|
72
|
+
// Don't include foreign members directly into this type def.
|
|
73
|
+
// in order to preserve type safety.
|
|
74
|
+
// [key: string]: any;
|
|
75
|
+
/**
|
|
76
|
+
* Specifies the type of GeoJSON object.
|
|
77
|
+
*/
|
|
78
|
+
type: GeoJsonTypes;
|
|
79
|
+
/**
|
|
80
|
+
* Bounding box of the coordinate range of the object's Geometries, Features, or Feature Collections.
|
|
81
|
+
* The value of the bbox member is an array of length 2*n where n is the number of dimensions
|
|
82
|
+
* represented in the contained geometries, with all axes of the most southwesterly point
|
|
83
|
+
* followed by all axes of the more northeasterly point.
|
|
84
|
+
* The axes order of a bbox follows the axes order of geometries.
|
|
85
|
+
* https://tools.ietf.org/html/rfc7946#section-5
|
|
86
|
+
*/
|
|
87
|
+
bbox?: BBox | undefined;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Union of GeoJSON objects.
|
|
91
|
+
*/
|
|
92
|
+
type GeoJSON<G extends Geometry | null = Geometry, P$1 = GeoJsonProperties> = G | Feature<G, P$1> | FeatureCollection<G, P$1>;
|
|
93
|
+
/**
|
|
94
|
+
* Geometry object.
|
|
95
|
+
* https://tools.ietf.org/html/rfc7946#section-3
|
|
96
|
+
*/
|
|
97
|
+
type Geometry = Point$1 | MultiPoint$1 | LineString$1 | MultiLineString$1 | Polygon$1 | MultiPolygon$1 | GeometryCollection$1;
|
|
98
|
+
/**
|
|
99
|
+
* Point geometry object.
|
|
100
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.2
|
|
101
|
+
*/
|
|
102
|
+
interface Point$1 extends GeoJsonObject {
|
|
103
|
+
type: "Point";
|
|
104
|
+
coordinates: Position;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* MultiPoint geometry object.
|
|
108
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.3
|
|
109
|
+
*/
|
|
110
|
+
interface MultiPoint$1 extends GeoJsonObject {
|
|
111
|
+
type: "MultiPoint";
|
|
112
|
+
coordinates: Position[];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* LineString geometry object.
|
|
116
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.4
|
|
117
|
+
*/
|
|
118
|
+
interface LineString$1 extends GeoJsonObject {
|
|
119
|
+
type: "LineString";
|
|
120
|
+
coordinates: Position[];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* MultiLineString geometry object.
|
|
124
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.5
|
|
125
|
+
*/
|
|
126
|
+
interface MultiLineString$1 extends GeoJsonObject {
|
|
127
|
+
type: "MultiLineString";
|
|
128
|
+
coordinates: Position[][];
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Polygon geometry object.
|
|
132
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.6
|
|
133
|
+
*/
|
|
134
|
+
interface Polygon$1 extends GeoJsonObject {
|
|
135
|
+
type: "Polygon";
|
|
136
|
+
coordinates: Position[][];
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* MultiPolygon geometry object.
|
|
140
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.7
|
|
141
|
+
*/
|
|
142
|
+
interface MultiPolygon$1 extends GeoJsonObject {
|
|
143
|
+
type: "MultiPolygon";
|
|
144
|
+
coordinates: Position[][][];
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Geometry Collection
|
|
148
|
+
* https://tools.ietf.org/html/rfc7946#section-3.1.8
|
|
149
|
+
*/
|
|
150
|
+
interface GeometryCollection$1<G extends Geometry = Geometry> extends GeoJsonObject {
|
|
151
|
+
type: "GeometryCollection";
|
|
152
|
+
geometries: G[];
|
|
153
|
+
}
|
|
154
|
+
type GeoJsonProperties = {
|
|
155
|
+
[name: string]: any;
|
|
156
|
+
} | null;
|
|
157
|
+
/**
|
|
158
|
+
* A feature object which contains a geometry and associated properties.
|
|
159
|
+
* https://tools.ietf.org/html/rfc7946#section-3.2
|
|
160
|
+
*/
|
|
161
|
+
interface Feature<G extends Geometry | null = Geometry, P$1 = GeoJsonProperties> extends GeoJsonObject {
|
|
162
|
+
type: "Feature";
|
|
163
|
+
/**
|
|
164
|
+
* The feature's geometry
|
|
165
|
+
*/
|
|
166
|
+
geometry: G;
|
|
167
|
+
/**
|
|
168
|
+
* A value that uniquely identifies this feature in a
|
|
169
|
+
* https://tools.ietf.org/html/rfc7946#section-3.2.
|
|
170
|
+
*/
|
|
171
|
+
id?: string | number | undefined;
|
|
172
|
+
/**
|
|
173
|
+
* Properties associated with this feature.
|
|
174
|
+
*/
|
|
175
|
+
properties: P$1;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* A collection of feature objects.
|
|
179
|
+
* https://tools.ietf.org/html/rfc7946#section-3.3
|
|
180
|
+
*/
|
|
181
|
+
interface FeatureCollection<G extends Geometry | null = Geometry, P$1 = GeoJsonProperties> extends GeoJsonObject {
|
|
182
|
+
type: "FeatureCollection";
|
|
183
|
+
features: Array<Feature<G, P$1>>;
|
|
184
|
+
}
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region src/lib/mapObject.d.ts
|
|
187
|
+
type MapObject = Point$1 | Feature;
|
|
188
|
+
type MapObjectFocusEventType = 'focus' | 'blur';
|
|
189
|
+
type MapObjectMouseEventType = 'mouseenter' | 'mouseleave' | 'mousedown' | 'mouseup';
|
|
190
|
+
type MapObjectEventType = MapObjectFocusEventType | MapObjectMouseEventType;
|
|
191
|
+
type MapObjectEvent<E> = E extends MapObjectFocusEventType ? FocusEvent : MouseEvent;
|
|
192
|
+
/**
|
|
193
|
+
* Supported interaction states for map objects.
|
|
194
|
+
*/
|
|
195
|
+
declare const mapObjectState: readonly ["default", "hover", "active"];
|
|
196
|
+
type MapObjectState = typeof mapObjectState[number];
|
|
197
|
+
type MapObjectStyles<TStyle> = Partial<Record<MapObjectState, TStyle>>;
|
|
198
|
+
/**
|
|
199
|
+
* Maps DOM event names to interaction state updates.
|
|
200
|
+
*/
|
|
201
|
+
declare function getObjectStateUpdate(event: MapObjectEventType): MapObjectState;
|
|
202
|
+
/**
|
|
203
|
+
* Resolves a style value for the current state (falls back to `default`).
|
|
204
|
+
*/
|
|
205
|
+
declare function resolveObjectStyle<TStyle>(state: MapObjectState, styles?: MapObjectStyles<TStyle>): TStyle | undefined;
|
|
206
|
+
//#endregion
|
|
207
|
+
//#region src/lib/feature.d.ts
|
|
208
|
+
/**
|
|
209
|
+
* A GeoJSON Feature used by d3-maps.
|
|
210
|
+
*
|
|
211
|
+
* This type allows extra top-level fields to be attached in `dataTransformer` (e.g. choropleth colors).
|
|
212
|
+
*/
|
|
213
|
+
type MapFeature = (Feature & Record<string, unknown>) | Feature;
|
|
214
|
+
/**
|
|
215
|
+
* Shared props contract for a single rendered feature.
|
|
216
|
+
*/
|
|
217
|
+
interface MapFeatureProps<TStyle = unknown> {
|
|
218
|
+
data: MapFeature;
|
|
219
|
+
styles?: MapObjectStyles<TStyle>;
|
|
220
|
+
fill?: string;
|
|
221
|
+
stroke?: string;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Resolves a stable key for a feature.
|
|
225
|
+
*
|
|
226
|
+
* Checks:
|
|
227
|
+
* 1) `feature[idKey]`
|
|
228
|
+
* 2) `feature.properties[idKey]`
|
|
229
|
+
* 3) fallback to the list index
|
|
230
|
+
*/
|
|
231
|
+
declare function getFeatureKey(feature: MapFeature, idKey: string | undefined, index: number): string | number;
|
|
232
|
+
//#endregion
|
|
233
|
+
//#region ../../node_modules/.pnpm/@types+topojson-specification@1.0.5/node_modules/@types/topojson-specification/index.d.ts
|
|
234
|
+
// ---------------------------------------------------------------
|
|
235
|
+
// TopoJSON Format Specification
|
|
236
|
+
// ---------------------------------------------------------------
|
|
237
|
+
|
|
238
|
+
// See: https://github.com/topojson/topojson-specification/
|
|
239
|
+
|
|
240
|
+
// 2. TopoJSON Objects
|
|
241
|
+
interface TopoJSON {
|
|
242
|
+
type: "Topology" | GeoJsonGeometryTypes | null;
|
|
243
|
+
bbox?: BBox | undefined;
|
|
244
|
+
}
|
|
245
|
+
// 2.1. Topology Objects
|
|
246
|
+
interface Topology<T extends Objects<Properties> = Objects<Properties>> extends TopoJSON {
|
|
247
|
+
type: "Topology";
|
|
248
|
+
objects: T;
|
|
249
|
+
arcs: Arc[];
|
|
250
|
+
transform?: Transform | undefined;
|
|
251
|
+
}
|
|
252
|
+
// 2.1.1. Positions
|
|
253
|
+
type Positions = number[];
|
|
254
|
+
// at least two elements
|
|
255
|
+
|
|
256
|
+
// 2.1.2. Transforms
|
|
257
|
+
interface Transform {
|
|
258
|
+
scale: [number, number];
|
|
259
|
+
translate: [number, number];
|
|
260
|
+
}
|
|
261
|
+
// 2.1.3. Arcs
|
|
262
|
+
type Arc = Positions[];
|
|
263
|
+
// at least two elements
|
|
264
|
+
|
|
265
|
+
// 2.1.4. Arc Indexes
|
|
266
|
+
type ArcIndexes = number[];
|
|
267
|
+
// 2.1.5. Objects
|
|
268
|
+
type Properties = GeoJsonProperties;
|
|
269
|
+
interface Objects<P$1 extends Properties = {}> {
|
|
270
|
+
[key: string]: GeometryObject<P$1>;
|
|
271
|
+
}
|
|
272
|
+
// 2.2. Geometry Objects
|
|
273
|
+
interface GeometryObjectA<P$1 extends Properties = {}> extends TopoJSON {
|
|
274
|
+
type: GeoJsonGeometryTypes | null;
|
|
275
|
+
id?: number | string | undefined;
|
|
276
|
+
properties?: P$1 | undefined;
|
|
277
|
+
}
|
|
278
|
+
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;
|
|
279
|
+
// 2.2.1. Point
|
|
280
|
+
interface Point<P$1 extends Properties = {}> extends GeometryObjectA<P$1> {
|
|
281
|
+
type: "Point";
|
|
282
|
+
coordinates: Positions;
|
|
283
|
+
}
|
|
284
|
+
// 2.2.2. MultiPoint
|
|
285
|
+
interface MultiPoint<P$1 extends Properties = {}> extends GeometryObjectA<P$1> {
|
|
286
|
+
type: "MultiPoint";
|
|
287
|
+
coordinates: Positions[];
|
|
288
|
+
}
|
|
289
|
+
// 2.2.3. LineString
|
|
290
|
+
interface LineString<P$1 extends Properties = {}> extends GeometryObjectA<P$1> {
|
|
291
|
+
type: "LineString";
|
|
292
|
+
arcs: ArcIndexes;
|
|
293
|
+
}
|
|
294
|
+
// 2.2.4. MultiLineString
|
|
295
|
+
interface MultiLineString<P$1 extends Properties = {}> extends GeometryObjectA<P$1> {
|
|
296
|
+
type: "MultiLineString";
|
|
297
|
+
arcs: ArcIndexes[];
|
|
298
|
+
}
|
|
299
|
+
// 2.2.5. Polygon
|
|
300
|
+
interface Polygon<P$1 extends Properties = {}> extends GeometryObjectA<P$1> {
|
|
301
|
+
type: "Polygon";
|
|
302
|
+
arcs: ArcIndexes[];
|
|
303
|
+
}
|
|
304
|
+
// 2.2.6. MultiPolygon
|
|
305
|
+
interface MultiPolygon<P$1 extends Properties = {}> extends GeometryObjectA<P$1> {
|
|
306
|
+
type: "MultiPolygon";
|
|
307
|
+
arcs: ArcIndexes[][];
|
|
308
|
+
}
|
|
309
|
+
// 2.2.7. Geometry Collection
|
|
310
|
+
interface GeometryCollection<P$1 extends Properties = {}> extends GeometryObjectA<P$1> {
|
|
311
|
+
type: "GeometryCollection";
|
|
312
|
+
geometries: Array<GeometryObject<P$1>>;
|
|
313
|
+
}
|
|
314
|
+
// More
|
|
315
|
+
interface NullObject extends GeometryObjectA {
|
|
316
|
+
type: null;
|
|
317
|
+
}
|
|
318
|
+
//#endregion
|
|
319
|
+
//#region src/lib/map.d.ts
|
|
320
|
+
type MapData = FeatureCollection | Topology;
|
|
321
|
+
type DataTransformer = (features: MapFeature[]) => MapFeature[];
|
|
322
|
+
/**
|
|
323
|
+
* Configuration for a d3-geo projection.
|
|
324
|
+
*
|
|
325
|
+
* d3-maps applies these options (if provided) before fitting the geometry to the map size.
|
|
326
|
+
*/
|
|
327
|
+
interface ProjectionConfig {
|
|
328
|
+
center?: [number, number];
|
|
329
|
+
rotate?: [number, number, number];
|
|
330
|
+
scale?: number;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Input configuration for creating a map context.
|
|
334
|
+
*
|
|
335
|
+
* In adapters, this is usually passed as component props.
|
|
336
|
+
*/
|
|
337
|
+
interface MapConfig {
|
|
338
|
+
width?: number;
|
|
339
|
+
height?: number;
|
|
340
|
+
aspectRatio?: number;
|
|
341
|
+
/**
|
|
342
|
+
* Projection factory from d3-geo (or a compatible implementation).
|
|
343
|
+
*
|
|
344
|
+
* Example: `geoEqualEarth`.
|
|
345
|
+
*/
|
|
346
|
+
projection?: () => GeoProjection;
|
|
347
|
+
projectionConfig?: ProjectionConfig;
|
|
348
|
+
/**
|
|
349
|
+
* TopoJSON or GeoJSON input.
|
|
350
|
+
*
|
|
351
|
+
* TopoJSON is automatically converted to GeoJSON features.
|
|
352
|
+
*/
|
|
353
|
+
data: MapData;
|
|
354
|
+
/**
|
|
355
|
+
* Optional feature transformer (filter/augment/normalize features).
|
|
356
|
+
*/
|
|
357
|
+
dataTransformer?: DataTransformer;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Fully computed, framework-agnostic map context.
|
|
361
|
+
*
|
|
362
|
+
* Adapters provide this context to child layers (features, markers, custom SVG).
|
|
363
|
+
*/
|
|
364
|
+
interface MapContext {
|
|
365
|
+
width: number;
|
|
366
|
+
height: number;
|
|
367
|
+
projection?: GeoProjection;
|
|
368
|
+
features: MapFeature[];
|
|
369
|
+
path: GeoPath;
|
|
370
|
+
renderPath: (feature: Feature) => ReturnType<GeoPath>;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Creates a configured projection and fits it to the provided GeoJSON (if present).
|
|
374
|
+
*/
|
|
375
|
+
declare function makeProjection({
|
|
376
|
+
width,
|
|
377
|
+
height,
|
|
378
|
+
config,
|
|
379
|
+
projection,
|
|
380
|
+
geoJson
|
|
381
|
+
}: {
|
|
382
|
+
width: number;
|
|
383
|
+
height: number;
|
|
384
|
+
config?: ProjectionConfig;
|
|
385
|
+
projection: () => GeoProjection;
|
|
386
|
+
geoJson?: FeatureCollection;
|
|
387
|
+
}): GeoProjection;
|
|
388
|
+
/**
|
|
389
|
+
* Normalizes input map data to GeoJSON features.
|
|
390
|
+
*
|
|
391
|
+
* - TopoJSON is converted via `topojson-client`.
|
|
392
|
+
* - If provided, `dataTransformer` is applied to the feature array.
|
|
393
|
+
*/
|
|
394
|
+
declare function makeFeatures(geoData: MapData, dataTransformer?: DataTransformer): [features: MapFeature[], geoJson: FeatureCollection];
|
|
395
|
+
declare const makePathFn: (mapProjection: GeoProjection) => GeoPath;
|
|
396
|
+
/**
|
|
397
|
+
* Creates a full {@link MapContext} from a {@link MapConfig}.
|
|
398
|
+
*/
|
|
399
|
+
declare function makeMapContext({
|
|
400
|
+
width,
|
|
401
|
+
height: passedHeight,
|
|
402
|
+
aspectRatio,
|
|
403
|
+
data,
|
|
404
|
+
dataTransformer,
|
|
405
|
+
projection: providedProjection,
|
|
406
|
+
projectionConfig
|
|
407
|
+
}: MapConfig): MapContext;
|
|
408
|
+
/**
|
|
409
|
+
* Type guard for TopoJSON topology inputs.
|
|
410
|
+
*/
|
|
411
|
+
declare function isTopology(data: MapData): data is Topology;
|
|
412
|
+
//#endregion
|
|
413
|
+
//#region src/lib/marker.d.ts
|
|
414
|
+
type MapMarkerCoordinates = [number, number];
|
|
415
|
+
/**
|
|
416
|
+
* Shared props contract for marker layers.
|
|
417
|
+
*/
|
|
418
|
+
interface MapMarkerProps<TStyle = unknown> {
|
|
419
|
+
coordinates?: MapMarkerCoordinates;
|
|
420
|
+
styles?: MapObjectStyles<TStyle>;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Computes an SVG transform (`translate(x, y)`) for the given coordinates using the active projection.
|
|
424
|
+
*
|
|
425
|
+
* Coordinates must be `[longitude, latitude]`.
|
|
426
|
+
*/
|
|
427
|
+
declare function getMarkerTransform(context: MapContext | undefined, coordinates: MapMarkerCoordinates, fallback?: string): string;
|
|
428
|
+
//#endregion
|
|
429
|
+
//#region src/lib/utils.d.ts
|
|
430
|
+
declare function isString(value: unknown): value is string;
|
|
431
|
+
declare function isDefined<T>(value: T | null | undefined): value is T;
|
|
432
|
+
declare const isNullish: (value: unknown) => value is null | undefined;
|
|
433
|
+
declare const isNumber: (value: unknown) => value is number;
|
|
434
|
+
declare const isStringOrNumber: (value: unknown) => value is string | number;
|
|
435
|
+
declare function isFunction(value: unknown): value is (...args: unknown[]) => unknown;
|
|
436
|
+
declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
437
|
+
declare function get<T>(url: string): Promise<T>;
|
|
438
|
+
declare const makeTransform: (x: number, y: number, k?: number) => string;
|
|
439
|
+
//#endregion
|
|
440
|
+
//#region src/lib/zoom.d.ts
|
|
441
|
+
type Extent = [[number, number], [number, number]];
|
|
442
|
+
interface ZoomConfigOptions {
|
|
443
|
+
minZoom?: number;
|
|
444
|
+
maxZoom?: number;
|
|
445
|
+
translateExtent?: Extent;
|
|
446
|
+
}
|
|
447
|
+
interface ZoomConfig {
|
|
448
|
+
scaleExtent: [number, number];
|
|
449
|
+
translateExtent: Extent;
|
|
450
|
+
}
|
|
451
|
+
type ZoomBehaviorOwnMethodName<TElement extends Element, TDatum> = Exclude<keyof ZoomBehavior$1<TElement, TDatum>, keyof Function>;
|
|
452
|
+
type ZoomBehaviorMethodName<TElement extends Element, TDatum> = Extract<{ [K in ZoomBehaviorOwnMethodName<TElement, TDatum>]: ZoomBehavior$1<TElement, TDatum>[K] extends ((...args: unknown[]) => unknown) ? K : never }[ZoomBehaviorOwnMethodName<TElement, TDatum>], string>;
|
|
453
|
+
type ZoomBehaviorMethodArgs<TElement extends Element, TDatum, TMethod extends ZoomBehaviorMethodName<TElement, TDatum>> = ZoomBehavior$1<TElement, TDatum>[TMethod] extends ((...args: infer TArgs) => unknown) ? TArgs : never;
|
|
454
|
+
type ZoomBehaviorSingleArg<TElement extends Element, TDatum, TMethod extends ZoomBehaviorMethodName<TElement, TDatum>> = ZoomBehaviorMethodArgs<TElement, TDatum, TMethod> extends [infer TArg] ? TArg : never;
|
|
455
|
+
type ZoomModifierValue<TElement extends Element, TDatum, TMethod extends ZoomBehaviorMethodName<TElement, TDatum>> = ZoomBehaviorMethodArgs<TElement, TDatum, TMethod> | ZoomBehaviorSingleArg<TElement, TDatum, TMethod>;
|
|
456
|
+
type ZoomModifiers<TElement extends Element = SVGSVGElement, TDatum = unknown> = Partial<{ [K in ZoomBehaviorMethodName<TElement, TDatum>]: ZoomModifierValue<TElement, TDatum, K> }>;
|
|
457
|
+
interface ZoomProps<TElement extends Element = SVGSVGElement, TDatum = unknown> {
|
|
458
|
+
center?: [number, number];
|
|
459
|
+
zoom?: number;
|
|
460
|
+
minZoom?: number;
|
|
461
|
+
maxZoom?: number;
|
|
462
|
+
translateExtent?: Extent;
|
|
463
|
+
modifiers?: ZoomModifiers<TElement, TDatum>;
|
|
464
|
+
}
|
|
465
|
+
interface ZoomEvent extends D3ZoomEvent$1<SVGSVGElement, unknown> {}
|
|
466
|
+
interface ZoomEvents {
|
|
467
|
+
onZoomStart?: (event: ZoomEvent) => void;
|
|
468
|
+
onZoom?: (event: ZoomEvent) => void;
|
|
469
|
+
onZoomEnd?: (event: ZoomEvent) => void;
|
|
470
|
+
}
|
|
471
|
+
interface ZoomBehaviorOptions<TElement extends Element = SVGSVGElement, TDatum = unknown> extends ZoomProps<TElement, TDatum>, ZoomEvents {}
|
|
472
|
+
type ZoomScaleSource = number | ZoomTransform$1 | {
|
|
473
|
+
transform: ZoomTransform$1;
|
|
474
|
+
};
|
|
475
|
+
type ZoomTargetElement = SVGSVGElement | SVGGElement;
|
|
476
|
+
declare const ZOOM_DEFAULTS: {
|
|
477
|
+
center: [number, number];
|
|
478
|
+
zoom: number;
|
|
479
|
+
minZoom: number;
|
|
480
|
+
maxZoom: number;
|
|
481
|
+
extent: Extent;
|
|
482
|
+
};
|
|
483
|
+
interface ApplyZoomOptions {
|
|
484
|
+
element: ZoomTargetElement | null | undefined;
|
|
485
|
+
behavior: ZoomBehavior$1<SVGSVGElement, unknown>;
|
|
486
|
+
center?: [number, number];
|
|
487
|
+
zoom?: number;
|
|
488
|
+
}
|
|
489
|
+
interface SetupZoomOptions extends ApplyZoomOptions {}
|
|
490
|
+
declare function getDefaultTranslateExtent(context?: MapContext): Extent;
|
|
491
|
+
declare function createZoomTransform(center: [number, number], zoomLevel: number): ZoomTransform$1;
|
|
492
|
+
declare function createZoomConfig(options: ZoomConfigOptions): ZoomConfig;
|
|
493
|
+
declare function createZoomBehavior<TElement extends Element = SVGSVGElement, TDatum = unknown>(context?: MapContext, options?: ZoomBehaviorOptions<TElement, TDatum>): ZoomBehavior$1<TElement, TDatum>;
|
|
494
|
+
declare function attachZoomBehavior(element: ZoomTargetElement | null | undefined, behavior: ZoomBehavior$1<SVGSVGElement, unknown>): void;
|
|
495
|
+
declare function applyZoomBehaviorTransform(element: ZoomTargetElement | null | undefined, behavior: ZoomBehavior$1<SVGSVGElement, unknown>, transform: ZoomTransform$1): void;
|
|
496
|
+
declare function applyZoomTransform(options: ApplyZoomOptions): void;
|
|
497
|
+
declare function setupZoom(options: SetupZoomOptions): void;
|
|
498
|
+
declare function getZoomScale(source: ZoomScaleSource): number;
|
|
499
|
+
declare function getInverseZoomScale(source: ZoomScaleSource, fallback?: number): number;
|
|
500
|
+
//#endregion
|
|
501
|
+
export { ApplyZoomOptions, type D3ZoomEvent, DataTransformer, Extent, MapConfig, MapContext, MapData, MapFeature, MapFeatureProps, MapMarkerCoordinates, MapMarkerProps, MapObject, MapObjectEvent, MapObjectEventType, MapObjectFocusEventType, MapObjectMouseEventType, MapObjectState, MapObjectStyles, ProjectionConfig, SetupZoomOptions, ZOOM_DEFAULTS, type ZoomBehavior, ZoomBehaviorMethodArgs, ZoomBehaviorMethodName, ZoomBehaviorOptions, ZoomBehaviorOwnMethodName, ZoomBehaviorSingleArg, ZoomConfig, ZoomConfigOptions, ZoomEvent, ZoomEvents, ZoomModifierValue, ZoomModifiers, ZoomProps, ZoomScaleSource, ZoomTargetElement, type ZoomTransform, applyZoomBehaviorTransform, applyZoomTransform, attachZoomBehavior, createZoomBehavior, createZoomConfig, createZoomTransform, get, getDefaultTranslateExtent, getFeatureKey, getInverseZoomScale, getMarkerTransform, getObjectStateUpdate, getZoomScale, isDefined, isFunction, isNullish, isNumber, isPlainObject, isString, isStringOrNumber, isTopology, makeFeatures, makeMapContext, makePathFn, makeProjection, makeTransform, mapObjectState, resolveObjectStyle, setupZoom };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(e,t,n,r,i){function a(e){return typeof e==`string`}function o(e){return e!==`undefined`}let s=e=>e==null,c=e=>Number.isFinite(e),l=e=>a(e)||c(e);function u(e){return typeof e==`function`}function d(e){if(Object.prototype.toString.call(e)!==`[object Object]`)return!1;let t=Object.getPrototypeOf(e);return t===null||t===Object.prototype}async function f(e){return(await fetch(e)).json()}let p=(e,t,n)=>`translate(${e}, ${t}) scale(${n??1})`;function m(e,t=`id`,n){let r=e[t];if(l(r))return r;let i=e.properties?.[t];return l(i)?i:n}function h({width:e,height:t,config:n,projection:r,geoJson:i}){let a=r();if(n?.center){let[e,t]=n.center;c(e)&&c(t)&&a.center([e,t])}if(n?.rotate){let[e,t,r]=n.rotate;c(e)&&c(t)&&a.rotate([e,t,c(r)?r:0])}return n&&c(n.scale)&&a.scale(n.scale),i?a.fitSize([e,t],i):a.translate([e/2,t/2]),a}function g(e,t){let r;if(y(e)){let t=Object.keys(e.objects)[0];if(t){let i=e.objects[t],a=(0,n.feature)(e,i);r=a.type===`FeatureCollection`?a:{type:`FeatureCollection`,features:[a]}}else r={type:`FeatureCollection`,features:[]}}else r=e;return[t?t(r.features):r.features,r]}let _=e=>(0,t.geoPath)().projection(e);function v({width:e=600,height:n,aspectRatio:r=16/9,data:i,dataTransformer:a,projection:o=t.geoEqualEarth,projectionConfig:s}){let[c,l]=g(i,a),u=n||e/r,d=h({width:e,height:u,projection:o,config:s,geoJson:l}),f=_(d);return{width:e,height:u,projection:d,features:c,path:f,renderPath:e=>f(e)}}function y(e){return e?.type===`Topology`}let b=[`default`,`hover`,`active`];function x(e){switch(e){case`focus`:case`mouseenter`:case`mouseup`:return`hover`;case`blur`:case`mouseleave`:return`default`;case`mousedown`:return`active`;default:return`default`}}function S(e,t){return t?.[e]??t?.default}function C(e,t,n=`translate(0, 0)`){let r=e?.projection;if(!r)return n;let i=r(t);return i?p(...i):n}let w={center:[0,0],zoom:1,minZoom:1,maxZoom:8,extent:[[0,0],[0,0]]};function T(e){return[[0,0],[e?.width??0,e?.height??0]]}function E(e,t){return i.zoomIdentity.translate(...e).scale(t)}function D(e){return{scaleExtent:[e.minZoom??w.minZoom,e.maxZoom??w.maxZoom],translateExtent:e.translateExtent??w.extent}}function O(e,t={}){let n=(0,i.zoom)(),r=D({minZoom:t.minZoom,maxZoom:t.maxZoom,translateExtent:t.translateExtent??T(e)});return n.scaleExtent(r.scaleExtent).translateExtent(r.translateExtent),t.onZoomStart&&n.on(`start`,t.onZoomStart),t.onZoom&&n.on(`zoom`,t.onZoom),t.onZoomEnd&&n.on(`end`,t.onZoomEnd),I(n,t.modifiers),n}function k(e,t){let n=L(e);n&&(0,r.select)(n).call(t)}function A(e,t,n){let i=L(e);i&&(0,r.select)(i).call(t.transform,n)}function j(e){let t=e.center??w.center,n=e.zoom??w.zoom;A(e.element,e.behavior,E(t,n))}function M(e){k(e.element,e.behavior),j(e)}function N(e){return c(e)?e:F(e)?e.k:e?.transform?.k??1}function P(e,t=1){let n=N(e);return!c(n)||n===0?t:1/n}function F(e){return!!(e&&c(e.k)&&c(e.x)&&c(e.y))}function I(e,t){if(t)for(let[n,r]of Object.entries(t)){if(!n||r===void 0)continue;let t=e[n];if(typeof t!=`function`)continue;let i=Array.isArray(r)?r:[r];t.apply(e,i)}}function L(e){return e?e instanceof SVGSVGElement?e:e.closest(`svg`):null}e.ZOOM_DEFAULTS=w,e.applyZoomBehaviorTransform=A,e.applyZoomTransform=j,e.attachZoomBehavior=k,e.createZoomBehavior=O,e.createZoomConfig=D,e.createZoomTransform=E,e.get=f,e.getDefaultTranslateExtent=T,e.getFeatureKey=m,e.getInverseZoomScale=P,e.getMarkerTransform=C,e.getObjectStateUpdate=x,e.getZoomScale=N,e.isDefined=o,e.isFunction=u,e.isNullish=s,e.isNumber=c,e.isPlainObject=d,e.isString=a,e.isStringOrNumber=l,e.isTopology=y,e.makeFeatures=g,e.makeMapContext=v,e.makePathFn=_,e.makeProjection=h,e.makeTransform=p,e.mapObjectState=b,e.resolveObjectStyle=S,e.setupZoom=M})(this.D3Maps=this.D3Maps||{},d3,topojson,d3,d3);
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { geoEqualEarth, geoPath } from "d3-geo";
|
|
2
|
+
import { feature } from "topojson-client";
|
|
3
|
+
import { select } from "d3-selection";
|
|
4
|
+
import { zoom, zoomIdentity } from "d3-zoom";
|
|
5
|
+
|
|
6
|
+
//#region src/lib/utils.ts
|
|
7
|
+
function isString(value) {
|
|
8
|
+
return typeof value === "string";
|
|
9
|
+
}
|
|
10
|
+
function isDefined(value) {
|
|
11
|
+
return value !== "undefined";
|
|
12
|
+
}
|
|
13
|
+
const isNullish = (value) => value == null;
|
|
14
|
+
const isNumber = (value) => Number.isFinite(value);
|
|
15
|
+
const isStringOrNumber = (value) => isString(value) || isNumber(value);
|
|
16
|
+
function isFunction(value) {
|
|
17
|
+
return typeof value === "function";
|
|
18
|
+
}
|
|
19
|
+
function isPlainObject(value) {
|
|
20
|
+
if (Object.prototype.toString.call(value) !== "[object Object]") return false;
|
|
21
|
+
const prototype = Object.getPrototypeOf(value);
|
|
22
|
+
return prototype === null || prototype === Object.prototype;
|
|
23
|
+
}
|
|
24
|
+
async function get(url) {
|
|
25
|
+
return (await fetch(url)).json();
|
|
26
|
+
}
|
|
27
|
+
const makeTransform = (x, y, k) => `translate(${x}, ${y}) scale(${k ?? 1})`;
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/lib/feature.ts
|
|
31
|
+
/**
|
|
32
|
+
* Resolves a stable key for a feature.
|
|
33
|
+
*
|
|
34
|
+
* Checks:
|
|
35
|
+
* 1) `feature[idKey]`
|
|
36
|
+
* 2) `feature.properties[idKey]`
|
|
37
|
+
* 3) fallback to the list index
|
|
38
|
+
*/
|
|
39
|
+
function getFeatureKey(feature$1, idKey = "id", index) {
|
|
40
|
+
const directValue = feature$1[idKey];
|
|
41
|
+
if (isStringOrNumber(directValue)) return directValue;
|
|
42
|
+
const propertyValue = feature$1.properties?.[idKey];
|
|
43
|
+
if (isStringOrNumber(propertyValue)) return propertyValue;
|
|
44
|
+
return index;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/lib/map.ts
|
|
49
|
+
/**
|
|
50
|
+
* Creates a configured projection and fits it to the provided GeoJSON (if present).
|
|
51
|
+
*/
|
|
52
|
+
function makeProjection({ width, height, config, projection, geoJson }) {
|
|
53
|
+
const mapProjection = projection();
|
|
54
|
+
if (config?.center) {
|
|
55
|
+
const [cx, cy] = config.center;
|
|
56
|
+
if (isNumber(cx) && isNumber(cy)) mapProjection.center([cx, cy]);
|
|
57
|
+
}
|
|
58
|
+
if (config?.rotate) {
|
|
59
|
+
const [rx, ry, rz] = config.rotate;
|
|
60
|
+
if (isNumber(rx) && isNumber(ry)) mapProjection.rotate([
|
|
61
|
+
rx,
|
|
62
|
+
ry,
|
|
63
|
+
isNumber(rz) ? rz : 0
|
|
64
|
+
]);
|
|
65
|
+
}
|
|
66
|
+
if (config && isNumber(config.scale)) mapProjection.scale(config.scale);
|
|
67
|
+
if (geoJson) mapProjection.fitSize([width, height], geoJson);
|
|
68
|
+
else mapProjection.translate([width / 2, height / 2]);
|
|
69
|
+
return mapProjection;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Normalizes input map data to GeoJSON features.
|
|
73
|
+
*
|
|
74
|
+
* - TopoJSON is converted via `topojson-client`.
|
|
75
|
+
* - If provided, `dataTransformer` is applied to the feature array.
|
|
76
|
+
*/
|
|
77
|
+
function makeFeatures(geoData, dataTransformer) {
|
|
78
|
+
let geoJson;
|
|
79
|
+
if (isTopology(geoData)) {
|
|
80
|
+
const objectKey = Object.keys(geoData.objects)[0];
|
|
81
|
+
if (objectKey) {
|
|
82
|
+
const topoObject = geoData.objects[objectKey];
|
|
83
|
+
const normalizedGeoJson = feature(geoData, topoObject);
|
|
84
|
+
geoJson = normalizedGeoJson.type === "FeatureCollection" ? normalizedGeoJson : {
|
|
85
|
+
type: "FeatureCollection",
|
|
86
|
+
features: [normalizedGeoJson]
|
|
87
|
+
};
|
|
88
|
+
} else geoJson = {
|
|
89
|
+
type: "FeatureCollection",
|
|
90
|
+
features: []
|
|
91
|
+
};
|
|
92
|
+
} else geoJson = geoData;
|
|
93
|
+
return [dataTransformer ? dataTransformer(geoJson.features) : geoJson.features, geoJson];
|
|
94
|
+
}
|
|
95
|
+
const makePathFn = (mapProjection) => geoPath().projection(mapProjection);
|
|
96
|
+
/**
|
|
97
|
+
* Creates a full {@link MapContext} from a {@link MapConfig}.
|
|
98
|
+
*/
|
|
99
|
+
function makeMapContext({ width = 600, height: passedHeight, aspectRatio = 16 / 9, data, dataTransformer, projection: providedProjection = geoEqualEarth, projectionConfig }) {
|
|
100
|
+
const [features, geoJson] = makeFeatures(data, dataTransformer);
|
|
101
|
+
const height = passedHeight || width / aspectRatio;
|
|
102
|
+
const projection = makeProjection({
|
|
103
|
+
width,
|
|
104
|
+
height,
|
|
105
|
+
projection: providedProjection,
|
|
106
|
+
config: projectionConfig,
|
|
107
|
+
geoJson
|
|
108
|
+
});
|
|
109
|
+
const pathFn = makePathFn(projection);
|
|
110
|
+
return {
|
|
111
|
+
width,
|
|
112
|
+
height,
|
|
113
|
+
projection,
|
|
114
|
+
features,
|
|
115
|
+
path: pathFn,
|
|
116
|
+
renderPath: (feature$1) => pathFn(feature$1)
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Type guard for TopoJSON topology inputs.
|
|
121
|
+
*/
|
|
122
|
+
function isTopology(data) {
|
|
123
|
+
return data?.type === "Topology";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/lib/mapObject.ts
|
|
128
|
+
/**
|
|
129
|
+
* Supported interaction states for map objects.
|
|
130
|
+
*/
|
|
131
|
+
const mapObjectState = [
|
|
132
|
+
"default",
|
|
133
|
+
"hover",
|
|
134
|
+
"active"
|
|
135
|
+
];
|
|
136
|
+
/**
|
|
137
|
+
* Maps DOM event names to interaction state updates.
|
|
138
|
+
*/
|
|
139
|
+
function getObjectStateUpdate(event) {
|
|
140
|
+
switch (event) {
|
|
141
|
+
case "focus":
|
|
142
|
+
case "mouseenter":
|
|
143
|
+
case "mouseup": return "hover";
|
|
144
|
+
case "blur":
|
|
145
|
+
case "mouseleave": return "default";
|
|
146
|
+
case "mousedown": return "active";
|
|
147
|
+
default: return "default";
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Resolves a style value for the current state (falls back to `default`).
|
|
152
|
+
*/
|
|
153
|
+
function resolveObjectStyle(state, styles) {
|
|
154
|
+
return styles?.[state] ?? styles?.default;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/lib/marker.ts
|
|
159
|
+
/**
|
|
160
|
+
* Computes an SVG transform (`translate(x, y)`) for the given coordinates using the active projection.
|
|
161
|
+
*
|
|
162
|
+
* Coordinates must be `[longitude, latitude]`.
|
|
163
|
+
*/
|
|
164
|
+
function getMarkerTransform(context, coordinates, fallback = "translate(0, 0)") {
|
|
165
|
+
const projection = context?.projection;
|
|
166
|
+
if (!projection) return fallback;
|
|
167
|
+
const projected = projection(coordinates);
|
|
168
|
+
if (!projected) return fallback;
|
|
169
|
+
return makeTransform(...projected);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region src/lib/zoom.ts
|
|
174
|
+
const ZOOM_DEFAULTS = {
|
|
175
|
+
center: [0, 0],
|
|
176
|
+
zoom: 1,
|
|
177
|
+
minZoom: 1,
|
|
178
|
+
maxZoom: 8,
|
|
179
|
+
extent: [[0, 0], [0, 0]]
|
|
180
|
+
};
|
|
181
|
+
function getDefaultTranslateExtent(context) {
|
|
182
|
+
return [[0, 0], [context?.width ?? 0, context?.height ?? 0]];
|
|
183
|
+
}
|
|
184
|
+
function createZoomTransform(center, zoomLevel) {
|
|
185
|
+
return zoomIdentity.translate(...center).scale(zoomLevel);
|
|
186
|
+
}
|
|
187
|
+
function createZoomConfig(options) {
|
|
188
|
+
return {
|
|
189
|
+
scaleExtent: [options.minZoom ?? ZOOM_DEFAULTS.minZoom, options.maxZoom ?? ZOOM_DEFAULTS.maxZoom],
|
|
190
|
+
translateExtent: options.translateExtent ?? ZOOM_DEFAULTS.extent
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function createZoomBehavior(context, options = {}) {
|
|
194
|
+
const behavior = zoom();
|
|
195
|
+
const config = createZoomConfig({
|
|
196
|
+
minZoom: options.minZoom,
|
|
197
|
+
maxZoom: options.maxZoom,
|
|
198
|
+
translateExtent: options.translateExtent ?? getDefaultTranslateExtent(context)
|
|
199
|
+
});
|
|
200
|
+
behavior.scaleExtent(config.scaleExtent).translateExtent(config.translateExtent);
|
|
201
|
+
if (options.onZoomStart) behavior.on("start", options.onZoomStart);
|
|
202
|
+
if (options.onZoom) behavior.on("zoom", options.onZoom);
|
|
203
|
+
if (options.onZoomEnd) behavior.on("end", options.onZoomEnd);
|
|
204
|
+
applyZoomModifiers(behavior, options.modifiers);
|
|
205
|
+
return behavior;
|
|
206
|
+
}
|
|
207
|
+
function attachZoomBehavior(element, behavior) {
|
|
208
|
+
const svgElement = getSvgElement(element);
|
|
209
|
+
if (!svgElement) return;
|
|
210
|
+
select(svgElement).call(behavior);
|
|
211
|
+
}
|
|
212
|
+
function applyZoomBehaviorTransform(element, behavior, transform) {
|
|
213
|
+
const svgElement = getSvgElement(element);
|
|
214
|
+
if (!svgElement) return;
|
|
215
|
+
select(svgElement).call(behavior.transform, transform);
|
|
216
|
+
}
|
|
217
|
+
function applyZoomTransform(options) {
|
|
218
|
+
const center = options.center ?? ZOOM_DEFAULTS.center;
|
|
219
|
+
const zoom$1 = options.zoom ?? ZOOM_DEFAULTS.zoom;
|
|
220
|
+
applyZoomBehaviorTransform(options.element, options.behavior, createZoomTransform(center, zoom$1));
|
|
221
|
+
}
|
|
222
|
+
function setupZoom(options) {
|
|
223
|
+
attachZoomBehavior(options.element, options.behavior);
|
|
224
|
+
applyZoomTransform(options);
|
|
225
|
+
}
|
|
226
|
+
function getZoomScale(source) {
|
|
227
|
+
if (isNumber(source)) return source;
|
|
228
|
+
if (isZoomTransform(source)) return source.k;
|
|
229
|
+
return source?.transform?.k ?? 1;
|
|
230
|
+
}
|
|
231
|
+
function getInverseZoomScale(source, fallback = 1) {
|
|
232
|
+
const scale = getZoomScale(source);
|
|
233
|
+
if (!isNumber(scale) || scale === 0) return fallback;
|
|
234
|
+
return 1 / scale;
|
|
235
|
+
}
|
|
236
|
+
function isZoomTransform(value) {
|
|
237
|
+
return Boolean(value && isNumber(value.k) && isNumber(value.x) && isNumber(value.y));
|
|
238
|
+
}
|
|
239
|
+
function applyZoomModifiers(behavior, modifiers) {
|
|
240
|
+
if (!modifiers) return;
|
|
241
|
+
for (const [methodName, methodArgs] of Object.entries(modifiers)) {
|
|
242
|
+
if (!methodName || methodArgs === void 0) continue;
|
|
243
|
+
const modifier = behavior[methodName];
|
|
244
|
+
if (typeof modifier !== "function") continue;
|
|
245
|
+
const normalizedArgs = Array.isArray(methodArgs) ? methodArgs : [methodArgs];
|
|
246
|
+
modifier.apply(behavior, normalizedArgs);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function getSvgElement(element) {
|
|
250
|
+
if (!element) return null;
|
|
251
|
+
if (element instanceof SVGSVGElement) return element;
|
|
252
|
+
return element.closest("svg");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
//#endregion
|
|
256
|
+
export { ZOOM_DEFAULTS, applyZoomBehaviorTransform, applyZoomTransform, attachZoomBehavior, createZoomBehavior, createZoomConfig, createZoomTransform, get, getDefaultTranslateExtent, getFeatureKey, getInverseZoomScale, getMarkerTransform, getObjectStateUpdate, getZoomScale, isDefined, isFunction, isNullish, isNumber, isPlainObject, isString, isStringOrNumber, isTopology, makeFeatures, makeMapContext, makePathFn, makeProjection, makeTransform, mapObjectState, resolveObjectStyle, setupZoom };
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@d3-maps/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"private": false,
|
|
6
|
+
"description": "Framework-agnostic core utilities for building reactive D3 maps",
|
|
7
|
+
"author": "Georgii Bukharov <souljorje@gmail.com>",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/souljorje/d3-maps.git",
|
|
15
|
+
"directory": "packages/core"
|
|
16
|
+
},
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.js",
|
|
21
|
+
"browser": "./dist/index.iife.js"
|
|
22
|
+
},
|
|
23
|
+
"./*.css": {
|
|
24
|
+
"import": "./dist/*.css",
|
|
25
|
+
"browser": "./dist/*.css"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"style": "dist/index.css",
|
|
29
|
+
"main": "dist/index.js",
|
|
30
|
+
"module": "dist/index.js",
|
|
31
|
+
"browser": "dist/index.iife.js",
|
|
32
|
+
"types": "dist/index.d.ts",
|
|
33
|
+
"files": [
|
|
34
|
+
"dist/*"
|
|
35
|
+
],
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"d3-geo": "^3.1.1",
|
|
38
|
+
"d3-selection": "^3.0.0",
|
|
39
|
+
"d3-zoom": "^3.0.0",
|
|
40
|
+
"topojson-client": "^3.1.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/d3-geo": "^3.1.0",
|
|
44
|
+
"@types/d3-selection": "^3.0.11",
|
|
45
|
+
"@types/d3-zoom": "^3.0.8",
|
|
46
|
+
"@types/geojson": "^7946.0.16",
|
|
47
|
+
"@types/topojson-client": "^3.1.5",
|
|
48
|
+
"@types/topojson-specification": "^1.0.5",
|
|
49
|
+
"typescript": "^5.9.3",
|
|
50
|
+
"tsdown": "0.19.0",
|
|
51
|
+
"vitest": "^4.0.15"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"typecheck": "tsc --noEmit",
|
|
55
|
+
"build": "pnpm run typecheck && tsdown",
|
|
56
|
+
"dev": "tsdown --watch",
|
|
57
|
+
"test": "vitest run",
|
|
58
|
+
"test:watch": "vitest"
|
|
59
|
+
}
|
|
60
|
+
}
|