@carto/ps-react-ui 4.7.1 → 4.9.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 (580) hide show
  1. package/dist/category-DwaeYjpX.js +656 -0
  2. package/dist/category-DwaeYjpX.js.map +1 -0
  3. package/dist/change-column-Cidl_M-4.js +1110 -0
  4. package/dist/change-column-Cidl_M-4.js.map +1 -0
  5. package/dist/data-zoom-layout-BH0LPwSy.js +28 -0
  6. package/dist/data-zoom-layout-BH0LPwSy.js.map +1 -0
  7. package/dist/echart-CU0KmClP.js +176 -0
  8. package/dist/echart-CU0KmClP.js.map +1 -0
  9. package/dist/exports-Cx-f6m6U.js +63 -0
  10. package/dist/exports-Cx-f6m6U.js.map +1 -0
  11. package/dist/formula-DuC0NQLH.js +79 -0
  12. package/dist/formula-DuC0NQLH.js.map +1 -0
  13. package/dist/markdown-BD1jcknS.js +8326 -0
  14. package/dist/markdown-BD1jcknS.js.map +1 -0
  15. package/dist/{styles-BYTyKQFP.js → option-builders-F-c9ELi1.js} +25 -45
  16. package/dist/option-builders-F-c9ELi1.js.map +1 -0
  17. package/dist/png-item-CS4z1iSH.js +45 -0
  18. package/dist/png-item-CS4z1iSH.js.map +1 -0
  19. package/dist/range-l4fNHLEg.js +213 -0
  20. package/dist/range-l4fNHLEg.js.map +1 -0
  21. package/dist/resolve-theme-color-BdojIw0K.js +47 -0
  22. package/dist/resolve-theme-color-BdojIw0K.js.map +1 -0
  23. package/dist/spread-CTuIXZSM.js +67 -0
  24. package/dist/spread-CTuIXZSM.js.map +1 -0
  25. package/dist/style-DVnT6HC1.js +131 -0
  26. package/dist/style-DVnT6HC1.js.map +1 -0
  27. package/dist/styles-cohnxh9F.js +23 -0
  28. package/dist/styles-cohnxh9F.js.map +1 -0
  29. package/dist/table-CQCAnDLb.js +388 -0
  30. package/dist/table-CQCAnDLb.js.map +1 -0
  31. package/dist/transforms-Cdx4fkU5.js +106 -0
  32. package/dist/transforms-Cdx4fkU5.js.map +1 -0
  33. package/dist/types/widgets/echart/utils.test.d.ts +1 -0
  34. package/dist/types/widgets/formula/config.test.d.ts +1 -0
  35. package/dist/types/widgets/stores/widget-store-branches.test.d.ts +1 -0
  36. package/dist/types/widgets/table/config.test.d.ts +1 -0
  37. package/dist/types/widgets-v2/actions/brush-toggle/brush-toggle.d.ts +56 -0
  38. package/dist/types/widgets-v2/actions/brush-toggle/index.d.ts +3 -0
  39. package/dist/types/widgets-v2/actions/brush-toggle/labels.d.ts +5 -0
  40. package/dist/types/widgets-v2/actions/brush-toggle/style.d.ts +12 -0
  41. package/dist/types/widgets-v2/actions/brush-toggle/transforms.d.ts +11 -0
  42. package/dist/types/widgets-v2/actions/brush-toggle/transforms.test.d.ts +1 -0
  43. package/dist/types/widgets-v2/actions/change-column/change-column-icon.d.ts +2 -0
  44. package/dist/types/widgets-v2/actions/change-column/change-column.d.ts +29 -0
  45. package/dist/types/widgets-v2/actions/change-column/index.d.ts +3 -0
  46. package/dist/types/widgets-v2/actions/change-column/labels.d.ts +5 -0
  47. package/dist/types/widgets-v2/actions/change-column/sortable-column-item.d.ts +14 -0
  48. package/dist/types/widgets-v2/actions/change-column/style.d.ts +33 -0
  49. package/dist/types/widgets-v2/actions/change-column/types.d.ts +10 -0
  50. package/dist/types/widgets-v2/actions/download/download.d.ts +18 -0
  51. package/dist/types/widgets-v2/actions/download/exports.d.ts +37 -0
  52. package/dist/types/widgets-v2/actions/download/icons.d.ts +12 -0
  53. package/dist/types/widgets-v2/actions/download/index.d.ts +6 -0
  54. package/dist/types/widgets-v2/actions/download/labels.d.ts +11 -0
  55. package/dist/types/widgets-v2/actions/download/png-item.d.ts +24 -0
  56. package/dist/types/widgets-v2/actions/download/style.d.ts +1 -0
  57. package/dist/types/widgets-v2/actions/download/types.d.ts +35 -0
  58. package/dist/types/widgets-v2/actions/fullscreen/fullscreen.d.ts +59 -0
  59. package/dist/types/widgets-v2/actions/fullscreen/index.d.ts +3 -0
  60. package/dist/types/widgets-v2/actions/fullscreen/labels.d.ts +5 -0
  61. package/dist/types/widgets-v2/actions/fullscreen/style.d.ts +48 -0
  62. package/dist/types/widgets-v2/actions/fullscreen/types.d.ts +14 -0
  63. package/dist/types/widgets-v2/actions/index.d.ts +9 -0
  64. package/dist/types/widgets-v2/actions/lock-selection/index.d.ts +3 -0
  65. package/dist/types/widgets-v2/actions/lock-selection/labels.d.ts +6 -0
  66. package/dist/types/widgets-v2/actions/lock-selection/lock-selection.d.ts +36 -0
  67. package/dist/types/widgets-v2/actions/lock-selection/style.d.ts +12 -0
  68. package/dist/types/widgets-v2/actions/lock-selection/transforms.d.ts +6 -0
  69. package/dist/types/widgets-v2/actions/relative-data/index.d.ts +3 -0
  70. package/dist/types/widgets-v2/actions/relative-data/labels.d.ts +5 -0
  71. package/dist/types/widgets-v2/actions/relative-data/relative-data.d.ts +39 -0
  72. package/dist/types/widgets-v2/actions/relative-data/style.d.ts +12 -0
  73. package/dist/types/widgets-v2/actions/relative-data/transforms.d.ts +30 -0
  74. package/dist/types/widgets-v2/actions/relative-data/transforms.test.d.ts +1 -0
  75. package/dist/types/widgets-v2/actions/searcher/filter.d.ts +6 -0
  76. package/dist/types/widgets-v2/actions/searcher/index.d.ts +4 -0
  77. package/dist/types/widgets-v2/actions/searcher/labels.d.ts +7 -0
  78. package/dist/types/widgets-v2/actions/searcher/searcher-toggle.d.ts +23 -0
  79. package/dist/types/widgets-v2/actions/searcher/searcher.d.ts +11 -0
  80. package/dist/types/widgets-v2/actions/searcher/style.d.ts +16 -0
  81. package/dist/types/widgets-v2/actions/stack-toggle/index.d.ts +3 -0
  82. package/dist/types/widgets-v2/actions/stack-toggle/labels.d.ts +5 -0
  83. package/dist/types/widgets-v2/actions/stack-toggle/stack-toggle.d.ts +10 -0
  84. package/dist/types/widgets-v2/actions/stack-toggle/style.d.ts +12 -0
  85. package/dist/types/widgets-v2/actions/stack-toggle/transforms.d.ts +13 -0
  86. package/dist/types/widgets-v2/actions/stack-toggle/transforms.test.d.ts +1 -0
  87. package/dist/types/widgets-v2/actions/zoom-toggle/index.d.ts +3 -0
  88. package/dist/types/widgets-v2/actions/zoom-toggle/labels.d.ts +5 -0
  89. package/dist/types/widgets-v2/actions/zoom-toggle/style.d.ts +12 -0
  90. package/dist/types/widgets-v2/actions/zoom-toggle/transforms.d.ts +51 -0
  91. package/dist/types/widgets-v2/actions/zoom-toggle/transforms.test.d.ts +1 -0
  92. package/dist/types/widgets-v2/actions/zoom-toggle/zoom-toggle.d.ts +35 -0
  93. package/dist/types/widgets-v2/bar/download.d.ts +24 -0
  94. package/dist/types/widgets-v2/bar/index.d.ts +4 -0
  95. package/dist/types/widgets-v2/bar/options.d.ts +43 -0
  96. package/dist/types/widgets-v2/bar/options.test.d.ts +1 -0
  97. package/dist/types/widgets-v2/bar/skeleton.d.ts +6 -0
  98. package/dist/types/widgets-v2/bar/types.d.ts +46 -0
  99. package/dist/types/widgets-v2/category/category-ui.d.ts +81 -0
  100. package/dist/types/widgets-v2/category/category.d.ts +48 -0
  101. package/dist/types/widgets-v2/category/components/category-bar-stacked.d.ts +28 -0
  102. package/dist/types/widgets-v2/category/components/category-bar.d.ts +23 -0
  103. package/dist/types/widgets-v2/category/components/category-legend.d.ts +18 -0
  104. package/dist/types/widgets-v2/category/components/category-row-multi.d.ts +31 -0
  105. package/dist/types/widgets-v2/category/components/category-row-other.d.ts +13 -0
  106. package/dist/types/widgets-v2/category/components/category-row-single.d.ts +28 -0
  107. package/dist/types/widgets-v2/category/components/category-row-stacked.d.ts +38 -0
  108. package/dist/types/widgets-v2/category/download.d.ts +16 -0
  109. package/dist/types/widgets-v2/category/download.test.d.ts +1 -0
  110. package/dist/types/widgets-v2/category/index.d.ts +10 -0
  111. package/dist/types/widgets-v2/category/skeleton.d.ts +11 -0
  112. package/dist/types/widgets-v2/category/style.d.ts +166 -0
  113. package/dist/types/widgets-v2/category/types.d.ts +53 -0
  114. package/dist/types/widgets-v2/echart/echart-ui.d.ts +44 -0
  115. package/dist/types/widgets-v2/echart/echart.d.ts +75 -0
  116. package/dist/types/widgets-v2/echart/index.d.ts +4 -0
  117. package/dist/types/widgets-v2/echart/shared-resize-observer.d.ts +5 -0
  118. package/dist/types/widgets-v2/echart/shared-resize-observer.test.d.ts +1 -0
  119. package/dist/types/widgets-v2/echart/style.d.ts +6 -0
  120. package/dist/types/widgets-v2/echart/use-chart-selection.d.ts +51 -0
  121. package/dist/types/widgets-v2/formula/delta.d.ts +22 -0
  122. package/dist/types/widgets-v2/formula/download.d.ts +20 -0
  123. package/dist/types/widgets-v2/formula/formula-ui.d.ts +20 -0
  124. package/dist/types/widgets-v2/formula/formula.d.ts +8 -0
  125. package/dist/types/widgets-v2/formula/index.d.ts +11 -0
  126. package/dist/types/widgets-v2/formula/note.d.ts +11 -0
  127. package/dist/types/widgets-v2/formula/prefix.d.ts +12 -0
  128. package/dist/types/widgets-v2/formula/series.d.ts +16 -0
  129. package/dist/types/widgets-v2/formula/skeleton.d.ts +4 -0
  130. package/dist/types/widgets-v2/formula/style.d.ts +29 -0
  131. package/dist/types/widgets-v2/formula/suffix.d.ts +12 -0
  132. package/dist/types/widgets-v2/formula/types.d.ts +43 -0
  133. package/dist/types/widgets-v2/formula/value.d.ts +14 -0
  134. package/dist/types/widgets-v2/histogram/download.d.ts +17 -0
  135. package/dist/types/widgets-v2/histogram/download.test.d.ts +1 -0
  136. package/dist/types/widgets-v2/histogram/index.d.ts +5 -0
  137. package/dist/types/widgets-v2/histogram/options.d.ts +42 -0
  138. package/dist/types/widgets-v2/histogram/options.test.d.ts +1 -0
  139. package/dist/types/widgets-v2/histogram/skeleton.d.ts +9 -0
  140. package/dist/types/widgets-v2/histogram/transforms.d.ts +17 -0
  141. package/dist/types/widgets-v2/histogram/transforms.test.d.ts +1 -0
  142. package/dist/types/widgets-v2/histogram/types.d.ts +51 -0
  143. package/dist/types/widgets-v2/index.d.ts +108 -0
  144. package/dist/types/widgets-v2/markdown/download.d.ts +16 -0
  145. package/dist/types/widgets-v2/markdown/download.test.d.ts +1 -0
  146. package/dist/types/widgets-v2/markdown/index.d.ts +6 -0
  147. package/dist/types/widgets-v2/markdown/markdown-content.d.ts +34 -0
  148. package/dist/types/widgets-v2/markdown/markdown-ui.d.ts +12 -0
  149. package/dist/types/widgets-v2/markdown/markdown.d.ts +6 -0
  150. package/dist/types/widgets-v2/markdown/skeleton.d.ts +4 -0
  151. package/dist/types/widgets-v2/markdown/style.d.ts +61 -0
  152. package/dist/types/widgets-v2/markdown/types.d.ts +4 -0
  153. package/dist/types/widgets-v2/note/labels.d.ts +5 -0
  154. package/dist/types/widgets-v2/note/style.d.ts +26 -0
  155. package/dist/types/widgets-v2/note/widget-note.d.ts +46 -0
  156. package/dist/types/widgets-v2/pie/download.d.ts +17 -0
  157. package/dist/types/widgets-v2/pie/download.test.d.ts +1 -0
  158. package/dist/types/widgets-v2/pie/index.d.ts +4 -0
  159. package/dist/types/widgets-v2/pie/options.d.ts +35 -0
  160. package/dist/types/widgets-v2/pie/options.test.d.ts +1 -0
  161. package/dist/types/widgets-v2/pie/skeleton.d.ts +4 -0
  162. package/dist/types/widgets-v2/pie/types.d.ts +57 -0
  163. package/dist/types/widgets-v2/provider/widget-provider.d.ts +32 -0
  164. package/dist/types/widgets-v2/range/index.d.ts +4 -0
  165. package/dist/types/widgets-v2/range/range-ui.d.ts +27 -0
  166. package/dist/types/widgets-v2/range/range.d.ts +24 -0
  167. package/dist/types/widgets-v2/range/skeleton.d.ts +9 -0
  168. package/dist/types/widgets-v2/range/style.d.ts +40 -0
  169. package/dist/types/widgets-v2/range/types.d.ts +37 -0
  170. package/dist/types/widgets-v2/scatterplot/download.d.ts +16 -0
  171. package/dist/types/widgets-v2/scatterplot/download.test.d.ts +1 -0
  172. package/dist/types/widgets-v2/scatterplot/index.d.ts +5 -0
  173. package/dist/types/widgets-v2/scatterplot/options.d.ts +42 -0
  174. package/dist/types/widgets-v2/scatterplot/options.test.d.ts +1 -0
  175. package/dist/types/widgets-v2/scatterplot/skeleton.d.ts +12 -0
  176. package/dist/types/widgets-v2/scatterplot/transforms.d.ts +17 -0
  177. package/dist/types/widgets-v2/scatterplot/transforms.test.d.ts +1 -0
  178. package/dist/types/widgets-v2/scatterplot/types.d.ts +54 -0
  179. package/dist/types/widgets-v2/selection-summary/labels.d.ts +6 -0
  180. package/dist/types/widgets-v2/selection-summary/selection-summary.d.ts +22 -0
  181. package/dist/types/widgets-v2/selection-summary/style.d.ts +23 -0
  182. package/dist/types/widgets-v2/spread/download.d.ts +15 -0
  183. package/dist/types/widgets-v2/spread/download.test.d.ts +1 -0
  184. package/dist/types/widgets-v2/spread/index.d.ts +6 -0
  185. package/dist/types/widgets-v2/spread/separator.d.ts +7 -0
  186. package/dist/types/widgets-v2/spread/skeleton.d.ts +9 -0
  187. package/dist/types/widgets-v2/spread/spread-ui.d.ts +18 -0
  188. package/dist/types/widgets-v2/spread/spread.d.ts +5 -0
  189. package/dist/types/widgets-v2/spread/types.d.ts +25 -0
  190. package/dist/types/widgets-v2/state/labels.d.ts +7 -0
  191. package/dist/types/widgets-v2/state/labels.test.d.ts +1 -0
  192. package/dist/types/widgets-v2/state/style.d.ts +19 -0
  193. package/dist/types/widgets-v2/state/widget-state.d.ts +19 -0
  194. package/dist/types/widgets-v2/stores/index.d.ts +8 -0
  195. package/dist/types/widgets-v2/stores/pipeline-middleware.d.ts +5 -0
  196. package/dist/types/widgets-v2/stores/pipeline-middleware.test.d.ts +1 -0
  197. package/dist/types/widgets-v2/stores/transforms.d.ts +4 -0
  198. package/dist/types/widgets-v2/stores/transforms.test.d.ts +1 -0
  199. package/dist/types/widgets-v2/stores/types.d.ts +55 -0
  200. package/dist/types/widgets-v2/stores/use-echart-instance.d.ts +15 -0
  201. package/dist/types/widgets-v2/stores/use-transform-enabled.d.ts +17 -0
  202. package/dist/types/widgets-v2/stores/use-transform.d.ts +12 -0
  203. package/dist/types/widgets-v2/stores/widget-context.d.ts +2 -0
  204. package/dist/types/widgets-v2/stores/widget-store-registry.d.ts +74 -0
  205. package/dist/types/widgets-v2/stores/widget-store-registry.test.d.ts +1 -0
  206. package/dist/types/widgets-v2/subheader/style.d.ts +10 -0
  207. package/dist/types/widgets-v2/subheader/subheader.d.ts +11 -0
  208. package/dist/types/widgets-v2/table/download.d.ts +18 -0
  209. package/dist/types/widgets-v2/table/download.test.d.ts +1 -0
  210. package/dist/types/widgets-v2/table/helpers.d.ts +32 -0
  211. package/dist/types/widgets-v2/table/helpers.test.d.ts +1 -0
  212. package/dist/types/widgets-v2/table/index.d.ts +7 -0
  213. package/dist/types/widgets-v2/table/labels.d.ts +22 -0
  214. package/dist/types/widgets-v2/table/skeleton.d.ts +22 -0
  215. package/dist/types/widgets-v2/table/style.d.ts +40 -0
  216. package/dist/types/widgets-v2/table/table-ui.d.ts +44 -0
  217. package/dist/types/widgets-v2/table/table.d.ts +50 -0
  218. package/dist/types/widgets-v2/table/types.d.ts +48 -0
  219. package/dist/types/widgets-v2/test-utils.d.ts +52 -0
  220. package/dist/types/widgets-v2/timeseries/download.d.ts +17 -0
  221. package/dist/types/widgets-v2/timeseries/download.test.d.ts +1 -0
  222. package/dist/types/widgets-v2/timeseries/index.d.ts +4 -0
  223. package/dist/types/widgets-v2/timeseries/options.d.ts +39 -0
  224. package/dist/types/widgets-v2/timeseries/options.test.d.ts +1 -0
  225. package/dist/types/widgets-v2/timeseries/skeleton.d.ts +8 -0
  226. package/dist/types/widgets-v2/timeseries/types.d.ts +60 -0
  227. package/dist/types/widgets-v2/toolbox/labels.d.ts +5 -0
  228. package/dist/types/widgets-v2/toolbox/style.d.ts +30 -0
  229. package/dist/types/widgets-v2/toolbox/toolbox.d.ts +49 -0
  230. package/dist/types/widgets-v2/types.d.ts +25 -0
  231. package/dist/types/widgets-v2/utils/data-zoom-layout.d.ts +11 -0
  232. package/dist/types/widgets-v2/utils/index.d.ts +3 -0
  233. package/dist/types/widgets-v2/utils/merge-options.d.ts +12 -0
  234. package/dist/types/widgets-v2/utils/merge-options.test.d.ts +1 -0
  235. package/dist/types/widgets-v2/utils/resolve-theme-color.d.ts +18 -0
  236. package/dist/types/widgets-v2/utils/resolve-theme-color.test.d.ts +1 -0
  237. package/dist/types/widgets-v2/wrapper/index.d.ts +4 -0
  238. package/dist/types/widgets-v2/wrapper/labels.d.ts +6 -0
  239. package/dist/types/widgets-v2/wrapper/style.d.ts +111 -0
  240. package/dist/types/widgets-v2/wrapper/widget-actions.d.ts +22 -0
  241. package/dist/types/widgets-v2/wrapper/widget-content.d.ts +12 -0
  242. package/dist/types/widgets-v2/wrapper/widget-wrapper.d.ts +51 -0
  243. package/dist/use-transform-DXPN3nY7.js +110 -0
  244. package/dist/use-transform-DXPN3nY7.js.map +1 -0
  245. package/dist/widget-context-DTGO0Yta.js +13 -0
  246. package/dist/widget-context-DTGO0Yta.js.map +1 -0
  247. package/dist/widget-store-registry-_W4Z4xp-.js +178 -0
  248. package/dist/widget-store-registry-_W4Z4xp-.js.map +1 -0
  249. package/dist/widgets/bar.js +14 -13
  250. package/dist/widgets/bar.js.map +1 -1
  251. package/dist/widgets/histogram.js +8 -7
  252. package/dist/widgets/histogram.js.map +1 -1
  253. package/dist/widgets/pie.js +19 -18
  254. package/dist/widgets/pie.js.map +1 -1
  255. package/dist/widgets/scatterplot.js +8 -7
  256. package/dist/widgets/scatterplot.js.map +1 -1
  257. package/dist/widgets/timeseries.js +11 -10
  258. package/dist/widgets/timeseries.js.map +1 -1
  259. package/dist/widgets/utils.js +8 -7
  260. package/dist/widgets/utils.js.map +1 -1
  261. package/dist/widgets-v2/actions.js +43 -0
  262. package/dist/widgets-v2/actions.js.map +1 -0
  263. package/dist/widgets-v2/bar.js +330 -0
  264. package/dist/widgets-v2/bar.js.map +1 -0
  265. package/dist/widgets-v2/category.js +104 -0
  266. package/dist/widgets-v2/category.js.map +1 -0
  267. package/dist/widgets-v2/echart.js +57 -0
  268. package/dist/widgets-v2/echart.js.map +1 -0
  269. package/dist/widgets-v2/formula.js +74 -0
  270. package/dist/widgets-v2/formula.js.map +1 -0
  271. package/dist/widgets-v2/histogram.js +353 -0
  272. package/dist/widgets-v2/histogram.js.map +1 -0
  273. package/dist/widgets-v2/markdown.js +68 -0
  274. package/dist/widgets-v2/markdown.js.map +1 -0
  275. package/dist/widgets-v2/pie.js +387 -0
  276. package/dist/widgets-v2/pie.js.map +1 -0
  277. package/dist/widgets-v2/range.js +52 -0
  278. package/dist/widgets-v2/range.js.map +1 -0
  279. package/dist/widgets-v2/scatterplot.js +411 -0
  280. package/dist/widgets-v2/scatterplot.js.map +1 -0
  281. package/dist/widgets-v2/spread.js +72 -0
  282. package/dist/widgets-v2/spread.js.map +1 -0
  283. package/dist/widgets-v2/stores.js +42 -0
  284. package/dist/widgets-v2/stores.js.map +1 -0
  285. package/dist/widgets-v2/table.js +78 -0
  286. package/dist/widgets-v2/table.js.map +1 -0
  287. package/dist/widgets-v2/timeseries.js +358 -0
  288. package/dist/widgets-v2/timeseries.js.map +1 -0
  289. package/dist/widgets-v2/utils.js +8 -0
  290. package/dist/widgets-v2/utils.js.map +1 -0
  291. package/dist/widgets-v2.js +953 -0
  292. package/dist/widgets-v2.js.map +1 -0
  293. package/package.json +71 -3
  294. package/src/components/lasso-tool/chip.test.tsx +176 -0
  295. package/src/components/lasso-tool/lasso-tool-inline.test.tsx +171 -0
  296. package/src/components/lasso-tool/lasso-tool.test.tsx +198 -0
  297. package/src/components/list-data/list-data.test.tsx +73 -0
  298. package/src/components/no-data-alert/no-data-alert.test.tsx +38 -0
  299. package/src/components/responsive-drawer/responsive-drawer.test.tsx +68 -0
  300. package/src/widgets/actions/brush-toggle/brush-overlay.test.tsx +465 -0
  301. package/src/widgets/actions/brush-toggle/brush-toggle.test.tsx +208 -0
  302. package/src/widgets/actions/change-column/change-column-dnd.test.tsx +193 -0
  303. package/src/widgets/actions/change-column/sortable-column-item.test.tsx +124 -0
  304. package/src/widgets/actions/zoom-toggle/zoom-toggle.test.tsx +322 -0
  305. package/src/widgets/category/components/category-rows.test.tsx +213 -0
  306. package/src/widgets/echart/utils.test.ts +277 -0
  307. package/src/widgets/formula/config.test.ts +37 -0
  308. package/src/widgets/range/components/range-item.test.tsx +243 -0
  309. package/src/widgets/stores/widget-store-branches.test.ts +275 -0
  310. package/src/widgets/table/config.test.ts +65 -0
  311. package/src/widgets/utils/chart-config/option-builders.test.ts +188 -0
  312. package/src/widgets-v2/PERFORMANCE.md +189 -0
  313. package/src/widgets-v2/actions/brush-toggle/brush-toggle.test.tsx +180 -0
  314. package/src/widgets-v2/actions/brush-toggle/brush-toggle.tsx +154 -0
  315. package/src/widgets-v2/actions/brush-toggle/index.ts +3 -0
  316. package/src/widgets-v2/actions/brush-toggle/labels.ts +9 -0
  317. package/src/widgets-v2/actions/brush-toggle/style.ts +11 -0
  318. package/src/widgets-v2/actions/brush-toggle/transforms.test.ts +47 -0
  319. package/src/widgets-v2/actions/brush-toggle/transforms.ts +31 -0
  320. package/src/widgets-v2/actions/change-column/change-column-icon.tsx +14 -0
  321. package/src/widgets-v2/actions/change-column/change-column.test.tsx +59 -0
  322. package/src/widgets-v2/actions/change-column/change-column.tsx +180 -0
  323. package/src/widgets-v2/actions/change-column/index.ts +7 -0
  324. package/src/widgets-v2/actions/change-column/labels.ts +9 -0
  325. package/src/widgets-v2/actions/change-column/sortable-column-item.tsx +56 -0
  326. package/src/widgets-v2/actions/change-column/style.ts +32 -0
  327. package/src/widgets-v2/actions/change-column/types.ts +11 -0
  328. package/src/widgets-v2/actions/download/download.test.tsx +327 -0
  329. package/src/widgets-v2/actions/download/download.tsx +144 -0
  330. package/src/widgets-v2/actions/download/exports.test.tsx +198 -0
  331. package/src/widgets-v2/actions/download/exports.ts +115 -0
  332. package/src/widgets-v2/actions/download/icons.tsx +26 -0
  333. package/src/widgets-v2/actions/download/index.ts +13 -0
  334. package/src/widgets-v2/actions/download/labels.ts +16 -0
  335. package/src/widgets-v2/actions/download/png-item.test.tsx +72 -0
  336. package/src/widgets-v2/actions/download/png-item.tsx +52 -0
  337. package/src/widgets-v2/actions/download/style.ts +3 -0
  338. package/src/widgets-v2/actions/download/types.ts +32 -0
  339. package/src/widgets-v2/actions/fullscreen/fullscreen.test.tsx +150 -0
  340. package/src/widgets-v2/actions/fullscreen/fullscreen.tsx +230 -0
  341. package/src/widgets-v2/actions/fullscreen/index.ts +7 -0
  342. package/src/widgets-v2/actions/fullscreen/labels.ts +9 -0
  343. package/src/widgets-v2/actions/fullscreen/style.ts +59 -0
  344. package/src/widgets-v2/actions/fullscreen/types.ts +15 -0
  345. package/src/widgets-v2/actions/index.ts +82 -0
  346. package/src/widgets-v2/actions/lock-selection/index.ts +10 -0
  347. package/src/widgets-v2/actions/lock-selection/labels.ts +11 -0
  348. package/src/widgets-v2/actions/lock-selection/lock-selection.test.tsx +187 -0
  349. package/src/widgets-v2/actions/lock-selection/lock-selection.tsx +130 -0
  350. package/src/widgets-v2/actions/lock-selection/style.ts +11 -0
  351. package/src/widgets-v2/actions/lock-selection/transforms.ts +27 -0
  352. package/src/widgets-v2/actions/relative-data/index.ts +3 -0
  353. package/src/widgets-v2/actions/relative-data/labels.ts +9 -0
  354. package/src/widgets-v2/actions/relative-data/relative-data.test.tsx +71 -0
  355. package/src/widgets-v2/actions/relative-data/relative-data.tsx +107 -0
  356. package/src/widgets-v2/actions/relative-data/style.ts +11 -0
  357. package/src/widgets-v2/actions/relative-data/transforms.test.ts +151 -0
  358. package/src/widgets-v2/actions/relative-data/transforms.ts +70 -0
  359. package/src/widgets-v2/actions/searcher/filter.ts +28 -0
  360. package/src/widgets-v2/actions/searcher/index.ts +8 -0
  361. package/src/widgets-v2/actions/searcher/labels.ts +13 -0
  362. package/src/widgets-v2/actions/searcher/searcher-toggle.tsx +91 -0
  363. package/src/widgets-v2/actions/searcher/searcher.test.tsx +92 -0
  364. package/src/widgets-v2/actions/searcher/searcher.tsx +112 -0
  365. package/src/widgets-v2/actions/searcher/style.ts +15 -0
  366. package/src/widgets-v2/actions/stack-toggle/index.ts +3 -0
  367. package/src/widgets-v2/actions/stack-toggle/labels.ts +9 -0
  368. package/src/widgets-v2/actions/stack-toggle/stack-toggle.test.tsx +61 -0
  369. package/src/widgets-v2/actions/stack-toggle/stack-toggle.tsx +54 -0
  370. package/src/widgets-v2/actions/stack-toggle/style.ts +11 -0
  371. package/src/widgets-v2/actions/stack-toggle/transforms.test.ts +43 -0
  372. package/src/widgets-v2/actions/stack-toggle/transforms.ts +25 -0
  373. package/src/widgets-v2/actions/zoom-toggle/index.ts +9 -0
  374. package/src/widgets-v2/actions/zoom-toggle/labels.ts +9 -0
  375. package/src/widgets-v2/actions/zoom-toggle/style.ts +11 -0
  376. package/src/widgets-v2/actions/zoom-toggle/transforms.test.ts +148 -0
  377. package/src/widgets-v2/actions/zoom-toggle/transforms.ts +171 -0
  378. package/src/widgets-v2/actions/zoom-toggle/zoom-toggle.test.tsx +107 -0
  379. package/src/widgets-v2/actions/zoom-toggle/zoom-toggle.tsx +106 -0
  380. package/src/widgets-v2/bar/download.test.tsx +91 -0
  381. package/src/widgets-v2/bar/download.tsx +66 -0
  382. package/src/widgets-v2/bar/index.ts +10 -0
  383. package/src/widgets-v2/bar/options.test.ts +334 -0
  384. package/src/widgets-v2/bar/options.ts +332 -0
  385. package/src/widgets-v2/bar/skeleton.test.tsx +19 -0
  386. package/src/widgets-v2/bar/skeleton.tsx +69 -0
  387. package/src/widgets-v2/bar/types.ts +51 -0
  388. package/src/widgets-v2/category/category-ui.test.tsx +746 -0
  389. package/src/widgets-v2/category/category-ui.tsx +389 -0
  390. package/src/widgets-v2/category/category.relative-data.test.tsx +107 -0
  391. package/src/widgets-v2/category/category.stack-toggle.test.tsx +85 -0
  392. package/src/widgets-v2/category/category.test.tsx +305 -0
  393. package/src/widgets-v2/category/category.tsx +121 -0
  394. package/src/widgets-v2/category/components/category-bar-stacked.test.tsx +121 -0
  395. package/src/widgets-v2/category/components/category-bar-stacked.tsx +73 -0
  396. package/src/widgets-v2/category/components/category-bar.test.tsx +64 -0
  397. package/src/widgets-v2/category/components/category-bar.tsx +49 -0
  398. package/src/widgets-v2/category/components/category-legend.test.tsx +51 -0
  399. package/src/widgets-v2/category/components/category-legend.tsx +39 -0
  400. package/src/widgets-v2/category/components/category-row-multi.tsx +86 -0
  401. package/src/widgets-v2/category/components/category-row-other.test.tsx +28 -0
  402. package/src/widgets-v2/category/components/category-row-other.tsx +33 -0
  403. package/src/widgets-v2/category/components/category-row-single.tsx +76 -0
  404. package/src/widgets-v2/category/components/category-row-stacked.test.tsx +244 -0
  405. package/src/widgets-v2/category/components/category-row-stacked.tsx +99 -0
  406. package/src/widgets-v2/category/download.test.ts +71 -0
  407. package/src/widgets-v2/category/download.ts +54 -0
  408. package/src/widgets-v2/category/index.ts +32 -0
  409. package/src/widgets-v2/category/skeleton.test.tsx +26 -0
  410. package/src/widgets-v2/category/skeleton.tsx +74 -0
  411. package/src/widgets-v2/category/style.ts +290 -0
  412. package/src/widgets-v2/category/types.ts +59 -0
  413. package/src/widgets-v2/echart/echart-ui.test.tsx +232 -0
  414. package/src/widgets-v2/echart/echart-ui.tsx +184 -0
  415. package/src/widgets-v2/echart/echart.test.tsx +229 -0
  416. package/src/widgets-v2/echart/echart.tsx +199 -0
  417. package/src/widgets-v2/echart/index.ts +22 -0
  418. package/src/widgets-v2/echart/shared-resize-observer.test.ts +91 -0
  419. package/src/widgets-v2/echart/shared-resize-observer.ts +56 -0
  420. package/src/widgets-v2/echart/style.ts +8 -0
  421. package/src/widgets-v2/echart/use-chart-selection.test.tsx +118 -0
  422. package/src/widgets-v2/echart/use-chart-selection.ts +115 -0
  423. package/src/widgets-v2/formula/delta.tsx +61 -0
  424. package/src/widgets-v2/formula/download.test.tsx +65 -0
  425. package/src/widgets-v2/formula/download.tsx +69 -0
  426. package/src/widgets-v2/formula/formula-ui.test.tsx +91 -0
  427. package/src/widgets-v2/formula/formula-ui.tsx +66 -0
  428. package/src/widgets-v2/formula/formula.test.tsx +50 -0
  429. package/src/widgets-v2/formula/formula.tsx +34 -0
  430. package/src/widgets-v2/formula/index.ts +17 -0
  431. package/src/widgets-v2/formula/note.tsx +25 -0
  432. package/src/widgets-v2/formula/prefix.tsx +25 -0
  433. package/src/widgets-v2/formula/series.tsx +67 -0
  434. package/src/widgets-v2/formula/skeleton.test.tsx +21 -0
  435. package/src/widgets-v2/formula/skeleton.tsx +27 -0
  436. package/src/widgets-v2/formula/style.ts +31 -0
  437. package/src/widgets-v2/formula/subcomponents.test.tsx +107 -0
  438. package/src/widgets-v2/formula/suffix.tsx +25 -0
  439. package/src/widgets-v2/formula/types.ts +48 -0
  440. package/src/widgets-v2/formula/value.tsx +31 -0
  441. package/src/widgets-v2/histogram/download.test.ts +94 -0
  442. package/src/widgets-v2/histogram/download.ts +60 -0
  443. package/src/widgets-v2/histogram/index.ts +10 -0
  444. package/src/widgets-v2/histogram/options.test.ts +318 -0
  445. package/src/widgets-v2/histogram/options.ts +338 -0
  446. package/src/widgets-v2/histogram/skeleton.test.tsx +16 -0
  447. package/src/widgets-v2/histogram/skeleton.tsx +70 -0
  448. package/src/widgets-v2/histogram/transforms.test.ts +46 -0
  449. package/src/widgets-v2/histogram/transforms.ts +30 -0
  450. package/src/widgets-v2/histogram/types.ts +55 -0
  451. package/src/widgets-v2/index.ts +204 -0
  452. package/src/widgets-v2/markdown/download.test.ts +66 -0
  453. package/src/widgets-v2/markdown/download.ts +53 -0
  454. package/src/widgets-v2/markdown/index.ts +6 -0
  455. package/src/widgets-v2/markdown/markdown-content.test.tsx +155 -0
  456. package/src/widgets-v2/markdown/markdown-content.tsx +72 -0
  457. package/src/widgets-v2/markdown/markdown-ui.test.tsx +75 -0
  458. package/src/widgets-v2/markdown/markdown-ui.tsx +55 -0
  459. package/src/widgets-v2/markdown/markdown.test.tsx +39 -0
  460. package/src/widgets-v2/markdown/markdown.tsx +17 -0
  461. package/src/widgets-v2/markdown/skeleton.test.tsx +15 -0
  462. package/src/widgets-v2/markdown/skeleton.tsx +32 -0
  463. package/src/widgets-v2/markdown/style.ts +53 -0
  464. package/src/widgets-v2/markdown/types.ts +4 -0
  465. package/src/widgets-v2/note/labels.ts +9 -0
  466. package/src/widgets-v2/note/style.ts +26 -0
  467. package/src/widgets-v2/note/widget-note.test.tsx +158 -0
  468. package/src/widgets-v2/note/widget-note.tsx +172 -0
  469. package/src/widgets-v2/pie/download.test.ts +78 -0
  470. package/src/widgets-v2/pie/download.ts +55 -0
  471. package/src/widgets-v2/pie/index.ts +10 -0
  472. package/src/widgets-v2/pie/options.test.ts +601 -0
  473. package/src/widgets-v2/pie/options.ts +513 -0
  474. package/src/widgets-v2/pie/skeleton.test.tsx +17 -0
  475. package/src/widgets-v2/pie/skeleton.tsx +32 -0
  476. package/src/widgets-v2/pie/types.ts +62 -0
  477. package/src/widgets-v2/provider/widget-provider.test.tsx +119 -0
  478. package/src/widgets-v2/provider/widget-provider.tsx +111 -0
  479. package/src/widgets-v2/range/index.ts +4 -0
  480. package/src/widgets-v2/range/range-ui.test.tsx +136 -0
  481. package/src/widgets-v2/range/range-ui.tsx +278 -0
  482. package/src/widgets-v2/range/range.test.tsx +68 -0
  483. package/src/widgets-v2/range/range.tsx +52 -0
  484. package/src/widgets-v2/range/skeleton.test.tsx +17 -0
  485. package/src/widgets-v2/range/skeleton.tsx +47 -0
  486. package/src/widgets-v2/range/style.ts +41 -0
  487. package/src/widgets-v2/range/types.ts +37 -0
  488. package/src/widgets-v2/scatterplot/download.test.ts +71 -0
  489. package/src/widgets-v2/scatterplot/download.ts +54 -0
  490. package/src/widgets-v2/scatterplot/index.ts +11 -0
  491. package/src/widgets-v2/scatterplot/options.test.ts +411 -0
  492. package/src/widgets-v2/scatterplot/options.ts +425 -0
  493. package/src/widgets-v2/scatterplot/skeleton.test.tsx +17 -0
  494. package/src/widgets-v2/scatterplot/skeleton.tsx +84 -0
  495. package/src/widgets-v2/scatterplot/transforms.test.ts +97 -0
  496. package/src/widgets-v2/scatterplot/transforms.ts +38 -0
  497. package/src/widgets-v2/scatterplot/types.ts +59 -0
  498. package/src/widgets-v2/selection-summary/labels.ts +11 -0
  499. package/src/widgets-v2/selection-summary/selection-summary.test.tsx +53 -0
  500. package/src/widgets-v2/selection-summary/selection-summary.tsx +62 -0
  501. package/src/widgets-v2/selection-summary/style.ts +23 -0
  502. package/src/widgets-v2/spread/download.test.ts +64 -0
  503. package/src/widgets-v2/spread/download.ts +59 -0
  504. package/src/widgets-v2/spread/index.ts +6 -0
  505. package/src/widgets-v2/spread/separator.tsx +11 -0
  506. package/src/widgets-v2/spread/skeleton.test.tsx +17 -0
  507. package/src/widgets-v2/spread/skeleton.tsx +38 -0
  508. package/src/widgets-v2/spread/spread-ui.test.tsx +108 -0
  509. package/src/widgets-v2/spread/spread-ui.tsx +52 -0
  510. package/src/widgets-v2/spread/spread.test.tsx +50 -0
  511. package/src/widgets-v2/spread/spread.tsx +31 -0
  512. package/src/widgets-v2/spread/types.ts +27 -0
  513. package/src/widgets-v2/state/labels.test.ts +33 -0
  514. package/src/widgets-v2/state/labels.ts +20 -0
  515. package/src/widgets-v2/state/style.ts +25 -0
  516. package/src/widgets-v2/state/widget-state.test.tsx +294 -0
  517. package/src/widgets-v2/state/widget-state.tsx +184 -0
  518. package/src/widgets-v2/stores/index.ts +49 -0
  519. package/src/widgets-v2/stores/pipeline-middleware.test.ts +187 -0
  520. package/src/widgets-v2/stores/pipeline-middleware.ts +91 -0
  521. package/src/widgets-v2/stores/transforms.test.ts +162 -0
  522. package/src/widgets-v2/stores/transforms.ts +70 -0
  523. package/src/widgets-v2/stores/types.ts +64 -0
  524. package/src/widgets-v2/stores/use-echart-instance.test.tsx +91 -0
  525. package/src/widgets-v2/stores/use-echart-instance.ts +29 -0
  526. package/src/widgets-v2/stores/use-transform-enabled.test.tsx +127 -0
  527. package/src/widgets-v2/stores/use-transform-enabled.ts +25 -0
  528. package/src/widgets-v2/stores/use-transform.test.tsx +262 -0
  529. package/src/widgets-v2/stores/use-transform.ts +158 -0
  530. package/src/widgets-v2/stores/widget-context.test.tsx +58 -0
  531. package/src/widgets-v2/stores/widget-context.ts +15 -0
  532. package/src/widgets-v2/stores/widget-store-registry.test.ts +292 -0
  533. package/src/widgets-v2/stores/widget-store-registry.ts +248 -0
  534. package/src/widgets-v2/subheader/style.ts +12 -0
  535. package/src/widgets-v2/subheader/subheader.test.tsx +30 -0
  536. package/src/widgets-v2/subheader/subheader.tsx +16 -0
  537. package/src/widgets-v2/table/download.test.ts +75 -0
  538. package/src/widgets-v2/table/download.ts +47 -0
  539. package/src/widgets-v2/table/helpers.test.ts +214 -0
  540. package/src/widgets-v2/table/helpers.ts +136 -0
  541. package/src/widgets-v2/table/index.ts +23 -0
  542. package/src/widgets-v2/table/labels.tsx +41 -0
  543. package/src/widgets-v2/table/skeleton.test.tsx +26 -0
  544. package/src/widgets-v2/table/skeleton.tsx +65 -0
  545. package/src/widgets-v2/table/style.ts +43 -0
  546. package/src/widgets-v2/table/table-ui.test.tsx +200 -0
  547. package/src/widgets-v2/table/table-ui.tsx +364 -0
  548. package/src/widgets-v2/table/table.test.tsx +119 -0
  549. package/src/widgets-v2/table/table.tsx +179 -0
  550. package/src/widgets-v2/table/types.ts +55 -0
  551. package/src/widgets-v2/test-utils.ts +107 -0
  552. package/src/widgets-v2/timeseries/download.test.ts +95 -0
  553. package/src/widgets-v2/timeseries/download.ts +86 -0
  554. package/src/widgets-v2/timeseries/index.ts +10 -0
  555. package/src/widgets-v2/timeseries/options.test.ts +394 -0
  556. package/src/widgets-v2/timeseries/options.ts +348 -0
  557. package/src/widgets-v2/timeseries/skeleton.test.tsx +13 -0
  558. package/src/widgets-v2/timeseries/skeleton.tsx +76 -0
  559. package/src/widgets-v2/timeseries/types.ts +65 -0
  560. package/src/widgets-v2/toolbox/labels.ts +9 -0
  561. package/src/widgets-v2/toolbox/style.ts +33 -0
  562. package/src/widgets-v2/toolbox/toolbox.test.tsx +200 -0
  563. package/src/widgets-v2/toolbox/toolbox.tsx +309 -0
  564. package/src/widgets-v2/types.ts +25 -0
  565. package/src/widgets-v2/utils/data-zoom-layout.ts +26 -0
  566. package/src/widgets-v2/utils/index.ts +3 -0
  567. package/src/widgets-v2/utils/merge-options.test.ts +52 -0
  568. package/src/widgets-v2/utils/merge-options.ts +50 -0
  569. package/src/widgets-v2/utils/resolve-theme-color.test.ts +43 -0
  570. package/src/widgets-v2/utils/resolve-theme-color.ts +34 -0
  571. package/src/widgets-v2/wrapper/index.ts +14 -0
  572. package/src/widgets-v2/wrapper/labels.ts +11 -0
  573. package/src/widgets-v2/wrapper/style.ts +134 -0
  574. package/src/widgets-v2/wrapper/widget-actions.test.tsx +52 -0
  575. package/src/widgets-v2/wrapper/widget-actions.tsx +43 -0
  576. package/src/widgets-v2/wrapper/widget-content.test.tsx +27 -0
  577. package/src/widgets-v2/wrapper/widget-content.tsx +29 -0
  578. package/src/widgets-v2/wrapper/widget-wrapper.test.tsx +159 -0
  579. package/src/widgets-v2/wrapper/widget-wrapper.tsx +178 -0
  580. package/dist/styles-BYTyKQFP.js.map +0 -1
