@jaiswald159/shared-settings

v2.0.4

Published

A lightweight shared settings library using [Pinia](https://pinia.vuejs.org/) for state management, built for Vue 3 / Nuxt 3 applications. Provides a centralized store to manage application settings such as language preferences, themes, and site-specific

Readme

@jaiswald159/shared-settings

A lightweight shared settings library using Pinia for state management, built for Vue 3 / Nuxt 3 applications. Provides a centralized store to manage application settings such as language preferences, themes, and site-specific configurations.


šŸ“¦ Installation

Install the package using npm:

npm install @jaiswald159/shared-settings

šŸš€ Basic Usage

Import and use the store in your Vue or Nuxt application:

import { useSettingsStore } from '@jaiswald159/shared-settings';

// Example of a dummy app store that provides application data
const myAppStore = () => ({
  getApplicationData: (key: string) => {
    if (key === 'loggedSiteID') {
      return 'site-abc-123'; // Replace with dynamic site ID
    }
    return null;
  }
});

// Initialize the settings store
const settingsStore = useSettingsStore(myAppStore);

// Update settings
settingsStore.UPDATE_SETTINGS({ 
  siteID: 'site-abc-123', 
  theme: 'dark', 
  notifications: true 
});

// Get settings for the current site
const currentSettings = settingsStore.GET_SETTINGS();

šŸŽÆ Complete Example: Nuxt 3 Implementation

Here's a complete example of how to use the library in a Nuxt 3 application:

1. Setup Dependencies

{
  "dependencies": {
    "@jaiswald159/shared-settings": "^1.0.2",
    "@pinia/nuxt": "^0.11.2",
    "pinia": "^2.1.7",
    "pinia-plugin-persistedstate": "^3.2.1"
  }
}

2. Nuxt Configuration (nuxt.config.ts)

export default defineNuxtConfig({
  modules: ['@pinia/nuxt'],
  pinia: {
    autoImports: ['defineStore', 'storeToRefs']
  }
})

3. Pinia Plugin (plugins/pinia.client.ts)

import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

export default defineNuxtPlugin((nuxtApp) => {
  const pinia = nuxtApp.$pinia
  if (pinia) {
    pinia.use(piniaPluginPersistedstate)
  }
})

4. App Store Helper

// composables/useAppStore.ts
export const useAppStore = () => ({
  getApplicationData: (key: string) => {
    if (key === 'loggedSiteID') return 'SITE123'
    return null
  }
})

5. Main Component (pages/index.vue)

<template>
  <div>
    <h2>Settings Management</h2>
    <div v-if="error" class="error">
      <p>Error: {{ error }}</p>
      <button @click="retry">Retry</button>
    </div>
    <div v-else>
      <pre>{{ currentSettings }}</pre>
      <button @click="saveSettings">Save Settings</button>
      <button @click="loadSettings">Load Settings</button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, ref, onMounted } from 'vue'
import { useSettingsStore } from '@jaiswald159/shared-settings'

// App store helper
const useAppStore = () => ({
  getApplicationData: (key: string) => {
    if (key === 'loggedSiteID') return 'SITE123'
    return null
  }
})

const error = ref<string | null>(null)
const store = ref<any>(null)

const retry = async () => {
  error.value = null
  await initializeStore()
}

const initializeStore = async () => {
  try {
    if (process.client) {
      // Wait for the app to be fully ready
      await new Promise(resolve => {
        if (document.readyState === 'complete') {
          resolve(undefined)
        } else {
          window.addEventListener('load', resolve)
        }
      })
  
      // Initialize the settings store
      store.value = useSettingsStore(useAppStore)
    }
  } catch (err) {
    console.error('Failed to load shared-settings:', err)
    error.value = 'Failed to load settings library.'
  }
}

const currentSettings = computed(() => {
  if (!store.value) return 'Loading...'
  try {
    return store.value.GET_SETTINGS()
  } catch (err) {
    console.error('Error getting settings:', err)
    return 'Error loading settings'
  }
})

const saveSettings = async () => {
  if (!store.value) {
    error.value = 'Store not initialized'
    return
  }
  
  try {
    store.value.UPDATE_SETTINGS({
      siteID: 'SITE123',
      language: 'fr',
      darkMode: true,
      theme: 'dark',
      notifications: true
    })
    console.log('Settings saved successfully!')
  } catch (err) {
    console.error('Error saving settings:', err)
    error.value = 'Failed to save settings'
  }
}

