modals & categories url issue & hightest rating mobile issue

This commit is contained in:
Kamen Stoykov 2019-04-25 17:36:48 +03:00
parent 183a6d2b38
commit e7bca9b637
16 changed files with 328 additions and 202 deletions

View File

@ -4,6 +4,7 @@
font-family: $font;
background: $background;
display: flex;
height: calculateRem(145);
margin: 0 calculateRem(16) calculateRem(11) calculateRem(16);
position: relative;
}

View File

@ -3,7 +3,14 @@ import PropTypes from 'prop-types'
import styles from './Modal.module.scss'
const Modal = props => {
const { visible, children, modalClassName, contentClassName } = props
const {
visible,
children,
modalClassName,
windowClassName,
contentClassName,
onClickClose,
} = props
return (
<div
@ -11,8 +18,10 @@ const Modal = props => {
visible ? styles.active : ''
}`}
>
<div className={styles.window}>
<div className={styles.close}>+</div>
<div className={`${styles.window} ${windowClassName}`}>
<div className={styles.close} onClick={onClickClose}>
+
</div>
<div className={contentClassName}>{visible && children}</div>
</div>
</div>
@ -21,17 +30,20 @@ const Modal = props => {
Modal.defaultProps = {
modalClassName: '',
windowClassName: '',
contentClassName: '',
}
Modal.propTypes = {
visible: PropTypes.bool.isRequired,
modalClassName: PropTypes.string,
windowClassName: PropTypes.string,
contentClassName: PropTypes.string,
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]).isRequired,
onClickClose: PropTypes.func.isRequired,
}
export default Modal

View File

@ -1,3 +1,5 @@
@import '../../styles/variables';
.wrapper {
width: 100%;
height: 100%;
@ -9,22 +11,40 @@
top: 0;
pointer-events: none;
opacity: 0;
background: rgba(0, 0, 0, 0.5);
background: rgba(255, 255, 255, 0.5);
z-index: 4096;
transition-property: opacity;
transition-duration: 0.25s;
.window {
width: 800px;
max-height: 90%;
width: 100%;
line-height: normal;
position: relative;
background: #f3f3f3;
box-shadow: 0px 0px 17px 0px #bbb;
border-radius: 16px;
background: #fff;
box-shadow: 0px 2px 16px rgba(0, 9, 26, 0.12);
overflow-x: hidden;
overflow-y: auto;
}
.close {
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
right: 8px;
top: 8px;
color: #fff;
font-size: 22px;
font-weight: 700;
border-radius: 50%;
background: #939ba1;
cursor: pointer;
transform: rotate(45deg);
}
}
.wrapper.active {
@ -32,22 +52,15 @@
opacity: 1;
}
// .PopupWindowWrapper > .PopupWindow > .Close {
// width:16px; height:16px; line-height:16px;
// position:absolute;
// right:0; top:0;
// font-size:24px;
// padding:8px;
// transform:rotate(45deg);
// z-index:1;
// cursor:pointer;
// }
@media (min-width: $desktop) {
.wrapper {
.window {
width: 400px;
max-height: 90%;
}
// .PopupWindowWrapper {
// background:transparent;
// }
// .PopupWindowWrapper > .PopupWindow {
// background:transparent;
// box-shadow:none;
// }
// .close {
// display:flex;
// }
}
}

View File

@ -1,13 +1,13 @@
import { combineReducers } from 'redux'
import { connectRouter } from 'connected-react-router'
import dapps from '../../modules/Dapps/Dapps.reducer'
import selectedCategory from '../../modules/CategorySelector/CategorySelector.reducer'
// import selectedCategory from '../../modules/CategorySelector/CategorySelector.reducer'
import vote from '../../modules/Vote/Vote.reducer'
export default history =>
combineReducers({
router: connectRouter(history),
dapps,
selectedCategory,
// selectedCategory,
vote,
})

View File

@ -9,7 +9,7 @@ import Vote from '../Vote'
export default () => [
<Switch key={1}>
<Route exact path="/" component={Home} />
<Route path="/categories" component={Filtered} />
<Route path="/categories/:id" component={Filtered} />
<Route path="/all" component={Dapps} />
<Route path="/recently-added" component={RecentlyAdded} />
</Switch>,

View File

@ -1,12 +1,12 @@
import { connect } from 'react-redux'
import { push } from 'connected-react-router'
import { selectCategory } from '../CategorySelector/CategorySelector.reducer'
// import { selectCategory } from '../CategorySelector/CategorySelector.reducer'
import Categories from './Categories'
const mapDispatchToProps = dispatch => ({
select: category => {
dispatch(push('/categories'))
dispatch(selectCategory(category))
dispatch(push(`/categories/${category}`))
// dispatch(selectCategory(category))
},
})

View File

@ -1,13 +1,17 @@
import { connect } from 'react-redux'
import { push } from 'connected-react-router'
import CategorySelector from './CategorySelector'
import { selectCategory } from './CategorySelector.reducer'
// import { selectCategory } from './CategorySelector.reducer'
const mapStateToProps = state => ({ category: state.selectedCategory })
// const mapStateToProps = state => ({ category: state.selectedCategory })
const mapDispatchToProps = dispatch => ({
select: category => dispatch(selectCategory(category)),
select: category => {
dispatch(push(`/categories/${category}`))
//dispatch(selectCategory(category))
},
})
export default connect(
mapStateToProps,
null,
mapDispatchToProps,
)(CategorySelector)

View File

@ -1,19 +1,19 @@
import reducerUtil from '../../common/utils/reducer'
import { EXCHANGES } from '../../common/data/categories'
// import reducerUtil from '../../common/utils/reducer'
// import { EXCHANGES } from '../../common/data/categories'
const UPDATE_CATEGORY = 'UPDATE_CATEGORY'
// const UPDATE_CATEGORY = 'UPDATE_CATEGORY'
export const selectCategory = category => ({
type: UPDATE_CATEGORY,
payload: category,
})
// export const selectCategory = category => ({
// type: UPDATE_CATEGORY,
// payload: category,
// })
const initialState = EXCHANGES
// const initialState = EXCHANGES
const categoryChange = (_, category) => category
// const categoryChange = (_, category) => category
const map = {
[UPDATE_CATEGORY]: categoryChange,
}
// const map = {
// [UPDATE_CATEGORY]: categoryChange,
// }
export default reducerUtil(map, initialState)
// export default reducerUtil(map, initialState)

View File

@ -1,9 +1,10 @@
import { connect } from 'react-redux'
import Filtered from './Filtered'
import filteredDapps from './Filtered.selector'
// import filteredDapps from './Filtered.selector'
// dapps: filteredDapps(state),
const mapStateToProps = state => ({
dapps: filteredDapps(state),
dapps: state.dapps,
})
export default connect(mapStateToProps)(Filtered)

View File

@ -1,24 +1,40 @@
import React from 'react'
import PropTypes from 'prop-types'
import { DappListModel } from '../../common/utils/models'
import CategorySelector from '../CategorySelector'
import DappList from '../../common/components/DappList'
import styles from './Filtered.module.scss'
const Filtered = props => {
const { dapps } = props
const { dapps, match } = props
const result =
match !== undefined
? dapps.filter(dapp => dapp.category === match.params.id)
: dapps
return (
<>
<CategorySelector />
<CategorySelector
category={match !== undefined ? match.params.id : undefined}
/>
<div className={styles.list}>
<DappList dapps={dapps} />
<DappList dapps={result} />
</div>
</>
)
}
Filtered.defaultProps = {
match: undefined,
}
Filtered.propTypes = {
dapps: DappListModel.isRequired,
match: PropTypes.shape({
params: PropTypes.shape({
id: PropTypes.node,
}).isRequired,
}),
}
export default Filtered

View File

@ -1,10 +1,10 @@
import { createSelector } from 'reselect'
// import { createSelector } from 'reselect'
const getCategory = state => state.selectedCategory
const getDapps = state => state.dapps
// const getCategory = state => state.selectedCategory
// const getDapps = state => state.dapps
export default createSelector(
[getCategory, getDapps],
(category, dapps) =>
category ? dapps.filter(dapp => dapp.category === category) : dapps,
)
// export default createSelector(
// [getCategory, getDapps],
// (category, dapps) =>
// category ? dapps.filter(dapp => dapp.category === category) : dapps,
// )

View File

@ -18,7 +18,15 @@
@media (min-width: $desktop) {
grid-auto-flow: row;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr;
overflow-x: hidden;
}
@media (min-width: 970px) {
grid-template-columns: 1fr 1fr 1fr;
}
@media (min-width: 1300px) {
grid-template-columns: 1fr 1fr 1fr 1fr;
}
}

View File

@ -1,7 +1,13 @@
import { connect } from 'react-redux'
import Vote from './Vote'
import { closeVoteAction } from './Vote.reducer'
const mapStateToProps = state => state.vote
// const mapDispatchToProps = dispatch => ({})
const mapDispatchToProps = dispatch => ({
onClickClose: () => dispatch(closeVoteAction()),
})
export default connect(mapStateToProps)(Vote)
export default connect(
mapStateToProps,
mapDispatchToProps,
)(Vote)

View File

@ -29,12 +29,16 @@ class Vote extends Component {
}
handleChange(e) {
this.setState({ sntValue: e.target.value })
const { value } = e.target
if (value !== '' && /^[1-9][0-9]*$/.test(value) === false) return
if (parseInt(value, 10) > 1571296) return
this.setState({ sntValue: value })
}
render() {
const { isUpvote, sntValue } = this.state
const { visible } = this.props
const { visible, onClickClose } = this.props
// TODO: extract these to props
@ -50,11 +54,16 @@ class Vote extends Component {
const currentSNTamount = 23456
const categoryPosition = 2
const upvoteSNTcost = 12422
const upvoteSNTcost = currentSNTamount + parseInt(sntValue, 10)
const downvoteSNTcost = 3244
return (
<Modal visible={visible}>
<Modal
visible={visible}
onClickClose={onClickClose}
windowClassName={styles.modalWindow}
contentClassName={styles.modalContent}
>
<div className={styles.tabs}>
<button
className={isUpvote ? styles.active : ''}
@ -83,34 +92,38 @@ class Vote extends Component {
{dapp.name}
</div>
<div className={styles.items}>
{isUpvote && upvoteSNTcost > 0 && (
<span className={styles.greenBadge}>
{`${upvoteSNTcost.toLocaleString()}`}
<div className={styles.itemRow}>
<span className={styles.item}>
<img src={sntIcon} alt="SNT" width="24" height="24" />
{currentSNTamount.toLocaleString()}
</span>
)}
{!isUpvote && downvoteSNTcost > 0 && (
<span className={styles.redBadge}>
{`${downvoteSNTcost.toLocaleString()}`}
{isUpvote && upvoteSNTcost > currentSNTamount && (
<span className={styles.greenBadge}>
{`${upvoteSNTcost.toLocaleString()}`}
</span>
)}
{!isUpvote && downvoteSNTcost > 0 && (
<span className={styles.redBadge}>
{`${downvoteSNTcost.toLocaleString()}`}
</span>
)}
</div>
<div className={styles.itemRow}>
<span className={styles.item}>
<img
src={CategoriesUtils(dapp.category)}
alt={getCategoryName(dapp.category)}
width="24"
height="24"
/>
{`${getCategoryName(dapp.category)}${categoryPosition}`}
</span>
)}
<span className={styles.item}>
<img src={sntIcon} alt="SNT" width="24" height="24" />
{currentSNTamount.toLocaleString()}
</span>
{isUpvote && upvoteSNTcost > 0 && (
<span className={styles.greenBadge}>
{`${categoryPosition - 1}`}
</span>
)}
<span className={styles.item}>
<img
src={CategoriesUtils(dapp.category)}
alt={getCategoryName(dapp.category)}
width="24"
height="24"
/>
{`${getCategoryName(dapp.category)}${categoryPosition}`}
</span>
{isUpvote && upvoteSNTcost > currentSNTamount && (
<span className={styles.greenBadge}>
{`${categoryPosition - 1}`}
</span>
)}
</div>
</div>
{!isUpvote && (
<div className={styles.inputArea}>
@ -118,8 +131,13 @@ class Vote extends Component {
</div>
)}
{isUpvote && (
<div className={styles.inputArea}>
<input type="text" value={sntValue} onChange={this.handleChange} />
<div className={`${styles.inputArea} ${styles.inputAreaBorder}`}>
<input
type="text"
value={sntValue}
onChange={this.handleChange}
style={{ width: `${19 * sntValue.length}px` }}
/>
</div>
)}
@ -154,6 +172,7 @@ class Vote extends Component {
Vote.propTypes = {
visible: PropTypes.bool.isRequired,
onClickClose: PropTypes.func.isRequired,
}
export default Vote

View File

@ -1,5 +1,29 @@
@import '../../common/styles/variables';
.modalWindow {
height: 100%;
display: flex;
flex-direction: column;
}
@media (min-width: $desktop) {
.modalWindow {
height: auto;
}
}
.modalContent {
display: flex;
flex-direction: column;
flex: 1 1 auto;
}
@media (min-width: $desktop) {
.modalContent {
height: 512px;
}
}
.tabs {
width: 100%;
border-bottom: 1px solid #eef2f5;
@ -8,38 +32,143 @@
.tabs button {
color: #939ba1;
position: relative;
background: transparent;
border: none;
text-transform: uppercase;
font-family: $font;
font-size: 13px;
height: calculateRem(40);
letter-spacing: calculateRem(0.2);
display: inline-block;
width: 130px;
display: inline-flex;
align-items: center;
cursor: pointer;
}
.tabs button:first-child {
margin-right: 32px;
}
.tabs button.active:after {
display: block;
clear: both;
content: '';
position: relative;
position: absolute;
left: 0;
bottom: -9px;
height: 1px;
right: 0;
bottom: 0;
width: 24px;
border-bottom: 2px solid $link-color;
margin: 0 auto;
margin: 0 auto 0 auto;
}
.tabs button.active {
color: $link-color;
}
.dapp {
height: 48px;
display: flex;
align-items: center;
font-family: $font;
font-weight: 500;
padding: 0 calculateRem(12);
}
.dapp .image {
max-width: 24px;
max-height: 24px;
border-radius: 50%;
margin-right: calculateRem(12);
}
.items {
display: flex;
flex-direction: column;
font-family: $font;
}
.items .itemRow {
height: 32px;
display: flex;
align-items: center;
padding: 0 calculateRem(12);
}
.items .item {
display: flex;
align-items: center;
}
.items .item img {
margin-right: calculateRem(12);
}
.badge {
border-radius: 24px;
color: #ffffff;
font-family: $font;
font-size: calculateRem(15);
margin-right: calculateRem(16);
margin-left: auto;
padding: calculateRem(3) calculateRem(10);
}
.greenBadge {
@extend .badge;
background: #44d058;
}
.redBadge {
@extend .badge;
background: #f00;
}
.inputArea {
width: calc(100% - 2 * 16px);
height: 64px;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
margin: auto;
}
.inputArea.inputAreaBorder {
border-bottom: 1px solid #eef2f5;
}
.inputArea input {
width: 19px;
border: none;
text-align: center;
font-size: calculateRem(32);
line-height: calculateRem(28);
font-family: $font;
margin-right: calculateRem(6);
}
.inputArea input:focus {
outline: none;
}
.inputArea::after {
transition: all 0.05s ease-in-out;
content: 'SNT';
color: #939ba1;
font-size: calculateRem(32);
line-height: calculateRem(28);
font-family: $font;
}
.inputArea span {
font-size: calculateRem(32);
line-height: calculateRem(28);
font-family: $font;
margin-right: calculateRem(6);
}
.footer {
// position: fixed;
bottom: 0;
width: 100%;
text-align: center;
}
@ -73,99 +202,3 @@
text-decoration: none;
color: $link-color;
}
.item,
.dapp {
line-height: 22px;
font-family: $font;
font-size: calculateRem(15);
display: block;
height: calculateRem(32);
padding: 0 calculateRem(12);
}
.item img,
.dapp img {
margin-right: calculateRem(12);
vertical-align: middle;
}
.dapp {
margin-bottom: calculateRem(12);
font-size: calculateRem(17);
font-weight: 500;
}
.dapp img {
vertical-align: bottom;
}
.badge {
float: right;
border-radius: 24px;
color: #ffffff;
font-family: $font;
font-size: calculateRem(15);
margin-right: calculateRem(16);
padding: calculateRem(3) calculateRem(10);
}
.greenBadge {
@extend .badge;
background: #44d058;
}
.redBadge {
@extend .badge;
background: #f00;
}
.inputArea {
text-align: center;
width: 300px;
position: relative;
// position: fixed;
top: 40%;
}
.inputArea input {
margin: auto;
width: 50%;
border: none;
border-bottom: 1px solid #eef2f5;
text-align: center;
font-size: calculateRem(32);
line-height: calculateRem(28);
font-family: $font;
}
.inputArea input:focus {
outline: none;
}
.inputArea::after {
position: absolute;
top: 5px;
right: 0;
transition: all 0.05s ease-in-out;
content: 'SNT';
color: #939ba1;
font-size: calculateRem(32);
line-height: calculateRem(28);
font-family: $font;
}
.inputArea span {
font-size: calculateRem(32);
line-height: calculateRem(28);
font-family: $font;
position: relative;
top: 5px;
}
.image {
max-width: calculateRem(40);
max-height: calculateRem(40);
margin-top: calculateRem(15);
margin-right: calculateRem(16);
}

View File

@ -3,6 +3,7 @@ import reducerUtil from '../../common/utils/reducer'
const SHOW_UP_VOTE = 'SHOW_UP_VOTE'
const SHOW_DOWN_VOTE = 'SHOW_DOWN_VOTE'
const CLOSE_VOTE = 'CLOSE_VOTE'
export const showUpVoteAction = () => ({
type: SHOW_UP_VOTE,
@ -14,6 +15,11 @@ export const showDownVoteAction = () => ({
payload: null,
})
export const closeVoteAction = () => ({
type: CLOSE_VOTE,
payload: null,
})
const showUpVote = state => {
return Object.assign({}, state, {
visible: true,
@ -26,9 +32,16 @@ const showDownVote = state => {
})
}
const closeVote = state => {
return Object.assign({}, state, {
visible: false,
})
}
const map = {
[SHOW_UP_VOTE]: showUpVote,
[SHOW_DOWN_VOTE]: showDownVote,
[CLOSE_VOTE]: closeVote,
}
export default reducerUtil(map, voteState)