@rowsncolumns/charts 28.0.0 → 28.1.1

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 (193) hide show
  1. package/dist/cjs/atoms/pivot-chart.atom.d.ts +14 -0
  2. package/dist/cjs/atoms/pivot-chart.atom.d.ts.map +1 -0
  3. package/dist/cjs/atoms/pivot-chart.atom.js +9 -0
  4. package/dist/cjs/basic-chart/basic-chart.d.ts +1 -1
  5. package/dist/cjs/basic-chart/basic-chart.d.ts.map +1 -1
  6. package/dist/cjs/basic-chart/basic-chart.js +863 -371
  7. package/dist/cjs/components/dialogs/pivot-chart-dialog.d.ts +39 -0
  8. package/dist/cjs/components/dialogs/pivot-chart-dialog.d.ts.map +1 -0
  9. package/dist/cjs/components/dialogs/pivot-chart-dialog.js +100 -0
  10. package/dist/cjs/components/editors/chart-editor.d.ts +13 -1
  11. package/dist/cjs/components/editors/chart-editor.d.ts.map +1 -1
  12. package/dist/cjs/components/editors/chart-editor.js +225 -59
  13. package/dist/cjs/config.d.ts.map +1 -1
  14. package/dist/cjs/config.js +149 -12
  15. package/dist/cjs/hooks/use-charts.d.ts +21 -6
  16. package/dist/cjs/hooks/use-charts.d.ts.map +1 -1
  17. package/dist/cjs/hooks/use-charts.js +13 -1
  18. package/dist/cjs/hooks/use-on-create-chart.d.ts.map +1 -1
  19. package/dist/cjs/hooks/use-on-create-chart.js +7 -1
  20. package/dist/cjs/hooks/use-on-move-chart-to-sheet.d.ts +12 -0
  21. package/dist/cjs/hooks/use-on-move-chart-to-sheet.d.ts.map +1 -0
  22. package/dist/cjs/hooks/use-on-move-chart-to-sheet.js +26 -0
  23. package/dist/cjs/hooks/use-on-move-chart.d.ts +1 -1
  24. package/dist/cjs/hooks/use-on-move-chart.d.ts.map +1 -1
  25. package/dist/cjs/hooks/use-on-move-chart.js +2 -2
  26. package/dist/cjs/hooks/use-on-resize-chart.d.ts +1 -1
  27. package/dist/cjs/hooks/use-on-resize-chart.d.ts.map +1 -1
  28. package/dist/cjs/hooks/use-on-resize-chart.js +2 -2
  29. package/dist/cjs/index.d.ts +11 -0
  30. package/dist/cjs/index.d.ts.map +1 -1
  31. package/dist/cjs/index.js +34 -1
  32. package/dist/cjs/interface/create-chart.d.ts.map +1 -1
  33. package/dist/cjs/interface/create-chart.js +33 -42
  34. package/dist/cjs/interface/delete-chart.d.ts.map +1 -1
  35. package/dist/cjs/interface/delete-chart.js +4 -0
  36. package/dist/cjs/interface/move-chart-to-sheet.d.ts +13 -0
  37. package/dist/cjs/interface/move-chart-to-sheet.d.ts.map +1 -0
  38. package/dist/cjs/interface/move-chart-to-sheet.js +136 -0
  39. package/dist/cjs/interface/move-chart.d.ts +2 -2
  40. package/dist/cjs/interface/move-chart.d.ts.map +1 -1
  41. package/dist/cjs/interface/move-chart.js +7 -1
  42. package/dist/cjs/interface/resize-chart.d.ts +2 -2
  43. package/dist/cjs/interface/resize-chart.d.ts.map +1 -1
  44. package/dist/cjs/interface/resize-chart.js +7 -1
  45. package/dist/cjs/interface/update-chart.d.ts.map +1 -1
  46. package/dist/cjs/interface/update-chart.js +4 -0
  47. package/dist/cjs/plotly/basic-chart/basic-chart.d.ts +1 -1
  48. package/dist/cjs/plotly/basic-chart/basic-chart.d.ts.map +1 -1
  49. package/dist/cjs/plotly/basic-chart/basic-chart.js +970 -585
  50. package/dist/cjs/plotly/basic-chart/helpers.d.ts.map +1 -1
  51. package/dist/cjs/plotly/basic-chart/helpers.js +3 -0
  52. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  53. package/dist/cjs/types.d.ts +7 -0
  54. package/dist/cjs/types.d.ts.map +1 -1
  55. package/dist/cjs/utils/axis-number-format.d.ts +69 -0
  56. package/dist/cjs/utils/axis-number-format.d.ts.map +1 -0
  57. package/dist/cjs/utils/axis-number-format.js +217 -0
  58. package/dist/cjs/utils/category-levels.d.ts +27 -0
  59. package/dist/cjs/utils/category-levels.d.ts.map +1 -0
  60. package/dist/cjs/utils/category-levels.js +80 -0
  61. package/dist/cjs/utils/chart-image.d.ts +8 -0
  62. package/dist/cjs/utils/chart-image.d.ts.map +1 -0
  63. package/dist/cjs/utils/chart-image.js +28 -0
  64. package/dist/cjs/utils/chart-orientation.d.ts +67 -0
  65. package/dist/cjs/utils/chart-orientation.d.ts.map +1 -0
  66. package/dist/cjs/utils/chart-orientation.js +265 -0
  67. package/dist/cjs/utils/chart-spec-utils.d.ts +3 -0
  68. package/dist/cjs/utils/chart-spec-utils.d.ts.map +1 -1
  69. package/dist/cjs/utils/chart-spec-utils.js +12 -4
  70. package/dist/cjs/utils/chart-styles.d.ts +75 -0
  71. package/dist/cjs/utils/chart-styles.d.ts.map +1 -0
  72. package/dist/cjs/utils/chart-styles.js +283 -0
  73. package/dist/cjs/utils/empty-cells.d.ts +30 -0
  74. package/dist/cjs/utils/empty-cells.d.ts.map +1 -0
  75. package/dist/cjs/utils/empty-cells.js +91 -0
  76. package/dist/cjs/utils/overrides.d.ts +6 -1
  77. package/dist/cjs/utils/overrides.d.ts.map +1 -1
  78. package/dist/cjs/utils/overrides.js +19 -7
  79. package/dist/cjs/utils/pie-explosion.d.ts +30 -0
  80. package/dist/cjs/utils/pie-explosion.d.ts.map +1 -0
  81. package/dist/cjs/utils/pie-explosion.js +55 -0
  82. package/dist/cjs/utils/plotly-click.d.ts +34 -0
  83. package/dist/cjs/utils/plotly-click.d.ts.map +1 -0
  84. package/dist/cjs/utils/plotly-click.js +49 -0
  85. package/dist/cjs/utils/plotly-surface.d.ts +14 -0
  86. package/dist/cjs/utils/plotly-surface.d.ts.map +1 -0
  87. package/dist/cjs/utils/plotly-surface.js +64 -0
  88. package/dist/cjs/utils/plotly-title.d.ts +12 -0
  89. package/dist/cjs/utils/plotly-title.d.ts.map +1 -0
  90. package/dist/cjs/utils/plotly-title.js +24 -0
  91. package/dist/cjs/utils/series-click.d.ts +39 -0
  92. package/dist/cjs/utils/series-click.d.ts.map +1 -0
  93. package/dist/cjs/utils/series-click.js +75 -0
  94. package/dist/cjs/utils/three-d.d.ts +17 -1
  95. package/dist/cjs/utils/three-d.d.ts.map +1 -1
  96. package/dist/cjs/utils/three-d.js +32 -4
  97. package/dist/esm/atoms/pivot-chart.atom.d.ts +14 -0
  98. package/dist/esm/atoms/pivot-chart.atom.d.ts.map +1 -0
  99. package/dist/esm/atoms/pivot-chart.atom.js +4 -0
  100. package/dist/esm/basic-chart/basic-chart.d.ts +1 -1
  101. package/dist/esm/basic-chart/basic-chart.d.ts.map +1 -1
  102. package/dist/esm/basic-chart/basic-chart.js +680 -178
  103. package/dist/esm/components/dialogs/pivot-chart-dialog.d.ts +39 -0
  104. package/dist/esm/components/dialogs/pivot-chart-dialog.d.ts.map +1 -0
  105. package/dist/esm/components/dialogs/pivot-chart-dialog.js +89 -0
  106. package/dist/esm/components/editors/chart-editor.d.ts +13 -1
  107. package/dist/esm/components/editors/chart-editor.d.ts.map +1 -1
  108. package/dist/esm/components/editors/chart-editor.js +164 -20
  109. package/dist/esm/config.d.ts.map +1 -1
  110. package/dist/esm/config.js +149 -12
  111. package/dist/esm/hooks/use-charts.d.ts +21 -6
  112. package/dist/esm/hooks/use-charts.d.ts.map +1 -1
  113. package/dist/esm/hooks/use-charts.js +13 -1
  114. package/dist/esm/hooks/use-on-create-chart.d.ts.map +1 -1
  115. package/dist/esm/hooks/use-on-create-chart.js +7 -1
  116. package/dist/esm/hooks/use-on-move-chart-to-sheet.d.ts +12 -0
  117. package/dist/esm/hooks/use-on-move-chart-to-sheet.d.ts.map +1 -0
  118. package/dist/esm/hooks/use-on-move-chart-to-sheet.js +19 -0
  119. package/dist/esm/hooks/use-on-move-chart.d.ts +1 -1
  120. package/dist/esm/hooks/use-on-move-chart.d.ts.map +1 -1
  121. package/dist/esm/hooks/use-on-move-chart.js +2 -2
  122. package/dist/esm/hooks/use-on-resize-chart.d.ts +1 -1
  123. package/dist/esm/hooks/use-on-resize-chart.d.ts.map +1 -1
  124. package/dist/esm/hooks/use-on-resize-chart.js +2 -2
  125. package/dist/esm/index.d.ts +11 -0
  126. package/dist/esm/index.d.ts.map +1 -1
  127. package/dist/esm/index.js +14 -0
  128. package/dist/esm/interface/create-chart.d.ts.map +1 -1
  129. package/dist/esm/interface/create-chart.js +36 -58
  130. package/dist/esm/interface/delete-chart.d.ts.map +1 -1
  131. package/dist/esm/interface/delete-chart.js +4 -0
  132. package/dist/esm/interface/move-chart-to-sheet.d.ts +13 -0
  133. package/dist/esm/interface/move-chart-to-sheet.d.ts.map +1 -0
  134. package/dist/esm/interface/move-chart-to-sheet.js +126 -0
  135. package/dist/esm/interface/move-chart.d.ts +2 -2
  136. package/dist/esm/interface/move-chart.d.ts.map +1 -1
  137. package/dist/esm/interface/move-chart.js +7 -1
  138. package/dist/esm/interface/resize-chart.d.ts +2 -2
  139. package/dist/esm/interface/resize-chart.d.ts.map +1 -1
  140. package/dist/esm/interface/resize-chart.js +7 -1
  141. package/dist/esm/interface/update-chart.d.ts.map +1 -1
  142. package/dist/esm/interface/update-chart.js +4 -0
  143. package/dist/esm/plotly/basic-chart/basic-chart.d.ts +1 -1
  144. package/dist/esm/plotly/basic-chart/basic-chart.d.ts.map +1 -1
  145. package/dist/esm/plotly/basic-chart/basic-chart.js +858 -463
  146. package/dist/esm/plotly/basic-chart/helpers.d.ts.map +1 -1
  147. package/dist/esm/plotly/basic-chart/helpers.js +3 -0
  148. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  149. package/dist/esm/types.d.ts +7 -0
  150. package/dist/esm/types.d.ts.map +1 -1
  151. package/dist/esm/utils/axis-number-format.d.ts +69 -0
  152. package/dist/esm/utils/axis-number-format.d.ts.map +1 -0
  153. package/dist/esm/utils/axis-number-format.js +170 -0
  154. package/dist/esm/utils/category-levels.d.ts +27 -0
  155. package/dist/esm/utils/category-levels.d.ts.map +1 -0
  156. package/dist/esm/utils/category-levels.js +65 -0
  157. package/dist/esm/utils/chart-image.d.ts +8 -0
  158. package/dist/esm/utils/chart-image.d.ts.map +1 -0
  159. package/dist/esm/utils/chart-image.js +24 -0
  160. package/dist/esm/utils/chart-orientation.d.ts +67 -0
  161. package/dist/esm/utils/chart-orientation.d.ts.map +1 -0
  162. package/dist/esm/utils/chart-orientation.js +234 -0
  163. package/dist/esm/utils/chart-spec-utils.d.ts +3 -0
  164. package/dist/esm/utils/chart-spec-utils.d.ts.map +1 -1
  165. package/dist/esm/utils/chart-spec-utils.js +10 -2
  166. package/dist/esm/utils/chart-styles.d.ts +75 -0
  167. package/dist/esm/utils/chart-styles.d.ts.map +1 -0
  168. package/dist/esm/utils/chart-styles.js +357 -0
  169. package/dist/esm/utils/empty-cells.d.ts +30 -0
  170. package/dist/esm/utils/empty-cells.d.ts.map +1 -0
  171. package/dist/esm/utils/empty-cells.js +67 -0
  172. package/dist/esm/utils/overrides.d.ts +6 -1
  173. package/dist/esm/utils/overrides.d.ts.map +1 -1
  174. package/dist/esm/utils/overrides.js +14 -2
  175. package/dist/esm/utils/pie-explosion.d.ts +30 -0
  176. package/dist/esm/utils/pie-explosion.d.ts.map +1 -0
  177. package/dist/esm/utils/pie-explosion.js +43 -0
  178. package/dist/esm/utils/plotly-click.d.ts +34 -0
  179. package/dist/esm/utils/plotly-click.d.ts.map +1 -0
  180. package/dist/esm/utils/plotly-click.js +44 -0
  181. package/dist/esm/utils/plotly-surface.d.ts +14 -0
  182. package/dist/esm/utils/plotly-surface.d.ts.map +1 -0
  183. package/dist/esm/utils/plotly-surface.js +45 -0
  184. package/dist/esm/utils/plotly-title.d.ts +12 -0
  185. package/dist/esm/utils/plotly-title.d.ts.map +1 -0
  186. package/dist/esm/utils/plotly-title.js +20 -0
  187. package/dist/esm/utils/series-click.d.ts +39 -0
  188. package/dist/esm/utils/series-click.d.ts.map +1 -0
  189. package/dist/esm/utils/series-click.js +66 -0
  190. package/dist/esm/utils/three-d.d.ts +17 -1
  191. package/dist/esm/utils/three-d.d.ts.map +1 -1
  192. package/dist/esm/utils/three-d.js +29 -3
  193. package/package.json +13 -9
