UNPKG

@coreui/vue-pro

Version:

UI Components Library for Vue.js

322 lines (318 loc) 14.1 kB
'use strict'; var vue = require('vue'); var utils = require('./utils.js'); var isRTL = require('../../utils/isRTL.js'); const CRangeSlider = vue.defineComponent({ name: 'CRangeSlider', props: { /** * Enable or disable clickable labels in the Vue Range Slider. * When set to `true`, users can click on labels to adjust the slider's value directly, enhancing interactivity and user experience. */ clickableLabels: { type: Boolean, default: true, }, /** * Control the interactive state of the Vue Range Slider with the `disabled` prop. * Setting it to `true` will disable all slider functionalities, preventing user interaction and visually indicating a non-interactive state. */ disabled: { type: Boolean, default: false, }, /** * Define the minimum distance between slider handles using the `distance` prop in the Vue Range Slider. * This ensures that the handles maintain a specified separation, preventing overlap and maintaining clear value distinctions. */ distance: { type: Number, default: 0, }, /** * Add descriptive labels to your Vue Range Slider by providing an array of `labels`. * These labels enhance the slider's usability by clearly indicating key values and providing contextual information to users. */ labels: { type: Array, default: () => [], }, /** * Specify the maximum value for the Vue Range Slider with the `max` prop. * This determines the upper limit of the slider's range, enabling precise control over the highest selectable value. */ max: { type: Number, default: 100, }, /** * Set the minimum value for the Vue Range Slider using the `min` prop. * This defines the lower bound of the slider's range, allowing you to control the starting point of user selection. */ min: { type: Number, default: 0, }, /** * The default name for a value passed using v-model. */ modelValue: [Number, Array], /** * Assign a `name` to the Vue Range Slider for form integration. * Whether using a single string or an array of strings, this prop ensures that the slider's values are correctly identified when submitting forms. */ name: { type: [String, Array], default: '', }, /** * Control the granularity of the Vue Range Slider by setting the `step` prop. * This defines the increment intervals between selectable values, allowing for precise adjustments based on your application's requirements. */ step: { type: Number, default: 1, }, /** * Toggle the visibility of tooltips in the Vue Range Slider with the `tooltips` prop. * When enabled, tooltips display the current value of the slider handles, providing real-time feedback to users. */ tooltips: { type: Boolean, default: true, }, /** * Customize the display format of tooltips in the Vue Range Slider using the `tooltipsFormat` function. * This allows you to format the tooltip values according to your specific requirements, enhancing the clarity and presentation of information. */ tooltipsFormat: { type: Function, default: null, }, /** * Controls the visual representation of the slider's track. When set to `'fill'`, the track is dynamically filled based on the slider's value(s). Setting it to `false` disables the filled track. */ track: { type: [Boolean, String], default: 'fill', validator: (value) => { return typeof value === 'boolean' || value === 'fill'; }, }, /** * Set the current value(s) of the Vue Range Slider using the `value` prop. * Whether you're using a single value or an array for multi-handle sliders, this prop controls the slider's position and ensures it reflects the desired state. */ value: { type: [Number, Array], default: () => [0], }, /** * Orient the Vue Range Slider vertically by setting the `vertical` prop to `true`. * This changes the slider's layout from horizontal to vertical, providing a different aesthetic and fitting various UI designs. */ vertical: { type: Boolean, default: false, }, }, emits: [ 'change', /** * Emit the new value whenever there’s a change event. */ 'update:modelValue', ], setup(props, { emit }) { const rangeSliderRef = vue.ref(null); const inputsRef = vue.ref([]); const labelsContainerRef = vue.ref(null); const labelsRef = vue.ref([]); const trackRef = vue.ref(null); const currentValue = vue.ref(props.modelValue ? Array.isArray(props.modelValue) ? props.modelValue : [props.modelValue] : Array.isArray(props.value) ? props.value : [props.value]); const isDragging = vue.ref(false); const _isRTL = vue.ref(false); const dragIndex = vue.ref(0); const thumbSize = vue.ref(); vue.watch(() => props.value, (newVal) => { currentValue.value = Array.isArray(newVal) ? newVal : [newVal]; }); vue.watch(() => props.modelValue, (newVal) => { if (newVal !== undefined) { currentValue.value = Array.isArray(newVal) ? newVal : [newVal]; } }); // Adjust labels container size based on labels vue.onMounted(() => { const maxSize = Math.max(...labelsRef.value.map((label) => props.vertical ? label.offsetWidth : label.offsetHeight)); if (labelsContainerRef.value) { labelsContainerRef.value.style[props.vertical ? 'width' : 'height'] = `${maxSize}px`; } if (rangeSliderRef.value) { _isRTL.value = isRTL.default(rangeSliderRef.value); thumbSize.value = utils.getThumbSize(rangeSliderRef.value, props.vertical); } }); vue.watch(isDragging, (newVal) => { if (newVal) { globalThis.addEventListener('mousemove', handleMouseMove); globalThis.addEventListener('mouseup', handleMouseUp); } else { globalThis.removeEventListener('mousemove', handleMouseMove); globalThis.removeEventListener('mouseup', handleMouseUp); } }); const updateNearestValue = (value) => { const nearestIndex = utils.getNearestValueIndex(value, currentValue.value); const newCurrentValue = [...currentValue.value]; newCurrentValue[nearestIndex] = utils.validateValue(value, currentValue.value, props.distance, nearestIndex); setTimeout(() => { if (inputsRef.value[nearestIndex]) { inputsRef.value[nearestIndex].focus(); } }, 0); currentValue.value = newCurrentValue; emit('change', newCurrentValue); emit('update:modelValue', newCurrentValue); }; const handleInputChange = (event, index) => { if (props.disabled) return; const target = event.target; const value = Number(target.value); const newCurrentValue = utils.updateValue(value, currentValue.value, props.distance, index); currentValue.value = newCurrentValue; emit('change', newCurrentValue); emit('update:modelValue', newCurrentValue); }; const handleInputsContainerMouseDown = (event) => { if (!trackRef.value || event.button !== 0 || props.disabled) return; const target = event.target; if (!(target instanceof HTMLInputElement) && target !== trackRef.value) { return; } const clickValue = utils.calculateClickValue(event, trackRef.value, props.min, props.max, props.step, props.vertical, _isRTL.value); const index = utils.getNearestValueIndex(clickValue, currentValue.value); isDragging.value = true; dragIndex.value = index; updateNearestValue(clickValue); }; const handleLabelClick = (event, value) => { if (!props.clickableLabels || props.disabled || event.button !== 0) return; updateNearestValue(value); }; const handleMouseMove = (event) => { if (!isDragging.value || !trackRef.value || props.disabled) return; const moveValue = utils.calculateMoveValue(event, trackRef.value, props.min, props.max, props.step, props.vertical, _isRTL.value); const newCurrentValue = utils.updateValue(moveValue, currentValue.value, props.distance, dragIndex.value); currentValue.value = newCurrentValue; emit('change', newCurrentValue); emit('update:modelValue', newCurrentValue); }; const handleMouseUp = () => { isDragging.value = false; }; return () => vue.h('div', { class: [ 'range-slider', { 'range-slider-vertical': props.vertical, disabled: props.disabled, }, ], ref: rangeSliderRef, }, [ vue.h('div', { class: 'range-slider-inputs-container', onMousedown: handleInputsContainerMouseDown, }, [ currentValue.value.map((value, index) => [ vue.h('input', { class: 'range-slider-input', type: 'range', min: props.min, max: props.max, step: props.step, value: value, name: Array.isArray(props.name) ? props.name[index] : `${props.name || ''}-${index}`, role: 'slider', 'aria-valuemin': props.min, 'aria-valuemax': props.max, 'aria-valuenow': value, 'aria-orientation': props.vertical ? 'vertical' : 'horizontal', disabled: props.disabled, onInput: (e) => handleInputChange(e, index), ref: (el) => { inputsRef.value[index] = el; }, }), props.tooltips && vue.h('div', { class: 'range-slider-tooltip', ...(thumbSize.value && { style: utils.calculateTooltipPosition(props.min, props.max, value, thumbSize.value, props.vertical, _isRTL.value), }), }, [ vue.h('div', { class: 'range-slider-tooltip-inner' }, [ props.tooltipsFormat ? props.tooltipsFormat(value) : value, ]), vue.h('div', { class: 'range-slider-tooltip-arrow' }), ]), ]), vue.h('div', { class: 'range-slider-track', ...(props.track && { style: utils.updateGradient(props.min, props.max, currentValue.value, props.vertical, _isRTL.value), }), ref: trackRef, }), ]), Array.isArray(props.labels) && props.labels.length > 0 && vue.h('div', { class: 'range-slider-labels-container', ref: labelsContainerRef, }, props.labels.map((label, index) => { const labelPosition = utils.calculateLabelPosition(props.min, props.max, props.labels, label, index); const labelValue = utils.getLabelValue(props.min, props.max, props.labels, label, index); const labelStyle = { ...(props.vertical ? { bottom: labelPosition } : _isRTL.value ? { right: labelPosition } : { left: labelPosition }), ...(typeof label === 'object' && 'style' in label ? label.style : {}), }; return vue.h('div', { class: [ 'range-slider-label', { clickable: props.clickableLabels, }, typeof label === 'object' && 'className' in label ? label.className : '', ], style: labelStyle, onMousedown: (event) => handleLabelClick(event, labelValue), key: index, ref: (el) => { labelsRef.value[index] = el; }, }, typeof label === 'object' && 'label' in label ? label.label : label); })), ]); }, }); exports.CRangeSlider = CRangeSlider; //# sourceMappingURL=CRangeSlider.js.map