@keenthemes/ktui 1.2.5 → 1.2.7
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 +14 -5
- package/dist/ktui.js +1538 -786
- package/dist/ktui.min.js +1 -1
- package/dist/ktui.min.js.map +1 -1
- package/dist/styles.css +85 -5
- package/lib/cjs/components/datatable/datatable-checkbox.d.ts +37 -1
- package/lib/cjs/components/datatable/datatable-checkbox.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-checkbox.js +143 -156
- package/lib/cjs/components/datatable/datatable-checkbox.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-column-utils.d.ts +30 -0
- package/lib/cjs/components/datatable/datatable-column-utils.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-column-utils.js +42 -0
- package/lib/cjs/components/datatable/datatable-column-utils.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-contracts.d.ts +2 -4
- package/lib/cjs/components/datatable/datatable-contracts.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-defaults.d.ts +20 -0
- package/lib/cjs/components/datatable/datatable-defaults.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-defaults.js +193 -0
- package/lib/cjs/components/datatable/datatable-defaults.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-layout-plugin.d.ts +7 -0
- package/lib/cjs/components/datatable/datatable-layout-plugin.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-layout-plugin.js +338 -0
- package/lib/cjs/components/datatable/datatable-layout-plugin.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-local-provider.d.ts +2 -2
- package/lib/cjs/components/datatable/datatable-local-provider.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-local-provider.js +85 -27
- package/lib/cjs/components/datatable/datatable-local-provider.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-pagination-renderer.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-pagination-renderer.js +13 -13
- package/lib/cjs/components/datatable/datatable-pagination-renderer.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-registry.d.ts +18 -0
- package/lib/cjs/components/datatable/datatable-registry.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-registry.js +66 -0
- package/lib/cjs/components/datatable/datatable-registry.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-remote-provider.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-remote-provider.js +1 -2
- package/lib/cjs/components/datatable/datatable-remote-provider.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-search-handler.d.ts +10 -0
- package/lib/cjs/components/datatable/datatable-search-handler.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-search-handler.js +65 -0
- package/lib/cjs/components/datatable/datatable-search-handler.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-sort.d.ts +31 -4
- package/lib/cjs/components/datatable/datatable-sort.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-sort.js +86 -58
- package/lib/cjs/components/datatable/datatable-sort.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-spinner.d.ts +30 -0
- package/lib/cjs/components/datatable/datatable-spinner.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-spinner.js +54 -0
- package/lib/cjs/components/datatable/datatable-spinner.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-state-persistence.d.ts +19 -0
- package/lib/cjs/components/datatable/datatable-state-persistence.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-state-persistence.js +59 -0
- package/lib/cjs/components/datatable/datatable-state-persistence.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-table-renderer.d.ts +2 -0
- package/lib/cjs/components/datatable/datatable-table-renderer.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-table-renderer.js +75 -16
- package/lib/cjs/components/datatable/datatable-table-renderer.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-utils.d.ts +10 -0
- package/lib/cjs/components/datatable/datatable-utils.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-utils.js +15 -0
- package/lib/cjs/components/datatable/datatable-utils.js.map +1 -0
- package/lib/cjs/components/datatable/datatable.d.ts +35 -34
- package/lib/cjs/components/datatable/datatable.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable.js +233 -497
- package/lib/cjs/components/datatable/datatable.js.map +1 -1
- package/lib/cjs/components/datatable/index.d.ts +1 -1
- package/lib/cjs/components/datatable/index.d.ts.map +1 -1
- package/lib/cjs/components/datatable/types.d.ts +127 -11
- package/lib/cjs/components/datatable/types.d.ts.map +1 -1
- package/lib/cjs/index.d.ts +1 -1
- package/lib/cjs/index.d.ts.map +1 -1
- package/lib/cjs/index.js +6 -0
- package/lib/cjs/index.js.map +1 -1
- package/lib/esm/components/datatable/datatable-checkbox.d.ts +37 -1
- package/lib/esm/components/datatable/datatable-checkbox.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-checkbox.js +142 -155
- package/lib/esm/components/datatable/datatable-checkbox.js.map +1 -1
- package/lib/esm/components/datatable/datatable-column-utils.d.ts +30 -0
- package/lib/esm/components/datatable/datatable-column-utils.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-column-utils.js +38 -0
- package/lib/esm/components/datatable/datatable-column-utils.js.map +1 -0
- package/lib/esm/components/datatable/datatable-contracts.d.ts +2 -4
- package/lib/esm/components/datatable/datatable-contracts.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-defaults.d.ts +20 -0
- package/lib/esm/components/datatable/datatable-defaults.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-defaults.js +190 -0
- package/lib/esm/components/datatable/datatable-defaults.js.map +1 -0
- package/lib/esm/components/datatable/datatable-layout-plugin.d.ts +7 -0
- package/lib/esm/components/datatable/datatable-layout-plugin.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-layout-plugin.js +334 -0
- package/lib/esm/components/datatable/datatable-layout-plugin.js.map +1 -0
- package/lib/esm/components/datatable/datatable-local-provider.d.ts +2 -2
- package/lib/esm/components/datatable/datatable-local-provider.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-local-provider.js +85 -27
- package/lib/esm/components/datatable/datatable-local-provider.js.map +1 -1
- package/lib/esm/components/datatable/datatable-pagination-renderer.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-pagination-renderer.js +13 -13
- package/lib/esm/components/datatable/datatable-pagination-renderer.js.map +1 -1
- package/lib/esm/components/datatable/datatable-registry.d.ts +18 -0
- package/lib/esm/components/datatable/datatable-registry.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-registry.js +63 -0
- package/lib/esm/components/datatable/datatable-registry.js.map +1 -0
- package/lib/esm/components/datatable/datatable-remote-provider.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-remote-provider.js +1 -2
- package/lib/esm/components/datatable/datatable-remote-provider.js.map +1 -1
- package/lib/esm/components/datatable/datatable-search-handler.d.ts +10 -0
- package/lib/esm/components/datatable/datatable-search-handler.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-search-handler.js +62 -0
- package/lib/esm/components/datatable/datatable-search-handler.js.map +1 -0
- package/lib/esm/components/datatable/datatable-sort.d.ts +31 -4
- package/lib/esm/components/datatable/datatable-sort.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-sort.js +85 -57
- package/lib/esm/components/datatable/datatable-sort.js.map +1 -1
- package/lib/esm/components/datatable/datatable-spinner.d.ts +30 -0
- package/lib/esm/components/datatable/datatable-spinner.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-spinner.js +51 -0
- package/lib/esm/components/datatable/datatable-spinner.js.map +1 -0
- package/lib/esm/components/datatable/datatable-state-persistence.d.ts +19 -0
- package/lib/esm/components/datatable/datatable-state-persistence.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-state-persistence.js +55 -0
- package/lib/esm/components/datatable/datatable-state-persistence.js.map +1 -0
- package/lib/esm/components/datatable/datatable-table-renderer.d.ts +2 -0
- package/lib/esm/components/datatable/datatable-table-renderer.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-table-renderer.js +75 -16
- package/lib/esm/components/datatable/datatable-table-renderer.js.map +1 -1
- package/lib/esm/components/datatable/datatable-utils.d.ts +10 -0
- package/lib/esm/components/datatable/datatable-utils.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-utils.js +12 -0
- package/lib/esm/components/datatable/datatable-utils.js.map +1 -0
- package/lib/esm/components/datatable/datatable.d.ts +35 -34
- package/lib/esm/components/datatable/datatable.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable.js +235 -499
- package/lib/esm/components/datatable/datatable.js.map +1 -1
- package/lib/esm/components/datatable/index.d.ts +1 -1
- package/lib/esm/components/datatable/index.d.ts.map +1 -1
- package/lib/esm/components/datatable/types.d.ts +127 -11
- package/lib/esm/components/datatable/types.d.ts.map +1 -1
- package/lib/esm/index.d.ts +1 -1
- package/lib/esm/index.d.ts.map +1 -1
- package/lib/esm/index.js +6 -0
- package/lib/esm/index.js.map +1 -1
- package/package.json +5 -1
- package/skills/ktui/SKILL.md +711 -0
- package/skills/ktui-datatable/SKILL.md +302 -0
- package/skills/ktui-install/SKILL.md +150 -0
- package/skills/ktui-select/SKILL.md +271 -0
- package/src/components/__tests__/component.test.ts +347 -0
- package/src/components/collapse/collapse.css +2 -2
- package/src/components/datatable/__tests__/architecture-boundaries.test.ts +56 -8
- package/src/components/datatable/__tests__/currency-sort.test.ts +25 -28
- package/src/components/datatable/__tests__/datatable-checkbox.test.ts +527 -0
- package/src/components/datatable/__tests__/datatable-column-utils.test.ts +117 -0
- package/src/components/datatable/__tests__/datatable-defaults.test.ts +57 -0
- package/src/components/datatable/__tests__/datatable-finalize-extended.test.ts +361 -0
- package/src/components/datatable/__tests__/datatable-fixed-layout.test.ts +427 -0
- package/src/components/datatable/__tests__/datatable-improvements.test.ts +484 -0
- package/src/components/datatable/__tests__/datatable-pagination-extended.test.ts +508 -0
- package/src/components/datatable/__tests__/datatable-public-api.test.ts +269 -0
- package/src/components/datatable/__tests__/datatable-registry.test.ts +172 -0
- package/src/components/datatable/__tests__/datatable-remote-provider.test.ts +468 -0
- package/src/components/datatable/__tests__/datatable-search-handler.test.ts +124 -0
- package/src/components/datatable/__tests__/datatable-sort-extended.test.ts +417 -0
- package/src/components/datatable/__tests__/datatable-spinner.test.ts +95 -0
- package/src/components/datatable/__tests__/datatable-table-renderer-extended.test.ts +425 -0
- package/src/components/datatable/__tests__/datatable-types.test.ts +117 -0
- package/src/components/datatable/__tests__/datatable-utils.test.ts +52 -0
- package/src/components/datatable/__tests__/locked-layout.test.ts +257 -0
- package/src/components/datatable/__tests__/multi-row-headers.test.ts +7 -7
- package/src/components/datatable/__tests__/pagination-reset.test.ts +147 -6
- package/src/components/datatable/__tests__/race-conditions.test.ts +11 -11
- package/src/components/datatable/__tests__/setup.ts +12 -4
- package/src/components/datatable/datatable-checkbox.ts +139 -143
- package/src/components/datatable/datatable-column-utils.ts +63 -0
- package/src/components/datatable/datatable-contracts.ts +2 -3
- package/src/components/datatable/datatable-defaults.ts +204 -0
- package/src/components/datatable/datatable-layout-plugin.ts +459 -0
- package/src/components/datatable/datatable-local-provider.ts +106 -35
- package/src/components/datatable/datatable-pagination-renderer.ts +13 -15
- package/src/components/datatable/datatable-registry.ts +89 -0
- package/src/components/datatable/datatable-remote-provider.ts +1 -3
- package/src/components/datatable/datatable-search-handler.ts +97 -0
- package/src/components/datatable/datatable-sort.ts +111 -66
- package/src/components/datatable/datatable-spinner.ts +103 -0
- package/src/components/datatable/datatable-state-persistence.ts +67 -0
- package/src/components/datatable/datatable-table-renderer.ts +81 -18
- package/src/components/datatable/datatable-utils.ts +12 -0
- package/src/components/datatable/datatable.css +98 -0
- package/src/components/datatable/datatable.ts +288 -583
- package/src/components/datatable/index.ts +8 -0
- package/src/components/datatable/types.ts +157 -23
- package/src/helpers/__tests__/dom.test.ts +776 -0
- package/src/helpers/__tests__/utils.test.ts +332 -0
- package/src/index.ts +15 -0
- package/skills/ktui-components/SKILL.md +0 -41
- package/skills/ktui-theming/SKILL.md +0 -50
- package/src/components/datatable/datatable-event-adapter.ts +0 -21
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KTUI - Free & Open-Source Tailwind UI Components by Keenthemes
|
|
3
|
+
* Copyright 2025 by Keenthemes Inc
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { KTDataTableConfigInterface, KTDataTableStateInterface } from './types';
|
|
7
|
+
|
|
8
|
+
/** Default page size options shown in the size selector */
|
|
9
|
+
export const DEFAULT_PAGE_SIZES = [5, 10, 20, 30, 50] as const;
|
|
10
|
+
|
|
11
|
+
/** Default debounce delay (ms) for search input */
|
|
12
|
+
export const DEFAULT_SEARCH_DELAY = 500;
|
|
13
|
+
|
|
14
|
+
/** Default maximum visible page buttons before showing '...' */
|
|
15
|
+
export const DEFAULT_PAGE_MORE_LIMIT = 3;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Default configuration for KTDataTable.
|
|
19
|
+
* Extracted from _initDefaultConfig() for separation of concerns.
|
|
20
|
+
* Note: sort.callback and search.callback are NOT included here because
|
|
21
|
+
* they require instance context (this._sortHandler). They are added
|
|
22
|
+
* in datatable.ts _initDefaultConfig().
|
|
23
|
+
*/
|
|
24
|
+
export const DATATABLE_DEFAULTS: Readonly<KTDataTableConfigInterface> = {
|
|
25
|
+
/**
|
|
26
|
+
* HTTP method for server-side API call
|
|
27
|
+
*/
|
|
28
|
+
requestMethod: 'GET',
|
|
29
|
+
/**
|
|
30
|
+
* Custom HTTP headers for the API request
|
|
31
|
+
*/
|
|
32
|
+
requestHeaders: {
|
|
33
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
34
|
+
},
|
|
35
|
+
/**
|
|
36
|
+
* Pagination info template
|
|
37
|
+
*/
|
|
38
|
+
info: '{start}-{end} of {total}',
|
|
39
|
+
/**
|
|
40
|
+
* Info text when there is no data
|
|
41
|
+
*/
|
|
42
|
+
infoEmpty: 'No records found',
|
|
43
|
+
/**
|
|
44
|
+
* Available page sizes
|
|
45
|
+
*/
|
|
46
|
+
pageSizes: [...DEFAULT_PAGE_SIZES],
|
|
47
|
+
/**
|
|
48
|
+
* Default page size
|
|
49
|
+
*/
|
|
50
|
+
pageSize: 10,
|
|
51
|
+
/**
|
|
52
|
+
* Enable or disable pagination more button
|
|
53
|
+
*/
|
|
54
|
+
pageMore: true,
|
|
55
|
+
/**
|
|
56
|
+
* Maximum number of pages before enabling pagination more button
|
|
57
|
+
*/
|
|
58
|
+
pageMoreLimit: 3,
|
|
59
|
+
/**
|
|
60
|
+
* Pagination button templates
|
|
61
|
+
*/
|
|
62
|
+
pagination: {
|
|
63
|
+
number: {
|
|
64
|
+
/**
|
|
65
|
+
* CSS classes to be added to the pagination button
|
|
66
|
+
*/
|
|
67
|
+
class: 'kt-datatable-pagination-button',
|
|
68
|
+
/**
|
|
69
|
+
* Text to be displayed in the pagination button
|
|
70
|
+
*/
|
|
71
|
+
text: '{page}',
|
|
72
|
+
},
|
|
73
|
+
previous: {
|
|
74
|
+
/**
|
|
75
|
+
* CSS classes to be added to the previous pagination button
|
|
76
|
+
*/
|
|
77
|
+
class: 'kt-datatable-pagination-button kt-datatable-pagination-prev',
|
|
78
|
+
/**
|
|
79
|
+
* Text to be displayed in the previous pagination button
|
|
80
|
+
*/
|
|
81
|
+
text: `
|
|
82
|
+
<svg class="rtl:transform rtl:rotate-180 size-3.5 shrink-0" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
83
|
+
<path d="M8.86501 16.7882V12.8481H21.1459C21.3724 12.8481 21.5897 12.7581 21.7498 12.5979C21.91 12.4378 22 12.2205 22 11.994C22 11.7675 21.91 11.5503 21.7498 11.3901C21.5897 11.2299 21.3724 11.1399 21.1459 11.1399H8.86501V7.2112C8.86628 7.10375 8.83517 6.9984 8.77573 6.90887C8.7163 6.81934 8.63129 6.74978 8.53177 6.70923C8.43225 6.66869 8.32283 6.65904 8.21775 6.68155C8.11267 6.70405 8.0168 6.75766 7.94262 6.83541L2.15981 11.6182C2.1092 11.668 2.06901 11.7274 2.04157 11.7929C2.01413 11.8584 2 11.9287 2 11.9997C2 12.0707 2.01413 12.141 2.04157 12.2065C2.06901 12.272 2.1092 12.3314 2.15981 12.3812L7.94262 17.164C8.0168 17.2417 8.11267 17.2953 8.21775 17.3178C8.32283 17.3403 8.43225 17.3307 8.53177 17.2902C8.63129 17.2496 8.7163 17.18 8.77573 17.0905C8.83517 17.001 8.86628 16.8956 8.86501 16.7882Z" fill="currentColor"/>
|
|
84
|
+
</svg>
|
|
85
|
+
`,
|
|
86
|
+
},
|
|
87
|
+
next: {
|
|
88
|
+
/**
|
|
89
|
+
* CSS classes to be added to the next pagination button
|
|
90
|
+
*/
|
|
91
|
+
class: 'kt-datatable-pagination-button kt-datatable-pagination-next',
|
|
92
|
+
/**
|
|
93
|
+
* Text to be displayed in the next pagination button
|
|
94
|
+
*/
|
|
95
|
+
text: `
|
|
96
|
+
<svg class="rtl:transform rtl:rotate-180 size-3.5 shrink-0" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
97
|
+
<path d="M15.135 7.21144V11.1516H2.85407C2.62756 11.1516 2.41032 11.2415 2.25015 11.4017C2.08998 11.5619 2 11.7791 2 12.0056C2 12.2321 2.08998 12.4494 2.25015 12.6096C2.41032 12.7697 2.62756 12.8597 2.85407 12.8597H15.135V16.7884C15.1337 16.8959 15.1648 17.0012 15.2243 17.0908C15.2837 17.1803 15.3687 17.2499 15.4682 17.2904C15.5677 17.3309 15.6772 17.3406 15.7822 17.3181C15.8873 17.2956 15.9832 17.242 16.0574 17.1642L21.8402 12.3814C21.8908 12.3316 21.931 12.2722 21.9584 12.2067C21.9859 12.1412 22 12.0709 22 11.9999C22 11.9289 21.9859 11.8586 21.9584 11.7931C21.931 11.7276 21.8908 11.6683 21.8402 11.6185L16.0574 6.83565C15.9832 6.75791 15.8873 6.70429 15.7822 6.68179C15.6772 6.65929 15.5677 6.66893 15.4682 6.70948C15.3687 6.75002 15.2837 6.81959 15.2243 6.90911C15.1648 6.99864 15.1337 7.10399 15.135 7.21144Z" fill="currentColor"/>
|
|
98
|
+
</svg>
|
|
99
|
+
`,
|
|
100
|
+
},
|
|
101
|
+
more: {
|
|
102
|
+
/**
|
|
103
|
+
* CSS classes to be added to the pagination more button
|
|
104
|
+
*/
|
|
105
|
+
class: 'kt-datatable-pagination-button kt-datatable-pagination-more',
|
|
106
|
+
/**
|
|
107
|
+
* Text to be displayed in the pagination more button
|
|
108
|
+
*/
|
|
109
|
+
text: '...',
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
/**
|
|
113
|
+
* Sorting options — classes only; callback is added in datatable.ts
|
|
114
|
+
*/
|
|
115
|
+
sort: {
|
|
116
|
+
/**
|
|
117
|
+
* CSS classes to be added to the sortable headers
|
|
118
|
+
*/
|
|
119
|
+
classes: {
|
|
120
|
+
base: 'kt-table-col',
|
|
121
|
+
asc: 'asc',
|
|
122
|
+
desc: 'desc',
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
/**
|
|
126
|
+
* Search options — delay only; callback is added in datatable.ts
|
|
127
|
+
*/
|
|
128
|
+
search: {
|
|
129
|
+
/**
|
|
130
|
+
* Delay in milliseconds before the search function is applied to the data array
|
|
131
|
+
* @default 500
|
|
132
|
+
*/
|
|
133
|
+
delay: 500, // ms
|
|
134
|
+
},
|
|
135
|
+
/**
|
|
136
|
+
* Loading spinner options
|
|
137
|
+
*/
|
|
138
|
+
loading: {
|
|
139
|
+
/**
|
|
140
|
+
* Template to be displayed during data fetching process
|
|
141
|
+
*/
|
|
142
|
+
template: `
|
|
143
|
+
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
|
|
144
|
+
<div class="kt-datatable-loading">
|
|
145
|
+
<svg class="animate-spin -ml-1 h-5 w-5 text-gray-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
146
|
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3"></circle>
|
|
147
|
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
148
|
+
</svg>
|
|
149
|
+
{content}
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
`,
|
|
153
|
+
/**
|
|
154
|
+
* Loading text to be displayed in the template
|
|
155
|
+
*/
|
|
156
|
+
content: 'Loading...',
|
|
157
|
+
},
|
|
158
|
+
/**
|
|
159
|
+
* Selectors of the elements to be targeted
|
|
160
|
+
*/
|
|
161
|
+
attributes: {
|
|
162
|
+
/**
|
|
163
|
+
* Data table element
|
|
164
|
+
*/
|
|
165
|
+
table: 'table[data-kt-datatable-table="true"]',
|
|
166
|
+
/**
|
|
167
|
+
* Pagination info element
|
|
168
|
+
*/
|
|
169
|
+
info: '[data-kt-datatable-info="true"]',
|
|
170
|
+
/**
|
|
171
|
+
* Page size dropdown element
|
|
172
|
+
*/
|
|
173
|
+
size: '[data-kt-datatable-size="true"]',
|
|
174
|
+
/**
|
|
175
|
+
* Pagination element
|
|
176
|
+
*/
|
|
177
|
+
pagination: '[data-kt-datatable-pagination="true"]',
|
|
178
|
+
/**
|
|
179
|
+
* Spinner element
|
|
180
|
+
*/
|
|
181
|
+
spinner: '[data-kt-datatable-spinner="true"]',
|
|
182
|
+
/**
|
|
183
|
+
* Checkbox element
|
|
184
|
+
*/
|
|
185
|
+
check: '[data-kt-datatable-check="true"]',
|
|
186
|
+
checkbox: '[data-kt-datatable-row-check="true"]',
|
|
187
|
+
},
|
|
188
|
+
/**
|
|
189
|
+
* Enable or disable state saving
|
|
190
|
+
*/
|
|
191
|
+
stateSave: true,
|
|
192
|
+
checkbox: {
|
|
193
|
+
checkedClass: 'checked',
|
|
194
|
+
},
|
|
195
|
+
/**
|
|
196
|
+
* Table layout algorithm: 'auto' (default) or 'fixed' for consistent column widths
|
|
197
|
+
*/
|
|
198
|
+
tableLayout: 'auto',
|
|
199
|
+
/**
|
|
200
|
+
* Private properties
|
|
201
|
+
*/
|
|
202
|
+
_state: {} as KTDataTableStateInterface,
|
|
203
|
+
loadingClass: 'loading',
|
|
204
|
+
};
|
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KTUI - Free & Open-Source Tailwind UI Components by Keenthemes
|
|
3
|
+
* Copyright 2025 by Keenthemes Inc
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
KTDataTableConfigInterface,
|
|
8
|
+
KTDataTableLayoutPluginContextInterface,
|
|
9
|
+
KTDataTableLayoutPluginInterface,
|
|
10
|
+
} from './types';
|
|
11
|
+
|
|
12
|
+
type Edge = 'left' | 'right';
|
|
13
|
+
|
|
14
|
+
const LOCKED_CELL_CLASS = 'kt-datatable-locked-cell';
|
|
15
|
+
const LOCKED_HEADER_CLASS = 'kt-datatable-locked-header';
|
|
16
|
+
const LOCKED_TOP_ROW_CLASS = 'kt-datatable-locked-top-row';
|
|
17
|
+
const LOCKED_BOTTOM_ROW_CLASS = 'kt-datatable-locked-bottom-row';
|
|
18
|
+
const LOCKED_LEFT_CLASS = 'kt-datatable-locked-left';
|
|
19
|
+
const LOCKED_RIGHT_CLASS = 'kt-datatable-locked-right';
|
|
20
|
+
const LOCKED_LAYOUT_SEPARATE_CLASS = 'kt-datatable-locked-layout-separate';
|
|
21
|
+
const LOCKED_HEADER_SECTION_CLASS = 'kt-datatable-locked-header-section';
|
|
22
|
+
|
|
23
|
+
const HEADER_Z_INDEX = 40;
|
|
24
|
+
const ROW_Z_INDEX = 30;
|
|
25
|
+
const COLUMN_Z_INDEX = 35;
|
|
26
|
+
const INTERSECTION_Z_INDEX = 45;
|
|
27
|
+
|
|
28
|
+
const toPositiveInteger = (value: number | undefined): number => {
|
|
29
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return Math.max(0, Math.floor(value));
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const hasStickyColumns = (config: KTDataTableConfigInterface): boolean => {
|
|
37
|
+
const lockedLayout = config.lockedLayout;
|
|
38
|
+
if (!lockedLayout?.stickyColumns) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
(lockedLayout.stickyColumns.left?.length || 0) > 0 ||
|
|
44
|
+
(lockedLayout.stickyColumns.right?.length || 0) > 0
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const hasLockedLayoutConfig = (config: KTDataTableConfigInterface): boolean => {
|
|
49
|
+
const lockedLayout = config.lockedLayout;
|
|
50
|
+
if (!lockedLayout) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
lockedLayout.stickyHeader === true ||
|
|
56
|
+
toPositiveInteger(lockedLayout.stickyRows?.top) > 0 ||
|
|
57
|
+
toPositiveInteger(lockedLayout.stickyRows?.bottom) > 0 ||
|
|
58
|
+
(lockedLayout.stickyColumns?.left?.length || 0) > 0 ||
|
|
59
|
+
(lockedLayout.stickyColumns?.right?.length || 0) > 0
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const getScrollContainer = (rootElement: HTMLElement): HTMLElement => {
|
|
64
|
+
return (
|
|
65
|
+
rootElement.closest<HTMLElement>('.kt-table-wrapper') ||
|
|
66
|
+
rootElement.querySelector<HTMLElement>('.kt-table-wrapper') ||
|
|
67
|
+
rootElement
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const clearStickyStyles = (
|
|
72
|
+
tableElement: HTMLTableElement,
|
|
73
|
+
scrollContainer: HTMLElement,
|
|
74
|
+
): void => {
|
|
75
|
+
tableElement.classList.remove(
|
|
76
|
+
'kt-datatable-locked-layout',
|
|
77
|
+
LOCKED_LAYOUT_SEPARATE_CLASS,
|
|
78
|
+
);
|
|
79
|
+
scrollContainer.classList.remove('kt-datatable-locked-layout-host');
|
|
80
|
+
tableElement.style.borderCollapse = '';
|
|
81
|
+
tableElement.style.borderSpacing = '';
|
|
82
|
+
|
|
83
|
+
const theadElement = tableElement.tHead;
|
|
84
|
+
if (theadElement) {
|
|
85
|
+
theadElement.classList.remove(LOCKED_HEADER_SECTION_CLASS);
|
|
86
|
+
theadElement.style.position = '';
|
|
87
|
+
theadElement.style.top = '';
|
|
88
|
+
theadElement.style.zIndex = '';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const stickyElements = tableElement.querySelectorAll<HTMLElement>(
|
|
92
|
+
`.${LOCKED_CELL_CLASS}`,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
stickyElements.forEach((element) => {
|
|
96
|
+
element.classList.remove(
|
|
97
|
+
LOCKED_CELL_CLASS,
|
|
98
|
+
LOCKED_HEADER_CLASS,
|
|
99
|
+
LOCKED_TOP_ROW_CLASS,
|
|
100
|
+
LOCKED_BOTTOM_ROW_CLASS,
|
|
101
|
+
LOCKED_LEFT_CLASS,
|
|
102
|
+
LOCKED_RIGHT_CLASS,
|
|
103
|
+
);
|
|
104
|
+
element.style.position = '';
|
|
105
|
+
element.style.top = '';
|
|
106
|
+
element.style.bottom = '';
|
|
107
|
+
element.style.left = '';
|
|
108
|
+
element.style.right = '';
|
|
109
|
+
element.style.zIndex = '';
|
|
110
|
+
element.style.backgroundColor = '';
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const getDirection = (tableElement: HTMLTableElement): 'ltr' | 'rtl' => {
|
|
115
|
+
const scopedDir = tableElement
|
|
116
|
+
.closest<HTMLElement>('[dir]')
|
|
117
|
+
?.getAttribute('dir');
|
|
118
|
+
const globalDir =
|
|
119
|
+
typeof document !== 'undefined'
|
|
120
|
+
? document.documentElement.getAttribute('dir')
|
|
121
|
+
: null;
|
|
122
|
+
return scopedDir === 'rtl' || globalDir === 'rtl' ? 'rtl' : 'ltr';
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const resolveEdgeProperty = (edge: Edge, direction: 'ltr' | 'rtl'): Edge => {
|
|
126
|
+
if (direction === 'rtl') {
|
|
127
|
+
return edge === 'left' ? 'right' : 'left';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return edge;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const setStickyEdge = (
|
|
134
|
+
element: HTMLElement,
|
|
135
|
+
edge: Edge,
|
|
136
|
+
offset: number,
|
|
137
|
+
direction: 'ltr' | 'rtl',
|
|
138
|
+
): void => {
|
|
139
|
+
const resolvedEdge = resolveEdgeProperty(edge, direction);
|
|
140
|
+
if (resolvedEdge === 'left') {
|
|
141
|
+
element.style.left = `${offset}px`;
|
|
142
|
+
element.style.right = '';
|
|
143
|
+
} else {
|
|
144
|
+
element.style.right = `${offset}px`;
|
|
145
|
+
element.style.left = '';
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const ensureStickyCell = (
|
|
150
|
+
element: HTMLElement,
|
|
151
|
+
className: string,
|
|
152
|
+
zIndex: number,
|
|
153
|
+
): void => {
|
|
154
|
+
element.classList.add(LOCKED_CELL_CLASS, className);
|
|
155
|
+
element.style.position = 'sticky';
|
|
156
|
+
element.style.zIndex = String(zIndex);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const measureStickyHeaderHeight = (
|
|
160
|
+
theadElement: HTMLTableSectionElement,
|
|
161
|
+
): number => Math.round(theadElement.offsetHeight);
|
|
162
|
+
|
|
163
|
+
/** Offset for top sticky body rows so they sit flush under a sticky header. */
|
|
164
|
+
const getStickyTopRowOffset = (
|
|
165
|
+
headerHeight: number,
|
|
166
|
+
useCollapsedBorders: boolean,
|
|
167
|
+
): number => {
|
|
168
|
+
if (headerHeight <= 0) {
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Collapsed row borders are shared between thead and the first tbody row.
|
|
173
|
+
return useCollapsedBorders ? headerHeight - 1 : headerHeight;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const markIntersectionZIndex = (element: HTMLElement): void => {
|
|
177
|
+
const isRowLocked =
|
|
178
|
+
element.classList.contains(LOCKED_HEADER_CLASS) ||
|
|
179
|
+
element.classList.contains(LOCKED_TOP_ROW_CLASS) ||
|
|
180
|
+
element.classList.contains(LOCKED_BOTTOM_ROW_CLASS);
|
|
181
|
+
const isColumnLocked =
|
|
182
|
+
element.classList.contains(LOCKED_LEFT_CLASS) ||
|
|
183
|
+
element.classList.contains(LOCKED_RIGHT_CLASS);
|
|
184
|
+
|
|
185
|
+
if (isRowLocked && isColumnLocked) {
|
|
186
|
+
element.style.zIndex = String(INTERSECTION_Z_INDEX);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const applyStickyHeader = (
|
|
191
|
+
theadElement: HTMLTableSectionElement,
|
|
192
|
+
enabled: boolean,
|
|
193
|
+
useSectionSticky: boolean,
|
|
194
|
+
): number => {
|
|
195
|
+
if (!enabled) {
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (useSectionSticky) {
|
|
200
|
+
theadElement.classList.add(LOCKED_HEADER_SECTION_CLASS);
|
|
201
|
+
theadElement.style.position = 'sticky';
|
|
202
|
+
theadElement.style.top = '0';
|
|
203
|
+
theadElement.style.zIndex = String(HEADER_Z_INDEX);
|
|
204
|
+
|
|
205
|
+
Array.from(theadElement.rows).forEach((row) => {
|
|
206
|
+
Array.from(row.cells).forEach((cell) => {
|
|
207
|
+
const headerCell = cell as HTMLTableCellElement;
|
|
208
|
+
headerCell.classList.add(LOCKED_CELL_CLASS, LOCKED_HEADER_CLASS);
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
return measureStickyHeaderHeight(theadElement);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
let cumulativeTop = 0;
|
|
216
|
+
Array.from(theadElement.rows).forEach((row) => {
|
|
217
|
+
const rowTop = cumulativeTop;
|
|
218
|
+
Array.from(row.cells).forEach((cell) => {
|
|
219
|
+
const headerCell = cell as HTMLTableCellElement;
|
|
220
|
+
ensureStickyCell(headerCell, LOCKED_HEADER_CLASS, HEADER_Z_INDEX);
|
|
221
|
+
headerCell.style.top = `${rowTop}px`;
|
|
222
|
+
});
|
|
223
|
+
cumulativeTop += row.offsetHeight;
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
return cumulativeTop;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const applyStickyRows = (
|
|
230
|
+
tbodyElement: HTMLTableSectionElement,
|
|
231
|
+
headerHeight: number,
|
|
232
|
+
topCount: number,
|
|
233
|
+
bottomCount: number,
|
|
234
|
+
useCollapsedBorders: boolean,
|
|
235
|
+
): void => {
|
|
236
|
+
const rows = Array.from(tbodyElement.rows);
|
|
237
|
+
|
|
238
|
+
let topOffset = getStickyTopRowOffset(headerHeight, useCollapsedBorders);
|
|
239
|
+
rows.slice(0, topCount).forEach((row) => {
|
|
240
|
+
const rowTop = topOffset;
|
|
241
|
+
Array.from(row.cells).forEach((cell) => {
|
|
242
|
+
const td = cell as HTMLTableCellElement;
|
|
243
|
+
ensureStickyCell(td, LOCKED_TOP_ROW_CLASS, ROW_Z_INDEX);
|
|
244
|
+
td.style.top = `${rowTop}px`;
|
|
245
|
+
});
|
|
246
|
+
topOffset += row.offsetHeight;
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
let bottomOffset = 0;
|
|
250
|
+
rows
|
|
251
|
+
.slice(Math.max(0, rows.length - bottomCount))
|
|
252
|
+
.reverse()
|
|
253
|
+
.forEach((row) => {
|
|
254
|
+
const rowBottom = bottomOffset;
|
|
255
|
+
Array.from(row.cells).forEach((cell) => {
|
|
256
|
+
const td = cell as HTMLTableCellElement;
|
|
257
|
+
ensureStickyCell(td, LOCKED_BOTTOM_ROW_CLASS, ROW_Z_INDEX);
|
|
258
|
+
td.style.bottom = `${rowBottom}px`;
|
|
259
|
+
});
|
|
260
|
+
bottomOffset += row.offsetHeight;
|
|
261
|
+
});
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const getColumnIndexMap = (
|
|
265
|
+
theadElement: HTMLTableSectionElement,
|
|
266
|
+
config: KTDataTableConfigInterface,
|
|
267
|
+
): Map<string, number> => {
|
|
268
|
+
const map = new Map<string, number>();
|
|
269
|
+
const typedHeaders = Array.from(
|
|
270
|
+
theadElement.querySelectorAll<HTMLTableCellElement>(
|
|
271
|
+
'th[data-kt-datatable-column]',
|
|
272
|
+
),
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
if (typedHeaders.length > 0) {
|
|
276
|
+
typedHeaders.forEach((th, index) => {
|
|
277
|
+
const column = th.getAttribute('data-kt-datatable-column');
|
|
278
|
+
if (column) {
|
|
279
|
+
map.set(column, index);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
return map;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (config.columns) {
|
|
286
|
+
Object.keys(config.columns).forEach((key, index) => {
|
|
287
|
+
map.set(key, index);
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return map;
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const getColumnCells = (
|
|
295
|
+
tableElement: HTMLTableElement,
|
|
296
|
+
columnIndex: number,
|
|
297
|
+
): HTMLTableCellElement[] => {
|
|
298
|
+
const cells: HTMLTableCellElement[] = [];
|
|
299
|
+
tableElement.querySelectorAll('tr').forEach((row) => {
|
|
300
|
+
const cell = row.children.item(columnIndex);
|
|
301
|
+
if (cell instanceof HTMLTableCellElement) {
|
|
302
|
+
cells.push(cell);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
return cells;
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const applyStickyColumns = (
|
|
309
|
+
tableElement: HTMLTableElement,
|
|
310
|
+
theadElement: HTMLTableSectionElement,
|
|
311
|
+
config: KTDataTableConfigInterface,
|
|
312
|
+
): void => {
|
|
313
|
+
const lockedColumns = config.lockedLayout?.stickyColumns;
|
|
314
|
+
if (!lockedColumns) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const direction = getDirection(tableElement);
|
|
319
|
+
const columnMap = getColumnIndexMap(theadElement, config);
|
|
320
|
+
|
|
321
|
+
let leftOffset = 0;
|
|
322
|
+
(lockedColumns.left || []).forEach((key) => {
|
|
323
|
+
const index = columnMap.get(key);
|
|
324
|
+
if (typeof index !== 'number') {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const cells = getColumnCells(tableElement, index);
|
|
329
|
+
if (cells.length === 0) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const width = cells[0].getBoundingClientRect().width;
|
|
334
|
+
cells.forEach((cell) => {
|
|
335
|
+
ensureStickyCell(cell, LOCKED_LEFT_CLASS, COLUMN_Z_INDEX);
|
|
336
|
+
setStickyEdge(cell, 'left', leftOffset, direction);
|
|
337
|
+
});
|
|
338
|
+
leftOffset += width;
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
let rightOffset = 0;
|
|
342
|
+
[...(lockedColumns.right || [])].reverse().forEach((key) => {
|
|
343
|
+
const index = columnMap.get(key);
|
|
344
|
+
if (typeof index !== 'number') {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const cells = getColumnCells(tableElement, index);
|
|
349
|
+
if (cells.length === 0) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const width = cells[0].getBoundingClientRect().width;
|
|
354
|
+
cells.forEach((cell) => {
|
|
355
|
+
ensureStickyCell(cell, LOCKED_RIGHT_CLASS, COLUMN_Z_INDEX);
|
|
356
|
+
setStickyEdge(cell, 'right', rightOffset, direction);
|
|
357
|
+
});
|
|
358
|
+
rightOffset += width;
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
tableElement
|
|
362
|
+
.querySelectorAll<HTMLElement>(`.${LOCKED_CELL_CLASS}`)
|
|
363
|
+
.forEach(markIntersectionZIndex);
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
export const createStickyLayoutPlugin =
|
|
367
|
+
(): KTDataTableLayoutPluginInterface => {
|
|
368
|
+
let resizeHandler: (() => void) | null = null;
|
|
369
|
+
let scrollContainerTarget: HTMLElement | null = null;
|
|
370
|
+
let isApplying = false;
|
|
371
|
+
|
|
372
|
+
const applyLayout = (
|
|
373
|
+
ctx: KTDataTableLayoutPluginContextInterface,
|
|
374
|
+
): void => {
|
|
375
|
+
if (isApplying || !hasLockedLayoutConfig(ctx.config)) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
isApplying = true;
|
|
380
|
+
try {
|
|
381
|
+
const scrollContainer = getScrollContainer(ctx.rootElement);
|
|
382
|
+
clearStickyStyles(ctx.tableElement, scrollContainer);
|
|
383
|
+
ctx.tableElement.classList.add('kt-datatable-locked-layout');
|
|
384
|
+
scrollContainer.classList.add('kt-datatable-locked-layout-host');
|
|
385
|
+
|
|
386
|
+
if (hasStickyColumns(ctx.config)) {
|
|
387
|
+
ctx.tableElement.classList.add(LOCKED_LAYOUT_SEPARATE_CLASS);
|
|
388
|
+
ctx.tableElement.style.borderCollapse = 'separate';
|
|
389
|
+
ctx.tableElement.style.borderSpacing = '0';
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const lockedLayout = ctx.config.lockedLayout || {};
|
|
393
|
+
const useCollapsedBorders = !hasStickyColumns(ctx.config);
|
|
394
|
+
const headerHeight = applyStickyHeader(
|
|
395
|
+
ctx.theadElement,
|
|
396
|
+
lockedLayout.stickyHeader === true,
|
|
397
|
+
useCollapsedBorders,
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
applyStickyRows(
|
|
401
|
+
ctx.tbodyElement,
|
|
402
|
+
headerHeight,
|
|
403
|
+
toPositiveInteger(lockedLayout.stickyRows?.top),
|
|
404
|
+
toPositiveInteger(lockedLayout.stickyRows?.bottom),
|
|
405
|
+
useCollapsedBorders,
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
applyStickyColumns(ctx.tableElement, ctx.theadElement, ctx.config);
|
|
409
|
+
} finally {
|
|
410
|
+
isApplying = false;
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
const detachResizeListener = (): void => {
|
|
415
|
+
if (!resizeHandler) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
window.removeEventListener('resize', resizeHandler);
|
|
420
|
+
if (scrollContainerTarget) {
|
|
421
|
+
scrollContainerTarget.removeEventListener('scroll', resizeHandler);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
resizeHandler = null;
|
|
425
|
+
scrollContainerTarget = null;
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
return {
|
|
429
|
+
beforeDraw: (ctx) => {
|
|
430
|
+
const scrollContainer = getScrollContainer(ctx.rootElement);
|
|
431
|
+
clearStickyStyles(ctx.tableElement, scrollContainer);
|
|
432
|
+
},
|
|
433
|
+
afterDraw: (ctx) => {
|
|
434
|
+
detachResizeListener();
|
|
435
|
+
applyLayout(ctx);
|
|
436
|
+
|
|
437
|
+
// Throttle re-layout with rAF to avoid running on every resize/scroll event
|
|
438
|
+
let rafId = 0;
|
|
439
|
+
const throttledApply = () => {
|
|
440
|
+
if (rafId) return;
|
|
441
|
+
rafId = requestAnimationFrame(() => {
|
|
442
|
+
rafId = 0;
|
|
443
|
+
applyLayout(ctx);
|
|
444
|
+
});
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
const scrollContainer = getScrollContainer(ctx.rootElement);
|
|
448
|
+
resizeHandler = throttledApply;
|
|
449
|
+
window.addEventListener('resize', resizeHandler);
|
|
450
|
+
scrollContainerTarget = scrollContainer;
|
|
451
|
+
scrollContainer.addEventListener('scroll', resizeHandler);
|
|
452
|
+
},
|
|
453
|
+
dispose: (ctx) => {
|
|
454
|
+
detachResizeListener();
|
|
455
|
+
const scrollContainer = getScrollContainer(ctx.rootElement);
|
|
456
|
+
clearStickyStyles(ctx.tableElement, scrollContainer);
|
|
457
|
+
},
|
|
458
|
+
};
|
|
459
|
+
};
|