Add redux, fix routing, add normalize
This commit is contained in:
@@ -1,12 +1,8 @@
|
|||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
- [ ] 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)
|
||||||
- [ ] Fix: Navigating between talent links for same class does not trigger re-render
|
- [ ] Fix: Tooltips cause horizontal scroll on less-wide screens. Investigate.
|
||||||
|
|
||||||
- [ ] Styling:
|
|
||||||
- [ ] SCSS: Normalize
|
|
||||||
- [ ] General:
|
|
||||||
- [ ] Add redux
|
|
||||||
- [ ] Responsiveness:
|
- [ ] Responsiveness:
|
||||||
- [ ] Tooltips on mobile need different UX
|
- [ ] Tooltips on mobile need different UX
|
||||||
- [ ] Talent tree:
|
- [ ] Talent tree:
|
||||||
|
|||||||
@@ -14,8 +14,10 @@
|
|||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.8.6",
|
"react-dom": "^16.8.6",
|
||||||
"react-loadable": "^5.5.0",
|
"react-loadable": "^5.5.0",
|
||||||
|
"react-redux": "^7.1.0",
|
||||||
"react-router-dom": "^5.0.1",
|
"react-router-dom": "^5.0.1",
|
||||||
"react-scripts": "3.0.1",
|
"react-scripts": "3.0.1",
|
||||||
|
"redux": "^4.0.4",
|
||||||
"typescript": "3.5.3"
|
"typescript": "3.5.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -47,11 +49,13 @@
|
|||||||
"@types/cheerio": "^0.22.12",
|
"@types/cheerio": "^0.22.12",
|
||||||
"@types/node-fetch": "^2.5.0",
|
"@types/node-fetch": "^2.5.0",
|
||||||
"@types/react-loadable": "^5.5.1",
|
"@types/react-loadable": "^5.5.1",
|
||||||
|
"@types/react-redux": "^7.1.1",
|
||||||
"@types/request": "^2.48.2",
|
"@types/request": "^2.48.2",
|
||||||
"@welldone-software/why-did-you-render": "^3.2.1",
|
"@welldone-software/why-did-you-render": "^3.2.1",
|
||||||
"cheerio": "^1.0.0-rc.3",
|
"cheerio": "^1.0.0-rc.3",
|
||||||
"gh-pages": "^2.0.1",
|
"gh-pages": "^2.0.1",
|
||||||
"node-fetch": "^2.6.0",
|
"node-fetch": "^2.6.0",
|
||||||
|
"redux-devtools": "^3.5.0",
|
||||||
"request": "^2.88.0",
|
"request": "^2.88.0",
|
||||||
"ts-node": "^8.3.0"
|
"ts-node": "^8.3.0"
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
|
||||||
import './App.scss'
|
import './App.scss'
|
||||||
|
import React from 'react'
|
||||||
import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom'
|
import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom'
|
||||||
import Loadable from 'react-loadable'
|
import Loadable from 'react-loadable'
|
||||||
import { PageLoader } from './components/PageLoader'
|
import { PageLoader } from './components/PageLoader'
|
||||||
@@ -17,7 +17,6 @@ const LoadablePlayground = Loadable({
|
|||||||
const App: React.FC = () => {
|
const App: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
{/* <Router basename={process.env.NODE_ENV !== 'development' ? '%PUBLIC_URL%' : ''}> */}
|
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<main>
|
<main>
|
||||||
<Switch>
|
<Switch>
|
||||||
|
|||||||
@@ -2,21 +2,18 @@ import './Calculator.scss'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Map } from 'immutable'
|
import { Map } from 'immutable'
|
||||||
import { TalentTree } from './TalentTree'
|
import { TalentTree } from './TalentTree'
|
||||||
import {
|
import { calcAvailablePoints } from '../lib/tree'
|
||||||
modifyTalentPoint,
|
import { classById } from '../data/classes'
|
||||||
calcAvailablePoints,
|
|
||||||
encodeKnownTalents,
|
|
||||||
} from '../lib/tree'
|
|
||||||
import { talentsBySpec } from '../data/talents'
|
|
||||||
import { classByName } from '../data/classes'
|
|
||||||
import { History } from 'history'
|
|
||||||
// import { debugPrintKnown } from '../lib/debug'
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { addPoint, removePoint } from '../store/calculator/actions'
|
||||||
|
import { Points } from '../store/calculator/types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
selectedClass: string
|
classId: number
|
||||||
history: History
|
points: Points
|
||||||
initialTalents?: Map<number, number>
|
addPoint: typeof addPoint
|
||||||
|
removePoint: typeof removePoint
|
||||||
}
|
}
|
||||||
|
|
||||||
const EMPTY_TALENTS = Map<number, number>()
|
const EMPTY_TALENTS = Map<number, number>()
|
||||||
@@ -28,46 +25,19 @@ export class Calculator extends React.PureComponent<Props> {
|
|||||||
knownTalents: EMPTY_TALENTS
|
knownTalents: EMPTY_TALENTS
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
if (this.props.initialTalents) {
|
|
||||||
this.setState({ knownTalents: this.props.initialTalents })
|
|
||||||
this.updateURL(this.props.initialTalents)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps: Props) {
|
|
||||||
if (prevProps.selectedClass !== this.props.selectedClass) {
|
|
||||||
this.setState({
|
|
||||||
knownTalents: EMPTY_TALENTS
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateURL(knownTalents: Map<number, number>) {
|
|
||||||
const { selectedClass } = this.props
|
|
||||||
const pointString = encodeKnownTalents(knownTalents, selectedClass)
|
|
||||||
this.props.history.replace(`/${selectedClass}` + (pointString ? `/${pointString}` : ''))
|
|
||||||
}
|
|
||||||
|
|
||||||
handleTalentPress = (specId: number, talentId: number, modifier: 1 | -1) => {
|
handleTalentPress = (specId: number, talentId: number, modifier: 1 | -1) => {
|
||||||
const talent = talentsBySpec[specId][talentId]
|
if (modifier === 1) {
|
||||||
console.log('Clicked talent: ', talentId)
|
this.props.addPoint(talentId)
|
||||||
|
} else {
|
||||||
const newKnownTalents = modifyTalentPoint(this.state.knownTalents, talent, modifier)
|
this.props.removePoint(talentId)
|
||||||
if (newKnownTalents !== this.state.knownTalents) {
|
|
||||||
this.updateURL(newKnownTalents)
|
|
||||||
}
|
}
|
||||||
this.setState({ knownTalents: newKnownTalents })
|
|
||||||
|
|
||||||
// Debug
|
|
||||||
// debugPrintKnown(newKnownTalents)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { selectedClass } = this.props
|
const { classId } = this.props
|
||||||
const { knownTalents } = this.state
|
const knownTalents = this.props.points
|
||||||
|
|
||||||
const classData = classByName[selectedClass]
|
const classData = classById[classId]
|
||||||
const availablePoints = calcAvailablePoints(knownTalents)
|
const availablePoints = calcAvailablePoints(knownTalents)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -99,3 +69,10 @@ export class Calculator extends React.PureComponent<Props> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
addPoint,
|
||||||
|
removePoint
|
||||||
|
}
|
||||||
|
)(Calculator)
|
||||||
+80
-16
@@ -1,33 +1,90 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Calculator } from '../components/Calculator'
|
import { connect } from 'react-redux'
|
||||||
import { ClassPicker } from '../components/ClassPicker'
|
|
||||||
import { match } from 'react-router-dom'
|
import { match } from 'react-router-dom'
|
||||||
import { RouteComponentProps } from 'react-router'
|
import { RouteComponentProps } from 'react-router'
|
||||||
import { decodeKnownTalents } from '../lib/tree'
|
import Calculator from '../components/Calculator'
|
||||||
import { classByName } from '../data/classes'
|
import { ClassPicker } from '../components/ClassPicker'
|
||||||
|
import { classByName, classById } from '../data/classes'
|
||||||
|
import { AppState } from '../store'
|
||||||
|
import { setClass, setPoints } from '../store/calculator/actions'
|
||||||
|
import { Points } from '../store/calculator/types'
|
||||||
|
import { decodeKnownTalents, encodeKnownTalents } from '../lib/tree'
|
||||||
|
|
||||||
interface Props extends RouteComponentProps {
|
interface Props extends RouteComponentProps {
|
||||||
match: match<{
|
match: match<{
|
||||||
selectedClass: string
|
selectedClass: string
|
||||||
pointString: string
|
pointString: string
|
||||||
}>
|
}>
|
||||||
|
classId: number
|
||||||
|
points: Points
|
||||||
|
setClass: typeof setClass
|
||||||
|
setPoints: typeof setPoints
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Home extends React.PureComponent<Props> {
|
export class Home extends React.PureComponent<Props> {
|
||||||
static whyDidYouRender = true
|
static whyDidYouRender = true
|
||||||
|
|
||||||
|
get classSlug() {
|
||||||
|
return classById[this.props.classId] && classById[this.props.classId].name.toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { selectedClass } = this.props.match.params
|
this.loadFromUrlParams()
|
||||||
if (selectedClass && !classByName[selectedClass]) {
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps: Props) {
|
||||||
|
const prevParams = prevProps.match.params
|
||||||
|
const { params } = this.props.match
|
||||||
|
|
||||||
|
if (prevParams.selectedClass !== params.selectedClass) {
|
||||||
|
// Class changed in route
|
||||||
|
this.loadFromUrlParams()
|
||||||
|
} else {
|
||||||
|
// Changes within same class
|
||||||
|
if (prevParams.pointString !== params.pointString) {
|
||||||
|
// Same class but point string changed
|
||||||
|
const decoded = decodeKnownTalents(params.pointString || '', this.props.classId)
|
||||||
|
if (!this.props.points.equals(decoded)) {
|
||||||
|
this.props.setPoints(decoded)
|
||||||
|
}
|
||||||
|
} else if (prevProps.points !== this.props.points) {
|
||||||
|
// Points map changed, update the URL
|
||||||
|
this.updateURL(this.props.points)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.props.setClass(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadFromUrlParams() {
|
||||||
|
const { selectedClass, pointString } = this.props.match.params
|
||||||
|
const c = selectedClass && classByName[selectedClass]
|
||||||
|
if (c) {
|
||||||
|
const points = pointString && decodeKnownTalents(pointString || '', c.id)
|
||||||
|
this.props.setClass(c.id, points)
|
||||||
|
} else {
|
||||||
|
this.props.setClass(null)
|
||||||
this.props.history.replace('/')
|
this.props.history.replace('/')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
updateURL(points: Points) {
|
||||||
const { match, history } = this.props
|
const { classId } = this.props
|
||||||
const { selectedClass, pointString } = match.params
|
const pointsString = encodeKnownTalents(points, classId)
|
||||||
|
if (pointsString !== this.props.match.params.pointString) {
|
||||||
|
this.props.history.replace(`/${this.classSlug}` + (pointsString ? `/${pointsString}` : ''))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (selectedClass && !classByName[selectedClass]) {
|
render() {
|
||||||
|
const { match, classId } = this.props
|
||||||
|
const { selectedClass } = match.params
|
||||||
|
|
||||||
|
const currentClass = classById[classId]
|
||||||
|
if (classId && !currentClass) {
|
||||||
|
// We're redirecting to /
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,14 +96,21 @@ export default class Home extends React.PureComponent<Props> {
|
|||||||
selected={selectedClass}
|
selected={selectedClass}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{selectedClass &&
|
{currentClass &&
|
||||||
<Calculator
|
<Calculator
|
||||||
initialTalents={pointString && decodeKnownTalents(pointString, selectedClass)}
|
classId={classId}
|
||||||
selectedClass={selectedClass}
|
points={this.props.points}
|
||||||
history={history}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
({ calculator }: AppState) => ({
|
||||||
|
classId: calculator.classId,
|
||||||
|
points: calculator.points,
|
||||||
|
}),
|
||||||
|
{ setClass, setPoints }
|
||||||
|
)(Home)
|
||||||
+12
-2
@@ -3,12 +3,22 @@ import ReactDOM from 'react-dom'
|
|||||||
import App from './App'
|
import App from './App'
|
||||||
import * as serviceWorker from './serviceWorker'
|
import * as serviceWorker from './serviceWorker'
|
||||||
|
|
||||||
|
import { Provider } from 'react-redux'
|
||||||
|
import store from './store'
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
const whyDidYouRender = require('@welldone-software/why-did-you-render/dist/no-classes-transpile/umd/whyDidYouRender.min.js')
|
const whyDidYouRender = require('@welldone-software/why-did-you-render/dist/no-classes-transpile/umd/whyDidYouRender.min.js')
|
||||||
whyDidYouRender(React)
|
whyDidYouRender(React, {
|
||||||
|
include: [/^ConnectFunction$/]
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
ReactDOM.render(<App />, document.getElementById('root'))
|
ReactDOM.render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<App />
|
||||||
|
</Provider>,
|
||||||
|
document.getElementById('root')
|
||||||
|
)
|
||||||
|
|
||||||
// If you want your app to work offline and load faster, you can change
|
// If you want your app to work offline and load faster, you can change
|
||||||
// unregister() to register() below. Note this comes with some pitfalls.
|
// unregister() to register() below. Note this comes with some pitfalls.
|
||||||
|
|||||||
+5
-5
@@ -5,7 +5,7 @@ import {
|
|||||||
talentsBySpecArray,
|
talentsBySpecArray,
|
||||||
talentsById
|
talentsById
|
||||||
} from '../data/talents';
|
} from '../data/talents';
|
||||||
import { classByName } from '../data/classes'
|
import { classById } from '../data/classes'
|
||||||
import spells from '../data/spells.json'
|
import spells from '../data/spells.json'
|
||||||
|
|
||||||
export const MAX_POINTS = 51
|
export const MAX_POINTS = 51
|
||||||
@@ -204,9 +204,9 @@ export const modifyTalentPoint = (known: Map<number, number>, talent: TalentData
|
|||||||
/**
|
/**
|
||||||
* Encodes a Map of known talents into a URL-friendly string.
|
* Encodes a Map of known talents into a URL-friendly string.
|
||||||
*/
|
*/
|
||||||
export function encodeKnownTalents(known: Map<number, number>, className: string): string {
|
export function encodeKnownTalents(known: Map<number, number>, classId: number): string {
|
||||||
let string = ''
|
let string = ''
|
||||||
const { specs } = classByName[className]
|
const { specs } = classById[classId]
|
||||||
for (let i = 0; i < specs.length; i++) {
|
for (let i = 0; i < specs.length; i++) {
|
||||||
const specId = specs[i]
|
const specId = specs[i]
|
||||||
const talents = talentsBySpecArray[specId].sort(SORT_TALENTS)
|
const talents = talentsBySpecArray[specId].sort(SORT_TALENTS)
|
||||||
@@ -222,8 +222,8 @@ export function encodeKnownTalents(known: Map<number, number>, className: string
|
|||||||
/**
|
/**
|
||||||
* Decodes a string of points into a Map of talents.
|
* Decodes a string of points into a Map of talents.
|
||||||
*/
|
*/
|
||||||
export function decodeKnownTalents(pointString: string, className: string): Map<number, number> {
|
export function decodeKnownTalents(pointString: string, classId: number): Map<number, number> {
|
||||||
const { specs } = classByName[className]
|
const { specs } = classById[classId]
|
||||||
let known = Map<number, number>()
|
let known = Map<number, number>()
|
||||||
|
|
||||||
// TODO: Make sure we validate the point string
|
// TODO: Make sure we validate the point string
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import {
|
||||||
|
CalculatorActionTypes,
|
||||||
|
SET_CLASS,
|
||||||
|
ADD_POINT,
|
||||||
|
REMOVE_POINT,
|
||||||
|
SET_POINTS,
|
||||||
|
Points,
|
||||||
|
} from './types'
|
||||||
|
|
||||||
|
export const setClass = (classId: number, points?: Points): CalculatorActionTypes => ({
|
||||||
|
type: SET_CLASS,
|
||||||
|
classId,
|
||||||
|
points
|
||||||
|
})
|
||||||
|
|
||||||
|
export const addPoint = (talentId: number): CalculatorActionTypes => ({
|
||||||
|
type: ADD_POINT,
|
||||||
|
talentId
|
||||||
|
})
|
||||||
|
|
||||||
|
export const removePoint = (talentId: number): CalculatorActionTypes => ({
|
||||||
|
type: REMOVE_POINT,
|
||||||
|
talentId
|
||||||
|
})
|
||||||
|
|
||||||
|
export const setPoints = (points: Points): CalculatorActionTypes => ({
|
||||||
|
type: SET_POINTS,
|
||||||
|
points
|
||||||
|
})
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import { Map } from 'immutable'
|
||||||
|
import {
|
||||||
|
CalculatorState,
|
||||||
|
CalculatorActionTypes,
|
||||||
|
SET_CLASS,
|
||||||
|
ADD_POINT,
|
||||||
|
REMOVE_POINT,
|
||||||
|
SET_POINTS
|
||||||
|
} from './types'
|
||||||
|
import { canLearnTalent, canUnlearnTalent, encodeKnownTalents } from '../../lib/tree'
|
||||||
|
import { talentsById } from '../../data/talents'
|
||||||
|
|
||||||
|
const initialState: CalculatorState = {
|
||||||
|
classId: null,
|
||||||
|
points: Map<number, number>(),
|
||||||
|
pointsEncoded: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function(state = initialState, action: CalculatorActionTypes): CalculatorState {
|
||||||
|
const { classId, points } = state
|
||||||
|
|
||||||
|
switch (action.type) {
|
||||||
|
case SET_CLASS: {
|
||||||
|
if (classId === action.classId) {
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
classId: action.classId,
|
||||||
|
points: action.points || Map(),
|
||||||
|
pointsEncoded: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case ADD_POINT: {
|
||||||
|
const { talentId } = action
|
||||||
|
const talent = talentsById[talentId]
|
||||||
|
if (!canLearnTalent(points, talent)) {
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
const nextPoints = points.set(talentId, points.get(talentId, 0) + 1)
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
points: nextPoints,
|
||||||
|
pointsEncoded: encodeKnownTalents(nextPoints, classId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case REMOVE_POINT: {
|
||||||
|
const { talentId } = action
|
||||||
|
const talent = talentsById[talentId]
|
||||||
|
if (!canUnlearnTalent(points, talent)) {
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
const nextPoints = points.set(talentId, points.get(talentId, 1) - 1)
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
points: nextPoints,
|
||||||
|
pointsEncoded: encodeKnownTalents(nextPoints, classId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case SET_POINTS: {
|
||||||
|
if (points.equals(action.points)) {
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
points: action.points
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { Map } from 'immutable'
|
||||||
|
|
||||||
|
export type Points = Map<number, number>
|
||||||
|
|
||||||
|
export interface CalculatorState {
|
||||||
|
classId: number
|
||||||
|
points: Points
|
||||||
|
pointsEncoded: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SET_CLASS = 'SET_CLASS'
|
||||||
|
export const ADD_POINT = 'ADD_POINT'
|
||||||
|
export const REMOVE_POINT = 'REMOVE_POINT'
|
||||||
|
export const SET_POINTS = 'SET_POINTS'
|
||||||
|
|
||||||
|
interface SetClassAction {
|
||||||
|
type: typeof SET_CLASS
|
||||||
|
classId: number
|
||||||
|
points?: Points
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AddPointAction {
|
||||||
|
type: typeof ADD_POINT
|
||||||
|
talentId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RemovePointAction {
|
||||||
|
type: typeof REMOVE_POINT
|
||||||
|
talentId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SetPointsAction {
|
||||||
|
type: typeof SET_POINTS
|
||||||
|
points: Points
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CalculatorActionTypes = SetClassAction | AddPointAction | RemovePointAction |
|
||||||
|
SetPointsAction
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { createStore, combineReducers, compose } from 'redux'
|
||||||
|
import calculator from './calculator/reducers'
|
||||||
|
|
||||||
|
const rootReducer = combineReducers({
|
||||||
|
calculator,
|
||||||
|
})
|
||||||
|
|
||||||
|
export type AppState = ReturnType<typeof rootReducer>
|
||||||
|
|
||||||
|
const store = createStore(
|
||||||
|
rootReducer,
|
||||||
|
compose(
|
||||||
|
(window as any).__REDUX_DEVTOOLS_EXTENSION__ && (window as any).__REDUX_DEVTOOLS_EXTENSION__()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
export default store
|
||||||
Vendored
+2
-1
@@ -51,4 +51,5 @@ interface Talent {
|
|||||||
|
|
||||||
type TalentClickHandler = (specId: number, talentId: number, modifier: 1 | -1) => void
|
type TalentClickHandler = (specId: number, talentId: number, modifier: 1 | -1) => void
|
||||||
|
|
||||||
type TooltipPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'left' | 'right'
|
type TooltipPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'left' | 'right'
|
||||||
|
|
||||||
|
|||||||
@@ -881,7 +881,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.2"
|
regenerator-runtime "^0.13.2"
|
||||||
|
|
||||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2":
|
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.5":
|
||||||
version "7.5.5"
|
version "7.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
|
||||||
integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
|
integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
|
||||||
@@ -1293,6 +1293,14 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.2.tgz#0e670ea254d559241b6eeb3894f8754991e73220"
|
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.2.tgz#0e670ea254d559241b6eeb3894f8754991e73220"
|
||||||
integrity sha512-ui3WwXmjTaY73fOQ3/m3nnajU/Orhi6cEu5rzX+BrAAJxa3eITXZ5ch9suPqtM03OWhAHhPSyBGCN4UKoxO20Q==
|
integrity sha512-ui3WwXmjTaY73fOQ3/m3nnajU/Orhi6cEu5rzX+BrAAJxa3eITXZ5ch9suPqtM03OWhAHhPSyBGCN4UKoxO20Q==
|
||||||
|
|
||||||
|
"@types/hoist-non-react-statics@^3.3.0":
|
||||||
|
version "3.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
|
||||||
|
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
hoist-non-react-statics "^3.3.0"
|
||||||
|
|
||||||
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
|
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
|
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
|
||||||
@@ -1367,6 +1375,16 @@
|
|||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
"@types/webpack" "*"
|
"@types/webpack" "*"
|
||||||
|
|
||||||
|
"@types/react-redux@^7.1.1":
|
||||||
|
version "7.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.1.tgz#eb01e89cf71cad77df9f442b819d5db692b997cb"
|
||||||
|
integrity sha512-owqNahzE8en/jR4NtrUJDJya3tKru7CIEGSRL/pVS84LtSCdSoT7qZTkrbBd3S4Lp11sAp+7LsvxIeONJVKMnw==
|
||||||
|
dependencies:
|
||||||
|
"@types/hoist-non-react-statics" "^3.3.0"
|
||||||
|
"@types/react" "*"
|
||||||
|
hoist-non-react-statics "^3.3.0"
|
||||||
|
redux "^4.0.0"
|
||||||
|
|
||||||
"@types/react-router-dom@^4.3.4":
|
"@types/react-router-dom@^4.3.4":
|
||||||
version "4.3.4"
|
version "4.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.3.4.tgz#63a7a8558129d2f4ff76e4bdd099bf4b98e25a0d"
|
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.3.4.tgz#63a7a8558129d2f4ff76e4bdd099bf4b98e25a0d"
|
||||||
@@ -4745,7 +4763,7 @@ hmac-drbg@^1.0.0:
|
|||||||
minimalistic-assert "^1.0.0"
|
minimalistic-assert "^1.0.0"
|
||||||
minimalistic-crypto-utils "^1.0.1"
|
minimalistic-crypto-utils "^1.0.1"
|
||||||
|
|
||||||
hoist-non-react-statics@^3.1.0:
|
hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b"
|
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b"
|
||||||
integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==
|
integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==
|
||||||
@@ -6273,7 +6291,7 @@ lodash.uniq@^4.5.0:
|
|||||||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||||
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
|
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
|
||||||
|
|
||||||
"lodash@>=3.5 <5", lodash@^4, lodash@^4.0.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.5, lodash@~4.17.10:
|
"lodash@>=3.5 <5", lodash@^4, lodash@^4.0.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.5, lodash@^4.2.0, lodash@~4.17.10:
|
||||||
version "4.17.15"
|
version "4.17.15"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
||||||
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
||||||
@@ -8216,7 +8234,7 @@ prompts@^2.0.1:
|
|||||||
kleur "^3.0.2"
|
kleur "^3.0.2"
|
||||||
sisteransi "^1.0.0"
|
sisteransi "^1.0.0"
|
||||||
|
|
||||||
prop-types@^15.5.0, prop-types@^15.6.2:
|
prop-types@^15.5.0, prop-types@^15.5.7, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||||
version "15.7.2"
|
version "15.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
||||||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||||
@@ -8443,7 +8461,7 @@ react-error-overlay@^5.1.6:
|
|||||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-5.1.6.tgz#0cd73407c5d141f9638ae1e0c63e7b2bf7e9929d"
|
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-5.1.6.tgz#0cd73407c5d141f9638ae1e0c63e7b2bf7e9929d"
|
||||||
integrity sha512-X1Y+0jR47ImDVr54Ab6V9eGk0Hnu7fVWGeHQSOXHf/C2pF9c6uy3gef8QUeuUiWlNb0i08InPSE5a/KJzNzw1Q==
|
integrity sha512-X1Y+0jR47ImDVr54Ab6V9eGk0Hnu7fVWGeHQSOXHf/C2pF9c6uy3gef8QUeuUiWlNb0i08InPSE5a/KJzNzw1Q==
|
||||||
|
|
||||||
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4:
|
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6:
|
||||||
version "16.8.6"
|
version "16.8.6"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
|
||||||
integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==
|
integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==
|
||||||
@@ -8455,6 +8473,18 @@ react-loadable@^5.5.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
prop-types "^15.5.0"
|
prop-types "^15.5.0"
|
||||||
|
|
||||||
|
react-redux@^7.1.0:
|
||||||
|
version "7.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.1.0.tgz#72af7cf490a74acdc516ea9c1dd80e25af9ea0b2"
|
||||||
|
integrity sha512-hyu/PoFK3vZgdLTg9ozbt7WF3GgX5+Yn3pZm5/96/o4UueXA+zj08aiSC9Mfj2WtD1bvpIb3C5yvskzZySzzaw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.4.5"
|
||||||
|
hoist-non-react-statics "^3.3.0"
|
||||||
|
invariant "^2.2.4"
|
||||||
|
loose-envify "^1.4.0"
|
||||||
|
prop-types "^15.7.2"
|
||||||
|
react-is "^16.8.6"
|
||||||
|
|
||||||
react-router-dom@^5.0.1:
|
react-router-dom@^5.0.1:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.0.1.tgz#ee66f4a5d18b6089c361958e443489d6bab714be"
|
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.0.1.tgz#ee66f4a5d18b6089c361958e443489d6bab714be"
|
||||||
@@ -8658,6 +8688,31 @@ redent@^1.0.0:
|
|||||||
indent-string "^2.1.0"
|
indent-string "^2.1.0"
|
||||||
strip-indent "^1.0.1"
|
strip-indent "^1.0.1"
|
||||||
|
|
||||||
|
redux-devtools-instrument@^1.9.0:
|
||||||
|
version "1.9.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/redux-devtools-instrument/-/redux-devtools-instrument-1.9.6.tgz#6b412595f74b9d48cfd4ecc13e585b1588ed6e7e"
|
||||||
|
integrity sha512-MwvY4cLEB2tIfWWBzrUR02UM9qRG2i7daNzywRvabOSVdvAY7s9BxSwMmVRH1Y/7QWjplNtOwgT0apKhHg2Qew==
|
||||||
|
dependencies:
|
||||||
|
lodash "^4.2.0"
|
||||||
|
symbol-observable "^1.0.2"
|
||||||
|
|
||||||
|
redux-devtools@^3.5.0:
|
||||||
|
version "3.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/redux-devtools/-/redux-devtools-3.5.0.tgz#d69ab76d4f0f8abdf6d24bcf5954d7a1aa2b6827"
|
||||||
|
integrity sha512-pGU8TZNvWxPaCCE432AGm6H6alQbAz80gQM5CzM3SjX9/oSNu/HPF17xFdPQJOXasqyih1Gv167kZDTRe7r0iQ==
|
||||||
|
dependencies:
|
||||||
|
lodash "^4.2.0"
|
||||||
|
prop-types "^15.5.7"
|
||||||
|
redux-devtools-instrument "^1.9.0"
|
||||||
|
|
||||||
|
redux@^4.0.0, redux@^4.0.4:
|
||||||
|
version "4.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.4.tgz#4ee1aeb164b63d6a1bcc57ae4aa0b6e6fa7a3796"
|
||||||
|
integrity sha512-vKv4WdiJxOWKxK0yRoaK3Y4pxxB0ilzVx6dszU2W8wLxlb2yikRph4iV/ymtdJ6ZxpBLFbyrxklnT5yBbQSl3Q==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.4.0"
|
||||||
|
symbol-observable "^1.2.0"
|
||||||
|
|
||||||
regenerate-unicode-properties@^8.0.2:
|
regenerate-unicode-properties@^8.0.2:
|
||||||
version "8.1.0"
|
version "8.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e"
|
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e"
|
||||||
@@ -9694,6 +9749,11 @@ svgo@^1.0.0, svgo@^1.2.2:
|
|||||||
unquote "~1.1.1"
|
unquote "~1.1.1"
|
||||||
util.promisify "~1.0.0"
|
util.promisify "~1.0.0"
|
||||||
|
|
||||||
|
symbol-observable@^1.0.2, symbol-observable@^1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
||||||
|
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
|
||||||
|
|
||||||
symbol-tree@^3.2.2:
|
symbol-tree@^3.2.2:
|
||||||
version "3.2.4"
|
version "3.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
||||||
|
|||||||
Reference in New Issue
Block a user