@svgrid/grid 2.1.22 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/README.md +9 -2
  2. package/dist/DockNodeView.svelte +306 -0
  3. package/dist/DockNodeView.svelte.d.ts +7 -0
  4. package/dist/DockPopoutHost.svelte +17 -0
  5. package/dist/DockPopoutHost.svelte.d.ts +14 -0
  6. package/dist/FlexRender.svelte +96 -96
  7. package/dist/GridFooter.svelte +178 -178
  8. package/dist/GridMenus.svelte +83 -3
  9. package/dist/SvAutoComplete.svelte +5 -3
  10. package/dist/SvCarousel.svelte +141 -163
  11. package/dist/SvColorInput.svelte +187 -186
  12. package/dist/SvColorInput.svelte.d.ts +1 -1
  13. package/dist/SvComboBox.svelte +7 -3
  14. package/dist/SvCommand.svelte +28 -85
  15. package/dist/SvContextMenu.svelte +116 -116
  16. package/dist/SvCountryInput.svelte +1 -1
  17. package/dist/SvDateRangeInput.svelte +1 -1
  18. package/dist/SvDateTimePicker.svelte +9 -3
  19. package/dist/SvDateTimePicker.svelte.d.ts +3 -0
  20. package/dist/SvDockLayout.svelte +244 -0
  21. package/dist/SvDockLayout.svelte.d.ts +77 -0
  22. package/dist/SvDockManager.svelte +922 -0
  23. package/dist/SvDockManager.svelte.d.ts +106 -0
  24. package/dist/SvDrawer.svelte +250 -228
  25. package/dist/SvDrawer.svelte.d.ts +4 -0
  26. package/dist/SvDropDownList.svelte +85 -13
  27. package/dist/SvDropDownList.svelte.d.ts +3 -0
  28. package/dist/SvField.svelte +21 -4
  29. package/dist/SvField.svelte.d.ts +1 -0
  30. package/dist/SvForm.svelte +201 -91
  31. package/dist/SvForm.svelte.d.ts +15 -22
  32. package/dist/SvGrid.controller.svelte.d.ts +32 -1
  33. package/dist/SvGrid.controller.svelte.js +156 -27
  34. package/dist/SvGrid.css +193 -5
  35. package/dist/SvGrid.svelte +3131 -2786
  36. package/dist/SvGrid.types.d.ts +452 -1
  37. package/dist/SvGridBoard.svelte +2339 -2339
  38. package/dist/SvGridChart.svelte +1724 -1724
  39. package/dist/SvGridChartPanel.svelte +485 -485
  40. package/dist/SvGridDropdown.svelte +689 -682
  41. package/dist/SvGridSelect.svelte +1 -0
  42. package/dist/SvGroupCell.svelte +109 -109
  43. package/dist/SvListBox.svelte +36 -14
  44. package/dist/SvMaskedInput.svelte +4 -3
  45. package/dist/SvMaskedInput.svelte.d.ts +1 -1
  46. package/dist/SvMenu.svelte +124 -124
  47. package/dist/SvMenuList.svelte +117 -165
  48. package/dist/SvMenuList.svelte.d.ts +2 -13
  49. package/dist/SvModal.svelte +12 -29
  50. package/dist/SvMultiSelect.svelte +1 -0
  51. package/dist/SvNumberInput.svelte +131 -130
  52. package/dist/SvNumberInput.svelte.d.ts +1 -1
  53. package/dist/SvOtpInput.svelte +158 -158
  54. package/dist/SvPagination.svelte +18 -18
  55. package/dist/SvPasswordInput.svelte +3 -2
  56. package/dist/SvPasswordInput.svelte.d.ts +1 -1
  57. package/dist/SvPhoneInput.svelte +3 -2
  58. package/dist/SvPhoneInput.svelte.d.ts +1 -1
  59. package/dist/SvPopover.svelte +139 -139
  60. package/dist/SvRowGroupPanel.svelte +170 -170
  61. package/dist/SvScrollArea.svelte +61 -61
  62. package/dist/SvStepper.svelte +6 -9
  63. package/dist/SvTagsInput.svelte +3 -2
  64. package/dist/SvTagsInput.svelte.d.ts +1 -1
  65. package/dist/SvTooltip.svelte +127 -138
  66. package/dist/SvTour.svelte +204 -176
  67. package/dist/SvTree.svelte +377 -377
  68. package/dist/SvTreeSelect.svelte +230 -229
  69. package/dist/build-api.js +76 -54
  70. package/dist/cdn/svgrid.js +10913 -6450
  71. package/dist/cdn/svgrid.svelte-external.js +9628 -5481
  72. package/dist/chart-export.js +8 -8
  73. package/dist/columns.js +6 -0
  74. package/dist/conditional-formatting.d.ts +67 -3
  75. package/dist/conditional-formatting.js +124 -17
  76. package/dist/createCarousel.svelte.d.ts +72 -0
  77. package/dist/createCarousel.svelte.js +80 -0
  78. package/dist/createCommand.svelte.d.ts +62 -0
  79. package/dist/createCommand.svelte.js +109 -0
  80. package/dist/createForm.svelte.d.ts +62 -0
  81. package/dist/createForm.svelte.js +374 -0
  82. package/dist/createMenu.svelte.d.ts +88 -0
  83. package/dist/createMenu.svelte.js +153 -0
  84. package/dist/createOverlay.svelte.d.ts +38 -0
  85. package/dist/createOverlay.svelte.js +68 -0
  86. package/dist/createPagination.d.ts +74 -0
  87. package/dist/createPagination.js +71 -0
  88. package/dist/createPopoverSelect.svelte.js +6 -12
  89. package/dist/createStepper.d.ts +44 -0
  90. package/dist/createStepper.js +26 -0
  91. package/dist/createTooltip.svelte.d.ts +32 -0
  92. package/dist/createTooltip.svelte.js +61 -0
  93. package/dist/datetime/timezone.d.ts +36 -0
  94. package/dist/datetime/timezone.js +110 -0
  95. package/dist/dock-context.d.ts +52 -0
  96. package/dist/dock-context.js +1 -0
  97. package/dist/dock-manager-model.d.ts +136 -0
  98. package/dist/dock-manager-model.js +514 -0
  99. package/dist/dock-model.d.ts +105 -0
  100. package/dist/dock-model.js +288 -0
  101. package/dist/dock-popout.d.ts +30 -0
  102. package/dist/dock-popout.js +81 -0
  103. package/dist/editor-contract.d.ts +2 -0
  104. package/dist/filter-operators.d.ts +6 -0
  105. package/dist/filter-operators.js +35 -2
  106. package/dist/filtering/excel-filters.d.ts +18 -1
  107. package/dist/filtering/excel-filters.js +56 -0
  108. package/dist/form-field.d.ts +73 -0
  109. package/dist/form-field.js +15 -0
  110. package/dist/index.d.ts +25 -5
  111. package/dist/index.js +40 -4
  112. package/dist/list-nav.d.ts +18 -0
  113. package/dist/list-nav.js +30 -0
  114. package/dist/list-option.d.ts +2 -0
  115. package/dist/menu-item.d.ts +20 -0
  116. package/dist/menu-item.js +1 -0
  117. package/dist/menus.d.ts +5 -0
  118. package/dist/menus.js +68 -16
  119. package/dist/panel-resize.d.ts +25 -0
  120. package/dist/panel-resize.js +40 -0
  121. package/dist/popover.d.ts +29 -3
  122. package/dist/popover.js +22 -6
  123. package/dist/recurrence.d.ts +33 -7
  124. package/dist/recurrence.js +151 -13
  125. package/dist/row-drag.d.ts +8 -0
  126. package/dist/row-drag.js +11 -0
  127. package/dist/scheduler-ical.d.ts +30 -0
  128. package/dist/scheduler-ical.js +180 -0
  129. package/dist/scheduler-model.d.ts +269 -0
  130. package/dist/scheduler-model.js +602 -0
  131. package/dist/scheduler-view.svelte.d.ts +22 -0
  132. package/dist/scheduler-view.svelte.js +13 -0
  133. package/dist/spreadsheet.js +20 -12
  134. package/dist/ui-app.types.d.ts +3 -0
  135. package/package.json +14 -10
  136. package/src/DockNodeView.svelte +306 -0
  137. package/src/DockPopoutHost.svelte +17 -0
  138. package/src/FlexRender.svelte +96 -96
  139. package/src/GridFooter.svelte +178 -178
  140. package/src/GridMenus.svelte +83 -3
  141. package/src/SvAutoComplete.svelte +5 -3
  142. package/src/SvCarousel.svelte +141 -163
  143. package/src/SvCarousel.test.ts +68 -0
  144. package/src/SvColorInput.svelte +187 -186
  145. package/src/SvComboBox.svelte +7 -3
  146. package/src/SvCommand.svelte +28 -85
  147. package/src/SvCommand.test.ts +78 -0
  148. package/src/SvContextMenu.svelte +116 -116
  149. package/src/SvCountryInput.svelte +1 -1
  150. package/src/SvDateRangeInput.svelte +1 -1
  151. package/src/SvDateTimePicker.svelte +9 -3
  152. package/src/SvDockLayout.svelte +244 -0
  153. package/src/SvDockManager.svelte +922 -0
  154. package/src/SvDrawer.svelte +250 -228
  155. package/src/SvDropDownList.svelte +85 -13
  156. package/src/SvField.svelte +21 -4
  157. package/src/SvForm.svelte +201 -91
  158. package/src/SvForm.test.ts +299 -0
  159. package/src/SvGrid.controller.svelte.ts +3158 -3034
  160. package/src/SvGrid.css +193 -5
  161. package/src/SvGrid.svelte +3131 -2786
  162. package/src/SvGrid.types.ts +1489 -1027
  163. package/src/SvGridBoard.svelte +2339 -2339
  164. package/src/SvGridChart.svelte +1724 -1724
  165. package/src/SvGridChartPanel.svelte +485 -485
  166. package/src/SvGridDropdown.svelte +689 -682
  167. package/src/SvGridSelect.svelte +1 -0
  168. package/src/SvGroupCell.svelte +109 -109
  169. package/src/SvListBox.svelte +36 -14
  170. package/src/SvMaskedInput.svelte +4 -3
  171. package/src/SvMenu.svelte +124 -124
  172. package/src/SvMenu.test.ts +97 -73
  173. package/src/SvMenuList.svelte +117 -165
  174. package/src/SvModal.svelte +12 -29
  175. package/src/SvModal.test.ts +82 -0
  176. package/src/SvMultiSelect.svelte +1 -0
  177. package/src/SvNumberInput.svelte +131 -130
  178. package/src/SvOtpInput.svelte +158 -158
  179. package/src/SvPagination.svelte +18 -18
  180. package/src/SvPasswordInput.svelte +3 -2
  181. package/src/SvPhoneInput.svelte +3 -2
  182. package/src/SvPopover.svelte +139 -139
  183. package/src/SvRowGroupPanel.svelte +170 -170
  184. package/src/SvScrollArea.svelte +61 -61
  185. package/src/SvStepper.svelte +6 -9
  186. package/src/SvTagsInput.svelte +3 -2
  187. package/src/SvTooltip.svelte +127 -138
  188. package/src/SvTooltip.test.ts +73 -0
  189. package/src/SvTour.svelte +204 -176
  190. package/src/SvTree.svelte +377 -377
  191. package/src/SvTreeSelect.svelte +230 -229
  192. package/src/a11y.contract.test.ts +49 -49
  193. package/src/a11y.test.ts +59 -59
  194. package/src/a11y.ts +61 -61
  195. package/src/build-api.ts +870 -847
  196. package/src/cell-formatting.ts +169 -169
  197. package/src/cell-render.ts +469 -469
  198. package/src/chart-export.ts +201 -201
  199. package/src/chart.ts +2296 -2296
  200. package/src/collaboration.test.ts +104 -104
  201. package/src/collaboration.ts +167 -167
  202. package/src/columns.ts +5 -0
  203. package/src/conditional-formatting.test.ts +104 -0
  204. package/src/conditional-formatting.ts +176 -19
  205. package/src/core.performance.test.ts +30 -30
  206. package/src/core.ts +1111 -1111
  207. package/src/createAutocomplete.svelte.ts +132 -132
  208. package/src/createCarousel.svelte.ts +104 -0
  209. package/src/createCombobox.svelte.ts +179 -179
  210. package/src/createCommand.svelte.ts +125 -0
  211. package/src/createCountryInput.svelte.ts +157 -157
  212. package/src/createDropdownList.svelte.ts +162 -162
  213. package/src/createForm.svelte.ts +333 -0
  214. package/src/createGrid.svelte.ts +42 -42
  215. package/src/createGrid.test.ts +10 -10
  216. package/src/createGridState.svelte.ts +17 -17
  217. package/src/createMenu.svelte.ts +202 -0
  218. package/src/createOverlay.svelte.ts +101 -0
  219. package/src/createPagination.test.ts +65 -0
  220. package/src/createPagination.ts +84 -0
  221. package/src/createPopoverSelect.svelte.ts +206 -210
  222. package/src/createStepper.test.ts +62 -0
  223. package/src/createStepper.ts +61 -0
  224. package/src/createTooltip.svelte.ts +86 -0
  225. package/src/createTree.svelte.ts +319 -319
  226. package/src/datetime/timezone.test.ts +69 -0
  227. package/src/datetime/timezone.ts +134 -0
  228. package/src/dock-context.ts +47 -0
  229. package/src/dock-manager-model.test.ts +275 -0
  230. package/src/dock-manager-model.ts +593 -0
  231. package/src/dock-model.test.ts +163 -0
  232. package/src/dock-model.ts +368 -0
  233. package/src/dock-popout.ts +94 -0
  234. package/src/editing.test.ts +859 -859
  235. package/src/editing.ts +675 -675
  236. package/src/editor-contract.ts +2 -0
  237. package/src/export-data-api.test.ts +126 -126
  238. package/src/export-format.test.ts +107 -107
  239. package/src/export-format.ts +598 -598
  240. package/src/filter-operators.test.ts +59 -0
  241. package/src/filter-operators.ts +35 -2
  242. package/src/filtering/excel-filters.test.ts +86 -1
  243. package/src/filtering/excel-filters.ts +65 -0
  244. package/src/flex-render.ts +3 -3
  245. package/src/form-field.ts +87 -0
  246. package/src/index.ts +623 -469
  247. package/src/keyboard.test.ts +59 -59
  248. package/src/keyboard.ts +97 -97
  249. package/src/list-nav.test.ts +49 -0
  250. package/src/list-nav.ts +29 -0
  251. package/src/list-option.ts +2 -0
  252. package/src/menu-item.ts +21 -0
  253. package/src/menus.test.ts +48 -0
  254. package/src/menus.ts +639 -582
  255. package/src/merge-objects.ts +48 -48
  256. package/src/overlays.test.ts +90 -90
  257. package/src/panel-resize.ts +52 -0
  258. package/src/popover.bounds.test.ts +93 -0
  259. package/src/popover.ts +51 -7
  260. package/src/recurrence.test.ts +88 -1
  261. package/src/recurrence.ts +166 -15
  262. package/src/render-component.ts +28 -28
  263. package/src/row-drag.ts +10 -0
  264. package/src/scheduler-ical.test.ts +68 -0
  265. package/src/scheduler-ical.ts +180 -0
  266. package/src/scheduler-model.test.ts +546 -0
  267. package/src/scheduler-model.ts +853 -0
  268. package/src/scheduler-view.svelte.ts +33 -0
  269. package/src/selection.test.ts +754 -754
  270. package/src/selection.ts +600 -600
  271. package/src/server-data-source.test.ts +289 -289
  272. package/src/server-data-source.ts +413 -413
  273. package/src/sparkline.test.ts +68 -68
  274. package/src/sparkline.ts +169 -169
  275. package/src/spreadsheet.test.ts +489 -489
  276. package/src/spreadsheet.ts +312 -304
  277. package/src/static-functions.ts +11 -11
  278. package/src/subscribe.ts +38 -38
  279. package/src/svgrid-wrapper.types.ts +501 -501
  280. package/src/svgrid.behavior.test.ts +706 -706
  281. package/src/svgrid.charting.test.ts +527 -527
  282. package/src/svgrid.effect-driven-filter.svelte.test.ts +116 -0
  283. package/src/svgrid.features.test.ts +157 -157
  284. package/src/svgrid.new-features.wrapper.test.ts +251 -251
  285. package/src/svgrid.wrapper.test.ts +40 -40
  286. package/src/test-setup.ts +62 -62
  287. package/src/themes/index.ts +159 -159
  288. package/src/ui-app.types.ts +3 -0
  289. package/src/virtual.test.ts +6 -4
  290. package/src/virtualization/column-virtualizer.test.ts +27 -27
  291. package/src/virtualization/column-virtualizer.ts +30 -30
  292. package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
  293. package/src/virtualization/types.ts +30 -30
  294. package/src/virtualization/virtualizer.test.ts +47 -47
  295. package/src/virtualization/virtualizer.ts +296 -296
