refactor(components): refactor switch (#4271)

This commit is contained in:
C.Y.Kun 2021-12-10 18:30:15 +08:00 committed by GitHub
parent 5e76d0f435
commit 6113e9e539
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 142 additions and 153 deletions

View File

@ -1,7 +1,7 @@
import { nextTick } from 'vue'
import { mount } from '@vue/test-utils'
import { Checked, CircleClose } from '@element-plus/icons-vue'
import Switch from '../src/index.vue'
import Switch from '../src/switch.vue'
jest.useFakeTimers()

View File

@ -1,13 +1,7 @@
import Switch from './src/index.vue'
import { withInstall } from '@element-plus/utils/with-install'
import Switch from './src/switch.vue'
import type { App } from 'vue'
import type { SFCWithInstall } from '@element-plus/utils/types'
export const ElSwitch = withInstall(Switch)
export default ElSwitch
Switch.install = (app: App): void => {
app.component(Switch.name, Switch)
}
const _Switch = Switch as SFCWithInstall<typeof Switch>
export default _Switch
export const ElSwitch = _Switch
export * from './src/switch'

View File

@ -0,0 +1,98 @@
import { buildProps, definePropType } from '@element-plus/utils/props'
import {
UPDATE_MODEL_EVENT,
CHANGE_EVENT,
INPUT_EVENT,
} from '@element-plus/utils/constants'
import { isBool, isNumber, isString } from '@element-plus/utils/util'
import type Switch from './switch.vue'
import type { Component, ExtractPropTypes } from 'vue'
export const switchProps = buildProps({
modelValue: {
type: [Boolean, String, Number],
default: false,
},
value: {
type: [Boolean, String, Number],
default: false,
},
disabled: {
type: Boolean,
default: false,
},
width: {
type: Number,
default: 40,
},
inlinePrompt: {
type: Boolean,
default: false,
},
activeIcon: {
type: definePropType<string | Component>([String, Object]),
default: '',
},
inactiveIcon: {
type: definePropType<string | Component>([String, Object]),
default: '',
},
activeText: {
type: String,
default: '',
},
inactiveText: {
type: String,
default: '',
},
activeColor: {
type: String,
default: '',
},
inactiveColor: {
type: String,
default: '',
},
borderColor: {
type: String,
default: '',
},
activeValue: {
type: [Boolean, String, Number],
default: true,
},
inactiveValue: {
type: [Boolean, String, Number],
default: false,
},
name: {
type: String,
default: '',
},
validateEvent: {
type: Boolean,
default: true,
},
id: String,
loading: {
type: Boolean,
default: false,
},
beforeChange: {
type: definePropType<() => Promise<boolean> | boolean>(Function),
},
} as const)
export type SwitchProps = ExtractPropTypes<typeof switchProps>
export const switchEmits = {
[UPDATE_MODEL_EVENT]: (val: boolean | string | number) =>
isBool(val) || isString(val) || isNumber(val),
[CHANGE_EVENT]: (val: boolean | string | number) =>
isBool(val) || isString(val) || isNumber(val),
[INPUT_EVENT]: (val: boolean | string | number) =>
isBool(val) || isString(val) || isNumber(val),
}
export type SwitchEmits = typeof switchEmits
export type SwitchInstance = InstanceType<typeof Switch>

View File

@ -94,133 +94,36 @@
</template>
<script lang="ts">
import {
defineComponent,
computed,
onMounted,
ref,
inject,
nextTick,
watch,
} from 'vue'
import { defineComponent, computed, onMounted, ref, nextTick, watch } from 'vue'
import { isPromise } from '@vue/shared'
import { elFormKey, elFormItemKey } from '@element-plus/tokens'
import { isBool } from '@element-plus/utils/util'
import { throwError, debugWarn } from '@element-plus/utils/error'
import ElIcon from '@element-plus/components/icon'
import { Loading } from '@element-plus/icons-vue'
import {
UPDATE_MODEL_EVENT,
CHANGE_EVENT,
INPUT_EVENT,
} from '@element-plus/utils/constants'
import { useDisabled, useFormItem } from '@element-plus/hooks'
import { switchProps, switchEmits } from './switch'
import type { PropType, Component } from 'vue'
import type { ElFormContext, ElFormItemContext } from '@element-plus/tokens'
type ValueType = boolean | string | number
interface ISwitchProps {
modelValue: ValueType
value: ValueType
disabled: boolean
width: number
activeIcon: string | Component
inactiveIcon: string | Component
activeText: string
inactiveText: string
activeColor: string
inactiveColor: string
borderColor: string
activeValue: ValueType
inactiveValue: ValueType
name: string
validateEvent: boolean
id: string
loading: boolean
beforeChange?: () => Promise<boolean> | boolean
}
const COMPONENT_NAME = 'ElSwitch'
export default defineComponent({
name: 'ElSwitch',
name: COMPONENT_NAME,
components: { ElIcon, Loading },
props: {
modelValue: {
type: [Boolean, String, Number],
default: false,
},
value: {
type: [Boolean, String, Number],
default: false,
},
disabled: {
type: Boolean,
default: false,
},
width: {
type: Number,
default: 40,
},
inlinePrompt: {
type: Boolean,
default: false,
},
activeIcon: {
type: [String, Object] as PropType<string | Component>,
default: '',
},
inactiveIcon: {
type: [String, Object] as PropType<string | Component>,
default: '',
},
activeText: {
type: String,
default: '',
},
inactiveText: {
type: String,
default: '',
},
activeColor: {
type: String,
default: '',
},
inactiveColor: {
type: String,
default: '',
},
borderColor: {
type: String,
default: '',
},
activeValue: {
type: [Boolean, String, Number],
default: true,
},
inactiveValue: {
type: [Boolean, String, Number],
default: false,
},
name: {
type: String,
default: '',
},
validateEvent: {
type: Boolean,
default: true,
},
id: String,
loading: {
type: Boolean,
default: false,
},
beforeChange: Function as PropType<() => Promise<boolean> | boolean>,
},
emits: ['update:modelValue', 'change', 'input'],
setup(props: ISwitchProps, ctx) {
const elForm = inject(elFormKey, {} as ElFormContext)
const elFormItem = inject(elFormItemKey, {} as ElFormItemContext)
props: switchProps,
emits: switchEmits,
setup(props, { emit }) {
const { formItem } = useFormItem()
const switchDisabled = useDisabled(computed(() => props.loading))
const isModelValue = ref(props.modelValue !== false)
const input = ref(null)
const core = ref(null)
const scope = 'ElSwitch'
const input = ref<HTMLInputElement>()
const core = ref<HTMLSpanElement>()
watch(
() => props.modelValue,
@ -236,43 +139,37 @@ export default defineComponent({
}
)
const actualValue = computed((): ValueType => {
const actualValue = computed(() => {
return isModelValue.value ? props.modelValue : props.value
})
const checked = computed((): boolean => {
return actualValue.value === props.activeValue
})
const checked = computed(() => actualValue.value === props.activeValue)
if (!~[props.activeValue, props.inactiveValue].indexOf(actualValue.value)) {
ctx.emit('update:modelValue', props.inactiveValue)
ctx.emit('change', props.inactiveValue)
ctx.emit('input', props.inactiveValue)
if (![props.activeValue, props.inactiveValue].includes(actualValue.value)) {
emit(UPDATE_MODEL_EVENT, props.inactiveValue)
emit(CHANGE_EVENT, props.inactiveValue)
emit(INPUT_EVENT, props.inactiveValue)
}
watch(checked, () => {
input.value.checked = checked.value
input.value!.checked = checked.value
if (props.activeColor || props.inactiveColor) {
setBackgroundColor()
}
if (props.validateEvent) {
elFormItem.validate?.('change')
formItem?.validate?.('change')
}
})
const switchDisabled = computed((): boolean => {
return props.disabled || props.loading || (elForm || {}).disabled
})
const handleChange = (): void => {
const val = checked.value ? props.inactiveValue : props.activeValue
ctx.emit('update:modelValue', val)
ctx.emit('change', val)
ctx.emit('input', val)
emit(UPDATE_MODEL_EVENT, val)
emit(CHANGE_EVENT, val)
emit(INPUT_EVENT, val)
nextTick(() => {
input.value.checked = checked.value
input.value!.checked = checked.value
})
}
@ -292,7 +189,7 @@ export default defineComponent({
)
if (!isExpectType) {
throwError(
scope,
COMPONENT_NAME,
'beforeChange must return type `Promise<boolean>` or `boolean`'
)
}
@ -305,7 +202,7 @@ export default defineComponent({
}
})
.catch((e) => {
debugWarn(scope, `some error occurred: ${e}`)
debugWarn(COMPONENT_NAME, `some error occurred: ${e}`)
})
} else if (shouldChange) {
handleChange()
@ -315,10 +212,10 @@ export default defineComponent({
const setBackgroundColor = (): void => {
const newColor = checked.value ? props.activeColor : props.inactiveColor
const coreEl = core.value
if (props.borderColor) coreEl.style.borderColor = props.borderColor
else if (!props.borderColor) coreEl.style.borderColor = newColor
coreEl.style.backgroundColor = newColor
coreEl.children[0].style.color = newColor
if (props.borderColor) coreEl!.style.borderColor = props.borderColor
else if (!props.borderColor) coreEl!.style.borderColor = newColor
coreEl!.style.backgroundColor = newColor
;(coreEl!.children[0] as HTMLDivElement).style.color = newColor
}
const focus = (): void => {
@ -330,7 +227,7 @@ export default defineComponent({
setBackgroundColor()
}
input.value.checked = checked.value
input.value!.checked = checked.value
})
return {