diff --git a/src/components/courses/CourseSelect.js b/src/components/courses/CourseSelect.js index af1eb9e8..055f8695 100644 --- a/src/components/courses/CourseSelect.js +++ b/src/components/courses/CourseSelect.js @@ -95,19 +95,24 @@ class CourseSelectModal extends Component { render() { const { classes } = this.props; const courses = [].concat(this.props.courses); + const style = { + default: { + margin: 1, + marginTop: 8, + color: "#fff", + } + }; return (
{ !this.props.hideTooltip ? - + + size="small" + style={style.default} + className="header-btn d-none d-sm-block"> school diff --git a/src/components/editor/Editor.js b/src/components/editor/Editor.js index f3a2e440..b98a8a38 100644 --- a/src/components/editor/Editor.js +++ b/src/components/editor/Editor.js @@ -9,11 +9,52 @@ import customCompleter from "./customCompleter.js"; import KeyboardShortcut from "./KeyboardShortcut.js"; import { browserType } from "../../utils/browserType"; import FontSize from "./FontSize.js"; +import Reference from "../reference/Reference.js"; +import SceneConfigMenu from "../structural/header/SceneConfigMenu.js"; +import { save } from "../../actions/projectActions.js"; + + +import sockets from "socket.io-client"; + + + +import "../../css/App.css"; + +import { + createTheme, + ThemeProvider, +} from "@material-ui/core"; /** - * Editor is a React Component that create the Ace Editor in the DOM. + * Editor is a React Component that creates the Ace Editor in the DOM. */ class Editor extends Component { + constructor(props) { + super(props); + this.state = { + savedSettings: [], + logMenuOpen: false, + availProj: [], + sampleProj: [], + collectionOpen: false, + projectsOpen: false, + projectTab: "a", + snackOpen: false, + lastMsgTime: 0, + anchorEl: null, + navAwayModal: false, + needsNewId: false, // this explicitly tells us to make a new id + spinnerOpen: false, + coursesOpen: false, + tourOpen: false, + welcomeOpen: false, + updateCollection: false, + fetchCollection: false, + socket: sockets(), + googleUser: undefined + }; + } + /** * Called when the Edtior is unmounting (Being removed from the DOM) * @@ -43,6 +84,9 @@ class Editor extends Component { console.error("Unable to attach custom completers"); } + if (this.props.refExName) { + this.props.referenceExampleActions.fetchReferenceExample(this.props.refExName); + } // Warn the issue before refreshing the page window.addEventListener("beforeunload", (event) => { let text; @@ -79,19 +123,170 @@ class Editor extends Component { this.props.userActions.updateUserSettings(this.props.user.uid,this.props.settings); this.setState({"previousSettings":this.props.settings}); } + /*if (this.state.savedSettings.length === 0 && this.props.scene.id !== 0) { + this.setState({ savedSettings: this.buildSettingsArr() }); + + window.addEventListener("beforeunload", (event) => { + let finalSettings = this.buildSettingsArr(); + + if (!this.settingsEqual(finalSettings)) { + event.preventDefault(); + event.returnValue = "You may have unsaved changes!"; + } + }); + } */ } + + /** + * Flatten the sceneSettings from props (object) into an array for comparason + * @returns {array} Array of the scene settings + */ + buildSettingsArr = () => { + const sceneSettings = this.props.scene.settings; + return [sceneSettings.floorColor, + sceneSettings.showCoordHelper, sceneSettings.showFloor, + sceneSettings.skyColor, sceneSettings.viewOnly]; + }; + /** + * Compare two arrays of setting and determine whether is the settings are equal or not + * @param {array} newSettings Settings to compare + * @returns {boolean} If settings are equal or not + */ + settingsEqual = (newSettings) => { + for (let i = 0; i < newSettings.length; ++i) { + if (newSettings[i] !== this.state.savedSettings[i]) { + return false; + } + } + return true; + } + + /** + * handeRender gets the information from Ace Editor and calls the action: render() + */ + handleRender = () => { + try { + let editor = window.ace.edit("ace-editor"); + this.props.actions.render(editor.getSession().getValue(), this.props.user ? this.props.user.uid : "anon"); + } catch (error) { + this.props.actions.render(this.props.text, this.props.user ? this.props.user.uid : "anon"); + } + } + + /** + * When the user clicks save it will upload the information to Firebase + */ + handleSave = (newCollectionID = undefined) => { + let editor, text; + if (!this.props.viewOnly) { + //If in editor mode, gets text directly from editor + editor = window.ace.edit("ace-editor"); + text = editor.getSession().getValue(); + } else { + //Otherwise, gets text from state (should be up to date since it is refreshed on editor unmount) + text = this.props.text; + } + + if (this.props.user && this.props.user.uid && text) { + this.setState({ spinnerOpen: true }); + let scene = document.querySelector("a-scene"); + // Access the scene and screen shot, with perspective view in a lossy jpeg format + let img = scene.components.screenshot.getCanvas("perspective").toDataURL("image/jpeg", 0.1); + + let newScene = { + name: (this.props.scene.name ? this.props.scene.name : "Untitled Scene"), + desc: this.props.scene.desc, + code: text, + uid: this.props.user.uid, + settings: { + ...this.props.scene.settings, + collectionID: newCollectionID || this.props.scene.settings.collectionID + }, + updateTime: Date.now(), + createTime: (this.props.scene.createTime ? this.props.scene.createTime : Date.now()) + }; + + save(this.props.user.uid, newScene, img, this.props.projectId).then((projectId) => { + if (!projectId) { + console.error("Could not save the scene"); + } + + this.props.actions.updateSavedText(text); + // If we have a new projectId reload page with it + if (projectId !== this.props.projectId) { + this.setState({ spinnerOpen: false }); + window.location.assign(`${window.origin}/scene/${projectId}`); + this.props.projectActions.asyncUserProj(this.props.user.uid); + } + if (!this.state.viewOnly) { + this.props.actions.refresh(text, this.props.user ? this.props.user.uid : "anon"); + } + this.setState({ spinnerOpen: false, saveOpen: false }); + this.state.socket.emit("save"); + return true; + }); + } else if (!text) { + alert("There is no code to save for this scene. Try adding some in the editor!"); + } else { + // TODO: Don't use alert + alert("We were unable to save your project. Are you currently logged in?"); + } + + if (!this.state.viewOnly) { + this.props.actions.refresh(text, this.props.user ? this.props.user.uid : "anon"); + } + this.setState({ savedSettings: this.buildSettingsArr() }); + } + + /** + * toggles the save drawer + */ + handleSaveToggle = () => this.setState({ saveOpen: !this.state.saveOpen }); + + /** + * forces save drawer closed + */ + handleSaveClose = () => this.setState({ saveOpen: false }); + + /** + * forces save drawer closed + */ + handleSaveOpen = () => this.setState({ saveOpen: true }); + /** * Creates the editor in the DOM */ render() { + const keyTheme = createTheme({ + palette: { + primary: { + main: "#ffeb3b", + }, + secondary: { + main: "#9c27b0", + } + }, + }); + const refTheme = createTheme({ + palette: { + primary: { + main: "#a31545", + contrastText: "#FFFFFF" + }, + secondary: { + main: "#4caf50", + contrastText: "#FFFFFF" + }, + }, + }); return ( -
+
- { browserType() === "desktop" ?
-
: null } + { browserType() === "desktop" ?
+ + + + + + + + + + + + + +
: null }
); } diff --git a/src/components/editor/FontSize.js b/src/components/editor/FontSize.js index 0ff83424..3059fa9f 100644 --- a/src/components/editor/FontSize.js +++ b/src/components/editor/FontSize.js @@ -3,10 +3,25 @@ import { Select, Button, Tooltip, + createTheme, + ThemeProvider, Icon } from "@material-ui/core"; + import "../../css/KeyboardShortcut.css"; + +const fontTheme = createTheme({ + palette: { + primary: { + main: "#9c27b0", + }, + secondary: { + main: "#9c27b0", + } + }, +}); + const options=[12, 14, 18, 24, 30, 36, 48]; class FontSize extends React.Component { @@ -17,21 +32,23 @@ class FontSize extends React.Component { render(){ return(
- - - + + + + +
); } diff --git a/src/components/editor/KeyboardShortcut.js b/src/components/editor/KeyboardShortcut.js index 1e4079bb..062c7ed0 100644 --- a/src/components/editor/KeyboardShortcut.js +++ b/src/components/editor/KeyboardShortcut.js @@ -5,6 +5,8 @@ import { Icon, Tooltip, Popover, + createTheme, + ThemeProvider, } from "@material-ui/core"; import "../../css/KeyboardShortcut.css"; @@ -69,6 +71,17 @@ const scene = [ }, ]; +const keyboardTheme = createTheme({ + palette: { + primary: { + main: "#1769aa", + }, + secondary: { + main: "#9c27b0", + } + }, +}); + /** * React components create the button with KeyboardShortcut window */ @@ -125,15 +138,18 @@ class KeyboardShortcut extends React.Component { render(){ return(
- - - + + + + + -
{ scene.settings.viewOnly @@ -50,11 +29,53 @@ export const Ide = ({ editor, editorActions, user, usersettings, userActions, au
: <> -
- +
+
+
-
- +
+
} diff --git a/src/components/reference/AssetReferencePage.js b/src/components/reference/AssetReferencePage.js index 186807f1..3d179c82 100644 --- a/src/components/reference/AssetReferencePage.js +++ b/src/components/reference/AssetReferencePage.js @@ -36,7 +36,7 @@ export default class AssetReference extends React.Component { return (
diff --git a/src/components/reference/Reference.js b/src/components/reference/Reference.js index c4e44723..85eb1e0e 100644 --- a/src/components/reference/Reference.js +++ b/src/components/reference/Reference.js @@ -1,6 +1,9 @@ import React, { Component } from "react"; + import myrReference from "../../myr/reference"; import * as refFunctions from "../../myr/reference"; +import ModelTab from "./ModelReferenceTab"; +import TextureTab from "./TextureReferenceTab"; import * as layoutTypes from "../../constants/LayoutTypes"; @@ -16,53 +19,99 @@ import { TableRow, TableCell, Tooltip, + Button, + createTheme, + ThemeProvider, + //Select, Hidden } from "@material-ui/core"; +import "../../css/KeyboardShortcut.css"; + const exitBtnStyle = { - //paddingbottom: 100, position: "absolute", top: 0, right: 10, }; const newTabStyle = { - position: "fixed", + position: "absolute", top: 0, right: 50, }; const assetReferenceBtn = { - position: "fixed", + position: "absolute", top: 0, - right: 95, + right: 50, }; +const buttonTheme = createTheme({ + palette: { + primary: { + main: "#a31545", + contrastText: "#FFFFFF" + }, + secondary: { + main: "#4caf50", + contrastText: "#FFFFFF" + }, + }, +}); + /** * Reference is a react component that creates drawer contains references */ class Reference extends Component { /** * Constructor - * value represents the current tab that's opened + * value represents the current reference tab that's opened + * assetValue represents the current asset reference tab that's opened */ constructor(props) { super(props); this.state = { value: "a", + assetValue: "a", + isResizing: false, + lastDownX: 0, + lastDownY: 0, + newWidth: {width: 0}, + newHeight: {height: 0}, + refOpen: false, + assetOpen: false, }; + this.tableData = myrReference(); } + componentDidMount() { + document.addEventListener("mousemove", e => this.handleMousemove(e)); + document.addEventListener("mouseup", e => this.handleMouseup(e)); + document.addEventListener("mousedown", e => this.handleMousedown(e)); + + const qsa = new URLSearchParams(window.location.search); + if (qsa.has("tab") && qsa.get("tab").toLowerCase() === "textures") { + this.setState({ assetValue: "b" }); + } + } + /** * Handler for when user clicked the tab, it updates the state "value" with value passed in * * @param {Event} event * @param {string} value tab to be changed to. It should be an alphabet +* @param {string} assetValue tab to be changed to in asset drawer. It should be an alphabet + */ handleChange = (event, value) => { this.setState({ value }); }; + handleAssetChange = (event, assetValue) => { + this.setState({ assetValue }); + }; + + /** * Handler for opening the reference page */ @@ -71,11 +120,82 @@ class Reference extends Component { this.setState({ value: "a" }); }; + /** + * Handler for opening the asset reference page + */ assetHandleOpen = () => { window.open(window.origin + "/asset-reference"); - this.setState({ value: "a" }); + this.setState({ assetValue: "a" }); }; + /** + * Handler for opening the reference drawer + */ + handleRefClick = () =>{ + this.setState({ + refOpen: true, + }); + }; + + /** + * Handler for closing the reference drawer + */ + handleRefClose = () => { + this.setState({ + refOpen: false, + }); + }; + + /** + * Handler for opening the asset reference drawer + */ + handleAssetClick = () =>{ + this.setState({ + assetOpen: true, + }); + }; + + /** + * Handler for closing the asset reference drawer + */ + handleAssetClose = () => { + this.setState({ + assetOpen: false, + }); + }; + + /** + * Checks that the drawer is only draggable whe the user pressed down on it + */ + handleMousedown = e => { + if(this.state.newHeight === undefined) { + this.setState({newHeight: {height: 1}}); + } + if(this.state.newWidth === undefined) { + this.setState({newWidth: {width: 1}}); + } + + console.log("e.clientX: ", e.clientX, " this.state.newWidth.width: ", this.state.newWidth.width); + if(((648-e.clientY) < (this.state.newHeight.height)) && ((e.clientX) < (528))) { + this.setState({ isResizing: true}); + } + }; + + /** + * Resizes the drawer + */ + handleMousemove = e => { + if (!this.state.isResizing) { + return; + } + + this.setState({ newWidth: { width: e.clientX} }); + this.setState({ newHeight: { height: (648 - e.clientY)} }); + }; + + handleMouseup = () => { + this.setState({ isResizing: false }); + }; nameHelper = (name, parameters) => { return ( @@ -128,20 +248,20 @@ class Reference extends Component { */ TableEx = (category) => { return ( - - +
+ - Name - Description - Example + Name + Description + Example {this.tableData[category].map((row, index) => ( - {this.nameHelper(row.name, row.parameters)} - {row.description} - {this.exampleHelper(row.example)} + {this.nameHelper(row.name, row.parameters)} + {row.description} + {this.exampleHelper(row.example)} ))} @@ -153,136 +273,223 @@ class Reference extends Component { * Reneter Button that will open Drawer of reference with different categories */ render() { - const style = { - margin: 2, - color: "#fff", - }; - const isDisabled = this.props.layoutType === layoutTypes.REFERENCE; + const isDisabled = this.props.layoutType === layoutTypes.REFERENCE; + + let tempHeight = this.state.newHeight; + let tempWidth = this.state.newWidth; + return ( -
+
{!isDisabled ? - - - help - - - - -
-

MYR API - Reference

- + +
-
- - category} - label={ - -
GEOMETRY
-
- } - value='a' /> - bubble_chart} - label={ - -
TRANSFORMATIONS
-
- } - value='b' /> - zoom_out_map} //swap_horiz control_camera category - label={ - -
ANIMATIONS
-
- } - value='c' /> - widgets} - label={ - -
GROUPS
-
- } - value='d' /> - highlight} - label={ - -
LIGHT
-
- } - value='e' /> -
-
- {
-

Key: array - bool - number - string - group - data

-
} - {this.state.value === "a" && -
- {this.TableEx("geometry")} -
} - {this.state.value === "b" && -
- {this.TableEx("transformations")} -
} - {this.state.value === "c" && -
- {this.TableEx("animations")} -
} - {this.state.value === "d" && -
- {this.TableEx("groups")} -
} - {this.state.value === "e" && -
- {this.TableEx("lights")} + help + + + + + + + + +
+ +
+

MYR API - Reference

+ { + this.handleRefClose(); + this.setState({ newHeight: 80}); + this.setState({ newWidth: 81}); + this.setState({ value: "a" }); + }}> + close + + + menu_book + +
+
+ + category} + label={ + +
GEOMETRY
+
+ } + value='a' /> + bubble_chart} + label={ + +
TRANSFORMATIONS
+
+ } + value='b' /> + zoom_out_map} //swap_horiz control_camera category + label={ + +
ANIMATIONS
+
+ } + value='c' /> + widgets} + label={ + +
GROUPS
+
+ } + value='d' /> + highlight} + label={ + +
LIGHT
+
+ } + value='e' /> +
+
+ {
+

Key: array + bool + number + string + group + data

} -
+ {this.state.value === "a" && +
+ {this.TableEx("geometry")} +
} + {this.state.value === "b" && +
+ {this.TableEx("transformations")} +
} + {this.state.value === "c" && +
+ {this.TableEx("animations")} +
} + {this.state.value === "d" && +
+ {this.TableEx("groups")} +
} + {this.state.value === "e" && +
+ {this.TableEx("lights")} +
} + +
+ +
+ +
+

MYR API - Asset Reference

+ { + this.handleAssetClose(); + this.setState({ newHeight: 80}); + this.setState({ newWidth: 81}); + this.setState({ assetValue: "a" }); + }}> + close + + + menu_book + +
+
+ + model} + label={ + +
MODEL
+
+ } + value='a' /> + texture} + label={ + +
TEXTURE
+
+ } + value='b' /> +
+ {this.state.assetValue === "a" && +
+ +
} + {this.state.assetValue === "b" && +
+ +
} +
+
+
+ : null}
); diff --git a/src/components/structural/header/Header.js b/src/components/structural/header/Header.js index 00dee71a..0e54ac65 100644 --- a/src/components/structural/header/Header.js +++ b/src/components/structural/header/Header.js @@ -1,7 +1,5 @@ import React, { Component, Fragment } from "react"; -import Reference from "../../reference/Reference.js"; import Collection from "../../collection/Collection.js"; -import SceneConfigMenu from "./SceneConfigMenu.js"; import Sidebar from "./Sidebar.js"; import MyrTour from "./MyrTour.js"; import ProjectView from "./ProjectView.js"; @@ -24,7 +22,7 @@ import { Snackbar, Popover, Avatar, - createMuiTheme, + createTheme, MuiThemeProvider } from "@material-ui/core"; import { save } from "../../../actions/projectActions.js"; @@ -266,7 +264,7 @@ class Header extends Component { open={this.state.logMenuOpen} onClick={() => this.setState({ logMenuOpen: !this.state.logMenuOpen })} label="logout" - style={{ marginTop: 5 }} /> + style={{ marginTop: 5, marginLeft: 15 }} /> Log In @@ -645,13 +643,13 @@ class Header extends Component { }, clear: { margin: 5, - marginRight: 10, + marginRight: 8, padding: 0, background: "linear-gradient(45deg, #FE3B3B 30%, #FF3B3B 90%)", }, clear_disabled: { margin: 5, - marginRight: 10, + marginRight: 8, padding: 0, background: "#222", border: "2px solid", @@ -666,7 +664,7 @@ class Header extends Component { color: "#777", }, }; - const theme = createMuiTheme({ + const theme = createTheme({ palette: { primary: { main: "#3f51b5", @@ -729,7 +727,7 @@ class Header extends Component { Show Welcome Screen -

{ window.location.assign(window.origin); }} > MYR @@ -778,6 +776,7 @@ class Header extends Component { id="new-btn" onClick={() => { window.location.assign(window.origin); }} style={style.default} + size= "small" className="header-btn d-none d-md-block" > add_circle_outline @@ -786,6 +785,7 @@ class Header extends Component { @@ -800,33 +800,16 @@ class Header extends Component { handleProjectToggle={this.handleProjectToggle} tab={this.state.projectTab} user={this.props.user} /> - -

-
- - + + + -
} - +
{!isDisabled ?
- - - settings - - + + + + + div { justify-content: left !important; } +.keyboard-button { + color: #673ab7; + background-color: #673ab7; + margin-left:2px; +} + +.reference-button { + display: inline-block; + margin-left:2px; +} + +.asset-button { + display: inline-block; + margin-left:2px; +} + .MuiTabs-root-76 { overflow: unset !important; } @@ -271,6 +289,41 @@ div.side-drawer>div { #ace-editor { border-bottom: 1px solid; border-top: 1px solid; + z-index: 5; + position: "absolute" +} + +.leftConsole { + position: "absolute"; + background-color: red; +} + +.console { + resize: both; + overflow: auto; +} + +.console-footer { + background-color: black; + z-index: 2000; +} + +#dragger { + width: 5px; + cursor: "ew-resize"; + padding: 4px 0 0; + border-top: "1px solid #ddd"; + position: absolute; + top: 0; + left: 0; + bottom: 0; + z-index: 100; + background-color: #f4f7f9; +} + +.config { + display: inline-block; + margin-left:2px; } .guided div#ace-editor { @@ -299,6 +352,20 @@ div#reference-tabs { min-height: unset !important; } +div#assetReference-tabs { + overflow: unset !important; + min-height: unset !important; +} + +.reference-drawer-header { + padding-bottom: 0px; + margin-bottom: 0px; +} + +#refTableHead { + margin-bottom: 20px; +} + .sidebar-btn .material-icons { margin: 0 .7em; } @@ -314,8 +381,22 @@ span.user-name { } div#reference-drawer>div { - width: 67%; + width: 81vh; + height: 80vh; + resize: both; + z-index: 10; + position: "absolute" +} + +div#textureReference-drawer>div { + width: 81vh; + height: 80vh; + resize: both; + z-index: 10; + position: "absolute" } + + .refExample { margin-right: 10px; } @@ -410,9 +491,13 @@ div#projectDrawer>div { } footer { - background-color: #222; + background-color: black; color: #fff; - margin-top: 20px; + margin-top: 0px; + padding-top: 1px; + padding-bottom: 1px; + z-index: 2000; + position: "absolute" } footer a { @@ -432,6 +517,6 @@ footer a:hover { z-index: 200; display: flex; bottom: 4px; - right: 18px; + right: 10px; } } diff --git a/src/myr/reference.js b/src/myr/reference.js index 2a5847d6..62de4f2f 100644 --- a/src/myr/reference.js +++ b/src/myr/reference.js @@ -155,6 +155,7 @@ export const dataText = (text) => { const geometry = [ { name: "box", + byname: "cube", parameters: [], description: The box function makes a 3D quadrilateral using the current cursor attributes. This function returns an {stringText("elementID")}., example: "box" diff --git a/src/myr/textureReference.js b/src/myr/textureReference.js index eeefe3b5..ccad1fbe 100644 --- a/src/myr/textureReference.js +++ b/src/myr/textureReference.js @@ -1,3 +1,150 @@ +import React from "react"; +import { + Tooltip, + Typography, + withStyles +} from "@material-ui/core"; + +/** + * Custom styles that inject to the cue cards + */ +const HtmlTooltip = withStyles(theme => ({ + tooltip: { + maxWidth: 300, + fontSize: theme.typography.pxToRem(14), + "& b": { + fontWeight: "inherit", + }, + }, +}))(Tooltip); + +/** + * Return a react elements contain a cue-card for String data type + */ +export const stringText = (text) => { + return ( + + String +

+ In computer science a string is any finite sequence of characters + (i.e., letters, numerals, symbols and punctuation marks).
+

+ {text === "elementID" && +

An elementID is a special type of string returned by geometries and groups.

} + + } + > + + {text} + +
); +}; + +/** + * Return a react elements contain a cue-card for Number data type + */ +export const numberText = (text) => { + return ( + + Number +

+ A number is any real number, or an expression that evaluates to a real number + (e.g., -2, 3.14, 1/3). +

+ + } + > + + {text} + +
); +}; + +/** + * Return a react elements contain a cue-card for Boolean data type + */ +export const boolText = (text) => { + return ( + + Bool +

+ In computer science, the bool data type has one of two possible Boolean values: true or false. +

+ + } + > + + {text} + +
); +}; + +/** + * Return a react elements contain a cue-card for Array data type + */ +export const arrayText = (text) => { + return ( + + Array +

+ In computer science, an array is a data structure that consists of a number of indexable elements. +

+

+ This code sets the color of the cursor to blue: +

+

+ let colors = ["blue", "green", "red"];
+ setColor(colors[0]);
+ box();
+

+ + } + > + + {text} + +
); +}; + +/** + * Return a react elements contain a cue-card for dynamic data type + */ +export const dataText = (text) => { + return ( + + Data +

+ Data can be of any valid JS datatype. This includes a strings, numbers, booleans, and objects among other datatypes.
+

+ + } + > + + {text} + +
); +}; + /** * List of texture reference */ @@ -97,7 +244,6 @@ const texture = [ image: "wood", creator: "Webtreats", }, - ]; -export default texture; +export default texture; \ No newline at end of file