Skip to content
This repository has been archived by the owner on Mar 23, 2024. It is now read-only.

Fix/search bar #144

Merged
merged 8 commits into from
Mar 9, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions client/src/app/(main)/layout.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
}

.content_zone {
margin-bottom: 48px;
padding-left: 88px;

@media screen and (max-width: 768px) {
Expand Down
4 changes: 4 additions & 0 deletions client/src/shared/ui/checkbox/checkbox.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
}

.label {
width: calc(100% - 26px);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--white-color);
font: var(--font-body-weight-m) var(--font-body-size-m) / var(--font-body-line-m)
var(--font-rubik);
Expand Down
38 changes: 38 additions & 0 deletions client/src/shared/ui/search-bar/actions/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { MultiValue } from 'react-select';
import { Action, ActionTypesEnum } from './types';
import { IOptionItem } from '../types';

export const changeFilterValue = (
filterIndex: number,
newValue: string | MultiValue<IOptionItem> | [number, number] | null
): Action<ActionTypesEnum.CHANGE_FILTER_VALUE> => ({
type: ActionTypesEnum.CHANGE_FILTER_VALUE,
payload: {
filterIndex,
newValue,
},
});

export const clearOneMultipleOption = (
filterIndex: number,
optionIndex: number
): Action<ActionTypesEnum.CLEAR_ONE_MULTIPLE_OPTION> => ({
type: ActionTypesEnum.CLEAR_ONE_MULTIPLE_OPTION,
payload: { filterIndex, optionIndex },
});

export const clearAllExceptOneMultipleOptions = (
filterIndex: number
): Action<ActionTypesEnum.CLEAR_ALL_EXCEPT_ONE_MULTIPLE_OPTIONS> => ({
type: ActionTypesEnum.CLEAR_ALL_EXCEPT_ONE_MULTIPLE_OPTIONS,
payload: filterIndex,
});

export const clearFilter = (filterIndex: number): Action<ActionTypesEnum.CLEAR_FILTER> => ({
type: ActionTypesEnum.CLEAR_FILTER,
payload: filterIndex,
});

export const clearAllFilters = (): Action<ActionTypesEnum.CLEAR_ALL_FILTERS> => ({
type: ActionTypesEnum.CLEAR_ALL_FILTERS,
});
9 changes: 9 additions & 0 deletions client/src/shared/ui/search-bar/actions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export {
changeFilterValue,
clearOneMultipleOption,
clearAllExceptOneMultipleOptions,
clearFilter,
clearAllFilters,
} from './actions';
export type { Action } from './types';
export { ActionTypesEnum } from './types';
39 changes: 39 additions & 0 deletions client/src/shared/ui/search-bar/actions/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { MultiValue } from 'react-select';
import { IOptionItem } from '../types';

export enum ActionTypesEnum {
CHANGE_FILTER_VALUE = 'CHANGE_FILTER_VALUE',
CLEAR_ONE_MULTIPLE_OPTION = 'CLEAR_ONE_MULTIPLE_OPTION',
CLEAR_ALL_EXCEPT_ONE_MULTIPLE_OPTIONS = 'CLEAR_ALL_EXCEPT_ONE_MULTIPLE_OPTIONS',
CLEAR_FILTER = 'CLEAR_FILTER',
CLEAR_ALL_FILTERS = 'CLEAR_ALL_FILTERS',
}

type ActionsWithoutPayload = ActionTypesEnum.CLEAR_ALL_FILTERS;

interface ActionWithoutPayload {
type: ActionsWithoutPayload;
}

export interface ActionWithPayload<T extends ActionTypesEnum> {
type: T;
payload: ActionsPayloads[T];
}

interface ActionsPayloads {
[ActionTypesEnum.CHANGE_FILTER_VALUE]: {
filterIndex: number;
newValue: string | MultiValue<IOptionItem> | [number, number] | null;
};
[ActionTypesEnum.CLEAR_ONE_MULTIPLE_OPTION]: {
filterIndex: number;
optionIndex: number;
};
[ActionTypesEnum.CLEAR_ALL_EXCEPT_ONE_MULTIPLE_OPTIONS]: number;
[ActionTypesEnum.CLEAR_FILTER]: number;
[ActionTypesEnum.CLEAR_ALL_FILTERS]: never;
}

export type Action<T extends ActionTypesEnum> = T extends ActionsWithoutPayload
? ActionWithoutPayload
: ActionWithPayload<T>;
1 change: 1 addition & 0 deletions client/src/shared/ui/search-bar/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { useTrackFilterArr } from './useTrackFiltersArr';
export { useFilters } from './useFilters';
export { useFilterReducer } from './useFilterReducer';
132 changes: 132 additions & 0 deletions client/src/shared/ui/search-bar/hooks/useFilterReducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { useReducer } from 'react';
import { Filter, IFilterState } from '../types';
import { ActionTypesEnum, Action } from '../actions';

const reducer = (state: IFilterState, action: Action<ActionTypesEnum>) => {
switch (action.type) {
case ActionTypesEnum.CLEAR_ONE_MULTIPLE_OPTION: {
const { filterIndex, optionIndex } = action.payload;
const filter = state.filterArr[filterIndex];

if (filter.type === 'checkbox' || filter.type === 'multiple') {
const newFilterValue = filter.filterValue.filter((item, i) => i !== optionIndex);

return {
isTimerDisabled: false,
filterArr: state.filterArr.map((item, i) => {
if (filterIndex === i) {
item.filterValue = newFilterValue;
}

return item;
}),
};
}

return state;
}

case ActionTypesEnum.CLEAR_ALL_EXCEPT_ONE_MULTIPLE_OPTIONS: {
const filterIndex = action.payload;
const filter = state.filterArr[filterIndex];

if (filter.type === 'checkbox' || filter.type === 'multiple') {
const newFilterValue = [filter.filterValue[0]];

return {
isTimerDisabled: true,
filterArr: state.filterArr.map((item, i) => {
if (filterIndex === i) {
item.filterValue = newFilterValue;
}

return item;
}),
};
}

return state;
}

case ActionTypesEnum.CHANGE_FILTER_VALUE: {
const { filterIndex, newValue } = action.payload;

return {
isTimerDisabled: false,
filterArr: state.filterArr.map((item, i) => {
if (filterIndex === i) {
item.filterValue = newValue;
}

return item;
}),
};
}

case ActionTypesEnum.CLEAR_FILTER: {
const filterIndex = action.payload;

return {
isTimerDisabled: false,
filterArr: state.filterArr.map((item, index) => {
if (index === filterIndex) {
switch (item.type) {
case 'text':
item.filterValue = '';

return item;

case 'multiple':
case 'checkbox':
item.filterValue = [];

return item;

case 'range':
item.filterValue = null;

return item;
}
}

return item;
}),
};
}

case ActionTypesEnum.CLEAR_ALL_FILTERS: {
return {
isTimerDisabled: false,
filterArr: state.filterArr.map(item => {
switch (item.type) {
case 'text':
item.filterValue = '';

return item;

case 'multiple':
case 'checkbox':
item.filterValue = [];

return item;

case 'range':
item.filterValue = null;

return item;
}
}),
};
}

default:
return state;
}
};

export const useFilterReducer = (initialState: Filter[]) => {
return useReducer(reducer, {
isTimerDisabled: false,
filterArr: initialState,
});
};
77 changes: 43 additions & 34 deletions client/src/shared/ui/search-bar/hooks/useTrackFiltersArr.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,55 @@
import { useEffect, useRef } from 'react';
import { Filter, IFilterParams } from '../types';

export const useTrackFilterArr = (
filterArr: Filter[],
onChange: (filterValues: string | null) => void
) => {
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
useEffect(() => {
if (timerRef.current) {
clearTimeout(timerRef.current);
}
const getFilterValues = (filterArr: Filter[]) =>
filterArr.reduce<{
[key: string]: string | string[] | [number, number];
}>((acc, { type, value, filterValue }) => {
switch (type) {
case 'text':
if (filterValue.length) {
acc[value] = filterValue;
}

timerRef.current = setTimeout(() => {
const filterValues: IFilterParams = filterArr.reduce<{
[key: string]: string | string[] | [number, number];
}>((acc, curr) => {
switch (curr.type) {
case 'text':
if (curr.filterValue.length) {
acc[curr.value] = curr.filterValue;
}
return acc;

case 'multiple':
case 'checkbox':
if (filterValue.length) {
acc[value] = filterValue.map<string>(item => item.label);
}

return acc;
return acc;

case 'multiple':
case 'checkbox':
if (curr.filterValue.length) {
acc[curr.value] = curr.filterValue.map<string>(item => item.label);
}
case 'range':
if (filterValue?.length) {
acc[value] = filterValue;
}

return acc;
return acc;
}
}, {});

case 'range':
if (curr.filterValue?.length) {
acc[curr.value] = curr.filterValue;
}
export const useTrackFilterArr = (
{ isTimerDisabled, filterArr }: { isTimerDisabled: boolean; filterArr: Filter[] },
onChange: (filterValues: string | null) => void
) => {
const filterValues: IFilterParams = getFilterValues(filterArr);

return acc;
}
}, {});
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

useEffect(() => {
if (isTimerDisabled) {
onChange(Object.keys(filterValues).length ? JSON.stringify(filterValues) : null);
}, 500);
}, [filterArr, onChange]);
} else {
if (timerRef.current) {
clearTimeout(timerRef.current);
}

timerRef.current = setTimeout(
() => onChange(Object.keys(filterValues).length ? JSON.stringify(filterValues) : null),
500
);
}
}, [isTimerDisabled, filterArr, onChange, filterValues]);
};
6 changes: 6 additions & 0 deletions client/src/shared/ui/search-bar/search-bar.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,10 @@
@media screen and (max-width: 900px) {
max-width: 546px;
}

@media (max-width: 768px) {
&_hidden {
display: none;
}
}
}
Loading
Loading