Skip to content

Commit

Permalink
Chore/update filter sort table editor to new popover (#25832)
Browse files Browse the repository at this point in the history
* Update table editor FilterPopover to new popover component

* Update table editor SortPopover to new popover component

* Don't close popovers

* Address feedback
  • Loading branch information
joshenlim committed May 13, 2024
1 parent d025e0f commit 88bbe70
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 106 deletions.
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,
PopoverContent_Shadcn_,
PopoverSeparator_Shadcn_,
PopoverTrigger_Shadcn_,
Popover_Shadcn_,
} from 'ui'
import { FilterOperatorOptions } from './Filter.constants'
import FilterRow from './FilterRow'

Expand All @@ -16,38 +23,54 @@ 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
type={(filters || []).length > 0 ? 'link' : 'text'}
icon={<FilterIcon strokeWidth={1.5} className="text-foreground-light" />}
>
{btnText}
</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 +110,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 @@ -129,12 +135,16 @@ const FilterOverlay = ({ table, filters: filtersFromUrl, setParams }: FilterOver
</div>
)}
</div>
<Popover.Separator />
<PopoverSeparator_Shadcn_ />
<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
83 changes: 47 additions & 36 deletions apps/studio/components/grid/components/header/sort/SortPopover.tsx
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,
PopoverContent_Shadcn_,
PopoverSeparator_Shadcn_,
PopoverTrigger_Shadcn_,
Popover_Shadcn_,
} from 'ui'
import SortRow from './SortRow'

export interface SortPopoverProps {
Expand All @@ -16,38 +23,48 @@ 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
type={(sorts || []).length > 0 ? 'link' : 'text'}
icon={<List strokeWidth={1.5} className="text-foreground-light" />}
>
{btnText}
</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 +85,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 @@ -125,7 +133,7 @@ const SortOverlay = ({ table, sorts: sortsFromUrl, setParams }: SortOverlayProps
</div>
)}

<Popover.Separator />
<PopoverSeparator_Shadcn_ />
<div className="px-3 flex flex-row justify-between">
{columns && columns.length > 0 ? (
<DropdownControl
Expand All @@ -135,19 +143,22 @@ const SortOverlay = ({ table, sorts: sortsFromUrl, setParams }: SortOverlayProps
align="start"
>
<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>
Pick {sorts.length > 1 ? 'another' : 'a'} column to sort by
</Button>
</DropdownControl>
) : (
<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
1 change: 1 addition & 0 deletions packages/ui/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export {
PopoverTrigger as PopoverTrigger_Shadcn_,
PopoverContent as PopoverContent_Shadcn_,
PopoverAnchor as PopoverAnchor_Shadcn_,
PopoverSeparator as PopoverSeparator_Shadcn_,
} from './src/components/shadcn/ui/popover'

export {
Expand Down
9 changes: 8 additions & 1 deletion packages/ui/src/components/shadcn/ui/popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,11 @@ const PopoverContent = React.forwardRef<
)
PopoverContent.displayName = 'PopoverContent'

export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
const PopoverSeparator = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, children, ...props }, ref) => (
<div ref={ref} {...props} className={cn('w-full h-px bg-border-overlay', className)} />
)
)
PopoverSeparator.displayName = 'PopoverSeparator'

export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor, PopoverSeparator }

0 comments on commit 88bbe70

Please sign in to comment.