HextaUI

BêtaConstruit sur l’UI SDK 0.1.0-beta.1 de Vercel. Son API peut changer.

Rating

Une notation par étoiles sur l’UI SDK de Vercel, avec étoiles entières ou demi-étoiles, aperçu au survol, moyennes en lecture seule et prise en charge des formulaires.

Dépend de
  • @tabler/icons-react
  • @ui-sdk/core
  • @ui-sdk/react
  • class-variance-authority
"use client"

import * as React from "react"

import { Rating, RatingItem } from "@/components/ui/ui-sdk-rating"

const labels = ["Terrible", "Poor", "Okay", "Good", "Great"]

function UiSdkRatingDemo() {
  const [value, setValue] = React.useState<number | null>(4)

  return (
    <div className="flex flex-col items-center gap-2">
      <Rating
        aria-label="How was your stay?"
        size="lg"
        value={value}
        onValueChange={setValue}
        getValueLabel={(score, max) =>
          labels[score - 1] + ", " + score + " of " + max
        }
      >
        <RatingItem />
      </Rating>
      <p
        aria-hidden="true"
        className="min-h-5 text-sm text-muted-foreground tabular-nums"
      >
        {value ? labels[value - 1] : "Not rated yet"}
      </p>
    </div>
  )
}

export { UiSdkRatingDemo }
pnpm dlx shadcn@latest add https://hextaui.com/r/ui-sdk-rating.json

Ajoute le composant, les tokens de thème HextaUI et les composants HextaUI dont il dépend.

UI SDK ajoute un peu de CSS dans une couche de cascade nommée ui-sdk. Déclarez cette couche en haut de votre feuille de style globale, au-dessus de l’import Tailwind, pour que vos classes l’emportent.

@layer ui-sdk;

@import "tailwindcss";
import { Rating, RatingItem } from "@/components/ui/ui-sdk-rating"
<Rating aria-label="Rating" defaultValue={3}>
  <RatingItem />
</Rating>

Rating affiche son enfant une fois par étoile, mettez donc un RatingItem à l’intérieur. Chaque étoile dessine une étoile vide avec une pleine découpée selon --rating-fill, pour afficher exactement les demi-étoiles et les moyennes comme 4,3.

Rating
└── RatingItem

Avec step={0.5}, chaque moitié d’étoile est son propre radio, et les flèches avancent d’une demi-étoile.

import { Rating, RatingItem } from "@/components/ui/ui-sdk-rating"

function UiSdkRatingHalf() {
  return (
    <Rating aria-label="Rating" step={0.5} defaultValue={3.5}>
      <RatingItem />
    </Rating>
  )
}

export { UiSdkRatingHalf }

Une notation en lecture seule est une image nommée par getValueLabel, et elle affiche n’importe quelle note, comme une moyenne.

"use client"

import { Rating, RatingItem } from "@/components/ui/ui-sdk-rating"

function UiSdkRatingReadOnly() {
  return (
    <div className="flex items-center gap-2 text-sm">
      <Rating
        readOnly
        size="sm"
        value={4.3}
        getValueLabel={(value, max) =>
          "Rated " + value + " out of " + max + " stars"
        }
      >
        <RatingItem />
      </Rating>
      <span className="font-medium tabular-nums">4.3</span>
      <span className="text-muted-foreground">(1,284 reviews)</span>
    </div>
  )
}

export { UiSdkRatingReadOnly }

size vaut sm, default ou lg.

import { Rating, RatingItem } from "@/components/ui/ui-sdk-rating"

function UiSdkRatingSizes() {
  return (
    <div className="flex flex-col items-center gap-4">
      <Rating aria-label="Small rating" size="sm" defaultValue={3}>
        <RatingItem />
      </Rating>
      <Rating aria-label="Default rating" defaultValue={3}>
        <RatingItem />
      </Rating>
      <Rating aria-label="Large rating" size="lg" defaultValue={3}>
        <RatingItem />
      </Rating>
    </div>
  )
}

export { UiSdkRatingSizes }

Avec deselectable, appuyer à nouveau sur l’étoile cochée efface la note.

import { Rating, RatingItem } from "@/components/ui/ui-sdk-rating"

function UiSdkRatingDeselectable() {
  return (
    <Rating aria-label="Rating" deselectable defaultValue={2}>
      <RatingItem />
    </Rating>
  )
}

export { UiSdkRatingDeselectable }

Passez value et onValueChange, où null signifie pas encore de note. Un contrôleur issu de createRatingController() définit la note depuis n’importe où.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import {
  createRatingController,
  Rating,
  RatingItem,
} from "@/components/ui/ui-sdk-rating"

function UiSdkRatingControlled() {
  const [rating] = React.useState(createRatingController)
  const [value, setValue] = React.useState<number | null>(null)

  return (
    <div className="flex flex-col items-center gap-3">
      <Rating
        aria-label="Rating"
        controller={rating}
        value={value}
        onValueChange={setValue}
      >
        <RatingItem />
      </Rating>
      <div className="flex gap-2">
        <Button variant="outline" size="sm" onClick={() => rating.setValue(5)}>
          Rate 5
        </Button>
        <Button
          variant="ghost"
          size="sm"
          disabled={value === null}
          onClick={() => setValue(null)}
        >
          Clear
        </Button>
      </div>
      <p className="text-sm text-muted-foreground tabular-nums">
        {value === null ? "No score" : "Score: " + value}
      </p>
    </div>
  )
}

export { UiSdkRatingControlled }

