From 85ece2e7376dd7a9074b38aeeaff014986d5347f Mon Sep 17 00:00:00 2001 From: Ben Ebsworth Date: Wed, 22 Aug 2018 17:54:09 +1000 Subject: [PATCH 1/4] refactored component locations to be more relavently placed to the views they are used in, shared components stay in the top level components directory --- src/components/AboutHeader/index.jsx | 76 ------ src/components/AboutHeader/style.js | 114 --------- src/components/AlertRow/index.jsx | 38 --- src/components/AlertRow/style.js | 113 --------- src/components/AlertRowGraph/index.jsx | 60 ----- src/components/AlertRowGraph/style.js | 18 -- src/components/Alerts/index.jsx | 48 ---- src/components/Alerts/style.js | 52 ----- src/components/ChartOptions/index.jsx | 98 -------- src/components/ChartOptions/style.js | 71 ------ src/components/Legend/index.jsx | 79 ------- src/components/Legend/style.js | 87 ------- src/components/LegendCompanyCode/index.jsx | 15 -- src/components/LegendCompanyCode/style.js | 31 --- src/components/LegendCompanyLogo/index.jsx | 32 --- src/components/LegendCompanyLogo/style.js | 17 -- .../LegendCompanyMarketCap/index.jsx | 69 ------ .../LegendCompanyMarketCap/style.js | 29 --- src/components/LegendCompanyPE/index.jsx | 15 -- src/components/LegendCompanyPE/style.js | 31 --- src/components/MoversList/index.jsx | 50 ---- src/components/MoversList/style.js | 44 ---- src/components/MoversListRow/index.jsx | 36 --- src/components/MoversListRow/style.js | 100 -------- src/components/ProfileAlerts/index.jsx | 22 -- src/components/ProfileAlerts/style.js | 15 -- src/components/ProfileChart/index.jsx | 22 -- src/components/ProfileChart/style.js | 14 -- src/components/ProfileHeader/index.jsx | 22 -- src/components/ProfileHeader/style.js | 14 -- src/components/ProfileSidePanel/index.jsx | 22 -- src/components/ProfileSidePanel/style.js | 15 -- src/components/TopChartVictory/components.jsx | 58 ----- src/components/TopChartVictory/index.jsx | 219 ------------------ src/components/TopChartVictory/style.js | 96 -------- src/components/TopShortsList/index.jsx | 64 ----- src/components/TopShortsList/style.js | 39 ---- src/components/TopShortsListRow/index.jsx | 43 ---- src/components/TopShortsListRow/style.js | 113 --------- src/components/WindowPicker/index.jsx | 33 --- src/components/WindowPicker/style.js | 14 -- src/services/sapi/client.js | 35 ++- src/views/aboutpage/index.jsx | 2 +- src/views/alerts/components/List/index.jsx | 6 +- src/views/alerts/index.jsx | 21 +- src/views/alerts/style.js | 5 + src/views/dashboard/index.jsx | 13 +- src/views/dashboard/style.js | 1 + src/views/stockprofile/index.jsx | 10 +- src/views/summary/index.jsx | 18 +- 50 files changed, 78 insertions(+), 2181 deletions(-) delete mode 100644 src/components/AboutHeader/index.jsx delete mode 100644 src/components/AboutHeader/style.js delete mode 100644 src/components/AlertRow/index.jsx delete mode 100644 src/components/AlertRow/style.js delete mode 100644 src/components/AlertRowGraph/index.jsx delete mode 100644 src/components/AlertRowGraph/style.js delete mode 100644 src/components/Alerts/index.jsx delete mode 100644 src/components/Alerts/style.js delete mode 100644 src/components/ChartOptions/index.jsx delete mode 100644 src/components/ChartOptions/style.js delete mode 100644 src/components/Legend/index.jsx delete mode 100644 src/components/Legend/style.js delete mode 100644 src/components/LegendCompanyCode/index.jsx delete mode 100644 src/components/LegendCompanyCode/style.js delete mode 100644 src/components/LegendCompanyLogo/index.jsx delete mode 100644 src/components/LegendCompanyLogo/style.js delete mode 100644 src/components/LegendCompanyMarketCap/index.jsx delete mode 100644 src/components/LegendCompanyMarketCap/style.js delete mode 100644 src/components/LegendCompanyPE/index.jsx delete mode 100644 src/components/LegendCompanyPE/style.js delete mode 100644 src/components/MoversList/index.jsx delete mode 100644 src/components/MoversList/style.js delete mode 100644 src/components/MoversListRow/index.jsx delete mode 100644 src/components/MoversListRow/style.js delete mode 100644 src/components/ProfileAlerts/index.jsx delete mode 100644 src/components/ProfileAlerts/style.js delete mode 100644 src/components/ProfileChart/index.jsx delete mode 100644 src/components/ProfileChart/style.js delete mode 100644 src/components/ProfileHeader/index.jsx delete mode 100644 src/components/ProfileHeader/style.js delete mode 100644 src/components/ProfileSidePanel/index.jsx delete mode 100644 src/components/ProfileSidePanel/style.js delete mode 100644 src/components/TopChartVictory/components.jsx delete mode 100644 src/components/TopChartVictory/index.jsx delete mode 100644 src/components/TopChartVictory/style.js delete mode 100644 src/components/TopShortsList/index.jsx delete mode 100644 src/components/TopShortsList/style.js delete mode 100644 src/components/TopShortsListRow/index.jsx delete mode 100644 src/components/TopShortsListRow/style.js delete mode 100644 src/components/WindowPicker/index.jsx delete mode 100644 src/components/WindowPicker/style.js diff --git a/src/components/AboutHeader/index.jsx b/src/components/AboutHeader/index.jsx deleted file mode 100644 index 895401b..0000000 --- a/src/components/AboutHeader/index.jsx +++ /dev/null @@ -1,76 +0,0 @@ -import React from 'react'; -import Transition from 'react-transition-group/Transition'; -import AboutHeaderHero from '../../assets/images/about-main.svg'; -import { - HeroWrapper, - HeroButtonWrapper, - HeroTitleWrapper, - duration, - transitionStyles, - Wrapper, -} from './style'; - -/** - * Top Navbar responsible for rendering the basic site-map layout including: blog | about | disclaimer etc - * Will also manage the implementation of the navbar collapse on mobile devices i.e transition to burger and burger animation on open/close etc. - * TODO: - * * handle mobile compaction of navbar component - * - */ -class AboutHeader extends React.Component { - constructor(props) { - super(props); - this.state = { - inside: false, - }; - } - componentDidMount() { - this.toggleEnterState(); - } - - toggleEnterState() { - this.setState({inside: true}); - } - - render() { - return ( - - {state => { - return ( - - - shorted-hero - - -
-
- Hello, we are -
-
Shorted
-
-
- Bringing ASX market short positions to your - front door, with added insights and - intelligence. -
-
- - Dashboard - -
-
-
- ); - }} -
- ); - } -} - -export default AboutHeader; diff --git a/src/components/AboutHeader/style.js b/src/components/AboutHeader/style.js deleted file mode 100644 index a252210..0000000 --- a/src/components/AboutHeader/style.js +++ /dev/null @@ -1,114 +0,0 @@ -import styled from 'styled-components'; - -export const duration = 500; -export const transitionStyles = { - entering: {opacity: 0, Ypos: -100}, - entered: {opacity: 1, Ypos: 0}, - exited: {opacity: 0}, -}; - -export const HeroButtonWrapper = styled.a` - grid-area: hero-button; - width: 210px; - height: 80px; - display: flex; - text-decoration: none; - justify-content: center; /* align horizontal */ - align-items: center; /* align vertical */ - margin: 5px; - border-radius: 30px; - color: white; - background: #282fc3; - outline: 0; - font-size: 30px; - font-family: 'Avenir Next,Segoe UI', Helvetica, Arial, sans-serif, - 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; - font-weight: bold; - background: linear-gradient(-135deg, #827d9f, #0f0646); - &:hover { - -webkit-box-shadow: -3px 4px 7px 0px rgba(0, 0, 0, 0.75); - -moz-box-shadow: -3px 4px 7px 0px rgba(0, 0, 0, 0.75); - box-shadow: -3px 4px 7px 0px rgba(0, 0, 0, 0.75); - color: #eee; - text-decoration: none !important; - height: 80px; - transition: height 0.66s ease-out; - margin-bottom: 5px; - } -`; - -export const Wrapper = styled.div` - display: grid; - grid-gap: 20px; - grid-template-columns: 1fr 2fr; - grid-template-areas: 'hero-title hero-image'; - margin-right: 100px; - margin-left: 100px; - margin-top: 250px; - opacity: ${props => props.opacity}; - transition: ${props => `${props.duration}ms ease-in-out`}; - transition-property: opacity, transform; - transform: ${props => `translateY(${props.Ypos}px)`}; -`; - -export const HeroWrapper = styled.div` - grid-area: hero-image; - width: 100%; -`; -export const HeroTitleWrapper = styled.div` - grid-area: hero-title; - display: grid; - grid-template-columns: 1fr; - grid-template-rows: 3fr 1fr 1fr; - grid-template-areas: - 'hero-title' - 'hero-description' - 'todo' - 'hero-button'; - .hero-title { - margin-top: 50%; - display: flex; - flex-direction: column; - .header-1 { - display: flex; - flex-direction: column; - text-align: center; - justify-content: center; - vertical-align: middle; - grid-area: hero-title; - float: left; - font-size: 70px; - font-weight: bold; - font-family: Avenir Next, sans-serif; - } - .header-2 { - display: flex; - flex-direction: column; - text-align: center; - justify-content: center; - vertical-align: middle; - grid-area: hero-title; - font-size: 75px; - font-family: Avenir Next, sans-serif; - font-weight: bold; - color: #282fc3; - float: left; - } - } - .hero-description { - display: flex; - flex-direction: column; - grid-area: hero-description; - font-weight: 300; - font-size: 30px; - font-family: Avenir Next, sans-serif; - } - .hero-button { - grid-area: hero-button; - display: flex; - flex-direction: column; - justify-content: center; - vertical-align: middle; - height: 70px; - } -`; diff --git a/src/components/AlertRow/index.jsx b/src/components/AlertRow/index.jsx deleted file mode 100644 index fee1503..0000000 --- a/src/components/AlertRow/index.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; -import AlertRowGraph from '../../components/AlertRowGraph'; -import { - Wrapper, - Name, - Code, - Percentage, - IndicatorWrapper, - IndicatorUp, - IndicatorDown, - PercentageChanged, - PercentageCurrent, - More, -} from './style'; - -/** - * Renders a given row in the alert & anomalies widget. - */ -const AlertRow = props => ( - - -
{props.code}
-
- {props.name} - - {props.current}% - - {props.changed}% - - - - {props.changed > 0 ? : } - - -
-); - -export default AlertRow; diff --git a/src/components/AlertRow/style.js b/src/components/AlertRow/style.js deleted file mode 100644 index 618548e..0000000 --- a/src/components/AlertRow/style.js +++ /dev/null @@ -1,113 +0,0 @@ -import styled from 'styled-components'; - -export const Wrapper = styled.div` - display: grid; - @media only screen and (min-width: 1024px) { - grid-template-columns: 90px 1fr 1fr 1fr 1fr; - grid-template-rows: repeat(2, 1fr); - grid-template-areas: - 'code name name percentage indicator graph' - 'code name name percentage indicator graph'; - } - @media only screen and (min-width: 1280px) { - grid-template-columns: 90px 1fr 70px 1fr 1fr; - grid-template-rows: repeat(2, 1fr); - grid-template-areas: - 'code name percentage indicator graph' - 'code name percentage indicator graph'; - }; - - margin: 6px; - margin-left: 7px; - margin-right: 7px; - height: 5vw; - background: #e2e2e2; - padding-top: 4px; - padding-bottom: 4px; -`; - -export const Name = styled.div` - grid-area: name; - display: flex; - font-size: 16px; - flex-direction: column; - justify-content: center; - vertical-align: middle; - font-family: Avenir Next, sans-serif; -`; - -export const Code = styled.div` - grid-area: code; - display: flex; - flex-direction: column; - justify-content: center; - vertical-align: middle; - padding-left: 10px; - .code { - background-color: gray; - width: 60px; - height: 45px; - display: flex; - font-size: 21px; - - font-family: Avenir Next, sans-serif; - flex-direction: column; - justify-content: center; - vertical-align: middle; - text-align: center; - margin-left: 5px; - } -`; -export const Percentage = styled.div` - grid-area: percentage; - display: grid; - grid-template-rows: repeat(2, 1fr); - grid-gap: 1px; - grid-template-areas: - 'current' - 'changed'; -`; -export const PercentageCurrent = styled.div` - grid-area: current; - font-size: 1.9vw; - font-weight: bold; - font-family: Avenir Next, sans-serif; -`; -export const PercentageChanged = styled.div` - grid-area: changed; - margin-left: 40px; - font-weight: bold; - font-size: 1.2vw; - font-family: Avenir Next, sans-serif; - color: ${props => (props.value > 0 ? `red` : `green`)}; - padding-bottom: 5px; -`; -export const IndicatorUp = styled.div` - width: 0; - height: 0; - border-left: 30px solid transparent; - border-right: 30px solid transparent; - border-bottom: 30px solid red; -`; -export const IndicatorDown = styled.div` - width: 0; - height: 0; - border-left: 30px solid transparent; - border-right: 30px solid transparent; - border-top: 30px solid green; -`; - -export const IndicatorWrapper = styled.div` - grid-area: indicator; - @media (min-width: 1024px) { - display: grid; - } - display: none; - flex-direction: column; - text-align: center; - vertical-align: middle; - justify-content: center; - margin: auto; - padding: auto; -`; - diff --git a/src/components/AlertRowGraph/index.jsx b/src/components/AlertRowGraph/index.jsx deleted file mode 100644 index 5498d3a..0000000 --- a/src/components/AlertRowGraph/index.jsx +++ /dev/null @@ -1,60 +0,0 @@ -import React from 'react'; -import { - VictoryChart, - VictoryAxis, - VictoryArea, - VictoryContainer, -} from 'victory'; -// import { LineChart, Line, ResponsiveContainer } from 'recharts'; -import {Wrapper} from './style'; - -/** - * LegendCompanyMarketCap - * - */ -class AlertRowGraph extends React.Component { - constructor(props) { - super(props); - this.state = {}; - } - getTickValues() { - return [ - new Date(2002, 1, 1), - new Date(2017, 1, 1), - new Date(2018, 1, 1), - ]; - } - - render() { - return ( - - } - > - - ''} - style={{axis: {stroke: 'none'}}} - /> - - - ); - } -} - -export default AlertRowGraph; diff --git a/src/components/AlertRowGraph/style.js b/src/components/AlertRowGraph/style.js deleted file mode 100644 index ad16aaa..0000000 --- a/src/components/AlertRowGraph/style.js +++ /dev/null @@ -1,18 +0,0 @@ -import styled from 'styled-components'; - -export const Wrapper = styled.div` - grid-area: graph; - margin: 5px; - max-width: 245px; - @media (min-width: 1280px) { - display: flex; - } - display: none; - margin-right: 10px; - flex-direction: column; - text-align: center; - justify-content: center; - -webkit-box-shadow: 1px 1px 2px 0px rgba(181, 181, 181, 1); - -moz-box-shadow: 1px 1px 2px 0px rgba(181, 181, 181, 1); - box-shadow: 1px 1px 2px 0px rgba(181, 181, 181, 1); -`; diff --git a/src/components/Alerts/index.jsx b/src/components/Alerts/index.jsx deleted file mode 100644 index 04c44be..0000000 --- a/src/components/Alerts/index.jsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react'; -import Transition from 'react-transition-group/Transition'; -import ShortedAPI from '../../services/sapi/client'; -import AlertRow from '../../components/AlertRow'; -import {duration, transitionStyles, Wrapper, Header, More, Rows} from './style'; -/** - * Responsible for the rendering/display of "alerts" which represent anomalous changes in short positions for a given stock. - */ -class Alerts extends React.Component { - constructor(props) { - super(props); - this.state = { - inside: false, - }; - } - componentDidMount() { - this.toggleEnterState(); - } - - toggleEnterState() { - this.setState({inside: true}); - } - - render() { - const alerts = this.props.data.map(alert => ( - - )); - return ( - - {state => { - return ( - -
Alerts & Anomalies
- - {alerts} - - Show More -
- ); - }} -
- ); - } -} - -export default Alerts; diff --git a/src/components/Alerts/style.js b/src/components/Alerts/style.js deleted file mode 100644 index 6f4c684..0000000 --- a/src/components/Alerts/style.js +++ /dev/null @@ -1,52 +0,0 @@ -import styled from 'styled-components' - -export const duration = 500 -export const transitionStyles = { - entering: { opacity: 0, Ypos: 500 }, - entered: { opacity: 1, Ypos: 0 }, - exited: { opacity: 0 } -} -export const Wrapper = styled.div` - border-radius: 5px; - border: 1px solid #eee; - opacity: ${props => props.opacity}; - transition: ${props => `${props.duration}ms ease-in-out`}; - transition-property: opacity, transform; - transform: ${props => `translateY(${props.Ypos}px)`}; - background: white; - display: grid; - grid-template-rows: 60px 1fr 60px; - grid-template-areas: - 'header' - 'rows' - 'more'; - float: left; - vertical-align: middle; - flex-direction: column; - text-align: center; - grid-area: top-alerts; -`; -export const Header = styled.div` - grid-area: header; - font-size: 30px; - font-family: Avenir Next,sans-serif; - font-weight: bold; -` -export const More = styled.div` - grid-area: more; - height: 50px; - display: flex; - flex-direction: column; - text-align: center; - font-size: 35px; - font-weight: bold; - background: #dadada; - border-radius: 0 0 15px 15px; - margin: 4px; - justify-content: center; - font-family: Avenir Next, sans-serif; -` -export const Rows = styled.div` - grid-area: rows; - height: 100%; -` diff --git a/src/components/ChartOptions/index.jsx b/src/components/ChartOptions/index.jsx deleted file mode 100644 index 2018a74..0000000 --- a/src/components/ChartOptions/index.jsx +++ /dev/null @@ -1,98 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import {Radio, Icon} from 'antd'; -// TODO: look at how antd style sheets can be inherently added into project without imports, plugin may be doing this -import 'antd/dist/antd.css'; -import { - Button, - OptionsWrapper, - OptionHeader, - Wrapper, - radioStyle, - buttonStyle, -} from './style'; - -const RadioButton = Radio.Button; -const RadioGroup = Radio.Group; - -const Options = props => ( - - Chart display -
- props.onOptionsChange(e.target.value)} - defaultValue="NORMAL" - buttonStyle="solid"> - - Normal - - - CandleStick - - - Smoothed - - - Area - - -
-
-); - -class ChartOptions extends React.Component { - constructor(props) { - super(props); - this.state = { - open: false, - }; - this.optionsArea = React.createRef(); - this.handleOutsideClick = this.handleOutsideClick.bind(this); - } - onComponentDidUnmount() { - document.removeEventListener('click', e => this.handleOutsideClick(e)); - } - handleOutsideClick(e) { - const chartOptionsArea = ReactDOM.findDOMNode(this); - // chartOptionsArea = this.node - if (chartOptionsArea.contains(e.target)) { - return; - } - this.handleSelect(); - } - - handleSelect() { - if (!this.state.open) { - // attach/remove event handler - document.addEventListener('click', this.handleOutsideClick, false); - } else { - document.removeEventListener( - 'click', - this.handleOutsideClick, - false, - ); - } - this.setState(prevState => ({ - open: !prevState.open, - })); - } - handleOptionsChange(e, v) { - console.log(e, v); - } - - render() { - return ( - - - this.props.onChartOptionChange(v)} - open={this.state.open} - /> - - ); - } -} - -export default ChartOptions; diff --git a/src/components/ChartOptions/style.js b/src/components/ChartOptions/style.js deleted file mode 100644 index 6402130..0000000 --- a/src/components/ChartOptions/style.js +++ /dev/null @@ -1,71 +0,0 @@ -import styled from 'styled-components'; - -// dropdown wrapper -export const Wrapper = styled.div` - margin-top: auto; - margin-bottom: auto; - margin-left: auto; - margin-right: 10px; - display: inline-block; - position: relative; -`; -export const buttonStyle = { - fontSize: 25, - fill: 'black', - color: 'black', -}; -export const Button = styled.div` - width: 30px; - height: 30px; - margin-right: 7px; - padding-top: 10px; - display: inline-block; - position: relative; -`; -// dropdown-content -export const OptionsWrapper = styled.div` - padding: 5px; - display: ${props => (props.open ? `flex` : `none`)}; - position: absolute; - z-index: 1; - background: white; - right: 0; - border-radius: 5px; - border: 1px solid #eee; - min-width: 160px; - box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); - flex-direction: column; - text-align: center; - align-items: center; - .chart-modes { - display: flex; - flex-direction: column; - text-align: center; - width: 130px; - } -`; -export const Option = styled.div` - font-size: 15px; - font-family: Avenir Next, sans-serif; - font-weight: 300; - display: flex; - flex-direction: column; - text-align: center; -`; -export const OptionHeader = styled.div` - font-size: 20px; - font-family: Avenir Next, sans-serif; - font-weight: bold; - display: flex; - flex-direction: column; - text-align: center; -`; - -export const OptionWrapper = styled.div``; - -export const radioStyle = { - display: 'block', - height: '30px', - lineHeight: '30px', - margin: 4, -}; diff --git a/src/components/Legend/index.jsx b/src/components/Legend/index.jsx deleted file mode 100644 index a59d8fd..0000000 --- a/src/components/Legend/index.jsx +++ /dev/null @@ -1,79 +0,0 @@ -import React from 'react'; -import Transition from 'react-transition-group/Transition'; -import LegendCompanyCode from '../../components/LegendCompanyCode'; -import LegendCompanyLogo from '../../components/LegendCompanyLogo'; -import LegendCompanyPE from '../../components/LegendCompanyPE'; -import LegendCompanyMarketCap from '../../components/LegendCompanyMarketCap'; -import { - Wrapper, - duration, - transitionStyles, - UnselectedWrapper, - CompanyName, - CompanySector, -} from './style'; -import ShortedAPI from '../../services/sapi/client'; -/** - * Renders a shorted.com.au logo - * TODO: add data fetch here, async or prefetch based of top-short positions - */ -class Legend extends React.Component { - constructor(props) { - super(props); - this.apiClient = new ShortedAPI(); - this.state = { - inside: false, - code: false, - }; - } - componentDidUpdate(prevProps) { - // You don't have to do this check first, but it can help prevent an unneeded render - if (prevProps.code !== this.props.code) { - this.setState({ - code: this.props.code, - inside: false, - }); - } - } - componentDidMount() { - this.toggleEnterState(); - } - - toggleEnterState() { - this.setState({inside: true}); - } - render() { - const data = this.apiClient.getStockSummary(this.props.code); - const logo = this.apiClient.getStockLogo(this.props.code); - return ( - - {state => { - return this.props.code ? ( - - - - - {data.metadata.name} - - {data.metadata.sector} - - - - ) : ( - -

hover over graph to show profile

-
- ); - }} -
- ); - } -} - -export default Legend; diff --git a/src/components/Legend/style.js b/src/components/Legend/style.js deleted file mode 100644 index a63a02f..0000000 --- a/src/components/Legend/style.js +++ /dev/null @@ -1,87 +0,0 @@ -import styled from 'styled-components'; - -export const duration = 500; -export const transitionStyles = { - entering: {opacity: 0, Ypos: 500}, - entered: {opacity: 1, Ypos: 0}, - exited: {opacity: 0}, -}; -export const Wrapper = styled.div` - opacity: ${props => props.opacity}; - transition: ${props => `${props.duration}ms ease-in-out`}; - transition-property: opacity, transform; - transform: ${props => `translateY(${props.Ypos}px)`}; - border-radius: 4px; - grid-area: legend; - display: grid; - grid-gap: 5px; - grid-template-rows: 40px 40px 40px 40px 100px; - grid-template-columns: repeat(4, 1fr); - grid-template-areas: - 'company-name company-name company-name company-name' - 'company-logo company-logo company-code company-code' - 'company-logo company-logo company-pe company-pe' - 'company-sector company-sector company-sector company-sector' - 'company-mc company-mc company-mc company-mc'; -`; -export const UnselectedWrapper = styled.div` - opacity: ${props => props.opacity}; - transition: ${props => `${props.duration}ms ease-in-out`}; - transition-property: opacity, transform; - transform: ${props => `translateY(${props.Ypos}px)`}; - grid-area: legend; - display: flex; - flex-direction: column; - vertical-align: middle; - justify-content: center; - text-align: center; - background: white; - border-radius: 4px; - height: 100%; - font-family: Avenir Next, sans-serif; - font-size: 22px; - font-weight: bold; - padding-left: 30px; - padding-right: 30px; -`; -export const CompanyHeader = styled.div` - display: flex; - border-radius: 5px; - flex-direction: column; - vertical-align: middle; - justify-content: center; - font-size: 0.8vh; - grid-area: company-header; -`; -export const CompanyMarketCap = styled.div` - grid-area: company-mc; - display: flex; - flex-direction: column; - vertical-align: middle; -`; -export const CompanySector = styled.div` - grid-area: company-sector; - display: flex; - border-radius: 5px; - background: white; - font-size: 1vh; - font-weight: bold; - font-family: Avenir Next, sans-serif; - text-align: center; - flex-direction: column; - vertical-align: middle; - justify-content: center; -`; -export const CompanyName = styled.div` - border-radius: 5px; - grid-area: company-name; - display: flex; - background: white; - font-size: 1vh; - font-weight: bold; - font-family: Avenir Next, sans-serif; - text-align: center; - flex-direction: column; - vertical-align: middle; - justify-content: center; -`; diff --git a/src/components/LegendCompanyCode/index.jsx b/src/components/LegendCompanyCode/index.jsx deleted file mode 100644 index 5e6cef3..0000000 --- a/src/components/LegendCompanyCode/index.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import {Wrapper, Code, Header} from './style'; - -/** - * Renders a nicely styled and dynamically scaling header for the legend view which will display the company icon/logo - * as well as its stock code. - * - */ -const LegendCompanyCode = props => ( - -
Issuer code
- {props.code} -
-); -export default LegendCompanyCode; diff --git a/src/components/LegendCompanyCode/style.js b/src/components/LegendCompanyCode/style.js deleted file mode 100644 index ef20411..0000000 --- a/src/components/LegendCompanyCode/style.js +++ /dev/null @@ -1,31 +0,0 @@ -import styled from 'styled-components'; - -export const Wrapper = styled.div` - background: white; - grid-area: company-code; - border-radius: 5px; - display: flex; - flex-direction: column; - justify-content: center; - vertical-align: middle; - text-align: center; -`; - -export const Code = styled.div` - flex-direction: column; - justify-content: center; - vertical-align: middle; - text-align: center; - font-size: 15px; - font-weight: 600; - font-family: Avenir Next, sans-serif; -`; -export const Header = styled.div` - flex-direction: column; - justify-content: center; - vertical-align: middle; - text-align: center; - font-size: 11px; - font-weight: bold; - font-family: Avenir Next, sans-serif; -`; diff --git a/src/components/LegendCompanyLogo/index.jsx b/src/components/LegendCompanyLogo/index.jsx deleted file mode 100644 index f95eb87..0000000 --- a/src/components/LegendCompanyLogo/index.jsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; -import {Wrapper, Image} from './style'; - -/** - * Renders a nicely styled and dynamically scaling header for the legend view which will display the company icon/logo - * as well as its stock code. - * - */ -class LegendCompanyLogo extends React.Component { - constructor(props) { - super(props) - this.state = {dimensions: {}}; - this.onImgLoad = this.onImgLoad.bind(this); - } - onImgLoad({target:img}) { - console.log(img) - this.setState({dimensions:{height:img.offsetHeight, - width:img.offsetWidth}}) - } - onComponentWillMount() { - console.log(this.props.logo) - } - render () { - console.log(this.state.dimensions) - return ( - - - - ) - } -} -export default LegendCompanyLogo; diff --git a/src/components/LegendCompanyLogo/style.js b/src/components/LegendCompanyLogo/style.js deleted file mode 100644 index 8dcebcd..0000000 --- a/src/components/LegendCompanyLogo/style.js +++ /dev/null @@ -1,17 +0,0 @@ -import styled from 'styled-components' - -export const Wrapper = styled.div` - display: flex; - flex-direction: column; - text-align: center; - vertical-align: middle; - justify-content: center; - grid-area: company-logo; -` -export const Image = styled.img` - /* width: 60px; - height: 60px; */ - margin-left: auto; - margin-right: auto; - display: block; -` diff --git a/src/components/LegendCompanyMarketCap/index.jsx b/src/components/LegendCompanyMarketCap/index.jsx deleted file mode 100644 index 897e194..0000000 --- a/src/components/LegendCompanyMarketCap/index.jsx +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react'; -import { - VictoryChart, - VictoryAxis, - VictoryLabel, - VictoryContainer, - VictoryLine, -} from 'victory'; -// import { LineChart, Line, ResponsiveContainer } from 'recharts'; -import {Wrapper, Header, Chart} from './style'; - -/** - * LegendCompanyMarketCap - * - */ -class LegendCompanyMarketCap extends React.Component { - constructor(props) { - super(props); - this.state = {}; - } - getTickValues() { - return [ - new Date(2002, 1, 1), - new Date(2017, 1, 1), - new Date(2018, 1, 1), - ]; - } - - render() { - // return ( - // - // - // - // - // - // - // - // ); - return ( - -
- -
- - }> - - new Date(x).getFullYear()} - /> - - -
- ); - } -} - -export default LegendCompanyMarketCap; diff --git a/src/components/LegendCompanyMarketCap/style.js b/src/components/LegendCompanyMarketCap/style.js deleted file mode 100644 index 1490f44..0000000 --- a/src/components/LegendCompanyMarketCap/style.js +++ /dev/null @@ -1,29 +0,0 @@ -import styled from 'styled-components'; - -export const Wrapper = styled.div` - grid-area: company-mc; - border-radius: 5px; - background: white; - display: grid; - height: 140px; - grid-template-rows: 20px 120px; - grid-template-areas: - 'header' - 'chart'; -`; -export const Header = styled.div` - grid-area: header; - padding-top: 3px; - display: flex; - flex-direction: column; - justify-content: center; - text-align: center; -`; -export const Chart = styled.div` - grid-area: chart; - display: flex; - flex-direction: column; - justify-content: center; - text-align: center; - height: 150px; -`; diff --git a/src/components/LegendCompanyPE/index.jsx b/src/components/LegendCompanyPE/index.jsx deleted file mode 100644 index 0e68926..0000000 --- a/src/components/LegendCompanyPE/index.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import {Wrapper, Header, PE} from './style'; - -/** - * Renders a nicely styled and dynamically scaling header for the legend view which will display the company icon/logo - * as well as its stock code. - * - */ -const LegendCompanyPE = props => ( - -
P/E ratio
- {props.pe} -
-); -export default LegendCompanyPE; diff --git a/src/components/LegendCompanyPE/style.js b/src/components/LegendCompanyPE/style.js deleted file mode 100644 index 72173d9..0000000 --- a/src/components/LegendCompanyPE/style.js +++ /dev/null @@ -1,31 +0,0 @@ -import styled from 'styled-components'; - -export const Wrapper = styled.div` - display: flex; - border-radius: 5px; - flex-direction: column; - text-align: center; - vertical-align: middle; - justify-content: center; - background: white; - grid-area: company-pe; -`; - -export const PE = styled.div` - flex-direction: column; - justify-content: center; - vertical-align: middle; - text-align: center; - font-size: 15px; - font-weight: 600; - font-family: Avenir Next, sans-serif; -`; -export const Header = styled.div` - flex-direction: column; - justify-content: center; - vertical-align: middle; - text-align: center; - font-size: 11px; - font-weight: bold; - font-family: Avenir Next, sans-serif; -`; diff --git a/src/components/MoversList/index.jsx b/src/components/MoversList/index.jsx deleted file mode 100644 index a1ab6bd..0000000 --- a/src/components/MoversList/index.jsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; -import Transition from 'react-transition-group/Transition'; -import ShortedAPI from '../../services/sapi/client'; -import MoversListRow from '../../components/MoversListRow'; -import {Wrapper, Header, More, duration, transitionStyles} from './style'; -/** - * Renders a list of TopShortsListRow components. These rows will display the stock code, stock name, and percentage shorted - * for the top say 20 stocks. With a "show more" button present at the bottom, taking them to a different view which will me dedicated to showing more short position - * information for more stocks (maybe top 100). Will perhaps a more verbose set of properties and graphics. - */ -class MoversList extends React.Component { - constructor(props) { - super(props); - this.state = { - inside: false, - }; - } - componentDidMount() { - this.toggleEnterState(); - } - - toggleEnterState() { - this.setState({inside: true}); - } - - render() { - const rows = this.props.data.data - .slice(0, 5) - .map(row_data => ( - - )); - return ( - - {state => { - return ( - -
Top Movers
- {rows} - show more -
- ); - }} -
- ); - } -} - -export default MoversList; diff --git a/src/components/MoversList/style.js b/src/components/MoversList/style.js deleted file mode 100644 index 40a8582..0000000 --- a/src/components/MoversList/style.js +++ /dev/null @@ -1,44 +0,0 @@ -import styled from 'styled-components'; - -export const duration = 500; -export const transitionStyles = { - entering: {opacity: 0, Ypos: 500}, - entered: {opacity: 1, Ypos: 0}, - exited: {opacity: 0}, -}; - -export const Wrapper = styled.div` - height: 100%; - grid-area: top-movers; - background: white; - display: flex; - flex-direction: column; - border-radius: 5px; - border: 1px solid #eee; - opacity: ${props => props.opacity}; - transition: ${props => `${props.duration}ms ease-in-out`}; - transition-property: opacity, transform; - transform: ${props => `translateY(${props.Ypos}px)`}; -`; -export const Header = styled.div` - height: 50px; - display: flex; - flex-direction: column; - text-align: center; - font-size: 22px; - font-weight: bold; - font-family: Avenir Next, sans-serif; -`; -export const More = styled.div` - height: 42px; - display: flex; - flex-direction: column; - text-align: center; - font-size: 22px; - font-weight: bold; - background: #dadada; - border-radius: 0 0 15px 15px; - margin: 4px; - justify-content: center; - font-family: Avenir Next, sans-serif; -`; diff --git a/src/components/MoversListRow/index.jsx b/src/components/MoversListRow/index.jsx deleted file mode 100644 index ee7e463..0000000 --- a/src/components/MoversListRow/index.jsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; - -import { - Wrapper, - Name, - Code, - Percentage, - IndicatorWrapper, - IndicatorUp, - IndicatorDown, - PercentageChanged, - PercentageCurrent, -} from './style'; - -/** - * Renders a given row in the alert & anomalies widget. - */ -const MoversListRow = props => ( - - -
{props.code}
-
- {props.name} - - {props.current}% - - {props.change}% - - - - {props.change > 0 ? : } - -
-); - -export default MoversListRow; diff --git a/src/components/MoversListRow/style.js b/src/components/MoversListRow/style.js deleted file mode 100644 index aee2e37..0000000 --- a/src/components/MoversListRow/style.js +++ /dev/null @@ -1,100 +0,0 @@ -import styled from 'styled-components'; - -export const Wrapper = styled.div` - display: grid; - grid-template-columns: 120px 60px 1fr; - grid-template-rows: repeat(2, 1fr); - grid-template-areas: - 'code percentage indicator' - 'code percentage indicator'; - margin: 2px; - margin-left: 7px; - margin-right: 7px; - height: 65px; - background: #e2e2e2; - padding-top: 4px; - padding-bottom: 4px; -`; - -export const Name = styled.div` - grid-area: name; - display: flex; - flex-direction: column; - justify-content: center; - vertical-align: middle; -`; - -export const Code = styled.div` - grid-area: code; - display: flex; - flex-direction: column; - justify-content: center; - vertical-align: middle; - .code { - background-color: gray; - width: 60px; - height: 45px; - display: flex; - flex-direction: column; - justify-content: center; - vertical-align: middle; - text-align: center; - margin-left: 5px; - } -`; -export const Percentage = styled.div` - grid-area: percentage; - display: grid; - display: block; - margin-right: 20px; - grid-template-rows: repeat(2, 1fr); - grid-gap: 1px; - grid-template-areas: - 'current' - 'changed'; -`; -export const PercentageCurrent = styled.div` - grid-area: current; - display: block; - float: right; - font-size: 21px; - font-weight: bold; - font-family: Avenir Next, sans-serif; -`; -export const PercentageChanged = styled.div` - grid-area: changed; - float: right; - display: block; - margin-left: 17px; - font-weight: bold; - font-family: Avenir Next, sans-serif; - color: ${props => (props.value > 0 ? `red` : `green`)}; - padding-bottom: 10px; -`; -export const IndicatorUp = styled.div` - width: 0; - height: 0; - border-left: 30px solid transparent; - border-right: 30px solid transparent; - border-bottom: 30px solid red; -`; -export const IndicatorDown = styled.div` - width: 0; - height: 0; - border-left: 30px solid transparent; - border-right: 30px solid transparent; - border-top: 30px solid green; -`; - -export const IndicatorWrapper = styled.div` - grid-area: indicator; - @media (min-width: 1650px) { - display: flex; - } - display: none; - flex-direction: column; - text-align: center; - vertical-align: middle; - justify-content: center; - margin-right: 10px; -`; diff --git a/src/components/ProfileAlerts/index.jsx b/src/components/ProfileAlerts/index.jsx deleted file mode 100644 index 53634b2..0000000 --- a/src/components/ProfileAlerts/index.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import {Wrapper} from './style'; - -/** - * Top Navbar responsible for rendering the basic site-map layout including: blog | about | disclaimer etc - * Will also manage the implementation of the navbar collapse on mobile devices i.e transition to burger and burger animation on open/close etc. - * TODO: - * * handle mobile compaction of navbar component - * - */ -class SearchBar extends React.Component { - constructor(props) { - super(props); - this.state = {}; - } - - render() { - return alerts go here; - } -} - -export default SearchBar; diff --git a/src/components/ProfileAlerts/style.js b/src/components/ProfileAlerts/style.js deleted file mode 100644 index 2e5f2a1..0000000 --- a/src/components/ProfileAlerts/style.js +++ /dev/null @@ -1,15 +0,0 @@ -import styled from 'styled-components'; - -export const Wrapper = styled.div` - grid-area: profile-alerts; - justify-content: center; - text-align: center; - vertical-align: middle; - display: flex; - flex-direction: column; - float: right; - margin-left: auto; - background: gray; - width: 100%; - height: 300px; -`; diff --git a/src/components/ProfileChart/index.jsx b/src/components/ProfileChart/index.jsx deleted file mode 100644 index e1a6bb4..0000000 --- a/src/components/ProfileChart/index.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import {Wrapper} from './style'; - -/** - * Top Navbar responsible for rendering the basic site-map layout including: blog | about | disclaimer etc - * Will also manage the implementation of the navbar collapse on mobile devices i.e transition to burger and burger animation on open/close etc. - * TODO: - * * handle mobile compaction of navbar component - * - */ -class SearchBar extends React.Component { - constructor(props) { - super(props); - this.state = {}; - } - - render() { - return chart goes here; - } -} - -export default SearchBar; diff --git a/src/components/ProfileChart/style.js b/src/components/ProfileChart/style.js deleted file mode 100644 index 4f11886..0000000 --- a/src/components/ProfileChart/style.js +++ /dev/null @@ -1,14 +0,0 @@ -import styled from 'styled-components'; - -export const Wrapper = styled.div` - grid-area: profile-chart; - justify-content: center; - text-align: center; - vertical-align: middle; - display: flex; - flex-direction: column; - float: right; - margin-left: auto; - background: gray; - width: 800px; -`; diff --git a/src/components/ProfileHeader/index.jsx b/src/components/ProfileHeader/index.jsx deleted file mode 100644 index 96a42c5..0000000 --- a/src/components/ProfileHeader/index.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import {Wrapper} from './style'; - -/** - * Top Navbar responsible for rendering the basic site-map layout including: blog | about | disclaimer etc - * Will also manage the implementation of the navbar collapse on mobile devices i.e transition to burger and burger animation on open/close etc. - * TODO: - * * handle mobile compaction of navbar component - * - */ -class SearchBar extends React.Component { - constructor(props) { - super(props); - this.state = {}; - } - - render() { - return {this.props.metadata.code}; - } -} - -export default SearchBar; diff --git a/src/components/ProfileHeader/style.js b/src/components/ProfileHeader/style.js deleted file mode 100644 index 3159dc8..0000000 --- a/src/components/ProfileHeader/style.js +++ /dev/null @@ -1,14 +0,0 @@ -import styled from 'styled-components'; - -export const Wrapper = styled.div` - grid-area: profile-header; - justify-content: center; - text-align: center; - vertical-align: middle; - display: flex; - flex-direction: column; - float: right; - margin-left: auto; - background: gray; - width: 100%; -`; diff --git a/src/components/ProfileSidePanel/index.jsx b/src/components/ProfileSidePanel/index.jsx deleted file mode 100644 index b6a3c76..0000000 --- a/src/components/ProfileSidePanel/index.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import {Wrapper} from './style'; - -/** - * Top Navbar responsible for rendering the basic site-map layout including: blog | about | disclaimer etc - * Will also manage the implementation of the navbar collapse on mobile devices i.e transition to burger and burger animation on open/close etc. - * TODO: - * * handle mobile compaction of navbar component - * - */ -class SearchBar extends React.Component { - constructor(props) { - super(props); - this.state = {}; - } - - render() { - return profile side panel; - } -} - -export default SearchBar; diff --git a/src/components/ProfileSidePanel/style.js b/src/components/ProfileSidePanel/style.js deleted file mode 100644 index 49eef09..0000000 --- a/src/components/ProfileSidePanel/style.js +++ /dev/null @@ -1,15 +0,0 @@ -import styled from 'styled-components'; - -export const Wrapper = styled.div` - grid-area: profile-side-panel; - justify-content: center; - text-align: center; - vertical-align: middle; - - display: flex; - flex-direction: column; - float: right; - margin-left: auto; - background: gray; - width: 100%; -`; diff --git a/src/components/TopChartVictory/components.jsx b/src/components/TopChartVictory/components.jsx deleted file mode 100644 index 7aca1f0..0000000 --- a/src/components/TopChartVictory/components.jsx +++ /dev/null @@ -1,58 +0,0 @@ -import React from 'react'; -import {TooltipWrapper} from './style'; -import {Sector} from 'recharts'; - -export class TopChartTooltip extends React.Component { - render() { - const {x, y, dx, dy, selectedCode, datum} = this.props; - const orientation = y <= 100 ? 'bottom' : 'top'; - const windowWidth = 400; - const p1_x = x <= windowWidth / 2 ? x + 10 : x - 10; - const p1_y = orientation === 'top' ? y - 10 : y + 10; - const p2_x = p1_x; - const p2_y = orientation === 'top' ? p1_y - 10 : p1_y + 10; - return selectedCode ? ( - - - - - - - - - {this.props.datum.date} - - - {`${selectedCode}: `} - - {`${this.props.datum[selectedCode]}`}% - - - - - ) : ( - - ); - } -} diff --git a/src/components/TopChartVictory/index.jsx b/src/components/TopChartVictory/index.jsx deleted file mode 100644 index 75b9f9f..0000000 --- a/src/components/TopChartVictory/index.jsx +++ /dev/null @@ -1,219 +0,0 @@ -import React from 'react'; -import { - VictoryChart, - VictoryAxis, - VictoryLabel, - VictoryTooltip, - VictoryLine, - VictoryArea, - VictoryVoronoiContainer, -} from 'victory'; -import Transition from 'react-transition-group/Transition'; -import { - duration, - transitionStyles, - Wrapper, - PickerWrapper, - ChartWrapper, - OptionsWrapper, - colors700, - colors300, -} from './style'; -import {TopChartTooltip} from './components'; -/** - * Chart - * Component responsible for rendering the page1 graphic displaying the top short positions - * TODO: - * * add more styling to graph currently nothing present - * * add more intelligent x-axis ticks as windowpicker is changed - * * refactor to use victory charts - * * multiple display modes such as candleStick etc. - * * implement brush and zoom - * * implement select to zoom - * * implement hover filtering/hiding other lines - * - * - */ -class TopChartVictory extends React.Component { - constructor(props) { - super(props); - this.state = { - inside: false, - windowWidth: 1700, - }; - this.handleWindowSizeChange = this.handleWindowSizeChange.bind(this); - } - componentWillUnmount() { - typeof window !== 'undefined' && - window.removeEventListener('resize', this.handleWindowSizeChange); - } - handleWindowSizeChange() { - typeof window !== 'undefined' && - setTimeout(this.setState({windowWidth: window.innerWidth})); - } - componentDidMount() { - typeof window !== 'undefined' && - setTimeout( - window.addEventListener('resize', this.handleWindowSizeChange), - ); - this.toggleEnterState(); - } - - toggleEnterState() { - this.setState({inside: true}); - } - handleVoronoiSelect(points, props) { - if (points[0]) { - console.log('voronio snapped to', points[0].childName) - this.props.onSelectCode(points[0].childName) - } - } - handleLineHover(e, key) { - console.log('new line slected:', key); - this.props.onSelectCode(key); - } - handleLineExit(e, key) { - console.log('exiting line', key); - } - - render() { - const {data, selectedCode} = this.props; - var lines = null; - if (this.props.mode === 'NORMAL') { - lines = data.dataKeys.map((key, index) => ( - } - data={data.data} - x="date" - y={key} - events={[ - { - childName: key, - target: 'data', - eventHandlers: { - onMouseOver: e => this.handleLineHover(e, key), - onMouseOut: e => this.handleLineExit(e, key), - }, - }, - ]} - style={{ - data: { - stroke: colors700[index], - strokeOpacity: - key === selectedCode || !selectedCode ? 1 : 0.2, - strokeWidth: 2, - }, - }} - /> - )); - } else if (this.props.mode === 'AREA') { - lines = data.dataKeys.map((key, index) => ( - this.handleLineHover(e, key), - }, - }, - ]} - style={{ - data: { - stroke: colors700[index], - fill: colors700[index], - fillOpacity: 0.3, - strokeWidth: 2, - }, - }} - /> - )); - } - return ( - - {state => { - return ( - - {this.props.picker} - - {this.props.options} - - - ``} - onActivated={(points,props) => this.handleVoronoiSelect(points,props)} - labelComponent={ - - } - cornerRadius={0} - flyoutStyle={{ - fill: 'white', - }} - /> - } - /> - }> - {lines} - - `${Math.round(t)}`} - tickLabelComponent={} - style={{ - ticks: {stroke: 'grey', size: 5}, - tickLabels: { - fontSize: 7, - padding: 4, - }, - }} - /> - - - - ); - }} - - ); - } -} - -export default TopChartVictory; diff --git a/src/components/TopChartVictory/style.js b/src/components/TopChartVictory/style.js deleted file mode 100644 index aa33bc8..0000000 --- a/src/components/TopChartVictory/style.js +++ /dev/null @@ -1,96 +0,0 @@ -import styled from 'styled-components'; -export const duration = 500; -export const transitionStyles = { - entering: {opacity: 0, Ypos: 500}, - entered: {opacity: 1, Ypos: 0}, - exited: {opacity: 0}, -}; - -export const TooltipWrapper = styled.svg` - .header { - font-size: 12px; - font-weight: bold; - } - .issuerCode { - font-size: 12px; - } - .value { - font-size: 12px; - } - .tooltip-card { - fill: #e1e1e1; - stroke: 1px solid #282626; - border: 1px solid #282626; - border-radius: 30px; - } -`; -export const Wrapper = styled.div` - grid-area: top-graph; - display: grid; - grid-template-rows: 30px 1fr; - grid-template-columns: repeat(3, 1fr); - grid-template-areas: - 'none picker options' - 'chart chart chart' - 'chart chart chart'; - opacity: ${props => props.opacity}; - transition: ${props => `${props.duration}ms ease-in-out`}; - transition-property: opacity, transform; - transform: ${props => `translateY(${props.Ypos}px)`}; - max-width: 1600px; - background-color: white; - border-radius: 5px; - border: 1px solid #eee; -`; -export const ChartWrapper = styled.div` - grid-area: chart; - display: flex; - flex: 1 1 auto; - height: calc(99%); - min-height: 0; -`; -export const PickerWrapper = styled.div` - grid-area: picker; - display: flex; - vertical-align: middle; - justify-content: center; -`; -export const OptionsWrapper = styled.div` - grid-area: options; - display: flex; - justify-content: flex-end; -`; -export const intervals = { - d: 2, - w: 7, - m: 40, - y: 100, -}; -export const colors700 = [ - '#D32F2F', - '#303F9F', - '#00796B', - '#388E3C', - '#388E3C', - '#F57C00', - '#455A64', - '#5D4037', - '#C2185B', - '#7B1FA2', - '#00796B', - '#7B1FA2', - '#C2185B', -]; -export const colors300 = [ - '#E57373', - '#F06292', - '#7986CB', - '#9575CD', - '#7986CB', - '#64B5F6', - '#F06292', - '#E57373', - '#FFB74D', - '#BCAAA4', - '#FFF176', -]; diff --git a/src/components/TopShortsList/index.jsx b/src/components/TopShortsList/index.jsx deleted file mode 100644 index 9ffbc00..0000000 --- a/src/components/TopShortsList/index.jsx +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react'; -import Transition from 'react-transition-group/Transition'; -import TopShortsListRow from '../../components/TopShortsListRow'; -import {Wrapper, Header, More, duration, transitionStyles} from './style'; -/** - * Renders a list of TopShortsListRow components. These rows will display the stock code, stock name, and percentage shorted - * for the top say 20 stocks. With a "show more" button present at the bottom, taking them to a different view which will me dedicated to showing more short position - * information for more stocks (maybe top 100). Will perhaps a more verbose set of properties and graphics. - * TODO: - * * load profile on select of a specific stock in list - */ -class TopShortsList extends React.Component { - constructor(props) { - super(props); - this.state = { - inside: false, - hovered: false, - }; - } - componentDidMount() { - this.toggleEnterState(); - } - - toggleEnterState() { - this.setState({inside: true}); - } - handleHover(value) { - this.setState({hovered: value}); - } - handleMouseLeave() { - this.setState({ - hovered: false, - }); - } - - render() { - const rows = this.props.data.map(row_data => ( - this.handleHover(row_data.code)} - key={row_data.code} - {...row_data} - /> - )); - return ( - - {state => { - return ( - this.handleMouseLeave()} - duration={duration} - {...transitionStyles[state]}> -
Top Short List
- {rows} - show more -
- ); - }} -
- ); - } -} - -export default TopShortsList; diff --git a/src/components/TopShortsList/style.js b/src/components/TopShortsList/style.js deleted file mode 100644 index 97c3f1e..0000000 --- a/src/components/TopShortsList/style.js +++ /dev/null @@ -1,39 +0,0 @@ -import styled from 'styled-components' - -export const duration = 500 -export const transitionStyles = { - entering: { opacity: 0, Ypos: 500 }, - entered: { opacity: 1, Ypos: 0 }, - exited: { opacity: 0 } -} - -export const Wrapper = styled.div` - grid-area: top-list; - background: white; - display: flex; - flex-direction: column; - border-radius: 5px; - border: 1px solid #eee; - opacity: ${props => props.opacity}; - transition: ${props => `${props.duration}ms ease-in-out`}; - transition-property: opacity, transform; - transform: ${props => `translateY(${props.Ypos}px)`}; -` -export const Header = styled.div` - height: 30px; - text-align: center; - font-size: 22px; - font-weight: bold; - font-family: Avenir Next, sans-serif; -` -export const More = styled.div` - height: 30px; - text-align: center; - font-size: 22px; - font-weight: bold; - background: #dadada; - border-radius: 0 0 15px 15px; - margin: 4px; - justify-content: center; - font-family: Avenir Next, sans-serif; -` diff --git a/src/components/TopShortsListRow/index.jsx b/src/components/TopShortsListRow/index.jsx deleted file mode 100644 index d991c39..0000000 --- a/src/components/TopShortsListRow/index.jsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; - -import {Wrapper, WrapperHovered, Code, Name, Percent} from './style'; - -/** - * Renders a specfiic row that is contained within the TopSHortList view. This will show - * the current short percentage of a given stock within the top x number of short positions - * takes a data prop which contains the following payload: - * data = { - * name: "JBHIFI LIMITED", - * code: "JBH", - * percentage: 19.6 - * } - */ - -const TopShortListRow = props => { - return props.isHovered ? ( - - -
{props.code}
-
- {props.name} - -
{props.current}%
-
-
- ) : ( - - -
{props.code}
-
- {props.name} - -
{props.current}%
-
-
- ); -}; - -export default TopShortListRow; diff --git a/src/components/TopShortsListRow/style.js b/src/components/TopShortsListRow/style.js deleted file mode 100644 index 630ee76..0000000 --- a/src/components/TopShortsListRow/style.js +++ /dev/null @@ -1,113 +0,0 @@ -import styled from 'styled-components'; - -export const Wrapper = styled.a` - display: grid; - color: black; - text-decoration: none !important; - @media (min-width: 901px) { - grid-template-columns: repeat(5, 1fr); - grid-template-areas: 'code name name name percentage'; - } - @media (max-width: 900px) { - grid-template-columns: repeat(2, 1fr); - grid-template-areas: 'code percentage'; - } - margin: 4px; - margin-left: 7px; - margin-right: 4px; - height: 50px; - background: #dadada; - border-radius: 0 30px 30px 0; - padding-top: 3px; - padding-bottom: 4px; - margin-bottom: 4px; - &:hover, - &:visited, - &:link, - &:active { - text-decoration: none !important; - color: black; - } -`; -export const WrapperHovered = styled.a` - display: grid; - color: black; - text-decoration: none !important; - z-index: 10; - grid-template-columns: repeat(5, 1fr); - grid-template-areas: 'code name name name percentage'; - margin: 4px; - margin-left: 7px; - margin-right: 4px; - height: 50px; - background: #dadada; - border-radius: 0 30px 30px 0; - padding-top: 4px; - padding-bottom: 4px; - margin-bottom: 6px; - -webkit-box-shadow: -3px 4px 7px 0px rgba(0, 0, 0, 0.25); - -moz-box-shadow: -3px 4px 7px 0px rgba(0, 0, 0, 0.25); - box-shadow: -3px 4px 7px 0px rgba(0, 0, 0, 0.25); - &:hover, - &:visited, - &:link, - &:active { - text-decoration: none !important; - color: black; - } -`; - -export const Name = styled.div` - grid-area: name; - @media (max-width: 1300px) { - display: none; - } - display: flex; - flex-direction: column; - justify-content: center; - vertical-align: middle; - font-size: 14px; -`; - -export const Code = styled.div` - grid-area: code; - display: flex; - flex-direction: column; - justify-content: center; - - .code { - width: 60px; - height: 45px; - display: flex; - flex-direction: column; - justify-content: center; - vertical-align: middle; - text-align: center; - font-size: 21px; - font-weight: bold; - } -`; - -export const Percent = styled.div` - grid-area: percentage; - margin-left: auto; - padding-right: 5px; - display: flex; - flex-direction: column; - justify-content: center; - vertical-align: middle; - - .circle { - background: #f98080; - height: 40px; - width: 40px; - border-radius: 50px; - display: flex; - flex-direction: column; - justify-content: center; - vertical-align: middle; - text-align: center; - font-size: 10px; - font-weight: 400; - } -`; diff --git a/src/components/WindowPicker/index.jsx b/src/components/WindowPicker/index.jsx deleted file mode 100644 index 41741af..0000000 --- a/src/components/WindowPicker/index.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; -import {Wrapper} from './style'; -import Button from '../Button'; - -/** - * WindowPicker - * Responsible for providing a time window selector across a range of values including 1d, 1w, 1m, 1y etc. - * will set the container state which will adjust the query. This will be set by a uni-directional handler passed - * down as a prop. - * - */ - -class WindowPicker extends React.Component { - render() { - const {options, selectedOption, onSelect, theme} = this.props; - const buttons = options.values.map(value => ( - + +); + +export default UnAuthorized; diff --git a/src/components/UnAuthorized/style.js b/src/components/UnAuthorized/style.js new file mode 100644 index 0000000..e7dcf93 --- /dev/null +++ b/src/components/UnAuthorized/style.js @@ -0,0 +1,23 @@ +import styled from 'styled-components'; + +export const Wrapper = styled.div` + width: 100%; + height: 300px; + background: white; + display: flex; + flex-direction: column; + justify-content: center; + align-content: center; + vertical-align: middle; +`; + +export const Message = styled.div` + color: black; + align-self: center; +` +export const Button = styled.button` + color: black; + width: 100px; + align-self: center; + +` diff --git a/src/views/aboutpage/components/AboutHeader/index.jsx b/src/views/aboutpage/components/AboutHeader/index.jsx new file mode 100644 index 0000000..b59ef60 --- /dev/null +++ b/src/views/aboutpage/components/AboutHeader/index.jsx @@ -0,0 +1,76 @@ +import React from 'react'; +import Transition from 'react-transition-group/Transition'; +import AboutHeaderHero from '../../../../assets/images/about-main.svg'; +import { + HeroWrapper, + HeroButtonWrapper, + HeroTitleWrapper, + duration, + transitionStyles, + Wrapper, +} from './style'; + +/** + * Top Navbar responsible for rendering the basic site-map layout including: blog | about | disclaimer etc + * Will also manage the implementation of the navbar collapse on mobile devices i.e transition to burger and burger animation on open/close etc. + * TODO: + * * handle mobile compaction of navbar component + * + */ +class AboutHeader extends React.Component { + constructor(props) { + super(props); + this.state = { + inside: false, + }; + } + componentDidMount() { + this.toggleEnterState(); + } + + toggleEnterState() { + this.setState({inside: true}); + } + + render() { + return ( + + {state => { + return ( + + + shorted-hero + + +
+
+ Hello, we are +
+
Shorted
+
+
+ Bringing ASX market short positions to your + front door, with added insights and + intelligence. +
+
+ + Dashboard + +
+
+
+ ); + }} +
+ ); + } +} + +export default AboutHeader; diff --git a/src/views/aboutpage/components/AboutHeader/style.js b/src/views/aboutpage/components/AboutHeader/style.js new file mode 100644 index 0000000..a252210 --- /dev/null +++ b/src/views/aboutpage/components/AboutHeader/style.js @@ -0,0 +1,114 @@ +import styled from 'styled-components'; + +export const duration = 500; +export const transitionStyles = { + entering: {opacity: 0, Ypos: -100}, + entered: {opacity: 1, Ypos: 0}, + exited: {opacity: 0}, +}; + +export const HeroButtonWrapper = styled.a` + grid-area: hero-button; + width: 210px; + height: 80px; + display: flex; + text-decoration: none; + justify-content: center; /* align horizontal */ + align-items: center; /* align vertical */ + margin: 5px; + border-radius: 30px; + color: white; + background: #282fc3; + outline: 0; + font-size: 30px; + font-family: 'Avenir Next,Segoe UI', Helvetica, Arial, sans-serif, + 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; + font-weight: bold; + background: linear-gradient(-135deg, #827d9f, #0f0646); + &:hover { + -webkit-box-shadow: -3px 4px 7px 0px rgba(0, 0, 0, 0.75); + -moz-box-shadow: -3px 4px 7px 0px rgba(0, 0, 0, 0.75); + box-shadow: -3px 4px 7px 0px rgba(0, 0, 0, 0.75); + color: #eee; + text-decoration: none !important; + height: 80px; + transition: height 0.66s ease-out; + margin-bottom: 5px; + } +`; + +export const Wrapper = styled.div` + display: grid; + grid-gap: 20px; + grid-template-columns: 1fr 2fr; + grid-template-areas: 'hero-title hero-image'; + margin-right: 100px; + margin-left: 100px; + margin-top: 250px; + opacity: ${props => props.opacity}; + transition: ${props => `${props.duration}ms ease-in-out`}; + transition-property: opacity, transform; + transform: ${props => `translateY(${props.Ypos}px)`}; +`; + +export const HeroWrapper = styled.div` + grid-area: hero-image; + width: 100%; +`; +export const HeroTitleWrapper = styled.div` + grid-area: hero-title; + display: grid; + grid-template-columns: 1fr; + grid-template-rows: 3fr 1fr 1fr; + grid-template-areas: + 'hero-title' + 'hero-description' + 'todo' + 'hero-button'; + .hero-title { + margin-top: 50%; + display: flex; + flex-direction: column; + .header-1 { + display: flex; + flex-direction: column; + text-align: center; + justify-content: center; + vertical-align: middle; + grid-area: hero-title; + float: left; + font-size: 70px; + font-weight: bold; + font-family: Avenir Next, sans-serif; + } + .header-2 { + display: flex; + flex-direction: column; + text-align: center; + justify-content: center; + vertical-align: middle; + grid-area: hero-title; + font-size: 75px; + font-family: Avenir Next, sans-serif; + font-weight: bold; + color: #282fc3; + float: left; + } + } + .hero-description { + display: flex; + flex-direction: column; + grid-area: hero-description; + font-weight: 300; + font-size: 30px; + font-family: Avenir Next, sans-serif; + } + .hero-button { + grid-area: hero-button; + display: flex; + flex-direction: column; + justify-content: center; + vertical-align: middle; + height: 70px; + } +`; diff --git a/src/views/stockprofile/components/ProfileAlerts/index.jsx b/src/views/stockprofile/components/ProfileAlerts/index.jsx new file mode 100644 index 0000000..53634b2 --- /dev/null +++ b/src/views/stockprofile/components/ProfileAlerts/index.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import {Wrapper} from './style'; + +/** + * Top Navbar responsible for rendering the basic site-map layout including: blog | about | disclaimer etc + * Will also manage the implementation of the navbar collapse on mobile devices i.e transition to burger and burger animation on open/close etc. + * TODO: + * * handle mobile compaction of navbar component + * + */ +class SearchBar extends React.Component { + constructor(props) { + super(props); + this.state = {}; + } + + render() { + return alerts go here; + } +} + +export default SearchBar; diff --git a/src/views/stockprofile/components/ProfileAlerts/style.js b/src/views/stockprofile/components/ProfileAlerts/style.js new file mode 100644 index 0000000..2e5f2a1 --- /dev/null +++ b/src/views/stockprofile/components/ProfileAlerts/style.js @@ -0,0 +1,15 @@ +import styled from 'styled-components'; + +export const Wrapper = styled.div` + grid-area: profile-alerts; + justify-content: center; + text-align: center; + vertical-align: middle; + display: flex; + flex-direction: column; + float: right; + margin-left: auto; + background: gray; + width: 100%; + height: 300px; +`; diff --git a/src/views/stockprofile/components/ProfileChart/index.jsx b/src/views/stockprofile/components/ProfileChart/index.jsx new file mode 100644 index 0000000..e1a6bb4 --- /dev/null +++ b/src/views/stockprofile/components/ProfileChart/index.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import {Wrapper} from './style'; + +/** + * Top Navbar responsible for rendering the basic site-map layout including: blog | about | disclaimer etc + * Will also manage the implementation of the navbar collapse on mobile devices i.e transition to burger and burger animation on open/close etc. + * TODO: + * * handle mobile compaction of navbar component + * + */ +class SearchBar extends React.Component { + constructor(props) { + super(props); + this.state = {}; + } + + render() { + return chart goes here; + } +} + +export default SearchBar; diff --git a/src/views/stockprofile/components/ProfileChart/style.js b/src/views/stockprofile/components/ProfileChart/style.js new file mode 100644 index 0000000..4f11886 --- /dev/null +++ b/src/views/stockprofile/components/ProfileChart/style.js @@ -0,0 +1,14 @@ +import styled from 'styled-components'; + +export const Wrapper = styled.div` + grid-area: profile-chart; + justify-content: center; + text-align: center; + vertical-align: middle; + display: flex; + flex-direction: column; + float: right; + margin-left: auto; + background: gray; + width: 800px; +`; diff --git a/src/views/stockprofile/components/ProfileHeader/index.jsx b/src/views/stockprofile/components/ProfileHeader/index.jsx new file mode 100644 index 0000000..96a42c5 --- /dev/null +++ b/src/views/stockprofile/components/ProfileHeader/index.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import {Wrapper} from './style'; + +/** + * Top Navbar responsible for rendering the basic site-map layout including: blog | about | disclaimer etc + * Will also manage the implementation of the navbar collapse on mobile devices i.e transition to burger and burger animation on open/close etc. + * TODO: + * * handle mobile compaction of navbar component + * + */ +class SearchBar extends React.Component { + constructor(props) { + super(props); + this.state = {}; + } + + render() { + return {this.props.metadata.code}; + } +} + +export default SearchBar; diff --git a/src/views/stockprofile/components/ProfileHeader/style.js b/src/views/stockprofile/components/ProfileHeader/style.js new file mode 100644 index 0000000..3159dc8 --- /dev/null +++ b/src/views/stockprofile/components/ProfileHeader/style.js @@ -0,0 +1,14 @@ +import styled from 'styled-components'; + +export const Wrapper = styled.div` + grid-area: profile-header; + justify-content: center; + text-align: center; + vertical-align: middle; + display: flex; + flex-direction: column; + float: right; + margin-left: auto; + background: gray; + width: 100%; +`; diff --git a/src/views/stockprofile/components/ProfileSidePanel/index.jsx b/src/views/stockprofile/components/ProfileSidePanel/index.jsx new file mode 100644 index 0000000..b6a3c76 --- /dev/null +++ b/src/views/stockprofile/components/ProfileSidePanel/index.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import {Wrapper} from './style'; + +/** + * Top Navbar responsible for rendering the basic site-map layout including: blog | about | disclaimer etc + * Will also manage the implementation of the navbar collapse on mobile devices i.e transition to burger and burger animation on open/close etc. + * TODO: + * * handle mobile compaction of navbar component + * + */ +class SearchBar extends React.Component { + constructor(props) { + super(props); + this.state = {}; + } + + render() { + return profile side panel; + } +} + +export default SearchBar; diff --git a/src/views/stockprofile/components/ProfileSidePanel/style.js b/src/views/stockprofile/components/ProfileSidePanel/style.js new file mode 100644 index 0000000..49eef09 --- /dev/null +++ b/src/views/stockprofile/components/ProfileSidePanel/style.js @@ -0,0 +1,15 @@ +import styled from 'styled-components'; + +export const Wrapper = styled.div` + grid-area: profile-side-panel; + justify-content: center; + text-align: center; + vertical-align: middle; + + display: flex; + flex-direction: column; + float: right; + margin-left: auto; + background: gray; + width: 100%; +`; diff --git a/src/views/summary/components/AlertRow/index.jsx b/src/views/summary/components/AlertRow/index.jsx new file mode 100644 index 0000000..fee1503 --- /dev/null +++ b/src/views/summary/components/AlertRow/index.jsx @@ -0,0 +1,38 @@ +import React from 'react'; +import AlertRowGraph from '../../components/AlertRowGraph'; +import { + Wrapper, + Name, + Code, + Percentage, + IndicatorWrapper, + IndicatorUp, + IndicatorDown, + PercentageChanged, + PercentageCurrent, + More, +} from './style'; + +/** + * Renders a given row in the alert & anomalies widget. + */ +const AlertRow = props => ( + + +
{props.code}
+
+ {props.name} + + {props.current}% + + {props.changed}% + + + + {props.changed > 0 ? : } + + +
+); + +export default AlertRow; diff --git a/src/views/summary/components/AlertRow/style.js b/src/views/summary/components/AlertRow/style.js new file mode 100644 index 0000000..618548e --- /dev/null +++ b/src/views/summary/components/AlertRow/style.js @@ -0,0 +1,113 @@ +import styled from 'styled-components'; + +export const Wrapper = styled.div` + display: grid; + @media only screen and (min-width: 1024px) { + grid-template-columns: 90px 1fr 1fr 1fr 1fr; + grid-template-rows: repeat(2, 1fr); + grid-template-areas: + 'code name name percentage indicator graph' + 'code name name percentage indicator graph'; + } + @media only screen and (min-width: 1280px) { + grid-template-columns: 90px 1fr 70px 1fr 1fr; + grid-template-rows: repeat(2, 1fr); + grid-template-areas: + 'code name percentage indicator graph' + 'code name percentage indicator graph'; + }; + + margin: 6px; + margin-left: 7px; + margin-right: 7px; + height: 5vw; + background: #e2e2e2; + padding-top: 4px; + padding-bottom: 4px; +`; + +export const Name = styled.div` + grid-area: name; + display: flex; + font-size: 16px; + flex-direction: column; + justify-content: center; + vertical-align: middle; + font-family: Avenir Next, sans-serif; +`; + +export const Code = styled.div` + grid-area: code; + display: flex; + flex-direction: column; + justify-content: center; + vertical-align: middle; + padding-left: 10px; + .code { + background-color: gray; + width: 60px; + height: 45px; + display: flex; + font-size: 21px; + + font-family: Avenir Next, sans-serif; + flex-direction: column; + justify-content: center; + vertical-align: middle; + text-align: center; + margin-left: 5px; + } +`; +export const Percentage = styled.div` + grid-area: percentage; + display: grid; + grid-template-rows: repeat(2, 1fr); + grid-gap: 1px; + grid-template-areas: + 'current' + 'changed'; +`; +export const PercentageCurrent = styled.div` + grid-area: current; + font-size: 1.9vw; + font-weight: bold; + font-family: Avenir Next, sans-serif; +`; +export const PercentageChanged = styled.div` + grid-area: changed; + margin-left: 40px; + font-weight: bold; + font-size: 1.2vw; + font-family: Avenir Next, sans-serif; + color: ${props => (props.value > 0 ? `red` : `green`)}; + padding-bottom: 5px; +`; +export const IndicatorUp = styled.div` + width: 0; + height: 0; + border-left: 30px solid transparent; + border-right: 30px solid transparent; + border-bottom: 30px solid red; +`; +export const IndicatorDown = styled.div` + width: 0; + height: 0; + border-left: 30px solid transparent; + border-right: 30px solid transparent; + border-top: 30px solid green; +`; + +export const IndicatorWrapper = styled.div` + grid-area: indicator; + @media (min-width: 1024px) { + display: grid; + } + display: none; + flex-direction: column; + text-align: center; + vertical-align: middle; + justify-content: center; + margin: auto; + padding: auto; +`; + diff --git a/src/views/summary/components/AlertRowGraph/index.jsx b/src/views/summary/components/AlertRowGraph/index.jsx new file mode 100644 index 0000000..5498d3a --- /dev/null +++ b/src/views/summary/components/AlertRowGraph/index.jsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { + VictoryChart, + VictoryAxis, + VictoryArea, + VictoryContainer, +} from 'victory'; +// import { LineChart, Line, ResponsiveContainer } from 'recharts'; +import {Wrapper} from './style'; + +/** + * LegendCompanyMarketCap + * + */ +class AlertRowGraph extends React.Component { + constructor(props) { + super(props); + this.state = {}; + } + getTickValues() { + return [ + new Date(2002, 1, 1), + new Date(2017, 1, 1), + new Date(2018, 1, 1), + ]; + } + + render() { + return ( + + } + > + + ''} + style={{axis: {stroke: 'none'}}} + /> + + + ); + } +} + +export default AlertRowGraph; diff --git a/src/views/summary/components/AlertRowGraph/style.js b/src/views/summary/components/AlertRowGraph/style.js new file mode 100644 index 0000000..ad16aaa --- /dev/null +++ b/src/views/summary/components/AlertRowGraph/style.js @@ -0,0 +1,18 @@ +import styled from 'styled-components'; + +export const Wrapper = styled.div` + grid-area: graph; + margin: 5px; + max-width: 245px; + @media (min-width: 1280px) { + display: flex; + } + display: none; + margin-right: 10px; + flex-direction: column; + text-align: center; + justify-content: center; + -webkit-box-shadow: 1px 1px 2px 0px rgba(181, 181, 181, 1); + -moz-box-shadow: 1px 1px 2px 0px rgba(181, 181, 181, 1); + box-shadow: 1px 1px 2px 0px rgba(181, 181, 181, 1); +`; diff --git a/src/views/summary/components/Alerts/index.jsx b/src/views/summary/components/Alerts/index.jsx new file mode 100644 index 0000000..83e3129 --- /dev/null +++ b/src/views/summary/components/Alerts/index.jsx @@ -0,0 +1,47 @@ +import React from 'react'; +import Transition from 'react-transition-group/Transition'; +import AlertRow from '../AlertRow'; +import {duration, transitionStyles, Wrapper, Header, More, Rows} from './style'; +/** + * Responsible for the rendering/display of "alerts" which represent anomalous changes in short positions for a given stock. + */ +class Alerts extends React.Component { + constructor(props) { + super(props); + this.state = { + inside: false, + }; + } + componentDidMount() { + this.toggleEnterState(); + } + + toggleEnterState() { + this.setState({inside: true}); + } + + render() { + const alerts = this.props.data.map(alert => ( + + )); + return ( + + {state => { + return ( + +
Alerts & Anomalies
+ + {alerts} + + Show More +
+ ); + }} +
+ ); + } +} + +export default Alerts; diff --git a/src/views/summary/components/Alerts/style.js b/src/views/summary/components/Alerts/style.js new file mode 100644 index 0000000..6f4c684 --- /dev/null +++ b/src/views/summary/components/Alerts/style.js @@ -0,0 +1,52 @@ +import styled from 'styled-components' + +export const duration = 500 +export const transitionStyles = { + entering: { opacity: 0, Ypos: 500 }, + entered: { opacity: 1, Ypos: 0 }, + exited: { opacity: 0 } +} +export const Wrapper = styled.div` + border-radius: 5px; + border: 1px solid #eee; + opacity: ${props => props.opacity}; + transition: ${props => `${props.duration}ms ease-in-out`}; + transition-property: opacity, transform; + transform: ${props => `translateY(${props.Ypos}px)`}; + background: white; + display: grid; + grid-template-rows: 60px 1fr 60px; + grid-template-areas: + 'header' + 'rows' + 'more'; + float: left; + vertical-align: middle; + flex-direction: column; + text-align: center; + grid-area: top-alerts; +`; +export const Header = styled.div` + grid-area: header; + font-size: 30px; + font-family: Avenir Next,sans-serif; + font-weight: bold; +` +export const More = styled.div` + grid-area: more; + height: 50px; + display: flex; + flex-direction: column; + text-align: center; + font-size: 35px; + font-weight: bold; + background: #dadada; + border-radius: 0 0 15px 15px; + margin: 4px; + justify-content: center; + font-family: Avenir Next, sans-serif; +` +export const Rows = styled.div` + grid-area: rows; + height: 100%; +` diff --git a/src/views/summary/components/ChartOptions/index.jsx b/src/views/summary/components/ChartOptions/index.jsx new file mode 100644 index 0000000..2018a74 --- /dev/null +++ b/src/views/summary/components/ChartOptions/index.jsx @@ -0,0 +1,98 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import {Radio, Icon} from 'antd'; +// TODO: look at how antd style sheets can be inherently added into project without imports, plugin may be doing this +import 'antd/dist/antd.css'; +import { + Button, + OptionsWrapper, + OptionHeader, + Wrapper, + radioStyle, + buttonStyle, +} from './style'; + +const RadioButton = Radio.Button; +const RadioGroup = Radio.Group; + +const Options = props => ( + + Chart display +
+ props.onOptionsChange(e.target.value)} + defaultValue="NORMAL" + buttonStyle="solid"> + + Normal + + + CandleStick + + + Smoothed + + + Area + + +
+
+); + +class ChartOptions extends React.Component { + constructor(props) { + super(props); + this.state = { + open: false, + }; + this.optionsArea = React.createRef(); + this.handleOutsideClick = this.handleOutsideClick.bind(this); + } + onComponentDidUnmount() { + document.removeEventListener('click', e => this.handleOutsideClick(e)); + } + handleOutsideClick(e) { + const chartOptionsArea = ReactDOM.findDOMNode(this); + // chartOptionsArea = this.node + if (chartOptionsArea.contains(e.target)) { + return; + } + this.handleSelect(); + } + + handleSelect() { + if (!this.state.open) { + // attach/remove event handler + document.addEventListener('click', this.handleOutsideClick, false); + } else { + document.removeEventListener( + 'click', + this.handleOutsideClick, + false, + ); + } + this.setState(prevState => ({ + open: !prevState.open, + })); + } + handleOptionsChange(e, v) { + console.log(e, v); + } + + render() { + return ( + + + this.props.onChartOptionChange(v)} + open={this.state.open} + /> + + ); + } +} + +export default ChartOptions; diff --git a/src/views/summary/components/ChartOptions/style.js b/src/views/summary/components/ChartOptions/style.js new file mode 100644 index 0000000..6402130 --- /dev/null +++ b/src/views/summary/components/ChartOptions/style.js @@ -0,0 +1,71 @@ +import styled from 'styled-components'; + +// dropdown wrapper +export const Wrapper = styled.div` + margin-top: auto; + margin-bottom: auto; + margin-left: auto; + margin-right: 10px; + display: inline-block; + position: relative; +`; +export const buttonStyle = { + fontSize: 25, + fill: 'black', + color: 'black', +}; +export const Button = styled.div` + width: 30px; + height: 30px; + margin-right: 7px; + padding-top: 10px; + display: inline-block; + position: relative; +`; +// dropdown-content +export const OptionsWrapper = styled.div` + padding: 5px; + display: ${props => (props.open ? `flex` : `none`)}; + position: absolute; + z-index: 1; + background: white; + right: 0; + border-radius: 5px; + border: 1px solid #eee; + min-width: 160px; + box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); + flex-direction: column; + text-align: center; + align-items: center; + .chart-modes { + display: flex; + flex-direction: column; + text-align: center; + width: 130px; + } +`; +export const Option = styled.div` + font-size: 15px; + font-family: Avenir Next, sans-serif; + font-weight: 300; + display: flex; + flex-direction: column; + text-align: center; +`; +export const OptionHeader = styled.div` + font-size: 20px; + font-family: Avenir Next, sans-serif; + font-weight: bold; + display: flex; + flex-direction: column; + text-align: center; +`; + +export const OptionWrapper = styled.div``; + +export const radioStyle = { + display: 'block', + height: '30px', + lineHeight: '30px', + margin: 4, +}; diff --git a/src/views/summary/components/Legend/index.jsx b/src/views/summary/components/Legend/index.jsx new file mode 100644 index 0000000..c12648d --- /dev/null +++ b/src/views/summary/components/Legend/index.jsx @@ -0,0 +1,79 @@ +import React from 'react'; +import Transition from 'react-transition-group/Transition'; +import LegendCompanyCode from '../LegendCompanyCode'; +import LegendCompanyLogo from '../LegendCompanyLogo'; +import LegendCompanyPE from '../LegendCompanyPE'; +import LegendCompanyMarketCap from '../LegendCompanyMarketCap'; +import { + Wrapper, + duration, + transitionStyles, + UnselectedWrapper, + CompanyName, + CompanySector, +} from './style'; +import ShortedAPI from '../../../../services/sapi/client'; +/** + * Renders a shorted.com.au logo + * TODO: add data fetch here, async or prefetch based of top-short positions + */ +class Legend extends React.Component { + constructor(props) { + super(props); + this.apiClient = new ShortedAPI(); + this.state = { + inside: false, + code: false, + }; + } + componentDidUpdate(prevProps) { + // You don't have to do this check first, but it can help prevent an unneeded render + if (prevProps.code !== this.props.code) { + this.setState({ + code: this.props.code, + inside: false, + }); + } + } + componentDidMount() { + this.toggleEnterState(); + } + + toggleEnterState() { + this.setState({inside: true}); + } + render() { + const data = this.apiClient.getStockSummary(this.props.code); + const logo = this.apiClient.getStockLogo(this.props.code); + return ( + + {state => { + return this.props.code ? ( + + + + + {data.metadata.name} + + {data.metadata.sector} + + + + ) : ( + +

hover over graph to show profile

+
+ ); + }} +
+ ); + } +} + +export default Legend; diff --git a/src/views/summary/components/Legend/style.js b/src/views/summary/components/Legend/style.js new file mode 100644 index 0000000..a63a02f --- /dev/null +++ b/src/views/summary/components/Legend/style.js @@ -0,0 +1,87 @@ +import styled from 'styled-components'; + +export const duration = 500; +export const transitionStyles = { + entering: {opacity: 0, Ypos: 500}, + entered: {opacity: 1, Ypos: 0}, + exited: {opacity: 0}, +}; +export const Wrapper = styled.div` + opacity: ${props => props.opacity}; + transition: ${props => `${props.duration}ms ease-in-out`}; + transition-property: opacity, transform; + transform: ${props => `translateY(${props.Ypos}px)`}; + border-radius: 4px; + grid-area: legend; + display: grid; + grid-gap: 5px; + grid-template-rows: 40px 40px 40px 40px 100px; + grid-template-columns: repeat(4, 1fr); + grid-template-areas: + 'company-name company-name company-name company-name' + 'company-logo company-logo company-code company-code' + 'company-logo company-logo company-pe company-pe' + 'company-sector company-sector company-sector company-sector' + 'company-mc company-mc company-mc company-mc'; +`; +export const UnselectedWrapper = styled.div` + opacity: ${props => props.opacity}; + transition: ${props => `${props.duration}ms ease-in-out`}; + transition-property: opacity, transform; + transform: ${props => `translateY(${props.Ypos}px)`}; + grid-area: legend; + display: flex; + flex-direction: column; + vertical-align: middle; + justify-content: center; + text-align: center; + background: white; + border-radius: 4px; + height: 100%; + font-family: Avenir Next, sans-serif; + font-size: 22px; + font-weight: bold; + padding-left: 30px; + padding-right: 30px; +`; +export const CompanyHeader = styled.div` + display: flex; + border-radius: 5px; + flex-direction: column; + vertical-align: middle; + justify-content: center; + font-size: 0.8vh; + grid-area: company-header; +`; +export const CompanyMarketCap = styled.div` + grid-area: company-mc; + display: flex; + flex-direction: column; + vertical-align: middle; +`; +export const CompanySector = styled.div` + grid-area: company-sector; + display: flex; + border-radius: 5px; + background: white; + font-size: 1vh; + font-weight: bold; + font-family: Avenir Next, sans-serif; + text-align: center; + flex-direction: column; + vertical-align: middle; + justify-content: center; +`; +export const CompanyName = styled.div` + border-radius: 5px; + grid-area: company-name; + display: flex; + background: white; + font-size: 1vh; + font-weight: bold; + font-family: Avenir Next, sans-serif; + text-align: center; + flex-direction: column; + vertical-align: middle; + justify-content: center; +`; diff --git a/src/views/summary/components/LegendCompanyCode/index.jsx b/src/views/summary/components/LegendCompanyCode/index.jsx new file mode 100644 index 0000000..5e6cef3 --- /dev/null +++ b/src/views/summary/components/LegendCompanyCode/index.jsx @@ -0,0 +1,15 @@ +import React from 'react'; +import {Wrapper, Code, Header} from './style'; + +/** + * Renders a nicely styled and dynamically scaling header for the legend view which will display the company icon/logo + * as well as its stock code. + * + */ +const LegendCompanyCode = props => ( + +
Issuer code
+ {props.code} +
+); +export default LegendCompanyCode; diff --git a/src/views/summary/components/LegendCompanyCode/style.js b/src/views/summary/components/LegendCompanyCode/style.js new file mode 100644 index 0000000..ef20411 --- /dev/null +++ b/src/views/summary/components/LegendCompanyCode/style.js @@ -0,0 +1,31 @@ +import styled from 'styled-components'; + +export const Wrapper = styled.div` + background: white; + grid-area: company-code; + border-radius: 5px; + display: flex; + flex-direction: column; + justify-content: center; + vertical-align: middle; + text-align: center; +`; + +export const Code = styled.div` + flex-direction: column; + justify-content: center; + vertical-align: middle; + text-align: center; + font-size: 15px; + font-weight: 600; + font-family: Avenir Next, sans-serif; +`; +export const Header = styled.div` + flex-direction: column; + justify-content: center; + vertical-align: middle; + text-align: center; + font-size: 11px; + font-weight: bold; + font-family: Avenir Next, sans-serif; +`; diff --git a/src/views/summary/components/LegendCompanyLogo/index.jsx b/src/views/summary/components/LegendCompanyLogo/index.jsx new file mode 100644 index 0000000..f95eb87 --- /dev/null +++ b/src/views/summary/components/LegendCompanyLogo/index.jsx @@ -0,0 +1,32 @@ +import React from 'react'; +import {Wrapper, Image} from './style'; + +/** + * Renders a nicely styled and dynamically scaling header for the legend view which will display the company icon/logo + * as well as its stock code. + * + */ +class LegendCompanyLogo extends React.Component { + constructor(props) { + super(props) + this.state = {dimensions: {}}; + this.onImgLoad = this.onImgLoad.bind(this); + } + onImgLoad({target:img}) { + console.log(img) + this.setState({dimensions:{height:img.offsetHeight, + width:img.offsetWidth}}) + } + onComponentWillMount() { + console.log(this.props.logo) + } + render () { + console.log(this.state.dimensions) + return ( + + + + ) + } +} +export default LegendCompanyLogo; diff --git a/src/views/summary/components/LegendCompanyLogo/style.js b/src/views/summary/components/LegendCompanyLogo/style.js new file mode 100644 index 0000000..8dcebcd --- /dev/null +++ b/src/views/summary/components/LegendCompanyLogo/style.js @@ -0,0 +1,17 @@ +import styled from 'styled-components' + +export const Wrapper = styled.div` + display: flex; + flex-direction: column; + text-align: center; + vertical-align: middle; + justify-content: center; + grid-area: company-logo; +` +export const Image = styled.img` + /* width: 60px; + height: 60px; */ + margin-left: auto; + margin-right: auto; + display: block; +` diff --git a/src/views/summary/components/LegendCompanyMarketCap/index.jsx b/src/views/summary/components/LegendCompanyMarketCap/index.jsx new file mode 100644 index 0000000..897e194 --- /dev/null +++ b/src/views/summary/components/LegendCompanyMarketCap/index.jsx @@ -0,0 +1,69 @@ +import React from 'react'; +import { + VictoryChart, + VictoryAxis, + VictoryLabel, + VictoryContainer, + VictoryLine, +} from 'victory'; +// import { LineChart, Line, ResponsiveContainer } from 'recharts'; +import {Wrapper, Header, Chart} from './style'; + +/** + * LegendCompanyMarketCap + * + */ +class LegendCompanyMarketCap extends React.Component { + constructor(props) { + super(props); + this.state = {}; + } + getTickValues() { + return [ + new Date(2002, 1, 1), + new Date(2017, 1, 1), + new Date(2018, 1, 1), + ]; + } + + render() { + // return ( + // + // + // + // + // + // + // + // ); + return ( + +
+ +
+ + }> + + new Date(x).getFullYear()} + /> + + +
+ ); + } +} + +export default LegendCompanyMarketCap; diff --git a/src/views/summary/components/LegendCompanyMarketCap/style.js b/src/views/summary/components/LegendCompanyMarketCap/style.js new file mode 100644 index 0000000..1490f44 --- /dev/null +++ b/src/views/summary/components/LegendCompanyMarketCap/style.js @@ -0,0 +1,29 @@ +import styled from 'styled-components'; + +export const Wrapper = styled.div` + grid-area: company-mc; + border-radius: 5px; + background: white; + display: grid; + height: 140px; + grid-template-rows: 20px 120px; + grid-template-areas: + 'header' + 'chart'; +`; +export const Header = styled.div` + grid-area: header; + padding-top: 3px; + display: flex; + flex-direction: column; + justify-content: center; + text-align: center; +`; +export const Chart = styled.div` + grid-area: chart; + display: flex; + flex-direction: column; + justify-content: center; + text-align: center; + height: 150px; +`; diff --git a/src/views/summary/components/LegendCompanyPE/index.jsx b/src/views/summary/components/LegendCompanyPE/index.jsx new file mode 100644 index 0000000..0e68926 --- /dev/null +++ b/src/views/summary/components/LegendCompanyPE/index.jsx @@ -0,0 +1,15 @@ +import React from 'react'; +import {Wrapper, Header, PE} from './style'; + +/** + * Renders a nicely styled and dynamically scaling header for the legend view which will display the company icon/logo + * as well as its stock code. + * + */ +const LegendCompanyPE = props => ( + +
P/E ratio
+ {props.pe} +
+); +export default LegendCompanyPE; diff --git a/src/views/summary/components/LegendCompanyPE/style.js b/src/views/summary/components/LegendCompanyPE/style.js new file mode 100644 index 0000000..72173d9 --- /dev/null +++ b/src/views/summary/components/LegendCompanyPE/style.js @@ -0,0 +1,31 @@ +import styled from 'styled-components'; + +export const Wrapper = styled.div` + display: flex; + border-radius: 5px; + flex-direction: column; + text-align: center; + vertical-align: middle; + justify-content: center; + background: white; + grid-area: company-pe; +`; + +export const PE = styled.div` + flex-direction: column; + justify-content: center; + vertical-align: middle; + text-align: center; + font-size: 15px; + font-weight: 600; + font-family: Avenir Next, sans-serif; +`; +export const Header = styled.div` + flex-direction: column; + justify-content: center; + vertical-align: middle; + text-align: center; + font-size: 11px; + font-weight: bold; + font-family: Avenir Next, sans-serif; +`; diff --git a/src/views/summary/components/MoversList/index.jsx b/src/views/summary/components/MoversList/index.jsx new file mode 100644 index 0000000..3f586f4 --- /dev/null +++ b/src/views/summary/components/MoversList/index.jsx @@ -0,0 +1,49 @@ +import React from 'react'; +import Transition from 'react-transition-group/Transition'; +import MoversListRow from '../MoversListRow'; +import {Wrapper, Header, More, duration, transitionStyles} from './style'; +/** + * Renders a list of TopShortsListRow components. These rows will display the stock code, stock name, and percentage shorted + * for the top say 20 stocks. With a "show more" button present at the bottom, taking them to a different view which will me dedicated to showing more short position + * information for more stocks (maybe top 100). Will perhaps a more verbose set of properties and graphics. + */ +class MoversList extends React.Component { + constructor(props) { + super(props); + this.state = { + inside: false, + }; + } + componentDidMount() { + this.toggleEnterState(); + } + + toggleEnterState() { + this.setState({inside: true}); + } + + render() { + const rows = this.props.data.data + .slice(0, 5) + .map(row_data => ( + + )); + return ( + + {state => { + return ( + +
Top Movers
+ {rows} + show more +
+ ); + }} +
+ ); + } +} + +export default MoversList; diff --git a/src/views/summary/components/MoversList/style.js b/src/views/summary/components/MoversList/style.js new file mode 100644 index 0000000..40a8582 --- /dev/null +++ b/src/views/summary/components/MoversList/style.js @@ -0,0 +1,44 @@ +import styled from 'styled-components'; + +export const duration = 500; +export const transitionStyles = { + entering: {opacity: 0, Ypos: 500}, + entered: {opacity: 1, Ypos: 0}, + exited: {opacity: 0}, +}; + +export const Wrapper = styled.div` + height: 100%; + grid-area: top-movers; + background: white; + display: flex; + flex-direction: column; + border-radius: 5px; + border: 1px solid #eee; + opacity: ${props => props.opacity}; + transition: ${props => `${props.duration}ms ease-in-out`}; + transition-property: opacity, transform; + transform: ${props => `translateY(${props.Ypos}px)`}; +`; +export const Header = styled.div` + height: 50px; + display: flex; + flex-direction: column; + text-align: center; + font-size: 22px; + font-weight: bold; + font-family: Avenir Next, sans-serif; +`; +export const More = styled.div` + height: 42px; + display: flex; + flex-direction: column; + text-align: center; + font-size: 22px; + font-weight: bold; + background: #dadada; + border-radius: 0 0 15px 15px; + margin: 4px; + justify-content: center; + font-family: Avenir Next, sans-serif; +`; diff --git a/src/views/summary/components/MoversListRow/index.jsx b/src/views/summary/components/MoversListRow/index.jsx new file mode 100644 index 0000000..ee7e463 --- /dev/null +++ b/src/views/summary/components/MoversListRow/index.jsx @@ -0,0 +1,36 @@ +import React from 'react'; + +import { + Wrapper, + Name, + Code, + Percentage, + IndicatorWrapper, + IndicatorUp, + IndicatorDown, + PercentageChanged, + PercentageCurrent, +} from './style'; + +/** + * Renders a given row in the alert & anomalies widget. + */ +const MoversListRow = props => ( + + +
{props.code}
+
+ {props.name} + + {props.current}% + + {props.change}% + + + + {props.change > 0 ? : } + +
+); + +export default MoversListRow; diff --git a/src/views/summary/components/MoversListRow/style.js b/src/views/summary/components/MoversListRow/style.js new file mode 100644 index 0000000..aee2e37 --- /dev/null +++ b/src/views/summary/components/MoversListRow/style.js @@ -0,0 +1,100 @@ +import styled from 'styled-components'; + +export const Wrapper = styled.div` + display: grid; + grid-template-columns: 120px 60px 1fr; + grid-template-rows: repeat(2, 1fr); + grid-template-areas: + 'code percentage indicator' + 'code percentage indicator'; + margin: 2px; + margin-left: 7px; + margin-right: 7px; + height: 65px; + background: #e2e2e2; + padding-top: 4px; + padding-bottom: 4px; +`; + +export const Name = styled.div` + grid-area: name; + display: flex; + flex-direction: column; + justify-content: center; + vertical-align: middle; +`; + +export const Code = styled.div` + grid-area: code; + display: flex; + flex-direction: column; + justify-content: center; + vertical-align: middle; + .code { + background-color: gray; + width: 60px; + height: 45px; + display: flex; + flex-direction: column; + justify-content: center; + vertical-align: middle; + text-align: center; + margin-left: 5px; + } +`; +export const Percentage = styled.div` + grid-area: percentage; + display: grid; + display: block; + margin-right: 20px; + grid-template-rows: repeat(2, 1fr); + grid-gap: 1px; + grid-template-areas: + 'current' + 'changed'; +`; +export const PercentageCurrent = styled.div` + grid-area: current; + display: block; + float: right; + font-size: 21px; + font-weight: bold; + font-family: Avenir Next, sans-serif; +`; +export const PercentageChanged = styled.div` + grid-area: changed; + float: right; + display: block; + margin-left: 17px; + font-weight: bold; + font-family: Avenir Next, sans-serif; + color: ${props => (props.value > 0 ? `red` : `green`)}; + padding-bottom: 10px; +`; +export const IndicatorUp = styled.div` + width: 0; + height: 0; + border-left: 30px solid transparent; + border-right: 30px solid transparent; + border-bottom: 30px solid red; +`; +export const IndicatorDown = styled.div` + width: 0; + height: 0; + border-left: 30px solid transparent; + border-right: 30px solid transparent; + border-top: 30px solid green; +`; + +export const IndicatorWrapper = styled.div` + grid-area: indicator; + @media (min-width: 1650px) { + display: flex; + } + display: none; + flex-direction: column; + text-align: center; + vertical-align: middle; + justify-content: center; + margin-right: 10px; +`; diff --git a/src/views/summary/components/TopChartVictory/components.jsx b/src/views/summary/components/TopChartVictory/components.jsx new file mode 100644 index 0000000..7aca1f0 --- /dev/null +++ b/src/views/summary/components/TopChartVictory/components.jsx @@ -0,0 +1,58 @@ +import React from 'react'; +import {TooltipWrapper} from './style'; +import {Sector} from 'recharts'; + +export class TopChartTooltip extends React.Component { + render() { + const {x, y, dx, dy, selectedCode, datum} = this.props; + const orientation = y <= 100 ? 'bottom' : 'top'; + const windowWidth = 400; + const p1_x = x <= windowWidth / 2 ? x + 10 : x - 10; + const p1_y = orientation === 'top' ? y - 10 : y + 10; + const p2_x = p1_x; + const p2_y = orientation === 'top' ? p1_y - 10 : p1_y + 10; + return selectedCode ? ( + + + + + + + + + {this.props.datum.date} + + + {`${selectedCode}: `} + + {`${this.props.datum[selectedCode]}`}% + + + + + ) : ( + + ); + } +} diff --git a/src/views/summary/components/TopChartVictory/index.jsx b/src/views/summary/components/TopChartVictory/index.jsx new file mode 100644 index 0000000..75b9f9f --- /dev/null +++ b/src/views/summary/components/TopChartVictory/index.jsx @@ -0,0 +1,219 @@ +import React from 'react'; +import { + VictoryChart, + VictoryAxis, + VictoryLabel, + VictoryTooltip, + VictoryLine, + VictoryArea, + VictoryVoronoiContainer, +} from 'victory'; +import Transition from 'react-transition-group/Transition'; +import { + duration, + transitionStyles, + Wrapper, + PickerWrapper, + ChartWrapper, + OptionsWrapper, + colors700, + colors300, +} from './style'; +import {TopChartTooltip} from './components'; +/** + * Chart + * Component responsible for rendering the page1 graphic displaying the top short positions + * TODO: + * * add more styling to graph currently nothing present + * * add more intelligent x-axis ticks as windowpicker is changed + * * refactor to use victory charts + * * multiple display modes such as candleStick etc. + * * implement brush and zoom + * * implement select to zoom + * * implement hover filtering/hiding other lines + * + * + */ +class TopChartVictory extends React.Component { + constructor(props) { + super(props); + this.state = { + inside: false, + windowWidth: 1700, + }; + this.handleWindowSizeChange = this.handleWindowSizeChange.bind(this); + } + componentWillUnmount() { + typeof window !== 'undefined' && + window.removeEventListener('resize', this.handleWindowSizeChange); + } + handleWindowSizeChange() { + typeof window !== 'undefined' && + setTimeout(this.setState({windowWidth: window.innerWidth})); + } + componentDidMount() { + typeof window !== 'undefined' && + setTimeout( + window.addEventListener('resize', this.handleWindowSizeChange), + ); + this.toggleEnterState(); + } + + toggleEnterState() { + this.setState({inside: true}); + } + handleVoronoiSelect(points, props) { + if (points[0]) { + console.log('voronio snapped to', points[0].childName) + this.props.onSelectCode(points[0].childName) + } + } + handleLineHover(e, key) { + console.log('new line slected:', key); + this.props.onSelectCode(key); + } + handleLineExit(e, key) { + console.log('exiting line', key); + } + + render() { + const {data, selectedCode} = this.props; + var lines = null; + if (this.props.mode === 'NORMAL') { + lines = data.dataKeys.map((key, index) => ( + } + data={data.data} + x="date" + y={key} + events={[ + { + childName: key, + target: 'data', + eventHandlers: { + onMouseOver: e => this.handleLineHover(e, key), + onMouseOut: e => this.handleLineExit(e, key), + }, + }, + ]} + style={{ + data: { + stroke: colors700[index], + strokeOpacity: + key === selectedCode || !selectedCode ? 1 : 0.2, + strokeWidth: 2, + }, + }} + /> + )); + } else if (this.props.mode === 'AREA') { + lines = data.dataKeys.map((key, index) => ( + this.handleLineHover(e, key), + }, + }, + ]} + style={{ + data: { + stroke: colors700[index], + fill: colors700[index], + fillOpacity: 0.3, + strokeWidth: 2, + }, + }} + /> + )); + } + return ( + + {state => { + return ( + + {this.props.picker} + + {this.props.options} + + + ``} + onActivated={(points,props) => this.handleVoronoiSelect(points,props)} + labelComponent={ + + } + cornerRadius={0} + flyoutStyle={{ + fill: 'white', + }} + /> + } + /> + }> + {lines} + + `${Math.round(t)}`} + tickLabelComponent={} + style={{ + ticks: {stroke: 'grey', size: 5}, + tickLabels: { + fontSize: 7, + padding: 4, + }, + }} + /> + + + + ); + }} + + ); + } +} + +export default TopChartVictory; diff --git a/src/views/summary/components/TopChartVictory/style.js b/src/views/summary/components/TopChartVictory/style.js new file mode 100644 index 0000000..aa33bc8 --- /dev/null +++ b/src/views/summary/components/TopChartVictory/style.js @@ -0,0 +1,96 @@ +import styled from 'styled-components'; +export const duration = 500; +export const transitionStyles = { + entering: {opacity: 0, Ypos: 500}, + entered: {opacity: 1, Ypos: 0}, + exited: {opacity: 0}, +}; + +export const TooltipWrapper = styled.svg` + .header { + font-size: 12px; + font-weight: bold; + } + .issuerCode { + font-size: 12px; + } + .value { + font-size: 12px; + } + .tooltip-card { + fill: #e1e1e1; + stroke: 1px solid #282626; + border: 1px solid #282626; + border-radius: 30px; + } +`; +export const Wrapper = styled.div` + grid-area: top-graph; + display: grid; + grid-template-rows: 30px 1fr; + grid-template-columns: repeat(3, 1fr); + grid-template-areas: + 'none picker options' + 'chart chart chart' + 'chart chart chart'; + opacity: ${props => props.opacity}; + transition: ${props => `${props.duration}ms ease-in-out`}; + transition-property: opacity, transform; + transform: ${props => `translateY(${props.Ypos}px)`}; + max-width: 1600px; + background-color: white; + border-radius: 5px; + border: 1px solid #eee; +`; +export const ChartWrapper = styled.div` + grid-area: chart; + display: flex; + flex: 1 1 auto; + height: calc(99%); + min-height: 0; +`; +export const PickerWrapper = styled.div` + grid-area: picker; + display: flex; + vertical-align: middle; + justify-content: center; +`; +export const OptionsWrapper = styled.div` + grid-area: options; + display: flex; + justify-content: flex-end; +`; +export const intervals = { + d: 2, + w: 7, + m: 40, + y: 100, +}; +export const colors700 = [ + '#D32F2F', + '#303F9F', + '#00796B', + '#388E3C', + '#388E3C', + '#F57C00', + '#455A64', + '#5D4037', + '#C2185B', + '#7B1FA2', + '#00796B', + '#7B1FA2', + '#C2185B', +]; +export const colors300 = [ + '#E57373', + '#F06292', + '#7986CB', + '#9575CD', + '#7986CB', + '#64B5F6', + '#F06292', + '#E57373', + '#FFB74D', + '#BCAAA4', + '#FFF176', +]; diff --git a/src/views/summary/components/TopShortsList/index.jsx b/src/views/summary/components/TopShortsList/index.jsx new file mode 100644 index 0000000..9ffbc00 --- /dev/null +++ b/src/views/summary/components/TopShortsList/index.jsx @@ -0,0 +1,64 @@ +import React from 'react'; +import Transition from 'react-transition-group/Transition'; +import TopShortsListRow from '../../components/TopShortsListRow'; +import {Wrapper, Header, More, duration, transitionStyles} from './style'; +/** + * Renders a list of TopShortsListRow components. These rows will display the stock code, stock name, and percentage shorted + * for the top say 20 stocks. With a "show more" button present at the bottom, taking them to a different view which will me dedicated to showing more short position + * information for more stocks (maybe top 100). Will perhaps a more verbose set of properties and graphics. + * TODO: + * * load profile on select of a specific stock in list + */ +class TopShortsList extends React.Component { + constructor(props) { + super(props); + this.state = { + inside: false, + hovered: false, + }; + } + componentDidMount() { + this.toggleEnterState(); + } + + toggleEnterState() { + this.setState({inside: true}); + } + handleHover(value) { + this.setState({hovered: value}); + } + handleMouseLeave() { + this.setState({ + hovered: false, + }); + } + + render() { + const rows = this.props.data.map(row_data => ( + this.handleHover(row_data.code)} + key={row_data.code} + {...row_data} + /> + )); + return ( + + {state => { + return ( + this.handleMouseLeave()} + duration={duration} + {...transitionStyles[state]}> +
Top Short List
+ {rows} + show more +
+ ); + }} +
+ ); + } +} + +export default TopShortsList; diff --git a/src/views/summary/components/TopShortsList/style.js b/src/views/summary/components/TopShortsList/style.js new file mode 100644 index 0000000..97c3f1e --- /dev/null +++ b/src/views/summary/components/TopShortsList/style.js @@ -0,0 +1,39 @@ +import styled from 'styled-components' + +export const duration = 500 +export const transitionStyles = { + entering: { opacity: 0, Ypos: 500 }, + entered: { opacity: 1, Ypos: 0 }, + exited: { opacity: 0 } +} + +export const Wrapper = styled.div` + grid-area: top-list; + background: white; + display: flex; + flex-direction: column; + border-radius: 5px; + border: 1px solid #eee; + opacity: ${props => props.opacity}; + transition: ${props => `${props.duration}ms ease-in-out`}; + transition-property: opacity, transform; + transform: ${props => `translateY(${props.Ypos}px)`}; +` +export const Header = styled.div` + height: 30px; + text-align: center; + font-size: 22px; + font-weight: bold; + font-family: Avenir Next, sans-serif; +` +export const More = styled.div` + height: 30px; + text-align: center; + font-size: 22px; + font-weight: bold; + background: #dadada; + border-radius: 0 0 15px 15px; + margin: 4px; + justify-content: center; + font-family: Avenir Next, sans-serif; +` diff --git a/src/views/summary/components/TopShortsListRow/index.jsx b/src/views/summary/components/TopShortsListRow/index.jsx new file mode 100644 index 0000000..d991c39 --- /dev/null +++ b/src/views/summary/components/TopShortsListRow/index.jsx @@ -0,0 +1,43 @@ +import React from 'react'; + +import {Wrapper, WrapperHovered, Code, Name, Percent} from './style'; + +/** + * Renders a specfiic row that is contained within the TopSHortList view. This will show + * the current short percentage of a given stock within the top x number of short positions + * takes a data prop which contains the following payload: + * data = { + * name: "JBHIFI LIMITED", + * code: "JBH", + * percentage: 19.6 + * } + */ + +const TopShortListRow = props => { + return props.isHovered ? ( + + +
{props.code}
+
+ {props.name} + +
{props.current}%
+
+
+ ) : ( + + +
{props.code}
+
+ {props.name} + +
{props.current}%
+
+
+ ); +}; + +export default TopShortListRow; diff --git a/src/views/summary/components/TopShortsListRow/style.js b/src/views/summary/components/TopShortsListRow/style.js new file mode 100644 index 0000000..630ee76 --- /dev/null +++ b/src/views/summary/components/TopShortsListRow/style.js @@ -0,0 +1,113 @@ +import styled from 'styled-components'; + +export const Wrapper = styled.a` + display: grid; + color: black; + text-decoration: none !important; + @media (min-width: 901px) { + grid-template-columns: repeat(5, 1fr); + grid-template-areas: 'code name name name percentage'; + } + @media (max-width: 900px) { + grid-template-columns: repeat(2, 1fr); + grid-template-areas: 'code percentage'; + } + margin: 4px; + margin-left: 7px; + margin-right: 4px; + height: 50px; + background: #dadada; + border-radius: 0 30px 30px 0; + padding-top: 3px; + padding-bottom: 4px; + margin-bottom: 4px; + &:hover, + &:visited, + &:link, + &:active { + text-decoration: none !important; + color: black; + } +`; +export const WrapperHovered = styled.a` + display: grid; + color: black; + text-decoration: none !important; + z-index: 10; + grid-template-columns: repeat(5, 1fr); + grid-template-areas: 'code name name name percentage'; + margin: 4px; + margin-left: 7px; + margin-right: 4px; + height: 50px; + background: #dadada; + border-radius: 0 30px 30px 0; + padding-top: 4px; + padding-bottom: 4px; + margin-bottom: 6px; + -webkit-box-shadow: -3px 4px 7px 0px rgba(0, 0, 0, 0.25); + -moz-box-shadow: -3px 4px 7px 0px rgba(0, 0, 0, 0.25); + box-shadow: -3px 4px 7px 0px rgba(0, 0, 0, 0.25); + &:hover, + &:visited, + &:link, + &:active { + text-decoration: none !important; + color: black; + } +`; + +export const Name = styled.div` + grid-area: name; + @media (max-width: 1300px) { + display: none; + } + display: flex; + flex-direction: column; + justify-content: center; + vertical-align: middle; + font-size: 14px; +`; + +export const Code = styled.div` + grid-area: code; + display: flex; + flex-direction: column; + justify-content: center; + + .code { + width: 60px; + height: 45px; + display: flex; + flex-direction: column; + justify-content: center; + vertical-align: middle; + text-align: center; + font-size: 21px; + font-weight: bold; + } +`; + +export const Percent = styled.div` + grid-area: percentage; + margin-left: auto; + padding-right: 5px; + display: flex; + flex-direction: column; + justify-content: center; + vertical-align: middle; + + .circle { + background: #f98080; + height: 40px; + width: 40px; + border-radius: 50px; + display: flex; + flex-direction: column; + justify-content: center; + vertical-align: middle; + text-align: center; + font-size: 10px; + font-weight: 400; + } +`; diff --git a/src/views/summary/components/WindowPicker/index.jsx b/src/views/summary/components/WindowPicker/index.jsx new file mode 100644 index 0000000..2582dac --- /dev/null +++ b/src/views/summary/components/WindowPicker/index.jsx @@ -0,0 +1,33 @@ +import React from 'react'; +import {Wrapper} from './style'; +import Button from '../../../../components/Button'; + +/** + * WindowPicker + * Responsible for providing a time window selector across a range of values including 1d, 1w, 1m, 1y etc. + * will set the container state which will adjust the query. This will be set by a uni-directional handler passed + * down as a prop. + * + */ + +class WindowPicker extends React.Component { + render() { + const {options, selectedOption, onSelect, theme} = this.props; + const buttons = options.values.map(value => ( +