@rowsncolumns/charts 27.0.30
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/LICENSE.md +100 -0
- package/Readme.md +81 -0
- package/dist/cjs/atoms/editing-chart.atom.d.ts +13 -0
- package/dist/cjs/atoms/editing-chart.atom.d.ts.map +1 -0
- package/dist/cjs/atoms/editing-chart.atom.js +26 -0
- package/dist/cjs/basic-chart/basic-chart.d.ts +5 -0
- package/dist/cjs/basic-chart/basic-chart.d.ts.map +1 -0
- package/dist/cjs/basic-chart/basic-chart.js +2250 -0
- package/dist/cjs/basic-chart/helpers.d.ts +12 -0
- package/dist/cjs/basic-chart/helpers.d.ts.map +1 -0
- package/dist/cjs/basic-chart/helpers.js +105 -0
- package/dist/cjs/basic-chart/index.d.ts +5 -0
- package/dist/cjs/basic-chart/index.d.ts.map +1 -0
- package/dist/cjs/basic-chart/index.js +12 -0
- package/dist/cjs/components/dialogs/chart-editor-dialog.d.ts +5 -0
- package/dist/cjs/components/dialogs/chart-editor-dialog.d.ts.map +1 -0
- package/dist/cjs/components/dialogs/chart-editor-dialog.js +25 -0
- package/dist/cjs/components/editors/chart-editor.d.ts +10 -0
- package/dist/cjs/components/editors/chart-editor.d.ts.map +1 -0
- package/dist/cjs/components/editors/chart-editor.js +631 -0
- package/dist/cjs/components/editors/chart-range-input.d.ts +17 -0
- package/dist/cjs/components/editors/chart-range-input.d.ts.map +1 -0
- package/dist/cjs/components/editors/chart-range-input.js +16 -0
- package/dist/cjs/config.d.ts +19 -0
- package/dist/cjs/config.d.ts.map +1 -0
- package/dist/cjs/config.js +196 -0
- package/dist/cjs/hooks/use-chart-editor.d.ts +8 -0
- package/dist/cjs/hooks/use-chart-editor.d.ts.map +1 -0
- package/dist/cjs/hooks/use-chart-editor.js +29 -0
- package/dist/cjs/hooks/use-charts.d.ts +35 -0
- package/dist/cjs/hooks/use-charts.d.ts.map +1 -0
- package/dist/cjs/hooks/use-charts.js +67 -0
- package/dist/cjs/hooks/use-on-create-chart.d.ts +13 -0
- package/dist/cjs/hooks/use-on-create-chart.d.ts.map +1 -0
- package/dist/cjs/hooks/use-on-create-chart.js +24 -0
- package/dist/cjs/hooks/use-on-delete-chart.d.ts +8 -0
- package/dist/cjs/hooks/use-on-delete-chart.d.ts.map +1 -0
- package/dist/cjs/hooks/use-on-delete-chart.js +19 -0
- package/dist/cjs/hooks/use-on-move-chart.d.ts +8 -0
- package/dist/cjs/hooks/use-on-move-chart.d.ts.map +1 -0
- package/dist/cjs/hooks/use-on-move-chart.js +15 -0
- package/dist/cjs/hooks/use-on-resize-chart.d.ts +8 -0
- package/dist/cjs/hooks/use-on-resize-chart.d.ts.map +1 -0
- package/dist/cjs/hooks/use-on-resize-chart.js +15 -0
- package/dist/cjs/hooks/use-on-update-chart.d.ts +9 -0
- package/dist/cjs/hooks/use-on-update-chart.d.ts.map +1 -0
- package/dist/cjs/hooks/use-on-update-chart.js +15 -0
- package/dist/cjs/index.d.ts +11 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +18 -0
- package/dist/cjs/interface/create-chart.d.ts +9 -0
- package/dist/cjs/interface/create-chart.d.ts.map +1 -0
- package/dist/cjs/interface/create-chart.js +110 -0
- package/dist/cjs/interface/delete-chart.d.ts +4 -0
- package/dist/cjs/interface/delete-chart.d.ts.map +1 -0
- package/dist/cjs/interface/delete-chart.js +26 -0
- package/dist/cjs/interface/move-chart.d.ts +5 -0
- package/dist/cjs/interface/move-chart.d.ts.map +1 -0
- package/dist/cjs/interface/move-chart.js +28 -0
- package/dist/cjs/interface/resize-chart.d.ts +5 -0
- package/dist/cjs/interface/resize-chart.d.ts.map +1 -0
- package/dist/cjs/interface/resize-chart.js +30 -0
- package/dist/cjs/interface/update-chart.d.ts +4 -0
- package/dist/cjs/interface/update-chart.d.ts.map +1 -0
- package/dist/cjs/interface/update-chart.js +29 -0
- package/dist/cjs/plotly/basic-chart/basic-chart.d.ts +5 -0
- package/dist/cjs/plotly/basic-chart/basic-chart.d.ts.map +1 -0
- package/dist/cjs/plotly/basic-chart/basic-chart.js +1586 -0
- package/dist/cjs/plotly/basic-chart/helpers.d.ts +20 -0
- package/dist/cjs/plotly/basic-chart/helpers.d.ts.map +1 -0
- package/dist/cjs/plotly/basic-chart/helpers.js +114 -0
- package/dist/cjs/plotly/basic-chart/index.d.ts +5 -0
- package/dist/cjs/plotly/basic-chart/index.d.ts.map +1 -0
- package/dist/cjs/plotly/basic-chart/index.js +12 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/cjs/types.d.ts +27 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +2 -0
- package/dist/cjs/utils/aggregation.d.ts +6 -0
- package/dist/cjs/utils/aggregation.d.ts.map +1 -0
- package/dist/cjs/utils/aggregation.js +127 -0
- package/dist/cjs/utils/bar-layout.d.ts +57 -0
- package/dist/cjs/utils/bar-layout.d.ts.map +1 -0
- package/dist/cjs/utils/bar-layout.js +81 -0
- package/dist/cjs/utils/boxplot.d.ts +62 -0
- package/dist/cjs/utils/boxplot.d.ts.map +1 -0
- package/dist/cjs/utils/boxplot.js +105 -0
- package/dist/cjs/utils/bubble.d.ts +52 -0
- package/dist/cjs/utils/bubble.d.ts.map +1 -0
- package/dist/cjs/utils/bubble.js +139 -0
- package/dist/cjs/utils/chart-spec-utils.d.ts +67 -0
- package/dist/cjs/utils/chart-spec-utils.d.ts.map +1 -0
- package/dist/cjs/utils/chart-spec-utils.js +355 -0
- package/dist/cjs/utils/echarts-gl-loader.d.ts +11 -0
- package/dist/cjs/utils/echarts-gl-loader.d.ts.map +1 -0
- package/dist/cjs/utils/echarts-gl-loader.js +59 -0
- package/dist/cjs/utils/error-bars.d.ts +58 -0
- package/dist/cjs/utils/error-bars.d.ts.map +1 -0
- package/dist/cjs/utils/error-bars.js +154 -0
- package/dist/cjs/utils/gradient.d.ts +52 -0
- package/dist/cjs/utils/gradient.d.ts.map +1 -0
- package/dist/cjs/utils/gradient.js +93 -0
- package/dist/cjs/utils/hierarchy.d.ts +28 -0
- package/dist/cjs/utils/hierarchy.d.ts.map +1 -0
- package/dist/cjs/utils/hierarchy.js +186 -0
- package/dist/cjs/utils/histogram.d.ts +46 -0
- package/dist/cjs/utils/histogram.d.ts.map +1 -0
- package/dist/cjs/utils/histogram.js +125 -0
- package/dist/cjs/utils/of-pie.d.ts +101 -0
- package/dist/cjs/utils/of-pie.d.ts.map +1 -0
- package/dist/cjs/utils/of-pie.js +159 -0
- package/dist/cjs/utils/overrides.d.ts +12 -0
- package/dist/cjs/utils/overrides.d.ts.map +1 -0
- package/dist/cjs/utils/overrides.js +171 -0
- package/dist/cjs/utils/radar.d.ts +37 -0
- package/dist/cjs/utils/radar.d.ts.map +1 -0
- package/dist/cjs/utils/radar.js +70 -0
- package/dist/cjs/utils/region-map-loader.d.ts +22 -0
- package/dist/cjs/utils/region-map-loader.d.ts.map +1 -0
- package/dist/cjs/utils/region-map-loader.js +174 -0
- package/dist/cjs/utils/region-map.d.ts +100 -0
- package/dist/cjs/utils/region-map.d.ts.map +1 -0
- package/dist/cjs/utils/region-map.js +327 -0
- package/dist/cjs/utils/stock.d.ts +64 -0
- package/dist/cjs/utils/stock.d.ts.map +1 -0
- package/dist/cjs/utils/stock.js +153 -0
- package/dist/cjs/utils/three-d.d.ts +96 -0
- package/dist/cjs/utils/three-d.d.ts.map +1 -0
- package/dist/cjs/utils/three-d.js +191 -0
- package/dist/cjs/utils/trendline.d.ts +41 -0
- package/dist/cjs/utils/trendline.d.ts.map +1 -0
- package/dist/cjs/utils/trendline.js +303 -0
- package/dist/cjs/utils/waterfall.d.ts +24 -0
- package/dist/cjs/utils/waterfall.d.ts.map +1 -0
- package/dist/cjs/utils/waterfall.js +61 -0
- package/dist/cjs/utils/webgl.d.ts +12 -0
- package/dist/cjs/utils/webgl.d.ts.map +1 -0
- package/dist/cjs/utils/webgl.js +36 -0
- package/dist/cjs/vitest.config.d.ts +3 -0
- package/dist/cjs/vitest.config.d.ts.map +1 -0
- package/dist/cjs/vitest.config.js +9 -0
- package/dist/esm/atoms/editing-chart.atom.d.ts +13 -0
- package/dist/esm/atoms/editing-chart.atom.d.ts.map +1 -0
- package/dist/esm/atoms/editing-chart.atom.js +12 -0
- package/dist/esm/basic-chart/basic-chart.d.ts +5 -0
- package/dist/esm/basic-chart/basic-chart.d.ts.map +1 -0
- package/dist/esm/basic-chart/basic-chart.js +2343 -0
- package/dist/esm/basic-chart/helpers.d.ts +12 -0
- package/dist/esm/basic-chart/helpers.d.ts.map +1 -0
- package/dist/esm/basic-chart/helpers.js +98 -0
- package/dist/esm/basic-chart/index.d.ts +5 -0
- package/dist/esm/basic-chart/index.d.ts.map +1 -0
- package/dist/esm/basic-chart/index.js +7 -0
- package/dist/esm/components/dialogs/chart-editor-dialog.d.ts +5 -0
- package/dist/esm/components/dialogs/chart-editor-dialog.d.ts.map +1 -0
- package/dist/esm/components/dialogs/chart-editor-dialog.js +19 -0
- package/dist/esm/components/editors/chart-editor.d.ts +10 -0
- package/dist/esm/components/editors/chart-editor.d.ts.map +1 -0
- package/dist/esm/components/editors/chart-editor.js +550 -0
- package/dist/esm/components/editors/chart-range-input.d.ts +17 -0
- package/dist/esm/components/editors/chart-range-input.d.ts.map +1 -0
- package/dist/esm/components/editors/chart-range-input.js +10 -0
- package/dist/esm/config.d.ts +19 -0
- package/dist/esm/config.d.ts.map +1 -0
- package/dist/esm/config.js +161 -0
- package/dist/esm/hooks/use-chart-editor.d.ts +8 -0
- package/dist/esm/hooks/use-chart-editor.d.ts.map +1 -0
- package/dist/esm/hooks/use-chart-editor.js +32 -0
- package/dist/esm/hooks/use-charts.d.ts +35 -0
- package/dist/esm/hooks/use-charts.d.ts.map +1 -0
- package/dist/esm/hooks/use-charts.js +62 -0
- package/dist/esm/hooks/use-on-create-chart.d.ts +13 -0
- package/dist/esm/hooks/use-on-create-chart.d.ts.map +1 -0
- package/dist/esm/hooks/use-on-create-chart.js +19 -0
- package/dist/esm/hooks/use-on-delete-chart.d.ts +8 -0
- package/dist/esm/hooks/use-on-delete-chart.d.ts.map +1 -0
- package/dist/esm/hooks/use-on-delete-chart.js +14 -0
- package/dist/esm/hooks/use-on-move-chart.d.ts +8 -0
- package/dist/esm/hooks/use-on-move-chart.d.ts.map +1 -0
- package/dist/esm/hooks/use-on-move-chart.js +10 -0
- package/dist/esm/hooks/use-on-resize-chart.d.ts +8 -0
- package/dist/esm/hooks/use-on-resize-chart.d.ts.map +1 -0
- package/dist/esm/hooks/use-on-resize-chart.js +10 -0
- package/dist/esm/hooks/use-on-update-chart.d.ts +9 -0
- package/dist/esm/hooks/use-on-update-chart.d.ts.map +1 -0
- package/dist/esm/hooks/use-on-update-chart.js +10 -0
- package/dist/esm/index.d.ts +11 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +13 -0
- package/dist/esm/interface/create-chart.d.ts +9 -0
- package/dist/esm/interface/create-chart.d.ts.map +1 -0
- package/dist/esm/interface/create-chart.js +126 -0
- package/dist/esm/interface/delete-chart.d.ts +4 -0
- package/dist/esm/interface/delete-chart.d.ts.map +1 -0
- package/dist/esm/interface/delete-chart.js +20 -0
- package/dist/esm/interface/move-chart.d.ts +5 -0
- package/dist/esm/interface/move-chart.d.ts.map +1 -0
- package/dist/esm/interface/move-chart.js +22 -0
- package/dist/esm/interface/resize-chart.d.ts +5 -0
- package/dist/esm/interface/resize-chart.d.ts.map +1 -0
- package/dist/esm/interface/resize-chart.js +24 -0
- package/dist/esm/interface/update-chart.d.ts +4 -0
- package/dist/esm/interface/update-chart.d.ts.map +1 -0
- package/dist/esm/interface/update-chart.js +26 -0
- package/dist/esm/plotly/basic-chart/basic-chart.d.ts +5 -0
- package/dist/esm/plotly/basic-chart/basic-chart.d.ts.map +1 -0
- package/dist/esm/plotly/basic-chart/basic-chart.js +1682 -0
- package/dist/esm/plotly/basic-chart/helpers.d.ts +20 -0
- package/dist/esm/plotly/basic-chart/helpers.d.ts.map +1 -0
- package/dist/esm/plotly/basic-chart/helpers.js +107 -0
- package/dist/esm/plotly/basic-chart/index.d.ts +5 -0
- package/dist/esm/plotly/basic-chart/index.d.ts.map +1 -0
- package/dist/esm/plotly/basic-chart/index.js +7 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -0
- package/dist/esm/types.d.ts +27 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/utils/aggregation.d.ts +6 -0
- package/dist/esm/utils/aggregation.d.ts.map +1 -0
- package/dist/esm/utils/aggregation.js +96 -0
- package/dist/esm/utils/bar-layout.d.ts +57 -0
- package/dist/esm/utils/bar-layout.d.ts.map +1 -0
- package/dist/esm/utils/bar-layout.js +70 -0
- package/dist/esm/utils/boxplot.d.ts +62 -0
- package/dist/esm/utils/boxplot.d.ts.map +1 -0
- package/dist/esm/utils/boxplot.js +91 -0
- package/dist/esm/utils/bubble.d.ts +52 -0
- package/dist/esm/utils/bubble.d.ts.map +1 -0
- package/dist/esm/utils/bubble.js +122 -0
- package/dist/esm/utils/chart-spec-utils.d.ts +67 -0
- package/dist/esm/utils/chart-spec-utils.d.ts.map +1 -0
- package/dist/esm/utils/chart-spec-utils.js +270 -0
- package/dist/esm/utils/echarts-gl-loader.d.ts +11 -0
- package/dist/esm/utils/echarts-gl-loader.d.ts.map +1 -0
- package/dist/esm/utils/echarts-gl-loader.js +51 -0
- package/dist/esm/utils/error-bars.d.ts +58 -0
- package/dist/esm/utils/error-bars.d.ts.map +1 -0
- package/dist/esm/utils/error-bars.js +134 -0
- package/dist/esm/utils/gradient.d.ts +52 -0
- package/dist/esm/utils/gradient.d.ts.map +1 -0
- package/dist/esm/utils/gradient.js +84 -0
- package/dist/esm/utils/hierarchy.d.ts +28 -0
- package/dist/esm/utils/hierarchy.d.ts.map +1 -0
- package/dist/esm/utils/hierarchy.js +150 -0
- package/dist/esm/utils/histogram.d.ts +46 -0
- package/dist/esm/utils/histogram.d.ts.map +1 -0
- package/dist/esm/utils/histogram.js +95 -0
- package/dist/esm/utils/of-pie.d.ts +101 -0
- package/dist/esm/utils/of-pie.d.ts.map +1 -0
- package/dist/esm/utils/of-pie.js +141 -0
- package/dist/esm/utils/overrides.d.ts +12 -0
- package/dist/esm/utils/overrides.d.ts.map +1 -0
- package/dist/esm/utils/overrides.js +159 -0
- package/dist/esm/utils/radar.d.ts +37 -0
- package/dist/esm/utils/radar.d.ts.map +1 -0
- package/dist/esm/utils/radar.js +49 -0
- package/dist/esm/utils/region-map-loader.d.ts +22 -0
- package/dist/esm/utils/region-map-loader.d.ts.map +1 -0
- package/dist/esm/utils/region-map-loader.js +141 -0
- package/dist/esm/utils/region-map.d.ts +100 -0
- package/dist/esm/utils/region-map.d.ts.map +1 -0
- package/dist/esm/utils/region-map.js +266 -0
- package/dist/esm/utils/stock.d.ts +64 -0
- package/dist/esm/utils/stock.d.ts.map +1 -0
- package/dist/esm/utils/stock.js +123 -0
- package/dist/esm/utils/three-d.d.ts +96 -0
- package/dist/esm/utils/three-d.d.ts.map +1 -0
- package/dist/esm/utils/three-d.js +162 -0
- package/dist/esm/utils/trendline.d.ts +41 -0
- package/dist/esm/utils/trendline.d.ts.map +1 -0
- package/dist/esm/utils/trendline.js +257 -0
- package/dist/esm/utils/waterfall.d.ts +24 -0
- package/dist/esm/utils/waterfall.d.ts.map +1 -0
- package/dist/esm/utils/waterfall.js +55 -0
- package/dist/esm/utils/webgl.d.ts +12 -0
- package/dist/esm/utils/webgl.d.ts.map +1 -0
- package/dist/esm/utils/webgl.js +30 -0
- package/dist/esm/vitest.config.d.ts +3 -0
- package/dist/esm/vitest.config.d.ts.map +1 -0
- package/dist/esm/vitest.config.js +7 -0
- package/package.json +96 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazy loader + registry for region-map (choropleth) geo data.
|
|
3
|
+
*
|
|
4
|
+
* echarts ships no map geometry, and the world GeoJSON plus the map
|
|
5
|
+
* chart module are dead weight for every non-map chart — so nothing
|
|
6
|
+
* here is imported eagerly. The renderer calls `loadRegionMapGeo(scope)`
|
|
7
|
+
* the first time a region-map spec is actually about to render:
|
|
8
|
+
*
|
|
9
|
+
* - the echarts `MapChart` / `GeoComponent` / `VisualMapComponent`
|
|
10
|
+
* modules are dynamically imported and registered once
|
|
11
|
+
* - the default `"world"` scope is built by dynamically importing the
|
|
12
|
+
* `world-atlas` countries TopoJSON (~110 KB) and converting it with
|
|
13
|
+
* `topojson-client`
|
|
14
|
+
* - any other scope must have been provided by the host through
|
|
15
|
+
* `registerChartGeoMap(name, geoJson)` (e.g. a US-states GeoJSON)
|
|
16
|
+
*
|
|
17
|
+
* Failures latch (mirroring `echarts-gl-loader.ts`): a failed import
|
|
18
|
+
* warns once and keeps resolving null, so the renderer draws an empty
|
|
19
|
+
* chart instead of crashing or re-fetching on every update. Everything
|
|
20
|
+
* is SSR-safe — no window/document access.
|
|
21
|
+
*/
|
|
22
|
+
import * as echarts from "echarts/core";
|
|
23
|
+
import { WORLD_GEO_SCOPE } from "./region-map";
|
|
24
|
+
const registeredMaps = new Map();
|
|
25
|
+
let modulesPromise = null;
|
|
26
|
+
let worldPromise = null;
|
|
27
|
+
const warnedScopes = new Set();
|
|
28
|
+
const extractRegionNames = (geoJson) => {
|
|
29
|
+
const features = geoJson?.features;
|
|
30
|
+
if (!Array.isArray(features))
|
|
31
|
+
return [];
|
|
32
|
+
const names = [];
|
|
33
|
+
for (const feature of features) {
|
|
34
|
+
const name = feature?.properties?.name;
|
|
35
|
+
if (typeof name === "string" && name.length > 0) {
|
|
36
|
+
names.push(name);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return names;
|
|
40
|
+
};
|
|
41
|
+
const warnOnce = (scope, message) => {
|
|
42
|
+
if (warnedScopes.has(scope))
|
|
43
|
+
return;
|
|
44
|
+
warnedScopes.add(scope);
|
|
45
|
+
// eslint-disable-next-line no-console
|
|
46
|
+
console.warn(message);
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Register the echarts modules the choropleth needs (map series, geo
|
|
50
|
+
* coordinate system, visualMap). Dynamically imported so non-map
|
|
51
|
+
* charts never pay for them; a failed import latches as unavailable.
|
|
52
|
+
*/
|
|
53
|
+
const ensureEchartsMapModules = () => {
|
|
54
|
+
if (!modulesPromise) {
|
|
55
|
+
modulesPromise = Promise.all([
|
|
56
|
+
import("echarts/charts"),
|
|
57
|
+
import("echarts/components"),
|
|
58
|
+
])
|
|
59
|
+
.then(([charts, components]) => {
|
|
60
|
+
echarts.use([
|
|
61
|
+
charts.MapChart,
|
|
62
|
+
components.GeoComponent,
|
|
63
|
+
components.VisualMapComponent,
|
|
64
|
+
]);
|
|
65
|
+
return true;
|
|
66
|
+
})
|
|
67
|
+
.catch(() => false);
|
|
68
|
+
}
|
|
69
|
+
return modulesPromise;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Host-provided geo data: register `geoJson` under `name` so region-map
|
|
73
|
+
* specs with `geoScope: name` can render on it (e.g. a US-states map).
|
|
74
|
+
* Region matching targets each feature's `properties.name`. The actual
|
|
75
|
+
* `echarts.registerMap` call is deferred until the map modules have
|
|
76
|
+
* loaded, so this is safe to call at module scope.
|
|
77
|
+
*/
|
|
78
|
+
export const registerChartGeoMap = (name, geoJson) => {
|
|
79
|
+
registeredMaps.set(name, {
|
|
80
|
+
geoJson,
|
|
81
|
+
regionNames: extractRegionNames(geoJson),
|
|
82
|
+
applied: false,
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
const loadWorldGeoMap = () => {
|
|
86
|
+
if (!worldPromise) {
|
|
87
|
+
worldPromise = Promise.all([
|
|
88
|
+
import("world-atlas/countries-110m.json"),
|
|
89
|
+
import("topojson-client"),
|
|
90
|
+
])
|
|
91
|
+
.then(([topologyModule, topojson]) => {
|
|
92
|
+
const topology = topologyModule.default ?? topologyModule;
|
|
93
|
+
const geoJson = topojson.feature(topology, topology.objects.countries);
|
|
94
|
+
const entry = {
|
|
95
|
+
geoJson,
|
|
96
|
+
regionNames: extractRegionNames(geoJson),
|
|
97
|
+
applied: false,
|
|
98
|
+
};
|
|
99
|
+
registeredMaps.set(WORLD_GEO_SCOPE, entry);
|
|
100
|
+
return entry;
|
|
101
|
+
})
|
|
102
|
+
.catch(() => {
|
|
103
|
+
warnOnce(WORLD_GEO_SCOPE, "[charts] Failed to load the world map geometry for region-map charts; the chart will render empty.");
|
|
104
|
+
return null;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return worldPromise;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Resolve the geo data for a scope, loading the echarts map modules
|
|
111
|
+
* (and, for "world", the bundled world GeoJSON) on first use. Resolves
|
|
112
|
+
* null — never throws — when the scope is unknown or an asset failed
|
|
113
|
+
* to load; the renderer then draws an empty chart.
|
|
114
|
+
*/
|
|
115
|
+
export const loadRegionMapGeo = async (scope) => {
|
|
116
|
+
const modulesReady = await ensureEchartsMapModules();
|
|
117
|
+
if (!modulesReady) {
|
|
118
|
+
warnOnce("echarts-map-modules", "[charts] Failed to load the echarts map modules for region-map charts; the chart will render empty.");
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
let entry = registeredMaps.get(scope) ?? null;
|
|
122
|
+
if (!entry && scope === WORLD_GEO_SCOPE) {
|
|
123
|
+
entry = await loadWorldGeoMap();
|
|
124
|
+
}
|
|
125
|
+
if (!entry) {
|
|
126
|
+
warnOnce(scope, `[charts] Region map: no geo data registered for scope "${scope}". Register one with registerChartGeoMap("${scope}", geoJson).`);
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
if (!entry.applied) {
|
|
130
|
+
echarts.registerMap(scope, entry.geoJson);
|
|
131
|
+
entry.applied = true;
|
|
132
|
+
}
|
|
133
|
+
return { regionNames: entry.regionNames };
|
|
134
|
+
};
|
|
135
|
+
/** Test hook — resets the module-level registries and latches. */
|
|
136
|
+
export const __resetRegionMapLoaderForTests = () => {
|
|
137
|
+
registeredMaps.clear();
|
|
138
|
+
modulesPromise = null;
|
|
139
|
+
worldPromise = null;
|
|
140
|
+
warnedScopes.clear();
|
|
141
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Region map / filled map (OOXML chartex `<cx:regionMap>`) helpers.
|
|
3
|
+
*
|
|
4
|
+
* Excel's filled map paints a choropleth: values keyed by geography
|
|
5
|
+
* (country/state names) shade regions on a map. The spec reuses the
|
|
6
|
+
* existing series/domain data model — the category domain carries the
|
|
7
|
+
* region labels and the first series carries the values — plus an
|
|
8
|
+
* optional `geoScope` naming the registered echarts map ("world" by
|
|
9
|
+
* default).
|
|
10
|
+
*
|
|
11
|
+
* Everything here is pure: region-name normalization + alias matching,
|
|
12
|
+
* choropleth data assembly, and the sequential color ramp for the value
|
|
13
|
+
* scale. The geo asset loading lives in `region-map-loader.ts`.
|
|
14
|
+
*/
|
|
15
|
+
type MaybeLike<T> = T | null | undefined;
|
|
16
|
+
/** Default geo scope — the lazily loaded world map. */
|
|
17
|
+
export declare const WORLD_GEO_SCOPE = "world";
|
|
18
|
+
/**
|
|
19
|
+
* True when a basic-chart spec should render as a region-map
|
|
20
|
+
* choropleth: the first-class `"regionMap"` chart type, or any payload
|
|
21
|
+
* degraded to another `chartType` that preserves the authored class as
|
|
22
|
+
* `sourceChartType: "REGION_MAP"` (case-insensitive, mirroring
|
|
23
|
+
* `isParetoSpec` / `isOfPieChartSpec`).
|
|
24
|
+
*/
|
|
25
|
+
export declare const isRegionMapSpec: (spec: {
|
|
26
|
+
chartType?: string;
|
|
27
|
+
sourceChartType?: MaybeLike<string>;
|
|
28
|
+
}) => boolean;
|
|
29
|
+
/** Coerce a `geoScope` value to a usable map name (default "world"). */
|
|
30
|
+
export declare const resolveGeoScope: (raw: MaybeLike<string>) => string;
|
|
31
|
+
/**
|
|
32
|
+
* Normalize a region label for matching: trim, collapse whitespace,
|
|
33
|
+
* casefold, strip diacritics ("Côte d'Ivoire" → "cote d'ivoire"),
|
|
34
|
+
* unify curly apostrophes, and drop periods ("U.S." → "us").
|
|
35
|
+
*/
|
|
36
|
+
export declare const normalizeRegionKey: (name: MaybeLike<string>) => string;
|
|
37
|
+
/**
|
|
38
|
+
* Groups of equivalent region names. Members are matched through
|
|
39
|
+
* `normalizeRegionKey`, so casing/diacritics/periods don't matter.
|
|
40
|
+
* Each group binds only when one of its members actually exists on the
|
|
41
|
+
* loaded map, so the table is map-agnostic: the canonical spellings
|
|
42
|
+
* below cover both the bundled world map's Natural Earth names
|
|
43
|
+
* ("United States of America", "Dem. Rep. Congo", …) and the long-form
|
|
44
|
+
* names hosts commonly author.
|
|
45
|
+
*/
|
|
46
|
+
export declare const REGION_NAME_ALIASES: readonly (readonly string[])[];
|
|
47
|
+
/**
|
|
48
|
+
* Build a lookup from normalized label → the map's own region name.
|
|
49
|
+
* Exact (normalized) names always win; alias-group members resolve to
|
|
50
|
+
* whichever member exists on the map, and never shadow a real region
|
|
51
|
+
* name.
|
|
52
|
+
*/
|
|
53
|
+
export declare const buildRegionNameIndex: (regionNames: readonly string[]) => Map<string, string>;
|
|
54
|
+
export type RegionMapDatum = {
|
|
55
|
+
/** The map's own region name (echarts matches `data[].name` on it). */
|
|
56
|
+
name: string;
|
|
57
|
+
value: number;
|
|
58
|
+
};
|
|
59
|
+
export type RegionMapData = {
|
|
60
|
+
/** Matched regions, deduped by region (last value wins). */
|
|
61
|
+
data: RegionMapDatum[];
|
|
62
|
+
/** Labels that matched no region on the map — skipped, never fatal. */
|
|
63
|
+
unmatched: string[];
|
|
64
|
+
/** visualMap extent derived from the matched values. */
|
|
65
|
+
min: number;
|
|
66
|
+
max: number;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Assemble echarts `map`-series data from parallel label/value arrays
|
|
70
|
+
* (category domain = region labels, first series = values).
|
|
71
|
+
*
|
|
72
|
+
* - labels are matched exactly first, then through the normalization +
|
|
73
|
+
* alias layer; unmatched labels are collected (not thrown)
|
|
74
|
+
* - blank labels and non-finite values are skipped
|
|
75
|
+
* - duplicate labels collapse onto one region (last value wins)
|
|
76
|
+
* - min/max span the matched values; degenerate extents are widened so
|
|
77
|
+
* the visualMap ramp stays valid (no data → 0..1, all-equal → the
|
|
78
|
+
* value against 0, all-zero → 0..1)
|
|
79
|
+
*/
|
|
80
|
+
export declare const buildRegionMapData: (labels: readonly MaybeLike<string | number>[], values: readonly MaybeLike<string | number | boolean>[], regionNames: readonly string[]) => RegionMapData;
|
|
81
|
+
/** Linear RGB mix of two 6-digit hex colors; `t` 0 → a, 1 → b. */
|
|
82
|
+
export declare const mixHexColors: (a: string, b: string, t: number) => string;
|
|
83
|
+
/**
|
|
84
|
+
* Sequential (single-hue, light→dark) color ramp for the choropleth's
|
|
85
|
+
* value scale, derived from the theme accent. Dark mode gets its own
|
|
86
|
+
* steps mixed toward the dark chart surface rather than a flipped
|
|
87
|
+
* light ramp, so low values stay legible on a dark background.
|
|
88
|
+
*/
|
|
89
|
+
export declare const buildSequentialColorRamp: (accentColor: MaybeLike<string>, isDarkMode: boolean) => string[];
|
|
90
|
+
/**
|
|
91
|
+
* `console.warn` the unmatched region labels for a scope once — the
|
|
92
|
+
* chart re-renders on every data tick, so an unguarded warn would spam
|
|
93
|
+
* the console. Keyed by scope + label set; a different set of
|
|
94
|
+
* unmatched labels warns again.
|
|
95
|
+
*/
|
|
96
|
+
export declare const warnOnceForUnmatchedRegions: (scope: string, unmatched: readonly string[]) => void;
|
|
97
|
+
/** Test hook — resets the warn-once registry. */
|
|
98
|
+
export declare const __resetRegionMapWarningsForTests: () => void;
|
|
99
|
+
export {};
|
|
100
|
+
//# sourceMappingURL=region-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"region-map.d.ts","sourceRoot":"","sources":["../../../utils/region-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;AAEzC,uDAAuD;AACvD,eAAO,MAAM,eAAe,UAAU,CAAC;AAEvC;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,MAAM;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;CACrC,KAAG,OAGoD,CAAC;AAEzD,wEAAwE;AACxE,eAAO,MAAM,eAAe,GAAI,KAAK,SAAS,CAAC,MAAM,CAAC,KAAG,MAGxD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,SAAS,CAAC,MAAM,CAAC,KAAG,MAS3C,CAAC;AAEnB;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAqDpD,CAAC;AAEX;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAC/B,aAAa,SAAS,MAAM,EAAE,KAC7B,GAAG,CAAC,MAAM,EAAE,MAAM,CA2BpB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,4DAA4D;IAC5D,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,uEAAuE;IACvE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAOF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,SAAS,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,EAC7C,QAAQ,SAAS,SAAS,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,EACvD,aAAa,SAAS,MAAM,EAAE,KAC7B,aAiCF,CAAC;AAeF,kEAAkE;AAClE,eAAO,MAAM,YAAY,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAU9D,CAAC;AAOF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,GACnC,aAAa,SAAS,CAAC,MAAM,CAAC,EAC9B,YAAY,OAAO,KAClB,MAAM,EAWR,CAAC;AAMF;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,GACtC,OAAO,MAAM,EACb,WAAW,SAAS,MAAM,EAAE,KAC3B,IAUF,CAAC;AAEF,iDAAiD;AACjD,eAAO,MAAM,gCAAgC,QAAO,IAEnD,CAAC"}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Region map / filled map (OOXML chartex `<cx:regionMap>`) helpers.
|
|
3
|
+
*
|
|
4
|
+
* Excel's filled map paints a choropleth: values keyed by geography
|
|
5
|
+
* (country/state names) shade regions on a map. The spec reuses the
|
|
6
|
+
* existing series/domain data model — the category domain carries the
|
|
7
|
+
* region labels and the first series carries the values — plus an
|
|
8
|
+
* optional `geoScope` naming the registered echarts map ("world" by
|
|
9
|
+
* default).
|
|
10
|
+
*
|
|
11
|
+
* Everything here is pure: region-name normalization + alias matching,
|
|
12
|
+
* choropleth data assembly, and the sequential color ramp for the value
|
|
13
|
+
* scale. The geo asset loading lives in `region-map-loader.ts`.
|
|
14
|
+
*/
|
|
15
|
+
/** Default geo scope — the lazily loaded world map. */
|
|
16
|
+
export const WORLD_GEO_SCOPE = "world";
|
|
17
|
+
/**
|
|
18
|
+
* True when a basic-chart spec should render as a region-map
|
|
19
|
+
* choropleth: the first-class `"regionMap"` chart type, or any payload
|
|
20
|
+
* degraded to another `chartType` that preserves the authored class as
|
|
21
|
+
* `sourceChartType: "REGION_MAP"` (case-insensitive, mirroring
|
|
22
|
+
* `isParetoSpec` / `isOfPieChartSpec`).
|
|
23
|
+
*/
|
|
24
|
+
export const isRegionMapSpec = (spec) => spec?.chartType === "regionMap" ||
|
|
25
|
+
(typeof spec?.sourceChartType === "string" &&
|
|
26
|
+
spec.sourceChartType.toUpperCase() === "REGION_MAP");
|
|
27
|
+
/** Coerce a `geoScope` value to a usable map name (default "world"). */
|
|
28
|
+
export const resolveGeoScope = (raw) => {
|
|
29
|
+
const scope = typeof raw === "string" ? raw.trim() : "";
|
|
30
|
+
return scope.length > 0 ? scope : WORLD_GEO_SCOPE;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Normalize a region label for matching: trim, collapse whitespace,
|
|
34
|
+
* casefold, strip diacritics ("Côte d'Ivoire" → "cote d'ivoire"),
|
|
35
|
+
* unify curly apostrophes, and drop periods ("U.S." → "us").
|
|
36
|
+
*/
|
|
37
|
+
export const normalizeRegionKey = (name) => String(name ?? "")
|
|
38
|
+
.normalize("NFD")
|
|
39
|
+
// Combining diacritical marks left behind by NFD decomposition.
|
|
40
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
41
|
+
.replace(/[\u2018\u2019]/g, "'")
|
|
42
|
+
.replace(/\./g, "")
|
|
43
|
+
.replace(/\s+/g, " ")
|
|
44
|
+
.trim()
|
|
45
|
+
.toLowerCase();
|
|
46
|
+
/**
|
|
47
|
+
* Groups of equivalent region names. Members are matched through
|
|
48
|
+
* `normalizeRegionKey`, so casing/diacritics/periods don't matter.
|
|
49
|
+
* Each group binds only when one of its members actually exists on the
|
|
50
|
+
* loaded map, so the table is map-agnostic: the canonical spellings
|
|
51
|
+
* below cover both the bundled world map's Natural Earth names
|
|
52
|
+
* ("United States of America", "Dem. Rep. Congo", …) and the long-form
|
|
53
|
+
* names hosts commonly author.
|
|
54
|
+
*/
|
|
55
|
+
export const REGION_NAME_ALIASES = [
|
|
56
|
+
["United States of America", "United States", "USA", "US", "America"],
|
|
57
|
+
["United Kingdom", "UK", "Great Britain", "Britain"],
|
|
58
|
+
["Russia", "Russian Federation"],
|
|
59
|
+
["South Korea", "Republic of Korea", "Korea, South", "Korea"],
|
|
60
|
+
[
|
|
61
|
+
"North Korea",
|
|
62
|
+
"Democratic People's Republic of Korea",
|
|
63
|
+
"Korea, North",
|
|
64
|
+
],
|
|
65
|
+
["Czechia", "Czech Republic"],
|
|
66
|
+
["Côte d'Ivoire", "Ivory Coast", "Cote d'Ivoire"],
|
|
67
|
+
[
|
|
68
|
+
"Dem. Rep. Congo",
|
|
69
|
+
"Democratic Republic of the Congo",
|
|
70
|
+
"DR Congo",
|
|
71
|
+
"DRC",
|
|
72
|
+
"Congo-Kinshasa",
|
|
73
|
+
"Congo (Kinshasa)",
|
|
74
|
+
],
|
|
75
|
+
[
|
|
76
|
+
"Congo",
|
|
77
|
+
"Republic of the Congo",
|
|
78
|
+
"Congo-Brazzaville",
|
|
79
|
+
"Congo (Brazzaville)",
|
|
80
|
+
],
|
|
81
|
+
["Myanmar", "Burma"],
|
|
82
|
+
["Vietnam", "Viet Nam"],
|
|
83
|
+
["Syria", "Syrian Arab Republic"],
|
|
84
|
+
["Iran", "Islamic Republic of Iran"],
|
|
85
|
+
["Laos", "Lao PDR", "Lao People's Democratic Republic"],
|
|
86
|
+
["Bolivia", "Plurinational State of Bolivia"],
|
|
87
|
+
["Venezuela", "Bolivarian Republic of Venezuela"],
|
|
88
|
+
["Tanzania", "United Republic of Tanzania"],
|
|
89
|
+
["Moldova", "Republic of Moldova"],
|
|
90
|
+
["Brunei", "Brunei Darussalam"],
|
|
91
|
+
["United Arab Emirates", "UAE"],
|
|
92
|
+
["Bahamas", "The Bahamas"],
|
|
93
|
+
["Gambia", "The Gambia"],
|
|
94
|
+
["Timor-Leste", "East Timor"],
|
|
95
|
+
["eSwatini", "Eswatini", "Swaziland"],
|
|
96
|
+
["Macedonia", "North Macedonia"],
|
|
97
|
+
["Bosnia and Herz.", "Bosnia and Herzegovina", "Bosnia"],
|
|
98
|
+
["Central African Rep.", "Central African Republic"],
|
|
99
|
+
["Dominican Rep.", "Dominican Republic"],
|
|
100
|
+
["Eq. Guinea", "Equatorial Guinea"],
|
|
101
|
+
["S. Sudan", "South Sudan"],
|
|
102
|
+
["Solomon Is.", "Solomon Islands"],
|
|
103
|
+
["W. Sahara", "Western Sahara"],
|
|
104
|
+
["Falkland Is.", "Falkland Islands"],
|
|
105
|
+
["Turkey", "Türkiye"],
|
|
106
|
+
["Netherlands", "The Netherlands", "Holland"],
|
|
107
|
+
["Palestine", "State of Palestine", "Palestinian Territories"],
|
|
108
|
+
];
|
|
109
|
+
/**
|
|
110
|
+
* Build a lookup from normalized label → the map's own region name.
|
|
111
|
+
* Exact (normalized) names always win; alias-group members resolve to
|
|
112
|
+
* whichever member exists on the map, and never shadow a real region
|
|
113
|
+
* name.
|
|
114
|
+
*/
|
|
115
|
+
export const buildRegionNameIndex = (regionNames) => {
|
|
116
|
+
const index = new Map();
|
|
117
|
+
for (const name of regionNames) {
|
|
118
|
+
const key = normalizeRegionKey(name);
|
|
119
|
+
if (key && !index.has(key)) {
|
|
120
|
+
index.set(key, name);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
for (const group of REGION_NAME_ALIASES) {
|
|
124
|
+
// The group's target is the member that exists on the map.
|
|
125
|
+
let target;
|
|
126
|
+
for (const member of group) {
|
|
127
|
+
const existing = index.get(normalizeRegionKey(member));
|
|
128
|
+
if (existing !== undefined) {
|
|
129
|
+
target = existing;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (target === undefined)
|
|
134
|
+
continue;
|
|
135
|
+
for (const member of group) {
|
|
136
|
+
const key = normalizeRegionKey(member);
|
|
137
|
+
if (key && !index.has(key)) {
|
|
138
|
+
index.set(key, target);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return index;
|
|
143
|
+
};
|
|
144
|
+
const toFiniteNumber = (value) => {
|
|
145
|
+
const n = typeof value === "number" ? value : Number(value);
|
|
146
|
+
return Number.isFinite(n) ? n : undefined;
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* Assemble echarts `map`-series data from parallel label/value arrays
|
|
150
|
+
* (category domain = region labels, first series = values).
|
|
151
|
+
*
|
|
152
|
+
* - labels are matched exactly first, then through the normalization +
|
|
153
|
+
* alias layer; unmatched labels are collected (not thrown)
|
|
154
|
+
* - blank labels and non-finite values are skipped
|
|
155
|
+
* - duplicate labels collapse onto one region (last value wins)
|
|
156
|
+
* - min/max span the matched values; degenerate extents are widened so
|
|
157
|
+
* the visualMap ramp stays valid (no data → 0..1, all-equal → the
|
|
158
|
+
* value against 0, all-zero → 0..1)
|
|
159
|
+
*/
|
|
160
|
+
export const buildRegionMapData = (labels, values, regionNames) => {
|
|
161
|
+
const index = buildRegionNameIndex(regionNames);
|
|
162
|
+
const byRegion = new Map();
|
|
163
|
+
const unmatched = [];
|
|
164
|
+
for (let i = 0; i < labels.length; i++) {
|
|
165
|
+
const label = String(labels[i] ?? "").trim();
|
|
166
|
+
if (!label)
|
|
167
|
+
continue;
|
|
168
|
+
const value = toFiniteNumber(values[i]);
|
|
169
|
+
if (value === undefined)
|
|
170
|
+
continue;
|
|
171
|
+
const region = index.get(normalizeRegionKey(label));
|
|
172
|
+
if (region === undefined) {
|
|
173
|
+
unmatched.push(label);
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
byRegion.set(region, value);
|
|
177
|
+
}
|
|
178
|
+
const data = [];
|
|
179
|
+
let min = Number.POSITIVE_INFINITY;
|
|
180
|
+
let max = Number.NEGATIVE_INFINITY;
|
|
181
|
+
byRegion.forEach((value, name) => {
|
|
182
|
+
data.push({ name, value });
|
|
183
|
+
if (value < min)
|
|
184
|
+
min = value;
|
|
185
|
+
if (value > max)
|
|
186
|
+
max = value;
|
|
187
|
+
});
|
|
188
|
+
if (data.length === 0) {
|
|
189
|
+
min = 0;
|
|
190
|
+
max = 1;
|
|
191
|
+
}
|
|
192
|
+
else if (min === max) {
|
|
193
|
+
if (min > 0)
|
|
194
|
+
min = 0;
|
|
195
|
+
else if (max < 0)
|
|
196
|
+
max = 0;
|
|
197
|
+
else
|
|
198
|
+
max = 1;
|
|
199
|
+
}
|
|
200
|
+
return { data, unmatched, min, max };
|
|
201
|
+
};
|
|
202
|
+
// ------------------------- Sequential ramp -------------------------
|
|
203
|
+
const HEX_COLOR_RE = /^#?([0-9a-f]{6})$/i;
|
|
204
|
+
const parseHexColor = (color) => {
|
|
205
|
+
const match = HEX_COLOR_RE.exec(String(color ?? "").trim());
|
|
206
|
+
if (!match)
|
|
207
|
+
return undefined;
|
|
208
|
+
const n = parseInt(match[1], 16);
|
|
209
|
+
return [(n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff];
|
|
210
|
+
};
|
|
211
|
+
/** Linear RGB mix of two 6-digit hex colors; `t` 0 → a, 1 → b. */
|
|
212
|
+
export const mixHexColors = (a, b, t) => {
|
|
213
|
+
const ca = parseHexColor(a);
|
|
214
|
+
const cb = parseHexColor(b);
|
|
215
|
+
if (!ca || !cb)
|
|
216
|
+
return b;
|
|
217
|
+
const clamped = Math.min(1, Math.max(0, t));
|
|
218
|
+
const channel = (i) => Math.round(ca[i] + (cb[i] - ca[i]) * clamped);
|
|
219
|
+
return `#${[channel(0), channel(1), channel(2)]
|
|
220
|
+
.map((c) => c.toString(16).padStart(2, "0"))
|
|
221
|
+
.join("")}`;
|
|
222
|
+
};
|
|
223
|
+
/** Fallback hue when the theme accent isn't a parseable hex color. */
|
|
224
|
+
const DEFAULT_RAMP_ACCENT = "#4472c4";
|
|
225
|
+
const LIGHT_RAMP_BASE = "#ffffff";
|
|
226
|
+
const DARK_RAMP_BASE = "#1f2733";
|
|
227
|
+
/**
|
|
228
|
+
* Sequential (single-hue, light→dark) color ramp for the choropleth's
|
|
229
|
+
* value scale, derived from the theme accent. Dark mode gets its own
|
|
230
|
+
* steps mixed toward the dark chart surface rather than a flipped
|
|
231
|
+
* light ramp, so low values stay legible on a dark background.
|
|
232
|
+
*/
|
|
233
|
+
export const buildSequentialColorRamp = (accentColor, isDarkMode) => {
|
|
234
|
+
const accent = parseHexColor(accentColor)
|
|
235
|
+
? String(accentColor).trim()
|
|
236
|
+
: DEFAULT_RAMP_ACCENT;
|
|
237
|
+
const base = isDarkMode ? DARK_RAMP_BASE : LIGHT_RAMP_BASE;
|
|
238
|
+
// Low end keeps a visible trace of the hue (never the bare surface,
|
|
239
|
+
// so "has a small value" and "has no data" read differently).
|
|
240
|
+
const stops = isDarkMode
|
|
241
|
+
? [0.25, 0.44, 0.63, 0.81, 1]
|
|
242
|
+
: [0.12, 0.34, 0.56, 0.78, 1];
|
|
243
|
+
return stops.map((t) => mixHexColors(base, accent, t));
|
|
244
|
+
};
|
|
245
|
+
// --------------------------- Warn-once ---------------------------
|
|
246
|
+
const warnedUnmatched = new Set();
|
|
247
|
+
/**
|
|
248
|
+
* `console.warn` the unmatched region labels for a scope once — the
|
|
249
|
+
* chart re-renders on every data tick, so an unguarded warn would spam
|
|
250
|
+
* the console. Keyed by scope + label set; a different set of
|
|
251
|
+
* unmatched labels warns again.
|
|
252
|
+
*/
|
|
253
|
+
export const warnOnceForUnmatchedRegions = (scope, unmatched) => {
|
|
254
|
+
if (unmatched.length === 0)
|
|
255
|
+
return;
|
|
256
|
+
const key = `${scope}|${[...unmatched].sort().join("")}`;
|
|
257
|
+
if (warnedUnmatched.has(key))
|
|
258
|
+
return;
|
|
259
|
+
warnedUnmatched.add(key);
|
|
260
|
+
// eslint-disable-next-line no-console
|
|
261
|
+
console.warn(`[charts] Region map ("${scope}"): skipped ${unmatched.length} label(s) that match no region on the map:`, unmatched);
|
|
262
|
+
};
|
|
263
|
+
/** Test hook — resets the warn-once registry. */
|
|
264
|
+
export const __resetRegionMapWarningsForTests = () => {
|
|
265
|
+
warnedUnmatched.clear();
|
|
266
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stock-chart shaping shared by chart renderers.
|
|
3
|
+
*
|
|
4
|
+
* Excel stock charts come in four variants distinguished by the ordered
|
|
5
|
+
* set of value series they carry:
|
|
6
|
+
* - HLC : High, Low, Close
|
|
7
|
+
* - OHLC : Open, High, Low, Close
|
|
8
|
+
* - VHLC : Volume, High, Low, Close
|
|
9
|
+
* - VOHLC : Volume, Open, High, Low, Close
|
|
10
|
+
*
|
|
11
|
+
* We render stock as a "line-derived" chart: each price role becomes its
|
|
12
|
+
* own line series (volume is kept out of the price plot — it lives on a
|
|
13
|
+
* different scale and would flatten the price lines — and is instead
|
|
14
|
+
* surfaced via `toVolumeSeries` for renderers to draw as a column
|
|
15
|
+
* sub-pane on its own value axis). The candlestick transform is also
|
|
16
|
+
* provided for renderers that prefer a true OHLC body (echarts
|
|
17
|
+
* CandlestickChart / plotly candlestick).
|
|
18
|
+
*/
|
|
19
|
+
export type StockVariant = "HLC" | "OHLC" | "VHLC" | "VOHLC";
|
|
20
|
+
export type StockRole = "volume" | "open" | "high" | "low" | "close";
|
|
21
|
+
/**
|
|
22
|
+
* The ordered roles for each variant. Series are matched to roles by
|
|
23
|
+
* position — series[0] takes role[0], etc.
|
|
24
|
+
*/
|
|
25
|
+
export declare const STOCK_ROLES: Record<StockVariant, StockRole[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Map positional series to their stock roles for a given variant.
|
|
28
|
+
* Series beyond the variant's role count are ignored.
|
|
29
|
+
*/
|
|
30
|
+
export declare const assignStockRoles: (seriesValues: (number | null | undefined)[][], variant?: StockVariant) => Partial<Record<StockRole, (number | null)[]>>;
|
|
31
|
+
/**
|
|
32
|
+
* Line-derived view: emit one line series per PRICE role (open/high/low/
|
|
33
|
+
* close), preserving variant order and dropping volume. Each returned
|
|
34
|
+
* series carries a role-derived display name and its value vector.
|
|
35
|
+
*/
|
|
36
|
+
export declare const toLineSeries: (seriesValues: (number | null | undefined)[][], variant?: StockVariant) => Array<{
|
|
37
|
+
role: StockRole;
|
|
38
|
+
name: string;
|
|
39
|
+
values: (number | null)[];
|
|
40
|
+
}>;
|
|
41
|
+
export interface StockVolumePoint {
|
|
42
|
+
/** Volume value at the category index (null when missing). */
|
|
43
|
+
value: number | null;
|
|
44
|
+
/** True on an up day — used to color the volume column. */
|
|
45
|
+
up: boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Volume view: extract the volume series for the V-variants and flag
|
|
49
|
+
* each point as an up/down day so renderers can color the columns to
|
|
50
|
+
* match the price plot. Up/down follows convention: close vs open where
|
|
51
|
+
* the point carries an open, otherwise close vs the previous close. A
|
|
52
|
+
* point with nothing to compare against (e.g. the first) counts as up.
|
|
53
|
+
* Returns null for variants without a volume role (HLC / OHLC) or when
|
|
54
|
+
* the volume series is absent.
|
|
55
|
+
*/
|
|
56
|
+
export declare const toVolumeSeries: (seriesValues: (number | null | undefined)[][], variant?: StockVariant) => StockVolumePoint[] | null;
|
|
57
|
+
/**
|
|
58
|
+
* Candlestick view: produce [open, close, low, high] tuples (echarts'
|
|
59
|
+
* candlestick data order) per category index. When the variant has no
|
|
60
|
+
* `open` (HLC / VHLC) the close value is used as the open so the body
|
|
61
|
+
* collapses to a tick — matching how Excel renders HLC stock bars.
|
|
62
|
+
*/
|
|
63
|
+
export declare const toCandlestick: (seriesValues: (number | null | undefined)[][], variant?: StockVariant) => Array<[number, number, number, number]>;
|
|
64
|
+
//# sourceMappingURL=stock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stock.d.ts","sourceRoot":"","sources":["../../../utils/stock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE7D,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;AAErE;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,YAAY,EAAE,SAAS,EAAE,CAKzD,CAAC;AAQF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAC3B,cAAc,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,EAAE,EAC7C,UAAS,YAAoB,KAC5B,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,CAS9C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,YAAY,GACvB,cAAc,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,EAAE,EAC7C,UAAS,YAAoB,KAC5B,KAAK,CAAC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAA;CAAE,CAoBpE,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,2DAA2D;IAC3D,EAAE,EAAE,OAAO,CAAC;CACb;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,cAAc,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,EAAE,EAC7C,UAAS,YAAoB,KAC5B,gBAAgB,EAAE,GAAG,IAkBvB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GACxB,cAAc,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,EAAE,EAC7C,UAAS,YAAoB,KAC5B,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAgBxC,CAAC"}
|