feat(switch): refresh switch UI (#852)

* feat(switch): 刷新设计规范

Signed-off-by: jacknan <zhumaonan@aliyun.com>

* feat(switch): 拆分特性demo

Signed-off-by: jacknan <zhumaonan@aliyun.com>

* feat(switch): 变量添加注释说明

Signed-off-by: jacknan <zhumaonan@aliyun.com>

* feat(switch): 刷新token定义

Signed-off-by: jacknan <zhumaonan@aliyun.com>

* feat(switch): 修改demo类型格式

Signed-off-by: jacknan <zhumaonan@aliyun.com>

* feat(switch): 优化变量定义

Signed-off-by: jacknan <zhumaonan@aliyun.com>

---------

Signed-off-by: jacknan <zhumaonan@aliyun.com>
This commit is contained in:
jacknan 2023-11-22 15:18:28 +08:00 committed by GitHub
parent 91dd24ec46
commit 4e14521326
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 286 additions and 172 deletions

View File

@ -1,7 +1,7 @@
<template>
<div class="switch-wrap">
<tiny-switch v-model="value" true-value="yes" false-value="no"></tiny-switch>
当前值{{ value }}
<p>默认</p>
<tiny-switch v-model="value1" true-value="1"></tiny-switch>
</div>
</template>
@ -14,7 +14,7 @@ export default {
},
data() {
return {
value: 'no'
value1: '1'
}
}
}

View File

@ -1,15 +1,9 @@
<template>
<div class="switch-wrap">
<div class="page__hd">
<h1 class="page__title">Switch</h1>
<p class="page__desc">Switch开关</p>
</div>
<div class="geot">
<div class="sbody">
<span class="title">禁用</span>
</div>
<tiny-switch v-model="value" disabled></tiny-switch>
</div>
<p>不可用-</p>
<tiny-switch :modelValue="true" disabled></tiny-switch>
<p>不可用-</p>
<tiny-switch :modelValue="false" disabled></tiny-switch>
</div>
</template>
@ -19,49 +13,12 @@ import { Switch } from '@opentiny/vue'
export default {
components: {
TinySwitch: Switch
},
data() {
return {
value: true
}
}
}
</script>
<style scoped>
.page__hd {
padding: 40px;
}
.page__title {
font-weight: 400;
font-size: 21px;
text-align: left;
}
.page__desc {
margin-top: 5px;
color: #888;
font-size: 14px;
text-align: left;
}
.title {
color: #333;
font-size: 16px;
}
.switch-wrap {
padding: 20px;
}
.sbody {
display: flex;
flex-direction: column;
height: 50px;
justify-content: space-around;
}
.geot {
display: flex;
height: 60px;
width: 100%;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #ddd;
}
</style>

View File

