pref: use hooks instead of directive (#108)

- remove v-resize
- add useContainerResize
- remove v-container
- add useContainerQueries
- add useContainerScroll
This commit is contained in:
Hayden
2025-02-01 11:56:17 +08:00
committed by GitHub
parent e5d9950429
commit 448ea4b1ba
10 changed files with 185 additions and 120 deletions

View File

@@ -1,6 +1,6 @@
<template>
<div class="flex h-full flex-col gap-4">
<div class="whitespace-nowrap px-4" v-container="container">
<div ref="container" class="whitespace-nowrap px-4">
<div :class="['flex gap-4', $sm('justify-end')]">
<Button
:class="[$sm('w-auto', 'w-full')]"
@@ -77,6 +77,7 @@ import { useContainerQueries } from 'hooks/container'
import { useDialog } from 'hooks/dialog'
import { useDownload } from 'hooks/download'
import Button from 'primevue/button'
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
const { data } = useDownload()
@@ -92,6 +93,6 @@ const openCreateTask = () => {
})
}
const container = Symbol('container')
const container = ref<HTMLElement | null>(null)
const { $sm } = useContainerQueries(container)
</script>

View File

@@ -1,14 +1,13 @@
<template>
<div
ref="contentContainer"
class="flex h-full flex-col gap-4 overflow-hidden"
v-resize="onContainerResize"
v-container="contentContainer"
>
<div
class="grid grid-cols-1 justify-center gap-4 px-8"
:style="$content_lg(contentStyle)"
>
<div class="col-span-full" v-container="toolbarContainer">
<div ref="toolbarContainer" class="col-span-full">
<div class="flex flex-col gap-4" :style="$toolbar_2xl(toolbarStyle)">
<ResponseInput
v-model="searchContent"
@@ -71,7 +70,7 @@ import ResponseSelect from 'components/ResponseSelect.vue'
import { configSetting, useConfig } from 'hooks/config'
import { useContainerQueries } from 'hooks/container'
import { useModels } from 'hooks/model'
import { defineResizeCallback } from 'hooks/resize'
import { useContainerResize } from 'hooks/resize'
import { chunk } from 'lodash'
import { app } from 'scripts/comfyAPI'
import { Model } from 'types/typings'
@@ -84,6 +83,12 @@ const { isMobile, cardWidth, gutter, aspect } = useConfig()
const { data, folders } = useModels()
const { t } = useI18n()
const toolbarContainer = ref<HTMLElement | null>(null)
const { $2xl: $toolbar_2xl } = useContainerQueries(toolbarContainer)
const contentContainer = ref<HTMLElement | null>(null)
const { $lg: $content_lg } = useContainerQueries(contentContainer)
const responseScroll = ref()
const searchContent = ref<string>()
@@ -143,8 +148,16 @@ const itemSize = computed(() => {
return itemWidth / aspect + itemGutter
})
const colSpan = ref(1)
const colSpanWidth = ref(cardWidth)
const { width } = useContainerResize(contentContainer)
const cols = computed(() => {
if (isMobile.value) {
return 1
}
const containerWidth = width.value
return Math.floor((containerWidth - gutter) / (cardWidth + gutter))
})
const list = computed(() => {
const mergedList = Object.values(data.value).flat()
@@ -180,33 +193,17 @@ const list = computed(() => {
const sortedList = filterList.sort(sortStrategy)
return chunk(sortedList, colSpan.value)
return chunk(sortedList, cols.value)
})
const toolbarContainer = Symbol('toolbar')
const { $2xl: $toolbar_2xl } = useContainerQueries(toolbarContainer)
const contentContainer = Symbol('content')
const { $lg: $content_lg } = useContainerQueries(contentContainer)
const contentStyle = {
const contentStyle = computed(() => ({
gridTemplateColumns: `repeat(auto-fit, ${cardWidth}px)`,
gap: `${gutter}px`,
paddingLeft: `1rem`,
paddingRight: `1rem`,
}
const toolbarStyle = {
flexDirection: 'row',
}
}))
const onContainerResize = defineResizeCallback((entries) => {
const entry = entries[0]
if (isMobile.value) {
colSpan.value = 1
} else {
const containerWidth = entry.contentRect.width
colSpan.value = Math.floor((containerWidth - gutter) / (cardWidth + gutter))
colSpanWidth.value = colSpan.value * (cardWidth + gutter) - gutter
}
})
const toolbarStyle = computed(() => ({
flexDirection: 'row',
}))
</script>

View File

@@ -1,8 +1,8 @@
<template>
<form
ref="container"
@submit.prevent="handleSubmit"
@reset.prevent="handleReset"
v-container="container"
>
<div class="mx-auto w-full max-w-[50rem]">
<div
@@ -63,7 +63,7 @@ import TabPanel from 'primevue/tabpanel'
import TabPanels from 'primevue/tabpanels'
import Tabs from 'primevue/tabs'
import { BaseModel, WithResolved } from 'types/typings'
import { toRaw, watch } from 'vue'
import { ref, toRaw, watch } from 'vue'
interface Props {
model: BaseModel
@@ -101,6 +101,6 @@ watch(
},
)
const container = Symbol('container')
const container = ref<HTMLElement | null>(null)
const { $xl } = useContainerQueries(container)
</script>

View File

@@ -5,8 +5,6 @@
data-scroll-viewport
class="h-full w-full overflow-auto scrollbar-none"
:style="{ contain: items ? 'strict' : undefined }"
@scroll="onContentScroll"
v-resize="onContainerResize"
>
<div data-scroll-content class="relative min-w-full">
<slot name="default">
@@ -60,7 +58,8 @@
</template>
<script setup lang="ts" generic="T">
import { defineResizeCallback } from 'hooks/resize'
import { useContainerResize } from 'hooks/resize'
import { useContainerScroll } from 'hooks/scroll'
import { clamp, throttle } from 'lodash'
import { nextTick, onUnmounted, ref, watch } from 'vue'
@@ -74,7 +73,6 @@ interface ScrollAreaProps {
const props = withDefaults(defineProps<ScrollAreaProps>(), {
scrollbar: true,
})
const emit = defineEmits(['scroll', 'resize'])
type ScrollbarDirection = 'horizontal' | 'vertical'
@@ -207,37 +205,40 @@ const calculateScrollThumbSize = () => {
})
}
const onContainerResize = defineResizeCallback((entries) => {
emit('resize', entries)
const viewport = ref<HTMLElement | null>(null)
const { width, height } = useContainerResize(viewport)
watch([width, height], () => {
if (isDragging.value) return
calculateScrollThumbSize()
})
const onContentScroll = throttle((event: Event) => {
emit('scroll', event)
if (isDragging.value) return
useContainerScroll(viewport, {
onScroll: (event) => {
if (isDragging.value) return
const container = event.target as HTMLDivElement
const content = getContainerContent()
const container = event.target as HTMLDivElement
const content = getContainerContent()
const resolveOffset = (item: Scrollbar, attr: ScrollbarAttribute) => {
const containerSize = container[attr.clientSize]
const contentSize = content[attr.clientSize]
const scrollOffset = container[attr.scrollOffset]
const resolveOffset = (item: Scrollbar, attr: ScrollbarAttribute) => {
const containerSize = container[attr.clientSize]
const contentSize = content[attr.clientSize]
const scrollOffset = container[attr.scrollOffset]
item.offset =
(scrollOffset / (contentSize - containerSize)) *
(containerSize - item.size)
}
item.offset =
(scrollOffset / (contentSize - containerSize)) *
(containerSize - item.size)
}
resolveOffset(scrollbars.value.horizontal, scrollbarAttrs.horizontal)
resolveOffset(scrollbars.value.vertical, scrollbarAttrs.vertical)
resolveOffset(scrollbars.value.horizontal, scrollbarAttrs.horizontal)
resolveOffset(scrollbars.value.vertical, scrollbarAttrs.vertical)
calculateLoadItems()
calculateLoadItems()
},
})
const viewport = ref<HTMLElement>()
const draggingDirection = ref<ScrollbarDirection>()
const prevDraggingEvent = ref<MouseEvent>()

View File

@@ -27,12 +27,7 @@
</slot>
<div v-else class="relative flex-1 overflow-hidden">
<div
ref="scrollArea"
class="h-full w-full overflow-auto scrollbar-none"
v-resize="checkScrollPosition"
@scroll="checkScrollPosition"
>
<div ref="scrollArea" class="h-full w-full overflow-auto scrollbar-none">
<div ref="contentArea" class="table max-w-full">
<div
v-show="showControlButton && scrollPosition !== 'left'"
@@ -157,11 +152,13 @@
<script setup lang="ts">
import { useConfig } from 'hooks/config'
import { useContainerResize } from 'hooks/resize'
import { useContainerScroll } from 'hooks/scroll'
import Button, { ButtonProps } from 'primevue/button'
import Drawer from 'primevue/drawer'
import Menu from 'primevue/menu'
import { SelectOptions } from 'types/typings'
import { computed, ref } from 'vue'
import { computed, ref, watch } from 'vue'
const current = defineModel()
@@ -202,7 +199,7 @@ const toggle = (event: MouseEvent) => {
}
// Select Button Type
const scrollArea = ref()
const scrollArea = ref<HTMLElement | null>(null)
const contentArea = ref()
type ScrollPosition = 'left' | 'right'
@@ -242,4 +239,16 @@ const checkScrollPosition = () => {
scrollPosition.value = position
showControlButton.value = contentWidth > containerWidth
}
const { width, height } = useContainerResize(scrollArea)
watch([width, height], () => {
checkScrollPosition()
})
useContainerScroll(scrollArea, {
onScroll: () => {
checkScrollPosition()
},
})
</script>