移除 FaDrawer / FaModal 组件内置的 FaScrollArea 组件

This commit is contained in:
Hooray
2025-11-09 19:56:22 +08:00
parent 663c195da1
commit 943f68e0e0
5 changed files with 47 additions and 37 deletions
@@ -30,7 +30,7 @@ const searchInput = ref('')
const searchInputRef = useTemplateRef('searchInputRef')
const { focused: searchInputFocused } = useFocus(searchInputRef)
const sourceList = ref<listTypes[]>([])
const actived = ref(-1)
const actived = ref(0)
const searchResultRef = useTemplateRef('searchResultRef')
const searchResultItemRef = useTemplateRef<HTMLElement[]>('searchResultItemRef')
@@ -69,7 +69,7 @@ const resultList = computed(() => {
watch(() => isShow.value, (val) => {
if (val) {
searchInput.value = ''
actived.value = -1
actived.value = 0
// 当搜索显示的时候绑定上、下、回车快捷键,隐藏的时候再解绑。另外当 input 处于 focus 状态时,采用 vue 来绑定键盘事件
hotkeys('up', keyUp)
hotkeys('down', keyDown)
@@ -89,7 +89,7 @@ watch(() => isShow.value, (val) => {
}
})
watch(() => resultList.value, () => {
actived.value = -1
actived.value = 0
handleScroll()
})
@@ -158,15 +158,13 @@ function keyDown() {
}
}
function keyEnter() {
if (actived.value !== -1) {
searchResultItemRef.value?.find(item => Number.parseInt(item.dataset.index!) === actived.value)?.click()
}
searchResultItemRef.value?.find(item => Number.parseInt(item.dataset.index!) === actived.value)?.click()
}
function handleScroll() {
if (searchResultRef.value?.areaRef?.ref?.el?.viewportElement) {
const contentDom = searchResultRef.value.areaRef.ref.el.viewportElement
if (searchResultRef.value?.ref?.el?.viewportElement) {
const contentDom = searchResultRef.value.ref.el.viewportElement
let scrollTo = 0
if (actived.value !== -1) {
if (resultList.value.length > 0) {
scrollTo = contentDom.scrollTop
const activedOffsetTop = searchResultItemRef.value?.find(item => Number.parseInt(item.dataset.index!) === actived.value)?.offsetTop ?? 0
const activedClientHeight = searchResultItemRef.value?.find(item => Number.parseInt(item.dataset.index!) === actived.value)?.clientHeight ?? 0
@@ -197,7 +195,7 @@ function pageJump(path: listTypes['path'], link: listTypes['link']) {
</script>
<template>
<FaModal ref="searchResultRef" v-model="isShow" border :footer="settingsStore.mode === 'pc'" :closable="false" class="w-full lg-max-w-2xl" content-class="flex flex-col p-0 min-h-auto" header-class="p-0" footer-class="p-0" @opened="searchInputFocused = true">
<FaModal v-model="isShow" border :footer="settingsStore.mode === 'pc'" :closable="false" class="w-full lg-max-w-2xl" content-class="flex flex-col p-0 min-h-auto" header-class="p-0" footer-class="p-0" @opened="searchInputFocused = true">
<template #header>
<div class="h-12 flex flex-shrink-0 items-center">
<div class="h-full w-14 flex-center">
@@ -236,7 +234,7 @@ function pageJump(path: listTypes['path'], link: listTypes['link']) {
</div>
</div>
</template>
<div>
<FaScrollArea ref="searchResultRef">
<template v-if="resultList.length > 0">
<div v-for="(item, index) in resultList" ref="searchResultItemRef" :key="item.path" class="p-4" :data-index="index" @click="pageJump(item.path, item.link)" @mouseover="actived = index">
<a class="flex cursor-pointer items-center border rounded-lg" :class="{ '-mt-4': index !== 0, 'bg-accent': index === actived }">
@@ -268,6 +266,6 @@ function pageJump(path: listTypes['path'], link: listTypes['link']) {
</p>
</div>
</template>
</div>
</FaScrollArea>
</FaModal>
</template>
+1
View File
@@ -30,6 +30,7 @@ export interface DrawerProps {
closeOnClickOverlay?: boolean
closeOnPressEscape?: boolean
destroyOnClose?: boolean
openAutoFocus?: boolean
contentClass?: HTMLAttributes['class']
headerClass?: HTMLAttributes['class']
footerClass?: HTMLAttributes['class']
+14 -10
View File
@@ -38,6 +38,7 @@ const props = withDefaults(
closeOnClickOverlay: true,
closeOnPressEscape: true,
destroyOnClose: true,
openAutoFocus: false,
},
)
@@ -135,6 +136,13 @@ async function onCancel() {
}
}
function handleOpenAutoFocus(e: Event) {
if (!props.openAutoFocus) {
e.preventDefault()
e.stopPropagation()
}
}
function handleFocusOutside(e: Event) {
e.preventDefault()
e.stopPropagation()
@@ -179,7 +187,7 @@ function handleAnimationEnd() {
})"
:side="props.side"
:force-mount="forceMount"
@open-auto-focus="handleFocusOutside"
@open-auto-focus="handleOpenAutoFocus"
@close-auto-focus="handleFocusOutside"
@focus-outside="handleFocusOutside"
@pointer-down-outside="handleClickOutside"
@@ -209,15 +217,11 @@ function handleAnimationEnd() {
<SheetTitle />
<SheetDescription />
</VisuallyHidden>
<div class="m-0 flex-1 of-y-hidden">
<FaScrollArea v-if="!!slots.default" class="h-full">
<div class="p-4">
<slot />
</div>
<div v-show="props.loading" class="absolute inset-0 z-1000 size-full flex-center bg-popover/75">
<FaIcon name="i-line-md:loading-twotone-loop" class="size-10" />
</div>
</FaScrollArea>
<div v-if="!!slots.default" class="relative m-0 flex-1 overflow-y-auto p-4">
<slot />
</div>
<div v-show="props.loading" class="absolute inset-0 z-1000 size-full flex-center bg-popover/75">
<FaIcon name="i-line-md:loading-twotone-loop" class="size-10" />
</div>
<SheetFooter
v-if="footer" :class="cn('p-3 gap-y-2', props.footerClass, {
+6
View File
@@ -34,6 +34,7 @@ export interface ModalProps {
closeOnClickOverlay?: boolean
closeOnPressEscape?: boolean
destroyOnClose?: boolean
openAutoFocus?: boolean
class?: HTMLAttributes['class']
headerClass?: HTMLAttributes['class']
contentClass?: HTMLAttributes['class']
@@ -147,6 +148,7 @@ export function useFaModal() {
alignCenter: true,
closeOnClickOverlay: false,
destroyOnClose: true,
openAutoFocus: true,
contentClass: 'py-0 min-h-auto',
footerClass: 'p-4',
}
@@ -161,6 +163,7 @@ export function useFaModal() {
alignCenter: true,
closeOnClickOverlay: false,
destroyOnClose: true,
openAutoFocus: true,
contentClass: 'py-0 min-h-auto',
footerClass: 'p-4',
}
@@ -175,6 +178,7 @@ export function useFaModal() {
alignCenter: true,
closeOnClickOverlay: false,
destroyOnClose: true,
openAutoFocus: true,
contentClass: 'py-0 min-h-auto',
footerClass: 'p-4',
}
@@ -189,6 +193,7 @@ export function useFaModal() {
alignCenter: true,
closeOnClickOverlay: false,
destroyOnClose: true,
openAutoFocus: true,
contentClass: 'py-0 min-h-auto',
footerClass: 'p-4',
}
@@ -203,6 +208,7 @@ export function useFaModal() {
showCancelButton: true,
closeOnClickOverlay: false,
destroyOnClose: true,
openAutoFocus: true,
contentClass: 'py-0 min-h-auto',
footerClass: 'p-4',
}
+16 -15
View File
@@ -42,6 +42,7 @@ const props = withDefaults(
closeOnClickOverlay: true,
closeOnPressEscape: true,
destroyOnClose: true,
openAutoFocus: false,
},
)
@@ -55,13 +56,8 @@ const slots = defineSlots<{
const dialogContentRef = useTemplateRef('dialogContentRef')
const dialogHeaderRef = ref()
const dialogAreaRef = useTemplateRef('dialogAreaRef')
const dialogRef = ref()
defineExpose({
areaRef: dialogAreaRef,
})
const modalId = shallowRef(props.id ?? useId())
const isOpen = ref(props.modelValue)
const isMaximize = ref(props.maximize)
@@ -174,6 +170,13 @@ async function onCancel() {
}
}
function handleOpenAutoFocus(e: Event) {
if (!props.openAutoFocus) {
e.preventDefault()
e.stopPropagation()
}
}
function handleFocusOutside(e: Event) {
e.preventDefault()
e.stopPropagation()
@@ -222,13 +225,13 @@ function handleAnimationEnd() {
:maximize="isMaximize"
:maximizable="props.maximizable"
:force-mount="forceMount"
:class="cn('left-0 right-0 top-0 md:top-[5vh] flex flex-col p-0 gap-0 mx-auto h-[calc-size(auto,size)] min-h-full md:min-h-auto max-h-full md:max-h-[90vh] translate-x-0 translate-y-0', props.class, {
:class="cn('left-0 right-0 top-0 md:top-[5vh] flex flex-col p-0 gap-0 mx-auto h-[calc-size(auto,size)] min-h-full overflow-hidden md:min-h-auto max-h-full md:max-h-[90vh] translate-x-0 translate-y-0', props.class, {
'md:top-0 size-full max-w-full max-h-full md:max-h-full': isMaximize,
'md:top-1/2 md:-translate-y-1/2!': props.alignCenter,
'duration-0': isDragging,
'hidden': isClosed,
})"
@open-auto-focus="handleFocusOutside"
@open-auto-focus="handleOpenAutoFocus"
@close-auto-focus="handleFocusOutside"
@focus-outside="handleFocusOutside"
@pointer-down-outside="handleClickOutside"
@@ -273,14 +276,12 @@ function handleAnimationEnd() {
<DialogTitle />
<DialogDescription />
</VisuallyHidden>
<FaScrollArea v-if="!!slots.default" ref="dialogAreaRef" class="flex-1">
<div :class="cn('min-h-40 p-4', props.contentClass)">
<slot />
</div>
<div v-show="props.loading" class="absolute inset-0 z-1000 size-full flex-center bg-popover/75">
<FaIcon name="i-line-md:loading-twotone-loop" class="size-10" />
</div>
</FaScrollArea>
<div v-if="!!slots.default" :class="cn('relative flex-1 min-h-40 p-4 overflow-y-auto', props.contentClass)">
<slot />
</div>
<div v-show="props.loading" class="absolute inset-0 z-1000 size-full flex-center bg-popover/75">
<FaIcon name="i-line-md:loading-twotone-loop" class="size-10" />
</div>
<DialogFooter
v-if="footer" :class="cn('p-3 gap-y-2', props.footerClass, {
'md:justify-center': props.center,