const loadSettings = () => {
  if (!store.value) {
    error.value = 'Store not initialized'
    return
  }
  
  try {
    const settings = store.value.GET_SETTINGS()
    console.log('Current settings:', settings)
  } catch (err) {
    console.error('Error loading settings:', err)
    error.value = 'Failed to load settings'
  }
}

onMounted(() => {
  initializeStore()
})
</script>

<style scoped>
.error {
  color: red;
  padding: 1rem;
  border: 1px solid red;
  border-radius: 4px;
  margin: 1rem 0;
}

button {
  padding: 0.5rem 1rem;
  margin: 0.5rem;
  border: 1px solid #ccc;
  border-radius: 4px;
  background: #f0f0f0;
  cursor: pointer;
}

button:hover {
  background: #e0e0e0;
}

pre {
  background: #f5f5f5;
  padding: 1rem;
  border-radius: 4px;
  overflow-x: auto;
}
</style>

6. Advanced Usage with Vue Persistence

For enhanced Vue integration, you can also create a custom implementation using @vueuse/core:

// composables/useCustomSettingsStore.ts
import { ref, computed } from 'vue'
import { useStorage } from '@vueuse/core'

interface Setting {
  siteID: string
  [key: string]: any
}

interface AppStore {
  getApplicationData: (key: string) => any
}

export const useCustomSettingsStore = (useStore?: () => AppStore) => {
  const siteID = computed(() => (useStore ? useStore().getApplicationData('loggedSiteID') : null))
  
  // Use Vue's persistence state instead of localStorage
  const storage = typeof window !== 'undefined' ? localStorage : undefined
  const settings = useStorage<Setting[]>('activityAttendanceSettings', [], storage)

  const UPDATE_SETTINGS = (data: Setting) => {
    if (!data.siteID) throw new Error('siteID is required')
  
    if (settings.value.length > 0) {
      const index = settings.value.findIndex((item: Setting) => item.siteID === data.siteID)
      if (index !== -1) {
        settings.value[index] = data
      } else {
        settings.value.push(data)
      }
    } else {
      settings.value.push(data)
    }
  }

  const GET_SETTINGS = () => {
    const findSetting = settings.value.find((item: Setting) => item.siteID === siteID.value)
    return findSetting || false
  }

  return {
    settings,
    UPDATE_SETTINGS,
    GET_SETTINGS,
  }
}

šŸ“ File Structure

shared-settings/
ā”œā”€ā”€ dist/                  # Compiled output files
ā”œā”€ā”€ useSettingsStore.ts    # Pinia store definition for shared settings
ā”œā”€ā”€ vite.config.ts         # Vite build configuration
ā”œā”€ā”€ tsconfig.json          # TypeScript configuration
ā”œā”€ā”€ index.ts               # Entry file
ā”œā”€ā”€ package.json           # NPM metadata
└── README.md              # Documentation file

āš™ļø package.json (CSS integration)

Below is the current package.json from this repository and a short explanation of how CSS is included and published with the package.

{
  "name": "@jaiswald159/shared-settings",
  "version": "2.0.1",
  "main": "dist/index.js",
  "type": "module",
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "require": "./dist/index.cjs",
      "style": "./dist/global.css"
    },
    "./dist/global.css": "./dist/global.css",
    "./global.css": "./global.css"
  },
  "scripts": {
    "build": "vite build",
    "dev": "vite",
    "postbuild": "powershell -Command \"if (Test-Path dist) { Copy-Item -Path global.css -Destination dist -Force }\""
  },
  "files": [
    "dist",
    "global.css"
  ],
  "style": "global.css",
  "dependencies": {
    "pinia": "^2.1.7",
    "pinia-plugin-persistedstate": "^3.2.0",
    "vue": "^3.3.4"
  }
}

Why this matters and how CSS is published:

  • exports["."].style and the top-level style field point to the published CSS so bundlers that respect the style field can auto-detect the package stylesheet.
  • The postbuild script ensures global.css is copied into dist/ after vite build so the published package contains the CSS alongside compiled JS.
  • The files array explicitly includes dist and global.css in the package tarball.

How to consume the CSS in host apps

  • Automatic (if your bundler supports style): importing the package can make bundlers pick up the style field. Example: import '@jaiswald159/shared-settings' (behavior depends on the bundler).
  • Manual import (recommended and explicit): in your application's entry point (Nuxt/Vite/webpack) import the stylesheet directly:

Nuxt 3 (nuxt.config.ts):

