Plan 2.5 final phase (P8). Closes the PrimeVue shell-migration workstream. - RFC-WS-PRIMEVUE-PLAN-2-5: added Supersessions section recording the governing-RFC divergences (§4 dark mode `<body>`→`<html>` per AD-2.5-D1; §7.4 workspace sub option A reversed to placeholder after visual review). Added closure summary (phases, ADs, brand-square recipe, suite delta, lessons). Status → COMPLETE. - BACKLOG: landed 8 items surfaced during Plan 2.5 (MOBILE-SHELL-PARITY, WORKSPACE-DROPDOWN-SUB-CONTENT, DENSITY-AWARE-SPACING, TOPBAR-H-VAR- DECLARE, CSP-FONT-SRC-LOCKDOWN, AUTO-IMPORTS-V2-SCAN, PNPM-RESOLUTIONS- ROOT, SHELLUI-STALE-DATA-THEME-CLEANUP). Marked GRADIENT-BRAND- ALIGNMENT as resolved. - useShellUiStore.toggleDensity: removed redundant applyDomAttributes() call (the AppShellV2 watch already covers density changes). Moved the DOM-write assertion to AppShellV2 watcher-coverage specs. Plan 2.5 status: COMPLETE. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
104 lines
3.2 KiB
TypeScript
104 lines
3.2 KiB
TypeScript
import { defineStore } from 'pinia'
|
|
import { ref } from 'vue'
|
|
|
|
// v2 shell UI state ONLY (RFC-WS-GUI-REDESIGN AD-G4). No tenant/org
|
|
// state — that stays in useAuthStore/useOrganisationStore. Owns the
|
|
// writes to <html data-density>/.dark; v2 bypasses Vuexy useSkins.ts.
|
|
//
|
|
// AD-2.5-D1 (RFC-WS-PRIMEVUE-PLAN-2-5 §4): dark mode is the single
|
|
// class `.dark` on document.documentElement. Tailwind v4's
|
|
// @custom-variant dark (in assets/styles/tailwind.css) and PrimeVue's
|
|
// darkModeSelector (in plugins/primevue/index.ts:31) both react to
|
|
// that class — one toggle, both ecosystems. The historic
|
|
// <html data-theme="..."> mechanism is superseded and removed.
|
|
|
|
export type ShellTheme = 'light' | 'dark'
|
|
export type ShellDensity = 'comfortable' | 'compact'
|
|
|
|
export interface ShellDrawerState {
|
|
isOpen: boolean
|
|
component: string | null
|
|
props: Record<string, unknown>
|
|
}
|
|
|
|
export const useShellUiStore = defineStore('shellUi', () => {
|
|
const sidebarCollapsed = ref(false)
|
|
const mobileOpen = ref(false)
|
|
const density = ref<ShellDensity>('comfortable')
|
|
const theme = ref<ShellTheme>('light')
|
|
const drawer = ref<ShellDrawerState>({ isOpen: false, component: null, props: {} })
|
|
|
|
function toggleSidebar(): void {
|
|
sidebarCollapsed.value = !sidebarCollapsed.value
|
|
}
|
|
|
|
function setMobileOpen(v: boolean): void {
|
|
mobileOpen.value = v
|
|
}
|
|
|
|
function setTheme(next: ShellTheme): void {
|
|
theme.value = next
|
|
}
|
|
|
|
function setDensity(next: ShellDensity): void {
|
|
density.value = next
|
|
}
|
|
|
|
/**
|
|
* Plan 2.5 P6 Fix 10: binary UI toggle between the two density extremes
|
|
* the topbar exposes. Pure state mutation — the <html data-density> write
|
|
* is owned by AppShellV2's `watch([theme, density])`, the single
|
|
* applyDomAttributes() authority. Mirrors setTheme(), which has always
|
|
* relied on that same watcher.
|
|
*
|
|
* Plan 2.5 P8 dedupe: this previously also called applyDomAttributes()
|
|
* directly, so a density toggle fired it twice (idempotent but redundant —
|
|
* once here, once via the watch). The direct call was removed; the watch
|
|
* still covers the change.
|
|
*/
|
|
function toggleDensity(): void {
|
|
density.value = density.value === 'compact' ? 'comfortable' : 'compact'
|
|
}
|
|
|
|
function applyDomAttributes(): void {
|
|
const el = document.documentElement
|
|
|
|
// AD-2.5-D1: dark mode is `.dark` on <html>. PrimeVue
|
|
// darkModeSelector and Tailwind v4 @custom-variant dark both
|
|
// resolve to this class — see file header.
|
|
if (theme.value === 'dark')
|
|
el.classList.add('dark')
|
|
else
|
|
el.classList.remove('dark')
|
|
|
|
// Density continues to use data-attribute (orthogonal axis to
|
|
// colour scheme; coexists with the .dark class on the same root).
|
|
// Density toggle UI lands in P6 Fix 10.
|
|
el.setAttribute('data-density', density.value)
|
|
}
|
|
|
|
function openDrawer(component: string, props: Record<string, unknown> = {}): void {
|
|
drawer.value = { isOpen: true, component, props }
|
|
}
|
|
|
|
function closeDrawer(): void {
|
|
drawer.value = { isOpen: false, component: null, props: {} }
|
|
}
|
|
|
|
return {
|
|
sidebarCollapsed,
|
|
mobileOpen,
|
|
density,
|
|
theme,
|
|
drawer,
|
|
toggleSidebar,
|
|
setMobileOpen,
|
|
setTheme,
|
|
setDensity,
|
|
toggleDensity,
|
|
applyDomAttributes,
|
|
openDrawer,
|
|
closeDrawer,
|
|
}
|
|
})
|