Skip to content

Commit cc7eb32

Browse files
author
Tenny
committed
feat(Theme): 新增主题模式切换
1 parent 6af09df commit cc7eb32

File tree

11 files changed

+166
-17
lines changed

11 files changed

+166
-17
lines changed

docs/.vitepress/theme/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ import '../../../style/md-input';
4343
import '../../../style/virtual-list';
4444
import '../../../style/theme-button';
4545
import '../../../style/theme-select';
46+
import '../../../style/theme-switch';
47+
import '../../../style/theme-radio';
4648

4749
export default {
4850
extends: DefaultTheme,

docs/components/theme.md

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,105 @@ createApp(App).use(router).mount('#app');
2020
:::
2121

2222
<script setup>
23-
import { ThemeButton, ThemeSelect } from '../../src'
23+
import {
24+
ThemeButton,
25+
ThemeSelect,
26+
ThemeSwitch,
27+
ThemeRadio,
28+
SunIcon,
29+
MoonIcon,
30+
ThemeDefaultIcon,
31+
Radio,
32+
RadioGroup
33+
} from '../../src';
34+
import { ref, watch } from 'vue';
35+
import { getTheme, applyTheme } from 'ph-utils/theme'
36+
37+
const theme = ref(getTheme());
38+
39+
watch(theme, (val) => {
40+
applyTheme(val).then();
41+
});
2442
</script>
2543

2644
### 基本使用
2745

2846
提供多种风格的主题切换按钮: 按钮、下拉选择、单选按钮组、开关
2947

48+
<ClientOnly>
49+
<CodePreview>
50+
<textarea lang="vue-html" v-pre>
51+
<nt-theme-button></nt-theme-button>
52+
<nt-theme-select class="ml-10"></nt-theme-select>
53+
<nt-theme-switch class="ml-10"></nt-theme-switch>
54+
<nt-theme-radio class="mt-15"></nt-theme-radio>
55+
</textarea>
56+
<template #preview>
57+
<ThemeButton></ThemeButton>
58+
<ThemeSelect class="ml-10"></ThemeSelect>
59+
<ThemeSwitch class="ml-10"></ThemeSwitch>
60+
<ThemeRadio class="mt-15"></ThemeRadio>
61+
</template>
62+
</CodePreview>
63+
</ClientOnly>
64+
65+
> 切换主题后,如果要让应用启动的时候也应用上一次切换的主题, 需要在应用开始的地方调用 `initTheme()` 函数
66+
67+
```js
68+
// main.js
69+
import { initTheme } from 'ph-utils/theme';
70+
71+
// await initTheme();
72+
initTheme().then();
73+
```
74+
75+
### 自定义风格
76+
77+
框架内置的主题切换其实就是一个排版的样式,核心的主题切换的逻辑是调用的 `ph-utils` 库的 `theme` 模块相关函数来实现的。
78+
79+
通常调用的模块有: `getTheme()``applyTheme()`
80+
81+
所以完全可以自定义按钮来实现主题切换, 下面就使用 `RadioGroup + Icon` 来实现。
82+
3083
<ClientOnly>
3184
<CodePreview>
3285
<textarea lang="vue" v-pre>
3386
<script setup lang="ts">
87+
import { ref, watch } from 'vue';
88+
import { getTheme, applyTheme } from 'ph-utils/theme';
89+
// 获取当前主题
90+
const theme = ref(getTheme());
91+
// 主题改变时, 应用主题
92+
watch(theme, (val) => {
93+
applyTheme(val).then();
94+
});
3495
</script>
3596
<template>
36-
<hr />
97+
<nt-radio-group v-model="theme">
98+
<nt-radio value="auto" type="button">
99+
<nt-theme-default-icon></nt-theme-default-icon>
100+
</nt-radio>
101+
<nt-radio value="light" type="button">
102+
<nt-sun-icon></nt-sun-icon>
103+
</nt-radio>
104+
<nt-radio value="dark" type="button">
105+
<nt-moon-icon></nt-moon-icon>
106+
</nt-radio>
107+
</nt-radio-group>
37108
</template>
38109
</textarea>
39110
<template #preview>
40-
<ThemeButton></ThemeButton>
41-
<ThemeSelect class="ml-10"></ThemeSelect>
111+
<RadioGroup v-model="theme">
112+
<Radio value="auto" type="button">
113+
<ThemeDefaultIcon></ThemeDefaultIcon>
114+
</Radio>
115+
<Radio value="light" type="button">
116+
<SunIcon></SunIcon>
117+
</Radio>
118+
<Radio value="dark" type="button">
119+
<MoonIcon></MoonIcon>
120+
</Radio>
121+
</RadioGroup>
42122
</template>
43123
</CodePreview>
44124
</ClientOnly>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<template>
2+
<RadioGroup v-model="theme">
3+
<Radio label="跟随系统" value="auto" type="button" />
4+
<Radio label="浅色主题" value="light" type="button" />
5+
<Radio label="深色主题" value="dark" type="button" />
6+
</RadioGroup>
7+
</template>
8+
9+
<script setup lang="ts">
10+
import { ref, watch } from 'vue';
11+
import Radio from '../radio/Radio.vue';
12+
import RadioGroup from '../radio/RadioGroup.vue';
13+
import { applyTheme, getTheme } from 'ph-utils/theme';
14+
15+
const theme = ref(getTheme());
16+
17+
watch(theme, (val) => {
18+
applyTheme(val as 'auto').then();
19+
});
20+
</script>
21+
22+
<style lang="less"></style>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<template>
2+
<Switch v-model="isDark" class="nt-theme-switch">
3+
<template #action="slotProp">
4+
<MoonIcon v-if="slotProp.checked"></MoonIcon>
5+
<SunIcon v-else></SunIcon>
6+
</template>
7+
</Switch>
8+
</template>
9+
10+
<script setup lang="ts">
11+
import { ref, watch } from 'vue';
12+
import Switch from '../Switch.vue';
13+
import MoonIcon from '../icon/Moon.vue';
14+
import SunIcon from '../icon/Sun.vue';
15+
import { getTheme, applyTheme } from 'ph-utils/theme';
16+
17+
const isDark = ref(getTheme() === 'dark');
18+
19+
watch(isDark, (val) => {
20+
applyTheme(val ? 'dark' : 'auto').then();
21+
});
22+
</script>

src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import exp from 'constants';
2+
13
export type { ColumnOption } from './components/table/Table.vue';
24

35
export { default as BaseIcon } from './components/icon/Base.vue';
@@ -24,6 +26,7 @@ export { default as DArrowLeft } from './components/icon/DArrowLeft.vue';
2426
export { default as DArrowRight } from './components/icon/DArrowRight.vue';
2527
export { default as SunIcon } from './components/icon/Sun.vue';
2628
export { default as MoonIcon } from './components/icon/Moon.vue';
29+
export { default as ThemeDefaultIcon } from './components/icon/ThemeDefault.vue';
2730

2831
export { default as Input } from './components/input/Input.vue';
2932
export { default as MdInput } from './components/input/MdInput.vue';
@@ -75,3 +78,5 @@ export { default as VirtualList } from './components/VirtualList.vue';
7578

7679
export { default as ThemeButton } from './components/theme/ThemeButton.vue';
7780
export { default as ThemeSelect } from './components/theme/ThemeSelect.vue';
81+
export { default as ThemeSwitch } from './components/theme/ThemeSwitch.vue';
82+
export { default as ThemeRadio } from './components/theme/ThemeRadio.vue';

style/radio/index.css

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
}
1313

