@astronox/core 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/package.json +7 -1
  2. package/src/admin/contracts.ts +28 -0
  3. package/src/admin/index.ts +2 -0
  4. package/src/admin/module.ts +28 -0
  5. package/src/auth/contracts.ts +53 -0
  6. package/src/auth/firebase/client.ts +53 -0
  7. package/src/auth/firebase/contracts.ts +8 -0
  8. package/src/auth/firebase/index.ts +1 -0
  9. package/src/auth/index.ts +2 -0
  10. package/src/auth/module.ts +99 -0
  11. package/src/client/index.ts +3 -0
  12. package/src/client/preferences.ts +400 -0
  13. package/src/components/actions/Button/Button.astro +42 -6
  14. package/src/components/actions/Button/Button.css +19 -0
  15. package/src/components/actions/IdentityProviderAction/IdentityProviderAction.astro +35 -0
  16. package/src/components/content/CardBody/CardBody.astro +3 -0
  17. package/src/components/content/CardBody/CardBody.css +10 -0
  18. package/src/components/content/MarkdownContent/markdownContentRenderer.ts +549 -0
  19. package/src/components/feedback/PreferenceBootstrap/PreferenceBootstrap.astro +403 -0
  20. package/src/components/forms/Checkbox/Checkbox.astro +6 -0
  21. package/src/components/forms/ChoiceGroup/ChoiceGroup.astro +57 -0
  22. package/src/components/forms/ChoiceGroup/ChoiceGroup.css +108 -0
  23. package/src/components/forms/ChoiceGroup/contracts.ts +9 -0
  24. package/src/components/forms/DateTimeInput/DateTimeInput.astro +14 -0
  25. package/src/components/forms/TextInput/TextInput.astro +7 -1
  26. package/src/components/forms/Textarea/Textarea.astro +32 -0
  27. package/src/components/forms/Textarea/Textarea.css +54 -0
  28. package/src/components/icons/BackIcon.astro +26 -0
  29. package/src/components/icons/DeleteIcon.astro +22 -0
  30. package/src/components/icons/StatusIndicatorIcon.astro +16 -0
  31. package/src/components/index.ts +10 -0
  32. package/src/components/media/CanvasFrame/CanvasFrame.astro +1 -17
  33. package/src/components/media/EmbedFrame/EmbedFrame.astro +1 -19
  34. package/src/components/media/EmbedFrame/EmbedFrame.css +0 -2
  35. package/src/components/navigation/SubNavigation/SubNavigation.css +0 -5
  36. package/src/composition/SurfaceGroup/SurfaceGroup.css +21 -7
  37. package/src/composition/SurfaceGroup/stickySurfaceGroupController.ts +3 -0
  38. package/src/data/firestore/client/contracts.ts +55 -0
  39. package/src/data/firestore/client/firebase.ts +12 -0
  40. package/src/data/firestore/client/index.ts +3 -0
  41. package/src/data/firestore/client/repository.ts +111 -0
  42. package/src/display/contracts.ts +40 -0
  43. package/src/display/index.ts +1 -0
  44. package/src/display/tableProgressive.ts +62 -0
  45. package/src/patterns/Combobox/Combobox.astro +23 -0
  46. package/src/patterns/Fullscreen/FullscreenControl.astro +4 -14
  47. package/src/patterns/Fullscreen/FullscreenControl.css +0 -26
  48. package/src/patterns/Modal/Modal.astro +1 -1
  49. package/src/patterns/Modal/Modal.css +38 -1
  50. package/src/patterns/Table/Table.astro +122 -7
  51. package/src/patterns/Table/TableBody.astro +8 -0
  52. package/src/patterns/Table/TableCell.css +6 -0
  53. package/src/patterns/Table/TableCellValue.astro +31 -0
  54. package/src/patterns/Table/TableFooter.astro +3 -1
  55. package/src/patterns/Table/TableHeader.astro +1 -0
  56. package/src/patterns/Table/TablePagination.astro +63 -47
  57. package/src/patterns/Table/TableSkeleton.astro +18 -22
  58. package/src/patterns/Table/TableSkeleton.css +30 -5
  59. package/src/patterns/Table/tableClientController.ts +253 -0
  60. package/src/patterns/Table/tableFilterController.ts +23 -31
  61. package/src/patterns/Table/tableProgressiveController.ts +615 -227
  62. package/src/patterns/Table/tableSortController.ts +25 -7
  63. package/src/styles/interaction.css +16 -0
  64. package/src/styles/style.css +2 -0
