/** * patient-table — responsive column hook * ---------------------------------------------------------------------------- * `useResponsiveColumns(handleRef, opts)` toggles column visibility by the * `responsiveTier` (`P0`–`P3`) baked onto each `ColDef` by `buildPatientColumns`, * driven by the *grid container's* width — measured with a `ResizeObserver`, NOT * the viewport — so the table responds correctly when embedded in a narrow panel, * split pane or sidebar rather than only at browser-window breakpoints. * * BREAKPOINTS (container inline-size → highest tier kept visible): * * < 640px → P0 only (pinned avatar / firstName / lastName / actions) * < 900px → P0 + P1 (+ phone, nextAppointment, balance) * < 1200px → P0 + P1 + P2 (+ email, carePlanStatus) * ≥ 1200px → all tiers (P0–P3) (+ fiscalCode, age, gender, source) * * Columns without a tier (the pinned avatar / firstName / lastName / actions * anchors, and every grouped `hide: true` column) are never touched — the * anchors stay visible at every width; the grouped columns are owned by * ColumnToggle only. * * RESPONSIVE ↔ PERSISTENCE RECONCILIATION * --------------------------------------- * `data-table.tsx` persists the full AG Grid column state to * `localStorage["data-table-col-state:"]` (load on grid-ready, save on * unmount). A user's explicit `ColumnToggle` choice lives in that persisted state * and MUST win over — and survive — the responsive baseline. Reconciliation: * * 1. The responsive baseline is exactly that — a *baseline*. We only auto * show/hide a column the user has NOT manually toggled this session. * 2. We track a `Set` of user-touched columns. We learn of a manual * toggle from the grid's `columnVisible` event: any such event whose * `source` is NOT `'api'` is user-driven (ColumnToggle tool panel, column * menu, context menu, drag), so we add those colIds to the set. Our own * responsive calls go through `setColumnsVisible(..., 'api')`, so they never * pollute the set (and we additionally guard with a re-entrancy flag). * 3. Once a column is user-touched, the hook leaves it alone forever this * session — its visibility is whatever the user chose, which is what * `data-table.tsx` then persists. The persisted choice therefore wins on * the next mount too: the DataTable persists the user-touched set with * its column state (`getUserTouchedColumns` / `markColumnsUserTouched` on * the handle), and every reconcile pass reads it live. * 4. A programmatic visibility write by someone else (the DataTable restoring * persisted state, a columnDefs update) schedules one reconcile pass, so * the baseline is re-asserted for untouched columns without a resize. * * CONSTRAINTS: no `any` (AG Grid's `ColumnEventType` is read off the typed event, * tier read via the typed `getResponsiveTier` helper). No DOM `data-*` attribute * is written here (PHI rule is moot — this hook only reads colIds and widths). */ import { type RefObject } from 'react'; import type { PatientTableHandle, ResponsiveTier } from './types'; /** * Container inline-size breakpoints. Each entry is the *minimum* container width * (px) at which the named tier becomes visible. Below the first threshold only * `P0` shows; at or above the last, everything shows. */ interface Breakpoint { /** Minimum container inline-size (px) for this tier to be shown. */ minWidth: number; /** The tier this breakpoint reveals. */ tier: ResponsiveTier; } export interface UseResponsiveColumnsOptions { /** * The grid container element to observe. The hook measures THIS element's * inline-size (not the viewport) so it works embedded. When the ref is unset * the hook is inert. */ containerRef: RefObject; /** * Disable responsive toggling entirely (the `responsive={false}` escape hatch * on `PatientTable`). When `false` the hook unobserves and makes no visibility * changes — column visibility is left entirely to persistence / ColumnToggle. * Default `true`. */ enabled?: boolean; /** * Override the container-width breakpoints. Ordered low→high `minWidth`; each * reveals its `tier`. Defaults to 640 / 900 / 1200 (P1 / P2 / P3). */ breakpoints?: readonly Breakpoint[]; } /** * Imperative surface returned by {@link useResponsiveColumns}. The responsive * effect runs internally; `lockColumns` lets a caller (the view switcher) mark * columns as user-touched so the responsive baseline stops auto-managing them. */ export interface UseResponsiveColumnsResult { /** * Mark the given colIds as "user-touched" so the responsive baseline leaves * their visibility alone from here on — exactly as if the user had toggled * them via `ColumnToggle`. The view switcher calls this when it applies a * view's column visibility, so its programmatic (`source: 'api'`) visibility * writes are not immediately re-hidden/re-shown by the next responsive * reconcile pass. No-op (with the colIds still recorded for a later mount of * the effect) when the hook is disabled. */ lockColumns: (colIds: readonly string[]) => void; } /** * Wire responsive, tier-driven column visibility to the patient grid. * * @param handleRef The `PatientTable` imperative handle ref — the hook reaches * the raw AG Grid API via `handleRef.current.getRawApi()`. * @param opts Container ref to observe, plus enable flag / breakpoint * overrides. See {@link UseResponsiveColumnsOptions}. * @returns {@link UseResponsiveColumnsResult} — `lockColumns(colIds)` * to mark columns user-touched (see the view-switcher * coordination note in patient-table.tsx). */ export declare function useResponsiveColumns(handleRef: RefObject, opts: UseResponsiveColumnsOptions): UseResponsiveColumnsResult; export {}; //# sourceMappingURL=use-responsive-columns.d.ts.map