Compare commits

...

2 Commits

Author SHA1 Message Date
achichenkov
ba5b2fb8d9 fix(Map): removed unnecessary hooks styles 2025-02-08 20:45:42 +03:00
achichenkov
deb47b66f6 fix(Map): Fixed a lot of design and architect issues after last million PRs 2025-02-08 20:33:27 +03:00
32 changed files with 318 additions and 300 deletions

View File

@@ -1,3 +0,0 @@
.active {
background-color: rgba(98, 98, 98, 0.33);
}

View File

@@ -1,3 +0,0 @@
.active {
background-color: rgba(98, 98, 98, 0.33);
}

View File

@@ -1,3 +0,0 @@
.active {
background-color: rgba(98, 98, 98, 0.33);
}

View File

@@ -1,3 +0,0 @@
.active {
background-color: rgba(98, 98, 98, 0.33);
}

View File

@@ -0,0 +1,48 @@
.TooltipActive {
pointer-events: auto !important;
position: relative;
z-index: 3;
}
.localCounter {
mix-blend-mode: screen;
display: flex;
align-items: center;
gap: 1px;
position: relative;
top: 1px;
color: var(--rf-node-local-counter);
&.hasUserCharacters {
color: var(--rf-has-user-characters);
}
& > i {
font-size: 9px;
position: relative;
}
& > span {
font-size: 9px;
line-height: 9px;
font-weight: var(--rf-local-counter-font-weight, 500);
@-moz-document url-prefix() {
position: relative;
top: -1px;
}
}
}
.Pathfinder {
.localCounter {
@-moz-document url-prefix() {
top: 0;
}
& > span {
position: relative;
top: -1px;
}
}
}

View File

@@ -2,25 +2,23 @@ import { useMemo } from 'react';
import clsx from 'clsx';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit/WdTooltip';
import { LocalCharactersList, CharItemProps } from '../../../mapInterface/widgets/LocalCharacters/components';
import { CharItemProps, LocalCharactersList } from '../../../mapInterface/widgets/LocalCharacters/components';
import { useLocalCharactersItemTemplate } from '../../../mapInterface/widgets/LocalCharacters/hooks/useLocalCharacters';
import { useLocalCharacterWidgetSettings } from '../../../mapInterface/widgets/LocalCharacters/hooks/useLocalWidgetSettings';
import classes from './SolarSystemLocalCounter.module.scss';
import { AvailableThemes } from '@/hooks/Mapper/mapRootProvider';
import { useTheme } from '@/hooks/Mapper/hooks/useTheme.ts';
interface LocalCounterProps {
localCounterCharacters: Array<CharItemProps>;
classes: { [key: string]: string };
hasUserCharacters: boolean;
showIcon?: boolean;
}
export function LocalCounter({
localCounterCharacters,
hasUserCharacters,
classes,
showIcon = true,
}: LocalCounterProps) {
export const LocalCounter = ({ localCounterCharacters, hasUserCharacters, showIcon = true }: LocalCounterProps) => {
const [settings] = useLocalCharacterWidgetSettings();
const itemTemplate = useLocalCharactersItemTemplate(settings.showShipName);
const theme = useTheme();
const pilotTooltipContent = useMemo(() => {
return (
@@ -42,23 +40,26 @@ export function LocalCounter({
}
return (
<div className={classes.LocalCounterLayer} style={{ zIndex: 9999 }}>
<div
className={clsx(classes.TooltipActive, {
[classes.Pathfinder]: theme === AvailableThemes.pathfinder,
})}
>
<WdTooltipWrapper
// @ts-ignore
content={pilotTooltipContent}
position={TooltipPosition.right}
offset={180}
interactive={true}
offset={0}
>
<div
className={clsx(classes.localCounter, {
[classes.hasUserCharacters]: hasUserCharacters,
})}
>
{showIcon && <i className="pi pi-users" style={{ fontSize: '0.50rem' }} />}
{showIcon && <i className="pi pi-users" />}
<span>{localCounterCharacters.length}</span>
</div>
</WdTooltipWrapper>
</div>
);
}
};

View File

@@ -25,7 +25,7 @@ $tooltip-bg: #202020;
border: 1px solid darken($pastel-blue, 10%);
border-radius: 5px;
position: relative;
z-index: 1;
z-index: 3;
overflow: hidden;
&.Mataria,
@@ -91,8 +91,8 @@ $tooltip-bg: #202020;
&.eve-system-status-home {
border: 1px solid var(--eve-solar-system-status-color-home-dark30);
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-home),
45deg,
var(--eve-solar-system-status-color-background),
transparent
);
&.selected {
@@ -273,7 +273,7 @@ $tooltip-bg: #202020;
height: 19px;
.hasLocalCounter {
margin-right: 1.25rem;
margin-right: 2px;
&.countAbove9 {
margin-right: 1.5rem;
}
@@ -319,7 +319,8 @@ $tooltip-bg: #202020;
.Handlers {
position: absolute;
z-index: 2;
z-index: 4;
pointer-events: none;
top: 0;
left: 0;
width: 100%;
@@ -332,6 +333,7 @@ $tooltip-bg: #202020;
border: 1px solid $pastel-blue;
width: 5px;
height: 5px;
pointer-events: auto;
&.selected {
border-color: $pastel-pink;
@@ -375,38 +377,3 @@ $tooltip-bg: #202020;
}
}
.LocalCounterLayer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 9999;
padding: 8;
.localCounter {
position: absolute;
pointer-events: auto;
top: 10.5px;
right: 8px;
mix-blend-mode: screen;
gap: 2px;
color: var(--rf-node-local-counter, #5cb85c);
&.hasUserCharacters {
color: var(--rf-has-user-characters, #fbbf24);
}
& > i {
position: relative;
top: 1px;
}
& > span {
font-size: 9px;
line-height: 9px;
font-weight: var(--rf-local-counter-font-weight, 500);
}
}
}

View File

@@ -65,6 +65,7 @@ export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>
nodeVars.status !== undefined ? classes[STATUS_CLASSES[nodeVars.status]] : '',
{ [classes.selected]: nodeVars.selected },
)}
onMouseDownCapture={e => nodeVars.dbClick(e)}
>
{nodeVars.visible && (
<>
@@ -120,18 +121,18 @@ export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>
{nodeVars.isWormhole && !nodeVars.customName && <div />}
<div className="flex items-center justify-end">
<div
className={clsx('flex items-center gap-1', {
[classes.hasLocalCounter]: nodeVars.charactersInSystem.length > 0,
[classes.countAbove9]: nodeVars.charactersInSystem.length > 9,
})}
>
<div className="flex items-center gap-1 justify-end">
<div className={clsx('flex items-center gap-1')}>
{nodeVars.locked && <i className={clsx(PrimeIcons.LOCK, classes.lockIcon)} />}
{nodeVars.hubs.includes(nodeVars.solarSystemId) && (
<i className={clsx(PrimeIcons.MAP_MARKER, classes.mapMarker)} />
)}
</div>
<LocalCounter
hasUserCharacters={nodeVars.hasUserCharacters}
localCounterCharacters={localCounterCharacters}
/>
</div>
</div>
</>
@@ -158,7 +159,7 @@ export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>
</>
)}
<div onMouseDownCapture={e => nodeVars.dbClick(e)} className={classes.Handlers}>
<div className={classes.Handlers}>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleTop, {
@@ -200,11 +201,6 @@ export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>
id="d"
/>
</div>
<LocalCounter
hasUserCharacters={nodeVars.hasUserCharacters}
localCounterCharacters={localCounterCharacters}
classes={classes}
/>
</>
);
});

View File

@@ -4,3 +4,17 @@
Only override what's different from the base
Currently none required
---------------------------------------------- */
.RootCustomNode {
&.eve-system-status-home {
border: 1px solid var(--eve-solar-system-status-color-home-dark30);
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-home),
transparent
);
&.selected {
border-color: var(--eve-solar-system-status-color-home);
}
}
}

