Add support for right-down arrows

This commit is contained in:
Melvin Valster
2019-07-24 13:25:57 +02:00
parent d5121960a1
commit 94a0a97fbf
6 changed files with 180 additions and 66 deletions
+1 -1
View File
@@ -3,9 +3,9 @@
- [ ] General: Add redux - [ ] General: Add redux
- [ ] General: Talent tooltips - [ ] General: Talent tooltips
- [ ] General: Responsive on mobile - [ ] General: Responsive on mobile
- [ ] Talent tree: Arrows for dependencies (all done except for right-down arrow)
- [ ] Talent tree: Reset button per tree (?) - [ ] Talent tree: Reset button per tree (?)
- [ ] Fix: Initial load `pointString` validation (make sure all talents are valid and their deps are met) - [ ] Fix: Initial load `pointString` validation (make sure all talents are valid and their deps are met)
- [x] Talent tree: Arrows for dependencies
- [x] System: Generate URL for chosen talents - [x] System: Generate URL for chosen talents
- [x] Talent tree: Prettier talent frames - [x] Talent tree: Prettier talent frames
- [x] Talent tree: Colour markings on icons - [x] Talent tree: Colour markings on icons
+126 -38
View File
@@ -13,7 +13,39 @@
} }
@function calcArrowHeight($length) { @function calcArrowHeight($length) {
@return 2px + ($row-offset * $length) + ($icon-size * ($length - 1)) @return 2px + ($row-offset * $length) + ($icon-size * ($length - 1));
}
@mixin arrow-left {
background-image: url('../images/arrows/left.png');
background-position: center left;
&.arrow--active {
background-image: url('../images/arrows/left-active.png');
}
// Cols
@for $i from 0 through 3 {
&[data-col="#{$i}"] {
left: -3px + calcLeftOffset($i);
}
}
}
@mixin arrow-right {
background-image: url('../images/arrows/right.png');
background-position: center right;
&.arrow--active {
background-image: url('../images/arrows/right-active.png');
}
// Cols
@for $i from 0 through 3 {
&[data-col="#{$i}"] {
left: 3px + calcRightOffset($i);
}
}
} }
.arrow { .arrow {
@@ -40,35 +72,11 @@
} }
&--right { &--right {
background-image: url('../images/arrows/right.png'); @include arrow-right()
background-position: center right;
&.arrow--active {
background-image: url('../images/arrows/right-active.png');
}
// Cols
@for $i from 0 through 3 {
&[data-col="#{$i}"] {
left: 3px + calcRightOffset($i);
}
}
} }
&--left { &--left {
background-image: url('../images/arrows/left.png'); @include arrow-left()
background-position: center left;
&.arrow--active {
background-image: url('../images/arrows/left-active.png');
}
// Cols
@for $i from 0 through 3 {
&[data-col="#{$i}"] {
left: -3px + calcLeftOffset($i);
}
}
} }
&--down { &--down {
@@ -83,7 +91,7 @@
// Rows // Rows
@for $i from 0 through 6 { @for $i from 0 through 6 {
&[data-row="#{$i}"] { &[data-row="#{$i}"] {
top: 40px + baseRowTopOffset($i); top: $icon-size + baseRowTopOffset($i);
} }
} }
@@ -102,23 +110,103 @@
} }
} }
&--right-down { &--right-down,
// Horizontal &--left-down {
::before { // Position based on row
content: ""; @for $i from 0 through 6 {
position: absolute; &[data-row="#{$i}"] {
height: $arrow-width; top: 12px + baseRowTopOffset($i);
background-image: url('../images/arrows/rightdown.png');
background-position: center right; &:after {
top: $arrow-width;
}
}
} }
// Vertical // Width
::after { @for $i from 0 through 3 {
&[data-width="#{$i}"] {
width: 2px + ($icon-size * $i);
}
}
// Height
@for $i from 0 through 3 {
&[data-height="#{$i}"] {
height: 8px + ($row-offset + ($icon-size * .5)) * $i;
}
}
// Horizontal
&:before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: $arrow-width;
}
// Arrow down
&:after {
content: ""; content: "";
position: absolute; position: absolute;
width: $arrow-width; width: $arrow-width;
top: $arrow-width;
right: 0;
bottom: -3px;
background-image: url('../images/arrows/down.png'); background-image: url('../images/arrows/down.png');
background-position: center bottom; background-position: center bottom;
} }
} }
&--right-down {
// Positioning based on cols
@for $i from 0 through 3 {
&[data-col="#{$i}"] {
left: 2px + calcRightOffset($i);
}
}
&.arrow--active {
&:before { background-image: url('../images/arrows/rightdown-active.png'); }
&:after { background-image: url('../images/arrows/down-active.png'); }
}
// Arrow to the right
&:before {
background-image: url('../images/arrows/rightdown.png');
background-position: center right;
}
// Arrow down
&:after {
right: 0;
}
}
&--left-down {
// Positioning based on cols
@for $i from 0 through 3 {
&[data-col="#{$i}"] {
left: -2px + calcLeftOffset($i);
}
}
&.arrow--active {
&:before { background-image: url('../images/arrows/leftdown-active.png'); }
&:after { background-image: url('../images/arrows/down-active.png'); }
}
// Arrow to the left
&:before {
background-image: url('../images/arrows/leftdown.png');
background-position: center left;
}
// Arrow down
&:after {
left: 0;
}
}
} }
+27 -10
View File
@@ -8,23 +8,40 @@ interface Props {
active?: boolean active?: boolean
} }
export const Arrow: FC<Props> = ({ from, to, active = false }) => { const getDirection = (from: TalentData, to: TalentData): string => {
const length = to.row === from.row if (to.row > from.row && to.col === from.col) {
? Math.abs(to.col - from.col) return 'down'
: to.row - from.row } else if (to.row === from.row && to.col > from.col) {
return 'right'
} else if (to.row === from.row && to.col < from.col) {
return 'left'
} else if (to.row > from.row && to.col === from.col + 1) {
return 'right-down'
} else if (to.row > from.row && to.col === from.col - 1) {
return 'left-down'
}
throw new Error(`Could not determine direction from (row ${from.row}, col ${from.col}) to (row ${to.row}, col ${to.col})`)
}
export const Arrow: FC<Props> = ({ from, to, active = false }) => {
const props = { const props = {
'data-col': from.col, 'data-col': from.col,
'data-row': from.row, 'data-row': from.row,
'data-length': length,
} }
const className = classNames('arrow', { const height = to.row - from.row
const width = Math.abs(to.col - from.col)
const dir = getDirection(from, to)
if (dir === 'right-down' || dir === 'left-down') {
props['data-height'] = height
props['data-width'] = width
} else {
props['data-length'] = to.row === from.row ? width : height
}
const className = classNames('arrow', `arrow--${dir}`, {
'arrow--active': active, 'arrow--active': active,
'arrow--down': to.row > from.row,
'arrow--right': to.row === from.row && to.col > from.col,
'arrow--left': to.row === from.row && to.col < from.col,
'arrow--right-down': to.row === from.row + 1 && to.col === from.col + 1
}) })
return <div className={className} {...props} /> return <div className={className} {...props} />
+8
View File
@@ -38,10 +38,18 @@
} }
} }
&--loading {
.icon__bg {
opacity: 0;
}
}
&__bg { &__bg {
position: absolute; position: absolute;
background-size: cover; background-size: cover;
border-radius: 5px; border-radius: 5px;
opacity: 1;
transition: all 100ms ease-out;
} }
&__frame { &__frame {
+15 -11
View File
@@ -1,4 +1,4 @@
import React, { FC } from 'react' import React, { FC, useState, useEffect } from 'react'
import classNames from 'classnames' import classNames from 'classnames'
import './Icon.scss' import './Icon.scss'
@@ -9,21 +9,25 @@ interface Props {
} }
export const Icon: FC<Props> = ({ name, size = 'medium', golden = false, children }) => { export const Icon: FC<Props> = ({ name, size = 'medium', golden = false, children }) => {
const className = classNames( const [hasLoadedImage, setHasLoadedImage] = useState(false)
'icon',
`icon--${size}`, {
'icon--golden': golden
}
)
const bgSize = size === 'medium' ? 'large' : 'medium' const bgSize = size === 'medium' ? 'large' : 'medium'
const bgStyle = { const url = `https://wow.zamimg.com/images/wow/icons/${bgSize}/${name}.jpg`
backgroundImage: `url(https://wow.zamimg.com/images/wow/icons/${bgSize}/${name}.jpg)`
} useEffect(() => {
const img = new Image()
img.onload = () => setHasLoadedImage(true)
img.src = url
}, [])
const className = classNames('icon', `icon--${size}`, {
'icon--golden': golden,
'icon--loading': !hasLoadedImage
})
return ( return (
<div className={className}> <div className={className}>
<div className="icon__bg" style={bgStyle} /> <div className="icon__bg" style={{ backgroundImage: `url(${url})` }} />
<div className="icon__frame" /> <div className="icon__frame" />
{children} {children}
</div> </div>
+3 -6
View File
@@ -14,6 +14,7 @@ interface Props {
export const TalentTree: React.FC<Props> = ({ specId, knownTalents, availablePoints, onTalentPress }) => { export const TalentTree: React.FC<Props> = ({ specId, knownTalents, availablePoints, onTalentPress }) => {
const talents = Object.values(talentsBySpec[specId]) const talents = Object.values(talentsBySpec[specId])
const bgImg = require(`../images/specs/${specId}.jpg`)
const handleClick = useCallback( const handleClick = useCallback(
(talentId) => onTalentPress(specId, talentId, 1), (talentId) => onTalentPress(specId, talentId, 1),
@@ -24,12 +25,8 @@ export const TalentTree: React.FC<Props> = ({ specId, knownTalents, availablePoi
[specId, onTalentPress] [specId, onTalentPress]
) )
const bodyStyle = {
backgroundImage: `url(${require(`../images/specs/${specId}.jpg`)})`
}
const arrows = talents const arrows = talents
.filter((talent) => talent.requires.length > 0) .filter((talent) => talent.requires.length)
.map((talent) => { .map((talent) => {
return <Arrow return <Arrow
key={talent.id} key={talent.id}
@@ -45,7 +42,7 @@ export const TalentTree: React.FC<Props> = ({ specId, knownTalents, availablePoi
<h3>{specNames[specId]} ({getPointsInSpec(specId, knownTalents)})</h3> <h3>{specNames[specId]} ({getPointsInSpec(specId, knownTalents)})</h3>
</div> </div>
<div className="tree__body" style={bodyStyle}> <div className="tree__body" style={{ backgroundImage: `url(${bgImg})` }}>
{talents.map((talent) => {talents.map((talent) =>
<TalentSlot <TalentSlot
key={talent.id} key={talent.id}