💥 Borderlands 3 skill planner PWA
8
.editorconfig
Normal file
|
@ -0,0 +1,8 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = false
|
||||
insert_final_newline = false
|
13
.gitignore
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
/node_modules
|
||||
.DS_Store
|
||||
/build
|
||||
*.lock
|
||||
*.log
|
||||
.vscode
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
9044
package-lock.json
generated
Normal file
26
package.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "pwa export --routes /about,/blog,/",
|
||||
"start": "sirv build -s",
|
||||
"watch": "pwa watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"preact": "^8.3.0",
|
||||
"preact-compat": "^3.18.0",
|
||||
"preact-router": "latest",
|
||||
"sirv-cli": "^0.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@pwa/cli": "latest",
|
||||
"@pwa/plugin-offline": "latest",
|
||||
"@pwa/preset-preact": "latest"
|
||||
},
|
||||
"browserslist": [
|
||||
">0.25%",
|
||||
"last 1 version",
|
||||
"not ie_mob 11",
|
||||
"not ie 11",
|
||||
"not dead"
|
||||
]
|
||||
}
|
BIN
src/assets/favicon.png
Normal file
After Width: | Height: | Size: 563 B |
26
src/assets/features.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
[
|
||||
{
|
||||
"title": "Instant Prototyping",
|
||||
"text": "Quickly scaffold new projects with your preferred view library and toolkit. Kick it off with a perfect Lighthouse score!"
|
||||
},
|
||||
{
|
||||
"title": "Feature Rich",
|
||||
"text": "Supports Babel, Bublé, Browserlist, TypeScript, PostCSS, ESLint, Prettier, and Service Workers out of the box!"
|
||||
},
|
||||
{
|
||||
"title": "Fully Extensible",
|
||||
"text": "Includes a plugin system that allows for easy, fine-grain control of your configuration... when needed."
|
||||
},
|
||||
{
|
||||
"title": "Plug 'n Play",
|
||||
"text": "Don't worry about configuration, unless you want to. Presets and plugins are automatically applied. Just install and go!"
|
||||
},
|
||||
{
|
||||
"title": "Framework Agnostic",
|
||||
"text": "Build with your preferred framework or with none at all! Official presets for Preact, React, Vue, and Svelte."
|
||||
},
|
||||
{
|
||||
"title": "Static Site Generator",
|
||||
"text": "Export your routes as \"pre-rendered\" HTML, which is great for SEO and works on any static hosting service."
|
||||
}
|
||||
]
|
BIN
src/assets/icon.png
Normal file
After Width: | Height: | Size: 14 KiB |
1
src/assets/link.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg>
|
After Width: | Height: | Size: 442 B |
14
src/assets/manifest.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "PWA/CLI",
|
||||
"short_name": "PWA/CLI",
|
||||
"background_color": "#3E82F7",
|
||||
"orientation": "portrait",
|
||||
"theme_color": "#1e88e5",
|
||||
"display": "standalone",
|
||||
"start_url": "/",
|
||||
"icons": [{
|
||||
"src": "/assets/icon.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}]
|
||||
}
|
2
src/assets/robots.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
User-agent: *
|
||||
Disallow:
|
1
src/assets/shapes/circle.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 12"><path fill="#ED412D" d="M6.5.1C3.4.1.8 2.8.8 6s2.6 5.9 5.7 5.9 5.7-2.7 5.7-5.9S9.7.1 6.5.1zm0 8.7C5 8.8 3.8 7.6 3.8 6S5 3.2 6.5 3.2 9.2 4.4 9.2 6 8 8.8 6.5 8.8z" /></svg>
|
After Width: | Height: | Size: 231 B |
1
src/assets/shapes/cross.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path fill="#FDBD00" d="M10.3 4.3H7.7V1.7C7.7.8 7 0 6 0S4.3.8 4.3 1.7v2.5H1.7C.8 4.3 0 5 0 6s.8 1.7 1.7 1.7h2.5v2.5C4.3 11.2 5 12 6 12s1.7-.8 1.7-1.7V7.7h2.5c1 0 1.8-.7 1.8-1.7s-.8-1.7-1.7-1.7z" class="cross"/></svg>
|
After Width: | Height: | Size: 277 B |
1
src/assets/shapes/penta.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 561.8 559.4"><path fill="#3E82F7" d="M383.4 559.4h-204l-2.6-.2c-51.3-4.4-94-37-108.8-83l-.2-.6L6 276.7l-.2-.5a119.4 119.4 0 0 1 43.7-131.4L212.1 23A115.7 115.7 0 0 1 351 23l163.5 122.5.4.3c39 30.3 56 82.6 42.2 130.3l-.3 1.1-61.5 198a116.1 116.1 0 0 1-111.9 84.2zm-197.9-120h195.2l61.1-196.8c0-.5-.3-1.6-.7-2.1L281.5 120.9 120.9 241.2l.2 1.2 60.8 195.8c.6.3 1.8.9 3.6 1.2zM441 240.3z"/></svg>
|
After Width: | Height: | Size: 445 B |
1
src/assets/shapes/point.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path fill="#8491A3" d="M6 7.5c-.9 0-1.5-.6-1.5-1.5S5.2 4.5 6 4.5c.9 0 1.5.7 1.5 1.5 0 .9-.6 1.5-1.5 1.5z"/></svg>
|
After Width: | Height: | Size: 175 B |
1
src/assets/shapes/square.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 14"><path fill="#2DA94F" stroke="#2DA94F" d="M5.9 1.2L.7 6.5c-.2.2-.2.5 0 .7l5.2 5.4c.2.2.5.2.7 0l5.2-5.4c.2-.2.2-.5 0-.7L6.6 1.2c-.2-.3-.5-.3-.7 0zM3.4 6.5L6 3.9c.2-.2.5-.2.7 0l2.6 2.6c.2.2.2.5 0 .7L6.6 9.9c-.2.2-.5.2-.7 0L3.4 7.3c-.2-.2-.2-.5 0-.8z" /></svg>
|
After Width: | Height: | Size: 317 B |
BIN
src/assets/twitter_heart.png
Normal file
After Width: | Height: | Size: 11 KiB |
39
src/assets/video.svg
Normal file
After Width: | Height: | Size: 247 KiB |
3
src/components/App/index.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
main {
|
||||
padding: 1rem 1rem 3rem;
|
||||
}
|
26
src/components/App/index.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { Component } from 'preact';
|
||||
import { Router } from 'preact-router';
|
||||
|
||||
import Nav from '@components/Nav';
|
||||
import Footer from '@components/Footer';
|
||||
|
||||
import Home from 'async!@pages/Home';
|
||||
import Zane from 'async!@pages/Zane';
|
||||
|
||||
import style from './index.css';
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<div>
|
||||
<Nav />
|
||||
<main>
|
||||
<Router>
|
||||
<Home path="/" />
|
||||
<Zane path="/Zane" />
|
||||
</Router>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
45
src/components/Footer/index.css
Normal file
|
@ -0,0 +1,45 @@
|
|||
.footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
font-family: var(--font-header);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.footer span a {
|
||||
color: inherit;
|
||||
transition: color var(--transition-duration);
|
||||
}
|
||||
|
||||
.footer a:hover {
|
||||
color: hsla(0, 100%, 50%, 1);
|
||||
}
|
||||
|
||||
.heart {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: transparent url(@assets/twitter_heart.png) no-repeat;
|
||||
background-size: 2900%;
|
||||
margin: 0 -8px;
|
||||
}
|
||||
|
||||
.heart:hover {
|
||||
background-position: right;
|
||||
animation: moveHeart 800ms steps(28) forwards;
|
||||
}
|
||||
|
||||
@keyframes moveHeart {
|
||||
0% {
|
||||
background-position: left;
|
||||
}
|
||||
50% {
|
||||
background-position: right;
|
||||
}
|
||||
100% {
|
||||
background-position: right;
|
||||
}
|
||||
}
|
11
src/components/Footer/index.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import style from './index.css';
|
||||
|
||||
export default function () {
|
||||
return (
|
||||
<footer class={ style.footer }>
|
||||
<span>Made with </span>
|
||||
<i class={ style.heart } />
|
||||
<span> by <a href="https://github.com/seigler">Joshua Seigler</a></span>
|
||||
</footer>
|
||||
);
|
||||
}
|
22
src/components/Nav/index.css
Normal file
|
@ -0,0 +1,22 @@
|
|||
.nav {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.nav a {
|
||||
display: inline-block;
|
||||
color: inherit;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.nav a:hover {
|
||||
background-color: var(--blue);
|
||||
color: var(--white);
|
||||
}
|
15
src/components/Nav/index.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { Component } from 'preact';
|
||||
import { Link } from 'preact-router';
|
||||
import style from './index.css';
|
||||
|
||||
export default function Nav (props) {
|
||||
return (
|
||||
<nav class={ style.nav }>
|
||||
<ul class={ style.links }>
|
||||
<li><Link href="/">Home</Link></li>
|
||||
<li><Link href="/Zane">Zane</Link></li>
|
||||
<li><Link href="/Amara">Amara</Link></li>
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
}
|
3
src/components/VaultHunter/index.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
10
src/components/VaultHunter/index.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import style from './index.css';
|
||||
|
||||
export default function VaultHunter ({name = 'Unnamed', skills = {}}) {
|
||||
return (
|
||||
<div>
|
||||
<h2>{ name }</h2>
|
||||
<pre>{JSON.stringify(skills, null, 2)}</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
41
src/index.css
Normal file
|
@ -0,0 +1,41 @@
|
|||
:root {
|
||||
--radius: 2px;
|
||||
--blue: #1E88E5;
|
||||
--white: #FFFFFF;
|
||||
--offwhite: #F8F8FA;
|
||||
--transition-duration: 300ms;
|
||||
--font-header: sans-serif;
|
||||
--font-list: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
*,
|
||||
*:after,
|
||||
*:before {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
color: #333;
|
||||
position: relative;
|
||||
font-family: var(--font-list);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
a {
|
||||
color: rgb(0,100,200);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: rgb(0,80,160);
|
||||
}
|
17
src/index.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>PWA</title>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="theme-color" content="#1e88e5"/>
|
||||
<meta name="mobile-web-app-capable" content="yes"/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<meta name="description" content="The universal PWA builder & CLI tool~!"/>
|
||||
<link rel="shortcut icon" href="/assets/favicon.png"/>
|
||||
<link rel="manifest" href="/assets/manifest.json"/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
19
src/index.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { h, render } from 'preact';
|
||||
import App from '@components/App';
|
||||
import './index.css';
|
||||
|
||||
let elem = document.querySelector('#app');
|
||||
let root = render(<App/>, elem, elem.firstElementChild);
|
||||
|
||||
if (process.env.NODE_ENV === 'development' && module.hot) {
|
||||
// enable preact devtools
|
||||
require('preact/debug');
|
||||
// respond to HMR updates
|
||||
module.hot.accept('@components/App', New => {
|
||||
New = require('@components/App').default;
|
||||
root = render(<New />, elem, root);
|
||||
});
|
||||
} else if (process.env.NODE_ENV === 'production') {
|
||||
// Service Worker registration
|
||||
require('offline-plugin/runtime').install();
|
||||
}
|
7
src/pages/Home/index.css
Normal file
|
@ -0,0 +1,7 @@
|
|||
.section {
|
||||
padding-bottom: 64px;
|
||||
}
|
||||
|
||||
.section h2 {
|
||||
margin-bottom: 16px;
|
||||
}
|
9
src/pages/Home/index.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import style from './index.css';
|
||||
|
||||
export default function () {
|
||||
return (
|
||||
<div>
|
||||
Home
|
||||
</div>
|
||||
);
|
||||
}
|
12
src/pages/Zane/index.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
import VaultHunter from '@components/VaultHunter';
|
||||
|
||||
import skills from './skills.js';
|
||||
|
||||
export default function Zane () {
|
||||
return (
|
||||
<VaultHunter
|
||||
name = 'Zane'
|
||||
skills = { skills }
|
||||
/>
|
||||
);
|
||||
}
|
70
src/pages/Zane/skills.js
Normal file
|
@ -0,0 +1,70 @@
|
|||
const skills = {
|
||||
"Hitman": {
|
||||
"0": {
|
||||
"SNTNL": {
|
||||
text: "Send into battle an automated SNTL drone that continually flies through the environment and attacks enemies with its Machine Guns. Pressing LB or RB (controller) while SNTNL is active causes it to attack the enemy under Zane's crosshairs, if any.",
|
||||
}
|
||||
},
|
||||
"1": {
|
||||
"Violent Speed": {
|
||||
text: "After killing an enemy, Zane gains increased Movement Speed for a few seconds.",
|
||||
},
|
||||
"Cold Bore": {
|
||||
text: "Zane gains increased Weapon Swap Speed. The next shot fired after swapping weapons deals Bonus Cryo Damage.",
|
||||
},
|
||||
"Violent Momentum": {
|
||||
text: "Zane's Gun Damage is increased while moving. The quicker he moves, the greater the Gun Damage bonus.",
|
||||
},
|
||||
},
|
||||
"2": {
|
||||
"Winter's Drone": {
|
||||
text: "Converts SNTNL's primary weapons to Cryo Damage.",
|
||||
},
|
||||
"Cool Hand": {
|
||||
text: "Zane gains increased Reload Speed. After killing an enemy, Zane's Reload Speed is increased for a few seconds.",
|
||||
},
|
||||
"Drone Delivery": {
|
||||
text: "SNTRY will occasionally drop a free grenade based on your current grenade mod while attacking enemies.",
|
||||
},
|
||||
"Salvation": {
|
||||
text: "After killing an enemy, Zane's weapons gain Life Steal for a few seconds.",
|
||||
},
|
||||
},
|
||||
"3": {
|
||||
"Bad Dose": {
|
||||
text: "SNTNL occasionally shoots out a beam of Radiation that weakens enemies and buffs Zane.",
|
||||
},
|
||||
"Seein' Red": {
|
||||
text: "Activating an Action Skill automatically activates all of Zane's kill skills.",
|
||||
},
|
||||
"Static Field": {
|
||||
text: "SNTNL emits a static field that sends a Shock beam to nearby enemies, draining their shields and replenishing Zane's.",
|
||||
},
|
||||
},
|
||||
"4": {
|
||||
"Boomsday": {
|
||||
text: "SNTNL adds a rocket pod to its primary weapons, allowing it to shoot rockets as well as machine guns.",
|
||||
},
|
||||
"Violent Violence": {
|
||||
text: "After killing an enemy, Zane gains increased Fire Rate for a few seconds.",
|
||||
},
|
||||
"Playing Dirty": {
|
||||
text: "After killing an enemy, Zane's next five shots all have a chance to fire an additional projectile.",
|
||||
},
|
||||
"Almighty Ordnance": {
|
||||
text: "Hold down LB or RB (controller) while SNTNL is deployed to paint a target area. SNTNL fires a missile barrage at that area, and if an enemy is killed, Almighty Ordnance's duration is reset. This can only be used once per Action Skill use.",
|
||||
},
|
||||
},
|
||||
"5": {
|
||||
"Good Misfortune": {
|
||||
text: "Killing an enemy increases Zane's Action Skill Duration. This skill has diminishing returns.",
|
||||
},
|
||||
},
|
||||
"6": {
|
||||
"Death Follows Close": {
|
||||
text: "All of Zane's kill skills gain increased effect and duration.",
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
export default skills;
|