Skip to content

Commit 82ff477

Browse files
author
berdysheva
committed
feat: add navigation
1 parent 4e45f19 commit 82ff477

File tree

25 files changed

+1418
-19
lines changed

25 files changed

+1418
-19
lines changed

src/components/RouterLink/RouterLink.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {WithChildren} from '../../models';
44

55
export interface RouterLinkProps {
66
href: string;
7+
[key: string]: any;
78
}
89

910
const RouterLink = ({href, children}: WithChildren<RouterLinkProps>) => {
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
@import '../../../../../styles/variables.scss';
2+
@import '../../../../../styles/mixins.scss';
3+
4+
.header {
5+
$root: &;
6+
$searchHeight: 36px;
7+
$searchWidth: 122px;
8+
$searchButtonWidth: 36px;
9+
$searchBreakpoint: 1120px;
10+
position: sticky;
11+
z-index: 98;
12+
top: 0;
13+
14+
display: flex;
15+
justify-content: center;
16+
align-items: center;
17+
18+
height: var(--header-height);
19+
20+
background-color: var(--yc-color-base-background);
21+
box-shadow: inset 0px -1px 0px var(--yc-color-line-generic);
22+
23+
&__wrapper {
24+
display: flex;
25+
justify-content: space-between;
26+
align-items: center;
27+
28+
height: var(--header-height);
29+
}
30+
31+
%menu-button {
32+
position: absolute;
33+
z-index: 2;
34+
}
35+
36+
&__mobile-menu-button {
37+
@include mobile-tablet-only();
38+
}
39+
40+
&__navigation,
41+
&__left,
42+
&__right {
43+
display: flex;
44+
align-items: center;
45+
}
46+
47+
&__navigation {
48+
position: relative;
49+
50+
flex: 1 0 0;
51+
justify-content: flex-start;
52+
53+
margin-right: $normalOffset;
54+
@include desktop-only();
55+
}
56+
57+
&__right {
58+
flex: 0;
59+
justify-content: flex-end;
60+
}
61+
62+
&__navigation-container {
63+
display: flex;
64+
overflow-x: hidden;
65+
flex: 1 0 0;
66+
justify-content: space-between;
67+
align-items: center;
68+
69+
margin-right: $indentS;
70+
}
71+
72+
&__buttons {
73+
display: flex;
74+
@include desktop-only();
75+
76+
& > * {
77+
&:not(:last-child) {
78+
margin-right: $indentXS;
79+
}
80+
}
81+
}
82+
83+
&__button {
84+
margin-top: 0;
85+
}
86+
87+
&__logo {
88+
margin: 0 $indentM 0 0;
89+
90+
cursor: pointer;
91+
}
92+
93+
@media (max-width: map-get($gridBreakpoints, 'md') - 1) {
94+
&__navigation-container {
95+
justify-content: flex-end;
96+
}
97+
98+
&__left {
99+
flex: 1 0 0;
100+
}
101+
}
102+
103+
@media (max-width: map-get($gridBreakpoints, 'sm') - 1) {
104+
&__navigation-container {
105+
margin-right: $indentXXS;
106+
}
107+
108+
&__logo {
109+
margin-right: 0;
110+
}
111+
}
112+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import React, {createRef, MouseEvent} from 'react';
2+
import block from 'bem-cn-lite';
3+
import {Col, Grid, Row} from '../../../../grid';
4+
import OutsideClick from '../../../OutsideClick/OutsideClick';
5+
import Control from '../../../Control/Control';
6+
7+
import Logo from '../Logo/Logo';
8+
9+
import {HeaderData, NavigationLogo} from '../../../../models/navigation';
10+
import Navigation from '../Navigation/Navigation';
11+
import MobileNavigation from '../MobileNavigation/MobileNavigation';
12+
import NavigationItem from '../NavigationItem/NavigationItem';
13+
14+
import {NavigationClose, NavigationOpen} from '../../../../icons';
15+
16+
import './Header.scss';
17+
18+
const b = block('header');
19+
20+
export interface HeaderProps {
21+
logo: NavigationLogo;
22+
data: HeaderData;
23+
}
24+
25+
interface HeaderState {
26+
isSidebarOpened: boolean;
27+
activeItemIndex: number;
28+
}
29+
30+
class Header extends React.Component<HeaderProps, HeaderState> {
31+
ref = createRef();
32+
state = {
33+
isSidebarOpened: false,
34+
activeItemIndex: -1,
35+
};
36+
37+
render() {
38+
return (
39+
<Grid className={b()}>
40+
<Row>
41+
<Col>
42+
<header className={b('wrapper')}>
43+
{this.renderLogo()}
44+
{this.renderLeft()}
45+
{this.renderRight()}
46+
{this.renderMobileNavigation()}
47+
</header>
48+
</Col>
49+
</Row>
50+
</Grid>
51+
);
52+
}
53+
54+
private renderLeft() {
55+
const {activeItemIndex} = this.state;
56+
const {leftItems} = this.props.data;
57+
58+
return (
59+
leftItems && (
60+
<div className={b('navigation-container')}>
61+
<Navigation
62+
className={b('navigation')}
63+
links={leftItems}
64+
activeItemIndex={activeItemIndex}
65+
onActiveItemChange={this.onActiveItemChange}
66+
/>
67+
</div>
68+
)
69+
);
70+
}
71+
72+
private renderLogo() {
73+
const {logo} = this.props;
74+
75+
if (!logo) {
76+
return null;
77+
}
78+
79+
return (
80+
<div className={b('left')}>
81+
<Logo {...logo} className={b('logo')} />
82+
</div>
83+
);
84+
}
85+
86+
private renderRight() {
87+
return (
88+
<div className={b('right')}>
89+
{this.renderMobileMenuButton()}
90+
{this.renderRightItems()}
91+
</div>
92+
);
93+
}
94+
95+
private renderRightItems() {
96+
const {rightItems} = this.props.data;
97+
98+
return (
99+
rightItems && (
100+
<div className={b('buttons')}>
101+
{rightItems.map((button) => (
102+
<NavigationItem key={button.text} data={button} className={b('button')} />
103+
))}
104+
</div>
105+
)
106+
);
107+
}
108+
109+
private renderMobileMenuButton() {
110+
const {isSidebarOpened} = this.state;
111+
const iconProps = {icon: isSidebarOpened ? NavigationClose : NavigationOpen, iconSize: 36};
112+
113+
return (
114+
<Control
115+
className={b('mobile-menu-button')}
116+
onClick={(e: MouseEvent) => {
117+
e.stopPropagation();
118+
this.onSidebarOpenedChange(!isSidebarOpened);
119+
}}
120+
size="l"
121+
{...iconProps}
122+
/>
123+
);
124+
}
125+
126+
private renderMobileNavigation() {
127+
const {leftItems, rightItems} = this.props.data;
128+
const {isSidebarOpened, activeItemIndex} = this.state;
129+
130+
return (
131+
<OutsideClick onOutsideClick={() => this.onSidebarOpenedChange(false)}>
132+
<MobileNavigation
133+
topItems={leftItems}
134+
bottomItems={rightItems}
135+
isOpened={isSidebarOpened}
136+
activeItemIndex={activeItemIndex}
137+
onActiveItemChange={this.onActiveItemChange}
138+
onClose={this.hideSidebar}
139+
/>
140+
</OutsideClick>
141+
);
142+
}
143+
144+
private onActiveItemChange = (index: number) => {
145+
this.setState({activeItemIndex: index});
146+
};
147+
148+
private onSidebarOpenedChange = (isSidebarOpened: boolean) => {
149+
this.setState({isSidebarOpened});
150+
};
151+
152+
private hideSidebar = () => {
153+
this.setState({isSidebarOpened: false});
154+
};
155+
}
156+
157+
export default Header;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
@import '../../../../../styles/variables.scss';
2+
@import '../../../../../styles/mixins.scss';
3+
4+
.logo {
5+
display: flex;
6+
align-items: center;
7+
8+
font-weight: 500;
9+
@include text-size(header-2);
10+
11+
&__icon {
12+
width: 178px;
13+
height: 36px;
14+
margin-right: $indentXXXS;
15+
16+
object-fit: contain;
17+
}
18+
19+
&__text {
20+
white-space: nowrap;
21+
}
22+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react';
2+
import block from 'bem-cn-lite';
3+
4+
import {NavigationLogo} from '../../../../models/navigation';
5+
import {Image} from '../../../index';
6+
import RouterLink from '../../../RouterLink/RouterLink';
7+
8+
import './Logo.scss';
9+
10+
const b = block('logo');
11+
12+
export interface LogoProps extends NavigationLogo {
13+
className?: string;
14+
}
15+
16+
const Logo: React.FC<LogoProps> = ({icon, text, className}) => (
17+
<RouterLink href="/" passHref>
18+
<div className={b(null, className)}>
19+
{icon && <Image className={b('icon')} src={icon} />}
20+
<span className={b('text')}>{text}</span>
21+
</div>
22+
</RouterLink>
23+
);
24+
25+
export default Logo;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
@import '../../../../../styles/variables.scss';
2+
@import '../../../../../styles/mixins.scss';
3+
4+
.mobile-navigation {
5+
position: fixed;
6+
z-index: 100;
7+
top: var(--header-height);
8+
left: 0;
9+
10+
width: 100%;
11+
12+
border-bottom-right-radius: $borderRadius;
13+
border-bottom-left-radius: $borderRadius;
14+
background-color: var(--yc-color-base-background);
15+
box-shadow: 0px 3px 10px var(--yc-color-sfx-shadow);
16+
@include text-size(body-2);
17+
@include mobile-tablet-only();
18+
19+
&__wrapper {
20+
padding: $indentM $indentS;
21+
}
22+
23+
&__button {
24+
margin-top: $indentSM;
25+
}
26+
27+
&__links {
28+
position: relative;
29+
30+
display: flex;
31+
flex-direction: column;
32+
33+
padding-bottom: $indentSM;
34+
@include reset-list-style();
35+
}
36+
37+
&__links-item {
38+
&:not(:last-child) {
39+
margin-bottom: $indentSM;
40+
}
41+
}
42+
43+
&__dropdown-item {
44+
&:not(:last-child) {
45+
margin-bottom: $indentXS;
46+
}
47+
}
48+
49+
&__popup {
50+
@include mobile-tablet-only();
51+
@include navigation-popup();
52+
}
53+
}

0 commit comments

Comments
 (0)