UNPKG

@telekom/scale-components

Version:

Scale is the digital design system for Telekom products and experiences.

259 lines (253 loc) 16.2 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const index = require('./index-a0ea3d79.js'); const index$1 = require('./index-53f5a5fc.js'); const utils = require('./utils-e9c3b953.js'); const statusNote = require('./status-note-dceee5a3.js'); const sliderCss = ":host{--width:368px;--height-track:6px;--background-track:var(--telekom-color-ui-faint);--radius-track:var(--telekom-radius-pill);--spacing-track:var(--telekom-spacing-composition-space-07) 0\n var(--telekom-spacing-composition-space-06);--spacing-x-inner-track:10px;--background-bar:var(--telekom-color-primary-standard);--radius-thumb:var(--telekom-radius-circle);--size-thumb:24px;--border-color-thumb:rgba(0, 0, 0, 0.05);--background-thumb:var(--telekom-color-ui-white);--shadow-thumb:0 0 2px 0 rgba(0, 0, 0, 0.24), 0 2px 4px 0 rgba(0, 0, 0, 0.24),\n 0 4px 12px 0 rgba(0, 0, 0, 0.26);--color-focus:var(--telekom-color-functional-focus-standard);--spacing-x-step-marks:8px;--color-step-mark:var(--telekom-color-text-and-icon-additional);--radius-step-mark:var(--telekom-radius-circle);--size-step-mark:4px;--font-label:var(--telekom-text-style-ui);--font-helper-text:var(--telekom-text-style-small-bold);--color-helper-text:var(--telekom-color-text-and-icon-additional)}[part~='base']{width:var(--width)}[part='label-wrapper']{display:flex;justify-content:space-between;align-items:flex-start}[part='label']{font:var(--font-label)}[part='value-text']{font:var(--font-label);font-variant-numeric:tabular-nums}[part='track-wrapper']{display:flex;position:relative;align-items:center}[part='track']{position:relative;box-sizing:border-box;display:flex;align-items:center;margin:var(--spacing-track);width:100%;height:var(--height-track);background:var(--background-track);border-radius:var(--radius-track);border:1px solid transparent}[part='inner-track']{position:absolute;display:flex;align-items:center;left:var(--spacing-x-inner-track);width:calc(100% - var(--spacing-x-inner-track) * 2);height:100%}[part='bar']{height:100%;position:absolute;z-index:1;border-radius:var(--radius-track);background-color:var(--background-bar);border:1px solid transparent}[part~='thumb-wrapper']{position:absolute;z-index:3;display:flex;align-items:center;justify-content:center;width:32px;height:32px;margin-left:-16px;background-color:transparent}[part~='thumb']{--_border:0 0 0 var(--telekom-spacing-composition-space-01)\n var(--border-color-thumb);width:var(--size-thumb);height:var(--size-thumb);box-sizing:border-box;border-radius:var(--radius-thumb);background-color:var(--background-thumb);box-shadow:var(--_border), var(--shadow-thumb);border:1px solid transparent}[part~='thumb']:focus{outline:var(--telekom-line-weight-highlight) solid var(--color-focus);outline-offset:1px}[part='step-marks']{width:100%;position:relative;z-index:2;display:flex;justify-content:space-between;padding:0 var(--spacing-x-step-marks)}[part='step-mark']{width:var(--size-step-mark);height:var(--size-step-mark);background:var(--color-step-mark);border-radius:var(--telekom-radius-circle)}@media screen and (forced-colors: active), (-ms-high-contrast: active){[part='step-mark']{border:2px solid}}[part='meta']{display:flex;justify-content:space-between}[part='helper-text']{font:var(--font-helper-text);color:var(--color-helper-text)}[part~='disabled'] [part='label-wrapper'],[part~='disabled'] [part='helper-text']{color:var(--telekom-color-text-and-icon-disabled)}[part~='disabled'] [part='bar']{background-color:var(--telekom-color-ui-border-disabled)}[part~='disabled'] [part~='thumb-wrapper']{display:none}[part~='thumb-wrapper']:hover{cursor:grab}[part~='thumb-wrapper']:active{cursor:grabbing}[part~='disabled'] [part='track-wrapper']{cursor:not-allowed}:host-context([data-platform='ios']){--height-track:4px;--size-thumb:26px;--size-step-mark:2px}:host([platform='ios']){--height-track:4px;--size-thumb:26px;--size-step-mark:2px}:host-context([data-platform='android']){--background-thumb:var(--telekom-color-primary-standard)}:host([platform='android']){--background-thumb:var(--telekom-color-primary-standard)}:host-context([data-platform='android']) [part~='thumb']{box-shadow:var(--_border), var(--telekom-shadow-raised-standard)}:host([platform='android']) [part~='thumb']{box-shadow:var(--_border), var(--telekom-shadow-raised-standard)}"; const Slider = class { constructor(hostRef) { index.registerInstance(this, hostRef); this.scaleChange = index.createEvent(this, "scale-change", 7); this.scaleChangeLegacy = index.createEvent(this, "scaleChange", 7); this.scaleInput = index.createEvent(this, "scale-input", 7); this.scaleInputLegacy = index.createEvent(this, "scaleInput", 7); /** (optional) the value of the slider */ this.value = 0; /** (optional) multi-thumb */ this.range = false; /** (optional) when `range` is true, the "from" value */ this.valueFrom = 0; /** (optional) when `range` is true, the "to" value */ this.valueTo = 0; /** t(optional) he minimal value of the slider */ this.min = 0; /** (optional) the maximal value of the slider */ this.max = 100; /** (optional) the step size to increase or decrease when dragging slider */ this.step = 1; /** (optional) show a mark for each step */ this.showStepMarks = false; /** (optional) slider display value */ this.showValue = true; /** (optional) slider value unit */ this.unit = ''; /** (optional) unit position */ this.unitPosition = 'after'; /** (optional) number of decimal places */ this.decimals = 0; /** (optional) disabled */ this.disabled = false; /** (optional) Aria label for range slider */ this.innerAriaValueText = '$from to $to'; // The actual position in % of the slider thumb this.position = 0; this.positionFrom = 25; this.positionTo = 75; // Don't know how to make TypeScript handle `this[offsetKey]` // private offsetLeft: number; // private offsetLeftFrom: number; // private offsetLeftTo: number; this.activeRangeThumb = null; this.internalId = utils.generateUniqueId(); this.onButtonDown = (event) => { if (this.disabled) { return; } this.setActiveRangeThumbFromEvent(event); this.onDragStart(); this.addGlobalListeners(); }; this.onKeyDown = (event) => { let steps = 0; this.setActiveRangeThumbFromEvent(event); if (['ArrowRight', 'ArrowLeft'].includes(event.key)) { steps = event.key === 'ArrowRight' ? this.step : -this.step; } if (['ArrowUp', 'ArrowDown'].includes(event.key)) { steps = event.key === 'ArrowUp' ? this.step * 10 : -this.step * 10; } const valueKey = this.getKeyFor('value'); this.setValue(this[valueKey] + steps, valueKey); utils.emitEvent(this, 'scaleChange', this.range ? [this.valueFrom, this.valueTo] : this.value); }; this.onDragStart = () => { const offsetKey = this.getKeyFor('offsetLeft'); this.dragging = true; this[offsetKey] = this.sliderTrack.getBoundingClientRect().left; }; this.onDragging = (event) => { if (!this.dragging) { return; } const valueKey = this.getKeyFor('value'); const offsetLeftKey = this.getKeyFor('offsetLeft'); const offsetLeft = this[offsetLeftKey]; const currentX = this.handleTouchEvent(event).clientX; const position = ((currentX - offsetLeft) / this.sliderTrack.offsetWidth) * 100; const nextValue = (position * (this.max - this.min)) / 100 + this.min; // https://stackoverflow.com/q/14627566 const roundedNextValue = Math.ceil(nextValue / this.step) * this.step; this.setValue(roundedNextValue, valueKey); }; this.onDragEnd = () => { this.dragging = false; utils.emitEvent(this, 'scaleChange', this.range ? [this.valueFrom, this.valueTo] : this.value); this.removeGlobalListeners(); }; this.setValue = (nextValue, valueKey = 'value') => { this[valueKey] = this.clamp(nextValue); utils.emitEvent(this, 'scaleInput', this.range ? [this.valueFrom, this.valueTo] : this.value); }; this.setActiveRangeThumbFromEvent = (event) => { if (!this.range) { this.activeRangeThumb = null; return; } const part = event.target.part; this.activeRangeThumb = part.contains('from') ? 'From' : 'To'; }; this.setPosition = (thumb) => { const valueKey = this.getKeyFor('value', thumb); const positionKey = this.getKeyFor('position', thumb); const clampedValue = this.clamp(this[valueKey]); // https://stackoverflow.com/a/25835683 // ((input - min) * 100) / (max - min) this[positionKey] = ((clampedValue - this.min) * 100) / (this.max - this.min); }; /** * Utility function * e.g. 'value' -> 'valueFrom' if `activeRangeThumb='From'` * @param propName * @returns {string} The prop name with the range suffix if needed */ this.getKeyFor = (propName, thumb) => { var _a; if (this.range) { return `${propName}${(_a = this.activeRangeThumb) !== null && _a !== void 0 ? _a : thumb}`; } return propName; }; this.getTextValue = () => { var _a, _b, _c, _d; if (this.range) { const from = (_a = this.valueFrom) === null || _a === void 0 ? void 0 : _a.toFixed(this.decimals); const to = (_b = this.valueTo) === null || _b === void 0 ? void 0 : _b.toFixed(this.decimals); return this.unitPosition === 'before' ? `${this.unit}${from} - ${this.unit}${to}` : `${from}${this.unit} - ${to}${this.unit}`; } return this.unitPosition === 'before' ? `${this.unit}${(_c = this.value) === null || _c === void 0 ? void 0 : _c.toFixed(this.decimals)}` : `${(_d = this.value) === null || _d === void 0 ? void 0 : _d.toFixed(this.decimals)}${this.unit}`; }; this.getNumberOfSteps = () => { const n = (this.max - this.min) / this.step + 1; return [...Array(n).keys()]; }; this.clamp = (val) => { let min = this.min; let max = this.max; // Take into account the other thumb, when `range=true` if (this.range) { if (this.activeRangeThumb === 'From') { max = Math.min(this.valueTo, this.max); } else if (this.activeRangeThumb === 'To') { min = Math.max(this.valueFrom, this.min); } } // Regular generic clamp return Math.min(Math.max(val, min), max); }; this.onDragging = this.onDragging.bind(this); this.onDragEnd = this.onDragEnd.bind(this); } handleValueChange() { this.setPosition(); } componentWillLoad() { if (this.sliderId == null) { this.sliderId = 'slider-' + this.internalId; } // Set initial position if (this.range) { this.setPosition('From'); this.setPosition('To'); } else { this.setPosition(); } } disconnectedCallback() { this.removeGlobalListeners(); } componentDidLoad() { if (this.customColor !== undefined) { statusNote.statusNote({ tag: 'deprecated', message: `Property "customColor" is deprecated. Please use css variable "--background-bar" to set the slider-bar color; e.g. <scale-slider value="20" style="--background-bar: green"></scale-slider>`, type: 'warn', source: this.hostElement, }); } if (this.thumbLarge !== undefined) { statusNote.statusNote({ tag: 'deprecated', message: `Property "thumbLarge" is deprecated.`, type: 'warn', source: this.hostElement, }); } if (this.trackSmall !== undefined) { statusNote.statusNote({ tag: 'deprecated', message: `Property "trackSmall" is deprecated.`, type: 'warn', source: this.hostElement, }); } } handleTouchEvent(event) { return event.type.indexOf('touch') === 0 ? event.touches[0] : event; } addGlobalListeners() { window.addEventListener('mousemove', this.onDragging); window.addEventListener('mouseup', this.onDragEnd); window.addEventListener('touchmove', this.onDragging); window.addEventListener('touchend', this.onDragEnd); } removeGlobalListeners() { window.removeEventListener('mousemove', this.onDragging); window.removeEventListener('mouseup', this.onDragEnd); window.removeEventListener('touchmove', this.onDragging); window.removeEventListener('touchend', this.onDragEnd); } getRangeAriaValueText() { const filledText = this.innerAriaValueText .replace(/\$from/g, `${this.valueFrom}`) .replace(/\$to/g, `${this.valueTo}`); return filledText; } render() { const helperTextId = `helper-message-${this.internalId}`; const ariaDescribedByAttr = { 'aria-describedBy': helperTextId }; return (index.h(index.Host, null, this.styles && index.h("style", null, this.styles), index.h("div", { part: index$1.classnames('base', this.disabled && 'disabled') }, index.h("div", { part: "label-wrapper" }, !!this.label && (index.h("label", { part: "label", id: `${this.sliderId}-label`, htmlFor: this.sliderId }, this.label)), this.showValue && (index.h("div", { part: "value-text" }, this.getTextValue()))), index.h("div", { part: "track-wrapper" }, index.h("div", { part: "track", ref: (el) => (this.sliderTrack = el) }, index.h("div", { part: "bar", style: { left: (this.range ? this.positionFrom : 0) + '%', width: `${this.range ? this.positionTo - this.positionFrom : this.position}%`, } }), this.showStepMarks && (index.h("div", { part: "step-marks" }, this.getNumberOfSteps().map(() => (index.h("span", { part: "step-mark" }))))), index.h("div", { part: "inner-track" }, this.range ? (index.h(index.Fragment, null, index.h("div", { part: "thumb-wrapper from", style: { left: `${this.positionFrom}%` }, onMouseDown: this.onButtonDown, onTouchStart: this.onButtonDown }, index.h("div", Object.assign({ part: "thumb from", tabindex: "0", role: "slider", id: this.sliderId + '-from', "aria-valuemin": this.min, "aria-valuenow": `${this.valueFrom} to ${this.valueTo}`, "aria-valuemax": this.max, "aria-valuetext": `${this.valueFrom} to ${this.valueTo}`, "aria-labelledby": `${this.sliderId}-label`, "aria-orientation": "horizontal", "aria-disabled": this.disabled }, (this.helperText ? ariaDescribedByAttr : {}), { onKeyDown: this.onKeyDown }))), index.h("div", { part: "thumb-wrapper to", style: { left: `${this.positionTo}%` }, onMouseDown: this.onButtonDown, onTouchStart: this.onButtonDown }, index.h("div", Object.assign({ part: "thumb to", tabindex: "0", role: "slider", id: this.sliderId + '-to', "aria-valuemin": this.min, "aria-valuenow": this.value, "aria-valuemax": this.max, "aria-valuetext": this.getRangeAriaValueText(), "aria-labelledby": `${this.sliderId}-label`, "aria-orientation": "horizontal", "aria-disabled": this.disabled }, (this.helperText ? ariaDescribedByAttr : {}), { onKeyDown: this.onKeyDown }))))) : (index.h("div", { part: "thumb-wrapper", style: { left: `${this.position}%` }, onMouseDown: this.onButtonDown, onTouchStart: this.onButtonDown }, index.h("div", Object.assign({ part: "thumb", tabindex: "0", role: "slider", id: this.sliderId, "aria-valuemin": this.min, "aria-valuenow": this.value, "aria-valuemax": this.max, "aria-valuetext": `${this.value}`, "aria-labelledby": `${this.sliderId}-label`, "aria-orientation": "horizontal", "aria-disabled": this.disabled }, (this.helperText ? ariaDescribedByAttr : {}), { onKeyDown: this.onKeyDown }))))))), index.h("input", { type: "hidden", value: this.getTextValue(), name: this.name }), this.helperText && (index.h("div", { part: "meta", id: helperTextId, "aria-live": "polite", "aria-relevant": "additions removals" }, index.h("div", { part: "helper-text" }, this.helperText)))))); } get hostElement() { return index.getElement(this); } static get watchers() { return { "value": ["handleValueChange"], "valueFrom": ["handleValueChange"], "valueTo": ["handleValueChange"] }; } }; Slider.style = sliderCss; exports.scale_slider = Slider;