Skip to content

Commit

Permalink
feat(courts): implement subcourt cascader
Browse files Browse the repository at this point in the history
  • Loading branch information
epiqueras committed Jan 4, 2019
1 parent 7b14172 commit cc2a45e
Show file tree
Hide file tree
Showing 5 changed files with 281 additions and 20 deletions.
2 changes: 1 addition & 1 deletion src/assets/images/breadcrumb.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 45 additions & 15 deletions src/components/breadcrumbs.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,45 +13,75 @@ const StyledBreadcrumbDiv = styled.div`
top: 0;
z-index: ${props => props.length - props.id};
`
const StyledBreadcrumb = styled(Breadcrumb)`
height: ${props => (props.large === 'true' ? 38 : 20)}px;
width: ${props => (props.large === 'true' ? 151 : 114)}px;
`
const StyledTitleDiv = styled.div`
color: white;
font-size: 10px;
left: 20px;
font-size: ${props => (props.large ? 14 : 10)}px;
left: ${props => (props.large ? 16 : 20)}px;
line-height: ${props => (props.large ? 38 : 20)}px;
${props => props.active && 'font-weight: bold;'}
position: absolute;
top: 0;
user-select: none;
`
const Breadcrumbs = ({ activeIndex, breadcrumbs, className, onClick }) => (
const Breadcrumbs = ({
activeIndex,
breadcrumbs,
className,
colorIndex,
large,
onClick
}) => (
<StyledDiv className={className}>
{breadcrumbs.map((b, i) => (
{(Array.isArray(breadcrumbs) ? breadcrumbs : [breadcrumbs]).map((b, i) => (
<StyledBreadcrumbDiv
id={i}
key={i}
length={breadcrumbs.length}
onClick={useCallback(
({ currentTarget: { id } }) => onClick(Number(id)),
[onClick]
)}
onClick={
onClick &&
useCallback(({ currentTarget: { id } }) => onClick(Number(id)), [
onClick
])
}
>
<Breadcrumb
className={`${['primary', 'secondary', 'ternary'][i % 3]}-fill`}
<StyledBreadcrumb
className={`${
['primary', 'secondary', 'ternary'][
(colorIndex === null ? i : colorIndex) % 3
]
}-fill`}
large={String(large)}
/>
<StyledTitleDiv active={i === activeIndex}>{b}</StyledTitleDiv>
<StyledTitleDiv active={i === activeIndex} large={large}>
{b}
</StyledTitleDiv>
</StyledBreadcrumbDiv>
))}
</StyledDiv>
)

Breadcrumbs.propTypes = {
activeIndex: PropTypes.number.isRequired,
breadcrumbs: PropTypes.arrayOf(PropTypes.node.isRequired).isRequired,
activeIndex: PropTypes.number,
breadcrumbs: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node.isRequired).isRequired,
PropTypes.node.isRequired
]).isRequired,
className: PropTypes.string,
onClick: PropTypes.func.isRequired
colorIndex: PropTypes.number,
large: PropTypes.bool,
onClick: PropTypes.func
}

Breadcrumbs.defaultProps = {
className: null
activeIndex: null,
className: null,
colorIndex: null,
large: false,
onClick: null
}

export default Breadcrumbs
224 changes: 224 additions & 0 deletions src/components/court-cascader-modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
import { Button, Cascader, Col, Modal, Row, Skeleton } from 'antd'
import React, { useCallback, useState } from 'react'
import Breadcrumbs from '../components/breadcrumbs'
import PropTypes from 'prop-types'
import ReactMarkdown from 'react-markdown'
import styled from 'styled-components/macro'
import { useDataloader } from '../bootstrap/dataloader'
import { useDrizzle } from '../temp/drizzle-react-hooks'

const StyledModal = styled(Modal)`
.ant-modal {
&-header {
padding: 0;
}
&-close-icon svg {
fill: white;
}
&-body {
height: 320px;
position: relative;
}
&-footer {
height: 250px;
overflow-y: scroll;
padding: 18px 42px 28px;
text-align: left;
}
}
`
const StyledDiv = styled.div`
font-size: 18px;
font-weight: bold;
margin-bottom: 20px;
`
const StyledTitleDiv = styled.div`
color: white;
font-size: 20px;
font-weight: bold;
height: 54px;
line-height: 54px;
text-align: center;
`
const StyledCascader = styled(Cascader)`
display: none;
& ~ div .popupClassName {
background: whitesmoke;
left: 0 !important;
top: 0 !important;
width: 100%;
.ant-cascader-menu {
height: 320px;
padding-top: 28px;
width: 135px;
&-item {
height: 38px;
padding-left: 18px;
}
}
}
`
const StyledBreadcrumbs = styled(Breadcrumbs)`
left: ${props => props.colorIndex * 135}px;
position: absolute;
top: ${props => props.columnIndex * 38 + 28}px;
z-index: ${props => props.optionLength - props.colorIndex + 2000};
`
const CourtCascaderModal = ({ onClick }) => {
const { useCacheCall } = useDrizzle()
const load = useDataloader()
const [subcourtIDs, setSubcourtIDs] = useState(['0'])
const options = useCacheCall(['PolicyRegistry', 'KlerosLiquid'], call => {
const options = [
{
children: undefined,
description: undefined,
label: undefined,
loading: false,
summary: undefined,
value: subcourtIDs[0]
}
]
let option = options[0]
for (let i = 0; i < subcourtIDs.length; i++) {
const policy = call('PolicyRegistry', 'policies', subcourtIDs[i])
if (policy) {
const policyJSON = load(policy.fileURI)
if (policyJSON) {
option.description = policyJSON.description
option.label = policyJSON.name
option.summary = policyJSON.summary
}
}
const subcourt = call('KlerosLiquid', 'getSubcourt', subcourtIDs[i])
if (subcourt)
option.children = subcourt.children.map(c => {
const child = {
children: undefined,
description: undefined,
label: undefined,
loading: false,
summary: undefined,
value: c
}
const policy = call('PolicyRegistry', 'policies', c)
if (policy) {
const policyJSON = load(policy.fileURI)
if (policyJSON) {
child.description = policyJSON.description
child.label = policyJSON.name
child.summary = policyJSON.summary
}
}
if (child.label === undefined) child.loading = true
return child
})
if (
option.label === undefined ||
subcourt === undefined ||
option.children.some(c => c.loading)
) {
option.loading = true
break
}
option = option.children.find(c => c.value === subcourtIDs[i + 1])
}
return options
})
const option = subcourtIDs.reduce((acc, ID, i) => {
const index = acc.findIndex(option => option.value === ID)
return i === subcourtIDs.length - 1
? {
description: acc[index].description,
loading: acc[index].loading,
summary: acc[index].summary
}
: acc[index].children
}, options)
return (
<StyledModal
centered
footer={
<Skeleton active loading={option.loading}>
{
<Row>
<Col span={12}>
<StyledDiv>Description</StyledDiv>
<ReactMarkdown source={option.description} />
</Col>
<Col span={12}>
<StyledDiv>Summary</StyledDiv>
<ReactMarkdown source={option.summary} />
</Col>
</Row>
}
</Skeleton>
}
onCancel={useCallback(() => onClick(), [onClick])}
title={
<StyledTitleDiv className="secondary-linear-background theme-linear-background">
Select Court{' '}
<Button
onClick={useCallback(
() => onClick(subcourtIDs[subcourtIDs.length - 1]),
[onClick]
)}
size="small"
type="primary"
>
Stake
</Button>
</StyledTitleDiv>
}
visible
width="90%"
>
<StyledCascader
changeOnSelect
getPopupContainer={useCallback(
() => document.getElementsByClassName('ant-modal-body')[0],
[]
)}
onChange={setSubcourtIDs}
options={options}
popupClassName="popupClassName"
popupVisible
value={subcourtIDs}
/>
{subcourtIDs.slice(0, subcourtIDs.length - 1).map((ID, i) => {
const subcourtIDsSubset = subcourtIDs.slice(
0,
subcourtIDs.indexOf(ID) + 1
)
const option = subcourtIDsSubset.reduce((acc, ID, i) => {
const index = acc.findIndex(option => option.value === ID)
return i === subcourtIDsSubset.length - 1
? { index, label: acc[index].label || '' }
: acc[index].children
}, options)
return (
<StyledBreadcrumbs
breadcrumbs={option.label}
colorIndex={i}
columnIndex={option.index}
key={`${ID}-${i}`}
large
optionLength={subcourtIDs.length}
/>
)
})}
</StyledModal>
)
}

CourtCascaderModal.propTypes = {
onClick: PropTypes.func.isRequired
}

export default CourtCascaderModal
4 changes: 2 additions & 2 deletions src/components/welcome-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ const StyledTextCardGrid = styled(StyledCardGrid)`
z-index: -1;
`
const WelcomeCard = ({ icon, text, version }) => (
<StyledCard bordered={false} className="secondary-linear-background">
<StyledCard bordered={false}>
<StyledIconCardGrid>
{icon}
<StyledDiv>{version}</StyledDiv>
</StyledIconCardGrid>
<StyledTextCardGrid className="theme-linear-background">
<StyledTextCardGrid className="secondary-linear-background theme-linear-background">
{text}
</StyledTextCardGrid>
</StyledCard>
Expand Down
11 changes: 9 additions & 2 deletions src/containers/courts.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Button, Col, Row, Spin } from 'antd'
import React, { useState } from 'react'
import React, { useCallback, useState } from 'react'
import { useDrizzle, useDrizzleState } from '../temp/drizzle-react-hooks'
import CourtCard from '../components/court-card'
import CourtCascaderModal from '../components/court-cascader-modal'
import CourtDrawer from '../components/court-drawer'
import TopBanner from '../components/top-banner'

Expand All @@ -11,6 +12,7 @@ export default () => {
account: drizzleState.accounts[0]
}))
const [activeID, setActiveID] = useState()
const [stakingID, setStakingID] = useState()
const subcourtIDs = useCacheCall(
'KlerosLiquid',
'getJuror',
Expand All @@ -21,7 +23,11 @@ export default () => {
<TopBanner
description="Select courts and stake PNK."
extra={
<Button size="large" type="primary">
<Button
onClick={useCallback(() => setStakingID(null), [])}
size="large"
type="primary"
>
Select Court
</Button>
}
Expand All @@ -41,6 +47,7 @@ export default () => {
{activeID !== undefined && (
<CourtDrawer ID={activeID} onClose={setActiveID} />
)}
{stakingID === null && <CourtCascaderModal onClick={setStakingID} />}
</>
)
}

0 comments on commit cc2a45e

Please sign in to comment.