@toolbox-web/grid 0.0.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/all.d.ts +3518 -0
- package/all.js +3762 -0
- package/all.js.map +1 -0
- package/index.d.ts +2367 -0
- package/index.js +3105 -0
- package/index.js.map +1 -0
- package/lib/plugins/clipboard/index.js +365 -0
- package/lib/plugins/clipboard/index.js.map +1 -0
- package/lib/plugins/column-virtualization/index.js +255 -0
- package/lib/plugins/column-virtualization/index.js.map +1 -0
- package/lib/plugins/context-menu/index.js +341 -0
- package/lib/plugins/context-menu/index.js.map +1 -0
- package/lib/plugins/export/index.js +305 -0
- package/lib/plugins/export/index.js.map +1 -0
- package/lib/plugins/filtering/index.js +759 -0
- package/lib/plugins/filtering/index.js.map +1 -0
- package/lib/plugins/grouping-columns/index.js +283 -0
- package/lib/plugins/grouping-columns/index.js.map +1 -0
- package/lib/plugins/grouping-rows/index.js +494 -0
- package/lib/plugins/grouping-rows/index.js.map +1 -0
- package/lib/plugins/master-detail/index.js +303 -0
- package/lib/plugins/master-detail/index.js.map +1 -0
- package/lib/plugins/multi-sort/index.js +270 -0
- package/lib/plugins/multi-sort/index.js.map +1 -0
- package/lib/plugins/pinned-columns/index.js +221 -0
- package/lib/plugins/pinned-columns/index.js.map +1 -0
- package/lib/plugins/pinned-rows/index.js +459 -0
- package/lib/plugins/pinned-rows/index.js.map +1 -0
- package/lib/plugins/pivot/index.js +326 -0
- package/lib/plugins/pivot/index.js.map +1 -0
- package/lib/plugins/reorder/index.js +260 -0
- package/lib/plugins/reorder/index.js.map +1 -0
- package/lib/plugins/selection/index.js +426 -0
- package/lib/plugins/selection/index.js.map +1 -0
- package/lib/plugins/server-side/index.js +241 -0
- package/lib/plugins/server-side/index.js.map +1 -0
- package/lib/plugins/tree/index.js +383 -0
- package/lib/plugins/tree/index.js.map +1 -0
- package/lib/plugins/undo-redo/index.js +289 -0
- package/lib/plugins/undo-redo/index.js.map +1 -0
- package/lib/plugins/visibility/index.js +430 -0
- package/lib/plugins/visibility/index.js.map +1 -0
- package/package.json +53 -0
- package/themes/dg-theme-contrast.css +43 -0
- package/themes/dg-theme-large.css +54 -0
- package/themes/dg-theme-standard.css +19 -0
- package/themes/dg-theme-vibrant.css +16 -0
- package/umd/grid.all.umd.js +660 -0
- package/umd/grid.all.umd.js.map +1 -0
- package/umd/grid.umd.js +105 -0
- package/umd/grid.umd.js.map +1 -0
- package/umd/plugins/clipboard.umd.js +9 -0
- package/umd/plugins/clipboard.umd.js.map +1 -0
- package/umd/plugins/column-virtualization.umd.js +2 -0
- package/umd/plugins/column-virtualization.umd.js.map +1 -0
- package/umd/plugins/context-menu.umd.js +53 -0
- package/umd/plugins/context-menu.umd.js.map +1 -0
- package/umd/plugins/export.umd.js +14 -0
- package/umd/plugins/export.umd.js.map +1 -0
- package/umd/plugins/filtering.umd.js +175 -0
- package/umd/plugins/filtering.umd.js.map +1 -0
- package/umd/plugins/grouping-columns.umd.js +29 -0
- package/umd/plugins/grouping-columns.umd.js.map +1 -0
- package/umd/plugins/grouping-rows.umd.js +40 -0
- package/umd/plugins/grouping-rows.umd.js.map +1 -0
- package/umd/plugins/master-detail.umd.js +27 -0
- package/umd/plugins/master-detail.umd.js.map +1 -0
- package/umd/plugins/multi-sort.umd.js +26 -0
- package/umd/plugins/multi-sort.umd.js.map +1 -0
- package/umd/plugins/pinned-columns.umd.js +2 -0
- package/umd/plugins/pinned-columns.umd.js.map +1 -0
- package/umd/plugins/pinned-rows.umd.js +73 -0
- package/umd/plugins/pinned-rows.umd.js.map +1 -0
- package/umd/plugins/pivot.umd.js +8 -0
- package/umd/plugins/pivot.umd.js.map +1 -0
- package/umd/plugins/reorder.umd.js +31 -0
- package/umd/plugins/reorder.umd.js.map +1 -0
- package/umd/plugins/selection.umd.js +34 -0
- package/umd/plugins/selection.umd.js.map +1 -0
- package/umd/plugins/server-side.umd.js +2 -0
- package/umd/plugins/server-side.umd.js.map +1 -0
- package/umd/plugins/tree.umd.js +11 -0
- package/umd/plugins/tree.umd.js.map +1 -0
- package/umd/plugins/undo-redo.umd.js +2 -0
- package/umd/plugins/undo-redo.umd.js.map +1 -0
- package/umd/plugins/visibility.umd.js +94 -0
- package/umd/plugins/visibility.umd.js.map +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../../../libs/grid/src/lib/core/plugin/base-plugin.ts","../../../../../../libs/grid/src/lib/plugins/multi-sort/multi-sort.ts","../../../../../../libs/grid/src/lib/plugins/multi-sort/MultiSortPlugin.ts"],"sourcesContent":["/**\r\n * Base Grid Plugin Class\r\n *\r\n * All plugins extend this abstract class.\r\n * Plugins are instantiated per-grid and manage their own state.\r\n */\r\n\r\nimport type { ColumnConfig, ColumnState, HeaderContentDefinition, ToolPanelDefinition } from '../types';\r\n\r\n// Forward declare to avoid circular imports\r\nexport interface GridElement {\r\n shadowRoot: ShadowRoot | null;\r\n rows: any[];\r\n columns: ColumnConfig[];\r\n gridConfig: any;\r\n requestRender(): void;\r\n requestAfterRender(): void;\r\n forceLayout(): Promise<void>;\r\n getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined;\r\n getPluginByName(name: string): BaseGridPlugin | undefined;\r\n dispatchEvent(event: Event): boolean;\r\n}\r\n\r\n/**\r\n * Keyboard modifier flags\r\n */\r\nexport interface KeyboardModifiers {\r\n ctrl?: boolean;\r\n shift?: boolean;\r\n alt?: boolean;\r\n meta?: boolean;\r\n}\r\n\r\n/**\r\n * Cell coordinates\r\n */\r\nexport interface CellCoords {\r\n row: number;\r\n col: number;\r\n}\r\n\r\n/**\r\n * Cell click event\r\n */\r\nexport interface CellClickEvent {\r\n rowIndex: number;\r\n colIndex: number;\r\n field: string;\r\n value: any;\r\n row: any;\r\n cellEl: HTMLElement;\r\n originalEvent: MouseEvent;\r\n}\r\n\r\n/**\r\n * Row click event\r\n */\r\nexport interface RowClickEvent {\r\n rowIndex: number;\r\n row: any;\r\n rowEl: HTMLElement;\r\n originalEvent: MouseEvent;\r\n}\r\n\r\n/**\r\n * Header click event\r\n */\r\nexport interface HeaderClickEvent {\r\n colIndex: number;\r\n field: string;\r\n column: ColumnConfig;\r\n headerEl: HTMLElement;\r\n originalEvent: MouseEvent;\r\n}\r\n\r\n/**\r\n * Scroll event\r\n */\r\nexport interface ScrollEvent {\r\n scrollTop: number;\r\n scrollLeft: number;\r\n scrollHeight: number;\r\n scrollWidth: number;\r\n clientHeight: number;\r\n clientWidth: number;\r\n originalEvent?: Event;\r\n}\r\n\r\n/**\r\n * Cell mouse event (for drag operations, selection, etc.)\r\n */\r\nexport interface CellMouseEvent {\r\n /** Event type: mousedown, mousemove, or mouseup */\r\n type: 'mousedown' | 'mousemove' | 'mouseup';\r\n /** Row index, undefined if not over a data cell */\r\n rowIndex?: number;\r\n /** Column index, undefined if not over a cell */\r\n colIndex?: number;\r\n /** Field name, undefined if not over a cell */\r\n field?: string;\r\n /** Cell value, undefined if not over a data cell */\r\n value?: unknown;\r\n /** Row data object, undefined if not over a data row */\r\n row?: unknown;\r\n /** Column configuration, undefined if not over a column */\r\n column?: ColumnConfig;\r\n /** The cell element, undefined if not over a cell */\r\n cellElement?: HTMLElement;\r\n /** The row element, undefined if not over a row */\r\n rowElement?: HTMLElement;\r\n /** Whether the event is over a header cell */\r\n isHeader: boolean;\r\n /** Cell coordinates if over a valid data cell */\r\n cell?: CellCoords;\r\n /** The original mouse event */\r\n originalEvent: MouseEvent;\r\n}\r\n\r\n/**\r\n * Context menu parameters\r\n */\r\nexport interface ContextMenuParams {\r\n x: number;\r\n y: number;\r\n rowIndex?: number;\r\n colIndex?: number;\r\n field?: string;\r\n value?: any;\r\n row?: any;\r\n column?: ColumnConfig;\r\n isHeader?: boolean;\r\n}\r\n\r\n/**\r\n * Context menu item\r\n */\r\nexport interface ContextMenuItem {\r\n id: string;\r\n label: string;\r\n icon?: string;\r\n disabled?: boolean;\r\n separator?: boolean;\r\n children?: ContextMenuItem[];\r\n action?: (params: ContextMenuParams) => void;\r\n}\r\n\r\n/**\r\n * Cell render context for plugin cell renderers.\r\n * Provides full context including position and editing state.\r\n *\r\n * Note: This differs from the core `CellRenderContext` in types.ts which is\r\n * simpler and used for column view renderers. This version provides additional\r\n * context needed by plugins that register custom cell renderers.\r\n */\r\nexport interface PluginCellRenderContext {\r\n /** The cell value */\r\n value: any;\r\n /** The field/column key */\r\n field: string;\r\n /** The row data object */\r\n row: any;\r\n /** Row index in the data array */\r\n rowIndex: number;\r\n /** Column index */\r\n colIndex: number;\r\n /** Column configuration */\r\n column: ColumnConfig;\r\n /** Whether the cell is currently in edit mode */\r\n isEditing: boolean;\r\n}\r\n\r\n/**\r\n * Header render context for plugin header renderers.\r\n */\r\nexport interface PluginHeaderRenderContext {\r\n /** Column configuration */\r\n column: ColumnConfig;\r\n /** Column index */\r\n colIndex: number;\r\n}\r\n\r\n/**\r\n * Cell renderer function type for plugins.\r\n */\r\nexport type CellRenderer = (ctx: PluginCellRenderContext) => string | HTMLElement;\r\n\r\n/**\r\n * Header renderer function type for plugins.\r\n */\r\nexport type HeaderRenderer = (ctx: PluginHeaderRenderContext) => string | HTMLElement;\r\n\r\n/**\r\n * Cell editor interface for plugins.\r\n */\r\nexport interface CellEditor {\r\n create(ctx: PluginCellRenderContext, commitFn: (value: any) => void, cancelFn: () => void): HTMLElement;\r\n getValue?(element: HTMLElement): any;\r\n focus?(element: HTMLElement): void;\r\n}\r\n\r\n/**\r\n * Abstract base class for all grid plugins.\r\n *\r\n * @template TConfig - Configuration type for the plugin\r\n */\r\nexport abstract class BaseGridPlugin<TConfig = unknown> {\r\n /** Unique plugin identifier (derived from class name by default) */\r\n abstract readonly name: string;\r\n\r\n /** Plugin version - override in subclass if needed */\r\n readonly version: string = '1.0.0';\r\n\r\n /** CSS styles to inject into the grid's shadow DOM */\r\n readonly styles?: string;\r\n\r\n /** Custom cell renderers keyed by type name */\r\n readonly cellRenderers?: Record<string, CellRenderer>;\r\n\r\n /** Custom header renderers keyed by type name */\r\n readonly headerRenderers?: Record<string, HeaderRenderer>;\r\n\r\n /** Custom cell editors keyed by type name */\r\n readonly cellEditors?: Record<string, CellEditor>;\r\n\r\n /** The grid instance this plugin is attached to */\r\n protected grid!: GridElement;\r\n\r\n /** Plugin configuration - merged with defaults in attach() */\r\n protected config!: TConfig;\r\n\r\n /** User-provided configuration from constructor */\r\n private readonly userConfig: Partial<TConfig>;\r\n\r\n /**\r\n * Default configuration - subclasses should override this getter.\r\n * Note: This must be a getter (not property initializer) for proper inheritance\r\n * since property initializers run after parent constructor.\r\n */\r\n protected get defaultConfig(): Partial<TConfig> {\r\n return {};\r\n }\r\n\r\n constructor(config: Partial<TConfig> = {}) {\r\n this.userConfig = config;\r\n }\r\n\r\n /**\r\n * Called when the plugin is attached to a grid.\r\n * Override to set up event listeners, initialize state, etc.\r\n */\r\n attach(grid: GridElement): void {\r\n this.grid = grid;\r\n // Merge config here (after subclass construction is complete)\r\n this.config = { ...this.defaultConfig, ...this.userConfig } as TConfig;\r\n }\r\n\r\n /**\r\n * Called when the plugin is detached from a grid.\r\n * Override to clean up event listeners, timers, etc.\r\n */\r\n detach(): void {\r\n // Override in subclass\r\n }\r\n\r\n /**\r\n * Get another plugin instance from the same grid.\r\n * Use for inter-plugin communication.\r\n */\r\n protected getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined {\r\n return this.grid?.getPlugin(PluginClass);\r\n }\r\n\r\n /**\r\n * Emit a custom event from the grid.\r\n */\r\n protected emit<T>(eventName: string, detail: T): void {\r\n this.grid?.dispatchEvent?.(new CustomEvent(eventName, { detail, bubbles: true }));\r\n }\r\n\r\n /**\r\n * Request a re-render of the grid.\r\n */\r\n protected requestRender(): void {\r\n this.grid?.requestRender?.();\r\n }\r\n\r\n /**\r\n * Request a lightweight style update without rebuilding DOM.\r\n * Use this instead of requestRender() when only CSS classes need updating.\r\n */\r\n protected requestAfterRender(): void {\r\n this.grid?.requestAfterRender?.();\r\n }\r\n\r\n /**\r\n * Get the current rows from the grid.\r\n */\r\n protected get rows(): any[] {\r\n return this.grid?.rows ?? [];\r\n }\r\n\r\n /**\r\n * Get the original unfiltered/unprocessed rows from the grid.\r\n * Use this when you need all source data regardless of active filters.\r\n */\r\n protected get sourceRows(): any[] {\r\n return (this.grid as any)?.sourceRows ?? [];\r\n }\r\n\r\n /**\r\n * Get the current columns from the grid.\r\n */\r\n protected get columns(): ColumnConfig[] {\r\n return this.grid?.columns ?? [];\r\n }\r\n\r\n /**\r\n * Get only visible columns from the grid (excludes hidden).\r\n * Use this for rendering that needs to match the grid template.\r\n */\r\n protected get visibleColumns(): ColumnConfig[] {\r\n return (this.grid as any)?.visibleColumns ?? [];\r\n }\r\n\r\n /**\r\n * Get the shadow root of the grid.\r\n */\r\n protected get shadowRoot(): ShadowRoot | null {\r\n return this.grid?.shadowRoot ?? null;\r\n }\r\n\r\n /**\r\n * Log a warning message.\r\n */\r\n protected warn(message: string): void {\r\n console.warn(`[tbw-grid:${this.name}] ${message}`);\r\n }\r\n\r\n // ===== Lifecycle Hooks (override as needed) =====\r\n\r\n /**\r\n * Transform rows before rendering.\r\n * Called during each render cycle before rows are rendered to the DOM.\r\n * Use this to filter, sort, or add computed properties to rows.\r\n *\r\n * @param rows - The current rows array (readonly to encourage returning a new array)\r\n * @returns The modified rows array to render\r\n *\r\n * @example\r\n * ```ts\r\n * processRows(rows: readonly any[]): any[] {\r\n * // Filter out hidden rows\r\n * return rows.filter(row => !row._hidden);\r\n * }\r\n * ```\r\n *\r\n * @example\r\n * ```ts\r\n * processRows(rows: readonly any[]): any[] {\r\n * // Add computed properties\r\n * return rows.map(row => ({\r\n * ...row,\r\n * _fullName: `${row.firstName} ${row.lastName}`\r\n * }));\r\n * }\r\n * ```\r\n */\r\n processRows?(rows: readonly any[]): any[];\r\n\r\n /**\r\n * Transform columns before rendering.\r\n * Called during each render cycle before column headers and cells are rendered.\r\n * Use this to add, remove, or modify column definitions.\r\n *\r\n * @param columns - The current columns array (readonly to encourage returning a new array)\r\n * @returns The modified columns array to render\r\n *\r\n * @example\r\n * ```ts\r\n * processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\r\n * // Add a selection checkbox column\r\n * return [\r\n * { field: '_select', header: '', width: 40 },\r\n * ...columns\r\n * ];\r\n * }\r\n * ```\r\n */\r\n processColumns?(columns: readonly ColumnConfig[]): ColumnConfig[];\r\n\r\n /**\r\n * Called before each render cycle begins.\r\n * Use this to prepare state or cache values needed during rendering.\r\n *\r\n * @example\r\n * ```ts\r\n * beforeRender(): void {\r\n * this.visibleRowCount = this.calculateVisibleRows();\r\n * }\r\n * ```\r\n */\r\n beforeRender?(): void;\r\n\r\n /**\r\n * Called after each render cycle completes.\r\n * Use this for DOM manipulation, adding event listeners to rendered elements,\r\n * or applying visual effects like selection highlights.\r\n *\r\n * @example\r\n * ```ts\r\n * afterRender(): void {\r\n * // Apply selection styling to rendered rows\r\n * const rows = this.shadowRoot?.querySelectorAll('.data-row');\r\n * rows?.forEach((row, i) => {\r\n * row.classList.toggle('selected', this.selectedRows.has(i));\r\n * });\r\n * }\r\n * ```\r\n */\r\n afterRender?(): void;\r\n\r\n /**\r\n * Render a custom row, bypassing the default row rendering.\r\n * Use this for special row types like group headers, detail rows, or footers.\r\n *\r\n * @param row - The row data object\r\n * @param rowEl - The row DOM element to render into\r\n * @param rowIndex - The index of the row in the data array\r\n * @returns `true` if the plugin handled rendering (prevents default), `false`/`void` for default rendering\r\n *\r\n * @example\r\n * ```ts\r\n * renderRow(row: any, rowEl: HTMLElement, rowIndex: number): boolean | void {\r\n * if (row._isGroupHeader) {\r\n * rowEl.innerHTML = `<div class=\"group-header\">${row._groupLabel}</div>`;\r\n * return true; // Handled - skip default rendering\r\n * }\r\n * // Return void to let default rendering proceed\r\n * }\r\n * ```\r\n */\r\n renderRow?(row: any, rowEl: HTMLElement, rowIndex: number): boolean | void;\r\n\r\n // ===== Interaction Hooks (override as needed) =====\r\n\r\n /**\r\n * Handle keyboard events on the grid.\r\n * Called when a key is pressed while the grid or a cell has focus.\r\n *\r\n * @param event - The native KeyboardEvent\r\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\r\n *\r\n * @example\r\n * ```ts\r\n * onKeyDown(event: KeyboardEvent): boolean | void {\r\n * // Handle Ctrl+A for select all\r\n * if (event.ctrlKey && event.key === 'a') {\r\n * this.selectAllRows();\r\n * return true; // Prevent default browser select-all\r\n * }\r\n * }\r\n * ```\r\n */\r\n onKeyDown?(event: KeyboardEvent): boolean | void;\r\n\r\n /**\r\n * Handle cell click events.\r\n * Called when a data cell is clicked (not headers).\r\n *\r\n * @param event - Cell click event with row/column context\r\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\r\n *\r\n * @example\r\n * ```ts\r\n * onCellClick(event: CellClickEvent): boolean | void {\r\n * if (event.field === '_select') {\r\n * this.toggleRowSelection(event.rowIndex);\r\n * return true; // Handled\r\n * }\r\n * }\r\n * ```\r\n */\r\n onCellClick?(event: CellClickEvent): boolean | void;\r\n\r\n /**\r\n * Handle row click events.\r\n * Called when any part of a data row is clicked.\r\n * Note: This is called in addition to onCellClick, not instead of.\r\n *\r\n * @param event - Row click event with row context\r\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\r\n *\r\n * @example\r\n * ```ts\r\n * onRowClick(event: RowClickEvent): boolean | void {\r\n * if (this.config.mode === 'row') {\r\n * this.selectRow(event.rowIndex, event.originalEvent);\r\n * return true;\r\n * }\r\n * }\r\n * ```\r\n */\r\n onRowClick?(event: RowClickEvent): boolean | void;\r\n\r\n /**\r\n * Handle header click events.\r\n * Called when a column header is clicked. Commonly used for sorting.\r\n *\r\n * @param event - Header click event with column context\r\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\r\n *\r\n * @example\r\n * ```ts\r\n * onHeaderClick(event: HeaderClickEvent): boolean | void {\r\n * if (event.column.sortable !== false) {\r\n * this.toggleSort(event.field);\r\n * return true;\r\n * }\r\n * }\r\n * ```\r\n */\r\n onHeaderClick?(event: HeaderClickEvent): boolean | void;\r\n\r\n /**\r\n * Handle scroll events on the grid viewport.\r\n * Called during scrolling. Note: This may be called frequently; debounce if needed.\r\n *\r\n * @param event - Scroll event with scroll position and viewport dimensions\r\n *\r\n * @example\r\n * ```ts\r\n * onScroll(event: ScrollEvent): void {\r\n * // Update sticky column positions\r\n * this.updateStickyPositions(event.scrollLeft);\r\n * }\r\n * ```\r\n */\r\n onScroll?(event: ScrollEvent): void;\r\n\r\n /**\r\n * Handle cell mousedown events.\r\n * Used for initiating drag operations like range selection or column resize.\r\n *\r\n * @param event - Mouse event with cell context\r\n * @returns `true` to indicate drag started (prevents text selection), `false`/`void` otherwise\r\n *\r\n * @example\r\n * ```ts\r\n * onCellMouseDown(event: CellMouseEvent): boolean | void {\r\n * if (event.rowIndex !== undefined && this.config.mode === 'range') {\r\n * this.startDragSelection(event.rowIndex, event.colIndex);\r\n * return true; // Prevent text selection\r\n * }\r\n * }\r\n * ```\r\n */\r\n onCellMouseDown?(event: CellMouseEvent): boolean | void;\r\n\r\n /**\r\n * Handle cell mousemove events during drag operations.\r\n * Only called when a drag is in progress (after mousedown returned true).\r\n *\r\n * @param event - Mouse event with current cell context\r\n * @returns `true` to continue handling the drag, `false`/`void` otherwise\r\n *\r\n * @example\r\n * ```ts\r\n * onCellMouseMove(event: CellMouseEvent): boolean | void {\r\n * if (this.isDragging && event.rowIndex !== undefined) {\r\n * this.extendSelection(event.rowIndex, event.colIndex);\r\n * return true;\r\n * }\r\n * }\r\n * ```\r\n */\r\n onCellMouseMove?(event: CellMouseEvent): boolean | void;\r\n\r\n /**\r\n * Handle cell mouseup events to end drag operations.\r\n *\r\n * @param event - Mouse event with final cell context\r\n * @returns `true` if drag was finalized, `false`/`void` otherwise\r\n *\r\n * @example\r\n * ```ts\r\n * onCellMouseUp(event: CellMouseEvent): boolean | void {\r\n * if (this.isDragging) {\r\n * this.finalizeDragSelection();\r\n * this.isDragging = false;\r\n * return true;\r\n * }\r\n * }\r\n * ```\r\n */\r\n onCellMouseUp?(event: CellMouseEvent): boolean | void;\r\n\r\n /**\r\n * Provide context menu items when right-clicking on the grid.\r\n * Multiple plugins can contribute items; they are merged into a single menu.\r\n *\r\n * @param params - Context about where the menu was triggered (row, column, etc.)\r\n * @returns Array of menu items to display\r\n *\r\n * @example\r\n * ```ts\r\n * getContextMenuItems(params: ContextMenuParams): ContextMenuItem[] {\r\n * if (params.isHeader) {\r\n * return [\r\n * { id: 'sort-asc', label: 'Sort Ascending', action: () => this.sortAsc(params.field) },\r\n * { id: 'sort-desc', label: 'Sort Descending', action: () => this.sortDesc(params.field) },\r\n * ];\r\n * }\r\n * return [\r\n * { id: 'copy', label: 'Copy Cell', action: () => this.copyCell(params) },\r\n * ];\r\n * }\r\n * ```\r\n */\r\n getContextMenuItems?(params: ContextMenuParams): ContextMenuItem[];\r\n\r\n // ===== Column State Hooks (override as needed) =====\r\n\r\n /**\r\n * Contribute plugin-specific state for a column.\r\n * Called by the grid when collecting column state for serialization.\r\n * Plugins can add their own properties to the column state.\r\n *\r\n * @param field - The field name of the column\r\n * @returns Partial column state with plugin-specific properties, or undefined if no state to contribute\r\n *\r\n * @example\r\n * ```ts\r\n * getColumnState(field: string): Partial<ColumnState> | undefined {\r\n * const filterModel = this.filterModels.get(field);\r\n * if (filterModel) {\r\n * // Uses module augmentation to add filter property to ColumnState\r\n * return { filter: filterModel } as Partial<ColumnState>;\r\n * }\r\n * return undefined;\r\n * }\r\n * ```\r\n */\r\n getColumnState?(field: string): Partial<ColumnState> | undefined;\r\n\r\n /**\r\n * Apply plugin-specific state to a column.\r\n * Called by the grid when restoring column state from serialized data.\r\n * Plugins should restore their internal state based on the provided state.\r\n *\r\n * @param field - The field name of the column\r\n * @param state - The column state to apply (may contain plugin-specific properties)\r\n *\r\n * @example\r\n * ```ts\r\n * applyColumnState(field: string, state: ColumnState): void {\r\n * // Check for filter property added via module augmentation\r\n * const filter = (state as any).filter;\r\n * if (filter) {\r\n * this.filterModels.set(field, filter);\r\n * this.applyFilter();\r\n * }\r\n * }\r\n * ```\r\n */\r\n applyColumnState?(field: string, state: ColumnState): void;\r\n\r\n // ===== Shell Integration Hooks (override as needed) =====\r\n\r\n /**\r\n * Register a tool panel for this plugin.\r\n * Return undefined if plugin has no tool panel.\r\n * The shell will create a toolbar toggle button and render the panel content\r\n * when the user opens the panel.\r\n *\r\n * @returns Tool panel definition, or undefined if plugin has no panel\r\n *\r\n * @example\r\n * ```ts\r\n * getToolPanel(): ToolPanelDefinition | undefined {\r\n * return {\r\n * id: 'columns',\r\n * title: 'Columns',\r\n * icon: '☰',\r\n * tooltip: 'Show/hide columns',\r\n * order: 10,\r\n * render: (container) => {\r\n * this.renderColumnList(container);\r\n * return () => this.cleanup();\r\n * },\r\n * };\r\n * }\r\n * ```\r\n */\r\n getToolPanel?(): ToolPanelDefinition | undefined;\r\n\r\n /**\r\n * Register content for the shell header center section.\r\n * Return undefined if plugin has no header content.\r\n * Examples: search input, selection summary, status indicators.\r\n *\r\n * @returns Header content definition, or undefined if plugin has no header content\r\n *\r\n * @example\r\n * ```ts\r\n * getHeaderContent(): HeaderContentDefinition | undefined {\r\n * return {\r\n * id: 'quick-filter',\r\n * order: 10,\r\n * render: (container) => {\r\n * const input = document.createElement('input');\r\n * input.type = 'text';\r\n * input.placeholder = 'Search...';\r\n * input.addEventListener('input', this.handleInput);\r\n * container.appendChild(input);\r\n * return () => input.removeEventListener('input', this.handleInput);\r\n * },\r\n * };\r\n * }\r\n * ```\r\n */\r\n getHeaderContent?(): HeaderContentDefinition | undefined;\r\n}\r\n","/**\n * Multi-Sort Core Logic\n *\n * Pure functions for multi-column sorting operations.\n */\n\nimport type { ColumnConfig } from '../../core/types';\nimport type { SortModel } from './types';\n\n/**\n * Apply multiple sort columns to a row array.\n * Sorts are applied in order - first sort has highest priority.\n *\n * @param rows - Array of row objects to sort\n * @param sorts - Ordered array of sort configurations\n * @param columns - Column configurations (for custom comparators)\n * @returns New sorted array (does not mutate original)\n */\nexport function applySorts<TRow = unknown>(rows: TRow[], sorts: SortModel[], columns: ColumnConfig<TRow>[]): TRow[] {\n if (!sorts.length) return [...rows];\n\n return [...rows].sort((a, b) => {\n for (const sort of sorts) {\n const col = columns.find((c) => c.field === sort.field);\n const comparator = col?.sortComparator ?? defaultComparator;\n const aVal = (a as Record<string, unknown>)[sort.field];\n const bVal = (b as Record<string, unknown>)[sort.field];\n const result = comparator(aVal, bVal, a, b);\n if (result !== 0) {\n return sort.direction === 'asc' ? result : -result;\n }\n }\n return 0;\n });\n}\n\n/**\n * Default comparator for sorting values.\n * Handles nulls, numbers, dates, and strings.\n *\n * @param a - First value\n * @param b - Second value\n * @returns Comparison result (-1, 0, 1)\n */\nexport function defaultComparator(a: unknown, b: unknown): number {\n // Handle nulls/undefined - push to end\n if (a == null && b == null) return 0;\n if (a == null) return 1;\n if (b == null) return -1;\n\n // Type-aware comparison\n if (typeof a === 'number' && typeof b === 'number') {\n return a - b;\n }\n\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() - b.getTime();\n }\n\n // Boolean comparison\n if (typeof a === 'boolean' && typeof b === 'boolean') {\n return a === b ? 0 : a ? -1 : 1;\n }\n\n // String comparison (fallback)\n return String(a).localeCompare(String(b));\n}\n\n/**\n * Toggle sort state for a field.\n * With shift key: adds/toggles in multi-sort list\n * Without shift key: replaces entire sort with single column\n *\n * @param current - Current sort model\n * @param field - Field to toggle\n * @param shiftKey - Whether shift key is held (multi-sort mode)\n * @param maxColumns - Maximum columns allowed in sort\n * @returns New sort model\n */\nexport function toggleSort(current: SortModel[], field: string, shiftKey: boolean, maxColumns: number): SortModel[] {\n const existing = current.find((s) => s.field === field);\n\n if (shiftKey) {\n // Multi-sort: add/toggle in list\n if (existing) {\n if (existing.direction === 'asc') {\n // Flip to descending\n return current.map((s) => (s.field === field ? { ...s, direction: 'desc' as const } : s));\n } else {\n // Remove from sort\n return current.filter((s) => s.field !== field);\n }\n } else if (current.length < maxColumns) {\n // Add new sort column\n return [...current, { field, direction: 'asc' as const }];\n }\n // Max columns reached, return unchanged\n return current;\n } else {\n // Single sort: replace all\n if (existing?.direction === 'asc') {\n return [{ field, direction: 'desc' }];\n } else if (existing?.direction === 'desc') {\n return [];\n }\n return [{ field, direction: 'asc' }];\n }\n}\n\n/**\n * Get the sort index (1-based) for a field in the sort model.\n * Returns undefined if the field is not in the sort model.\n *\n * @param sortModel - Current sort model\n * @param field - Field to check\n * @returns 1-based index or undefined\n */\nexport function getSortIndex(sortModel: SortModel[], field: string): number | undefined {\n const index = sortModel.findIndex((s) => s.field === field);\n return index >= 0 ? index + 1 : undefined;\n}\n\n/**\n * Get the sort direction for a field in the sort model.\n *\n * @param sortModel - Current sort model\n * @param field - Field to check\n * @returns Sort direction or undefined if not sorted\n */\nexport function getSortDirection(sortModel: SortModel[], field: string): 'asc' | 'desc' | undefined {\n return sortModel.find((s) => s.field === field)?.direction;\n}\n","/**\r\n * Multi-Sort Plugin (Class-based)\r\n *\r\n * Provides multi-column sorting capabilities for tbw-grid.\r\n * Supports shift+click for adding secondary sort columns.\r\n */\r\n\r\nimport { BaseGridPlugin, HeaderClickEvent } from '../../core/plugin/base-plugin';\r\nimport type { ColumnState } from '../../core/types';\r\nimport { applySorts, getSortDirection, getSortIndex, toggleSort } from './multi-sort';\r\nimport type { MultiSortConfig, SortModel } from './types';\r\n\r\n/**\r\n * Multi-Sort Plugin for tbw-grid\r\n *\r\n * @example\r\n * ```ts\r\n * new MultiSortPlugin({ maxSortColumns: 3, showSortIndex: true })\r\n * ```\r\n */\r\nexport class MultiSortPlugin extends BaseGridPlugin<MultiSortConfig> {\r\n readonly name = 'multiSort';\r\n override readonly version = '1.0.0';\r\n\r\n protected override get defaultConfig(): Partial<MultiSortConfig> {\r\n return {\r\n enabled: true,\r\n maxSortColumns: 3,\r\n showSortIndex: true,\r\n };\r\n }\r\n\r\n // ===== Internal State =====\r\n private sortModel: SortModel[] = [];\r\n\r\n // ===== Lifecycle =====\r\n\r\n override detach(): void {\r\n this.sortModel = [];\r\n }\r\n\r\n // ===== Hooks =====\r\n\r\n override processRows(rows: readonly unknown[]): unknown[] {\r\n if (this.sortModel.length === 0) {\r\n return [...rows];\r\n }\r\n return applySorts([...rows], this.sortModel, [...this.columns]);\r\n }\r\n\r\n override onHeaderClick(event: HeaderClickEvent): boolean {\r\n const column = this.columns.find((c) => c.field === event.field);\r\n if (!column?.sortable) return false;\r\n\r\n const shiftKey = event.originalEvent.shiftKey;\r\n const maxColumns = this.config.maxSortColumns ?? 3;\r\n\r\n this.sortModel = toggleSort(this.sortModel, event.field, shiftKey, maxColumns);\r\n\r\n this.emit('sort-change', { sortModel: [...this.sortModel] });\r\n this.requestRender();\r\n\r\n return true;\r\n }\r\n\r\n override afterRender(): void {\r\n const shadowRoot = this.shadowRoot;\r\n if (!shadowRoot) return;\r\n\r\n const showIndex = this.config.showSortIndex !== false;\r\n\r\n // Update all sortable header cells with sort indicators\r\n const headerCells = shadowRoot.querySelectorAll('.header-row .cell[data-field]');\r\n headerCells.forEach((cell) => {\r\n const field = cell.getAttribute('data-field');\r\n if (!field) return;\r\n\r\n const sortIndex = getSortIndex(this.sortModel, field);\r\n const sortDir = getSortDirection(this.sortModel, field);\r\n\r\n // Remove existing sort index badge (always clean up)\r\n const existingBadge = cell.querySelector('.sort-index');\r\n existingBadge?.remove();\r\n\r\n if (sortDir) {\r\n // Column is sorted - remove base indicator and add our own\r\n const existingIndicator = cell.querySelector('[part~=\"sort-indicator\"], .sort-indicator');\r\n existingIndicator?.remove();\r\n\r\n cell.setAttribute('data-sort', sortDir);\r\n\r\n // Add sort arrow indicator\r\n const indicator = document.createElement('span');\r\n indicator.className = 'sort-indicator';\r\n indicator.style.marginLeft = '4px';\r\n indicator.style.opacity = '0.8';\r\n indicator.textContent = sortDir === 'asc' ? '▲' : '▼';\r\n cell.appendChild(indicator);\r\n\r\n // Add sort index badge if multiple columns sorted and showSortIndex is enabled\r\n if (showIndex && this.sortModel.length > 1 && sortIndex !== undefined) {\r\n const badge = document.createElement('span');\r\n badge.className = 'sort-index';\r\n badge.textContent = String(sortIndex);\r\n cell.appendChild(badge);\r\n }\r\n } else {\r\n cell.removeAttribute('data-sort');\r\n // For unsorted columns, leave the base indicator (⇅) alone\r\n }\r\n });\r\n }\r\n\r\n // ===== Public API =====\r\n\r\n /**\r\n * Get the current sort model.\r\n * @returns Copy of the current sort model\r\n */\r\n getSortModel(): SortModel[] {\r\n return [...this.sortModel];\r\n }\r\n\r\n /**\r\n * Set the sort model programmatically.\r\n * @param model - New sort model to apply\r\n */\r\n setSortModel(model: SortModel[]): void {\r\n this.sortModel = [...model];\r\n this.emit('sort-change', { sortModel: [...model] });\r\n this.requestRender();\r\n }\r\n\r\n /**\r\n * Clear all sorting.\r\n */\r\n clearSort(): void {\r\n this.sortModel = [];\r\n this.emit('sort-change', { sortModel: [] });\r\n this.requestRender();\r\n }\r\n\r\n /**\r\n * Get the sort index (1-based) for a specific field.\r\n * @param field - Field to check\r\n * @returns 1-based index or undefined if not sorted\r\n */\r\n getSortIndex(field: string): number | undefined {\r\n return getSortIndex(this.sortModel, field);\r\n }\r\n\r\n /**\r\n * Get the sort direction for a specific field.\r\n * @param field - Field to check\r\n * @returns Sort direction or undefined if not sorted\r\n */\r\n getSortDirection(field: string): 'asc' | 'desc' | undefined {\r\n return getSortDirection(this.sortModel, field);\r\n }\r\n\r\n // ===== Column State Hooks =====\r\n\r\n /**\r\n * Return sort state for a column if it's in the sort model.\r\n */\r\n override getColumnState(field: string): Partial<ColumnState> | undefined {\r\n const index = this.sortModel.findIndex((s) => s.field === field);\r\n if (index === -1) return undefined;\r\n\r\n const sortEntry = this.sortModel[index];\r\n return {\r\n sort: {\r\n direction: sortEntry.direction,\r\n priority: index,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * Apply sort state from column state.\r\n * Rebuilds the sort model from all column states.\r\n */\r\n override applyColumnState(field: string, state: ColumnState): void {\r\n // Only process if the column has sort state\r\n if (!state.sort) {\r\n // Remove this field from sortModel if it exists\r\n this.sortModel = this.sortModel.filter((s) => s.field !== field);\r\n return;\r\n }\r\n\r\n // Find existing entry or add new one\r\n const existingIndex = this.sortModel.findIndex((s) => s.field === field);\r\n const newEntry: SortModel = {\r\n field,\r\n direction: state.sort.direction,\r\n };\r\n\r\n if (existingIndex !== -1) {\r\n // Update existing entry\r\n this.sortModel[existingIndex] = newEntry;\r\n } else {\r\n // Add at the correct priority position\r\n this.sortModel.splice(state.sort.priority, 0, newEntry);\r\n }\r\n\r\n // Re-sort the model by priority to ensure correct order\r\n // This is handled after all columns are processed, but we maintain order here\r\n }\r\n\r\n // ===== Styles =====\r\n\r\n override readonly styles = `\r\n .header-cell[data-sort=\"asc\"]::after {\r\n content: '↑';\r\n margin-left: 4px;\r\n opacity: 0.8;\r\n }\r\n .header-cell[data-sort=\"desc\"]::after {\r\n content: '↓';\r\n margin-left: 4px;\r\n opacity: 0.8;\r\n }\r\n .sort-index {\r\n font-size: 10px;\r\n background: var(--tbw-multi-sort-badge-bg, var(--tbw-color-panel-bg));\r\n color: var(--tbw-multi-sort-badge-color, var(--tbw-color-fg));\r\n border-radius: 50%;\r\n width: 14px;\r\n height: 14px;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n margin-left: 2px;\r\n font-weight: 600;\r\n }\r\n `;\r\n}\r\n"],"names":["BaseGridPlugin","config","grid","PluginClass","eventName","detail","message","applySorts","rows","sorts","columns","a","b","sort","comparator","c","defaultComparator","aVal","bVal","result","toggleSort","current","field","shiftKey","maxColumns","existing","s","getSortIndex","sortModel","index","getSortDirection","MultiSortPlugin","event","shadowRoot","showIndex","cell","sortIndex","sortDir","indicator","badge","model","state","existingIndex","newEntry"],"mappings":"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"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
class g {
|
|
2
|
+
/** Plugin version - override in subclass if needed */
|
|
3
|
+
version = "1.0.0";
|
|
4
|
+
/** CSS styles to inject into the grid's shadow DOM */
|
|
5
|
+
styles;
|
|
6
|
+
/** Custom cell renderers keyed by type name */
|
|
7
|
+
cellRenderers;
|
|
8
|
+
/** Custom header renderers keyed by type name */
|
|
9
|
+
headerRenderers;
|
|
10
|
+
/** Custom cell editors keyed by type name */
|
|
11
|
+
cellEditors;
|
|
12
|
+
/** The grid instance this plugin is attached to */
|
|
13
|
+
grid;
|
|
14
|
+
/** Plugin configuration - merged with defaults in attach() */
|
|
15
|
+
config;
|
|
16
|
+
/** User-provided configuration from constructor */
|
|
17
|
+
userConfig;
|
|
18
|
+
/**
|
|
19
|
+
* Default configuration - subclasses should override this getter.
|
|
20
|
+
* Note: This must be a getter (not property initializer) for proper inheritance
|
|
21
|
+
* since property initializers run after parent constructor.
|
|
22
|
+
*/
|
|
23
|
+
get defaultConfig() {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
constructor(t = {}) {
|
|
27
|
+
this.userConfig = t;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Called when the plugin is attached to a grid.
|
|
31
|
+
* Override to set up event listeners, initialize state, etc.
|
|
32
|
+
*/
|
|
33
|
+
attach(t) {
|
|
34
|
+
this.grid = t, this.config = { ...this.defaultConfig, ...this.userConfig };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Called when the plugin is detached from a grid.
|
|
38
|
+
* Override to clean up event listeners, timers, etc.
|
|
39
|
+
*/
|
|
40
|
+
detach() {
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get another plugin instance from the same grid.
|
|
44
|
+
* Use for inter-plugin communication.
|
|
45
|
+
*/
|
|
46
|
+
getPlugin(t) {
|
|
47
|
+
return this.grid?.getPlugin(t);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Emit a custom event from the grid.
|
|
51
|
+
*/
|
|
52
|
+
emit(t, e) {
|
|
53
|
+
this.grid?.dispatchEvent?.(new CustomEvent(t, { detail: e, bubbles: !0 }));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Request a re-render of the grid.
|
|
57
|
+
*/
|
|
58
|
+
requestRender() {
|
|
59
|
+
this.grid?.requestRender?.();
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Request a lightweight style update without rebuilding DOM.
|
|
63
|
+
* Use this instead of requestRender() when only CSS classes need updating.
|
|
64
|
+
*/
|
|
65
|
+
requestAfterRender() {
|
|
66
|
+
this.grid?.requestAfterRender?.();
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get the current rows from the grid.
|
|
70
|
+
*/
|
|
71
|
+
get rows() {
|
|
72
|
+
return this.grid?.rows ?? [];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get the original unfiltered/unprocessed rows from the grid.
|
|
76
|
+
* Use this when you need all source data regardless of active filters.
|
|
77
|
+
*/
|
|
78
|
+
get sourceRows() {
|
|
79
|
+
return this.grid?.sourceRows ?? [];
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Get the current columns from the grid.
|
|
83
|
+
*/
|
|
84
|
+
get columns() {
|
|
85
|
+
return this.grid?.columns ?? [];
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get only visible columns from the grid (excludes hidden).
|
|
89
|
+
* Use this for rendering that needs to match the grid template.
|
|
90
|
+
*/
|
|
91
|
+
get visibleColumns() {
|
|
92
|
+
return this.grid?.visibleColumns ?? [];
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get the shadow root of the grid.
|
|
96
|
+
*/
|
|
97
|
+
get shadowRoot() {
|
|
98
|
+
return this.grid?.shadowRoot ?? null;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Log a warning message.
|
|
102
|
+
*/
|
|
103
|
+
warn(t) {
|
|
104
|
+
console.warn(`[tbw-grid:${this.name}] ${t}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function y(s) {
|
|
108
|
+
return s.filter((t) => t.sticky === "left");
|
|
109
|
+
}
|
|
110
|
+
function m(s) {
|
|
111
|
+
return s.filter((t) => t.sticky === "right");
|
|
112
|
+
}
|
|
113
|
+
function u(s) {
|
|
114
|
+
return s.some((t) => t.sticky === "left" || t.sticky === "right");
|
|
115
|
+
}
|
|
116
|
+
function a(s, t) {
|
|
117
|
+
const e = s.shadowRoot;
|
|
118
|
+
if (!e) return;
|
|
119
|
+
const i = Array.from(e.querySelectorAll(".header-row .cell"));
|
|
120
|
+
if (!i.length) return;
|
|
121
|
+
const c = /* @__PURE__ */ new Map();
|
|
122
|
+
t.forEach((r, o) => {
|
|
123
|
+
r.field && c.set(r.field, o);
|
|
124
|
+
});
|
|
125
|
+
let f = 0;
|
|
126
|
+
for (const r of t)
|
|
127
|
+
if (r.sticky === "left") {
|
|
128
|
+
const o = c.get(r.field), n = i.find((l) => l.getAttribute("data-field") === r.field);
|
|
129
|
+
n && (n.classList.add("sticky-left"), n.style.left = f + "px", o !== void 0 && e.querySelectorAll(`.data-grid-row .cell[data-col="${o}"]`).forEach((l) => {
|
|
130
|
+
l.classList.add("sticky-left"), l.style.left = f + "px";
|
|
131
|
+
}), f += n.offsetWidth);
|
|
132
|
+
}
|
|
133
|
+
let d = 0;
|
|
134
|
+
for (const r of [...t].reverse())
|
|
135
|
+
if (r.sticky === "right") {
|
|
136
|
+
const o = c.get(r.field), n = i.find((l) => l.getAttribute("data-field") === r.field);
|
|
137
|
+
n && (n.classList.add("sticky-right"), n.style.right = d + "px", o !== void 0 && e.querySelectorAll(`.data-grid-row .cell[data-col="${o}"]`).forEach((l) => {
|
|
138
|
+
l.classList.add("sticky-right"), l.style.right = d + "px";
|
|
139
|
+
}), d += n.offsetWidth);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function h(s) {
|
|
143
|
+
const t = s.shadowRoot;
|
|
144
|
+
if (!t) return;
|
|
145
|
+
t.querySelectorAll(".sticky-left, .sticky-right").forEach((i) => {
|
|
146
|
+
i.classList.remove("sticky-left", "sticky-right"), i.style.left = "", i.style.right = "";
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
class p extends g {
|
|
150
|
+
name = "pinnedColumns";
|
|
151
|
+
version = "1.0.0";
|
|
152
|
+
get defaultConfig() {
|
|
153
|
+
return {
|
|
154
|
+
enabled: !0
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
// ===== Internal State =====
|
|
158
|
+
isApplied = !1;
|
|
159
|
+
leftOffsets = /* @__PURE__ */ new Map();
|
|
160
|
+
rightOffsets = /* @__PURE__ */ new Map();
|
|
161
|
+
// ===== Lifecycle =====
|
|
162
|
+
detach() {
|
|
163
|
+
this.leftOffsets.clear(), this.rightOffsets.clear(), this.isApplied = !1;
|
|
164
|
+
}
|
|
165
|
+
// ===== Detection =====
|
|
166
|
+
/**
|
|
167
|
+
* Auto-detect sticky columns from column configuration.
|
|
168
|
+
*/
|
|
169
|
+
static detect(t, e) {
|
|
170
|
+
const i = e?.columns;
|
|
171
|
+
return Array.isArray(i) ? u(i) : !1;
|
|
172
|
+
}
|
|
173
|
+
// ===== Hooks =====
|
|
174
|
+
processColumns(t) {
|
|
175
|
+
return this.config.enabled ? (this.isApplied = u([...t]), [...t]) : (this.isApplied = !1, [...t]);
|
|
176
|
+
}
|
|
177
|
+
afterRender() {
|
|
178
|
+
if (!this.config.enabled || !this.isApplied)
|
|
179
|
+
return;
|
|
180
|
+
const t = this.grid, e = [...this.columns];
|
|
181
|
+
if (!u(e)) {
|
|
182
|
+
h(t), this.isApplied = !1;
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
queueMicrotask(() => {
|
|
186
|
+
a(t, e);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
// ===== Public API =====
|
|
190
|
+
/**
|
|
191
|
+
* Re-apply sticky offsets (e.g., after column resize).
|
|
192
|
+
*/
|
|
193
|
+
refreshStickyOffsets() {
|
|
194
|
+
const t = [...this.columns];
|
|
195
|
+
a(this.grid, t);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Get columns pinned to the left.
|
|
199
|
+
*/
|
|
200
|
+
getLeftPinnedColumns() {
|
|
201
|
+
const t = [...this.columns];
|
|
202
|
+
return y(t);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Get columns pinned to the right.
|
|
206
|
+
*/
|
|
207
|
+
getRightPinnedColumns() {
|
|
208
|
+
const t = [...this.columns];
|
|
209
|
+
return m(t);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Clear all sticky positioning.
|
|
213
|
+
*/
|
|
214
|
+
clearStickyPositions() {
|
|
215
|
+
h(this.grid);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
export {
|
|
219
|
+
p as PinnedColumnsPlugin
|
|
220
|
+
};
|
|
221
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../../../libs/grid/src/lib/core/plugin/base-plugin.ts","../../../../../../libs/grid/src/lib/plugins/pinned-columns/pinned-columns.ts","../../../../../../libs/grid/src/lib/plugins/pinned-columns/PinnedColumnsPlugin.ts"],"sourcesContent":["/**\r\n * Base Grid Plugin Class\r\n *\r\n * All plugins extend this abstract class.\r\n * Plugins are instantiated per-grid and manage their own state.\r\n */\r\n\r\nimport type { ColumnConfig, ColumnState, HeaderContentDefinition, ToolPanelDefinition } from '../types';\r\n\r\n// Forward declare to avoid circular imports\r\nexport interface GridElement {\r\n shadowRoot: ShadowRoot | null;\r\n rows: any[];\r\n columns: ColumnConfig[];\r\n gridConfig: any;\r\n requestRender(): void;\r\n requestAfterRender(): void;\r\n forceLayout(): Promise<void>;\r\n getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined;\r\n getPluginByName(name: string): BaseGridPlugin | undefined;\r\n dispatchEvent(event: Event): boolean;\r\n}\r\n\r\n/**\r\n * Keyboard modifier flags\r\n */\r\nexport interface KeyboardModifiers {\r\n ctrl?: boolean;\r\n shift?: boolean;\r\n alt?: boolean;\r\n meta?: boolean;\r\n}\r\n\r\n/**\r\n * Cell coordinates\r\n */\r\nexport interface CellCoords {\r\n row: number;\r\n col: number;\r\n}\r\n\r\n/**\r\n * Cell click event\r\n */\r\nexport interface CellClickEvent {\r\n rowIndex: number;\r\n colIndex: number;\r\n field: string;\r\n value: any;\r\n row: any;\r\n cellEl: HTMLElement;\r\n originalEvent: MouseEvent;\r\n}\r\n\r\n/**\r\n * Row click event\r\n */\r\nexport interface RowClickEvent {\r\n rowIndex: number;\r\n row: any;\r\n rowEl: HTMLElement;\r\n originalEvent: MouseEvent;\r\n}\r\n\r\n/**\r\n * Header click event\r\n */\r\nexport interface HeaderClickEvent {\r\n colIndex: number;\r\n field: string;\r\n column: ColumnConfig;\r\n headerEl: HTMLElement;\r\n originalEvent: MouseEvent;\r\n}\r\n\r\n/**\r\n * Scroll event\r\n */\r\nexport interface ScrollEvent {\r\n scrollTop: number;\r\n scrollLeft: number;\r\n scrollHeight: number;\r\n scrollWidth: number;\r\n clientHeight: number;\r\n clientWidth: number;\r\n originalEvent?: Event;\r\n}\r\n\r\n/**\r\n * Cell mouse event (for drag operations, selection, etc.)\r\n */\r\nexport interface CellMouseEvent {\r\n /** Event type: mousedown, mousemove, or mouseup */\r\n type: 'mousedown' | 'mousemove' | 'mouseup';\r\n /** Row index, undefined if not over a data cell */\r\n rowIndex?: number;\r\n /** Column index, undefined if not over a cell */\r\n colIndex?: number;\r\n /** Field name, undefined if not over a cell */\r\n field?: string;\r\n /** Cell value, undefined if not over a data cell */\r\n value?: unknown;\r\n /** Row data object, undefined if not over a data row */\r\n row?: unknown;\r\n /** Column configuration, undefined if not over a column */\r\n column?: ColumnConfig;\r\n /** The cell element, undefined if not over a cell */\r\n cellElement?: HTMLElement;\r\n /** The row element, undefined if not over a row */\r\n rowElement?: HTMLElement;\r\n /** Whether the event is over a header cell */\r\n isHeader: boolean;\r\n /** Cell coordinates if over a valid data cell */\r\n cell?: CellCoords;\r\n /** The original mouse event */\r\n originalEvent: MouseEvent;\r\n}\r\n\r\n/**\r\n * Context menu parameters\r\n */\r\nexport interface ContextMenuParams {\r\n x: number;\r\n y: number;\r\n rowIndex?: number;\r\n colIndex?: number;\r\n field?: string;\r\n value?: any;\r\n row?: any;\r\n column?: ColumnConfig;\r\n isHeader?: boolean;\r\n}\r\n\r\n/**\r\n * Context menu item\r\n */\r\nexport interface ContextMenuItem {\r\n id: string;\r\n label: string;\r\n icon?: string;\r\n disabled?: boolean;\r\n separator?: boolean;\r\n children?: ContextMenuItem[];\r\n action?: (params: ContextMenuParams) => void;\r\n}\r\n\r\n/**\r\n * Cell render context for plugin cell renderers.\r\n * Provides full context including position and editing state.\r\n *\r\n * Note: This differs from the core `CellRenderContext` in types.ts which is\r\n * simpler and used for column view renderers. This version provides additional\r\n * context needed by plugins that register custom cell renderers.\r\n */\r\nexport interface PluginCellRenderContext {\r\n /** The cell value */\r\n value: any;\r\n /** The field/column key */\r\n field: string;\r\n /** The row data object */\r\n row: any;\r\n /** Row index in the data array */\r\n rowIndex: number;\r\n /** Column index */\r\n colIndex: number;\r\n /** Column configuration */\r\n column: ColumnConfig;\r\n /** Whether the cell is currently in edit mode */\r\n isEditing: boolean;\r\n}\r\n\r\n/**\r\n * Header render context for plugin header renderers.\r\n */\r\nexport interface PluginHeaderRenderContext {\r\n /** Column configuration */\r\n column: ColumnConfig;\r\n /** Column index */\r\n colIndex: number;\r\n}\r\n\r\n/**\r\n * Cell renderer function type for plugins.\r\n */\r\nexport type CellRenderer = (ctx: PluginCellRenderContext) => string | HTMLElement;\r\n\r\n/**\r\n * Header renderer function type for plugins.\r\n */\r\nexport type HeaderRenderer = (ctx: PluginHeaderRenderContext) => string | HTMLElement;\r\n\r\n/**\r\n * Cell editor interface for plugins.\r\n */\r\nexport interface CellEditor {\r\n create(ctx: PluginCellRenderContext, commitFn: (value: any) => void, cancelFn: () => void): HTMLElement;\r\n getValue?(element: HTMLElement): any;\r\n focus?(element: HTMLElement): void;\r\n}\r\n\r\n/**\r\n * Abstract base class for all grid plugins.\r\n *\r\n * @template TConfig - Configuration type for the plugin\r\n */\r\nexport abstract class BaseGridPlugin<TConfig = unknown> {\r\n /** Unique plugin identifier (derived from class name by default) */\r\n abstract readonly name: string;\r\n\r\n /** Plugin version - override in subclass if needed */\r\n readonly version: string = '1.0.0';\r\n\r\n /** CSS styles to inject into the grid's shadow DOM */\r\n readonly styles?: string;\r\n\r\n /** Custom cell renderers keyed by type name */\r\n readonly cellRenderers?: Record<string, CellRenderer>;\r\n\r\n /** Custom header renderers keyed by type name */\r\n readonly headerRenderers?: Record<string, HeaderRenderer>;\r\n\r\n /** Custom cell editors keyed by type name */\r\n readonly cellEditors?: Record<string, CellEditor>;\r\n\r\n /** The grid instance this plugin is attached to */\r\n protected grid!: GridElement;\r\n\r\n /** Plugin configuration - merged with defaults in attach() */\r\n protected config!: TConfig;\r\n\r\n /** User-provided configuration from constructor */\r\n private readonly userConfig: Partial<TConfig>;\r\n\r\n /**\r\n * Default configuration - subclasses should override this getter.\r\n * Note: This must be a getter (not property initializer) for proper inheritance\r\n * since property initializers run after parent constructor.\r\n */\r\n protected get defaultConfig(): Partial<TConfig> {\r\n return {};\r\n }\r\n\r\n constructor(config: Partial<TConfig> = {}) {\r\n this.userConfig = config;\r\n }\r\n\r\n /**\r\n * Called when the plugin is attached to a grid.\r\n * Override to set up event listeners, initialize state, etc.\r\n */\r\n attach(grid: GridElement): void {\r\n this.grid = grid;\r\n // Merge config here (after subclass construction is complete)\r\n this.config = { ...this.defaultConfig, ...this.userConfig } as TConfig;\r\n }\r\n\r\n /**\r\n * Called when the plugin is detached from a grid.\r\n * Override to clean up event listeners, timers, etc.\r\n */\r\n detach(): void {\r\n // Override in subclass\r\n }\r\n\r\n /**\r\n * Get another plugin instance from the same grid.\r\n * Use for inter-plugin communication.\r\n */\r\n protected getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined {\r\n return this.grid?.getPlugin(PluginClass);\r\n }\r\n\r\n /**\r\n * Emit a custom event from the grid.\r\n */\r\n protected emit<T>(eventName: string, detail: T): void {\r\n this.grid?.dispatchEvent?.(new CustomEvent(eventName, { detail, bubbles: true }));\r\n }\r\n\r\n /**\r\n * Request a re-render of the grid.\r\n */\r\n protected requestRender(): void {\r\n this.grid?.requestRender?.();\r\n }\r\n\r\n /**\r\n * Request a lightweight style update without rebuilding DOM.\r\n * Use this instead of requestRender() when only CSS classes need updating.\r\n */\r\n protected requestAfterRender(): void {\r\n this.grid?.requestAfterRender?.();\r\n }\r\n\r\n /**\r\n * Get the current rows from the grid.\r\n */\r\n protected get rows(): any[] {\r\n return this.grid?.rows ?? [];\r\n }\r\n\r\n /**\r\n * Get the original unfiltered/unprocessed rows from the grid.\r\n * Use this when you need all source data regardless of active filters.\r\n */\r\n protected get sourceRows(): any[] {\r\n return (this.grid as any)?.sourceRows ?? [];\r\n }\r\n\r\n /**\r\n * Get the current columns from the grid.\r\n */\r\n protected get columns(): ColumnConfig[] {\r\n return this.grid?.columns ?? [];\r\n }\r\n\r\n /**\r\n * Get only visible columns from the grid (excludes hidden).\r\n * Use this for rendering that needs to match the grid template.\r\n */\r\n protected get visibleColumns(): ColumnConfig[] {\r\n return (this.grid as any)?.visibleColumns ?? [];\r\n }\r\n\r\n /**\r\n * Get the shadow root of the grid.\r\n */\r\n protected get shadowRoot(): ShadowRoot | null {\r\n return this.grid?.shadowRoot ?? null;\r\n }\r\n\r\n /**\r\n * Log a warning message.\r\n */\r\n protected warn(message: string): void {\r\n console.warn(`[tbw-grid:${this.name}] ${message}`);\r\n }\r\n\r\n // ===== Lifecycle Hooks (override as needed) =====\r\n\r\n /**\r\n * Transform rows before rendering.\r\n * Called during each render cycle before rows are rendered to the DOM.\r\n * Use this to filter, sort, or add computed properties to rows.\r\n *\r\n * @param rows - The current rows array (readonly to encourage returning a new array)\r\n * @returns The modified rows array to render\r\n *\r\n * @example\r\n * ```ts\r\n * processRows(rows: readonly any[]): any[] {\r\n * // Filter out hidden rows\r\n * return rows.filter(row => !row._hidden);\r\n * }\r\n * ```\r\n *\r\n * @example\r\n * ```ts\r\n * processRows(rows: readonly any[]): any[] {\r\n * // Add computed properties\r\n * return rows.map(row => ({\r\n * ...row,\r\n * _fullName: `${row.firstName} ${row.lastName}`\r\n * }));\r\n * }\r\n * ```\r\n */\r\n processRows?(rows: readonly any[]): any[];\r\n\r\n /**\r\n * Transform columns before rendering.\r\n * Called during each render cycle before column headers and cells are rendered.\r\n * Use this to add, remove, or modify column definitions.\r\n *\r\n * @param columns - The current columns array (readonly to encourage returning a new array)\r\n * @returns The modified columns array to render\r\n *\r\n * @example\r\n * ```ts\r\n * processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\r\n * // Add a selection checkbox column\r\n * return [\r\n * { field: '_select', header: '', width: 40 },\r\n * ...columns\r\n * ];\r\n * }\r\n * ```\r\n */\r\n processColumns?(columns: readonly ColumnConfig[]): ColumnConfig[];\r\n\r\n /**\r\n * Called before each render cycle begins.\r\n * Use this to prepare state or cache values needed during rendering.\r\n *\r\n * @example\r\n * ```ts\r\n * beforeRender(): void {\r\n * this.visibleRowCount = this.calculateVisibleRows();\r\n * }\r\n * ```\r\n */\r\n beforeRender?(): void;\r\n\r\n /**\r\n * Called after each render cycle completes.\r\n * Use this for DOM manipulation, adding event listeners to rendered elements,\r\n * or applying visual effects like selection highlights.\r\n *\r\n * @example\r\n * ```ts\r\n * afterRender(): void {\r\n * // Apply selection styling to rendered rows\r\n * const rows = this.shadowRoot?.querySelectorAll('.data-row');\r\n * rows?.forEach((row, i) => {\r\n * row.classList.toggle('selected', this.selectedRows.has(i));\r\n * });\r\n * }\r\n * ```\r\n */\r\n afterRender?(): void;\r\n\r\n /**\r\n * Render a custom row, bypassing the default row rendering.\r\n * Use this for special row types like group headers, detail rows, or footers.\r\n *\r\n * @param row - The row data object\r\n * @param rowEl - The row DOM element to render into\r\n * @param rowIndex - The index of the row in the data array\r\n * @returns `true` if the plugin handled rendering (prevents default), `false`/`void` for default rendering\r\n *\r\n * @example\r\n * ```ts\r\n * renderRow(row: any, rowEl: HTMLElement, rowIndex: number): boolean | void {\r\n * if (row._isGroupHeader) {\r\n * rowEl.innerHTML = `<div class=\"group-header\">${row._groupLabel}</div>`;\r\n * return true; // Handled - skip default rendering\r\n * }\r\n * // Return void to let default rendering proceed\r\n * }\r\n * ```\r\n */\r\n renderRow?(row: any, rowEl: HTMLElement, rowIndex: number): boolean | void;\r\n\r\n // ===== Interaction Hooks (override as needed) =====\r\n\r\n /**\r\n * Handle keyboard events on the grid.\r\n * Called when a key is pressed while the grid or a cell has focus.\r\n *\r\n * @param event - The native KeyboardEvent\r\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\r\n *\r\n * @example\r\n * ```ts\r\n * onKeyDown(event: KeyboardEvent): boolean | void {\r\n * // Handle Ctrl+A for select all\r\n * if (event.ctrlKey && event.key === 'a') {\r\n * this.selectAllRows();\r\n * return true; // Prevent default browser select-all\r\n * }\r\n * }\r\n * ```\r\n */\r\n onKeyDown?(event: KeyboardEvent): boolean | void;\r\n\r\n /**\r\n * Handle cell click events.\r\n * Called when a data cell is clicked (not headers).\r\n *\r\n * @param event - Cell click event with row/column context\r\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\r\n *\r\n * @example\r\n * ```ts\r\n * onCellClick(event: CellClickEvent): boolean | void {\r\n * if (event.field === '_select') {\r\n * this.toggleRowSelection(event.rowIndex);\r\n * return true; // Handled\r\n * }\r\n * }\r\n * ```\r\n */\r\n onCellClick?(event: CellClickEvent): boolean | void;\r\n\r\n /**\r\n * Handle row click events.\r\n * Called when any part of a data row is clicked.\r\n * Note: This is called in addition to onCellClick, not instead of.\r\n *\r\n * @param event - Row click event with row context\r\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\r\n *\r\n * @example\r\n * ```ts\r\n * onRowClick(event: RowClickEvent): boolean | void {\r\n * if (this.config.mode === 'row') {\r\n * this.selectRow(event.rowIndex, event.originalEvent);\r\n * return true;\r\n * }\r\n * }\r\n * ```\r\n */\r\n onRowClick?(event: RowClickEvent): boolean | void;\r\n\r\n /**\r\n * Handle header click events.\r\n * Called when a column header is clicked. Commonly used for sorting.\r\n *\r\n * @param event - Header click event with column context\r\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\r\n *\r\n * @example\r\n * ```ts\r\n * onHeaderClick(event: HeaderClickEvent): boolean | void {\r\n * if (event.column.sortable !== false) {\r\n * this.toggleSort(event.field);\r\n * return true;\r\n * }\r\n * }\r\n * ```\r\n */\r\n onHeaderClick?(event: HeaderClickEvent): boolean | void;\r\n\r\n /**\r\n * Handle scroll events on the grid viewport.\r\n * Called during scrolling. Note: This may be called frequently; debounce if needed.\r\n *\r\n * @param event - Scroll event with scroll position and viewport dimensions\r\n *\r\n * @example\r\n * ```ts\r\n * onScroll(event: ScrollEvent): void {\r\n * // Update sticky column positions\r\n * this.updateStickyPositions(event.scrollLeft);\r\n * }\r\n * ```\r\n */\r\n onScroll?(event: ScrollEvent): void;\r\n\r\n /**\r\n * Handle cell mousedown events.\r\n * Used for initiating drag operations like range selection or column resize.\r\n *\r\n * @param event - Mouse event with cell context\r\n * @returns `true` to indicate drag started (prevents text selection), `false`/`void` otherwise\r\n *\r\n * @example\r\n * ```ts\r\n * onCellMouseDown(event: CellMouseEvent): boolean | void {\r\n * if (event.rowIndex !== undefined && this.config.mode === 'range') {\r\n * this.startDragSelection(event.rowIndex, event.colIndex);\r\n * return true; // Prevent text selection\r\n * }\r\n * }\r\n * ```\r\n */\r\n onCellMouseDown?(event: CellMouseEvent): boolean | void;\r\n\r\n /**\r\n * Handle cell mousemove events during drag operations.\r\n * Only called when a drag is in progress (after mousedown returned true).\r\n *\r\n * @param event - Mouse event with current cell context\r\n * @returns `true` to continue handling the drag, `false`/`void` otherwise\r\n *\r\n * @example\r\n * ```ts\r\n * onCellMouseMove(event: CellMouseEvent): boolean | void {\r\n * if (this.isDragging && event.rowIndex !== undefined) {\r\n * this.extendSelection(event.rowIndex, event.colIndex);\r\n * return true;\r\n * }\r\n * }\r\n * ```\r\n */\r\n onCellMouseMove?(event: CellMouseEvent): boolean | void;\r\n\r\n /**\r\n * Handle cell mouseup events to end drag operations.\r\n *\r\n * @param event - Mouse event with final cell context\r\n * @returns `true` if drag was finalized, `false`/`void` otherwise\r\n *\r\n * @example\r\n * ```ts\r\n * onCellMouseUp(event: CellMouseEvent): boolean | void {\r\n * if (this.isDragging) {\r\n * this.finalizeDragSelection();\r\n * this.isDragging = false;\r\n * return true;\r\n * }\r\n * }\r\n * ```\r\n */\r\n onCellMouseUp?(event: CellMouseEvent): boolean | void;\r\n\r\n /**\r\n * Provide context menu items when right-clicking on the grid.\r\n * Multiple plugins can contribute items; they are merged into a single menu.\r\n *\r\n * @param params - Context about where the menu was triggered (row, column, etc.)\r\n * @returns Array of menu items to display\r\n *\r\n * @example\r\n * ```ts\r\n * getContextMenuItems(params: ContextMenuParams): ContextMenuItem[] {\r\n * if (params.isHeader) {\r\n * return [\r\n * { id: 'sort-asc', label: 'Sort Ascending', action: () => this.sortAsc(params.field) },\r\n * { id: 'sort-desc', label: 'Sort Descending', action: () => this.sortDesc(params.field) },\r\n * ];\r\n * }\r\n * return [\r\n * { id: 'copy', label: 'Copy Cell', action: () => this.copyCell(params) },\r\n * ];\r\n * }\r\n * ```\r\n */\r\n getContextMenuItems?(params: ContextMenuParams): ContextMenuItem[];\r\n\r\n // ===== Column State Hooks (override as needed) =====\r\n\r\n /**\r\n * Contribute plugin-specific state for a column.\r\n * Called by the grid when collecting column state for serialization.\r\n * Plugins can add their own properties to the column state.\r\n *\r\n * @param field - The field name of the column\r\n * @returns Partial column state with plugin-specific properties, or undefined if no state to contribute\r\n *\r\n * @example\r\n * ```ts\r\n * getColumnState(field: string): Partial<ColumnState> | undefined {\r\n * const filterModel = this.filterModels.get(field);\r\n * if (filterModel) {\r\n * // Uses module augmentation to add filter property to ColumnState\r\n * return { filter: filterModel } as Partial<ColumnState>;\r\n * }\r\n * return undefined;\r\n * }\r\n * ```\r\n */\r\n getColumnState?(field: string): Partial<ColumnState> | undefined;\r\n\r\n /**\r\n * Apply plugin-specific state to a column.\r\n * Called by the grid when restoring column state from serialized data.\r\n * Plugins should restore their internal state based on the provided state.\r\n *\r\n * @param field - The field name of the column\r\n * @param state - The column state to apply (may contain plugin-specific properties)\r\n *\r\n * @example\r\n * ```ts\r\n * applyColumnState(field: string, state: ColumnState): void {\r\n * // Check for filter property added via module augmentation\r\n * const filter = (state as any).filter;\r\n * if (filter) {\r\n * this.filterModels.set(field, filter);\r\n * this.applyFilter();\r\n * }\r\n * }\r\n * ```\r\n */\r\n applyColumnState?(field: string, state: ColumnState): void;\r\n\r\n // ===== Shell Integration Hooks (override as needed) =====\r\n\r\n /**\r\n * Register a tool panel for this plugin.\r\n * Return undefined if plugin has no tool panel.\r\n * The shell will create a toolbar toggle button and render the panel content\r\n * when the user opens the panel.\r\n *\r\n * @returns Tool panel definition, or undefined if plugin has no panel\r\n *\r\n * @example\r\n * ```ts\r\n * getToolPanel(): ToolPanelDefinition | undefined {\r\n * return {\r\n * id: 'columns',\r\n * title: 'Columns',\r\n * icon: '☰',\r\n * tooltip: 'Show/hide columns',\r\n * order: 10,\r\n * render: (container) => {\r\n * this.renderColumnList(container);\r\n * return () => this.cleanup();\r\n * },\r\n * };\r\n * }\r\n * ```\r\n */\r\n getToolPanel?(): ToolPanelDefinition | undefined;\r\n\r\n /**\r\n * Register content for the shell header center section.\r\n * Return undefined if plugin has no header content.\r\n * Examples: search input, selection summary, status indicators.\r\n *\r\n * @returns Header content definition, or undefined if plugin has no header content\r\n *\r\n * @example\r\n * ```ts\r\n * getHeaderContent(): HeaderContentDefinition | undefined {\r\n * return {\r\n * id: 'quick-filter',\r\n * order: 10,\r\n * render: (container) => {\r\n * const input = document.createElement('input');\r\n * input.type = 'text';\r\n * input.placeholder = 'Search...';\r\n * input.addEventListener('input', this.handleInput);\r\n * container.appendChild(input);\r\n * return () => input.removeEventListener('input', this.handleInput);\r\n * },\r\n * };\r\n * }\r\n * ```\r\n */\r\n getHeaderContent?(): HeaderContentDefinition | undefined;\r\n}\r\n","/**\n * Sticky Columns Core Logic\n *\n * Pure functions for applying sticky (pinned) column positioning.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport type { StickyPosition } from './types';\n\n/**\n * Get columns that should be sticky on the left.\n *\n * @param columns - Array of column configurations\n * @returns Array of columns with sticky='left'\n */\nexport function getLeftStickyColumns(columns: any[]): any[] {\n return columns.filter((col) => col.sticky === 'left');\n}\n\n/**\n * Get columns that should be sticky on the right.\n *\n * @param columns - Array of column configurations\n * @returns Array of columns with sticky='right'\n */\nexport function getRightStickyColumns(columns: any[]): any[] {\n return columns.filter((col) => col.sticky === 'right');\n}\n\n/**\n * Check if any columns have sticky positioning.\n *\n * @param columns - Array of column configurations\n * @returns True if any column has sticky position\n */\nexport function hasStickyColumns(columns: any[]): boolean {\n return columns.some((col) => col.sticky === 'left' || col.sticky === 'right');\n}\n\n/**\n * Get the sticky position of a column.\n *\n * @param column - Column configuration\n * @returns The sticky position or null if not sticky\n */\nexport function getColumnStickyPosition(column: any): StickyPosition | null {\n if (column.sticky === 'left') return 'left';\n if (column.sticky === 'right') return 'right';\n return null;\n}\n\n/**\n * Calculate left offsets for sticky-left columns.\n * Returns a map of field -> offset in pixels.\n *\n * @param columns - Array of column configurations (in order)\n * @param getColumnWidth - Function to get column width by field\n * @returns Map of field to left offset\n */\nexport function calculateLeftStickyOffsets(\n columns: any[],\n getColumnWidth: (field: string) => number\n): Map<string, number> {\n const offsets = new Map<string, number>();\n let currentOffset = 0;\n\n for (const col of columns) {\n if (col.sticky === 'left') {\n offsets.set(col.field, currentOffset);\n currentOffset += getColumnWidth(col.field);\n }\n }\n\n return offsets;\n}\n\n/**\n * Calculate right offsets for sticky-right columns.\n * Processes columns in reverse order.\n *\n * @param columns - Array of column configurations (in order)\n * @param getColumnWidth - Function to get column width by field\n * @returns Map of field to right offset\n */\nexport function calculateRightStickyOffsets(\n columns: any[],\n getColumnWidth: (field: string) => number\n): Map<string, number> {\n const offsets = new Map<string, number>();\n let currentOffset = 0;\n\n // Process in reverse for right-sticky columns\n const reversed = [...columns].reverse();\n for (const col of reversed) {\n if (col.sticky === 'right') {\n offsets.set(col.field, currentOffset);\n currentOffset += getColumnWidth(col.field);\n }\n }\n\n return offsets;\n}\n\n/**\n * Apply sticky offsets to header and body cells.\n * This modifies the DOM elements in place.\n *\n * @param host - The grid host element\n * @param columns - Array of column configurations\n */\nexport function applyStickyOffsets(host: HTMLElement, columns: any[]): void {\n const shadowRoot = host.shadowRoot;\n if (!shadowRoot) return;\n\n const headerCells = Array.from(shadowRoot.querySelectorAll('.header-row .cell')) as HTMLElement[];\n if (!headerCells.length) return;\n\n // Build column index map for matching body cells (which use data-col, not data-field)\n const fieldToIndex = new Map<string, number>();\n columns.forEach((col, i) => {\n if (col.field) fieldToIndex.set(col.field, i);\n });\n\n // Apply left sticky\n let left = 0;\n for (const col of columns) {\n if (col.sticky === 'left') {\n const colIndex = fieldToIndex.get(col.field);\n const cell = headerCells.find((c) => c.getAttribute('data-field') === col.field);\n if (cell) {\n cell.classList.add('sticky-left');\n cell.style.left = left + 'px';\n // Body cells use data-col (column index), not data-field\n if (colIndex !== undefined) {\n shadowRoot.querySelectorAll(`.data-grid-row .cell[data-col=\"${colIndex}\"]`).forEach((el) => {\n el.classList.add('sticky-left');\n (el as HTMLElement).style.left = left + 'px';\n });\n }\n left += cell.offsetWidth;\n }\n }\n }\n\n // Apply right sticky (process in reverse)\n let right = 0;\n for (const col of [...columns].reverse()) {\n if (col.sticky === 'right') {\n const colIndex = fieldToIndex.get(col.field);\n const cell = headerCells.find((c) => c.getAttribute('data-field') === col.field);\n if (cell) {\n cell.classList.add('sticky-right');\n cell.style.right = right + 'px';\n // Body cells use data-col (column index), not data-field\n if (colIndex !== undefined) {\n shadowRoot.querySelectorAll(`.data-grid-row .cell[data-col=\"${colIndex}\"]`).forEach((el) => {\n el.classList.add('sticky-right');\n (el as HTMLElement).style.right = right + 'px';\n });\n }\n right += cell.offsetWidth;\n }\n }\n }\n}\n\n/**\n * Clear sticky positioning from all cells.\n *\n * @param host - The grid host element\n */\nexport function clearStickyOffsets(host: HTMLElement): void {\n const shadowRoot = host.shadowRoot;\n if (!shadowRoot) return;\n\n const cells = shadowRoot.querySelectorAll('.sticky-left, .sticky-right');\n cells.forEach((cell) => {\n cell.classList.remove('sticky-left', 'sticky-right');\n (cell as HTMLElement).style.left = '';\n (cell as HTMLElement).style.right = '';\n });\n}\n","/**\n * Pinned Columns Plugin (Class-based)\n *\n * Enables column pinning (sticky left/right positioning).\n */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig } from '../../core/types';\nimport {\n applyStickyOffsets,\n clearStickyOffsets,\n getLeftStickyColumns,\n getRightStickyColumns,\n hasStickyColumns,\n} from './pinned-columns';\nimport type { PinnedColumnsConfig } from './types';\n\n/**\n * Pinned Columns Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new PinnedColumnsPlugin({ enabled: true })\n * ```\n */\nexport class PinnedColumnsPlugin extends BaseGridPlugin<PinnedColumnsConfig> {\n readonly name = 'pinnedColumns';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<PinnedColumnsConfig> {\n return {\n enabled: true,\n };\n }\n\n // ===== Internal State =====\n private isApplied = false;\n private leftOffsets = new Map<string, number>();\n private rightOffsets = new Map<string, number>();\n\n // ===== Lifecycle =====\n\n override detach(): void {\n this.leftOffsets.clear();\n this.rightOffsets.clear();\n this.isApplied = false;\n }\n\n // ===== Detection =====\n\n /**\n * Auto-detect sticky columns from column configuration.\n */\n static detect(rows: readonly unknown[], config: { columns?: ColumnConfig[] }): boolean {\n const columns = config?.columns;\n if (!Array.isArray(columns)) return false;\n return hasStickyColumns(columns);\n }\n\n // ===== Hooks =====\n\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n if (!this.config.enabled) {\n this.isApplied = false;\n return [...columns];\n }\n\n // Mark that we have sticky columns to apply\n this.isApplied = hasStickyColumns([...columns]);\n return [...columns];\n }\n\n override afterRender(): void {\n if (!this.config.enabled || !this.isApplied) {\n return;\n }\n\n const host = this.grid as unknown as HTMLElement;\n const columns = [...this.columns];\n\n if (!hasStickyColumns(columns)) {\n clearStickyOffsets(host);\n this.isApplied = false;\n return;\n }\n\n // Apply sticky offsets after a microtask to ensure DOM is ready\n queueMicrotask(() => {\n applyStickyOffsets(host, columns);\n });\n }\n\n // ===== Public API =====\n\n /**\n * Re-apply sticky offsets (e.g., after column resize).\n */\n refreshStickyOffsets(): void {\n const columns = [...this.columns];\n applyStickyOffsets(this.grid as unknown as HTMLElement, columns);\n }\n\n /**\n * Get columns pinned to the left.\n */\n getLeftPinnedColumns(): ColumnConfig[] {\n const columns = [...this.columns];\n return getLeftStickyColumns(columns);\n }\n\n /**\n * Get columns pinned to the right.\n */\n getRightPinnedColumns(): ColumnConfig[] {\n const columns = [...this.columns];\n return getRightStickyColumns(columns);\n }\n\n /**\n * Clear all sticky positioning.\n */\n clearStickyPositions(): void {\n clearStickyOffsets(this.grid as unknown as HTMLElement);\n }\n}\n"],"names":["BaseGridPlugin","config","grid","PluginClass","eventName","detail","message","getLeftStickyColumns","columns","col","getRightStickyColumns","hasStickyColumns","applyStickyOffsets","host","shadowRoot","headerCells","fieldToIndex","i","left","colIndex","cell","c","el","right","clearStickyOffsets","PinnedColumnsPlugin","rows"],"mappings":"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"}
|