@tipolox/litgrid-web 1.1.0 → 1.2.0
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/README.md +2 -0
- package/dist/components/DataGrid.d.ts +21 -1
- package/dist/components/DataGrid.d.ts.map +1 -1
- package/dist/components/DataGrid.js +163 -35
- package/dist/components/DataGrid.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +22 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/components/DataGrid.ts +166 -37
- package/src/index.ts +4 -0
- package/src/types.ts +30 -0
|
@@ -7,6 +7,11 @@ import {
|
|
|
7
7
|
type GridConfig,
|
|
8
8
|
type GridFilter,
|
|
9
9
|
type GridSelection,
|
|
10
|
+
type GridRowKey,
|
|
11
|
+
type SelectionChangeDetail,
|
|
12
|
+
type GridExternalState,
|
|
13
|
+
type GridQueryChangeDetail,
|
|
14
|
+
type SortDirection,
|
|
10
15
|
type SelectionIntent
|
|
11
16
|
} from '@tipolox/litgrid-core'
|
|
12
17
|
import {
|
|
@@ -118,6 +123,7 @@ export class DataGrid extends LitElement {
|
|
|
118
123
|
private previousBodyCursor = ''
|
|
119
124
|
private previousBodyUserSelect = ''
|
|
120
125
|
private activeHeaderMenu: string | null = null
|
|
126
|
+
private headerMenuCapabilities = { sortable: true, filterable: true }
|
|
121
127
|
private columnChooserOpen = false
|
|
122
128
|
private columnChooserPosition: { left: number; top: number } | null = null
|
|
123
129
|
private quickSearchVisible = false
|
|
@@ -145,6 +151,7 @@ export class DataGrid extends LitElement {
|
|
|
145
151
|
private shouldRestoreHeaderMenuFocus = false
|
|
146
152
|
private screenReaderMessage = ''
|
|
147
153
|
private lastScreenReaderAnnouncement = ''
|
|
154
|
+
private paginationFocusTarget: HTMLButtonElement | null = null
|
|
148
155
|
|
|
149
156
|
// Use a larger cap (2^26) to avoid hitting browser element-height limits.
|
|
150
157
|
|
|
@@ -171,6 +178,21 @@ export class DataGrid extends LitElement {
|
|
|
171
178
|
this.ariaLabel = 'Data grid'
|
|
172
179
|
this.ariaDescription = ''
|
|
173
180
|
this.screenReaderAnnouncements = true
|
|
181
|
+
this.engine.onSelectionChange(detail => {
|
|
182
|
+
this.requestUpdate()
|
|
183
|
+
this.dispatchEvent(new CustomEvent<SelectionChangeDetail>('selection-change', {
|
|
184
|
+
detail, bubbles: true, composed: true
|
|
185
|
+
}))
|
|
186
|
+
})
|
|
187
|
+
this.engine.onQueryChange(detail => {
|
|
188
|
+
this.quickSearchDraft = detail.query.search
|
|
189
|
+
this.clearBestFitWidths()
|
|
190
|
+
this.resetVerticalScroll()
|
|
191
|
+
this.requestUpdate()
|
|
192
|
+
this.dispatchEvent(new CustomEvent<GridQueryChangeDetail>('query-change', {
|
|
193
|
+
detail, bubbles: true, composed: true
|
|
194
|
+
}))
|
|
195
|
+
})
|
|
174
196
|
}
|
|
175
197
|
|
|
176
198
|
static styles = [lightThemeStyles, darkThemeStyles, css`
|
|
@@ -701,7 +723,17 @@ export class DataGrid extends LitElement {
|
|
|
701
723
|
this.updateScrollbarWidth()
|
|
702
724
|
}
|
|
703
725
|
|
|
726
|
+
willUpdate() {
|
|
727
|
+
// Reconcile after schema and persisted visibility settle, before triggers disappear.
|
|
728
|
+
this.reconcileHeaderMenuCapabilities()
|
|
729
|
+
}
|
|
730
|
+
|
|
704
731
|
updated() {
|
|
732
|
+
const paginationTarget = this.paginationFocusTarget
|
|
733
|
+
this.paginationFocusTarget = null
|
|
734
|
+
if (paginationTarget?.isConnected && paginationTarget.disabled) {
|
|
735
|
+
this.renderRoot.querySelector<HTMLElement>('.pagination button:not([disabled]), .pagination select')?.focus()
|
|
736
|
+
}
|
|
705
737
|
this.updateScrollbarWidth()
|
|
706
738
|
|
|
707
739
|
if (this.shouldFocusViewport) {
|
|
@@ -1162,6 +1194,7 @@ export class DataGrid extends LitElement {
|
|
|
1162
1194
|
})
|
|
1163
1195
|
}
|
|
1164
1196
|
const isOpening = this.activeHeaderMenu !== columnKey
|
|
1197
|
+
this.headerMenuCapabilities = this.getColumnCapabilities(columnKey)
|
|
1165
1198
|
|
|
1166
1199
|
this.activeHeaderMenu = isOpening ? columnKey : null
|
|
1167
1200
|
this.headerMenuTrigger = isOpening && event.currentTarget instanceof HTMLButtonElement
|
|
@@ -1171,6 +1204,20 @@ export class DataGrid extends LitElement {
|
|
|
1171
1204
|
this.requestUpdate()
|
|
1172
1205
|
}
|
|
1173
1206
|
|
|
1207
|
+
private getColumnCapabilities(columnKey: string) {
|
|
1208
|
+
const column = this.getResolvedColumns().find(column => column.key === columnKey)
|
|
1209
|
+
return { sortable: !!column && column.sortable !== false, filterable: !!column && column.filterable !== false }
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
private reconcileHeaderMenuCapabilities() {
|
|
1213
|
+
if (this.activeHeaderMenu === null) return
|
|
1214
|
+
const next = this.getColumnCapabilities(this.activeHeaderMenu)
|
|
1215
|
+
if (!this.isColumnVisible(this.activeHeaderMenu)
|
|
1216
|
+
|| next.sortable !== this.headerMenuCapabilities.sortable || next.filterable !== this.headerMenuCapabilities.filterable) {
|
|
1217
|
+
this.closeHeaderMenu(true)
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1174
1221
|
private closeHeaderMenu(restoreFocus = false) {
|
|
1175
1222
|
if (this.activeHeaderMenu === null && !this.columnChooserOpen) return
|
|
1176
1223
|
this.activeHeaderMenu = null
|
|
@@ -1320,6 +1367,10 @@ export class DataGrid extends LitElement {
|
|
|
1320
1367
|
|
|
1321
1368
|
private announceRowSelection(rowIndex: number, intent: SelectionIntent = 'replace') {
|
|
1322
1369
|
const anchor = this.engine.getSelection().anchorRowIndex ?? rowIndex
|
|
1370
|
+
if (intent === 'range' && this.engine.getConfig().selection.rowKey !== undefined) {
|
|
1371
|
+
this.announceScreenReader(`${this.getDisplayedSelectedRowCount()} displayed rows selected.`)
|
|
1372
|
+
return
|
|
1373
|
+
}
|
|
1323
1374
|
this.announceScreenReader(intent === 'range' && anchor !== rowIndex
|
|
1324
1375
|
? `Rows ${Math.min(anchor, rowIndex) + 1} through ${Math.max(anchor, rowIndex) + 1} selected.`
|
|
1325
1376
|
: `Row ${rowIndex + 1} ${this.engine.isRowSelected(rowIndex) ? 'selected' : 'deselected'}.`)
|
|
@@ -1330,6 +1381,10 @@ export class DataGrid extends LitElement {
|
|
|
1330
1381
|
}
|
|
1331
1382
|
|
|
1332
1383
|
private announceVisibleRowCount(context: string) {
|
|
1384
|
+
if (this.engine.getConfig().queryMode === 'external') {
|
|
1385
|
+
this.announceScreenReader(`${context}. Server query requested.`)
|
|
1386
|
+
return
|
|
1387
|
+
}
|
|
1333
1388
|
const rowCount = this.engine.getRowCount()
|
|
1334
1389
|
this.announceScreenReader(rowCount === 0 ? `${context}. No matching rows.` : `${context}. ${rowCount} ${rowCount === 1 ? 'row' : 'rows'} shown.`)
|
|
1335
1390
|
}
|
|
@@ -1337,22 +1392,25 @@ export class DataGrid extends LitElement {
|
|
|
1337
1392
|
|
|
1338
1393
|
|
|
1339
1394
|
private handleSortAscending(columnKey: string) {
|
|
1395
|
+
if (!this.getColumnCapabilities(columnKey).sortable) return
|
|
1340
1396
|
this.engine.sortBy(columnKey, 'asc')
|
|
1341
|
-
this.announceScreenReader(`${this.getColumnLabel(columnKey)} sorted ascending.`)
|
|
1397
|
+
this.announceScreenReader(`${this.getColumnLabel(columnKey)} ${this.engine.getConfig().queryMode === 'external' ? 'ascending sort requested' : 'sorted ascending'}.`)
|
|
1342
1398
|
this.requestUpdate()
|
|
1343
1399
|
this.closeHeaderMenu(true)
|
|
1344
1400
|
}
|
|
1345
1401
|
|
|
1346
1402
|
private handleSortDescending(columnKey: string) {
|
|
1403
|
+
if (!this.getColumnCapabilities(columnKey).sortable) return
|
|
1347
1404
|
this.engine.sortBy(columnKey, 'desc')
|
|
1348
|
-
this.announceScreenReader(`${this.getColumnLabel(columnKey)} sorted descending.`)
|
|
1405
|
+
this.announceScreenReader(`${this.getColumnLabel(columnKey)} ${this.engine.getConfig().queryMode === 'external' ? 'descending sort requested' : 'sorted descending'}.`)
|
|
1349
1406
|
this.requestUpdate()
|
|
1350
1407
|
this.closeHeaderMenu(true)
|
|
1351
1408
|
}
|
|
1352
1409
|
|
|
1353
|
-
private handleClearSort() {
|
|
1410
|
+
private handleClearSort(columnKey: string) {
|
|
1411
|
+
if (!this.getColumnCapabilities(columnKey).sortable) return
|
|
1354
1412
|
this.engine.clearSort()
|
|
1355
|
-
this.announceScreenReader('Sorting cleared.')
|
|
1413
|
+
this.announceScreenReader(this.engine.getConfig().queryMode === 'external' ? 'Clear sorting requested.' : 'Sorting cleared.')
|
|
1356
1414
|
this.requestUpdate()
|
|
1357
1415
|
this.closeHeaderMenu(true)
|
|
1358
1416
|
}
|
|
@@ -1560,7 +1618,8 @@ export class DataGrid extends LitElement {
|
|
|
1560
1618
|
const threshold = Math.max(0, Math.floor(this.quickSearchDebounceThreshold))
|
|
1561
1619
|
const debounceMs = Math.max(0, Math.floor(this.quickSearchDebounceMs))
|
|
1562
1620
|
|
|
1563
|
-
|
|
1621
|
+
const external = this.engine.getConfig().queryMode === 'external'
|
|
1622
|
+
if (debounceMs === 0 || (!external && this.sourceRowCount < threshold)) {
|
|
1564
1623
|
this.applyQuickSearch(query)
|
|
1565
1624
|
return
|
|
1566
1625
|
}
|
|
@@ -1596,19 +1655,57 @@ export class DataGrid extends LitElement {
|
|
|
1596
1655
|
return this.engine.getFilters()
|
|
1597
1656
|
}
|
|
1598
1657
|
|
|
1658
|
+
sortBy(columnKey: string, direction: SortDirection) {
|
|
1659
|
+
this.engine.sortBy(columnKey, direction)
|
|
1660
|
+
this.resetVerticalScroll()
|
|
1661
|
+
this.requestUpdate()
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
clearSort() {
|
|
1665
|
+
this.engine.clearSort()
|
|
1666
|
+
this.resetVerticalScroll()
|
|
1667
|
+
this.requestUpdate()
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
getQuery() { return this.engine.getQuery() }
|
|
1671
|
+
refreshQuery() { this.engine.refreshQuery() }
|
|
1672
|
+
get externalState() { return this.engine.getExternalState() }
|
|
1673
|
+
set externalState(value: GridExternalState | null) {
|
|
1674
|
+
if (value) this.setExternalState(value)
|
|
1675
|
+
}
|
|
1676
|
+
setExternalState(value: GridExternalState): boolean {
|
|
1677
|
+
if (!this.engine.setExternalState(value)) return false
|
|
1678
|
+
this.clearQuickSearchDebounce()
|
|
1679
|
+
this.quickSearchDraft = this.engine.getQuickSearch()
|
|
1680
|
+
this.filterDrafts = new Map(this.engine.getFilters().map(filter => [filter.columnKey, filter]))
|
|
1681
|
+
this.sourceRowCount = this.engine.getRowCount()
|
|
1682
|
+
this.clearBestFitWidths()
|
|
1683
|
+
this.requestUpdate()
|
|
1684
|
+
return true
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
private getPageLabel() {
|
|
1688
|
+
const pagination = this.engine.getPagination()
|
|
1689
|
+
if (this.engine.getConfig().queryMode === 'external' && pagination.totalKnown && pagination.pageIndex > 0 && pagination.pageIndex >= pagination.pageCount) return `Page ${pagination.pageIndex + 1} (out of range)`
|
|
1690
|
+
return pagination.totalKnown === false ? `Page ${pagination.pageIndex + 1}`
|
|
1691
|
+
: `Page ${pagination.pageCount === 0 ? 0 : pagination.pageIndex + 1} of ${pagination.pageCount}`
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1599
1694
|
setPage(pageIndex: number) {
|
|
1695
|
+
this.rememberPaginationFocus()
|
|
1600
1696
|
this.engine.setPage(pageIndex)
|
|
1601
1697
|
this.resetVerticalScroll()
|
|
1602
1698
|
const pagination = this.engine.getPagination()
|
|
1603
|
-
this.announceScreenReader(
|
|
1699
|
+
this.announceScreenReader(`${this.getPageLabel()}.`)
|
|
1604
1700
|
this.requestUpdate()
|
|
1605
1701
|
}
|
|
1606
1702
|
|
|
1607
1703
|
setPageSize(pageSize: number) {
|
|
1704
|
+
this.rememberPaginationFocus()
|
|
1608
1705
|
this.engine.setPageSize(pageSize)
|
|
1609
1706
|
this.resetVerticalScroll()
|
|
1610
1707
|
const pagination = this.engine.getPagination()
|
|
1611
|
-
this.announceScreenReader(`${pagination.pageSize} rows per page.
|
|
1708
|
+
this.announceScreenReader(`${pagination.pageSize} rows per page. ${this.getPageLabel()}.`)
|
|
1612
1709
|
this.requestUpdate()
|
|
1613
1710
|
}
|
|
1614
1711
|
|
|
@@ -1616,6 +1713,11 @@ export class DataGrid extends LitElement {
|
|
|
1616
1713
|
return this.engine.getPagination()
|
|
1617
1714
|
}
|
|
1618
1715
|
|
|
1716
|
+
private rememberPaginationFocus() {
|
|
1717
|
+
const active = this.shadowRoot?.activeElement
|
|
1718
|
+
if (active instanceof HTMLButtonElement && active.closest('.pagination')) this.paginationFocusTarget = active
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1619
1721
|
getTotalRowCount() {
|
|
1620
1722
|
return this.engine.getTotalRowCount()
|
|
1621
1723
|
}
|
|
@@ -1634,11 +1736,17 @@ export class DataGrid extends LitElement {
|
|
|
1634
1736
|
this.requestUpdate()
|
|
1635
1737
|
}
|
|
1636
1738
|
|
|
1739
|
+
private announceAllRowsSelected() {
|
|
1740
|
+
const count = this.engine.getSelectableRowCount()
|
|
1741
|
+
const qualifier = count === this.engine.getRowCount() ? '' : 'selectable '
|
|
1742
|
+
this.announceScreenReader(`All ${count} ${qualifier}displayed rows selected.`)
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1637
1745
|
selectAllRows() {
|
|
1638
|
-
if (this.engine.getConfig().selection.mode !== 'multi-row' || this.engine.
|
|
1639
|
-
const unchanged = this.engine.getSelection().rowIndexes.size === this.engine.
|
|
1746
|
+
if (this.engine.getConfig().selection.mode !== 'multi-row' || this.engine.getSelectableRowCount() === 0) return
|
|
1747
|
+
const unchanged = this.engine.getSelection().rowIndexes.size === this.engine.getSelectableRowCount()
|
|
1640
1748
|
this.engine.selectAllRows()
|
|
1641
|
-
if (!unchanged) this.
|
|
1749
|
+
if (!unchanged) this.announceAllRowsSelected()
|
|
1642
1750
|
this.requestUpdate()
|
|
1643
1751
|
}
|
|
1644
1752
|
|
|
@@ -1654,6 +1762,22 @@ export class DataGrid extends LitElement {
|
|
|
1654
1762
|
return this.engine.getSelection()
|
|
1655
1763
|
}
|
|
1656
1764
|
|
|
1765
|
+
getSelectionSnapshot(): SelectionChangeDetail {
|
|
1766
|
+
return this.engine.getSelectionSnapshot()
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
selectRowByKey(key: GridRowKey, intent?: SelectionIntent): boolean {
|
|
1770
|
+
const previous = this.engine.getSelectionSnapshot()
|
|
1771
|
+
const selected = this.engine.selectRowByKey(key, intent)
|
|
1772
|
+
const next = this.engine.getSelectionSnapshot()
|
|
1773
|
+
if (selected && next !== previous) {
|
|
1774
|
+
const index = next.rows.find(row => row.key === key)?.rowIndex
|
|
1775
|
+
?? previous.rows.find(row => row.key === key)?.rowIndex
|
|
1776
|
+
if (index !== undefined) this.announceRowSelection(index, intent)
|
|
1777
|
+
}
|
|
1778
|
+
return selected
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1657
1781
|
isRowSelected(rowIndex: number) {
|
|
1658
1782
|
return this.engine.isRowSelected(rowIndex)
|
|
1659
1783
|
}
|
|
@@ -1713,6 +1837,7 @@ export class DataGrid extends LitElement {
|
|
|
1713
1837
|
}
|
|
1714
1838
|
|
|
1715
1839
|
private applyHeaderFilter(columnKey: string) {
|
|
1840
|
+
if (!this.getColumnCapabilities(columnKey).filterable) return
|
|
1716
1841
|
const filter = this.getFilterDraft(columnKey)
|
|
1717
1842
|
this.setFilter({
|
|
1718
1843
|
...filter,
|
|
@@ -1722,6 +1847,7 @@ export class DataGrid extends LitElement {
|
|
|
1722
1847
|
}
|
|
1723
1848
|
|
|
1724
1849
|
private clearHeaderFilter(columnKey: string) {
|
|
1850
|
+
if (!this.getColumnCapabilities(columnKey).filterable) return
|
|
1725
1851
|
this.clearFilter(columnKey)
|
|
1726
1852
|
this.closeHeaderMenu(true)
|
|
1727
1853
|
}
|
|
@@ -2534,6 +2660,7 @@ export class DataGrid extends LitElement {
|
|
|
2534
2660
|
}
|
|
2535
2661
|
|
|
2536
2662
|
set data(value: unknown[]) {
|
|
2663
|
+
if (this.engine.getConfig().queryMode === 'external') return
|
|
2537
2664
|
this.clearQuickSearchDebounce()
|
|
2538
2665
|
|
|
2539
2666
|
this.sourceRowCount = Array.isArray(value) ? value.length : 0
|
|
@@ -2549,26 +2676,17 @@ export class DataGrid extends LitElement {
|
|
|
2549
2676
|
}
|
|
2550
2677
|
|
|
2551
2678
|
private getDisplayedSelectedRowCount() {
|
|
2552
|
-
|
|
2553
|
-
let selectedCount = 0
|
|
2554
|
-
|
|
2555
|
-
for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
|
|
2556
|
-
if (this.engine.isRowSelected(rowIndex)) {
|
|
2557
|
-
selectedCount += 1
|
|
2558
|
-
}
|
|
2559
|
-
}
|
|
2560
|
-
|
|
2561
|
-
return selectedCount
|
|
2679
|
+
return this.engine.getSelectionSnapshot().rowIndexes.length
|
|
2562
2680
|
}
|
|
2563
2681
|
|
|
2564
2682
|
private areAllDisplayedRowsSelected() {
|
|
2565
|
-
const rowCount = this.engine.
|
|
2683
|
+
const rowCount = this.engine.getSelectableRowCount()
|
|
2566
2684
|
return rowCount > 0 && this.getDisplayedSelectedRowCount() === rowCount
|
|
2567
2685
|
}
|
|
2568
2686
|
|
|
2569
2687
|
private hasPartiallySelectedDisplayedRows() {
|
|
2570
2688
|
const selectedCount = this.getDisplayedSelectedRowCount()
|
|
2571
|
-
return selectedCount > 0 && selectedCount < this.engine.
|
|
2689
|
+
return selectedCount > 0 && selectedCount < this.engine.getSelectableRowCount()
|
|
2572
2690
|
}
|
|
2573
2691
|
|
|
2574
2692
|
private handleSelectAllRows() {
|
|
@@ -2577,7 +2695,7 @@ export class DataGrid extends LitElement {
|
|
|
2577
2695
|
this.announceScreenReader('All displayed rows deselected.')
|
|
2578
2696
|
} else {
|
|
2579
2697
|
this.engine.selectAllRows()
|
|
2580
|
-
this.
|
|
2698
|
+
this.announceAllRowsSelected()
|
|
2581
2699
|
}
|
|
2582
2700
|
this.requestUpdate()
|
|
2583
2701
|
}
|
|
@@ -2758,6 +2876,12 @@ export class DataGrid extends LitElement {
|
|
|
2758
2876
|
render() {
|
|
2759
2877
|
const count = this.engine.getRowCount()
|
|
2760
2878
|
const pagination = this.engine.getPagination()
|
|
2879
|
+
const external = this.engine.getConfig().queryMode === 'external'
|
|
2880
|
+
const externalState = external ? this.engine.getExternalState() : null
|
|
2881
|
+
const outOfRange = external && pagination.totalKnown && pagination.pageIndex > 0 && pagination.pageIndex >= pagination.pageCount
|
|
2882
|
+
const externalStatus = externalState?.loading ? 'Loading results.' : externalState?.error
|
|
2883
|
+
? externalState.error : outOfRange ? 'Requested page is out of range. Choose a previous page.'
|
|
2884
|
+
: count === 0 ? (pagination.totalRows === 0 ? 'No results.' : 'No rows on this page.') : ''
|
|
2761
2885
|
const pageSizeOptions = [25, 50, 100].includes(pagination.pageSize)
|
|
2762
2886
|
? [25, 50, 100]
|
|
2763
2887
|
: [pagination.pageSize, 25, 50, 100].sort((left, right) => left - right)
|
|
@@ -2881,14 +3005,16 @@ export class DataGrid extends LitElement {
|
|
|
2881
3005
|
return html`
|
|
2882
3006
|
<div
|
|
2883
3007
|
class="shell"
|
|
3008
|
+
aria-busy=${external ? String(!!externalState?.loading) : nothing}
|
|
2884
3009
|
style=${`--virtual-grid-template: ${gridTemplate}; --total-column-width: ${totalColumnWidth}px; --total-row-width: ${totalRowWidth}px; --row-header-width: ${rowHeaderWidth}px; --header-grid-template: ${headerGridTemplate}; --scrollbar-width: ${this.scrollbarWidth}px;`}
|
|
2885
3010
|
@keydown=${this.handleShellKeyDown}
|
|
2886
3011
|
>
|
|
2887
3012
|
<p id=${`${this.gridId}-description`} class="screen-reader-only">${this.getScreenReaderDescription()}</p>
|
|
3013
|
+
${external && externalStatus ? html`<div class="external-status" role=${externalState?.error ? 'alert' : 'status'}>${externalStatus}</div>` : nothing}
|
|
2888
3014
|
${this.screenReaderAnnouncements
|
|
2889
3015
|
? html`<div class="screen-reader-only" role="status" aria-live="polite" aria-atomic="true">${this.screenReaderMessage}</div>`
|
|
2890
3016
|
: ''}
|
|
2891
|
-
${this.quickSearchVisible
|
|
3017
|
+
${this.quickSearchVisible || external
|
|
2892
3018
|
? html`
|
|
2893
3019
|
<div class="quick-search" role="search">
|
|
2894
3020
|
<input
|
|
@@ -2938,7 +3064,7 @@ export class DataGrid extends LitElement {
|
|
|
2938
3064
|
aria-label="Select all displayed rows"
|
|
2939
3065
|
.checked=${this.areAllDisplayedRowsSelected()}
|
|
2940
3066
|
.indeterminate=${this.hasPartiallySelectedDisplayedRows()}
|
|
2941
|
-
?disabled=${this.engine.
|
|
3067
|
+
?disabled=${this.engine.getSelectableRowCount() === 0}
|
|
2942
3068
|
@change=${() => this.handleSelectAllRows()}
|
|
2943
3069
|
/>`
|
|
2944
3070
|
: html`<span>#</span>`}
|
|
@@ -3006,19 +3132,22 @@ export class DataGrid extends LitElement {
|
|
|
3006
3132
|
${this.quickSearchVisible ? 'Hide Quick Search' : 'Show Quick Search'}
|
|
3007
3133
|
</button>
|
|
3008
3134
|
<hr class="header-menu-divider" />
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3135
|
+
${column.sortable === false ? '' : html`
|
|
3136
|
+
<button type="button" @click=${() => this.handleSortAscending(column.key)}>
|
|
3137
|
+
Sort Asc
|
|
3138
|
+
</button>
|
|
3139
|
+
<button type="button" @click=${() => this.handleSortDescending(column.key)}>
|
|
3140
|
+
Sort Desc
|
|
3141
|
+
</button>
|
|
3142
|
+
<button type="button" @click=${() => this.handleClearSort(column.key)}>
|
|
3143
|
+
Clear Sort
|
|
3144
|
+
</button>
|
|
3145
|
+
`}
|
|
3018
3146
|
<button type="button" @click=${(event: MouseEvent) => this.toggleColumnChooser(event)}>
|
|
3019
3147
|
Columns
|
|
3020
3148
|
</button>
|
|
3021
3149
|
${(() => {
|
|
3150
|
+
if (column.filterable === false) return ''
|
|
3022
3151
|
const draft = this.getFilterDraft(column.key)
|
|
3023
3152
|
const valueFree = this.isValueFreeFilter(draft.operator)
|
|
3024
3153
|
|
|
@@ -3146,7 +3275,7 @@ export class DataGrid extends LitElement {
|
|
|
3146
3275
|
? html`
|
|
3147
3276
|
<div class="pagination" role="navigation" aria-label="Pagination">
|
|
3148
3277
|
<div class="pagination-summary">
|
|
3149
|
-
<span>${pagination.totalRows} rows</span>
|
|
3278
|
+
<span>${pagination.totalKnown === false ? 'Total unknown' : `${pagination.totalRows} rows`}</span>
|
|
3150
3279
|
<label>
|
|
3151
3280
|
Rows per page
|
|
3152
3281
|
<select
|
|
@@ -3163,7 +3292,7 @@ export class DataGrid extends LitElement {
|
|
|
3163
3292
|
</label>
|
|
3164
3293
|
</div>
|
|
3165
3294
|
<div class="pagination-navigation">
|
|
3166
|
-
<span
|
|
3295
|
+
<span>${this.getPageLabel()}</span>
|
|
3167
3296
|
<button
|
|
3168
3297
|
type="button"
|
|
3169
3298
|
?disabled=${pagination.pageIndex === 0}
|
|
@@ -3171,7 +3300,7 @@ export class DataGrid extends LitElement {
|
|
|
3171
3300
|
>Previous</button>
|
|
3172
3301
|
<button
|
|
3173
3302
|
type="button"
|
|
3174
|
-
?disabled=${pagination.pageCount === 0 || pagination.pageIndex >= pagination.pageCount - 1}
|
|
3303
|
+
?disabled=${pagination.totalKnown === false ? !pagination.hasMore : pagination.pageCount === 0 || pagination.pageIndex >= pagination.pageCount - 1}
|
|
3175
3304
|
@click=${() => this.setPage(pagination.pageIndex + 1)}
|
|
3176
3305
|
>Next</button>
|
|
3177
3306
|
</div>
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from './components/DataGrid.js'
|
|
2
|
+
export type { GridQuery, GridQueryFilter, GridQueryReason, GridQueryChangeDetail, GridQuerySnapshot, GridExternalTotal, GridExternalState, SortDirection } from './types.js'
|
|
2
3
|
export type {
|
|
3
4
|
BestFitColumnWidth,
|
|
4
5
|
ColumnReorderDetail,
|
|
@@ -8,6 +9,9 @@ export type {
|
|
|
8
9
|
GridFilter,
|
|
9
10
|
GridPaginationState,
|
|
10
11
|
GridSelection,
|
|
12
|
+
GridRowKey,
|
|
13
|
+
SelectedRowSnapshot,
|
|
14
|
+
SelectionChangeDetail,
|
|
11
15
|
SelectionIntent,
|
|
12
16
|
GridColumn,
|
|
13
17
|
GridTheme,
|
package/src/types.ts
CHANGED
|
@@ -3,13 +3,23 @@ import type {
|
|
|
3
3
|
GridFilter,
|
|
4
4
|
GridPaginationState,
|
|
5
5
|
GridSelection,
|
|
6
|
+
GridRowKey,
|
|
7
|
+
SelectionChangeDetail,
|
|
8
|
+
GridExternalState,
|
|
9
|
+
GridQuerySnapshot,
|
|
10
|
+
GridQueryChangeDetail,
|
|
11
|
+
SortDirection,
|
|
6
12
|
SelectionIntent
|
|
7
13
|
} from '@tipolox/litgrid-core'
|
|
8
14
|
import type { ColumnStateChangeDetail, GridColumnState } from './columnState/columnState.js'
|
|
9
15
|
|
|
10
16
|
export type { ColumnStateChangeDetail, GridColumnState }
|
|
17
|
+
export type { GridQuery, GridQueryFilter, GridQueryReason, GridQueryChangeDetail, GridQuerySnapshot, GridExternalTotal, GridExternalState, SortDirection } from '@tipolox/litgrid-core'
|
|
11
18
|
|
|
12
19
|
export type {
|
|
20
|
+
GridRowKey,
|
|
21
|
+
SelectedRowSnapshot,
|
|
22
|
+
SelectionChangeDetail,
|
|
13
23
|
GridConfig,
|
|
14
24
|
GridFilter,
|
|
15
25
|
GridPaginationState,
|
|
@@ -24,6 +34,10 @@ export type GridColumn = {
|
|
|
24
34
|
header?: string
|
|
25
35
|
width?: number | string
|
|
26
36
|
hidden?: boolean
|
|
37
|
+
/** Show local sorting actions in the header menu. Defaults to true; does not change query state or imperative APIs. */
|
|
38
|
+
sortable?: boolean
|
|
39
|
+
/** Show local column filtering controls. Defaults to true; does not affect quick search or imperative APIs. */
|
|
40
|
+
filterable?: boolean
|
|
27
41
|
accessor?: (row: unknown, rowIndex: number) => unknown
|
|
28
42
|
render?: (value: unknown, row: unknown, rowIndex: number) => unknown
|
|
29
43
|
}
|
|
@@ -47,6 +61,13 @@ export type ColumnVisibilityChangeDetail = {
|
|
|
47
61
|
}
|
|
48
62
|
|
|
49
63
|
export type DataGridElement = HTMLElement & {
|
|
64
|
+
/** Additive external-query capabilities; production DataGrid implements them. */
|
|
65
|
+
externalState?: GridExternalState | null
|
|
66
|
+
setExternalState?: (state: GridExternalState) => boolean
|
|
67
|
+
getQuery?: () => GridQuerySnapshot
|
|
68
|
+
refreshQuery?: () => void
|
|
69
|
+
sortBy?: (columnKey: string, direction: SortDirection) => void
|
|
70
|
+
clearSort?: () => void
|
|
50
71
|
data: unknown[]
|
|
51
72
|
columns: GridColumn[]
|
|
52
73
|
config: GridConfig
|
|
@@ -103,6 +124,15 @@ export type DataGridElement = HTMLElement & {
|
|
|
103
124
|
selectAllRows: () => void
|
|
104
125
|
clearSelection: () => void
|
|
105
126
|
getSelection: () => GridSelection
|
|
127
|
+
getSelectionSnapshot: () => SelectionChangeDetail
|
|
128
|
+
selectRowByKey: (key: GridRowKey, intent?: SelectionIntent) => boolean
|
|
106
129
|
isRowSelected: (rowIndex: number) => boolean
|
|
107
130
|
isCellSelected: (rowIndex: number, columnKey: string) => boolean
|
|
108
131
|
}
|
|
132
|
+
|
|
133
|
+
declare global {
|
|
134
|
+
interface HTMLElementEventMap {
|
|
135
|
+
'selection-change': CustomEvent<SelectionChangeDetail>
|
|
136
|
+
'query-change': CustomEvent<GridQueryChangeDetail>
|
|
137
|
+
}
|
|
138
|
+
}
|