{"version":3,"file":"useGlobalStore.mjs","sources":["../../../src/lib/hooks/useGlobalStore.ts"],"sourcesContent":["/**\n * @file useGlobalStore Hook\n * @description Hook for accessing global Zustand store with selectors\n */\n\nimport { useCallback, useMemo, useRef } from 'react';\nimport { type StoreState, useStore } from '../state/store';\n\n/**\n * Selector function type\n */\nexport type Selector<T> = (state: StoreState) => T;\n\n/**\n * Hook for accessing specific state slice\n */\nexport function useGlobalStore<T>(selector: Selector<T>): T {\n  return useStore(selector);\n}\n\n/**\n * Hook for accessing multiple state slices\n *\n * Note: Uses a combined selector to comply with Rules of Hooks.\n * Hooks must not be called inside loops, conditions, or nested functions.\n */\nexport function useGlobalStoreMultiple<T extends Record<string, unknown>>(selectors: {\n  [K in keyof T]: Selector<T[K]>;\n}): T {\n  // Use a version counter for stable useMemo dependency tracking\n  // This avoids the problematic ternary dependency pattern\n  const versionRef = useRef(0);\n  const selectorsRef = useRef(selectors);\n  const keysRef = useRef<string[]>([]);\n\n  // Check if selectors have actually changed (shallow comparison of values)\n  const currentKeys = Object.keys(selectors);\n  const keysChanged =\n    currentKeys.length !== keysRef.current.length ||\n    currentKeys.some((key, i) => key !== keysRef.current[i]);\n  const selectorsChanged =\n    keysChanged ||\n    currentKeys.some((key) => selectors[key as keyof T] !== selectorsRef.current[key as keyof T]);\n\n  if (selectorsChanged) {\n    selectorsRef.current = selectors;\n    keysRef.current = currentKeys;\n    // Increment version to trigger useMemo recalculation\n    versionRef.current++;\n  }\n\n  // Create a combined selector that extracts all values at once\n  // Using version counter as dependency ensures stable reference tracking\n  const combinedSelector = useMemo(() => {\n    const stableSelectors = selectorsRef.current;\n    return (state: StoreState): T => {\n      const result = {} as T;\n      for (const key of Object.keys(stableSelectors) as (keyof T)[]) {\n        result[key] = stableSelectors[key](state);\n      }\n      return result;\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [versionRef.current]);\n\n  // Use shallow comparison for the result to prevent unnecessary re-renders\n  const resultRef = useRef<T | null>(null);\n\n  return useStore((state) => {\n    const newResult = combinedSelector(state);\n\n    if (resultRef.current !== null && resultRef.current !== undefined) {\n      // Shallow compare the results\n      const keys = Object.keys(newResult) as (keyof T)[];\n      const prevResult = resultRef.current;\n      const hasChanged = keys.some((key) => !Object.is(newResult[key], prevResult[key]));\n      if (!hasChanged) {\n        return resultRef.current;\n      }\n    }\n\n    resultRef.current = newResult;\n    return newResult;\n  });\n}\n\n/**\n * Hook for computed values from store\n *\n * Uses the compute function directly as a selector with memoization\n * to prevent unnecessary re-renders.\n */\nexport function useGlobalStoreComputed<T>(\n  compute: (state: StoreState) => T,\n  deps: unknown[] = []\n): T {\n  // Memoize the selector based on deps to maintain referential stability\n  // eslint-disable-next-line react-hooks/exhaustive-deps\n  const memoizedSelector = useCallback(compute, deps);\n\n  // Use a custom equality function that caches and compares results\n  const resultRef = useRef<{ value: T; initialized: boolean }>({\n    value: undefined as T,\n    initialized: false,\n  });\n\n  return useStore((state) => {\n    const newValue = memoizedSelector(state);\n\n    // On first run, initialize the cache\n    if (!resultRef.current.initialized) {\n      resultRef.current = { value: newValue, initialized: true };\n      return newValue;\n    }\n\n    // For objects/arrays, use shallow comparison for stability\n    const prevValue = resultRef.current.value;\n    if (Object.is(newValue, prevValue)) {\n      return prevValue;\n    }\n\n    // Update cache and return new value\n    resultRef.current.value = newValue;\n    return newValue;\n  });\n}\n\n/**\n * Hook for store actions\n */\nexport function useGlobalStoreActions(): Record<string, unknown> {\n\n\n  return useStore((_state) => ({\n    // Add your store actions here based on your actual store implementation\n    // Example: reset: state.reset,\n  }));\n}\n\n/**\n * Extended state interface for optional hydration state\n */\ninterface HydratableState {\n  _hydrated?: boolean;\n}\n\n/**\n * Hook for checking if store is hydrated (useful for SSR)\n */\nexport function useStoreHydrated(): boolean {\n\n  return useStore((state) => (state as StoreState & HydratableState)._hydrated ?? true);\n}\n\n/**\n * Create a bound selector hook for a specific slice\n */\nexport function createSliceHook<T>(selector: Selector<T>) {\n  return function useSlice(): T {\n    return useStore(selector);\n  };\n}\n\n/**\n * Create a bound action hook\n */\nexport function createActionHook<T extends (...args: unknown[]) => unknown>(\n  actionSelector: Selector<T>\n) {\n  return function useAction(): T {\n    return useStore(actionSelector);\n  };\n}\n\n/**\n * Hook for subscribing to store changes with callback\n */\nexport function useGlobalStoreSubscription<T>(\n  selector: Selector<T>,\n  callback: (value: T, prevValue: T) => void\n): { value: T; ref: (node: T | null) => void } {\n  const value = useStore(selector);\n  const prevValueRef = useCallback(\n    (node: T | null) => {\n      if (node !== null && node !== value) {\n        callback(value, node as T);\n      }\n    },\n    [value, callback]\n  );\n\n  return { value, ref: prevValueRef };\n}\n\n/**\n * Extended state interface for optional nested structures\n * These allow flexibility for different store configurations\n */\ninterface ExtendedStoreState {\n  ui?: {\n    sidebarOpen?: boolean;\n    theme?: string;\n  };\n  user?: {\n    current?: unknown;\n  };\n  notifications?: {\n    items?: unknown[];\n    unreadCount?: number;\n  };\n  // Direct properties from UISlice\n  sidebarOpen?: boolean;\n  theme?: string;\n}\n\n/**\n * Type guard for accessing optional nested state\n */\nfunction getNestedOrDirect<T>(\n  state: StoreState & ExtendedStoreState,\n  nestedPath: keyof ExtendedStoreState,\n  nestedKey: string,\n  directKey: keyof StoreState,\n  defaultValue: T\n): T {\n  const nested = state[nestedPath];\n  if (nested != null && typeof nested === 'object' && nestedKey in nested) {\n    return (nested as Record<string, unknown>)[nestedKey] as T;\n  }\n  if (directKey in state) {\n    return state[directKey] as T;\n  }\n  return defaultValue;\n}\n\n/**\n * Common selectors for convenience\n */\nexport const globalSelectors = {\n  // UI state - check nested first, then direct properties\n  sidebarOpen: (state: StoreState): boolean =>\n    getNestedOrDirect(state, 'ui', 'sidebarOpen', 'sidebarOpen' as keyof StoreState, true),\n  theme: (state: StoreState): string =>\n    getNestedOrDirect(state, 'ui', 'theme', 'theme' as keyof StoreState, 'light'),\n\n  // User state\n  currentUser: (state: StoreState): unknown => {\n    const extended = state as StoreState & ExtendedStoreState;\n    return extended.user?.current ?? null;\n  },\n  isAuthenticated: (state: StoreState): boolean => {\n    const extended = state as StoreState & ExtendedStoreState;\n    return extended.user?.current != null;\n  },\n\n  // Notifications\n  notifications: (state: StoreState): unknown[] => {\n    const extended = state as StoreState & ExtendedStoreState;\n    return extended.notifications?.items ?? [];\n  },\n  unreadCount: (state: StoreState): number => {\n    const extended = state as StoreState & ExtendedStoreState;\n    return extended.notifications?.unreadCount ?? 0;\n  },\n} as const;\n\n/**\n * Pre-built hooks using common selectors\n */\nexport const useIsSidebarOpen = (): boolean => useGlobalStore(globalSelectors.sidebarOpen);\nexport const useCurrentUser = (): unknown => useGlobalStore(globalSelectors.currentUser);\nexport const useIsAuthenticated = (): boolean => useGlobalStore(globalSelectors.isAuthenticated);\nexport const useUnreadNotificationCount = (): number => useGlobalStore(globalSelectors.unreadCount);\n"],"names":["useGlobalStore","selector","useStore","useGlobalStoreMultiple","selectors","versionRef","useRef","selectorsRef","keysRef","currentKeys","key","i","combinedSelector","useMemo","stableSelectors","state","result","resultRef","newResult","keys","prevResult","useGlobalStoreComputed","compute","deps","memoizedSelector","useCallback","newValue","prevValue","useGlobalStoreActions","_state","useStoreHydrated","createSliceHook","createActionHook","actionSelector","useGlobalStoreSubscription","callback","value","prevValueRef","node","getNestedOrDirect","nestedPath","nestedKey","directKey","defaultValue","nested","globalSelectors","useIsSidebarOpen","useCurrentUser","useIsAuthenticated","useUnreadNotificationCount"],"mappings":"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"}