- {formattedFilters.map((filter, index) => (
+ {filters.map((filter, index) => (
))}
- {formattedFilters.length == 0 && (
+ {filters.length == 0 && (
No filters applied to this view
Add a column below to filter the view
@@ -66,10 +118,13 @@ const Filter: FC = () => {
)}
-
+
} type="text" onClick={onAddFilter}>
Add filter
+
)
diff --git a/studio/components/grid/components/header/filter/FilterRow.tsx b/studio/components/grid/components/header/filter/FilterRow.tsx
index 6e16d1f8864..6a1a977f427 100644
--- a/studio/components/grid/components/header/filter/FilterRow.tsx
+++ b/studio/components/grid/components/header/filter/FilterRow.tsx
@@ -1,113 +1,26 @@
-import { debounce } from 'lodash'
-import { FC, memo, useState, useEffect, ChangeEvent, useCallback } from 'react'
+import { FC, memo } from 'react'
import { Button, Input, IconChevronDown, IconX } from 'ui'
-import { useUrlState } from 'hooks'
-import { Filter } from 'components/grid/types'
+import { Filter, FilterOperator } from 'components/grid/types'
import { DropdownControl } from 'components/grid/components/common'
import { useTrackedState } from 'components/grid/store'
import { FilterOperatorOptions } from './Filter.constants'
interface Props {
- filter: Filter
filterIdx: number
+ filter: Filter
+ onChange: (index: number, filter: Filter) => void
+ onDelete: (index: number) => void
}
-// [Area of improvement] Input field loses focus after the debounce (because of useUrlState?)
-const FilterRow: FC
= ({ filter, filterIdx }) => {
+const FilterRow: FC = ({ filter, filterIdx, onChange, onDelete }) => {
const state = useTrackedState()
- const [{ filter: filters }, setParams] = useUrlState({ arrayKeys: ['filter'] })
const column = state.table?.columns.find((x) => x.name === filter.column)
const columnOptions =
state.table?.columns?.map((x) => {
return { value: x.name, label: x.name, postLabel: x.dataType }
}) || []
- const [filterValue, setFilterValue] = useState(filter.value)
-
- useEffect(() => {
- setFilterValue(filter.value)
- }, [filterIdx])
-
- function onRemoveFilter() {
- setParams((prevParams) => {
- const existingFilters = (prevParams?.filter ?? []) as string[]
- const updatedFilters = existingFilters.filter((filter: string, idx: number) => {
- if (idx !== filterIdx) return filter
- })
- return {
- ...prevParams,
- filter: updatedFilters,
- }
- })
- }
-
- function onColumnChange(column: string | number) {
- setParams((prevParams) => {
- const existingFilters = (prevParams?.filter ?? []) as string[]
- const updatedFilters = existingFilters.map((filter: string, idx: number) => {
- if (idx === filterIdx) {
- const [_, operator, ...value] = filter.split(':')
- const formattedValue = value.join(':')
- return `${column}:${operator}:${formattedValue}`
- } else {
- return filter
- }
- })
- return {
- ...prevParams,
- filter: updatedFilters,
- }
- })
- }
-
- function onOperatorChange(operator: string | number) {
- setParams((prevParams) => {
- const existingFilters = (prevParams?.filter ?? []) as string[]
- const updatedFilters = existingFilters.map((filter: string, idx: number) => {
- if (idx === filterIdx) {
- const [column, _, ...value] = filter.split(':')
- const formattedValue = value.join(':')
- const selectedOperator = FilterOperatorOptions.find((option) => option.value === operator)
- return `${column}:${selectedOperator?.abbrev}:${formattedValue}`
- } else {
- return filter
- }
- })
- return {
- ...prevParams,
- filter: updatedFilters,
- }
- })
- }
-
- function onValueChange(event: ChangeEvent) {
- const value = event.target.value
- setFilterValue(value)
- debounceHandler({
- filterIdx,
- value: { ...filter, value: value },
- })
- }
-
- const updateFilterValue = (payload: { filterIdx: number; value: Filter }) => {
- setParams((prevParams) => {
- const existingFilters = (prevParams?.filter ?? []) as string[]
- const updatedFilters = existingFilters.map((filter: string, idx: number) => {
- if (idx === filterIdx) {
- const [column, operator] = filter.split(':')
- return `${column}:${operator}:${payload.value.value}`
- } else {
- return filter
- }
- })
- return {
- ...prevParams,
- filter: updatedFilters,
- }
- })
- }
- const debounceHandler = useCallback(debounce(updateFilterValue, 600), [filters])
const placeholder =
column?.format === 'timestamptz'
@@ -118,7 +31,11 @@ const FilterRow: FC = ({ filter, filterIdx }) => {
return (
-
+ onChange(filterIdx, { ...filter, column: nextColumn as string })}
+ >
-
+
+ onChange(filterIdx, {
+ ...filter,
+ operator: nextOperator as FilterOperator,
+ })
+ }
+ >
)
diff --git a/studio/components/grid/components/header/sort/SortPopover.tsx b/studio/components/grid/components/header/sort/SortPopover.tsx
index 65e778b1237..f2177bde207 100644
--- a/studio/components/grid/components/header/sort/SortPopover.tsx
+++ b/studio/components/grid/components/header/sort/SortPopover.tsx
@@ -1,10 +1,13 @@
-import React, { FC } from 'react'
+import React, { FC, useState, useMemo, useCallback } from 'react'
+import { isEqual } from 'lodash'
+import update from 'immutability-helper'
import { Button, IconList, IconChevronDown, Popover } from 'ui'
-
import { useUrlState } from 'hooks'
+
import SortRow from './SortRow'
import { useTrackedState } from 'components/grid/store'
import { DropdownControl } from 'components/grid/components/common'
+import { Sort } from 'components/grid/types'
import { formatSortURLParams } from 'components/grid/SupabaseGrid.utils'
const SortPopover: FC = () => {
@@ -15,7 +18,7 @@ const SortPopover: FC = () => {
: 'Sort'
return (
- }>
+ }>
0 ? 'link' : 'text'}
@@ -32,14 +35,18 @@ const SortPopover: FC = () => {
}
export default SortPopover
-const Sort: FC = () => {
+const SortOverlay: FC = () => {
const state = useTrackedState()
- const [{ sort: sorts }, setParams] = useUrlState({ arrayKeys: ['sort'] })
- const formattedSorts = formatSortURLParams(sorts as string[])
+ const [{ sort: sortsFromUrl }, setParams] = useUrlState({ arrayKeys: ['sort'] })
+ const initialSorts = useMemo(
+ () => formatSortURLParams((sortsFromUrl as string[]) ?? []),
+ [sortsFromUrl]
+ )
+ const [sorts, setSorts] = useState(initialSorts)
const columns = state?.table?.columns!.filter((x) => {
- const found = formattedSorts.find((y) => y.column == x.name)
+ const found = sorts.find((y) => y.column == x.name)
return !found
})
@@ -49,21 +56,59 @@ const Sort: FC = () => {
}) || []
function onAddSort(columnName: string | number) {
+ setSorts([...sorts, { column: columnName as string, ascending: true }])
+ }
+
+ function onApplySort() {
setParams((prevParams) => {
- const existingSorts = (prevParams?.sort ?? []) as string[]
return {
...prevParams,
- sort: existingSorts.concat([`${columnName}:asc`]),
+ sort: sorts.map((sort) => `${sort.column}:${sort.ascending ? 'asc' : 'desc'}`),
}
})
}
+ const onDeleteSort = useCallback((column: string) => {
+ setSorts((currentSorts) => currentSorts.filter((sort) => sort.column !== column))
+ }, [])
+
+ const onToggleSort = useCallback((column: string, ascending: boolean) => {
+ setSorts((currentSorts) => {
+ const idx = currentSorts.findIndex((x) => x.column === column)
+
+ return update(currentSorts, {
+ [idx]: {
+ $merge: { ascending },
+ },
+ })
+ })
+ }, [])
+
+ const onDragSort = useCallback((dragIndex: number, hoverIndex: number) => {
+ setSorts((currentSort) =>
+ update(currentSort, {
+ $splice: [
+ [dragIndex, 1],
+ [hoverIndex, 0, currentSort[dragIndex]],
+ ],
+ })
+ )
+ }, [])
+
return (
- {formattedSorts.map((sort, index) => (
-
+ {sorts.map((sort, index) => (
+
))}
- {formattedSorts.length === 0 && (
+ {sorts.length === 0 && (
No sorts applied to this view
Add a column below to sort the view
@@ -71,7 +116,7 @@ const Sort: FC = () => {
)}
-
+
{columns && columns.length > 0 ? (
{
iconRight={}
className="sb-grid-dropdown__item-trigger"
>
- {`Pick ${formattedSorts.length > 1 ? 'another' : 'a'} column to sort by`}
+ {`Pick ${sorts.length > 1 ? 'another' : 'a'} column to sort by`}
) : (
All columns have been added
)}
+
+ Apply sorting
+
)
diff --git a/studio/components/grid/components/header/sort/SortRow.tsx b/studio/components/grid/components/header/sort/SortRow.tsx
index f6fecf5f74c..64a914de7b1 100644
--- a/studio/components/grid/components/header/sort/SortRow.tsx
+++ b/studio/components/grid/components/header/sort/SortRow.tsx
@@ -1,10 +1,8 @@
-import update from 'immutability-helper'
import { FC, useRef, memo } from 'react'
import { Button, IconMenu, Toggle, IconX } from 'ui'
import { XYCoord } from 'dnd-core'
import { useDrag, useDrop, DropTargetMonitor } from 'react-dnd'
-import { useUrlState } from 'hooks'
import { DragItem, Sort } from 'components/grid/types'
import { useTrackedState } from 'components/grid/store'
@@ -12,11 +10,13 @@ type SortRowProps = {
index: number
columnName: string
sort: Sort
+ onDelete: (columnName: string) => void
+ onToggle: (columnName: string, ascending: boolean) => void
+ onDrag: (dragIndex: number, hoverIndex: number) => void
}
-const SortRow: FC
= ({ index, columnName, sort }) => {
+const SortRow: FC = ({ index, columnName, sort, onDelete, onToggle, onDrag }) => {
const state = useTrackedState()
- const [_, setParams] = useUrlState({ arrayKeys: ['sort'] })
const column = state?.table?.columns.find((x) => x.name === columnName)
if (!column) return null
@@ -89,49 +89,9 @@ const SortRow: FC = ({ index, columnName, sort }) => {
},
})
- function onToggle(value: boolean) {
- setParams((prevParams) => {
- const existingSorts = (prevParams?.sort ?? []) as string[]
- const updatedSorts = existingSorts.map((sort: string) => {
- const [column] = sort.split(':')
- return column === columnName ? `${columnName}:${value ? 'asc' : 'desc'}` : sort
- })
- return {
- ...prevParams,
- sort: updatedSorts,
- }
- })
- }
-
- function onRemoveSort() {
- setParams((prevParams) => {
- const existingSorts = (prevParams?.sort ?? []) as string[]
- const updatedSorts = existingSorts.filter((sort: string) => {
- const [column] = sort.split(':')
- if (column !== columnName) return sort
- })
- return {
- ...prevParams,
- sort: updatedSorts,
- }
- })
- }
-
const moveSort = (dragIndex: number, hoverIndex: number) => {
if (dragIndex == hoverIndex) return
- setParams((prevParams) => {
- const existingSorts = (prevParams?.sort ?? []) as string[]
- const updatedSorts = update(existingSorts, {
- $splice: [
- [dragIndex, 1],
- [hoverIndex, 0, existingSorts[dragIndex]],
- ],
- })
- return {
- ...prevParams,
- sort: updatedSorts,
- }
- })
+ onDrag(dragIndex, hoverIndex)
}
const opacity = isDragging ? 0 : 1
@@ -144,12 +104,9 @@ const SortRow: FC = ({ index, columnName, sort }) => {
style={{ opacity }}
data-handler-id={handlerId}
>
- }
- size="tiny"
- type="text"
- onClick={onRemoveSort}
- />
+
+
+
{index > 0 ? 'then by' : 'sort by'}
@@ -163,12 +120,15 @@ const SortRow: FC = ({ index, columnName, sort }) => {
layout="flex"
defaultChecked={sort.ascending}
// @ts-ignore
- onChange={(e: boolean) => onToggle(e)}
+ onChange={(e: boolean) => onToggle(columnName, e)}
/>
-
-
-
+ }
+ size="tiny"
+ type="text"
+ onClick={() => onDelete(columnName)}
+ />
)
}