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
JavaScript
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.
*/