128 lines
2.6 KiB
Vue
128 lines
2.6 KiB
Vue
<template>
|
|
<div
|
|
:class="[
|
|
cn.b(),
|
|
cn.is('loading', loading),
|
|
cn.is('checked', checked),
|
|
cn.is('focused', focused),
|
|
cn.is('disabled', disabled),
|
|
]"
|
|
>
|
|
<div
|
|
:class="cn.e('control')"
|
|
@click="switchValue"
|
|
>
|
|
<input
|
|
type="checkbox"
|
|
:class="cn.e('input')"
|
|
:disabled="disabled"
|
|
:checked="checked"
|
|
:value="trueValue"
|
|
@focus="focused = true"
|
|
@blur="focused = false"
|
|
@change="handleChange"
|
|
>
|
|
<div :class="cn.e('action')">
|
|
<UiSpinner
|
|
v-if="loading && !disabled"
|
|
:class="cn.e('spinner')"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, ref, toRef } from 'vue'
|
|
import { isPromise } from '@vue/shared'
|
|
import { useField } from 'vee-validate'
|
|
import useClassname from '../../composables/use-classname'
|
|
|
|
import UiSpinner from '../spinner/index.vue'
|
|
|
|
export interface Props {
|
|
id: string
|
|
disabled?: boolean
|
|
modelValue?: boolean | string | number
|
|
trueValue?: boolean | string | number
|
|
falseValue?: boolean | string | number
|
|
dummy?: boolean
|
|
loading?: boolean
|
|
beforeChange?: () => Promise<boolean> | boolean
|
|
}
|
|
|
|
defineOptions({
|
|
name: 'UiSwitch',
|
|
})
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
disabled: false,
|
|
trueValue: true,
|
|
falseValue: false,
|
|
dummy: false,
|
|
modelValue: undefined,
|
|
loading: false,
|
|
})
|
|
|
|
const emit = defineEmits<{
|
|
'update:modelValue': [state: boolean | string | number]
|
|
change: [state: boolean | string | number]
|
|
focus: []
|
|
blur: []
|
|
}>()
|
|
|
|
const cn = useClassname('ui-switch')
|
|
|
|
const { checked, handleChange: fieldHandleChange } = useField(
|
|
toRef(props, 'id'),
|
|
null,
|
|
{
|
|
type: 'checkbox',
|
|
initialValue: props.modelValue ?? props.falseValue,
|
|
checkedValue: props.trueValue,
|
|
uncheckedValue: props.falseValue,
|
|
syncVModel: true,
|
|
},
|
|
)
|
|
|
|
const focused = ref(false)
|
|
|
|
const handleChange = computed<(e: Event) => void>(() => {
|
|
return props.dummy ? handleChangeDummy : fieldHandleChange
|
|
})
|
|
|
|
function handleChangeDummy() {
|
|
emit('change', checked.value ? props.falseValue : props.trueValue)
|
|
}
|
|
|
|
async function switchValue(e: Event) {
|
|
if (props.disabled || props.loading)
|
|
return
|
|
|
|
const { beforeChange } = props
|
|
|
|
if (!beforeChange) {
|
|
handleChange.value(e)
|
|
return
|
|
}
|
|
|
|
const shouldChange = beforeChange()
|
|
|
|
if (isPromise(shouldChange)) {
|
|
try {
|
|
const result = await shouldChange
|
|
|
|
result && handleChange.value(e)
|
|
}
|
|
catch {}
|
|
}
|
|
else if (shouldChange) {
|
|
handleChange.value(e)
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
@use 'styles';
|
|
</style>
|