Maplist generator: Custom Order (#346)

* new select for multiple modes

* add custom order generation

* remove log + better select position

* little optimization

* import fix

* add icons for modes in select

* pretty

* rename variable

* import replace

* arrow function

* render only if selected

* Update pages/maps.tsx

Co-authored-by: Kalle <38327916+Sendouc@users.noreply.github.com>

Co-authored-by: Kalle <38327916+Sendouc@users.noreply.github.com>
This commit is contained in:
Igor 2021-04-02 15:46:08 +03:00 committed by GitHub
parent fdeb541e64
commit 10951fe39e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 227 additions and 10 deletions

View File

@ -0,0 +1,177 @@
import { ChevronDownIcon } from "@chakra-ui/icons";
import { useMyTheme } from "hooks/common";
import { useState } from "react";
import ReactSelect, {
components,
GroupedOptionsType,
OptionsType,
OptionTypeBase,
ValueType,
} from "react-select";
import { SelectComponents } from "react-select/src/components";
import { Box, Flex } from "@chakra-ui/react";
import ModeImage from "./ModeImage";
interface SelectProps {
options?:
| OptionsType<{
label: string;
value: any;
data?: any;
}>
| GroupedOptionsType<{
label: string;
value: string;
data?: any;
}>;
width?: string;
value?: ValueType<OptionTypeBase, boolean>;
setValue: (value: any) => void;
components?: Partial<SelectComponents<OptionTypeBase, boolean>>;
isDisabled?: boolean;
menuIsOpen?: boolean;
hideMenuBeforeTyping?: boolean;
}
const DropdownIndicator = (props: any) => {
const { textColor } = useMyTheme();
return (
<components.DropdownIndicator {...props}>
<ChevronDownIcon fontSize="1.3rem" color={textColor} />
</components.DropdownIndicator>
);
};
const Option = (props: any) => {
return (
<components.Option {...props}>
<Flex alignItems="center">
<Box mr="0.5em">
<ModeImage size={24} mode={props.value} />
</Box>
{props.label}
</Flex>
</components.Option>
);
};
const MultipleModeSelector: React.FC<SelectProps> = ({
options,
components,
setValue,
isDisabled = false,
menuIsOpen = false,
hideMenuBeforeTyping,
}) => {
const {
borderColor,
themeColorHex,
bgColor,
themeColorOpaque,
textColor,
} = useMyTheme();
const [inputValue, setInputValue] = useState("");
const [selectedModes, setSelectedModes] = useState([]);
const handleChange = (selectedOption: any) => {
if (!selectedOption) {
setSelectedModes([]);
return;
}
const newSelectedOption = selectedOption.map(
(opt: { label: string; data: string }) => ({
label: opt.label,
data: opt.data,
value: Math.random(),
})
);
setSelectedModes(newSelectedOption);
setValue(newSelectedOption);
};
const menuIsOpenCheck = () => {
if (menuIsOpen) return true;
if (hideMenuBeforeTyping) {
return !!(inputValue.length >= 3);
}
return undefined;
};
return (
<ReactSelect
className="basic-single"
classNamePrefix="select"
value={selectedModes}
inputValue={inputValue}
onInputChange={(newValue) => setInputValue(newValue)}
menuIsOpen={menuIsOpenCheck()}
onChange={handleChange}
placeholder={null}
isMulti={true}
isDisabled={isDisabled}
isClearable={true}
options={options}
components={
hideMenuBeforeTyping
? {
IndicatorSeparator: () => null,
DropdownIndicator: () => null,
Option,
...components,
}
: {
IndicatorSeparator: () => null,
DropdownIndicator,
Option,
...components,
}
}
theme={(theme) => ({
...theme,
borderRadius: 5,
colors: {
...theme.colors,
primary25: `${themeColorHex}`,
primary: themeColorHex,
neutral0: bgColor,
neutral5: bgColor,
},
})}
styles={{
singleValue: (base) => ({
...base,
padding: 5,
borderRadius: 5,
color: textColor,
display: "flex",
}),
input: (base) => ({
...base,
color: textColor,
}),
multiValue: (base) => ({
...base,
background: themeColorHex,
color: "black",
}),
option: (styles, { isFocused }) => {
return {
...styles,
backgroundColor: isFocused ? themeColorOpaque : undefined,
color: textColor,
};
},
menu: (styles) => ({ ...styles, zIndex: 999 }),
control: (base) => ({
...base,
borderColor,
minHeight: "2.5rem",
background: "hsla(0, 0%, 0%, 0)",
}),
}}
/>
);
};
export default MultipleModeSelector;

View File

@ -25,6 +25,7 @@ import { RankedMode } from "@prisma/client";
import ModeImage from "components/common/ModeImage";
import SubText from "components/common/SubText";
import HeaderBanner from "components/layout/HeaderBanner";
import MultipleModeSelector from "components/common/MultipleModeSelector";
import { useRouter } from "next/router";
import { ChangeEvent, Fragment, useEffect, useState } from "react";
import { FiCheck, FiFilter, FiRotateCw } from "react-icons/fi";
@ -40,9 +41,12 @@ const MapsGeneratorPage = () => {
Record<string, RankedMode[]>
>(getInitialStages());
const [generationMode, setGenerationMode] = useState<
"EQUAL" | "SZ_EVERY_OTHER"
"EQUAL" | "SZ_EVERY_OTHER" | "CUSTOM_ORDER"
>("EQUAL");
const [maplist, setMaplist] = useState("");
const [modes, setModes] = useState<
{ label: string; value: number; data?: string }[]
>([]);
const [count, setCount] = useState(9);
const [editing, setEditing] = useState(false);
const [copied, setCopied] = useState<null | "URL" | "LIST">(null);
@ -127,15 +131,22 @@ const MapsGeneratorPage = () => {
const modesFromGenerationMode =
generationMode === "SZ_EVERY_OTHER"
? ["TC", "RM", "CB"]
: ["SZ", "TC", "RM", "CB"];
: generationMode === "EQUAL"
? ["SZ", "TC", "RM", "CB"]
: transformModesToStringArray();
const modes = (shuffleArray(
modesFromGenerationMode
) as RankedMode[]).filter((mode) => modeStages[mode].length > 0);
const filteredModes = (modesFromGenerationMode as RankedMode[]).filter(
(mode) => modeStages[mode].length > 0
);
const modes =
generationMode === "CUSTOM_ORDER"
? filteredModes
: shuffleArray(filteredModes);
if (modes.length === 0) {
return "I can't generate a maplist without any maps in it you know.";
return generationMode === "CUSTOM_ORDER"
? "I can't generate a maplist without any modes you know."
: "I can't generate a maplist without any maps in it you know.";
}
const stagesAlreadyPicked = new Set<string>();
const isSZFirst = false;
@ -149,8 +160,8 @@ const MapsGeneratorPage = () => {
generationMode !== "SZ_EVERY_OTHER" ||
i % 2 === Number(isSZFirst)
) {
modes.push(modes.shift() as RankedMode);
modeOfRound = modes[0];
modes.push(modes.shift() as RankedMode);
}
const stageArray = modeStages[modeOfRound];
@ -336,7 +347,9 @@ const MapsGeneratorPage = () => {
</Stack>
<RadioGroup
onChange={(value) =>
setGenerationMode(value as "EQUAL" | "SZ_EVERY_OTHER")
setGenerationMode(
value as "EQUAL" | "SZ_EVERY_OTHER" | "CUSTOM_ORDER"
)
}
value={generationMode}
>
@ -347,9 +360,23 @@ const MapsGeneratorPage = () => {
<Radio value="SZ_EVERY_OTHER">
<Trans>SZ every other</Trans>
</Radio>
<Radio value="CUSTOM_ORDER">
<Trans>Custom order</Trans>
</Radio>
</Stack>
</RadioGroup>
<FormLabel htmlFor="count" fontSize="sm">
{generationMode === "CUSTOM_ORDER" && <MultipleModeSelector
options={[
{ label: "Splat Zones", value: "SZ", data: "SZ" },
{ label: "Tower Control", value: "TC", data: "TC" },
{ label: "Rainmaker", value: "RM", data: "RM" },
{ label: "Clam Blitz", value: "CB", data: "CB" },
]}
isDisabled={generationMode !== "CUSTOM_ORDER"}
setValue={getModeValues}
width={"90%"}
/>}
<FormLabel htmlFor="count" fontSize="sm" mt={4}>
<Trans>Amount of maps to generate</Trans>
</FormLabel>
<NumberInput
@ -390,6 +417,19 @@ const MapsGeneratorPage = () => {
)}
</>
);
function getModeValues(
value: { label: string; value: number; data?: string }[]
) {
setModes(value);
}
function transformModesToStringArray() {
return modes.map(mode => {
if (mode.data) return mode.data;
else return "";
});
}
};
MapsGeneratorPage.header = (