diff --git a/packages/varlet-vue2-cli/site/mobile/components/AppType.vue b/packages/varlet-vue2-cli/site/mobile/components/AppType.vue index 7894b9b..c0c692e 100644 --- a/packages/varlet-vue2-cli/site/mobile/components/AppType.vue +++ b/packages/varlet-vue2-cli/site/mobile/components/AppType.vue @@ -14,7 +14,7 @@ export default { .app-type { width: 100%; padding: 15px 0; - color: #888; + color: var(--site-config-color-sub-text); font-size: 14px; } diff --git a/packages/varlet-vue2-ui/src/cell/Cell.vue b/packages/varlet-vue2-ui/src/cell/Cell.vue index f77a8e4..2486597 100644 --- a/packages/varlet-vue2-ui/src/cell/Cell.vue +++ b/packages/varlet-vue2-ui/src/cell/Cell.vue @@ -1,6 +1,6 @@ diff --git a/packages/varlet-vue2-ui/src/cell/__test__/__snapshots__/index.spec.js.snap b/packages/varlet-vue2-ui/src/cell/__test__/__snapshots__/index.spec.js.snap index c11eb21..f436c34 100644 --- a/packages/varlet-vue2-ui/src/cell/__test__/__snapshots__/index.spec.js.snap +++ b/packages/varlet-vue2-ui/src/cell/__test__/__snapshots__/index.spec.js.snap @@ -1,91 +1,101 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`test cell example 1`] = ` -"
-
基本使用
-
- -
-
这是单元格
- +"
+
+
基本使用
+
+ +
+
这是单元格
+ +
+
- -
-
- -
-
这是单元格
- +
+ +
+
这是单元格
+ +
+
-
-
-
-
显示图标
-
-
-
-
这是单元格
- +
+
显示图标
+
+
+
+
这是单元格
+ +
+
-
-
-
-
-
-
这是单元格
- +
+
+
+
这是单元格
+ +
+
-
-
-
-
显示描述
-
-
-
-
这是单元格
-
描述
+
+
显示描述
+
+
+
+
这是单元格
+
+ 描述 +
+
+
-
-
-
- -
-
这是单元格
-
描述
+
+ +
+
这是单元格
+
+ 描述 +
+
+
-
-
-
-
显示边框
-
- -
-
这是单元格
- +
+
显示边框
+
+ +
+
这是单元格
+ +
+
- -
-
- -
-
这是单元格
- +
+ +
+
这是单元格
+ +
+
-
" `; exports[`test cell props 1`] = ` "
-
+
This is Cell
-
This is desc
+
+ This is desc +
+
+
+
text
-
text
" `; diff --git a/packages/varlet-vue2-ui/src/cell/__test__/index.spec.js b/packages/varlet-vue2-ui/src/cell/__test__/index.spec.js index da68104..13e1590 100644 --- a/packages/varlet-vue2-ui/src/cell/__test__/index.spec.js +++ b/packages/varlet-vue2-ui/src/cell/__test__/index.spec.js @@ -28,7 +28,7 @@ test('test cell props', async () => { icon: 'fire', }, scopedSlots: { - extra: 'text', + extra: '
text
', }, }) expect(wrapper.find('.var-icon').exists()).toBe(true) diff --git a/packages/varlet-vue2-ui/src/form-details/formDetails.less b/packages/varlet-vue2-ui/src/form-details/formDetails.less index edf02d0..cfa2136 100644 --- a/packages/varlet-vue2-ui/src/form-details/formDetails.less +++ b/packages/varlet-vue2-ui/src/form-details/formDetails.less @@ -13,7 +13,7 @@ } .var { - &-form-details-enter-from, + &-form-details-enter, &-form-details-leave-to { opacity: 0; margin-top: 2px !important; diff --git a/packages/varlet-vue2-ui/src/form/Form.vue b/packages/varlet-vue2-ui/src/form/Form.vue new file mode 100644 index 0000000..389c793 --- /dev/null +++ b/packages/varlet-vue2-ui/src/form/Form.vue @@ -0,0 +1,43 @@ + + + + + diff --git a/packages/varlet-vue2-ui/src/form/example/index.vue b/packages/varlet-vue2-ui/src/form/example/index.vue new file mode 100644 index 0000000..90d4595 --- /dev/null +++ b/packages/varlet-vue2-ui/src/form/example/index.vue @@ -0,0 +1,180 @@ + + + + + diff --git a/packages/varlet-vue2-ui/src/form/example/locale/en-US.ts b/packages/varlet-vue2-ui/src/form/example/locale/en-US.ts new file mode 100644 index 0000000..0cf4462 --- /dev/null +++ b/packages/varlet-vue2-ui/src/form/example/locale/en-US.ts @@ -0,0 +1,31 @@ +export default { + example: 'Form Example', + username: 'Please input username', + usernameMessage: 'The username cannot be empty', + password: 'Please input password', + passwordMessage: 'The password cannot be empty', + department: 'Please select department', + departmentMessage: 'The select cannot be empty', + eat: 'Eat', + sleep: 'Sleep', + play: 'Play game', + departmentUnit: ' department', + group: 'Please select group', + groupMessage: 'The select cannot be empty', + groupUnit: ' group', + genderMessage: 'The gender cannot be empty', + male: 'Male', + female: 'Female', + rateMessage: 'It has to be greater than 2', + likeMessage: 'The select cannot be empty', + licenseMessage: 'You must turn on', + countMessage: 'It has to be greater than 10', + rangeMessage: 'It has to be greater than 10', + filesMessage: 'Upload at least one picture', + controller: 'Form control', + reset: 'Empty form', + resetValidation: 'Empty the validation', + validate: 'Trigger validation', + disabled: 'Form disabled', + readonly: 'Form readonly', +} diff --git a/packages/varlet-vue2-ui/src/form/example/locale/index.ts b/packages/varlet-vue2-ui/src/form/example/locale/index.ts new file mode 100644 index 0000000..d2e375e --- /dev/null +++ b/packages/varlet-vue2-ui/src/form/example/locale/index.ts @@ -0,0 +1,23 @@ +// lib +import _zhCN from '../../../locale/zh-CN' +import _enCN from '../../../locale/en-US' +// mobile example doc +import zhCN from './zh-CN' +import enUS from './en-US' +import { useLocale, add as _add, use as _use } from '../../../locale' + +const { add, use: exampleUse, pack, packs, merge } = useLocale() + +const use = (lang: string) => { + _use(lang) + exampleUse(lang) +} + +export { add, pack, packs, merge, use } + +// lib +_add('zh-CN', _zhCN) +_add('en-US', _enCN) +// mobile example doc +add('zh-CN', zhCN as any) +add('en-US', enUS as any) diff --git a/packages/varlet-vue2-ui/src/form/example/locale/zh-CN.ts b/packages/varlet-vue2-ui/src/form/example/locale/zh-CN.ts new file mode 100644 index 0000000..01c109e --- /dev/null +++ b/packages/varlet-vue2-ui/src/form/example/locale/zh-CN.ts @@ -0,0 +1,31 @@ +export default { + example: '表单示例', + username: '请输入用户名', + usernameMessage: '用户名不能为空', + password: '请输入密码', + passwordMessage: '请输入密码', + department: '请选择部门', + departmentMessage: '必须选一个部门', + eat: '吃饭', + sleep: '睡觉', + play: '打游戏', + departmentUnit: '部', + group: '请选择组织', + groupMessage: '至少选择一个组织', + groupUnit: '组', + genderMessage: '必须选择一个性别', + male: '男', + female: '女', + rateMessage: '必须大于2', + likeMessage: '至少选择一个爱好', + licenseMessage: '您必须开启', + countMessage: '必须大于10', + rangeMessage: '必须大于10', + filesMessage: '至少上传一张图片', + controller: '表单控制', + reset: '清空表单', + resetValidation: '清空验证', + validate: '触发验证', + disabled: '表单禁用', + readonly: '表单只读', +} diff --git a/packages/varlet-vue2-ui/src/form/index.ts b/packages/varlet-vue2-ui/src/form/index.ts new file mode 100644 index 0000000..add80af --- /dev/null +++ b/packages/varlet-vue2-ui/src/form/index.ts @@ -0,0 +1,10 @@ +import type { App } from 'vue' +import Form from './Form.vue' + +Form.install = function (app: App) { + app.component(Form.name, Form) +} + +export const _FormComponent = Form + +export default Form diff --git a/packages/varlet-vue2-ui/src/form/props.ts b/packages/varlet-vue2-ui/src/form/props.ts new file mode 100644 index 0000000..12333a4 --- /dev/null +++ b/packages/varlet-vue2-ui/src/form/props.ts @@ -0,0 +1,10 @@ +export const props = { + disabled: { + type: Boolean, + default: false, + }, + readonly: { + type: Boolean, + default: false, + }, +} diff --git a/packages/varlet-vue2-ui/src/radio-group/RadioGroup.vue b/packages/varlet-vue2-ui/src/radio-group/RadioGroup.vue new file mode 100644 index 0000000..61db042 --- /dev/null +++ b/packages/varlet-vue2-ui/src/radio-group/RadioGroup.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/packages/varlet-vue2-ui/src/radio-group/__tests__/__snapshots__/index.spec.js.snap b/packages/varlet-vue2-ui/src/radio-group/__tests__/__snapshots__/index.spec.js.snap new file mode 100644 index 0000000..3648642 --- /dev/null +++ b/packages/varlet-vue2-ui/src/radio-group/__tests__/__snapshots__/index.spec.js.snap @@ -0,0 +1,237 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`test radio example 1`] = ` +"
+
基本使用
+
+
+
+
当前的值: false
+
+ + + +
+
设置状态值
+
+
+
+
当前的值: 0
+
+ + + +
+
修改图标和颜色
+
+
+
+
当前的值: false
+
+ + + +
+
禁用
+
+
+
+
当前的值: false
+
+ + + +
+
只读
+
+
+
+
当前的值: false
+
+ + + +
+
单选框组
+
+
+
+
+
+
吃饭
+
+ + + +
+
+
+
+
睡觉
+
+ + + +
+
+ + + +
+
当前的值: 0
+
单选框字段校验
+
+
+
+
+ 当前的值: false +
+
+ + + +
+
单选框组字段校验
+
+
+
+
+
+
吃饭
+
+ + + +
+
+
+
+
睡觉
+
+ + + +
+
+ + + +
+
当前的值: 0
+
+
" +`; + +exports[`test radio group layout direction 1`] = ` +"
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+ + + +
+
+ + + +
" +`; + +exports[`test radio group validation 1`] = ` +"
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+ + + +
+
+ +
+
必须选第一个
+
+
+
+
" +`; + +exports[`test radio group validation 2`] = ` +"
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+ + + +
+
+ + + +
" +`; + +exports[`test radio validation 1`] = ` +"
+
+
+
+
+ +
+
您必须勾选
+
+
+
+
" +`; + +exports[`test radio validation 2`] = ` +"
+
+
+
+
+ + + +
" +`; diff --git a/packages/varlet-vue2-ui/src/radio-group/__tests__/index.spec.js b/packages/varlet-vue2-ui/src/radio-group/__tests__/index.spec.js new file mode 100644 index 0000000..be7b9bf --- /dev/null +++ b/packages/varlet-vue2-ui/src/radio-group/__tests__/index.spec.js @@ -0,0 +1,280 @@ +import example from '../example' +import RadioGroup from '..' +import Radio from '../../radio' +import VarRadioGroup from '../RadioGroup' +import VarRadio from '../../radio/Radio' +import { mount } from '@vue/test-utils' +import Vue from 'vue' +import { delay } from '../../utils/jest' + +test('test radio example', () => { + const wrapper = mount(example) + expect(wrapper.html()).toMatchSnapshot() + wrapper.destroy() +}) + +test('test radio group plugin', () => { + const app = Vue.use(RadioGroup) + expect(app.component(RadioGroup.name)).toBeTruthy() +}) + +test('test radio plugin', () => { + const app = Vue.use(Radio) + expect(app.component(Radio.name)).toBeTruthy() +}) + +test('test radio check value', async () => { + const onInput = jest.fn((value) => wrapper.setProps({ value: value })) + + const wrapper = mount(VarRadio, { + propsData: { + value: false, + }, + listeners: { + input: onInput, + }, + }) + + await wrapper.find('.var-radio').trigger('click') + expect(onInput).lastCalledWith(true) + expect(wrapper.props('value')).toBe(true) + + await wrapper.find('.var-radio').trigger('click') + expect(onInput).lastCalledWith(false) + expect(wrapper.props('value')).toBe(false) + + wrapper.destroy() +}) + +test('test radio check value with custom value', async () => { + const onInput = jest.fn((value) => wrapper.setProps({ value })) + + const wrapper = mount(VarRadio, { + propsData: { + value: 0, + uncheckedValue: 0, + checkedValue: 1, + }, + listeners: { + input: onInput, + }, + }) + + await wrapper.find('.var-radio').trigger('click') + expect(onInput).lastCalledWith(1) + expect(wrapper.props('value')).toBe(1) + + wrapper.destroy() +}) + +test('test radio onClick & onChange', async () => { + const onClick = jest.fn() + const onChange = jest.fn() + + const wrapper = mount(VarRadio, { + propsData: { + value: false, + }, + listeners: { + click: onClick, + change: onChange, + }, + }) + + await wrapper.find('.var-radio').trigger('click') + expect(onClick).toHaveBeenCalledTimes(1) + expect(onChange).lastCalledWith(true) + + wrapper.destroy() +}) + +test('test radio toggle method', async () => { + const onInput = jest.fn((value) => wrapper.setProps({ value })) + + const wrapper = mount(VarRadio, { + propsData: { + value: false, + }, + listeners: { + input: onInput, + }, + }) + + wrapper.vm.toggle() + await delay(16) + + expect(onInput).lastCalledWith(true) + expect(wrapper.props('value')).toBe(true) + + wrapper.destroy() +}) + +test('test radio disabled', async () => { + const onInput = jest.fn((value) => wrapper.setProps({ value })) + const onClick = jest.fn() + const onChange = jest.fn() + + const wrapper = mount(VarRadio, { + propsData: { + value: false, + disabled: true, + }, + listeners: { + input: onInput, + click: onClick, + change: onChange, + }, + }) + + await wrapper.find('.var-radio').trigger('click') + + expect(onInput).toHaveBeenCalledTimes(0) + expect(onClick).toHaveBeenCalledTimes(0) + expect(onChange).toHaveBeenCalledTimes(0) + expect(wrapper.props('value')).toBe(false) + + wrapper.destroy() +}) + +test('test radio readonly', async () => { + const onInput = jest.fn((value) => wrapper.setProps({ value })) + const onClick = jest.fn() + const onChange = jest.fn() + + const wrapper = mount(VarRadio, { + propsData: { + value: false, + readonly: true, + }, + listeners: { + input: onInput, + click: onClick, + change: onChange, + }, + }) + + await wrapper.find('.var-radio').trigger('click') + + expect(onInput).toHaveBeenCalledTimes(0) + expect(onClick).toHaveBeenCalledTimes(1) + expect(onChange).toHaveBeenCalledTimes(0) + expect(wrapper.props('value')).toBe(false) + + wrapper.destroy() +}) + +test('test radio with radio group', async () => { + const wrapper = mount({ + components: { + [VarRadioGroup.name]: VarRadioGroup, + [VarRadio.name]: VarRadio, + }, + data: () => ({ + value: 2, + }), + template: ` + + + + + `, + }) + + await wrapper.find('.var-radio').trigger('click') + expect(wrapper.vm.value).toBe(1) + + await wrapper.find('.var-radio').trigger('click') + expect(wrapper.vm.value).toBe(1) + + wrapper.destroy() +}) + +test('test radio validation', async () => { + const onInput = jest.fn((value) => wrapper.setProps({ value })) + + const wrapper = mount(VarRadio, { + propsData: { + value: false, + rules: [(v) => v || '您必须勾选'], + }, + listeners: { + input: onInput, + }, + }) + + wrapper.vm.validate() + await delay(16) + + expect(wrapper.find('.var-form-details__message').text()).toBe('您必须勾选') + expect(wrapper.html()).toMatchSnapshot() + + await wrapper.find('.var-radio').trigger('click') + await delay(16) + + expect(wrapper.find('.var-form-details__message').exists()).toBeFalsy() + expect(wrapper.html()).toMatchSnapshot() + + wrapper.vm.reset() + await delay(16) + expect(wrapper.props('value')).toBe(false) + + wrapper.destroy() +}) + +test('test radio group validation', async () => { + const wrapper = mount({ + components: { + [VarRadioGroup.name]: VarRadioGroup, + [VarRadio.name]: VarRadio, + }, + data: () => ({ + value: 2, + }), + template: ` + + + + + `, + }) + + const { radioGroup } = wrapper.vm.$refs + + radioGroup.validate() + await delay(16) + expect(wrapper.find('.var-form-details__message').text()).toBe('必须选第一个') + expect(wrapper.html()).toMatchSnapshot() + + radioGroup.reset() + await delay(16) + expect(wrapper.vm.value).toBe(undefined) + + await wrapper.find('.var-radio').trigger('click') + await delay(16) + + expect(wrapper.find('.var-form-details__message').exists()).toBeFalsy() + expect(wrapper.html()).toMatchSnapshot() + + wrapper.destroy() +}) + +test('test radio group layout direction', async () => { + const wrapper = mount({ + components: { + [VarRadioGroup.name]: VarRadioGroup, + [VarRadio.name]: VarRadio, + }, + data: () => ({ + value: 2, + }), + template: ` + + + + + `, + }) + + expect(wrapper.html()).toMatchSnapshot() + wrapper.destroy() +}) diff --git a/packages/varlet-vue2-ui/src/radio-group/docs/en-US.md b/packages/varlet-vue2-ui/src/radio-group/docs/en-US.md new file mode 100644 index 0000000..90ffd3a --- /dev/null +++ b/packages/varlet-vue2-ui/src/radio-group/docs/en-US.md @@ -0,0 +1,211 @@ +# Radio + +### Install + +```js +import Vue from 'vue' +import { Radio, RadioGroup } from '@varlet/ui' + +Vue.use(RadioGroup).use(Radio) +``` + +### Basic Usage + +```html +Current value: {{ value }} +``` + +```js +export default { + data: () => ({ + value: false + }) +} +``` + +### Set State value + +```html + + Current value: {{ value }} + +``` + +```js +export default { + data: () => ({ + value: 0 + }) +} +``` + +### Modify the icon and color + +```html + + + + + +``` + +### Disabled + +```html +Current value: {{ value }} +``` + +### Readonly + +```html +Current value: {{ value }} +``` + +### RadioGroup + +In the radio group, you must set the `checked-value` to identify the `radio`, +The radio group bind group checked identifiers. + +```html + + Eat + Sleep + +``` + +```js +export default { + data: () => ({ + value: 0 + }) +} +``` + +### Radio validation + +```html + + Current value: {{ value }} + +``` + +### RadioGroup validate + +```html + + Eat + Sleep + +``` + + +## API + +### Props + +#### RadioGroup Props + +| Prop | Description | Type | Default | +| --- | --- | --- | --- | +| `v-model` | The value of the binding | _any_ | `-` | +| `direction` | The layout direction,Optional value is `horizontal` `vertical` | _string_ | `horizontal` | +| `rules` | The validation rules,Returns `true` to indicate that the validation passed,The remaining values are converted to text as user prompts | _Array<(value: any) => any>_ | `-` | + +#### Radio Props + +| Prop | Description | Type | Default | +| --- | --- | --- | --- | +| `v-model` | The value of the binding | _any_ | `false` | +| `checked-value` | Checked value | _any_ | `true` | +| `unchecked-value` | Unchecked value | _any_ | `false` | +| `checked-color` | Checked color | _any_ | `-` | +| `unchecked-color` | Unchecked color | _any_ | `-` | +| `icon-size` | Icon size | _string \| number_ | `-` | +| `readonly` | Whether the readonly | _boolean_ | `false` | +| `disabled` | Whether the disabled | _boolean_ | `false` | +| `ripple` | Whether to open ripple | _boolean_ | `true` | +| `rules` | The validation rules,Returns `true` to indicate that the validation passed,The remaining values are converted to text as user prompts | _Array<(value: any) => any>_ | `-` | + +### Methods + +#### RadioGroup Methods + +| Method | Description | Arguments | Return | +| --- | --- | --- | --- | +| `validate` | Trigger validate | `-` | `valid: Promise` | +| `resetValidation` | Clearing validate messages | `-` | `-` | +| `reset` | Clear the value of the binding(set to `undefined`) and validate messages | `-` | `-` | + +#### Radio Methods + +| Method | Description | Arguments | Return | +| --- | --- | --- | --- | +| `validate` | Trigger validate | `-` | `valid: Promise` | +| `resetValidation` | Clearing validate messages | `-` | `-` | +| `reset` | Clear the value of the binding(set to `unchecked-value`) and validate messages | `-` | `-` | +| `toggle` | Toggle the checked state, pass `checked-value` to check, `unchecked-value` to uncheck, do not pass or other cases to reverse | `value: any` | `-` | + +### Events + +#### RadioGroup Events + +| Event | Description | Arguments | +| --- | --- | --- | +| `change` | Trigger on change | `value: any` | + +#### Radio Events + +| Event | Description | Arguments | +| --- | --- | --- | +| `click` | Triggered on Click | `e: Event` | +| `change` | Trigger on change | `value: any` | + +### Slots + +#### RadioGroup Slots + +| Slot | Description | Arguments | +| --- | --- | --- | +| `default` | Radio group content | `-` | + +#### Radio Slots + +| Slot | Description | Arguments | +| --- | --- | --- | +| `checked-icon` | Checked icon | `-` | +| `unchecked-icon` | Unchecked icon | `-` | +| `default` | Displayed text | `-` | + +### Style Variables +Here are the CSS variables used by the component, Styles can be customized using [StyleProvider](#/en-US/style-provider) + +#### Radio Variables + +| Variable | Default | +| --- | --- | +| `--radio-checked-color` | `var(--color-primary)` | +| `--radio-unchecked-color` | `#555` | +| `--radio-disabled-color` | `var(--color-text-disabled)` | +| `--radio-error-color` | `var(--color-danger)` | +| `--radio-action-padding` | `6px` | +| `--radio-icon-size` | `24px` | \ No newline at end of file diff --git a/packages/varlet-vue2-ui/src/radio-group/docs/zh-CN.md b/packages/varlet-vue2-ui/src/radio-group/docs/zh-CN.md new file mode 100644 index 0000000..baed266 --- /dev/null +++ b/packages/varlet-vue2-ui/src/radio-group/docs/zh-CN.md @@ -0,0 +1,211 @@ +# 单选框 + +### 引入 + +```js +import Vue from 'vue' +import { Radio, RadioGroup } from '@varlet/ui' + +Vue.use(RadioGroup).use(Radio) +``` + +### 基本使用 + +```html +当前的值: {{ value }} +``` + +```js +export default { + data: () => ({ + value: false + }) +} +``` + +### 设置状态值 + +```html + + 当前的值: {{ value }} + +``` + +```js +export default { + data: () => ({ + value: 0 + }) +} +``` + +### 修改图标和颜色 + +```html + + + + + +``` + +### 禁用 + +```html +当前的值: {{ value }} +``` + +### 只读 + +```html +当前的值: {{ value }} +``` + +### 单选框组 + +在单选框组中,需要给 `radio` 设置 `checked-value` 用来标识, +单选框组会绑定选择的标识。 + +```html + + 吃饭 + 睡觉 + +``` + +```js +export default { + data: () => ({ + value: 0 + }) +} +``` + +### 单选框字段校验 + +```html + + 当前的值: {{ value }} + +``` + +### 单选框组字段校验 + +```html + + 吃饭 + 睡觉 + +``` + + +## API + +### 属性 + +#### RadioGroup Props + +| 参数 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| `v-model` | 绑定的值 | _any_ | `-` | +| `direction` | 布局方向,可选值为 `horizontal` `vertical` | _string_ | `horizontal` | +| `rules` | 验证规则,返回 `true` 表示验证通过,其余的值则转换为文本作为用户提示 | _Array<(value: any) => any>_ | `-` | + +#### Radio Props + +| 参数 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| `v-model` | 绑定的值 | _any_ | `false` | +| `checked-value` | 选中状态的值 | _any_ | `true` | +| `unchecked-value` | 未选中状态的值 | _any_ | `false` | +| `checked-color` | 选中状态的颜色 | _any_ | `-` | +| `unchecked-color` | 未选中状态的颜色 | _any_ | `-` | +| `icon-size` | 图标尺寸 | _string \| number_ | `-` | +| `disabled` | 是否禁用 | _boolean_ | `false` | +| `readonly` | 是否只读 | _boolean_ | `false` | +| `ripple` | 是否开启水波纹 | _boolean_ | `true` | +| `rules` | 验证规则,返回 `true` 表示验证通过,其余的值则转换为文本作为用户提示 | _Array<(value: any) => any>_ | `-` | + +### 方法 + +#### RadioGroup Methods + +| 方法名 | 说明 | 参数 | 返回值 | +| --- | --- | --- | --- | +| `validate` | 触发校验 | `-` | `valid: Promise` | +| `resetValidation` | 清空校验信息 | `-` | `-` | +| `reset` | 清空绑定的值(设置为 `undefined`)和校验信息 | `-` | `-` | + +#### Radio Methods + +| 方法名 | 说明 | 参数 | 返回值 | +| --- | --- | --- | --- | +| `validate` | 触发校验 | `-` | `valid: Promise` | +| `resetValidation` | 清空校验信息 | `-` | `-` | +| `reset` | 清空绑定的值(设置为 `unchecked-value`)和校验信息 | `-` | `-` | +| `toggle` | 切换选中状态,传 `checked-value` 为选中,`unchecked-value` 为取消选中,不传或其他情况为取反 | `value: any` | `-` | + +### 事件 + +#### RadioGroup Events + +| 事件名 | 说明 | 参数 | +| --- | --- | --- | +| `change` | 变更时触发 | `value: any` | + +#### Radio Events + +| 事件名 | 说明 | 参数 | +| --- | --- | --- | +| `click` | 点击时触发 | `e: Event` | +| `change` | 状态变更时触发 | `value: any` | + +### 插槽 + +#### RadioGroup Slots + +| 插槽名 | 说明 | 参数 | +| --- | --- | --- | +| `default` | 单选框组的内容 | `-` | + +#### Radio Slots + +| 插槽名 | 说明 | 参数 | +| --- | --- | --- | +| `checked-icon` | 选中图标 | `-` | +| `unchecked-icon` | 未选中图标 | `-` | +| `default` | 显示的文本 | `-` | + +### 样式变量 +以下为组件使用的 css 变量,可以使用 [StyleProvider 组件](#/zh-CN/style-provider)进行样式定制 + +#### Radio Variables + +| 变量名 | 默认值 | +| --- | --- | +| `--radio-checked-color` | `var(--color-primary)` | +| `--radio-unchecked-color` | `#555` | +| `--radio-disabled-color` | `var(--color-text-disabled)` | +| `--radio-error-color` | `var(color-danger)` | +| `--radio-action-padding` | `6px` | +| `--radio-icon-size` | `24px` | diff --git a/packages/varlet-vue2-ui/src/radio-group/example/index.vue b/packages/varlet-vue2-ui/src/radio-group/example/index.vue new file mode 100644 index 0000000..9f173c4 --- /dev/null +++ b/packages/varlet-vue2-ui/src/radio-group/example/index.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/packages/varlet-vue2-ui/src/radio-group/example/locale/en-US.ts b/packages/varlet-vue2-ui/src/radio-group/example/locale/en-US.ts new file mode 100644 index 0000000..a0e3bf0 --- /dev/null +++ b/packages/varlet-vue2-ui/src/radio-group/example/locale/en-US.ts @@ -0,0 +1,17 @@ +export default { + basicUsage: 'Basic Usage', + currentValue: 'Current value:', + setState: 'Set State value', + setStyle: 'Modify the icon and color', + disabled: 'Disabled', + readonly: 'Readonly', + eat: 'Eat', + sleep: 'Sleep', + checkAll: 'Check All', + inverseAll: 'Inverse All', + radioGroup: 'RadioGroup', + radioValidate: 'Radio validation', + radioGroupValidate: 'RadioGroup validate', + radioValidateMessage: 'Please check your choice', + radioGroupValidateMessage: 'Please check eat', +} diff --git a/packages/varlet-vue2-ui/src/radio-group/example/locale/index.ts b/packages/varlet-vue2-ui/src/radio-group/example/locale/index.ts new file mode 100644 index 0000000..d2e375e --- /dev/null +++ b/packages/varlet-vue2-ui/src/radio-group/example/locale/index.ts @@ -0,0 +1,23 @@ +// lib +import _zhCN from '../../../locale/zh-CN' +import _enCN from '../../../locale/en-US' +// mobile example doc +import zhCN from './zh-CN' +import enUS from './en-US' +import { useLocale, add as _add, use as _use } from '../../../locale' + +const { add, use: exampleUse, pack, packs, merge } = useLocale() + +const use = (lang: string) => { + _use(lang) + exampleUse(lang) +} + +export { add, pack, packs, merge, use } + +// lib +_add('zh-CN', _zhCN) +_add('en-US', _enCN) +// mobile example doc +add('zh-CN', zhCN as any) +add('en-US', enUS as any) diff --git a/packages/varlet-vue2-ui/src/radio-group/example/locale/zh-CN.ts b/packages/varlet-vue2-ui/src/radio-group/example/locale/zh-CN.ts new file mode 100644 index 0000000..a391952 --- /dev/null +++ b/packages/varlet-vue2-ui/src/radio-group/example/locale/zh-CN.ts @@ -0,0 +1,17 @@ +export default { + basicUsage: '基本使用', + currentValue: '当前的值:', + setState: '设置状态值', + setStyle: '修改图标和颜色', + disabled: '禁用', + readonly: '只读', + eat: '吃饭', + sleep: '睡觉', + checkAll: '全选', + inverseAll: '反选', + radioGroup: '单选框组', + radioValidate: '单选框字段校验', + radioGroupValidate: '单选框组字段校验', + radioValidateMessage: '请勾选', + radioGroupValidateMessage: '必须选择吃饭', +} diff --git a/packages/varlet-vue2-ui/src/radio-group/index.ts b/packages/varlet-vue2-ui/src/radio-group/index.ts new file mode 100644 index 0000000..dc28b15 --- /dev/null +++ b/packages/varlet-vue2-ui/src/radio-group/index.ts @@ -0,0 +1,10 @@ +import { VueConstructor } from 'vue' +import RadioGroup from './RadioGroup.vue' + +RadioGroup.install = function (app: VueConstructor) { + app.component(RadioGroup.name, RadioGroup) +} + +export const _RadioGroupComponent = RadioGroup + +export default RadioGroup diff --git a/packages/varlet-vue2-ui/src/radio-group/props.ts b/packages/varlet-vue2-ui/src/radio-group/props.ts new file mode 100644 index 0000000..7251c96 --- /dev/null +++ b/packages/varlet-vue2-ui/src/radio-group/props.ts @@ -0,0 +1,26 @@ +import type { PropType } from 'vue' + +export type ValidateTriggers = 'onChange' + +export function directionValidator(direction: string) { + return ['horizontal', 'vertical'].includes(direction) +} + +export const props = { + value: { + type: [String, Number, Boolean, Object, Array] as PropType, + default: undefined, + }, + direction: { + type: String as PropType<'horizontal' | 'vertical'>, + default: 'horizontal', + validator: directionValidator, + }, + validateTrigger: { + type: Array as PropType>, + default: () => ['onChange'], + }, + rules: { + type: Array as PropType any>>, + }, +} diff --git a/packages/varlet-vue2-ui/src/radio-group/radioGroup.less b/packages/varlet-vue2-ui/src/radio-group/radioGroup.less new file mode 100644 index 0000000..88ddbbb --- /dev/null +++ b/packages/varlet-vue2-ui/src/radio-group/radioGroup.less @@ -0,0 +1,12 @@ +.var-radio-group { + display: flex; + flex-wrap: wrap; + + &--horizontal { + flex-direction: row; + } + + &--vertical { + flex-direction: column; + } +} diff --git a/packages/varlet-vue2-ui/src/radio/Radio.vue b/packages/varlet-vue2-ui/src/radio/Radio.vue new file mode 100644 index 0000000..cc6e29f --- /dev/null +++ b/packages/varlet-vue2-ui/src/radio/Radio.vue @@ -0,0 +1,189 @@ + + + + + diff --git a/packages/varlet-vue2-ui/src/radio/docs/zh-CN.md b/packages/varlet-vue2-ui/src/radio/docs/zh-CN.md new file mode 100644 index 0000000..47c0e05 --- /dev/null +++ b/packages/varlet-vue2-ui/src/radio/docs/zh-CN.md @@ -0,0 +1,31 @@ +## API + +### 属性 + +| 参数 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| `v-model` | 绑定的值 | _any_ | `false` | +| `checked-value` | 选中状态的值 | _any_ | `true` | +| `unchecked-value` | 未选中状态的值 | _any_ | `false` | +| `checked-color` | 选中状态的颜色 | _any_ | `-` | +| `unchecked-color` | 未选中状态的颜色 | _any_ | `-` | +| `icon-size` | 图标尺寸 | _string |\ number_ | `-` | +| `disabled` | 是否禁用 | _boolean_ | `false` | +| `readonly` | 是否只读 | _boolean_ | `false` | +| `ripple` | 是否开启水波纹 | _boolean_ | `true` | +| `rules` | 验证规则,返回 `true` 表示验证通过,其余的值则转换为文本作为用户提示 | _Array<(value: any) => any>_ | `-` | + +### 事件 + +| 事件名 | 说明 | 参数 | +| --- | --- | --- | +| `click` | 点击时触发 | `e: Event` | +| `change` | 状态变更时触发 | `value: any` | + +### 插槽 + +| 插槽名 | 说明 | 参数 | +| --- | --- | --- | +| `checked-icon` | 选中图标 | `-` | +| `unchecked-icon` | 未选中图标 | `-` | +| `default` | 显示的文本 | `-` | \ No newline at end of file diff --git a/packages/varlet-vue2-ui/src/radio/index.ts b/packages/varlet-vue2-ui/src/radio/index.ts new file mode 100644 index 0000000..428c108 --- /dev/null +++ b/packages/varlet-vue2-ui/src/radio/index.ts @@ -0,0 +1,10 @@ +import type { App } from 'vue' +import Radio from './Radio.vue' + +Radio.install = function (app: App) { + app.component(Radio.name, Radio) +} + +export const _RadioComponent = Radio + +export default Radio diff --git a/packages/varlet-vue2-ui/src/radio/props.ts b/packages/varlet-vue2-ui/src/radio/props.ts new file mode 100644 index 0000000..8cbf6dc --- /dev/null +++ b/packages/varlet-vue2-ui/src/radio/props.ts @@ -0,0 +1,46 @@ +import type { PropType } from 'vue' + +export type ValidateTriggers = 'onChange' + +export const props = { + value: { + type: [String, Number, Boolean, Object, Array] as PropType, + default: false, + }, + checkedValue: { + type: [String, Number, Boolean, Object, Array] as PropType, + default: true, + }, + uncheckedValue: { + type: [String, Number, Boolean, Object, Array] as PropType, + default: false, + }, + disabled: { + type: Boolean, + default: false, + }, + readonly: { + type: Boolean, + default: false, + }, + checkedColor: { + type: String, + }, + uncheckedColor: { + type: String, + }, + iconSize: { + type: [String, Number], + }, + ripple: { + type: Boolean, + default: true, + }, + validateTrigger: { + type: Array as PropType>, + default: () => ['onChange'], + }, + rules: { + type: Array as PropType any>>, + }, +} diff --git a/packages/varlet-vue2-ui/src/radio/radio.less b/packages/varlet-vue2-ui/src/radio/radio.less new file mode 100644 index 0000000..ef81c79 --- /dev/null +++ b/packages/varlet-vue2-ui/src/radio/radio.less @@ -0,0 +1,78 @@ +@radio-checked-color: var(--color-primary); +@radio-unchecked-color: #555; +@radio-disabled-color: var(--color-text-disabled); +@radio-error-color: var(--color-danger); +@radio-icon-size: 24px; +@radio-action-padding: 6px; + +:root { + --radio-checked-color: @radio-checked-color; + --radio-unchecked-color: @radio-unchecked-color; + --radio-disabled-color: @radio-disabled-color; + --radio-error-color: @radio-error-color; + --radio-icon-size: @radio-icon-size; + --radio-action-padding: @radio-action-padding; +} + +@keyframes var-vibrate-animation { + 0% { + opacity: 1; + transform: scale(1); + } + + 50% { + opacity: 0.8; + transform: scale(0.8); + } + + 100% { + opacity: 1; + transform: scale(1); + } +} + +.var-radio { + display: flex; + align-items: center; + transform: translateX(calc(-1 * var(--radio-action-padding))); + cursor: pointer; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + + &__wrap { + display: inline-flex; + flex-direction: column; + } + + &__action { + display: flex; + justify-content: center; + align-items: center; + padding: var(--radio-action-padding); + border-radius: 50%; + } + + &__icon[var-radio-cover] { + display: block; + font-size: var(--radio-icon-size); + } + + &--with-animation[var-radio-cover] { + animation: var-vibrate-animation 0.25s; + } + + &--checked { + color: var(--radio-checked-color); + } + + &--unchecked { + color: var(--radio-unchecked-color); + } + + &--disabled { + color: var(--radio-disabled-color); + } + + &--error { + color: var(--radio-error-color); + } +} diff --git a/packages/varlet-vue2-ui/src/utils/mixins/validation.js b/packages/varlet-vue2-ui/src/utils/mixins/validation.js index c836080..960bfda 100644 --- a/packages/varlet-vue2-ui/src/utils/mixins/validation.js +++ b/packages/varlet-vue2-ui/src/utils/mixins/validation.js @@ -6,7 +6,7 @@ export const ValidationMixin = { }), methods: { - async validateRules(rules, value, apis) { + async _validate(rules, value, apis) { if (!isArray(rules) || !rules.length) { return true } @@ -26,9 +26,9 @@ export const ValidationMixin = { return valid }, - async validateWithTrigger(validateTrigger, trigger, rules, value, apis) { + async _validateWithTrigger(validateTrigger, trigger, rules, value, apis) { if (validateTrigger.includes(trigger)) { - await this.validateRules(rules, value, apis) + await this._validate(rules, value, apis) } },