UNPKG

5.76 kBJavaScriptView Raw
1var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
2
3/** @jsx h */
4import cn from 'classnames';
5import { h, Component } from 'preact';
6import { LogoDark, LogoTextOnly } from './logo';
7import { Drawer } from './drawer';
8import { Shield } from './shield';
9import { Footer } from './footer';
10import { TapIconButton } from './button';
11import omit from './utils/omit';
12import { throttle } from './utils/throttle';
13
14class DrawerContentWrapper extends Component {
15 constructor(props) {
16 super(props);
17 this.handleScroll = throttle(this.handleScroll.bind(this), 200);
18 }
19
20 handleScroll() {
21 if (!this.base) {
22 return;
23 }
24 const { updateScrolledDown, scrolledDown } = this.props;
25 const { scrollTop } = this.base;
26 if (scrollTop === 0 && scrolledDown) {
27 updateScrolledDown(false);
28 }
29 if (!scrolledDown && scrollTop > 0) {
30 updateScrolledDown(true);
31 }
32 }
33
34 componentDidMount() {
35 this.base.addEventListener('scroll', this.handleScroll, { passive: true });
36 }
37
38 componentWillUnMount() {
39 this.base.removeEventListener('scroll', this.handleScroll, {
40 passive: true
41 });
42 }
43
44 render({ style, children }) {
45 return h(
46 'div',
47 { className: 'v-scroll h-100 w-100', style: style },
48 children
49 );
50 }
51}
52
53export class AppLayout extends Component {
54 constructor(props) {
55 super(props);
56 this.state = {
57 drawerScrolledDown: false
58 };
59 this.updateScrolledDown = this.updateScrolledDown.bind(this);
60 }
61
62 updateScrolledDown(scrolledDown) {
63 this.setState({ drawerScrolledDown: scrolledDown });
64 }
65
66 render(props, { drawerScrolledDown }) {
67 const {
68 drawerOpen,
69 screenIsWide,
70 drawerEnabled,
71 drawerContent,
72 statusBarContent,
73 extraContent,
74 drawerWidth = 256,
75 mainContent,
76 doCloseDrawer,
77 hideFooter,
78 version,
79 logoHref,
80 logoOnClick,
81 preventScroll
82 } = props;
83
84 const rest = omit(props, ['drawerOpen', 'screenIsWide', 'drawerEnabled', 'drawerContent', 'statusBarContent', 'extraContent', 'drawerWidth', 'mainContent', 'doCloseDrawer', 'hideFooter', 'version', 'logoHref', 'logoOnClick', 'preventScroll']);
85
86 const drawerAlwaysVisible = drawerEnabled && screenIsWide;
87 const useFloatingDrawer = drawerEnabled && !screenIsWide;
88 const showShield = useFloatingDrawer && drawerOpen;
89 const floatingDrawerHidden = useFloatingDrawer && !drawerOpen;
90
91 const leftMarginStyles = drawerAlwaysVisible ? {
92 width: `calc(100% - ${drawerWidth}px)`,
93 marginLeft: `${drawerWidth}px`
94 } : {
95 width: '100%'
96 };
97
98 if (showShield || preventScroll) {
99 document.body.classList.add('overflow-hidden');
100 } else {
101 document.body.classList.remove('overflow-hidden');
102 }
103
104 const drawerHeaderHeightPx = (drawerAlwaysVisible ? 80 : 60) + 'px';
105
106 const logoClasses = cn({ pointer: logoHref || logoOnClick });
107
108 return h(
109 'div',
110 _extends({ className: 'bg-near-white dark-blue sans-serif min-vh-100' }, rest),
111 statusBarContent && h(
112 'div',
113 {
114 className: 'sticky no-print top-0 bg-light-gray z-1 shadow-1',
115 style: leftMarginStyles
116 },
117 statusBarContent
118 ),
119 drawerEnabled && h(
120 Drawer,
121 {
122 'aria-hidden': floatingDrawerHidden,
123 className: useFloatingDrawer ? 'z-4 transition' : '',
124 style: {
125 width: `${drawerWidth}px`,
126 transform: floatingDrawerHidden ? `translateX(${-drawerWidth}px)` : null
127 }
128 },
129 h(
130 'div',
131 {
132 className: cn('absolute left-0 bg-dark-blue z-1 top-0 pa2 pa3-l', { 'shadow-1': drawerScrolledDown }),
133 style: {
134 width: `${drawerWidth}px`,
135 height: drawerHeaderHeightPx
136 }
137 },
138 screenIsWide && h(
139 'a',
140 {
141 className: cn('db link white', logoClasses),
142 href: logoHref,
143 onClick: logoOnClick
144 },
145 h(LogoDark, null)
146 ),
147 !screenIsWide && h(
148 'div',
149 { className: 'flex items-center justify-between' },
150 h(TapIconButton, {
151 icon: 'close',
152 color: 'white',
153 className: 'flex-shrink-0',
154 onClick: e => {
155 e.preventDefault();
156 doCloseDrawer();
157 }
158 }),
159 h(
160 'a',
161 {
162 className: logoClasses,
163 href: logoHref,
164 onClick: logoOnClick
165 },
166 h(LogoTextOnly, {
167 className: 'mh3',
168 style: { paddingTop: '6px', width: '168px' }
169 })
170 )
171 )
172 ),
173 h(
174 DrawerContentWrapper,
175 {
176 scrolledDown: drawerScrolledDown,
177 updateScrolledDown: this.updateScrolledDown,
178 style: {
179 paddingTop: drawerHeaderHeightPx,
180 paddingBottom: '120px'
181 }
182 },
183 drawerContent
184 )
185 ),
186 showShield && h(Shield, {
187 className: 'z-3 dn-l',
188 onClick: () => doCloseDrawer && doCloseDrawer()
189 }),
190 h(
191 'main',
192 {
193 style: leftMarginStyles,
194 className: 'bg-nearly-white overflow-x-hidden'
195 },
196 mainContent,
197 hideFooter !== true && h(Footer, { version: version })
198 ),
199 extraContent
200 );
201 }
202}
\No newline at end of file