@xen-orchestra/web-core 0.57.0 → 0.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/copy-all-menu-item/VtsCopyAllMenuItem.vue +48 -0
- package/lib/components/copy-button/VtsCopyButton.vue +2 -2
- package/lib/components/overlay/VtsOverlayList.vue +2 -2
- package/lib/components/ui/tree-item-label/UiTreeItemLabel.vue +34 -14
- package/lib/composables/copy.composable.ts +6 -0
- package/lib/locales/cs.json +50 -2
- package/lib/locales/de.json +3 -0
- package/lib/locales/ko.json +2 -0
- package/lib/locales/nl.json +132 -1
- package/lib/locales/pt.json +1 -1
- package/lib/locales/sk.json +50 -2
- package/lib/packages/overlay/README.md +19 -4
- package/lib/packages/overlay/create-event-handler.ts +4 -11
- package/lib/packages/overlay/types.ts +1 -0
- package/lib/packages/overlay/use-overlay-store.ts +5 -0
- package/lib/packages/overlay/use-overlay.ts +25 -10
- package/lib/packages/remote-resource/define-remote-resource.ts +55 -26
- package/lib/packages/remote-resource/sse.store.ts +4 -4
- package/package.json +1 -1
package/lib/locales/sk.json
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"action:create-network": "Vytvoriť sieť",
|
|
40
40
|
"action:create-traffic-rule": "Vytvoriť pravidlo pre prevádzku",
|
|
41
41
|
"action:create-vdi": "Vytvoriť obraz virt. disku",
|
|
42
|
+
"action:create-vif": "Vytvoriť virt. sieťové rozhranie",
|
|
42
43
|
"action:delete": "Odstrániť",
|
|
43
44
|
"action:delete-filter": "Odstrániť filter",
|
|
44
45
|
"action:delete-group": "Odstrániť skupinu",
|
|
@@ -61,27 +62,34 @@
|
|
|
61
62
|
"action:disconnect-n-vifs": "Odpojiť 1 virt. sieťové rozhranie | Odpojiť {n} virt. sieťové rozhrania | Odpojiť {n} virt. sieťových rozhraní",
|
|
62
63
|
"action:download-bugtools-archive": "Stiahnuť archív s výstupom bugtools",
|
|
63
64
|
"action:duplicate": "Zduplikovať",
|
|
65
|
+
"action:duplicate-n-vms": "Zduplikovať 1 virt. stroj | Zduplikovať {n} virt. stroje | Zduplikovať {n} virt. strojov",
|
|
64
66
|
"action:edit": "Upraviť",
|
|
65
67
|
"action:edit-config": "Upraviť konfiguráciu",
|
|
66
68
|
"action:enable-light-mode": "Zapnúť svetlý režim",
|
|
67
69
|
"action:export": "Export",
|
|
70
|
+
"action:export-content": "Exportovať obsah",
|
|
68
71
|
"action:export-n-vms": "Exportovať 1 virt. stroj | Exportovať {n} virt. strojov",
|
|
69
72
|
"action:export-n-vms-manually": "Exportovať 1 virt. stroj ručne | Exportovať {n} virt. strojov ručne",
|
|
70
73
|
"action:export-table-to": "Exportovať tabuľku do {type}",
|
|
74
|
+
"action:export-vdi": "Exportovať obraz virt. disku",
|
|
71
75
|
"action:export-vm": "Exportovať virt. stroj | Exportovať virt. stroje",
|
|
72
76
|
"action:filter": "Filtrovať",
|
|
77
|
+
"action:force-migrate-on-sr": "Vynútiť presťahovanie na repozitár úložiska",
|
|
73
78
|
"action:force-reboot": "Vynútiť reštart",
|
|
74
79
|
"action:force-shutdown": "Vynútiť vypnutie",
|
|
75
80
|
"action:forget": "Zabudnúť",
|
|
76
81
|
"action:go-back": "Prejsť späť",
|
|
77
82
|
"action:group": "Skupina",
|
|
78
83
|
"action:hide": "Skryť",
|
|
84
|
+
"action:import": "Naimportovať",
|
|
85
|
+
"action:import-export": "Import/Export",
|
|
79
86
|
"action:install-app": "Inštalovať {name}",
|
|
80
87
|
"action:install-guest-tools": "Nainštalovať nástroje do hosťa",
|
|
81
88
|
"action:load-now": "Načítať teraz",
|
|
82
89
|
"action:log-out": "Odhlásiť sa",
|
|
83
90
|
"action:migrate": "Migrovať",
|
|
84
91
|
"action:migrate-n-vms": "Migrovať 1 virt. stroj | Migrovať {n} virt. strojov",
|
|
92
|
+
"action:migrate-vdi-on-sr": "Presťahovať na repozitár úložiska",
|
|
85
93
|
"action:move-filters-to-parent-group": "Presunúť filtre do nadradenej skupiny",
|
|
86
94
|
"action:open": "Otvoriť",
|
|
87
95
|
"action:open-app": "@:action:open {name}",
|
|
@@ -152,6 +160,8 @@
|
|
|
152
160
|
"allocated-space": "Pridelený priestor",
|
|
153
161
|
"allow": "Umožniť",
|
|
154
162
|
"allow-self-signed-ssl": "Je možné že bude potrebné vo vami využívanom prehliadači povoliť sami sebou podpísané SSL certifikáty",
|
|
163
|
+
"allowed-ips": "Umožnené IP adresy",
|
|
164
|
+
"allowed-ips-example": "Napríklad: 192.168.0.1; 2001:db8::1",
|
|
155
165
|
"and": "A",
|
|
156
166
|
"any-property": "Akákoľvek vlastnosť",
|
|
157
167
|
"api-error-details": "Podrobnosti o chybe rozhrania API",
|
|
@@ -220,6 +230,7 @@
|
|
|
220
230
|
"build-number": "Číslo zostavenia",
|
|
221
231
|
"by": "Od",
|
|
222
232
|
"bytes:gi": "GiB",
|
|
233
|
+
"bytes:kbs": "kB/s",
|
|
223
234
|
"bytes:ki": "KiB",
|
|
224
235
|
"bytes:mi": "MiB",
|
|
225
236
|
"cancel": "Zrušiť",
|
|
@@ -240,6 +251,7 @@
|
|
|
240
251
|
"community": "Komunita",
|
|
241
252
|
"community-name": "{name} komunita",
|
|
242
253
|
"compression": "Kompresia",
|
|
254
|
+
"compression-not-available": "Pre repozitárov úložiska z rovnakého fondu nie je kompresia k dispozícii.",
|
|
243
255
|
"concurrency": "Súbežnosť",
|
|
244
256
|
"configuration": "Konfigurácia",
|
|
245
257
|
"configure-for-protected": "Aby ste zaistili ochranu svojich virt. strojov, zvážte nastavenie {backup-job}",
|
|
@@ -282,6 +294,7 @@
|
|
|
282
294
|
"creating-new-network": "Vytváranie novej siete",
|
|
283
295
|
"creating-new-traffic-rule": "Vytvoriť nové pravidlo pre prevádzku",
|
|
284
296
|
"creating-new-vdi": "Vytvára sa nový obraz virt. disku",
|
|
297
|
+
"creating-new-vif": "Vytváranie nového virt. sieťového rozhrania",
|
|
285
298
|
"creation-date": "Dátum vytvorenia",
|
|
286
299
|
"cron-pattern": "Vzorec plánovania (cron)",
|
|
287
300
|
"current": "Súčasné",
|
|
@@ -306,6 +319,8 @@
|
|
|
306
319
|
"deploy-xoa-status:waiting": "Čaká sa až XOA odpovie…",
|
|
307
320
|
"descending": "zostupne",
|
|
308
321
|
"description": "Popis",
|
|
322
|
+
"destination-sr": "Cieľový repozitár úložiska",
|
|
323
|
+
"destination-sr-mandatory": "Je potrebné určiť cieľový repozitár úložiska.",
|
|
309
324
|
"device": "Zariadenie",
|
|
310
325
|
"device-config": "Konfigurácia zariadenia",
|
|
311
326
|
"dhcp": "DHCP",
|
|
@@ -332,6 +347,9 @@
|
|
|
332
347
|
"documentation-name": "Dokumentácia pre {name}",
|
|
333
348
|
"done": "Hotovo",
|
|
334
349
|
"drop": "Zahodiť",
|
|
350
|
+
"duplicate-vm:error-message": "Pri duplikovaní virt. stroje došlo k chybe. Skontrolujte nastavenia (názov, repozitár úložiska, kompresiu) a skúste to znova.",
|
|
351
|
+
"duplicating-vm": "Duplikovanie virt. stroje",
|
|
352
|
+
"duplication-method": "Metóda duplikácie",
|
|
335
353
|
"duration": "Trvanie",
|
|
336
354
|
"empty": "Prázdne",
|
|
337
355
|
"enabled": "Povolené",
|
|
@@ -352,6 +370,7 @@
|
|
|
352
370
|
"failed": "Neúspešné",
|
|
353
371
|
"failure": "Zlyhanie",
|
|
354
372
|
"fast-clone": "Rýchly klon",
|
|
373
|
+
"fast-clone-available": "Rýchly klon je k dispozícii iba pre zastavené virt. stroje.",
|
|
355
374
|
"fetching-fresh-data": "Získavanie aktuálnych údajov",
|
|
356
375
|
"field:exceeds-max-characters": "Je potrebné, aby zadanie v kolónke bolo dlhé najviac {max} znakov.",
|
|
357
376
|
"file": "Súbor",
|
|
@@ -369,6 +388,7 @@
|
|
|
369
388
|
"free-space": "Voľné miesto",
|
|
370
389
|
"free-space-on-sr": "Voľný priestor na repozitári úložiska",
|
|
371
390
|
"from-vm": "Od virt. stroje",
|
|
391
|
+
"full-copy": "Úplná kópia",
|
|
372
392
|
"fullscreen": "Celá obrazovka",
|
|
373
393
|
"gateway": "Prehod",
|
|
374
394
|
"general": "Všeobecné",
|
|
@@ -378,6 +398,7 @@
|
|
|
378
398
|
"graphics-display": "Grafika a zobrazenie",
|
|
379
399
|
"groups": "Skupiny",
|
|
380
400
|
"guest-tools": "Nástroje pre hostí",
|
|
401
|
+
"gzip": "GZIP",
|
|
381
402
|
"hardware": "Hardvér",
|
|
382
403
|
"hardware-specifications": "Hardvérové špecifikácie",
|
|
383
404
|
"heartbeat-storage-repository": "Repozitár úložiska kontroly stavu",
|
|
@@ -412,6 +433,9 @@
|
|
|
412
433
|
"interfaces": "Rozhrania",
|
|
413
434
|
"interrupted": "Prerušené",
|
|
414
435
|
"invalid-field": "Neplatné pole",
|
|
436
|
+
"invalid-ip-addresses": "Jedna alebo viac neplatných IP adries",
|
|
437
|
+
"invalid-mac-address": "Neplatná MAC adresa",
|
|
438
|
+
"invalid-rate-limit": "Obmedzovanie rýchlosti nemôže byť záporné",
|
|
415
439
|
"ip-address": "IP adresa",
|
|
416
440
|
"ip-address-info": "Pravidlo je možné uplatniť na jednotlivú IP adresu alebo podsieť (v CIDR formáte). Príklady: 192.168.1.10 alebo 10.20.0.0/16",
|
|
417
441
|
"ip-address-invalid": "Formát IP adresy nie je platný.",
|
|
@@ -455,6 +479,8 @@
|
|
|
455
479
|
"job:create:in-progress": "Prebieha vytváranie…",
|
|
456
480
|
"job:delete:in-progress": "Prebieha odstraňovanie…",
|
|
457
481
|
"job:disconnect:in-progress": "Prebieha odpájanie…",
|
|
482
|
+
"job:export:in-progress": "Prebieha export…",
|
|
483
|
+
"job:migrate:in-progress": "Sťahovanie prebieha…",
|
|
458
484
|
"job:network-delete:has-n-physical-pif-connected": "Sieť má pripojené jedno fyzické rozhranie | Sieť má pripojené {n} fyzické rozhrania | Sieť má pripojených {n} fyzických rozhraní",
|
|
459
485
|
"job:network-delete:has-n-vif-attached": "Sieť má pripojené jedno virtuálne rozhranie | Sieť má pripojená {n} virtuálne rozhranie | Sieť má pripojených {n} virtuálnych rozhraní",
|
|
460
486
|
"job:pbd-plug:missing-pbd": "Žiadne fyzické blokové zariadenia na pripojenie",
|
|
@@ -474,6 +500,12 @@
|
|
|
474
500
|
"job:vbd-disconnect:vbd-disconnected": "Virt. blokové zariadenie je odpojené",
|
|
475
501
|
"job:vdi-delete:missing-vdi": "Žiadne virt. disky na zmazanie",
|
|
476
502
|
"job:vdi-delete:vbd-attached": "Nie je možné zmazať virt. disk, ku ktorému je pripojené virtuálne blokové zariadenie",
|
|
503
|
+
"job:vdi-detach:in-progress": "Prebieha rušenie priloženia…",
|
|
504
|
+
"job:vdi-export:format-required": "Vyžaduje sa formát exportu obrazu virt. disku",
|
|
505
|
+
"job:vdi-export:missing-vdi": "Žiadny obraz virt. disku na export",
|
|
506
|
+
"job:vdi-migrate:in-progress": "Prebieha sťahovanie…",
|
|
507
|
+
"job:vdi-migrate:missing-sr": "Je potrebný repozitár úložiska",
|
|
508
|
+
"job:vdi-migrate:missing-vdi": "Nevybraný žiadny obraz virt. disku",
|
|
477
509
|
"job:vif-connect:missing-vif": "Žiadna virt. sieťové rozhrania na pripojenie",
|
|
478
510
|
"job:vif-connect:vif-already-connected": "Virt. sieťové rozhranie už je pripojené",
|
|
479
511
|
"job:vif-delete:blocked-operation": "Mazanie virt. sieťového rozhrania je blokované",
|
|
@@ -657,6 +689,8 @@
|
|
|
657
689
|
"new-vdi:add": "Pridať nový obraz virt. disku",
|
|
658
690
|
"new-vdi:error-message": "Pri pridávaní obrazu virt. disku došlo k chybe. Skontrolujte nastavenie (názov, repozitár úložiska, pridelený priestor) a skúste to znova.",
|
|
659
691
|
"new-vif": "Nové VIF",
|
|
692
|
+
"new-vif:add": "Pridať nové virt. sieťové rozhranie",
|
|
693
|
+
"new-vif:error-message": "Došlo k chybe pri vytváraní nového virt. sieťového rozhrania. Skontrolujte nastavenia a skúste to znova.",
|
|
660
694
|
"new-vm": "Nový VM",
|
|
661
695
|
"new-vm:add": "Pridať nový virt. stroj",
|
|
662
696
|
"new-vm:description": "Popis VM",
|
|
@@ -725,6 +759,7 @@
|
|
|
725
759
|
"operation:clean-reboot": "Reštartovanie",
|
|
726
760
|
"operation:clean-shutdown": "Vypínanie",
|
|
727
761
|
"operation:destroy": "Odstraňovanie",
|
|
762
|
+
"operation:duplicate": "Duplikovanie",
|
|
728
763
|
"operation:force-reboot": "Vynútené reštartovanie",
|
|
729
764
|
"operation:force-shutdown": "Vynútené vypínanie",
|
|
730
765
|
"operation:pause": "Pozastavenie",
|
|
@@ -741,6 +776,7 @@
|
|
|
741
776
|
"other": "Iné",
|
|
742
777
|
"other-properties": "Ostatné vlastnosti",
|
|
743
778
|
"other-settings": "Ďalšie nastavenia",
|
|
779
|
+
"ova": "OVA",
|
|
744
780
|
"page-not-found": "Táto stránka sa nedá nájsť…",
|
|
745
781
|
"page-please-wait": "Drž sa pevne, astronaut! Stránka sa pripravuje na start.{newline}Práve nastavujeme trysky a kalibrujeme systémy. Už to dlho nepotrvá, zostaňte naladení!",
|
|
746
782
|
"pagination:all": "@:all",
|
|
@@ -885,8 +921,9 @@
|
|
|
885
921
|
"see-details": "Zobraziť podrobnosti",
|
|
886
922
|
"select-compression": "Vyberte kompresiu",
|
|
887
923
|
"select-destination-host": "Vyberte cieľového hostiteľa",
|
|
924
|
+
"select-format": "Vyberte formát",
|
|
888
925
|
"select-host": "Vyberte hostiteľa",
|
|
889
|
-
"select-to-see-details": "
|
|
926
|
+
"select-to-see-details": "Podrobnosti si zobrazíte kliknutím na {icon}",
|
|
890
927
|
"selected-vms-in-execution": "Niektoré z vybraných virt. stroje sú spustené",
|
|
891
928
|
"self-signed-certificates": "Samy sebou podpísané certifikáty",
|
|
892
929
|
"send-us-feedback": "Pošlite nám spätnú väzbu",
|
|
@@ -927,6 +964,7 @@
|
|
|
927
964
|
"sr-disconnect-info-pool-mixed": "Lokálne repozitára úložiska budú odpojené od svojich príslušných hostiteľov. Zdieľané repozitáre budú odpojené od všetkých hostiteľov v tomto fonde.",
|
|
928
965
|
"sr-disconnect-info-pool-shared": "Repozitár úložiska bude odpojený od {hostsCount} hostiteľov v tomto fonde. | Každý zdieľaný repozitár úložiska bude odpojený od všetkých hostiteľov v tomto fonde.",
|
|
929
966
|
"sr-disconnect-title": "Chystáte sa odpojiť 1 repozitár úložiska | Chystáte sa odpojiť {n} repozitára úložiska | Chystáte sa odpojiť {n} repozitárov úložiska",
|
|
967
|
+
"sr-required": "Je potrebný repozitár úložiska.",
|
|
930
968
|
"ssh-account": "SSH účet",
|
|
931
969
|
"ssh-key": "SSH kľúč",
|
|
932
970
|
"ssh-login": "SSH prihlasovacie meno",
|
|
@@ -1022,6 +1060,8 @@
|
|
|
1022
1060
|
"traffic-rule": "Pravidlo pre prevádzku",
|
|
1023
1061
|
"traffic-rule-delete-warning": "Túto akciu nemožno vziať späť. Označené pravidlo pre prevádzku bude nevratne zmazané.",
|
|
1024
1062
|
"traffic-rules": "Pravidlá pre prevádzku",
|
|
1063
|
+
"traffic-rules:format-warning": "Chyba nastavenia: Pravidlá pre prevádzku nie sú v očakávanom formáte. {check-doc} ohľadom toho, ako ich previesť.",
|
|
1064
|
+
"traffic-rules:format-warning:check-doc": "Pozrite sa do najaktuálnejšej dokumentácie",
|
|
1025
1065
|
"traffic-rules:info-message": "Implementácia poradia pravidiel pre prevádzku ešte prebieha. Chovanie môže byť nebezpečné.",
|
|
1026
1066
|
"traffic-rules:no-traffic-rules-detected": "Nezjištěná žiadna pravidla pre prevádzku",
|
|
1027
1067
|
"transfer-size": "Objem prenosu",
|
|
@@ -1038,7 +1078,10 @@
|
|
|
1038
1078
|
"unable-to-create-new-network": "Nebolo možné vytvoriť novú sieť",
|
|
1039
1079
|
"unable-to-create-new-traffic-rule": "Nebolo možné vytvoriť nové pravidlo pre prevádzku",
|
|
1040
1080
|
"unable-to-create-new-vdi": "Nebolo možné vytvoriť nový obraz virt. disku",
|
|
1081
|
+
"unable-to-create-new-vif": "Nebolo možné vytvoriť nové virt. sieťové rozhranie",
|
|
1041
1082
|
"unable-to-delete-network": "Sieť nebolo možné zmazať",
|
|
1083
|
+
"unable-to-duplicate-vm": "Nebolo možné zduplikovať virt. stroj",
|
|
1084
|
+
"unable-to-migrate-vdi": "Nebolo možné presťahovať obraz virt. disku na repozitár úložiska",
|
|
1042
1085
|
"unknown": "Neznámo",
|
|
1043
1086
|
"unlocked": "Odomknuté",
|
|
1044
1087
|
"unnamed": "Bez názvu",
|
|
@@ -1072,6 +1115,7 @@
|
|
|
1072
1115
|
"vdi-in-use": "Obraz virt. disku už je využívaný",
|
|
1073
1116
|
"vdi-name": "Názov obrazu virt. disku",
|
|
1074
1117
|
"vdi-not-attached-to-vm": "Nepripojený žiadny virt. stroj",
|
|
1118
|
+
"vdi-on-different-sr-warning": "Tento repozitár úložiska je lokálny pre hostiteľa, ktorý nedrží ostatné disky virt. stroje. Virt. stroj nebude môcť naštartovať pokiaľ sa nebudú všetky jeho disky nachádzať na rovnakom hostiteľovi alebo na zdieľanom úložisku.",
|
|
1075
1119
|
"vdi-snapshot-source": "Zdroj obrazu virt. disku",
|
|
1076
1120
|
"vdi-snpashot-time": "Dátum zachytenia stavu",
|
|
1077
1121
|
"vdi-throughput": "Objemy čítania/zápisov na virt. disky",
|
|
@@ -1102,6 +1146,8 @@
|
|
|
1102
1146
|
"vm-is-running": "Virt. stroj je spustený",
|
|
1103
1147
|
"vm-limit-topology": "Limit topológie virt. stroje",
|
|
1104
1148
|
"vm-management": "Správa VM",
|
|
1149
|
+
"vm-name": "Názov virt. stroje",
|
|
1150
|
+
"vm-name-required": "Je potrebný názov virt. stroje.",
|
|
1105
1151
|
"vm-not-running": "Virt. stroj nie je spustený",
|
|
1106
1152
|
"vm-off": "Tento virtuálny stroj je vypnutý.",
|
|
1107
1153
|
"vm-protected-deletion": "Tento virtuálny počítač je momentálne chránený pred vymazaním.",
|
|
@@ -1156,5 +1202,7 @@
|
|
|
1156
1202
|
"xoa-password-confirm-different": "Heslo do XOA nebolo zadané dvakrát rovnako",
|
|
1157
1203
|
"xoa-ssh-account": "Účet pre SSH prístup do XOA",
|
|
1158
1204
|
"xoa-ssh-password-confirm-different": "Heslo pre SSH nebolo zadané dvakrát rovnako",
|
|
1159
|
-
"
|
|
1205
|
+
"xva": "XVA",
|
|
1206
|
+
"you-are-currently-on": "V tejto chvíli sa nachádzate na: {0}",
|
|
1207
|
+
"zstd": "ZSTD"
|
|
1160
1208
|
}
|
|
@@ -163,12 +163,13 @@ An event that is _not_ declared in `useOverlay` can also be handled at open time
|
|
|
163
163
|
|
|
164
164
|
## Aborted overlays
|
|
165
165
|
|
|
166
|
-
An overlay
|
|
166
|
+
An overlay is not tied to the lifecycle of the component that opened it: it stays open until the user makes a decision, or until it is aborted. An overlay can be aborted in three ways:
|
|
167
167
|
|
|
168
|
-
-
|
|
169
|
-
-
|
|
168
|
+
- `open()` is called again on the same `useOverlay` instance while the overlay is still open (the previous one is replaced),
|
|
169
|
+
- the `abort()` function returned by `useOverlay` is called,
|
|
170
|
+
- or the store's `abortAll()` is called (see below).
|
|
170
171
|
|
|
171
|
-
In
|
|
172
|
+
In all cases the promise resolves with `OVERLAY_ABORT_EVENT` (a symbol) as its `event`, and no handler runs:
|
|
172
173
|
|
|
173
174
|
```ts
|
|
174
175
|
const response = await open()
|
|
@@ -180,6 +181,10 @@ if (response.event === OVERLAY_ABORT_EVENT) {
|
|
|
180
181
|
|
|
181
182
|
The `OVERLAY_ABORT_EVENT` case is always part of the response union, so exhaustive `event` checks will remind you it exists.
|
|
182
183
|
|
|
184
|
+
Aborting an overlay whose decision is already being resolved is a no-op: the promise resolves with the actual response.
|
|
185
|
+
|
|
186
|
+
An overlay can also be aborted while one of its handlers is still awaiting (for example, navigating away while a save is in progress). In that case the overlay closes right away with `OVERLAY_ABORT_EVENT`. The handler's async work keeps running — it cannot be cancelled — but the value it eventually returns is ignored, and the caller sees an abort.
|
|
187
|
+
|
|
183
188
|
## Busy and disabled triggers
|
|
184
189
|
|
|
185
190
|
Inside the overlay component, use `useOverlayTrigger()` to wire the controls that emit events (buttons, typically) to the overlay's state:
|
|
@@ -283,3 +288,13 @@ Opened overlays live in a Pinia store (`useOverlayStore`). The app must render t
|
|
|
283
288
|
```
|
|
284
289
|
|
|
285
290
|
Styling and animations are up to you. The store also exposes `isCurrent(key)` to style stacked overlays (e.g. dim the ones below the topmost), and each `overlay` carries its current `status`.
|
|
291
|
+
|
|
292
|
+
Since overlays are not tied to the component that opened them, dismissing them on navigation is the app's decision. If that's the behavior you want, wire it once on the router:
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
router.isReady().then(() => {
|
|
296
|
+
router.afterEach(() => useOverlayStore().abortAll())
|
|
297
|
+
})
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Register the guard only once the router is ready: the initial navigation also triggers `afterEach`, which would abort any overlay opened while the app was starting up.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { isThenable } from '@core/packages/overlay/is-thenable.ts'
|
|
2
2
|
import { KEEP_OVERLAY_OPEN } from '@core/packages/overlay/symbols.ts'
|
|
3
|
-
import type { Overlay, OverlayEventHandler, OverlayResponse } from '@core/packages/overlay/types.ts'
|
|
4
|
-
import { nextTick } from 'vue'
|
|
3
|
+
import type { Overlay, OverlayEventHandler, OverlayResponse, OverlayStatus } from '@core/packages/overlay/types.ts'
|
|
5
4
|
|
|
6
5
|
export function createEventHandler({
|
|
7
6
|
overlay,
|
|
@@ -12,7 +11,7 @@ export function createEventHandler({
|
|
|
12
11
|
overlay: Overlay
|
|
13
12
|
definitionEvents: Record<string, true | OverlayEventHandler>
|
|
14
13
|
openEvents: Record<string, OverlayEventHandler>
|
|
15
|
-
settle: (response: OverlayResponse<PropertyKey, unknown>) => void
|
|
14
|
+
settle: (response: OverlayResponse<PropertyKey, unknown>) => Promise<void>
|
|
16
15
|
}) {
|
|
17
16
|
const shouldStop = (payload: unknown) => payload === KEEP_OVERLAY_OPEN || overlay.status === 'settled'
|
|
18
17
|
|
|
@@ -32,7 +31,7 @@ export function createEventHandler({
|
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
try {
|
|
35
|
-
overlay.status = 'locked'
|
|
34
|
+
overlay.status = 'locked' as OverlayStatus
|
|
36
35
|
|
|
37
36
|
// The definition handler receives the raw emit arguments, then the open
|
|
38
37
|
// handler receives the payload it produced. An event handled at open
|
|
@@ -62,13 +61,7 @@ export function createEventHandler({
|
|
|
62
61
|
}
|
|
63
62
|
}
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// The leave transition freezes the DOM in its last painted state,
|
|
68
|
-
// so paint a non-busy frame before removing the overlay
|
|
69
|
-
await nextTick()
|
|
70
|
-
|
|
71
|
-
settle({ event: eventName, payload })
|
|
64
|
+
await settle({ event: eventName, payload })
|
|
72
65
|
} finally {
|
|
73
66
|
if (overlay.status !== 'settled') {
|
|
74
67
|
overlay.status = 'idle'
|
|
@@ -15,6 +15,10 @@ export const useOverlayStore = defineStore('overlay', () => {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
function abortAll() {
|
|
19
|
+
registry.value.forEach(overlay => overlay.abort())
|
|
20
|
+
}
|
|
21
|
+
|
|
18
22
|
function isCurrent(key: symbol): boolean {
|
|
19
23
|
return Array.from(registry.value.keys()).at(-1) === key
|
|
20
24
|
}
|
|
@@ -27,6 +31,7 @@ export const useOverlayStore = defineStore('overlay', () => {
|
|
|
27
31
|
overlays: computed(() => Array.from(registry.value.values())),
|
|
28
32
|
register,
|
|
29
33
|
unregister,
|
|
34
|
+
abortAll,
|
|
30
35
|
isCurrent,
|
|
31
36
|
get,
|
|
32
37
|
}
|
|
@@ -13,7 +13,7 @@ import type {
|
|
|
13
13
|
} from '@core/packages/overlay/types.ts'
|
|
14
14
|
import { useOverlayStore } from '@core/packages/overlay/use-overlay-store.ts'
|
|
15
15
|
import { reactiveComputed } from '@vueuse/core'
|
|
16
|
-
import { defineAsyncComponent, markRaw,
|
|
16
|
+
import { defineAsyncComponent, markRaw, nextTick, reactive, type AsyncComponentLoader, type Component } from 'vue'
|
|
17
17
|
import type { ComponentProps } from 'vue-component-type-helpers'
|
|
18
18
|
|
|
19
19
|
export function useOverlay<TComponent extends Component, const TEvents extends OverlayEvents<TComponent>>({
|
|
@@ -29,9 +29,11 @@ export function useOverlay<TComponent extends Component, const TEvents extends O
|
|
|
29
29
|
|
|
30
30
|
const component = defineAsyncComponent(componentLoader)
|
|
31
31
|
|
|
32
|
-
let
|
|
32
|
+
let currentOverlay: Overlay | undefined
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
function abort() {
|
|
35
|
+
currentOverlay?.abort()
|
|
36
|
+
}
|
|
35
37
|
|
|
36
38
|
function open<TOpenEvents extends OpenEvents<TComponent, TEvents>>(
|
|
37
39
|
...args: MaybeOptionalArgs<
|
|
@@ -45,8 +47,7 @@ export function useOverlay<TComponent extends Component, const TEvents extends O
|
|
|
45
47
|
) {
|
|
46
48
|
const { events: openEvents, props } = args[0] ?? {}
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
abortCurrent?.()
|
|
50
|
+
abort()
|
|
50
51
|
|
|
51
52
|
return new Promise<ExtractOverlayResponse<TComponent, TEvents, TOpenEvents>>(resolve => {
|
|
52
53
|
const definitionEvents = events as Record<string, true | OverlayEventHandler>
|
|
@@ -60,16 +61,30 @@ export function useOverlay<TComponent extends Component, const TEvents extends O
|
|
|
60
61
|
props: {},
|
|
61
62
|
status: 'idle',
|
|
62
63
|
lastTrigger: undefined,
|
|
64
|
+
abort: () => settle({ event: OVERLAY_ABORT_EVENT, payload: undefined }),
|
|
63
65
|
})
|
|
64
66
|
|
|
65
|
-
|
|
67
|
+
currentOverlay = overlay
|
|
68
|
+
|
|
69
|
+
async function settle(response: ExtractOverlayResponse<TComponent, TEvents, TOpenEvents>) {
|
|
70
|
+
if (overlay.status === 'settled') {
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
66
74
|
overlay.status = 'settled'
|
|
67
|
-
|
|
75
|
+
|
|
76
|
+
// The `leave` transition freezes the DOM in its last painted state,
|
|
77
|
+
// so paint a non-busy frame before removing the overlay
|
|
78
|
+
await nextTick()
|
|
79
|
+
|
|
68
80
|
resolve(response)
|
|
81
|
+
|
|
69
82
|
overlayStore.unregister(overlay)
|
|
70
|
-
}
|
|
71
83
|
|
|
72
|
-
|
|
84
|
+
if (currentOverlay === overlay) {
|
|
85
|
+
currentOverlay = undefined
|
|
86
|
+
}
|
|
87
|
+
}
|
|
73
88
|
|
|
74
89
|
const handleEvent = createEventHandler({
|
|
75
90
|
overlay,
|
|
@@ -95,5 +110,5 @@ export function useOverlay<TComponent extends Component, const TEvents extends O
|
|
|
95
110
|
})
|
|
96
111
|
}
|
|
97
112
|
|
|
98
|
-
return { open }
|
|
113
|
+
return { open, abort }
|
|
99
114
|
}
|
|
@@ -46,7 +46,7 @@ export function defineRemoteResource<
|
|
|
46
46
|
receivedData: any,
|
|
47
47
|
calledFrom: 'execute' | 'update',
|
|
48
48
|
context?: ResourceContext<TArgs>
|
|
49
|
-
) => void
|
|
49
|
+
) => Promise<void>
|
|
50
50
|
cacheExpirationMs?: number | false
|
|
51
51
|
pollingIntervalMs?: number | false
|
|
52
52
|
stream?: boolean
|
|
@@ -60,7 +60,7 @@ export function defineRemoteResource<TData, TState extends object, TArgs extends
|
|
|
60
60
|
receivedData: any,
|
|
61
61
|
calledFrom: 'execute' | 'update',
|
|
62
62
|
context?: ResourceContext<TArgs>
|
|
63
|
-
) => void
|
|
63
|
+
) => Promise<void>
|
|
64
64
|
cacheExpirationMs?: number | false
|
|
65
65
|
pollingIntervalMs?: number | false
|
|
66
66
|
stream?: boolean
|
|
@@ -79,8 +79,8 @@ export function defineRemoteResource<
|
|
|
79
79
|
receivedData: any,
|
|
80
80
|
calledFrom: 'execute' | 'update',
|
|
81
81
|
context?: ResourceContext<TArgs>
|
|
82
|
-
) => void
|
|
83
|
-
onDataRemoved?: (data: Ref<NoInfer<TData>>, receivedData: any) => void
|
|
82
|
+
) => Promise<void>
|
|
83
|
+
onDataRemoved?: (data: Ref<NoInfer<TData>>, receivedData: any) => Promise<void>
|
|
84
84
|
stream?: boolean
|
|
85
85
|
initWatchCollection: () => {
|
|
86
86
|
collectionId: string
|
|
@@ -89,7 +89,7 @@ export function defineRemoteResource<
|
|
|
89
89
|
handleDelete: THandleDelete
|
|
90
90
|
handlePost: THandlePost
|
|
91
91
|
handleWatching: THandleWatching
|
|
92
|
-
predicate?: (receivedData: TData, context: ResourceContext<TArgs> | undefined) => boolean
|
|
92
|
+
predicate?: (receivedData: TData, context: ResourceContext<TArgs> | undefined) => Promise<boolean> | boolean
|
|
93
93
|
}
|
|
94
94
|
}): UseRemoteResource<TState, TArgs>
|
|
95
95
|
|
|
@@ -106,8 +106,8 @@ export function defineRemoteResource<
|
|
|
106
106
|
receivedData: any,
|
|
107
107
|
calledFrom: 'execute' | 'update',
|
|
108
108
|
context?: ResourceContext<TArgs>
|
|
109
|
-
) => void
|
|
110
|
-
onDataRemoved?: (data: Ref<NoInfer<TData>>, receivedData: any) => void
|
|
109
|
+
) => Promise<void>
|
|
110
|
+
onDataRemoved?: (data: Ref<NoInfer<TData>>, receivedData: any) => Promise<void>
|
|
111
111
|
cacheExpirationMs?: number | false
|
|
112
112
|
pollingIntervalMs?: number | false
|
|
113
113
|
stream?: boolean
|
|
@@ -118,15 +118,18 @@ export function defineRemoteResource<
|
|
|
118
118
|
handleDelete: THandleDelete
|
|
119
119
|
handlePost: THandlePost
|
|
120
120
|
handleWatching: THandleWatching
|
|
121
|
-
predicate?: (receivedData: TData, context: ResourceContext<TArgs> | undefined) => boolean
|
|
121
|
+
predicate?: (receivedData: TData, context: ResourceContext<TArgs> | undefined) => Promise<boolean> | boolean
|
|
122
122
|
}
|
|
123
123
|
}) {
|
|
124
124
|
const cache = new Map<
|
|
125
125
|
string,
|
|
126
126
|
{
|
|
127
127
|
count: number
|
|
128
|
+
evictionTimeout?: NodeJS.Timeout // TODO : Fixme , the actual type in browser is number, but NodeJS.Timeout in nodejs.
|
|
129
|
+
// Due to transitive types of vates/types, the type is NodeJS.Timeout.
|
|
128
130
|
pause: VoidFunction
|
|
129
131
|
resume: VoidFunction
|
|
132
|
+
isPaused: boolean
|
|
130
133
|
state: object
|
|
131
134
|
stateScope: EffectScope
|
|
132
135
|
isReady: Ref<boolean>
|
|
@@ -181,9 +184,14 @@ export function defineRemoteResource<
|
|
|
181
184
|
|
|
182
185
|
const onDataReceived =
|
|
183
186
|
config.onDataReceived ??
|
|
184
|
-
((
|
|
187
|
+
(async (
|
|
188
|
+
data: Ref<TData>,
|
|
189
|
+
receivedData: any,
|
|
190
|
+
calledFrom: 'execute' | 'update',
|
|
191
|
+
context?: ResourceContext<TArgs>
|
|
192
|
+
) => {
|
|
185
193
|
// allow to ignore some update (like for sub collection. E.g. vms/:id/vdis)
|
|
186
|
-
if (watchCollection?.predicate?.(receivedData, context) === false) {
|
|
194
|
+
if ((await watchCollection?.predicate?.(receivedData, context)) === false) {
|
|
187
195
|
return
|
|
188
196
|
}
|
|
189
197
|
|
|
@@ -217,9 +225,9 @@ export function defineRemoteResource<
|
|
|
217
225
|
|
|
218
226
|
const onDataRemoved =
|
|
219
227
|
config.onDataRemoved ??
|
|
220
|
-
((data: Ref<TData>, receivedData: any, context?: ResourceContext<TArgs>) => {
|
|
228
|
+
(async (data: Ref<TData>, receivedData: any, context?: ResourceContext<TArgs>) => {
|
|
221
229
|
// allow to ignore some update (like for sub collection. E.g. vms/:id/vdis)
|
|
222
|
-
if (watchCollection?.predicate?.(receivedData, context) === false) {
|
|
230
|
+
if ((await watchCollection?.predicate?.(receivedData, context)) === false) {
|
|
223
231
|
return
|
|
224
232
|
}
|
|
225
233
|
|
|
@@ -246,7 +254,12 @@ export function defineRemoteResource<
|
|
|
246
254
|
entry.count += 1
|
|
247
255
|
|
|
248
256
|
if (entry.count === 1) {
|
|
249
|
-
entry.
|
|
257
|
+
clearTimeout(entry.evictionTimeout)
|
|
258
|
+
entry.evictionTimeout = undefined
|
|
259
|
+
if (entry.isPaused) {
|
|
260
|
+
entry.resume()
|
|
261
|
+
entry.isPaused = false
|
|
262
|
+
}
|
|
250
263
|
}
|
|
251
264
|
}
|
|
252
265
|
|
|
@@ -263,13 +276,21 @@ export function defineRemoteResource<
|
|
|
263
276
|
return
|
|
264
277
|
}
|
|
265
278
|
|
|
266
|
-
|
|
279
|
+
function cleanUp() {
|
|
280
|
+
if (entry === undefined) {
|
|
281
|
+
return
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
entry.pause()
|
|
285
|
+
entry.isPaused = true
|
|
286
|
+
entry.stateScope.stop()
|
|
287
|
+
cache.delete(url)
|
|
288
|
+
}
|
|
267
289
|
|
|
268
290
|
if (cacheExpiration !== false) {
|
|
269
|
-
setTimeout(
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}, cacheExpiration)
|
|
291
|
+
entry.evictionTimeout = setTimeout(cleanUp, cacheExpiration)
|
|
292
|
+
} else {
|
|
293
|
+
cleanUp()
|
|
273
294
|
}
|
|
274
295
|
}
|
|
275
296
|
|
|
@@ -285,6 +306,8 @@ export function defineRemoteResource<
|
|
|
285
306
|
const lastError = ref<Error>()
|
|
286
307
|
|
|
287
308
|
const hasError = computed(() => lastError.value !== undefined)
|
|
309
|
+
const stateScope = effectScope(true)
|
|
310
|
+
const sharedContext = { ...context, scope: stateScope }
|
|
288
311
|
|
|
289
312
|
const data = shallowRef(buildData()) as Ref<TData>
|
|
290
313
|
// trigger reactivity on data when no more updates since 100ms or after 500ms
|
|
@@ -322,7 +345,7 @@ export function defineRemoteResource<
|
|
|
322
345
|
}
|
|
323
346
|
|
|
324
347
|
for await (const event of readNDJSONStream(response.body)) {
|
|
325
|
-
onDataReceived(data, event, 'execute',
|
|
348
|
+
await onDataReceived(data, event, 'execute', sharedContext)
|
|
326
349
|
void flushData()
|
|
327
350
|
}
|
|
328
351
|
|
|
@@ -332,7 +355,7 @@ export function defineRemoteResource<
|
|
|
332
355
|
void flushData()
|
|
333
356
|
}
|
|
334
357
|
} else {
|
|
335
|
-
onDataReceived(data, await response.json(), 'execute',
|
|
358
|
+
await onDataReceived(data, await response.json(), 'execute', sharedContext)
|
|
336
359
|
void flushData()
|
|
337
360
|
}
|
|
338
361
|
|
|
@@ -358,12 +381,12 @@ export function defineRemoteResource<
|
|
|
358
381
|
handleWatching,
|
|
359
382
|
handlePost,
|
|
360
383
|
resource,
|
|
361
|
-
onDataReceived: receivedData => {
|
|
362
|
-
onDataReceived(data, receivedData, 'update',
|
|
384
|
+
onDataReceived: async receivedData => {
|
|
385
|
+
await onDataReceived(data, receivedData, 'update', sharedContext)
|
|
363
386
|
void flushData()
|
|
364
387
|
},
|
|
365
|
-
onDataRemoved: receivedData => {
|
|
366
|
-
onDataRemoved(data, receivedData,
|
|
388
|
+
onDataRemoved: async receivedData => {
|
|
389
|
+
await onDataRemoved(data, receivedData, sharedContext)
|
|
367
390
|
void flushData()
|
|
368
391
|
},
|
|
369
392
|
})
|
|
@@ -379,13 +402,13 @@ export function defineRemoteResource<
|
|
|
379
402
|
resume = timeoutPoll.resume
|
|
380
403
|
}
|
|
381
404
|
|
|
382
|
-
const
|
|
383
|
-
const state = stateScope.run(() => buildState(data, context))!
|
|
405
|
+
const state = stateScope.run(() => buildState(data, sharedContext))!
|
|
384
406
|
|
|
385
407
|
cache.set(url, {
|
|
386
408
|
count: 0,
|
|
387
409
|
pause,
|
|
388
410
|
resume,
|
|
411
|
+
isPaused: true,
|
|
389
412
|
state,
|
|
390
413
|
stateScope,
|
|
391
414
|
isReady,
|
|
@@ -423,6 +446,12 @@ export function defineRemoteResource<
|
|
|
423
446
|
throw new Error('No effect scope found. Please provide a scope or use this function within a Vue component.')
|
|
424
447
|
}
|
|
425
448
|
|
|
449
|
+
if (!scope.active) {
|
|
450
|
+
throw new Error(
|
|
451
|
+
'useRemoteResource() was called with an inactive effect scope. This usually means a stale scope/context, (e.g. captured from an unmounted component) is being reused'
|
|
452
|
+
)
|
|
453
|
+
}
|
|
454
|
+
|
|
426
455
|
const isEnabled = toRef(optionsOrParentContext?.isEnabled ?? true)
|
|
427
456
|
|
|
428
457
|
return scope.run(() => {
|
|
@@ -2,7 +2,7 @@ import { useNow } from '@vueuse/core'
|
|
|
2
2
|
import { defineStore } from 'pinia'
|
|
3
3
|
import { computed, ref, watch as watchVue } from 'vue'
|
|
4
4
|
|
|
5
|
-
type EventFn = (object: unknown) => void
|
|
5
|
+
type EventFn = (object: unknown) => Promise<void>
|
|
6
6
|
export type THandlePost = (sseId: string) => Promise<string>
|
|
7
7
|
export type THandleDelete = (sseId: string, subscriptionId: string) => Promise<void>
|
|
8
8
|
export type THandleWatching = (
|
|
@@ -13,9 +13,9 @@ export type THandleWatching = (
|
|
|
13
13
|
configs: Record<
|
|
14
14
|
string,
|
|
15
15
|
{
|
|
16
|
-
add:
|
|
17
|
-
update:
|
|
18
|
-
remove:
|
|
16
|
+
add: EventFn
|
|
17
|
+
update: EventFn
|
|
18
|
+
remove: EventFn
|
|
19
19
|
}
|
|
20
20
|
>
|
|
21
21
|
}
|