Compare commits

...

22 Commits

Author SHA1 Message Date
CI
fa0c7f3c66 chore: release version v1.37.8 2025-01-10 09:04:36 +00:00
guarzo
5f58645b41 fix: fix issue with newly added systems not adding a connection (#114)
* fix: resolve issue with newly added systems not connecting
2025-01-10 13:04:05 +04:00
CI
b1149cecaf chore: release version v1.37.6
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-09 21:57:08 +00:00
Aleksei Chichenkov
8f28d2be65 Merge pull request #111 from guarzo/guarzo/themefixes
fix: support additional theme names
2025-01-10 00:56:39 +03:00
Gustav
d758b54ef8 fix: support additional theme names 2025-01-09 15:06:32 -05:00
Gustav
58293b4dc4 refactor: providing some additional variables for theme support 2025-01-09 14:24:03 -05:00
CI
f2083f4256 chore: release version v1.37.5
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-09 19:09:14 +00:00
Aleksei Chichenkov
6c7bd5804e Merge pull request #109 from guarzo/guarzo/themefixes
fix: restore node styling, simplify framework for new themes
2025-01-09 22:08:47 +03:00
Gustav
483ae21e89 fix: restore node styling, simplify framework for new themes 2025-01-09 13:38:11 -05:00
CI
f734565844 chore: release version v1.37.4 2025-01-09 13:21:32 +00:00
CI
8c718ba181 chore: release version v1.37.3 2025-01-09 12:52:30 +00:00
Aleksei Chichenkov
c8d8734601 Merge pull request #108 from wanderer-industries/fix-dbclick
fix(Map): Fixed dbclick behaviour
2025-01-09 15:51:16 +03:00
achichenkov
5c757e8255 fix(Map): Fixed dbclick behaviour 2025-01-09 15:50:03 +03:00
CI
82f90ef759 chore: release version v1.37.2
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-09 07:26:08 +00:00
Aleksei Chichenkov
167c8eea6b Merge pull request #107 from guarzo/guarzo/theme-pathfinder
fix: fix route color
2025-01-09 10:25:37 +03:00
Gustav
d76079d4c7 theme cleanup 2025-01-08 23:45:27 -05:00
Gustav
bf9c4cda02 route color fix 2025-01-08 23:44:12 -05:00
CI
af00402546 chore: release version v1.37.1
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2025-01-08 21:53:07 +00:00
Aleksei Chichenkov
a245842ca4 Merge pull request #106 from guarzo/guarzo/themes
refactor: cleaned up scss classes, and updated theme dropdown to use declarative style
2025-01-09 00:52:36 +03:00
Gustav
8ddd672f13 fix: add back pathfinder theme font 2025-01-08 16:44:23 -05:00
Gustav
92f471c0b0 refactor: declarative dropdown for theme choice 2025-01-08 16:20:34 -05:00
Gustav
9e2a2c5b44 chore: clean up theme scss class usage 2025-01-08 16:20:25 -05:00
26 changed files with 1789 additions and 538 deletions

View File

@@ -2,6 +2,76 @@
<!-- changelog -->
## [v1.37.8](https://github.com/wanderer-industries/wanderer/compare/v1.37.7...v1.37.8) (2025-01-10)
### Bug Fixes:
* fix issue with newly added systems not adding a connection (#114)
* resolve issue with newly added systems not connecting
## [v1.37.7](https://github.com/wanderer-industries/wanderer/compare/v1.37.6...v1.37.7) (2025-01-10)
### Bug Fixes:
* support additional theme names
## [v1.37.6](https://github.com/wanderer-industries/wanderer/compare/v1.37.5...v1.37.6) (2025-01-09)
### Bug Fixes:
* support additional theme names
## [v1.37.5](https://github.com/wanderer-industries/wanderer/compare/v1.37.4...v1.37.5) (2025-01-09)
### Bug Fixes:
* restore node styling, simplify framework for new themes
## [v1.37.4](https://github.com/wanderer-industries/wanderer/compare/v1.37.3...v1.37.4) (2025-01-09)
### Bug Fixes:
* Map: Fixed dbclick behaviour
## [v1.37.3](https://github.com/wanderer-industries/wanderer/compare/v1.37.2...v1.37.3) (2025-01-09)
### Bug Fixes:
* Map: Fixed dbclick behaviour
## [v1.37.2](https://github.com/wanderer-industries/wanderer/compare/v1.37.1...v1.37.2) (2025-01-09)
## [v1.37.1](https://github.com/wanderer-industries/wanderer/compare/v1.37.0...v1.37.1) (2025-01-08)
### Bug Fixes:
* add back pathfinder theme font
## [v1.37.0](https://github.com/wanderer-industries/wanderer/compare/v1.36.2...v1.37.0) (2025-01-08)

View File

@@ -0,0 +1,173 @@
// feel free to rename these imports or the file path as you see fit
import { useMemo } from 'react';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
import { useDoubleClick } from '@/hooks/Mapper/hooks/useDoubleClick.ts';
import { REGIONS_MAP, Spaces } from '@/hooks/Mapper/constants';
import { MapSolarSystemType } from '../../map.types';
import {
LABELS_INFO,
LABELS_ORDER,
getActivityType,
} from '@/hooks/Mapper/components/map/constants.ts';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
import { getSystemClassStyles, prepareUnsplashedChunks } from '@/hooks/Mapper/components/map/helpers';
import { sortWHClasses } from '@/hooks/Mapper/helpers';
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager.ts';
import { OutCommand } from '@/hooks/Mapper/types';
const SpaceToClass: Record<string, string> = {
[Spaces.Caldari]: 'Caldaria',
[Spaces.Matar]: 'Mataria',
[Spaces.Amarr]: 'Amarria',
[Spaces.Gallente]: 'Gallente',
};
const sortedLabels = (labels: string[]) => {
if (!labels) return [];
return LABELS_ORDER.filter(x => labels.includes(x)).map(x => LABELS_INFO[x]);
};
interface UseSolarSystemNodeParams {
data: MapSolarSystemType;
selected: boolean;
}
export function useSolarSystemNode({ data, selected }: UseSolarSystemNodeParams) {
// 1) Bring in relevant global state
const { interfaceSettings } = useMapRootState();
const { isShowUnsplashedSignatures } = interfaceSettings;
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
const {
data: {
characters,
presentCharacters,
wormholesData,
hubs,
kills,
userCharacters,
isConnecting,
hoverNodeId,
visibleNodes,
showKSpaceBG,
isThickConnections,
},
outCommand,
} = useMapState();
// 2) Extract data from the node
const {
system_class,
security,
class_title,
solar_system_id,
statics,
effect_name,
region_name,
region_id,
is_shattered,
solar_system_name,
} = data.system_static_info;
const { locked, name, tag, status, labels, id, temporary_name: temporaryName } = data || {};
const signatures = data.system_signatures;
// 3) Compute derived values
const visible = useMemo(() => visibleNodes.has(id), [id, visibleNodes]);
const charactersInSystem = useMemo(() => {
return characters
.filter(c => c.location?.solar_system_id === solar_system_id)
.filter(c => c.online);
}, [characters, presentCharacters, solar_system_id]);
const isWormhole = isWormholeSpace(system_class);
const classTitleColor = useMemo(
() => getSystemClassStyles({ systemClass: system_class, security }),
[security, system_class],
);
const sortedStatics = useMemo(
() => sortWHClasses(wormholesData, statics),
[wormholesData, statics],
);
const labelsManager = useMemo(() => new LabelsManager(labels ?? ''), [labels]);
const labelsInfo = useMemo(() => sortedLabels(labelsManager.list), [labelsManager]);
const labelCustom = useMemo(() => labelsManager.customLabel, [labelsManager]);
const killsCount = useMemo(() => {
const systemKills = kills[solar_system_id];
if (!systemKills) return null;
return systemKills;
}, [kills, solar_system_id]);
const hasUserCharacters = useMemo(() => {
return charactersInSystem.some(x => userCharacters.includes(x.eve_id));
}, [charactersInSystem, userCharacters]);
const dbClick = useDoubleClick(() => {
outCommand({
type: OutCommand.openSettings,
data: {
system_id: solar_system_id.toString(),
},
});
});
const showHandlers = isConnecting || hoverNodeId === id;
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
const regionClass = showKSpaceBG ? SpaceToClass[space] : null;
const systemName = (isTempSystemNameEnabled && temporaryName) || solar_system_name;
const customName = (isTempSystemNameEnabled && temporaryName && name)
|| (solar_system_name !== name && name);
const [unsplashedLeft, unsplashedRight] = useMemo(() => {
if (!isShowUnsplashedSignatures) {
return [[], []];
}
return prepareUnsplashedChunks(
signatures
.filter(s => s.group === 'Wormhole' && !s.linked_system)
.map(s => ({
eve_id: s.eve_id,
type: s.type,
custom_info: s.custom_info,
})),
);
}, [isShowUnsplashedSignatures, signatures]);
return {
selected,
visible,
isWormhole,
classTitleColor,
killsCount,
hasUserCharacters,
showHandlers,
regionClass,
systemName,
customName,
labelCustom,
is_shattered,
tag,
status,
labelsInfo,
dbClick,
sortedStatics,
effect_name,
region_name,
solar_system_id,
locked,
hubs,
charactersInSystem,
unsplashedLeft,
unsplashedRight,
isThickConnections,
};
}

View File

@@ -1,8 +1,11 @@
.MapRoot {
width: 100%;
height: 100%;
}
.BackgroundAlternateColor {
background-color: var(--rf-soft-bg-color, #2f2f2f);
background-color: var(--rf-bg-color, #000000);
&.BackgroundAlternateColor {
background-color: var(--rf-soft-bg-color, #171717);
--rf-node-bg-color: var(--rf-node-soft-bg-color, #2b2b2b);
}
}

View File

@@ -1,4 +1,4 @@
import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect } from 'react';
import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect, useMemo } from 'react';
import ReactFlow, {
Background,
ConnectionMode,
@@ -23,10 +23,12 @@ import {
ContextMenuConnection,
ContextMenuRoot,
SolarSystemEdge,
SolarSystemNode,
SolarSystemNodeDefault,
SolarSystemNodeTheme,
useContextMenuConnectionHandlers,
useContextMenuRootHandlers,
} from './components';
import { wrapNode } from './utils/wrapNode';
import { OnMapAddSystemCallback, OnMapSelectionChange } from './map.types';
import { SESSION_KEY } from '@/hooks/Mapper/constants.ts';
import { SolarSystemConnection, SolarSystemRawType } from '@/hooks/Mapper/types';
@@ -75,11 +77,8 @@ const initialEdges = [
},
];
const nodeTypes = {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
custom: SolarSystemNode,
} as never;
const edgeTypes = {
floating: SolarSystemEdge,
@@ -124,6 +123,17 @@ const MapComp = ({
const [nodes, , onNodesChange] = useNodesState<Node<SolarSystemRawType>>(initialNodes);
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>>(initialEdges);
const nodeTypes = useMemo(() => {
return {
custom:
theme !== '' && theme !== 'default'
? wrapNode(SolarSystemNodeTheme)
: wrapNode(SolarSystemNodeDefault),
};
}, [theme]);
useMapHandlers(refn, onSelectionChange);
useUpdateNodes(nodes);
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers({ onAddSystem });

View File

@@ -1,8 +1,33 @@
@import "@/hooks/Mapper/components/map/styles/eve-common-variables";
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
.react-flow__edge.selected {
.EdgePathBack {
stroke: var(--pastel-yellow);
.EdgePathBack {
fill: none;
stroke: #80a5c5;
stroke-width: 3px;
&.TimeCrit {
stroke: #f11ab2;
stroke-width: 4px;
}
&.Hovered {
stroke: #b5c8d9;
&.TimeCrit {
stroke: #ef7dce;
}
}
&.Tick {
stroke-width: 5px;
&.TimeCrit {
stroke-width: 6px;
}
}
&.Gate {
stroke: #9aff40;
}
}
@@ -53,43 +78,28 @@
}
}
.EdgePathBack {
fill: none;
stroke: #80a5c5;
stroke-width: 3px;
&.TimeCrit {
stroke: #f11ab2;
stroke-width: 4px;
}
&.Hovered {
stroke: #b5c8d9;
&.TimeCrit {
stroke: #ef7dce;
}
}
&.Tick {
stroke-width: 5px;
&.TimeCrit {
stroke-width: 6px;
}
}
&.Gate {
stroke: #9aff40;
}
}
.ClickPath {
fill: none;
stroke: none;
stroke-width: 8px;
}
.Handle {
border: 1px solid var(--pastel-blue);
width: 5px;
height: 5px;
z-index: 1001;
&.Tick {
width: 7px;
height: 7px;
}
&.Right {
margin-left: 0px;
}
}
.LinkLabel {
font-size: 9px;
line-height: 10px;
@@ -106,21 +116,3 @@
height: 8px;
font-size: 8px;
}
.Handle {
min-width: initial;
min-height: initial;
border: 1px solid var(--pastel-blue);
width: 5px;
height: 5px;
z-index: 1001;
&.Tick {
width: 7px;
height: 7px;
&.Right {
margin-left: 0px;
}
}
}

View File

@@ -130,7 +130,7 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
/>
<div
className="absolute flex items-center gap-1"
className="absolute flex items-center gap-1 pointer-events-none"
style={{
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
}}

View File

@@ -6,7 +6,6 @@ import clsx from 'clsx';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import {
EFFECT_BACKGROUND_STYLES,
LABELS_INFO,
@@ -77,15 +76,12 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
const { locked, name, tag, status, labels, id, temporary_name: temporaryName } = data || {};
const {
data: {
characters,
presentCharacters,
wormholesData,
hubs,
kills,
userCharacters,
isConnecting,
hoverNodeId,
visibleNodes,
@@ -98,10 +94,8 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
const visible = useMemo(() => visibleNodes.has(id), [id, visibleNodes]);
const charactersInSystem = useMemo(() => {
return characters
.filter(c => c.location?.solar_system_id === solar_system_id)
.filter(c => c.online);
}, [characters, presentCharacters, solar_system_id]);
return characters.filter(c => c.location?.solar_system_id === solar_system_id).filter(c => c.online);
}, [characters, solar_system_id]);
const isWormhole = isWormholeSpace(system_class);
const classTitleColor = useMemo(
@@ -121,10 +115,6 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
return systemKills;
}, [kills, solar_system_id]);
const hasUserCharacters = useMemo(() => {
return charactersInSystem.some(x => userCharacters.includes(x.eve_id));
}, [charactersInSystem, userCharacters]);
const dbClick = useDoubleClick(() => {
outCommand({
type: OutCommand.openSettings,
@@ -134,20 +124,58 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
});
});
const showHandlers = isConnecting || hoverNodeId === id;
const dropHandler = isConnecting ? 'all' : 'none';
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
const regionClass = showKSpaceBG ? SpaceToClass[space] : null;
const systemName = isTempSystemNameEnabled && temporaryName || solar_system_name;
const hasTempName = isTempSystemNameEnabled && temporaryName;
const customName = (isTempSystemNameEnabled && temporaryName && name) || (solar_system_name !== name && name);
// systemName: if temporary name is enabled and present, use it; otherwise use solar_system_name
const systemName = hasTempName
? temporaryName
: solar_system_name;
// hsCustomLabel: if temporary name is enabled and present, show region_name, otherwise labelCustom
const hsCustomLabel = hasTempName
? region_name
: labelCustom;
// whCustomLabel: default to solar_system_name; if that's falsy, use labelCustom
const whCustomLabel = solar_system_name || labelCustom;
// customLabel: if wormhole, use whCustomLabel; otherwise hsCustomLabel
const customLabel = isWormhole
? whCustomLabel
: hsCustomLabel;
// whCustomName: if name differs from solar_system_name, use name; otherwise blank
const whCustomName = (name !== solar_system_name)
? name
: '';
// hsSuffix: if name differs from solar_system_name, append name; otherwise blank
const needsHsSuffix = (name !== solar_system_name);
const hsSuffix = needsHsSuffix
? name
: '';
// hsCustomName: if there's a temp name, show "solar_system_name + suffix", otherwise "region_name + suffix"
const hsCustomName = hasTempName
? `${solar_system_name} ${hsSuffix}`
: `${region_name} ${hsSuffix}`;
// customName: if wormhole, use whCustomName; otherwise hsCustomName
const customName = isWormhole
? whCustomName
: hsCustomName;
const [unsplashedLeft, unsplashedRight] = useMemo(() => {
if (!isShowUnsplashedSignatures) {
return [[], []];
}
return prepareUnsplashedChunks(
signatures
.filter(s => s.group === 'Wormhole' && !s.linked_system)
@@ -163,22 +191,22 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
<>
{visible && (
<div className={classes.Bookmarks}>
{labelCustom !== '' && (
{customLabel !== '' && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
<span className={clsx(classes.textShadowThin)}>{labelCustom}</span>
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] ">{customLabel}</span>
</div>
)}
{is_shattered && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered)}>
<span className={clsx('pi pi-chart-pie', classes.icon)} />
<span className={clsx('pi pi-chart-pie text-[0.55rem]')} />
</div>
)}
{killsCount && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[getActivityType(killsCount)])}>
<div className={clsx(classes.BookmarkWithIcon)}>
<span className={clsx(PrimeIcons.BOLT, classes.icon)} />
<span className={clsx(PrimeIcons.BOLT, 'text-[0.65rem]')} />
<span className={clsx(classes.text)}>{killsCount}</span>
</div>
</div>
@@ -191,42 +219,37 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
))}
</div>
)}
<div
className={clsx(
classes.RootCustomNode,
regionClass,
classes[STATUS_CLASSES[status]],
{ [classes.selected]: selected },
'flex flex-col w-[130px] h-[34px]',
'px-[6px] pt-[2px] pb-[3px] text-[10px]',
'leading-[1] space-y-[1px]',
'shadow-[0_0_5px_rgba(45,45,45,0.5)]',
'border border-[var(--pastel-blue-darken10)] rounded-[5px]',
)}
>
{visible && (
<>
<div className={classes.HeadRow}>
<div
className={clsx(
classes.classTitle,
classTitleColor,
classes.textShadowThin,
)}
>
<div className={clsx(classes.HeadRow, 'flex items-center gap-[3px]')}>
<div className={clsx(classes.classTitle, classTitleColor, '[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]')}>
{class_title ?? '-'}
</div>
{tag != null && tag !== '' && (
<div className={clsx(classes.TagTitle, classes.tagSkyFontMedium)}>{tag}</div>
)}
<div
className={clsx(
classes.classSystemName,
classes.textShadowThin,
classes.systemNameOverflow
'flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans',
)}
>
{systemName}
</div>
{isWormhole && (
<div className={classes.statics}>
<div className={clsx(classes.statics, 'flex gap-[2px] text-[8px]')}>
{sortedStatics.map(x => (
<WormholeClassComp key={x} id={x} />
))}
@@ -234,49 +257,38 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
)}
{effect_name !== null && isWormhole && (
<div
className={clsx(
classes.effect,
EFFECT_BACKGROUND_STYLES[effect_name],
)}
></div>
<div className={clsx(classes.effect, EFFECT_BACKGROUND_STYLES[effect_name])}></div>
)}
</div>
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
{customName && (
<div className={clsx(classes.customName)}>
{customName}
<div className={clsx(classes.BottomRow, 'flex items-center gap-[3px]')}>
<div className="flex items-center gap-2">
{tag != null && tag !== '' && (
<div className={clsx(classes.TagTitle, 'font-medium')}>{`[${tag}]`}</div>
)}
<div
className={clsx(classes.customName)}
title={`${customName ?? ''} ${labelCustom ?? ''}`}
>
{customName} {labelCustom}
</div>
)}
</div>
{!isWormhole && !customName && (
<div className={clsx(classes.regionName)}>
{region_name}
</div>
)}
{isWormhole && !customName && <div />}
<div className="flex items-center justify-end">
<div className="flex gap-1 items-center">
{locked && <i className={clsx(PrimeIcons.LOCK, classes.lockIcon)} />}
{hubs.includes(solar_system_id.toString()) && (
<i className={clsx(PrimeIcons.MAP_MARKER, classes.mapMarkerIcon)} />
)}
{charactersInSystem.length > 0 && (
<div
className={clsx(
classes.localCounter,
{ [classes.hasUserCharacters]: hasUserCharacters },
)}
>
<i className={clsx('pi pi-users', classes.localCounterIcon)} />
<span className="font-sans">{charactersInSystem.length}</span>
</div>
)}
</div>
<div className="flex items-center ml-auto gap-[2px]">
{locked && <i className={clsx(PrimeIcons.LOCK, 'text-[0.45rem] font-bold')} />}
{hubs.includes(solar_system_id.toString()) && (
<i className={clsx(PrimeIcons.MAP_MARKER, 'text-[0.45rem] font-bold')} />
)}
{charactersInSystem.length > 0 && (
<div
className={clsx(
classes.localCounter,
'flex gap-[2px]'
)}
>
<span className="font-sans text-[0.65rem]">{charactersInSystem.length}</span>
</div>
)}
</div>
</div>
</>
@@ -290,9 +302,8 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
))}
</div>
)}
{visible && isShowUnsplashedSignatures && (
<div className={clsx([classes.Unsplashed, classes['Unsplashed--right']])}>
<div className={clsx(classes.Unsplashed, classes['Unsplashed--right'])}>
{unsplashedRight.map(x => (
<UnsplashedSignature key={x.sig_id} signature={x} />
))}
@@ -300,13 +311,33 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
)}
<div onMouseDownCapture={dbClick} className={classes.Handlers}>
<Handle
type="target"
position={Position.Bottom}
style={{
width: '100%',
height: '100%',
background: 'none',
cursor: 'cell',
pointerEvents: dropHandler,
opacity: 0,
borderRadius: 0 }}
id="whole-node-target"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleTop, {
[classes.selected]: selected,
[classes.Tick]: isThickConnections,
})}
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
style={{
width: '100%',
height: '55%',
background: 'none',
cursor: 'cell',
opacity: 0,
borderRadius: 0,
visibility: showHandlers ? 'visible' : 'hidden',}}
position={Position.Top}
id="a"
/>
@@ -316,7 +347,7 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
[classes.selected]: selected,
[classes.Tick]: isThickConnections,
})}
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
style={{ visibility: showHandlers ? 'visible' : 'hidden', cursor: 'cell', zIndex: 10 }}
position={Position.Right}
id="b"
/>
@@ -326,7 +357,7 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
[classes.selected]: selected,
[classes.Tick]: isThickConnections,
})}
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
style={{ visibility: 'hidden', cursor: 'cell' }}
position={Position.Bottom}
id="c"
/>
@@ -336,7 +367,7 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
[classes.selected]: selected,
[classes.Tick]: isThickConnections,
})}
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
style={{ visibility: showHandlers ? 'visible' : 'hidden', cursor: 'cell', zIndex: 10 }}
position={Position.Left}
id="d"
/>

