UNPKG

@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
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