Skip to content

Settings Store

The Settings Store manages application-wide settings and user preferences using Pinia. It provides a comprehensive settings system with support for themes, layouts, and user customization options.

Overview

The settings store (stores/settings.ts) handles all user preference management including theme selection, layout options, and UI customization. Settings are persisted using cookies for SSR compatibility.

Basic Usage

typescript
import { useSettingsStore } from '~/stores/settings'
import { storeToRefs } from 'pinia'

const settingsStore = useSettingsStore()

// Reactive state and computed properties
const {
  settings,
  openDrawer,
  isRtl,
  isDarkMode,
  isMiniLayout,
  canReset
} = storeToRefs(settingsStore)

State Properties

Settings Object

The main settings object contains all user preferences:

typescript
interface Settings {
  colorScheme: 'light' | 'dark'      // Theme selection
  direction: 'ltr' | 'rtl'           // Text direction
  contrast: 'high' | 'normal'        // Contrast level
  primaryColor: string               // Primary color preset
  navLayout: 'vertical' | 'horizontal' | 'mini'  // Navigation layout
  compactLayout: boolean             // Compact spacing
  fontFamily: string                 // Font selection
}

Reactive State

PropertyTypeDescription
settingsSettingsComplete settings object
openDrawerbooleanSettings drawer visibility state

Computed Getters

PropertyTypeDescription
isRtlbooleanWhether RTL (right-to-left) layout is active
isMiniLayoutbooleanWhether mini sidebar layout is active
isHorizontalLayoutbooleanWhether horizontal navigation is active
isVerticalLayoutbooleanWhether vertical navigation is active (default)
isDarkModebooleanWhether dark theme is active
canResetbooleanWhether settings differ from defaults

Actions

updateSettings(newSettings: Partial<Settings>)

Updates multiple settings at once and persists to storage.

typescript
// Update theme and layout
settingsStore.updateSettings({
  colorScheme: 'dark',
  primaryColor: 'cyan',
  navLayout: 'mini',
  compactLayout: true
})

// Update font and contrast
settingsStore.updateSettings({
  fontFamily: 'Inter',
  contrast: 'high'
})

updateField<K extends keyof Settings>(field: K, value: Settings[K])

Updates a single setting field.

typescript
// Change theme
settingsStore.updateField('colorScheme', 'dark')

// Change primary color
settingsStore.updateField('primaryColor', 'purple')

// Toggle layout
settingsStore.updateField('navLayout', 'mini')

resetSettings()

Resets all settings to default values.

typescript
settingsStore.resetSettings()
// All settings revert to defaults

Drawer Actions

typescript
// Open settings drawer
settingsStore.onOpenDrawer()

// Close settings drawer
settingsStore.onCloseDrawer()

Default Settings

typescript
const defaultSettings: Settings = {
  colorScheme: 'light',      // Light theme by default
  direction: 'ltr',          // Left-to-right text direction
  contrast: 'high',          // High contrast for accessibility
  primaryColor: 'default',   // Default green color (#00A76F)
  navLayout: 'vertical',     // Vertical navigation sidebar
  compactLayout: true,       // Compact spacing
  fontFamily: 'Inter'        // Modern font
}

Available Options

Color Schemes

  • 'light' - Light theme (default)
  • 'dark' - Dark theme

Text Directions

  • 'ltr' - Left-to-right (default)
  • 'rtl' - Right-to-left (for RTL languages like Persian/Arabic)

Contrast Levels

  • 'high' - High contrast (default)
  • 'normal' - Normal contrast

