"use client" /** * TablePropertiesDrawerButton — reusable Properties button + drawer combo. * * Centralises the "sliders" icon button and the full `TablePropertiesDrawer` that was * previously duplicated per hub (TeamDrawerToolbar, ComplianceDrawerToolbar, …). * * Pass any `useTableState` return object as `state` — the component only reads * display-agnostic fields so it is compatible with any row shape. */ import * as React from "react" import { cn } from "../../lib/utils" import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "../ui/tooltip" import { Button } from "../ui/button" import { railTriggerProps } from "../ui/floating-sheet-panel" import { TablePropertiesDrawer } from "./drawer" import type { ActiveFilter, ConditionalRule, FilterFieldDef, SortRule, } from "../../lib/table-properties-types" import type { RowHeight } from "../../lib/row-height" import type { DataListViewType } from "../../lib/data-list-view" import type { DataListDisplayOptions } from "../../lib/data-list-display-options" // ───────────────────────────────────────────────────────────────────────────── // State interface // ───────────────────────────────────────────────────────────────────────────── /** * Structural interface over the fields consumed from `useTableState`. * Any `ReturnType>` is assignable to this type because: * - All non-row fields have the same shape regardless of `T`. * - `rows: T[]` is assignable to `rows: { length: number }` (arrays expose `.length`). */ export interface TablePropertiesDrawerButtonState { sheetOpen: boolean setSheetOpen: (v: boolean) => void /** * Optional deep-link target consumed by `TablePropertiesDrawer`'s `initialPanel`. * Required only if the hub deep-links into a specific drawer panel (e.g. the * column header's **Add Conditional Rule** sets it to `"conditional-rules"`). * Default state is `null`, which makes the drawer open on its index panel. */ sheetInitialPanel?: string | null setSheetInitialPanel?: (v: string | null) => void showGridlines: boolean setShowGridlines: (v: boolean) => void rowHeight: RowHeight setRowHeight: (v: RowHeight) => void activeFilters: ActiveFilter[] addFilter: (fieldKey: string, fromDrawer?: boolean) => void updateFilter: (id: string, patch: Partial) => void removeFilter: (id: string) => void getConnector: (leftFilterId: string) => "and" | "or" toggleConnector: (leftFilterId: string) => void openFilterPanelInDrawer: () => void filterBarVisible: boolean setFilterBarVisible: (v: boolean) => void drawerExpandedFilters: Set setDrawerExpandedFilters: React.Dispatch>> /** Only `.length` is read — compatible with any `T[]`. */ rows: { length: number } sortRules: SortRule[] setSortRules: (rules: SortRule[]) => void addSortRule: (fieldKey: string) => void removeSortRule: (id: string) => void toggleSortDir: (id: string) => void colOrder: string[] setColOrder: (order: string[]) => void hiddenCols: Set toggleColVisibility: (key: string) => void moveCol: (key: string, dir: "up" | "down") => void groupBy: string | null setGroupBy: (key: string | null) => void sortKey?: string } // ───────────────────────────────────────────────────────────────────────────── // Props // ───────────────────────────────────────────────────────────────────────────── export interface TablePropertiesDrawerButtonProps { /** Pass any `useTableState` return value. */ state: TablePropertiesDrawerButtonState totalRows: number filterFields: FilterFieldDef[] /** * Column definitions for the drawer's Sort / Group / Columns panels. * Required — derive from your column defs (`columnsToFieldDefinitions`) so * the drawer never falls back to product-specific defaults. */ fieldDefinitions: { key: string; label: string; sortable?: boolean }[] resolveColumnLabel?: (key: string) => string displayOptions: DataListDisplayOptions onDisplayOptionsChange: (patch: Partial) => void conditionalRules: ConditionalRule[] onAddConditionalRule: (rule: Omit) => void onRemoveConditionalRule: (id: string) => void onUpdateConditionalRule: (id: string, patch: Partial) => void /** * Whether pagination is enabled. Defaults to `false`. * Placements passes `true` because it supports server-side pagination. */ pagination?: boolean onPaginationChange?: (v: boolean) => void /** View type shown in the drawer header tile grid. */ currentView?: DataListViewType onViewChange?: (v: DataListViewType) => void /** * Subset of view types this hub implements. When provided, the drawer's view-type tile grid is * filtered so users cannot select an unsupported view (e.g. List hub never offers Dashboard). */ supportedViewTypes?: readonly DataListViewType[] /** Shown below the "Properties" title in the drawer header (e.g. "Team", "Compliance"). */ lifecycleTabLabel?: string /** * Edit-mode rename support — forwarded to `TablePropertiesDrawer`. When set * (and not in creation mode), the drawer renders an editable Name input on * the main panel bound to this controlled pair. Omit both to keep the * existing read-only header. */ viewName?: string onViewNameChange?: (name: string) => void /** * When the active view is Board and more than one entry is provided, the drawer renders * a selector for which field splits the board into swimlane columns. */ boardGroupByColumnOptions?: { key: string; label: string }[] /** Extra controls rendered before the Properties button (e.g. a dashboard-edit button). */ extraActions?: React.ReactNode /** Optional custom option renderer for filter values (e.g. status chips). */ renderFilterOptionValue?: (fieldKey: string, value: string) => React.ReactNode filterFieldContext?: Record /** Show record counts on each view tab. Surfaced as a toggle in the Display panel. */ showViewCounts?: boolean onShowViewCountsChange?: (v: boolean) => void /** * When `true`, the trigger `