11<script lang="ts">
22import { defineComponent } from " vue" ;
3- import { importSources , ImportSource } from " ../ts/importSources" ;
3+ import { ImportSource } from " ../ts/importSources" ;
4+ import ImportModal from " ./ImportModal.vue" ;
45import ImportUrlModal from " ./ImportUrlModal.vue" ;
56
6- // Split button on the index page: the primary action creates a new empty
7- // course, the dropdown lists every registered import source. New sources are
8- // picked up automatically from the importSources registry.
7+ // "New" button on the index page. The button opens a card chooser (ImportModal)
8+ // that lists every registered import source — including the blank-document
9+ // option. Picking a card dispatches here: navigate runs immediately, fileInput
10+ // triggers the hidden <input>, and modal opens the shared text-input dialog.
911export default defineComponent ({
1012 name: " ImportMenu" ,
1113
12- components: { ImportUrlModal },
14+ components: { ImportModal , ImportUrlModal },
1315
1416 data() {
1517 return {
16- open: false ,
17- sources: importSources ,
18- primary: importSources .find ((s ) => s .id === " new" ) as ImportSource ,
1918 acceptFor: " " as string ,
2019 pendingOnFiles: null as ((files : FileList ) => Promise <void >) | null ,
2120 busy: false ,
2221 };
2322 },
2423
25- computed: {
26- extraSources(): ImportSource [] {
27- return this .sources .filter (
28- (s ) => s .id !== " new" && (s .available ? s .available () : true )
29- );
24+ methods: {
25+ openChooser() {
26+ (this .$refs .chooser as any )?.open ();
3027 },
31- },
3228
33- methods: {
3429 select(source : ImportSource ) {
35- this .open = false ;
3630 if (source .kind === " navigate" ) {
3731 source .onSelect ?.();
3832 } else if (source .kind === " fileInput" ) {
3933 this .acceptFor = source .accept || " " ;
4034 this .pendingOnFiles = source .onFiles || null ;
4135 this .$nextTick (() => (this .$refs .file as HTMLInputElement )?.click ());
4236 } else if (source .kind === " modal" ) {
43- (this .$refs .modal as any )?.open (source );
37+ // The chooser modal is closing; wait a tick so its backdrop is gone
38+ // before the text-input modal opens.
39+ this .$nextTick (() => (this .$refs .modal as any )?.open (source ));
4440 }
4541 },
4642
@@ -56,48 +52,22 @@ export default defineComponent({
5652 }
5753 input .value = " " ;
5854 },
59-
60- onOutsideClick(e : MouseEvent ) {
61- if (! (this .$el as HTMLElement ).contains (e .target as Node )) {
62- this .open = false ;
63- }
64- },
65- },
66-
67- mounted() {
68- document .addEventListener (" click" , this .onOutsideClick );
69- },
70-
71- beforeUnmount() {
72- document .removeEventListener (" click" , this .onOutsideClick );
7355 },
7456});
7557 </script >
7658
7759<template >
78- <div class =" btn-group" :class =" { show: open }" >
79- <button class =" btn btn-primary" type =" button" :disabled =" busy" @click =" select(primary)" >
80- <span v-if =" busy" class =" spinner-border spinner-border-sm me-1" ></span >
81- <i v-else class =" bi" :class =" primary.icon" ></i >
82- {{ $t(primary.labelKey) }}
83- </button >
60+ <div >
8461 <button
85- class =" btn btn-primary dropdown-toggle dropdown-toggle-split "
62+ class =" btn btn-primary"
8663 type =" button"
87- :aria-expanded =" open"
88- :aria-label =" $t('index.import.more')"
89- @click.stop =" open = !open"
90- ></button >
91- <ul class =" dropdown-menu dropdown-menu-end" :class =" { show: open }" >
92- <li >
93- <h6 class =" dropdown-header" >{{ $t('index.import.heading') }}</h6 >
94- </li >
95- <li v-for =" source in extraSources" :key =" source.id" >
96- <button class =" dropdown-item" type =" button" @click =" select(source)" >
97- <i class =" bi me-2" :class =" source.icon" ></i >{{ $t(source.labelKey) }}
98- </button >
99- </li >
100- </ul >
64+ :disabled =" busy"
65+ @click =" openChooser"
66+ >
67+ <span v-if =" busy" class =" spinner-border spinner-border-sm me-1" ></span >
68+ <i v-else class =" bi bi-file-earmark-plus-fill" ></i >
69+ {{ $t("index.import.new") }}
70+ </button >
10171
10272 <input
10373 ref =" file"
@@ -107,15 +77,7 @@ export default defineComponent({
10777 @change =" onFileChange"
10878 />
10979
80+ <ImportModal ref="chooser" @select =" select " />
11081 <ImportUrlModal ref="modal" />
11182 </div >
11283</template >
113-
114- <style scoped>
115- /* We toggle the menu manually (no Bootstrap Popper), so dropdown-menu-end has
116- no effect — force right alignment so the menu opens inside the viewport. */
117- .dropdown-menu {
118- right : 0 ;
119- left : auto ;
120- }
121- </style >
0 commit comments