Primary Colors

  • 'default' - Green (#00A76F)
  • 'cyan' - Blue (#078DEE)
  • 'purple' - Purple (#7635dc)
  • 'blue' - Blue (#0C68E9)
  • 'orange' - Orange (#fda92d)
  • 'red' - Red (#FF3030)
  • 'vertical' - Vertical sidebar (default)
  • 'horizontal' - Horizontal top navigation
  • 'mini' - Mini collapsed sidebar

Font Families

  • 'Inter' - Modern sans-serif (default)
  • 'Roboto' - Google's font
  • 'Open Sans' - Clean readable font

Reactive Theme Classes

Use computed properties to apply theme classes dynamically:

vue
<template>
  <div :class="themeClasses">
    <nav :class="{ 'mini-sidebar': isMiniLayout }">
      <!-- Navigation content -->
    </nav>

    <main :class="{ 'rtl-layout': isRtl }">
      <!-- Main content -->
    </main>
  </div>
</template>

<script setup>
const settingsStore = useSettingsStore()
const { isMiniLayout, isRtl, isDarkMode, settings } = storeToRefs(settingsStore)

const themeClasses = computed(() => ({
  'dark-theme': isDarkMode.value,
  'rtl-layout': isRtl.value,
  'mini-sidebar': isMiniLayout.value,
  'compact-layout': settings.value.compactLayout,
  'high-contrast': settings.value.contrast === 'high'
}))
</script>

Persistence

Settings are automatically persisted using cookies for SSR compatibility:

typescript
// Settings are saved to cookies automatically
// Available across server and client
// Persist for 1 year by default

Integration with UI Components

Settings Drawer

The settings store integrates with the settings drawer component:

vue
<template>
  <v-navigation-drawer
    v-model="openDrawer"
    temporary
    right
    width="400"
  >
    <SettingsPanel />
  </v-navigation-drawer>
</template>

<script setup>
const settingsStore = useSettingsStore()
const { openDrawer } = storeToRefs(settingsStore)
</script>

Color Picker Component

vue
<template>
  <div class="color-picker">
    <v-btn
      v-for="color in colorPresets"
      :key="color.key"
      :color="color.value"
      @click="settingsStore.updateField('primaryColor', color.key)"
      :variant="settings.primaryColor === color.key ? 'flat' : 'outlined'"
    >
      {{ color.name }}
    </v-btn>
  </div>
</template>

<script setup>
const settingsStore = useSettingsStore()
const { settings } = storeToRefs(settingsStore)

const colorPresets = [
  { name: 'Default', value: '#00A76F', key: 'default' },
  { name: 'Cyan', value: '#078DEE', key: 'cyan' },
  { name: 'Purple', value: '#7635dc', key: 'purple' },
  { name: 'Blue', value: '#0C68E9', key: 'blue' },
  { name: 'Orange', value: '#fda92d', key: 'orange' },
  { name: 'Red', value: '#FF3030', key: 'red' }
]
</script>

Layout Management

Vertical Layout (Default)

vue
<template>
  <div class="vertical-layout">
    <v-navigation-drawer
      :mini="isMiniLayout"
      permanent
    >
      <NavItems />
    </v-navigation-drawer>

    <v-main>
      <AppBar />
      <slot />
    </v-main>
  </div>
</template>

Horizontal Layout

vue
<template>
  <div class="horizontal-layout">
    <v-app-bar>
      <HorizontalNav />
    </v-app-bar>

    <v-main>
      <slot />
    </v-main>
  </div>
</template>

Theme Integration

CSS Custom Properties

Settings integrate with CSS custom properties for dynamic theming:

css
:root {
  --primary-color: #00A76F;
  --font-family: 'Inter', sans-serif;
}

[data-theme="dark"] {
  --primary-color: #078DEE;
}

[data-theme="compact"] {
  --spacing-unit: 0.5rem;
}

Vuetify Theme Integration

typescript
// plugins/vuetify.ts
export default defineNuxtPlugin((nuxtApp) => {
  const settingsStore = useSettingsStore()

  const vuetify = createVuetify({
    theme: {
      defaultTheme: settingsStore.isDarkMode ? 'dark' : 'light'
    }
  })

  nuxtApp.vueApp.use(vuetify)
})

Internationalization Support

Settings store supports RTL layouts for international users:

typescript
// Automatic RTL detection
const isRtl = computed(() => {
  return settingsStore.settings.direction === 'rtl' ||
         $i18n.locale.value === 'fa' // Persian locale
})

Performance Considerations

  • Settings are reactive but updates are batched
  • Cookie persistence is optimized for SSR
  • Computed properties are cached automatically
  • Minimal re-renders on settings changes

Testing Settings

typescript
describe('Settings Store', () => {
  it('should update settings', () => {
    const settingsStore = useSettingsStore()

    // Test theme change
    settingsStore.updateField('colorScheme', 'dark')
    expect(settingsStore.isDarkMode).toBe(true)

    // Test reset
    settingsStore.resetSettings()
    expect(settingsStore.settings.colorScheme).toBe('light')
  })
})

Common Patterns

Theme Toggle

vue
<template>
  <v-btn @click="toggleTheme">
    <v-icon>{{ isDarkMode ? 'mdi-white-balance-sunny' : 'mdi-moon-waning-crescent' }}</v-icon>
    {{ isDarkMode ? 'Light' : 'Dark' }} Mode
  </v-btn>
</template>

<script setup>
const settingsStore = useSettingsStore()
const { isDarkMode } = storeToRefs(settingsStore)

const toggleTheme = () => {
  const newTheme = isDarkMode.value ? 'light' : 'dark'
  settingsStore.updateField('colorScheme', newTheme)
}
</script>

Layout Switcher

vue
<template>
  <v-select
    v-model="settings.navLayout"
    :items="layoutOptions"
    label="Navigation Layout"
    @update:model-value="settingsStore.updateField('navLayout', $event)"
  />
</template>

<script setup>
const settingsStore = useSettingsStore()
const { settings } = storeToRefs(settingsStore)

const layoutOptions = [
  { title: 'Vertical', value: 'vertical' },
  { title: 'Horizontal', value: 'horizontal' },
  { title: 'Mini', value: 'mini' }
]
</script>

Next Steps

Released under the MIT License.