diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..7a9dfa04 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "pwa-chrome", + "request": "launch", + "name": "Launch Chrome against localhost", + "url": "http://localhost:8080", + "webRoot": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 8fc648a8..dd9b994a 100644 --- a/README.md +++ b/README.md @@ -81,8 +81,8 @@ The BSD 2-Clause License * * * -![Alfred P. Sloan](./app/images/aps-sm.png) +![Alfred P. Sloan](./app/images/apsf-logo.png) -![University of California Riverside](./app/images/ucr-sm.png) +![University of Georgia at Athens](./app/images/uga-logo@2x.png) ![Pitch Interactive](./app/images/pitch.png) diff --git a/app/app.global.css b/app/app.global.css index a37c731c..88745829 100644 --- a/app/app.global.css +++ b/app/app.global.css @@ -10,6 +10,12 @@ -webkit-box-sizing: border-box; } +/* This gets rid of the blue border around different elements, might need to reimplement in future to +to accomodate keyboard only users or find another way to visually replace it */ +*:focus { + outline: 0 !important; +} + ::-webkit-scrollbar { -webkit-appearance: none; cursor: pointer; @@ -21,9 +27,10 @@ border: 2px solid white; } -::-webkit-scrollbar-track { - background-color: inherit; -} +/* This can later be used to add the line to show scroll bar range */ +/* ::-webkit-scrollbar-track { + +} */ ::-webkit-scrollbar:vertical { width: 10px; @@ -41,30 +48,30 @@ html { body { position: relative; height: 100vh; - font-weight: 200; - background-color: #333; + font-weight: 400; + background-color: #575A5C; color: #eee; - font-family: 'IBM Plex Sans', Verdana, Sans-serif; + font-family: 'Open Sans'; margin: 0; overflow: hidden; } h1 { - font-weight: 200; + font-weight: 400; font-size: 1.5rem; margin: 0; padding: 0; } h2 { - font-weight: 200; + font-weight: 400; font-size: 1.2rem; margin: 0; padding: 0; } p { - font-weight: 200; + font-weight: 400; } li { @@ -73,6 +80,7 @@ li { a { text-decoration: none; + text-underline-offset: auto; } a:hover { @@ -86,4 +94,25 @@ img { button { cursor: pointer; + background: none; + border: none; + outline: none !important; +} + +button:hover { + fill:#F09E6A; +} + +button:focus { + outline: none; +} + +.__react_component_tooltip { + padding: 2px 6px !important; + font-weight: 600; + z-index: 9999999 !important; +} +.__react_component_tooltip.show { + opacity: 1 !important; + z-index: 9999999 !important; } diff --git a/app/components/About.js b/app/components/About.js index 15eb28c5..e89e0dfe 100644 --- a/app/components/About.js +++ b/app/components/About.js @@ -2,13 +2,17 @@ import React, { Component } from 'react'; import { Link, Redirect } from 'react-router-dom'; import close from 'images/close.svg'; -import aps from 'images/aps.png'; -import ucr from 'images/ucr.png'; +import closeDefault from 'images/closeDefault.svg'; +import aps from 'images/apsf-logo.png'; +import uga from 'images/uga-logo@2x.png'; import pitch from 'images/pitch.png'; +import arrow from 'images/arrow.svg'; +import arrowHover from 'images/arrowHover.svg'; import { pageView } from '../analytics'; import SideBar from './SideBar'; import styles from './Home.css'; +import gstyles from './general.css'; export default class About extends Component { constructor(props) { @@ -16,19 +20,77 @@ export default class About extends Component { pageView('/about'); - this.state = { redirect: null }; + this.state = { + redirect: null, + hoveringClose: false, + link1: arrow, + link2: arrow, + link3: arrow, + link4: arrow, + link5: arrow, + }; + } + + /*This function deals with when the mouse hovers over the edit icon on top right of + the home screen and changes img src accordingly to correct svg file */ + handleMouseOver (title) { + switch(title) { + case 'New to Phinch?': + this.setState({ link1: arrowHover }); + break; + case 'View our Flagship Datasets': + this.setState({ link2: arrowHover }); + break; + case 'Join the Community': + this.setState({ link3: arrowHover }); + break; + case 'About Phinch': + this.setState({ link4: arrowHover }); + break; + case 'Find a software issue?': + this.setState({ link5: arrowHover }); + break; + } + } + + /*This function deals with the mouse leaving an icon (no longer hovering) and + changed img src to correct svg file */ + handleMouseLeave (title) { + switch(title) { + case 'New to Phinch?': + this.setState({ link1: arrow }); + break; + case 'View our Flagship Datasets': + this.setState({ link2: arrow }); + break; + case 'Join the Community': + this.setState({ link3: arrow }); + break; + case 'About Phinch': + this.setState({ link4: arrow }); + break; + case 'Find a software issue?': + this.setState({ link5: arrow }); + break; + } } render() { if (this.state.redirect !== null && this.state.redirect !== '/about') { - return ; + return (); } return (
- -
-
+ +
+ +
this.setState({hoveringClose: true})} + onMouseOut={() => this.setState({hoveringClose: false})} + > + Home +

About Phinch

PHINCH is an open-source framework for visualizing biological data, funded by a grant from the Alfred P. Sloan foundation. This project represents an interdisciplinary collaboration between Pitch Interactive, a data visualization studio in Oakland, CA, and biological researchers at UC Davis. {/* eslint-disable-line max-len */} @@ -42,13 +104,11 @@ export default class About extends Component {

Scientific visualization represents an innovative method towards tackling the current bottleneck in bioinformatics; in addition to giving researchers a unique approach for exploring large datasets, it stands to empower biologists with the ability to conduct powerful analyses without requiring a deep level of computational knowledge. {/* eslint-disable-line max-len */}

-
-
- Home - Alfred P. Sloan Logo - University of California Riverside Logo - Pitch Interactive Logo -
+
+ Alfred P. Sloan Logo + University of Georgia Logo + Pitch Interactive Logo +
diff --git a/app/components/CheckBoxes.css b/app/components/CheckBoxes.css index 8ff61352..edf548b2 100644 --- a/app/components/CheckBoxes.css +++ b/app/components/CheckBoxes.css @@ -1,6 +1,7 @@ .group { color: #000; - font-weight: 300; + font-size: 14px; + font-weight: 500; } .row { @@ -21,7 +22,7 @@ .name { font-size: 14px; - font-weight: 400; + font-weight: 600; vertical-align: top; margin-right: 1rem; overflow-wrap: break-word; diff --git a/app/components/CheckBoxes.js b/app/components/CheckBoxes.js index e63d8b1c..9e3c114e 100644 --- a/app/components/CheckBoxes.js +++ b/app/components/CheckBoxes.js @@ -4,7 +4,7 @@ import styles from './CheckBoxes.css'; import gstyle from './general.css'; export default class CheckBoxes extends Component { - render() { + render() { const buttons = this.props.filter.expanded ? (
+ ); + } + + /*This function deals with when the mouse hovers over the browse icon on top right of + and changes img src accordingly to correct svg file */ + handleMouseOver (button) { + switch(button) { + case "help": + if(this.state.helpButton === needHelp) { + this.setState({helpButton: needHelpHover}); + } + break; + case "viewViz": + const element = document.getElementById("stackedgraph"); + element.style.backgroundColor = "#F09E6A"; + } + } + + /*This function deals with the mouse leaving an icon (no longer hovering) and + changed img src to correct svg file */ + handleMouseLeave (button) { + switch(button) { + case "help": + if(this.state.helpButton === needHelpHover) { + this.setState({helpButton: needHelp}); + } + break; + case "viewViz": + const element = document.getElementById("stackedgraph"); + element.style.backgroundColor = "#001226"; + } + } + render() { const redirect = this.state.redirect === null ? '' : ; + const helpButtons = this.state.counter > 0 ? this.makeHelpButtons() : ''; if (redirect) { return redirect; @@ -594,8 +799,8 @@ export default class Filter extends Component { className={gstyle.button} style={{ position: 'absolute', - top: '96px', - left: '16px', + top: 'calc(100vh - 40px)', + right: '16px', width: '68px', textAlign: 'center', zIndex: 10, @@ -630,116 +835,220 @@ export default class Filter extends Component { {redirect} {result} -
-
+
+
Phinch +
-
Visualization Type
-
- Stacked bargraph -
Stacked bargraph
-
-
(e.key === ' ' ? viewVisualization() : null)} - > - View Visualization -
-
-
- {this.renderHeader()} -
+ + + The uploaded data file can be explored through a number{' '} + of distinct visualization types. +

+ Click on one of the listed options to select that visualization type,{' '} + and then click “View Visualization” to see the graphs made by the option{' '} + you choose. +
} + style={{ backgroundColor: 'rgba(255, 255, 255, 0.2)', boxShadow: 'inset rgba(255, 255, 255, 0.5) 0px 0px 10px'}} + > +
+
Visualization Type
+
+ Stacked bargraph +
Stacked Bargraph
+
+
+ Bubble Graph +
Bubble Graph
+
+
+ Sankey bargraph +
Sankey graph
+
+
(e.key === ' ' ? viewVisualization() : null)} + onMouseEnter={() => this.handleMouseOver("viewViz")} + onMouseLeave={() => this.handleMouseLeave("viewViz")} + > + View Visualization +
+
+ + +
+
+ {this.renderHeader()} +
+
-
+
-
+ - {this.displayFilters()} +
(e.key === ' ' ? this.resetFilters() : null)} + className={`${styles.section} ${styles.left}`} + style={{ + display: 'inline-block', + height: (this.state.height - (this.state.counter == 1 ? 240 : 155)), + overflowY: 'overlay', + }} > - Reset Filters + + On the left panel, click the arrow button to view filtering{' '} + options for file metadata. There are three categories for{' '} + filtering: “Date Range” (to filter by time/date),{' '} + “Numerical Range” (for metadata categories that are exclusively{' '} + numerical, such as pH, temperature, etc), and “Categories”{' '} + (For metadata categories that are text only or alphanumeric{' '} + combinations). +

+ “Date Range” and “Numerical Range” display the file data as histograms,{' '} + while “Categories” show the metadata values with associated checkboxes.{' '} + Histograms can be filtered using slider bars, while Categorical data can{' '} + be filtered by selecting or unselecting each checkbox. +

+ All metadata populated in this panel is generated FROM THE FILE ITSELF,{' '} + and the app dynamically populates all this information after file upload. +

+ Changing filter selections in this panel will cause the sample list{' '} + to automatically update, displaying only those samples that meet the{' '} + chosen filtering selections. +
+ : +
+ If you would like to reset the filters, scroll all the way down{' '} + on the left column to find the “Reset Filters” button. +
} + overlayStyle={{maxWidth: '600px'}} + > +
+ {this.displayFilters()} +
(e.key === ' ' ? this.resetFilters() : null)} + > + Reset Filters +
+
+
-
-
- this.state.data[index].sampleName} - > - {this.tableRow} - - = 1 ? 240 : 155)), + overflowY: 'overlay', }} - useList - data={this.state.deleted} - row={this.row} - dataKey="sampleName" - itemHeight={28} - badge - /> -
+ onDragStart={this.dragStart} + onDrop={this.dragEnd} + > + + In the sample info panel, the graph shows the distribution of samples{' '} + (e.g. range of sequencing depth across samples in the uploaded file).{' '} + The red line indicates the position of the present sample row in the{' '} + overall dataset. +

