Skip to content

Commit 4637055

Browse files
committed
add SfRating
1 parent 4ab3ddd commit 4637055

File tree

3 files changed

+81
-0
lines changed

3 files changed

+81
-0
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { component$ } from '@builder.io/qwik';
2+
import { SfRatingSize } from '../../shared/SfRating';
3+
import { SfIconStar, SfIconStarFilled, SfIconStarHalf } from '../SfIcons';
4+
import { SfRatingProps } from './types';
5+
6+
const sizeClasses = {
7+
[SfRatingSize.xs]: 'text-xs',
8+
[SfRatingSize.sm]: 'text-sm',
9+
[SfRatingSize.base]: 'text-base',
10+
[SfRatingSize.lg]: 'text-lg',
11+
[SfRatingSize.xl]: 'text-xl',
12+
};
13+
14+
export const SfRating = component$<SfRatingProps>(
15+
({
16+
size = SfRatingSize.base,
17+
max = 5,
18+
value = 0,
19+
halfIncrement,
20+
ariaLabel,
21+
class: _class,
22+
...attributes
23+
}) => {
24+
const clamp = (value: number, min: number, max: number) => {
25+
return Math.min(Math.max(value, min), max);
26+
};
27+
28+
const roundToNearest = (value: number, step: number) => {
29+
return Math.round(value / step) * step;
30+
};
31+
32+
const precision = halfIncrement ? 0.5 : 1;
33+
const ratingValue = clamp(roundToNearest(value, precision), 0, max);
34+
const partiallyFilled = Number(!Number.isInteger(ratingValue)); // 0 or 1
35+
const filled = Math.ceil(ratingValue - partiallyFilled);
36+
const empty = max - filled - partiallyFilled;
37+
const label = ariaLabel ?? `${value} out of ${max}`;
38+
39+
return (
40+
<div
41+
role="img"
42+
aria-label={label}
43+
title={label}
44+
class={`inline-flex items-center text-warning-500 ${sizeClasses[size]} ${_class}`}
45+
data-testid="rating"
46+
{...attributes}
47+
>
48+
{[...Array(filled).keys()].map((key) => (
49+
<SfIconStarFilled
50+
aria-hidden="true"
51+
class="w-[1.5em] h-[1.5em]"
52+
key={key}
53+
/>
54+
))}
55+
{Boolean(partiallyFilled) && (
56+
<SfIconStarHalf aria-hidden="true" class="w-[1.5em] h-[1.5em]" />
57+
)}
58+
{[...Array(empty).keys()].map((key) => (
59+
<SfIconStar
60+
aria-hidden="true"
61+
class="text-disabled-500 w-[1.5em] h-[1.5em]"
62+
key={key}
63+
/>
64+
))}
65+
</div>
66+
);
67+
}
68+
);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './types';
2+
3+
export { SfRating } from './SfRating';
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { SfRatingSize } from '../../shared/SfRating';
2+
3+
export type SfRatingProps = {
4+
class?: string;
5+
value?: number;
6+
max?: number;
7+
size?: `${SfRatingSize}`;
8+
halfIncrement?: boolean;
9+
ariaLabel?: string;
10+
};

0 commit comments

Comments
 (0)