View File

@@ -25,7 +25,7 @@ export const SolarSystemNodeTheme = memo((props: NodeProps<MapSolarSystemType>)
<div className={classes.Bookmarks}>
{nodeVars.labelCustom !== '' && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] ">{nodeVars.labelCustom}</span>
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]">{nodeVars.labelCustom}</span>
</div>
)}
@@ -65,6 +65,7 @@ export const SolarSystemNodeTheme = memo((props: NodeProps<MapSolarSystemType>)
nodeVars.status !== undefined ? classes[STATUS_CLASSES[nodeVars.status]] : '',
{ [classes.selected]: nodeVars.selected },
)}
onMouseDownCapture={e => nodeVars.dbClick(e)}
>
{nodeVars.visible && (
<>
@@ -130,18 +131,18 @@ export const SolarSystemNodeTheme = memo((props: NodeProps<MapSolarSystemType>)
{nodeVars.isWormhole && !nodeVars.customName && <div />}
<div className="flex items-center justify-end">
<div
className={clsx('flex items-center gap-1', {
[classes.hasLocalCounter]: nodeVars.charactersInSystem.length > 0,
[classes.countAbove9]: nodeVars.charactersInSystem.length > 9,
})}
>
<div className="flex items-center gap-1 justify-end">
<div className={clsx('flex items-center gap-1')}>
{nodeVars.locked && <i className={clsx(PrimeIcons.LOCK, classes.lockIcon)} />}
{nodeVars.hubs.includes(nodeVars.solarSystemId) && (
<i className={clsx(PrimeIcons.MAP_MARKER, classes.mapMarker)} />
)}
</div>
<LocalCounter
hasUserCharacters={nodeVars.hasUserCharacters}
localCounterCharacters={localCounterCharacters}
/>
</div>
</div>
</>
@@ -168,7 +169,7 @@ export const SolarSystemNodeTheme = memo((props: NodeProps<MapSolarSystemType>)
</>
)}
<div onMouseDownCapture={e => nodeVars.dbClick(e)} className={classes.Handlers}>
<div className={classes.Handlers}>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleTop, {
@@ -210,11 +211,6 @@ export const SolarSystemNodeTheme = memo((props: NodeProps<MapSolarSystemType>)
id="d"
/>
</div>
<LocalCounter
hasUserCharacters={nodeVars.hasUserCharacters}
localCounterCharacters={localCounterCharacters}
classes={classes}
/>
</>
);
});

View File

@@ -31,6 +31,6 @@
--window-corner: #72716f;
--rf-local-counter-font-weight: 500;
--rf-node-local-counter: #5cb85c;
--rf-has-user-characters: #fbbf24;
--rf-node-local-counter: inherit;
--rf-has-user-characters: #ffc75d;
}

