@codella-software/react 2.3.0 → 2.3.2
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.
- package/dist/filters-and-sort/useFiltersAndSort.d.ts +3 -1
- package/dist/filters-and-sort/useFiltersAndSort.d.ts.map +1 -1
- package/dist/index.cjs +21 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +21 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/dist/table-builder/index.d.ts +0 -2
- package/dist/table-builder/index.d.ts.map +0 -1
- package/dist/table-builder/useTableService.d.ts +0 -38
- package/dist/table-builder/useTableService.d.ts.map +0 -1
|
@@ -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":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -184,8 +184,25 @@ function useFiltersAndSort(options) {
|
|
|
184
184
|
}
|
|
185
185
|
size = 1;
|
|
186
186
|
}
|
|
187
|
-
|
|
188
|
-
|
|
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;
|
package/dist/index.cjs.map
CHANGED
|
@@ -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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
186
|
-
|
|
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
|
package/dist/index.mjs.map
CHANGED
|
@@ -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.
|
|
3
|
+
"version": "2.3.2",
|
|
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.
|
|
22
|
+
"@codella-software/utils": "^2.3.1"
|
|
23
23
|
},
|
|
24
24
|
"exports": {
|
|
25
25
|
".": {
|
|
@@ -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"}
|