Add optional toggle for Extra grid slots
This commit is contained in:
parent
59096faec9
commit
d1b8e52bdb
12 changed files with 300 additions and 34 deletions
32
src/components/ExtraWeapons/index.scss
Normal file
32
src/components/ExtraWeapons/index.scss
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
.ExtraWeapons {
|
||||||
|
background: #ECEBFF;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 20px auto;
|
||||||
|
max-width: 766px;
|
||||||
|
padding: 16px 16px 16px 0;
|
||||||
|
position: relative;
|
||||||
|
left: 8px;
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
color: #4F3C79;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
line-height: 1.2;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-right: 16px;
|
||||||
|
text-align: center;
|
||||||
|
width: 206px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WeaponUnit .WeaponImage {
|
||||||
|
background: #D5D3F6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WeaponUnit .WeaponImage .icon svg {
|
||||||
|
fill: #8F8AC6;
|
||||||
|
}
|
||||||
|
}
|
||||||
54
src/components/ExtraWeapons/index.tsx
Normal file
54
src/components/ExtraWeapons/index.tsx
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import React from 'react'
|
||||||
|
import WeaponUnit from '~components/WeaponUnit'
|
||||||
|
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
// GridType
|
||||||
|
export enum GridType {
|
||||||
|
Class,
|
||||||
|
Character,
|
||||||
|
Weapon,
|
||||||
|
Summon
|
||||||
|
}
|
||||||
|
|
||||||
|
// Props
|
||||||
|
interface Props {
|
||||||
|
grid: GridArray<Weapon>
|
||||||
|
editable: boolean
|
||||||
|
exists: boolean
|
||||||
|
found?: boolean
|
||||||
|
onSelect: (type: GridType, weapon: Weapon, position: number) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const ExtraWeapons = (props: Props) => {
|
||||||
|
const numWeapons: number = 3
|
||||||
|
|
||||||
|
function receiveWeapon(weapon: Weapon, position: number) {
|
||||||
|
props.onSelect(GridType.Weapon, weapon, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ExtraWeapons">
|
||||||
|
<span>Additional<br />Weapons</span>
|
||||||
|
<ul id="grid_weapons">
|
||||||
|
{
|
||||||
|
Array.from(Array(numWeapons)).map((x, i) => {
|
||||||
|
return (
|
||||||
|
<li key={`grid_unit_${i}`} >
|
||||||
|
<WeaponUnit
|
||||||
|
editable={props.editable}
|
||||||
|
onReceiveData={receiveWeapon}
|
||||||
|
position={i}
|
||||||
|
unitType={1}
|
||||||
|
weapon={props.grid[i]}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ExtraWeapons
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
.Extra {
|
||||||
|
color: #888;
|
||||||
|
display: flex;
|
||||||
|
font-weight: 500;
|
||||||
|
gap: 8px;
|
||||||
|
line-height: 34px;
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { ChangeEvent, useEffect, useState } from 'react'
|
||||||
import { useCookies } from 'react-cookie'
|
import { useCookies } from 'react-cookie'
|
||||||
import api from '~utils/api'
|
import api from '~utils/api'
|
||||||
|
|
||||||
|
|
@ -28,6 +28,7 @@ interface Props {
|
||||||
characters?: GridArray<Character>
|
characters?: GridArray<Character>
|
||||||
weapons?: GridArray<Weapon>
|
weapons?: GridArray<Weapon>
|
||||||
summons?: GridArray<Summon>
|
summons?: GridArray<Summon>
|
||||||
|
extra: boolean
|
||||||
editable: boolean
|
editable: boolean
|
||||||
exists: boolean
|
exists: boolean
|
||||||
pushHistory?: (path: string) => void
|
pushHistory?: (path: string) => void
|
||||||
|
|
@ -51,6 +52,8 @@ const Party = (props: Props) => {
|
||||||
const [mainSummon, setMainSummon] = useState<Summon>()
|
const [mainSummon, setMainSummon] = useState<Summon>()
|
||||||
const [friendSummon, setFriendSummon] = useState<Summon>()
|
const [friendSummon, setFriendSummon] = useState<Summon>()
|
||||||
|
|
||||||
|
const [extra, setExtra] = useState<boolean>(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setPartyId(props.partyId || '')
|
setPartyId(props.partyId || '')
|
||||||
setMainWeapon(props.mainWeapon)
|
setMainWeapon(props.mainWeapon)
|
||||||
|
|
@ -59,7 +62,8 @@ const Party = (props: Props) => {
|
||||||
setCharacters(props.characters || {})
|
setCharacters(props.characters || {})
|
||||||
setWeapons(props.weapons || {})
|
setWeapons(props.weapons || {})
|
||||||
setSummons(props.summons || {})
|
setSummons(props.summons || {})
|
||||||
}, [props.partyId, props.mainWeapon, props.mainSummon, props.friendSummon, props.characters, props.weapons, props.summons])
|
setExtra(props.extra || false)
|
||||||
|
}, [props.partyId, props.mainWeapon, props.mainSummon, props.friendSummon, props.characters, props.weapons, props.summons, props.extra])
|
||||||
|
|
||||||
const weaponGrid = (
|
const weaponGrid = (
|
||||||
<WeaponGrid
|
<WeaponGrid
|
||||||
|
|
@ -68,6 +72,7 @@ const Party = (props: Props) => {
|
||||||
grid={weapons}
|
grid={weapons}
|
||||||
editable={props.editable}
|
editable={props.editable}
|
||||||
exists={props.exists}
|
exists={props.exists}
|
||||||
|
extra={extra}
|
||||||
onSelect={itemSelected}
|
onSelect={itemSelected}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
@ -97,8 +102,15 @@ const Party = (props: Props) => {
|
||||||
const [currentTab, setCurrentTab] = useState<GridType>(GridType.Weapon)
|
const [currentTab, setCurrentTab] = useState<GridType>(GridType.Weapon)
|
||||||
const [partyId, setPartyId] = useState('')
|
const [partyId, setPartyId] = useState('')
|
||||||
|
|
||||||
|
function checkboxChanged(event: React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
setExtra(event.target.checked)
|
||||||
|
}
|
||||||
|
|
||||||
function segmentClicked(event: React.ChangeEvent<HTMLInputElement>) {
|
function segmentClicked(event: React.ChangeEvent<HTMLInputElement>) {
|
||||||
switch(event.target.value) {
|
switch(event.target.value) {
|
||||||
|
case 'class':
|
||||||
|
setCurrentTab(GridType.Class)
|
||||||
|
break
|
||||||
case 'characters':
|
case 'characters':
|
||||||
setCurrentTab(GridType.Character)
|
setCurrentTab(GridType.Character)
|
||||||
break
|
break
|
||||||
|
|
@ -136,9 +148,14 @@ const Party = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createParty() {
|
async function createParty() {
|
||||||
const body = (cookies.userId === undefined) ? {} : {
|
const body = (cookies.userId === undefined) ? {
|
||||||
party: {
|
party: {
|
||||||
user_id: cookies.userId
|
is_extra: extra
|
||||||
|
}
|
||||||
|
} : {
|
||||||
|
party: {
|
||||||
|
user_id: cookies.userId,
|
||||||
|
is_extra: extra
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -249,8 +266,11 @@ const Party = (props: Props) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PartySegmentedControl
|
<PartySegmentedControl
|
||||||
|
extra={extra}
|
||||||
|
editable={props.editable}
|
||||||
selectedTab={currentTab}
|
selectedTab={currentTab}
|
||||||
onClick={segmentClicked}
|
onClick={segmentClicked}
|
||||||
|
onCheckboxChange={checkboxChanged}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
||||||
16
src/components/PartySegmentedControl/index.scss
Normal file
16
src/components/PartySegmentedControl/index.scss
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
.PartyNavigation {
|
||||||
|
display: flex;
|
||||||
|
gap: 58px;
|
||||||
|
justify-content: right;
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 760px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Extra {
|
||||||
|
color: #888;
|
||||||
|
display: flex;
|
||||||
|
font-weight: 500;
|
||||||
|
gap: 8px;
|
||||||
|
line-height: 34px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
import SegmentedControl from '~components/SegmentedControl'
|
import SegmentedControl from '~components/SegmentedControl'
|
||||||
import Segment from '~components/Segment'
|
import Segment from '~components/Segment'
|
||||||
|
import ToggleSwitch from '~components/ToggleSwitch'
|
||||||
|
|
||||||
// GridType
|
// GridType
|
||||||
export enum GridType {
|
export enum GridType {
|
||||||
|
|
@ -11,21 +14,23 @@ export enum GridType {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
editable: boolean
|
||||||
|
extra: boolean
|
||||||
selectedTab: GridType
|
selectedTab: GridType
|
||||||
onClick: (event: React.ChangeEvent<HTMLInputElement>) => void
|
onClick: (event: React.ChangeEvent<HTMLInputElement>) => void
|
||||||
|
onCheckboxChange: (event: React.ChangeEvent<HTMLInputElement>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const PartySegmentedControl = (props: Props) => {
|
const PartySegmentedControl = (props: Props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="PartyNavigation">
|
||||||
<SegmentedControl>
|
<SegmentedControl>
|
||||||
{/* <Segment
|
<Segment
|
||||||
groupName="grid"
|
groupName="grid"
|
||||||
name="class"
|
name="class"
|
||||||
selected={props.selectedTab === GridType.Class}
|
selected={props.selectedTab === GridType.Class}
|
||||||
onClick={props.onClick}
|
onClick={props.onClick}
|
||||||
>Class</Segment> */}
|
>Class</Segment>
|
||||||
|
|
||||||
<Segment
|
<Segment
|
||||||
groupName="grid"
|
groupName="grid"
|
||||||
|
|
@ -48,6 +53,16 @@ const PartySegmentedControl = (props: Props) => {
|
||||||
onClick={props.onClick}
|
onClick={props.onClick}
|
||||||
>Summons</Segment>
|
>Summons</Segment>
|
||||||
</SegmentedControl>
|
</SegmentedControl>
|
||||||
|
|
||||||
|
<div className="Extra">
|
||||||
|
Extra
|
||||||
|
<ToggleSwitch
|
||||||
|
name="Extra"
|
||||||
|
editable={props.editable}
|
||||||
|
checked={props.extra}
|
||||||
|
onChange={props.onCheckboxChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
60
src/components/ToggleSwitch/index.scss
Normal file
60
src/components/ToggleSwitch/index.scss
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
.toggle-switch {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 18px;
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
width: 58px;
|
||||||
|
|
||||||
|
&-checkbox {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-label {
|
||||||
|
border-radius: 17px;
|
||||||
|
display: block;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 34px;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-disabled {
|
||||||
|
background-color: #ddd;
|
||||||
|
cursor: not-allowed;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
background-color: #ddd;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-switch {
|
||||||
|
background: #e4e4e4;
|
||||||
|
display: block;
|
||||||
|
width: 24px;
|
||||||
|
margin: 5px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 24px;
|
||||||
|
border-radius: 17px;
|
||||||
|
transition: all 0.18s ease-in 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-checkbox:checked + &-label {
|
||||||
|
background: #ECEBFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-checkbox:checked + &-label &-switch {
|
||||||
|
background: #8C86FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-checkbox:checked + &-label {
|
||||||
|
.toggle-switch-inner {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
.toggle-switch-switch {
|
||||||
|
right: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/components/ToggleSwitch/index.tsx
Normal file
31
src/components/ToggleSwitch/index.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
name: string
|
||||||
|
checked: boolean
|
||||||
|
editable: boolean
|
||||||
|
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const ToggleSwitch: React.FC<Props> = (props: Props) => {
|
||||||
|
return (
|
||||||
|
<div className="toggle-switch">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={props.checked}
|
||||||
|
disabled={!props.editable}
|
||||||
|
className="toggle-switch-checkbox"
|
||||||
|
name={props.name}
|
||||||
|
id={props.name}
|
||||||
|
onChange={props.onChange}
|
||||||
|
/>
|
||||||
|
<label className="toggle-switch-label" htmlFor={props.name}>
|
||||||
|
<span className="toggle-switch-switch" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ToggleSwitch
|
||||||
|
|
@ -3,6 +3,10 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ExtraWeapons #grid_weapons > * {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
#grid_weapons {
|
#grid_weapons {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import WeaponUnit from '~components/WeaponUnit'
|
import WeaponUnit from '~components/WeaponUnit'
|
||||||
|
import ExtraWeapons from '~components/ExtraWeapons'
|
||||||
|
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
|
|
@ -17,6 +18,7 @@ interface Props {
|
||||||
partyId?: string
|
partyId?: string
|
||||||
mainhand?: Weapon | undefined
|
mainhand?: Weapon | undefined
|
||||||
grid: GridArray<Weapon>
|
grid: GridArray<Weapon>
|
||||||
|
extra: boolean
|
||||||
editable: boolean
|
editable: boolean
|
||||||
exists: boolean
|
exists: boolean
|
||||||
found?: boolean
|
found?: boolean
|
||||||
|
|
@ -26,38 +28,59 @@ interface Props {
|
||||||
const WeaponGrid = (props: Props) => {
|
const WeaponGrid = (props: Props) => {
|
||||||
const numWeapons: number = 9
|
const numWeapons: number = 9
|
||||||
|
|
||||||
|
const extraGrid = (
|
||||||
|
<ExtraWeapons
|
||||||
|
grid={props.grid}
|
||||||
|
editable={props.editable}
|
||||||
|
exists={false}
|
||||||
|
onSelect={
|
||||||
|
function (type: GridType, weapon: Weapon, position: number): void {
|
||||||
|
throw new Error('Function not implemented.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
function receiveWeapon(weapon: Weapon, position: number) {
|
function receiveWeapon(weapon: Weapon, position: number) {
|
||||||
props.onSelect(GridType.Weapon, weapon, position)
|
props.onSelect(GridType.Weapon, weapon, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="WeaponGrid">
|
<div id="weapon_grids">
|
||||||
<WeaponUnit
|
<div className="WeaponGrid">
|
||||||
editable={props.editable}
|
<WeaponUnit
|
||||||
key="grid_mainhand"
|
editable={props.editable}
|
||||||
onReceiveData={receiveWeapon}
|
key="grid_mainhand"
|
||||||
position={-1}
|
onReceiveData={receiveWeapon}
|
||||||
unitType={0}
|
position={-1}
|
||||||
weapon={props.mainhand}
|
unitType={0}
|
||||||
/>
|
weapon={props.mainhand}
|
||||||
|
/>
|
||||||
|
|
||||||
<ul id="grid_weapons">
|
<ul id="grid_weapons">
|
||||||
{
|
{
|
||||||
Array.from(Array(numWeapons)).map((x, i) => {
|
Array.from(Array(numWeapons)).map((x, i) => {
|
||||||
return (
|
return (
|
||||||
<li key={`grid_unit_${i}`} >
|
<li key={`grid_unit_${i}`} >
|
||||||
<WeaponUnit
|
<WeaponUnit
|
||||||
editable={props.editable}
|
editable={props.editable}
|
||||||
onReceiveData={receiveWeapon}
|
onReceiveData={receiveWeapon}
|
||||||
position={i}
|
position={i}
|
||||||
unitType={1}
|
unitType={1}
|
||||||
weapon={props.grid[i]}
|
weapon={props.grid[i]}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{ (() => {
|
||||||
|
if(props.extra) {
|
||||||
|
return extraGrid
|
||||||
|
}
|
||||||
|
})() }
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ const NewRoute: React.FC<Props> = () => {
|
||||||
<div id="Content">
|
<div id="Content">
|
||||||
<Party
|
<Party
|
||||||
editable={true}
|
editable={true}
|
||||||
|
extra={false}
|
||||||
exists={false}
|
exists={false}
|
||||||
pushHistory={callback}
|
pushHistory={callback}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ const PartyRoute: React.FC<PartyProps> = ({ match }) => {
|
||||||
const [friendSummon, setFriendSummon] = useState<Summon>()
|
const [friendSummon, setFriendSummon] = useState<Summon>()
|
||||||
|
|
||||||
const [partyId, setPartyId] = useState('')
|
const [partyId, setPartyId] = useState('')
|
||||||
|
const [extra, setExtra] = useState<boolean>(false)
|
||||||
const [cookies, setCookie] = useCookies(['user'])
|
const [cookies, setCookie] = useCookies(['user'])
|
||||||
const shortcode = match.params.hash || ''
|
const shortcode = match.params.hash || ''
|
||||||
|
|
||||||
|
|
@ -69,6 +70,7 @@ const PartyRoute: React.FC<PartyProps> = ({ match }) => {
|
||||||
summons[gridSummon.position] = gridSummon.summon
|
summons[gridSummon.position] = gridSummon.summon
|
||||||
})
|
})
|
||||||
|
|
||||||
|
setExtra(response.data.party.is_extra)
|
||||||
setFound(true)
|
setFound(true)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
setCharacters(characters)
|
setCharacters(characters)
|
||||||
|
|
@ -101,6 +103,7 @@ const PartyRoute: React.FC<PartyProps> = ({ match }) => {
|
||||||
summons={summons}
|
summons={summons}
|
||||||
editable={editable}
|
editable={editable}
|
||||||
exists={found}
|
exists={found}
|
||||||
|
extra={extra}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue