UNPKG

feeles-ide

Version:

The hackable and serializable IDE to make learning material

342 lines (305 loc) 11.4 kB
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;