Rating Group
Allows users to rate items using a set of icons.
import { RatingGroup, type RatingGroupProps } from '~/components/ui/rating-group'
export const Demo = (props: RatingGroupProps) => {
return (
<RatingGroup defaultValue={3} {...props}>
Label
</RatingGroup>
)
}
import { RatingGroup, type RatingGroupProps } from '~/components/ui/rating-group'
export const Demo = (props: RatingGroupProps) => {
return (
<RatingGroup defaultValue={3} {...props}>
Label
</RatingGroup>
)
}
Usage
import { RatinGroup } from '~/components/ui/rating-group'
Examples
Different color
Use the colorPalette
prop to change the color of the rating group.
<RatingGroup colorPalette="red" value={3}>
Label
</RatingGroup>
Rating count
Use the count
prop to render a specific number of stars.
<RatingGroup count={10}>Label</RatingGroup>
Half star rating
Use the allowHalf
prop to enable half star ratings.
<RatingGroup value={3.5} allowHalf>
Label
</RatingGroup>
Installation
npx @park-ui/cli components add rating-group
1
Add Styled Primitive
Copy the code snippet below into ~/components/ui/styled/rating-group.tsx
'use client'
import type { Assign } from '@ark-ui/react'
import { RatingGroup } from '@ark-ui/react/rating-group'
import { type RatingGroupVariantProps, ratingGroup } from 'styled-system/recipes'
import type { ComponentProps, HTMLStyledProps } from 'styled-system/types'
import { createStyleContext } from './utils/create-style-context'
const { withProvider, withContext } = createStyleContext(ratingGroup)
export type RootProviderProps = ComponentProps<typeof RootProvider>
export const RootProvider = withProvider<
HTMLDivElement,
Assign<Assign<HTMLStyledProps<'div'>, RatingGroup.RootProviderBaseProps>, RatingGroupVariantProps>
>(RatingGroup.RootProvider, 'root')
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
HTMLDivElement,
Assign<Assign<HTMLStyledProps<'div'>, RatingGroup.RootBaseProps>, RatingGroupVariantProps>
>(RatingGroup.Root, 'root')
export const Control = withContext<
HTMLDivElement,
Assign<HTMLStyledProps<'div'>, RatingGroup.ControlBaseProps>
>(RatingGroup.Control, 'control')
export const Item = withContext<
HTMLSpanElement,
Assign<HTMLStyledProps<'span'>, RatingGroup.ItemBaseProps>
>(RatingGroup.Item, 'item')
export const Label = withContext<
HTMLLabelElement,
Assign<HTMLStyledProps<'label'>, RatingGroup.LabelBaseProps>
>(RatingGroup.Label, 'label')
export {
RatingGroupContext as Context,
RatingGroupItemContext as ItemContext,
RatingGroupHiddenInput as HiddenInput,
} from '@ark-ui/react/rating-group'
import { type Assign, RatingGroup } from '@ark-ui/solid'
import type { ComponentProps } from 'solid-js'
import { type RatingGroupVariantProps, ratingGroup } from 'styled-system/recipes'
import type { HTMLStyledProps } from 'styled-system/types'
import { createStyleContext } from './utils/create-style-context'
const { withProvider, withContext } = createStyleContext(ratingGroup)
export type RootProviderProps = ComponentProps<typeof RootProvider>
export const RootProvider = withProvider<
Assign<Assign<HTMLStyledProps<'div'>, RatingGroup.RootProviderBaseProps>, RatingGroupVariantProps>
>(RatingGroup.RootProvider, 'root')
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
Assign<Assign<HTMLStyledProps<'div'>, RatingGroup.RootBaseProps>, RatingGroupVariantProps>
>(RatingGroup.Root, 'root')
export const Control = withContext<Assign<HTMLStyledProps<'div'>, RatingGroup.ControlBaseProps>>(
RatingGroup.Control,
'control',
)
export const Item = withContext<Assign<HTMLStyledProps<'span'>, RatingGroup.ItemBaseProps>>(
RatingGroup.Item,
'item',
)
export const Label = withContext<Assign<HTMLStyledProps<'label'>, RatingGroup.LabelBaseProps>>(
RatingGroup.Label,
'label',
)
export {
RatingGroupContext as Context,
RatingGroupHiddenInput as HiddenInput,
RatingGroupItemContext as ItemContext,
} from '@ark-ui/solid'
No snippet found
2
Add Composition
Copy the composition snippet below into ~/components/ui/rating-group.tsx
'use client'
import { forwardRef } from 'react'
import * as StyledRatingGroup from './styled/rating-group'
export interface RatingGroupProps extends StyledRatingGroup.RootProps {}
export const RatingGroup = forwardRef<HTMLDivElement, RatingGroupProps>((props, ref) => {
const { children, ...rootProps } = props
return (
<StyledRatingGroup.Root ref={ref} {...rootProps}>
{children && <StyledRatingGroup.Label>{children}</StyledRatingGroup.Label>}
<StyledRatingGroup.Control>
<StyledRatingGroup.Context>
{({ items }) =>
items.map((index) => (
<StyledRatingGroup.Item key={index} index={index}>
<StyledRatingGroup.ItemContext>
{(item) => <StarIcon isHalf={item.half} />}
</StyledRatingGroup.ItemContext>
</StyledRatingGroup.Item>
))
}
</StyledRatingGroup.Context>
</StyledRatingGroup.Control>
<StyledRatingGroup.HiddenInput />
</StyledRatingGroup.Root>
)
})
RatingGroup.displayName = 'RatingGroup'
type IconProps = {
isHalf: boolean
}
const StarIcon = (props: IconProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="inherit"
stroke="inherit"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<title>Star Icon</title>
<defs>
<linearGradient id="half">
<stop offset="50%" stopColor="var(--colors-color-palette-default)" />
<stop offset="50%" stopColor="var(--colors-bg-emphasized)" />
</linearGradient>
</defs>
<polygon
fill={props.isHalf ? 'url(#half)' : 'inherit'}
points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"
/>
</svg>
)
import { Index, Show, children } from 'solid-js'
import * as StyledRatingGroup from './styled/rating-group'
export interface RatingGroupProps extends StyledRatingGroup.RootProps {}
export const RatingGroup = (props: RatingGroupProps) => {
const getChildren = children(() => props.children)
return (
<StyledRatingGroup.Root {...props}>
<Show when={getChildren()}>
<StyledRatingGroup.Label>{getChildren()}</StyledRatingGroup.Label>
</Show>
<StyledRatingGroup.Control>
<StyledRatingGroup.Context>
{(context) => (
<Index each={context().items}>
{(index) => (
<StyledRatingGroup.Item index={index()}>
<StyledRatingGroup.ItemContext>
{(item) => (
<Show when={item().highlighted} fallback={<StarIcon />}>
<StarIcon half={item().half} />
</Show>
)}
</StyledRatingGroup.ItemContext>
</StyledRatingGroup.Item>
)}
</Index>
)}
</StyledRatingGroup.Context>
</StyledRatingGroup.Control>
<StyledRatingGroup.HiddenInput />
</StyledRatingGroup.Root>
)
}
interface Props {
half?: boolean
}
const StarIcon = (props: Props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="inherit"
stroke="inherit"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<title>Star Icon</title>
<defs>
<linearGradient id="half">
<stop offset="50%" stop-color="var(--colors-color-palette-default)" />
<stop offset="50%" stop-color="var(--colors-bg-emphasized)" />
</linearGradient>
</defs>
<polygon
fill={props.half ? 'url(#half)' : 'inherit'}
points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"
/>
</svg>
)
3
Integrate Recipe
If you're not using @park-ui/preset
, add the following recipe to yourpanda.config.ts
:
import { ratingGroupAnatomy } from '@ark-ui/anatomy'
import { defineSlotRecipe } from '@pandacss/dev'
export const ratingGroup = defineSlotRecipe({
className: 'ratingGroup',
slots: ratingGroupAnatomy.keys(),
base: {
root: {
display: 'flex',
flexDirection: 'column',
gap: '1.5',
},
label: {
color: 'fg.default',
fontWeight: 'medium',
},
control: {
display: 'flex',
},
item: {
cursor: 'pointer',
transitionDuration: 'normal',
transitionProperty: 'color, fill',
transitionTimingFunction: 'default',
fill: 'bg.emphasized',
_highlighted: {
fill: 'colorPalette.default',
},
_focusVisible: {
outline: 'none',
},
},
},
defaultVariants: {
size: 'md',
},
variants: {
size: {
sm: {
control: {
gap: '0',
},
item: {
'& svg': {
width: '4',
height: '4',
},
},
label: {
textStyle: 'sm',
},
},
md: {
control: {
gap: '0.5',
},
item: {
'& svg': {
width: '5',
height: '5',
},
},
label: {
textStyle: 'sm',
},
},
lg: {
control: {
gap: '0.5',
},
item: {
'& svg': {
width: '6',
height: '6',
},
},
label: {
textStyle: 'md',
},
},
},
},
})