react select weapon select

This commit is contained in:
Sendou
2020-02-01 15:50:18 +02:00
parent 8f3b778fe6
commit 1626a824ac
12 changed files with 2112 additions and 1386 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -11,13 +11,14 @@
"@sendou/react-sketch": "^0.5.2",
"@testing-library/jest-dom": "^5.0.2",
"@testing-library/react": "^9.4.0",
"@testing-library/user-event": "^8.0.7",
"@types/jest": "^25.1.0",
"@types/node": "^13.5.1",
"@testing-library/user-event": "^8.1.0",
"@types/jest": "^25.1.1",
"@types/node": "^13.5.3",
"@types/reach__router": "^1.2.6",
"@types/react": "^16.9.19",
"@types/react-dom": "^16.9.5",
"@types/react-infinite-scroller": "^1.2.1",
"@types/react-select": "^3.0.10",
"apollo-boost": "^0.4.7",
"emotion-theming": "^10.0.27",
"graphql": "^14.6.0",
@@ -27,9 +28,10 @@
"react-draggable": "^4.2.0",
"react-helmet-async": "^1.0.4",
"react-hotkeys-hook": "^1.5.4",
"react-icons": "^3.8.0",
"react-icons": "^3.9.0",
"react-infinite-scroller": "^1.2.4",
"react-scripts": "3.3.0",
"react-scripts": "^3.3.1",
"react-select": "^3.0.8",
"typescript": "^3.7.5"
},
"scripts": {
@@ -54,6 +56,6 @@
]
},
"devDependencies": {
"@types/webpack-env": "^1.15.0"
"@types/webpack-env": "^1.15.1"
}
}

View File

@@ -79,13 +79,14 @@ const BuildsPage: React.FC<RouteComponentProps> = () => {
/>
<Box mt="1em">
<WeaponSelector
weapon={weapon}
setWeapon={(weapon: Weapon | null) => setWeapon(weapon)}
dropdownMode={isSmall}
setValue={(weapon: string) => setWeapon(weapon as Weapon)}
autoFocus
/>
</Box>
{weapon && (
<AbilitySelector abilities={abilities} setAbilities={setAbilities} />
<Box mt="1em">
<AbilitySelector abilities={abilities} setAbilities={setAbilities} />
</Box>
)}
{loading && <Loading />}
{buildsFilterByAbilities.length > 0 && data && (

View File

@@ -1,101 +1,41 @@
import React from "react"
import { Weapon } from "../../types"
import { Input, Flex, PseudoBox, Select } from "@chakra-ui/core"
import { useState } from "react"
import { weapons } from "../../utils/lists"
import { Flex, Box } from "@chakra-ui/core"
import { weaponSelectOptions } from "../../utils/lists"
import WeaponImage from "./WeaponImage"
import { useContext } from "react"
import MyThemeContext from "../../themeContext"
import FieldsetWithLegend from "./FieldsetWithLegend"
import { components } from "react-select"
import Select from "../elements/Select"
interface WeaponSelectorProps {
weapon: Weapon | null
setWeapon: (weapon: Weapon | null) => void
dropdownMode?: boolean
setValue: (value: string) => void
autoFocus?: boolean
}
const singleOption = (props: any) => (
<components.Option {...props}>
<Flex alignItems="center" color={props.isFocused ? "black" : undefined}>
<Box mr="0.5em">
<WeaponImage size="SMALL" englishName={props.label} />
</Box>
{props.label}
</Flex>
</components.Option>
)
const WeaponSelector: React.FC<WeaponSelectorProps> = ({
weapon,
setWeapon,
dropdownMode = false,
setValue,
autoFocus = false,
}) => {
const { darkerBgColor } = useContext(MyThemeContext)
const [input, setInput] = useState("")
const filterWeaponArray = (weapon: Weapon) =>
input === "" || weapon.toLowerCase().indexOf(input.toLowerCase()) !== -1
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) =>
setInput(event.target.value)
if (dropdownMode)
return (
<Select
placeholder="Filter weapons"
value={weapon ?? ""}
onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
setWeapon(event.target.value as Weapon)
}
mb="1em"
>
{weapons.map(weapon => (
<option
key={weapon}
value={weapon}
style={{ background: darkerBgColor }}
>
{weapon}
</option>
))}
</Select>
)
return (
<>
<Input
placeholder="Filter weapons"
value={input}
onChange={handleInputChange}
w="50%"
ml="auto"
mr="auto"
onKeyDown={(event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key !== "Enter") return
const oneWeaponArray = weapons.filter(filterWeaponArray)
if (oneWeaponArray.length !== 1) return
setWeapon(oneWeaponArray[0])
}}
autoFocus
/>
<FieldsetWithLegend
title="Click a weapon to select it"
titleFontSize="md"
dividerMode
centerTitle
fullWidth
mt="1em"
>
<Flex flexWrap="wrap" justifyContent="center">
{weapons.filter(filterWeaponArray).map(weapon => (
<PseudoBox
key={weapon}
px="2px"
cursor="pointer"
onClick={() => setWeapon(weapon)}
userSelect="none"
transition="0.5s"
_hover={{
bg: "rgba(128, 128, 128, 0.3)",
borderRadius: "50%",
transition: "background-color .5s",
}}
>
<WeaponImage englishName={weapon} size="SMALL" />
</PseudoBox>
))}
</Flex>
</FieldsetWithLegend>
</>
<Select
options={weaponSelectOptions}
setValue={setValue}
placeholder="Select a weapon"
components={{
IndicatorSeparator: () => null,
Option: singleOption,
}}
autoFocus={autoFocus}
/>
)
}

