@econnect/webcomponents-library
Documentation
Styleguide
Documentation
Styleguide
  • Getting Started

    • Installation
    • Patch Notes
    • Styleguide
  • Styling

    • e-connect colors
  • UI Components

    • Form

      • Search Field
      • Form Field
      • Forms
    • Filter

      • Filter
      • Search Filter
    • Grid

      • Grid
      • Grid Row
    • Inputs

      • File Input
      • Input Checkbox
      • Input Dropdown
      • Input Date
      • Input Date Time
      • Input Date Time Local
      • Input File
      • Input Label
      • Input Multi Select
      • Input Number
      • Input Radio
      • Input Range
      • Input Select
      • Input Switch
      • Input Slider
      • Input Text
      • Input TextArea
      • Input Time
      • Input Time Range
    • Controls

      • Booleans
      • Button
      • Checkboxes
      • Date Displays
      • Dropdown Lists
      • Text Displays
    • Headers

      • e-header
    • Cards

      • e-card
    • Images

      • Icon
      • Images
      • Flag Image
      • Hamburger
    • Iframe
    • Pager
    • Settings
    • Document Validation
    • Navigation

      • Navigation
      • Navigation Menu
      • Navigation Footer
      • Navigation Button
      • Navigation Button Item
      • Navigation Search Item

Input Range v1.0

The <e-input-range> component is a component that can be used for .

Usage

An e-input-range has attributes that must be provided,

<e-input-range
  id=""
  value=""
  required=""
/>

Attributes

id | Type: String | required
label | Type: String | Default: ''
options | Type: Array | Default: selectOptions

Examples

Below a few interactive examples of unavailable can be found.

Default
<e-input-range
  id="rangeInputWithOptions"
  :value="'40-60'"
/>
40-60
Labelled, label='textInput with Label'
<div class="input-example-field">
  <e-input-label
    :id="'rangeInputWithLabel'"
    :value="'rangeInput with Label'"
  />
  <e-input-range
    :id="'rangeInputWithLabel'"
    :value="'40-60'"
  />
</div>
40-60

Source Code

e-input-range.vue
<script lang="ts" setup>
import { computed, ref, watch } from 'vue';
import { isEmpty } from 'lodash-es';

import { EInput_Range } from 'types/e-input/interfaces/EInput_Range';

export type Props = EInput_Range;
const props = withDefaults(defineProps<Props>(), {
  label: '',
  id: '',
  required: false,
  meta: () => {
    return {
      disabled: false,
      max: 100,
      min: 0,
      steps: 1
    };
  }
});

const emit = defineEmits<{
  (e: 'update:value', value: string): void;
}>();

const focus = ref('');

const localValue = ref(props.value || '10-90');

const minRangeValue = computed(() => {
  return Number.parseInt(localValue.value.split('-')[0] || '0');
});
const maxRangeValue = computed(() => {
  return Number.parseInt(localValue.value.split('-')[1] || '0');
});

const isDisabled = computed(() => props.disabled ?? props.meta?.disabled);

const stateClasses = computed(() => {
  return {
    active: !isEmpty(focus.value),
    'focus-min': focus.value === 'min',
    'focus-max': focus.value === 'max',
    disabled: isDisabled.value
  };
});

const rangeMin = computed(() => {
  return props.meta.min || 0;
});

const rangeMax = computed(() => {
  return props.meta.max || 100;
});

const rangeSteps = computed(() => {
  return props.meta.steps || 1;
});

function setMinValue(event: InputEvent) {
  const target = event.target as HTMLInputElement;
  const newValue = Math.min(target.valueAsNumber, maxRangeValue.value);

  localValue.value = `${newValue}-${maxRangeValue.value}`;
  emitUpdate(localValue.value);
}

function setMaxValue(event: InputEvent) {
  const target = event.target as HTMLInputElement;
  const newValue = Math.max(target.valueAsNumber, minRangeValue.value);

  localValue.value = `${minRangeValue.value}-${newValue}`;
  emitUpdate(localValue.value);
}

const emitUpdate = (newValue: string) => {
  localValue.value = newValue;

  emit('update:value', newValue);
};

watch(
  () => props.value,
  newValue => {
    localValue.value = newValue;
  }
);
</script>

