Add support for right-down arrows
This commit is contained in:
@@ -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
@@ -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
@@ -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} />
|
||||||
|
|||||||
@@ -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
@@ -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>
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
Reference in New Issue
Block a user