Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore/update filter sort table editor to new popover #25832

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { useUrlState } from 'hooks'
import update from 'immutability-helper'
import { isEqual } from 'lodash'
import { FilterIcon, Plus } from 'lucide-react'
import { KeyboardEvent, useCallback, useMemo, useState } from 'react'
import { Button, IconFilter, IconPlus, Popover } from 'ui'

import { formatFilterURLParams } from 'components/grid/SupabaseGrid.utils'
import type { Filter, SupaTable } from 'components/grid/types'
import {
Button,
Popover,
joshenlim marked this conversation as resolved.
Show resolved Hide resolved
PopoverContent_Shadcn_,
PopoverTrigger_Shadcn_,
Popover_Shadcn_,
} from 'ui'
import { FilterOperatorOptions } from './Filter.constants'
import FilterRow from './FilterRow'

Expand All @@ -16,38 +23,55 @@ export interface FilterPopoverProps {
}

const FilterPopover = ({ table, filters, setParams }: FilterPopoverProps) => {
const [open, setOpen] = useState(false)

const btnText =
(filters || []).length > 0
? `Filtered by ${filters.length} rule${filters.length > 1 ? 's' : ''}`
: 'Filter'

const onApplyFilters = (appliedFilters: Filter[]) => {
setParams((prevParams) => {
return {
...prevParams,
filter: appliedFilters.map((filter) => {
const selectedOperator = FilterOperatorOptions.find(
(option) => option.value === filter.operator
)

return `${filter.column}:${selectedOperator?.abbrev}:${filter.value}`
}),
}
})
}

return (
<Popover
size="large"
align="start"
className="sb-grid-filter-popover"
overlay={<FilterOverlay table={table} filters={filters} setParams={setParams} />}
>
<Button
asChild
type={(filters || []).length > 0 ? 'link' : 'text'}
icon={
<div className="text-foreground-light">
<IconFilter strokeWidth={1.5} />
</div>
}
>
<span>{btnText}</span>
</Button>
</Popover>
<Popover_Shadcn_ open={open} onOpenChange={setOpen} modal={false}>
<PopoverTrigger_Shadcn_ asChild>
<Button
asChild
joshenlim marked this conversation as resolved.
Show resolved Hide resolved
type={(filters || []).length > 0 ? 'link' : 'text'}
icon={<FilterIcon strokeWidth={1.5} className="text-foreground-light" />}
>
<span>{btnText}</span>
</Button>
</PopoverTrigger_Shadcn_>
<PopoverContent_Shadcn_ className="p-0 w-96" side="bottom" align="start">
<FilterOverlay table={table} filters={filters} onApplyFilters={onApplyFilters} />
</PopoverContent_Shadcn_>
</Popover_Shadcn_>
)
}

export default FilterPopover

export interface FilterOverlayProps extends FilterPopoverProps {}
interface FilterOverlayProps {
table: SupaTable
filters: string[]
onApplyFilters: (filter: Filter[]) => void
}

