<template>
<view class="uni-switch" :class="switchClasses" @click.stop="handleTap">
<view class="uni-switch-thumb" :class="thumbClasses"></view>
</view>
</template>
<script lang="uts" setup>
import { FORM_KEY, LABEL_KEY, buildPointerEvent } from '../common.uts'
import { UniSwitchElement } from './global.uts'
import { FormContext, LabelContext } from '../types.uts'
interface SwitchProps {
/**
* 是否禁用
* @uniPlatform {
"app": {
"harmony": {
"unixvVer": "5.0"
}
}
}
*/
disabled?: boolean;
/**
* 当前是否选中,可用来设置默认选中
* @uniPlatform {
"app": {
"harmony": {
"unixvVer": "5.0"
}
}
}
*/
checked?: boolean;
/**
* 表单的控件名称,作为键值对的一部分与表单(form组件)一同提交
* @uniPlatform {
"app": {
"harmony": {
"unixvVer": "5.0"
}
}
}
*/
name?: string;
/**
* 开关选择器滑块的类名
* @uniPlatform {
"app": {
"harmony": {
"unixvVer": "5.0"
}
}
}
*/
thumbClass?: string.ClassString;
/**
* 开关选择器滑块选中的类名
* @uniPlatform {
"app": {
"harmony": {
"unixvVer": "5.0"
}
}
}
*/
thumbActiveClass?: string.ClassString;
/**
* 开关选择器选中的类名
* @uniPlatform {
"app": {
"harmony": {
"unixvVer": "5.0"
}
}
}
*/
switchActiveClass?: string.ClassString;
}
const props = withDefaults(defineProps<SwitchProps>(), {
disabled: false,
checked: false,
name: '',
thumbClass: '',
thumbActiveClass: '',
switchActiveClass: ''
})
defineOptions({
name: 'switch',
// @ts-ignore
rootElement: {
class: UniSwitchElement
},
externalClasses: ['thumb-class', 'thumb-active-class', 'switch-active-class']
})
const attrs = useAttrs()
const id = attrs.id
const proxy = getCurrentInstance()?.proxy
type UniSwitchChangeEventDetail = {
value : boolean
}
// #ifdef WEB
class UniCustomEvent<T> {
detail : T
constructor(type : string, detail : T) {
// this.type = type
this.detail = detail
}
}
// #endif
class UniSwitchChangeEvent extends UniCustomEvent<UniSwitchChangeEventDetail> {
constructor(value : boolean) {
super('change', { value } as UniSwitchChangeEventDetail)
}
}
const emit = defineEmits<{
change : [event: UniSwitchChangeEvent]
}>()
const formCtx = inject<FormContext | null>(FORM_KEY, null)
const labelCtx = inject<LabelContext | null>(LABEL_KEY, null)
const isChecked = ref(props.checked)
const initialChecked = ref(false)
const enableTransition = ref(false)
const thumbClasses = computed<string[]>(() => {
const classes: string[] = []
if (enableTransition.value) {
classes.push('thumb-transition-enabled')
}
if (isChecked.value) {
classes.push('uni-switch-active-thumb', props.thumbActiveClass)
} else {
classes.push(props.thumbClass)
}
return classes
})
const switchClasses = computed<string[]>(() => {
const classes: string[] = []
if (enableTransition.value) {
classes.push('transition-enabled')
}
if (props.disabled) {
classes.push('uni-switch-disabled')
}
if (isChecked.value) {
classes.push('uni-switch-active', props.switchActiveClass)
}
return classes
})
watch(() => props.checked, (value : boolean) => {
isChecked.value = value
})
onMounted(() => {
initialChecked.value = props.checked
// register with form if available
if (formCtx && props.name) {
formCtx.registerField({
name: props.name,
getValue: () => isChecked.value,
reset: () => { isChecked.value = initialChecked.value }
})
}
// register with label if available
if (labelCtx != null) {
labelCtx.register({
id: id || props.name,
activate: (event: UniPointerEvent) => {
proxy?.$el?.dispatchEvent('click', buildPointerEvent(event))
},
getDisabled: () => props.disabled
})
}
enableTransition.value = true
})
onUnmounted(() => {
if (formCtx && props.name) {
formCtx.unregisterField(props.name)
}
if (labelCtx != null) {
labelCtx.unregister(id || props.name)
}
})
const handleTap = () => {
if (props.disabled) {
return
}
isChecked.value = !isChecked.value
emit('change', new UniSwitchChangeEvent(isChecked.value))
}
</script>
<style>
.uni-switch {
width: 52px;
height: 32px;
border: 2px solid #e9e9ea;
border-radius: 16px;
background-color: #e9e9ea;
}
.uni-switch.transition-enabled {
transition-duration: 0.1s;
transition-property: background-color;
}
.uni-switch-disabled {
opacity: 0.7;
}
.uni-switch-active {
background-color: #007aff;
border-color: #007aff;
}
.uni-switch-thumb {
width: 28px;
height: 28px;
border-radius: 14px;
transform: translate(0);
background-color: #ffffff;
}
.uni-switch-thumb.thumb-transition-enabled {
transition-duration: 0.15s;
transition-property: transform;
}
.uni-switch-active-thumb {
transform: translateX(20px);
}
</style>