@solidports/recharts 0.1.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 (306) hide show
  1. package/LICENSE +22 -0
  2. package/NOTICE +19 -0
  3. package/README.md +102 -0
  4. package/dist/dev.js +28564 -0
  5. package/dist/index.d.ts +7859 -0
  6. package/dist/index.js +28546 -0
  7. package/package.json +98 -0
  8. package/src/animation/AnimatedItems.tsx +183 -0
  9. package/src/animation/AnimationController.ts +33 -0
  10. package/src/animation/AnimationControllerImpl.ts +36 -0
  11. package/src/animation/AnimationHandle.ts +307 -0
  12. package/src/animation/CSSTransitionAnimate.tsx +170 -0
  13. package/src/animation/JavascriptAnimate.tsx +119 -0
  14. package/src/animation/easing.ts +221 -0
  15. package/src/animation/matchBy.ts +164 -0
  16. package/src/animation/timeoutController.ts +70 -0
  17. package/src/animation/useAnimationController.tsx +34 -0
  18. package/src/animation/useAnimationStartSnapshot.ts +118 -0
  19. package/src/animation/util.ts +41 -0
  20. package/src/cartesian/Area.tsx +915 -0
  21. package/src/cartesian/AreaRevealShape.tsx +190 -0
  22. package/src/cartesian/Bar.tsx +1369 -0
  23. package/src/cartesian/BarStack.tsx +152 -0
  24. package/src/cartesian/Brush.tsx +951 -0
  25. package/src/cartesian/CartesianAxis.tsx +722 -0
  26. package/src/cartesian/CartesianGrid.tsx +711 -0
  27. package/src/cartesian/ErrorBar.tsx +361 -0
  28. package/src/cartesian/Funnel.tsx +803 -0
  29. package/src/cartesian/GraphicalItemClipPath.tsx +67 -0
  30. package/src/cartesian/Line.tsx +863 -0
  31. package/src/cartesian/LineDrawShape.tsx +89 -0
  32. package/src/cartesian/ReferenceArea.tsx +331 -0
  33. package/src/cartesian/ReferenceDot.tsx +304 -0
  34. package/src/cartesian/ReferenceLine.tsx +474 -0
  35. package/src/cartesian/Scatter.tsx +888 -0
  36. package/src/cartesian/XAxis.tsx +433 -0
  37. package/src/cartesian/YAxis.tsx +443 -0
  38. package/src/cartesian/ZAxis.tsx +155 -0
  39. package/src/cartesian/cartesianPositionToCSSTranslate.tsx +29 -0
  40. package/src/cartesian/cartesianViewBoxToTrapezoid.tsx +21 -0
  41. package/src/cartesian/getCartesianPosition.tsx +267 -0
  42. package/src/cartesian/getEquidistantTicks.ts +144 -0
  43. package/src/cartesian/getTicks.ts +225 -0
  44. package/src/cartesian/isOutsidePosition.ts +15 -0
  45. package/src/cartesian/useAnimatedLineLength.ts +54 -0
  46. package/src/chart/AreaChart.tsx +29 -0
  47. package/src/chart/BarChart.tsx +29 -0
  48. package/src/chart/CartesianChart.tsx +131 -0
  49. package/src/chart/CategoricalChart.tsx +94 -0
  50. package/src/chart/ComposedChart.tsx +29 -0
  51. package/src/chart/FunnelChart.tsx +29 -0
  52. package/src/chart/LineChart.tsx +29 -0
  53. package/src/chart/PieChart.tsx +50 -0
  54. package/src/chart/PolarChart.tsx +164 -0
  55. package/src/chart/RadarChart.tsx +70 -0
  56. package/src/chart/RadialBarChart.tsx +46 -0
  57. package/src/chart/RechartsWrapper.tsx +551 -0
  58. package/src/chart/Sankey.tsx +1534 -0
  59. package/src/chart/ScatterChart.tsx +29 -0
  60. package/src/chart/SunburstChart.tsx +504 -0
  61. package/src/chart/Treemap.tsx +1223 -0
  62. package/src/chart/createInitialRootProps.ts +24 -0
  63. package/src/chart/types.ts +53 -0
  64. package/src/component/ActivePoints.tsx +144 -0
  65. package/src/component/Cell.tsx +37 -0
  66. package/src/component/Cursor.tsx +185 -0
  67. package/src/component/Customized.tsx +50 -0
  68. package/src/component/DefaultLegendContent.tsx +348 -0
  69. package/src/component/DefaultTooltipContent.tsx +224 -0
  70. package/src/component/Dots.tsx +157 -0
  71. package/src/component/Label.tsx +636 -0
  72. package/src/component/LabelList.tsx +375 -0
  73. package/src/component/Legend.tsx +512 -0
  74. package/src/component/ResponsiveContainer.tsx +348 -0
  75. package/src/component/Text.tsx +541 -0
  76. package/src/component/Tooltip.tsx +470 -0
  77. package/src/component/TooltipBoundingBox.tsx +146 -0
  78. package/src/component/responsiveContainerUtils.ts +134 -0
  79. package/src/container/ClipPathProvider.tsx +51 -0
  80. package/src/container/Layer.tsx +38 -0
  81. package/src/container/RootSurface.tsx +120 -0
  82. package/src/container/Surface.tsx +61 -0
  83. package/src/context/CellsContext.tsx +54 -0
  84. package/src/context/ErrorBarContext.tsx +146 -0
  85. package/src/context/GraphicalItemChildrenScope.tsx +18 -0
  86. package/src/context/PanoramaContext.tsx +9 -0
  87. package/src/context/RegisterGraphicalItemId.tsx +30 -0
  88. package/src/context/accessibilityContext.tsx +6 -0
  89. package/src/context/brushUpdateContext.tsx +9 -0
  90. package/src/context/chartDataContext.tsx +97 -0
  91. package/src/context/chartLayoutContext.tsx +201 -0
  92. package/src/context/legendPayloadContext.tsx +20 -0
  93. package/src/context/legendPortalContext.tsx +12 -0
  94. package/src/context/tooltipContext.tsx +89 -0
  95. package/src/context/tooltipPortalContext.tsx +12 -0
  96. package/src/context/useTooltipAxis.ts +22 -0
  97. package/src/hooks.ts +476 -0
  98. package/src/index.ts +280 -0
  99. package/src/polar/Pie.tsx +1360 -0
  100. package/src/polar/PolarAngleAxis.tsx +616 -0
  101. package/src/polar/PolarGrid.tsx +312 -0
  102. package/src/polar/PolarRadiusAxis.tsx +547 -0
  103. package/src/polar/Radar.tsx +734 -0
  104. package/src/polar/RadialBar.tsx +917 -0
  105. package/src/polar/defaultPolarAngleAxisProps.tsx +27 -0
  106. package/src/polar/defaultPolarRadiusAxisProps.tsx +25 -0
  107. package/src/shape/Cross.tsx +89 -0
  108. package/src/shape/Curve.tsx +343 -0
  109. package/src/shape/Dot.tsx +70 -0
  110. package/src/shape/Polygon.tsx +195 -0
  111. package/src/shape/Rectangle.tsx +470 -0
  112. package/src/shape/Sector.tsx +347 -0
  113. package/src/shape/Symbols.tsx +139 -0
  114. package/src/shape/Trapezoid.tsx +280 -0
  115. package/src/state/RechartsStateContext.tsx +12 -0
  116. package/src/state/RechartsStateProvider.tsx +87 -0
  117. package/src/state/RechartsStoreContext.tsx +25 -0
  118. package/src/state/RechartsStoreProvider.tsx +35 -0
  119. package/src/state/ReportChartProps.tsx +40 -0
  120. package/src/state/ReportEventSettings.tsx +30 -0
  121. package/src/state/ReportMainChartProps.tsx +61 -0
  122. package/src/state/ReportPolarOptions.tsx +35 -0
  123. package/src/state/SetGraphicalItem.ts +79 -0
  124. package/src/state/SetLegendPayload.ts +134 -0
  125. package/src/state/SetTooltipEntrySettings.tsx +72 -0
  126. package/src/state/actions.ts +464 -0
  127. package/src/state/brushSlice.ts +13 -0
  128. package/src/state/cartesianAxisSlice.ts +141 -0
  129. package/src/state/chartDataSlice.ts +50 -0
  130. package/src/state/chartState.ts +297 -0
  131. package/src/state/errorBarSlice.ts +29 -0
  132. package/src/state/eventSettingsSlice.ts +8 -0
  133. package/src/state/events.ts +76 -0
  134. package/src/state/externalEventsMiddleware.ts +255 -0
  135. package/src/state/graphicalItemsSlice.ts +74 -0
  136. package/src/state/hooks/useChartSelectors.tsx +45 -0
  137. package/src/state/keyboardEventsMiddleware.ts +228 -0
  138. package/src/state/layoutSlice.ts +21 -0
  139. package/src/state/legendSlice.ts +57 -0
  140. package/src/state/mouseEventsMiddleware.ts +185 -0
  141. package/src/state/optionsSlice.ts +47 -0
  142. package/src/state/polarAxisSlice.ts +10 -0
  143. package/src/state/polarOptionsSlice.ts +12 -0
  144. package/src/state/rawData.ts +26 -0
  145. package/src/state/referenceElementsSlice.ts +40 -0
  146. package/src/state/renderedTicksSlice.ts +15 -0
  147. package/src/state/rootPropsSlice.ts +47 -0
  148. package/src/state/selectors/areaSelectors.ts +240 -0
  149. package/src/state/selectors/arrayEqualityCheck.ts +29 -0
  150. package/src/state/selectors/axisSelectors.ts +2157 -0
  151. package/src/state/selectors/barSelectors.ts +310 -0
  152. package/src/state/selectors/barStackSelectors.ts +77 -0
  153. package/src/state/selectors/brushSelectors.ts +33 -0
  154. package/src/state/selectors/chartSelector.ts +185 -0
  155. package/src/state/selectors/combiners/combineActiveLabel.ts +16 -0
  156. package/src/state/selectors/combiners/combineActiveTooltipIndex.ts +95 -0
  157. package/src/state/selectors/combiners/combineAllBarPositions.ts +122 -0
  158. package/src/state/selectors/combiners/combineAxisRangeWithReverse.ts +16 -0
  159. package/src/state/selectors/combiners/combineBarPosition.ts +23 -0
  160. package/src/state/selectors/combiners/combineBarSizeList.ts +80 -0
  161. package/src/state/selectors/combiners/combineCheckedDomain.ts +48 -0
  162. package/src/state/selectors/combiners/combineConfiguredScale.ts +111 -0
  163. package/src/state/selectors/combiners/combineCoordinateForDefaultIndex.ts +45 -0
  164. package/src/state/selectors/combiners/combineDisplayedStackedData.ts +50 -0
  165. package/src/state/selectors/combiners/combineInverseScaleFunction.ts +16 -0
  166. package/src/state/selectors/combiners/combineRealScaleType.ts +44 -0
  167. package/src/state/selectors/combiners/combineStackedData.ts +27 -0
  168. package/src/state/selectors/combiners/combineTooltipInteractionState.ts +86 -0
  169. package/src/state/selectors/combiners/combineTooltipPayload.ts +205 -0
  170. package/src/state/selectors/combiners/combineTooltipPayloadConfigurations.ts +58 -0
  171. package/src/state/selectors/containerSelectors.ts +12 -0
  172. package/src/state/selectors/dataSelectors.ts +88 -0
  173. package/src/state/selectors/funnelSelectors.ts +75 -0
  174. package/src/state/selectors/graphicalItemSelectors.ts +30 -0
  175. package/src/state/selectors/legendSelectors.ts +29 -0
  176. package/src/state/selectors/lineSelectors.ts +134 -0
  177. package/src/state/selectors/numberDomainEqualityCheck.ts +14 -0
  178. package/src/state/selectors/pickAxisId.ts +5 -0
  179. package/src/state/selectors/pickAxisType.ts +3 -0
  180. package/src/state/selectors/pieSelectors.ts +97 -0
  181. package/src/state/selectors/polarAxisSelectors.ts +201 -0
  182. package/src/state/selectors/polarGridSelectors.ts +28 -0
  183. package/src/state/selectors/polarScaleSelectors.ts +133 -0
  184. package/src/state/selectors/polarSelectors.ts +219 -0
  185. package/src/state/selectors/radarSelectors.ts +191 -0
  186. package/src/state/selectors/radialBarSelectors.ts +435 -0
  187. package/src/state/selectors/rootPropsSelectors.ts +25 -0
  188. package/src/state/selectors/scatterSelectors.ts +96 -0
  189. package/src/state/selectors/selectActivePropsFromChartPointer.ts +26 -0
  190. package/src/state/selectors/selectAllAxes.ts +17 -0
  191. package/src/state/selectors/selectChartOffset.ts +16 -0
  192. package/src/state/selectors/selectChartOffsetInternal.ts +136 -0
  193. package/src/state/selectors/selectLegendArea.ts +20 -0
  194. package/src/state/selectors/selectPlotArea.ts +22 -0
  195. package/src/state/selectors/selectTooltipAxisId.ts +7 -0
  196. package/src/state/selectors/selectTooltipAxisType.ts +32 -0
  197. package/src/state/selectors/selectTooltipEventType.ts +48 -0
  198. package/src/state/selectors/selectTooltipPayloadSearcher.ts +6 -0
  199. package/src/state/selectors/selectTooltipSettings.ts +39 -0
  200. package/src/state/selectors/selectTooltipState.ts +8 -0
  201. package/src/state/selectors/selectors.ts +271 -0
  202. package/src/state/selectors/tooltipSelectors.ts +578 -0
  203. package/src/state/selectors/touchSelectors.ts +31 -0
  204. package/src/state/store.ts +29 -0
  205. package/src/state/storeIdentity.ts +9 -0
  206. package/src/state/teardownWrite.ts +10 -0
  207. package/src/state/tooltipInteraction.ts +23 -0
  208. package/src/state/tooltipSlice.ts +334 -0
  209. package/src/state/touchEventsMiddleware.ts +214 -0
  210. package/src/state/types/AnimationSettings.ts +20 -0
  211. package/src/state/types/AreaSettings.ts +14 -0
  212. package/src/state/types/BarSettings.ts +15 -0
  213. package/src/state/types/LineSettings.ts +9 -0
  214. package/src/state/types/PieSettings.ts +34 -0
  215. package/src/state/types/RadarSettings.ts +5 -0
  216. package/src/state/types/RadialBarSettings.ts +10 -0
  217. package/src/state/types/ScatterSettings.ts +12 -0
  218. package/src/state/types/StackedGraphicalItem.ts +34 -0
  219. package/src/state/useChartState.ts +17 -0
  220. package/src/state/zIndexSlice.ts +39 -0
  221. package/src/synchronisation/syncSelectors.ts +7 -0
  222. package/src/synchronisation/types.ts +44 -0
  223. package/src/synchronisation/useChartSynchronisation.tsx +404 -0
  224. package/src/theme/RechartsTheme.ts +16 -0
  225. package/src/theme/RechartsThemeContext.ts +19 -0
  226. package/src/theme/darkTheme.ts +13 -0
  227. package/src/theme/graphicalItemIdentity.ts +70 -0
  228. package/src/theme/legacyTheme.ts +17 -0
  229. package/src/theme/lightTheme.ts +13 -0
  230. package/src/types.ts +54 -0
  231. package/src/util/ActiveShapeUtils.tsx +162 -0
  232. package/src/util/BarUtils.tsx +62 -0
  233. package/src/util/BarePortal.tsx +50 -0
  234. package/src/util/CamelCaseSVGAttrs.ts +34 -0
  235. package/src/util/CartesianUtils.ts +48 -0
  236. package/src/util/ChartUtils.ts +802 -0
  237. package/src/util/Constants.ts +42 -0
  238. package/src/util/CssPrefixUtils.ts +20 -0
  239. package/src/util/DOMUtils.ts +129 -0
  240. package/src/util/DataUtils.ts +216 -0
  241. package/src/util/Events.ts +16 -0
  242. package/src/util/FunnelUtils.tsx +15 -0
  243. package/src/util/Global.ts +12 -0
  244. package/src/util/IfOverflow.ts +15 -0
  245. package/src/util/LRUCache.ts +41 -0
  246. package/src/util/LogUtils.ts +22 -0
  247. package/src/util/PolarUtils.ts +132 -0
  248. package/src/util/RadialBarUtils.tsx +24 -0
  249. package/src/util/ReactUtils.ts +95 -0
  250. package/src/util/ReduceCSSCalc.ts +187 -0
  251. package/src/util/ScatterUtils.tsx +52 -0
  252. package/src/util/ShapeElementProps.tsx +87 -0
  253. package/src/util/TickUtils.ts +60 -0
  254. package/src/util/XAxisUtils.ts +48 -0
  255. package/src/util/YAxisUtils.ts +48 -0
  256. package/src/util/axisPropsAreEqual.ts +48 -0
  257. package/src/util/createCartesianCharts.tsx +150 -0
  258. package/src/util/createEventProxy.ts +20 -0
  259. package/src/util/createPolarCharts.tsx +112 -0
  260. package/src/util/cursor/getCursorPoints.ts +46 -0
  261. package/src/util/cursor/getCursorRectangle.ts +28 -0
  262. package/src/util/cursor/getRadialCursorPoints.ts +32 -0
  263. package/src/util/excludeEventProps.ts +171 -0
  264. package/src/util/getActiveCoordinate.ts +213 -0
  265. package/src/util/getAxisTypeBasedOnLayout.ts +21 -0
  266. package/src/util/getClassNameFromUnknown.ts +6 -0
  267. package/src/util/getEveryNth.ts +27 -0
  268. package/src/util/getRadiusAndStrokeWidthFromDot.ts +35 -0
  269. package/src/util/getRelativeCoordinate.ts +80 -0
  270. package/src/util/getSliced.ts +13 -0
  271. package/src/util/hoverDedupe.ts +41 -0
  272. package/src/util/isDomainSpecifiedByUser.ts +219 -0
  273. package/src/util/isWellBehavedNumber.ts +7 -0
  274. package/src/util/pathTotalLength.ts +25 -0
  275. package/src/util/payload/getUniqPayload.ts +28 -0
  276. package/src/util/propsAreEqual.ts +121 -0
  277. package/src/util/resolveDefaultProps.ts +161 -0
  278. package/src/util/round.ts +32 -0
  279. package/src/util/scale/CartesianScaleHelper.ts +72 -0
  280. package/src/util/scale/CustomScaleDefinition.ts +81 -0
  281. package/src/util/scale/RechartsScale.ts +130 -0
  282. package/src/util/scale/createCategoricalInverse.ts +72 -0
  283. package/src/util/scale/getNiceTickValues.ts +311 -0
  284. package/src/util/scale/index.ts +2 -0
  285. package/src/util/scale/util/arithmetic.ts +54 -0
  286. package/src/util/solid-1-compat.ts +310 -0
  287. package/src/util/solidJsxCamelAugment.d.ts +84 -0
  288. package/src/util/stacks/getStackSeriesIdentifier.ts +21 -0
  289. package/src/util/stacks/stackTypes.ts +60 -0
  290. package/src/util/structurallyEqual.ts +31 -0
  291. package/src/util/svgPropertiesAndEvents.ts +53 -0
  292. package/src/util/svgPropertiesNoEvents.ts +571 -0
  293. package/src/util/tooltip/translate.ts +171 -0
  294. package/src/util/typedDataKey.ts +48 -0
  295. package/src/util/types.ts +1560 -0
  296. package/src/util/useAnimationId.ts +31 -0
  297. package/src/util/useElementOffset.ts +110 -0
  298. package/src/util/useId.ts +8 -0
  299. package/src/util/usePrefersReducedMotion.ts +48 -0
  300. package/src/util/useReportScale.ts +24 -0
  301. package/src/util/useUniqueId.ts +30 -0
  302. package/src/zIndex/DefaultZIndexes.ts +84 -0
  303. package/src/zIndex/ZIndexLayer.tsx +121 -0
  304. package/src/zIndex/ZIndexPortal.tsx +79 -0
  305. package/src/zIndex/getZIndexFromUnknown.ts +14 -0
  306. package/src/zIndex/zIndexSelectors.ts +48 -0
