feat: Establish core application layout, global styles, and theme mode management.
This commit is contained in:
parent
a2ce1d79a2
commit
0e095b35c5
4 changed files with 79 additions and 30 deletions
|
|
@ -1113,3 +1113,33 @@ ul {
|
|||
color: var(--text-secondary);
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
/* =========================
|
||||
Discovery page: Quasar dark-mode overrides (scoped)
|
||||
ไม่กระทบหน้าอื่น เพราะล็อคด้วย .discovery-page
|
||||
========================= */
|
||||
|
||||
/* Dark mode: input background + border */
|
||||
html.dark .discovery-page .input-dark-override .q-field__control {
|
||||
background-color: #0f172a !important; /* slate-900 */
|
||||
border-color: rgba(255, 255, 255, 0.10) !important;
|
||||
}
|
||||
|
||||
/* Dark mode: input text + icons */
|
||||
html.dark .discovery-page .input-dark-override .q-field__native,
|
||||
html.dark .discovery-page .input-dark-override .q-field__input,
|
||||
html.dark .discovery-page .input-dark-override .q-field__prefix,
|
||||
html.dark .discovery-page .input-dark-override .q-field__suffix,
|
||||
html.dark .discovery-page .input-dark-override .q-select__dropdown-icon,
|
||||
html.dark .discovery-page .input-dark-override .q-field__append .q-icon {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
/* Dark mode: placeholder */
|
||||
html.dark .discovery-page .input-dark-override .q-placeholder {
|
||||
color: #94a3b8 !important; /* slate-400 */
|
||||
}
|
||||
|
||||
/* Dark mode: category label ให้ชัดขึ้น (แก้กรณี class ไม่ติด) */
|
||||
html.dark .discovery-page .category-item .q-item__label {
|
||||
color: #e2e8f0 !important; /* slate-200 */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,36 +12,13 @@ const { currentUser, logout } = useAuth()
|
|||
const { t } = useI18n()
|
||||
const $q = useQuasar()
|
||||
|
||||
const isDarkMode = ref(false)
|
||||
// Use centralized theme management
|
||||
const { isDark, set } = useThemeMode()
|
||||
|
||||
const isHydrated = ref(false)
|
||||
|
||||
const applyTheme = (value: boolean) => {
|
||||
// Tailwind dark mode
|
||||
document.documentElement.classList.toggle('dark', value)
|
||||
|
||||
// Quasar dark mode
|
||||
$q.dark.set(value)
|
||||
|
||||
// persist
|
||||
localStorage.setItem('theme', value ? 'dark' : 'light')
|
||||
}
|
||||
|
||||
const toggleDarkMode = (val: boolean) => {
|
||||
isDarkMode.value = val
|
||||
applyTheme(val)
|
||||
}
|
||||
|
||||
// Sync Dark Mode state on mount
|
||||
onMounted(() => {
|
||||
isHydrated.value = true
|
||||
const savedTheme = localStorage.getItem('theme')
|
||||
const preferredDark = window.matchMedia?.('(prefers-color-scheme: dark)')?.matches ?? false
|
||||
|
||||
isDarkMode.value = savedTheme
|
||||
? savedTheme === 'dark'
|
||||
: preferredDark
|
||||
|
||||
applyTheme(isDarkMode.value)
|
||||
})
|
||||
|
||||
// User Initials
|
||||
|
|
@ -88,19 +65,19 @@ const handleLogout = async () => {
|
|||
class="hover:bg-slate-100 dark:hover:bg-white/10 transition-colors"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label class="font-bold text-sm">{{ item.label }}</q-item-label>
|
||||
<q-item-label class="font-bold text-sm text-slate-800 dark:text-white">{{ item.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item class="hover:bg-slate-100 dark:hover:bg-white/10 transition-colors">
|
||||
<q-item-section>
|
||||
<q-item-label class="font-bold text-sm">{{ $t('userMenu.darkMode') }}</q-item-label>
|
||||
<q-item-label class="font-bold text-sm text-slate-800 dark:text-white">{{ $t('userMenu.darkMode') }}</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section side>
|
||||
<q-toggle
|
||||
v-if="isHydrated"
|
||||
:model-value="isDarkMode"
|
||||
@update:model-value="toggleDarkMode"
|
||||
:model-value="isDark"
|
||||
@update:model-value="set"
|
||||
color="blue"
|
||||
keep-color
|
||||
/>
|
||||
|
|
|
|||
39
Frontend-Learner/composables/useThemeMode.ts
Normal file
39
Frontend-Learner/composables/useThemeMode.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import { useQuasar } from 'quasar'
|
||||
|
||||
export const useThemeMode = () => {
|
||||
const $q = useQuasar()
|
||||
|
||||
// deterministic on SSR: default = light
|
||||
const isDark = useState<boolean>('theme:isDark', () => false)
|
||||
|
||||
const applyTheme = (value: boolean) => {
|
||||
if (!process.client) return
|
||||
document.documentElement.classList.toggle('dark', value)
|
||||
$q.dark.set(value)
|
||||
localStorage.setItem('theme', value ? 'dark' : 'light')
|
||||
}
|
||||
|
||||
const initTheme = () => {
|
||||
if (!process.client) return
|
||||
const saved = localStorage.getItem('theme') // 'dark' | 'light' | null
|
||||
const prefersDark = window.matchMedia?.('(prefers-color-scheme: dark)')?.matches ?? false
|
||||
|
||||
const value = saved ? saved === 'dark' : prefersDark
|
||||
isDark.value = value
|
||||
applyTheme(value)
|
||||
}
|
||||
|
||||
onMounted(initTheme)
|
||||
|
||||
watch(isDark, (v) => applyTheme(v))
|
||||
|
||||
const toggle = () => {
|
||||
isDark.value = !isDark.value
|
||||
}
|
||||
|
||||
const set = (v: boolean) => {
|
||||
isDark.value = v
|
||||
}
|
||||
|
||||
return { isDark, toggle, set }
|
||||
}
|
||||
|
|
@ -10,6 +10,9 @@ const leftDrawerOpen = ref(false)
|
|||
const toggleLeftDrawer = () => {
|
||||
leftDrawerOpen.value = !leftDrawerOpen.value
|
||||
}
|
||||
|
||||
// Initialize global theme management
|
||||
useThemeMode()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue