"use client" import * as React from "react" import { cn } from "../../lib/utils" import { Button } from "../ui/button" import { Input } from "../ui/input" import { Tip } from "../ui/tip" import { FilterDateCalendar } from "../data-table/filter-date-calendar" import { FilterDateRangeControl } from "../data-table/filter-date-range-control" import { FilterPersonOptionList } from "../data-table/filter-person-option-list" import { FilterRangeControl } from "../data-table/filter-range-control" import { FilterTextValueInput } from "../data-table/filter-text-value-input" import type { FilterFieldRuntimeContext } from "../../lib/column-filter-context" import { type ActiveFilter, type ConditionalRule, type FilterFieldDef, type FilterOperator, OPERATOR_LABELS, FILTER_TEXT_MASK_FORMAT_HINT, } from "../../lib/table-properties-types" import { PROPERTIES_SHEET_TOOLTIP_Z, PROPERTIES_SHEET_TOOLTIP_Z_INDEX } from "./sheet-portal-z" import { PropertiesCardActions } from "./properties-card-icon-action" import { ConditionalRuleColorPicker } from "./conditional-rule-color-picker" type DrawerFilterCardBaseProps = { fieldDef: FilterFieldDef fieldContext?: FilterFieldRuntimeContext expanded: boolean onToggleExpand: () => void onRemove: (id: string) => void renderOptionLabel?: (value: string) => React.ReactNode } export type DrawerFilterCardProps = | (DrawerFilterCardBaseProps & { variant?: "filter" filter: ActiveFilter onUpdate: (id: string, patch: Partial) => void }) | (DrawerFilterCardBaseProps & { variant: "conditional" filter: ConditionalRule onUpdate: (id: string, patch: Partial) => void }) /** Inline filter card used inside the Table Properties drawer (filter or conditional rule). */ export function DrawerFilterCard(props: DrawerFilterCardProps) { const { fieldDef, fieldContext, expanded, onToggleExpand, onRemove, renderOptionLabel, } = props const isCond = props.variant === "conditional" const filter = props.filter const filterId = filter.id // eslint-disable-next-line @typescript-eslint/no-explicit-any const onUpdate = props.onUpdate as (id: string, patch: any) => void const [optSearch, setOptSearch] = React.useState("") const isPersonSelect = fieldDef.type === "select" && fieldDef.selectVariant === "person" const personOptions = fieldContext?.personOptions ?? [] const options = isPersonSelect ? personOptions.map(p => ({ value: p.value, label: p.label })) : (fieldDef.options ?? []) const showSearch = !isPersonSelect && options.length > 8 const filteredOpts = optSearch ? options.filter(o => o.label.toLowerCase().includes(optSearch.toLowerCase())) : options const values = filter.values const selectedValueSet = React.useMemo(() => new Set(values), [values]) /** Roving tabindex over the option list (same pattern as `ViewSegmentedControl`). */ const optionRefs = React.useRef<(HTMLDivElement | null)[]>([]) const [activeOptionIndex, setActiveOptionIndex] = React.useState(0) const optionCount = filteredOpts.length const activeIndex = activeOptionIndex < optionCount ? activeOptionIndex : 0 React.useEffect(() => { setActiveOptionIndex(0) }, [optSearch]) const moveActiveOption = React.useCallback( (index: number) => { if (optionCount === 0) return const i = ((index % optionCount) + optionCount) % optionCount setActiveOptionIndex(i) requestAnimationFrame(() => { optionRefs.current[i]?.focus() }) }, [optionCount], ) const onOptionKeyDown = React.useCallback( (e: React.KeyboardEvent, index: number) => { if (e.key === "ArrowDown") { e.preventDefault() moveActiveOption(index + 1) } else if (e.key === "ArrowUp") { e.preventDefault() moveActiveOption(index - 1) } else if (e.key === "Home") { e.preventDefault() moveActiveOption(0) } else if (e.key === "End") { e.preventDefault() moveActiveOption(optionCount - 1) } }, [moveActiveOption, optionCount], ) const rangeMin = fieldContext?.rangeMin ?? fieldDef.rangeMin ?? 0 const rangeMax = fieldContext?.rangeMax ?? fieldDef.rangeMax ?? 100 const rangeStep = fieldContext?.rangeStep ?? fieldDef.rangeStep ?? 1 const effectiveOperator = (fieldDef.type === "select" || fieldDef.type === "date" || fieldDef.type === "date-range") && filter.operator !== "is" && filter.operator !== "is_not" ? "is" : filter.operator const hideOperatorCycle = fieldDef.type === "range" || fieldDef.type === "date-range" function toggleValue(val: string) { const next = values.includes(val) ? values.filter(v => v !== val) : [...values, val] onUpdate(filterId, { values: next }) } function cycleOperator() { const ops = fieldDef.operators const idx = ops.indexOf(effectiveOperator as FilterOperator) const i = idx === -1 ? 0 : idx onUpdate(filterId, { operator: ops[(i + 1) % ops.length] }) } const removeLabel = isCond ? "rule" : "filter" const expandLabel = expanded ? `Collapse ${fieldDef.label}` : `Expand ${fieldDef.label}` const rule = isCond ? (props.filter as ConditionalRule) : null const hasSelection = fieldDef.type === "date-range" ? Boolean(values[0] || values[1]) : fieldDef.type === "range" ? values.length >= 2 : values.length > 0 return (

{fieldDef.label}

{hideOperatorCycle ? ( between ) : (