export default defineNuxtConfig({
  css: ['@jaiswald159/shared-settings/dist/global.css']
})

Vite / Vue main (main.ts):

import '@jaiswald159/shared-settings/dist/global.css'
import { createApp } from 'vue'
import App from './App.vue'
// ...

Simple theme toggle helper (use with the settings store)

// utils/applyThemeFromStore.ts
export function applyThemeFromSettings(store: any) {
  const settings = store.GET_SETTINGS?.() || {}
  // prefer an explicit theme string, fall back to darkMode boolean
  const theme = settings.theme ?? (settings.darkMode ? 'dark' : 'light')
  if (theme === 'dark') {
    document.documentElement.setAttribute('data-theme', 'dark')
  } else {
    document.documentElement.removeAttribute('data-theme')
  }
}

Usage example in a component (after store init):

import { useSettingsStore } from '@jaiswald159/shared-settings'
import { applyThemeFromSettings } from './utils/applyThemeFromStore'

const store = useSettingsStore(appStore)
applyThemeFromSettings(store)

// Optionally watch for changes and reapply
// watch(() => store.GET_SETTINGS(), () => applyThemeFromSettings(store))

Notes & compatibility

  • The style field is widely respected by many bundlers but not an official Node resolution field — for the most predictable result import the stylesheet explicitly as shown above.
  • The postbuild script uses PowerShell (Windows). If you publish from Unix-based CI, replace it with a portable copy command (or use an npm postbuild JS script) to ensure cross-platform builds.

🧱 Features

  • āœ… Based on Pinia state management
  • āœ… Manages site-specific settings with siteID
  • āœ… Automatic persistence with pinia-plugin-persistedstate
  • āœ… Reusable across multiple Vue/Nuxt projects
  • āœ… Lightweight and modular
  • āœ… Published as a global NPM package
  • āœ… TypeScript support
  • āœ… Vue 3 / Nuxt 3 compatibility

šŸ“š API Reference

Settings Interface

interface Setting {
  siteID: string;
  [key: string]: any;
}

App Store Interface

interface AppStore {
  getApplicationData: (key: string) => any;
}

Store Methods

  • UPDATE_SETTINGS(data: Setting): Save settings with persistence
  • GET_SETTINGS(): Retrieve settings for current site
  • settings: Reactive settings array

Example CSS (Theme integration)

/* Theme variables used by the host app to sync with settings */
:root {
  --color-bg: #ffffff;
  --color-text: #111827;
  --color-accent: #0ea5e9;
}

[data-theme="dark"] {
  --color-bg: #0b1220;
  --color-text: #e6edf3;
  --color-accent: #60a5fa;
}

/* Basic layout utilities that read the theme variables */
.app-root {
  background: var(--color-bg);
  color: var(--color-text);
  transition: background .2s ease, color .2s ease;
}

.btn {
  background: var(--color-accent);
  color: #fff;
  border-radius: 6px;
  padding: .5rem 1rem;
}

You can apply the active theme from the store by toggling a data-theme attribute on the document element based on the store value (for example, darkMode or theme).


šŸ” Troubleshooting

Common Issues

  1. Pinia not initialized

    • Ensure @pinia/nuxt is in modules array
    • Check that Pinia is properly configured
  2. Persistence not working

    • Verify pinia-plugin-persistedstate is installed
    • Check browser console for errors
    • Ensure client-side execution
  3. TypeScript errors

    • Check TypeScript configuration
    • Verify all dependencies are installed
    • Restart development server

Debug Steps

  1. Check console logs for error messages
  2. Verify localStorage in browser dev tools
  3. Check network tab for failed requests
  4. Restart development server if needed

šŸ“¤ Build & Publish Guide

  1. Build the package

    npm run build
  2. Update the version

    npm version patch   # Or use minor / major as needed
  3. Publish to NPM

    npm publish --access public

āœ… Ensure publishConfig.access is set to public in package.json for scoped packages.


🌐 Usage in Multiple Vue/Nuxt Apps

Once published, this package can be installed in any Vue 3 or Nuxt 3 project and used as a shared module for managing settings across multiple applications or deployments.


šŸ› ļø Notes

  • Ensure you have logged in to NPM (npm login) before publishing.
  • If you update files, always bump the version before publishing again.
  • Remove any paths references in tsconfig.json that point outside the package root.
  • The library automatically handles client-side persistence using localStorage.

šŸ“„ License

MIT Ā© jaiswald159