feeles-ide
Version:
The hackable and serializable IDE to make learning material
342 lines (305 loc) • 11.4 kB
JavaScript
import _defineProperty from 'babel-runtime/helpers/defineProperty';
import _regeneratorRuntime from 'babel-runtime/regenerator';
import _asyncToGenerator from 'babel-runtime/helpers/asyncToGenerator';
import _Object$getPrototypeOf from 'babel-runtime/core-js/object/get-prototype-of';
import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
import _createClass from 'babel-runtime/helpers/createClass';
import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
import _inherits from 'babel-runtime/helpers/inherits';
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import beautify from 'js-beautify';
import RaisedButton from 'material-ui/RaisedButton';
import LinearProgress from 'material-ui/LinearProgress';
import AvStop from 'material-ui/svg-icons/av/stop';
import { red50, red500 } from 'material-ui/styles/colors';
import ContentReply from 'material-ui/svg-icons/content/reply';
import { SourceFile } from '../../File/';
import ShotCard from './';
import Editor from '../EditorCard/Editor';
import excessiveCare from './excessiveCare';
var getStyle = function getStyle(props, context, state) {
var _context$muiTheme = context.muiTheme,
palette = _context$muiTheme.palette,
transitions = _context$muiTheme.transitions;
var shooting = state.shooting,
height = state.height;
return {
root: {
display: 'flex',
flexDirection: 'column'
},
editor: {
position: 'relative',
boxSizing: 'border-box',
width: '100%',
height: height,
transform: 'translate(' + (shooting ? '-500px' : 0) + ')',
opacity: shooting ? 0 : 1,
transition: transitions.easeOut()
},
menu: {
position: 'relative',
display: 'flex',
flexDirection: 'row-reverse',
justifyContent: 'space-between',
alignItems: 'flex-end',
height: '2.25em',
marginBottom: '0.25em'
},
shoot: {
marginRight: 9,
marginBottom: 4,
transform: '\n rotateY(' + (shooting ? 180 : 0) + 'deg)'
},
label: {
color: palette.secondaryTextColor,
fontSize: '.8rem'
},
error: {
flex: '0 1 auto',
margin: 0,
padding: 8,
backgroundColor: red50,
color: red500,
fontFamily: 'Consolas, "Liberation Mono", Menlo, Courier, monospace',
overflow: 'scroll'
},
restore: {
margin: 4
}
};
};
var ShotPane = function (_PureComponent) {
_inherits(ShotPane, _PureComponent);
function ShotPane() {
var _ref,
_this2 = this;
var _temp, _this, _ret;
_classCallCheck(this, ShotPane);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = ShotPane.__proto__ || _Object$getPrototypeOf(ShotPane)).call.apply(_ref, [this].concat(args))), _this), _this.state = {
shooting: false,
height: 0,
error: null,
loading: false,
canRestore: false,
file: _this.props.file || SourceFile.shot(''),
cardAnchorEl: null
}, _this.handleShot = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
if (!_this.state.shooting) {
_context.next = 2;
break;
}
return _context.abrupt('return');
case 2:
_context.next = 4;
return _this.shotCode();
case 4:
_this.setState({ shooting: true });
case 5:
case 'end':
return _context.stop();
}
}
}, _callee, _this2);
})), _this.handleChange = function (cm) {
var canRestore = cm.getValue() !== _this.state.file.text;
_this.setState({ canRestore: canRestore });
}, _this.handleRestore = function () {
_this.codeMirror.setValue(_this.state.file.text);
}, _this.handleViewportChange = function (cm) {
var lastLine = cm.lastLine() + 1;
var height = cm.heightAtLine(lastLine, 'local');
// もしエディタの描画領域が広過ぎて ShotCard が画面からはみ出すなら, height を更新しない
var cardAnchorEl = _this.state.cardAnchorEl;
if (cardAnchorEl) {
var offsetParent = cardAnchorEl.offsetParent,
offsetTop = cardAnchorEl.offsetTop;
var appendedHeight = height - _this.state.height;
var containerHeight = offsetParent.clientHeight - parseInt(offsetParent.style.paddingTop, 10) - parseInt(offsetParent.style.paddingBottom, 10);
if (offsetTop + appendedHeight >= containerHeight) {
return;
}
}
_this.setState({ height: height });
}, _temp), _possibleConstructorReturn(_this, _ret);
}
_createClass(ShotPane, [{
key: 'componentDidMount',
value: function componentDidMount() {
this.codeMirror.on('beforeChange', excessiveCare);
this.codeMirror.on('change', this.handleChange);
this.codeMirror.on('swapDoc', this.handleChange);
this.codeMirror.on('viewportChange', this.handleViewportChange);
this.codeMirror.on('swapDoc', this.handleViewportChange);
this.handleViewportChange(this.codeMirror);
this.props.globalEvent.on('message.runCode', this.handleShot);
}
}, {
key: 'componentDidUpdate',
value: function componentDidUpdate(prevProps, prevState) {
var _this3 = this;
if (prevProps.file !== this.props.file) {
var file = this.props.file || SourceFile.shot('');
this.setState({ file: file });
}
if (!prevState.shooting && this.state.shooting) {
// shooting アニメーションをもとにもどす
setTimeout(function () {
_this3.setState({ shooting: false });
}, 1000);
}
if (prevState.height !== this.state.height) {
setTimeout(function () {
// 表示可能領域が変わったので、トランジション後に再描画する
if (_this3.codeMirror) {
_this3.codeMirror.refresh();
}
}, 300);
}
// ShotCard の中にある最も下の要素を取得する
var cardAnchorEl = document.querySelector('#ShotCard-BottomAnchor');
if (!prevState.cardAnchorEl && cardAnchorEl) {
this.setState({ cardAnchorEl: cardAnchorEl });
}
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
this.props.globalEvent.off('message.runCode', this.handleShot);
}
}, {
key: 'shotCode',
value: function () {
var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
var text, configs, formatted, name, file, request;
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
text = this.codeMirror ? this.codeMirror.getValue('\n') : this.state.file.text;
// コードのフォーマット
if (this.props.loadConfig('feelesrc').formatOnSendCode || false) {
// import .jsbeautifyrc
configs = this.props.loadConfig('jsbeautifyrc');
formatted = beautify(text, configs.js || {});
this.codeMirror.setValue(formatted);
}
// コードをファイルにする
name = this.state.file.name;
file = SourceFile.shot(text, name);
// frame に shot をおくる
request = {
query: 'shot',
value: file.serialize()
};
this.props.globalEvent.emit('postMessage', request);
case 6:
case 'end':
return _context2.stop();
}
}
}, _callee2, this);
}));
function shotCode() {
return _ref3.apply(this, arguments);
}
return shotCode;
}()
}, {
key: 'render',
value: function render() {
var _this4 = this;
var _props = this.props,
localization = _props.localization,
getConfig = _props.getConfig,
loadConfig = _props.loadConfig;
var styles = getStyle(this.props, this.context, this.state);
// TODO: Enter で実行か Shift-Enter で実行か
var _loadConfig = loadConfig('feelesrc'),
sendCodeOnEnter = _loadConfig.sendCodeOnEnter;
var shootKey = sendCodeOnEnter ? 'Enter' : 'Ctrl-Enter';
var extraKeys = _defineProperty({}, shootKey, this.handleShot);
return React.createElement(
'div',
null,
this.state.error ? React.createElement(
'pre',
{ style: styles.error },
this.state.error.message
) : null,
this.state.loading ? React.createElement(LinearProgress, null) : null,
React.createElement(
'div',
{ style: styles.menu },
React.createElement(RaisedButton, {
primary: true,
label: localization.shotCard.button,
icon: this.state.shooting ? React.createElement(AvStop, null) : React.createElement(ContentReply, null),
labelPosition: 'before',
disabled: this.state.shooting,
onClick: this.handleShot,
style: styles.shoot
}),
React.createElement(
'span',
{ style: styles.label },
localization.shotCard.shoot
),
React.createElement('div', { style: { flex: 1 } }),
React.createElement(RaisedButton, {
secondary: true,
label: localization.shotCard.restore,
onClick: this.handleRestore,
style: styles.restore,
disabled: !this.state.canRestore
})
),
React.createElement(
'div',
{ style: styles.editor },
React.createElement(Editor, {
isSelected: true,
isCared: true,
file: this.state.file,
getConfig: getConfig,
codemirrorRef: function codemirrorRef(ref) {
return _this4.codeMirror = ref;
},
snippets: this.props.completes,
extraKeys: extraKeys,
lineNumbers: false,
findFile: this.props.findFile,
loadConfig: this.props.loadConfig,
fileView: this.props.fileView,
handleSetLinkObjects: this.props.handleSetLinkObjects
})
)
);
}
}]);
return ShotPane;
}(PureComponent);
ShotPane.propTypes = {
fileView: PropTypes.object.isRequired,
files: PropTypes.array.isRequired,
findFile: PropTypes.func.isRequired,
localization: PropTypes.object.isRequired,
getConfig: PropTypes.func.isRequired,
loadConfig: PropTypes.func.isRequired,
file: PropTypes.object,
completes: PropTypes.array,
globalEvent: PropTypes.object.isRequired,
handleSetLinkObjects: PropTypes.func.isRequired
};
ShotPane.contextTypes = {
muiTheme: PropTypes.object.isRequired
};
export default ShotPane;