@@ -0,0 +1,357 @@
1
+ /**
2
+ * Office's Change Colors gallery. Colorful 1 is the theme itself (no
3
+ * `colors` — the renderer keeps its accent fallback); the rest are the
4
+ * default Office theme's derived palettes.
5
+ */
6
+ export const CHART_COLOR_PALETTES = [
7
+ { id: "colorful1", label: "Colorful Palette 1", kind: "colorful" },
8
+ {
9
+ id: "colorful2",
10
+ label: "Colorful Palette 2",
11
+ kind: "colorful",
12
+ colors: ["#4472C4", "#A5A5A5", "#5B9BD5", "#264478", "#636363", "#255E91"],
13
+ },
14
+ {
15
+ id: "colorful3",
16
+ label: "Colorful Palette 3",
17
+ kind: "colorful",
18
+ colors: ["#ED7D31", "#FFC000", "#70AD47", "#9E480E", "#997300", "#43682B"],
19
+ },
20
+ {
21
+ id: "colorful4",
22
+ label: "Colorful Palette 4",
23
+ kind: "colorful",
24
+ colors: ["#70AD47", "#4472C4", "#FFC000", "#43682B", "#264478", "#997300"],
25
+ },
26
+ {
27
+ id: "mono1",
28
+ label: "Monochromatic Palette 1",
29
+ kind: "monochromatic",
30
+ colors: ["#1F3864", "#2F5597", "#4472C4", "#8FAADC", "#B4C7E7", "#DAE3F3"],
31
+ },
32
+ {
33
+ id: "mono2",
34
+ label: "Monochromatic Palette 2",
35
+ kind: "monochromatic",
36
+ colors: ["#833C0B", "#C55A11", "#ED7D31", "#F4B183", "#F8CBAD", "#FBE5D6"],
37
+ },
38
+ {
39
+ id: "mono3",
40
+ label: "Monochromatic Palette 3",
41
+ kind: "monochromatic",
42
+ colors: ["#525252", "#7B7B7B", "#A5A5A5", "#C9C9C9", "#DBDBDB", "#EDEDED"],
43
+ },
44
+ {
45
+ id: "mono4",
46
+ label: "Monochromatic Palette 4",
47
+ kind: "monochromatic",
48
+ colors: ["#375623", "#548235", "#70AD47", "#A9D18E", "#C5E0B4", "#E2F0D9"],
49
+ },
50
+ ];
51
+ const stripSeriesColor = (entry) => {
52
+ const { color: _color, gradient: _gradient, dataPointColors: _dataPointColors, negativeColor: _negativeColor, ...rest } = entry;
53
+ return rest;
54
+ };
55
+ /**
56
+ * Strip the per-series colour overrides so the chart-level palette shows
57
+ * through. Hierarchy specs (treemap / sunburst) carry no series.
58
+ */
59
+ const withoutSeriesColors = (spec) => "series" in spec && Array.isArray(spec.series)
60
+ ? { ...spec, series: spec.series.map(stripSeriesColor) }
61
+ : spec;
62
+ /**
63
+ * Change Colors: set the palette and clear every series' own colour so the
64
+ * palette applies (a series colour always wins over the palette).
65
+ */
66
+ export const applyChartColorPalette = (spec, palette) => ({
67
+ ...withoutSeriesColors(spec),
68
+ palette: palette.colors ? [...palette.colors] : undefined,
69
+ });
70
+ /**
71
+ * Reset to Match Style: drop the per-series formatting overrides so the
72
+ * series follow the chart style's palette again. Chart-level fields are
73
+ * untouched — they *are* the style.
74
+ */
75
+ export const resetChartToMatchStyle = (spec) => withoutSeriesColors(spec);
76
+ /**
77
+ * The series colour cycle a renderer should use: the spec's palette when
78
+ * one is set, else the caller's theme accents.
79
+ */
80
+ export const getChartPalette = (spec, fallback) => spec?.palette && spec.palette.length > 0
81
+ ? [...spec.palette]
82
+ : [...fallback];
83
+ const GRAYSCALE_PALETTE = [
84
+ "#404040",
85
+ "#7F7F7F",
86
+ "#A5A5A5",
87
+ "#BFBFBF",
88
+ "#D9D9D9",
89
+ "#595959",
90
+ ];
91
+ const MONOCHROME_PALETTE = CHART_COLOR_PALETTES.find((palette) => palette.id === "mono1").colors;
92
+ const COLORFUL_PREVIEW = ["#4472C4", "#ED7D31", "#A5A5A5", "#FFC000"];
93
+ const setTitleFormat = (spec, format) => ({
94
+ ...(spec.titleTextFormat ?? {}),
95
+ ...format,
96
+ });
97
+ const setAxisGridlines = (spec, show) => ({
98
+ valueAxisOptions: {
99
+ ...(spec.valueAxisOptions ?? {}),
100
+ showMajorGridlines: show,
101
+ },
102
+ });
103
+ /** Text colour for every chart text element (dark-frame styles). */
104
+ const setTextColor = (spec, color) => ({
105
+ titleTextFormat: setTitleFormat(spec, { color }),
106
+ legend: {
107
+ ...(spec.legend ?? {}),
108
+ textFormat: { ...(spec.legend?.textFormat ?? {}), color },
109
+ },
110
+ valueAxisOptions: {
111
+ ...(spec.valueAxisOptions ?? {}),
112
+ textFormat: { ...(spec.valueAxisOptions?.textFormat ?? {}), color },
113
+ },
114
+ categoryAxisOptions: {
115
+ ...(spec.categoryAxisOptions ?? {}),
116
+ textFormat: { ...(spec.categoryAxisOptions?.textFormat ?? {}), color },
117
+ },
118
+ dataLabels: {
119
+ ...(spec.dataLabels ?? {}),
120
+ textFormat: { ...(spec.dataLabels?.textFormat ?? {}), color },
121
+ },
122
+ });
123
+ /**
124
+ * The Chart Styles gallery. Each entry pins the look Excel's style of that
125
+ * id implies onto the spec fields both renderers read: chart-area fill,
126
+ * gridlines, data labels, legend placement, title font and — for the
127
+ * grayscale / monochromatic rows — the palette. Per-series colours are
128
+ * left alone (Reset to Match Style clears them).
129
+ */
130
+ export const CHART_STYLES = [
131
+ {
132
+ id: 2,
133
+ label: "Style 1",
134
+ preview: { background: "#FFFFFF", bars: COLORFUL_PREVIEW },
135
+ apply: (spec) => ({
136
+ ...spec,
137
+ chartStyle: 2,
138
+ backgroundColor: undefined,
139
+ ...setAxisGridlines(spec, true),
140
+ dataLabels: { ...(spec.dataLabels ?? {}), show: false },
141
+ titleTextFormat: setTitleFormat(spec, {
142
+ fontSize: 14,
143
+ bold: false,
144
+ color: undefined,
145
+ }),
146
+ }),
147
+ },
148
+ {
149
+ id: 10,
150
+ label: "Style 2",
151
+ preview: { background: "#FFFFFF", bars: COLORFUL_PREVIEW },
152
+ apply: (spec) => ({
153
+ ...spec,
154
+ chartStyle: 10,
155
+ backgroundColor: undefined,
156
+ ...setAxisGridlines(spec, true),
157
+ dataLabels: {
158
+ ...(spec.dataLabels ?? {}),
159
+ show: true,
160
+ position: "outsideEnd",
161
+ },
162
+ legend: { ...(spec.legend ?? {}), position: "top" },
163
+ titleTextFormat: setTitleFormat(spec, { fontSize: 16, bold: true }),
164
+ }),
165
+ },
166
+ {
167
+ id: 18,
168
+ label: "Style 3",
169
+ preview: { background: "#FFFFFF", bars: COLORFUL_PREVIEW },
170
+ apply: (spec) => ({
171
+ ...spec,
172
+ chartStyle: 18,
173
+ backgroundColor: undefined,
174
+ ...setAxisGridlines(spec, false),
175
+ dataLabels: {
176
+ ...(spec.dataLabels ?? {}),
177
+ show: true,
178
+ position: "insideEnd",
179
+ },
180
+ legend: { ...(spec.legend ?? {}), position: "bottom" },
181
+ titleTextFormat: setTitleFormat(spec, { fontSize: 14, bold: true }),
182
+ }),
183
+ },
184
+ {
185
+ id: 26,
186
+ label: "Style 4",
187
+ preview: { background: "#F2F2F2", bars: COLORFUL_PREVIEW },
188
+ apply: (spec) => ({
189
+ ...spec,
190
+ chartStyle: 26,
191
+ backgroundColor: "#F2F2F2",
192
+ ...setAxisGridlines(spec, true),
193
+ dataLabels: { ...(spec.dataLabels ?? {}), show: false },
194
+ legend: { ...(spec.legend ?? {}), position: "bottom" },
195
+ titleTextFormat: setTitleFormat(spec, { fontSize: 18, bold: true }),
196
+ }),
197
+ },
198
+ {
199
+ id: 1,
200
+ label: "Style 5",
201
+ preview: { background: "#FFFFFF", bars: GRAYSCALE_PALETTE.slice(0, 4) },
202
+ apply: (spec) => ({
203
+ ...spec,
204
+ chartStyle: 1,
205
+ backgroundColor: undefined,
206
+ palette: [...GRAYSCALE_PALETTE],
207
+ ...setAxisGridlines(spec, true),
208
+ titleTextFormat: setTitleFormat(spec, { fontSize: 14, bold: false }),
209
+ }),
210
+ },
211
+ {
212
+ id: 3,
213
+ label: "Style 6",
214
+ preview: { background: "#FFFFFF", bars: MONOCHROME_PALETTE.slice(0, 4) },
215
+ apply: (spec) => ({
216
+ ...spec,
217
+ chartStyle: 3,
218
+ backgroundColor: undefined,
219
+ palette: [...MONOCHROME_PALETTE],
220
+ ...setAxisGridlines(spec, true),
221
+ titleTextFormat: setTitleFormat(spec, { fontSize: 14, bold: false }),
222
+ }),
223
+ },
224
+ {
225
+ id: 34,
226
+ label: "Style 7",
227
+ preview: { background: "#404040", bars: COLORFUL_PREVIEW },
228
+ apply: (spec) => ({
229
+ ...spec,
230
+ chartStyle: 34,
231
+ backgroundColor: "#404040",
232
+ ...setTextColor(spec, "#F2F2F2"),
233
+ valueAxisOptions: {
234
+ ...(spec.valueAxisOptions ?? {}),
235
+ textFormat: { ...(spec.valueAxisOptions?.textFormat ?? {}), color: "#F2F2F2" },
236
+ showMajorGridlines: true,
237
+ },
238
+ titleTextFormat: setTitleFormat(spec, {
239
+ fontSize: 16,
240
+ bold: true,
241
+ color: "#FFFFFF",
242
+ }),
243
+ }),
244
+ },
245
+ {
246
+ id: 42,
247
+ label: "Style 8",
248
+ preview: { background: "#000000", bars: COLORFUL_PREVIEW },
249
+ apply: (spec) => ({
250
+ ...spec,
251
+ chartStyle: 42,
252
+ backgroundColor: "#000000",
253
+ ...setTextColor(spec, "#FFFFFF"),
254
+ valueAxisOptions: {
255
+ ...(spec.valueAxisOptions ?? {}),
256
+ textFormat: { ...(spec.valueAxisOptions?.textFormat ?? {}), color: "#FFFFFF" },
257
+ showMajorGridlines: false,
258
+ },
259
+ titleTextFormat: setTitleFormat(spec, {
260
+ fontSize: 16,
261
+ bold: true,
262
+ color: "#FFFFFF",
263
+ }),
264
+ }),
265
+ },
266
+ ];
267
+ export const applyChartStyle = (spec, style) => style.apply(spec);
268
+ /** Excel inserts placeholder axis titles when a layout shows axis titles the chart lacks. */
269
+ const AXIS_TITLE_PLACEHOLDER = "Axis Title";
270
+ const withAxisTitles = (spec, show) => show
271
+ ? {
272
+ verticalAxisTitle: spec.verticalAxisTitle || AXIS_TITLE_PLACEHOLDER,
273
+ horizontalAxisTitle: spec.horizontalAxisTitle || AXIS_TITLE_PLACEHOLDER,
274
+ }
275
+ : { verticalAxisTitle: undefined, horizontalAxisTitle: undefined };
276
+ const withDataLabels = (spec, show, position) => ({
277
+ dataLabels: {
278
+ ...(spec.dataLabels ?? {}),
279
+ show,
280
+ ...(show && position ? { position } : {}),
281
+ },
282
+ });
283
+ /**
284
+ * Excel's Quick Layout gallery (the first six of the column-chart set).
285
+ * Layouts arrange the existing elements; they never touch colours.
286
+ */
287
+ export const CHART_QUICK_LAYOUTS = [
288
+ {
289
+ id: 1,
290
+ label: "Layout 1",
291
+ apply: (spec) => ({
292
+ ...spec,
293
+ legend: { ...(spec.legend ?? {}), position: "right" },
294
+ ...withDataLabels(spec, false),
295
+ ...withAxisTitles(spec, false),
296
+ }),
297
+ },
298
+ {
299
+ id: 2,
300
+ label: "Layout 2",
301
+ apply: (spec) => ({
302
+ ...spec,
303
+ legend: { ...(spec.legend ?? {}), position: "top" },
304
+ ...withDataLabels(spec, true, "outsideEnd"),
305
+ ...withAxisTitles(spec, false),
306
+ valueAxisOptions: {
307
+ ...(spec.valueAxisOptions ?? {}),
308
+ showMajorGridlines: false,
309
+ },
310
+ }),
311
+ },
312
+ {
313
+ id: 3,
314
+ label: "Layout 3",
315
+ apply: (spec) => ({
316
+ ...spec,
317
+ legend: { ...(spec.legend ?? {}), position: "bottom" },
318
+ ...withDataLabels(spec, false),
319
+ ...withAxisTitles(spec, false),
320
+ }),
321
+ },
322
+ {
323
+ id: 4,
324
+ label: "Layout 4",
325
+ apply: (spec) => ({
326
+ ...spec,
327
+ legend: { ...(spec.legend ?? {}), position: "none" },
328
+ ...withDataLabels(spec, true, "insideEnd"),
329
+ ...withAxisTitles(spec, false),
330
+ valueAxisOptions: {
331
+ ...(spec.valueAxisOptions ?? {}),
332
+ showMajorGridlines: false,
333
+ },
334
+ }),
335
+ },
336
+ {
337
+ id: 5,
338
+ label: "Layout 5",
339
+ apply: (spec) => ({
340
+ ...spec,
341
+ legend: { ...(spec.legend ?? {}), position: "none" },
342
+ ...withDataLabels(spec, false),
343
+ ...withAxisTitles(spec, true),
344
+ }),
345
+ },
346
+ {
347
+ id: 6,
348
+ label: "Layout 6",
349
+ apply: (spec) => ({
350
+ ...spec,
351
+ legend: { ...(spec.legend ?? {}), position: "right" },
352
+ ...withDataLabels(spec, false),
353
+ ...withAxisTitles(spec, true),
354
+ }),
355
+ },
356
+ ];
357
+ export const applyChartQuickLayout = (spec, layout) => layout.apply(spec);
@@ -0,0 +1,30 @@
1
+ import type { EmbeddedChart } from "@rowsncolumns/spreadsheet";
2
+ type ChartSpec = EmbeddedChart["spec"];
3
+ export type EmptyCellsPolicy = NonNullable<ChartSpec["emptyCells"]>;
4
+ type EmptyCellsSpec = Pick<ChartSpec, "emptyCells" | "showNaAsEmpty">;
5
+ export declare const isChartErrorValue: (value: unknown) => boolean;
6
+ /** Excel's default is a gap. */
7
+ export declare const getEmptyCellsPolicy: (spec: EmptyCellsSpec) => EmptyCellsPolicy;
8
+ /** Only a line-like series has a line to carry across the hole. */
9
+ export declare const isLineLikeChartType: (chartType: ChartSpec["chartType"] | null | undefined) => boolean;
10
+ /**
11
+ * "Connect data points with line" — the renderer bridges the gap
12
+ * (echarts `connectNulls`, plotly `connectgaps`).
13
+ */
14
+ export declare const connectsAcrossEmptyCells: (spec: EmptyCellsSpec, lineLike: boolean) => boolean;
15
+ /**
16
+ * Plot-ready values for one series under the chart's "Hidden and Empty
17
+ * Cells" settings. `values` are the sanitized numbers (blanks and errors
18
+ * already `undefined`), `rawValues` the cells as read, index-aligned, so
19
+ * a blank can be told from an error:
20
+ *
21
+ * - a blank follows `emptyCells`: `"zero"` plots 0, `"gaps"` and
22
+ * `"connect"` leave `null` (the renderer's connect flag bridges it);
23
+ * - an error is a blank while `showNaAsEmpty` is on (Excel's default);
24
+ * with it off a line-like series is drawn straight through the point
25
+ * (linear interpolation between its numeric neighbours — Excel's
26
+ * pre-2016 #N/A behaviour), and any other series leaves a gap.
27
+ */
28
+ export declare const applyEmptyCellsPolicy: (values: readonly (number | null | undefined)[], rawValues: readonly unknown[], spec: EmptyCellsSpec, lineLike: boolean) => (number | null)[];
29
+ export {};
30
+ //# sourceMappingURL=empty-cells.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"empty-cells.d.ts","sourceRoot":"","sources":["../../../utils/empty-cells.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,KAAK,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;AAEvC,MAAM,MAAM,gBAAgB,GAAG,WAAW,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC;AAEpE,KAAK,cAAc,GAAG,IAAI,CAAC,SAAS,EAAE,YAAY,GAAG,eAAe,CAAC,CAAC;AAetE,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,OACuB,CAAC;AAE3E,gCAAgC;AAChC,eAAO,MAAM,mBAAmB,GAAI,MAAM,cAAc,KAAG,gBAChC,CAAC;AAE5B,mEAAmE;AACnE,eAAO,MAAM,mBAAmB,GAC9B,WAAW,SAAS,CAAC,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,KACnD,OAAuD,CAAC;AAE3D;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GACnC,MAAM,cAAc,EACpB,UAAU,OAAO,KAChB,OAA8D,CAAC;AAElE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,SAAS,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,EAC9C,WAAW,SAAS,OAAO,EAAE,EAC7B,MAAM,cAAc,EACpB,UAAU,OAAO,KAChB,CAAC,MAAM,GAAG,IAAI,CAAC,EA4BjB,CAAC"}
@@ -0,0 +1,67 @@
1
+ const CHART_ERROR_VALUES = new Set([
2
+ "#N/A",
3
+ "#VALUE!",
4
+ "#REF!",
5
+ "#DIV/0!",
6
+ "#NUM!",
7
+ "#NAME?",
8
+ "#NULL!",
9
+ "#SPILL!",
10
+ "#CALC!",
11
+ "#GETTING_DATA",
12
+ ]);
13
+ export const isChartErrorValue = (value) => typeof value === "string" && CHART_ERROR_VALUES.has(value.toUpperCase());
14
+ /** Excel's default is a gap. */
15
+ export const getEmptyCellsPolicy = (spec) => spec.emptyCells ?? "gaps";
16
+ /** Only a line-like series has a line to carry across the hole. */
17
+ export const isLineLikeChartType = (chartType) => chartType === "line" || chartType === "area";
18
+ /**
19
+ * "Connect data points with line" — the renderer bridges the gap
20
+ * (echarts `connectNulls`, plotly `connectgaps`).
21
+ */
22
+ export const connectsAcrossEmptyCells = (spec, lineLike) => lineLike && getEmptyCellsPolicy(spec) === "connect";
23
+ /**
24
+ * Plot-ready values for one series under the chart's "Hidden and Empty
25
+ * Cells" settings. `values` are the sanitized numbers (blanks and errors
26
+ * already `undefined`), `rawValues` the cells as read, index-aligned, so
27
+ * a blank can be told from an error:
28
+ *
29
+ * - a blank follows `emptyCells`: `"zero"` plots 0, `"gaps"` and
30
+ * `"connect"` leave `null` (the renderer's connect flag bridges it);
31
+ * - an error is a blank while `showNaAsEmpty` is on (Excel's default);
32
+ * with it off a line-like series is drawn straight through the point
33
+ * (linear interpolation between its numeric neighbours — Excel's
34
+ * pre-2016 #N/A behaviour), and any other series leaves a gap.
35
+ */
36
+ export const applyEmptyCellsPolicy = (values, rawValues, spec, lineLike) => {
37
+ const policy = getEmptyCellsPolicy(spec);
38
+ const naAsEmpty = spec.showNaAsEmpty !== false;
39
+ const plotted = values.map((value) => typeof value === "number" && Number.isFinite(value) ? value : null);
40
+ const legacyErrors = [];
41
+ for (let i = 0; i < plotted.length; i++) {
42
+ if (plotted[i] !== null)
43
+ continue;
44
+ if (!naAsEmpty && isChartErrorValue(rawValues[i])) {
45
+ legacyErrors.push(i);
46
+ continue;
47
+ }
48
+ if (policy === "zero")
49
+ plotted[i] = 0;
50
+ }
51
+ if (lineLike) {
52
+ for (const i of legacyErrors) {
53
+ let prev = i - 1;
54
+ while (prev >= 0 && plotted[prev] === null)
55
+ prev--;
56
+ let next = i + 1;
57
+ while (next < plotted.length && plotted[next] === null)
58
+ next++;
59
+ if (prev < 0 || next >= plotted.length)
60
+ continue;
61
+ const a = plotted[prev];
62
+ const b = plotted[next];
63
+ plotted[i] = a + ((b - a) * (i - prev)) / (next - prev);
64
+ }
65
+ }
66
+ return plotted;
67
+ };
@@ -2,7 +2,12 @@ import type { LegendComponentOption } from "echarts";
2
2
  /** Build an echarts `textStyle` from a parsed `ChartTextFormat`. */
