Skip to content

Commit 00a8a2d

Browse files
committed
Better filtering behaviour
1 parent 511dc88 commit 00a8a2d

File tree

1 file changed

+54
-17
lines changed

1 file changed

+54
-17
lines changed

apps/webapp/app/components/code/TSQLResultsTable.tsx

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
import { useVirtualizer } from "@tanstack/react-virtual";
1919
import { formatDurationMilliseconds, MachinePresetName } from "@trigger.dev/core/v3";
2020
import { ClipboardCheckIcon, ClipboardIcon } from "lucide-react";
21-
import { memo, useEffect, useMemo, useRef, useState } from "react";
21+
import { forwardRef, memo, useEffect, useMemo, useRef, useState } from "react";
2222
import { EnvironmentLabel } from "~/components/environments/EnvironmentLabel";
2323
import { MachineLabelCombo } from "~/components/MachineLabelCombo";
2424
import { DateTimeAccurate } from "~/components/primitives/DateTime";
@@ -188,16 +188,14 @@ const fuzzyFilter: FilterFn<RowData> = (row, columnId, value, addMeta) => {
188188
/**
189189
* Debounced input component for filter inputs
190190
*/
191-
function DebouncedInput({
192-
value: initialValue,
193-
onChange,
194-
debounce = 300,
195-
...props
196-
}: {
197-
value: string;
198-
onChange: (value: string) => void;
199-
debounce?: number;
200-
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, "onChange">) {
191+
const DebouncedInput = forwardRef<
192+
HTMLInputElement,
193+
{
194+
value: string;
195+
onChange: (value: string) => void;
196+
debounce?: number;
197+
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, "onChange">
198+
>(function DebouncedInput({ value: initialValue, onChange, debounce = 300, ...props }, ref) {
201199
const [value, setValue] = useState(initialValue);
202200

203201
useEffect(() => {
@@ -212,8 +210,8 @@ function DebouncedInput({
212210
return () => clearTimeout(timeout);
213211
}, [value, debounce, onChange]);
214212

215-
return <input {...props} value={value} onChange={(e) => setValue(e.target.value)} />;
216-
}
213+
return <input ref={ref} {...props} value={value} onChange={(e) => setValue(e.target.value)} />;
214+
});
217215

218216
// Extended column meta to store OutputColumnMetadata
219217
interface ColumnMeta {
@@ -785,7 +783,7 @@ function HeaderCellContent({
785783
<div
786784
className={cn(
787785
"flex w-full items-center gap-1 overflow-hidden bg-background-dimmed px-2 py-1.5",
788-
"text-sm font-medium text-text-bright",
786+
"font-mono text-xs font-medium text-text-bright",
789787
alignment === "right" && "justify-end",
790788
canSort && "cursor-pointer select-none"
791789
)}
@@ -846,12 +844,31 @@ function HeaderCellContent({
846844
/**
847845
* Filter input cell for the filter row
848846
*/
849-
function FilterCell({ column, width }: { column: Column<RowData, unknown>; width: number }) {
847+
function FilterCell({
848+
column,
849+
width,
850+
shouldFocus,
851+
onFocused,
852+
}: {
853+
column: Column<RowData, unknown>;
854+
width: number;
855+
shouldFocus?: boolean;
856+
onFocused?: () => void;
857+
}) {
850858
const columnFilterValue = column.getFilterValue() as string;
859+
const inputRef = useRef<HTMLInputElement>(null);
860+
861+
useEffect(() => {
862+
if (shouldFocus && inputRef.current) {
863+
inputRef.current.focus();
864+
onFocused?.();
865+
}
866+
}, [shouldFocus, onFocused]);
851867

852868
return (
853869
<div className="flex items-center bg-background-dimmed px-1.5 pb-1" style={{ width }}>
854870
<DebouncedInput
871+
ref={inputRef}
855872
value={columnFilterValue ?? ""}
856873
onChange={(value) => column.setFilterValue(value)}
857874
placeholder="Filter..."
@@ -879,6 +896,8 @@ export const TSQLResultsTable = memo(function TSQLResultsTable({
879896
// State for column filters and filter row visibility
880897
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
881898
const [showFilters, setShowFilters] = useState(false);
899+
// Track which column's filter should be focused
900+
const [focusFilterColumn, setFocusFilterColumn] = useState<string | null>(null);
882901
// State for column sorting
883902
const [sorting, setSorting] = useState<SortingState>([]);
884903

@@ -971,7 +990,14 @@ export const TSQLResultsTable = memo(function TSQLResultsTable({
971990
<HeaderCellContent
972991
alignment={meta?.alignment ?? "left"}
973992
tooltip={meta?.outputColumn.description}
974-
onFilterClick={() => setShowFilters(!showFilters)}
993+
onFilterClick={() => {
994+
if (!showFilters) {
995+
setFocusFilterColumn(header.id);
996+
} else {
997+
setColumnFilters([]);
998+
}
999+
setShowFilters(!showFilters);
1000+
}}
9751001
showFilters={showFilters}
9761002
hasActiveFilter={!!header.column.getFilterValue()}
9771003
sortDirection={header.column.getIsSorted()}
@@ -1005,6 +1031,8 @@ export const TSQLResultsTable = memo(function TSQLResultsTable({
10051031
key={`filter-${header.id}`}
10061032
column={header.column}
10071033
width={header.getSize()}
1034+
shouldFocus={focusFilterColumn === header.id}
1035+
onFocused={() => setFocusFilterColumn(null)}
10081036
/>
10091037
))}
10101038
</tr>
@@ -1057,7 +1085,14 @@ export const TSQLResultsTable = memo(function TSQLResultsTable({
10571085
<HeaderCellContent
10581086
alignment={meta?.alignment ?? "left"}
10591087
tooltip={meta?.outputColumn.description}
1060-
onFilterClick={() => setShowFilters(!showFilters)}
1088+
onFilterClick={() => {
1089+
if (!showFilters) {
1090+
setFocusFilterColumn(header.id);
1091+
} else {
1092+
setColumnFilters([]);
1093+
}
1094+
setShowFilters(!showFilters);
1095+
}}
10611096
showFilters={showFilters}
10621097
hasActiveFilter={!!header.column.getFilterValue()}
10631098
sortDirection={header.column.getIsSorted()}
@@ -1091,6 +1126,8 @@ export const TSQLResultsTable = memo(function TSQLResultsTable({
10911126
key={`filter-${header.id}`}
10921127
column={header.column}
10931128
width={header.getSize()}
1129+
shouldFocus={focusFilterColumn === header.id}
1130+
onFocused={() => setFocusFilterColumn(null)}
10941131
/>
10951132
))}
10961133
</tr>

0 commit comments

Comments
 (0)