6
6
:type =" 'Dropdown'"
7
7
:windowEdgeMargin =" 0"
8
8
:escapeCloses =" false"
9
- v-bind =" { direction, scrollableY, minWidth }"
9
+ v-bind =" { direction, scrollableY: scrollableY && virtualScrollingEntryHeight === 0 , minWidth }"
10
10
ref =" floatingMenu"
11
11
data-hover-menu-keep-open
12
12
>
13
- <template v-for =" (section , sectionIndex ) in entries " :key =" sectionIndex " >
14
- <Separator :type =" 'List'" :direction =" 'Vertical'" v-if =" sectionIndex > 0" />
13
+ <!-- If we put the scrollableY on the layoutcol for non-font dropdowns then for some reason it always creates a tiny scrollbar.
14
+ However when we are using the virtual scrolling then we need the layoutcol to be scrolling so we can bind the events without using $refs. -->
15
+ <LayoutCol ref =" scroller" :scrollableY =" scrollableY && virtualScrollingEntryHeight !== 0" @scroll =" onScroll" :style =" { minWidth: virtualScrollingEntryHeight ? `${minWidth}px` : `inherit` }" >
16
+ <LayoutRow v-if =" virtualScrollingEntryHeight" class =" scroll-spacer" :style =" { height: `${virtualScrollingStartIndex * virtualScrollingEntryHeight}px` }" ></LayoutRow >
17
+ <template v-for =" (section , sectionIndex ) in entries " :key =" sectionIndex " >
18
+ <Separator :type =" 'List'" :direction =" 'Vertical'" v-if =" sectionIndex > 0" />
19
+ <LayoutRow
20
+ v-for =" (entry, entryIndex) in virtualScrollingEntryHeight ? section.slice(virtualScrollingStartIndex, virtualScrollingEndIndex) : section"
21
+ :key =" entryIndex + (virtualScrollingEntryHeight ? virtualScrollingStartIndex : 0)"
22
+ class =" row"
23
+ :class =" { open: isEntryOpen(entry), active: entry.label === highlighted?.label }"
24
+ :style =" { height: virtualScrollingEntryHeight || '20px' }"
25
+ @click =" () => onEntryClick(entry)"
26
+ @pointerenter =" () => onEntryPointerEnter(entry)"
27
+ @pointerleave =" () => onEntryPointerLeave(entry)"
28
+ >
29
+ <CheckboxInput v-if =" entry.checkbox" v-model:checked =" entry.checked" :outlineStyle =" true" :disableTabIndex =" true" class =" entry-checkbox" />
30
+ <IconLabel v-else-if =" entry.icon && drawIcon" :icon =" entry.icon" class =" entry-icon" />
31
+ <div v-else-if =" drawIcon" class =" no-icon" ></div >
32
+
33
+ <link v-if =" entry.font" rel =" stylesheet" :href =" entry.font?.toString()" />
34
+
35
+ <span class =" entry-label" :style =" { fontFamily: `${!entry.font ? 'inherit' : entry.value}` }" >{{ entry.label }}</span >
36
+
37
+ <IconLabel v-if =" entry.shortcutRequiresLock && !fullscreen.state.keyboardLocked" :icon =" 'Info'" :title =" keyboardLockInfoMessage" />
38
+ <UserInputLabel v-else-if =" entry.shortcut?.length" :inputKeys =" [entry.shortcut]" />
39
+
40
+ <div class =" submenu-arrow" v-if =" entry.children?.length" ></div >
41
+ <div class =" no-submenu-arrow" v-else ></div >
42
+
43
+ <MenuList
44
+ v-if =" entry.children"
45
+ @naturalWidth =" (newNaturalWidth: number) => $emit('naturalWidth', newNaturalWidth)"
46
+ :open =" entry.ref?.open || false"
47
+ :direction =" 'TopRight'"
48
+ :entries =" entry.children"
49
+ v-bind =" { defaultAction, minWidth, drawIcon, scrollableY }"
50
+ :ref =" (ref: typeof FloatingMenu) => ref && (entry.ref = ref)"
51
+ />
52
+ </LayoutRow >
53
+ </template >
15
54
<LayoutRow
16
- v-for =" (entry, entryIndex) in section"
17
- :key =" entryIndex"
18
- class =" row"
19
- :class =" { open: isEntryOpen(entry), active: entry.label === highlighted?.label }"
20
- @click =" () => onEntryClick(entry)"
21
- @pointerenter =" () => onEntryPointerEnter(entry)"
22
- @pointerleave =" () => onEntryPointerLeave(entry)"
23
- >
24
- <CheckboxInput v-if =" entry.checkbox" v-model:checked =" entry.checked" :outlineStyle =" true" :disableTabIndex =" true" class =" entry-checkbox" />
25
- <IconLabel v-else-if =" entry.icon && drawIcon" :icon =" entry.icon" class =" entry-icon" />
26
- <div v-else-if =" drawIcon" class =" no-icon" ></div >
27
-
28
- <span class =" entry-label" >{{ entry.label }}</span >
29
-
30
- <IconLabel v-if =" entry.shortcutRequiresLock && !fullscreen.state.keyboardLocked" :icon =" 'Info'" :title =" keyboardLockInfoMessage" />
31
- <UserInputLabel v-else-if =" entry.shortcut?.length" :inputKeys =" [entry.shortcut]" />
32
-
33
- <div class =" submenu-arrow" v-if =" entry.children?.length" ></div >
34
- <div class =" no-submenu-arrow" v-else ></div >
35
-
36
- <MenuList
37
- v-if =" entry.children"
38
- @naturalWidth =" (newNaturalWidth: number) => $emit('naturalWidth', newNaturalWidth)"
39
- :open =" entry.ref?.open || false"
40
- :direction =" 'TopRight'"
41
- :entries =" entry.children"
42
- v-bind =" { defaultAction, minWidth, drawIcon, scrollableY }"
43
- :ref =" (ref: typeof FloatingMenu) => ref && (entry.ref = ref)"
44
- />
45
- </LayoutRow >
46
- </template >
55
+ v-if =" virtualScrollingEntryHeight"
56
+ class =" scroll-spacer"
57
+ :style =" { height: `${virtualScrollingTotalHeight - virtualScrollingEndIndex * virtualScrollingEntryHeight}px` }"
58
+ ></LayoutRow >
59
+ </LayoutCol >
47
60
</FloatingMenu >
48
61
</template >
49
62
52
65
.floating-menu-container .floating-menu-content {
53
66
padding : 4px 0 ;
54
67
68
+ .scroll-spacer {
69
+ flex : 0 0 auto ;
70
+ }
71
+
55
72
.row {
56
73
height : 20px ;
57
74
align-items : center ;
@@ -145,6 +162,7 @@ import { defineComponent, PropType } from "vue";
145
162
import { IconName } from " @/utility-functions/icons" ;
146
163
147
164
import FloatingMenu , { MenuDirection } from " @/components/floating-menus/FloatingMenu.vue" ;
165
+ import LayoutCol from " @/components/layout/LayoutCol.vue" ;
148
166
import LayoutRow from " @/components/layout/LayoutRow.vue" ;
149
167
import CheckboxInput from " @/components/widgets/inputs/CheckboxInput.vue" ;
150
168
import IconLabel from " @/components/widgets/labels/IconLabel.vue" ;
@@ -158,6 +176,7 @@ interface MenuListEntryData<Value = string> {
158
176
value? : Value ;
159
177
label? : string ;
160
178
icon? : IconName ;
179
+ font? : URL ;
161
180
checkbox? : boolean ;
162
181
shortcut? : string [];
163
182
shortcutRequiresLock? : boolean ;
@@ -182,13 +201,15 @@ const MenuList = defineComponent({
182
201
drawIcon: { type: Boolean as PropType <boolean >, default: false },
183
202
interactive: { type: Boolean as PropType <boolean >, default: false },
184
203
scrollableY: { type: Boolean as PropType <boolean >, default: false },
204
+ virtualScrollingEntryHeight: { type: Number as PropType <number >, default: 0 },
185
205
defaultAction: { type: Function as PropType <() => void >, required: false },
186
206
},
187
207
data() {
188
208
return {
189
209
isOpen: this .open ,
190
210
keyboardLockInfoMessage: this .fullscreen .keyboardLockApiSupported ? KEYBOARD_LOCK_USE_FULLSCREEN : KEYBOARD_LOCK_SWITCH_BROWSER ,
191
211
highlighted: this .activeEntry as MenuListEntry | undefined ,
212
+ virtualScrollingEntriesStart: 0 ,
192
213
};
193
214
},
194
215
watch: {
@@ -326,6 +347,10 @@ const MenuList = defineComponent({
326
347
// Interactive menus should keep the active entry the same as the highlighted one
327
348
if (this .interactive && newHighlight ?.value !== this .activeEntry ?.value ) this .$emit (" update:activeEntry" , newHighlight );
328
349
},
350
+ onScroll(e : Event ) {
351
+ if (! this .virtualScrollingEntryHeight ) return ;
352
+ this .virtualScrollingEntriesStart = (e .target as HTMLElement )?.scrollTop || 0 ;
353
+ },
329
354
},
330
355
computed: {
331
356
entriesWithoutRefs(): MenuListEntryData [][] {
@@ -336,6 +361,15 @@ const MenuList = defineComponent({
336
361
})
337
362
);
338
363
},
364
+ virtualScrollingTotalHeight() {
365
+ return this .entries [0 ].length * this .virtualScrollingEntryHeight ;
366
+ },
367
+ virtualScrollingStartIndex() {
368
+ return Math .floor (this .virtualScrollingEntriesStart / this .virtualScrollingEntryHeight );
369
+ },
370
+ virtualScrollingEndIndex() {
371
+ return Math .min (this .entries [0 ].length , this .virtualScrollingStartIndex + 1 + 400 / this .virtualScrollingEntryHeight );
372
+ },
339
373
},
340
374
components: {
341
375
FloatingMenu ,
@@ -344,6 +378,7 @@ const MenuList = defineComponent({
344
378
CheckboxInput ,
345
379
UserInputLabel ,
346
380
LayoutRow ,
381
+ LayoutCol ,
347
382
},
348
383
});
349
384
export default MenuList ;
0 commit comments