@alfadocs/ui-kit-debug 1.9.4 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks/actions-cell-renderer-xfSYiTXQ.js +1887 -0
- package/dist/_chunks/actions-cell-renderer-xfSYiTXQ.js.map +1 -0
- package/dist/_chunks/{balance-badge-cell-DHTiSFyD.js → balance-badge-cell-C_Qz76x7.js} +447 -426
- package/dist/_chunks/balance-badge-cell-C_Qz76x7.js.map +1 -0
- package/dist/_chunks/{data-table-tabs-C1kRUzat.js → data-table-tabs-D24qj89A.js} +343 -325
- package/dist/_chunks/data-table-tabs-D24qj89A.js.map +1 -0
- package/dist/_chunks/{file-manager-lAeBBfZi.js → file-manager-Df6hyUYg.js} +2 -2
- package/dist/_chunks/{file-manager-lAeBBfZi.js.map → file-manager-Df6hyUYg.js.map} +1 -1
- package/dist/agent-catalog.json +1 -1
- package/dist/components/data-table/data-table.d.ts +11 -0
- package/dist/components/data-table/data-table.d.ts.map +1 -1
- package/dist/components/data-table/index.js +1 -1
- package/dist/components/data-table/toolbar.d.ts +2 -1
- package/dist/components/data-table/toolbar.d.ts.map +1 -1
- package/dist/components/data-table-tabs/data-table-tabs.d.ts +2 -0
- package/dist/components/data-table-tabs/data-table-tabs.d.ts.map +1 -1
- package/dist/components/data-table-tabs/index.js +1 -1
- package/dist/components/data-table-tabs/use-data-table-filter-tabs.d.ts +7 -0
- package/dist/components/data-table-tabs/use-data-table-filter-tabs.d.ts.map +1 -1
- package/dist/components/file-manager/index.js +1 -1
- package/dist/components/patient-table/index.js +1 -1
- package/dist/components/patient-table/use-responsive-columns.d.ts +7 -4
- package/dist/components/patient-table/use-responsive-columns.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/tokens.css +1 -1
- package/package.json +1 -1
- package/dist/_chunks/actions-cell-renderer-WUTyAes8.js +0 -1795
- package/dist/_chunks/actions-cell-renderer-WUTyAes8.js.map +0 -1
- package/dist/_chunks/balance-badge-cell-DHTiSFyD.js.map +0 -1
- package/dist/_chunks/data-table-tabs-C1kRUzat.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"balance-badge-cell-DHTiSFyD.js","sources":["../../src/components/patient-table/cell-renderers/transaction-chip-cell.tsx","../../src/components/patient-table/cell-renderers/care-plan-status-cell.tsx","../../src/components/patient-table/cell-renderers/next-appointment-cell.tsx","../../src/components/patient-table/columns.ts","../../src/components/patient-table/use-responsive-columns.ts","../../src/components/patient-table/patient-table.tsx","../../src/components/patient-table/cell-renderers/balance-badge-cell.tsx"],"sourcesContent":["import type { CustomCellRendererProps } from 'ag-grid-react';\nimport { TransactionChip } from '../../transaction-chip/transaction-chip';\nimport type { TransactionState } from '../../transaction-chip/transaction-chip';\nimport type { PatientRow } from '../types';\n\nexport interface TransactionChipCellParams {\n /** State shown for a positive amount; zero always reads as `settled`. */\n positiveState: Extract<TransactionState, 'debt' | 'credit' | 'to-invoice'>;\n /** Show the state label beside the amount. Default `true` — a table cell has no other caption. */\n showLabel?: boolean;\n}\n\n/**\n * Money column → `TransactionChip`, the kit's one financial-state pill (PRS\n * §27: \"Financial transaction state pill (debt / credit / settled)\"). Takes the\n * cell VALUE in integer cents — the `PatientRow` contract — and hands the chip\n * major units, so the row model and the chip each keep their own convention.\n */\nexport function TransactionChipCell(\n props: CustomCellRendererProps<PatientRow, number | null | undefined> &\n TransactionChipCellParams,\n) {\n const { value, positiveState, showLabel = true } = props;\n const cents =\n typeof value === 'number' && Number.isFinite(value) ? value : null;\n if (cents === null) return null;\n const state: TransactionState = cents > 0 ? positiveState : 'settled';\n return (\n <TransactionChip state={state} amount={cents / 100} showLabel={showLabel} />\n );\n}\n","/**\n * care-plan-status-cell — patient-table cell renderer\n * ----------------------------------------------------------------------------\n * A thin domain preset over the data-table `StatusCellRenderer` / kit `Badge`:\n * it bakes the patient care-plan lifecycle `variantMap` so consumers don't have\n * to re-declare it on every column, and localises the label via the `ui`\n * namespace (`ui.patientTable.carePlanStatus.*`).\n *\n * Reads `PatientRow.carePlanStatus` (preferring `data.carePlanStatus`, falling\n * back to AG Grid's resolved `value`), maps it to a Badge variant:\n *\n * accepted → success estimate → info none → neutral (muted)\n *\n * GROUND-TRUTH NOTES (vs. the task brief):\n * • `StatusCellRenderer` is not exported from a barrel and is a tiny wrapper\n * around `Badge`; this preset composes `Badge` directly (same primitive,\n * no extra indirection) rather than wrapping the wrapper.\n * • The brand says the \"muted\" variant is `none`; `Badge`'s neutral variant is\n * already the muted grey chip, so `none → 'neutral'`.\n * • No event handling: a status badge is presentational, so there are no\n * provided callbacks to wire.\n */\nimport type { CustomCellRendererProps } from 'ag-grid-react';\nimport { useTranslation } from 'react-i18next';\nimport { Badge } from '../../badge/badge';\nimport type { PatientCarePlanStatus, PatientRow } from '../types';\n\n/** Badge variant baked per care-plan lifecycle state. */\ntype CarePlanBadgeVariant = 'success' | 'info' | 'neutral';\n\n/**\n * Lifecycle → Badge-variant map. Frozen so it can't be mutated by a consumer\n * holding the reference.\n */\nconst CARE_PLAN_VARIANT_MAP: Readonly<\n Record<PatientCarePlanStatus, CarePlanBadgeVariant>\n> = {\n accepted: 'success',\n estimate: 'info',\n none: 'neutral',\n};\n\n/**\n * `cellRendererParams` for `CarePlanStatusCell`.\n *\n * The lifecycle status, label source and variant map are all baked in, so the\n * only knob is the Badge size — defaults to the compact `sm` chip that fits the\n * standard AG Grid row height.\n */\nexport interface CarePlanStatusCellParams {\n /** Badge size. Defaults to `'sm'` to fit the standard row height. */\n size?: 'sm' | 'md' | 'lg';\n}\n\nfunction isCarePlanStatus(value: unknown): value is PatientCarePlanStatus {\n return value === 'accepted' || value === 'estimate' || value === 'none';\n}\n\n/**\n * Patient care-plan lifecycle badge cell.\n *\n * Pass as `cellRenderer` on the `carePlanStatus` column; the patient\n * `variantMap` and localisation are handled internally.\n */\nexport function CarePlanStatusCell(\n props: CustomCellRendererProps<PatientRow, PatientCarePlanStatus> &\n CarePlanStatusCellParams,\n) {\n const { t } = useTranslation('ui');\n const { data, value, size = 'sm' } = props;\n\n // Prefer the typed row field; fall back to AG Grid's resolved cell value.\n const raw = data?.carePlanStatus ?? value;\n if (!isCarePlanStatus(raw)) return null;\n\n const variant = CARE_PLAN_VARIANT_MAP[raw];\n\n return (\n <Badge variant={variant} withDot size={size}>\n {t(`patientTable.carePlanStatus.${raw}`)}\n </Badge>\n );\n}\n","/**\n * next-appointment-cell — patient-table cell renderer\n * ----------------------------------------------------------------------------\n * Smart upcoming-appointment cell. Renders `PatientRow.nextAppointment` as a\n * locale-aware *relative* phrase (\"in 3 days\", \"tomorrow\") via the kit\n * `Timestamp`, mirroring the `relative` preset of the data-table\n * `DateCellRenderer`. When the appointment falls within a configurable window\n * (default 7 days from now), the cell takes a token-bound emphasis treatment so\n * imminent visits stand out at a glance. When `nextAppointment` is absent it\n * renders a muted \"no upcoming\" empty-state label.\n *\n * Reads `PatientRow.nextAppointment` (preferring the typed `data.nextAppointment`\n * field, falling back to AG Grid's resolved `value`).\n *\n * GROUND-TRUTH NOTES (vs. the task brief):\n * • The brief asked for a highlight \"within 7 days\". That window is exposed as\n * `soonWithinDays` (default 7) so a column can tune it without a new renderer,\n * matching the param-driven shape of the other patient renderers.\n * • The brief named `Avatar`, `Tag`, `Icon`, `IconButton`, `Tooltip` as\n * candidate primitives. This cell is purely presentational (a date + optional\n * emphasis), so it composes only `Timestamp` plus a decorative lucide glyph —\n * the same minimal-primitive approach the analogous `CarePlanStatusCell`\n * takes. There is no `Icon`/`IconButton` standalone component in the kit\n * (renderers use `lucide-react` glyphs directly), and no row callback is\n * provided, so no interactive affordance is wired.\n * • The emphasis is a CVA `tone` variant bound entirely to design tokens\n * (`--warning-foreground`); NO colour is hardcoded and nothing is pushed into\n * AG Grid `cellStyle`/`cellClass`.\n * • PHI rule (PRS 26 §6): no row field reaches a DOM `data-*` attribute here.\n */\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { CalendarClock } from 'lucide-react';\nimport type { CustomCellRendererProps } from 'ag-grid-react';\nimport { useTranslation } from 'react-i18next';\nimport { Timestamp } from '../../timestamp';\nimport type { PatientRow } from '../types';\n\n/* ------------------------------------------------------------------ */\n/* CVA — cell wrapper */\n/* */\n/* `tone` is the only stylistic axis: `soon` lifts the row to a */\n/* token-bound warning emphasis, `default` inherits the cell colour. */\n/* ------------------------------------------------------------------ */\n\nconst nextAppointmentVariants = cva(\n 'ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)] ds:whitespace-nowrap',\n {\n variants: {\n tone: {\n default: '',\n // Imminent visit: warning foreground token + medium weight. Fully\n // token-bound — no literal colour, no AG Grid cellStyle.\n soon: 'ds:text-[color:var(--warning-foreground)] ds:font-medium',\n // Absent appointment: muted empty state.\n empty: 'ds:text-[color:var(--muted-foreground)]',\n },\n },\n defaultVariants: { tone: 'default' },\n },\n);\n\ntype NextAppointmentVariantProps = VariantProps<typeof nextAppointmentVariants>;\n\n/* ------------------------------------------------------------------ */\n/* Params */\n/* ------------------------------------------------------------------ */\n\n/**\n * `cellRendererParams` for {@link NextAppointmentCell}.\n *\n * Localisation and the relative-date formatting are handled internally; the\n * only knob is the imminence window.\n */\nexport interface NextAppointmentCellParams {\n /**\n * Highlight the cell when the appointment is at most this many days away\n * (and not in the past). Defaults to `7`. A value `<= 0` disables the\n * highlight.\n */\n soonWithinDays?: number;\n}\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst DEFAULT_SOON_WITHIN_DAYS = 7;\n\nfunction toTime(value: unknown): number | null {\n if (value == null || value === '') return null;\n const t =\n value instanceof Date\n ? value.getTime()\n : typeof value === 'number'\n ? value\n : new Date(String(value)).getTime();\n return Number.isNaN(t) ? null : t;\n}\n\n/**\n * Resolve the emphasis tone for a parsed appointment time. `soon` covers any\n * upcoming visit within the window (including today); past appointments fall\n * back to `default`.\n */\nfunction resolveTone(\n time: number,\n soonWithinDays: number,\n): NonNullable<NextAppointmentVariantProps['tone']> {\n if (soonWithinDays <= 0) return 'default';\n const deltaMs = time - Date.now();\n if (deltaMs < 0) return 'default';\n return deltaMs <= soonWithinDays * DAY_MS ? 'soon' : 'default';\n}\n\n/* ------------------------------------------------------------------ */\n/* NextAppointmentCell */\n/* ------------------------------------------------------------------ */\n\n/**\n * Smart upcoming-appointment cell.\n *\n * Pass as `cellRenderer` on the `nextAppointment` column; the relative\n * formatting, imminence emphasis and empty state are handled internally.\n */\nexport function NextAppointmentCell(\n props: CustomCellRendererProps<PatientRow, string> &\n NextAppointmentCellParams,\n) {\n const { t } = useTranslation('ui');\n const { data, value, soonWithinDays = DEFAULT_SOON_WITHIN_DAYS } = props;\n\n // Prefer the typed row field; fall back to AG Grid's resolved cell value.\n const time = toTime(data?.nextAppointment ?? value);\n\n if (time === null) {\n return (\n <span className={nextAppointmentVariants({ tone: 'empty' })}>\n {t('patientTable.nextAppointment.none')}\n </span>\n );\n }\n\n const tone = resolveTone(time, soonWithinDays);\n\n return (\n <span className={nextAppointmentVariants({ tone })}>\n {tone === 'soon' ? (\n <CalendarClock aria-hidden=\"true\" className=\"ds:size-3.5 ds:shrink-0\" />\n ) : null}\n <Timestamp value={time} format=\"relative\" shape=\"bare\" />\n </span>\n );\n}\n","/**\n * patient-table — column definitions\n * ----------------------------------------------------------------------------\n * `buildPatientColumns(t, opts)` returns the ordered `ColDef<PatientRow>[]` for\n * the patient grid. Every patient field is its OWN column — sortable, filterable\n * and individually hideable via ColumnToggle — rather than several fields jumbled\n * into one composite cell. Each column wires the right kit cell renderer + the\n * right kit filter + floatingFilter, a localised `headerName` (from\n * `ui.patientTable.column.*`) and a stable `colId`.\n *\n * Identity is SPLIT across three pinned-start columns (the spec's\n * \"Avatar · First · Last\"):\n * • `avatar` — ImageCellRenderer (photo, initials fallback), sr-only header.\n * • `firstName` — plain text.\n * • `lastName` — LinkCellRenderer (the patient-detail link, `identityHref`).\n * All three are pinned-start P0 anchors and never auto-hide.\n *\n * COLUMN VISIBILITY\n * -----------------\n * `●` default-visible columns carry a `responsiveTier`; the responsive hook hides\n * them by tier as the grid narrows. Every other (grouped) column ships\n * `hide: true` and NO tier, so the hook leaves it alone and a user ColumnToggle\n * override wins. The anchors (avatar / firstName / lastName / actions) omit a\n * tier so they are never auto-hidden.\n *\n * RESPONSIVE TIER MECHANISM\n * -------------------------\n * Each column may carry a custom `responsiveTier` (`P0`–`P3`) property. AG Grid's\n * `ColDef` is an open/extensible shape — extra properties pass through untouched\n * at runtime — but its TS type is closed, so we type each entry as\n * `PatientColDefWithTier` and read the tier back via {@link getResponsiveTier}\n * (no `any`, no reliance on AG Grid's loose `context`).\n *\n * CUSTOM FIELDS\n * -------------\n * `opts.customFields` appends one dynamic column per descriptor after the\n * standard columns: `colId: \\`custom:${fieldName}\\``, a LITERAL `headerName`\n * (the practice-defined label — never i18n-keyed), a `valueGetter` reading\n * `row.customFields?.[fieldName]`, and a renderer + filter chosen by `type`\n * (`text` → plain text · `checkbox` → ✓/— · `dropdown` → Tag chip). All ship\n * `hide: true` and no tier.\n *\n * CONSTRAINTS:\n * • No colour / spacing / radius / font literal in any `cellStyle`/`cellClass`\n * here — only token-bound `ds:` utility classes — so the AG Grid theme bridge\n * stays in control of theming.\n * • CSS logical properties only.\n * • PHI rule (PRS 26 §6): no `PatientRow` field other than `id` is ever placed\n * in a DOM `data-*` attribute. Renderers surface name/contact/DOB as text\n * content only; these defs pass values, not attributes.\n */\nimport type { TFunction } from 'i18next';\nimport { Eye, Mail, MessageSquare, Pencil, Trash2, Users } from 'lucide-react';\nimport { createElement } from 'react';\nimport {\n ActionsCellRenderer,\n actionsColumnWidth,\n DateCellRenderer,\n DateRangeFilter,\n DateRangeFloatingFilter,\n ImageCellRenderer,\n LinkCellRenderer,\n NumberFilter,\n NumberFloatingFilter,\n SelectFilter,\n SelectFloatingFilter,\n StatusCellRenderer,\n TagListCellRenderer,\n TextFilter,\n TextFloatingFilter,\n TypeaheadFilter,\n TypeaheadFloatingFilter,\n UserCellRenderer,\n type ActionDef,\n type StatusCellRendererParams,\n type UserCellValue,\n} from '../data-table';\nimport { TransactionChipCell } from './cell-renderers/transaction-chip-cell';\nimport { CarePlanStatusCell } from './cell-renderers/care-plan-status-cell';\nimport { NextAppointmentCell } from './cell-renderers/next-appointment-cell';\nimport type {\n PatientColDef,\n PatientColumnDef,\n PatientColumnId,\n PatientCustomFieldDef,\n PatientRow,\n PatientRowAction,\n ResponsiveTier,\n} from './types';\n\n/* -------------------------------------------------------------------------- */\n/* Responsive-tier extension */\n/* -------------------------------------------------------------------------- */\n\n/**\n * A patient leaf column carrying its responsive-priority tier. AG Grid passes\n * the extra `responsiveTier` key through untouched; we keep it strongly typed\n * (no `any`, no reliance on AG Grid's `context: any`) so the responsive hook can\n * read it back via {@link getResponsiveTier}.\n */\nexport type PatientColDefWithTier = PatientColDef & {\n /**\n * Responsive priority. Lower numbers survive longer as the grid narrows:\n * `P0` always visible, `P3` first to hide. Consumed by `useResponsiveColumns`.\n */\n responsiveTier?: ResponsiveTier;\n};\n\n/**\n * Read the responsive tier off a column definition. Returns `undefined` for\n * columns that opt out of responsive toggling — the pinned anchors and every\n * `hide: true` grouped column deliberately omit a tier so they are never\n * auto-managed by the responsive hook.\n */\nexport function getResponsiveTier(\n colDef: PatientColumnDef,\n): ResponsiveTier | undefined {\n return (colDef as PatientColDefWithTier).responsiveTier;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Options */\n/* -------------------------------------------------------------------------- */\n\n/** Options threaded into {@link buildPatientColumns}. */\nexport interface BuildPatientColumnsOptions {\n /**\n * Mask sensitive contact details (email) — when `true` the `email` column\n * ships hidden. Forwarded from `PatientTable`'s `hidePatientDetails`. Default\n * `false`.\n */\n hideDetails?: boolean;\n /**\n * Live getter for the actions-column density. When it returns `true`, the\n * `ActionsCellRenderer` collapses its inline buttons into a single \"…\" overflow\n * menu (the spec's 180→88px compact density). Reading via a getter lets a\n * density toggle flip without rebuilding column defs (preserves column state) —\n * call `api.refreshCells({ force: true })` after flipping the backing value.\n * Defaults to a getter that always returns `false`.\n */\n getActionsCompact?: () => boolean;\n /**\n * Invoked when a per-row action button is activated, with the resolved action\n * descriptor and the patient row. Only `row.id` ever leaves this boundary into\n * persistence / agent surfaces — never PHI.\n */\n onRowAction?: (action: PatientRowAction, patient: PatientRow) => void;\n /**\n * Derive the patient-detail href for the `lastName` link column, e.g.\n * `(row) => \\`/patients/${row.id}\\``. When omitted the lastName cell renders a\n * button that calls `onRowAction` with the `show` action instead.\n */\n identityHref?: string | ((row: PatientRow) => string | undefined);\n /**\n * Per-practice custom-field descriptors. Each appends one dynamic `custom:*`\n * column (hidden by default) after the standard columns. See\n * {@link PatientCustomFieldDef}.\n */\n customFields?: PatientCustomFieldDef[];\n}\n\n/* -------------------------------------------------------------------------- */\n/* Row actions */\n/* -------------------------------------------------------------------------- */\n\n/**\n * The fixed number of per-row actions the patient grid builds (show · edit ·\n * email reminder · sms reminder · reassign · delete — see {@link buildRowActions}).\n * Exposed so the component can size the actions column to its EXPANDED footprint\n * (`actionsColumnWidth(PATIENT_ROW_ACTION_COUNT)`) when flipping the runtime\n * compact-actions toggle off, without rebuilding the column defs. Keep in sync\n * with the array {@link buildRowActions} returns.\n */\nexport const PATIENT_ROW_ACTION_COUNT = 6;\n\n/**\n * Build the per-row `ActionDef`s for the actions column. Each is mapped from the\n * spec's closed action set; `hidden` / `disabled` predicates encode the spec's\n * per-row rules (e.g. `sendEmailReminder` hidden when `!emailEnabled`).\n *\n * Bridges the kit `ActionDef<PatientRow>` shape (icon/label/onClick) to the\n * patient `onRowAction(action, patient)` contract by re-deriving a\n * `PatientRowAction` descriptor on click.\n */\nfunction buildRowActions(\n t: TFunction,\n onRowAction: BuildPatientColumnsOptions['onRowAction'],\n): ActionDef<PatientRow>[] {\n const dispatch =\n (action: PatientRowAction) =>\n (patient: PatientRow): void => {\n onRowAction?.(action, patient);\n };\n\n // Each entry pairs the kit ActionDef (what the renderer needs) with the\n // patient-typed descriptor handed back to `onRowAction`.\n const make = (\n action: PatientRowAction,\n overrides: Omit<ActionDef<PatientRow>, 'label' | 'onClick' | 'icon'>,\n ): ActionDef<PatientRow> => ({\n icon: action.icon,\n label: action.label,\n variant: action.variant,\n onClick: dispatch(action),\n ...overrides,\n });\n\n return [\n make(\n {\n id: 'show',\n label: t('patientTable.actions.show'),\n icon: createElement(Eye, { 'aria-hidden': 'true' }),\n },\n {},\n ),\n make(\n {\n id: 'edit',\n label: t('patientTable.actions.edit'),\n icon: createElement(Pencil, { 'aria-hidden': 'true' }),\n },\n {},\n ),\n make(\n {\n id: 'sendEmailReminder',\n label: t('patientTable.actions.sendEmailReminder'),\n icon: createElement(Mail, { 'aria-hidden': 'true' }),\n },\n // Hidden when the patient has email contact disabled (spec).\n { hidden: (patient) => !patient.emailEnabled },\n ),\n make(\n {\n id: 'sendSmsReminder',\n label: t('patientTable.actions.sendSmsReminder'),\n icon: createElement(MessageSquare, { 'aria-hidden': 'true' }),\n },\n // Hidden when no phone number is SMS-enabled (spec).\n {\n hidden: (patient) =>\n !(patient.phoneNumbers ?? []).some((p) => p.smsEnabled),\n },\n ),\n make(\n {\n id: 'reassign',\n label: t('patientTable.actions.reassign'),\n icon: createElement(Users, { 'aria-hidden': 'true' }),\n },\n {},\n ),\n make(\n {\n id: 'delete',\n label: t('patientTable.actions.delete'),\n icon: createElement(Trash2, { 'aria-hidden': 'true' }),\n variant: 'destructive',\n },\n // Disabled (with a reason) for archived patients — the row model's\n // closest \"cannot delete\" signal. Surfaced as the inline tooltip.\n {\n disabled: (patient) => Boolean(patient.isArchived),\n disabledReason: t('patientTable.actions.deleteLocked'),\n },\n ),\n ];\n}\n\n/* -------------------------------------------------------------------------- */\n/* Helpers */\n/* -------------------------------------------------------------------------- */\n\nconst NO = () => false;\n\n/** Glyph for boolean / eligibility valueFormatter (✓ when true, — otherwise). */\nconst CHECK = '✓';\nconst DASH = '—';\n\n/** Compute integer age (years) from an ISO date-of-birth string. */\nfunction ageFromDob(dob: string | undefined): number | null {\n if (!dob) return null;\n const birth = new Date(dob);\n const ms = birth.getTime();\n if (Number.isNaN(ms)) return null;\n const now = new Date();\n let age = now.getFullYear() - birth.getFullYear();\n const m = now.getMonth() - birth.getMonth();\n if (m < 0 || (m === 0 && now.getDate() < birth.getDate())) age -= 1;\n return age >= 0 ? age : null;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Custom-field columns */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Build the dynamic `custom:*` columns from the practice descriptors. Each is\n * appended hidden (no tier) after the standard columns: a literal `headerName`\n * (the descriptor's `label`, never i18n-keyed), a `valueGetter` reading\n * `row.customFields?.[fieldName]`, and a renderer + filter chosen by `type`.\n */\nfunction buildCustomFieldColumns(\n fields: PatientCustomFieldDef[],\n): PatientColDefWithTier[] {\n return fields.map((field): PatientColDefWithTier => {\n const colId: PatientColumnId = `custom:${field.fieldName}`;\n const base: PatientColDefWithTier = {\n colId,\n headerName: field.label,\n hide: true,\n // No tier — custom fields are opt-in via ColumnToggle only.\n };\n\n if (field.type === 'checkbox') {\n return {\n ...base,\n valueGetter: (params) =>\n params.data?.customFields?.[field.fieldName] === true,\n valueFormatter: (params) => (params.value === true ? CHECK : DASH),\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n filterParams: {\n options: [\n { value: true, label: CHECK },\n { value: false, label: DASH },\n ],\n },\n minWidth: 110,\n width: 130,\n };\n }\n\n if (field.type === 'dropdown') {\n return {\n ...base,\n // TagListCellRenderer expects an array; wrap the single value.\n valueGetter: (params) => {\n const raw = params.data?.customFields?.[field.fieldName];\n return typeof raw === 'string' && raw ? [raw] : [];\n },\n cellRenderer: TagListCellRenderer,\n filterValueGetter: (params) => {\n const raw = params.data?.customFields?.[field.fieldName];\n return typeof raw === 'string' ? raw : '';\n },\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n filterParams: {\n // Undefined (not []) when the field declares no options, so the\n // filter derives its choices from the rows. An empty array would\n // mean \"this column has no facets\" and leave the filter unusable.\n options: field.options?.map((opt) => ({\n value: opt,\n label: opt,\n })),\n },\n sortable: false,\n minWidth: 130,\n width: 160,\n };\n }\n\n // text\n return {\n ...base,\n valueGetter: (params) => {\n const raw = params.data?.customFields?.[field.fieldName];\n return typeof raw === 'string' ? raw : '';\n },\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 140,\n width: 170,\n };\n });\n}\n\n/* -------------------------------------------------------------------------- */\n/* buildPatientColumns */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Build the ordered patient-grid column definitions — one field per column.\n *\n * @param t `react-i18next` translation fn (default `ui` namespace).\n * @param opts See {@link BuildPatientColumnsOptions} — density getter, row-action\n * callback, detail masking, identity href, custom fields.\n * @returns Ordered `PatientColumnDef[]`. Default-visible columns carry a\n * `responsiveTier` (read via {@link getResponsiveTier}); the pinned\n * anchors and every grouped (`hide: true`) column omit it so they are\n * never auto-hidden.\n */\nexport function buildPatientColumns(\n t: TFunction,\n opts: BuildPatientColumnsOptions = {},\n): PatientColumnDef[] {\n const {\n hideDetails = false,\n getActionsCompact = NO,\n onRowAction,\n identityHref,\n customFields = [],\n } = opts;\n\n const actions = buildRowActions(t, onRowAction);\n\n const genderLabels: Record<NonNullable<PatientRow['gender']>, string> = {\n m: t('patientTable.gender.m'),\n f: t('patientTable.gender.f'),\n x: t('patientTable.gender.x'),\n };\n\n // Tri-state marketing-consent → label + Badge variant (StatusCellRenderer).\n const consentLabels = {\n yes: t('patientTable.consent.yes'),\n no: t('patientTable.consent.no'),\n notSpecified: t('patientTable.consent.notSpecified'),\n };\n const consentVariantMap: StatusCellRendererParams['variantMap'] = {\n [consentLabels.yes]: 'success',\n [consentLabels.no]: 'error',\n [consentLabels.notSpecified]: 'neutral',\n };\n\n const header = (id: PatientColumnId): string =>\n t(`patientTable.column.${id}`);\n\n const columns: PatientColDefWithTier[] = [\n /* ── Avatar (P0, pinned-start) ─────────────────────────────────── */\n {\n colId: 'avatar',\n // sr-only header — the photo column needs no visible label, but AG Grid\n // and AT still want a name. `headerName` reads \"Photo\"; the visible header\n // TEXT is clipped via the kit's `sr-only-th` convention (clips only\n // `.ag-header-cell-text`, keeping the header-cell box in the grid chrome\n // and the name in the a11y tree — see ag-grid-theme.css).\n headerName: header('avatar'),\n headerClass: 'sr-only-th',\n pinned: 'left',\n cellRenderer: ImageCellRenderer,\n cellRendererParams: {\n srcField: 'avatarUrl',\n fallbackField: 'lastName',\n size: 'sm',\n shape: 'circle',\n },\n sortable: false,\n filter: false,\n resizable: false,\n cellClass: 'ag-cell-graphic',\n // Hug the 32px (size=\"sm\") avatar + cell padding — a tight fixed column,\n // not a wide empty one. width === minWidth === maxWidth pins it to size so\n // the pinned group can't stretch it (pinned columns never flex).\n //\n // 48 = 32 (avatar) + 16 (--spacing-sm each side, the reduced padding the\n // theme gives graphic-only cells). The old 52 assumed 20px of padding\n // against a cell that actually had 30px, so the avatar clipped by 10px.\n width: 48,\n minWidth: 48,\n maxWidth: 48,\n // Anchor — never auto-hidden (no tier).\n },\n\n /* ── First name (P0, pinned-start) — plain text ────────────────── */\n {\n colId: 'firstName',\n headerName: header('firstName'),\n pinned: 'left',\n field: 'firstName',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n // Size to content: width === minWidth so the pinned column sits tight with\n // no dead space before the next pinned column (pinned columns can't flex).\n width: 140,\n minWidth: 120,\n // Anchor — never auto-hidden (no tier).\n },\n\n /* ── Last name (P0, pinned-start) — the patient-detail link ────── */\n {\n colId: 'lastName',\n headerName: header('lastName'),\n pinned: 'left',\n field: 'lastName',\n cellRenderer: LinkCellRenderer,\n cellRendererParams: {\n href: identityHref,\n // When no detail href is supplied the cell renders a button; route its\n // click through the same `show` row action.\n onClick: onRowAction\n ? (row: PatientRow) =>\n onRowAction(\n { id: 'show', label: t('patientTable.actions.show') },\n row,\n )\n : undefined,\n },\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n // Size to content: a modest pinned width with no stretched default, so the\n // avatar + first + last group ends tight against the scrollable columns.\n width: 150,\n minWidth: 130,\n // Anchor — never auto-hidden (no tier).\n },\n\n /* ── Phone (P1) ────────────────────────────────────────────────── */\n {\n colId: 'phone',\n headerName: header('phone'),\n responsiveTier: 'P1',\n // The primary phone number string (prefix + number).\n valueGetter: (params) => params.data?.phoneNumbers?.[0]?.number ?? '',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 160,\n width: 180,\n },\n\n /* ── Next appointment (P1) ─────────────────────────────────────── */\n {\n colId: 'nextAppointment',\n headerName: header('nextAppointment'),\n responsiveTier: 'P1',\n field: 'nextAppointment',\n cellRenderer: NextAppointmentCell,\n filter: DateRangeFilter,\n floatingFilterComponent: DateRangeFloatingFilter,\n minWidth: 160,\n width: 180,\n },\n\n /* ── Unpaid balance (P1) ───────────────────────────────────────── */\n {\n colId: 'unpaidBalance',\n headerName: header('unpaidBalance'),\n responsiveTier: 'P1',\n field: 'unpaidAmount',\n type: 'numericColumn',\n // The kit's financial-state pill: `debt` while anything is owed, `settled` at zero.\n cellRenderer: TransactionChipCell,\n cellRendererParams: { positiveState: 'debt' },\n filter: NumberFilter,\n floatingFilterComponent: NumberFloatingFilter,\n // Money is integer cents; the inRange filter compares cents directly.\n filterParams: { defaultOperator: 'inRange', min: 0 },\n minWidth: 150,\n width: 170,\n },\n\n /* ── Email (P2) — hidden when masking ──────────────────────────── */\n {\n colId: 'email',\n headerName: header('email'),\n responsiveTier: 'P2',\n field: 'email',\n // Emit `hide` ONLY when masking (never a literal `hide: false`). A static\n // `hide: false` is re-applied by AG Grid on every `columnDefs` reconcile,\n // clobbering a runtime hide from the ViewSwitcher / responsive hook.\n hide: hideDetails || undefined,\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 200,\n width: 240,\n },\n\n /* ── Care-plan status (P2) ─────────────────────────────────────── */\n {\n colId: 'carePlanStatus',\n headerName: header('carePlanStatus'),\n responsiveTier: 'P2',\n field: 'carePlanStatus',\n cellRenderer: CarePlanStatusCell,\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n filterParams: {\n options: [\n {\n value: 'accepted',\n label: t('patientTable.carePlanStatus.accepted'),\n },\n {\n value: 'estimate',\n label: t('patientTable.carePlanStatus.estimate'),\n },\n { value: 'none', label: t('patientTable.carePlanStatus.none') },\n ],\n },\n sortable: false,\n minWidth: 130,\n width: 150,\n },\n\n /* ── Fiscal code (P3, text) ────────────────────────────────────── */\n {\n colId: 'fiscalCode',\n headerName: header('fiscalCode'),\n responsiveTier: 'P3',\n field: 'italianFiscalCode',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 160,\n width: 180,\n },\n\n /* ── Age (P3, derived numeric) ─────────────────────────────────── */\n {\n colId: 'age',\n headerName: header('age'),\n responsiveTier: 'P3',\n // Left-aligned like the text columns — `numericColumn` (which right-aligns\n // header + cell) is for ledger figures, not a 2-digit age. Sort stays\n // numeric because the valueGetter returns a number.\n valueGetter: (params) =>\n ageFromDob(params.data?.dateOfBirth) ?? undefined,\n filter: NumberFilter,\n floatingFilterComponent: NumberFloatingFilter,\n minWidth: 90,\n width: 110,\n },\n\n /* ── Gender (P3) ───────────────────────────────────────────────── */\n {\n colId: 'gender',\n headerName: header('gender'),\n responsiveTier: 'P3',\n // Map the raw code to a localised label for both cell + set filter.\n valueGetter: (params) =>\n params.data?.gender ? genderLabels[params.data.gender] : DASH,\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n filterParams: {\n options: [\n { value: genderLabels.m, label: genderLabels.m },\n { value: genderLabels.f, label: genderLabels.f },\n { value: genderLabels.x, label: genderLabels.x },\n ],\n },\n minWidth: 110,\n width: 130,\n },\n\n /* ── Source (P3) ───────────────────────────────────────────────── */\n {\n colId: 'source',\n headerName: header('source'),\n responsiveTier: 'P3',\n // TagListCellRenderer expects an array; wrap the resolved source string.\n valueGetter: (params) => {\n const s = params.data?.source ?? params.data?.customSource;\n return s ? [s] : [];\n },\n cellRenderer: TagListCellRenderer,\n filterValueGetter: (params) =>\n params.data?.source ?? params.data?.customSource ?? '',\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n sortable: false,\n minWidth: 120,\n width: 150,\n },\n\n /* ── Grouped: Identity — hidden by default ─────────────────────── */\n {\n colId: 'dateOfBirth',\n headerName: header('dateOfBirth'),\n hide: true,\n field: 'dateOfBirth',\n cellRenderer: DateCellRenderer,\n cellRendererParams: { format: 'date' },\n filter: DateRangeFilter,\n floatingFilterComponent: DateRangeFloatingFilter,\n minWidth: 140,\n width: 160,\n },\n {\n colId: 'placeOfBirth',\n headerName: header('placeOfBirth'),\n hide: true,\n field: 'placeOfBirth',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 140,\n width: 160,\n },\n {\n colId: 'countryOfBirth',\n headerName: header('countryOfBirth'),\n hide: true,\n field: 'countryOfBirth',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 130,\n width: 150,\n },\n\n /* ── Grouped: Contact — hidden by default ──────────────────────── */\n {\n colId: 'smsReminders',\n headerName: header('smsReminders'),\n hide: true,\n valueGetter: (params) => Boolean(params.data?.smsReminderEligible),\n valueFormatter: (params) => (params.value === true ? CHECK : DASH),\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n filterParams: {\n options: [\n { value: true, label: CHECK },\n { value: false, label: DASH },\n ],\n },\n minWidth: 120,\n width: 140,\n },\n {\n colId: 'emailReminders',\n headerName: header('emailReminders'),\n hide: true,\n valueGetter: (params) => Boolean(params.data?.emailReminderEligible),\n valueFormatter: (params) => (params.value === true ? CHECK : DASH),\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n filterParams: {\n options: [\n { value: true, label: CHECK },\n { value: false, label: DASH },\n ],\n },\n minWidth: 120,\n width: 140,\n },\n {\n colId: 'pec',\n headerName: header('pec'),\n hide: true,\n field: 'pecAddress',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 200,\n width: 240,\n },\n\n /* ── Grouped: Appointments — hidden by default ─────────────────── */\n {\n colId: 'lastVisit',\n headerName: header('lastVisit'),\n hide: true,\n field: 'lastAppointment',\n cellRenderer: DateCellRenderer,\n cellRendererParams: { format: 'relative' },\n filter: DateRangeFilter,\n floatingFilterComponent: DateRangeFloatingFilter,\n minWidth: 140,\n width: 160,\n },\n\n /* ── Grouped: Financial — hidden by default ────────────────────── */\n {\n colId: 'billableAmount',\n headerName: header('billableAmount'),\n hide: true,\n type: 'numericColumn',\n field: 'billableAmount',\n // \"Fatturabile\" is the chip's own `to-invoice` state; `settled` at zero.\n // Same shape as unpaidBalance: the cell value stays in cents and the\n // renderer converts, so the inRange filter compares cents directly.\n cellRenderer: TransactionChipCell,\n cellRendererParams: { positiveState: 'to-invoice' },\n filter: NumberFilter,\n floatingFilterComponent: NumberFloatingFilter,\n filterParams: { defaultOperator: 'inRange', min: 0 },\n minWidth: 140,\n width: 160,\n },\n {\n colId: 'discount',\n headerName: header('discount'),\n hide: true,\n field: 'defaultDiscount',\n type: 'numericColumn',\n valueFormatter: (params) =>\n params.value == null ? '' : `${params.value}%`,\n filter: NumberFilter,\n floatingFilterComponent: NumberFloatingFilter,\n minWidth: 110,\n width: 130,\n },\n\n /* ── Grouped: Clinical — hidden by default ─────────────────────── */\n {\n colId: 'diagnosis',\n headerName: header('diagnosis'),\n hide: true,\n field: 'diagnosis',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 180,\n width: 220,\n },\n {\n colId: 'insurance',\n headerName: header('insurance'),\n hide: true,\n field: 'defaultInsurance',\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n minWidth: 140,\n width: 160,\n },\n {\n colId: 'assignedTo',\n headerName: header('assignedTo'),\n hide: true,\n cellRenderer: UserCellRenderer,\n valueGetter: (params) =>\n params.data?.personalAssistant\n ? ({ name: params.data.personalAssistant } satisfies UserCellValue)\n : null,\n comparator: (a: UserCellValue | null, b: UserCellValue | null) =>\n (a?.name ?? '').localeCompare(b?.name ?? ''),\n filterValueGetter: (params) => params.data?.personalAssistant ?? '',\n filter: TypeaheadFilter,\n floatingFilterComponent: TypeaheadFloatingFilter,\n minWidth: 160,\n width: 180,\n },\n {\n colId: 'job',\n headerName: header('job'),\n hide: true,\n field: 'job',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 140,\n width: 160,\n },\n\n /* ── Grouped: Admin — hidden by default ────────────────────────── */\n {\n colId: 'marketingConsent',\n headerName: header('marketingConsent'),\n hide: true,\n cellRenderer: StatusCellRenderer,\n cellRendererParams: {\n variantMap: consentVariantMap,\n } satisfies StatusCellRendererParams,\n valueGetter: (params) => {\n switch (params.data?.marketingConsent) {\n case 1:\n return consentLabels.yes;\n case 0:\n return consentLabels.no;\n default:\n return consentLabels.notSpecified;\n }\n },\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n filterParams: {\n options: [\n { value: consentLabels.yes, label: consentLabels.yes },\n { value: consentLabels.no, label: consentLabels.no },\n {\n value: consentLabels.notSpecified,\n label: consentLabels.notSpecified,\n },\n ],\n },\n sortable: false,\n minWidth: 150,\n width: 170,\n },\n {\n colId: 'patientNumber',\n headerName: header('patientNumber'),\n hide: true,\n // `YYYY/N` yearly numbering.\n valueGetter: (params) => {\n const year = params.data?.yearlyNumberingYear;\n const num = params.data?.yearlyNumberingNumber;\n return year != null && num != null ? `${year}/${num}` : '';\n },\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 120,\n width: 140,\n },\n {\n colId: 'portal',\n headerName: header('portal'),\n hide: true,\n valueGetter: (params) => Boolean(params.data?.mydentista),\n valueFormatter: (params) => (params.value === true ? CHECK : DASH),\n filter: SelectFilter,\n floatingFilterComponent: SelectFloatingFilter,\n filterParams: {\n options: [\n { value: true, label: CHECK },\n { value: false, label: DASH },\n ],\n },\n minWidth: 110,\n width: 130,\n },\n {\n colId: 'createdAt',\n headerName: header('createdAt'),\n hide: true,\n field: 'createdAt',\n cellRenderer: DateCellRenderer,\n cellRendererParams: { format: 'date' },\n filter: DateRangeFilter,\n floatingFilterComponent: DateRangeFloatingFilter,\n minWidth: 140,\n width: 160,\n },\n\n /* ── Grouped: Address — hidden by default ──────────────────────── */\n {\n colId: 'city',\n headerName: header('city'),\n hide: true,\n field: 'city',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 120,\n width: 150,\n },\n {\n colId: 'province',\n headerName: header('province'),\n hide: true,\n field: 'province',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 100,\n width: 120,\n },\n {\n colId: 'postcode',\n headerName: header('postcode'),\n hide: true,\n field: 'postcode',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 110,\n width: 130,\n },\n {\n colId: 'country',\n headerName: header('country'),\n hide: true,\n field: 'countryCode',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 110,\n width: 130,\n },\n {\n colId: 'street',\n headerName: header('street'),\n hide: true,\n field: 'street',\n filter: TextFilter,\n floatingFilterComponent: TextFloatingFilter,\n minWidth: 180,\n width: 220,\n },\n\n /* ── Custom fields (per-practice, dynamic) — appended, hidden ──── */\n ...buildCustomFieldColumns(customFields),\n\n /* ── Actions (P0, pinned-end) ──────────────────────────────────── */\n {\n colId: 'actions',\n headerName: header('actions'),\n // Pinned to the inline-end edge so actions stay reachable while the field\n // columns scroll. AG Grid `pinned` is physical — the grid's `enableRtl`\n // flips this to the start edge under RTL. Locked: neither a header drag\n // nor a restored column-state snapshot may move it off the edge.\n pinned: 'right',\n lockPinned: true,\n suppressMovable: true,\n // Header + action group left-aligned (justify-start), consistent with every\n // other column — no right-aligned header (the actions still sit at the\n // pinned inline-end edge, the group just starts at the cell's leading edge).\n cellClass: 'ds:justify-start ag-cell-graphic',\n cellRenderer: ActionsCellRenderer,\n cellRendererParams: {\n actions,\n intent: 'ghost',\n group: true,\n label: t('patientTable.actions.group'),\n collapsed: getActionsCompact,\n },\n sortable: false,\n filter: false,\n resizable: false,\n // The renderer collapses to a single overflow trigger via\n // `getActionsCompact`; the expanded width is DERIVED from the action count\n // so a row showing every action can't clip — 88px collapsed → fits-all.\n width: actionsColumnWidth(actions.length),\n minWidth: 88,\n // Anchor — never auto-hidden (no tier).\n },\n ];\n\n return columns;\n}\n","/**\n * patient-table — responsive column hook\n * ----------------------------------------------------------------------------\n * `useResponsiveColumns(handleRef, opts)` toggles column visibility by the\n * `responsiveTier` (`P0`–`P3`) baked onto each `ColDef` by `buildPatientColumns`,\n * driven by the *grid container's* width — measured with a `ResizeObserver`, NOT\n * the viewport — so the table responds correctly when embedded in a narrow panel,\n * split pane or sidebar rather than only at browser-window breakpoints.\n *\n * BREAKPOINTS (container inline-size → highest tier kept visible):\n *\n * < 640px → P0 only (pinned avatar / firstName / lastName / actions)\n * < 900px → P0 + P1 (+ phone, nextAppointment, balance)\n * < 1200px → P0 + P1 + P2 (+ email, carePlanStatus)\n * ≥ 1200px → all tiers (P0–P3) (+ fiscalCode, age, gender, source)\n *\n * Columns without a tier (the pinned avatar / firstName / lastName / actions\n * anchors, and every grouped `hide: true` column) are never touched — the\n * anchors stay visible at every width; the grouped columns are owned by\n * ColumnToggle only.\n *\n * RESPONSIVE ↔ PERSISTENCE RECONCILIATION\n * ---------------------------------------\n * `data-table.tsx` persists the full AG Grid column state to\n * `localStorage[\"data-table-col-state:<gridId>\"]` (load on grid-ready, save on\n * unmount). A user's explicit `ColumnToggle` choice lives in that persisted state\n * and MUST win over — and survive — the responsive baseline. Reconciliation:\n *\n * 1. The responsive baseline is exactly that — a *baseline*. We only auto\n * show/hide a column the user has NOT manually toggled this session.\n * 2. We track a `Set<colId>` of user-touched columns. We learn of a manual\n * toggle from the grid's `columnVisible` event: any such event whose\n * `source` is NOT `'api'` is user-driven (ColumnToggle tool panel, column\n * menu, context menu, drag), so we add those colIds to the set. Our own\n * responsive calls go through `setColumnsVisible(..., 'api')`, so they never\n * pollute the set (and we additionally guard with a re-entrancy flag).\n * 3. Once a column is user-touched, the hook leaves it alone forever this\n * session — its visibility is whatever the user chose, which is what\n * `data-table.tsx` then persists on unmount. The persisted choice therefore\n * wins on the next mount too: we seed the user-touched set from the saved\n * column state so a previously hidden/shown column is treated as a manual\n * override and not clobbered by the baseline on re-entry.\n *\n * CONSTRAINTS: no `any` (AG Grid's `ColumnEventType` is read off the typed event,\n * tier read via the typed `getResponsiveTier` helper). No DOM `data-*` attribute\n * is written here (PHI rule is moot — this hook only reads colIds and widths).\n */\nimport { useCallback, useEffect, useRef, type RefObject } from 'react';\nimport type { Column, ColumnVisibleEvent } from 'ag-grid-community';\nimport { getResponsiveTier } from './columns';\nimport type { PatientRow, PatientTableHandle, ResponsiveTier } from './types';\n\n/* -------------------------------------------------------------------------- */\n/* Breakpoint model */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Container inline-size breakpoints. Each entry is the *minimum* container width\n * (px) at which the named tier becomes visible. Below the first threshold only\n * `P0` shows; at or above the last, everything shows.\n */\ninterface Breakpoint {\n /** Minimum container inline-size (px) for this tier to be shown. */\n minWidth: number;\n /** The tier this breakpoint reveals. */\n tier: ResponsiveTier;\n}\n\nconst DEFAULT_BREAKPOINTS: readonly Breakpoint[] = [\n { minWidth: 0, tier: 'P0' },\n { minWidth: 640, tier: 'P1' },\n { minWidth: 900, tier: 'P2' },\n { minWidth: 1200, tier: 'P3' },\n];\n\n/** Resolve the set of tiers that should be visible at a given container width. */\nfunction visibleTiersForWidth(\n width: number,\n breakpoints: readonly Breakpoint[],\n): Set<ResponsiveTier> {\n const visible = new Set<ResponsiveTier>();\n for (const bp of breakpoints) {\n if (width >= bp.minWidth) visible.add(bp.tier);\n }\n // P0 is always visible regardless of width.\n visible.add('P0');\n return visible;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Options */\n/* -------------------------------------------------------------------------- */\n\nexport interface UseResponsiveColumnsOptions {\n /**\n * The grid container element to observe. The hook measures THIS element's\n * inline-size (not the viewport) so it works embedded. When the ref is unset\n * the hook is inert.\n */\n containerRef: RefObject<HTMLElement | null>;\n /**\n * Disable responsive toggling entirely (the `responsive={false}` escape hatch\n * on `PatientTable`). When `false` the hook unobserves and makes no visibility\n * changes — column visibility is left entirely to persistence / ColumnToggle.\n * Default `true`.\n */\n enabled?: boolean;\n /**\n * Override the container-width breakpoints. Ordered low→high `minWidth`; each\n * reveals its `tier`. Defaults to 640 / 900 / 1200 (P1 / P2 / P3).\n */\n breakpoints?: readonly Breakpoint[];\n}\n\n/* -------------------------------------------------------------------------- */\n/* Return shape */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Imperative surface returned by {@link useResponsiveColumns}. The responsive\n * effect runs internally; `lockColumns` lets a caller (the view switcher) mark\n * columns as user-touched so the responsive baseline stops auto-managing them.\n */\nexport interface UseResponsiveColumnsResult {\n /**\n * Mark the given colIds as \"user-touched\" so the responsive baseline leaves\n * their visibility alone from here on — exactly as if the user had toggled\n * them via `ColumnToggle`. The view switcher calls this when it applies a\n * view's column visibility, so its programmatic (`source: 'api'`) visibility\n * writes are not immediately re-hidden/re-shown by the next responsive\n * reconcile pass. No-op (with the colIds still recorded for a later mount of\n * the effect) when the hook is disabled.\n */\n lockColumns: (colIds: readonly string[]) => void;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Hook */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Wire responsive, tier-driven column visibility to the patient grid.\n *\n * @param handleRef The `PatientTable` imperative handle ref — the hook reaches\n * the raw AG Grid API via `handleRef.current.getRawApi()`.\n * @param opts Container ref to observe, plus enable flag / breakpoint\n * overrides. See {@link UseResponsiveColumnsOptions}.\n * @returns {@link UseResponsiveColumnsResult} — `lockColumns(colIds)`\n * to mark columns user-touched (see the view-switcher\n * coordination note in patient-table.tsx).\n */\nexport function useResponsiveColumns(\n handleRef: RefObject<PatientTableHandle | null>,\n opts: UseResponsiveColumnsOptions,\n): UseResponsiveColumnsResult {\n const {\n containerRef,\n enabled = true,\n breakpoints = DEFAULT_BREAKPOINTS,\n } = opts;\n\n // colIds the user has explicitly toggled this session — responsive auto-toggle\n // must never override these (their persisted visibility wins). A ref, not\n // state: mutating it must not re-run the effect or re-render. Shared between\n // the responsive effect (which seeds + grows it from `columnVisible` events)\n // and the exported `lockColumns` (which the view switcher uses to register a\n // view-driven visibility change as a user override).\n const userTouchedRef = useRef<Set<string>>(new Set());\n // Re-entrancy guard: true while WE are applying a responsive change, so the\n // resulting `columnVisible` event is not misread as a user toggle even if a\n // future AG Grid version reports a non-`api` source for it.\n const applyingRef = useRef(false);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!enabled || !container) return;\n\n // Resolve the live API lazily on each tick — the grid may not be ready on\n // the first observer callback.\n const getApi = () => handleRef.current?.getRawApi();\n\n /**\n * Record a user-driven visibility change so the column is left alone from\n * here on. `source: 'api'` is the only programmatic source the hook itself\n * uses; everything else (tool panel, column menu, context menu, drag) is the\n * user. The re-entrancy flag covers our own calls defensively.\n */\n const onColumnVisible = (event: ColumnVisibleEvent<PatientRow>) => {\n if (applyingRef.current) return;\n if (event.source === 'api') return;\n const touched = event.columns ?? (event.column ? [event.column] : []);\n for (const col of touched) {\n userTouchedRef.current.add(col.getColId());\n }\n };\n\n // The grid API is frequently not ready on the first effect tick (AG Grid\n // mounts asynchronously). `wired` ensures the one-time seed + listener\n // attach happens the first time we actually have an API, no matter whether\n // that is here on mount or later from a ResizeObserver tick.\n let wired = false;\n\n /**\n * One-time wiring once the API is live: seed the user-touched set from the\n * persisted/current column state, then subscribe to `columnVisible` so later\n * manual toggles are recorded. Seeding treats an already-hidden tiered\n * column that *would* be visible at the current width as a deliberate user\n * hide (restored from localStorage by data-table.tsx) so the baseline does\n * not clobber it. We only positively detect *hidden* overrides — auto-show\n * would otherwise fight a deliberate hide.\n */\n const ensureWired = (api: NonNullable<ReturnType<typeof getApi>>) => {\n if (wired) return;\n wired = true;\n const cols = api.getColumns();\n const width = container.getBoundingClientRect().width;\n const visibleTiers = visibleTiersForWidth(width, breakpoints);\n if (cols) {\n for (const col of cols) {\n const tier = getResponsiveTier(col.getColDef());\n if (tier && !col.isVisible() && visibleTiers.has(tier)) {\n userTouchedRef.current.add(col.getColId());\n }\n }\n }\n api.addEventListener('columnVisible', onColumnVisible);\n };\n\n /** Apply the responsive baseline for the current container width. */\n const reconcile = () => {\n const api = getApi();\n if (!api) return;\n ensureWired(api);\n const columns = api.getColumns();\n if (!columns) return;\n\n const width = container.getBoundingClientRect().width;\n const visibleTiers = visibleTiersForWidth(width, breakpoints);\n\n // Compute the show / hide buckets, skipping user-touched and untiered\n // columns. Batch the calls so AG Grid fires one event per direction.\n const toShow: Column[] = [];\n const toHide: Column[] = [];\n\n for (const col of columns) {\n const colId = col.getColId();\n // The user owns this column's visibility — never auto-manage it.\n if (userTouchedRef.current.has(colId)) continue;\n\n const tier = getResponsiveTier(col.getColDef());\n // No tier ⇒ anchor column (Identity / Actions): always left visible.\n if (!tier) continue;\n\n const shouldShow = visibleTiers.has(tier);\n if (shouldShow && !col.isVisible()) toShow.push(col);\n else if (!shouldShow && col.isVisible()) toHide.push(col);\n }\n\n if (toShow.length === 0 && toHide.length === 0) return;\n\n applyingRef.current = true;\n try {\n // AG Grid tags API-driven changes with `source: 'api'`, so the\n // resulting `columnVisible` events are ignored by `onColumnVisible`\n // (they are the baseline, not user intent). `applyingRef` is the belt\n // to that braces in case a future version reports a different source.\n if (toShow.length > 0) api.setColumnsVisible(toShow, true);\n if (toHide.length > 0) api.setColumnsVisible(toHide, false);\n } finally {\n applyingRef.current = false;\n }\n };\n\n // Observe the GRID CONTAINER (not the viewport) so embedded tables respond\n // to their own box. The first observer tick fires synchronously on observe,\n // and `reconcile` lazily wires + seeds the moment the grid API is live.\n const observer = new ResizeObserver(() => reconcile());\n observer.observe(container);\n reconcile();\n\n return () => {\n observer.disconnect();\n // The API may have torn down already on unmount; guard the removal.\n getApi()?.removeEventListener('columnVisible', onColumnVisible);\n };\n }, [containerRef, enabled, breakpoints, handleRef]);\n\n // Stable across renders — `userTouchedRef` is a ref, so adding to it never\n // re-runs the responsive effect. Recording a colId here makes the next\n // `reconcile` pass skip it (it `continue`s on `userTouchedRef.has(colId)`),\n // which is exactly how a manual ColumnToggle override is honoured. The view\n // switcher calls this for every column it shows/hides so the responsive\n // baseline doesn't immediately fight the applied view.\n const lockColumns = useCallback((colIds: readonly string[]) => {\n for (const id of colIds) userTouchedRef.current.add(id);\n }, []);\n\n return { lockColumns };\n}\n","/**\n * patient-table — Domain-Specific component\n * ----------------------------------------------------------------------------\n * `PatientTable` is a thin composition over the kit's `data-table` engine\n * (AG Grid Community) specialised for the AlfaDocs patient list. It bakes in:\n *\n * • `buildPatientColumns(t, …)` (one field per column — split identity,\n * grouped hidden columns, dynamic custom fields) merged with a\n * consumer-supplied `columns` override.\n * • the kit data-table built-in renderers (Image / Link / Date / Currency /\n * Status / TagList / User) plus the three patient renderers kept for the\n * domain columns (NextAppointment / BalanceBadge / CarePlanStatus).\n * • a default toolbar — `QuickSearch` + `FilterChips` + `ColumnToggle` +\n * `ExportMenu` + a `BulkAction` per `props.bulkActions` — overridable\n * wholesale via `props.toolbar`.\n * • `useResponsiveColumns` (container-width column-priority tiers) when\n * `props.responsive` (default `true`).\n * • multiple row selection, pagination, archived-row tint, loading / empty\n * overlays.\n * • `useAgentRegistration(dataTableAgent, handle, gridId)` — the SAME agent\n * adapter `data-table` registers, keyed by `gridId`.\n *\n * The `forwardRef` exposes the inner `DataTableHandle<PatientRow>` so consumers\n * (e.g. the `patients` pattern's WarningStack \"View\" actions) can drive the\n * grid imperatively — `setFilter`, `setSort`, `getSelection`, etc.\n *\n * CONSTRAINTS (CI-blocking — src/docs/23-constraints.mdx, 26-agent-readiness.mdx):\n * • No hardcoded colour / spacing / radius / shadow / font — token-bound `ds:`\n * utilities only. Row tint goes through the token-bound\n * `data-table-row-archived` class on the AG Grid theme bridge, never an\n * inline `cellStyle`.\n * • CSS logical properties only (`ms/me/ps/pe`, `text-start/text-end`).\n * • All user-visible strings via `useTranslation('ui')` → `ui.patientTable.*`.\n * • TypeScript strict — no `any`. `forwardRef` exposes the curated handle.\n * • PHI rule (PRS 26 §6): only `row.id` reaches DOM `data-*`. This component\n * writes no PHI attribute; AG Grid surfaces ids via its native `row-id`.\n */\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n type RefObject,\n} from 'react';\nimport { useTranslation } from 'react-i18next';\nimport type { GetRowIdParams, RowClassParams } from 'ag-grid-community';\nimport {\n actionsColumnWidth,\n DataTable,\n dataTableAgent,\n type DataTableHandle,\n} from '../data-table';\nimport { Select } from '../select';\nimport { useAgentRegistration } from '../../agent';\nimport {\n buildPatientColumns,\n getResponsiveTier,\n PATIENT_ROW_ACTION_COUNT,\n type BuildPatientColumnsOptions,\n} from './columns';\nimport {\n useResponsiveColumns,\n type UseResponsiveColumnsResult,\n} from './use-responsive-columns';\nimport type {\n PatientBulkAction,\n PatientColumnDef,\n PatientCustomFieldDef,\n PatientRow,\n PatientRowAction,\n PatientTableHandle,\n PatientView,\n} from './types';\n\n/* -------------------------------------------------------------------------- */\n/* Props */\n/* -------------------------------------------------------------------------- */\n\nexport interface PatientTableProps {\n /**\n * Row data. `undefined` ⇒ the grid renders its loading overlay (the\n * data-table contract: `rowData === undefined` is the loading sentinel). Pass\n * `[]` for an explicit empty state.\n */\n rowData?: PatientRow[];\n /**\n * Override / extend the baked patient columns. When provided, these column\n * definitions REPLACE the kit defaults entirely (the consumer is expected to\n * compose `buildPatientColumns(t, …)` — re-exported from the barrel — with\n * their own additions). When omitted, the kit's `buildPatientColumns` output\n * is used.\n */\n columns?: PatientColumnDef[];\n /**\n * REQUIRED. Persistence key (column state → `localStorage`) AND the agent\n * instance address (`data-component-id`). Two tables on one page need\n * distinct `gridId`s.\n */\n gridId: string;\n /** Row-height preset. Defaults to the engine default (`'default'`). */\n density?: 'compact' | 'default' | 'expanded';\n /**\n * Collapse the per-row actions column into a single \"…\" overflow trigger\n * independently of row `density`. When `true` the actions cells render the\n * collapsed menu and the column shrinks to its 88px compact footprint; when\n * `false` they render the inline button group at the full expanded width.\n * When `undefined` (the default) compactness FOLLOWS `density` — `compact`\n * density collapses the actions, every other density keeps them inline — so\n * existing consumers are unaffected. Flipping this at runtime re-reads the\n * actions cells via a stable ref-backed getter (no `columnDefs` rebuild, so\n * column state is preserved) and resizes the actions column to match.\n */\n actionsCompact?: boolean;\n /**\n * Grid height — forwarded to the underlying `DataTable.heightClass` (a Tailwind\n * height class). Pass `\"ds:h-full\"` to fill a sized parent, or an explicit\n * value like `\"ds:h-[75vh]\"`. Omit to use the engine default (`ds:h-[500px]`).\n */\n heightClass?: string;\n /**\n * Enable container-width column-priority responsiveness (lower-priority\n * columns hide as the grid narrows). Default `true`. Set `false` to leave\n * column visibility entirely to persistence / `ColumnToggle`.\n */\n responsive?: boolean;\n /**\n * Saved column/filter/sort presets. When non-empty AND no custom `toolbar`\n * is supplied, the default toolbar renders a `Select`-based view switcher in\n * the FilterBar; choosing a view applies its column visibility (via the\n * responsive-hook lock so the choice is honoured like a manual ColumnToggle),\n * `filterModel` (`setFilter`) and `sortModel` (`setSort`). Omitted entirely\n * when empty / undefined or when `toolbar` is supplied (a custom toolbar owns\n * its own view UI).\n */\n views?: PatientView[];\n /**\n * Invoked when a per-row action button is activated, with the resolved action\n * descriptor and the patient row. Only `row.id` ever leaves this boundary into\n * persistence / agent surfaces — never PHI.\n */\n onRowAction?: (action: PatientRowAction, patient: PatientRow) => void;\n /**\n * Toolbar bulk actions (operate on the current selection). Each renders a\n * `DataTable.Toolbar.BulkAction` that surfaces only while rows are selected;\n * its `onSelect` receives the selected row ids (never PHI).\n */\n bulkActions?: PatientBulkAction[];\n /**\n * Force the loading overlay independently of `rowData`. When `true` the grid\n * shows the loading skeleton even if `rowData` is an array (e.g. a background\n * refresh). `rowData === undefined` also triggers loading.\n */\n loading?: boolean;\n /**\n * Replace the default toolbar wholesale. Pass a `<DataTable.Toolbar>…</…>`\n * tree (it has access to the same toolbar context). When omitted the default\n * QuickSearch + FilterChips + ColumnToggle + ExportMenu + BulkActions toolbar\n * is rendered.\n */\n toolbar?: ReactNode;\n /**\n * Omit the `QuickSearch` field from the DEFAULT toolbar. The rest of the\n * default toolbar (view switcher when `views` is supplied, FilterChips,\n * ColumnToggle, ExportMenu, BulkActions) is unaffected. Ignored when a custom\n * `toolbar` is supplied (that toolbar owns its own composition). Default\n * `false` (search shown). Use for surfaces that provide their own search\n * affordance elsewhere on the page.\n */\n hideQuickSearch?: boolean;\n /**\n * Enable the grid's client-side pagination. When `false` (the default), every\n * row loads into a single virtualised, scrollable view and filter / sort act\n * over the WHOLE set (no pager) — the right model for a \"load and filter all\"\n * list, which is what a patient list is. Pass `true` for a surface that really\n * wants a pager.\n */\n pagination?: boolean;\n /**\n * Page-size dropdown options shown in the pagination panel — forwarded to the\n * underlying `DataTable.paginationPageSizeSelector`. Pass `false` to hide the\n * page-size chooser entirely (pagination navigation is unaffected). Default\n * `[10, 25, 50, 100]` (the engine default).\n */\n paginationPageSizeSelector?: number[] | false;\n /**\n * Mask sensitive contact details (email) in the Contact / Email columns.\n * Forwarded to `buildPatientColumns`' `hideDetails`. Ignored when `columns`\n * is supplied (the consumer owns their columns then). Default `false`.\n */\n hideDetails?: boolean;\n /**\n * Derive the patient-detail href for the Identity cell, e.g.\n * `(row) => \\`/patients/${row.id}\\``. Forwarded to `buildPatientColumns`.\n * Ignored when `columns` is supplied. When omitted the Identity cell routes\n * its click through `onRowAction` with the `show` action.\n */\n identityHref?: BuildPatientColumnsOptions['identityHref'];\n /**\n * Per-practice custom-field descriptors. Each appends one dynamic `custom:*`\n * column (hidden by default, toggleable via ColumnToggle) after the standard\n * columns. Forwarded to `buildPatientColumns`. Ignored when `columns` is\n * supplied (the consumer owns their columns then).\n */\n customFields?: PatientCustomFieldDef[];\n /** Override the default region aria-label (`ui.patientTable.regionLabel`). */\n 'aria-label'?: string;\n /** Extra class names merged onto the inner grid wrapper. */\n className?: string;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Row class — archived tint */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Token-bound archived-row tint. The class is defined on the AG Grid theme\n * bridge (`src/tokens/ag-grid-theme.css` → `.data-table-row-archived`) so the\n * recessive muted fill stays under token control — no colour ever enters a\n * `cellStyle` / `cellClass` literal here.\n */\nfunction getPatientRowClass(\n params: RowClassParams<PatientRow>,\n): string | undefined {\n return params.data?.isArchived ? 'data-table-row-archived' : undefined;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Row id — key selection / bulk actions on the real patient id */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Use the stable `PatientRow.id` as AG Grid's node id. Without this AG Grid\n * keys nodes by row index, so `getSelectedNodes().map(n => n.id)`,\n * `setSelection(ids)`, the bulk-action bridge and the agent adapter would all\n * receive index strings (`\"0\"`, `\"1\"`) instead of patient ids. Forwarded to the\n * engine via `gridOptions.getRowId` (the path `data-table.tsx` spreads through\n * untouched). `id` is the ONLY field permitted on a DOM hook (PHI rule).\n */\nfunction getPatientRowId(params: GetRowIdParams<PatientRow>): string {\n return params.data.id;\n}\n\n/* -------------------------------------------------------------------------- */\n/* View switcher — default-toolbar control for the `views` prop */\n/* -------------------------------------------------------------------------- */\n\ninterface ViewSwitcherProps {\n /** Saved views to choose from (already known non-empty by the caller). */\n views: PatientView[];\n /** The live patient-table handle ref (filter / sort / raw API access). */\n handleRef: RefObject<PatientTableHandle | null>;\n /**\n * Mark a view's visibility writes as user-touched so `useResponsiveColumns`\n * stops auto-managing them — see the coordination note at the call site.\n */\n lockColumns: UseResponsiveColumnsResult['lockColumns'];\n /** Accessible label for the control (`ui.patientTable.views.label`). */\n label: string;\n}\n\n/**\n * A minimal, accessible saved-view switcher rendered in the DEFAULT toolbar's\n * FilterBar. Uses the kit `Select` (convenience `options` form) — a small,\n * labelled single-choice control that fits the toolbar's compact density and\n * carries Radix's listbox a11y for free.\n *\n * On selection it applies the chosen {@link PatientView} through the patient\n * handle + raw AG Grid API:\n * • column visibility — show the view's listed toggleable columns, hide the\n * rest of the toggleable (tiered) ones. Untiered columns (the pinned\n * Identity / Actions anchors) are NEVER hidden.\n * • `filterModel` via `handle.setFilter`.\n * • `sortModel` via `handle.setSort`.\n *\n * RESPONSIVE COORDINATION (critical): a view-applied visibility change must not\n * be immediately re-hidden/re-shown by `useResponsiveColumns`. We route every\n * colId we touch through `lockColumns(colIds)`, which adds them to the same\n * `userTouchedRef` set the hook grows from non-`api` `columnVisible` events —\n * so from then on the responsive baseline treats them as a user override and\n * leaves them alone, exactly as a manual ColumnToggle would.\n */\nfunction ViewSwitcher({\n views,\n handleRef,\n lockColumns,\n label,\n}: ViewSwitcherProps) {\n const [activeViewId, setActiveViewId] = useState<string>('');\n\n const options = useMemo(\n () => views.map((view) => ({ value: view.id, label: view.label })),\n [views],\n );\n\n const applyView = useCallback(\n (viewId: string) => {\n const view = views.find((v) => v.id === viewId);\n const handle = handleRef.current;\n if (!view || !handle) return;\n\n const api = handle.getRawApi();\n if (api) {\n const wanted = new Set<string>(view.columns);\n const toShow: string[] = [];\n const toHide: string[] = [];\n for (const col of api.getColumns() ?? []) {\n // Only the tiered (auto-hideable) field columns participate; the\n // pinned Identity / Actions anchors carry no tier and are never\n // hidden by a view.\n if (getResponsiveTier(col.getColDef()) === undefined) continue;\n const colId = col.getColId();\n if (wanted.has(colId)) toShow.push(colId);\n else toHide.push(colId);\n }\n // Lock BEFORE the visibility write so a synchronous responsive\n // reconcile (e.g. a ResizeObserver tick triggered by the layout shift)\n // already sees these colIds as user-touched.\n lockColumns([...toShow, ...toHide]);\n if (toShow.length > 0) api.setColumnsVisible(toShow, true);\n if (toHide.length > 0) api.setColumnsVisible(toHide, false);\n }\n\n handle.setFilter(view.filterModel);\n handle.setSort(view.sortModel);\n api?.onFilterChanged();\n },\n [views, handleRef, lockColumns],\n );\n\n const handleValueChange = useCallback(\n (next: string) => {\n setActiveViewId(next);\n if (next) applyView(next);\n },\n [applyView],\n );\n\n return (\n // Width-constraining wrapper (same pattern as the toolbar's QuickSearch) so\n // the Select's intrinsic `w-full` trigger sizes to a compact, legible width\n // instead of absorbing the whole FilterBar row.\n <div className=\"ds:w-[12rem] ds:max-w-full ds:min-w-0 ds:shrink-0\">\n <Select\n options={options}\n value={activeViewId}\n onValueChange={handleValueChange}\n size=\"sm\"\n aria-label={label}\n />\n </div>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* PatientTable */\n/* -------------------------------------------------------------------------- */\n\nexport const PatientTable = forwardRef<PatientTableHandle, PatientTableProps>(\n function PatientTable(\n {\n rowData,\n columns,\n gridId,\n density,\n actionsCompact,\n heightClass,\n responsive = true,\n views,\n onRowAction,\n bulkActions,\n loading = false,\n toolbar,\n hideQuickSearch = false,\n pagination = false,\n paginationPageSizeSelector,\n hideDetails = false,\n identityHref,\n customFields,\n 'aria-label': ariaLabel,\n className,\n },\n ref,\n ) {\n const { t } = useTranslation('ui');\n\n // Internal handle ref forwarded BOTH to the consumer's ref (below) and to\n // `useResponsiveColumns`, which reaches the live AG Grid API via\n // `handle.getRawApi()`.\n const handleRef = useRef<PatientTableHandle | null>(null);\n\n // Container the responsive hook observes (its inline-size drives the\n // column-priority tiers). Observing the grid's own box — not the viewport —\n // is what makes the table responsive when embedded in a narrow panel.\n const containerRef = useRef<HTMLDivElement | null>(null);\n\n // Mirror the handle into state once the inner DataTable mounts so the\n // agent-registration effect (which keys off `handle` reactively) fires with\n // a live handle. A bare ref mutation would not re-run the effect.\n const [registrableHandle, setRegistrableHandle] =\n useState<PatientTableHandle | null>(null);\n\n // Expose the inner DataTable handle to the consumer ref. Re-runs when the\n // mirrored handle becomes available so a consumer reading the ref in an\n // effect sees the live handle, not the initial `null`.\n useImperativeHandle(\n ref,\n () => (registrableHandle ?? handleRef.current) as PatientTableHandle,\n [registrableHandle],\n );\n\n // Responsive column-priority tiers, driven by the grid container's width.\n // Inert when `responsive` is false (the hook unobserves and makes no calls).\n // The container ref is widened to the hook's `HTMLElement` param at the call\n // site (RefObject is invariant on its element type) — same cast pattern as\n // `useDirection` in tooltip.tsx. `lockColumns` lets the ViewSwitcher mark a\n // view's visibility writes as user-touched (see ViewSwitcher's coordination\n // note) — called before the hook value is consumed in the default toolbar.\n const { lockColumns } = useResponsiveColumns(handleRef, {\n containerRef: containerRef as RefObject<HTMLElement | null>,\n enabled: responsive,\n });\n\n // Actions-column compactness. Explicit `actionsCompact` wins; otherwise it\n // follows `density` (`compact` collapses, every other density stays inline)\n // — the legacy coupling, preserved as the default so existing consumers are\n // unaffected.\n const compact = actionsCompact ?? density === 'compact';\n\n // Ref-backed live getter for the compactness flag. The actions cell renderer\n // calls this every render, so flipping `compact` need only update the ref +\n // refresh the cells — it never rebuilds `columnDefs` (which would discard the\n // user's column state). The getter identity is stable, so it is NOT a\n // `columnDefs` dependency.\n const actionsCompactRef = useRef(compact);\n const getActionsCompact = useCallback(() => actionsCompactRef.current, []);\n\n // On a `compact` change — OR once the grid handle first becomes live — sync\n // the ref the renderer reads, then drive the LIVE grid: set the actions\n // column to its collapsed (88px) or expanded (derived from the row-action\n // count) width and force the actions cells to re-read the getter. Depending\n // on `registrableHandle` makes the initial compact state apply as soon as the\n // DataTable mirrors its handle in (the api is null on first paint), so a\n // page mounting already-compact lands at 88px without a column-defs rebuild.\n useEffect(() => {\n actionsCompactRef.current = compact;\n const api = handleRef.current?.getRawApi();\n if (!api) return;\n api.setColumnWidths([\n {\n key: 'actions',\n newWidth: compact ? 88 : actionsColumnWidth(PATIENT_ROW_ACTION_COUNT),\n },\n ]);\n api.refreshCells({ force: true });\n }, [compact, registrableHandle]);\n\n // Columns: consumer override wins wholesale; otherwise the baked patient\n // columns. Memoised on the inputs the builder reads — `density` is NO LONGER\n // among them: the actions column reads its compactness through the stable\n // `getActionsCompact` getter, so a density (or `actionsCompact`) flip resizes\n // + refreshes the live grid without rebuilding the defs (preserving column\n // state). `density` still drives row height via the DataTable prop below.\n const columnDefs = useMemo<PatientColumnDef[]>(\n () =>\n columns ??\n buildPatientColumns(t, {\n hideDetails,\n onRowAction,\n identityHref,\n customFields,\n getActionsCompact,\n }),\n [\n columns,\n t,\n hideDetails,\n onRowAction,\n identityHref,\n customFields,\n getActionsCompact,\n ],\n );\n\n // Default toolbar — an optional ViewSwitcher + QuickSearch + FilterChips\n // (FilterBar slot) and ColumnToggle + ExportMenu + one BulkAction per\n // `bulkActions` (Actions slot). Overridable wholesale via `props.toolbar`;\n // when a custom `toolbar` is supplied the ViewSwitcher is NOT injected — a\n // custom toolbar owns its own view UI. The switcher is also omitted when\n // `views` is empty / undefined. All toolbar parts translate their own labels\n // through the `dataTable.*` namespace; the BulkAction labels come from the\n // consumer-supplied descriptors.\n const hasViews = (views?.length ?? 0) > 0;\n const defaultToolbar = (\n <DataTable.Toolbar>\n <DataTable.Toolbar.FilterBar>\n {hasViews && views ? (\n <ViewSwitcher\n views={views}\n handleRef={handleRef}\n lockColumns={lockColumns}\n label={t('patientTable.views.label')}\n />\n ) : null}\n {hideQuickSearch ? null : <DataTable.Toolbar.QuickSearch />}\n <DataTable.Toolbar.FilterChips />\n </DataTable.Toolbar.FilterBar>\n <DataTable.Toolbar.Actions>\n {bulkActions?.map((action) => (\n <DataTable.Toolbar.BulkAction\n key={action.id}\n label={action.label}\n icon={action.icon}\n variant={action.variant}\n onClick={(api) => {\n // Resolve the selection via the live grid API → row ids only\n // (never PHI). Falls back to an empty list if the API is unset.\n const ids =\n api\n ?.getSelectedNodes()\n .map((node) => node.id)\n .filter((id): id is string => id != null) ?? [];\n action.onSelect(ids);\n }}\n />\n ))}\n <DataTable.Toolbar.ColumnToggle />\n <DataTable.Toolbar.ExportMenu />\n </DataTable.Toolbar.Actions>\n </DataTable.Toolbar>\n );\n\n const toolbarContent = toolbar ?? defaultToolbar;\n\n // `rowData === undefined` is the data-table loading sentinel; `loading`\n // forces it independently (e.g. a background refresh over existing rows).\n const effectiveRowData = loading ? undefined : rowData;\n\n // Stable `gridOptions` forwarding `getRowId` so AG Grid keys nodes by the\n // real `PatientRow.id` (see getPatientRowId) — selection, bulk actions and\n // the agent adapter all then return patient ids, not row-index strings.\n const gridOptions = useMemo(() => ({ getRowId: getPatientRowId }), []);\n\n // Register the data-table agent adapter keyed by `gridId` — the same\n // adapter `data-table` registers internally. Idempotent overwrite with the\n // identical handle under the same `(adapterId, gridId)` key; both share the\n // unmount lifecycle so the dedup is harmless and the registration stays\n // addressable as `patient-table`'s `gridId` for the duration.\n useAgentRegistration(\n dataTableAgent,\n registrableHandle as DataTableHandle | null,\n gridId,\n );\n\n return (\n <div\n ref={containerRef}\n role=\"region\"\n aria-label={ariaLabel ?? t('patientTable.regionLabel')}\n data-component=\"patient-table\"\n data-component-id={gridId}\n className=\"ds:flex ds:w-full ds:min-h-0 ds:flex-1 ds:flex-col\"\n >\n <DataTable<PatientRow>\n ref={(instance) => {\n handleRef.current = instance;\n // Mirror into state once (or when the instance identity changes) so\n // the registration effect picks up the live handle.\n setRegistrableHandle((prev) =>\n prev === instance ? prev : instance,\n );\n }}\n gridId={gridId}\n columnDefs={columnDefs}\n rowData={effectiveRowData}\n rowSelection=\"multiple\"\n pagination={pagination}\n {...(heightClass !== undefined ? { heightClass } : {})}\n {...(paginationPageSizeSelector !== undefined\n ? { paginationPageSizeSelector }\n : {})}\n density={density}\n getRowClass={getPatientRowClass}\n gridOptions={gridOptions}\n className={className}\n >\n {toolbarContent}\n </DataTable>\n </div>\n );\n },\n);\n\nPatientTable.displayName = 'PatientTable';\n","import type { CustomCellRendererProps } from 'ag-grid-react';\nimport { useTranslation } from 'react-i18next';\nimport { Badge } from '../../badge/badge';\nimport type { PatientRow } from '../types';\n\n/**\n * patient-table — BalanceBadgeCell\n * ----------------------------------------------------------------------------\n * Threshold-coloured unpaid-balance badge for the patient grid.\n *\n * Reads {@link PatientRow.unpaidAmount} (integer cents), formats it to the\n * display currency via `Intl.NumberFormat` (cents → major unit at the render\n * boundary — see types.ts), and wraps the result in a kit {@link Badge} whose\n * semantic variant buckets by outstanding-balance threshold:\n *\n * | cents bucket | meaning | Badge variant |\n * | ----------------------- | ------------------ | ------------- |\n * | `0` | settled | `success` |\n * | `> 0` and `<= 50000` | small balance | `warning` |\n * | `> 50000` | overdue / large | `error` |\n *\n * NOTE on the variant name: the task brief calls the red bucket\n * `destructive`; the kit `Badge` exposes that semantic tier as the `error`\n * variant (backed by the `--destructive` / `--error-foreground` tokens). There\n * is no `destructive` Badge variant, so `error` is the correct destructive-tier\n * mapping — recorded here so the brief↔code discrepancy is intentional.\n *\n * The bucket meaning is colour-coded, so it is also surfaced as an sr-only\n * status word inside the badge (alongside the `withDot` indicator) — the\n * threshold tier is never conveyed by colour alone.\n *\n * Returns `null` when `unpaidAmount` is missing / non-finite (empty cell), per\n * the data-table renderer convention.\n */\n\n/** Display + threshold-classification result for a single balance value. */\ntype BalanceBucket = {\n variant: 'success' | 'warning' | 'error';\n /** i18n key suffix under `ui.patientTable.balance.status.*`. */\n statusKey: 'settled' | 'pending' | 'overdue';\n};\n\n/** Cents at or below which a non-zero balance is treated as a small balance. */\nconst SMALL_BALANCE_CENTS_MAX = 50000;\n\nfunction classifyBalance(cents: number): BalanceBucket {\n if (cents === 0) return { variant: 'success', statusKey: 'settled' };\n if (cents <= SMALL_BALANCE_CENTS_MAX)\n return { variant: 'warning', statusKey: 'pending' };\n return { variant: 'error', statusKey: 'overdue' };\n}\n\nfunction readUnpaidCents(value: unknown): number | null {\n if (value == null || value === '') return null;\n const n = typeof value === 'number' ? value : Number(value);\n return Number.isFinite(n) ? n : null;\n}\n\nexport interface BalanceBadgeCellParams {\n /** ISO 4217 currency code used to format the balance. Default `'EUR'`. */\n currency?: string;\n /**\n * `Intl.NumberFormat` escape hatch, merged on top of the currency defaults.\n * E.g. `{ maximumFractionDigits: 0 }` for whole-currency display.\n */\n options?: Intl.NumberFormatOptions;\n}\n\nexport function BalanceBadgeCell(\n props: CustomCellRendererProps<PatientRow> & BalanceBadgeCellParams,\n) {\n const { value, currency = 'EUR', options } = props;\n const { t, i18n } = useTranslation('ui');\n\n const cents = readUnpaidCents(value);\n if (cents === null) return null;\n\n const locale = i18n.language || 'en';\n const formatted = new Intl.NumberFormat(locale, {\n style: 'currency',\n currency,\n ...options,\n }).format(cents / 100);\n\n const { variant, statusKey } = classifyBalance(cents);\n const status = t(`patientTable.balance.status.${statusKey}`);\n\n return (\n <Badge variant={variant} withDot size=\"sm\" className=\"ds:tabular-nums\">\n {/* Visible: the formatted amount. The threshold meaning is colour-coded,\n so the status word is surfaced to AT as sr-only text — the bucket is\n never conveyed by colour alone. */}\n <span>{formatted}</span>\n <span className=\"ds:sr-only\">{status}</span>\n </Badge>\n );\n}\n"],"names":["TransactionChipCell","props","value","positiveState","showLabel","cents","state","TransactionChip","CARE_PLAN_VARIANT_MAP","isCarePlanStatus","CarePlanStatusCell","t","useTranslation","data","size","raw","variant","jsx","Badge","nextAppointmentVariants","cva","DAY_MS","DEFAULT_SOON_WITHIN_DAYS","toTime","resolveTone","time","soonWithinDays","deltaMs","NextAppointmentCell","tone","CalendarClock","Timestamp","getResponsiveTier","colDef","PATIENT_ROW_ACTION_COUNT","buildRowActions","onRowAction","dispatch","action","patient","make","overrides","createElement","Eye","Pencil","Mail","MessageSquare","p","Users","Trash2","NO","CHECK","DASH","ageFromDob","dob","birth","ms","now","age","m","buildCustomFieldColumns","fields","field","base","params","_b","_a","SelectFilter","SelectFloatingFilter","TagListCellRenderer","opt","TextFilter","TextFloatingFilter","buildPatientColumns","opts","hideDetails","getActionsCompact","identityHref","customFields","actions","genderLabels","consentLabels","consentVariantMap","header","id","ImageCellRenderer","LinkCellRenderer","row","_c","DateRangeFilter","DateRangeFloatingFilter","NumberFilter","NumberFloatingFilter","s","DateCellRenderer","UserCellRenderer","a","b","TypeaheadFilter","TypeaheadFloatingFilter","StatusCellRenderer","year","num","ActionsCellRenderer","actionsColumnWidth","DEFAULT_BREAKPOINTS","visibleTiersForWidth","width","breakpoints","visible","bp","useResponsiveColumns","handleRef","containerRef","enabled","userTouchedRef","useRef","applyingRef","useEffect","container","getApi","onColumnVisible","event","touched","col","wired","ensureWired","api","cols","visibleTiers","tier","reconcile","columns","toShow","toHide","colId","shouldShow","observer","useCallback","colIds","getPatientRowClass","getPatientRowId","ViewSwitcher","views","lockColumns","label","activeViewId","setActiveViewId","useState","options","useMemo","view","applyView","viewId","v","handle","wanted","handleValueChange","next","Select","PatientTable","forwardRef","rowData","gridId","density","actionsCompact","heightClass","responsive","bulkActions","loading","toolbar","hideQuickSearch","pagination","paginationPageSizeSelector","ariaLabel","className","ref","registrableHandle","setRegistrableHandle","useImperativeHandle","compact","actionsCompactRef","columnDefs","hasViews","defaultToolbar","jsxs","DataTable","ids","node","toolbarContent","effectiveRowData","gridOptions","useAgentRegistration","dataTableAgent","instance","prev","SMALL_BALANCE_CENTS_MAX","classifyBalance","readUnpaidCents","BalanceBadgeCell","currency","i18n","locale","formatted","statusKey","status"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"data-table-tabs-C1kRUzat.js","sources":["../../src/components/data-table-tabs/data-table-tabs.agent.ts","../../src/components/data-table-tabs/use-data-table-filter-tabs.ts","../../src/components/data-table-tabs/data-table-tabs.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — DataTableTabs. */\n/* */\n/* Operations work against the curated `DataTableTabsHandle` (saved-filter */\n/* tab strip). Tab ids are opaque machine ids — labels are display-only. */\n/* See `src/docs/26-agent-readiness.mdx`. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { DataTableTabsHandle } from './data-table-tabs';\n\nexport const dataTableTabsAgent: AgentAdapter<DataTableTabsHandle> = {\n id: 'data-table-tabs',\n capabilities: ['select_single', 'view_change'],\n state: {\n activeTabId: {\n type: 'string | null',\n descriptionKey: 'ui.agent.dataTableTabs.state.activeTabId',\n description: 'Id of the currently-active saved-filter tab.',\n read: (handle) => handle.getActiveTabId(),\n },\n tabs: {\n type: 'Array<{ id, label }>',\n descriptionKey: 'ui.agent.dataTableTabs.state.tabs',\n description: 'The saved-filter tabs, in strip order (ids + labels).',\n read: (handle) =>\n handle.getTabs().map(({ id, label }) => ({ id, label })),\n },\n },\n actions: {\n select_tab: {\n // Applies a saved filter model — a view change, like apply_filter.\n safety: 'read',\n argsType: '{ id: string }',\n argsSchema: {\n type: 'object',\n properties: {\n id: { type: 'string', description: 'Tab id to activate.' },\n },\n required: ['id'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.dataTableTabs.actions.selectTab',\n description:\n 'Activate a saved-filter tab: snapshots the outgoing tab, applies the incoming filter model to the table.',\n invoke: (handle, args: { id: string }) => {\n handle.selectTab(args.id);\n },\n },\n get_tabs: {\n safety: 'read',\n returns: 'Array<{ id: string, label: string }>',\n returnsSchema: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n label: { type: 'string' },\n },\n required: ['id', 'label'],\n },\n },\n idempotent: true,\n descriptionKey: 'ui.agent.dataTableTabs.actions.getTabs',\n description: 'Return the saved-filter tabs (ids + labels) in order.',\n invoke: (handle) =>\n handle.getTabs().map(({ id, label }) => ({ id, label })),\n },\n add_tab: {\n safety: 'write',\n argsType: '{ label?: string }',\n argsSchema: {\n type: 'object',\n properties: {\n label: {\n type: 'string',\n description:\n 'Display name for the new tab. Defaults to a numbered name.',\n },\n },\n required: [],\n },\n idempotent: false,\n descriptionKey: 'ui.agent.dataTableTabs.actions.addTab',\n description:\n \"Add a new tab capturing the table's current filter model, and activate it. Returns the new tab id.\",\n returns: 'string',\n returnsSchema: { type: 'string' },\n invoke: (handle, args: { label?: string }) => handle.addTab(args.label),\n },\n rename_tab: {\n safety: 'write',\n argsType: '{ id: string, label: string }',\n argsSchema: {\n type: 'object',\n properties: {\n id: { type: 'string', description: 'Tab id to rename.' },\n label: { type: 'string', description: 'New display name.' },\n },\n required: ['id', 'label'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.dataTableTabs.actions.renameTab',\n description:\n 'Rename a saved-filter tab. No-op on locked tabs and empty labels.',\n invoke: (handle, args: { id: string; label: string }) => {\n handle.renameTab(args.id, args.label);\n },\n },\n remove_tab: {\n // Removes a saved filter permanently — destructive, do not downgrade.\n safety: 'destructive',\n argsType: '{ id: string }',\n argsSchema: {\n type: 'object',\n properties: {\n id: { type: 'string', description: 'Tab id to remove.' },\n },\n required: ['id'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.dataTableTabs.actions.removeTab',\n description:\n 'Remove a saved-filter tab (its saved filter is lost). No-op on locked tabs and the last remaining tab.',\n invoke: (handle, args: { id: string }) => {\n handle.removeTab(args.id);\n },\n },\n },\n domHooks: {\n root: { attr: 'data-component', value: 'data-table-tabs' },\n instanceId: { attr: 'data-component-id', sourceProp: 'id' },\n item: { attr: 'data-tab-id' },\n },\n};\n","/**\n * use-data-table-filter-tabs — headless sync engine for DataTableTabs.\n * ----------------------------------------------------------------------------\n * Owns the tabs array + active tab id and keeps the grid's AG Grid filter\n * model in lock-step with the active tab. The grid is a PROJECTION of the\n * active tab; the hook is the single owner of tab state.\n *\n * Four flows, all through `getTable()` resolved at call time (never held):\n *\n * 1. SUBSCRIBE-LATE — the grid api resolves after mount (ToolbarProvider\n * convention), so a mount effect polls a 50 ms interval until\n * `getTable()?.getRawApi()` resolves, then attaches a `filterChanged`\n * listener (FilterChips precedent — a one-shot `if (!api) return` would\n * bail permanently and never capture).\n * 2. CAPTURE — tabs are LIVE views: every user/host filter edit is written\n * into the active tab. The listener skips ONLY while `applyingRef` is set\n * (our own restore). It deliberately does NOT filter on\n * `event.source === 'api'`: external programmatic `setFilter` calls (page\n * warning actions, the data-table agent) are user intent and must land in\n * the active tab.\n * 3. RESTORE — activating a tab snapshots the outgoing tab defensively, then\n * applies the incoming model under the `applyingRef` guard and calls\n * `api.onFilterChanged()` so floating filters / chips / agents refresh\n * (ViewSwitcher precedent — `setFilterModel` alone doesn't re-run\n * listeners).\n * 4. INITIAL APPLY — when `applyOnActivate`, the active tab's model applies\n * once as soon as the api resolves (the patients FilteredView-proven\n * poll-then-set path; `firstDataRendered` gating is forbidden — zero-row\n * grids may never fire it).\n *\n * Persistence is opt-in via `persistKey` → `data-table-tabs:<persistKey>`\n * with a versioned envelope; the active tab id is deliberately NOT persisted\n * so a restored session lands on the first tab instead of silently\n * re-applying a stale aggressive filter.\n */\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport type { GridApi } from 'ag-grid-community';\nimport { useControllableState } from '../../hooks/use-controllable-state';\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\n/** One saved-filter tab. */\nexport interface SavedTab {\n /** Opaque machine-generated id — emitted as `data-tab-id`, never the label. */\n id: string;\n /** User-typed name; rendered text only — never used in DOM hooks or keys. */\n label: string;\n /** AG Grid filter model (`DataTableHandle.getFilter()` shape); `{}` = none. */\n filterModel: Record<string, unknown>;\n /** Captured quick-filter text — only present when `captureQuickFilter`. */\n quickFilter?: string;\n /** Locked tabs cannot be renamed or removed (e.g. an \"All\" tab). */\n locked?: boolean;\n}\n\n/**\n * The narrow grid seam the tabs ride on. Structural, and `getRawApi` is\n * deliberately typed against the un-parameterised `GridApi` so any\n * `DataTableHandle<TData>` (incl. `PatientTableHandle`) satisfies it —\n * `GridApi<TData>` is invariant, so pinning a row type here would reject\n * every concretely-typed handle.\n */\nexport interface DataTableTabsGridSeam {\n getFilter: () => Record<string, unknown>;\n setFilter: (model: Record<string, unknown>) => void;\n getRawApi: () => GridApi | undefined;\n}\n\nexport interface UseDataTableFilterTabsOptions {\n /** Resolve the grid seam at call time — `undefined` until grid-ready. */\n getTable: () => DataTableTabsGridSeam | null | undefined;\n tabs?: SavedTab[];\n defaultTabs?: SavedTab[];\n onTabsChange?: (tabs: SavedTab[]) => void;\n activeTabId?: string;\n defaultActiveTabId?: string;\n onActiveTabChange?: (id: string) => void;\n /** Opt-in localStorage persistence — disabled while `tabs` is controlled. */\n persistKey?: string;\n /** Capture/restore `quickFilterText` alongside the model. Default false. */\n captureQuickFilter?: boolean;\n /** Apply the active tab's model on mount + tab switch. Default true. */\n applyOnActivate?: boolean;\n /** Cap on the number of tabs. Default 8. */\n maxTabs?: number;\n /** Factory for a new tab; `index` is 1-based (used for the default name). */\n createTab?: (index: number) => SavedTab;\n /** Id factory — default `crypto.randomUUID()` with a counter fallback. */\n makeTabId?: () => string;\n}\n\nexport interface UseDataTableFilterTabsResult {\n tabs: SavedTab[];\n activeTabId: string;\n selectTab: (id: string) => void;\n addTab: (label?: string, filterModel?: Record<string, unknown>) => string;\n renameTab: (id: string, label: string) => void;\n removeTab: (id: string) => void;\n atCapacity: boolean;\n /** Synchronous snapshots for the imperative handle / agent adapter —\n * fresh immediately after a mutation, before React re-renders. */\n getTabsSnapshot: () => SavedTab[];\n getActiveTabIdSnapshot: () => string;\n}\n\n/* ------------------------------------------------------------------ */\n/* Helpers */\n/* ------------------------------------------------------------------ */\n\nlet fallbackCounter = 0;\n\nfunction defaultMakeTabId(): string {\n try {\n if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {\n return crypto.randomUUID();\n }\n } catch {\n /* noop — fall through to the counter */\n }\n fallbackCounter += 1;\n return `tab-${fallbackCounter}`;\n}\n\n/** Versioned persistence envelope — mirrors the col-state loader's guards. */\ninterface PersistEnvelope {\n v: 1;\n tabs: SavedTab[];\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n !Array.isArray(value) &&\n Object.getPrototypeOf(value) !== null\n );\n}\n\nfunction isValidEnvelope(value: unknown): value is PersistEnvelope {\n if (!isPlainObject(value) || value.v !== 1 || !Array.isArray(value.tabs)) {\n return false;\n }\n return (value.tabs as unknown[]).every(\n (tab) =>\n isPlainObject(tab) &&\n typeof tab.id === 'string' &&\n typeof tab.label === 'string' &&\n isPlainObject(tab.filterModel),\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Hook */\n/* ------------------------------------------------------------------ */\n\nexport function useDataTableFilterTabs(\n options: UseDataTableFilterTabsOptions,\n): UseDataTableFilterTabsResult {\n const {\n getTable,\n tabs: tabsProp,\n defaultTabs,\n onTabsChange,\n activeTabId: activeTabIdProp,\n defaultActiveTabId,\n onActiveTabChange,\n persistKey,\n captureQuickFilter = false,\n applyOnActivate = true,\n maxTabs = 8,\n createTab,\n makeTabId = defaultMakeTabId,\n } = options;\n\n const { t } = useTranslation();\n\n const isControlled = tabsProp !== undefined;\n if (import.meta.env.DEV && isControlled && persistKey) {\n // eslint-disable-next-line no-console\n console.warn(\n 'DataTableTabs: `persistKey` is ignored while `tabs` is controlled — persist from `onTabsChange` instead.',\n );\n }\n\n // Uncontrolled seed: defaultTabs, else one unfiltered tab.\n const seed = useMemo<SavedTab[]>(() => {\n if (defaultTabs && defaultTabs.length > 0) return defaultTabs;\n return [\n {\n id: 'default',\n label: t('dataTableTabs.newTabName', { number: 1 }),\n filterModel: {},\n },\n ];\n // The seed is computed once for the uncontrolled path; label language at\n // mount time is acceptable for a user-renameable default.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Persistence backs the UNCONTROLLED state only. The envelope validates on\n // load; any mismatch falls back to the seed. Read once, on mount.\n const persistedTabs = useMemo<SavedTab[] | null>(() => {\n if (!persistKey || isControlled || typeof window === 'undefined') {\n return null;\n }\n try {\n const raw = window.localStorage.getItem(`data-table-tabs:${persistKey}`);\n if (!raw) return null;\n const parsed: unknown = JSON.parse(raw);\n return isValidEnvelope(parsed) ? parsed.tabs : null;\n } catch {\n return null;\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const [tabsState, setTabsState] = useControllableState<SavedTab[]>({\n value: tabsProp,\n defaultValue: persistedTabs ?? seed,\n onChange: onTabsChange,\n });\n const tabs = useMemo(() => tabsState ?? seed, [tabsState, seed]);\n\n // Mirror every uncontrolled tabs write into storage synchronously\n // (crash-safe — no effect lag). The ref also updates synchronously so\n // successive same-tick writes compose instead of both reading the\n // pre-render snapshot.\n const setTabs = useCallback(\n (update: (prev: SavedTab[]) => SavedTab[]) => {\n const next = update(tabsRef.current);\n tabsRef.current = next;\n setTabsState(next);\n if (persistKey && !isControlled) {\n try {\n const envelope: PersistEnvelope = { v: 1, tabs: next };\n window.localStorage.setItem(\n `data-table-tabs:${persistKey}`,\n JSON.stringify(envelope),\n );\n } catch {\n // localStorage may be unavailable in some environments\n }\n }\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [setTabsState, persistKey, isControlled],\n );\n\n const [activeIdState, setActiveIdState] = useControllableState<string>({\n value: activeTabIdProp,\n defaultValue: defaultActiveTabId ?? tabs[0]?.id ?? 'default',\n onChange: onActiveTabChange,\n });\n const activeTabId = activeIdState ?? tabs[0]?.id ?? 'default';\n\n /* -- refs mirroring the latest state for event handlers ------------ */\n const tabsRef = useRef(tabs);\n tabsRef.current = tabs;\n const activeIdRef = useRef(activeTabId);\n activeIdRef.current = activeTabId;\n /** True while the hook itself is writing the grid's filter model. */\n const applyingRef = useRef(false);\n const getTableRef = useRef(getTable);\n getTableRef.current = getTable;\n\n /* -- CAPTURE: write the live model into the active tab ------------- */\n const captureIntoActive = useCallback(() => {\n const seam = getTableRef.current();\n if (!seam) return;\n const model = seam.getFilter() ?? {};\n const quick = captureQuickFilter\n ? ((): string | undefined => {\n const raw = seam.getRawApi()?.getGridOption('quickFilterText');\n return typeof raw === 'string' && raw.length > 0 ? raw : undefined;\n })()\n : undefined;\n setTabs((prev) =>\n prev.map((tab) =>\n tab.id === activeIdRef.current\n ? {\n ...tab,\n filterModel: model,\n ...(captureQuickFilter ? { quickFilter: quick } : {}),\n }\n : tab,\n ),\n );\n }, [captureQuickFilter, setTabs]);\n\n /* -- RESTORE: apply a tab's model to the grid ----------------------- */\n const applyTab = useCallback(\n (tab: SavedTab | undefined) => {\n if (!tab) return;\n const seam = getTableRef.current();\n if (!seam) return;\n applyingRef.current = true;\n try {\n seam.setFilter(tab.filterModel ?? {});\n const api = seam.getRawApi();\n if (captureQuickFilter) {\n api?.setGridOption('quickFilterText', tab.quickFilter ?? '');\n }\n // Re-run filtering + notify listeners (floating filters, chips, agent\n // state) — setFilterModel alone does not.\n api?.onFilterChanged();\n } finally {\n // AG Grid dispatches filterChanged synchronously, so a synchronous\n // clear is exact: a host setFilter one tick later is never swallowed.\n // If a grid path ever fires async, the stray capture writes back the\n // model we just applied — idempotent, harmless.\n applyingRef.current = false;\n }\n },\n [captureQuickFilter],\n );\n\n /* -- SUBSCRIBE-LATE + INITIAL APPLY --------------------------------- */\n useEffect(() => {\n let cleanup: (() => void) | undefined;\n const handler = () => {\n if (applyingRef.current) return;\n captureIntoActive();\n };\n const wire = (seam: DataTableTabsGridSeam) => {\n const api = seam.getRawApi();\n if (!api) return false;\n api.addEventListener('filterChanged', handler);\n cleanup = () => api.removeEventListener('filterChanged', handler);\n if (applyOnActivate) {\n const active = tabsRef.current.find(\n (tab) => tab.id === activeIdRef.current,\n );\n if (\n active &&\n (Object.keys(active.filterModel ?? {}).length > 0 ||\n (captureQuickFilter && active.quickFilter))\n ) {\n applyTab(active);\n }\n }\n return true;\n };\n const seam = getTableRef.current();\n if (seam && wire(seam)) return () => cleanup?.();\n // Grid not ready yet — poll until it is (never bail one-shot).\n const timer = window.setInterval(() => {\n const late = getTableRef.current();\n if (!late) return;\n if (wire(late)) window.clearInterval(timer);\n }, 50);\n return () => {\n window.clearInterval(timer);\n cleanup?.();\n };\n }, [applyOnActivate, applyTab, captureIntoActive, captureQuickFilter]);\n\n /* -- public reducers ------------------------------------------------ */\n\n const selectTab = useCallback(\n (id: string) => {\n if (id === activeIdRef.current) return;\n const next = tabsRef.current.find((tab) => tab.id === id);\n if (!next) return;\n // Defensive final snapshot of the outgoing tab — quickFilter has no\n // model event, so this is its only reliable capture point.\n captureIntoActive();\n setActiveIdState(id);\n activeIdRef.current = id;\n if (applyOnActivate) applyTab(next);\n },\n [applyOnActivate, applyTab, captureIntoActive, setActiveIdState],\n );\n\n const addTab = useCallback(\n (label?: string, filterModel?: Record<string, unknown>): string => {\n if (tabsRef.current.length >= maxTabs) return activeIdRef.current;\n const index = tabsRef.current.length + 1;\n const fresh: SavedTab = createTab\n ? createTab(index)\n : {\n id: makeTabId(),\n label: label ?? t('dataTableTabs.newTabName', { number: index }),\n // New tab = copy of the CURRENT live model, so the grid view does\n // not jump on add — the natural gesture is \"save my current\n // filter as a tab\".\n filterModel:\n filterModel ?? getTableRef.current()?.getFilter() ?? {},\n };\n setTabs((prev) => [...prev, fresh]);\n setActiveIdState(fresh.id);\n activeIdRef.current = fresh.id;\n return fresh.id;\n },\n [createTab, makeTabId, maxTabs, setActiveIdState, setTabs, t],\n );\n\n const renameTab = useCallback(\n (id: string, label: string) => {\n const trimmed = label.trim();\n if (!trimmed) return;\n setTabs((prev) =>\n prev.map((tab) =>\n tab.id === id && !tab.locked ? { ...tab, label: trimmed } : tab,\n ),\n );\n },\n [setTabs],\n );\n\n const removeTab = useCallback(\n (id: string) => {\n const current = tabsRef.current;\n const target = current.find((tab) => tab.id === id);\n if (!target || target.locked || current.length <= 1) return;\n const index = current.findIndex((tab) => tab.id === id);\n const next = current.filter((tab) => tab.id !== id);\n setTabs(() => next);\n if (activeIdRef.current === id) {\n // Activate the inline-start neighbour (else the next one) and apply\n // its model so the grid never shows a removed tab's filter.\n const neighbour = next[Math.max(0, index - 1)];\n if (neighbour) {\n setActiveIdState(neighbour.id);\n activeIdRef.current = neighbour.id;\n if (applyOnActivate) applyTab(neighbour);\n }\n }\n },\n [applyOnActivate, applyTab, setActiveIdState, setTabs],\n );\n\n const getTabsSnapshot = useCallback(() => tabsRef.current, []);\n const getActiveTabIdSnapshot = useCallback(() => activeIdRef.current, []);\n\n return {\n tabs,\n activeTabId,\n selectTab,\n addTab,\n renameTab,\n removeTab,\n atCapacity: tabs.length >= maxTabs,\n getTabsSnapshot,\n getActiveTabIdSnapshot,\n };\n}\n","/**\n * DataTableTabs — saved-filter folder tabs for any kit DataTable.\n * ----------------------------------------------------------------------------\n * A folder-tab strip attached above a table region. Each tab captures and\n * restores its own AG Grid filter model (tabs are LIVE views — any filter\n * edit rewrites the active tab); a \"+\" button saves the current filter as a\n * new tab; tabs rename via double-click / F2 and remove via Delete/Backspace\n * or the pointer-only ✕.\n *\n * ARIA: a real APG tablist built on Radix Tabs primitives with\n * `activationMode=\"manual\"` (arrow keys rove without applying filter models;\n * Enter/Space activates). Every trigger points at ONE stable hand-rolled\n * tabpanel that wraps `children` — the grid never remounts on tab switch and\n * no dangling ARIA refs exist. The \"+\" button and the rename editor are\n * SIBLINGS of the tablist (tablist purity).\n *\n * Grid wiring rides the narrow `DataTableTabsGridSeam` resolved at call time\n * (`getTable={() => tableRef.current}`) — see use-data-table-filter-tabs.ts\n * for the sync engine. Works against any `DataTableHandle`-shaped ref,\n * including `PatientTableHandle`.\n */\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useRef,\n useState,\n type KeyboardEvent,\n type ReactNode,\n} from 'react';\nimport * as TabsPrimitive from '@radix-ui/react-tabs';\nimport { cva, cx, type VariantProps } from 'class-variance-authority';\nimport { Check, Plus, X } from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\nimport { IconButton } from '../button/icon-button';\nimport { Popover } from '../popover';\nimport { TextInput } from '../text-input';\nimport { useDirection } from '../../hooks/use-direction';\nimport { useAgentRegistration } from '../../agent';\nimport { dataTableTabsAgent } from './data-table-tabs.agent';\nimport {\n useDataTableFilterTabs,\n type DataTableTabsGridSeam,\n type SavedTab,\n} from './use-data-table-filter-tabs';\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nexport const dataTableTabsListVariants = cva(\n [\n // The strip scrolls horizontally when tabs overflow; focusable tabs inside\n // (with an inset focus ring, below) satisfy scrollable-region-focusable.\n // No baseline border and no leading padding: the first tab sits flush with\n // the table's inline-start edge and the active tab merges straight into the\n // grid header below — the table's own rounded top is the only edge drawn.\n 'ds:flex ds:items-end ds:gap-[var(--spacing-2xs)] ds:overflow-x-auto',\n 'ds:pe-[var(--spacing-2xs)]',\n ].join(' '),\n {\n variants: {\n size: {\n md: '',\n lg: '',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nexport const dataTableTabsTriggerVariants = cva(\n [\n // `group` so the pointer-only ✕ can reveal on hover/focus/active; `relative`\n // so that ✕ can be absolutely positioned in the trailing padding (below).\n 'ds:group ds:relative ds:inline-flex ds:items-center ds:justify-center',\n 'ds:whitespace-nowrap ds:select-none ds:cursor-pointer ds:shrink-0',\n // Logical top corners so the leading corner can be squared in either text\n // direction (see the first-tab override + the grid corner below). Inactive\n // tabs take a crisp white fill so they read clearly against a warm page\n // instead of dissolving into it; muted text keeps them clearly unselected.\n // (Focus ring is INSET — see FOCUS_RING — so the scroll container never\n // clips it and the flush-leading first tab keeps its ring.)\n 'ds:[border-start-start-radius:var(--radius-md)] ds:[border-start-end-radius:var(--radius-md)]',\n 'ds:bg-background ds:text-muted-foreground',\n // Match the grid header's eyebrow treatment (uppercase, tracked, medium):\n // the strip reads as one unit with the header below. `text-transform` is\n // display-only — the stored label keeps its case for the rename editor, and\n // uppercase is a no-op on caseless scripts (Arabic, CJK).\n 'ds:font-medium ds:uppercase ds:tracking-[var(--letter-spacing-uppercase)]',\n 'ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n 'ds:hover:bg-muted/40 ds:hover:text-foreground',\n // NOTE: the focus ring is applied by the component (see FOCUS_RING), gated\n // on keyboard modality — Radix focuses the trigger on click, which would\n // otherwise show a `:focus-visible` ring on a plain mouse selection.\n // Active = the table header surface (`--muted`): a 1px downward pull fuses\n // it into one continuous grey sheet with the grid header below. That fuse\n // (grey fill + foreground text) is the whole selection cue — no accent bar.\n 'ds:data-[state=active]:bg-muted ds:data-[state=active]:text-foreground',\n 'ds:data-[state=active]:z-[1]',\n 'ds:data-[state=active]:[margin-block-end:-1px]',\n 'ds:disabled:pointer-events-none ds:disabled:opacity-50',\n ].join(' '),\n {\n variants: {\n size: {\n // Symmetric inline padding so the label centres and every tab matches,\n // removable or not; the ✕ lives in the trailing padding (absolute).\n md: 'ds:h-10 ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:text-[length:var(--font-size-2xs)]',\n lg: 'ds:h-12 ds:ps-[var(--spacing-lg)] ds:pe-[var(--spacing-lg)] ds:text-[length:var(--font-size-xs)]',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\n/**\n * Inset keyboard focus ring — applied only while the user is navigating by\n * keyboard (see `useKeyboardModality`). Inset (offset negated) so the scroll\n * container never clips it and the flush-leading first tab keeps its ring.\n */\nconst FOCUS_RING =\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-ring ds:focus-visible:[outline-offset:calc(-1*var(--focus-ring-width))]';\n\n/**\n * True while the last input was a keyboard, false after any pointer press.\n * Radix Tabs programmatically focuses a trigger on click, which makes Chrome\n * match `:focus-visible` even for a mouse selection; gating the ring on real\n * keyboard use restores the intended behaviour without losing keyboard focus\n * visibility. Document-level listeners catch the Tab press that moves focus\n * INTO the tablist (its target is outside this component's subtree).\n */\nfunction useKeyboardModality(): boolean {\n const [keyboard, setKeyboard] = useState(false);\n useEffect(() => {\n // Functional updates return the same value when unchanged, so React bails\n // out of re-rendering except on an actual modality transition.\n const onKey = () => setKeyboard((k) => k || true);\n const onPointer = () => setKeyboard((k) => (k ? false : k));\n document.addEventListener('keydown', onKey, true);\n document.addEventListener('pointerdown', onPointer, true);\n return () => {\n document.removeEventListener('keydown', onKey, true);\n document.removeEventListener('pointerdown', onPointer, true);\n };\n }, []);\n return keyboard;\n}\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\n/** Curated imperative handle — also the agent-adapter surface. */\nexport interface DataTableTabsHandle {\n getTabs: () => SavedTab[];\n getActiveTabId: () => string | null;\n /** Snapshots the outgoing tab, applies the incoming tab's filter model. */\n selectTab: (id: string) => void;\n /** Default model = a copy of the current live model (grid view unchanged). */\n addTab: (label?: string, filterModel?: Record<string, unknown>) => string;\n /** No-op on locked tabs and empty/whitespace labels. */\n renameTab: (id: string, label: string) => void;\n /** No-op on locked tabs and the last remaining tab. */\n removeTab: (id: string) => void;\n}\n\nexport interface DataTableTabsProps extends VariantProps<\n typeof dataTableTabsTriggerVariants\n> {\n /**\n * Resolve the grid seam at call time — `undefined` until the grid is ready\n * (same convention as `ToolbarProvider.getApi`). Any `DataTableHandle`\n * satisfies it: `getTable={() => tableRef.current}`.\n */\n getTable: () => DataTableTabsGridSeam | null | undefined;\n /** Controlled tabs — pair with `onTabsChange`. */\n tabs?: SavedTab[];\n /** Uncontrolled seed. Default: one unfiltered tab. */\n defaultTabs?: SavedTab[];\n onTabsChange?: (tabs: SavedTab[]) => void;\n activeTabId?: string;\n defaultActiveTabId?: string;\n onActiveTabChange?: (id: string) => void;\n /**\n * Opt-in localStorage persistence under `data-table-tabs:<persistKey>`.\n * Ignored (with a DEV warning) while `tabs` is controlled. The active tab\n * id is deliberately not persisted.\n */\n persistKey?: string;\n /** Capture/restore the quick-filter text alongside the model. Default false. */\n captureQuickFilter?: boolean;\n /** Apply the active tab's model on mount + switch. Default true. */\n applyOnActivate?: boolean;\n /** Show the \"+\" button. Default true. */\n allowAdd?: boolean;\n /** Allow rename (double-click / F2 / popover editor). Default true. */\n allowRename?: boolean;\n /** Allow removal (Delete/Backspace / ✕). Default true. */\n allowRemove?: boolean;\n /** Tab cap — \"+\" disables at the cap. Default 8. */\n maxTabs?: number;\n /** Factory for new tabs; `index` is 1-based. */\n createTab?: (index: number) => SavedTab;\n /** Id factory — default `crypto.randomUUID()`. */\n makeTabId?: () => string;\n /** Open the rename editor on a freshly-added tab. Default true. */\n renameOnAdd?: boolean;\n /** `data-component-id` + agent instance id. */\n id?: string;\n className?: string;\n /** Accessible tablist name — defaults to the localised \"Saved views\". */\n 'aria-label'?: string;\n /** The table region — rendered inside the single stable tabpanel. */\n children: ReactNode;\n}\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport const DataTableTabs = forwardRef<\n DataTableTabsHandle,\n DataTableTabsProps\n>(function DataTableTabs(\n {\n getTable,\n tabs: tabsProp,\n defaultTabs,\n onTabsChange,\n activeTabId: activeTabIdProp,\n defaultActiveTabId,\n onActiveTabChange,\n persistKey,\n captureQuickFilter = false,\n applyOnActivate = true,\n allowAdd = true,\n allowRename = true,\n allowRemove = true,\n maxTabs = 8,\n createTab,\n makeTabId,\n renameOnAdd = true,\n size = 'md',\n id,\n className,\n 'aria-label': ariaLabel,\n children,\n },\n ref,\n) {\n const { t } = useTranslation();\n const rootRef = useRef<HTMLDivElement>(null);\n const dir = useDirection(rootRef);\n const keyboardNav = useKeyboardModality();\n const baseId = useId();\n const panelId = `${baseId}-panel`;\n const hintId = `${baseId}-hint`;\n\n const {\n tabs,\n activeTabId,\n selectTab,\n addTab,\n renameTab,\n removeTab,\n atCapacity,\n getTabsSnapshot,\n getActiveTabIdSnapshot,\n } = useDataTableFilterTabs({\n getTable,\n tabs: tabsProp,\n defaultTabs,\n onTabsChange,\n activeTabId: activeTabIdProp,\n defaultActiveTabId,\n onActiveTabChange,\n persistKey,\n captureQuickFilter,\n applyOnActivate,\n maxTabs,\n createTab,\n makeTabId,\n });\n\n /* -- rename editor state -------------------------------------------- */\n const [renamingId, setRenamingId] = useState<string | null>(null);\n const renameInputRef = useRef<HTMLInputElement>(null);\n const renamingTab = tabs.find((tab) => tab.id === renamingId);\n\n /* -- announcements (sr-only live region) ----------------------------- */\n const [announcement, setAnnouncement] = useState('');\n useEffect(() => {\n if (!announcement) return;\n const timer = setTimeout(() => setAnnouncement(''), 3000);\n return () => clearTimeout(timer);\n }, [announcement]);\n\n /* -- focus management ------------------------------------------------ */\n const triggerRefs = useRef(new Map<string, HTMLButtonElement>());\n const focusTrigger = useCallback((tabId: string) => {\n // After a state flush — the trigger may have just (un)mounted.\n requestAnimationFrame(() => {\n triggerRefs.current.get(tabId)?.focus();\n });\n }, []);\n\n const canRename = useCallback(\n (tab: SavedTab) => allowRename && !tab.locked,\n [allowRename],\n );\n const canRemove = useCallback(\n (tab: SavedTab) => allowRemove && !tab.locked && tabs.length > 1,\n [allowRemove, tabs.length],\n );\n\n const openRename = useCallback(\n (tab: SavedTab) => {\n if (!canRename(tab)) return;\n setRenamingId(tab.id);\n },\n [canRename],\n );\n\n const commitRename = useCallback(() => {\n if (renamingId) {\n const value = renameInputRef.current?.value ?? '';\n if (value.trim()) {\n renameTab(renamingId, value);\n setAnnouncement(\n t('dataTableTabs.announce.renamed', { name: value.trim() }),\n );\n }\n }\n setRenamingId(null);\n }, [renamingId, renameTab, t]);\n\n const cancelRename = useCallback(() => setRenamingId(null), []);\n\n const handleAdd = useCallback(() => {\n // Mirror the hook's default naming — state hasn't flushed yet, so the\n // fresh tab isn't in `tabs` at announce time.\n const label = t('dataTableTabs.newTabName', { number: tabs.length + 1 });\n const newId = addTab();\n setAnnouncement(t('dataTableTabs.announce.added', { name: label }));\n if (renameOnAdd && allowRename) {\n // The rename popover autofocuses its input; focusing the trigger too\n // would steal focus from it — the editor's onCloseAutoFocus returns\n // focus to the trigger instead.\n setRenamingId(newId);\n } else {\n focusTrigger(newId);\n }\n }, [addTab, allowRename, focusTrigger, renameOnAdd, t, tabs.length]);\n\n const handleRemove = useCallback(\n (tab: SavedTab) => {\n if (!canRemove(tab)) return;\n const index = tabs.findIndex((candidate) => candidate.id === tab.id);\n const neighbour = tabs.filter((candidate) => candidate.id !== tab.id)[\n Math.max(0, index - 1)\n ];\n removeTab(tab.id);\n setAnnouncement(t('dataTableTabs.announce.removed', { name: tab.label }));\n if (neighbour) focusTrigger(neighbour.id);\n },\n [canRemove, focusTrigger, removeTab, t, tabs],\n );\n\n const handleTriggerKeyDown = useCallback(\n (event: KeyboardEvent<HTMLButtonElement>, tab: SavedTab) => {\n // APG dismissible-tab pattern: Delete/Backspace removes; F2 renames.\n // The visual ✕ is pointer-only (aria-hidden) to avoid nested-interactive.\n if (event.key === 'Delete' || event.key === 'Backspace') {\n event.preventDefault();\n handleRemove(tab);\n } else if (event.key === 'F2') {\n event.preventDefault();\n openRename(tab);\n }\n },\n [handleRemove, openRename],\n );\n\n /* -- curated handle + agent registration ----------------------------- */\n // Latest-value refs so the stable handle object never closes over stale\n // callbacks (DataTable handle precedent — stable identity, live reads).\n // Reads go through the hook's synchronous snapshots, so a mutation is\n // visible to the very next handle call, before React re-renders.\n const selectTabRef = useRef(selectTab);\n selectTabRef.current = selectTab;\n const addTabRef = useRef(addTab);\n addTabRef.current = addTab;\n const renameTabRef = useRef(renameTab);\n renameTabRef.current = renameTab;\n const removeTabHandlerRef = useRef(handleRemove);\n removeTabHandlerRef.current = handleRemove;\n\n const handleRef = useRef<DataTableTabsHandle>({\n getTabs: () => getTabsSnapshot(),\n getActiveTabId: () => getActiveTabIdSnapshot() ?? null,\n selectTab: (tabId) => selectTabRef.current(tabId),\n addTab: (label, filterModel) => addTabRef.current(label, filterModel),\n renameTab: (tabId, label) => renameTabRef.current(tabId, label),\n removeTab: (tabId) => {\n const tab = getTabsSnapshot().find((candidate) => candidate.id === tabId);\n if (tab) removeTabHandlerRef.current(tab);\n },\n });\n\n useImperativeHandle(ref, () => handleRef.current, []);\n useAgentRegistration(dataTableTabsAgent, handleRef.current, id);\n\n /* -- render ----------------------------------------------------------- */\n const activeTriggerId = `${baseId}-tab-${activeTabId}`;\n\n return (\n <div\n ref={rootRef}\n data-component=\"data-table-tabs\"\n data-component-id={id}\n className={cx('ds:flex ds:min-w-0 ds:flex-col', className)}\n >\n <Popover.Root\n open={renamingId !== null}\n onOpenChange={(open) => {\n if (!open) cancelRename();\n }}\n >\n <TabsPrimitive.Root\n value={activeTabId}\n onValueChange={selectTab}\n orientation=\"horizontal\"\n activationMode=\"manual\"\n dir={dir}\n >\n <div className=\"ds:flex ds:items-end ds:gap-[var(--spacing-2xs)]\">\n <TabsPrimitive.List\n aria-label={ariaLabel ?? t('dataTableTabs.tablistLabel')}\n aria-describedby={hintId}\n className={cx(\n dataTableTabsListVariants({ size }),\n 'ds:min-w-0 ds:flex-initial',\n )}\n >\n {tabs.map((tab, index) => {\n const trigger = (\n <TabsPrimitive.Trigger\n key={tab.id}\n ref={(node) => {\n if (node) triggerRefs.current.set(tab.id, node);\n else triggerRefs.current.delete(tab.id);\n }}\n value={tab.id}\n id={`${baseId}-tab-${tab.id}`}\n aria-controls={panelId}\n data-tab-id={tab.id}\n className={cx(\n dataTableTabsTriggerVariants({ size }),\n // The leading tab sits flush in the panel's leading-top\n // corner: square that corner (and the grid's, below) so\n // the leading edge is one straight line with no cut-in.\n index === 0 && 'ds:[border-start-start-radius:0]',\n // Keyboard-only focus ring; in pointer mode suppress the\n // UA default outline that Radix's click-focus would show.\n keyboardNav\n ? FOCUS_RING\n : 'ds:focus-visible:outline-none',\n )}\n onKeyDown={(event) => handleTriggerKeyDown(event, tab)}\n onDoubleClick={() => openRename(tab)}\n >\n <span className=\"ds:max-w-[12rem] ds:truncate\">\n {tab.label}\n </span>\n {canRemove(tab) && (\n // Visual-only dismiss — pointer removal fires here;\n // keyboard removal is Delete/Backspace on the trigger.\n // No role/tabIndex: interactive content inside <button>\n // is invalid HTML (kit TabsTrigger precedent). Absolutely\n // positioned in the trailing padding and hidden until the\n // tab is hovered/focused or active, so it reserves no flow\n // space — every tab keeps the same symmetric padding\n // whether or not it can be removed, and labels never\n // reflow when it reveals.\n <span\n aria-hidden=\"true\"\n className={cx(\n 'ds:absolute ds:end-[var(--spacing-2xs)] ds:top-1/2 ds:-translate-y-1/2',\n 'ds:inline-flex ds:items-center ds:justify-center',\n 'ds:size-3.5 ds:rounded-[var(--radius-sm)]',\n 'ds:opacity-0 ds:transition-opacity ds:motion-reduce:transition-none',\n 'ds:group-hover:opacity-100 ds:group-focus-visible:opacity-100',\n 'ds:group-data-[state=active]:opacity-100',\n 'ds:hover:bg-muted-foreground/20',\n )}\n onPointerDown={(event) => {\n event.stopPropagation();\n event.preventDefault();\n handleRemove(tab);\n }}\n >\n <X className=\"ds:size-3\" />\n </span>\n )}\n </TabsPrimitive.Trigger>\n );\n return renamingId === tab.id ? (\n <Popover.Anchor asChild key={tab.id}>\n {trigger}\n </Popover.Anchor>\n ) : (\n trigger\n );\n })}\n </TabsPrimitive.List>\n {allowAdd && (\n <IconButton\n intent=\"ghost\"\n size=\"sm\"\n icon={<Plus aria-hidden=\"true\" />}\n tooltip={\n atCapacity\n ? t('dataTableTabs.maxTabsReached')\n : t('dataTableTabs.addTab')\n }\n disabled={atCapacity}\n className=\"ds:mb-[var(--spacing-2xs)] ds:shrink-0\"\n onClick={handleAdd}\n />\n )}\n </div>\n\n {/* ONE stable tabpanel — the grid never remounts on tab switch.\n Square the nested AG-Grid wrapper's leading-top corner so it lines\n up flush under the leading tab instead of curving in beneath it.\n The selector no-ops for non-AG table regions. */}\n <div\n role=\"tabpanel\"\n id={panelId}\n aria-labelledby={activeTriggerId}\n className=\"ds:min-w-0 ds:[&_.ag-root-wrapper]:[border-start-start-radius:0]\"\n >\n {children}\n </div>\n </TabsPrimitive.Root>\n\n {/* Rename editor — sibling of the tablist, anchored to the renaming\n trigger via Popover.Anchor (never a Popover.Trigger on a tab). */}\n <Popover.Content\n side=\"bottom\"\n align=\"start\"\n aria-label={t('dataTableTabs.rename.title')}\n className=\"ds:w-64\"\n onCloseAutoFocus={(event) => {\n // Return focus to the (possibly freshly-added) tab trigger so\n // keyboard users don't land back on the \"+\" button.\n event.preventDefault();\n if (renamingTab) focusTrigger(renamingTab.id);\n else if (activeTabId) focusTrigger(activeTabId);\n }}\n onOpenAutoFocus={(event) => {\n event.preventDefault();\n renameInputRef.current?.focus();\n renameInputRef.current?.select();\n }}\n >\n <form\n className=\"ds:flex ds:items-center ds:gap-[var(--spacing-2xs)]\"\n onSubmit={(event) => {\n event.preventDefault();\n commitRename();\n }}\n >\n <TextInput\n ref={renameInputRef}\n size=\"sm\"\n aria-label={t('dataTableTabs.rename.inputLabel')}\n defaultValue={renamingTab?.label ?? ''}\n maxLength={60}\n className=\"ds:min-w-0 ds:flex-1\"\n />\n <IconButton\n type=\"submit\"\n intent=\"ghost\"\n size=\"sm\"\n icon={<Check aria-hidden=\"true\" />}\n tooltip={t('dataTableTabs.rename.save')}\n />\n <IconButton\n type=\"button\"\n intent=\"ghost\"\n size=\"sm\"\n icon={<X aria-hidden=\"true\" />}\n tooltip={t('dataTableTabs.rename.cancel')}\n onClick={cancelRename}\n />\n </form>\n </Popover.Content>\n </Popover.Root>\n\n {/* Keyboard hint for the tablist (aria-describedby target). */}\n <span id={hintId} className=\"ds:sr-only\">\n {t('dataTableTabs.hint')}\n </span>\n {/* Live region for add/remove/rename announcements. */}\n <span role=\"status\" aria-live=\"polite\" className=\"ds:sr-only\">\n {announcement}\n </span>\n </div>\n );\n});\n\nDataTableTabs.displayName = 'DataTableTabs';\n\nexport type { SavedTab, DataTableTabsGridSeam };\n"],"names":["dataTableTabsAgent","handle","id","label","args","fallbackCounter","defaultMakeTabId","isPlainObject","value","isValidEnvelope","tab","useDataTableFilterTabs","options","getTable","tabsProp","defaultTabs","onTabsChange","activeTabIdProp","defaultActiveTabId","onActiveTabChange","persistKey","captureQuickFilter","applyOnActivate","maxTabs","createTab","makeTabId","t","useTranslation","isControlled","seed","useMemo","persistedTabs","raw","parsed","tabsState","setTabsState","useControllableState","tabs","setTabs","useCallback","update","next","tabsRef","envelope","activeIdState","setActiveIdState","_a","activeTabId","_b","useRef","activeIdRef","applyingRef","getTableRef","captureIntoActive","seam","model","quick","prev","applyTab","api","useEffect","cleanup","handler","wire","active","timer","late","selectTab","addTab","filterModel","index","fresh","renameTab","trimmed","removeTab","current","target","neighbour","getTabsSnapshot","getActiveTabIdSnapshot","dataTableTabsListVariants","cva","dataTableTabsTriggerVariants","FOCUS_RING","useKeyboardModality","keyboard","setKeyboard","useState","onKey","k","onPointer","DataTableTabs","forwardRef","allowAdd","allowRename","allowRemove","renameOnAdd","size","className","ariaLabel","children","ref","rootRef","dir","useDirection","keyboardNav","baseId","useId","panelId","hintId","atCapacity","renamingId","setRenamingId","renameInputRef","renamingTab","announcement","setAnnouncement","triggerRefs","focusTrigger","tabId","canRename","canRemove","openRename","commitRename","cancelRename","handleAdd","newId","handleRemove","candidate","handleTriggerKeyDown","event","selectTabRef","addTabRef","renameTabRef","removeTabHandlerRef","handleRef","useImperativeHandle","useAgentRegistration","activeTriggerId","jsxs","cx","Popover","open","TabsPrimitive","jsx","trigger","node","X","IconButton","Plus","TextInput","Check"],"mappings":"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"}
|