fix(leads): keep the filter panel from shifting the page

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-11-08 20:58:18 +03:30
co-authored by Cursor
parent ef58922e13
commit 3c9b906bd6
2 changed files with 58 additions and 48 deletions
+1 -1
View File
@@ -360,7 +360,7 @@ function place() {
function liftAboveOverlays() {
let z = 120;
for (const el of document.querySelectorAll('.ant-modal-wrap, .ant-drawer, .ant-popover')) {
for (const el of document.querySelectorAll('.ant-modal-wrap, .ant-drawer, .ant-popover, [data-lead-filter]')) {
const zi = Number.parseInt(window.getComputedStyle(el).zIndex, 10);
if (Number.isFinite(zi)) z = Math.max(z, zi + 20);
}
+57 -47
View File
@@ -22,7 +22,6 @@ import {
Modal,
Modal as AModal,
Pagination as APagination,
Popover as APopover,
RadioButton as ARadioButton,
RadioGroup as ARadioGroup,
Row as ARow,
@@ -340,6 +339,20 @@ function toggleFilterSection(name: NonNullable<typeof filterSection.value>) {
const filterAnchor = ref<HTMLElement | null>(null);
const filterPopoverMaxHeight = ref('min(70vh, 32rem)');
const filterPanelStyle = computed(() => ({
position: 'absolute' as const,
top: 'calc(100% + 6px)',
insetInlineStart: '0',
zIndex: 1050,
width: 'min(20rem, calc(100vw - 1.5rem))',
maxHeight: filterPopoverMaxHeight.value,
overflow: 'auto',
background: token.value.colorBgElevated,
borderRadius: `${token.value.borderRadiusLG}px`,
boxShadow: token.value.boxShadowSecondary,
padding: `${token.value.paddingSM}px`,
}));
function placeFilterPopover() {
const el = filterAnchor.value;
if (!el) return;
@@ -347,18 +360,26 @@ function placeFilterPopover() {
filterPopoverMaxHeight.value = `${Math.max(220, space)}px`;
}
function onFilterPopover(open: boolean) {
if (!open && document.getElementById('jalali-picker')) return;
if (open) placeFilterPopover();
moreFiltersOpen.value = open;
if (open) {
window.addEventListener('resize', placeFilterPopover);
window.addEventListener('scroll', placeFilterPopover, true);
} else {
function onFilterDocPointer(event: Event) {
const target = event.target;
if (!(target instanceof Node)) return;
if (filterAnchor.value?.contains(target)) return;
if (document.getElementById('jalali-picker')?.contains(target)) return;
moreFiltersOpen.value = false;
filterSection.value = null;
document.removeEventListener('pointerdown', onFilterDocPointer, true);
}
function toggleFilterPopover() {
if (moreFiltersOpen.value) {
moreFiltersOpen.value = false;
filterSection.value = null;
window.removeEventListener('resize', placeFilterPopover);
window.removeEventListener('scroll', placeFilterPopover, true);
document.removeEventListener('pointerdown', onFilterDocPointer, true);
return;
}
placeFilterPopover();
moreFiltersOpen.value = true;
document.addEventListener('pointerdown', onFilterDocPointer, true);
}
function dateFacetLabel(value: string) {
@@ -1531,13 +1552,12 @@ onMounted(async () => {
onBeforeUnmount(() => {
stopImportPoll();
revokeLogoUrls();
window.removeEventListener('resize', placeFilterPopover);
window.removeEventListener('scroll', placeFilterPopover, true);
document.removeEventListener('pointerdown', onFilterDocPointer, true);
});
</script>
<template>
<a-row :gutter="[16, 16]" @click="closePopups">
<a-row :gutter="[16, 16]" align="top" :style="{ alignContent: 'flex-start' }" @click="closePopups">
<a-col :span="24">
<a-flex justify="space-between" align="center" wrap="wrap" :gap="token.marginSM">
<a-typography-title :level="3" :style="{ margin: 0 }">لیدها</a-typography-title>
@@ -1712,38 +1732,29 @@ onBeforeUnmount(() => {
</a-button>
</template>
</a-input-search>
<div ref="filterAnchor" :style="isMobile ? { width: '100%' } : undefined">
<a-popover
:open="moreFiltersOpen"
trigger="click"
placement="bottomRight"
:arrow="false"
:auto-adjust-overflow="false"
:overlay-inner-style="{
width: 'min(20rem, calc(100vw - 1.5rem))',
maxHeight: filterPopoverMaxHeight,
overflow: 'auto',
}"
@open-change="onFilterPopover"
<div
ref="filterAnchor"
:style="{ position: 'relative', width: isMobile ? '100%' : undefined }"
>
<a-button
:block="isMobile"
:style="mobileFilterBadgeCount
? {
borderColor: token.colorPrimary,
color: token.colorPrimary,
background: token.colorPrimaryBg,
}
: undefined"
@click.stop="toggleFilterPopover"
>
<a-button
:block="isMobile"
:style="mobileFilterBadgeCount
? {
borderColor: token.colorPrimary,
color: token.colorPrimary,
background: token.colorPrimaryBg,
}
: undefined"
>
<a-flex align="center" justify="center" :gap="6">
<FilterOutlined />
<span>{{ filterBtnLabel }}</span>
<DownOutlined />
</a-flex>
</a-button>
<template #content>
<a-flex vertical :gap="10" @click.stop>
<a-flex align="center" justify="center" :gap="6">
<FilterOutlined />
<span>{{ filterBtnLabel }}</span>
<DownOutlined />
</a-flex>
</a-button>
<div v-if="moreFiltersOpen" data-lead-filter :style="filterPanelStyle" @click.stop>
<a-flex vertical :gap="10">
<div v-for="facet in textFacets" :key="facet.key">
<div
role="button"
@@ -1926,8 +1937,7 @@ onBeforeUnmount(() => {
پاک کردن فیلترها
</a-button>
</a-flex>
</template>
</a-popover>
</div>
</div>
<a-select
:value="sort"