Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
liuyunhe committed May 21, 2024
2 parents d0f051d + 1e3e53e commit 2c459ab
Show file tree
Hide file tree
Showing 12 changed files with 357 additions and 2 deletions.
1 change: 1 addition & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export default defineConfig({
items: [
{ text: 'Button', link: '/components/button' },
{ text: 'Input', link: '/components/input' },
{ text: 'Switch', link: '/components/switch' }
]
}
],
Expand Down
37 changes: 37 additions & 0 deletions docs/components/switch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
title: Switch | Sharp-UI
description: Switch 组件的文档
---

# Switch 开关

表示两种相互对立的状态间的切换,多用于触发「开/关」。

## 基础用法

绑定 v-model 到一个 Boolean 类型的变量。 可以使用 **--s-switch-on-color** 属性与 **--s-switch-off-color** 属性来设置开关的背景色。

<preview path="../demo/Switch/Basic.vue" title="基础Switch" description="Switch 基础用例"></preview>

## 禁用状态

设置 **disabled** 属性,接受一个 boolean,设置true即可禁用。

<preview path="../demo/Switch/Disabled.vue" title="Switch 禁用状态" description="Switch 禁用状态"></preview>

## 不同尺寸

设置 **size** 属性,接受**large / small**,呈现不同的尺寸。

<preview path="../demo/Switch/Size.vue" title="Switch 不同尺寸" description="Switch 不同尺寸"></preview>

## 支持自定义 value 类型

你可以设置 **active-value****inactive-value** 属性, 它们接受 boolean | string | number 类型的值。
<preview path="../demo/Switch/CustomValue.vue" title="支持自定义 value 类型" description="Switch 支持自定义 value 类型"></preview>

## 文字描述

使用 **active-text** 属性与 **inactive-text** 属性来设置开关的文字描述。