View File

@@ -0,0 +1,92 @@
import React, { useContext } from "react"
import ReactSelect, {
components,
OptionsType,
GroupedOptionsType,
ValueType,
OptionTypeBase,
createFilter,
} from "react-select"
import MyThemeContext from "../../themeContext"
import { SelectComponents } from "react-select/src/components"
interface SelectProps {
options:
| OptionsType<{
label: string
value: string
}>
| GroupedOptionsType<{
label: string
value: string
}>
placeholder: string
setValue: (value: string) => void
autoFocus?: boolean
components?: Partial<
SelectComponents<{
label: string
value: string
}>
>
}
const Select: React.FC<SelectProps> = ({
options,
components,
placeholder,
setValue,
autoFocus = false,
}) => {
const {
colorMode,
darkerBgColor,
themeColorHex,
themeColorHexLighter,
} = useContext(MyThemeContext)
const handleChange = (selectedOption: any) => {
setValue(selectedOption.value)
}
return (
<ReactSelect
className="basic-single"
classNamePrefix="select"
onChange={handleChange}
placeholder={placeholder}
isSearchable
options={options}
components={{
IndicatorSeparator: () => null,
...components,
}}
theme={theme => ({
...theme,
borderRadius: 5,
colors: {
...theme.colors,
primary25: `${themeColorHexLighter}`,
primary: `${themeColorHex}`,
neutral0: darkerBgColor,
},
})}
autoFocus={autoFocus}
styles={{
singleValue: base => ({
...base,
padding: 5,
borderRadius: 5,
color: colorMode === "light" ? "black" : "white",
display: "flex",
}),
input: base => ({
...base,
color: colorMode === "light" ? "black" : "white",
}),
}}
/>
)
}
export default Select

View File

