mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-26 20:19:50 +01:00
feat: nerd time
This commit is contained in:
1 parent
b409e26ca5
commit
ad8cbb96b7
5 files changed
+85
-172
No files matched your search
@@ -90,10 +90,11 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
|
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
|
||||||
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
||||||
const { strings, unwrap } = useLocale();
|
const { strings, unwrap } = useLocale();
|
||||||
|
const disturbed = TankopediaEphemeral.use((state) => state.disturbed);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<QuickInputs />
|
{disturbed && <QuickInputs />}
|
||||||
|
|
||||||
{display === TankopediaDisplay.ShootingRange && (
|
{display === TankopediaDisplay.ShootingRange && (
|
||||||
<Box
|
<Box
|
||||||
@@ -323,9 +324,9 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
direction="column"
|
direction="column"
|
||||||
align="center"
|
align="center"
|
||||||
position="absolute"
|
position="absolute"
|
||||||
bottom="4"
|
bottom={disturbed ? '4' : '-100%'}
|
||||||
left="50%"
|
left="50%"
|
||||||
style={{ transform: 'translateX(-50%)' }}
|
style={{ transform: 'translateX(-50%)', transitionDuration: '200ms' }}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
|
|||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
import { TankType } from '@blitzkit/core';
|
||||||
|
import { Heading } from '@radix-ui/themes';
|
||||||
|
import { clamp } from 'three/src/math/MathUtils.js';
|
||||||
|
import { Var, type VarName } from '../../../../../core/radix/var';
|
||||||
|
import { useLocale } from '../../../../../hooks/useLocale';
|
||||||
|
import { Duel } from '../../../../../stores/duel';
|
||||||
|
import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral';
|
||||||
|
|
||||||
|
interface TitleProps {
|
||||||
|
outline?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Title({ outline }: TitleProps) {
|
||||||
|
const { unwrap } = useLocale();
|
||||||
|
const protagonist = Duel.use((state) => state.protagonist.tank);
|
||||||
|
const disturbed = TankopediaEphemeral.use((state) => state.disturbed);
|
||||||
|
const revealed = TankopediaEphemeral.use((state) => state.revealed);
|
||||||
|
const treeColor =
|
||||||
|
protagonist.type === TankType.COLLECTOR
|
||||||
|
? 'blue'
|
||||||
|
: protagonist.type === TankType.PREMIUM
|
||||||
|
? 'amber'
|
||||||
|
: undefined;
|
||||||
|
const name = unwrap(protagonist.name);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Heading
|
||||||
|
weight="bold"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
marginLeft: disturbed ? '2rem' : 0,
|
||||||
|
left: disturbed ? 0 : '50%',
|
||||||
|
transform: disturbed ? 'translate(0, -50%)' : 'translate(-50%, -50%)',
|
||||||
|
userSelect: 'none',
|
||||||
|
pointerEvents: 'none',
|
||||||
|
transitionDuration: '1s',
|
||||||
|
fontSize: disturbed
|
||||||
|
? '3rem'
|
||||||
|
: `${16 * clamp(12 / name.length, 0, 1)}rem`,
|
||||||
|
color: outline ? Var('gray-a7') : undefined,
|
||||||
|
WebkitTextStroke: outline
|
||||||
|
? `${disturbed ? '1px' : '2px'} ${Var(
|
||||||
|
`${treeColor ? (`${treeColor}-11` satisfies VarName) : 'gray-12'}`,
|
||||||
|
)}`
|
||||||
|
: undefined,
|
||||||
|
letterSpacing: disturbed ? 0 : '-0.06em',
|
||||||
|
}}
|
||||||
|
color={treeColor}
|
||||||
|
wrap="nowrap"
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</Heading>
|
||||||
|
);
|
||||||
|
}
|
||||||
+2
-2
@@ -128,8 +128,8 @@ export function Controls({
|
|||||||
|
|
||||||
camera.position
|
camera.position
|
||||||
.copy(inspectModeInitialPosition)
|
.copy(inspectModeInitialPosition)
|
||||||
.applyAxisAngle(I_HAT, (Math.PI / 16) * Math.sin(t / 9))
|
.applyAxisAngle(I_HAT, (Math.PI / 32) * Math.sin(t / 9))
|
||||||
.applyAxisAngle(J_HAT, (-Math.PI / 4) * Math.sin(t / 7));
|
.applyAxisAngle(J_HAT, (-Math.PI / 16) * Math.sin(t / 7));
|
||||||
|
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -30,7 +30,6 @@ import { AutoClear } from './components/AutoClear';
|
|||||||
import { Controls } from './components/Control';
|
import { Controls } from './components/Control';
|
||||||
import { InitialFogReveal } from './components/InitialFogReveal';
|
import { InitialFogReveal } from './components/InitialFogReveal';
|
||||||
import { Lighting } from './components/Lighting';
|
import { Lighting } from './components/Lighting';
|
||||||
import { SceneProps } from './components/SceneProps';
|
|
||||||
import { TankModel } from './components/TankModel';
|
import { TankModel } from './components/TankModel';
|
||||||
import { TransitionSkeleton } from './components/TransitionSkeleton';
|
import { TransitionSkeleton } from './components/TransitionSkeleton';
|
||||||
|
|
||||||
@@ -234,7 +233,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
outline: naked ? '1rem red solid' : undefined,
|
outline: naked ? '1rem red solid' : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!naked && <SceneProps />}
|
{/* {!naked && <SceneProps />} */}
|
||||||
{(display === TankopediaDisplay.Model ||
|
{(display === TankopediaDisplay.Model ||
|
||||||
(display === TankopediaDisplay.DynamicArmor &&
|
(display === TankopediaDisplay.DynamicArmor &&
|
||||||
!hideTankModelUnderArmor)) && <TankModel />}
|
!hideTankModelUnderArmor)) && <TankModel />}
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
import { assertSecret, TankType } from '@blitzkit/core';
|
import { TankType } from '@blitzkit/core';
|
||||||
import { CaretLeftIcon, MixIcon, UpdateIcon } from '@radix-ui/react-icons';
|
import { Box, Flex } from '@radix-ui/themes';
|
||||||
import { Box, Dialog, Flex, Heading, Link, Spinner } from '@radix-ui/themes';
|
|
||||||
import { times } from 'lodash-es';
|
import { times } from 'lodash-es';
|
||||||
import { Suspense, useEffect, useMemo, useRef, useState } from 'react';
|
import { Suspense, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { NAVBAR_HEIGHT } from '../../../constants/navbar';
|
import { NAVBAR_HEIGHT } from '../../../constants/navbar';
|
||||||
import { awaitableModelDefinitions } from '../../../core/awaitables/modelDefinitions';
|
import { awaitableModelDefinitions } from '../../../core/awaitables/modelDefinitions';
|
||||||
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 { tankToDuelMember } from '../../../core/blitzkit/tankToDuelMember';
|
|
||||||
import { Var } from '../../../core/radix/var';
|
|
||||||
import { useFullScreen } from '../../../hooks/useFullScreen';
|
import { useFullScreen } from '../../../hooks/useFullScreen';
|
||||||
import { useLocale } from '../../../hooks/useLocale';
|
import { useLocale } from '../../../hooks/useLocale';
|
||||||
import { Duel } from '../../../stores/duel';
|
import { Duel } from '../../../stores/duel';
|
||||||
@@ -16,10 +13,9 @@ import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
|
|||||||
import type { MaybeSkeletonComponentProps } from '../../../types/maybeSkeletonComponentProps';
|
import type { MaybeSkeletonComponentProps } from '../../../types/maybeSkeletonComponentProps';
|
||||||
import type { ThicknessRange } from '../../Armor/components/StaticArmor';
|
import type { ThicknessRange } from '../../Armor/components/StaticArmor';
|
||||||
import { classIcons } from '../../ClassIcon';
|
import { classIcons } from '../../ClassIcon';
|
||||||
import { ScienceIcon } from '../../ScienceIcon';
|
|
||||||
import { TankSearch } from '../../TankSearch';
|
|
||||||
import { Options } from './components/Options';
|
import { Options } from './components/Options';
|
||||||
import { TankSandbox } from './components/TankSandbox';
|
import { TankSandbox } from './components/TankSandbox';
|
||||||
|
import { Title } from './components/TankSandbox/Title';
|
||||||
|
|
||||||
const [provisionDefinitions, modelDefinitions, tankDefinitions] =
|
const [provisionDefinitions, modelDefinitions, tankDefinitions] =
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@@ -110,6 +106,8 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
top={isFullScreen ? '0' : undefined}
|
top={isFullScreen ? '0' : undefined}
|
||||||
left={isFullScreen ? '0' : undefined}
|
left={isFullScreen ? '0' : undefined}
|
||||||
>
|
>
|
||||||
|
<Title />
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
className="tank-sandbox-container"
|
className="tank-sandbox-container"
|
||||||
flexGrow="1"
|
flexGrow="1"
|
||||||
@@ -122,10 +120,11 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
width="100%"
|
width="100%"
|
||||||
height="100%"
|
height="100%"
|
||||||
position="relative"
|
position="relative"
|
||||||
left={disturbed ? '0' : { initial: '0', md: '12.5%' }}
|
style={{
|
||||||
style={{ transitionDuration: '200ms' }}
|
transitionDuration: '1s',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{!revealed && (
|
{/* {!revealed && (
|
||||||
<Spinner
|
<Spinner
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@@ -136,13 +135,22 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
height: Var('space-6'),
|
height: Var('space-6'),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)} */}
|
||||||
|
|
||||||
{/* {skeleton && <TankSandboxLoader id={protagonist.id} />} */}
|
{/* {skeleton && <TankSandboxLoader id={protagonist.id} />} */}
|
||||||
|
|
||||||
<Suspense>
|
<Box
|
||||||
<TankSandbox ref={canvas} thicknessRange={thicknessRange} />
|
style={{
|
||||||
</Suspense>
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
transitionDuration: '2s',
|
||||||
|
opacity: revealed ? 1 : 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Suspense>
|
||||||
|
<TankSandbox ref={canvas} thicknessRange={thicknessRange} />
|
||||||
|
</Suspense>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Options
|
<Options
|
||||||
@@ -153,157 +161,7 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Flex
|
<Title outline />
|
||||||
className="tankopedia-title-bar"
|
|
||||||
justify="center"
|
|
||||||
position={{ initial: 'static', md: 'absolute' }}
|
|
||||||
left={{
|
|
||||||
initial: '0',
|
|
||||||
md: disturbed ? '3' : '5',
|
|
||||||
lg: disturbed ? '7' : '9',
|
|
||||||
}}
|
|
||||||
pt="7"
|
|
||||||
pb="4"
|
|
||||||
top={{ initial: '8', md: '50%' }}
|
|
||||||
mt={{ initial: '0', md: '-9' }}
|
|
||||||
style={{
|
|
||||||
transitionDuration: '200ms',
|
|
||||||
userSelect: 'none',
|
|
||||||
// background: 'red',
|
|
||||||
}}
|
|
||||||
direction="column"
|
|
||||||
align="center"
|
|
||||||
gap="2"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
align="center"
|
|
||||||
gap="3"
|
|
||||||
width="100%"
|
|
||||||
justify={{ initial: 'center', md: 'start' }}
|
|
||||||
>
|
|
||||||
<Heading
|
|
||||||
color={treeColor}
|
|
||||||
trim="end"
|
|
||||||
size={
|
|
||||||
disturbed
|
|
||||||
? { initial: '6', lg: '7' }
|
|
||||||
: { initial: '7', lg: '8' }
|
|
||||||
}
|
|
||||||
style={{ transitionDuration: '200ms', position: 'relative' }}
|
|
||||||
>
|
|
||||||
<Icon width="1em" height="1em" />
|
|
||||||
</Heading>
|
|
||||||
|
|
||||||
<Heading
|
|
||||||
weight="medium"
|
|
||||||
size={
|
|
||||||
disturbed
|
|
||||||
? { initial: '7', lg: '8' }
|
|
||||||
: { initial: '8', lg: '9' }
|
|
||||||
}
|
|
||||||
style={{ transitionDuration: '200ms' }}
|
|
||||||
wrap="nowrap"
|
|
||||||
color={treeColor}
|
|
||||||
>
|
|
||||||
{unwrap(protagonist.name)}
|
|
||||||
</Heading>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex
|
|
||||||
gap="4"
|
|
||||||
ml={disturbed ? '0' : '-2'}
|
|
||||||
style={{ transitionDuration: '200ms' }}
|
|
||||||
>
|
|
||||||
<Link color="gray" underline="always" href="/tools/tankopedia">
|
|
||||||
<Flex align="center" gap="1">
|
|
||||||
<CaretLeftIcon />
|
|
||||||
{strings.website.tools.tankopedia.meta.back}
|
|
||||||
</Flex>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
color="gray"
|
|
||||||
underline="always"
|
|
||||||
href={`/tools/compare?tanks=${compareTanks.join('%2C')}`}
|
|
||||||
>
|
|
||||||
<Flex align="center" gap="1">
|
|
||||||
<MixIcon />
|
|
||||||
{strings.website.tools.tankopedia.meta.compare}
|
|
||||||
</Flex>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Dialog.Root open={showSwapDialog} onOpenChange={setShowSwapDialog}>
|
|
||||||
<Dialog.Trigger>
|
|
||||||
<Link
|
|
||||||
color="gray"
|
|
||||||
underline="always"
|
|
||||||
href="#"
|
|
||||||
onClick={(event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
setShowSwapDialog(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Flex align="center" gap="1">
|
|
||||||
<UpdateIcon />
|
|
||||||
{strings.website.tools.tankopedia.meta.swap.button}
|
|
||||||
</Flex>
|
|
||||||
</Link>
|
|
||||||
</Dialog.Trigger>
|
|
||||||
|
|
||||||
<Dialog.Content>
|
|
||||||
<Dialog.Title>
|
|
||||||
{strings.website.tools.tankopedia.meta.swap.title}
|
|
||||||
</Dialog.Title>
|
|
||||||
|
|
||||||
<TankSearch
|
|
||||||
compact
|
|
||||||
onSelect={(tank) => {
|
|
||||||
const model = modelDefinitions.models[tank.id];
|
|
||||||
|
|
||||||
setShowSwapDialog(false);
|
|
||||||
mutateTankopediaEphemeral((draft) => {
|
|
||||||
draft.model = modelDefinitions.models[tank.id];
|
|
||||||
});
|
|
||||||
mutateDuel((draft) => {
|
|
||||||
draft.protagonist = tankToDuelMember(
|
|
||||||
tank,
|
|
||||||
model,
|
|
||||||
provisionDefinitions,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
window.history.replaceState(
|
|
||||||
null,
|
|
||||||
'',
|
|
||||||
`/tools/tankopedia/${tank.id}`,
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Dialog.Content>
|
|
||||||
</Dialog.Root>
|
|
||||||
|
|
||||||
{assertSecret(import.meta.env.PUBLIC_PROMOTE_OPENTEST) ===
|
|
||||||
'true' && (
|
|
||||||
<Link
|
|
||||||
color="jade"
|
|
||||||
href={`https://${
|
|
||||||
assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH) ===
|
|
||||||
'opentest'
|
|
||||||
? ''
|
|
||||||
: 'opentest.'
|
|
||||||
}blitzkit.app/tools/tankopedia/${protagonist.id}`}
|
|
||||||
>
|
|
||||||
<Flex align="center" gap="1">
|
|
||||||
<ScienceIcon width="1.25em" height="1.25em" />
|
|
||||||
{assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH) ===
|
|
||||||
'opentest'
|
|
||||||
? strings.website.tools.tankopedia.meta.vanilla
|
|
||||||
: strings.website.tools.tankopedia.meta.opentest}
|
|
||||||
</Flex>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in new issue
Block a user