Avec un name, la note est envoyée avec le formulaire. required bloque l’envoi tant qu’il n’y a pas de note, avec le message du navigateur.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import { Rating, RatingItem } from "@/components/ui/ui-sdk-rating"

function UiSdkRatingForm() {
  const [submitted, setSubmitted] = React.useState<string | null>(null)

  return (
    <form
      className="flex flex-col items-start gap-3"
      onSubmit={(event) => {
        event.preventDefault()
        setSubmitted(String(new FormData(event.currentTarget).get("score")))
      }}
      onReset={() => setSubmitted(null)}
    >
      <div className="flex flex-col gap-1.5">
        <span id="rating-form-label" className="text-sm font-medium">
          Rate this answer
        </span>
        <Rating aria-labelledby="rating-form-label" name="score" required>
          <RatingItem />
        </Rating>
      </div>
      <div className="flex gap-2">
        <Button type="submit" size="sm">
          Submit
        </Button>
        <Button type="reset" variant="outline" size="sm">
          Reset
        </Button>
      </div>
      <p role="status" className="text-sm text-muted-foreground">
        {submitted ? "Submitted score=" + submitted : "Nothing submitted yet."}
      </p>
    </form>
  )
}

export { UiSdkRatingForm }

Passez votre propre icône comme enfant de RatingItem et changez-la avec data-filled. tone règle la couleur.

import { IconHeart, IconHeartFilled } from "@tabler/icons-react"

import { Rating, RatingItem } from "@/components/ui/ui-sdk-rating"

function UiSdkRatingCustomIcon() {
  return (
    <Rating
      aria-label="How much did you like it?"
      defaultValue={3}
      tone="destructive"
    >
      <RatingItem>
        <IconHeart
          aria-hidden="true"
          className="size-6 group-data-[filled]/rating-item:hidden group-data-[partial]/rating-item:hidden"
        />
        <IconHeartFilled
          aria-hidden="true"
          className="hidden size-6 group-data-[filled]/rating-item:block group-data-[partial]/rating-item:block"
        />
      </RatingItem>
    </Rating>
  )
}

export { UiSdkRatingCustomIcon }

Une notation désactivée ne prend pas le focus, ne change pas et n’envoie rien.

import { Rating, RatingItem } from "@/components/ui/ui-sdk-rating"

function UiSdkRatingDisabled() {
  return (
    <Rating aria-label="Rating" disabled defaultValue={3}>
      <RatingItem />
    </Rating>
  )
}

export { UiSdkRatingDisabled }

Les étoiles partent de la droite, et une demi-étoile se remplit depuis son bord droit.

import { Rating, RatingItem } from "@/components/ui/ui-sdk-rating"

function UiSdkRatingRtl() {
  return (
    <div dir="rtl">
      <Rating aria-label="التقييم" step={0.5} defaultValue={2.5}>
        <RatingItem />
      </Rating>
    </div>
  )
}

export { UiSdkRatingRtl }
ToucheAction
TabEntre dans la notation, sur l’étoile cochée ou la première.
←→↑↓Passe à la note suivante ou précédente et la coche, en s’arrêtant aux extrémités sauf si loop est activé.
SpaceCoche l’étoile ciblée, ou la décoche si elle l’est déjà et que la notation est désélectionnable.
EnterEnvoie le formulaire.

Une notation est un groupe de radios avec un seul arrêt de tabulation. Chaque étoile contient un radio par note possible, nommé par getValueLabel : nommez donc les notes avec des mots courants, comme « Super, 5 sur 5 ». Les icônes d’étoile sont masquées aux lecteurs d’écran. Une notation en lecture seule est une seule image.

Les étoiles ne grossissent au survol et ne rétrécissent à l’appui que si le mouvement est autorisé. Sur écran tactile, chaque étoile reçoit un espacement supplémentaire, et en mode couleurs forcées les étoiles pleines prennent la couleur du texte et les vides le gris du système.

Chaque partie enveloppe la partie UI SDK du même nom et accepte toutes ses props, y compris render et un className qui peut être une fonction de son état.

PropTypePar défaut
value
number | null–
defaultValue
number | null–
onValueChange
(value: number | null, details) => void–
max
number5
step
number1
size
"sm" | "default" | "lg""default"
tone
"default" | "neutral" | "destructive""default"
deselectable
booleanfalse
readOnly
booleanfalse
disabled
booleanfalse
required
booleanfalse
name
string–
loop
booleanfalse
getValueLabelNomme chaque note pour son radio, et une notation en lecture seule.
(value: number, max: number) => string–
controllerIssu de createRatingController().
RatingController–
AttributDescription
data-slot="rating"Cible la notation en CSS.
data-sizeLa taille : sm, default ou lg.
data-toneLe ton : default, neutral ou destructive.
data-readonlyPrésent en lecture seule.
data-disabledPrésent lorsque l’élément est désactivé.
--rating-colorLa couleur des étoiles pleines, réglée par tone.
PropTypePar défaut
childrenL’icône de l’étoile. Par défaut, une étoile Tabler remplie selon --rating-fill.
ReactNode–
render
ReactElement | (props, state) => ReactElement–
AttributDescription
data-slot="rating-item"Cible une étoile en CSS.
data-filledPrésent sur les étoiles entièrement couvertes par la note affichée.
data-partialPrésent sur l’étoile partiellement couverte par la note affichée.
data-checkedPrésent sur l’étoile où se termine la note.
data-highlightedPrésent sur les étoiles jusqu’à la note prévisualisée par le pointeur.
--rating-fillLa part de l’étoile couverte par la note affichée, de 0 à 1.