1
+ import * as React from 'react' ;
2
+ import styled from 'styled-components' ;
3
+
4
+ import { Flex , Link , SearchBox } from '@redocly/developer-portal/ui' ;
5
+
6
+ export default function NavBar ( props ) {
7
+ const { items, logo } = props ;
8
+
9
+ const [ isMobileMenuOpened , setMobileMenuOpened ] = React . useState ( false ) ;
10
+ const [ pageIsScrolled , setPageIsScrolled ] = React . useState ( false ) ;
11
+ const toggleMobileMenu = ( ) => setMobileMenuOpened ( ! isMobileMenuOpened ) ;
12
+ const hideMobileMenu = ( ) => setMobileMenuOpened ( false ) ;
13
+
14
+ React . useEffect ( ( ) => {
15
+ const handleScroll = ( ) => setPageIsScrolled ( window . scrollY > 10 ) ;
16
+
17
+ document . addEventListener ( 'scroll' , handleScroll ) ;
18
+
19
+ return ( ) => document . removeEventListener ( 'scroll' , handleScroll ) ;
20
+ } , [ ] ) ;
21
+
22
+ const navItems = items
23
+ . filter ( item => item . type !== 'search' )
24
+ . map ( ( item , index ) => {
25
+ return (
26
+ < NavItem key = { index } onClick = { hideMobileMenu } >
27
+ < Link to = { item . link } > { item . label } </ Link >
28
+ </ NavItem >
29
+ ) ;
30
+ } ) ;
31
+
32
+ return (
33
+ < NavWrapper scrolled = { pageIsScrolled ? 1 : 0 } >
34
+ < Flex
35
+ maxWidth = { { xs : '100%' , large : '1400px' } }
36
+ alignItems = 'center'
37
+ justifyContent = 'space-between'
38
+ width = '100%'
39
+ mx = 'auto' >
40
+ < img src = { logo } alt = '' height = '50' />
41
+ < NavItems >
42
+ { navItems }
43
+ < SearchBox pathPrefix = { props . pathPrefix } />
44
+ </ NavItems >
45
+ </ Flex >
46
+ < NavControls >
47
+ < MobileMenuIcon onClick = { toggleMobileMenu } />
48
+ </ NavControls >
49
+ < MobileMenu isShown = { isMobileMenuOpened } >
50
+ < CloseIcon onClick = { hideMobileMenu } />
51
+ { navItems }
52
+ < SearchBox />
53
+ </ MobileMenu >
54
+ </ NavWrapper >
55
+ ) ;
56
+ }
57
+
58
+ const NavItem = styled . li `
59
+ padding: 10px 0;
60
+ ` ;
61
+
62
+ const NavWrapper = styled . nav < { scrolled : boolean } > `
63
+ padding: 15px 20px;
64
+ display: flex;
65
+ position: sticky;
66
+ z-index: 50;
67
+ transition: all 0.25s ease;
68
+ background-color: #227a88;
69
+ @media only screen and (min-width: ${ ( { theme } ) => theme . breakpoints . large } ) {
70
+ padding: 10px 15px;
71
+ box-shadow: ${ ( { scrolled } ) => ( scrolled ? '0px 1px 0px 0px rgba(225,225,225,0.2)' : 'none' ) } ;
72
+ top: 0;
73
+ left: 0;
74
+ right: 0;
75
+ }
76
+ ` ;
77
+
78
+ const NavItems = styled . ul `
79
+ display: none;
80
+ margin: 0 0 0 40px;
81
+ padding: 0;
82
+ align-items: center;
83
+ justify-content: start;
84
+ & li {
85
+ list-style: none;
86
+ margin-right: 20px;
87
+ & a {
88
+ color: #ffffff;
89
+ text-decoration: none;
90
+ }
91
+ }
92
+ @media only screen and (min-width: ${ ( { theme } ) => theme . breakpoints . medium } ) {
93
+ display: flex;
94
+ }
95
+ ` ;
96
+
97
+ export const MobileMenu = styled . ul < { isShown : boolean } > `
98
+ background: ${ props => props . theme . colors . primary . main } ;
99
+ list-style: none;
100
+ padding: 50px 40px;
101
+ margin: 0;
102
+ position: absolute;
103
+ border-top: 1px solid transparent;
104
+ z-index: 100;
105
+ color: ${ props => props . theme . colors . primary . contrastText } ;
106
+ top: 0;
107
+ right: 0;
108
+ left: 0;
109
+ bottom: 0;
110
+ font-size: 1.1875rem;
111
+ box-shadow: 0px 10px 100px 0px rgba(35, 35, 35, 0.1);
112
+ text-align: left;
113
+ display: none;
114
+ @media only screen and (max-width: ${ ( { theme } ) => theme . breakpoints . medium } ) {
115
+ position: fixed;
116
+ display: ${ props => ( props . isShown ? 'flex' : 'none' ) } ;
117
+ flex-direction: column;
118
+ overflow-y: auto;
119
+ }
120
+ & li {
121
+ list-style: none;
122
+ margin-right: 20px;
123
+ & a {
124
+ color: #ffffff;
125
+ text-decoration: none;
126
+ }
127
+ }
128
+ ` ;
129
+
130
+ export const NavControls = styled . div `
131
+ padding: 10px;
132
+ display: flex;
133
+ align-items: center;
134
+ flex: 1;
135
+ justify-content: flex-end;
136
+ @media only screen and (min-width: ${ ( { theme } ) => theme . breakpoints . medium } ) {
137
+ display: none;
138
+ }
139
+ ` ;
140
+
141
+ export const MobileMenuIcon = styled . span `
142
+ width: 1.25em;
143
+ height: 1.25em;
144
+ display: inline-block;
145
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1' x='0' y='0' viewBox='0 0 396.7 396.7' xml:space='preserve'%3E%3Cpath fill='white' d='M17 87.8h362.7c9.4 0 17-7.6 17-17s-7.6-17-17-17H17c-9.3 0-17 7.7-17 17C0 80.2 7.7 87.8 17 87.8zM17 215.3h362.7c9.4 0 17-7.6 17-17s-7.6-17-17-17H17c-9.3 0-17 7.7-17 17S7.7 215.3 17 215.3zM17 342.8h362.7c9.4 0 17-7.6 17-17s-7.6-17-17-17H17c-9.3 0-17 7.7-17 17S7.7 342.8 17 342.8z'/%3E%3C/svg%3E");
146
+ cursor: pointer;
147
+ @media only screen and (min-width: ${ ( { theme } ) => theme . breakpoints . medium } ) {
148
+ display: none;
149
+ }
150
+ ` ;
151
+
152
+ export const CloseIcon = styled . i `
153
+ cursor: pointer;
154
+ position: absolute;
155
+ right: 20px;
156
+ top: 25px;
157
+ width: 15px;
158
+ height: 15px;
159
+ background-repeat: no-repeat;
160
+ background-size: 15px 15px;
161
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1' viewBox='0 0 15.6 15.6' enable-background='new 0 0 15.642 15.642'%3E%3Cpath fill-rule='evenodd' fill='white' d='M8.9 7.8l6.5-6.5c0.3-0.3 0.3-0.8 0-1.1 -0.3-0.3-0.8-0.3-1.1 0L7.8 6.8 1.3 0.2c-0.3-0.3-0.8-0.3-1.1 0 -0.3 0.3-0.3 0.8 0 1.1l6.5 6.5L0.2 14.4c-0.3 0.3-0.3 0.8 0 1.1 0.1 0.1 0.3 0.2 0.5 0.2s0.4-0.1 0.5-0.2l6.5-6.5 6.5 6.5c0.1 0.1 0.3 0.2 0.5 0.2 0.2 0 0.4-0.1 0.5-0.2 0.3-0.3 0.3-0.8 0-1.1L8.9 7.8z'/%3E%3C/svg%3E");
162
+ ` ;
0 commit comments