mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 10:55:25 +02:00
fix: improve filter popover positioning and reliability across all views
This commit is contained in:
@@ -265,6 +265,7 @@ export function ArticleDetails({ data, onEdit, rowStatuses }: ArticleDetailsProp
|
||||
)}
|
||||
</span>
|
||||
<button
|
||||
id={`details-filter-trigger-${col}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setOpenFilterCol(openFilterCol === col ? null : col);
|
||||
@@ -286,6 +287,7 @@ export function ArticleDetails({ data, onEdit, rowStatuses }: ArticleDetailsProp
|
||||
|
||||
{openFilterCol === col && (
|
||||
<ColumnFilterPopover
|
||||
triggerId={`details-filter-trigger-${col}`}
|
||||
uniqueValues={getUniqueValues(col)}
|
||||
selectedValues={columnFilters[col] || []}
|
||||
onToggle={(val) => toggleColumnFilter(col, val)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useMemo, useEffect, useRef } from 'react';
|
||||
import React, { useState, useMemo, useEffect, useRef, useCallback } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { Search, Check, X, Filter } from 'lucide-react';
|
||||
import { cn } from '../lib/utils';
|
||||
@@ -43,34 +43,44 @@ export function ColumnFilterPopover({
|
||||
const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null);
|
||||
const [position, setPosition] = useState({ top: 0, left: 0 });
|
||||
|
||||
const updatePosition = useCallback(() => {
|
||||
if (!triggerId) return;
|
||||
const trigger = document.getElementById(triggerId);
|
||||
if (!trigger) return;
|
||||
|
||||
const rect = trigger.getBoundingClientRect();
|
||||
setPosition({
|
||||
top: rect.bottom + window.scrollY + 4,
|
||||
left: Math.min(rect.left + window.scrollX, window.innerWidth + window.scrollX - 300)
|
||||
});
|
||||
}, [triggerId]);
|
||||
|
||||
useEffect(() => {
|
||||
const container = document.createElement('div');
|
||||
container.id = 'filter-portal-' + Math.random().toString(36).substr(2, 9);
|
||||
container.style.position = 'fixed';
|
||||
container.id = 'filter-portal-' + Math.random().toString(36).substring(2, 9);
|
||||
container.style.position = 'absolute';
|
||||
container.style.zIndex = '9999';
|
||||
container.style.top = '0';
|
||||
container.style.left = '0';
|
||||
container.style.width = '100%';
|
||||
container.style.pointerEvents = 'none';
|
||||
document.body.appendChild(container);
|
||||
setPortalContainer(container);
|
||||
|
||||
if (triggerId) {
|
||||
const trigger = document.getElementById(triggerId);
|
||||
if (trigger) {
|
||||
const rect = trigger.getBoundingClientRect();
|
||||
setPosition({
|
||||
top: rect.bottom + window.scrollY + 4,
|
||||
left: Math.min(rect.left + window.scrollX, window.innerWidth - 300)
|
||||
});
|
||||
}
|
||||
}
|
||||
updatePosition();
|
||||
|
||||
// Update on scroll and resize
|
||||
window.addEventListener('scroll', updatePosition, true);
|
||||
window.addEventListener('resize', updatePosition);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', updatePosition, true);
|
||||
window.removeEventListener('resize', updatePosition);
|
||||
if (document.body.contains(container)) {
|
||||
document.body.removeChild(container);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
}, [updatePosition]);
|
||||
|
||||
const isDraggingRef = React.useRef(false);
|
||||
const dragStartRef = React.useRef<number | null>(null);
|
||||
@@ -202,8 +212,8 @@ export function ColumnFilterPopover({
|
||||
)}
|
||||
style={{
|
||||
zIndex: 9999,
|
||||
top: position.top - window.scrollY,
|
||||
left: position.left - window.scrollX
|
||||
top: position.top,
|
||||
left: position.left
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
|
||||
@@ -454,6 +454,7 @@ export function DimensionsView({ data, headers, onEdit, onSaveRow, onCaptureStat
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative">
|
||||
<button
|
||||
id="dims-filter-trigger-line"
|
||||
onClick={() => setOpenFilter(openFilter === 'line' ? null : 'line')}
|
||||
className={cn(
|
||||
"flex items-center gap-2 px-3 py-2 rounded-md border text-sm transition-colors",
|
||||
@@ -465,6 +466,7 @@ export function DimensionsView({ data, headers, onEdit, onSaveRow, onCaptureStat
|
||||
</button>
|
||||
{openFilter === 'line' && (
|
||||
<ColumnFilterPopover
|
||||
triggerId="dims-filter-trigger-line"
|
||||
uniqueValues={uniqueLines}
|
||||
selectedValues={lineFilter}
|
||||
onToggle={val => setLineFilter(prev => prev.includes(val) ? prev.filter(v => v !== val) : [...prev, val])}
|
||||
@@ -479,6 +481,7 @@ export function DimensionsView({ data, headers, onEdit, onSaveRow, onCaptureStat
|
||||
|
||||
<div className="relative">
|
||||
<button
|
||||
id="dims-filter-trigger-class"
|
||||
onClick={() => setOpenFilter(openFilter === 'class' ? null : 'class')}
|
||||
className={cn(
|
||||
"flex items-center gap-2 px-3 py-2 rounded-md border text-sm transition-colors",
|
||||
@@ -490,6 +493,7 @@ export function DimensionsView({ data, headers, onEdit, onSaveRow, onCaptureStat
|
||||
</button>
|
||||
{openFilter === 'class' && (
|
||||
<ColumnFilterPopover
|
||||
triggerId="dims-filter-trigger-class"
|
||||
uniqueValues={uniqueClasses}
|
||||
selectedValues={classFilter}
|
||||
onToggle={val => setClassFilter(prev => prev.includes(val) ? prev.filter(v => v !== val) : [...prev, val])}
|
||||
|
||||
@@ -255,6 +255,7 @@ export function MatrixView({ data, headers, rowStatuses }: MatrixViewProps) {
|
||||
)}
|
||||
</span>
|
||||
<button
|
||||
id={`matrix-filter-trigger-${index}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setOpenFilterCol(openFilterCol === index ? null : index);
|
||||
@@ -276,6 +277,7 @@ export function MatrixView({ data, headers, rowStatuses }: MatrixViewProps) {
|
||||
|
||||
{openFilterCol === index && (
|
||||
<ColumnFilterPopover
|
||||
triggerId={`matrix-filter-trigger-${index}`}
|
||||
uniqueValues={getUniqueValues(index)}
|
||||
selectedValues={columnFilters[index] || []}
|
||||
onToggle={(val) => toggleColumnFilter(index, val)}
|
||||
|
||||
@@ -422,6 +422,7 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi
|
||||
) : (
|
||||
<>
|
||||
<button
|
||||
id={`missing-filter-trigger-${col}`}
|
||||
onClick={(e) => { e.stopPropagation(); setOpenFilter(openFilter === col ? null : col); }}
|
||||
className={cn(
|
||||
"w-full flex items-center justify-between px-1.5 py-0.5 bg-slate-800 border rounded text-[10px] transition-colors",
|
||||
@@ -433,6 +434,7 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi
|
||||
</button>
|
||||
{openFilter === col && (
|
||||
<ColumnFilterPopover
|
||||
triggerId={`missing-filter-trigger-${col}`}
|
||||
uniqueValues={allValues}
|
||||
selectedValues={selectedFilters}
|
||||
onToggle={(val) => setColumnFilters(prev => {
|
||||
|
||||
@@ -2067,16 +2067,27 @@ function TextFilterPopover({ value, onChange, onClose, triggerId }: {
|
||||
}) {
|
||||
const [position, setPosition] = useState({ top: 0, left: 0 });
|
||||
|
||||
useEffect(() => {
|
||||
const updatePosition = useCallback(() => {
|
||||
const trigger = document.getElementById(triggerId);
|
||||
if (trigger) {
|
||||
const rect = trigger.getBoundingClientRect();
|
||||
setPosition({
|
||||
top: rect.bottom + window.scrollY + 4,
|
||||
left: Math.min(rect.left + window.scrollX, window.innerWidth - 300)
|
||||
left: Math.min(rect.left + window.scrollX, window.innerWidth + window.scrollX - 300)
|
||||
});
|
||||
}
|
||||
}, [triggerId]);
|
||||
|
||||
useEffect(() => {
|
||||
updatePosition();
|
||||
window.addEventListener('scroll', updatePosition, true);
|
||||
window.addEventListener('resize', updatePosition);
|
||||
return () => {
|
||||
window.removeEventListener('scroll', updatePosition, true);
|
||||
window.removeEventListener('resize', updatePosition);
|
||||
};
|
||||
}, [updatePosition]);
|
||||
|
||||
const addTerm = () => {
|
||||
if (value.terms.length < 5) {
|
||||
onChange({ ...value, terms: [...value.terms, ''] });
|
||||
@@ -2101,10 +2112,10 @@ function TextFilterPopover({ value, onChange, onClose, triggerId }: {
|
||||
|
||||
return createPortal(
|
||||
<div
|
||||
className="fixed z-[9999] w-72 bg-slate-800 border border-slate-700 rounded-lg shadow-2xl p-4 flex flex-col gap-4 animate-in fade-in zoom-in-95 duration-100"
|
||||
className="absolute z-[9999] w-72 bg-slate-800 border border-slate-700 rounded-lg shadow-2xl p-4 flex flex-col gap-4 animate-in fade-in zoom-in-95 duration-100"
|
||||
style={{
|
||||
top: position.top - window.scrollY,
|
||||
left: position.left - window.scrollX
|
||||
top: position.top,
|
||||
left: position.left
|
||||
}}
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
|
||||
@@ -326,6 +326,7 @@ export function ProductDescriptions({ data, headers, asinColumnIndex, onEdit, ro
|
||||
)}
|
||||
</span>
|
||||
<button
|
||||
id={`desc-filter-trigger-${col}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setOpenFilterCol(openFilterCol === col ? null : col);
|
||||
@@ -347,6 +348,7 @@ export function ProductDescriptions({ data, headers, asinColumnIndex, onEdit, ro
|
||||
|
||||
{openFilterCol === col && (
|
||||
<ColumnFilterPopover
|
||||
triggerId={`desc-filter-trigger-${col}`}
|
||||
uniqueValues={getUniqueValues(col)}
|
||||
selectedValues={columnFilters[col] || []}
|
||||
onToggle={(val) => toggleColumnFilter(col, val)}
|
||||
|
||||
Reference in New Issue
Block a user