@ -1,6 +1,8 @@
<template>
<div class="switch-wrap">
<tiny-switch :value="value" @change="handleChange"></tiny-switch>
<p>默认</p>
<tiny-switch v-model="value1" true-value="1" @change="handleChange"></tiny-switch>
<span class="status">{{ status }}</span>
</div>
</template>
@ -13,13 +15,13 @@ export default {
},
data() {
return {
value: true
value1: '1',
status: '已开启'
}
},
methods: {
handleChange(val) {
let msg = val ? '已开启' : '已关闭'
console.log(msg)
this.status = val ? '已开启' : '已关闭'
}
}
}
@ -29,4 +31,8 @@ export default {
.switch-wrap {
padding: 20px;
}
.status {
margin-left: 20px;
}
</style>

View File

@ -0,0 +1,44 @@
<template>
<div class="switch-wrap">
<p>加载状态</p>
<tiny-switch :modelValue="true" @change="handleChangeLoading1" :loading="loading1"></tiny-switch>
<p>加载状态-小尺寸</p>
<tiny-switch :modelValue="true" @change="handleChangeLoading2" :loading="loading2" mini></tiny-switch>
</div>
</template>
<script lang="jsx">
import { Switch } from '@opentiny/vue'
export default {
components: {
TinySwitch: Switch
},
data() {
return {
loading1: false,
loading2: false
}
},
methods: {
handleChangeLoading1() {
this.loading1 = true
setTimeout(() => {
this.loading1 = false
}, 2000)
},
handleChangeLoading2() {
this.loading2 = true
setTimeout(() => {
this.loading2 = false
}, 2000)
}
}
}
</script>
<style scoped>
.switch-wrap {
padding: 20px;
}
</style>

View File

@ -0,0 +1,22 @@
<template>
<div class="switch-wrap">
<p>小尺寸</p>
<tiny-switch mini></tiny-switch>
</div>
</template>
<script lang="jsx">
import { Switch } from '@opentiny/vue'
export default {
components: {
TinySwitch: Switch
}
}
</script>
<style scoped>
.switch-wrap {
padding: 20px;
}
</style>

View File

@ -1,42 +0,0 @@
<template>
<div>
<tiny-list v-for="item of dataList" :key="item.id">
{{ item.content }}
<template #description>
<span style="color: grey">{{ item.contentdes }}</span>
</template>
<template #suffix>
<tiny-switch v-model="item.value"></tiny-switch>
</template>
</tiny-list>
</div>
</template>
<script lang="jsx">
import { List, Switch } from '@opentiny/vue'
export default {
components: {
TinyList: List,
TinySwitch: Switch
},
data() {
return {
dataList: [
{
id: 1,
content: '主文本',
contentdes: '',
value: true
},
{
id: 2,
content: '主文本',
contentdes: '此处是辅助说明文本',
value: false
}
]
}
}
}
</script>

View File

@ -3,52 +3,64 @@ export default {
owner: '',
demos: [
{
demoId: 'disabled',
demoId: 'base',
name: {
'zh-CN': '是否禁用',
'zh-CN': '基础用法',
'en-US': 'button type'
},
desc: {
'zh-CN': '<p>是否禁用</p>',
'en-US': '<p>button type</p>'
'zh-CN': '<p>基础用法</p>',
'en-US': '<p>base</p>'
},
codeFiles: ['base.vue']
},
{
demoId: 'disabled',
name: {
'zh-CN': '状态不可用',
'en-US': 'disabled'
},
desc: {
'zh-CN': '<p>状态不可用</p>',
'en-US': '<p>disabled</p>'
},
codeFiles: ['disabled.vue']
},
{
demoId: 'switch-event-change',
demoId: 'loading',
name: {
'zh-CN': '值改变事件',
'en-US': 'button round'
'zh-CN': '加载状态',
'en-US': 'loading'
},
desc: {
'zh-CN': '<p>值改变事件</p>',
'en-US': '<p>button round</p>'
'zh-CN': '<p>加载中状态</p>',
'en-US': '<p>loading</p>'
},
codeFiles: ['switch-event-change.vue']
codeFiles: ['loading.vue']
},
{
demoId: 'scenario',
demoId: 'mini',
name: {
'zh-CN': '应用场景',
'en-US': 'events'
'zh-CN': '小尺寸',
'en-US': 'mini'
},
desc: {
'zh-CN': '<p>应用场景</p>',
'en-US': '<p>bbutton click</p>'
'zh-CN': '<p>小尺寸</p>',
'en-US': '<p>mini</p>'
},
codeFiles: ['scenario.vue']
codeFiles: ['mini.vue']
},
{
demoId: 'true-false-value',
demoId: 'event',
name: {
'zh-CN': '自定义打开关闭的值',
'en-US': 'true-false-value'
'zh-CN': '开关状态改变事件',
'en-US': 'switch status event'
},
desc: {
'zh-CN': '<p>自定义打开关闭的值</p>',
'en-US': '<p>bbutton click</p>'
'zh-CN': '<p>开关的状态改变事件</p>',
'en-US': '<p>switch status event</p>'
},
codeFiles: ['true-false-value.vue']
codeFiles: ['event.vue']
}
],
apis: [
@ -58,7 +70,7 @@ export default {
properties: [
{
name: 'disabled',
type: 'Boolean',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '<p>是否禁用该属性默认为false</p>',
@ -68,33 +80,43 @@ export default {
},
{
name: 'false-value',
type: 'String',
type: 'string',
defaultValue: 'false',
desc: {
'zh-CN': '<p>switch 关闭时的值该属性默认为false</p>',
'en-US': 'display different button'
},
demoId: 'true-false-value'
demoId: 'base'
},
{
name: 'true-value',
type: 'String',
type: 'string',
defaultValue: 'true',
desc: {
'zh-CN': '<p>switch 打开时的值该属性默认为true</p>',
'en-US': 'display different button'
},
demoId: 'true-false-value'
demoId: 'base'
},
{
name: 'value',
type: 'String',
name: 'v-model',
type: 'string',
defaultValue: '',
desc: {
'zh-CN': '<p>绑定值</p>',
'en-US': 'display different button'
},
demoId: 'switch-event-change'
demoId: 'base'
},
{
name: 'mini',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '<p>小尺寸</p>',
'en-US': 'mini'
},
demoId: 'mini'
}
],
events: [
@ -106,7 +128,7 @@ export default {
'zh-CN': '<p>switch 状态发生变化时的回调函数,可获取新状态的值</p>',
'en-US': 'Click'
},
demoId: 'switch-event-change'
demoId: 'event'
}
]
}

View File

@ -34,15 +34,17 @@ export const toggle =
export const computedWarpClasses =
({ prefixCls, props, state }: Pick<ISwitchRenderlessParams, 'prefixCls' | 'props' | 'state'>) =>
(): ISwitchClass => [
{
[prefixCls]: true,
[`${prefixCls}-checked`]: state.currentValue === props.trueValue,
[`${prefixCls}-disabled`]: state.disabled,
mini: props.mini,
disabled: state.disabled
}
]
(): ISwitchClass => {
return [
{
[prefixCls]: true,
[`${prefixCls}-checked`]: state.currentValue === props.trueValue,
[`${prefixCls}-disabled`]: state.disabled,
mini: props.mini,
disabled: state.disabled
}
]
}
export const computedInnerClasses =
({ prefixCls }: Pick<ISwitchRenderlessParams, 'prefixCls'>) =>

View File

@ -36,7 +36,7 @@ export const renderless = (
wrapClasses: computed(() => api.computedWarpClasses()),
style: computed(() => api.computedStyle()),
formDisabled: computed(() => (parent.tinyForm || {}).disabled),
disabled: computed(() => props.disabled || state.formDisabled || state.isDisplayOnly),
disabled: computed(() => props.disabled || state.formDisabled || state.isDisplayOnly || props.loading),
isDisplayOnly: computed(() => props.displayOnly || (parent.tinyForm || {}).displayOnly),
showText: computed(() => {
// 用户没传showText属性时aurora默认是展示文本

View File

@ -14,13 +14,25 @@
@import './vars.less';
@switch-prefix-cls: ~'@{css-prefix}mobile-switch';
@btn-size: var(--ti-mobile-switch-btn-size-normal, 18px);
@btn-size-half: calc(@btn-size / 2);
@loading-size: calc(@btn-size - 6px);
@loading-size-half: calc(@loading-size / 2);
@btn-padding: 3px;
@loading-left: calc(calc(@btn-size-half - @loading-size-half) + @btn-padding);
@btn-size-mini: var(--ti-mobile-switch-btn-size-mini, 16px);
@btn-size-half-mini: calc(@btn-size-mini / 2);
@loading-size-mini: calc(@btn-size-mini - 6px);
@loading-size-half-mini: calc(@loading-size-mini / 2);
@btn-padding-mini: 2px;
@loading-left-mini: calc(calc(@btn-size-half-mini - @loading-size-half-mini) + @btn-padding-mini);
.@{switch-prefix-cls} {
width: var(--ti-mobile-switch-width, 50px);
height: var(--ti-mobile-switch-height, 30px);
border: 1px solid var(--ti-mobile-switch-border-color, #e5e5e5);
border-radius: var(--ti-mobile-switch-border-radius, 40px);
background-color: var(--ti-mobile-switch-bgcolor, #fff);
width: var(--ti-mobile-switch-width-normal, 40px);
height: var(--ti-mobile-switch-height-mormal, 24px);
border-radius: 999px;
background-color: var(--ti-mobile-switch-bg-color-default, #c2c2c2);
position: relative;
cursor: pointer;
display: inline-block;
@ -28,36 +40,38 @@
outline: none;
transition: all 0.2s ease-in-out;
&.mini {
width: var(--ti-mobile-switch-width-small, 36px);
height: var(--ti-mobile-switch-height-small, 20px);
}
&.disabled {
background-color: var(--ti-mobile-switch-disabled-bgcolor, #f5f5f5);
background-color: var(--ti-mobile-switch-bg-color-disabled, #dbdbdb);
cursor: not-allowed;
&::after {
border-color: var(--ti-mobile-switch-after-border-color, #ccc);
&:after {
cursor: not-allowed;
}
&.@{switch-prefix-cls}-checked {
background-color: var(--ti-mobile-switch-disabled-checked-bgcolor, #b3eee0);
border-color: var(--ti-mobile-switch-disabled-checked-bgcolor, #b3eee0);
background-color: var(--ti-mobile-switch-bg-color-checked-disabled, #b3d6ff);
&::after {
border-color: rgba(0, 0, 0, 0.1);
&:after {
border-color: rgb(0 0 0 / 10%);
}
}
}
&::after {
&:after {
content: '';
width: 28px;
height: 28px;
width: @btn-size;
height: @btn-size;
border-radius: 50%;
background-color: var(--ti-mobile-switch-font-color, #fff);
box-shadow: 0 3px 3px rgba(0, 0, 0, 0.1);
border: 1px solid var(--ti-mobile-switch-after-border-color, #ccc);
box-shadow: 0 3px 3px rgb(0 0 0 / 10%);
position: absolute;
left: -1px;
top: 0;
left: @btn-padding;
top: calc(calc(var(--ti-mobile-switch-height-mormal) - @btn-size) / 2);
cursor: pointer;
transition:
left 0.2s ease-in-out,
@ -65,13 +79,84 @@
box-sizing: border-box;
}
&.mini:after {
width: @btn-size-mini;
height: @btn-size-mini;
top: calc(calc(var(--ti-mobile-switch-height-mini, 20px) - @btn-size-mini) / 2);
left: @btn-padding-mini;
}
&&-checked {
border-color: var(--ti-mobile-switch-checked-color, #00c696);
background-color: var(--ti-mobile-switch-checked-color, #00c696);
background-color: var(--ti-mobile-switch-bg-color-checked, #1476ff);
&:after {
left: calc(~'100% - 28px');
border-color: var(--ti-mobile-switch-bgcolor, #fff);
left: calc(calc(100% - @btn-size) - @btn-padding);
}
}
&&-checked.mini:after {
left: calc(calc(100% - @btn-size-mini) - @btn-padding-mini);
}
&.mini.checked {
&:after {
left: calc(calc(100% - @btn-size-mini) - @btn-padding-mini);
}
}
&-loading {
width: @loading-size;
height: @loading-size;
position: absolute;
z-index: 2;
top: calc(50% - @loading-size-half);
left: @loading-left;
border-radius: 50%;
background-image: conic-gradient(#dbdbdb01, #dbdbdb);
animation: circle 1.5s linear infinite;
display: inline-block;
&-inner {
width: @btn-size-half;
height: @btn-size-half;
border-radius: 50%;
background-color: #fff;
position: absolute;
top: 2px;
left: 2px;
}
}
&.mini &-loading {
width: @loading-size-mini;
height: @loading-size-mini;
top: calc(50% - @loading-size-half-mini);
left: @loading-left-mini;
&-inner {
width: @btn-size-half-mini;
height: @btn-size-half-mini;
}
}
&&-checked &-loading {
left: calc(calc(@loading-left + 50%) - @btn-padding);
background-image: conic-gradient(#b3d6ff01, #b3d6ff);
}
&&-checked.mini &-loading {
left: calc(calc(@loading-left-mini + 50%) - @btn-padding-mini);
}
@keyframes circle {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
}

View File

@ -1,12 +1,22 @@
:root {
--ti-mobile-switch-width: 50px;
--ti-mobile-switch-height: 30px;
--ti-mobile-switch-border-radius: 40px;
--ti-mobile-switch-border-color: #e5e5e5;
--ti-mobile-switch-bgcolor: var(--ti-mobile-base-color-light, #fff);
--ti-mobile-switch-disabled-bgcolor: var(--ti-mobile-base-color-selected-background, #f5f5f5);
--ti-mobile-switch-disabled-after-border-color: #ccc;
--ti-mobile-switch-disabled-checked-bgcolor: #b3eee0;
--ti-mobile-switch-after-border-color: #ccc;
--ti-mobile-switch-checked-color: #00c696;
// 默认尺寸宽度
--ti-mobile-switch-width-normal: calc(var(--ti-mobile-base-size-width-small) + 4px);
// 默认尺寸高度
--ti-mobile-switch-height-mormal: var(--ti-mobile-base-size-height-mini, 24px);
// 小尺寸宽度
--ti-mobile-switch-width-mini: var(--ti-mobile-base-size-width-small, 36px);
// 小尺寸高度
--ti-mobile-switch-height-mini: calc(var(--ti-mobile-base-size-height-mini) - 4px);
// 默认尺寸按钮大小
--ti-mobile-switch-btn-size-normal: calc(var(--ti-mobile-base-size-width-min) - 6px);
// 小尺寸按钮大小
--ti-mobile-switch-btn-size-mini: calc(var(--ti-mobile-base-size-width-min) - 8px);
// 默认关状态背景色
--ti-mobile-switch-bg-color-default: var(--ti-mobile-color-bg-control-default, #c2c2c2);
// 禁用状态下背景色
--ti-mobile-switch-bg-color-disabled: var(--ti-mobile-color-bg-control-disable-1, #dbdbdb);
// 开状态禁用背景色
--ti-mobile-switch-bg-color-checked-disabled: var(--ti-mobile-color-bg-control-disable-2, #b3d6ff);
// 开状态背景色
--ti-mobile-switch-bg-color-checked: var(--ti-mobile-color-bg-control-active, #1476ff);
}

View File

@ -65,6 +65,10 @@ export const switchProps = {
displayOnly: {
type: Boolean,
default: false
},
loading: {
type: Boolean,
default: false
}
}

View File

@ -10,7 +10,11 @@
*
-->
<template>
<span :class="state.wrapClasses" :disabled="true" tabindex="0" @click="toggle" @keydown.space="toggle"> </span>
<span :class="state.wrapClasses" :disabled="disabled" tabindex="0" @click="toggle" @keydown.space="toggle">
<div v-if="loading" class="tiny-mobile-switch-loading">
<div class="tiny-mobile-switch-loading-inner"></div>
</div>
</span>
</template>
<script lang="ts">
@ -19,7 +23,7 @@ import { props, setup, defineComponent } from '@opentiny/vue-common'
import '@opentiny/vue-theme-mobile/switch/index.less'
export default defineComponent({
props: [...props, 'modelValue', 'trueValue', 'falseValue', 'disabled'],
props: [...props, 'modelValue', 'trueValue', 'falseValue', 'disabled', 'loading', 'mini'],
setup(props, context) {
return setup({ props, context, renderless, api })
}