@carto/ps-react-ui 4.7.0 → 4.8.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 (578) 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/merge-options-DCkkHZIf.js +34 -0
  16. package/dist/merge-options-DCkkHZIf.js.map +1 -0
  17. package/dist/{styles-BYTyKQFP.js → option-builders-F-c9ELi1.js} +25 -45
  18. package/dist/option-builders-F-c9ELi1.js.map +1 -0
  19. package/dist/png-item-CS4z1iSH.js +45 -0
  20. package/dist/png-item-CS4z1iSH.js.map +1 -0
  21. package/dist/range-DsqTjSpg.js +186 -0
  22. package/dist/range-DsqTjSpg.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-HIpXuq4G.js +390 -0
  30. package/dist/table-HIpXuq4G.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 +41 -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 +49 -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 +40 -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 +47 -0
  143. package/dist/types/widgets-v2/index.d.ts +107 -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 +50 -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 +19 -0
  166. package/dist/types/widgets-v2/range/range.d.ts +19 -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 +50 -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 +44 -0
  216. package/dist/types/widgets-v2/table/table-ui.d.ts +38 -0
  217. package/dist/types/widgets-v2/table/table.d.ts +50 -0
  218. package/dist/types/widgets-v2/table/types.d.ts +37 -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 +56 -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/utils/data-zoom-layout.d.ts +11 -0
  231. package/dist/types/widgets-v2/utils/index.d.ts +2 -0
  232. package/dist/types/widgets-v2/utils/merge-options.d.ts +12 -0
  233. package/dist/types/widgets-v2/utils/merge-options.test.d.ts +1 -0
  234. package/dist/types/widgets-v2/wrapper/index.d.ts +4 -0
  235. package/dist/types/widgets-v2/wrapper/labels.d.ts +6 -0
  236. package/dist/types/widgets-v2/wrapper/style.d.ts +111 -0
  237. package/dist/types/widgets-v2/wrapper/widget-actions.d.ts +22 -0
  238. package/dist/types/widgets-v2/wrapper/widget-content.d.ts +12 -0
  239. package/dist/types/widgets-v2/wrapper/widget-wrapper.d.ts +51 -0
  240. package/dist/use-transform-DXPN3nY7.js +110 -0
  241. package/dist/use-transform-DXPN3nY7.js.map +1 -0
  242. package/dist/widget-context-DTGO0Yta.js +13 -0
  243. package/dist/widget-context-DTGO0Yta.js.map +1 -0
  244. package/dist/widget-store-registry-_W4Z4xp-.js +178 -0
  245. package/dist/widget-store-registry-_W4Z4xp-.js.map +1 -0
  246. package/dist/widgets/actions.js +688 -668
  247. package/dist/widgets/actions.js.map +1 -1
  248. package/dist/widgets/bar.js +14 -13
  249. package/dist/widgets/bar.js.map +1 -1
  250. package/dist/widgets/histogram.js +38 -37
  251. package/dist/widgets/histogram.js.map +1 -1
  252. package/dist/widgets/pie.js +19 -18
  253. package/dist/widgets/pie.js.map +1 -1
  254. package/dist/widgets/scatterplot.js +8 -7
  255. package/dist/widgets/scatterplot.js.map +1 -1
  256. package/dist/widgets/timeseries.js +11 -10
  257. package/dist/widgets/timeseries.js.map +1 -1
  258. package/dist/widgets/utils.js +8 -7
  259. package/dist/widgets/utils.js.map +1 -1
  260. package/dist/widgets-v2/actions.js +43 -0
  261. package/dist/widgets-v2/actions.js.map +1 -0
  262. package/dist/widgets-v2/bar.js +327 -0
  263. package/dist/widgets-v2/bar.js.map +1 -0
  264. package/dist/widgets-v2/category.js +104 -0
  265. package/dist/widgets-v2/category.js.map +1 -0
  266. package/dist/widgets-v2/echart.js +57 -0
  267. package/dist/widgets-v2/echart.js.map +1 -0
  268. package/dist/widgets-v2/formula.js +74 -0
  269. package/dist/widgets-v2/formula.js.map +1 -0
  270. package/dist/widgets-v2/histogram.js +350 -0
  271. package/dist/widgets-v2/histogram.js.map +1 -0
  272. package/dist/widgets-v2/markdown.js +68 -0
  273. package/dist/widgets-v2/markdown.js.map +1 -0
  274. package/dist/widgets-v2/pie.js +381 -0
  275. package/dist/widgets-v2/pie.js.map +1 -0
  276. package/dist/widgets-v2/range.js +52 -0
  277. package/dist/widgets-v2/range.js.map +1 -0
  278. package/dist/widgets-v2/scatterplot.js +405 -0
  279. package/dist/widgets-v2/scatterplot.js.map +1 -0
  280. package/dist/widgets-v2/spread.js +72 -0
  281. package/dist/widgets-v2/spread.js.map +1 -0
  282. package/dist/widgets-v2/stores.js +42 -0
  283. package/dist/widgets-v2/stores.js.map +1 -0
  284. package/dist/widgets-v2/table.js +78 -0
  285. package/dist/widgets-v2/table.js.map +1 -0
  286. package/dist/widgets-v2/timeseries.js +352 -0
  287. package/dist/widgets-v2/timeseries.js.map +1 -0
  288. package/dist/widgets-v2/utils.js +7 -0
  289. package/dist/widgets-v2/utils.js.map +1 -0
  290. package/dist/widgets-v2.js +953 -0
  291. package/dist/widgets-v2.js.map +1 -0
  292. package/package.json +73 -5
  293. package/src/components/lasso-tool/chip.test.tsx +176 -0
  294. package/src/components/lasso-tool/lasso-tool-inline.test.tsx +171 -0
  295. package/src/components/lasso-tool/lasso-tool.test.tsx +198 -0
  296. package/src/components/list-data/list-data.test.tsx +73 -0
  297. package/src/components/no-data-alert/no-data-alert.test.tsx +38 -0
  298. package/src/components/responsive-drawer/responsive-drawer.test.tsx +68 -0
  299. package/src/widgets/actions/brush-toggle/brush-overlay.test.tsx +465 -0
  300. package/src/widgets/actions/brush-toggle/brush-overlay.tsx +24 -2
  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/histogram/config.ts +1 -3
  309. package/src/widgets/range/components/range-item.test.tsx +243 -0
  310. package/src/widgets/stores/widget-store-branches.test.ts +275 -0
  311. package/src/widgets/table/config.test.ts +65 -0
  312. package/src/widgets/utils/chart-config/option-builders.test.ts +188 -0
  313. package/src/widgets-v2/PERFORMANCE.md +189 -0
  314. package/src/widgets-v2/actions/brush-toggle/brush-toggle.test.tsx +180 -0
  315. package/src/widgets-v2/actions/brush-toggle/brush-toggle.tsx +154 -0
  316. package/src/widgets-v2/actions/brush-toggle/index.ts +3 -0
  317. package/src/widgets-v2/actions/brush-toggle/labels.ts +9 -0
  318. package/src/widgets-v2/actions/brush-toggle/style.ts +11 -0
  319. package/src/widgets-v2/actions/brush-toggle/transforms.test.ts +47 -0
  320. package/src/widgets-v2/actions/brush-toggle/transforms.ts +31 -0
  321. package/src/widgets-v2/actions/change-column/change-column-icon.tsx +14 -0
  322. package/src/widgets-v2/actions/change-column/change-column.test.tsx +59 -0
  323. package/src/widgets-v2/actions/change-column/change-column.tsx +180 -0
  324. package/src/widgets-v2/actions/change-column/index.ts +7 -0
  325. package/src/widgets-v2/actions/change-column/labels.ts +9 -0
  326. package/src/widgets-v2/actions/change-column/sortable-column-item.tsx +56 -0
  327. package/src/widgets-v2/actions/change-column/style.ts +32 -0
  328. package/src/widgets-v2/actions/change-column/types.ts +11 -0
  329. package/src/widgets-v2/actions/download/download.test.tsx +327 -0
  330. package/src/widgets-v2/actions/download/download.tsx +144 -0
  331. package/src/widgets-v2/actions/download/exports.test.tsx +198 -0
  332. package/src/widgets-v2/actions/download/exports.ts +115 -0
  333. package/src/widgets-v2/actions/download/icons.tsx +26 -0
  334. package/src/widgets-v2/actions/download/index.ts +13 -0
  335. package/src/widgets-v2/actions/download/labels.ts +16 -0
  336. package/src/widgets-v2/actions/download/png-item.test.tsx +72 -0
  337. package/src/widgets-v2/actions/download/png-item.tsx +52 -0
  338. package/src/widgets-v2/actions/download/style.ts +3 -0
  339. package/src/widgets-v2/actions/download/types.ts +32 -0
  340. package/src/widgets-v2/actions/fullscreen/fullscreen.test.tsx +150 -0
  341. package/src/widgets-v2/actions/fullscreen/fullscreen.tsx +230 -0
  342. package/src/widgets-v2/actions/fullscreen/index.ts +7 -0
  343. package/src/widgets-v2/actions/fullscreen/labels.ts +9 -0
  344. package/src/widgets-v2/actions/fullscreen/style.ts +59 -0
  345. package/src/widgets-v2/actions/fullscreen/types.ts +15 -0
  346. package/src/widgets-v2/actions/index.ts +82 -0
  347. package/src/widgets-v2/actions/lock-selection/index.ts +10 -0
  348. package/src/widgets-v2/actions/lock-selection/labels.ts +11 -0
  349. package/src/widgets-v2/actions/lock-selection/lock-selection.test.tsx +187 -0
  350. package/src/widgets-v2/actions/lock-selection/lock-selection.tsx +130 -0
  351. package/src/widgets-v2/actions/lock-selection/style.ts +11 -0
  352. package/src/widgets-v2/actions/lock-selection/transforms.ts +27 -0
  353. package/src/widgets-v2/actions/relative-data/index.ts +3 -0
  354. package/src/widgets-v2/actions/relative-data/labels.ts +9 -0
  355. package/src/widgets-v2/actions/relative-data/relative-data.test.tsx +71 -0
  356. package/src/widgets-v2/actions/relative-data/relative-data.tsx +107 -0
  357. package/src/widgets-v2/actions/relative-data/style.ts +11 -0
  358. package/src/widgets-v2/actions/relative-data/transforms.test.ts +151 -0
  359. package/src/widgets-v2/actions/relative-data/transforms.ts +70 -0
  360. package/src/widgets-v2/actions/searcher/filter.ts +28 -0
  361. package/src/widgets-v2/actions/searcher/index.ts +8 -0
  362. package/src/widgets-v2/actions/searcher/labels.ts +13 -0
  363. package/src/widgets-v2/actions/searcher/searcher-toggle.tsx +91 -0
  364. package/src/widgets-v2/actions/searcher/searcher.test.tsx +92 -0
  365. package/src/widgets-v2/actions/searcher/searcher.tsx +112 -0
  366. package/src/widgets-v2/actions/searcher/style.ts +15 -0
  367. package/src/widgets-v2/actions/stack-toggle/index.ts +3 -0
  368. package/src/widgets-v2/actions/stack-toggle/labels.ts +9 -0
  369. package/src/widgets-v2/actions/stack-toggle/stack-toggle.test.tsx +61 -0
  370. package/src/widgets-v2/actions/stack-toggle/stack-toggle.tsx +54 -0
  371. package/src/widgets-v2/actions/stack-toggle/style.ts +11 -0
  372. package/src/widgets-v2/actions/stack-toggle/transforms.test.ts +43 -0
  373. package/src/widgets-v2/actions/stack-toggle/transforms.ts +25 -0
  374. package/src/widgets-v2/actions/zoom-toggle/index.ts +9 -0
  375. package/src/widgets-v2/actions/zoom-toggle/labels.ts +9 -0
  376. package/src/widgets-v2/actions/zoom-toggle/style.ts +11 -0
  377. package/src/widgets-v2/actions/zoom-toggle/transforms.test.ts +148 -0
  378. package/src/widgets-v2/actions/zoom-toggle/transforms.ts +171 -0
  379. package/src/widgets-v2/actions/zoom-toggle/zoom-toggle.test.tsx +107 -0
  380. package/src/widgets-v2/actions/zoom-toggle/zoom-toggle.tsx +106 -0
  381. package/src/widgets-v2/bar/download.test.tsx +91 -0
  382. package/src/widgets-v2/bar/download.tsx +66 -0
  383. package/src/widgets-v2/bar/index.ts +10 -0
  384. package/src/widgets-v2/bar/options.test.ts +317 -0
  385. package/src/widgets-v2/bar/options.ts +326 -0
  386. package/src/widgets-v2/bar/skeleton.test.tsx +19 -0
  387. package/src/widgets-v2/bar/skeleton.tsx +69 -0
  388. package/src/widgets-v2/bar/types.ts +46 -0
  389. package/src/widgets-v2/category/category-ui.test.tsx +746 -0
  390. package/src/widgets-v2/category/category-ui.tsx +389 -0
  391. package/src/widgets-v2/category/category.relative-data.test.tsx +107 -0
  392. package/src/widgets-v2/category/category.stack-toggle.test.tsx +85 -0
  393. package/src/widgets-v2/category/category.test.tsx +305 -0
  394. package/src/widgets-v2/category/category.tsx +121 -0
  395. package/src/widgets-v2/category/components/category-bar-stacked.test.tsx +121 -0
  396. package/src/widgets-v2/category/components/category-bar-stacked.tsx +73 -0
  397. package/src/widgets-v2/category/components/category-bar.test.tsx +64 -0
  398. package/src/widgets-v2/category/components/category-bar.tsx +49 -0
  399. package/src/widgets-v2/category/components/category-legend.test.tsx +51 -0
  400. package/src/widgets-v2/category/components/category-legend.tsx +39 -0
  401. package/src/widgets-v2/category/components/category-row-multi.tsx +86 -0
  402. package/src/widgets-v2/category/components/category-row-other.test.tsx +28 -0
  403. package/src/widgets-v2/category/components/category-row-other.tsx +33 -0
  404. package/src/widgets-v2/category/components/category-row-single.tsx +76 -0
  405. package/src/widgets-v2/category/components/category-row-stacked.test.tsx +244 -0
  406. package/src/widgets-v2/category/components/category-row-stacked.tsx +99 -0
  407. package/src/widgets-v2/category/download.test.ts +71 -0
  408. package/src/widgets-v2/category/download.ts +54 -0
  409. package/src/widgets-v2/category/index.ts +32 -0
  410. package/src/widgets-v2/category/skeleton.test.tsx +26 -0
  411. package/src/widgets-v2/category/skeleton.tsx +74 -0
  412. package/src/widgets-v2/category/style.ts +290 -0
  413. package/src/widgets-v2/category/types.ts +54 -0
  414. package/src/widgets-v2/echart/echart-ui.test.tsx +232 -0
  415. package/src/widgets-v2/echart/echart-ui.tsx +184 -0
  416. package/src/widgets-v2/echart/echart.test.tsx +229 -0
  417. package/src/widgets-v2/echart/echart.tsx +199 -0
  418. package/src/widgets-v2/echart/index.ts +22 -0
  419. package/src/widgets-v2/echart/shared-resize-observer.test.ts +91 -0
  420. package/src/widgets-v2/echart/shared-resize-observer.ts +56 -0
  421. package/src/widgets-v2/echart/style.ts +8 -0
  422. package/src/widgets-v2/echart/use-chart-selection.test.tsx +118 -0
  423. package/src/widgets-v2/echart/use-chart-selection.ts +115 -0
  424. package/src/widgets-v2/formula/delta.tsx +61 -0
  425. package/src/widgets-v2/formula/download.test.tsx +65 -0
  426. package/src/widgets-v2/formula/download.tsx +69 -0
  427. package/src/widgets-v2/formula/formula-ui.test.tsx +91 -0
  428. package/src/widgets-v2/formula/formula-ui.tsx +66 -0
  429. package/src/widgets-v2/formula/formula.test.tsx +50 -0
  430. package/src/widgets-v2/formula/formula.tsx +34 -0
  431. package/src/widgets-v2/formula/index.ts +17 -0
  432. package/src/widgets-v2/formula/note.tsx +25 -0
  433. package/src/widgets-v2/formula/prefix.tsx +25 -0
  434. package/src/widgets-v2/formula/series.tsx +67 -0
  435. package/src/widgets-v2/formula/skeleton.test.tsx +21 -0
  436. package/src/widgets-v2/formula/skeleton.tsx +27 -0
  437. package/src/widgets-v2/formula/style.ts +31 -0
  438. package/src/widgets-v2/formula/subcomponents.test.tsx +107 -0
  439. package/src/widgets-v2/formula/suffix.tsx +25 -0
  440. package/src/widgets-v2/formula/types.ts +44 -0
  441. package/src/widgets-v2/formula/value.tsx +31 -0
  442. package/src/widgets-v2/histogram/download.test.ts +94 -0
  443. package/src/widgets-v2/histogram/download.ts +60 -0
  444. package/src/widgets-v2/histogram/index.ts +10 -0
  445. package/src/widgets-v2/histogram/options.test.ts +304 -0
  446. package/src/widgets-v2/histogram/options.ts +337 -0
  447. package/src/widgets-v2/histogram/skeleton.test.tsx +16 -0
  448. package/src/widgets-v2/histogram/skeleton.tsx +70 -0
  449. package/src/widgets-v2/histogram/transforms.test.ts +46 -0
  450. package/src/widgets-v2/histogram/transforms.ts +30 -0
  451. package/src/widgets-v2/histogram/types.ts +51 -0
  452. package/src/widgets-v2/index.ts +201 -0
  453. package/src/widgets-v2/markdown/download.test.ts +66 -0
  454. package/src/widgets-v2/markdown/download.ts +53 -0
  455. package/src/widgets-v2/markdown/index.ts +6 -0
  456. package/src/widgets-v2/markdown/markdown-content.test.tsx +155 -0
  457. package/src/widgets-v2/markdown/markdown-content.tsx +72 -0
  458. package/src/widgets-v2/markdown/markdown-ui.test.tsx +75 -0
  459. package/src/widgets-v2/markdown/markdown-ui.tsx +55 -0
  460. package/src/widgets-v2/markdown/markdown.test.tsx +39 -0
  461. package/src/widgets-v2/markdown/markdown.tsx +17 -0
  462. package/src/widgets-v2/markdown/skeleton.test.tsx +15 -0
  463. package/src/widgets-v2/markdown/skeleton.tsx +32 -0
  464. package/src/widgets-v2/markdown/style.ts +53 -0
  465. package/src/widgets-v2/markdown/types.ts +4 -0
  466. package/src/widgets-v2/note/labels.ts +9 -0
  467. package/src/widgets-v2/note/style.ts +26 -0
  468. package/src/widgets-v2/note/widget-note.test.tsx +158 -0
  469. package/src/widgets-v2/note/widget-note.tsx +172 -0
  470. package/src/widgets-v2/pie/download.test.ts +78 -0
  471. package/src/widgets-v2/pie/download.ts +55 -0
  472. package/src/widgets-v2/pie/index.ts +10 -0
  473. package/src/widgets-v2/pie/options.test.ts +585 -0
  474. package/src/widgets-v2/pie/options.ts +509 -0
  475. package/src/widgets-v2/pie/skeleton.test.tsx +17 -0
  476. package/src/widgets-v2/pie/skeleton.tsx +32 -0
  477. package/src/widgets-v2/pie/types.ts +55 -0
  478. package/src/widgets-v2/provider/widget-provider.test.tsx +119 -0
  479. package/src/widgets-v2/provider/widget-provider.tsx +111 -0
  480. package/src/widgets-v2/range/index.ts +4 -0
  481. package/src/widgets-v2/range/range-ui.test.tsx +130 -0
  482. package/src/widgets-v2/range/range-ui.tsx +211 -0
  483. package/src/widgets-v2/range/range.test.tsx +68 -0
  484. package/src/widgets-v2/range/range.tsx +46 -0
  485. package/src/widgets-v2/range/skeleton.test.tsx +17 -0
  486. package/src/widgets-v2/range/skeleton.tsx +47 -0
  487. package/src/widgets-v2/range/style.ts +41 -0
  488. package/src/widgets-v2/range/types.ts +37 -0
  489. package/src/widgets-v2/scatterplot/download.test.ts +71 -0
  490. package/src/widgets-v2/scatterplot/download.ts +54 -0
  491. package/src/widgets-v2/scatterplot/index.ts +11 -0
  492. package/src/widgets-v2/scatterplot/options.test.ts +399 -0
  493. package/src/widgets-v2/scatterplot/options.ts +421 -0
  494. package/src/widgets-v2/scatterplot/skeleton.test.tsx +17 -0
  495. package/src/widgets-v2/scatterplot/skeleton.tsx +84 -0
  496. package/src/widgets-v2/scatterplot/transforms.test.ts +97 -0
  497. package/src/widgets-v2/scatterplot/transforms.ts +38 -0
  498. package/src/widgets-v2/scatterplot/types.ts +55 -0
  499. package/src/widgets-v2/selection-summary/labels.ts +11 -0
  500. package/src/widgets-v2/selection-summary/selection-summary.test.tsx +53 -0
  501. package/src/widgets-v2/selection-summary/selection-summary.tsx +62 -0
  502. package/src/widgets-v2/selection-summary/style.ts +23 -0
  503. package/src/widgets-v2/spread/download.test.ts +64 -0
  504. package/src/widgets-v2/spread/download.ts +59 -0
  505. package/src/widgets-v2/spread/index.ts +6 -0
  506. package/src/widgets-v2/spread/separator.tsx +11 -0
  507. package/src/widgets-v2/spread/skeleton.test.tsx +17 -0
  508. package/src/widgets-v2/spread/skeleton.tsx +38 -0
  509. package/src/widgets-v2/spread/spread-ui.test.tsx +108 -0
  510. package/src/widgets-v2/spread/spread-ui.tsx +52 -0
  511. package/src/widgets-v2/spread/spread.test.tsx +50 -0
  512. package/src/widgets-v2/spread/spread.tsx +31 -0
  513. package/src/widgets-v2/spread/types.ts +27 -0
  514. package/src/widgets-v2/state/labels.test.ts +33 -0
  515. package/src/widgets-v2/state/labels.ts +20 -0
  516. package/src/widgets-v2/state/style.ts +25 -0
  517. package/src/widgets-v2/state/widget-state.test.tsx +294 -0
  518. package/src/widgets-v2/state/widget-state.tsx +184 -0
  519. package/src/widgets-v2/stores/index.ts +49 -0
  520. package/src/widgets-v2/stores/pipeline-middleware.test.ts +187 -0
  521. package/src/widgets-v2/stores/pipeline-middleware.ts +91 -0
  522. package/src/widgets-v2/stores/transforms.test.ts +162 -0
  523. package/src/widgets-v2/stores/transforms.ts +70 -0
  524. package/src/widgets-v2/stores/types.ts +64 -0
  525. package/src/widgets-v2/stores/use-echart-instance.test.tsx +91 -0
  526. package/src/widgets-v2/stores/use-echart-instance.ts +29 -0
  527. package/src/widgets-v2/stores/use-transform-enabled.test.tsx +127 -0
  528. package/src/widgets-v2/stores/use-transform-enabled.ts +25 -0
  529. package/src/widgets-v2/stores/use-transform.test.tsx +262 -0
  530. package/src/widgets-v2/stores/use-transform.ts +158 -0
  531. package/src/widgets-v2/stores/widget-context.test.tsx +58 -0
  532. package/src/widgets-v2/stores/widget-context.ts +15 -0
  533. package/src/widgets-v2/stores/widget-store-registry.test.ts +292 -0
  534. package/src/widgets-v2/stores/widget-store-registry.ts +248 -0
  535. package/src/widgets-v2/subheader/style.ts +12 -0
  536. package/src/widgets-v2/subheader/subheader.test.tsx +30 -0
  537. package/src/widgets-v2/subheader/subheader.tsx +16 -0
  538. package/src/widgets-v2/table/download.test.ts +75 -0
  539. package/src/widgets-v2/table/download.ts +47 -0
  540. package/src/widgets-v2/table/helpers.test.ts +214 -0
  541. package/src/widgets-v2/table/helpers.ts +136 -0
  542. package/src/widgets-v2/table/index.ts +23 -0
  543. package/src/widgets-v2/table/labels.tsx +41 -0
  544. package/src/widgets-v2/table/skeleton.test.tsx +26 -0
  545. package/src/widgets-v2/table/skeleton.tsx +65 -0
  546. package/src/widgets-v2/table/style.ts +46 -0
  547. package/src/widgets-v2/table/table-ui.test.tsx +200 -0
  548. package/src/widgets-v2/table/table-ui.tsx +331 -0
  549. package/src/widgets-v2/table/table.test.tsx +119 -0
  550. package/src/widgets-v2/table/table.tsx +174 -0
  551. package/src/widgets-v2/table/types.ts +44 -0
  552. package/src/widgets-v2/test-utils.ts +107 -0
  553. package/src/widgets-v2/timeseries/download.test.ts +95 -0
  554. package/src/widgets-v2/timeseries/download.ts +86 -0
  555. package/src/widgets-v2/timeseries/index.ts +10 -0
  556. package/src/widgets-v2/timeseries/options.test.ts +379 -0
  557. package/src/widgets-v2/timeseries/options.ts +341 -0
  558. package/src/widgets-v2/timeseries/skeleton.test.tsx +13 -0
  559. package/src/widgets-v2/timeseries/skeleton.tsx +76 -0
  560. package/src/widgets-v2/timeseries/types.ts +61 -0
  561. package/src/widgets-v2/toolbox/labels.ts +9 -0
  562. package/src/widgets-v2/toolbox/style.ts +33 -0
  563. package/src/widgets-v2/toolbox/toolbox.test.tsx +200 -0
  564. package/src/widgets-v2/toolbox/toolbox.tsx +309 -0
  565. package/src/widgets-v2/utils/data-zoom-layout.ts +26 -0
  566. package/src/widgets-v2/utils/index.ts +2 -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/wrapper/index.ts +14 -0
  570. package/src/widgets-v2/wrapper/labels.ts +11 -0
  571. package/src/widgets-v2/wrapper/style.ts +134 -0
  572. package/src/widgets-v2/wrapper/widget-actions.test.tsx +52 -0
  573. package/src/widgets-v2/wrapper/widget-actions.tsx +43 -0
  574. package/src/widgets-v2/wrapper/widget-content.test.tsx +27 -0
  575. package/src/widgets-v2/wrapper/widget-content.tsx +29 -0
  576. package/src/widgets-v2/wrapper/widget-wrapper.test.tsx +159 -0
  577. package/src/widgets-v2/wrapper/widget-wrapper.tsx +178 -0
  578. package/dist/styles-BYTyKQFP.js.map +0 -1
