mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
feat: some guessing mechanics
This commit is contained in:
1 parent
46441d74d8
commit
232f24181f
10 files changed
+265
-76
No files matched your search
+3
-2
@@ -40,9 +40,10 @@
|
|||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"sharp": "^0.33.5",
|
|
||||||
"googleapis": "^144.0.0",
|
"googleapis": "^144.0.0",
|
||||||
"lodash-es": "^4.17.21"
|
"lodash-es": "^4.17.21",
|
||||||
|
"quicklime": "^1.2.0",
|
||||||
|
"sharp": "^0.33.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
|
|||||||
@@ -857,7 +857,12 @@
|
|||||||
"name": "Guess the tank",
|
"name": "Guess the tank",
|
||||||
"button": "Guess away",
|
"button": "Guess away",
|
||||||
"description": "Silhouette mini-game",
|
"description": "Silhouette mini-game",
|
||||||
"seo_description": "Play a tank guessing game with a silhouette in World of Tanks Blitz"
|
"seo_description": "Play a tank guessing game with a silhouette in World of Tanks Blitz",
|
||||||
|
"search": {
|
||||||
|
"placeholder": "Type to guess...",
|
||||||
|
"button": "Guess",
|
||||||
|
"no_results": "No results"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"tankopedia": {
|
"tankopedia": {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
import { literals } from '@blitzkit/i18n/src/literals';
|
import { literals } from '@blitzkit/i18n/src/literals';
|
||||||
import { CaretLeftIcon, Link2Icon, PersonIcon } from '@radix-ui/react-icons';
|
import { CaretLeftIcon, Link2Icon, PersonIcon } from '@radix-ui/react-icons';
|
||||||
import {
|
import {
|
||||||
Badge,
|
|
||||||
Button,
|
Button,
|
||||||
Dialog,
|
Dialog,
|
||||||
Flex,
|
Flex,
|
||||||
@@ -22,6 +21,7 @@ import type { embedConfigurations } from '../../constants/embeds';
|
|||||||
import { useLocale } from '../../hooks/useLocale';
|
import { useLocale } from '../../hooks/useLocale';
|
||||||
import { EmbedState, type EmbedStateStore } from '../../stores/embedState';
|
import { EmbedState, type EmbedStateStore } from '../../stores/embedState';
|
||||||
import { CopyButton } from '../CopyButton';
|
import { CopyButton } from '../CopyButton';
|
||||||
|
import { SearchResults } from '../SearchResults';
|
||||||
|
|
||||||
interface GenerateURLProps {
|
interface GenerateURLProps {
|
||||||
embed: keyof typeof embedConfigurations;
|
embed: keyof typeof embedConfigurations;
|
||||||
@@ -118,25 +118,19 @@ export function GenerateURL({ embed }: GenerateURLProps) {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{!searching && results.length > 0 && (
|
{!searching && results.length > 0 && (
|
||||||
<Flex direction="column" gap="2" width="100%">
|
<SearchResults.Root>
|
||||||
{results.map((result) => (
|
{results.map((result) => (
|
||||||
<Button
|
<SearchResults.Item
|
||||||
variant="ghost"
|
text={result.nickname}
|
||||||
|
discriminator={strings.common.regions.short[result.region]}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCopyUser(result);
|
setCopyUser(result);
|
||||||
setCopyDialogOpen(true);
|
setCopyDialogOpen(true);
|
||||||
setSearchDialogOpen(false);
|
setSearchDialogOpen(false);
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
<Flex justify="between" width="100%" gap="2">
|
|
||||||
{result.nickname}
|
|
||||||
<Badge>
|
|
||||||
{strings.common.regions.short[result.region]}
|
|
||||||
</Badge>
|
|
||||||
</Flex>
|
|
||||||
</Button>
|
|
||||||
))}
|
))}
|
||||||
</Flex>
|
</SearchResults.Root>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import {
|
||||||
|
Badge,
|
||||||
|
Button,
|
||||||
|
Flex,
|
||||||
|
type ButtonProps,
|
||||||
|
type FlexProps,
|
||||||
|
} from '@radix-ui/themes';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
function Root(props: FlexProps) {
|
||||||
|
return <Flex direction="column" gap="2" width="100%" {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ItemProps extends ButtonProps {
|
||||||
|
text: string;
|
||||||
|
discriminator?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Item({ text, discriminator, ...props }: ItemProps) {
|
||||||
|
return (
|
||||||
|
<Button variant="ghost" {...props} highContrast>
|
||||||
|
<Flex
|
||||||
|
justify={discriminator === undefined ? 'center' : 'between'}
|
||||||
|
width="100%"
|
||||||
|
gap="2"
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
{discriminator !== undefined && <Badge>{discriminator}</Badge>}
|
||||||
|
</Flex>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SearchResults = { Root, Item };
|
||||||
+6
-4
@@ -36,6 +36,7 @@ interface ControlsProps {
|
|||||||
autoRotate?: boolean;
|
autoRotate?: boolean;
|
||||||
zoomable?: boolean;
|
zoomable?: boolean;
|
||||||
enableRotate?: boolean;
|
enableRotate?: boolean;
|
||||||
|
distanceScale?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Controls({
|
export function Controls({
|
||||||
@@ -43,6 +44,7 @@ export function Controls({
|
|||||||
autoRotate = true,
|
autoRotate = true,
|
||||||
zoomable = true,
|
zoomable = true,
|
||||||
enableRotate = true,
|
enableRotate = true,
|
||||||
|
distanceScale = 1,
|
||||||
}: ControlsProps) {
|
}: ControlsProps) {
|
||||||
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
||||||
const display = TankopediaEphemeral.use((state) => state.display);
|
const display = TankopediaEphemeral.use((state) => state.display);
|
||||||
@@ -94,11 +96,11 @@ export function Controls({
|
|||||||
protagonistHullOrigin.y +
|
protagonistHullOrigin.y +
|
||||||
protagonistTurretOrigin.y +
|
protagonistTurretOrigin.y +
|
||||||
protagonistGunOrigin.y;
|
protagonistGunOrigin.y;
|
||||||
const inspectModeInitialPosition = [
|
const inspectModeInitialPosition = new Vector3(
|
||||||
-8,
|
-8,
|
||||||
gunHeight + (naked ? 10 : 2),
|
gunHeight + (naked ? 10 : 2),
|
||||||
-13,
|
-13,
|
||||||
] as const;
|
).multiplyScalar(distanceScale);
|
||||||
const protagonistGunOriginOnlyY = new Vector3(
|
const protagonistGunOriginOnlyY = new Vector3(
|
||||||
0,
|
0,
|
||||||
protagonistTurretModelDefinition.gun_origin.y,
|
protagonistTurretModelDefinition.gun_origin.y,
|
||||||
@@ -203,7 +205,7 @@ export function Controls({
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Pose.Default: {
|
case Pose.Default: {
|
||||||
camera.position.set(...inspectModeInitialPosition);
|
camera.position.copy(inspectModeInitialPosition);
|
||||||
orbitControls.current?.target.set(0, 1.25, 0);
|
orbitControls.current?.target.set(0, 1.25, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -312,7 +314,7 @@ export function Controls({
|
|||||||
.add(shellOrigin);
|
.add(shellOrigin);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
camera.position.set(...inspectModeInitialPosition);
|
camera.position.copy(inspectModeInitialPosition);
|
||||||
orbitControls.current.target.set(0, gunHeight / (naked ? 4 : 2), 0);
|
orbitControls.current.target.set(0, gunHeight / (naked ? 4 : 2), 0);
|
||||||
orbitControls.current.enablePan = true;
|
orbitControls.current.enablePan = true;
|
||||||
orbitControls.current.enableZoom = true;
|
orbitControls.current.enableZoom = true;
|
||||||
|
|||||||
@@ -3,9 +3,14 @@ import { awaitableModelDefinitions } from '../../../../core/awaitables/modelDefi
|
|||||||
import { awaitableProvisionDefinitions } from '../../../../core/awaitables/provisionDefinitions';
|
import { awaitableProvisionDefinitions } from '../../../../core/awaitables/provisionDefinitions';
|
||||||
import { awaitableTankDefinitions } from '../../../../core/awaitables/tankDefinitions';
|
import { awaitableTankDefinitions } from '../../../../core/awaitables/tankDefinitions';
|
||||||
import { Var } from '../../../../core/radix/var';
|
import { Var } from '../../../../core/radix/var';
|
||||||
|
import {
|
||||||
|
LocaleProvider,
|
||||||
|
type LocaleAcceptorProps,
|
||||||
|
} from '../../../../hooks/useLocale';
|
||||||
import { Duel } from '../../../../stores/duel';
|
import { Duel } from '../../../../stores/duel';
|
||||||
import { TankopediaEphemeral } from '../../../../stores/tankopediaEphemeral';
|
import { TankopediaEphemeral } from '../../../../stores/tankopediaEphemeral';
|
||||||
import { GuessRenderer } from './components/GuessRenderer';
|
import { GuessRenderer } from './components/GuessRenderer';
|
||||||
|
import { Guesser } from './components/Guesser';
|
||||||
|
|
||||||
const [tankDefinitions, modelDefinitions, provisionDefinitions] =
|
const [tankDefinitions, modelDefinitions, provisionDefinitions] =
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@@ -16,17 +21,19 @@ const [tankDefinitions, modelDefinitions, provisionDefinitions] =
|
|||||||
|
|
||||||
const ids = Object.keys(tankDefinitions.tanks);
|
const ids = Object.keys(tankDefinitions.tanks);
|
||||||
|
|
||||||
export function Page() {
|
export function Page({ locale }: LocaleAcceptorProps) {
|
||||||
const id = Number(ids[Math.floor(Math.random() * ids.length)]);
|
const id = Number(ids[Math.floor(Math.random() * ids.length)]);
|
||||||
const tank = tankDefinitions.tanks[id];
|
const tank = tankDefinitions.tanks[id];
|
||||||
const model = modelDefinitions.models[id];
|
const model = modelDefinitions.models[id];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TankopediaEphemeral.Provider data={model}>
|
<LocaleProvider locale={locale}>
|
||||||
<Duel.Provider data={{ tank, model, provisionDefinitions }}>
|
<TankopediaEphemeral.Provider data={model}>
|
||||||
<Content />
|
<Duel.Provider data={{ tank, model, provisionDefinitions }}>
|
||||||
</Duel.Provider>
|
<Content />
|
||||||
</TankopediaEphemeral.Provider>
|
</Duel.Provider>
|
||||||
|
</TankopediaEphemeral.Provider>
|
||||||
|
</LocaleProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,54 +46,31 @@ function Content() {
|
|||||||
backgroundColor: Var('cyan-3'),
|
backgroundColor: Var('cyan-3'),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex flexGrow="2" position="relative">
|
<Box
|
||||||
<Box
|
position="absolute"
|
||||||
position="absolute"
|
width="100%"
|
||||||
width="100%"
|
height="100%"
|
||||||
height="100%"
|
top="0"
|
||||||
top="0"
|
left="0"
|
||||||
left="0"
|
style={{
|
||||||
style={
|
backgroundImage: `
|
||||||
{
|
|
||||||
// backgroundImage:
|
|
||||||
// 'url(https://images.pexels.com/photos/3052361/pexels-photo-3052361.jpeg)',
|
|
||||||
// backgroundSize: 'cover',
|
|
||||||
// backgroundPosition: 'center',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
position="absolute"
|
|
||||||
width="100%"
|
|
||||||
height="100%"
|
|
||||||
top="0"
|
|
||||||
left="0"
|
|
||||||
style={{
|
|
||||||
backgroundImage: `
|
|
||||||
radial-gradient(circle, ${Var('white-a1')} 20%, transparent 20%),
|
radial-gradient(circle, ${Var('white-a1')} 20%, transparent 20%),
|
||||||
radial-gradient(circle at 0 0, ${Var('white-a1')} 10%, transparent 10%),
|
radial-gradient(circle at 0 0, ${Var('white-a1')} 10%, transparent 10%),
|
||||||
radial-gradient(circle at 100% 0, ${Var('white-a1')} 10%, transparent 10%),
|
radial-gradient(circle at 100% 0, ${Var('white-a1')} 10%, transparent 10%),
|
||||||
radial-gradient(circle at 0 100%, ${Var('white-a1')} 10%, transparent 10%),
|
radial-gradient(circle at 0 100%, ${Var('white-a1')} 10%, transparent 10%),
|
||||||
radial-gradient(circle at 100% 100%, ${Var('white-a1')} 10%, transparent 10%)
|
radial-gradient(circle at 100% 100%, ${Var('white-a1')} 10%, transparent 10%)
|
||||||
`,
|
`,
|
||||||
backgroundSize: '4rem 4rem',
|
backgroundSize: '4rem 4rem',
|
||||||
// background: `radial-gradient(circle, ${Var('cyan-a1')} 15%, ${Var('cyan-a3')}, ${Var('cyan-a1')}, ${Var('gray-1')} 50%)`,
|
// background: `radial-gradient(circle, ${Var('cyan-a1')} 15%, ${Var('cyan-a3')}, ${Var('cyan-a1')}, ${Var('gray-1')} 50%)`,
|
||||||
// background: `linear-gradient(90deg, ${Var('black-a3')}, ${Var('black-a9')} 66%)`,
|
// background: `linear-gradient(90deg, ${Var('black-a3')}, ${Var('black-a9')} 66%)`,
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<GuessRenderer />
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex
|
|
||||||
flexGrow="1"
|
|
||||||
style={{
|
|
||||||
background: Var('color-panel-solid'),
|
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
b
|
|
||||||
</Flex>
|
<Box position="absolute" width="100%" height="100%" top="0" left="0">
|
||||||
|
<GuessRenderer />
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Guesser />
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Canvas, invalidate } from '@react-three/fiber';
|
import { Canvas, invalidate } from '@react-three/fiber';
|
||||||
|
import { type QuicklimeCallback, Quicklime } from 'quicklime';
|
||||||
import { useCallback, useEffect, useRef } from 'react';
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
import { Fog } from 'three';
|
import { Fog } from 'three';
|
||||||
import { Controls } from '../../../../../components/Tankopedia/HeroSection/components/TankSandbox/components/Control';
|
import { Controls } from '../../../../../components/Tankopedia/HeroSection/components/TankSandbox/components/Control';
|
||||||
@@ -7,15 +8,17 @@ import { TankModel } from '../../../../../components/Tankopedia/HeroSection/comp
|
|||||||
import { Var } from '../../../../../core/radix/var';
|
import { Var } from '../../../../../core/radix/var';
|
||||||
import { TankopediaDisplay } from '../../../../../stores/tankopediaPersistent/constants';
|
import { TankopediaDisplay } from '../../../../../stores/tankopediaPersistent/constants';
|
||||||
|
|
||||||
const nearConcealed = -40;
|
const nearConcealed = -45;
|
||||||
const farConcealed = 22;
|
const farConcealed = 22;
|
||||||
const nearRevealed = 20;
|
const nearRevealed = 25;
|
||||||
const farRevealed = 22;
|
const farRevealed = 25;
|
||||||
const animationDuration = 1;
|
const animationDuration = 1;
|
||||||
|
|
||||||
|
export const revealEvent = new Quicklime<boolean>();
|
||||||
|
|
||||||
export function GuessRenderer() {
|
export function GuessRenderer() {
|
||||||
const fog = useRef(new Fog('black', nearConcealed, farConcealed));
|
const fog = useRef(new Fog('black', nearConcealed, farConcealed));
|
||||||
const reveal = useCallback(() => {
|
const reveal = useCallback((show: boolean) => {
|
||||||
const t0 = Date.now() / 1000;
|
const t0 = Date.now() / 1000;
|
||||||
|
|
||||||
function frame() {
|
function frame() {
|
||||||
@@ -30,14 +33,27 @@ export function GuessRenderer() {
|
|||||||
|
|
||||||
invalidate();
|
invalidate();
|
||||||
|
|
||||||
if (y < 1) requestAnimationFrame(frame);
|
if (y < 1) {
|
||||||
|
requestAnimationFrame(frame);
|
||||||
|
} else {
|
||||||
|
fog.current.near = nearRevealed;
|
||||||
|
fog.current.far = farRevealed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
requestAnimationFrame(frame);
|
requestAnimationFrame(frame);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(reveal, 2000);
|
const handleRevealEvent: QuicklimeCallback<boolean> = ({ data }) => {
|
||||||
|
reveal(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
revealEvent.on(handleRevealEvent);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
revealEvent.off(handleRevealEvent);
|
||||||
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -48,7 +64,7 @@ export function GuessRenderer() {
|
|||||||
camera={{ position: [-5, 5, -5] }}
|
camera={{ position: [-5, 5, -5] }}
|
||||||
scene={{ fog: fog.current }}
|
scene={{ fog: fog.current }}
|
||||||
>
|
>
|
||||||
<Controls zoomable={false} />
|
<Controls distanceScale={2} zoomable={false} />
|
||||||
<Lighting display={TankopediaDisplay.Model} />
|
<Lighting display={TankopediaDisplay.Model} />
|
||||||
<TankModel />
|
<TankModel />
|
||||||
</Canvas>
|
</Canvas>
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
import {
|
||||||
|
SEARCH_KEYS,
|
||||||
|
TankDefinition,
|
||||||
|
TIER_ROMAN_NUMERALS,
|
||||||
|
} from '@blitzkit/core';
|
||||||
|
import { MagnifyingGlassIcon, PaperPlaneIcon } from '@radix-ui/react-icons';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Flex,
|
||||||
|
Spinner,
|
||||||
|
Text,
|
||||||
|
TextField,
|
||||||
|
} from '@radix-ui/themes';
|
||||||
|
import fuzzysort from 'fuzzysort';
|
||||||
|
import { debounce } from 'lodash-es';
|
||||||
|
import { useCallback, useRef, useState } from 'react';
|
||||||
|
import { classIcons } from '../../../../../components/ClassIcon';
|
||||||
|
import { SearchResults } from '../../../../../components/SearchResults';
|
||||||
|
import { awaitableTankDefinitions } from '../../../../../core/awaitables/tankDefinitions';
|
||||||
|
import { awaitableTankNames } from '../../../../../core/awaitables/tankNames';
|
||||||
|
import { useLocale } from '../../../../../hooks/useLocale';
|
||||||
|
import { revealEvent } from './GuessRenderer';
|
||||||
|
|
||||||
|
const { go } = fuzzysort;
|
||||||
|
|
||||||
|
const [tankNames, tankDefinitions] = await Promise.all([
|
||||||
|
awaitableTankNames,
|
||||||
|
awaitableTankDefinitions,
|
||||||
|
]);
|
||||||
|
|
||||||
|
export function Guesser() {
|
||||||
|
const { strings, unwrap } = useLocale();
|
||||||
|
const input = useRef<HTMLInputElement>(null);
|
||||||
|
const [searching, setSearching] = useState(false);
|
||||||
|
const [results, setResults] = useState<TankDefinition[] | null>(null);
|
||||||
|
const [selected, setSelected] = useState<TankDefinition | null>(null);
|
||||||
|
|
||||||
|
const requestSearch = useCallback(() => {
|
||||||
|
setSelected(null);
|
||||||
|
setSearching(true);
|
||||||
|
search();
|
||||||
|
}, []);
|
||||||
|
const search = useCallback(
|
||||||
|
debounce(() => {
|
||||||
|
if (!input.current) return;
|
||||||
|
|
||||||
|
setSearching(false);
|
||||||
|
const trimmed = input.current.value.trim();
|
||||||
|
|
||||||
|
if (trimmed.length === 0) {
|
||||||
|
setResults(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchResults = go(trimmed, tankNames, {
|
||||||
|
keys: SEARCH_KEYS,
|
||||||
|
limit: 6,
|
||||||
|
});
|
||||||
|
|
||||||
|
setResults(
|
||||||
|
searchResults.map((result) => tankDefinitions.tanks[result.obj.id]),
|
||||||
|
);
|
||||||
|
}, 500),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
direction="column"
|
||||||
|
position="absolute"
|
||||||
|
bottom="0"
|
||||||
|
left="50%"
|
||||||
|
style={{ transform: 'translateX(-50%)' }}
|
||||||
|
width="100%"
|
||||||
|
p="4"
|
||||||
|
maxWidth="25rem"
|
||||||
|
gap="4"
|
||||||
|
>
|
||||||
|
{results !== null && (
|
||||||
|
<Card variant="classic">
|
||||||
|
<Box py="2" px="3">
|
||||||
|
{results.length === 0 && (
|
||||||
|
<Flex justify="center">
|
||||||
|
<Text color="gray">
|
||||||
|
{strings.website.tools.guess.search.no_results}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<SearchResults.Root>
|
||||||
|
{results.map((result) => {
|
||||||
|
const Icon = classIcons[result.class];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SearchResults.Item
|
||||||
|
onClick={() => {
|
||||||
|
if (!input.current) return;
|
||||||
|
|
||||||
|
input.current.value = unwrap(result.name);
|
||||||
|
setSelected(result);
|
||||||
|
setResults(null);
|
||||||
|
}}
|
||||||
|
text={unwrap(result.name)}
|
||||||
|
discriminator={
|
||||||
|
<Flex
|
||||||
|
align="center"
|
||||||
|
gap="1"
|
||||||
|
width="38px"
|
||||||
|
justify="center"
|
||||||
|
>
|
||||||
|
<Icon width="1em" height="1em" />
|
||||||
|
{TIER_ROMAN_NUMERALS[result.tier]}
|
||||||
|
</Flex>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</SearchResults.Root>
|
||||||
|
</Box>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Flex gap="3">
|
||||||
|
<TextField.Root
|
||||||
|
ref={input}
|
||||||
|
onChange={requestSearch}
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
placeholder={strings.website.tools.guess.search.placeholder}
|
||||||
|
size="3"
|
||||||
|
variant="classic"
|
||||||
|
>
|
||||||
|
<TextField.Slot>
|
||||||
|
{searching ? <Spinner /> : <MagnifyingGlassIcon />}
|
||||||
|
</TextField.Slot>
|
||||||
|
</TextField.Root>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
size="3"
|
||||||
|
disabled={selected === null}
|
||||||
|
onClick={() => {
|
||||||
|
revealEvent.dispatch(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{strings.website.tools.guess.search.button} <PaperPlaneIcon />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -4,10 +4,12 @@ import BaseLayout from '../../../../layouts/BaseLayout.astro';
|
|||||||
import { Page } from './_index';
|
import { Page } from './_index';
|
||||||
|
|
||||||
export { getStaticPaths } from '../../index.astro';
|
export { getStaticPaths } from '../../index.astro';
|
||||||
|
|
||||||
|
const { locale } = Astro.params;
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout noFooter>
|
<BaseLayout noFooter>
|
||||||
<PageWrapper p="0" maxWidth="unset">
|
<PageWrapper p="0" maxWidth="unset" color="cyan">
|
||||||
<Page client:load />
|
<Page {locale} client:load />
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
Reference in new issue
Block a user