UNPKG

@amaui/ui-react

Version:
373 lines (372 loc) 19.6 kB
"use strict"; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = __importDefault(require("react")); const utils_1 = require("@amaui/utils"); const style_react_1 = require("@amaui/style-react"); const IconMaterialGradeW100_1 = __importDefault(require("@amaui/icons-material-rounded-react/IconMaterialGradeW100")); const IconMaterialGradeW100Filled_1 = __importDefault(require("@amaui/icons-material-rounded-react/IconMaterialGradeW100Filled")); const utils_2 = require("../utils"); const useStyle = (0, style_react_1.style)(theme => ({ root: { display: 'inline-flex', alignItems: 'flex-start', cursor: 'pointer', touchAction: 'none' }, iconWrapper: { display: 'inline-flex', position: 'relative', transition: theme.methods.transitions.make('transform', { duration: 'xs' }), '&:hover': { transform: 'scale(1.14)' } }, iconWrapper_readOnly: { '&:hover': { transform: 'none' } }, icon: { display: 'inline-flex' }, active: { display: 'inline-flex', position: 'absolute', inset: '0', overflow: 'hidden', height: '100%', width: '0px' }, inactive: { opacity: theme.palette.light ? '0.24' : '0.4' }, focus_outline: { outline: `1px solid ${theme.palette.text.default.secondary}` }, readOnly: { cursor: 'default' }, disabled: { opacity: theme.palette.visual_contrast.default.opacity.disabled, cursor: 'default' } }), { name: 'amaui-Rating' }); const Rating = react_1.default.forwardRef((props_, ref) => { var _a; const theme = (0, style_react_1.useAmauiTheme)(); const props = react_1.default.useMemo(() => { var _a, _b, _c, _d, _e, _f, _g, _h; return (Object.assign(Object.assign(Object.assign({}, (_d = (_c = (_b = (_a = theme === null || theme === void 0 ? void 0 : theme.ui) === null || _a === void 0 ? void 0 : _a.elements) === null || _b === void 0 ? void 0 : _b.all) === null || _c === void 0 ? void 0 : _c.props) === null || _d === void 0 ? void 0 : _d.default), (_h = (_g = (_f = (_e = theme === null || theme === void 0 ? void 0 : theme.ui) === null || _e === void 0 ? void 0 : _e.elements) === null || _f === void 0 ? void 0 : _f.amauiRating) === null || _g === void 0 ? void 0 : _g.props) === null || _h === void 0 ? void 0 : _h.default), props_)); }, [props_]); const { tonal = true, color = 'primary', colorInactive = 'default', size = 'regular', value: value_, valueDefault, valueActive: valueActive_, valueActiveDefault, onChange, onChangeActive, values = 5, precision = 1, onlyValue, readOnly, disabled, icon, icons, iconInactive = (0, jsx_runtime_1.jsx)(IconMaterialGradeW100_1.default, {}), iconActive = (0, jsx_runtime_1.jsx)(IconMaterialGradeW100Filled_1.default, {}), IconProps, IconActiveProps, IconInactiveProps, Component = 'span', className, children } = props, other = __rest(props, ["tonal", "color", "colorInactive", "size", "value", "valueDefault", "valueActive", "valueActiveDefault", "onChange", "onChangeActive", "values", "precision", "onlyValue", "readOnly", "disabled", "icon", "icons", "iconInactive", "iconActive", "IconProps", "IconActiveProps", "IconInactiveProps", "Component", "className", "children"]); const { classes } = useStyle(); const [init, setInit] = react_1.default.useState(false); const [value, setValue] = react_1.default.useState(valueDefault !== undefined ? valueDefault : value_); const [valueActive, setValueActive] = react_1.default.useState(valueActiveDefault !== undefined ? valueActiveDefault : valueActive_); const [hover, setHover] = react_1.default.useState(false); const [focus, setFocus] = react_1.default.useState(false); const [mouseDown, setMouseDown] = react_1.default.useState(false); const refs = { root: react_1.default.useRef(undefined), props: react_1.default.useRef(undefined), value: react_1.default.useRef(undefined), valueActive: react_1.default.useRef(undefined), values: react_1.default.useRef([]), mouseDown: react_1.default.useRef(undefined), hover: react_1.default.useRef(undefined), direction: react_1.default.useRef(undefined) }; refs.props.current = props; refs.value.current = value; refs.valueActive.current = valueActive; refs.mouseDown.current = mouseDown; refs.hover.current = hover; refs.direction.current = theme.direction; const min = 0; const max = values; const valueDecimals = (String(precision).includes('e-') ? +String(precision).split('e-')[1] : (_a = String(precision).split('.')[1]) === null || _a === void 0 ? void 0 : _a.length) || 0; const valuePrecision = (valueMouse) => { let value__ = (0, utils_1.valueFromPercentageWithinRange)(valueMouse * 100, min, max); if (refs.direction.current === 'rtl') value__ = (max + min) - value__; if (value__ <= min) return min; if (value__ >= max) return max; // previous value let previous = (0, utils_1.clamp)(+(value__ - (value__ % precision)).toFixed(valueDecimals), min, max); if (value__ < 0) previous -= precision; // next value const next = (0, utils_1.clamp)(+(previous + precision).toFixed(valueDecimals), min, max); const valueNew = value__ > next ? previous : next; return valueNew; }; react_1.default.useEffect(() => { var _a; const onMouseUp = () => { if (!disabled && !readOnly) setMouseDown(false); }; const onMouseMove = (event) => { if (!refs.props.current.disabled && !refs.props.current.readOnly && (refs.mouseDown.current || refs.hover.current)) { const valuePrevious = refs.hover.current ? refs.valueActive.current : refs.value.current; const x = event.clientX; const rect = refs.root.current.getBoundingClientRect(); const { width: width_ } = rect; // Value to the precision point value const value__ = valuePrecision((x - rect.x) / width_); const valueNew = value__; if (valueNew !== valuePrevious) { if (!props.hasOwnProperty('value')) { // Inner controlled value if (refs.hover.current) setValueActive(valueNew); else setValue(valueNew); } if (refs.hover.current) { if ((0, utils_1.is)('function', onChangeActive)) onChangeActive(valueNew); } else { if ((0, utils_1.is)('function', onChange)) onChange(valueNew); } } } }; const onTouchMove = (event) => { if (!refs.props.current.disabled && !refs.props.current.readOnly && refs.mouseDown.current) { const valuePrevious = refs.hover.current ? refs.valueActive.current : refs.value.current; const x = event.touches[0].clientX; const rect = refs.root.current.getBoundingClientRect(); const { width: width_ } = rect; // Value to the precision point value const value__ = valuePrecision((x - rect.x) / width_); const valueNew = value__; if (valueNew !== valuePrevious) { // Inner controlled value if (!props.hasOwnProperty('value')) setValue(valueNew); if ((0, utils_1.is)('function', onChange)) onChange(valueNew); } } }; const rootDocument = (0, utils_1.isEnvironment)('browser') ? (((_a = refs.root.current) === null || _a === void 0 ? void 0 : _a.ownerDocument) || window.document) : undefined; rootDocument.addEventListener('mouseup', onMouseUp); rootDocument.addEventListener('mousemove', onMouseMove); rootDocument.addEventListener('touchend', onMouseUp, { passive: true }); rootDocument.addEventListener('touchmove', onTouchMove, { passive: true }); setInit(true); return () => { rootDocument.removeEventListener('mouseup', onMouseUp); rootDocument.removeEventListener('touchend', onMouseUp); rootDocument.removeEventListener('mousemove', onMouseMove); rootDocument.removeEventListener('touchmove', onTouchMove); }; }, []); react_1.default.useEffect(() => { if (init && value_ !== value) setValue(value_); }, [value_]); react_1.default.useEffect(() => { if (init && valueActive_ !== valueActive) setValueActive(valueActive_); }, [valueActive_]); const onMouseDown = react_1.default.useCallback(() => { if (!disabled && !readOnly) setMouseDown(true); }, [disabled, readOnly]); const onClick = react_1.default.useCallback((event) => { if (!disabled && !readOnly) { // Make precision value // if value is same as previous value clear // otherwise update the value y const x = event.clientX; const rect = refs.root.current.getBoundingClientRect(); const { width: width_ } = rect; const valueNew = valuePrecision((x - rect.x) / width_); if (value === valueNew) onClear(); else { if (!props.hasOwnProperty('value')) setValue(valueNew); if ((0, utils_1.is)('function', onChange)) onChange(valueNew); } } }, [disabled, readOnly, value, valueActive]); const move = (forward_ = true) => { const forward = theme.direction === 'ltr' ? forward_ : !forward_; let value__ = refs.value.current || 0; // previous value const previous = (0, utils_1.clamp)(+(value__ - precision).toFixed(valueDecimals), min, max); // next value const next = (0, utils_1.clamp)(+(value__ + precision).toFixed(valueDecimals), min, max); value__ = forward ? next : previous; const valueNew = value__; if (valueNew !== refs.value.current) { if (!props.hasOwnProperty('value')) setValue(valueNew); if ((0, utils_1.is)('function', onChange)) onChange(valueNew); } }; const onClear = () => { if (!props.hasOwnProperty('value')) setValue(''); if ((0, utils_1.is)('function', onChange)) onChange(''); setHover(false); }; const onKeyDown = react_1.default.useCallback((event) => { if (!disabled && !readOnly) { if (['Enter', 'Escape', 'ArrowUp', 'ArrowRight', 'ArrowDown', 'ArrowLeft', 'Home', 'End'].includes(event.key)) { // Prevent default event.preventDefault(); switch (event.key) { case 'End': if (!props.hasOwnProperty('value')) setValue(0); if ((0, utils_1.is)('function', onChange)) return onChange(0); return; case 'Home': if (!props.hasOwnProperty('value')) setValue(values); if ((0, utils_1.is)('function', onChange)) return onChange(values); return; case 'ArrowUp': case 'ArrowRight': return move(); case 'ArrowDown': case 'ArrowLeft': return move(false); case 'Enter': if (value === valueActive) { if (!props.hasOwnProperty('value')) setValue(''); if ((0, utils_1.is)('function', onChange)) onChange(''); } return; case 'Escape': if (!props.hasOwnProperty('value')) setValue(''); if ((0, utils_1.is)('function', onChange)) onChange(''); return; default: break; } } } }, [disabled, readOnly, value, valueActive, precision]); const onFocus = react_1.default.useCallback((event) => { if (!disabled && !readOnly && !mouseDown) setFocus(true); }, [disabled, readOnly, mouseDown]); const onBlur = react_1.default.useCallback(() => { if (!disabled && !readOnly) setFocus(false); }, [disabled, readOnly]); const onMouseEnter = react_1.default.useCallback(() => { if (!disabled && !readOnly) setHover(true); }, [disabled, readOnly]); const onMouseLeave = react_1.default.useCallback(() => { if (!disabled && !readOnly) setHover(false); }, [disabled, readOnly]); const width = (index) => { const value__ = !hover ? value : valueActive; if (value__ > index - 1 && value__ <= index) { if (value__ === index) return '100%'; return `${(value__ % 1) * 100}%`; } if ((0, utils_1.is)('number', value__) && index < +(value__).toFixed(1) && !onlyValue) return '100%'; }; const getIcon = (index, inactive = true) => { var _a, _b, _c, _d; if (inactive) return ((_a = icons === null || icons === void 0 ? void 0 : icons[index]) === null || _a === void 0 ? void 0 : _a.iconInactive) || ((_b = icons === null || icons === void 0 ? void 0 : icons[index]) === null || _b === void 0 ? void 0 : _b.icon) || icon || iconInactive; return ((_c = icons === null || icons === void 0 ? void 0 : icons[index]) === null || _c === void 0 ? void 0 : _c.iconActive) || ((_d = icons === null || icons === void 0 ? void 0 : icons[index]) === null || _d === void 0 ? void 0 : _d.icon) || icon || iconActive; }; const selected = (index) => value > index - 1 && value <= index; return ((0, jsx_runtime_1.jsx)(Component, Object.assign({ ref: item => { if (ref) { if ((0, utils_1.is)('function', ref)) ref(item); else ref.current = ref; } refs.root.current = item; }, tabIndex: (!disabled && !readOnly) ? 0 : undefined, onBlur: onBlur, onFocus: onFocus, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onMouseDown: onMouseDown, onTouchStart: onMouseDown, onKeyDown: onKeyDown, className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('Rating', theme) && [ 'amaui-Rating-root', `amaui-Rating-size-${size}`, hover && `amaui-Button-hover`, mouseDown && `amaui-Button-mouse-down`, focus && [ `amaui-Button-focus`, [undefined, 0].includes(value) && `amaui-Button-focus-noValue` ], readOnly && `amaui-Rating-read-only`, disabled && `amaui-Rating-disabled` ], className, classes.root, focus && [ classes.focus, [undefined, 0].includes(value) && classes.focus_outline ], readOnly && classes.readOnly, disabled && classes.disabled ]) }, other, { children: new Array(values).fill(undefined).map((item, index) => { var _a, _b, _c, _d, _e, _f, _g, _h; const IconInactive = getIcon(index + 1); const IconActive = getIcon(index + 1, false); return ((0, jsx_runtime_1.jsxs)("span", Object.assign({ ref: item_ => refs.values.current.push(item_), onClick: onClick, className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('Rating', theme) && [ 'amaui-Rating-icon-wrapper', focus && selected(index + 1) && 'amaui-Rating-focus' ], classes.iconWrapper, focus && selected(index + 1) && classes.focus_outline, (readOnly || disabled) && classes.iconWrapper_readOnly ]) }, { children: [(0, jsx_runtime_1.jsx)("span", Object.assign({ className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('Rating', theme) && [ 'amaui-Rating-icon', 'amaui-Rating-icon-inactive' ], classes.icon, classes.inactive ]) }, { children: react_1.default.cloneElement(IconInactive, Object.assign(Object.assign({ color: colorInactive, size: ((_a = IconInactive.props) === null || _a === void 0 ? void 0 : _a.size) !== undefined ? (_b = IconInactive.props) === null || _b === void 0 ? void 0 : _b.size : size }, IconProps), IconInactiveProps)) })), (0, jsx_runtime_1.jsx)("span", Object.assign({ className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('Rating', theme) && [ 'amaui-Rating-icon', 'amaui-Rating-icon-active' ], classes.icon, classes.active ]), style: { width: width(index + 1) } }, { children: react_1.default.cloneElement(IconActive, Object.assign(Object.assign({ tonal: ((_c = IconActive.props) === null || _c === void 0 ? void 0 : _c.tonal) !== undefined ? (_d = IconActive.props) === null || _d === void 0 ? void 0 : _d.tonal : tonal, color: ((_e = IconActive.props) === null || _e === void 0 ? void 0 : _e.color) !== undefined ? (_f = IconActive.props) === null || _f === void 0 ? void 0 : _f.color : color, size: ((_g = IconActive.props) === null || _g === void 0 ? void 0 : _g.size) !== undefined ? (_h = IconActive.props) === null || _h === void 0 ? void 0 : _h.size : size }, IconProps), IconActiveProps)) }))] }), index)); }) }))); }); Rating.displayName = 'amaui-Rating'; exports.default = Rating;