UNPKG

igniteui-webcomponents

Version:

Ignite UI for Web Components is a complete library of UI components, giving you the ability to build modern web applications using encapsulation and the concept of reusable components in a dependency-free approach.

302 lines 11.7 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { html, LitElement, nothing } from 'lit'; import { property, query, state } from 'lit/decorators.js'; import { range } from 'lit/directives/range.js'; import { repeat } from 'lit/directives/repeat.js'; import { styleMap } from 'lit/directives/style-map.js'; import { addThemingController } from '../../theming/theming-controller.js'; import { addKeybindings, arrowDown, arrowLeft, arrowRight, arrowUp, endKey, homeKey, } from '../common/controllers/key-bindings.js'; import { addSlotController, setSlots, } from '../common/controllers/slot.js'; import { registerComponent } from '../common/definitions/register.js'; import { EventEmitterMixin } from '../common/mixins/event-emitter.js'; import { FormAssociatedMixin } from '../common/mixins/forms/associated.js'; import { FormValueNumberTransformers } from '../common/mixins/forms/form-transformers.js'; import { createFormValueState } from '../common/mixins/forms/form-value.js'; import { asNumber, bindIf, clamp, formatString, isLTR, numberOfDecimals, roundPrecise, } from '../common/util.js'; import IgcIconComponent from '../icon/icon.js'; import IgcRatingSymbolComponent from './rating-symbol.js'; import { styles } from './themes/rating.base.css.js'; import { styles as shared } from './themes/shared/rating.common.css.js'; import { all } from './themes/themes.js'; const Slots = setSlots('symbol', 'value-label'); export default class IgcRatingComponent extends FormAssociatedMixin(EventEmitterMixin(LitElement)) { static { this.tagName = 'igc-rating'; } static { this.styles = [styles, shared]; } static register() { registerComponent(IgcRatingComponent, IgcIconComponent, IgcRatingSymbolComponent); } get _isInteractive() { return !(this.readOnly || this.disabled); } get _hasProjectedSymbols() { return this._symbols.length > 0; } get _valueText() { const value = this._round(this.value); return this.valueFormat ? formatString(this.valueFormat, value, this.max) : `${value} of ${this.max}`; } set max(value) { this._max = this._hasProjectedSymbols ? this._symbols.length : Math.max(0, value); if (this._max < this.value) { this.value = this._max; } } get max() { return this._max; } set step(value) { this._step = this.single ? 1 : clamp(value, 0.001, 1); } get step() { return this._step; } set value(number) { const value = this.hasUpdated ? clamp(asNumber(number), 0, this.max) : Math.max(asNumber(number), 0); this._formValue.setValueAndFormState(value); } get value() { return this._formValue.value; } set single(value) { this._single = Boolean(value); if (this._single) { this.step = 1; this.value = Math.ceil(this.value); } } get single() { return this._single; } constructor() { super(); this._slots = addSlotController(this, { slots: Slots, onChange: this._handleSlotChange, }); this._formValue = createFormValueState(this, { initialValue: 0, transformers: FormValueNumberTransformers, }); this._max = 5; this._step = 1; this._single = false; this._symbols = []; this._hoverValue = -1; this._hoverState = false; this.hoverPreview = false; this.readOnly = false; this.allowReset = false; addThemingController(this, all); addKeybindings(this, { skip: () => !this._isInteractive, bindingDefaults: { repeat: true }, }) .set(arrowUp, () => this._emitValueUpdate(this.value + this.step)) .set(arrowRight, () => this._emitValueUpdate(isLTR(this) ? this.value + this.step : this.value - this.step)) .set(arrowDown, () => this._emitValueUpdate(this.value - this.step)) .set(arrowLeft, () => this._emitValueUpdate(isLTR(this) ? this.value - this.step : this.value + this.step)) .set(homeKey, () => this._emitValueUpdate(this.step)) .set(endKey, () => this._emitValueUpdate(this.max)); } firstUpdated() { this._formValue.setValueAndFormState(clamp(this.value, 0, this.max)); this._pristine = true; } updated() { if (this._hasProjectedSymbols) { this._updateProjectedSymbols(); } } _handleClick({ clientX }) { const value = this._calcNewValue(clientX); const sameValue = this.value === value; if (this.allowReset && sameValue) { this._emitValueUpdate(0); } else if (!sameValue) { this._emitValueUpdate(value); } } _handlePointerMove({ clientX }) { const value = this._calcNewValue(clientX); if (this._hoverValue !== value) { this._hoverValue = value; this.emitEvent('igcHover', { detail: this._hoverValue }); } } _handleSlotChange({ slot, }) { if (slot === 'symbol') { this._symbols = this._slots.getAssignedElements('symbol', { selector: IgcRatingSymbolComponent.tagName, }); if (this._hasProjectedSymbols) { this.max = this._symbols.length; } } } _handleHoverEnabled() { this._hoverState = true; } _handleHoverDisabled() { this._hoverState = false; } _emitValueUpdate(next) { this._setTouchedState(); const clamped = clamp(next, 0, this.max); if (clamped !== this.value) { this.value = clamped; this.emitEvent('igcChange', { detail: this.value }); } } _calcNewValue(x) { const { width, left, right } = this._container?.getBoundingClientRect() ?? new DOMRect(1, 1, 1, 1); const percent = isLTR(this) ? (x - left) / width : (right - x) / width; const value = this._round(this.max * percent); return clamp(value, this.step, this.max); } _round(value) { return roundPrecise(Math.ceil(value / this.step) * this.step, numberOfDecimals(this.step)); } _updateProjectedSymbols() { const ltr = isLTR(this); const partFull = '[part="symbol full"]'; const partEmpty = '[part="symbol empty"]'; for (const [i, symbol] of this._symbols.entries()) { const full = symbol.renderRoot.querySelector(partFull); const empty = symbol.renderRoot.querySelector(partEmpty); const { forward, backward } = this._clipSymbol(i, ltr); if (full) { full.style.clipPath = forward; } if (empty) { empty.style.clipPath = backward; } } } _clipSymbol(index, isLTR = true) { const value = this._hoverState ? this._hoverValue : this.value; const progress = index + 1 - value; const exclusive = progress === 0 || value === index + 1 ? 0 : 1; const selection = this.single ? exclusive : progress; const activate = (p) => clamp(p * 100, 0, 100); const forward = `inset(0 ${activate(isLTR ? selection : 1 - selection)}% 0 0)`; const backward = `inset(0 0 0 ${activate(isLTR ? 1 - selection : selection)}%)`; return { backward: isLTR ? backward : forward, forward: isLTR ? forward : backward, }; } stepUp(n = 1) { this.value += this._round(n * this.step); } stepDown(n = 1) { this.value -= this._round(n * this.step); } _renderSymbols() { const ltr = isLTR(this); return html ` ${repeat(range(this.max), (i) => i, (i) => { const { forward, backward } = this._clipSymbol(i, ltr); return html ` <igc-rating-symbol exportparts="symbol, full, empty"> <igc-icon collection="default" name="star_filled" style=${styleMap({ clipPath: forward })} ></igc-icon> <igc-icon collection="default" name="star_outlined" style=${styleMap({ clipPath: backward })} slot="empty" ></igc-icon> </igc-rating-symbol> `; })} `; } render() { const hoverActive = this.hoverPreview && this._isInteractive; const valueLabelHidden = !this._slots.hasAssignedNodes('value-label', true); return html ` <label part="label" id="rating-label" ?hidden=${!this.label} >${this.label}</label > <div part="base" role="slider" tabindex=${this.disabled ? -1 : 0} aria-labelledby="rating-label" aria-valuemin="0" aria-valuenow=${this.value} aria-valuemax=${this.max} aria-valuetext=${this._valueText} > <div aria-hidden="true" part="symbols" @click=${bindIf(this._isInteractive, this._handleClick)} @pointerenter=${bindIf(hoverActive, this._handleHoverEnabled)} @pointerleave=${bindIf(hoverActive, this._handleHoverDisabled)} @pointercancel=${bindIf(hoverActive, this._handleHoverDisabled)} @pointermove=${bindIf(hoverActive, this._handlePointerMove)} > <slot name="symbol"> ${this._hasProjectedSymbols ? nothing : this._renderSymbols()} </slot> </div> <label part="value-label" ?hidden=${valueLabelHidden}> <slot name="value-label"></slot> </label> </div> `; } } __decorate([ query('[part="symbols"]', true) ], IgcRatingComponent.prototype, "_container", void 0); __decorate([ state() ], IgcRatingComponent.prototype, "_hoverValue", void 0); __decorate([ state() ], IgcRatingComponent.prototype, "_hoverState", void 0); __decorate([ property({ type: Number }) ], IgcRatingComponent.prototype, "max", null); __decorate([ property({ type: Number }) ], IgcRatingComponent.prototype, "step", null); __decorate([ property() ], IgcRatingComponent.prototype, "label", void 0); __decorate([ property({ attribute: 'value-format' }) ], IgcRatingComponent.prototype, "valueFormat", void 0); __decorate([ property({ type: Number }) ], IgcRatingComponent.prototype, "value", null); __decorate([ property({ type: Boolean, reflect: true, attribute: 'hover-preview' }) ], IgcRatingComponent.prototype, "hoverPreview", void 0); __decorate([ property({ type: Boolean, reflect: true, attribute: 'readonly' }) ], IgcRatingComponent.prototype, "readOnly", void 0); __decorate([ property({ type: Boolean, reflect: true }) ], IgcRatingComponent.prototype, "single", null); __decorate([ property({ type: Boolean, reflect: true, attribute: 'allow-reset' }) ], IgcRatingComponent.prototype, "allowReset", void 0); //# sourceMappingURL=rating.js.map