@warkypublic/svelix 0.1.44 → 0.1.47
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 +93 -33
- package/dist/components/BetterMenu/BetterMenu.svelte +21 -37
- package/dist/components/BetterMenu/BetterMenuAsyncButton.svelte +21 -31
- package/dist/components/BetterMenu/BetterMenuPreview.svelte +27 -29
- package/dist/components/BetterMenu/MenuRenderer.svelte +40 -51
- package/dist/components/Boxer/Boxer.svelte +488 -602
- package/dist/components/Boxer/BoxerTarget.svelte +6 -46
- package/dist/components/Boxer/BoxerTarget.svelte.d.ts +1 -0
- package/dist/components/Boxer/types.d.ts +0 -1
- package/dist/components/Button.svelte +19 -43
- package/dist/components/ButtonPreview.svelte +2 -11
- package/dist/components/CardGrid/CardGrid.svelte +1141 -0
- package/dist/components/CardGrid/CardGrid.svelte.d.ts +61 -0
- package/dist/components/CardGrid/CardGridFilterPanel.svelte +320 -0
- package/dist/components/CardGrid/CardGridFilterPanel.svelte.d.ts +11 -0
- package/dist/components/CardGrid/DefaultCard.svelte +287 -0
- package/dist/components/CardGrid/DefaultCard.svelte.d.ts +10 -0
- package/dist/components/CardGrid/ImageCardStory.svelte +308 -0
- package/dist/components/CardGrid/ImageCardStory.svelte.d.ts +18 -0
- package/dist/components/CardGrid/cardGrid.d.ts +33 -0
- package/dist/components/CardGrid/cardGrid.js +21 -0
- package/dist/components/CardGrid/index.d.ts +4 -0
- package/dist/components/CardGrid/index.js +4 -0
- package/dist/components/ContentEditor/CollaboraInsertTextDemo.svelte +5 -10
- package/dist/components/ContentEditor/ContentEditor.svelte +24 -50
- package/dist/components/ContentEditor/subcomponents/AudioPlayer.svelte +17 -17
- package/dist/components/ContentEditor/subcomponents/CollaboraEditor.svelte +244 -309
- package/dist/components/ContentEditor/subcomponents/EmailViewer.svelte +2 -3
- package/dist/components/ContentEditor/subcomponents/ImageViewer.svelte +14 -15
- package/dist/components/ContentEditor/subcomponents/MarkdownViewer.svelte +74 -90
- package/dist/components/ContentEditor/subcomponents/MonacoEditor.svelte +75 -103
- package/dist/components/ContentEditor/subcomponents/Office365Editor.svelte +234 -297
- package/dist/components/ContentEditor/subcomponents/PdfViewer.svelte +14 -20
- package/dist/components/ContentEditor/subcomponents/TextEditor.svelte +122 -150
- package/dist/components/ContentEditor/subcomponents/UnknownFile.svelte +105 -126
- package/dist/components/ContentEditor/subcomponents/VideoPlayer.svelte +17 -17
- package/dist/components/ContentEditor/subcomponents/ZipViewer.svelte +2 -3
- package/dist/components/ErrorBoundary/ErrorBoundary.svelte +41 -58
- package/dist/components/ErrorBoundary/ErrorBoundaryPreview.svelte +8 -16
- package/dist/components/Former/Former.svelte +306 -424
- package/dist/components/Former/FormerButtonArea.svelte +17 -68
- package/dist/components/Former/FormerDrawer.svelte +26 -78
- package/dist/components/Former/FormerDrawerPreview.svelte +99 -89
- package/dist/components/Former/FormerModal.svelte +24 -70
- package/dist/components/Former/FormerModalPreview.svelte +99 -89
- package/dist/components/Former/FormerPreview.svelte +76 -78
- package/dist/components/Former/FormerRestApiPreview.svelte +55 -48
- package/dist/components/FormerControllers/ButtonCtrl.svelte +13 -44
- package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrl.svelte +280 -445
- package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlCalendar.svelte +95 -136
- package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlPickerPanel.svelte +3 -62
- package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlTimeFields.svelte +143 -217
- package/dist/components/FormerControllers/IconButtonCtrl.svelte +13 -42
- package/dist/components/FormerControllers/InlineWrapper.svelte +12 -41
- package/dist/components/FormerControllers/InlineWrapperPreview.svelte +4 -26
- package/dist/components/FormerControllers/NativeSelectCtrl.svelte +3 -29
- package/dist/components/FormerControllers/NumberInputCtrl.svelte +9 -35
- package/dist/components/FormerControllers/PasswordInputCtrl.svelte +4 -23
- package/dist/components/FormerControllers/SwitchCtrl.svelte +3 -20
- package/dist/components/FormerControllers/TextAreaCtrl.svelte +3 -24
- package/dist/components/FormerControllers/TextInputCtrl.svelte +3 -24
- package/dist/components/GlobalStateStore/GlobalStateStoreProvider.svelte +73 -120
- package/dist/components/Gridler/components/Gridler.svelte +338 -442
- package/dist/components/Gridler/components/GridlerCanvas.svelte +720 -1115
- package/dist/components/Gridler/components/GridlerEditor.svelte +2 -15
- package/dist/components/Gridler/components/GridlerFull.svelte +520 -841
- package/dist/components/Gridler/components/GridlerFull.svelte.d.ts +1 -1
- package/dist/components/Gridler/components/GridlerFullWithFormerPreview.svelte +194 -147
- package/dist/components/Gridler/components/GridlerSearch.svelte +7 -16
- package/dist/components/Gridler/components/GridlerSearchToggle.svelte +7 -19
- package/dist/components/Gridler/types.d.ts +1 -1
- package/dist/components/Icons/IconAdd.svelte +1 -3
- package/dist/components/Icons/IconAlertCircle.svelte +1 -3
- package/dist/components/Icons/IconAlertTriangle.svelte +1 -3
- package/dist/components/Icons/IconCamera.svelte +1 -3
- package/dist/components/Icons/IconClose.svelte +1 -3
- package/dist/components/Icons/IconEdit.svelte +1 -3
- package/dist/components/Icons/IconFilter.svelte +1 -3
- package/dist/components/Icons/IconSearch.svelte +1 -3
- package/dist/components/Icons/IconSort.svelte +1 -3
- package/dist/components/Icons/IconTrash.svelte +1 -3
- package/dist/components/Portal/Portal.svelte +14 -38
- package/dist/components/Screenshot/Screenshot.svelte +7 -13
- package/dist/components/Svark/Svark.svelte +680 -852
- package/dist/components/Svark/SvarkContextMenu.svelte +44 -63
- package/dist/components/Svark/SvarkInfiniteLoader.svelte +2 -6
- package/dist/components/Svark/SvarkPager.svelte +2 -28
- package/dist/components/Svark/SvarkSelectionDemo.svelte +4 -8
- package/dist/components/Svark/SvarkTopBar.svelte +9 -74
- package/dist/components/SvarkGrid/SvarkGrid.svelte +877 -1123
- package/dist/components/SvarkGrid/components/SvarkColumnFilterForm.svelte +45 -58
- package/dist/components/SvarkGrid/components/SvarkContextMenu.svelte +44 -63
- package/dist/components/SvarkGrid/components/SvarkHeaderFilterCell.svelte +41 -75
- package/dist/components/SvarkGrid/components/SvarkInfiniteLoader.svelte +2 -6
- package/dist/components/SvarkGrid/components/SvarkPager.svelte +2 -28
- package/dist/components/SvarkGrid/components/SvarkSearchPopover.svelte +8 -16
- package/dist/components/SvarkGrid/components/SvarkTopBar.svelte +6 -62
- package/dist/components/SvarkGrid/internal/SvarkGridView.svelte +345 -507
- package/dist/components/VTree/VTree.svelte +349 -431
- package/dist/components/VTree/VTreeContextMenu.svelte +21 -33
- package/dist/components/VTree/VTreeEventsDemo.svelte +48 -58
- package/dist/components/VTree/VTreeRow.svelte +13 -65
- package/dist/components/VTree/VTreeSearch.svelte +6 -13
- package/dist/components/VTree/VTreeVirtualViewport.svelte +37 -106
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/llm/COMPONENT_GUIDE.md +240 -0
- package/llm/plans/card_grid.plan.md +305 -0
- package/package.json +48 -46
|
@@ -1,549 +1,387 @@
|
|
|
1
|
-
<script lang="ts"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export interface Props {
|
|
22
|
-
// sizing
|
|
23
|
-
heightStyle: string;
|
|
24
|
-
widthStyle: string;
|
|
25
|
-
menuProviderID: string;
|
|
26
|
-
|
|
27
|
-
// grid render
|
|
28
|
-
hideHeader: boolean;
|
|
29
|
-
columns: IColumnConfig[];
|
|
30
|
-
data: Array<Record<string, unknown>>;
|
|
31
|
-
loading: boolean;
|
|
32
|
-
error: string | null;
|
|
33
|
-
select: boolean;
|
|
34
|
-
multiselect: boolean;
|
|
35
|
-
gridDynamic?: any;
|
|
36
|
-
gridConfig: Record<string, unknown>;
|
|
37
|
-
onInit: (api: IApi) => void;
|
|
38
|
-
|
|
39
|
-
// top bar
|
|
40
|
-
topBarLeft?: Snippet;
|
|
41
|
-
topBarRight?: Snippet;
|
|
42
|
-
searchOpen: boolean;
|
|
43
|
-
searchValue: string;
|
|
44
|
-
onToggleSearch: () => void;
|
|
45
|
-
onSearchChange: (value: string) => void;
|
|
46
|
-
|
|
47
|
-
// infinite scroll
|
|
48
|
-
infiniteScroll: boolean;
|
|
49
|
-
infiniteLoading: boolean;
|
|
50
|
-
total: number;
|
|
51
|
-
onLoadNextPage: () => void;
|
|
52
|
-
|
|
53
|
-
// pager
|
|
54
|
-
showPager: boolean;
|
|
55
|
-
page: number;
|
|
56
|
-
totalPages: number;
|
|
57
|
-
pageStart: number;
|
|
58
|
-
pageEnd: number;
|
|
59
|
-
onPageFirst: () => void;
|
|
60
|
-
onPagePrev: () => void;
|
|
61
|
-
onPageNext: () => void;
|
|
62
|
-
onPageLast: () => void;
|
|
63
|
-
|
|
64
|
-
// row + context menu
|
|
65
|
-
gridApi: IApi | null;
|
|
66
|
-
uniqueID: string;
|
|
67
|
-
genericColumnsById: Record<string, GridColumn<Record<string, unknown>, unknown>>;
|
|
68
|
-
selectedIds: Array<string | number>;
|
|
69
|
-
onSelectedIdsChange: (ids: Array<string | number>) => void;
|
|
70
|
-
onRowClick?: (ctx: { id: string | number; row?: any; event: MouseEvent }) => void;
|
|
71
|
-
onRowDblClick?: (ctx: { id: string | number; row?: any; event: MouseEvent }) => void;
|
|
72
|
-
onRowContextMenu?: (ctx: { id: string | number; row?: any; event: MouseEvent }) => void;
|
|
73
|
-
contextMenuProps?: SvarkContextMenuProps;
|
|
74
|
-
onGridEvent?: GridCommonProps<Record<string, unknown>, unknown>['onGridEvent'];
|
|
75
|
-
onCellEvent?: GridCommonProps<Record<string, unknown>, unknown>['onCellEvent'];
|
|
76
|
-
onMenuClick?: GridCommonProps<Record<string, unknown>, unknown>['onMenuClick'];
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const {
|
|
80
|
-
heightStyle,
|
|
81
|
-
widthStyle,
|
|
82
|
-
menuProviderID,
|
|
83
|
-
hideHeader,
|
|
84
|
-
columns,
|
|
85
|
-
data,
|
|
86
|
-
loading,
|
|
87
|
-
error,
|
|
88
|
-
select,
|
|
89
|
-
multiselect,
|
|
90
|
-
gridDynamic,
|
|
91
|
-
gridConfig,
|
|
92
|
-
onInit,
|
|
93
|
-
// top bar
|
|
94
|
-
topBarLeft,
|
|
95
|
-
topBarRight,
|
|
96
|
-
searchOpen,
|
|
97
|
-
searchValue,
|
|
98
|
-
onToggleSearch,
|
|
99
|
-
onSearchChange,
|
|
100
|
-
// infinite scroll
|
|
101
|
-
infiniteScroll,
|
|
102
|
-
infiniteLoading,
|
|
103
|
-
total,
|
|
104
|
-
onLoadNextPage,
|
|
105
|
-
// pager
|
|
106
|
-
showPager,
|
|
107
|
-
page,
|
|
108
|
-
totalPages,
|
|
109
|
-
pageStart,
|
|
110
|
-
pageEnd,
|
|
111
|
-
onPageFirst,
|
|
112
|
-
onPagePrev,
|
|
113
|
-
onPageNext,
|
|
114
|
-
onPageLast,
|
|
115
|
-
// row + context menu
|
|
116
|
-
gridApi,
|
|
117
|
-
uniqueID,
|
|
118
|
-
genericColumnsById,
|
|
119
|
-
selectedIds,
|
|
120
|
-
onSelectedIdsChange,
|
|
121
|
-
onRowClick,
|
|
122
|
-
onRowDblClick,
|
|
123
|
-
onRowContextMenu,
|
|
124
|
-
contextMenuProps,
|
|
125
|
-
onGridEvent,
|
|
126
|
-
onCellEvent,
|
|
127
|
-
onMenuClick
|
|
128
|
-
}: Props = $props();
|
|
129
|
-
|
|
130
|
-
// Dark mode detection — priority: inline color-scheme style > .dark class > OS media query
|
|
131
|
-
let isDark = $state(false);
|
|
132
|
-
|
|
133
|
-
$effect(() => {
|
|
134
|
-
const mq = window.matchMedia('(prefers-color-scheme: dark)');
|
|
135
|
-
const html = document.documentElement;
|
|
136
|
-
|
|
137
|
-
function update() {
|
|
138
|
-
const explicit = html.style.colorScheme;
|
|
139
|
-
if (explicit === 'dark' || explicit === 'light') {
|
|
140
|
-
isDark = explicit === 'dark';
|
|
141
|
-
} else {
|
|
142
|
-
isDark = html.classList.contains('dark') || mq.matches;
|
|
143
|
-
}
|
|
1
|
+
<script lang="ts">/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { Grid, Willow, WillowDark } from "@svar-ui/svelte-grid";
|
|
3
|
+
import BetterMenu from "../../BetterMenu/BetterMenu.svelte";
|
|
4
|
+
import SvarkContextMenu from "../components/SvarkContextMenu.svelte";
|
|
5
|
+
import SvarkInfiniteLoader from "../components/SvarkInfiniteLoader.svelte";
|
|
6
|
+
import SvarkPager from "../components/SvarkPager.svelte";
|
|
7
|
+
import SvarkSearchPopover from "../components/SvarkSearchPopover.svelte";
|
|
8
|
+
import SvarkTopBar from "../components/SvarkTopBar.svelte";
|
|
9
|
+
const { heightStyle, widthStyle, menuProviderID, hideHeader, columns, data, loading, error, select, multiselect, gridDynamic, gridConfig, onInit, topBarLeft, topBarRight, searchOpen, searchValue, onToggleSearch, onSearchChange, infiniteScroll, infiniteLoading, total, onLoadNextPage, showPager, page, totalPages, pageStart, pageEnd, onPageFirst, onPagePrev, onPageNext, onPageLast, gridApi, uniqueID, genericColumnsById, selectedIds, onSelectedIdsChange, onRowClick, onRowDblClick, onRowContextMenu, contextMenuProps, onGridEvent, onCellEvent, onMenuClick } = $props();
|
|
10
|
+
// Dark mode detection — priority: inline color-scheme style > .dark class > OS media query
|
|
11
|
+
let isDark = $state(false);
|
|
12
|
+
$effect(() => {
|
|
13
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
14
|
+
const html = document.documentElement;
|
|
15
|
+
function update() {
|
|
16
|
+
const explicit = html.style.colorScheme;
|
|
17
|
+
if (explicit === "dark" || explicit === "light") {
|
|
18
|
+
isDark = explicit === "dark";
|
|
19
|
+
} else {
|
|
20
|
+
isDark = html.classList.contains("dark") || mq.matches;
|
|
144
21
|
}
|
|
145
|
-
|
|
146
|
-
update();
|
|
147
|
-
mq.addEventListener('change', update);
|
|
148
|
-
|
|
149
|
-
const observer = new MutationObserver(update);
|
|
150
|
-
observer.observe(html, { attributeFilter: ['class', 'style'] });
|
|
151
|
-
|
|
152
|
-
return () => {
|
|
153
|
-
mq.removeEventListener('change', update);
|
|
154
|
-
observer.disconnect();
|
|
155
|
-
};
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
let containerRef = $state<HTMLElement | undefined>();
|
|
159
|
-
let scrollContainerRef = $state<HTMLElement | null>(null);
|
|
160
|
-
let gridWrapperRef = $state<HTMLElement | null>(null);
|
|
161
|
-
|
|
162
|
-
type BetterMenuRef = {
|
|
163
|
-
show: (id: string, options?: Partial<Omit<BetterMenuInstance, 'id'>>) => void;
|
|
164
|
-
set: (id: string, options?: Partial<Omit<BetterMenuInstance, 'id'>>) => void;
|
|
165
|
-
hide: (id: string) => void;
|
|
166
|
-
};
|
|
167
|
-
let betterMenuRef = $state<BetterMenuRef | undefined>();
|
|
168
|
-
|
|
169
|
-
const searchMenuId = $derived(`${menuProviderID}:search`);
|
|
170
|
-
let searchMenuWasOpen = $state(false);
|
|
171
|
-
|
|
172
|
-
function openSearchMenu() {
|
|
173
|
-
if (!betterMenuRef) return;
|
|
174
|
-
const rect =
|
|
175
|
-
gridWrapperRef?.getBoundingClientRect() ?? containerRef?.getBoundingClientRect();
|
|
176
|
-
if (!rect) return;
|
|
177
|
-
betterMenuRef.set(searchMenuId, {
|
|
178
|
-
x: rect.left + 10,
|
|
179
|
-
y: rect.top + 10,
|
|
180
|
-
renderer: SearchMenu as any,
|
|
181
|
-
onHide: () => {
|
|
182
|
-
if (searchOpen) onToggleSearch();
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
22
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
23
|
+
update();
|
|
24
|
+
mq.addEventListener("change", update);
|
|
25
|
+
const observer = new MutationObserver(update);
|
|
26
|
+
observer.observe(html, { attributeFilter: ["class", "style"] });
|
|
27
|
+
return () => {
|
|
28
|
+
mq.removeEventListener("change", update);
|
|
29
|
+
observer.disconnect();
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
let containerRef = $state();
|
|
33
|
+
let scrollContainerRef = $state(null);
|
|
34
|
+
let gridWrapperRef = $state(null);
|
|
35
|
+
let betterMenuRef = $state();
|
|
36
|
+
const searchMenuId = $derived(`${menuProviderID}:search`);
|
|
37
|
+
let searchMenuWasOpen = $state(false);
|
|
38
|
+
function openSearchMenu() {
|
|
39
|
+
if (!betterMenuRef) return;
|
|
40
|
+
const rect = gridWrapperRef?.getBoundingClientRect() ?? containerRef?.getBoundingClientRect();
|
|
41
|
+
if (!rect) return;
|
|
42
|
+
betterMenuRef.set(searchMenuId, {
|
|
43
|
+
x: rect.left + 10,
|
|
44
|
+
y: rect.top + 10,
|
|
45
|
+
renderer: SearchMenu,
|
|
46
|
+
onHide: () => {
|
|
47
|
+
if (searchOpen) onToggleSearch();
|
|
199
48
|
}
|
|
200
49
|
});
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
betterMenuRef.set(searchMenuId, { renderer: SearchMenu as any });
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
// ── Row resolution (SVAR renders rows with data-id) ───────────────────────
|
|
212
|
-
|
|
213
|
-
function resolveRowFromTarget(target: EventTarget | null) {
|
|
214
|
-
if (!containerRef || !(target instanceof Element)) return undefined;
|
|
215
|
-
const rowEl = target.closest('[data-id]') as HTMLElement | null;
|
|
216
|
-
if (!rowEl || !containerRef.contains(rowEl)) return undefined;
|
|
217
|
-
|
|
218
|
-
const attrId = rowEl.getAttribute('data-id');
|
|
219
|
-
if (!attrId) return undefined;
|
|
220
|
-
|
|
221
|
-
const row = data.find((item) => String(item?.[uniqueID]) === attrId);
|
|
222
|
-
const idValue = row?.[uniqueID];
|
|
223
|
-
const resolvedId =
|
|
224
|
-
typeof idValue === 'string' || typeof idValue === 'number' ? idValue : attrId;
|
|
225
|
-
|
|
226
|
-
return { id: resolvedId, row };
|
|
50
|
+
}
|
|
51
|
+
$effect(() => {
|
|
52
|
+
if (!betterMenuRef) return;
|
|
53
|
+
if (searchOpen && !searchMenuWasOpen) {
|
|
54
|
+
searchMenuWasOpen = true;
|
|
55
|
+
queueMicrotask(() => openSearchMenu());
|
|
56
|
+
return;
|
|
227
57
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
const cellEl = target.closest('[data-col-id], [data-header-id]') as HTMLElement | null;
|
|
232
|
-
if (!cellEl || !containerRef.contains(cellEl)) return undefined;
|
|
233
|
-
|
|
234
|
-
const colId = cellEl.getAttribute('data-col-id') ?? cellEl.getAttribute('data-header-id');
|
|
235
|
-
return colId ? String(colId) : undefined;
|
|
58
|
+
if (!searchOpen && searchMenuWasOpen) {
|
|
59
|
+
searchMenuWasOpen = false;
|
|
60
|
+
betterMenuRef.hide(searchMenuId);
|
|
236
61
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
62
|
+
});
|
|
63
|
+
// While the search popover is open, keep it in sync with the grid search value.
|
|
64
|
+
// (Context-menu "Clear Search" updates the grid state but would not rerender the popover otherwise.)
|
|
65
|
+
$effect(() => {
|
|
66
|
+
if (!betterMenuRef) return;
|
|
67
|
+
if (!searchMenuWasOpen) return;
|
|
68
|
+
void searchValue;
|
|
69
|
+
betterMenuRef.set(searchMenuId, { renderer: SearchMenu });
|
|
70
|
+
});
|
|
71
|
+
// ── Row resolution (SVAR renders rows with data-id) ───────────────────────
|
|
72
|
+
function resolveRowFromTarget(target) {
|
|
73
|
+
if (!containerRef || !(target instanceof Element)) return undefined;
|
|
74
|
+
const rowEl = target.closest("[data-id]");
|
|
75
|
+
if (!rowEl || !containerRef.contains(rowEl)) return undefined;
|
|
76
|
+
const attrId = rowEl.getAttribute("data-id");
|
|
77
|
+
if (!attrId) return undefined;
|
|
78
|
+
const row = data.find((item) => String(item?.[uniqueID]) === attrId);
|
|
79
|
+
const idValue = row?.[uniqueID];
|
|
80
|
+
const resolvedId = typeof idValue === "string" || typeof idValue === "number" ? idValue : attrId;
|
|
81
|
+
return {
|
|
82
|
+
id: resolvedId,
|
|
83
|
+
row
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function resolveColumnIdFromTarget(target) {
|
|
87
|
+
if (!containerRef || !(target instanceof Element)) return undefined;
|
|
88
|
+
const cellEl = target.closest("[data-col-id], [data-header-id]");
|
|
89
|
+
if (!cellEl || !containerRef.contains(cellEl)) return undefined;
|
|
90
|
+
const colId = cellEl.getAttribute("data-col-id") ?? cellEl.getAttribute("data-header-id");
|
|
91
|
+
return colId ? String(colId) : undefined;
|
|
92
|
+
}
|
|
93
|
+
function resolveGenericColumn(target) {
|
|
94
|
+
const columnId = resolveColumnIdFromTarget(target);
|
|
95
|
+
return columnId ? genericColumnsById[columnId] : undefined;
|
|
96
|
+
}
|
|
97
|
+
// ── Context menu ──────────────────────────────────────────────────────────
|
|
98
|
+
let contextMenuOpen = $state(false);
|
|
99
|
+
let contextMenuX = $state(0);
|
|
100
|
+
let contextMenuY = $state(0);
|
|
101
|
+
let contextMenuEvent = $state(null);
|
|
102
|
+
let contextMenuRowId = $state(null);
|
|
103
|
+
let contextMenuRow = $state(null);
|
|
104
|
+
function buildContextMenuCtx() {
|
|
105
|
+
if (!contextMenuEvent) return null;
|
|
106
|
+
return {
|
|
107
|
+
api: gridApi ?? undefined,
|
|
108
|
+
row: contextMenuRow,
|
|
109
|
+
rowId: contextMenuRowId,
|
|
110
|
+
selectedIds,
|
|
111
|
+
data,
|
|
112
|
+
event: contextMenuEvent
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const contextMenuItems = $derived.by(() => {
|
|
116
|
+
if (!contextMenuOpen) return [];
|
|
117
|
+
if (!contextMenuProps) return [];
|
|
118
|
+
const ctx = buildContextMenuCtx();
|
|
119
|
+
if (!ctx) return [];
|
|
120
|
+
const resolved = typeof contextMenuProps.items === "function" ? contextMenuProps.items(ctx) : contextMenuProps.items ?? [];
|
|
121
|
+
return (resolved ?? []).map((it) => ({
|
|
122
|
+
...it,
|
|
123
|
+
kind: it.kind ?? "item"
|
|
124
|
+
}));
|
|
125
|
+
});
|
|
126
|
+
function closeContextMenu() {
|
|
127
|
+
contextMenuOpen = false;
|
|
128
|
+
contextMenuEvent = null;
|
|
129
|
+
contextMenuRowId = null;
|
|
130
|
+
contextMenuRow = null;
|
|
131
|
+
}
|
|
132
|
+
function handleContextMenuItem(item) {
|
|
133
|
+
if (item.id === "__close__") {
|
|
134
|
+
closeContextMenu();
|
|
135
|
+
return;
|
|
241
136
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
event: contextMenuEvent
|
|
137
|
+
const ctx = buildContextMenuCtx();
|
|
138
|
+
closeContextMenu();
|
|
139
|
+
if (!ctx) return;
|
|
140
|
+
const menuItem = item;
|
|
141
|
+
menuItem.onselect?.(ctx);
|
|
142
|
+
contextMenuProps?.onselectitem?.(menuItem, ctx);
|
|
143
|
+
}
|
|
144
|
+
// ── Events: click/dblclick/contextmenu + keydown shortcuts ────────────────
|
|
145
|
+
$effect(() => {
|
|
146
|
+
if (!containerRef) return;
|
|
147
|
+
const node = containerRef;
|
|
148
|
+
function handleClick(ev) {
|
|
149
|
+
const resolved = resolveRowFromTarget(ev.target);
|
|
150
|
+
const column = resolveGenericColumn(ev.target);
|
|
151
|
+
const coords = {
|
|
152
|
+
x: ev.clientX,
|
|
153
|
+
y: ev.clientY,
|
|
154
|
+
code: String(ev.button ?? 0)
|
|
261
155
|
};
|
|
156
|
+
onGridEvent?.("click", resolved?.row, column, coords, {
|
|
157
|
+
rowId: resolved?.id,
|
|
158
|
+
event: ev
|
|
159
|
+
});
|
|
160
|
+
if (resolved?.row && column) {
|
|
161
|
+
onCellEvent?.("click", resolved.row, column, coords, {
|
|
162
|
+
rowId: resolved.id,
|
|
163
|
+
event: ev
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
if (!resolved) return;
|
|
167
|
+
onRowClick?.({
|
|
168
|
+
...resolved,
|
|
169
|
+
event: ev
|
|
170
|
+
});
|
|
262
171
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
172
|
+
function handleDoubleClick(ev) {
|
|
173
|
+
const resolved = resolveRowFromTarget(ev.target);
|
|
174
|
+
const column = resolveGenericColumn(ev.target);
|
|
175
|
+
const coords = {
|
|
176
|
+
x: ev.clientX,
|
|
177
|
+
y: ev.clientY,
|
|
178
|
+
code: String(ev.button ?? 0)
|
|
179
|
+
};
|
|
180
|
+
onGridEvent?.("dblclick", resolved?.row, column, coords, {
|
|
181
|
+
rowId: resolved?.id,
|
|
182
|
+
event: ev
|
|
183
|
+
});
|
|
184
|
+
if (resolved?.row && column) {
|
|
185
|
+
onCellEvent?.("dblclick", resolved.row, column, coords, {
|
|
186
|
+
rowId: resolved.id,
|
|
187
|
+
event: ev
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
if (!resolved) return;
|
|
191
|
+
onRowDblClick?.({
|
|
192
|
+
...resolved,
|
|
193
|
+
event: ev
|
|
194
|
+
});
|
|
281
195
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
if (
|
|
285
|
-
closeContextMenu();
|
|
196
|
+
function handleContextMenu(ev) {
|
|
197
|
+
// Only handle right-clicks on the grid surface itself, not on the toolbar/pager.
|
|
198
|
+
if (!containerRef || !(ev.target instanceof Element) || !ev.target.closest(".wx-grid") || !containerRef.contains(ev.target.closest(".wx-grid"))) {
|
|
286
199
|
return;
|
|
287
200
|
}
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if (
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
function handleClick(ev: MouseEvent) {
|
|
303
|
-
const resolved = resolveRowFromTarget(ev.target);
|
|
304
|
-
const column = resolveGenericColumn(ev.target);
|
|
305
|
-
const coords = { x: ev.clientX, y: ev.clientY, code: String(ev.button ?? 0) };
|
|
306
|
-
onGridEvent?.('click', resolved?.row, column, coords, {
|
|
307
|
-
rowId: resolved?.id,
|
|
201
|
+
const resolved = resolveRowFromTarget(ev.target);
|
|
202
|
+
const column = resolveGenericColumn(ev.target);
|
|
203
|
+
const coords = {
|
|
204
|
+
x: ev.clientX,
|
|
205
|
+
y: ev.clientY,
|
|
206
|
+
code: String(ev.button ?? 2)
|
|
207
|
+
};
|
|
208
|
+
onGridEvent?.("contextmenu", resolved?.row, column, coords, {
|
|
209
|
+
rowId: resolved?.id,
|
|
210
|
+
event: ev
|
|
211
|
+
});
|
|
212
|
+
if (resolved?.row && column) {
|
|
213
|
+
onCellEvent?.("contextmenu", resolved.row, column, coords, {
|
|
214
|
+
rowId: resolved.id,
|
|
308
215
|
event: ev
|
|
309
216
|
});
|
|
310
|
-
|
|
311
|
-
|
|
217
|
+
}
|
|
218
|
+
void onMenuClick?.(resolved?.row, column, coords);
|
|
219
|
+
if (resolved) onRowContextMenu?.({
|
|
220
|
+
...resolved,
|
|
221
|
+
event: ev
|
|
222
|
+
});
|
|
223
|
+
ev.preventDefault();
|
|
224
|
+
ev.stopPropagation();
|
|
225
|
+
// Right-clicking a row should behave like "select + open menu".
|
|
226
|
+
if (resolved?.id != null && gridApi && select !== false) {
|
|
227
|
+
gridApi.exec("select-row", {
|
|
228
|
+
id: resolved.id,
|
|
229
|
+
toggle: false,
|
|
230
|
+
range: false
|
|
231
|
+
});
|
|
232
|
+
onSelectedIdsChange([resolved.id]);
|
|
233
|
+
}
|
|
234
|
+
contextMenuEvent = ev;
|
|
235
|
+
contextMenuX = ev.clientX;
|
|
236
|
+
contextMenuY = ev.clientY;
|
|
237
|
+
const ctxRowId = resolved?.id ?? selectedIds[0] ?? null;
|
|
238
|
+
const ctxRow = ctxRowId != null ? data.find((item) => String(item?.[uniqueID]) === String(ctxRowId)) ?? null : null;
|
|
239
|
+
contextMenuRowId = ctxRowId;
|
|
240
|
+
contextMenuRow = ctxRow;
|
|
241
|
+
contextMenuOpen = true;
|
|
242
|
+
}
|
|
243
|
+
function onKeyDown(ev) {
|
|
244
|
+
const resolved = resolveRowFromTarget(ev.target);
|
|
245
|
+
const column = resolveGenericColumn(ev.target);
|
|
246
|
+
const coords = {
|
|
247
|
+
x: 0,
|
|
248
|
+
y: 0,
|
|
249
|
+
code: ev.code || ev.key
|
|
250
|
+
};
|
|
251
|
+
onGridEvent?.("keydown", resolved?.row, column, coords, {
|
|
252
|
+
rowId: resolved?.id,
|
|
253
|
+
event: ev
|
|
254
|
+
});
|
|
255
|
+
if (resolved?.row && column) {
|
|
256
|
+
if (ev.key === "Enter") {
|
|
257
|
+
onCellEvent?.("enter_key", resolved.row, column, coords, {
|
|
312
258
|
rowId: resolved.id,
|
|
313
259
|
event: ev
|
|
314
260
|
});
|
|
315
261
|
}
|
|
316
|
-
if (
|
|
317
|
-
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
function handleDoubleClick(ev: MouseEvent) {
|
|
321
|
-
const resolved = resolveRowFromTarget(ev.target);
|
|
322
|
-
const column = resolveGenericColumn(ev.target);
|
|
323
|
-
const coords = { x: ev.clientX, y: ev.clientY, code: String(ev.button ?? 0) };
|
|
324
|
-
onGridEvent?.('dblclick', resolved?.row, column, coords, {
|
|
325
|
-
rowId: resolved?.id,
|
|
326
|
-
event: ev
|
|
327
|
-
});
|
|
328
|
-
if (resolved?.row && column) {
|
|
329
|
-
onCellEvent?.('dblclick', resolved.row, column, coords, {
|
|
262
|
+
if (ev.key === "Delete" || ev.key === "Backspace") {
|
|
263
|
+
onCellEvent?.("delete_key", resolved.row, column, coords, {
|
|
330
264
|
rowId: resolved.id,
|
|
331
265
|
event: ev
|
|
332
266
|
});
|
|
333
267
|
}
|
|
334
|
-
if (
|
|
335
|
-
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
function handleContextMenu(ev: MouseEvent) {
|
|
339
|
-
// Only handle right-clicks on the grid surface itself, not on the toolbar/pager.
|
|
340
|
-
if (
|
|
341
|
-
!containerRef ||
|
|
342
|
-
!(ev.target instanceof Element) ||
|
|
343
|
-
!ev.target.closest('.wx-grid') ||
|
|
344
|
-
!containerRef.contains(ev.target.closest('.wx-grid'))
|
|
345
|
-
) {
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
const resolved = resolveRowFromTarget(ev.target);
|
|
350
|
-
const column = resolveGenericColumn(ev.target);
|
|
351
|
-
const coords = { x: ev.clientX, y: ev.clientY, code: String(ev.button ?? 2) };
|
|
352
|
-
onGridEvent?.('contextmenu', resolved?.row, column, coords, {
|
|
353
|
-
rowId: resolved?.id,
|
|
354
|
-
event: ev
|
|
355
|
-
});
|
|
356
|
-
if (resolved?.row && column) {
|
|
357
|
-
onCellEvent?.('contextmenu', resolved.row, column, coords, {
|
|
268
|
+
if (ev.key === "Tab") {
|
|
269
|
+
onCellEvent?.("tab_key", resolved.row, column, coords, {
|
|
358
270
|
rowId: resolved.id,
|
|
359
271
|
event: ev
|
|
360
272
|
});
|
|
361
273
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
274
|
+
}
|
|
275
|
+
const key = ev.key?.toLowerCase?.() ?? "";
|
|
276
|
+
const isFind = (ev.ctrlKey || ev.metaKey) && !ev.altKey && !ev.shiftKey && key === "f";
|
|
277
|
+
if (isFind) {
|
|
365
278
|
ev.preventDefault();
|
|
366
279
|
ev.stopPropagation();
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
if (resolved?.id != null && gridApi && select !== false) {
|
|
370
|
-
gridApi.exec('select-row', { id: resolved.id, toggle: false, range: false });
|
|
371
|
-
onSelectedIdsChange([resolved.id]);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
contextMenuEvent = ev;
|
|
375
|
-
contextMenuX = ev.clientX;
|
|
376
|
-
contextMenuY = ev.clientY;
|
|
377
|
-
|
|
378
|
-
const ctxRowId = (resolved?.id ?? selectedIds[0] ?? null) as string | number | null;
|
|
379
|
-
const ctxRow: Record<string, unknown> | null =
|
|
380
|
-
ctxRowId != null
|
|
381
|
-
? data.find((item) => String(item?.[uniqueID]) === String(ctxRowId)) ?? null
|
|
382
|
-
: null;
|
|
383
|
-
|
|
384
|
-
contextMenuRowId = ctxRowId;
|
|
385
|
-
contextMenuRow = ctxRow;
|
|
386
|
-
contextMenuOpen = true;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
function onKeyDown(ev: KeyboardEvent) {
|
|
390
|
-
const resolved = resolveRowFromTarget(ev.target);
|
|
391
|
-
const column = resolveGenericColumn(ev.target);
|
|
392
|
-
const coords = { x: 0, y: 0, code: ev.code || ev.key };
|
|
393
|
-
onGridEvent?.('keydown', resolved?.row, column, coords, {
|
|
394
|
-
rowId: resolved?.id,
|
|
395
|
-
event: ev
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
if (resolved?.row && column) {
|
|
399
|
-
if (ev.key === 'Enter') {
|
|
400
|
-
onCellEvent?.('enter_key', resolved.row, column, coords, { rowId: resolved.id, event: ev });
|
|
401
|
-
}
|
|
402
|
-
if (ev.key === 'Delete' || ev.key === 'Backspace') {
|
|
403
|
-
onCellEvent?.('delete_key', resolved.row, column, coords, { rowId: resolved.id, event: ev });
|
|
404
|
-
}
|
|
405
|
-
if (ev.key === 'Tab') {
|
|
406
|
-
onCellEvent?.('tab_key', resolved.row, column, coords, { rowId: resolved.id, event: ev });
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
const key = ev.key?.toLowerCase?.() ?? '';
|
|
411
|
-
const isFind = (ev.ctrlKey || ev.metaKey) && !ev.altKey && !ev.shiftKey && key === 'f';
|
|
412
|
-
if (isFind) {
|
|
413
|
-
ev.preventDefault();
|
|
414
|
-
ev.stopPropagation();
|
|
415
|
-
onToggleSearch();
|
|
416
|
-
return;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
if (searchOpen && ev.key === 'Escape') {
|
|
420
|
-
ev.preventDefault();
|
|
421
|
-
ev.stopPropagation();
|
|
422
|
-
onToggleSearch();
|
|
423
|
-
return;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
if (contextMenuOpen && ev.key === 'Escape') {
|
|
427
|
-
ev.preventDefault();
|
|
428
|
-
ev.stopPropagation();
|
|
429
|
-
closeContextMenu();
|
|
430
|
-
}
|
|
280
|
+
onToggleSearch();
|
|
281
|
+
return;
|
|
431
282
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
node.addEventListener('keydown', onKeyDown, true);
|
|
437
|
-
|
|
438
|
-
return () => {
|
|
439
|
-
node.removeEventListener('click', handleClick);
|
|
440
|
-
node.removeEventListener('dblclick', handleDoubleClick);
|
|
441
|
-
node.removeEventListener('contextmenu', handleContextMenu);
|
|
442
|
-
node.removeEventListener('keydown', onKeyDown, true);
|
|
443
|
-
};
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
// ── Infinite scroll watchers (calls onLoadNextPage) ───────────────────────
|
|
447
|
-
|
|
448
|
-
function maybeLoadNextPage() {
|
|
449
|
-
if (!infiniteScroll || !containerRef || loading || infiniteLoading || data.length >= total) return;
|
|
450
|
-
|
|
451
|
-
const scrollEl = scrollContainerRef;
|
|
452
|
-
if (scrollEl && scrollEl.scrollHeight > scrollEl.clientHeight + 1) {
|
|
453
|
-
const { scrollTop, scrollHeight, clientHeight } = scrollEl;
|
|
454
|
-
if (scrollHeight - scrollTop - clientHeight < 150) {
|
|
455
|
-
onLoadNextPage();
|
|
456
|
-
}
|
|
283
|
+
if (searchOpen && ev.key === "Escape") {
|
|
284
|
+
ev.preventDefault();
|
|
285
|
+
ev.stopPropagation();
|
|
286
|
+
onToggleSearch();
|
|
457
287
|
return;
|
|
458
288
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
289
|
+
if (contextMenuOpen && ev.key === "Escape") {
|
|
290
|
+
ev.preventDefault();
|
|
291
|
+
ev.stopPropagation();
|
|
292
|
+
closeContextMenu();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
node.addEventListener("click", handleClick);
|
|
296
|
+
node.addEventListener("dblclick", handleDoubleClick);
|
|
297
|
+
node.addEventListener("contextmenu", handleContextMenu);
|
|
298
|
+
node.addEventListener("keydown", onKeyDown, true);
|
|
299
|
+
return () => {
|
|
300
|
+
node.removeEventListener("click", handleClick);
|
|
301
|
+
node.removeEventListener("dblclick", handleDoubleClick);
|
|
302
|
+
node.removeEventListener("contextmenu", handleContextMenu);
|
|
303
|
+
node.removeEventListener("keydown", onKeyDown, true);
|
|
304
|
+
};
|
|
305
|
+
});
|
|
306
|
+
// ── Infinite scroll watchers (calls onLoadNextPage) ───────────────────────
|
|
307
|
+
function maybeLoadNextPage() {
|
|
308
|
+
if (!infiniteScroll || !containerRef || loading || infiniteLoading || data.length >= total) return;
|
|
309
|
+
const scrollEl = scrollContainerRef;
|
|
310
|
+
if (scrollEl && scrollEl.scrollHeight > scrollEl.clientHeight + 1) {
|
|
311
|
+
const { scrollTop, scrollHeight, clientHeight } = scrollEl;
|
|
312
|
+
if (scrollHeight - scrollTop - clientHeight < 150) {
|
|
462
313
|
onLoadNextPage();
|
|
463
314
|
}
|
|
315
|
+
return;
|
|
464
316
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
317
|
+
const { bottom } = containerRef.getBoundingClientRect();
|
|
318
|
+
if (bottom - window.innerHeight < 150) {
|
|
319
|
+
onLoadNextPage();
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
$effect(() => {
|
|
323
|
+
if (!infiniteScroll || loading || infiniteLoading) return;
|
|
324
|
+
void data.length;
|
|
325
|
+
void total;
|
|
326
|
+
queueMicrotask(() => {
|
|
327
|
+
maybeLoadNextPage();
|
|
474
328
|
});
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
y: containerRef?.clientHeight ?? 0
|
|
510
|
-
},
|
|
511
|
-
undefined
|
|
512
|
-
);
|
|
513
|
-
maybeLoadNextPage();
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
window.addEventListener('scroll', onViewportChange, true);
|
|
517
|
-
window.addEventListener('resize', onViewportChange);
|
|
518
|
-
|
|
519
|
-
const candidate = containerRef.querySelector('.wx-scroll');
|
|
520
|
-
if (candidate) {
|
|
521
|
-
attach(candidate);
|
|
522
|
-
} else {
|
|
523
|
-
const obs = new MutationObserver(() => {
|
|
524
|
-
const el = containerRef?.querySelector('.wx-scroll');
|
|
525
|
-
if (el) {
|
|
526
|
-
attach(el);
|
|
527
|
-
obs.disconnect();
|
|
528
|
-
}
|
|
529
|
-
});
|
|
530
|
-
obs.observe(containerRef, { childList: true, subtree: true });
|
|
531
|
-
return () => {
|
|
329
|
+
});
|
|
330
|
+
$effect(() => {
|
|
331
|
+
if (!infiniteScroll || !containerRef) return;
|
|
332
|
+
let scrollEl = null;
|
|
333
|
+
function attach(el) {
|
|
334
|
+
scrollEl = el;
|
|
335
|
+
scrollContainerRef = el;
|
|
336
|
+
el.addEventListener("scroll", onScroll);
|
|
337
|
+
queueMicrotask(onViewportChange);
|
|
338
|
+
}
|
|
339
|
+
function onScroll() {
|
|
340
|
+
onGridEvent?.("scroll", undefined, undefined, {
|
|
341
|
+
x: scrollContainerRef?.scrollLeft ?? 0,
|
|
342
|
+
y: scrollContainerRef?.scrollTop ?? 0
|
|
343
|
+
}, undefined);
|
|
344
|
+
maybeLoadNextPage();
|
|
345
|
+
}
|
|
346
|
+
function onViewportChange() {
|
|
347
|
+
onGridEvent?.("resize", undefined, undefined, {
|
|
348
|
+
x: containerRef?.clientWidth ?? 0,
|
|
349
|
+
y: containerRef?.clientHeight ?? 0
|
|
350
|
+
}, undefined);
|
|
351
|
+
maybeLoadNextPage();
|
|
352
|
+
}
|
|
353
|
+
window.addEventListener("scroll", onViewportChange, true);
|
|
354
|
+
window.addEventListener("resize", onViewportChange);
|
|
355
|
+
const candidate = containerRef.querySelector(".wx-scroll");
|
|
356
|
+
if (candidate) {
|
|
357
|
+
attach(candidate);
|
|
358
|
+
} else {
|
|
359
|
+
const obs = new MutationObserver(() => {
|
|
360
|
+
const el = containerRef?.querySelector(".wx-scroll");
|
|
361
|
+
if (el) {
|
|
362
|
+
attach(el);
|
|
532
363
|
obs.disconnect();
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
}
|
|
539
|
-
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
obs.observe(containerRef, {
|
|
367
|
+
childList: true,
|
|
368
|
+
subtree: true
|
|
369
|
+
});
|
|
540
370
|
return () => {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
window.removeEventListener(
|
|
371
|
+
obs.disconnect();
|
|
372
|
+
scrollEl?.removeEventListener("scroll", onScroll);
|
|
373
|
+
window.removeEventListener("scroll", onViewportChange, true);
|
|
374
|
+
window.removeEventListener("resize", onViewportChange);
|
|
544
375
|
scrollContainerRef = null;
|
|
545
376
|
};
|
|
546
|
-
}
|
|
377
|
+
}
|
|
378
|
+
return () => {
|
|
379
|
+
scrollEl?.removeEventListener("scroll", onScroll);
|
|
380
|
+
window.removeEventListener("scroll", onViewportChange, true);
|
|
381
|
+
window.removeEventListener("resize", onViewportChange);
|
|
382
|
+
scrollContainerRef = null;
|
|
383
|
+
};
|
|
384
|
+
});
|
|
547
385
|
</script>
|
|
548
386
|
|
|
549
387
|
{#snippet SearchMenu({ onClose }: { onClose: () => void })}
|