From 67b9da71eb808c39692c9408b856946d1f96d2cf Mon Sep 17 00:00:00 2001
From: ajuner <106791576@qq.com>
Date: Sun, 11 Apr 2021 09:57:20 +0800
Subject: [PATCH] refactor(switch): use composition api (#3885)
* refactor(switch): use composition api
* test: fix
* fix: lint
* fix: remove prefixCls
* fix: use getPropsSlot
* fix: use emits
---
components/_util/props-util/index.js | 5 +
components/switch/__tests__/index.test.js | 12 +-
components/switch/index.tsx | 133 +++++++++---------
components/vc-switch/Switch.jsx | 124 ----------------
components/vc-switch/{index.js => index.ts} | 2 +-
.../{PropTypes.js => src/PropTypes.ts} | 2 +-
components/vc-switch/src/Switch.tsx | 99 +++++++++++++
7 files changed, 182 insertions(+), 195 deletions(-)
delete mode 100644 components/vc-switch/Switch.jsx
rename components/vc-switch/{index.js => index.ts} (57%)
rename components/vc-switch/{PropTypes.js => src/PropTypes.ts} (91%)
create mode 100644 components/vc-switch/src/Switch.tsx
diff --git a/components/_util/props-util/index.js b/components/_util/props-util/index.js
index 21a3c10d7..86eb35642 100644
--- a/components/_util/props-util/index.js
+++ b/components/_util/props-util/index.js
@@ -389,6 +389,10 @@ function isValidElement(element) {
return element && element.__v_isVNode && typeof element.type !== 'symbol'; // remove text node
}
+function getPropsSlot(slots, props, prop = 'default') {
+ return slots[prop]?.() ?? props[prop];
+}
+
export {
splitAttrs,
hasProp,
@@ -411,5 +415,6 @@ export {
getAllChildren,
findDOMNode,
flattenChildren,
+ getPropsSlot,
};
export default hasProp;
diff --git a/components/switch/__tests__/index.test.js b/components/switch/__tests__/index.test.js
index 810343dc5..0742de8e0 100644
--- a/components/switch/__tests__/index.test.js
+++ b/components/switch/__tests__/index.test.js
@@ -9,7 +9,11 @@ describe('Switch', () => {
mountTest(Switch);
it('should has click wave effect', async () => {
- const wrapper = mount(Switch);
+ const wrapper = mount({
+ render() {
+ return ;
+ },
+ });
wrapper.find('.ant-switch').trigger('click');
await new Promise(resolve => setTimeout(resolve, 0));
expect(wrapper.html()).toMatchSnapshot();
@@ -19,7 +23,11 @@ describe('Switch', () => {
resetWarned();
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
- mount(Switch, { props: { value: '' } });
+ mount({
+ render() {
+ return ;
+ },
+ });
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antdv: Switch] `value` is not validate prop, do you mean `checked`?',
);
diff --git a/components/switch/index.tsx b/components/switch/index.tsx
index 4a388e1eb..d97797517 100644
--- a/components/switch/index.tsx
+++ b/components/switch/index.tsx
@@ -1,86 +1,85 @@
-import { defineComponent, inject } from 'vue';
+import { defineComponent, inject, onBeforeMount, ref, ExtractPropTypes, computed } from 'vue';
import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined';
import PropTypes from '../_util/vue-types';
-import hasProp, { getOptionProps, getComponent } from '../_util/props-util';
import VcSwitch from '../vc-switch';
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>;
const Switch = defineComponent({
name: 'ASwitch',
__ANT_SWITCH: true,
inheritAttrs: false,
- props: {
- prefixCls: PropTypes.string,
- // size=default and size=large are the same
- size: PropTypes.oneOf(tuple('small', 'default', 'large')),
- disabled: PropTypes.looseBool,
- checkedChildren: PropTypes.any,
- unCheckedChildren: PropTypes.any,
- tabindex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
- checked: PropTypes.looseBool,
- defaultChecked: PropTypes.looseBool,
- autofocus: PropTypes.looseBool,
- loading: PropTypes.looseBool,
- onChange: PropTypes.func,
- onClick: PropTypes.func,
- 'onUpdate:checked': PropTypes.func,
- },
- // emits: ['change', 'click', 'update:checked'],
- setup() {
- return {
- refSwitchNode: undefined,
- configProvider: inject('configProvider', defaultConfigProvider),
- };
- },
- created() {
- warning(
- hasProp(this, 'checked') || !('value' in this.$attrs),
- 'Switch',
- '`value` is not validate prop, do you mean `checked`?',
- );
- },
- methods: {
- focus() {
- this.refSwitchNode?.focus();
- },
- blur() {
- this.refSwitchNode?.blur();
- },
- saveRef(c) {
- this.refSwitchNode = c;
- },
- },
+ props: switchProps,
+ setup(props: SwitchProps, { attrs, slots, expose }) {
+ const configProvider = inject('configProvider', defaultConfigProvider);
+ const refSwitchNode = ref();
- render() {
- const { prefixCls: customizePrefixCls, size, loading, disabled, ...restProps } = getOptionProps(
- this,
- );
- const { getPrefixCls } = this.configProvider;
- const prefixCls = getPrefixCls('switch', customizePrefixCls);
- const { $attrs } = this;
- const classes = {
- [$attrs.class as string]: $attrs.class,
- [`${prefixCls}-small`]: size === 'small',
- [`${prefixCls}-loading`]: loading,
+ const focus = () => {
+ refSwitchNode.value?.focus();
};
- const loadingIcon = loading ? : null;
- const switchProps = {
- ...restProps,
- ...$attrs,
- prefixCls,
- loadingIcon,
- checkedChildren: getComponent(this, 'checkedChildren'),
- unCheckedChildren: getComponent(this, 'unCheckedChildren'),
- disabled: disabled || loading,
- class: classes,
- ref: this.saveRef,
+ const blur = () => {
+ refSwitchNode.value?.blur();
};
- return (
+
+ 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);
+ });
+ return () => (
-
+ : null
+ }
+ checkedChildren={getPropsSlot(slots, props, 'checkedChildren')}
+ unCheckedChildren={getPropsSlot(slots, props, 'unCheckedChildren')}
+ disabled={props.disabled || props.loading}
+ class={{
+ [attrs.class as string]: attrs.class,
+ [`${prefixCls.value}-small`]: props.size === 'small',
+ [`${prefixCls.value}-loading`]: props.loading,
+ }}
+ ref={refSwitchNode}
+ />
);
},
diff --git a/components/vc-switch/Switch.jsx b/components/vc-switch/Switch.jsx
deleted file mode 100644
index c3ac8a6f4..000000000
--- a/components/vc-switch/Switch.jsx
+++ /dev/null
@@ -1,124 +0,0 @@
-import { switchPropTypes } from './PropTypes';
-import BaseMixin from '../_util/BaseMixin';
-import { hasProp, getOptionProps, getComponent } from '../_util/props-util';
-import Omit from 'omit.js';
-import { defineComponent } from 'vue';
-
-// function noop () {
-// }
-export default defineComponent({
- name: 'VcSwitch',
- mixins: [BaseMixin],
- inheritAttrs: false,
- props: {
- ...switchPropTypes,
- prefixCls: switchPropTypes.prefixCls.def('rc-switch'),
- // onChange: switchPropTypes.onChange.def(noop),
- // onClick: switchPropTypes.onClick.def(noop),
- },
- data() {
- let checked = false;
- if (hasProp(this, 'checked')) {
- checked = !!this.checked;
- } else {
- checked = !!this.defaultChecked;
- }
- return {
- stateChecked: checked,
- };
- },
- watch: {
- checked(val) {
- this.stateChecked = val;
- },
- },
- mounted() {
- this.$nextTick(() => {
- const { autofocus, disabled } = this;
- if (autofocus && !disabled) {
- this.focus();
- }
- });
- },
- methods: {
- saveRef(c) {
- this.refSwitchNode = c;
- },
- setChecked(checked, e) {
- if (this.disabled) {
- return;
- }
- if (!hasProp(this, 'checked')) {
- this.stateChecked = checked;
- }
- this.__emit('update:checked', checked);
- this.__emit('change', checked, e);
- },
- handleClick(e) {
- const checked = !this.stateChecked;
- this.setChecked(checked, e);
- this.__emit('click', checked, e);
- },
- handleKeyDown(e) {
- if (e.keyCode === 37) {
- // Left
- this.setChecked(false, e);
- } else if (e.keyCode === 39) {
- // Right
- this.setChecked(true, e);
- }
- },
- handleMouseUp(e) {
- this.refSwitchNode?.blur();
-
- this.__emit('mouseup', e);
- },
- focus() {
- this.refSwitchNode?.focus();
- },
- blur() {
- this.refSwitchNode?.blur();
- },
- },
- render() {
- const { prefixCls, disabled, loadingIcon, ...restProps } = getOptionProps(this);
- const checked = this.stateChecked;
- const { $attrs } = this;
- const switchClassName = {
- [$attrs.class]: $attrs.class,
- [prefixCls]: true,
- [`${prefixCls}-checked`]: checked,
- [`${prefixCls}-disabled`]: disabled,
- };
- const spanProps = {
- ...Omit(restProps, [
- 'checkedChildren',
- 'unCheckedChildren',
- 'checked',
- 'autofocus',
- 'defaultChecked',
- ]),
- ...$attrs,
- onKeydown: this.handleKeyDown,
- onClick: this.handleClick,
- onMouseup: this.handleMouseUp,
- type: 'button',
- role: 'switch',
- 'aria-checked': checked,
- disabled,
- class: switchClassName,
- ref: this.saveRef,
- };
-
- return (
-
- );
- },
-});
diff --git a/components/vc-switch/index.js b/components/vc-switch/index.ts
similarity index 57%
rename from components/vc-switch/index.js
rename to components/vc-switch/index.ts
index bfde65dd6..dc4db4c32 100644
--- a/components/vc-switch/index.js
+++ b/components/vc-switch/index.ts
@@ -1,4 +1,4 @@
// base rc-switch 1.9.0
-import Switch from './Switch';
+import Switch from './src/Switch';
export default Switch;
diff --git a/components/vc-switch/PropTypes.js b/components/vc-switch/src/PropTypes.ts
similarity index 91%
rename from components/vc-switch/PropTypes.js
rename to components/vc-switch/src/PropTypes.ts
index 17bc7ddf9..70ed02ad4 100644
--- a/components/vc-switch/PropTypes.js
+++ b/components/vc-switch/src/PropTypes.ts
@@ -1,4 +1,4 @@
-import PropTypes from '../_util/vue-types';
+import PropTypes from '../../_util/vue-types';
export const switchPropTypes = {
prefixCls: PropTypes.string,
diff --git a/components/vc-switch/src/Switch.tsx b/components/vc-switch/src/Switch.tsx
new file mode 100644
index 000000000..4064553b8
--- /dev/null
+++ b/components/vc-switch/src/Switch.tsx
@@ -0,0 +1,99 @@
+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 () => (
+
+ );
+ },
+});