refactor: switch #3885

This commit is contained in:
tangjinzhou 2021-04-11 15:12:26 +08:00
parent 67b9da71eb
commit 4a7fcf95f5
7 changed files with 97 additions and 267 deletions

View File

@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Switch should has click wave effect 1`] = `
<button class="ant-switch ant-switch-checked" type="button" role="switch" aria-checked="true">
<button type="button" role="switch" aria-checked="true" class="ant-switch ant-switch-checked">
<!----><span class="ant-switch-inner"><!----></span>
</button>
`;

View File

@ -1,7 +1,16 @@
import { defineComponent, inject, onBeforeMount, ref, ExtractPropTypes, computed } from '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 VcSwitch from '../vc-switch';
import KeyCode from '../_util/KeyCode';
import Wave from '../_util/wave';
import { defaultConfigProvider } from '../config-provider';
import warning from '../_util/warning';
@ -18,7 +27,7 @@ const switchProps = {
checkedChildren: PropTypes.any,
unCheckedChildren: PropTypes.any,
tabindex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
// defaultChecked: PropTypes.looseBool,
defaultChecked: PropTypes.looseBool,
autofocus: PropTypes.looseBool,
loading: PropTypes.looseBool,
checked: PropTypes.looseBool,
@ -31,10 +40,24 @@ const Switch = defineComponent({
__ANT_SWITCH: true,
inheritAttrs: false,
props: switchProps,
setup(props: SwitchProps, { attrs, slots, expose }) {
const configProvider = inject('configProvider', defaultConfigProvider);
const refSwitchNode = ref();
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();
};
@ -44,42 +67,85 @@ const Switch = defineComponent({
expose({ focus, blur });
onBeforeMount(() => {
if ('defaultChecked' in attrs) {
console.warn(
`[antdv: Switch]: 'defaultChecked' will be obsolete, please use 'v-model:checked'`,
);
}
warning(
!('value' in attrs),
'Switch',
'`value` is not validate prop, do you mean `checked`?',
);
});
const { getPrefixCls } = configProvider;
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>
<VcSwitch
{...Omit(props, ['prefixCls', 'size', 'loading', 'disabled'])}
<button
{...Omit(props, [
'prefixCls',
'checkedChildren',
'unCheckedChildren',
'checked',
'autofocus',
'defaultChecked',
])}
{...attrs}
checked={props.checked}
prefixCls={prefixCls.value}
loadingIcon={
props.loading ? <LoadingOutlined class={`${prefixCls.value}-loading-icon`} /> : null
}
checkedChildren={getPropsSlot(slots, props, 'checkedChildren')}
unCheckedChildren={getPropsSlot(slots, props, 'unCheckedChildren')}
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>
);
},

View File

@ -1,117 +0,0 @@
@switchPrefixCls: rc-switch;
@duration: 0.3s;
.@{switchPrefixCls} {
position: relative;
display: inline-block;
box-sizing: border-box;
width: 44px;
height: 22px;
line-height: 20px;
padding: 0;
vertical-align: middle;
border-radius: 20px 20px;
border: 1px solid #ccc;
background-color: #ccc;
cursor: pointer;
transition: all @duration cubic-bezier(0.35, 0, 0.25, 1);
&-inner {
color: #fff;
font-size: 12px;
position: absolute;
left: 24px;
top: 0;
}
&:after {
position: absolute;
width: 18px;
height: 18px;
left: 2px;
top: 1px;
border-radius: 50% 50%;
background-color: #fff;
content: ' ';
cursor: pointer;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.26);
transform: scale(1);
transition: left @duration cubic-bezier(0.35, 0, 0.25, 1);
animation-timing-function: cubic-bezier(0.35, 0, 0.25, 1);
animation-duration: @duration;
animation-name: rcSwitchOff;
}
&:hover:after {
transform: scale(1.1);
animation-name: rcSwitchOn;
}
&:focus {
box-shadow: 0 0 0 2px tint(#2db7f5, 80%);
outline: none;
}
&-checked {
border: 1px solid #87d068;
background-color: #87d068;
.@{switchPrefixCls}-inner {
left: 6px;
}
&:after {
left: 22px;
}
}
&-disabled {
cursor: no-drop;
background: #ccc;
border-color: #ccc;
&:after {
background: #9e9e9e;
animation-name: none;
cursor: no-drop;
}
&:hover:after {
transform: scale(1);
animation-name: none;
}
}
&-label {
display: inline-block;
line-height: 20px;
font-size: 14px;
padding-left: 10px;
vertical-align: middle;
white-space: normal;
pointer-events: none;
user-select: text;
}
}
@keyframes rcSwitchOn {
0% {
transform: scale(1);
}
50% {
transform: scale(1.25);
}
100% {
transform: scale(1.1);
}
}
@keyframes rcSwitchOff {
0% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}

View File

@ -1,4 +0,0 @@
// base rc-switch 1.9.0
import Switch from './src/Switch';
export default Switch;

View File

@ -1,16 +0,0 @@
import PropTypes from '../../_util/vue-types';
export const switchPropTypes = {
prefixCls: PropTypes.string,
disabled: PropTypes.looseBool.def(false),
checkedChildren: PropTypes.any,
unCheckedChildren: PropTypes.any,
// onChange: PropTypes.func,
// onMouseUp: PropTypes.func,
// onClick: PropTypes.func,
tabindex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
checked: PropTypes.looseBool,
defaultChecked: PropTypes.looseBool.def(false),
autofocus: PropTypes.looseBool.def(false),
loadingIcon: PropTypes.any,
};

View File

@ -1,99 +0,0 @@
import { switchPropTypes } from './PropTypes';
import Omit from 'omit.js';
import { defineComponent, nextTick, onMounted, ref } from 'vue';
import KeyCode from '../../_util/KeyCode';
import { getPropsSlot } from '../../_util/props-util';
export default defineComponent({
name: 'VcSwitch',
inheritAttrs: false,
props: {
...switchPropTypes,
prefixCls: switchPropTypes.prefixCls.def('rc-switch'),
},
emits: ['update:checked', 'mouseup', 'change', 'click'],
setup(props, { attrs, slots, emit, expose }) {
const checked = ref('checked' in props ? !!props.checked : !!props.defaultChecked);
const refSwitchNode = ref();
onMounted(() => {
nextTick(() => {
if (props.autofocus && !props.disabled) {
refSwitchNode.value.focus();
}
});
});
const setChecked = (check: boolean, e: MouseEvent | KeyboardEvent) => {
if (props.disabled) {
return;
}
checked.value = !checked.value;
emit('update:checked', checked);
emit('change', check, e);
};
const handleClick = (e: MouseEvent) => {
setChecked(checked.value, e);
emit('click', checked.value, e);
};
const handleKeyDown = (e: KeyboardEvent) => {
if (e.keyCode === KeyCode.LEFT) {
setChecked(false, e);
} else if (e.keyCode === KeyCode.RIGHT) {
setChecked(true, e);
}
};
const handleMouseUp = (e: MouseEvent) => {
refSwitchNode.value?.blur();
emit('mouseup', e);
};
const focus = () => {
refSwitchNode.value?.focus();
};
const blur = () => {
refSwitchNode.value?.blur();
};
expose({ focus, blur });
return () => (
<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}
class={{
[attrs.class as string]: attrs.class,
[props.prefixCls]: true,
[`${props.prefixCls}-checked`]: checked.value,
[`${props.prefixCls}-disabled`]: props.disabled,
}}
ref={refSwitchNode}
>
{props.loadingIcon}
<span class={`${props.prefixCls}-inner`}>
{checked.value
? getPropsSlot(slots, props, 'checkedChildren')
: getPropsSlot(slots, props, 'unCheckedChildren')}
</span>
</button>
);
},
});

2
v2-doc

@ -1 +1 @@
Subproject commit 496ff9154658d1ff28917f0bea6adfcb0cd05d43
Subproject commit 31d85319dcc0438b3c80957c99f57b931b047c11