mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-12 22:26:12 -05:00
react select weapon select
This commit is contained in:
3164
frontend-react/package-lock.json
generated
3164
frontend-react/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 && (
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
92
frontend-react/src/components/elements/Select.tsx
Normal file
92
frontend-react/src/components/elements/Select.tsx
Normal 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
|
||||
@@ -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} />
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -6,10 +6,8 @@ import {
|
||||
AccordionHeader,
|
||||
AccordionIcon,
|
||||
AccordionPanel,
|
||||
SimpleGrid,
|
||||
Box,
|
||||
Grid,
|
||||
Icon,
|
||||
Flex,
|
||||
Heading,
|
||||
} from "@chakra-ui/core"
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ const MyThemeContext = React.createContext<Theme>({
|
||||
themeColorWithShade: "pink.200",
|
||||
grayWithShade: "gray.300",
|
||||
themeColorHex: "#D53F8C",
|
||||
themeColorHexLighter: "#FBB6CE",
|
||||
themeColor: "pink",
|
||||
})
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user