@coreui/coreui-pro
Version:
The most popular front-end framework for developing responsive, mobile-first projects on the web rewritten by the CoreUI Team
468 lines (441 loc) • 18.8 kB
JavaScript
/*!
* CoreUI rating.js v5.23.0 (https://coreui.io)
* Copyright 2025 The CoreUI Team (https://github.com/orgs/coreui/people)
* Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE)
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./util/sanitizer.js'), require('./util/index.js'), require('./tooltip.js')) :
typeof define === 'function' && define.amd ? define(['exports', './base-component', './dom/event-handler', './dom/manipulator', './dom/selector-engine', './util/sanitizer', './util/index', './tooltip'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Rating = {}, global.BaseComponent, global.EventHandler, global.Manipulator, global.SelectorEngine, global.Sanitizer, global.Index, global.Tooltip));
})(this, (function (exports, BaseComponent, EventHandler, Manipulator, SelectorEngine, sanitizer_js, index_js, Tooltip) { 'use strict';
/**
* --------------------------------------------------------------------------
* CoreUI PRO rating.js
* License (https://coreui.io/pro/license/)
* --------------------------------------------------------------------------
*/
/**
* Constants
*/
const NAME = 'rating';
const DATA_KEY = 'coreui.rating';
const EVENT_KEY = `.${DATA_KEY}`;
const DATA_API_KEY = '.data-api';
const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']);
const EVENT_CHANGE = `change${EVENT_KEY}`;
const EVENT_CLICK = `click${EVENT_KEY}`;
const EVENT_FOCUSIN = `focusin${EVENT_KEY}`;
const EVENT_FOCUSOUT = `focusout${EVENT_KEY}`;
const EVENT_HOVER = `hover${EVENT_KEY}`;
const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`;
const EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`;
const EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}`;
const CLASS_NAME_ACTIVE = 'active';
const CLASS_NAME_DISABLED = 'disabled';
const CLASS_NAME_RATING = 'rating';
const CLASS_NAME_RATING_ITEM = 'rating-item';
const CLASS_NAME_RATING_ITEM_ICON = 'rating-item-icon';
const CLASS_NAME_RATING_ITEM_CUSTOM_ICON = 'rating-item-custom-icon';
const CLASS_NAME_RATING_ITEM_CUSTOM_ICON_ACTIVE = 'rating-item-custom-icon-active';
const CLASS_NAME_RATING_ITEM_INPUT = 'rating-item-input';
const CLASS_NAME_RATING_ITEM_LABEL = 'rating-item-label';
const CLASS_NAME_READONLY = 'readonly';
const SELECTOR_DATA_TOGGLE = '[data-coreui-toggle="rating"]';
const SELECTOR_RATING_ITEM_INPUT = '.rating-item-input';
const SELECTOR_RATING_ITEM_LABEL = '.rating-item-label';
// js-docs-start svg-allow-list
const svgAllowList = {
...sanitizer_js.DefaultAllowlist,
svg: ['xmlns', 'version', 'baseprofile', 'width', 'height', 'viewbox', 'preserveaspectratio', 'aria-hidden', 'role', 'focusable'],
g: ['id', 'class', 'transform', 'style'],
path: ['id', 'class', 'd', 'fill', 'fill-opacity', 'fill-rule', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-opacity'],
circle: ['id', 'class', 'cx', 'cy', 'r', 'fill', 'fill-opacity', 'stroke', 'stroke-width', 'stroke-opacity'],
rect: ['id', 'class', 'x', 'y', 'width', 'height', 'rx', 'ry', 'fill', 'fill-opacity', 'stroke', 'stroke-width', 'stroke-opacity'],
ellipse: ['id', 'class', 'cx', 'cy', 'rx', 'ry', 'fill', 'fill-opacity', 'stroke', 'stroke-width', 'stroke-opacity'],
line: ['id', 'class', 'x1', 'y1', 'x2', 'y2', 'stroke', 'stroke-width', 'stroke-opacity'],
polygon: ['id', 'class', 'points', 'fill', 'fill-opacity', 'stroke', 'stroke-width', 'stroke-opacity'],
polyline: ['id', 'class', 'points', 'fill', 'fill-opacity', 'stroke', 'stroke-width', 'stroke-opacity'],
text: ['id', 'class', 'x', 'y', 'dx', 'dy', 'text-anchor', 'font-family', 'font-size', 'font-weight', 'fill', 'fill-opacity', 'stroke', 'stroke-width', 'stroke-opacity'],
tspan: ['id', 'class', 'x', 'y', 'dx', 'dy', 'text-anchor', 'font-family', 'font-size', 'font-weight', 'fill', 'fill-opacity', 'stroke', 'stroke-width', 'stroke-opacity'],
defs: [],
symbol: ['id', 'class', 'viewbox', 'preserveaspectratio'],
use: ['id', 'class', 'x', 'y', 'width', 'height', 'href'],
image: ['id', 'class', 'x', 'y', 'width', 'height', 'href', 'preserveaspectratio', 'xlink:href'],
pattern: ['id', 'class', 'x', 'y', 'width', 'height', 'patternunits', 'patterncontentunits', 'patterntransform', 'preserveaspectratio'],
lineargradient: ['id', 'class', 'gradientunits', 'x1', 'y1', 'x2', 'y2', 'spreadmethod', 'gradienttransform'],
radialgradient: ['id', 'class', 'gradientunits', 'cx', 'cy', 'r', 'fx', 'fy', 'spreadmethod', 'gradienttransform'],
mask: ['id', 'class', 'x', 'y', 'width', 'height', 'maskunits', 'maskcontentunits', 'masktransform'],
clippath: ['id', 'class', 'clippathunits'],
marker: ['id', 'class', 'markerunits', 'markerwidth', 'markerheight', 'orient', 'preserveaspectratio', 'viewbox', 'refx', 'refy'],
title: [],
desc: []
};
// js-docs-end svg-allow-list
const Default = {
activeIcon: null,
allowClear: false,
allowList: svgAllowList,
disabled: false,
highlightOnlySelected: false,
icon: null,
itemCount: 5,
name: null,
precision: 1,
readOnly: false,
sanitize: true,
sanitizeFn: null,
size: null,
tooltips: false,
value: null
};
const DefaultType = {
activeIcon: '(object|string|null)',
allowClear: 'boolean',
allowList: 'object',
disabled: 'boolean',
highlightOnlySelected: 'boolean',
icon: '(object|string|null)',
itemCount: 'number',
name: '(string|null)',
precision: 'number',
readOnly: 'boolean',
sanitize: 'boolean',
sanitizeFn: '(null|function)',
size: '(string|null)',
tooltips: '(array|boolean|object)',
value: '(number|null)'
};
/**
* Class definition
*/
class Rating extends BaseComponent {
constructor(element, config) {
super(element);
this._config = this._getConfig(config);
this._currentValue = this._config.value;
this._name = this._config.name || index_js.getUID(`${this.constructor.NAME}-name-`).toString();
this._tooltip = null;
this._createRating();
this._addEventListeners();
}
// Getters
static get Default() {
return Default;
}
static get DefaultType() {
return DefaultType;
}
static get NAME() {
return NAME;
}
// Public
update(config) {
this._config = this._getConfig(config);
this._currentValue = this._config.value;
this._element.innerHTML = '';
this._createRating();
this._addEventListeners();
}
reset(value = null) {
this._currentValue = value;
this._element.innerHTML = '';
this._createRating();
this._addEventListeners();
EventHandler.trigger(this._element, EVENT_CHANGE, {
value
});
}
// Private
_addEventListeners() {
EventHandler.on(this._element, EVENT_CLICK, SELECTOR_RATING_ITEM_INPUT, ({
target
}) => {
if (this._config.disabled || this._config.readOnly) {
return;
}
// eslint-disable-next-line eqeqeq
if (this._config.allowClear && this._currentValue == target.value) {
this._currentValue = null;
target.checked = false;
this._resetLabels();
EventHandler.trigger(this._element, EVENT_CHANGE, {
value: null
});
}
});
EventHandler.on(this._element, EVENT_CHANGE, SELECTOR_RATING_ITEM_INPUT, ({
target
}) => {
if (this._config.disabled || this._config.readOnly) {
return;
}
this._currentValue = target.value;
EventHandler.trigger(this._element, EVENT_CHANGE, {
value: target.value
});
const inputs = SelectorEngine.find(SELECTOR_RATING_ITEM_INPUT, this._element);
this._resetLabels();
if (this._config.highlightOnlySelected) {
const label = SelectorEngine.findOne(SELECTOR_RATING_ITEM_LABEL, target.parentElement);
label.classList.add(CLASS_NAME_ACTIVE);
return;
}
for (const input of inputs) {
const label = SelectorEngine.findOne(SELECTOR_RATING_ITEM_LABEL, input.parentElement);
label.classList.add(CLASS_NAME_ACTIVE);
if (input === target) {
break;
}
}
});
EventHandler.on(this._element, EVENT_MOUSEENTER, SELECTOR_RATING_ITEM_LABEL, ({
target
}) => {
if (this._config.disabled || this._config.readOnly) {
return;
}
const label = target.closest(SELECTOR_RATING_ITEM_LABEL);
const labels = SelectorEngine.find(SELECTOR_RATING_ITEM_LABEL, this._element);
this._resetLabels();
const input = SelectorEngine.findOne(SELECTOR_RATING_ITEM_INPUT, label.parentElement);
EventHandler.trigger(this._element, EVENT_HOVER, {
value: input.value
});
this._createTooltip(label.parentElement, input.value);
if (this._config.highlightOnlySelected) {
label.classList.add(CLASS_NAME_ACTIVE);
return;
}
for (const _label of labels) {
_label.classList.add(CLASS_NAME_ACTIVE);
if (_label === label) {
break;
}
}
});
EventHandler.on(this._element, EVENT_MOUSELEAVE, SELECTOR_RATING_ITEM_LABEL, () => {
if (this._config.disabled || this._config.readOnly) {
return;
}
if (this._tooltip) {
this._tooltip.hide();
}
const checkedInput = SelectorEngine.findOne(`${SELECTOR_RATING_ITEM_INPUT}[value="${this._currentValue}"]`, this._element);
this._resetLabels();
EventHandler.trigger(this._element, EVENT_HOVER, {
value: null
});
if (checkedInput && this._config.highlightOnlySelected) {
const label = SelectorEngine.findOne(SELECTOR_RATING_ITEM_LABEL, checkedInput.parentElement);
label.classList.add(CLASS_NAME_ACTIVE);
return;
}
if (checkedInput) {
const inputs = SelectorEngine.find(SELECTOR_RATING_ITEM_INPUT, this._element);
this._resetLabels();
for (const input of inputs) {
const label = SelectorEngine.findOne(SELECTOR_RATING_ITEM_LABEL, input.parentElement);
label.classList.add(CLASS_NAME_ACTIVE);
if (input === checkedInput) {
break;
}
}
}
});
EventHandler.on(this._element, EVENT_FOCUSIN, SELECTOR_RATING_ITEM_INPUT, ({
target
}) => {
EventHandler.trigger(this._element, EVENT_HOVER, {
value: target.value
});
this._createTooltip(target.parentElement, target.value);
});
EventHandler.on(this._element, EVENT_FOCUSOUT, SELECTOR_RATING_ITEM_INPUT, () => {
EventHandler.trigger(this._element, EVENT_HOVER, {
value: null
});
if (this._tooltip) {
this._tooltip.hide();
}
});
}
_createTooltip(selector, value) {
if (this._config.tooltips === false) {
return;
}
if (this._tooltip) {
this._tooltip.hide();
}
let tooltipTitle;
if (typeof this._config.tooltips === 'boolean') {
tooltipTitle = value;
}
if (typeof this._config.tooltips === 'object') {
tooltipTitle = this._config.tooltips[value];
}
if (Array.isArray(this._config.tooltips)) {
tooltipTitle = this._config.tooltips[value - 1];
}
this._tooltip = new Tooltip(selector, {
title: tooltipTitle
});
}
_configAfterMerge(config) {
if (typeof config.tooltips === 'string') {
config.tooltips = config.tooltips.split(',');
}
return config;
}
_resetLabels() {
const labels = SelectorEngine.find(SELECTOR_RATING_ITEM_LABEL, this._element);
for (const label of labels) {
label.classList.remove(CLASS_NAME_ACTIVE);
}
}
_createRating() {
this._element.classList.add(CLASS_NAME_RATING);
if (this._config.size) {
this._element.classList.add(`rating-${this._config.size}`);
}
if (this._config.disabled) {
this._element.classList.add(CLASS_NAME_DISABLED);
}
if (this._config.readOnly) {
this._element.classList.add(CLASS_NAME_READONLY);
}
this._element.setAttribute('role', 'radiogroup');
Array.from({
length: this._config.itemCount
}, (_, index) => this._createRatingItem(index));
}
_createRatingItem(index) {
const ratingItemElement = document.createElement('div');
ratingItemElement.classList.add(CLASS_NAME_RATING_ITEM);
const numberOfRadios = 1 / this._config.precision;
// eslint-disable-next-line array-callback-return
Array.from({
length: numberOfRadios
}, (_, _index) => {
const ratingItemId = index_js.getUID(`${this.constructor.NAME}${index}`).toString();
const isNotLastItem = _index + 1 < numberOfRadios;
const value = numberOfRadios === 1 ? index + 1 : (_index + 1) * Number(this._config.precision) + index;
// Create label
const ratingItemLabelElement = document.createElement('label');
ratingItemLabelElement.classList.add(CLASS_NAME_RATING_ITEM_LABEL);
ratingItemLabelElement.setAttribute('for', ratingItemId);
// eslint-disable-next-line eqeqeq
if (this._config.highlightOnlySelected && this._currentValue == value) {
ratingItemLabelElement.classList.add(CLASS_NAME_ACTIVE);
}
if (!this._config.highlightOnlySelected && this._currentValue >= value) {
ratingItemLabelElement.classList.add(CLASS_NAME_ACTIVE);
}
if (isNotLastItem) {
ratingItemLabelElement.style.zIndex = 1 / this._config.precision - _index;
ratingItemLabelElement.style.position = 'absolute';
ratingItemLabelElement.style.width = `${this._config.precision * (_index + 1) * 100}%`;
ratingItemLabelElement.style.overflow = 'hidden';
ratingItemLabelElement.style.opacity = 0;
}
if (this._config.icon) {
const ratingItemIconElement = document.createElement('div');
ratingItemIconElement.classList.add(CLASS_NAME_RATING_ITEM_CUSTOM_ICON);
ratingItemIconElement.innerHTML = this._sanitizeIcon(typeof this._config.icon === 'object' ? this._config.icon[index + 1] : this._config.icon);
ratingItemLabelElement.append(ratingItemIconElement);
} else {
const ratingItemIconElement = document.createElement('div');
ratingItemIconElement.classList.add(CLASS_NAME_RATING_ITEM_ICON);
ratingItemLabelElement.append(ratingItemIconElement);
}
if (this._config.icon && this._config.activeIcon) {
const ratingItemIconActiveElement = document.createElement('div');
ratingItemIconActiveElement.classList.add(CLASS_NAME_RATING_ITEM_CUSTOM_ICON_ACTIVE);
ratingItemIconActiveElement.innerHTML = this._sanitizeIcon(typeof this._config.activeIcon === 'object' ? this._config.activeIcon[index + 1] : this._config.activeIcon);
ratingItemLabelElement.append(ratingItemIconActiveElement);
}
// Create input
const ratingItemInputElement = document.createElement('input');
ratingItemInputElement.classList.add(CLASS_NAME_RATING_ITEM_INPUT);
ratingItemInputElement.id = ratingItemId;
ratingItemInputElement.type = 'radio';
ratingItemInputElement.value = value;
ratingItemInputElement.name = this._name;
if (this._config.disabled || this._config.readOnly) {
ratingItemInputElement.setAttribute('disabled', true);
}
if (this._currentValue === value) {
ratingItemInputElement.checked = true;
}
// Append elements
if (this._config.precision === 1) {
ratingItemElement.append(ratingItemLabelElement);
ratingItemElement.append(ratingItemInputElement);
} else {
const wrapper = document.createElement('div');
wrapper.append(ratingItemLabelElement);
wrapper.append(ratingItemInputElement);
ratingItemElement.append(wrapper);
}
});
this._element.append(ratingItemElement);
}
_sanitizeIcon(icon) {
return this._config.sanitize ? sanitizer_js.sanitizeHtml(icon, this._config.allowList, this._config.sanitizeFn) : icon;
}
_getConfig(config) {
const dataAttributes = Manipulator.getDataAttributes(this._element);
for (const dataAttribute of Object.keys(dataAttributes)) {
if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {
delete dataAttributes[dataAttribute];
}
}
config = {
...dataAttributes,
...(typeof config === 'object' && config ? config : {})
};
config = this._mergeConfigObj(config, this._element);
config = this._configAfterMerge(config);
this._typeCheckConfig(config);
return config;
}
// Static
static ratingInterface(element, config) {
const data = Rating.getOrCreateInstance(element, config);
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`);
}
data[config]();
}
}
static jQueryInterface(config) {
return this.each(function () {
const data = Rating.getOrCreateInstance(this, config);
if (typeof config !== 'string') {
return;
}
if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
throw new TypeError(`No method named "${config}"`);
}
data[config](this);
});
}
}
/**
* Data API implementation
*/
EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
const ratings = SelectorEngine.find(SELECTOR_DATA_TOGGLE);
for (let i = 0, len = ratings.length; i < len; i++) {
Rating.ratingInterface(ratings[i]);
}
});
/**
* jQuery
*/
index_js.defineJQueryPlugin(Rating);
exports.default = Rating;
exports.svgAllowList = svgAllowList;
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
}));
//# sourceMappingURL=rating.js.map