@playcanvas/pcui
Version:
User interface component library for the web
337 lines (334 loc) • 11.9 kB
JavaScript
import { CLASS_MULTIPLE_VALUES } from '../../class.mjs';
import { Element } from '../Element/index.mjs';
import { NumericInput } from '../NumericInput/index.mjs';
var _a;
const CLASS_SLIDER = 'pcui-slider';
const CLASS_SLIDER_CONTAINER = `${CLASS_SLIDER}-container`;
const CLASS_SLIDER_BAR = `${CLASS_SLIDER}-bar`;
const CLASS_SLIDER_HANDLE = `${CLASS_SLIDER}-handle`;
const CLASS_SLIDER_ACTIVE = `${CLASS_SLIDER}-active`;
const IS_CHROME = /Chrome\//.test((_a = globalThis.navigator) === null || _a === void 0 ? void 0 : _a.userAgent);
/**
* The SliderInput shows a NumericInput and a slider widget next to it. It acts as a proxy of the
* NumericInput.
*/
class SliderInput extends Element {
/**
* Creates a new SliderInput.
*
* @param args - The arguments.
*/
constructor(args = {}) {
var _a, _b, _c, _d, _e;
super(args);
this._historyCombine = false;
this._historyPostfix = null;
this._cursorHandleOffset = 0;
this._pointerId = null;
this._onPointerDown = (evt) => {
if ((evt.pointerType === 'mouse' && evt.button !== 0) || !this.enabled || this.readOnly || this._pointerId !== null)
return;
evt.stopPropagation();
this._domSlider.setPointerCapture(evt.pointerId);
this._pointerId = evt.pointerId;
this._onSlideStart(evt.pageX);
};
this._onPointerMove = (evt) => {
if (evt.pointerId !== this._pointerId)
return;
evt.stopPropagation();
evt.preventDefault();
this._onSlideMove(evt.pageX);
};
this._onPointerUp = (evt) => {
if (evt.pointerId !== this._pointerId || this._pointerId === null)
return;
evt.stopPropagation();
this._domSlider.releasePointerCapture(evt.pointerId);
this._onSlideEnd(evt.pageX);
this._pointerId = null;
};
this._onKeyDown = (evt) => {
if (evt.key === 'Escape') {
this.blur();
return;
}
if (!this.enabled || this.readOnly)
return;
// move slider with left / right arrow keys
if (evt.key !== 'ArrowLeft' && evt.key !== 'ArrowRight')
return;
evt.stopPropagation();
evt.preventDefault();
let x = evt.key === 'ArrowLeft' ? -1 : 1;
if (evt.shiftKey) {
x *= 10;
}
this.value += x * this.step;
};
this.class.add(CLASS_SLIDER);
const numericInput = new NumericInput({
allowNull: args.allowNull,
hideSlider: true,
min: args.min,
max: args.max,
keyChange: args.keyChange,
placeholder: args.placeholder,
precision: (_a = args.precision) !== null && _a !== void 0 ? _a : 2,
renderChanges: args.renderChanges,
step: args.step
});
// propagate change event
numericInput.on('change', (value) => {
this._onValueChange(value);
});
// propagate focus / blur events
numericInput.on('focus', () => {
this.emit('focus');
});
numericInput.on('blur', () => {
this.emit('blur');
});
numericInput.parent = this;
this.dom.appendChild(numericInput.dom);
this._numericInput = numericInput;
this._sliderMin = (_c = (_b = args.sliderMin) !== null && _b !== void 0 ? _b : args.min) !== null && _c !== void 0 ? _c : 0;
this._sliderMax = (_e = (_d = args.sliderMax) !== null && _d !== void 0 ? _d : args.max) !== null && _e !== void 0 ? _e : 1;
this._domSlider = document.createElement('div');
this._domSlider.classList.add(CLASS_SLIDER_CONTAINER);
this.dom.appendChild(this._domSlider);
this._domBar = document.createElement('div');
this._domBar.classList.add(CLASS_SLIDER_BAR);
this._domBar.ui = this;
this._domSlider.appendChild(this._domBar);
this._domHandle = document.createElement('div');
this._domHandle.ui = this;
this._domHandle.tabIndex = 0;
this._domHandle.classList.add(CLASS_SLIDER_HANDLE);
this._domBar.appendChild(this._domHandle);
this._domSlider.addEventListener('pointerdown', this._onPointerDown);
this._domHandle.addEventListener('keydown', this._onKeyDown);
if (args.value !== undefined) {
this.value = args.value;
}
if (args.values !== undefined) {
this.values = args.values;
}
// update the handle in case a 0 value has been
// passed through the constructor
if (this.value === 0) {
this._updateHandle(0);
}
}
destroy() {
if (this._destroyed)
return;
this._domSlider.removeEventListener('pointerdown', this._onPointerDown);
this._domHandle.removeEventListener('keydown', this._onKeyDown);
super.destroy();
}
_updateHandle(value) {
const left = Math.max(0, Math.min(1, ((value || 0) - this._sliderMin) / (this._sliderMax - this._sliderMin))) * 100;
const handleWidth = this._domHandle.getBoundingClientRect().width;
this._domHandle.style.left = `calc(${left}% + ${handleWidth / 2}px)`;
}
_onValueChange(value) {
this._updateHandle(value);
if (!this._suppressChange) {
this.emit('change', value);
}
if (this._binding) {
this._binding.setValue(value);
}
}
// Calculates the distance in pixels between
// the cursor x and the middle of the handle.
// If the cursor is not on the handle sets the offset to 0
_calculateCursorHandleOffset(pageX) {
// not sure why but the left side needs a margin of a couple of pixels
// to properly determine if the cursor is on the handle (in Chrome)
const margin = IS_CHROME ? 2 : 0;
const rect = this._domHandle.getBoundingClientRect();
const left = rect.left - margin;
const right = rect.right;
if (pageX >= left && pageX <= right) {
this._cursorHandleOffset = pageX - (left + (right - left) / 2);
}
else {
this._cursorHandleOffset = 0;
}
return this._cursorHandleOffset;
}
_onSlideStart(pageX) {
this._domHandle.focus();
this._domSlider.addEventListener('pointermove', this._onPointerMove);
this._domSlider.addEventListener('pointerup', this._onPointerUp);
this.class.add(CLASS_SLIDER_ACTIVE);
// calculate the cursor - handle offset. If there is
// an offset that means the cursor is on the handle so
// do not move the handle until the cursor moves.
if (!this._calculateCursorHandleOffset(pageX)) {
this._onSlideMove(pageX);
}
if (this.binding) {
this._historyCombine = this.binding.historyCombine;
this._historyPostfix = this.binding.historyPostfix;
this.binding.historyCombine = true;
this.binding.historyPostfix = `(${Date.now()})`;
}
}
_onSlideMove(pageX) {
const rect = this._domBar.getBoundingClientRect();
// reduce pageX by the initial cursor - handle offset
pageX -= this._cursorHandleOffset;
const x = Math.max(0, Math.min(1, (pageX - rect.left) / rect.width));
const range = this._sliderMax - this._sliderMin;
let value = (x * range) + this._sliderMin;
value = parseFloat(value.toFixed(this.precision));
this.value = value;
}
_onSlideEnd(pageX) {
// when slide ends only move the handle if the cursor is no longer
// on the handle
if (!this._calculateCursorHandleOffset(pageX)) {
this._onSlideMove(pageX);
}
this.class.remove(CLASS_SLIDER_ACTIVE);
this._domSlider.removeEventListener('pointermove', this._onPointerMove);
this._domSlider.removeEventListener('pointerup', this._onPointerUp);
if (this.binding) {
this.binding.historyCombine = this._historyCombine;
this.binding.historyPostfix = this._historyPostfix;
this._historyCombine = false;
this._historyPostfix = null;
}
}
focus() {
this._numericInput.focus();
}
blur() {
this._domHandle.blur();
this._numericInput.blur();
}
/**
* Sets the minimum value that the slider field can take.
*/
set sliderMin(value) {
if (this._sliderMin === value)
return;
this._sliderMin = value;
this._updateHandle(this.value);
}
/**
* Gets the minimum value that the slider field can take.
*/
get sliderMin() {
return this._sliderMin;
}
/**
* Sets the maximum value that the slider field can take.
*/
set sliderMax(value) {
if (this._sliderMax === value)
return;
this._sliderMax = value;
this._updateHandle(this.value);
}
/**
* Gets the maximum value that the slider field can take.
*/
get sliderMax() {
return this._sliderMax;
}
set value(value) {
this._numericInput.value = value;
if (this._numericInput.class.contains(CLASS_MULTIPLE_VALUES)) {
this.class.add(CLASS_MULTIPLE_VALUES);
}
else {
this.class.remove(CLASS_MULTIPLE_VALUES);
}
}
get value() {
return this._numericInput.value;
}
/* eslint accessor-pairs: 0 */
set values(values) {
this._numericInput.values = values;
if (this._numericInput.class.contains(CLASS_MULTIPLE_VALUES)) {
this.class.add(CLASS_MULTIPLE_VALUES);
}
else {
this.class.remove(CLASS_MULTIPLE_VALUES);
}
}
set renderChanges(value) {
this._numericInput.renderChanges = value;
}
get renderChanges() {
return this._numericInput.renderChanges;
}
/**
* Sets the minimum value that the numeric input field can take.
*/
set min(value) {
this._numericInput.min = value;
}
/**
* Gets the minimum value that the numeric input field can take.
*/
get min() {
return this._numericInput.min;
}
/**
* Sets the maximum value that the numeric input field can take.
*/
set max(value) {
this._numericInput.max = value;
}
/**
* Gets the maximum value that the numeric input field can take.
*/
get max() {
return this._numericInput.max;
}
/**
* Sets the amount that the value will be increased or decreased when using the arrow keys. Holding Shift will use 10x the step.
*/
set step(value) {
this._numericInput.step = value;
}
/**
* Gets the amount that the value will be increased or decreased when using the arrow keys.
*/
get step() {
return this._numericInput.step;
}
/**
* Sets the maximum number of decimals a value can take.
*/
set precision(value) {
this._numericInput.precision = value;
}
/**
* Gets the maximum number of decimals a value can take.
*/
get precision() {
return this._numericInput.precision;
}
set keyChange(value) {
this._numericInput.keyChange = value;
}
get keyChange() {
return this._numericInput.keyChange;
}
set placeholder(value) {
this._numericInput.placeholder = value;
}
get placeholder() {
return this._numericInput.placeholder;
}
}
Element.register('slider', SliderInput, { renderChanges: true });
export { SliderInput };
//# sourceMappingURL=index.mjs.map