usePersonalization.ts 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import { ref } from 'vue'
  2. export type Theme = 'dark' | 'light' | 'system'
  3. interface PersonalizationOptions {
  4. colorKey: string
  5. defaultColor: string
  6. defaultTheme: Theme
  7. storageThemeKey: string
  8. }
  9. let personalizationInstance: ReturnType<typeof createPersonalization> | null = null
  10. function createPersonalization(options: PersonalizationOptions) {
  11. const { colorKey, defaultColor, defaultTheme, storageThemeKey } = options
  12. const rootElement = window.document.documentElement
  13. const prefersDarkMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
  14. const theme = ref<Theme>((localStorage.getItem(storageThemeKey) as Theme) || defaultTheme)
  15. const color = ref(localStorage.getItem(colorKey) || defaultColor)
  16. const isDark = ref(false)
  17. function setTheme(value: Theme) {
  18. theme.value = value
  19. if (value === 'system') {
  20. localStorage.removeItem(storageThemeKey)
  21. } else if (['dark', 'light'].includes(value)) {
  22. localStorage.setItem(storageThemeKey, theme.value)
  23. }
  24. updateThemeClass()
  25. }
  26. function setColor(value: string) {
  27. color.value = value
  28. localStorage.setItem(colorKey, color.value)
  29. }
  30. function updateThemeClass() {
  31. const isDarkTheme =
  32. localStorage.getItem(storageThemeKey) === 'dark' ||
  33. (!localStorage.getItem(storageThemeKey) && prefersDarkMediaQuery.matches)
  34. isDark.value = isDarkTheme
  35. rootElement.classList.toggle('dark', isDarkTheme)
  36. }
  37. function applyThemeFromStorage() {
  38. const storageTheme = localStorage.getItem(storageThemeKey) as Theme
  39. if (['dark', 'light'].includes(storageTheme)) {
  40. setTheme(storageTheme)
  41. } else {
  42. setTheme('system')
  43. }
  44. }
  45. function destroy() {
  46. prefersDarkMediaQuery.removeEventListener('change', updateThemeClass)
  47. window.removeEventListener('storage', applyThemeFromStorage)
  48. }
  49. updateThemeClass()
  50. prefersDarkMediaQuery.addEventListener('change', updateThemeClass)
  51. window.addEventListener('storage', applyThemeFromStorage)
  52. return {
  53. color,
  54. isDark,
  55. theme,
  56. setColor,
  57. setTheme,
  58. destroy,
  59. }
  60. }
  61. export const usePersonalization = () => {
  62. if (!personalizationInstance) {
  63. personalizationInstance = createPersonalization({
  64. colorKey: 'color',
  65. defaultColor: '#8e51ff',
  66. defaultTheme: 'system',
  67. storageThemeKey: 'theme',
  68. })
  69. }
  70. return personalizationInstance
  71. }