A list already shows the values people want to filter by. Let them select one. Select shared in a row, and the field gets is:shared. Select it again, and the filter goes away. The field shows every filter that runs, so the person can read it and edit it like typed text.
1 import { Button } from "@ngrok/mantle/button" ; 2 import { DataTable , createColumnHelper , tableFeatures , useTable } from "@ngrok/mantle/data-table" ; 3 import { Empty } from "@ngrok/mantle/empty" ; 4 import { 5 filterItemsByQuery , 6 parseFilterQuery , 7 queryUsesQualifiers , 8 toggleFacet , 9 } from "@ngrok/mantle/query-filter" ; 10 import { Query } from "@ngrok/mantle/query" ; 11 import { MagnifyingGlassIcon } from "@phosphor-icons/react/MagnifyingGlass" ; 12 import { useDeferredValue , useState } from "react" ; 13 import { type Project , projectFilterGrammar , projects } from "~/features/query-demos" ; 14 15 type ProjectState = "shared" | "private" | "archived" ; 16 17 /** The states a project row shows. */ 18 function projectStates ( project : Project ) : ProjectState [] { ⋯ 19 const visibility = project . shared ? "shared" : "private" ; 20 return project . archived ? [ visibility , "archived" ] : [ visibility ]; 21 } 22 23 /** Whether the query already keeps only the projects in this state. */ 24 function hasStateFilter ( query : string , state : ProjectState ) { ⋯ 25 return parseFilterQuery ( projectFilterGrammar , query ) .some ( 26 ( token ) => 27 token . kind === "facet" && 28 token . key === "is" && 29 ! token . negated && 30 token . members . length === 1 && 31 token . members [ 0 ]?. value === state , 32 ); 33 } 34 35 const features = tableFeatures ({}); 36 const columnHelper = createColumnHelper < typeof features , Project >(); 37 38 export function FilterFromAValueDemo () { ⋯ 39 const [ query , setQuery ] = useState ( "" ); 40 const deferredQuery = useDeferredValue ( query ); 41 const visibleProjects = filterItemsByQuery ({ ⋯ 42 grammar : projectFilterGrammar , 43 items : projects , 44 query : deferredQuery , 45 }); 46 47 function toggleState ( state : ProjectState ) { ⋯ 48 setQuery (( current ) => 49 toggleFacet ({ ⋯ 50 grammar : projectFilterGrammar , 51 query : current , 52 facet : { key : "is" , value : state , negated : false } , 53 }) , 54 ); 55 } 56 57 const columns = columnHelper .columns ([ ⋯ 58 columnHelper .accessor ( "name" , { ⋯ 59 id : "name" , 60 header : () => < DataTable.Header >Project</ DataTable.Header > , 61 cell : ( props ) => ( 62 < DataTable.Cell className = "text-strong" translate = "no" > ⋯ 63 { props .getValue ()} 64 </ DataTable.Cell > 65 ) , 66 }) , 67 columnHelper .display ({ ⋯ 68 id : "state" , 69 header : () => < DataTable.Header >State</ DataTable.Header > , 70 cell : ( props ) => ( 71 < DataTable.Cell > ⋯ 72 < span className = "flex flex-wrap gap-1" > ⋯ 73 { projectStates ( props . row . original ) .map (( state ) => ( 74 < Button ⋯ 75 key = { state } 76 type = "button" 77 appearance = "outlined" 78 intent = "neutral" 79 size = "xs" 80 className = "aria-pressed:border-accent-600 aria-pressed:bg-accent-500/10 aria-pressed:text-accent-700" 81 aria-pressed = { hasStateFilter ( query , state )} 82 onClick = {() => toggleState ( state )} 83 > 84 { state } 85 </ Button > 86 ))} 87 </ span > 88 </ DataTable.Cell > 89 ) , 90 }) , 91 ]); 92 const table = useTable ({ features , data : visibleProjects , columns }); 93 const rows = table .getRowModel (). rows ; 94 95 return ( 96 < div className = "w-full space-y-3" > ⋯ 97 < Query.Root ⋯ 98 grammar = { projectFilterGrammar } 99 items = { projects } 100 value = { query } 101 onValueChange = { setQuery } 102 > 103 < Query.Input ⋯ 104 aria-label = "Filter projects" 105 placeholder = "Filter projects, or select a state in the table" 106 /> 107 < Query.Content > ⋯ 108 < Query.List >{( suggestion ) => < Query.Item suggestion = { suggestion } />}</ Query.List > 109 < Query.Empty /> 110 < Query.Footer > ⋯ 111 < Query.Legend /> 112 < Query.KeyboardHints /> 113 </ Query.Footer > 114 </ Query.Content > 115 </ Query.Root > 116 < DataTable.Root table = { table }> ⋯ 117 < DataTable.Head /> 118 < DataTable.Body > ⋯ 119 { rows . length > 0 ? ( 120 rows .map (( row ) => < DataTable.Row key = { row . id } row = { row } />) 121 ) : ( 122 < DataTable.EmptyRow > ⋯ 123 < Empty.Root > ⋯ 124 < Empty.Icon svg = {< MagnifyingGlassIcon />} /> 125 < Empty.Title >No projects match this filter</ Empty.Title > 126 < Empty.Description > ⋯ 127 { queryUsesQualifiers ( projectFilterGrammar , deferredQuery ) 128 ? "Remove a filter to see more projects." 129 : "Check the spelling, or try a filter like is:shared." } 130 </ Empty.Description > 131 < Empty.Actions > ⋯ 132 < Button ⋯ 133 type = "button" 134 appearance = "outlined" 135 intent = "neutral" 136 onClick = {() => setQuery ( "" )} 137 > 138 Clear filter 139 </ Button > 140 </ Empty.Actions > 141 </ Empty.Root > 142 </ DataTable.EmptyRow > 143 )} 144 </ DataTable.Body > 145 </ DataTable.Root > 146 </ div > 147 ); 148 }