@krizaka/charts 0.0.0-stage → 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.
Files changed (273) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +47 -2
  3. package/dist/bar.d.ts +17 -0
  4. package/dist/bar.js +16 -0
  5. package/dist/bar.js.map +1 -0
  6. package/dist/candlestick-IG626XL4.js +13 -0
  7. package/dist/candlestick-IG626XL4.js.map +1 -0
  8. package/dist/candlestick.d.ts +42 -0
  9. package/dist/candlestick.js +13 -0
  10. package/dist/candlestick.js.map +1 -0
  11. package/dist/canvas-4MSYDVFO.js +14 -0
  12. package/dist/canvas-4MSYDVFO.js.map +1 -0
  13. package/dist/cartesian-BRhNQ3ib.d.ts +34 -0
  14. package/dist/chart.d.ts +58 -0
  15. package/dist/chart.js +11 -0
  16. package/dist/chart.js.map +1 -0
  17. package/dist/chunk-3CJHBJXA.js +186 -0
  18. package/dist/chunk-3CJHBJXA.js.map +1 -0
  19. package/dist/chunk-6FINSUOS.js +49 -0
  20. package/dist/chunk-6FINSUOS.js.map +1 -0
  21. package/dist/chunk-7CSAGFBW.js +166 -0
  22. package/dist/chunk-7CSAGFBW.js.map +1 -0
  23. package/dist/chunk-7TIFCK5E.js +103 -0
  24. package/dist/chunk-7TIFCK5E.js.map +1 -0
  25. package/dist/chunk-AHQXJSAQ.js +38 -0
  26. package/dist/chunk-AHQXJSAQ.js.map +1 -0
  27. package/dist/chunk-ANUCSRUN.js +43 -0
  28. package/dist/chunk-ANUCSRUN.js.map +1 -0
  29. package/dist/chunk-BISL7DYW.js +66 -0
  30. package/dist/chunk-BISL7DYW.js.map +1 -0
  31. package/dist/chunk-BZ5CQFPH.js +19 -0
  32. package/dist/chunk-BZ5CQFPH.js.map +1 -0
  33. package/dist/chunk-DWJPPEMC.js +455 -0
  34. package/dist/chunk-DWJPPEMC.js.map +1 -0
  35. package/dist/chunk-DZB6HEVO.js +73 -0
  36. package/dist/chunk-DZB6HEVO.js.map +1 -0
  37. package/dist/chunk-EK3Z647J.js +133 -0
  38. package/dist/chunk-EK3Z647J.js.map +1 -0
  39. package/dist/chunk-I5QUZVU2.js +234 -0
  40. package/dist/chunk-I5QUZVU2.js.map +1 -0
  41. package/dist/chunk-OB5EFDDH.js +118 -0
  42. package/dist/chunk-OB5EFDDH.js.map +1 -0
  43. package/dist/chunk-OER6ETT4.js +155 -0
  44. package/dist/chunk-OER6ETT4.js.map +1 -0
  45. package/dist/chunk-PTSB3H5Q.js +205 -0
  46. package/dist/chunk-PTSB3H5Q.js.map +1 -0
  47. package/dist/chunk-TPJDOB7R.js +15 -0
  48. package/dist/chunk-TPJDOB7R.js.map +1 -0
  49. package/dist/chunk-UNK4MJVG.js +378 -0
  50. package/dist/chunk-UNK4MJVG.js.map +1 -0
  51. package/dist/chunk-VKO2OYYM.js +88 -0
  52. package/dist/chunk-VKO2OYYM.js.map +1 -0
  53. package/dist/chunk-VKTZD7UT.js +107 -0
  54. package/dist/chunk-VKTZD7UT.js.map +1 -0
  55. package/dist/chunk-W5FX4TU3.js +102 -0
  56. package/dist/chunk-W5FX4TU3.js.map +1 -0
  57. package/dist/chunk-WBH2QU77.js +178 -0
  58. package/dist/chunk-WBH2QU77.js.map +1 -0
  59. package/dist/chunk-WIRCIOLH.js +145 -0
  60. package/dist/chunk-WIRCIOLH.js.map +1 -0
  61. package/dist/chunk-XAJ3KCC4.js +116 -0
  62. package/dist/chunk-XAJ3KCC4.js.map +1 -0
  63. package/dist/chunk-XC6BQ3RN.js +92 -0
  64. package/dist/chunk-XC6BQ3RN.js.map +1 -0
  65. package/dist/chunk-XLLYNNC6.js +89 -0
  66. package/dist/chunk-XLLYNNC6.js.map +1 -0
  67. package/dist/chunk-XR4ZJDOV.js +15 -0
  68. package/dist/chunk-XR4ZJDOV.js.map +1 -0
  69. package/dist/context-BgYUymEO.d.ts +163 -0
  70. package/dist/frame-I7_xYGy5.d.ts +45 -0
  71. package/dist/full-S2MBPEEP.js +11 -0
  72. package/dist/full-S2MBPEEP.js.map +1 -0
  73. package/dist/funnel-QU5ZPC52.js +13 -0
  74. package/dist/funnel-QU5ZPC52.js.map +1 -0
  75. package/dist/funnel.d.ts +26 -0
  76. package/dist/funnel.js +13 -0
  77. package/dist/funnel.js.map +1 -0
  78. package/dist/gauge.d.ts +38 -0
  79. package/dist/gauge.js +12 -0
  80. package/dist/gauge.js.map +1 -0
  81. package/dist/geo.d.ts +37 -0
  82. package/dist/geo.js +13 -0
  83. package/dist/geo.js.map +1 -0
  84. package/dist/globe.d.ts +36 -0
  85. package/dist/globe.js +12 -0
  86. package/dist/globe.js.map +1 -0
  87. package/dist/heatmap-JWQOKUGM.js +13 -0
  88. package/dist/heatmap-JWQOKUGM.js.map +1 -0
  89. package/dist/heatmap.d.ts +47 -0
  90. package/dist/heatmap.js +15 -0
  91. package/dist/heatmap.js.map +1 -0
  92. package/dist/host-_F8RgiIz.d.ts +15 -0
  93. package/dist/index.d.ts +22 -0
  94. package/dist/index.js +101 -0
  95. package/dist/index.js.map +1 -0
  96. package/dist/line.d.ts +25 -0
  97. package/dist/line.js +18 -0
  98. package/dist/line.js.map +1 -0
  99. package/dist/native.d.ts +300 -0
  100. package/dist/native.js +875 -0
  101. package/dist/native.js.map +1 -0
  102. package/dist/pie.d.ts +37 -0
  103. package/dist/pie.js +16 -0
  104. package/dist/pie.js.map +1 -0
  105. package/dist/provider.d.ts +73 -0
  106. package/dist/provider.js +24 -0
  107. package/dist/provider.js.map +1 -0
  108. package/dist/radar-75GRJ2IK.js +13 -0
  109. package/dist/radar-75GRJ2IK.js.map +1 -0
  110. package/dist/radar.d.ts +22 -0
  111. package/dist/radar.js +13 -0
  112. package/dist/radar.js.map +1 -0
  113. package/dist/sankey-XQTX2EG6.js +13 -0
  114. package/dist/sankey-XQTX2EG6.js.map +1 -0
  115. package/dist/sankey.d.ts +28 -0
  116. package/dist/sankey.js +13 -0
  117. package/dist/sankey.js.map +1 -0
  118. package/dist/scatter-XSNG4OKD.js +13 -0
  119. package/dist/scatter-XSNG4OKD.js.map +1 -0
  120. package/dist/scatter.d.ts +41 -0
  121. package/dist/scatter.js +15 -0
  122. package/dist/scatter.js.map +1 -0
  123. package/dist/sparkline.d.ts +26 -0
  124. package/dist/sparkline.js +15 -0
  125. package/dist/sparkline.js.map +1 -0
  126. package/dist/svg-BJUHYX3B.js +14 -0
  127. package/dist/svg-BJUHYX3B.js.map +1 -0
  128. package/dist/treemap-OMGGGB5U.js +13 -0
  129. package/dist/treemap-OMGGGB5U.js.map +1 -0
  130. package/dist/treemap.d.ts +24 -0
  131. package/dist/treemap.js +14 -0
  132. package/dist/treemap.js.map +1 -0
  133. package/dist/world-110m-DVFWXS42.js +18 -0
  134. package/dist/world-110m-DVFWXS42.js.map +1 -0
  135. package/package.json +518 -3
  136. package/registry/area-chart.json +784 -0
  137. package/registry/bar-chart.json +783 -0
  138. package/registry/calendar-heatmap.json +459 -0
  139. package/registry/candlestick.json +552 -0
  140. package/registry/chart.json +440 -0
  141. package/registry/charts-provider.json +190 -0
  142. package/registry/examples/area-chart/credits-usage.d.ts +4 -0
  143. package/registry/examples/area-chart/credits-usage.js +38 -0
  144. package/registry/examples/area-chart/credits-usage.tsx +32 -0
  145. package/registry/examples/area-chart/native/credits-usage.d.ts +4 -0
  146. package/registry/examples/area-chart/native/credits-usage.dark.png +0 -0
  147. package/registry/examples/area-chart/native/credits-usage.js +33 -0
  148. package/registry/examples/area-chart/native/credits-usage.light.png +0 -0
  149. package/registry/examples/area-chart/native/credits-usage.tsx +27 -0
  150. package/registry/examples/area-chart/storage.d.ts +4 -0
  151. package/registry/examples/area-chart/storage.js +27 -0
  152. package/registry/examples/area-chart/storage.tsx +21 -0
  153. package/registry/examples/bar-chart/jobs-by-status.d.ts +4 -0
  154. package/registry/examples/bar-chart/jobs-by-status.js +37 -0
  155. package/registry/examples/bar-chart/jobs-by-status.tsx +31 -0
  156. package/registry/examples/bar-chart/native/jobs-by-status.d.ts +4 -0
  157. package/registry/examples/bar-chart/native/jobs-by-status.dark.png +0 -0
  158. package/registry/examples/bar-chart/native/jobs-by-status.js +32 -0
  159. package/registry/examples/bar-chart/native/jobs-by-status.light.png +0 -0
  160. package/registry/examples/bar-chart/native/jobs-by-status.tsx +26 -0
  161. package/registry/examples/bar-chart/plans-by-quarter.d.ts +4 -0
  162. package/registry/examples/bar-chart/plans-by-quarter.js +31 -0
  163. package/registry/examples/bar-chart/plans-by-quarter.tsx +26 -0
  164. package/registry/examples/bar-chart/top-supporters.d.ts +4 -0
  165. package/registry/examples/bar-chart/top-supporters.js +32 -0
  166. package/registry/examples/bar-chart/top-supporters.tsx +27 -0
  167. package/registry/examples/calendar-heatmap/publishing.d.ts +4 -0
  168. package/registry/examples/calendar-heatmap/publishing.js +28 -0
  169. package/registry/examples/calendar-heatmap/publishing.tsx +23 -0
  170. package/registry/examples/candlestick/auction-bids.d.ts +4 -0
  171. package/registry/examples/candlestick/auction-bids.js +37 -0
  172. package/registry/examples/candlestick/auction-bids.tsx +32 -0
  173. package/registry/examples/chart/polar-uploads.d.ts +4 -0
  174. package/registry/examples/chart/polar-uploads.js +34 -0
  175. package/registry/examples/chart/polar-uploads.tsx +30 -0
  176. package/registry/examples/charts-provider/french-orazaka.d.ts +4 -0
  177. package/registry/examples/charts-provider/french-orazaka.js +26 -0
  178. package/registry/examples/charts-provider/french-orazaka.tsx +22 -0
  179. package/registry/examples/charts-provider/native/french-orazaka.d.ts +4 -0
  180. package/registry/examples/charts-provider/native/french-orazaka.dark.png +0 -0
  181. package/registry/examples/charts-provider/native/french-orazaka.js +17 -0
  182. package/registry/examples/charts-provider/native/french-orazaka.light.png +0 -0
  183. package/registry/examples/charts-provider/native/french-orazaka.tsx +17 -0
  184. package/registry/examples/funnel/signup.d.ts +4 -0
  185. package/registry/examples/funnel/signup.js +30 -0
  186. package/registry/examples/funnel/signup.tsx +24 -0
  187. package/registry/examples/gauge/challenge-goal.d.ts +4 -0
  188. package/registry/examples/gauge/challenge-goal.js +24 -0
  189. package/registry/examples/gauge/challenge-goal.tsx +18 -0
  190. package/registry/examples/gauge/credits-quota.d.ts +4 -0
  191. package/registry/examples/gauge/credits-quota.js +26 -0
  192. package/registry/examples/gauge/credits-quota.tsx +19 -0
  193. package/registry/examples/gauge/native/credits-quota.d.ts +4 -0
  194. package/registry/examples/gauge/native/credits-quota.dark.png +0 -0
  195. package/registry/examples/gauge/native/credits-quota.js +25 -0
  196. package/registry/examples/gauge/native/credits-quota.light.png +0 -0
  197. package/registry/examples/gauge/native/credits-quota.tsx +18 -0
  198. package/registry/examples/geo-map/viewers.d.ts +4 -0
  199. package/registry/examples/geo-map/viewers.js +43 -0
  200. package/registry/examples/geo-map/viewers.tsx +29 -0
  201. package/registry/examples/globe/deployment-regions.d.ts +4 -0
  202. package/registry/examples/globe/deployment-regions.js +36 -0
  203. package/registry/examples/globe/deployment-regions.tsx +30 -0
  204. package/registry/examples/heatmap/upload-activity.d.ts +4 -0
  205. package/registry/examples/heatmap/upload-activity.js +30 -0
  206. package/registry/examples/heatmap/upload-activity.tsx +25 -0
  207. package/registry/examples/line-chart/creator-earnings.d.ts +4 -0
  208. package/registry/examples/line-chart/creator-earnings.js +35 -0
  209. package/registry/examples/line-chart/creator-earnings.tsx +30 -0
  210. package/registry/examples/line-chart/forecast.d.ts +4 -0
  211. package/registry/examples/line-chart/forecast.js +36 -0
  212. package/registry/examples/line-chart/forecast.tsx +30 -0
  213. package/registry/examples/line-chart/live-jobs.d.ts +4 -0
  214. package/registry/examples/line-chart/live-jobs.js +21 -0
  215. package/registry/examples/line-chart/live-jobs.tsx +23 -0
  216. package/registry/examples/line-chart/native/creator-earnings.d.ts +4 -0
  217. package/registry/examples/line-chart/native/creator-earnings.dark.png +0 -0
  218. package/registry/examples/line-chart/native/creator-earnings.js +32 -0
  219. package/registry/examples/line-chart/native/creator-earnings.light.png +0 -0
  220. package/registry/examples/line-chart/native/creator-earnings.tsx +26 -0
  221. package/registry/examples/line-chart/states.d.ts +4 -0
  222. package/registry/examples/line-chart/states.js +16 -0
  223. package/registry/examples/line-chart/states.tsx +13 -0
  224. package/registry/examples/pie-chart/credit-spend.d.ts +4 -0
  225. package/registry/examples/pie-chart/credit-spend.js +30 -0
  226. package/registry/examples/pie-chart/credit-spend.tsx +24 -0
  227. package/registry/examples/pie-chart/native/credit-spend.d.ts +4 -0
  228. package/registry/examples/pie-chart/native/credit-spend.dark.png +0 -0
  229. package/registry/examples/pie-chart/native/credit-spend.js +17 -0
  230. package/registry/examples/pie-chart/native/credit-spend.light.png +0 -0
  231. package/registry/examples/pie-chart/native/credit-spend.tsx +12 -0
  232. package/registry/examples/pie-chart/storage-by-kind.d.ts +4 -0
  233. package/registry/examples/pie-chart/storage-by-kind.js +31 -0
  234. package/registry/examples/pie-chart/storage-by-kind.tsx +26 -0
  235. package/registry/examples/radar-chart/model-comparison.d.ts +4 -0
  236. package/registry/examples/radar-chart/model-comparison.js +33 -0
  237. package/registry/examples/radar-chart/model-comparison.tsx +28 -0
  238. package/registry/examples/sankey/credit-flow.d.ts +4 -0
  239. package/registry/examples/sankey/credit-flow.js +37 -0
  240. package/registry/examples/sankey/credit-flow.tsx +32 -0
  241. package/registry/examples/scatter-chart/watch-time.d.ts +4 -0
  242. package/registry/examples/scatter-chart/watch-time.js +36 -0
  243. package/registry/examples/scatter-chart/watch-time.tsx +31 -0
  244. package/registry/examples/sparkline/bars.d.ts +4 -0
  245. package/registry/examples/sparkline/bars.js +12 -0
  246. package/registry/examples/sparkline/bars.tsx +7 -0
  247. package/registry/examples/sparkline/native/stat-trend.d.ts +4 -0
  248. package/registry/examples/sparkline/native/stat-trend.dark.png +0 -0
  249. package/registry/examples/sparkline/native/stat-trend.js +12 -0
  250. package/registry/examples/sparkline/native/stat-trend.light.png +0 -0
  251. package/registry/examples/sparkline/native/stat-trend.tsx +7 -0
  252. package/registry/examples/sparkline/stat-trend.d.ts +4 -0
  253. package/registry/examples/sparkline/stat-trend.js +18 -0
  254. package/registry/examples/sparkline/stat-trend.tsx +15 -0
  255. package/registry/examples/sparkline/table-rows.d.ts +4 -0
  256. package/registry/examples/sparkline/table-rows.js +23 -0
  257. package/registry/examples/sparkline/table-rows.tsx +26 -0
  258. package/registry/examples/treemap/storage.d.ts +4 -0
  259. package/registry/examples/treemap/storage.js +17 -0
  260. package/registry/examples/treemap/storage.tsx +13 -0
  261. package/registry/funnel.json +486 -0
  262. package/registry/gauge.json +599 -0
  263. package/registry/geo-map.json +483 -0
  264. package/registry/globe.json +468 -0
  265. package/registry/heatmap.json +530 -0
  266. package/registry/index.json +420 -0
  267. package/registry/line-chart.json +792 -0
  268. package/registry/pie-chart.json +1287 -0
  269. package/registry/radar-chart.json +458 -0
  270. package/registry/sankey.json +502 -0
  271. package/registry/scatter-chart.json +558 -0
  272. package/registry/sparkline.json +562 -0
  273. package/registry/treemap.json +431 -0
