nuxt-charts 3.0.0 → 3.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.
Files changed (49) hide show
  1. package/README.md +53 -6
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +56 -3
  4. package/dist/runtime/assets/components.css +1 -1
  5. package/dist/runtime/assets/theme.css +1 -1
  6. package/dist/runtime/components/AreaChart.vue +1 -0
  7. package/dist/runtime/components/BarChart.vue +1 -0
  8. package/dist/runtime/components/DataChart.vue +91 -0
  9. package/dist/runtime/components/DataChart.vue.d.ts +27 -0
  10. package/dist/runtime/components/DottedMap.vue +261 -0
  11. package/dist/runtime/components/DottedMap.vue.d.ts +45 -0
  12. package/dist/runtime/components/LineChart.vue +1 -0
  13. package/dist/runtime/components/MetricChart.vue +322 -0
  14. package/dist/runtime/components/MetricChart.vue.d.ts +54 -0
  15. package/dist/runtime/components/TopoJSONMap.vue +273 -0
  16. package/dist/runtime/components/TopoJSONMap.vue.d.ts +45 -0
  17. package/dist/runtime/components/internal/CartesianFrame.vue +73 -4
  18. package/dist/runtime/components/internal/CartesianFrame.vue.d.ts +5 -0
  19. package/dist/runtime/components/internal/ChartAccessibility.vue.d.ts +1 -1
  20. package/dist/runtime/components/internal/CubeBarShape.vue.d.ts +1 -1
  21. package/dist/runtime/components/internal/MapCanvas.vue +297 -0
  22. package/dist/runtime/components/internal/MapCanvas.vue.d.ts +79 -0
  23. package/dist/runtime/components/internal/VariantBarShape.vue.d.ts +2 -2
  24. package/dist/runtime/data/world.json +182 -0
  25. package/dist/runtime/maps.d.ts +2 -0
  26. package/dist/runtime/maps.js +1 -0
  27. package/dist/runtime/spec/csv.d.ts +14 -0
  28. package/dist/runtime/spec/csv.js +13 -0
  29. package/dist/runtime/spec/index.d.ts +11 -0
  30. package/dist/runtime/spec/index.js +5 -0
  31. package/dist/runtime/spec/metric.d.ts +130 -0
  32. package/dist/runtime/spec/metric.js +63 -0
  33. package/dist/runtime/spec/resolve.d.ts +44 -0
  34. package/dist/runtime/spec/resolve.js +41 -0
  35. package/dist/runtime/spec/transform.d.ts +58 -0
  36. package/dist/runtime/spec/transform.js +199 -0
  37. package/dist/runtime/spec/types.d.ts +132 -0
  38. package/dist/runtime/spec/types.js +2 -0
  39. package/dist/runtime/types/charts.d.ts +131 -0
  40. package/dist/runtime/types/maps.d.ts +237 -0
  41. package/dist/runtime/types/maps.js +0 -0
  42. package/dist/runtime/types.d.ts +3 -0
  43. package/dist/runtime/utils/axis.d.ts +18 -0
  44. package/dist/runtime/utils/axis.js +44 -1
  45. package/dist/runtime/utils/dottedMap.d.ts +14 -0
  46. package/dist/runtime/utils/dottedMap.js +69 -0
  47. package/dist/runtime/utils/maps.d.ts +33 -0
  48. package/dist/runtime/utils/maps.js +77 -0
  49. package/package.json +23 -3
