@codella-software/react 2.3.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -73,8 +73,10 @@ export interface UseFiltersAndSortReturn<T extends Record<string, any> = Record<
73
73
  isSortedBy: (field: string) => boolean;
74
74
  /** Set page */
75
75
  setPage: (page: number) => void;
76
- /** Set page size */
76
+ /** Set page size (resets to page 0) */
77
77
  setPageSize: (size: number) => void;
78
+ /** Set both page and pageSize atomically */
79
+ setPagination: (page: number, size?: number) => void;
78
80
  /** Go to next page */
79
81
  nextPage: () => void;
80
82
  /** Go to previous page */
@@ -1 +1 @@
1
- {"version":3,"file":"useFiltersAndSort.d.ts","sourceRoot":"","sources":["../../src/filters-and-sort/useFiltersAndSort.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useFiltersAndSort.d.ts","sourceRoot":"","sources":["../../src/filters-and-sort/useFiltersAndSort.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,KAAK,kBAAkB,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAG5G;;GAEG;AACH,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IACvF,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAA;IAClB,0DAA0D;IAC1D,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,6BAA6B;IAC7B,YAAY,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAA;IAC7C,uDAAuD;IACvD,cAAc,CAAC,EAAE,CAAC,CAAA;IAClB,gDAAgD;IAChD,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,iEAAiE;IACjE,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,kCAAkC;IAClC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAC3F,wDAAwD;IACxD,OAAO,CAAC,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAA;IAClC,uFAAuF;IACvF,MAAM,CAAC,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAA;IAChC,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,oDAAoD;IACpD,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC,KAAK,OAAO,CAAA;CAChF;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAE1F,2BAA2B;IAC3B,OAAO,EAAE,CAAC,CAAA;IACV,yBAAyB;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,6BAA6B;IAC7B,aAAa,EAAE,aAAa,CAAA;IAC5B,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,+CAA+C;IAC/C,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAA;IAG5B,gCAAgC;IAChC,SAAS,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IAC3D,mCAAmC;IACnC,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IACzC,8BAA8B;IAC9B,WAAW,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,IAAI,CAAA;IAChD,mDAAmD;IACnD,eAAe,EAAE,MAAM,IAAI,CAAA;IAC3B,2CAA2C;IAC3C,SAAS,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,OAAO,CAAA;IACjD,sCAAsC;IACtC,aAAa,EAAE,MAAM,OAAO,CAAA;IAC5B,kCAAkC;IAClC,iBAAiB,EAAE,MAAM,CAAA;IAGzB,iEAAiE;IACjE,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,0DAA0D;IAC1D,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,yBAAyB;IACzB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,2CAA2C;IAC3C,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAA;IAGtC,eAAe;IACf,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,uCAAuC;IACvC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,4CAA4C;IAC5C,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACpD,sBAAsB;IACtB,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,uBAAuB;IACvB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,6BAA6B;IAC7B,WAAW,EAAE,OAAO,CAAA;IACpB,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAA;IAGd,uBAAuB;IACvB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,yBAAyB;IACzB,UAAU,EAAE,MAAM,IAAI,CAAA;IAGtB,6BAA6B;IAC7B,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,8BAA8B;IAC9B,oBAAoB,EAAE,MAAM,IAAI,CAAA;IAChC,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAA;IAGlB,uEAAuE;IACvE,UAAU,EAAE,MAAM,qBAAqB,CAAC,CAAC,CAAC,CAAA;CAC3C;AAsDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACnF,OAAO,EAAE,wBAAwB,CAAC,CAAC,CAAC,GACnC,uBAAuB,CAAC,CAAC,CAAC,CAuU5B;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5E,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,GACpD,uBAAuB,CAAC,CAAC,CAAC,CAO5B;AAED,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,CAAA"}
package/dist/index.cjs CHANGED
@@ -184,8 +184,25 @@ function useFiltersAndSort(options) {
184
184
  }
185
185
  size = 1;
186
186
  }
187
- const currentPage = svc.getCurrentState().pagination.page;
188
- svc.setPagination(currentPage, size);
187
+ svc.setPagination(0, size);
188
+ },
189
+ [svc, debug]
190
+ );
191
+ const setPagination = react.useCallback(
192
+ (page, size) => {
193
+ if (page < 0) {
194
+ if (debug) {
195
+ console.warn("useFiltersAndSort: setPagination called with negative page, clamping to 0");
196
+ }
197
+ page = 0;
198
+ }
199
+ if (size !== void 0 && size < 1) {
200
+ if (debug) {
201
+ console.warn("useFiltersAndSort: setPagination called with size < 1, clamping to 1");
202
+ }
203
+ size = 1;
204
+ }
205
+ svc.setPagination(page, size);
189
206
  },
190
207
  [svc, debug]
191
208
  );
