Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#340-sidebar #398

Merged
merged 5 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
13 changes: 10 additions & 3 deletions src/features/CategoryItem/CategoryItem.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
.category-list__item {
display: flex;
align-items: center;
min-height: 45px;
max-width: 270px;
background-color: var.$body-bg;
padding: 10px 40px 10px 14px;
padding: 5px 40px 5px 14px;
margin-bottom: 5px;
border-radius: 5px;
color: var.$body-color;
Expand All @@ -17,7 +15,16 @@
color: var.$theme-primary-color;
}


.category-list__link {
text-decoration: none;
color: inherit;
gap: 5px
}


.category-list__link_active {
text-decoration: none;
gap: 5px;
color: var.$theme-primary-color;
}
22 changes: 18 additions & 4 deletions src/features/CategoryItem/CategoryItem.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,23 @@ export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {
name: 'Тестовое имя категории',
slug: '/test',
count: 999,
id: 999
item: {
id: 3434,
name: 'Тестовая категория',
slug: '/test',
total_count: 1,
branches: [
{
id: 3,
name: 'Тестовая подкатегория',
slug: '/test',
products_count: 5,
branches: []
}
],
root: false,
is_prohibited: false,
is_visible_on_main: false
}
}
}
68 changes: 46 additions & 22 deletions src/features/CategoryItem/CategoryItem.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,63 @@
import type { FC } from 'react'
import { type FC } from 'react'
import { useDispatch } from 'react-redux'
import { NavLink } from 'react-router-dom'

import { setCategoryId } from '@/entities/Category/slice/categoryIdSlice'
import { setCategorySlug } from '@/entities/Category/slice/categorySlugSlice'
import styles from '@/features/CategoryItem/CategoryItem.module.scss'
import { useHandleActiveItem } from '@/features/CategoryItem/models/useHandleActiveItem'
import SideBar from '@/features/SideBar'
import { Routes } from '@/shared/config/routerConfig/routes'
import Link from '@/shared/ui/Link/Link'
import type { MainCategoryInfo } from '@/widgets/CategoryList/types/types'

type Props = {
name: string
slug: string
count: number
id: number
item: MainCategoryInfo
}

/**
* Компонент единицы категории в списке категорий бокового меню
* @param {string} name - название категории;
* @param {string} slug - URL для страницы категориии;
* @param {number} count - количество товаров в категории;
* @param {number} id - id категории;
* @param {MainCategoryInfo} item - главная категория с ветками;
*/
export const CategoryItem: FC<Props> = ({ name, slug, count, id }) => {
export const CategoryItem: FC<Props> = ({ item }) => {
const dispatch = useDispatch()

const { itemActive, branch } = useHandleActiveItem(item)

return (
<li className={styles['category-list__item']}>
<Link
to={`${Routes.CATEGORIES}/${slug}`}
className={styles['category-list__link']}
onClick={() => {
dispatch(setCategoryId(id))
dispatch(setCategorySlug(slug))
}}>
{name} ({count})
</Link>
</li>
<SideBar
key={item.id}
isVisible={true}
title={`${item.name} (${item.total_count})`}
onClick={() => {
dispatch(setCategoryId(item.id))
dispatch(setCategorySlug(item.slug))
}}
categorySidebar={{
to: `${Routes.CATEGORIES}/${item.slug}`,
activeElement: itemActive,
branch: branch,
itemName: item.name
}}>
{item.branches && (
<ul role="list">
{item.branches.map(el => (
<li key={el.id} className={styles['category-list__item']}>
<NavLink
role="link"
to={`${Routes.CATEGORIES}/${el.slug}`}
className={({ isActive }) =>
isActive ? styles['category-list__link_active'] : styles['category-list__link']
}
onClick={() => {
dispatch(setCategoryId(el.id))
dispatch(setCategorySlug(el.slug))
}}>
{el.name} ({el.products_count})
</NavLink>
</li>
))}
</ul>
)}
</SideBar>
)
}
31 changes: 31 additions & 0 deletions src/features/CategoryItem/models/useHandleActiveItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useEffect, useState } from 'react'
import { useSelector } from 'react-redux'
import { useParams } from 'react-router-dom'

