| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- import React, { useCallback, useEffect, useMemo, useState, type CSSProperties } from "react";
- import data from "../../assets/data/2.0/data.json";
- import styles from "./MachineSelector.module.css";
- import Icon from "../icon";
- import { Autocomplete, Box, Popper, TextField, InputAdornment } from "@mui/material";
- import SelectMenu from "./SelectFactorioMenu";
- import Tooltip from "../Tooltip";
- import type { Recipe } from "../../../scripts/factorio-dump/helpers/recipes.helper";
- import type { Machine } from "../../../scripts/factorio-dump/process-data.models";
- import { useQualityScroller } from "../../hooks/useQualityScroller";
- const recipeList = data.recipeGroup.flatMap((r) => r.subGroup.flatMap((s) => (s.children ?? []) as Recipe[]));
- const machines = data.machines as Machine[];
- type MachineSelectorProps = {
- className?: string;
- style?: CSSProperties;
- onChange?: (selection: { machine: Machine | null; qualityLevel: number; recipe: Recipe | null }) => void;
- };
- function MachineSelector({ style, className, onChange }: MachineSelectorProps) {
- const [machine, setMachine] = React.useState<Machine | null>(null);
- const [recipe, setRecipe] = useState<null | Recipe>(null);
- const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
- const [showMenuRecipe, setShowMenuRecipe] = useState(false);
- const { scrollRef, activeQuality, setQualityName } = useQualityScroller("normal");
- useEffect(() => {
- if (onChange) {
- onChange({ machine, qualityLevel: activeQuality.level, recipe });
- }
- }, [machine, activeQuality, recipe]);
- const machineOptions = useMemo(() => {
- if (recipe == null) return [];
- const categories = recipe.categories ?? [];
- return machines.filter((m) => m.crafting_categories.some((c) => categories.includes(c)));
- }, [recipe]);
- useEffect(() => {
- if (machineOptions.length === 1) setMachine(machineOptions[0]);
- if (machine && !machineOptions.includes(machine)) setMachine(null);
- }, [machineOptions, machine]);
- const onOpenRecipe = useCallback(
- (event: React.MouseEvent<HTMLElement>) => {
- setAnchorEl(anchorEl ? null : event.currentTarget);
- setShowMenuRecipe(!showMenuRecipe);
- },
- [anchorEl, showMenuRecipe],
- );
- const onSelectRecipe = useCallback((name: string) => {
- setRecipe(recipeList.find((r) => r.name === name) ?? null);
- setAnchorEl(null);
- setShowMenuRecipe(false);
- }, []);
- return (
- <div className={className} style={{ ...style, display: "flex", gap: "12px", alignItems: "center" }}>
- {/* Recipe Trigger */}
- <div onClick={onOpenRecipe} className={styles.machineSpecRecipe} style={{ cursor: "pointer" }}>
- {recipe == null ? (
- <div
- style={{
- padding: "8px 12px",
- border: "1px dashed #646464",
- borderRadius: "4px",
- }}
- >
- Select recipe
- </div>
- ) : (
- <Tooltip title={recipe.name}>
- <div>
- <Icon iconName={recipe.icon ?? ""} size={40} />
- </div>
- </Tooltip>
- )}
- </div>
- <Autocomplete
- ref={scrollRef}
- value={machine}
- onChange={(_: any, newValue: Machine | null) => {
- setMachine(newValue);
- }}
- disablePortal
- options={machineOptions}
- sx={{ width: 300 }}
- getOptionLabel={(option) => option.name}
- disabled={!recipe}
- renderInput={(params: any) => (
- <TextField
- {...params}
- label={recipe ? "Machine (Alt+Scroll)" : "Select recipe first"}
- InputProps={{
- ...params.InputProps,
- startAdornment: machine ? (
- <InputAdornment position="start">
- <div title="Alt + Scroll to change quality" style={{ cursor: "ns-resize", display: "flex" }}>
- <Icon iconName={machine.icon ?? ""} size={28} qualityLevel={activeQuality.level} />
- </div>
- </InputAdornment>
- ) : null,
- }}
- />
- )}
- renderOption={(props: any, option: Machine) => {
- const { key, ...optionProps } = props;
- return (
- <Box key={key} component="li" {...optionProps} sx={{ gap: 2 }}>
- <Icon iconName={option.icon ?? ""} size={30} qualityLevel={activeQuality.level} />
- {option.name}
- </Box>
- );
- }}
- />
- <Popper open={showMenuRecipe} anchorEl={anchorEl} placement="bottom-start" style={{ zIndex: 1300 }}>
- <SelectMenu
- title="Select recipe"
- categories={data.recipeGroup}
- onSelectItem={onSelectRecipe}
- onClose={() => setShowMenuRecipe(false)}
- />
- </Popper>
- </div>
- );
- }
- export default MachineSelector;
|