UNPKG

@evg-b/evg-ui

Version:

EVG-UI library inspired by Material Design.

347 lines (308 loc) 11.3 kB
import _extends from '@babel/runtime/helpers/extends'; import _slicedToArray from '@babel/runtime/helpers/slicedToArray'; import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties'; import _defineProperty from '@babel/runtime/helpers/defineProperty'; import React, { useRef, useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import withStyles from '../styles/withStyles.js'; import Color from '../styles/Color/Color.js'; import TouchDriver from '../TouchDriver/TouchDriver.js'; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } var ArrayStates = { 'white': { 'hover': 0.04, 'focus': 0.12, 'selected': 0.08, 'activated': 0.12, 'pressed': 0.12, 'dragged': 0.08 }, 'gray': { 'hover': 0.12, 'focus': 0.36, 'selected': 0.24, 'activated': 0.36, 'pressed': 0.48, 'dragged': 0.32 }, 'color': { 'hover': 0.08, 'focus': 0.24, 'selected': 0.16, 'activated': 0.24, 'pressed': 0.32, 'dragged': 0.16 } }; var OverlayColor = function OverlayColor(color) { var contrast = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; return contrast ? Color(color).Contrast() : Color(color).Base(); }; var OpacityFromColor = function OpacityFromColor(color, effect) { var opacity; switch (color) { case '#000000': opacity = ArrayStates['white'][effect]; break; case '#FFFFFF': opacity = ArrayStates['gray'][effect]; break; default: opacity = ArrayStates['color'][effect]; break; } return opacity; }; var absolutePosition = { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }; var styles = { base: _objectSpread(_objectSpread({}, absolutePosition), {}, { overflow: 'hidden', outline: 'none', '&:before': { content: '""', pointerEvents: 'none', position: 'absolute', top: 0, left: 0, opacity: 0, width: 'var(--evg-ripple-size,100%)', height: 'var(--evg-ripple-size,100%)', borderRadius: '50%', backgroundColor: function backgroundColor(props) { return OverlayColor(props.color, props.contrast); }, transformOrigin: 'center center', transform: 'scale(1)', willChange: 'transform,opacity' }, '&:after': _objectSpread({ content: '""', pointerEvents: 'none', position: 'absolute', opacity: 0, width: '100%', height: '100%', backgroundColor: function backgroundColor(props) { return OverlayColor(props.color, props.contrast); } }, absolutePosition), '&:hover:after': { opacity: function opacity(props) { return OpacityFromColor(OverlayColor(props.color, props.contrast), 'hover'); } } }), rippleFocus: { '&:after,&:hover:after': { opacity: function opacity(props) { return OpacityFromColor(OverlayColor(props.color, props.contrast), 'focus'); } } }, rippleActive: { '&:after,&:hover:after': { opacity: function opacity(props) { return OpacityFromColor(OverlayColor(props.color, props.contrast), 'activated'); } } }, rippleStart: { '&:before': { animation: "$startRipple 225ms both" } }, rippleEnd: { '&:before': { animation: "$startRipple 225ms both,$endRipple 225ms linear both 50ms" } }, '@keyframes startRipple': { '0%': { animationTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)', opacity: 0, transform: 'translate(var(--evg-ripple-coord-start)) scale(.2,.2)' }, '50%': { opacity: 'var(--evg-ripple-opacity)' }, '100%': { opacity: 'var(--evg-ripple-opacity)', transform: 'translate(var(--evg-ripple-coord-end)) scale(1,1)' } }, '@keyframes endRipple': { '0%': { opacity: 'var(--evg-ripple-opacity)' }, '100%': { opacity: 0 } } }; /** * Ripple сигнализирует пользователю что его взаимодействие с компонентом было получено, используя эффект ряби в точке касания. * Так же помогает понять с каким именно компонентом произошло взаимодействие если они расположены слишком близко друг к другу. */ var Ripple = /*#__PURE__*/React.forwardRef(function Ripple(props, ref) { var _classNames; var classes = props.classes; props.className; props.children; var _props$component = props.component, Component = _props$component === void 0 ? 'span' : _props$component, _props$rippleCenter = props.rippleCenter, rippleCenter = _props$rippleCenter === void 0 ? false : _props$rippleCenter, _props$isFocus = props.isFocus, isFocus = _props$isFocus === void 0 ? false : _props$isFocus, _props$isPressed = props.isPressed, isPressed = _props$isPressed === void 0 ? false : _props$isPressed, _props$isActive = props.isActive, isActive = _props$isActive === void 0 ? false : _props$isActive, color = props.color, _props$contrast = props.contrast, contrast = _props$contrast === void 0 ? true : _props$contrast, otherProps = _objectWithoutProperties(props, ["classes", "className", "children", "component", "rippleCenter", "isFocus", "isPressed", "isActive", "color", "contrast"]); var RippleRef = useRef(); RippleRef = ref || RippleRef; var _useState = useState(false), _useState2 = _slicedToArray(_useState, 2), startRipple = _useState2[0], setStartRipple = _useState2[1]; var _useState3 = useState(false), _useState4 = _slicedToArray(_useState3, 2), endRipple = _useState4[0], setEndRipple = _useState4[1]; var isEndTouch = useRef(false); var startRippleDone = useRef(false); var startTouch = function startTouch(_ref) { var startX = _ref.startX, startY = _ref.startY; RippleStartAnimation(startX, startY, rippleCenter); }; var RippleStartAnimation = function RippleStartAnimation(startX, startY, center) { isEndTouch.current = false; var RippleRef_S = RippleRef.current; var _RippleRef_S$getBound = RippleRef_S.getBoundingClientRect(), width = _RippleRef_S$getBound.width, height = _RippleRef_S$getBound.height; var rippleSize = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)); var coordY, coordX, coordEndY, coordEndX; if (center) { coordX = width / 2 - rippleSize / 2; coordY = height / 2 - rippleSize / 2; } else { coordX = startX - rippleSize / 2; coordY = startY - rippleSize / 2; } coordEndX = width / 2 - rippleSize / 2; coordEndY = height / 2 - rippleSize / 2; RippleRef_S.style.setProperty('--evg-ripple-size', "".concat(rippleSize, "px")); RippleRef_S.style.setProperty('--evg-ripple-coord-start', "".concat(coordX, "px,").concat(coordY, "px")); RippleRef_S.style.setProperty('--evg-ripple-coord-end', "".concat(coordEndX, "px,").concat(coordEndY, "px")); RippleRef_S.style.setProperty('--evg-ripple-opacity', OpacityFromColor(OverlayColor(color, contrast), 'pressed')); setStartRipple(true); }; var endTouch = function endTouch() { isEndTouch.current = true; animationStartRippleDone(); }; var animationStartRippleDone = function animationStartRippleDone() { if (isEndTouch.current && startRippleDone.current) { setEndRipple(true); } }; var onAnimationEnd = function onAnimationEnd(e) { if (e.animationName.includes('startRipple')) { startRippleDone.current = true; animationStartRippleDone(); } if (e.animationName.includes('endRipple')) { // end DefaultSetState(); } }; var DefaultSetState = function DefaultSetState() { setStartRipple(false); setEndRipple(false); isEndTouch.current = false; startRippleDone.current = false; }; useEffect(function () { if (isPressed) { RippleStartAnimation(0, 0, true); } else { isEndTouch.current = true; animationStartRippleDone(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [isPressed]); return /*#__PURE__*/React.createElement(TouchDriver, _extends({ className: classNames(classes.base, (_classNames = {}, _defineProperty(_classNames, classes.rippleStart, startRipple), _defineProperty(_classNames, classes.rippleEnd, endRipple), _defineProperty(_classNames, classes.rippleFocus, isFocus), _defineProperty(_classNames, classes.rippleActive, isActive), _classNames)), onAnimationEnd: onAnimationEnd, innerRef: RippleRef, component: Component, moveStart: startTouch, moveEnd: endTouch }, otherProps)); }); Ripple.propTypes = { /** * Объект содержит jss стили компонента. */ classes: PropTypes.object, /** * Чтобы указать CSS классы, используйте этот атрибут. */ className: PropTypes.string, /** * Это свойство не реализуется. */ children: PropTypes.any, /** * Корневой узел. Это HTML элемент или компонент. */ component: PropTypes.elementType, /** * Если true, Ripple эффект стартует в центре. */ rippleCenter: PropTypes.bool, /** * Если true, примет состояние для focus. */ isFocus: PropTypes.bool, /** * Если true, примет состояние для pressed. */ isPressed: PropTypes.bool, /** * Если true, примет состояние для active. */ isActive: PropTypes.bool, /** * Название цвета в разных форматах. */ color: PropTypes.string, /** * Если true, цвет волны будет белым или серым автоматически. * Для лучшего восприятия в зависимости от основного цвета. */ contrast: PropTypes.bool }; Ripple.defaultProps = { rippleCenter: false, isFocus: false, isPressed: false, isActive: false, color: 'default', contrast: true }; Ripple.displayName = 'RippleEVG'; var Ripple$1 = withStyles(styles)(Ripple); export default Ripple$1;