@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.
- package/LICENSE +201 -0
- package/README.md +564 -0
- package/dist/style.css +1 -0
- package/dist/table-data-grid.es.js +2566 -0
- package/dist/table-data-grid.umd.js +1 -0
- package/dist/types/components/TableDataGrid.vue.d.ts +99 -0
- package/dist/types/components/TableDataGrid.vue.d.ts.map +1 -0
- package/dist/types/components/TableDataGridCellRenderer.vue.d.ts +21 -0
- package/dist/types/components/TableDataGridCellRenderer.vue.d.ts.map +1 -0
- package/dist/types/components/TableDataGridColumnVisibilityMenu.vue.d.ts +26 -0
- package/dist/types/components/TableDataGridColumnVisibilityMenu.vue.d.ts.map +1 -0
- package/dist/types/components/TableDataGridFilters.vue.d.ts +36 -0
- package/dist/types/components/TableDataGridFilters.vue.d.ts.map +1 -0
- package/dist/types/components/TableDataGridHeaderRenderer.vue.d.ts +13 -0
- package/dist/types/components/TableDataGridHeaderRenderer.vue.d.ts.map +1 -0
- package/dist/types/components/TableDataGridSearch.vue.d.ts +11 -0
- package/dist/types/components/TableDataGridSearch.vue.d.ts.map +1 -0
- package/dist/types/composables/index.d.ts +182 -0
- package/dist/types/composables/index.d.ts.map +1 -0
- package/dist/types/composables/useDatatableColumnDefs.d.ts +21 -0
- package/dist/types/composables/useDatatableColumnDefs.d.ts.map +1 -0
- package/dist/types/composables/useDatatableColumnSizing.d.ts +31 -0
- package/dist/types/composables/useDatatableColumnSizing.d.ts.map +1 -0
- package/dist/types/composables/useDatatableGridSync.d.ts +63 -0
- package/dist/types/composables/useDatatableGridSync.d.ts.map +1 -0
- package/dist/types/composables/useDatatablePagination.d.ts +17 -0
- package/dist/types/composables/useDatatablePagination.d.ts.map +1 -0
- package/dist/types/composables/useDatatableSelection.d.ts +18 -0
- package/dist/types/composables/useDatatableSelection.d.ts.map +1 -0
- package/dist/types/composables/useI18n.d.ts +9 -0
- package/dist/types/composables/useI18n.d.ts.map +1 -0
- package/dist/types/composables/useTableDataGridConfig.d.ts +24 -0
- package/dist/types/composables/useTableDataGridConfig.d.ts.map +1 -0
- package/dist/types/composables/useTableDataGridFetchers.d.ts +33 -0
- package/dist/types/composables/useTableDataGridFetchers.d.ts.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/types/index.d.ts +89 -0
- package/dist/types/types/index.d.ts.map +1 -0
- package/dist/types/utils/headers.d.ts +3 -0
- package/dist/types/utils/headers.d.ts.map +1 -0
- package/dist/types/utils/rowKey.d.ts +3 -0
- package/dist/types/utils/rowKey.d.ts.map +1 -0
- package/dist/types/utils/tableConfig.d.ts +35 -0
- package/dist/types/utils/tableConfig.d.ts.map +1 -0
- package/dist/types/utils/tablePreferencesInterop.d.ts +6 -0
- package/dist/types/utils/tablePreferencesInterop.d.ts.map +1 -0
- 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}
|