+ There will be icons appear for only one row at a time, on mouse over in{' '} + the filter page window. You can change the order of the samples by using{' '} + a long press of the mouse on the “up/down arrow” button on the left, or{' '} + remove the sample from the pool by the “delete” button on the right{' '} + (After the sample is manually removed, it will be listed under{' '} + “Archived Sample” at the bottom).
: ''} + overlayStyle={{width: '250px', paddingBottom: '5rem'}} + style={this.state.counter == 8 ? {zIndex: 0} : ''} + > + this.state.data[index].sampleName} + > + {this.tableRow} + + {this.renderModal()} + +
+ + 0} + inheritParentBackgroundColor={false} + toolTipTitle={"*mouse click anywhere to advance"} + overlayStyle={{zIndex: '1001'}} + innerStyle={{color: 'white', fontWeight: '600', fontSize: '10px'}} + style={{boxShadow: 'none'}} + > +
+ {helpButtons} +
+
); diff --git a/app/components/FilterChart.js b/app/components/FilterChart.js index f5565499..423ab8b8 100644 --- a/app/components/FilterChart.js +++ b/app/components/FilterChart.js @@ -4,7 +4,7 @@ import Toggle from 'react-toggle'; import { Range } from 'rc-slider'; import { scaleLinear, scaleLog } from 'd3-scale'; -import close from 'images/close.svg'; +import close from 'images/orangeX.svg'; import styles from './FilterChart.css'; import gstyle from './general.css'; @@ -60,7 +60,7 @@ export default class FilterChart extends Component { } const barWidth = (this.xscale(1) - this.xscale(0)); - const strokeWidth = barWidth > 2 ? 0.5 : 0; + const strokeWidth = barWidth > 2 ? 2 : 1; const filter = this.props.filters[this.props.name]; const bars = this.props.data.values.map((d, i) => { @@ -156,7 +156,7 @@ export default class FilterChart extends Component { {range}
) :
; - const style = { width: (this.props.width - (this.padding * 2)), margin: '0 16px' }; + const style = { width: (this.props.width - (this.padding * 2)), margin: '0 16px', }; const brush = filter.expanded ? (
this.props.toggleLog(this.props.name)} + style={{backgroundColor: "#b2b2b2 !important", height: "12px !important", }} />
Log Scale diff --git a/app/components/FilterRow.css b/app/components/FilterRow.css index e1acb7d5..d5838f6f 100644 --- a/app/components/FilterRow.css +++ b/app/components/FilterRow.css @@ -2,7 +2,7 @@ cursor: pointer; width: 20px; height: 20px; - line-height: 1.25rem; + /* line-height: 1.25rem; */ padding: 0; margin: 0; background: #b2b2b2; @@ -15,8 +15,9 @@ } .delete img { - width: 100%; - height: 100%; + width: 10px; + height: 10px; + margin-top: 3px; } .drag { @@ -24,9 +25,17 @@ pointer-events: none; } +.drag img { + width: 100%; + height: 100%; + padding-bottom: 2px; + padding-top: 2px; + margin-top: 0px; +} + .row { background: white; - cursor: move; + cursor: -webkit-grab; } .row:hover .delete { @@ -34,7 +43,7 @@ } .row.grey { - background: #f4f4f4; + background: #f0efef; } .noLeft { @@ -42,8 +51,8 @@ } .cell { - font-family: 'IBM Plex Sans Condensed', Verdana, Sans-serif; - font-weight: 300; + font-family: 'Open Sans'; + font-weight: 600; color: black; padding: 0.25rem 0.5rem; line-height: 1rem; @@ -56,3 +65,60 @@ text-overflow: ellipsis; white-space: nowrap; } + +.cell:hover { + min-width: fit-content !important; +} + +.modal { + display: block; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + margin: 0.5rem 2rem; + z-index: 8; +} + +.modal p { + font-size: 16px; + font-weight: 500; + margin: 1rem 0; +} + +.modal code { + word-break: break-all; +} + +.modal .modalTitle { + font-weight: 800; + font-style: italic; + word-wrap: break-word; +} + +/* This is the Delete button on confirm delete pop up */ +.modal .button { + position: absolute; + bottom: 0.75rem; + right: 7.25rem; + border-radius: 15px; +} + +.modal .button:hover { + background: #F09E6A; + border: none; + color: white; +} + +/* This is the Cancel button on confirm delete pop up */ +.modal .button.cancel { + right: 0; + background: none; +} + +.modal .button.cancel:hover { + background: #F09E6A; + border: none; + color: white; +} \ No newline at end of file diff --git a/app/components/FilterRow.js b/app/components/FilterRow.js index 5252d527..fc9913f3 100644 --- a/app/components/FilterRow.js +++ b/app/components/FilterRow.js @@ -1,4 +1,5 @@ import React, { Component } from 'react'; +import Modal from './Modal'; import menu from 'images/menu.svg'; import close from 'images/close.svg'; @@ -9,6 +10,21 @@ import styles from './FilterRow.css'; import gstyle from './general.css'; export default class FilterRow extends Component { + constructor(props){ + super(props); + this.state = { + deleting: false, + }; + } + + delete = () => { + this.setState({ deleting: true }); + } + + cancel = () => { + this.setState({ deleting: false }); + } + render() { const action = this.props.isRemoved ? (
@@ -26,8 +42,8 @@ export default class FilterRow extends Component {
(e.key === ' ' ? this.props.removeDatum() : null)} + onClick={this.delete} + onKeyPress={e => (e.key === ' ' ? this.delete() : null)} >
delete @@ -35,16 +51,74 @@ export default class FilterRow extends Component {
); + let modalContent = null; + if (this.state.deleting) { + modalContent = ( +
+

+ Are you sure you want to archive sample: + + {this.state.deleting ? ` ${this.props.data.sampleName}` : ''} + + ? +

+

If yes, it can always be found and added back using the Archived Samples tab.

+
(e.key === ' ' ? this.cancel() : null)} + > + Cancel +
+
(e.key === ' ' ? this.props.removeDatum() : null)} + > + Archive +
+ {this.cancel} +
+ ); + } + + const modal = (this.state.deleting) ? ( + + ) : null; + const className = (this.props.index % 2 === 0) ? ( `${styles.row} ${styles.grey}` ) : styles.row; + const tableWidth = this.props.tableWidth - 250;//this scales down tableWidth for the drag, close, and frequency chart cells + return (
+ > {(this.props.data.order !== undefined) ? this.props.data.order.toLocaleString() : ''}
+ > this.props.updatePhinchName(e, this.props.data)} - /> + />
+ > {this.props.data.biomid}
+ > {this.props.data.sampleName}
+ > {(this.props.data.reads !== undefined) ? this.props.data.reads.toLocaleString() : ''}
@@ -101,7 +178,7 @@ export default class FilterRow extends Component { value={this.props.data.reads} width={120 * 2} height={18 * 2} - /> + />
@@ -109,6 +186,7 @@ export default class FilterRow extends Component {
{action} + {modal}
); } diff --git a/app/components/Home.css b/app/components/Home.css index 463dcfc4..5784644b 100644 --- a/app/components/Home.css +++ b/app/components/Home.css @@ -6,11 +6,11 @@ .sectionTitle { display: inline-block; vertical-align: top; - color: #666; - font-size: 20px; + color: #888e8d; + font-size: 22px; height: 26px; line-height: 26px; - font-weight: 400; + font-weight: 600; letter-spacing: 4px; text-transform: uppercase; } @@ -20,13 +20,23 @@ vertical-align: top; margin: 12px 6px; padding: 0; - width: calc(100vw - 660px); - height: 2px; - background: #191919; + width: calc(100vw - 640px); + height: 1px; + background: #001226; +} + +.sectionRuleFlagShip { + display: inline-block; + vertical-align: top; + margin: 12px 6px; + padding: 0; + width: calc(100vw - 791.99px); + height: 1px; + background: #001226; } .area { - padding: 1rem; + padding: 1rem 0 0 0; } .rightSpace { @@ -36,23 +46,58 @@ .left { width: 400px; vertical-align: top; - background: #191919; + background: #001226; +} + +.left .bottomLeft { + position: absolute; + bottom: 1em; + left: 0; + +} + +.help { + display: inline-block; + margin: .5rem 2rem; + width: 152.47px; +} + +.helpIcons { + display: inline-block; + margin-left: .5rem; } + + .right { width: calc(100vw - 400px); vertical-align: top; } +.top { + height: 60vh; + width: 100%; +} + +.bottom { + height: calc(100vh - 70vh); + width: 100%; + padding-top: 2rem; +} + .scroll { - height: 100vh; + height: 200vh; overflow-y: scroll; } .edit { position: absolute; - top: 3rem; - right: 2rem; + max-width: fit-content; + font-size: 13px; + font-family: 'Open Sans'; + font-weight: 600; + top:3.5em; + right: 1rem; cursor: pointer; } @@ -68,7 +113,7 @@ .modal p { font-size: 16px; - font-weight: 300; + font-weight: 400; margin: 1rem 0; } @@ -82,38 +127,63 @@ word-wrap: break-word; } +/* This is the Delete button on confirm delete pop up */ .modal .button { position: absolute; bottom: 0.75rem; - right: 6.25rem; + right: 7.25rem; + border-radius: 15px; } +.modal .button:hover { + background: #F09E6A; + border: none; + color: white; +} + +/* This is the Cancel button on confirm delete pop up */ .modal .button.cancel { right: 0; - background: #00bbda; + background: none; +} + +.modal .button.cancel:hover { + background: #F09E6A; + border: none; color: white; } .info { text-align: center; - padding: 50px; - padding-bottom: 40px; - height: 238px; + margin: 20px 20px 4px; + height: 195px; } - +/* Controls font for version and letters below logo on side bar */ .info p { font-size: 16px; - margin: 38px; - margin-bottom: 0; + margin: 1.5rem 0 0 0; +} + +.info a { + color: #FF4A14; + text-decoration: underline; +} + +.info p.vUpdate { + font-size: 13px; + font-weight: 700; + margin: .5rem 0 0 0; + color: #FF4A14; } .logo { width: 130px; + height: 84px; cursor: pointer; } .links { - height: calc(100vh - 238px); + height: calc(100vh - 300px); overflow-y: scroll; } @@ -123,67 +193,106 @@ .projectType.top { padding-top: 2rem; + overflow-y: scroll; +} + +.projectType.bottom { + padding-bottom: 4rem; } .about { background: white; - color: #191919; + color: #001226; + padding: .5rem 1.5rem; + overflow-y: auto; } .about h2 { text-transform: uppercase; - font-size: 13px; - font-weight: 300; - letter-spacing: 4px; + color: #888e8d; + font-size: 22px; + font-weight: 600; + letter-spacing: .15rem; margin-bottom: 2rem; + margin-top: 1rem; + margin-left: 1rem; +} + +.about .bottom { + height: calc(100vh - 400px); + width: 100%; + padding: 3rem; } .about .center { - width: calc(100vw - 700px); + width: calc(100vw - 425px); padding: 3rem; } .about .center p { - font-weight: 300; + font-weight: 400; margin-bottom: 1rem; } +.about p { + margin: 1rem; + margin-right: 2.5rem; + margin-left: 1rem; + font-size: 16px; + font-weight: 400; +} + p.first::first-letter { - vertical-align: top; - color: #00bbda; + vertical-align: left; + color: #F09E6A; font-size: 800%; font-weight: 100; float: left; - margin: 0; - margin-top: -2.5rem; - margin-bottom: -2.5rem; - margin-right: 0.5rem; - margin-left: -0.75rem; - padding: 0; + margin: -3rem 0; + margin-left: -0.08em; } .about .logos { - width: 300px; - vertical-align: top; - padding: 3rem 1rem; + width: 100%; + vertical-align: middle; + padding: 1rem 1rem; + display: flex; + align-items: center; +} + +.about .logos img { + width: 33%; + padding: 0 3%; } .alogo { - width: 100%; - vertical-align: top; - padding: 1rem 2rem; +} + +.ugalogo { +} + +.pitchlogo { } .about .close { float: right; - width: 2rem; - height: 2rem; - background: #00bbda; + width: 1.5rem; + height: 1.5rem; + margin: 1rem; + + background: white; + border-color: #F09E6A; border-radius: 1rem; + border-bottom: #f8f8f8 1px solid; + transition: border 0.5s ease-in-out; color: white; font-weight: 400; - vertical-align: middle; text-align: center; font-size: 1.5rem; line-height: 1.75rem; } + +.about .close:hover { + background: #F09E6A; + border-color: white; +} diff --git a/app/components/Home.js b/app/components/Home.js index d37041de..06d08121 100644 --- a/app/components/Home.js +++ b/app/components/Home.js @@ -1,15 +1,26 @@ import React, { Component } from 'react'; import { Redirect } from 'react-router'; import _clonedeep from 'lodash.clonedeep'; +import Spotlight from "rc-spotlight"; +import 'antd/dist/antd.css'; +import { Tooltip } from 'antd'; +import ReactTooltip from 'react-tooltip'; import editOff from 'images/edit-off.svg'; import editOn from 'images/edit-on.svg'; +import editHover from 'images/edit-hover.svg'; +import fsIcon from 'images/flagshipIcons.svg'; +import helpGo from 'images/needHelpHP.svg'; +import helpStop from 'images/needHelpHover.svg' +import arrow from 'images/arrow.svg'; +import arrowHover from 'images/arrowHover.svg'; import { pageView } from '../analytics'; import DataContainer from '../datacontainer'; -import { getProjects, setProjectFilters, deleteProject } from '../projects'; +import { getFSProjects, getProjects, setProjectFilters, deleteProject } from '../projects'; +import SpotlightWithToolTip from './SpotlightWithToolTip'; -import ProjectList from './ProjectList'; +import { FSProjectList, ProjectList } from './ProjectList'; import SideBar from './SideBar'; import Loader from './Loader'; import Modal from './Modal'; @@ -56,9 +67,22 @@ export default class Home extends Component { editing: false, deleting: false, erroring: false, + helping: false, + helpIcon: helpGo, error: this.errors.file(), redirect: null, projects: getProjects(), + fsProjects: getFSProjects(), + iconSRC: editOff, + help1: false, + help2: false, + help3: false, + help4: false, + link1: arrow, + link2: arrow, + link3: arrow, + link4: arrow, + link5: arrow, }; this.success = this.success.bind(this); @@ -70,19 +94,19 @@ export default class Home extends Component { this.cancelRemove = this.cancelRemove.bind(this); this.completeRemove = this.completeRemove.bind(this); } - + // this is to find if file up load for new project worked success() { this.setState({ redirect: '/filter', loading: false, }); } - + //this reports errors if file uploaded for new project failed failure(type = 'file', path = '') { const error = this.errors[type](path); this.setState({ loading: false, erroring: true, error }); } - + //This takes you to data view page when a project is selected at home screen view(project) { if (project.slug === 'newproject') { this.setState({ redirect: '/newproject' }); @@ -95,7 +119,7 @@ export default class Home extends Component { this.failure(); } } - + //This handels deletion and renaming of listed projects in home screen edit() { const editing = !this.state.editing; Object.keys(this.shouldUpdate).forEach(k => { @@ -111,8 +135,16 @@ export default class Home extends Component { ); }); this.shouldUpdate = {}; + //this const will handle the deleting of data when projects are deleted const deleting = editing ? this.state.deleting : false; this.setState({ editing, deleting }); + if(this.state.iconSRC === editOn) { + this.setState({iconSRC: editOff}); + } + else + { + this.setState({iconSRC: editOn}); + } } updateName(project, name) { @@ -144,7 +176,70 @@ export default class Home extends Component { this.setState({ deleting: true }); } + /*This function deals with when the mouse hovers over the edit icon on top right of + the home screen and changes img src accordingly to correct svg file */ + handleMouseOver (title) { + switch(title) { + case "edit": + if(this.state.iconSRC === editOff) { + this.setState({ iconSRC: editHover }); + } + break; + case 'New to Phinch?': + this.setState({ link1: arrowHover }); + break; + case 'View our Flagship Datasets': + this.setState({ link2: arrowHover }); + break; + case 'Join the Community': + this.setState({ link3: arrowHover }); + break; + case 'About Phinch': + this.setState({ link4: arrowHover }); + break; + case 'Find a software issue?': + this.setState({ link5: arrowHover }); + break; + } + } + + /*This function deals with the mouse leaving an icon (no longer hovering) and + changed img src to correct svg file */ + handleMouseLeave (title) { + switch(title) { + case "edit": + if(this.state.iconSRC === editHover) { + this.setState({iconSRC: editOff}); + } + else if(this.state.iconSRC === editOn) + { + this.setState({iconSRC: editOn}); + } + else + { + this.setState({iconSRC: editOff}); + } + break; + case 'New to Phinch?': + this.setState({ link1: arrow }); + break; + case 'View our Flagship Datasets': + this.setState({ link2: arrow }); + break; + case 'Join the Community': + this.setState({ link3: arrow }); + break; + case 'About Phinch': + this.setState({ link4: arrow }); + break; + case 'Find a software issue?': + this.setState({ link5: arrow }); + break; + } + } + render() { + console.log("render() method"); if (this.state.redirect !== null && this.state.redirect !== '/') { return ; } @@ -197,6 +292,7 @@ export default class Home extends Component {
); } + const modal = (this.state.deleting || this.state.erroring) ? ( ) : null; - const projects = ProjectList({ - projectList: this.state.projects, + const projects = ; + const flagshipProjects = FSProjectList({ + projectList: this.state.fsProjects, view: this.view, - updateName: this.updateName, - remove: this.remove, - editing: this.state.editing, type: 'projects', }); + const hideStep4 = this.state.projects.length === 1; return (
-
(e.key === ' ' ? this.edit() : null)} + - edit -
-
+
(e.key === ' ' ? this.edit() : null)} + onMouseEnter={() => this.handleMouseOver("edit")} + onMouseLeave={() => this.handleMouseLeave("edit")} + > + edit +
+ +
-

Projects

+

PROJECTS

+ {projects} + {modal}
- {projects}
- {modal} +
+
+

FLAGSHIP DATASETS

+
+ {flagshipProjects} +
+
diff --git a/app/components/LinkList.css b/app/components/LinkList.css index 54c6e244..d42da5ea 100644 --- a/app/components/LinkList.css +++ b/app/components/LinkList.css @@ -1,40 +1,46 @@ .link { - color: white; - margin: 24px 26px; + color: #FFFFFF; + margin: 0px 26px 12px 26px; vertical-align: top; cursor: pointer; } .arrow { - width: 14px; - height: 14px; - border-radius: 7px; + width: 22px; + height: 22px; margin: 8px 0; - margin-right: 8px; + margin-right: 4px; + margin-top: 4px; padding: 0; - background: #00bbda; display: inline-block; vertical-align: top; } .arrow img { display: inline-block; - width: 14px; - margin-bottom: 2px; + width: 20px; + transform: translateY(2px); } + .link h2 { font-size: 22px; - font-weight: 300; - color: #00bbda; + font-weight: 400; + color: #F09E6A; +} + + +.link h2:hover { + border-bottom: 1px solid #F09E6A; + width: fit-content; } .link p { font-size: 16px; - font-weight: 200; + font-weight: 400; margin: 0; - margin-top: 4px; padding: 0; + line-height: 1.2em; } .info { diff --git a/app/components/LinkList.js b/app/components/LinkList.js index a66f24c6..4b4a3a49 100644 --- a/app/components/LinkList.js +++ b/app/components/LinkList.js @@ -1,35 +1,54 @@ import { shell } from 'electron'; -import React from 'react'; +import React, { MouseEventHandler, MouseEvent} from 'react'; -import arrow from 'images/arrow.svg'; import styles from './LinkList.css'; const linkList = [ { name: 'New to Phinch?', - action: () => { shell.openExternal('http://phinch.org/Tutorials'); }, - info: 'Get started with these tutorials', + icon: (context) => (
right facing arrow
), + action: () => { shell.openExternal('https://phinch.org/Tutorials'); }, + info: 'Click here for data formatting instructions and visualization tutorials.', + handleMouseOver: (context) => { context.handleMouseOver('New to Phinch?'); }, + handleMouseLeave: (context) => { context.handleMouseLeave('New to Phinch?'); }, }, { - name: 'View our Gallery', - action: () => { shell.openExternal('https://github.com/PhinchApp/Phinch/wiki/Tutorials'); }, - info: 'See what other researchers have created with Phinch.', + name: 'View our Flagship Datasets', + icon: (context) => (
right facing arrow
), + action: () => { shell.openExternal('https://www.phinch.org/Flagship-Datasets'); }, + info: 'Download and explore datasets from published research studies.', + handleMouseOver: (context) => { context.handleMouseOver('View our Flagship Datasets'); }, + handleMouseLeave: (context) => { context.handleMouseLeave('View our Flagship Datasets'); }, }, { name: 'Join the Community', - action: () => { shell.openExternal('http://phinch.org/Community'); }, - info: 'Discuss features and get the latest update feeds.', + icon: (context) => (
right facing arrow
), + action: () => { shell.openExternal('https://phinch.org/Community'); }, + info: 'Join our Slack channel to discuss features and get help.', + handleMouseOver: (context) => { context.handleMouseOver('Join the Community'); }, + handleMouseLeave: (context) => { context.handleMouseLeave('Join the Community'); }, }, { name: 'About Phinch', + icon: (context) => (
right facing arrow
), action: (context) => { context.setState({ redirect: '/about' }); }, - info: 'What is this all about anyway?', + info: 'Click here for more information about Phinch and our project team.', + handleMouseOver: (context) => { context.handleMouseOver('About Phinch'); }, + handleMouseLeave: (context) => { context.handleMouseLeave('About Phinch'); }, + }, + { + name: 'Find a software issue?', + icon: (context) => (
right facing arrow
), + action: () => { shell.openExternal('https://github.com/PhinchApp/Phinch/issues' ); }, + info: 'Report software bugs and errors on our Github issue tracker.', + handleMouseOver: (context) => { context.handleMouseOver('Find a software issue?'); }, + handleMouseLeave: (context) => { context.handleMouseLeave('Find a software issue?'); }, }, ]; -const icon = (
right facing arrow
); function InfoLink(l, i, context) { + return (
l.action(context)} onKeyPress={e => (e.key === ' ' ? l.action(context) : null)} + onMouseEnter={() => l.handleMouseOver(context)} + onMouseLeave={() => l.handleMouseLeave(context)} > - {icon} + {l.icon(context)}

{l.name}

{l.info}

diff --git a/app/components/Modal.css b/app/components/Modal.css index b9dcee11..62d10aca 100644 --- a/app/components/Modal.css +++ b/app/components/Modal.css @@ -3,7 +3,7 @@ height: 24px; border-radius: 8px 8px 0 0; font-size: 13px; - font-weight: 400; + font-weight: 600; text-align: center; vertical-align: top; cursor: pointer; @@ -11,25 +11,29 @@ z-index: 5; } +.toggle:hover { + background-color: #F09E6A !important; +} + .toggle.shown { - background-color: #1fbbd8 !important; + background-color: #F09E6A !important; } .badge { vertical-align: middle; - width: 24px; - height: 24px; + width: 20px; + height: 20px; border-radius: 12px; display: inline-block; - background-color: #f00; + background-color: #FF4A14; color: white; - margin-left: 120px; - margin-bottom: 16px !important; + margin-left: 119px; + margin-bottom: 14px !important; text-align: center; - font-family: 'IBM Plex Sans Condensed', Verdana, Sans-serif; - line-height: 24px; - font-size: 12px; - font-weight: 400; + font-family: 'Open Sans'; + line-height: 20px; + font-size: 16px; + font-weight: 500; z-index: 9; } @@ -41,7 +45,6 @@ border: 2px solid #333; background: #1a1a1a; color: #808080; - z-index: 5; } .modal .title { @@ -64,9 +67,9 @@ .dataContainer > svg { width: 100%; - font-family: 'IBM Plex Sans Condensed', Verdana, Sans-serif; - font-weight: 200; - font-size: 12px; + font-family: 'Open Sans'; + font-weight: 400; + font-size: 16px; padding-top: 6px; overflow: visible; transform-origin: 0 0; diff --git a/app/components/Modal.js b/app/components/Modal.js index de2f5bda..55dd4a72 100644 --- a/app/components/Modal.js +++ b/app/components/Modal.js @@ -1,7 +1,8 @@ import React, { Component } from 'react'; import { FixedSizeList as List } from 'react-window'; +import SpotlightWithToolTip from './SpotlightWithToolTip'; -import close from 'images/close.svg'; +import close from 'images/orangeX.svg'; import styles from './Modal.css'; import gstyle from './general.css'; @@ -9,7 +10,7 @@ import gstyle from './general.css'; export default class Modal extends Component { constructor(props) { super(props); - const showHidden = props.show ? props.show : false; + const showHidden = props.show || props.spotlight ? props.show : false; this.state = { showHidden }; this.toggleHidden = this.toggleHidden.bind(this); } @@ -36,7 +37,7 @@ export default class Modal extends Component { > {this.props.buttonTitle}
- ) : ''; + ) :
; const badge = (this.props.badge && this.props.data.length) ? (
{this.props.data.length}
- ) : ''; + ) :
; const modal = (this.state.showHidden && this.props.data.length) ? (
this.props.data[index][this.props.dataKey]} - > + > {this.stackRow} ) : ( @@ -94,17 +95,36 @@ export default class Modal extends Component { ) }
- ) : ''; + ) :
; return ( -
- {modal} - {button} - {badge} + + “Archived Samples” show a list of samples that have been manually{' '} + removed by the user. These samples will not be carried through to the{' '} + visualization galleries. +

+ To add these samples back to the main filter page list{' '} + (and regain the ability to visualize these data), hover on a{' '} + sample and click the “rejoin” button on the right hand side. +

+ The “Archived Samples” tab not be visible in the filter page{' '} + window unless you choose to remove samples from the main list{' '} + using the “X” button.
+ } + overlayStyle={{maxWidth: "700px"}} + > + {modal} + + {badge} + {button}
); } diff --git a/app/components/NewProject.css b/app/components/NewProject.css index 3711a65e..78972ef6 100644 --- a/app/components/NewProject.css +++ b/app/components/NewProject.css @@ -1,20 +1,38 @@ h1.heading { color: #666; margin: 0; - margin-bottom: 4rem; + margin-bottom: 1rem; line-height: 1rem; - font-size: 14px; - font-weight: 300; + font-size: 22px; + font-weight: 600; letter-spacing: 4px; text-transform: uppercase; } +h1.flagshipHeading { + display: inline-block; + color: #F09E6A; + margin: 0; + margin-bottom: 1rem; + line-height: 1rem; + font-size: 22px; + font-weight: 600; + white-space: nowrap; +} + .left { display: inline-block; position: relative; vertical-align: top; width: 100px; - background: #333; + background: #575A5C; +} + +.right { + display: inline-block; + position: relative; + vertical-align: baseline; + width: 200px; } .section { @@ -27,59 +45,118 @@ h1.heading { padding: 2rem 6rem; background: transparent; color: #808080; + overflow-y: auto; + overflow-x: hidden; +} + +.flagshipLink { + width: calc(100vw - 600px); + max-width: 350px; + min-width: 272px; overflow-y: scroll; } +.flagshipLink p { + display: inline-block; + margin: 0; + font-weight: 400; + color: black; + font-size: 16px; +} + +.help img{ + width: 151.75px !important; + border: none; + background: none; + padding: 0; +} + +.downloadFS { + padding: 0; + padding-top: 1rem; + border: none; + background: none; +} + .column { - width: 640px; + width: calc(100vw - 150px); } -.column p { +/* .column p { width: 540px; font-weight: 300; color: black; margin: 0.5rem 0; font-size: 1rem; -} +} */ input.wide, -textarea.textarea, +.textarea, .warning { - width: 540px; - margin: 1rem; - margin-left: 0; - padding: 0.25rem 0.5rem; + width: 440px; + margin: 1rem 4rem 1rem 0; + padding: 3rem 0.25rem 0.5rem; resize: none; - border: 1px solid #dcdcdc; - background: white; - color: #666; + border-radius: 8px; + border: 1px solid #F09E6A; + background: #FFFFFF; + color: #E6E6E6; font-size: 1rem; - font-weight: 200; - font-family: 'IBM Plex Mono', Verdana, Sans-serif; + font-weight: 600; + font-family: 'Open Sans'; + text-align: center; + line-height: 1.3em; + } -textarea.textarea { - height: 132px; +.textarea { + display: inline-block; + height: 272px; +} + +.textarea p { + color: #b2b2b2; + font-size: 1rem; + font-weight: 400; + font-family: 'Open Sans'; + text-align: center; +} + +.textarea img { + padding: 0.5rem } -textarea.textarea.drag { - background: #00bbda; +.textarea.drag { + background: #F09E6A; color: white; } -textarea.textarea.drag * { +.textarea.drag * { pointer-events: none; } .button { + display: block; color: white; - background: #00bbda; + background: #FFFFFF; } +.browse { + display: block; + padding: 0 0 1rem 0; + border: none; + background: none; +} + + .filter { - float: right; + display: block; + padding: 0; + border: none; + background: none; } + .loader { display: block; } @@ -93,11 +170,12 @@ td { margin: 0; padding: 0.25rem 0; color: black; - font-weight: 300; + font-weight: 400; + font-size: 16px; } td.label { - font-weight: 400; + font-weight: 600; text-align: right; padding-right: 1rem; } @@ -108,9 +186,7 @@ td.label { } .error { - margin-left: 7.5rem; - color: black; - font-weight: 300; + font-weight: 400; } .link { @@ -123,11 +199,15 @@ td.label { border-bottom: black 1px solid; } + .warning { - max-width: 520px; - color: black; - background-color: rgba(255, 0, 0, 0.125); - margin: 0; + color: #FF4A14; + padding: 0; + border: none; + background: none; + text-align: left; + font-size: 14px; + width: 540px; } .warning ul { @@ -148,7 +228,7 @@ td.label { .warning ul li div, .warning .head div { max-width: 100%; - padding: 0 0.25em; + padding-bottom: 1rem; } .warning .head { diff --git a/app/components/NewProject.js b/app/components/NewProject.js index 2e9e3544..d5ca7836 100644 --- a/app/components/NewProject.js +++ b/app/components/NewProject.js @@ -1,9 +1,20 @@ import React, { Component } from 'react'; import { Link, Redirect } from 'react-router-dom'; import { remote, shell } from 'electron'; +import ReactTooltip from 'react-tooltip'; import logo from 'images/phinch.svg'; import back from 'images/back.svg'; +import hoverBack from 'images/backArrowW.svg'; +import upload from 'images/upload.svg'; +import browseOn from 'images/browseOn.svg'; +import browseOff from 'images/browseOff.svg'; +import filterOn from 'images/filterOn.svg'; +import filterOff from 'images/filterOff.svg'; +import flagshipOn from 'images/flagshipOn.svg'; +import flagshipOff from 'images/flagshipOff.svg'; +import needHelp from 'images/needHelp.svg'; +import needHelpHover from 'images/needHelpHover.svg'; import { createProject } from '../projects'; import DataContainer from '../datacontainer'; @@ -14,6 +25,8 @@ import Loader from './Loader'; import styles from './NewProject.css'; import gstyle from './general.css'; +import { style } from 'd3'; +import { StrategyValues } from 'pako'; const { dialog } = remote; @@ -32,17 +45,18 @@ export default class NewProject extends Component { const communityInfo = (

- {'If you’re having trouble, visit our Help page or '} + {'Phinch currently supports upload of a single BIOM-formatted data file. '} + {'All accompanying metadata MUST be embedded within the BIOM file during '} + {'uplaod (taxonomy strings, environmental metadata, gene names, etc.). '} + {'Click here to view BIOM file formatting instructions for Phinch: '} shell.openExternal('https://github.com/PhinchApp/Phinch')} - onKeyPress={e => (e.key === ' ' ? shell.openExternal('https://github.com/PhinchApp/Phinch') : null)} > - Community page + https://github.com/PhinchApp/Phinch - {' to see if you can resolve this. Once you have a correct data file, try loading it again.'}

); @@ -88,25 +102,33 @@ export default class NewProject extends Component { width: window.innerWidth, height: window.innerHeight, showLeftSidebar: true, + browse: browseOff, + filter: filterOff, + flagship: flagshipOff, + helpButton: needHelp, + backArrow: back, }; + /*This sets the width of the sidemenu to 100px (same as logo) and it also has a min max set for + the size of the sidebar to maximize other components screen size + NOTE: left min and max should be removed someday as the current formula is more effecient.*/ this.metrics = { - padding: 16, - leftSidebar: 121, + leftSidebar: "100px", left: { - min: 27, - max: 121, + min: -10, + max: (window.innerWidth*0.0672 + 30), }, }; this.menuItems = [ { - id: 'back', + id: "back", name: 'Back', action: () => { this.setState({ redirect: '/Home' }); + this.rebuildTooltip(); }, - icon: back, + icon: back-arrow, }, ]; @@ -120,6 +142,8 @@ export default class NewProject extends Component { this.handleOpenButton = this.handleOpenButton.bind(this); this.toggleMenu = this.toggleMenu.bind(this); this.updateDimensions = this.updateDimensions.bind(this); + this.handleMouseLeave = this.handleMouseLeave.bind(this); + this.handleMouseOver = this.handleMouseOver.bind(this); } componentDidMount() { @@ -149,6 +173,10 @@ export default class NewProject extends Component { this.setState({ showLeftSidebar }); } + rebuildTooltip() { + console.log("render() method"); + } + updateSummary(project) { DataContainer.setSummary(project, () => { const summary = DataContainer.getSummary(); @@ -254,12 +282,74 @@ export default class NewProject extends Component { return false; } + /*This function deals with when the mouse hovers over the browse icon on top right of + and changes img src accordingly to correct svg file */ + handleMouseOver (button) { + switch(button) { + case "browse": + if(this.state.browse === browseOff) { + this.setState({browse: browseOn}); + } + break; + case "filter": + if(this.state.filter === filterOff) { + this.setState({filter: filterOn}); + } + break; + case "flagship": + if(this.state.flagship === flagshipOff) { + this.setState({flagship: flagshipOn}); + } + break; + case "help": + if(this.state.helpButton === needHelp) { + this.setState({helpButton: needHelpHover}); + } + break; + case 'back': + this.setState({ backArrow: hoverBack }); + break; + } + } + + /*This function deals with the mouse leaving an icon (no longer hovering) and + changed img src to correct svg file */ + handleMouseLeave (button) { + switch(button) { + case "browse": + if(this.state.browse === browseOn) { + this.setState({browse: browseOff}); + } + break; + case "filter": + if(this.state.filter === filterOn) { + this.setState({filter: filterOff}); + } + break; + case "flagship": + if(this.state.flagship === flagshipOn) { + this.setState({flagship: flagshipOff}); + } + break; + case "help": + if(this.state.helpButton === needHelpHover) { + this.setState({helpButton: needHelp}); + } + break; + case 'back': + this.setState({ backArrow: back }); + break; + } + } + render() { const redirect = this.state.redirect === null ? '' : ; + const result = (this.state.valid === 'Yes') ? ( ) : (
{this.state.error}
); - const indicator = (this.state.valid === 'Yes') ? ( -
- ) : ( -
- ); + const indicatorBG = (this.state.valid === 'Yes') ? '#00da3e' : '#ff2514'; + const indicator =
const table = (this.state.valid === null) ? null : ( @@ -309,15 +396,9 @@ export default class NewProject extends Component { r: 0, g: 0, b: 0, a: 0.25 }; } else if (this.state.valid !== null) { - if (this.state.valid === 'Yes') { - microbeColor = { - r: 0, g: 255, b: 0, a: 0.5 - }; - } else { - microbeColor = { - r: 255, g: 0, b: 0, a: 0.25 - }; - } + microbeColor = { + r: 0, g: 0, b: 0, a: 0.25 + }; } return (
@@ -336,6 +417,15 @@ export default class NewProject extends Component { Phinch +
-
+

New Project

-

- To start a new project, you can browse for a file on your local hard drive or drag the file to the box below. {/* eslint-disable-line max-len */} -

- - -