Refactor, tweaks, clean-up, improve
This commit is contained in:
+27
-29
@@ -4,6 +4,12 @@ body {
|
||||
color: white;
|
||||
background-color: #111;
|
||||
font-family: Verdana;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
// Normalize etc.
|
||||
ul {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -28,6 +34,17 @@ a {
|
||||
}
|
||||
}
|
||||
|
||||
.yellow { color: $color-yellow; }
|
||||
.green { color: $quality-2; }
|
||||
|
||||
.tight {
|
||||
margin-bottom: 0;
|
||||
|
||||
& + p {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.calculator {
|
||||
&__points {
|
||||
color: white;
|
||||
@@ -69,35 +86,16 @@ a {
|
||||
}
|
||||
}
|
||||
|
||||
.class-picker {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
.index {
|
||||
min-height: 85vh;
|
||||
}
|
||||
|
||||
.index__class-picker {
|
||||
margin: 2em 0;
|
||||
}
|
||||
|
||||
&--center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__class {
|
||||
margin-right: 1em;
|
||||
opacity: 1;
|
||||
transition: all .1s ease-out;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&--active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&--inactive {
|
||||
opacity: .4;
|
||||
|
||||
&:hover {
|
||||
opacity: .5;
|
||||
}
|
||||
}
|
||||
}
|
||||
footer {
|
||||
padding: 2em;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
+9
-3
@@ -1,8 +1,8 @@
|
||||
import React from 'react'
|
||||
import './App.scss'
|
||||
import { IndexRoute } from './components/IndexRoute'
|
||||
import { PlaygroundRoute } from './components/PlaygroundRoute'
|
||||
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
|
||||
import { Playground } from './components/Playground'
|
||||
import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom'
|
||||
|
||||
const App: React.FC = () => {
|
||||
return (
|
||||
@@ -10,9 +10,15 @@ const App: React.FC = () => {
|
||||
{/* <Router basename={process.env.NODE_ENV !== 'development' ? '%PUBLIC_URL%' : ''}> */}
|
||||
<div className="App">
|
||||
<Switch>
|
||||
<Route exact path="/playground" component={PlaygroundRoute} />
|
||||
<Route exact path="/playground" component={Playground} />
|
||||
<Route path="/:selectedClass?/:pointString?" component={IndexRoute} />
|
||||
</Switch>
|
||||
|
||||
<footer>
|
||||
<Link to="/">Home</Link>
|
||||
{' - '}
|
||||
<Link to="/playground">Components</Link>
|
||||
</footer>
|
||||
</div>
|
||||
</Router>
|
||||
);
|
||||
|
||||
@@ -5,13 +5,12 @@ import {
|
||||
modifyTalentPoint,
|
||||
calcAvailablePoints,
|
||||
encodeKnownTalents,
|
||||
SORT_TALENTS_BY_SPEC
|
||||
} from '../lib/tree'
|
||||
import { talentsBySpec, talentsById } from '../data/talents'
|
||||
import { talentsBySpec } from '../data/talents'
|
||||
import { classByName } from '../data/classes'
|
||||
import { History } from 'history'
|
||||
import { spells } from '../data/spells'
|
||||
import { debugPrintKnown } from '../lib/debug'
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
interface Props {
|
||||
selectedClass: string
|
||||
@@ -96,10 +95,11 @@ export class Calculator extends React.PureComponent<Props> {
|
||||
Points: {availablePoints}
|
||||
</div>
|
||||
|
||||
<h4>Quick links</h4>
|
||||
<ul>
|
||||
<li><a href="/shaman/-5505000055523051-55">Shaman test</a></li>
|
||||
<li><a href="/shaman/-5595000055523051-55">Shaman test broken</a></li>
|
||||
<li><a href="/rogue/325323125551351-3253552122555155231-55225313333212151">Full Rogue</a></li>
|
||||
<li><Link to="/shaman/-5505000055523051-55">Shaman test</Link></li>
|
||||
<li><Link to="/shaman/-5595000055523051-55">Shaman test broken</Link></li>
|
||||
<li><Link to="/rogue/325323125551351-3253552122555155231-55225313333212151">Full Rogue (shouldn't be possible)</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
.class-picker {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
|
||||
&--center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__class {
|
||||
margin-right: 1em;
|
||||
opacity: 1;
|
||||
transition: all .1s ease-out;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&--active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&--inactive {
|
||||
opacity: .4;
|
||||
|
||||
&:hover {
|
||||
opacity: .5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import './ClassPicker.scss'
|
||||
import React from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { classByName } from '../data/classes'
|
||||
@@ -8,6 +9,7 @@ interface Props {
|
||||
/** Name of the selected class, lowercase */
|
||||
selected?: string
|
||||
center?: boolean
|
||||
className?: string
|
||||
}
|
||||
|
||||
const classNameForItem = (c: ClassData, selected: string) => classNames('class-picker__class', {
|
||||
@@ -24,7 +26,7 @@ export class ClassPicker extends React.PureComponent<Props> {
|
||||
const cn = classNames('class-picker', {
|
||||
'class-picker--has-selection': !!selected,
|
||||
'class-picker--center': center,
|
||||
})
|
||||
}, this.props.className)
|
||||
|
||||
return (
|
||||
<ul className={cn}>
|
||||
|
||||
@@ -60,17 +60,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
&--loading {
|
||||
&--loaded {
|
||||
.icon__bg {
|
||||
opacity: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.icon--fade-in .icon__bg {
|
||||
animation: fadeIn .1s forwards;
|
||||
}
|
||||
}
|
||||
|
||||
&__bg {
|
||||
position: absolute;
|
||||
background-size: cover;
|
||||
opacity: 1;
|
||||
transition: all 100ms ease-out;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&__frame {
|
||||
@@ -79,4 +82,5 @@
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+24
-7
@@ -3,31 +3,48 @@ import classNames from 'classnames'
|
||||
import './Icon.scss'
|
||||
|
||||
interface Props {
|
||||
name: string
|
||||
name?: string
|
||||
size?: 'small' | 'medium' | 'large'
|
||||
golden?: boolean
|
||||
}
|
||||
|
||||
export const Icon: FC<Props> = ({ name, size = 'medium', golden = false, children }) => {
|
||||
const [hasLoadedImage, setHasLoadedImage] = useState(false)
|
||||
const NOT_FOUND_ICON = 'inv_misc_questionmark'
|
||||
|
||||
export const Icon: FC<Props> = ({ name: defaultName, size = 'medium', golden = false, children }) => {
|
||||
const [hasLoadedImage, setLoadedImage] = useState(false)
|
||||
const [fadeIn, setFadeIn] = useState(false)
|
||||
const [name, setName] = useState(defaultName)
|
||||
|
||||
const bgSize = size !== 'small' ? 'large' : 'medium'
|
||||
const url = `https://wow.zamimg.com/images/wow/icons/${bgSize}/${name}.jpg`
|
||||
|
||||
const start = Date.now()
|
||||
|
||||
useEffect(() => {
|
||||
if (!name) return
|
||||
const img = new Image()
|
||||
img.onload = () => setHasLoadedImage(true)
|
||||
img.onload = () => {
|
||||
const loadTime = Date.now() - start
|
||||
if (loadTime >= 300) {
|
||||
setFadeIn(true)
|
||||
}
|
||||
setLoadedImage(true)
|
||||
}
|
||||
img.onerror = () => setName(NOT_FOUND_ICON)
|
||||
img.src = url
|
||||
}, [])
|
||||
}, [name, url])
|
||||
|
||||
const className = classNames('icon', `icon--${size}`, {
|
||||
'icon--golden': golden,
|
||||
'icon--loading': !hasLoadedImage
|
||||
'icon--loaded': hasLoadedImage,
|
||||
'icon--fade-in': fadeIn,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className="icon__bg" style={{ backgroundImage: `url(${url})` }} />
|
||||
{name &&
|
||||
<div className="icon__bg" style={{ backgroundImage: `url(${url})` }} />
|
||||
}
|
||||
<div className="icon__frame" />
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -33,7 +33,11 @@ export class IndexRoute extends React.PureComponent<Props> {
|
||||
|
||||
return (
|
||||
<div className="index">
|
||||
<ClassPicker center selected={selectedClass} />
|
||||
<ClassPicker
|
||||
className="index__class-picker"
|
||||
center
|
||||
selected={selectedClass}
|
||||
/>
|
||||
|
||||
{selectedClass &&
|
||||
<Calculator
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
.playground-section {
|
||||
margin-bottom: 2em;
|
||||
padding-bottom: 2em;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, .1);
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import './Playground.scss'
|
||||
import React, { FC } from 'react'
|
||||
import { ClassPicker } from './ClassPicker'
|
||||
import { RouteComponentProps } from 'react-router'
|
||||
import { Icon } from './Icon';
|
||||
import { Tooltip } from './Tooltip';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
//
|
||||
}
|
||||
|
||||
const iconNames = [
|
||||
'',
|
||||
'this_doesnot_exist_and_will_cause_default_icon',
|
||||
'spell_holy_prayerofhealing',
|
||||
'ability_sap',
|
||||
'class_shaman',
|
||||
'inv_ammo_firetar',
|
||||
'spell_shadow_requiem',
|
||||
]
|
||||
|
||||
const DEEP_WOUNDS = <Tooltip title="Deep Wounds" fixed>
|
||||
<p className="tight">Rank 1/3</p>
|
||||
<p className="yellow">Your critical strikes cause the opponent to bleed, dealing 20% of your melee weapon's average damage over 12 sec.</p>
|
||||
|
||||
<p className="tight">Next rank:</p>
|
||||
<p className="yellow">Your critical strikes cause the opponent to bleed, dealing 40% of your melee weapon's average damage over 12 sec.</p>
|
||||
|
||||
<p className="green">Click to learn</p>
|
||||
</Tooltip>
|
||||
|
||||
const Section: FC<any> = (props) => {
|
||||
return <div className="playground-section">
|
||||
<div className="container">
|
||||
<h2>{props.title}</h2>
|
||||
|
||||
{props.children}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
export class Playground extends React.PureComponent<Props> {
|
||||
static whyDidYouRender = true
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="playground">
|
||||
<Section title="Class Picker">
|
||||
<ClassPicker />
|
||||
</Section>
|
||||
|
||||
<Section title="Icons">
|
||||
<h3>Small Icons</h3>
|
||||
<div className="inline-items">
|
||||
{iconNames.map((n) => <Icon key={n} name={n} size="small" />)}
|
||||
</div>
|
||||
|
||||
<h3>Medium Icons</h3>
|
||||
<div className="inline-items">
|
||||
{iconNames.map((n) => <Icon key={n} name={n} size="medium" />)}
|
||||
</div>
|
||||
|
||||
<h3>Large Icons</h3>
|
||||
<div className="inline-items">
|
||||
{iconNames.map((n) => <Icon key={n} name={n} size="large" />)}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section title="Tooltips">
|
||||
<h3>No params</h3>
|
||||
<Tooltip />
|
||||
|
||||
<h3>Simple text content</h3>
|
||||
<Tooltip children="Sanctuary Post" />
|
||||
|
||||
<h3>Using HTML inside</h3>
|
||||
<Tooltip>
|
||||
<strong>I can use normal HTML in here</strong>
|
||||
<br />
|
||||
And even <a href="/warrior">link</a> to exciting places!
|
||||
</Tooltip>
|
||||
|
||||
<h3>Fixed width</h3>
|
||||
{DEEP_WOUNDS}
|
||||
|
||||
<h3>Overriding fixed width</h3>
|
||||
{React.cloneElement(DEEP_WOUNDS, {
|
||||
width: '500px'
|
||||
})}
|
||||
|
||||
<h3>No fixed</h3>
|
||||
{React.cloneElement(DEEP_WOUNDS, {
|
||||
fixed: false
|
||||
})}
|
||||
</Section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
import React from 'react'
|
||||
import { ClassPicker } from './ClassPicker'
|
||||
import { match } from 'react-router-dom'
|
||||
import { RouteComponentProps } from 'react-router'
|
||||
import { Icon } from './Icon';
|
||||
import { Tooltip } from './Tooltip';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
match: match<{
|
||||
selectedClass: string
|
||||
pointString: string
|
||||
}>
|
||||
}
|
||||
|
||||
const iconNames = [
|
||||
'foo',
|
||||
'spell_holy_prayerofhealing',
|
||||
'ability_sap',
|
||||
'class_shaman',
|
||||
'inv_ammo_firetar',
|
||||
'spell_shadow_requiem',
|
||||
]
|
||||
|
||||
export class PlaygroundRoute extends React.PureComponent<Props> {
|
||||
static whyDidYouRender = true
|
||||
|
||||
render() {
|
||||
const { match, history } = this.props
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="playground container">
|
||||
<h2>Class Picker</h2>
|
||||
<ClassPicker />
|
||||
|
||||
<h2>Icons</h2>
|
||||
|
||||
<h3>Small Icons</h3>
|
||||
<div className="inline-items">
|
||||
{iconNames.map((n) => <Icon key={n} name={n} size="small" />)}
|
||||
</div>
|
||||
|
||||
<h3>Medium Icons</h3>
|
||||
<div className="inline-items">
|
||||
{iconNames.map((n) => <Icon key={n} name={n} size="medium" />)}
|
||||
</div>
|
||||
|
||||
<h3>Large Icons</h3>
|
||||
<div className="inline-items">
|
||||
{iconNames.map((n) => <Icon key={n} name={n} size="large" />)}
|
||||
</div>
|
||||
|
||||
<h2>Tooltip</h2>
|
||||
<Tooltip inline />
|
||||
|
||||
<Tooltip>
|
||||
<strong>I can use normal HTML in here</strong>
|
||||
<br />
|
||||
And even <a href="#">link</a> to exciting places!
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="Sanctuary Post" />
|
||||
|
||||
<Tooltip title="Fixed width" fixed />
|
||||
|
||||
<Tooltip title="Override fixed width" fixed width="600px" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -66,4 +66,16 @@
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 14px;
|
||||
line-height: 1.3;
|
||||
// margin-bottom: 1px;
|
||||
}
|
||||
|
||||
&__body {
|
||||
p:last-child {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ export const Tooltip: FC<Props> = (props) => {
|
||||
<div className="tooltip__inner">
|
||||
<div className="tooltip__top">
|
||||
<div className="tooltip__body">
|
||||
{title}
|
||||
{title && <div className="tooltip__title tight">{title}</div>}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { List, Map, fromJS } from 'immutable'
|
||||
import { Map } from 'immutable'
|
||||
import {
|
||||
talentsBySpec,
|
||||
talentToSpec,
|
||||
|
||||
+21
-5
@@ -7,11 +7,13 @@ $col-offset: 44px;
|
||||
$col-gutter: 16px;
|
||||
$col-distance: $icon-size + $col-gutter;
|
||||
|
||||
$color-yellow: #ffd100;
|
||||
$color-green: #1eff00;
|
||||
$color-dark-green: #40bf40;
|
||||
$color-subtle: #9d9d9d;
|
||||
$color-icon-overlay: #6396d6;
|
||||
// Item quality colours
|
||||
$quality-0: #9d9d9d;
|
||||
$quality-1: #fff;
|
||||
$quality-2: #1eff00;
|
||||
$quality-3: #0070dd;
|
||||
$quality-4: #a335ee;
|
||||
$quality-5: #ff8000;
|
||||
|
||||
// Class colours
|
||||
$color-warrior: #c69b6d;
|
||||
@@ -23,3 +25,17 @@ $color-shaman: #2359ff;
|
||||
$color-mage: #68ccef;
|
||||
$color-warlock: #9382c9;
|
||||
$color-druid: #ff7c0a;
|
||||
|
||||
// Colours used in app
|
||||
$color-yellow: #ffd100;
|
||||
$color-green: $quality-2;
|
||||
$color-dark-green: #40bf40;
|
||||
$color-subtle: $quality-0;
|
||||
$color-icon-overlay: #6396d6;
|
||||
|
||||
$color-description: $color-yellow;
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
Reference in New Issue
Block a user