@forgedevstack/grid-table 1.0.1 → 1.0.3

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/CHANGELOG.md CHANGED
@@ -5,6 +5,43 @@ All notable changes to grid-table will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.0.3] - 2026-02-07
9
+
10
+ ### Added
11
+
12
+ - **Overflow tooltip**: When cell content is truncated, show full content on hover. Table prop `showOverflowTooltip` (default `true`); override per column with `showOverflowTooltip: true | false`.
13
+ - **Expandable sub-cell**: Optional extra content per cell, expandable by double-click or arrow. Column: `renderSubCell: (row) => ReactNode`, `subCellExpandTrigger: 'doubleClick' | 'arrow' | 'both'`. Table prop `subCellExpandTrigger` sets default for all columns. State in context (`expandedCellIds`, `toggleCellExpansion`).
14
+ - **Cell auto-size on double-click**: Table prop `enableCellAutoSizeOnDoubleClick`. When `true`, double-clicking a truncated cell (without sub-cell) expands that cell to fit content. State: `autoSizedCellIds`, `toggleCellAutoSize` in context.
15
+ - **Expand row on double-click**: Table prop `expandRowOnDoubleClick`. When `true`, double-clicking a row toggles row expansion (`renderRowExpansion`). All rows can show one full-width sub-row when expanded.
16
+ - **Table-level options**: All cell/row expand and tooltip options are configurable via GridTable props: `showOverflowTooltip`, `enableCellAutoSizeOnDoubleClick`, `subCellExpandTrigger`, `expandRowOnDoubleClick`. Column definitions can override where applicable.
17
+ - **@forgedevstack/bear integration**: Grid-table uses Bear (ForgeStack) for checkboxes, overflow tooltip, and theme. Peer dependency `@forgedevstack/bear`; Bear styles are imported from grid-table entry so one import brings them in. Theme controllable via `themeMode: 'light' | 'dark' | 'system'` prop.
18
+ - **Header/body alignment**: Expand column spacer in header when `enableRowExpansion` so column names align with data. Header cell widths use explicit px for consistency.
19
+ - **Sub-row (renderRowExpansion)**: Signature is now `(row: T, rowId: string | number) => ReactNode`. User controls content (editable form, sub-table, etc.) and can use `rowId` for save/update.
20
+
21
+ ## [1.0.2] - 2026-01-17
22
+
23
+ ### Changed
24
+
25
+ - **BREAKING**: Migrated from Tailwind CSS to SCSS for zero-config styling
26
+ - Removed Tailwind CSS dependency completely
27
+ - All styles now self-contained in SCSS
28
+ - Compiled CSS included in package
29
+ - No Tailwind configuration required
30
+ - Fixed package.json exports order - moved `types` before `import` and `require` to resolve TypeScript warnings
31
+ - Updated package name to `@forgedevstack/grid-table` to match npm organization
32
+
33
+ ### Added
34
+
35
+ - SCSS source files for better maintainability
36
+ - Compiled CSS file in dist for zero-config usage
37
+ - Build process now compiles SCSS to CSS automatically
38
+
39
+ ## [1.0.1] - 2026-01-17
40
+
41
+ ### Changed
42
+
43
+ - Initial npm publication with `@forgedevstack` scope
44
+
8
45
  ## [0.1.0] - 2026-01-15
9
46
 
10
47
  ### Added
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # grid-table
2
2
 
3
- A powerful, headless grid table component for React with Tailwind CSS styling, drag-and-drop columns, filtering, sorting, and responsive mobile support.
3
+ A powerful, headless grid table component for React with SCSS styling, drag-and-drop columns, filtering, sorting, and responsive mobile support. Zero-config - no Tailwind CSS required!
4
4
 
5
5
  ## Features
6
6
 
7
- - **Tailwind CSS + DIVs**: No `<table>` elements, fully customizable with Tailwind
7
+ - **SCSS Styling**: Self-contained SCSS styles, no Tailwind CSS dependency required
8
8
  - **React 16.8+**: Works with all React versions that support hooks
9
9
  - **Dark/Light Theme**: Built-in theme support with customizable colors
10
10
  - **Filtering**: Column-level and global filtering with multiple operators
@@ -17,6 +17,8 @@ A powerful, headless grid table component for React with Tailwind CSS styling, d
17
17
  - **Responsive**: Mobile-first design with drawer for filters/sorting
18
18
  - **Skeleton Loading**: Beautiful loading states
19
19
  - **Empty States**: Customizable empty state component
20
+ - **Overflow tooltip**: Show full cell content on hover when truncated (per-column `showOverflowTooltip`)
21
+ - **Expandable sub-cell**: Extra content per cell via double-click or arrow; `renderSubCell` and `subCellExpandTrigger`
20
22
  - **Context API**: No prop drilling, access state from anywhere
21
23
  - **TypeScript**: Full type safety
22
24
  - **Accessible**: ARIA attributes and keyboard navigation
@@ -24,13 +26,31 @@ A powerful, headless grid table component for React with Tailwind CSS styling, d
24
26
  ## Installation
25
27
 
26
28
  ```bash
27
- npm install grid-table
29
+ npm install @forgedevstack/grid-table
28
30
  # or
29
- pnpm add grid-table
31
+ pnpm add @forgedevstack/grid-table
30
32
  # or
31
- yarn add grid-table
33
+ yarn add @forgedevstack/grid-table
32
34
  ```
33
35
 
36
+ ### Dependencies
37
+
38
+ Grid-table is part of ForgeStack and uses **@forgedevstack/bear** for checkboxes, tooltips, and theming. Install Bear (peer dependency):
39
+
40
+ ```bash
41
+ npm install @forgedevstack/grid-table @forgedevstack/bear
42
+ ```
43
+
44
+ ### Import CSS (Required)
45
+
46
+ Import the grid-table CSS once in your app. Bear styles are loaded automatically when you import from grid-table:
47
+
48
+ ```tsx
49
+ import '@forgedevstack/grid-table/grid-table.css';
50
+ ```
51
+
52
+ No need to import Bear styles separately; the grid-table entry pulls them in.
53
+
34
54
  ## Quick Start
35
55
 
