SlidingSwitch.vue 2.43 KB
<script lang="ts" setup>
  import { ControlComponentValue } from './control.config';

  const props = defineProps<{
    value?: ControlComponentValue;
  }>();

  const emit = defineEmits(['update:value', 'change']);

  const handleChange = (event: Event) => {
    const _value = (event.target as HTMLInputElement).checked;
    emit('update:value', _value);
    emit('change', _value);
  };
</script>

<template>
  <label class="sliding-switch">
    <input
      :value="props.value?.value"
      type="checkbox"
      :checked="props.value?.value"
      @change="handleChange"
    />
    <span class="slider"></span>
    <span class="on">ON</span>
    <span class="off">OFF</span>
  </label>
</template>

<style scoped lang="less">
  .sliding-switch {
    position: relative;
    display: block;
    font-weight: 700;
    line-height: 40px;
    width: 80px;
    height: 40px;
    font-size: 14px;
    cursor: pointer;
    user-select: none;

    input[type='checkbox'] {
      display: none;
    }

    .slider {
      width: 80px;
      height: 40px;
      display: flex;
      align-items: center;
      box-sizing: border-box;
      border: 2px solid #ecf0f3;
      border-radius: 20px;
      box-shadow: -2px -2px 8px #fff, -2px -2px 12px hsl(0deg 0% 100% / 50%),
        inset -2px -2px 8px #fff, inset -2px -2px 12px hsl(0deg 0% 100% / 50%),
        inset 2px 2px 4px hsl(0deg 0% 100% / 10%), inset 2px 2px 8px rgb(0 0 0 / 30%),
        2px 2px 8px rgb(0 0 0 / 30%);
      background-color: #ecf0f3;
      z-index: -1;
    }

    .slider::after {
      cursor: pointer;
      display: block;
      content: '';
      width: 24px;
      height: 24px;
      border-radius: 50%;
      margin-left: 6px;
      margin-right: 6px;
      background-color: #ecf0f3;
      box-shadow: -2px -2px 8px #fff, -2px -2px 12px hsl(0deg 0% 100% / 50%),
        inset 2px 2px 4px hsl(0deg 0% 100% / 10%), 2px 2px 8px rgb(0 0 0 / 30%);
      z-index: 999;
      transition: 0.5s;
    }

    input:checked ~ .off {
      opacity: 0;
    }

    input:checked ~ .slider::after {
      transform: translateX(35px);
    }

    input:not(:checked) ~ .on {
      opacity: 0;
      transform: translateX(0);
    }

    .on,
    .off {
      position: absolute;
      top: 0;
      display: inline-block;
      margin-left: 3px;
      width: 34px;
      text-align: center;
      transition: 0.2s;
    }

    .on {
      color: #039be5;
    }

    .off {
      right: 6px;
      color: #999;
    }
  }
</style>