UNPKG

14.8 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', {
4 value: true
5});
6
7var _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; };
8
9var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
10
11var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
12
13function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
14
15function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
16
17function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
18
19var _react = require('react');
20
21var _react2 = _interopRequireDefault(_react);
22
23var _reactDom = require('react-dom');
24
25var _reactDom2 = _interopRequireDefault(_reactDom);
26
27var CANCEL_DISTANCE_ON_SCROLL = 20;
28
29var styles = {
30 root: {
31 position: 'absolute',
32 top: 0,
33 left: 0,
34 right: 0,
35 bottom: 0,
36 overflow: 'hidden'
37 },
38 sidebar: {
39 zIndex: 2,
40 position: 'absolute',
41 top: 0,
42 bottom: 0,
43 transition: 'transform .3s ease-out',
44 WebkitTransition: '-webkit-transform .3s ease-out',
45 willChange: 'transform',
46 overflowY: 'auto'
47 },
48 content: {
49 position: 'absolute',
50 top: 0,
51 left: 0,
52 right: 0,
53 bottom: 0,
54 overflow: 'auto',
55 transition: 'left .3s ease-out, right .3s ease-out'
56 },
57 overlay: {
58 zIndex: 1,
59 position: 'fixed',
60 top: 0,
61 left: 0,
62 right: 0,
63 bottom: 0,
64 opacity: 0,
65 visibility: 'hidden',
66 transition: 'opacity .3s ease-out',
67 backgroundColor: 'rgba(0,0,0,.3)'
68 },
69 dragHandle: {
70 zIndex: 1,
71 position: 'fixed',
72 top: 0,
73 bottom: 0
74 }
75};
76
77var Sidebar = (function (_React$Component) {
78 _inherits(Sidebar, _React$Component);
79
80 function Sidebar(props) {
81 _classCallCheck(this, Sidebar);
82
83 _get(Object.getPrototypeOf(Sidebar.prototype), 'constructor', this).call(this, props);
84
85 this.state = {
86 // the detected width of the sidebar in pixels
87 sidebarWidth: 0,
88
89 // keep track of touching params
90 touchIdentifier: null,
91 touchStartX: null,
92 touchStartY: null,
93 touchCurrentX: null,
94 touchCurrentY: null,
95
96 // if touch is supported by the browser
97 dragSupported: typeof window === 'object' && 'ontouchstart' in window
98 };
99
100 this.overlayClicked = this.overlayClicked.bind(this);
101 this.onTouchStart = this.onTouchStart.bind(this);
102 this.onTouchMove = this.onTouchMove.bind(this);
103 this.onTouchEnd = this.onTouchEnd.bind(this);
104 this.onScroll = this.onScroll.bind(this);
105 }
106
107 _createClass(Sidebar, [{
108 key: 'componentDidMount',
109 value: function componentDidMount() {
110 this.saveSidebarWidth();
111 }
112 }, {
113 key: 'componentDidUpdate',
114 value: function componentDidUpdate() {
115 // filter out the updates when we're touching
116 if (!this.isTouching()) {
117 this.saveSidebarWidth();
118 }
119 }
120 }, {
121 key: 'onTouchStart',
122 value: function onTouchStart(ev) {
123 // filter out if a user starts swiping with a second finger
124 if (!this.isTouching()) {
125 var touch = ev.targetTouches[0];
126 this.setState({
127 touchIdentifier: touch.identifier,
128 touchStartX: touch.clientX,
129 touchStartY: touch.clientY,
130 touchCurrentX: touch.clientX,
131 touchCurrentY: touch.clientY
132 });
133 }
134 }
135 }, {
136 key: 'onTouchMove',
137 value: function onTouchMove(ev) {
138 if (this.isTouching()) {
139 for (var ind = 0; ind < ev.targetTouches.length; ind++) {
140 // we only care about the finger that we are tracking
141 if (ev.targetTouches[ind].identifier === this.state.touchIdentifier) {
142 this.setState({
143 touchCurrentX: ev.targetTouches[ind].clientX,
144 touchCurrentY: ev.targetTouches[ind].clientY
145 });
146 break;
147 }
148 }
149 }
150 }
151 }, {
152 key: 'onTouchEnd',
153 value: function onTouchEnd() {
154 if (this.isTouching()) {
155 // trigger a change to open if sidebar has been dragged beyond dragToggleDistance
156 var touchWidth = this.touchSidebarWidth();
157
158 if (this.props.open && touchWidth < this.state.sidebarWidth - this.props.dragToggleDistance || !this.props.open && touchWidth > this.props.dragToggleDistance) {
159 this.props.onSetOpen(!this.props.open);
160 }
161
162 this.setState({
163 touchIdentifier: null,
164 touchStartX: null,
165 touchStartY: null,
166 touchCurrentX: null,
167 touchCurrentY: null
168 });
169 }
170 }
171
172 // This logic helps us prevents the user from sliding the sidebar horizontally
173 // while scrolling the sidebar vertically. When a scroll event comes in, we're
174 // cancelling the ongoing gesture if it did not move horizontally much.
175 }, {
176 key: 'onScroll',
177 value: function onScroll() {
178 if (this.isTouching() && this.inCancelDistanceOnScroll()) {
179 this.setState({
180 touchIdentifier: null,
181 touchStartX: null,
182 touchStartY: null,
183 touchCurrentX: null,
184 touchCurrentY: null
185 });
186 }
187 }
188
189 // True if the on going gesture X distance is less than the cancel distance
190 }, {
191 key: 'inCancelDistanceOnScroll',
192 value: function inCancelDistanceOnScroll() {
193 var cancelDistanceOnScroll = undefined;
194
195 if (this.props.pullRight) {
196 cancelDistanceOnScroll = Math.abs(this.state.touchCurrentX - this.state.touchStartX) < CANCEL_DISTANCE_ON_SCROLL;
197 } else {
198 cancelDistanceOnScroll = Math.abs(this.state.touchStartX - this.state.touchCurrentX) < CANCEL_DISTANCE_ON_SCROLL;
199 }
200 return cancelDistanceOnScroll;
201 }
202 }, {
203 key: 'isTouching',
204 value: function isTouching() {
205 return this.state.touchIdentifier !== null;
206 }
207 }, {
208 key: 'overlayClicked',
209 value: function overlayClicked() {
210 if (this.props.open) {
211 this.props.onSetOpen(false);
212 }
213 }
214 }, {
215 key: 'saveSidebarWidth',
216 value: function saveSidebarWidth() {
217 var width = _reactDom2['default'].findDOMNode(this.refs.sidebar).offsetWidth;
218
219 if (width !== this.state.sidebarWidth) {
220 this.setState({ sidebarWidth: width });
221 }
222 }
223
224 // calculate the sidebarWidth based on current touch info
225 }, {
226 key: 'touchSidebarWidth',
227 value: function touchSidebarWidth() {
228 // if the sidebar is open and start point of drag is inside the sidebar
229 // we will only drag the distance they moved their finger
230 // otherwise we will move the sidebar to be below the finger.
231 if (this.props.pullRight) {
232 if (this.props.open && window.innerWidth - this.state.touchStartX < this.state.sidebarWidth) {
233 if (this.state.touchCurrentX > this.state.touchStartX) {
234 return this.state.sidebarWidth + this.state.touchStartX - this.state.touchCurrentX;
235 }
236 return this.state.sidebarWidth;
237 }
238 return Math.min(window.innerWidth - this.state.touchCurrentX, this.state.sidebarWidth);
239 }
240
241 if (this.props.open && this.state.touchStartX < this.state.sidebarWidth) {
242 if (this.state.touchCurrentX > this.state.touchStartX) {
243 return this.state.sidebarWidth;
244 }
245 return this.state.sidebarWidth - this.state.touchStartX + this.state.touchCurrentX;
246 }
247 return Math.min(this.state.touchCurrentX, this.state.sidebarWidth);
248 }
249 }, {
250 key: 'render',
251 value: function render() {
252 var sidebarStyle = _extends({}, styles.sidebar);
253 var contentStyle = _extends({}, styles.content);
254 var overlayStyle = _extends({}, styles.overlay);
255 var useTouch = this.state.dragSupported && this.props.touch;
256 var isTouching = this.isTouching();
257 var rootProps = {
258 style: styles.root
259 };
260 var dragHandle = undefined;
261
262 // sidebarStyle right/left
263 if (this.props.pullRight) {
264 sidebarStyle.right = 0;
265 sidebarStyle.transform = 'translateX(100%)';
266 sidebarStyle.WebkitTransform = 'translateX(100%)';
267 if (this.props.shadow) {
268 sidebarStyle.boxShadow = '-2px 2px 4px rgba(0, 0, 0, 0.15)';
269 }
270 } else {
271 sidebarStyle.left = 0;
272 sidebarStyle.transform = 'translateX(-100%)';
273 sidebarStyle.WebkitTransform = 'translateX(-100%)';
274 if (this.props.shadow) {
275 sidebarStyle.boxShadow = '2px 2px 4px rgba(0, 0, 0, 0.15)';
276 }
277 }
278
279 if (isTouching) {
280 var percentage = this.touchSidebarWidth() / this.state.sidebarWidth;
281
282 // slide open to what we dragged
283 if (this.props.pullRight) {
284 sidebarStyle.transform = 'translateX(' + (1 - percentage) * 100 + '%)';
285 sidebarStyle.WebkitTransform = 'translateX(' + (1 - percentage) * 100 + '%)';
286 } else {
287 sidebarStyle.transform = 'translateX(-' + (1 - percentage) * 100 + '%)';
288 sidebarStyle.WebkitTransform = 'translateX(-' + (1 - percentage) * 100 + '%)';
289 }
290
291 // fade overlay to match distance of drag
292 overlayStyle.opacity = percentage;
293 overlayStyle.visibility = 'visible';
294 } else if (this.props.docked) {
295 // show sidebar
296 if (this.state.sidebarWidth !== 0) {
297 sidebarStyle.transform = 'translateX(0%)';
298 sidebarStyle.WebkitTransform = 'translateX(0%)';
299 }
300
301 // make space on the left/right side of the content for the sidebar
302 if (this.props.pullRight) {
303 contentStyle.right = this.state.sidebarWidth + 'px';
304 } else {
305 contentStyle.left = this.state.sidebarWidth + 'px';
306 }
307 } else if (this.props.open) {
308 // slide open sidebar
309 sidebarStyle.transform = 'translateX(0%)';
310 sidebarStyle.WebkitTransform = 'translateX(0%)';
311
312 // show overlay
313 overlayStyle.opacity = 1;
314 overlayStyle.visibility = 'visible';
315 }
316
317 if (isTouching || !this.props.transitions) {
318 sidebarStyle.transition = 'none';
319 sidebarStyle.WebkitTransition = 'none';
320 contentStyle.transition = 'none';
321 overlayStyle.transition = 'none';
322 }
323
324 if (useTouch) {
325 if (this.props.open) {
326 rootProps.onTouchStart = this.onTouchStart;
327 rootProps.onTouchMove = this.onTouchMove;
328 rootProps.onTouchEnd = this.onTouchEnd;
329 rootProps.onTouchCancel = this.onTouchEnd;
330 rootProps.onScroll = this.onScroll;
331 } else {
332 var dragHandleStyle = _extends({}, styles.dragHandle);
333 dragHandleStyle.width = this.props.touchHandleWidth;
334
335 // dragHandleStyle right/left
336 if (this.props.pullRight) {
337 dragHandleStyle.right = 0;
338 } else {
339 dragHandleStyle.left = 0;
340 }
341
342 dragHandle = _react2['default'].createElement('div', { style: dragHandleStyle,
343 onTouchStart: this.onTouchStart, onTouchMove: this.onTouchMove,
344 onTouchEnd: this.onTouchEnd, onTouchCancel: this.onTouchEnd });
345 }
346 }
347
348 return _react2['default'].createElement(
349 'div',
350 rootProps,
351 _react2['default'].createElement(
352 'div',
353 { style: sidebarStyle, ref: 'sidebar' },
354 this.props.sidebar
355 ),
356 _react2['default'].createElement('div', { style: overlayStyle,
357 onClick: this.overlayClicked, onTouchTap: this.overlayClicked }),
358 _react2['default'].createElement(
359 'div',
360 { style: contentStyle },
361 dragHandle,
362 this.props.children
363 )
364 );
365 }
366 }]);
367
368 return Sidebar;
369})(_react2['default'].Component);
370
371Sidebar.propTypes = {
372 // main content to render
373 children: _react2['default'].PropTypes.node.isRequired,
374
375 // sidebar content to render
376 sidebar: _react2['default'].PropTypes.node.isRequired,
377
378 // boolean if sidebar should be docked
379 docked: _react2['default'].PropTypes.bool,
380
381 // boolean if sidebar should slide open
382 open: _react2['default'].PropTypes.bool,
383
384 // boolean if transitions should be disabled
385 transitions: _react2['default'].PropTypes.bool,
386
387 // boolean if touch gestures are enabled
388 touch: _react2['default'].PropTypes.bool,
389
390 // max distance from the edge we can start touching
391 touchHandleWidth: _react2['default'].PropTypes.number,
392
393 // Place the sidebar on the right
394 pullRight: _react2['default'].PropTypes.bool,
395
396 // Enable/Disable sidebar shadow
397 shadow: _react2['default'].PropTypes.bool,
398
399 // distance we have to drag the sidebar to toggle open state
400 dragToggleDistance: _react2['default'].PropTypes.number,
401
402 // callback called when the overlay is clicked
403 onSetOpen: _react2['default'].PropTypes.func
404};
405
406Sidebar.defaultProps = {
407 docked: false,
408 open: false,
409 transitions: true,
410 touch: true,
411 touchHandleWidth: 20,
412 pullRight: false,
413 shadow: true,
414 dragToggleDistance: 30,
415 onSetOpen: function onSetOpen() {}
416};
417
418exports['default'] = Sidebar;
419module.exports = exports['default'];
\No newline at end of file