@@ -0,0 +1,69 @@
1
+ import { geoBounds, geoContains, geoPath } from "d3-geo";
2
+ import { createMapProjection, DEFAULT_MAP_REGION, inRegion, mapCountryId, mapFeatures, projectMapPoint, regionGeometry } from "./maps.js";
3
+ export function filterMapCountries(collection, countries) {
4
+ if (!countries?.length) return collection;
5
+ const selected = new Set(countries.map((code) => code.toUpperCase()));
6
+ return { type: "FeatureCollection", features: collection.features.filter((feature, index) => [mapCountryId(feature, index), feature.properties?.iso_a2, feature.properties?.iso_a3, feature.properties?.name].some((id) => selected.has(String(id).toUpperCase()))) };
7
+ }
8
+ export function generateMapDots(collection, projection, width, height, options) {
9
+ if (!projection.invert) throw new Error("DottedMap requires an invertible projection.");
10
+ const spacing = options.mapWidth ? width / Math.max(1, options.mapWidth) : height / Math.max(1, options.mapHeight ?? 60);
11
+ const rowHeight = options.grid === "diagonal" ? spacing * Math.sqrt(3) / 2 : spacing;
12
+ const path = geoPath(projection);
13
+ const countries = collection.features.map((feature, index) => ({ feature, id: mapCountryId(feature, index), bounds: geoBounds(feature) }));
14
+ const dots = [];
15
+ for (let row = 0, y = rowHeight / 2; y < height; row++, y += rowHeight) {
16
+ const offset = options.grid === "diagonal" && row % 2 ? spacing / 2 : 0;
17
+ for (let x = spacing / 2 + offset; x < width; x += spacing) {
18
+ const coordinates = projection.invert([x, y]);
19
+ if (!coordinates?.every(Number.isFinite) || !inRegion(coordinates, options.region)) continue;
20
+ const projected = projectMapPoint(projection, coordinates, path);
21
+ if (!projected || Math.hypot(projected[0] - x, projected[1] - y) > spacing / 2) continue;
22
+ const country = countries.find(({ bounds: [[west, south], [east, north]], feature }) => coordinates[1] >= south && coordinates[1] <= north && (west <= east ? coordinates[0] >= west && coordinates[0] <= east : coordinates[0] >= west || coordinates[0] <= east) && geoContains(feature, coordinates));
23
+ if (country) dots.push({ x, y, lng: coordinates[0], lat: coordinates[1], countryId: country.id, label: String(country.feature.properties?.name ?? country.id) });
24
+ }
25
+ }
26
+ return dots;
27
+ }
28
+ export function getMap(options = {}) {
29
+ const collection = filterMapCountries(mapFeatures(options.geoJson ?? options.geojsonWorld, options.mapFeatureKey), options.countries);
30
+ if (!collection.features.length) throw new Error("No map features matched. Check your country codes or geometry.");
31
+ const region = options.region ?? (!options.countries?.length && !options.geoJson && !options.geojsonWorld ? DEFAULT_MAP_REGION : void 0);
32
+ const geometry = region ? regionGeometry(region) : collection;
33
+ const preview = createMapProjection(options.projection ?? "mercator", geometry, 1e3, 500, { padding: 0 }, region);
34
+ const [[x0, y0], [x1, y1]] = geoPath(preview).bounds(geometry);
35
+ const ratio = (x1 - x0) / (y1 - y0);
36
+ const width = options.width ?? options.mapWidth ?? Math.max(1, Math.round((options.height ?? options.mapHeight ?? 60) * ratio));
37
+ const height = options.height ?? options.mapHeight ?? Math.max(1, Math.round(width / ratio));
38
+ const projection = createMapProjection(options.projection ?? "mercator", geometry, width, height, { padding: 0 }, region);
39
+ const dots = generateMapDots(collection, projection, width, height, { mapWidth: width, mapHeight: height, region, grid: options.grid });
40
+ return { width, height, points: Object.fromEntries(dots.map((dot) => [`${dot.x};${dot.y}`, dot])), region, countries: options.countries, grid: options.grid ?? "vertical", projection: options.projection ?? "mercator", scale: projection.scale(), translate: projection.translate() };
41
+ }
42
+ export function getPin(map, pin) {
43
+ if (map.X_MIN !== void 0 && map.Y_MAX !== void 0 && map.X_RANGE && map.Y_RANGE) {
44
+ const radians = Math.PI / 180;
45
+ const earthRadius = 6378137;
46
+ const rawX = map.width * (pin.lng * radians * earthRadius - map.X_MIN) / map.X_RANGE;
47
+ const rawY = map.height * (map.Y_MAX - Math.log(Math.tan(Math.PI / 4 + pin.lat * radians / 2)) * earthRadius) / map.Y_RANGE;
48
+ const step = map.ystep ?? (map.grid === "diagonal" ? Math.sqrt(3) / 2 : 1);
49
+ const row2 = Math.round(rawY / step);
50
+ const offset2 = map.grid === "diagonal" && row2 % 2 === 0 ? 0.5 : 0;
51
+ const x2 = Math.round(rawX - offset2) + offset2;
52
+ const y2 = row2 * step;
53
+ const lng = (x2 / map.width * map.X_RANGE + map.X_MIN) / earthRadius / radians;
54
+ const lat = (2 * Math.atan(Math.exp((map.Y_MAX - y2 / map.height * map.Y_RANGE) / earthRadius)) - Math.PI / 2) / radians;
55
+ return { ...pin, x: x2, y: y2, lat, lng };
56
+ }
57
+ const geometry = map.scale !== void 0 ? { type: "Sphere" } : map.region ? regionGeometry(map.region) : filterMapCountries(mapFeatures(), map.countries);
58
+ const projection = createMapProjection(map.projection ?? "mercator", geometry, map.width, map.height, { padding: 0 }, map.region);
59
+ if (map.scale !== void 0 && map.translate) projection.scale(map.scale).translate(map.translate);
60
+ const point = projectMapPoint(projection, [pin.lng, pin.lat]);
61
+ if (!point) throw new Error("Pin coordinates cannot be projected onto this map.");
62
+ const rowHeight = map.grid === "diagonal" ? Math.sqrt(3) / 2 : 1;
63
+ const row = Math.round((point[1] - rowHeight / 2) / rowHeight);
64
+ const offset = map.grid === "diagonal" && row % 2 ? 0.5 : 0;
65
+ const x = Math.round(point[0] - 0.5 - offset) + 0.5 + offset;
66
+ const y = row * rowHeight + rowHeight / 2;
67
+ const coordinates = projection.invert?.([x, y]);
68
+ return { ...pin, x, y, lat: coordinates?.[1] ?? pin.lat, lng: coordinates?.[0] ?? pin.lng };
69
+ }
@@ -0,0 +1,33 @@
1
+ import type { GeoProjection, GeoPermissibleObjects } from "d3-geo";
2
+ import type { Feature, FeatureCollection, MultiPolygon } from "geojson";
3
+ import type { MapAccessor, MapBaseProps, MapGeometry, MapProjection, MapRegion, MapRegionName } from "../types/maps.js";
4
+ import type { MapInteractionKind } from "../types/maps.js";
5
+ export interface MapMark {
6
+ key: string;
7
+ kind: MapInteractionKind;
8
+ values: Record<string, unknown>;
9
+ path?: string;
10
+ x?: number;
11
+ y?: number;
12
+ radius?: number;
13
+ polygon?: string;
14
+ color: string;
15
+ stroke?: string;
16
+ strokeWidth?: number;
17
+ strokeOpacity?: number;
18
+ dasharray?: string;
19
+ cursor?: string;
20
+ label?: string;
21
+ showLabel?: boolean;
22
+ }
23
+ export declare const MAP_REGIONS: Record<Exclude<MapRegionName, "world">, MapRegion>;
24
+ export declare const DEFAULT_MAP_REGION: MapRegion;
25
+ export declare function mapValue<T, V>(accessor: MapAccessor<T, V> | undefined, datum: T, index: number, fallback: V): V;
26
+ export declare function mapFeatures(source?: MapGeometry, objectName?: string): FeatureCollection;
27
+ export declare function regionGeometry(region: MapRegion): MultiPolygon;
28
+ export declare function inRegion(coordinates: [number, number], region?: MapRegion): boolean;
29
+ export declare function createMapProjection(option: MapProjection | undefined, geometry: GeoPermissibleObjects, width: number, height: number, props: Pick<MapBaseProps, "fitView" | "fitViewPadding" | "padding">, region?: MapRegion): GeoProjection;
30
+ /** Use the projection stream for point visibility: orthographic's back face must be clipped. */
31
+ export declare function projectMapPoint(projection: GeoProjection, coordinates: [number, number], path?: import("d3-geo").GeoPath<any, GeoPermissibleObjects>): [number, number] | undefined;
32
+ export declare function mapCountryId(feature: Feature, index: number): string;
33
+ export declare function containsMapPoint(collection: FeatureCollection, coordinates: [number, number]): boolean;
@@ -0,0 +1,77 @@
1
+ import { geoAlbersUsa, geoArea, geoContains, geoEqualEarth, geoEquirectangular, geoMercator, geoNaturalEarth1, geoOrthographic, geoPath } from "d3-geo";
2
+ import { feature } from "topojson-client";
3
+ import world from "../data/world.json";
4
+ export const MAP_REGIONS = {
5
+ usa: { lat: { min: 24, max: 50 }, lng: { min: -125, max: -66 } },
6
+ europe: { lat: { min: 34, max: 72 }, lng: { min: -25, max: 45 } },
7
+ asia: { lat: { min: -10, max: 55 }, lng: { min: 60, max: 150 } },
8
+ oceania: { lat: { min: -50, max: 0 }, lng: { min: 110, max: 180 } }
9
+ };
10
+ export const DEFAULT_MAP_REGION = { lat: { min: -60, max: 85 }, lng: { min: -180, max: 180 } };
11
+ export function mapValue(accessor, datum, index, fallback) {
12
+ return typeof accessor === "function" ? accessor(datum, index) : accessor ?? fallback;
13
+ }
14
+ function normalizeGeometry(geometry) {
15
+ if (!geometry) return null;
16
+ if (geometry.type === "GeometryCollection") return { ...geometry, geometries: geometry.geometries.map((g) => normalizeGeometry(g)) };
17
+ if (geometry.type !== "Polygon" && geometry.type !== "MultiPolygon") return geometry;
18
+ const orient = (rings) => rings.map((ring, index) => {
19
+ const large = geoArea({ type: "Polygon", coordinates: [ring] }) > 2 * Math.PI;
20
+ return large === (index === 0) ? [...ring].reverse() : ring;
21
+ });
22
+ return geometry.type === "Polygon" ? { ...geometry, coordinates: orient(geometry.coordinates) } : { ...geometry, coordinates: geometry.coordinates.map(orient) };
23
+ }
24
+ export function mapFeatures(source, objectName) {
25
+ const input = source ?? world;
26
+ let collection;
27
+ if (input.type === "Topology") {
28
+ const topology = input;
29
+ const name = objectName ?? (topology.objects.countries ? "countries" : topology.objects.land ? "land" : Object.keys(topology.objects)[0]);
30
+ const object = name ? topology.objects[name] : void 0;
31
+ if (!object) throw new Error(`TopoJSON object "${name ?? ""}" was not found. Set mapFeatureKey to an object in your topology.`);
32
+ const converted = feature(topology, object);
33
+ collection = converted.type === "FeatureCollection" ? converted : { type: "FeatureCollection", features: [converted] };
34
+ } else if (input.type === "FeatureCollection") collection = input;
35
+ else if (input.type === "Feature") collection = { type: "FeatureCollection", features: [input] };
36
+ else collection = { type: "FeatureCollection", features: [{ type: "Feature", properties: {}, geometry: input }] };
37
+ return { type: "FeatureCollection", features: collection.features.filter((f) => f.geometry).map((f) => ({ ...f, geometry: normalizeGeometry(f.geometry) })) };
38
+ }
39
+ export function regionGeometry(region) {
40
+ const polygons = [];
41
+ const end = region.lng.max < region.lng.min ? region.lng.max + 360 : region.lng.max;
42
+ for (let left = region.lng.min; left < end; left += 30) {
43
+ const right = Math.min(left + 30, end);
44
+ polygons.push([[[left, region.lat.min], [left, region.lat.max], [right, region.lat.max], [right, region.lat.min], [left, region.lat.min]]]);
45
+ }
46
+ return { type: "MultiPolygon", coordinates: polygons };
47
+ }
48
+ export function inRegion(coordinates, region) {
49
+ if (!region) return true;
50
+ const [lng, lat] = coordinates;
51
+ const longitude = region.lng.min <= region.lng.max ? lng >= region.lng.min && lng <= region.lng.max : lng >= region.lng.min || lng <= region.lng.max;
52
+ return longitude && lat >= region.lat.min && lat <= region.lat.max;
53
+ }
54
+ export function createMapProjection(option, geometry, width, height, props, region) {
55
+ const names = { mercator: geoMercator, equalEarth: geoEqualEarth, naturalEarth: geoNaturalEarth1, equirectangular: geoEquirectangular, orthographic: geoOrthographic, albersUsa: geoAlbersUsa };
56
+ const projection = typeof option === "string" ? names[option]() : typeof option === "function" ? "stream" in option ? option : option() : geoNaturalEarth1();
57
+ if (region && region.lng.min > region.lng.max && (option === void 0 || typeof option === "string")) projection.rotate([-(region.lng.min + region.lng.max + 360) / 2, 0]);
58
+ if (props.fitView !== false) {
59
+ const padding = props.padding;
60
+ const base = typeof padding === "number" ? padding : props.fitViewPadding ?? 12;
61
+ const sides = typeof padding === "object" ? padding : {};
62
+ projection.fitExtent([[sides.left ?? base, sides.top ?? base], [Math.max(base + 1, width - (sides.right ?? base)), Math.max(base + 1, height - (sides.bottom ?? base))]], geometry);
63
+ }
64
+ return projection;
65
+ }
66
+ export function projectMapPoint(projection, coordinates, path = geoPath(projection)) {
67
+ if (!coordinates.every(Number.isFinite) || Math.abs(coordinates[1]) > 90) return;
68
+ const point = projection(coordinates);
69
+ if (!point?.every(Number.isFinite) || !path({ type: "Point", coordinates })) return;
70
+ return point;
71
+ }
72
+ export function mapCountryId(feature2, index) {
73
+ return String(feature2.id ?? feature2.properties?.iso_a3 ?? feature2.properties?.ISO_A3 ?? feature2.properties?.name ?? index);
74
+ }
75
+ export function containsMapPoint(collection, coordinates) {
76
+ return collection.features.some((f) => geoContains(f, coordinates));
77
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-charts",
3
- "version": "3.0.0",
4
- "description": "Beautiful chart components for Nuxt (vccs engine) — Area, Bar, Line, Donut, Bubble, Radar, Radial Bar, Funnel, Sankey, Candlestick, Status Tracker",
3
+ "version": "3.1.0",
4
+ "description": "Beautiful chart and D3 map components for Nuxt — Area, Bar, Line, Donut, Bubble, Radar, Radial Bar, Funnel, Sankey, Candlestick, Status Tracker, Dotted Map, TopoJSON Map",
5
5
  "license": "MIT",
6
6
  "author": "Dennis Adriaansen <adriaansendennis@gmail.com>",
7
7
  "homepage": "https://nuxtcharts.com",
@@ -39,6 +39,16 @@
39
39
  "types": "./dist/runtime/enums.d.ts",
40
40
  "import": "./dist/runtime/enums.js",
41
41
  "default": "./dist/runtime/enums.js"
42
+ },
43
+ "./spec": {
44
+ "types": "./dist/runtime/spec/index.d.ts",
45
+ "import": "./dist/runtime/spec/index.js",
46
+ "default": "./dist/runtime/spec/index.js"
47
+ },
48
+ "./maps": {
49
+ "types": "./dist/runtime/maps.d.ts",
50
+ "import": "./dist/runtime/maps.js",
51
+ "default": "./dist/runtime/maps.js"
42
52
  }
