Skip to content

Commit

Permalink
refactor: convert defaultSortConfigs to array (instead of object)
Browse files Browse the repository at this point in the history
- merge SelectedSort[] type with SortConfig[]
- rename selectedSorts (to be plural)
- rename to deleteSelectedSort (to be specific)
  • Loading branch information
David Horm committed Mar 16, 2023
1 parent 375b1c8 commit e3dfe80
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export const CustomSortControls = () => {
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
const open = Boolean(anchorEl);
const id = open ? "add-sorts-dialog" : undefined;
const { selectedSort, sortByOptions } = useFilterState();
const { toggleSelectedSort, deleteSort } = useFilterDispatch();
const { selectedSorts, sortByOptions } = useFilterState();
const { toggleSelectedSort, deleteSelectedSort } = useFilterDispatch();

return (
<>
Expand All @@ -47,14 +47,14 @@ export const CustomSortControls = () => {
</Button>
</li>

{selectedSort.map(({ sortBy, direction }) => (
{selectedSorts.map(({ sortBy, direction }) => (
<li key={sortBy}>
<Chip
label={sortBy}
variant="outlined"
icon={<Arrow direction={direction} />}
onClick={() => toggleSelectedSort({ sortBy, allowDelete: false })}
onDelete={() => deleteSort(sortBy)}
onDelete={() => deleteSelectedSort(sortBy)}
/>
</li>
))}
Expand All @@ -77,7 +77,7 @@ export const CustomSortControls = () => {
<ListItemIcon>
<Arrow
direction={
selectedSort.find((i) => i.sortBy === sortBy)?.direction
selectedSorts.find((i) => i.sortBy === sortBy)?.direction
}
/>
</ListItemIcon>
Expand Down
16 changes: 8 additions & 8 deletions src/components/ServiceProvider/FilterAndSortProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,28 @@ import {
const FilterAndSortContext = createContext<
Omit<
ReturnType<typeof useCollectionFilters>,
"filterDispatch" | "toggleSelectedSort" | "deleteSort"
"filterDispatch" | "toggleSelectedSort" | "deleteSelectedSort"
> & { sortByOptions: typeof sortByOptions }
>({
filterState: initialFilterState,
sliderControls: [],
initialSliderValues: {},
applyFiltersAndSorts: () => [],
selectedSort: [],
selectedSorts: [],
sortByOptions,
});

const FilterAndSortDispatchContext = createContext<
Pick<
ReturnType<typeof useCollectionFilters>,
"filterDispatch" | "toggleSelectedSort" | "deleteSort"
"filterDispatch" | "toggleSelectedSort" | "deleteSelectedSort"
>
>({
filterDispatch: (state) => state,
toggleSelectedSort: () => {
return;
},
deleteSort: () => {
deleteSelectedSort: () => {
return;
},
});
Expand All @@ -45,24 +45,24 @@ export const FilterAndSortProvider = ({
sliderControls,
initialSliderValues,
applyFiltersAndSorts,
selectedSort,
selectedSorts,
toggleSelectedSort,
deleteSort,
deleteSelectedSort,
} = useCollectionFilters();

const state = {
filterState,
sliderControls,
initialSliderValues,
applyFiltersAndSorts,
selectedSort,
selectedSorts,
sortByOptions,
};

const dispatch = {
filterDispatch,
toggleSelectedSort,
deleteSort,
deleteSelectedSort,
};

return (
Expand Down
53 changes: 27 additions & 26 deletions src/services/filter-sort-services/sort.service.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import type { Dispatch, SetStateAction } from "react";
import type {
BoardGame,
SortByOption,
CollectionFilterState,
} from "./useCollectionFilters";
import type { BoardGame, CollectionFilterState } from "./useCollectionFilters";

type SortDirection = "ASC" | "DESC";

Expand All @@ -14,17 +10,17 @@ export type SortFn = (
filterState: CollectionFilterState
) => number;

export type SelectedSort = {
sortBy: SortByOption;
export type SortConfig = {
/** Label */
sortBy: string;

/** Direction to sort */
direction: SortDirection;

/** Sort function */
sort: SortFn;
};

export type SortConfig = Record<
SortByOption,
Pick<SelectedSort, "direction" | "sort">
>;

export const stringSort = (direction: SortDirection, a: string, b: string) =>
direction === "ASC" ? a.localeCompare(b) : b.localeCompare(a);

Expand All @@ -48,28 +44,33 @@ export const numberSort = (
*/
export const toggleSelectedSort =
(
selectedSort: SelectedSort[],
setSelectedSort: Dispatch<SetStateAction<SelectedSort[]>>,
sortConfig: SortConfig
selectedSorts: SortConfig[],
setSelectedSorts: Dispatch<SetStateAction<SortConfig[]>>,
defaultSortConfigs: SortConfig[]
) =>
({ sortBy, allowDelete }: { sortBy: SortByOption; allowDelete: boolean }) => {
const existingSelectedSort = selectedSort.find((s) => s.sortBy === sortBy);
const { direction, sort } = sortConfig[sortBy];
({ sortBy, allowDelete }: { sortBy: string; allowDelete: boolean }) => {
const existingSelectedSort = selectedSorts.find((s) => s.sortBy === sortBy);
const { direction, sort } =
defaultSortConfigs.find((s) => s.sortBy === sortBy) ||
defaultSortConfigs[0];

if (!existingSelectedSort) {
// if doesn't exist in array, then append to end
setSelectedSort((existing) => [...existing, { sortBy, direction, sort }]);
setSelectedSorts((existing) => [
...existing,
{ sortBy, direction, sort },
]);
} else if (allowDelete && existingSelectedSort.direction !== direction) {
// if direction is different than default, then remove from array
setSelectedSort((existing) =>
setSelectedSorts((existing) =>
existing.filter((e) => e.sortBy !== sortBy)
);
} else {
const toggledDirection =
existingSelectedSort.direction === "ASC" ? "DESC" : "ASC";

// if direction is default (or don't allow delete), then toggle.
setSelectedSort((existing) =>
setSelectedSorts((existing) =>
existing.map((e) => ({
sortBy: e.sortBy,
sort: e.sort,
Expand All @@ -79,15 +80,15 @@ export const toggleSelectedSort =
}
};

export const deleteSort =
(setSelectedSort: Dispatch<SetStateAction<SelectedSort[]>>) =>
export const deleteSelectedSort =
(setSelectedSorts: Dispatch<SetStateAction<SortConfig[]>>) =>
(sortBy: string) =>
setSelectedSort((existing) => existing.filter((e) => e.sortBy !== sortBy));
setSelectedSorts((existing) => existing.filter((e) => e.sortBy !== sortBy));

export const applySort =
(filterState: CollectionFilterState, selectedSort: SelectedSort[]) =>
(filterState: CollectionFilterState, selectedSorts: SortConfig[]) =>
(a: BoardGame, b: BoardGame): number => {
for (const { direction, sort } of selectedSort) {
for (const { direction, sort } of selectedSorts) {
const sortResult = sort(direction, a, b, filterState);
if (sortResult !== 0) {
return sortResult;
Expand Down
55 changes: 24 additions & 31 deletions src/services/filter-sort-services/useCollectionFilters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ import { showNotRecommendedService } from "./show-not-recommended.service";
import { showRatingsService } from "./show-ratings.service";
import {
applySort,
deleteSort,
SelectedSort,
deleteSelectedSort,
SortConfig,
stringSort,
toggleSelectedSort,
Expand Down Expand Up @@ -219,11 +218,11 @@ export type BoardGame = ReturnType<
>[number];

export const applyFiltersAndSorts =
(filterState: CollectionFilterState, selectedSort: SelectedSort[]) =>
(games: SimpleBoardGame[]): BoardGame[] => {
(filterState: CollectionFilterState, selectedSorts: SortConfig[]) =>
(games: SimpleBoardGame[]) => {
filterState.isDebug && printDebugMessage("All Games", games);

const filter = _.flow(
const filter: (g: SimpleBoardGame[]) => BoardGame[] = _.flow(
showExpansionsService.applyFilters(filterState), // Show as many things as needed from here
showInvalidPlayerCountService.applyFilters(filterState),
playerCountRangeService.applyFilters(filterState), // Start removing things as needed from here
Expand All @@ -234,37 +233,31 @@ export const applyFiltersAndSorts =
showNotRecommendedService.applyFilters(filterState) // But do one more filter based on isPlayerCountWithinRange
);

return filter(games).sort(applySort(filterState, selectedSort));
return filter(games).sort(applySort(filterState, selectedSorts));
};

/** List of options the user can sort by */
export const sortByOptions = [
"Name",
"Player Count Recommendation",
"Average Playtime",
"Complexity",
"Ratings",
] as const;

export type SortByOption = typeof sortByOptions[number];

const sortConfig: SortConfig = {
"Name": {
const defaultSortConfigs: SortConfig[] = [
{
sortBy: "Name",
direction: "ASC",
sort: (dir, a, b) => stringSort(dir, a.name, b.name),
},
"Player Count Recommendation": {
{
sortBy: "Player Count Recommendation",
direction: "DESC",
sort: playerCountRecommendationService.sort,
},
"Average Playtime": { direction: "DESC", sort: playtimeService.sort },
"Complexity": { direction: "DESC", sort: complexityService.sort },
"Ratings": { direction: "DESC", sort: ratingsService.sort },
};
{ sortBy: "Average Playtime", direction: "DESC", sort: playtimeService.sort },
{ sortBy: "Complexity", direction: "DESC", sort: complexityService.sort },
{ sortBy: "Ratings", direction: "DESC", sort: ratingsService.sort },
];

/** List of options the user can sort by */
export const sortByOptions = defaultSortConfigs.map((s) => s.sortBy);

export const useCollectionFilters = () => {
const [filterState, filterDispatch] = useReducer(reducer, initialFilterState);
const [selectedSort, setSelectedSort] = useState<SelectedSort[]>([]);
const [selectedSorts, setSelectedSorts] = useState<SortConfig[]>([]);

const sliderControls: Array<{
sliderLabel: string;
Expand All @@ -291,13 +284,13 @@ export const useCollectionFilters = () => {
filterDispatch,
sliderControls,
initialSliderValues,
applyFiltersAndSorts: applyFiltersAndSorts(filterState, selectedSort),
selectedSort,
applyFiltersAndSorts: applyFiltersAndSorts(filterState, selectedSorts),
selectedSorts,
toggleSelectedSort: toggleSelectedSort(
selectedSort,
setSelectedSort,
sortConfig
selectedSorts,
setSelectedSorts,
defaultSortConfigs
),
deleteSort: deleteSort(setSelectedSort),
deleteSelectedSort: deleteSelectedSort(setSelectedSorts),
};
};

0 comments on commit e3dfe80

Please sign in to comment.