@rjsf/core
Version:
A simple React component capable of building HTML forms out of a JSON schema.
64 lines (63 loc) • 3.38 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useCallback } from 'react';
/** The `RatingWidget` component renders a star or heart rating input
*
* Features:
* - Configurable number of stars/hearts (1-5) with default of 5
* - Supports different shapes (star, heart)
* - Supports minimum and maximum values from schema
* - Handles required, disabled, and readonly states
* - Provides focus and blur event handling for accessibility
* - Uses Unicode characters for better visual representation
*
* @param props - The `WidgetProps` for this component
*/
export default function RatingWidget({ id, value, required, disabled, readonly, autofocus, onChange, onFocus, onBlur, schema, options, htmlName, }) {
const { stars = 5, shape = 'star' } = options;
// Use schema.maximum if provided, otherwise use stars option (limited to 1-5)
const numStars = schema.maximum ? Math.min(schema.maximum, 5) : Math.min(Math.max(stars, 1), 5);
const min = schema.minimum || 0;
/** Handles clicking on a star to set the rating */
const handleStarClick = useCallback((starValue) => {
if (!disabled && !readonly) {
onChange(starValue);
}
}, [onChange, disabled, readonly]);
/** Handles focus events for accessibility */
const handleFocus = useCallback((event) => {
if (onFocus) {
// Get the star value from the data attribute
const starValue = Number(event.target.dataset.value);
onFocus(id, starValue);
}
}, [onFocus, id]);
/** Handles blur events for accessibility */
const handleBlur = useCallback((event) => {
if (onBlur) {
// Get the star value from the data attribute
const starValue = Number(event.target.dataset.value);
onBlur(id, starValue);
}
}, [onBlur, id]);
// Get the appropriate Unicode character based on shape option
const getSymbol = (isFilled) => {
if (shape === 'heart') {
return isFilled ? '♥' : '♡';
}
return isFilled ? '★' : '☆';
};
return (_jsx(_Fragment, { children: _jsxs("div", { className: 'rating-widget', style: {
display: 'inline-flex',
fontSize: '1.5rem',
cursor: disabled || readonly ? 'default' : 'pointer',
}, children: [[...Array(numStars)].map((_, index) => {
const starValue = min + index;
const isFilled = starValue <= value;
return (_jsx("span", { onClick: () => handleStarClick(starValue), onFocus: handleFocus, onBlur: handleBlur, "data-value": starValue, tabIndex: disabled || readonly ? -1 : 0, role: 'radio', "aria-checked": starValue === value, "aria-label": `${starValue} ${shape === 'heart' ? 'heart' : 'star'}${starValue === 1 ? '' : 's'}`, style: {
color: isFilled ? '#FFD700' : '#ccc',
padding: '0 0.2rem',
transition: 'color 0.2s',
userSelect: 'none',
}, children: getSymbol(isFilled) }, index));
}), _jsx("input", { type: 'hidden', id: id, name: htmlName || id, value: value || '', required: required, disabled: disabled || readonly, "aria-hidden": 'true' })] }) }));
}