braindrops.nicwands.com/shared/Toggle.vue
2026-08-11 10:24:27 -04:00

88 lines
1.9 KiB
Vue

<template>
<label class="shared-toggle" :class="{ 'is-on': model, 'is-disabled': disabled }">
<input
type="checkbox"
:disabled="disabled"
v-model="model"
/>
<span class="track">
<span class="thumb" />
</span>
<span v-if="$slots.default" class="toggle-label"><slot /></span>
</label>
</template>
<script setup>
const props = defineProps({
disabled: {
type: Boolean,
default: false,
},
})
const model = defineModel()
</script>
<style lang="scss">
.shared-toggle {
--t-duration: 250ms;
--t-ease: var(--ease-out-quad);
--track-w: #{rs(36px)};
--track-h: #{rs(20px)};
--thumb-size: #{rs(14px)};
display: inline-flex;
align-items: center;
gap: rs(10px);
cursor: pointer;
user-select: none;
input[type='checkbox'] {
position: absolute;
opacity: 0;
width: 0;
height: 0;
pointer-events: none;
}
.track {
flex-shrink: 0;
width: var(--track-w);
height: var(--track-h);
border-radius: var(--track-h);
background: var(--grey-400);
position: relative;
transition: background var(--t-duration) var(--t-ease);
}
.thumb {
position: absolute;
top: 50%;
left: rs(3px);
transform: translateY(-50%);
width: var(--thumb-size);
height: var(--thumb-size);
border-radius: 50%;
background: var(--ivory);
transition: transform var(--t-duration) var(--t-ease), left var(--t-duration) var(--t-ease);
}
.toggle-label {
@include p;
}
&.is-on {
.track {
background: var(--theme-contrast);
}
.thumb {
left: calc(var(--track-w) - var(--thumb-size) - rs(3px));
}
}
&.is-disabled {
opacity: 0.3;
pointer-events: none;
}
}
</style>