import { getCategorySelector } from '@/widgets/CategoryList/selectors/selectors'
import type { BranchesData, MainCategoryInfo } from '@/widgets/CategoryList/types/types'

/**
* Функция сравнения и вывода активной категории и подкатегории
* @param {MainCategoryInfo} item - значение, которое нужно заменить;
*/
export const useHandleActiveItem = (item: MainCategoryInfo) => {
const { slug } = useParams()
const getMainCategories = useSelector(getCategorySelector)

const [itemActive, setItemActive] = useState<MainCategoryInfo | BranchesData>()
const [branch, setBranch] = useState<MainCategoryInfo | BranchesData>()

const activeMainCategory = getMainCategories.find(el => el.slug === slug)
const activeBranchCategory = item.branches.find(el => el.slug === slug)

useEffect(() => {
if (activeMainCategory) {
setItemActive(activeMainCategory)
} else if (activeBranchCategory) {
setItemActive(activeBranchCategory)
setBranch(getMainCategories.find(el => el.branches.find(el => el.name === itemActive?.name)))
}
}, [itemActive])
return { itemActive, branch }
}
8 changes: 8 additions & 0 deletions src/features/SideBar/model/types/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { BranchesData, MainCategoryInfo } from '@/widgets/CategoryList/types/types'

export type TCategorySidebar = {
to?: string | undefined
activeElement?: MainCategoryInfo | BranchesData
branch?: MainCategoryInfo | BranchesData
itemName?: string
}
51 changes: 39 additions & 12 deletions src/features/SideBar/ui/SideBar.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { KeyboardEvent, KeyboardEventHandler, ReactElement, useState, type FC } from 'react'
import { KeyboardEvent, KeyboardEventHandler, ReactElement, useState, type FC, useEffect } from 'react'

import ArrowIcon from '@/assets/images/sideBarMenu/IconArrowDown.svg'
import { TCategorySidebar } from '@/features/SideBar/model/types/types'
import Link from '@/shared/ui/Link/Link'
import Paragraph from '@/shared/ui/Paragraph/Paragraph'

import styles from './SideBar.module.scss'