36
56
  ```tsx
@@ -224,6 +244,11 @@ const columns: ColumnDefinition<User>[] = [
224
244
  | `showPagination` | `boolean` | `true` | Show pagination controls |
225
245
  | `showFilter` | `boolean` | `true` | Show filter controls |
226
246
  | `showGlobalFilter` | `boolean` | `true` | Show global search |
247
+ | `showOverflowTooltip` | `boolean` | `true` | Show full content on hover when cell is truncated |
248
+ | `enableCellAutoSizeOnDoubleClick` | `boolean` | `false` | Double-click cell to expand width to fit content |
249
+ | `subCellExpandTrigger` | `'doubleClick' \| 'arrow' \| 'both'` | `'both'` | Default trigger for sub-cell expand |
250
+ | `expandRowOnDoubleClick` | `boolean` | `false` | Double-click row to toggle row expansion |
251
+ | `themeMode` | `'light' \| 'dark' \| 'system'` | — | Control theme for table (and Bear components) |
227
252
  | `stickyHeader` | `boolean` | `true` | Sticky header on scroll |
228
253
 
229
254
  ### ColumnDefinition
@@ -246,6 +271,9 @@ const columns: ColumnDefinition<User>[] = [
246
271
  | `filterOptions` | `FilterOption[]` | Options for select filter |
247
272
  | `sortFn` | `(a, b, dir) => number` | Custom sort function |
248
273
  | `filterFn` | `(value, filter, op) => boolean` | Custom filter function |
274
+ | `showOverflowTooltip` | `boolean` | When truncated, show full content on hover (default true) |
275
+ | `renderSubCell` | `(row) => ReactNode` | Optional expandable sub-content below cell |
276
+ | `subCellExpandTrigger` | `'doubleClick' \| 'arrow' \| 'both'` | How to expand sub-cell (default `'both'`) |
249
277
 
250
278
  ## License
251
279
 
@@ -0,0 +1 @@
1
+ :root{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #262626;--gt-text-secondary: #595959;--gt-text-muted: #8c8c8c;--gt-border-color: rgba(0, 0, 0, 0.06);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.light{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #171717;--gt-text-secondary: #525252;--gt-text-muted: #737373;--gt-border-color: rgba(0, 0, 0, 0.08);--gt-accent-primary: #2563eb;--gt-accent-success: #16a34a;--gt-accent-warning: #ca8a04;--gt-accent-error: #dc2626}html.dark,.dark{--gt-bg-primary: #1e1e1e;--gt-bg-secondary: #2b2b2b;--gt-bg-tertiary: #3c3c3c;--gt-bg-hover: #4e4e4e;--gt-text-primary: #e4e6eb;--gt-text-secondary: #b4b8c0;--gt-text-muted: #8c9199;--gt-border-color: rgba(255, 255, 255, 0.12);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}:root{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #262626;--gt-text-secondary: #595959;--gt-text-muted: #8c8c8c;--gt-border-color: rgba(0, 0, 0, 0.06);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.light{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #171717;--gt-text-secondary: #525252;--gt-text-muted: #737373;--gt-border-color: rgba(0, 0, 0, 0.08);--gt-accent-primary: #2563eb;--gt-accent-success: #16a34a;--gt-accent-warning: #ca8a04;--gt-accent-error: #dc2626}html.dark,.dark{--gt-bg-primary: #1e1e1e;--gt-bg-secondary: #2b2b2b;--gt-bg-tertiary: #3c3c3c;--gt-bg-hover: #4e4e4e;--gt-text-primary: #e4e6eb;--gt-text-secondary: #b4b8c0;--gt-text-muted: #8c9199;--gt-border-color: rgba(255, 255, 255, 0.12);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.grid-table{background-color:var(--gt-bg-primary, #ffffff);border-radius:.5rem;border:1px solid var(--gt-border-color, rgba(0, 0, 0, 0.06));overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",sans-serif;font-size:14px;line-height:1.5;width:100%;display:flex;flex-direction:column}.grid-table-container{overflow:auto;width:100%}.grid-table-studio-layout{background-color:var(--gt-bg-primary)}.grid-table-studio-main{overflow:auto}.light .grid-table{color:var(--gt-text-primary)}.light .grid-table .grid-cell-value,.light .grid-table .grid-cell-value *,.light .grid-table .grid-cell-label,.light .grid-table .grid-header-cell,.light .grid-table .grid-header-content,.light .grid-table .grid-pagination,.light .grid-table .grid-pagination *,.light .grid-table .grid-table-toolbar,.light .grid-table .grid-table-toolbar input,.light .grid-table .grid-table-toolbar .search-icon,.light .grid-table .grid-table-toolbar .clear-button,.light .grid-table .grid-table-toolbar .filter-badge,.light .grid-table .grid-cell-subcell{color:var(--gt-text-primary)}.light .grid-table .grid-pagination-info span,.light .grid-table .grid-table-toolbar input::placeholder{color:var(--gt-text-secondary)}.light .grid-table input,.light .grid-table select,.light .grid-table button:not(.grid-cell-expand-trigger){color:var(--gt-text-primary);background-color:var(--gt-bg-primary);border-color:var(--gt-border-color)}.light .grid-table .grid-header-cell{background-color:var(--gt-bg-secondary)}.light .grid-table .grid-row{background-color:var(--gt-bg-primary)}.light .grid-table .grid-row:hover{background-color:var(--gt-bg-hover)}:root{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #262626;--gt-text-secondary: #595959;--gt-text-muted: #8c8c8c;--gt-border-color: rgba(0, 0, 0, 0.06);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.light{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #171717;--gt-text-secondary: #525252;--gt-text-muted: #737373;--gt-border-color: rgba(0, 0, 0, 0.08);--gt-accent-primary: #2563eb;--gt-accent-success: #16a34a;--gt-accent-warning: #ca8a04;--gt-accent-error: #dc2626}html.dark,.dark{--gt-bg-primary: #1e1e1e;--gt-bg-secondary: #2b2b2b;--gt-bg-tertiary: #3c3c3c;--gt-bg-hover: #4e4e4e;--gt-text-primary: #e4e6eb;--gt-text-secondary: #b4b8c0;--gt-text-muted: #8c9199;--gt-border-color: rgba(255, 255, 255, 0.12);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.grid-header{display:flex;background-color:var(--gt-bg-secondary, #f5f5f5);width:100%;min-width:100%}.grid-header-cell{position:relative;display:flex;align-items:center;gap:.5rem;padding:.75rem 1rem;font-weight:500;font-size:.875rem;color:var(--gt-text-secondary, #595959);user-select:none;border-bottom:1px solid var(--gt-border-color, rgba(0, 0, 0, 0.06));transition:color,background-color;transition-duration:.15s;flex-shrink:0;min-width:0}.grid-header-cell:hover{color:var(--gt-text-primary, #262626);background-color:var(--gt-bg-hover, #e0e0e0)}.grid-header-cell.sticky-left,.grid-header-cell.sticky-right{box-shadow:2px 0 4px -2px rgba(0,0,0,.2)}.grid-header>.relative:last-child .grid-header-cell{flex:1 1 auto !important;width:auto !important;min-width:0 !important;max-width:none !important}.grid-header-content{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.grid-header-sort{display:flex;align-items:center;justify-content:center;gap:.125rem}.grid-header-filter{padding:.25rem;border-radius:.25rem;transition:background-color;transition-duration:.15s}.grid-header-filter:hover{background-color:var(--gt-bg-tertiary, #3c3c3c)}.grid-header-resize{position:absolute;right:0;top:0;bottom:0;width:.25rem;cursor:col-resize;transition:background-color;transition-duration:.15s}.grid-header-resize:hover{background-color:var(--gt-accent-primary, #1890ff)}.grid-header-select{display:flex;align-items:center;justify-content:center;padding:0 .5rem;border-bottom:1px solid var(--gt-border-color, rgba(0, 0, 0, 0.06))}.grid-header-expand-spacer{width:2.5rem;min-width:2.5rem;flex-shrink:0;border-bottom:1px solid var(--gt-border-color, rgba(0, 0, 0, 0.06))}.grid-header-checkbox{width:1rem;height:1rem;border-radius:.25rem;border:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08))}.grid-header-actions{display:flex;align-items:center;justify-content:center;gap:.25rem}.grid-header-actions button{border:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08));border-radius:.25rem;cursor:pointer;transition:background-color,color,border-color;transition-duration:.15s;padding:.25rem;background-color:rgba(0,0,0,0);color:var(--gt-text-secondary, #808080);width:1.5rem;height:1.5rem}.grid-header-actions button:hover{background-color:var(--gt-bg-hover, #e0e0e0);color:var(--gt-text-primary, #262626)}.icon-sm{width:.875rem;height:.875rem}.icon-md{width:1rem;height:1rem}.icon-lg{width:4rem;height:4rem}:root{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #262626;--gt-text-secondary: #595959;--gt-text-muted: #8c8c8c;--gt-border-color: rgba(0, 0, 0, 0.06);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.light{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #171717;--gt-text-secondary: #525252;--gt-text-muted: #737373;--gt-border-color: rgba(0, 0, 0, 0.08);--gt-accent-primary: #2563eb;--gt-accent-success: #16a34a;--gt-accent-warning: #ca8a04;--gt-accent-error: #dc2626}html.dark,.dark{--gt-bg-primary: #1e1e1e;--gt-bg-secondary: #2b2b2b;--gt-bg-tertiary: #3c3c3c;--gt-bg-hover: #4e4e4e;--gt-text-primary: #e4e6eb;--gt-text-secondary: #b4b8c0;--gt-text-muted: #8c9199;--gt-border-color: rgba(255, 255, 255, 0.12);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.grid-body{display:flex;flex-direction:column;background-color:var(--gt-bg-primary, #ffffff);width:100%}:root{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #262626;--gt-text-secondary: #595959;--gt-text-muted: #8c8c8c;--gt-border-color: rgba(0, 0, 0, 0.06);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.light{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #171717;--gt-text-secondary: #525252;--gt-text-muted: #737373;--gt-border-color: rgba(0, 0, 0, 0.08);--gt-accent-primary: #2563eb;--gt-accent-success: #16a34a;--gt-accent-warning: #ca8a04;--gt-accent-error: #dc2626}html.dark,.dark{--gt-bg-primary: #1e1e1e;--gt-bg-secondary: #2b2b2b;--gt-bg-tertiary: #3c3c3c;--gt-bg-hover: #4e4e4e;--gt-text-primary: #e4e6eb;--gt-text-secondary: #b4b8c0;--gt-text-muted: #8c9199;--gt-border-color: rgba(255, 255, 255, 0.12);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.grid-row{display:flex;align-items:stretch;border-bottom:1px solid var(--gt-border-color, rgba(0, 0, 0, 0.06));transition:background-color;transition-duration:.15s;cursor:default;width:100%;min-width:100%}.grid-row:hover{background-color:var(--gt-bg-hover, #4e4e4e)}.grid-row.selected{background-color:rgba(24,144,255,.1)}.grid-row.disabled{opacity:.5;cursor:not-allowed}.grid-row.clickable{cursor:pointer}.grid-row-select{display:flex;align-items:center;justify-content:center;padding:0 .5rem}.grid-row-checkbox{width:1rem;height:1rem;border-radius:.25rem;border:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08))}.grid-row-expand{display:flex;align-items:center;justify-content:center;padding:0 .5rem}.grid-row-expand-button{width:1.5rem;height:1.5rem;display:flex;align-items:center;justify-content:center;border-radius:.25rem;transition:background-color;transition-duration:.15s}.grid-row-expand-button:hover{background-color:var(--gt-bg-tertiary, #3c3c3c)}.grid-row-expansion{border-bottom:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08));background-color:var(--gt-bg-secondary, #2b2b2b);padding:1rem}:root{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #262626;--gt-text-secondary: #595959;--gt-text-muted: #8c8c8c;--gt-border-color: rgba(0, 0, 0, 0.06);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.light{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #171717;--gt-text-secondary: #525252;--gt-text-muted: #737373;--gt-border-color: rgba(0, 0, 0, 0.08);--gt-accent-primary: #2563eb;--gt-accent-success: #16a34a;--gt-accent-warning: #ca8a04;--gt-accent-error: #dc2626}html.dark,.dark{--gt-bg-primary: #1e1e1e;--gt-bg-secondary: #2b2b2b;--gt-bg-tertiary: #3c3c3c;--gt-bg-hover: #4e4e4e;--gt-text-primary: #e4e6eb;--gt-text-secondary: #b4b8c0;--gt-text-muted: #8c9199;--gt-border-color: rgba(255, 255, 255, 0.12);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.grid-cell{display:flex;flex-direction:column;align-items:stretch;padding:.75rem 1rem;flex-shrink:0;min-width:0}.grid-cell.sticky-left,.grid-cell.sticky-right{box-shadow:2px 0 4px -2px rgba(0,0,0,.2)}.grid-cell-inner{display:flex;align-items:center;min-width:0;gap:.25rem}.grid-row>.grid-cell:last-child{flex:1 1 auto !important;width:auto !important;min-width:0 !important;max-width:none !important}.grid-cell-value{color:var(--gt-text-primary, #262626);display:block;min-width:0}.grid-cell-value--truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;display:block}.grid-cell-value--truncate>*,.grid-cell-value-text{display:block !important;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.grid-cell-value-wrapper{display:block;min-width:0;overflow:hidden}.grid-cell-highlight{background-color:var(--gt-accent-primary, #1890ff);color:inherit;padding:0 .125em;border-radius:.125rem;font-weight:500}.grid-cell-label{font-weight:500;color:var(--gt-text-muted, #606060);margin-right:.5rem;font-size:.875rem;flex-shrink:0}.grid-cell-expand-trigger{flex-shrink:0;width:1.25rem;height:1.25rem;padding:0;border:none;border-radius:.25rem;background:rgba(0,0,0,0);cursor:pointer;color:var(--gt-text-muted, #606060);transition:transform,color,background-color;transition-duration:.15s}.grid-cell-expand-trigger::after{content:"";display:inline-block;width:.35rem;height:.35rem;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:rotate(45deg);margin-left:.25rem}.grid-cell-expand-trigger:hover{color:var(--gt-accent-primary, #1890ff);background-color:var(--gt-bg-tertiary, #3c3c3c)}.grid-cell-expand-trigger--expanded::after{transform:rotate(-135deg)}.grid-cell--auto-sized .grid-cell-value{white-space:normal;overflow:visible;text-overflow:clip}.grid-cell-subcell{margin-top:.5rem;padding-top:.5rem;border-top:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08));font-size:.8125rem;color:var(--gt-text-secondary, #808080);min-width:0}:root{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #262626;--gt-text-secondary: #595959;--gt-text-muted: #8c8c8c;--gt-border-color: rgba(0, 0, 0, 0.06);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.light{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #171717;--gt-text-secondary: #525252;--gt-text-muted: #737373;--gt-border-color: rgba(0, 0, 0, 0.08);--gt-accent-primary: #2563eb;--gt-accent-success: #16a34a;--gt-accent-warning: #ca8a04;--gt-accent-error: #dc2626}html.dark,.dark{--gt-bg-primary: #1e1e1e;--gt-bg-secondary: #2b2b2b;--gt-bg-tertiary: #3c3c3c;--gt-bg-hover: #4e4e4e;--gt-text-primary: #e4e6eb;--gt-text-secondary: #b4b8c0;--gt-text-muted: #8c9199;--gt-border-color: rgba(255, 255, 255, 0.12);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.grid-table-toolbar{display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:.75rem 1rem;border-bottom:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08))}.grid-table-toolbar .toolbar-search-wrapper{flex:1;position:relative}.grid-table-toolbar input{border:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08));border-radius:.25rem;background-color:var(--gt-bg-secondary, #2b2b2b);color:var(--gt-text-primary, #a9b7c6);outline:none;transition:border-color,box-shadow;transition-duration:.15s}.grid-table-toolbar input:focus{outline:none;border-color:var(--gt-accent-primary, #1890ff);box-shadow:0 0 0 2px rgba(24,144,255,.2)}.grid-table-toolbar input{width:100%;padding-left:2.5rem;padding-right:.75rem;padding-top:.5rem;padding-bottom:.5rem;font-size:.875rem}.grid-table-toolbar .search-icon{position:absolute;left:.75rem;top:50%;transform:translateY(-50%);width:1rem;height:1rem;color:var(--gt-text-muted, #606060);pointer-events:none}.grid-table-toolbar .clear-button{position:absolute;right:.75rem;top:50%;transform:translateY(-50%);color:var(--gt-text-muted, #606060);background:none;border:none;cursor:pointer;padding:.25rem;display:flex;align-items:center;justify-content:center;transition:color;transition-duration:.15s}.grid-table-toolbar .clear-button:hover{color:var(--gt-text-primary, #a9b7c6)}.grid-table-toolbar .filter-badge{display:flex;align-items:center;justify-content:center;gap:.5rem;padding:.5rem .75rem;font-size:.875rem;border-radius:.25rem;border:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08));color:var(--gt-text-secondary, #808080);transition:background-color;transition-duration:.15s}.grid-table-toolbar .filter-badge:hover{background-color:var(--gt-bg-hover, #4e4e4e)}.grid-table-toolbar .toolbar-actions{display:flex;align-items:center;justify-content:center;gap:.5rem}.grid-table-toolbar .toolbar-action-button{padding:.5rem;border-radius:.25rem;border:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08));background-color:rgba(0,0,0,0);transition:background-color;transition-duration:.15s}.grid-table-toolbar .toolbar-action-button:hover{background-color:var(--gt-bg-hover, #4e4e4e)}:root{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #262626;--gt-text-secondary: #595959;--gt-text-muted: #8c8c8c;--gt-border-color: rgba(0, 0, 0, 0.06);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.light{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #171717;--gt-text-secondary: #525252;--gt-text-muted: #737373;--gt-border-color: rgba(0, 0, 0, 0.08);--gt-accent-primary: #2563eb;--gt-accent-success: #16a34a;--gt-accent-warning: #ca8a04;--gt-accent-error: #dc2626}html.dark,.dark{--gt-bg-primary: #1e1e1e;--gt-bg-secondary: #2b2b2b;--gt-bg-tertiary: #3c3c3c;--gt-bg-hover: #4e4e4e;--gt-text-primary: #e4e6eb;--gt-text-secondary: #b4b8c0;--gt-text-muted: #8c9199;--gt-border-color: rgba(255, 255, 255, 0.12);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.grid-pagination{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:1rem;padding:.75rem 1rem;border-top:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08));background-color:var(--gt-bg-secondary, #2b2b2b);font-size:.875rem;color:var(--gt-text-secondary, #808080)}.grid-pagination-info{display:flex;align-items:center;justify-content:center;gap:1rem}.grid-pagination-range{font-size:.875rem;color:var(--gt-text-secondary, #808080)}.grid-pagination-size{display:flex;align-items:center;justify-content:center;gap:.5rem}.grid-pagination-size label{font-size:.875rem;color:var(--gt-text-muted, #606060)}.grid-pagination-size select{border:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08));border-radius:.25rem;background-color:var(--gt-bg-secondary, #2b2b2b);color:var(--gt-text-primary, #a9b7c6);outline:none;transition:border-color,box-shadow;transition-duration:.15s}.grid-pagination-size select:focus{outline:none;border-color:var(--gt-accent-primary, #1890ff);box-shadow:0 0 0 2px rgba(24,144,255,.2)}.grid-pagination-size select{padding:.25rem .5rem;font-size:.875rem}.grid-pagination-controls{display:flex;align-items:center;justify-content:center;gap:.25rem}.grid-pagination-pages{display:flex;align-items:center;justify-content:center;gap:.25rem}.grid-pagination button{border:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08));border-radius:.25rem;cursor:pointer;transition:background-color,color,border-color;transition-duration:.15s;padding:.5rem;background-color:var(--gt-bg-primary, #1e1e1e);color:var(--gt-text-primary, #a9b7c6);min-width:2rem;height:2rem;font-size:.875rem}.grid-pagination button:hover:not(:disabled){background-color:var(--gt-bg-hover, #4e4e4e)}.grid-pagination button:disabled{opacity:.5;cursor:not-allowed}.grid-pagination button.active{background-color:var(--gt-accent-primary, #1890ff);color:#fff}:root{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #262626;--gt-text-secondary: #595959;--gt-text-muted: #8c8c8c;--gt-border-color: rgba(0, 0, 0, 0.06);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.light{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #171717;--gt-text-secondary: #525252;--gt-text-muted: #737373;--gt-border-color: rgba(0, 0, 0, 0.08);--gt-accent-primary: #2563eb;--gt-accent-success: #16a34a;--gt-accent-warning: #ca8a04;--gt-accent-error: #dc2626}html.dark,.dark{--gt-bg-primary: #1e1e1e;--gt-bg-secondary: #2b2b2b;--gt-bg-tertiary: #3c3c3c;--gt-bg-hover: #4e4e4e;--gt-text-primary: #e4e6eb;--gt-text-secondary: #b4b8c0;--gt-text-muted: #8c9199;--gt-border-color: rgba(255, 255, 255, 0.12);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.filter-popup{position:absolute;z-index:50;background-color:var(--gt-bg-primary, #1e1e1e);border:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08));border-radius:.5rem;box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);min-width:280px}.filter-popup-header{padding:.75rem 1rem;border-bottom:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08))}.filter-popup-header .header-content{display:flex;align-items:center;justify-content:space-between}.filter-popup-header .header-title{font-weight:500;color:var(--gt-text-primary, #a9b7c6)}.filter-popup-header .header-close{padding:.25rem;border-radius:.25rem;background:none;border:none;cursor:pointer;color:var(--gt-text-muted, #606060);transition:background-color,color;transition-duration:.15s}.filter-popup-header .header-close:hover{background-color:var(--gt-bg-hover, #4e4e4e);color:var(--gt-text-primary, #a9b7c6)}.filter-popup-body{padding:1rem}.filter-popup-body .filter-field{margin-bottom:1rem}.filter-popup-body .filter-field:last-child{margin-bottom:0}.filter-popup-body label{display:block;font-size:.75rem;color:var(--gt-text-muted, #606060);margin-bottom:.25rem}.filter-popup-body input,.filter-popup-body select{border:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08));border-radius:.25rem;background-color:var(--gt-bg-secondary, #2b2b2b);color:var(--gt-text-primary, #a9b7c6);outline:none;transition:border-color,box-shadow;transition-duration:.15s}.filter-popup-body input:focus,.filter-popup-body select:focus{outline:none;border-color:var(--gt-accent-primary, #1890ff);box-shadow:0 0 0 2px rgba(24,144,255,.2)}.filter-popup-body input,.filter-popup-body select{width:100%;padding:.5rem .75rem;font-size:.875rem}.filter-popup-footer{padding:.75rem 1rem;border-top:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08));display:flex;align-items:center;justify-content:space-between}.filter-popup-footer button{padding:.375rem .75rem;font-size:.875rem;border-radius:.25rem;border:none;cursor:pointer;transition:background-color,color,opacity;transition-duration:.15s}.filter-popup-footer .filter-clear{color:var(--gt-text-muted, #606060)}.filter-popup-footer .filter-clear:hover{color:var(--gt-text-primary, #a9b7c6)}.filter-popup-footer .filter-apply{background-color:var(--gt-accent-primary, #1890ff);color:#fff}.filter-popup-footer .filter-apply:hover{opacity:.9}:root{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #262626;--gt-text-secondary: #595959;--gt-text-muted: #8c8c8c;--gt-border-color: rgba(0, 0, 0, 0.06);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.light{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #171717;--gt-text-secondary: #525252;--gt-text-muted: #737373;--gt-border-color: rgba(0, 0, 0, 0.08);--gt-accent-primary: #2563eb;--gt-accent-success: #16a34a;--gt-accent-warning: #ca8a04;--gt-accent-error: #dc2626}html.dark,.dark{--gt-bg-primary: #1e1e1e;--gt-bg-secondary: #2b2b2b;--gt-bg-tertiary: #3c3c3c;--gt-bg-hover: #4e4e4e;--gt-text-primary: #e4e6eb;--gt-text-secondary: #b4b8c0;--gt-text-muted: #8c9199;--gt-border-color: rgba(255, 255, 255, 0.12);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.mobile-drawer-container{position:fixed;inset:0;z-index:50}.mobile-drawer-overlay{position:absolute;inset:0;background-color:#000;transition:opacity;transition-duration:.15s}.mobile-drawer{position:absolute;bottom:0;left:0;right:0;background-color:var(--gt-bg-primary, #1e1e1e);border-radius:1rem 1rem 0 0;box-shadow:0 -4px 6px -1px rgba(0,0,0,.1),0 -2px 4px -1px rgba(0,0,0,.06);max-height:80vh;overflow:hidden;display:flex;flex-direction:column}.drawer-header{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;border-bottom:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08))}.drawer-header h3{font-size:1.125rem;font-weight:500;color:var(--gt-text-primary, #a9b7c6)}.drawer-header button{border:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08));border-radius:.25rem;cursor:pointer;transition:background-color,color,border-color;transition-duration:.15s;padding:.5rem;background-color:rgba(0,0,0,0);color:var(--gt-text-secondary, #808080)}.drawer-header button:hover{background-color:var(--gt-bg-hover, #4e4e4e)}.drawer-content{flex:1;overflow-y:auto}.drawer-filter-content,.drawer-sort-content,.drawer-columns-content{padding:1rem}.drawer-filter-content>div:first-child,.drawer-sort-content>div:first-child,.drawer-columns-content>div:first-child{display:flex;align-items:center;justify-content:space-between;margin-bottom:1rem}.drawer-filter-content>div:first-child button,.drawer-sort-content>div:first-child button,.drawer-columns-content>div:first-child button{font-size:.875rem;color:var(--gt-accent-primary, #1890ff);background:none;border:none;cursor:pointer;text-decoration:underline}.drawer-filter-content>div:first-child button:hover,.drawer-sort-content>div:first-child button:hover,.drawer-columns-content>div:first-child button:hover{text-decoration:none}.filter-item{margin-bottom:.75rem}.filter-item label{display:block;font-size:.875rem;font-weight:500;color:var(--gt-text-secondary, #808080);margin-bottom:.25rem}.filter-item input{border:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08));border-radius:.25rem;background-color:var(--gt-bg-secondary, #2b2b2b);color:var(--gt-text-primary, #a9b7c6);outline:none;transition:border-color,box-shadow;transition-duration:.15s}.filter-item input:focus{outline:none;border-color:var(--gt-accent-primary, #1890ff);box-shadow:0 0 0 2px rgba(24,144,255,.2)}.filter-item input{width:100%;padding:.5rem .75rem;font-size:.875rem}.drawer-sort-content button{border:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08));border-radius:.25rem;cursor:pointer;transition:background-color,color,border-color;transition-duration:.15s;width:100%;display:flex;align-items:center;justify-content:space-between;padding:.5rem .75rem;margin-bottom:.5rem;background-color:rgba(0,0,0,0);color:var(--gt-text-primary, #a9b7c6);font-size:.875rem}.drawer-sort-content button.active{border-color:var(--gt-accent-primary, #1890ff);background-color:rgba(24,144,255,.1)}.drawer-sort-content button:hover:not(.active){background-color:var(--gt-bg-hover, #4e4e4e)}.drawer-columns-content label{display:flex;align-items:center;justify-content:center;gap:.75rem;padding:.5rem .75rem;border-radius:.25rem;cursor:pointer;transition:background-color;transition-duration:.15s}.drawer-columns-content label:hover{background-color:var(--gt-bg-hover, #4e4e4e)}.drawer-columns-content label input[type=checkbox]{width:1rem;height:1rem;border-radius:.25rem;border:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08))}.drawer-columns-content label span{font-size:.875rem;color:var(--gt-text-primary, #a9b7c6)}:root{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #262626;--gt-text-secondary: #595959;--gt-text-muted: #8c8c8c;--gt-border-color: rgba(0, 0, 0, 0.06);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.light{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #171717;--gt-text-secondary: #525252;--gt-text-muted: #737373;--gt-border-color: rgba(0, 0, 0, 0.08);--gt-accent-primary: #2563eb;--gt-accent-success: #16a34a;--gt-accent-warning: #ca8a04;--gt-accent-error: #dc2626}html.dark,.dark{--gt-bg-primary: #1e1e1e;--gt-bg-secondary: #2b2b2b;--gt-bg-tertiary: #3c3c3c;--gt-bg-hover: #4e4e4e;--gt-text-primary: #e4e6eb;--gt-text-secondary: #b4b8c0;--gt-text-muted: #8c9199;--gt-border-color: rgba(255, 255, 255, 0.12);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.grid-skeleton{background-color:var(--gt-bg-primary, #1e1e1e)}.grid-skeleton-header{display:flex;border-bottom:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08));background-color:var(--gt-bg-secondary, #2b2b2b)}.grid-skeleton-header>div{padding:.75rem 1rem;flex-shrink:0}.grid-skeleton-header>div>div{border-radius:.25rem;background-color:var(--gt-bg-tertiary, #3c3c3c)}.grid-skeleton-header>div>div.animate-pulse{animation:pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite}.grid-skeleton-body{background-color:var(--gt-bg-primary, #1e1e1e)}.grid-skeleton-row{display:flex;border-bottom:1px solid var(--gt-border-color, rgba(255, 255, 255, 0.08))}.grid-skeleton-cell{padding:.75rem 1rem;flex-shrink:0}.grid-skeleton-cell>div{border-radius:.25rem;background-color:var(--gt-bg-tertiary, #3c3c3c)}.grid-skeleton-cell>div.animate-pulse{animation:pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite}@keyframes pulse{0%,100%{opacity:1}50%{opacity:.5}}:root{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #262626;--gt-text-secondary: #595959;--gt-text-muted: #8c8c8c;--gt-border-color: rgba(0, 0, 0, 0.06);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.light{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #171717;--gt-text-secondary: #525252;--gt-text-muted: #737373;--gt-border-color: rgba(0, 0, 0, 0.08);--gt-accent-primary: #2563eb;--gt-accent-success: #16a34a;--gt-accent-warning: #ca8a04;--gt-accent-error: #dc2626}html.dark,.dark{--gt-bg-primary: #1e1e1e;--gt-bg-secondary: #2b2b2b;--gt-bg-tertiary: #3c3c3c;--gt-bg-hover: #4e4e4e;--gt-text-primary: #e4e6eb;--gt-text-secondary: #b4b8c0;--gt-text-muted: #8c9199;--gt-border-color: rgba(255, 255, 255, 0.12);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.grid-empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:4rem 2rem;text-align:center;background-color:var(--gt-bg-primary, #1e1e1e)}.grid-empty-icon{margin-bottom:1rem;color:var(--gt-text-muted, #606060);font-size:2.25rem}.grid-empty-icon svg{width:4rem;height:4rem}.grid-empty-title{font-size:1.125rem;font-weight:500;color:var(--gt-text-primary, #a9b7c6);margin-bottom:.5rem}.grid-empty-description{font-size:.875rem;color:var(--gt-text-muted, #606060);max-width:24rem;margin-bottom:1rem}.grid-empty-action{margin-top:1rem}:root{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #262626;--gt-text-secondary: #595959;--gt-text-muted: #8c8c8c;--gt-border-color: rgba(0, 0, 0, 0.06);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.light{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #171717;--gt-text-secondary: #525252;--gt-text-muted: #737373;--gt-border-color: rgba(0, 0, 0, 0.08);--gt-accent-primary: #2563eb;--gt-accent-success: #16a34a;--gt-accent-warning: #ca8a04;--gt-accent-error: #dc2626}html.dark,.dark{--gt-bg-primary: #1e1e1e;--gt-bg-secondary: #2b2b2b;--gt-bg-tertiary: #3c3c3c;--gt-bg-hover: #4e4e4e;--gt-text-primary: #e4e6eb;--gt-text-secondary: #b4b8c0;--gt-text-muted: #8c9199;--gt-border-color: rgba(255, 255, 255, 0.12);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.flex{display:flex}.flex-1{flex:1}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-4{gap:1rem}.relative{position:relative}.absolute{position:absolute}.sticky{position:sticky}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rounded{border-radius:.25rem}.rounded-lg{border-radius:.5rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.shadow-sm{box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.shadow-xl{box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05)}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-4{padding:1rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pl-10{padding-left:2.5rem}.pr-3{padding-right:.75rem}.w-full{width:100%}.w-4{width:1rem}.h-4{height:1rem}.w-3\.5{width:.875rem}.h-3\.5{height:.875rem}.min-w-\[280px\]{min-width:280px}.text-sm{font-size:.875rem}.text-xs{font-size:.75rem}.text-white{color:#fff}.font-medium{font-weight:500}.left-3{left:.75rem}.right-3{right:.75rem}.top-1\/2{top:50%}.-translate-y-1\/2{transform:translateY(-50%)}.z-50{z-index:50}.mr-2{margin-right:.5rem}.mb-1{margin-bottom:.25rem}.mt-1{margin-top:.25rem}.space-y-4>*+*{margin-top:1rem}.opacity-90{opacity:.9}.cursor-pointer{cursor:pointer}.cursor-col-resize{cursor:col-resize}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.flex-shrink-0{flex-shrink:0}@media(max-width: 768px){.w-full-sm{width:100%}}:root{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #262626;--gt-text-secondary: #595959;--gt-text-muted: #8c8c8c;--gt-border-color: rgba(0, 0, 0, 0.06);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.light{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #171717;--gt-text-secondary: #525252;--gt-text-muted: #737373;--gt-border-color: rgba(0, 0, 0, 0.08);--gt-accent-primary: #2563eb;--gt-accent-success: #16a34a;--gt-accent-warning: #ca8a04;--gt-accent-error: #dc2626}html.dark,.dark{--gt-bg-primary: #1e1e1e;--gt-bg-secondary: #2b2b2b;--gt-bg-tertiary: #3c3c3c;--gt-bg-hover: #4e4e4e;--gt-text-primary: #e4e6eb;--gt-text-secondary: #b4b8c0;--gt-text-muted: #8c9199;--gt-border-color: rgba(255, 255, 255, 0.12);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.bg-theme-primary{background-color:var(--gt-bg-primary, #1e1e1e) !important}.bg-theme-secondary{background-color:var(--gt-bg-secondary, #2b2b2b) !important}.bg-theme-tertiary{background-color:var(--gt-bg-tertiary, #3c3c3c) !important}.bg-theme-hover{background-color:var(--gt-bg-hover, #4e4e4e) !important}.text-theme-primary{color:var(--gt-text-primary, #a9b7c6) !important}.text-theme-secondary{color:var(--gt-text-secondary, #808080) !important}.text-theme-muted{color:var(--gt-text-muted, #606060) !important}.border-theme-border{border-color:var(--gt-border-color, rgba(255, 255, 255, 0.08)) !important}.bg-accent-primary{background-color:var(--gt-accent-primary, #1890ff) !important}.text-accent-primary{color:var(--gt-accent-primary, #1890ff) !important}.bg-accent-primary\/10{background-color:rgba(24,144,255,.1) !important}.hover\:bg-theme-hover:hover{background-color:var(--gt-bg-hover, #4e4e4e) !important}.hover\:text-theme-primary:hover{color:var(--gt-text-primary, #a9b7c6) !important}.hover\:bg-accent-primary\/90:hover{background-color:rgba(24,144,255,.9) !important}.hover\:bg-theme-tertiary:hover{background-color:var(--gt-bg-tertiary, #3c3c3c) !important}.hover\:opacity-90:hover{opacity:.9}:root{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #262626;--gt-text-secondary: #595959;--gt-text-muted: #8c8c8c;--gt-border-color: rgba(0, 0, 0, 0.06);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}.light{--gt-bg-primary: #ffffff;--gt-bg-secondary: #f5f5f5;--gt-bg-tertiary: #ebebeb;--gt-bg-hover: #e0e0e0;--gt-text-primary: #171717;--gt-text-secondary: #525252;--gt-text-muted: #737373;--gt-border-color: rgba(0, 0, 0, 0.08);--gt-accent-primary: #2563eb;--gt-accent-success: #16a34a;--gt-accent-warning: #ca8a04;--gt-accent-error: #dc2626}html.dark,.dark{--gt-bg-primary: #1e1e1e;--gt-bg-secondary: #2b2b2b;--gt-bg-tertiary: #3c3c3c;--gt-bg-hover: #4e4e4e;--gt-text-primary: #e4e6eb;--gt-text-secondary: #b4b8c0;--gt-text-muted: #8c9199;--gt-border-color: rgba(255, 255, 255, 0.12);--gt-accent-primary: #1890ff;--gt-accent-success: #52c41a;--gt-accent-warning: #faad14;--gt-accent-error: #ff4d4f}@media(max-width: 768px){.grid-row{flex-wrap:wrap;gap:.5rem;padding:1rem}.grid-cell{width:100%;padding:.25rem 0}}
package/dist/index.d.mts CHANGED
@@ -132,6 +132,9 @@ interface ColumnDefinition<T = unknown> {
132
132
  sortFn?: (a: unknown, b: unknown, direction: SortDirection) => number;
133
133
  filterFn?: (value: unknown, filterValue: unknown, operator: FilterOperator) => boolean;
134
134
  meta?: Record<string, unknown>;
135
+ showOverflowTooltip?: boolean;
136
+ renderSubCell?: (row: T) => ReactNode;
137
+ subCellExpandTrigger?: 'doubleClick' | 'arrow' | 'both';
135
138
  }
136
139
  interface FilterOption {
137
140
  value: string | number | boolean;
@@ -376,7 +379,7 @@ interface GridTableProps<T extends RowData = RowData> {
376
379
  getRowStyle?: (row: T, index: number) => React.CSSProperties;
377
380
  isRowDisabled?: (row: T) => boolean;
378
381
  isRowSelectable?: (row: T) => boolean;
379
- renderRowExpansion?: (row: T) => ReactNode;
382
+ renderRowExpansion?: (row: T, rowId: string | number) => ReactNode;
380
383
  renderHeader?: () => ReactNode;
381
384
  renderFooter?: () => ReactNode;
382
385
  renderEmpty?: () => ReactNode;
@@ -425,6 +428,119 @@ interface GridTableRef<T extends RowData = RowData> {
425
428
  exportData: (format: 'json' | 'csv') => void;
426
429
  }
427
430
 
431
+ interface TableContextState<T extends RowData = RowData> {
432
+ data: T[];
433
+ originalData: T[];
434
+ columns: ColumnDefinition<T>[];
435
+ columnStates: ColumnState[];
436
+ sorting: SortValue[];
437
+ filters: FilterValue[];
438
+ globalFilter: string;
439
+ page: number;
440
+ pageSize: number;
441
+ totalItems: number;
442
+ selectedIds: Set<string | number>;
443
+ expandedIds: Set<string | number>;
444
+ expandedCellIds: Set<string>;
445
+ autoSizedColumnIds: Set<string>;
446
+ loading: boolean;
447
+ error: Error | string | null;
448
+ theme: Theme;
449
+ translations: Translations;
450
+ currentBreakpoint: Breakpoint;
451
+ mobileBreakpoint: Breakpoint;
452
+ draggingColumnId: string | null;
453
+ resizingColumnId: string | null;
454
+ activeFilterColumnId: string | null;
455
+ showMobileDrawer: boolean;
456
+ mobileDrawerContent: 'filter' | 'sort' | 'columns' | null;
457
+ }
458
+ interface TableContextActions<T extends RowData = RowData> {
459
+ setData: (data: T[]) => void;
460
+ setLoading: (loading: boolean) => void;
461
+ setError: (error: Error | string | null) => void;
462
+ setSorting: (columnId: string, direction: SortDirection) => void;
463
+ toggleSorting: (columnId: string) => void;
464
+ clearSorting: () => void;
465
+ setFilter: (columnId: string, value: unknown, operator?: FilterOperator) => void;
466
+ removeFilter: (columnId: string) => void;
467
+ clearFilters: () => void;
468
+ setGlobalFilter: (value: string) => void;
469
+ setPage: (page: number) => void;
470
+ setPageSize: (pageSize: number) => void;
471
+ selectRow: (id: string | number) => void;
472
+ deselectRow: (id: string | number) => void;
473
+ toggleRow: (id: string | number) => void;
474
+ selectAll: () => void;
475
+ deselectAll: () => void;
476
+ expandRow: (id: string | number) => void;
477
+ collapseRow: (id: string | number) => void;
478
+ toggleRowExpansion: (id: string | number) => void;
479
+ toggleCellExpansion: (rowId: string | number, columnId: string) => void;
480
+ toggleColumnAutoSize: (columnId: string) => void;
481
+ reorderColumn: (sourceId: string, targetId: string) => void;
482
+ resizeColumn: (columnId: string, width: number) => void;
483
+ toggleColumnVisibility: (columnId: string) => void;
484
+ resetColumns: () => void;
485
+ setDraggingColumn: (columnId: string | null) => void;
486
+ setResizingColumn: (columnId: string | null) => void;
487
+ setActiveFilterColumn: (columnId: string | null) => void;
488
+ openMobileDrawer: (content: 'filter' | 'sort' | 'columns') => void;
489
+ closeMobileDrawer: () => void;
490
+ refresh: () => void;
491
+ reset: () => void;
492
+ }
493
+ type SubCellExpandTrigger = 'doubleClick' | 'arrow' | 'both';
494
+ interface TableOptions {
495
+ showOverflowTooltip?: boolean;
496
+ enableCellAutoSizeOnDoubleClick?: boolean;
497
+ subCellExpandTrigger?: SubCellExpandTrigger;
498
+ expandRowOnDoubleClick?: boolean;
499
+ globalFilterColumns?: string[];
500
+ }
501
+ interface TableContextValue<T extends RowData = RowData> {
502
+ state: TableContextState<T>;
503
+ actions: TableContextActions<T>;
504
+ tableOptions: TableOptions;
505
+ computed: {
506
+ filteredData: T[];
507
+ sortedData: T[];
508
+ paginatedData: T[];
509
+ visibleColumns: ColumnDefinition<T>[];
510
+ totalPages: number;
511
+ canGoNext: boolean;
512
+ canGoPrevious: boolean;
513
+ allSelected: boolean;
514
+ someSelected: boolean;
515
+ isMobile: boolean;
516
+ isTablet: boolean;
517
+ isDesktop: boolean;
518
+ };
519
+ }
520
+ interface TableProviderProps<T extends RowData = RowData> {
521
+ children: ReactNode;
522
+ data: T[];
523
+ columns: ColumnDefinition<T>[];
524
+ loading?: boolean;
525
+ error?: Error | string | null;
526
+ theme?: Partial<Theme>;
527
+ translations?: Partial<Translations>;
528
+ mobileBreakpoint?: Breakpoint;
529
+ paginationConfig?: PaginationConfig;
530
+ filterConfig?: FilterConfig;
531
+ sortConfig?: SortConfig;
532
+ enableMultiSort?: boolean;
533
+ enableRowSelection?: boolean;
534
+ enableMultiSelect?: boolean;
535
+ getRowId?: (row: T) => string | number;
536
+ onStateChange?: (state: TableContextState<T>) => void;
537
+ showOverflowTooltip?: boolean;
538
+ enableCellAutoSizeOnDoubleClick?: boolean;
539
+ subCellExpandTrigger?: SubCellExpandTrigger;
540
+ expandRowOnDoubleClick?: boolean;
541
+ globalFilterColumns?: string[];
542
+ }
543
+
428
544
  interface GridTableComponentProps<T extends RowData = RowData> {
429
545
  data: T[];
430
546
  columns: ColumnDefinition<T>[];
@@ -478,15 +594,23 @@ interface GridTableComponentProps<T extends RowData = RowData> {
478
594
  getRowClassName?: (row: T, index: number) => string;
479
595
  getRowStyle?: (row: T, index: number) => CSSProperties;
480
596
  isRowDisabled?: (row: T) => boolean;
481
- renderRowExpansion?: (row: T) => ReactNode;
597
+ renderRowExpansion?: (row: T, rowId: string | number) => ReactNode;
598
+ themeMode?: 'light' | 'dark' | 'system';
482
599
  renderHeader?: () => ReactNode;
483
600
  renderFooter?: () => ReactNode;
484
601
  tableRef?: RefObject<GridTableRef<T>>;
485
602
  className?: string;
486
603
  style?: CSSProperties;
604
+ showOverflowTooltip?: boolean;
605
+ enableCellAutoSizeOnDoubleClick?: boolean;
606
+ subCellExpandTrigger?: SubCellExpandTrigger;
607
+ expandRowOnDoubleClick?: boolean;
608
+ globalFilterColumns?: string[];
609
+ themeOverride?: Record<string, unknown>;
610
+ studio?: boolean;
487
611
  }
488
612
 
489
- declare function GridTable<T extends RowData = RowData>({ data, columns, loading, error, theme, translations, mobileBreakpoint, paginationConfig, filterConfig, sortConfig, enableMultiSelect, getRowId, ...props }: GridTableComponentProps<T>): ReactNode;
613
+ declare function GridTable<T extends RowData = RowData>({ data, columns, loading, error, theme, translations, mobileBreakpoint, paginationConfig, filterConfig, sortConfig, enableMultiSelect, getRowId, showOverflowTooltip, enableCellAutoSizeOnDoubleClick, subCellExpandTrigger, expandRowOnDoubleClick, themeMode, themeOverride, studio, ...props }: GridTableComponentProps<T>): ReactNode;
490
614
 
491
615
  interface GridHeaderProps<T extends RowData = RowData> {
492
616
  columns: ColumnDefinition<T>[];
@@ -499,6 +623,7 @@ interface GridHeaderProps<T extends RowData = RowData> {
499
623
  enableDragDrop?: boolean;
500
624
  enableResize?: boolean;
501
625
  enableSelection?: boolean;
626
+ enableExpansion?: boolean;
502
627
  allSelected?: boolean;
503
628
  someSelected?: boolean;
504
629
  onSelectAll?: () => void;
@@ -507,7 +632,7 @@ interface GridHeaderProps<T extends RowData = RowData> {
507
632
  getSortDirection?: (columnId: string) => SortDirection;
508
633
  }
509
634
 
510
- declare function GridHeader<T extends RowData = RowData>({ columns, columnStates, className, style, sticky, enableSort, enableFilter, enableDragDrop, enableResize, enableSelection, allSelected, someSelected, onSelectAll, onSort, onFilterOpen, getSortDirection, }: GridHeaderProps<T>): ReactNode;
635
+ declare function GridHeader<T extends RowData = RowData>({ columns, columnStates, className, style, sticky, enableSort, enableFilter, enableDragDrop, enableResize, enableSelection, enableExpansion, allSelected, someSelected, onSelectAll, onSort, onFilterOpen, getSortDirection, }: GridHeaderProps<T>): ReactNode;
511
636
 
512
637
  interface CellClickEvent<T extends RowData = RowData> {
513
638
  row: T;
@@ -519,6 +644,7 @@ interface GridCellProps<T extends RowData = RowData> {
519
644
  column: ColumnDefinition<T>;
520
645
  row: T;
521
646
  rowIndex: number;
647
+ rowId: string | number;
522
648
  value: unknown;
523
649
  width?: number | string;
524
650
  align?: Alignment;
@@ -552,7 +678,7 @@ interface GridBodyProps<T extends RowData = RowData> {
552
678
  getRowClassName?: (row: T, index: number) => string;
553
679
  getRowStyle?: (row: T, index: number) => CSSProperties;
554
680
  isRowDisabled?: (row: T) => boolean;
555
- renderRowExpansion?: (row: T) => ReactNode;
681
+ renderRowExpansion?: (row: T, rowId: string | number) => ReactNode;
556
682
  }
557
683
 
558
684
  declare function GridBody<T extends RowData = RowData>({ data, columns, columnStates, className, style, isMobile, showMobileLabels, enableSelection, enableExpansion, selectedIds, expandedIds, onRowClick, onRowDoubleClick, onCellClick, onRowSelect, onRowExpand, getRowId, getRowClassName, getRowStyle, isRowDisabled, renderRowExpansion, }: GridBodyProps<T>): ReactNode;
@@ -577,13 +703,13 @@ interface GridRowProps<T extends RowData = RowData> {
577
703
  onExpand?: (expanded: boolean) => void;
578
704
  enableSelection?: boolean;
579
705
  enableExpansion?: boolean;
580
- renderExpansion?: (row: T) => ReactNode;
706
+ renderExpansion?: (row: T, rowId: string | number) => ReactNode;
581
707
  getRowId: (row: T) => string | number;
582
708
  }
583
709
 
584
710
  declare function GridRow<T extends RowData = RowData>({ row, rowIndex, columns, columnStates, isSelected, isExpanded, isDisabled, isMobile, showMobileLabels, className, style, onClick, onDoubleClick, onContextMenu, onCellClick, onSelect, onExpand, enableSelection, enableExpansion, renderExpansion, getRowId, }: GridRowProps<T>): ReactNode;
585
711
 
586
- declare function GridCell<T extends RowData = RowData>({ column, row, rowIndex, value, width, align, className, style, showLabel, labelText, sticky, stickyOffset, onClick, }: GridCellProps<T>): ReactNode;
712
+ declare function GridCell<T extends RowData = RowData>({ column, row, rowIndex, rowId, value, width, align, className, style, showLabel, labelText, sticky, stickyOffset, onClick, }: GridCellProps<T>): ReactNode;
587
713
 
588
714
  interface PaginationProps {
589
715
  page: number;
@@ -638,103 +764,8 @@ interface MobileDrawerProps {
638
764
 
639
765
  declare function MobileDrawer({ isOpen, content, onClose, className, style, }: MobileDrawerProps): ReactNode;
640
766
 
641
- interface TableContextState<T extends RowData = RowData> {
642
- data: T[];
643
- originalData: T[];
644
- columns: ColumnDefinition<T>[];
645
- columnStates: ColumnState[];
646
- sorting: SortValue[];
647
- filters: FilterValue[];
648
- globalFilter: string;
649
- page: number;
650
- pageSize: number;
651
- totalItems: number;
652
- selectedIds: Set<string | number>;
653
- expandedIds: Set<string | number>;
654
- loading: boolean;
655
- error: Error | string | null;
656
- theme: Theme;
657
- translations: Translations;
658
- currentBreakpoint: Breakpoint;
659
- mobileBreakpoint: Breakpoint;
660
- draggingColumnId: string | null;
661
- resizingColumnId: string | null;
662
- activeFilterColumnId: string | null;
663
- showMobileDrawer: boolean;
664
- mobileDrawerContent: 'filter' | 'sort' | 'columns' | null;
665
- }
666
- interface TableContextActions<T extends RowData = RowData> {
667
- setData: (data: T[]) => void;
668
- setLoading: (loading: boolean) => void;
669
- setError: (error: Error | string | null) => void;
670
- setSorting: (columnId: string, direction: SortDirection) => void;
671
- toggleSorting: (columnId: string) => void;
672
- clearSorting: () => void;
673
- setFilter: (columnId: string, value: unknown, operator?: FilterOperator) => void;
674
- removeFilter: (columnId: string) => void;
675
- clearFilters: () => void;
676
- setGlobalFilter: (value: string) => void;
677
- setPage: (page: number) => void;
678
- setPageSize: (pageSize: number) => void;
679
- selectRow: (id: string | number) => void;
680
- deselectRow: (id: string | number) => void;
681
- toggleRow: (id: string | number) => void;
682
- selectAll: () => void;
683
- deselectAll: () => void;
684
- expandRow: (id: string | number) => void;
685
- collapseRow: (id: string | number) => void;
686
- toggleRowExpansion: (id: string | number) => void;
687
- reorderColumn: (sourceId: string, targetId: string) => void;
688
- resizeColumn: (columnId: string, width: number) => void;
689
- toggleColumnVisibility: (columnId: string) => void;
690
- resetColumns: () => void;
691
- setDraggingColumn: (columnId: string | null) => void;
692
- setResizingColumn: (columnId: string | null) => void;
693
- setActiveFilterColumn: (columnId: string | null) => void;
694
- openMobileDrawer: (content: 'filter' | 'sort' | 'columns') => void;
695
- closeMobileDrawer: () => void;
696
- refresh: () => void;
697
- reset: () => void;
698
- }
699
- interface TableContextValue<T extends RowData = RowData> {
700
- state: TableContextState<T>;
701
- actions: TableContextActions<T>;
702
- computed: {
703
- filteredData: T[];
704
- sortedData: T[];
705
- paginatedData: T[];
706
- visibleColumns: ColumnDefinition<T>[];
707
- totalPages: number;
708
- canGoNext: boolean;
709
- canGoPrevious: boolean;
710
- allSelected: boolean;
711
- someSelected: boolean;
712
- isMobile: boolean;
713
- isTablet: boolean;
714
- isDesktop: boolean;
715
- };
716
- }
717
- interface TableProviderProps<T extends RowData = RowData> {
718
- children: ReactNode;
719
- data: T[];
720
- columns: ColumnDefinition<T>[];
721
- loading?: boolean;
722
- error?: Error | string | null;
723
- theme?: Partial<Theme>;
724
- translations?: Partial<Translations>;
725
- mobileBreakpoint?: Breakpoint;
726
- paginationConfig?: PaginationConfig;
727
- filterConfig?: FilterConfig;
728
- sortConfig?: SortConfig;
729
- enableMultiSort?: boolean;
730
- enableRowSelection?: boolean;
731
- enableMultiSelect?: boolean;
732
- getRowId?: (row: T) => string | number;
733
- onStateChange?: (state: TableContextState<T>) => void;
734
- }
735
-
736
767
  declare const TableContext: react.Context<TableContextValue<RowData> | null>;
737
- declare function TableProvider<T extends RowData>({ children, data, columns, loading, error, theme, translations, mobileBreakpoint, paginationConfig, filterConfig: _filterConfig, sortConfig: _sortConfig, enableMultiSort, getRowId, onStateChange, }: TableProviderProps<T>): ReactNode;
768
+ declare function TableProvider<T extends RowData>({ children, data, columns, loading, error, theme, translations, mobileBreakpoint, paginationConfig, filterConfig: _filterConfig, sortConfig: _sortConfig, enableMultiSort, getRowId, onStateChange, showOverflowTooltip, enableCellAutoSizeOnDoubleClick, subCellExpandTrigger, expandRowOnDoubleClick, globalFilterColumns, }: TableProviderProps<T>): ReactNode;
738
769
  declare function useTableContext<T extends RowData = RowData>(): TableContextValue<T>;
739
770
 
740
771
  interface UseSortReturn {