diff options
Diffstat (limited to 'src/components')
21 files changed, 601 insertions, 5 deletions
diff --git a/src/components/modals/Modal.js b/src/components/modals/Modal.js new file mode 100644 index 00000000..e2d19fcb --- /dev/null +++ b/src/components/modals/Modal.js @@ -0,0 +1,82 @@ +import PropTypes from "prop-types"; +import React from "react"; + +class Modal extends React.Component { + static propTypes = { + title: PropTypes.string.isRequired, + show: PropTypes.bool.isRequired, + onSubmit: PropTypes.func.isRequired, + onCancel: PropTypes.func.isRequired, + }; + static idCounter = 0; + + constructor() { + super(); + this.id = "modal-" + Modal.idCounter; + } + + componentDidMount() { + this.openOrCloseModal(); + } + + componentDidUpdate() { + this.openOrCloseModal(); + } + + onSubmit() { + this.props.onSubmit(); + this.closeModal(); + } + + onCancel() { + this.props.onCancel(); + this.closeModal(); + } + + openModal() { + window["$"]("#" + this.id).modal("show"); + } + + closeModal() { + window["$"]("#" + this.id).modal("hide"); + } + + openOrCloseModal() { + if (this.props.show) { + this.openModal(); + } else { + this.closeModal(); + } + } + + render() { + return ( + <div className="modal" id={this.id} role="dialog"> + <div className="modal-dialog" role="document"> + <div className="modal-content"> + <div className="modal-header"> + <h5 className="modal-title">{this.props.title}</h5> + <button type="button" className="close" onClick={this.onCancel.bind(this)} + aria-label="Close"> + <span aria-hidden="true">×</span> + </button> + </div> + <div className="modal-body"> + {this.props.children} + </div> + <div className="modal-footer"> + <button type="button" className="btn btn-secondary" onClick={this.onCancel.bind(this)}> + Close + </button> + <button type="button" className="btn btn-primary" onClick={this.onSubmit.bind(this)}> + Save + </button> + </div> + </div> + </div> + </div> + ); + } +} + +export default Modal; diff --git a/src/components/modals/TextInputModal.js b/src/components/modals/TextInputModal.js new file mode 100644 index 00000000..4acf25b3 --- /dev/null +++ b/src/components/modals/TextInputModal.js @@ -0,0 +1,41 @@ +import PropTypes from "prop-types"; +import React from "react"; +import Modal from "./Modal"; + +class TextInputModal extends React.Component { + static propTypes = { + title: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + show: PropTypes.bool.isRequired, + callback: PropTypes.func.isRequired, + initialValue: PropTypes.string, + }; + + onSubmit() { + this.props.callback(this.refs.textInput.value); + this.refs.textInput.value = ""; + } + + onCancel() { + this.props.callback(undefined); + this.refs.textInput.value = ""; + } + + render() { + return ( + <Modal title={this.props.title} + show={this.props.show} + onSubmit={this.onSubmit.bind(this)} + onCancel={this.onCancel.bind(this)}> + <form> + <div className="form-group"> + <label className="form-control-label">{this.props.label}:</label> + <input type="text" className="form-control" ref="textInput" value={this.props.initialValue}/> + </div> + </form> + </Modal> + ); + } +} + +export default TextInputModal; diff --git a/src/components/navigation/Navbar.js b/src/components/navigation/Navbar.js index a5f0510f..bd6fd750 100644 --- a/src/components/navigation/Navbar.js +++ b/src/components/navigation/Navbar.js @@ -1,8 +1,8 @@ import React, {Component} from 'react'; +import FontAwesome from "react-fontawesome"; +import Mailto from "react-mailto"; import {Link} from "react-router-dom"; import "./Navbar.css"; -import Mailto from "react-mailto"; -import FontAwesome from "react-fontawesome"; class Navbar extends Component { render() { @@ -19,7 +19,7 @@ class Navbar extends Component { </div> <div className="user-controls navbar-button-group"> <Mailto className="support" title="Support" email="opendc.tudelft@gmail.com" - headers={{subject: "OpenDC%20Support"}}> + headers={{subject: "OpenDC Support"}}> <FontAwesome name="question-circle" size="lg"/> </Mailto> <Link className="username" title="My Profile" to="/profile">Profile</Link> diff --git a/src/components/navigation/Navbar.sass b/src/components/navigation/Navbar.sass index d40eecfb..a592eab0 100644 --- a/src/components/navigation/Navbar.sass +++ b/src/components/navigation/Navbar.sass @@ -1,5 +1,5 @@ -@import ../../style-globals/mixins.sass -@import ../../style-globals/variables.sass +@import ../../style-globals/_mixins.sass +@import ../../style-globals/_variables.sass .opendc-navbar position: relative diff --git a/src/components/not-found/BlinkingCursor.js b/src/components/not-found/BlinkingCursor.js new file mode 100644 index 00000000..f6c9768c --- /dev/null +++ b/src/components/not-found/BlinkingCursor.js @@ -0,0 +1,8 @@ +import React from "react"; +import "./BlinkingCursor.css"; + +const BlinkingCursor = () => ( + <span className="blinking-cursor">_</span> +); + +export default BlinkingCursor; diff --git a/src/components/not-found/BlinkingCursor.sass b/src/components/not-found/BlinkingCursor.sass new file mode 100644 index 00000000..6be1476d --- /dev/null +++ b/src/components/not-found/BlinkingCursor.sass @@ -0,0 +1,35 @@ +.blinking-cursor + -webkit-animation: 1s blink step-end infinite + -moz-animation: 1s blink step-end infinite + -o-animation: 1s blink step-end infinite + animation: 1s blink step-end infinite + +@keyframes blink + from, to + color: #eeeeee + 50% + color: #333333 + +@-moz-keyframes blink + from, to + color: #eeeeee + 50% + color: #333333 + +@-webkit-keyframes blink + from, to + color: #eeeeee + 50% + color: #333333 + +@-ms-keyframes blink + from, to + color: #eeeeee + 50% + color: #333333 + +@-o-keyframes blink + from, to + color: #eeeeee + 50% + color: #333333 diff --git a/src/components/not-found/CodeBlock.js b/src/components/not-found/CodeBlock.js new file mode 100644 index 00000000..24d100cc --- /dev/null +++ b/src/components/not-found/CodeBlock.js @@ -0,0 +1,30 @@ +import React from "react"; +import "./CodeBlock.css"; + +const CodeBlock = () => { + const textBlock = + " oo oooo oo <br/>" + + " oo oo oo oo <br/>" + + " oo oo oo oo <br/>" + + " oooooo oo oo oooooo <br/>" + + " oo oo oo oo <br/>" + + " oo oooo oo <br/>"; + const charList = textBlock.split(''); + + // Binary representation of the string "OpenDC!" ;) + const binaryString = "01001111011100000110010101101110010001000100001100100001"; + + let binaryIndex = 0; + for (let i = 0; i < charList.length; i++) { + if (charList[i] === "o") { + charList[i] = binaryString[binaryIndex]; + binaryIndex++; + } + } + + return ( + <div className="code-block" dangerouslySetInnerHTML={{__html: textBlock}}/> + ); +}; + +export default CodeBlock; diff --git a/src/components/not-found/CodeBlock.sass b/src/components/not-found/CodeBlock.sass new file mode 100644 index 00000000..51a3d3d0 --- /dev/null +++ b/src/components/not-found/CodeBlock.sass @@ -0,0 +1,3 @@ +.code-block + white-space: pre-wrap + margin-top: 60px diff --git a/src/components/not-found/TerminalWindow.js b/src/components/not-found/TerminalWindow.js new file mode 100644 index 00000000..52d3c062 --- /dev/null +++ b/src/components/not-found/TerminalWindow.js @@ -0,0 +1,26 @@ +import React from "react"; +import {Link} from "react-router-dom"; +import BlinkingCursor from "./BlinkingCursor"; +import CodeBlock from "./CodeBlock"; +import "./TerminalWindow.css"; + +const TerminalWindow = () => ( + <div className="terminal-window"> + <div className="terminal-header">Terminal -- bash</div> + <div className="terminal-body"> + <div className="segfault">$ status<br/> + opendc[4264]: segfault at 0000051497be459d1 err 12 in libopendc.9.0.4<br/> + opendc[4269]: segfault at 000004234855fc2db err 3 in libopendc.9.0.4<br/> + opendc[4270]: STDERR Page does not exist<br/> + </div> + <CodeBlock/> + <div className="sub-title">Got lost?<BlinkingCursor/></div> + <Link to="/" className="home-btn"> + <span className="fa fa-home"/> GET ME BACK TO OPENDC + </Link> + </div> + </div> +); + + +export default TerminalWindow; diff --git a/src/components/not-found/TerminalWindow.sass b/src/components/not-found/TerminalWindow.sass new file mode 100644 index 00000000..4f51a77f --- /dev/null +++ b/src/components/not-found/TerminalWindow.sass @@ -0,0 +1,70 @@ +.terminal-window + width: 600px + height: 400px + display: block + + position: absolute + top: 0 + bottom: 0 + left: 0 + right: 0 + + margin: auto + + -webkit-user-select: none + -moz-user-select: none + -ms-user-select: none + user-select: none + cursor: default + + overflow: hidden + + box-shadow: 5px 5px 20px #444444 + +.terminal-header + font-family: monospace + background: #cccccc + color: #444444 + height: 30px + line-height: 30px + padding-left: 10px + + border-top-left-radius: 7px + border-top-right-radius: 7px + +.terminal-body + font-family: monospace + text-align: center + background-color: #333333 + color: #eeeeee + padding: 10px + + height: 100% + +.segfault + text-align: left + +.sub-title + margin-top: 20px + +.home-btn + margin-top: 10px + padding: 5px + display: inline-block + border: 1px solid #eeeeee + color: #eeeeee + text-decoration: none + cursor: pointer + + -webkit-transition: all 200ms + -moz-transition: all 200ms + -o-transition: all 200ms + transition: all 200ms + +.home-btn:hover + background: #eeeeee + color: #333333 + +.home-btn:active + background: #333333 + color: #eeeeee diff --git a/src/components/projects/FilterButton.js b/src/components/projects/FilterButton.js new file mode 100644 index 00000000..8d6b7146 --- /dev/null +++ b/src/components/projects/FilterButton.js @@ -0,0 +1,23 @@ +import classNames from 'classnames'; +import PropTypes from 'prop-types'; +import React from 'react'; +import "./FilterButton.css"; + +const FilterButton = ({active, children, onClick}) => ( + <div className={classNames("project-filter-button", {"active": active})} + onClick={() => { + if (!active) { + onClick(); + } + }}> + {children} + </div> +); + +FilterButton.propTypes = { + active: PropTypes.bool.isRequired, + children: PropTypes.node.isRequired, + onClick: PropTypes.func.isRequired +}; + +export default FilterButton; diff --git a/src/components/projects/FilterButton.sass b/src/components/projects/FilterButton.sass new file mode 100644 index 00000000..0cad68e3 --- /dev/null +++ b/src/components/projects/FilterButton.sass @@ -0,0 +1,23 @@ +@import ../../style-globals/_mixins.sass +@import ../../style-globals/_variables.sass + +.project-filter-button + display: inline-block + width: 33.3% + //margin-right: -4px + padding: 10px $global-padding + + font-size: 12pt + border-right: 1px solid #06326b + + +clickable + +transition(background, $transition-length) + +.project-filter-button:last-of-type + border: 0 + +.project-filter-button:hover + background: #0c60bf + +.project-filter-button:active, .project-filter-button.active + background: #073d7d diff --git a/src/components/projects/FilterPanel.js b/src/components/projects/FilterPanel.js new file mode 100644 index 00000000..050bf0aa --- /dev/null +++ b/src/components/projects/FilterPanel.js @@ -0,0 +1,15 @@ +import React from 'react'; +import FilterLink from "../../containers/projects/FilterLink"; +import "./FilterPanel.css"; + +const ProjectFilterPanel = () => ( + <div className="filter-menu"> + <div className="project-filters"> + <FilterLink filter="SHOW_ALL">All Projects</FilterLink> + <FilterLink filter="SHOW_OWN">My Projects</FilterLink> + <FilterLink filter="SHOW_SHARED">Projects shared with me</FilterLink> + </div> + </div> +); + +export default ProjectFilterPanel; diff --git a/src/components/projects/FilterPanel.sass b/src/components/projects/FilterPanel.sass new file mode 100644 index 00000000..a70c7a90 --- /dev/null +++ b/src/components/projects/FilterPanel.sass @@ -0,0 +1,21 @@ +@import ../../style-globals/_mixins.sass +@import ../../style-globals/_variables.sass + +.filter-menu + display: block + + background: #0761b1 + border: 1px solid #06326b + color: #eee + + text-align: center + + +border-radius($standard-border-radius) + overflow: hidden + + margin-bottom: 20px + + .project-filters + display: block + overflow: hidden + margin: 0 -1px diff --git a/src/components/projects/NewProjectButton.js b/src/components/projects/NewProjectButton.js new file mode 100644 index 00000000..9eaf6df4 --- /dev/null +++ b/src/components/projects/NewProjectButton.js @@ -0,0 +1,16 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import './NewProjectButton.css'; + +const NewProjectButton = ({onClick}) => ( + <div className="new-project-btn" onClick={onClick}> + <span className="fa fa-plus"/> + New Project + </div> +); + +NewProjectButton.propTypes = { + onClick: PropTypes.func.isRequired, +}; + +export default NewProjectButton; diff --git a/src/components/projects/NewProjectButton.sass b/src/components/projects/NewProjectButton.sass new file mode 100644 index 00000000..89435902 --- /dev/null +++ b/src/components/projects/NewProjectButton.sass @@ -0,0 +1,31 @@ +@import ../../style-globals/_mixins.sass +@import ../../style-globals/_variables.sass + +.new-project-btn + $button-height: 35px + + display: inline-block + position: absolute + bottom: $navbar-height + 40px + right: 40px + padding: 0 10px + height: $button-height + line-height: $button-height + font-size: 14pt + + background: #679436 + color: #eee + border: 1px solid #507830 + + +border-radius($standard-border-radius) + +clickable + +transition(all, $transition-length) + + span + margin-right: 10px + +.new-project-btn:hover + background: #73ac45 + +.new-project-btn:active + background: #5c8835 diff --git a/src/components/projects/NoProjectsAlert.js b/src/components/projects/NoProjectsAlert.js new file mode 100644 index 00000000..957435c7 --- /dev/null +++ b/src/components/projects/NoProjectsAlert.js @@ -0,0 +1,11 @@ +import React from 'react'; +import "./NoProjectsAlert.css"; + +const NoProjectsAlert = () => ( + <div className="no-projects-alert alert alert-info"> + <span className="info-icon fa fa-2x fa-question-circle"/> + <strong>No projects here yet...</strong> Add some with the 'New Project' button! + </div> +); + +export default NoProjectsAlert; diff --git a/src/components/projects/NoProjectsAlert.sass b/src/components/projects/NoProjectsAlert.sass new file mode 100644 index 00000000..a526f9ad --- /dev/null +++ b/src/components/projects/NoProjectsAlert.sass @@ -0,0 +1,10 @@ +.no-projects-alert + position: relative + padding-left: 50px + + .info-icon + position: absolute + top: 11px + left: 15px + bottom: 10px + font-size: 20pt diff --git a/src/components/projects/ProjectAuth.js b/src/components/projects/ProjectAuth.js new file mode 100644 index 00000000..7e3abae1 --- /dev/null +++ b/src/components/projects/ProjectAuth.js @@ -0,0 +1,22 @@ +import classNames from 'classnames'; +import React from 'react'; +import Shapes from "../../shapes/index"; +import {AUTH_DESCRIPTION_MAP, AUTH_ICON_MAP} from "../../util/authorizations"; +import {parseAndFormatDateTime} from "../../util/date-time"; + +const ProjectAuth = ({projectAuth}) => ( + <div className="project-row"> + <div>{projectAuth.simulation.name}</div> + <div>{parseAndFormatDateTime(projectAuth.simulation.datetimeLastEdited)}</div> + <div> + <span className={classNames("fa", "fa-" + AUTH_ICON_MAP[projectAuth.authorizationLevel])}/> + {AUTH_DESCRIPTION_MAP[projectAuth.authorizationLevel]} + </div> + </div> +); + +ProjectAuth.propTypes = { + projectAuth: Shapes.Authorization.isRequired, +}; + +export default ProjectAuth; diff --git a/src/components/projects/ProjectAuthList.js b/src/components/projects/ProjectAuthList.js new file mode 100644 index 00000000..093b3279 --- /dev/null +++ b/src/components/projects/ProjectAuthList.js @@ -0,0 +1,31 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import Shapes from "../../shapes/index"; +import NoProjectsAlert from "./NoProjectsAlert"; +import ProjectAuth from "./ProjectAuth"; +import "./ProjectAuthList.css"; + +const ProjectAuthList = ({authorizations, onProjectClick}) => ( + <div className="project-list"> + <div className="list-head"> + <div>Project name</div> + <div>Last edited</div> + <div>Access rights</div> + </div> + <div className="list-body"> + {authorizations.length === 0 ? + <NoProjectsAlert/> : + authorizations.map(authorization => ( + <ProjectAuth projectAuth={authorization} key={authorization.simulation.id}/> + )) + } + </div> + </div> +); + +ProjectAuthList.propTypes = { + authorizations: PropTypes.arrayOf(Shapes.Authorization).isRequired, + onProjectClick: PropTypes.func.isRequired, +}; + +export default ProjectAuthList; diff --git a/src/components/projects/ProjectAuthList.sass b/src/components/projects/ProjectAuthList.sass new file mode 100644 index 00000000..86e1123c --- /dev/null +++ b/src/components/projects/ProjectAuthList.sass @@ -0,0 +1,98 @@ +@import ../../style-globals/_mixins.sass +@import ../../style-globals/_variables.sass + +.project-list + display: block + font-size: 12pt + border: 0 + + .list-head, .list-body .project-row + display: block + position: relative + + .list-head div, .list-body .project-row div + padding: 0 10px + display: inline-block + + .list-head + font-weight: bold + + // Address default margin between inline-blocks + div + margin-right: -4px + +.project-row + background: #f8f8f8 + border: 1px solid #b6b6b6 + height: 40px + line-height: 40px + clear: both + + +transition(background, $transition-length) + +clickable + +.project-row:hover + background: #fff + +.project-row:active + background: #cccccc + +.project-row:not(:first-of-type) + margin-top: -1px + +// Sizing of table columns +.project-row, .project-list .list-head + div:first-of-type + width: 50% + + div:nth-of-type(2) + width: 30% + + div:last-of-type + width: 20% + + span + margin-right: 10px + +.project-row.active + border-bottom: 0 + background: #3442b1 + color: #eee + +.project-view + padding: 10px + overflow: hidden + border: 1px solid #b6b6b6 + border-top: 0 + + background: #3442b1 + color: #eee + + .participants + display: inline-block + float: left + + .access-buttons + display: inline-block + float: right + + .inline-btn + margin-left: 10px + + .open + background: #e38829 + + .open:hover + background: #ff992e + + .open:active + background: #ba6f21 + + .edit + background: #2c3897 + + .edit:hover + background: #3a4ac8 + + .edit:active + background: #242d7a |
