Skip to content

Commit cd11aa4

Browse files
committed
Cleaned up Props and added 3px of padding to container
1 parent bfc6cac commit cd11aa4

File tree

1 file changed

+127
-87
lines changed

1 file changed

+127
-87
lines changed

react-graph/src/Infobox.tsx

Lines changed: 127 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -24,96 +24,136 @@
2424
import * as React from 'react';
2525
import { AxisIdentifier, AxisMap, GraphContext, IHandlers } from './GraphContext';
2626

27+
export type origin = ("upper-right" | "upper-left" | "upper-center" | "lower-right" | "lower-left" | "lower-center")
28+
2729
interface IProps {
28-
// Specifies the upper left corner of the box (or other spots depending on origin)
29-
x: number,
30-
y: number,
31-
usePixelPositioning?: boolean,
32-
disallowSnapping?: boolean,
33-
axis?: AxisIdentifier,
34-
origin?: "upper-right" | "upper-left" | "upper-center" | "lower-right" | "lower-left" | "lower-center",
35-
// Specifies the offset of the pox from the origin point, In pixels
36-
offset?: number,
37-
// Dom ID of child, used for sizing of child
38-
childId: string,
39-
opacity?: number,
40-
// Function to move box
41-
setPosition?: (x: number, y: number) => void,
42-
onMouseMove?: (x: number, y: number) => void
30+
/**
31+
* Specifies the X coordinate of the upper left corner of the box (or other spots depending on origin).
32+
*/
33+
X: number,
34+
/**
35+
* Specifies the Y coordinate of the upper left corner of the box (or other spots depending on origin).
36+
*/
37+
Y: number,
38+
/**
39+
* Determines if the positioning should be based on pixel values.
40+
*/
41+
UsePixelPositioning?: boolean,
42+
/**
43+
* If true, snapping of the box to grid or other elements is disallowed.
44+
*/
45+
DisallowSnapping?: boolean,
46+
/**
47+
* Identifier for the axis the box is associated with.
48+
*/
49+
Axis?: AxisIdentifier,
50+
/**
51+
* Specifies the origin point for positioning the box.
52+
*/
53+
Origin?: origin,
54+
/**
55+
* Specifies the offset of the box from the origin point, in pixels.
56+
*/
57+
Offset?: number,
58+
/**
59+
* DOM ID of the child element, used for sizing the child.
60+
*/
61+
ChildID: string,
62+
/**
63+
* Opacity of the box.
64+
*/
65+
Opacity?: number,
66+
/**
67+
* Function to set the position of the box.
68+
*/
69+
SetPosition?: (x: number, y: number) => void,
70+
/**
71+
* Event handler for mouse move events.
72+
*/
73+
OnMouseMove?: (x: number, y: number) => void,
74+
}
75+
76+
interface IGraphicProps {
77+
X: number,
78+
Y: number,
79+
Width: number,
80+
Height: number,
81+
Opacity?: number
4382
}
4483

