ant-design-vue/components/switch/index.tsx
2021-04-11 15:12:26 +08:00

155 lines
4.4 KiB
Vue

import {
defineComponent,
inject,
onBeforeMount,
ref,
ExtractPropTypes,
computed,
onMounted,
nextTick,
} from 'vue';
import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined';
import PropTypes from '../_util/vue-types';
import KeyCode from '../_util/KeyCode';
import Wave from '../_util/wave';
import { defaultConfigProvider } from '../config-provider';
import warning from '../_util/warning';
import { tuple, withInstall } from '../_util/type';
import { getPropsSlot } from '../_util/props-util';
import Omit from 'omit.js';
export const SwitchSizes = tuple('small', 'default', 'large');
const switchProps = {
prefixCls: PropTypes.string,
size: PropTypes.oneOf(SwitchSizes),
disabled: PropTypes.looseBool,
checkedChildren: PropTypes.any,
unCheckedChildren: PropTypes.any,
tabindex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
defaultChecked: PropTypes.looseBool,
autofocus: PropTypes.looseBool,
loading: PropTypes.looseBool,
checked: PropTypes.looseBool,
};
export type SwitchProps = Partial<ExtractPropTypes<typeof switchProps>>;
const Switch = defineComponent({
name: 'ASwitch',
__ANT_SWITCH: true,
inheritAttrs: false,
props: switchProps,
emits: ['update:checked', 'mouseup', 'change', 'click', 'keydown'],
setup(props: SwitchProps, { attrs, slots, expose, emit }) {
onBeforeMount(() => {
warning(
!('defaultChecked' in attrs),
'Switch',
`'defaultChecked' is deprecated, please use 'v-model:checked'`,
);
warning(
!('value' in attrs),
'Switch',
'`value` is not validate prop, do you mean `checked`?',
);
});
const configProvider = inject('configProvider', defaultConfigProvider);
const { getPrefixCls } = configProvider;
const refSwitchNode = ref();
const focus = () => {
refSwitchNode.value?.focus();
};
const blur = () => {
refSwitchNode.value?.blur();
};
expose({ focus, blur });
const prefixCls = computed(() => {
return getPrefixCls('switch', props.prefixCls);
});
const checked = computed(() => {
return 'checked' in props ? !!props.checked : !!props.defaultChecked;
});
onMounted(() => {
nextTick(() => {
if (props.autofocus && !props.disabled) {
refSwitchNode.value.focus();
}
});
});
const setChecked = (check: boolean, e: MouseEvent | KeyboardEvent) => {
if (props.disabled) {
return;
}
emit('update:checked', check);
emit('change', check, e);
};
const handleClick = (e: MouseEvent) => {
focus();
const newChecked = !checked.value;
setChecked(newChecked, e);
emit('click', newChecked, e);
};
const handleKeyDown = (e: KeyboardEvent) => {
if (e.keyCode === KeyCode.LEFT) {
setChecked(false, e);
} else if (e.keyCode === KeyCode.RIGHT) {
setChecked(true, e);
}
emit('keydown', e);
};
const handleMouseUp = (e: MouseEvent) => {
refSwitchNode.value?.blur();
emit('mouseup', e);
};
return () => (
<Wave insertExtraNode>
<button
{...Omit(props, [
'prefixCls',
'checkedChildren',
'unCheckedChildren',
'checked',
'autofocus',
'defaultChecked',
])}
{...attrs}
onKeydown={handleKeyDown}
onClick={handleClick}
onMouseup={handleMouseUp}
type="button"
role="switch"
aria-checked={checked.value}
disabled={props.disabled || props.loading}
class={{
[attrs.class as string]: attrs.class,
[prefixCls.value]: true,
[`${prefixCls.value}-small`]: props.size === 'small',
[`${prefixCls.value}-loading`]: props.loading,
[`${prefixCls.value}-checked`]: checked.value,
[`${prefixCls.value}-disabled`]: props.disabled,
}}
ref={refSwitchNode}
>
{props.loading ? <LoadingOutlined class={`${prefixCls.value}-loading-icon`} /> : null}
<span class={`${prefixCls.value}-inner`}>
{checked.value
? getPropsSlot(slots, props, 'checkedChildren')
: getPropsSlot(slots, props, 'unCheckedChildren')}
</span>
</button>
</Wave>
);
},
});
export default withInstall(Switch);