mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-12-12 10:45:54 +00:00
fix: restore styling for local characters list (#152)
This commit is contained in:
@@ -1,2 +1,3 @@
|
|||||||
export * from './useSystemInfo';
|
export * from './useSystemInfo';
|
||||||
export * from './useGetOwnOnlineCharacters';
|
export * from './useGetOwnOnlineCharacters';
|
||||||
|
export * from './useElementWidth';
|
||||||
|
|||||||
43
assets/js/hooks/Mapper/components/hooks/useElementWidth.ts
Normal file
43
assets/js/hooks/Mapper/components/hooks/useElementWidth.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { useState, useLayoutEffect, RefObject } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* useElementWidth
|
||||||
|
*
|
||||||
|
* A custom hook that accepts a ref to an HTML element and returns its current width.
|
||||||
|
* It uses a ResizeObserver and window resize listener to update the width when necessary.
|
||||||
|
*
|
||||||
|
* @param ref - A RefObject pointing to an HTML element.
|
||||||
|
* @returns The current width of the element.
|
||||||
|
*/
|
||||||
|
export function useElementWidth<T extends HTMLElement>(ref: RefObject<T>): number {
|
||||||
|
const [width, setWidth] = useState<number>(0);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
const updateWidth = () => {
|
||||||
|
if (ref.current) {
|
||||||
|
const newWidth = ref.current.getBoundingClientRect().width;
|
||||||
|
if (newWidth > 0) {
|
||||||
|
setWidth(newWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
updateWidth(); // Initial measurement
|
||||||
|
|
||||||
|
const observer = new ResizeObserver(() => {
|
||||||
|
const id = setTimeout(updateWidth, 100);
|
||||||
|
return () => clearTimeout(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ref.current) {
|
||||||
|
observer.observe(ref.current);
|
||||||
|
}
|
||||||
|
window.addEventListener("resize", updateWidth);
|
||||||
|
return () => {
|
||||||
|
observer.disconnect();
|
||||||
|
window.removeEventListener("resize", updateWidth);
|
||||||
|
};
|
||||||
|
}, [ref]);
|
||||||
|
|
||||||
|
return width;
|
||||||
|
}
|
||||||
@@ -1,16 +1,13 @@
|
|||||||
import { useMemo, useRef } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
|
import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
|
||||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||||
import clsx from 'clsx';
|
import { sortCharacters } from '@/hooks/Mapper/components/mapInterface/helpers/sortCharacters';
|
||||||
import { LayoutEventBlocker, WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
|
|
||||||
import { sortCharacters } from '@/hooks/Mapper/components/mapInterface/helpers/sortCharacters.ts';
|
|
||||||
import { useMapCheckPermissions, useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
import { useMapCheckPermissions, useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||||
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
|
import { UserPermission } from '@/hooks/Mapper/types/permissions';
|
||||||
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
|
|
||||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
|
||||||
import { LocalCharactersList } from './components/LocalCharactersList';
|
import { LocalCharactersList } from './components/LocalCharactersList';
|
||||||
import { useLocalCharactersItemTemplate } from './hooks/useLocalCharacters';
|
import { useLocalCharactersItemTemplate } from './hooks/useLocalCharacters';
|
||||||
import { useLocalCharacterWidgetSettings } from './hooks/useLocalWidgetSettings';
|
import { useLocalCharacterWidgetSettings } from './hooks/useLocalWidgetSettings';
|
||||||
|
import { LocalCharactersHeader } from './components/LocalCharactersHeader';
|
||||||
|
|
||||||
export const LocalCharacters = () => {
|
export const LocalCharacters = () => {
|
||||||
const {
|
const {
|
||||||
@@ -18,14 +15,12 @@ export const LocalCharacters = () => {
|
|||||||
} = useMapRootState();
|
} = useMapRootState();
|
||||||
|
|
||||||
const [settings, setSettings] = useLocalCharacterWidgetSettings();
|
const [settings, setSettings] = useLocalCharacterWidgetSettings();
|
||||||
|
|
||||||
const [systemId] = selectedSystems;
|
const [systemId] = selectedSystems;
|
||||||
const restrictOfflineShowing = useMapGetOption('restrict_offline_showing');
|
const restrictOfflineShowing = useMapGetOption("restrict_offline_showing");
|
||||||
const isAdminOrManager = useMapCheckPermissions([UserPermission.MANAGE_MAP]);
|
const isAdminOrManager = useMapCheckPermissions([UserPermission.MANAGE_MAP]);
|
||||||
|
|
||||||
const showOffline = useMemo(
|
const showOffline = useMemo(
|
||||||
() => !restrictOfflineShowing || isAdminOrManager,
|
() => !restrictOfflineShowing || isAdminOrManager,
|
||||||
[isAdminOrManager, restrictOfflineShowing],
|
[isAdminOrManager, restrictOfflineShowing]
|
||||||
);
|
);
|
||||||
|
|
||||||
const sorted = useMemo(() => {
|
const sorted = useMemo(() => {
|
||||||
@@ -42,7 +37,6 @@ export const LocalCharacters = () => {
|
|||||||
if (!showOffline || !settings.showOffline) {
|
if (!showOffline || !settings.showOffline) {
|
||||||
return filtered.filter(c => c.online);
|
return filtered.filter(c => c.online);
|
||||||
}
|
}
|
||||||
|
|
||||||
return filtered;
|
return filtered;
|
||||||
}, [
|
}, [
|
||||||
characters,
|
characters,
|
||||||
@@ -58,64 +52,18 @@ export const LocalCharacters = () => {
|
|||||||
const isNotSelectedSystem = selectedSystems.length !== 1;
|
const isNotSelectedSystem = selectedSystems.length !== 1;
|
||||||
const showList = sorted.length > 0 && selectedSystems.length === 1;
|
const showList = sorted.length > 0 && selectedSystems.length === 1;
|
||||||
|
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
|
||||||
const compact = useMaxWidth(ref, 145);
|
|
||||||
|
|
||||||
const itemTemplate = useLocalCharactersItemTemplate(settings.showShipName);
|
const itemTemplate = useLocalCharactersItemTemplate(settings.showShipName);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Widget
|
<Widget
|
||||||
label={
|
label={
|
||||||
<div className="flex w-full items-center" ref={ref}>
|
<LocalCharactersHeader
|
||||||
<div className="flex-shrink-0 select-none mr-2">
|
sortedCount={sorted.length}
|
||||||
Local{showList ? ` [${sorted.length}]` : ''}
|
showList={showList}
|
||||||
</div>
|
showOffline={showOffline}
|
||||||
<div className="flex-grow overflow-hidden">
|
settings={settings}
|
||||||
<LayoutEventBlocker className="flex items-center gap-2 justify-end">
|
setSettings={setSettings}
|
||||||
{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">
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{isNotSelectedSystem && (
|
{isNotSelectedSystem && (
|
||||||
@@ -123,19 +71,17 @@ export const LocalCharacters = () => {
|
|||||||
System is not selected
|
System is not selected
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isNobodyHere && !isNotSelectedSystem && (
|
{isNobodyHere && !isNotSelectedSystem && (
|
||||||
<div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm">
|
<div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm">
|
||||||
Nobody here
|
Nobody here
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{showList && (
|
{showList && (
|
||||||
<LocalCharactersList
|
<LocalCharactersList
|
||||||
items={sorted}
|
items={sorted}
|
||||||
itemSize={settings.compact ? 26 : 41}
|
itemSize={settings.compact ? 26 : 41}
|
||||||
itemTemplate={itemTemplate}
|
itemTemplate={itemTemplate}
|
||||||
containerClassName="w-full h-full overflow-x-hidden overflow-y-auto"
|
containerClassName="w-full h-full overflow-x-hidden overflow-y-auto custom-scrollbar select-none"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Widget>
|
</Widget>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// .VirtualScroller {
|
.VirtualScroller {
|
||||||
// height: 100% !important;
|
height: 100% !important;
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import React, { useRef } from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth';
|
||||||
|
import { LayoutEventBlocker, WdResponsiveCheckbox, WdDisplayMode } from '@/hooks/Mapper/components/ui-kit';
|
||||||
|
import { useElementWidth } from '@/hooks/Mapper/components/hooks';
|
||||||
|
|
||||||
|
interface LocalCharactersHeaderProps {
|
||||||
|
sortedCount: number;
|
||||||
|
showList: boolean;
|
||||||
|
showOffline: boolean;
|
||||||
|
settings: {
|
||||||
|
compact: boolean;
|
||||||
|
showOffline: boolean;
|
||||||
|
showShipName: boolean;
|
||||||
|
};
|
||||||
|
setSettings: (fn: (prev: any) => any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LocalCharactersHeader: React.FC<LocalCharactersHeaderProps> = ({
|
||||||
|
sortedCount,
|
||||||
|
showList,
|
||||||
|
showOffline,
|
||||||
|
settings,
|
||||||
|
setSettings,
|
||||||
|
}) => {
|
||||||
|
const headerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const headerWidth = useElementWidth(headerRef) || 300;
|
||||||
|
|
||||||
|
const reservedWidth = 100;
|
||||||
|
const availableWidthForCheckboxes = Math.max(headerWidth - reservedWidth, 0);
|
||||||
|
|
||||||
|
let displayMode: WdDisplayMode = "full";
|
||||||
|
if (availableWidthForCheckboxes >= 150) {
|
||||||
|
displayMode = "full";
|
||||||
|
} else if (availableWidthForCheckboxes >= 100) {
|
||||||
|
displayMode = "abbr";
|
||||||
|
} else {
|
||||||
|
displayMode = "checkbox";
|
||||||
|
}
|
||||||
|
|
||||||
|
const compact = useMaxWidth(headerRef, 145);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex w-full items-center text-xs" ref={headerRef}>
|
||||||
|
<div className="flex-shrink-0 select-none mr-2">
|
||||||
|
Local{showList ? ` [${sortedCount}]` : ""}
|
||||||
|
</div>
|
||||||
|
<div className="flex-grow overflow-hidden">
|
||||||
|
<LayoutEventBlocker className="flex items-center gap-2 justify-end">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{showOffline && (
|
||||||
|
<WdResponsiveCheckbox
|
||||||
|
tooltipContent="Show offline characters in system"
|
||||||
|
size="xs"
|
||||||
|
labelFull="Show offline"
|
||||||
|
labelAbbreviated="Offline"
|
||||||
|
value={settings.showOffline}
|
||||||
|
onChange={() =>
|
||||||
|
setSettings((prev: any) => ({ ...prev, showOffline: !prev.showOffline }))
|
||||||
|
}
|
||||||
|
classNameLabel={clsx("whitespace-nowrap text-stone-400 hover:text-stone-200 transition duration-300", { truncate: compact })}
|
||||||
|
displayMode={displayMode}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{settings.compact && (
|
||||||
|
<WdResponsiveCheckbox
|
||||||
|
tooltipContent="Show ship name in compact rows"
|
||||||
|
size="xs"
|
||||||
|
labelFull="Show ship name"
|
||||||
|
labelAbbreviated="Ship name"
|
||||||
|
value={settings.showShipName}
|
||||||
|
onChange={() =>
|
||||||
|
setSettings((prev: any) => ({ ...prev, showShipName: !prev.showShipName }))
|
||||||
|
}
|
||||||
|
classNameLabel={clsx("whitespace-nowrap text-stone-400 hover:text-stone-200 transition duration-300", { truncate: compact })}
|
||||||
|
displayMode={displayMode}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
className={clsx("w-4 h-4 cursor-pointer", {
|
||||||
|
"hero-bars-2": settings.compact,
|
||||||
|
"hero-bars-3": !settings.compact,
|
||||||
|
})}
|
||||||
|
onClick={() => setSettings((prev: any) => ({ ...prev, compact: !prev.compact }))}
|
||||||
|
/>
|
||||||
|
</LayoutEventBlocker>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -15,7 +15,6 @@ export const SystemKills: React.FC = () => {
|
|||||||
} = useMapRootState();
|
} = useMapRootState();
|
||||||
|
|
||||||
const [systemId] = selectedSystems || [];
|
const [systemId] = selectedSystems || [];
|
||||||
|
|
||||||
const [settingsDialogVisible, setSettingsDialogVisible] = useState(false);
|
const [settingsDialogVisible, setSettingsDialogVisible] = useState(false);
|
||||||
|
|
||||||
const systemNameMap = useMemo(() => {
|
const systemNameMap = useMemo(() => {
|
||||||
@@ -41,7 +40,9 @@ export const SystemKills: React.FC = () => {
|
|||||||
const filteredKills = useMemo(() => {
|
const filteredKills = useMemo(() => {
|
||||||
if (!settings.whOnly || !visible) return kills;
|
if (!settings.whOnly || !visible) return kills;
|
||||||
return kills.filter(kill => {
|
return kills.filter(kill => {
|
||||||
const system = systems.find(sys => sys.system_static_info.solar_system_id === kill.solar_system_id);
|
const system = systems.find(
|
||||||
|
sys => sys.system_static_info.solar_system_id === kill.solar_system_id
|
||||||
|
);
|
||||||
if (!system) {
|
if (!system) {
|
||||||
console.warn(`System with id ${kill.solar_system_id} not found.`);
|
console.warn(`System with id ${kill.solar_system_id} not found.`);
|
||||||
return false;
|
return false;
|
||||||
@@ -55,60 +56,62 @@ export const SystemKills: React.FC = () => {
|
|||||||
<div className="flex flex-col flex-1 min-h-0">
|
<div className="flex flex-col flex-1 min-h-0">
|
||||||
<Widget
|
<Widget
|
||||||
label={
|
label={
|
||||||
<KillsHeader systemId={systemId} onOpenSettings={() => setSettingsDialogVisible(true)} />
|
<KillsHeader
|
||||||
|
systemId={systemId}
|
||||||
|
onOpenSettings={() => setSettingsDialogVisible(true)}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{!isSubscriptionActive && (
|
<div className="relative h-full">
|
||||||
<div className="w-full h-full flex justify-center items-center select-none text-center text-stone-400/80 text-sm">
|
{!isSubscriptionActive ? (
|
||||||
Kills available with 'Active' map subscription only (contact map administrators)
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
</div>
|
<span className="select-none text-center text-stone-400/80 text-sm">
|
||||||
)}
|
Kills available with 'Active' map subscription only (contact map administrators)
|
||||||
{isSubscriptionActive && (
|
</span>
|
||||||
<>
|
</div>
|
||||||
{isNothingSelected && (
|
) : isNothingSelected ? (
|
||||||
<div className="w-full h-full flex justify-center items-center select-none text-center text-stone-400/80 text-sm">
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
|
<span className="select-none text-center text-stone-400/80 text-sm">
|
||||||
No system selected (or toggle “Show all systems”)
|
No system selected (or toggle “Show all systems”)
|
||||||
</div>
|
</span>
|
||||||
)}
|
</div>
|
||||||
|
) : showLoading ? (
|
||||||
{!isNothingSelected && showLoading && (
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
<div className="w-full h-full flex justify-center items-center select-none text-center text-stone-400/80 text-sm">
|
<span className="select-none text-center text-stone-400/80 text-sm">
|
||||||
Loading Kills...
|
Loading Kills...
|
||||||
</div>
|
</span>
|
||||||
)}
|
</div>
|
||||||
|
) : error ? (
|
||||||
{!isNothingSelected && !showLoading && error && (
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
<div className="w-full h-full flex justify-center items-center select-none text-center text-red-400 text-sm">
|
<span className="select-none text-center text-red-400 text-sm">
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</span>
|
||||||
)}
|
</div>
|
||||||
|
) : !filteredKills || filteredKills.length === 0 ? (
|
||||||
{!isNothingSelected &&
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
!showLoading &&
|
<span className="select-none text-center text-stone-400/80 text-sm">
|
||||||
!error &&
|
No kills found
|
||||||
(!filteredKills || filteredKills.length === 0) && (
|
</span>
|
||||||
<div className="w-full h-full flex justify-center items-center select-none text-center text-stone-400/80 text-sm">
|
</div>
|
||||||
No kills found
|
) : (
|
||||||
</div>
|
<div className="h-full overflow-y-auto">
|
||||||
)}
|
<SystemKillsContent
|
||||||
|
key={settings.compact ? 'compact' : 'normal'}
|
||||||
{!isNothingSelected && !showLoading && !error && (
|
kills={filteredKills}
|
||||||
<div className="flex-1 flex flex-col overflow-y-auto">
|
systemNameMap={systemNameMap}
|
||||||
<SystemKillsContent
|
compact={settings.compact}
|
||||||
key={settings.compact ? 'compact' : 'normal'}
|
onlyOneSystem={!visible}
|
||||||
kills={filteredKills}
|
/>
|
||||||
systemNameMap={systemNameMap}
|
</div>
|
||||||
compact={settings.compact}
|
)}
|
||||||
onlyOneSystem={!visible}
|
</div>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Widget>
|
</Widget>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<KillsSettingsDialog visible={settingsDialogVisible} setVisible={setSettingsDialogVisible} />
|
<KillsSettingsDialog
|
||||||
|
visible={settingsDialogVisible}
|
||||||
|
setVisible={setSettingsDialogVisible}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import clsx from 'clsx';
|
|
||||||
import { zkillLink } from '../helpers';
|
|
||||||
import classes from './SystemKillRow.module.scss';
|
|
||||||
|
|
||||||
interface AttackerRowSubInfoProps {
|
|
||||||
finalBlowCharId: number | null | undefined;
|
|
||||||
finalBlowCharName?: string;
|
|
||||||
attackerPortraitUrl: string | null;
|
|
||||||
|
|
||||||
finalBlowCorpId: number | null | undefined;
|
|
||||||
finalBlowCorpName?: string;
|
|
||||||
attackerCorpLogoUrl: string | null;
|
|
||||||
|
|
||||||
finalBlowAllianceId: number | null | undefined;
|
|
||||||
finalBlowAllianceName?: string;
|
|
||||||
attackerAllianceLogoUrl: string | null;
|
|
||||||
|
|
||||||
containerHeight?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const AttackerRowSubInfo: React.FC<AttackerRowSubInfoProps> = ({
|
|
||||||
finalBlowCharId = 0,
|
|
||||||
finalBlowCharName,
|
|
||||||
attackerPortraitUrl,
|
|
||||||
containerHeight = 8,
|
|
||||||
}) => {
|
|
||||||
if (!attackerPortraitUrl || finalBlowCharId === null || finalBlowCharId <= 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const containerClass = `h-${containerHeight}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={clsx('flex items-start gap-1', containerClass)}>
|
|
||||||
<div className="relative shrink-0 w-auto h-full overflow-hidden">
|
|
||||||
<a
|
|
||||||
href={zkillLink('character', finalBlowCharId)}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="block h-full"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={attackerPortraitUrl}
|
|
||||||
alt={finalBlowCharName || 'AttackerPortrait'}
|
|
||||||
className={clsx(classes.killRowImage, 'h-full w-auto object-contain')}
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -29,8 +29,7 @@ export const FullKillRow: React.FC<FullKillRowProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
killmail_id = 0,
|
killmail_id = 0,
|
||||||
|
// Victim data
|
||||||
// Victim
|
|
||||||
victim_char_name = '',
|
victim_char_name = '',
|
||||||
victim_alliance_ticker = '',
|
victim_alliance_ticker = '',
|
||||||
victim_corp_ticker = '',
|
victim_corp_ticker = '',
|
||||||
@@ -41,8 +40,7 @@ export const FullKillRow: React.FC<FullKillRowProps> = ({
|
|||||||
victim_ship_type_id = 0,
|
victim_ship_type_id = 0,
|
||||||
victim_corp_name = '',
|
victim_corp_name = '',
|
||||||
victim_alliance_name = '',
|
victim_alliance_name = '',
|
||||||
|
// Attacker data
|
||||||
// Attacker
|
|
||||||
final_blow_char_id = 0,
|
final_blow_char_id = 0,
|
||||||
final_blow_char_name = '',
|
final_blow_char_name = '',
|
||||||
final_blow_alliance_ticker = '',
|
final_blow_alliance_ticker = '',
|
||||||
@@ -53,14 +51,12 @@ export const FullKillRow: React.FC<FullKillRowProps> = ({
|
|||||||
final_blow_alliance_id = 0,
|
final_blow_alliance_id = 0,
|
||||||
final_blow_ship_name = '',
|
final_blow_ship_name = '',
|
||||||
final_blow_ship_type_id = 0,
|
final_blow_ship_type_id = 0,
|
||||||
|
|
||||||
total_value = 0,
|
total_value = 0,
|
||||||
kill_time = '',
|
kill_time = '',
|
||||||
} = killDetails || {};
|
} = killDetails || {};
|
||||||
|
|
||||||
const attackerIsNpc = final_blow_char_id === 0;
|
const attackerIsNpc = final_blow_char_id === 0;
|
||||||
const victimAffiliation =
|
const victimAffiliation = victim_alliance_ticker || victim_corp_ticker || null;
|
||||||
victim_alliance_ticker || victim_corp_ticker || null;
|
|
||||||
const attackerAffiliation = attackerIsNpc
|
const attackerAffiliation = attackerIsNpc
|
||||||
? ''
|
? ''
|
||||||
: final_blow_alliance_ticker || final_blow_corp_ticker || '';
|
: final_blow_alliance_ticker || final_blow_corp_ticker || '';
|
||||||
@@ -69,7 +65,7 @@ export const FullKillRow: React.FC<FullKillRowProps> = ({
|
|||||||
total_value != null && total_value > 0 ? `${formatISK(total_value)} ISK` : null;
|
total_value != null && total_value > 0 ? `${formatISK(total_value)} ISK` : null;
|
||||||
const killTimeAgo = kill_time ? formatTimeMixed(kill_time) : '0h ago';
|
const killTimeAgo = kill_time ? formatTimeMixed(kill_time) : '0h ago';
|
||||||
|
|
||||||
// Victim images, now also pulling victimShipUrl
|
// Build victim images
|
||||||
const {
|
const {
|
||||||
victimPortraitUrl,
|
victimPortraitUrl,
|
||||||
victimCorpLogoUrl,
|
victimCorpLogoUrl,
|
||||||
@@ -81,7 +77,8 @@ export const FullKillRow: React.FC<FullKillRowProps> = ({
|
|||||||
victim_corp_id,
|
victim_corp_id,
|
||||||
victim_alliance_id,
|
victim_alliance_id,
|
||||||
});
|
});
|
||||||
// Attacker images
|
|
||||||
|
// Build attacker images
|
||||||
const {
|
const {
|
||||||
attackerPortraitUrl,
|
attackerPortraitUrl,
|
||||||
attackerCorpLogoUrl,
|
attackerCorpLogoUrl,
|
||||||
@@ -92,7 +89,7 @@ export const FullKillRow: React.FC<FullKillRowProps> = ({
|
|||||||
final_blow_alliance_id,
|
final_blow_alliance_id,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Primary corp/alliance logo for victim
|
// Primary image for victim
|
||||||
const { url: victimPrimaryImageUrl, tooltip: victimPrimaryTooltip } =
|
const { url: victimPrimaryImageUrl, tooltip: victimPrimaryTooltip } =
|
||||||
getPrimaryLogoAndTooltip(
|
getPrimaryLogoAndTooltip(
|
||||||
victimAllianceLogoUrl,
|
victimAllianceLogoUrl,
|
||||||
@@ -102,7 +99,7 @@ export const FullKillRow: React.FC<FullKillRowProps> = ({
|
|||||||
'Victim'
|
'Victim'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Primary image for attacker => NPC => ship, else corp/alliance
|
// Primary image for attacker
|
||||||
const { url: attackerPrimaryImageUrl, tooltip: attackerPrimaryTooltip } =
|
const { url: attackerPrimaryImageUrl, tooltip: attackerPrimaryTooltip } =
|
||||||
getAttackerPrimaryImageAndTooltip(
|
getAttackerPrimaryImageAndTooltip(
|
||||||
attackerIsNpc,
|
attackerIsNpc,
|
||||||
@@ -119,34 +116,15 @@ export const FullKillRow: React.FC<FullKillRowProps> = ({
|
|||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
classes.killRowContainer,
|
classes.killRowContainer,
|
||||||
'h-18 w-full justify-between items-start text-sm py-[4px]'
|
'w-full text-sm py-1 px-2',
|
||||||
|
'flex flex-col sm:flex-row'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{/* ---------------- Victim Side ---------------- */}
|
<div className="w-full flex flex-col sm:flex-row items-start gap-2">
|
||||||
<div className="flex items-start gap-1 min-w-0 h-full">
|
{/* Victim Section */}
|
||||||
{victimShipUrl && (
|
<div className="flex items-start gap-1 min-w-0">
|
||||||
<div className="relative shrink-0 w-14 h-14 overflow-hidden">
|
{victimShipUrl && (
|
||||||
<a
|
<div className="relative shrink-0 w-12 h-12 sm:w-14 sm:h-14 overflow-hidden">
|
||||||
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}
|
|
||||||
>
|
|
||||||
<div className="relative shrink-0 w-14 h-14 overflow-hidden">
|
|
||||||
<a
|
<a
|
||||||
href={zkillLink('kill', killmail_id)}
|
href={zkillLink('kill', killmail_id)}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@@ -154,109 +132,136 @@ export const FullKillRow: React.FC<FullKillRowProps> = ({
|
|||||||
className="block w-full h-full"
|
className="block w-full h-full"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={victimPrimaryImageUrl}
|
src={victimShipUrl}
|
||||||
alt="VictimPrimaryLogo"
|
alt="VictimShip"
|
||||||
className={clsx(classes.killRowImage, 'w-full h-full object-contain')}
|
className={clsx(
|
||||||
|
classes.killRowImage,
|
||||||
|
'w-full h-full object-contain'
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</WdTooltipWrapper>
|
)}
|
||||||
)}
|
{victimPrimaryImageUrl && (
|
||||||
|
<WdTooltipWrapper
|
||||||
<VictimRowSubInfo
|
content={victimPrimaryTooltip}
|
||||||
victimCharName={victim_char_name}
|
position={TooltipPosition.top}
|
||||||
victimCharacterId={victim_char_id}
|
>
|
||||||
victimPortraitUrl={victimPortraitUrl}
|
<div className="relative shrink-0 w-12 h-12 sm:w-14 sm:h-14 overflow-hidden">
|
||||||
/>
|
<a
|
||||||
|
href={zkillLink('kill', killmail_id)}
|
||||||
<div className="flex flex-col text-stone-200 leading-4 min-w-0 overflow-hidden">
|
target="_blank"
|
||||||
<div className="truncate">
|
rel="noopener noreferrer"
|
||||||
<span className="font-semibold">{victim_char_name}</span>
|
className="block w-full h-full"
|
||||||
{victimAffiliation && (
|
>
|
||||||
<span className="ml-1 text-stone-400">/ {victimAffiliation}</span>
|
<img
|
||||||
)}
|
src={victimPrimaryImageUrl}
|
||||||
</div>
|
alt="VictimPrimaryLogo"
|
||||||
<div className="truncate text-stone-300">
|
className={clsx(
|
||||||
{victim_ship_name}
|
classes.killRowImage,
|
||||||
{killValueFormatted && (
|
'w-full h-full object-contain'
|
||||||
<>
|
)}
|
||||||
<span className="ml-1 text-stone-400">/</span>
|
/>
|
||||||
<span className="ml-1 text-green-400">
|
</a>
|
||||||
{killValueFormatted}
|
</div>
|
||||||
</span>
|
</WdTooltipWrapper>
|
||||||
</>
|
)}
|
||||||
)}
|
<VictimRowSubInfo
|
||||||
</div>
|
victimCharName={victim_char_name}
|
||||||
<div className="truncate text-stone-400">
|
victimCharacterId={victim_char_id}
|
||||||
{!onlyOneSystem && systemName && <span>{systemName}</span>}
|
victimPortraitUrl={victimPortraitUrl}
|
||||||
</div>
|
/>
|
||||||
</div>
|
<div className="flex flex-col text-stone-200 leading-4 min-w-0 overflow-hidden">
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-start gap-1 min-w-0 h-full">
|
|
||||||
<div className="flex flex-col items-end leading-4 min-w-0 overflow-hidden text-right">
|
|
||||||
{!attackerIsNpc && (
|
|
||||||
<div className="truncate font-semibold">
|
<div className="truncate font-semibold">
|
||||||
{final_blow_char_name}
|
{victim_char_name}
|
||||||
{attackerAffiliation && (
|
{victimAffiliation && (
|
||||||
<span className="ml-1 text-stone-400">/ {attackerAffiliation}</span>
|
<span className="ml-1 text-stone-400">/ {victimAffiliation}</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
<div className="truncate text-stone-300">
|
||||||
{!attackerIsNpc && final_blow_ship_name && (
|
{victim_ship_name}
|
||||||
<div className="truncate text-stone-300">{final_blow_ship_name}</div>
|
{killValueFormatted && (
|
||||||
)}
|
<>
|
||||||
<div className="truncate text-red-400">{killTimeAgo}</div>
|
<span className="ml-1 text-stone-400">/</span>
|
||||||
</div>
|
<span className="ml-1 text-green-400">{killValueFormatted}</span>
|
||||||
|
</>
|
||||||
{!attackerIsNpc && attackerPortraitUrl && final_blow_char_id && final_blow_char_id > 0 && (
|
)}
|
||||||
<div className="relative shrink-0 w-14 h-14 overflow-hidden">
|
</div>
|
||||||
<a
|
<div className="truncate text-stone-400">
|
||||||
href={zkillLink('character', final_blow_char_id)}
|
{!onlyOneSystem && systemName && <span>{systemName}</span>}
|
||||||
target="_blank"
|
</div>
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="block w-full h-full"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={attackerPortraitUrl}
|
|
||||||
alt="AttackerPortrait"
|
|
||||||
className={clsx(classes.killRowImage, 'w-full h-full object-contain')}
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
|
{/* Attacker Section */}
|
||||||
{attackerPrimaryImageUrl && (
|
<div className="flex items-start gap-1 min-w-0 sm:ml-auto">
|
||||||
<WdTooltipWrapper
|
<div className="flex flex-col items-end leading-4 min-w-0 overflow-hidden text-right">
|
||||||
content={attackerPrimaryTooltip}
|
{!attackerIsNpc && (
|
||||||
position={TooltipPosition.top}
|
<div className="truncate font-semibold">
|
||||||
>
|
{final_blow_char_name}
|
||||||
<div className="relative shrink-0 w-14 h-14 overflow-hidden">
|
{attackerAffiliation && (
|
||||||
|
<span className="ml-1 text-stone-400">/ {attackerAffiliation}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!attackerIsNpc && final_blow_ship_name && (
|
||||||
|
<div className="truncate text-stone-300">{final_blow_ship_name}</div>
|
||||||
|
)}
|
||||||
|
<div className="truncate text-red-400">{killTimeAgo}</div>
|
||||||
|
</div>
|
||||||
|
{(!attackerIsNpc && attackerPortraitUrl && final_blow_char_id > 0) && (
|
||||||
|
<div className="relative shrink-0 w-12 h-12 sm:w-14 sm:h-14 overflow-hidden">
|
||||||
<a
|
<a
|
||||||
href={zkillLink('kill', killmail_id)}
|
href={zkillLink('character', final_blow_char_id)}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="block w-full h-full"
|
className="block w-full h-full"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={attackerPrimaryImageUrl}
|
src={attackerPortraitUrl}
|
||||||
alt={attackerIsNpc ? 'NpcShip' : 'AttackerPrimaryLogo'}
|
alt="AttackerPortrait"
|
||||||
className={clsx(classes.killRowImage, 'w-full h-full object-contain')}
|
className={clsx(
|
||||||
|
classes.killRowImage,
|
||||||
|
'w-full h-full object-contain'
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
{attackerSubscript && (
|
|
||||||
<span
|
|
||||||
className={clsx(
|
|
||||||
attackerSubscript.cssClass,
|
|
||||||
classes.attackerCountLabel
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{attackerSubscript.label}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</WdTooltipWrapper>
|
)}
|
||||||
)}
|
{attackerPrimaryImageUrl && (
|
||||||
|
<WdTooltipWrapper
|
||||||
|
content={attackerPrimaryTooltip}
|
||||||
|
position={TooltipPosition.top}
|
||||||
|
>
|
||||||
|
<div className="relative shrink-0 w-12 h-12 sm:w-14 sm:h-14 overflow-hidden">
|
||||||
|
<a
|
||||||
|
href={zkillLink('kill', killmail_id)}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="block w-full h-full"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={attackerPrimaryImageUrl}
|
||||||
|
alt={attackerIsNpc ? 'NpcShip' : 'AttackerPrimaryLogo'}
|
||||||
|
className={clsx(
|
||||||
|
classes.killRowImage,
|
||||||
|
'w-full h-full object-contain'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{attackerSubscript && (
|
||||||
|
<span
|
||||||
|
className={clsx(
|
||||||
|
attackerSubscript.cssClass,
|
||||||
|
classes.attackerCountLabel
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{attackerSubscript.label}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</WdTooltipWrapper>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
import React from 'react';
|
import React, { useRef } from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
import {
|
import {
|
||||||
LayoutEventBlocker,
|
LayoutEventBlocker,
|
||||||
WdCheckbox,
|
WdResponsiveCheckbox,
|
||||||
WdImgButton,
|
WdImgButton,
|
||||||
TooltipPosition,
|
TooltipPosition,
|
||||||
SystemView,
|
WdDisplayMode,
|
||||||
} from '@/hooks/Mapper/components/ui-kit';
|
} from '@/hooks/Mapper/components/ui-kit';
|
||||||
import { useKillsWidgetSettings } from '../hooks/useKillsWidgetSettings';
|
import { useKillsWidgetSettings } from '../hooks/useKillsWidgetSettings';
|
||||||
import { PrimeIcons } from 'primereact/api';
|
import { PrimeIcons } from 'primereact/api';
|
||||||
|
import { useElementWidth } from '@/hooks/Mapper/components/hooks';
|
||||||
|
|
||||||
interface KillsWidgetHeaderProps {
|
interface KillsHeaderProps {
|
||||||
systemId?: string;
|
systemId?: string;
|
||||||
onOpenSettings: () => void;
|
onOpenSettings: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const KillsHeader: React.FC<KillsWidgetHeaderProps> = ({ systemId, onOpenSettings }) => {
|
export const KillsHeader: React.FC<KillsHeaderProps> = ({ systemId, onOpenSettings }) => {
|
||||||
const [settings, setSettings] = useKillsWidgetSettings();
|
const [settings, setSettings] = useKillsWidgetSettings();
|
||||||
const { showAll } = settings;
|
const { showAll } = settings;
|
||||||
|
|
||||||
@@ -22,35 +24,48 @@ export const KillsHeader: React.FC<KillsWidgetHeaderProps> = ({ systemId, onOpen
|
|||||||
setSettings(prev => ({ ...prev, showAll: !prev.showAll }));
|
setSettings(prev => ({ ...prev, showAll: !prev.showAll }));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const headerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const headerWidth = useElementWidth(headerRef) || 300;
|
||||||
|
|
||||||
|
const reservedWidth = 100;
|
||||||
|
const availableWidth = Math.max(headerWidth - reservedWidth, 0);
|
||||||
|
|
||||||
|
let displayMode: WdDisplayMode = "full";
|
||||||
|
if (availableWidth >= 60) {
|
||||||
|
displayMode = "full";
|
||||||
|
} else {
|
||||||
|
displayMode = "abbr";
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-between items-center text-xs w-full">
|
<div className="flex w-full items-center text-xs" ref={headerRef}>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex-shrink-0 select-none mr-2">
|
||||||
<div className="text-stone-400">
|
Kills{systemId && !showAll && ' in '}
|
||||||
Kills
|
</div>
|
||||||
{systemId && !showAll && ' in '}
|
<div className="flex-grow overflow-hidden">
|
||||||
</div>
|
<LayoutEventBlocker className="flex items-center gap-2 justify-end">
|
||||||
{systemId && !showAll && <SystemView systemId={systemId} className="select-none text-center" hideRegion />}
|
<div className="flex items-center gap-2">
|
||||||
|
<WdResponsiveCheckbox
|
||||||
|
tooltipContent="Show all systems"
|
||||||
|
size="xs"
|
||||||
|
labelFull="Show all systems"
|
||||||
|
labelAbbreviated="All"
|
||||||
|
value={showAll}
|
||||||
|
onChange={onToggleShowAllVisible}
|
||||||
|
classNameLabel={clsx("whitespace-nowrap text-stone-400 hover:text-stone-200 transition duration-300")}
|
||||||
|
displayMode={displayMode}
|
||||||
|
/>
|
||||||
|
<WdImgButton
|
||||||
|
className={PrimeIcons.SLIDERS_H}
|
||||||
|
onClick={onOpenSettings}
|
||||||
|
tooltip={{
|
||||||
|
content: 'Open Kills Settings',
|
||||||
|
position: TooltipPosition.left,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</LayoutEventBlocker>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<LayoutEventBlocker className="flex gap-2 items-center">
|
|
||||||
<WdCheckbox
|
|
||||||
size="xs"
|
|
||||||
labelSide="left"
|
|
||||||
label="Show all systems"
|
|
||||||
value={showAll}
|
|
||||||
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
|
|
||||||
onChange={onToggleShowAllVisible}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<WdImgButton
|
|
||||||
className={PrimeIcons.SLIDERS_H}
|
|
||||||
onClick={onOpenSettings}
|
|
||||||
tooltip={{
|
|
||||||
content: 'Open Kills Settings',
|
|
||||||
position: TooltipPosition.left,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</LayoutEventBlocker>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// VictimSubRowInfo.tsx
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { zkillLink } from '../helpers';
|
import { zkillLink } from '../helpers';
|
||||||
@@ -15,13 +14,13 @@ export const VictimRowSubInfo: React.FC<VictimRowSubInfoProps> = ({
|
|||||||
victimPortraitUrl,
|
victimPortraitUrl,
|
||||||
victimCharName,
|
victimCharName,
|
||||||
}) => {
|
}) => {
|
||||||
if (!victimPortraitUrl || victimCharacterId === null || victimCharacterId <= 0) {
|
if (!victimPortraitUrl || !victimCharacterId || victimCharacterId <= 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-start gap-1 h-14">
|
<div className="flex items-start gap-1">
|
||||||
<div className="relative shrink-0 w-14 h-14 overflow-hidden">
|
<div className="relative shrink-0 w-12 h-12 sm:w-14 sm:h-14 overflow-hidden">
|
||||||
<a
|
<a
|
||||||
href={zkillLink('character', victimCharacterId)}
|
href={zkillLink('character', victimCharacterId)}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import { WdCheckbox, WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display modes for the responsive checkbox.
|
||||||
|
*
|
||||||
|
* - "full": show the full label (e.g. "Show offline" or "Show ship name")
|
||||||
|
* - "abbr": show the abbreviated label (e.g. "Offline" or "Ship name")
|
||||||
|
* - "checkbox": show only the checkbox (no text)
|
||||||
|
* - "hide": do not render the checkbox at all
|
||||||
|
*/
|
||||||
|
export type WdDisplayMode = "full" | "abbr" | "checkbox" | "hide";
|
||||||
|
|
||||||
|
|
||||||
|
export interface WdResponsiveCheckboxProps {
|
||||||
|
tooltipContent: string;
|
||||||
|
size: 'xs' | 'normal' | 'm';
|
||||||
|
labelFull: string;
|
||||||
|
labelAbbreviated: string;
|
||||||
|
value: boolean;
|
||||||
|
onChange: () => void;
|
||||||
|
classNameLabel?: string;
|
||||||
|
containerClassName?: string;
|
||||||
|
labelSide?: 'left' | 'right';
|
||||||
|
displayMode: WdDisplayMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WdResponsiveCheckbox: React.FC<WdResponsiveCheckboxProps> = ({
|
||||||
|
tooltipContent,
|
||||||
|
size,
|
||||||
|
labelFull,
|
||||||
|
labelAbbreviated,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
classNameLabel,
|
||||||
|
containerClassName,
|
||||||
|
labelSide = 'left',
|
||||||
|
displayMode,
|
||||||
|
}) => {
|
||||||
|
if (displayMode === "hide") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const label =
|
||||||
|
displayMode === "full"
|
||||||
|
? labelFull
|
||||||
|
: displayMode === "abbr"
|
||||||
|
? labelAbbreviated
|
||||||
|
: displayMode === "checkbox"
|
||||||
|
? ""
|
||||||
|
: labelFull;
|
||||||
|
|
||||||
|
const checkbox = (
|
||||||
|
<div className={clsx("min-w-0", containerClassName)}>
|
||||||
|
<WdCheckbox
|
||||||
|
size={size}
|
||||||
|
labelSide={labelSide}
|
||||||
|
label={label}
|
||||||
|
value={value}
|
||||||
|
classNameLabel={classNameLabel}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return tooltipContent ? (
|
||||||
|
<WdTooltipWrapper content={tooltipContent}>{checkbox}</WdTooltipWrapper>
|
||||||
|
) : (
|
||||||
|
checkbox
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './WdResponsiveCheckbox';
|
||||||
@@ -11,3 +11,5 @@ export * from './WdImgButton';
|
|||||||
export * from './WdTooltip';
|
export * from './WdTooltip';
|
||||||
export * from './WdCheckbox';
|
export * from './WdCheckbox';
|
||||||
export * from './TimeAgo';
|
export * from './TimeAgo';
|
||||||
|
export * from './WdTooltipWrapper';
|
||||||
|
export * from './WdResponsiveCheckBox';
|
||||||
|
|||||||
Reference in New Issue
Block a user