2024-01-21 11:06:40 -06:00
|
|
|
import { slateDark } from '@radix-ui/colors';
|
2024-02-06 15:10:54 -06:00
|
|
|
import {
|
|
|
|
|
CaretDownIcon,
|
|
|
|
|
MagnifyingGlassIcon,
|
|
|
|
|
TrashIcon,
|
|
|
|
|
} from '@radix-ui/react-icons';
|
2024-01-21 11:06:40 -06:00
|
|
|
import {
|
|
|
|
|
Button,
|
|
|
|
|
Card,
|
|
|
|
|
DropdownMenu,
|
|
|
|
|
Flex,
|
|
|
|
|
Inset,
|
|
|
|
|
Text,
|
|
|
|
|
TextField,
|
|
|
|
|
} from '@radix-ui/themes';
|
|
|
|
|
import { go } from 'fuzzysort';
|
2024-02-06 15:10:54 -06:00
|
|
|
import { use, useEffect, useMemo, useRef, useState } from 'react';
|
2024-03-07 13:49:38 -06:00
|
|
|
import { TANK_TYPES, TreeType } from '../../../../../components/Tanks';
|
2024-02-09 16:01:06 -06:00
|
|
|
import { resolveNearPenetration } from '../../../../../core/blitz/resolveNearPenetration';
|
2024-01-21 11:06:40 -06:00
|
|
|
import { asset } from '../../../../../core/blitzkrieg/asset';
|
2024-02-09 16:01:06 -06:00
|
|
|
import { modelDefinitions } from '../../../../../core/blitzkrieg/modelDefinitions';
|
|
|
|
|
import { normalizeBoundingBox } from '../../../../../core/blitzkrieg/normalizeBoundingBox';
|
|
|
|
|
import { resolveDpm } from '../../../../../core/blitzkrieg/resolveDpm';
|
|
|
|
|
import { resolveReload } from '../../../../../core/blitzkrieg/resolveReload';
|
2024-01-21 11:06:40 -06:00
|
|
|
import {
|
2024-02-06 13:18:39 -06:00
|
|
|
NATIONS,
|
2024-01-21 11:06:40 -06:00
|
|
|
TANK_ICONS,
|
|
|
|
|
TANK_ICONS_COLLECTOR,
|
|
|
|
|
TANK_ICONS_PREMIUM,
|
|
|
|
|
TIER_ROMAN_NUMERALS,
|
|
|
|
|
TankDefinition,
|
|
|
|
|
tanksDefinitionsArray,
|
|
|
|
|
} from '../../../../../core/blitzkrieg/tankDefinitions';
|
|
|
|
|
import { tankIcon } from '../../../../../core/blitzkrieg/tankIcon';
|
2024-02-09 16:01:06 -06:00
|
|
|
import { unionBoundingBox } from '../../../../../core/blitzkrieg/unionBoundingBox';
|
2024-01-21 11:06:40 -06:00
|
|
|
import { theme } from '../../../../../stitches.config';
|
|
|
|
|
import mutateTankopediaPersistent, {
|
2024-02-10 00:04:43 -06:00
|
|
|
SORT_NAMES,
|
2024-01-21 11:06:40 -06:00
|
|
|
TankopediaSortDirection,
|
|
|
|
|
useTankopediaPersistent,
|
|
|
|
|
} from '../../../../../stores/tankopedia';
|
|
|
|
|
import * as styles from '../../page.css';
|
|
|
|
|
import { PageTurner } from '../PageTurner';
|
|
|
|
|
import { CompactSearchResultRow } from './components/CompactSearchResultRow';
|
|
|
|
|
import { Options } from './components/Options';
|
|
|
|
|
|
|
|
|
|
interface TankSearchProps {
|
|
|
|
|
compact?: boolean;
|
|
|
|
|
onSelect?: (tank: TankDefinition) => void;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 13:18:39 -06:00
|
|
|
const treeTypeOrder: TreeType[] = ['researchable', 'premium', 'collector'];
|
|
|
|
|
|
2024-01-21 11:06:40 -06:00
|
|
|
export function TankSearch({ compact, onSelect = () => {} }: TankSearchProps) {
|
2024-02-09 23:37:22 -06:00
|
|
|
const tanksPerPage = compact ? 16 : 96;
|
2024-02-06 13:18:39 -06:00
|
|
|
const nations = use(NATIONS);
|
2024-01-21 11:06:40 -06:00
|
|
|
const filters = useTankopediaPersistent((state) => state.filters);
|
|
|
|
|
const sort = useTankopediaPersistent((state) => state.sort);
|
2024-02-06 15:01:03 -06:00
|
|
|
const awaitedTanks = use(tanksDefinitionsArray);
|
2024-02-09 16:01:06 -06:00
|
|
|
const awaitedModelDefinitions = use(modelDefinitions);
|
2024-02-06 15:01:03 -06:00
|
|
|
const defaultSortedTanks = useMemo(
|
|
|
|
|
() =>
|
|
|
|
|
awaitedTanks
|
2024-02-06 13:18:39 -06:00
|
|
|
.sort(
|
|
|
|
|
(a, b) =>
|
2024-02-09 19:43:02 -06:00
|
|
|
(treeTypeOrder.indexOf(a.treeType) -
|
|
|
|
|
treeTypeOrder.indexOf(b.treeType)) *
|
|
|
|
|
(sort.direction === 'descending' ? -1 : 1),
|
2024-02-06 13:18:39 -06:00
|
|
|
)
|
|
|
|
|
.sort(
|
|
|
|
|
(a, b) =>
|
2024-03-07 13:49:38 -06:00
|
|
|
(TANK_TYPES.indexOf(a.type) - TANK_TYPES.indexOf(b.type)) *
|
2024-02-09 19:43:02 -06:00
|
|
|
(sort.direction === 'descending' ? -1 : 1),
|
2024-02-06 13:18:39 -06:00
|
|
|
)
|
2024-02-09 19:43:02 -06:00
|
|
|
.sort(
|
|
|
|
|
(a, b) =>
|
|
|
|
|
(nations.indexOf(a.nation) - nations.indexOf(b.nation)) *
|
|
|
|
|
(sort.direction === 'descending' ? -1 : 1),
|
|
|
|
|
),
|
2024-02-06 15:01:03 -06:00
|
|
|
[],
|
2024-02-06 13:18:39 -06:00
|
|
|
);
|
2024-01-21 11:06:40 -06:00
|
|
|
const input = useRef<HTMLInputElement>(null);
|
2024-02-09 16:01:06 -06:00
|
|
|
const searchableTanks = useMemo(() => {
|
|
|
|
|
const filtered = defaultSortedTanks.filter(
|
|
|
|
|
(tank) =>
|
|
|
|
|
(filters.tiers.length === 0
|
|
|
|
|
? true
|
|
|
|
|
: filters.tiers.includes(tank.tier)) &&
|
|
|
|
|
(filters.types.length === 0
|
|
|
|
|
? true
|
|
|
|
|
: filters.types.includes(tank.type)) &&
|
|
|
|
|
(filters.treeTypes.length === 0
|
|
|
|
|
? true
|
|
|
|
|
: filters.treeTypes.includes(tank.treeType)) &&
|
|
|
|
|
(filters.nations.length === 0
|
|
|
|
|
? true
|
|
|
|
|
: filters.nations.includes(tank.nation)) &&
|
|
|
|
|
(filters.test === 'include'
|
|
|
|
|
? true
|
|
|
|
|
: filters.test === 'exclude'
|
|
|
|
|
? !tank.testing
|
|
|
|
|
: tank.testing),
|
|
|
|
|
);
|
|
|
|
|
let sorted: TankDefinition[] = [];
|
2024-01-21 11:06:40 -06:00
|
|
|
|
2024-02-09 16:01:06 -06:00
|
|
|
switch (sort.by) {
|
|
|
|
|
case 'meta.tier':
|
|
|
|
|
sorted = filtered.sort((a, b) => a.tier - b.tier);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'meta.name':
|
|
|
|
|
sorted = filtered.sort((a, b) => a.name?.localeCompare(b.name));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'survivability.health': {
|
|
|
|
|
sorted = filtered.sort((a, b) => {
|
|
|
|
|
const aHealth = a.health + a.turrets.at(-1)!.health;
|
|
|
|
|
const bHealth = b.health + b.turrets.at(-1)!.health;
|
|
|
|
|
|
|
|
|
|
return aHealth - bHealth;
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case 'survivability.viewRange':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) => a.turrets.at(-1)!.viewRange - b.turrets.at(-1)!.viewRange,
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'survivability.camouflageStill':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) => a.camouflage.still - b.camouflage.still,
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'survivability.camouflageMoving':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) => a.camouflage.moving - b.camouflage.moving,
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'survivability.camouflageShooting':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) =>
|
|
|
|
|
a.turrets.at(-1)!.guns.at(-1)!.camouflageLoss -
|
|
|
|
|
b.turrets.at(-1)!.guns.at(-1)!.camouflageLoss,
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'survivability.size':
|
|
|
|
|
sorted = filtered.sort((a, b) => {
|
|
|
|
|
const aTankModelDefinition = awaitedModelDefinitions[a.id];
|
|
|
|
|
const bTankModelDefinition = awaitedModelDefinitions[b.id];
|
|
|
|
|
const aTurretModelDefinition =
|
|
|
|
|
aTankModelDefinition.turrets[a.turrets.at(-1)!.id];
|
|
|
|
|
const bTurretModelDefinition =
|
|
|
|
|
bTankModelDefinition.turrets[b.turrets.at(-1)!.id];
|
|
|
|
|
const aSize = normalizeBoundingBox(
|
|
|
|
|
unionBoundingBox(
|
|
|
|
|
aTankModelDefinition.boundingBox,
|
|
|
|
|
aTurretModelDefinition.boundingBox,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
const bSize = normalizeBoundingBox(
|
|
|
|
|
unionBoundingBox(
|
|
|
|
|
bTankModelDefinition.boundingBox,
|
|
|
|
|
bTurretModelDefinition.boundingBox,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
const aVolume = aSize[0] * aSize[1] * aSize[2];
|
|
|
|
|
const bVolume = bSize[0] * bSize[1] * bSize[2];
|
|
|
|
|
|
|
|
|
|
return aVolume - bVolume;
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'fire.dpm':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) =>
|
|
|
|
|
resolveDpm(
|
|
|
|
|
a.turrets.at(-1)!.guns.at(-1)!,
|
|
|
|
|
a.turrets.at(-1)!.guns.at(-1)!.shells[0],
|
|
|
|
|
) -
|
|
|
|
|
resolveDpm(
|
|
|
|
|
b.turrets.at(-1)!.guns.at(-1)!,
|
|
|
|
|
b.turrets.at(-1)!.guns.at(-1)!.shells[0],
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'fire.reload':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) =>
|
|
|
|
|
resolveReload(a.turrets.at(-1)!.guns.at(-1)!) -
|
|
|
|
|
resolveReload(b.turrets.at(-1)!.guns.at(-1)!),
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'fire.caliber':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) =>
|
|
|
|
|
a.turrets.at(-1)!.guns.at(-1)!.shells[0].caliber -
|
|
|
|
|
b.turrets.at(-1)!.guns.at(-1)!.shells[0].caliber,
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'fire.standardPenetration':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) =>
|
|
|
|
|
resolveNearPenetration(
|
|
|
|
|
a.turrets.at(-1)!.guns.at(-1)!.shells[0].penetration,
|
|
|
|
|
) -
|
|
|
|
|
resolveNearPenetration(
|
|
|
|
|
b.turrets.at(-1)!.guns.at(-1)!.shells[0].penetration,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'fire.premiumPenetration':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) =>
|
|
|
|
|
resolveNearPenetration(
|
|
|
|
|
(
|
|
|
|
|
a.turrets.at(-1)!.guns.at(-1)!.shells[1] ??
|
|
|
|
|
a.turrets.at(-1)!.guns.at(-1)!.shells[0]
|
|
|
|
|
).penetration,
|
|
|
|
|
) -
|
|
|
|
|
resolveNearPenetration(
|
|
|
|
|
(
|
|
|
|
|
b.turrets.at(-1)!.guns.at(-1)!.shells[1] ??
|
|
|
|
|
b.turrets.at(-1)!.guns.at(-1)!.shells[0]
|
|
|
|
|
).penetration,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'fire.damage':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) =>
|
|
|
|
|
a.turrets.at(-1)!.guns.at(-1)!.shells[0].damage.armor -
|
|
|
|
|
b.turrets.at(-1)!.guns.at(-1)!.shells[0].damage.armor,
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'fire.shellVelocity':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) =>
|
|
|
|
|
a.turrets.at(-1)!.guns.at(-1)!.shells[0].speed -
|
|
|
|
|
b.turrets.at(-1)!.guns.at(-1)!.shells[0].speed,
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'fire.aimTime':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) =>
|
|
|
|
|
a.turrets.at(-1)!.guns.at(-1)!.aimTime -
|
|
|
|
|
b.turrets.at(-1)!.guns.at(-1)!.aimTime,
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'fire.dispersionStill':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) =>
|
|
|
|
|
a.turrets.at(-1)!.guns.at(-1)!.dispersion.base -
|
|
|
|
|
b.turrets.at(-1)!.guns.at(-1)!.dispersion.base,
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'fire.dispersionMoving':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) =>
|
|
|
|
|
a.tracks.at(-1)!.dispersion.move - b.tracks.at(-1)!.dispersion.move,
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'fire.gunDepression':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) =>
|
|
|
|
|
awaitedModelDefinitions[a.id].turrets[a.turrets.at(-1)!.id].guns[
|
|
|
|
|
a.turrets.at(-1)!.guns.at(-1)!.id
|
|
|
|
|
].pitch.max -
|
|
|
|
|
awaitedModelDefinitions[b.id].turrets[b.turrets.at(-1)!.id].guns[
|
|
|
|
|
b.turrets.at(-1)!.guns.at(-1)!.id
|
|
|
|
|
].pitch.max,
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'fire.gunElevation':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) =>
|
|
|
|
|
// reversed because of negative values
|
|
|
|
|
awaitedModelDefinitions[b.id].turrets[b.turrets.at(-1)!.id].guns[
|
|
|
|
|
b.turrets.at(-1)!.guns.at(-1)!.id
|
|
|
|
|
].pitch.min -
|
|
|
|
|
awaitedModelDefinitions[a.id].turrets[a.turrets.at(-1)!.id].guns[
|
|
|
|
|
a.turrets.at(-1)!.guns.at(-1)!.id
|
|
|
|
|
].pitch.min,
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'maneuverability.forwardsSpeed':
|
|
|
|
|
sorted = filtered.sort((a, b) => a.speed.forwards - b.speed.forwards);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'maneuverability.backwardsSpeed':
|
|
|
|
|
sorted = filtered.sort((a, b) => a.speed.backwards - b.speed.backwards);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'maneuverability.power':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) => a.engines.at(-1)!.power - b.engines.at(-1)!.power,
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'maneuverability.powerToWeight':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) =>
|
|
|
|
|
a.engines.at(-1)!.power /
|
|
|
|
|
(a.weight +
|
|
|
|
|
a.engines.at(-1)!.weight +
|
|
|
|
|
a.tracks.at(-1)!.weight +
|
|
|
|
|
a.turrets.at(-1)!.weight +
|
|
|
|
|
a.turrets.at(-1)!.guns.at(-1)!.weight) -
|
|
|
|
|
b.engines.at(-1)!.power /
|
|
|
|
|
(b.weight +
|
|
|
|
|
b.engines.at(-1)!.weight +
|
|
|
|
|
b.tracks.at(-1)!.weight +
|
|
|
|
|
b.turrets.at(-1)!.weight +
|
|
|
|
|
b.turrets.at(-1)!.guns.at(-1)!.weight),
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'maneuverability.weight':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) =>
|
|
|
|
|
a.weight +
|
|
|
|
|
a.engines.at(-1)!.weight +
|
|
|
|
|
a.tracks.at(-1)!.weight +
|
|
|
|
|
a.turrets.at(-1)!.weight +
|
|
|
|
|
a.turrets.at(-1)!.guns.at(-1)!.weight -
|
|
|
|
|
(b.weight +
|
|
|
|
|
b.engines.at(-1)!.weight +
|
|
|
|
|
b.tracks.at(-1)!.weight +
|
|
|
|
|
b.turrets.at(-1)!.weight +
|
|
|
|
|
b.turrets.at(-1)!.guns.at(-1)!.weight),
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'maneuverability.traverseSpeed':
|
|
|
|
|
sorted = filtered.sort(
|
|
|
|
|
(a, b) =>
|
|
|
|
|
a.tracks.at(-1)!.traverseSpeed - b.tracks.at(-1)!.traverseSpeed,
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sort.direction === 'ascending' ? sorted : sorted.reverse();
|
2024-02-22 18:11:06 -06:00
|
|
|
}, [
|
|
|
|
|
sort,
|
|
|
|
|
...Object.entries(filters)
|
|
|
|
|
.filter(([name]) => name !== 'page')
|
|
|
|
|
.map(([, value]) => value),
|
|
|
|
|
]);
|
2024-01-21 11:06:40 -06:00
|
|
|
const [searchResults, setSearchedList] = useState(searchableTanks);
|
|
|
|
|
const page = useTankopediaPersistent((state) => state.filters.page);
|
2024-02-22 18:11:06 -06:00
|
|
|
const wideFormat = typeof window !== 'undefined' && window.innerWidth > 512;
|
|
|
|
|
const chunkSize = wideFormat ? tanksPerPage / 2 : Infinity;
|
2024-01-21 11:06:40 -06:00
|
|
|
const searchResultsPageSlice = searchResults.slice(
|
|
|
|
|
page * tanksPerPage,
|
|
|
|
|
(page + 1) * tanksPerPage,
|
|
|
|
|
);
|
|
|
|
|
const firstChunk = searchResultsPageSlice.slice(0, chunkSize);
|
|
|
|
|
const secondChunk = searchResultsPageSlice.slice(chunkSize);
|
|
|
|
|
|
2024-02-09 16:01:06 -06:00
|
|
|
useEffect(() => {
|
|
|
|
|
setSearchedList(searchableTanks);
|
|
|
|
|
}, [searchableTanks]);
|
2024-01-21 11:06:40 -06:00
|
|
|
useEffect(() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.filters.page = Math.min(
|
|
|
|
|
Math.max(0, page),
|
|
|
|
|
Math.ceil(searchResults.length / tanksPerPage) - 1,
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}, [searchResults]);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<Flex direction="column" gap="4">
|
|
|
|
|
<Flex gap="2">
|
|
|
|
|
<TextField.Root style={{ flex: 1 }}>
|
|
|
|
|
<TextField.Slot>
|
|
|
|
|
<MagnifyingGlassIcon height="16" width="16" />
|
|
|
|
|
</TextField.Slot>
|
2024-02-06 15:01:03 -06:00
|
|
|
|
2024-01-21 11:06:40 -06:00
|
|
|
<TextField.Input
|
|
|
|
|
ref={input}
|
|
|
|
|
placeholder="Search tanks..."
|
|
|
|
|
onChange={(event) => {
|
2024-02-22 18:11:06 -06:00
|
|
|
const results = go(event.target.value, searchableTanks, {
|
|
|
|
|
keys: [
|
|
|
|
|
'name',
|
|
|
|
|
'nameFull',
|
|
|
|
|
'id',
|
|
|
|
|
] satisfies (keyof TankDefinition)[],
|
|
|
|
|
all: true,
|
|
|
|
|
}).map(({ obj }) => obj);
|
|
|
|
|
|
|
|
|
|
setSearchedList(results);
|
2024-01-21 11:06:40 -06:00
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</TextField.Root>
|
|
|
|
|
|
|
|
|
|
<DropdownMenu.Root>
|
|
|
|
|
<DropdownMenu.Trigger>
|
2024-02-06 15:18:18 -06:00
|
|
|
<Button variant="soft" color="gray">
|
2024-01-21 11:06:40 -06:00
|
|
|
Sort
|
|
|
|
|
<CaretDownIcon />
|
|
|
|
|
</Button>
|
|
|
|
|
</DropdownMenu.Trigger>
|
|
|
|
|
<DropdownMenu.Content>
|
|
|
|
|
<DropdownMenu.Label>By</DropdownMenu.Label>
|
2024-02-09 16:01:06 -06:00
|
|
|
|
|
|
|
|
<DropdownMenu.Sub>
|
|
|
|
|
<DropdownMenu.SubTrigger>Meta</DropdownMenu.SubTrigger>
|
|
|
|
|
<DropdownMenu.SubContent>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'meta.tier'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'meta.tier';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Tier
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'meta.name'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'meta.name';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Name
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
</DropdownMenu.SubContent>
|
|
|
|
|
</DropdownMenu.Sub>
|
|
|
|
|
|
|
|
|
|
<DropdownMenu.Sub>
|
|
|
|
|
<DropdownMenu.SubTrigger>Fire</DropdownMenu.SubTrigger>
|
|
|
|
|
<DropdownMenu.SubContent>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'fire.dpm'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'fire.dpm';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
DPM
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'fire.reload'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'fire.reload';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Reload
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'fire.caliber'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'fire.caliber';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Caliber
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'fire.standardPenetration'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'fire.standardPenetration';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Standard penetration
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'fire.premiumPenetration'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'fire.premiumPenetration';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Premium penetration
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'fire.damage'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'fire.damage';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Damage
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'fire.shellVelocity'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'fire.shellVelocity';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Shell velocity
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'fire.aimTime'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'fire.aimTime';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Aim time
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'fire.dispersionStill'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'fire.dispersionStill';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Dispersion still
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'fire.dispersionMoving'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'fire.dispersionMoving';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Dispersion moving
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'fire.gunDepression'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'fire.gunDepression';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Gun depression
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'fire.gunElevation'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'fire.gunElevation';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Gun elevation
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
</DropdownMenu.SubContent>
|
|
|
|
|
</DropdownMenu.Sub>
|
|
|
|
|
|
|
|
|
|
<DropdownMenu.Sub>
|
|
|
|
|
<DropdownMenu.SubTrigger>
|
|
|
|
|
Maneuverability
|
|
|
|
|
</DropdownMenu.SubTrigger>
|
|
|
|
|
<DropdownMenu.SubContent>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'maneuverability.forwardsSpeed'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'maneuverability.forwardsSpeed';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Forwards speed
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'maneuverability.backwardsSpeed'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'maneuverability.backwardsSpeed';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Backwards speed
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'maneuverability.power'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'maneuverability.power';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Power
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'maneuverability.powerToWeight'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'maneuverability.powerToWeight';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Power to weight
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'maneuverability.weight'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'maneuverability.weight';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Weight
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'maneuverability.traverseSpeed'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'maneuverability.traverseSpeed';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Traverse speed
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
</DropdownMenu.SubContent>
|
|
|
|
|
</DropdownMenu.Sub>
|
2024-01-21 11:06:40 -06:00
|
|
|
|
2024-02-09 23:44:38 -06:00
|
|
|
<DropdownMenu.Sub>
|
|
|
|
|
<DropdownMenu.SubTrigger>Survivability</DropdownMenu.SubTrigger>
|
|
|
|
|
<DropdownMenu.SubContent>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'survivability.health'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'survivability.health';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Health
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'survivability.viewRange'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'survivability.viewRange';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
View range
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'survivability.camouflageStill'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'survivability.camouflageStill';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Camouflage
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'survivability.camouflageMoving'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'survivability.camouflageMoving';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Camouflage moving
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'survivability.camouflageShooting'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'survivability.camouflageShooting';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Camouflage shooting
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
<DropdownMenu.CheckboxItem
|
|
|
|
|
checked={sort.by === 'survivability.size'}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.by = 'survivability.size';
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Size
|
|
|
|
|
</DropdownMenu.CheckboxItem>
|
|
|
|
|
</DropdownMenu.SubContent>
|
|
|
|
|
</DropdownMenu.Sub>
|
|
|
|
|
|
2024-01-21 11:06:40 -06:00
|
|
|
<DropdownMenu.Separator />
|
|
|
|
|
|
|
|
|
|
<DropdownMenu.Label>Direction</DropdownMenu.Label>
|
|
|
|
|
<DropdownMenu.RadioGroup
|
|
|
|
|
value={sort.direction}
|
|
|
|
|
onValueChange={(value) =>
|
|
|
|
|
mutateTankopediaPersistent((draft) => {
|
|
|
|
|
draft.sort.direction = value as TankopediaSortDirection;
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<DropdownMenu.RadioItem value="ascending">
|
|
|
|
|
Ascending
|
|
|
|
|
</DropdownMenu.RadioItem>
|
|
|
|
|
<DropdownMenu.RadioItem value="descending">
|
|
|
|
|
Descending
|
|
|
|
|
</DropdownMenu.RadioItem>
|
|
|
|
|
</DropdownMenu.RadioGroup>
|
|
|
|
|
</DropdownMenu.Content>
|
|
|
|
|
</DropdownMenu.Root>
|
2024-02-06 15:10:54 -06:00
|
|
|
|
|
|
|
|
<Button
|
|
|
|
|
color="red"
|
|
|
|
|
variant="soft"
|
|
|
|
|
onClick={() =>
|
|
|
|
|
useTankopediaPersistent.setState({
|
|
|
|
|
filters: {
|
|
|
|
|
nations: [],
|
|
|
|
|
tiers: [],
|
|
|
|
|
treeTypes: [],
|
|
|
|
|
types: [],
|
|
|
|
|
test: 'include',
|
|
|
|
|
page: 0,
|
|
|
|
|
},
|
2024-02-09 16:04:09 -06:00
|
|
|
sort: {
|
|
|
|
|
by: 'meta.tier',
|
|
|
|
|
direction: 'descending',
|
|
|
|
|
},
|
2024-02-06 15:10:54 -06:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<TrashIcon />
|
|
|
|
|
</Button>
|
2024-01-21 11:06:40 -06:00
|
|
|
</Flex>
|
|
|
|
|
|
2024-02-06 15:10:54 -06:00
|
|
|
<Options />
|
2024-01-21 11:06:40 -06:00
|
|
|
</Flex>
|
|
|
|
|
|
2024-02-10 00:04:43 -06:00
|
|
|
<Flex justify="center">
|
|
|
|
|
<Text color="gray">
|
|
|
|
|
Sorting: {SORT_NAMES[sort.by]}, {sort.direction}
|
|
|
|
|
</Text>
|
|
|
|
|
</Flex>
|
|
|
|
|
|
2024-01-21 11:06:40 -06:00
|
|
|
{!compact && (
|
|
|
|
|
<PageTurner tanksPerPage={tanksPerPage} searchedList={searchResults} />
|
|
|
|
|
)}
|
2024-02-06 13:28:05 -06:00
|
|
|
<Flex wrap="wrap" gap="2" justify={compact ? 'between' : 'center'}>
|
2024-01-21 11:06:40 -06:00
|
|
|
{!compact &&
|
|
|
|
|
searchResultsPageSlice.map((tank) => (
|
|
|
|
|
<Card
|
|
|
|
|
key={tank.id}
|
|
|
|
|
onClick={() => onSelect(tank)}
|
|
|
|
|
className={styles.listing}
|
|
|
|
|
style={{
|
|
|
|
|
flex: 1,
|
|
|
|
|
cursor: 'pointer',
|
2024-02-06 13:28:05 -06:00
|
|
|
height: 64,
|
2024-01-21 11:06:40 -06:00
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Inset
|
|
|
|
|
key={tank.id}
|
|
|
|
|
style={{
|
|
|
|
|
minWidth: 256,
|
2024-02-06 13:28:05 -06:00
|
|
|
minHeight: 64,
|
2024-01-21 11:06:40 -06:00
|
|
|
position: 'relative',
|
|
|
|
|
display: 'flex',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<img
|
|
|
|
|
className={styles.flag}
|
|
|
|
|
src={asset(`flags/scratched/${tank.nation}.webp`)}
|
|
|
|
|
style={{
|
|
|
|
|
width: '100%',
|
|
|
|
|
height: '100%',
|
|
|
|
|
objectFit: 'contain',
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
|
2024-02-09 19:48:33 -06:00
|
|
|
<div
|
|
|
|
|
className={styles.listingShadow}
|
|
|
|
|
style={{
|
|
|
|
|
width: '100%',
|
|
|
|
|
height: '100%',
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
transition: `box-shadow ${theme.durations.regular}`,
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
|
2024-01-21 11:06:40 -06:00
|
|
|
<div
|
|
|
|
|
style={{
|
|
|
|
|
width: 256,
|
|
|
|
|
height: 128,
|
|
|
|
|
}}
|
|
|
|
|
className={styles.listingImage}
|
|
|
|
|
>
|
|
|
|
|
<img
|
|
|
|
|
src={tankIcon(tank.id)}
|
|
|
|
|
style={{
|
|
|
|
|
objectFit: 'contain',
|
|
|
|
|
objectPosition: 'left center',
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<Flex
|
|
|
|
|
align="center"
|
|
|
|
|
justify="between"
|
|
|
|
|
className={styles.listingLabel}
|
|
|
|
|
style={{
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
bottom: 8,
|
|
|
|
|
padding: '0 12px',
|
|
|
|
|
width: '100%',
|
|
|
|
|
boxSizing: 'border-box',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Flex align="center" justify="center" gap="1">
|
|
|
|
|
<img
|
|
|
|
|
src={
|
2024-02-03 15:54:21 -06:00
|
|
|
(tank.treeType === 'collector'
|
2024-01-21 11:06:40 -06:00
|
|
|
? TANK_ICONS_COLLECTOR
|
2024-02-03 15:54:21 -06:00
|
|
|
: tank.treeType === 'premium'
|
2024-01-21 11:06:40 -06:00
|
|
|
? TANK_ICONS_PREMIUM
|
|
|
|
|
: TANK_ICONS)[tank.type]
|
|
|
|
|
}
|
|
|
|
|
style={{ width: '1em', height: '1em' }}
|
|
|
|
|
/>
|
|
|
|
|
<Text
|
|
|
|
|
size="4"
|
|
|
|
|
color={
|
2024-02-03 15:54:21 -06:00
|
|
|
tank.treeType === 'collector'
|
2024-01-21 11:06:40 -06:00
|
|
|
? 'blue'
|
2024-02-03 15:54:21 -06:00
|
|
|
: tank.treeType === 'premium'
|
2024-01-21 11:06:40 -06:00
|
|
|
? 'amber'
|
|
|
|
|
: undefined
|
|
|
|
|
}
|
|
|
|
|
weight="medium"
|
|
|
|
|
style={{
|
|
|
|
|
color:
|
2024-02-03 15:54:21 -06:00
|
|
|
tank.treeType === 'researchable'
|
2024-01-21 11:06:40 -06:00
|
|
|
? slateDark.slate12
|
|
|
|
|
: undefined,
|
|
|
|
|
whiteSpace: 'nowrap',
|
|
|
|
|
maxWidth: 160,
|
|
|
|
|
display: 'block',
|
|
|
|
|
textOverflow: 'ellipsis',
|
|
|
|
|
overflow: 'hidden',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{tank.name}
|
|
|
|
|
</Text>
|
|
|
|
|
</Flex>
|
|
|
|
|
|
|
|
|
|
<Text size="4" color="gray" highContrast={false}>
|
|
|
|
|
{TIER_ROMAN_NUMERALS[tank.tier]}
|
|
|
|
|
</Text>
|
|
|
|
|
</Flex>
|
|
|
|
|
</Inset>
|
|
|
|
|
</Card>
|
|
|
|
|
))}
|
|
|
|
|
|
|
|
|
|
{compact && (
|
|
|
|
|
<CompactSearchResultRow tanks={firstChunk} onSelect={onSelect} />
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{compact && secondChunk.length > 0 && (
|
|
|
|
|
<CompactSearchResultRow tanks={secondChunk} onSelect={onSelect} />
|
|
|
|
|
)}
|
|
|
|
|
</Flex>
|
|
|
|
|
<PageTurner tanksPerPage={tanksPerPage} searchedList={searchResults} />
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|