@@ -1,70 +1,86 @@
1
1
  ---
2
2
  import type { HTMLAttributes } from 'astro/types';
3
- import type {
4
- TableColumn,
5
- TablePaginationModel,
6
- } from '../../display/contracts';
3
+ import type { TablePaginationModel } from '../../display/contracts';
7
4
  import TableFooter from './TableFooter.astro';
8
- import TableSkeleton from './TableSkeleton.astro';
9
5
 
10
6
  type Props = HTMLAttributes<'div'> & {
11
- columns: readonly TableColumn[];
12
7
  errorLabel: string;
13
8
  loadingLabel: string;
14
9
  nextLabel: string;
15
- pagination: TablePaginationModel;
16
- skeletonRows?: number;
10
+ pagination?: TablePaginationModel;
11
+ progressive?: boolean;
17
12
  };
18
13
 
19
14
  const {
20
15
  class: className,
21
- columns,
22
16
  errorLabel,
23
17
  loadingLabel,
24
18
  nextLabel,
25
19
  pagination,
26
- skeletonRows = 3,
20
+ progressive = false,
27
21
  ...attrs
28
22
  }: Props = Astro.props;
29
23
  ---
30
24
 
31
25
  {
32
- pagination.nextHref ? (
33
- <TableFooter
34
- {...attrs}
35
- class:list={['table-pagination', className]}
36
- padding="none"
37
- data-table-pagination
38
- data-table-page={pagination.page}
39
- data-table-total-pages={pagination.totalPages}
40
- data-table-loading-label={loadingLabel}
41
- data-table-error-label={errorLabel}
42
- data-table-progressive-state="idle"
43
- >
44
- <template data-table-skeleton-template>
45
- <TableSkeleton columns={columns} rows={skeletonRows} />
46
- </template>
47
- <div class="table-pagination__content">
48
- <a
49
- class="table-pagination__link"
50
- href={pagination.nextHref}
51
- data-table-pagination-link
52
- >
53
- {nextLabel}
54
- </a>
55
- <span
56
- class="table-pagination__status"
57
- data-table-pagination-status
58
- role="status"
59
- aria-live="polite"
60
- hidden
61
- />
62
- <span
63
- class="table-pagination__sentinel"
64
- data-table-pagination-sentinel
65
- aria-hidden="true"
66
- />
67
- </div>
68
- </TableFooter>
26
+ pagination?.nextHref || progressive ? (
27
+ <>
28
+ <TableFooter
29
+ {...attrs}
30
+ class:list={['table-pagination', className]}
31
+ padding="none"
32
+ surface={!progressive}
33
+ data-table-pagination
34
+ data-table-loading-label={loadingLabel}
35
+ data-table-error-label={errorLabel}
36
+ data-table-progressive-state="idle"
37
+ hidden={progressive || !pagination?.nextHref}
38
+ >
39
+ <div class="table-pagination__content">
40
+ {
41
+ pagination?.nextHref ? (
42
+ <a
43
+ class="table-pagination__link"
44
+ href={pagination.nextHref}
45
+ data-table-pagination-link
46
+ >
47
+ {nextLabel}
48
+ </a>
49
+ ) : null
50
+ }
51
+ <span
52
+ class="table-pagination__status"
53
+ data-table-pagination-status
54
+ role="status"
55
+ aria-live="polite"
56
+ hidden
57
+ />
58
+ <span
59
+ class="table-pagination__sentinel"
60
+ data-table-pagination-sentinel
61
+ aria-hidden="true"
62
+ />
63
+ </div>
64
+ </TableFooter>
65
+ {
66
+ progressive && pagination?.nextHref ? (
67
+ <noscript>
68
+ <TableFooter
69
+ class:list={['table-pagination', className]}
70
+ padding="none"
71
+ >
72
+ <div class="table-pagination__content">
73
+ <a
74
+ class="table-pagination__link"
75
+ href={pagination.nextHref}
76
+ >
77
+ {nextLabel}
78
+ </a>
79
+ </div>
80
+ </TableFooter>
81
+ </noscript>
82
+ ) : null
83
+ }
84
+ </>
69
85
  ) : null
70
86
  }
@@ -11,26 +11,22 @@ const { columns, rows = 3 }: Props = Astro.props;
11
11
  const rowCount = Math.max(1, Math.floor(rows));
12
12
  ---
13
13
 
14
- <table class="table-skeleton" aria-hidden="true">
15
- <tbody>
16
- {
17
- Array.from({ length: rowCount }, () => (
18
- <tr
19
- class="table-skeleton__row"
20
- data-table-skeleton-row
21
- aria-hidden="true"
14
+ {
15
+ Array.from({ length: rowCount }, () => (
16
+ <tr
17
+ class="table-skeleton__row"
18
+ data-table-skeleton-row
19
+ aria-hidden="true"
20
+ >
21
+ {columns.map((column) => (
22
+ <td
23
+ class="table-skeleton__cell"
24
+ data-align={getTableColumnAlignment(column)}
25
+ data-table-column-role={column.role}
22
26
  >
23
- {columns.map((column) => (
24
- <td
25
- class="table-skeleton__cell"
26
- data-align={getTableColumnAlignment(column)}
27
- data-table-column-role={column.role}
28
- >
29
- <span class="table-skeleton__placeholder" />
30
- </td>
31
- ))}
32
- </tr>
33
- ))
34
- }
35
- </tbody>
36
- </table>
27
+ <span class="table-skeleton__placeholder" />
28
+ </td>
29
+ ))}
30
+ </tr>
31
+ ))
32
+ }
@@ -1,16 +1,41 @@
1
- .table-skeleton {
2
- display: none;
3
- }
4
-
5
1
  .table-skeleton__row {
6
- display: grid;
2
+ display: none;
7
3
  min-width: 0;
8
4
  min-height: var(--table-row-min-block-size);
9
5
  grid-column: 1 / -1;
10
6
  grid-template-columns: subgrid;
7
+ border-block-start: 0;
8
+ }
9
+
10
+ .table-skeleton__row + .table-skeleton__row,
11
+ [data-nox-table-enhanced]
12
+ .table[data-table-loading-state='appending']
13
+ .table-body__row:not([hidden])
14
+ + .table-skeleton__row {
11
15
  border-block-start: var(--nox-border-width) solid var(--nox-border);
12
16
  }
13
17
 
18
+ [data-nox-table-enhanced]
19
+ .table[data-table-loading-state='pending']
20
+ .table-body__row[data-table-row],
21
+ [data-nox-table-enhanced]
22
+ .table[data-table-loading-state='refreshing']
23
+ .table-body__row[data-table-row] {
24
+ display: none;
25
+ }
26
+
27
+ [data-nox-table-enhanced]
28
+ .table[data-table-loading-state='pending']
29
+ .table-skeleton__row,
30
+ [data-nox-table-enhanced]
31
+ .table[data-table-loading-state='refreshing']
32
+ .table-skeleton__row,
33
+ [data-nox-table-enhanced]
34
+ .table[data-table-loading-state='appending']
35
+ .table-skeleton__row {
36
+ display: grid;
37
+ }
38
+
14
39
  .table-skeleton__cell {
15
40
  display: grid;
16
41
  min-width: 0;
@@ -0,0 +1,253 @@
1
+ export type ClientTableState = 'ready' | 'empty' | 'error';
2
+
3
+ export type ClientTablePage<TItem> = {
4
+ items: readonly TItem[];
5
+ nextCursor?: string;
6
+ };
7
+
8
+ export type ClientTablePaginationController = {
9
+ destroy(): void;
10
+ remove(key: string): void;
11
+ };
12
+
13
+ type ClientTablePaginationOptions<TItem> = {
14
+ createRow(item: TItem): HTMLElement | null;
15
+ emptyMessage: string;
16
+ errorMessage: string;
17
+ getKey(item: TItem): string;
18
+ loadPage(cursor?: string): Promise<ClientTablePage<TItem>>;
19
+ root: HTMLElement;
20
+ };
21
+
22
+ export function replaceClientTableRows(
23
+ root: HTMLElement,
24
+ rows: readonly HTMLElement[],
25
+ {
26
+ message,
27
+ state = rows.length > 0 ? 'ready' : 'empty',
28
+ }: {
29
+ message?: string;
30
+ state?: ClientTableState;
31
+ } = {},
32
+ ): void {
33
+ const rowsRoot = root.querySelector<HTMLElement>('[data-table-rows]');
34
+ if (!rowsRoot) return;
35
+
36
+ rowsRoot
37
+ .querySelectorAll(
38
+ ':scope > [data-table-row], :scope > [data-table-skeleton-row]',
39
+ )
40
+ .forEach((row) => row.remove());
41
+
42
+ const stateRow = rowsRoot.querySelector<HTMLElement>(
43
+ ':scope > [data-table-state]',
44
+ );
45
+ if (stateRow) {
46
+ stateRow.hidden = state === 'ready';
47
+ stateRow.dataset.tableState = state === 'ready' ? 'empty' : state;
48
+ const content = stateRow.querySelector<HTMLElement>(
49
+ '[data-widget="text-block"]',
50
+ );
51
+ if (content && message !== undefined) content.textContent = message;
52
+ if (content) {
53
+ content.setAttribute('role', state === 'error' ? 'alert' : 'status');
54
+ }
55
+ }
56
+
57
+ if (state === 'ready') {
58
+ const fragment = document.createDocumentFragment();
59
+ rows.forEach((row) => {
60
+ row.hidden = false;
61
+ fragment.append(row);
62
+ });
63
+ rowsRoot.insertBefore(fragment, stateRow);
64
+ root.dispatchEvent(
65
+ new CustomEvent('table:rows-appended', {
66
+ bubbles: true,
67
+ detail: { rows },
68
+ }),
69
+ );
70
+ }
71
+
72
+ root.dataset.tableLoadingState = state;
73
+ root.removeAttribute('aria-busy');
74
+ }
75
+
76
+ export function createClientTablePagination<TItem>({
77
+ createRow,
78
+ emptyMessage,
79
+ errorMessage,
80
+ getKey,
81
+ loadPage,
82
+ root,
83
+ }: ClientTablePaginationOptions<TItem>): ClientTablePaginationController {
84
+ const rowsRoot = root.querySelector<HTMLElement>('[data-table-rows]');
85
+ const pagination = root.querySelector<HTMLElement>('[data-table-pagination]');
86
+ const sentinel = root.querySelector<HTMLElement>(
87
+ '[data-table-pagination-sentinel]',
88
+ );
89
+ const skeletonTemplate = root.querySelector<HTMLTemplateElement>(
90
+ '[data-table-skeleton-template]',
91
+ );
92
+ const keys = new Set<string>();
93
+ let nextCursor: string | undefined;
94
+ let loading = false;
95
+ let disposed = false;
96
+
97
+ if (pagination) {
98
+ pagination.hidden = false;
99
+ pagination.dataset.tableProgressiveEnhanced = 'true';
100
+ }
101
+
102
+ const setPaginationState = (state: 'idle' | 'loading' | 'error'): void => {
103
+ if (!pagination) return;
104
+ pagination.dataset.tableProgressiveState = state;
105
+ const status = pagination.querySelector<HTMLElement>(
106
+ '[data-table-pagination-status]',
107
+ );
108
+ if (!status) return;
109
+ status.textContent =
110
+ state === 'loading'
111
+ ? (pagination.dataset.tableLoadingLabel ?? '')
112
+ : state === 'error'
113
+ ? (pagination.dataset.tableErrorLabel ?? '')
114
+ : '';
115
+ status.hidden = state === 'idle';
116
+ };
117
+
118
+ const appendSkeleton = (): HTMLElement[] => {
119
+ if (!rowsRoot || !skeletonTemplate) return [];
120
+ const skeletonRows = Array.from(
121
+ skeletonTemplate.content.querySelectorAll<HTMLElement>(
122
+ '[data-table-skeleton-row]',
123
+ ),
124
+ ).map((row) => row.cloneNode(true) as HTMLElement);
125
+ const stateRow = rowsRoot.querySelector<HTMLElement>(
126
+ ':scope > [data-table-state]',
127
+ );
128
+ const fragment = document.createDocumentFragment();
129
+ skeletonRows.forEach((row) => fragment.append(row));
130
+ rowsRoot.insertBefore(fragment, stateRow);
131
+ return skeletonRows;
132
+ };
133
+
134
+ const createPageRows = (page: ClientTablePage<TItem>): HTMLElement[] => {
135
+ const rows: HTMLElement[] = [];
136
+ for (const item of page.items) {
137
+ const key = getKey(item);
138
+ if (keys.has(key)) continue;
139
+ const row = createRow(item);
140
+ if (!row) continue;
141
+ keys.add(key);
142
+ rows.push(row);
143
+ }
144
+ nextCursor = page.nextCursor;
145
+ return rows;
146
+ };
147
+
148
+ const appendRows = (rows: readonly HTMLElement[]): void => {
149
+ if (!rowsRoot) return;
150
+ const stateRow = rowsRoot.querySelector<HTMLElement>(
151
+ ':scope > [data-table-state]',
152
+ );
153
+ const fragment = document.createDocumentFragment();
154
+ rows.forEach((row) => {
155
+ row.hidden = false;
156
+ fragment.append(row);
157
+ });
158
+ rowsRoot.insertBefore(fragment, stateRow);
159
+ root.dispatchEvent(
160
+ new CustomEvent('table:rows-appended', {
161
+ bubbles: true,
162
+ detail: { rows },
163
+ }),
164
+ );
165
+ root.dataset.tableLoadingState = 'ready';
166
+ root.removeAttribute('aria-busy');
167
+ };
168
+
169
+ const load = async (append: boolean): Promise<void> => {
170
+ if (loading || disposed || (append && !nextCursor)) return;
171
+ loading = true;
172
+ root.setAttribute('aria-busy', 'true');
173
+ root.dataset.tableLoadingState = append ? 'appending' : 'pending';
174
+ setPaginationState('loading');
175
+ const skeletonRows = append ? appendSkeleton() : [];
176
+ let shouldContinue = false;
177
+
178
+ try {
179
+ const page = await loadPage(append ? nextCursor : undefined);
180
+ if (disposed) return;
181
+ const rows = createPageRows(page);
182
+ if (append) {
183
+ skeletonRows.forEach((row) => row.remove());
184
+ appendRows(rows);
185
+ } else {
186
+ replaceClientTableRows(root, rows, { message: emptyMessage });
187
+ }
188
+ setPaginationState('idle');
189
+ if (!nextCursor) observer?.disconnect();
190
+ shouldContinue = Boolean(
191
+ nextCursor &&
192
+ sentinel &&
193
+ sentinel.getBoundingClientRect().top <= window.innerHeight + 512,
194
+ );
195
+ } catch (error) {
196
+ if (disposed) return;
197
+ console.error('[table] Failed to load client page', error);
198
+ if (append) {
199
+ skeletonRows.forEach((row) => row.remove());
200
+ root.removeAttribute('aria-busy');
201
+ root.dataset.tableLoadingState = 'ready';
202
+ setPaginationState('error');
203
+ } else {
204
+ replaceClientTableRows(root, [], {
205
+ message: errorMessage,
206
+ state: 'error',
207
+ });
208
+ setPaginationState('error');
209
+ }
210
+ } finally {
211
+ loading = false;
212
+ if (shouldContinue && !disposed) {
213
+ window.requestAnimationFrame(() => void load(true));
214
+ }
215
+ }
216
+ };
217
+
218
+ const observer =
219
+ sentinel && 'IntersectionObserver' in window
220
+ ? new IntersectionObserver(
221
+ (entries) => {
222
+ if (entries.some((entry) => entry.isIntersecting)) void load(true);
223
+ },
224
+ { rootMargin: '512px 0px' },
225
+ )
226
+ : null;
227
+
228
+ const initialize = async (): Promise<void> => {
229
+ await load(false);
230
+ if (!disposed && nextCursor && sentinel && observer) {
231
+ observer.observe(sentinel);
232
+ }
233
+ };
234
+
235
+ void initialize();
236
+
237
+ return {
238
+ destroy() {
239
+ disposed = true;
240
+ observer?.disconnect();
241
+ },
242
+ remove(key) {
243
+ if (!keys.delete(key)) return;
244
+ const row = Array.from(
245
+ root.querySelectorAll<HTMLElement>('[data-table-row]'),
246
+ ).find((candidate) => candidate.dataset.tableRowKey === key);
247
+ row?.remove();
248
+ if (keys.size === 0) {
249
+ replaceClientTableRows(root, [], { message: emptyMessage });
250
+ }
251
+ },
252
+ };
253
+ }
@@ -4,6 +4,10 @@ import {
4
4
  type ComboboxChangeDetail,
5
5
  } from '../Combobox/comboboxController';
6
6
  import { resetTableSort } from './tableSortController';
7
+ import {
8
+ hasTableProgressive,
9
+ setTableProgressiveFilters,
10
+ } from './tableProgressiveController';
7
11
 
8
12
  type FilterValues = Readonly<Record<string, readonly string[]>>;
9
13
 
@@ -104,23 +108,10 @@ export function initTableFilters(root: HTMLElement): () => void {
104
108
  }
105
109
  };
106
110
 
107
- const isLoadingAllRows = (): boolean => {
108
- const pagination = table.querySelector<HTMLElement>(
109
- '[data-table-pagination]',
110
- );
111
- const state = pagination?.dataset.tableProgressiveState;
112
- return Boolean(
113
- pagination &&
114
- !pagination.hidden &&
115
- state !== 'complete' &&
116
- state !== 'error',
117
- );
118
- };
119
-
120
111
  const updateEmptyState = (visibleRows: number, active: boolean): void => {
121
112
  const empty = table.querySelector<HTMLElement>('[data-table-filter-empty]');
122
113
  if (!empty) return;
123
- empty.hidden = !active || visibleRows > 0 || isLoadingAllRows();
114
+ empty.hidden = !active || visibleRows > 0;
124
115
  };
125
116
 
126
117
  const applyFilters = (): void => {
@@ -133,6 +124,16 @@ export function initTableFilters(root: HTMLElement): () => void {
133
124
  rows.some((row) => row.hasAttribute(exclusionsAttribute));
134
125
  const active =
135
126
  query.length > 0 || activeSelections.length > 0 || hasExternalFilter;
127
+
128
+ if (
129
+ setTableProgressiveFilters(table, {
130
+ search: search?.value.trim() ?? '',
131
+ selections: Object.fromEntries(activeSelections),
132
+ })
133
+ ) {
134
+ return;
135
+ }
136
+
136
137
  let visibleRows = 0;
137
138
 
138
139
  for (const row of rows) {
@@ -153,14 +154,17 @@ export function initTableFilters(root: HTMLElement): () => void {
153
154
  if (row.hidden !== !visible) row.hidden = !visible;
154
155
  if (visible) visibleRows += 1;
155
156
  }
156
-
157
157
  updateEmptyState(visibleRows, active);
158
- if (active && isLoadingAllRows()) {
159
- table.dispatchEvent(new CustomEvent('table:load-all', { bubbles: true }));
160
- }
161
158
  };
162
159
 
163
160
  const scheduleFilters = (): void => {
161
+ if (
162
+ table.dataset.tablePreparation === 'controlled' ||
163
+ hasTableProgressive(table)
164
+ ) {
165
+ applyFilters();
166
+ return;
167
+ }
164
168
  if (scheduledFrame !== null) return;
165
169
  scheduledFrame = window.requestAnimationFrame(() => {
166
170
  scheduledFrame = null;
@@ -192,11 +196,7 @@ export function initTableFilters(root: HTMLElement): () => void {
192
196
  const detail = (event as CustomEvent<{ rows?: readonly HTMLElement[] }>)
193
197
  .detail;
194
198
  registerRows(detail?.rows ?? []);
195
- scheduleFilters();
196
- };
197
-
198
- const handleProgressiveComplete = (): void => {
199
- scheduleFilters();
199
+ if (!hasTableProgressive(table)) scheduleFilters();
200
200
  };
201
201
 
202
202
  const handleReset = (): void => {
@@ -226,10 +226,6 @@ export function initTableFilters(root: HTMLElement): () => void {
226
226
  root.addEventListener('combobox:change', handleComboboxChange);
227
227
  root.addEventListener(refreshEvent, scheduleFilters);
228
228
  table.addEventListener('table:rows-appended', handleRowsAppended);
229
- table.addEventListener(
230
- 'table:progressive-complete',
231
- handleProgressiveComplete,
232
- );
233
229
  reset?.addEventListener('click', handleReset);
234
230
 
235
231
  return () => {
@@ -241,10 +237,6 @@ export function initTableFilters(root: HTMLElement): () => void {
241
237
  root.removeEventListener('combobox:change', handleComboboxChange);
242
238
  root.removeEventListener(refreshEvent, scheduleFilters);
243
239
  table.removeEventListener('table:rows-appended', handleRowsAppended);
244
- table.removeEventListener(
245
- 'table:progressive-complete',
246
- handleProgressiveComplete,
247
- );
248
240
  reset?.removeEventListener('click', handleReset);
249
241
  for (const row of rows) row.hidden = false;
250
242
  updateEmptyState(0, false);