feat: visually indicate the origin of a fit (browser, ESI, ..) (#50)

This commit is contained in:
Patric Stout
2023-12-22 16:33:10 +01:00
committed by GitHub
parent f79e44ec4e
commit bbfd275f1d
3 changed files with 51 additions and 14 deletions

View File

@@ -4,19 +4,24 @@ import React from "react";
import { EsiContext } from "../EsiProvider";
import { EsiFit, ShipSnapshotContext } from "../ShipSnapshotProvider";
import { EveDataContext } from "../EveDataProvider";
import { Icon } from "../Icon";
import { Icon, IconName } from "../Icon";
import { TreeListing, TreeHeader, TreeHeaderAction, TreeLeaf } from "../TreeListing";
import { LocalFitContext } from "../LocalFitProvider";
import styles from "./HullListing.module.css";
interface ListingFit {
origin: "local" | "esi-character";
fit: EsiFit;
}
interface ListingHull {
name: string;
fits: EsiFit[];
fits: ListingFit[];
}
interface ListingHulls {
[typeId: string]: ListingFit;
[typeId: string]: ListingHull;
}
interface ListingGroup {
@@ -35,7 +40,7 @@ const factionIdToRace: Record<number, string> = {
1: "Non-Empire",
} as const;
const Hull = (props: { typeId: number, entry: ListingFit }) => {
const Hull = (props: { typeId: number, entry: ListingHull }) => {
const shipSnapShot = React.useContext(ShipSnapshotContext);
const getChildren = React.useCallback(() => {
@@ -43,9 +48,24 @@ const Hull = (props: { typeId: number, entry: ListingFit }) => {
return <TreeLeaf level={4} content={"No Item"} />;
} else {
let index = 0;
return <>{props.entry.fits.sort((a, b) => a.name.localeCompare(b.name)).map((fit) => {
return <>{props.entry.fits.sort((a, b) => a.fit.name.localeCompare(b.fit.name)).map((fit) => {
index += 1;
return <TreeLeaf key={`${fit.ship_type_id}-${index}`} level={4} content={fit.name} onClick={() => shipSnapShot.changeFit(fit)} />;
let icon: IconName | undefined;
let iconTitle: string | undefined;
switch (fit.origin) {
case "local":
icon = "fitting-local";
iconTitle = "Browser-stored fitting";
break;
case "esi-character":
icon = "fitting-character";
iconTitle = "In-game personal fitting";
break;
}
return <TreeLeaf key={`${fit.fit.ship_type_id}-${index}`} level={4} content={fit.fit.name} onClick={() => shipSnapShot.changeFit(fit.fit)} icon={icon} iconTitle={iconTitle} />;
})}</>;
}
}, [props, shipSnapShot]);
@@ -106,14 +126,14 @@ export const HullListing = () => {
currentHull: false,
});
const [localCharacterFits, setLocalCharacterFits] = React.useState<Record<string, EsiFit[]>>({});
const [esiCharacterFits, setEsiCharacterFits] = React.useState<Record<string, EsiFit[]>>({});
const [localCharacterFits, setLocalCharacterFits] = React.useState<Record<string, ListingFit[]>>({});
const [esiCharacterFits, setEsiCharacterFits] = React.useState<Record<string, ListingFit[]>>({});
React.useEffect(() => {
if (!localFit.loaded) return;
if (!localFit.fittings) return;
const newLocalCharacterFits: Record<string, EsiFit[]> = {};
const newLocalCharacterFits: Record<string, ListingFit[]> = {};
for (const fit of localFit.fittings) {
if (fit.ship_type_id === undefined) continue;
@@ -121,7 +141,10 @@ export const HullListing = () => {
newLocalCharacterFits[fit.ship_type_id] = [];
}
newLocalCharacterFits[fit.ship_type_id].push(fit);
newLocalCharacterFits[fit.ship_type_id].push({
origin: "local",
fit
});
}
setLocalCharacterFits(newLocalCharacterFits);
@@ -133,7 +156,7 @@ export const HullListing = () => {
const charFittings = esi.characters[esi.currentCharacter].charFittings || [];
const newEsiCharacterFits: Record<string, EsiFit[]> = {};
const newEsiCharacterFits: Record<string, ListingFit[]> = {};
for (const fit of charFittings) {
if (fit.ship_type_id === undefined) continue;
@@ -141,7 +164,10 @@ export const HullListing = () => {
newEsiCharacterFits[fit.ship_type_id] = [];
}
newEsiCharacterFits[fit.ship_type_id].push(fit);
newEsiCharacterFits[fit.ship_type_id].push({
origin: "esi-character",
fit,
});
}
setEsiCharacterFits(newEsiCharacterFits);
@@ -161,7 +187,7 @@ export const HullListing = () => {
if (filter.currentHull && shipSnapShot.fit?.ship_type_id !== parseInt(typeId)) continue;
const fits: EsiFit[] = [];
const fits: ListingFit[] = [];
if (anyFilter) {
if (filter.localCharacter && Object.keys(localCharacterFits).includes(typeId)) fits.push(...localCharacterFits[typeId]);
if (filter.esiCharacter && Object.keys(esiCharacterFits).includes(typeId)) fits.push(...esiCharacterFits[typeId]);

View File

@@ -36,4 +36,12 @@
.leaf {
cursor: pointer;
position: relative;
}
.leafIcon {
left: -16px;
margin-top: 2px;
pointer-events: none;
position: absolute;
}

View File

@@ -41,7 +41,7 @@ export const TreeHeader = (props: { icon?: string, text: string, action?: React.
</>
}
export const TreeLeaf = (props: { level: number, height?: number, content: string, onClick?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void }) => {
export const TreeLeaf = (props: { level: number, height?: number, icon?: IconName, iconTitle?: string, content: string, onClick?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void }) => {
const stylesHeader = styles[`header${props.level}`];
const height = props.height ?? 20;
@@ -50,6 +50,9 @@ export const TreeLeaf = (props: { level: number, height?: number, content: strin
return <div>
<TreeContext.Provider value={{size: height}}>
<div style={style} className={clsx(styles.header, stylesHeader, {[styles.headerHover]: props.onClick !== undefined, [styles.leaf]: props.onClick !== undefined})} onClick={props.onClick}>
{props.icon !== undefined && <span className={styles.leafIcon}>
<Icon name={props.icon} size={12} title={props.iconTitle} />
</span>}
<span className={styles.headerText}>
{props.content}
</span>