diff --git a/packages/web_ui/src/components/UsersFilters.tsx b/packages/web_ui/src/components/UsersFilters.tsx index ca54014c..3aeae466 100644 --- a/packages/web_ui/src/components/UsersFilters.tsx +++ b/packages/web_ui/src/components/UsersFilters.tsx @@ -1,5 +1,5 @@ import React, { useRef } from "react"; -import { InputRef, Input, Space, Segmented, Button, Typography, Tag } from "antd"; +import { InputRef, Input, Space, Segmented, Typography, Tag } from "antd"; import type { FilterDropdownProps } from "antd/es/table/interface"; import { CheckOutlined, CloseOutlined, MinusOutlined } from "@ant-design/icons"; import { UserDetails } from "@clusterio/lib"; @@ -135,13 +135,16 @@ export function useUserFilter( ? decodeFilter(String(selectedKeys[0])) : {}; - // Update the live filter as the user types/toggles; the URL is only written - // when the dropdown closes (see filterDropdownProps.onOpenChange below). - function update(next: Partial) { + // Update the live filter as the user types/toggles; the URL is only written when the + // dropdown closes (see onOpenChange), or immediately when `commit` is set (e.g. clear). + function update(next: Partial, commit = false) { const encoded = encodeFilter({ ...filter, ...next }); const values = encoded === "{}" ? null : [encoded]; setSelectedKeys(values ?? []); tableState.setFilter(columnKey, values); + if (commit) { + tableState.commitFilter(columnKey, values); + } } return ( @@ -154,7 +157,7 @@ export function useUserFilter( allowClear onSearch={() => close()} onChange={(e) => update({ search: e.target.value !== "" ? e.target.value : undefined })} - onClear={() => update({ search: undefined })} + onClear={() => update({ search: undefined }, true)} /> {withStatus &&<> diff --git a/packages/web_ui/src/util/useColumnSearch.tsx b/packages/web_ui/src/util/useColumnSearch.tsx index 4fe3451d..37c4833c 100644 --- a/packages/web_ui/src/util/useColumnSearch.tsx +++ b/packages/web_ui/src/util/useColumnSearch.tsx @@ -47,6 +47,8 @@ export default function useColumnSearch( onClear={() => { setSelectedKeys([]); tableState.setFilter(columnKey, null); + // Clearing is a deliberate action, so write it to the URL right away. + tableState.commitFilter(columnKey, null); }} /> diff --git a/packages/web_ui/src/util/useTableQueryState.ts b/packages/web_ui/src/util/useTableQueryState.ts index b7e43e3e..8ee1c1a5 100644 --- a/packages/web_ui/src/util/useTableQueryState.ts +++ b/packages/web_ui/src/util/useTableQueryState.ts @@ -35,8 +35,8 @@ export interface TableQueryState { filteredValue: (columnKey: string) => string[] | null; /** Set a column's live filter — filters the table immediately without writing the URL. */ setFilter: (columnKey: string, values: string[] | null) => void; - /** Commit a column's live filter to the URL; call when its filter dropdown closes. */ - commitFilter: (columnKey: string) => void; + /** Commit a column's filter to the URL: pass `values` to write now (e.g. clear), omit to commit the override. */ + commitFilter: (columnKey: string, values?: string[] | null) => void; /** Controlled pagination ({@link current}/{@link pageSize}), or `false` when disabled. */ pagination: false | TablePaginationConfig; } @@ -114,17 +114,22 @@ export default function useTableQueryState(options: TableQueryStateOptions): setLiveFilters(prev => ({ ...prev, [columnKey]: values })); }, - commitFilter(columnKey) { - // Only write the URL when a live edit is pending for this column. - if (!(columnKey in liveFilters)) { + commitFilter(columnKey, values) { + // Explicit values (e.g. from the clear button) are written immediately; otherwise + // commit the current live override, if any (e.g. when the dropdown closes). + let effective = values; + if (effective === undefined && columnKey in liveFilters) { + effective = liveFilters[columnKey]; + } + if (effective === undefined) { return; } const next = new URLSearchParams(params); const codec = codecs[columnKey]; if (codec) { - codec.encode(next, liveFilters[columnKey], prefix); + codec.encode(next, effective, prefix); } else { - applyFilters(next, prefix, { [columnKey]: liveFilters[columnKey] }); + applyFilters(next, prefix, { [columnKey]: effective }); } // Avoid pushing a duplicate history entry when nothing actually changed. if (next.toString() !== paramsString) {