@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.
- package/LICENSE +22 -0
- package/NOTICE +19 -0
- package/README.md +102 -0
- package/dist/dev.js +28564 -0
- package/dist/index.d.ts +7859 -0
- package/dist/index.js +28546 -0
- package/package.json +98 -0
- package/src/animation/AnimatedItems.tsx +183 -0
- package/src/animation/AnimationController.ts +33 -0
- package/src/animation/AnimationControllerImpl.ts +36 -0
- package/src/animation/AnimationHandle.ts +307 -0
- package/src/animation/CSSTransitionAnimate.tsx +170 -0
- package/src/animation/JavascriptAnimate.tsx +119 -0
- package/src/animation/easing.ts +221 -0
- package/src/animation/matchBy.ts +164 -0
- package/src/animation/timeoutController.ts +70 -0
- package/src/animation/useAnimationController.tsx +34 -0
- package/src/animation/useAnimationStartSnapshot.ts +118 -0
- package/src/animation/util.ts +41 -0
- package/src/cartesian/Area.tsx +915 -0
- package/src/cartesian/AreaRevealShape.tsx +190 -0
- package/src/cartesian/Bar.tsx +1369 -0
- package/src/cartesian/BarStack.tsx +152 -0
- package/src/cartesian/Brush.tsx +951 -0
- package/src/cartesian/CartesianAxis.tsx +722 -0
- package/src/cartesian/CartesianGrid.tsx +711 -0
- package/src/cartesian/ErrorBar.tsx +361 -0
- package/src/cartesian/Funnel.tsx +803 -0
- package/src/cartesian/GraphicalItemClipPath.tsx +67 -0
- package/src/cartesian/Line.tsx +863 -0
- package/src/cartesian/LineDrawShape.tsx +89 -0
- package/src/cartesian/ReferenceArea.tsx +331 -0
- package/src/cartesian/ReferenceDot.tsx +304 -0
- package/src/cartesian/ReferenceLine.tsx +474 -0
- package/src/cartesian/Scatter.tsx +888 -0
- package/src/cartesian/XAxis.tsx +433 -0
- package/src/cartesian/YAxis.tsx +443 -0
- package/src/cartesian/ZAxis.tsx +155 -0
- package/src/cartesian/cartesianPositionToCSSTranslate.tsx +29 -0
- package/src/cartesian/cartesianViewBoxToTrapezoid.tsx +21 -0
- package/src/cartesian/getCartesianPosition.tsx +267 -0
- package/src/cartesian/getEquidistantTicks.ts +144 -0
- package/src/cartesian/getTicks.ts +225 -0
- package/src/cartesian/isOutsidePosition.ts +15 -0
- package/src/cartesian/useAnimatedLineLength.ts +54 -0
- package/src/chart/AreaChart.tsx +29 -0
- package/src/chart/BarChart.tsx +29 -0
- package/src/chart/CartesianChart.tsx +131 -0
- package/src/chart/CategoricalChart.tsx +94 -0
- package/src/chart/ComposedChart.tsx +29 -0
- package/src/chart/FunnelChart.tsx +29 -0
- package/src/chart/LineChart.tsx +29 -0
- package/src/chart/PieChart.tsx +50 -0
- package/src/chart/PolarChart.tsx +164 -0
- package/src/chart/RadarChart.tsx +70 -0
- package/src/chart/RadialBarChart.tsx +46 -0
- package/src/chart/RechartsWrapper.tsx +551 -0
- package/src/chart/Sankey.tsx +1534 -0
- package/src/chart/ScatterChart.tsx +29 -0
- package/src/chart/SunburstChart.tsx +504 -0
- package/src/chart/Treemap.tsx +1223 -0
- package/src/chart/createInitialRootProps.ts +24 -0
- package/src/chart/types.ts +53 -0
- package/src/component/ActivePoints.tsx +144 -0
- package/src/component/Cell.tsx +37 -0
- package/src/component/Cursor.tsx +185 -0
- package/src/component/Customized.tsx +50 -0
- package/src/component/DefaultLegendContent.tsx +348 -0
- package/src/component/DefaultTooltipContent.tsx +224 -0
- package/src/component/Dots.tsx +157 -0
- package/src/component/Label.tsx +636 -0
- package/src/component/LabelList.tsx +375 -0
- package/src/component/Legend.tsx +512 -0
- package/src/component/ResponsiveContainer.tsx +348 -0
- package/src/component/Text.tsx +541 -0
- package/src/component/Tooltip.tsx +470 -0
- package/src/component/TooltipBoundingBox.tsx +146 -0
- package/src/component/responsiveContainerUtils.ts +134 -0
- package/src/container/ClipPathProvider.tsx +51 -0
- package/src/container/Layer.tsx +38 -0
- package/src/container/RootSurface.tsx +120 -0
- package/src/container/Surface.tsx +61 -0
- package/src/context/CellsContext.tsx +54 -0
- package/src/context/ErrorBarContext.tsx +146 -0
- package/src/context/GraphicalItemChildrenScope.tsx +18 -0
- package/src/context/PanoramaContext.tsx +9 -0
- package/src/context/RegisterGraphicalItemId.tsx +30 -0
- package/src/context/accessibilityContext.tsx +6 -0
- package/src/context/brushUpdateContext.tsx +9 -0
- package/src/context/chartDataContext.tsx +97 -0
- package/src/context/chartLayoutContext.tsx +201 -0
- package/src/context/legendPayloadContext.tsx +20 -0
- package/src/context/legendPortalContext.tsx +12 -0
- package/src/context/tooltipContext.tsx +89 -0
- package/src/context/tooltipPortalContext.tsx +12 -0
- package/src/context/useTooltipAxis.ts +22 -0
- package/src/hooks.ts +476 -0
- package/src/index.ts +280 -0
- package/src/polar/Pie.tsx +1360 -0
- package/src/polar/PolarAngleAxis.tsx +616 -0
- package/src/polar/PolarGrid.tsx +312 -0
- package/src/polar/PolarRadiusAxis.tsx +547 -0
- package/src/polar/Radar.tsx +734 -0
- package/src/polar/RadialBar.tsx +917 -0
- package/src/polar/defaultPolarAngleAxisProps.tsx +27 -0
- package/src/polar/defaultPolarRadiusAxisProps.tsx +25 -0
- package/src/shape/Cross.tsx +89 -0
- package/src/shape/Curve.tsx +343 -0
- package/src/shape/Dot.tsx +70 -0
- package/src/shape/Polygon.tsx +195 -0
- package/src/shape/Rectangle.tsx +470 -0
- package/src/shape/Sector.tsx +347 -0
- package/src/shape/Symbols.tsx +139 -0
- package/src/shape/Trapezoid.tsx +280 -0
- package/src/state/RechartsStateContext.tsx +12 -0
- package/src/state/RechartsStateProvider.tsx +87 -0
- package/src/state/RechartsStoreContext.tsx +25 -0
- package/src/state/RechartsStoreProvider.tsx +35 -0
- package/src/state/ReportChartProps.tsx +40 -0
- package/src/state/ReportEventSettings.tsx +30 -0
- package/src/state/ReportMainChartProps.tsx +61 -0
- package/src/state/ReportPolarOptions.tsx +35 -0
- package/src/state/SetGraphicalItem.ts +79 -0
- package/src/state/SetLegendPayload.ts +134 -0
- package/src/state/SetTooltipEntrySettings.tsx +72 -0
- package/src/state/actions.ts +464 -0
- package/src/state/brushSlice.ts +13 -0
- package/src/state/cartesianAxisSlice.ts +141 -0
- package/src/state/chartDataSlice.ts +50 -0
- package/src/state/chartState.ts +297 -0
- package/src/state/errorBarSlice.ts +29 -0
- package/src/state/eventSettingsSlice.ts +8 -0
- package/src/state/events.ts +76 -0
- package/src/state/externalEventsMiddleware.ts +255 -0
- package/src/state/graphicalItemsSlice.ts +74 -0
- package/src/state/hooks/useChartSelectors.tsx +45 -0
- package/src/state/keyboardEventsMiddleware.ts +228 -0
- package/src/state/layoutSlice.ts +21 -0
- package/src/state/legendSlice.ts +57 -0
- package/src/state/mouseEventsMiddleware.ts +185 -0
- package/src/state/optionsSlice.ts +47 -0
- package/src/state/polarAxisSlice.ts +10 -0
- package/src/state/polarOptionsSlice.ts +12 -0
- package/src/state/rawData.ts +26 -0
- package/src/state/referenceElementsSlice.ts +40 -0
- package/src/state/renderedTicksSlice.ts +15 -0
- package/src/state/rootPropsSlice.ts +47 -0
- package/src/state/selectors/areaSelectors.ts +240 -0
- package/src/state/selectors/arrayEqualityCheck.ts +29 -0
- package/src/state/selectors/axisSelectors.ts +2157 -0
- package/src/state/selectors/barSelectors.ts +310 -0
- package/src/state/selectors/barStackSelectors.ts +77 -0
- package/src/state/selectors/brushSelectors.ts +33 -0
- package/src/state/selectors/chartSelector.ts +185 -0
- package/src/state/selectors/combiners/combineActiveLabel.ts +16 -0
- package/src/state/selectors/combiners/combineActiveTooltipIndex.ts +95 -0
- package/src/state/selectors/combiners/combineAllBarPositions.ts +122 -0
- package/src/state/selectors/combiners/combineAxisRangeWithReverse.ts +16 -0
- package/src/state/selectors/combiners/combineBarPosition.ts +23 -0
- package/src/state/selectors/combiners/combineBarSizeList.ts +80 -0
- package/src/state/selectors/combiners/combineCheckedDomain.ts +48 -0
- package/src/state/selectors/combiners/combineConfiguredScale.ts +111 -0
- package/src/state/selectors/combiners/combineCoordinateForDefaultIndex.ts +45 -0
- package/src/state/selectors/combiners/combineDisplayedStackedData.ts +50 -0
- package/src/state/selectors/combiners/combineInverseScaleFunction.ts +16 -0
- package/src/state/selectors/combiners/combineRealScaleType.ts +44 -0
- package/src/state/selectors/combiners/combineStackedData.ts +27 -0
- package/src/state/selectors/combiners/combineTooltipInteractionState.ts +86 -0
- package/src/state/selectors/combiners/combineTooltipPayload.ts +205 -0
- package/src/state/selectors/combiners/combineTooltipPayloadConfigurations.ts +58 -0
- package/src/state/selectors/containerSelectors.ts +12 -0
- package/src/state/selectors/dataSelectors.ts +88 -0
- package/src/state/selectors/funnelSelectors.ts +75 -0
- package/src/state/selectors/graphicalItemSelectors.ts +30 -0
- package/src/state/selectors/legendSelectors.ts +29 -0
- package/src/state/selectors/lineSelectors.ts +134 -0
- package/src/state/selectors/numberDomainEqualityCheck.ts +14 -0
- package/src/state/selectors/pickAxisId.ts +5 -0
- package/src/state/selectors/pickAxisType.ts +3 -0
- package/src/state/selectors/pieSelectors.ts +97 -0
- package/src/state/selectors/polarAxisSelectors.ts +201 -0
- package/src/state/selectors/polarGridSelectors.ts +28 -0
- package/src/state/selectors/polarScaleSelectors.ts +133 -0
- package/src/state/selectors/polarSelectors.ts +219 -0
- package/src/state/selectors/radarSelectors.ts +191 -0
- package/src/state/selectors/radialBarSelectors.ts +435 -0
- package/src/state/selectors/rootPropsSelectors.ts +25 -0
- package/src/state/selectors/scatterSelectors.ts +96 -0
- package/src/state/selectors/selectActivePropsFromChartPointer.ts +26 -0
- package/src/state/selectors/selectAllAxes.ts +17 -0
- package/src/state/selectors/selectChartOffset.ts +16 -0
- package/src/state/selectors/selectChartOffsetInternal.ts +136 -0
- package/src/state/selectors/selectLegendArea.ts +20 -0
- package/src/state/selectors/selectPlotArea.ts +22 -0
- package/src/state/selectors/selectTooltipAxisId.ts +7 -0
- package/src/state/selectors/selectTooltipAxisType.ts +32 -0
- package/src/state/selectors/selectTooltipEventType.ts +48 -0
- package/src/state/selectors/selectTooltipPayloadSearcher.ts +6 -0
- package/src/state/selectors/selectTooltipSettings.ts +39 -0
- package/src/state/selectors/selectTooltipState.ts +8 -0
- package/src/state/selectors/selectors.ts +271 -0
- package/src/state/selectors/tooltipSelectors.ts +578 -0
- package/src/state/selectors/touchSelectors.ts +31 -0
- package/src/state/store.ts +29 -0
- package/src/state/storeIdentity.ts +9 -0
- package/src/state/teardownWrite.ts +10 -0
- package/src/state/tooltipInteraction.ts +23 -0
- package/src/state/tooltipSlice.ts +334 -0
- package/src/state/touchEventsMiddleware.ts +214 -0
- package/src/state/types/AnimationSettings.ts +20 -0
- package/src/state/types/AreaSettings.ts +14 -0
- package/src/state/types/BarSettings.ts +15 -0
- package/src/state/types/LineSettings.ts +9 -0
- package/src/state/types/PieSettings.ts +34 -0
- package/src/state/types/RadarSettings.ts +5 -0
- package/src/state/types/RadialBarSettings.ts +10 -0
- package/src/state/types/ScatterSettings.ts +12 -0
- package/src/state/types/StackedGraphicalItem.ts +34 -0
- package/src/state/useChartState.ts +17 -0
- package/src/state/zIndexSlice.ts +39 -0
- package/src/synchronisation/syncSelectors.ts +7 -0
- package/src/synchronisation/types.ts +44 -0
- package/src/synchronisation/useChartSynchronisation.tsx +404 -0
- package/src/theme/RechartsTheme.ts +16 -0
- package/src/theme/RechartsThemeContext.ts +19 -0
- package/src/theme/darkTheme.ts +13 -0
- package/src/theme/graphicalItemIdentity.ts +70 -0
- package/src/theme/legacyTheme.ts +17 -0
- package/src/theme/lightTheme.ts +13 -0
- package/src/types.ts +54 -0
- package/src/util/ActiveShapeUtils.tsx +162 -0
- package/src/util/BarUtils.tsx +62 -0
- package/src/util/BarePortal.tsx +50 -0
- package/src/util/CamelCaseSVGAttrs.ts +34 -0
- package/src/util/CartesianUtils.ts +48 -0
- package/src/util/ChartUtils.ts +802 -0
- package/src/util/Constants.ts +42 -0
- package/src/util/CssPrefixUtils.ts +20 -0
- package/src/util/DOMUtils.ts +129 -0
- package/src/util/DataUtils.ts +216 -0
- package/src/util/Events.ts +16 -0
- package/src/util/FunnelUtils.tsx +15 -0
- package/src/util/Global.ts +12 -0
- package/src/util/IfOverflow.ts +15 -0
- package/src/util/LRUCache.ts +41 -0
- package/src/util/LogUtils.ts +22 -0
- package/src/util/PolarUtils.ts +132 -0
- package/src/util/RadialBarUtils.tsx +24 -0
- package/src/util/ReactUtils.ts +95 -0
- package/src/util/ReduceCSSCalc.ts +187 -0
- package/src/util/ScatterUtils.tsx +52 -0
- package/src/util/ShapeElementProps.tsx +87 -0
- package/src/util/TickUtils.ts +60 -0
- package/src/util/XAxisUtils.ts +48 -0
- package/src/util/YAxisUtils.ts +48 -0
- package/src/util/axisPropsAreEqual.ts +48 -0
- package/src/util/createCartesianCharts.tsx +150 -0
- package/src/util/createEventProxy.ts +20 -0
- package/src/util/createPolarCharts.tsx +112 -0
- package/src/util/cursor/getCursorPoints.ts +46 -0
- package/src/util/cursor/getCursorRectangle.ts +28 -0
- package/src/util/cursor/getRadialCursorPoints.ts +32 -0
- package/src/util/excludeEventProps.ts +171 -0
- package/src/util/getActiveCoordinate.ts +213 -0
- package/src/util/getAxisTypeBasedOnLayout.ts +21 -0
- package/src/util/getClassNameFromUnknown.ts +6 -0
- package/src/util/getEveryNth.ts +27 -0
- package/src/util/getRadiusAndStrokeWidthFromDot.ts +35 -0
- package/src/util/getRelativeCoordinate.ts +80 -0
- package/src/util/getSliced.ts +13 -0
- package/src/util/hoverDedupe.ts +41 -0
- package/src/util/isDomainSpecifiedByUser.ts +219 -0
- package/src/util/isWellBehavedNumber.ts +7 -0
- package/src/util/pathTotalLength.ts +25 -0
- package/src/util/payload/getUniqPayload.ts +28 -0
- package/src/util/propsAreEqual.ts +121 -0
- package/src/util/resolveDefaultProps.ts +161 -0
- package/src/util/round.ts +32 -0
- package/src/util/scale/CartesianScaleHelper.ts +72 -0
- package/src/util/scale/CustomScaleDefinition.ts +81 -0
- package/src/util/scale/RechartsScale.ts +130 -0
- package/src/util/scale/createCategoricalInverse.ts +72 -0
- package/src/util/scale/getNiceTickValues.ts +311 -0
- package/src/util/scale/index.ts +2 -0
- package/src/util/scale/util/arithmetic.ts +54 -0
- package/src/util/solid-1-compat.ts +310 -0
- package/src/util/solidJsxCamelAugment.d.ts +84 -0
- package/src/util/stacks/getStackSeriesIdentifier.ts +21 -0
- package/src/util/stacks/stackTypes.ts +60 -0
- package/src/util/structurallyEqual.ts +31 -0
- package/src/util/svgPropertiesAndEvents.ts +53 -0
- package/src/util/svgPropertiesNoEvents.ts +571 -0
- package/src/util/tooltip/translate.ts +171 -0
- package/src/util/typedDataKey.ts +48 -0
- package/src/util/types.ts +1560 -0
- package/src/util/useAnimationId.ts +31 -0
- package/src/util/useElementOffset.ts +110 -0
- package/src/util/useId.ts +8 -0
- package/src/util/usePrefersReducedMotion.ts +48 -0
- package/src/util/useReportScale.ts +24 -0
- package/src/util/useUniqueId.ts +30 -0
- package/src/zIndex/DefaultZIndexes.ts +84 -0
- package/src/zIndex/ZIndexLayer.tsx +121 -0
- package/src/zIndex/ZIndexPortal.tsx +79 -0
- package/src/zIndex/getZIndexFromUnknown.ts +14 -0
- package/src/zIndex/zIndexSelectors.ts +48 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* eslint-disable import/no-cycle, sort-keys */
|
|
2
|
+
import type { Component } from 'solid-js';
|
|
3
|
+
import type { ActiveDotType, DotType } from "./types"
|
|
4
|
+
|
|
5
|
+
export const SCALE_TYPES = [
|
|
6
|
+
"auto",
|
|
7
|
+
"linear",
|
|
8
|
+
"pow",
|
|
9
|
+
"sqrt",
|
|
10
|
+
"log",
|
|
11
|
+
"identity",
|
|
12
|
+
"time",
|
|
13
|
+
"band",
|
|
14
|
+
"point",
|
|
15
|
+
"ordinal",
|
|
16
|
+
"quantile",
|
|
17
|
+
"quantize",
|
|
18
|
+
"utc",
|
|
19
|
+
"sequential",
|
|
20
|
+
"threshold",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get the display name of a component.
|
|
25
|
+
* In Solid, components don't have displayName, so we fall back to .name.
|
|
26
|
+
* @param {Object} Comp Specified Component
|
|
27
|
+
* @return {String} Display name of Component
|
|
28
|
+
*/
|
|
29
|
+
export const getDisplayName = (Comp: Component | string) => {
|
|
30
|
+
if (typeof Comp === "string") {
|
|
31
|
+
return Comp
|
|
32
|
+
}
|
|
33
|
+
if (!Comp) {
|
|
34
|
+
return ""
|
|
35
|
+
}
|
|
36
|
+
return Comp.name || "Component"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const isClipDot = (dot: ActiveDotType | DotType): boolean => {
|
|
40
|
+
if (dot && typeof dot === "object" && "clipDot" in dot) {
|
|
41
|
+
return Boolean(dot.clipDot)
|
|
42
|
+
}
|
|
43
|
+
return true
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* React `<Foo />` builds a vNode (lazy descriptor) that `cloneElement` can
|
|
47
|
+
* re-instantiate per iteration with merged props. Solid `<Foo />` evaluates
|
|
48
|
+
* eagerly to a live DOM Node — function reference + original props are gone.
|
|
49
|
+
* The closest emulation: detect a Node value and clone it, optionally applying
|
|
50
|
+
* iteration-specific attrs. Spies inside the original component fire ONCE at
|
|
51
|
+
* JSX evaluation; only the rendered DOM count and final attribute values are
|
|
52
|
+
* recoverable. Documented under Cluster C / GOTCHA-016. */
|
|
53
|
+
export function isJsxNode(value: unknown): value is Node {
|
|
54
|
+
return value != null && typeof value === "object" && "cloneNode" in value
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const SVG_CAMEL_TO_KEBAB: Record<string, string> = {
|
|
58
|
+
tabindex: "tabindex",
|
|
59
|
+
readOnly: "readonly",
|
|
60
|
+
contentEditable: "contenteditable",
|
|
61
|
+
spellCheck: "spellcheck",
|
|
62
|
+
autoFocus: "autofocus",
|
|
63
|
+
clipPath: "clip-path",
|
|
64
|
+
clipRule: "clip-rule",
|
|
65
|
+
fillOpacity: "fill-opacity",
|
|
66
|
+
strokeWidth: "stroke-width",
|
|
67
|
+
strokeOpacity: "stroke-opacity",
|
|
68
|
+
strokeLinecap: "stroke-linecap",
|
|
69
|
+
strokeLinejoin: "stroke-linejoin",
|
|
70
|
+
strokeDasharray: "stroke-dasharray",
|
|
71
|
+
strokeDashoffset: "stroke-dashoffset",
|
|
72
|
+
textAnchor: "text-anchor",
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function attrNameFor(key: string): string {
|
|
76
|
+
return SVG_CAMEL_TO_KEBAB[key] ?? key
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* Apply iteration props onto a fresh clone of a user-supplied JSX Node.
|
|
80
|
+
* Skips function values, undefined, `style`, and the "key"/"children"/"className"/"class"
|
|
81
|
+
* meta-props; other objects are stringified like React does. Booleans serialize as "true"/"false" to match recharts upstream
|
|
82
|
+
* `cloneElement` behaviour where downstream `getAttribute` reads the string. */
|
|
83
|
+
export function cloneJsxNodeWithProps(node: Node, props: Record<string, unknown>): Node {
|
|
84
|
+
const cloned = node.cloneNode(true)
|
|
85
|
+
if (!(cloned instanceof Element)) return cloned
|
|
86
|
+
|
|
87
|
+
for (const key in props) {
|
|
88
|
+
if (key === "key" || key === "children" || key === "className" || key === "class") continue
|
|
89
|
+
const value = props[key]
|
|
90
|
+
if (value == null || typeof value === "function" || key === "style") continue
|
|
91
|
+
/* React stringifies object values on DOM attributes (payload -> "[object Object]", arrays -> "0,300") */
|
|
92
|
+
cloned.setAttribute(attrNameFor(key), String(value))
|
|
93
|
+
}
|
|
94
|
+
return cloned
|
|
95
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/* eslint-disable import/no-cycle */
|
|
2
|
+
import { isNan } from "./DataUtils"
|
|
3
|
+
|
|
4
|
+
const MULTIPLY_OR_DIVIDE_REGEX = /(-?\d+(?:\.\d+)?[a-zA-Z%]*)([*/])(-?\d+(?:\.\d+)?[a-zA-Z%]*)/
|
|
5
|
+
const ADD_OR_SUBTRACT_REGEX = /(-?\d+(?:\.\d+)?[a-zA-Z%]*)([+-])(-?\d+(?:\.\d+)?[a-zA-Z%]*)/
|
|
6
|
+
const CSS_LENGTH_UNIT_REGEX = /^(px|cm|vh|vw|em|rem|%|mm|in|pt|pc|ex|ch|vmin|vmax|Q)$/
|
|
7
|
+
const NUM_SPLIT_REGEX = /(-?\d+(?:\.\d+)?)([a-zA-Z%]+)?/
|
|
8
|
+
|
|
9
|
+
type SupportedUnits = "cm" | "mm" | "pt" | "pc" | "in" | "Q" | "px"
|
|
10
|
+
|
|
11
|
+
const CONVERSION_RATES: Record<SupportedUnits, number> = {
|
|
12
|
+
Q: 96 / (2.54 * 40),
|
|
13
|
+
cm: 96 / 2.54,
|
|
14
|
+
in: 96,
|
|
15
|
+
mm: 96 / 25.4,
|
|
16
|
+
pc: 96 / 6,
|
|
17
|
+
pt: 96 / 72,
|
|
18
|
+
px: 1,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const FIXED_CSS_LENGTH_UNITS: ReadonlySet<SupportedUnits> = new Set([
|
|
22
|
+
"cm",
|
|
23
|
+
"mm",
|
|
24
|
+
"pt",
|
|
25
|
+
"pc",
|
|
26
|
+
"in",
|
|
27
|
+
"Q",
|
|
28
|
+
"px",
|
|
29
|
+
])
|
|
30
|
+
|
|
31
|
+
function isSupportedUnit(unit: string): unit is SupportedUnits {
|
|
32
|
+
return FIXED_CSS_LENGTH_UNITS.has(unit as SupportedUnits)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const STR_NAN = "NaN"
|
|
36
|
+
|
|
37
|
+
function convertToPx(value: number, unit: SupportedUnits): number {
|
|
38
|
+
return value * CONVERSION_RATES[unit]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
class DecimalCSS {
|
|
42
|
+
static parse(str: string) {
|
|
43
|
+
const [, numStr, unit] = NUM_SPLIT_REGEX.exec(str) ?? []
|
|
44
|
+
|
|
45
|
+
if (numStr == null) {
|
|
46
|
+
return DecimalCSS.NaN
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return new DecimalCSS(parseFloat(numStr), unit ?? "")
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static NaN = new DecimalCSS(NaN, "")
|
|
53
|
+
|
|
54
|
+
constructor(
|
|
55
|
+
public num: number,
|
|
56
|
+
public unit: string,
|
|
57
|
+
) {
|
|
58
|
+
this.num = num
|
|
59
|
+
this.unit = unit
|
|
60
|
+
|
|
61
|
+
if (isNan(num)) {
|
|
62
|
+
this.unit = ""
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (unit !== "" && !CSS_LENGTH_UNIT_REGEX.test(unit)) {
|
|
66
|
+
this.num = NaN
|
|
67
|
+
this.unit = ""
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (isSupportedUnit(unit)) {
|
|
71
|
+
this.num = convertToPx(num, unit)
|
|
72
|
+
this.unit = "px"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
add(other: DecimalCSS) {
|
|
77
|
+
if (this.unit !== other.unit) {
|
|
78
|
+
return new DecimalCSS(NaN, "")
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return new DecimalCSS(this.num + other.num, this.unit)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
subtract(other: DecimalCSS) {
|
|
85
|
+
if (this.unit !== other.unit) {
|
|
86
|
+
return new DecimalCSS(NaN, "")
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return new DecimalCSS(this.num - other.num, this.unit)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
multiply(other: DecimalCSS) {
|
|
93
|
+
if (this.unit !== "" && other.unit !== "" && this.unit !== other.unit) {
|
|
94
|
+
return new DecimalCSS(NaN, "")
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return new DecimalCSS(this.num * other.num, this.unit || other.unit)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
divide(other: DecimalCSS) {
|
|
101
|
+
if (this.unit !== "" && other.unit !== "" && this.unit !== other.unit) {
|
|
102
|
+
return new DecimalCSS(NaN, "")
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return new DecimalCSS(this.num / other.num, this.unit || other.unit)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
toString() {
|
|
109
|
+
return `${this.num}${this.unit}`
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
isNaN() {
|
|
113
|
+
return isNan(this.num)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function calculateArithmetic(expr: string | undefined): string {
|
|
118
|
+
if (expr == null || expr.includes(STR_NAN)) {
|
|
119
|
+
return STR_NAN
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let newExpr = expr
|
|
123
|
+
while (newExpr.includes("*") || newExpr.includes("/")) {
|
|
124
|
+
const [, leftOperand, operator, rightOperand] = MULTIPLY_OR_DIVIDE_REGEX.exec(newExpr) ?? []
|
|
125
|
+
const lTs = DecimalCSS.parse(leftOperand ?? "")
|
|
126
|
+
const rTs = DecimalCSS.parse(rightOperand ?? "")
|
|
127
|
+
const result = operator === "*" ? lTs.multiply(rTs) : lTs.divide(rTs)
|
|
128
|
+
if (result.isNaN()) {
|
|
129
|
+
return STR_NAN
|
|
130
|
+
}
|
|
131
|
+
newExpr = newExpr.replace(MULTIPLY_OR_DIVIDE_REGEX, () => result.toString())
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
while (newExpr.includes("+") || /.-\d+(?:\.\d+)?/.test(newExpr)) {
|
|
135
|
+
const [, leftOperand, operator, rightOperand] = ADD_OR_SUBTRACT_REGEX.exec(newExpr) ?? []
|
|
136
|
+
const lTs = DecimalCSS.parse(leftOperand ?? "")
|
|
137
|
+
const rTs = DecimalCSS.parse(rightOperand ?? "")
|
|
138
|
+
const result = operator === "+" ? lTs.add(rTs) : lTs.subtract(rTs)
|
|
139
|
+
if (result.isNaN()) {
|
|
140
|
+
return STR_NAN
|
|
141
|
+
}
|
|
142
|
+
newExpr = newExpr.replace(ADD_OR_SUBTRACT_REGEX, () => result.toString())
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return newExpr
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const PARENTHESES_REGEX = /\(([^()]*)\)/
|
|
149
|
+
|
|
150
|
+
function calculateParentheses(expr: string): string {
|
|
151
|
+
let newExpr = expr
|
|
152
|
+
let match: ReturnType<typeof RegExp.prototype.exec> | null
|
|
153
|
+
while ((match = PARENTHESES_REGEX.exec(newExpr)) != null) {
|
|
154
|
+
const [, parentheticalExpression] = match
|
|
155
|
+
newExpr = newExpr.replace(PARENTHESES_REGEX, () =>
|
|
156
|
+
calculateArithmetic(parentheticalExpression),
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return newExpr
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function evaluateExpression(expression: string): string {
|
|
164
|
+
let newExpr = expression.replace(/\s+/g, "")
|
|
165
|
+
newExpr = calculateParentheses(newExpr)
|
|
166
|
+
newExpr = calculateArithmetic(newExpr)
|
|
167
|
+
|
|
168
|
+
return newExpr
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function safeEvaluateExpression(expression: string): string {
|
|
172
|
+
try {
|
|
173
|
+
return evaluateExpression(expression)
|
|
174
|
+
} catch {
|
|
175
|
+
return STR_NAN
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function reduceCSSCalc(expression: string): string {
|
|
180
|
+
const result = safeEvaluateExpression(expression.slice(5, -1))
|
|
181
|
+
|
|
182
|
+
if (result === STR_NAN) {
|
|
183
|
+
return ""
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return result
|
|
187
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/* eslint-disable import/no-cycle */
|
|
2
|
+
import type { JSX } from '@solidjs/web';
|
|
3
|
+
import { SymbolType } from "./types"
|
|
4
|
+
import type { ShapeAnimationProps } from "./types"
|
|
5
|
+
import { ScatterPointItem, ScatterCustomizedShape } from "../cartesian/Scatter"
|
|
6
|
+
import { Symbols } from "../shape/Symbols"
|
|
7
|
+
import { Shape } from "./ActiveShapeUtils"
|
|
8
|
+
import { DATA_ITEM_GRAPHICAL_ITEM_ID_ATTRIBUTE_NAME } from "./Constants"
|
|
9
|
+
import { GraphicalItemId } from "../state/graphicalItemsSlice"
|
|
10
|
+
|
|
11
|
+
export type ScatterShapeProps = ScatterPointItem &
|
|
12
|
+
ShapeAnimationProps & {
|
|
13
|
+
isActive?: boolean
|
|
14
|
+
index: number
|
|
15
|
+
[DATA_ITEM_GRAPHICAL_ITEM_ID_ATTRIBUTE_NAME]: GraphicalItemId
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function ScatterSymbol(
|
|
19
|
+
props: {
|
|
20
|
+
option: ScatterCustomizedShape
|
|
21
|
+
isActive: boolean
|
|
22
|
+
} & ScatterShapeProps,
|
|
23
|
+
): JSX.Element {
|
|
24
|
+
return <>{renderScatterSymbol(props)}</>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Runs inside ScatterSymbol's JSX expression so every reactive read is tracked. */
|
|
28
|
+
function renderScatterSymbol(
|
|
29
|
+
props: {
|
|
30
|
+
option: ScatterCustomizedShape
|
|
31
|
+
isActive: boolean
|
|
32
|
+
} & ScatterShapeProps,
|
|
33
|
+
): JSX.Element {
|
|
34
|
+
/* eslint-disable solid/reactivity -- option type and shapeProps are stable at mount; component re-renders via Solid's fine-grained reactivity on JSX reads below */
|
|
35
|
+
const { option: _option, isActive: _isActive, ...shapeProps } = props
|
|
36
|
+
if (typeof props.option === "string") {
|
|
37
|
+
const symbolType = props.option as SymbolType
|
|
38
|
+
return (
|
|
39
|
+
<Shape
|
|
40
|
+
option={<Symbols type={symbolType} {...shapeProps} />}
|
|
41
|
+
isActive={props.isActive}
|
|
42
|
+
shapeType="symbols"
|
|
43
|
+
{...shapeProps}
|
|
44
|
+
/>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<Shape option={props.option} isActive={props.isActive} shapeType="symbols" {...shapeProps} />
|
|
50
|
+
)
|
|
51
|
+
/* eslint-enable solid/reactivity */
|
|
52
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { createContext, untrack, useContext } from "solid-js"
|
|
2
|
+
import type { JSX } from "@solidjs/web"
|
|
3
|
+
import { mergeProps } from "./solid-1-compat"
|
|
4
|
+
import { cloneJsxNodeWithProps, isJsxNode } from "./ReactUtils"
|
|
5
|
+
import { camelizeSvgPropsForHandler } from "./svgPropertiesNoEvents"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Upstream renders a shape passed as an element with
|
|
9
|
+
* `cloneElement(option, { ...props, ...option.props })`. Solid evaluates `<Sector fill="red" />`
|
|
10
|
+
* eagerly, so there is no unrendered element to clone. Instead the caller provides the props it
|
|
11
|
+
* would inject, then reads the option lazily inside the provider: the built-in shape evaluated
|
|
12
|
+
* there merges them under its own explicit props.
|
|
13
|
+
*
|
|
14
|
+
* Each evaluation arms a one-shot token: only the first shape evaluated after arming takes it,
|
|
15
|
+
* so shapes nested inside that shape (e.g. the curves of AreaRevealShape) are unaffected.
|
|
16
|
+
*/
|
|
17
|
+
type ShapeElementPropsToken = { props: Record<string, unknown>; consumed: boolean }
|
|
18
|
+
|
|
19
|
+
export type ShapeElementPropsSlot = { token: ShapeElementPropsToken | null }
|
|
20
|
+
|
|
21
|
+
const ShapeElementPropsContext = createContext<ShapeElementPropsSlot | null>(null)
|
|
22
|
+
|
|
23
|
+
export function createShapeElementPropsSlot(): ShapeElementPropsSlot {
|
|
24
|
+
return { token: null }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Arm the slot with the props for the next shape evaluated inside the provider. */
|
|
28
|
+
export function armShapeElementProps(slot: ShapeElementPropsSlot, props: Record<string, unknown>): ShapeElementPropsToken {
|
|
29
|
+
const token = { consumed: false, props }
|
|
30
|
+
slot.token = token
|
|
31
|
+
return token
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Stop offering the injected props to shapes evaluated from now on. */
|
|
35
|
+
export function disarmShapeElementProps(slot: ShapeElementPropsSlot): void {
|
|
36
|
+
slot.token = null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function ShapeElementPropsProvider(props: { slot: ShapeElementPropsSlot; children: JSX.Element }) {
|
|
40
|
+
/* eslint-disable-next-line solid/reactivity -- the slot is a stable mutable container */
|
|
41
|
+
return <ShapeElementPropsContext value={props.slot}>{props.children}</ShapeElementPropsContext>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Merge props injected by an enclosing ShapeElementPropsProvider under the shape's own props.
|
|
46
|
+
* Own props win, like upstream's `{ ...props, ...option.props }`.
|
|
47
|
+
*/
|
|
48
|
+
export function useShapeElementProps<P extends object>(own: P): P {
|
|
49
|
+
const slot = useContext(ShapeElementPropsContext)
|
|
50
|
+
const token = slot?.token
|
|
51
|
+
if (token == null || token.consumed) {
|
|
52
|
+
return own
|
|
53
|
+
}
|
|
54
|
+
token.consumed = true
|
|
55
|
+
return untrack(() => mergeProps(token.props, own)) as unknown as P
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Renders a `shape`-style option the way upstream does for element / function / default:
|
|
60
|
+
* - an element: built-in shapes take `shapeProps` while evaluated; other elements are cloned
|
|
61
|
+
* with them applied (Solid cannot clone an unrendered element);
|
|
62
|
+
* - a function: called with the props in upstream's camelCase spelling;
|
|
63
|
+
* - anything else: `renderDefault(shapeProps)`.
|
|
64
|
+
*/
|
|
65
|
+
export function ShapeOption(props: {
|
|
66
|
+
option: unknown
|
|
67
|
+
shapeProps: Record<string, unknown>
|
|
68
|
+
renderDefault: (shapeProps: Record<string, unknown>) => JSX.Element
|
|
69
|
+
}) {
|
|
70
|
+
const slot = createShapeElementPropsSlot()
|
|
71
|
+
const render = (): JSX.Element => {
|
|
72
|
+
const token = armShapeElementProps(slot, props.shapeProps)
|
|
73
|
+
/* read once: an element option mints a new node per read */
|
|
74
|
+
const option = props.option
|
|
75
|
+
disarmShapeElementProps(slot)
|
|
76
|
+
if (isJsxNode(option)) {
|
|
77
|
+
return token.consumed
|
|
78
|
+
? (option as unknown as JSX.Element)
|
|
79
|
+
: (cloneJsxNodeWithProps(option, props.shapeProps) as unknown as JSX.Element)
|
|
80
|
+
}
|
|
81
|
+
if (typeof option === "function") {
|
|
82
|
+
return (option as (p: Record<string, unknown>) => JSX.Element)(camelizeSvgPropsForHandler(props.shapeProps))
|
|
83
|
+
}
|
|
84
|
+
return props.renderDefault(props.shapeProps)
|
|
85
|
+
}
|
|
86
|
+
return <ShapeElementPropsProvider slot={slot}>{render()}</ShapeElementPropsProvider>
|
|
87
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/* eslint-disable import/no-cycle */
|
|
2
|
+
import { getAngledRectangleWidth } from "./CartesianUtils"
|
|
3
|
+
import { getEveryNth } from "./getEveryNth"
|
|
4
|
+
import { Size, CartesianTickItem, CartesianViewBoxRequired } from "./types"
|
|
5
|
+
|
|
6
|
+
export function getAngledTickWidth(contentSize: Size, unitSize: Size, angle: number | undefined) {
|
|
7
|
+
const size = {
|
|
8
|
+
height: contentSize.height + unitSize.height,
|
|
9
|
+
width: contentSize.width + unitSize.width,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return getAngledRectangleWidth(size, angle)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getTickBoundaries(
|
|
16
|
+
viewBox: CartesianViewBoxRequired,
|
|
17
|
+
sign: number,
|
|
18
|
+
sizeKey: string,
|
|
19
|
+
) {
|
|
20
|
+
const isWidth = sizeKey === "width"
|
|
21
|
+
const { x, y, width, height } = viewBox
|
|
22
|
+
if (sign === 1) {
|
|
23
|
+
return {
|
|
24
|
+
end: isWidth ? x + width : y + height,
|
|
25
|
+
start: isWidth ? x : y,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
end: isWidth ? x : y,
|
|
30
|
+
start: isWidth ? x + width : y + height,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function isVisible(
|
|
35
|
+
sign: number,
|
|
36
|
+
tickPosition: number,
|
|
37
|
+
getSize: () => number,
|
|
38
|
+
start: number,
|
|
39
|
+
end: number,
|
|
40
|
+
): boolean {
|
|
41
|
+
/* Since getSize() is expensive (it reads the ticks' size from the DOM), we do this check first to avoid calculating
|
|
42
|
+
* the tick's size. */
|
|
43
|
+
if (sign * tickPosition < sign * start || sign * tickPosition > sign * end) {
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const size = getSize()
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
sign * (tickPosition - (sign * size) / 2 - start) >= 0 &&
|
|
51
|
+
sign * (tickPosition + (sign * size) / 2 - end) <= 0
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function getNumberIntervalTicks(
|
|
56
|
+
ticks: ReadonlyArray<CartesianTickItem>,
|
|
57
|
+
interval: number,
|
|
58
|
+
): ReadonlyArray<CartesianTickItem> | undefined {
|
|
59
|
+
return getEveryNth(ticks, interval + 1)
|
|
60
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
type IGetBoundingClient = Pick<Element, "getBoundingClientRect">
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Calculates the height of the X-axis based on the tick labels and the axis label.
|
|
5
|
+
* @param params - The parameters object.
|
|
6
|
+
* @param [params.ticks] - An array-like object of tick elements, each with a `getBoundingClientRect` method.
|
|
7
|
+
* @param [params.label] - The axis label element, with a `getBoundingClientRect` method.
|
|
8
|
+
* @param [params.labelGapWithTick=5] - The gap between the label and the tick.
|
|
9
|
+
* @param [params.tickSize=0] - The length of the tick line.
|
|
10
|
+
* @param [params.tickMargin=0] - The margin between the tick line and the tick text.
|
|
11
|
+
* @returns The calculated height of the X-axis.
|
|
12
|
+
*/
|
|
13
|
+
export const getCalculatedXAxisHeight = ({
|
|
14
|
+
ticks,
|
|
15
|
+
label,
|
|
16
|
+
labelGapWithTick = 5,
|
|
17
|
+
tickSize = 0,
|
|
18
|
+
tickMargin = 0,
|
|
19
|
+
}: {
|
|
20
|
+
ticks: ArrayLike<IGetBoundingClient> | null
|
|
21
|
+
label: IGetBoundingClient | null | undefined
|
|
22
|
+
labelGapWithTick: number | undefined
|
|
23
|
+
tickSize: number | undefined
|
|
24
|
+
tickMargin: number | undefined
|
|
25
|
+
}): number => {
|
|
26
|
+
let maxTickHeight = 0
|
|
27
|
+
if (ticks) {
|
|
28
|
+
Array.from(ticks).forEach((tickNode: IGetBoundingClient) => {
|
|
29
|
+
if (tickNode) {
|
|
30
|
+
const bbox = tickNode.getBoundingClientRect()
|
|
31
|
+
|
|
32
|
+
if (bbox.height > maxTickHeight) {
|
|
33
|
+
maxTickHeight = bbox.height
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const labelHeight = label ? label.getBoundingClientRect().height : 0
|
|
39
|
+
|
|
40
|
+
const tickHeight = tickSize + tickMargin
|
|
41
|
+
|
|
42
|
+
const updatedXAxisHeight = maxTickHeight + tickHeight + labelHeight + (label ? labelGapWithTick : 0)
|
|
43
|
+
|
|
44
|
+
return Math.round(updatedXAxisHeight)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return 0
|
|
48
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
type IGetBoundingClient = Pick<Element, "getBoundingClientRect">
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Calculates the width of the Y-axis based on the tick labels and the axis label.
|
|
5
|
+
* @param params - The parameters object.
|
|
6
|
+
* @param [params.ticks] - An array-like object of tick elements, each with a `getBoundingClientRect` method.
|
|
7
|
+
* @param [params.label] - The axis label element, with a `getBoundingClientRect` method.
|
|
8
|
+
* @param [params.labelGapWithTick=5] - The gap between the label and the tick.
|
|
9
|
+
* @param [params.tickSize=0] - The length of the tick line.
|
|
10
|
+
* @param [params.tickMargin=0] - The margin between the tick line and the tick text.
|
|
11
|
+
* @returns The calculated width of the Y-axis.
|
|
12
|
+
*/
|
|
13
|
+
export const getCalculatedYAxisWidth = ({
|
|
14
|
+
ticks,
|
|
15
|
+
label,
|
|
16
|
+
labelGapWithTick = 5,
|
|
17
|
+
tickSize = 0,
|
|
18
|
+
tickMargin = 0,
|
|
19
|
+
}: {
|
|
20
|
+
ticks: ArrayLike<IGetBoundingClient> | null
|
|
21
|
+
label: IGetBoundingClient | null | undefined
|
|
22
|
+
labelGapWithTick: number | undefined
|
|
23
|
+
tickSize: number | undefined
|
|
24
|
+
tickMargin: number | undefined
|
|
25
|
+
}): number => {
|
|
26
|
+
let maxTickWidth = 0
|
|
27
|
+
if (ticks) {
|
|
28
|
+
Array.from(ticks).forEach((tickNode: IGetBoundingClient) => {
|
|
29
|
+
if (tickNode) {
|
|
30
|
+
const bbox = tickNode.getBoundingClientRect()
|
|
31
|
+
|
|
32
|
+
if (bbox.width > maxTickWidth) {
|
|
33
|
+
maxTickWidth = bbox.width
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const labelWidth = label ? label.getBoundingClientRect().width : 0
|
|
39
|
+
|
|
40
|
+
const tickWidth = tickSize + tickMargin
|
|
41
|
+
|
|
42
|
+
const updatedYAxisWidth = maxTickWidth + tickWidth + labelWidth + (label ? labelGapWithTick : 0)
|
|
43
|
+
|
|
44
|
+
return Math.round(updatedYAxisWidth)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return 0
|
|
48
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { XAxisProps, YAxisProps } from "../index"
|
|
2
|
+
import { AxisDomain } from "./types"
|
|
3
|
+
import { AxisRange } from "../state/selectors/axisSelectors"
|
|
4
|
+
import { propsAreEqual } from "./propsAreEqual"
|
|
5
|
+
|
|
6
|
+
function shortArraysAreEqual(
|
|
7
|
+
arr1: AxisDomain | AxisRange | undefined,
|
|
8
|
+
arr2: AxisDomain | AxisRange | undefined,
|
|
9
|
+
): boolean {
|
|
10
|
+
if (arr1 === arr2) {
|
|
11
|
+
return true
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (Array.isArray(arr1) && arr1.length === 2 && Array.isArray(arr2) && arr2.length === 2) {
|
|
15
|
+
return arr1[0] === arr2[0] && arr1[1] === arr2[1]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return false
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Usually we would not compare array props deeply for performance consideration.
|
|
23
|
+
* However, for axis props, domain is sometimes defined as a two-elements array, and range is always
|
|
24
|
+
* a two-elements array. So we can do a shallow comparison for the rest props and a shallow
|
|
25
|
+
* comparison for these two array props.
|
|
26
|
+
* @param prevProps
|
|
27
|
+
* @param nextProps
|
|
28
|
+
*/
|
|
29
|
+
export function axisPropsAreEqual<T extends YAxisProps | XAxisProps>(
|
|
30
|
+
prevProps: T,
|
|
31
|
+
nextProps: T,
|
|
32
|
+
): boolean {
|
|
33
|
+
if (prevProps === nextProps) {
|
|
34
|
+
return true
|
|
35
|
+
}
|
|
36
|
+
const { domain: prevDomain, range: prevRange, ...prevRest } = prevProps
|
|
37
|
+
const { domain: nextDomain, range: nextRange, ...nextRest } = nextProps
|
|
38
|
+
|
|
39
|
+
if (!shortArraysAreEqual(prevDomain, nextDomain)) {
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!shortArraysAreEqual(prevRange, nextRange)) {
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return propsAreEqual(prevRest, nextRest)
|
|
48
|
+
}
|