588 lines
15 KiB
JavaScript
588 lines
15 KiB
JavaScript
/*! DSFR v1.11.2 | SPDX-License-Identifier: MIT | License-Filename: LICENSE.md | restricted use (see terms and conditions) */
|
|
|
|
const config = {
|
|
prefix: 'fr',
|
|
namespace: 'dsfr',
|
|
organisation: '@gouvfr',
|
|
version: '1.11.2'
|
|
};
|
|
|
|
const api = window[config.namespace];
|
|
|
|
const RangeSelector = {
|
|
RANGE: api.internals.ns.selector('range'),
|
|
RANGE_SM: api.internals.ns.selector('range--sm'),
|
|
RANGE_STEP: api.internals.ns.selector('range--step'),
|
|
RANGE_DOUBLE: api.internals.ns.selector('range--double'),
|
|
RANGE_DOUBLE_STEP: api.internals.ns.selector('range--double') + api.internals.ns.selector('range--step'),
|
|
RANGE_INPUT: api.internals.ns.selector('range input[type=range]:nth-of-type(1)'),
|
|
RANGE_INPUT2: `${api.internals.ns.selector('range--double')} input[type=range]:nth-of-type(2)`,
|
|
RANGE_OUTPUT: api.internals.ns.selector('range__output'),
|
|
RANGE_MIN: api.internals.ns.selector('range__min'),
|
|
RANGE_MAX: api.internals.ns.selector('range__max'),
|
|
RANGE_PREFIX: api.internals.ns.attr('prefix'),
|
|
RANGE_SUFFIX: api.internals.ns.attr('suffix')
|
|
};
|
|
|
|
const RangeEmission = {
|
|
VALUE: api.internals.ns.emission('range', 'value'),
|
|
VALUE2: api.internals.ns.emission('range', 'value2'),
|
|
OUTPUT: api.internals.ns.emission('range', 'output'),
|
|
CONSTRAINTS: api.internals.ns.emission('range', 'constraints'),
|
|
MIN: api.internals.ns.emission('range', 'min'),
|
|
MAX: api.internals.ns.emission('range', 'max'),
|
|
STEP: api.internals.ns.emission('range', 'step'),
|
|
PREFIX: api.internals.ns.emission('range', 'prefix'),
|
|
SUFFIX: api.internals.ns.emission('range', 'suffix'),
|
|
DISABLED: api.internals.ns.emission('range', 'disabled'),
|
|
ENABLE_POINTER: api.internals.ns.emission('range', 'enable_pointer')
|
|
};
|
|
|
|
class RangeModel {
|
|
constructor () {
|
|
this._width = 0;
|
|
this._min = 0;
|
|
this._max = 0;
|
|
this._value = 0;
|
|
this._thumbSize = 24;
|
|
this._innerWidth = 0;
|
|
this._prefix = '';
|
|
this._suffix = '';
|
|
this._background = {};
|
|
}
|
|
|
|
configure (model) {
|
|
if (!model) return;
|
|
this._prefix = model._prefix;
|
|
this._suffix = model._suffix;
|
|
this._width = model.width;
|
|
this.setConstraints(model._constraints);
|
|
this.value = model.value;
|
|
this.update();
|
|
}
|
|
|
|
setPrefix (value) {
|
|
this._prefix = value !== null ? value : '';
|
|
}
|
|
|
|
setSuffix (value) {
|
|
this._suffix = value !== null ? value : '';
|
|
}
|
|
|
|
_decorate (value) {
|
|
return `${this._prefix}${value}${this._suffix}`;
|
|
}
|
|
|
|
get width () {
|
|
return this._width;
|
|
}
|
|
|
|
set width (value) {
|
|
this._width = value;
|
|
}
|
|
|
|
get isSm () {
|
|
return this._isSm;
|
|
}
|
|
|
|
set isSm (value) {
|
|
if (this._isSm === value) return;
|
|
this._isSm = value;
|
|
this.setThumbSize(value ? 16 : 24);
|
|
this.update();
|
|
}
|
|
|
|
setThumbSize (value, mult = 1) {
|
|
this._thumbSize = value;
|
|
this._innerPadding = value * mult;
|
|
}
|
|
|
|
get textValue () {
|
|
return this._decorate(this._value);
|
|
}
|
|
|
|
get value () {
|
|
return this._value;
|
|
}
|
|
|
|
set value (value) {
|
|
this._value = value;
|
|
}
|
|
|
|
get outputX () {
|
|
return this._outputX;
|
|
}
|
|
|
|
setConstraints (constraints) {
|
|
this._constraints = constraints;
|
|
this._min = constraints.min;
|
|
this._max = constraints.max;
|
|
this._step = constraints.step;
|
|
this._rangeWidth = constraints.rangeWidth;
|
|
}
|
|
|
|
get min () {
|
|
return this._min;
|
|
}
|
|
|
|
get textMin () {
|
|
return this._decorate(this._min);
|
|
}
|
|
|
|
get max () {
|
|
return this._max;
|
|
}
|
|
|
|
get textMax () {
|
|
return this._decorate(this._max);
|
|
}
|
|
|
|
get step () {
|
|
return this._step;
|
|
}
|
|
|
|
get output () {
|
|
return {
|
|
text: this.textValue,
|
|
transform: `translateX(${this._translateX}px) translateX(-${this._centerPercent}%)`
|
|
};
|
|
}
|
|
|
|
_getRatio (value) {
|
|
return (value - this._min) / this._rangeWidth;
|
|
}
|
|
|
|
get progress () {
|
|
return this._progress;
|
|
}
|
|
|
|
update () {
|
|
this._update();
|
|
}
|
|
|
|
_update () {
|
|
this._innerWidth = this._width - this._innerPadding;
|
|
const ratio = this._getRatio(this._value);
|
|
this._translateX = ratio * this._width;
|
|
this._centerPercent = ratio * 100;
|
|
this._progress = {
|
|
right: `${(this._innerWidth * ratio + this._innerPadding * 0.5).toFixed(2)}px`
|
|
};
|
|
}
|
|
}
|
|
|
|
class RangeModelStep extends RangeModel {
|
|
get stepWidth () {
|
|
return `${this._stepWidth.toFixed(3)}px`;
|
|
}
|
|
|
|
_update () {
|
|
super._update();
|
|
const steps = this._rangeWidth / this._step;
|
|
this._stepWidth = this._innerWidth / steps;
|
|
while (this._stepWidth < 4) this._stepWidth *= 2;
|
|
}
|
|
}
|
|
|
|
class RangeModelDouble extends RangeModel {
|
|
get value2 () {
|
|
return this._value;
|
|
}
|
|
|
|
set value2 (value) {
|
|
if (this._value2 === value) return;
|
|
this._value2 = value;
|
|
this.update();
|
|
}
|
|
|
|
get textValue () {
|
|
return `${this._decorate(this._value)} - ${this._decorate(this._value2)}`;
|
|
}
|
|
|
|
setThumbSize (value) {
|
|
super.setThumbSize(value, 2);
|
|
}
|
|
|
|
_update () {
|
|
super._update();
|
|
const ratio = this._getRatio((this._value + this._value2) * 0.5);
|
|
this._translateX = ratio * this._width;
|
|
this._centerPercent = ratio * 100;
|
|
const ratio1 = this._getRatio(this._value);
|
|
const ratio2 = this._getRatio(this._value2);
|
|
this._progress = {
|
|
left: `${(this._innerWidth * ratio1 + this._innerPadding * 0.25).toFixed(2)}px`,
|
|
right: `${(this._innerWidth * ratio2 + this._innerPadding * 0.75).toFixed(2)}px`
|
|
};
|
|
}
|
|
}
|
|
|
|
class RangeModelDoubleStep extends RangeModelDouble {
|
|
get stepWidth () {
|
|
return `${this._stepWidth.toFixed(3)}px`;
|
|
}
|
|
|
|
_update () {
|
|
super._update();
|
|
const steps = this._rangeWidth / this._step;
|
|
this._stepWidth = this._innerWidth / steps;
|
|
if (this._stepWidth < 4) this._stepWidth *= Math.ceil(4 / this._stepWidth);
|
|
}
|
|
}
|
|
|
|
const RangeTypes = {
|
|
STEP: 'step',
|
|
DOUBLE: 'double',
|
|
DOUBLE_STEP: 'double-step',
|
|
DEFAULT: 'default'
|
|
};
|
|
|
|
class Range extends api.core.Instance {
|
|
static get instanceClassName () {
|
|
return 'Range';
|
|
}
|
|
|
|
init () {
|
|
this._retrieveType();
|
|
this._retrieveSize();
|
|
if (this.isLegacy) {
|
|
this.isResizing = true;
|
|
this.isMouseMoving = true;
|
|
} else {
|
|
this._observer = new ResizeObserver(this.resize.bind(this));
|
|
this._observer.observe(this.node);
|
|
}
|
|
|
|
this.addAscent(RangeEmission.CONSTRAINTS, this.setConstraints.bind(this));
|
|
this.addAscent(RangeEmission.VALUE, this.setValue.bind(this));
|
|
this.addAscent(RangeEmission.VALUE2, this.setValue2.bind(this));
|
|
if (this.getAttribute(RangeSelector.RANGE_PREFIX)) this.setPrefix(this.getAttribute(RangeSelector.RANGE_PREFIX));
|
|
if (this.getAttribute(RangeSelector.RANGE_SUFFIX)) this.setSuffix(this.getAttribute(RangeSelector.RANGE_SUFFIX));
|
|
this.update();
|
|
}
|
|
|
|
_retrieveType () {
|
|
switch (true) {
|
|
case this.matches(RangeSelector.RANGE_DOUBLE_STEP):
|
|
this.type = RangeTypes.DOUBLE;
|
|
break;
|
|
|
|
case this.matches(RangeSelector.RANGE_DOUBLE):
|
|
this.type = RangeTypes.DOUBLE;
|
|
break;
|
|
|
|
case this.matches(RangeSelector.RANGE_STEP):
|
|
this.type = RangeTypes.STEP;
|
|
break;
|
|
|
|
default:
|
|
this.type = RangeTypes.DEFAULT;
|
|
}
|
|
}
|
|
|
|
set type (value) {
|
|
if (this._type === value) return;
|
|
this._type = value;
|
|
|
|
const oldModel = this._model;
|
|
|
|
switch (this._type) {
|
|
case RangeTypes.DOUBLE_STEP:
|
|
this._model = new RangeModelDoubleStep();
|
|
break;
|
|
|
|
case RangeTypes.DOUBLE:
|
|
this._model = new RangeModelDouble();
|
|
break;
|
|
|
|
case RangeTypes.STEP:
|
|
this._model = new RangeModelStep();
|
|
break;
|
|
|
|
default:
|
|
this._model = new RangeModel();
|
|
}
|
|
|
|
this._model.configure(oldModel);
|
|
}
|
|
|
|
get type () {
|
|
return this._type;
|
|
}
|
|
|
|
_retrieveSize () {
|
|
this._model.isSm = this.matches(RangeSelector.RANGE_SM);
|
|
}
|
|
|
|
resize () {
|
|
this._retrieveWidth();
|
|
this.update();
|
|
}
|
|
|
|
_retrieveWidth () {
|
|
this._model.width = this.getRect().width;
|
|
}
|
|
|
|
setValue (value) {
|
|
this._model.value = value;
|
|
switch (this._type) {
|
|
case RangeTypes.DOUBLE_STEP:
|
|
case RangeTypes.DOUBLE:
|
|
this.descend(RangeEmission.VALUE, value);
|
|
break;
|
|
}
|
|
this.update();
|
|
}
|
|
|
|
setValue2 (value) {
|
|
this._model.value2 = value;
|
|
this.descend(RangeEmission.VALUE2, value);
|
|
this.update();
|
|
}
|
|
|
|
setConstraints (constraints) {
|
|
this._model.setConstraints(constraints);
|
|
this.update();
|
|
this.descend(RangeEmission.CONSTRAINTS, constraints);
|
|
}
|
|
|
|
setPrefix (value) {
|
|
this._model.setPrefix(value);
|
|
this.update();
|
|
}
|
|
|
|
setSuffix (value) {
|
|
this._model.setSuffix(value);
|
|
this.update();
|
|
}
|
|
|
|
mutate (attributesNames) {
|
|
switch (true) {
|
|
case attributesNames.includes('class'):
|
|
this._retrieveType();
|
|
this._retrieveSize();
|
|
break;
|
|
|
|
case attributesNames.includes(RangeSelector.RANGE_PREFIX):
|
|
case attributesNames.includes(RangeSelector.RANGE_SUFFIX):
|
|
this._model.setPrefix(this.getAttribute(RangeSelector.RANGE_PREFIX));
|
|
this._model.setSuffix(this.getAttribute(RangeSelector.RANGE_SUFFIX));
|
|
this.update();
|
|
break;
|
|
}
|
|
}
|
|
|
|
update () {
|
|
this._model.update();
|
|
this.descend(RangeEmission.OUTPUT, this._model.output);
|
|
this.descend(RangeEmission.MIN, this._model.textMin);
|
|
this.descend(RangeEmission.MAX, this._model.textMax);
|
|
const progress = this._model.progress;
|
|
if (progress.left) {
|
|
this.style.setProperty('--progress-left', progress.left);
|
|
} else {
|
|
this.style.removeProperty('--progress-left');
|
|
}
|
|
if (progress.right) {
|
|
this.style.setProperty('--progress-right', progress.right);
|
|
if (this.isLegacy) {
|
|
if (progress.left) {
|
|
this.style.setProperty('background-position-x', progress.left);
|
|
this.style.setProperty('background-size', `${parseFloat(progress.right) - parseFloat(progress.left)}px ${this._model.isSm ? '8px' : '12px'}`);
|
|
}
|
|
}
|
|
} else {
|
|
this.style.removeProperty('--progress-right');
|
|
if (this.isLegacy) {
|
|
this.style.removeProperty('background-size');
|
|
this.style.removeProperty('background-position-x');
|
|
}
|
|
}
|
|
if (this._model.stepWidth) this.style.setProperty('--step-width', this._model.stepWidth);
|
|
else this.style.removeProperty('--step-width');
|
|
}
|
|
|
|
mouseMove (point) {
|
|
if (this._type !== RangeTypes.DOUBLE && this._type !== RangeTypes.DOUBLE_STEP) return;
|
|
const x = point.x - this.getRect().left;
|
|
this.descend(RangeEmission.ENABLE_POINTER, (parseFloat(this._model.progress.right) - parseFloat(this._model.progress.left)) / 2 + parseFloat(this._model.progress.left) < x ? 2 : 1);
|
|
}
|
|
|
|
dispose () {
|
|
this._observer.disconnect();
|
|
}
|
|
}
|
|
|
|
class RangeConstraints {
|
|
constructor (node) {
|
|
this._min = isNaN(node.min) ? 0 : node.min;
|
|
this._max = isNaN(node.max) ? 100 : node.max;
|
|
this._step = isNaN(node.step) ? 1 : node.step;
|
|
this._rangeWidth = this._max - this._min;
|
|
}
|
|
|
|
get min () {
|
|
return this._min;
|
|
}
|
|
|
|
get max () {
|
|
return this._max;
|
|
}
|
|
|
|
get step () {
|
|
return this._step;
|
|
}
|
|
|
|
get rangeWidth () {
|
|
return this._rangeWidth;
|
|
}
|
|
|
|
test (min, max, step) {
|
|
return this._min === min && this._max === max && this._step === step;
|
|
}
|
|
}
|
|
|
|
class RangeInput extends api.core.Instance {
|
|
static get instanceClassName () {
|
|
return 'RangeInput';
|
|
}
|
|
|
|
init () {
|
|
this._init();
|
|
this.node.value = this.getAttribute('value');
|
|
this._changing = this.change.bind(this);
|
|
this._listenerType = this.isLegacy ? 'change' : 'input';
|
|
this.listen(this._listenerType, this._changing);
|
|
if (this.isLegacy) this.addDescent(RangeEmission.ENABLE_POINTER, this._enablePointer.bind(this));
|
|
this.change();
|
|
}
|
|
|
|
_init () {
|
|
this._pointerId = 1;
|
|
this.request(() => {
|
|
if (!this.hasAttribute('min')) this.setAttribute('min', 0);
|
|
this.ascend(RangeEmission.CONSTRAINTS, new RangeConstraints(this.node));
|
|
this.ascend(RangeEmission.DISABLED, this.node.disabled);
|
|
});
|
|
|
|
this.addDescent(RangeEmission.VALUE2, this.setValue.bind(this));
|
|
}
|
|
|
|
_enablePointer (pointerId) {
|
|
const isEnabled = pointerId === this._pointerId;
|
|
if (this._isPointerEnabled === isEnabled) return;
|
|
this._isPointerEnabled = isEnabled;
|
|
if (isEnabled) this.style.removeProperty('pointer-events');
|
|
else this.style.setProperty('pointer-events', 'none');
|
|
}
|
|
|
|
setValue (value) {
|
|
if (parseFloat(this.node.value) > value) {
|
|
this.node.value = value;
|
|
this.change();
|
|
}
|
|
}
|
|
|
|
change () {
|
|
this.ascend(RangeEmission.VALUE, parseFloat(this.node.value));
|
|
}
|
|
|
|
mutate (attributesNames) {
|
|
if (attributesNames.includes('disabled')) this.ascend(RangeEmission.DISABLED, this.node.disabled);
|
|
if (attributesNames.includes('min') || attributesNames.includes('max') || attributesNames.includes('step')) {
|
|
this.ascend(RangeEmission.CONSTRAINTS, new RangeConstraints(this.node));
|
|
this.change();
|
|
}
|
|
}
|
|
|
|
dispose () {
|
|
if (this._listenerType) this.unlisten(this._listenerType, this._changing);
|
|
}
|
|
}
|
|
|
|
class RangeInput2 extends RangeInput {
|
|
static get instanceClassName () {
|
|
return 'RangeInput2';
|
|
}
|
|
|
|
_init () {
|
|
this._pointerId = 2;
|
|
this.addDescent(RangeEmission.CONSTRAINTS, this.setConstraints.bind(this));
|
|
this.addDescent(RangeEmission.VALUE, this.setValue.bind(this));
|
|
}
|
|
|
|
setValue (value) {
|
|
if (parseFloat(this.node.value) < value) {
|
|
this.node.value = value;
|
|
this.change();
|
|
}
|
|
}
|
|
|
|
change () {
|
|
this.ascend(RangeEmission.VALUE2, parseFloat(this.node.value));
|
|
}
|
|
|
|
setConstraints (constraints) {
|
|
this.node.min = constraints.min;
|
|
this.node.max = constraints.max;
|
|
this.node.step = constraints.step;
|
|
this.change();
|
|
}
|
|
|
|
mutate (attributesNames) {}
|
|
}
|
|
|
|
class RangeOutput extends api.core.Instance {
|
|
static get instanceClassName () {
|
|
return 'RangeOutput';
|
|
}
|
|
|
|
init () {
|
|
this.addDescent(RangeEmission.OUTPUT, this.change.bind(this));
|
|
}
|
|
|
|
change (data) {
|
|
this.node.innerText = data.text;
|
|
this.node.style.transform = data.transform;
|
|
}
|
|
}
|
|
|
|
class RangeLimit extends api.core.Instance {
|
|
static get instanceClassName () {
|
|
return 'RangeLimit';
|
|
}
|
|
|
|
init () {
|
|
switch (true) {
|
|
case this.matches(RangeSelector.RANGE_MIN):
|
|
this.addDescent(RangeEmission.MIN, this.change.bind(this));
|
|
break;
|
|
|
|
case this.matches(RangeSelector.RANGE_MAX):
|
|
this.addDescent(RangeEmission.MAX, this.change.bind(this));
|
|
break;
|
|
}
|
|
}
|
|
|
|
change (text) {
|
|
this.node.innerText = text;
|
|
}
|
|
}
|
|
|
|
api.range = {
|
|
Range: Range,
|
|
RangeInput: RangeInput,
|
|
RangeInput2: RangeInput2,
|
|
RangeOutput: RangeOutput,
|
|
RangeLimit: RangeLimit,
|
|
RangeEmission: RangeEmission,
|
|
RangeSelector: RangeSelector
|
|
};
|
|
|
|
api.internals.register(api.range.RangeSelector.RANGE, api.range.Range);
|
|
api.internals.register(api.range.RangeSelector.RANGE_INPUT, api.range.RangeInput);
|
|
api.internals.register(api.range.RangeSelector.RANGE_INPUT2, api.range.RangeInput2);
|
|
api.internals.register(api.range.RangeSelector.RANGE_OUTPUT, api.range.RangeOutput);
|
|
api.internals.register(api.range.RangeSelector.RANGE_MIN, api.range.RangeLimit);
|
|
api.internals.register(api.range.RangeSelector.RANGE_MAX, api.range.RangeLimit);
|
|
//# sourceMappingURL=range.module.js.map
|