Better prototype with functioning dependencies and right-clicking

This commit is contained in:
Melvin Valster
2019-07-17 16:20:45 +02:00
parent 7d1b822830
commit f8da695da4
14 changed files with 314 additions and 272 deletions
+6
View File
@@ -0,0 +1,6 @@
- [ ] Prevent reducing talent points on a row when it is a dependency for points already spent in the next row
- [ ] Prevent reducing talent points on a talent that is a requirement for another talent with points in it
- [ ] Talent tooltips
- [ ] Change class
- [ ] Generate URL for chosen talents
- [ ] Responsive on mobile
+4
View File
@@ -7,6 +7,7 @@
"@types/node": "12.6.2", "@types/node": "12.6.2",
"@types/react": "16.8.23", "@types/react": "16.8.23",
"@types/react-dom": "16.8.4", "@types/react-dom": "16.8.4",
"classnames": "^2.2.6",
"immutable": "^4.0.0-rc.12", "immutable": "^4.0.0-rc.12",
"node-sass": "^4.12.0", "node-sass": "^4.12.0",
"react": "^16.8.6", "react": "^16.8.6",
@@ -34,5 +35,8 @@
"last 1 firefox version", "last 1 firefox version",
"last 1 safari version" "last 1 safari version"
] ]
},
"devDependencies": {
"@welldone-software/why-did-you-render": "^3.2.1"
} }
} }
+1 -1
View File
@@ -19,7 +19,7 @@
work correctly both with client-side routing and a non-root public URL. work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`. Learn how to configure a non-root public URL by running `npm run build`.
--> -->
<title>React App</title> <title>Talent Calculator</title>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
+11 -37
View File
@@ -2,44 +2,18 @@
text-align: center; text-align: center;
} }
.calculator {
}
.trees {
display: flex;
}
.tree { .tree {
position: relative; position: relative;
width: 400px; min-width: 300px;
height: 500px; height: 600px;
border: 1px solid black; border: 1px solid black;
} background-size: cover;
background-position: center;
.talent {
position: absolute;
width: 40px;
height: 40px;
border: 1px solid black;
border-radius: 2px;
small {
font-size: 8px;
line-height: 1em;
}
&[data-row="0"] { top: 70px; }
&[data-row="1"] { top: 140px; }
&[data-row="2"] { top: 210px; }
&[data-row="3"] { top: 280px; }
&[data-row="4"] { top: 400px; }
&[data-col="0"] { left: 10px; }
&[data-col="1"] { left: 60px; }
&[data-col="2"] { left: 110px; }
&[data-col="3"] { left: 160px; }
&__rank {
position: absolute;
padding: 1px 2px;
bottom: -2px;
right: -2px;
color: white;
font-size: 12px;
background: black;
border-radius: 2px;
}
} }
+19 -105
View File
@@ -1,137 +1,51 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { List, Map, fromJS } from 'immutable' import { Map } from 'immutable'
import { TalentTree } from './TalentTree'; import { TalentTree } from './TalentTree';
import { modifyPointsInTree, modifyKnownTalents } from '../lib/tree'; import {
import { talentsBySpec, specNames } from '../data/talents'; modifyTalentPoint,
calcAvailablePoints
} from '../lib/tree';
import { talentsBySpec } from '../data/talents';
import { classByName } from '../data/classes'; import { classByName } from '../data/classes';
import { number } from 'prop-types';
const createTalent = (name: string, row: number, column: number, ranks: string | string[], type: Talent['type'] = 'talent'): Talent => {
return {
name,
row,
column,
ranks: typeof ranks === 'string' ? [ranks] : ranks,
type
}
}
/**
* Max rows: 7
* Max cols: 4
*
* verify: no talent on same [row, column] combination
* potentially: sort talents based on row and column so order doesn't matter
*/
const warlockTalents: TalentTree[] = [
// Affliction
{
id: 164,
name: 'Affliction',
icon: 'https://wow.zamimg.com/images/wow/icons/small/spell_shadow_deathcoil.jpg',
talents: [
// Row 1
createTalent('Suppression', 0, 1, [
'Reduces the chance for enemies to resist your Affliction spells by 2%.',
'Reduces the chance for enemies to resist your Affliction spells by 4%.',
'Reduces the chance for enemies to resist your Affliction spells by 6%.',
'Reduces the chance for enemies to resist your Affliction spells by 8%.',
'Reduces the chance for enemies to resist your Affliction spells by 10%.',
]),
createTalent('Improved Corruption', 0, 2, [
'Reduces the casting time of your corruption spell by 0.4 sec.',
'Reduces the casting time of your corruption spell by 0.8 sec.',
'Reduces the casting time of your corruption spell by 1.2 sec.',
'Reduces the casting time of your corruption spell by 1.6 sec.',
'Reduces the casting time of your corruption spell by 2 sec.',
]),
// Row 2
createTalent('Improved Curse of Weakness', 1, 0, [
'Increases the effect of your Curse of Weakness by 6%.',
'Increases the effect of your Curse of Weakness by 13%.',
'Increases the effect of your Curse of Weakness by 20%.'
]),
createTalent('Improved Drain Soul', 1, 1, [
'Gives you a 50% chance to get a 100% increase to your Mana regeneration for 10 sec if the target is killed by you while you drain its soul. In addition your Mana may continue to regenerate while casting at 50% of normal.',
'Gives you a 100% chance to get a 100% increase to your Mana regeneration for 10 sec if the target is killed by you while you drain its soul. In addition your Mana may continue to regenerate while casting at 50% of normal.',
]),
createTalent('Improved Life Tap', 1, 2, [
'Increases the amount of Mana awarded by your Life Tap spell by 10%.',
'Increases the amount of Mana awarded by your Life Tap spell by 20%.',
]),
createTalent('Improved Drain Life', 1, 3, [
'Increases the Health drained by your Drain Life spell by 2%.',
'Increases the Health drained by your Drain Life spell by 4%.',
'Increases the Health drained by your Drain Life spell by 6%.',
'Increases the Health drained by your Drain Life spell by 8%.',
'Increases the Health drained by your Drain Life spell by 10%.',
]),
// Row 3
createTalent('Improved Curse of Agony', 2, 0, [
'Increases the damage done by your Curse of Agony by 2%.',
'Increases the damage done by your Curse of Agony by 4%.',
'Increases the damage done by your Curse of Agony by 6%.',
]),
createTalent('Fel Concentration', 2, 1, [
'Gives you a 14% chance to avoid interruption caused by damage while channeling the Drain Life, Drain Mana, or Drain Soul spell.',
'Gives you a 28% chance to avoid interruption caused by damage while channeling the Drain Life, Drain Mana, or Drain Soul spell.',
'Gives you a 42% chance to avoid interruption caused by damage while channeling the Drain Life, Drain Mana, or Drain Soul spell.',
'Gives you a 56% chance to avoid interruption caused by damage while channeling the Drain Life, Drain Mana, or Drain Soul spell.',
'Gives you a 70% chance to avoid interruption caused by damage while channeling the Drain Life, Drain Mana, or Drain Soul spell.',
]),
createTalent('Amplify Curse', 2, 2, [
'Increases the effect of your next Curse of Weakness or Curse of Agony by 50%, or your next Curse of Exhaustion by 20%. Lasts 30 sec.',
]),
]
}
// Demonoloy
// Destruction
]
interface Props { interface Props {
forClass: string forClass: string
pointString?: string // e.g. 2305302300--001 pointString?: string // e.g. 2305302300--001
} }
const initialSpentPoints: List<List<number>> = fromJS([
[], [], []
])
const initMap = Map<number, number>() const initMap = Map<number, number>()
export const Calculator: React.FC<Props> = ({ forClass = 'warlock', pointString = '' }) => { export const Calculator: React.FC<Props> = ({ forClass = 'warlock', pointString = '' }) => {
const [knownTalents, setKnownTalents] = useState(initMap) const [knownTalents, setKnownTalents] = useState(initMap)
const [spentPoints, setSpentPoints] = useState(initialSpentPoints)
const selectedClass = classByName[forClass] const selectedClass = classByName[forClass]
const availablePoints = calcAvailablePoints(knownTalents)
console.log(knownTalents)
const handleTalentPress = (specId: number, talentId: number, modifier: 1 | -1) => { const handleTalentPress = (specId: number, talentId: number, modifier: 1 | -1) => {
console.log('onTalentPress', { specId, talentId, modifier })
const talent = talentsBySpec[specId][talentId] const talent = talentsBySpec[specId][talentId]
setKnownTalents(
modifyTalentPoint(knownTalents, talent, modifier)
setKnownTalents(modifyKnownTalents(knownTalents, talent, modifier)) )
} }
return ( return (
<div className="calculator"> <div className="calculator">
<div className="trees">
{selectedClass.specs.map((specId, specIndex) => ( {selectedClass.specs.map((specId, specIndex) => (
<TalentTree <TalentTree
key={specId} key={specId}
specId={specId} specId={specId}
availablePoints={availablePoints}
knownTalents={knownTalents} knownTalents={knownTalents}
spentPoints={spentPoints.get(specIndex)}
onTalentPress={handleTalentPress} onTalentPress={handleTalentPress}
/> />
))} ))}
</div> </div>
<div className="calculator__points">
Points: {availablePoints}
</div>
</div>
) )
} }
(Calculator as any).whyDidYouRender = true
+1
View File
@@ -1,4 +1,5 @@
.icon { .icon {
background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; background-size: cover;
+64
View File
@@ -0,0 +1,64 @@
.talent {
position: absolute;
width: 40px;
height: 40px;
border: 1px solid black;
border-radius: 2px;
&:after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 10;
}
small {
font-size: 8px;
line-height: 1em;
}
&--disabled {
filter: grayscale(100%)
}
&:not(&--disabled) {
cursor: pointer;
}
&--maxed {
}
$row-distance: 70px;
&[data-row="0"] { top: $row-distance; }
&[data-row="1"] { top: $row-distance * 2; }
&[data-row="2"] { top: $row-distance * 3; }
&[data-row="3"] { top: $row-distance * 4; }
&[data-row="4"] { top: $row-distance * 5; }
&[data-row="5"] { top: $row-distance * 6; }
&[data-row="6"] { top: $row-distance * 7; }
$col-distance-offset: 30px;
$col-distance: 50px;
&[data-col="0"] { left: $col-distance-offset; }
&[data-col="1"] { left: $col-distance-offset + ($col-distance * 1); }
&[data-col="2"] { left: $col-distance-offset + ($col-distance * 2); }
&[data-col="3"] { left: $col-distance-offset + ($col-distance * 3); }
&__points {
position: absolute;
padding: 1px 2px;
bottom: -2px;
right: -2px;
color: white;
font-size: 12px;
background: black;
border-radius: 2px;
}
}
+44 -9
View File
@@ -1,30 +1,65 @@
import React, { FC } from 'react'
import './TalentSlot.scss' import './TalentSlot.scss'
import React, { FC } from 'react'
import { Icon } from './Icon' import { Icon } from './Icon'
import classNames from 'classnames'
import { spells } from '../data/spells' import { spells } from '../data/spells'
import { Map } from 'immutable';
import { getPointsInSpec, calcMeetsRequirements } from '../lib/tree';
interface Props { interface Props {
key: number
talent: TalentData talent: TalentData
/** Points spent */ specId: number
points: number availablePoints: number
/** All spent talents */
knownTalents: Map<number, number>
/** Disabled override */
disabled?: boolean
onClick?: (e: any) => void onClick?: (e: any) => void
onRightClick?: (e: any) => void
}
const isAvailable = (talent: TalentData, specId: number, knownTalents: Map<number, number>): boolean => {
// Dependent on other talents?
if (!calcMeetsRequirements(talent, knownTalents)) {
return false
}
const pointsInSpec = getPointsInSpec(specId, knownTalents)
return talent.row * 5 <= pointsInSpec
} }
export const TalentSlot: FC<Props> = (props) => { export const TalentSlot: FC<Props> = (props) => {
const { talent, points } = props const { talent, specId, knownTalents, availablePoints } = props
const requiredPointsSpent = talent.row * 5 const points = knownTalents.get(talent.id, 0)
const showPoints = points > 0 || availablePoints > 0
const disabled = false // props.disabled || !showPoints || !isAvailable(talent, specId, knownTalents)
const cn = classNames('talent', {
'talent--disabled': !!disabled,
'talent--maxed': points >= talent.ranks.length
})
const handleContextMenu = (e) => {
if (props.onRightClick) props.onRightClick(e)
e.preventDefault()
return false
}
return ( return (
<div <div
className="talent" className={cn}
title={talent.ranks[0].toString()} title={talent.ranks[0].toString()}
data-row={talent.row} data-row={talent.row}
data-col={talent.col} data-col={talent.col}
onClick={props.onClick} onClick={!disabled ? props.onClick : () => {}}
onContextMenu={handleContextMenu}
> >
<Icon name={talent.icon} /> <Icon name={talent.icon} />
<div className="talent__rank">{points}/{talent.ranks.length}</div>
{showPoints &&
<div className="talent__points">{points}/{talent.ranks.length}</div>
}
</div> </div>
) )
} }
(TalentSlot as any).whyDidYouRender = true
+20 -13
View File
@@ -1,38 +1,45 @@
import React, { MouseEvent } from 'react' import React, { MouseEvent, useCallback } from 'react'
import { List, Map } from 'immutable' import { Map } from 'immutable'
import { TalentSlot } from './TalentSlot'; import { TalentSlot } from './TalentSlot';
import { getTreePointCount } from '../lib/tree'; import { getPointsInSpec } from '../lib/tree';
import { talentsBySpec, specNames } from '../data/talents'; import { talentsBySpec, specNames } from '../data/talents';
interface Props { interface Props {
specId: number specId: number
spentPoints: List<number> availablePoints: number
knownTalents: Map<number, number> knownTalents: Map<number, number>
onTalentPress: TalentClickHandler onTalentPress: TalentClickHandler
} }
export const TalentTree: React.FC<Props> = ({ specId, spentPoints, knownTalents, onTalentPress }) => { export const TalentTree: React.FC<Props> = ({ specId, knownTalents, availablePoints, onTalentPress }) => {
const talents = Object.values(talentsBySpec[specId]) const talents = Object.values(talentsBySpec[specId])
const handleTalentPress = (talentId: number) => { const handleTalentPress = useCallback((talentId: number, modifier: 1 | -1) => {
return (e: MouseEvent) => { return (e: MouseEvent) => {
onTalentPress(specId, talentId, e.shiftKey ? -1 : 1) onTalentPress(specId, talentId, modifier)
} }
}, [specId, onTalentPress])
const style = {
backgroundImage: `url("https://wow.zamimg.com/images/wow/talents/backgrounds/classic/${specId}.jpg")`
} }
return ( return (
<div className="tree"> <div className="tree" style={style}>
<h2>{specNames[specId]}</h2> <h2>{specNames[specId]} ({getPointsInSpec(specId, knownTalents)})</h2>
{talents.map((talent, index) => {talents.map((talent, index) =>
<TalentSlot <TalentSlot
key={talent.id} key={talent.id}
specId={specId}
talent={talent} talent={talent}
points={knownTalents.get(talent.id, 0)} availablePoints={availablePoints}
onClick={handleTalentPress(talent.id)} knownTalents={knownTalents}
onClick={handleTalentPress(talent.id, 1)}
onRightClick={handleTalentPress(talent.id, -1)}
/> />
)} )}
Spent: {getTreePointCount(spentPoints)}
</div> </div>
) )
} }
(TalentTree as any).whyDidYouRender = true
+9
View File
@@ -3743,3 +3743,12 @@ export const talentsBySpec: Root = {
} }
} }
} }
export const talentToSpec: {[key: number]: number} = {}
for (let specId in talentsBySpec) {
for (let talentId in talentsBySpec[specId]) {
talentToSpec[talentId] = parseInt(specId, 10)
}
}
console.log(talentToSpec)
+5
View File
@@ -4,6 +4,11 @@ import './index.css';
import App from './App'; import App from './App';
import * as serviceWorker from './serviceWorker'; import * as serviceWorker from './serviceWorker';
if (process.env.NODE_ENV !== 'production') {
const whyDidYouRender = require('@welldone-software/why-did-you-render')
whyDidYouRender(React)
}
ReactDOM.render(<App />, document.getElementById('root')); ReactDOM.render(<App />, 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
+30 -30
View File
@@ -1,40 +1,40 @@
import im from 'immutable' import im from 'immutable'
import { import {
setTalentPointsInTree, // setTalentPointsInTree,
getTreePointCount getPointsInSpec
} from './tree' } from './tree'
describe('setTalentPointsInTree', () => { // describe('setTalentPointsInTree', () => {
it('sets points on an empty tree', () => { // it('sets points on an empty tree', () => {
const tree = im.List() // const tree = im.List()
expect(setTalentPointsInTree(tree, 2, 5).toJS()).toEqual([0, 0, 5]) // expect(setTalentPointsInTree(tree, 2, 5).toJS()).toEqual([0, 0, 5])
}) // })
it('sets points in the end of the current range', () => { // it('sets points in the end of the current range', () => {
const tree = im.List([0, 1]) // const tree = im.List([0, 1])
expect(setTalentPointsInTree(tree, 2, 5).toJS()).toEqual([0, 1, 5]) // expect(setTalentPointsInTree(tree, 2, 5).toJS()).toEqual([0, 1, 5])
}) // })
it('sets points in the middle of the current range', () => { // it('sets points in the middle of the current range', () => {
const tree = im.List([0, 0, 0, 0, 0, 0, 5]) // const tree = im.List([0, 0, 0, 0, 0, 0, 5])
expect(setTalentPointsInTree(tree, 2, 5).toJS()).toEqual([0, 0, 5, 0, 0, 0, 5]) // expect(setTalentPointsInTree(tree, 2, 5).toJS()).toEqual([0, 0, 5, 0, 0, 0, 5])
}) // })
it('does not mutate the tree for points already set', () => { // it('does not mutate the tree for points already set', () => {
const tree = im.List([0, 3, 2, 0, 5]) // const tree = im.List([0, 3, 2, 0, 5])
expect(setTalentPointsInTree(tree, 1, 3)).toStrictEqual(tree) // expect(setTalentPointsInTree(tree, 1, 3)).toStrictEqual(tree)
}) // })
}) // })
describe('getTreePointCount', () => { // describe('getTreePointCount', () => {
it('returns proper count', () => { // it('returns proper count', () => {
const result = getTreePointCount(im.List([0, 0, 4, 5, 3, 0, 0])) // const result = getPointsInSpec(im.List([0, 0, 4, 5, 3, 0, 0]))
expect(result).toBe(12) // expect(result).toBe(12)
}) // })
it('returns 0 for empty list', () => { // it('returns 0 for empty list', () => {
const result = getTreePointCount(im.List()) // const result = getPointsInSpec(im.List())
expect(result).toBe(0) // expect(result).toBe(0)
}) // })
}) // })
+64 -53
View File
@@ -1,91 +1,102 @@
import { List, Map, fromJS } from 'immutable' import { List, Map, fromJS } from 'immutable'
import { talentsBySpec, talentToSpec } from '../data/talents';
/** export const MAX_POINTS = 51
* Sets proper values for a tree, filling in 0s in between.
*/
export function setTalentPointsInTree(tree: List<number>, talentIndex: number, points: number): List<number> {
// Ensure all values until `index` are set, otherwise set to 0
for (let i = tree.size; i < talentIndex; i++) {
tree = tree.set(i, 0)
}
return tree.set(talentIndex, points)
}
/** /**
* Returns the overall points spent in the tree. * Returns the overall points spent in the tree.
*/ */
export function getTreePointCount(tree: List<number>): number { export function getPointsInSpec(specId: number, known: Map<number, number>): number {
return tree.reduce((reduction, value) => value + reduction, 0) // TODO: Hard to test this method when referencing talents from a file. Improve this.
return Object.values(talentsBySpec[specId]).reduce((prev: number, current: TalentData) => {
return prev + known.get(current.id, 0)
}, 0)
} }
export function canRemovePoint() { export function calcAvailablePoints(known: Map<number, number>): number {
return Math.max(0, MAX_POINTS - known.reduce((prev, current) => prev + current, 0))
} }
export function canSetPoint() { /**
* Returns whether a talent's other talent requirements are met.
*/
export function calcMeetsRequirements(talent: TalentData, known: Map<number, number>): boolean {
if (talent.requires.length === 0) {
return true
}
return talent.requires.reduce((prev, current) => {
if (!prev) return false
return known.get(current.id, 0) >= current.qty
}, true)
} }
export const modifyPointsInTree = (tree: List<number>, talent: TalentData, talentIndex: number, modifier: 1 | -1): List<number> => { /**
const currentPoints = tree.get(talentIndex, 0) * Adds a single talent point to the Map, if possible.
*/
export const addTalentPoint = (known: Map<number, number>, talent: TalentData): Map<number, number> => {
const currentPoints = known.get(talent.id, 0)
// TODO: We should prevent reducing talent points on a row when it is a dependency for points already spent in the next row. // Support for specific Talent dependency requirement.
if (talent.requires.length > 0 && !calcMeetsRequirements(talent, known)) {
return known
}
// TODO: Support for specific Talent dependency requirement. // Spend a maximum of 51 points
if (calcAvailablePoints(known) === 0) {
// TODO: Spend a maximum of 51 points return known
}
// Check we have the required amount of points spent in the tree for this talent // Check we have the required amount of points spent in the tree for this talent
const requiredPoints = talent.row * 5 const requiredPoints = talent.row * 5
const spentPointCount = getTreePointCount(tree) const pointsInSpec = getPointsInSpec(talentToSpec[talent.id], known)
if (requiredPoints > spentPointCount) return tree if (requiredPoints > pointsInSpec) {
return known
let newPoints = currentPoints
if (modifier === 1) {
if (currentPoints === talent.ranks.length) return tree
newPoints = currentPoints + 1
} else if (modifier === -1) {
if (currentPoints === 0) return tree
newPoints = currentPoints - 1
} }
return setTalentPointsInTree(tree, talentIndex, newPoints) // Reached the max rank?
if (currentPoints >= talent.ranks.length) {
return known
} }
export const modifyKnownTalents = (known: Map<number, number>, talent: TalentData, modifier: 1 | -1): Map<number, number> => { return known.set(talent.id, currentPoints + 1)
}
/**
* Removes a single talent point from the Map, if possible.
*/
export const removeTalentPoint = (known: Map<number, number>, talent: TalentData): Map<number, number> => {
const currentPoints = known.get(talent.id, 0) const currentPoints = known.get(talent.id, 0)
// TODO: We should prevent reducing talent points on a row when it is a dependency for points already spent in the next row. // TODO: We should prevent reducing talent points on a row when it is a dependency for points already spent in the next row.
// TODO: Support for specific Talent dependency requirement. // Already no points for this talent
if (currentPoints === 0) {
// TODO: Spend a maximum of 51 points return known
// TODO: Check we have the required amount of points spent in the tree for this talent
const requiredPoints = talent.row * 5
// const spentPointCount = known.get(talent.id, 0)
// if (requiredPoints > spentPointCount) return tree
let newPoints = currentPoints
if (modifier === 1) {
if (currentPoints === talent.ranks.length) return known
newPoints = currentPoints + 1
} else if (modifier === -1) {
if (currentPoints === 0) return known
newPoints = currentPoints - 1
} }
return newPoints === 0 return currentPoints === 1
? known.remove(talent.id) ? known.remove(talent.id)
: known.set(talent.id, newPoints) : known.set(talent.id, currentPoints - 1)
} }
/**
* Either adds or removes a talent point based on the modifier.
*/
export const modifyTalentPoint = (known: Map<number, number>, talent: TalentData, modifier: 1 | -1): Map<number, number> => {
if (modifier === 1) {
return addTalentPoint(known, talent)
} else {
return removeTalentPoint(known, talent)
}
}
// TODO
export function parsePointString(str: string): List<List<number>> { export function parsePointString(str: string): List<List<number>> {
const list: Array<number[]> = [] const list: Array<number[]> = []
const trees = str.split('-') const trees = str.split('-')
trees.map((stringForTree, index) => { trees.map((stringForTree, index) => {
const points = stringForTree.split('').map(a => parseInt(a, 2)) const points = stringForTree.split('').map(a => parseInt(a, 10))
list[index] = points list[index] = points
}) })
+13 -1
View File
@@ -1505,6 +1505,13 @@
"@webassemblyjs/wast-parser" "1.8.5" "@webassemblyjs/wast-parser" "1.8.5"
"@xtuc/long" "4.2.2" "@xtuc/long" "4.2.2"
"@welldone-software/why-did-you-render@^3.2.1":
version "3.2.1"
resolved "http://npm.soundtrackyourbrand.com/@welldone-software%2fwhy-did-you-render/-/why-did-you-render-3.2.1.tgz#9dc6fd8f8cb1640fbd386694290dbf9244a3a354"
integrity sha512-7rCVpFyE5Pnm0qyO8ByWfiFAKONvq6GAUUFuGjdJiOXnsAokdotu5EJ6VDBraV1I7UiVj9+TQRbwvrfsFKU0sw==
dependencies:
lodash "^4"
"@xtuc/ieee754@^1.2.0": "@xtuc/ieee754@^1.2.0":
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
@@ -2498,6 +2505,11 @@ class-utils@^0.3.5:
isobject "^3.0.0" isobject "^3.0.0"
static-extend "^0.1.1" static-extend "^0.1.1"
classnames@^2.2.6:
version "2.2.6"
resolved "http://npm.soundtrackyourbrand.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
clean-css@4.2.x: clean-css@4.2.x:
version "4.2.1" version "4.2.1"
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17"
@@ -6070,7 +6082,7 @@ lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
lodash@^4.0.0, lodash@~4.17.10: lodash@^4, lodash@^4.0.0, lodash@~4.17.10:
version "4.17.14" version "4.17.14"
resolved "http://npm.soundtrackyourbrand.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba" resolved "http://npm.soundtrackyourbrand.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba"
integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw== integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==