@@ -250,6 +267,7 @@ function useFiltersAndSort(options) {
250
267
  // Pagination actions
251
268
  setPage,
252
269
  setPageSize,
270
+ setPagination,
253
271
  nextPage,
254
272
  prevPage,
255
273
  firstPage,
@@ -280,6 +298,7 @@ function useFiltersAndSort(options) {
280
298
  isSortedBy,
281
299
  setPage,
282
300
  setPageSize,
301
+ setPagination,
283
302
  nextPage,
284
303
  prevPage,
285
304
  firstPage,
@@ -590,11 +609,6 @@ function useRichContent(options = {}) {
590
609
  setSelection
591
610
  };
592
611
  }
593
- function useTableService() {
594
- throw new Error(
595
- "useTableService is deprecated. TableBuilder is a configuration builder, not a stateful service. Use FiltersAndSortService with useFiltersAndSort hook for reactive state management. See documentation: https://github.com/CodellaSoftware/codella-utils"
596
- );
597
- }
598
612
  const TabsContext = react.createContext(void 0);
599
613
  function TabsProvider({ config, children }) {
600
614
  const [service] = react.useState(() => new tabs.TabsService(config));
@@ -670,7 +684,6 @@ exports.useLiveUpdates = useLiveUpdates;
670
684
  exports.useRichContent = useRichContent;
671
685
  exports.useSetActiveTab = useSetActiveTab;
672
686
  exports.useTabChange = useTabChange;
673
- exports.useTableService = useTableService;
674
687
  exports.useTabs = useTabs;
675
688
  exports.useTabsContext = useTabsContext;
676
689
  exports.useTabsService = useTabsService;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/filters-and-sort/useFiltersAndSort.ts","../src/form-builder/useFormBuilder.ts","../src/live-updates/LiveUpdateProvider.tsx","../src/live-updates/useLiveListener.ts","../src/live-updates/useLiveRequest.ts","../src/live-updates/useLiveUpdateListener.ts","../src/live-updates/useLiveUpdates.ts","../src/rich-content/useRichContent.ts","../src/table-builder/useTableService.ts","../src/tabs/useTabsHooks.tsx"],"sourcesContent":["import { FiltersAndSortService, type FilterAndSortState, type SortDirection } from '@codella-software/utils'\nimport { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from 'react'\n\n/**\n * Configuration for creating a new FiltersAndSortService instance\n */\nexport interface FiltersAndSortConfig<T extends Record<string, any> = Record<string, any>> {\n /** Unique storage key for persistence */\n storageKey: string\n /** Whether to persist state to storage (default: true) */\n persistToStorage?: boolean\n /** Initial state override */\n initialState?: Partial<FilterAndSortState<T>>\n /** Default filter values used when clearing filters */\n defaultFilters?: T\n /** Skip hydrating state from storage on init */\n skipStorageHydration?: boolean\n /** Debounce time for storage persistence in ms (default: 300) */\n storageDebounceMs?: number\n /** Optional storage key prefix */\n storageKeyPrefix?: string\n}\n\n/**\n * Hook options for useFiltersAndSort\n */\nexport interface UseFiltersAndSortOptions<T extends Record<string, any> = Record<string, any>> {\n /** An existing FiltersAndSortService instance to use */\n service?: FiltersAndSortService<T>\n /** Configuration to create a new service instance (mutually exclusive with service) */\n config?: FiltersAndSortConfig<T>\n /** Enable debug logging */\n debug?: boolean\n /** Custom equality function for state comparison */\n isEqual?: (prev: FilterAndSortState<T>, next: FilterAndSortState<T>) => boolean\n}\n\n/**\n * Hook return value for useFiltersAndSort\n */\nexport interface UseFiltersAndSortReturn<T extends Record<string, any> = Record<string, any>> {\n // State\n /** Current filter state */\n filters: T\n /** Current sort field */\n sortBy: string | null\n /** Current sort direction */\n sortDirection: SortDirection\n /** Current page (0-based) */\n page: number\n /** Page size */\n pageSize: number\n /** Current search query */\n query: string\n /** Full state object for advanced use cases */\n state: FilterAndSortState<T>\n\n // Filter actions\n /** Set a single filter value */\n setFilter: <K extends keyof T>(key: K, value: T[K]) => void\n /** Set multiple filters at once */\n setFilters: (filters: Partial<T>) => void\n /** Clear a specific filter */\n clearFilter: <K extends keyof T>(key: K) => void\n /** Clear all filters (resets to defaultFilters) */\n clearAllFilters: () => void\n /** Check if a specific filter is active */\n hasFilter: <K extends keyof T>(key: K) => boolean\n /** Check if any filters are active */\n hasAnyFilters: () => boolean\n /** Get count of active filters */\n activeFilterCount: number\n\n // Sort actions\n /** Set/toggle sort (auto-toggles between asc -> desc -> none) */\n setSort: (field: string) => void\n /** Toggle sort direction for field (alias for setSort) */\n toggleSort: (field: string) => void\n /** Clear current sort */\n clearSort: () => void\n /** Check if a field is currently sorted */\n isSortedBy: (field: string) => boolean\n\n // Pagination actions\n /** Set page */\n setPage: (page: number) => void\n /** Set page size */\n setPageSize: (size: number) => void\n /** Go to next page */\n nextPage: () => void\n /** Go to previous page */\n prevPage: () => void\n /** Go to first page */\n firstPage: () => void\n /** Check if on first page */\n isFirstPage: boolean\n /** Calculate offset for API requests */\n offset: number\n\n // Query actions\n /** Set search query */\n setQuery: (query: string) => void\n /** Clear search query */\n clearQuery: () => void\n\n // General actions\n /** Reset to initial state */\n reset: () => void\n /** Clear storage and reset */\n clearStorageAndReset: () => void\n /** Get the storage key being used */\n storageKey: string\n\n // Service access\n /** Direct access to the underlying service (for advanced use cases) */\n getService: () => FiltersAndSortService<T>\n}\n\n/**\n * Default shallow equality check for filter state\n */\nfunction defaultIsEqual<T>(prev: FilterAndSortState<T>, next: FilterAndSortState<T>): boolean {\n if (prev === next) return true\n\n // Quick reference equality checks\n if (prev.query !== next.query) return false\n if (prev.pagination.page !== next.pagination.page) return false\n if (prev.pagination.limit !== next.pagination.limit) return false\n\n // Sort comparison\n const prevSort = prev.sort\n const nextSort = next.sort\n if (prevSort?.fieldName !== nextSort?.fieldName) return false\n if (prevSort?.direction !== nextSort?.direction) return false\n\n // Shallow filter comparison\nconst prevFilters = prev.filters as Record<string, unknown>\nconst nextFilters = next.filters as Record<string, unknown>\nconst prevKeys = Object.keys(prevFilters)\nconst nextKeys = Object.keys(nextFilters)\n\nif (prevKeys.length !== nextKeys.length) return false\n\nfor (const key of prevKeys) {\n if (prevFilters[key] !== nextFilters[key]) return false\n}\n return true\n}\n\n/**\n * Count active filters (non-null, non-undefined, non-empty string values)\n */\nfunction countActiveFilters<T extends Record<string, unknown>>(filters: T, defaultFilters?: T): number {\n let count = 0\n const keys = Object.keys(filters as Record<string, unknown>) as Array<keyof T>\n for (const key of keys) {\n const value = filters[key]\n const defaultValue = defaultFilters?.[key]\n const isDefault = value === defaultValue\n const isEmpty = value === null || value === undefined || value === ''\n const isEmptyArray = Array.isArray(value) && value.length === 0\n\n if (!isEmpty && !isEmptyArray && !isDefault) {\n count++\n }\n }\n return count\n}\n\n/**\n * React hook that wraps FiltersAndSortService with enhanced functionality\n *\n * @typeParam T - The filters object type\n * @param options - Hook options including service instance or config\n * @returns Filters and sort state with control methods\n *\n * @example\n * ```tsx\n * // Option 1: Create service externally (recommended for sharing between components)\n * const service = new FiltersAndSortService<MyFilters>({ storageKey: 'my-filters' });\n *\n * function MyTable() {\n * const {\n * filters,\n * setFilter,\n * clearAllFilters,\n * hasAnyFilters,\n * activeFilterCount\n * } = useFiltersAndSort({ service });\n *\n * return (\n * <div>\n * <input\n * value={filters.name || ''}\n * onChange={(e) => setFilter('name', e.target.value)}\n * />\n * {hasAnyFilters() && (\n * <button onClick={clearAllFilters}>\n * Clear ({activeFilterCount})\n * </button>\n * )}\n * </div>\n * );\n * }\n *\n * // Option 2: Let the hook create the service (simpler for single-component use)\n * function SimpleTable() {\n * const filters = useFiltersAndSort({\n * config: {\n * storageKey: 'simple-table',\n * defaultFilters: { status: 'all' }\n * }\n * });\n * // ...\n * }\n * ```\n */\nexport function useFiltersAndSort<T extends Record<string, any> = Record<string, any>>(\n options: UseFiltersAndSortOptions<T>\n): UseFiltersAndSortReturn<T> {\n const { service: externalService, config, debug = false, isEqual = defaultIsEqual } = options\n\n // Validate options\n if (!externalService && !config) {\n throw new Error(\n 'useFiltersAndSort: either `service` or `config` must be provided. ' +\n 'Pass an existing FiltersAndSortService instance via `service`, or ' +\n 'provide a `config` object to create a new service.'\n )\n }\n\n if (externalService && config) {\n if (debug) {\n console.warn(\n 'useFiltersAndSort: both `service` and `config` were provided. ' +\n 'The `service` will be used and `config` will be ignored.'\n )\n }\n }\n\n // Track if we own the service (created it ourselves)\n const ownsServiceRef = useRef(false)\n const serviceRef = useRef<FiltersAndSortService<T> | null>(null)\n const isEqualRef = useRef(isEqual)\n\n // Keep isEqual ref updated\n useEffect(() => {\n isEqualRef.current = isEqual\n }, [isEqual])\n\n // Initialize service (only once)\n if (!serviceRef.current) {\n if (externalService) {\n serviceRef.current = externalService\n ownsServiceRef.current = false\n } else if (config) {\n serviceRef.current = new FiltersAndSortService<T>({\n storageKey: config.storageKey,\n persistToStorage: config.persistToStorage,\n initialState: config.initialState,\n defaultFilters: config.defaultFilters,\n skipStorageHydration: config.skipStorageHydration,\n storageDebounceMs: config.storageDebounceMs,\n storageKeyPrefix: config.storageKeyPrefix,\n })\n ownsServiceRef.current = true\n\n if (debug) {\n console.log(`useFiltersAndSort: created service with key \"${config.storageKey}\"`)\n }\n }\n }\n\n const svc = serviceRef.current!\n\n // Get default filters for active filter counting\n const defaultFiltersRef = useRef<T | undefined>(config?.defaultFilters)\n\n // Use useSyncExternalStore for better concurrent mode support\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n const subscription = svc.getState().subscribe(() => {\n onStoreChange()\n })\n return () => subscription.unsubscribe()\n },\n [svc]\n )\n\n const getSnapshot = useCallback(() => svc.getCurrentState(), [svc])\n\n // For SSR - return initial state\n const getServerSnapshot = useCallback(() => svc.getDefaultState(), [svc])\n\n const state = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)\n\n // Debug logging\n useEffect(() => {\n if (!debug) return\n\n const sub = svc.getState().subscribe((newState) => {\n console.log(`useFiltersAndSort [${svc.getStorageKey()}]:`, newState)\n })\n return () => sub.unsubscribe()\n }, [svc, debug])\n\n // Cleanup owned service on unmount\n useEffect(() => {\n return () => {\n if (ownsServiceRef.current && serviceRef.current) {\n if (debug) {\n console.log(`useFiltersAndSort: destroying owned service \"${serviceRef.current.getStorageKey()}\"`)\n }\n serviceRef.current.destroy()\n serviceRef.current = null\n }\n }\n }, [debug])\n\n // Stable callback references using useCallback\n const setFilter = useCallback(\n <K extends keyof T>(key: K, value: T[K]) => {\n svc.setFilters({ [key]: value } as unknown as Partial<T>)\n },\n [svc]\n )\n\n const setFilters = useCallback(\n (filters: Partial<T>) => {\n svc.setFilters(filters)\n },\n [svc]\n )\n\n const clearFilter = useCallback(\n <K extends keyof T>(key: K) => {\n svc.removeFilter(key)\n },\n [svc]\n )\n\n const clearAllFilters = useCallback(() => {\n svc.clearFilters()\n }, [svc])\n\n const hasFilter = useCallback(\n <K extends keyof T>(key: K): boolean => {\n const value = svc.getCurrentState().filters[key]\n const defaultValue = defaultFiltersRef.current?.[key]\n if (value === defaultValue) return false\n if (value === null || value === undefined || value === '') return false\n if (Array.isArray(value) && value.length === 0) return false\n return true\n },\n [svc]\n )\n\n const hasAnyFilters = useCallback((): boolean => {\n return countActiveFilters(svc.getCurrentState().filters, defaultFiltersRef.current) > 0\n }, [svc])\n\n const setSort = useCallback(\n (field: string) => {\n svc.setSort(field)\n },\n [svc]\n )\n\n const clearSort = useCallback(() => {\n svc.setSort('')\n }, [svc])\n\n const isSortedBy = useCallback(\n (field: string): boolean => {\n return svc.getCurrentState().sort?.fieldName === field\n },\n [svc]\n )\n\n const setPage = useCallback(\n (page: number) => {\n if (page < 0) {\n if (debug) {\n console.warn('useFiltersAndSort: setPage called with negative value, clamping to 0')\n }\n page = 0\n }\n svc.setPagination(page)\n },\n [svc, debug]\n )\n\n const setPageSize = useCallback(\n (size: number) => {\n if (size < 1) {\n if (debug) {\n console.warn('useFiltersAndSort: setPageSize called with value < 1, clamping to 1')\n }\n size = 1\n }\n const currentPage = svc.getCurrentState().pagination.page\n svc.setPagination(currentPage, size)\n },\n [svc, debug]\n )\n\n const nextPage = useCallback(() => {\n svc.setPagination(svc.getCurrentState().pagination.page + 1)\n }, [svc])\n\n const prevPage = useCallback(() => {\n const newPage = Math.max(0, svc.getCurrentState().pagination.page - 1)\n svc.setPagination(newPage)\n }, [svc])\n\n const firstPage = useCallback(() => {\n svc.setPagination(0)\n }, [svc])\n\n const setQuery = useCallback(\n (query: string) => {\n svc.setQuery(query)\n },\n [svc]\n )\n\n const clearQuery = useCallback(() => {\n svc.setQuery('')\n }, [svc])\n\n const reset = useCallback(() => {\n svc.reset()\n }, [svc])\n\n const clearStorageAndReset = useCallback(() => {\n svc.clearStorage()\n svc.reset()\n }, [svc])\n\n const getService = useCallback(() => svc, [svc])\n\n // Derived values\n const activeFilterCount = useMemo(\n () => countActiveFilters(state.filters, defaultFiltersRef.current),\n [state.filters]\n )\n\n const isFirstPage = state.pagination.page === 0\n const offset = state.pagination.page * state.pagination.limit\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(\n () => ({\n // State\n filters: state.filters,\n sortBy: state.sort?.fieldName ?? null,\n sortDirection: state.sort?.direction ?? 'asc',\n page: state.pagination.page,\n pageSize: state.pagination.limit,\n query: state.query,\n state,\n\n // Filter actions\n setFilter,\n setFilters,\n clearFilter,\n clearAllFilters,\n hasFilter,\n hasAnyFilters,\n activeFilterCount,\n\n // Sort actions\n setSort,\n toggleSort: setSort,\n clearSort,\n isSortedBy,\n\n // Pagination actions\n setPage,\n setPageSize,\n nextPage,\n prevPage,\n firstPage,\n isFirstPage,\n offset,\n\n // Query actions\n setQuery,\n clearQuery,\n\n // General actions\n reset,\n clearStorageAndReset,\n storageKey: svc.getStorageKey(),\n\n // Service access\n getService,\n }),\n [\n state,\n setFilter,\n setFilters,\n clearFilter,\n clearAllFilters,\n hasFilter,\n hasAnyFilters,\n activeFilterCount,\n setSort,\n clearSort,\n isSortedBy,\n setPage,\n setPageSize,\n nextPage,\n prevPage,\n firstPage,\n isFirstPage,\n offset,\n setQuery,\n clearQuery,\n reset,\n clearStorageAndReset,\n svc,\n getService,\n ]\n )\n}\n\n/**\n * Convenience hook for creating a filters service with simpler API\n * Equivalent to useFiltersAndSort({ config: ... })\n */\nexport function useFilters<T extends Record<string, any> = Record<string, any>>(\n storageKey: string,\n options?: Omit<FiltersAndSortConfig<T>, 'storageKey'>\n): UseFiltersAndSortReturn<T> {\n return useFiltersAndSort<T>({\n config: {\n storageKey,\n ...options,\n },\n })\n}\n\nexport type { FilterAndSortState, SortDirection }\n","// React hook for FormBuilder with proper state management\nimport { FormBuilder } from '@codella-software/utils'\nimport { useEffect, useMemo, useState } from 'react'\n\n/**\n * Hook options for useFormBuilder\n */\nexport interface UseFormBuilderOptions<T extends Record<string, any>> {\n /** FormBuilder instance */\n builder: FormBuilder<T>\n}\n\n/**\n * Hook return value for useFormBuilder\n */\nexport interface UseFormBuilderReturn<T extends Record<string, any>> {\n /** Current form values */\n values: T\n /** Form validation errors */\n errors: Record<string, string | undefined>\n /** Form touched fields */\n touched: Record<string, boolean>\n /** Current form step (for multi-step forms) */\n currentStep: number\n /** Total form steps */\n totalSteps: number\n /** Is form currently submitting */\n isSubmitting: boolean\n /** Is form valid */\n isValid: boolean\n /** Set field value */\n setFieldValue: (field: keyof T, value: any) => Promise<void>\n /** Set field touched */\n setFieldTouched: (field: keyof T, touched: boolean) => void\n /** Validate entire form */\n validate: () => Promise<Record<string, string | undefined>>\n /** Submit form */\n submit: () => Promise<T | null>\n /** Reset form to initial values */\n reset: () => void\n /** Go to next step */\n nextStep: () => void\n /** Go to previous step */\n prevStep: () => void\n}\n\n/**\n * React hook that wraps FormBuilder and provides form state management\n *\n * @param options - Hook options including FormBuilder instance\n * @returns Form state and methods\n *\n * @example\n * ```tsx\n * const builder = new FormBuilder<{ name: string }>()\n * .addField({ name: 'name', type: 'text', label: 'Name' })\n * .build();\n *\n * function MyForm() {\n * const form = useFormBuilder({ builder });\n * return (\n * <form onSubmit={(e) => { e.preventDefault(); form.submit(); }}>\n * <input\n * value={form.values.name}\n * onChange={(e) => form.setFieldValue('name', e.target.value)}\n * />\n * {form.errors.name && <span>{form.errors.name}</span>}\n * <button type=\"submit\">Submit</button>\n * </form>\n * );\n * }\n * ```\n */\nexport function useFormBuilder<T extends Record<string, any>>(\n options: UseFormBuilderOptions<T>\n): UseFormBuilderReturn<T> {\n const { builder } = options\n\n if (!builder) {\n throw new Error('useFormBuilder: builder is required')\n }\n\n // Subscribe to form state\n const [state, setState] = useState(() => builder.currentState)\n const [isSubmitting, setIsSubmitting] = useState(false)\n\n useEffect(() => {\n if (!builder || !builder.state$) {\n return\n }\n\n const subscription = builder.state$.subscribe((newState: any) => {\n setState(newState)\n })\n\n return () => subscription.unsubscribe()\n }, [builder])\n\n const memoizedReturn = useMemo(() => ({\n values: state.values,\n errors: state.errors,\n touched: state.touched,\n currentStep: 0, // Multi-step requires MultiStepFormBuilder\n totalSteps: 1, // Multi-step requires MultiStepFormBuilder\n isSubmitting,\n isValid: Object.keys(state.errors).length === 0,\n\n setFieldValue: async (field: keyof T, value: any) => {\n builder.setFieldValue(field as string, value)\n },\n\n setFieldTouched: (field: keyof T, touched: boolean) => {\n builder.setFieldTouched(field as string, touched)\n },\n\n validate: async () => {\n return builder.validate()\n },\n\n submit: async () => {\n setIsSubmitting(true)\n try {\n await builder.submit()\n setIsSubmitting(false)\n return null\n } catch (error) {\n setIsSubmitting(false)\n throw error\n }\n },\n\n reset: () => {\n builder.reset()\n },\n\n nextStep: () => {\n // Multi-step forms use MultiStepFormBuilder, not FormBuilder\n console.warn('nextStep() is only available on MultiStepFormBuilder')\n },\n\n prevStep: () => {\n // Multi-step forms use MultiStepFormBuilder, not FormBuilder\n console.warn('prevStep() is only available on MultiStepFormBuilder')\n },\n }), [builder, state, isSubmitting])\n\n return memoizedReturn\n}\n","import { FC, ReactNode, createContext, useContext, useEffect, useState } from 'react';\n\nimport type { IAuthProvider } from '@codella-software/utils';\nimport {\n LiveUpdateService,\n SSEService,\n WebsocketService,\n type EventMapping,\n type LiveUpdateType,\n} from '@codella-software/utils/live-updates';\n\nexport interface LiveUpdateContextValue {\n service: LiveUpdateService;\n isConnected: boolean;\n type: LiveUpdateType;\n canSend: boolean;\n}\n\nconst LiveUpdateContext = createContext<LiveUpdateContextValue | null>(null);\n\n/**\n * Hook to access the LiveUpdateContext\n * Must be used within a LiveUpdateProvider\n */\nexport const useLiveUpdateContext = () => {\n const context = useContext(LiveUpdateContext);\n if (!context) {\n throw new Error('useLiveUpdateContext must be used within LiveUpdateProvider');\n }\n return context;\n};\n\nexport interface LiveUpdateProviderProps {\n children: ReactNode;\n authProvider: IAuthProvider;\n sseUrl?: string;\n wsUrl?: string;\n sseEnabled?: boolean;\n wsEnabled?: boolean;\n eventMappings?: Record<string, EventMapping>;\n}\n\n/**\n * Provider component for live updates service\n * Manages service initialization and connection state\n * \n * @example\n * ```tsx\n * import { LiveUpdateProvider } from '@codella/react';\n * import { createFirebaseAuthProvider } from '@codella-software/utils';\n * \n * function App() {\n * const authProvider = createFirebaseAuthProvider({ auth: getAuth() });\n * \n * return (\n * <LiveUpdateProvider\n * authProvider={authProvider}\n * sseUrl=\"http://api.example.com/sse\"\n * wsUrl=\"ws://api.example.com/ws\"\n * eventMappings={{\n * 'notifications': { sseEvent: 'GET_NOTIFICATIONS' }\n * }}\n * >\n * <YourApp />\n * </LiveUpdateProvider>\n * );\n * }\n * ```\n */\nexport const LiveUpdateProvider: FC<LiveUpdateProviderProps> = ({\n children,\n authProvider,\n sseUrl = 'http://localhost:3000/sse',\n wsUrl = 'ws://localhost:3000/ws',\n sseEnabled = true,\n wsEnabled = true,\n eventMappings = {},\n}) => {\n const [isConnected, setIsConnected] = useState(false);\n const [service] = useState(() => {\n const sseService = new SSEService({\n url: sseUrl,\n authProvider,\n });\n\n const wsService = new WebsocketService({\n url: wsUrl,\n authProvider,\n });\n\n return new LiveUpdateService(wsService, sseService);\n });\n\n useEffect(() => {\n if (authProvider.isAuthenticated()) {\n service.initialize({\n sseEnabled,\n wsEnabled,\n authProvider,\n eventMappings,\n });\n } else {\n service.disconnect();\n }\n\n const subscription = service.isConnected$.subscribe(setIsConnected);\n\n return () => {\n subscription.unsubscribe();\n };\n }, [authProvider, sseEnabled, wsEnabled, service, eventMappings]);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n service.disconnect();\n };\n }, [service]);\n\n return (\n <LiveUpdateContext.Provider\n value={{\n service,\n isConnected,\n type: service.type,\n canSend: service.canSend,\n }}\n >\n {children}\n </LiveUpdateContext.Provider>\n );\n};\n","import { useEffect, useState } from 'react';\n\nimport type { WS_ACTIONS } from '@codella-software/utils/live-updates';\nimport { useLiveUpdateContext } from './LiveUpdateProvider';\n\n/**\n * Hook to listen for specific WebSocket actions\n * @param action - The WS_ACTIONS enum value to listen for\n * @param enabled - Whether to subscribe (defaults to true)\n * @returns The latest message payload or null\n */\nexport const useLiveListener = <T = unknown>(action: WS_ACTIONS, enabled = true) => {\n const { service } = useLiveUpdateContext();\n const [data, setData] = useState<T | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n if (!enabled || !service) {\n return;\n }\n\n const subscription = service.on(action).subscribe({\n next: (value) => {\n setData(value);\n setError(null);\n },\n error: (err) => {\n setError(err instanceof Error ? err : new Error(String(err)));\n },\n });\n\n return () => {\n subscription.unsubscribe();\n };\n }, [action, enabled, service]);\n\n return { data, error };\n};\n","import { useCallback } from 'react';\n\nimport type { WS_ACTIONS } from '@codella-software/utils/live-updates';\nimport { useLiveUpdateContext } from './LiveUpdateProvider';\n\n/**\n * Hook for request-response pattern over WebSocket\n * @param sendAction - The action to send\n * @param responseAction - The action to wait for\n * @returns Function to send request and promise for response\n */\nexport const useLiveRequest = <TReq = unknown, TRes = unknown>() => {\n const { service, canSend } = useLiveUpdateContext();\n\n const request = useCallback(\n (sendAction: WS_ACTIONS, responseAction: WS_ACTIONS, payload?: TReq) => {\n if (!canSend) {\n return Promise.reject(new Error('WebSocket not available for sending'));\n }\n\n return service\n .on(responseAction)\n .toPromise()\n .catch(() => {\n throw new Error(`No response received for action: ${responseAction}`);\n });\n },\n [canSend, service],\n );\n\n return { request, canSend };\n};\n","import { useCallback } from 'react';\n\nimport type { WS_ACTIONS } from '@codella-software/utils/live-updates';\nimport { useLiveUpdateContext } from './LiveUpdateProvider';\n\nexport interface UseUpdateListenerOptions {\n enabled?: boolean;\n}\n\n/**\n * Hook to listen for and handle live updates with callback\n * @param action - The WS_ACTIONS enum value to listen for\n * @param onUpdate - Callback when update is received\n * @param enabled - Whether to subscribe (defaults to true)\n */\nexport const useLiveUpdateListener = <T = unknown>(\n action: WS_ACTIONS,\n onUpdate: (data: T) => void,\n { enabled = true }: UseUpdateListenerOptions = {},\n) => {\n const { service } = useLiveUpdateContext();\n\n const handleUpdate = useCallback(\n (data: T) => {\n onUpdate(data);\n },\n [onUpdate],\n );\n\n // Subscribe to updates\n // The subscription is managed by the effect in the hook\n // Users need to call subscribe manually or use useLiveListener instead\n const subscribe = useCallback(() => {\n if (!enabled || !service) {\n return () => {};\n }\n\n const subscription = service.on(action).subscribe(handleUpdate);\n\n return () => {\n subscription.unsubscribe();\n };\n }, [action, enabled, service, handleUpdate]);\n\n return { subscribe };\n};\n","import { useEffect, useState } from 'react';\n\nimport { useLiveUpdateContext } from './LiveUpdateProvider';\n\n/**\n * Hook to subscribe to live update events\n * @param eventName - The event name (must be mapped in LiveUpdateProvider.eventMappings)\n * @returns The latest event data or null\n */\nexport const useLiveUpdates = <T = unknown>(eventName: string) => {\n const { service } = useLiveUpdateContext();\n const [data, setData] = useState<T | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n if (!service) {\n return;\n }\n\n const subscription = service.on(eventName).subscribe({\n next: (value) => {\n setData(value);\n setError(null);\n },\n error: (err) => {\n setError(err instanceof Error ? err : new Error(String(err)));\n },\n });\n\n return () => {\n subscription.unsubscribe();\n };\n }, [eventName, service]);\n\n return { data, error };\n};\n","/**\n * React hook for RichContentService\n */\n\nimport {\n ContentEditableAdapter,\n DocumentNode,\n MarkType,\n RichContentConfig,\n RichContentService,\n RichContentState,\n Selection,\n} from '@codella-software/utils/rich-content';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\n/**\n * Hook options for useRichContent\n */\nexport interface UseRichContentOptions extends RichContentConfig {\n /** DOM element ref for contentEditable */\n editorRef?: React.RefObject<HTMLElement>;\n /** Custom adapter instance */\n adapter?: ContentEditableAdapter;\n}\n\n/**\n * Hook return value for useRichContent\n */\nexport interface UseRichContentReturn {\n /** Service instance */\n service: RichContentService;\n /** Current document content */\n content: DocumentNode;\n /** Full state object */\n state: RichContentState;\n /** Current editor focus state */\n isFocused: boolean;\n /** Can undo */\n canUndo: boolean;\n /** Can redo */\n canRedo: boolean;\n /** Active mark types */\n selectedFormats: Set<MarkType>;\n /** Current selection */\n selection: Selection | null;\n /** Is dirty (has unsaved changes) */\n isDirty: boolean;\n\n // Commands\n /** Insert text */\n insertText: (text: string) => void;\n /** Insert paragraph */\n insertParagraph: () => void;\n /** Insert heading */\n insertHeading: (level: 1 | 2 | 3 | 4 | 5 | 6) => void;\n /** Insert image */\n insertImage: (url: string) => void;\n /** Upload image file */\n uploadImage: (file: File) => Promise<void>;\n /** Insert mention */\n insertMention: (id: string, label: string) => void;\n /** Insert list */\n insertList: (type: 'ordered' | 'unordered') => void;\n /** Toggle mark */\n toggleMark: (mark: MarkType) => void;\n /** Delete content */\n deleteContent: () => void;\n /** Undo */\n undo: () => void;\n /** Redo */\n redo: () => void;\n /** Clear history */\n clearHistory: () => void;\n /** Focus editor */\n focus: () => void;\n /** Set focus state */\n setFocus: (focused: boolean) => void;\n /** Get plain text */\n getPlainText: () => string;\n /** Set selection */\n setSelection: (selection: Selection | null) => void;\n}\n\n/**\n * React hook that wraps RichContentService and manages state subscriptions\n */\nexport function useRichContent(options: UseRichContentOptions = {}): UseRichContentReturn {\n // Create service instance\n const service = useMemo(() => RichContentService.create(options), []);\n\n // State from service\n const [content, setContent] = useState<DocumentNode>(service.getContent());\n const [state, setState] = useState<RichContentState>(service.getState());\n const [isFocused, setIsFocused] = useState(false);\n const [canUndo, setCanUndo] = useState(false);\n const [canRedo, setCanRedo] = useState(false);\n const [selectedFormats, setSelectedFormats] = useState<Set<MarkType>>(new Set());\n\n // Subscriptions\n useEffect(() => {\n if (!service) {\n return\n }\n\n const contentSub = service.getContent$().subscribe(setContent);\n const stateSub = service.getState$().subscribe((newState) => {\n setState(newState);\n setIsFocused(newState.isFocused);\n setCanUndo(newState.canUndo);\n setCanRedo(newState.canRedo);\n setSelectedFormats(newState.selectedFormats || new Set());\n });\n\n return () => {\n contentSub.unsubscribe();\n stateSub.unsubscribe();\n };\n }, [service]);\n\n // Attach adapter if provided\n useEffect(() => {\n if (options.adapter) {\n service.attachAdapter(options.adapter);\n return () => service.detachAdapter();\n }\n }, [options.adapter, service]);\n\n // Setup default adapter if editorRef provided\n const defaultAdapterRef = useRef<ContentEditableAdapter | null>(null);\n\n useEffect(() => {\n if (options.editorRef?.current && !options.adapter && !defaultAdapterRef.current) {\n const { DefaultContentEditableAdapter } = require('@codella-software/utils/rich-content');\n const adapter = new DefaultContentEditableAdapter();\n adapter.mount(options.editorRef.current);\n service.attachAdapter(adapter);\n defaultAdapterRef.current = adapter;\n\n return () => {\n adapter.unmount();\n service.detachAdapter();\n defaultAdapterRef.current = null;\n };\n }\n }, [options.editorRef, options.adapter, service]);\n\n // Command callbacks\n const insertText = useCallback((text: string) => service.insertText(text), [service]);\n const insertParagraph = useCallback(() => service.insertParagraph(), [service]);\n const insertHeading = useCallback((level: any) => service.insertHeading(level), [service]);\n const insertImage = useCallback((url: string) => service.insertImage(url), [service]);\n const uploadImage = useCallback((file: File) => service.uploadImage(file), [service]);\n const insertMention = useCallback((id: string, label: string) => service.insertMention(id, label), [service]);\n const insertList = useCallback((type: 'ordered' | 'unordered') => service.insertList(type), [service]);\n const toggleMark = useCallback((mark: MarkType) => service.toggleMark(mark), [service]);\n const deleteContent = useCallback(() => service.deleteContent(), [service]);\n const undo = useCallback(() => service.undo(), [service]);\n const redo = useCallback(() => service.redo(), [service]);\n const clearHistory = useCallback(() => service.clearHistory(), [service]);\n const focus = useCallback(() => service.getAdapter()?.focus(), [service]);\n const setFocus = useCallback((focused: boolean) => service.setFocus(focused), [service]);\n const getPlainText = useCallback(() => service.getPlainText(), [service]);\n const setSelection = useCallback((sel: Selection | null) => service.setSelection(sel), [service]);\n\n return {\n service,\n content,\n state,\n isFocused,\n canUndo,\n canRedo,\n selectedFormats,\n selection: state.selection || null,\n isDirty: state.isDirty,\n\n insertText,\n insertParagraph,\n insertHeading,\n insertImage,\n uploadImage,\n insertMention,\n insertList,\n toggleMark,\n deleteContent,\n undo,\n redo,\n clearHistory,\n focus,\n setFocus,\n getPlainText,\n setSelection,\n };\n}\n","/**\n * @deprecated TableBuilder is a configuration builder, not a stateful service.\n * \n * For reactive table state management, use FiltersAndSortService instead:\n * \n * @example\n * ```tsx\n * import { createFiltersAndSortService } from '@codella-software/utils';\n * import { useFiltersAndSort } from '@codella-software/react';\n * \n * const service = createFiltersAndSortService({\n * storageKey: 'my-table',\n * initialState: { pagination: { page: 0, limit: 10 } }\n * });\n * \n * function MyTable() {\n * const { page, pageSize, filters, setFilter, setPage } = useFiltersAndSort({ service });\n * \n * // Fetch data with current state\n * const { data } = useQuery({\n * queryKey: ['data', page, pageSize, filters],\n * queryFn: () => fetchData({ page, pageSize, ...filters })\n * });\n * \n * // Build table config\n * const tableConfig = createTableBuilder()\n * .data(data?.items ?? [])\n * .totalRows(data?.total ?? 0)\n * .columns([...])\n * .filtersAndSort(service.getCurrentState(), service.getActions())\n * .build();\n * \n * return <DynamicTable config={tableConfig} />;\n * }\n * ```\n */\nexport function useTableService() {\n throw new Error(\n 'useTableService is deprecated. TableBuilder is a configuration builder, not a stateful service. ' +\n 'Use FiltersAndSortService with useFiltersAndSort hook for reactive state management. ' +\n 'See documentation: https://github.com/CodellaSoftware/codella-utils'\n );\n}\n","import type { TabChangeEvent, TabsConfig } from '@codella-software/utils/tabs';\nimport { TabsService } from '@codella-software/utils/tabs';\nimport React, { createContext, useContext, useEffect, useState } from 'react';\n\ninterface TabsContextValue {\n service: TabsService;\n activeTabId: string;\n tabs: ReturnType<TabsService['getTabs']>;\n}\n\nconst TabsContext = createContext<TabsContextValue | undefined>(undefined);\n\n/**\n * Props for TabsProvider\n */\nexport interface TabsProviderProps {\n config: TabsConfig;\n children: React.ReactNode;\n}\n\n/**\n * Tabs provider for React\n */\nexport function TabsProvider({ config, children }: TabsProviderProps) {\n const [service] = useState(() => new TabsService(config));\n const [activeTabId, setActiveTabId] = useState(() => service.getActiveTabId());\n const [tabs, setTabs] = useState(() => service.getTabs());\n\n useEffect(() => {\n const activeTabSubscription = service.getActiveTabId$().subscribe(setActiveTabId);\n const tabsSubscription = service.getTabs$().subscribe(setTabs);\n\n return () => {\n activeTabSubscription.unsubscribe();\n tabsSubscription.unsubscribe();\n };\n }, [service]);\n\n useEffect(() => {\n return () => {\n service.destroy();\n };\n }, [service]);\n\n return (\n <TabsContext.Provider value={{ service, activeTabId, tabs }}>\n {children}\n </TabsContext.Provider>\n );\n}\n\n/**\n * Hook to access tabs service from context\n */\nexport function useTabsContext(): TabsContextValue {\n const context = useContext(TabsContext);\n if (!context) {\n throw new Error('useTabsContext must be used within a TabsProvider');\n }\n return context;\n}\n\n/**\n * Hook to use tabs service directly\n */\nexport function useTabsService() {\n const { service } = useTabsContext();\n return service;\n}\n\n/**\n * Hook to get active tab\n */\nexport function useActiveTab() {\n const { service } = useTabsContext();\n const [activeTab, setActiveTab] = useState(() => service.getActiveTab());\n\n useEffect(() => {\n const subscription = service.getActiveTab$().subscribe(setActiveTab);\n return () => subscription.unsubscribe();\n }, [service]);\n\n return activeTab;\n}\n\n/**\n * Hook to get all tabs\n */\nexport function useTabs() {\n const { tabs } = useTabsContext();\n return tabs;\n}\n\n/**\n * Hook to listen to tab changes\n */\nexport function useTabChange() {\n const { service } = useTabsContext();\n const [changeEvent, setChangeEvent] = useState<TabChangeEvent | null>(null);\n\n useEffect(() => {\n const subscription = service.onTabChange$().subscribe(setChangeEvent);\n return () => subscription.unsubscribe();\n }, [service]);\n\n return changeEvent;\n}\n\n/**\n * Hook to set active tab\n */\nexport function useSetActiveTab() {\n const { service } = useTabsContext();\n return {\n setActiveTab: (tabId: string) => service.setActiveTab(tabId),\n setActiveTabByIndex: (index: number) => service.setActiveTabByIndex(index),\n nextTab: () => service.nextTab(),\n previousTab: () => service.previousTab(),\n };\n}\n"],"names":["useRef","useEffect","FiltersAndSortService","useCallback","useSyncExternalStore","useMemo","useState","createContext","useContext","SSEService","WebsocketService","LiveUpdateService","jsx","RichContentService","TabsService","tabs"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/filters-and-sort/useFiltersAndSort.ts","../src/form-builder/useFormBuilder.ts","../src/live-updates/LiveUpdateProvider.tsx","../src/live-updates/useLiveListener.ts","../src/live-updates/useLiveRequest.ts","../src/live-updates/useLiveUpdateListener.ts","../src/live-updates/useLiveUpdates.ts","../src/rich-content/useRichContent.ts","../src/tabs/useTabsHooks.tsx"],"sourcesContent":["import { FiltersAndSortService, type FilterAndSortState, type SortDirection } from '@codella-software/utils'\nimport { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from 'react'\n\n/**\n * Configuration for creating a new FiltersAndSortService instance\n */\nexport interface FiltersAndSortConfig<T extends Record<string, any> = Record<string, any>> {\n /** Unique storage key for persistence */\n storageKey: string\n /** Whether to persist state to storage (default: true) */\n persistToStorage?: boolean\n /** Initial state override */\n initialState?: Partial<FilterAndSortState<T>>\n /** Default filter values used when clearing filters */\n defaultFilters?: T\n /** Skip hydrating state from storage on init */\n skipStorageHydration?: boolean\n /** Debounce time for storage persistence in ms (default: 300) */\n storageDebounceMs?: number\n /** Optional storage key prefix */\n storageKeyPrefix?: string\n}\n\n/**\n * Hook options for useFiltersAndSort\n */\nexport interface UseFiltersAndSortOptions<T extends Record<string, any> = Record<string, any>> {\n /** An existing FiltersAndSortService instance to use */\n service?: FiltersAndSortService<T>\n /** Configuration to create a new service instance (mutually exclusive with service) */\n config?: FiltersAndSortConfig<T>\n /** Enable debug logging */\n debug?: boolean\n /** Custom equality function for state comparison */\n isEqual?: (prev: FilterAndSortState<T>, next: FilterAndSortState<T>) => boolean\n}\n\n/**\n * Hook return value for useFiltersAndSort\n */\nexport interface UseFiltersAndSortReturn<T extends Record<string, any> = Record<string, any>> {\n // State\n /** Current filter state */\n filters: T\n /** Current sort field */\n sortBy: string | null\n /** Current sort direction */\n sortDirection: SortDirection\n /** Current page (0-based) */\n page: number\n /** Page size */\n pageSize: number\n /** Current search query */\n query: string\n /** Full state object for advanced use cases */\n state: FilterAndSortState<T>\n\n // Filter actions\n /** Set a single filter value */\n setFilter: <K extends keyof T>(key: K, value: T[K]) => void\n /** Set multiple filters at once */\n setFilters: (filters: Partial<T>) => void\n /** Clear a specific filter */\n clearFilter: <K extends keyof T>(key: K) => void\n /** Clear all filters (resets to defaultFilters) */\n clearAllFilters: () => void\n /** Check if a specific filter is active */\n hasFilter: <K extends keyof T>(key: K) => boolean\n /** Check if any filters are active */\n hasAnyFilters: () => boolean\n /** Get count of active filters */\n activeFilterCount: number\n\n // Sort actions\n /** Set/toggle sort (auto-toggles between asc -> desc -> none) */\n setSort: (field: string) => void\n /** Toggle sort direction for field (alias for setSort) */\n toggleSort: (field: string) => void\n /** Clear current sort */\n clearSort: () => void\n /** Check if a field is currently sorted */\n isSortedBy: (field: string) => boolean\n\n // Pagination actions\n /** Set page */\n setPage: (page: number) => void\n /** Set page size (resets to page 0) */\n setPageSize: (size: number) => void\n /** Set both page and pageSize atomically */\n setPagination: (page: number, size?: number) => void\n /** Go to next page */\n nextPage: () => void\n /** Go to previous page */\n prevPage: () => void\n /** Go to first page */\n firstPage: () => void\n /** Check if on first page */\n isFirstPage: boolean\n /** Calculate offset for API requests */\n offset: number\n\n // Query actions\n /** Set search query */\n setQuery: (query: string) => void\n /** Clear search query */\n clearQuery: () => void\n\n // General actions\n /** Reset to initial state */\n reset: () => void\n /** Clear storage and reset */\n clearStorageAndReset: () => void\n /** Get the storage key being used */\n storageKey: string\n\n // Service access\n /** Direct access to the underlying service (for advanced use cases) */\n getService: () => FiltersAndSortService<T>\n}\n\n/**\n * Default shallow equality check for filter state\n */\nfunction defaultIsEqual<T>(prev: FilterAndSortState<T>, next: FilterAndSortState<T>): boolean {\n if (prev === next) return true\n\n // Quick reference equality checks\n if (prev.query !== next.query) return false\n if (prev.pagination.page !== next.pagination.page) return false\n if (prev.pagination.limit !== next.pagination.limit) return false\n\n // Sort comparison\n const prevSort = prev.sort\n const nextSort = next.sort\n if (prevSort?.fieldName !== nextSort?.fieldName) return false\n if (prevSort?.direction !== nextSort?.direction) return false\n\n // Shallow filter comparison\n const prevFilters = prev.filters as Record<string, unknown>\n const nextFilters = next.filters as Record<string, unknown>\n const prevKeys = Object.keys(prevFilters)\n const nextKeys = Object.keys(nextFilters)\n\n if (prevKeys.length !== nextKeys.length) return false\n\n for (const key of prevKeys) {\n if (prevFilters[key] !== nextFilters[key]) return false\n }\n\n return true\n}\n\n/**\n * Count active filters (non-null, non-undefined, non-empty string values)\n */\nfunction countActiveFilters<T extends Record<string, unknown>>(filters: T, defaultFilters?: T): number {\n let count = 0\n const keys = Object.keys(filters as Record<string, unknown>) as Array<keyof T>\n for (const key of keys) {\n const value = filters[key]\n const defaultValue = defaultFilters?.[key]\n const isDefault = value === defaultValue\n const isEmpty = value === null || value === undefined || value === ''\n const isEmptyArray = Array.isArray(value) && value.length === 0\n\n if (!isEmpty && !isEmptyArray && !isDefault) {\n count++\n }\n }\n return count\n}\n\n/**\n * React hook that wraps FiltersAndSortService with enhanced functionality\n *\n * @typeParam T - The filters object type\n * @param options - Hook options including service instance or config\n * @returns Filters and sort state with control methods\n *\n * @example\n * ```tsx\n * // Option 1: Create service externally (recommended for sharing between components)\n * const service = new FiltersAndSortService<MyFilters>({ storageKey: 'my-filters' });\n *\n * function MyTable() {\n * const {\n * filters,\n * setFilter,\n * clearAllFilters,\n * hasAnyFilters,\n * activeFilterCount\n * } = useFiltersAndSort({ service });\n *\n * return (\n * <div>\n * <input\n * value={filters.name || ''}\n * onChange={(e) => setFilter('name', e.target.value)}\n * />\n * {hasAnyFilters() && (\n * <button onClick={clearAllFilters}>\n * Clear ({activeFilterCount})\n * </button>\n * )}\n * </div>\n * );\n * }\n *\n * // Option 2: Let the hook create the service (simpler for single-component use)\n * function SimpleTable() {\n * const filters = useFiltersAndSort({\n * config: {\n * storageKey: 'simple-table',\n * defaultFilters: { status: 'all' }\n * }\n * });\n * // ...\n * }\n * ```\n */\nexport function useFiltersAndSort<T extends Record<string, any> = Record<string, any>>(\n options: UseFiltersAndSortOptions<T>\n): UseFiltersAndSortReturn<T> {\n const { service: externalService, config, debug = false, isEqual = defaultIsEqual } = options\n\n // Validate options\n if (!externalService && !config) {\n throw new Error(\n 'useFiltersAndSort: either `service` or `config` must be provided. ' +\n 'Pass an existing FiltersAndSortService instance via `service`, or ' +\n 'provide a `config` object to create a new service.'\n )\n }\n\n if (externalService && config) {\n if (debug) {\n console.warn(\n 'useFiltersAndSort: both `service` and `config` were provided. ' +\n 'The `service` will be used and `config` will be ignored.'\n )\n }\n }\n\n // Track if we own the service (created it ourselves)\n const ownsServiceRef = useRef(false)\n const serviceRef = useRef<FiltersAndSortService<T> | null>(null)\n const isEqualRef = useRef(isEqual)\n\n // Keep isEqual ref updated\n useEffect(() => {\n isEqualRef.current = isEqual\n }, [isEqual])\n\n // Initialize service (only once)\n if (!serviceRef.current) {\n if (externalService) {\n serviceRef.current = externalService\n ownsServiceRef.current = false\n } else if (config) {\n serviceRef.current = new FiltersAndSortService<T>({\n storageKey: config.storageKey,\n persistToStorage: config.persistToStorage,\n initialState: config.initialState,\n defaultFilters: config.defaultFilters,\n skipStorageHydration: config.skipStorageHydration,\n storageDebounceMs: config.storageDebounceMs,\n storageKeyPrefix: config.storageKeyPrefix,\n })\n ownsServiceRef.current = true\n\n if (debug) {\n console.log(`useFiltersAndSort: created service with key \"${config.storageKey}\"`)\n }\n }\n }\n\n const svc = serviceRef.current!\n\n // Get default filters for active filter counting\n const defaultFiltersRef = useRef<T | undefined>(config?.defaultFilters)\n\n // Use useSyncExternalStore for better concurrent mode support\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n const subscription = svc.getState().subscribe(() => {\n onStoreChange()\n })\n return () => subscription.unsubscribe()\n },\n [svc]\n )\n\n const getSnapshot = useCallback(() => svc.getCurrentState(), [svc])\n\n // For SSR - return initial state\n const getServerSnapshot = useCallback(() => svc.getDefaultState(), [svc])\n\n const state = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)\n\n // Debug logging\n useEffect(() => {\n if (!debug) return\n\n const sub = svc.getState().subscribe((newState) => {\n console.log(`useFiltersAndSort [${svc.getStorageKey()}]:`, newState)\n })\n return () => sub.unsubscribe()\n }, [svc, debug])\n\n // Cleanup owned service on unmount\n useEffect(() => {\n return () => {\n if (ownsServiceRef.current && serviceRef.current) {\n if (debug) {\n console.log(`useFiltersAndSort: destroying owned service \"${serviceRef.current.getStorageKey()}\"`)\n }\n serviceRef.current.destroy()\n serviceRef.current = null\n }\n }\n }, [debug])\n\n // Stable callback references using useCallback\n const setFilter = useCallback(\n <K extends keyof T>(key: K, value: T[K]) => {\n svc.setFilters({ [key]: value } as unknown as Partial<T>)\n },\n [svc]\n )\n\n const setFilters = useCallback(\n (filters: Partial<T>) => {\n svc.setFilters(filters)\n },\n [svc]\n )\n\n const clearFilter = useCallback(\n <K extends keyof T>(key: K) => {\n svc.removeFilter(key)\n },\n [svc]\n )\n\n const clearAllFilters = useCallback(() => {\n svc.clearFilters()\n }, [svc])\n\n const hasFilter = useCallback(\n <K extends keyof T>(key: K): boolean => {\n const value = svc.getCurrentState().filters[key]\n const defaultValue = defaultFiltersRef.current?.[key]\n if (value === defaultValue) return false\n if (value === null || value === undefined || value === '') return false\n if (Array.isArray(value) && value.length === 0) return false\n return true\n },\n [svc]\n )\n\n const hasAnyFilters = useCallback((): boolean => {\n return countActiveFilters(svc.getCurrentState().filters, defaultFiltersRef.current) > 0\n }, [svc])\n\n const setSort = useCallback(\n (field: string) => {\n svc.setSort(field)\n },\n [svc]\n )\n\n const clearSort = useCallback(() => {\n svc.setSort('')\n }, [svc])\n\n const isSortedBy = useCallback(\n (field: string): boolean => {\n return svc.getCurrentState().sort?.fieldName === field\n },\n [svc]\n )\n\n const setPage = useCallback(\n (page: number) => {\n if (page < 0) {\n if (debug) {\n console.warn('useFiltersAndSort: setPage called with negative value, clamping to 0')\n }\n page = 0\n }\n svc.setPagination(page)\n },\n [svc, debug]\n )\n\n const setPageSize = useCallback(\n (size: number) => {\n if (size < 1) {\n if (debug) {\n console.warn('useFiltersAndSort: setPageSize called with value < 1, clamping to 1')\n }\n size = 1\n }\n // Reset to page 0 when changing page size to avoid out-of-bounds\n svc.setPagination(0, size)\n },\n [svc, debug]\n )\n\n const setPagination = useCallback(\n (page: number, size?: number) => {\n if (page < 0) {\n if (debug) {\n console.warn('useFiltersAndSort: setPagination called with negative page, clamping to 0')\n }\n page = 0\n }\n if (size !== undefined && size < 1) {\n if (debug) {\n console.warn('useFiltersAndSort: setPagination called with size < 1, clamping to 1')\n }\n size = 1\n }\n svc.setPagination(page, size)\n },\n [svc, debug]\n )\n\n const nextPage = useCallback(() => {\n svc.setPagination(svc.getCurrentState().pagination.page + 1)\n }, [svc])\n\n const prevPage = useCallback(() => {\n const newPage = Math.max(0, svc.getCurrentState().pagination.page - 1)\n svc.setPagination(newPage)\n }, [svc])\n\n const firstPage = useCallback(() => {\n svc.setPagination(0)\n }, [svc])\n\n const setQuery = useCallback(\n (query: string) => {\n svc.setQuery(query)\n },\n [svc]\n )\n\n const clearQuery = useCallback(() => {\n svc.setQuery('')\n }, [svc])\n\n const reset = useCallback(() => {\n svc.reset()\n }, [svc])\n\n const clearStorageAndReset = useCallback(() => {\n svc.clearStorage()\n svc.reset()\n }, [svc])\n\n const getService = useCallback(() => svc, [svc])\n\n // Derived values\n const activeFilterCount = useMemo(\n () => countActiveFilters(state.filters, defaultFiltersRef.current),\n [state.filters]\n )\n\n const isFirstPage = state.pagination.page === 0\n const offset = state.pagination.page * state.pagination.limit\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(\n () => ({\n // State\n filters: state.filters,\n sortBy: state.sort?.fieldName ?? null,\n sortDirection: state.sort?.direction ?? 'asc',\n page: state.pagination.page,\n pageSize: state.pagination.limit,\n query: state.query,\n state,\n\n // Filter actions\n setFilter,\n setFilters,\n clearFilter,\n clearAllFilters,\n hasFilter,\n hasAnyFilters,\n activeFilterCount,\n\n // Sort actions\n setSort,\n toggleSort: setSort,\n clearSort,\n isSortedBy,\n\n // Pagination actions\n setPage,\n setPageSize,\n setPagination,\n nextPage,\n prevPage,\n firstPage,\n isFirstPage,\n offset,\n\n // Query actions\n setQuery,\n clearQuery,\n\n // General actions\n reset,\n clearStorageAndReset,\n storageKey: svc.getStorageKey(),\n\n // Service access\n getService,\n }),\n [\n state,\n setFilter,\n setFilters,\n clearFilter,\n clearAllFilters,\n hasFilter,\n hasAnyFilters,\n activeFilterCount,\n setSort,\n clearSort,\n isSortedBy,\n setPage,\n setPageSize,\n setPagination,\n nextPage,\n prevPage,\n firstPage,\n isFirstPage,\n offset,\n setQuery,\n clearQuery,\n reset,\n clearStorageAndReset,\n svc,\n getService,\n ]\n )\n}\n\n/**\n * Convenience hook for creating a filters service with simpler API\n * Equivalent to useFiltersAndSort({ config: ... })\n */\nexport function useFilters<T extends Record<string, any> = Record<string, any>>(\n storageKey: string,\n options?: Omit<FiltersAndSortConfig<T>, 'storageKey'>\n): UseFiltersAndSortReturn<T> {\n return useFiltersAndSort<T>({\n config: {\n storageKey,\n ...options,\n },\n })\n}\n\nexport type { FilterAndSortState, SortDirection }\n","// React hook for FormBuilder with proper state management\nimport { FormBuilder } from '@codella-software/utils'\nimport { useEffect, useMemo, useState } from 'react'\n\n/**\n * Hook options for useFormBuilder\n */\nexport interface UseFormBuilderOptions<T extends Record<string, any>> {\n /** FormBuilder instance */\n builder: FormBuilder<T>\n}\n\n/**\n * Hook return value for useFormBuilder\n */\nexport interface UseFormBuilderReturn<T extends Record<string, any>> {\n /** Current form values */\n values: T\n /** Form validation errors */\n errors: Record<string, string | undefined>\n /** Form touched fields */\n touched: Record<string, boolean>\n /** Current form step (for multi-step forms) */\n currentStep: number\n /** Total form steps */\n totalSteps: number\n /** Is form currently submitting */\n isSubmitting: boolean\n /** Is form valid */\n isValid: boolean\n /** Set field value */\n setFieldValue: (field: keyof T, value: any) => Promise<void>\n /** Set field touched */\n setFieldTouched: (field: keyof T, touched: boolean) => void\n /** Validate entire form */\n validate: () => Promise<Record<string, string | undefined>>\n /** Submit form */\n submit: () => Promise<T | null>\n /** Reset form to initial values */\n reset: () => void\n /** Go to next step */\n nextStep: () => void\n /** Go to previous step */\n prevStep: () => void\n}\n\n/**\n * React hook that wraps FormBuilder and provides form state management\n *\n * @param options - Hook options including FormBuilder instance\n * @returns Form state and methods\n *\n * @example\n * ```tsx\n * const builder = new FormBuilder<{ name: string }>()\n * .addField({ name: 'name', type: 'text', label: 'Name' })\n * .build();\n *\n * function MyForm() {\n * const form = useFormBuilder({ builder });\n * return (\n * <form onSubmit={(e) => { e.preventDefault(); form.submit(); }}>\n * <input\n * value={form.values.name}\n * onChange={(e) => form.setFieldValue('name', e.target.value)}\n * />\n * {form.errors.name && <span>{form.errors.name}</span>}\n * <button type=\"submit\">Submit</button>\n * </form>\n * );\n * }\n * ```\n */\nexport function useFormBuilder<T extends Record<string, any>>(\n options: UseFormBuilderOptions<T>\n): UseFormBuilderReturn<T> {\n const { builder } = options\n\n if (!builder) {\n throw new Error('useFormBuilder: builder is required')\n }\n\n // Subscribe to form state\n const [state, setState] = useState(() => builder.currentState)\n const [isSubmitting, setIsSubmitting] = useState(false)\n\n useEffect(() => {\n if (!builder || !builder.state$) {\n return\n }\n\n const subscription = builder.state$.subscribe((newState: any) => {\n setState(newState)\n })\n\n return () => subscription.unsubscribe()\n }, [builder])\n\n const memoizedReturn = useMemo(() => ({\n values: state.values,\n errors: state.errors,\n touched: state.touched,\n currentStep: 0, // Multi-step requires MultiStepFormBuilder\n totalSteps: 1, // Multi-step requires MultiStepFormBuilder\n isSubmitting,\n isValid: Object.keys(state.errors).length === 0,\n\n setFieldValue: async (field: keyof T, value: any) => {\n builder.setFieldValue(field as string, value)\n },\n\n setFieldTouched: (field: keyof T, touched: boolean) => {\n builder.setFieldTouched(field as string, touched)\n },\n\n validate: async () => {\n return builder.validate()\n },\n\n submit: async () => {\n setIsSubmitting(true)\n try {\n await builder.submit()\n setIsSubmitting(false)\n return null\n } catch (error) {\n setIsSubmitting(false)\n throw error\n }\n },\n\n reset: () => {\n builder.reset()\n },\n\n nextStep: () => {\n // Multi-step forms use MultiStepFormBuilder, not FormBuilder\n console.warn('nextStep() is only available on MultiStepFormBuilder')\n },\n\n prevStep: () => {\n // Multi-step forms use MultiStepFormBuilder, not FormBuilder\n console.warn('prevStep() is only available on MultiStepFormBuilder')\n },\n }), [builder, state, isSubmitting])\n\n return memoizedReturn\n}\n","import { FC, ReactNode, createContext, useContext, useEffect, useState } from 'react';\n\nimport type { IAuthProvider } from '@codella-software/utils';\nimport {\n LiveUpdateService,\n SSEService,\n WebsocketService,\n type EventMapping,\n type LiveUpdateType,\n} from '@codella-software/utils/live-updates';\n\nexport interface LiveUpdateContextValue {\n service: LiveUpdateService;\n isConnected: boolean;\n type: LiveUpdateType;\n canSend: boolean;\n}\n\nconst LiveUpdateContext = createContext<LiveUpdateContextValue | null>(null);\n\n/**\n * Hook to access the LiveUpdateContext\n * Must be used within a LiveUpdateProvider\n */\nexport const useLiveUpdateContext = () => {\n const context = useContext(LiveUpdateContext);\n if (!context) {\n throw new Error('useLiveUpdateContext must be used within LiveUpdateProvider');\n }\n return context;\n};\n\nexport interface LiveUpdateProviderProps {\n children: ReactNode;\n authProvider: IAuthProvider;\n sseUrl?: string;\n wsUrl?: string;\n sseEnabled?: boolean;\n wsEnabled?: boolean;\n eventMappings?: Record<string, EventMapping>;\n}\n\n/**\n * Provider component for live updates service\n * Manages service initialization and connection state\n * \n * @example\n * ```tsx\n * import { LiveUpdateProvider } from '@codella/react';\n * import { createFirebaseAuthProvider } from '@codella-software/utils';\n * \n * function App() {\n * const authProvider = createFirebaseAuthProvider({ auth: getAuth() });\n * \n * return (\n * <LiveUpdateProvider\n * authProvider={authProvider}\n * sseUrl=\"http://api.example.com/sse\"\n * wsUrl=\"ws://api.example.com/ws\"\n * eventMappings={{\n * 'notifications': { sseEvent: 'GET_NOTIFICATIONS' }\n * }}\n * >\n * <YourApp />\n * </LiveUpdateProvider>\n * );\n * }\n * ```\n */\nexport const LiveUpdateProvider: FC<LiveUpdateProviderProps> = ({\n children,\n authProvider,\n sseUrl = 'http://localhost:3000/sse',\n wsUrl = 'ws://localhost:3000/ws',\n sseEnabled = true,\n wsEnabled = true,\n eventMappings = {},\n}) => {\n const [isConnected, setIsConnected] = useState(false);\n const [service] = useState(() => {\n const sseService = new SSEService({\n url: sseUrl,\n authProvider,\n });\n\n const wsService = new WebsocketService({\n url: wsUrl,\n authProvider,\n });\n\n return new LiveUpdateService(wsService, sseService);\n });\n\n useEffect(() => {\n if (authProvider.isAuthenticated()) {\n service.initialize({\n sseEnabled,\n wsEnabled,\n authProvider,\n eventMappings,\n });\n } else {\n service.disconnect();\n }\n\n const subscription = service.isConnected$.subscribe(setIsConnected);\n\n return () => {\n subscription.unsubscribe();\n };\n }, [authProvider, sseEnabled, wsEnabled, service, eventMappings]);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n service.disconnect();\n };\n }, [service]);\n\n return (\n <LiveUpdateContext.Provider\n value={{\n service,\n isConnected,\n type: service.type,\n canSend: service.canSend,\n }}\n >\n {children}\n </LiveUpdateContext.Provider>\n );\n};\n","import { useEffect, useState } from 'react';\n\nimport type { WS_ACTIONS } from '@codella-software/utils/live-updates';\nimport { useLiveUpdateContext } from './LiveUpdateProvider';\n\n/**\n * Hook to listen for specific WebSocket actions\n * @param action - The WS_ACTIONS enum value to listen for\n * @param enabled - Whether to subscribe (defaults to true)\n * @returns The latest message payload or null\n */\nexport const useLiveListener = <T = unknown>(action: WS_ACTIONS, enabled = true) => {\n const { service } = useLiveUpdateContext();\n const [data, setData] = useState<T | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n if (!enabled || !service) {\n return;\n }\n\n const subscription = service.on(action).subscribe({\n next: (value) => {\n setData(value);\n setError(null);\n },\n error: (err) => {\n setError(err instanceof Error ? err : new Error(String(err)));\n },\n });\n\n return () => {\n subscription.unsubscribe();\n };\n }, [action, enabled, service]);\n\n return { data, error };\n};\n","import { useCallback } from 'react';\n\nimport type { WS_ACTIONS } from '@codella-software/utils/live-updates';\nimport { useLiveUpdateContext } from './LiveUpdateProvider';\n\n/**\n * Hook for request-response pattern over WebSocket\n * @param sendAction - The action to send\n * @param responseAction - The action to wait for\n * @returns Function to send request and promise for response\n */\nexport const useLiveRequest = <TReq = unknown, TRes = unknown>() => {\n const { service, canSend } = useLiveUpdateContext();\n\n const request = useCallback(\n (sendAction: WS_ACTIONS, responseAction: WS_ACTIONS, payload?: TReq) => {\n if (!canSend) {\n return Promise.reject(new Error('WebSocket not available for sending'));\n }\n\n return service\n .on(responseAction)\n .toPromise()\n .catch(() => {\n throw new Error(`No response received for action: ${responseAction}`);\n });\n },\n [canSend, service],\n );\n\n return { request, canSend };\n};\n","import { useCallback } from 'react';\n\nimport type { WS_ACTIONS } from '@codella-software/utils/live-updates';\nimport { useLiveUpdateContext } from './LiveUpdateProvider';\n\nexport interface UseUpdateListenerOptions {\n enabled?: boolean;\n}\n\n/**\n * Hook to listen for and handle live updates with callback\n * @param action - The WS_ACTIONS enum value to listen for\n * @param onUpdate - Callback when update is received\n * @param enabled - Whether to subscribe (defaults to true)\n */\nexport const useLiveUpdateListener = <T = unknown>(\n action: WS_ACTIONS,\n onUpdate: (data: T) => void,\n { enabled = true }: UseUpdateListenerOptions = {},\n) => {\n const { service } = useLiveUpdateContext();\n\n const handleUpdate = useCallback(\n (data: T) => {\n onUpdate(data);\n },\n [onUpdate],\n );\n\n // Subscribe to updates\n // The subscription is managed by the effect in the hook\n // Users need to call subscribe manually or use useLiveListener instead\n const subscribe = useCallback(() => {\n if (!enabled || !service) {\n return () => {};\n }\n\n const subscription = service.on(action).subscribe(handleUpdate);\n\n return () => {\n subscription.unsubscribe();\n };\n }, [action, enabled, service, handleUpdate]);\n\n return { subscribe };\n};\n","import { useEffect, useState } from 'react';\n\nimport { useLiveUpdateContext } from './LiveUpdateProvider';\n\n/**\n * Hook to subscribe to live update events\n * @param eventName - The event name (must be mapped in LiveUpdateProvider.eventMappings)\n * @returns The latest event data or null\n */\nexport const useLiveUpdates = <T = unknown>(eventName: string) => {\n const { service } = useLiveUpdateContext();\n const [data, setData] = useState<T | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n if (!service) {\n return;\n }\n\n const subscription = service.on(eventName).subscribe({\n next: (value) => {\n setData(value);\n setError(null);\n },\n error: (err) => {\n setError(err instanceof Error ? err : new Error(String(err)));\n },\n });\n\n return () => {\n subscription.unsubscribe();\n };\n }, [eventName, service]);\n\n return { data, error };\n};\n","/**\n * React hook for RichContentService\n */\n\nimport {\n ContentEditableAdapter,\n DocumentNode,\n MarkType,\n RichContentConfig,\n RichContentService,\n RichContentState,\n Selection,\n} from '@codella-software/utils/rich-content';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\n/**\n * Hook options for useRichContent\n */\nexport interface UseRichContentOptions extends RichContentConfig {\n /** DOM element ref for contentEditable */\n editorRef?: React.RefObject<HTMLElement>;\n /** Custom adapter instance */\n adapter?: ContentEditableAdapter;\n}\n\n/**\n * Hook return value for useRichContent\n */\nexport interface UseRichContentReturn {\n /** Service instance */\n service: RichContentService;\n /** Current document content */\n content: DocumentNode;\n /** Full state object */\n state: RichContentState;\n /** Current editor focus state */\n isFocused: boolean;\n /** Can undo */\n canUndo: boolean;\n /** Can redo */\n canRedo: boolean;\n /** Active mark types */\n selectedFormats: Set<MarkType>;\n /** Current selection */\n selection: Selection | null;\n /** Is dirty (has unsaved changes) */\n isDirty: boolean;\n\n // Commands\n /** Insert text */\n insertText: (text: string) => void;\n /** Insert paragraph */\n insertParagraph: () => void;\n /** Insert heading */\n insertHeading: (level: 1 | 2 | 3 | 4 | 5 | 6) => void;\n /** Insert image */\n insertImage: (url: string) => void;\n /** Upload image file */\n uploadImage: (file: File) => Promise<void>;\n /** Insert mention */\n insertMention: (id: string, label: string) => void;\n /** Insert list */\n insertList: (type: 'ordered' | 'unordered') => void;\n /** Toggle mark */\n toggleMark: (mark: MarkType) => void;\n /** Delete content */\n deleteContent: () => void;\n /** Undo */\n undo: () => void;\n /** Redo */\n redo: () => void;\n /** Clear history */\n clearHistory: () => void;\n /** Focus editor */\n focus: () => void;\n /** Set focus state */\n setFocus: (focused: boolean) => void;\n /** Get plain text */\n getPlainText: () => string;\n /** Set selection */\n setSelection: (selection: Selection | null) => void;\n}\n\n/**\n * React hook that wraps RichContentService and manages state subscriptions\n */\nexport function useRichContent(options: UseRichContentOptions = {}): UseRichContentReturn {\n // Create service instance\n const service = useMemo(() => RichContentService.create(options), []);\n\n // State from service\n const [content, setContent] = useState<DocumentNode>(service.getContent());\n const [state, setState] = useState<RichContentState>(service.getState());\n const [isFocused, setIsFocused] = useState(false);\n const [canUndo, setCanUndo] = useState(false);\n const [canRedo, setCanRedo] = useState(false);\n const [selectedFormats, setSelectedFormats] = useState<Set<MarkType>>(new Set());\n\n // Subscriptions\n useEffect(() => {\n if (!service) {\n return\n }\n\n const contentSub = service.getContent$().subscribe(setContent);\n const stateSub = service.getState$().subscribe((newState) => {\n setState(newState);\n setIsFocused(newState.isFocused);\n setCanUndo(newState.canUndo);\n setCanRedo(newState.canRedo);\n setSelectedFormats(newState.selectedFormats || new Set());\n });\n\n return () => {\n contentSub.unsubscribe();\n stateSub.unsubscribe();\n };\n }, [service]);\n\n // Attach adapter if provided\n useEffect(() => {\n if (options.adapter) {\n service.attachAdapter(options.adapter);\n return () => service.detachAdapter();\n }\n }, [options.adapter, service]);\n\n // Setup default adapter if editorRef provided\n const defaultAdapterRef = useRef<ContentEditableAdapter | null>(null);\n\n useEffect(() => {\n if (options.editorRef?.current && !options.adapter && !defaultAdapterRef.current) {\n const { DefaultContentEditableAdapter } = require('@codella-software/utils/rich-content');\n const adapter = new DefaultContentEditableAdapter();\n adapter.mount(options.editorRef.current);\n service.attachAdapter(adapter);\n defaultAdapterRef.current = adapter;\n\n return () => {\n adapter.unmount();\n service.detachAdapter();\n defaultAdapterRef.current = null;\n };\n }\n }, [options.editorRef, options.adapter, service]);\n\n // Command callbacks\n const insertText = useCallback((text: string) => service.insertText(text), [service]);\n const insertParagraph = useCallback(() => service.insertParagraph(), [service]);\n const insertHeading = useCallback((level: any) => service.insertHeading(level), [service]);\n const insertImage = useCallback((url: string) => service.insertImage(url), [service]);\n const uploadImage = useCallback((file: File) => service.uploadImage(file), [service]);\n const insertMention = useCallback((id: string, label: string) => service.insertMention(id, label), [service]);\n const insertList = useCallback((type: 'ordered' | 'unordered') => service.insertList(type), [service]);\n const toggleMark = useCallback((mark: MarkType) => service.toggleMark(mark), [service]);\n const deleteContent = useCallback(() => service.deleteContent(), [service]);\n const undo = useCallback(() => service.undo(), [service]);\n const redo = useCallback(() => service.redo(), [service]);\n const clearHistory = useCallback(() => service.clearHistory(), [service]);\n const focus = useCallback(() => service.getAdapter()?.focus(), [service]);\n const setFocus = useCallback((focused: boolean) => service.setFocus(focused), [service]);\n const getPlainText = useCallback(() => service.getPlainText(), [service]);\n const setSelection = useCallback((sel: Selection | null) => service.setSelection(sel), [service]);\n\n return {\n service,\n content,\n state,\n isFocused,\n canUndo,\n canRedo,\n selectedFormats,\n selection: state.selection || null,\n isDirty: state.isDirty,\n\n insertText,\n insertParagraph,\n insertHeading,\n insertImage,\n uploadImage,\n insertMention,\n insertList,\n toggleMark,\n deleteContent,\n undo,\n redo,\n clearHistory,\n focus,\n setFocus,\n getPlainText,\n setSelection,\n };\n}\n","import type { TabChangeEvent, TabsConfig } from '@codella-software/utils/tabs';\nimport { TabsService } from '@codella-software/utils/tabs';\nimport React, { createContext, useContext, useEffect, useState } from 'react';\n\ninterface TabsContextValue {\n service: TabsService;\n activeTabId: string;\n tabs: ReturnType<TabsService['getTabs']>;\n}\n\nconst TabsContext = createContext<TabsContextValue | undefined>(undefined);\n\n/**\n * Props for TabsProvider\n */\nexport interface TabsProviderProps {\n config: TabsConfig;\n children: React.ReactNode;\n}\n\n/**\n * Tabs provider for React\n */\nexport function TabsProvider({ config, children }: TabsProviderProps) {\n const [service] = useState(() => new TabsService(config));\n const [activeTabId, setActiveTabId] = useState(() => service.getActiveTabId());\n const [tabs, setTabs] = useState(() => service.getTabs());\n\n useEffect(() => {\n const activeTabSubscription = service.getActiveTabId$().subscribe(setActiveTabId);\n const tabsSubscription = service.getTabs$().subscribe(setTabs);\n\n return () => {\n activeTabSubscription.unsubscribe();\n tabsSubscription.unsubscribe();\n };\n }, [service]);\n\n useEffect(() => {\n return () => {\n service.destroy();\n };\n }, [service]);\n\n return (\n <TabsContext.Provider value={{ service, activeTabId, tabs }}>\n {children}\n </TabsContext.Provider>\n );\n}\n\n/**\n * Hook to access tabs service from context\n */\nexport function useTabsContext(): TabsContextValue {\n const context = useContext(TabsContext);\n if (!context) {\n throw new Error('useTabsContext must be used within a TabsProvider');\n }\n return context;\n}\n\n/**\n * Hook to use tabs service directly\n */\nexport function useTabsService() {\n const { service } = useTabsContext();\n return service;\n}\n\n/**\n * Hook to get active tab\n */\nexport function useActiveTab() {\n const { service } = useTabsContext();\n const [activeTab, setActiveTab] = useState(() => service.getActiveTab());\n\n useEffect(() => {\n const subscription = service.getActiveTab$().subscribe(setActiveTab);\n return () => subscription.unsubscribe();\n }, [service]);\n\n return activeTab;\n}\n\n/**\n * Hook to get all tabs\n */\nexport function useTabs() {\n const { tabs } = useTabsContext();\n return tabs;\n}\n\n/**\n * Hook to listen to tab changes\n */\nexport function useTabChange() {\n const { service } = useTabsContext();\n const [changeEvent, setChangeEvent] = useState<TabChangeEvent | null>(null);\n\n useEffect(() => {\n const subscription = service.onTabChange$().subscribe(setChangeEvent);\n return () => subscription.unsubscribe();\n }, [service]);\n\n return changeEvent;\n}\n\n/**\n * Hook to set active tab\n */\nexport function useSetActiveTab() {\n const { service } = useTabsContext();\n return {\n setActiveTab: (tabId: string) => service.setActiveTab(tabId),\n setActiveTabByIndex: (index: number) => service.setActiveTabByIndex(index),\n nextTab: () => service.nextTab(),\n previousTab: () => service.previousTab(),\n };\n}\n"],"names":["useRef","useEffect","FiltersAndSortService","useCallback","useSyncExternalStore","useMemo","useState","createContext","useContext","SSEService","WebsocketService","LiveUpdateService","jsx","RichContentService","TabsService","tabs"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -42,6 +42,5 @@ export { useFiltersAndSort, type UseFiltersAndSortOptions, type UseFiltersAndSor
42
42
  export { useFormBuilder, type UseFormBuilderOptions, type UseFormBuilderReturn } from './form-builder';
43
43
  export * from './live-updates';
44
44
  export * from './rich-content';
45
- export { useTableService } from './table-builder';
46
45
  export * from './tabs';
47
46
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,KAAK,uBAAuB,EAAE,MAAM,oBAAoB,CAAA;AACnH,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AACtG,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,cAAc,QAAQ,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,KAAK,uBAAuB,EAAE,MAAM,oBAAoB,CAAA;AACnH,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AACtG,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,QAAQ,CAAA"}
package/dist/index.mjs CHANGED
@@ -182,8 +182,25 @@ function useFiltersAndSort(options) {
182
182
  }
183
183
  size = 1;
184
184
  }
185
- const currentPage = svc.getCurrentState().pagination.page;
186
- svc.setPagination(currentPage, size);
185
+ svc.setPagination(0, size);
186
+ },
187
+ [svc, debug]
188
+ );
189
+ const setPagination = useCallback(
190
+ (page, size) => {
191
+ if (page < 0) {
192
+ if (debug) {
193
+ console.warn("useFiltersAndSort: setPagination called with negative page, clamping to 0");
194
+ }
195
+ page = 0;
196
+ }
197
+ if (size !== void 0 && size < 1) {
198
+ if (debug) {
199
+ console.warn("useFiltersAndSort: setPagination called with size < 1, clamping to 1");
200
+ }
201
+ size = 1;
202
+ }
203
+ svc.setPagination(page, size);
187
204
  },
188
205
  [svc, debug]
189
206
  );
@@ -248,6 +265,7 @@ function useFiltersAndSort(options) {
248
265
  // Pagination actions
249
266
  setPage,
250
267
  setPageSize,
268
+ setPagination,
251
269
  nextPage,
252
270
  prevPage,
253
271
  firstPage,
@@ -278,6 +296,7 @@ function useFiltersAndSort(options) {
278
296
  isSortedBy,
279
297
  setPage,
280
298
  setPageSize,
299
+ setPagination,
281
300
  nextPage,
282
301
  prevPage,
283
302
  firstPage,
@@ -588,11 +607,6 @@ function useRichContent(options = {}) {
588
607
  setSelection
589
608
  };
590
609
  }
591
- function useTableService() {
592
- throw new Error(
593
- "useTableService is deprecated. TableBuilder is a configuration builder, not a stateful service. Use FiltersAndSortService with useFiltersAndSort hook for reactive state management. See documentation: https://github.com/CodellaSoftware/codella-utils"
594
- );
595
- }
596
610
  const TabsContext = createContext(void 0);
597
611
  function TabsProvider({ config, children }) {
598
612
  const [service] = useState(() => new TabsService(config));
@@ -669,7 +683,6 @@ export {
669
683
  useRichContent,
670
684
  useSetActiveTab,
671
685
  useTabChange,
672
- useTableService,
673
686
  useTabs,
674
687
  useTabsContext,
675
688
  useTabsService
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/filters-and-sort/useFiltersAndSort.ts","../src/form-builder/useFormBuilder.ts","../src/live-updates/LiveUpdateProvider.tsx","../src/live-updates/useLiveListener.ts","../src/live-updates/useLiveRequest.ts","../src/live-updates/useLiveUpdateListener.ts","../src/live-updates/useLiveUpdates.ts","../src/rich-content/useRichContent.ts","../src/table-builder/useTableService.ts","../src/tabs/useTabsHooks.tsx"],"sourcesContent":["import { FiltersAndSortService, type FilterAndSortState, type SortDirection } from '@codella-software/utils'\nimport { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from 'react'\n\n/**\n * Configuration for creating a new FiltersAndSortService instance\n */\nexport interface FiltersAndSortConfig<T extends Record<string, any> = Record<string, any>> {\n /** Unique storage key for persistence */\n storageKey: string\n /** Whether to persist state to storage (default: true) */\n persistToStorage?: boolean\n /** Initial state override */\n initialState?: Partial<FilterAndSortState<T>>\n /** Default filter values used when clearing filters */\n defaultFilters?: T\n /** Skip hydrating state from storage on init */\n skipStorageHydration?: boolean\n /** Debounce time for storage persistence in ms (default: 300) */\n storageDebounceMs?: number\n /** Optional storage key prefix */\n storageKeyPrefix?: string\n}\n\n/**\n * Hook options for useFiltersAndSort\n */\nexport interface UseFiltersAndSortOptions<T extends Record<string, any> = Record<string, any>> {\n /** An existing FiltersAndSortService instance to use */\n service?: FiltersAndSortService<T>\n /** Configuration to create a new service instance (mutually exclusive with service) */\n config?: FiltersAndSortConfig<T>\n /** Enable debug logging */\n debug?: boolean\n /** Custom equality function for state comparison */\n isEqual?: (prev: FilterAndSortState<T>, next: FilterAndSortState<T>) => boolean\n}\n\n/**\n * Hook return value for useFiltersAndSort\n */\nexport interface UseFiltersAndSortReturn<T extends Record<string, any> = Record<string, any>> {\n // State\n /** Current filter state */\n filters: T\n /** Current sort field */\n sortBy: string | null\n /** Current sort direction */\n sortDirection: SortDirection\n /** Current page (0-based) */\n page: number\n /** Page size */\n pageSize: number\n /** Current search query */\n query: string\n /** Full state object for advanced use cases */\n state: FilterAndSortState<T>\n\n // Filter actions\n /** Set a single filter value */\n setFilter: <K extends keyof T>(key: K, value: T[K]) => void\n /** Set multiple filters at once */\n setFilters: (filters: Partial<T>) => void\n /** Clear a specific filter */\n clearFilter: <K extends keyof T>(key: K) => void\n /** Clear all filters (resets to defaultFilters) */\n clearAllFilters: () => void\n /** Check if a specific filter is active */\n hasFilter: <K extends keyof T>(key: K) => boolean\n /** Check if any filters are active */\n hasAnyFilters: () => boolean\n /** Get count of active filters */\n activeFilterCount: number\n\n // Sort actions\n /** Set/toggle sort (auto-toggles between asc -> desc -> none) */\n setSort: (field: string) => void\n /** Toggle sort direction for field (alias for setSort) */\n toggleSort: (field: string) => void\n /** Clear current sort */\n clearSort: () => void\n /** Check if a field is currently sorted */\n isSortedBy: (field: string) => boolean\n\n // Pagination actions\n /** Set page */\n setPage: (page: number) => void\n /** Set page size */\n setPageSize: (size: number) => void\n /** Go to next page */\n nextPage: () => void\n /** Go to previous page */\n prevPage: () => void\n /** Go to first page */\n firstPage: () => void\n /** Check if on first page */\n isFirstPage: boolean\n /** Calculate offset for API requests */\n offset: number\n\n // Query actions\n /** Set search query */\n setQuery: (query: string) => void\n /** Clear search query */\n clearQuery: () => void\n\n // General actions\n /** Reset to initial state */\n reset: () => void\n /** Clear storage and reset */\n clearStorageAndReset: () => void\n /** Get the storage key being used */\n storageKey: string\n\n // Service access\n /** Direct access to the underlying service (for advanced use cases) */\n getService: () => FiltersAndSortService<T>\n}\n\n/**\n * Default shallow equality check for filter state\n */\nfunction defaultIsEqual<T>(prev: FilterAndSortState<T>, next: FilterAndSortState<T>): boolean {\n if (prev === next) return true\n\n // Quick reference equality checks\n if (prev.query !== next.query) return false\n if (prev.pagination.page !== next.pagination.page) return false\n if (prev.pagination.limit !== next.pagination.limit) return false\n\n // Sort comparison\n const prevSort = prev.sort\n const nextSort = next.sort\n if (prevSort?.fieldName !== nextSort?.fieldName) return false\n if (prevSort?.direction !== nextSort?.direction) return false\n\n // Shallow filter comparison\nconst prevFilters = prev.filters as Record<string, unknown>\nconst nextFilters = next.filters as Record<string, unknown>\nconst prevKeys = Object.keys(prevFilters)\nconst nextKeys = Object.keys(nextFilters)\n\nif (prevKeys.length !== nextKeys.length) return false\n\nfor (const key of prevKeys) {\n if (prevFilters[key] !== nextFilters[key]) return false\n}\n return true\n}\n\n/**\n * Count active filters (non-null, non-undefined, non-empty string values)\n */\nfunction countActiveFilters<T extends Record<string, unknown>>(filters: T, defaultFilters?: T): number {\n let count = 0\n const keys = Object.keys(filters as Record<string, unknown>) as Array<keyof T>\n for (const key of keys) {\n const value = filters[key]\n const defaultValue = defaultFilters?.[key]\n const isDefault = value === defaultValue\n const isEmpty = value === null || value === undefined || value === ''\n const isEmptyArray = Array.isArray(value) && value.length === 0\n\n if (!isEmpty && !isEmptyArray && !isDefault) {\n count++\n }\n }\n return count\n}\n\n/**\n * React hook that wraps FiltersAndSortService with enhanced functionality\n *\n * @typeParam T - The filters object type\n * @param options - Hook options including service instance or config\n * @returns Filters and sort state with control methods\n *\n * @example\n * ```tsx\n * // Option 1: Create service externally (recommended for sharing between components)\n * const service = new FiltersAndSortService<MyFilters>({ storageKey: 'my-filters' });\n *\n * function MyTable() {\n * const {\n * filters,\n * setFilter,\n * clearAllFilters,\n * hasAnyFilters,\n * activeFilterCount\n * } = useFiltersAndSort({ service });\n *\n * return (\n * <div>\n * <input\n * value={filters.name || ''}\n * onChange={(e) => setFilter('name', e.target.value)}\n * />\n * {hasAnyFilters() && (\n * <button onClick={clearAllFilters}>\n * Clear ({activeFilterCount})\n * </button>\n * )}\n * </div>\n * );\n * }\n *\n * // Option 2: Let the hook create the service (simpler for single-component use)\n * function SimpleTable() {\n * const filters = useFiltersAndSort({\n * config: {\n * storageKey: 'simple-table',\n * defaultFilters: { status: 'all' }\n * }\n * });\n * // ...\n * }\n * ```\n */\nexport function useFiltersAndSort<T extends Record<string, any> = Record<string, any>>(\n options: UseFiltersAndSortOptions<T>\n): UseFiltersAndSortReturn<T> {\n const { service: externalService, config, debug = false, isEqual = defaultIsEqual } = options\n\n // Validate options\n if (!externalService && !config) {\n throw new Error(\n 'useFiltersAndSort: either `service` or `config` must be provided. ' +\n 'Pass an existing FiltersAndSortService instance via `service`, or ' +\n 'provide a `config` object to create a new service.'\n )\n }\n\n if (externalService && config) {\n if (debug) {\n console.warn(\n 'useFiltersAndSort: both `service` and `config` were provided. ' +\n 'The `service` will be used and `config` will be ignored.'\n )\n }\n }\n\n // Track if we own the service (created it ourselves)\n const ownsServiceRef = useRef(false)\n const serviceRef = useRef<FiltersAndSortService<T> | null>(null)\n const isEqualRef = useRef(isEqual)\n\n // Keep isEqual ref updated\n useEffect(() => {\n isEqualRef.current = isEqual\n }, [isEqual])\n\n // Initialize service (only once)\n if (!serviceRef.current) {\n if (externalService) {\n serviceRef.current = externalService\n ownsServiceRef.current = false\n } else if (config) {\n serviceRef.current = new FiltersAndSortService<T>({\n storageKey: config.storageKey,\n persistToStorage: config.persistToStorage,\n initialState: config.initialState,\n defaultFilters: config.defaultFilters,\n skipStorageHydration: config.skipStorageHydration,\n storageDebounceMs: config.storageDebounceMs,\n storageKeyPrefix: config.storageKeyPrefix,\n })\n ownsServiceRef.current = true\n\n if (debug) {\n console.log(`useFiltersAndSort: created service with key \"${config.storageKey}\"`)\n }\n }\n }\n\n const svc = serviceRef.current!\n\n // Get default filters for active filter counting\n const defaultFiltersRef = useRef<T | undefined>(config?.defaultFilters)\n\n // Use useSyncExternalStore for better concurrent mode support\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n const subscription = svc.getState().subscribe(() => {\n onStoreChange()\n })\n return () => subscription.unsubscribe()\n },\n [svc]\n )\n\n const getSnapshot = useCallback(() => svc.getCurrentState(), [svc])\n\n // For SSR - return initial state\n const getServerSnapshot = useCallback(() => svc.getDefaultState(), [svc])\n\n const state = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)\n\n // Debug logging\n useEffect(() => {\n if (!debug) return\n\n const sub = svc.getState().subscribe((newState) => {\n console.log(`useFiltersAndSort [${svc.getStorageKey()}]:`, newState)\n })\n return () => sub.unsubscribe()\n }, [svc, debug])\n\n // Cleanup owned service on unmount\n useEffect(() => {\n return () => {\n if (ownsServiceRef.current && serviceRef.current) {\n if (debug) {\n console.log(`useFiltersAndSort: destroying owned service \"${serviceRef.current.getStorageKey()}\"`)\n }\n serviceRef.current.destroy()\n serviceRef.current = null\n }\n }\n }, [debug])\n\n // Stable callback references using useCallback\n const setFilter = useCallback(\n <K extends keyof T>(key: K, value: T[K]) => {\n svc.setFilters({ [key]: value } as unknown as Partial<T>)\n },\n [svc]\n )\n\n const setFilters = useCallback(\n (filters: Partial<T>) => {\n svc.setFilters(filters)\n },\n [svc]\n )\n\n const clearFilter = useCallback(\n <K extends keyof T>(key: K) => {\n svc.removeFilter(key)\n },\n [svc]\n )\n\n const clearAllFilters = useCallback(() => {\n svc.clearFilters()\n }, [svc])\n\n const hasFilter = useCallback(\n <K extends keyof T>(key: K): boolean => {\n const value = svc.getCurrentState().filters[key]\n const defaultValue = defaultFiltersRef.current?.[key]\n if (value === defaultValue) return false\n if (value === null || value === undefined || value === '') return false\n if (Array.isArray(value) && value.length === 0) return false\n return true\n },\n [svc]\n )\n\n const hasAnyFilters = useCallback((): boolean => {\n return countActiveFilters(svc.getCurrentState().filters, defaultFiltersRef.current) > 0\n }, [svc])\n\n const setSort = useCallback(\n (field: string) => {\n svc.setSort(field)\n },\n [svc]\n )\n\n const clearSort = useCallback(() => {\n svc.setSort('')\n }, [svc])\n\n const isSortedBy = useCallback(\n (field: string): boolean => {\n return svc.getCurrentState().sort?.fieldName === field\n },\n [svc]\n )\n\n const setPage = useCallback(\n (page: number) => {\n if (page < 0) {\n if (debug) {\n console.warn('useFiltersAndSort: setPage called with negative value, clamping to 0')\n }\n page = 0\n }\n svc.setPagination(page)\n },\n [svc, debug]\n )\n\n const setPageSize = useCallback(\n (size: number) => {\n if (size < 1) {\n if (debug) {\n console.warn('useFiltersAndSort: setPageSize called with value < 1, clamping to 1')\n }\n size = 1\n }\n const currentPage = svc.getCurrentState().pagination.page\n svc.setPagination(currentPage, size)\n },\n [svc, debug]\n )\n\n const nextPage = useCallback(() => {\n svc.setPagination(svc.getCurrentState().pagination.page + 1)\n }, [svc])\n\n const prevPage = useCallback(() => {\n const newPage = Math.max(0, svc.getCurrentState().pagination.page - 1)\n svc.setPagination(newPage)\n }, [svc])\n\n const firstPage = useCallback(() => {\n svc.setPagination(0)\n }, [svc])\n\n const setQuery = useCallback(\n (query: string) => {\n svc.setQuery(query)\n },\n [svc]\n )\n\n const clearQuery = useCallback(() => {\n svc.setQuery('')\n }, [svc])\n\n const reset = useCallback(() => {\n svc.reset()\n }, [svc])\n\n const clearStorageAndReset = useCallback(() => {\n svc.clearStorage()\n svc.reset()\n }, [svc])\n\n const getService = useCallback(() => svc, [svc])\n\n // Derived values\n const activeFilterCount = useMemo(\n () => countActiveFilters(state.filters, defaultFiltersRef.current),\n [state.filters]\n )\n\n const isFirstPage = state.pagination.page === 0\n const offset = state.pagination.page * state.pagination.limit\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(\n () => ({\n // State\n filters: state.filters,\n sortBy: state.sort?.fieldName ?? null,\n sortDirection: state.sort?.direction ?? 'asc',\n page: state.pagination.page,\n pageSize: state.pagination.limit,\n query: state.query,\n state,\n\n // Filter actions\n setFilter,\n setFilters,\n clearFilter,\n clearAllFilters,\n hasFilter,\n hasAnyFilters,\n activeFilterCount,\n\n // Sort actions\n setSort,\n toggleSort: setSort,\n clearSort,\n isSortedBy,\n\n // Pagination actions\n setPage,\n setPageSize,\n nextPage,\n prevPage,\n firstPage,\n isFirstPage,\n offset,\n\n // Query actions\n setQuery,\n clearQuery,\n\n // General actions\n reset,\n clearStorageAndReset,\n storageKey: svc.getStorageKey(),\n\n // Service access\n getService,\n }),\n [\n state,\n setFilter,\n setFilters,\n clearFilter,\n clearAllFilters,\n hasFilter,\n hasAnyFilters,\n activeFilterCount,\n setSort,\n clearSort,\n isSortedBy,\n setPage,\n setPageSize,\n nextPage,\n prevPage,\n firstPage,\n isFirstPage,\n offset,\n setQuery,\n clearQuery,\n reset,\n clearStorageAndReset,\n svc,\n getService,\n ]\n )\n}\n\n/**\n * Convenience hook for creating a filters service with simpler API\n * Equivalent to useFiltersAndSort({ config: ... })\n */\nexport function useFilters<T extends Record<string, any> = Record<string, any>>(\n storageKey: string,\n options?: Omit<FiltersAndSortConfig<T>, 'storageKey'>\n): UseFiltersAndSortReturn<T> {\n return useFiltersAndSort<T>({\n config: {\n storageKey,\n ...options,\n },\n })\n}\n\nexport type { FilterAndSortState, SortDirection }\n","// React hook for FormBuilder with proper state management\nimport { FormBuilder } from '@codella-software/utils'\nimport { useEffect, useMemo, useState } from 'react'\n\n/**\n * Hook options for useFormBuilder\n */\nexport interface UseFormBuilderOptions<T extends Record<string, any>> {\n /** FormBuilder instance */\n builder: FormBuilder<T>\n}\n\n/**\n * Hook return value for useFormBuilder\n */\nexport interface UseFormBuilderReturn<T extends Record<string, any>> {\n /** Current form values */\n values: T\n /** Form validation errors */\n errors: Record<string, string | undefined>\n /** Form touched fields */\n touched: Record<string, boolean>\n /** Current form step (for multi-step forms) */\n currentStep: number\n /** Total form steps */\n totalSteps: number\n /** Is form currently submitting */\n isSubmitting: boolean\n /** Is form valid */\n isValid: boolean\n /** Set field value */\n setFieldValue: (field: keyof T, value: any) => Promise<void>\n /** Set field touched */\n setFieldTouched: (field: keyof T, touched: boolean) => void\n /** Validate entire form */\n validate: () => Promise<Record<string, string | undefined>>\n /** Submit form */\n submit: () => Promise<T | null>\n /** Reset form to initial values */\n reset: () => void\n /** Go to next step */\n nextStep: () => void\n /** Go to previous step */\n prevStep: () => void\n}\n\n/**\n * React hook that wraps FormBuilder and provides form state management\n *\n * @param options - Hook options including FormBuilder instance\n * @returns Form state and methods\n *\n * @example\n * ```tsx\n * const builder = new FormBuilder<{ name: string }>()\n * .addField({ name: 'name', type: 'text', label: 'Name' })\n * .build();\n *\n * function MyForm() {\n * const form = useFormBuilder({ builder });\n * return (\n * <form onSubmit={(e) => { e.preventDefault(); form.submit(); }}>\n * <input\n * value={form.values.name}\n * onChange={(e) => form.setFieldValue('name', e.target.value)}\n * />\n * {form.errors.name && <span>{form.errors.name}</span>}\n * <button type=\"submit\">Submit</button>\n * </form>\n * );\n * }\n * ```\n */\nexport function useFormBuilder<T extends Record<string, any>>(\n options: UseFormBuilderOptions<T>\n): UseFormBuilderReturn<T> {\n const { builder } = options\n\n if (!builder) {\n throw new Error('useFormBuilder: builder is required')\n }\n\n // Subscribe to form state\n const [state, setState] = useState(() => builder.currentState)\n const [isSubmitting, setIsSubmitting] = useState(false)\n\n useEffect(() => {\n if (!builder || !builder.state$) {\n return\n }\n\n const subscription = builder.state$.subscribe((newState: any) => {\n setState(newState)\n })\n\n return () => subscription.unsubscribe()\n }, [builder])\n\n const memoizedReturn = useMemo(() => ({\n values: state.values,\n errors: state.errors,\n touched: state.touched,\n currentStep: 0, // Multi-step requires MultiStepFormBuilder\n totalSteps: 1, // Multi-step requires MultiStepFormBuilder\n isSubmitting,\n isValid: Object.keys(state.errors).length === 0,\n\n setFieldValue: async (field: keyof T, value: any) => {\n builder.setFieldValue(field as string, value)\n },\n\n setFieldTouched: (field: keyof T, touched: boolean) => {\n builder.setFieldTouched(field as string, touched)\n },\n\n validate: async () => {\n return builder.validate()\n },\n\n submit: async () => {\n setIsSubmitting(true)\n try {\n await builder.submit()\n setIsSubmitting(false)\n return null\n } catch (error) {\n setIsSubmitting(false)\n throw error\n }\n },\n\n reset: () => {\n builder.reset()\n },\n\n nextStep: () => {\n // Multi-step forms use MultiStepFormBuilder, not FormBuilder\n console.warn('nextStep() is only available on MultiStepFormBuilder')\n },\n\n prevStep: () => {\n // Multi-step forms use MultiStepFormBuilder, not FormBuilder\n console.warn('prevStep() is only available on MultiStepFormBuilder')\n },\n }), [builder, state, isSubmitting])\n\n return memoizedReturn\n}\n","import { FC, ReactNode, createContext, useContext, useEffect, useState } from 'react';\n\nimport type { IAuthProvider } from '@codella-software/utils';\nimport {\n LiveUpdateService,\n SSEService,\n WebsocketService,\n type EventMapping,\n type LiveUpdateType,\n} from '@codella-software/utils/live-updates';\n\nexport interface LiveUpdateContextValue {\n service: LiveUpdateService;\n isConnected: boolean;\n type: LiveUpdateType;\n canSend: boolean;\n}\n\nconst LiveUpdateContext = createContext<LiveUpdateContextValue | null>(null);\n\n/**\n * Hook to access the LiveUpdateContext\n * Must be used within a LiveUpdateProvider\n */\nexport const useLiveUpdateContext = () => {\n const context = useContext(LiveUpdateContext);\n if (!context) {\n throw new Error('useLiveUpdateContext must be used within LiveUpdateProvider');\n }\n return context;\n};\n\nexport interface LiveUpdateProviderProps {\n children: ReactNode;\n authProvider: IAuthProvider;\n sseUrl?: string;\n wsUrl?: string;\n sseEnabled?: boolean;\n wsEnabled?: boolean;\n eventMappings?: Record<string, EventMapping>;\n}\n\n/**\n * Provider component for live updates service\n * Manages service initialization and connection state\n * \n * @example\n * ```tsx\n * import { LiveUpdateProvider } from '@codella/react';\n * import { createFirebaseAuthProvider } from '@codella-software/utils';\n * \n * function App() {\n * const authProvider = createFirebaseAuthProvider({ auth: getAuth() });\n * \n * return (\n * <LiveUpdateProvider\n * authProvider={authProvider}\n * sseUrl=\"http://api.example.com/sse\"\n * wsUrl=\"ws://api.example.com/ws\"\n * eventMappings={{\n * 'notifications': { sseEvent: 'GET_NOTIFICATIONS' }\n * }}\n * >\n * <YourApp />\n * </LiveUpdateProvider>\n * );\n * }\n * ```\n */\nexport const LiveUpdateProvider: FC<LiveUpdateProviderProps> = ({\n children,\n authProvider,\n sseUrl = 'http://localhost:3000/sse',\n wsUrl = 'ws://localhost:3000/ws',\n sseEnabled = true,\n wsEnabled = true,\n eventMappings = {},\n}) => {\n const [isConnected, setIsConnected] = useState(false);\n const [service] = useState(() => {\n const sseService = new SSEService({\n url: sseUrl,\n authProvider,\n });\n\n const wsService = new WebsocketService({\n url: wsUrl,\n authProvider,\n });\n\n return new LiveUpdateService(wsService, sseService);\n });\n\n useEffect(() => {\n if (authProvider.isAuthenticated()) {\n service.initialize({\n sseEnabled,\n wsEnabled,\n authProvider,\n eventMappings,\n });\n } else {\n service.disconnect();\n }\n\n const subscription = service.isConnected$.subscribe(setIsConnected);\n\n return () => {\n subscription.unsubscribe();\n };\n }, [authProvider, sseEnabled, wsEnabled, service, eventMappings]);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n service.disconnect();\n };\n }, [service]);\n\n return (\n <LiveUpdateContext.Provider\n value={{\n service,\n isConnected,\n type: service.type,\n canSend: service.canSend,\n }}\n >\n {children}\n </LiveUpdateContext.Provider>\n );\n};\n","import { useEffect, useState } from 'react';\n\nimport type { WS_ACTIONS } from '@codella-software/utils/live-updates';\nimport { useLiveUpdateContext } from './LiveUpdateProvider';\n\n/**\n * Hook to listen for specific WebSocket actions\n * @param action - The WS_ACTIONS enum value to listen for\n * @param enabled - Whether to subscribe (defaults to true)\n * @returns The latest message payload or null\n */\nexport const useLiveListener = <T = unknown>(action: WS_ACTIONS, enabled = true) => {\n const { service } = useLiveUpdateContext();\n const [data, setData] = useState<T | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n if (!enabled || !service) {\n return;\n }\n\n const subscription = service.on(action).subscribe({\n next: (value) => {\n setData(value);\n setError(null);\n },\n error: (err) => {\n setError(err instanceof Error ? err : new Error(String(err)));\n },\n });\n\n return () => {\n subscription.unsubscribe();\n };\n }, [action, enabled, service]);\n\n return { data, error };\n};\n","import { useCallback } from 'react';\n\nimport type { WS_ACTIONS } from '@codella-software/utils/live-updates';\nimport { useLiveUpdateContext } from './LiveUpdateProvider';\n\n/**\n * Hook for request-response pattern over WebSocket\n * @param sendAction - The action to send\n * @param responseAction - The action to wait for\n * @returns Function to send request and promise for response\n */\nexport const useLiveRequest = <TReq = unknown, TRes = unknown>() => {\n const { service, canSend } = useLiveUpdateContext();\n\n const request = useCallback(\n (sendAction: WS_ACTIONS, responseAction: WS_ACTIONS, payload?: TReq) => {\n if (!canSend) {\n return Promise.reject(new Error('WebSocket not available for sending'));\n }\n\n return service\n .on(responseAction)\n .toPromise()\n .catch(() => {\n throw new Error(`No response received for action: ${responseAction}`);\n });\n },\n [canSend, service],\n );\n\n return { request, canSend };\n};\n","import { useCallback } from 'react';\n\nimport type { WS_ACTIONS } from '@codella-software/utils/live-updates';\nimport { useLiveUpdateContext } from './LiveUpdateProvider';\n\nexport interface UseUpdateListenerOptions {\n enabled?: boolean;\n}\n\n/**\n * Hook to listen for and handle live updates with callback\n * @param action - The WS_ACTIONS enum value to listen for\n * @param onUpdate - Callback when update is received\n * @param enabled - Whether to subscribe (defaults to true)\n */\nexport const useLiveUpdateListener = <T = unknown>(\n action: WS_ACTIONS,\n onUpdate: (data: T) => void,\n { enabled = true }: UseUpdateListenerOptions = {},\n) => {\n const { service } = useLiveUpdateContext();\n\n const handleUpdate = useCallback(\n (data: T) => {\n onUpdate(data);\n },\n [onUpdate],\n );\n\n // Subscribe to updates\n // The subscription is managed by the effect in the hook\n // Users need to call subscribe manually or use useLiveListener instead\n const subscribe = useCallback(() => {\n if (!enabled || !service) {\n return () => {};\n }\n\n const subscription = service.on(action).subscribe(handleUpdate);\n\n return () => {\n subscription.unsubscribe();\n };\n }, [action, enabled, service, handleUpdate]);\n\n return { subscribe };\n};\n","import { useEffect, useState } from 'react';\n\nimport { useLiveUpdateContext } from './LiveUpdateProvider';\n\n/**\n * Hook to subscribe to live update events\n * @param eventName - The event name (must be mapped in LiveUpdateProvider.eventMappings)\n * @returns The latest event data or null\n */\nexport const useLiveUpdates = <T = unknown>(eventName: string) => {\n const { service } = useLiveUpdateContext();\n const [data, setData] = useState<T | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n if (!service) {\n return;\n }\n\n const subscription = service.on(eventName).subscribe({\n next: (value) => {\n setData(value);\n setError(null);\n },\n error: (err) => {\n setError(err instanceof Error ? err : new Error(String(err)));\n },\n });\n\n return () => {\n subscription.unsubscribe();\n };\n }, [eventName, service]);\n\n return { data, error };\n};\n","/**\n * React hook for RichContentService\n */\n\nimport {\n ContentEditableAdapter,\n DocumentNode,\n MarkType,\n RichContentConfig,\n RichContentService,\n RichContentState,\n Selection,\n} from '@codella-software/utils/rich-content';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\n/**\n * Hook options for useRichContent\n */\nexport interface UseRichContentOptions extends RichContentConfig {\n /** DOM element ref for contentEditable */\n editorRef?: React.RefObject<HTMLElement>;\n /** Custom adapter instance */\n adapter?: ContentEditableAdapter;\n}\n\n/**\n * Hook return value for useRichContent\n */\nexport interface UseRichContentReturn {\n /** Service instance */\n service: RichContentService;\n /** Current document content */\n content: DocumentNode;\n /** Full state object */\n state: RichContentState;\n /** Current editor focus state */\n isFocused: boolean;\n /** Can undo */\n canUndo: boolean;\n /** Can redo */\n canRedo: boolean;\n /** Active mark types */\n selectedFormats: Set<MarkType>;\n /** Current selection */\n selection: Selection | null;\n /** Is dirty (has unsaved changes) */\n isDirty: boolean;\n\n // Commands\n /** Insert text */\n insertText: (text: string) => void;\n /** Insert paragraph */\n insertParagraph: () => void;\n /** Insert heading */\n insertHeading: (level: 1 | 2 | 3 | 4 | 5 | 6) => void;\n /** Insert image */\n insertImage: (url: string) => void;\n /** Upload image file */\n uploadImage: (file: File) => Promise<void>;\n /** Insert mention */\n insertMention: (id: string, label: string) => void;\n /** Insert list */\n insertList: (type: 'ordered' | 'unordered') => void;\n /** Toggle mark */\n toggleMark: (mark: MarkType) => void;\n /** Delete content */\n deleteContent: () => void;\n /** Undo */\n undo: () => void;\n /** Redo */\n redo: () => void;\n /** Clear history */\n clearHistory: () => void;\n /** Focus editor */\n focus: () => void;\n /** Set focus state */\n setFocus: (focused: boolean) => void;\n /** Get plain text */\n getPlainText: () => string;\n /** Set selection */\n setSelection: (selection: Selection | null) => void;\n}\n\n/**\n * React hook that wraps RichContentService and manages state subscriptions\n */\nexport function useRichContent(options: UseRichContentOptions = {}): UseRichContentReturn {\n // Create service instance\n const service = useMemo(() => RichContentService.create(options), []);\n\n // State from service\n const [content, setContent] = useState<DocumentNode>(service.getContent());\n const [state, setState] = useState<RichContentState>(service.getState());\n const [isFocused, setIsFocused] = useState(false);\n const [canUndo, setCanUndo] = useState(false);\n const [canRedo, setCanRedo] = useState(false);\n const [selectedFormats, setSelectedFormats] = useState<Set<MarkType>>(new Set());\n\n // Subscriptions\n useEffect(() => {\n if (!service) {\n return\n }\n\n const contentSub = service.getContent$().subscribe(setContent);\n const stateSub = service.getState$().subscribe((newState) => {\n setState(newState);\n setIsFocused(newState.isFocused);\n setCanUndo(newState.canUndo);\n setCanRedo(newState.canRedo);\n setSelectedFormats(newState.selectedFormats || new Set());\n });\n\n return () => {\n contentSub.unsubscribe();\n stateSub.unsubscribe();\n };\n }, [service]);\n\n // Attach adapter if provided\n useEffect(() => {\n if (options.adapter) {\n service.attachAdapter(options.adapter);\n return () => service.detachAdapter();\n }\n }, [options.adapter, service]);\n\n // Setup default adapter if editorRef provided\n const defaultAdapterRef = useRef<ContentEditableAdapter | null>(null);\n\n useEffect(() => {\n if (options.editorRef?.current && !options.adapter && !defaultAdapterRef.current) {\n const { DefaultContentEditableAdapter } = require('@codella-software/utils/rich-content');\n const adapter = new DefaultContentEditableAdapter();\n adapter.mount(options.editorRef.current);\n service.attachAdapter(adapter);\n defaultAdapterRef.current = adapter;\n\n return () => {\n adapter.unmount();\n service.detachAdapter();\n defaultAdapterRef.current = null;\n };\n }\n }, [options.editorRef, options.adapter, service]);\n\n // Command callbacks\n const insertText = useCallback((text: string) => service.insertText(text), [service]);\n const insertParagraph = useCallback(() => service.insertParagraph(), [service]);\n const insertHeading = useCallback((level: any) => service.insertHeading(level), [service]);\n const insertImage = useCallback((url: string) => service.insertImage(url), [service]);\n const uploadImage = useCallback((file: File) => service.uploadImage(file), [service]);\n const insertMention = useCallback((id: string, label: string) => service.insertMention(id, label), [service]);\n const insertList = useCallback((type: 'ordered' | 'unordered') => service.insertList(type), [service]);\n const toggleMark = useCallback((mark: MarkType) => service.toggleMark(mark), [service]);\n const deleteContent = useCallback(() => service.deleteContent(), [service]);\n const undo = useCallback(() => service.undo(), [service]);\n const redo = useCallback(() => service.redo(), [service]);\n const clearHistory = useCallback(() => service.clearHistory(), [service]);\n const focus = useCallback(() => service.getAdapter()?.focus(), [service]);\n const setFocus = useCallback((focused: boolean) => service.setFocus(focused), [service]);\n const getPlainText = useCallback(() => service.getPlainText(), [service]);\n const setSelection = useCallback((sel: Selection | null) => service.setSelection(sel), [service]);\n\n return {\n service,\n content,\n state,\n isFocused,\n canUndo,\n canRedo,\n selectedFormats,\n selection: state.selection || null,\n isDirty: state.isDirty,\n\n insertText,\n insertParagraph,\n insertHeading,\n insertImage,\n uploadImage,\n insertMention,\n insertList,\n toggleMark,\n deleteContent,\n undo,\n redo,\n clearHistory,\n focus,\n setFocus,\n getPlainText,\n setSelection,\n };\n}\n","/**\n * @deprecated TableBuilder is a configuration builder, not a stateful service.\n * \n * For reactive table state management, use FiltersAndSortService instead:\n * \n * @example\n * ```tsx\n * import { createFiltersAndSortService } from '@codella-software/utils';\n * import { useFiltersAndSort } from '@codella-software/react';\n * \n * const service = createFiltersAndSortService({\n * storageKey: 'my-table',\n * initialState: { pagination: { page: 0, limit: 10 } }\n * });\n * \n * function MyTable() {\n * const { page, pageSize, filters, setFilter, setPage } = useFiltersAndSort({ service });\n * \n * // Fetch data with current state\n * const { data } = useQuery({\n * queryKey: ['data', page, pageSize, filters],\n * queryFn: () => fetchData({ page, pageSize, ...filters })\n * });\n * \n * // Build table config\n * const tableConfig = createTableBuilder()\n * .data(data?.items ?? [])\n * .totalRows(data?.total ?? 0)\n * .columns([...])\n * .filtersAndSort(service.getCurrentState(), service.getActions())\n * .build();\n * \n * return <DynamicTable config={tableConfig} />;\n * }\n * ```\n */\nexport function useTableService() {\n throw new Error(\n 'useTableService is deprecated. TableBuilder is a configuration builder, not a stateful service. ' +\n 'Use FiltersAndSortService with useFiltersAndSort hook for reactive state management. ' +\n 'See documentation: https://github.com/CodellaSoftware/codella-utils'\n );\n}\n","import type { TabChangeEvent, TabsConfig } from '@codella-software/utils/tabs';\nimport { TabsService } from '@codella-software/utils/tabs';\nimport React, { createContext, useContext, useEffect, useState } from 'react';\n\ninterface TabsContextValue {\n service: TabsService;\n activeTabId: string;\n tabs: ReturnType<TabsService['getTabs']>;\n}\n\nconst TabsContext = createContext<TabsContextValue | undefined>(undefined);\n\n/**\n * Props for TabsProvider\n */\nexport interface TabsProviderProps {\n config: TabsConfig;\n children: React.ReactNode;\n}\n\n/**\n * Tabs provider for React\n */\nexport function TabsProvider({ config, children }: TabsProviderProps) {\n const [service] = useState(() => new TabsService(config));\n const [activeTabId, setActiveTabId] = useState(() => service.getActiveTabId());\n const [tabs, setTabs] = useState(() => service.getTabs());\n\n useEffect(() => {\n const activeTabSubscription = service.getActiveTabId$().subscribe(setActiveTabId);\n const tabsSubscription = service.getTabs$().subscribe(setTabs);\n\n return () => {\n activeTabSubscription.unsubscribe();\n tabsSubscription.unsubscribe();\n };\n }, [service]);\n\n useEffect(() => {\n return () => {\n service.destroy();\n };\n }, [service]);\n\n return (\n <TabsContext.Provider value={{ service, activeTabId, tabs }}>\n {children}\n </TabsContext.Provider>\n );\n}\n\n/**\n * Hook to access tabs service from context\n */\nexport function useTabsContext(): TabsContextValue {\n const context = useContext(TabsContext);\n if (!context) {\n throw new Error('useTabsContext must be used within a TabsProvider');\n }\n return context;\n}\n\n/**\n * Hook to use tabs service directly\n */\nexport function useTabsService() {\n const { service } = useTabsContext();\n return service;\n}\n\n/**\n * Hook to get active tab\n */\nexport function useActiveTab() {\n const { service } = useTabsContext();\n const [activeTab, setActiveTab] = useState(() => service.getActiveTab());\n\n useEffect(() => {\n const subscription = service.getActiveTab$().subscribe(setActiveTab);\n return () => subscription.unsubscribe();\n }, [service]);\n\n return activeTab;\n}\n\n/**\n * Hook to get all tabs\n */\nexport function useTabs() {\n const { tabs } = useTabsContext();\n return tabs;\n}\n\n/**\n * Hook to listen to tab changes\n */\nexport function useTabChange() {\n const { service } = useTabsContext();\n const [changeEvent, setChangeEvent] = useState<TabChangeEvent | null>(null);\n\n useEffect(() => {\n const subscription = service.onTabChange$().subscribe(setChangeEvent);\n return () => subscription.unsubscribe();\n }, [service]);\n\n return changeEvent;\n}\n\n/**\n * Hook to set active tab\n */\nexport function useSetActiveTab() {\n const { service } = useTabsContext();\n return {\n setActiveTab: (tabId: string) => service.setActiveTab(tabId),\n setActiveTabByIndex: (index: number) => service.setActiveTabByIndex(index),\n nextTab: () => service.nextTab(),\n previousTab: () => service.previousTab(),\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/filters-and-sort/useFiltersAndSort.ts","../src/form-builder/useFormBuilder.ts","../src/live-updates/LiveUpdateProvider.tsx","../src/live-updates/useLiveListener.ts","../src/live-updates/useLiveRequest.ts","../src/live-updates/useLiveUpdateListener.ts","../src/live-updates/useLiveUpdates.ts","../src/rich-content/useRichContent.ts","../src/tabs/useTabsHooks.tsx"],"sourcesContent":["import { FiltersAndSortService, type FilterAndSortState, type SortDirection } from '@codella-software/utils'\nimport { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from 'react'\n\n/**\n * Configuration for creating a new FiltersAndSortService instance\n */\nexport interface FiltersAndSortConfig<T extends Record<string, any> = Record<string, any>> {\n /** Unique storage key for persistence */\n storageKey: string\n /** Whether to persist state to storage (default: true) */\n persistToStorage?: boolean\n /** Initial state override */\n initialState?: Partial<FilterAndSortState<T>>\n /** Default filter values used when clearing filters */\n defaultFilters?: T\n /** Skip hydrating state from storage on init */\n skipStorageHydration?: boolean\n /** Debounce time for storage persistence in ms (default: 300) */\n storageDebounceMs?: number\n /** Optional storage key prefix */\n storageKeyPrefix?: string\n}\n\n/**\n * Hook options for useFiltersAndSort\n */\nexport interface UseFiltersAndSortOptions<T extends Record<string, any> = Record<string, any>> {\n /** An existing FiltersAndSortService instance to use */\n service?: FiltersAndSortService<T>\n /** Configuration to create a new service instance (mutually exclusive with service) */\n config?: FiltersAndSortConfig<T>\n /** Enable debug logging */\n debug?: boolean\n /** Custom equality function for state comparison */\n isEqual?: (prev: FilterAndSortState<T>, next: FilterAndSortState<T>) => boolean\n}\n\n/**\n * Hook return value for useFiltersAndSort\n */\nexport interface UseFiltersAndSortReturn<T extends Record<string, any> = Record<string, any>> {\n // State\n /** Current filter state */\n filters: T\n /** Current sort field */\n sortBy: string | null\n /** Current sort direction */\n sortDirection: SortDirection\n /** Current page (0-based) */\n page: number\n /** Page size */\n pageSize: number\n /** Current search query */\n query: string\n /** Full state object for advanced use cases */\n state: FilterAndSortState<T>\n\n // Filter actions\n /** Set a single filter value */\n setFilter: <K extends keyof T>(key: K, value: T[K]) => void\n /** Set multiple filters at once */\n setFilters: (filters: Partial<T>) => void\n /** Clear a specific filter */\n clearFilter: <K extends keyof T>(key: K) => void\n /** Clear all filters (resets to defaultFilters) */\n clearAllFilters: () => void\n /** Check if a specific filter is active */\n hasFilter: <K extends keyof T>(key: K) => boolean\n /** Check if any filters are active */\n hasAnyFilters: () => boolean\n /** Get count of active filters */\n activeFilterCount: number\n\n // Sort actions\n /** Set/toggle sort (auto-toggles between asc -> desc -> none) */\n setSort: (field: string) => void\n /** Toggle sort direction for field (alias for setSort) */\n toggleSort: (field: string) => void\n /** Clear current sort */\n clearSort: () => void\n /** Check if a field is currently sorted */\n isSortedBy: (field: string) => boolean\n\n // Pagination actions\n /** Set page */\n setPage: (page: number) => void\n /** Set page size (resets to page 0) */\n setPageSize: (size: number) => void\n /** Set both page and pageSize atomically */\n setPagination: (page: number, size?: number) => void\n /** Go to next page */\n nextPage: () => void\n /** Go to previous page */\n prevPage: () => void\n /** Go to first page */\n firstPage: () => void\n /** Check if on first page */\n isFirstPage: boolean\n /** Calculate offset for API requests */\n offset: number\n\n // Query actions\n /** Set search query */\n setQuery: (query: string) => void\n /** Clear search query */\n clearQuery: () => void\n\n // General actions\n /** Reset to initial state */\n reset: () => void\n /** Clear storage and reset */\n clearStorageAndReset: () => void\n /** Get the storage key being used */\n storageKey: string\n\n // Service access\n /** Direct access to the underlying service (for advanced use cases) */\n getService: () => FiltersAndSortService<T>\n}\n\n/**\n * Default shallow equality check for filter state\n */\nfunction defaultIsEqual<T>(prev: FilterAndSortState<T>, next: FilterAndSortState<T>): boolean {\n if (prev === next) return true\n\n // Quick reference equality checks\n if (prev.query !== next.query) return false\n if (prev.pagination.page !== next.pagination.page) return false\n if (prev.pagination.limit !== next.pagination.limit) return false\n\n // Sort comparison\n const prevSort = prev.sort\n const nextSort = next.sort\n if (prevSort?.fieldName !== nextSort?.fieldName) return false\n if (prevSort?.direction !== nextSort?.direction) return false\n\n // Shallow filter comparison\n const prevFilters = prev.filters as Record<string, unknown>\n const nextFilters = next.filters as Record<string, unknown>\n const prevKeys = Object.keys(prevFilters)\n const nextKeys = Object.keys(nextFilters)\n\n if (prevKeys.length !== nextKeys.length) return false\n\n for (const key of prevKeys) {\n if (prevFilters[key] !== nextFilters[key]) return false\n }\n\n return true\n}\n\n/**\n * Count active filters (non-null, non-undefined, non-empty string values)\n */\nfunction countActiveFilters<T extends Record<string, unknown>>(filters: T, defaultFilters?: T): number {\n let count = 0\n const keys = Object.keys(filters as Record<string, unknown>) as Array<keyof T>\n for (const key of keys) {\n const value = filters[key]\n const defaultValue = defaultFilters?.[key]\n const isDefault = value === defaultValue\n const isEmpty = value === null || value === undefined || value === ''\n const isEmptyArray = Array.isArray(value) && value.length === 0\n\n if (!isEmpty && !isEmptyArray && !isDefault) {\n count++\n }\n }\n return count\n}\n\n/**\n * React hook that wraps FiltersAndSortService with enhanced functionality\n *\n * @typeParam T - The filters object type\n * @param options - Hook options including service instance or config\n * @returns Filters and sort state with control methods\n *\n * @example\n * ```tsx\n * // Option 1: Create service externally (recommended for sharing between components)\n * const service = new FiltersAndSortService<MyFilters>({ storageKey: 'my-filters' });\n *\n * function MyTable() {\n * const {\n * filters,\n * setFilter,\n * clearAllFilters,\n * hasAnyFilters,\n * activeFilterCount\n * } = useFiltersAndSort({ service });\n *\n * return (\n * <div>\n * <input\n * value={filters.name || ''}\n * onChange={(e) => setFilter('name', e.target.value)}\n * />\n * {hasAnyFilters() && (\n * <button onClick={clearAllFilters}>\n * Clear ({activeFilterCount})\n * </button>\n * )}\n * </div>\n * );\n * }\n *\n * // Option 2: Let the hook create the service (simpler for single-component use)\n * function SimpleTable() {\n * const filters = useFiltersAndSort({\n * config: {\n * storageKey: 'simple-table',\n * defaultFilters: { status: 'all' }\n * }\n * });\n * // ...\n * }\n * ```\n */\nexport function useFiltersAndSort<T extends Record<string, any> = Record<string, any>>(\n options: UseFiltersAndSortOptions<T>\n): UseFiltersAndSortReturn<T> {\n const { service: externalService, config, debug = false, isEqual = defaultIsEqual } = options\n\n // Validate options\n if (!externalService && !config) {\n throw new Error(\n 'useFiltersAndSort: either `service` or `config` must be provided. ' +\n 'Pass an existing FiltersAndSortService instance via `service`, or ' +\n 'provide a `config` object to create a new service.'\n )\n }\n\n if (externalService && config) {\n if (debug) {\n console.warn(\n 'useFiltersAndSort: both `service` and `config` were provided. ' +\n 'The `service` will be used and `config` will be ignored.'\n )\n }\n }\n\n // Track if we own the service (created it ourselves)\n const ownsServiceRef = useRef(false)\n const serviceRef = useRef<FiltersAndSortService<T> | null>(null)\n const isEqualRef = useRef(isEqual)\n\n // Keep isEqual ref updated\n useEffect(() => {\n isEqualRef.current = isEqual\n }, [isEqual])\n\n // Initialize service (only once)\n if (!serviceRef.current) {\n if (externalService) {\n serviceRef.current = externalService\n ownsServiceRef.current = false\n } else if (config) {\n serviceRef.current = new FiltersAndSortService<T>({\n storageKey: config.storageKey,\n persistToStorage: config.persistToStorage,\n initialState: config.initialState,\n defaultFilters: config.defaultFilters,\n skipStorageHydration: config.skipStorageHydration,\n storageDebounceMs: config.storageDebounceMs,\n storageKeyPrefix: config.storageKeyPrefix,\n })\n ownsServiceRef.current = true\n\n if (debug) {\n console.log(`useFiltersAndSort: created service with key \"${config.storageKey}\"`)\n }\n }\n }\n\n const svc = serviceRef.current!\n\n // Get default filters for active filter counting\n const defaultFiltersRef = useRef<T | undefined>(config?.defaultFilters)\n\n // Use useSyncExternalStore for better concurrent mode support\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n const subscription = svc.getState().subscribe(() => {\n onStoreChange()\n })\n return () => subscription.unsubscribe()\n },\n [svc]\n )\n\n const getSnapshot = useCallback(() => svc.getCurrentState(), [svc])\n\n // For SSR - return initial state\n const getServerSnapshot = useCallback(() => svc.getDefaultState(), [svc])\n\n const state = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)\n\n // Debug logging\n useEffect(() => {\n if (!debug) return\n\n const sub = svc.getState().subscribe((newState) => {\n console.log(`useFiltersAndSort [${svc.getStorageKey()}]:`, newState)\n })\n return () => sub.unsubscribe()\n }, [svc, debug])\n\n // Cleanup owned service on unmount\n useEffect(() => {\n return () => {\n if (ownsServiceRef.current && serviceRef.current) {\n if (debug) {\n console.log(`useFiltersAndSort: destroying owned service \"${serviceRef.current.getStorageKey()}\"`)\n }\n serviceRef.current.destroy()\n serviceRef.current = null\n }\n }\n }, [debug])\n\n // Stable callback references using useCallback\n const setFilter = useCallback(\n <K extends keyof T>(key: K, value: T[K]) => {\n svc.setFilters({ [key]: value } as unknown as Partial<T>)\n },\n [svc]\n )\n\n const setFilters = useCallback(\n (filters: Partial<T>) => {\n svc.setFilters(filters)\n },\n [svc]\n )\n\n const clearFilter = useCallback(\n <K extends keyof T>(key: K) => {\n svc.removeFilter(key)\n },\n [svc]\n )\n\n const clearAllFilters = useCallback(() => {\n svc.clearFilters()\n }, [svc])\n\n const hasFilter = useCallback(\n <K extends keyof T>(key: K): boolean => {\n const value = svc.getCurrentState().filters[key]\n const defaultValue = defaultFiltersRef.current?.[key]\n if (value === defaultValue) return false\n if (value === null || value === undefined || value === '') return false\n if (Array.isArray(value) && value.length === 0) return false\n return true\n },\n [svc]\n )\n\n const hasAnyFilters = useCallback((): boolean => {\n return countActiveFilters(svc.getCurrentState().filters, defaultFiltersRef.current) > 0\n }, [svc])\n\n const setSort = useCallback(\n (field: string) => {\n svc.setSort(field)\n },\n [svc]\n )\n\n const clearSort = useCallback(() => {\n svc.setSort('')\n }, [svc])\n\n const isSortedBy = useCallback(\n (field: string): boolean => {\n return svc.getCurrentState().sort?.fieldName === field\n },\n [svc]\n )\n\n const setPage = useCallback(\n (page: number) => {\n if (page < 0) {\n if (debug) {\n console.warn('useFiltersAndSort: setPage called with negative value, clamping to 0')\n }\n page = 0\n }\n svc.setPagination(page)\n },\n [svc, debug]\n )\n\n const setPageSize = useCallback(\n (size: number) => {\n if (size < 1) {\n if (debug) {\n console.warn('useFiltersAndSort: setPageSize called with value < 1, clamping to 1')\n }\n size = 1\n }\n // Reset to page 0 when changing page size to avoid out-of-bounds\n svc.setPagination(0, size)\n },\n [svc, debug]\n )\n\n const setPagination = useCallback(\n (page: number, size?: number) => {\n if (page < 0) {\n if (debug) {\n console.warn('useFiltersAndSort: setPagination called with negative page, clamping to 0')\n }\n page = 0\n }\n if (size !== undefined && size < 1) {\n if (debug) {\n console.warn('useFiltersAndSort: setPagination called with size < 1, clamping to 1')\n }\n size = 1\n }\n svc.setPagination(page, size)\n },\n [svc, debug]\n )\n\n const nextPage = useCallback(() => {\n svc.setPagination(svc.getCurrentState().pagination.page + 1)\n }, [svc])\n\n const prevPage = useCallback(() => {\n const newPage = Math.max(0, svc.getCurrentState().pagination.page - 1)\n svc.setPagination(newPage)\n }, [svc])\n\n const firstPage = useCallback(() => {\n svc.setPagination(0)\n }, [svc])\n\n const setQuery = useCallback(\n (query: string) => {\n svc.setQuery(query)\n },\n [svc]\n )\n\n const clearQuery = useCallback(() => {\n svc.setQuery('')\n }, [svc])\n\n const reset = useCallback(() => {\n svc.reset()\n }, [svc])\n\n const clearStorageAndReset = useCallback(() => {\n svc.clearStorage()\n svc.reset()\n }, [svc])\n\n const getService = useCallback(() => svc, [svc])\n\n // Derived values\n const activeFilterCount = useMemo(\n () => countActiveFilters(state.filters, defaultFiltersRef.current),\n [state.filters]\n )\n\n const isFirstPage = state.pagination.page === 0\n const offset = state.pagination.page * state.pagination.limit\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(\n () => ({\n // State\n filters: state.filters,\n sortBy: state.sort?.fieldName ?? null,\n sortDirection: state.sort?.direction ?? 'asc',\n page: state.pagination.page,\n pageSize: state.pagination.limit,\n query: state.query,\n state,\n\n // Filter actions\n setFilter,\n setFilters,\n clearFilter,\n clearAllFilters,\n hasFilter,\n hasAnyFilters,\n activeFilterCount,\n\n // Sort actions\n setSort,\n toggleSort: setSort,\n clearSort,\n isSortedBy,\n\n // Pagination actions\n setPage,\n setPageSize,\n setPagination,\n nextPage,\n prevPage,\n firstPage,\n isFirstPage,\n offset,\n\n // Query actions\n setQuery,\n clearQuery,\n\n // General actions\n reset,\n clearStorageAndReset,\n storageKey: svc.getStorageKey(),\n\n // Service access\n getService,\n }),\n [\n state,\n setFilter,\n setFilters,\n clearFilter,\n clearAllFilters,\n hasFilter,\n hasAnyFilters,\n activeFilterCount,\n setSort,\n clearSort,\n isSortedBy,\n setPage,\n setPageSize,\n setPagination,\n nextPage,\n prevPage,\n firstPage,\n isFirstPage,\n offset,\n setQuery,\n clearQuery,\n reset,\n clearStorageAndReset,\n svc,\n getService,\n ]\n )\n}\n\n/**\n * Convenience hook for creating a filters service with simpler API\n * Equivalent to useFiltersAndSort({ config: ... })\n */\nexport function useFilters<T extends Record<string, any> = Record<string, any>>(\n storageKey: string,\n options?: Omit<FiltersAndSortConfig<T>, 'storageKey'>\n): UseFiltersAndSortReturn<T> {\n return useFiltersAndSort<T>({\n config: {\n storageKey,\n ...options,\n },\n })\n}\n\nexport type { FilterAndSortState, SortDirection }\n","// React hook for FormBuilder with proper state management\nimport { FormBuilder } from '@codella-software/utils'\nimport { useEffect, useMemo, useState } from 'react'\n\n/**\n * Hook options for useFormBuilder\n */\nexport interface UseFormBuilderOptions<T extends Record<string, any>> {\n /** FormBuilder instance */\n builder: FormBuilder<T>\n}\n\n/**\n * Hook return value for useFormBuilder\n */\nexport interface UseFormBuilderReturn<T extends Record<string, any>> {\n /** Current form values */\n values: T\n /** Form validation errors */\n errors: Record<string, string | undefined>\n /** Form touched fields */\n touched: Record<string, boolean>\n /** Current form step (for multi-step forms) */\n currentStep: number\n /** Total form steps */\n totalSteps: number\n /** Is form currently submitting */\n isSubmitting: boolean\n /** Is form valid */\n isValid: boolean\n /** Set field value */\n setFieldValue: (field: keyof T, value: any) => Promise<void>\n /** Set field touched */\n setFieldTouched: (field: keyof T, touched: boolean) => void\n /** Validate entire form */\n validate: () => Promise<Record<string, string | undefined>>\n /** Submit form */\n submit: () => Promise<T | null>\n /** Reset form to initial values */\n reset: () => void\n /** Go to next step */\n nextStep: () => void\n /** Go to previous step */\n prevStep: () => void\n}\n\n/**\n * React hook that wraps FormBuilder and provides form state management\n *\n * @param options - Hook options including FormBuilder instance\n * @returns Form state and methods\n *\n * @example\n * ```tsx\n * const builder = new FormBuilder<{ name: string }>()\n * .addField({ name: 'name', type: 'text', label: 'Name' })\n * .build();\n *\n * function MyForm() {\n * const form = useFormBuilder({ builder });\n * return (\n * <form onSubmit={(e) => { e.preventDefault(); form.submit(); }}>\n * <input\n * value={form.values.name}\n * onChange={(e) => form.setFieldValue('name', e.target.value)}\n * />\n * {form.errors.name && <span>{form.errors.name}</span>}\n * <button type=\"submit\">Submit</button>\n * </form>\n * );\n * }\n * ```\n */\nexport function useFormBuilder<T extends Record<string, any>>(\n options: UseFormBuilderOptions<T>\n): UseFormBuilderReturn<T> {\n const { builder } = options\n\n if (!builder) {\n throw new Error('useFormBuilder: builder is required')\n }\n\n // Subscribe to form state\n const [state, setState] = useState(() => builder.currentState)\n const [isSubmitting, setIsSubmitting] = useState(false)\n\n useEffect(() => {\n if (!builder || !builder.state$) {\n return\n }\n\n const subscription = builder.state$.subscribe((newState: any) => {\n setState(newState)\n })\n\n return () => subscription.unsubscribe()\n }, [builder])\n\n const memoizedReturn = useMemo(() => ({\n values: state.values,\n errors: state.errors,\n touched: state.touched,\n currentStep: 0, // Multi-step requires MultiStepFormBuilder\n totalSteps: 1, // Multi-step requires MultiStepFormBuilder\n isSubmitting,\n isValid: Object.keys(state.errors).length === 0,\n\n setFieldValue: async (field: keyof T, value: any) => {\n builder.setFieldValue(field as string, value)\n },\n\n setFieldTouched: (field: keyof T, touched: boolean) => {\n builder.setFieldTouched(field as string, touched)\n },\n\n validate: async () => {\n return builder.validate()\n },\n\n submit: async () => {\n setIsSubmitting(true)\n try {\n await builder.submit()\n setIsSubmitting(false)\n return null\n } catch (error) {\n setIsSubmitting(false)\n throw error\n }\n },\n\n reset: () => {\n builder.reset()\n },\n\n nextStep: () => {\n // Multi-step forms use MultiStepFormBuilder, not FormBuilder\n console.warn('nextStep() is only available on MultiStepFormBuilder')\n },\n\n prevStep: () => {\n // Multi-step forms use MultiStepFormBuilder, not FormBuilder\n console.warn('prevStep() is only available on MultiStepFormBuilder')\n },\n }), [builder, state, isSubmitting])\n\n return memoizedReturn\n}\n","import { FC, ReactNode, createContext, useContext, useEffect, useState } from 'react';\n\nimport type { IAuthProvider } from '@codella-software/utils';\nimport {\n LiveUpdateService,\n SSEService,\n WebsocketService,\n type EventMapping,\n type LiveUpdateType,\n} from '@codella-software/utils/live-updates';\n\nexport interface LiveUpdateContextValue {\n service: LiveUpdateService;\n isConnected: boolean;\n type: LiveUpdateType;\n canSend: boolean;\n}\n\nconst LiveUpdateContext = createContext<LiveUpdateContextValue | null>(null);\n\n/**\n * Hook to access the LiveUpdateContext\n * Must be used within a LiveUpdateProvider\n */\nexport const useLiveUpdateContext = () => {\n const context = useContext(LiveUpdateContext);\n if (!context) {\n throw new Error('useLiveUpdateContext must be used within LiveUpdateProvider');\n }\n return context;\n};\n\nexport interface LiveUpdateProviderProps {\n children: ReactNode;\n authProvider: IAuthProvider;\n sseUrl?: string;\n wsUrl?: string;\n sseEnabled?: boolean;\n wsEnabled?: boolean;\n eventMappings?: Record<string, EventMapping>;\n}\n\n/**\n * Provider component for live updates service\n * Manages service initialization and connection state\n * \n * @example\n * ```tsx\n * import { LiveUpdateProvider } from '@codella/react';\n * import { createFirebaseAuthProvider } from '@codella-software/utils';\n * \n * function App() {\n * const authProvider = createFirebaseAuthProvider({ auth: getAuth() });\n * \n * return (\n * <LiveUpdateProvider\n * authProvider={authProvider}\n * sseUrl=\"http://api.example.com/sse\"\n * wsUrl=\"ws://api.example.com/ws\"\n * eventMappings={{\n * 'notifications': { sseEvent: 'GET_NOTIFICATIONS' }\n * }}\n * >\n * <YourApp />\n * </LiveUpdateProvider>\n * );\n * }\n * ```\n */\nexport const LiveUpdateProvider: FC<LiveUpdateProviderProps> = ({\n children,\n authProvider,\n sseUrl = 'http://localhost:3000/sse',\n wsUrl = 'ws://localhost:3000/ws',\n sseEnabled = true,\n wsEnabled = true,\n eventMappings = {},\n}) => {\n const [isConnected, setIsConnected] = useState(false);\n const [service] = useState(() => {\n const sseService = new SSEService({\n url: sseUrl,\n authProvider,\n });\n\n const wsService = new WebsocketService({\n url: wsUrl,\n authProvider,\n });\n\n return new LiveUpdateService(wsService, sseService);\n });\n\n useEffect(() => {\n if (authProvider.isAuthenticated()) {\n service.initialize({\n sseEnabled,\n wsEnabled,\n authProvider,\n eventMappings,\n });\n } else {\n service.disconnect();\n }\n\n const subscription = service.isConnected$.subscribe(setIsConnected);\n\n return () => {\n subscription.unsubscribe();\n };\n }, [authProvider, sseEnabled, wsEnabled, service, eventMappings]);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n service.disconnect();\n };\n }, [service]);\n\n return (\n <LiveUpdateContext.Provider\n value={{\n service,\n isConnected,\n type: service.type,\n canSend: service.canSend,\n }}\n >\n {children}\n </LiveUpdateContext.Provider>\n );\n};\n","import { useEffect, useState } from 'react';\n\nimport type { WS_ACTIONS } from '@codella-software/utils/live-updates';\nimport { useLiveUpdateContext } from './LiveUpdateProvider';\n\n/**\n * Hook to listen for specific WebSocket actions\n * @param action - The WS_ACTIONS enum value to listen for\n * @param enabled - Whether to subscribe (defaults to true)\n * @returns The latest message payload or null\n */\nexport const useLiveListener = <T = unknown>(action: WS_ACTIONS, enabled = true) => {\n const { service } = useLiveUpdateContext();\n const [data, setData] = useState<T | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n if (!enabled || !service) {\n return;\n }\n\n const subscription = service.on(action).subscribe({\n next: (value) => {\n setData(value);\n setError(null);\n },\n error: (err) => {\n setError(err instanceof Error ? err : new Error(String(err)));\n },\n });\n\n return () => {\n subscription.unsubscribe();\n };\n }, [action, enabled, service]);\n\n return { data, error };\n};\n","import { useCallback } from 'react';\n\nimport type { WS_ACTIONS } from '@codella-software/utils/live-updates';\nimport { useLiveUpdateContext } from './LiveUpdateProvider';\n\n/**\n * Hook for request-response pattern over WebSocket\n * @param sendAction - The action to send\n * @param responseAction - The action to wait for\n * @returns Function to send request and promise for response\n */\nexport const useLiveRequest = <TReq = unknown, TRes = unknown>() => {\n const { service, canSend } = useLiveUpdateContext();\n\n const request = useCallback(\n (sendAction: WS_ACTIONS, responseAction: WS_ACTIONS, payload?: TReq) => {\n if (!canSend) {\n return Promise.reject(new Error('WebSocket not available for sending'));\n }\n\n return service\n .on(responseAction)\n .toPromise()\n .catch(() => {\n throw new Error(`No response received for action: ${responseAction}`);\n });\n },\n [canSend, service],\n );\n\n return { request, canSend };\n};\n","import { useCallback } from 'react';\n\nimport type { WS_ACTIONS } from '@codella-software/utils/live-updates';\nimport { useLiveUpdateContext } from './LiveUpdateProvider';\n\nexport interface UseUpdateListenerOptions {\n enabled?: boolean;\n}\n\n/**\n * Hook to listen for and handle live updates with callback\n * @param action - The WS_ACTIONS enum value to listen for\n * @param onUpdate - Callback when update is received\n * @param enabled - Whether to subscribe (defaults to true)\n */\nexport const useLiveUpdateListener = <T = unknown>(\n action: WS_ACTIONS,\n onUpdate: (data: T) => void,\n { enabled = true }: UseUpdateListenerOptions = {},\n) => {\n const { service } = useLiveUpdateContext();\n\n const handleUpdate = useCallback(\n (data: T) => {\n onUpdate(data);\n },\n [onUpdate],\n );\n\n // Subscribe to updates\n // The subscription is managed by the effect in the hook\n // Users need to call subscribe manually or use useLiveListener instead\n const subscribe = useCallback(() => {\n if (!enabled || !service) {\n return () => {};\n }\n\n const subscription = service.on(action).subscribe(handleUpdate);\n\n return () => {\n subscription.unsubscribe();\n };\n }, [action, enabled, service, handleUpdate]);\n\n return { subscribe };\n};\n","import { useEffect, useState } from 'react';\n\nimport { useLiveUpdateContext } from './LiveUpdateProvider';\n\n/**\n * Hook to subscribe to live update events\n * @param eventName - The event name (must be mapped in LiveUpdateProvider.eventMappings)\n * @returns The latest event data or null\n */\nexport const useLiveUpdates = <T = unknown>(eventName: string) => {\n const { service } = useLiveUpdateContext();\n const [data, setData] = useState<T | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n if (!service) {\n return;\n }\n\n const subscription = service.on(eventName).subscribe({\n next: (value) => {\n setData(value);\n setError(null);\n },\n error: (err) => {\n setError(err instanceof Error ? err : new Error(String(err)));\n },\n });\n\n return () => {\n subscription.unsubscribe();\n };\n }, [eventName, service]);\n\n return { data, error };\n};\n","/**\n * React hook for RichContentService\n */\n\nimport {\n ContentEditableAdapter,\n DocumentNode,\n MarkType,\n RichContentConfig,\n RichContentService,\n RichContentState,\n Selection,\n} from '@codella-software/utils/rich-content';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\n/**\n * Hook options for useRichContent\n */\nexport interface UseRichContentOptions extends RichContentConfig {\n /** DOM element ref for contentEditable */\n editorRef?: React.RefObject<HTMLElement>;\n /** Custom adapter instance */\n adapter?: ContentEditableAdapter;\n}\n\n/**\n * Hook return value for useRichContent\n */\nexport interface UseRichContentReturn {\n /** Service instance */\n service: RichContentService;\n /** Current document content */\n content: DocumentNode;\n /** Full state object */\n state: RichContentState;\n /** Current editor focus state */\n isFocused: boolean;\n /** Can undo */\n canUndo: boolean;\n /** Can redo */\n canRedo: boolean;\n /** Active mark types */\n selectedFormats: Set<MarkType>;\n /** Current selection */\n selection: Selection | null;\n /** Is dirty (has unsaved changes) */\n isDirty: boolean;\n\n // Commands\n /** Insert text */\n insertText: (text: string) => void;\n /** Insert paragraph */\n insertParagraph: () => void;\n /** Insert heading */\n insertHeading: (level: 1 | 2 | 3 | 4 | 5 | 6) => void;\n /** Insert image */\n insertImage: (url: string) => void;\n /** Upload image file */\n uploadImage: (file: File) => Promise<void>;\n /** Insert mention */\n insertMention: (id: string, label: string) => void;\n /** Insert list */\n insertList: (type: 'ordered' | 'unordered') => void;\n /** Toggle mark */\n toggleMark: (mark: MarkType) => void;\n /** Delete content */\n deleteContent: () => void;\n /** Undo */\n undo: () => void;\n /** Redo */\n redo: () => void;\n /** Clear history */\n clearHistory: () => void;\n /** Focus editor */\n focus: () => void;\n /** Set focus state */\n setFocus: (focused: boolean) => void;\n /** Get plain text */\n getPlainText: () => string;\n /** Set selection */\n setSelection: (selection: Selection | null) => void;\n}\n\n/**\n * React hook that wraps RichContentService and manages state subscriptions\n */\nexport function useRichContent(options: UseRichContentOptions = {}): UseRichContentReturn {\n // Create service instance\n const service = useMemo(() => RichContentService.create(options), []);\n\n // State from service\n const [content, setContent] = useState<DocumentNode>(service.getContent());\n const [state, setState] = useState<RichContentState>(service.getState());\n const [isFocused, setIsFocused] = useState(false);\n const [canUndo, setCanUndo] = useState(false);\n const [canRedo, setCanRedo] = useState(false);\n const [selectedFormats, setSelectedFormats] = useState<Set<MarkType>>(new Set());\n\n // Subscriptions\n useEffect(() => {\n if (!service) {\n return\n }\n\n const contentSub = service.getContent$().subscribe(setContent);\n const stateSub = service.getState$().subscribe((newState) => {\n setState(newState);\n setIsFocused(newState.isFocused);\n setCanUndo(newState.canUndo);\n setCanRedo(newState.canRedo);\n setSelectedFormats(newState.selectedFormats || new Set());\n });\n\n return () => {\n contentSub.unsubscribe();\n stateSub.unsubscribe();\n };\n }, [service]);\n\n // Attach adapter if provided\n useEffect(() => {\n if (options.adapter) {\n service.attachAdapter(options.adapter);\n return () => service.detachAdapter();\n }\n }, [options.adapter, service]);\n\n // Setup default adapter if editorRef provided\n const defaultAdapterRef = useRef<ContentEditableAdapter | null>(null);\n\n useEffect(() => {\n if (options.editorRef?.current && !options.adapter && !defaultAdapterRef.current) {\n const { DefaultContentEditableAdapter } = require('@codella-software/utils/rich-content');\n const adapter = new DefaultContentEditableAdapter();\n adapter.mount(options.editorRef.current);\n service.attachAdapter(adapter);\n defaultAdapterRef.current = adapter;\n\n return () => {\n adapter.unmount();\n service.detachAdapter();\n defaultAdapterRef.current = null;\n };\n }\n }, [options.editorRef, options.adapter, service]);\n\n // Command callbacks\n const insertText = useCallback((text: string) => service.insertText(text), [service]);\n const insertParagraph = useCallback(() => service.insertParagraph(), [service]);\n const insertHeading = useCallback((level: any) => service.insertHeading(level), [service]);\n const insertImage = useCallback((url: string) => service.insertImage(url), [service]);\n const uploadImage = useCallback((file: File) => service.uploadImage(file), [service]);\n const insertMention = useCallback((id: string, label: string) => service.insertMention(id, label), [service]);\n const insertList = useCallback((type: 'ordered' | 'unordered') => service.insertList(type), [service]);\n const toggleMark = useCallback((mark: MarkType) => service.toggleMark(mark), [service]);\n const deleteContent = useCallback(() => service.deleteContent(), [service]);\n const undo = useCallback(() => service.undo(), [service]);\n const redo = useCallback(() => service.redo(), [service]);\n const clearHistory = useCallback(() => service.clearHistory(), [service]);\n const focus = useCallback(() => service.getAdapter()?.focus(), [service]);\n const setFocus = useCallback((focused: boolean) => service.setFocus(focused), [service]);\n const getPlainText = useCallback(() => service.getPlainText(), [service]);\n const setSelection = useCallback((sel: Selection | null) => service.setSelection(sel), [service]);\n\n return {\n service,\n content,\n state,\n isFocused,\n canUndo,\n canRedo,\n selectedFormats,\n selection: state.selection || null,\n isDirty: state.isDirty,\n\n insertText,\n insertParagraph,\n insertHeading,\n insertImage,\n uploadImage,\n insertMention,\n insertList,\n toggleMark,\n deleteContent,\n undo,\n redo,\n clearHistory,\n focus,\n setFocus,\n getPlainText,\n setSelection,\n };\n}\n","import type { TabChangeEvent, TabsConfig } from '@codella-software/utils/tabs';\nimport { TabsService } from '@codella-software/utils/tabs';\nimport React, { createContext, useContext, useEffect, useState } from 'react';\n\ninterface TabsContextValue {\n service: TabsService;\n activeTabId: string;\n tabs: ReturnType<TabsService['getTabs']>;\n}\n\nconst TabsContext = createContext<TabsContextValue | undefined>(undefined);\n\n/**\n * Props for TabsProvider\n */\nexport interface TabsProviderProps {\n config: TabsConfig;\n children: React.ReactNode;\n}\n\n/**\n * Tabs provider for React\n */\nexport function TabsProvider({ config, children }: TabsProviderProps) {\n const [service] = useState(() => new TabsService(config));\n const [activeTabId, setActiveTabId] = useState(() => service.getActiveTabId());\n const [tabs, setTabs] = useState(() => service.getTabs());\n\n useEffect(() => {\n const activeTabSubscription = service.getActiveTabId$().subscribe(setActiveTabId);\n const tabsSubscription = service.getTabs$().subscribe(setTabs);\n\n return () => {\n activeTabSubscription.unsubscribe();\n tabsSubscription.unsubscribe();\n };\n }, [service]);\n\n useEffect(() => {\n return () => {\n service.destroy();\n };\n }, [service]);\n\n return (\n <TabsContext.Provider value={{ service, activeTabId, tabs }}>\n {children}\n </TabsContext.Provider>\n );\n}\n\n/**\n * Hook to access tabs service from context\n */\nexport function useTabsContext(): TabsContextValue {\n const context = useContext(TabsContext);\n if (!context) {\n throw new Error('useTabsContext must be used within a TabsProvider');\n }\n return context;\n}\n\n/**\n * Hook to use tabs service directly\n */\nexport function useTabsService() {\n const { service } = useTabsContext();\n return service;\n}\n\n/**\n * Hook to get active tab\n */\nexport function useActiveTab() {\n const { service } = useTabsContext();\n const [activeTab, setActiveTab] = useState(() => service.getActiveTab());\n\n useEffect(() => {\n const subscription = service.getActiveTab$().subscribe(setActiveTab);\n return () => subscription.unsubscribe();\n }, [service]);\n\n return activeTab;\n}\n\n/**\n * Hook to get all tabs\n */\nexport function useTabs() {\n const { tabs } = useTabsContext();\n return tabs;\n}\n\n/**\n * Hook to listen to tab changes\n */\nexport function useTabChange() {\n const { service } = useTabsContext();\n const [changeEvent, setChangeEvent] = useState<TabChangeEvent | null>(null);\n\n useEffect(() => {\n const subscription = service.onTabChange$().subscribe(setChangeEvent);\n return () => subscription.unsubscribe();\n }, [service]);\n\n return changeEvent;\n}\n\n/**\n * Hook to set active tab\n */\nexport function useSetActiveTab() {\n const { service } = useTabsContext();\n return {\n setActiveTab: (tabId: string) => service.setActiveTab(tabId),\n setActiveTabByIndex: (index: number) => service.setActiveTabByIndex(index),\n nextTab: () => service.nextTab(),\n previousTab: () => service.previousTab(),\n };\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codella-software/react",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "description": "React hooks for Codella core services (FormBuilder, TableBuilder, FiltersAndSort, RichContent)",
5
5
  "keywords": [
6
6
  "form-builder",
@@ -19,7 +19,7 @@
19
19
  "license": "MIT",
20
20
  "type": "module",
21
21
  "dependencies": {
22
- "@codella-software/utils": "^2.3.0"
22
+ "@codella-software/utils": "^2.3.1"
23
23
  },
24
24
  "exports": {
25
25
  ".": {
@@ -1,2 +0,0 @@
1
- export { useTableService } from './useTableService';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/table-builder/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
@@ -1,38 +0,0 @@
1
- /**
2
- * @deprecated TableBuilder is a configuration builder, not a stateful service.
3
- *
4
- * For reactive table state management, use FiltersAndSortService instead:
5
- *
6
- * @example
7
- * ```tsx
8
- * import { createFiltersAndSortService } from '@codella-software/utils';
9
- * import { useFiltersAndSort } from '@codella-software/react';
10
- *
11
- * const service = createFiltersAndSortService({
12
- * storageKey: 'my-table',
13
- * initialState: { pagination: { page: 0, limit: 10 } }
14
- * });
15
- *
16
- * function MyTable() {
17
- * const { page, pageSize, filters, setFilter, setPage } = useFiltersAndSort({ service });
18
- *
19
- * // Fetch data with current state
20
- * const { data } = useQuery({
21
- * queryKey: ['data', page, pageSize, filters],
22
- * queryFn: () => fetchData({ page, pageSize, ...filters })
23
- * });
24
- *
25
- * // Build table config
26
- * const tableConfig = createTableBuilder()
27
- * .data(data?.items ?? [])
28
- * .totalRows(data?.total ?? 0)
29
- * .columns([...])
30
- * .filtersAndSort(service.getCurrentState(), service.getActions())
31
- * .build();
32
- *
33
- * return <DynamicTable config={tableConfig} />;
34
- * }
35
- * ```
36
- */
37
- export declare function useTableService(): void;
38
- //# sourceMappingURL=useTableService.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useTableService.d.ts","sourceRoot":"","sources":["../../src/table-builder/useTableService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,eAAe,SAM9B"}