84+
const offsetDefault = 0;
85+
const widthPadding = 3;
86+
4587
const Infobox: React.FunctionComponent<IProps> = (props) => {
4688
const context = React.useContext(GraphContext);
4789
const [isSelected, setSelected] = React.useState<boolean>(false);
48-
const [position, setPosition] = React.useState<{x: number, y: number}>({x: props.x, y: props.y});
49-
const [dimension, setDimensions] = React.useState<{width: number, height: number}>({width: 100, height: 100});
90+
const [position, setPosition] = React.useState<{ x: number, y: number }>({ x: props.X, y: props.Y });
91+
const [size, setSize] = React.useState<{ width: number, height: number }>({ width: 100, height: 100 });
5092
const [guid, setGuid] = React.useState<string>("");
51-
const offsetDefault = 0;
52-
93+
5394
// Functions
5495
const calculateX = React.useCallback((xArg: number) => {
55-
let x: number = (props.usePixelPositioning ?? false) ? context.XApplyPixelOffset(xArg) : context.XTransformation(xArg);
96+
let x: number = (props.UsePixelPositioning ?? false) ? context.XApplyPixelOffset(xArg) : context.XTransformation(xArg);
5697
// Convert x/y to upper-left corner
57-
switch(props.origin) {
98+
switch (props.Origin) {
5899
case "lower-right":
59100
case "upper-right": {
60-
x -= (dimension.width + (props.offset ?? offsetDefault));
101+
x -= (size.width + (props.Offset ?? offsetDefault));
61102
break;
62103
}
63104
case "lower-center":
64105
case "upper-center": {
65-
x -= Math.floor(dimension.width / 2);
106+
x -= Math.floor(size.width / 2);
66107
break;
67108
}
68109
// Do-nothing case
69-
case undefined:
110+
case undefined:
70111
case "lower-left":
71112
case "upper-left":
72-
x += props.offset ?? offsetDefault;
113+
x += props.Offset ?? offsetDefault;
73114
break;
74115
}
75116
return x;
76-
}, [context.XApplyPixelOffset, context.XTransformation, props.origin, props.offset, props.usePixelPositioning, dimension]);
77-
117+
}, [context.XApplyPixelOffset, context.XTransformation, props.Origin, props.Offset, props.UsePixelPositioning, size]);
118+
78119
const calculateY = React.useCallback((yArg: number) => {
79-
let y: number = (props.usePixelPositioning ?? false) ? context.YApplyPixelOffset(yArg) : context.YTransformation(yArg, AxisMap.get(props.axis));
120+
let y: number = (props.UsePixelPositioning ?? false) ? context.YApplyPixelOffset(yArg) : context.YTransformation(yArg, AxisMap.get(props.Axis));
80121
// Convert x/y to upper-left corner
81-
switch(props.origin) {
82-
case undefined:
122+
switch (props.Origin) {
123+
case undefined:
83124
case "upper-left":
84125
case "upper-right":
85126
case "upper-center":
86-
y += props.offset ?? offsetDefault;
127+
y += props.Offset ?? offsetDefault;
87128
break;
88129
case "lower-left":
89130
case "lower-right":
90131
case "lower-center":
91-
y -= (dimension.height + (props.offset ?? offsetDefault));
132+
y -= (size.height + (props.Offset ?? offsetDefault));
92133
break;
93134
}
94135
return y;
95-
}, [context.YApplyPixelOffset, context.YTransformation, props.origin, props.offset, props.usePixelPositioning, props.axis, dimension]);
96-
136+
}, [context.YApplyPixelOffset, context.YTransformation, props.Origin, props.Offset, props.UsePixelPositioning, props.Axis, size]);
137+
97138
const onClick = React.useCallback((xArg: number, yArg: number) => {
98-
const xP = calculateX(props.x);
139+
const xP = calculateX(props.X);
99140
const xT = context.XTransformation(xArg);
100-
const yP = calculateY(props.y);
101-
const yT = context.YTransformation(yArg, AxisMap.get(props.axis));
102-
if (xT <= xP + dimension.width && xT >= xP && yT <= yP + dimension.height && yT >= yP) {
141+
const yP = calculateY(props.Y);
142+
const yT = context.YTransformation(yArg, AxisMap.get(props.Axis));
143+
if (xT <= xP + size.width && xT >= xP && yT <= yP + size.height && yT >= yP) {
103144
setSelected(true);
104145
}
105-
}, [props.x, props.y, calculateX, calculateY, dimension, setSelected, context.XTransformation, context.YTransformation, props.axis]);
106-
107-
// Note: this is the only function not effected by usePixelPositioning
108-
const onMove = props.onMouseMove === undefined ? undefined : React.useCallback((xArg: number, yArg: number) => {
109-
if (props.onMouseMove !== undefined) props.onMouseMove(xArg, yArg);
110-
}, [props.onMouseMove]);
146+
}, [props.X, props.Y, calculateX, calculateY, size, setSelected, context.XTransformation, context.YTransformation, props.Axis]);
111147

148+
// Note: this is the only function not effected by usePixelPositioning
149+
const onMove = props.OnMouseMove === undefined ? undefined : React.useCallback((xArg: number, yArg: number) => {
150+
if (props.OnMouseMove !== undefined) props.OnMouseMove(xArg, yArg);
151+
}, [props.OnMouseMove]);
112152

113153
// useEffect
114154
React.useEffect(() => {
115155
const id = context.RegisterSelect({
116-
axis: props.axis,
156+
axis: props.Axis,
117157
allowSnapping: false,
118158
onRelease: (_) => setSelected(false),
119159
onPlotLeave: (_) => setSelected(false),
@@ -123,82 +163,82 @@ const Infobox: React.FunctionComponent<IProps> = (props) => {
123163
setGuid(id)
124164
return () => { context.RemoveSelect(id) }
125165
}, []);
126-
166+
127167
React.useEffect(() => {
128168
if (guid === "")
129169
return;
130-
170+
131171
context.UpdateSelect(guid, {
132-
axis: props.axis,
172+
axis: props.Axis,
133173
allowSnapping: false,
134174
onRelease: (_) => setSelected(false),
135175
onPlotLeave: (_) => setSelected(false),
136176
onClick,
137177
onMove
138178
} as IHandlers)
139-
}, [onClick, onMove, props.axis]);
140-
179+
}, [onClick, onMove, props.Axis]);
180+
141181
React.useEffect(() => {
142-
setPosition({x: props.x, y: props.y});
143-
}, [props.x, props.y]);
182+
setPosition({ x: props.X, y: props.Y });
183+
}, [props.X, props.Y]);
144184