@@ -10,15 +10,59 @@
10
10
  * component just paints the result and the logic is unit-testable.
11
11
  */
12
12
 
13
+ /** A single gradient stop along the normalized 0..1 domain. */
14
+ export type ColorScaleStop = { offset: number; color: string }
15
+
16
+ /**
17
+ * How `minValue`/`maxValue` (and the derived column extremes) are read:
18
+ * - `absolute` (default): literal data values.
19
+ * - `percent`: 0..100 positions along the column's own min..max span, so you
20
+ * can say "tint the top 20%" without knowing the numbers up front.
21
+ */
22
+ export type ScaleBounds = 'absolute' | 'percent'
23
+
13
24
  export type ColorScaleFormat = {
14
25
  type: 'colorScale'
15
26
  /** 2-stop (min/max) or 3-stop (min/mid/max) gradient. Hex colors. */
16
- min: string
27
+ min?: string
17
28
  mid?: string
18
- max: string
29
+ max?: string
30
+ /**
31
+ * N-stop gradient along the normalized domain (offsets 0..1). When present
32
+ * this overrides `min`/`mid`/`max`, enabling banded / traffic-light scales.
33
+ */
34
+ stops?: ReadonlyArray<ColorScaleStop>
35
+ /**
36
+ * `hue` (default): interpolate between stop colors into an opaque fill.
37
+ * `alpha`: keep a single `base` color and interpolate its *opacity*, so the
38
+ * tint composites over zebra striping, selection, and pinned backgrounds
39
+ * instead of painting over them - Adaptable's "live heat map" look.
40
+ */
41
+ mode?: 'hue' | 'alpha'
42
+ /** Base color for `alpha` mode. Default `#2563eb`. */
43
+ base?: string
44
+ /** [min, max] opacity for `alpha` mode. Default [0.05, 0.85]. */
45
+ alphaBounds?: readonly [number, number]
19
46
  /** Fix the scale; otherwise derived from the column's data. */
20
47
  minValue?: number
21
48
  maxValue?: number
49
+ /** Interpret `minValue`/`maxValue` as absolute values or 0..100 percents. */
50
+ bounds?: ScaleBounds
51
+ /**
52
+ * Diverging scale pinned at 0: negatives and positives shade outward from a
53
+ * neutral midpoint, symmetric around zero. Ideal for P&L / price deltas.
54
+ */
55
+ zeroCentred?: boolean
56
+ /** Flip the ramp so the lowest values attract the most attention. */
57
+ reverse?: boolean
58
+ /**
59
+ * Tint by this cell's value as a proportion of another column's value on the
60
+ * SAME row (a field key on the row object), instead of the column extremes.
61
+ * Column comparison - e.g. filled vs target, open vs total.
62
+ */
63
+ compareColumn?: string
64
+ /** Attach a tooltip showing the raw value or its % position on the ramp. */
65
+ tooltip?: 'value' | 'percent'
22
66
  }