@@ -1,7 +1,7 @@
import React from "react"
import React, { useState } from "react"
import { useQuery } from "@apollo/react-hooks"
import { USER } from "../../graphql/queries/user"
import { UserData, FreeAgentPostsData } from "../../types"
import { UserData, FreeAgentPostsData, Weapon } from "../../types"
import { FREE_AGENT_POSTS } from "../../graphql/queries/freeAgentPosts"
import Loading from "../common/Loading"
import Error from "../common/Error"
@@ -9,8 +9,10 @@ import { RouteComponentProps } from "@reach/router"
import PostsAccordion from "./PostsAccordion"
import PageHeader from "../common/PageHeader"
import { Helmet } from "react-helmet-async"
import WeaponSelector from "../common/WeaponSelector"
const FreeAgentsPage: React.FC<RouteComponentProps> = () => {
const [weapon, setWeapon] = useState<Weapon | null>(null)
const { data, error, loading } = useQuery<FreeAgentPostsData>(
FREE_AGENT_POSTS
)
@@ -32,6 +34,10 @@ const FreeAgentsPage: React.FC<RouteComponentProps> = () => {
<title>Free Agents | sendou.ink</title>
</Helmet>
<PageHeader title="Free Agents" />
<WeaponSelector
setValue={(weapon: string) => setWeapon(weapon as Weapon)}
autoFocus
/>
<PostsAccordion posts={faPosts} />
</>
)

View File

@@ -6,10 +6,8 @@ import {
AccordionHeader,
AccordionIcon,
AccordionPanel,
SimpleGrid,
Box,
Grid,
Icon,
Flex,
Heading,
} from "@chakra-ui/core"

View File

@@ -8,7 +8,6 @@ import useLocalStorage from "@rehooks/local-storage"
import { ThemeColor } from "../../types"
import { MyThemeProvider } from "../../themeContext"
import { Theme } from "../../types"
import { Helmet } from "react-helmet-async"
import Footer from "./Footer"
import { useEffect } from "react"
@@ -27,12 +26,14 @@ const App: React.FC = () => {
light: {
colorMode: "light",
bgColor: "#eff0f3",
darkerBgColor: "#D6D7DA",
//darkerBgColor: "#D6D7DA", FFF0F5
darkerBgColor: "#FFFAFA",
textColor: "#0d0d0d",
borderStyle: "1px solid rgba(0, 0, 0, .2)",
themeColorWithShade: `${themeColor}.500`,
grayWithShade: "gray.600",
themeColorHex: chakraTheme.colors[themeColor]["500"],
themeColorHexLighter: chakraTheme.colors[themeColor]["200"],
themeColor,
} as Theme,
dark: {
@@ -44,6 +45,7 @@ const App: React.FC = () => {
themeColorWithShade: `${themeColor}.200`,
grayWithShade: "gray.300",
themeColorHex: chakraTheme.colors[themeColor]["500"],
themeColorHexLighter: chakraTheme.colors[themeColor]["200"],
themeColor,
} as Theme,
}

View File

@@ -1,4 +1,4 @@
import React, { StrictMode } from "react"
import React from "react"
import ReactDOM from "react-dom"
import App from "./components/root/App"
import {
@@ -23,18 +23,16 @@ const client = new ApolloClient({
const customTheme = { ...theme, icons: { ...theme.icons, ...customIcons } }
ReactDOM.render(
<StrictMode>
<HelmetProvider>
<ApolloProvider client={client}>
<ThemeProvider theme={customTheme}>
<ColorModeProvider>
<CSSReset />
<App />
</ColorModeProvider>
</ThemeProvider>
</ApolloProvider>
</HelmetProvider>
</StrictMode>,
<HelmetProvider>
<ApolloProvider client={client}>
<ThemeProvider theme={customTheme}>
<ColorModeProvider>
<CSSReset />
<App />
</ColorModeProvider>
</ThemeProvider>
</ApolloProvider>
</HelmetProvider>,
document.getElementById("root")
)

View File

@@ -10,6 +10,7 @@ const MyThemeContext = React.createContext<Theme>({
themeColorWithShade: "pink.200",
grayWithShade: "gray.300",
themeColorHex: "#D53F8C",
themeColorHexLighter: "#FBB6CE",
themeColor: "pink",
})

View File

@@ -38,15 +38,14 @@ export type ShoesGear = ElementType<typeof shoesGearEnglish>
export interface Theme {
colorMode: "dark" | "light"
bgColor: "#eff0f3" | "#232946"
darkerBgColor: "#0A102D" | "#D6D7DA"
textColor: "#fffffe" | "#0d0d0d"
borderStyle:
| "1px solid rgba(0, 0, 0, .2)"
| "1px solid rgba(255, 255, 255, .2)"
grayWithShade: "gray.300" | "gray.600"
bgColor: string
darkerBgColor: string
textColor: string
borderStyle: string
grayWithShade: string
themeColorWithShade: string
themeColorHex: string
themeColorHexLighter: string
themeColor: ThemeColor
}

View File

@@ -1,3 +1,7 @@
import { Weapon } from "../types"
import { wpnSmall } from "../assets/imageImports"
import english_internal from "./english_internal.json"
export const headOnlyAbilities = ["CB", "LDE", "OG", "T"] as const
export const clothingOnlyAbilities = ["H", "NS", "TI", "RP", "AD"] as const
export const shoesOnlyAbilities = ["DR", "SJ", "OS"] as const
@@ -1104,6 +1108,47 @@ export const brellas = [
"Kensa Undercover Brella",
] as const
const weaponMap = (weapon: Weapon) => ({ value: weapon, label: weapon })
export const weaponSelectOptions = [
{
label: "Shooters",
options: [...shooters, ...semiauto].map(weaponMap),
},
{
label: "Blasters",
options: blasters.map(weaponMap),
},
{
label: "Rollers",
options: rollers.map(weaponMap),
},
{
label: "Brushes",
options: brushes.map(weaponMap),
},
{
label: "Chargers",
options: chargers.map(weaponMap),
},
{
label: "Sloshers",
options: sloshers.map(weaponMap),
},
{
label: "Splatlings",
options: splatlings.map(weaponMap),
},
{
label: "Dualies",
options: dualies.map(weaponMap),
},
{
label: "Brellas",
options: brellas.map(weaponMap),
},
]
export const weaponsByCategory = {
Shooters: shooters,
"Semi-automatic Shooters": semiauto,