@kong-ui-public/table-data-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.
Files changed (48) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +564 -0
  3. package/dist/style.css +1 -0
  4. package/dist/table-data-grid.es.js +2566 -0
  5. package/dist/table-data-grid.umd.js +1 -0
  6. package/dist/types/components/TableDataGrid.vue.d.ts +99 -0
  7. package/dist/types/components/TableDataGrid.vue.d.ts.map +1 -0
  8. package/dist/types/components/TableDataGridCellRenderer.vue.d.ts +21 -0
  9. package/dist/types/components/TableDataGridCellRenderer.vue.d.ts.map +1 -0
  10. package/dist/types/components/TableDataGridColumnVisibilityMenu.vue.d.ts +26 -0
  11. package/dist/types/components/TableDataGridColumnVisibilityMenu.vue.d.ts.map +1 -0
  12. package/dist/types/components/TableDataGridFilters.vue.d.ts +36 -0
  13. package/dist/types/components/TableDataGridFilters.vue.d.ts.map +1 -0
  14. package/dist/types/components/TableDataGridHeaderRenderer.vue.d.ts +13 -0
  15. package/dist/types/components/TableDataGridHeaderRenderer.vue.d.ts.map +1 -0
  16. package/dist/types/components/TableDataGridSearch.vue.d.ts +11 -0
  17. package/dist/types/components/TableDataGridSearch.vue.d.ts.map +1 -0
  18. package/dist/types/composables/index.d.ts +182 -0
  19. package/dist/types/composables/index.d.ts.map +1 -0
  20. package/dist/types/composables/useDatatableColumnDefs.d.ts +21 -0
  21. package/dist/types/composables/useDatatableColumnDefs.d.ts.map +1 -0
  22. package/dist/types/composables/useDatatableColumnSizing.d.ts +31 -0
  23. package/dist/types/composables/useDatatableColumnSizing.d.ts.map +1 -0
  24. package/dist/types/composables/useDatatableGridSync.d.ts +63 -0
  25. package/dist/types/composables/useDatatableGridSync.d.ts.map +1 -0
  26. package/dist/types/composables/useDatatablePagination.d.ts +17 -0
  27. package/dist/types/composables/useDatatablePagination.d.ts.map +1 -0
  28. package/dist/types/composables/useDatatableSelection.d.ts +18 -0
  29. package/dist/types/composables/useDatatableSelection.d.ts.map +1 -0
  30. package/dist/types/composables/useI18n.d.ts +9 -0
  31. package/dist/types/composables/useI18n.d.ts.map +1 -0
  32. package/dist/types/composables/useTableDataGridConfig.d.ts +24 -0
  33. package/dist/types/composables/useTableDataGridConfig.d.ts.map +1 -0
  34. package/dist/types/composables/useTableDataGridFetchers.d.ts +33 -0
  35. package/dist/types/composables/useTableDataGridFetchers.d.ts.map +1 -0
  36. package/dist/types/index.d.ts +6 -0
  37. package/dist/types/index.d.ts.map +1 -0
  38. package/dist/types/types/index.d.ts +89 -0
  39. package/dist/types/types/index.d.ts.map +1 -0
  40. package/dist/types/utils/headers.d.ts +3 -0
  41. package/dist/types/utils/headers.d.ts.map +1 -0
  42. package/dist/types/utils/rowKey.d.ts +3 -0
  43. package/dist/types/utils/rowKey.d.ts.map +1 -0
  44. package/dist/types/utils/tableConfig.d.ts +35 -0
  45. package/dist/types/utils/tableConfig.d.ts.map +1 -0
  46. package/dist/types/utils/tablePreferencesInterop.d.ts +6 -0
  47. package/dist/types/utils/tablePreferencesInterop.d.ts.map +1 -0
  48. package/package.json +79 -0