23
67
 
24
68
  export type DataBarFormat = {
@@ -27,6 +71,15 @@ export type DataBarFormat = {
27
71
  negativeColor?: string
28
72
  minValue?: number
29
73
  maxValue?: number
74
+ /** Interpret `minValue`/`maxValue` as absolute values or 0..100 percents. */
75
+ bounds?: ScaleBounds
76
+ /**
77
+ * Size the bar by this cell's value as a proportion of another column's
78
+ * value on the same row (a field key), instead of the column extremes.
79
+ */
80
+ compareColumn?: string
81
+ /** Fill the bar with a left-to-right gradient rather than a flat color. */
82
+ gradient?: boolean
30
83
  /** Show the cell's text on top of the bar. Default true. */
31
84
  showValue?: boolean
32
85
  }
@@ -66,9 +119,11 @@ export type ResolvedCellFormat = {
66
119
  background?: string
67
120
  color?: string
68
121
  fontWeight?: string | number
69
- dataBar?: { percent: number; color: string; fromRight: boolean }
122
+ dataBar?: { percent: number; color: string; fromRight: boolean; gradient?: boolean }
70
123
  icon?: string
71
124
  iconOnly?: boolean
125
+ /** Tooltip text (value / percentile), when a format requests one. */
126
+ title?: string
72
127
  }
73
128
 
74
129
  export type ColumnStat = { min: number; max: number }
@@ -158,13 +213,85 @@ export function lerpColor(a: string, b: string, t: number): string {
158
213
  ])
