Skip to content

Commit 2bee180

Browse files
committed
fix(app): show fluid fuel recipe icons
1 parent e6012f5 commit 2bee180

2 files changed

Lines changed: 67 additions & 16 deletions

File tree

app/src/lib/icons.test.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const { manifest } = vi.hoisted(() => ({
1111
icons: {
1212
"item/iron-plate": { s: 0, x: 3072, y: 3008 },
1313
"module/speed-module": { s: 0, x: 10, y: 20 }, // item-kind in a non-"item" folder
14+
"fluid/kerosene": { s: 0, x: 64, y: 128 },
1415
},
1516
},
1617
}));
@@ -57,6 +58,21 @@ test("item-kind falls back to non-item folders (module/)", async () => {
5758
});
5859
});
5960

61+
test("fluid-fuel conversion recipes use the source fluid icon with a fuel badge", async () => {
62+
const { container } = render(
63+
<IconProvider>
64+
<Icon kind="recipe" name="burn-fluid-kerosene" size="md" noHover />
65+
</IconProvider>,
66+
);
67+
await waitFor(() => {
68+
const span = container.querySelector("span[title='burn-fluid-kerosene']") as HTMLElement;
69+
expect(span.getAttribute("style")).toContain(
70+
"calc(var(--icon-md) * -1) calc(var(--icon-md) * -2)",
71+
);
72+
expect(container.querySelector("[data-icon-badge='fluid-fuel'] svg")).toBeTruthy();
73+
});
74+
});
75+
6076
test("the default icon shows no internal-name title (a rich hover card replaces it)", async () => {
6177
const { container } = render(
6278
<IconProvider>

app/src/lib/icons.tsx

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -226,21 +226,54 @@ export function RawIcon({ kind, name, size = "sm", noTitle = false, title }: Ico
226226
if (!m) return <span style={base} />;
227227

228228
// synthetic recipes (mine-X, boil-X-250, generate-X, spoil-X, pump-X) have no
229-
// recipe/ sprite — fall back to the subject's item/entity/fluid icon
229+
// recipe/ sprite — fall back to the subject's item/entity/fluid icon. Fluid
230+
// fuel conversions are also synthetic, but their `burn-fluid-X` subject is
231+
// known to be a fluid (and `fluid-` is part of the recipe prefix, not its id).
230232
const synthetic = /^(mine|pump|spoil|boil|generate)-(.+?)(?:-\d+)?$/.exec(name);
233+
const burnedFluid = /^burn-fluid-(.+)$/.exec(name)?.[1];
234+
const withFuelBadge = (el: React.ReactNode) =>
235+
burnedFluid ? (
236+
<span style={{ ...base, position: "relative" }}>
237+
{el}
238+
<span
239+
aria-hidden
240+
data-icon-badge="fluid-fuel"
241+
style={{
242+
position: "absolute",
243+
right: "-3px",
244+
bottom: "-3px",
245+
display: "inline-flex",
246+
borderRadius: "9999px",
247+
background: "rgba(0,0,0,0.75)",
248+
padding: "1px",
249+
pointerEvents: "none",
250+
}}
251+
>
252+
<Fuel
253+
color="var(--info)"
254+
strokeWidth={2.75}
255+
style={{ width: `calc(${v} * 0.5)`, height: `calc(${v} * 0.5)` }}
256+
/>
257+
</span>
258+
</span>
259+
) : (
260+
el
261+
);
231262
// generate-heat-<reactor> → the reactor entity (the "heat-" is the product, not the subject)
232263
const subject = synthetic?.[2]?.replace(/^heat-/, "");
233264
const slot =
234265
name === "pyops-electricity"
235266
? m.icons["ammo-category/electric"]
236267
: kind === "recipe"
237268
? (m.icons[`recipe/${name}`] ??
238-
(subject
239-
? (itemIdx[subject] ??
240-
m.icons[`entity/${subject}`] ??
241-
m.icons[`fluid/${subject}`] ??
242-
m.icons[`resource/${subject}`])
243-
: undefined))
269+
(burnedFluid
270+
? m.icons[`fluid/${burnedFluid}`]
271+
: subject
272+
? (itemIdx[subject] ??
273+
m.icons[`entity/${subject}`] ??
274+
m.icons[`fluid/${subject}`] ??
275+
m.icons[`resource/${subject}`])
276+
: undefined))
244277
: kind === "fluid"
245278
? m.icons[`fluid/${name}`]
246279
: kind === "entity"
@@ -261,14 +294,16 @@ export function RawIcon({ kind, name, size = "sm", noTitle = false, title }: Ico
261294
// the CSS var so the sprite tracks whatever --icon-* resolves to.
262295
const cells = m.atlasSize / m.cell;
263296
return withSpoil(
264-
<span
265-
title={noTitle ? undefined : `${title ?? name}${spoilSuffix}`}
266-
style={{
267-
...base,
268-
backgroundImage: `url(/icons/${m.sheets[slot.s]})`,
269-
backgroundPosition: `calc(${v} * ${-slot.x / m.cell}) calc(${v} * ${-slot.y / m.cell})`,
270-
backgroundSize: `calc(${v} * ${cells}) calc(${v} * ${cells})`,
271-
}}
272-
/>,
297+
withFuelBadge(
298+
<span
299+
title={noTitle ? undefined : `${title ?? name}${spoilSuffix}`}
300+
style={{
301+
...base,
302+
backgroundImage: `url(/icons/${m.sheets[slot.s]})`,
303+
backgroundPosition: `calc(${v} * ${-slot.x / m.cell}) calc(${v} * ${-slot.y / m.cell})`,
304+
backgroundSize: `calc(${v} * ${cells}) calc(${v} * ${cells})`,
305+
}}
306+
/>,
307+
),
273308
);
274309
}

0 commit comments

Comments
 (0)