wj-elements
Version:
WebJET Elements is a modern set of user interface tools harnessing the power of web components designed to simplify web application development.
323 lines (322 loc) • 10.7 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
import WJElement from "./wje-element.js";
const styles = "/*\n[ WJ Rate ]\n*/\n\n:host {\n display: flex;\n color: var(--wje-rate-color);\n}\n\n:host([readonly]) .wje-rate-icon {\n cursor: default;\n}\n\n.native-rate {\n position: relative;\n display: flex;\n gap: var(--wje-rate-gap);\n}\n\n.wje-rate-icon {\n position: relative;\n cursor: pointer;\n}\n\n.selected {\n color: var(--wje-rate-selected-color);\n}\n\n:host(:not([readonly])) .wje-rate-icon:hover {\n transform: scale(1.2);\n}\n\nwje-icon:first-child {\n color: var(--wje-rate-color);\n}\n\nwje-icon:last-child {\n color: currentColor;\n position: absolute;\n top: 0;\n left: 0;\n}\n\n:host([disabled]) .native-rate {\n pointer-events: none;\n opacity: 0.5;\n}\n";
class Rate extends WJElement {
/**
* Creates an instance of Rate.
* @class
*/
constructor() {
super();
/**
* Sets the hover value of the rating component.
* @type {string}
*/
__publicField(this, "className", "Rate");
/**
* Event handler for the mouse enter event.
* @param {Event} e The event.
*/
__publicField(this, "onMouseEnter", (e) => {
e.preventDefault();
this.hoverValue = this.getValueFromXPosition(e.clientX);
this.changeRate();
});
/**
* Event handler for the mouse leave event.
* @param {Event} e The event.
*/
__publicField(this, "onMouseLeave", (e) => {
e.preventDefault();
this.hoverValue = 0;
this.changeRate();
});
/**
* Event handler for the mouse move event.
* @param {Event} e The event.
*/
__publicField(this, "onMouseMove", (e) => {
e.preventDefault();
let newValue = +this.getValueFromXPosition(e.clientX);
if (newValue !== +this.hoverValue) {
this.hoverValue = newValue;
this.changeRate();
}
});
/**
* Event handler for the touch start event.
* @param {Event} e The event.
*/
__publicField(this, "onTouchStart", (e) => {
e.preventDefault();
this.hoverValue = this.getValueFromXPosition(e.touches[0].clientX);
this.changeRate();
});
/**
* Event handler for the touch end event.
* @param {Event} e The event.
*/
__publicField(this, "onTouchEnd", (e) => {
e.preventDefault();
this.hoverValue = 0;
this.changeRate();
});
/**
* Event handler for the touch move event.
* @param {Event} e The event.
*/
__publicField(this, "onTouchMove", (e) => {
e.preventDefault();
this.hoverValue = this.getValueFromXPosition(e.touches[0].clientX);
this.changeRate();
});
/**
* Event handler for the click event.
* @param {Event} e The event.
*/
__publicField(this, "onClick", (e) => {
e.preventDefault();
this.value = +this.hoverValue;
});
}
/**
* Sets the precision of the rating component.
* @param {number} value The value to set.
*/
set precision(value) {
this.setAttribute("precision", value);
}
/**
* Gets the precision of the rating component.
* @returns {number} The value of the precision.
*/
get precision() {
return this.hasAttribute("precision") ? +this.getAttribute("precision") : 1;
}
/**
* Sets the maximum value of the rating component.
* @param {number} value The value to set.
*/
set max(value) {
this.setAttribute("max", value);
}
/**
* Gets the maximum value of the rating component.
* @returns {number} The value of the maximum value.
*/
get max() {
return this.hasAttribute("icons") ? this.icons.length : +this.getAttribute("max");
}
/**
* Sets the icons of the rating component.
* @param {Array<string>} value The value to set.
*/
set icons(value) {
return value;
}
/**
* Gets the icons of the rating component.
* @returns {Array<string>} The value of the icons.
*/
get icons() {
return this.hasAttribute("icons") ? JSON.parse(this.getAttribute("icons").replace(/'/g, '"')) : ["star"];
}
/**
* Sets the value of the rating component.
* @param {number} value The value to set.
*/
set value(value) {
this.setAttribute("value", value);
}
/**
* Gets the value of the rating component.
* @returns {number} The value of the rating component.
*/
get value() {
return this.hasAttribute("value") ? +this.getAttribute("value") : 0;
}
/**
* Returns the CSS styles for the component.
* @static
* @returns {CSSStyleSheet}
*/
static get cssStyleSheet() {
return styles;
}
/**
* Returns the list of attributes to observe for changes.
* @static
* @returns {Array<string>}
*/
static get observedAttributes() {
return ["is-hover", "value", "max", "disabled", "readonly"];
}
/**
* Called when an attribute changes.
* @param {string} name The name of the attribute that changed.
* @param {string} old The old value of the attribute.
* @param {string} newName The new value of the attribute.
*/
attributeChangedCallback(name, old, newName) {
if (super.attributeChangedCallback) {
super.attributeChangedCallback(name, old, newName);
}
if (old !== newName && name !== "is-hover") {
this.syncAria();
}
}
/**
* Sets up the attributes for the component.
*/
setupAttributes() {
this.isShadowRoot = "open";
this.syncAria();
}
/**
* Draws the component for the rating component.
* @returns {DocumentFragment}
*/
draw() {
let fragment = document.createDocumentFragment();
let native = document.createElement("div");
native.setAttribute("part", "native");
native.classList.add("native-rate");
this.native = native;
if (this.hasAttribute("icons")) {
let icons = this.icons;
for (let i = 0; i < icons.length; i++) {
native.appendChild(this.createIcons(i));
}
} else {
for (let i = 0; i < this.max; i++) {
native.appendChild(this.createIcons(i));
}
}
this.changeRate();
fragment.appendChild(native);
return fragment;
}
/**
* Adds event listeners after the component is drawn.
*/
afterDraw() {
this.syncAria();
if (this.hasAttribute("disabled") || this.hasAttribute("readonly")) {
return;
}
this.addEventListener("mouseenter", this.onMouseEnter);
this.addEventListener("mouseleave", this.onMouseLeave);
this.addEventListener("mousemove", this.onMouseMove);
this.addEventListener("touchstart", this.onTouchStart);
this.addEventListener("touchend", this.onTouchEnd);
this.addEventListener("touchmove", this.onTouchMove);
this.addEventListener("click", this.onClick);
}
/**
* Creates the icons for the rating component.
* @param {number} i The index of the icon.
* @returns {Element} The icon element.
*/
createIcons(i) {
let div = document.createElement("div");
div.classList.add("wje-rate-icon");
let icon = this.getIcons(i);
let clone = icon.cloneNode(true);
div.appendChild(icon);
div.appendChild(clone);
return div;
}
/**
* Changes the rate of the rating component.
*/
changeRate() {
const icons = this.native.children;
const rateValue = this.value !== this.hoverValue && this.hoverValue !== 0 && this.hoverValue !== void 0 ? this.hoverValue : this.value;
for (let i = 0; i < icons.length; i++) {
const icon = icons[i];
const firstIcon = icon.querySelector("wje-icon:first-child");
const lastIcon = icon.querySelector("wje-icon:last-child");
const isSelected = i < rateValue;
const isPartial = rateValue > i && rateValue < i + 1;
if (isSelected) {
icon.classList.add("selected");
if (this.hasAttribute("selected") && this.getAttribute("selected") === "filled") {
lastIcon.setAttribute("filled", "");
}
} else {
icon.classList.remove("selected");
lastIcon.removeAttribute("filled");
}
if (isPartial) {
const percent = ((rateValue - i) * 100).toFixed(2);
icon.classList.add("half");
firstIcon.style.clipPath = `inset(0 0 0 ${percent}%)`;
lastIcon.style.clipPath = `inset(0 ${100 - percent}% 0 0)`;
lastIcon.removeAttribute("hidden");
} else {
icon.classList.remove("half");
firstIcon.style.clipPath = ``;
lastIcon.style.clipPath = ``;
lastIcon.setAttribute("hidden", "");
}
icon.setAttribute("data-index", i);
icon.setAttribute("data-rate", rateValue);
}
this.syncAria();
}
/**
* Sync ARIA attributes on host.
*/
syncAria() {
this.setAriaState({
role: "slider",
valuemin: 0,
valuemax: this.max,
valuenow: this.value,
disabled: this.hasAttribute("disabled"),
readonly: this.hasAttribute("readonly")
});
}
/**
* Returns the icons for the rating component.
* @param {number} index The index of the icon.
* @returns {Element} The icon element.
*/
getIcons(index) {
let icon = document.createElement("wje-icon");
icon.setAttribute("name", this.max ? this.icons[0] : this.icons[index]);
if (this.hasAttribute("filled")) icon.setAttribute("filled", "");
return icon;
}
/**
* Returns the value from the x position.
* @param {number} coordinate The x coordinate.
* @returns {number} The value from the x position.
*/
getValueFromXPosition(coordinate) {
const { left, right, width } = this.native.getBoundingClientRect();
const value = this.roundToPrecision((coordinate - left) / width * this.max, this.precision);
return Math.min(Math.max(value, 0), this.max);
}
/**
* Rounds a given number to the nearest specified precision.
* @param {number} numberToRound The number to be rounded.
* @param {number} [precision] The precision to which the number should be rounded.
* @returns {number} - The rounded number.
* @example
* roundToPrecision(2.3); // Returns 2.5
* roundToPrecision(2.3, 0.1); // Returns 2.3
* roundToPrecision(2.6, 1); // Returns 3
*/
roundToPrecision(numberToRound, precision = 0.5) {
const multiplier = 1 / precision;
return Math.ceil(numberToRound * multiplier) / multiplier;
}
}
Rate.define("wje-rate", Rate);
export {
Rate as default
};
//# sourceMappingURL=wje-rate.js.map