55 :icon =" isExpanded ? 'pi pi-chevron-up' : 'pi pi-chevron-down'"
66 text
77 severity =" secondary"
8- @click =" isExpanded = !isExpanded "
8+ @click =" toggleExpanded "
99 class =" toggle-expanded-button"
1010 v-tooltip =" $t('sideToolbar.queueTab.showFlatList')"
1111 />
1818 />
1919 </template >
2020 <template #body >
21- <div v-if =" tasks.length > 0" class =" queue-grid" >
22- <TaskItem
23- v-for =" task in tasks"
24- :key =" task.key"
25- :task =" task"
26- :isFlatTask =" isExpanded"
27- @contextmenu =" handleContextMenu"
28- />
21+ <div
22+ v-if =" visibleTasks.length > 0"
23+ ref =" scrollContainer"
24+ class =" scroll-container"
25+ >
26+ <div class =" queue-grid" >
27+ <TaskItem
28+ v-for =" task in visibleTasks"
29+ :key =" task.key"
30+ :task =" task"
31+ :isFlatTask =" isExpanded"
32+ @contextmenu =" handleContextMenu"
33+ />
34+ </div >
35+ <div ref =" loadMoreTrigger" style =" height : 1px " />
2936 </div >
3037 <div v-else >
3138 <NoResultsPlaceholder
4148</template >
4249
4350<script setup lang="ts">
51+ import { ref , computed , onMounted , onUnmounted , watch , nextTick } from ' vue'
52+ import { useInfiniteScroll , useResizeObserver } from ' @vueuse/core'
53+ import { useI18n } from ' vue-i18n'
54+ import { useConfirm } from ' primevue/useconfirm'
55+ import { useToast } from ' primevue/usetoast'
4456import Button from ' primevue/button'
4557import ConfirmPopup from ' primevue/confirmpopup'
4658import ContextMenu from ' primevue/contextmenu'
59+ import type { MenuItem } from ' primevue/menuitem'
4760import TaskItem from ' ./queue/TaskItem.vue'
4861import SideBarTabTemplate from ' ./SidebarTabTemplate.vue'
4962import NoResultsPlaceholder from ' @/components/common/NoResultsPlaceholder.vue'
50- import { useConfirm } from ' primevue/useconfirm'
51- import { useToast } from ' primevue/usetoast'
5263import { TaskItemImpl , useQueueStore } from ' @/stores/queueStore'
53- import { computed , onMounted , onUnmounted , ref } from ' vue'
54- import { useI18n } from ' vue-i18n'
55- import type { MenuItem } from ' primevue/menuitem'
5664import { api } from ' @/scripts/api'
5765
5866const confirm = useConfirm ()
5967const toast = useToast ()
6068const queueStore = useQueueStore ()
6169const { t } = useI18n ()
6270
63- const tasks = computed (() =>
71+ const isExpanded = ref (false )
72+ const visibleTasks = ref <TaskItemImpl []>([])
73+ const scrollContainer = ref <HTMLElement | null >(null )
74+ const loadMoreTrigger = ref <HTMLElement | null >(null )
75+
76+ const ITEMS_PER_PAGE = 8
77+ const SCROLL_THRESHOLD = 100 // pixels from bottom to trigger load
78+
79+ const allTasks = computed (() =>
6480 isExpanded .value ? queueStore .flatTasks : queueStore .tasks
6581)
6682
83+ const loadMoreItems = () => {
84+ const currentLength = visibleTasks .value .length
85+ const newTasks = allTasks .value .slice (
86+ currentLength ,
87+ currentLength + ITEMS_PER_PAGE
88+ )
89+ visibleTasks .value .push (... newTasks )
90+ }
91+
92+ const checkAndLoadMore = () => {
93+ if (! scrollContainer .value ) return
94+
95+ const { scrollHeight, scrollTop, clientHeight } = scrollContainer .value
96+ if (scrollHeight - scrollTop - clientHeight < SCROLL_THRESHOLD ) {
97+ loadMoreItems ()
98+ }
99+ }
100+
101+ useInfiniteScroll (
102+ scrollContainer ,
103+ () => {
104+ if (visibleTasks .value .length < allTasks .value .length ) {
105+ loadMoreItems ()
106+ }
107+ },
108+ { distance: SCROLL_THRESHOLD }
109+ )
110+
111+ // Use ResizeObserver to detect container size changes
112+ // This is necessary as the sidebar tab can change size when user drags the splitter.
113+ useResizeObserver (scrollContainer , () => {
114+ nextTick (() => {
115+ checkAndLoadMore ()
116+ })
117+ })
118+
119+ const toggleExpanded = () => {
120+ isExpanded .value = ! isExpanded .value
121+ visibleTasks .value = allTasks .value .slice (0 , ITEMS_PER_PAGE )
122+ }
123+
67124const removeTask = (task : TaskItemImpl ) => {
68125 if (task .isRunning ) {
69126 api .interrupt ()
@@ -75,9 +132,9 @@ const removeAllTasks = async () => {
75132 await queueStore .clear ()
76133}
77134
78- const confirmRemoveAll = (event ) => {
135+ const confirmRemoveAll = (event : Event ) => {
79136 confirm .require ({
80- target: event .currentTarget ,
137+ target: event .currentTarget as HTMLElement ,
81138 message: ' Do you want to delete all tasks?' ,
82139 icon: ' pi pi-info-circle' ,
83140 rejectProps: {
@@ -101,27 +158,35 @@ const confirmRemoveAll = (event) => {
101158 })
102159}
103160
104- const onStatus = () => queueStore .update ()
161+ const onStatus = () => {
162+ queueStore .update ()
163+ visibleTasks .value = allTasks .value .slice (0 , ITEMS_PER_PAGE )
164+ }
105165
106166const menu = ref (null )
107167const menuTargetTask = ref <TaskItemImpl | null >(null )
108- const menuItems = computed <MenuItem []>(() => {
109- return [
110- {
111- label: t (' delete' ),
112- icon: ' pi pi-trash' ,
113- command : () => removeTask (menuTargetTask .value ! )
114- },
115- {
116- label: t (' loadWorkflow' ),
117- icon: ' pi pi-file-export' ,
118- command : () => menuTargetTask .value ?.loadWorkflow ()
119- }
120- ]
121- })
122- const handleContextMenu = ({ task , event }) => {
168+ const menuItems = computed <MenuItem []>(() => [
169+ {
170+ label: t (' delete' ),
171+ icon: ' pi pi-trash' ,
172+ command : () => menuTargetTask .value && removeTask (menuTargetTask .value )
173+ },
174+ {
175+ label: t (' loadWorkflow' ),
176+ icon: ' pi pi-file-export' ,
177+ command : () => menuTargetTask .value ?.loadWorkflow ()
178+ }
179+ ])
180+
181+ const handleContextMenu = ({
182+ task ,
183+ event
184+ }: {
185+ task: TaskItemImpl
186+ event: Event
187+ }) => {
123188 menuTargetTask .value = task
124- menu .value .show (event )
189+ menu .value ? .show (event )
125190}
126191
127192onMounted (() => {
@@ -133,10 +198,31 @@ onUnmounted(() => {
133198 api .removeEventListener (' status' , onStatus )
134199})
135200
136- const isExpanded = ref (false )
201+ // Watch for changes in allTasks and reset visibleTasks if necessary
202+ watch (
203+ allTasks ,
204+ (newTasks ) => {
205+ if (
206+ visibleTasks .value .length === 0 ||
207+ visibleTasks .value .length > newTasks .length
208+ ) {
209+ visibleTasks .value = newTasks .slice (0 , ITEMS_PER_PAGE )
210+ }
211+
212+ nextTick (() => {
213+ checkAndLoadMore ()
214+ })
215+ },
216+ { immediate: true }
217+ )
137218 </script >
138219
139220<style scoped>
221+ .scroll-container {
222+ height : 100% ;
223+ overflow-y : auto ;
224+ }
225+
140226.queue-grid {
141227 display : grid ;
142228 grid-template-columns : repeat (auto-fill , minmax (200px , 1fr ));
0 commit comments