mirror of
https://github.com/fantastic-admin/basic.git
synced 2026-10-07 13:07:58 +08:00
refactor: 移除 FaDrawer / FaModal 组件内置的 FaScrollArea 组件
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -30,6 +30,7 @@ export interface DrawerProps {
|
||||
closeOnClickOverlay?: boolean
|
||||
closeOnPressEscape?: boolean
|
||||
destroyOnClose?: boolean
|
||||
openAutoFocus?: boolean
|
||||
contentClass?: HTMLAttributes['class']
|
||||
headerClass?: HTMLAttributes['class']
|
||||
footerClass?: HTMLAttributes['class']
|
||||
|
||||
@@ -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, {
|
||||
|
||||
@@ -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',
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user