import BaseEvent from "@pencil.js/base-event";
import Circle from "@pencil.js/circle";
import Rectangle from "@pencil.js/rectangle";
import Input from "@pencil.js/input";
import MouseEvent from "@pencil.js/mouse-event";
import Position from "@pencil.js/position";
import Vector from "@pencil.js/vector";
import { constrain, map } from "@pencil.js/math";
import "@pencil.js/draggable";
/**
* @module Slider
*/
const constrainerKey = Symbol("_constrainer");
const moveHandleKey = Symbol("_moveHandle");
/**
* Slider class
* <br><img src="./media/examples/slider.png" alt="slider demo"/>
* @class
* @extends {module:Input}
*/
export default class Slider extends Input {
/**
* Slider constructor
* @param {PositionDefinition} positionDefinition - Top-left corner
* @param {SliderOptions} [options] - Specific options
*/
constructor (positionDefinition, options) {
super(positionDefinition, Rectangle, options);
const radius = (this.height - 2) / 2;
this.handle = new Circle([0, this.height / 2], radius, {
fill: this.options.foreground,
cursor: Rectangle.cursors.ewResize,
origin: this.getOrigin(),
});
this.add(this.handle);
this[constrainerKey] = new Vector([radius + 1, this.height / 2], [this.width - radius - 1, this.height / 2]);
this.handle.draggable({
constrain: this[constrainerKey],
});
this.handle.on(MouseEvent.events.drag, () => this.fire(new BaseEvent(Slider.events.change, this)), true);
}
/**
* @inheritDoc
*/
click (position) {
const origin = this.getOrigin();
this.handle.position.set(position.x - origin.x, 0)
.constrain(this[constrainerKey].start, this[constrainerKey].end);
super.click();
}
/**
* Change this slider's size
* @param {Number} newWidth - A new size in pixels
*/
set width (newWidth) {
if (newWidth < Slider.HEIGHT) {
throw new RangeError(`Slider is too small, minimum is ${Slider.HEIGHT}px.`);
}
this.options.width = newWidth;
this[constrainerKey].end = new Position(this.width - Slider.HEIGHT, 0);
this[moveHandleKey](this.value);
}
/**
* Return this slider's width
* @return {Number}
*/
get width () {
return this.options.width;
}
/**
* Return this slider's height
* @return {Number}
*/
get height () { // eslint-disable-line class-methods-use-this
return Slider.HEIGHT;
}
/**
* Returns this current value
* @return {Number}
*/
get value () {
const { min, max } = this.options;
const { start, end } = this[constrainerKey];
return map(this.handle.position.x, start.x, end.x, min, max);
}
/**
* Change this current value
* @param {Number} newValue - A new value to set
*/
set value (newValue) {
this[moveHandleKey](constrain(newValue, this.options.min, this.options.max));
}
/**
* @typedef {Object} SliderOptions
* @extends InputOptions
* @prop {Number} [min=0] - Minimum value when the slider is at lowest
* @prop {Number} [max=10] - Maximum value when the slider is at highest
* @prop {Number} [value=0] - Initial value
* @prop {Number} [width=200] - Size of the slider
*/
/**
* @type {SliderOptions}
*/
static get defaultOptions () {
return {
...super.defaultOptions,
min: 0,
max: 1,
value: 0,
width: 200,
};
}
/**
* Height of sliders
* @type {Number}
*/
static get HEIGHT () {
return 20;
}
}
/**
* Move the handle to a specified value
* @param {Number} value - New value to use
* @memberOf Slider#
*/
Slider.prototype[moveHandleKey] = function moveHandle (value) {
const { min, max } = this.options;
const { start, end } = this[constrainerKey];
this.handle.position.x = map(value, min, max, start.x, end.x);
};