export interface ISideBar {
interface ISideBar {
title?: string
isVisible?: boolean
onClick?: () => void
onKeyUp?: KeyboardEventHandler<HTMLLIElement>
children?: ReactElement | JSX.Element | JSX.Element[]
categorySidebar?: TCategorySidebar | undefined
}

/**
Expand All @@ -20,11 +23,30 @@ export interface ISideBar {
* @param {function} onClick - функция выхода из профиля handleLogOut;
* @param {function} onKeyUp - функция выхода из профиля handleLogOut при нажатии клавиши Enter;
* @param {JSX.Element} children - контент;
* Далее, для сайдбара на странице категори;
* @param {
* to: string,
* activeElement: MainCategoryInfo | BranchesData,
* branch: MainCategoryInfo | BranchesData,
* itemName: string
* } categorySidebar - объект с параметрами для сайдбара на странице категори;
*/

const SideBar: FC<ISideBar> = ({ title, isVisible, onClick, onKeyUp, children }) => {
const SideBar: FC<ISideBar> = ({ title, isVisible, onClick, onKeyUp, children, categorySidebar }) => {
const [isActive, setIsActive] = useState(false)

useEffect(() => {
if (
categorySidebar?.activeElement?.name === categorySidebar?.itemName &&
categorySidebar?.activeElement !== undefined
) {
setIsActive(true)
} else if (categorySidebar?.branch) {
setIsActive(true)
} else {
setIsActive(false)
}
}, [categorySidebar?.activeElement?.slug, categorySidebar?.branch])

const handleClick = () => {
setIsActive(!isActive)
}
Expand All @@ -38,17 +60,22 @@ const SideBar: FC<ISideBar> = ({ title, isVisible, onClick, onKeyUp, children })
}

return (
<li
tabIndex={0}
role="button"
onKeyUp={onKeyUp}
onKeyDown={handleKeyDown}
onClick={onClick}
className={styles.sideBar}>
<li tabIndex={0} role="button" onKeyUp={onKeyUp} onKeyDown={handleKeyDown} className={styles.sideBar}>
<div onClick={handleClick} className={styles.sideBar__header}>
<Paragraph className={styles.sideBar__headerText}>{title}</Paragraph>
{categorySidebar?.to ? (
<Link to={categorySidebar?.to}>
<Paragraph className={styles.sideBar__headerText} onClick={onClick}>
{title}
</Paragraph>
</Link>
) : (
<Paragraph className={styles.sideBar__headerText} onClick={onClick}>
{title}
</Paragraph>
)}
{isVisible && (
<ArrowIcon
onClick={handleClick}
className={`${styles.sideBar__headerArrow} ${isActive && styles.sideBar__headerArrow_active}`}
/>
)}
Expand Down
2 changes: 1 addition & 1 deletion src/shared/libs/hooks/useReplaceValueFromLocation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useLocation } from 'react-router'
/**
* Функция извлечения нужных параметров из URL с помощью useLocation
* @param {string} varN - значение, которое нужно заменить;
* @param {string} changeN - на чт нужно заменить;
* @param {string} changeN - на что нужно заменить;
*/
export const useReplaceValueFromLocation = (var1: string, change1: string, var2: string, change2: string) => {
const location = useLocation()
Expand Down
2 changes: 2 additions & 0 deletions src/widgets/CategoryList/CategoryList.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ https://github.com/Studio-Yandex-Practicum/maxboom_frontend/issues/61 */
.category-list__items {
max-height: calc(100% - 50px);
overflow-y: auto;
gap: 10px;
display: grid;
}
24 changes: 2 additions & 22 deletions src/widgets/CategoryList/CategoryList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { getLoading } from '@/pages/ProductsPage/selectors/selectors'
import { NUMBER_OF_CATEGORY_LINES } from '@/shared/constants/constants'
import { useAppDispatch } from '@/shared/libs/hooks/store'
import Heading, { HeadingType } from '@/shared/ui/Heading/Heading'
import { getCategoryBranchesSelector, getCategorySelector } from '@/widgets/CategoryList/selectors/selectors'
import { getCategorySelector } from '@/widgets/CategoryList/selectors/selectors'
import { getCategoryBranches } from '@/widgets/CategoryList/services/getCategoryBranches'
import { getCategories } from '@/widgets/CategoryList/services/getCatergories'

Expand All @@ -21,7 +21,6 @@ import styles from './CategoryList.module.scss'
*/
export const CategoryList: FC = () => {
const dispatch = useAppDispatch()
const categoryBranches = useSelector(getCategoryBranchesSelector)
const getMainCategories = useSelector(getCategorySelector)
const categorySlug = useSelector(selectCategorySlug)

Expand All @@ -44,26 +43,7 @@ export const CategoryList: FC = () => {
? Array(NUMBER_OF_CATEGORY_LINES)
.fill(0)
.map((_, i) => <CategoryItemSkeleton key={i} />)
: categoryBranches.branches?.length > 0
? categoryBranches.branches.map(item => (
<CategoryItem
key={item.id}
id={item.id}
name={item.name}
slug={item.slug}
count={item.products_count}
/>
))
: getMainCategories.map(item => (
<CategoryItem
key={item.id}
id={item.id}
name={item.name}
slug={item.slug}
// Поля count в списке категорий верхнего уровня на бэке нет, если мы его оставляем, то нужно будет запросить вывод количества товаров
count={0}
/>
))}
: getMainCategories.map(item => <CategoryItem item={item} key={item.id} />)}
</ul>
</div>
)
Expand Down
1 change: 1 addition & 0 deletions src/widgets/CategoryList/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface IMainCategorySchema {
}

export interface MainCategoryInfo {
total_count?: number
id: number
name: string
slug: string
Expand Down
Loading