UNPKG

atom-nuclide

Version:

A unified developer experience for web and mobile development, built as a suite of features on top of Atom to provide hackability and the support of an active community.

290 lines (255 loc) 11.6 kB
Object.defineProperty(exports, '__esModule', { value: true }); var _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; }; })(); var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _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; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } function _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; } /* * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the license found in the LICENSE file in * the root directory of this source tree. */ var _atom2; function _atom() { return _atom2 = require('atom'); } var _PanelComponentScroller2; function _PanelComponentScroller() { return _PanelComponentScroller2 = require('./PanelComponentScroller'); } var _reactForAtom2; function _reactForAtom() { return _reactForAtom2 = require('react-for-atom'); } var MINIMUM_LENGTH = 100; /** * A container for centralizing the logic for making panels scrollable, * resizeable, dockable, etc. */ var PanelComponent = (function (_React$Component) { _inherits(PanelComponent, _React$Component); _createClass(PanelComponent, null, [{ key: 'defaultProps', value: { hidden: false, initialLength: 200, noScroll: false, onResize: function onResize(width) {} }, enumerable: true }]); function PanelComponent(props) { _classCallCheck(this, PanelComponent); _get(Object.getPrototypeOf(PanelComponent.prototype), 'constructor', this).call(this, props); this.state = { isResizing: false, length: this.props.initialLength }; // Bind main events to this object. _updateSize is only ever bound within these. this._handleDoubleClick = this._handleDoubleClick.bind(this); this._handleMouseDown = this._handleMouseDown.bind(this); this._handleMouseMove = this._handleMouseMove.bind(this); this._handleMouseUp = this._handleMouseUp.bind(this); } _createClass(PanelComponent, [{ key: 'componentDidMount', value: function componentDidMount() { // Note: This method is called via `requestAnimationFrame` rather than `process.nextTick` like // Atom's tree-view does because this does not have a guarantee a paint will have already // happened when `componentDidMount` gets called the first time. this._animationFrameRequestId = window.requestAnimationFrame(this._repaint.bind(this)); } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { if (this._resizeSubscriptions != null) { this._resizeSubscriptions.dispose(); } if (this._animationFrameRequestId != null) { window.cancelAnimationFrame(this._animationFrameRequestId); } } /** * Forces the potentially scrollable region to redraw so its scrollbars are drawn with styles from * the active theme. This mimics the login in Atom's tree-view [`onStylesheetChange`][1]. * * [1] https://github.com/atom/tree-view/blob/v0.201.5/lib/tree-view.coffee#L722 */ }, { key: '_repaint', value: function _repaint() { var element = (_reactForAtom2 || _reactForAtom()).ReactDOM.findDOMNode(this); var isVisible = window.getComputedStyle(element, null).getPropertyValue('visibility'); if (isVisible) { // Force a redraw so the scrollbars are styled correctly based on the theme element.style.display = 'none'; element.offsetWidth; element.style.display = ''; } } }, { key: 'render', value: function render() { // We create an overlay to always display the resize cursor while the user // is resizing the panel, even if their mouse leaves the handle. var resizeCursorOverlay = null; if (this.state.isResizing) { resizeCursorOverlay = (_reactForAtom2 || _reactForAtom()).React.createElement('div', { className: 'nuclide-ui-panel-component-resize-cursor-overlay ' + this.props.dock }); } var containerStyle = undefined; if (this.props.dock === 'left' || this.props.dock === 'right') { containerStyle = { width: this.state.length, minWidth: MINIMUM_LENGTH }; } else if (this.props.dock === 'top' || this.props.dock === 'bottom') { containerStyle = { height: this.state.length, minHeight: MINIMUM_LENGTH }; } var content = (_reactForAtom2 || _reactForAtom()).React.cloneElement((_reactForAtom2 || _reactForAtom()).React.Children.only(this.props.children), { ref: 'child' }); var wrappedContent = undefined; if (this.props.noScroll) { wrappedContent = content; } else { wrappedContent = (_reactForAtom2 || _reactForAtom()).React.createElement( (_PanelComponentScroller2 || _PanelComponentScroller()).PanelComponentScroller, { overflowX: this.props.overflowX }, content ); } // Use the `tree-view-resizer` class from Atom's [tree-view][1] because it is targeted by some // themes, like [spacegray-dark-ui][2], to customize the scroll bar in the tree-view. Use this // inside `PanelComponent` rather than just file-tree so any scrollable panels created with this // component are styled accordingly. // // [1] https://github.com/atom/tree-view/blob/v0.201.5/lib/tree-view.coffee#L28 // [2] https://github.com/cannikin/spacegray-dark-ui/blob/v0.12.0/styles/tree-view.less#L21 return (_reactForAtom2 || _reactForAtom()).React.createElement( 'div', { className: 'nuclide-ui-panel-component tree-view-resizer ' + this.props.dock, hidden: this.props.hidden, style: containerStyle }, (_reactForAtom2 || _reactForAtom()).React.createElement('div', { className: 'nuclide-ui-panel-component-resize-handle ' + this.props.dock, ref: 'handle', onMouseDown: this._handleMouseDown, onDoubleClick: this._handleDoubleClick }), (_reactForAtom2 || _reactForAtom()).React.createElement( 'div', { className: 'nuclide-ui-panel-component-content' }, wrappedContent ), resizeCursorOverlay ); } /** * Returns the current resizable length. * * For panels docked left or right, the length is the width. For panels * docked top or bottom, it's the height. */ }, { key: 'getLength', value: function getLength() { return this.state.length; } }, { key: 'focus', value: function focus() { this.refs.child.focus(); } }, { key: 'getChildComponent', value: function getChildComponent() { return this.refs.child; } }, { key: '_handleMouseDown', value: function _handleMouseDown(event) { var _this = this; if (this._resizeSubscriptions != null) { this._resizeSubscriptions.dispose(); } window.addEventListener('mousemove', this._handleMouseMove); window.addEventListener('mouseup', this._handleMouseUp); this._resizeSubscriptions = new (_atom2 || _atom()).CompositeDisposable({ dispose: function dispose() { window.removeEventListener('mousemove', _this._handleMouseMove); } }, { dispose: function dispose() { window.removeEventListener('mouseup', _this._handleMouseUp); } }); this.setState({ isResizing: true }); } }, { key: '_handleMouseMove', value: function _handleMouseMove(event) { var containerEl = (_reactForAtom2 || _reactForAtom()).ReactDOM.findDOMNode(this); var length = 0; switch (this.props.dock) { case 'left': length = event.pageX - containerEl.getBoundingClientRect().left; break; case 'top': length = event.pageY - containerEl.getBoundingClientRect().top; break; case 'bottom': length = containerEl.getBoundingClientRect().bottom - event.pageY; break; case 'right': length = containerEl.getBoundingClientRect().right - event.pageX; break; } this._updateSize(length); } }, { key: '_handleMouseUp', value: function _handleMouseUp(event) { if (this._resizeSubscriptions) { this._resizeSubscriptions.dispose(); } this.setState({ isResizing: false }); } /** * Resize the pane to fit its contents. */ }, { key: '_handleDoubleClick', value: function _handleDoubleClick() { var _this2 = this; // Reset size to 0 and read the content's natural width (after re-layout) // to determine the size to scale to. this.setState({ length: 0 }); this.forceUpdate(function () { var length = 0; var childNode = (_reactForAtom2 || _reactForAtom()).ReactDOM.findDOMNode(_this2.refs.child); var handle = (_reactForAtom2 || _reactForAtom()).ReactDOM.findDOMNode(_this2.refs.handle); if (_this2.props.dock === 'left' || _this2.props.dock === 'right') { length = childNode.offsetWidth + handle.offsetWidth; } else if (_this2.props.dock === 'top' || _this2.props.dock === 'bottom') { length = childNode.offsetHeight + handle.offsetHeight; } else { throw new Error('unhandled dock'); } _this2._updateSize(length); }); } // Whether this is width or height depends on the orientation of this panel. }, { key: '_updateSize', value: function _updateSize(newSize) { this.setState({ length: newSize }); this.props.onResize.call(null, newSize); } }]); return PanelComponent; })((_reactForAtom2 || _reactForAtom()).React.Component); exports.PanelComponent = PanelComponent; /* * When `true`, this component does not wrap its children in a scrolling container and instead * provides a simple container with visible (the default in CSS) overflow. Default: false. */