HextaUI

BetaGebaut auf Vercels UI SDK 0.1.0-beta.1. Die API kann sich ändern.

Rating

Eine Sternebewertung auf Vercels UI SDK mit ganzen oder halben Sternen, Hover-Vorschau, schreibgeschützten Durchschnitten und Formularunterstützung.

Abhängig von
  • @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

Fügt die Komponente, die HextaUI-Theme-Tokens und alle HextaUI-Komponenten hinzu, von denen sie abhängt.

UI SDK bringt etwas eigenes CSS in einem Cascade Layer namens ui-sdk mit. Deklariere diesen Layer ganz oben im globalen Stylesheet, über dem Tailwind-Import, damit deine Klassen gewinnen.

@layer ui-sdk;

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

Rating rendert sein Kind einmal pro Stern, also setze ein RatingItem hinein. Jeder Stern zeichnet einen leeren Stern mit einem gefüllten, der auf --rating-fill zugeschnitten ist, sodass halbe Sterne und Durchschnitte wie 4,3 exakt erscheinen.

Rating
└── RatingItem

Mit step={0.5} ist jede Sternhälfte ein eigenes Radio, und die Pfeiltasten bewegen sich um einen halben Stern.

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 }

Eine schreibgeschützte Bewertung ist ein Bild, benannt durch getValueLabel, und zeigt jede Punktzahl, etwa einen Durchschnitt.

"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 ist sm, default oder 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 }

Mit deselectable löscht ein erneuter Druck auf den gewählten Stern die Punktzahl.

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

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

export { UiSdkRatingDeselectable }

Übergib value und onValueChange, wobei null „noch keine Punktzahl“ bedeutet. Ein Controller aus createRatingController() setzt die Punktzahl von überall.

"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 }

Mit einem name wird die Punktzahl mit dem Formular gesendet. required blockiert das Absenden, bis es eine Punktzahl gibt, mit der Meldung des Browsers.

"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 }

Übergib dein eigenes Symbol als Kind von RatingItem und wechsle es mit data-filled. tone legt die Farbe fest.

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 }

Eine deaktivierte Bewertung lässt sich weder fokussieren noch ändern und sendet nichts.

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

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

export { UiSdkRatingDisabled }

Die Sterne beginnen rechts, und ein halber Stern füllt sich von seinem rechten Rand.

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 }
TasteAktion
TabSpringt in die Bewertung, auf den gewählten oder den ersten Stern.
←→↑↓Wechselt zur nächsten oder vorherigen Punktzahl und wählt sie, an den Enden ist Schluss, außer loop ist gesetzt.
SpaceWählt den fokussierten Stern, oder hebt die Auswahl auf, wenn er gewählt ist und die Bewertung abwählbar ist.
EnterSendet das Formular ab.

Eine Bewertung ist eine Radio-Gruppe mit einem Tab-Stopp. Jeder Stern enthält ein Radio pro wählbarer Punktzahl, benannt durch getValueLabel. Benenne Punktzahlen also mit Worten, die Menschen benutzen, etwa „Super, 5 von 5“. Die Sternsymbole sind für Screenreader verborgen. Eine schreibgeschützte Bewertung ist stattdessen ein einzelnes Bild.

Sterne wachsen beim Hovern und schrumpfen beim Drücken nur, wenn Bewegung erlaubt ist. Auf Touchscreens bekommt jeder Stern zusätzlichen Innenabstand, und im Modus „Erzwungene Farben“ nutzen gefüllte Sterne die Textfarbe und leere das Systemgrau.

Jeder Teil umhüllt den gleichnamigen UI-SDK-Teil und nimmt alle seine Props an, auch render und einen className, der eine Funktion des Zustands sein kann.

PropTypStandard
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
getValueLabelBenennt jede Punktzahl für ihr Radio und für eine schreibgeschützte Bewertung.
(value: number, max: number) => string–
controllerAus createRatingController().
RatingController–
AttributBeschreibung
data-slot="rating"Spricht die Bewertung in CSS an.
data-sizeDie Größe: sm, default oder lg.
data-toneDer Ton: default, neutral oder destructive.
data-readonlyVorhanden, wenn schreibgeschützt.
data-disabledVorhanden, wenn deaktiviert.
--rating-colorDie Farbe gefüllter Sterne, gesetzt durch tone.
PropTypStandard
childrenDas Symbol des Sterns. Standard ist ein Tabler-Stern, der sich nach --rating-fill füllt.
ReactNode–
render
ReactElement | (props, state) => ReactElement–
AttributBeschreibung
data-slot="rating-item"Spricht einen Stern in CSS an.
data-filledVorhanden auf Sternen, die die angezeigte Punktzahl ganz abdeckt.
data-partialVorhanden auf dem Stern, den die angezeigte Punktzahl teilweise abdeckt.
data-checkedVorhanden auf dem Stern, auf dem die Punktzahl endet.
data-highlightedVorhanden auf den Sternen bis zur Punktzahl, die ein Zeiger in der Vorschau zeigt.
--rating-fillWie viel des Sterns die angezeigte Punktzahl abdeckt, von 0 bis 1.