43
53
  },
44
54
  "main": "./dist/module.mjs",
@@ -47,7 +57,14 @@
47
57
  "dist"
48
58
  ],
49
59
  "dependencies": {
60
+ "@types/d3-geo": "^3.1.0",
61
+ "@types/geojson": "^7946.0.16",
62
+ "@types/topojson-specification": "^1.0.5",
63
+ "d3-geo": "^3.1.1",
64
+ "d3-selection": "^3.0.0",
65
+ "d3-zoom": "^3.0.0",
50
66
  "motion-v": "^2.3.0",
67
+ "topojson-client": "^3.1.0",
51
68
  "vccs": "^0.5.0"
52
69
  },
53
70
  "devDependencies": {
@@ -59,7 +76,10 @@
59
76
  "@nuxt/schema": "^4.3.0",
60
77
  "@nuxt/test-utils": "^4.2.0",
61
78
  "@tailwindcss/vite": "^4.2.2",
79
+ "@types/d3-selection": "^3.0.11",
80
+ "@types/d3-zoom": "^3.0.8",
62
81
  "@types/node": "latest",
82
+ "@types/topojson-client": "^3.1.5",
63
83
  "@vitejs/plugin-vue": "^6.0.8",
64
84
  "@vue/test-utils": "^2.4.11",
65
85
  "eslint": "^9.39.2",
@@ -73,7 +93,7 @@
73
93
  },
74
94
  "publishConfig": {
75
95
  "access": "public",
76
- "tag": "beta"
96
+ "tag": "next"
77
97
  },
78
98
  "scripts": {
79
99
  "dev": "nuxi dev playground",