Skip to content

Commit 34a09ab

Browse files
committed
updates
1 parent 1074ff9 commit 34a09ab

1 file changed

Lines changed: 30 additions & 46 deletions

File tree

apps/app/src/routes/_protected/connection/$resourceId/table/-components/toolbar/filter-search-bar.tsx

Lines changed: 30 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,17 @@ type Stage =
4040
| { step: 'operator'; column: string }
4141
| { step: 'value'; column: string; ref: Filter }
4242

43-
// Unified filter field: chips live inline in an input-styled container; typing
44-
// suggests column filters or sends the text to AI. Backspace on an empty input
45-
// removes the last chip.
43+
function splitParts(value: string) {
44+
return value
45+
.split(',')
46+
.map(part => part.trim())
47+
.filter(part => part !== '')
48+
}
49+
50+
function operatorMatches(filter: Filter, text: string) {
51+
return filter.label.toLowerCase().includes(text) || filter.operator.toLowerCase().includes(text)
52+
}
53+
4654
export function FilterSearchBar({ table, schema }: { table: string; schema: string }) {
4755
const isOnline = useSubscription(appStore, { selector: state => state.isOnline })
4856
const { connectionResource } = useRouteContext()
@@ -52,25 +60,21 @@ export function FilterSearchBar({ table, schema }: { table: string; schema: stri
5260
const query = useSubscription(store, { selector: state => state.prompt })
5361
const [isFocused, setIsFocused] = useState(false)
5462
const [stage, setStage] = useState<Stage>({ step: 'idle' })
55-
// cmdk keeps its highlight by item value; when the stage swaps the whole item
56-
// list the old value no longer exists and nothing is highlighted — so we
57-
// control it and point it at the first item on every transition
5863
const [highlighted, setHighlighted] = useState('')
5964
const [freeAiUsage, setFreeAiUsage] = useState<{
6065
remaining: number
6166
max: number
6267
} | null>(null)
6368

64-
const setQuery = (value: string) => {
69+
const setPrompt = (value: string) =>
6570
store.set(state => ({ ...state, prompt: value }) satisfies typeof state)
6671

72+
const setQuery = (value: string) => {
73+
setPrompt(value)
74+
6775
const trimmed = value.trim().toLowerCase()
6876
if (stage.step === 'operator') {
69-
const first = SQL_FILTERS_LIST.find(
70-
filter =>
71-
filter.label.toLowerCase().includes(trimmed) ||
72-
filter.operator.toLowerCase().includes(trimmed),
73-
)
77+
const first = SQL_FILTERS_LIST.find(filter => operatorMatches(filter, trimmed))
7478
setHighlighted(first ? `operator:${first.operator.toLowerCase()}` : '')
7579
} else if (stage.step === 'value') {
7680
setHighlighted('apply-value')
@@ -82,6 +86,12 @@ export function FilterSearchBar({ table, schema }: { table: string; schema: stri
8286
const setFilters = (updater: (filters: ActiveFilter[]) => ActiveFilter[]) =>
8387
store.set(state => ({ ...state, filters: updater(state.filters) }) satisfies typeof state)
8488

89+
const resetStage = () => {
90+
setStage({ step: 'idle' })
91+
setHighlighted('')
92+
setPrompt('')
93+
}
94+
8595
const { columns } = useTableColumnsContext()
8696
const { data: enums } = useQuery(resourceEnumsQueryOptions({ connectionResource }))
8797

@@ -104,7 +114,6 @@ export function FilterSearchBar({ table, schema }: { table: string; schema: stri
104114
values: filter.values,
105115
}) satisfies Omit<ActiveFilter, 'ref'> & { ref?: ActiveFilter['ref'] },
106116
)
107-
// For future updates if we'll have new filters
108117
.filter(f => !!f.ref) as ActiveFilter[],
109118
}) satisfies typeof state,
110119
)
@@ -166,7 +175,7 @@ export function FilterSearchBar({ table, schema }: { table: string; schema: stri
166175

167176
const pickColumn = (columnId: string) => {
168177
setStage({ step: 'operator', column: columnId })
169-
store.set(state => ({ ...state, prompt: '' }) satisfies typeof state)
178+
setPrompt('')
170179
setHighlighted(`operator:${SQL_FILTERS_LIST[0]!.operator.toLowerCase()}`)
171180
inputRef.current?.focus()
172181
}
@@ -175,45 +184,29 @@ export function FilterSearchBar({ table, schema }: { table: string; schema: stri
175184
if (stage.step !== 'operator') return
176185
if (ref.hasValue === false) {
177186
setFilters(current => [...current, { column: stage.column, ref, values: [] }])
178-
setStage({ step: 'idle' })
179-
setHighlighted('')
187+
resetStage()
180188
} else {
181189
setStage({ step: 'value', column: stage.column, ref })
182190
setHighlighted('apply-value')
191+
setPrompt('')
183192
}
184-
store.set(state => ({ ...state, prompt: '' }) satisfies typeof state)
185193
inputRef.current?.focus()
186194
}
187195

188196
const applyValue = () => {
189197
if (stage.step !== 'value') return
190-
const values = stage.ref.isArray
191-
? query
192-
.split(',')
193-
.map(value => value.trim())
194-
.filter(value => value !== '')
195-
: [query]
198+
const values = stage.ref.isArray ? splitParts(query) : [query]
196199
setFilters(current => [...current, { column: stage.column, ref: stage.ref, values }])
197-
setStage({ step: 'idle' })
198-
setHighlighted('')
199-
store.set(state => ({ ...state, prompt: '' }) satisfies typeof state)
200+
resetStage()
200201
inputRef.current?.focus()
201202
}
202203

203-
// Suggested values for the value stage — enum members, or true/false for
204-
// boolean columns
205204
const stageColumn =
206205
stage.step === 'value' ? columns?.find(column => column.id === stage.column) : undefined
207206
const suggestedValues =
208207
stageColumn?.availableValues ??
209208
(stageColumn?.uiType === 'boolean' ? ['true', 'false'] : undefined)
210-
const committedParts =
211-
stage.step === 'value' && stage.ref.isArray
212-
? query
213-
.split(',')
214-
.map(part => part.trim())
215-
.filter(part => part !== '')
216-
: []
209+
const committedParts = stage.step === 'value' && stage.ref.isArray ? splitParts(query) : []
217210
const valueFilterText = (
218211
stage.step === 'value' && stage.ref.isArray ? (query.split(',').at(-1) ?? '') : query
219212
).trim()
@@ -224,29 +217,21 @@ export function FilterSearchBar({ table, schema }: { table: string; schema: stri
224217
const pickSuggestedValue = (value: string) => {
225218
if (stage.step !== 'value') return
226219
if (stage.ref.isArray) {
227-
// Multi-value operators collect picks into the comma list (click again to
228-
// remove); Enter on Apply commits the whole list
229220
const committed = valueFilterText ? committedParts.slice(0, -1) : committedParts
230221
const next = committed.includes(value)
231222
? committed.filter(part => part !== value)
232223
: [...committed, value]
233224
setQuery(next.join(', '))
234225
} else {
235226
setFilters(current => [...current, { column: stage.column, ref: stage.ref, values: [value] }])
236-
setStage({ step: 'idle' })
237-
setHighlighted('')
238-
store.set(state => ({ ...state, prompt: '' }) satisfies typeof state)
227+
resetStage()
239228
}
240229
inputRef.current?.focus()
241230
}
242231

243232
const matchingOperators = SQL_FILTERS_GROUPED.map(group => ({
244233
...group,
245-
filters: group.filters.filter(
246-
filter =>
247-
filter.label.toLowerCase().includes(trimmedQuery.toLowerCase()) ||
248-
filter.operator.toLowerCase().includes(trimmedQuery.toLowerCase()),
249-
),
234+
filters: group.filters.filter(filter => operatorMatches(filter, trimmedQuery.toLowerCase())),
250235
})).filter(group => group.filters.length > 0)
251236

252237
const placeholder =
@@ -374,7 +359,6 @@ export function FilterSearchBar({ table, schema }: { table: string; schema: stri
374359
absolute bottom-full left-0 z-30 mb-2 w-full overflow-hidden
375360
rounded-xl bg-popover p-1 shadow-lg ring-1 ring-foreground/4
376361
"
377-
// Keep the input focused while clicking suggestions
378362
onMouseDown={e => e.preventDefault()}
379363
>
380364
<CommandList className="max-h-64">

0 commit comments

Comments
 (0)