@rowsncolumns/charts 27.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/LICENSE.md +100 -0
  2. package/Readme.md +81 -0
  3. package/dist/cjs/atoms/editing-chart.atom.d.ts +13 -0
  4. package/dist/cjs/atoms/editing-chart.atom.d.ts.map +1 -0
  5. package/dist/cjs/atoms/editing-chart.atom.js +26 -0
  6. package/dist/cjs/basic-chart/basic-chart.d.ts +5 -0
  7. package/dist/cjs/basic-chart/basic-chart.d.ts.map +1 -0
  8. package/dist/cjs/basic-chart/basic-chart.js +2250 -0
  9. package/dist/cjs/basic-chart/helpers.d.ts +12 -0
  10. package/dist/cjs/basic-chart/helpers.d.ts.map +1 -0
  11. package/dist/cjs/basic-chart/helpers.js +105 -0
  12. package/dist/cjs/basic-chart/index.d.ts +5 -0
  13. package/dist/cjs/basic-chart/index.d.ts.map +1 -0
  14. package/dist/cjs/basic-chart/index.js +12 -0
  15. package/dist/cjs/components/dialogs/chart-editor-dialog.d.ts +5 -0
  16. package/dist/cjs/components/dialogs/chart-editor-dialog.d.ts.map +1 -0
  17. package/dist/cjs/components/dialogs/chart-editor-dialog.js +25 -0
  18. package/dist/cjs/components/editors/chart-editor.d.ts +10 -0
  19. package/dist/cjs/components/editors/chart-editor.d.ts.map +1 -0
  20. package/dist/cjs/components/editors/chart-editor.js +631 -0
  21. package/dist/cjs/components/editors/chart-range-input.d.ts +17 -0
  22. package/dist/cjs/components/editors/chart-range-input.d.ts.map +1 -0
  23. package/dist/cjs/components/editors/chart-range-input.js +16 -0
  24. package/dist/cjs/config.d.ts +19 -0
  25. package/dist/cjs/config.d.ts.map +1 -0
  26. package/dist/cjs/config.js +196 -0
  27. package/dist/cjs/hooks/use-chart-editor.d.ts +8 -0
  28. package/dist/cjs/hooks/use-chart-editor.d.ts.map +1 -0
  29. package/dist/cjs/hooks/use-chart-editor.js +29 -0
  30. package/dist/cjs/hooks/use-charts.d.ts +35 -0
  31. package/dist/cjs/hooks/use-charts.d.ts.map +1 -0
  32. package/dist/cjs/hooks/use-charts.js +67 -0
  33. package/dist/cjs/hooks/use-on-create-chart.d.ts +13 -0
  34. package/dist/cjs/hooks/use-on-create-chart.d.ts.map +1 -0
  35. package/dist/cjs/hooks/use-on-create-chart.js +24 -0
  36. package/dist/cjs/hooks/use-on-delete-chart.d.ts +8 -0
  37. package/dist/cjs/hooks/use-on-delete-chart.d.ts.map +1 -0
  38. package/dist/cjs/hooks/use-on-delete-chart.js +19 -0
  39. package/dist/cjs/hooks/use-on-move-chart.d.ts +8 -0
  40. package/dist/cjs/hooks/use-on-move-chart.d.ts.map +1 -0
  41. package/dist/cjs/hooks/use-on-move-chart.js +15 -0
  42. package/dist/cjs/hooks/use-on-resize-chart.d.ts +8 -0
  43. package/dist/cjs/hooks/use-on-resize-chart.d.ts.map +1 -0
  44. package/dist/cjs/hooks/use-on-resize-chart.js +15 -0
  45. package/dist/cjs/hooks/use-on-update-chart.d.ts +9 -0
  46. package/dist/cjs/hooks/use-on-update-chart.d.ts.map +1 -0
  47. package/dist/cjs/hooks/use-on-update-chart.js +15 -0
  48. package/dist/cjs/index.d.ts +11 -0
  49. package/dist/cjs/index.d.ts.map +1 -0
  50. package/dist/cjs/index.js +18 -0
  51. package/dist/cjs/interface/create-chart.d.ts +9 -0
  52. package/dist/cjs/interface/create-chart.d.ts.map +1 -0
  53. package/dist/cjs/interface/create-chart.js +110 -0
  54. package/dist/cjs/interface/delete-chart.d.ts +4 -0
  55. package/dist/cjs/interface/delete-chart.d.ts.map +1 -0
  56. package/dist/cjs/interface/delete-chart.js +26 -0
  57. package/dist/cjs/interface/move-chart.d.ts +5 -0
  58. package/dist/cjs/interface/move-chart.d.ts.map +1 -0
  59. package/dist/cjs/interface/move-chart.js +28 -0
  60. package/dist/cjs/interface/resize-chart.d.ts +5 -0
  61. package/dist/cjs/interface/resize-chart.d.ts.map +1 -0
  62. package/dist/cjs/interface/resize-chart.js +30 -0
  63. package/dist/cjs/interface/update-chart.d.ts +4 -0
  64. package/dist/cjs/interface/update-chart.d.ts.map +1 -0
  65. package/dist/cjs/interface/update-chart.js +29 -0
  66. package/dist/cjs/plotly/basic-chart/basic-chart.d.ts +5 -0
  67. package/dist/cjs/plotly/basic-chart/basic-chart.d.ts.map +1 -0
  68. package/dist/cjs/plotly/basic-chart/basic-chart.js +1586 -0
  69. package/dist/cjs/plotly/basic-chart/helpers.d.ts +20 -0
  70. package/dist/cjs/plotly/basic-chart/helpers.d.ts.map +1 -0
  71. package/dist/cjs/plotly/basic-chart/helpers.js +114 -0
  72. package/dist/cjs/plotly/basic-chart/index.d.ts +5 -0
  73. package/dist/cjs/plotly/basic-chart/index.d.ts.map +1 -0
  74. package/dist/cjs/plotly/basic-chart/index.js +12 -0
  75. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
  76. package/dist/cjs/types.d.ts +27 -0
  77. package/dist/cjs/types.d.ts.map +1 -0
  78. package/dist/cjs/types.js +2 -0
  79. package/dist/cjs/utils/aggregation.d.ts +6 -0
  80. package/dist/cjs/utils/aggregation.d.ts.map +1 -0
  81. package/dist/cjs/utils/aggregation.js +127 -0
  82. package/dist/cjs/utils/bar-layout.d.ts +57 -0
  83. package/dist/cjs/utils/bar-layout.d.ts.map +1 -0
  84. package/dist/cjs/utils/bar-layout.js +81 -0
  85. package/dist/cjs/utils/boxplot.d.ts +62 -0
  86. package/dist/cjs/utils/boxplot.d.ts.map +1 -0
  87. package/dist/cjs/utils/boxplot.js +105 -0
  88. package/dist/cjs/utils/bubble.d.ts +52 -0
  89. package/dist/cjs/utils/bubble.d.ts.map +1 -0
  90. package/dist/cjs/utils/bubble.js +139 -0
  91. package/dist/cjs/utils/chart-spec-utils.d.ts +67 -0
  92. package/dist/cjs/utils/chart-spec-utils.d.ts.map +1 -0
  93. package/dist/cjs/utils/chart-spec-utils.js +355 -0
  94. package/dist/cjs/utils/echarts-gl-loader.d.ts +11 -0
  95. package/dist/cjs/utils/echarts-gl-loader.d.ts.map +1 -0
  96. package/dist/cjs/utils/echarts-gl-loader.js +59 -0
  97. package/dist/cjs/utils/error-bars.d.ts +58 -0
  98. package/dist/cjs/utils/error-bars.d.ts.map +1 -0
  99. package/dist/cjs/utils/error-bars.js +154 -0
  100. package/dist/cjs/utils/gradient.d.ts +52 -0
  101. package/dist/cjs/utils/gradient.d.ts.map +1 -0
  102. package/dist/cjs/utils/gradient.js +93 -0
  103. package/dist/cjs/utils/hierarchy.d.ts +28 -0
  104. package/dist/cjs/utils/hierarchy.d.ts.map +1 -0
  105. package/dist/cjs/utils/hierarchy.js +186 -0
  106. package/dist/cjs/utils/histogram.d.ts +46 -0
  107. package/dist/cjs/utils/histogram.d.ts.map +1 -0
  108. package/dist/cjs/utils/histogram.js +125 -0
  109. package/dist/cjs/utils/of-pie.d.ts +101 -0
  110. package/dist/cjs/utils/of-pie.d.ts.map +1 -0
  111. package/dist/cjs/utils/of-pie.js +159 -0
  112. package/dist/cjs/utils/overrides.d.ts +12 -0
  113. package/dist/cjs/utils/overrides.d.ts.map +1 -0
  114. package/dist/cjs/utils/overrides.js +171 -0
  115. package/dist/cjs/utils/radar.d.ts +37 -0
  116. package/dist/cjs/utils/radar.d.ts.map +1 -0
  117. package/dist/cjs/utils/radar.js +70 -0
  118. package/dist/cjs/utils/region-map-loader.d.ts +22 -0
  119. package/dist/cjs/utils/region-map-loader.d.ts.map +1 -0
  120. package/dist/cjs/utils/region-map-loader.js +174 -0
  121. package/dist/cjs/utils/region-map.d.ts +100 -0
  122. package/dist/cjs/utils/region-map.d.ts.map +1 -0
  123. package/dist/cjs/utils/region-map.js +327 -0
  124. package/dist/cjs/utils/stock.d.ts +64 -0
  125. package/dist/cjs/utils/stock.d.ts.map +1 -0
  126. package/dist/cjs/utils/stock.js +153 -0
  127. package/dist/cjs/utils/three-d.d.ts +96 -0
  128. package/dist/cjs/utils/three-d.d.ts.map +1 -0
  129. package/dist/cjs/utils/three-d.js +191 -0
  130. package/dist/cjs/utils/trendline.d.ts +41 -0
  131. package/dist/cjs/utils/trendline.d.ts.map +1 -0
  132. package/dist/cjs/utils/trendline.js +303 -0
  133. package/dist/cjs/utils/waterfall.d.ts +24 -0
  134. package/dist/cjs/utils/waterfall.d.ts.map +1 -0
  135. package/dist/cjs/utils/waterfall.js +61 -0
  136. package/dist/cjs/utils/webgl.d.ts +12 -0
  137. package/dist/cjs/utils/webgl.d.ts.map +1 -0
  138. package/dist/cjs/utils/webgl.js +36 -0
  139. package/dist/cjs/vitest.config.d.ts +3 -0
  140. package/dist/cjs/vitest.config.d.ts.map +1 -0
  141. package/dist/cjs/vitest.config.js +9 -0
  142. package/dist/esm/atoms/editing-chart.atom.d.ts +13 -0
  143. package/dist/esm/atoms/editing-chart.atom.d.ts.map +1 -0
  144. package/dist/esm/atoms/editing-chart.atom.js +12 -0
  145. package/dist/esm/basic-chart/basic-chart.d.ts +5 -0
  146. package/dist/esm/basic-chart/basic-chart.d.ts.map +1 -0
  147. package/dist/esm/basic-chart/basic-chart.js +2343 -0
  148. package/dist/esm/basic-chart/helpers.d.ts +12 -0
  149. package/dist/esm/basic-chart/helpers.d.ts.map +1 -0
  150. package/dist/esm/basic-chart/helpers.js +98 -0
  151. package/dist/esm/basic-chart/index.d.ts +5 -0
  152. package/dist/esm/basic-chart/index.d.ts.map +1 -0
  153. package/dist/esm/basic-chart/index.js +7 -0
  154. package/dist/esm/components/dialogs/chart-editor-dialog.d.ts +5 -0
  155. package/dist/esm/components/dialogs/chart-editor-dialog.d.ts.map +1 -0
  156. package/dist/esm/components/dialogs/chart-editor-dialog.js +19 -0
  157. package/dist/esm/components/editors/chart-editor.d.ts +10 -0
  158. package/dist/esm/components/editors/chart-editor.d.ts.map +1 -0
  159. package/dist/esm/components/editors/chart-editor.js +550 -0
  160. package/dist/esm/components/editors/chart-range-input.d.ts +17 -0
  161. package/dist/esm/components/editors/chart-range-input.d.ts.map +1 -0
  162. package/dist/esm/components/editors/chart-range-input.js +10 -0
  163. package/dist/esm/config.d.ts +19 -0
  164. package/dist/esm/config.d.ts.map +1 -0
  165. package/dist/esm/config.js +161 -0
  166. package/dist/esm/hooks/use-chart-editor.d.ts +8 -0
  167. package/dist/esm/hooks/use-chart-editor.d.ts.map +1 -0
  168. package/dist/esm/hooks/use-chart-editor.js +32 -0
  169. package/dist/esm/hooks/use-charts.d.ts +35 -0
  170. package/dist/esm/hooks/use-charts.d.ts.map +1 -0
  171. package/dist/esm/hooks/use-charts.js +62 -0
  172. package/dist/esm/hooks/use-on-create-chart.d.ts +13 -0
  173. package/dist/esm/hooks/use-on-create-chart.d.ts.map +1 -0
  174. package/dist/esm/hooks/use-on-create-chart.js +19 -0
  175. package/dist/esm/hooks/use-on-delete-chart.d.ts +8 -0
  176. package/dist/esm/hooks/use-on-delete-chart.d.ts.map +1 -0
  177. package/dist/esm/hooks/use-on-delete-chart.js +14 -0
  178. package/dist/esm/hooks/use-on-move-chart.d.ts +8 -0
  179. package/dist/esm/hooks/use-on-move-chart.d.ts.map +1 -0
  180. package/dist/esm/hooks/use-on-move-chart.js +10 -0
  181. package/dist/esm/hooks/use-on-resize-chart.d.ts +8 -0
  182. package/dist/esm/hooks/use-on-resize-chart.d.ts.map +1 -0
  183. package/dist/esm/hooks/use-on-resize-chart.js +10 -0
  184. package/dist/esm/hooks/use-on-update-chart.d.ts +9 -0
  185. package/dist/esm/hooks/use-on-update-chart.d.ts.map +1 -0
  186. package/dist/esm/hooks/use-on-update-chart.js +10 -0
  187. package/dist/esm/index.d.ts +11 -0
  188. package/dist/esm/index.d.ts.map +1 -0
  189. package/dist/esm/index.js +13 -0
  190. package/dist/esm/interface/create-chart.d.ts +9 -0
  191. package/dist/esm/interface/create-chart.d.ts.map +1 -0
  192. package/dist/esm/interface/create-chart.js +126 -0
  193. package/dist/esm/interface/delete-chart.d.ts +4 -0
  194. package/dist/esm/interface/delete-chart.d.ts.map +1 -0
  195. package/dist/esm/interface/delete-chart.js +20 -0
  196. package/dist/esm/interface/move-chart.d.ts +5 -0
  197. package/dist/esm/interface/move-chart.d.ts.map +1 -0
  198. package/dist/esm/interface/move-chart.js +22 -0
  199. package/dist/esm/interface/resize-chart.d.ts +5 -0
  200. package/dist/esm/interface/resize-chart.d.ts.map +1 -0
  201. package/dist/esm/interface/resize-chart.js +24 -0
  202. package/dist/esm/interface/update-chart.d.ts +4 -0
  203. package/dist/esm/interface/update-chart.d.ts.map +1 -0
  204. package/dist/esm/interface/update-chart.js +26 -0
  205. package/dist/esm/plotly/basic-chart/basic-chart.d.ts +5 -0
  206. package/dist/esm/plotly/basic-chart/basic-chart.d.ts.map +1 -0
  207. package/dist/esm/plotly/basic-chart/basic-chart.js +1682 -0
  208. package/dist/esm/plotly/basic-chart/helpers.d.ts +20 -0
  209. package/dist/esm/plotly/basic-chart/helpers.d.ts.map +1 -0
  210. package/dist/esm/plotly/basic-chart/helpers.js +107 -0
  211. package/dist/esm/plotly/basic-chart/index.d.ts +5 -0
  212. package/dist/esm/plotly/basic-chart/index.d.ts.map +1 -0
  213. package/dist/esm/plotly/basic-chart/index.js +7 -0
  214. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -0
  215. package/dist/esm/types.d.ts +27 -0
  216. package/dist/esm/types.d.ts.map +1 -0
  217. package/dist/esm/types.js +1 -0
  218. package/dist/esm/utils/aggregation.d.ts +6 -0
  219. package/dist/esm/utils/aggregation.d.ts.map +1 -0
  220. package/dist/esm/utils/aggregation.js +96 -0
  221. package/dist/esm/utils/bar-layout.d.ts +57 -0
  222. package/dist/esm/utils/bar-layout.d.ts.map +1 -0
  223. package/dist/esm/utils/bar-layout.js +70 -0
  224. package/dist/esm/utils/boxplot.d.ts +62 -0
  225. package/dist/esm/utils/boxplot.d.ts.map +1 -0
  226. package/dist/esm/utils/boxplot.js +91 -0
  227. package/dist/esm/utils/bubble.d.ts +52 -0
  228. package/dist/esm/utils/bubble.d.ts.map +1 -0
  229. package/dist/esm/utils/bubble.js +122 -0
  230. package/dist/esm/utils/chart-spec-utils.d.ts +67 -0
  231. package/dist/esm/utils/chart-spec-utils.d.ts.map +1 -0
  232. package/dist/esm/utils/chart-spec-utils.js +270 -0
  233. package/dist/esm/utils/echarts-gl-loader.d.ts +11 -0
  234. package/dist/esm/utils/echarts-gl-loader.d.ts.map +1 -0
  235. package/dist/esm/utils/echarts-gl-loader.js +51 -0
  236. package/dist/esm/utils/error-bars.d.ts +58 -0
  237. package/dist/esm/utils/error-bars.d.ts.map +1 -0
  238. package/dist/esm/utils/error-bars.js +134 -0
  239. package/dist/esm/utils/gradient.d.ts +52 -0
  240. package/dist/esm/utils/gradient.d.ts.map +1 -0
  241. package/dist/esm/utils/gradient.js +84 -0
  242. package/dist/esm/utils/hierarchy.d.ts +28 -0
  243. package/dist/esm/utils/hierarchy.d.ts.map +1 -0
  244. package/dist/esm/utils/hierarchy.js +150 -0
  245. package/dist/esm/utils/histogram.d.ts +46 -0
  246. package/dist/esm/utils/histogram.d.ts.map +1 -0
  247. package/dist/esm/utils/histogram.js +95 -0
  248. package/dist/esm/utils/of-pie.d.ts +101 -0
  249. package/dist/esm/utils/of-pie.d.ts.map +1 -0
  250. package/dist/esm/utils/of-pie.js +141 -0
  251. package/dist/esm/utils/overrides.d.ts +12 -0
  252. package/dist/esm/utils/overrides.d.ts.map +1 -0
  253. package/dist/esm/utils/overrides.js +159 -0
  254. package/dist/esm/utils/radar.d.ts +37 -0
  255. package/dist/esm/utils/radar.d.ts.map +1 -0
  256. package/dist/esm/utils/radar.js +49 -0
  257. package/dist/esm/utils/region-map-loader.d.ts +22 -0
  258. package/dist/esm/utils/region-map-loader.d.ts.map +1 -0
  259. package/dist/esm/utils/region-map-loader.js +141 -0
  260. package/dist/esm/utils/region-map.d.ts +100 -0
  261. package/dist/esm/utils/region-map.d.ts.map +1 -0
  262. package/dist/esm/utils/region-map.js +266 -0
  263. package/dist/esm/utils/stock.d.ts +64 -0
  264. package/dist/esm/utils/stock.d.ts.map +1 -0
  265. package/dist/esm/utils/stock.js +123 -0
  266. package/dist/esm/utils/three-d.d.ts +96 -0
  267. package/dist/esm/utils/three-d.d.ts.map +1 -0
  268. package/dist/esm/utils/three-d.js +162 -0
  269. package/dist/esm/utils/trendline.d.ts +41 -0
  270. package/dist/esm/utils/trendline.d.ts.map +1 -0
  271. package/dist/esm/utils/trendline.js +257 -0
  272. package/dist/esm/utils/waterfall.d.ts +24 -0
  273. package/dist/esm/utils/waterfall.d.ts.map +1 -0
  274. package/dist/esm/utils/waterfall.js +55 -0
  275. package/dist/esm/utils/webgl.d.ts +12 -0
  276. package/dist/esm/utils/webgl.d.ts.map +1 -0
  277. package/dist/esm/utils/webgl.js +30 -0
  278. package/dist/esm/vitest.config.d.ts +3 -0
  279. package/dist/esm/vitest.config.d.ts.map +1 -0
  280. package/dist/esm/vitest.config.js +7 -0
  281. package/package.json +96 -0
