SlidingSwitch.vue
2.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
<script lang="ts">
  export default {
    inheritAttrs: false,
  };
</script>
<script lang="ts" setup>
  import { RadioRecord } from '../../detail/config/util';
  interface VisualComponentProps<Layout = Recordable, Value = Recordable> {
    value?: Value;
    layout?: Layout;
    radio?: RadioRecord;
    random?: boolean;
    add?: (key: string, method: Fn) => void;
    update?: () => void;
    remove?: (key: string) => void;
  }
  const props = defineProps<VisualComponentProps>();
  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>