View File

@@ -1,54 +1,30 @@
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
$pastel-blue: #5a7d9a;
$pastel-pink: #d291bc;
$pastel-green: #88b04b;
$pastel-yellow: #ffdd59;
$dark-bg: #2d2d2d;
$text-color: #ffffff;
$tooltip-bg: #202020; // Dark background for tooltips
.RootCustomNode {
display: flex;
width: 130px;
height: 34px;
flex-direction: column;
padding: 2px 6px;
font-size: 10px;
background-color: var(--tooltip-bg);
box-shadow: 0 0 5px rgba(45, 45, 45, 0.5);
border: 1px solid var(--pastel-blue-darken10);
background-color: $tooltip-bg;
box-shadow: 0 0 5px rgba($dark-bg, 0.5);
border: 1px solid darken($pastel-blue, 10%);
border-radius: 5px;
position: relative;
z-index: 1;
overflow: hidden;
.lockIcon {
font-size: 0.45rem;
font-weight: bold;
}
.mapMarkerIcon {
font-size: 0.45rem;
font-weight: bold;
}
.localCounterIcon {
font-size: 0.50rem;
}
.textShadowThin {
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.4);
}
.tagSkyFontMedium {
color: #38bdf8; /* same as Tailwind text-sky-400 */
font-weight: 500; /* same as font-medium */
}
.hasUserCharacters {
/* used to replace text-amber-300 */
color: #fbbf24;
}
.systemNameOverflow {
flex-grow: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-family: sans-serif;
}
&.Mataria,
&.Amarria,
&.Gallente,
@@ -104,56 +80,68 @@
}
}
&.selected {
border-color: var(--pastel-pink);
.selected {
border-color: var($pastel-pink, #d291bc);
box-shadow: 0 0 10px #9a1af1c2;
}
.tooltip {
background-color: var(--tooltip-bg);
color: var(--text-color);
background-color: $tooltip-bg;
color: $text-color;
padding: 5px 10px;
border-radius: 3px;
border: 1px solid var(--pastel-pink);
border: 1px solid $pastel-pink;
}
&.eve-system-status-home {
.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-friendly), transparent);
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-friendly),
transparent
);
&.selected {
border-color: var(--eve-solar-system-status-color-home);
}
}
&.eve-system-status-friendly {
.eve-system-status-friendly {
border: 1px solid var(--eve-solar-system-status-color-friendly-dark20);
background-image: linear-gradient(275deg, var(--eve-solar-system-status-friendly-dark30), transparent);
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-friendly-dark30),
transparent
);
&.selected {
border-color: var(--eve-solar-system-status-color-friendly-dark5);
}
}
&.eve-system-status-lookingFor {
.eve-system-status-lookingFor {
border: 1px solid var(--eve-solar-system-status-color-lookingFor-dark15);
background-image: linear-gradient(275deg, #45ff8f2f, #457fff2f);
&.selected {
border-color: var(--pastel-pink);
border-color: var(--pastel-pink, #d291bc);
}
}
&.eve-system-status-warning {
background-image: linear-gradient(275deg, var(--eve-solar-system-status-warning), transparent);
.eve-system-status-warning {
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-warning),
transparent
);
}
&.eve-system-status-dangerous {
background-image: linear-gradient(275deg, var(--eve-solar-system-status-dangerous), transparent);
.eve-system-status-dangerous {
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-dangerous),
transparent
);
}
&.eve-system-status-target {
background-image: linear-gradient(275deg, var(--eve-solar-system-status-target), transparent);
.eve-system-status-target {
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-target),
transparent
);
}
}
@@ -178,6 +166,8 @@
padding-left: 3px;
padding-right: 3px;
//background-color: #833ca4;
&:not(:first-child) {
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
}
@@ -196,6 +186,7 @@
height: 8px;
font-size: 8px;
}
& > .text {
margin-top: 1px;
font-size: 9px;
@@ -230,7 +221,9 @@
padding-left: 3px;
padding-right: 3px;
display: block;
background-color: #833ca4;
&:not(:first-child) {
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
}
@@ -261,11 +254,13 @@
.TagTitle {
font-size: 11px;
font-weight: bold;
font-weight: medium;
text-shadow: 0 0 2px rgba(231, 146, 52, 0.73);
color: #ffb01d;
color: var(--rf-tag-color, #38BDF8);
}
/* Firefox kostyl */
@-moz-document url-prefix() {
.classSystemName {
font-family: inherit !important;
@@ -274,6 +269,7 @@
}
.classSystemName {
//font-weight: bold;
}
.solarSystemName {
@@ -288,7 +284,20 @@
.localCounter {
display: flex;
//align-items: center;
gap: 2px;
& > i {
position: relative;
top: 1px;
}
& > span {
font-size: 9px;
line-height: 9px;
font-weight: 500;
//margin-top: 1px;
}
}
}
@@ -309,6 +318,8 @@
& > * {
line-height: 10px;
}
/* Firefox kostyl */
@-moz-document url-prefix() {
position: relative;
top: -1px;
@@ -327,22 +338,26 @@
.Handle {
min-width: initial;
min-height: initial;
border: 1px solid var(--pastel-blue);
border: 1px solid $pastel-blue;
width: 5px;
height: 5px;
&.selected {
border-color: var(--pastel-pink);
border-color: $pastel-pink;
}
&.HandleTop {
top: -2px;
}
&.HandleRight {
right: -2px;
}
&.HandleBottom {
bottom: -2px;
}
&.HandleLeft {
left: -2px;
}
@@ -354,12 +369,15 @@
&.HandleTop {
top: -3px;
}
&.HandleRight {
right: -3px;
}
&.HandleBottom {
bottom: -3px;
}
&.HandleLeft {
left: -3px;
}

View File

@@ -0,0 +1,235 @@
import { memo } from 'react';
import { Handle, Position } from 'reactflow';
import clsx from 'clsx';
import classes from './SolarSystemNodeDefault.module.scss';
import { PrimeIcons } from 'primereact/api';
import { useSolarSystemNode } from '../../hooks/useSolarSystemNode';
import {
MARKER_BOOKMARK_BG_STYLES,
STATUS_CLASSES,
EFFECT_BACKGROUND_STYLES,
} from '@/hooks/Mapper/components/map/constants';
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
export const SolarSystemNodeDefault = memo((props) => {
const nodeVars = useSolarSystemNode(props);
return (
<>
{nodeVars.visible && (
<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>
</div>
)}
{nodeVars.isShattered && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered)}>
<span className={clsx('pi pi-chart-pie', classes.icon)} />
</div>
)}
{nodeVars.killsCount && (
<div
className={clsx(
classes.Bookmark,
MARKER_BOOKMARK_BG_STYLES[nodeVars.killsActivityType!]
)}
>
<div className={clsx(classes.BookmarkWithIcon)}>
<span className={clsx(PrimeIcons.BOLT, classes.icon)} />
<span className={clsx(classes.text)}>{nodeVars.killsCount}</span>
</div>
</div>
)}
{nodeVars.labelsInfo.map(x => (
<div
key={x.id}
className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[x.id])}
>
{x.shortName}
</div>
))}
</div>
)}
<div
className={clsx(
classes.RootCustomNode,
nodeVars.regionClass && classes[nodeVars.regionClass],
classes[STATUS_CLASSES[nodeVars.status]],
{ [classes.selected]: nodeVars.selected },
)}
>
{nodeVars.visible && (
<>
<div className={classes.HeadRow}>
<div
className={clsx(
classes.classTitle,
nodeVars.classTitleColor,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]',
)}
>
{nodeVars.classTitle ?? '-'}
</div>
{nodeVars.tag != null && nodeVars.tag !== '' && (
<div className={clsx(classes.TagTitle, 'text-sky-400 font-medium')}>
{nodeVars.tag}
</div>
)}
<div
className={clsx(
classes.classSystemName,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans',
)}
>
{nodeVars.systemName}
</div>
{nodeVars.isWormhole && (
<div className={classes.statics}>
{nodeVars.sortedStatics.map(whClass => (
<WormholeClassComp key={whClass} id={whClass} />
))}
</div>
)}
{nodeVars.effectName !== null && nodeVars.isWormhole && (
<div
className={clsx(
classes.effect,
EFFECT_BACKGROUND_STYLES[nodeVars.effectName],
)}
/>
)}
</div>
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
{nodeVars.customName && (
<div className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-blue-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5">
{nodeVars.customName}
</div>
)}
{!nodeVars.isWormhole && !nodeVars.customName && (
<div
className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-stone-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5"
>
{nodeVars.regionName}
</div>
)}
{nodeVars.isWormhole && !nodeVars.customName && <div />}
<div className="flex items-center justify-end">
<div className="flex gap-1 items-center">
{nodeVars.locked && (
<i
className={PrimeIcons.LOCK}
style={{ fontSize: '0.45rem', fontWeight: 'bold' }}
/>
)}
{nodeVars.hubs.includes(nodeVars.solarSystemId.toString()) && (
<i
className={PrimeIcons.MAP_MARKER}
style={{ fontSize: '0.45rem', fontWeight: 'bold' }}
/>
)}
{nodeVars.charactersInSystem.length > 0 && (
<div
className={clsx(classes.localCounter, {
['text-amber-300']: nodeVars.hasUserCharacters,
})}
>
<i className="pi pi-users" style={{ fontSize: '0.50rem' }} />
<span className="font-sans">
{nodeVars.charactersInSystem.length}
</span>
</div>
)}
</div>
</div>
</div>
</>
)}
</div>
{nodeVars.visible && (
<>
{nodeVars.unsplashedLeft.length > 0 && (
<div className={classes.Unsplashed}>
{nodeVars.unsplashedLeft.map(sig => (
<UnsplashedSignature key={sig.sig_id} signature={sig} />
))}
</div>
)}
{nodeVars.unsplashedRight.length > 0 && (
<div className={clsx(classes.Unsplashed, classes['Unsplashed--right'])}>
{nodeVars.unsplashedRight.map(sig => (
<UnsplashedSignature key={sig.sig_id} signature={sig} />
))}
</div>
)}
</>
)}
<div onMouseDownCapture={nodeVars.dbClick} className={classes.Handlers}>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleTop, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Top}
id="a"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleRight, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Right}
id="b"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleBottom, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Bottom}
id="c"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleLeft, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Left}
id="d"
/>
</div>
</>
);
});

