Files
calendar/apps/web/modules/data-table/contexts/DataTableStateContext.tsx
T
Eunjae LeeGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1368ffe55d refactor: move data-table hooks/contexts/provider from features to web modules (#27833)
* refactor: move data-table hooks/contexts/provider from features to web modules

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix: update broken useColumnResizing import path in DataTable.tsx

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix: update remaining broken import paths for moved hooks

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* docs: update GUIDE.md import paths and file references for moved modules

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-02-13 00:00:40 +09:00

170 lines
4.8 KiB
TypeScript

"use client";
import type { SortingState, OnChangeFn, VisibilityState, ColumnSizingState } from "@tanstack/react-table";
import { useQueryState } from "nuqs";
import { createContext, useContext, useRef, useMemo } from "react";
import { useElementByClassName } from "@calcom/lib/hooks/useElementByClassName";
import {
activeFiltersParser,
sortingParser,
columnVisibilityParser,
columnSizingParser,
segmentIdParser,
pageIndexParser,
pageSizeParser,
searchTermParser,
DEFAULT_PAGE_SIZE,
} from "@calcom/features/data-table/lib/parsers";
import type { ActiveFilters, SegmentIdentifier } from "@calcom/features/data-table/lib/types";
import { CTA_CONTAINER_CLASS_NAME } from "@calcom/features/data-table/lib/utils";
export type ActiveFiltersValidator = (filters: ActiveFilters) => ActiveFilters;
export type ActiveFiltersValidatorState = ActiveFiltersValidator | "loading" | undefined;
export type DataTableStateContextType = {
tableIdentifier: string;
ctaContainerRef: React.RefObject<HTMLDivElement>;
filterToOpen: React.MutableRefObject<string | undefined>;
pageIndex: number;
setPageIndex: (pageIndex: number | null) => void;
pageSize: number;
setPageSize: (pageSize: number | null) => void;
searchTerm: string;
setSearchTerm: (searchTerm: string | null) => void;
activeFilters: ActiveFilters;
setActiveFilters: ReturnType<typeof useQueryState<ActiveFilters>>[1];
sorting: SortingState;
setSorting: OnChangeFn<SortingState>;
columnVisibility: VisibilityState;
setColumnVisibility: OnChangeFn<VisibilityState>;
columnSizing: ColumnSizingState;
setColumnSizing: OnChangeFn<ColumnSizingState>;
segmentIdRaw: string | null;
setSegmentIdRaw: (segmentId: string | null) => void;
defaultPageSize: number;
timeZone?: string;
validateActiveFilters?: ActiveFiltersValidatorState;
};
export const DataTableStateContext = createContext<DataTableStateContextType | null>(null);
export function useDataTableState() {
const context = useContext(DataTableStateContext);
if (!context) {
throw new Error("useDataTableState must be used within a DataTableStateProvider");
}
return context;
}
interface DataTableStateProviderProps {
tableIdentifier: string;
children: React.ReactNode;
defaultPageSize?: number;
ctaContainerClassName?: string;
timeZone?: string;
preferredSegmentId?: SegmentIdentifier | null;
validateActiveFilters?: ActiveFiltersValidatorState;
}
export function DataTableStateProvider({
tableIdentifier,
children,
defaultPageSize = DEFAULT_PAGE_SIZE,
ctaContainerClassName = CTA_CONTAINER_CLASS_NAME,
timeZone,
preferredSegmentId,
validateActiveFilters,
}: DataTableStateProviderProps) {
if (!tableIdentifier.trim()) {
throw new Error("tableIdentifier is required and cannot be empty");
}
const filterToOpen = useRef<string | undefined>(undefined);
const [pageIndex, setPageIndex] = useQueryState("page", pageIndexParser);
const [pageSize, setPageSize] = useQueryState("size", pageSizeParser);
const [searchTerm, setSearchTerm] = useQueryState("q", searchTermParser);
const [activeFilters, setActiveFilters] = useQueryState("activeFilters", activeFiltersParser);
const [sorting, setSorting] = useQueryState("sorting", sortingParser);
const [columnVisibility, setColumnVisibility] = useQueryState<VisibilityState>(
"cols",
columnVisibilityParser
);
const [columnSizing, setColumnSizing] = useQueryState<ColumnSizingState>("widths", columnSizingParser);
const initialSegmentId = useMemo(
() => (preferredSegmentId ? String(preferredSegmentId.id) : null),
[preferredSegmentId]
);
const [segmentIdRaw, setSegmentIdRaw] = useQueryState(
"segment",
initialSegmentId ? segmentIdParser.withDefault(initialSegmentId) : segmentIdParser
);
const ctaContainerRef = useElementByClassName<HTMLDivElement>(ctaContainerClassName);
const value = useMemo(
() => ({
tableIdentifier,
ctaContainerRef,
filterToOpen,
pageIndex,
setPageIndex,
pageSize,
setPageSize,
searchTerm,
setSearchTerm,
activeFilters,
setActiveFilters,
sorting,
setSorting,
columnVisibility,
setColumnVisibility,
columnSizing,
setColumnSizing,
segmentIdRaw,
setSegmentIdRaw,
defaultPageSize,
timeZone,
validateActiveFilters,
}),
[
tableIdentifier,
ctaContainerRef,
pageIndex,
setPageIndex,
pageSize,
setPageSize,
searchTerm,
setSearchTerm,
activeFilters,
setActiveFilters,
sorting,
setSorting,
columnVisibility,
setColumnVisibility,
columnSizing,
setColumnSizing,
segmentIdRaw,
setSegmentIdRaw,
defaultPageSize,
timeZone,
validateActiveFilters,
]
);
return <DataTableStateContext.Provider value={value}>{children}</DataTableStateContext.Provider>;
}