159
214
  }
160
215
 
216
+ /** Build an `rgba()` string from a hex color + opacity. Falls back to the hex. */
217
+ export function rgba(hex: string, alpha: number): string {
218
+ const rgb = parseHex(hex)
219
+ if (!rgb) return hex
220
+ return `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${clamp01(alpha).toFixed(3)})`
221
+ }
222
+
223
+ /** Color of an N-stop ramp at normalized position `t`. Stops sorted by offset. */
224
+ function stopsColorAt(stops: ReadonlyArray<ColorScaleStop>, t: number): string {
225
+ const sorted = [...stops].sort((a, b) => a.offset - b.offset)
226
+ if (sorted.length === 0) return '#000000'
227
+ if (t <= sorted[0]!.offset) return sorted[0]!.color
228
+ const last = sorted[sorted.length - 1]!
229
+ if (t >= last.offset) return last.color
230
+ for (let i = 0; i < sorted.length - 1; i += 1) {
231
+ const a = sorted[i]!
232
+ const b = sorted[i + 1]!
233
+ if (t >= a.offset && t <= b.offset) {
234
+ const span = b.offset - a.offset || 1
235
+ return lerpColor(a.color, b.color, (t - a.offset) / span)
236
+ }
237
+ }
238
+ return last.color
239
+ }
240
+
241
+ /** The interpolated color for a color-scale at normalized position `t`. */
161
242
  function colorScaleAt(fmt: ColorScaleFormat, t: number): string {
243
+ if (fmt.stops && fmt.stops.length >= 2) return stopsColorAt(fmt.stops, t)
244
+ const min = fmt.min ?? '#ffffff'
245
+ const max = fmt.max ?? '#000000'
162
246
  if (fmt.mid) {
163
247
  return t <= 0.5
164
- ? lerpColor(fmt.min, fmt.mid, t / 0.5)
165
- : lerpColor(fmt.mid, fmt.max, (t - 0.5) / 0.5)
248
+ ? lerpColor(min, fmt.mid, t / 0.5)
249
+ : lerpColor(fmt.mid, max, (t - 0.5) / 0.5)
166
250
  }
167
- return lerpColor(fmt.min, fmt.max, t)
251
+ return lerpColor(min, max, t)
252
+ }
253
+
254
+ /**
255
+ * Normalized position (0..1) of `n` within a format's domain, honoring
256
+ * absolute/percent bounds, zero-centred diverging scales, cross-column
257
+ * comparison, and reverse. Returns null when the domain can't be resolved
258
+ * (e.g. compareColumn missing / zero on the row).
259
+ */
260
+ function domainT(
261
+ fmt: ColorScaleFormat | DataBarFormat,
262
+ n: number,
263
+ row: unknown,
264
+ stat: ColumnStat | null,
265
+ ): number | null {
266
+ let t: number
267
+
268
+ if (fmt.compareColumn) {
269
+ const other = Number((row as Record<string, unknown> | null)?.[fmt.compareColumn])
270
+ if (!Number.isFinite(other) || other === 0) return null
271
+ t = clamp01(n / other)
272
+ } else if (fmt.type === 'colorScale' && fmt.zeroCentred) {
273
+ const dMin = fmt.minValue ?? stat?.min ?? n
274
+ const dMax = fmt.maxValue ?? stat?.max ?? n
275
+ const bound = Math.max(Math.abs(dMin), Math.abs(dMax)) || 1
276
+ t = clamp01(0.5 + n / bound / 2)
277
+ } else {
278
+ const dMin = stat?.min ?? n
279
+ const dMax = stat?.max ?? n
280
+ let lo: number
281
+ let hi: number
282
+ if (fmt.bounds === 'percent') {
283
+ const span = dMax - dMin
284
+ lo = dMin + ((fmt.minValue ?? 0) / 100) * span
285
+ hi = dMin + ((fmt.maxValue ?? 100) / 100) * span
286
+ } else {
287
+ lo = fmt.minValue ?? dMin
288
+ hi = fmt.maxValue ?? dMax
289
+ }
290
+ t = hi === lo ? 0.5 : clamp01((n - lo) / (hi - lo))
291
+ }
292
+
293
+ if (fmt.type === 'colorScale' && fmt.reverse) t = 1 - t
294
+ return t
168
295
  }
