gemma-menu
Version:
A pie/radial menu component for React
76 lines (66 loc) • 6.57 kB
JavaScript
import React, { useState } from 'react';
function styleInject(css, ref) {
if ( ref === void 0 ) ref = {};
var insertAt = ref.insertAt;
if (!css || typeof document === 'undefined') { return; }
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
if (insertAt === 'top') {
if (head.firstChild) {
head.insertBefore(style, head.firstChild);
} else {
head.appendChild(style);
}
} else {
head.appendChild(style);
}
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
}
var css_248z = "[data-pie-menu] {\n position: fixed;\n left: var(--pie-x, 0);\n top: var(--pie-y, 0);\n margin: 0;\n -webkit-font-smoothing: antialiased;\n box-sizing: border-box;\n pointer-events: auto;\n width: var(--radius-outer, 300px);\n height: var(--radius-outer, 300px);\n border-radius: 100%;\n z-index: 5;\n touch-action: none;\n user-select: none;\n opacity: 1;\n transform: translate(-50%, -50%);\n animation: appear 250ms both;\n box-shadow: 1px 0.9px 2.3px -3px rgba(20, 10, 0, 0.02),\n 2.4px 2.2px 5.5px -3px rgba(20, 10, 0, 0.025),\n 4.5px 4.1px 10.4px -3px rgba(20, 10, 0, 0.03),\n 8px 7.4px 18.5px -3px rgba(20, 10, 0, 0.035),\n 15px 13.8px 34.7px -3px rgba(20, 0, 10, 0.04),\n 36px 33px 83px -3px rgba(20, 10, 0, 0.1);\n}\n\n[data-pie-menu] > ul {\n pointer-events: auto;\n -webkit-font-smoothing: antialiased;\n padding: 0;\n list-style: none;\n margin: 0;\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n border-radius: inherit;\n overflow: hidden;\n position: relative;\n user-select: none;\n}\n\n[data-pie-kind='bubble'] {\n box-shadow: none;\n}\n[data-pie-kind='bubble'] [data-pie-item-content] {\n transition: 0.3s;\n}\n\n[data-pie-kind='bubble'] [data-pie-item] {\n background: none !important;\n}\n\n[data-pie-kind='bubble'] [data-pie-item-active] > * > * {\n transition: 250ms;\n}\n\n[data-pie-kind='bubble'] [data-pie-item-active]:hover > * > * {\n scale: 1.2;\n}\n\n[data-pie-label] {\n opacity: 1;\n font-weight: bold;\n position: absolute;\n height: var(--radius-inner, 30%);\n width: var(--radius-inner, 30%);\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n border-radius: inherit;\n background-color: white;\n z-index: 10;\n pointer-events: none;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);\n}\n\n[data-pie-item] {\n --pie-item-rotation: calc(var(--pie-item-angle) * var(--pie-item-index));\n --pie-item-skew: calc(90deg - var(--pie-item-angle));\n transform: rotate(var(--pie-item-rotation)) skew(var(--pie-item-skew));\n pointer-events: auto;\n list-style: none;\n user-select: none;\n position: absolute;\n bottom: 50%;\n right: 50%;\n width: 100%;\n height: 100%;\n transform-origin: 100% 100%;\n overflow: hidden;\n transition: background 250ms ease 0s;\n background-color: var(--bg-color);\n}\n\n[data-pie-item-content] {\n position: absolute;\n height: var(--pie-item-length);\n bottom: 0px;\n right: 0px;\n display: flex;\n align-items: center;\n justify-content: center;\n transform-origin: bottom right;\n transform: skew(calc(var(--pie-item-skew) * -1))\n rotate(calc(-90deg + var(--pie-item-angle) / 2))\n translate(50%, calc(var(--pie-item-offset) * -1));\n}\n\n[data-pie-item-content] > * {\n position: absolute;\n transform: rotate(calc(var(--pie-item-rotation) * -1))\n rotate(calc(90deg + var(--pie-item-angle) / 2 * -1));\n}\n\n[data-pie-item-active='true'] {\n background-color: var(--active-color) !important;\n}\n\n@keyframes appear {\n from {\n opacity: 0;\n transform: translate(-50%, -50%) scale(0.8);\n }\n to {\n opacity: 1;\n transform: translate(-50%, -50%) scale(1);\n }\n}\n";
styleInject(css_248z);
const Menu = (props) => {
var _a;
const { isOpen = false, children, position, kind = 'wheel', outerRadius = 300, innerRadius = 100, backgroundColor, activeColor } = props;
const [activeItem, setActiveItem] = useState(null);
const angle = 360 / children.length;
const handleSetActive = (e) => {
const target = e.target;
if (target.hasAttribute('data-pie-item')) {
const index = parseInt(target.getAttribute('data-pie-item-index') || '0');
setActiveItem(index);
}
};
return (React.createElement(React.Fragment, null, isOpen && (React.createElement("div", { "data-pie-menu": true, "data-pie-kind": kind, style: {
'--pie-x': (position === null || position === void 0 ? void 0 : position.x) + 'px',
'--pie-y': (position === null || position === void 0 ? void 0 : position.y) + 'px',
'--radius-outer': outerRadius + 'px'
} },
React.createElement("div", { "data-pie-label": true, style: { '--radius-inner': innerRadius + 'px' } }, activeItem !== null && ((_a = children[activeItem]) === null || _a === void 0 ? void 0 : _a.props.label)),
React.createElement("ul", { onMouseMove: (e) => handleSetActive(e), role: "menu", "aria-label": "radial menu" }, children === null || children === void 0 ? void 0 : children.map((child, index) => React.cloneElement(child, {
index,
angle,
isActive: index === activeItem,
length: (outerRadius - innerRadius) / 2,
offset: innerRadius / 2,
backgroundColor: backgroundColor || child.props.backgroundColor || '#fafafa',
activeColor: activeColor || child.props.activeColor || '#efefef'
})))))));
};
const MenuItem = (props) => {
const { children, label, action, close, index, isActive, angle, length, offset, backgroundColor, activeColor } = props;
return (React.createElement("li", { "data-pie-item": true, "data-pie-item-index": index, "data-pie-item-active": isActive, style: {
'--pie-item-index': index,
'--pie-item-angle': angle + 'deg',
'--pie-item-length': length + 'px',
'--pie-item-offset': offset + 'px',
'--bg-color': backgroundColor,
'--active-color': activeColor
}, role: "menuitem", "aria-label": label, onMouseEnter: action, onMouseUp: close, onClick: close, tabIndex: index !== undefined ? index + 1 : undefined },
React.createElement("div", { "data-pie-item-content": true }, children)));
};
export { Menu, MenuItem };