11import bindAll from 'lodash.bindall' ;
22import PropTypes from 'prop-types' ;
33import React from 'react' ;
4- import { injectIntl , intlShape } from 'react-intl' ;
4+ import { FormattedMessage , injectIntl , intlShape } from 'react-intl' ;
55import { connect } from 'react-redux' ;
66import AceEditor from 'react-ace' ;
77import {
@@ -21,12 +21,20 @@ import 'ace-builds/src-noconflict/ext-language_tools';
2121
2222import SnippetsCompleter from './ruby-tab/snippets-completer' ;
2323
24+ import rubyIcon from './ruby-tab/icon--ruby.svg' ;
25+ import RubyDownloader from './ruby-downloader.jsx' ;
26+ import collectMetadata from '../lib/collect-metadata.js' ;
27+ import { closeFileMenu } from '../reducers/menus.js' ;
28+ import styles from './ruby-tab/ruby-tab.css' ;
29+ import ReactTooltip from 'react-tooltip' ;
30+
2431class RubyTab extends React . Component {
2532 constructor ( props ) {
2633 super ( props ) ;
2734 bindAll ( this , [
2835 'setAceEditorRef'
2936 ] ) ;
37+ this . mainTooltipId = 'ruby-downloader-tooltip' ;
3038 }
3139
3240 componentDidUpdate ( prevProps ) {
@@ -79,6 +87,17 @@ class RubyTab extends React.Component {
7987 this . aceEditorRef = ref ;
8088 }
8189
90+ getSaveToComputerHandler ( downloadProjectCallback ) {
91+ return ( ) => {
92+ this . props . onRequestCloseFile ( ) ;
93+ downloadProjectCallback ( ) ;
94+ if ( this . props . onProjectTelemetryEvent ) {
95+ const metadata = collectMetadata ( this . props . vm , this . props . projectTitle , this . props . locale ) ;
96+ this . props . onProjectTelemetryEvent ( 'projectDidSave' , metadata ) ;
97+ }
98+ } ;
99+ }
100+
82101 render ( ) {
83102 const {
84103 onChange,
@@ -93,34 +112,66 @@ class RubyTab extends React.Component {
93112 const completers = [ new SnippetsCompleter ( ) ] ;
94113
95114 return (
96- < AceEditor
97- annotations = { errors }
98- editorProps = { { $blockScrolling : true } }
99- fontSize = { 16 }
100- height = "inherit"
101- markers = { markers }
102- mode = "ruby"
103- name = "ruby-editor"
104- ref = { this . setAceEditorRef }
105- setOptions = { {
106- tabSize : 2 ,
107- useSoftTabs : true ,
108- showInvisibles : true ,
109- enableAutoIndent : true ,
110- enableBasicAutocompletion : completers ,
111- enableLiveAutocompletion : true
112- } }
113- style = { {
114- border : '1px solid hsla(0, 0%, 0%, 0.15)' ,
115- borderBottomRightRadius : '0.5rem' ,
116- borderTopRightRadius : '0.5rem' ,
117- fontFamily : [ 'Monaco' , 'Menlo' , 'Consolas' , 'source-code-pro' , 'monospace' ]
118- } }
119- theme = "clouds"
120- value = { code }
121- width = "100%"
122- onChange = { onChange }
123- />
115+ < >
116+ < AceEditor
117+ annotations = { errors }
118+ editorProps = { { $blockScrolling : true } }
119+ fontSize = { 16 }
120+ height = "inherit"
121+ markers = { markers }
122+ mode = "ruby"
123+ name = "ruby-editor"
124+ ref = { this . setAceEditorRef }
125+ setOptions = { {
126+ tabSize : 2 ,
127+ useSoftTabs : true ,
128+ showInvisibles : true ,
129+ enableAutoIndent : true ,
130+ enableBasicAutocompletion : completers ,
131+ enableLiveAutocompletion : true
132+ } }
133+ style = { {
134+ border : '1px solid hsla(0, 0%, 0%, 0.15)' ,
135+ borderBottomRightRadius : '0.5rem' ,
136+ borderTopRightRadius : '0.5rem' ,
137+ fontFamily : [ 'Monaco' , 'Menlo' , 'Consolas' , 'source-code-pro' , 'monospace' ]
138+ } }
139+ theme = "clouds"
140+ value = { code }
141+ width = "100%"
142+ onChange = { onChange }
143+ />
144+ < div className = { styles . wrapper } >
145+ < RubyDownloader > { ( _ , downloadProjectCallback ) => (
146+ < button
147+ className = { styles . button }
148+ onClick = { this . getSaveToComputerHandler ( downloadProjectCallback ) }
149+ data-tip
150+ data-for = { 'ruby-downloader-tooltip' }
151+ >
152+ < img
153+ src = { rubyIcon }
154+ alt = "ruby download"
155+ className = { styles . img }
156+ />
157+
158+ </ button >
159+ ) }
160+ </ RubyDownloader >
161+ < ReactTooltip
162+ id = { this . mainTooltipId }
163+ place = "left"
164+ effect = "solid"
165+ className = { styles . tooltip }
166+ >
167+ < FormattedMessage
168+ defaultMessage = "Download Ruby code to your compute"
169+ description = "Menu bar item for downloading Ruby code to your computer"
170+ id = "gui.smalruby3.menuBar.downloadRubyCodeToComputer"
171+ />
172+ </ ReactTooltip >
173+ </ div >
174+ </ >
124175 ) ;
125176 }
126177}
@@ -131,21 +182,29 @@ RubyTab.propTypes = {
131182 intl : intlShape . isRequired ,
132183 isVisible : PropTypes . bool ,
133184 onChange : PropTypes . func ,
185+ onRequestCloseFile : PropTypes . func ,
186+ onProjectTelemetryEvent : PropTypes . func ,
134187 rubyCode : rubyCodeShape ,
135188 targetCodeToBlocks : PropTypes . func ,
136189 updateRubyCodeTargetState : PropTypes . func ,
137- vm : PropTypes . instanceOf ( VM ) . isRequired
190+ vm : PropTypes . instanceOf ( VM ) . isRequired ,
191+ projectTitle : PropTypes . string ,
192+ locale : PropTypes . string . isRequired
138193} ;
139194
140195const mapStateToProps = state => ( {
141196 blocksTabVisible : state . scratchGui . editorTab . activeTabIndex === BLOCKS_TAB_INDEX ,
142197 editingTarget : state . scratchGui . targets . editingTarget ,
143- rubyCode : state . scratchGui . rubyCode
198+ rubyCode : state . scratchGui . rubyCode ,
199+ vm : state . scratchGui . vm ,
200+ projectTitle : state . scratchGui . projectTitle ,
201+ locale : state . locales . local
144202} ) ;
145203
146204const mapDispatchToProps = dispatch => ( {
147205 onChange : code => dispatch ( updateRubyCode ( code ) ) ,
148- updateRubyCodeTargetState : target => dispatch ( updateRubyCodeTarget ( target ) )
206+ updateRubyCodeTargetState : target => dispatch ( updateRubyCodeTarget ( target ) ) ,
207+ onRequestCloseFile : ( ) => dispatch ( closeFileMenu ( ) )
149208} ) ;
150209
151210export default RubyToBlocksConverterHOC ( injectIntl ( connect (
0 commit comments