@@ -0,0 +1,155 @@
1
+ "use client";
2
+ import {
3
+ EChartHost,
4
+ baseOption,
5
+ categoryAxis,
6
+ tooltipHtml,
7
+ valueAxis
8
+ } from "./chunk-I5QUZVU2.js";
9
+ import {
10
+ read
11
+ } from "./chunk-W5FX4TU3.js";
12
+ import {
13
+ categoryFormatter,
14
+ shareFormatter,
15
+ valueFormatter
16
+ } from "./chunk-XLLYNNC6.js";
17
+ import {
18
+ useSettings
19
+ } from "./chunk-DWJPPEMC.js";
20
+ import {
21
+ capitalize,
22
+ fill
23
+ } from "./chunk-3CJHBJXA.js";
24
+
25
+ // src/candlestick/candlestick.tsx
26
+ import { useCallback, useMemo } from "react";
27
+ import { jsx } from "react/jsx-runtime";
28
+ var engine = () => import("./candlestick-IG626XL4.js");
29
+ var DEFAULT_COLUMNS = { open: "Open", high: "High", low: "Low", close: "Close", volume: "Volume" };
30
+ function Candlestick(props) {
31
+ const { data, x, open, high, low, close, volume, format, volumeFormat = "compact", xFormat, thresholds = [], zoom, columnLabels = DEFAULT_COLUMNS } = props;
32
+ const { locale, labels } = useSettings(props);
33
+ const fmt = useMemo(
34
+ () => ({
35
+ value: valueFormatter(format, locale, labels.noValue),
36
+ axis: valueFormatter(format, locale, labels.noValue, true),
37
+ volume: valueFormatter(volumeFormat, locale, labels.noValue),
38
+ category: categoryFormatter(xFormat, locale),
39
+ share: shareFormatter(locale)
40
+ }),
41
+ [format, volumeFormat, xFormat, locale, labels.noValue]
42
+ );
43
+ const rows = useMemo(
44
+ () => data.map((d, i) => ({
45
+ x: fmt.category(read(x, d, i)),
46
+ o: Number(read(open, d, i)),
47
+ h: Number(read(high, d, i)),
48
+ l: Number(read(low, d, i)),
49
+ c: Number(read(close, d, i)),
50
+ v: volume ? Number(read(volume, d, i)) : null
51
+ })),
52
+ [data, x, open, high, low, close, volume, fmt]
53
+ );
54
+ const isEmpty = rows.length === 0;
55
+ const first = rows[0];
56
+ const last = rows[rows.length - 1];
57
+ const hi = Math.max(...rows.map((r) => r.h));
58
+ const lo = Math.min(...rows.map((r) => r.l));
59
+ const change = first && last && first.o ? last.c / first.o - 1 : null;
60
+ const summary = isEmpty ? labels.empty : [
61
+ fill(labels.summaryIntro, { kind: labels.kinds.candlestick, count: `${rows.length} (${first.x} \u2013 ${last.x})` }),
62
+ `${capitalize(fill(labels.summaryOhlc, { open: fmt.value(first.o), close: fmt.value(last.c), low: fmt.value(lo), high: fmt.value(hi) }))}${change === null ? "" : `, ${fill(change >= 0 ? labels.summaryTrendUp : labels.summaryTrendDown, { change: fmt.share(Math.abs(change)) })}`}.`
63
+ ].join(" ");
64
+ const items = rows.map((r, i) => ({ seriesIndex: 0, dataIndex: i, text: `${r.x}: ${columnLabels.open} ${fmt.value(r.o)}, ${columnLabels.high} ${fmt.value(r.h)}, ${columnLabels.low} ${fmt.value(r.l)}, ${columnLabels.close} ${fmt.value(r.c)}` }));
65
+ const zoomOn = zoom ?? rows.length >= 40;
66
+ const build = useCallback(
67
+ (theme) => {
68
+ const base = baseOption(theme, true);
69
+ const withVolume = rows.some((r) => r.v != null);
70
+ const xs = rows.map((r) => r.x);
71
+ const grids = withVolume ? [
72
+ { left: 8, right: 12, top: 12, bottom: "30%", containLabel: true },
73
+ { left: 8, right: 12, top: "76%", bottom: 8, containLabel: true }
74
+ ] : [{ left: 8, right: 12, top: 12, bottom: 8, containLabel: true }];
75
+ return {
76
+ ...base,
77
+ tooltip: {
78
+ ...base.tooltip,
79
+ trigger: "axis",
80
+ axisPointer: { type: "line", lineStyle: { color: theme.axis } },
81
+ formatter: (params) => {
82
+ const r = rows[params[0].dataIndex];
83
+ const up = r.c >= r.o;
84
+ return tooltipHtml(
85
+ r.x,
86
+ [
87
+ { label: columnLabels.open, value: fmt.value(r.o) },
88
+ { label: columnLabels.high, value: fmt.value(r.h) },
89
+ { label: columnLabels.low, value: fmt.value(r.l) },
90
+ { label: columnLabels.close, value: fmt.value(r.c), color: up ? theme.success : theme.danger },
91
+ ...r.v != null ? [{ label: columnLabels.volume, value: fmt.volume(r.v) }] : []
92
+ ]
93
+ );
94
+ }
95
+ },
96
+ grid: grids,
97
+ xAxis: grids.map((_, i) => ({ ...categoryAxis(theme, xs), gridIndex: i, axisLabel: { color: theme.text2, hideOverlap: true, show: i === grids.length - 1 } })),
98
+ yAxis: [
99
+ { ...valueAxis(theme, (v) => fmt.axis(v)), scale: true, gridIndex: 0 },
100
+ ...withVolume ? [{ ...valueAxis(theme, (v) => fmt.volume(v)), gridIndex: 1, splitNumber: 2 }] : []
101
+ ],
102
+ dataZoom: zoomOn ? [{ type: "inside", xAxisIndex: grids.map((_, i) => i), start: Math.max(0, 100 - 40 / rows.length * 100), end: 100 }] : [],
103
+ series: [
104
+ {
105
+ type: "candlestick",
106
+ data: rows.map((r) => [r.o, r.c, r.l, r.h]),
107
+ barMaxWidth: 14,
108
+ itemStyle: { color: theme.surface, color0: theme.danger, borderColor: theme.success, borderColor0: theme.danger, borderWidth: 1.5 },
109
+ markLine: thresholds.length ? {
110
+ symbol: "none",
111
+ silent: true,
112
+ data: thresholds.map((t) => ({
113
+ yAxis: t.value,
114
+ label: { formatter: fill(labels.summaryThreshold, { label: t.label, value: fmt.value(t.value) }), color: theme.text, position: "insideEndTop", fontWeight: 600 },
115
+ lineStyle: { color: t.tone && t.tone !== "neutral" ? theme[t.tone === "accent" ? "accent" : t.tone] : theme.text2, type: [6, 4], width: 1.5 }
116
+ }))
117
+ } : void 0
118
+ },
119
+ ...withVolume ? [
120
+ {
121
+ type: "bar",
122
+ xAxisIndex: 1,
123
+ yAxisIndex: 1,
124
+ barMaxWidth: 14,
125
+ data: rows.map((r) => ({ value: r.v, itemStyle: { color: r.c >= r.o ? theme.success : theme.danger, opacity: 0.45, borderRadius: [2, 2, 0, 0] } }))
126
+ }
127
+ ] : []
128
+ ]
129
+ };
130
+ },
131
+ [rows, fmt, thresholds, zoomOn, columnLabels, labels.summaryThreshold]
132
+ );
133
+ return /* @__PURE__ */ jsx(
134
+ EChartHost,
135
+ {
136
+ kind: "candlestick",
137
+ frame: props,
138
+ defaultHeight: 340,
139
+ table: {
140
+ columns: [labels.category, columnLabels.open, columnLabels.high, columnLabels.low, columnLabels.close, ...volume ? [columnLabels.volume] : []],
141
+ rows: rows.map((r) => [r.x, fmt.value(r.o), fmt.value(r.h), fmt.value(r.l), fmt.value(r.c), ...volume ? [fmt.volume(r.v)] : []])
142
+ },
143
+ summary,
144
+ isEmpty,
145
+ engine,
146
+ build,
147
+ items
148
+ }
149
+ );
150
+ }
151
+
152
+ export {
153
+ Candlestick
154
+ };
155
+ //# sourceMappingURL=chunk-OER6ETT4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/candlestick/candlestick.tsx"],"sourcesContent":["\"use client\";\n// Open, high, low, close per period — a price, an auction's bids per hour — with what a colour-blind reader needs: a\n// rising candle is hollow (outlined in the success tone), a falling one filled (danger), so the shape says the\n// direction as well as the colour. Optional volume in its own panel below (never a second axis on the same plot),\n// zoom by wheel and drag, thresholds. ECharts, loaded when the chart nears the screen.\nimport { useCallback, useMemo } from \"react\";\n\nimport { read } from \"../core/data\";\nimport { categoryFormatter, shareFormatter, valueFormatter } from \"../core/format\";\nimport { capitalize, fill } from \"../core/labels\";\nimport type { Accessor, CategoryFormat, Datum, Threshold, ValueFormat } from \"../core/types\";\nimport { EChartHost, type EngineProps, type WalkItem } from \"../echarts/host\";\nimport { baseOption, categoryAxis, type ResolvedTheme, tooltipHtml, valueAxis } from \"../echarts/theme\";\nimport type { ChartFrameProps } from \"../web/frame\";\nimport { useSettings } from \"../web/frame\";\n\nconst engine = () => import(\"../echarts/engines/candlestick\");\n\nexport interface CandlestickProps<T extends Datum> extends ChartFrameProps, EngineProps {\n /** One row per period, oldest first. */\n data: readonly T[];\n /** The field of the period (a date, an hour). */\n x: Accessor<T, unknown>;\n/** The field of the price at the start of the period. */\n open: Accessor<T, number>;\n/** The field of the highest price of the period. */\n high: Accessor<T, number>;\n/** The field of the lowest price of the period. */\n low: Accessor<T, number>;\n/** The field of the price at the end of the period. */\n close: Accessor<T, number>;\n /** The field of the volume: drawn in its own panel under the candles. */\n volume?: Accessor<T, number>;\n /** How the prices and the volume are written. */\n format?: ValueFormat;\n/** How the volume is written. Default `\"compact\"`. */\n volumeFormat?: ValueFormat;\n /** How the periods are written. */\n xFormat?: CategoryFormat;\n /** Horizontal lines: a reserve price, a target. */\n thresholds?: readonly Threshold[];\n /** Zoom with the wheel and drag to pan. Default true from 40 periods. */\n zoom?: boolean;\n /** The words of the table's columns and the tooltip's rows. */\n columnLabels?: { readonly open: string; readonly high: string; readonly low: string; readonly close: string; readonly volume: string };\n}\n\nconst DEFAULT_COLUMNS = { open: \"Open\", high: \"High\", low: \"Low\", close: \"Close\", volume: \"Volume\" };\n\nexport function Candlestick<T extends Datum>(props: CandlestickProps<T>) {\n const { data, x, open, high, low, close, volume, format, volumeFormat = \"compact\", xFormat, thresholds = [], zoom, columnLabels = DEFAULT_COLUMNS } = props;\n const { locale, labels } = useSettings(props);\n const fmt = useMemo(\n () => ({\n value: valueFormatter(format, locale, labels.noValue),\n axis: valueFormatter(format, locale, labels.noValue, true),\n volume: valueFormatter(volumeFormat, locale, labels.noValue),\n category: categoryFormatter(xFormat, locale),\n share: shareFormatter(locale),\n }),\n [format, volumeFormat, xFormat, locale, labels.noValue],\n );\n const rows = useMemo(\n () =>\n data.map((d, i) => ({\n x: fmt.category(read(x, d, i)),\n o: Number(read(open, d, i)),\n h: Number(read(high, d, i)),\n l: Number(read(low, d, i)),\n c: Number(read(close, d, i)),\n v: volume ? Number(read(volume, d, i)) : null,\n })),\n [data, x, open, high, low, close, volume, fmt],\n );\n const isEmpty = rows.length === 0;\n const first = rows[0];\n const last = rows[rows.length - 1];\n const hi = Math.max(...rows.map((r) => r.h));\n const lo = Math.min(...rows.map((r) => r.l));\n const change = first && last && first.o ? last.c / first.o - 1 : null;\n const summary = isEmpty\n ? labels.empty\n : [\n fill(labels.summaryIntro, { kind: labels.kinds.candlestick, count: `${rows.length} (${first!.x} – ${last!.x})` }),\n `${capitalize(fill(labels.summaryOhlc, { open: fmt.value(first!.o), close: fmt.value(last!.c), low: fmt.value(lo), high: fmt.value(hi) }))}${change === null ? \"\" : `, ${fill(change >= 0 ? labels.summaryTrendUp : labels.summaryTrendDown, { change: fmt.share(Math.abs(change)) })}`}.`,\n ].join(\" \");\n const items: WalkItem[] = rows.map((r, i) => ({ seriesIndex: 0, dataIndex: i, text: `${r.x}: ${columnLabels.open} ${fmt.value(r.o)}, ${columnLabels.high} ${fmt.value(r.h)}, ${columnLabels.low} ${fmt.value(r.l)}, ${columnLabels.close} ${fmt.value(r.c)}` }));\n const zoomOn = zoom ?? rows.length >= 40;\n\n const build = useCallback(\n (theme: ResolvedTheme) => {\n const base = baseOption(theme, true);\n const withVolume = rows.some((r) => r.v != null);\n const xs = rows.map((r) => r.x);\n const grids = withVolume\n ? [\n { left: 8, right: 12, top: 12, bottom: \"30%\", containLabel: true },\n { left: 8, right: 12, top: \"76%\", bottom: 8, containLabel: true },\n ]\n : [{ left: 8, right: 12, top: 12, bottom: 8, containLabel: true }];\n return {\n ...base,\n tooltip: {\n ...(base.tooltip as object),\n trigger: \"axis\",\n axisPointer: { type: \"line\", lineStyle: { color: theme.axis } },\n formatter: (params: { dataIndex: number }[]) => {\n const r = rows[params[0]!.dataIndex]!;\n const up = r.c >= r.o;\n return tooltipHtml(\n r.x,\n [\n { label: columnLabels.open, value: fmt.value(r.o) },\n { label: columnLabels.high, value: fmt.value(r.h) },\n { label: columnLabels.low, value: fmt.value(r.l) },\n { label: columnLabels.close, value: fmt.value(r.c), color: up ? theme.success : theme.danger },\n ...(r.v != null ? [{ label: columnLabels.volume, value: fmt.volume(r.v) }] : []),\n ],\n );\n },\n },\n grid: grids,\n xAxis: grids.map((_, i) => ({ ...categoryAxis(theme, xs), gridIndex: i, axisLabel: { color: theme.text2, hideOverlap: true, show: i === grids.length - 1 } })),\n yAxis: [\n { ...valueAxis(theme, (v: number) => fmt.axis(v)), scale: true, gridIndex: 0 },\n ...(withVolume ? [{ ...valueAxis(theme, (v: number) => fmt.volume(v)), gridIndex: 1, splitNumber: 2 }] : []),\n ],\n dataZoom: zoomOn ? [{ type: \"inside\", xAxisIndex: grids.map((_, i) => i), start: Math.max(0, 100 - (40 / rows.length) * 100), end: 100 }] : [],\n series: [\n {\n type: \"candlestick\",\n data: rows.map((r) => [r.o, r.c, r.l, r.h]),\n barMaxWidth: 14,\n itemStyle: { color: theme.surface, color0: theme.danger, borderColor: theme.success, borderColor0: theme.danger, borderWidth: 1.5 },\n markLine: thresholds.length\n ? {\n symbol: \"none\",\n silent: true,\n data: thresholds.map((t) => ({\n yAxis: t.value,\n label: { formatter: fill(labels.summaryThreshold, { label: t.label, value: fmt.value(t.value) }), color: theme.text, position: \"insideEndTop\", fontWeight: 600 },\n lineStyle: { color: t.tone && t.tone !== \"neutral\" ? theme[t.tone === \"accent\" ? \"accent\" : t.tone] : theme.text2, type: [6, 4], width: 1.5 },\n })),\n }\n : undefined,\n },\n ...(withVolume\n ? [\n {\n type: \"bar\",\n xAxisIndex: 1,\n yAxisIndex: 1,\n barMaxWidth: 14,\n data: rows.map((r) => ({ value: r.v, itemStyle: { color: r.c >= r.o ? theme.success : theme.danger, opacity: 0.45, borderRadius: [2, 2, 0, 0] } })),\n },\n ]\n : []),\n ],\n };\n },\n [rows, fmt, thresholds, zoomOn, columnLabels, labels.summaryThreshold],\n );\n\n return (\n <EChartHost\n kind=\"candlestick\"\n frame={props}\n defaultHeight={340}\n table={{\n columns: [labels.category, columnLabels.open, columnLabels.high, columnLabels.low, columnLabels.close, ...(volume ? [columnLabels.volume] : [])],\n rows: rows.map((r) => [r.x, fmt.value(r.o), fmt.value(r.h), fmt.value(r.l), fmt.value(r.c), ...(volume ? [fmt.volume(r.v)] : [])]),\n }}\n summary={summary}\n isEmpty={isEmpty}\n engine={engine}\n build={build}\n items={items}\n />\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,205 @@
1
+ "use client";
2
+ import {
3
+ useWorld
4
+ } from "./chunk-6FINSUOS.js";
5
+ import {
6
+ valueFormatter
7
+ } from "./chunk-XLLYNNC6.js";
8
+ import {
9
+ ChartFrame,
10
+ Tooltip,
11
+ colorOf,
12
+ useInView,
13
+ useMounted,
14
+ useReducedMotion,
15
+ useSettings
16
+ } from "./chunk-DWJPPEMC.js";
17
+ import {
18
+ fill
19
+ } from "./chunk-3CJHBJXA.js";
20
+
21
+ // src/globe/globe.tsx
22
+ import { geoDistance, geoGraticule10, geoInterpolate, geoOrthographic, geoPath } from "d3-geo";
23
+ import { useEffect, useId, useMemo, useRef, useState } from "react";
24
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
25
+ var SPEED = 6;
26
+ function Globe3D(props) {
27
+ const { points, arcs = [], regions = [], format, center, autoRotate = true, pointLabel, valueLabel } = props;
28
+ const { locale, labels } = useSettings(props);
29
+ const mounted = useMounted();
30
+ const { world, failed } = useWorld(mounted);
31
+ const reduced = useReducedMotion();
32
+ const plotRef = useRef(null);
33
+ const onScreen = useInView(plotRef, { margin: "0px" });
34
+ const fmt = useMemo(() => valueFormatter(format, locale, labels.noValue), [format, locale, labels.noValue]);
35
+ const uid = useId().replace(/:/g, "");
36
+ const start = center ?? (points[0] ? [points[0].lon, points[0].lat] : [0, 20]);
37
+ const [rotation, setRotation] = useState([-start[0], -Math.max(-60, Math.min(60, start[1]))]);
38
+ const [active, setActive] = useState(null);
39
+ const [dragging, setDragging] = useState(false);
40
+ const drag = useRef(null);
41
+ const highlighted = useMemo(() => new Set(regions.map((r) => r.toUpperCase())), [regions]);
42
+ const turning = autoRotate && !reduced && onScreen && !dragging && active === null && mounted && Boolean(world);
43
+ useEffect(() => {
44
+ if (!turning) return void 0;
45
+ let frame = 0;
46
+ let last = performance.now();
47
+ const tick = (now) => {
48
+ if (now - last >= 33 && document.visibilityState === "visible") {
49
+ const dt = (now - last) / 1e3;
50
+ last = now;
51
+ setRotation(([lon, lat]) => [lon + SPEED * dt, lat]);
52
+ } else if (document.visibilityState !== "visible") last = now;
53
+ frame = requestAnimationFrame(tick);
54
+ };
55
+ frame = requestAnimationFrame(tick);
56
+ return () => cancelAnimationFrame(frame);
57
+ }, [turning]);
58
+ const pointAt = (ref) => {
59
+ if (typeof ref !== "string") return [ref[0], ref[1]];
60
+ const p = points.find((q) => q.label === ref);
61
+ return p ? [p.lon, p.lat] : null;
62
+ };
63
+ const summary = points.length ? `${fill(labels.summaryIntro, { kind: labels.kinds.globe, count: points.length })} ${points.map((p) => p.value == null ? p.label : `${p.label} ${fmt(p.value)}`).join(", ")}.` : labels.empty;
64
+ const table = { columns: [pointLabel ?? labels.category, valueLabel ?? labels.value, "lon", "lat"], rows: points.map((p) => [p.label, p.value == null ? labels.noValue : fmt(p.value), p.lon.toFixed(2), p.lat.toFixed(2)]) };
65
+ const focus = (i) => {
66
+ setActive(i);
67
+ if (i !== null) {
68
+ const p = points[i];
69
+ setRotation([-p.lon, -Math.max(-60, Math.min(60, p.lat))]);
70
+ }
71
+ };
72
+ const onKeyDown = (event) => {
73
+ const n = points.length;
74
+ if (!n) return;
75
+ const moves = {
76
+ ArrowRight: () => ((active ?? -1) + 1) % n,
77
+ ArrowDown: () => ((active ?? -1) + 1) % n,
78
+ ArrowLeft: () => ((active ?? 0) - 1 + n) % n,
79
+ ArrowUp: () => ((active ?? 0) - 1 + n) % n,
80
+ Home: () => 0,
81
+ End: () => n - 1,
82
+ Escape: () => null
83
+ };
84
+ const move = moves[event.key];
85
+ if (!move) return;
86
+ event.preventDefault();
87
+ focus(move());
88
+ };
89
+ const onPointerDown = (event) => {
90
+ drag.current = { x: event.clientX, y: event.clientY, r: rotation };
91
+ setDragging(true);
92
+ event.currentTarget.setPointerCapture?.(event.pointerId);
93
+ };
94
+ const onPointerMove = (event) => {
95
+ if (!drag.current) return;
96
+ const { x, y, r } = drag.current;
97
+ setRotation([r[0] + (event.clientX - x) * 0.4, Math.max(-75, Math.min(75, r[1] - (event.clientY - y) * 0.4))]);
98
+ };
99
+ const onPointerUp = () => {
100
+ drag.current = null;
101
+ setDragging(false);
102
+ };
103
+ return /* @__PURE__ */ jsx(
104
+ ChartFrame,
105
+ {
106
+ kind: "globe",
107
+ frame: { ...props, error: props.error ?? (failed || void 0), loading: props.loading || mounted && !world && !failed },
108
+ pending: !world && !failed,
109
+ defaultHeight: 360,
110
+ table,
111
+ summary,
112
+ isEmpty: points.length === 0,
113
+ announcement: active === null ? "" : `${points[active].label}${points[active].value != null ? `: ${fmt(points[active].value)}` : ""}`,
114
+ handlers: { onKeyDown, onBlur: () => setActive(null) },
115
+ plotRef,
116
+ children: (width, height) => {
117
+ if (!world) return null;
118
+ const radius = Math.max(20, Math.min(width, height) / 2 - 12);
119
+ const cx = width / 2;
120
+ const cy = height / 2;
121
+ const projection = geoOrthographic().scale(radius).translate([cx, cy]).rotate([rotation[0], rotation[1]]).clipAngle(90).precision(0.5);
122
+ const path = geoPath(projection);
123
+ const facing = [-rotation[0], -rotation[1]];
124
+ const visible = (lon, lat) => geoDistance([lon, lat], facing) < Math.PI / 2 - 0.02;
125
+ const current = active !== null ? points[active] : null;
126
+ const tipAt = current && visible(current.lon, current.lat) ? projection([current.lon, current.lat]) : null;
127
+ const placed = [];
128
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
129
+ /* @__PURE__ */ jsxs(
130
+ "svg",
131
+ {
132
+ className: "kzc-svg kzc-enter-pop",
133
+ width,
134
+ height,
135
+ viewBox: `0 0 ${width} ${height}`,
136
+ "aria-hidden": true,
137
+ style: { cursor: dragging ? "grabbing" : "grab", touchAction: "none" },
138
+ onPointerDown,
139
+ onPointerMove,
140
+ onPointerUp,
141
+ onPointerCancel: onPointerUp,
142
+ children: [
143
+ /* @__PURE__ */ jsxs("defs", { children: [
144
+ /* @__PURE__ */ jsxs("radialGradient", { id: `${uid}-light`, cx: "35%", cy: "30%", r: "75%", children: [
145
+ /* @__PURE__ */ jsx("stop", { offset: "0%", style: { stopColor: "color-mix(in oklab, var(--kz-accent) 14%, var(--kz-surface-2))" } }),
146
+ /* @__PURE__ */ jsx("stop", { offset: "100%", style: { stopColor: "var(--kz-surface-1)" } })
147
+ ] }),
148
+ /* @__PURE__ */ jsxs("radialGradient", { id: `${uid}-halo`, cx: "50%", cy: "50%", r: "50%", children: [
149
+ /* @__PURE__ */ jsx("stop", { offset: "86%", style: { stopColor: "var(--kz-accent)", stopOpacity: 0.22 } }),
150
+ /* @__PURE__ */ jsx("stop", { offset: "100%", style: { stopColor: "var(--kz-accent)", stopOpacity: 0 } })
151
+ ] })
152
+ ] }),
153
+ /* @__PURE__ */ jsx("circle", { cx, cy, r: radius * 1.12, style: { fill: `url(#${uid}-halo)` } }),
154
+ /* @__PURE__ */ jsx("circle", { cx, cy, r: radius, style: { fill: `url(#${uid}-light)`, stroke: "var(--kz-border-default)" } }),
155
+ /* @__PURE__ */ jsx("path", { d: path(geoGraticule10()) ?? "", style: { fill: "none", stroke: "var(--kz-border-subtle)", strokeWidth: 0.75 } }),
156
+ world.features.map((f, i) => /* @__PURE__ */ jsx(
157
+ "path",
158
+ {
159
+ d: path(f) ?? "",
160
+ style: {
161
+ fill: f.id && highlighted.has(f.id) ? "color-mix(in oklab, var(--kz-accent) 45%, var(--kz-surface-3))" : "var(--kz-surface-3)",
162
+ stroke: "var(--kz-surface-1)",
163
+ strokeWidth: 0.5
164
+ }
165
+ },
166
+ `${f.id ?? "x"}-${i}`
167
+ )),
168
+ arcs.map((arc, i) => {
169
+ const a = pointAt(arc.from);
170
+ const b = pointAt(arc.to);
171
+ if (!a || !b) return null;
172
+ const interpolate = geoInterpolate(a, b);
173
+ const line = { type: "LineString", coordinates: Array.from({ length: 33 }, (_, k) => interpolate(k / 32)) };
174
+ return /* @__PURE__ */ jsx("path", { d: path(line) ?? "", style: { fill: "none", stroke: "var(--kz-accent-text)", strokeWidth: 1.5, strokeDasharray: "4 3", opacity: 0.85 } }, i);
175
+ }),
176
+ points.map((p, i) => {
177
+ if (!visible(p.lon, p.lat)) return null;
178
+ const at0 = projection([p.lon, p.lat]);
179
+ const box = at0 && { x: at0[0] + 10, y: at0[1] - 8, w: p.label.length * 6.6, h: 16 };
180
+ const free = box && (active === i || !placed.some((b) => box.x < b.x + b.w && b.x < box.x + box.w && box.y < b.y + b.h && b.y < box.y + box.h));
181
+ if (free) placed.push(box);
182
+ const at = projection([p.lon, p.lat]);
183
+ if (!at) return null;
184
+ const color = colorOf(p.tone ?? "accent", 1);
185
+ const on = active === i;
186
+ return /* @__PURE__ */ jsxs("g", { children: [
187
+ /* @__PURE__ */ jsx("circle", { cx: at[0], cy: at[1], r: on ? 11 : 8, style: { fill: color, opacity: 0.22 } }),
188
+ /* @__PURE__ */ jsx("circle", { cx: at[0], cy: at[1], r: on ? 5.5 : 4.5, className: "kzc-dot", style: { fill: color } }),
189
+ free && (on || points.length <= 8) && width >= 320 && /* @__PURE__ */ jsx("text", { x: at[0] + 10, y: at[1], dominantBaseline: "central", className: "kzc-threshold-label", children: p.label })
190
+ ] }, `${p.label}-${i}`);
191
+ })
192
+ ]
193
+ }
194
+ ),
195
+ current && tipAt && /* @__PURE__ */ jsx(Tooltip, { x: tipAt[0], y: tipAt[1], width, title: current.label, rows: current.value == null ? [] : [{ label: valueLabel ?? labels.value, value: fmt(current.value) }] })
196
+ ] });
197
+ }
198
+ }
199
+ );
200
+ }
201
+
202
+ export {
203
+ Globe3D
204
+ };
205
+ //# sourceMappingURL=chunk-PTSB3H5Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/globe/globe.tsx"],"sourcesContent":["\"use client\";\n// A 3D globe without WebGL: the orthographic projection of d3-geo, lit like a sphere (a radial light, an atmosphere\n// in the brand accent), the far side clipped, great-circle arcs between places. Plain SVG — server-safe, sharp at\n// any size, readable by the export, a fraction of echarts-gl's 300 kB (see the package README). It turns slowly\n// while on screen and the page is visible, never under reduced motion; it is dragged with the pointer, and the\n// keyboard walks the places, turning the globe to each one.\nimport { geoDistance, geoGraticule10, geoInterpolate, geoOrthographic, geoPath } from \"d3-geo\";\nimport { type KeyboardEvent, type PointerEvent, useEffect, useId, useMemo, useRef, useState } from \"react\";\n\nimport { valueFormatter } from \"../core/format\";\nimport { fill } from \"../core/labels\";\nimport type { Tone, ValueFormat } from \"../core/types\";\nimport type { GeoPoint } from \"../geo/geo-map\";\nimport { useWorld } from \"../geo/world\";\nimport { ChartFrame, type ChartFrameProps, useSettings } from \"../web/frame\";\nimport { useInView, useMounted, useReducedMotion } from \"../web/hooks\";\nimport { colorOf, Tooltip } from \"../web/parts\";\n\nexport interface GlobePoint extends GeoPoint {\n /** Its tone: a healthy region in `\"success\"`, a degraded one in `\"warning\"`. Default `\"accent\"`. */\n readonly tone?: Tone;\n}\n\nexport interface GlobeArc {\n /** The labels of two points (or their coordinates, `[lon, lat]`). */\n readonly from: string | readonly [number, number];\n readonly to: string | readonly [number, number];\n}\n\nexport interface Globe3DProps extends ChartFrameProps {\n /** The places: deployment regions, data centres, audiences. */\n points: readonly GlobePoint[];\n /** Great-circle arcs between places: replication, traffic. */\n arcs?: readonly GlobeArc[];\n /** Countries to tint in the accent (ISO alpha-2): where the service is available. */\n regions?: readonly string[];\n /** How the points' values are written. */\n format?: ValueFormat;\n /** The longitude and latitude facing the reader at first. Default: the first point. */\n center?: readonly [number, number];\n /** Turn slowly while on screen (never under reduced motion). Default true. */\n autoRotate?: boolean;\n /** The header of the places and of their values in the table. */\n pointLabel?: string;\n/** The header of the points' values (“Availability”). */\n valueLabel?: string;\n}\n\nconst SPEED = 6; // degrees per second: one turn a minute.\n\n/** The world as a lit sphere: deployment regions, the arcs between them, a slow turn. */\nexport function Globe3D(props: Globe3DProps) {\n const { points, arcs = [], regions = [], format, center, autoRotate = true, pointLabel, valueLabel } = props;\n const { locale, labels } = useSettings(props);\n const mounted = useMounted();\n const { world, failed } = useWorld(mounted);\n const reduced = useReducedMotion();\n const plotRef = useRef<HTMLDivElement | null>(null);\n const onScreen = useInView(plotRef, { margin: \"0px\" });\n const fmt = useMemo(() => valueFormatter(format, locale, labels.noValue), [format, locale, labels.noValue]);\n const uid = useId().replace(/:/g, \"\");\n const start = center ?? (points[0] ? [points[0].lon, points[0].lat] : [0, 20]);\n const [rotation, setRotation] = useState<[number, number]>([-start[0], -Math.max(-60, Math.min(60, start[1]))]);\n const [active, setActive] = useState<number | null>(null);\n const [dragging, setDragging] = useState(false);\n const drag = useRef<{ x: number; y: number; r: [number, number] } | null>(null);\n const highlighted = useMemo(() => new Set(regions.map((r) => r.toUpperCase())), [regions]);\n\n // The slow turn: only on screen, page visible, no reduced motion, not while dragged or a place is focused.\n const turning = autoRotate && !reduced && onScreen && !dragging && active === null && mounted && Boolean(world);\n useEffect(() => {\n if (!turning) return undefined;\n let frame = 0;\n let last = performance.now();\n const tick = (now: number) => {\n // ~30 frames a second is plenty for a turn of 6°/s, and half the work.\n if (now - last >= 33 && document.visibilityState === \"visible\") {\n const dt = (now - last) / 1000;\n last = now;\n setRotation(([lon, lat]) => [lon + SPEED * dt, lat]);\n } else if (document.visibilityState !== \"visible\") last = now;\n frame = requestAnimationFrame(tick);\n };\n frame = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(frame);\n }, [turning]);\n\n const pointAt = (ref: GlobeArc[\"from\"]): [number, number] | null => {\n if (typeof ref !== \"string\") return [ref[0], ref[1]];\n const p = points.find((q) => q.label === ref);\n return p ? [p.lon, p.lat] : null;\n };\n\n const summary = points.length\n ? `${fill(labels.summaryIntro, { kind: labels.kinds.globe, count: points.length })} ${points.map((p) => (p.value == null ? p.label : `${p.label} ${fmt(p.value)}`)).join(\", \")}.`\n : labels.empty;\n const table = { columns: [pointLabel ?? labels.category, valueLabel ?? labels.value, \"lon\", \"lat\"], rows: points.map((p) => [p.label, p.value == null ? labels.noValue : fmt(p.value), p.lon.toFixed(2), p.lat.toFixed(2)]) };\n\n const focus = (i: number | null) => {\n setActive(i);\n if (i !== null) {\n const p = points[i]!;\n setRotation([-p.lon, -Math.max(-60, Math.min(60, p.lat))]);\n }\n };\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n const n = points.length;\n if (!n) return;\n const moves: Record<string, () => number | null> = {\n ArrowRight: () => ((active ?? -1) + 1) % n,\n ArrowDown: () => ((active ?? -1) + 1) % n,\n ArrowLeft: () => ((active ?? 0) - 1 + n) % n,\n ArrowUp: () => ((active ?? 0) - 1 + n) % n,\n Home: () => 0,\n End: () => n - 1,\n Escape: () => null,\n };\n const move = moves[event.key];\n if (!move) return;\n event.preventDefault();\n focus(move());\n };\n\n const onPointerDown = (event: PointerEvent<SVGSVGElement>) => {\n drag.current = { x: event.clientX, y: event.clientY, r: rotation };\n setDragging(true);\n event.currentTarget.setPointerCapture?.(event.pointerId);\n };\n const onPointerMove = (event: PointerEvent<SVGSVGElement>) => {\n if (!drag.current) return;\n const { x, y, r } = drag.current;\n setRotation([r[0] + (event.clientX - x) * 0.4, Math.max(-75, Math.min(75, r[1] - (event.clientY - y) * 0.4))]);\n };\n const onPointerUp = () => {\n drag.current = null;\n setDragging(false);\n };\n\n return (\n <ChartFrame\n kind=\"globe\"\n frame={{ ...props, error: props.error ?? (failed || undefined), loading: props.loading || (mounted && !world && !failed) }}\n pending={!world && !failed}\n defaultHeight={360}\n table={table}\n summary={summary}\n isEmpty={points.length === 0}\n announcement={active === null ? \"\" : `${points[active]!.label}${points[active]!.value != null ? `: ${fmt(points[active]!.value)}` : \"\"}`}\n handlers={{ onKeyDown, onBlur: () => setActive(null) }}\n plotRef={plotRef}\n >\n {(width, height) => {\n if (!world) return null;\n const radius = Math.max(20, Math.min(width, height) / 2 - 12);\n const cx = width / 2;\n const cy = height / 2;\n const projection = geoOrthographic().scale(radius).translate([cx, cy]).rotate([rotation[0], rotation[1]]).clipAngle(90).precision(0.5);\n const path = geoPath(projection);\n const facing: [number, number] = [-rotation[0], -rotation[1]];\n const visible = (lon: number, lat: number) => geoDistance([lon, lat], facing) < Math.PI / 2 - 0.02;\n const current = active !== null ? points[active] : null;\n const tipAt = current && visible(current.lon, current.lat) ? projection([current.lon, current.lat]) : null;\n const placed: { x: number; y: number; w: number; h: number }[] = [];\n return (\n <>\n <svg\n className=\"kzc-svg kzc-enter-pop\"\n width={width}\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n aria-hidden\n style={{ cursor: dragging ? \"grabbing\" : \"grab\", touchAction: \"none\" }}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerUp}\n >\n <defs>\n <radialGradient id={`${uid}-light`} cx=\"35%\" cy=\"30%\" r=\"75%\">\n <stop offset=\"0%\" style={{ stopColor: \"color-mix(in oklab, var(--kz-accent) 14%, var(--kz-surface-2))\" }} />\n <stop offset=\"100%\" style={{ stopColor: \"var(--kz-surface-1)\" }} />\n </radialGradient>\n <radialGradient id={`${uid}-halo`} cx=\"50%\" cy=\"50%\" r=\"50%\">\n <stop offset=\"86%\" style={{ stopColor: \"var(--kz-accent)\", stopOpacity: 0.22 }} />\n <stop offset=\"100%\" style={{ stopColor: \"var(--kz-accent)\", stopOpacity: 0 }} />\n </radialGradient>\n </defs>\n <circle cx={cx} cy={cy} r={radius * 1.12} style={{ fill: `url(#${uid}-halo)` }} />\n <circle cx={cx} cy={cy} r={radius} style={{ fill: `url(#${uid}-light)`, stroke: \"var(--kz-border-default)\" }} />\n <path d={path(geoGraticule10()) ?? \"\"} style={{ fill: \"none\", stroke: \"var(--kz-border-subtle)\", strokeWidth: 0.75 }} />\n {world.features.map((f, i) => (\n <path\n key={`${f.id ?? \"x\"}-${i}`}\n d={path(f) ?? \"\"}\n style={{\n fill: f.id && highlighted.has(f.id) ? \"color-mix(in oklab, var(--kz-accent) 45%, var(--kz-surface-3))\" : \"var(--kz-surface-3)\",\n stroke: \"var(--kz-surface-1)\",\n strokeWidth: 0.5,\n }}\n />\n ))}\n {arcs.map((arc, i) => {\n const a = pointAt(arc.from);\n const b = pointAt(arc.to);\n if (!a || !b) return null;\n const interpolate = geoInterpolate(a, b);\n const line = { type: \"LineString\" as const, coordinates: Array.from({ length: 33 }, (_, k) => interpolate(k / 32)) };\n return <path key={i} d={path(line) ?? \"\"} style={{ fill: \"none\", stroke: \"var(--kz-accent-text)\", strokeWidth: 1.5, strokeDasharray: \"4 3\", opacity: 0.85 }} />;\n })}\n {points.map((p, i) => {\n if (!visible(p.lon, p.lat)) return null;\n const at0 = projection([p.lon, p.lat]);\n // A label is written only where it does not cover another (the active place's always).\n const box = at0 && { x: at0[0] + 10, y: at0[1] - 8, w: p.label.length * 6.6, h: 16 };\n const free = box && (active === i || !placed.some((b) => box.x < b.x + b.w && b.x < box.x + box.w && box.y < b.y + b.h && b.y < box.y + box.h));\n if (free) placed.push(box);\n const at = projection([p.lon, p.lat]);\n if (!at) return null;\n const color = colorOf(p.tone ?? \"accent\", 1);\n const on = active === i;\n return (\n <g key={`${p.label}-${i}`}>\n <circle cx={at[0]} cy={at[1]} r={on ? 11 : 8} style={{ fill: color, opacity: 0.22 }} />\n <circle cx={at[0]} cy={at[1]} r={on ? 5.5 : 4.5} className=\"kzc-dot\" style={{ fill: color }} />\n {free && (on || points.length <= 8) && width >= 320 && (\n <text x={at[0] + 10} y={at[1]} dominantBaseline=\"central\" className=\"kzc-threshold-label\">\n {p.label}\n </text>\n )}\n </g>\n );\n })}\n </svg>\n {current && tipAt && <Tooltip x={tipAt[0]} y={tipAt[1]} width={width} title={current.label} rows={current.value == null ? [] : [{ label: valueLabel ?? labels.value, value: fmt(current.value) }]} />}\n </>\n );\n }}\n </ChartFrame>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,15 @@
1
+ "use client";
2
+
3
+ // src/web/realtime.ts
4
+ import { useCallback, useState } from "react";
5
+ function useSlidingWindow(size, initial = []) {
6
+ const [data, setData] = useState(() => initial.slice(-size));
7
+ const push = useCallback((...points) => setData((current) => [...current, ...points].slice(-size)), [size]);
8
+ const reset = useCallback((points = []) => setData(points.slice(-size)), [size]);
9
+ return { data, push, reset };
10
+ }
11
+
12
+ export {
13
+ useSlidingWindow
14
+ };
15
+ //# sourceMappingURL=chunk-TPJDOB7R.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/web/realtime.ts"],"sourcesContent":["\"use client\";\n// Live data — a job queue's throughput, an auction's bids — as a sliding window: append points as they arrive, keep\n// the last `size`. Pass the window to a chart with `live`: no entrance replayed on each update, no announcement of\n// every point (the reader asks for it with the keyboard).\nimport { useCallback, useState } from \"react\";\n\nexport interface SlidingWindow<T> {\n /** The points in the window, oldest first. */\n readonly data: readonly T[];\n /** Appends one point or several; the oldest fall out past `size`. */\n readonly push: (...points: T[]) => void;\n /** Replaces the whole window (a reconnection, a new period). */\n readonly reset: (points?: readonly T[]) => void;\n}\n\nexport function useSlidingWindow<T>(size: number, initial: readonly T[] = []): SlidingWindow<T> {\n const [data, setData] = useState<readonly T[]>(() => initial.slice(-size));\n const push = useCallback((...points: T[]) => setData((current) => [...current, ...points].slice(-size)), [size]);\n const reset = useCallback((points: readonly T[] = []) => setData(points.slice(-size)), [size]);\n return { data, push, reset };\n}\n"],"mappings":";;;AAIA,SAAS,aAAa,gBAAgB;AAW/B,SAAS,iBAAoB,MAAc,UAAwB,CAAC,GAAqB;AAC9F,QAAM,CAAC,MAAM,OAAO,IAAI,SAAuB,MAAM,QAAQ,MAAM,CAAC,IAAI,CAAC;AACzE,QAAM,OAAO,YAAY,IAAI,WAAgB,QAAQ,CAAC,YAAY,CAAC,GAAG,SAAS,GAAG,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;AAC/G,QAAM,QAAQ,YAAY,CAAC,SAAuB,CAAC,MAAM,QAAQ,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;AAC7F,SAAO,EAAE,MAAM,MAAM,MAAM;AAC7B;","names":[]}