GrRating
Берут, когда оценка ставится в один клик.
Когда брать
- оценка ставится в один клик — отзыв, качество поддержки, сложность задачи;
- оценка дробная —
allowHalfдля половинок; - рядом нужна расшифровка —
textsподписывает ступени словами: «Плохо», «Отлично»; - оценку показывают, а не ставят —
readonlyдля средней оценки в карточке товара.
Когда взять другое
| Нужно | Берите |
|---|---|
| Шкала числовая и широкая | GrSlider |
| Нужно точное число | GrNumberInput |
| Вариантов немного и они названы | GrRadioGroup |
| Показать долю, а не оценку | GrProgressBar |
Оценка словами
<GrRating v-model="score" :texts="['Ужасно', 'Плохо', 'Нормально', 'Хорошо', 'Отлично']" show-text />
texts — по одной подписи на деление. Подпись уходит и в видимый текст, и в
aria-valuetext («4 из 5, хорошо»): в этом и смысл рейтинга — диктор читает
оценку словами, а не голым числом. Дробное значение округляется вверх до своего
деления, массив короче max оставляет верхние деления без подписи.
formatText сильнее texts для видимого текста — у потребителя может быть своя
формулировка.
Предпросмотр
Наведение показывает оценку под курсором и эмитит hoverChange; уход со шкалы
и потеря фокуса возвращают модель и шлют hoverChange(null).
Обработчик висит на самой шкале, а не на компоненте целиком: подпись лежит рядом
со шкалой, и курсор, переведённый на неё, обязан гасить предпросмотр — иначе он
залипает. В readonly и disabled предпросмотра нет вовсе.
С клавиатуры предпросмотра не существует: стрелки сразу фиксируют оценку, и
hoverChange там не эмитится.
Компактный вид
<GrRating :model-value="3" readonly compact show-text />
compact рисует только заполненные символы — для таблиц и списков, где пять
звёзд в каждой строке съедают ширину. Половинка считается символом: «2,5»
рисуется тремя. Для диктора компактный режим ничем не отличается — роль и
текстовая подпись те же.
Режимы и доступность
Интерактивная шкала реализует slider-паттерн: role="slider",
aria-valuemin/max/now/valuetext, стрелки, Home/End. Режим только для
чтения — role="img" с текстовой подписью, чтобы оценка читалась одной фразой,
а не набором элементов.
disabled гасит шкалу токеном --gr-disabled-fg, а не прозрачностью:
opacity разбавляет выверенные на AA цвета.
Оформление
| Точка | Что задаёт |
|---|---|
size | xs…lg, читается из GrConfigProvider |
tone | цвет заливки из шкалы тонов |
--gr-rating-color | цвет заливки точечно, сильнее tone |
--gr-rating-void-color | цвет незаполненного символа |
icon / слот #symbol | свой символ вместо встроенной звезды |
Символ по умолчанию — инлайновый SVG: иконочные маски i-lucide-star дают
только контур. Свой символ — Vue-компонент или класс иконки вашей UnoCSS-сборки
(i-lucide-* требует вашего presetIcons, см.
«Иконки»).
Нативная форма
Проп name рендерит input[type="hidden"] со значением оценки. 0 — «не
выбрано»: input не рендерится, как у неотмеченной радиокнопки.
Playground 14
Загружается…
<GrRating />Установка
npm i @feugene/granularityИмпорт
import { GrRating } from '@feugene/granularity/components/GrRating'API
Props
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
tone | "primary" | "neutral" | "success" | "warning" | "danger" | "info" | "slate" | "azure" | undefined | "warning" | Тон заливки; точечно перекрывается переменной `--gr-rating-color`. |
icon | string | Component | undefined | undefined | Символ вместо встроенной звезды: Vue-компонент либо класс иконки вашей UnoCSS-сборки (`'i-lucide-heart'` — тогда нужен ваш `presetIcons`, см. `docs/installation.md`). |
disabled | boolean | undefined | false | — |
readonly | boolean | undefined | false | Только показ: без ввода и фокуса. |
invalid | boolean | undefined | false | Визуальное и ARIA-состояние ошибки. |
required | boolean | undefined | false | Обязательное поле (`aria-required`). |
size | "xs" | "sm" | "md" | "lg" | undefined | undefined | — |
ariaLabel | string | undefined | undefined | — |
clearable | boolean | undefined | false | Повторный клик по текущей оценке сбрасывает её в `0`. |
name | string | undefined | undefined | Имя для нативной формы: hidden input со значением; `0` — «не выбрано», input не рендерится. |
max | number | undefined | 5 | Количество символов шкалы. |
compact | boolean | undefined | false | Компактный вид: рисуются только заполненные символы. Осмысленно вместе с `readonly` — в списках и таблицах пять звёзд в каждой строке съедают ширину. |
allowHalf | boolean | undefined | false | Половинчатые оценки: клик по левой половине символа даёт `.5`. |
showText | boolean | undefined | false | Показывать числовую подпись справа от шкалы. |
formatText | ((value: number) => string) | undefined | undefined | Формат подписи. По умолчанию — само значение. Сильнее `texts`. |
texts | string[] | undefined | undefined | Подписи по делениям: «3 из 5, нормально» вместо «3 из 5». Уходят и в видимый текст, и в `aria-valuetext` — ради этого рейтинг и существует. Массив короче `max` оставляет верхние деления без подписи. |
modelValueобязательный | number | — | Текущая оценка. Дробная (`3.5`) поддерживается при `allowHalf`. |
Slots
| Slot | Type | Описание |
|---|---|---|
symbol | { index: number; filled: boolean; } | Свой символ вместо звезды. `filled` — закрашенная половина символа. |
text | { value: number; } | Подпись рядом с оценкой. |
Events
| Event | Type | Описание |
|---|---|---|
update:modelValue | [value: number] | — |
change | [value: number] | — |
clear | [] | — |
focus | [event: FocusEvent] | — |
blur | [event: FocusEvent] | — |
hoverChange | [value: number | null] | — |
Methods / Expose
| Methods / Expose | Type | Описание |
|---|---|---|
focus | () => void | — |
blur | () => void | — |
Примеры 3
Базовая оценка
Оценка в один клик: v-model — число, show-text печатает значение рядом. Шкала фокусируется и управляется стрелками, Home/End.
Score: 4 — click a star, or use arrow keys, Home / End.
<script setup lang="ts">
import { ref } from 'vue'
import { GrRating } from '@feugene/granularity'
const score = ref(4)
</script>
<template>
<div class="grid gap-4">
<GrRating v-model="score" show-text aria-label="Rate the delivery" />
<p class="text-sm text-[var(--gr-muted-fg)]">
Score: <code>{{ score }}</code> — click a star, or use arrow keys, Home / End.
</p>
</div>
</template>Шкала — role="slider" с aria-valuenow/aria-valuetext, поэтому скринридер объявляет «4 из 5», а не пять безымянных иконок.
Половинки, сброс и только чтение
allow-half даёт половинчатые оценки (клик по левой половине символа), clearable сбрасывает повторным кликом, readonly показывает чужие оценки без ввода.
- Anna K.
Arrived a day earlier than promised.
- Mark T.
Good quality, packaging could be better.
- Elena P.
Exactly as described.
<script setup lang="ts">
import { ref } from 'vue'
import { GrRating } from '@feugene/granularity'
const myScore = ref(3.5)
const reviews = [
{ author: 'Anna K.', score: 5, text: 'Arrived a day earlier than promised.' },
{ author: 'Mark T.', score: 3.5, text: 'Good quality, packaging could be better.' },
{ author: 'Elena P.', score: 4, text: 'Exactly as described.' },
]
</script>
<template>
<div class="grid gap-6">
<div class="grid gap-2">
<span class="text-sm font-medium">Your rating</span>
<GrRating
v-model="myScore"
allow-half
clearable
show-text
:format-text="(v) => (v ? `${v} / 5` : 'Not rated')"
aria-label="Your rating"
/>
</div>
<ul class="grid gap-3">
<li v-for="review in reviews" :key="review.author" class="grid gap-1">
<div class="flex items-center gap-2">
<GrRating :model-value="review.score" readonly allow-half size="sm" />
<span class="text-sm font-medium">{{ review.author }}</span>
</div>
<p class="text-sm text-[var(--gr-muted-fg)]">
{{ review.text }}
</p>
</li>
</ul>
</div>
</template>В режиме readonly шкала становится role="img" с оценкой в подписи — она не попадает в таб-порядок и не притворяется контролом.
Свой символ, тон и размер
Символ меняется пропом icon (любая UnoCSS-иконка) или слотом #symbol, цвет — тоном либо переменной --gr-rating-color, размер — size или --gr-rating-symbol-size.
<script setup lang="ts">
import { ref } from 'vue'
import { GrRating } from '@feugene/granularity'
const likes = ref(3)
const difficulty = ref(2)
const size = ref(4)
// Подписи по делениям: диктор читает «4 из 5, хорошо», а не голое число.
const service = ref(4)
const serviceTexts = ['Ужасно', 'Плохо', 'Нормально', 'Хорошо', 'Отлично']
</script>
<template>
<div class="grid gap-6">
<div class="grid gap-2">
<span class="text-sm font-medium">Custom symbol and tone</span>
<GrRating
v-model="likes"
icon="i-lucide-heart"
tone="danger"
aria-label="How much you liked it"
/>
</div>
<div class="grid gap-2">
<span class="text-sm font-medium">Own colour via CSS variable</span>
<GrRating
v-model="difficulty"
:max="4"
style="--gr-rating-color: var(--gr-info)"
aria-label="Difficulty"
/>
</div>
<div class="grid gap-2">
<span class="text-sm font-medium">Labels per step</span>
<GrRating
v-model="service"
:texts="serviceTexts"
show-text
aria-label="Service quality"
/>
</div>
<div class="grid gap-2">
<span class="text-sm font-medium">Compact read-only (for tables and lists)</span>
<div class="flex items-center gap-6">
<GrRating :model-value="3" readonly compact show-text aria-label="Compact rating" />
<GrRating :model-value="4.5" readonly compact allow-half show-text aria-label="Compact half rating" />
</div>
</div>
<div class="grid gap-2">
<span class="text-sm font-medium">Sizes and disabled</span>
<div class="flex items-center gap-6">
<GrRating v-model="size" size="sm" aria-label="Small" />
<GrRating v-model="size" size="md" aria-label="Medium" />
<GrRating v-model="size" size="lg" aria-label="Large" />
<GrRating :model-value="2" disabled aria-label="Disabled" />
</div>
</div>
</div>
</template>Доступность
- Паттерн APG
slider (интерактивный) / img (readonly)- Клавиши
→/↑— больше,←/↓— меньше,Home/End— к краям