@@ -0,0 +1,170 @@
1
+ import { createMemo, createSignal, untrack, createEffect } from "solid-js"
2
+ import type { Accessor } from "solid-js"
3
+ import type { JSX } from "@solidjs/web"
4
+ import { useAnimationController } from "./useAnimationController"
5
+ import { getTransitionVal } from "./util"
6
+ import { resolveIsAnimationActive, usePrefersReducedMotion } from "../util/usePrefersReducedMotion"
7
+ import { CSSTransitionAnimation } from "./AnimationHandle"
8
+ import { RequestAnimationFrameTimeoutController } from "./timeoutController"
9
+ import type { EasingInput, NamedBezier } from "./easing"
10
+ import type { AnimationController } from "./AnimationController"
11
+
12
+ type CSSTransitionAnimateProps = {
13
+ animationController?: AnimationController
14
+ animationId: string
15
+ attributeName: string
16
+ begin?: number
17
+ canBegin?: boolean
18
+ /* Children receive an accessor and are invoked once, like JavascriptAnimate. A CSS
19
+ transition only runs when the SAME element changes its style: re-invoking children
20
+ per style change would mount a new element already at `to`, and nothing animates. */
21
+ children: (style: Accessor<Record<string, string | number>>) => JSX.Element
22
+ duration?: number
23
+ easing?: NamedBezier
24
+ from: string
25
+ isActive?: boolean | "auto"
26
+ onAnimationEnd?: () => void
27
+ onAnimationStart?: () => void
28
+ to: string
29
+ }
30
+
31
+ const defaultProps = {
32
+ begin: 0,
33
+ canBegin: true,
34
+ duration: 1000,
35
+ easing: "ease",
36
+ isActive: true,
37
+ onAnimationEnd: () => {},
38
+ onAnimationStart: () => {},
39
+ } as const satisfies Partial<CSSTransitionAnimateProps>
40
+
41
+ export function extractCssEasing(easingInput: EasingInput): NamedBezier | undefined {
42
+ if (easingInput === "spring" || typeof easingInput !== "string") {
43
+ return undefined
44
+ }
45
+ return easingInput
46
+ }
47
+
48
+ export function CSSTransitionAnimate(outsideProps: CSSTransitionAnimateProps) {
49
+ /* Live getters with defaults (resolveDefaultProps would freeze them at setup — see
50
+ * GOTCHA-005-C). A literal view skips mergeProps' per-key defineProperty and key scan;
51
+ * this runs once per animated shape. `??` matches mergeProps: no prop here admits null. */
52
+ const props = {
53
+ get animationController() {
54
+ return outsideProps.animationController
55
+ },
56
+ get animationId() {
57
+ return outsideProps.animationId
58
+ },
59
+ get attributeName() {
60
+ return outsideProps.attributeName
61
+ },
62
+ get begin() {
63
+ return outsideProps.begin ?? defaultProps.begin
64
+ },
65
+ get canBegin() {
66
+ return outsideProps.canBegin ?? defaultProps.canBegin
67
+ },
68
+ get children() {
69
+ return outsideProps.children
70
+ },
71
+ get duration() {
72
+ return outsideProps.duration ?? defaultProps.duration
73
+ },
74
+ get easing() {
75
+ return outsideProps.easing ?? defaultProps.easing
76
+ },
77
+ get from() {
78
+ return outsideProps.from
79
+ },
80
+ get isActive() {
81
+ return outsideProps.isActive ?? defaultProps.isActive
82
+ },
83
+ get onAnimationEnd() {
84
+ return outsideProps.onAnimationEnd ?? defaultProps.onAnimationEnd
85
+ },
86
+ get onAnimationStart() {
87
+ return outsideProps.onAnimationStart ?? defaultProps.onAnimationStart
88
+ },
89
+ get to() {
90
+ return outsideProps.to
91
+ },
92
+ }
93
+
94
+ const prefersReducedMotion = usePrefersReducedMotion()
95
+ const isActive = createMemo(() => resolveIsAnimationActive(props.isActive, prefersReducedMotion))
96
+
97
+ const animationController = useAnimationController(() => props.animationController)
98
+ const [style, setStyle] = createSignal<string | number>(
99
+ untrack(() => (isActive() === false ? props.to : props.from)),
100
+ )
101
+ /* Plain mutable mirrors React's useRef — flipping it must NOT trigger a
102
+ * re-render. Re-renders happen only when `style` changes (signal) or
103
+ * canBegin/isActive flip. The post-start render then reads the flag and
104
+ * adds `transition`. */
105
+ let initialized = false
106
+
107
+ createEffect(
108
+ () =>
109
+ isActive() === false || !props.canBegin
110
+ ? null
111
+ : {
112
+ animationId: props.animationId + props.attributeName,
113
+ begin: props.begin,
114
+ controller: animationController(),
115
+ duration: props.duration,
116
+ easing: props.easing,
117
+ from: props.from,
118
+ onAnimationEnd: props.onAnimationEnd,
119
+ onAnimationStart: props.onAnimationStart,
120
+ to: props.to,
121
+ },
122
+ (run) => {
123
+ if (run == null) {
124
+ return undefined
125
+ }
126
+ initialized = true
127
+
128
+ const animation = new CSSTransitionAnimation({
129
+ animationBegin: run.begin,
130
+ animationDuration: run.duration,
131
+ animationId: run.animationId,
132
+ easing: run.easing,
133
+ from: run.from,
134
+ onAnimationEnd: run.onAnimationEnd,
135
+ onAnimationStart: () => {
136
+ setStyle(run.from)
137
+ run.onAnimationStart()
138
+ },
139
+ to: run.to,
140
+ })
141
+
142
+ return run.controller(new RequestAnimationFrameTimeoutController(), animation, setStyle)
143
+ },
144
+ )
145
+
146
+ const childStyle = createMemo<Record<string, string | number>>(() => {
147
+ /* Always read `style()` so the memo tracks it regardless of which
148
+ * branch we currently take. Otherwise: memo first evaluates with
149
+ * initialized=false, never reads style, never subscribes — when the
150
+ * start-effect later flips initialized and setStyle fires, the memo
151
+ * has no dependency on style and doesn't re-evaluate. */
152
+ const styleNow = style()
153
+ if (isActive() === false) {
154
+ return { [props.attributeName]: props.to }
155
+ }
156
+ if (!props.canBegin) {
157
+ return { [props.attributeName]: props.from }
158
+ }
159
+ if (initialized) {
160
+ return {
161
+ transition: getTransitionVal([props.attributeName], props.duration, props.easing),
162
+ [props.attributeName]: styleNow,
163
+ }
164
+ }
165
+ return { [props.attributeName]: props.from }
166
+ })
167
+
168
+ /* eslint-disable-next-line solid/reactivity -- one-time children() call keeps element identity; childStyle is reactive */
169
+ return untrack(() => props.children(childStyle))
170
+ }
@@ -0,0 +1,119 @@
1
+ import { createMemo, createSignal, untrack, createEffect } from "solid-js"
2
+ import type { JSX } from "@solidjs/web"
3
+ import { createEasingFunction } from "./easing"
4
+ import type { EasingInput } from "./easing"
5
+ import { useAnimationController } from "./useAnimationController"
6
+ import { resolveIsAnimationActive, usePrefersReducedMotion } from "../util/usePrefersReducedMotion"
7
+ import { JavascriptAnimation } from "./AnimationHandle"
8
+ import { RequestAnimationFrameTimeoutController } from "./timeoutController"
9
+ import type { AnimationController } from "./AnimationController"
10
+
11
+ import { mergeProps } from "../util/solid-1-compat"
12
+ type JavascriptAnimateProps = {
13
+ animationController?: AnimationController
14
+ animationId: string
15
+ begin?: number
16
+ canBegin?: boolean
17
+ /* Children receives an Accessor<number> instead of a plain number. Solid
18
+ evaluates the children fn ONCE at setup and the returned JSX is mounted
19
+ stable across animation ticks; reading `t()` inside reactive attribute
20
+ slots (e.g. `<path d={getD(t())}/>`) updates only the attribute, not the
21
+ element identity. Plain-value children gets re-instantiated every tick,
22
+ replacing the DOM node on each render — tests holding a captured DOM ref
23
+ see stale attributes. */
24
+ children: (time: () => number) => JSX.Element
25
+ duration?: number
26
+ easing?: EasingInput
27
+ isActive?: boolean | "auto"
28
+ onAnimationEnd?: () => void
29
+ onAnimationStart?: () => void
30
+ }
31
+
32
+ const defaultJavascriptAnimateProps = {
33
+ begin: 0,
34
+ canBegin: true,
35
+ duration: 1000,
36
+ easing: "ease",
37
+ isActive: true,
38
+ onAnimationEnd: () => {},
39
+ onAnimationStart: () => {},
40
+ } as const satisfies Partial<JavascriptAnimateProps>
41
+
42
+ const from = 0
43
+ const to = 1
44
+
45
+ export function JavascriptAnimate(outsideProps: JavascriptAnimateProps) {
46
+ /* mergeProps preserves the props proxy lazily so child callbacks and
47
+ * downstream toggles (isActive, canBegin) stay reactive. resolveDefaultProps
48
+ * would freeze them at setup — see GOTCHA-005-C. */
49
+ const props = mergeProps(defaultJavascriptAnimateProps, outsideProps)
50
+
51
+ const prefersReducedMotion = usePrefersReducedMotion()
52
+ const isActive = createMemo(() => resolveIsAnimationActive(props.isActive, prefersReducedMotion))
53
+
54
+ const animationController = useAnimationController(() => props.animationController)
55
+
56
+ /* Each committed frame is tagged with the animation that produced it. When
57
+ animationId flips, the previous animation's frame no longer applies and
58
+ time reads `from` immediately, the same as upstream remounting
59
+ JavascriptAnimate via `key={animationId}`; no stale `t=1` frame leaks into
60
+ the new animation's first render (GOTCHA-014-G). */
61
+ const [frame, setFrame] = createSignal<{ animationId: string; t: number }>(
62
+ untrack(() => ({ animationId: props.animationId, t: isActive() ? from : to })),
63
+ )
64
+ const time = createMemo(() => {
65
+ if (!isActive()) {
66
+ return to
67
+ }
68
+ const current = frame()
69
+ return current.animationId === props.animationId ? current.t : from
70
+ })
71
+
72
+ createEffect(
73
+ () =>
74
+ isActive() === false || !props.canBegin
75
+ ? null
76
+ : {
77
+ animationId: props.animationId,
78
+ begin: props.begin,
79
+ controller: animationController(),
80
+ duration: props.duration,
81
+ easing: props.easing,
82
+ onAnimationEnd: props.onAnimationEnd,
83
+ onAnimationStart: props.onAnimationStart,
84
+ },
85
+ (run) => {
86
+ if (run == null) {
87
+ return undefined
88
+ }
89
+ const easingFunction = createEasingFunction(run.easing)
90
+ if (easingFunction == null) {
91
+ return undefined
92
+ }
93
+
94
+ const animation = new JavascriptAnimation({
95
+ animationBegin: run.begin,
96
+ animationDuration: run.duration,
97
+ animationId: run.animationId,
98
+ easing: easingFunction,
99
+ from,
100
+ onAnimationEnd: run.onAnimationEnd,
101
+ onAnimationStart: run.onAnimationStart,
102
+ to,
103
+ })
104
+
105
+ return run.controller(new RequestAnimationFrameTimeoutController(), animation, (value) =>
106
+ setFrame({ animationId: run.animationId, t: Number(value) }),
107
+ )
108
+ },
109
+ )
110
+
111
+ /* perf: invoke children ONCE at setup. The `time` accessor is
112
+ reactive on its own, so consumers reading `t()` inside their createMemo
113
+ subscribe to the frame signal directly. Building the subtree at setup and
114
+ returning it directly lets attribute bindings update in place instead of
115
+ re-creating every computation inside the children thunk per tick. */
116
+ /* eslint-disable-next-line solid/reactivity -- intentional one-time children() call at setup for perf (see comment above); time is a reactive accessor */
117
+ const subtree = props.children(time)
118
+ return subtree
119
+ }
@@ -0,0 +1,221 @@
1
+ export const ACCURACY = 1e-4
2
+
3
+ const cubicBezierFactor = (c1: number, c2: number) => [0, 3 * c1, 3 * c2 - 6 * c1, 3 * c1 - 3 * c2 + 1]
4
+
5
+ const evaluatePolynomial = (params: ReadonlyArray<number>, animationElapsedTime: number) =>
6
+ params.map((param, i) => param * animationElapsedTime ** i).reduce((pre, curr) => pre + curr)
7
+
8
+ const cubicBezier = (c1: number, c2: number) => (animationElapsedTime: number) => {
9
+ const params = cubicBezierFactor(c1, c2)
10
+
11
+ return evaluatePolynomial(params, animationElapsedTime)
12
+ }
13
+
14
+ const derivativeCubicBezier = (c1: number, c2: number) => (animationElapsedTime: number) => {
15
+ const params = cubicBezierFactor(c1, c2)
16
+ const newParams = [...params.map((param, i) => param * i).slice(1), 0]
17
+
18
+ return evaluatePolynomial(newParams, animationElapsedTime)
19
+ }
20
+
21
+ type CubicBezierTemplate = `cubic-bezier(${number},${number},${number},${number})`
22
+
23
+ export type NamedBezier = "linear" | "ease" | "ease-in" | "ease-out" | "ease-in-out" | CubicBezierTemplate
24
+
25
+ type BezierInput = [NamedBezier] | [number, number, number, number]
26
+
27
+ export type BezierEasingFunction = {
28
+ isStepper: false
29
+ (animationElapsedTime: number): number
30
+ }
31
+
32
+ const parseCubicBezier = (easing: string): [number, number, number, number] | null => {
33
+ const easingParts = easing.split("(")
34
+ if (easingParts.length !== 2 || easingParts[0] !== "cubic-bezier") {
35
+ return null
36
+ }
37
+ const numbers = easingParts[1]?.split(")")[0]?.split(",")
38
+ if (numbers == null || numbers.length !== 4) {
39
+ return null
40
+ }
41
+ const coords = numbers.map((x: string) => parseFloat(x))
42
+ return [coords[0] as number, coords[1] as number, coords[2] as number, coords[3] as number]
43
+ }
44
+
45
+ const getBezierCoordinates = (...args: BezierInput): [number, number, number, number] => {
46
+ if (args.length === 1) {
47
+ switch (args[0]) {
48
+ case "linear":
49
+ return [0.0, 0.0, 1.0, 1.0]
50
+ case "ease":
51
+ return [0.25, 0.1, 0.25, 1.0]
52
+ case "ease-in":
53
+ return [0.42, 0.0, 1.0, 1.0]
54
+ case "ease-out":
55
+ return [0.42, 0.0, 0.58, 1.0]
56
+ case "ease-in-out":
57
+ return [0.0, 0.0, 0.58, 1.0]
58
+ default: {
59
+ const easing = parseCubicBezier(args[0])
60
+ if (easing) {
61
+ return easing
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ if (args.length === 4) {
68
+ return args
69
+ }
70
+
71
+ // Fallback for invalid inputs. The previous implementation was buggy and would lead to NaN.
72
+ // Returning linear easing is a safe default.
73
+ return [0.0, 0.0, 1.0, 1.0]
74
+ }
75
+
76
+ const createBezierEasing = (x1: number, y1: number, x2: number, y2: number): BezierEasingFunction => {
77
+ const curveX = cubicBezier(x1, x2)
78
+ const curveY = cubicBezier(y1, y2)
79
+ const derCurveX = derivativeCubicBezier(x1, x2)
80
+ const rangeValue = (value: number): number => {
81
+ if (value > 1) {
82
+ return 1
83
+ }
84
+ if (value < 0) {
85
+ return 0
86
+ }
87
+
88
+ return value
89
+ }
90
+
91
+ const bezier = (_animationElapsedTime: number) => {
92
+ const animationElapsedTime = _animationElapsedTime > 1 ? 1 : _animationElapsedTime
93
+ let x = animationElapsedTime
94
+
95
+ for (let i = 0; i < 8; ++i) {
96
+ const evalT = curveX(x) - animationElapsedTime
97
+ const derVal = derCurveX(x)
98
+
99
+ if (Math.abs(evalT - animationElapsedTime) < ACCURACY || derVal < ACCURACY) {
100
+ return curveY(x)
101
+ }
102
+
103
+ x = rangeValue(x - evalT / derVal)
104
+ }
105
+
106
+ return curveY(x)
107
+ }
108
+
109
+ bezier.isStepper = false as const
110
+
111
+ return bezier
112
+ }
113
+
114
+ // calculate cubic-bezier using Newton's method
115
+ export const configBezier = (...args: BezierInput): BezierEasingFunction => {
116
+ return createBezierEasing(...getBezierCoordinates(...args))
117
+ }
118
+
119
+ type SpringInput = {
120
+ /**
121
+ * The stiffness coefficient of the spring.
122
+ * Higher values create a more forceful, faster pull towards the target.
123
+ */
124
+ stiff?: number
125
+ /**
126
+ * The damping coefficient (friction/resistance).
127
+ * Higher values reduce bounciness and stop oscillation sooner. Lower values create more bounce.
128
+ */
129
+ damping?: number
130
+ /**
131
+ * The physics simulation time step in milliseconds.
132
+ * Determines the granularity of the baked frames. 16.67ms roughly equals a 60fps frame delta.
133
+ */
134
+ dt?: number
135
+ }
136
+
137
+ /**
138
+ * Creates a performance-optimized, progress-based spring easing function.
139
+ * It pre-calculates ("bakes") spring physics frames upfront based on a fixed duration,
140
+ * then returns a pure, lightweight function mapping progress (0 to 1) to the animated position.
141
+ * This approach is ideal for low-power devices because it removes heavy physics math from the frame loop.
142
+ */
143
+ export const createSpringEasing = (config: SpringInput = {}): EasingFunction => {
144
+ const { stiff = 100, damping = 8, dt = 16.67 } = config
145
+ const destX = 1
146
+
147
+ const positions: number[] = [0]
148
+ let currX = 0
149
+ let currV = 0
150
+
151
+ // Safety valve to prevent accidental infinite loops if physics config is extreme
152
+ const maxIterations = 10000
153
+ let iterations = 0
154
+
155
+ // 1. Run the simulation until the spring completely stops moving
156
+ while (iterations < maxIterations) {
157
+ const FSpring = -(currX - destX) * stiff
158
+ const FDamping = currV * damping
159
+
160
+ currV += ((FSpring - FDamping) * dt) / 1000
161
+ currX += (currV * dt) / 1000
162
+
163
+ positions.push(currX)
164
+
165
+ // Stop only when position is essentially at 1.0 AND bounce velocity has died down
166
+ if (Math.abs(currX - destX) < ACCURACY && Math.abs(currV) < ACCURACY) {
167
+ break
168
+ }
169
+ iterations++
170
+ }
171
+
172
+ // Force the absolute final element to be exactly 1.0 for a perfect finish
173
+ positions[positions.length - 1] = destX
174
+ const maxIndex = positions.length - 1
175
+
176
+ // 2. The ultra-smooth runtime function mapping your 0..1 progress
177
+ return (t: number): number => {
178
+ if (t <= 0) return 0
179
+ if (t >= 1) return destX
180
+
181
+ // Scale t (0..1) proportionally across our entire pre-calculated array
182
+ const exactFrame = t * maxIndex
183
+ const index = Math.floor(exactFrame)
184
+ const fraction = exactFrame - index
185
+
186
+ // Blend between the two closest frames
187
+ return (positions[index] ?? 0) + ((positions[index + 1] ?? 0) - (positions[index] ?? 0)) * fraction
188
+ }
189
+ }
190
+
191
+ export type EasingFunction = (t: number) => number
192
+
193
+ /**
194
+ * @inline
195
+ */
196
+ export type EasingInput = NamedBezier | "spring" | EasingFunction
197
+
198
+ export const createEasingFunction = (easing: EasingInput): EasingFunction | null => {
199
+ if (typeof easing === "string") {
200
+ switch (easing) {
201
+ case "ease":
202
+ case "ease-in-out":
203
+ case "ease-out":
204
+ case "ease-in":
205
+ case "linear":
206
+ return configBezier(easing)
207
+ case "spring":
208
+ return createSpringEasing()
209
+ default:
210
+ if (easing.split("(")[0] === "cubic-bezier") {
211
+ return configBezier(easing)
212
+ }
213
+ }
214
+ }
215
+
216
+ if (typeof easing === "function") {
217
+ return easing
218
+ }
219
+
220
+ return null
221
+ }
@@ -0,0 +1,164 @@
1
+ import { getValueByDataKey } from "../util/ChartUtils"
2
+ import type { DataKey } from "../util/types"
3
+ import type {
4
+ AnimationItem,
5
+ AnimationMatchBy,
6
+ AnimationMatchByProp,
7
+ } from "../state/types/AnimationSettings"
8
+
9
+ export type { AnimationItem, AnimationMatchBy, AnimationMatchByProp }
10
+
11
+ /**
12
+ * Match animation items by their array index (the default behavior).
13
+ *
14
+ * Previous items are paired with next items based on their position
15
+ * in the array, with proportional stretching when array lengths differ.
16
+ *
17
+ * @since 3.9
18
+ */
19
+ export const matchByIndex = "index" as const
20
+
21
+ /**
22
+ * Match animation items sequentially: previous item 0 pairs with next item 0,
23
+ * previous item 1 pairs with next item 1, and so on. Extra next items animate in;
24
+ * extra previous items are dropped.
25
+ *
26
+ * @since 3.9
27
+ */
28
+ export const matchAppend = "append" as const
29
+
30
+ /**
31
+ * Create a matching function that pairs items by a data key from their payload.
32
+ *
33
+ * @since 3.9
34
+ */
35
+ export function matchByDataKey(
36
+ dataKey: DataKey<Record<string, unknown>>,
37
+ ): AnimationMatchBy<{ payload?: unknown }> {
38
+ return (item: { payload?: unknown }): string | number | null => {
39
+ if (item.payload == null || typeof item.payload !== "object") {
40
+ return null
41
+ }
42
+ const value = getValueByDataKey(item.payload as Record<string, unknown>, dataKey)
43
+ if (value == null) {
44
+ return null
45
+ }
46
+ if (typeof value === "string" || typeof value === "number") {
47
+ return value
48
+ }
49
+ return JSON.stringify(value)
50
+ }
51
+ }
52
+
53
+ function tagAlignedItems<T>(
54
+ alignedPrevItems: ReadonlyArray<T | undefined>,
55
+ nextItems: ReadonlyArray<T>,
56
+ removedPrevItems: ReadonlyArray<T> = [],
57
+ ): ReadonlyArray<AnimationItem<T>> {
58
+ const tagged: Array<AnimationItem<T>> = []
59
+ for (const prev of removedPrevItems) {
60
+ tagged.push({ status: "removed", prev })
61
+ }
62
+ for (let i = 0; i < nextItems.length; i++) {
63
+ const prev = alignedPrevItems[i]
64
+ /* i is within bounds; noUncheckedIndexedAccess cannot see that */
65
+ const next = nextItems[i] as T
66
+ if (prev != null) {
67
+ tagged.push({ status: "matched", prev, next })
68
+ } else {
69
+ tagged.push({ status: "added", next })
70
+ }
71
+ }
72
+ return tagged
73
+ }
74
+
75
+ function matchByIndexImpl<T>(
76
+ prevItems: ReadonlyArray<T>,
77
+ nextItems: ReadonlyArray<T>,
78
+ ): ReadonlyArray<AnimationItem<T>> {
79
+ const factor = prevItems.length / nextItems.length
80
+ const alignedPrevItems = nextItems.map((_, i) => prevItems[Math.floor(i * factor)])
81
+ return tagAlignedItems(alignedPrevItems, nextItems)
82
+ }
83
+
84
+ function matchAppendImpl<T>(
85
+ prevItems: ReadonlyArray<T>,
86
+ nextItems: ReadonlyArray<T>,
87
+ ): ReadonlyArray<AnimationItem<T>> {
88
+ const alignedPrevItems = nextItems.map((_, i) => prevItems[i])
89
+ return tagAlignedItems(alignedPrevItems, nextItems)
90
+ }
91
+
92
+ function buildPrevKeyMap<T>(
93
+ prevItems: ReadonlyArray<T>,
94
+ matchBy: AnimationMatchBy<T>,
95
+ ): ReadonlyMap<string | number, T> {
96
+ const prevMap = new Map<string | number, T>()
97
+ for (let i = 0; i < prevItems.length; i++) {
98
+ const item = prevItems[i]
99
+ if (item == null) {
100
+ continue
101
+ }
102
+ const key = matchBy(item, i)
103
+ if (key != null && !prevMap.has(key)) {
104
+ prevMap.set(key, item)
105
+ }
106
+ }
107
+ return prevMap
108
+ }
109
+
110
+ function matchByKey<T>(
111
+ prevItems: ReadonlyArray<T>,
112
+ nextItems: ReadonlyArray<T>,
113
+ matchBy: AnimationMatchBy<T>,
114
+ ): ReadonlyArray<AnimationItem<T>> {
115
+ const prevMap = buildPrevKeyMap(prevItems, matchBy)
116
+ const matchedKeys = new Set<string | number>()
117
+
118
+ const alignedPrevItems = nextItems.map((next, i) => {
119
+ const key = matchBy(next, i)
120
+ if (key != null) {
121
+ const prev = prevMap.get(key)
122
+ if (prev !== undefined) {
123
+ matchedKeys.add(key)
124
+ return prev
125
+ }
126
+ }
127
+ return undefined
128
+ })
129
+
130
+ const removedPrevItems: Array<T> = []
131
+ for (const [key, item] of prevMap) {
132
+ if (!matchedKeys.has(key)) {
133
+ removedPrevItems.push(item)
134
+ }
135
+ }
136
+
137
+ return tagAlignedItems(alignedPrevItems, nextItems, removedPrevItems)
138
+ }
139
+
140
+ /**
141
+ * Match previous items to next items using the given matching strategy.
142
+ *
143
+ * On first render, all next items are returned as `{ status: 'added' }`.
144
+ * For key-based matching, unmatched previous items are `{ status: 'removed' }`.
145
+ */
146
+ export function matchAnimationItems<T>(
147
+ prevItems: ReadonlyArray<T> | null,
148
+ nextItems: ReadonlyArray<T> | undefined,
149
+ matchBy: AnimationMatchByProp<T>,
150
+ ): ReadonlyArray<AnimationItem<T>> | null {
151
+ if (nextItems == null) {
152
+ return null
153
+ }
154
+ if (prevItems == null) {
155
+ return nextItems.map((next) => ({ status: "added" as const, next }))
156
+ }
157
+ if (matchBy === matchByIndex) {
158
+ return matchByIndexImpl(prevItems, nextItems)
159
+ }
160
+ if (matchBy === matchAppend) {
161
+ return matchAppendImpl(prevItems, nextItems)
162
+ }
163
+ return matchByKey(prevItems, nextItems, matchBy)
164
+ }