尝试构建一个5星评级组件,就像示例链接。
peer-hover
似乎起作用,但peer-checked
不像peer-hover
那样起作用。
(items
包含一个数组[1,2,3,4,5])
你能指出发生这个问题的原因吗?
import { RadioGroup } from '@headlessui/react'
import { useController } from "react-hook-form";
import { classNames } from '../libs/frontend/utils'
import { StarIcon } from '@heroicons/react/24/outline';
import { StarIcon as StarIconSolid } from '@heroicons/react/20/solid';
export const RadioGroupStars = (props) => {
const {
field: { value, onChange }
} = useController(props);
const { items } = props;
return (
<>
<RadioGroup
value={value}
onChange={onChange}
className="w-full my-1">
<RadioGroup.Label className="sr-only"> Choose a option </RadioGroup.Label>
<div className="flex flex-row-reverse justify-center gap-1">
{items.map((item) => (
<RadioGroup.Option
key={item}
value={item}
className={({ active, checked }) =>
classNames(
'cursor-pointer text-gray-200',
'flex-1 hover:text-yellow-600',
'peer',
'peer-hover:text-yellow-600 peer-checked:text-yellow-500',
active ? 'text-yellow-500' : '',
checked ? 'text-yellow-500' : '',
)
}
>
<RadioGroup.Label as={StarIconSolid} className='' />
</RadioGroup.Option>
))}
</div>
</RadioGroup>
</>
);
}
我认为peer-checked
不工作的原因可能是因为RadioGroup.Option
输出div
包装图标为Label
(而不是input
),因此伪类:checked
不应用,而peer-hover
确实工作。
因为组件可以访问选定的value
并且额定值是可比较的:
项包含数组[1,2,3,4,5]
RadioGroup.Option
可以将自己的值与选择的值进行比较,作为不同类渲染的条件(或者等价地比较index
)。
由于此列表还使用flex-row-reverse
来实现兄弟悬停,因此考虑将reverse()
放在map()
之前的items
,以保持迭代项的正确顺序。
在live on: stackblitz中测试了这个例子(为了简单起见省略了react-hook-form
的逻辑):
<div className="flex flex-row-reverse justify-center gap-1">
{[...items].reverse().map((item) => (
<RadioGroup.Option
key={item}
value={item}
className={({ active, checked }) =>
classNames(
"cursor-pointer text-gray-200",
"flex-1 hover:text-yellow-400",
"peer",
"peer-hover:text-yellow-400",
active ? "text-yellow-500" : "",
checked ? "text-yellow-500" : "",
// 👇 Add a compare with selected value here
value >= item ? "text-yellow-500" : ""
)
}
>
<RadioGroup.Label as={BsStarFill} className="w-6 h-6" />
</RadioGroup.Option>
))}
</div>
另一方面,因为RadioGroup
需要setValue
(一个状态set
函数)来处理它的onChange
prop,所以不太确定useController()
返回的field.onChange
是否可以与它一起工作。
如果没有,也许可以考虑在组件中托管一个状态,并与useController同步,这样它的函数仍然可以被使用。
我在下面的代码中使用了react和tailwing,尽可能使用简单的控件
export default function RatingControl() {
const [rating, setRating] = useState(0);
return (
<div className="grid w-full sm:col-span-3 sm:grid-cols-10">
<dt className="pb-2 font-bold text-gray-500 sm:col-span-10 ">
Rate Survey
</dt>
{Array.from({ length: 5 }, (_, k) => k + 1).map((item) => (
<button
key={`buttonItem${item}`}
type="button"
onClick={() => {
setRating(item);
}}
className={classNames(
rating !== item ? 'bg-black' : 'bg-gray-800',
' mr-4 mt-2 lg:mt-0 rounded-md text-lg font-semibold text-gray-500 shadow-sm hover:text-white hover:bg-gray-800 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white'
)}
>
{item}
</button>
))}
</div>
);
}