1414
.nt-radio__label {
15-
color: var(--nt-radio-label-color);
1615
transition: color 0.3s ease-in-out;
1716
margin-left: 5px;
1817
}
@@ -98,9 +97,7 @@
9897
.is-checked.nt-radio--button .nt-radio__label {
9998
background-color: var(--nt-radio-checked-color);
10099
color: #fff;
101-
border-top-color: var(--nt-radio-checked-color);
102-
border-bottom-color: var(--nt-radio-checked-color);
103-
border-right-color: #fff;
100+
border-color: var(--nt-radio-checked-color);
104101
}
105102
.nt-radio-group .nt-radio--button:not(:first-child) {
106103
margin-left: 0;

style/switch/index.css

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,40 @@
11
.nt-switch {
22
--nt-switch-height: 22px;
3-
--nt-switch-width: calc(var(--nt-switch-height) * 2);
3+
--nt-switch-width: 40px;
44
display: inline-flex;
55
width: var(--nt-switch-width);
66
height: var(--nt-switch-height);
77
align-items: center;
88
justify-content: flex-start;
9-
background-color: #d9d9d9;
9+
background-color: #dcdfe6;
1010
border-radius: calc(var(--nt-switch-height) / 2);
1111
padding: 2px;
1212
position: relative;
1313
cursor: pointer;
14-
transition: background-color 0.3s ease-in-out;
14+
transition: all 0.3s ease-in-out;
1515
vertical-align: middle;
16+
border: 1px solid #c2c2c2;
17+
}
18+
19+
.nt-switch:hover,
20+
.nt-switch.nt-switch--checked:hover {
21+
border-color: var(--nt-primary-color, #722ed1);
1622
}
1723

1824
.nt-switch--checked {
19-
background-color: var(--nt-primary-color, #52c41a);
25+
background-color: var(--nt-primary-color, #722ed1);
26+
border-color: var(--nt-primary-color, #722ed1);
2027
}
2128

2229
.nt-switch-action {
2330
position: absolute;
24-
width: calc(var(--nt-switch-height) - 4px);
31+
width: calc(var(--nt-switch-height) - 6px);
2532
top: 2px;
2633
left: 2px;
2734
bottom: 2px;
28-
background-color: white;
35+
background-color: #ffffff;
2936
border-radius: 50%;
30-
transition: left 0.3s ease-in-out;
37+
transition: all 0.3s ease-in-out;
3138
display: flex;
3239
justify-content: center;
3340
align-items: center;
@@ -37,12 +44,12 @@
3744

3845
.nt-switch-text {
3946
color: #fff;
40-
padding-left: calc(var(--nt-switch-width) - 18px);
47+
padding-left: calc(var(--nt-switch-width) - 20px);
4148
transition: padding-left 0.3s ease-in-out;
4249
}
4350

4451
.nt-switch--checked .nt-switch-action {
45-
left: calc(var(--nt-switch-width) - var(--nt-switch-height) + 2px);
52+
left: calc(var(--nt-switch-width) - var(--nt-switch-height) + 3px);
4653
}
4754
.nt-switch--checked .nt-switch-text {
4855
padding-left: 5px;

style/theme-radio/index.css

Whitespace-only changes.

style/theme-radio/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import '../radio/index.css';
2+
import './index.css';

style/theme-switch/index.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.nt-theme-switch.nt-switch--checked {
2+
background-color: rgba(101, 117, 113, 0.16);
3+
border-color: #3c3f44;
4+
}
5+
6+
.nt-theme-switch.nt-switch--checked .nt-switch-action {
7+
background-color: #000000;
8+
color: #ffffff;
9+
}

0 commit comments

Comments
 (0)