@@ -0,0 +1,425 @@
1
+ import type { EChartsOption } from 'echarts'
2
+ import * as echarts from 'echarts'
3
+ import type { CallbackDataParams } from 'echarts/types/dist/shared'
4
+ import {
5
+ buildGridConfig,
6
+ buildLegendConfig,
7
+ createTooltipFormatter,
8
+ createTooltipPositioner,
9
+ niceNum,
10
+ } from '../../widgets/utils/chart-config'
11
+ import { ZOOM_LAYOUT } from '../actions/zoom-toggle'
12
+ import type { OptionFactory } from '../echart'
13
+ import { mergeOptions, resolveThemeColor } from '../utils'
14
+ import type {
15
+ ScatterplotEChartsOption,
16
+ ScatterplotOptionFactoryInput,
17
+ ScatterplotOptionsInput,
18
+ ScatterplotWidgetData,
19
+ } from './types'
20
+
21
+ /**
22
+ * Builds the **structural** ECharts option for a scatterplot widget — both
23
+ * axes `type: 'value'` (not category, unlike Bar/Histogram), grid, tooltip
24
+ * triggered per-item rather than per-axis. Intentionally data-agnostic: no
25
+ * series, no dataset, no `legend.show` (those depend on data and are added
26
+ * by {@link createScatterplotOptionFactory}).
27
+ *
28
+ * Styling parity with v1: dark themed tooltip via `createTooltipFormatter`
29
+ * + `createTooltipPositioner`, `buildGridConfig`-based grid, polished
30
+ * axisLine/Tick/splitLine, `overlineDelicate` axis labels, structural
31
+ * legend wired via `buildLegendConfig` (toggled by the merger), and the
32
+ * CARTO `qualitative.bold + secondary` palette — same pattern bar /
33
+ * histogram / pie already use. {@link createScatterplotOptionFactory}
34
+ * wraps this builder in its structural-phase branch.
35
+ */
36
+ export function scatterplotOptions({
37
+ theme,
38
+ xFormatter,
39
+ yFormatter,
40
+ }: ScatterplotOptionsInput): ScatterplotEChartsOption {
41
+ return {
42
+ grid: {
43
+ left: parseInt(theme.spacing(1)),
44
+ top: parseInt(theme.spacing(3)),
45
+ right: parseInt(theme.spacing(1)),
46
+ // Default: no legend. Merger bumps `bottom` when there are >1 series.
47
+ ...buildGridConfig(false, theme),
48
+ containLabel: true,
49
+ },
50
+ tooltip: {
51
+ // Per-point trigger — different from Bar's 'axis' trigger because
52
+ // points don't share an x-coordinate.
53
+ trigger: 'item',
54
+ backgroundColor: theme.palette.grey[900],
55
+ borderWidth: 0,
56
+ padding: [parseInt(theme.spacing(1)), parseInt(theme.spacing(1))],
57
+ textStyle: {
58
+ color: theme.palette.common.white,
59
+ fontSize: 11,
60
+ fontFamily: theme.typography.caption.fontFamily,
61
+ },
62
+ position: createTooltipPositioner(theme),
63
+ formatter: buildScatterTooltipFormatter(xFormatter, yFormatter),
64
+ },
65
+ // Legend styling baked here; `show` is toggled by the merger based on
66
+ // series count.
67
+ legend: {
68
+ ...buildLegendConfig({ hasLegend: false, labelFormatter: undefined }),
69
+ },
70
+ axisPointer: { lineStyle: { color: theme.palette.grey[400] } },
71
+ color: [
72
+ theme.palette.secondary.main,
73
+ ...Object.values(
74
+ (theme.palette as { qualitative?: { bold?: Record<string, string> } })
75
+ .qualitative?.bold ?? {},
76
+ ),
77
+ ],
78
+ xAxis: {
79
+ type: 'value',
80
+ axisLine: { show: false },
81
+ axisTick: { show: false },
82
+ axisLabel: {
83
+ fontSize: theme.typography.overlineDelicate?.fontSize,
84
+ fontFamily: theme.typography.overlineDelicate?.fontFamily,
85
+ color: theme.palette.black?.[60],
86
+ margin: parseInt(theme.spacing(1)),
87
+ hideOverlap: true,
88
+ showMinLabel: true,
89
+ showMaxLabel: true,
90
+ ...(xFormatter && { formatter: xFormatter }),
91
+ },
92
+ splitLine: {
93
+ show: true,
94
+ lineStyle: { color: theme.palette.black?.[4] ?? theme.palette.divider },
95
+ },
96
+ },
97
+ yAxis: {
98
+ type: 'value',
99
+ axisLine: { show: false },
100
+ axisTick: { show: false },
101
+ axisLabel: {
102
+ fontSize: theme.typography.overlineDelicate?.fontSize,
103
+ fontFamily: theme.typography.overlineDelicate?.fontFamily,
104
+ color: theme.palette.black?.[60],
105
+ margin: parseInt(theme.spacing(1)),
106
+ hideOverlap: true,
107
+ showMinLabel: true,
108
+ showMaxLabel: true,
109
+ ...(yFormatter && { formatter: yFormatter }),
110
+ },
111
+ splitLine: {
112
+ show: true,
113
+ lineStyle: { color: theme.palette.black?.[4] ?? theme.palette.divider },
114
+ },
115
+ },
116
+ } as ScatterplotEChartsOption
117
+ }
118
+
119
+ /**
120
+ * Returns the scatterplot widget's {@link OptionFactory} — one closure
121
+ * that owns BOTH phases of option construction:
122
+ *
123
+ * - **Structural phase** (`option == null`) — builds the theme-aware
124
+ * structural option via {@link scatterplotOptions}, optionally merging
125
+ * the consumer-supplied `optionsOverride`. Called once by Provider to
126
+ * seed `rawOptions` in the store.
127
+ * - **Merge phase** (`option != null`) — fuses post-pipeline `state.data`
128
+ * (`ScatterplotWidgetData`) into the option via the dataset API. Each
129
+ * series's `[x, y]` tuples land in `dataset[i].source` as 2-column
130
+ * rows; the series uses positional encoding (`encode: { x: 0, y: 1 }`)
131
+ * and `type: 'scatter'`. niceMin/niceMax are computed over both axes
132
+ * so the chart frames data on rounded extents and numeric jitters
133
+ * don't shift gridlines per render. Reactive `ctx.formatter` (driven
134
+ * by RelativeData) re-derives the y-axis label and tooltip at fusion
135
+ * time; `xFormatter` stays baked at structural-build time (relative
136
+ * is a values-axis concept; x is coordinate-space).
137
+ *
138
+ * When `ZoomToggle` installs a `dataZoom` slider, grid bottom is
139
+ * extended and the slider is positioned above the legend row (if any) —
140
+ * same layout dance bar / histogram / timeseries do.
141
+ */
142
+ export function createScatterplotOptionFactory(
143
+ options: ScatterplotOptionFactoryInput,
144
+ ): OptionFactory {
145
+ const { theme, xFormatter, yFormatter, optionsOverride } = options
146
+ const series = options.series
147
+ const symbolSize = options.symbolSize ?? 8
148
+ const selection = options.selection
149
+ const selectionSet =
150
+ selection && selection.length > 0 ? new Set<string>(selection) : null
151
+ return (option, data, ctx) => {
152
+ if (option == null) {
153
+ const structural = scatterplotOptions({ theme, xFormatter, yFormatter })
154
+ return optionsOverride
155
+ ? (mergeOptions(
156
+ structural as unknown as Record<string, unknown>,
157
+ optionsOverride as Partial<Record<string, unknown>>,
158
+ ) as EChartsOption)
159
+ : structural
160
+ }
161
+
162
+ const seriesArr = Array.isArray(data) ? (data as ScatterplotWidgetData) : []
163
+ if (seriesArr.length === 0) {
164
+ return { ...option, dataset: [], series: [] }
165
+ }
166
+ const hasLegend = seriesArr.length > 1
167
+ const baseLegend =
168
+ typeof option.legend === 'object' && !Array.isArray(option.legend)
169
+ ? option.legend
170
+ : {}
171
+ const baseGrid =
172
+ typeof option.grid === 'object' && !Array.isArray(option.grid)
173
+ ? option.grid
174
+ : {}
175
+ const baseTooltip =
176
+ typeof option.tooltip === 'object' && !Array.isArray(option.tooltip)
177
+ ? option.tooltip
178
+ : {}
179
+ const baseXAxis =
180
+ typeof option.xAxis === 'object' && !Array.isArray(option.xAxis)
181
+ ? option.xAxis
182
+ : {}
183
+ const baseYAxis =
184
+ typeof option.yAxis === 'object' && !Array.isArray(option.yAxis)
185
+ ? option.yAxis
186
+ : {}
187
+
188
+ const reactiveFormatter = ctx?.formatter
189
+
190
+ const { niceMinX, niceMaxX, niceMinY, niceMaxY } =
191
+ computeScatterBounds(seriesArr)
192
+
193
+ // Dim non-selected points via `series.itemStyle.color`. The selection
194
+ // key is `${seriesIndex}:${dataIndex}`; we read those off the params
195
+ // ECharts hands to the callback per-data.
196
+ //
197
+ // We *always* emit `itemStyle.color` (a passthrough when nothing is
198
+ // selected), not conditionally — dropping the key between renders
199
+ // would let ECharts' default merge keep the previous callback alive
200
+ // and items would stay dimmed forever after an external clear.
201
+ // Keeping the key always-present means a plain setOption merge swaps
202
+ // the callback in place, no `replaceMerge` and no entry-animation
203
+ // flash on selection on/off.
204
+ const makeDimColor =
205
+ (seriesIdx: number) => (params: CallbackDataParams) => {
206
+ const base = params.color as string
207
+ if (!selectionSet) return base
208
+ const key = `${seriesIdx}:${params.dataIndex}`
209
+ return selectionSet.has(key)
210
+ ? base
211
+ : echarts.color.modifyAlpha(base, 0.15)
212
+ }
213
+
214
+ // Zoom slider layout: when ZoomToggle has installed `dataZoom`,
215
+ // reserve grid space and lift any x-slider above the legend (if
216
+ // present). Scatter supports 2D zoom via `axes: ['x', 'y']` — in
217
+ // that case there's also a vertical y-slider on the right edge, so
218
+ // we reserve `grid.right` separately. Inside-only dataZoom entries
219
+ // (no `type: 'slider'`) take no grid space.
220
+ const dataZoomLayout = layoutDataZoomForScatter(option.dataZoom, hasLegend)
221
+ const hasXSlider = dataZoomLayout?.hasXSlider ?? false
222
+ const hasYSlider = dataZoomLayout?.hasYSlider ?? false
223
+ const fallbackBottom =
224
+ typeof baseGrid.bottom === 'number' ? baseGrid.bottom : 24
225
+ const baseBottom = hasLegend ? 56 : fallbackBottom
226
+ const gridBottom = hasXSlider
227
+ ? baseBottom + ZOOM_LAYOUT.sliderHeight + ZOOM_LAYOUT.sliderGap
228
+ : baseBottom
229
+ const fallbackRight =
230
+ typeof baseGrid.right === 'number' ? baseGrid.right : 8
231
+ const gridRight = hasYSlider
232
+ ? fallbackRight + ZOOM_LAYOUT.sliderHeight + ZOOM_LAYOUT.sliderGap
233
+ : fallbackRight
234
+
235
+ return {
236
+ ...option,
237
+ // ECharts dataset.source wants a mutable [number, number][] shape; we
238
+ // hold readonly tuples internally, so cast at the boundary.
239
+ dataset: seriesArr.map((s) => ({ source: s as unknown as number[][] })),
240
+ series: seriesArr.map((_, i) => {
241
+ const overrideColor = resolveThemeColor(theme, series?.[i]?.color)
242
+ return {
243
+ type: 'scatter' as const,
244
+ datasetIndex: i,
245
+ name: series?.[i]?.name ?? `Series ${i + 1}`,
246
+ encode: { x: 0, y: 1 },
247
+ symbolSize,
248
+ emphasis: { focus: 'series' },
249
+ itemStyle: { color: makeDimColor(i) },
250
+ ...(overrideColor ? { color: overrideColor } : {}),
251
+ }
252
+ }),
253
+ legend: { ...baseLegend, show: hasLegend },
254
+ grid: { ...baseGrid, bottom: gridBottom, right: gridRight },
255
+ ...(dataZoomLayout ? { dataZoom: dataZoomLayout.entries } : {}),
256
+ xAxis: {
257
+ ...baseXAxis,
258
+ min: niceMinX,
259
+ max: niceMaxX,
260
+ } as EChartsOption['xAxis'],
261
+ yAxis: {
262
+ ...baseYAxis,
263
+ min: niceMinY,
264
+ max: niceMaxY,
265
+ axisLabel: {
266
+ ...((baseYAxis as { axisLabel?: object }).axisLabel ?? {}),
267
+ // Re-derive the y-axis formatter at fusion time so RelativeData's
268
+ // percent formatter (written to `state.formatter`) flows through
269
+ // without rebuilding the structural option. Falls back to the
270
+ // structural `yFormatter` already baked in `baseYAxis.axisLabel`
271
+ // (which `String(value)` if neither is set).
272
+ ...(reactiveFormatter ? { formatter: reactiveFormatter } : {}),
273
+ },
274
+ } as EChartsOption['yAxis'],
275
+ tooltip: {
276
+ ...baseTooltip,
277
+ // Rebuild the tooltip formatter so the live y-axis formatter is
278
+ // applied to the y-coordinate in the (x, y) label. xFormatter is
279
+ // structural — relative is a values-axis concept, so xFormatter
280
+ // doesn't change under RelativeData.
281
+ formatter: buildReactiveScatterTooltipFormatter(
282
+ (baseTooltip as { formatter?: unknown }).formatter,
283
+ reactiveFormatter,
284
+ xFormatter,
285
+ ),
286
+ },
287
+ } as EChartsOption
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Lay out the `dataZoom` array for the scatter chart:
293
+ * - Detect whether any horizontal (x-axis) slider is present — if so
294
+ * lift it above the legend row when a legend is shown.
295
+ * - Detect whether any vertical (y-axis) slider is present — the
296
+ * caller reserves `grid.right` so the slider doesn't overlap the
297
+ * plot area.
298
+ *
299
+ * Returns `null` when there's no `dataZoom` so callers can skip the
300
+ * layout adjustment entirely. An entry is considered an "x-slider" if
301
+ * it has `xAxisIndex` set (or no axis index — defaults to x in ECharts).
302
+ * A "y-slider" has `yAxisIndex` set.
303
+ */
304
+ function layoutDataZoomForScatter(
305
+ dataZoom: unknown,
306
+ hasLegend: boolean,
307
+ ): { entries: unknown[]; hasXSlider: boolean; hasYSlider: boolean } | null {
308
+ if (!Array.isArray(dataZoom) || dataZoom.length === 0) return null
309
+ let hasXSlider = false
310
+ let hasYSlider = false
311
+ const entries = dataZoom.map((entry: unknown) => {
312
+ if (entry == null || typeof entry !== 'object') return entry
313
+ const dz = entry as {
314
+ type?: string
315
+ xAxisIndex?: unknown
316
+ yAxisIndex?: unknown
317
+ bottom?: number
318
+ }
319
+ if (dz.type !== 'slider') return dz
320
+ const targetsY = dz.yAxisIndex !== undefined
321
+ if (targetsY) {
322
+ hasYSlider = true
323
+ return dz
324
+ }
325
+ // Either explicit x or defaulted (ECharts defaults sliders to xAxis
326
+ // when no axis index is provided).
327
+ hasXSlider = true
328
+ if (hasLegend) {
329
+ return { ...dz, bottom: ZOOM_LAYOUT.sliderBottomWithLegend }
330
+ }
331
+ return dz
332
+ })
333
+ return { entries, hasXSlider, hasYSlider }
334
+ }
335
+
336
+ /**
337
+ * If a reactive (store-driven) y formatter is provided, re-build the
338
+ * scatter tooltip formatter using it. Otherwise, return the structural
339
+ * formatter unchanged so the original `xFormatter` / `yFormatter`
340
+ * baked into `scatterplotOptions` still applies. The structural
341
+ * formatter has stable identity per `scatterplotOptions` call, so this
342
+ * path doesn't churn ECharts on every render.
343
+ */
344
+ function buildReactiveScatterTooltipFormatter(
345
+ structuralFormatter: unknown,
346
+ reactiveYFormatter: ((value: number) => string) | undefined,
347
+ xFormatter: ((value: number) => string) | undefined,
348
+ ) {
349
+ if (!reactiveYFormatter) return structuralFormatter
350
+ return createTooltipFormatter((item) => {
351
+ const value = item.value as readonly [number, number] | undefined
352
+ const x = value?.[0]
353
+ const y = value?.[1]
354
+ const formattedX =
355
+ typeof x === 'number' ? (xFormatter ? xFormatter(x) : String(x)) : ''
356
+ const formattedY =
357
+ typeof y === 'number' ? reactiveYFormatter(y) : String(y ?? '')
358
+ const marker = typeof item.marker === 'string' ? item.marker : ''
359
+ const seriesName = item.seriesName ? `${item.seriesName}: ` : ''
360
+ return {
361
+ name: `(${formattedX}, ${formattedY})`,
362
+ seriesName,
363
+ marker,
364
+ value: '',
365
+ }
366
+ })
367
+ }
368
+
369
+ function buildScatterTooltipFormatter(
370
+ xFormatter: ((value: number) => string) | undefined,
371
+ yFormatter: ((value: number) => string) | undefined,
372
+ ) {
373
+ return createTooltipFormatter((item) => {
374
+ const value = item.value as readonly [number, number] | undefined
375
+ const x = value?.[0]
376
+ const y = value?.[1]
377
+ const formattedX =
378
+ typeof x === 'number' ? (xFormatter ? xFormatter(x) : String(x)) : ''
379
+ const formattedY =
380
+ typeof y === 'number' ? (yFormatter ? yFormatter(y) : String(y)) : ''
381
+ const marker = typeof item.marker === 'string' ? item.marker : ''
382
+ const seriesName = item.seriesName ? `${item.seriesName}: ` : ''
383
+ return {
384
+ name: `(${formattedX}, ${formattedY})`,
385
+ seriesName,
386
+ marker,
387
+ value: '',
388
+ }
389
+ })
390
+ }
391
+
392
+ function computeScatterBounds(seriesArr: ScatterplotWidgetData): {
393
+ niceMinX: number
394
+ niceMaxX: number
395
+ niceMinY: number
396
+ niceMaxY: number
397
+ } {
398
+ let minX = Infinity
399
+ let maxX = -Infinity
400
+ let minY = Infinity
401
+ let maxY = -Infinity
402
+ for (const series of seriesArr) {
403
+ for (const point of series) {
404
+ const x = point?.[0]
405
+ const y = point?.[1]
406
+ if (typeof x === 'number' && Number.isFinite(x)) {
407
+ if (x < minX) minX = x
408
+ if (x > maxX) maxX = x
409
+ }
410
+ if (typeof y === 'number' && Number.isFinite(y)) {
411
+ if (y < minY) minY = y
412
+ if (y > maxY) maxY = y
413
+ }
414
+ }
415
+ }
416
+ // Mirror bar's `computeNiceBounds`: clamp min to 0 when data is
417
+ // non-negative (gridline reads cleanly from zero), apply `niceNum` to
418
+ // negative mins, and floor max=0 to 1 so the chart always has range.
419
+ // Scatter can have free coordinates so we apply this per-axis.
420
+ const niceMaxX = Number.isFinite(maxX) ? (maxX <= 0 ? 1 : niceNum(maxX)) : 1
421
+ const niceMaxY = Number.isFinite(maxY) ? (maxY <= 0 ? 1 : niceNum(maxY)) : 1
422
+ const niceMinX = Number.isFinite(minX) ? (minX < 0 ? niceNum(minX) : 0) : 0
423
+ const niceMinY = Number.isFinite(minY) ? (minY < 0 ? niceNum(minY) : 0) : 0
424
+ return { niceMinX, niceMaxX, niceMinY, niceMaxY }
425
+ }
@@ -0,0 +1,17 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { render } from '@testing-library/react'
3
+ import { ScatterplotSkeleton } from './skeleton'
4
+
5
+ describe('<ScatterplotSkeleton>', () => {
6
+ it('renders the default 24 dots', () => {
7
+ const { container } = render(<ScatterplotSkeleton />)
8
+ expect(
9
+ container.querySelectorAll('.MuiSkeleton-root').length,
10
+ ).toBeGreaterThan(20)
11
+ })
12
+
13
+ it('honours custom count', () => {
14
+ const { container } = render(<ScatterplotSkeleton count={5} />)
15
+ expect(container.firstChild).not.toBeNull()
16
+ })
17
+ })
@@ -0,0 +1,84 @@
1
+ import { Box, Skeleton } from '@mui/material'
2
+ import type { SxProps, Theme } from '@mui/material'
3
+
4
+ const styles = {
5
+ container: {
6
+ display: 'flex',
7
+ alignItems: 'center',
8
+ justifyContent: 'space-between',
9
+ flexDirection: 'column',
10
+ gap: ({ spacing }) => spacing(1),
11
+ height: ({ spacing }) => spacing(38),
12
+ },
13
+ grid: {
14
+ position: 'relative',
15
+ flex: '1 1 auto',
16
+ width: '100%',
17
+ },
18
+ legend: {
19
+ display: 'flex',
20
+ alignItems: 'center',
21
+ gap: ({ spacing }) => spacing(2),
22
+ height: ({ spacing }) => spacing(5),
23
+ },
24
+ legendItem: {
25
+ display: 'flex',
26
+ alignItems: 'center',
27
+ gap: ({ spacing }) => spacing(1.5),
28
+ },
29
+ } satisfies Record<string, SxProps<Theme>>
30
+
31
+ // `sx` callback that needs runtime args — extracted at module scope so the
32
+ // `styles` object can satisfy `Record<string, SxProps<Theme>>` cleanly
33
+ // (function `sx`-with-args isn't assignable to plain `SxProps<Theme>`).
34
+ const dotSx = (top: string, left: string, size: number): SxProps<Theme> => ({
35
+ position: 'absolute',
36
+ top,
37
+ left,
38
+ width: size,
39
+ height: size,
40
+ borderRadius: '50%',
41
+ })
42
+
43
+ export interface ScatterplotSkeletonProps {
44
+ /** Number of dots to render. */
45
+ count?: number
46
+ }
47
+
48
+ /**
49
+ * Loading state for the Scatterplot widget. Mirrors a scatter chart's
50
+ * silhouette — a deterministic spread of small circular dots in the plot
51
+ * area plus a legend stub — so the skeleton reads as "a scatter chart"
52
+ * rather than a generic list. Matches bar/histogram/pie skeleton structure
53
+ * (grid stub + legend stub in a flex column).
54
+ */
55
+ export function ScatterplotSkeleton({ count = 24 }: ScatterplotSkeletonProps) {
56
+ // Deterministic pseudo-scatter positions so the skeleton doesn't flicker.
57
+ const dots = Array.from({ length: count }, (_, i) => {
58
+ const top = 10 + ((i * 37) % 80)
59
+ const left = 5 + ((i * 53) % 90)
60
+ const size = 8 + ((i * 7) % 6)
61
+ return { top: `${top}%`, left: `${left}%`, size }
62
+ })
63
+ return (
64
+ <Box sx={styles.container}>
65
+ <Box sx={styles.grid}>
66
+ {dots.map((d, i) => (
67
+ <Skeleton
68
+ key={`dot-${i}`}
69
+ variant='circular'
70
+ sx={dotSx(d.top, d.left, d.size)}
71
+ />
72
+ ))}
73
+ </Box>
74
+ <Box sx={styles.legend}>
75
+ {[0, 1].map((i) => (
76
+ <Box key={`legend-${i}`} sx={styles.legendItem}>
77
+ <Skeleton variant='circular' width={8} height={8} />
78
+ <Skeleton width={48} height={8} />
79
+ </Box>
80
+ ))}
81
+ </Box>
82
+ </Box>
83
+ )
84
+ }
@@ -0,0 +1,97 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { toRelativeScatterplotData } from './transforms'
3
+
4
+ describe('toRelativeScatterplotData', () => {
5
+ it('rewrites each tuple y as its share of the series total in 0-100, x stays raw', () => {
6
+ expect(
7
+ toRelativeScatterplotData([
8
+ [
9
+ [1, 25],
10
+ [2, 75],
11
+ ],
12
+ ]),
13
+ ).toEqual([
14
+ [
15
+ [1, 25],
16
+ [2, 75],
17
+ ],
18
+ ])
19
+ })
20
+
21
+ it('handles non-100 totals (5/20 = 25%)', () => {
22
+ expect(
23
+ toRelativeScatterplotData([
24
+ [
25
+ [10, 5],
26
+ [20, 15],
27
+ ],
28
+ ]),
29
+ ).toEqual([
30
+ [
31
+ [10, 25],
32
+ [20, 75],
33
+ ],
34
+ ])
35
+ })
36
+
37
+ it('handles multiple series independently', () => {
38
+ expect(
39
+ toRelativeScatterplotData([
40
+ [
41
+ [1, 3],
42
+ [2, 7],
43
+ ],
44
+ [
45
+ [10, 1],
46
+ [20, 1],
47
+ ],
48
+ ]),
49
+ ).toEqual([
50
+ [
51
+ [1, 30],
52
+ [2, 70],
53
+ ],
54
+ [
55
+ [10, 50],
56
+ [20, 50],
57
+ ],
58
+ ])
59
+ })
60
+
61
+ it('returns a series unchanged when its total y is zero', () => {
62
+ const input = [
63
+ [
64
+ [1, 0],
65
+ [2, 0],
66
+ ],
67
+ ]
68
+ expect(toRelativeScatterplotData(input)).toEqual(input)
69
+ })
70
+
71
+ it('produces signed share-of-magnitude percentages for mixed-sign y values', () => {
72
+ // Denominator is |1000| + |-990| = 1990
73
+ const out = toRelativeScatterplotData([
74
+ [
75
+ [1, 1000],
76
+ [2, -990],
77
+ ],
78
+ ]) as [number, number][][]
79
+ expect(out[0]?.[0]?.[0]).toBe(1)
80
+ expect(out[0]?.[0]?.[1]).toBeCloseTo(50.25126, 4)
81
+ expect(out[0]?.[1]?.[0]).toBe(2)
82
+ expect(out[0]?.[1]?.[1]).toBeCloseTo(-49.74874, 4)
83
+ })
84
+
85
+ it('returns the input unchanged when shape does not match [number, number][]', () => {
86
+ expect(toRelativeScatterplotData(null)).toBe(null)
87
+ expect(toRelativeScatterplotData('not an array')).toBe('not an array')
88
+ // Flat number array (histogram shape) isn't a tuple array — pass
89
+ // through unchanged so attaching the wrong transform to the wrong
90
+ // widget is a no-op rather than data corruption.
91
+ expect(toRelativeScatterplotData([[10, 20, 70]])).toEqual([[10, 20, 70]])
92
+ // Named-value (bar shape) — pass through.
93
+ expect(toRelativeScatterplotData([[{ name: 'a', value: 10 }]])).toEqual([
94
+ [{ name: 'a', value: 10 }],
95
+ ])
96
+ })
97
+ })
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Scatterplot-specific `RelativeData` transform. Scatter data is
3
+ * `[number, number][]` — each series is a list of `[x, y]` tuples.
4
+ * Relative is a values-axis concept, so this rewrites `y` to its
5
+ * share of the series's total `y` (0–100) and leaves `x` raw — x is
6
+ * coordinate space, not part of the cohort total.
7
+ *
8
+ * Pass to `<Widget.RelativeData transform={toRelativeScatterplotData} />`.
9
+ *
10
+ * The denominator is the sum of |y| across the series so mixed-sign
11
+ * y values produce sane signed shares-of-magnitude. A series whose
12
+ * total y-magnitude is zero (all-zero or empty input) is returned
13
+ * unchanged so a stalled or empty data set doesn't show misleading 0%
14
+ * values. Inputs whose shape isn't `[number, number][]` fall through
15
+ * untouched.
16
+ */
17
+ export const toRelativeScatterplotData = (input: unknown): unknown => {
18
+ if (!Array.isArray(input)) return input
19
+ return input.map((series: unknown): unknown => {
20
+ if (!isXyTupleArray(series)) return series
21
+ const total = series.reduce((acc, [, y]) => acc + Math.abs(y), 0)
22
+ if (total <= 0) return series
23
+ return series.map(([x, y]) => [x, (y / total) * 100] as [number, number])
24
+ })
25
+ }
26
+
27
+ function isXyTupleArray(v: unknown): v is [number, number][] {
28
+ if (!Array.isArray(v)) return false
29
+ return v.every(
30
+ (item) =>
31
+ Array.isArray(item) &&
32
+ item.length === 2 &&
33
+ typeof item[0] === 'number' &&
34
+ Number.isFinite(item[0]) &&
35
+ typeof item[1] === 'number' &&
36
+ Number.isFinite(item[1]),
37
+ )
38
+ }