From 656f1af15c4142c5b271b552e942cd453f8856c5 Mon Sep 17 00:00:00 2001 From: gitstart-twenty <140154534+gitstart-twenty@users.noreply.github.com> Date: Tue, 15 Aug 2023 10:12:11 +0800 Subject: [PATCH] feat: I can hide/show filter bar and add filters directly from filter bar (#1173) * I can hide/show filter bar and add filters directly from filter bar Co-authored-by: v1b3m * Add requested changes Co-authored-by: v1b3m * Revert breaking changes Co-authored-by: v1b3m --------- Co-authored-by: v1b3m --- .../ui/board/components/BoardHeader.tsx | 1 + .../components/DropdownButton.tsx | 6 +- .../components/FilterDropdownButton.tsx | 9 ++ .../MultipleFiltersDropdownButton.tsx | 18 +++- .../components/SortAndFilterBar.tsx | 101 +++++++++++------- .../components/SortDropdownButton.tsx | 28 ++++- .../states/sortAndFilterBarScopedState.ts | 6 ++ .../table-header/components/TableHeader.tsx | 4 + 8 files changed, 129 insertions(+), 44 deletions(-) create mode 100644 front/src/modules/ui/filter-n-sort/states/sortAndFilterBarScopedState.ts diff --git a/front/src/modules/ui/board/components/BoardHeader.tsx b/front/src/modules/ui/board/components/BoardHeader.tsx index e975ca5a4..4f9cc5eb5 100644 --- a/front/src/modules/ui/board/components/BoardHeader.tsx +++ b/front/src/modules/ui/board/components/BoardHeader.tsx @@ -71,6 +71,7 @@ export function BoardHeader({ HotkeyScope={FiltersHotkeyScope.FilterDropdownButton} /> + context={context} isSortSelected={sorts.length > 0} availableSorts={availableSorts || []} onSortSelect={sortSelect} diff --git a/front/src/modules/ui/filter-n-sort/components/DropdownButton.tsx b/front/src/modules/ui/filter-n-sort/components/DropdownButton.tsx index c3e3861ed..78ac86c7c 100644 --- a/front/src/modules/ui/filter-n-sort/components/DropdownButton.tsx +++ b/front/src/modules/ui/filter-n-sort/components/DropdownButton.tsx @@ -16,6 +16,7 @@ type OwnProps = { onIsUnfoldedChange?: (newIsUnfolded: boolean) => void; resetState?: () => void; HotkeyScope: FiltersHotkeyScope; + color?: string; }; const StyledDropdownButtonContainer = styled.div` @@ -33,7 +34,8 @@ type StyledDropdownButtonProps = { const StyledDropdownButton = styled.div` background: ${({ theme }) => theme.background.primary}; border-radius: ${({ theme }) => theme.border.radius.sm}; - color: ${(props) => (props.isActive ? props.theme.color.blue : 'none')}; + color: ${({ isActive, theme, color }) => + color ?? (isActive ? theme.color.blue : 'none')}; cursor: pointer; display: flex; filter: ${(props) => (props.isUnfolded ? 'brightness(0.95)' : 'none')}; @@ -56,6 +58,7 @@ function DropdownButton({ isUnfolded = false, onIsUnfoldedChange, HotkeyScope, + color, }: OwnProps) { useScopedHotkeys( [Key.Enter, Key.Escape], @@ -81,6 +84,7 @@ function DropdownButton({ onClick={onButtonClick} isActive={isActive} aria-selected={isActive} + color={color} > {label} diff --git a/front/src/modules/ui/filter-n-sort/components/FilterDropdownButton.tsx b/front/src/modules/ui/filter-n-sort/components/FilterDropdownButton.tsx index ddb731743..43ba7911d 100644 --- a/front/src/modules/ui/filter-n-sort/components/FilterDropdownButton.tsx +++ b/front/src/modules/ui/filter-n-sort/components/FilterDropdownButton.tsx @@ -11,9 +11,15 @@ import { SingleEntityFilterDropdownButton } from './SingleEntityFilterDropdownBu export function FilterDropdownButton({ context, HotkeyScope, + isPrimaryButton = false, + color, + label, }: { context: Context; HotkeyScope: FiltersHotkeyScope; + isPrimaryButton?: boolean; + color?: string; + label?: string; }) { const [availableFilters] = useRecoilScopedState( availableFiltersScopedState, @@ -29,6 +35,9 @@ export function FilterDropdownButton({ ); } diff --git a/front/src/modules/ui/filter-n-sort/components/MultipleFiltersDropdownButton.tsx b/front/src/modules/ui/filter-n-sort/components/MultipleFiltersDropdownButton.tsx index 0fe9a3fa6..3a765b607 100644 --- a/front/src/modules/ui/filter-n-sort/components/MultipleFiltersDropdownButton.tsx +++ b/front/src/modules/ui/filter-n-sort/components/MultipleFiltersDropdownButton.tsx @@ -9,6 +9,7 @@ import { selectedOperandInDropdownScopedState } from '@/ui/filter-n-sort/states/ import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; +import { sortAndFilterBarScopedState } from '../states/sortAndFilterBarScopedState'; import { FiltersHotkeyScope } from '../types/FiltersHotkeyScope'; import DropdownButton from './DropdownButton'; @@ -24,9 +25,15 @@ import { FilterDropdownTextSearchInput } from './FilterDropdownTextSearchInput'; export function MultipleFiltersDropdownButton({ context, HotkeyScope, + isPrimaryButton = false, + color, + label, }: { context: Context; HotkeyScope: FiltersHotkeyScope; + isPrimaryButton?: boolean; + color?: string; + label?: string; }) { const [isUnfolded, setIsUnfolded] = useState(false); @@ -67,10 +74,16 @@ export function MultipleFiltersDropdownButton({ const setHotkeyScope = useSetHotkeyScope(); + const [isSortAndFilterBarOpen, setIsSortAndFilterBarOpen] = + useRecoilScopedState(sortAndFilterBarScopedState, context); + function handleIsUnfoldedChange(newIsUnfolded: boolean) { - if (newIsUnfolded) { + if (newIsUnfolded && (!isFilterSelected || !isPrimaryButton)) { setHotkeyScope(HotkeyScope); setIsUnfolded(true); + setIsSortAndFilterBarOpen(true); + } else if (newIsUnfolded && isFilterSelected && isPrimaryButton) { + setIsSortAndFilterBarOpen(!isSortAndFilterBarOpen); } else { if (filterDefinitionUsedInDropdown?.type === 'entity') { setHotkeyScope(HotkeyScope); @@ -82,11 +95,12 @@ export function MultipleFiltersDropdownButton({ return ( {!filterDefinitionUsedInDropdown ? ( diff --git a/front/src/modules/ui/filter-n-sort/components/SortAndFilterBar.tsx b/front/src/modules/ui/filter-n-sort/components/SortAndFilterBar.tsx index dc7aa3a95..34bb99028 100644 --- a/front/src/modules/ui/filter-n-sort/components/SortAndFilterBar.tsx +++ b/front/src/modules/ui/filter-n-sort/components/SortAndFilterBar.tsx @@ -8,9 +8,12 @@ import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoi import { useRemoveFilter } from '../hooks/useRemoveFilter'; import { availableFiltersScopedState } from '../states/availableFiltersScopedState'; import { filtersScopedState } from '../states/filtersScopedState'; +import { sortAndFilterBarScopedState } from '../states/sortAndFilterBarScopedState'; +import { FiltersHotkeyScope } from '../types/FiltersHotkeyScope'; import { SelectedSortType } from '../types/interface'; import { getOperandLabelShort } from '../utils/getOperandLabel'; +import { FilterDropdownButton } from './FilterDropdownButton'; import SortOrFilterChip from './SortOrFilterChip'; type OwnProps = { @@ -18,6 +21,7 @@ type OwnProps = { sorts: Array>; onRemoveSort: (sortId: SelectedSortType['key']) => void; onCancelClick: () => void; + hasFilterButton?: boolean; }; const StyledBar = styled.div` @@ -37,6 +41,7 @@ const StyledChipcontainer = styled.div` height: 40px; justify-content: space-between; margin-left: ${({ theme }) => theme.spacing(2)}; + margin-right: ${({ theme }) => theme.spacing(1)}; overflow-x: auto; `; @@ -61,11 +66,17 @@ const StyledCancelButton = styled.button` } `; +const StyledFilterContainer = styled.div` + align-items: center; + display: flex; +`; + function SortAndFilterBar({ context, sorts, onRemoveSort, onCancelClick, + hasFilterButton = false, }: OwnProps) { const theme = useTheme(); @@ -79,6 +90,11 @@ function SortAndFilterBar({ context, ); + const [isSortAndFilterBarOpen] = useRecoilScopedState( + sortAndFilterBarScopedState, + context, + ); + const filtersWithDefinition = filters.map((filter) => { const filterDefinition = availableFilters.find((availableFilter) => { return availableFilter.field === filter.field; @@ -97,47 +113,60 @@ function SortAndFilterBar({ onCancelClick(); } - if (!filtersWithDefinition.length && !sorts.length) { + if ( + (!filtersWithDefinition.length && !sorts.length) || + !isSortAndFilterBarOpen + ) { return null; } return ( - - {sorts.map((sort) => { - return ( - - ) : ( - - ) - } - onRemove={() => onRemoveSort(sort.key)} - /> - ); - })} - {filtersWithDefinition.map((filter) => { - return ( - { - removeFilter(filter.field); - }} - /> - ); - })} - + + + {sorts.map((sort) => { + return ( + + ) : ( + + ) + } + onRemove={() => onRemoveSort(sort.key)} + /> + ); + })} + {filtersWithDefinition.map((filter) => { + return ( + { + removeFilter(filter.field); + }} + /> + ); + })} + + {hasFilterButton && ( + + )} + {filters.length + sorts.length > 0 && ( = { onSortSelect: (sort: SelectedSortType) => void; availableSorts: SortType[]; HotkeyScope: FiltersHotkeyScope; + context: Context; + isPrimaryButton?: boolean; }; const options: Array['order']> = ['asc', 'desc']; @@ -27,6 +31,8 @@ export function SortDropdownButton({ availableSorts, onSortSelect, HotkeyScope, + context, + isPrimaryButton, }: OwnProps) { const theme = useTheme(); @@ -47,6 +53,9 @@ export function SortDropdownButton({ setSelectedSortDirection('asc'); }, []); + const [isSortAndFilterBarOpen, setIsSortAndFilterBarOpen] = + useRecoilScopedState(sortAndFilterBarScopedState, context); + function handleIsUnfoldedChange(newIsUnfolded: boolean) { if (newIsUnfolded) { setIsUnfolded(true); @@ -56,6 +65,18 @@ export function SortDropdownButton({ } } + useEffect(() => { + if (isSortAndFilterBarOpen && isPrimaryButton && isSortSelected) { + setIsUnfolded(true); + } + }, [isPrimaryButton, isSortAndFilterBarOpen, isSortSelected]); + + function handleAddSort(sort: SortType) { + setIsUnfolded(false); + onSortItemSelect(sort); + setIsSortAndFilterBarOpen(true); + } + return ( ({ {availableSorts.map((sort, index) => ( { - setIsUnfolded(false); - onSortItemSelect(sort); - }} + onClick={() => handleAddSort(sort)} > {sort.icon} diff --git a/front/src/modules/ui/filter-n-sort/states/sortAndFilterBarScopedState.ts b/front/src/modules/ui/filter-n-sort/states/sortAndFilterBarScopedState.ts new file mode 100644 index 000000000..34458b1d9 --- /dev/null +++ b/front/src/modules/ui/filter-n-sort/states/sortAndFilterBarScopedState.ts @@ -0,0 +1,6 @@ +import { atomFamily } from 'recoil'; + +export const sortAndFilterBarScopedState = atomFamily({ + key: 'sortAndFilterBarScopedState', + default: false, +}); diff --git a/front/src/modules/ui/table/table-header/components/TableHeader.tsx b/front/src/modules/ui/table/table-header/components/TableHeader.tsx index f198880e3..e698e3546 100644 --- a/front/src/modules/ui/table/table-header/components/TableHeader.tsx +++ b/front/src/modules/ui/table/table-header/components/TableHeader.tsx @@ -78,12 +78,15 @@ export function TableHeader({ + context={TableContext} isSortSelected={sorts.length > 0} availableSorts={availableSorts || []} onSortSelect={sortSelect} HotkeyScope={FiltersHotkeyScope.FilterDropdownButton} + isPrimaryButton /> ({ onCancelClick={() => { handleSortsUpdate([]); }} + hasFilterButton /> } />