Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quantum-kittens
GitHub Repository: quantum-kittens/platypus
Path: blob/main/frontend/vue/components/common/SliderInput.vue
3375 views
<template>
  <div class="slider-input">
    <span class="slider-input__min">{{ internal_min }}</span>
    <input
      type="range"
      class="slider-input__range"
      :max="internal_max"
      :min="internal_min"
      :step="internal_step"
      :value="internal_value"
      :style="`--fill-percentage:${fillPercentage}`"
      @input="valueChange"
    >
    <span class="slider-input__max">{{ internal_max }}</span>
    <input
      type="number"
      class="slider-input__number"
      :max="internal_max"
      :min="internal_min"
      :step="internal_step"
      :value="internal_value"
      @input="valueChange"
    >
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue-demi'

export default defineComponent({
  name: 'SliderInput',
  components: {
  },
  props: {
    value: {
      type: Number,
      default: 0
    },
    min: {
      type: Number,
      default: 0
    },
    max: {
      type: Number,
      default: 10
    },
    step: {
      type: Number,
      default: 1
    }
  },
  data () {
    return {
      internal_value: 0,
      internal_min: 0,
      internal_max: 0,
      internal_step: 0
    }
  },
  computed: {
    range (): number {
      return this.internal_max - this.internal_min
    },
    fillPercentage (): number {
      return (this.internal_value - this.internal_min) * 100 / this.range
    }
  },
  watch: {
    value (newVal) {
      this.internal_value = newVal
    },
    min (newVal) {
      this.internal_min = newVal
    },
    max (newVal) {
      this.internal_max = newVal
    },
    step (newVal) {
      this.internal_step = newVal
    }
  },
  mounted () {
    this.internal_value = this.value
    this.internal_min = this.min
    this.internal_max = this.max
    this.internal_step = this.step
  },
  methods: {
    valueChange (evt: CustomEvent) {
      const input = evt.target as HTMLInputElement
      const newValue = Number(input.value)
      this.internal_value = Math.min(Math.max(newValue, this.min), this.max)
      this.$emit('valueChanged', this.internal_value)
    }
  }
})
</script>

<style lang="scss" scoped>
@import 'carbon-components/scss/globals/scss/typography';
@import '~/../scss/variables/colors.scss';

.slider-input {
  display: grid;
  grid-template-columns: min-content auto min-content min-content;
  align-items: center;
  height: 2.5rem;
  gap: $spacing-05;

  &__range {
    outline: none;
    -webkit-appearance: none;
    appearance: none;
    height: 2px;
    background: $background-color-light-2;
    background: linear-gradient(90deg, $background-color-dark 0%, $background-color-dark calc(var(--fill-percentage, 0) * 1%), $background-color-light-2 calc(var(--fill-percentage, 0) * 1% + 1%), $background-color-light-2 100%);

    @mixin thumbStyle {
      -webkit-appearance: none;
      appearance: none;
      outline: none;
      width: 1rem;
      height: 1rem;
      border-radius: 50%;
      border: none;
      background: $background-color-dark;
      cursor: pointer;
      transition: all 0.2s ease-in;

      &:hover {
        width: 1.5rem;
        height: 1.5rem;
      }
    }

    &::-webkit-slider-thumb {
      @include thumbStyle;
    }
    &::-moz-range-thumb {
      @include thumbStyle;
    }
    &::-ms-thumb {
      @include thumbStyle;
    }
  }

  &__min,
  &__max {
    @include type-style('code-02');
  }

  &__number {
    @include type-style('code-02');
    width: 4rem;
    height: 2.5rem;
    background-color: $background-color-white;
    border-bottom: 1px solid $border-color-secondary;
    text-align: center;
  }
}
</style>