@warkypublic/svelix 0.1.46 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +492 -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 +489 -614
- package/dist/components/CardGrid/CardGrid.svelte.d.ts +4 -0
- package/dist/components/CardGrid/CardGridFilterPanel.svelte +111 -90
- package/dist/components/CardGrid/DefaultCard.svelte +86 -97
- package/dist/components/CardGrid/DefaultCard.svelte.d.ts +1 -0
- package/dist/components/CardGrid/ImageCardStory.svelte +87 -36
- package/dist/components/CardGrid/cardGrid.d.ts +24 -0
- package/dist/components/CardGrid/cardGrid.js +20 -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 +16 -80
- package/dist/components/Former/FormerDrawerPreview.svelte +99 -89
- package/dist/components/Former/FormerModal.svelte +16 -74
- 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/Former/formerOverlayHelpers.svelte.d.ts +11 -0
- package/dist/components/Former/formerOverlayHelpers.svelte.js +38 -0
- package/dist/components/Former/index.d.ts +1 -0
- package/dist/components/Former/index.js +1 -0
- package/dist/components/FormerControllers/ButtonCtrl.svelte +13 -49
- package/dist/components/FormerControllers/CtrlFieldShell.svelte +15 -0
- package/dist/components/FormerControllers/CtrlFieldShell.svelte.d.ts +12 -0
- package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrl.svelte +285 -457
- 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 -47
- package/dist/components/FormerControllers/InlineWrapper.svelte +12 -41
- package/dist/components/FormerControllers/InlineWrapperPreview.svelte +4 -26
- package/dist/components/FormerControllers/NativeSelectCtrl.svelte +7 -39
- package/dist/components/FormerControllers/NumberInputCtrl.svelte +21 -43
- package/dist/components/FormerControllers/PasswordInputCtrl.svelte +8 -33
- package/dist/components/FormerControllers/SwitchCtrl.svelte +4 -20
- package/dist/components/FormerControllers/TextAreaCtrl.svelte +7 -34
- package/dist/components/FormerControllers/TextInputCtrl.svelte +7 -34
- package/dist/components/FormerControllers/ctrlLoading.svelte.d.ts +8 -0
- package/dist/components/FormerControllers/ctrlLoading.svelte.js +43 -0
- package/dist/components/FormerControllers/index.d.ts +2 -0
- package/dist/components/FormerControllers/index.js +2 -0
- package/dist/components/FormerControllers/utils.d.ts +1 -0
- package/dist/components/FormerControllers/utils.js +3 -0
- package/dist/components/GlobalStateStore/GlobalStateStoreProvider.svelte +73 -120
- package/dist/components/Gridler/components/Gridler.svelte +344 -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 +521 -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/Gridler/utils/serverPaging.d.ts +1 -0
- package/dist/components/Gridler/utils/serverPaging.js +7 -0
- 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 +454 -431
- package/dist/components/VTree/VTree.utils.d.ts +23 -0
- package/dist/components/VTree/VTree.utils.js +73 -0
- package/dist/components/VTree/VTreeContextMenu.svelte +21 -33
- package/dist/components/VTree/VTreeControlledSelectionDemo.svelte +20 -0
- package/dist/components/VTree/VTreeControlledSelectionDemo.svelte.d.ts +18 -0
- package/dist/components/VTree/VTreeCustomizationDemo.svelte +23 -0
- package/dist/components/VTree/VTreeCustomizationDemo.svelte.d.ts +18 -0
- package/dist/components/VTree/VTreeEventsDemo.svelte +48 -58
- package/dist/components/VTree/VTreeInteractionDemo.svelte +29 -0
- package/dist/components/VTree/VTreeInteractionDemo.svelte.d.ts +18 -0
- package/dist/components/VTree/VTreeMultiSelectDemo.svelte +27 -0
- package/dist/components/VTree/VTreeMultiSelectDemo.svelte.d.ts +18 -0
- package/dist/components/VTree/VTreeRow.svelte +77 -115
- package/dist/components/VTree/VTreeRow.svelte.d.ts +34 -18
- package/dist/components/VTree/VTreeSearch.svelte +6 -13
- package/dist/components/VTree/VTreeVirtualViewport.svelte +54 -143
- package/dist/components/VTree/VTreeVirtualViewport.svelte.d.ts +35 -18
- package/dist/components/VTree/types.d.ts +39 -1
- package/llm/COMPONENT_GUIDE.md +25 -0
- package/package.json +51 -50
|
@@ -1,605 +1,492 @@
|
|
|
1
|
-
<script lang="ts"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
// Consume the pointer sequence so it cannot activate elements behind the backdrop.
|
|
491
|
-
e.preventDefault();
|
|
492
|
-
e.stopPropagation();
|
|
493
|
-
store.setOpened(false);
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
function onOptionPointerDown(e: PointerEvent, index: number) {
|
|
497
|
-
// Commit selection before blur/focusout closes the popover on some browsers.
|
|
498
|
-
e.preventDefault();
|
|
499
|
-
e.stopPropagation();
|
|
500
|
-
onOptionSubmit(index);
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
function syncInsideDialogFlag() {
|
|
504
|
-
insideDialog = !!anchorEl?.closest('dialog, [role="dialog"]');
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// Public API via bind:this
|
|
508
|
-
export function clear() {
|
|
509
|
-
onClear();
|
|
510
|
-
}
|
|
511
|
-
export function close() {
|
|
512
|
-
store.setOpened(false);
|
|
513
|
-
activeOptionIndex = null;
|
|
514
|
-
}
|
|
515
|
-
export function focus() {
|
|
516
|
-
suppressOpenOnFocus = false;
|
|
517
|
-
targetRef?.focus();
|
|
518
|
-
}
|
|
519
|
-
export function getValue() {
|
|
520
|
-
return value;
|
|
521
|
-
}
|
|
522
|
-
export function open() {
|
|
523
|
-
if (disabled) return;
|
|
524
|
-
store.setOpened(true);
|
|
525
|
-
activeOptionIndex = null;
|
|
526
|
-
}
|
|
527
|
-
export function refetch() {
|
|
528
|
-
return store.refetch();
|
|
529
|
-
}
|
|
530
|
-
export function setValue(val: unknown) {
|
|
531
|
-
value = val;
|
|
532
|
-
onChange?.(val);
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
$effect(() => {
|
|
536
|
-
if (!$store.opened || effectiveDisablePortal || disabled) {
|
|
537
|
-
return;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
updatePopoverPosition();
|
|
541
|
-
|
|
542
|
-
if (typeof window === "undefined") {
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
const onWindowChange = () => {
|
|
547
|
-
updatePopoverPosition();
|
|
548
|
-
};
|
|
549
|
-
|
|
550
|
-
window.addEventListener("resize", onWindowChange);
|
|
551
|
-
window.addEventListener("scroll", onWindowChange, true);
|
|
552
|
-
|
|
553
|
-
return () => {
|
|
554
|
-
window.removeEventListener("resize", onWindowChange);
|
|
555
|
-
window.removeEventListener("scroll", onWindowChange, true);
|
|
556
|
-
};
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
$effect(() => {
|
|
560
|
-
if (!$store.opened || disabled) {
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
const overlay = registerOverlay({
|
|
565
|
-
kind: "popover",
|
|
566
|
-
mount: effectiveDisablePortal ? "inline" : "portal",
|
|
567
|
-
});
|
|
568
|
-
backdropZ = overlay.layer.backdrop;
|
|
569
|
-
dropdownZ = overlay.layer.content;
|
|
570
|
-
|
|
571
|
-
return () => {
|
|
572
|
-
overlay.unregister();
|
|
573
|
-
backdropZ = 1090;
|
|
574
|
-
dropdownZ = 1100;
|
|
575
|
-
};
|
|
576
|
-
});
|
|
577
|
-
|
|
578
|
-
$effect(() => {
|
|
579
|
-
void anchorEl;
|
|
580
|
-
void storeOpened;
|
|
581
|
-
syncInsideDialogFlag();
|
|
582
|
-
});
|
|
583
|
-
|
|
584
|
-
$effect(() => {
|
|
585
|
-
if (!$store.opened || !effectiveDisablePortal || disabled || typeof window === "undefined") {
|
|
586
|
-
return;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
const onDocumentPointerDown = (event: PointerEvent) => {
|
|
590
|
-
const target = event.target as Node | null;
|
|
591
|
-
if (!target) return;
|
|
592
|
-
|
|
593
|
-
const insideAnchor = !!anchorEl?.contains(target);
|
|
594
|
-
const insideDropdown = !!dropdownEl?.contains(target);
|
|
595
|
-
if (!insideAnchor && !insideDropdown) {
|
|
596
|
-
store.setOpened(false);
|
|
597
|
-
}
|
|
598
|
-
};
|
|
599
|
-
|
|
600
|
-
window.addEventListener("pointerdown", onDocumentPointerDown, true);
|
|
601
|
-
return () => window.removeEventListener("pointerdown", onDocumentPointerDown, true);
|
|
602
|
-
});
|
|
1
|
+
<script lang="ts">/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
|
|
2
|
+
import { untrack } from "svelte";
|
|
3
|
+
import { createVirtualizer } from "@tanstack/svelte-virtual";
|
|
4
|
+
import { registerOverlay } from "../OverlayStack";
|
|
5
|
+
import Portal from "../Portal/Portal.svelte";
|
|
6
|
+
import { createBoxerStore } from "./store";
|
|
7
|
+
import BoxerTarget from "./BoxerTarget.svelte";
|
|
8
|
+
let { adapter, clearable = true, data = [], dataSource: dataSourceProp = undefined, debounceMs = 300, disablePortal = false, disabled, error, id, label, leftSection, mah = 200, multiSelect, name, onAPICall: onAPICallProp = undefined, onBufferChange, onChange, onResolveValues: onResolveValuesProp = undefined, openOnClear, pageSize = 50, placeholder, rightSection, searchColumns, selectFirst, showAll, value = $bindable(undefined) } = $props();
|
|
9
|
+
const instanceId = `boxer-${Math.random().toString(36).slice(2, 10)}`;
|
|
10
|
+
// eslint-disable-next-line svelte/no-unused-svelte-ignore
|
|
11
|
+
// svelte-ignore state_referenced_locally -- id is intentionally captured once for a stable inputId
|
|
12
|
+
const inputId = id ?? `${instanceId}-input`;
|
|
13
|
+
const listboxId = `${instanceId}-listbox`;
|
|
14
|
+
// Derive effective dataSource, onAPICall, resolver from adapter when not explicitly provided.
|
|
15
|
+
// eslint-disable-next-line svelte/no-unused-svelte-ignore
|
|
16
|
+
// svelte-ignore state_referenced_locally -- adapter/dataSourceProp/onAPICallProp/onResolveValuesProp captured once at mount
|
|
17
|
+
const dataSource = dataSourceProp ?? (adapter ? "server" : "local");
|
|
18
|
+
// svelte-ignore state_referenced_locally
|
|
19
|
+
const onAPICall = onAPICallProp ?? (adapter ? (params) => adapter.fetch(params) : undefined);
|
|
20
|
+
// svelte-ignore state_referenced_locally
|
|
21
|
+
const onResolveValues = onResolveValuesProp ?? (adapter?.resolveByValue ? (values, signal) => adapter.resolveByValue(values, signal) : undefined);
|
|
22
|
+
// Create store once with initial props
|
|
23
|
+
// eslint-disable-next-line svelte/no-unused-svelte-ignore
|
|
24
|
+
// svelte-ignore state_referenced_locally -- all props captured at mount for store initialisation
|
|
25
|
+
const store = createBoxerStore({
|
|
26
|
+
clearable,
|
|
27
|
+
data,
|
|
28
|
+
dataSource,
|
|
29
|
+
disabled,
|
|
30
|
+
error,
|
|
31
|
+
label,
|
|
32
|
+
mah,
|
|
33
|
+
multiSelect,
|
|
34
|
+
onAPICall,
|
|
35
|
+
onBufferChange,
|
|
36
|
+
onChange,
|
|
37
|
+
onResolveValues,
|
|
38
|
+
openOnClear,
|
|
39
|
+
pageSize,
|
|
40
|
+
placeholder,
|
|
41
|
+
searchColumns,
|
|
42
|
+
selectFirst,
|
|
43
|
+
showAll,
|
|
44
|
+
value
|
|
45
|
+
});
|
|
46
|
+
// Fine-grained derived values so $effect blocks only re-run when the
|
|
47
|
+
// specific field they care about actually changes (not on every store update).
|
|
48
|
+
const storeSearch = $derived($store.search);
|
|
49
|
+
const storeOpened = $derived($store.opened);
|
|
50
|
+
const storeBoxerData = $derived($store.boxerData);
|
|
51
|
+
const storeSelectedItems = $derived($store.selectedItems);
|
|
52
|
+
const storeInput = $derived($store.input);
|
|
53
|
+
const boxerDataLength = $derived($store.boxerData.length);
|
|
54
|
+
// Sync value prop changes into store
|
|
55
|
+
$effect(() => {
|
|
56
|
+
store.updateProps({ value });
|
|
57
|
+
});
|
|
58
|
+
// Virtualizer
|
|
59
|
+
let parentEl = $state(undefined);
|
|
60
|
+
let targetRef = $state(undefined);
|
|
61
|
+
let anchorEl = $state(undefined);
|
|
62
|
+
let dropdownEl = $state(undefined);
|
|
63
|
+
let popupTop = $state(0);
|
|
64
|
+
let popupLeft = $state(0);
|
|
65
|
+
let popupWidth = $state(0);
|
|
66
|
+
let activeOptionIndex = $state(null);
|
|
67
|
+
let backdropZ = $state(1090);
|
|
68
|
+
let dropdownZ = $state(1100);
|
|
69
|
+
let pointerInteractingWithDropdown = $state(false);
|
|
70
|
+
let insideDialog = $state(false);
|
|
71
|
+
let suppressOpenOnFocus = $state(false);
|
|
72
|
+
const effectiveDisablePortal = $derived(disablePortal || insideDialog);
|
|
73
|
+
// Plain variable — NOT $state to avoid deep proxy on the complex virtualizer object.
|
|
74
|
+
let rawVirtualizer = null;
|
|
75
|
+
// Virtualizer outputs as $state — updated directly by the subscribe callback.
|
|
76
|
+
let virtualItems = $state.raw([]);
|
|
77
|
+
let totalSize = $state(0);
|
|
78
|
+
// Create virtualizer only when parentEl changes (dropdown mount/unmount).
|
|
79
|
+
$effect(() => {
|
|
80
|
+
if (!parentEl) {
|
|
81
|
+
rawVirtualizer = null;
|
|
82
|
+
virtualItems = [];
|
|
83
|
+
totalSize = 0;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const initialCount = untrack(() => boxerDataLength);
|
|
87
|
+
const v = createVirtualizer({
|
|
88
|
+
count: initialCount,
|
|
89
|
+
estimateSize: () => 36,
|
|
90
|
+
getScrollElement: () => parentEl
|
|
91
|
+
});
|
|
92
|
+
return v.subscribe((instance) => {
|
|
93
|
+
rawVirtualizer = instance;
|
|
94
|
+
virtualItems = instance.getVirtualItems();
|
|
95
|
+
totalSize = instance.getTotalSize();
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
// Update virtualizer count when data length actually changes (preserves scroll).
|
|
99
|
+
$effect(() => {
|
|
100
|
+
const count = boxerDataLength;
|
|
101
|
+
if (rawVirtualizer && parentEl) {
|
|
102
|
+
rawVirtualizer.setOptions({
|
|
103
|
+
count,
|
|
104
|
+
estimateSize: () => 36,
|
|
105
|
+
getScrollElement: () => parentEl
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
// Debounced search — only reacts to actual search text changes.
|
|
110
|
+
let searchTimeout;
|
|
111
|
+
let didInitialFetch = false;
|
|
112
|
+
$effect(() => {
|
|
113
|
+
const search = storeSearch;
|
|
114
|
+
// Skip the initial tick: the initial-fetch effect below handles first load.
|
|
115
|
+
if (!didInitialFetch) return;
|
|
116
|
+
clearTimeout(searchTimeout);
|
|
117
|
+
searchTimeout = setTimeout(() => {
|
|
118
|
+
if (search !== undefined && storeOpened) {
|
|
119
|
+
store.fetchData(search, true);
|
|
120
|
+
parentEl?.scrollTo?.({ top: 0 });
|
|
121
|
+
}
|
|
122
|
+
}, debounceMs);
|
|
123
|
+
return () => clearTimeout(searchTimeout);
|
|
124
|
+
});
|
|
125
|
+
// Initial data fetch + resolve labels for preloaded value(s).
|
|
126
|
+
$effect(() => {
|
|
127
|
+
didInitialFetch = true;
|
|
128
|
+
store.fetchData("", true);
|
|
129
|
+
resolvePreloadedValue(value);
|
|
130
|
+
return () => {
|
|
131
|
+
clearTimeout(searchTimeout);
|
|
132
|
+
store.cancel();
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
let lastResolvedKey = null;
|
|
136
|
+
async function resolvePreloadedValue(val) {
|
|
137
|
+
if (val == null || val === "" || Array.isArray(val) && val.length === 0) return;
|
|
138
|
+
if (!onResolveValues) return;
|
|
139
|
+
const values = Array.isArray(val) ? val : [val];
|
|
140
|
+
const key = JSON.stringify(values);
|
|
141
|
+
if (key === lastResolvedKey) return;
|
|
142
|
+
lastResolvedKey = key;
|
|
143
|
+
try {
|
|
144
|
+
const items = await onResolveValues(values);
|
|
145
|
+
if (!items || items.length === 0) return;
|
|
146
|
+
if (multiSelect) {
|
|
147
|
+
store.setSelectedItems(items);
|
|
148
|
+
} else {
|
|
149
|
+
store.setSelectedItems(items.slice(0, 1));
|
|
150
|
+
}
|
|
151
|
+
} catch (err) {
|
|
152
|
+
console.error("Boxer resolveByValue error:", err);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// When value prop changes from outside, resolve any new labels we don't yet have.
|
|
156
|
+
$effect(() => {
|
|
157
|
+
void value;
|
|
158
|
+
untrack(() => {
|
|
159
|
+
if (value == null) {
|
|
160
|
+
store.clearSelectedItems();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const knownKeys = new Set($store.selectedItems.map((i) => JSON.stringify(i.value)));
|
|
164
|
+
const needed = (Array.isArray(value) ? value : [value]).filter((v) => !knownKeys.has(JSON.stringify(v)));
|
|
165
|
+
if (needed.length === 0) {
|
|
166
|
+
if (!multiSelect) {
|
|
167
|
+
// Keep the single-item list in sync with the chosen value.
|
|
168
|
+
const match = $store.selectedItems.find((i) => JSON.stringify(i.value) === JSON.stringify(value));
|
|
169
|
+
if (match) store.setSelectedItems([match]);
|
|
170
|
+
} else {
|
|
171
|
+
// Drop any selected items whose value is no longer in `value`.
|
|
172
|
+
const keep = new Set(value.map((v) => JSON.stringify(v)));
|
|
173
|
+
const filtered = $store.selectedItems.filter((i) => keep.has(JSON.stringify(i.value)));
|
|
174
|
+
if (filtered.length !== $store.selectedItems.length) {
|
|
175
|
+
store.setSelectedItems(filtered);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
// Fall back to boxerData, then to the resolver.
|
|
181
|
+
const fromBoxer = $store.boxerData.filter((i) => needed.some((v) => JSON.stringify(v) === JSON.stringify(i.value)));
|
|
182
|
+
fromBoxer.forEach((i) => store.addSelectedItem(i));
|
|
183
|
+
const stillMissing = needed.filter((v) => !fromBoxer.some((i) => JSON.stringify(i.value) === JSON.stringify(v)));
|
|
184
|
+
if (stillMissing.length > 0) resolvePreloadedValue(stillMissing);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
// Sync displayed input + emit onBufferChange from selectedItems (single source of truth).
|
|
188
|
+
$effect(() => {
|
|
189
|
+
const selected = storeSelectedItems;
|
|
190
|
+
const opened = storeOpened;
|
|
191
|
+
const currentInput = storeInput;
|
|
192
|
+
if (multiSelect) {
|
|
193
|
+
const labels = selected.map((i) => i.label).join(", ");
|
|
194
|
+
if (!opened && currentInput !== labels) store.setInput(labels);
|
|
195
|
+
onBufferChange?.(selected);
|
|
196
|
+
} else {
|
|
197
|
+
const item = selected[0] ?? null;
|
|
198
|
+
if (item && currentInput !== item.label && !opened) {
|
|
199
|
+
store.setInput(item.label);
|
|
200
|
+
} else if (!item && !opened && currentInput !== "") {
|
|
201
|
+
store.setInput("");
|
|
202
|
+
}
|
|
203
|
+
onBufferChange?.(item);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
// When data arrives, opportunistically enrich selectedItems for multiselect
|
|
207
|
+
// (adds labels for items that just loaded).
|
|
208
|
+
$effect(() => {
|
|
209
|
+
const boxerData = storeBoxerData;
|
|
210
|
+
if (!multiSelect || !Array.isArray(value) || value.length === 0) return;
|
|
211
|
+
untrack(() => {
|
|
212
|
+
const known = new Set($store.selectedItems.map((i) => JSON.stringify(i.value)));
|
|
213
|
+
for (const v of value) {
|
|
214
|
+
if (known.has(JSON.stringify(v))) continue;
|
|
215
|
+
const match = boxerData.find((i) => JSON.stringify(i.value) === JSON.stringify(v));
|
|
216
|
+
if (match) store.addSelectedItem(match);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
// Select first
|
|
221
|
+
// `value` is read via untrack() — this effect should react to boxerData becoming
|
|
222
|
+
// ready, not to its own write (onOptionSubmit assigns `value`), which would
|
|
223
|
+
// otherwise cause a spurious second fire whenever the first option's value is
|
|
224
|
+
// falsy (0/"").
|
|
225
|
+
$effect(() => {
|
|
226
|
+
const boxerData = storeBoxerData;
|
|
227
|
+
if (selectFirst && boxerData.length > 0 && !multiSelect && !untrack(() => value)) {
|
|
228
|
+
onOptionSubmit(0);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
function onOptionSubmit(index) {
|
|
232
|
+
const option = $store.boxerData?.[index];
|
|
233
|
+
if (!option) return;
|
|
234
|
+
activeOptionIndex = index;
|
|
235
|
+
if (multiSelect) {
|
|
236
|
+
const currentValues = Array.isArray(value) ? value : [];
|
|
237
|
+
const isSelected = currentValues.includes(option.value);
|
|
238
|
+
if (isSelected) {
|
|
239
|
+
value = currentValues.filter((v) => v !== option.value);
|
|
240
|
+
store.removeSelectedItem(option.value);
|
|
241
|
+
} else {
|
|
242
|
+
value = [...currentValues, option.value];
|
|
243
|
+
store.addSelectedItem(option);
|
|
244
|
+
}
|
|
245
|
+
onChange?.(value);
|
|
246
|
+
} else {
|
|
247
|
+
value = option.value;
|
|
248
|
+
store.setSelectedItems([option]);
|
|
249
|
+
onChange?.(option.value);
|
|
250
|
+
store.setSearch("");
|
|
251
|
+
store.setInput(option.label);
|
|
252
|
+
store.setOpened(false);
|
|
253
|
+
activeOptionIndex = null;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
function onClear() {
|
|
257
|
+
if (showAll && selectFirst) {
|
|
258
|
+
onOptionSubmit(0);
|
|
259
|
+
} else {
|
|
260
|
+
value = multiSelect ? [] : null;
|
|
261
|
+
store.clearSelectedItems();
|
|
262
|
+
onChange?.(value);
|
|
263
|
+
store.setSearch("");
|
|
264
|
+
store.setInput("");
|
|
265
|
+
activeOptionIndex = null;
|
|
266
|
+
targetRef?.focus();
|
|
267
|
+
}
|
|
268
|
+
if (openOnClear) {
|
|
269
|
+
store.setOpened(true);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function focusDropdownItem(index) {
|
|
273
|
+
const length = $store.boxerData.length;
|
|
274
|
+
if (length === 0) return;
|
|
275
|
+
const clamped = Math.max(0, Math.min(index, length - 1));
|
|
276
|
+
activeOptionIndex = clamped;
|
|
277
|
+
rawVirtualizer?.scrollToIndex(clamped, { align: "auto" });
|
|
278
|
+
requestAnimationFrame(() => {
|
|
279
|
+
const el = parentEl?.querySelector(`[data-index="${clamped}"]`);
|
|
280
|
+
if (el) {
|
|
281
|
+
el.scrollIntoView({ block: "nearest" });
|
|
282
|
+
el.focus();
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
function onInputKeydown(e) {
|
|
287
|
+
switch (e.key) {
|
|
288
|
+
case "ArrowDown":
|
|
289
|
+
e.preventDefault();
|
|
290
|
+
if (!$store.opened) store.setOpened(true);
|
|
291
|
+
requestAnimationFrame(() => {
|
|
292
|
+
if ($store.boxerData.length > 0) focusDropdownItem(0);
|
|
293
|
+
});
|
|
294
|
+
break;
|
|
295
|
+
case "Enter":
|
|
296
|
+
e.preventDefault();
|
|
297
|
+
if (!$store.opened) {
|
|
298
|
+
store.setOpened(true);
|
|
299
|
+
} else if (!multiSelect) {
|
|
300
|
+
const selectedIndex = $store.boxerData.findIndex((item) => item.value === value);
|
|
301
|
+
if (selectedIndex >= 0) {
|
|
302
|
+
store.setOpened(false);
|
|
303
|
+
activeOptionIndex = null;
|
|
304
|
+
} else {
|
|
305
|
+
onOptionSubmit(0);
|
|
306
|
+
}
|
|
307
|
+
} else {
|
|
308
|
+
store.setOpened(false);
|
|
309
|
+
activeOptionIndex = null;
|
|
310
|
+
}
|
|
311
|
+
break;
|
|
312
|
+
case "Escape":
|
|
313
|
+
if ($store.opened) {
|
|
314
|
+
e.preventDefault();
|
|
315
|
+
store.setOpened(false);
|
|
316
|
+
activeOptionIndex = null;
|
|
317
|
+
}
|
|
318
|
+
break;
|
|
319
|
+
case "Tab":
|
|
320
|
+
if (multiSelect && $store.opened && $store.boxerData.length > 0) {
|
|
321
|
+
e.preventDefault();
|
|
322
|
+
onOptionSubmit(0);
|
|
323
|
+
}
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
function onDropdownItemKeydown(e, index) {
|
|
328
|
+
const length = $store.boxerData.length;
|
|
329
|
+
const pageSizeNav = Math.max(1, Math.floor(mah / 36));
|
|
330
|
+
switch (e.key) {
|
|
331
|
+
case "ArrowDown":
|
|
332
|
+
e.preventDefault();
|
|
333
|
+
if (index < length - 1) focusDropdownItem(index + 1);
|
|
334
|
+
break;
|
|
335
|
+
case "ArrowUp":
|
|
336
|
+
e.preventDefault();
|
|
337
|
+
if (index === 0) {
|
|
338
|
+
activeOptionIndex = null;
|
|
339
|
+
targetRef?.focus();
|
|
340
|
+
} else {
|
|
341
|
+
focusDropdownItem(index - 1);
|
|
342
|
+
}
|
|
343
|
+
break;
|
|
344
|
+
case "Home":
|
|
345
|
+
e.preventDefault();
|
|
346
|
+
focusDropdownItem(0);
|
|
347
|
+
break;
|
|
348
|
+
case "End":
|
|
349
|
+
e.preventDefault();
|
|
350
|
+
focusDropdownItem(length - 1);
|
|
351
|
+
break;
|
|
352
|
+
case "PageDown":
|
|
353
|
+
e.preventDefault();
|
|
354
|
+
focusDropdownItem(Math.min(length - 1, index + pageSizeNav));
|
|
355
|
+
break;
|
|
356
|
+
case "PageUp":
|
|
357
|
+
e.preventDefault();
|
|
358
|
+
focusDropdownItem(Math.max(0, index - pageSizeNav));
|
|
359
|
+
break;
|
|
360
|
+
case "Enter":
|
|
361
|
+
case " ":
|
|
362
|
+
e.preventDefault();
|
|
363
|
+
onOptionSubmit(index);
|
|
364
|
+
if (!multiSelect) targetRef?.focus();
|
|
365
|
+
break;
|
|
366
|
+
case "Escape":
|
|
367
|
+
e.preventDefault();
|
|
368
|
+
store.setOpened(false);
|
|
369
|
+
activeOptionIndex = null;
|
|
370
|
+
suppressOpenOnFocus = true;
|
|
371
|
+
targetRef?.focus();
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function updatePopoverPosition() {
|
|
376
|
+
if (!anchorEl) return;
|
|
377
|
+
const rect = anchorEl.getBoundingClientRect();
|
|
378
|
+
const viewportWidth = typeof window === "undefined" ? rect.width : window.innerWidth;
|
|
379
|
+
const horizontalPadding = 8;
|
|
380
|
+
const maxWidth = Math.max(160, viewportWidth - horizontalPadding * 2);
|
|
381
|
+
const nextWidth = Math.min(Math.max(220, rect.width), maxWidth);
|
|
382
|
+
const maxLeft = viewportWidth - nextWidth - horizontalPadding;
|
|
383
|
+
popupTop = rect.bottom + 4;
|
|
384
|
+
popupWidth = nextWidth;
|
|
385
|
+
popupLeft = Math.min(Math.max(rect.left, horizontalPadding), Math.max(horizontalPadding, maxLeft));
|
|
386
|
+
}
|
|
387
|
+
function markDropdownPointerInteraction() {
|
|
388
|
+
pointerInteractingWithDropdown = true;
|
|
389
|
+
setTimeout(() => {
|
|
390
|
+
pointerInteractingWithDropdown = false;
|
|
391
|
+
}, 0);
|
|
392
|
+
}
|
|
393
|
+
function onBackdropPointerDown(e) {
|
|
394
|
+
// Consume the pointer sequence so it cannot activate elements behind the backdrop.
|
|
395
|
+
e.preventDefault();
|
|
396
|
+
e.stopPropagation();
|
|
397
|
+
store.setOpened(false);
|
|
398
|
+
}
|
|
399
|
+
function onOptionPointerDown(e, index) {
|
|
400
|
+
// Commit selection before blur/focusout closes the popover on some browsers.
|
|
401
|
+
e.preventDefault();
|
|
402
|
+
e.stopPropagation();
|
|
403
|
+
onOptionSubmit(index);
|
|
404
|
+
}
|
|
405
|
+
function syncInsideDialogFlag() {
|
|
406
|
+
insideDialog = !!anchorEl?.closest("dialog, [role=\"dialog\"]");
|
|
407
|
+
}
|
|
408
|
+
// Public API via bind:this
|
|
409
|
+
export function clear() {
|
|
410
|
+
onClear();
|
|
411
|
+
}
|
|
412
|
+
export function close() {
|
|
413
|
+
store.setOpened(false);
|
|
414
|
+
activeOptionIndex = null;
|
|
415
|
+
}
|
|
416
|
+
export function focus() {
|
|
417
|
+
suppressOpenOnFocus = false;
|
|
418
|
+
targetRef?.focus();
|
|
419
|
+
}
|
|
420
|
+
export function getValue() {
|
|
421
|
+
return value;
|
|
422
|
+
}
|
|
423
|
+
export function open() {
|
|
424
|
+
if (disabled) return;
|
|
425
|
+
store.setOpened(true);
|
|
426
|
+
activeOptionIndex = null;
|
|
427
|
+
}
|
|
428
|
+
export function refetch() {
|
|
429
|
+
return store.refetch();
|
|
430
|
+
}
|
|
431
|
+
export function setValue(val) {
|
|
432
|
+
value = val;
|
|
433
|
+
onChange?.(val);
|
|
434
|
+
}
|
|
435
|
+
$effect(() => {
|
|
436
|
+
if (!$store.opened || effectiveDisablePortal || disabled) {
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
updatePopoverPosition();
|
|
440
|
+
if (typeof window === "undefined") {
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
const onWindowChange = () => {
|
|
444
|
+
updatePopoverPosition();
|
|
445
|
+
};
|
|
446
|
+
window.addEventListener("resize", onWindowChange);
|
|
447
|
+
window.addEventListener("scroll", onWindowChange, true);
|
|
448
|
+
return () => {
|
|
449
|
+
window.removeEventListener("resize", onWindowChange);
|
|
450
|
+
window.removeEventListener("scroll", onWindowChange, true);
|
|
451
|
+
};
|
|
452
|
+
});
|
|
453
|
+
$effect(() => {
|
|
454
|
+
if (!$store.opened || disabled) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
const overlay = registerOverlay({
|
|
458
|
+
kind: "popover",
|
|
459
|
+
mount: effectiveDisablePortal ? "inline" : "portal"
|
|
460
|
+
});
|
|
461
|
+
backdropZ = overlay.layer.backdrop;
|
|
462
|
+
dropdownZ = overlay.layer.content;
|
|
463
|
+
return () => {
|
|
464
|
+
overlay.unregister();
|
|
465
|
+
backdropZ = 1090;
|
|
466
|
+
dropdownZ = 1100;
|
|
467
|
+
};
|
|
468
|
+
});
|
|
469
|
+
$effect(() => {
|
|
470
|
+
void anchorEl;
|
|
471
|
+
void storeOpened;
|
|
472
|
+
syncInsideDialogFlag();
|
|
473
|
+
});
|
|
474
|
+
$effect(() => {
|
|
475
|
+
if (!$store.opened || !effectiveDisablePortal || disabled || typeof window === "undefined") {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
const onDocumentPointerDown = (event) => {
|
|
479
|
+
const target = event.target;
|
|
480
|
+
if (!target) return;
|
|
481
|
+
const insideAnchor = !!anchorEl?.contains(target);
|
|
482
|
+
const insideDropdown = !!dropdownEl?.contains(target);
|
|
483
|
+
if (!insideAnchor && !insideDropdown) {
|
|
484
|
+
store.setOpened(false);
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
window.addEventListener("pointerdown", onDocumentPointerDown, true);
|
|
488
|
+
return () => window.removeEventListener("pointerdown", onDocumentPointerDown, true);
|
|
489
|
+
});
|
|
603
490
|
</script>
|
|
604
491
|
|
|
605
492
|
<div
|
|
@@ -631,6 +518,7 @@
|
|
|
631
518
|
{inputId}
|
|
632
519
|
{label}
|
|
633
520
|
{leftSection}
|
|
521
|
+
{name}
|
|
634
522
|
{placeholder}
|
|
635
523
|
isFetching={$store.isFetching}
|
|
636
524
|
search={$store.input}
|
|
@@ -657,6 +545,7 @@
|
|
|
657
545
|
<div
|
|
658
546
|
class="fixed inset-0"
|
|
659
547
|
data-testid="boxer-backdrop"
|
|
548
|
+
role="presentation"
|
|
660
549
|
onpointerdown={onBackdropPointerDown}
|
|
661
550
|
style:z-index={backdropZ}
|
|
662
551
|
></div>
|
|
@@ -670,6 +559,7 @@
|
|
|
670
559
|
data-testid="boxer-dropdown"
|
|
671
560
|
id={listboxId}
|
|
672
561
|
role="listbox"
|
|
562
|
+
tabindex="-1"
|
|
673
563
|
aria-label={label ?? "Options"}
|
|
674
564
|
onpointerdown={markDropdownPointerInteraction}
|
|
675
565
|
style:z-index={dropdownZ}
|