Skip to content

Commit 23b361c

Browse files
committed
fix: open api ref in a new tab #48
1 parent 00ee8dd commit 23b361c

File tree

3 files changed

+483
-1
lines changed

3 files changed

+483
-1
lines changed

docs/gatsby-config.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ module.exports = {
5353
},
5454
footerNavConfig: {
5555
'API Reference': {
56-
href: 'https://awslabs.github.io/aws-lambda-powertools-python/api/'
56+
href: 'https://awslabs.github.io/aws-lambda-powertools-python/api/',
57+
target: '_blank'
5758
},
5859
Serverless: {
5960
href: 'https://aws.amazon.com/serverless/'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
2+
import PropTypes from 'prop-types';
3+
import React, { Fragment, useEffect, useMemo, useRef } from 'react';
4+
import styled from '@emotion/styled';
5+
import useKey from 'react-use/lib/useKey';
6+
import useWindowSize from 'react-use/lib/useWindowSize';
7+
import { IconTwitter } from '@apollo/space-kit/icons/IconTwitter';
8+
import { IconYoutube } from '@apollo/space-kit/icons/IconYoutube';
9+
import { boxShadow } from './search';
10+
import { breakpoints, colors, smallCaps } from 'gatsby-theme-apollo-core';
11+
import { size, transparentize } from 'polished';
12+
13+
const Wrapper = styled.div({
14+
width: '100%',
15+
height: '100%',
16+
backgroundColor: transparentize(0.5, colors.text2),
17+
overflow: 'auto',
18+
position: 'fixed',
19+
top: 0,
20+
left: 0,
21+
zIndex: 3,
22+
perspective: '1000px',
23+
transitionProperty: 'opacity, visibility',
24+
transitionDuration: '150ms',
25+
transitionTimingFunction: 'ease-in-out'
26+
});
27+
28+
const transitionDuration = 150; // in ms
29+
const Menu = styled.div({
30+
width: 700,
31+
marginBottom: 24,
32+
borderRadius: 4,
33+
boxShadow,
34+
backgroundColor: 'white',
35+
overflow: 'hidden',
36+
position: 'absolute',
37+
transformOrigin: '25% 25%',
38+
transition: `transform ${transitionDuration}ms ease-in-out`,
39+
outline: 'none',
40+
[breakpoints.md]: {
41+
width: 450
42+
},
43+
[breakpoints.sm]: {
44+
width: 'calc(100vw - 48px)'
45+
}
46+
});
47+
48+
const MenuTitle = styled.h6(smallCaps, {
49+
margin: 24,
50+
marginBottom: 0,
51+
fontSize: 13,
52+
fontWeight: 600,
53+
color: colors.text3
54+
});
55+
56+
const StyledNav = styled.nav({
57+
display: 'flex',
58+
flexWrap: 'wrap',
59+
margin: 12
60+
});
61+
62+
const NavItem = styled.div({
63+
display: 'block',
64+
width: '50%',
65+
[breakpoints.md]: {
66+
width: '100%'
67+
}
68+
});
69+
70+
const NavItemInner = styled.a({
71+
display: 'block',
72+
height: '100%',
73+
padding: 12,
74+
borderRadius: 4,
75+
color: colors.text1,
76+
textDecoration: 'none',
77+
backgroundColor: 'transparent',
78+
transitionProperty: 'color, background-color',
79+
transitionDuration: '150ms',
80+
transitionTimingFunction: 'ease-in-out',
81+
'@media (hover: hover)': {
82+
':hover': {
83+
color: 'white',
84+
backgroundColor: colors.primary,
85+
p: {
86+
color: colors.primaryLight
87+
}
88+
}
89+
}
90+
});
91+
92+
export const NavItemTitle = styled.h4({
93+
marginBottom: 8,
94+
fontWeight: 600,
95+
color: 'inherit'
96+
});
97+
98+
export const NavItemDescription = styled.p({
99+
marginBottom: 0,
100+
fontSize: 14,
101+
lineHeight: 1.5,
102+
color: colors.text3,
103+
transition: 'color 150ms ease-in-out'
104+
});
105+
106+
const FooterNav = styled.nav({
107+
display: 'flex',
108+
alignItems: 'center',
109+
padding: '16px 24px',
110+
backgroundColor: colors.background,
111+
[breakpoints.md]: {
112+
display: 'block'
113+
}
114+
});
115+
116+
const FooterNavItem = styled.a({
117+
color: colors.text2,
118+
textDecoration: 'none',
119+
':hover': {
120+
color: colors.text3
121+
},
122+
':not(:last-child)': {
123+
marginRight: 24
124+
}
125+
});
126+
127+
const SocialLinks = styled.div({
128+
display: 'flex',
129+
marginLeft: 'auto',
130+
[breakpoints.md]: {
131+
marginTop: 8
132+
}
133+
});
134+
135+
const SocialLink = styled.a({
136+
color: colors.text2,
137+
':hover': {
138+
color: colors.text3
139+
},
140+
':not(:last-child)': {
141+
marginRight: 24
142+
},
143+
svg: {
144+
...size(24),
145+
display: 'block',
146+
fill: 'currentColor'
147+
}
148+
});
149+
150+
export default function DocsetSwitcher(props) {
151+
const menuRef = useRef(null);
152+
const { width } = useWindowSize();
153+
useKey('Escape', props.onClose);
154+
155+
useEffect(() => {
156+
if (props.open) {
157+
// focus the menu after it has been transitioned in
158+
setTimeout(() => {
159+
menuRef.current.focus();
160+
}, transitionDuration);
161+
}
162+
}, [props.open]);
163+
164+
const { current } = props.buttonRef;
165+
const menuStyles = useMemo(() => {
166+
if (!current) {
167+
return null;
168+
}
169+
170+
const { top, left, height } = current.getBoundingClientRect();
171+
return {
172+
top: top + height + 2,
173+
left
174+
};
175+
// eslint-disable-next-line react-hooks/exhaustive-deps
176+
}, [current, width, props.open]);
177+
178+
function handleWrapperClick(event) {
179+
if (event.target === event.currentTarget) {
180+
props.onClose();
181+
}
182+
}
183+
184+
const hasSocialUrls = Boolean(
185+
props.spectrumUrl || props.twitterUrl || props.youtubeUrl
186+
);
187+
return (
188+
<Wrapper
189+
onClick={handleWrapperClick}
190+
style={{
191+
opacity: props.open ? 1 : 0,
192+
visibility: props.open ? 'visible' : 'hidden'
193+
}}
194+
>
195+
<Menu
196+
ref={menuRef}
197+
tabIndex={-1}
198+
style={{
199+
...menuStyles,
200+
transform:
201+
!props.open && 'translate3d(0,-24px,-16px) rotate3d(1,0,0.1,8deg)'
202+
}}
203+
>
204+
<MenuTitle>{props.siteName}</MenuTitle>
205+
<StyledNav>
206+
{props.navItems.map(navItem => (
207+
<NavItem key={navItem.url}>
208+
<NavItemInner href={navItem.url} target={navItem.target}>
209+
<NavItemTitle>{navItem.title}</NavItemTitle>
210+
<NavItemDescription>{navItem.description}</NavItemDescription>
211+
</NavItemInner>
212+
</NavItem>
213+
))}
214+
</StyledNav>
215+
{(props.footerNavConfig || hasSocialUrls) && (
216+
<FooterNav>
217+
<Fragment>
218+
{props.footerNavConfig &&
219+
Object.entries(props.footerNavConfig).map(([text, props]) => (
220+
<FooterNavItem key={text} {...props}>
221+
{text}
222+
</FooterNavItem>
223+
))}
224+
{hasSocialUrls && (
225+
<SocialLinks>
226+
{props.twitterUrl && (
227+
<SocialLink
228+
href={props.twitterUrl}
229+
title="Twitter"
230+
target="_blank"
231+
>
232+
<IconTwitter />
233+
</SocialLink>
234+
)}
235+
{props.youtubeUrl && (
236+
<SocialLink
237+
href={props.youtubeUrl}
238+
title="YouTube"
239+
target="_blank"
240+
>
241+
<IconYoutube />
242+
</SocialLink>
243+
)}
244+
</SocialLinks>
245+
)}
246+
</Fragment>
247+
</FooterNav>
248+
)}
249+
</Menu>
250+
</Wrapper>
251+
);
252+
}
253+
254+
DocsetSwitcher.propTypes = {
255+
open: PropTypes.bool.isRequired,
256+
onClose: PropTypes.func.isRequired,
257+
buttonRef: PropTypes.object.isRequired,
258+
siteName: PropTypes.string.isRequired,
259+
navItems: PropTypes.array.isRequired,
260+
footerNavConfig: PropTypes.object.isRequired,
261+
spectrumUrl: PropTypes.string,
262+
twitterUrl: PropTypes.string,
263+
youtubeUrl: PropTypes.string
264+
};

0 commit comments

Comments
 (0)