components#PokemonType TypeScript Examples

The following examples show how to use components#PokemonType. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: PokemonSlot.tsx    From nuzlocke with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
function PokemonSlot({ pokemon }: PokemonSlotProps): JSX.Element {
  return (
    <div className={styles.selector} style={{ backgroundColor: `${TYPE_COLOR[pokemon?.type]}50` }}>
      <div className={styles.info}>
        <div className={styles.image}>
          <PkmImage name={pokemon?.text} />
        </div>
        <span>{pokemon?.text}</span>
      </div>
      <PokemonType pokemon={pokemon} />
    </div>
  );
}
Example #2
Source File: DetailSelector.tsx    From nuzlocke with BSD 3-Clause "New" or "Revised" License 4 votes vote down vote up
function DetailSelector({
  children,
  details,
  handleDetail,
  limitGen,
}: DetailSelectorProps): JSX.Element {
  const { t } = useTranslation('common');
  const [open, setOpen] = useState(false);
  const itemSize = useRemtoPx();
  const values = useFilter();
  const filteredEncounters = details?.filter((p) => {
    const foundPokemon = POKEMAP.get(p.id);
    return (
      foundPokemon?.text.toUpperCase().includes(values.search) &&
      (limitGen ? foundPokemon.generation <= limitGen : true) &&
      (values.gens.length > 0 ? values.gens.includes(foundPokemon.generation) : true) &&
      (values.types.length > 0
        ? values.types.includes(foundPokemon.type) || values.types.includes(foundPokemon?.dualtype)
        : true)
    );
  });

  const handleClick = (detail: PokemonDetail) => {
    handleDetail(detail);
    values.reset();
    setOpen(false);
  };

  const handleClose = () => {
    values.reset();
    setOpen(false);
  };

  const renderRow: React.FC<RowProps> = ({ index, style }) => {
    const detail = filteredEncounters[index];
    const foundPokemon = POKEMAP.get(detail.id);
    return (
      <div className={styles.rowContainer} style={style}>
        <div
          className={styles.row}
          data-testid={`poke-${foundPokemon.text}`}
          onClick={() => handleClick(detail)}
          role="presentation"
          style={{ backgroundColor: `${TYPE_COLOR[foundPokemon.type]}50` }}
        >
          <div className={styles.details}>
            <div className={styles.image}>
              <PkmImage name={foundPokemon?.text} shiny={detail?.shiny} />
            </div>
            <b>{foundPokemon.text}</b>
          </div>
          <PokemonType pokemon={foundPokemon} />
        </div>
      </div>
    );
  };
  return (
    <Modal
      closeOnDimmerClick
      open={open}
      trigger={
        <div onClick={() => setOpen(true)} role="presentation">
          {children}
        </div>
      }
    >
      <Modal.Content className={styles.content} scrolling>
        <Filter hideGen={!!limitGen} values={values} />
        {/* @ts-ignore */}
        <FixedSizeList
          height={400}
          itemCount={filteredEncounters?.length ?? 0}
          itemSize={itemSize}
          width="100%"
        >
          {renderRow}
        </FixedSizeList>
      </Modal.Content>
      <Modal.Actions>
        <Button onClick={handleClose}>{t('cancel', { ns: 'common' })}</Button>
      </Modal.Actions>
    </Modal>
  );
}
Example #3
Source File: EncounterSelector.tsx    From nuzlocke with BSD 3-Clause "New" or "Revised" License 4 votes vote down vote up
function EncounterSelector({
  children,
  encounters,
  handleEncounter,
  limitGen,
}: EncounterSelectorProps): JSX.Element {
  const { t } = useTranslation('common');
  const [open, setOpen] = useState(false);
  const itemSize = useRemtoPx();
  const values = useFilter();
  const filteredEncounters = encounters.filter((p) => {
    const foundPokemon = POKEMAP.get(p.pokemon);
    return (
      foundPokemon?.text.toUpperCase().includes(values.search) &&
      (limitGen ? foundPokemon.generation <= limitGen : true) &&
      (values.gens.length > 0 ? values.gens.includes(foundPokemon.generation) : true) &&
      (values.types.length > 0
        ? values.types.includes(foundPokemon.type) || values.types.includes(foundPokemon?.dualtype)
        : true)
    );
  });

  const handleClick = (encounter: TEncounter) => {
    handleEncounter(encounter);
    values.reset();
    setOpen(false);
  };

  const handleClose = () => {
    values.reset();
    setOpen(false);
  };

  const renderRow: React.FC<RowProps> = ({ index, style }) => {
    const encounter = filteredEncounters[index];
    const foundPokemon = POKEMAP.get(encounter.pokemon);
    return (
      <div className={styles.rowContainer} style={style}>
        <div
          className={styles.row}
          data-testid={`poke-${foundPokemon.text}`}
          onClick={() => handleClick(encounter)}
          role="presentation"
          style={{ backgroundColor: `${TYPE_COLOR[foundPokemon.type]}50` }}
        >
          <div className={styles.details}>
            <div className={styles.image}>
              <PkmImage name={foundPokemon?.text} />
            </div>
            <b>{encounter.nickname || foundPokemon.text}</b>
          </div>
          <PokemonType pokemon={foundPokemon} />
        </div>
      </div>
    );
  };
  return (
    <Modal
      closeOnDimmerClick
      open={open}
      trigger={
        <div onClick={() => setOpen(true)} role="presentation">
          {children}
        </div>
      }
    >
      <Modal.Content className={styles.content} scrolling>
        <Filter hideGen={!!limitGen} values={values} />
        {/* @ts-ignore */}
        <FixedSizeList
          height={400}
          itemCount={filteredEncounters.length}
          itemSize={itemSize}
          width="100%"
        >
          {renderRow}
        </FixedSizeList>
      </Modal.Content>
      <Modal.Actions>
        <Button onClick={handleClose}>{t('cancel', { ns: 'common' })}</Button>
      </Modal.Actions>
    </Modal>
  );
}
Example #4
Source File: PokemonSelector.tsx    From nuzlocke with BSD 3-Clause "New" or "Revised" License 4 votes vote down vote up
function PokemonSelector({
  children,
  dupes,
  filter = false,
  handlePokemon,
  limitGen,
  suggestions,
  tooltip = false,
}: PokemonSelectorProps): JSX.Element {
  const { t } = useTranslation('common');
  const [open, setOpen] = useState(false);
  const values = useFilter();
  const itemSize = useRemtoPx();
  const toggleShowAllTooltip = useStore(useCallback((state) => state.toggleShowAllTooltip, []));

  const filteredPokemon = useMemo(() => {
    let filtered = POKEMON.filter(
      (p) =>
        (typeof filter === 'boolean' ? true : filter.includes(p.value)) &&
        p.text.toUpperCase().includes(values.search) &&
        (limitGen ? p.generation <= limitGen : true) &&
        (values.gens.length > 0 ? values.gens.includes(p.generation) : true) &&
        (values.types.length > 0
          ? values.types.includes(p.type) || values.types.includes(p?.dualtype)
          : true)
    );
    if (suggestions) {
      filtered = filtered.sort(
        (a, b) =>
          Number(suggestions.includes(b.type) || suggestions.includes(b.dualtype)) -
          Number(suggestions.includes(a.type) || suggestions.includes(a.dualtype))
      );
    }
    return filtered;
  }, [filter, suggestions, limitGen, values]);

  const handleClick = (pokemonId: number) => {
    handlePokemon(pokemonId);
    values.reset();
    setOpen(false);
  };

  const onKeyPress = (e: React.KeyboardEvent<HTMLDivElement>) => {
    if (e.key === 'Enter' || e.key === ' ') setOpen(true);
  };

  const handleClose = () => {
    values.reset();
    setOpen(false);
  };

  const renderRow: React.FC<RowProps> = ({ index, style }) => {
    const pokemon = filteredPokemon[index];
    const dupe = pokemon?.evolve
      ? pokemon.evolve.some((evoId) => dupes?.includes(evoId))
      : dupes?.includes(pokemon?.value);
    return (
      <div className={styles.rowContainer} style={style}>
        <div
          className={`${styles.row} ${dupe ? styles.dupe : ''}`}
          data-testid={`poke-${pokemon.text}`}
          onClick={() => handleClick(pokemon.value)}
          role="presentation"
          style={{ backgroundColor: `${TYPE_COLOR[pokemon.type]}50` }}
        >
          {suggestions &&
            !dupe &&
            (suggestions.includes(pokemon.type) || suggestions.includes(pokemon.dualtype)) && (
              <div className={styles.suggestion}>
                <Icon name="star" />
                <span>{t('suggested')}</span>
              </div>
            )}
          {dupe && (
            <span className={styles.suggestion} title={t('dupe_description')}>
              DUPE
            </span>
          )}
          <div className={styles.details}>
            <div className={styles.image}>
              <PkmImage name={pokemon?.text} />
            </div>
            <b>{pokemon.text}</b>
          </div>
          <PokemonType pokemon={pokemon} />
        </div>
      </div>
    );
  };

  return (
    <Modal
      closeOnDimmerClick
      open={open}
      trigger={
        <div
          data-testid="pokemon-selector"
          onClick={() => setOpen(true)}
          onKeyPress={onKeyPress}
          role="button"
          tabIndex={0}
        >
          {children}
        </div>
      }
    >
      <Modal.Content className={styles.content} scrolling>
        <Filter hideGen={!!limitGen} values={values} />
        {tooltip && (
          <aside className={styles.callout}>
            <Icon name="lightbulb outline" />
            <p>{t('show_all_tooltip')}</p>
            <Button data-testid="remove-tooltip" type="button" icon onClick={toggleShowAllTooltip}>
              <Icon className={styles.icon} name="close" />
            </Button>
          </aside>
        )}
        {/* @ts-ignore */}
        <FixedSizeList
          height={400}
          itemCount={filteredPokemon.length}
          itemSize={itemSize}
          width="100%"
        >
          {renderRow}
        </FixedSizeList>
      </Modal.Content>
      <Modal.Actions>
        <Button onClick={handleClose}>{t('cancel')}</Button>
      </Modal.Actions>
    </Modal>
  );
}
Example #5
Source File: BadgeDetail.tsx    From nuzlocke with BSD 3-Clause "New" or "Revised" License 4 votes vote down vote up
function BadgeDetail({ selectedDetail }: BadgeDetailProps): JSX.Element {
  const { t } = useTranslation('badges');
  const [view, setView] = useState(0);
  const selectedGame = useStore(useCallback((state) => state.selectedGame, []));
  const darkMode = useStore(useCallback((state) => state.darkMode, []));
  const isSplit = !PHYS_SPEC_SPLIT.includes(selectedGame?.value);
  const colors = darkMode ? D_STAT_COLOR : STAT_COLOR;

  const getContent = () => {
    if (!!selectedDetail?.rematch && view === 1) {
      return selectedDetail.rematch;
    }
    return selectedDetail?.content;
  };
  return (
    <>
      <div className={styles.title}>{selectedDetail?.name}</div>
      {selectedDetail?.type === 'REMATCH' && (
        <div className={styles.rematch}>
          <Radio
            checked={view === 0}
            label={t('original')}
            onChange={(e: React.FormEvent<HTMLInputElement>, data: CheckboxProps) =>
              setView(data.value as number)
            }
            value={0}
          />
          <Radio
            checked={view === 1}
            label={t('rematch')}
            onChange={(e: React.FormEvent<HTMLInputElement>, data: CheckboxProps) =>
              setView(data.value as number)
            }
            value={1}
          />
        </div>
      )}
      <div className={styles.gymPokemon}>
        {getContent()?.map((pokemon, ind) => {
          const poke = POKEMAP.get(pokemon.id);
          let stats = undefined;
          try {
            stats = new Pokemon(GAME_GENERATION[selectedGame?.value], getSmogonName(poke.text), {
              level: pokemon?.level,
            });
          } catch {
            // do nothing
          }

          return (
            <div
              className={styles.pokemon}
              key={`pokemon-${pokemon.id}-${ind + 1}`}
              style={{ border: `2px solid ${TYPE_COLOR[poke?.type]}` }}
            >
              <div className={styles.pokemonHeader}>
                <div className={styles.pokemonName}>
                  {selectedDetail?.type === 'TRIAL' && ind === 0 && (
                    <span className={styles.totem}>TOTEM</span>
                  )}
                  {selectedDetail?.type === 'DYNAMAX' &&
                    ind === (selectedDetail?.content?.length ?? 0) - 1 && (
                      <span className={styles.totem}>DYNAMAX</span>
                    )}
                  <div className={styles.pokemonImage}>
                    <PkmImage name={poke?.text} />
                  </div>
                  <span>{poke?.text}</span>
                  <span>Lv. {pokemon?.level}</span>
                </div>
                <div className={styles.pokemonDetails}>
                  <PokemonType pokemon={poke} />
                  {!!pokemon?.ability && (
                    <div className={styles.pokemonLabel}>
                      <span>{t('ability')}:</span>
                      <span className={styles.value}>{pokemon?.ability}</span>
                    </div>
                  )}
                  {!!pokemon?.item && (
                    <div className={styles.pokemonLabel}>
                      <span>{t('item')}:</span>
                      <div className={styles.item}>
                        <span className={styles.value}>{pokemon?.item}</span>
                        <div className="pkitem-wrapper">
                          <div className={`pkitem pkitem-${getSmogonItemName(pokemon?.item)}`} />
                        </div>
                      </div>
                    </div>
                  )}
                </div>
              </div>
              <div className={styles.extendedDetails}>
                {stats?.stats && (
                  <div className={styles.stats}>
                    {Object.entries(stats.stats).map(([key, value]) => (
                      <React.Fragment key={`${key}-${value}`}>
                        <span className={styles.statLabel}>{t(key)}:</span>
                        <span style={{ color: colors[key][0] }}>{value}</span>
                        <div
                          className={styles.statBar}
                          style={{
                            width: `${(value / 450) * 100}%`,
                            backgroundColor: colors[key][0],
                            border: `1px solid ${colors[key][1]}`,
                          }}
                        />
                      </React.Fragment>
                    ))}
                  </div>
                )}
                <Moves moves={pokemon?.moves} showStatus={isSplit} />
              </div>
            </div>
          );
        })}
      </div>
    </>
  );
}
Example #6
Source File: Member.tsx    From nuzlocke with BSD 3-Clause "New" or "Revised" License 4 votes vote down vote up
function Member({ index, pokemonDetail }: MemberProps): JSX.Element {
  const { t } = useTranslation('builder');
  const pokemon = POKEMAP.get(pokemonDetail.id);
  const changeTeamMember = useStore(useCallback((state) => state.changeTeamMember, []));
  const deleteTeamMember = useStore(useCallback((state) => state.deleteTeamMember, []));
  const darkMode = useStore(useCallback((state) => state.darkMode, []));
  const [expanded, setExpanded] = useState(false);

  return (
    <>
      <div
        className={styles.member}
        data-testid={`team-poke-${pokemon.text}`}
        style={{ backgroundColor: `${TYPE_COLOR[pokemon.type]}50` }}
      >
        <div className={styles.info}>
          <div className={styles.pokemonImage}>
            <PkmImage name={pokemon?.text} />
          </div>
          <b>{pokemon.text}</b>
        </div>
        <div className={styles.other}>
          <PokemonType pokemon={pokemon} />
          <Icon
            className={styles.arrow}
            style={{ transform: expanded ? 'rotate(90deg)' : undefined }}
            name="angle right"
            onClick={() => setExpanded((prevState) => !prevState)}
          />
        </div>
      </div>
      <div className={`${styles.details} ${expanded ? styles.open : ''}`}>
        <div>
          <Dropdown
            aria-label="nature-selector"
            basic
            className={`${dropdownStyles.dropdown} ${styles.nature}`}
            data-testid={`team-nature-${pokemon?.text}`}
            inline
            lazyLoad
            onChange={(e, data) =>
              changeTeamMember(index, { ...pokemonDetail, nature: data.value as unknown as string })
            }
            options={NATURES}
            placeholder={t('select_nature', { ns: 'calculator' })}
            search
            selection
            value={pokemonDetail.nature ?? ''}
          />
          <Natures />
        </div>
        <Dropdown
          aria-label="ability"
          basic
          className={dropdownStyles.dropdown}
          data-testid={`team-ability-${pokemon?.text}`}
          inline
          lazyLoad
          onChange={(e, data) =>
            changeTeamMember(index, { ...pokemonDetail, ability: data.value as string })
          }
          options={[...new Set(ABILITIES[8])].map((smogonAbility) => {
            return { text: smogonAbility, value: smogonAbility };
          })}
          placeholder={t('select_ability', { ns: 'calculator' })}
          search
          selection
          value={pokemonDetail.ability ?? ''}
        />
        <ItemSelector
          dataTestId={`team-item-${pokemon?.text}`}
          item={pokemonDetail.item}
          onChange={(newItem) => changeTeamMember(index, { ...pokemonDetail, item: newItem })}
        />
        <MoveSelector
          currentMoveId={pokemonDetail?.moves?.length > 0 ? pokemonDetail.moves[0] : null}
          handleMove={(moveId: number) =>
            changeTeamMember(index, {
              ...pokemonDetail,
              moves: [
                moveId,
                pokemonDetail.moves[1],
                pokemonDetail.moves[2],
                pokemonDetail.moves[3],
              ],
            })
          }
        />
        <MoveSelector
          currentMoveId={pokemonDetail?.moves?.length > 1 ? pokemonDetail.moves[1] : null}
          handleMove={(moveId: number) =>
            changeTeamMember(index, {
              ...pokemonDetail,
              moves: [
                pokemonDetail.moves[0],
                moveId,
                pokemonDetail.moves[2],
                pokemonDetail.moves[3],
              ],
            })
          }
        />
        <MoveSelector
          currentMoveId={pokemonDetail?.moves?.length > 2 ? pokemonDetail.moves[2] : null}
          handleMove={(moveId: number) =>
            changeTeamMember(index, {
              ...pokemonDetail,
              moves: [
                pokemonDetail.moves[0],
                pokemonDetail.moves[1],
                moveId,
                pokemonDetail.moves[3],
              ],
            })
          }
        />
        <MoveSelector
          currentMoveId={pokemonDetail?.moves?.length > 3 ? pokemonDetail.moves[3] : null}
          handleMove={(moveId: number) =>
            changeTeamMember(index, {
              ...pokemonDetail,
              moves: [
                pokemonDetail.moves[0],
                pokemonDetail.moves[1],
                pokemonDetail.moves[2],
                moveId,
              ],
            })
          }
        />
        <Button
          className={styles.delete}
          color="red"
          data-testid={`delete-team-${index}`}
          inverted={darkMode}
          onClick={() => deleteTeamMember(index)}
        >
          {t('delete')}
          <Icon className="icon close" />
        </Button>
      </div>
    </>
  );
}
Example #7
Source File: Detail.tsx    From nuzlocke with BSD 3-Clause "New" or "Revised" License 4 votes vote down vote up
function Detail({ encounter }: DetailProps): JSX.Element {
  const { t } = useTranslation('tracker');
  const darkMode = useStore(useCallback((state) => state.darkMode, []));
  const changeDetails = useStore(useCallback((state) => state.changeDetails, []));
  const exportTeamMember = useStore(useCallback((state) => state.exportTeamMember, []));
  const exportToGame = useStore(useCallback((state) => state.exportToGame, []));
  const selectedGame = useStore(useCallback((state) => state.selectedGame, []));
  const gamesList = useStore(useCallback((state) => state.gamesList, []));
  const soulink = useStore(useCallback((state) => state.soulink, []));
  const isNatureAbilityGen = useStore(selectNAGeneration);
  const isItemGenderGen = useStore(selectItemGeneration);
  const foundPokemon = POKEMAP.get(encounter.pokemon);
  const [show, setShow] = useState(false);
  const [level, setLevel] = useState(encounter?.details?.level);
  const [metLevel, setMetLevel] = useState(encounter?.details?.metLevel);
  const [gender, setGender] = useState(encounter?.details?.gender);
  const [ability, setAbility] = useState(encounter?.details?.ability);
  const [nature, setNature] = useState(encounter?.details?.nature);
  const [item, setItem] = useState(encounter?.details?.item);
  const [faint, setFaint] = useState(encounter?.details?.faint);
  const [moveOne, setMoveOne] = useState(encounter?.details?.moves[0]);
  const [moveTwo, setMoveTwo] = useState(encounter?.details?.moves[1]);
  const [moveThree, setMoveThree] = useState(encounter?.details?.moves[2]);
  const [moveFour, setMoveFour] = useState(encounter?.details?.moves[3]);
  const [shiny, setShiny] = useState(encounter?.details?.shiny);
  const [ivhp, setIvhp] = useState(encounter?.details?.ivhp);
  const [ivatk, setIvatk] = useState(encounter?.details?.ivatk);
  const [ivdef, setIvdef] = useState(encounter?.details?.ivdef);
  const [ivspatk, setIvspatk] = useState(encounter?.details?.ivspatk);
  const [ivspeed, setIvspeed] = useState(encounter?.details?.ivspeed);
  const [ivspdef, setIvspdef] = useState(encounter?.details?.ivspdef);
  const [evhp, setEvhp] = useState(encounter?.details?.evhp);
  const [evatk, setEvatk] = useState(encounter?.details?.evatk);
  const [evdef, setEvdef] = useState(encounter?.details?.evdef);
  const [evspatk, setEvspatk] = useState(encounter?.details?.evspatk);
  const [evspeed, setEvspeed] = useState(encounter?.details?.evspeed);
  const [evspdef, setEvspdef] = useState(encounter?.details?.evspdef);
  const [soulLink, setSoulLink] = useState(encounter?.details?.soulink);

  const limitGen = GAME_GENERATION[selectedGame?.value] || undefined;
  const foundSoulLink = POKEMAP.get(soulLink);

  const handleClose = () => {
    setShow(false);
    setLevel(encounter?.details?.level);
    setMetLevel(encounter?.details?.metLevel);
    setGender(encounter?.details?.gender);
    setAbility(encounter?.details?.ability);
    setNature(encounter?.details?.nature);
    setItem(encounter?.details?.item);
    setFaint(encounter?.details?.faint);
    setMoveOne(encounter?.details?.moves[0]);
    setMoveTwo(encounter?.details?.moves[1]);
    setMoveThree(encounter?.details?.moves[2]);
    setMoveFour(encounter?.details?.moves[3]);
    setShiny(encounter?.details?.shiny);
    setSoulLink(encounter?.details?.soulink);
  };

  const handleSave = () => {
    changeDetails(
      encounter.id,
      level,
      metLevel,
      gender,
      ability,
      nature,
      item,
      faint,
      moveOne,
      moveTwo,
      moveThree,
      moveFour,
      shiny,
      ivhp,
      ivatk,
      ivdef,
      ivspatk,
      ivspdef,
      ivspeed,
      evhp,
      evatk,
      evdef,
      evspatk,
      evspdef,
      evspeed,
      soulLink
    );
    setShow(false);
  };

  const handleExport = () => {
    exportTeamMember({
      ability,
      id: encounter.pokemon,
      level,
      item,
      nature,
      moves: [moveOne, moveTwo, moveThree, moveFour],
    });
    toast.success(t('pokemon_export'));
  };

  const handleGameExport = (game: string) => {
    if (game !== selectedGame?.value) {
      exportToGame(encounter, game, `From ${selectedGame?.text} - ${new Date().toLocaleString()}`);
      handleClose();
      toast.success(t('pokemon_export'));
    }
  };

  const handleDeleteSoulLink = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
    e.preventDefault();
    e.stopPropagation();
    setSoulLink(null);
  };

  return (
    <Modal
      open={show}
      trigger={
        <Button
          aria-label="edit encounter"
          basic
          compact
          data-testid={`edit-encounter-${encounter.id}`}
          icon
          inverted={darkMode}
          onClick={() => setShow(true)}
          type="button"
        >
          <Icon name="pencil" />
        </Button>
      }
    >
      <Modal.Content className={styles.content} scrolling>
        <div className={styles.header}>
          <div className={styles.headerLeft}>
            <div className={styles.image}>
              <PkmImage name={foundPokemon?.text} shiny={shiny} />
            </div>
            <span className={styles.name}>{foundPokemon.text}</span>
          </div>
          <PokemonType pokemon={foundPokemon} />
        </div>
        {soulink && (
          <div className={styles.soulink}>
            <span>Soul Link Pokémon:</span>
            <PokemonSelector handlePokemon={(pokemonId) => setSoulLink(pokemonId)}>
              {soulLink ? (
                <div className={styles.selector} data-testid={`soullink-${encounter.id}`}>
                  <div className={styles.image}>
                    <PkmImage name={foundSoulLink.text} />
                  </div>
                  <span className={styles.soulLinkName}>{foundSoulLink.text}</span>
                  <Button
                    aria-label="delete-soullink"
                    className={styles.deleteSoullink}
                    data-testid={`delete-soullink-${encounter.id}`}
                    icon
                    onClick={handleDeleteSoulLink}
                    style={{ boxShadow: 'none' }}
                  >
                    <Icon name="trash" />
                  </Button>
                </div>
              ) : (
                <div className={styles.selector} data-testid={`soullink-${encounter.id}`}>
                  <span
                    data-testid={`soullink-empty-${encounter.id}`}
                    className={styles.placeholder}
                  >
                    Pokémon...
                  </span>
                </div>
              )}
            </PokemonSelector>
          </div>
        )}
        <details open>
          <summary data-testid="detail-summary">{t('details', { ns: 'badges' })}</summary>
          <div className={styles.expandable}>
            <Input
              className={styles.input}
              data-testid="level"
              maxLength={3}
              label={t('level', { ns: 'rules' })}
              onChange={(e, data) => setLevel(Number(data.value))}
              value={Number.isNaN(level) ? '' : level}
            />
            <Input
              className={styles.input}
              data-testid="metlevel"
              label={t('met_level')}
              maxLength={3}
              onChange={(e, data) => setMetLevel(Number(data.value))}
              value={Number.isNaN(metLevel) ? '' : metLevel}
            />
            {isItemGenderGen && (
              <Dropdown
                aria-label="gender-selector"
                className={dropdownStyles.dropdown}
                clearable
                data-testid="gender"
                inline
                lazyLoad
                onChange={(e, data) => setGender(data.value as unknown as Gender)}
                options={GENDERS}
                placeholder={t('gender', { ns: 'calculator' })}
                selection
                value={gender ?? ''}
              />
            )}
            {isNatureAbilityGen && (
              <div className={styles.natureContainer}>
                <Dropdown
                  aria-label="nature-selector"
                  className={dropdownStyles.dropdown}
                  clearable
                  data-testid="nature"
                  inline
                  lazyLoad
                  onChange={(e, data) => setNature(data.value as unknown as string)}
                  options={NATURES}
                  placeholder={t('select_nature', { ns: 'calculator' })}
                  search
                  selection
                  value={nature ?? ''}
                />
                <Natures />
              </div>
            )}
            {isNatureAbilityGen && (
              <div className={styles.natureContainer}>
                <Dropdown
                  aria-label="ability"
                  className={dropdownStyles.dropdown}
                  clearable
                  data-testid="ability"
                  inline
                  lazyLoad
                  onChange={(e, data) => setAbility(data.value as unknown as string)}
                  options={[...new Set(ABILITIES[8])].map((smogonAbility) => {
                    return { text: smogonAbility, value: smogonAbility };
                  })}
                  placeholder={t('select_ability', { ns: 'calculator' })}
                  search
                  selection
                  value={ability ?? ''}
                />
                <Abilities text={ability ?? ''} />
              </div>
            )}
            {isItemGenderGen && (
              <ItemSelector item={item} onChange={(newItem) => setItem(newItem)} />
            )}
            <Checkbox
              checked={shiny}
              className={styles.checkbox}
              data-testid="shiny"
              label="Shiny"
              onChange={(e, data) => setShiny(data.checked)}
            />
          </div>
        </details>
        <details className={styles.expandable}>
          <summary data-testid="move-summary">Moves</summary>
          <div className={styles.expandable}>
            <div data-testid="move-1">
              <span>Move 1:</span>
              <MoveSelector
                currentMoveId={moveOne}
                handleMove={(moveId: number) => setMoveOne(moveId)}
                limitGen={limitGen}
              />
            </div>
            <div data-testid="move-2">
              <span>Move 2:</span>
              <MoveSelector
                currentMoveId={moveTwo}
                handleMove={(moveId: number) => setMoveTwo(moveId)}
                limitGen={limitGen}
              />
            </div>
            <div data-testid="move-3">
              <span>Move 3:</span>
              <MoveSelector
                currentMoveId={moveThree}
                handleMove={(moveId: number) => setMoveThree(moveId)}
                limitGen={limitGen}
              />
            </div>
            <div data-testid="move-4">
              <span>Move 4:</span>
              <MoveSelector
                currentMoveId={moveFour}
                handleMove={(moveId: number) => setMoveFour(moveId)}
                limitGen={limitGen}
              />
            </div>
          </div>
        </details>

        <details
          className={styles.expandable}
          style={{ display: limitGen > 2 ? undefined : 'none' }}
        >
          <summary data-testid="stats-summary">Stats</summary>
          <div className={styles.expandable}>
            HP:
            <fieldset className={styles.fieldset}>
              <RangeSelector
                name="ivhp"
                value={ivhp ?? 0}
                onChange={(newValue: number) => setIvhp(newValue)}
              />
              <RangeSelector
                name="evhp"
                value={evhp ?? 0}
                onChange={(newValue: number) => setEvhp(newValue)}
              />
            </fieldset>
            ATK:
            <fieldset className={styles.fieldset}>
              <RangeSelector
                name="ivatk"
                value={ivatk ?? 0}
                onChange={(newValue: number) => setIvatk(newValue)}
              />
              <RangeSelector
                name="evatk"
                value={evatk ?? 0}
                onChange={(newValue: number) => setEvatk(newValue)}
              />
            </fieldset>
            DEF:
            <fieldset className={styles.fieldset}>
              <RangeSelector
                name="ivdef"
                value={ivdef ?? 0}
                onChange={(newValue: number) => setIvdef(newValue)}
              />
              <RangeSelector
                name="evdef"
                value={evdef ?? 0}
                onChange={(newValue: number) => setEvdef(newValue)}
              />
            </fieldset>
            SPATK:
            <fieldset className={styles.fieldset}>
              <RangeSelector
                name="ivspatk"
                value={ivspatk ?? 0}
                onChange={(newValue: number) => setIvspatk(newValue)}
              />
              <RangeSelector
                name="evspatk"
                value={evspatk ?? 0}
                onChange={(newValue: number) => setEvspatk(newValue)}
              />
            </fieldset>
            SPDEF:
            <fieldset className={styles.fieldset}>
              <RangeSelector
                name="ivspdef"
                value={ivspdef ?? 0}
                onChange={(newValue: number) => setIvspdef(newValue)}
              />
              <RangeSelector
                name="evspdef"
                value={evspdef ?? 0}
                onChange={(newValue: number) => setEvspdef(newValue)}
              />
            </fieldset>
            SPEED:
            <fieldset className={styles.fieldset}>
              <RangeSelector
                name="ivspeed"
                value={ivspeed ?? 0}
                onChange={(newValue: number) => setIvspeed(newValue)}
              />
              <RangeSelector
                name="evspeed"
                value={evspeed ?? 0}
                onChange={(newValue: number) => setEvspeed(newValue)}
              />
            </fieldset>
          </div>
        </details>
        {encounter?.status?.value === 2 && (
          <label>
            <div>{t('cause_of_fainting')}:</div>
            <textarea
              className={styles.textarea}
              data-testid="cause of fainting"
              maxLength={250}
              onChange={(e) => setFaint(e.target.value)}
              rows={5}
              value={faint}
            />
          </label>
        )}
      </Modal.Content>
      <Modal.Actions>
        <Button onClick={handleClose}>{t('cancel', { ns: 'common' })}</Button>
        <Button onClick={handleExport}>{t('export_to_builder')}</Button>
        <Dropdown
          button
          data-testid="export-to-game"
          onChange={(e, data) => handleGameExport(data.value as string)}
          options={gamesList}
          text={t('export_to_game')}
          value={selectedGame?.value}
        />
        <Button onClick={handleSave} primary>
          {t('save', { ns: 'common' })}
        </Button>
      </Modal.Actions>
    </Modal>
  );
}