const FilterOverlay = ({ table, filters: filtersFromUrl, setParams }: FilterOverlayProps) => {
const FilterOverlay = ({ table, filters: filtersFromUrl, onApplyFilters }: FilterOverlayProps) => {
const initialFilters = useMemo(
() => formatFilterURLParams((filtersFromUrl as string[]) ?? []),
[filtersFromUrl]
Expand Down Expand Up @@ -87,25 +111,8 @@ const FilterOverlay = ({ table, filters: filtersFromUrl, setParams }: FilterOver
)
}, [])

function onApplyFilter() {
setParams((prevParams) => {
return {
...prevParams,
filter: filters.map((filter) => {
const selectedOperator = FilterOperatorOptions.find(
(option) => option.value === filter.operator
)

return `${filter.column}:${selectedOperator?.abbrev}:${filter.value}`
}),
}
})
}

function handleEnterKeyDown(event: KeyboardEvent<HTMLInputElement>) {
if (event.key === 'Enter') {
onApplyFilter()
}
if (event.key === 'Enter') onApplyFilters(filters)
}

return (
Expand All @@ -131,10 +138,14 @@ const FilterOverlay = ({ table, filters: filtersFromUrl, setParams }: FilterOver
</div>
<Popover.Separator />
joshenlim marked this conversation as resolved.
Show resolved Hide resolved
<div className="px-3 flex flex-row justify-between">
<Button icon={<IconPlus />} type="text" onClick={onAddFilter}>
<Button icon={<Plus />} type="text" onClick={onAddFilter}>
Add filter
</Button>
<Button disabled={isEqual(filters, initialFilters)} type="default" onClick={onApplyFilter}>
<Button
disabled={isEqual(filters, initialFilters)}
type="default"
onClick={() => onApplyFilters(filters)}
>
Apply filter
</Button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Button, IconChevronDown, IconX, Input } from 'ui'
import { DropdownControl } from 'components/grid/components/common'
import type { Filter, FilterOperator, SupaTable } from 'components/grid/types'
import { FilterOperatorOptions } from './Filter.constants'
import { X } from 'lucide-react'

export interface FilterRowProps {
table: SupaTable
Expand All @@ -29,7 +30,7 @@ const FilterRow = ({ table, filter, filterIdx, onChange, onDelete, onKeyDown }:
: 'Enter a value'

return (
<div className="sb-grid-filter-row px-3">
<div className="flex w-full items-center justify-between gap-x-1 px-3">
<DropdownControl
align="start"
options={columnOptions}
Expand Down Expand Up @@ -84,9 +85,9 @@ const FilterRow = ({ table, filter, filterIdx, onChange, onDelete, onKeyDown }:
onKeyDown={onKeyDown}
/>
<Button
icon={<IconX strokeWidth={1.5} size={14} />}
size="tiny"
type="text"
className="px-1"
icon={<X strokeWidth={1.5} size={14} />}
onClick={() => onDelete(filterIdx)}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { useUrlState } from 'hooks'
import update from 'immutability-helper'
import { isEqual } from 'lodash'
import { ChevronDown, List } from 'lucide-react'
import { useCallback, useMemo, useState } from 'react'
import { Button, IconChevronDown, IconList, Popover } from 'ui'

import { DropdownControl } from 'components/grid/components/common'
import { formatSortURLParams } from 'components/grid/SupabaseGrid.utils'
import { DropdownControl } from 'components/grid/components/common'
import type { Sort, SupaTable } from 'components/grid/types'
import {
Button,
Popover,
PopoverContent_Shadcn_,
PopoverTrigger_Shadcn_,
Popover_Shadcn_,
} from 'ui'
import SortRow from './SortRow'

export interface SortPopoverProps {
Expand All @@ -16,38 +23,49 @@ export interface SortPopoverProps {
}

const SortPopover = ({ table, sorts, setParams }: SortPopoverProps) => {
const [open, setOpen] = useState(false)

const btnText =
(sorts || []).length > 0
? `Sorted by ${sorts.length} rule${sorts.length > 1 ? 's' : ''}`
: 'Sort'

const onApplySorts = (appliedSorts: Sort[]) => {
setParams((prevParams) => {
return {
...prevParams,
sort: appliedSorts.map((sort) => `${sort.column}:${sort.ascending ? 'asc' : 'desc'}`),
}
})
}

return (
<Popover
size="large"
align="start"
className="sb-grid-sort-popover"
overlay={<SortOverlay table={table} sorts={sorts} setParams={setParams} />}
>
<Button
asChild
type={(sorts || []).length > 0 ? 'link' : 'text'}
icon={
<div className="text-foreground-light">
<IconList strokeWidth={1.5} />
</div>
}
>
<span>{btnText}</span>
</Button>
</Popover>
<Popover_Shadcn_ modal={false} open={open} onOpenChange={setOpen}>
<PopoverTrigger_Shadcn_ asChild>
<Button
asChild
joshenlim marked this conversation as resolved.
Show resolved Hide resolved
type={(sorts || []).length > 0 ? 'link' : 'text'}
icon={<List strokeWidth={1.5} className="text-foreground-light" />}
>
<span>{btnText}</span>
</Button>
</PopoverTrigger_Shadcn_>
<PopoverContent_Shadcn_ className="p-0 w-96" side="bottom" align="start">
<SortOverlay table={table} sorts={sorts} onApplySorts={onApplySorts} />
</PopoverContent_Shadcn_>
</Popover_Shadcn_>
)
}

export default SortPopover

export interface SortOverlayProps extends SortPopoverProps {}
export interface SortOverlayProps {
table: SupaTable
sorts: string[]
onApplySorts: (sorts: Sort[]) => void
}

const SortOverlay = ({ table, sorts: sortsFromUrl, setParams }: SortOverlayProps) => {
const SortOverlay = ({ table, sorts: sortsFromUrl, onApplySorts }: SortOverlayProps) => {
const initialSorts = useMemo(
() => formatSortURLParams((sortsFromUrl as string[]) ?? []),
[sortsFromUrl]
Expand All @@ -68,15 +86,6 @@ const SortOverlay = ({ table, sorts: sortsFromUrl, setParams }: SortOverlayProps
setSorts([...sorts, { column: columnName as string, ascending: true }])
}

function onApplySort() {
setParams((prevParams) => {
return {
...prevParams,
sort: sorts.map((sort) => `${sort.column}:${sort.ascending ? 'asc' : 'desc'}`),
}
})
}

const onDeleteSort = useCallback((column: string) => {
setSorts((currentSorts) => currentSorts.filter((sort) => sort.column !== column))
}, [])
Expand Down Expand Up @@ -137,7 +146,7 @@ const SortOverlay = ({ table, sorts: sortsFromUrl, setParams }: SortOverlayProps
<Button
asChild
type="text"
iconRight={<IconChevronDown />}
iconRight={<ChevronDown />}
className="sb-grid-dropdown__item-trigger"
>
<span>{`Pick ${sorts.length > 1 ? 'another' : 'a'} column to sort by`}</span>
Expand All @@ -147,7 +156,11 @@ const SortOverlay = ({ table, sorts: sortsFromUrl, setParams }: SortOverlayProps
<p className="text-sm text-foreground-light">All columns have been added</p>
)}
<div className="flex items-center">
<Button disabled={isEqual(sorts, initialSorts)} type="default" onClick={onApplySort}>
<Button
disabled={isEqual(sorts, initialSorts)}
type="default"
onClick={() => onApplySorts(sorts)}
>
Apply sorting
</Button>
</div>
Expand Down
24 changes: 0 additions & 24 deletions apps/studio/styles/grid.scss
Original file line number Diff line number Diff line change
Expand Up @@ -562,30 +562,6 @@
@apply block;
}

/*
header/filter/FilterDropdown
*/

.sb-grid-filter-popover {
@apply overflow-visible;
}

.sb-grid-filter-popover__misc {
@apply py-2;
}

.sb-grid-filter-popover__misc__text {
@apply block;
}

/*
header/filter/FilterRow
*/

.sb-grid-filter-row {
@apply flex w-full items-center justify-between space-x-1;
}

/*
header/sort/SortDropdown
*/
Expand Down