145185
React.useEffect(() => {
146-
if (props.setPosition === undefined)
186+
if (props.SetPosition === undefined)
147187
return;
148-
if (!isSelected && (props.x !== position.x || props.y !== position.y))
149-
props.setPosition(position.x, position.y);
188+
if (!isSelected && (props.X !== position.x || props.Y !== position.y))
189+
props.SetPosition(position.x, position.y);
150190
}, [isSelected, position]);
151-
191+
152192
React.useEffect(() => {
153193
if (context.CurrentMode !== 'select')
154194
setSelected(false);
155-
},[context.CurrentMode]);
156-
195+
}, [context.CurrentMode]);
196+
157197
React.useEffect(() => {
158-
if (isSelected && !(props.disallowSnapping ?? false))
159-
setPosition({x: context.XHoverSnap, y: context.YHoverSnap[AxisMap.get(props.axis)]});
160-
}, [context.XHoverSnap, context.YHoverSnap, props.axis]);
161-
198+
if (isSelected && !(props.DisallowSnapping ?? false))
199+
setPosition({ x: context.XHoverSnap, y: context.YHoverSnap[AxisMap.get(props.Axis)] });
200+
}, [context.XHoverSnap, context.YHoverSnap, props.Axis]);
201+
162202
React.useEffect(() => {
163-
if (isSelected && (props.disallowSnapping ?? false))
164-
setPosition({x: context.XHover, y: context.YHover[AxisMap.get(props.axis)]});
165-
}, [context.XHover, context.YHover, props.axis]);
166-
203+
if (isSelected && (props.DisallowSnapping ?? false))
204+
setPosition({ x: context.XHover, y: context.YHover[AxisMap.get(props.Axis)] });
205+
}, [context.XHover, context.YHover, props.Axis]);
206+
167207
// Get Heights and Widths
168208
React.useEffect(() => {
169-
const domEle = document.getElementById(props.childId);
170-
if (domEle == null) {
171-
console.error(`Invalid element id passed for child element in infobox ${props.childId}`);
172-
setDimensions({width: 100, height: 100});
173-
return;
174-
}
175-
if (dimension.width === Math.ceil(domEle.clientWidth) && dimension.height === Math.ceil(domEle.clientHeight)) return;
176-
setDimensions({width: Math.ceil(domEle.clientWidth), height: Math.ceil(domEle.clientHeight)});
177-
}, [props.children, props.childId]);
209+
const newSize = getSize(props.ChildID);
210+
if (newSize.width - widthPadding !== size.width || newSize.height !== size.height)
211+
setSize(newSize);
212+
213+
}, [props.children, props.ChildID]);
178214

179215
return (
180216
<g>
181-
<InfoGraphic x={calculateX(props.x)} y={calculateY(props.y)} width={dimension.width} height={dimension.height} opacity={props.opacity} />
182-
<foreignObject x={calculateX(props.x)} y={calculateY(props.y)} width={dimension.width} height={dimension.height}>
217+
<InfoGraphic X={calculateX(props.X)} Y={calculateY(props.Y)} Width={size.width} Height={size.height} Opacity={props.Opacity} />
218+
<foreignObject x={calculateX(props.X)} y={calculateY(props.Y)} width={size.width} height={size.height} >
183219
{props.children}
184220
</foreignObject>
185-
{props.setPosition !== undefined && (props.x !== position.x || props.y !== position.y) ?
186-
<InfoGraphic x={calculateX(position.x)} y={calculateY(position.y)} width={dimension.width} height={dimension.height} opacity={props.opacity} />
221+
{props.SetPosition !== undefined && (props.X !== position.x || props.Y !== position.y) ?
222+
<InfoGraphic X={calculateX(position.x)} Y={calculateY(position.y)} Width={size.width} Height={size.height} Opacity={props.Opacity} />
187223
: null}
188224
</g>);
189225
}
190226

191-
interface IGraphicProps {
192-
x: number,
193-
y: number,
194-
width: number,
195-
height: number,
196-
opacity?: number
197-
}
198227
const InfoGraphic: React.FunctionComponent<IGraphicProps> = (props) => {
199228
return (
200-
<path d={`M ${props.x} ${props.y} h ${props.width} v ${props.height} h -${props.width} v -${props.height}`} stroke={'black'} style={{opacity: props.opacity ?? 1}} />
229+
<path d={`M ${props.X} ${props.Y} h ${props.Width} v ${props.Height} h -${props.Width} v -${props.Height}`} stroke={'black'} style={{ opacity: props.Opacity ?? 1 }} />
201230
);
202231
}
203232

233+
//helper functions
234+
const getSize = (childID: string): { width: number, height: number } => {
235+
const childElement = document.getElementById(childID);
236+
if (childElement == null) {
237+
console.error(`Invalid element id passed for child element in infobox ${childID}`);
238+
return { width: 100, height: 100 };
239+
}
240+
const width = Math.ceil(childElement.clientWidth) + widthPadding
241+
const height = Math.ceil(childElement.clientHeight)
242+
return { width, height }
243+
}
204244
export default Infobox;

0 commit comments

Comments
 (0)