picocrank 1.21.4 → 1.21.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/vue/components/Header.vue +29 -6
- package/vue/composables/useTheme.js +43 -10
package/package.json
CHANGED
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
v-if="themeToggleEnabled"
|
|
28
28
|
type="button"
|
|
29
29
|
class="theme-toggle neutral"
|
|
30
|
-
:aria-label="
|
|
31
|
-
:title="
|
|
30
|
+
:aria-label="themeToggleLabel"
|
|
31
|
+
:title="themeToggleTitle"
|
|
32
32
|
@click="toggleTheme"
|
|
33
33
|
>
|
|
34
|
-
<HugeiconsIcon :icon="
|
|
34
|
+
<HugeiconsIcon :icon="themeToggleIcon" width="1em" height="1em" />
|
|
35
35
|
</button>
|
|
36
36
|
|
|
37
37
|
<slot name = "user-info">
|
|
@@ -46,9 +46,9 @@
|
|
|
46
46
|
</template>
|
|
47
47
|
|
|
48
48
|
<script setup>
|
|
49
|
-
import { ref } from "vue";
|
|
49
|
+
import { computed, ref } from "vue";
|
|
50
50
|
import { HugeiconsIcon } from "@hugeicons/vue";
|
|
51
|
-
import { Menu01Icon, Moon02Icon, Sun01Icon } from "@hugeicons/core-free-icons";
|
|
51
|
+
import { BulbIcon, Menu01Icon, Moon02Icon, Sun01Icon } from "@hugeicons/core-free-icons";
|
|
52
52
|
|
|
53
53
|
import Breadcrumbs from "./Breadcrumbs.vue";
|
|
54
54
|
import TopBar from "./TopBar.vue";
|
|
@@ -56,7 +56,30 @@
|
|
|
56
56
|
import { useTheme } from '../composables/useTheme.js';
|
|
57
57
|
import { useResponsiveNav } from '../composables/useResponsiveNav.js';
|
|
58
58
|
|
|
59
|
-
const {
|
|
59
|
+
const { theme, nextTheme, toggleTheme } = useTheme();
|
|
60
|
+
|
|
61
|
+
const themeToggleMeta = {
|
|
62
|
+
auto: {
|
|
63
|
+
icon: BulbIcon,
|
|
64
|
+
title: 'Auto',
|
|
65
|
+
},
|
|
66
|
+
light: {
|
|
67
|
+
icon: Sun01Icon,
|
|
68
|
+
title: 'Light',
|
|
69
|
+
},
|
|
70
|
+
dark: {
|
|
71
|
+
icon: Moon02Icon,
|
|
72
|
+
title: 'Dark',
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const themeToggleIcon = computed(() => themeToggleMeta[theme.value]?.icon ?? BulbIcon);
|
|
77
|
+
const themeToggleTitle = computed(() => themeToggleMeta[theme.value]?.title ?? 'Auto');
|
|
78
|
+
const themeToggleLabel = computed(() => {
|
|
79
|
+
const current = themeToggleMeta[theme.value]?.title ?? 'Auto';
|
|
80
|
+
const next = themeToggleMeta[nextTheme.value]?.title ?? 'Auto';
|
|
81
|
+
return `Theme ${current.toLowerCase()}. Switch to ${next.toLowerCase()} mode`;
|
|
82
|
+
});
|
|
60
83
|
|
|
61
84
|
const emit = defineEmits(["toggleSidebar", "logoClick"]);
|
|
62
85
|
|
|
@@ -9,15 +9,30 @@ function getSystemTheme() {
|
|
|
9
9
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
function
|
|
12
|
+
function readStoredPreference() {
|
|
13
13
|
if (typeof window === 'undefined') {
|
|
14
|
-
return '
|
|
14
|
+
return 'auto'
|
|
15
15
|
}
|
|
16
16
|
const stored = localStorage.getItem(STORAGE_KEY)
|
|
17
|
-
if (stored === 'light' || stored === 'dark') {
|
|
17
|
+
if (stored === 'light' || stored === 'dark' || stored === 'auto') {
|
|
18
18
|
return stored
|
|
19
19
|
}
|
|
20
|
-
return
|
|
20
|
+
return 'auto'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function resolveTheme(preference) {
|
|
24
|
+
return preference === 'auto' ? getSystemTheme() : preference
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getNextTheme(preference) {
|
|
28
|
+
const system = getSystemTheme()
|
|
29
|
+
if (preference === 'auto') {
|
|
30
|
+
return system === 'light' ? 'dark' : 'light'
|
|
31
|
+
}
|
|
32
|
+
if (preference === 'light') {
|
|
33
|
+
return system === 'light' ? 'auto' : 'dark'
|
|
34
|
+
}
|
|
35
|
+
return system === 'dark' ? 'auto' : 'light'
|
|
21
36
|
}
|
|
22
37
|
|
|
23
38
|
function applyTheme(value) {
|
|
@@ -27,32 +42,50 @@ function applyTheme(value) {
|
|
|
27
42
|
document.documentElement.setAttribute('data-theme', value)
|
|
28
43
|
}
|
|
29
44
|
|
|
30
|
-
export const theme = ref(
|
|
45
|
+
export const theme = ref(readStoredPreference())
|
|
31
46
|
|
|
32
|
-
applyTheme(theme.value)
|
|
47
|
+
applyTheme(resolveTheme(theme.value))
|
|
33
48
|
|
|
34
49
|
watch(theme, (value) => {
|
|
35
|
-
applyTheme(value)
|
|
50
|
+
applyTheme(resolveTheme(value))
|
|
36
51
|
if (typeof localStorage !== 'undefined') {
|
|
37
52
|
localStorage.setItem(STORAGE_KEY, value)
|
|
38
53
|
}
|
|
39
54
|
})
|
|
40
55
|
|
|
56
|
+
if (typeof window !== 'undefined') {
|
|
57
|
+
const media = window.matchMedia('(prefers-color-scheme: dark)')
|
|
58
|
+
const onSystemThemeChange = () => {
|
|
59
|
+
if (theme.value === 'auto') {
|
|
60
|
+
applyTheme(getSystemTheme())
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (typeof media.addEventListener === 'function') {
|
|
64
|
+
media.addEventListener('change', onSystemThemeChange)
|
|
65
|
+
} else if (typeof media.addListener === 'function') {
|
|
66
|
+
media.addListener(onSystemThemeChange)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
41
70
|
export function useTheme() {
|
|
42
|
-
const
|
|
71
|
+
const resolvedTheme = computed(() => resolveTheme(theme.value))
|
|
72
|
+
const nextTheme = computed(() => getNextTheme(theme.value))
|
|
73
|
+
const isDark = computed(() => resolvedTheme.value === 'dark')
|
|
43
74
|
|
|
44
75
|
function toggleTheme() {
|
|
45
|
-
theme.value = theme.value
|
|
76
|
+
theme.value = getNextTheme(theme.value)
|
|
46
77
|
}
|
|
47
78
|
|
|
48
79
|
function setTheme(value) {
|
|
49
|
-
if (value === 'light' || value === 'dark') {
|
|
80
|
+
if (value === 'light' || value === 'dark' || value === 'auto') {
|
|
50
81
|
theme.value = value
|
|
51
82
|
}
|
|
52
83
|
}
|
|
53
84
|
|
|
54
85
|
return {
|
|
55
86
|
theme,
|
|
87
|
+
resolvedTheme,
|
|
88
|
+
nextTheme,
|
|
56
89
|
isDark,
|
|
57
90
|
toggleTheme,
|
|
58
91
|
setTheme,
|