<preview path="../demo/Switch/Text.vue" title="支持文字描述" description="Switch 文字描述"></preview>
14 changes: 14 additions & 0 deletions docs/demo/Switch/Basic.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script setup>
import { ref } from 'vue'
import Switch from '@/components/Switch/Switch.vue'
const test = ref(true)
</script>
<template>
<Switch v-model="test" />
</template>
<style scoped>
.s-switch {
--s-switch-on-color:#13ce66;
--s-switch-off-color:#ff4949
}
</style>
9 changes: 9 additions & 0 deletions docs/demo/Switch/CustomValue.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script setup>
import { ref } from 'vue'
import Switch from '@/components/Switch/Switch.vue'
const test = ref('right')
</script>
<template>
<Switch v-model="test" activeValue="right" inactiveValue="wrong"/>
<h4>model-value: {{test}}</h4>
</template>
22 changes: 22 additions & 0 deletions docs/demo/Switch/Disabled.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script setup>
import { ref } from 'vue'
import Switch from '@/components/Switch/Switch.vue'
const test = ref(true)
const test2 = ref(false)
</script>
<template>
<div class="demo-switch-container">
<div class="demo-switch-container-title">正常:</div>
<Switch v-model="test" /> <br/>
</div>
<div class="demo-switch-container">
<div class="demo-switch-container-title">禁用:</div>
<Switch v-model="test2" disabled/>
</div>
</template>
<style scoped>
.demo-switch-container {
display: flex;
align-items: center;
}
</style>
21 changes: 21 additions & 0 deletions docs/demo/Switch/Size.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup>
import { ref } from 'vue'
import Switch from '@/components/Switch/Switch.vue'
const test = ref(false)
</script>
<template>
<div class="switch-size-container">
<Switch v-model="test" size="large"/>
<Switch v-model="test"/>
<Switch v-model="test" size="small"/>
</div>
</template>
<style scoped>
.switch-size-container {
display: flex;
align-items: center;
.s-switch {
margin-right: 10px;
}
}
</style>
8 changes: 8 additions & 0 deletions docs/demo/Switch/Text.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script setup>
import { ref } from 'vue'
import Switch from '@/components/Switch/Switch.vue'
const test = ref(false)
</script>
<template>
<Switch v-model="test" activeText="ON" inactiveText="OFF"/>
</template>
17 changes: 15 additions & 2 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,19 @@
<Button type="success" disabled>成功按钮</Button>
<Button type="info" loading>信息按钮</Button>
</div>
<div style="margin-bottom: 20px;width: 300px;">
<div style="margin-bottom: 20px; width: 300px">
<Input v-model="inputValue" show-password clearable></Input>
</div>
<div style="margin-bottom: 20px">
<Switch
v-model="switchValue"
active-value="1"
inactive-value="0"
active-text="打开"
inactive-text="关闭"
@change="(e) => inlineConsole('switch change', e)"
></Switch>
</div>
<div style="margin-bottom: 20px">
<Collapse v-model="openedValue" accordion>
<CollapseItem name="a">
Expand Down Expand Up @@ -108,6 +118,7 @@ import type { TooltipInstance } from './components/Tooltip/types'
import type { Options as PopperOptions } from '@popperjs/core'
import Dropdown from '@/components/Dropdown/Dropdown'
import Input from '@/components/Input/Input.vue'
import Switch from '@/components/Switch/Switch.vue'
import type { DropdownInstance, MenuOption } from '@/components/Dropdown/types'
// import Message from '@/components/Message/Message.vue'
import { createMessage } from '@/components/Message/methods'
Expand All @@ -124,6 +135,8 @@ const trigger = ref<any>('hover')
const inputValue = ref('hello word!')
const switchValue = ref('1')
const popperOptions: Partial<PopperOptions> = {
placement: 'right-end',
strategy: 'fixed'
Expand Down Expand Up @@ -164,7 +177,7 @@ onMounted(() => {
console.log('🚀 ~ onMounted ~ instance:', instance)
})
createMessage({ type: 'success', message: 'hello word', showClose: true })
createMessage({ type: 'danger', message: 'hello word', showClose: true})
createMessage({ type: 'danger', message: 'hello word', showClose: true })
if (buttonRef.value) {
console.log('🚀 ~ onMounted ~ buttonRef.value:', buttonRef.value)
console.log('🚀 ~ onMounted ~ buttonRef.value.ref:', buttonRef.value.ref)
Expand Down
82 changes: 82 additions & 0 deletions src/components/Switch/Switch.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<template>
<div
class="s-switch"
:class="{
[`s-switch--${size}`]: size,
'is-disabled': disabled,
'is-checked': checked
}"
@click="switchValue"
>
<input
class="s-switch__input"
type="checkbox"
role="switch"
ref="input"
:name="name"
:disabled="disabled"
@keydown.enter="switchValue"
/>
<div class="s-switch__core">
<div class="s-switch__core-inner">
<span v-if="activeText || inactiveText" class="s-switch__core-inner-text">
{{ checked ? activeText : inactiveText }}
</span>
</div>
<div class="s-switch__core-action"></div>
</div>
</div>
</template>

<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue'
import type { SwitchProps, SwitchEmits } from './types'
defineOptions({ name: 's-switch', inheritAttrs: false })
// 定义组件接收的 props
const props = withDefaults(defineProps<SwitchProps>(), {
activeValue: true, // 开启状态的值
inactiveValue: false // 关闭状态的值
})
// 定义组件发出的事件
const emits = defineEmits<SwitchEmits>()
// 创建一个 input 元素的引用
const input = ref<HTMLInputElement>()
// 使用 ref 来跟踪开关的内部值
const innerValue = ref(props.modelValue)
// 计算当前是否被选中
const checked = computed(() => innerValue.value === props.activeValue)
// 切换开关状态的逻辑
const switchValue = () => {
if (props.disabled) return // 如果设置了 disabled,则不进行任何操作
const newValue = checked.value ? props.inactiveValue : props.activeValue
innerValue.value = newValue
emits('update:modelValue', newValue) // 更新父组件绑定的值
emits('change', newValue) // 发出 change 事件
}
// 在组件挂载时,设置 input 元素的 checked 状态
onMounted(() => {
input.value!.checked = checked.value
})
// 监听 checked 的变化,更新 input 元素的 checked 状态
watch(checked, (val) => {
input.value!.checked = val
})
// 监听 props.modelValue 的变化,更新 innerValue 的值
watch(
() => props.modelValue,
(newValue) => {
innerValue.value = newValue
}
)
</script>

<style scoped></style>
126 changes: 126 additions & 0 deletions src/components/Switch/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
.s-switch {
--s-switch-on-color: var(--s-color-primary);
--s-switch-off-color: var(--s-border-color);
--s-switch-on-border-color: var(--s-color-primary);
--s-switch-off-border-color: var(--s-border-color);
}

.s-switch {
display: inline-flex;
align-items: center;
font-size: 14px;
line-height: 20px;
height: 32px;
.s-switch__input {
position: absolute;
width: 0;
height: 0;
opacity: 0;
margin: 0;
&:focus-visible {
& ~ .s-switch__core {
outline: 2px solid var(--s-switch-on-color);
outline-offset: 1px;
}
}
}
&.is-disabled {
opacity: .6;
.s-switch__core {
cursor: not-allowed;
}
}
&.is-checked {
.s-switch__core {
border-color:var(--s-switch-on-border-color);
background-color: var(--s-switch-on-color);
.s-switch__core-action {
left: calc(100% - 17px);
}
.s-switch__core-inner {
padding: 0 18px 0 4px;
}
}
}
}
.s-switch--large {
font-size: 14px;
line-height: 24px;
height: 40px;
.s-switch__core {
min-width: 50px;
height: 24px;
border-radius: 12px;
.s-switch__core-action {
width: 20px;
height: 20px;
}
}
&.is-checked {
.s-switch__core .s-switch__core-action {
left: calc(100% - 21px);
color: var(--s-switch-on-color);
}
}
}
.s-switch--small {
font-size: 12px;
line-height: 16px;
height: 24px;
.s-switch__core {
min-width: 30px;
height: 16px;
border-radius: 8px;
.s-switch__core-action {
width: 12px;
height: 12px;
}
}
&.is-checked {
.s-switch__core .s-switch-core-action {
left: calc(100% - 13px);
color: var(--s-switch-on-color);
}
}
}
.s-switch__core {
display: inline-flex;
align-items: center;
position: relative;
height: 20px;
min-width: 40px;
border: 1px solid var(--s-switch-off-border-color);
outline: none;
border-radius: 10px;
box-sizing: border-box;
background: var(--s-switch-off-color);
cursor: pointer;
transition: border-color var(--s-transition-duration),background-color var(--s-transition-duration);
.s-switch__core-action {
position: absolute;
left: 1px;
border-radius: var(--s-border-radius-circle);
width: 16px;
height: 16px;
background-color: var(--s-color-white);
transition: all var(--s-transition-duration);
}
.s-switch__core-inner {
width: 100%;
transition: all var(--s-transition-duration);
height: 16px;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
padding: 0 4px 0 18px;
.s-switch__core-inner-text {
font-size: 12px;
color: var(--s-color-white);
user-select: none;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
Loading

0 comments on commit 2c459ab

Please sign in to comment.