View File

@@ -1,19 +1,19 @@
$friendlyBase: #3bbd39;
$friendlyAlpha: #3bbd3952;
$friendlyBase: #3bbd39;
$friendlyAlpha: #3bbd3952;
$friendlyDark20: darken($friendlyBase, 20%);
$friendlyDark30: darken($friendlyBase, 30%);
$friendlyDark5: darken($friendlyBase, 5%);
$lookingForBase: #43c2fd;
$lookingForBase: #43c2fd;
$lookingForAlpha: rgba(67, 176, 253, 0.48);
$lookingForDark15: darken($lookingForBase, 15%);
$homeBase: rgb(197, 253, 67);
$homeAlpha: rgba(197, 253, 67, 0.32);
$homeBase: rgb(179, 253, 67);
$homeAlpha: rgba(186, 248, 48, 0.32);
$homeBackground: #a0fa5636;
$homeDark30: darken($homeBase, 30%);
:root {
--pastel-blue: #5a7d9a;
--pastel-pink: #d291bc;
@@ -98,6 +98,7 @@ $homeDark30: darken($homeBase, 30%);
--eve-solar-system-status-color-unknown: transparent;
--eve-solar-system-status-home: #{$homeAlpha};
--eve-solar-system-status-color-home: #{$homeBase};
--eve-solar-system-status-color-background: #{$homeBackground};
--eve-solar-system-status-color-home-dark30: #{$homeDark30};
--eve-solar-system-status-friendly: #{$friendlyAlpha};
--eve-solar-system-status-color-friendly: #{$friendlyBase};

View File

@@ -2,6 +2,10 @@
@import './eve-common';
@import url('https://fonts.googleapis.com/css2?family=Oxygen:wght@300;400;700&display=swap');
$homeBase: rgb(197, 253, 67);
$homeAlpha: rgba(197, 253, 67, 0.32);
$homeDark30: darken($homeBase, 30%);
.pathfinder-theme {
/* -- Override values from the default theme -- */
--rf-bg-color: #000000;
@@ -41,4 +45,10 @@
--eve-wh-type-color-c6: #d9534f;
--eve-wh-type-color-c13: #7986cb;
--eve-wh-type-color-drifter: #44aa82;
--rf-node-local-counter: #5cb85c;
--rf-has-user-characters: #ffc75d;
--eve-solar-system-status-home: #{$homeAlpha};
--eve-solar-system-status-color-home: #{$homeBase};
}

View File

@@ -8,6 +8,8 @@ import { LocalCharactersList } from './components/LocalCharactersList';
import { useLocalCharactersItemTemplate } from './hooks/useLocalCharacters';
import { useLocalCharacterWidgetSettings } from './hooks/useLocalWidgetSettings';
import { LocalCharactersHeader } from './components/LocalCharactersHeader';
import classes from './LocalCharacters.module.scss';
import clsx from 'clsx';
export const LocalCharacters = () => {
const {
@@ -16,11 +18,11 @@ export const LocalCharacters = () => {
const [settings, setSettings] = useLocalCharacterWidgetSettings();
const [systemId] = selectedSystems;
const restrictOfflineShowing = useMapGetOption("restrict_offline_showing");
const restrictOfflineShowing = useMapGetOption('restrict_offline_showing');
const isAdminOrManager = useMapCheckPermissions([UserPermission.MANAGE_MAP]);
const showOffline = useMemo(
() => !restrictOfflineShowing || isAdminOrManager,
[isAdminOrManager, restrictOfflineShowing]
[isAdminOrManager, restrictOfflineShowing],
);
const sorted = useMemo(() => {
@@ -81,7 +83,10 @@ export const LocalCharacters = () => {
items={sorted}
itemSize={settings.compact ? 26 : 41}
itemTemplate={itemTemplate}
containerClassName="w-full h-full overflow-x-hidden overflow-y-auto custom-scrollbar select-none"
containerClassName={clsx(
'w-full h-full overflow-x-hidden overflow-y-auto custom-scrollbar select-none',
classes.VirtualScroller,
)}
/>
)}
</Widget>

View File

@@ -1,8 +1,7 @@
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';
import { LayoutEventBlocker, TooltipPosition, WdCheckbox, WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
interface LocalCharactersHeaderProps {
sortedCount: number;
@@ -24,68 +23,54 @@ export const LocalCharactersHeader: React.FC<LocalCharactersHeaderProps> = ({
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);
const compactOffline = useMaxWidth(headerRef, 145);
const compactShipName = useMaxWidth(headerRef, 195);
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"
<div className="flex w-full items-center text-xs justify-between" ref={headerRef}>
<div className="flex-shrink-0 select-none mr-2">Local{showList ? ` [${sortedCount}]` : ''}</div>
<LayoutEventBlocker className="flex items-center gap-2 justify-end">
<div className="flex items-center gap-2">
{showOffline && (
<WdTooltipWrapper content="Show offline characters in system" position={TooltipPosition.top}>
<WdCheckbox
size="xs"
labelFull="Show offline"
labelAbbreviated="Offline"
labelSide="left"
label={compactOffline ? '' : '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}
onChange={() => setSettings((prev: any) => ({ ...prev, showOffline: !prev.showOffline }))}
classNameLabel={clsx('whitespace-nowrap text-stone-400 hover:text-stone-200 transition duration-300', {
truncate: compactOffline,
})}
/>
)}
{settings.compact && (
<WdResponsiveCheckbox
tooltipContent="Show ship name in compact rows"
</WdTooltipWrapper>
)}
{settings.compact && (
<WdTooltipWrapper content="Show ship name in compact rows" position={TooltipPosition.top}>
<WdCheckbox
size="xs"
labelFull="Show ship name"
labelAbbreviated="Ship name"
labelSide="left"
label={compactShipName ? '' : '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}
onChange={() => setSettings((prev: any) => ({ ...prev, showShipName: !prev.showShipName }))}
classNameLabel={clsx('whitespace-nowrap text-stone-400 hover:text-stone-200 transition duration-300', {
truncate: compactShipName,
})}
/>
)}
</div>
</WdTooltipWrapper>
)}
</div>
<WdTooltipWrapper content="Enable compact mode" position={TooltipPosition.top}>
<span
className={clsx("w-4 h-4 cursor-pointer", {
"hero-bars-2": settings.compact,
"hero-bars-3": !settings.compact,
className={clsx('w-4 h-4 min-w-[1rem] cursor-pointer', {
'hero-bars-2': settings.compact,
'hero-bars-3': !settings.compact,
})}
onClick={() => setSettings((prev: any) => ({ ...prev, compact: !prev.compact }))}
/>
</LayoutEventBlocker>
</div>
</WdTooltipWrapper>
</LayoutEventBlocker>
</div>
);
};

View File

@@ -1,13 +1,6 @@
.VirtualScroller {
height: 100% !important;
}
.CharacterRow {
//border-left-width: 1px;
&.CardBorderLeftIsOwn {
border-left-color: rgb(251 146 60 / 1)
}
}

View File

@@ -0,0 +1,27 @@
import classes from './LocalCharactersItemTemplate.module.scss';
import clsx from 'clsx';
import { CharacterCard } from '@/hooks/Mapper/components/ui-kit';
import { CharItemProps } from '@/hooks/Mapper/components/mapInterface/widgets/LocalCharacters/components';
import { VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
export type LocalCharactersItemTemplateProps = { showShipName: boolean } & CharItemProps &
VirtualScrollerTemplateOptions;
export const LocalCharactersItemTemplate = ({ showShipName, ...options }: LocalCharactersItemTemplateProps) => {
return (
<div
className={clsx(
classes.CharacterRow,
'box-border flex items-center w-full whitespace-nowrap overflow-hidden text-ellipsis min-w-[0px]',
{
'surface-hover': options.odd,
'border-b border-gray-600 border-opacity-20': !options.last,
'bg-green-500 hover:bg-green-700 transition duration-300 bg-opacity-10 hover:bg-opacity-10': options.online,
},
)}
style={{ height: `${options.props.itemSize}px` }}
>
<CharacterCard showShipName={showShipName} {...options} />
</div>
);
};

View File

@@ -0,0 +1 @@
export * from './LocalCharactersItemTemplate.tsx';

View File

@@ -5,23 +5,24 @@ import { CharItemProps } from './types';
type LocalCharactersListProps = {
items: Array<CharItemProps>;
itemSize: number;
itemTemplate: (char: CharItemProps, options: VirtualScrollerTemplateOptions) => React.ReactNode;
containerClassName?: string;
};
export function LocalCharactersList({ items, itemSize, itemTemplate, containerClassName }: LocalCharactersListProps) {
export const LocalCharactersList = ({
items,
itemSize,
itemTemplate,
containerClassName,
}: LocalCharactersListProps) => {
return (
<VirtualScroller
items={items}
itemSize={itemSize}
orientation="vertical"
className={clsx('w-full h-full', containerClassName)}
autoSize={false}
itemTemplate={itemTemplate}
/>
);
}
};

View File

@@ -1,2 +1,3 @@
export * from './LocalCharactersItemTemplate';
export * from './LocalCharactersList';
export * from './types';

View File

@@ -1,33 +1,12 @@
import { useCallback } from 'react';
import { VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
import clsx from 'clsx';
import classes from './useLocalCharacters.module.scss';
import { CharacterCard } from '@/hooks/Mapper/components/ui-kit';
import { CharItemProps } from '../components';
import { CharItemProps, LocalCharactersItemTemplate } from '../components';
export function useLocalCharactersItemTemplate(showShipName: boolean) {
return useCallback(
(char: CharItemProps, options: VirtualScrollerTemplateOptions) => {
return (
<div
className={clsx(classes.CharacterRow, 'box-border flex items-center', {
'surface-hover': options.odd,
'border-b border-gray-600 border-opacity-20': !options.last,
'bg-green-500 hover:bg-green-700 transition duration-300 bg-opacity-10 hover:bg-opacity-10': char.online,
})}
style={{
height: `${options.props.itemSize}px`,
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
minWidth: 0,
width: '100%',
}}
>
<CharacterCard showShipName={showShipName} {...char} />
</div>
);
},
(char: CharItemProps, options: VirtualScrollerTemplateOptions) => (
<LocalCharactersItemTemplate {...char} {...options} showShipName={showShipName} />
),
[showShipName],
);
}

View File

@@ -1,7 +1,7 @@
import classes from './RoutesList.module.scss';
import { Route, SystemStaticInfoShort } from '@/hooks/Mapper/types/routes.ts';
import clsx from 'clsx';
import { SystemViewStandalone, WdTooltip, WdTooltipHandlers } from '@/hooks/Mapper/components/ui-kit';
import { SystemViewStandalone, TooltipPosition, WdTooltip, WdTooltipHandlers } from '@/hooks/Mapper/components/ui-kit';
import { getBackgroundClass, getShapeClass } from '@/hooks/Mapper/components/map/helpers';
import { MouseEvent, useCallback, useRef, useState } from 'react';
import { Commands } from '@/hooks/Mapper/types';
@@ -46,9 +46,11 @@ export const RouteSystem = ({
<>
<WdTooltip
ref={tooltipRef}
position={TooltipPosition.top}
// targetSelector={`.tooltip-route-sys_${destination}_${solar_system_id}`}
content={() => (
<SystemViewStandalone
className="mx-[4px]"
security={security}
system_class={system_class}
class_title={class_title}
@@ -63,8 +65,8 @@ export const RouteSystem = ({
tooltipRef.current?.show(e);
onMouseEnter?.(solar_system_id);
}}
onMouseLeave={e => {
tooltipRef.current?.hide(e);
onMouseLeave={() => {
tooltipRef.current?.hide();
onMouseLeave?.();
}}
onContextMenu={handleContext}

View File

@@ -184,7 +184,7 @@ export const RoutesWidgetComp = () => {
}, [data, update]);
const ref = useRef<HTMLDivElement>(null);
const compact = useMaxWidth(ref, 155);
const compact = useMaxWidth(ref, 170);
const [openAddSystem, setOpenAddSystem] = useState<boolean>(false);
const onAddSystem = useCallback(() => setOpenAddSystem(true), []);
@@ -217,14 +217,14 @@ export const RoutesWidgetComp = () => {
}}
/>
<WdTooltipWrapper content="Show shortest route">
<WdTooltipWrapper content="Show shortest route" position={TooltipPosition.top}>
<WdCheckbox
size="xs"
labelSide="left"
label={compact ? '' : 'Show shortest'}
value={!isSecure}
onChange={handleSecureChange}
classNameLabel={clsx('text-red-400')}
classNameLabel="text-red-400 whitespace-nowrap"
/>
</WdTooltipWrapper>
<WdImgButton

View File

@@ -1,15 +1,15 @@
import React, { useRef } from 'react';
import clsx from 'clsx';
import {
LayoutEventBlocker,
WdResponsiveCheckbox,
WdImgButton,
SystemView,
TooltipPosition,
WdDisplayMode,
WdCheckbox,
WdImgButton,
WdTooltipWrapper,
} from '@/hooks/Mapper/components/ui-kit';
import { useKillsWidgetSettings } from '../hooks/useKillsWidgetSettings';
import { PrimeIcons } from 'primereact/api';
import { useElementWidth } from '@/hooks/Mapper/components/hooks';
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
interface KillsHeaderProps {
systemId?: string;
@@ -25,47 +25,41 @@ export const KillsHeader: React.FC<KillsHeaderProps> = ({ systemId, onOpenSettin
};
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";
}
const compact = useMaxWidth(headerRef, 150);
return (
<div className="flex w-full items-center text-xs" ref={headerRef}>
<div className="flex-shrink-0 select-none mr-2">
Kills{systemId && !showAll && ' in '}
<div className="flex w-full items-center justify-between text-xs" ref={headerRef}>
<div className="flex items-center gap-1">
<div className="text-stone-400">
Kills
{systemId && !showAll && ' in '}
</div>
{systemId && !showAll && <SystemView systemId={systemId} className="select-none text-center" hideRegion />}
</div>
<div className="flex-grow overflow-hidden">
<LayoutEventBlocker className="flex items-center gap-2 justify-end">
<div className="flex items-center gap-2">
<WdResponsiveCheckbox
tooltipContent="Show all systems"
<LayoutEventBlocker className="flex items-center gap-2 justify-end">
<div className="flex items-center gap-2">
<WdTooltipWrapper content="Show all systems" position={TooltipPosition.top}>
<WdCheckbox
size="xs"
labelFull="Show all systems"
labelAbbreviated="All"
labelSide="left"
label={compact ? 'All' : 'Show all systems'}
value={showAll}
onChange={onToggleShowAllVisible}
classNameLabel={clsx("whitespace-nowrap text-stone-400 hover:text-stone-200 transition duration-300")}
displayMode={displayMode}
classNameLabel="whitespace-nowrap text-stone-400 hover:text-stone-200 transition duration-300"
/>
<WdImgButton
className={PrimeIcons.SLIDERS_H}
onClick={onOpenSettings}
tooltip={{
content: 'Open Kills Settings',
position: TooltipPosition.left,
}}
/>
</div>
</LayoutEventBlocker>
</div>
</WdTooltipWrapper>
<WdImgButton
className={PrimeIcons.SLIDERS_H}
onClick={onOpenSettings}
tooltip={{
content: 'Open Kills Settings',
position: TooltipPosition.top,
}}
/>
</div>
</LayoutEventBlocker>
</div>
);
};

View File

@@ -3,7 +3,12 @@ import { Dialog } from 'primereact/dialog';
import { useCallback, useMemo, useState } from 'react';
import { TabPanel, TabView } from 'primereact/tabview';
import { PrettySwitchbox } from './components';
import { InterfaceStoredSettingsProps, useMapRootState, InterfaceStoredSettings } from '@/hooks/Mapper/mapRootProvider';
import {
InterfaceStoredSettingsProps,
useMapRootState,
InterfaceStoredSettings,
AvailableThemes
} from '@/hooks/Mapper/mapRootProvider';
import { OutCommand } from '@/hooks/Mapper/types';
import { Dropdown } from 'primereact/dropdown';
import { WidgetsSettings } from '@/hooks/Mapper/components/mapRootContent/components/MapSettings/components/WidgetsSettings/WidgetsSettings.tsx';
@@ -112,8 +117,8 @@ const UI_CHECKBOXES_PROPS: SettingsListItem[] = [
];
const THEME_OPTIONS = [
{ label: 'Default', value: 'default' },
{ label: 'Pathfinder', value: 'pathfinder' },
{ label: 'Default', value: AvailableThemes.default },
{ label: 'Pathfinder', value: AvailableThemes.pathfinder },
];
const THEME_SETTING: SettingsListItem = {

View File

@@ -2,12 +2,12 @@ import classes from './WdCheckbox.module.scss';
import { Checkbox, CheckboxChangeEvent } from 'primereact/checkbox';
import { WithClassName } from '@/hooks/Mapper/types/common';
import clsx from 'clsx';
import { useMemo } from 'react';
import React, { useMemo } from 'react';
let counter = 0;
export interface WdCheckboxProps {
label: string;
label: React.ReactNode | string;
classNameLabel?: string;
value: boolean;
labelSide?: 'left' | 'right';

View File

@@ -10,8 +10,7 @@ import { WdCheckbox, WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
* - "checkbox": show only the checkbox (no text)
* - "hide": do not render the checkbox at all
*/
export type WdDisplayMode = "full" | "abbr" | "checkbox" | "hide";
export type WdDisplayMode = 'full' | 'abbr' | 'checkbox' | 'hide';
export interface WdResponsiveCheckboxProps {
tooltipContent: string;
@@ -38,21 +37,21 @@ export const WdResponsiveCheckbox: React.FC<WdResponsiveCheckboxProps> = ({
labelSide = 'left',
displayMode,
}) => {
if (displayMode === "hide") {
if (displayMode === 'hide') {
return null;
}
const label =
displayMode === "full"
displayMode === 'full'
? labelFull
: displayMode === "abbr"
? labelAbbreviated
: displayMode === "checkbox"
? ""
: labelFull;
: displayMode === 'abbr'
? labelAbbreviated
: displayMode === 'checkbox'
? ''
: labelFull;
const checkbox = (
<div className={clsx("min-w-0", containerClassName)}>
<div className={clsx('min-w-0', containerClassName)}>
<WdCheckbox
size={size}
labelSide={labelSide}
@@ -64,9 +63,5 @@ export const WdResponsiveCheckbox: React.FC<WdResponsiveCheckboxProps> = ({
</div>
);
return tooltipContent ? (
<WdTooltipWrapper content={tooltipContent}>{checkbox}</WdTooltipWrapper>
) : (
checkbox
);
return tooltipContent ? <WdTooltipWrapper content={tooltipContent}>{checkbox}</WdTooltipWrapper> : checkbox;
};

View File

@@ -1,13 +1,4 @@
import React, {
ForwardedRef,
forwardRef,
useCallback,
useEffect,
useImperativeHandle,
useLayoutEffect,
useRef,
useState,
} from 'react';
import React, { ForwardedRef, forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import clsx from 'clsx';
import debounce from 'lodash.debounce';
@@ -58,11 +49,11 @@ export const WdTooltip = forwardRef(function WdTooltip(
className,
...restProps
}: TooltipProps,
ref: ForwardedRef<WdTooltipHandlers>
ref: ForwardedRef<WdTooltipHandlers>,
) {
// Always initialize position so we never have a null value.
const [visible, setVisible] = useState(false);
const [pos, setPos] = useState<OffsetPosition>({ left: 0, top: 0 });
const [pos, setPos] = useState<OffsetPosition | null>(null);
const tooltipRef = useRef<HTMLDivElement>(null);
const [isMouseInsideTooltip, setIsMouseInsideTooltip] = useState(false);
@@ -80,8 +71,13 @@ export const WdTooltip = forwardRef(function WdTooltip(
let newLeft = x;
let newTop = y;
if (newLeft < 0) newLeft = 10;
if (newTop < 0) newTop = 10;
if (newLeft < 0) {
newLeft = 10;
}
if (newTop < 0) {
newTop = 10;
}
const rightEdge = newLeft + tooltipWidth + 10;
if (rightEdge > window.innerWidth) {
@@ -99,11 +95,13 @@ export const WdTooltip = forwardRef(function WdTooltip(
const scheduleHide = useCallback(() => {
if (!interactive) {
setVisible(false);
setPos(null);
return;
}
if (!hideTimeoutRef.current) {
hideTimeoutRef.current = setTimeout(() => {
setVisible(false);
setPos(null);
}, LEAVE_DELAY);
}
}, [interactive]);
@@ -130,12 +128,12 @@ export const WdTooltip = forwardRef(function WdTooltip(
clearTimeout(hideTimeoutRef.current);
}
setVisible(false);
setPos(null);
},
getIsMouseInside: () => isMouseInsideTooltip,
}));
// Recalculate position once the tooltip element has been rendered.
useLayoutEffect(() => {
useEffect(() => {
if (!tooltipRef.current || !triggerInfo) return;
const tooltipEl = tooltipRef.current;
@@ -147,30 +145,36 @@ export const WdTooltip = forwardRef(function WdTooltip(
const tooltipBounds = tooltipEl.getBoundingClientRect();
x = rect.left - tooltipBounds.width - offset;
y = rect.top + rect.height / 2 - tooltipBounds.height / 2;
if (x <= 0) {
x = rect.left + rect.width + offset;
}
setPos(calcTooltipPosition({ x, y }));
return;
}
if (tPosition === TooltipPosition.right) {
x = rect.left + rect.width + offset;
y = rect.top + rect.height / 2 - tooltipEl.offsetHeight / 2;
setPos(calcTooltipPosition({ x, y }));
return;
}
if (tPosition === TooltipPosition.top) {
x = rect.left + rect.width / 2 - tooltipEl.offsetWidth / 2;
y = rect.top - tooltipEl.offsetHeight - offset;
setPos(calcTooltipPosition({ x, y }));
return;
}
if (tPosition === TooltipPosition.bottom) {
x = rect.left + rect.width / 2 - tooltipEl.offsetWidth / 2;
y = rect.bottom + offset;
setPos(calcTooltipPosition({ x, y }));
return;
}
// Default case: use stored coordinates.
setPos(calcTooltipPosition({ x, y }));
}, [calcTooltipPosition, triggerInfo, tPosition, offset]);
@@ -184,6 +188,7 @@ export const WdTooltip = forwardRef(function WdTooltip(
scheduleHide();
return;
}
const triggerEl = targetEl.closest(targetSelector);
const insideTooltip = interactive && tooltipRef.current?.contains(targetEl);
@@ -196,6 +201,7 @@ export const WdTooltip = forwardRef(function WdTooltip(
clearTimeout(hideTimeoutRef.current);
hideTimeoutRef.current = null;
}
setVisible(true);
if (triggerEl && tooltipRef.current) {
@@ -206,30 +212,26 @@ export const WdTooltip = forwardRef(function WdTooltip(
let y = evt.clientY;
switch (tPosition) {
case TooltipPosition.left: {
case TooltipPosition.left:
x = rect.left - tooltipEl.offsetWidth - offset;
y = rect.top + rect.height / 2 - tooltipEl.offsetHeight / 2;
if (x <= 0) {
x = rect.left + rect.width + offset;
}
break;
}
case TooltipPosition.right: {
case TooltipPosition.right:
x = rect.left + rect.width + offset;
y = rect.top + rect.height / 2 - tooltipEl.offsetHeight / 2;
break;
}
case TooltipPosition.top: {
case TooltipPosition.top:
x = rect.left + rect.width / 2 - tooltipEl.offsetWidth / 2;
y = rect.top - tooltipEl.offsetHeight - offset;
break;
}
case TooltipPosition.bottom: {
case TooltipPosition.bottom:
x = rect.left + rect.width / 2 - tooltipEl.offsetWidth / 2;
y = rect.bottom + offset;
break;
}
default:
}
setPos(calcTooltipPosition({ x, y }));
@@ -237,13 +239,10 @@ export const WdTooltip = forwardRef(function WdTooltip(
};
const debounced = debounce(handleMouseMove, 15);
const listener = (evt: Event) => {
debounced(evt as MouseEvent);
};
document.addEventListener('mousemove', listener);
document.addEventListener('mousemove', debounced);
return () => {
document.removeEventListener('mousemove', listener);
document.removeEventListener('mousemove', debounced);
debounced.cancel();
};
}, [targetSelector, interactive, tPosition, offset, calcTooltipPosition, scheduleHide]);
@@ -265,11 +264,12 @@ export const WdTooltip = forwardRef(function WdTooltip(
classes.tooltip,
interactive ? 'pointer-events-auto' : 'pointer-events-none',
'absolute p-1 border rounded-sm border-green-300 border-opacity-10 bg-stone-900 bg-opacity-90',
className
className,
pos === null ? 'invisible' : '',
)}
style={{
top: pos.top,
left: pos.left,
top: pos?.top ?? 0,
left: pos?.left ?? 0,
zIndex: 10000,
}}
onMouseEnter={() => {
@@ -289,7 +289,7 @@ export const WdTooltip = forwardRef(function WdTooltip(
>
{typeof content === 'function' ? content() : content}
</div>,
document.body
document.body,
);
});

View File

@@ -1,4 +1,4 @@
import { forwardRef, HTMLProps, ReactNode } from 'react';
import { forwardRef, HTMLProps, ReactNode, useMemo } from 'react';
import clsx from 'clsx';
import { WdTooltip, WdTooltipHandlers, TooltipProps } from '@/hooks/Mapper/components/ui-kit';
import classes from './WdTooltipWrapper.module.scss';
@@ -13,11 +13,8 @@ export type WdTooltipWrapperProps = {
Omit<TooltipProps, 'content'>;
export const WdTooltipWrapper = forwardRef<WdTooltipHandlers, WdTooltipWrapperProps>(
(
{ className, children, content, offset, position, targetSelector, interactive = false, size, ...props },
forwardedRef,
) => {
const suffix = Math.random().toString(36).slice(2, 7);
({ className, children, content, offset, position, targetSelector, interactive, size, ...props }, forwardedRef) => {
const suffix = useMemo(() => Math.random().toString(36).slice(2, 7), []);
const autoClass = `wdTooltipAutoTrigger-${suffix}`;
const finalTargetSelector = targetSelector || `.${autoClass}`;

View File

@@ -0,0 +1,7 @@
import { AvailableThemes, useMapRootState } from '@/hooks/Mapper/mapRootProvider';
export const useTheme = (): AvailableThemes => {
const { interfaceSettings } = useMapRootState();
return interfaceSettings.theme;
};

View File

@@ -46,6 +46,11 @@ const INITIAL_DATA: MapRootData = {
linkSignatureToSystem: null,
};
export enum AvailableThemes {
default = 'default',
pathfinder = 'pathfinder',
}
export enum InterfaceStoredSettingsProps {
isShowMenu = 'isShowMenu',
isShowMinimap = 'isShowMinimap',
@@ -65,7 +70,7 @@ export type InterfaceStoredSettings = {
isShowUnsplashedSignatures: boolean;
isShowBackgroundPattern: boolean;
isSoftBackground: boolean;
theme: string;
theme: AvailableThemes;
};
export const STORED_INTERFACE_DEFAULT_VALUES: InterfaceStoredSettings = {
@@ -76,7 +81,7 @@ export const STORED_INTERFACE_DEFAULT_VALUES: InterfaceStoredSettings = {
isShowUnsplashedSignatures: false,
isShowBackgroundPattern: true,
isSoftBackground: false,
theme: 'default',
theme: AvailableThemes.default,
};
export interface MapRootContextProps {