View File

@@ -0,0 +1,402 @@
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
$pastel-blue: #5a7d9a;
$pastel-pink: #d291bc;
$pastel-green: #88b04b;
$pastel-yellow: #ffdd59;
$dark-bg: #2d2d2d;
$text-color: #ffffff;
$tooltip-bg: #202020; // Dark background for tooltips
.RootCustomNode {
display: flex;
width: 130px;
height: 34px;
flex-direction: column;
padding: 2px 6px;
font-size: 10px;
background-color: var(--rf-node-bg-color, #202020) !important;
color: var(--rf-text-color, #ffffff);
font-family: var(--rf-node-font-family, inherit) !important;
font-weight: var(--rf-node-font-weight, inherit) !important;
box-shadow: 0 0 5px rgba($dark-bg, 0.5);
border: 1px solid darken($pastel-blue, 10%);
border-radius: 5px;
position: relative;
z-index: 1;
overflow: hidden;
&.Mataria,
&.Amarria,
&.Gallente,
&.Caldaria {
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-size: cover;
background-position: 50% 50%;
z-index: -1;
background-repeat: no-repeat;
border-radius: 3px;
}
}
&.Mataria {
&::before {
background-image: url('/images/mataria-180.png');
opacity: 0.6;
background-position-x: 1px;
background-position-y: -14px;
}
}
&.Caldaria {
&::before {
background-image: url('/images/caldaria-180.png');
opacity: 0.6;
background-position-x: 1px;
background-position-y: -10px;
}
}
&.Amarria {
&::before {
opacity: 0.45;
background-image: url('/images/amarr-180.png');
background-position-x: 0;
background-position-y: -13px;
}
}
&.Gallente {
&::before {
opacity: 0.5;
background-image: url('/images/gallente-180.png');
background-position-x: 1px;
background-position-y: 0;
}
}
.selected {
border-color: var($pastel-pink, #d291bc);
box-shadow: 0 0 10px #9a1af1c2;
}
.tooltip {
background-color: $tooltip-bg;
color: $text-color;
padding: 5px 10px;
border-radius: 3px;
border: 1px solid $pastel-pink;
}
.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-friendly),
transparent
);
&.selected {
border-color: var(--eve-solar-system-status-color-home);
}
}
.eve-system-status-friendly {
border: 1px solid var(--eve-solar-system-status-color-friendly-dark20);
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-friendly-dark30),
transparent
);
&.selected {
border-color: var(--eve-solar-system-status-color-friendly-dark5);
}
}
.eve-system-status-lookingFor {
border: 1px solid var(--eve-solar-system-status-color-lookingFor-dark15);
background-image: linear-gradient(275deg, #45ff8f2f, #457fff2f);
&.selected {
border-color: var(--pastel-pink, #d291bc);
}
}
.eve-system-status-warning {
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-warning),
transparent
);
}
.eve-system-status-dangerous {
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-dangerous),
transparent
);
}
.eve-system-status-target {
background-image: linear-gradient(
275deg,
var(--eve-solar-system-status-target),
transparent
);
}
}
.Bookmarks {
position: absolute;
width: 100%;
z-index: 1;
display: flex;
left: 4px;
& > .Bookmark {
min-width: 13px;
height: 22px;
position: relative;
top: -13px;
border-radius: 5px;
color: #ffffff;
font-size: 8px;
text-align: center;
padding-top: 2px;
font-weight: bolder;
padding-left: 3px;
padding-right: 3px;
//background-color: #833ca4;
&:not(:first-child) {
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
}
}
.BookmarkWithIcon {
display: flex;
justify-content: center;
align-items: center;
margin-top: -2px;
text-shadow: 0 0 3px rgba(0, 0, 0, 1);
padding-right: 2px;
& > .icon {
width: 8px;
height: 8px;
font-size: 8px;
}
& > .text {
margin-top: 1px;
font-size: 9px;
}
}
}
.Unsplashed {
position: absolute;
width: calc(50% - 4px);
z-index: -1;
display: flex;
flex-wrap: wrap;
gap: 2px;
left: 2px;
&--right {
left: calc(50% + 6px);
}
& > .Signature {
width: 13px;
height: 4px;
position: relative;
top: 3px;
border-radius: 5px;
color: #ffffff;
font-size: 8px;
text-align: center;
padding-top: 2px;
font-weight: bolder;
padding-left: 3px;
padding-right: 3px;
display: block;
background-color: #833ca4;
&:not(:first-child) {
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
}
}
}
.icon {
width: 8px;
height: 8px;
font-size: 8px;
}
.HeadRow {
display: flex;
align-items: center;
gap: 3px;
font-size: 11px;
line-height: 14px;
font-weight: 500;
position: relative;
top: 1px;
.classTitle {
font-size: 11px;
font-weight: bold;
text-shadow: 0 0 2px rgb(0 0 0 / 73%);
}
.TagTitle {
font-size: 11px;
font-weight: medium;
text-shadow: 0 0 2px rgba(231, 146, 52, 0.73);
color: var(--rf-tag-color, #38BDF8);
}
/* Firefox kostyl */
@-moz-document url-prefix() {
.classSystemName {
font-family: inherit !important;
font-weight: bold;
}
}
.classSystemName {
font-family: inherit !important;
font-weight: bold;
}
.solarSystemName {
}
}
.BottomRow {
display: flex;
justify-content: space-between;
align-items: center;
height: 19px;
.regionName {
color: var(--rf-region-name, #D6D3D1)
}
.customName {
color: var(--rf-custom-name, #93C5FD)
}
.localCounter {
display: flex;
//align-items: center;
gap: 2px;
.hasUserCharacters {
color: var(--rf-has-user-characters, #fbbf24);
}
& > i {
position: relative;
top: 1px;
}
& > span {
font-size: 9px;
line-height: 9px;
font-weight: 500;
//margin-top: 1px;
}
}
}
.effect {
width: 8px;
height: 8px;
margin-top: -2px;
box-sizing: border-box;
border-radius: 2px;
margin-left: 1px;
}
.statics {
display: flex;
gap: 2px;
font-size: 8px;
& > * {
line-height: 10px;
}
/* Firefox kostyl */
@-moz-document url-prefix() {
position: relative;
top: -1px;
}
}
.Handlers {
position: absolute;
z-index: 2;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.Handle {
min-width: initial;
min-height: initial;
border: 1px solid $pastel-blue;
width: 5px;
height: 5px;
&.selected {
border-color: $pastel-pink;
}
&.HandleTop {
top: -2px;
}
&.HandleRight {
right: -2px;
}
&.HandleBottom {
bottom: -2px;
}
&.HandleLeft {
left: -2px;
}
&.Tick {
width: 7px;
height: 7px;
&.HandleTop {
top: -3px;
}
&.HandleRight {
right: -3px;
}
&.HandleBottom {
bottom: -3px;
}
&.HandleLeft {
left: -3px;
}
}
}

View File

@@ -0,0 +1,236 @@
import { memo } from 'react';
import { Handle, Position } from 'reactflow';
import clsx from 'clsx';
import classes from './SolarSystemNodeTheme.module.scss';
import { PrimeIcons } from 'primereact/api';
import { useSolarSystemNode } from '../../hooks/useSolarSystemNode';
import {
MARKER_BOOKMARK_BG_STYLES,
STATUS_CLASSES,
EFFECT_BACKGROUND_STYLES,
} from '@/hooks/Mapper/components/map/constants';
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
export const SolarSystemNodeTheme = memo((props) => {
const nodeVars = useSolarSystemNode(props);
return (
<>
{nodeVars.visible && (
<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>
</div>
)}
{nodeVars.isShattered && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered)}>
<span className={clsx('pi pi-chart-pie', classes.icon)} />
</div>
)}
{nodeVars.killsCount && (
<div
className={clsx(
classes.Bookmark,
MARKER_BOOKMARK_BG_STYLES[nodeVars.killsActivityType!]
)}
>
<div className={clsx(classes.BookmarkWithIcon)}>
<span className={clsx(PrimeIcons.BOLT, classes.icon)} />
<span className={clsx(classes.text)}>{nodeVars.killsCount}</span>
</div>
</div>
)}
{nodeVars.labelsInfo.map(x => (
<div
key={x.id}
className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[x.id])}
>
{x.shortName}
</div>
))}
</div>
)}
<div
className={clsx(
classes.RootCustomNode,
nodeVars.regionClass && classes[nodeVars.regionClass],
classes[STATUS_CLASSES[nodeVars.status]],
{ [classes.selected]: nodeVars.selected },
)}
>
{nodeVars.visible && (
<>
<div className={classes.HeadRow}>
<div
className={clsx(
classes.classTitle,
nodeVars.classTitleColor,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]',
)}
>
{nodeVars.classTitle ?? '-'}
</div>
{nodeVars.tag != null && nodeVars.tag !== '' && (
<div className={clsx(classes.TagTitle)}>
{nodeVars.tag}
</div>
)}
<div
className={clsx(
classes.classSystemName,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] flex-grow overflow-hidden text-ellipsis whitespace-nowrap',
)}
>
{nodeVars.systemName}
</div>
{nodeVars.isWormhole && (
<div className={classes.statics}>
{nodeVars.sortedStatics.map(whClass => (
<WormholeClassComp key={whClass} id={whClass} />
))}
</div>
)}
{nodeVars.effectName !== null && nodeVars.isWormhole && (
<div className={clsx(classes.effect, EFFECT_BACKGROUND_STYLES[nodeVars.effectName])} />
)}
</div>
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
{nodeVars.customName && (
<div
className={clsx(
classes.CustomName,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] whitespace-nowrap overflow-hidden text-ellipsis mr-0.5',
)}
>
{nodeVars.customName}
</div>
)}
{!nodeVars.isWormhole && !nodeVars.customName && (
<div
className={clsx(
classes.RegionName,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] whitespace-nowrap overflow-hidden text-ellipsis mr-0.5',
)}
>
{nodeVars.regionName}
</div>
)}
{nodeVars.isWormhole && !nodeVars.customName && <div />}
<div className="flex items-center justify-end">
<div className="flex gap-1 items-center">
{nodeVars.locked && (
<i
className={PrimeIcons.LOCK}
style={{ fontSize: '0.45rem', fontWeight: 'bold' }}
/>
)}
{nodeVars.hubs.includes(nodeVars.solarSystemId.toString()) && (
<i
className={PrimeIcons.MAP_MARKER}
style={{ fontSize: '0.45rem', fontWeight: 'bold' }}
/>
)}
{nodeVars.charactersInSystem.length > 0 && (
<div
className={clsx(classes.localCounter, {
[classes.hasUserCharacters]: nodeVars.hasUserCharacters,
})}
>
<i className="pi pi-users" style={{ fontSize: '0.50rem' }} />
<span className="font-sans">{nodeVars.charactersInSystem.length}</span>
</div>
)}
</div>
</div>
</div>
</>
)}
</div>
{nodeVars.visible && (
<>
{nodeVars.unsplashedLeft.length > 0 && (
<div className={classes.Unsplashed}>
{nodeVars.unsplashedLeft.map(sig => (
<UnsplashedSignature key={sig.sig_id} signature={sig} />
))}
</div>
)}
{nodeVars.unsplashedRight.length > 0 && (
<div className={clsx(classes.Unsplashed, classes['Unsplashed--right'])}>
{nodeVars.unsplashedRight.map(sig => (
<UnsplashedSignature key={sig.sig_id} signature={sig} />
))}
</div>
)}
</>
)}
<div onMouseDownCapture={nodeVars.dbClick} className={classes.Handlers}>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleTop, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Top}
id="a"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleRight, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Right}
id="b"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleBottom, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Bottom}
id="c"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleLeft, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Left}
id="d"
/>
</div>
</>
);
});

