UNPKG

@fluentui/react

Version:

Reusable React components for building web experiences.

146 lines 7.76 kB
import { __assign } from "tslib"; import * as React from 'react'; import { classNamesFunction, css, format, divProperties, getNativeProps, KeyCodes, useFocusRects, } from '../../Utilities'; import { Icon } from '../../Icon'; import { FocusZone, FocusZoneDirection } from '../../FocusZone'; import { RatingSize } from './Rating.types'; import { useId, useWarnings, useControllableValue, useMergedRefs } from '@fluentui/react-hooks'; var getClassNames = classNamesFunction(); var RatingStar = function (props) { return (React.createElement("div", { className: props.classNames.ratingStar }, React.createElement(Icon, { className: props.classNames.ratingStarBack, iconName: props.fillPercentage === 0 || props.fillPercentage === 100 ? props.icon : props.unselectedIcon }), !props.disabled && (React.createElement(Icon, { className: props.classNames.ratingStarFront, iconName: props.icon, style: { width: props.fillPercentage + '%' } })))); }; var useComponentRef = function (componentRef, rating) { React.useImperativeHandle(componentRef, function () { return ({ rating: rating, }); }, [rating]); }; var useDebugWarnings = function (props) { if (process.env.NODE_ENV !== 'production') { // eslint-disable-next-line react-hooks/rules-of-hooks -- build-time conditional useWarnings({ name: 'Rating', props: props, controlledUsage: { valueProp: 'rating', defaultValueProp: 'defaultRating', onChangeProp: 'onChange', readOnlyProp: 'readOnly', }, }); } }; var getClampedRating = function (rating, min, max) { return Math.min(Math.max(rating !== null && rating !== void 0 ? rating : min, min), max); }; var getFillingPercentage = function (starNum, displayRating) { var ceilValue = Math.ceil(displayRating); var fillPercentage = 100; if (starNum === displayRating) { fillPercentage = 100; } else if (starNum === ceilValue) { fillPercentage = 100 * (displayRating % 1); } else if (starNum > ceilValue) { fillPercentage = 0; } return fillPercentage; }; var getStarId = function (id, starNum) { return "".concat(id, "-star-").concat(starNum - 1); }; export var RatingBase = React.forwardRef(function (props, forwardedRef) { var id = useId('Rating'); var labelId = useId('RatingLabel'); var ariaLabel = props.ariaLabel, ariaLabelFormat = props.ariaLabelFormat, disabled = props.disabled, getAriaLabel = props.getAriaLabel, styles = props.styles, // eslint-disable-next-line deprecation/deprecation _a = props.min, // eslint-disable-next-line deprecation/deprecation minFromProps = _a === void 0 ? props.allowZeroStars ? 0 : 1 : _a, _b = props.max, max = _b === void 0 ? 5 : _b, readOnly = props.readOnly, size = props.size, theme = props.theme, _c = props.icon, icon = _c === void 0 ? 'FavoriteStarFill' : _c, _d = props.unselectedIcon, unselectedIcon = _d === void 0 ? 'FavoriteStar' : _d, onRenderStar = props.onRenderStar; // Ensure min is >= 0 to avoid issues elsewhere var min = Math.max(minFromProps, 0); var _e = useControllableValue(props.rating, props.defaultRating, props.onChange), rating = _e[0], setRating = _e[1]; /** Rating clamped within valid range. Will be `min` if `rating` is undefined. */ var displayRating = getClampedRating(rating, min, max); useDebugWarnings(props); useComponentRef(props.componentRef, displayRating); var rootRef = React.useRef(null); var mergedRootRefs = useMergedRefs(rootRef, forwardedRef); useFocusRects(rootRef); var divProps = getNativeProps(props, divProperties); var classNames = getClassNames(styles, { disabled: disabled, readOnly: readOnly, theme: theme, }); var readOnlyAriaLabel = getAriaLabel === null || getAriaLabel === void 0 ? void 0 : getAriaLabel(displayRating, max); var normalModeAriaLabel = ariaLabel ? ariaLabel : readOnlyAriaLabel; var stars = []; var renderStar = function (starProps, renderer) { return renderer ? renderer(starProps) : React.createElement(RatingStar, __assign({}, starProps)); }; var _loop_1 = function (starNum) { var fillPercentage = getFillingPercentage(starNum, displayRating); var onSelectStar = function (ev) { // Use the actual rating (not display value) here, to ensure that we update if the actual // rating is undefined and the user clicks the first star. if (rating === undefined || Math.ceil(rating) !== starNum) { setRating(starNum, ev); } }; var onStarKeyDown = function (event) { // eslint-disable-next-line deprecation/deprecation var which = event.which; var newRating = starNum; switch (which) { case KeyCodes.right: case KeyCodes.down: newRating = Math.min(max, newRating + 1); break; case KeyCodes.left: case KeyCodes.up: newRating = Math.max(1, newRating - 1); break; case KeyCodes.home: case KeyCodes.pageUp: newRating = 1; break; case KeyCodes.end: case KeyCodes.pageDown: newRating = max; break; } if (newRating !== starNum && (rating === undefined || Math.ceil(rating) !== newRating)) { setRating(newRating, event); } }; stars.push(React.createElement("button", __assign({ className: css(classNames.ratingButton, size === RatingSize.Large ? classNames.ratingStarIsLarge : classNames.ratingStarIsSmall), id: getStarId(id, starNum), key: starNum }, (starNum === Math.ceil(displayRating) && { 'data-is-current': true }), { onKeyDown: onStarKeyDown, onClick: onSelectStar, disabled: !!(disabled || readOnly), role: "radio", "aria-hidden": readOnly ? 'true' : undefined, type: "button", "aria-checked": starNum === Math.ceil(displayRating) }), React.createElement("span", { id: "".concat(labelId, "-").concat(starNum), className: classNames.labelText }, format(ariaLabelFormat || '', starNum, max)), renderStar({ fillPercentage: fillPercentage, disabled: disabled, classNames: classNames, icon: fillPercentage > 0 ? icon : unselectedIcon, starNum: starNum, unselectedIcon: unselectedIcon, }, onRenderStar))); }; for (var starNum = 1; starNum <= max; starNum++) { _loop_1(starNum); } var rootSizeClass = size === RatingSize.Large ? classNames.rootIsLarge : classNames.rootIsSmall; return (React.createElement("div", __assign({ ref: mergedRootRefs, className: css('ms-Rating-star', classNames.root, rootSizeClass), "aria-label": !readOnly ? normalModeAriaLabel : undefined, id: id, role: !readOnly ? 'radiogroup' : undefined }, divProps), React.createElement(FocusZone, __assign({ direction: FocusZoneDirection.bidirectional, className: css(classNames.ratingFocusZone, rootSizeClass), defaultActiveElement: '#' + getStarId(id, Math.ceil(displayRating)) }, (readOnly && { allowFocusRoot: true, disabled: true, role: 'textbox', 'aria-label': readOnlyAriaLabel, 'aria-readonly': true, 'data-is-focusable': true, tabIndex: 0, })), stars))); }); RatingBase.displayName = 'RatingBase'; //# sourceMappingURL=Rating.base.js.map