mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-05-21 22:09:16 +08:00
feat(Form): support using switch
This commit is contained in:
parent
0e23124c3a
commit
1db9536182
@ -1,13 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<demo-block :title="$t('fieldType')">
|
<demo-block :title="$t('fieldType')">
|
||||||
<van-form>
|
<van-form @submit="onSubmit">
|
||||||
<van-field
|
<van-field name="checkbox" :label="$t('checkbox')">
|
||||||
name="checkbox"
|
|
||||||
:label="$t('checkbox')"
|
|
||||||
:rules="[{ required: true, message: $t('requireCheckbox') }]"
|
|
||||||
>
|
|
||||||
<van-checkbox v-model="checkbox" slot="input" shape="square" />
|
<van-checkbox v-model="checkbox" slot="input" shape="square" />
|
||||||
</van-field>
|
</van-field>
|
||||||
|
|
||||||
|
<van-field name="switch" :label="$t('switch')">
|
||||||
|
<van-switch v-model="switchChecked" slot="input" size="24" />
|
||||||
|
</van-field>
|
||||||
|
|
||||||
<div style="margin: 16px;">
|
<div style="margin: 16px;">
|
||||||
<van-button type="info" round block>{{ $t('submit') }}</van-button>
|
<van-button type="info" round block>{{ $t('submit') }}</van-button>
|
||||||
</div>
|
</div>
|
||||||
@ -20,12 +21,14 @@ export default {
|
|||||||
i18n: {
|
i18n: {
|
||||||
'zh-CN': {
|
'zh-CN': {
|
||||||
submit: '提交',
|
submit: '提交',
|
||||||
|
switch: '开关',
|
||||||
checkbox: '复选框',
|
checkbox: '复选框',
|
||||||
fieldType: '表单项类型',
|
fieldType: '表单项类型',
|
||||||
requireCheckbox: '请勾选复选框',
|
requireCheckbox: '请勾选复选框',
|
||||||
},
|
},
|
||||||
'en-US': {
|
'en-US': {
|
||||||
submit: 'Submit',
|
submit: 'Submit',
|
||||||
|
switch: 'Switch',
|
||||||
checkbox: 'Checkbox',
|
checkbox: 'Checkbox',
|
||||||
fieldType: 'Field Type',
|
fieldType: 'Field Type',
|
||||||
requireCheckbox: 'Checkbox is required',
|
requireCheckbox: 'Checkbox is required',
|
||||||
@ -35,7 +38,14 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
checkbox: false,
|
checkbox: false,
|
||||||
|
switchChecked: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onSubmit(values) {
|
||||||
|
console.log(values);
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
0
src/form/demo/Validate.vue
Normal file
0
src/form/demo/Validate.vue
Normal file
@ -5,22 +5,27 @@ exports[`renders demo correctly 1`] = `
|
|||||||
<div>
|
<div>
|
||||||
<form class="van-form">
|
<form class="van-form">
|
||||||
<div class="van-cell van-field">
|
<div class="van-cell van-field">
|
||||||
<div class="van-cell__title van-field__label"><span>username</span></div>
|
<div class="van-cell__title van-field__label"><span>用户名</span></div>
|
||||||
<div class="van-cell__value">
|
<div class="van-cell__value">
|
||||||
<div class="van-field__body"><input type="text" name="username" placeholder="username" class="van-field__control"></div>
|
<div class="van-field__body"><input type="text" name="username" placeholder="用户名" class="van-field__control"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="van-cell van-field">
|
<div class="van-cell van-field">
|
||||||
<div class="van-cell__title van-field__label"><span>password</span></div>
|
<div class="van-cell__title van-field__label"><span>密码</span></div>
|
||||||
<div class="van-cell__value">
|
<div class="van-cell__value">
|
||||||
<div class="van-field__body"><input type="password" name="password" placeholder="password" class="van-field__control"></div>
|
<div class="van-field__body"><input type="password" name="password" placeholder="密码" class="van-field__control"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div style="margin: 16px;"><button class="van-button van-button--info van-button--normal van-button--block van-button--round"><span class="van-button__text">提交</span></button></div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<form class="van-form">
|
||||||
<div class="van-cell van-field">
|
<div class="van-cell van-field">
|
||||||
<div class="van-cell__title van-field__label"><span>agree</span></div>
|
<div class="van-cell__title van-field__label"><span>复选框</span></div>
|
||||||
<div class="van-cell__value">
|
<div class="van-cell__value">
|
||||||
<div class="van-field__body">
|
<div class="van-field__body">
|
||||||
<div class="van-field__control">
|
<div class="van-field__control van-field__control--custom">
|
||||||
<div role="checkbox" tabindex="0" aria-checked="false" class="van-checkbox">
|
<div role="checkbox" tabindex="0" aria-checked="false" class="van-checkbox">
|
||||||
<div class="van-checkbox__icon van-checkbox__icon--square"><i class="van-icon van-icon-success">
|
<div class="van-checkbox__icon van-checkbox__icon--square"><i class="van-icon van-icon-success">
|
||||||
<!----></i></div>
|
<!----></i></div>
|
||||||
@ -28,7 +33,20 @@ exports[`renders demo correctly 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div> <button class="van-button van-button--primary van-button--normal"><span class="van-button__text">submit</span></button>
|
</div>
|
||||||
|
<div class="van-cell van-field">
|
||||||
|
<div class="van-cell__title van-field__label"><span>开关</span></div>
|
||||||
|
<div class="van-cell__value">
|
||||||
|
<div class="van-field__body">
|
||||||
|
<div class="van-field__control van-field__control--custom">
|
||||||
|
<div role="switch" aria-checked="false" class="van-switch" style="font-size: 24px;">
|
||||||
|
<div class="van-switch__node"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="margin: 16px;"><button class="van-button van-button--info van-button--normal van-button--block van-button--round"><span class="van-button__text">提交</span></button></div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
73
src/switch/index.js
Normal file
73
src/switch/index.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// Utils
|
||||||
|
import { createNamespace, addUnit } from '../utils';
|
||||||
|
import { BLUE } from '../utils/constant';
|
||||||
|
import { switchProps } from './shared';
|
||||||
|
|
||||||
|
// Mixins
|
||||||
|
import { FieldMixin } from '../mixins/field';
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import Loading from '../loading';
|
||||||
|
|
||||||
|
const [createComponent, bem] = createNamespace('switch');
|
||||||
|
|
||||||
|
export default createComponent({
|
||||||
|
mixins: [FieldMixin],
|
||||||
|
|
||||||
|
props: switchProps,
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
checked() {
|
||||||
|
return this.value === this.activeValue;
|
||||||
|
},
|
||||||
|
|
||||||
|
style() {
|
||||||
|
return {
|
||||||
|
fontSize: addUnit(this.size),
|
||||||
|
backgroundColor: this.checked ? this.activeColor : this.inactiveColor,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onClick(event) {
|
||||||
|
this.$emit('click', event);
|
||||||
|
|
||||||
|
if (!this.disabled && !this.loading) {
|
||||||
|
const newValue = this.checked ? this.inactiveValue : this.activeValue;
|
||||||
|
this.$emit('input', newValue);
|
||||||
|
this.$emit('change', newValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
genLoading() {
|
||||||
|
if (this.loading) {
|
||||||
|
const color = this.checked
|
||||||
|
? this.activeColor || BLUE
|
||||||
|
: this.inactiveColor || '';
|
||||||
|
|
||||||
|
return <Loading class={bem('loading')} color={color} />;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { checked, loading, disabled } = this;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class={bem({
|
||||||
|
on: checked,
|
||||||
|
loading,
|
||||||
|
disabled,
|
||||||
|
})}
|
||||||
|
role="switch"
|
||||||
|
style={this.style}
|
||||||
|
aria-checked={String(checked)}
|
||||||
|
onClick={this.onClick}
|
||||||
|
>
|
||||||
|
<div class={bem('node')}>{this.genLoading()}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
@ -1,78 +0,0 @@
|
|||||||
// Utils
|
|
||||||
import { createNamespace, addUnit } from '../utils';
|
|
||||||
import { BLUE } from '../utils/constant';
|
|
||||||
import { emit, inherit } from '../utils/functional';
|
|
||||||
import { switchProps, SharedSwitchProps } from './shared';
|
|
||||||
|
|
||||||
// Components
|
|
||||||
import Loading from '../loading';
|
|
||||||
|
|
||||||
// Types
|
|
||||||
import { CreateElement, RenderContext } from 'vue/types';
|
|
||||||
import { DefaultSlots } from '../utils/types';
|
|
||||||
|
|
||||||
export type SwitchEvents = {
|
|
||||||
onChange?(checked: boolean): void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const [createComponent, bem] = createNamespace('switch');
|
|
||||||
|
|
||||||
function Switch(
|
|
||||||
h: CreateElement,
|
|
||||||
props: SharedSwitchProps,
|
|
||||||
slots: DefaultSlots,
|
|
||||||
ctx: RenderContext<SharedSwitchProps>
|
|
||||||
) {
|
|
||||||
const {
|
|
||||||
size,
|
|
||||||
value,
|
|
||||||
loading,
|
|
||||||
disabled,
|
|
||||||
activeColor,
|
|
||||||
activeValue,
|
|
||||||
inactiveColor,
|
|
||||||
inactiveValue,
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const checked = value === activeValue;
|
|
||||||
|
|
||||||
const switchStyle = {
|
|
||||||
fontSize: addUnit(size),
|
|
||||||
backgroundColor: checked ? activeColor : inactiveColor,
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadingColor = checked ? activeColor || BLUE : inactiveColor || '';
|
|
||||||
|
|
||||||
function onClick(event: PointerEvent) {
|
|
||||||
emit(ctx, 'click', event);
|
|
||||||
|
|
||||||
if (!disabled && !loading) {
|
|
||||||
const newValue = checked ? inactiveValue : activeValue;
|
|
||||||
emit(ctx, 'input', newValue);
|
|
||||||
emit(ctx, 'change', newValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
class={bem({
|
|
||||||
on: checked,
|
|
||||||
loading,
|
|
||||||
disabled,
|
|
||||||
})}
|
|
||||||
role="switch"
|
|
||||||
style={switchStyle}
|
|
||||||
aria-checked={String(checked)}
|
|
||||||
onClick={onClick}
|
|
||||||
{...inherit(ctx)}
|
|
||||||
>
|
|
||||||
<div class={bem('node')}>
|
|
||||||
{loading && <Loading class={bem('loading')} color={loadingColor} />}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Switch.props = switchProps;
|
|
||||||
|
|
||||||
export default createComponent<SharedSwitchProps, SwitchEvents>(Switch);
|
|
@ -5,11 +5,9 @@ test('emit event', () => {
|
|||||||
const input = jest.fn();
|
const input = jest.fn();
|
||||||
const change = jest.fn();
|
const change = jest.fn();
|
||||||
const wrapper = mount(Switch, {
|
const wrapper = mount(Switch, {
|
||||||
context: {
|
listeners: {
|
||||||
on: {
|
input,
|
||||||
input,
|
change,
|
||||||
change,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
wrapper.trigger('click');
|
wrapper.trigger('click');
|
||||||
@ -22,11 +20,9 @@ test('disabled', () => {
|
|||||||
const input = jest.fn();
|
const input = jest.fn();
|
||||||
const change = jest.fn();
|
const change = jest.fn();
|
||||||
const wrapper = mount(Switch, {
|
const wrapper = mount(Switch, {
|
||||||
context: {
|
listeners: {
|
||||||
on: {
|
input,
|
||||||
input,
|
change,
|
||||||
change,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
propsData: {
|
propsData: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
@ -47,11 +43,9 @@ test('active-value & inactive-value prop', () => {
|
|||||||
activeValue: '1',
|
activeValue: '1',
|
||||||
inactiveValue: '2',
|
inactiveValue: '2',
|
||||||
},
|
},
|
||||||
context: {
|
listeners: {
|
||||||
on: {
|
input,
|
||||||
input,
|
change,
|
||||||
change,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -86,10 +80,8 @@ test('size prop', () => {
|
|||||||
test('click event', () => {
|
test('click event', () => {
|
||||||
const click = jest.fn();
|
const click = jest.fn();
|
||||||
const wrapper = mount(Switch, {
|
const wrapper = mount(Switch, {
|
||||||
context: {
|
listeners: {
|
||||||
on: {
|
click,
|
||||||
click,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user