UNPKG

@material-ui/lab

Version:

Material-UI Lab - Incubator for Material-UI React components.

606 lines (505 loc) 17.9 kB
"use strict"; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.styles = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties")); var React = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _clsx = _interopRequireDefault(require("clsx")); var _utils = require("@material-ui/utils"); var _styles = require("@material-ui/core/styles"); var _utils2 = require("@material-ui/core/utils"); var _Star = _interopRequireDefault(require("../internal/svg-icons/Star")); function clamp(value, min, max) { if (value < min) { return min; } if (value > max) { return max; } return value; } function getDecimalPrecision(num) { var decimalPart = num.toString().split('.')[1]; return decimalPart ? decimalPart.length : 0; } function roundValueToPrecision(value, precision) { if (value == null) { return value; } var nearest = Math.round(value / precision) * precision; return Number(nearest.toFixed(getDecimalPrecision(precision))); } var styles = function styles(theme) { return { /* Styles applied to the root element. */ root: { display: 'inline-flex', position: 'relative', fontSize: theme.typography.pxToRem(24), color: '#ffb400', cursor: 'pointer', textAlign: 'left', WebkitTapHighlightColor: 'transparent', '&$disabled': { opacity: 0.5, pointerEvents: 'none' }, '&$focusVisible $iconActive': { outline: '1px solid #999' } }, /* Styles applied to the root element if `size="small"`. */ sizeSmall: { fontSize: theme.typography.pxToRem(18) }, /* Styles applied to the root element if `size="large"`. */ sizeLarge: { fontSize: theme.typography.pxToRem(30) }, /* Styles applied to the root element if `readOnly={true}`. */ readOnly: { pointerEvents: 'none' }, /* Pseudo-class applied to the root element if `disabled={true}`. */ disabled: {}, /* Pseudo-class applied to the root element if keyboard focused. */ focusVisible: {}, /* Visually hide an element. */ visuallyhidden: { border: 0, clip: 'rect(0 0 0 0)', height: 1, margin: -1, color: '#000', overflow: 'hidden', padding: 0, position: 'absolute', top: 20, width: 1 }, /* Styles applied to the pristine label. */ pristine: { 'input:focus + &': { top: 0, bottom: 0, position: 'absolute', outline: '1px solid #999', width: '100%' } }, /* Styles applied to the label elements. */ label: { cursor: 'inherit' }, /* Styles applied to the icon wrapping elements. */ icon: { display: 'flex', transition: theme.transitions.create('transform', { duration: theme.transitions.duration.shortest }), // Fix mouseLeave issue. // https://github.com/facebook/react/issues/4492 pointerEvents: 'none' }, /* Styles applied to the icon wrapping elements when empty. */ iconEmpty: { color: theme.palette.action.disabled }, /* Styles applied to the icon wrapping elements when filled. */ iconFilled: {}, /* Styles applied to the icon wrapping elements when hover. */ iconHover: {}, /* Styles applied to the icon wrapping elements when focus. */ iconFocus: {}, /* Styles applied to the icon wrapping elements when active. */ iconActive: { transform: 'scale(1.2)' }, /* Styles applied to the icon wrapping elements when decimals are necessary. */ decimal: { position: 'relative' } }; }; exports.styles = styles; function IconContainer(props) { var value = props.value, other = (0, _objectWithoutProperties2.default)(props, ["value"]); return /*#__PURE__*/React.createElement("span", other); } process.env.NODE_ENV !== "production" ? IconContainer.propTypes = { value: _propTypes.default.number.isRequired } : void 0; var defaultIcon = /*#__PURE__*/React.createElement(_Star.default, { fontSize: "inherit" }); function defaultLabelText(value) { return "".concat(value, " Star").concat(value !== 1 ? 's' : ''); } var Rating = /*#__PURE__*/React.forwardRef(function Rating(props, ref) { var classes = props.classes, className = props.className, _props$defaultValue = props.defaultValue, defaultValue = _props$defaultValue === void 0 ? null : _props$defaultValue, _props$disabled = props.disabled, disabled = _props$disabled === void 0 ? false : _props$disabled, emptyIcon = props.emptyIcon, _props$emptyLabelText = props.emptyLabelText, emptyLabelText = _props$emptyLabelText === void 0 ? 'Empty' : _props$emptyLabelText, _props$getLabelText = props.getLabelText, getLabelText = _props$getLabelText === void 0 ? defaultLabelText : _props$getLabelText, _props$icon = props.icon, icon = _props$icon === void 0 ? defaultIcon : _props$icon, _props$IconContainerC = props.IconContainerComponent, IconContainerComponent = _props$IconContainerC === void 0 ? IconContainer : _props$IconContainerC, _props$max = props.max, max = _props$max === void 0 ? 5 : _props$max, nameProp = props.name, onChange = props.onChange, onChangeActive = props.onChangeActive, onMouseLeave = props.onMouseLeave, onMouseMove = props.onMouseMove, _props$precision = props.precision, precision = _props$precision === void 0 ? 1 : _props$precision, _props$readOnly = props.readOnly, readOnly = _props$readOnly === void 0 ? false : _props$readOnly, _props$size = props.size, size = _props$size === void 0 ? 'medium' : _props$size, valueProp = props.value, other = (0, _objectWithoutProperties2.default)(props, ["classes", "className", "defaultValue", "disabled", "emptyIcon", "emptyLabelText", "getLabelText", "icon", "IconContainerComponent", "max", "name", "onChange", "onChangeActive", "onMouseLeave", "onMouseMove", "precision", "readOnly", "size", "value"]); var name = (0, _utils2.unstable_useId)(nameProp); var _useControlled = (0, _utils2.useControlled)({ controlled: valueProp, default: defaultValue, name: 'Rating' }), _useControlled2 = (0, _slicedToArray2.default)(_useControlled, 2), valueDerived = _useControlled2[0], setValueState = _useControlled2[1]; var valueRounded = roundValueToPrecision(valueDerived, precision); var theme = (0, _styles.useTheme)(); var _React$useState = React.useState({ hover: -1, focus: -1 }), _React$useState$ = _React$useState[0], hover = _React$useState$.hover, focus = _React$useState$.focus, setState = _React$useState[1]; var value = valueRounded; if (hover !== -1) { value = hover; } if (focus !== -1) { value = focus; } var _useIsFocusVisible = (0, _utils2.useIsFocusVisible)(), isFocusVisible = _useIsFocusVisible.isFocusVisible, onBlurVisible = _useIsFocusVisible.onBlurVisible, focusVisibleRef = _useIsFocusVisible.ref; var _React$useState2 = React.useState(false), focusVisible = _React$useState2[0], setFocusVisible = _React$useState2[1]; var rootRef = React.useRef(); var handleFocusRef = (0, _utils2.useForkRef)(focusVisibleRef, rootRef); var handleRef = (0, _utils2.useForkRef)(handleFocusRef, ref); var handleMouseMove = function handleMouseMove(event) { if (onMouseMove) { onMouseMove(event); } var rootNode = rootRef.current; var _rootNode$getBounding = rootNode.getBoundingClientRect(), right = _rootNode$getBounding.right, left = _rootNode$getBounding.left; var _rootNode$firstChild$ = rootNode.firstChild.getBoundingClientRect(), width = _rootNode$firstChild$.width; var percent; if (theme.direction === 'rtl') { percent = (right - event.clientX) / (width * max); } else { percent = (event.clientX - left) / (width * max); } var newHover = roundValueToPrecision(max * percent + precision / 2, precision); newHover = clamp(newHover, precision, max); setState(function (prev) { return prev.hover === newHover && prev.focus === newHover ? prev : { hover: newHover, focus: newHover }; }); setFocusVisible(false); if (onChangeActive && hover !== newHover) { onChangeActive(event, newHover); } }; var handleMouseLeave = function handleMouseLeave(event) { if (onMouseLeave) { onMouseLeave(event); } var newHover = -1; setState({ hover: newHover, focus: newHover }); if (onChangeActive && hover !== newHover) { onChangeActive(event, newHover); } }; var handleChange = function handleChange(event) { var newValue = parseFloat(event.target.value); setValueState(newValue); if (onChange) { onChange(event, newValue); } }; var handleClear = function handleClear(event) { // Ignore keyboard events // https://github.com/facebook/react/issues/7407 if (event.clientX === 0 && event.clientY === 0) { return; } setState({ hover: -1, focus: -1 }); setValueState(null); if (onChange && parseFloat(event.target.value) === valueRounded) { onChange(event, null); } }; var handleFocus = function handleFocus(event) { if (isFocusVisible(event)) { setFocusVisible(true); } var newFocus = parseFloat(event.target.value); setState(function (prev) { return { hover: prev.hover, focus: newFocus }; }); if (onChangeActive && focus !== newFocus) { onChangeActive(event, newFocus); } }; var handleBlur = function handleBlur(event) { if (hover !== -1) { return; } if (focusVisible !== false) { setFocusVisible(false); onBlurVisible(); } var newFocus = -1; setState(function (prev) { return { hover: prev.hover, focus: newFocus }; }); if (onChangeActive && focus !== newFocus) { onChangeActive(event, newFocus); } }; var item = function item(state, labelProps) { var id = "".concat(name, "-").concat(String(state.value).replace('.', '-')); var container = /*#__PURE__*/React.createElement(IconContainerComponent, { value: state.value, className: (0, _clsx.default)(classes.icon, state.filled ? classes.iconFilled : classes.iconEmpty, state.hover && classes.iconHover, state.focus && classes.iconFocus, state.active && classes.iconActive) }, emptyIcon && !state.filled ? emptyIcon : icon); if (readOnly) { return /*#__PURE__*/React.createElement("span", (0, _extends2.default)({ key: state.value }, labelProps), container); } return /*#__PURE__*/React.createElement(React.Fragment, { key: state.value }, /*#__PURE__*/React.createElement("label", (0, _extends2.default)({ className: classes.label, htmlFor: id }, labelProps), container, /*#__PURE__*/React.createElement("span", { className: classes.visuallyhidden }, getLabelText(state.value))), /*#__PURE__*/React.createElement("input", { onFocus: handleFocus, onBlur: handleBlur, onChange: handleChange, onClick: handleClear, disabled: disabled, value: state.value, id: id, type: "radio", name: name, checked: state.checked, className: classes.visuallyhidden })); }; return /*#__PURE__*/React.createElement("span", (0, _extends2.default)({ ref: handleRef, onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, className: (0, _clsx.default)(classes.root, className, size !== 'medium' && classes["size".concat((0, _utils2.capitalize)(size))], disabled && classes.disabled, focusVisible && classes.focusVisible, readOnly && classes.readOnly), role: readOnly ? 'img' : null, "aria-label": readOnly ? getLabelText(value) : null }, other), Array.from(new Array(max)).map(function (_, index) { var itemValue = index + 1; if (precision < 1) { var items = Array.from(new Array(1 / precision)); return /*#__PURE__*/React.createElement("span", { key: itemValue, className: (0, _clsx.default)(classes.decimal, itemValue === Math.ceil(value) && (hover !== -1 || focus !== -1) && classes.iconActive) }, items.map(function ($, indexDecimal) { var itemDecimalValue = roundValueToPrecision(itemValue - 1 + (indexDecimal + 1) * precision, precision); return item({ value: itemDecimalValue, filled: itemDecimalValue <= value, hover: itemDecimalValue <= hover, focus: itemDecimalValue <= focus, checked: itemDecimalValue === valueRounded }, { style: items.length - 1 === indexDecimal ? {} : { width: itemDecimalValue === value ? "".concat((indexDecimal + 1) * precision * 100, "%") : '0%', overflow: 'hidden', zIndex: 1, position: 'absolute' } }); })); } return item({ value: itemValue, active: itemValue === value && (hover !== -1 || focus !== -1), filled: itemValue <= value, hover: itemValue <= hover, focus: itemValue <= focus, checked: itemValue === valueRounded }); }), !readOnly && !disabled && valueRounded == null && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("input", { value: "", id: "".concat(name, "-empty"), type: "radio", name: name, defaultChecked: true, className: classes.visuallyhidden }), /*#__PURE__*/React.createElement("label", { className: classes.pristine, htmlFor: "".concat(name, "-empty") }, /*#__PURE__*/React.createElement("span", { className: classes.visuallyhidden }, emptyLabelText)))); }); process.env.NODE_ENV !== "production" ? Rating.propTypes = { // ----------------------------- Warning -------------------------------- // | These PropTypes are generated from the TypeScript type definitions | // | To update them edit the d.ts file and run "yarn proptypes" | // ---------------------------------------------------------------------- /** * Override or extend the styles applied to the component. * See [CSS API](#css) below for more details. */ classes: _propTypes.default.object, /** * @ignore */ className: _propTypes.default.string, /** * The default value. Use when the component is not controlled. */ defaultValue: _propTypes.default.number, /** * If `true`, the rating will be disabled. */ disabled: _propTypes.default.bool, /** * The icon to display when empty. */ emptyIcon: _propTypes.default.node, /** * The label read when the rating input is empty. */ emptyLabelText: _propTypes.default.node, /** * Accepts a function which returns a string value that provides a user-friendly name for the current value of the rating. * * For localization purposes, you can use the provided [translations](/guides/localization/). * * @param {number} value The rating label's value to format. * @returns {string} */ getLabelText: _propTypes.default.func, /** * The icon to display. */ icon: _propTypes.default.node, /** * The component containing the icon. */ IconContainerComponent: _propTypes.default.elementType, /** * Maximum rating. */ max: _propTypes.default.number, /** * The name attribute of the radio `input` elements. * If `readOnly` is false, the prop is required, * this input name`should be unique within the parent form. */ name: (0, _utils.chainPropTypes)(_propTypes.default.string, function (props) { if (!props.readOnly && !props.name) { return new Error(['Material-UI: The prop `name` is required (when `readOnly` is false).', 'Additionally, the input name should be unique within the parent form.'].join('\n')); } return null; }), /** * Callback fired when the value changes. * * @param {object} event The event source of the callback. * @param {number} value The new value. */ onChange: _propTypes.default.func, /** * Callback function that is fired when the hover state changes. * * @param {object} event The event source of the callback. * @param {number} value The new value. */ onChangeActive: _propTypes.default.func, /** * @ignore */ onMouseLeave: _propTypes.default.func, /** * @ignore */ onMouseMove: _propTypes.default.func, /** * The minimum increment value change allowed. */ precision: (0, _utils.chainPropTypes)(_propTypes.default.number, function (props) { if (props.precision < 0.1) { return new Error(['Material-UI: The prop `precision` should be above 0.1.', 'A value below this limit has an imperceptible impact.'].join('\n')); } return null; }), /** * Removes all hover effects and pointer events. */ readOnly: _propTypes.default.bool, /** * The size of the rating. */ size: _propTypes.default.oneOf(['large', 'medium', 'small']), /** * The rating value. */ value: _propTypes.default.number } : void 0; var _default = (0, _styles.withStyles)(styles, { name: 'MuiRating' })(Rating); exports.default = _default;