fix: improve local character header shrink behavior (#146)

This commit is contained in:
guarzo
2025-02-04 14:00:30 -07:00
committed by GitHub
parent a70fa50eab
commit d31046eebb
5 changed files with 248 additions and 112 deletions

View File

@@ -66,43 +66,55 @@ export const LocalCharacters = () => {
return (
<Widget
label={
<div className="flex justify-between items-center text-xs w-full" ref={ref}>
<span className="select-none">Local{showList ? ` [${sorted.length}]` : ''}</span>
<LayoutEventBlocker className="flex items-center gap-2">
{showOffline && (
<WdTooltipWrapper content="Show offline characters in system">
<WdCheckbox
size="xs"
labelSide="left"
label={compact ? '' : 'Show offline'}
value={settings.showOffline}
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
onChange={() => setSettings(prev => ({ ...prev, showOffline: !prev.showOffline }))}
/>
</WdTooltipWrapper>
)}
<div className="flex w-full items-center" ref={ref}>
<div className="flex-shrink-0 select-none mr-2">
Local{showList ? ` [${sorted.length}]` : ''}
</div>
<div className="flex-grow overflow-hidden">
<LayoutEventBlocker className="flex items-center gap-2 justify-end">
{showOffline && (
<WdTooltipWrapper content="Show offline characters in system">
<div className={clsx("min-w-0", { "max-w-[100px]": compact })}>
<WdCheckbox
size="xs"
labelSide="left"
label="Show offline"
value={settings.showOffline}
classNameLabel={clsx("whitespace-nowrap", { "truncate": compact })}
onChange={() =>
setSettings(prev => ({ ...prev, showOffline: !prev.showOffline }))
}
/>
</div>
</WdTooltipWrapper>
)}
{settings.compact && (
<WdTooltipWrapper content="Show ship name in compact rows">
<WdCheckbox
size="xs"
labelSide="left"
label={compact ? '' : 'Show ship name'}
value={settings.showShipName}
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
onChange={() => setSettings(prev => ({ ...prev, showShipName: !prev.showShipName }))}
/>
</WdTooltipWrapper>
)}
{settings.compact && (
<WdTooltipWrapper content="Show ship name in compact rows">
<div className={clsx("min-w-0", { "max-w-[100px]": compact })}>
<WdCheckbox
size="xs"
labelSide="left"
label="Show ship name"
value={settings.showShipName}
classNameLabel={clsx("whitespace-nowrap", { "truncate": compact })}
onChange={() =>
setSettings(prev => ({ ...prev, showShipName: !prev.showShipName }))
}
/>
</div>
</WdTooltipWrapper>
)}
<span
className={clsx('w-4 h-4 cursor-pointer', {
['hero-bars-2']: settings.compact,
['hero-bars-3']: !settings.compact,
})}
onClick={() => setSettings(prev => ({ ...prev, compact: !prev.compact }))}
/>
</LayoutEventBlocker>
<span
className={clsx("w-4 h-4 cursor-pointer", {
"hero-bars-2": settings.compact,
"hero-bars-3": !settings.compact,
})}
onClick={() => setSettings(prev => ({ ...prev, compact: !prev.compact }))}
/>
</LayoutEventBlocker>
</div>
</div>
}
>

View File

@@ -21,10 +21,15 @@ export interface CompactKillRowProps {
onlyOneSystem: boolean;
}
export const CompactKillRow: React.FC<CompactKillRowProps> = ({ killDetails, systemName, onlyOneSystem }) => {
export const CompactKillRow: React.FC<CompactKillRowProps> = ({
killDetails,
systemName,
onlyOneSystem,
}) => {
const {
killmail_id = 0,
// Victim
victim_char_name = 'Unknown Pilot',
victim_alliance_ticker = '',
victim_corp_ticker = '',
@@ -36,6 +41,7 @@ export const CompactKillRow: React.FC<CompactKillRowProps> = ({ killDetails, sys
victim_alliance_id = 0,
victim_ship_type_id = 0,
// Attacker
final_blow_char_id = 0,
final_blow_char_name = '',
final_blow_alliance_ticker = '',
@@ -51,66 +57,110 @@ export const CompactKillRow: React.FC<CompactKillRowProps> = ({ killDetails, sys
} = killDetails || {};
const attackerIsNpc = final_blow_char_id === 0;
const victimAffiliationTicker = victim_alliance_ticker || victim_corp_ticker || 'No Ticker';
const killValueFormatted = total_value != null && total_value > 0 ? `${formatISK(total_value)} ISK` : null;
// Tickers & strings
const victimAffiliationTicker =
victim_alliance_ticker || victim_corp_ticker || 'No Ticker';
const killValueFormatted =
total_value != null && total_value > 0 ? `${formatISK(total_value)} ISK` : null;
const attackerName = attackerIsNpc ? '' : final_blow_char_name;
const attackerTicker = attackerIsNpc ? '' : final_blow_alliance_ticker || final_blow_corp_ticker || '';
const attackerTicker = attackerIsNpc
? ''
: final_blow_alliance_ticker || final_blow_corp_ticker || '';
const killTimeAgo = kill_time ? formatTimeMixed(kill_time) : '0h ago';
const attackerSubscript = getAttackerSubscript(killDetails);
const { victimCorpLogoUrl, victimAllianceLogoUrl } = buildVictimImageUrls({
// Victim images, including the ship
const {
victimCorpLogoUrl,
victimAllianceLogoUrl,
victimShipUrl,
} = buildVictimImageUrls({
victim_char_id,
victim_ship_type_id,
victim_corp_id,
victim_alliance_id,
});
// Attacker corp/alliance
const { attackerCorpLogoUrl, attackerAllianceLogoUrl } = buildAttackerImageUrls({
final_blow_char_id,
final_blow_corp_id,
final_blow_alliance_id,
});
const { url: victimPrimaryLogoUrl, tooltip: victimPrimaryTooltip } = getPrimaryLogoAndTooltip(
victimAllianceLogoUrl,
victimCorpLogoUrl,
victim_alliance_name,
victim_corp_name,
'Victim',
);
// Victim corp/alliance logo
const { url: victimPrimaryLogoUrl, tooltip: victimPrimaryTooltip } =
getPrimaryLogoAndTooltip(
victimAllianceLogoUrl,
victimCorpLogoUrl,
victim_alliance_name,
victim_corp_name,
'Victim'
);
const { url: attackerPrimaryImageUrl, tooltip: attackerPrimaryTooltip } = getAttackerPrimaryImageAndTooltip(
attackerIsNpc,
attackerAllianceLogoUrl,
attackerCorpLogoUrl,
final_blow_alliance_name,
final_blow_corp_name,
final_blow_ship_type_id || 0,
);
// Attacker corp/alliance or NPC ship
const { url: attackerPrimaryImageUrl, tooltip: attackerPrimaryTooltip } =
getAttackerPrimaryImageAndTooltip(
attackerIsNpc,
attackerAllianceLogoUrl,
attackerCorpLogoUrl,
final_blow_alliance_name,
final_blow_corp_name,
final_blow_ship_type_id
);
return (
<div
className={clsx(
'h-10 flex items-center border-b border-stone-800',
'text-xs whitespace-nowrap overflow-hidden leading-none',
'text-xs whitespace-nowrap overflow-hidden leading-none'
)}
>
{victimPrimaryLogoUrl && (
<WdTooltipWrapper content={victimPrimaryTooltip} position={TooltipPosition.top}>
<a
href={zkillLink('kill', killmail_id)}
target="_blank"
rel="noopener noreferrer"
className="relative shrink-0 w-8 h-8 overflow-hidden"
>
<img
src={victimPrimaryLogoUrl}
alt="VictimPrimaryLogo"
className={clsx(classes.killRowImage, 'w-full h-full object-contain')}
/>
</a>
</WdTooltipWrapper>
)}
<div className="flex items-center gap-1">
{victimShipUrl && (
<div className="relative shrink-0 w-8 h-8 overflow-hidden">
<a
href={zkillLink('kill', killmail_id)}
target="_blank"
rel="noopener noreferrer"
className="block w-full h-full"
>
<img
src={victimShipUrl}
alt="VictimShip"
className={clsx(
classes.killRowImage,
'w-full h-full object-contain'
)}
/>
</a>
</div>
)}
{victimPrimaryLogoUrl && (
<WdTooltipWrapper
content={victimPrimaryTooltip}
position={TooltipPosition.top}
>
<a
href={zkillLink('kill', killmail_id)}
target="_blank"
rel="noopener noreferrer"
className="relative shrink-0 w-8 h-8 overflow-hidden"
>
<img
src={victimPrimaryLogoUrl}
alt="VictimPrimaryLogo"
className={clsx(
classes.killRowImage,
'w-full h-full object-contain'
)}
/>
</a>
</WdTooltipWrapper>
)}
</div>
<div className="flex flex-col ml-2 min-w-0 overflow-hidden leading-[1rem]">
<div className="truncate text-stone-200">
{victim_char_name}
@@ -126,28 +176,32 @@ export const CompactKillRow: React.FC<CompactKillRowProps> = ({ killDetails, sys
)}
</div>
</div>
<div className="flex items-center ml-auto gap-2">
<div className="flex flex-col items-end min-w-0 overflow-hidden text-right leading-[1rem]">
{!attackerIsNpc && (attackerName || attackerTicker) && (
<div className="truncate text-stone-200">
{attackerName}
{attackerTicker && <span className="ml-1 text-stone-400">/ {attackerTicker}</span>}
{attackerTicker && (
<span className="ml-1 text-stone-400">/ {attackerTicker}</span>
)}
</div>
)}
<div className="truncate text-stone-400">
{!onlyOneSystem && systemName ? (
<>
{systemName} / <span className="ml-1 text-red-400">{killTimeAgo}</span>
{systemName} /{' '}
<span className="ml-1 text-red-400">{killTimeAgo}</span>
</>
) : (
<span className="text-red-400">{killTimeAgo}</span>
)}
</div>
</div>
{attackerPrimaryImageUrl && (
<WdTooltipWrapper content={attackerPrimaryTooltip} position={TooltipPosition.top}>
<WdTooltipWrapper
content={attackerPrimaryTooltip}
position={TooltipPosition.top}
>
<a
href={zkillLink('kill', killmail_id)}
target="_blank"
@@ -157,14 +211,17 @@ export const CompactKillRow: React.FC<CompactKillRowProps> = ({ killDetails, sys
<img
src={attackerPrimaryImageUrl}
alt={attackerIsNpc ? 'NpcShip' : 'AttackerPrimaryLogo'}
className={clsx(classes.killRowImage, 'w-full h-full object-contain')}
className={clsx(
classes.killRowImage,
'w-full h-full object-contain'
)}
/>
{attackerSubscript && (
<span
className={clsx(
classes.attackerCountLabel,
attackerSubscript.cssClass,
'text-[0.6rem] leading-none px-[2px]',
'text-[0.6rem] leading-none px-[2px]'
)}
style={{ bottom: 0, right: 0 }}
>

View File

@@ -1,4 +1,3 @@
// FullKillRow.tsx
import React from 'react';
import clsx from 'clsx';
import { DetailedKill } from '@/hooks/Mapper/types/kills';
@@ -23,10 +22,15 @@ export interface FullKillRowProps {
onlyOneSystem: boolean;
}
export const FullKillRow: React.FC<FullKillRowProps> = ({ killDetails, systemName, onlyOneSystem }) => {
export const FullKillRow: React.FC<FullKillRowProps> = ({
killDetails,
systemName,
onlyOneSystem,
}) => {
const {
killmail_id = 0,
// Victim
victim_char_name = '',
victim_alliance_ticker = '',
victim_corp_ticker = '',
@@ -38,6 +42,7 @@ export const FullKillRow: React.FC<FullKillRowProps> = ({ killDetails, systemNam
victim_corp_name = '',
victim_alliance_name = '',
// Attacker
final_blow_char_id = 0,
final_blow_char_name = '',
final_blow_alliance_ticker = '',
@@ -54,50 +59,93 @@ export const FullKillRow: React.FC<FullKillRowProps> = ({ killDetails, systemNam
} = killDetails || {};
const attackerIsNpc = final_blow_char_id === 0;
const victimAffiliation = victim_alliance_ticker || victim_corp_ticker;
const attackerAffiliation = attackerIsNpc ? '' : final_blow_alliance_ticker || final_blow_corp_ticker || '';
const victimAffiliation =
victim_alliance_ticker || victim_corp_ticker || null;
const attackerAffiliation = attackerIsNpc
? ''
: final_blow_alliance_ticker || final_blow_corp_ticker || '';
const killValueFormatted = total_value !== null && total_value > 0 ? `${formatISK(total_value)} ISK` : null;
const killValueFormatted =
total_value != null && total_value > 0 ? `${formatISK(total_value)} ISK` : null;
const killTimeAgo = kill_time ? formatTimeMixed(kill_time) : '0h ago';
const { victimPortraitUrl, victimCorpLogoUrl, victimAllianceLogoUrl } = buildVictimImageUrls({
// Victim images, now also pulling victimShipUrl
const {
victimPortraitUrl,
victimCorpLogoUrl,
victimAllianceLogoUrl,
victimShipUrl,
} = buildVictimImageUrls({
victim_char_id,
victim_ship_type_id,
victim_corp_id,
victim_alliance_id,
});
const { attackerPortraitUrl, attackerCorpLogoUrl, attackerAllianceLogoUrl } = buildAttackerImageUrls({
// Attacker images
const {
attackerPortraitUrl,
attackerCorpLogoUrl,
attackerAllianceLogoUrl,
} = buildAttackerImageUrls({
final_blow_char_id,
final_blow_corp_id,
final_blow_alliance_id,
});
const { url: victimPrimaryImageUrl, tooltip: victimPrimaryTooltip } = getPrimaryLogoAndTooltip(
victimAllianceLogoUrl,
victimCorpLogoUrl,
victim_alliance_name,
victim_corp_name,
'Victim',
);
// Primary corp/alliance logo for victim
const { url: victimPrimaryImageUrl, tooltip: victimPrimaryTooltip } =
getPrimaryLogoAndTooltip(
victimAllianceLogoUrl,
victimCorpLogoUrl,
victim_alliance_name,
victim_corp_name,
'Victim'
);
const { url: attackerPrimaryImageUrl, tooltip: attackerPrimaryTooltip } = getAttackerPrimaryImageAndTooltip(
attackerIsNpc,
attackerAllianceLogoUrl,
attackerCorpLogoUrl,
final_blow_alliance_name,
final_blow_corp_name,
final_blow_ship_type_id || 0,
);
// Primary image for attacker => NPC => ship, else corp/alliance
const { url: attackerPrimaryImageUrl, tooltip: attackerPrimaryTooltip } =
getAttackerPrimaryImageAndTooltip(
attackerIsNpc,
attackerAllianceLogoUrl,
attackerCorpLogoUrl,
final_blow_alliance_name,
final_blow_corp_name,
final_blow_ship_type_id
);
const attackerSubscript = getAttackerSubscript(killDetails);
return (
<div className={clsx(classes.killRowContainer, 'h-18 w-full justify-between items-start text-sm py-[4px]')}>
<div
className={clsx(
classes.killRowContainer,
'h-18 w-full justify-between items-start text-sm py-[4px]'
)}
>
{/* ---------------- Victim Side ---------------- */}
<div className="flex items-start gap-1 min-w-0 h-full">
{/* Victim top-level logo (corp or alliance), with tooltip */}
{victimShipUrl && (
<div className="relative shrink-0 w-14 h-14 overflow-hidden">
<a
href={zkillLink('kill', killmail_id)}
target="_blank"
rel="noopener noreferrer"
className="block w-full h-full"
>
<img
src={victimShipUrl}
alt="VictimShip"
className={clsx(classes.killRowImage, 'w-full h-full object-contain')}
/>
</a>
</div>
)}
{victimPrimaryImageUrl && (
<WdTooltipWrapper content={victimPrimaryTooltip} position={TooltipPosition.top}>
<WdTooltipWrapper
content={victimPrimaryTooltip}
position={TooltipPosition.top}
>
<div className="relative shrink-0 w-14 h-14 overflow-hidden">
<a
href={zkillLink('kill', killmail_id)}
@@ -114,26 +162,34 @@ export const FullKillRow: React.FC<FullKillRowProps> = ({ killDetails, systemNam
</div>
</WdTooltipWrapper>
)}
<VictimRowSubInfo
victimCharName={victim_char_name}
victimCharacterId={victim_char_id}
victimPortraitUrl={victimPortraitUrl}
/>
<div className="flex flex-col text-stone-200 leading-4 min-w-0 overflow-hidden">
<div className="truncate">
<span className="font-semibold">{victim_char_name}</span>
{victimAffiliation && <span className="ml-1 text-stone-400">/ {victimAffiliation}</span>}
{victimAffiliation && (
<span className="ml-1 text-stone-400">/ {victimAffiliation}</span>
)}
</div>
<div className="truncate text-stone-300">
{victim_ship_name}
{killValueFormatted && (
<>
<span className="ml-1 text-stone-400">/</span>
<span className="ml-1 text-green-400">{killValueFormatted}</span>
<span className="ml-1 text-green-400">
{killValueFormatted}
</span>
</>
)}
</div>
<div className="truncate text-stone-400">{!onlyOneSystem && systemName && <span>{systemName}</span>}</div>
<div className="truncate text-stone-400">
{!onlyOneSystem && systemName && <span>{systemName}</span>}
</div>
</div>
</div>
@@ -142,7 +198,9 @@ export const FullKillRow: React.FC<FullKillRowProps> = ({ killDetails, systemNam
{!attackerIsNpc && (
<div className="truncate font-semibold">
{final_blow_char_name}
{attackerAffiliation && <span className="ml-1 text-stone-400">/ {attackerAffiliation}</span>}
{attackerAffiliation && (
<span className="ml-1 text-stone-400">/ {attackerAffiliation}</span>
)}
</div>
)}
{!attackerIsNpc && final_blow_ship_name && (
@@ -151,7 +209,7 @@ export const FullKillRow: React.FC<FullKillRowProps> = ({ killDetails, systemNam
<div className="truncate text-red-400">{killTimeAgo}</div>
</div>
{!attackerIsNpc && attackerPortraitUrl && final_blow_char_id !== null && final_blow_char_id > 0 && (
{!attackerIsNpc && attackerPortraitUrl && final_blow_char_id && final_blow_char_id > 0 && (
<div className="relative shrink-0 w-14 h-14 overflow-hidden">
<a
href={zkillLink('character', final_blow_char_id)}
@@ -169,7 +227,10 @@ export const FullKillRow: React.FC<FullKillRowProps> = ({ killDetails, systemNam
)}
{attackerPrimaryImageUrl && (
<WdTooltipWrapper content={attackerPrimaryTooltip} position={TooltipPosition.top}>
<WdTooltipWrapper
content={attackerPrimaryTooltip}
position={TooltipPosition.top}
>
<div className="relative shrink-0 w-14 h-14 overflow-hidden">
<a
href={zkillLink('kill', killmail_id)}
@@ -183,7 +244,12 @@ export const FullKillRow: React.FC<FullKillRowProps> = ({ killDetails, systemNam
className={clsx(classes.killRowImage, 'w-full h-full object-contain')}
/>
{attackerSubscript && (
<span className={clsx(attackerSubscript.cssClass, classes.attackerCountLabel)}>
<span
className={clsx(
attackerSubscript.cssClass,
classes.attackerCountLabel
)}
>
{attackerSubscript.label}
</span>
)}

View File

@@ -96,7 +96,7 @@ export function getAttackerPrimaryImageAndTooltip(
corpUrl: string | null,
allianceName: string,
corpName: string,
finalBlowShipTypeId: number,
finalBlowShipTypeId: number | null,
npcFallback: string = 'NPC Attacker',
) {
if (isNpc) {