strat-gameplay-webapp/frontend-sba/components/UI/ToggleSwitch.vue
Cal Corum 2381456189 test: Skip unstable test suites
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-22 20:18:33 -06:00

112 lines
2.7 KiB
Vue

<template>
<div class="flex items-center gap-3">
<!-- Toggle Switch -->
<button
type="button"
:disabled="disabled"
:class="switchClasses"
role="switch"
:aria-checked="modelValue"
@click="handleToggle"
>
<!-- Track -->
<span
aria-hidden="true"
:class="trackClasses"
/>
<!-- Thumb -->
<span
aria-hidden="true"
:class="thumbClasses"
/>
</button>
<!-- Label (optional) -->
<label
v-if="label"
:class="labelClasses"
@click="handleToggle"
>
{{ label }}
</label>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
interface Props {
modelValue: boolean
label?: string
disabled?: boolean
size?: 'sm' | 'md' | 'lg'
}
const props = withDefaults(defineProps<Props>(), {
disabled: false,
size: 'md',
})
const emit = defineEmits<{
'update:modelValue': [value: boolean]
}>()
const switchClasses = computed(() => {
const base = 'relative inline-flex items-center flex-shrink-0 cursor-pointer rounded-full transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed'
// Size classes
const sizeClasses = {
sm: 'h-5 w-9',
md: 'h-6 w-11',
lg: 'h-7 w-14',
}
return `${base} ${sizeClasses[props.size]}`
})
const trackClasses = computed(() => {
const base = 'pointer-events-none absolute h-full w-full rounded-full transition-colors duration-200'
const color = props.modelValue
? 'bg-gradient-to-r from-green-500 to-green-600'
: 'bg-gray-300 dark:bg-gray-600'
return `${base} ${color}`
})
const thumbClasses = computed(() => {
const base = 'pointer-events-none absolute bg-white rounded-full shadow-lg transform transition-transform duration-200 ease-in-out'
// Size-specific dimensions and positions
const sizeClasses = {
sm: 'h-4 w-4',
md: 'h-5 w-5',
lg: 'h-6 w-6',
}
// Position based on state
const translateClasses = {
sm: props.modelValue ? 'translate-x-4' : 'translate-x-0.5',
md: props.modelValue ? 'translate-x-5' : 'translate-x-0.5',
lg: props.modelValue ? 'translate-x-7' : 'translate-x-0.5',
}
return `${base} ${sizeClasses[props.size]} ${translateClasses[props.size]}`
})
const labelClasses = computed(() => {
const base = 'text-sm font-medium cursor-pointer select-none'
const color = props.disabled
? 'text-gray-400 cursor-not-allowed'
: 'text-gray-700 dark:text-gray-200'
return `${base} ${color}`
})
const handleToggle = () => {
if (!props.disabled) {
emit('update:modelValue', !props.modelValue)
}
}
</script>