@shoelace-style/shoelace
Version:
A forward-thinking library of web components.
281 lines (278 loc) • 8.49 kB
JavaScript
import {
rating_styles_default
} from "./chunk.X2WW2TWJ.js";
import {
clamp
} from "./chunk.HF7GESMZ.js";
import {
LocalizeController
} from "./chunk.WLV3FVBR.js";
import {
SlIcon
} from "./chunk.E6QAPUBK.js";
import {
watch
} from "./chunk.CCJUT23E.js";
import {
component_styles_default
} from "./chunk.TUVJKY7S.js";
import {
ShoelaceElement
} from "./chunk.UYAO2JRR.js";
import {
__decorateClass
} from "./chunk.B3BW2AY6.js";
// src/components/rating/rating.component.ts
import { classMap } from "lit/directives/class-map.js";
import { eventOptions, property, query, state } from "lit/decorators.js";
import { html } from "lit";
import { styleMap } from "lit/directives/style-map.js";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
var SlRating = class extends ShoelaceElement {
constructor() {
super(...arguments);
this.localize = new LocalizeController(this);
this.hoverValue = 0;
this.isHovering = false;
this.label = "";
this.value = 0;
this.max = 5;
this.precision = 1;
this.readonly = false;
this.disabled = false;
this.getSymbol = () => '<sl-icon name="star-fill" library="system"></sl-icon>';
}
getValueFromMousePosition(event) {
return this.getValueFromXCoordinate(event.clientX);
}
getValueFromTouchPosition(event) {
return this.getValueFromXCoordinate(event.touches[0].clientX);
}
getValueFromXCoordinate(coordinate) {
const isRtl = this.localize.dir() === "rtl";
const { left, right, width } = this.rating.getBoundingClientRect();
const value = isRtl ? this.roundToPrecision((right - coordinate) / width * this.max, this.precision) : this.roundToPrecision((coordinate - left) / width * this.max, this.precision);
return clamp(value, 0, this.max);
}
handleClick(event) {
if (this.disabled) {
return;
}
this.setValue(this.getValueFromMousePosition(event));
this.emit("sl-change");
}
setValue(newValue) {
if (this.disabled || this.readonly) {
return;
}
this.value = newValue === this.value ? 0 : newValue;
this.isHovering = false;
}
handleKeyDown(event) {
const isLtr = this.localize.dir() === "ltr";
const isRtl = this.localize.dir() === "rtl";
const oldValue = this.value;
if (this.disabled || this.readonly) {
return;
}
if (event.key === "ArrowDown" || isLtr && event.key === "ArrowLeft" || isRtl && event.key === "ArrowRight") {
const decrement = event.shiftKey ? 1 : this.precision;
this.value = Math.max(0, this.value - decrement);
event.preventDefault();
}
if (event.key === "ArrowUp" || isLtr && event.key === "ArrowRight" || isRtl && event.key === "ArrowLeft") {
const increment = event.shiftKey ? 1 : this.precision;
this.value = Math.min(this.max, this.value + increment);
event.preventDefault();
}
if (event.key === "Home") {
this.value = 0;
event.preventDefault();
}
if (event.key === "End") {
this.value = this.max;
event.preventDefault();
}
if (this.value !== oldValue) {
this.emit("sl-change");
}
}
handleMouseEnter(event) {
this.isHovering = true;
this.hoverValue = this.getValueFromMousePosition(event);
}
handleMouseMove(event) {
this.hoverValue = this.getValueFromMousePosition(event);
}
handleMouseLeave() {
this.isHovering = false;
}
handleTouchStart(event) {
this.isHovering = true;
this.hoverValue = this.getValueFromTouchPosition(event);
event.preventDefault();
}
handleTouchMove(event) {
this.hoverValue = this.getValueFromTouchPosition(event);
}
handleTouchEnd(event) {
this.isHovering = false;
this.setValue(this.hoverValue);
this.emit("sl-change");
event.preventDefault();
}
roundToPrecision(numberToRound, precision = 0.5) {
const multiplier = 1 / precision;
return Math.ceil(numberToRound * multiplier) / multiplier;
}
handleHoverValueChange() {
this.emit("sl-hover", {
detail: {
phase: "move",
value: this.hoverValue
}
});
}
handleIsHoveringChange() {
this.emit("sl-hover", {
detail: {
phase: this.isHovering ? "start" : "end",
value: this.hoverValue
}
});
}
/** Sets focus on the rating. */
focus(options) {
this.rating.focus(options);
}
/** Removes focus from the rating. */
blur() {
this.rating.blur();
}
render() {
const isRtl = this.localize.dir() === "rtl";
const counter = Array.from(Array(this.max).keys());
let displayValue = 0;
if (this.disabled || this.readonly) {
displayValue = this.value;
} else {
displayValue = this.isHovering ? this.hoverValue : this.value;
}
return html`
<div
part="base"
class=${classMap({
rating: true,
"rating--readonly": this.readonly,
"rating--disabled": this.disabled,
"rating--rtl": isRtl
})}
role="slider"
aria-label=${this.label}
aria-disabled=${this.disabled ? "true" : "false"}
aria-readonly=${this.readonly ? "true" : "false"}
aria-valuenow=${this.value}
aria-valuemin=${0}
aria-valuemax=${this.max}
tabindex=${this.disabled ? "-1" : "0"}
=${this.handleClick}
=${this.handleKeyDown}
=${this.handleMouseEnter}
=${this.handleTouchStart}
=${this.handleMouseLeave}
=${this.handleTouchEnd}
=${this.handleMouseMove}
=${this.handleTouchMove}
>
<span class="rating__symbols">
${counter.map((index) => {
if (displayValue > index && displayValue < index + 1) {
return html`
<span
class=${classMap({
rating__symbol: true,
"rating__partial-symbol-container": true,
"rating__symbol--hover": this.isHovering && Math.ceil(displayValue) === index + 1
})}
role="presentation"
>
<div
style=${styleMap({
clipPath: isRtl ? `inset(0 ${(displayValue - index) * 100}% 0 0)` : `inset(0 0 0 ${(displayValue - index) * 100}%)`
})}
>
${unsafeHTML(this.getSymbol(index + 1))}
</div>
<div
class="rating__partial--filled"
style=${styleMap({
clipPath: isRtl ? `inset(0 0 0 ${100 - (displayValue - index) * 100}%)` : `inset(0 ${100 - (displayValue - index) * 100}% 0 0)`
})}
>
${unsafeHTML(this.getSymbol(index + 1))}
</div>
</span>
`;
}
return html`
<span
class=${classMap({
rating__symbol: true,
"rating__symbol--hover": this.isHovering && Math.ceil(displayValue) === index + 1,
"rating__symbol--active": displayValue >= index + 1
})}
role="presentation"
>
${unsafeHTML(this.getSymbol(index + 1))}
</span>
`;
})}
</span>
</div>
`;
}
};
SlRating.styles = [component_styles_default, rating_styles_default];
SlRating.dependencies = { "sl-icon": SlIcon };
__decorateClass([
query(".rating")
], SlRating.prototype, "rating", 2);
__decorateClass([
state()
], SlRating.prototype, "hoverValue", 2);
__decorateClass([
state()
], SlRating.prototype, "isHovering", 2);
__decorateClass([
property()
], SlRating.prototype, "label", 2);
__decorateClass([
property({ type: Number })
], SlRating.prototype, "value", 2);
__decorateClass([
property({ type: Number })
], SlRating.prototype, "max", 2);
__decorateClass([
property({ type: Number })
], SlRating.prototype, "precision", 2);
__decorateClass([
property({ type: Boolean, reflect: true })
], SlRating.prototype, "readonly", 2);
__decorateClass([
property({ type: Boolean, reflect: true })
], SlRating.prototype, "disabled", 2);
__decorateClass([
property()
], SlRating.prototype, "getSymbol", 2);
__decorateClass([
eventOptions({ passive: true })
], SlRating.prototype, "handleTouchMove", 1);
__decorateClass([
watch("hoverValue")
], SlRating.prototype, "handleHoverValueChange", 1);
__decorateClass([
watch("isHovering")
], SlRating.prototype, "handleIsHoveringChange", 1);
export {
SlRating
};