<template>
  <div
    class="e-input-range"
    :class="stateClasses"
  >
    <span
      class="current-value-label"
      :title="localValue"
      v-text="localValue"
    />

    <div class="input-container">
      <label
        :title="rangeMin.toString()"
        v-text="rangeMin.toString()"
      />

      <div class="range-container">
        <input
          :id="id"
          class="slider"
          type="range"
          :name="id"
          :disabled="isDisabled"
          :required="required"
          :min="rangeMin"
          :value="minRangeValue"
          :max="rangeMax"
          :step="rangeSteps"
          @blur="focus = ''"
          @focus="focus = 'min'"
          @mousedown.left="focus = 'min'"
          @mouseup.left="focus = ''"
          @input.stop="setMinValue"
        />
        <input
          :id="`${id}-2`"
          class="slider"
          type="range"
          :name="id"
          :disabled="isDisabled"
          :required="required"
          :min="rangeMin"
          :value="maxRangeValue"
          :max="rangeMax"
          :step="rangeSteps"
          @blur="focus = ''"
          @focus="focus = 'max'"
          @mousedown.left="focus = 'max'"
          @mouseup.left="focus = ''"
          @input.stop="setMaxValue"
        />

        <div
          class="slider-display"
          :class="{
            disabled: isDisabled
          }"
        >
          <div class="track" />
          <div
            class="range"
            :style="{
              left: `${(minRangeValue / (rangeMax - rangeMin)) * 100}%`,
              right: `${100 - (maxRangeValue / (rangeMax - rangeMin)) * 100}%`
            }"
          />
          <div
            class="thumb"
            :class="{
              active: focus === 'min'
            }"
            :style="{
              left: `${(minRangeValue / (rangeMax - rangeMin)) * 100}%`
            }"
          />
          <div
            class="thumb"
            :class="{
              active: focus === 'max'
            }"
            :style="{
              left: `${(maxRangeValue / (rangeMax - rangeMin)) * 100}%`
            }"
          />
        </div>
      </div>

      <label
        :title="rangeMax.toString()"
        v-text="rangeMax.toString()"
      />
    </div>
  </div>
</template>

<style scoped lang="scss">
$option-height: 24px;

.e-input-range {
  align-items: center;
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  border: none;
  position: relative;
  margin: 10px 0;
  gap: 10px;
  padding-left: 10px;

  $thumb-size: 18px;
  $bar-height: 10px;

  .current-value-label {
    min-width: 60px;
  }

  .input-container {
    align-items: center;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    gap: 20px;
    flex: 1 1 auto;

    label {
      min-width: 30px;
    }

    .range-container {
      position: relative;
      align-items: center;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      border: none;
      flex: 1 1 auto;
      gap: 10px;

      .slider {
        height: $bar-height;
        margin: 0;
        opacity: 0;
        padding: 0;
        pointer-events: none;
        position: absolute;
        width: 100%;
        z-index: 2;

        &::-moz-range-thumb {
          border-radius: 50%;
          cursor: pointer;
          height: $thumb-size;
          pointer-events: all;
          width: $thumb-size;
        }

        &::-webkit-slider-thumb {
          border-radius: 50%;
          cursor: pointer;
          height: $thumb-size;
          pointer-events: all;
          width: $thumb-size;
        }

        &:not(:disabled) {
          &:active {
            background: var(--e-input-focus-background);

            &::-moz-range-thumb {
              cursor: grabbing;
              background: var(--e-secondary);
            }
            &::-webkit-slider-thumb {
              cursor: grabbing;
              background: var(--e-secondary);
            }
          }
        }

        &:disabled {
          cursor: not-allowed;

          &::-moz-range-thumb {
            cursor: not-allowed;
            background: var(--e-disabled);
          }

          &::-webkit-slider-thumb {
            cursor: not-allowed;
            background: var(--e-disabled);
          }
        }
      }

      .slider-display {
        background: var(--e-input-background);
        border-radius: 5px;
        height: $bar-height;
        position: relative;
        user-input: none;
        user-select: none;
        width: 100%;
        z-index: 1;
        -webkit-appearance: none;

        .track,
        .range,
        .thumb {
          position: absolute;
          user-select: none;
        }

        .track,
        .range {
          top: 0;
          bottom: 0;
        }

        .track {
          background-color: var(--e-input-background);
          border-radius: 5px;
          left: 0;
          right: 0;
          z-index: 1;
        }
        .range {
          background-image: linear-gradient(to right, var(--e-accent), var(--e-accent));
          z-index: 2;
        }

        .thumb {
          background-color: var(--e-accent);
          border-radius: calc($thumb-size / 2);
          height: $thumb-size;
          width: $thumb-size;
          z-index: 3;
          top: calc($bar-height / 2);
          transform: translate(calc($thumb-size / 4 * -2), calc($thumb-size / 2 * -1));

          &.active {
            background-color: var(--e-secondary);
            outline: 1px solid var(--e-foreground);
          }
        }

        &.disabled {
          .range {
            background-color: var(--e-disabled);
            background-image: linear-gradient(
              var(--e-disabled),
              var(--e-disabled),
              var(--e-disabled)
            );
          }

          .thumb {
            background-color: var(--e-disabled);
            cursor: not-allowed;
          }
        }
      }
    }
  }

  &:not(.disabled) {
    .input-container {
      .range-container {
        .slider {
          &:active {
            background: var(--e-input-focus-background);
          }
        }
      }
    }

    &.active {
      .slider-display {
        .track {
          background-color: var(--e-input-focus-background);
        }
      }
    }

    &.focus-min {
      .slider-display {
        .range {
          background-image: linear-gradient(
            to right,
            var(--e-secondary),
            var(--e-secondary),
            var(--e-accent)
          );
        }
      }
    }

    &.focus-max {
      .slider-display {
        .range {
          background-image: linear-gradient(
            to right,
            var(--e-accent),
            var(--e-secondary),
            var(--e-secondary)
          );
        }
      }
    }
  }

  &.disabled {
    .input-container {
      label {
        cursor: not-allowed;
        user-input: none;
      }
    }
  }
}
</style>
Last Updated:: 10/10/24, 2:56 PM
Contributors: AzureAD\MarcelLommers, Marcel Lommers
Prev
Input Radio
Next
Input Select