169
296
 
170
297
  /**
@@ -203,23 +330,42 @@ export function resolveCellFormat<TData = unknown>(
203
330
  if (value == null || value === '' || !Number.isFinite(n)) continue
204
331
 
205
332
  if (fmt.type === 'colorScale') {
206
- const lo = fmt.minValue ?? stat?.min ?? n
207
- const hi = fmt.maxValue ?? stat?.max ?? n
208
- const t = hi === lo ? 0.5 : clamp01((n - lo) / (hi - lo))
209
- out.background = colorScaleAt(fmt, t)
210
- // The scale fills the whole cell, so keep the text legible against it.
211
- const c = contrastText(out.background)
212
- if (c) out.color = c
333
+ const t = domainT(fmt, n, row, stat)
334
+ if (t == null) continue
335
+ if (fmt.mode === 'alpha') {
336
+ const [alo, ahi] = fmt.alphaBounds ?? [0.05, 0.85]
337
+ out.background = rgba(fmt.base ?? '#2563eb', alo + (ahi - alo) * t)
338
+ // Translucent tint composites over the row bg; leave text color alone.
339
+ } else {
340
+ out.background = colorScaleAt(fmt, t)
341
+ // The scale fills the whole cell, so keep the text legible against it.
342
+ const c = contrastText(out.background)
343
+ if (c) out.color = c
344
+ }
345
+ if (fmt.tooltip === 'value') out.title = String(value)
346
+ else if (fmt.tooltip === 'percent') out.title = `${Math.round(t * 100)}%`
213
347
  } else if (fmt.type === 'dataBar') {
214
- const lo = fmt.minValue ?? Math.min(0, stat?.min ?? 0)
215
- const hi = fmt.maxValue ?? stat?.max ?? n
216
- const span = hi - lo || 1
217
- const percent = clamp01((n - lo) / span) * 100
348
+ let percent: number
349
+ if (fmt.compareColumn) {
350
+ const t = domainT(fmt, n, row, stat)
351
+ if (t == null) continue
352
+ percent = t * 100
353
+ } else if (fmt.bounds === 'percent') {
354
+ const t = domainT(fmt, n, row, stat)
355
+ if (t == null) continue
356
+ percent = t * 100
357
+ } else {
358
+ const lo = fmt.minValue ?? Math.min(0, stat?.min ?? 0)
359
+ const hi = fmt.maxValue ?? stat?.max ?? n
360
+ const span = hi - lo || 1
361
+ percent = clamp01((n - lo) / span) * 100
362
+ }
218
363
  const negative = n < 0
219
364
  out.dataBar = {
220
365
  percent,
221
366
  color: negative ? (fmt.negativeColor ?? '#ef4444') : fmt.color,
222
367
  fromRight: false,
368
+ gradient: fmt.gradient,
223
369
  }
224
370
  if (fmt.showValue === false) out.color = 'transparent'
225
371
  } else if (fmt.type === 'iconSet') {
@@ -231,9 +377,20 @@ export function resolveCellFormat<TData = unknown>(
231
377
  return out
232
378
  }
233
379
 
234
- /** Column ids that need a numeric min/max precomputed (colorScale/dataBar). */
380
+ /**
381
+ * Whether any format needs a numeric min/max precomputed. Color-scale and
382
+ * data-bar formats do, EXCEPT when they derive their range from another column
383
+ * on the same row (`compareColumn`) - those read the row, not column stats.
384
+ */
385
+ export function formatNeedsStats(f: ConditionalFormat<any>): boolean {
386
+ return (
387
+ (f.type === 'colorScale' || f.type === 'dataBar') &&
388
+ !('compareColumn' in f && f.compareColumn)
389
+ )
390
+ }
391
+
235
392
  export function formatsNeedingStats(
236
393
  formats: ReadonlyArray<ConditionalFormat<any>>,
237
394
  ): boolean {
238
- return formats.some((f) => f.type === 'colorScale' || f.type === 'dataBar')
395
+ return formats.some(formatNeedsStats)
239
396
  }