3
3
  export declare const textFormatToEchartsTextStyle: (fmt: any) => Record<string, any> | undefined;
4
4
  export declare const applyLegendOverrides: (legend: LegendComponentOption, opts: any) => LegendComponentOption;
5
- export declare const applyAxisOverrides: (axis: any, opts: any) => any;
5
+ /**
6
+ * `numberFormatPattern` is the axis' RESOLVED format (explicit pattern or
7
+ * the linked source-cell format — see `resolveValueAxisNumberFormat`); it
8
+ * becomes the tick-label formatter.
9
+ */
10
+ export declare const applyAxisOverrides: (axis: any, opts: any, numberFormatPattern?: string) => any;
6
11
  export declare const dataLabelFormatter: (opts: any) => (params: any) => string;
7
12
  export declare const buildSeriesLabelOption: (opts: any) => {
8
13
  readonly show: true;
@@ -1 +1 @@
1
- {"version":3,"file":"overrides.d.ts","sourceRoot":"","sources":["../../../utils/overrides.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AASrD,oEAAoE;AACpE,eAAO,MAAM,4BAA4B,GAAI,KAAK,GAAG,oCASpD,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,qBAAqB,EAC7B,MAAM,GAAG,KACR,qBA6CF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,MAAM,GAAG,EAAE,MAAM,GAAG,QAsBtD,CAAC;AAqBF,eAAO,MAAM,kBAAkB,GAAI,MAAM,GAAG,MAAM,QAAQ,GAAG,WA4B5D,CAAC;AAWF,eAAO,MAAM,sBAAsB,GAAI,MAAM,GAAG;;;iCAvCU,GAAG;aA+C5D,CAAC"}
1
+ {"version":3,"file":"overrides.d.ts","sourceRoot":"","sources":["../../../utils/overrides.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAWrD,oEAAoE;AACpE,eAAO,MAAM,4BAA4B,GAAI,KAAK,GAAG,oCASpD,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,qBAAqB,EAC7B,MAAM,GAAG,KACR,qBA6CF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAC7B,MAAM,GAAG,EACT,MAAM,GAAG,EACT,sBAAsB,MAAM,QA4B7B,CAAC;AAqBF,eAAO,MAAM,kBAAkB,GAAI,MAAM,GAAG,MAAM,QAAQ,GAAG,WA4B5D,CAAC;AAWF,eAAO,MAAM,sBAAsB,GAAI,MAAM,GAAG;;;iCAvCU,GAAG;aA+C5D,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { ssfFormat } from "@rowsncolumns/utils";
2
+ import { valueAxisLabelFormatter } from "./axis-number-format";
2
3
  // Map ChartSpec.legend / dataLabels / axisOptions to ECharts option
3
4
  // fragments. Each helper is a no-op when the spec field is missing, so
4
5
  // callers can spread the result without checks. Shared by the main
@@ -69,10 +70,21 @@ export const applyLegendOverrides = (legend, opts) => {
69
70
  }
70
71
  return next;
71
72
  };
72
- export const applyAxisOverrides = (axis, opts) => {
73
- if (!opts)
73
+ /**
74
+ * `numberFormatPattern` is the axis' RESOLVED format (explicit pattern or
75
+ * the linked source-cell format — see `resolveValueAxisNumberFormat`); it
76
+ * becomes the tick-label formatter.
77
+ */
78
+ export const applyAxisOverrides = (axis, opts, numberFormatPattern) => {
79
+ const formatter = valueAxisLabelFormatter(numberFormatPattern);
80
+ if (!opts && !formatter)
74
81
  return axis;
75
82
  const next = { ...axis };
83
+ if (formatter) {
84
+ next.axisLabel = { ...(next.axisLabel ?? {}), formatter };
85
+ }
86
+ if (!opts)
87
+ return next;
76
88
  if (opts.logarithmic)
77
89
  next.type = "log";
78
90
  if (opts.reverse)
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Exploded pie / doughnut (rowsncolumns/spreadsheet#668 C-32). Excel stores
3
+ * the explosion as a percentage of the pie radius (`<c:explosion val="25"/>`,
4
+ * 25 being the gallery default); each slice moves outward along its own
5
+ * bisector by that much. Both renderers resolve the spec value here.
6
+ */
7
+ /** Excel's gallery "Exploded Pie" / "Exploded Doughnut" explosion. */
8
+ export declare const DEFAULT_PIE_EXPLOSION = 25;
9
+ /**
10
+ * The explosion percentage a spec asks for, `0` when the slices touch
11
+ * (absent, non-numeric, negative or `0`).
12
+ */
13
+ export declare const resolveExplosion: (spec: {
14
+ explosion?: number | null;
15
+ } | null | undefined) => number;
16
+ /**
17
+ * The outer radius of a pie in pixels for the container it renders in:
18
+ * echarts' `radius` is a percentage of half the shorter side (default
19
+ * `"75%"`) or an absolute pixel count.
20
+ */
21
+ export declare const pieRadiusPx: (radius: (string | number)[] | null | undefined, width: number, height: number) => number;
22
+ /**
23
+ * echarts has no slice offset of its own; the exploded look is its
24
+ * `select` state (`selectedOffset`, in pixels) with every slice selected.
25
+ * The percentage is of the pie radius, so the offset scales with it.
26
+ */
27
+ export declare const explosionOffsetPx: (explosion: number, radiusPx: number) => number;
28
+ /** plotly pie `pull` — a fraction of the radius. */
29
+ export declare const explosionToPlotlyPull: (explosion: number) => number | undefined;
30
+ //# sourceMappingURL=pie-explosion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pie-explosion.d.ts","sourceRoot":"","sources":["../../../utils/pie-explosion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,sEAAsE;AACtE,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAKxC;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAC3B,MAAM;IAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,KACrD,MAIF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,GACtB,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,EAC9C,OAAO,MAAM,EACb,QAAQ,MAAM,KACb,MAOF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,WAAW,MAAM,EAAE,UAAU,MAAM,WAC3B,CAAC;AAE3C,oDAAoD;AACpD,eAAO,MAAM,qBAAqB,GAAI,WAAW,MAAM,uBACG,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Exploded pie / doughnut (rowsncolumns/spreadsheet#668 C-32). Excel stores
3
+ * the explosion as a percentage of the pie radius (`<c:explosion val="25"/>`,
4
+ * 25 being the gallery default); each slice moves outward along its own
5
+ * bisector by that much. Both renderers resolve the spec value here.
6
+ */
7
+ /** Excel's gallery "Exploded Pie" / "Exploded Doughnut" explosion. */
8
+ export const DEFAULT_PIE_EXPLOSION = 25;
9
+ /** OOXML `CT_UnsignedInt` explosion — Excel's dialog caps it at 400%. */
10
+ const MAX_PIE_EXPLOSION = 400;
11
+ /**
12
+ * The explosion percentage a spec asks for, `0` when the slices touch
13
+ * (absent, non-numeric, negative or `0`).
14
+ */
15
+ export const resolveExplosion = (spec) => {
16
+ const explosion = spec?.explosion;
17
+ if (typeof explosion !== "number" || !Number.isFinite(explosion))
18
+ return 0;
19
+ return Math.min(MAX_PIE_EXPLOSION, Math.max(0, explosion));
20
+ };
21
+ /**
22
+ * The outer radius of a pie in pixels for the container it renders in:
23
+ * echarts' `radius` is a percentage of half the shorter side (default
24
+ * `"75%"`) or an absolute pixel count.
25
+ */
26
+ export const pieRadiusPx = (radius, width, height) => {
27
+ const half = Math.max(0, Math.min(width, height)) / 2;
28
+ const outer = radius?.[radius.length - 1] ?? "75%";
29
+ if (typeof outer === "number")
30
+ return Math.max(0, outer);
31
+ const parsed = Number.parseFloat(outer);
32
+ if (!Number.isFinite(parsed))
33
+ return half * 0.75;
34
+ return outer.trim().endsWith("%") ? (half * parsed) / 100 : parsed;
35
+ };
36
+ /**
37
+ * echarts has no slice offset of its own; the exploded look is its
38
+ * `select` state (`selectedOffset`, in pixels) with every slice selected.
39
+ * The percentage is of the pie radius, so the offset scales with it.
40
+ */
41
+ export const explosionOffsetPx = (explosion, radiusPx) => Math.round((radiusPx * explosion) / 100);
42
+ /** plotly pie `pull` — a fraction of the radius. */
43
+ export const explosionToPlotlyPull = (explosion) => explosion > 0 ? Math.min(1, explosion / 100) : undefined;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * plotly click → chart selection (rowsncolumns/spreadsheet#668 C-37 / C-38).
3
+ *
4
+ * The plotly renderer is interactive with `layout.dragmode: false`: plotly
5
+ * reports `plotly_click` but never starts a drag, so a body drag reaches
6
+ * the embed's move handler instead. plotly does not track that drag either
7
+ * and treats the mouseup as a click, so the caller compares the click's
8
+ * pointer position with where the pointer went down.
9
+ */
10
+ /** Pointer travel (px) beyond which a mouseup is a drag, not a click. */
11
+ export declare const CLICK_DRAG_TOLERANCE_PX = 3;
12
+ export declare const wasDragged: (pointerDown: {
13
+ x: number;
14
+ y: number;
15
+ } | undefined, clickEvent: {
16
+ clientX?: number;
17
+ clientY?: number;
18
+ } | undefined) => boolean;
19
+ export type PlotlyClickPoint = {
20
+ curveNumber: number;
21
+ /** Index of the clicked point within its trace (a category index). */
22
+ pointNumber?: number;
23
+ data?: unknown;
24
+ };
25
+ /**
26
+ * The `spec.series` entry (and data point) a plotly click lands on. Traces
27
+ * tagged with `meta.rncSeriesIndex` resolve through the tag; untagged ones
28
+ * map by curve position while it fits; anything else is not a series.
29
+ */
30
+ export declare const resolvePlotlyClickSelection: (points: ReadonlyArray<PlotlyClickPoint> | undefined, seriesCount: number) => {
31
+ seriesIndex: number;
32
+ dataIndex?: number;
33
+ } | undefined;
34
+ //# sourceMappingURL=plotly-click.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plotly-click.d.ts","sourceRoot":"","sources":["../../../utils/plotly-click.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,yEAAyE;AACzE,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,eAAO,MAAM,UAAU,GACrB,aAAa;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,EACjD,YAAY;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,KAC7D,OAQF,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,GACtC,QAAQ,aAAa,CAAC,gBAAgB,CAAC,GAAG,SAAS,EACnD,aAAa,MAAM,KAClB;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAkBhD,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * plotly click → chart selection (rowsncolumns/spreadsheet#668 C-37 / C-38).
3
+ *
4
+ * The plotly renderer is interactive with `layout.dragmode: false`: plotly
5
+ * reports `plotly_click` but never starts a drag, so a body drag reaches
6
+ * the embed's move handler instead. plotly does not track that drag either
7
+ * and treats the mouseup as a click, so the caller compares the click's
8
+ * pointer position with where the pointer went down.
9
+ */
10
+ /** Pointer travel (px) beyond which a mouseup is a drag, not a click. */
11
+ export const CLICK_DRAG_TOLERANCE_PX = 3;
12
+ export const wasDragged = (pointerDown, clickEvent) => {
13
+ if (!pointerDown || !clickEvent)
14
+ return false;
15
+ const { clientX, clientY } = clickEvent;
16
+ if (typeof clientX !== "number" || typeof clientY !== "number")
17
+ return false;
18
+ return (Math.abs(clientX - pointerDown.x) > CLICK_DRAG_TOLERANCE_PX ||
19
+ Math.abs(clientY - pointerDown.y) > CLICK_DRAG_TOLERANCE_PX);
20
+ };
21
+ /**
22
+ * The `spec.series` entry (and data point) a plotly click lands on. Traces
23
+ * tagged with `meta.rncSeriesIndex` resolve through the tag; untagged ones
24
+ * map by curve position while it fits; anything else is not a series.
25
+ */
26
+ export const resolvePlotlyClickSelection = (points, seriesCount) => {
27
+ const point = points?.[0];
28
+ if (!point)
29
+ return undefined;
30
+ const meta = point.data
31
+ ?.meta;
32
+ const seriesIndex = typeof meta?.rncSeriesIndex === "number"
33
+ ? meta.rncSeriesIndex
34
+ : point.curveNumber < seriesCount
35
+ ? point.curveNumber
36
+ : undefined;
37
+ if (seriesIndex === undefined || seriesIndex >= seriesCount) {
38
+ return undefined;
39
+ }
40
+ const dataIndex = point.pointNumber;
41
+ return typeof dataIndex === "number" && dataIndex >= 0
42
+ ? { seriesIndex, dataIndex }
43
+ : { seriesIndex };
44
+ };