import { Ref, WatchSource } from 'vue'; import { ColDef, GridApi, GridOptions, GridReadyEvent, RowClickedEvent, RowDoubleClickedEvent, CellClickedEvent, CellDoubleClickedEvent, CellValueChangedEvent, GetRowIdFunc, RowClassParams, IRowNode, IsExternalFilterPresentParams, GridSizeChangedEvent, CellContextMenuEvent, ColumnState, PostSortRowsParams, RowDragEndEvent } from 'ag-grid-community'; import { CoarGridColumnBuilder } from './coar-grid-column-builder'; import { CoarGridColumnFactory } from './coar-grid-column-factory'; type ColumnBuilderLike = { build(): ColDef; }; /** Column definition input - either a builder or a factory function */ export type ColumnDefinition = ColumnBuilderLike | ((factory: CoarGridColumnFactory) => ColumnBuilderLike); /** Configuration for tree (hierarchical) data */ export interface TreeDataConfig { /** Extract children from a row. Return empty array for leaf nodes. */ children: (row: TData) => TData[]; /** Extract a unique ID from a row. Used for tracking expanded state. */ rowId: (row: TData) => string; } /** Metadata about a tree node, available to cell renderers via AG Grid context */ export interface TreeNodeMeta { /** Nesting depth (0 = root) */ depth: number; /** Whether this node has children */ hasChildren: boolean; /** Whether this node is currently expanded */ isExpanded: boolean; /** Number of direct children */ childCount: number; } /** Tree context available on AG Grid's `context.coarTree` */ export interface CoarTreeContext { meta: Map; toggleRow: (id: string) => void; getRowId: (row: TData) => string; } /** Options for row drag highlight */ export interface RowDragHighlightOptions { /** Validate if dragged row can be dropped on target. Return `false` to show "not allowed" feedback. */ canDrop?: (draggedData: TData, targetData: TData) => boolean; } /** Options for column state persistence */ export interface ColumnPersistenceOptions { /** Bucket size in pixels. Grid width is rounded to the nearest bucket. Default: 100 */ bucketSize?: number; /** Debounce delay in ms for saving column state. Default: 500 */ debounceMs?: number; } /** Options for row selection */ export interface RowSelectionOptions { /** Show a checkbox column for selection (default: false for 'single', true for 'multiple') */ checkboxes?: boolean; /** Show a select-all checkbox in the header (only applies to 'multiple' mode, default: true when checkboxes is true) */ headerCheckbox?: boolean; /** Allow clicking anywhere on a row to select it (default: true) */ enableClickSelection?: boolean; } /** * Fluent builder for AG Grid configuration. * * @example * ```ts * const gridBuilder = CoarGridBuilder.create() * .columns([ * col => col.field('name').header('Name').flex(1), * col => col.field('email').header('Email').flex(1), * col => col.field('role').header('Role').width(100), * ]) * .rowData(users) * .rowId(user => user.id) * .onRowClicked(event => console.log(event.data)); * ``` */ export declare class CoarGridBuilder { #private; /** Reactive flag that becomes true when grid is ready */ readonly gridReady: Readonly>; private constructor(); /** Create a new grid builder */ static create(): CoarGridBuilder; /** Get the AG Grid API (available after grid ready) */ get api(): GridApi | undefined; /** Define columns using builders or factory functions */ columns(definitions: ColumnDefinition[]): this; /** Set default column definition applied to all columns */ defaultColDef(definition: Partial> | ((builder: CoarGridColumnBuilder) => CoarGridColumnBuilder)): this; /** Set row data (static array) */ rowData(data: TData[] | null): this; /** Set row data (reactive ref) */ rowDataRef(data: Ref): this; /** * Set row ID getter for immutable data updates. * When set, AG Grid uses delta updates instead of replacing all rows, * which preserves scroll position and improves performance. */ rowId(getRowId: GetRowIdFunc): this; /** * Enable row selection. * * @param mode - `'single'` or `'multiple'` * @param options - Optional configuration for checkboxes and click behavior * * @example * ```ts * // Click to select, no checkboxes * .rowSelection('single') * * // Checkboxes + click to select * .rowSelection('multiple', { checkboxes: true }) * * // Checkboxes only, no click selection * .rowSelection('multiple', { checkboxes: true, enableClickSelection: false }) * ``` */ rowSelection(mode: 'single' | 'multiple', options?: RowSelectionOptions): this; /** Set row class rules */ rowClassRules(rules: Record) => boolean) | string>): this; /** Set dynamic row class */ rowClass(fn: (params: RowClassParams) => string | string[] | undefined): this; /** Set initial sort column and direction */ defaultSort(field: string, direction: 'asc' | 'desc'): this; /** Set custom post-sort function to reorder rows after AG Grid sorts */ sortFunction(fn: (params: PostSortRowsParams) => void): this; /** Re-trigger sort and filter when the given watch source changes */ updateSortAndFilterWhen(trigger: WatchSource): this; /** Merge column state to restore column widths, order, visibility */ columnState(state: ColumnState[] | Ref): this; /** * Enable tree data mode with nested children. * * The builder flattens the tree before passing it to AG Grid, * respecting `openRows()` for expand/collapse. When a quick filter * is active, matching branches are automatically expanded. * * @example * ```ts * builder.treeData({ * children: (row) => row.children ?? [], * rowId: (row) => row.id, * }) * ``` */ treeData(config: TreeDataConfig): this; /** Set which parent rows are expanded (reactive ref of row IDs) */ openRows(openRows: Ref): this; /** * Force all tree parents to be expanded while the ref is `true`. * * When switching to `true`, the current open-state is saved. All parents * are shown expanded and chevron toggle is disabled. * When switching back to `false`, the saved open-state is restored. * * @example * ```ts * const forceExpanded = computed(() => showSubTodos.value && !!search.value) * builder * .treeData({ children: row => row.children, rowId: row => row.id }) * .openRows(openRows) * .forceExpanded(forceExpanded) * ``` */ forceExpanded(source: Ref): this; /** Enable full-row editing mode */ fullRowEdit(value?: boolean): this; /** Stop cell editing when cells lose focus */ stopEditingWhenCellsLoseFocus(value?: boolean): this; /** Enable shift-key column resize mode */ shiftResizeMode(value?: boolean): this; /** * Set the column auto-size strategy. * * @param strategy - `'fitGridWidth'` (columns fill the grid) or `'fitCellContents'` (columns fit their content) * * @example * ```ts * builder.autoSize('fitGridWidth') * ``` */ autoSize(strategy: 'fitGridWidth' | 'fitCellContents'): this; /** * Persist column state (widths, order, visibility, sort) in IndexedDB. * * The grid container width is rounded to the nearest bucket (default: 100px). * Each bucket gets its own saved column state, so different container * widths (monitor switch, sidebar collapse) each keep their own layout. * When no exact bucket exists, the nearest saved state is applied. * * **Live sync:** Multiple grids with the same `gridKey` synchronize * column changes instantly. Resizing a column in one grid updates all * others immediately. The IndexedDB write is debounced (default: 500ms) * but the cross-grid broadcast is instant. * * @param gridKey - Unique key for this grid (e.g. `'todo-list'`, `'admin-users'`). * Grids with the same key share persisted state and sync live. * @param options - Optional bucket size and debounce configuration * * @example * ```ts * // Basic usage * const builder = CoarGridBuilder.create() * .persistColumnState('admin-users') * .columns([...]) * * // Two grids with the same key sync column changes live * const gridA = CoarGridBuilder.create() * .persistColumnState('users') * .columns(sharedColumns) * .rowData(teamA); * * const gridB = CoarGridBuilder.create() * .persistColumnState('users') * .columns(sharedColumns) * .rowData(teamB); * ``` */ persistColumnState(gridKey: string, options?: ColumnPersistenceOptions): this; /** * Reset the persisted column state for a specific bucket and restore AG Grid defaults. * If no bucket is specified, the current width bucket is used. * * @param bucket - Width bucket to reset (e.g. `800`, `1200`). Defaults to the current bucket. * * @example * ```ts * // Reset current bucket * builder.resetPersistedState() * * // Reset a specific bucket * builder.resetPersistedState(1200) * ``` */ resetPersistedState(bucket?: number): Promise; /** * Reset all persisted column states for this grid (all buckets) * and restore AG Grid defaults. * * @example * ```ts * builder.resetPersistedStates() * ``` */ resetPersistedStates(): Promise; /** * Enable managed row drag & drop reordering. * AG Grid handles the visual reorder. Dragging is automatically * disabled when a column sort is active. * * Use `onRowDragEnd()` to persist the new order. * Use `.rowDrag()` on a column to show the drag handle. * * @example * ```ts * builder * .columns([col => col.field('name').rowDrag().flex(1)]) * .rowDragManaged() * .onRowDragEnd(() => { * const newOrder = builder.getDisplayedRowData(); * store.updateOrder(newOrder); * }); * ``` */ rowDragManaged(value?: boolean): this; /** * Handle row drag end event. Fires after a row has been dropped. * Use `getDisplayedRowData()` to read the new order. * * For tree data, use `event.node.data` (dragged) and `event.overNode?.data` (target). */ onRowDragEnd(handler: (event: RowDragEndEvent) => void): this; /** * Enable drop target highlighting during row drag. * Shows visual feedback on the target row: * - `.coar-drop-target` (blue outline) for valid targets * - `.coar-drop-target--invalid` (red dashed) for invalid targets * * @param options - Pass `canDrop` to validate drop targets * * @example * ```ts * builder.rowDragHighlight({ * canDrop: (dragged, target) => dragged.id !== target.id, * }) * ``` */ rowDragHighlight(options?: RowDragHighlightOptions | boolean): this; /** * Get tree node metadata (depth, hasChildren, isExpanded, childCount) for a given row ID. * Requires `treeData()` to be configured. Returns `undefined` if not found. */ getTreeMeta(rowId: string): TreeNodeMeta | undefined; /** * Get all row data in the current display order. * Useful after drag & drop to persist the new order. */ getDisplayedRowData(): TData[]; /** Handle grid ready event */ onGridReady(handler: (event: GridReadyEvent) => void): this; /** Handle row click */ onRowClicked(handler: (event: RowClickedEvent) => void): this; /** Handle row double-click */ onRowDoubleClicked(handler: (event: RowDoubleClickedEvent) => void): this; /** Handle cell click */ onCellClicked(handler: (event: CellClickedEvent) => void): this; /** Handle cell double-click */ onCellDoubleClicked(handler: (event: CellDoubleClickedEvent) => void): this; /** * Handle cell value change after an in-cell edit is committed. * Fires once per cell commit. Use together with column-level `editable()` and (optionally) `cellEditorConfig()`. */ onCellValueChanged(handler: (event: CellValueChangedEvent) => void): this; /** Handle grid size changed event */ onGridSizeChanged(handler: (event: GridSizeChangedEvent) => void): this; /** Handle cell context menu (right-click). Ctrl+click is passed through to the browser. */ onCellContextMenu(handler: (event: CellContextMenuEvent) => void): this; /** * Handle click on the grid viewport (empty area outside cells). * Wired by the wrapper component. */ onViewportClick(handler: ($event: MouseEvent, api: GridApi) => void): this; /** * Handle context menu on the grid viewport (empty area outside cells). * Wired by the wrapper component. */ onViewportContextMenu(handler: ($event: MouseEvent, api: GridApi) => void): this; /** Set external filter */ externalFilter(doesFilterPass: (node: IRowNode) => boolean, isFilterPresent?: (params: IsExternalFilterPresentParams) => boolean): this; /** Re-trigger external filter when the given watch source changes */ updateExternalFilterWhen(trigger: WatchSource): this; /** * Set the quick filter search text (reactive ref). * When set, row data is filtered before being passed to AG Grid. * * @example * ```ts * const search = ref(''); * builder.quickFilterText(search); * ``` */ quickFilterText(source: Ref): this; /** * Set a custom quick filter function. Overrides the default per-column matching. * * @param fn - Receives the normalized (lowercased, trimmed) search value and row data. * Return `true` to keep the row visible. * * @example * ```ts * builder.quickFilterFn((search, data) => { * return data.name.toLowerCase().includes(search) * || data.email.toLowerCase().includes(search); * }); * ``` */ quickFilterFn(fn: (searchValue: string, data: TData) => boolean): this; /** * Set a custom filter function that operates on the entire data array. * When set, AG Grid's per-row quick filter is bypassed — the data is filtered * by this function before being passed to AG Grid. * * This is useful for tree data where you need sibling-aware filtering * (e.g. keeping all children of a parent when any child matches). * * @param fn - Receives the full data array and the current search text. * Return the filtered array, or `null` to fall back to the * default quick filter for that evaluation. * * @example * ```ts * builder * .treeData({ children: row => row.children, rowId: row => row.id }) * .customFilter((items, search) => { * if (!showGroupFilter.value) return null; // fall back to quickFilter * if (!search.trim()) return items; * const q = search.toLowerCase(); * return items.filter(parent => * parent.name.toLowerCase().includes(q) || * parent.children.some(c => c.name.toLowerCase().includes(q)) * ); * }) * ``` */ customFilter(fn: (data: TData[], searchText: string) => TData[] | null): this; /** * Re-run the data pipeline when the given watch sources change. * Use this when `customFilter` or `quickFilterFn` depends on external reactive state. * * @example * ```ts * const showSubTodos = ref(false); * builder * .customFilter((todos, search) => { ... }) * .updateOn(showSubTodos) * ``` */ updateOn(...sources: WatchSource[]): this; /** * Enable search text highlighting using the CSS Custom Highlight API. * Matching text in grid cells is highlighted without modifying the DOM. * * Requires `quickFilterText()` to be set. * * @example * ```ts * builder * .quickFilterText(searchRef) * .searchHighlight() * ``` */ searchHighlight(value?: boolean): this; /** Enable row animation */ animateRows(value?: boolean): this; /** Set any AG Grid option directly */ option>(key: K, value: GridOptions[K]): this; /** Merge additional grid options */ options(options: GridOptions): this; /** @internal Called by the wrapper component to bind to AG Grid */ _bind(api: GridApi, gridElement?: HTMLElement): void; /** @internal Called by the wrapper component on unmount */ _destroy(): void; /** @internal Get viewport click handler (for wrapper component) */ _getViewportClickHandler(): (($event: MouseEvent, api: GridApi) => void) | undefined; /** @internal Get viewport context menu handler (for wrapper component) */ _getViewportContextMenuHandler(): (($event: MouseEvent, api: GridApi) => void) | undefined; /** @internal Check if a cell context menu handler is registered (for wrapper component) */ _hasCellContextMenuHandler(): boolean; /** Get column definitions (for wrapper component) */ _getColumnDefs(): ColDef[]; /** Get grid options (for wrapper component) */ _getGridOptions(): GridOptions; /** Get static row data (for wrapper component) */ _getRowData(): TData[] | null; /** @internal Whether data is loaded asynchronously (rowDataRef or tree/filter pipeline) */ _isAsyncData(): boolean; } export {}; //# sourceMappingURL=coar-grid-builder.d.ts.map