@@ -1,30 +1,30 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { createCoreRowModel, createSortedRowModel, createSvGrid, sortFns, tableFeatures } from './index'
3
-
4
- describe('core performance smoke', () => {
5
- it('handles large synthetic shape with bounded row model call', () => {
6
- const columns = Array.from({ length: 50 }, (_, index) => ({
7
- field: `c${index}`,
8
- editorType: 'number' as const,
9
- }))
10
- const data = Array.from({ length: 5000 }, (_, rowIndex) => {
11
- const row: Record<string, number> = {}
12
- for (let col = 0; col < 50; col += 1) row[`c${col}`] = rowIndex + col
13
- return row
14
- })
15
- const grid = createSvGrid({
16
- _features: tableFeatures({}),
17
- _rowModels: {
18
- coreRowModel: createCoreRowModel(),
19
- sortedRowModel: createSortedRowModel(sortFns),
20
- },
21
- columns,
22
- data,
23
- state: { sorting: [{ id: 'c0', desc: true }, { id: 'c1', desc: false }] },
24
- })
25
-
26
- const rows = grid.getRowModel().rows
27
- expect(rows.length).toBe(5000)
28
- expect(rows[0]?.getCellValueByColumnId('c0')).toBe(4999)
29
- })
30
- })
1
+ import { describe, expect, it } from 'vitest'
2
+ import { createCoreRowModel, createSortedRowModel, createSvGrid, sortFns, tableFeatures } from './index'
3
+
4
+ describe('core performance smoke', () => {
5
+ it('handles large synthetic shape with bounded row model call', () => {
6
+ const columns = Array.from({ length: 50 }, (_, index) => ({
7
+ field: `c${index}`,
8
+ editorType: 'number' as const,
9
+ }))
10
+ const data = Array.from({ length: 5000 }, (_, rowIndex) => {
11
+ const row: Record<string, number> = {}
12
+ for (let col = 0; col < 50; col += 1) row[`c${col}`] = rowIndex + col
13
+ return row
14
+ })
15
+ const grid = createSvGrid({
16
+ _features: tableFeatures({}),
17
+ _rowModels: {
18
+ coreRowModel: createCoreRowModel(),
19
+ sortedRowModel: createSortedRowModel(sortFns),
20
+ },
21
+ columns,
22
+ data,
23
+ state: { sorting: [{ id: 'c0', desc: true }, { id: 'c1', desc: false }] },
24
+ })
25
+
26
+ const rows = grid.getRowModel().rows
27
+ expect(rows.length).toBe(5000)
28
+ expect(rows[0]?.getCellValueByColumnId('c0')).toBe(4999)
29
+ })
30
+ })