|
1 | 1 | import React from "react";
|
2 | 2 | import {
|
3 |
| - VictoryContainer, |
4 | 3 | Selection,
|
5 | 4 | Rect,
|
6 | 5 | DomainTuple,
|
7 | 6 | VictoryContainerProps,
|
| 7 | + VictoryContainer, |
| 8 | + VictoryEventHandler, |
8 | 9 | } from "victory-core";
|
9 | 10 | import { BrushHelpers } from "./brush-helpers";
|
10 | 11 | import { defaults } from "lodash";
|
@@ -37,186 +38,188 @@ export interface VictoryBrushContainerProps extends VictoryContainerProps {
|
37 | 38 | ) => void;
|
38 | 39 | }
|
39 | 40 |
|
40 |
| -type ComponentClass<TProps> = { new (props: TProps): React.Component<TProps> }; |
| 41 | +interface VictoryBrushContainerMutatedProps extends VictoryBrushContainerProps { |
| 42 | + domain: { x: DomainTuple; y: DomainTuple }; |
| 43 | + currentDomain: { x: DomainTuple; y: DomainTuple } | undefined; |
| 44 | + cachedBrushDomain: { x: DomainTuple; y: DomainTuple } | undefined; |
| 45 | +} |
41 | 46 |
|
42 |
| -export function brushContainerMixin< |
43 |
| - TBase extends ComponentClass<TProps>, |
44 |
| - TProps extends VictoryBrushContainerProps, |
45 |
| ->(Base: TBase) { |
46 |
| - // @ts-expect-error "TS2545: A mixin class must have a constructor with a single rest parameter of type 'any[]'." |
47 |
| - return class VictoryBrushContainer extends Base { |
48 |
| - static displayName = "VictoryBrushContainer"; |
49 |
| - static defaultProps = { |
50 |
| - ...VictoryContainer.defaultProps, |
51 |
| - allowDrag: true, |
52 |
| - allowDraw: true, |
53 |
| - allowResize: true, |
54 |
| - brushComponent: <Rect />, |
55 |
| - brushStyle: { |
56 |
| - stroke: "transparent", |
57 |
| - fill: "black", |
58 |
| - fillOpacity: 0.1, |
59 |
| - }, |
60 |
| - handleComponent: <Rect />, |
61 |
| - handleStyle: { |
62 |
| - stroke: "transparent", |
63 |
| - fill: "transparent", |
64 |
| - }, |
65 |
| - handleWidth: 8, |
66 |
| - mouseMoveThreshold: 0, |
67 |
| - }; |
| 47 | +export const VICTORY_BRUSH_CONTAINER_DEFAULT_PROPS = { |
| 48 | + allowDrag: true, |
| 49 | + allowDraw: true, |
| 50 | + allowResize: true, |
| 51 | + brushComponent: <Rect />, |
| 52 | + brushStyle: { |
| 53 | + stroke: "transparent", |
| 54 | + fill: "black", |
| 55 | + fillOpacity: 0.1, |
| 56 | + }, |
| 57 | + handleComponent: <Rect />, |
| 58 | + handleStyle: { |
| 59 | + stroke: "transparent", |
| 60 | + fill: "transparent", |
| 61 | + }, |
| 62 | + handleWidth: 8, |
| 63 | + mouseMoveThreshold: 0, |
| 64 | +}; |
68 | 65 |
|
69 |
| - static defaultEvents(props) { |
70 |
| - return [ |
71 |
| - { |
72 |
| - target: "parent", |
73 |
| - eventHandlers: { |
74 |
| - onMouseDown: (evt, targetProps) => { |
75 |
| - return props.disable |
76 |
| - ? {} |
77 |
| - : BrushHelpers.onMouseDown(evt, targetProps); |
78 |
| - }, |
79 |
| - onTouchStart: (evt, targetProps) => { |
80 |
| - return props.disable |
81 |
| - ? {} |
82 |
| - : BrushHelpers.onMouseDown(evt, targetProps); |
83 |
| - }, |
84 |
| - onGlobalMouseMove: (evt, targetProps) => { |
85 |
| - return props.disable || |
86 |
| - (!targetProps.isPanning && !targetProps.isSelecting) |
87 |
| - ? {} |
88 |
| - : BrushHelpers.onGlobalMouseMove(evt, targetProps); |
89 |
| - }, |
90 |
| - onGlobalTouchMove: (evt, targetProps) => { |
91 |
| - return props.disable || |
92 |
| - (!targetProps.isPanning && !targetProps.isSelecting) |
93 |
| - ? {} |
94 |
| - : BrushHelpers.onGlobalMouseMove(evt, targetProps); |
95 |
| - }, |
96 |
| - onGlobalMouseUp: (evt, targetProps) => { |
97 |
| - return props.disable |
98 |
| - ? {} |
99 |
| - : BrushHelpers.onGlobalMouseUp(evt, targetProps); |
100 |
| - }, |
101 |
| - onGlobalTouchEnd: (evt, targetProps) => { |
102 |
| - return props.disable |
103 |
| - ? {} |
104 |
| - : BrushHelpers.onGlobalMouseUp(evt, targetProps); |
105 |
| - }, |
106 |
| - onGlobalTouchCancel: (evt, targetProps) => { |
107 |
| - return props.disable |
108 |
| - ? {} |
109 |
| - : BrushHelpers.onGlobalMouseUp(evt, targetProps); |
110 |
| - }, |
111 |
| - }, |
112 |
| - }, |
113 |
| - ]; |
114 |
| - } |
| 66 | +export const useVictoryBrushContainer = ( |
| 67 | + initialProps: VictoryBrushContainerProps, |
| 68 | +) => { |
| 69 | + const props = { |
| 70 | + ...VICTORY_BRUSH_CONTAINER_DEFAULT_PROPS, |
| 71 | + ...(initialProps as VictoryBrushContainerMutatedProps), |
| 72 | + }; |
| 73 | + const { children } = props; |
115 | 74 |
|
116 |
| - getSelectBox(props, coordinates) { |
117 |
| - const { x, y } = coordinates; |
118 |
| - const { brushStyle, brushComponent, name } = props; |
119 |
| - const brushComponentStyle = |
120 |
| - brushComponent.props && brushComponent.props.style; |
121 |
| - const cursor = !props.allowDrag && !props.allowResize ? "auto" : "move"; |
122 |
| - return x[0] !== x[1] && y[0] !== y[1] |
123 |
| - ? React.cloneElement(brushComponent, { |
124 |
| - key: `${name}-brush`, |
125 |
| - width: Math.abs(x[1] - x[0]) || 1, |
126 |
| - height: Math.abs(y[1] - y[0]) || 1, |
127 |
| - x: Math.min(x[0], x[1]), |
128 |
| - y: Math.min(y[0], y[1]), |
129 |
| - cursor, |
130 |
| - style: defaults({}, brushComponentStyle, brushStyle), |
131 |
| - }) |
132 |
| - : null; |
133 |
| - } |
| 75 | + const getSelectBox = (coordinates) => { |
| 76 | + const { x, y } = coordinates; |
| 77 | + const { brushStyle, brushComponent, name } = props; |
| 78 | + const brushComponentStyle = |
| 79 | + brushComponent.props && brushComponent.props.style; |
| 80 | + const cursor = !props.allowDrag && !props.allowResize ? "auto" : "move"; |
| 81 | + return x[0] !== x[1] && y[0] !== y[1] |
| 82 | + ? React.cloneElement(brushComponent, { |
| 83 | + key: `${name}-brush`, |
| 84 | + width: Math.abs(x[1] - x[0]) || 1, |
| 85 | + height: Math.abs(y[1] - y[0]) || 1, |
| 86 | + x: Math.min(x[0], x[1]), |
| 87 | + y: Math.min(y[0], y[1]), |
| 88 | + cursor, |
| 89 | + style: defaults({}, brushComponentStyle, brushStyle), |
| 90 | + }) |
| 91 | + : null; |
| 92 | + }; |
134 | 93 |
|
135 |
| - getCursorPointers(props) { |
136 |
| - const cursors = { |
137 |
| - yProps: "ns-resize", |
138 |
| - xProps: "ew-resize", |
139 |
| - }; |
140 |
| - if (!props.allowResize && props.allowDrag) { |
141 |
| - cursors.xProps = "move"; |
142 |
| - cursors.yProps = "move"; |
143 |
| - } else if (!props.allowResize && !props.allowDrag) { |
144 |
| - cursors.xProps = "auto"; |
145 |
| - cursors.yProps = "auto"; |
146 |
| - } |
147 |
| - return cursors; |
| 94 | + const getCursorPointers = () => { |
| 95 | + const cursors = { |
| 96 | + yProps: "ns-resize", |
| 97 | + xProps: "ew-resize", |
| 98 | + }; |
| 99 | + if (!props.allowResize && props.allowDrag) { |
| 100 | + cursors.xProps = "move"; |
| 101 | + cursors.yProps = "move"; |
| 102 | + } else if (!props.allowResize && !props.allowDrag) { |
| 103 | + cursors.xProps = "auto"; |
| 104 | + cursors.yProps = "auto"; |
148 | 105 | }
|
| 106 | + return cursors; |
| 107 | + }; |
149 | 108 |
|
150 |
| - getHandles(props, domain) { |
151 |
| - const { handleWidth, handleStyle, handleComponent, name } = props; |
152 |
| - const domainBox = BrushHelpers.getDomainBox(props, domain); |
153 |
| - const { x1, x2, y1, y2 } = domainBox; |
154 |
| - const { top, bottom, left, right } = BrushHelpers.getHandles( |
155 |
| - props, |
156 |
| - domainBox, |
157 |
| - ); |
158 |
| - const width = Math.abs(x2 - x1) || 1; |
159 |
| - const height = Math.abs(y2 - y1) || 1; |
160 |
| - const handleComponentStyle = |
161 |
| - (handleComponent.props && handleComponent.props.style) || {}; |
162 |
| - const style = defaults({}, handleComponentStyle, handleStyle); |
| 109 | + const getHandles = (domain) => { |
| 110 | + const { handleWidth, handleStyle, handleComponent, name } = props; |
| 111 | + const domainBox = BrushHelpers.getDomainBox(props, domain); |
| 112 | + const { x1, x2, y1, y2 } = domainBox; |
| 113 | + const { top, bottom, left, right } = BrushHelpers.getHandles( |
| 114 | + props, |
| 115 | + domainBox, |
| 116 | + ); |
| 117 | + const width = Math.abs(x2 - x1) || 1; |
| 118 | + const height = Math.abs(y2 - y1) || 1; |
| 119 | + const handleComponentStyle = |
| 120 | + (handleComponent.props && handleComponent.props.style) || {}; |
| 121 | + const style = defaults({}, handleComponentStyle, handleStyle); |
163 | 122 |
|
164 |
| - const cursors = this.getCursorPointers(props); |
165 |
| - const yProps = { |
166 |
| - style, |
167 |
| - width, |
168 |
| - height: handleWidth, |
169 |
| - cursor: cursors.yProps, |
170 |
| - }; |
171 |
| - const xProps = { |
172 |
| - style, |
173 |
| - width: handleWidth, |
174 |
| - height, |
175 |
| - cursor: cursors.xProps, |
176 |
| - }; |
| 123 | + const cursors = getCursorPointers(); |
| 124 | + const yProps = { |
| 125 | + style, |
| 126 | + width, |
| 127 | + height: handleWidth, |
| 128 | + cursor: cursors.yProps, |
| 129 | + }; |
| 130 | + const xProps = { |
| 131 | + style, |
| 132 | + width: handleWidth, |
| 133 | + height, |
| 134 | + cursor: cursors.xProps, |
| 135 | + }; |
177 | 136 |
|
178 |
| - const handleProps = { |
179 |
| - top: top && Object.assign({ x: top.x1, y: top.y1 }, yProps), |
180 |
| - bottom: bottom && Object.assign({ x: bottom.x1, y: bottom.y1 }, yProps), |
181 |
| - left: left && Object.assign({ y: left.y1, x: left.x1 }, xProps), |
182 |
| - right: right && Object.assign({ y: right.y1, x: right.x1 }, xProps), |
183 |
| - }; |
184 |
| - const handles = ["top", "bottom", "left", "right"].reduce( |
185 |
| - (memo, curr) => |
186 |
| - handleProps[curr] |
187 |
| - ? memo.concat( |
188 |
| - React.cloneElement( |
189 |
| - handleComponent, |
190 |
| - Object.assign( |
191 |
| - { key: `${name}-handle-${curr}` }, |
192 |
| - handleProps[curr], |
193 |
| - ), |
| 137 | + const handleProps = { |
| 138 | + top: top && Object.assign({ x: top.x1, y: top.y1 }, yProps), |
| 139 | + bottom: bottom && Object.assign({ x: bottom.x1, y: bottom.y1 }, yProps), |
| 140 | + left: left && Object.assign({ y: left.y1, x: left.x1 }, xProps), |
| 141 | + right: right && Object.assign({ y: right.y1, x: right.x1 }, xProps), |
| 142 | + }; |
| 143 | + const handles = ["top", "bottom", "left", "right"].reduce( |
| 144 | + (memo, curr) => |
| 145 | + handleProps[curr] |
| 146 | + ? memo.concat( |
| 147 | + React.cloneElement( |
| 148 | + handleComponent, |
| 149 | + Object.assign( |
| 150 | + { key: `${name}-handle-${curr}` }, |
| 151 | + handleProps[curr], |
194 | 152 | ),
|
195 |
| - ) |
196 |
| - : memo, |
197 |
| - [] as React.ReactElement[], |
198 |
| - ); |
199 |
| - return handles.length ? handles : null; |
200 |
| - } |
| 153 | + ), |
| 154 | + ) |
| 155 | + : memo, |
| 156 | + [] as React.ReactElement[], |
| 157 | + ); |
| 158 | + return handles.length ? handles : null; |
| 159 | + }; |
201 | 160 |
|
202 |
| - getRect(props) { |
203 |
| - const { currentDomain, cachedBrushDomain } = props; |
204 |
| - const brushDomain = defaults({}, props.brushDomain, props.domain); |
205 |
| - const domain = isEqual(brushDomain, cachedBrushDomain) |
206 |
| - ? defaults({}, currentDomain, brushDomain) |
207 |
| - : brushDomain; |
208 |
| - const coordinates = Selection.getDomainCoordinates(props, domain); |
209 |
| - const selectBox = this.getSelectBox(props, coordinates); |
210 |
| - return selectBox ? [selectBox, this.getHandles(props, domain)] : []; |
211 |
| - } |
| 161 | + const getRect = () => { |
| 162 | + const { currentDomain, cachedBrushDomain } = props; |
| 163 | + const brushDomain = defaults({}, props.brushDomain, props.domain); |
| 164 | + const domain = isEqual(brushDomain, cachedBrushDomain) |
| 165 | + ? defaults({}, currentDomain, brushDomain) |
| 166 | + : brushDomain; |
| 167 | + const coordinates = Selection.getDomainCoordinates(props, domain); |
| 168 | + const selectBox = getSelectBox(coordinates); |
| 169 | + return selectBox ? [selectBox, getHandles(domain)] : []; |
| 170 | + }; |
212 | 171 |
|
213 |
| - // Overrides method in VictoryContainer |
214 |
| - getChildren(props) { |
215 |
| - return [ |
216 |
| - ...React.Children.toArray(props.children), |
217 |
| - ...this.getRect(props), |
218 |
| - ]; |
219 |
| - } |
| 172 | + return { |
| 173 | + props, |
| 174 | + children: [ |
| 175 | + ...React.Children.toArray(children), |
| 176 | + ...getRect(), |
| 177 | + ] as React.ReactElement[], |
220 | 178 | };
|
221 |
| -} |
222 |
| -export const VictoryBrushContainer = brushContainerMixin(VictoryContainer); |
| 179 | +}; |
| 180 | + |
| 181 | +export const VictoryBrushContainer = ( |
| 182 | + initialProps: VictoryBrushContainerProps, |
| 183 | +) => { |
| 184 | + const { props, children } = useVictoryBrushContainer(initialProps); |
| 185 | + return <VictoryContainer {...props}>{children}</VictoryContainer>; |
| 186 | +}; |
| 187 | + |
| 188 | +VictoryBrushContainer.role = "container"; |
| 189 | + |
| 190 | +VictoryBrushContainer.defaultEvents = ( |
| 191 | + initialProps: VictoryBrushContainerProps, |
| 192 | +) => { |
| 193 | + const props = { ...VICTORY_BRUSH_CONTAINER_DEFAULT_PROPS, ...initialProps }; |
| 194 | + const createEventHandler = |
| 195 | + ( |
| 196 | + handler: VictoryEventHandler, |
| 197 | + isDisabled?: (targetProps: any) => boolean, |
| 198 | + ): VictoryEventHandler => |
| 199 | + // eslint-disable-next-line max-params |
| 200 | + (event, targetProps, eventKey, context) => |
| 201 | + props.disable || isDisabled?.(targetProps) |
| 202 | + ? {} |
| 203 | + : handler(event, { ...props, ...targetProps }, eventKey, context); |
| 204 | + |
| 205 | + return [ |
| 206 | + { |
| 207 | + target: "parent", |
| 208 | + eventHandlers: { |
| 209 | + onMouseDown: createEventHandler(BrushHelpers.onMouseDown), |
| 210 | + onTouchStart: createEventHandler(BrushHelpers.onMouseDown), |
| 211 | + onGlobalMouseMove: createEventHandler( |
| 212 | + BrushHelpers.onGlobalMouseMove, |
| 213 | + (targetProps) => !targetProps.isPanning && !targetProps.isSelecting, |
| 214 | + ), |
| 215 | + onGlobalTouchMove: createEventHandler( |
| 216 | + BrushHelpers.onGlobalMouseMove, |
| 217 | + (targetProps) => !targetProps.isPanning && !targetProps.isSelecting, |
| 218 | + ), |
| 219 | + onGlobalMouseUp: createEventHandler(BrushHelpers.onGlobalMouseUp), |
| 220 | + onGlobalTouchEnd: createEventHandler(BrushHelpers.onGlobalMouseUp), |
| 221 | + onGlobalTouchCancel: createEventHandler(BrushHelpers.onGlobalMouseUp), |
| 222 | + }, |
| 223 | + }, |
| 224 | + ]; |
| 225 | +}; |
0 commit comments