@@ -0,0 +1,550 @@
1
+ import { BarChartIcon, ChevronRightIcon, CrossCircledIcon, TriangleDownIcon, } from "@rowsncolumns/icons";
2
+ import { defaultCellCoords } from "@rowsncolumns/spreadsheet-state";
3
+ import { Button, DropdownLeftSlot, DropdownMenuContent, DropdownMenuItem, DropdownMenuPortal, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownRightSlot, IconButton, Input, Stack, Tabs, TabsList, TabsTrigger, TabsContent, } from "@rowsncolumns/ui";
4
+ import { DropdownMenu } from "@rowsncolumns/ui";
5
+ import { DropdownMenuTrigger } from "@rowsncolumns/ui";
6
+ import { useFormik } from "formik";
7
+ import React, { useMemo } from "react";
8
+ import { useSetChartEditorDialogState } from "../../atoms/editing-chart.atom";
9
+ import { ChartRangeInput } from "./chart-range-input";
10
+ import { CHART_TYPES, getChartPresetLabel } from "../../config";
11
+ import { uuid } from "@rowsncolumns/utils";
12
+ import { isBasicChartSpec, isBubbleChartSpec, isHierarchyChartSpec, mergeChartSpec, } from "../../utils/chart-spec-utils";
13
+ const AGGREGATE_OPTIONS = [
14
+ { label: "Sum", value: "SUM" },
15
+ { label: "Average", value: "AVERAGE" },
16
+ { label: "Count", value: "COUNT" },
17
+ { label: "Median", value: "MEDIAN" },
18
+ { label: "Min", value: "MIN" },
19
+ { label: "Max", value: "MAX" },
20
+ ];
21
+ const Section = ({ title, defaultOpen = false, children, }) => (React.createElement("details", { open: defaultOpen, className: "group border border-rnc-border border-solid rounded-md mb-2 px-3" },
22
+ React.createElement("summary", { className: "flex items-center gap-1.5 cursor-pointer list-none py-2 font-system text-sm font-medium select-none [&::-webkit-details-marker]:hidden" },
23
+ React.createElement(ChevronRightIcon, { className: "shrink-0 transition-transform group-open:rotate-90" }),
24
+ title),
25
+ React.createElement("div", { className: "flex flex-col gap-2 pb-3" }, children)));
26
+ export const ChartEditor = ({ chart, sheetId, onSubmit }) => {
27
+ const setChartDialogState = useSetChartEditorDialogState();
28
+ // Close dialog
29
+ const closeDialog = () => setChartDialogState(false);
30
+ // Keyed on chart identity so a position sync (move/resize while the
31
+ // editor is open) does not reinitialize a dirty form.
32
+ const initialValues = useMemo(() => chart ?? {
33
+ chartId: uuid(),
34
+ position: {
35
+ sheetId,
36
+ overlayPosition: { anchorCell: defaultCellCoords },
37
+ },
38
+ spec: { chartType: "line", series: [], domains: [] },
39
+ },
40
+ // eslint-disable-next-line react-hooks/exhaustive-deps
41
+ [chart?.chartId]);
42
+ // Formik
43
+ const { handleSubmit, setFieldValue, resetForm, values } = useFormik({
44
+ initialValues,
45
+ enableReinitialize: true,
46
+ onSubmit(values) {
47
+ // Submit against the live position so edits do not revert a
48
+ // chart moved or resized while the editor was open.
49
+ onSubmit?.(chart ? { ...values, position: chart.position } : values);
50
+ // Close dialog
51
+ closeDialog();
52
+ },
53
+ });
54
+ const onCancelForm = () => {
55
+ resetForm();
56
+ closeDialog();
57
+ };
58
+ const basicSpec = isBasicChartSpec(values.spec) ? values.spec : undefined;
59
+ const bubbleSpec = isBubbleChartSpec(values.spec) ? values.spec : undefined;
60
+ const treemapSpec = isHierarchyChartSpec(values.spec)
61
+ ? values.spec
62
+ : undefined;
63
+ // Cartesian (XY) chart types have value/category axes; circular and
64
+ // polar/derived types (pie/doughnut/radar) do not, so the axis-scale
65
+ // controls are gated to these.
66
+ const isCartesianBasic = basicSpec
67
+ ? basicSpec.chartType !== "pie" &&
68
+ basicSpec.chartType !== "doughnut" &&
69
+ basicSpec.chartType !== "radar"
70
+ : false;
71
+ // Bar/column-only features. gapWidth is consumed by both renderers
72
+ // (echarts barCategoryGap / plotly bargap); overlap by echarts
73
+ // (barGap).
74
+ const isBarOrColumnBasic = basicSpec
75
+ ? basicSpec.chartType === "bar" || basicSpec.chartType === "column"
76
+ : false;
77
+ // Check aggregate
78
+ const isAggregateType = basicSpec
79
+ ? basicSpec.series.some((serie) => serie.aggregateType)
80
+ : false;
81
+ const bubbleAggregateEnabled = bubbleSpec
82
+ ? bubbleSpec.series.some((serie) => serie.aggregateType)
83
+ : false;
84
+ const renderChartDataSection = (label, field, data, helperText) => {
85
+ const sections = data.length > 0 ? data : [{ sources: [] }];
86
+ return (React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-3" },
87
+ React.createElement("label", { className: "font-system text-sm" }, label),
88
+ helperText ? (React.createElement("p", { className: "m-0 text-xs text-rnc-text-weak" }, helperText)) : null,
89
+ sections.map((domain, i) => {
90
+ const sources = domain.sources.length > 0 ? domain.sources : [undefined];
91
+ return sources.map((source, idx) => (React.createElement("div", { key: `${values.chartId}-${label}-${sections.length}-${i}-${idx}` },
92
+ React.createElement(ChartRangeInput, { sheetId: sheetId, range: source, onChangeRange: (range) => {
93
+ setFieldValue(`spec.${field}[${i}].sources[${idx}]`, range);
94
+ }, required: true, placeholder: "Select a range", className: "mb-0 flex-1" }))));
95
+ })));
96
+ };
97
+ return (React.createElement("form", { onSubmit: handleSubmit, className: "flex flex-col" },
98
+ React.createElement(Tabs, { defaultValue: "setup", className: "w-full" },
99
+ React.createElement(TabsList, { className: "grid-cols-2" },
100
+ React.createElement(TabsTrigger, { value: "setup" }, "Setup"),
101
+ React.createElement(TabsTrigger, { value: "customize" }, "Customize")),
102
+ React.createElement(TabsContent, { value: "setup", className: "flex flex-col gap-2" },
103
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
104
+ React.createElement("label", { className: "font-system text-sm" }, "Chart type"),
105
+ React.createElement(DropdownMenu, null,
106
+ React.createElement(DropdownMenuTrigger, { asChild: true },
107
+ React.createElement(Button, { variant: "outline", size: "xs", className: "py-1.5 px-1.5" },
108
+ getChartPresetLabel(values.spec),
109
+ React.createElement(DropdownRightSlot, null,
110
+ React.createElement(TriangleDownIcon, null)))),
111
+ React.createElement(DropdownMenuPortal, null,
112
+ React.createElement(DropdownMenuContent, { align: "start", sideOffset: 0 }, CHART_TYPES.map(([category, types]) => {
113
+ return (React.createElement(DropdownMenuSub, { key: category },
114
+ React.createElement(DropdownMenuSubTrigger, null,
115
+ React.createElement(DropdownLeftSlot, null,
116
+ React.createElement(BarChartIcon, null)),
117
+ category,
118
+ React.createElement(DropdownRightSlot, null,
119
+ React.createElement(ChevronRightIcon, null))),
120
+ React.createElement(DropdownMenuSubContent, { sideOffset: 2, alignOffset: -5 }, types.map(([name, config]) => {
121
+ return (React.createElement(DropdownMenuItem, { key: name, onClick: () => {
122
+ setFieldValue("spec", mergeChartSpec(values.spec, config));
123
+ } }, name));
124
+ }))));
125
+ }))))),
126
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
127
+ React.createElement("label", { className: "font-system text-sm" }, "Chart data range"),
128
+ React.createElement(ChartRangeInput, { key: values.chartId, sheetId: sheetId, range: values.spec?.dataRange ?? undefined, onChangeRange: (range) => {
129
+ setFieldValue("spec.dataRange", range);
130
+ }, required: true, placeholder: "Select a range", className: "mb-0" })),
131
+ basicSpec ? (React.createElement(React.Fragment, null,
132
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
133
+ React.createElement("label", { className: "font-system text-sm" }, "Horizontal (category) axis labels"),
134
+ (basicSpec.domains ?? []).map((domain, i) => {
135
+ return domain.sources.map((source, idx) => {
136
+ return (React.createElement(ChartRangeInput, { key: `${values.chartId}-domains-${basicSpec.domains.length}-${i}-${idx}`, sheetId: sheetId, range: source, onChangeRange: (range) => {
137
+ setFieldValue(`spec.domains[${i}].sources[${idx}]`, range);
138
+ }, required: true, placeholder: "Select a range", className: "mb-0" }));
139
+ });
140
+ })),
141
+ React.createElement("label", { className: "font-system text-sm flex items-center gap-1 pb-2" },
142
+ React.createElement("input", { type: "checkbox", checked: isAggregateType, name: "showColumnStripes", onChange: (e) => {
143
+ const nextSeries = basicSpec.series.map((serie) => ({
144
+ ...serie,
145
+ aggregateType: e.target.checked ? "SUM" : undefined,
146
+ }));
147
+ setFieldValue("spec.series", nextSeries);
148
+ } }),
149
+ "Aggregate"),
150
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
151
+ React.createElement("label", { className: "font-system text-sm" }, "Legend entries (series)"),
152
+ basicSpec.series.map((domain, i) => {
153
+ return domain.sources.map((source, idx) => {
154
+ return (React.createElement("div", { className: "border border-rnc-border border-solid p-2 relative", key: `${values.chartId}-series-${basicSpec.series.length}-${i}-${idx}` },
155
+ React.createElement("div", { className: "flex gap-2 " },
156
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
157
+ React.createElement("label", { className: "font-system text-sm" }, "Name"),
158
+ React.createElement(Input, { value: domain.dataLabel ?? "", type: "text", name: "fromValue", onChange: (e) => setFieldValue(`spec.series[${i}].dataLabel`, e.target.value), className: "text-xs", placeholder: "Enter value" })),
159
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 flex-1" },
160
+ React.createElement("label", { className: "font-system text-sm" }, "Y values"),
161
+ React.createElement(ChartRangeInput, { sheetId: sheetId, range: source, onChangeRange: (range) => {
162
+ setFieldValue(`spec.series[${i}].sources[${idx}]`, range);
163
+ }, required: true, placeholder: "Select a range", className: "mb-0" })),
164
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2 w-20" },
165
+ React.createElement("label", { className: "font-system text-sm" }, "Color"),
166
+ React.createElement("div", { className: "flex items-center gap-1" },
167
+ React.createElement("input", { type: "color", value: domain.color ?? "#4285F4", onChange: (e) => setFieldValue(`spec.series[${i}].color`, e.target.value), className: "h-6 w-8 p-0 border border-rnc-border rounded-sm bg-transparent cursor-pointer", "aria-label": "Series color" }),
168
+ domain.color ? (React.createElement(IconButton, { type: "button", onClick: () => setFieldValue(`spec.series[${i}].color`, undefined), "aria-label": "Clear series color" },
169
+ React.createElement(CrossCircledIcon, null))) : null)),
170
+ isAggregateType ? (React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2 w-32" },
171
+ React.createElement("label", { className: "font-system text-sm" }, "Aggregate type"),
172
+ React.createElement("select", { className: "border border-rnc-border rounded-sm px-1 py-1 text-xs bg-transparent", value: domain.aggregateType ?? "", onChange: (event) => {
173
+ const aggregateType = event.target.value;
174
+ setFieldValue(`spec.series[${i}].aggregateType`, aggregateType || undefined);
175
+ } },
176
+ React.createElement("option", { value: "" }, "None"),
177
+ AGGREGATE_OPTIONS.map((option) => (React.createElement("option", { key: option.value, value: option.value }, option.label)))))) : null,
178
+ basicSpec.chartType === "combo" ? (React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2 w-32" },
179
+ React.createElement("label", { className: "font-system text-sm" }, "Series type"),
180
+ React.createElement("select", { className: "border border-rnc-border rounded-sm px-1 py-1 text-xs bg-transparent", value: domain.seriesChartType ?? "bar", onChange: (event) => {
181
+ setFieldValue(`spec.series[${i}].seriesChartType`, event.target.value || undefined);
182
+ } },
183
+ React.createElement("option", { value: "bar" }, "Bar"),
184
+ React.createElement("option", { value: "column" }, "Column"),
185
+ React.createElement("option", { value: "line" }, "Line"),
186
+ React.createElement("option", { value: "area" }, "Area")))) : null),
187
+ basicSpec.chartType !== "pie" &&
188
+ basicSpec.chartType !== "doughnut" &&
189
+ basicSpec.chartType !== "radar" &&
190
+ basicSpec.chartType !== "stock" ? (React.createElement("details", { className: "mt-2 group/fmt" },
191
+ React.createElement("summary", { className: "cursor-pointer list-none py-1 font-system text-xs text-rnc-muted-foreground select-none [&::-webkit-details-marker]:hidden flex items-center gap-1" },
192
+ React.createElement(ChevronRightIcon, { className: "transition-transform group-open/fmt:rotate-90" }),
193
+ "Format"),
194
+ React.createElement("div", { className: "flex flex-col gap-2 pt-2 border border-rnc-border border-solid mt-2" },
195
+ React.createElement("label", { className: "font-system text-sm flex items-center gap-1" },
196
+ React.createElement("input", { type: "checkbox", checked: !!domain.useSecondaryAxis, onChange: (e) => setFieldValue(`spec.series[${i}].useSecondaryAxis`, e.target.checked || undefined) }),
197
+ "Plot on secondary axis"),
198
+ React.createElement("div", { className: "flex items-center gap-2" },
199
+ React.createElement("label", { className: "font-system text-sm flex items-center gap-1" },
200
+ React.createElement("input", { type: "checkbox", checked: (domain.trendlines?.length ?? 0) > 0, onChange: (e) => setFieldValue(`spec.series[${i}].trendlines`, e.target.checked
201
+ ? [
202
+ domain.trendlines?.[0] ?? {
203
+ type: "linear",
204
+ },
205
+ ]
206
+ : undefined) }),
207
+ "Trendline"),
208
+ (domain.trendlines?.length ?? 0) > 0 ? (React.createElement("select", { className: "border border-rnc-border rounded-sm px-1 py-1 text-xs bg-transparent", value: domain.trendlines?.[0]?.type ?? "linear", onChange: (event) => setFieldValue(`spec.series[${i}].trendlines[0]`, {
209
+ ...(domain.trendlines?.[0] ?? {}),
210
+ type: event.target.value,
211
+ ...(event.target.value === "poly"
212
+ ? {
213
+ order: domain.trendlines?.[0]
214
+ ?.order ?? 2,
215
+ }
216
+ : {}),
217
+ }) },
218
+ React.createElement("option", { value: "linear" }, "Linear"),
219
+ React.createElement("option", { value: "poly" }, "Polynomial"),
220
+ React.createElement("option", { value: "exp" }, "Exponential"),
221
+ React.createElement("option", { value: "log" }, "Logarithmic"))) : null),
222
+ React.createElement("div", { className: "flex items-center gap-2 flex-wrap" },
223
+ React.createElement("label", { className: "font-system text-sm flex items-center gap-1" },
224
+ React.createElement("input", { type: "checkbox", checked: !!domain.errorBars, onChange: (e) => setFieldValue(`spec.series[${i}].errorBars`, e.target.checked
225
+ ? domain.errorBars ?? {
226
+ valueType: "percentage",
227
+ value: 5,
228
+ }
229
+ : undefined) }),
230
+ "Error bars"),
231
+ domain.errorBars ? (React.createElement(React.Fragment, null,
232
+ React.createElement("select", { className: "border border-rnc-border rounded-sm px-1 py-1 text-xs bg-transparent", value: domain.errorBars?.valueType ??
233
+ "percentage", onChange: (event) => setFieldValue(`spec.series[${i}].errorBars`, {
234
+ ...(domain.errorBars ?? {}),
235
+ valueType: event.target.value,
236
+ }) },
237
+ React.createElement("option", { value: "fixedVal" }, "Fixed"),
238
+ React.createElement("option", { value: "percentage" }, "Percentage"),
239
+ React.createElement("option", { value: "stdDev" }, "Std deviation"),
240
+ React.createElement("option", { value: "stdErr" }, "Std error")),
241
+ domain.errorBars?.valueType !==
242
+ "stdErr" ? (React.createElement(Input, { type: "number", value: domain.errorBars?.value ?? "", onChange: (e) => setFieldValue(`spec.series[${i}].errorBars`, {
243
+ ...(domain.errorBars ?? {}),
244
+ value: e.target.value === ""
245
+ ? undefined
246
+ : Number(e.target.value),
247
+ }), className: "text-xs w-20", placeholder: "Value" })) : null)) : null),
248
+ React.createElement("label", { className: "font-system text-sm flex items-center gap-1" },
249
+ React.createElement("input", { type: "checkbox", checked: !!domain.gradient, onChange: (e) => setFieldValue(`spec.series[${i}].gradient`, e.target.checked
250
+ ? domain.gradient ?? {
251
+ type: "linear",
252
+ degree: 90,
253
+ stops: [
254
+ {
255
+ position: 0,
256
+ color: domain.color ?? "#4285F4",
257
+ },
258
+ {
259
+ position: 1,
260
+ color: "#FFFFFF",
261
+ },
262
+ ],
263
+ }
264
+ : undefined) }),
265
+ "Gradient fill")))) : null,
266
+ React.createElement(IconButton, { className: "absolute right-1 top-1", type: "button", onClick: () => {
267
+ const currentSeries = basicSpec.series;
268
+ setFieldValue("spec.series", currentSeries.filter((_, seriesIndex) => seriesIndex !== i));
269
+ } },
270
+ React.createElement(CrossCircledIcon, null))));
271
+ });
272
+ }),
273
+ React.createElement("div", { className: "py-1" },
274
+ React.createElement(Button, { variant: "link", onClick: () => {
275
+ const nextSeries = basicSpec.series;
276
+ const lastSeries = nextSeries[nextSeries.length - 1];
277
+ setFieldValue("spec.series", [
278
+ ...nextSeries,
279
+ {
280
+ sources: lastSeries
281
+ ? lastSeries.sources
282
+ : values.spec.dataRange
283
+ ? [values.spec.dataRange]
284
+ : [],
285
+ },
286
+ ]);
287
+ }, type: "button", className: "cursor-pointer" }, "Add series"))))) : null,
288
+ bubbleSpec ? (React.createElement(React.Fragment, null,
289
+ React.createElement("label", { className: "font-system text-sm flex items-center gap-1 pb-2" },
290
+ React.createElement("input", { type: "checkbox", checked: bubbleAggregateEnabled, onChange: (e) => {
291
+ const nextSeries = bubbleSpec.series.map((serie) => ({
292
+ ...serie,
293
+ aggregateType: e.target.checked ? "SUM" : undefined,
294
+ }));
295
+ setFieldValue("spec.series", nextSeries);
296
+ } }),
297
+ "Aggregate"),
298
+ React.createElement("label", { className: "font-system text-sm flex items-center gap-1 pb-2" },
299
+ React.createElement("input", { type: "checkbox", checked: bubbleSpec.useFirstRowAsHeaders !== false, onChange: (e) => setFieldValue("spec.useFirstRowAsHeaders", e.target.checked) }),
300
+ "Use first row as headers"),
301
+ bubbleSpec.chartType === "bubble"
302
+ ? renderChartDataSection("Bubble labels", "bubbleLabels", bubbleSpec.bubbleLabels ?? [], "Select the cells containing labels for each bubble.")
303
+ : null,
304
+ renderChartDataSection("X values", "domains", bubbleSpec.domains ?? [], "Choose the numeric values for the horizontal axis."),
305
+ React.createElement(Stack, { direction: "vertical", className: "gap-2 pb-2" },
306
+ React.createElement("label", { className: "font-system text-sm" }, "Legend entries (series)"),
307
+ bubbleSpec.series.map((seriesEntry, i) => {
308
+ const sources = seriesEntry.sources.length > 0
309
+ ? seriesEntry.sources
310
+ : [undefined];
311
+ return sources.map((source, idx) => (React.createElement("div", { className: "border border-rnc-border border-solid p-2 relative", key: `${values.chartId}-bubble-series-${bubbleSpec.series.length}-${i}-${idx}` },
312
+ React.createElement("div", { className: "flex gap-2 items-start" },
313
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
314
+ React.createElement("label", { className: "font-system text-sm" }, "Name"),
315
+ React.createElement(Input, { value: seriesEntry.dataLabel ?? "", type: "text", onChange: (e) => setFieldValue(`spec.series[${i}].dataLabel`, e.target.value), className: "text-xs", placeholder: "Enter label" })),
316
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 flex-1" },
317
+ React.createElement("label", { className: "font-system text-sm" }, "Y values"),
318
+ React.createElement(ChartRangeInput, { sheetId: sheetId, range: source, onChangeRange: (range) => {
319
+ setFieldValue(`spec.series[${i}].sources[${idx}]`, range);
320
+ }, required: true, placeholder: "Select a range", className: "mb-0 flex-1" })),
321
+ bubbleSpec.chartType === "scatter" ? (React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2 w-24" },
322
+ React.createElement("label", { className: "font-system text-sm" }, "Marker"),
323
+ React.createElement("select", { className: "border border-rnc-border rounded-sm px-1 py-1 text-xs bg-transparent", value: seriesEntry.marker?.symbol ?? "", onChange: (event) => setFieldValue(`spec.series[${i}].marker`, {
324
+ ...(seriesEntry.marker ?? {}),
325
+ symbol: event.target.value || undefined,
326
+ }) },
327
+ React.createElement("option", { value: "" }, "Auto"),
328
+ React.createElement("option", { value: "circle" }, "Circle"),
329
+ React.createElement("option", { value: "square" }, "Square"),
330
+ React.createElement("option", { value: "diamond" }, "Diamond"),
331
+ React.createElement("option", { value: "triangle" }, "Triangle"),
332
+ React.createElement("option", { value: "x" }, "X"),
333
+ React.createElement("option", { value: "plus" }, "Plus"),
334
+ React.createElement("option", { value: "star" }, "Star"),
335
+ React.createElement("option", { value: "none" }, "None")),
336
+ React.createElement("label", { className: "font-system text-xs" }, "Marker size"),
337
+ React.createElement(Input, { type: "number", min: 1, value: seriesEntry.marker?.size ?? "", onChange: (e) => setFieldValue(`spec.series[${i}].marker`, {
338
+ ...(seriesEntry.marker ?? {}),
339
+ size: e.target.value === ""
340
+ ? undefined
341
+ : Number(e.target.value),
342
+ }), className: "text-xs", placeholder: "6" }))) : null,
343
+ bubbleAggregateEnabled ? (React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2 w-32" },
344
+ React.createElement("label", { className: "font-system text-sm" }, "Aggregate type"),
345
+ React.createElement("select", { className: "border border-rnc-border rounded-sm px-1 py-1 text-xs bg-transparent", value: seriesEntry.aggregateType ?? "", onChange: (event) => {
346
+ const aggregateType = event.target.value;
347
+ setFieldValue(`spec.series[${i}].aggregateType`, aggregateType || undefined);
348
+ } },
349
+ React.createElement("option", { value: "" }, "None"),
350
+ AGGREGATE_OPTIONS.map((option) => (React.createElement("option", { key: option.value, value: option.value }, option.label)))))) : null),
351
+ React.createElement(IconButton, { className: "absolute right-1 top-1", type: "button", onClick: () => {
352
+ const currentSeries = bubbleSpec.series;
353
+ setFieldValue("spec.series", currentSeries.filter((_, index) => index !== i));
354
+ } },
355
+ React.createElement(CrossCircledIcon, null)))));
356
+ }),
357
+ React.createElement("div", { className: "py-1" },
358
+ React.createElement(Button, { variant: "link", type: "button", onClick: () => {
359
+ const nextSeries = bubbleSpec.series;
360
+ const lastSeries = nextSeries[nextSeries.length - 1];
361
+ setFieldValue("spec.series", [
362
+ ...nextSeries,
363
+ {
364
+ sources: lastSeries
365
+ ? lastSeries.sources
366
+ : values.spec.dataRange
367
+ ? [values.spec.dataRange]
368
+ : [],
369
+ },
370
+ ]);
371
+ }, className: "cursor-pointer" }, "Add series"))))) : null,
372
+ treemapSpec ? (React.createElement(React.Fragment, null,
373
+ React.createElement("label", { className: "font-system text-sm flex items-center gap-1 pb-2" },
374
+ React.createElement("input", { type: "checkbox", checked: treemapSpec.useFirstRowAsHeaders !== false, onChange: (e) => setFieldValue("spec.useFirstRowAsHeaders", e.target.checked) }),
375
+ "Use first row as headers"),
376
+ renderChartDataSection("Labels", "labels", treemapSpec.labels ?? [], "Select the cells that contain node labels."),
377
+ renderChartDataSection("Parent labels", "parentLabels", treemapSpec.parentLabels ?? [], "Choose the cells that define each node's parent label."),
378
+ renderChartDataSection("Size data", "sizeData", treemapSpec.sizeData ?? [], "Provide numeric values to determine each node's size."))) : null,
379
+ React.createElement("label", { className: "font-system text-sm flex items-center gap-1" },
380
+ React.createElement("input", { type: "checkbox", checked: values.spec.hiddenDimensionStrategy === "SHOW_ALL", name: "showColumnStripes", onChange: (e) => {
381
+ setFieldValue("spec.hiddenDimensionStrategy", e.target.checked
382
+ ? "SHOW_ALL"
383
+ : "SKIP_HIDDEN_ROWS_AND_COLUMNS");
384
+ } }),
385
+ "Show data in hidden rows and columns")),
386
+ React.createElement(TabsContent, { value: "customize", className: "flex flex-col" },
387
+ React.createElement(Section, { title: "Chart style", defaultOpen: true },
388
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
389
+ React.createElement("label", { className: "font-system text-sm" }, "Chart title"),
390
+ React.createElement(Input, { value: values?.spec.title ?? "", type: "text", onChange: (e) => setFieldValue(`spec.title`, e.target.value), className: "text-xs", placeholder: "Enter chart title" }))),
391
+ basicSpec ? (React.createElement(React.Fragment, null,
392
+ React.createElement(Section, { title: "Legend" },
393
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
394
+ React.createElement("label", { className: "font-system text-sm" }, "Legend"),
395
+ React.createElement("select", { className: "border border-rnc-border rounded-sm px-1 py-1 text-xs bg-transparent w-32", value: values?.spec.legend?.position ?? "", onChange: (e) => setFieldValue("spec.legend.position", e.target.value || undefined) },
396
+ React.createElement("option", { value: "" }, "Auto"),
397
+ React.createElement("option", { value: "top" }, "Top"),
398
+ React.createElement("option", { value: "bottom" }, "Bottom"),
399
+ React.createElement("option", { value: "left" }, "Left"),
400
+ React.createElement("option", { value: "right" }, "Right"),
401
+ React.createElement("option", { value: "none" }, "None")))),
402
+ React.createElement(Section, { title: "Vertical axis" },
403
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
404
+ React.createElement("label", { className: "font-system text-sm" }, "Vertical axis title"),
405
+ React.createElement(Input, { value: values?.spec.verticalAxisTitle ?? "", type: "text", onChange: (e) => setFieldValue(`spec.verticalAxisTitle`, e.target.value), className: "text-xs", placeholder: "Vertical axis title" })),
406
+ isCartesianBasic ? (React.createElement(Stack, { direction: "vertical", className: "gap-2 pb-2" },
407
+ React.createElement("label", { className: "font-system text-sm" }, "Value axis"),
408
+ React.createElement("div", { className: "flex flex-wrap gap-2 items-end" },
409
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 w-20" },
410
+ React.createElement("label", { className: "font-system text-xs" }, "Min"),
411
+ React.createElement(Input, { type: "number", value: values?.spec.valueAxisOptions?.min ?? "", onChange: (e) => setFieldValue("spec.valueAxisOptions.min", e.target.value === ""
412
+ ? undefined
413
+ : Number(e.target.value)), className: "text-xs", placeholder: "Auto" })),
414
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 w-20" },
415
+ React.createElement("label", { className: "font-system text-xs" }, "Max"),
416
+ React.createElement(Input, { type: "number", value: values?.spec.valueAxisOptions?.max ?? "", onChange: (e) => setFieldValue("spec.valueAxisOptions.max", e.target.value === ""
417
+ ? undefined
418
+ : Number(e.target.value)), className: "text-xs", placeholder: "Auto" }))),
419
+ React.createElement("label", { className: "font-system text-xs flex items-center gap-1" },
420
+ React.createElement("input", { type: "checkbox", checked: Boolean(values?.spec.valueAxisOptions?.logarithmic), onChange: (e) => setFieldValue("spec.valueAxisOptions.logarithmic", e.target.checked || undefined) }),
421
+ "Logarithmic scale"),
422
+ React.createElement("label", { className: "font-system text-xs flex items-center gap-1" },
423
+ React.createElement("input", { type: "checkbox", checked: Boolean(values?.spec.valueAxisOptions?.reverse), onChange: (e) => setFieldValue("spec.valueAxisOptions.reverse", e.target.checked || undefined) }),
424
+ "Reverse direction"),
425
+ React.createElement("label", { className: "font-system text-xs flex items-center gap-1" },
426
+ React.createElement("input", { type: "checkbox", checked: values?.spec.valueAxisOptions?.showMajorGridlines !==
427
+ false, onChange: (e) => setFieldValue("spec.valueAxisOptions.showMajorGridlines", e.target.checked) }),
428
+ "Gridlines"),
429
+ basicSpec.series.some((s) => s.useSecondaryAxis) ? (React.createElement(React.Fragment, null,
430
+ React.createElement("label", { className: "font-system text-sm pt-1" }, "Secondary value axis"),
431
+ React.createElement("div", { className: "flex flex-wrap gap-2 items-end" },
432
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 w-20" },
433
+ React.createElement("label", { className: "font-system text-xs" }, "Min"),
434
+ React.createElement(Input, { type: "number", value: values?.spec.secondaryValueAxisOptions?.min ??
435
+ "", onChange: (e) => setFieldValue("spec.secondaryValueAxisOptions.min", e.target.value === ""
436
+ ? undefined
437
+ : Number(e.target.value)), className: "text-xs", placeholder: "Auto" })),
438
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 w-20" },
439
+ React.createElement("label", { className: "font-system text-xs" }, "Max"),
440
+ React.createElement(Input, { type: "number", value: values?.spec.secondaryValueAxisOptions?.max ??
441
+ "", onChange: (e) => setFieldValue("spec.secondaryValueAxisOptions.max", e.target.value === ""
442
+ ? undefined
443
+ : Number(e.target.value)), className: "text-xs", placeholder: "Auto" }))),
444
+ React.createElement("label", { className: "font-system text-xs flex items-center gap-1" },
445
+ React.createElement("input", { type: "checkbox", checked: Boolean(values?.spec.secondaryValueAxisOptions
446
+ ?.logarithmic), onChange: (e) => setFieldValue("spec.secondaryValueAxisOptions.logarithmic", e.target.checked || undefined) }),
447
+ "Logarithmic scale"),
448
+ React.createElement("label", { className: "font-system text-xs flex items-center gap-1" },
449
+ React.createElement("input", { type: "checkbox", checked: values?.spec.secondaryValueAxisOptions
450
+ ?.showMajorGridlines !== false, onChange: (e) => setFieldValue("spec.secondaryValueAxisOptions.showMajorGridlines", e.target.checked) }),
451
+ "Gridlines"))) : null)) : null),
452
+ React.createElement(Section, { title: "Horizontal axis" },
453
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
454
+ React.createElement("label", { className: "font-system text-sm" }, "Horizontal axis title"),
455
+ React.createElement(Input, { value: values?.spec.horizontalAxisTitle ?? "", type: "text", onChange: (e) => setFieldValue(`spec.horizontalAxisTitle`, e.target.value), className: "text-xs", placeholder: "Horizontal axis title" })),
456
+ isCartesianBasic ? (React.createElement(React.Fragment, null,
457
+ React.createElement("label", { className: "font-system text-sm pt-1" }, "Category axis"),
458
+ React.createElement("label", { className: "font-system text-xs flex items-center gap-1" },
459
+ React.createElement("input", { type: "checkbox", checked: values?.spec.categoryAxisOptions
460
+ ?.showMajorGridlines === true, onChange: (e) => setFieldValue("spec.categoryAxisOptions.showMajorGridlines", e.target.checked) }),
461
+ "Gridlines"))) : null),
462
+ React.createElement(Section, { title: "Data labels" },
463
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
464
+ React.createElement("label", { className: "font-system text-sm flex items-center gap-1" },
465
+ React.createElement("input", { type: "checkbox", checked: Boolean(values?.spec.dataLabels?.show), onChange: (e) => setFieldValue("spec.dataLabels.show", e.target.checked) }),
466
+ "Show data labels"),
467
+ values?.spec.dataLabels?.show ? (React.createElement("div", { className: "pl-4 flex flex-col gap-1" },
468
+ React.createElement("label", { className: "font-system text-xs flex items-center gap-1" },
469
+ React.createElement("input", { type: "checkbox", checked: values?.spec.dataLabels?.showValue !== false, onChange: (e) => setFieldValue("spec.dataLabels.showValue", e.target.checked) }),
470
+ "Value"),
471
+ React.createElement("label", { className: "font-system text-xs flex items-center gap-1" },
472
+ React.createElement("input", { type: "checkbox", checked: Boolean(values?.spec.dataLabels?.showCategoryName), onChange: (e) => setFieldValue("spec.dataLabels.showCategoryName", e.target.checked) }),
473
+ "Category name"),
474
+ React.createElement("label", { className: "font-system text-xs flex items-center gap-1" },
475
+ React.createElement("input", { type: "checkbox", checked: Boolean(values?.spec.dataLabels?.showSeriesName), onChange: (e) => setFieldValue("spec.dataLabels.showSeriesName", e.target.checked) }),
476
+ "Series name"),
477
+ React.createElement("label", { className: "font-system text-xs flex items-center gap-1" },
478
+ React.createElement("input", { type: "checkbox", checked: Boolean(values?.spec.dataLabels?.showPercent), onChange: (e) => setFieldValue("spec.dataLabels.showPercent", e.target.checked) }),
479
+ "Percentage"),
480
+ React.createElement("label", { className: "font-system text-xs" }, "Label position"),
481
+ React.createElement("select", { className: "border border-rnc-border rounded-sm px-1 py-1 text-xs bg-transparent w-32", value: values?.spec.dataLabels?.position ?? "outside", onChange: (e) => setFieldValue("spec.dataLabels.position", e.target.value) },
482
+ React.createElement("option", { value: "center" }, "Center"),
483
+ React.createElement("option", { value: "inside" }, "Inside"),
484
+ React.createElement("option", { value: "outside" }, "Outside"),
485
+ React.createElement("option", { value: "insideEnd" }, "Inside end"),
486
+ React.createElement("option", { value: "outsideEnd" }, "Outside end"),
487
+ React.createElement("option", { value: "bestFit" }, "Best fit")),
488
+ React.createElement("label", { className: "font-system text-xs" }, "Number format pattern"),
489
+ React.createElement(Input, { value: values?.spec.dataLabels?.numberFormatPattern ?? "", type: "text", onChange: (e) => setFieldValue("spec.dataLabels.numberFormatPattern", e.target.value || undefined), className: "text-xs w-32", placeholder: "#,##0.00" }))) : null)),
490
+ basicSpec &&
491
+ (basicSpec.chartType === "histogram" ||
492
+ basicSpec.chartType === "radar" ||
493
+ basicSpec.chartType === "stock" ||
494
+ basicSpec.chartType === "doughnut" ||
495
+ isBarOrColumnBasic) ? (React.createElement(Section, { title: "Chart options" },
496
+ basicSpec.chartType === "histogram" ? (React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
497
+ React.createElement("label", { className: "font-system text-sm" }, "Bins (leave blank for auto)"),
498
+ React.createElement(Input, { value: basicSpec.binCount ?? "", type: "number", min: 1, onChange: (e) => {
499
+ const raw = e.target.value;
500
+ setFieldValue("spec.binCount", raw === "" ? undefined : Number(raw));
501
+ }, className: "text-xs", placeholder: "Auto (Sturges)" }))) : null,
502
+ basicSpec.chartType === "radar" ? (React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
503
+ React.createElement("label", { className: "font-system text-sm" }, "Radar style"),
504
+ React.createElement("select", { className: "border border-rnc-border rounded-sm px-1 py-1 text-xs bg-transparent", value: basicSpec.radarStyle ?? "standard", onChange: (e) => setFieldValue("spec.radarStyle", e.target.value) },
505
+ React.createElement("option", { value: "standard" }, "Standard"),
506
+ React.createElement("option", { value: "marker" }, "With markers"),
507
+ React.createElement("option", { value: "filled" }, "Filled")))) : null,
508
+ basicSpec.chartType === "stock" ? (React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
509
+ React.createElement("label", { className: "font-system text-sm" }, "Stock variant"),
510
+ React.createElement("select", { className: "border border-rnc-border rounded-sm px-1 py-1 text-xs bg-transparent", value: basicSpec.stockVariant ?? "HLC", onChange: (e) => setFieldValue("spec.stockVariant", e.target.value) },
511
+ React.createElement("option", { value: "HLC" }, "High-Low-Close"),
512
+ React.createElement("option", { value: "OHLC" }, "Open-High-Low-Close"),
513
+ React.createElement("option", { value: "VHLC" }, "Volume-High-Low-Close"),
514
+ React.createElement("option", { value: "VOHLC" }, "Volume-Open-High-Low-Close")))) : null,
515
+ basicSpec.chartType === "doughnut" ? (React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
516
+ React.createElement("label", { className: "font-system text-sm" }, "Doughnut hole size (0-0.9)"),
517
+ React.createElement(Input, { type: "number", min: 0, max: 0.9, step: 0.05, value: basicSpec.doughnutHoleSize ?? "", onChange: (e) => setFieldValue("spec.doughnutHoleSize", e.target.value === ""
518
+ ? undefined
519
+ : Number(e.target.value)), className: "text-xs w-32", placeholder: "0.5" }))) : null,
520
+ isBarOrColumnBasic ? (React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
521
+ React.createElement("label", { className: "font-system text-sm" }, "Gap width (0-500)"),
522
+ React.createElement(Input, { type: "number", min: 0, max: 500, value: basicSpec.gapWidth ?? "", onChange: (e) => setFieldValue("spec.gapWidth", e.target.value === ""
523
+ ? undefined
524
+ : Number(e.target.value)), className: "text-xs w-32", placeholder: "150" }))) : null,
525
+ isBarOrColumnBasic ? (React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
526
+ React.createElement("label", { className: "font-system text-sm" }, "Series overlap (-100 to 100)"),
527
+ React.createElement(Input, { type: "number", min: -100, max: 100, value: basicSpec.overlap ?? "", onChange: (e) => setFieldValue("spec.overlap", e.target.value === ""
528
+ ? undefined
529
+ : Number(e.target.value)), className: "text-xs w-32", placeholder: "0" }))) : null)) : null)) : null,
530
+ bubbleSpec && bubbleSpec.chartType === "scatter" ? (React.createElement(Section, { title: "Chart options" },
531
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
532
+ React.createElement("label", { className: "font-system text-sm" }, "Scatter style"),
533
+ React.createElement("select", { className: "border border-rnc-border rounded-sm px-1 py-1 text-xs bg-transparent w-56", value: bubbleSpec.scatterStyle ?? "lineMarker", onChange: (e) => setFieldValue("spec.scatterStyle", e.target.value) },
534
+ React.createElement("option", { value: "lineMarker" }, "Straight lines with markers"),
535
+ React.createElement("option", { value: "line" }, "Straight lines"),
536
+ React.createElement("option", { value: "marker" }, "Markers only"),
537
+ React.createElement("option", { value: "smoothMarker" }, "Smooth lines with markers"),
538
+ React.createElement("option", { value: "smooth" }, "Smooth lines"))))) : null,
539
+ bubbleSpec ? (React.createElement(Section, { title: "Axis titles" },
540
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
541
+ React.createElement("label", { className: "font-system text-sm" }, "Vertical axis title"),
542
+ React.createElement(Input, { value: values?.spec.verticalAxisTitle ?? "", type: "text", onChange: (e) => setFieldValue(`spec.verticalAxisTitle`, e.target.value), className: "text-xs", placeholder: "Vertical axis title" })),
543
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
544
+ React.createElement("label", { className: "font-system text-sm" }, "Horizontal axis title"),
545
+ React.createElement(Input, { value: values?.spec.horizontalAxisTitle ?? "", type: "text", onChange: (e) => setFieldValue(`spec.horizontalAxisTitle`, e.target.value), className: "text-xs", placeholder: "Horizontal axis title" })))) : null)),
546
+ React.createElement("div", { className: "flex gap-1 py-2 mt-1" },
547
+ React.createElement(Button, { type: "submit", variant: "primary", size: "xs" }, "Save"),
548
+ React.createElement(Button, { size: "xs", onClick: onCancelForm, type: "button" }, "Cancel"))));
549
+ };
550
+ export default ChartEditor;
@@ -0,0 +1,17 @@
1
+ import type { SheetRange } from "@rowsncolumns/spreadsheet";
2
+ import React from "react";
3
+ type ChartRangeInputProps = {
4
+ sheetId: number;
5
+ range?: SheetRange;
6
+ onChangeRange(range: SheetRange): void;
7
+ required?: boolean;
8
+ placeholder?: string;
9
+ className?: string;
10
+ };
11
+ /**
12
+ * Formula range input that keeps the raw text the user is typing in
13
+ * local state and only forwards complete, valid ranges to the caller.
14
+ */
15
+ export declare const ChartRangeInput: ({ sheetId, range, onChangeRange, required, placeholder, className, }: ChartRangeInputProps) => React.JSX.Element;
16
+ export {};
17
+ //# sourceMappingURL=chart-range-input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-range-input.d.ts","sourceRoot":"","sources":["../../../../components/editors/chart-range-input.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,KAAK,oBAAoB,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAI,sEAO7B,oBAAoB,sBAWtB,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { FormulaInput, useFormulaInputState } from "@rowsncolumns/spreadsheet";
2
+ import React from "react";
3
+ /**
4
+ * Formula range input that keeps the raw text the user is typing in
5
+ * local state and only forwards complete, valid ranges to the caller.
6
+ */
7
+ export const ChartRangeInput = ({ sheetId, range, onChangeRange, required, placeholder, className, }) => {
8
+ const formulaInput = useFormulaInputState(sheetId, range);
9
+ return (React.createElement(FormulaInput, { value: formulaInput.value, onChange: (value) => formulaInput.onChange(value, onChangeRange), required: required, placeholder: placeholder, className: className }));
10
+ };