{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "data-table-zustand",
  "title": "Data Table zustand Store Adapter",
  "description": "Client-side state management adapter using zustand for app-level filter persistence.",
  "dependencies": [
    "zustand"
  ],
  "registryDependencies": [
    "https://data-table.openstatus.dev/r/data-table.json"
  ],
  "files": [
    {
      "path": "src/lib/store/adapters/zustand/index.ts",
      "content": "/**\n * Zustand Adapter for BYOS\n *\n * This adapter integrates with existing Zustand stores via a slice pattern.\n * It is client-only and does not support SSR.\n */\n\n\"use client\";\n\nimport { useMemo, useRef } from \"react\";\nimport type { StoreApi } from \"zustand\";\nimport type { InternalStoreAdapter } from \"../../adapter/types\";\nimport { getSchemaDefaults } from \"../../schema/serialization\";\nimport type { SchemaDefinition, StoreSnapshot } from \"../../schema/types\";\nimport { getFilterSliceKeys } from \"./slice\";\nimport type { ZustandAdapterOptions } from \"./types\";\n\nexport type {\n  ZustandAdapterOptions,\n  FilterSlice,\n  FilterSliceState,\n  FilterSliceActions,\n} from \"./types\";\nexport {\n  createFilterSlice,\n  getFilterSliceKeys,\n  getFilterSliceFromState,\n} from \"./slice\";\n\n/**\n * Create a Zustand adapter from an existing store\n *\n * @example\n * ```typescript\n * import { create } from 'zustand';\n * import { createFilterSlice, useZustandAdapter } from '@/lib/store/adapters/zustand';\n *\n * const schema = createSchema({\n *   regions: field.array(field.string()).default([]),\n *   host: field.string(),\n * });\n *\n * // Create store with filter slice\n * const useAppStore = create((set, get) => ({\n *   user: null,\n *   ...createFilterSlice(schema.definition, 'my-table', set, get),\n * }));\n *\n * // In component\n * function MyComponent() {\n *   const adapter = useZustandAdapter(useAppStore, schema.definition, { id: 'my-table' });\n *   return (\n *     <DataTableStoreProvider adapter={adapter}>\n *       <DataTable />\n *     </DataTableStoreProvider>\n *   );\n * }\n * ```\n */\nexport function useZustandAdapter<\n  TStore extends Record<string, unknown>,\n  TFilters extends Record<string, unknown>,\n>(\n  useStore: {\n    (): TStore;\n    getState: () => TStore;\n    subscribe: (\n      listener: (state: TStore, prevState: TStore) => void,\n    ) => () => void;\n  },\n  schema: SchemaDefinition,\n  options: ZustandAdapterOptions<TFilters>,\n): InternalStoreAdapter<TFilters> {\n  const { id, initialState } = options;\n  const keys = getFilterSliceKeys(id);\n  const defaults = useMemo(\n    () => getSchemaDefaults(schema) as TFilters,\n    [schema],\n  );\n\n  const versionRef = useRef(0);\n\n  // Cache server snapshot to avoid infinite loop with useSyncExternalStore\n  const serverSnapshotRef = useRef<StoreSnapshot<TFilters>>({\n    state: { ...defaults, ...initialState } as TFilters,\n    version: 0,\n  });\n\n  const adapter = useMemo<InternalStoreAdapter<TFilters>>(() => {\n    const storeApi = useStore as unknown as StoreApi<TStore>;\n\n    return {\n      subscribe(listener: () => void) {\n        return storeApi.subscribe(() => {\n          versionRef.current++;\n          listener();\n        });\n      },\n\n      getSnapshot(): StoreSnapshot<TFilters> {\n        const state = storeApi.getState();\n        const filters = (state[keys.state] as TFilters) || defaults;\n        return {\n          state: filters,\n          version: versionRef.current,\n        };\n      },\n\n      // Zustand adapter is client-only, return cached snapshot for SSR\n      getServerSnapshot(): StoreSnapshot<TFilters> {\n        return serverSnapshotRef.current;\n      },\n\n      setState(partial: Partial<TFilters>) {\n        const state = storeApi.getState();\n        const setFilters = state[keys.setFilters] as\n          | ((partial: Partial<TFilters>) => void)\n          | undefined;\n\n        if (setFilters) {\n          setFilters(partial);\n        } else {\n          // Fallback: direct state update if slice actions not found\n          const current = (state[keys.state] as TFilters) || defaults;\n          (storeApi as unknown as StoreApi<Record<string, unknown>>).setState({\n            [keys.state]: { ...current, ...partial },\n          });\n        }\n      },\n\n      setField<K extends keyof TFilters>(key: K, value: TFilters[K]) {\n        this.setState({ [key]: value } as unknown as Partial<TFilters>);\n      },\n\n      reset(fields?: (keyof TFilters)[]) {\n        const state = storeApi.getState();\n        const resetFilters = state[keys.resetFilters] as\n          | ((fields?: (keyof TFilters)[]) => void)\n          | undefined;\n\n        if (resetFilters) {\n          resetFilters(fields);\n        } else {\n          // Fallback: direct reset\n          if (fields) {\n            const resetPartial: Partial<TFilters> = {};\n            for (const field of fields) {\n              resetPartial[field] = defaults[field];\n            }\n            this.setState(resetPartial);\n          } else {\n            (storeApi as unknown as StoreApi<Record<string, unknown>>).setState(\n              {\n                [keys.state]: defaults,\n              },\n            );\n          }\n        }\n      },\n\n      pause() {\n        const state = storeApi.getState();\n        const pauseFilters = state[keys.pauseFilters] as\n          | (() => void)\n          | undefined;\n\n        if (pauseFilters) {\n          pauseFilters();\n        } else {\n          (storeApi as unknown as StoreApi<Record<string, unknown>>).setState({\n            [keys.paused]: true,\n          });\n        }\n      },\n\n      resume() {\n        const state = storeApi.getState();\n        const resumeFilters = state[keys.resumeFilters] as\n          | (() => void)\n          | undefined;\n\n        if (resumeFilters) {\n          resumeFilters();\n        } else {\n          const pending = state[keys.pending] as Partial<TFilters> | null;\n          (storeApi as unknown as StoreApi<Record<string, unknown>>).setState({\n            [keys.paused]: false,\n            [keys.pending]: null,\n          });\n\n          if (pending) {\n            this.setState(pending);\n          }\n        }\n      },\n\n      isPaused() {\n        const state = storeApi.getState();\n        return (state[keys.paused] as boolean) || false;\n      },\n\n      destroy() {\n        // Nothing to clean up for Zustand adapter\n      },\n\n      getTableId() {\n        return id;\n      },\n\n      getSchema() {\n        return schema;\n      },\n\n      getDefaults() {\n        return defaults;\n      },\n    };\n  }, [useStore, schema, id, defaults, initialState, keys]);\n\n  return adapter;\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/store/adapters/zustand/types.ts",
      "content": "/**\n * Zustand Adapter Types\n */\n\nimport type { StoreApi } from \"zustand\";\nimport type { CreateAdapterOptions } from \"../../adapter/types\";\n\n/**\n * Zustand-specific adapter options\n */\nexport interface ZustandAdapterOptions<T extends Record<string, unknown>>\n  extends CreateAdapterOptions<T> {\n  // Zustand adapter uses existing store integration, no additional options needed\n}\n\n/**\n * State shape for the filter slice in a Zustand store\n */\nexport interface FilterSliceState<T extends Record<string, unknown>> {\n  filters: T;\n  filtersPaused: boolean;\n  filtersPending: Partial<T> | null;\n}\n\n/**\n * Actions for the filter slice\n */\nexport interface FilterSliceActions<T extends Record<string, unknown>> {\n  setFilters: (partial: Partial<T>) => void;\n  resetFilters: (fields?: (keyof T)[]) => void;\n  pauseFilters: () => void;\n  resumeFilters: () => void;\n}\n\n/**\n * Complete filter slice type\n */\nexport type FilterSlice<T extends Record<string, unknown>> =\n  FilterSliceState<T> & FilterSliceActions<T>;\n\n/**\n * Zustand store API type for adapter\n */\nexport type ZustandStoreApi<T> = StoreApi<T>;\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/store/adapters/zustand/slice.ts",
      "content": "/**\n * Zustand Slice Creator for Filter State\n *\n * Creates a namespaced slice for filter state that can be added to existing Zustand stores.\n */\n\nimport { getSchemaDefaults } from \"../../schema/serialization\";\nimport type { SchemaDefinition } from \"../../schema/types\";\nimport type { FilterSlice } from \"./types\";\n\n/**\n * Create state keys for a filter slice (namespaced by table ID)\n */\nexport function getFilterSliceKeys(tableId: string) {\n  return {\n    state: `filters_${tableId}` as const,\n    paused: `filters_${tableId}_paused` as const,\n    pending: `filters_${tableId}_pending` as const,\n    setFilters: `setFilters_${tableId}` as const,\n    resetFilters: `resetFilters_${tableId}` as const,\n    pauseFilters: `pauseFilters_${tableId}` as const,\n    resumeFilters: `resumeFilters_${tableId}` as const,\n  };\n}\n\n/**\n * Type helper for getting the slice keys type\n */\nexport type FilterSliceKeys = ReturnType<typeof getFilterSliceKeys>;\n\n/**\n * Create a filter slice for a Zustand store\n *\n * @example\n * ```typescript\n * import { create } from 'zustand';\n * import { createFilterSlice } from '@/lib/store/adapters/zustand';\n *\n * const schema = createSchema({\n *   regions: field.array(field.string()).default([]),\n *   host: field.string(),\n * });\n *\n * const useStore = create((set, get) => ({\n *   // Your existing state\n *   user: null,\n *   theme: 'light',\n *\n *   // Add filter slice\n *   ...createFilterSlice(schema.definition, 'my-table', set, get),\n * }));\n * ```\n */\nexport function createFilterSlice<T extends Record<string, unknown>>(\n  schema: SchemaDefinition,\n  tableId: string,\n  set: (\n    partial:\n      | Record<string, unknown>\n      | ((state: Record<string, unknown>) => Record<string, unknown>),\n  ) => void,\n  get: () => Record<string, unknown>,\n  initialState?: Partial<T>,\n): Record<string, unknown> {\n  const keys = getFilterSliceKeys(tableId);\n  const defaults = getSchemaDefaults(schema) as T;\n  const initial = { ...defaults, ...initialState };\n\n  return {\n    // State\n    [keys.state]: initial,\n    [keys.paused]: false,\n    [keys.pending]: null,\n\n    // Actions\n    [keys.setFilters]: (partial: Partial<T>) => {\n      const state = get();\n      const isPaused = state[keys.paused] as boolean;\n\n      if (isPaused) {\n        const pending = (state[keys.pending] as Partial<T> | null) || {};\n        set({ [keys.pending]: { ...pending, ...partial } });\n        return;\n      }\n\n      const current = state[keys.state] as T;\n      set({ [keys.state]: { ...current, ...partial } });\n    },\n\n    [keys.resetFilters]: (fields?: (keyof T)[]) => {\n      const state = get();\n      const current = state[keys.state] as T;\n\n      if (fields) {\n        const resetPartial: Partial<T> = {};\n        for (const field of fields) {\n          resetPartial[field] = defaults[field];\n        }\n        set({ [keys.state]: { ...current, ...resetPartial } });\n      } else {\n        set({ [keys.state]: defaults });\n      }\n    },\n\n    [keys.pauseFilters]: () => {\n      set({ [keys.paused]: true });\n    },\n\n    [keys.resumeFilters]: () => {\n      const state = get();\n      const pending = state[keys.pending] as Partial<T> | null;\n\n      set({ [keys.paused]: false, [keys.pending]: null });\n\n      if (pending) {\n        const current = state[keys.state] as T;\n        set({ [keys.state]: { ...current, ...pending } });\n      }\n    },\n  };\n}\n\n/**\n * Get filter slice from a Zustand store state\n */\nexport function getFilterSliceFromState<T extends Record<string, unknown>>(\n  state: Record<string, unknown>,\n  tableId: string,\n): FilterSlice<T> | null {\n  const keys = getFilterSliceKeys(tableId);\n\n  if (!(keys.state in state)) {\n    return null;\n  }\n\n  return {\n    filters: state[keys.state] as T,\n    filtersPaused: state[keys.paused] as boolean,\n    filtersPending: state[keys.pending] as Partial<T> | null,\n    setFilters: state[keys.setFilters] as (partial: Partial<T>) => void,\n    resetFilters: state[keys.resetFilters] as (fields?: (keyof T)[]) => void,\n    pauseFilters: state[keys.pauseFilters] as () => void,\n    resumeFilters: state[keys.resumeFilters] as () => void,\n  };\n}\n",
      "type": "registry:lib"
    }
  ],
  "type": "registry:block"
}