slider icon indicating copy to clipboard operation
slider copied to clipboard

Does this specific 'step' feature exist? - possible feature request

Open ShirinShirinak opened this issue 2 years ago • 2 comments

Can we do steps like: 0,100,200,1000,5000 (so the value would be changing and we would have markings on the slider, so in this case there would be 3 markings on the slider (100,200.1000).

If we can't do it right now, will it be possible to add this feature in the future please? Thank you

ShirinShirinak avatar May 16 '23 08:05 ShirinShirinak

In Vue 3 you can do it by yourself using Teleport feature

prnews-io-tech avatar Jul 06 '23 10:07 prnews-io-tech

As i see some demand on this feature i would like to share my solution to implement steps. Feel free to use it (just pass :discrete="true"):

<script setup lang="ts">
// this plugin has bugged typescript annotations. See https://github.com/vueform/slider/issues/71
// eslint-disable-next-line import/default
import Slider from '@vueform/slider'
import '@vueform/slider/themes/default.css'

interface Props {
  max?: number;
  min?: number;
  step?: number;
  discrete?: boolean;
  tooltips?: boolean;
  modelValue: number | number[];
}

const props = withDefaults(defineProps<Props>(), {
  min: 0,
  max: 100,
  step: 10,
  tooltips: true
})

const emit = defineEmits(['update:modelValue'])

const slider = ref(null)

const model = computed({
  get () {
    return props.modelValue
  },
  set (newValue) {
    emit('update:modelValue', newValue)
  }
})

const isArray = computed(() => Array.isArray(model.value) && model.value.length > 1)

const isDefault = (index: number) => {
  if (!isArray.value) {
    return (model.value as number) < index * props.step
  }
  const modelArray = model.value as number[]
  const first = modelArray[0]
  const last = modelArray[modelArray.length - 1]
  const value = (index + props.min / props.step) * props.step
  return last < value || first > value
}

const isActive = (index: number) => {
  if (!isArray.value) {
    return (model.value as number) >= index * props.step
  }
  const modelArray = model.value as number[]
  const first = modelArray[0]
  const last = modelArray[modelArray.length - 1]
  const value = (index + props.min / props.step) * props.step
  return last >= value && first <= value
}

</script>

<template>
  <Slider
    id="slider"
    ref="slider"
    v-model="model"
    class="slider"
    :tooltips="props.tooltips"
    :step="props.step"
    :min="props.min"
    :max="props.max"
    tooltip-position="bottom"
    :lazy="false"
  />
  <Teleport v-if="slider" to="div.slider-handle-lower > .slider-touch-area">
    @
  </Teleport>
  <Teleport v-if="slider && isArray" to="div.slider-handle-upper > .slider-touch-area">
    @
  </Teleport>
  <Teleport v-if="slider && discrete" to="div.slider-base">
    <div class="line absolute bottom-0 top-[-1px] grid grid-flow-col items-center w-full">
      <div class="h-1 w-1" />
      <div
        v-for="i in ((props.max - props.min) / props.step) - 1"
        :key="`point-${i}`"
        class="h-1 w-1 border !box-content bg-white-100 rounded-full"
        :class="{
          'border-accent': isActive(i),
          'border-black-5': isDefault(i)
        }"
      />
    </div>
  </Teleport>
</template>

<style scoped lang="postcss">
.slider {
  @apply my-4;
  --slider-height: theme('spacing.1');
  --slider-bg: theme('colors.black-5');
  --slider-connect-bg: theme('colors.accent');
  --slider-radius: theme('borderRadius.sm');
  --slider-handle-border: theme('borderWidth.DEFAULT') solid theme('borderColor.black-10');
  --slider-handle-shadow: theme('boxShadow.gray');
  --slider-handle-shadow-active: theme('boxShadow.gray');
  --slider-tooltip-bg: theme('colors.transparent');
  --slider-tooltip-bg-disabled: theme('colors.transparent');
  --slider-tooltip-color: theme('colors.black-100');
  --slider-tooltip-radius: theme('borderRadius.none');
  --slider-tooltip-py: theme('spacing.0');
  --slider-tooltip-px: theme('spacing.0');
  --slider-tooltip-arrow-size: theme('spacing.0');
  --slider-tooltip-distance: theme('spacing.1');
  :deep(.slider-base) {
     .slider-origin {
      .slider-handle {
        &:focus {
          box-shadow: none;
        }
        .slider-touch-area {
          @apply flex items-center justify-center text-accent;
          .nuxt-icon {
            @apply h-3 w-3;
          }
        }
        .slider-tooltip {
          @apply text-other;
        }
      }
    }
  }
}
</style>

prnews-io-tech avatar Oct 18 '23 08:10 prnews-io-tech