@@ -0,0 +1,144 @@
1
+ import { useCallback, useState, type ComponentType } from 'react'
2
+ import {
3
+ CircularProgress,
4
+ IconButton,
5
+ ListItemIcon,
6
+ ListItemText,
7
+ Menu,
8
+ MenuItem,
9
+ type SvgIconProps,
10
+ } from '@mui/material'
11
+ import DownloadIcon from '@mui/icons-material/FileDownload'
12
+ import { Tooltip } from '../../../components'
13
+ import { triggerLinkDownload } from './exports'
14
+ import type { DownloadItem } from './types'
15
+ import { DEFAULT_DOWNLOAD_LABELS, type DownloadLabels } from './labels'
16
+
17
+ export interface DownloadProps {
18
+ items: readonly DownloadItem[]
19
+ labels?: Partial<DownloadLabels>
20
+ icon?: ComponentType<SvgIconProps>
21
+ iconProps?: SvgIconProps
22
+ /**
23
+ * Fires when an item's `resolve()` rejects (or any step in the click-through
24
+ * path throws). The error is normalised to a real `Error` first. Surface
25
+ * to your telemetry / toast layer here; the trigger already shows a visual
26
+ * error indicator independently.
27
+ */
28
+ onError?: (err: Error) => void
29
+ }
30
+
31
+ export function Download({
32
+ items,
33
+ labels,
34
+ icon: Icon = DownloadIcon,
35
+ iconProps,
36
+ onError,
37
+ }: DownloadProps) {
38
+ const _labels = { ...DEFAULT_DOWNLOAD_LABELS, ...labels }
39
+ const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null)
40
+ const [isDownloading, setIsDownloading] = useState(false)
41
+ const [error, setError] = useState<Error | null>(null)
42
+
43
+ const open = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
44
+ // Clear any prior error when the user re-engages the menu — the next
45
+ // attempt is a fresh interaction, not a continuation of the failed one.
46
+ setError(null)
47
+ setAnchorEl(e.currentTarget)
48
+ }, [])
49
+ const close = useCallback(() => setAnchorEl(null), [])
50
+
51
+ const onSelect = useCallback(
52
+ (item: DownloadItem) => {
53
+ // Single-flight: ignore re-entry while a download is already in flight.
54
+ // The trigger is `disabled` and the menu is closed during that window,
55
+ // so this guard mostly defends against future call sites that bypass
56
+ // the menu (e.g. keyboard shortcuts).
57
+ if (item.disabled || isDownloading) return
58
+ close()
59
+ setIsDownloading(true)
60
+ setError(null)
61
+ item
62
+ .resolve()
63
+ .then(({ url, filename, revoke }) => {
64
+ // `item.filename` overrides what `resolve()` returns — lets consumers
65
+ // template the filename with widget metadata declaratively.
66
+ triggerLinkDownload({ url, filename: item.filename ?? filename })
67
+ // Defer revoke past the current task so Safari / Firefox-on-slow-disk
68
+ // can dispatch the download before the blob URL is invalidated.
69
+ // Synchronous revoke here can silently cancel the download.
70
+ if (revoke) setTimeout(revoke, 0)
71
+ })
72
+ .catch((err: unknown) => {
73
+ const e = err instanceof Error ? err : new Error(String(err))
74
+ setError(e)
75
+ onError?.(e)
76
+ })
77
+ .finally(() => {
78
+ setIsDownloading(false)
79
+ })
80
+ },
81
+ [close, isDownloading, onError],
82
+ )
83
+
84
+ const triggerLabel = isDownloading
85
+ ? _labels.loading
86
+ : error
87
+ ? _labels.error
88
+ : _labels.trigger
89
+ const triggerActive = Boolean(anchorEl) || isDownloading
90
+
91
+ return (
92
+ <>
93
+ <Tooltip title={triggerLabel}>
94
+ <IconButton
95
+ size='small'
96
+ aria-label={triggerLabel}
97
+ aria-haspopup='true'
98
+ aria-expanded={anchorEl ? 'true' : undefined}
99
+ aria-busy={isDownloading || undefined}
100
+ data-error={error ? 'true' : undefined}
101
+ disabled={isDownloading || items.length === 0}
102
+ onClick={open}
103
+ className={triggerActive ? 'active' : undefined}
104
+ >
105
+ {isDownloading ? (
106
+ <CircularProgress size={18} color='inherit' />
107
+ ) : (
108
+ <Icon fontSize='small' {...iconProps} />
109
+ )}
110
+ </IconButton>
111
+ </Tooltip>
112
+ <Menu
113
+ anchorEl={anchorEl}
114
+ open={Boolean(anchorEl)}
115
+ onClose={close}
116
+ variant='menu'
117
+ elevation={8}
118
+ anchorOrigin={{
119
+ vertical: 'bottom',
120
+ horizontal: 'right',
121
+ }}
122
+ transformOrigin={{
123
+ vertical: 'top',
124
+ horizontal: 'right',
125
+ }}
126
+ >
127
+ {items.map((item) => (
128
+ <MenuItem
129
+ key={item.id}
130
+ disabled={item.disabled}
131
+ onClick={() => void onSelect(item)}
132
+ >
133
+ {item.icon && (
134
+ <ListItemIcon sx={{ color: 'text.primary' }}>
135
+ {item.icon}
136
+ </ListItemIcon>
137
+ )}
138
+ <ListItemText>{item.label}</ListItemText>
139
+ </MenuItem>
140
+ ))}
141
+ </Menu>
142
+ </>
143
+ )
144
+ }
@@ -0,0 +1,198 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest'
2
+ import { render } from '@testing-library/react'
3
+
4
+ // Hoisted mock for html2canvas — tests reach into `mockToBlob` to switch
5
+ // between "blob produced" and "toBlob returned null" without re-mocking.
6
+ type ToBlobCb = (blob: Blob | null) => void
7
+ const mockToBlob = vi.fn((cb: ToBlobCb) => cb(new Blob(['png-bytes'])))
8
+
9
+ vi.mock('html2canvas', () => ({
10
+ default: () => Promise.resolve({ toBlob: mockToBlob }),
11
+ }))
12
+
13
+ import {
14
+ toCsvString,
15
+ downloadToCSV,
16
+ triggerLinkDownload,
17
+ downloadDOMToPNG,
18
+ sanitizeFilename,
19
+ } from './exports'
20
+ import { CSVIcon, PNGIcon } from './icons'
21
+
22
+ let csvText = ''
23
+
24
+ let revokeSpy: ReturnType<typeof vi.spyOn>
25
+
26
+ beforeEach(() => {
27
+ csvText = ''
28
+ mockToBlob.mockReset()
29
+ mockToBlob.mockImplementation((cb: ToBlobCb) => cb(new Blob(['png-bytes'])))
30
+ vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:mock')
31
+ revokeSpy = vi
32
+ .spyOn(URL, 'revokeObjectURL')
33
+ .mockImplementation(() => undefined)
34
+ const RealBlob = global.Blob
35
+ vi.stubGlobal(
36
+ 'Blob',
37
+ class extends RealBlob {
38
+ constructor(parts: BlobPart[], opts?: BlobPropertyBag) {
39
+ csvText = typeof parts[0] === 'string' ? parts[0] : ''
40
+ super(parts, opts)
41
+ }
42
+ },
43
+ )
44
+ })
45
+
46
+ describe('toCsvString', () => {
47
+ it('joins rows with newlines and cells with commas', () => {
48
+ expect(
49
+ toCsvString([
50
+ ['name', 'value'],
51
+ ['a', 1],
52
+ ['b', 2],
53
+ ]),
54
+ ).toBe('name,value\na,1\nb,2')
55
+ })
56
+
57
+ it('emits blank cells for null and undefined', () => {
58
+ expect(toCsvString([[null, undefined, 'x']])).toBe(',,x')
59
+ })
60
+
61
+ it('stringifies numbers and booleans', () => {
62
+ expect(toCsvString([[1, false, true]])).toBe('1,false,true')
63
+ })
64
+
65
+ it('JSON.stringifies objects and arrays', () => {
66
+ expect(toCsvString([[{ a: 1 }, [1, 2]]])).toBe('"{""a"":1}","[1,2]"')
67
+ })
68
+
69
+ it('quotes cells containing comma / quote / newline', () => {
70
+ expect(toCsvString([['a,b', 'he said "hi"', 'line1\nline2']])).toBe(
71
+ '"a,b","he said ""hi""","line1\nline2"',
72
+ )
73
+ })
74
+
75
+ describe('formula injection guard', () => {
76
+ it.each([
77
+ [
78
+ '=',
79
+ '=HYPERLINK("https://attacker.example")',
80
+ '\'=HYPERLINK("https://attacker.example")',
81
+ ],
82
+ ['+', '+1+1', "'+1+1"],
83
+ ['-', '-2+1', "'-2+1"],
84
+ ['@', '@SUM(A1:A2)', "'@SUM(A1:A2)"],
85
+ ['\\t', '\tleading-tab', "'\tleading-tab"],
86
+ ['\\r', '\rleading-cr', "'\rleading-cr"],
87
+ ])(
88
+ 'prefixes cells starting with %s with a single quote',
89
+ (_label, raw, expectedRaw) => {
90
+ // The escape step also quotes if the value contains comma/quote/newline,
91
+ // so we re-derive the exact wire representation from the raw expected.
92
+ const expected = /[",\n\r]/.test(expectedRaw)
93
+ ? `"${expectedRaw.replace(/"/g, '""')}"`
94
+ : expectedRaw
95
+ expect(toCsvString([[raw]])).toBe(expected)
96
+ },
97
+ )
98
+
99
+ it('does not touch values where the dangerous char is not leading', () => {
100
+ expect(toCsvString([['a=b']])).toBe('a=b')
101
+ expect(toCsvString([['1+2']])).toBe('1+2')
102
+ })
103
+ })
104
+ })
105
+
106
+ describe('downloadToCSV', () => {
107
+ it('serialises rows then wraps them in a URL handle with a working revoke', () => {
108
+ const handle = downloadToCSV([['x', 1]])
109
+ expect(handle.url).toBe('blob:mock')
110
+ expect(csvText).toBe('x,1')
111
+ handle.revoke?.()
112
+ expect(revokeSpy).toHaveBeenCalledWith('blob:mock')
113
+ })
114
+ })
115
+
116
+ describe('triggerLinkDownload', () => {
117
+ it('synthesises an anchor, clicks it, then removes it', () => {
118
+ const appendSpy = vi.spyOn(document.body, 'appendChild')
119
+ triggerLinkDownload({ url: 'blob:x', filename: 'demo.csv' })
120
+ expect(appendSpy).toHaveBeenCalled()
121
+ const anchor = appendSpy.mock.calls[0]?.[0] as HTMLAnchorElement
122
+ expect(anchor.href).toBe('blob:x')
123
+ expect(anchor.download).toBe('demo.csv')
124
+ expect(anchor.isConnected).toBe(false)
125
+ })
126
+
127
+ it('applies sanitizeFilename to the anchor download attribute', () => {
128
+ const appendSpy = vi.spyOn(document.body, 'appendChild')
129
+ const before = appendSpy.mock.calls.length
130
+ triggerLinkDownload({
131
+ url: 'blob:y',
132
+ filename: '..\\evil\r\nname.csv',
133
+ })
134
+ const anchor = appendSpy.mock.calls[before]?.[0] as HTMLAnchorElement
135
+ expect(anchor.download).toBe('.._evil__name.csv')
136
+ })
137
+ })
138
+
139
+ describe('sanitizeFilename', () => {
140
+ it('replaces path separators, NUL, and ASCII control chars with underscore', () => {
141
+ expect(sanitizeFilename('a/b\\c\x00\x07d\r\ne.csv')).toBe('a_b_c__d__e.csv')
142
+ })
143
+
144
+ it('clamps long filenames to 200 chars', () => {
145
+ const long = 'x'.repeat(500) + '.csv'
146
+ const out = sanitizeFilename(long)
147
+ expect(out.length).toBe(200)
148
+ expect(out.startsWith('x')).toBe(true)
149
+ })
150
+
151
+ it('falls back to "download" for empty or whitespace-only input', () => {
152
+ expect(sanitizeFilename('')).toBe('download')
153
+ // Whitespace stays — sanitizer only handles control bytes — but a leading
154
+ // NUL run that empties the string still falls back.
155
+ expect(sanitizeFilename('\x00\x00\x00')).toBe('___')
156
+ })
157
+ })
158
+
159
+ describe('downloadDOMToPNG', () => {
160
+ it('rasterises the element via html2canvas and resolves with a handle', async () => {
161
+ const handle = await downloadDOMToPNG({
162
+ element: document.createElement('div'),
163
+ })
164
+ expect(handle.url).toBe('blob:mock')
165
+ handle.revoke?.()
166
+ expect(revokeSpy).toHaveBeenCalledWith('blob:mock')
167
+ })
168
+
169
+ it('rejects when canvas.toBlob returns null', async () => {
170
+ mockToBlob.mockImplementation((cb: ToBlobCb) => cb(null))
171
+ await expect(
172
+ downloadDOMToPNG({ element: document.createElement('div') }),
173
+ ).rejects.toThrow(/toBlob/)
174
+ })
175
+
176
+ it('forwards pixelRatio and backgroundColor overrides', async () => {
177
+ // We can't easily inspect the call args without re-hoisting the mock,
178
+ // so just guard the happy path still works with overrides supplied.
179
+ const handle = await downloadDOMToPNG({
180
+ element: document.createElement('div'),
181
+ pixelRatio: 4,
182
+ backgroundColor: '#fff',
183
+ })
184
+ expect(handle.url).toBe('blob:mock')
185
+ })
186
+ })
187
+
188
+ describe('download icons', () => {
189
+ it('CSVIcon renders an SVG element', () => {
190
+ const { container } = render(<CSVIcon fontSize='small' />)
191
+ expect(container.querySelector('svg')).not.toBeNull()
192
+ })
193
+
194
+ it('PNGIcon renders an SVG element', () => {
195
+ const { container } = render(<PNGIcon fontSize='small' />)
196
+ expect(container.querySelector('svg')).not.toBeNull()
197
+ })
198
+ })
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Pure download helpers — no React, no DOM lifecycle outside the link click.
3
+ * Component layer in {@link ./download.tsx} orchestrates these.
4
+ */
5
+
6
+ import html2canvas from 'html2canvas'
7
+
8
+ export interface DownloadHandle {
9
+ url: string
10
+ revoke: () => void
11
+ }
12
+
13
+ // Leading characters that Excel / Sheets / Numbers interpret as a formula
14
+ // when the CSV is opened. Prefixing the cell with a single quote forces the
15
+ // spreadsheet app to render it as plain text. Tab and CR are included
16
+ // because some tooling strips leading whitespace before formula evaluation.
17
+ const CSV_FORMULA_PREFIX = /^[=+\-@\t\r]/
18
+
19
+ export function toCsvString(rows: readonly (readonly unknown[])[]): string {
20
+ const escape = (cell: unknown): string => {
21
+ if (cell == null) return ''
22
+ let value =
23
+ typeof cell === 'string'
24
+ ? cell
25
+ : typeof cell === 'number' || typeof cell === 'boolean'
26
+ ? String(cell)
27
+ : JSON.stringify(cell)
28
+ if (CSV_FORMULA_PREFIX.test(value)) value = `'${value}`
29
+ if (/[",\n\r]/.test(value)) return `"${value.replace(/"/g, '""')}"`
30
+ return value
31
+ }
32
+ return rows.map((row) => row.map(escape).join(',')).join('\n')
33
+ }
34
+
35
+ export function downloadToCSV(
36
+ rows: readonly (readonly unknown[])[],
37
+ ): DownloadHandle {
38
+ const csv = toCsvString(rows)
39
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' })
40
+ const url = URL.createObjectURL(blob)
41
+ return { url, revoke: () => URL.revokeObjectURL(url) }
42
+ }
43
+
44
+ // Path separators, NUL, and ASCII control chars. Filenames containing these
45
+ // can confuse the browser download stack and historically enabled
46
+ // Content-Disposition-style trickery.
47
+ // eslint-disable-next-line no-control-regex
48
+ const UNSAFE_FILENAME_CHARS = /[\\/\x00-\x1f\x7f]/g
49
+ const MAX_FILENAME_LENGTH = 200
50
+
51
+ export function sanitizeFilename(name: string): string {
52
+ return (
53
+ (name ?? '')
54
+ .replace(UNSAFE_FILENAME_CHARS, '_')
55
+ .slice(0, MAX_FILENAME_LENGTH) || 'download'
56
+ )
57
+ }
58
+
59
+ /**
60
+ * Triggers a browser download for the given URL by synthesizing an anchor,
61
+ * clicking it, and removing it. Pure DOM — no React. The filename is run
62
+ * through {@link sanitizeFilename} before being assigned to `a.download`.
63
+ */
64
+ export function triggerLinkDownload(args: {
65
+ url: string
66
+ filename: string
67
+ }): void {
68
+ const { url, filename } = args
69
+ const a = document.createElement('a')
70
+ a.href = url
71
+ a.download = sanitizeFilename(filename)
72
+ a.style.display = 'none'
73
+ document.body.appendChild(a)
74
+ a.click()
75
+ a.remove()
76
+ }
77
+
78
+ export interface DownloadDOMToPNGOptions {
79
+ element: HTMLElement
80
+ /** html2canvas `scale`. Default 2 — crisp on hi-DPI displays. */
81
+ pixelRatio?: number
82
+ /**
83
+ * html2canvas `backgroundColor`. Default `null` (transparent) so the PNG
84
+ * inherits whatever surface it lands on when the user pastes it.
85
+ */
86
+ backgroundColor?: string | null
87
+ }
88
+
89
+ /**
90
+ * Rasterise an HTMLElement to PNG via `html2canvas` and wrap the result in
91
+ * a `DownloadHandle`. The caller is responsible for calling `handle.revoke`
92
+ * after the link click is dispatched. Used by per-widget download configs
93
+ * to power the PNG menu item.
94
+ */
95
+ export async function downloadDOMToPNG(
96
+ opts: DownloadDOMToPNGOptions,
97
+ ): Promise<DownloadHandle> {
98
+ const { element, pixelRatio = 2, backgroundColor = null } = opts
99
+ const canvas = await html2canvas(element, {
100
+ scale: pixelRatio,
101
+ backgroundColor,
102
+ useCORS: true,
103
+ logging: false,
104
+ })
105
+ return new Promise<DownloadHandle>((resolve, reject) => {
106
+ canvas.toBlob((blob) => {
107
+ if (!blob) {
108
+ reject(new Error('[widgets-v2] toBlob() returned null'))
109
+ return
110
+ }
111
+ const url = URL.createObjectURL(blob)
112
+ resolve({ url, revoke: () => URL.revokeObjectURL(url) })
113
+ }, 'image/png')
114
+ })
115
+ }
@@ -0,0 +1,26 @@
1
+ import { SvgIcon, type SvgIconProps } from '@mui/material'
2
+ import ImageOutlined from '@mui/icons-material/ImageOutlined'
3
+
4
+ /**
5
+ * Generic "image" glyph used for the PNG download item. Wraps MUI's
6
+ * `ImageOutlined` so callers can swap it via the per-widget download
7
+ * config's `icon` override without pulling MUI directly.
8
+ */
9
+ export function PNGIcon(props: SvgIconProps) {
10
+ return <ImageOutlined {...props} />
11
+ }
12
+
13
+ /**
14
+ * "CSV" rectangle with the letters spelled inside — matches v1 visual and is
15
+ * easier to recognise in a download menu than a generic table glyph.
16
+ */
17
+ export function CSVIcon(props: SvgIconProps) {
18
+ return (
19
+ <SvgIcon viewBox='0 0 18 18' {...props}>
20
+ <path
21
+ fill='currentColor'
22
+ d='M4.313 11.25h2.25v-1.125H4.688v-2.25h1.875V6.75h-2.25a.726.726 0 0 0-.535.216.726.726 0 0 0-.216.534v3c0 .213.072.39.216.534a.726.726 0 0 0 .534.216Zm2.925 0h2.25c.212 0 .39-.072.534-.216a.726.726 0 0 0 .216-.534V9.375a.931.931 0 0 0-.216-.59.658.658 0 0 0-.534-.273H8.363v-.637h1.875V6.75h-2.25a.726.726 0 0 0-.535.216.726.726 0 0 0-.216.534v1.125c0 .213.072.403.216.572a.675.675 0 0 0 .534.253h1.126v.675H7.238v1.125Zm4.95 0h1.124l1.313-4.5H13.5l-.75 2.588L12 6.75h-1.125l1.313 4.5ZM3 15c-.413 0-.766-.147-1.06-.44a1.445 1.445 0 0 1-.44-1.06v-9c0-.412.147-.766.44-1.06C2.235 3.148 2.588 3 3 3h12c.412 0 .766.147 1.06.44.293.294.44.648.44 1.06v9c0 .412-.147.766-.44 1.06-.294.293-.647.44-1.06.44H3Zm0-1.5h12v-9H3v9Z'
23
+ />
24
+ </SvgIcon>
25
+ )
26
+ }
@@ -0,0 +1,13 @@
1
+ export { Download, type DownloadProps } from './download'
2
+ export {
3
+ downloadToCSV,
4
+ downloadDOMToPNG,
5
+ toCsvString,
6
+ triggerLinkDownload,
7
+ type DownloadHandle,
8
+ type DownloadDOMToPNGOptions,
9
+ } from './exports'
10
+ export { CSVIcon, PNGIcon } from './icons'
11
+ export { buildPngDownloadItem, type BuildPngDownloadItemArgs } from './png-item'
12
+ export type { DownloadItem } from './types'
13
+ export { DEFAULT_DOWNLOAD_LABELS, type DownloadLabels } from './labels'
@@ -0,0 +1,16 @@
1
+ export interface DownloadLabels {
2
+ trigger: string
3
+ /** Tooltip / aria-label shown on the trigger while a download is in flight. */
4
+ loading: string
5
+ /**
6
+ * Tooltip / aria-label shown on the trigger after a download failed. Cleared
7
+ * the next time the menu opens.
8
+ */
9
+ error: string
10
+ }
11
+
12
+ export const DEFAULT_DOWNLOAD_LABELS: DownloadLabels = {
13
+ trigger: 'Download',
14
+ loading: 'Downloading…',
15
+ error: 'Download failed',
16
+ }
@@ -0,0 +1,72 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest'
2
+ import { buildPngDownloadItem } from './png-item'
3
+
4
+ type ToBlobCb = (blob: Blob | null) => void
5
+ const mockToBlob = vi.fn((cb: ToBlobCb) => cb(new Blob(['png-bytes'])))
6
+
7
+ vi.mock('html2canvas', () => ({
8
+ default: () => Promise.resolve({ toBlob: mockToBlob }),
9
+ }))
10
+
11
+ let revokeSpy: ReturnType<typeof vi.spyOn>
12
+
13
+ beforeEach(() => {
14
+ mockToBlob.mockReset()
15
+ mockToBlob.mockImplementation((cb: ToBlobCb) => cb(new Blob(['png-bytes'])))
16
+ vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:png')
17
+ revokeSpy = vi
18
+ .spyOn(URL, 'revokeObjectURL')
19
+ .mockImplementation(() => undefined)
20
+ })
21
+
22
+ describe('buildPngDownloadItem', () => {
23
+ it('returns an item with the canonical PNG shape (id, label, icon)', () => {
24
+ const item = buildPngDownloadItem({
25
+ filename: 'demo',
26
+ getCaptureEl: () => document.createElement('div'),
27
+ })
28
+ expect(item.id).toBe('png')
29
+ expect(item.label).toBe('PNG')
30
+ expect(item.icon).toBeTruthy()
31
+ })
32
+
33
+ it('honours a label override', () => {
34
+ const item = buildPngDownloadItem({
35
+ filename: 'demo',
36
+ getCaptureEl: () => document.createElement('div'),
37
+ label: 'Export image',
38
+ })
39
+ expect(item.label).toBe('Export image')
40
+ })
41
+
42
+ it('resolve() rasterises the capture element and returns a handle with .png filename', async () => {
43
+ const item = buildPngDownloadItem({
44
+ filename: 'sales',
45
+ getCaptureEl: () => document.createElement('div'),
46
+ })
47
+ const handle = await item.resolve()
48
+ expect(handle.url).toBe('blob:png')
49
+ expect(handle.filename).toBe('sales.png')
50
+ handle.revoke?.()
51
+ expect(revokeSpy).toHaveBeenCalledWith('blob:png')
52
+ })
53
+
54
+ it('resolve() rejects when getCaptureEl returns null', async () => {
55
+ const item = buildPngDownloadItem({
56
+ filename: 'demo',
57
+ getCaptureEl: () => null,
58
+ })
59
+ await expect(item.resolve()).rejects.toThrow(/No PNG capture element/)
60
+ })
61
+
62
+ it('forwards pixelRatio and backgroundColor overrides', async () => {
63
+ const item = buildPngDownloadItem({
64
+ filename: 'sales',
65
+ getCaptureEl: () => document.createElement('div'),
66
+ pixelRatio: 4,
67
+ backgroundColor: '#fff',
68
+ })
69
+ const handle = await item.resolve()
70
+ expect(handle.filename).toBe('sales.png')
71
+ })
72
+ })
@@ -0,0 +1,52 @@
1
+ import { downloadDOMToPNG } from './exports'
2
+ import { PNGIcon } from './icons'
3
+ import type { DownloadItem } from './types'
4
+
5
+ export interface BuildPngDownloadItemArgs {
6
+ /** Base filename (without extension). The item appends `.png`. */
7
+ filename: string
8
+ /**
9
+ * Reads the capture element to rasterise. Called at click time so the
10
+ * download config doesn't capture a stale reference. Wire it to
11
+ * `() => getCaptureEl(id)` from `widgets-v2/stores`.
12
+ */
13
+ getCaptureEl: () => HTMLElement | null
14
+ /** html2canvas `scale`. Default 2. */
15
+ pixelRatio?: number
16
+ /** html2canvas `backgroundColor`. Default transparent (`null`). */
17
+ backgroundColor?: string | null
18
+ /** Override the menu label. Default `'PNG'`. */
19
+ label?: string
20
+ }
21
+
22
+ /**
23
+ * Builds the standard PNG `DownloadItem` used by every per-widget download
24
+ * config. Centralised so the menu label, icon, error message, and filename
25
+ * suffix stay consistent across widgets without each `create*DownloadConfig`
26
+ * re-deriving the same shape.
27
+ */
28
+ export function buildPngDownloadItem(
29
+ args: BuildPngDownloadItemArgs,
30
+ ): DownloadItem {
31
+ return {
32
+ id: 'png',
33
+ label: args.label ?? 'PNG',
34
+ icon: <PNGIcon fontSize='small' />,
35
+ resolve: async () => {
36
+ const el = args.getCaptureEl()
37
+ if (!el) {
38
+ throw new Error('[widgets-v2] No PNG capture element available')
39
+ }
40
+ const handle = await downloadDOMToPNG({
41
+ element: el,
42
+ pixelRatio: args.pixelRatio,
43
+ backgroundColor: args.backgroundColor,
44
+ })
45
+ return {
46
+ url: handle.url,
47
+ filename: `${args.filename}.png`,
48
+ revoke: handle.revoke,
49
+ }
50
+ },
51
+ }
52
+ }
@@ -0,0 +1,3 @@
1
+ import type { SxProps, Theme } from '@mui/material'
2
+
3
+ export const styles = {} satisfies Record<string, SxProps<Theme>>
@@ -0,0 +1,32 @@
1
+ import type { ReactElement } from 'react'
2
+
3
+ export interface DownloadItem {
4
+ /** Unique key (also used as menu label key). */
5
+ id: string
6
+ /** Display label in the menu. */
7
+ label: string
8
+ /**
9
+ * Optional leading icon shown next to the label. Pass a rendered element
10
+ * (e.g. `<DownloadIcon fontSize='small' />`) so consumers can fully
11
+ * control sizing / colour. Rendered inside an MUI `<ListItemIcon>`.
12
+ */
13
+ icon?: ReactElement
14
+ /**
15
+ * When `true`, the menu item is rendered but non-clickable (greyed out).
16
+ * Useful for items whose underlying export is unavailable in the current
17
+ * widget state (no rows, missing permission, etc.).
18
+ */
19
+ disabled?: boolean
20
+ /**
21
+ * Override for the resolved filename. When set, `resolve()`'s `filename`
22
+ * is ignored and this value is used instead — handy when the consumer
23
+ * wants to template the filename with widget metadata without having to
24
+ * close over it inside `resolve()`.
25
+ */
26
+ filename?: string
27
+ /**
28
+ * Resolves to a Blob URL the trigger will click. Optional `revoke` is
29
+ * called after the click is dispatched.
30
+ */
31
+ resolve: () => Promise<{ url: string; filename: string; revoke?: () => void }>
32
+ }