@busychild/react-planet
Version:
A react lib for building circular menus in a very easy and handy way.
282 lines (272 loc) • 13.2 kB
JavaScript
import * as React from 'react';
import useResizeObserver from 'use-resize-observer';
import { useSpring, animated } from 'react-spring';
import { useDrag } from 'react-use-gesture';
import { makeStyles } from '@mui/styles';
import { ClickAwayListener } from '@mui/material';
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var DEFAULT_DRAG_RADIUS = 12;
var DEFAULT_BOUNCE_RADIUS = 3;
function DragableContainer(props) {
var children = props.children, on = props.on, dragable = props.dragable, dragRadius = props.dragRadius, bounceRadius = props.bounceRadius, open = props.open, bounceOnOpen = props.bounceOnOpen, bounceOnClose = props.bounceOnClose, bounceDirection = props.bounceDirection;
if (on) {
var _a = useSpring(function () { return ({
x: 0,
y: 0,
config: { tension: 400, friction: 7, precision: 0.1 },
cursor: "pointer",
}); }), _b = _a[0], x = _b.x, y = _b.y, cursor = _b.cursor, set_1 = _a[1];
var classes = useStyles$3(props);
React.useEffect(function () {
if ((open && bounceOnOpen) || (!open && bounceOnClose)) {
var bRadius = bounceRadius ? bounceRadius : DEFAULT_BOUNCE_RADIUS;
var x_1 = bRadius;
var y_1 = bRadius;
switch (bounceDirection) {
case "LEFT":
x_1 = -bRadius;
y_1 = 0;
break;
case "RIGHT":
x_1 = bRadius;
y_1 = 0;
break;
case "TOP":
x_1 = 0;
y_1 = -bRadius;
break;
case "BOTTOM":
x_1 = 0;
y_1 = bRadius;
break;
}
set_1({ x: x_1, y: y_1 });
setTimeout(function () { return set_1({ x: 0, y: 0 }); }, 100);
}
}, [open]);
// Creates a drag gesture
var bind = useDrag(function (_a) {
var down = _a.down, _b = _a.movement, dX = _b[0], dY = _b[1];
if (dragable) {
var rMax = dragRadius ? dragRadius : DEFAULT_DRAG_RADIUS;
var r = Math.sqrt(Math.pow(dX, 2) + Math.pow(dY, 2));
// Find point along radius with rMax length
// See: https://math.stackexchange.com/q/1630886
if (r > rMax) {
dX *= rMax / r;
dY *= rMax / r;
}
set_1({
x: down ? dX : 0,
y: down ? dY : 0,
immediate: down,
cursor: down ? "grabbing" : "pointer",
});
}
});
return (React.createElement("div", { className: classes.root },
React.createElement(animated.div, __assign({}, bind(), { className: classes.dragable, style: { cursor: cursor, left: x, top: y } }), children)));
}
else {
return React.createElement(React.Fragment, null, children);
}
}
var useStyles$3 = makeStyles({
root: {
top: 0,
left: 0,
},
dragable: {
position: "absolute",
},
});
// prettier-ignore
function Orbit(props) {
var orbitRadius = props.orbitRadius, planetWidth = props.planetWidth, planetHeight = props.planetHeight, open = props.open, tension = props.tension, friction = props.friction, mass = props.mass;
var classes = useStyles$2(props);
var position = useSpring({
reverse: !open,
from: getInitalOrbitPosition(planetWidth, planetHeight),
to: getFinalOrbitPosition(planetWidth, planetHeight, orbitRadius),
config: { mass: mass, tension: tension, friction: friction },
});
return React.createElement(animated.div, { className: classes.orbit, style: position });
}
function getInitalOrbitPosition(planetWidth, planetHeight) {
return {
width: 0,
height: 0,
top: planetWidth / 2,
left: planetHeight / 2,
opacity: 0,
};
}
function getFinalOrbitPosition(planetWidth, planetHeight, orbitRadius) {
return {
width: orbitRadius * 2,
height: orbitRadius * 2,
top: 0 - orbitRadius + planetHeight / 2,
left: 0 - orbitRadius + planetWidth / 2,
opacity: 1,
};
}
var orbitDefaultStyle = {
position: 'absolute',
borderRadius: '100%',
borderWidth: 2,
borderStyle: 'dotted',
borderColor: 'lightgrey',
zIndex: 0,
};
var useStyles$2 = makeStyles({
orbit: function (props) {
return props.orbitStyle ? props.orbitStyle(orbitDefaultStyle) : orbitDefaultStyle;
},
});
function Satellite(props) {
var children = props.children, index = props.index, satelliteCount = props.satelliteCount, open = props.open, planetWidth = props.planetWidth, planetHeight = props.planetHeight, tension = props.tension, friction = props.friction, mass = props.mass, orbitRadius = props.orbitRadius, rotation = props.rotation, dragable = props.dragable, dragRadius = props.dragRadius, orientation = props.orientation;
var classes = useStyles$1(props);
var _a = useResizeObserver(), ref = _a.ref, _b = _a.height, height = _b === void 0 ? 0 : _b, _c = _a.width, width = _c === void 0 ? 0 : _c;
var position = useSpring({
reverse: !open,
from: getInitalSatellitePosition(width, height, planetWidth, planetHeight),
to: getFinalSatellitePosition(index, satelliteCount, width, height, planetWidth, planetHeight, orbitRadius, rotation, orientation),
config: { mass: mass, tension: tension, friction: friction },
});
return (React.createElement(animated.div, { className: classes.root, style: position },
React.createElement(DragableContainer, { on: dragable, dragRadius: dragRadius, dragable: dragable },
React.createElement("div", { ref: ref }, children))));
}
function getFinalSatellitePosition(index, satelliteCount, width, height, planetWidth, planetHeight, orbitRadius, rotation, orientation) {
var _a = getFinalDeltaPositions(index, satelliteCount, width, height, orbitRadius, rotation), deltaX = _a.deltaX, deltaY = _a.deltaY, angle = _a.angle;
var transform = {};
switch (orientation) {
case "OUTSIDE":
transform = { transform: "rotate(" + angle + "deg)" };
break;
case "INSIDE":
transform = { transform: "rotate(" + (angle + 180) + "deg)" };
break;
case "READABLE":
transform =
angle > 90 && angle < 270
? { transform: "rotate(" + (angle + 180) + "deg)" }
: { transform: "rotate(" + angle + "deg)" };
break;
default:
transform = { transform: "rotate(" + 0 + "deg)" };
}
return __assign({ top: planetHeight / 2 + deltaX, left: planetWidth / 2 - deltaY, opacity: 1 }, transform);
}
function getInitalSatellitePosition(width, height, planetWidth, planetHeight) {
return {
top: planetHeight / 2 - height / 2,
left: planetWidth / 2 - width / 2,
opacity: 0,
};
}
function getFinalDeltaPositions(index, satelliteCount, width, height, orbitRadius, rotation) {
var SEPARATION_ANGLE = 360 / satelliteCount;
var FAN_ANGLE = (satelliteCount - 1) * SEPARATION_ANGLE;
var BASE_ANGLE = (180 - FAN_ANGLE) / 2 + 90 + rotation;
var TARGET_ANGLE = BASE_ANGLE + index * SEPARATION_ANGLE;
return {
deltaX: orbitRadius * Math.cos(toRadians(TARGET_ANGLE)) - height / 2,
deltaY: orbitRadius * Math.sin(toRadians(TARGET_ANGLE)) + width / 2,
angle: TARGET_ANGLE,
};
}
var useStyles$1 = makeStyles({
root: function (props) { return ({
position: "absolute",
zIndex: props.open ? 2 : 0,
}); },
});
// UTILITY FUNCTIONS
var DEG_TO_RAD = 0.0174533;
function toRadians(degrees) {
return degrees * DEG_TO_RAD;
}
// prettier-ignore
var DEFAULT_MASS = 1;
var DEFAULT_TENSTION = 500;
var DEFAULT_FRICTION = 17;
var DEFAULT_ROTATION = 0;
var DEFAULT_RADIUS = 100;
function Planet(props) {
var centerContent = props.centerContent, children = props.children, open = props.open, onClick = props.onClick, mass = props.mass, tension = props.tension, friction = props.friction, orbitRadius = props.orbitRadius, rotation = props.rotation, orbitStyle = props.orbitStyle, hideOrbit = props.hideOrbit, onClose = props.onClose, autoClose = props.autoClose, dragablePlanet = props.dragablePlanet, dragRadiusPlanet = props.dragRadiusPlanet, dragableSatellites = props.dragableSatellites, dragRadiusSatellites = props.dragRadiusSatellites, bounceRadius = props.bounceRadius, bounce = props.bounce, bounceOnOpen = props.bounceOnOpen, bounceOnClose = props.bounceOnClose, bounceDirection = props.bounceDirection, satelliteOrientation = props.satelliteOrientation;
var classes = useStyles(props);
var _a = useResizeObserver(), ref = _a.ref, _b = _a.height, height = _b === void 0 ? 0 : _b, _c = _a.width, width = _c === void 0 ? 0 : _c;
var _d = React.useState(!!open), _open = _d[0], setOpen = _d[1];
React.useEffect(function () {
setOpen(!!open);
}, [open]);
var satellites = [];
var satelliteCount = React.Children.count(children);
React.Children.forEach(children, function (c, i) {
satellites[i] = (React.createElement(Satellite, { key: i, index: i, open: _open, satelliteCount: satelliteCount, planetHeight: height, planetWidth: width, mass: mass ? mass : DEFAULT_MASS, friction: friction ? friction : DEFAULT_FRICTION, tension: tension ? tension : DEFAULT_TENSTION, orbitRadius: orbitRadius ? orbitRadius : DEFAULT_RADIUS, rotation: rotation ? rotation : DEFAULT_ROTATION, dragable: !!dragableSatellites, dragRadius: dragRadiusSatellites, orientation: satelliteOrientation }, c));
});
var onPlanet = function (e) {
if (onClick) {
onClick(e);
}
else {
if (_open && autoClose) {
setOpen(false);
if (onClose) {
onClose(e);
}
}
else {
setOpen(true);
}
}
};
var onClickAway = function (e) {
if (autoClose) {
setOpen(false);
}
if (onClose && _open) {
onClose(e);
}
};
return (React.createElement(ClickAwayListener, { onClickAway: onClickAway },
React.createElement("div", { className: classes.root },
!hideOrbit && (React.createElement(Orbit, { open: _open, orbitStyle: orbitStyle, planetHeight: height, planetWidth: width, mass: mass ? mass : DEFAULT_MASS, friction: friction ? friction : DEFAULT_FRICTION, tension: tension ? tension : DEFAULT_TENSTION, orbitRadius: orbitRadius ? orbitRadius : DEFAULT_RADIUS })),
React.createElement(React.Fragment, null, satellites),
React.createElement("div", { className: classes.planetContent, onClick: onPlanet },
React.createElement(DragableContainer, { on: !!dragablePlanet || !!bounce || !!bounceOnOpen || !!bounceOnClose, dragable: !!dragablePlanet, dragRadius: dragRadiusPlanet, open: _open, bounceRadius: bounceRadius, bounceOnOpen: (bounce && !!!bounceOnClose) || bounceOnOpen, bounceOnClose: (bounce && !!!bounceOnOpen) || bounceOnClose, bounceDirection: bounceDirection },
React.createElement("div", { ref: ref }, centerContent))))));
}
var useStyles = makeStyles({
root: {
position: "relative",
},
planetContent: {
position: "absolute",
zIndex: 1,
},
});
export { Planet };
//# sourceMappingURL=index.es.js.map