diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx
index 261eda83b65..907213481ca 100644
--- a/src/components/gui/gui.jsx
+++ b/src/components/gui/gui.jsx
@@ -364,6 +364,7 @@ const GUIComponent = props => {
diff --git a/src/containers/ruby-tab.jsx b/src/containers/ruby-tab.jsx
index 06d21eaf4e9..ec80ac5874d 100644
--- a/src/containers/ruby-tab.jsx
+++ b/src/containers/ruby-tab.jsx
@@ -1,7 +1,7 @@
import bindAll from 'lodash.bindall';
import PropTypes from 'prop-types';
import React from 'react';
-import {injectIntl, intlShape} from 'react-intl';
+import {FormattedMessage, injectIntl, intlShape} from 'react-intl';
import {connect} from 'react-redux';
import AceEditor from 'react-ace';
import {
@@ -21,12 +21,20 @@ import 'ace-builds/src-noconflict/ext-language_tools';
import SnippetsCompleter from './ruby-tab/snippets-completer';
+import rubyIcon from './ruby-tab/icon--ruby.svg';
+import RubyDownloader from './ruby-downloader.jsx';
+import collectMetadata from '../lib/collect-metadata.js';
+import {closeFileMenu} from '../reducers/menus.js';
+import styles from './ruby-tab/ruby-tab.css';
+import ReactTooltip from 'react-tooltip';
+
class RubyTab extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'setAceEditorRef'
]);
+ this.mainTooltipId = 'ruby-downloader-tooltip';
}
componentDidUpdate (prevProps) {
@@ -79,6 +87,17 @@ class RubyTab extends React.Component {
this.aceEditorRef = ref;
}
+ getSaveToComputerHandler (downloadProjectCallback) {
+ return () => {
+ this.props.onRequestCloseFile();
+ downloadProjectCallback();
+ if (this.props.onProjectTelemetryEvent) {
+ const metadata = collectMetadata(this.props.vm, this.props.projectTitle, this.props.locale);
+ this.props.onProjectTelemetryEvent('projectDidSave', metadata);
+ }
+ };
+ }
+
render () {
const {
onChange,
@@ -93,34 +112,66 @@ class RubyTab extends React.Component {
const completers = [new SnippetsCompleter()];
return (
-
+ <>
+
+
+
{(_, downloadProjectCallback) => (
+
+ )}
+
+
+
+
+
+ >
);
}
}
@@ -131,21 +182,29 @@ RubyTab.propTypes = {
intl: intlShape.isRequired,
isVisible: PropTypes.bool,
onChange: PropTypes.func,
+ onRequestCloseFile: PropTypes.func,
+ onProjectTelemetryEvent: PropTypes.func,
rubyCode: rubyCodeShape,
targetCodeToBlocks: PropTypes.func,
updateRubyCodeTargetState: PropTypes.func,
- vm: PropTypes.instanceOf(VM).isRequired
+ vm: PropTypes.instanceOf(VM).isRequired,
+ projectTitle: PropTypes.string,
+ locale: PropTypes.string.isRequired
};
const mapStateToProps = state => ({
blocksTabVisible: state.scratchGui.editorTab.activeTabIndex === BLOCKS_TAB_INDEX,
editingTarget: state.scratchGui.targets.editingTarget,
- rubyCode: state.scratchGui.rubyCode
+ rubyCode: state.scratchGui.rubyCode,
+ vm: state.scratchGui.vm,
+ projectTitle: state.scratchGui.projectTitle,
+ locale: state.locales.local
});
const mapDispatchToProps = dispatch => ({
onChange: code => dispatch(updateRubyCode(code)),
- updateRubyCodeTargetState: target => dispatch(updateRubyCodeTarget(target))
+ updateRubyCodeTargetState: target => dispatch(updateRubyCodeTarget(target)),
+ onRequestCloseFile: () => dispatch(closeFileMenu())
});
export default RubyToBlocksConverterHOC(injectIntl(connect(
diff --git a/src/containers/ruby-tab/icon--ruby.svg b/src/containers/ruby-tab/icon--ruby.svg
new file mode 100644
index 00000000000..0e063e7f941
--- /dev/null
+++ b/src/containers/ruby-tab/icon--ruby.svg
@@ -0,0 +1,83 @@
+
+
+
+
diff --git a/src/containers/ruby-tab/ruby-tab.css b/src/containers/ruby-tab/ruby-tab.css
new file mode 100644
index 00000000000..f55152f40eb
--- /dev/null
+++ b/src/containers/ruby-tab/ruby-tab.css
@@ -0,0 +1,67 @@
+@import "../../css/colors.css";
+@import "../../css/units.css";
+@import "../../css/z-index.css";
+
+.button {
+ z-index: $z-index-add-button;
+ width: 2.75rem;
+ height: 2.75rem;
+ border: none;
+ border-radius: 100%;
+ background-color: $looks-secondary;
+ box-shadow: 0 0 0 4px $looks-transparent;
+ transition: transform, box-shadow 0.5s;
+}
+.wrapper {
+ position: absolute;
+ width: 2.75rem;
+ height: 2.75rem;
+ bottom: 1rem;
+ right: 1rem;
+ border-radius: 100%;
+}
+.button:hover {
+ transform: scale(1.1);
+ border-radius: 100%;
+ box-shadow: 0 0 0 6px $looks-transparent;
+ background-color: $extensions-primary;
+}
+
+.img {
+ width: 2rem;
+}
+
+.tooltip {
+ background-color: $extensions-primary !important;
+ opacity: 1 !important;
+ border: 1px solid hsla(0, 0%, 0%, .1) !important;
+ border-radius: $form-radius !important;
+ box-shadow: 0 0 .5rem hsla(0, 0%, 0%, .25) !important;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !important;
+ z-index: $z-index-tooltip !important;
+}
+
+.tooltip:after {
+ background-color: $extensions-primary;
+}
+
+$arrow-size: 0.5rem;
+$arrow-inset: -0.25rem;
+$arrow-rounding: 0.125rem;
+
+.tooltip:after {
+ content: "";
+ border-top: 1px solid hsla(0, 0%, 0%, .1) !important;
+ border-left: 0 !important;
+ border-bottom: 0 !important;
+ border-right: 1px solid hsla(0, 0%, 0%, .1) !important;
+ border-radius: $arrow-rounding;
+ height: $arrow-size !important;
+ width: $arrow-size !important;
+}
+
+.tooltip:global(.place-left):after {
+ margin-top: $arrow-inset !important;
+ right: $arrow-inset !important;
+ transform: rotate(45deg) !important;
+}
\ No newline at end of file