mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-22 19:19:52 +02:00
tooltips
This commit is contained in:
1 parent
6fb51e703d
commit
26bb350709
6 files changed
+166
-95
No files matched your search
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"tabWidth": 2
|
||||
}
|
||||
@@ -32,6 +32,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
&[data-parent-array="true"] {
|
||||
*:first-child > & {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
*:last-child > & {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
svg,
|
||||
img {
|
||||
width: 1em;
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface ButtonProps extends ComponentProps<"button"> {
|
||||
variant?: "solid" | "surface" | "soft" | "inverted" | "ghost" | "outline";
|
||||
size?: "regular" | "minor";
|
||||
array?: boolean;
|
||||
parentArray?: boolean;
|
||||
}
|
||||
|
||||
export const LIGHT_TEXT_COLORS = new Set<Color>([
|
||||
@@ -34,6 +35,7 @@ export function Button({
|
||||
radius = "max",
|
||||
size = "regular",
|
||||
array = false,
|
||||
parentArray = false,
|
||||
...props
|
||||
}: ButtonProps) {
|
||||
return (
|
||||
@@ -45,6 +47,7 @@ export function Button({
|
||||
data-radius={radius}
|
||||
data-size={size}
|
||||
data-array={array}
|
||||
data-parent-array={parentArray}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -35,6 +35,8 @@ import { Section } from "../Section";
|
||||
import { Text } from "../Text";
|
||||
import styles from "./index.module.css";
|
||||
import type { ComputedCharacteristics } from "../../tankopedia/computeCharacteristics";
|
||||
import { Tooltip } from "../Tooltip";
|
||||
import { useGameStrings } from "../../hooks/useGameStrings";
|
||||
|
||||
interface TankopediaLoadoutProps {
|
||||
characteristics: ComputedCharacteristics;
|
||||
@@ -178,6 +180,7 @@ function EquipmentOption({
|
||||
const equipment = useEquipment(tank.tank!);
|
||||
const slot = equipment.preset.slots[equipmentIndex];
|
||||
const id = slot.options_catalog_i_ds[optionIndex];
|
||||
const gameStrings = useGameStrings("EquipmentEntity");
|
||||
|
||||
const isSelected = Tankopedia.use(
|
||||
(state) =>
|
||||
@@ -186,25 +189,27 @@ function EquipmentOption({
|
||||
);
|
||||
|
||||
return (
|
||||
<Button
|
||||
color={isSelected ? undefined : "gray"}
|
||||
variant={isSelected ? "surface" : "soft"}
|
||||
data-selected={isSelected}
|
||||
radius="1"
|
||||
array
|
||||
className={styles.slot}
|
||||
onClick={() => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
if (isSelected) {
|
||||
delete draft.protagonist.equipment[equipmentIndex];
|
||||
} else {
|
||||
draft.protagonist.equipment[equipmentIndex] = optionIndex;
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img src={`/media/equipment/${id}.webp`} />
|
||||
</Button>
|
||||
<Tooltip tooltip={gameStrings[equipment.equipments[id].equipment_name]}>
|
||||
<Button
|
||||
color={isSelected ? undefined : "gray"}
|
||||
variant={isSelected ? "surface" : "soft"}
|
||||
data-selected={isSelected}
|
||||
radius="1"
|
||||
parentArray
|
||||
className={styles.slot}
|
||||
onClick={() => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
if (isSelected) {
|
||||
delete draft.protagonist.equipment[equipmentIndex];
|
||||
} else {
|
||||
draft.protagonist.equipment[equipmentIndex] = optionIndex;
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img src={`/media/equipment/${id}.webp`} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -362,6 +367,7 @@ function LineElement({ index, lineName, stage }: LineElementProps) {
|
||||
const alternates = Tankopedia.use((state) => state.protagonist.alternates);
|
||||
const tank = useProtagonist();
|
||||
const strings = useStrings();
|
||||
const gameStrings = useGameStrings("TankEntity");
|
||||
const upgradePreset = useUpgradePreset(tank.tank!.tank_upgrade_preset);
|
||||
|
||||
let price: StandardPrice | undefined;
|
||||
@@ -405,93 +411,94 @@ function LineElement({ index, lineName, stage }: LineElementProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<Button
|
||||
className={styles.stage}
|
||||
color={isSelected ? undefined : "gray"}
|
||||
variant={isSelected ? "surface" : "soft"}
|
||||
radius="1"
|
||||
onClick={() => {
|
||||
// TODO: make minimum selectable upgrade the last free module for tanks like the destiny
|
||||
<Tooltip tooltip={gameStrings[stage.display_name]}>
|
||||
<div className={styles.wrapper}>
|
||||
<Button
|
||||
className={styles.stage}
|
||||
color={isSelected ? undefined : "gray"}
|
||||
variant={isSelected ? "surface" : "soft"}
|
||||
radius="1"
|
||||
onClick={() => {
|
||||
// TODO: make minimum selectable upgrade the last free module for tanks like the destiny
|
||||
|
||||
Tankopedia.mutate((draft) => {
|
||||
const tankData = tank.tank!;
|
||||
Tankopedia.mutate((draft) => {
|
||||
const tankData = tank.tank!;
|
||||
|
||||
if (isAlternativeLine(lineName)) {
|
||||
const originalLine = originalLineName(lineName)!;
|
||||
if (isAlternativeLine(lineName)) {
|
||||
const originalLine = originalLineName(lineName)!;
|
||||
|
||||
draft.protagonist.alternates[originalLine] = true;
|
||||
draft.protagonist.upgrades[lineName] = index;
|
||||
} else {
|
||||
draft.protagonist.alternates[lineName] = false;
|
||||
draft.protagonist.upgrades[lineName] = index;
|
||||
}
|
||||
|
||||
for (const required of stage.required_upgrades) {
|
||||
for (const line of tankData.upgrade_lines) {
|
||||
let i = 0;
|
||||
|
||||
for (const candidateStage of line.stages) {
|
||||
if (candidateStage.tech_name === required) {
|
||||
draft.protagonist.upgrades[line.name] = Math.max(
|
||||
draft.protagonist.upgrades[line.name],
|
||||
i,
|
||||
);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const line of tankData.upgrade_lines) {
|
||||
if (draft.protagonist.alternates[line.name]) {
|
||||
continue;
|
||||
draft.protagonist.alternates[originalLine] = true;
|
||||
draft.protagonist.upgrades[lineName] = index;
|
||||
} else {
|
||||
draft.protagonist.alternates[lineName] = false;
|
||||
draft.protagonist.upgrades[lineName] = index;
|
||||
}
|
||||
|
||||
let i = draft.protagonist.upgrades[line.name];
|
||||
for (const required of stage.required_upgrades) {
|
||||
for (const line of tankData.upgrade_lines) {
|
||||
let i = 0;
|
||||
|
||||
while (i > 0) {
|
||||
const currentStage = line.stages[i];
|
||||
|
||||
const valid = currentStage.required_upgrades.every(
|
||||
(required) => {
|
||||
for (const otherLine of tankData.upgrade_lines) {
|
||||
const idx = draft.protagonist.upgrades[otherLine.name];
|
||||
|
||||
if (otherLine.stages[idx]?.tech_name === required) {
|
||||
return true;
|
||||
}
|
||||
for (const candidateStage of line.stages) {
|
||||
if (candidateStage.tech_name === required) {
|
||||
draft.protagonist.upgrades[line.name] = Math.max(
|
||||
draft.protagonist.upgrades[line.name],
|
||||
i,
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
);
|
||||
|
||||
if (valid) break;
|
||||
|
||||
i--;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
draft.protagonist.upgrades[line.name] = i;
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
className={styles.icon}
|
||||
src={`/media/modules/${stage.stage_type}.webp`}
|
||||
/>
|
||||
{/* {stage.display_name} */}
|
||||
for (const line of tankData.upgrade_lines) {
|
||||
if (draft.protagonist.alternates[line.name]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
<Text weight="light" lowContrast size="minor" className={styles.tier}>
|
||||
{isAlternativeLine(lineName)
|
||||
? strings.tanks.loadout.alternative
|
||||
: romanize(stage.number)}
|
||||
</Text>
|
||||
</Button>
|
||||
let i = draft.protagonist.upgrades[line.name];
|
||||
|
||||
{<Price className={styles.price} price={price} />}
|
||||
</div>
|
||||
while (i > 0) {
|
||||
const currentStage = line.stages[i];
|
||||
|
||||
const valid = currentStage.required_upgrades.every(
|
||||
(required) => {
|
||||
for (const otherLine of tankData.upgrade_lines) {
|
||||
const idx = draft.protagonist.upgrades[otherLine.name];
|
||||
|
||||
if (otherLine.stages[idx]?.tech_name === required) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
);
|
||||
|
||||
if (valid) break;
|
||||
|
||||
i--;
|
||||
}
|
||||
|
||||
draft.protagonist.upgrades[line.name] = i;
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
className={styles.icon}
|
||||
src={`/media/modules/${stage.stage_type}.webp`}
|
||||
/>
|
||||
|
||||
<Text weight="light" lowContrast size="minor" className={styles.tier}>
|
||||
{isAlternativeLine(lineName)
|
||||
? strings.tanks.loadout.alternative
|
||||
: romanize(stage.number)}
|
||||
</Text>
|
||||
</Button>
|
||||
|
||||
{<Price className={styles.price} price={price} />}
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
.tooltip {
|
||||
position: relative;
|
||||
|
||||
.text {
|
||||
display: none;
|
||||
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
z-index: 1;
|
||||
background-color: var(--gray-12);
|
||||
color: var(--gray-1);
|
||||
border-radius: var(--space-3);
|
||||
|
||||
padding: var(--space-2) var(--space-3);
|
||||
margin-bottom: var(--space-3);
|
||||
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus-within {
|
||||
.text {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import type { ReactNode } from "react";
|
||||
import styles from "./index.module.css";
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
tooltip: string;
|
||||
}
|
||||
|
||||
export function Tooltip({ children, tooltip }: Props) {
|
||||
return (
|
||||
<div className={styles.tooltip}>
|
||||
<div className={styles.text}>{tooltip}</div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user