3
.babelrc
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"plugins": ["@babel/plugin-transform-runtime"]
|
||||
}
|
||||
3
.eslintrc.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
||||
9
.gitignore
vendored
|
|
@ -11,6 +11,9 @@ pids
|
|||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Next
|
||||
.next
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
|
|
@ -44,9 +47,9 @@ dist/
|
|||
|
||||
# Source images
|
||||
# Instructions will be provided to download these from the game
|
||||
src/images/weapon*
|
||||
src/images/summon*
|
||||
src/images/chara*
|
||||
public/images/weapon*
|
||||
public/images/summon*
|
||||
public/images/chara*
|
||||
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
|
|
|
|||
10
.sassrc.js
|
|
@ -1,10 +0,0 @@
|
|||
const path = require('path');
|
||||
const cwd = process.cwd();
|
||||
|
||||
module.exports = {
|
||||
data: '@import "globals";',
|
||||
'includePaths': [
|
||||
'node_modules',
|
||||
'assets/scss'
|
||||
]
|
||||
}
|
||||
34
README.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
|
||||
|
||||
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 1C0 0.447715 0.447715 0 1 0H11C11.5523 0 12 0.447715 12 1C12 1.55228 11.5523 2 11 2H1C0.447715 2 0 1.55228 0 1ZM0 6C0 5.44772 0.447715 5 1 5H11C11.5523 5 12 5.44772 12 6C12 6.55228 11.5523 7 11 7H1C0.447715 7 0 6.55228 0 6ZM1 10C0.447715 10 0 10.4477 0 11C0 11.5523 0.447715 12 1 12H11C11.5523 12 12 11.5523 12 11C12 10.4477 11.5523 10 11 10H1Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 492 B |
10
components/AboutModal/index.scss
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
.AboutModal p {
|
||||
font-size: $font-regular;
|
||||
line-height: 1.24;
|
||||
margin: 0;
|
||||
margin-bottom: $unit * 2;
|
||||
|
||||
&:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
36
components/AboutModal/index.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import React from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import api from '~utils/api'
|
||||
|
||||
import Modal from '~components/Modal'
|
||||
import Overlay from '~components/Overlay'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
interface Props {
|
||||
close: () => void
|
||||
}
|
||||
|
||||
const AboutModal = (props: Props) => {
|
||||
return (
|
||||
createPortal(
|
||||
<div>
|
||||
<Modal
|
||||
title="About"
|
||||
styleName="AboutModal"
|
||||
close={ () => {} }
|
||||
>
|
||||
<div>
|
||||
<p>Siero is a tool to save and share parties for <a href="https://game.granbluefantasy.jp">Granblue Fantasy.</a></p>
|
||||
<p>All you need to do to get started is start adding things. Siero will make a URL and you can share your party with the world.</p>
|
||||
<p>If you want to save your parties for safe keeping or to edit them later, you can make a free account.</p>
|
||||
</div>
|
||||
</Modal>
|
||||
<Overlay onClick={props.close} />
|
||||
</div>,
|
||||
document.body
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export default AboutModal
|
||||
29
components/App/index.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import React from 'react'
|
||||
import { Route, Routes, useNavigate } from 'react-router-dom'
|
||||
|
||||
import Header from '~components/Header'
|
||||
|
||||
import NewRoute from '~routes/NewRoute'
|
||||
import PartyRoute from '~routes/PartyRoute'
|
||||
import PartiesRoute from '~routes/PartiesRoute'
|
||||
import ProfileRoute from '~routes/ProfileRoute'
|
||||
|
||||
|
||||
const App = () => {
|
||||
const navigate = useNavigate()
|
||||
const route = (pathname: string) => navigate(pathname)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Header navigate={route} />
|
||||
<Routes>
|
||||
<Route path='/' element={<NewRoute />} />
|
||||
<Route path='/parties/' element={<PartiesRoute />} />
|
||||
<Route path='/p/:hash' element={<PartyRoute />} />
|
||||
<Route path='/:username' element={<ProfileRoute />} />
|
||||
</Routes>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
|
|
@ -1,13 +1,10 @@
|
|||
button {
|
||||
border: none;
|
||||
font-weight: $medium;
|
||||
}
|
||||
|
||||
.Button {
|
||||
align-items: center;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
display: inline-flex;
|
||||
font-size: 1.4rem;
|
||||
font-weight: $medium;
|
||||
gap: 6px;
|
||||
padding: 8px 12px;
|
||||
|
||||
|
|
@ -86,10 +83,4 @@ button {
|
|||
.text {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
@include media('>small', '<=medium') {
|
||||
.Button {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,10 +3,6 @@ import classNames from 'classnames'
|
|||
|
||||
import './index.scss'
|
||||
|
||||
import New from '../../../assets/new'
|
||||
import Menu from '../../../assets/menu'
|
||||
import Link from '../../../assets/link'
|
||||
|
||||
interface Props {
|
||||
color: string
|
||||
disabled: boolean
|
||||
|
|
@ -36,11 +32,17 @@ class Button extends React.Component<Props, State> {
|
|||
render() {
|
||||
let icon
|
||||
if (this.props.type === 'new') {
|
||||
icon = <span className='icon'><New /></span>
|
||||
icon = <span className='icon'>
|
||||
<img src="/icons/new.svg" />
|
||||
</span>
|
||||
} else if (this.props.type === 'menu') {
|
||||
icon = <span className='icon'><Menu /></span>
|
||||
icon = <span className='icon'>
|
||||
<img src="/icons/menu.svg" />
|
||||
</span>
|
||||
} else if (this.props.type === 'link') {
|
||||
icon = <span className='icon'><Link /></span>
|
||||
icon = <span className='icon'>
|
||||
<img src="/icons/link.svg" />
|
||||
</span>
|
||||
}
|
||||
|
||||
const classes = classNames({
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
import React, { useState } from 'react'
|
||||
import { useModal as useModal } from '~utils/useModal'
|
||||
|
||||
import CharacterUnit from '~components/CharacterUnit'
|
||||
import SearchModal from '~components/SearchModal'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
|
|
@ -19,12 +22,31 @@ interface Props {
|
|||
}
|
||||
|
||||
const CharacterGrid = (props: Props) => {
|
||||
const { open, openModal, closeModal } = useModal()
|
||||
const [searchPosition, setSearchPosition] = useState(0)
|
||||
|
||||
const numCharacters: number = 5
|
||||
|
||||
function isCharacter(object: Character | Weapon | Summon): object is Character {
|
||||
// There aren't really any unique fields here
|
||||
return (object as Character).gender !== undefined
|
||||
}
|
||||
|
||||
function openSearchModal(position: number) {
|
||||
setSearchPosition(position)
|
||||
openModal()
|
||||
}
|
||||
|
||||
function receiveCharacter(character: Character, position: number) {
|
||||
props.onSelect(GridType.Character, character, position)
|
||||
}
|
||||
|
||||
function sendData(object: Character | Weapon | Summon, position: number) {
|
||||
if (isCharacter(object)) {
|
||||
receiveCharacter(object, position)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="CharacterGrid">
|
||||
<ul id="grid_characters">
|
||||
|
|
@ -33,8 +55,8 @@ const CharacterGrid = (props: Props) => {
|
|||
return (
|
||||
<li key={`grid_unit_${i}`} >
|
||||
<CharacterUnit
|
||||
onClick={() => { openSearchModal(i) }}
|
||||
editable={props.editable}
|
||||
onReceiveData={receiveCharacter}
|
||||
position={i}
|
||||
character={props.grid[i]}
|
||||
/>
|
||||
|
|
@ -42,6 +64,16 @@ const CharacterGrid = (props: Props) => {
|
|||
)
|
||||
})
|
||||
}
|
||||
{open ? (
|
||||
<SearchModal
|
||||
grid={props.grid}
|
||||
close={closeModal}
|
||||
send={sendData}
|
||||
fromPosition={searchPosition}
|
||||
object="characters"
|
||||
placeholderText="Search for a character..."
|
||||
/>
|
||||
) : null}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
.CharacterResult h5 {
|
||||
color: #555;
|
||||
display: inline-block;
|
||||
font-size: 18px;
|
||||
font-size: $font-large;
|
||||
font-weight: 500;
|
||||
margin: 2px 4px 4px 0;
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
.CharacterResult .stars {
|
||||
display: inline-block;
|
||||
color: #FFA15E;
|
||||
font-size: 21px;
|
||||
font-size: $font-xlarge;
|
||||
}
|
||||
|
||||
.CharacterResult .stars > span {
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
import React from 'react'
|
||||
import WeaponLabelIcon from '~components/WeaponLabelIcon'
|
||||
|
||||
import images from '../../images/chara-grid/*.jpg'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
interface Props {
|
||||
|
|
@ -14,18 +12,11 @@ const Element = ['null', 'wind', 'fire', 'water', 'earth', 'dark', 'light']
|
|||
|
||||
class CharacterResult extends React.Component<Props> {
|
||||
render() {
|
||||
let imgSrc
|
||||
|
||||
const character = this.props.data
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
imgSrc = images[character.granblue_id]
|
||||
} else if (process.env.NODE_ENV === 'production') {
|
||||
imgSrc = `${process.env.REACT_APP_SIERO_IMG_URL}/chara-grid/${character.granblue_id}.jpg`
|
||||
}
|
||||
|
||||
return (
|
||||
<li className="CharacterResult" onClick={this.props.onClick}>
|
||||
<img alt={character.name.en} src={imgSrc} />
|
||||
<img alt={character.name.en} src={`/images/chara-grid/${character.granblue_id}_01.jpg`} />
|
||||
<div>
|
||||
<div>
|
||||
<h5>{character.name.en}</h5>
|
||||
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
.CharacterUnit h3 {
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
font-size: $font-regular;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
max-width: 131px;
|
||||
62
components/CharacterUnit/index.tsx
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import UncapIndicator from '~components/UncapIndicator'
|
||||
|
||||
import PlusIcon from '~public/icons/plus.svg'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
interface Props {
|
||||
onClick: () => void
|
||||
character: Character | undefined
|
||||
position: number
|
||||
editable: boolean
|
||||
}
|
||||
|
||||
const CharacterUnit = (props: Props) => {
|
||||
const [imageUrl, setImageUrl] = useState('')
|
||||
|
||||
const classes = classnames({
|
||||
CharacterUnit: true,
|
||||
'editable': props.editable,
|
||||
'filled': (props.character !== undefined)
|
||||
})
|
||||
|
||||
const character = props.character
|
||||
|
||||
useEffect(() => {
|
||||
generateImageUrl()
|
||||
})
|
||||
|
||||
|
||||
function generateImageUrl() {
|
||||
let imgSrc = ""
|
||||
|
||||
if (props.character) {
|
||||
const character = props.character!
|
||||
imgSrc = `/images/chara-main/${character.granblue_id}_01.jpg`
|
||||
}
|
||||
|
||||
setImageUrl(imgSrc)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={classes}>
|
||||
<div className="CharacterImage" onClick={props.onClick}>
|
||||
<img alt={character?.name.en} className="grid_image" src={imageUrl} />
|
||||
{ (props.editable) ? <span className='icon'><PlusIcon /></span> : '' }
|
||||
</div>
|
||||
<UncapIndicator
|
||||
type="character"
|
||||
flb={character?.uncap.flb || false}
|
||||
uncapLevel={(character?.rarity == 2) ? 3 : 4}
|
||||
/>
|
||||
<h3 className="CharacterName">{character?.name.en}</h3>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CharacterUnit
|
||||
33
components/Fieldset/index.scss
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
.Fieldset {
|
||||
border: none;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
margin: 0 0 $unit 0;
|
||||
|
||||
.Input {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
border: none;
|
||||
|
||||
background-color: white;
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
color: $grey-00;
|
||||
display: block;
|
||||
font-size: $font-regular;
|
||||
padding: 12px 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.InputError {
|
||||
color: $error;
|
||||
font-size: $font-small;
|
||||
margin: $unit 0;
|
||||
padding: ($unit / 2) ($unit * 2);
|
||||
}
|
||||
}
|
||||
|
||||
::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */
|
||||
color: #a9a9a9 !important;
|
||||
opacity: 1; /* Firefox */
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react'
|
||||
import './index.css'
|
||||
import './index.scss'
|
||||
|
||||
interface Props {
|
||||
fieldName: string
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
import './index.css'
|
||||
import './index.scss'
|
||||
|
||||
import mainhandImages from '../../images/weapon-main/*.jpg'
|
||||
import gridImages from '../../images/weapon-grid/*.jpg'
|
||||
|
|
@ -39,23 +39,13 @@ const GridRep = (props: Props) => {
|
|||
}
|
||||
|
||||
function generateMainhandImage() {
|
||||
return (mainhand)
|
||||
? <img src={
|
||||
process.env.NODE_ENV === 'development'
|
||||
? mainhandImages[mainhand?.granblue_id || 0]
|
||||
: `${process.env.REACT_APP_SIERO_IMG_URL}/weapon-main/${mainhand?.granblue_id}.jpg`
|
||||
} />
|
||||
: <img />
|
||||
return (mainhand) ?
|
||||
<img alt={mainhand?.name.en} src={`/images/weapon-main/${mainhand?.granblue_id}.jpg`} /> : ''
|
||||
}
|
||||
|
||||
function generateGridImage(position: number) {
|
||||
return (weapons[position])
|
||||
? <img src={
|
||||
process.env.NODE_ENV === 'development'
|
||||
? gridImages[weapons[position]?.granblue_id || 0]
|
||||
: `${process.env.REACT_APP_SIERO_IMG_URL}/weapon-grid/${weapons[position]?.granblue_id}.jpg`
|
||||
} />
|
||||
: <img />
|
||||
return (weapons[position]) ?
|
||||
<img alt={weapons[position]?.name.en} src={`/images/weapon-grid/${weapons[position]?.granblue_id}.jpg`} /> : ''
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react'
|
||||
|
||||
import './index.css'
|
||||
import './index.scss'
|
||||
|
||||
interface Props {}
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { useHistory, useLocation } from 'react-router-dom'
|
||||
import { useNavigate, useLocation } from 'react-router-dom'
|
||||
import { useCookies } from 'react-cookie'
|
||||
|
||||
import Button from '~components/Button'
|
||||
|
|
@ -15,7 +15,7 @@ const Header = (props: Props) => {
|
|||
const [username, setUsername] = useState(undefined)
|
||||
const [cookies, setCookie, removeCookie] = useCookies(['user'])
|
||||
|
||||
let history = useHistory()
|
||||
let navigate = useNavigate()
|
||||
let location = useLocation()
|
||||
|
||||
const route = (pathname: string) => props.navigate(pathname)
|
||||
|
|
@ -39,13 +39,13 @@ const Header = (props: Props) => {
|
|||
}
|
||||
|
||||
function newParty() {
|
||||
props.navigate('/')
|
||||
navigate('/')
|
||||
}
|
||||
|
||||
function logout() {
|
||||
removeCookie('user')
|
||||
window.history.replaceState(null, `Grid Tool`, `/`)
|
||||
history.go(0)
|
||||
navigate(0)
|
||||
}
|
||||
|
||||
return (
|
||||
14
components/LoginModal/index.scss
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
.LoginForm {
|
||||
#fields {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $unit;
|
||||
}
|
||||
.fieldset {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -8,9 +8,9 @@ import Fieldset from '~components/Fieldset'
|
|||
import Modal from '~components/Modal'
|
||||
import Overlay from '~components/Overlay'
|
||||
|
||||
import './index.css'
|
||||
import './index.scss'
|
||||
|
||||
import New from '../../../assets/new'
|
||||
// import New from '../../../assets/new'
|
||||
|
||||
interface Props {
|
||||
cookies: Cookies
|
||||
|
|
@ -108,11 +108,11 @@ const LoginModal = (props: Props) => {
|
|||
return (
|
||||
createPortal(
|
||||
<div>
|
||||
<Modal styleName="LoginForm">
|
||||
<div id="ModalTop">
|
||||
<h1>Log in</h1>
|
||||
<i className='close' onClick={props.close}><New /></i>
|
||||
</div>
|
||||
<Modal
|
||||
title="Log in"
|
||||
styleName="LoginForm"
|
||||
close={ () => {} }
|
||||
>
|
||||
<form className="form" onSubmit={submit}>
|
||||
<div id="fields">
|
||||
<Fieldset
|
||||
81
components/Modal/index.scss
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
.ModalContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
.Modal {
|
||||
background: #f5f5f5;
|
||||
border-radius: $unit;
|
||||
color: $grey-00;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
min-width: $unit * 45;
|
||||
max-width: $unit * 60;
|
||||
overflow-y: auto;
|
||||
padding: $unit * 3;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
|
||||
#ModalTop {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
margin-right: -8px;
|
||||
|
||||
h2 {
|
||||
font-size: $font-xlarge;
|
||||
font-weight: 500;
|
||||
text-align: left;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: $grey-50;
|
||||
padding: $unit / 2;
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
transform: rotate(45deg);
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
fill: $grey-00;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ModalBottom {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
a {
|
||||
color: #666;
|
||||
font-size: $font-regular;
|
||||
font-weight: 500;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.Button {
|
||||
display: block;
|
||||
min-height: $unit * 5;
|
||||
text-align: center;
|
||||
min-width: $unit * 11;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,10 +1,13 @@
|
|||
import React from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import './index.css'
|
||||
import './index.scss'
|
||||
import PlusIcon from '~public/icons/plus.svg'
|
||||
|
||||
interface Props {
|
||||
styleName?: string
|
||||
title: string
|
||||
close: () => void
|
||||
}
|
||||
|
||||
class Modal extends React.Component<Props> {
|
||||
|
|
@ -12,6 +15,10 @@ class Modal extends React.Component<Props> {
|
|||
return (
|
||||
<div className="ModalContainer">
|
||||
<div className={classnames("Modal", this.props.styleName)}>
|
||||
<div id="ModalTop">
|
||||
<h2>{this.props.title}</h2>
|
||||
<PlusIcon onClick={this.props.close} />
|
||||
</div>
|
||||
{this.props.children}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react'
|
||||
|
||||
import './index.css'
|
||||
import './index.scss'
|
||||
|
||||
interface Props {
|
||||
onClick: OnClickEvent
|
||||
|
|
@ -11,14 +11,15 @@ import SummonGrid from '~components/SummonGrid'
|
|||
import CharacterGrid from '~components/CharacterGrid'
|
||||
|
||||
// GridType
|
||||
export enum GridType {
|
||||
enum GridType {
|
||||
Class,
|
||||
Character,
|
||||
Weapon,
|
||||
Summon
|
||||
}
|
||||
export { GridType }
|
||||
|
||||
import './index.css'
|
||||
import './index.scss'
|
||||
|
||||
interface Props {
|
||||
partyId?: string
|
||||
48
components/SearchModal/index.scss
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
.ModalContainer .Modal.SearchModal {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 420px;
|
||||
min-width: 600px;
|
||||
padding: 0;
|
||||
|
||||
#ModalTop {
|
||||
background: $grey-90;
|
||||
gap: $unit;
|
||||
margin: 0;
|
||||
padding: ($unit * 3) ($unit * 3) ($unit * 1.5) ($unit * 3);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
|
||||
label {
|
||||
width: 100%;
|
||||
|
||||
.Input {
|
||||
box-sizing: border-box;
|
||||
padding: 12px 8px;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.SearchModal #results_container {
|
||||
margin: 0;
|
||||
max-height: 330px;
|
||||
padding: 0 12px 12px 12px;
|
||||
}
|
||||
|
||||
.SearchModal #NoResults {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.SearchModal #NoResults h2 {
|
||||
color: #ccc;
|
||||
font-size: $font-large;
|
||||
font-weight: 500;
|
||||
margin-top: -32px;
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react'
|
||||
|
||||
import { createPortal } from 'react-dom'
|
||||
import api from '~utils/api'
|
||||
|
||||
|
|
@ -8,11 +9,13 @@ import CharacterResult from '~components/CharacterResult'
|
|||
import WeaponResult from '~components/WeaponResult'
|
||||
import SummonResult from '~components/SummonResult'
|
||||
|
||||
import './index.css'
|
||||
import './index.scss'
|
||||
import PlusIcon from '~public/icons/plus.svg'
|
||||
|
||||
interface Props {
|
||||
close: () => void
|
||||
send: (object: Character | Weapon | Summon, position: number) => any
|
||||
grid: GridArray<Character|Weapon|Summon>
|
||||
placeholderText: string
|
||||
fromPosition: number
|
||||
object: 'weapons' | 'characters' | 'summons'
|
||||
|
|
@ -47,8 +50,17 @@ class SearchModal extends React.Component<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
filterExclusions = (o: Character | Weapon | Summon) => {
|
||||
if (this.props.grid[this.props.fromPosition] &&
|
||||
o.granblue_id == this.props.grid[this.props.fromPosition].granblue_id) {
|
||||
return null
|
||||
} else return o
|
||||
}
|
||||
|
||||
fetchResults = (query: string) => {
|
||||
api.search(this.props.object, query)
|
||||
const excludes = Object.values(this.props.grid).filter(this.filterExclusions).map((o) => { return o.name.en }).join(',')
|
||||
|
||||
api.search(this.props.object, query, excludes)
|
||||
.then((response) => {
|
||||
const data = response.data
|
||||
const totalResults = data.length
|
||||
|
|
@ -155,25 +167,28 @@ class SearchModal extends React.Component<Props, State> {
|
|||
return (
|
||||
createPortal(
|
||||
<div>
|
||||
<Modal styleName="SearchModal" key="search_modal">
|
||||
<div id="input_container">
|
||||
<label className="search_label" htmlFor="search_input">
|
||||
<input
|
||||
autoComplete="off"
|
||||
type="text"
|
||||
name="query"
|
||||
className="Input"
|
||||
id="search_input"
|
||||
ref={this.searchInput}
|
||||
value={query}
|
||||
placeholder={this.props.placeholderText}
|
||||
onChange={this.inputChanged}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className="ModalContainer">
|
||||
<div className="Modal SearchModal" key="search_modal">
|
||||
<div id="ModalTop">
|
||||
<label className="search_label" htmlFor="search_input">
|
||||
<input
|
||||
autoComplete="off"
|
||||
type="text"
|
||||
name="query"
|
||||
className="Input"
|
||||
id="search_input"
|
||||
ref={this.searchInput}
|
||||
value={query}
|
||||
placeholder={this.props.placeholderText}
|
||||
onChange={this.inputChanged}
|
||||
/>
|
||||
</label>
|
||||
<PlusIcon onClick={this.props.close} />
|
||||
</div>
|
||||
|
||||
{content}
|
||||
</Modal>
|
||||
{content}
|
||||
</div>
|
||||
</div>
|
||||
<Overlay onClick={this.props.close} />
|
||||
</div>,
|
||||
document.body
|
||||
|
|
@ -33,11 +33,4 @@
|
|||
background: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include media('>small', '<=medium') {
|
||||
.Segment {
|
||||
flex-grow: 1;
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
}
|
||||
|
|
@ -13,14 +13,4 @@
|
|||
overflow: hidden;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@include media('>small', '<=medium') {
|
||||
.SegmentedControlWrapper {
|
||||
justify-content: stretch;
|
||||
}
|
||||
|
||||
.SegmentedControl {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#ModalTop h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-size: $font-xlarge;
|
||||
text-align: left;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
|
@ -55,7 +55,7 @@
|
|||
|
||||
#ModalBottom a {
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
font-size: $font-regular;
|
||||
font-weight: 500;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
|
|
@ -8,9 +8,9 @@ import Fieldset from '~components/Fieldset'
|
|||
import Modal from '~components/Modal'
|
||||
import Overlay from '~components/Overlay'
|
||||
|
||||
import './index.css'
|
||||
import './index.scss'
|
||||
|
||||
import New from '../../../assets/new'
|
||||
// import New from '../../../assets/new'
|
||||
|
||||
interface Props {
|
||||
cookies: Cookies
|
||||
|
|
@ -160,11 +160,11 @@ class SignupModal extends React.Component<Props, State> {
|
|||
return (
|
||||
createPortal(
|
||||
<div>
|
||||
<Modal styleName="SignupForm">
|
||||
<div id="ModalTop">
|
||||
<h1>Sign up</h1>
|
||||
<i className='close' onClick={this.props.close}><New /></i>
|
||||
</div>
|
||||
<Modal
|
||||
title="Sign up"
|
||||
styleName="SignupForm"
|
||||
close={ () => {} }
|
||||
>
|
||||
<form className="form" onSubmit={this.process}>
|
||||
<div id="fields">
|
||||
<Fieldset
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
& .Label {
|
||||
color: $grey-50;
|
||||
font-size: 12px;
|
||||
font-size: $font-small;
|
||||
font-weight: $medium;
|
||||
margin-bottom: $unit;
|
||||
text-align: center;
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
.SummonResult h5 {
|
||||
color: #555;
|
||||
display: inline-block;
|
||||
font-size: 18px;
|
||||
font-size: $font-large;
|
||||
font-weight: 500;
|
||||
margin: 2px 4px 4px 0;
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
.SummonResult .stars {
|
||||
display: inline-block;
|
||||
color: #FFA15E;
|
||||
font-size: 21px;
|
||||
font-size: $font-xlarge;
|
||||
}
|
||||
|
||||
.SummonResult .stars > span {
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
import React from 'react'
|
||||
import WeaponLabelIcon from '~components/WeaponLabelIcon'
|
||||
|
||||
import gridImages from '../../images/summon-grid/*.jpg'
|
||||
|
||||
import './index.css'
|
||||
import './index.scss'
|
||||
|
||||
interface Props {
|
||||
data: Summon
|
||||
|
|
@ -14,18 +12,11 @@ const Element = ['null', 'wind', 'fire', 'water', 'earth', 'dark', 'light']
|
|||
|
||||
class SummonResult extends React.Component<Props> {
|
||||
render() {
|
||||
let imgSrc
|
||||
|
||||
const summon = this.props.data
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
imgSrc = gridImages[summon.granblue_id]
|
||||
} else if (process.env.NODE_ENV === 'production') {
|
||||
imgSrc = `${process.env.REACT_APP_SIERO_IMG_URL}/summon-grid/${summon.granblue_id}.jpg`
|
||||
}
|
||||
|
||||
return (
|
||||
<li className="SummonResult" onClick={this.props.onClick}>
|
||||
<img alt={summon.name.en} src={imgSrc} />
|
||||
<img alt={summon.name.en} src={`/images/summon-grid/${summon.granblue_id}.jpg`} />
|
||||
<div>
|
||||
<div>
|
||||
<h5>{summon.name.en}</h5>
|
||||
|
|
@ -50,7 +50,7 @@
|
|||
|
||||
h3 {
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
font-size: $font-regular;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
import classnames from 'classnames'
|
||||
import { useModal as useModal } from '~utils/useModal'
|
||||
|
||||
import SearchModal from '~components/SearchModal'
|
||||
import UncapIndicator from '~components/UncapIndicator'
|
||||
|
||||
import mainImages from '../../images/summon-main/*.jpg'
|
||||
import gridImages from '../../images/summon-grid/*.jpg'
|
||||
import Plus from '../../../assets/plus.svg'
|
||||
import PlusIcon from '~public/icons/plus.svg'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
|
|
@ -41,22 +40,15 @@ const SummonUnit = (props: Props) => {
|
|||
})
|
||||
|
||||
function generateImageUrl() {
|
||||
let imgSrc
|
||||
let imgSrc = ""
|
||||
if (props.summon) {
|
||||
const summon = props.summon!
|
||||
|
||||
// Generate the correct source for the weapon
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
if (props.unitType == 0 || props.unitType == 2)
|
||||
imgSrc = mainImages[summon.granblue_id]
|
||||
else
|
||||
imgSrc = gridImages[summon.granblue_id]
|
||||
} else if (process.env.NODE_ENV === 'production') {
|
||||
if (props.unitType == 0 || props.unitType == 2)
|
||||
imgSrc = `${process.env.REACT_APP_SIERO_IMG_URL}/summon-main/${summon.granblue_id}.jpg`
|
||||
else
|
||||
imgSrc = `${process.env.REACT_APP_SIERO_IMG_URL}/summon-grid/${summon.granblue_id}.jpg`
|
||||
}
|
||||
// Generate the correct source for the summon
|
||||
if (props.unitType == 0 || props.unitType == 2)
|
||||
imgSrc = `/images/summon-main/${summon.granblue_id}.jpg`
|
||||
else
|
||||
imgSrc = `/images/summon-grid/${summon.granblue_id}.jpg`
|
||||
}
|
||||
|
||||
setImageUrl(imgSrc)
|
||||
|
|
@ -77,13 +69,8 @@ const SummonUnit = (props: Props) => {
|
|||
<div>
|
||||
<div className={classes} onClick={openModalIfEditable}>
|
||||
<div className="SummonImage">
|
||||
{
|
||||
(imageUrl != '')
|
||||
? <img className="grid_image" src={imageUrl} />
|
||||
: <img className="grid_image" />
|
||||
|
||||
}
|
||||
{ (props.editable) ? <span className='icon'><Plus /></span> : '' }
|
||||
<img alt={summon?.name.en} className="grid_image" src={imageUrl} />
|
||||
{ (props.editable) ? <span className='icon'><PlusIcon /></span> : '' }
|
||||
</div>
|
||||
<UncapIndicator
|
||||
type="summon"
|
||||
|
|
@ -2,7 +2,7 @@ import React from 'react'
|
|||
import classnames from 'classnames'
|
||||
import UncapStar from '~components/UncapStar'
|
||||
|
||||
import './index.css'
|
||||
import './index.scss'
|
||||
|
||||
|
||||
interface Props {
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
import React from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import './index.css'
|
||||
|
||||
import Star from '../../../assets/star'
|
||||
import './index.scss'
|
||||
|
||||
interface Props {
|
||||
uncap: boolean
|
||||
|
|
@ -16,7 +14,7 @@ const UncapStar = (props: Props) => {
|
|||
})
|
||||
|
||||
return (
|
||||
<li className={classes}><Star /></li>
|
||||
<li className={classes}><img src="/icons/star.svg" /></li>
|
||||
)
|
||||
}
|
||||
|
||||
79
components/WeaponLabelIcon/index.scss
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
.WeaponLabelIcon {
|
||||
display: inline-block;
|
||||
background-size: 60px 25px;
|
||||
height: 25px;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
/* Elements */
|
||||
|
||||
.fire {
|
||||
background-image: url('/images/labels/element/Label_Element_Fire.png')
|
||||
}
|
||||
|
||||
.water {
|
||||
background-image: url('/images/labels/element/Label_Element_Water.png')
|
||||
}
|
||||
|
||||
.earth {
|
||||
background-image: url('/images/labels/element/Label_Element_Earth.png')
|
||||
}
|
||||
|
||||
.wind {
|
||||
background-image: url('/images/labels/element/Label_Element_Wind.png')
|
||||
}
|
||||
|
||||
.dark {
|
||||
background-image: url('/images/labels/element/Label_Element_Dark.png')
|
||||
}
|
||||
|
||||
.light {
|
||||
background-image: url('/images/labels/element/Label_Element_Light.png')
|
||||
}
|
||||
|
||||
.null {
|
||||
background-image: url('/images/labels/element/Label_Element_Any.png')
|
||||
}
|
||||
|
||||
/* Proficiencies */
|
||||
|
||||
.sword {
|
||||
background-image: url('/images/labels/proficiency/Label_Weapon_Sabre.png')
|
||||
}
|
||||
|
||||
.dagger {
|
||||
background-image: url('/images/labels/proficiency/Label_Weapon_Dagger.png')
|
||||
}
|
||||
|
||||
.axe {
|
||||
background-image: url('/images/labels/proficiency/Label_Weapon_Axe.png')
|
||||
}
|
||||
|
||||
.spear {
|
||||
background-image: url('/images/labels/proficiency/Label_Weapon_Spear.png')
|
||||
}
|
||||
|
||||
.staff {
|
||||
background-image: url('/images/labels/proficiency/Label_Weapon_Staff.png')
|
||||
}
|
||||
|
||||
.fist {
|
||||
background-image: url('/images/labels/proficiency/Label_Weapon_Melee.png')
|
||||
}
|
||||
|
||||
.harp {
|
||||
background-image: url('/images/labels/proficiency/Label_Weapon_Harp.png')
|
||||
}
|
||||
|
||||
.gun {
|
||||
background-image: url('/images/labels/proficiency/Label_Weapon_Gun.png')
|
||||
}
|
||||
|
||||
.bow {
|
||||
background-image: url('/images/labels/proficiency/Label_Weapon_Bow.png')
|
||||
}
|
||||
|
||||
.katana {
|
||||
background-image: url('/images/labels/proficiency/Label_Weapon_Katana.png')
|
||||
}
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react'
|
||||
|
||||
import './index.css'
|
||||
import './index.scss'
|
||||
|
||||
interface Props {
|
||||
labelType: string
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
.WeaponResult h5 {
|
||||
color: #555;
|
||||
display: inline-block;
|
||||
font-size: 18px;
|
||||
font-size: $font-large;
|
||||
font-weight: 500;
|
||||
margin: 2px 4px 4px 0;
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
.WeaponResult .stars {
|
||||
display: inline-block;
|
||||
color: #FFA15E;
|
||||
font-size: 21px;
|
||||
font-size: $font-xlarge;
|
||||
}
|
||||
|
||||
.WeaponResult .stars > span {
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
import React from 'react'
|
||||
import WeaponLabelIcon from '~components/WeaponLabelIcon'
|
||||
|
||||
import gridImages from '../../images/weapon-grid/*.jpg'
|
||||
|
||||
import './index.css'
|
||||
import './index.scss'
|
||||
|
||||
interface Props {
|
||||
data: Weapon
|
||||
|
|
@ -16,18 +14,11 @@ const Series = ['seraphic', 'grand', 'opus', 'draconic', 'revenant', 'primal', '
|
|||
|
||||
class WeaponResult extends React.Component<Props> {
|
||||
render() {
|
||||
let imgSrc
|
||||
|
||||
const weapon = this.props.data
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
imgSrc = gridImages[weapon.granblue_id]
|
||||
} else if (process.env.NODE_ENV === 'production') {
|
||||
imgSrc = `${process.env.REACT_APP_SIERO_IMG_URL}/weapon-grid/${weapon.granblue_id}.jpg`
|
||||
}
|
||||
|
||||
return (
|
||||
<li className="WeaponResult" onClick={this.props.onClick}>
|
||||
<img alt={weapon.name.en} src={imgSrc} />
|
||||
<img alt={weapon.name.en} src={`/images/weapon-grid/${weapon.granblue_id}.jpg`} />
|
||||
<div>
|
||||
<div>
|
||||
<h5>{weapon.name.en}</h5>
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
.WeaponUnit h3 {
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
font-size: $font-regular;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
import classnames from 'classnames'
|
||||
import { useModal as useModal } from '~utils/useModal'
|
||||
|
||||
import SearchModal from '~components/SearchModal'
|
||||
import UncapIndicator from '~components/UncapIndicator'
|
||||
|
||||
import mainImages from '../../images/weapon-main/*.jpg'
|
||||
import gridImages from '../../images/weapon-grid/*.jpg'
|
||||
import Plus from '../../../assets/plus.svg'
|
||||
import PlusIcon from '~public/icons/plus.svg'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
|
|
@ -41,22 +40,14 @@ const WeaponUnit = (props: Props) => {
|
|||
})
|
||||
|
||||
function generateImageUrl() {
|
||||
let imgSrc
|
||||
let imgSrc = ""
|
||||
if (props.weapon) {
|
||||
const weapon = props.weapon!
|
||||
|
||||
// Generate the correct source for the weapon
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
if (props.unitType == 0)
|
||||
imgSrc = mainImages[weapon.granblue_id]
|
||||
else
|
||||
imgSrc = gridImages[weapon.granblue_id]
|
||||
} else if (process.env.NODE_ENV === 'production') {
|
||||
if (props.unitType == 0)
|
||||
imgSrc = `${process.env.REACT_APP_SIERO_IMG_URL}/weapon-main/${weapon.granblue_id}.jpg`
|
||||
else
|
||||
imgSrc = `${process.env.REACT_APP_SIERO_IMG_URL}/weapon-grid/${weapon.granblue_id}.jpg`
|
||||
}
|
||||
if (props.unitType == 0)
|
||||
imgSrc = `/images/weapon-main/${weapon.granblue_id}.jpg`
|
||||
else
|
||||
imgSrc = `/images/weapon-grid/${weapon.granblue_id}.jpg`
|
||||
}
|
||||
|
||||
setImageUrl(imgSrc)
|
||||
|
|
@ -76,13 +67,8 @@ const WeaponUnit = (props: Props) => {
|
|||
<div>
|
||||
<div className={classes} onClick={openModalIfEditable}>
|
||||
<div className="WeaponImage">
|
||||
{
|
||||
(imageUrl != '')
|
||||
? <img className="grid_image" src={imageUrl} />
|
||||
: <img className="grid_image" />
|
||||
|
||||
}
|
||||
{ (props.editable) ? <span className='icon'><Plus /></span> : '' }
|
||||
<img alt={weapon?.name.en} className="grid_image" src={imageUrl} />
|
||||
{ (props.editable) ? <span className='icon'><PlusIcon /></span> : '' }
|
||||
</div>
|
||||
<UncapIndicator
|
||||
type="weapon"
|
||||
17
index.js
|
|
@ -1,17 +0,0 @@
|
|||
const express = require('express');
|
||||
const path = require('path');
|
||||
|
||||
const app = express();
|
||||
|
||||
// Serve the static files from the React app
|
||||
app.use(express.static(path.join(__dirname, 'dist')));
|
||||
|
||||
// Handles any requests that don't match the ones above
|
||||
app.get('*', (req, res) =>{
|
||||
res.sendFile(path.join(__dirname+'/dist/index.html'));
|
||||
});
|
||||
|
||||
const port = process.env.PORT || 3000;
|
||||
app.listen(port);
|
||||
|
||||
console.log('App is listening on port ' + port);
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src"
|
||||
}
|
||||
}
|
||||
5
next-env.d.ts
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
21
next.config.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {reactStrictMode: true,
|
||||
sassOptions: {
|
||||
prependData: '@import "variables";',
|
||||
includePaths: [path.join(__dirname, 'styles')],
|
||||
},
|
||||
webpack(config) {
|
||||
config.module.rules.push({
|
||||
test: /\.svg$/,
|
||||
use: ["@svgr/webpack"]
|
||||
});
|
||||
config.module.rules[2].oneOf.forEach((one) => {
|
||||
if (!`${one.issuer?.and}`.includes('_app')) return;
|
||||
one.issuer.and = [path.resolve(__dirname)];
|
||||
});
|
||||
return config;
|
||||
},
|
||||
};
|
||||
|
||||
21801
package-lock.json
generated
73
package.json
|
|
@ -1,52 +1,35 @@
|
|||
{
|
||||
"name": "hensei-web",
|
||||
"version": "1.0.0",
|
||||
"author": "Justin Edmund (@jedmund)",
|
||||
"main": "./src/index.html",
|
||||
"browserslist": "last 2 versions",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.8.7",
|
||||
"@babel/plugin-transform-runtime": "^7.8.3",
|
||||
"@svgr/parcel-plugin-svgr": "^5.4.0",
|
||||
"@types/axios": "^0.14.0",
|
||||
"@types/classnames": "^2.2.10",
|
||||
"@types/react-router": "^5.1.8",
|
||||
"@types/react-router-dom": "^5.1.5",
|
||||
"@types/recompose": "^0.30.7",
|
||||
"autoprefixer": "^9.8.6",
|
||||
"axios": "^0.20.0",
|
||||
"classnames": "^2.2.6",
|
||||
"components": "^0.1.0",
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"i18next": "^19.7.0",
|
||||
"i18next-browser-languagedetector": "^6.0.1",
|
||||
"i18next-xhr-backend": "^3.2.2",
|
||||
"include-media": "^1.4.9",
|
||||
"meyer-reset-scss": "^2.0.4",
|
||||
"parcel-bundler": "^1.12.4",
|
||||
"postcss-modules": "^3.2.2",
|
||||
"postcss-normalize": "^9.0.0",
|
||||
"react": "^16.13.1",
|
||||
"react-cookie": "^4.0.3",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-i18next": "^11.7.2",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"recompose": "^0.30.0",
|
||||
"sass": "^1.27.0",
|
||||
"typescript": "^4.0.2"
|
||||
},
|
||||
"name": "grid-web",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "parcel ./src/index.html",
|
||||
"start": "PORT=1234 /opt/homebrew/bin/node index.js",
|
||||
"prestart": "parcel build ./src/index.html",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"dev": "next dev -p 1234",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"compilerOptions": {
|
||||
"jsx": "react"
|
||||
"dependencies": {
|
||||
"@radix-ui/react-dialog": "^0.1.5",
|
||||
"@radix-ui/react-dropdown-menu": "^0.1.4",
|
||||
"@radix-ui/react-hover-card": "^0.1.3",
|
||||
"@radix-ui/react-label": "^0.1.4",
|
||||
"@radix-ui/react-switch": "^0.1.4",
|
||||
"@svgr/webpack": "^6.2.0",
|
||||
"axios": "^0.25.0",
|
||||
"classnames": "^2.3.1",
|
||||
"meyer-reset-scss": "^2.0.4",
|
||||
"next": "12.0.8",
|
||||
"react": "17.0.2",
|
||||
"react-cookie": "^4.1.1",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-i18next": "^11.15.3",
|
||||
"react-router-dom": "^6.2.1",
|
||||
"sass": "^1.49.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.12",
|
||||
"node-sass-package-importer": "^5.3.2"
|
||||
"@types/node": "17.0.11",
|
||||
"@types/react": "17.0.38",
|
||||
"eslint": "8.7.0",
|
||||
"eslint-config-next": "12.0.8",
|
||||
"typescript": "4.5.5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
9
pages/_app.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import '../styles/globals.scss'
|
||||
|
||||
import type { AppProps } from 'next/app'
|
||||
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
return <Component {...pageProps} />
|
||||
}
|
||||
|
||||
export default MyApp
|
||||
13
pages/api/hello.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
type Data = {
|
||||
name: string
|
||||
}
|
||||
|
||||
export default function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
res.status(200).json({ name: 'John Doe' })
|
||||
}
|
||||
23
pages/index.tsx
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import type { NextPage } from 'next'
|
||||
import Head from 'next/head'
|
||||
import styles from '../styles/Home.module.css'
|
||||
|
||||
import { CookiesProvider } from 'react-cookie'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
|
||||
import App from '~/components/App'
|
||||
|
||||
const Home: NextPage = () => {
|
||||
if (typeof window === "undefined")
|
||||
return null
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<CookiesProvider>
|
||||
<App />
|
||||
</CookiesProvider>
|
||||
</BrowserRouter>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home
|
||||
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
3
public/icons/menu.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 1C0 0.447715 0.447715 0 1 0H11C11.5523 0 12 0.447715 12 1C12 1.55228 11.5523 2 11 2H1C0.447715 2 0 1.55228 0 1ZM0 6C0 5.44772 0.447715 5 1 5H11C11.5523 5 12 5.44772 12 6C12 6.55228 11.5523 7 11 7H1C0.447715 7 0 6.55228 0 6ZM1 10C0.447715 10 0 10.4477 0 11C0 11.5523 0.447715 12 1 12H11C11.5523 12 12 11.5523 12 11C12 10.4477 11.5523 10 11 10H1Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 496 B |
|
Before Width: | Height: | Size: 414 B After Width: | Height: | Size: 414 B |
|
Before Width: | Height: | Size: 700 B After Width: | Height: | Size: 700 B |
|
Before Width: | Height: | Size: 754 B After Width: | Height: | Size: 754 B |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 6 KiB After Width: | Height: | Size: 6 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |