UNPKG

@ishitatsuyuki/oruga-next

Version:

UI components for Vue.js and CSS framework agnostic

722 lines (707 loc) 25.4 kB
'use strict'; var vue = require('vue'); var helpers = require('./helpers.js'); var config = require('./config.js'); var BaseComponentMixin = require('./BaseComponentMixin-a03c02e3.js'); var Tooltip = require('./Tooltip-48d63c9f.js'); var script = vue.defineComponent({ name: 'OSliderThumb', components: { [Tooltip.script.name]: Tooltip.script }, configField: 'slider', inheritAttrs: false, inject: ['$slider'], emits: ['update:modelValue', 'dragstart', 'dragend'], props: { modelValue: { type: Number, default: 0 }, variant: { type: String, default: '' }, tooltip: { type: Boolean, default: true }, indicator: { type: Boolean, default: false }, customFormatter: Function, format: { type: String, default: 'raw', validator: (value) => { return [ 'raw', 'percent' ].indexOf(value) >= 0; } }, locale: { type: [String, Array], default: () => { return helpers.getValueByPath(config.getOptions(), 'locale'); } }, tooltipAlways: { type: Boolean, default: false } }, data() { return { isFocused: false, dragging: false, startX: 0, startPosition: 0, newPosition: null, oldValue: this.modelValue }; }, computed: { disabled() { return this.$parent.disabled; }, max() { return this.$parent.max; }, min() { return this.$parent.min; }, step() { return this.$parent.step; }, precision() { return this.$parent.precision; }, currentPosition() { return `${(this.modelValue - this.min) / (this.max - this.min) * 100}%`; }, wrapperStyle() { return { left: this.currentPosition }; }, formattedValue() { if (typeof this.customFormatter !== 'undefined') { return this.customFormatter(this.modelValue); } if (this.format === 'percent') { return new Intl.NumberFormat(this.locale, { style: 'percent' }).format(((this.modelValue - this.min)) / (this.max - this.min)); } return new Intl.NumberFormat(this.locale).format(this.modelValue); } }, methods: { onFocus() { this.isFocused = true; }, onBlur() { this.isFocused = false; }, onButtonDown(event) { if (this.disabled) return; event.preventDefault(); this.onDragStart(event); if (typeof window !== 'undefined') { document.addEventListener('mousemove', this.onDragging); document.addEventListener('touchmove', this.onDragging); document.addEventListener('mouseup', this.onDragEnd); document.addEventListener('touchend', this.onDragEnd); document.addEventListener('contextmenu', this.onDragEnd); } }, onLeftKeyDown() { if (this.disabled || this.modelvalue === this.min) return; this.newPosition = parseFloat(this.currentPosition) - this.step / (this.max - this.min) * 100; this.setPosition(this.newPosition); this.$parent.emitValue('change'); }, onRightKeyDown() { if (this.disabled || this.modelvalue === this.max) return; this.newPosition = parseFloat(this.currentPosition) + this.step / (this.max - this.min) * 100; this.setPosition(this.newPosition); this.$parent.emitValue('change'); }, onHomeKeyDown() { if (this.disabled || this.modelvalue === this.min) return; this.newPosition = 0; this.setPosition(this.newPosition); this.$parent.emitValue('change'); }, onEndKeyDown() { if (this.disabled || this.modelvalue === this.max) return; this.newPosition = 100; this.setPosition(this.newPosition); this.$parent.emitValue('change'); }, onDragStart(event) { this.dragging = true; this.$emit('dragstart'); if (event.type === 'touchstart') { event.clientX = event.touches[0].clientX; } this.startX = event.clientX; this.startPosition = parseFloat(this.currentPosition); this.newPosition = this.startPosition; }, onDragging(event) { if (this.dragging) { if (event.type === 'touchmove') { event.clientX = event.touches[0].clientX; } const diff = (event.clientX - this.startX) / this.$parent.sliderSize() * 100; this.newPosition = this.startPosition + diff; this.setPosition(this.newPosition); } }, onDragEnd() { this.dragging = false; this.$emit('dragend'); if (this.modelvalue !== this.oldValue) { this.$parent.emitValue('change'); } this.setPosition(this.newPosition); if (typeof window !== 'undefined') { document.removeEventListener('mousemove', this.onDragging); document.removeEventListener('touchmove', this.onDragging); document.removeEventListener('mouseup', this.onDragEnd); document.removeEventListener('touchend', this.onDragEnd); document.removeEventListener('contextmenu', this.onDragEnd); } }, setPosition(percent) { if (percent === null || isNaN(percent)) return; if (percent < 0) { percent = 0; } else if (percent > 100) { percent = 100; } const stepLength = 100 / ((this.max - this.min) / this.step); const steps = Math.round(percent / stepLength); let value = steps * stepLength / 100 * (this.max - this.min) + this.min; value = parseFloat(value.toFixed(this.precision)); this.$emit('update:modelValue', value); if (!this.dragging && value !== this.oldValue) { this.oldValue = value; } } } }); const _hoisted_1 = { key: 0 }; function render(_ctx, _cache, $props, $setup, $data, $options) { const _component_o_tooltip = vue.resolveComponent("o-tooltip"); return vue.openBlock(), vue.createBlock("div", { class: _ctx.$slider.thumbWrapperClasses, style: _ctx.wrapperStyle }, [vue.createVNode(_component_o_tooltip, { label: _ctx.formattedValue, variant: _ctx.variant, always: _ctx.dragging || _ctx.isFocused || _ctx.tooltipAlways, active: !_ctx.disabled && _ctx.tooltip }, { default: vue.withCtx(() => [vue.createVNode("div", vue.mergeProps(_ctx.$attrs, { class: _ctx.$slider.thumbClasses, tabindex: _ctx.disabled ? false : 0, onMousedown: _cache[1] || (_cache[1] = (...args) => _ctx.onButtonDown(...args)), onTouchstart: _cache[2] || (_cache[2] = (...args) => _ctx.onButtonDown(...args)), onFocus: _cache[3] || (_cache[3] = (...args) => _ctx.onFocus(...args)), onBlur: _cache[4] || (_cache[4] = (...args) => _ctx.onBlur(...args)), onKeydown: [_cache[5] || (_cache[5] = vue.withKeys(vue.withModifiers((...args) => _ctx.onLeftKeyDown(...args), ["prevent"]), ["left"])), _cache[6] || (_cache[6] = vue.withKeys(vue.withModifiers((...args) => _ctx.onRightKeyDown(...args), ["prevent"]), ["right"])), _cache[7] || (_cache[7] = vue.withKeys(vue.withModifiers((...args) => _ctx.onLeftKeyDown(...args), ["prevent"]), ["down"])), _cache[8] || (_cache[8] = vue.withKeys(vue.withModifiers((...args) => _ctx.onRightKeyDown(...args), ["prevent"]), ["up"])), _cache[9] || (_cache[9] = vue.withKeys(vue.withModifiers((...args) => _ctx.onHomeKeyDown(...args), ["prevent"]), ["home"])), _cache[10] || (_cache[10] = vue.withKeys(vue.withModifiers((...args) => _ctx.onEndKeyDown(...args), ["prevent"]), ["end"]))] }), [_ctx.indicator ? (vue.openBlock(), vue.createBlock("span", _hoisted_1, vue.toDisplayString(_ctx.formattedValue), 1 /* TEXT */ )) : vue.createCommentVNode("v-if", true)], 16 /* FULL_PROPS */ , ["tabindex"])]), _: 1 }, 8 /* PROPS */ , ["label", "variant", "always", "active"])], 6 /* CLASS, STYLE */ ); } script.render = render; script.__file = "src/components/slider/SliderThumb.vue"; /** * @displayName Slider Tick */ var script$1 = vue.defineComponent({ name: 'OSliderTick', mixins: [BaseComponentMixin.BaseComponentMixin], configField: 'slider', inject: ['$slider'], props: { /** Value of single tick */ value: { variant: Number, default: 0 }, tickClass: [String, Function, Array], tickHiddenClass: [String, Function, Array], tickLabelClass: [String, Function, Array] }, computed: { rootClasses() { return [ this.computedClass('tickClass', 'o-slide__tick'), { [this.computedClass('tickHiddenClass', 'o-slide__tick--hidden')]: this.hidden }, ]; }, tickLabelClasses() { return [ this.computedClass('tickLabelClass', 'o-slide__tick-label') ]; }, position() { const pos = (this.value - this.$parent.min) / (this.$parent.max - this.$parent.min) * 100; return (pos >= 0 && pos <= 100) ? pos : 0; }, hidden() { return this.value === this.$parent.min || this.value === this.$parent.max; }, tickStyle() { return { 'left': this.position + '%' }; } }, created() { if (!this.$slider) { throw new Error('You should wrap oSliderTick on a oSlider'); } } }); function render$1(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createBlock("div", { class: _ctx.rootClasses, style: _ctx.tickStyle }, [_ctx.$slots.default ? (vue.openBlock(), vue.createBlock("span", { key: 0, class: _ctx.tickLabelClasses }, [vue.renderSlot(_ctx.$slots, "default")], 2 /* CLASS */ )) : vue.createCommentVNode("v-if", true)], 6 /* CLASS, STYLE */ ); } script$1.render = render$1; script$1.__file = "src/components/slider/SliderTick.vue"; /** * A slider to select a value or range from a given range * @displayName Slider * @requires ./SliderTick.vue * @style _slider.scss */ var script$2 = vue.defineComponent({ name: 'OSlider', components: { [script.name]: script, [script$1.name]: script$1 }, configField: 'slider', mixins: [BaseComponentMixin.BaseComponentMixin], provide() { return { $slider: this }; }, emits: ['update:modelValue', 'change', 'dragging', 'dragstart', 'dragend'], props: { /** @model */ modelValue: { type: [Number, Array], default: 0 }, /** Minimum value */ min: { type: Number, default: 0 }, /** Maximum value */ max: { type: Number, default: 100 }, /** Step interval of ticks */ step: { type: Number, default: 1 }, /** * Color of the slider * @values primary, info, success, warning, danger, and any other custom color */ variant: { type: String }, /** * Vertical size of slider, optional * @values small, medium, large */ size: String, /** Show tick marks */ ticks: { type: Boolean, default: false }, /** Show tooltip when thumb is being dragged */ tooltip: { type: Boolean, default: () => { return helpers.getValueByPath(config.getOptions(), 'slider.tooltip', true); } }, /** * Color of the tooltip * @values primary, info, success, warning, danger, and any other custom color */ tooltipVariant: String, /** Rounded thumb */ rounded: { type: Boolean, default: () => { return helpers.getValueByPath(config.getOptions(), 'slider.rounded', false); } }, disabled: { type: Boolean, default: false }, /** Update v-model only when dragging is finished */ lazy: { type: Boolean, default: false }, /** Function to format the tooltip label for display */ customFormatter: Function, ariaLabel: [String, Array], /** Increases slider size on focus */ biggerSliderFocus: { type: Boolean, default: false }, indicator: { type: Boolean, default: false }, format: { type: String, default: 'raw', validator: (value) => { return [ 'raw', 'percent' ].indexOf(value) >= 0; } }, locale: { type: [String, Array], default: () => { return helpers.getValueByPath(config.getOptions(), 'locale'); } }, /** Tooltip displays always */ tooltipAlways: { type: Boolean, default: false }, rootClass: [String, Function, Array], sizeClass: [String, Function, Array], trackClass: [String, Function, Array], fillClass: [String, Function, Array], thumbRoundedClass: [String, Function, Array], thumbDraggingClass: [String, Function, Array], disabledClass: [String, Function, Array], thumbWrapperClass: [String, Function, Array], thumbClass: [String, Function, Array], variantClass: [String, Function, Array] }, data() { return { value1: null, value2: null, dragging: false, isRange: false }; }, computed: { rootClasses() { return [ this.computedClass('rootClass', 'o-slide'), { [this.computedClass('sizeClass', 'o-slide--', this.size)]: this.size }, { [this.computedClass('disabledClass', 'o-slide--disabled')]: this.disabled }, ]; }, trackClasses() { return [ this.computedClass('trackClass', 'o-slide__track'), ]; }, fillClasses() { return [ this.computedClass('fillClass', 'o-slide__fill'), { [this.computedClass('variantClass', 'o-slide__fill--', this.variant)]: this.variant }, ]; }, thumbClasses() { return [ this.computedClass('thumbClass', 'o-slide__thumb'), { [this.computedClass('thumbDraggingClass', 'o-slide__thumb--dragging')]: this.dragging }, { [this.computedClass('thumbRoundedClass', 'o-slide__thumb--rounded')]: this.rounded }, ]; }, thumbWrapperClasses() { return [ this.computedClass('thumbWrapperClass', 'o-slide__thumb-wrapper'), ]; }, newTooltipVariant() { return this.tooltipVariant ? this.tooltipVariant : this.variant; }, tickValues() { if (!this.ticks || this.min > this.max || this.step === 0) return []; const result = []; for (let i = this.min + this.step; i < this.max; i = i + this.step) { result.push(i); } return result; }, minValue() { return Math.min(this.value1, this.value2); }, maxValue() { return Math.max(this.value1, this.value2); }, barSize() { return this.isRange ? `${100 * (this.maxValue - this.minValue) / (this.max - this.min)}%` : `${100 * (this.value1 - this.min) / (this.max - this.min)}%`; }, barStart() { return this.isRange ? `${100 * (this.minValue - this.min) / (this.max - this.min)}%` : '0%'; }, precision() { const precisions = [this.min, this.max, this.step].map((item) => { const decimal = ('' + item).split('.')[1]; return decimal ? decimal.length : 0; }); return Math.max(...precisions); }, barStyle() { return { width: this.barSize, left: this.barStart }; } }, watch: { value1() { this.onInternalValueUpdate(); }, value2() { this.onInternalValueUpdate(); }, min() { this.setValues(this.value); }, max() { this.setValues(this.value); }, /** * When v-model is changed set the new active step. */ modelValue(value) { this.setValues(value); } }, methods: { setValues(newValue) { if (this.min > this.max) { return; } if (Array.isArray(newValue)) { this.isRange = true; const smallValue = typeof newValue[0] !== 'number' || isNaN(newValue[0]) ? this.min : Math.min(Math.max(this.min, newValue[0]), this.max); const largeValue = typeof newValue[1] !== 'number' || isNaN(newValue[1]) ? this.max : Math.max(Math.min(this.max, newValue[1]), this.min); this.value1 = this.isThumbReversed ? largeValue : smallValue; this.value2 = this.isThumbReversed ? smallValue : largeValue; } else { this.isRange = false; this.value1 = isNaN(newValue) ? this.min : Math.min(this.max, Math.max(this.min, newValue)); this.value2 = null; } }, onInternalValueUpdate() { if (this.isRange) { this.isThumbReversed = this.value1 > this.value2; } if (!this.lazy || !this.dragging) { this.emitValue('update:modelValue'); } if (this.dragging) { this.emitValue('dragging'); } }, sliderSize() { return this.$refs.slider.getBoundingClientRect().width; }, onSliderClick(event) { if (this.disabled || this.isTrackClickDisabled) return; const sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left; const percent = (event.clientX - sliderOffsetLeft) / this.sliderSize() * 100; const targetValue = this.min + percent * (this.max - this.min) / 100; const diffFirst = Math.abs(targetValue - this.value1); if (!this.isRange) { if (diffFirst < this.step / 2) return; this.$refs.button1.setPosition(percent); } else { const diffSecond = Math.abs(targetValue - this.value2); if (diffFirst <= diffSecond) { if (diffFirst < this.step / 2) return; this.$refs['button1'].setPosition(percent); } else { if (diffSecond < this.step / 2) return; this.$refs['button2'].setPosition(percent); } } this.emitValue('change'); }, onDragStart() { this.dragging = true; this.$emit('dragstart'); }, onDragEnd() { this.isTrackClickDisabled = true; setTimeout(() => { // avoid triggering onSliderClick after dragend this.isTrackClickDisabled = false; }, 0); this.dragging = false; this.$emit('dragend'); if (this.lazy) { this.emitValue('update:modelValue'); } }, emitValue(event) { const val = this.isRange ? [this.minValue, this.maxValue] : this.value1; this.$emit(event, val); } }, created() { this.isThumbReversed = false; this.isTrackClickDisabled = false; this.setValues(this.modelValue); } }); function render$2(_ctx, _cache, $props, $setup, $data, $options) { const _component_o_slider_tick = vue.resolveComponent("o-slider-tick"); const _component_o_slider_thumb = vue.resolveComponent("o-slider-thumb"); return vue.openBlock(), vue.createBlock("div", { onClick: _cache[3] || (_cache[3] = (...args) => _ctx.onSliderClick(...args)), class: _ctx.rootClasses }, [vue.createVNode("div", { class: _ctx.trackClasses, ref: "slider" }, [vue.createVNode("div", { class: _ctx.fillClasses, style: _ctx.barStyle }, null, 6 /* CLASS, STYLE */ ), _ctx.ticks ? (vue.openBlock(true), vue.createBlock(vue.Fragment, { key: 0 }, vue.renderList(_ctx.tickValues, (val, key) => { return vue.openBlock(), vue.createBlock(_component_o_slider_tick, { key: key, value: val }, null, 8 /* PROPS */ , ["value"]); }), 128 /* KEYED_FRAGMENT */ )) : vue.createCommentVNode("v-if", true), vue.renderSlot(_ctx.$slots, "default"), vue.createVNode(_component_o_slider_thumb, { modelValue: _ctx.value1, "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => _ctx.value1 = $event), variant: _ctx.newTooltipVariant, tooltip: _ctx.tooltip, "custom-formatter": _ctx.customFormatter, indicator: _ctx.indicator, ref: "button1", role: "slider", format: _ctx.format, locale: _ctx.locale, "tooltip-always": _ctx.tooltipAlways, "aria-valuenow": _ctx.value1, "aria-valuemin": _ctx.min, "aria-valuemax": _ctx.max, "aria-orientation": "horizontal", "aria-label": Array.isArray(_ctx.ariaLabel) ? _ctx.ariaLabel[0] : _ctx.ariaLabel, "aria-disabled": _ctx.disabled, onDragstart: _ctx.onDragStart, onDragend: _ctx.onDragEnd }, null, 8 /* PROPS */ , ["modelValue", "variant", "tooltip", "custom-formatter", "indicator", "format", "locale", "tooltip-always", "aria-valuenow", "aria-valuemin", "aria-valuemax", "aria-label", "aria-disabled", "onDragstart", "onDragend"]), _ctx.isRange ? vue.createVNode(_component_o_slider_thumb, { key: 1, modelValue: _ctx.value2, "onUpdate:modelValue": _cache[2] || (_cache[2] = $event => _ctx.value2 = $event), variant: _ctx.newTooltipVariant, tooltip: _ctx.tooltip, "custom-formatter": _ctx.customFormatter, indicator: _ctx.indicator, ref: "button2", role: "slider", format: _ctx.format, locale: _ctx.locale, "tooltip-always": _ctx.tooltipAlways, "aria-valuenow": _ctx.value2, "aria-valuemin": _ctx.min, "aria-valuemax": _ctx.max, "aria-orientation": "horizontal", "aria-label": Array.isArray(_ctx.ariaLabel) ? _ctx.ariaLabel[1] : '', "aria-disabled": _ctx.disabled, onDragstart: _ctx.onDragStart, onDragend: _ctx.onDragEnd }, null, 8 /* PROPS */ , ["modelValue", "variant", "tooltip", "custom-formatter", "indicator", "format", "locale", "tooltip-always", "aria-valuenow", "aria-valuemin", "aria-valuemax", "aria-label", "aria-disabled", "onDragstart", "onDragend"]) : vue.createCommentVNode("v-if", true)], 2 /* CLASS */ )], 2 /* CLASS */ ); } script$2.render = render$2; script$2.__file = "src/components/slider/Slider.vue"; exports.script = script$2; exports.script$1 = script$1;