mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-22 03:07:04 +02:00
feat: upgrade to radix 3.0
This commit is contained in:
1 parent
194e5fceca
commit
cc6de50de6
13 files changed
+373
-332
No files matched your search
+1
-2
@@ -15,11 +15,10 @@ export default function Page() {
|
||||
|
||||
return (
|
||||
<PageWrapper>
|
||||
<TextField.Root>
|
||||
<TextField.Root placeholder="Search tools...">
|
||||
<TextField.Slot>
|
||||
<MagnifyingGlassIcon height="16" width="16" />
|
||||
</TextField.Slot>
|
||||
<TextField.Input ref={input} placeholder="Search tools..." />
|
||||
</TextField.Root>
|
||||
|
||||
<Flex gap="3" wrap="wrap" align="center" justify="center">
|
||||
|
||||
@@ -53,29 +53,26 @@ export function PageTurner({
|
||||
<CaretLeftIcon />
|
||||
)}
|
||||
</Button>
|
||||
<TextField.Root>
|
||||
|
||||
<TextField.Root
|
||||
type="number"
|
||||
ref={pageInput}
|
||||
style={{ textAlign: 'center' }}
|
||||
min={1}
|
||||
max={pages}
|
||||
onBlur={(event) => {
|
||||
onPageChange(
|
||||
Math.max(0, Math.min(pages - 1, event.target.valueAsNumber - 1)),
|
||||
false,
|
||||
);
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
(event.target as HTMLInputElement).blur();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<TextField.Slot>Page</TextField.Slot>
|
||||
<TextField.Input
|
||||
type="number"
|
||||
ref={pageInput}
|
||||
style={{ width: 64, textAlign: 'center' }}
|
||||
min={1}
|
||||
max={pages}
|
||||
onBlur={(event) => {
|
||||
onPageChange(
|
||||
Math.max(
|
||||
0,
|
||||
Math.min(pages - 1, event.target.valueAsNumber - 1),
|
||||
),
|
||||
false,
|
||||
);
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
(event.target as HTMLInputElement).blur();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<TextField.Slot>out of {pages}</TextField.Slot>
|
||||
</TextField.Root>
|
||||
<Button
|
||||
|
||||
@@ -605,7 +605,7 @@ export default function Page() {
|
||||
|
||||
<Dialog.Content>
|
||||
<Flex gap="4" justify="center">
|
||||
<TextField.Input
|
||||
<TextField.Root
|
||||
ref={scoreInput}
|
||||
type="number"
|
||||
placeholder="Type a score..."
|
||||
@@ -638,7 +638,7 @@ export default function Page() {
|
||||
|
||||
<Dialog.Content>
|
||||
<Flex gap="4" justify="center">
|
||||
<TextField.Input
|
||||
<TextField.Root
|
||||
ref={positionInput}
|
||||
type="number"
|
||||
placeholder="Type a position..."
|
||||
@@ -759,105 +759,100 @@ export default function Page() {
|
||||
|
||||
<Dialog.Content>
|
||||
<Flex gap="4" direction="column">
|
||||
<TextField.Root>
|
||||
<TextField.Root
|
||||
onChange={debounce(
|
||||
async (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const trimmedSearch = event.target.value.trim();
|
||||
const encodedSearch = encodeURIComponent(trimmedSearch);
|
||||
|
||||
if (trimmedSearch) {
|
||||
if (season === 0) {
|
||||
setSearchLoading(true);
|
||||
|
||||
const accountList = await searchCurrentRatingPlayers(
|
||||
region,
|
||||
trimmedSearch,
|
||||
);
|
||||
const values = Object.values(accountList).filter(
|
||||
(searchedPlayer) => !searchedPlayer.skip,
|
||||
);
|
||||
const valuesWithTypes =
|
||||
values as ((typeof values)[number] & {
|
||||
skip: false;
|
||||
})[];
|
||||
|
||||
useLeaderboardCache.setState(
|
||||
produce((draft: LeaderboardCache) => {
|
||||
if (draft) {
|
||||
values.forEach((player) => {
|
||||
const playerWithTypes =
|
||||
player as typeof player & { skip: false };
|
||||
|
||||
draft[region][season][
|
||||
playerWithTypes.number - 1
|
||||
] = playerWithTypes.spa_id;
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
useNameCache.setState(
|
||||
produce((draft: typeof names) => {
|
||||
valuesWithTypes.forEach((searchedPlayer) => {
|
||||
if (searchedPlayer.nickname) {
|
||||
draft[region][searchedPlayer.spa_id] = {
|
||||
nickname: searchedPlayer.nickname,
|
||||
clan: searchedPlayer.clan_tag,
|
||||
};
|
||||
}
|
||||
});
|
||||
}),
|
||||
);
|
||||
setSearchResults(
|
||||
valuesWithTypes.map((searchedPlayer) => ({
|
||||
account_id: searchedPlayer.spa_id,
|
||||
nickname: searchedPlayer.nickname,
|
||||
number: searchedPlayer.number,
|
||||
})),
|
||||
);
|
||||
setSearchLoading(false);
|
||||
} else {
|
||||
setSearchLoading(true);
|
||||
const accountList = await fetchBlitz<AccountList>(
|
||||
`https://api.wotblitz.${region}/wotb/account/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${encodedSearch}`,
|
||||
);
|
||||
const playerKeys = Object.keys(
|
||||
leaderboard[region][season],
|
||||
);
|
||||
|
||||
setSearchResults(
|
||||
accountList
|
||||
.map((searchedPlayer) => ({
|
||||
...searchedPlayer,
|
||||
number: parseInt(
|
||||
playerKeys.find(
|
||||
(indexAsString) =>
|
||||
leaderboard[region][season][
|
||||
parseInt(indexAsString)
|
||||
] === searchedPlayer.account_id,
|
||||
)!,
|
||||
),
|
||||
}))
|
||||
.filter((player) => !Number.isNaN(player.number)),
|
||||
);
|
||||
|
||||
setSearchLoading(false);
|
||||
}
|
||||
} else {
|
||||
setSearchResults(undefined);
|
||||
}
|
||||
},
|
||||
500,
|
||||
)}
|
||||
placeholder="Search for player..."
|
||||
>
|
||||
<TextField.Slot>
|
||||
<MagnifyingGlassIcon height="16" width="16" />
|
||||
</TextField.Slot>
|
||||
|
||||
<TextField.Input
|
||||
onChange={debounce(
|
||||
async (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const trimmedSearch = event.target.value.trim();
|
||||
const encodedSearch = encodeURIComponent(trimmedSearch);
|
||||
|
||||
if (trimmedSearch) {
|
||||
if (season === 0) {
|
||||
setSearchLoading(true);
|
||||
|
||||
const accountList =
|
||||
await searchCurrentRatingPlayers(
|
||||
region,
|
||||
trimmedSearch,
|
||||
);
|
||||
const values = Object.values(accountList).filter(
|
||||
(searchedPlayer) => !searchedPlayer.skip,
|
||||
);
|
||||
const valuesWithTypes =
|
||||
values as ((typeof values)[number] & {
|
||||
skip: false;
|
||||
})[];
|
||||
|
||||
useLeaderboardCache.setState(
|
||||
produce((draft: LeaderboardCache) => {
|
||||
if (draft) {
|
||||
values.forEach((player) => {
|
||||
const playerWithTypes =
|
||||
player as typeof player & { skip: false };
|
||||
|
||||
draft[region][season][
|
||||
playerWithTypes.number - 1
|
||||
] = playerWithTypes.spa_id;
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
useNameCache.setState(
|
||||
produce((draft: typeof names) => {
|
||||
valuesWithTypes.forEach((searchedPlayer) => {
|
||||
if (searchedPlayer.nickname) {
|
||||
draft[region][searchedPlayer.spa_id] = {
|
||||
nickname: searchedPlayer.nickname,
|
||||
clan: searchedPlayer.clan_tag,
|
||||
};
|
||||
}
|
||||
});
|
||||
}),
|
||||
);
|
||||
setSearchResults(
|
||||
valuesWithTypes.map((searchedPlayer) => ({
|
||||
account_id: searchedPlayer.spa_id,
|
||||
nickname: searchedPlayer.nickname,
|
||||
number: searchedPlayer.number,
|
||||
})),
|
||||
);
|
||||
setSearchLoading(false);
|
||||
} else {
|
||||
setSearchLoading(true);
|
||||
const accountList = await fetchBlitz<AccountList>(
|
||||
`https://api.wotblitz.${region}/wotb/account/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${encodedSearch}`,
|
||||
);
|
||||
const playerKeys = Object.keys(
|
||||
leaderboard[region][season],
|
||||
);
|
||||
|
||||
setSearchResults(
|
||||
accountList
|
||||
.map((searchedPlayer) => ({
|
||||
...searchedPlayer,
|
||||
number: parseInt(
|
||||
playerKeys.find(
|
||||
(indexAsString) =>
|
||||
leaderboard[region][season][
|
||||
parseInt(indexAsString)
|
||||
] === searchedPlayer.account_id,
|
||||
)!,
|
||||
),
|
||||
}))
|
||||
.filter(
|
||||
(player) => !Number.isNaN(player.number),
|
||||
),
|
||||
);
|
||||
|
||||
setSearchLoading(false);
|
||||
}
|
||||
} else {
|
||||
setSearchResults(undefined);
|
||||
}
|
||||
},
|
||||
500,
|
||||
)}
|
||||
placeholder="Search for player..."
|
||||
/>
|
||||
</TextField.Root>
|
||||
|
||||
<Flex direction="column" gap="2">
|
||||
|
||||
@@ -37,19 +37,18 @@ export function Menu({ Builder, reset }: ContextMenuProps) {
|
||||
<Heading size="4">Edit title</Heading>
|
||||
|
||||
<Flex gap="2" style={{ width: '100%' }}>
|
||||
<TextField.Root style={{ flex: 1 }}>
|
||||
<TextField.Input
|
||||
ref={titleInput}
|
||||
defaultValue={session.title}
|
||||
placeholder="Enter title..."
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter' && titleInput.current) {
|
||||
setShowTitleEditor(false);
|
||||
useSession.setState({ title: titleInput.current!.value });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</TextField.Root>
|
||||
<TextField.Root
|
||||
style={{ flex: 1 }}
|
||||
ref={titleInput}
|
||||
defaultValue={session.title}
|
||||
placeholder="Enter title..."
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter' && titleInput.current) {
|
||||
setShowTitleEditor(false);
|
||||
useSession.setState({ title: titleInput.current!.value });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<Dialog.Close>
|
||||
<Button
|
||||
|
||||
@@ -56,38 +56,36 @@ export default function Page() {
|
||||
<PageWrapper color="blue">
|
||||
<div className={styles.toolBar}>
|
||||
<div style={{ flex: 1, boxSizing: 'border-box', position: 'relative' }}>
|
||||
<TextField.Root>
|
||||
<TextField.Root
|
||||
defaultValue={session.isTracking ? session.nickname : undefined}
|
||||
ref={input}
|
||||
onChange={(event) => {
|
||||
event.stopPropagation();
|
||||
|
||||
if (event.target.value) {
|
||||
setShowSearchResults(true);
|
||||
setSearchResults(undefined);
|
||||
} else {
|
||||
setShowSearchResults(false);
|
||||
}
|
||||
|
||||
handleChange(event);
|
||||
}}
|
||||
onBlur={(event) => {
|
||||
// TODO: remove this hack when https://github.com/radix-ui/primitives/issues/2193 (https://github.com/radix-ui/primitives/issues/1969) is fixed
|
||||
if (showSearchResults) event.target.focus();
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Escape') {
|
||||
setShowSearchResults(false);
|
||||
input.current?.blur();
|
||||
}
|
||||
}}
|
||||
placeholder="Search for a player..."
|
||||
>
|
||||
<TextField.Slot>
|
||||
<PersonIcon height="16" width="16" />
|
||||
</TextField.Slot>
|
||||
|
||||
<TextField.Input
|
||||
defaultValue={session.isTracking ? session.nickname : undefined}
|
||||
ref={input}
|
||||
onChange={(event) => {
|
||||
event.stopPropagation();
|
||||
|
||||
if (event.target.value) {
|
||||
setShowSearchResults(true);
|
||||
setSearchResults(undefined);
|
||||
} else {
|
||||
setShowSearchResults(false);
|
||||
}
|
||||
|
||||
handleChange(event);
|
||||
}}
|
||||
onBlur={(event) => {
|
||||
// TODO: remove this hack when https://github.com/radix-ui/primitives/issues/2193 is fixed
|
||||
if (showSearchResults) event.target.focus();
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Escape') {
|
||||
setShowSearchResults(false);
|
||||
input.current?.blur();
|
||||
}
|
||||
}}
|
||||
placeholder="Search for a player..."
|
||||
/>
|
||||
</TextField.Root>
|
||||
|
||||
<DropdownMenu.Root open={showSearchResults} modal={false}>
|
||||
|
||||
+26
-27
@@ -89,34 +89,33 @@ export function Miscellaneous() {
|
||||
justify="end"
|
||||
style={{ width: 180 }}
|
||||
>
|
||||
<TextField.Root>
|
||||
<TextField.Root
|
||||
ref={crewInput}
|
||||
defaultValue={Math.round(crewMastery * 100)}
|
||||
style={{ textAlign: 'right' }}
|
||||
onBlur={() => {
|
||||
const newValue = Number(crewInput.current!.value) / 100;
|
||||
|
||||
if (isNaN(newValue)) {
|
||||
crewInput.current!.value = `${Math.round(crewMastery * 100)}`;
|
||||
return;
|
||||
}
|
||||
|
||||
mutateDuel((draft) => {
|
||||
draft.protagonist!.crewMastery = clamp(
|
||||
newValue / 100,
|
||||
0.5,
|
||||
1,
|
||||
);
|
||||
});
|
||||
}}
|
||||
onKeyUp={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
crewInput.current?.blur();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<TextField.Slot>Crew</TextField.Slot>
|
||||
<TextField.Input
|
||||
ref={crewInput}
|
||||
defaultValue={Math.round(crewMastery * 100)}
|
||||
style={{ textAlign: 'right' }}
|
||||
onBlur={() => {
|
||||
const newValue = Number(crewInput.current!.value) / 100;
|
||||
|
||||
if (isNaN(newValue)) {
|
||||
crewInput.current!.value = `${Math.round(crewMastery * 100)}`;
|
||||
return;
|
||||
}
|
||||
|
||||
mutateDuel((draft) => {
|
||||
draft.protagonist!.crewMastery = clamp(
|
||||
newValue / 100,
|
||||
0.5,
|
||||
1,
|
||||
);
|
||||
});
|
||||
}}
|
||||
onKeyUp={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
crewInput.current?.blur();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<TextField.Slot>%</TextField.Slot>
|
||||
</TextField.Root>
|
||||
|
||||
|
||||
@@ -418,25 +418,25 @@ export function Characteristics() {
|
||||
setPenetrationDistance(value);
|
||||
}, 500)}
|
||||
/>
|
||||
<TextField.Root style={{ width: 64 }}>
|
||||
<TextField.Input
|
||||
ref={penetrationDistanceInput}
|
||||
defaultValue={penetrationDistance}
|
||||
onBlur={() => {
|
||||
setPenetrationDistance(
|
||||
Math.min(
|
||||
parseInt(penetrationDistanceInput.current!.value),
|
||||
500,
|
||||
),
|
||||
);
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.currentTarget.blur();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<TextField.Slot>m</TextField.Slot>
|
||||
<TextField.Root
|
||||
style={{ width: 64 }}
|
||||
ref={penetrationDistanceInput}
|
||||
defaultValue={penetrationDistance}
|
||||
onBlur={() => {
|
||||
setPenetrationDistance(
|
||||
Math.min(
|
||||
parseInt(penetrationDistanceInput.current!.value),
|
||||
500,
|
||||
),
|
||||
);
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.currentTarget.blur();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<TextField.Slot side="right">m</TextField.Slot>
|
||||
</TextField.Root>
|
||||
</Flex>
|
||||
</>
|
||||
|
||||
@@ -54,7 +54,32 @@ export function RotationInputs({ isFullScreen }: RotationInputsProps) {
|
||||
<TextField.Root
|
||||
style={{
|
||||
flex: 1,
|
||||
textAlign: 'right',
|
||||
}}
|
||||
onBlur={() => {
|
||||
const [pitch, yaw] = applyPitchYawLimits(
|
||||
protagonist.pitch,
|
||||
degToRad(Number(turretYawInput.current!.value)),
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
);
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
mutateDuel((state) => {
|
||||
state.protagonist!.pitch = pitch;
|
||||
state.protagonist!.yaw = yaw;
|
||||
});
|
||||
turretYawInput.current!.value = radToDeg(protagonist.yaw).toFixed(
|
||||
1,
|
||||
);
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
turretYawInput.current?.blur();
|
||||
}
|
||||
}}
|
||||
onFocus={() => turretYawInput.current?.focus()}
|
||||
ref={turretYawInput}
|
||||
>
|
||||
<TextField.Slot
|
||||
style={{
|
||||
@@ -63,40 +88,38 @@ export function RotationInputs({ isFullScreen }: RotationInputsProps) {
|
||||
>
|
||||
Yaw
|
||||
</TextField.Slot>
|
||||
<TextField.Input
|
||||
onBlur={() => {
|
||||
const [pitch, yaw] = applyPitchYawLimits(
|
||||
protagonist.pitch,
|
||||
degToRad(Number(turretYawInput.current!.value)),
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
);
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
mutateDuel((state) => {
|
||||
state.protagonist!.pitch = pitch;
|
||||
state.protagonist!.yaw = yaw;
|
||||
});
|
||||
turretYawInput.current!.value = radToDeg(protagonist.yaw).toFixed(
|
||||
1,
|
||||
);
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
turretYawInput.current?.blur();
|
||||
}
|
||||
}}
|
||||
onFocus={() => turretYawInput.current?.focus()}
|
||||
ref={turretYawInput}
|
||||
style={{ textAlign: 'right' }}
|
||||
/>
|
||||
<TextField.Slot style={{ userSelect: 'none' }}>°</TextField.Slot>
|
||||
</TextField.Root>
|
||||
|
||||
<TextField.Root
|
||||
style={{
|
||||
flex: 1,
|
||||
textAlign: 'right',
|
||||
}}
|
||||
onBlur={() => {
|
||||
const [pitch, yaw] = applyPitchYawLimits(
|
||||
degToRad(-Number(gunPitchInput.current!.value) + initialGunPitch),
|
||||
protagonist.yaw,
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
);
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
mutateDuel((state) => {
|
||||
state.protagonist!.pitch = pitch;
|
||||
state.protagonist!.yaw = yaw;
|
||||
});
|
||||
gunPitchInput.current!.value = (
|
||||
-radToDeg(protagonist.pitch) + initialGunPitch
|
||||
).toFixed(1);
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
gunPitchInput.current?.blur();
|
||||
}
|
||||
}}
|
||||
onFocus={() => gunPitchInput.current?.focus()}
|
||||
ref={gunPitchInput}
|
||||
>
|
||||
<TextField.Slot
|
||||
style={{
|
||||
@@ -105,35 +128,6 @@ export function RotationInputs({ isFullScreen }: RotationInputsProps) {
|
||||
>
|
||||
Pitch
|
||||
</TextField.Slot>
|
||||
<TextField.Input
|
||||
onBlur={() => {
|
||||
const [pitch, yaw] = applyPitchYawLimits(
|
||||
degToRad(
|
||||
-Number(gunPitchInput.current!.value) + initialGunPitch,
|
||||
),
|
||||
protagonist.yaw,
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
);
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
mutateDuel((state) => {
|
||||
state.protagonist!.pitch = pitch;
|
||||
state.protagonist!.yaw = yaw;
|
||||
});
|
||||
gunPitchInput.current!.value = (
|
||||
-radToDeg(protagonist.pitch) + initialGunPitch
|
||||
).toFixed(1);
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
gunPitchInput.current?.blur();
|
||||
}
|
||||
}}
|
||||
onFocus={() => gunPitchInput.current?.focus()}
|
||||
ref={gunPitchInput}
|
||||
style={{ textAlign: 'right' }}
|
||||
/>
|
||||
<TextField.Slot
|
||||
style={{
|
||||
userSelect: 'none',
|
||||
|
||||
@@ -32,32 +32,31 @@ export function PageTurner({ searchedList, tanksPerPage }: PageTurnerProps) {
|
||||
>
|
||||
<CaretLeftIcon />
|
||||
</Button>
|
||||
<TextField.Root>
|
||||
<TextField.Root
|
||||
defaultValue={1}
|
||||
type="number"
|
||||
ref={pageInput}
|
||||
min={1}
|
||||
max={Math.floor(searchedList.length / tanksPerPage) + 1}
|
||||
style={{ textAlign: 'center' }}
|
||||
onBlur={(event) => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
draft.filters.page = Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
Math.floor(searchedList.length / tanksPerPage),
|
||||
event.target.valueAsNumber - 1,
|
||||
),
|
||||
);
|
||||
});
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
(event.target as HTMLInputElement).blur();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<TextField.Slot>Page</TextField.Slot>
|
||||
<TextField.Input
|
||||
defaultValue={1}
|
||||
type="number"
|
||||
ref={pageInput}
|
||||
min={1}
|
||||
max={Math.floor(searchedList.length / tanksPerPage) + 1}
|
||||
style={{ width: 64, textAlign: 'center' }}
|
||||
onBlur={(event) => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
draft.filters.page = Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
Math.floor(searchedList.length / tanksPerPage),
|
||||
event.target.valueAsNumber - 1,
|
||||
),
|
||||
);
|
||||
});
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
(event.target as HTMLInputElement).blur();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<TextField.Slot>
|
||||
out of {Math.floor(searchedList.length / tanksPerPage) + 1}
|
||||
</TextField.Slot>
|
||||
|
||||
@@ -392,27 +392,26 @@ export function TankSearch({ compact, onSelect = () => {} }: TankSearchProps) {
|
||||
<>
|
||||
<Flex direction="column" gap="4">
|
||||
<Flex gap="2">
|
||||
<TextField.Root style={{ flex: 1 }}>
|
||||
<TextField.Root
|
||||
style={{ flex: 1 }}
|
||||
ref={input}
|
||||
placeholder="Search tanks..."
|
||||
onChange={(event) => {
|
||||
const results = go(event.target.value, searchableTanks, {
|
||||
keys: [
|
||||
'name',
|
||||
'nameFull',
|
||||
'id',
|
||||
] satisfies (keyof TankDefinition)[],
|
||||
all: true,
|
||||
}).map(({ obj }) => obj);
|
||||
|
||||
setSearchedList(results);
|
||||
}}
|
||||
>
|
||||
<TextField.Slot>
|
||||
<MagnifyingGlassIcon height="16" width="16" />
|
||||
</TextField.Slot>
|
||||
|
||||
<TextField.Input
|
||||
ref={input}
|
||||
placeholder="Search tanks..."
|
||||
onChange={(event) => {
|
||||
const results = go(event.target.value, searchableTanks, {
|
||||
keys: [
|
||||
'name',
|
||||
'nameFull',
|
||||
'id',
|
||||
] satisfies (keyof TankDefinition)[],
|
||||
all: true,
|
||||
}).map(({ obj }) => obj);
|
||||
|
||||
setSearchedList(results);
|
||||
}}
|
||||
/>
|
||||
</TextField.Root>
|
||||
|
||||
<DropdownMenu.Root>
|
||||
@@ -779,12 +778,12 @@ export function TankSearch({ compact, onSelect = () => {} }: TankSearchProps) {
|
||||
flex: 1,
|
||||
cursor: 'pointer',
|
||||
height: 64,
|
||||
minWidth: 256,
|
||||
}}
|
||||
>
|
||||
<Inset
|
||||
key={tank.id}
|
||||
style={{
|
||||
minWidth: 256,
|
||||
minHeight: 64,
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Flex, Theme } from '@radix-ui/themes';
|
||||
import { Flex, FlexProps, Theme } from '@radix-ui/themes';
|
||||
import { ComponentProps } from 'react';
|
||||
|
||||
interface PageWrapperProps extends ComponentProps<typeof Flex> {
|
||||
type PageWrapperProps = FlexProps & {
|
||||
color?: ComponentProps<typeof Theme>['accentColor'];
|
||||
size?: number;
|
||||
}
|
||||
};
|
||||
|
||||
export default function PageWrapper({
|
||||
style,
|
||||
|
||||
Reference in new issue
Block a user