View File

@@ -1 +1,2 @@
export * from './SolarSystemNode';
export * from './SolarSystemNodeDefault';
export * from './SolarSystemNodeTheme';

View File

@@ -1,15 +1,17 @@
import { useEffect, useState } from 'react';
import { BackgroundVariant } from 'reactflow';
export function useBackgroundVars(themeName?: string) {
const [variant, setVariant] = useState<BackgroundVariant>(BackgroundVariant.Dots);
const [gap, setGap] = useState<number>(16);
const [size, setSize] = useState<number>(1);
const [color, setColor] = useState('#81818b')
const [color, setColor] = useState('#81818b');
useEffect(() => {
let themeEl = document.querySelector('.pathfinder-theme, .neon-theme');
useEffect(() => {
// match any element whose entire `class` attribute ends with "-theme"
let themeEl = document.querySelector('[class$="-theme"]');
// If none is found, fall back to the <html> element
if (!themeEl) {
themeEl = document.documentElement;
}
@@ -18,6 +20,7 @@ export function useBackgroundVars(themeName?: string) {
const rawVariant = style.getPropertyValue('--rf-bg-variant').replace(/['"]/g, '').trim().toLowerCase();
let finalVariant: BackgroundVariant = BackgroundVariant.Dots;
if (rawVariant === 'lines') {
finalVariant = BackgroundVariant.Lines;
} else if (rawVariant === 'cross') {
@@ -26,7 +29,7 @@ export function useBackgroundVars(themeName?: string) {
const cssVarGap = style.getPropertyValue('--rf-bg-gap');
const cssVarSize = style.getPropertyValue('--rf-bg-size');
const cssColor = style.getPropertyValue('--rf-bg-color');
const cssColor = style.getPropertyValue('--rf-bg-pattern-color');
const gapNum = parseInt(cssVarGap, 10) || 16;
const sizeNum = parseInt(cssVarSize, 10) || 1;
@@ -35,8 +38,7 @@ export function useBackgroundVars(themeName?: string) {
setGap(gapNum);
setSize(sizeNum);
setColor(cssColor);
}, [themeName]);
}, [themeName]);
return { variant, gap, size, color };
}

View File

@@ -0,0 +1,171 @@
import { useMemo } from 'react';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider';
import { useDoubleClick } from '@/hooks/Mapper/hooks/useDoubleClick';
import { REGIONS_MAP, Spaces } from '@/hooks/Mapper/constants';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace';
import { getSystemClassStyles, prepareUnsplashedChunks } from '@/hooks/Mapper/components/map/helpers';
import { sortWHClasses } from '@/hooks/Mapper/helpers';
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager';
import { OutCommand } from '@/hooks/Mapper/types';
import { LABELS_INFO, LABELS_ORDER } from '@/hooks/Mapper/components/map/constants';
function getActivityType(count: number) {
if (count <= 5) return 'activityNormal';
if (count <= 30) return 'activityWarn';
return 'activityDanger';
}
const SpaceToClass: Record<string, string> = {
[Spaces.Caldari]: 'Caldaria',
[Spaces.Matar]: 'Mataria',
[Spaces.Amarr]: 'Amarria',
[Spaces.Gallente]: 'Gallente',
};
function sortedLabels(labels: string[]) {
if (!labels) return [];
return LABELS_ORDER.filter(x => labels.includes(x)).map(x => LABELS_INFO[x]);
}
export function useSolarSystemNode(props: any) {
const { data, selected, id } = props;
const { system_static_info, system_signatures, locked, name, tag, status, labels, temporary_name } = data;
const {
system_class,
security,
class_title,
solar_system_id,
statics,
effect_name,
region_name,
region_id,
is_shattered,
solar_system_name,
} = system_static_info;
// Global map state
const { interfaceSettings } = useMapRootState();
const { isShowUnsplashedSignatures } = interfaceSettings;
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
const {
data: {
characters,
presentCharacters,
wormholesData,
hubs,
kills,
userCharacters,
isConnecting,
hoverNodeId,
visibleNodes,
showKSpaceBG,
isThickConnections,
},
outCommand,
} = useMapState();
// logic
const visible = useMemo(() => visibleNodes.has(id), [id, visibleNodes]);
const charactersInSystem = useMemo(() => {
return characters.filter(c => c.location?.solar_system_id === solar_system_id).filter(c => c.online);
// eslint-disable-next-line
}, [characters, presentCharacters, solar_system_id]);
const isWormhole = isWormholeSpace(system_class);
const classTitleColor = useMemo(
() => getSystemClassStyles({ systemClass: system_class, security }),
[security, system_class],
);
const sortedStatics = useMemo(() => sortWHClasses(wormholesData, statics), [wormholesData, statics]);
const labelsManager = useMemo(() => new LabelsManager(labels ?? ''), [labels]);
const labelsInfo = useMemo(() => sortedLabels(labelsManager.list), [labelsManager]);
const labelCustom = useMemo(() => labelsManager.customLabel, [labelsManager]);
const killsCount = useMemo(() => kills[solar_system_id] ?? null, [kills, solar_system_id]);
const killsActivityType = killsCount ? getActivityType(killsCount) : null;
const hasUserCharacters = useMemo(() => {
return charactersInSystem.some(x => userCharacters.includes(x.eve_id));
}, [charactersInSystem, userCharacters]);
const dbClick = useDoubleClick(() => {
outCommand({
type: OutCommand.openSettings,
data: { system_id: solar_system_id.toString() },
});
});
const showHandlers = isConnecting || hoverNodeId === id;
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
const regionClass = showKSpaceBG ? SpaceToClass[space] : null;
const systemName = (isTempSystemNameEnabled && temporary_name) || solar_system_name;
const customName =
(isTempSystemNameEnabled && temporary_name && name) || (solar_system_name !== name && name);
const [unsplashedLeft, unsplashedRight] = useMemo(() => {
if (!isShowUnsplashedSignatures) {
return [[], []];
}
return prepareUnsplashedChunks(
system_signatures
.filter(s => s.group === 'Wormhole' && !s.linked_system)
.map(s => ({
eve_id: s.eve_id,
type: s.type,
custom_info: s.custom_info,
})),
);
}, [isShowUnsplashedSignatures, system_signatures]);
const nodeVars = {
// original props
id,
selected,
// computed
visible,
isWormhole,
classTitleColor,
killsCount,
killsActivityType,
hasUserCharacters,
showHandlers,
regionClass,
systemName,
customName,
labelCustom,
isShattered: is_shattered,
tag,
status,
labelsInfo,
dbClick,
sortedStatics,
effectName: effect_name,
regionName: region_name,
solarSystemId: solar_system_id,
solarSystemName: solar_system_name,
locked,
hubs,
name: name,
isConnecting,
hoverNodeId,
charactersInSystem,
unsplashedLeft,
unsplashedRight,
isThickConnections,
classTitle: class_title,
temporaryName: temporary_name,
};
return nodeVars;
}

View File

@@ -0,0 +1,31 @@
@import './eve-common-variables';
@import './eve-common';
.default-theme {
--rf-bg-color: #000000;
--rf-soft-bg-color: #171717;
--rf-node-bg-color: #202020;
--rf-node-soft-bg-color: #2b2b2b;
--rf-text-color: #ffffff;
--rf-tag-color: #38BDF8;
--rf-region-name: #D6D3D1;
--rf-custom-name: #93C5FD;
--rf-bg-variant: "dots";
--rf-bg-gap: 16;
--rf-bg-size: 1;
--rf-bg-pattern-color: #81818a;
--pastel-blue: #5a7d9a;
--pastel-pink: #d291bc;
--pastel-green: #88b04b;
--pastel-yellow: #ffdd59;
--dark-bg: #2d2d2d;
--text-color: #ffffff;
--tooltip-bg: #202020;
}

View File

@@ -485,3 +485,49 @@
.wd-marker-bookmark-color-danger {
background-color: #d10600;
}
.react-flow {
color: var(--text-color);
&__pane {
cursor: auto;
}
&__minimap {
background-color: rgba(66, 66, 66, 1);
opacity: 0.7;
border: 1px solid #2f2f2f;
border-radius: 4px;
overflow: hidden;
}
&__minimap-mask {
fill: rgba(28, 28, 28, 0.75);
}
&__controls {
filter: brightness(1.5);
}
&__minimap-node {
fill: #ffb03a;
}
}
.context-menu-active {
background-color: rgba(131, 131, 131, 0.33);
}
.p-dialog {
.p-dialog-header {
height: 40px;
padding: 1rem;
padding-right: 10px !important;
}
.p-dialog-title {
font-size: 1rem !important;
}
.p-dialog-header-icons {
align-self: initial !important;
}
}

View File

@@ -1,2 +1,2 @@
@import './neon-theme.scss';
@import './default-theme.scss';
@import './pathfinder-theme.scss';

View File

@@ -1,85 +0,0 @@
@import './eve-common-variables';
@import './eve-common';
.neon-theme {
--pastel-blue: #5a7d9a;
--pastel-pink: #d291bc;
--pastel-green: #88b04b;
--pastel-yellow: #ffdd59;
--dark-bg: #2d2d2d;
--text-color: #ffffff;
--tooltip-bg: #202020;
--rf-bg-variant: "dots"; /* or lines, if you prefer */
--rf-bg-gap: 16; /* some default gap */
.react-flow {
color: var(--text-color);
&__node {
/* cursor: auto; */
}
&__pane {
cursor: auto;
}
/*
&__edge {
stroke: var(--pastel-pink);
stroke-width: 2px;
&.selected {
stroke: var(--pastel-yellow);
}
}
*/
&__handle {
/* background-color: var(--pastel-green);
box-shadow: 0 0 5px rgba(88, 176, 75, 0.5); // or define an RGB var
*/
}
&__minimap {
background-color: rgba(66, 66, 66, 1);
opacity: 0.7;
border: 1px solid #2f2f2f;
border-radius: 4px;
overflow: hidden;
}
&__minimap-mask {
fill: rgba(28, 28, 28, 0.75);
}
&__controls {
filter: brightness(1.5);
}
&__minimap-node {
fill: #ffb03a;
}
}
.context-menu-active {
background-color: rgba(131, 131, 131, 0.33);
}
.p-dialog {
.p-dialog-header {
height: 40px;
padding: 1rem;
padding-right: 10px !important;
}
.p-dialog-title {
font-size: 1rem !important;
}
.p-dialog-header-icons {
align-self: initial !important;
}
}
}

View File

@@ -1,21 +1,26 @@
@import './eve-common-variables';
@import './eve-common';
@import url('https://fonts.googleapis.com/css2?family=Oxygen:wght@300;400;700&display=swap');
.pathfinder-theme {
--rf-bg-color: #000000;
--rf-soft-bg-color: #282828;
--rf-bg-variant: "lines";
--rf-bg-gap: 32;
--rf-bg-color: #353535;
--rf-soft-bg-color: #282829;
--rf-node-bg-color: #202020;
--rf-node-soft-bg-color: #313335;
--rf-node-font-weight: bold;
--rf-text-color: #adadad;
--rf-region-name: var(--rf-text-color);
--rf-custom-name: var(--rf-text-color);
--tooltip-bg: #202020;
--pf-text-color: #adadad;
--pf-dark-bg: #2d2d2d;
--pf-tooltip-bg: #202020;
--rf-bg-variant: "lines";
--rf-bg-gap: 32;
--rf-bg-size: 1;
--rf-bg-color: #313131;
--rf-bg-pattern-color: #313131;
--eve-effect-pulsar: #428bca;
--eve-effect-magnetar: #e06fdf;
--eve-effect-wolfRayet: #e28a0d;
@@ -35,163 +40,12 @@
--eve-wh-type-color-c13: #7986cb;
--eve-wh-type-color-drifter: #44aa82;
.MapRoot {
background-color: var(--rf-bg-color);
&.isSoftBackground {
background-color: var(--rf-soft-bg-color);
}
&.soft-bg {
--rf-bg-color: var(--rf-soft-bg-color);
}
}
--rf-node-font-weight: bold;
--rf-node-line-height: normal;
--rf-node-font-family: 'Oxygen', sans-serif;
--rf-node-text-color: var(--pf-text-color);
.RootCustomNode {
background-color: #313335 !important; /* or var(--pf-dark-bg) */
font-weight: 600;
line-height: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: var(--pf-text-color);
}
.HeadRow {
display: flex;
align-items: center;
gap: 3px;
font-size: 11px;
line-height: 14px;
font-weight: 500;
position: relative;
top: 1px;
.classTitle {
font-size: 11px;
font-weight: bold;
text-shadow: 0 0 1px rgba(0, 0, 0, 0.7);
}
@-moz-document url-prefix() {
.classSystemName {
font-family: inherit !important;
font-weight: bold;
mix-blend-mode: screen;
}
}
.classSystemName {
font-family: inherit !important;
font-weight: bold;
color: var(--pf-text-color);
mix-blend-mode: screen;
text-shadow: rgba(0, 0, 0, 0.4) 1px 1px;
}
.textEllipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.fontSans {
font-family: sans-serif;
}
}
.BottomRow {
.localCounter {
display: flex;
gap: 2px;
& > i {
position: relative;
top: 1px;
}
& > span {
font-size: 9px;
line-height: 9px;
font-weight: 700;
mix-blend-mode: screen;
color: #5cb85c; /* or var(--eve-wh-type-color-high) */
}
}
.customName {
font-family: inherit !important;
font-weight: bold;
color: var(--pf-text-color);
mix-blend-mode: screen;
text-shadow: rgba(0, 0, 0, 0.4) 1px 1px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
flex-shrink: 1;
}
.tagTitle {
font-size: 9px;
font-family: inherit !important;
font-weight: bold;
color: #fbbf24 !important;
mix-blend-mode: screen;
}
}
.Handlers {
position: absolute;
z-index: 2;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.Handle {
min-width: initial;
min-height: initial;
border: 1px solid var(--pastel-blue, #5a7d9a);
width: 5px;
height: 5px;
&.selected {
border-color: var(--pastel-pink, #d291bc);
}
&.HandleTop {
top: -2px;
}
&.HandleRight {
right: -2px;
}
&.HandleBottom {
bottom: -2px;
}
&.HandleLeft {
left: -2px;
}
&.Tick {
width: 7px;
height: 7px;
&.HandleTop {
top: -3px;
}
&.HandleRight {
right: -3px;
}
&.HandleBottom {
bottom: -3px;
}
&.HandleLeft {
left: -3px;
}
}
}
.react-flow {
color: var(--pf-text-color);
}
}
--rf-tag-color: #fbbf24;
--rf-has-user-characters: #5cb85c;
}

View File

@@ -0,0 +1,11 @@
// wrapNode.ts
import { NodeProps } from 'reactflow';
import { SolarSystemNodeProps } from '../components/SolarSystemNode';
export function wrapNode<T>(
SolarSystemNode: React.FC<SolarSystemNodeProps<T>>
): React.FC<NodeProps<T>> {
return function NodeAdapter(props) {
return <SolarSystemNode {...props} />;
};
}

View File

@@ -8,7 +8,6 @@
.RouteSystem {
width: 8px;
height: 8px;
background: #ffffff;
cursor: pointer;
transition: opacity 200ms;

View File

@@ -16,7 +16,7 @@ export const MapRootContent = ({}: MapRootContentProps) => {
const { interfaceSettings } = useMapRootState();
const { isShowMenu } = interfaceSettings;
const themeClass = `${interfaceSettings.theme ?? 'neon'}-theme`;
const themeClass = `${interfaceSettings.theme ?? 'default'}-theme`;
const [showOnTheMap, setShowOnTheMap] = useState(false);
const [showMapSettings, setShowMapSettings] = useState(false);

View File

@@ -42,46 +42,93 @@ export interface MapSettingsProps {
onHide: () => void;
}
type CheckboxesList = {
type SettingsListItem = {
prop: keyof UserSettings;
label: string;
}[];
type: 'checkbox' | 'dropdown';
options?: { label: string; value: string }[];
};
const COMMON_CHECKBOXES_PROPS: CheckboxesList = [
{ prop: InterfaceStoredSettingsProps.isShowMinimap, label: 'Show Minimap' },
const COMMON_CHECKBOXES_PROPS: SettingsListItem[] = [
{
prop: InterfaceStoredSettingsProps.isShowMinimap,
label: 'Show Minimap',
type: 'checkbox',
},
];
const SYSTEMS_CHECKBOXES_PROPS: CheckboxesList = [
{ prop: InterfaceStoredSettingsProps.isShowKSpace, label: 'Highlight Low/High-security systems' },
{ prop: UserSettingsRemoteProps.select_on_spash, label: 'Auto-select splashed' },
const SYSTEMS_CHECKBOXES_PROPS: SettingsListItem[] = [
{
prop: InterfaceStoredSettingsProps.isShowKSpace,
label: 'Highlight Low/High-security systems',
type: 'checkbox',
},
{
prop: UserSettingsRemoteProps.select_on_spash,
label: 'Auto-select splashed',
type: 'checkbox',
},
];
const SIGNATURES_CHECKBOXES_PROPS: CheckboxesList = [
{ prop: UserSettingsRemoteProps.link_signature_on_splash, label: 'Link signature on splash' },
{ prop: InterfaceStoredSettingsProps.isShowUnsplashedSignatures, label: 'Show unsplashed signatures' },
const SIGNATURES_CHECKBOXES_PROPS: SettingsListItem[] = [
{
prop: UserSettingsRemoteProps.link_signature_on_splash,
label: 'Link signature on splash',
type: 'checkbox',
},
{
prop: InterfaceStoredSettingsProps.isShowUnsplashedSignatures,
label: 'Show unsplashed signatures',
type: 'checkbox',
},
];
const CONNECTIONS_CHECKBOXES_PROPS: CheckboxesList = [
{ prop: UserSettingsRemoteProps.delete_connection_with_sigs, label: 'Delete connections to linked signatures' },
{ prop: InterfaceStoredSettingsProps.isThickConnections, label: 'Thicker connections' },
const CONNECTIONS_CHECKBOXES_PROPS: SettingsListItem[] = [
{
prop: UserSettingsRemoteProps.delete_connection_with_sigs,
label: 'Delete connections to linked signatures',
type: 'checkbox',
},
{
prop: InterfaceStoredSettingsProps.isThickConnections,
label: 'Thicker connections',
type: 'checkbox',
},
];
const UI_CHECKBOXES_PROPS: CheckboxesList = [
{ prop: InterfaceStoredSettingsProps.isShowMenu, label: 'Enable compact map menu bar' },
{ prop: InterfaceStoredSettingsProps.isShowBackgroundPattern, label: 'Show background pattern' },
{ prop: InterfaceStoredSettingsProps.isSoftBackground, label: 'Enable soft background' },
const UI_CHECKBOXES_PROPS: SettingsListItem[] = [
{
prop: InterfaceStoredSettingsProps.isShowMenu,
label: 'Enable compact map menu bar',
type: 'checkbox',
},
{
prop: InterfaceStoredSettingsProps.isShowBackgroundPattern,
label: 'Show background pattern',
type: 'checkbox',
},
{
prop: InterfaceStoredSettingsProps.isSoftBackground,
label: 'Enable soft background',
type: 'checkbox',
},
];
const THEME_OPTIONS = [
{ label: 'Default', value: 'neon' },
{ label: 'Default', value: 'default' },
{ label: 'Pathfinder', value: 'pathfinder' },
];
const THEME_SETTING: SettingsListItem = {
prop: 'theme',
label: 'Theme',
type: 'dropdown',
options: THEME_OPTIONS,
};
export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
const [activeIndex, setActiveIndex] = useState(0);
const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState();
const [userRemoteSettings, setUserRemoteSettings] = useState<UserSettingsRemote>({
...DEFAULT_REMOTE_SETTINGS,
});
@@ -104,50 +151,63 @@ export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
});
};
const handleChangeChecked = useCallback(
(prop: keyof UserSettings) => async (checked: boolean) => {
const handleSettingChange = useCallback(
async (prop: keyof UserSettings, value: boolean | string) => {
if (UserSettingsRemoteList.includes(prop as any)) {
const newRemoteSettings = {
...userRemoteSettings,
[prop]: checked,
[prop]: value,
};
await outCommand({
type: OutCommand.updateUserSettings,
data: newRemoteSettings,
});
setUserRemoteSettings(newRemoteSettings);
return;
} else {
setInterfaceSettings({
...interfaceSettings,
[prop]: value,
});
}
setInterfaceSettings({
...interfaceSettings,
[prop]: checked,
});
},
[userRemoteSettings, interfaceSettings, outCommand, setInterfaceSettings],
);
const renderCheckboxesList = (list: CheckboxesList) => {
return list.map((x) => (
<PrettySwitchbox
key={x.prop}
label={x.label}
checked={mergedSettings[x.prop]}
setChecked={handleChangeChecked(x.prop)}
/>
));
const renderSettingItem = (item: SettingsListItem) => {
const currentValue = mergedSettings[item.prop];
if (item.type === 'checkbox') {
return (
<PrettySwitchbox
key={item.prop}
label={item.label}
checked={!!currentValue}
setChecked={(checked) => handleSettingChange(item.prop, checked)}
/>
);
}
if (item.type === 'dropdown' && item.options) {
return (
<div key={item.prop} className="flex items-center gap-2 mt-2">
<label className="text-sm">{item.label}:</label>
<Dropdown
className="text-sm"
value={currentValue}
options={item.options}
onChange={(e) => handleSettingChange(item.prop, e.value)}
placeholder="Select a theme"
/>
</div>
);
}
return null;
};
const handleChangeTheme = useCallback(
(newThemeValue: string) => {
setInterfaceSettings({
...interfaceSettings,
theme: newThemeValue,
});
},
[interfaceSettings, setInterfaceSettings]
);
const renderSettingsList = (list: SettingsListItem[]) => {
return list.map(renderSettingItem);
};
return (
<Dialog
@@ -172,39 +232,30 @@ export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
>
<TabPanel header="Common" headerClassName={styles.verticalTabHeader}>
<div className="w-full h-full flex flex-col gap-1">
{renderCheckboxesList(COMMON_CHECKBOXES_PROPS)}
{renderSettingsList(COMMON_CHECKBOXES_PROPS)}
</div>
</TabPanel>
<TabPanel header="Systems" headerClassName={styles.verticalTabHeader}>
<div className="w-full h-full flex flex-col gap-1">
{renderCheckboxesList(SYSTEMS_CHECKBOXES_PROPS)}
{renderSettingsList(SYSTEMS_CHECKBOXES_PROPS)}
</div>
</TabPanel>
<TabPanel header="Connections" headerClassName={styles.verticalTabHeader}>
{renderCheckboxesList(CONNECTIONS_CHECKBOXES_PROPS)}
{renderSettingsList(CONNECTIONS_CHECKBOXES_PROPS)}
</TabPanel>
<TabPanel header="Signatures" headerClassName={styles.verticalTabHeader}>
{renderCheckboxesList(SIGNATURES_CHECKBOXES_PROPS)}
{renderSettingsList(SIGNATURES_CHECKBOXES_PROPS)}
</TabPanel>
<TabPanel header="User Interface" headerClassName={styles.verticalTabHeader}>
{renderCheckboxesList(UI_CHECKBOXES_PROPS)}
{renderSettingsList(UI_CHECKBOXES_PROPS)}
</TabPanel>
<TabPanel header="Theme" headerClassName={styles.verticalTabHeader}>
<div className="flex items-center gap-2 mt-2">
<label className="text-sm">Select Theme:</label>
<Dropdown
className="text-sm"
value={interfaceSettings.theme || 'neon'} // default to "neon"
options={THEME_OPTIONS}
onChange={(e) => handleChangeTheme(e.value)}
placeholder="Select a theme"
/>
</div>
{renderSettingItem(THEME_SETTING)}
</TabPanel>
</TabView>
</div>

View File

@@ -59,7 +59,7 @@ export const STORED_INTERFACE_DEFAULT_VALUES: InterfaceStoredSettings = {
isShowUnsplashedSignatures: false,
isShowBackgroundPattern: true,
isSoftBackground: false,
theme: 'neon',
theme: 'default',
}
export interface MapRootContextProps {

View File

@@ -284,7 +284,7 @@ defmodule WandererApp.Map.Server.SystemsImpl do
location.solar_system_id
) do
{:ok, existing_system} when not is_nil(existing_system) ->
{:ok, updated_system} =
updated_system =
existing_system
|> WandererApp.MapSystemRepo.update_position!(%{
position_x: position.x,
@@ -292,8 +292,8 @@ defmodule WandererApp.Map.Server.SystemsImpl do
})
|> WandererApp.MapSystemRepo.cleanup_labels!(map_opts)
|> WandererApp.MapSystemRepo.update_visible!(%{visible: true})
|> WandererApp.MapSystemRepo.cleanup_tags()
|> WandererApp.MapSystemRepo.cleanup_temporary_name()
|> WandererApp.MapSystemRepo.cleanup_tags!()
|> WandererApp.MapSystemRepo.cleanup_temporary_name!()
@ddrt.insert(
{existing_system.solar_system_id,

View File

@@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do
@source_url "https://github.com/wanderer-industries/wanderer"
@version "1.37.0"
@version "1.37.8"
def project do
[