package/README.md ADDED
@@ -0,0 +1,564 @@
1
+ # @kong-ui-public/table-data-grid
2
+
3
+ Reusable AG Grid wrapper for Vue table data grids.
4
+
5
+ ## Core Features
6
+
7
+ - Server-driven pagination with known or unknown total row counts
8
+ - Infinite cursor loading for scroll-driven datasets
9
+ - Typed headers and fetcher contracts
10
+ - Slotted cell rendering by column key
11
+ - Controlled or internal table configuration state
12
+ - Column ordering, visibility, pinning, sorting, sizing, and fit-to-width behavior
13
+ - Row click, cell click, single-row selection, multi-row selection, and disabled selection modes
14
+ - Bulk action slot for selected rows
15
+ - Header-driven `KFilterGroup` filters
16
+ - Opt-in server-driven search
17
+ - Composable toolbar slots for host-owned controls
18
+ - Row and cell attribute hooks for test ids, accessibility, and integration-specific metadata
19
+ - Built-in loading, empty, and error states with override slots
20
+ - Table preference and sort payload conversion helpers
21
+ - `agGridOptions` escape hatch for lower-level AG Grid configuration
22
+
23
+ ## Dependencies
24
+
25
+ Install the package in the host application:
26
+
27
+ ```sh
28
+ pnpm add @kong-ui-public/table-data-grid
29
+ ```
30
+
31
+ Host applications must provide these peer dependencies:
32
+
33
+ - `vue`
34
+ - `@kong-ui-public/i18n`
35
+ - `@kong/icons`
36
+ - `@kong/kongponents`
37
+
38
+ The package depends directly on these AG Grid packages, so consumers do not need to install them separately:
39
+
40
+ - `ag-grid-community`
41
+ - `ag-grid-vue3`
42
+
43
+ ## Usage
44
+
45
+ ### Basic Paginated Table
46
+
47
+ ```vue
48
+ <template>
49
+ <TableDataGrid
50
+ v-model:filter-selection="filterSelection"
51
+ :fetcher="fetchRows"
52
+ :headers="headers"
53
+ mode="pagination"
54
+ row-key="id"
55
+ row-selection="multiple"
56
+ :table-config="tableConfig"
57
+ @row:select="selectedRows = $event"
58
+ @update:table-config="tableConfig = $event"
59
+ >
60
+ <template #status="{ row }">
61
+ <KBadge :appearance="row.status >= 500 ? 'danger' : 'success'">
62
+ {{ row.status }}
63
+ </KBadge>
64
+ </template>
65
+ </TableDataGrid>
66
+ </template>
67
+
68
+ <script setup lang="ts">
69
+ import type {
70
+ TableDataGridConfig,
71
+ TableDataGridFetcher,
72
+ TableDataGridHeader,
73
+ } from '@kong-ui-public/table-data-grid'
74
+ import type { FilterGroupSelection } from '@kong/kongponents'
75
+ import { ref } from 'vue'
76
+ import { TableDataGrid } from '@kong-ui-public/table-data-grid'
77
+
78
+ type Row = {
79
+ id: string
80
+ route: string
81
+ status: number
82
+ latency: number
83
+ }
84
+
85
+ const headers: Array<TableDataGridHeader<Row>> = [
86
+ { key: 'route', label: 'Route', sortable: true },
87
+ { key: 'status', label: 'Status', sortable: true, width: 120 },
88
+ { key: 'latency', label: 'Latency', sortable: true, tooltip: 'Latency in milliseconds' },
89
+ ]
90
+
91
+ const tableConfig = ref<TableDataGridConfig>()
92
+ const filterSelection = ref<FilterGroupSelection>({})
93
+ const selectedRows = ref<Row[]>([])
94
+
95
+ const fetchRows: TableDataGridFetcher<Row> = async ({
96
+ page = 1,
97
+ pageSize,
98
+ sortColumnKey,
99
+ sortColumnOrder,
100
+ filterSelection,
101
+ }) => {
102
+ const response = await fetch('/api/requests', {
103
+ method: 'POST',
104
+ body: JSON.stringify({ page, pageSize, sortColumnKey, sortColumnOrder, filterSelection }),
105
+ })
106
+
107
+ return response.json()
108
+ }
109
+ </script>
110
+ ```
111
+
112
+ The fetcher must return `{ data, total?, cursor?, hasMore? }`. Paginated tables use `page`, `pageSize`, `sortColumnKey`, `sortColumnOrder`, `search`, and `filterSelection`; infinite tables use `startRow`, `endRow`, `cursor`, `pageSize`, `sortColumnKey`, `sortColumnOrder`, `search`, and `filterSelection`.
113
+
114
+ ### Infinite Loading
115
+
116
+ ```vue
117
+ <TableDataGrid
118
+ :fetcher="fetchRows"
119
+ :headers="headers"
120
+ mode="infinite"
121
+ row-key="id"
122
+ />
123
+ ```
124
+
125
+ ```ts
126
+ const fetchRows: TableDataGridFetcher<Row> = async ({ startRow = 0, pageSize, cursor }) => {
127
+ const result = await fetchRequests({ cursor, limit: pageSize, offset: startRow })
128
+
129
+ return {
130
+ data: result.items,
131
+ cursor: result.nextCursor,
132
+ hasMore: result.hasMore,
133
+ }
134
+ }
135
+ ```
136
+
137
+ ### Filterable Headers
138
+
139
+ Headers can include `filter?: Filter` from `@kong/kongponents`. When at least one header has a filter definition, the table renders `KFilterGroup` in the toolbar.
140
+
141
+ ```ts
142
+ const headers: Array<TableDataGridHeader<Row>> = [
143
+ {
144
+ key: 'status',
145
+ label: 'Status',
146
+ filter: {
147
+ label: 'Status',
148
+ multiple: true,
149
+ options: [
150
+ { label: '200', value: '200' },
151
+ { label: '500', value: '500' },
152
+ ],
153
+ },
154
+ },
155
+ ]
156
+ ```
157
+
158
+ Applied and cleared default filters update `v-model:filter-selection` and are passed to the fetcher as `filterSelection`.
159
+
160
+ Pass `outside-filters` to move the built-in filters to a Vue Teleport target instead of rendering them in the toolbar.
161
+
162
+ ```vue
163
+ <div id="table-data-grid-filter-bar" />
164
+
165
+ <TableDataGrid
166
+ v-model:filter-selection="filterSelection"
167
+ :fetcher="fetchRows"
168
+ :headers="headers"
169
+ outside-filters="#table-data-grid-filter-bar"
170
+ />
171
+ ```
172
+
173
+ `TableDataGrid` forwards KFilterGroup custom slots named `filter-${header.key}`. These slots follow the [Kongponents KFilterGroup filter content contract](https://kongponents.konghq.com/components/filter-group.html#filter-content): use the matching filter key to target the `filter-*` slot, and when custom content is provided, KFilterGroup cannot determine that filter's selected value.
174
+
175
+ This means custom filter slots are host-owned. The custom slot receives no slot props, and the host app must update that filter's `selection.*` entry in `v-model:filter-selection`. For custom filter slots, update the model from the host's `filter:apply` listener. `TableDataGrid` also treats custom filter clears as host-owned so apply and clear follow the same contract and the wrapper does not overwrite custom state with a built-in selection payload.
176
+
177
+ ```vue
178
+ <TableDataGrid
179
+ v-model:filter-selection="filterSelection"
180
+ :fetcher="fetchRows"
181
+ :headers="headers"
182
+ @filter:apply="handleFilterApply"
183
+ @filter:clear="handleFilterClear"
184
+ >
185
+ <template #filter-method>
186
+ <KButton
187
+ v-for="method in ['GET', 'POST', 'PUT', 'DELETE']"
188
+ :key="method"
189
+ @click="selectedMethod = method"
190
+ >
191
+ {{ method }}
192
+ </KButton>
193
+ </template>
194
+ </TableDataGrid>
195
+ ```
196
+
197
+ ```ts
198
+ const handleFilterApply = (filterKey: string) => {
199
+ if (filterKey !== 'method') {
200
+ return
201
+ }
202
+
203
+ filterSelection.value = {
204
+ ...filterSelection.value,
205
+ method: {
206
+ operator: 'eq',
207
+ text: selectedMethod.value,
208
+ value: selectedMethod.value,
209
+ },
210
+ }
211
+ }
212
+
213
+ const handleFilterClear = (filterKey: string) => {
214
+ if (filterKey !== 'method') {
215
+ return
216
+ }
217
+
218
+ filterSelection.value = {
219
+ ...filterSelection.value,
220
+ method: undefined,
221
+ }
222
+ }
223
+ ```
224
+
225
+ `filter:apply` and `filter:clear` are notifications. `v-model:filter-selection` remains the canonical state for active filters and fetcher input.
226
+
227
+ ### Search
228
+
229
+ Set `enable-search` to render a `KInput` search control. Search changes are debounced before refreshing the fetcher, and the fetcher receives the current value as `search`.
230
+
231
+ ```vue
232
+ <TableDataGrid
233
+ enable-search
234
+ :fetcher="fetchRows"
235
+ :headers="headers"
236
+ />
237
+ ```
238
+
239
+ Pass `outside-search` to move the built-in search control to a Vue Teleport target instead of rendering it in the toolbar.
240
+
241
+ ```vue
242
+ <div id="table-data-grid-search-bar" />
243
+
244
+ <TableDataGrid
245
+ enable-search
246
+ :fetcher="fetchRows"
247
+ :headers="headers"
248
+ outside-search="#table-data-grid-search-bar"
249
+ />
250
+ ```
251
+
252
+ ### Controlled Table Config
253
+
254
+ The table owns internal config state when `table-config` is omitted. Provide `table-config` and listen for `update:table-config` when the host needs to initialize, track, or persist user table preferences.
255
+
256
+ ```vue
257
+ <TableDataGrid
258
+ :fetcher="fetchRows"
259
+ :headers="headers"
260
+ :table-config="tableConfig"
261
+ @update:table-config="saveTableConfig"
262
+ />
263
+ ```
264
+
265
+ ```ts
266
+ const saveTableConfig = (config: TableDataGridConfig) => {
267
+ tableConfig.value = config
268
+ localStorage.setItem('requests-table-config', JSON.stringify(config))
269
+ }
270
+ ```
271
+
272
+ Config fields are optional overrides. For example, `columnVisibility: { latency: false }` hides only that column while the rest default to visible.
273
+
274
+ Host applications can map Kongponents `tablePreferences` into `tableConfig` with the exported conversion helpers:
275
+
276
+ ```ts
277
+ import {
278
+ toTableDataGridConfig,
279
+ toTablePreferences,
280
+ toTableSortPayload,
281
+ } from '@kong-ui-public/table-data-grid'
282
+ ```
283
+
284
+ ### Row Selection And Bulk Actions
285
+
286
+ ```vue
287
+ <TableDataGrid
288
+ :fetcher="fetchRows"
289
+ :headers="headers"
290
+ row-selection="multiple"
291
+ @row:select="selectedRows = $event"
292
+ >
293
+ <template #bulk-action-items="{ selectedRows }">
294
+ <KDropdownItem @click="deleteRows(selectedRows)">
295
+ Delete {{ selectedRows.length }} rows
296
+ </KDropdownItem>
297
+ </template>
298
+ </TableDataGrid>
299
+ ```
300
+
301
+ Use `row-selection="single"` for click-based single row selection, `row-selection="multiple"` for checkbox-backed multi-row selection, and `row-selection="none"` to disable selection UI and selection events.
302
+
303
+ Use `headers[].disableRowClick` for action or control columns that should not emit `row:click` while the rest of the row remains clickable.
304
+
305
+ ```ts
306
+ const headers: Array<TableDataGridHeader<Row>> = [
307
+ { key: 'route', label: 'Route', sortable: true },
308
+ { key: 'status', label: 'Status', sortable: true },
309
+ {
310
+ key: 'actions',
311
+ label: 'Actions',
312
+ disableRowClick: true,
313
+ width: 120,
314
+ },
315
+ ]
316
+ ```
317
+
318
+ ```vue
319
+ <TableDataGrid
320
+ :fetcher="fetchRows"
321
+ :headers="headers"
322
+ row-selection="multiple"
323
+ @row:click="openRequest"
324
+ @row:select="selectedRows = $event"
325
+ >
326
+ <template #actions="{ row }">
327
+ <KButton
328
+ appearance="tertiary"
329
+ size="small"
330
+ @click="inspectRequest(row)"
331
+ >
332
+ Inspect
333
+ </KButton>
334
+ </template>
335
+ </TableDataGrid>
336
+ ```
337
+
338
+ Clicking the `actions` column slot does not emit `row:click`, but normal AG Grid selection behavior is preserved.
339
+
340
+ ### Cell Layout
341
+
342
+ Cells are vertically centered by default. The wrapper stretches AG Grid cell wrappers and the package cell renderer so slotted content can fill the row height.
343
+
344
+ Host apps can customize per-cell content layout with `cellAttrs`:
345
+
346
+ ```vue
347
+ <TableDataGrid
348
+ :cell-attrs="getCellAttrs"
349
+ :fetcher="fetchRows"
350
+ :headers="headers"
351
+ />
352
+ ```
353
+
354
+ ```ts
355
+ const getCellAttrs: TableDataGridCellAttrs<Row> = ({ column }) => ({
356
+ class: {
357
+ 'requests-table-cell-right': column.key === 'latency',
358
+ },
359
+ style: column.key === 'latency'
360
+ ? { justifyContent: 'flex-end', textAlign: 'right' }
361
+ : undefined,
362
+ })
363
+ ```
364
+
365
+ Use `headers[].agGridColumnOptions.cellClass` or `headers[].agGridColumnOptions.cellStyle` when the host must style the underlying AG Grid cell instead of the rendered cell content.
366
+
367
+ ### Custom States
368
+
369
+ ```vue
370
+ <TableDataGrid
371
+ :error="hasError"
372
+ :fetcher="fetchRows"
373
+ :headers="headers"
374
+ >
375
+ <template #empty-state>
376
+ <KEmptyState title="No requests found" />
377
+ </template>
378
+
379
+ <template #error-state>
380
+ <KEmptyState
381
+ icon-variant="error"
382
+ title="Requests failed to load"
383
+ />
384
+ </template>
385
+ </TableDataGrid>
386
+ ```
387
+
388
+ ### Toolbar Composition
389
+
390
+ ```vue
391
+ <TableDataGrid
392
+ :fetcher="fetchRows"
393
+ :headers="headers"
394
+ :hide-bulk-actions="true"
395
+ >
396
+ <template #toolbar-left>
397
+ <EntityFilter />
398
+ </template>
399
+
400
+ <template #toolbar-right>
401
+ <KButton @click="downloadRows">
402
+ Download
403
+ </KButton>
404
+ </template>
405
+ </TableDataGrid>
406
+ ```
407
+
408
+ Use `toolbar` to replace the host-controlled toolbar content, or `toolbar-left` / `toolbar-right` to compose host controls alongside built-in search, filters, and bulk actions. The column visibility menu stays the rightmost toolbar control unless `hideColumnVisibility` is true.
409
+
410
+ Use `outside-search` and `outside-filters` to move built-in controls to Teleport targets. Use `outside-actions` for custom toolbar-adjacent controls that must stay mounted outside the visible table toolbar. The slot is mounted even when `hideToolbar` is true and while the table is loading, empty, or in an error state.
411
+
412
+ ```vue
413
+ <TableDataGrid
414
+ enable-search
415
+ :fetcher="fetchRows"
416
+ :headers="headers"
417
+ hide-toolbar
418
+ >
419
+ <template
420
+ #outside-actions="{
421
+ refresh,
422
+ search,
423
+ selectedRows,
424
+ updateSearch,
425
+ }"
426
+ >
427
+ <Teleport to="#kong-ui-app-page-header-action-button">
428
+ <KInput
429
+ :model-value="search"
430
+ type="search"
431
+ @update:model-value="updateSearch"
432
+ />
433
+
434
+ <KButton @click="refresh">
435
+ Refresh {{ selectedRows.length ? `(${selectedRows.length})` : '' }}
436
+ </KButton>
437
+ </Teleport>
438
+ </template>
439
+ </TableDataGrid>
440
+ ```
441
+
442
+ ## Props
443
+
444
+ | Prop | Type | Required | Default | Notes |
445
+ | --- | --- | --- | --- | --- |
446
+ | `headers` | `Array<TableDataGridHeader<Row>>` | Yes | - | Column definitions used to build AG Grid columns, slots, filters, and table config defaults. |
447
+ | `fetcher` | `TableDataGridFetcher<Row>` | Yes | - | Async function called whenever the table needs rows. |
448
+ | `mode` | `'pagination' \| 'infinite'` | No | `'pagination'` | Controls whether the table renders pagination or an infinite datasource. |
449
+ | `rowKey` | `Extract<keyof Row, string>` | No | `'id'` | Row field used for AG Grid row identity and exposed selection methods. |
450
+ | `pageSize` | `number` | No | `25` | Default page or block size when `tableConfig.pageSize` is not set. |
451
+ | `initialFetcherParams` | `{ search?: string }` | No | `{}` | Initial fetcher params currently limited to `search`. |
452
+ | `loading` | `boolean` | No | `false` | Shows the table skeleton before rendering the grid. |
453
+ | `error` | `boolean` | No | `false` | Shows the error state instead of the grid. |
454
+ | `enableSearch` | `boolean` | No | `false` | Renders the built-in search input and passes debounced search changes to the fetcher. |
455
+ | `outsideSearch` | `string \| HTMLElement` | No | - | Vue Teleport target for the built-in search input. |
456
+ | `outsideFilters` | `string \| HTMLElement` | No | - | Vue Teleport target for the built-in filters. |
457
+ | `hideToolbar` | `boolean` | No | `false` | Hides toolbar-rendered search, filters, selected-row actions, and the column visibility menu. Built-in controls with outside targets remain mounted. |
458
+ | `hideBulkActions` | `boolean` | No | `false` | Hides the selected-row actions dropdown while leaving the rest of the toolbar visible. |
459
+ | `hideColumnVisibility` | `boolean` | No | `false` | Hides the column visibility menu while leaving the rest of the toolbar visible. |
460
+ | `hidePagination` | `boolean` | No | `false` | Hides pagination controls in pagination mode. |
461
+ | `hidePaginationWhenOptional` | `boolean` | No | `false` | Hides pagination controls when the loaded dataset fits on one page. |
462
+ | `rowSelection` | `'none' \| 'single' \| 'multiple'` | No | `'none'` | Enables no selection, click-based single selection, or checkbox-backed multi-selection. |
463
+ | `agGridOptions` | `TableDataGridGridOptions<Row>` | No | `{}` | Pass-through for lower-level AG Grid options. The wrapper still owns its documented behavior. |
464
+ | `paginationPageSizeOptions` | `number[]` | No | `[10, 15, 25, 50, 100]` | Page size choices shown in pagination mode. The active page size is included if missing. |
465
+ | `refreshKey` | `string \| number` | No | - | Refetches when the key changes. |
466
+ | `rowAttrs` | `(row: Row) => Record<string, unknown>` | No | - | Applies DOM attributes to rendered AG Grid rows. |
467
+ | `cellAttrs` | `({ row, rowValue, column, rowIndex, colIndex }) => Record<string, unknown>` | No | - | Applies DOM attributes to rendered cell content. |
468
+ | `tableConfig` | `TableDataGridConfig` | No | Internal state | Optional controlled table state for column order, visibility, widths, pinning, sort, and page size. |
469
+
470
+ ## Header Options
471
+
472
+ | Field | Type | Default | Notes |
473
+ | --- | --- | --- | --- |
474
+ | `key` | `string` | - | Column key. Also controls the dynamic cell slot name. |
475
+ | `label` | `string` | - | Header label. |
476
+ | `sortable` | `boolean` | `false` | Enables AG Grid sorting and wrapper sort payload updates for the column. |
477
+ | `hideable` | `boolean` | `true` | Allows the column visibility menu to hide or show the column. |
478
+ | `hideLabel` | `boolean` | `false` | Hides the visual header label while keeping the column key and slot mapping. |
479
+ | `disableRowClick` | `boolean` | `false` | Suppresses `row:click` when clicks originate from this column without changing selection behavior. |
480
+ | `tooltip` | `string` | - | Header tooltip text and fallback cell tooltip text. |
481
+ | `width` / `minWidth` / `maxWidth` | `number` | - | AG Grid column sizing constraints in pixels. |
482
+ | `resizable` | `boolean` | `true` | Enables AG Grid column resizing. |
483
+ | `draggable` | `boolean` | `true` | Enables column reordering. |
484
+ | `pinned` | `'left' \| 'right' \| false` | `false` | Initial pinned column state. |
485
+ | `filter` | `Filter` | - | KFilterGroup filter definition for the column. |
486
+ | `agGridColumnOptions` | `Partial<ColDef<Row>>` | - | Escape hatch for lower-level AG Grid column options. These options can override package defaults and bypass wrapper guarantees. |
487
+
488
+ ## Models
489
+
490
+ | Model | Type | Default | Notes |
491
+ | --- | --- | --- | --- |
492
+ | `v-model:filter-selection` | `FilterGroupSelection` | `{}` | Canonical active filter state. Default filters update it when applied or cleared. Custom filter slots are host-owned, so the host must update this model on custom apply and clear. Passed to the fetcher as `filterSelection`. |
493
+
494
+ ## Events
495
+
496
+ | Event | Payload | When it fires |
497
+ | --- | --- | --- |
498
+ | `row:click` | `(row: Row, event: RowClickedEvent<Row>)` | A row is clicked and the row has data. |
499
+ | `cell:click` | `{ row: Row, columnKey: string, value: any }` | A cell is clicked and the cell has row data and a column id. |
500
+ | `row:select` | `Row[]` | The selected row set changes. |
501
+ | `update:tableConfig` | `TableDataGridConfig` | User interaction or wrapper-managed refitting changes table config state. |
502
+ | `sort` | `{ sortColumnKey?: string, sortColumnOrder?: 'asc' \| 'desc' }` | Sort state changes before the updated table config is emitted. |
503
+ | `state` | `{ state: 'loading' \| 'error' \| 'success' \| 'empty', hasData: boolean }` | The table loading, error, success, or empty state changes. |
504
+ | `grid:ready` | `GridApi<Row>` | AG Grid is ready and the wrapper has applied initial column state. |
505
+ | `filter:apply` | `(filterKey: string, selection: FilterGroupSelection)` | KFilterGroup emits apply. Default filters update `v-model:filter-selection`; custom filter slots must write their selected value to the model in the host. |
506
+ | `filter:clear` | `(filterKey: string, selection: FilterGroupSelection)` | KFilterGroup emits clear. Default filters update `v-model:filter-selection`; custom filter slots must clear their value from the model in the host. |
507
+ | `filter:open` | `filterKey: string` | A KFilterGroup filter popover opens. |
508
+ | `filter:close` | `filterKey: string` | A KFilterGroup filter popover closes. |
509
+
510
+ ## Slots
511
+
512
+ | Slot | Props | Notes |
513
+ | --- | --- | --- |
514
+ | `[header.key]` | `{ row, rowValue, column, rowIndex, refreshCell, selected }` | Dynamic cell slot for the matching column key. Call `refreshCell()` when async slot content changes internal loading or display state. |
515
+ | `bulk-action-items` | `{ selectedRows }` | Dropdown items rendered inside the selected-row actions dropdown. |
516
+ | `toolbar` | `{ selectedRows, filterSelection, filters, search, updateFilterSelection, updateSearch, refresh }` | Replaces the host-controlled toolbar content. The column visibility menu still renders as the rightmost toolbar control unless `hideColumnVisibility` is true. |
517
+ | `toolbar-left` | `{ selectedRows, filterSelection, filters, search, updateFilterSelection, updateSearch, refresh }` | Renders before built-in bulk actions, search, and filters. |
518
+ | `toolbar-right` | `{ selectedRows, filterSelection, filters, search, updateFilterSelection, updateSearch, refresh }` | Renders before the built-in column visibility menu. |
519
+ | `outside-actions` | `{ selectedRows, filterSelection, filters, search, updateFilterSelection, updateSearch, refresh }` | Always-mounted renderless slot for custom controls that host apps Teleport outside the visible table toolbar. |
520
+ | `filter-${header.key}` | None | Forwards custom filter popover content to the built-in KFilterGroup. The host app must update `v-model:filter-selection` when custom filter values are applied or cleared. |
521
+ | `empty-state` | None | Replaces the default empty state. |
522
+ | `error-state` | None | Replaces the default error state. |
523
+
524
+ ## Exposed Methods
525
+
526
+ Use a template ref on `TableDataGrid` to call these methods.
527
+
528
+ | Method | Signature | Notes |
529
+ | --- | --- | --- |
530
+ | `refresh` | `() => void` | Refetches the first page or rebuilds the infinite datasource. |
531
+ | `selectRowByKey` | `(key: string) => void` | Selects the row with the matching `rowKey` value when it is currently loaded. |
532
+ | `deselectAll` | `() => void` | Clears AG Grid selection and emits `row:select` with `[]`. |
533
+ | `getGridApi` | `() => GridApi<Row> \| undefined` | Returns the underlying AG Grid API after `grid:ready`. |
534
+
535
+ ## Exported Types
536
+
537
+ - `TableDataGridMode`
538
+ - `TableDataGridSort`
539
+ - `TableDataGridSortColumnOrder`
540
+ - `TableDataGridPinnedState`
541
+ - `TableDataGridState`
542
+ - `TableDataGridStatePayload`
543
+ - `TableDataGridConfig`
544
+ - `TableDataGridHeader`
545
+ - `TableDataGridFetcherParams`
546
+ - `TableDataGridFetcherResult`
547
+ - `TableDataGridFetcher`
548
+ - `TableDataGridRowSelectionMode`
549
+ - `TableDataGridRowKey`
550
+ - `TableDataGridTeleportTarget`
551
+ - `TableDataGridCellSlotProps`
552
+ - `TableDataGridRowAttrs`
553
+ - `TableDataGridCellAttrs`
554
+ - `TableDataGridGridOptions`
555
+
556
+ ## Sandbox
557
+
558
+ Run the package sandbox to test the interactive mock-data playground:
559
+
560
+ ```sh
561
+ pnpm --filter @kong-ui-public/table-data-grid run dev
562
+ ```
563
+
564
+ The sandbox includes an `Actions` column that uses `disableRowClick: true`. Click the `Inspect` button in that column to verify action-cell clicks do not emit `row:click`, while row selection still updates the selected-row debug panel.
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ .datatable-cell-content[data-v-0e73bfa7]{align-items:center;display:flex;height:100%;min-width:0;width:100%}.table-data-grid-header{align-items:center;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;box-shadow:none;box-sizing:border-box;color:#6c7489;color:var(--kui-color-text-neutral, #6c7489);display:inline-flex;flex-wrap:nowrap;font-family:Inter,Roboto,Helvetica,sans-serif;font-family:var(--kui-font-family-text, "Inter", Roboto, Helvetica, sans-serif);font-size:14px;font-size:var(--kui-font-size-30, 14px);font-weight:600;font-weight:var(--kui-font-weight-semibold, 600);gap:6px;gap:var(--kui-space-30, 6px);height:100%;line-height:20px;line-height:var(--kui-line-height-30, 20px);margin:0;min-width:0;padding:0;text-align:left;width:100%}.table-data-grid-header.is-sortable{cursor:pointer}.table-data-grid-header.is-sorted{color:#0044f4;color:var(--kui-color-text-primary, #0044f4)}.table-data-grid-header:focus{outline:none}.table-data-grid-header:focus-visible{border-radius:2px;border-radius:var(--kui-border-radius-10, 2px);box-shadow:0 0 0 4px #0044f433;box-shadow:var(--kui-shadow-focus, 0px 0px 0px 4px rgba(0, 68, 244, .2))}.header-label{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.header-tooltip,.header-tooltip-trigger,.header-sort-icon{display:inline-flex;flex-shrink:0}.header-tooltip-trigger{cursor:help}.header-tooltip-icon{color:#6c7489;color:var(--kui-color-text-neutral, #6c7489)}.header-sort-icon{color:#afb7c5;color:var(--kui-color-text-neutral-weak, #afb7c5)}.is-sorted .header-sort-icon{color:#0044f4;color:var(--kui-color-text-primary, #0044f4)}.header-tooltip :deep(svg),.header-sort-icon :deep(svg){color:currentColor;display:block;flex-shrink:0}.table-data-grid-column-visibility-menu[data-v-c7a3d29f]{line-height:12px;line-height:var(--kui-line-height-10, 12px)}.table-data-grid-column-visibility-menu .column-search-wrapper[data-v-c7a3d29f]{padding:6px;padding:var(--kui-space-30, 6px)}.table-data-grid-column-visibility-menu[data-v-c7a3d29f] .k-input.column-search{max-width:100%;width:100%}.table-data-grid-column-visibility-menu[data-v-c7a3d29f] .k-input.column-search ::-webkit-search-cancel-button{-webkit-appearance:none}.table-data-grid-column-visibility-menu .column-items[data-v-c7a3d29f]{max-height:250px;overflow-y:auto}.table-data-grid-column-visibility-menu .column-visibility-item[data-v-c7a3d29f]{align-items:center;display:flex}.table-data-grid-column-visibility-menu .column-label[data-v-c7a3d29f]{cursor:pointer;display:block;max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.table-data-grid-column-visibility-menu .column-actions[data-v-c7a3d29f]{display:flex;justify-content:flex-end;padding:4px;padding:var(--kui-space-20, 4px)}.table-data-grid-column-visibility-menu .clear-search[data-v-c7a3d29f]{flex-shrink:0}.datatable-search[data-v-9d5cbebc]{align-items:center;display:flex;min-width:0}[data-v-9d5cbebc] .k-input.datatable-search-input{max-width:100%;min-width:220px;width:260px}[data-v-9d5cbebc] .k-input.datatable-search-input ::-webkit-search-cancel-button{-webkit-appearance:none}.clear-search[data-v-9d5cbebc]{flex-shrink:0}.kong-ui-public-table-data-grid[data-v-b0aefb40]{border:1px solid #e0e4ea;border:1px solid var(--kui-color-border, #e0e4ea);border-radius:4px;border-radius:var(--kui-border-radius-20, 4px);display:flex;flex-direction:column;gap:0;height:100%;min-height:360px;overflow:hidden;position:relative;width:100%}.datatable-toolbar[data-v-b0aefb40]{align-items:center;background:#fff;background:var(--kui-color-background, #ffffff);border-bottom:1px solid #e0e4ea;border-bottom:1px solid var(--kui-color-border, #e0e4ea);display:flex;gap:8px;gap:var(--kui-space-40, 8px);justify-content:space-between;padding:6px;padding:var(--kui-space-30, 6px)}.datatable-outside-actions-host[data-v-b0aefb40]{height:0;overflow:hidden;pointer-events:none;position:absolute;width:0}.datatable-toolbar-primary[data-v-b0aefb40]{align-items:center;display:flex;flex:1 1 auto;gap:8px;gap:var(--kui-space-40, 8px);min-width:0}.datatable-toolbar-secondary[data-v-b0aefb40]{align-items:center;display:flex;flex:0 0 auto;gap:8px;gap:var(--kui-space-40, 8px);min-width:0}.datatable-toolbar-selection[data-v-b0aefb40]{align-items:center;display:flex;min-width:0}.datatable-bulk-actions-dropdown[data-v-b0aefb40] .k-button.datatable-bulk-actions-trigger{border-width:1px;border-width:var(--kui-border-width-10, 1px);font-size:14px;font-size:var(--kui-font-size-30, 14px);width:140px}.table-data-grid-grid[data-v-b0aefb40]{flex:1 1 420px;min-height:0;width:100%}.table-data-grid-grid[data-v-b0aefb40] .ag-header-cell{border-right:1px solid #e0e4ea;border-right:1px solid var(--kui-color-border, #e0e4ea)}.table-data-grid-grid[data-v-b0aefb40] .ag-header-cell[col-id=ag-Grid-SelectionColumn]{border-right:0;gap:0}.table-data-grid-grid[data-v-b0aefb40] .ag-header-cell-resize:after{display:none}.table-data-grid-grid[data-v-b0aefb40] .ag-root-wrapper{border:0;border-radius:0}.table-data-grid-grid[data-v-b0aefb40] .ag-cell{align-items:center;display:flex}.table-data-grid-grid[data-v-b0aefb40] .ag-cell-wrapper,.table-data-grid-grid[data-v-b0aefb40] .ag-cell-value{align-items:center;display:flex;height:100%;min-width:0;width:100%}.datatable-pagination[data-v-b0aefb40]{background:#fff;background:var(--kui-color-background, #ffffff);border-top:1px solid #e0e4ea;border-top:1px solid var(--kui-color-border, #e0e4ea);padding:4px 12px 6px;padding:var(--kui-space-20, 4px) var(--kui-space-50, 12px) var(--kui-space-30, 6px)}.datatable-pagination-control[data-v-b0aefb40]{margin-top:0;padding:0;width:100%}.datatable-pagination-control[data-v-b0aefb40] .pagination-text.large-screen{padding-left:8px;padding-left:var(--kui-space-40, 8px)}.datatable-pagination-control[data-v-b0aefb40] .pagination-button.placeholder{align-items:center;box-sizing:border-box;display:flex;justify-content:center}