mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-11-28 12:03:22 +00:00
Compare commits
141 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82f90ef759 | ||
|
|
167c8eea6b | ||
|
|
d76079d4c7 | ||
|
|
bf9c4cda02 | ||
|
|
af00402546 | ||
|
|
a245842ca4 | ||
|
|
8ddd672f13 | ||
|
|
92f471c0b0 | ||
|
|
9e2a2c5b44 | ||
|
|
5f5d3df003 | ||
|
|
c66cc8868e | ||
|
|
0d6528ce4f | ||
|
|
34c385ac5f | ||
|
|
b6d12e73a9 | ||
|
|
1118858120 | ||
|
|
ae3a34d5bf | ||
|
|
43df42e49b | ||
|
|
e670f3bf03 | ||
|
|
c26a9404c5 | ||
|
|
c0fad4ca92 | ||
|
|
16dbf9378b | ||
|
|
4001fe5eac | ||
|
|
2992dd8f8b | ||
|
|
98a03d1e59 | ||
|
|
2088393c79 | ||
|
|
093042b88a | ||
|
|
e5ef35c186 | ||
|
|
1cd23d5efd | ||
|
|
ead5818a3f | ||
|
|
a5f66ada68 | ||
|
|
0919742853 | ||
|
|
f3efffd259 | ||
|
|
f85317983c | ||
|
|
76f709b768 | ||
|
|
e3b2356302 | ||
|
|
3d810211ee | ||
|
|
7453795dc5 | ||
|
|
9de7cd99ee | ||
|
|
51489c1aa5 | ||
|
|
25dd6de770 | ||
|
|
2a825f5a02 | ||
|
|
908d249eb9 | ||
|
|
6cd119e8f4 | ||
|
|
9a59c8eb75 | ||
|
|
452c022d41 | ||
|
|
27e9bab82a | ||
|
|
edef860530 | ||
|
|
032cb63411 | ||
|
|
a1791ba578 | ||
|
|
3a69fd7786 | ||
|
|
8a90723c2e | ||
|
|
af2fc342c7 | ||
|
|
05ea2fcdbe | ||
|
|
6d4321fead | ||
|
|
3f6364c9ea | ||
|
|
0d11b12282 | ||
|
|
0796bcf7d0 | ||
|
|
0b5bec142a | ||
|
|
a5020b58f2 | ||
|
|
f039a74a8f | ||
|
|
0e6bb7390b | ||
|
|
b52b4eecca | ||
|
|
8186977d1d | ||
|
|
86adcfe4d7 | ||
|
|
ce2dd872c4 | ||
|
|
aadc53c90e | ||
|
|
cbc1b6b5c8 | ||
|
|
1aed7a9232 | ||
|
|
b549189644 | ||
|
|
35279d17b4 | ||
|
|
bb403aa0c5 | ||
|
|
04327c288b | ||
|
|
94d60e40d0 | ||
|
|
8505fcb6b7 | ||
|
|
e0a37f7635 | ||
|
|
9aec57166d | ||
|
|
a3739f2950 | ||
|
|
3d3b152758 | ||
|
|
0e03730543 | ||
|
|
97e07a6511 | ||
|
|
a77a51ba15 | ||
|
|
42e706e1c2 | ||
|
|
025dd06053 | ||
|
|
bcb421d879 | ||
|
|
66056ab54b | ||
|
|
bb92f76ceb | ||
|
|
84076b340b | ||
|
|
48caae5c0e | ||
|
|
77dd23795a | ||
|
|
2771d6304e | ||
|
|
9946edffa4 | ||
|
|
50bf2fd9d3 | ||
|
|
bdcde168aa | ||
|
|
5807142e20 | ||
|
|
ec2d9565b9 | ||
|
|
a18a71c73d | ||
|
|
93a6bd1156 | ||
|
|
581a410aef | ||
|
|
ab02fe988c | ||
|
|
b8d20fb21b | ||
|
|
12fa1a0be8 | ||
|
|
85a84f7507 | ||
|
|
2385313013 | ||
|
|
c7ce727571 | ||
|
|
8b165ff478 | ||
|
|
6d7d0cc72d | ||
|
|
f7eba5d4fd | ||
|
|
73ef6dae73 | ||
|
|
7fa6df1e5e | ||
|
|
e1a2ffb151 | ||
|
|
6d7727a32d | ||
|
|
6d7a94bd5a | ||
|
|
ecc3fb17e1 | ||
|
|
209e2bf0a5 | ||
|
|
b1947e57a4 | ||
|
|
74507501a5 | ||
|
|
c73481fd58 | ||
|
|
7795ad0b0c | ||
|
|
aff768f413 | ||
|
|
310b60f5b6 | ||
|
|
100f0be86a | ||
|
|
87e115e40d | ||
|
|
ef5f36e4c4 | ||
|
|
099650420d | ||
|
|
8ccf7fffa5 | ||
|
|
b97a055bf7 | ||
|
|
663fee6699 | ||
|
|
33d5f3938b | ||
|
|
ef6b45d7a1 | ||
|
|
c1ecd3690e | ||
|
|
3250fe1ec6 | ||
|
|
48e8cd93b9 | ||
|
|
afacbb16b6 | ||
|
|
dfad127f32 | ||
|
|
300c1b5a18 | ||
|
|
bb38e1710b | ||
|
|
0857a82de5 | ||
|
|
da5afcc91c | ||
|
|
0002979fda | ||
|
|
080af16d41 | ||
|
|
d03a0b7083 |
@@ -6,3 +6,4 @@ export EVE_CLIENT_WITH_WALLET_ID="<EVE_CLIENT_WITH_WALLET_ID>"
|
||||
export EVE_CLIENT_WITH_WALLET_SECRET="<EVE_CLIENT_WITH_WALLET_SECRET>"
|
||||
export GIT_SHA="1111"
|
||||
export WANDERER_INVITES="false"
|
||||
export WANDERER_PUBLIC_API_DISABLED="false"
|
||||
|
||||
1182
CHANGELOG.md
1182
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,8 @@
|
||||
@import 'primereact/resources/themes/arya-blue/theme.css' layer(primereact);
|
||||
/*@import 'primereact/resources/themes/bootstrap4-dark-blue/theme.css' layer(primereact);*/
|
||||
|
||||
@import '../js/hooks/Mapper/components/map/styles/index.scss';
|
||||
|
||||
@layer tailwind-base {
|
||||
@tailwind base;
|
||||
}
|
||||
@@ -466,3 +468,467 @@ body {
|
||||
transform: rotate(-360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Map refresh */
|
||||
.socket {
|
||||
scale: 0.5;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
left: 50%;
|
||||
/* margin-left: -75px; */
|
||||
top: 50%;
|
||||
/* margin-top: -50px; */
|
||||
}
|
||||
|
||||
.hex-brick {
|
||||
background: #000;
|
||||
width: 30px;
|
||||
height: 17px;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
animation-name: fade;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
-webkit-animation-name: fade;
|
||||
-webkit-animation-duration: 2s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.hex-brick--active {
|
||||
animation-name: fade-active;
|
||||
-webkit-animation-name: fade-active;
|
||||
}
|
||||
|
||||
.h2 {
|
||||
transform: rotate(60deg);
|
||||
-webkit-transform: rotate(60deg);
|
||||
}
|
||||
|
||||
.h3 {
|
||||
transform: rotate(-60deg);
|
||||
-webkit-transform: rotate(-60deg);
|
||||
}
|
||||
|
||||
.gel {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
transition: all 0.3s;
|
||||
-webkit-transition: all 0.3s;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
.center-gel {
|
||||
margin-left: -15px;
|
||||
margin-top: -15px;
|
||||
|
||||
animation-name: pulse-version;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
-webkit-animation-name: pulse-version;
|
||||
-webkit-animation-duration: 2s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
margin-left: -47px;
|
||||
margin-top: -15px;
|
||||
}
|
||||
|
||||
.c2 {
|
||||
margin-left: -31px;
|
||||
margin-top: -43px;
|
||||
}
|
||||
|
||||
.c3 {
|
||||
margin-left: 1px;
|
||||
margin-top: -43px;
|
||||
}
|
||||
|
||||
.c4 {
|
||||
margin-left: 17px;
|
||||
margin-top: -15px;
|
||||
}
|
||||
.c5 {
|
||||
margin-left: -31px;
|
||||
margin-top: 13px;
|
||||
}
|
||||
|
||||
.c6 {
|
||||
margin-left: 1px;
|
||||
margin-top: 13px;
|
||||
}
|
||||
|
||||
.c7 {
|
||||
margin-left: -63px;
|
||||
margin-top: -43px;
|
||||
}
|
||||
|
||||
.c8 {
|
||||
margin-left: 33px;
|
||||
margin-top: -43px;
|
||||
}
|
||||
|
||||
.c9 {
|
||||
margin-left: -15px;
|
||||
margin-top: 41px;
|
||||
}
|
||||
|
||||
.c10 {
|
||||
margin-left: -63px;
|
||||
margin-top: 13px;
|
||||
}
|
||||
|
||||
.c11 {
|
||||
margin-left: 33px;
|
||||
margin-top: 13px;
|
||||
}
|
||||
|
||||
.c12 {
|
||||
margin-left: -15px;
|
||||
margin-top: -71px;
|
||||
}
|
||||
|
||||
.c13 {
|
||||
margin-left: -47px;
|
||||
margin-top: -71px;
|
||||
}
|
||||
|
||||
.c14 {
|
||||
margin-left: 17px;
|
||||
margin-top: -71px;
|
||||
}
|
||||
|
||||
.c15 {
|
||||
margin-left: -47px;
|
||||
margin-top: 41px;
|
||||
}
|
||||
|
||||
.c16 {
|
||||
margin-left: 17px;
|
||||
margin-top: 41px;
|
||||
}
|
||||
|
||||
.c17 {
|
||||
margin-left: -79px;
|
||||
margin-top: -15px;
|
||||
}
|
||||
|
||||
.c18 {
|
||||
margin-left: 49px;
|
||||
margin-top: -15px;
|
||||
}
|
||||
|
||||
.c19 {
|
||||
margin-left: -63px;
|
||||
margin-top: -99px;
|
||||
}
|
||||
|
||||
.c20 {
|
||||
margin-left: 33px;
|
||||
margin-top: -99px;
|
||||
}
|
||||
|
||||
.c21 {
|
||||
margin-left: 1px;
|
||||
margin-top: -99px;
|
||||
}
|
||||
|
||||
.c22 {
|
||||
margin-left: -31px;
|
||||
margin-top: -99px;
|
||||
}
|
||||
|
||||
.c23 {
|
||||
margin-left: -63px;
|
||||
margin-top: 69px;
|
||||
}
|
||||
|
||||
.c24 {
|
||||
margin-left: 33px;
|
||||
margin-top: 69px;
|
||||
}
|
||||
|
||||
.c25 {
|
||||
margin-left: 1px;
|
||||
margin-top: 69px;
|
||||
}
|
||||
|
||||
.c26 {
|
||||
margin-left: -31px;
|
||||
margin-top: 69px;
|
||||
}
|
||||
|
||||
.c27 {
|
||||
margin-left: -79px;
|
||||
margin-top: -15px;
|
||||
}
|
||||
|
||||
.c28 {
|
||||
margin-left: -95px;
|
||||
margin-top: -43px;
|
||||
}
|
||||
|
||||
.c29 {
|
||||
margin-left: -95px;
|
||||
margin-top: 13px;
|
||||
}
|
||||
|
||||
.c30 {
|
||||
margin-left: 49px;
|
||||
margin-top: 41px;
|
||||
}
|
||||
|
||||
.c31 {
|
||||
margin-left: -79px;
|
||||
margin-top: -71px;
|
||||
}
|
||||
|
||||
.c32 {
|
||||
margin-left: -111px;
|
||||
margin-top: -15px;
|
||||
}
|
||||
|
||||
.c33 {
|
||||
margin-left: 65px;
|
||||
margin-top: -43px;
|
||||
}
|
||||
|
||||
.c34 {
|
||||
margin-left: 65px;
|
||||
margin-top: 13px;
|
||||
}
|
||||
|
||||
.c35 {
|
||||
margin-left: -79px;
|
||||
margin-top: 41px;
|
||||
}
|
||||
|
||||
.c36 {
|
||||
margin-left: 49px;
|
||||
margin-top: -71px;
|
||||
}
|
||||
|
||||
.c37 {
|
||||
margin-left: 81px;
|
||||
margin-top: -15px;
|
||||
}
|
||||
|
||||
.r1 {
|
||||
animation-name: pulse-version;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-delay: 0.2s;
|
||||
-webkit-animation-name: pulse-version;
|
||||
-webkit-animation-duration: 2s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.r2 {
|
||||
animation-name: pulse-version;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-delay: 0.4s;
|
||||
-webkit-animation-name: pulse-version;
|
||||
-webkit-animation-duration: 2s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.r3 {
|
||||
animation-name: pulse-version;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-delay: 0.6s;
|
||||
-webkit-animation-name: pulse-version;
|
||||
-webkit-animation-duration: 2s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-delay: 0.6s;
|
||||
}
|
||||
|
||||
.r1 > .hex-brick {
|
||||
animation-name: fade;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-delay: 0.2s;
|
||||
-webkit-animation-name: fade;
|
||||
-webkit-animation-duration: 2s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.r1 > .hex-brick--active {
|
||||
animation-name: fade-active;
|
||||
-webkit-animation-name: fade-active;
|
||||
}
|
||||
|
||||
.r2 > .hex-brick {
|
||||
animation-name: fade;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-delay: 0.4s;
|
||||
-webkit-animation-name: fade;
|
||||
-webkit-animation-duration: 2s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.r2 > .hex-brick--active {
|
||||
animation-name: fade-active;
|
||||
-webkit-animation-name: fade-active;
|
||||
}
|
||||
|
||||
.r3 > .hex-brick {
|
||||
animation-name: fade;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-delay: 0.6s;
|
||||
-webkit-animation-name: fade;
|
||||
-webkit-animation-duration: 2s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-delay: 0.6s;
|
||||
}
|
||||
|
||||
.r3 > .hex-brick--active {
|
||||
animation-name: fade-active;
|
||||
-webkit-animation-name: fade-active;
|
||||
}
|
||||
|
||||
@keyframes pulse-version {
|
||||
0% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform: scale(0.01);
|
||||
transform: scale(0.01);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade {
|
||||
0% {
|
||||
background: #09d0e2;
|
||||
}
|
||||
|
||||
50% {
|
||||
background: #8ae6ee;
|
||||
}
|
||||
|
||||
100% {
|
||||
background: #09d0e2;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-active {
|
||||
0% {
|
||||
background: #ff52d9;
|
||||
}
|
||||
|
||||
50% {
|
||||
background: #ff52d9;
|
||||
}
|
||||
|
||||
100% {
|
||||
background: #ff52d9;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes pulse {
|
||||
0% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform: scale(0.01);
|
||||
transform: scale(0.01);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes fade {
|
||||
0% {
|
||||
background: #abf8ff;
|
||||
}
|
||||
|
||||
50% {
|
||||
background: #389ca6;
|
||||
}
|
||||
|
||||
100% {
|
||||
background: #abf8ff;
|
||||
}
|
||||
}
|
||||
/* Map refresh END */
|
||||
|
||||
.inputContainer {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
align-items: center;
|
||||
}
|
||||
.inputContainer > span:nth-child(1),
|
||||
.inputContainer > label:nth-child(1) {
|
||||
color: var(--gray-200);
|
||||
font-size: 13px;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.inputContainer > :nth-child(2) {
|
||||
border-bottom: 2px dotted #3f3f3f;
|
||||
height: 1px;
|
||||
margin: 0 12px;
|
||||
}
|
||||
|
||||
.smallInputSwitch {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.smallInputSwitch .p-inputswitch {
|
||||
height: 1rem;
|
||||
width: 2rem;
|
||||
}
|
||||
.smallInputSwitch .p-inputswitch.p-inputswitch-checked .p-inputswitch-slider::before {
|
||||
transform: translateX(1rem);
|
||||
}
|
||||
.smallInputSwitch .p-inputswitch.p-highlight .p-inputswitch-slider:before {
|
||||
transform: translateX(1rem);
|
||||
}
|
||||
.smallInputSwitch .p-inputswitch .p-inputswitch-slider::before {
|
||||
width: 0.8rem;
|
||||
height: 0.8rem;
|
||||
margin-top: -0.4rem;
|
||||
margin-left: -3px;
|
||||
}
|
||||
|
||||
.checkboxRoot.sizeXS {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
.checkboxRoot.sizeXS .p-checkbox-box,
|
||||
.checkboxRoot.sizeXS .p-checkbox-input {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
.checkboxRoot.sizeM {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.checkboxRoot.sizeM .p-checkbox-box,
|
||||
.checkboxRoot.sizeM .p-checkbox-input {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
@@ -108,3 +108,7 @@
|
||||
.p-dropdown-empty-message {
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
|
||||
.p-autocomplete .p-autocomplete-multiple-container .p-autocomplete-token {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
@@ -88,6 +88,23 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
|
||||
setSystem(undefined);
|
||||
}, []);
|
||||
|
||||
const onSystemTemporaryName = useCallback((temporaryName?: string) => {
|
||||
const { system, outCommand } = ref.current;
|
||||
if (!system) {
|
||||
return;
|
||||
}
|
||||
|
||||
outCommand({
|
||||
type: OutCommand.updateSystemTemporaryName,
|
||||
data: {
|
||||
system_id: system,
|
||||
value: temporaryName ?? '',
|
||||
},
|
||||
});
|
||||
setSystem(undefined);
|
||||
}, []);
|
||||
|
||||
|
||||
const onSystemStatus = useCallback((status: number) => {
|
||||
const { system, outCommand } = ref.current;
|
||||
if (!system) {
|
||||
@@ -161,6 +178,7 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
|
||||
onLockToggle,
|
||||
onHubToggle,
|
||||
onSystemTag,
|
||||
onSystemTemporaryName,
|
||||
onSystemStatus,
|
||||
onSystemLabels,
|
||||
onOpenSettings,
|
||||
|
||||
@@ -6,6 +6,9 @@ import { PrimeIcons } from 'primereact/api';
|
||||
import { ContextMenuSystemProps } from '@/hooks/Mapper/components/contexts';
|
||||
import { useWaypointMenu } from '@/hooks/Mapper/components/contexts/hooks';
|
||||
import { FastSystemActions } from '@/hooks/Mapper/components/contexts/components';
|
||||
import { useMapCheckPermissions } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
|
||||
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
|
||||
|
||||
export const useContextMenuSystemItems = ({
|
||||
onDeleteSystem,
|
||||
@@ -25,6 +28,7 @@ export const useContextMenuSystemItems = ({
|
||||
const getStatus = useStatusMenu(systems, systemId, onSystemStatus);
|
||||
const getLabels = useLabelsMenu(systems, systemId, onSystemLabels, onCustomLabelDialog);
|
||||
const getWaypointMenu = useWaypointMenu(onWaypointSet);
|
||||
const canLockSystem = useMapCheckPermissions([UserPermission.LOCK_SYSTEM]);
|
||||
|
||||
return useMemo(() => {
|
||||
const system = systemId ? getSystemById(systems, systemId) : undefined;
|
||||
@@ -41,6 +45,8 @@ export const useContextMenuSystemItems = ({
|
||||
<FastSystemActions
|
||||
systemId={systemId}
|
||||
systemName={system.system_static_info.solar_system_name}
|
||||
regionName={system.system_static_info.region_name}
|
||||
isWH={isWormholeSpace(system.system_static_info.system_class)}
|
||||
showEdit
|
||||
onOpenSettings={onOpenSettings}
|
||||
/>
|
||||
@@ -58,19 +64,25 @@ export const useContextMenuSystemItems = ({
|
||||
command: onHubToggle,
|
||||
},
|
||||
...(system.locked
|
||||
? [
|
||||
{
|
||||
label: 'Unlock',
|
||||
icon: PrimeIcons.LOCK_OPEN,
|
||||
command: onLockToggle,
|
||||
},
|
||||
]
|
||||
? canLockSystem
|
||||
? [
|
||||
{
|
||||
label: 'Unlock',
|
||||
icon: PrimeIcons.LOCK_OPEN,
|
||||
command: onLockToggle,
|
||||
},
|
||||
]
|
||||
: []
|
||||
: [
|
||||
{
|
||||
label: 'Lock',
|
||||
icon: PrimeIcons.LOCK,
|
||||
command: onLockToggle,
|
||||
},
|
||||
...(canLockSystem
|
||||
? [
|
||||
{
|
||||
label: 'Lock',
|
||||
icon: PrimeIcons.LOCK,
|
||||
command: onLockToggle,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{ separator: true },
|
||||
{
|
||||
label: 'Delete',
|
||||
@@ -80,6 +92,7 @@ export const useContextMenuSystemItems = ({
|
||||
]),
|
||||
];
|
||||
}, [
|
||||
canLockSystem,
|
||||
systems,
|
||||
systemId,
|
||||
getTags,
|
||||
|
||||
@@ -10,6 +10,7 @@ import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/ty
|
||||
import { FastSystemActions } from '@/hooks/Mapper/components/contexts/components';
|
||||
import { useJumpPlannerMenu } from '@/hooks/Mapper/components/contexts/hooks';
|
||||
import { Route } from '@/hooks/Mapper/types/routes.ts';
|
||||
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
|
||||
|
||||
export interface ContextMenuSystemInfoProps {
|
||||
systemStatics: Map<number, SolarSystemStaticInfoRaw>;
|
||||
@@ -48,7 +49,6 @@ export const ContextMenuSystemInfo: React.FC<ContextMenuSystemInfoProps> = ({
|
||||
if (!systemId || !system) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
className: classes.FastActions,
|
||||
@@ -57,6 +57,8 @@ export const ContextMenuSystemInfo: React.FC<ContextMenuSystemInfoProps> = ({
|
||||
<FastSystemActions
|
||||
systemId={systemId}
|
||||
systemName={system.solar_system_name}
|
||||
regionName={system.region_name}
|
||||
isWH={isWormholeSpace(system.system_class)}
|
||||
onOpenSettings={onOpenSettings}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -9,13 +9,22 @@ import { PrimeIcons } from 'primereact/api';
|
||||
export interface FastSystemActionsProps {
|
||||
systemId: string;
|
||||
systemName: string;
|
||||
regionName: string;
|
||||
isWH: boolean;
|
||||
showEdit?: boolean;
|
||||
onOpenSettings(): void;
|
||||
}
|
||||
|
||||
export const FastSystemActions = ({ systemId, systemName, onOpenSettings, showEdit }: FastSystemActionsProps) => {
|
||||
const ref = useRef({ systemId, systemName });
|
||||
ref.current = { systemId, systemName };
|
||||
export const FastSystemActions = ({
|
||||
systemId,
|
||||
systemName,
|
||||
regionName,
|
||||
isWH,
|
||||
onOpenSettings,
|
||||
showEdit,
|
||||
}: FastSystemActionsProps) => {
|
||||
const ref = useRef({ systemId, systemName, regionName, isWH });
|
||||
ref.current = { systemId, systemName, regionName, isWH };
|
||||
|
||||
const handleOpenZKB = useCallback(
|
||||
() => window.open(`https://zkillboard.com/system/${ref.current.systemId}`, '_blank'),
|
||||
@@ -27,10 +36,17 @@ export const FastSystemActions = ({ systemId, systemName, onOpenSettings, showEd
|
||||
[],
|
||||
);
|
||||
|
||||
const handleOpenDotlan = useCallback(
|
||||
() => window.open(`https://evemaps.dotlan.net/system/${ref.current.systemName}`, '_blank'),
|
||||
[],
|
||||
);
|
||||
const handleOpenDotlan = useCallback(() => {
|
||||
if (ref.current.isWH) {
|
||||
window.open(`https://evemaps.dotlan.net/system/${ref.current.systemName}`, '_blank');
|
||||
return;
|
||||
}
|
||||
|
||||
return window.open(
|
||||
`https://evemaps.dotlan.net/map/${ref.current.regionName.replace(/ /gim, '_')}/${ref.current.systemName}#jumps`,
|
||||
'_blank',
|
||||
);
|
||||
}, []);
|
||||
|
||||
const copySystemNameToClipboard = useCallback(async () => {
|
||||
try {
|
||||
@@ -43,9 +59,9 @@ export const FastSystemActions = ({ systemId, systemName, onOpenSettings, showEd
|
||||
return (
|
||||
<LayoutEventBlocker className={clsx('flex px-2 gap-2 justify-between items-center h-full')}>
|
||||
<div className={clsx('flex gap-2 items-center h-full', classes.Links)}>
|
||||
<WdImgButton source={ZKB_ICON} onClick={handleOpenZKB} />
|
||||
<WdImgButton source={ANOIK_ICON} onClick={handleOpenAnoikis} />
|
||||
<WdImgButton source={DOTLAN_ICON} onClick={handleOpenDotlan} />
|
||||
<WdImgButton tooltip={{ content: 'Open zkillboard' }} source={ZKB_ICON} onClick={handleOpenZKB} />
|
||||
<WdImgButton tooltip={{ content: 'Open Anoikis' }} source={ANOIK_ICON} onClick={handleOpenAnoikis} />
|
||||
<WdImgButton tooltip={{ content: 'Open Dotlan' }} source={DOTLAN_ICON} onClick={handleOpenDotlan} />
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 items-center pl-1">
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
.MapRoot {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,16 +12,12 @@ import ReactFlow, {
|
||||
OnSelectionChangeFunc,
|
||||
SelectionDragHandler,
|
||||
SelectionMode,
|
||||
useEdgesState,
|
||||
useNodesState,
|
||||
useReactFlow,
|
||||
} from 'reactflow';
|
||||
import 'reactflow/dist/style.css';
|
||||
import classes from './Map.module.scss';
|
||||
import './styles/neon-theme.scss';
|
||||
import './styles/eve-common.scss';
|
||||
import { MapProvider, useMapState } from './MapProvider';
|
||||
import { useMapHandlers, useUpdateNodes } from './hooks';
|
||||
import { useNodesState, useEdgesState, useMapHandlers, useUpdateNodes } from './hooks';
|
||||
import { MapHandlers, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import {
|
||||
ContextMenuConnection,
|
||||
@@ -31,11 +27,13 @@ import {
|
||||
useContextMenuConnectionHandlers,
|
||||
useContextMenuRootHandlers,
|
||||
} from './components';
|
||||
import { OnMapSelectionChange } from './map.types';
|
||||
import { OnMapAddSystemCallback, OnMapSelectionChange } from './map.types';
|
||||
import { SESSION_KEY } from '@/hooks/Mapper/constants.ts';
|
||||
import { SolarSystemConnection, SolarSystemRawType } from '@/hooks/Mapper/types';
|
||||
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||
import { NodeSelectionMouseHandler } from '@/hooks/Mapper/components/contexts/types.ts';
|
||||
import clsx from 'clsx';
|
||||
import { useBackgroundVars } from './hooks/useBackgroundVars';
|
||||
|
||||
const DEFAULT_VIEW_PORT = { zoom: 1, x: 0, y: 0 };
|
||||
|
||||
@@ -93,12 +91,16 @@ interface MapCompProps {
|
||||
onSelectionChange: OnMapSelectionChange;
|
||||
onManualDelete(systems: string[]): void;
|
||||
onConnectionInfoClick?(e: SolarSystemConnection): void;
|
||||
onAddSystem?: OnMapAddSystemCallback;
|
||||
onSelectionContextMenu?: NodeSelectionMouseHandler;
|
||||
minimapClasses?: string;
|
||||
isShowMinimap?: boolean;
|
||||
onSystemContextMenu: (event: MouseEvent<Element>, systemId: string) => void;
|
||||
showKSpaceBG?: boolean;
|
||||
isThickConnections?: boolean;
|
||||
isShowBackgroundPattern?: boolean;
|
||||
isSoftBackground?: boolean;
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
const MapComp = ({
|
||||
@@ -113,16 +115,21 @@ const MapComp = ({
|
||||
isShowMinimap,
|
||||
showKSpaceBG,
|
||||
isThickConnections,
|
||||
isShowBackgroundPattern,
|
||||
isSoftBackground,
|
||||
theme,
|
||||
onAddSystem,
|
||||
}: MapCompProps) => {
|
||||
const { getNode } = useReactFlow();
|
||||
const [nodes, , onNodesChange] = useNodesState<SolarSystemRawType>(initialNodes);
|
||||
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>[]>(initialEdges);
|
||||
const { getNode, getNodes } = useReactFlow();
|
||||
const [nodes, , onNodesChange] = useNodesState<Node<SolarSystemRawType>>(initialNodes);
|
||||
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>>(initialEdges);
|
||||
|
||||
useMapHandlers(refn, onSelectionChange);
|
||||
useUpdateNodes(nodes);
|
||||
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers();
|
||||
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers({ onAddSystem });
|
||||
const { handleConnectionContext, ...connectionCtxProps } = useContextMenuConnectionHandlers();
|
||||
const { update } = useMapState();
|
||||
const { variant, gap, size, color } = useBackgroundVars(theme);
|
||||
|
||||
const onConnect: OnConnect = useCallback(
|
||||
params => {
|
||||
@@ -181,6 +188,12 @@ const MapComp = ({
|
||||
(changes: NodeChange[]) => {
|
||||
const systemsIdsToRemove: string[] = [];
|
||||
|
||||
// prevents single node deselection on background / same node click
|
||||
// allows deseletion of all nodes if multiple are currently selected
|
||||
if (changes.length === 1 && changes[0].type == 'select' && changes[0].selected === false) {
|
||||
changes[0].selected = getNodes().filter(node => node.selected).length === 1;
|
||||
}
|
||||
|
||||
const nextChanges = changes.reduce((acc, change) => {
|
||||
if (change.type !== 'remove') {
|
||||
return [...acc, change];
|
||||
@@ -218,7 +231,7 @@ const MapComp = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={classes.MapRoot}>
|
||||
<div className={clsx(classes.MapRoot, { [classes.BackgroundAlternateColor]: isSoftBackground })}>
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
@@ -265,7 +278,7 @@ const MapComp = ({
|
||||
selectionMode={SelectionMode.Partial}
|
||||
>
|
||||
{isShowMinimap && <MiniMap pannable zoomable ariaLabel="Mini map" className={minimapClasses} />}
|
||||
<Background />
|
||||
{isShowBackgroundPattern && <Background variant={variant} gap={gap} size={size} color={color} />}
|
||||
</ReactFlow>
|
||||
{/* <button className="z-auto btn btn-primary absolute top-20 right-20" onClick={handleGetPassages}>
|
||||
Test // DON NOT REMOVE
|
||||
|
||||
@@ -32,6 +32,7 @@ const INITIAL_DATA: MapData = {
|
||||
visibleNodes: new Set(),
|
||||
showKSpaceBG: false,
|
||||
isThickConnections: false,
|
||||
userPermissions: {},
|
||||
};
|
||||
|
||||
export interface MapContextProps {
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
|
||||
|
||||
.ConnectionTimeEOL {
|
||||
background-image: linear-gradient(207deg, transparent, #7452c3e3);
|
||||
background-image: linear-gradient(207deg, transparent, var(--conn-time-eol));
|
||||
}
|
||||
|
||||
.ConnectionFrigate {
|
||||
background-image: linear-gradient(207deg, transparent, #325d88);
|
||||
background-image: linear-gradient(207deg, transparent, var(--conn-frigate));
|
||||
}
|
||||
|
||||
.ConnectionSave {
|
||||
background-image: linear-gradient(207deg, transparent, rgba(155, 102, 45, 0.85));
|
||||
background-image: linear-gradient(207deg, transparent, var(--conn-save));
|
||||
}
|
||||
|
||||
.SelectedItem {
|
||||
background-color: rgba(98, 98, 98, 0.33);
|
||||
background-color: var(--selected-item-bg);
|
||||
}
|
||||
|
||||
@@ -3,10 +3,17 @@ import { ContextMenu } from 'primereact/contextmenu';
|
||||
import { PrimeIcons } from 'primereact/api';
|
||||
import { MenuItem } from 'primereact/menuitem';
|
||||
import { Edge } from '@reactflow/core/dist/esm/types/edges';
|
||||
import { MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import clsx from 'clsx';
|
||||
import classes from './ContextMenuConnection.module.scss';
|
||||
import { MASS_STATE_NAMES, MASS_STATE_NAMES_ORDER } from '@/hooks/Mapper/components/map/constants.ts';
|
||||
import {
|
||||
MASS_STATE_NAMES,
|
||||
MASS_STATE_NAMES_ORDER,
|
||||
SHIP_SIZES_NAMES,
|
||||
SHIP_SIZES_NAMES_ORDER,
|
||||
SHIP_SIZES_NAMES_SHORT,
|
||||
SHIP_SIZES_SIZE,
|
||||
} from '@/hooks/Mapper/components/map/constants.ts';
|
||||
|
||||
export interface ContextMenuConnectionProps {
|
||||
contextMenuRef: RefObject<ContextMenu>;
|
||||
@@ -35,46 +42,72 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
|
||||
}
|
||||
|
||||
const isFrigateSize = edge.data?.ship_size_type === ShipSizeStatus.small;
|
||||
const isWormhole = edge.data?.type !== ConnectionType.gate;
|
||||
|
||||
return [
|
||||
{
|
||||
label: `EOL`,
|
||||
className: clsx({
|
||||
[classes.ConnectionTimeEOL]: edge.data?.time_status === TimeStatus.eol,
|
||||
}),
|
||||
icon: PrimeIcons.CLOCK,
|
||||
command: onChangeTimeState,
|
||||
},
|
||||
{
|
||||
label: `Frigate`,
|
||||
className: clsx({
|
||||
[classes.ConnectionFrigate]: isFrigateSize,
|
||||
}),
|
||||
icon: PrimeIcons.CLOUD,
|
||||
command: () =>
|
||||
onChangeShipSizeStatus(
|
||||
edge.data?.ship_size_type === ShipSizeStatus.small ? ShipSizeStatus.normal : ShipSizeStatus.small,
|
||||
),
|
||||
},
|
||||
{
|
||||
label: `Save mass`,
|
||||
className: clsx({
|
||||
[classes.ConnectionSave]: edge.data?.locked,
|
||||
}),
|
||||
icon: PrimeIcons.LOCK,
|
||||
command: () => onToggleMassSave(!edge.data?.locked),
|
||||
},
|
||||
...(!isFrigateSize
|
||||
...(isWormhole
|
||||
? [
|
||||
{
|
||||
label: `Mass status`,
|
||||
icon: PrimeIcons.CHART_PIE,
|
||||
items: MASS_STATE_NAMES_ORDER.map(x => ({
|
||||
label: MASS_STATE_NAMES[x],
|
||||
label: `EOL`,
|
||||
className: clsx({
|
||||
[classes.ConnectionTimeEOL]: edge.data?.time_status === TimeStatus.eol,
|
||||
}),
|
||||
icon: PrimeIcons.CLOCK,
|
||||
command: onChangeTimeState,
|
||||
},
|
||||
{
|
||||
label: `Frigate`,
|
||||
className: clsx({
|
||||
[classes.ConnectionFrigate]: isFrigateSize,
|
||||
}),
|
||||
icon: PrimeIcons.CLOUD,
|
||||
command: () =>
|
||||
onChangeShipSizeStatus(
|
||||
edge.data?.ship_size_type === ShipSizeStatus.small ? ShipSizeStatus.large : ShipSizeStatus.small,
|
||||
),
|
||||
},
|
||||
{
|
||||
label: `Save mass`,
|
||||
className: clsx({
|
||||
[classes.ConnectionSave]: edge.data?.locked,
|
||||
}),
|
||||
icon: PrimeIcons.LOCK,
|
||||
command: () => onToggleMassSave(!edge.data?.locked),
|
||||
},
|
||||
...(!isFrigateSize
|
||||
? [
|
||||
{
|
||||
label: `Mass status`,
|
||||
icon: PrimeIcons.CHART_PIE,
|
||||
items: MASS_STATE_NAMES_ORDER.map(x => ({
|
||||
label: MASS_STATE_NAMES[x],
|
||||
className: clsx({
|
||||
[classes.SelectedItem]: edge.data?.mass_status === x,
|
||||
}),
|
||||
command: () => onChangeMassState(x),
|
||||
})),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
{
|
||||
label: `Ship Size`,
|
||||
icon: PrimeIcons.CLOUD,
|
||||
items: SHIP_SIZES_NAMES_ORDER.map(x => ({
|
||||
label: (
|
||||
<div className="grid grid-cols-[20px_120px_1fr_40px] gap-2 items-center">
|
||||
<div className="text-[12px] font-bold text-stone-400">{SHIP_SIZES_NAMES_SHORT[x]}</div>
|
||||
<div>{SHIP_SIZES_NAMES[x]}</div>
|
||||
<div></div>
|
||||
<div className="flex justify-end whitespace-nowrap text-[12px] font-bold text-stone-500">
|
||||
{SHIP_SIZES_SIZE[x]} t.
|
||||
</div>
|
||||
</div>
|
||||
) as unknown as string, // TODO my lovely kostyl
|
||||
className: clsx({
|
||||
[classes.SelectedItem]: edge.data?.mass_status === x,
|
||||
[classes.SelectedItem]: edge.data?.ship_size_type === x,
|
||||
}),
|
||||
command: () => onChangeMassState(x),
|
||||
command: () => onChangeShipSizeStatus(x),
|
||||
})),
|
||||
},
|
||||
]
|
||||
@@ -85,7 +118,7 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
|
||||
command: onDeleteConnection,
|
||||
},
|
||||
];
|
||||
}, [edge, onChangeTimeState, onDeleteConnection, onChangeMassState, onChangeShipSizeStatus]);
|
||||
}, [edge, onChangeTimeState, onDeleteConnection, onChangeShipSizeStatus, onToggleMassSave, onChangeMassState]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ContextMenu } from 'primereact/contextmenu';
|
||||
import { useMapState } from '../../MapProvider.tsx';
|
||||
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { Edge } from '@reactflow/core/dist/esm/types/edges';
|
||||
import { MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||
|
||||
export const useContextMenuConnectionHandlers = () => {
|
||||
@@ -47,6 +47,23 @@ export const useContextMenuConnectionHandlers = () => {
|
||||
setEdge(undefined);
|
||||
};
|
||||
|
||||
const onChangeType = useCallback((type: ConnectionType) => {
|
||||
const { edge, outCommand } = ref.current;
|
||||
|
||||
if (!edge) {
|
||||
return;
|
||||
}
|
||||
|
||||
outCommand({
|
||||
type: OutCommand.updateConnectionType,
|
||||
data: {
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
value: type,
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onChangeMassState = useCallback((status: MassState) => {
|
||||
const { edge, outCommand } = ref.current;
|
||||
|
||||
@@ -80,14 +97,16 @@ export const useContextMenuConnectionHandlers = () => {
|
||||
},
|
||||
});
|
||||
|
||||
outCommand({
|
||||
type: OutCommand.updateConnectionMassStatus,
|
||||
data: {
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
value: MassState.normal,
|
||||
},
|
||||
});
|
||||
if (status === ShipSizeStatus.small) {
|
||||
outCommand({
|
||||
type: OutCommand.updateConnectionMassStatus,
|
||||
data: {
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
value: MassState.normal,
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onToggleMassSave = useCallback((locked: boolean) => {
|
||||
@@ -118,6 +137,7 @@ export const useContextMenuConnectionHandlers = () => {
|
||||
contextMenuRef,
|
||||
onDeleteConnection,
|
||||
onChangeTimeState,
|
||||
onChangeType,
|
||||
onChangeMassState,
|
||||
onChangeShipSizeStatus,
|
||||
onToggleMassSave,
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { useReactFlow, XYPosition } from 'reactflow';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import { ContextMenu } from 'primereact/contextmenu';
|
||||
import { useMapState } from '../../MapProvider.tsx';
|
||||
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||
import { OnMapAddSystemCallback } from '@/hooks/Mapper/components/map/map.types.ts';
|
||||
|
||||
export const useContextMenuRootHandlers = () => {
|
||||
type UseContextMenuRootHandlers = {
|
||||
onAddSystem?: OnMapAddSystemCallback;
|
||||
};
|
||||
|
||||
export const useContextMenuRootHandlers = ({ onAddSystem }: UseContextMenuRootHandlers = {}) => {
|
||||
const rf = useReactFlow();
|
||||
const contextMenuRef = useRef<ContextMenu | null>(null);
|
||||
const { outCommand } = useMapState();
|
||||
const [position, setPosition] = useState<XYPosition | null>(null);
|
||||
|
||||
const handleRootContext = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
@@ -18,14 +20,17 @@ export const useContextMenuRootHandlers = () => {
|
||||
contextMenuRef.current?.show(e);
|
||||
};
|
||||
|
||||
const onAddSystem = () => {
|
||||
outCommand({ type: OutCommand.manualAddSystem, data: { coordinates: position } });
|
||||
};
|
||||
const ref = useRef({ onAddSystem, position });
|
||||
ref.current = { onAddSystem, position };
|
||||
|
||||
const onAddSystemCallback = useCallback(() => {
|
||||
ref.current.onAddSystem?.({ coordinates: position });
|
||||
}, [position]);
|
||||
|
||||
return {
|
||||
handleRootContext,
|
||||
|
||||
contextMenuRef,
|
||||
onAddSystem,
|
||||
onAddSystem: onAddSystemCallback,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,29 +1,57 @@
|
||||
@import "@/hooks/Mapper/components/map/styles/neon-variables";
|
||||
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
|
||||
|
||||
.react-flow__edge.selected {
|
||||
.EdgePathBack {
|
||||
stroke: $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;
|
||||
}
|
||||
}
|
||||
|
||||
.EdgePathFront {
|
||||
fill: none;
|
||||
|
||||
stroke: #2c3844;
|
||||
stroke-width: 2px;
|
||||
|
||||
&.MassVerge:not(&.Frigate) {
|
||||
stroke: #af2900;
|
||||
stroke: #af0000;
|
||||
}
|
||||
|
||||
&.MassHalf:not(&.Frigate) {
|
||||
stroke: #a85f00;
|
||||
stroke: #ffd700;
|
||||
}
|
||||
|
||||
&.Frigate {
|
||||
stroke: #d4f0ff;
|
||||
}
|
||||
|
||||
&.Gate {
|
||||
stroke: #1c1e15;
|
||||
}
|
||||
|
||||
&.Hovered {
|
||||
stroke: #4e5d6c;
|
||||
stroke-width: 2px;
|
||||
@@ -50,41 +78,29 @@
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ClickPath {
|
||||
fill: none;
|
||||
stroke: none;
|
||||
stroke-width: 8px;
|
||||
}
|
||||
|
||||
.LinkLabel{
|
||||
.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;
|
||||
padding: 2px 4px;
|
||||
@@ -100,22 +116,3 @@
|
||||
height: 8px;
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
.Handle {
|
||||
min-width: initial;
|
||||
min-height: initial;
|
||||
border: 1px solid #5a7d9a;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
z-index: 1001;
|
||||
|
||||
&.Tick {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
|
||||
&.Right {
|
||||
margin-left: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import classes from './SolarSystemEdge.module.scss';
|
||||
import { EdgeLabelRenderer, EdgeProps, getBezierPath, Position, useStore } from 'reactflow';
|
||||
import { EdgeLabelRenderer, EdgeProps, getBezierPath, getSmoothStepPath, Position, useStore } from 'reactflow';
|
||||
import { getEdgeParams } from '@/hooks/Mapper/components/map/utils.ts';
|
||||
import clsx from 'clsx';
|
||||
import { MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import { PrimeIcons } from 'primereact/api';
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
|
||||
import { SHIP_SIZES_DESCRIPTION, SHIP_SIZES_NAMES_SHORT } from '@/hooks/Mapper/components/map/constants.ts';
|
||||
|
||||
const MAP_TRANSLATES: Record<string, string> = {
|
||||
[Position.Top]: 'translate(-48%, 0%)',
|
||||
@@ -30,9 +31,18 @@ const MAP_OFFSETS: Record<string, { x: number; y: number }> = {
|
||||
[Position.Right]: { x: 0, y: 0 },
|
||||
};
|
||||
|
||||
export const SHIP_SIZES_COLORS = {
|
||||
[ShipSizeStatus.small]: 'bg-indigo-400',
|
||||
[ShipSizeStatus.medium]: 'bg-cyan-500',
|
||||
[ShipSizeStatus.large]: '',
|
||||
[ShipSizeStatus.freight]: 'bg-lime-400',
|
||||
[ShipSizeStatus.capital]: 'bg-red-400',
|
||||
};
|
||||
|
||||
export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }: EdgeProps<SolarSystemConnection>) => {
|
||||
const sourceNode = useStore(useCallback(store => store.nodeInternals.get(source), [source]));
|
||||
const targetNode = useStore(useCallback(store => store.nodeInternals.get(target), [target]));
|
||||
const isWormhole = data?.type !== ConnectionType.gate;
|
||||
|
||||
const {
|
||||
data: { isThickConnections },
|
||||
@@ -45,7 +55,9 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
|
||||
|
||||
const offset = isThickConnections ? MAP_OFFSETS_TICK[targetPos] : MAP_OFFSETS[targetPos];
|
||||
|
||||
const [edgePath, labelX, labelY] = getBezierPath({
|
||||
const method = isWormhole ? getBezierPath : getSmoothStepPath;
|
||||
|
||||
const [edgePath, labelX, labelY] = method({
|
||||
sourceX: sx - offset.x,
|
||||
sourceY: sy - offset.y,
|
||||
sourcePosition: sourcePos,
|
||||
@@ -53,8 +65,9 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
|
||||
targetX: tx + offset.x,
|
||||
targetY: ty + offset.y,
|
||||
});
|
||||
|
||||
return [edgePath, labelX, labelY, sx, sy, tx, ty, sourcePos, targetPos];
|
||||
}, [isThickConnections, sourceNode, targetNode]);
|
||||
}, [isThickConnections, sourceNode, targetNode, isWormhole]);
|
||||
|
||||
if (!sourceNode || !targetNode || !data) {
|
||||
return null;
|
||||
@@ -66,8 +79,9 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
|
||||
id={`back_${id}`}
|
||||
className={clsx(classes.EdgePathBack, {
|
||||
[classes.Tick]: isThickConnections,
|
||||
[classes.TimeCrit]: data.time_status === TimeStatus.eol,
|
||||
[classes.TimeCrit]: isWormhole && data.time_status === TimeStatus.eol,
|
||||
[classes.Hovered]: hovered,
|
||||
[classes.Gate]: !isWormhole,
|
||||
})}
|
||||
d={path}
|
||||
markerEnd={markerEnd}
|
||||
@@ -78,9 +92,10 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
|
||||
className={clsx(classes.EdgePathFront, {
|
||||
[classes.Tick]: isThickConnections,
|
||||
[classes.Hovered]: hovered,
|
||||
[classes.MassVerge]: data.mass_status === MassState.verge,
|
||||
[classes.MassHalf]: data.mass_status === MassState.half,
|
||||
[classes.Frigate]: data.ship_size_type === ShipSizeStatus.small,
|
||||
[classes.MassVerge]: isWormhole && data.mass_status === MassState.verge,
|
||||
[classes.MassHalf]: isWormhole && data.mass_status === MassState.half,
|
||||
[classes.Frigate]: isWormhole && data.ship_size_type === ShipSizeStatus.small,
|
||||
[classes.Gate]: !isWormhole,
|
||||
})}
|
||||
d={path}
|
||||
markerEnd={markerEnd}
|
||||
@@ -115,12 +130,12 @@ 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)`,
|
||||
}}
|
||||
>
|
||||
{data.locked && (
|
||||
{isWormhole && data.locked && (
|
||||
<WdTooltipWrapper
|
||||
content="Save mass"
|
||||
className={clsx(
|
||||
@@ -131,6 +146,19 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
|
||||
<span className={clsx(PrimeIcons.LOCK, classes.icon)} />
|
||||
</WdTooltipWrapper>
|
||||
)}
|
||||
|
||||
{isWormhole && data.ship_size_type !== ShipSizeStatus.large && (
|
||||
<WdTooltipWrapper
|
||||
content={SHIP_SIZES_DESCRIPTION[data.ship_size_type]}
|
||||
className={clsx(
|
||||
classes.LinkLabel,
|
||||
'pointer-events-auto rounded opacity-100 cursor-auto text-neutral-900 font-bold',
|
||||
SHIP_SIZES_COLORS[data.ship_size_type],
|
||||
)}
|
||||
>
|
||||
{SHIP_SIZES_NAMES_SHORT[data.ship_size_type]}
|
||||
</WdTooltipWrapper>
|
||||
)}
|
||||
</div>
|
||||
</EdgeLabelRenderer>
|
||||
</>
|
||||
|
||||
@@ -1,284 +1,217 @@
|
||||
@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; // Темный фон для подсказок
|
||||
|
||||
.RootCustomNode {
|
||||
display: flex;
|
||||
width: 130px;
|
||||
height: 34px;
|
||||
|
||||
flex-direction: column;
|
||||
padding: 2px 6px;
|
||||
font-size: 10px;
|
||||
|
||||
background-color: $tooltip-bg;
|
||||
box-shadow: 0 0 5px rgba($dark-bg, 0.5);
|
||||
border: 1px solid darken($pastel-blue, 10%);
|
||||
border-radius: 5px;
|
||||
background-color: var(--rf-node-bg-color, #202020) !important;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
font-weight: var(--rf-node-font-weight, bold);
|
||||
color: var(--rf-text-color, #ffffff);
|
||||
font-family: var(--rf-node-font, inherit);
|
||||
}
|
||||
|
||||
&.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: $pastel-pink;
|
||||
box-shadow: 0 0 10px #9a1af1c2;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
background-color: $tooltip-bg;
|
||||
color: $text-color;
|
||||
padding: 5px 10px;
|
||||
/* Region backgrounds for optional tinted headers, etc. */
|
||||
.Mataria,
|
||||
.Amarria,
|
||||
.Gallente,
|
||||
.Caldaria {
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-size: cover;
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
z-index: -1;
|
||||
border-radius: 3px;
|
||||
border: 1px solid $pastel-pink;
|
||||
}
|
||||
}
|
||||
.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;
|
||||
}
|
||||
|
||||
&.eve-system-status-home {
|
||||
border: 1px solid darken($eve-solar-system-status-color-home, 30%);
|
||||
background-image: linear-gradient(275deg, $eve-solar-system-status-friendly, transparent);
|
||||
.selected {
|
||||
border-color: var(--pastel-pink, #d291bc);
|
||||
box-shadow: 0 0 10px #9a1af1c2;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border-color: $eve-solar-system-status-color-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
|
||||
);
|
||||
&.selected {
|
||||
border-color: var(--eve-solar-system-status-color-home);
|
||||
}
|
||||
|
||||
&.eve-system-status-friendly {
|
||||
border: 1px solid darken($eve-solar-system-status-color-friendly, 20%);
|
||||
background-image: linear-gradient(275deg, darken($eve-solar-system-status-friendly, 30%), transparent);
|
||||
|
||||
&.selected {
|
||||
border-color: darken($eve-solar-system-status-color-friendly, 5%);
|
||||
}
|
||||
}
|
||||
.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 darken($eve-solar-system-status-color-lookingFor, 15%);
|
||||
background-image: linear-gradient(275deg, #45ff8f2f, #457fff2f);
|
||||
|
||||
&.selected {
|
||||
border-color: $pastel-pink;
|
||||
}
|
||||
}
|
||||
|
||||
&.eve-system-status-warning {
|
||||
background-image: linear-gradient(275deg, $eve-solar-system-status-warning, transparent);
|
||||
}
|
||||
|
||||
&.eve-system-status-dangerous {
|
||||
background-image: linear-gradient(275deg, $eve-solar-system-status-dangerous, transparent);
|
||||
}
|
||||
|
||||
&.eve-system-status-target {
|
||||
background-image: linear-gradient(275deg, $eve-solar-system-status-target, transparent);
|
||||
}
|
||||
.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: 0;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
.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;
|
||||
&: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;
|
||||
}
|
||||
}
|
||||
|
||||
.HeadRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
|
||||
.TagTitle {
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
font-weight: 500;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 0 2px rgba(231, 146, 52, 0.73);
|
||||
color: var(--rf-tag-color, #ffb01d);
|
||||
}
|
||||
|
||||
/* The top row (HeadRow). */
|
||||
.HeadRow {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
|
||||
.classTitle {
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 0 2px rgb(0 0 0 / 73%);
|
||||
text-shadow: 0 0 2px rgba(0, 0, 0, 0.73);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.TagTitle {
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 0 2px rgba(231, 146, 52, 0.73);
|
||||
|
||||
color: #ffb01d;
|
||||
}
|
||||
|
||||
/* Firefox kostyl */
|
||||
@-moz-document url-prefix() {
|
||||
.classSystemName {
|
||||
font-family: inherit !important;
|
||||
font-family: var(--rf-node-font, inherit);
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.classSystemName {
|
||||
//font-weight: bold;
|
||||
}
|
||||
|
||||
.solarSystemName {
|
||||
}
|
||||
}
|
||||
|
||||
/* Usually ~19px tall for bottom row. */
|
||||
.BottomRow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 19px;
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
.hasUserCharacters {
|
||||
color: var(--rf-has-user-characters, #fbbf24);
|
||||
}
|
||||
}
|
||||
|
||||
/* The systemName overflow + effect icon, etc. */
|
||||
.systemNameOverflow {
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* The node's invisible handles/double-click overlay. */
|
||||
.Handlers {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
@@ -288,29 +221,25 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* The little React Flow handles on each edge. */
|
||||
.Handle {
|
||||
min-width: initial;
|
||||
min-height: initial;
|
||||
border: 1px solid $pastel-blue;
|
||||
border: 1px solid var(--pastel-blue, #5a7d9a);
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
|
||||
&.selected {
|
||||
border-color: $pastel-pink;
|
||||
border-color: var(--pastel-pink, #d291bc);
|
||||
}
|
||||
|
||||
&.HandleTop {
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
&.HandleRight {
|
||||
right: -2px;
|
||||
}
|
||||
|
||||
&.HandleBottom {
|
||||
bottom: -2px;
|
||||
}
|
||||
|
||||
&.HandleLeft {
|
||||
left: -2px;
|
||||
}
|
||||
@@ -322,17 +251,28 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
&.HandleTop {
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
&.HandleRight {
|
||||
right: -3px;
|
||||
}
|
||||
|
||||
&.HandleBottom {
|
||||
bottom: -3px;
|
||||
}
|
||||
|
||||
&.HandleLeft {
|
||||
left: -3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Unsplashed signature containers. */
|
||||
.Unsplashed {
|
||||
position: absolute;
|
||||
width: calc(50% - 4px);
|
||||
z-index: -1;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2px;
|
||||
left: 2px;
|
||||
&--right {
|
||||
left: calc(50% + 6px);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@ import { Handle, Position, WrapNodeProps } from 'reactflow';
|
||||
import { MapSolarSystemType } from '../../map.types';
|
||||
import classes from './SolarSystemNode.module.scss';
|
||||
import clsx from 'clsx';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
|
||||
|
||||
import {
|
||||
EFFECT_BACKGROUND_STYLES,
|
||||
LABELS_INFO,
|
||||
@@ -12,8 +16,9 @@ import {
|
||||
} from '@/hooks/Mapper/components/map/constants.ts';
|
||||
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
|
||||
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
|
||||
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
|
||||
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
|
||||
import { getSystemClassStyles } from '@/hooks/Mapper/components/map/helpers';
|
||||
import { getSystemClassStyles, prepareUnsplashedChunks } from '@/hooks/Mapper/components/map/helpers';
|
||||
import { sortWHClasses } from '@/hooks/Mapper/helpers';
|
||||
import { PrimeIcons } from 'primereact/api';
|
||||
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager.ts';
|
||||
@@ -50,6 +55,11 @@ export const getActivityType = (count: number) => {
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarSystemType>) => {
|
||||
const { interfaceSettings } = useMapRootState();
|
||||
const { isShowUnsplashedSignatures } = interfaceSettings;
|
||||
|
||||
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
|
||||
|
||||
const {
|
||||
system_class,
|
||||
security,
|
||||
@@ -63,9 +73,10 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
solar_system_name,
|
||||
} = data.system_static_info;
|
||||
|
||||
const { locked, name, tag, status, labels, id } = data || {};
|
||||
const signatures = data.system_signatures;
|
||||
|
||||
const { locked, name, tag, status, labels, id, temporary_name: temporaryName } = data || {};
|
||||
|
||||
const customName = solar_system_name !== name ? name : undefined;
|
||||
|
||||
const {
|
||||
data: {
|
||||
@@ -87,8 +98,9 @@ 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);
|
||||
// eslint-disable-next-line
|
||||
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);
|
||||
@@ -106,7 +118,6 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
if (!systemKills) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return systemKills;
|
||||
}, [kills, solar_system_id]);
|
||||
|
||||
@@ -128,26 +139,44 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
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 (
|
||||
<>
|
||||
{visible && (
|
||||
<div className={classes.Bookmarks}>
|
||||
{labelCustom !== '' && (
|
||||
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
|
||||
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] ">{labelCustom}</span>
|
||||
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]">{labelCustom}</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>
|
||||
@@ -160,32 +189,44 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={clsx(classes.RootCustomNode, regionClass, classes[STATUS_CLASSES[status]], {
|
||||
[classes.selected]: selected,
|
||||
})}
|
||||
onMouseDownCapture={dbClick}
|
||||
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.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, 'text-sky-400 font-medium')}>{tag}</div>
|
||||
<div className={clsx(classes.TagTitle, "color: #38bdf8; font-weight: 500;")}>
|
||||
{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',
|
||||
'flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans',
|
||||
)}
|
||||
>
|
||||
{solar_system_name}
|
||||
{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} />
|
||||
))}
|
||||
@@ -197,47 +238,56 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
|
||||
<div className={clsx(classes.BottomRow, 'flex items-center gap-[3px]')}>
|
||||
{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">
|
||||
<div className={clsx('font-bold', classes.customName)}>
|
||||
{customName}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isWormhole && !customName && (
|
||||
<div
|
||||
className={clsx(
|
||||
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-stone-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5',
|
||||
)}
|
||||
>
|
||||
{region_name}
|
||||
</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={PrimeIcons.LOCK} style={{ fontSize: '0.45rem', fontWeight: 'bold' }}></i>}
|
||||
|
||||
{hubs.includes(solar_system_id.toString()) && (
|
||||
<i className={PrimeIcons.MAP_MARKER} style={{ fontSize: '0.45rem', fontWeight: 'bold' }}></i>
|
||||
)}
|
||||
|
||||
{charactersInSystem.length > 0 && (
|
||||
<div className={clsx(classes.localCounter, { ['text-amber-300']: hasUserCharacters })}>
|
||||
<i className="pi pi-users" style={{ fontSize: '0.50rem' }}></i>
|
||||
<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,
|
||||
{ [classes.hasUserCharacters]: hasUserCharacters },
|
||||
'flex gap-[2px]'
|
||||
)}
|
||||
>
|
||||
<i className="pi pi-users text-[0.50rem]" />
|
||||
<span className="text-[0.65rem]">{charactersInSystem.length}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div onMouseDownCapture={dbClick} className={classes.Handlers}>
|
||||
{visible && isShowUnsplashedSignatures && (
|
||||
<div className={classes.Unsplashed}>
|
||||
{unsplashedLeft.map(x => (
|
||||
<UnsplashedSignature key={x.sig_id} signature={x} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{visible && isShowUnsplashedSignatures && (
|
||||
<div className={clsx(classes.Unsplashed, classes['Unsplashed--right'])}>
|
||||
{unsplashedRight.map(x => (
|
||||
<UnsplashedSignature key={x.sig_id} signature={x} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={classes.Handlers}>
|
||||
<Handle
|
||||
type="source"
|
||||
className={clsx(classes.Handle, classes.HandleTop, {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
|
||||
|
||||
.Signature {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
display: block;
|
||||
|
||||
& > .Box {
|
||||
width: 13px;
|
||||
height: 4px;
|
||||
border-radius: 4px;
|
||||
color: var(--text-color);
|
||||
font-size: 8px;
|
||||
text-align: center;
|
||||
font-weight: bolder;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
import { InfoDrawer } from '@/hooks/Mapper/components/ui-kit';
|
||||
|
||||
import classes from './UnsplashedSignature.module.scss';
|
||||
import { SystemSignature } from '@/hooks/Mapper/types/signatures';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { WORMHOLE_CLASS_STYLES, WORMHOLES_ADDITIONAL_INFO } from '@/hooks/Mapper/components/map/constants.ts';
|
||||
import { useMemo } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { renderInfoColumn } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders';
|
||||
|
||||
import { k162Types } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureK162TypeSelect';
|
||||
|
||||
interface UnsplashedSignatureProps {
|
||||
signature: SystemSignature;
|
||||
}
|
||||
export const UnsplashedSignature = ({ signature }: UnsplashedSignatureProps) => {
|
||||
const {
|
||||
data: { wormholesData },
|
||||
} = useMapRootState();
|
||||
|
||||
const whData = useMemo(() => wormholesData[signature.type], [signature.type, wormholesData]);
|
||||
const whClass = useMemo(() => (whData ? WORMHOLES_ADDITIONAL_INFO[whData.dest] : null), [whData]);
|
||||
|
||||
const k162TypeOption = useMemo(() => {
|
||||
if (!signature.custom_info) {
|
||||
return null;
|
||||
}
|
||||
const customInfo = JSON.parse(signature.custom_info);
|
||||
if (!customInfo.k162Type) {
|
||||
return null;
|
||||
}
|
||||
return k162Types.find(x => x.value === customInfo.k162Type);
|
||||
}, [signature]);
|
||||
|
||||
const whClassStyle = useMemo(() => {
|
||||
if (signature.type === 'K162' && k162TypeOption) {
|
||||
const k162Data = wormholesData[k162TypeOption.whClassName];
|
||||
const k162Class = k162Data ? WORMHOLES_ADDITIONAL_INFO[k162Data.dest] : null;
|
||||
return k162Class ? WORMHOLE_CLASS_STYLES[k162Class.wormholeClassID] : '';
|
||||
}
|
||||
return whClass ? WORMHOLE_CLASS_STYLES[whClass.wormholeClassID] : '';
|
||||
}, [signature, whClass, k162TypeOption, wormholesData]);
|
||||
|
||||
return (
|
||||
<WdTooltipWrapper
|
||||
className={clsx(classes.Signature)}
|
||||
content={
|
||||
(
|
||||
<div className="flex flex-col gap-1">
|
||||
<InfoDrawer title={<b className="text-slate-50">{signature.eve_id}</b>}>
|
||||
{renderInfoColumn(signature)}
|
||||
</InfoDrawer>
|
||||
</div>
|
||||
) as React.ReactNode
|
||||
}
|
||||
>
|
||||
<div className={clsx(classes.Box, whClassStyle)}>
|
||||
<svg width="13" height="4" viewBox="0 0 13 4" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="13" height="4" rx="2" className={whClassStyle} fill="currentColor" />
|
||||
</svg>
|
||||
</div>
|
||||
</WdTooltipWrapper>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './UnsplashedSignature.tsx';
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MassState } from '@/hooks/Mapper/types';
|
||||
import { ConnectionType, MassState, ShipSizeStatus } from '@/hooks/Mapper/types';
|
||||
|
||||
export enum SOLAR_SYSTEM_CLASS_IDS {
|
||||
ccp1 = -1,
|
||||
@@ -712,6 +712,13 @@ export const STATUS_CLASSES: Record<number, string> = {
|
||||
[STATUSES.dangerous]: 'eve-system-status-dangerous',
|
||||
};
|
||||
|
||||
export const TYPE_NAMES_ORDER = [ConnectionType.wormhole, ConnectionType.gate];
|
||||
|
||||
export const TYPE_NAMES = {
|
||||
[ConnectionType.wormhole]: 'Wormhole',
|
||||
[ConnectionType.gate]: 'Gate',
|
||||
};
|
||||
|
||||
export const MASS_STATE_NAMES_ORDER = [MassState.verge, MassState.half, MassState.normal];
|
||||
|
||||
export const MASS_STATE_NAMES = {
|
||||
@@ -720,16 +727,41 @@ export const MASS_STATE_NAMES = {
|
||||
[MassState.verge]: 'Verge of collapse',
|
||||
};
|
||||
|
||||
// export const SHIP_SIZES_NAMES_ORDER = [
|
||||
// ShipSizeStatus.small,
|
||||
// ShipSizeStatus.normal,
|
||||
// // ShipSizeStatus.large,
|
||||
// // ShipSizeStatus.capital,
|
||||
// ];
|
||||
//
|
||||
// export const SHIP_SIZES_NAMES = {
|
||||
// [ShipSizeStatus.small]: 'Frigate',
|
||||
// [ShipSizeStatus.normal]: 'Normal',
|
||||
// // [ShipSizeStatus.large]: 'Normal',
|
||||
// // [ShipSizeStatus.capital]: 'Normal',
|
||||
// };
|
||||
export const SHIP_SIZES_NAMES_ORDER = [
|
||||
ShipSizeStatus.small,
|
||||
ShipSizeStatus.medium,
|
||||
ShipSizeStatus.large,
|
||||
ShipSizeStatus.freight,
|
||||
ShipSizeStatus.capital,
|
||||
];
|
||||
|
||||
export const SHIP_SIZES_NAMES = {
|
||||
[ShipSizeStatus.small]: 'Frigate',
|
||||
[ShipSizeStatus.medium]: 'Medium',
|
||||
[ShipSizeStatus.large]: 'Normal',
|
||||
[ShipSizeStatus.freight]: 'Huge',
|
||||
[ShipSizeStatus.capital]: 'Capital',
|
||||
};
|
||||
export const SHIP_SIZES_SIZE = {
|
||||
[ShipSizeStatus.small]: '5K',
|
||||
[ShipSizeStatus.medium]: '62K',
|
||||
[ShipSizeStatus.large]: '375K',
|
||||
[ShipSizeStatus.freight]: '1M',
|
||||
[ShipSizeStatus.capital]: '2M',
|
||||
};
|
||||
|
||||
export const SHIP_SIZES_DESCRIPTION = {
|
||||
[ShipSizeStatus.small]: 'Frigate wormhole - up to Destroyer | 5K t.',
|
||||
[ShipSizeStatus.medium]: 'Cruise wormhole - up to Battlecruiser | 62K t.',
|
||||
[ShipSizeStatus.large]: 'Large wormhole - up to Battleship | 375K t.',
|
||||
[ShipSizeStatus.freight]: 'Huge wormhole - up to Freighter | 1M t.',
|
||||
[ShipSizeStatus.capital]: 'Capital wormhole - up to Capital | 2M t.',
|
||||
};
|
||||
|
||||
export const SHIP_SIZES_NAMES_SHORT = {
|
||||
[ShipSizeStatus.small]: 'S',
|
||||
[ShipSizeStatus.medium]: 'M',
|
||||
[ShipSizeStatus.large]: 'L',
|
||||
[ShipSizeStatus.freight]: 'H',
|
||||
[ShipSizeStatus.capital]: 'XL',
|
||||
};
|
||||
|
||||
@@ -3,3 +3,4 @@ export * from './convertSystem2Node';
|
||||
export * from './getSystemClassStyles';
|
||||
export * from './getShapeClass';
|
||||
export * from './getBackgroundClass';
|
||||
export * from './prepareUnsplashedChunks';
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// Helper function to split an array into chunks of size
|
||||
const chunkArray = (array: any[], size: number) => {
|
||||
const chunks = [];
|
||||
for (let i = 0; i < array.length; i += size) {
|
||||
chunks.push(array.slice(i, i + size));
|
||||
}
|
||||
return chunks;
|
||||
};
|
||||
|
||||
export const prepareUnsplashedChunks = (items: any[]) => {
|
||||
// Split the items into chunks of 4
|
||||
const chunks = chunkArray(items, 4);
|
||||
|
||||
// Get the column elements
|
||||
const leftColumn: any[] = [];
|
||||
const rightColumn: any[] = [];
|
||||
|
||||
chunks.forEach((chunk, index) => {
|
||||
const column = index % 2 === 0 ? leftColumn : rightColumn;
|
||||
|
||||
chunk.forEach(item => {
|
||||
column.push(item);
|
||||
});
|
||||
});
|
||||
|
||||
return [leftColumn, rightColumn];
|
||||
};
|
||||
@@ -12,6 +12,7 @@ export const useMapAddSystems = () => {
|
||||
return useCallback((systems: CommandAddSystems) => {
|
||||
const { rf } = ref.current;
|
||||
const nodes = rf.getNodes();
|
||||
|
||||
const prepared: Node[] = systems.filter(x => !nodes.some(y => x.id === y.id)).map(convertSystem2Node);
|
||||
rf.addNodes(prepared);
|
||||
}, []);
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './useMapHandlers';
|
||||
export * from './useUpdateNodes';
|
||||
export * from './useNodesEdgesState';
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
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')
|
||||
|
||||
useEffect(() => {
|
||||
let themeEl = document.querySelector('.pathfinder-theme, .neon-theme');
|
||||
if (!themeEl) {
|
||||
themeEl = document.documentElement;
|
||||
}
|
||||
|
||||
const style = getComputedStyle(themeEl as HTMLElement);
|
||||
|
||||
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') {
|
||||
finalVariant = BackgroundVariant.Cross;
|
||||
}
|
||||
|
||||
const cssVarGap = style.getPropertyValue('--rf-bg-gap');
|
||||
const cssVarSize = style.getPropertyValue('--rf-bg-size');
|
||||
const cssColor = style.getPropertyValue('--rf-bg-pattern-color');
|
||||
|
||||
const gapNum = parseInt(cssVarGap, 10) || 16;
|
||||
const sizeNum = parseInt(cssVarSize, 10) || 1;
|
||||
|
||||
setVariant(finalVariant);
|
||||
setGap(gapNum);
|
||||
setSize(sizeNum);
|
||||
setColor(cssColor);
|
||||
|
||||
}, [themeName]);
|
||||
|
||||
return { variant, gap, size, color };
|
||||
}
|
||||
@@ -112,13 +112,17 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
|
||||
connections: [],
|
||||
});
|
||||
selectSystem(systemId as CommandSelectSystem);
|
||||
}, 100);
|
||||
}, 500);
|
||||
break;
|
||||
|
||||
case Commands.routes:
|
||||
// do nothing here
|
||||
break;
|
||||
|
||||
case Commands.signaturesUpdated:
|
||||
// do nothing here
|
||||
break;
|
||||
|
||||
case Commands.linkSignatureToSystem:
|
||||
// do nothing here
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { useState, useCallback, type Dispatch, type SetStateAction } from 'react';
|
||||
|
||||
import { applyNodeChanges, applyEdgeChanges } from '../utils/changes';
|
||||
import { OnNodesChange, Edge, OnEdgesChange, Node } from 'reactflow';
|
||||
|
||||
/**
|
||||
* Hook for managing the state of nodes - should only be used for prototyping / simple use cases.
|
||||
*
|
||||
* @public
|
||||
* @param initialNodes
|
||||
* @returns an array [nodes, setNodes, onNodesChange]
|
||||
*/
|
||||
export function useNodesState<NodeType extends Node>(
|
||||
initialNodes: NodeType[],
|
||||
): [NodeType[], Dispatch<SetStateAction<NodeType[]>>, OnNodesChange] {
|
||||
const [nodes, setNodes] = useState(initialNodes);
|
||||
const onNodesChange: OnNodesChange = useCallback(changes => setNodes(nds => applyNodeChanges(changes, nds)), []);
|
||||
|
||||
return [nodes, setNodes, onNodesChange];
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for managing the state of edges - should only be used for prototyping / simple use cases.
|
||||
*
|
||||
* @public
|
||||
* @param initialEdges
|
||||
* @returns an array [edges, setEdges, onEdgesChange]
|
||||
*/
|
||||
export function useEdgesState<EdgeType extends Edge = Edge>(
|
||||
initialEdges: EdgeType[],
|
||||
): [EdgeType[], Dispatch<SetStateAction<EdgeType[]>>, OnEdgesChange] {
|
||||
const [edges, setEdges] = useState(initialEdges);
|
||||
const onEdgesChange: OnEdgesChange = useCallback(changes => setEdges(eds => applyEdgeChanges(changes, eds)), []);
|
||||
|
||||
return [edges, setEdges, onEdgesChange];
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { SolarSystemRawType } from '@/hooks/Mapper/types/system';
|
||||
import { SolarSystemConnection } from '@/hooks/Mapper/types';
|
||||
import { XYPosition } from 'reactflow';
|
||||
|
||||
export type MapSolarSystemType = Omit<SolarSystemRawType, 'position'>;
|
||||
|
||||
@@ -7,3 +8,5 @@ export type OnMapSelectionChange = (event: {
|
||||
systems: string[];
|
||||
connections: Pick<SolarSystemConnection, 'source' | 'target'>[];
|
||||
}) => void;
|
||||
|
||||
export type OnMapAddSystemCallback = (props: { coordinates: XYPosition | null }) => void;
|
||||
|
||||
@@ -1,78 +1,114 @@
|
||||
$eve-link-color-default: #333;
|
||||
$eve-link-color-top-mass-0: #333;
|
||||
$eve-link-color-top-mass-1: #5a4520;
|
||||
$eve-link-color-top-mass-2: #672c2c;
|
||||
$eve-link-color-middle-mass-0: #333;
|
||||
$eve-link-color-middle-mass-1: #333;
|
||||
$eve-link-color-middle-mass-2: #333;
|
||||
$eve-link-color-middle-time-0: #5c5c5c;
|
||||
$eve-link-color-middle-time-1: #ff00cd;
|
||||
$eve-link-color-middle-time-1-border: #99f3ff;
|
||||
:root {
|
||||
--pastel-blue: #5a7d9a;
|
||||
--pastel-pink: #d291bc;
|
||||
--pastel-green: #88b04b;
|
||||
--pastel-yellow: #ffdd59;
|
||||
--dark-bg: #2d2d2d;
|
||||
--text-color: #ffffff;
|
||||
--tooltip-bg: #202020;
|
||||
|
||||
$eve-link-color-top-mass-1-time-1: #796300;
|
||||
$eve-link-color-top-mass-2-time-1: #8c1717;
|
||||
$eve-link-color-temp: orange;
|
||||
--pastel-blue-darken10: #4f6b86;
|
||||
--pastel-blue-lighten10: #6da3af;
|
||||
--pastel-pink-darken10: #bb7ca9;
|
||||
--pastel-pink-lighten10: #e0a6cb;
|
||||
|
||||
$eve-effect-pulsar: #40aef5;
|
||||
$eve-effect-magnetar: #f058f8;
|
||||
$eve-effect-wolfRayet: #ef7843;
|
||||
$eve-effect-blackHole: #1b1b1b;
|
||||
$eve-effect-cataclysmicVariable: #ffea90;
|
||||
$eve-effect-redGiant: #fd3c3c;
|
||||
$eve-effect-dazhLiminalityLocus: #ff6464;
|
||||
$eve-effect-imperialStellarObservatory: #6991ce;
|
||||
$eve-effect-stateStellarObservatory: #6991ce;
|
||||
$eve-effect-republicStellarObservatory: #6991ce;
|
||||
$eve-effect-federalStellarObservatory: #6991ce;
|
||||
--pastel-green-darken10: #79a244;
|
||||
--pastel-green-lighten10: #99cf52;
|
||||
|
||||
$eve-wh-type-color-high: #5dffd2;
|
||||
$eve-wh-type-color-low: #f79400;
|
||||
$eve-wh-type-color-null: #fc3c3c;
|
||||
$eve-wh-type-color-c1: #69bfce;
|
||||
$eve-wh-type-color-c2: #6991ce;
|
||||
$eve-wh-type-color-c3: #a8cb70;
|
||||
$eve-wh-type-color-c4: #e39c68;
|
||||
$eve-wh-type-color-c5: #de8686;
|
||||
$eve-wh-type-color-c6: #e76363;
|
||||
$eve-wh-type-color-c13: #988cb5;
|
||||
$eve-wh-type-color-drifter: #ff44f6;
|
||||
$eve-wh-type-color-thera: #ffffff;
|
||||
$eve-wh-type-color-zarzakh: #212121;
|
||||
--pastel-yellow-darken10: #e6c44f;
|
||||
--pastel-yellow-lighten10: #ffe874;
|
||||
|
||||
$eve-security-color-10: #2c74df;
|
||||
$eve-security-color-09: #3998e8;
|
||||
$eve-security-color-08: #4dcbf5;
|
||||
$eve-security-color-07: #60d8a2;
|
||||
$eve-security-color-06: #71e454;
|
||||
$eve-security-color-05: #f2fc81;
|
||||
$eve-security-color-04: #d96c07;
|
||||
$eve-security-color-03: #cb440f;
|
||||
$eve-security-color-02: #b91117;
|
||||
$eve-security-color-01: #732020;
|
||||
$eve-security-color-00: #8b3263;
|
||||
$eve-security-color-m-01: #8b3263;
|
||||
$eve-security-color-m-02: #8b3263;
|
||||
$eve-security-color-m-03: #8b3263;
|
||||
$eve-security-color-m-04: #8b3263;
|
||||
$eve-security-color-m-05: #8b3263;
|
||||
$eve-security-color-m-06: #8b3263;
|
||||
$eve-security-color-m-07: #8b3263;
|
||||
$eve-security-color-m-08: #8b3263;
|
||||
$eve-security-color-m-09: #8b3263;
|
||||
$eve-security-color-m-10: #8b3263;
|
||||
/* Eve Link Colors */
|
||||
--eve-link-color-default: #333;
|
||||
--eve-link-color-top-mass-0: #333;
|
||||
--eve-link-color-top-mass-1: #5a4520;
|
||||
--eve-link-color-top-mass-2: #672c2c;
|
||||
--eve-link-color-middle-mass-0: #333;
|
||||
--eve-link-color-middle-mass-1: #333;
|
||||
--eve-link-color-middle-mass-2: #333;
|
||||
--eve-link-color-middle-time-0: #5c5c5c;
|
||||
--eve-link-color-middle-time-1: #ff00cd;
|
||||
--eve-link-color-middle-time-1-border: #99f3ff;
|
||||
--eve-link-color-top-mass-1-time-1: #796300;
|
||||
--eve-link-color-top-mass-2-time-1: #8c1717;
|
||||
--eve-link-color-temp: orange;
|
||||
|
||||
$eve-solar-system-status-unknown: transparent;
|
||||
$eve-solar-system-status-friendly: #3bbd3952;
|
||||
$eve-solar-system-status-warning: #906518a6;
|
||||
$eve-solar-system-status-target: #b439ff6b;
|
||||
$eve-solar-system-status-dangerous: #d54040;
|
||||
$eve-solar-system-status-lookingFor: rgba(67, 176, 253, 0.48);
|
||||
$eve-solar-system-status-home: rgb(197, 253, 67);
|
||||
/* Wormhole Effects */
|
||||
--eve-effect-pulsar: #40aef5;
|
||||
--eve-effect-magnetar: #f058f8;
|
||||
--eve-effect-wolfRayet: #ef7843;
|
||||
--eve-effect-blackHole: #1b1b1b;
|
||||
--eve-effect-cataclysmicVariable: #ffea90;
|
||||
--eve-effect-redGiant: #fd3c3c;
|
||||
--eve-effect-dazhLiminalityLocus: #ff6464;
|
||||
--eve-effect-imperialStellarObservatory: #6991ce;
|
||||
--eve-effect-stateStellarObservatory: #6991ce;
|
||||
--eve-effect-republicStellarObservatory: #6991ce;
|
||||
--eve-effect-federalStellarObservatory: #6991ce;
|
||||
|
||||
$eve-solar-system-status-color-unknown: transparent;
|
||||
$eve-solar-system-status-color-friendly: #3bbd39;
|
||||
$eve-solar-system-status-color-warning: #ffb93b;
|
||||
$eve-solar-system-status-color-target: #b439ff;
|
||||
$eve-solar-system-status-color-dangerous: #d54040;
|
||||
$eve-solar-system-status-color-lookingFor: #43c2fd;
|
||||
$eve-solar-system-status-color-home: rgb(197, 253, 67);
|
||||
/* WH Types */
|
||||
--eve-wh-type-color-high: #5dffd2;
|
||||
--eve-wh-type-color-low: #f79400;
|
||||
--eve-wh-type-color-null: #fc3c3c;
|
||||
--eve-wh-type-color-c1: #69bfce;
|
||||
--eve-wh-type-color-c2: #6991ce;
|
||||
--eve-wh-type-color-c3: #a8cb70;
|
||||
--eve-wh-type-color-c4: #e39c68;
|
||||
--eve-wh-type-color-c5: #de8686;
|
||||
--eve-wh-type-color-c6: #e76363;
|
||||
--eve-wh-type-color-c13: #988cb5;
|
||||
--eve-wh-type-color-drifter: #ff44f6;
|
||||
--eve-wh-type-color-thera: #ffffff;
|
||||
--eve-wh-type-color-zarzakh: #212121;
|
||||
|
||||
/* Security Colors */
|
||||
--eve-security-color-10: #2c74df;
|
||||
--eve-security-color-09: #3998e8;
|
||||
--eve-security-color-08: #4dcbf5;
|
||||
--eve-security-color-07: #60d8a2;
|
||||
--eve-security-color-06: #71e454;
|
||||
--eve-security-color-05: #f2fc81;
|
||||
--eve-security-color-04: #d96c07;
|
||||
--eve-security-color-03: #cb440f;
|
||||
--eve-security-color-02: #b91117;
|
||||
--eve-security-color-01: #732020;
|
||||
--eve-security-color-00: #8b3263;
|
||||
--eve-security-color-m-01: #8b3263;
|
||||
--eve-security-color-m-02: #8b3263;
|
||||
--eve-security-color-m-03: #8b3263;
|
||||
--eve-security-color-m-04: #8b3263;
|
||||
--eve-security-color-m-05: #8b3263;
|
||||
--eve-security-color-m-06: #8b3263;
|
||||
--eve-security-color-m-07: #8b3263;
|
||||
--eve-security-color-m-08: #8b3263;
|
||||
--eve-security-color-m-09: #8b3263;
|
||||
--eve-security-color-m-10: #8b3263;
|
||||
|
||||
/* Solar System Status */
|
||||
--eve-solar-system-status-unknown: transparent;
|
||||
--eve-solar-system-status-friendly: #3bbd3952;
|
||||
--eve-solar-system-status-warning: #906518a6;
|
||||
--eve-solar-system-status-target: #b439ff6b;
|
||||
--eve-solar-system-status-dangerous: #d54040;
|
||||
--eve-solar-system-status-lookingFor: rgba(67, 176, 253, 0.48);
|
||||
--eve-solar-system-status-home: rgb(197, 253, 67);
|
||||
|
||||
--eve-solar-system-status-color-unknown: transparent;
|
||||
--eve-solar-system-status-color-friendly: #3bbd39;
|
||||
--eve-solar-system-status-color-warning: #ffb93b;
|
||||
--eve-solar-system-status-color-target: #b439ff;
|
||||
--eve-solar-system-status-color-dangerous: #d54040;
|
||||
--eve-solar-system-status-color-lookingFor: #43c2fd;
|
||||
--eve-solar-system-status-color-home: rgb(197, 253, 67);
|
||||
|
||||
--eve-solar-system-status-color-friendly-dark20: #2d9b2e;
|
||||
--eve-solar-system-status-friendly-dark30: #28892a;
|
||||
--eve-solar-system-status-color-friendly-dark5: #38b538;
|
||||
--eve-solar-system-status-color-lookingFor-dark15: #32aadf;
|
||||
|
||||
/* Context Menu */
|
||||
--conn-time-eol: #7452c3e3;
|
||||
--conn-frigate: #325d88;
|
||||
--conn-save: rgba(155, 102, 45, 0.85);
|
||||
--selected-item-bg: rgba(98, 98, 98, 0.33);
|
||||
}
|
||||
|
||||
@@ -1,535 +1,448 @@
|
||||
@import "eve-common-variables";
|
||||
@import './eve-common-variables';
|
||||
|
||||
|
||||
.eve-wh-effect-color-pulsar {
|
||||
fill: $eve-effect-pulsar;
|
||||
background-color: $eve-effect-pulsar;
|
||||
fill: var(--eve-effect-pulsar);
|
||||
background-color: var(--eve-effect-pulsar);
|
||||
}
|
||||
|
||||
.eve-wh-effect-color-magnetar {
|
||||
fill: $eve-effect-magnetar;
|
||||
background-color: $eve-effect-magnetar;
|
||||
fill: var(--eve-effect-magnetar);
|
||||
background-color: var(--eve-effect-magnetar);
|
||||
}
|
||||
|
||||
.eve-wh-effect-color-wolfRayet {
|
||||
fill: $eve-effect-wolfRayet;
|
||||
background-color: $eve-effect-wolfRayet;
|
||||
fill: var(--eve-effect-wolfRayet);
|
||||
background-color: var(--eve-effect-wolfRayet);
|
||||
}
|
||||
|
||||
.eve-wh-effect-color-blackHole {
|
||||
fill: $eve-effect-blackHole;
|
||||
background-color: $eve-effect-blackHole;
|
||||
box-shadow: 0 0 8px rgba(255 255 255 / 33);
|
||||
fill: var(--eve-effect-blackHole);
|
||||
background-color: var(--eve-effect-blackHole);
|
||||
box-shadow: 0 0 8px rgba(255, 255, 255, 0.33);
|
||||
}
|
||||
|
||||
.eve-wh-effect-color-cataclysmicVariable {
|
||||
fill: $eve-effect-cataclysmicVariable;
|
||||
background-color: $eve-effect-cataclysmicVariable;
|
||||
fill: var(--eve-effect-cataclysmicVariable);
|
||||
background-color: var(--eve-effect-cataclysmicVariable);
|
||||
}
|
||||
|
||||
.eve-wh-effect-color-redGiant {
|
||||
fill: $eve-effect-redGiant;
|
||||
background-color: $eve-effect-redGiant;
|
||||
fill: var(--eve-effect-redGiant);
|
||||
background-color: var(--eve-effect-redGiant);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-pulsar {
|
||||
color: $eve-effect-pulsar;
|
||||
color: var(--eve-effect-pulsar);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-magnetar {
|
||||
color: $eve-effect-magnetar;
|
||||
color: var(--eve-effect-magnetar);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-wolfRayet {
|
||||
color: $eve-effect-wolfRayet;
|
||||
color: var(--eve-effect-wolfRayet);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-blackHole {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-cataclysmicVariable {
|
||||
color: $eve-effect-cataclysmicVariable;
|
||||
color: var(--eve-effect-cataclysmicVariable);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-redGiant {
|
||||
color: $eve-effect-redGiant;
|
||||
color: var(--eve-effect-redGiant);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-dazhLiminalityLocus {
|
||||
color: $eve-effect-dazhLiminalityLocus;
|
||||
color: var(--eve-effect-dazhLiminalityLocus);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-imperialStellarObservatory {
|
||||
color: $eve-effect-imperialStellarObservatory;
|
||||
color: var(--eve-effect-imperialStellarObservatory);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-stateStellarObservatory {
|
||||
color: $eve-effect-stateStellarObservatory;
|
||||
color: var(--eve-effect-stateStellarObservatory);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-republicStellarObservatory {
|
||||
color: $eve-effect-republicStellarObservatory;
|
||||
color: var(--eve-effect-republicStellarObservatory);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-federalStellarObservatory {
|
||||
color: $eve-effect-federalStellarObservatory;
|
||||
color: var(--eve-effect-federalStellarObservatory);
|
||||
}
|
||||
|
||||
/* Security color classes */
|
||||
.eve-security-color-10 {
|
||||
color: $eve-security-color-10 !important;
|
||||
fill: $eve-security-color-10;
|
||||
color: var(--eve-security-color-10) !important;
|
||||
fill: var(--eve-security-color-10);
|
||||
}
|
||||
|
||||
.eve-security-color-09 {
|
||||
color: $eve-security-color-09 !important;
|
||||
fill: $eve-security-color-09;
|
||||
color: var(--eve-security-color-09) !important;
|
||||
fill: var(--eve-security-color-09);
|
||||
}
|
||||
|
||||
.eve-security-color-08 {
|
||||
color: $eve-security-color-08 !important;
|
||||
fill: $eve-security-color-08;
|
||||
color: var(--eve-security-color-08) !important;
|
||||
fill: var(--eve-security-color-08);
|
||||
}
|
||||
|
||||
.eve-security-color-07 {
|
||||
color: $eve-security-color-07 !important;
|
||||
fill: $eve-security-color-07;
|
||||
color: var(--eve-security-color-07) !important;
|
||||
fill: var(--eve-security-color-07);
|
||||
}
|
||||
|
||||
.eve-security-color-06 {
|
||||
color: $eve-security-color-06 !important;
|
||||
fill: $eve-security-color-06;
|
||||
color: var(--eve-security-color-06) !important;
|
||||
fill: var(--eve-security-color-06);
|
||||
}
|
||||
|
||||
.eve-security-color-05 {
|
||||
color: $eve-security-color-05 !important;
|
||||
fill: $eve-security-color-05;
|
||||
color: var(--eve-security-color-05) !important;
|
||||
fill: var(--eve-security-color-05);
|
||||
}
|
||||
|
||||
.eve-security-color-04 {
|
||||
color: $eve-security-color-04 !important;
|
||||
fill: $eve-security-color-04;
|
||||
color: var(--eve-security-color-04) !important;
|
||||
fill: var(--eve-security-color-04);
|
||||
}
|
||||
|
||||
.eve-security-color-03 {
|
||||
color: $eve-security-color-03 !important;
|
||||
fill: $eve-security-color-03;
|
||||
color: var(--eve-security-color-03) !important;
|
||||
fill: var(--eve-security-color-03);
|
||||
}
|
||||
|
||||
.eve-security-color-02 {
|
||||
color: $eve-security-color-02 !important;
|
||||
fill: $eve-security-color-02;
|
||||
color: var(--eve-security-color-02) !important;
|
||||
fill: var(--eve-security-color-02);
|
||||
}
|
||||
|
||||
.eve-security-color-01 {
|
||||
color: $eve-security-color-01 !important;
|
||||
fill: $eve-security-color-01;
|
||||
color: var(--eve-security-color-01) !important;
|
||||
fill: var(--eve-security-color-01);
|
||||
}
|
||||
|
||||
.eve-security-color-00 {
|
||||
color: $eve-security-color-00 !important;
|
||||
fill: $eve-security-color-00;
|
||||
color: var(--eve-security-color-00) !important;
|
||||
fill: var(--eve-security-color-00);
|
||||
}
|
||||
|
||||
.eve-security-color-m-01 {
|
||||
color: $eve-security-color-m-01 !important;
|
||||
fill: $eve-security-color-m-01;
|
||||
color: var(--eve-security-color-m-01) !important;
|
||||
fill: var(--eve-security-color-m-01);
|
||||
}
|
||||
|
||||
.eve-security-color-m-02 {
|
||||
color: $eve-security-color-m-02 !important;
|
||||
fill: $eve-security-color-m-02;
|
||||
color: var(--eve-security-color-m-02) !important;
|
||||
fill: var(--eve-security-color-m-02);
|
||||
}
|
||||
|
||||
.eve-security-color-m-03 {
|
||||
color: $eve-security-color-m-03 !important;
|
||||
fill: $eve-security-color-m-03;
|
||||
color: var(--eve-security-color-m-03) !important;
|
||||
fill: var(--eve-security-color-m-03);
|
||||
}
|
||||
|
||||
.eve-security-color-m-04 {
|
||||
color: $eve-security-color-m-04 !important;
|
||||
fill: $eve-security-color-m-04;
|
||||
color: var(--eve-security-color-m-04) !important;
|
||||
fill: var(--eve-security-color-m-04);
|
||||
}
|
||||
|
||||
.eve-security-color-m-05 {
|
||||
color: $eve-security-color-m-05 !important;
|
||||
fill: $eve-security-color-m-05;
|
||||
color: var(--eve-security-color-m-05) !important;
|
||||
fill: var(--eve-security-color-m-05);
|
||||
}
|
||||
|
||||
.eve-security-color-m-06 {
|
||||
color: $eve-security-color-m-06 !important;
|
||||
fill: $eve-security-color-m-06;
|
||||
color: var(--eve-security-color-m-06) !important;
|
||||
fill: var(--eve-security-color-m-06);
|
||||
}
|
||||
|
||||
.eve-security-color-m-07 {
|
||||
color: $eve-security-color-m-07 !important;
|
||||
fill: $eve-security-color-m-07;
|
||||
color: var(--eve-security-color-m-07) !important;
|
||||
fill: var(--eve-security-color-m-07);
|
||||
}
|
||||
|
||||
.eve-security-color-m-08 {
|
||||
color: $eve-security-color-m-08 !important;
|
||||
fill: $eve-security-color-m-08;
|
||||
color: var(--eve-security-color-m-08) !important;
|
||||
fill: var(--eve-security-color-m-08);
|
||||
}
|
||||
|
||||
.eve-security-color-m-09 {
|
||||
color: $eve-security-color-m-09 !important;
|
||||
fill: $eve-security-color-m-09;
|
||||
color: var(--eve-security-color-m-09) !important;
|
||||
fill: var(--eve-security-color-m-09);
|
||||
}
|
||||
|
||||
.eve-security-color-m-10 {
|
||||
color: $eve-security-color-m-10 !important;
|
||||
fill: $eve-security-color-m-10;
|
||||
color: var(--eve-security-color-m-10) !important;
|
||||
fill: var(--eve-security-color-m-10);
|
||||
}
|
||||
|
||||
/* Security backgrounds */
|
||||
.eve-security-background-10 {
|
||||
background-color: $eve-security-color-10;
|
||||
fill: $eve-security-color-10;
|
||||
background-color: var(--eve-security-color-10);
|
||||
fill: var(--eve-security-color-10);
|
||||
}
|
||||
|
||||
.eve-security-background-09 {
|
||||
background-color: $eve-security-color-09;
|
||||
fill: $eve-security-color-09;
|
||||
background-color: var(--eve-security-color-09);
|
||||
fill: var(--eve-security-color-09);
|
||||
}
|
||||
|
||||
.eve-security-background-08 {
|
||||
background-color: $eve-security-color-08;
|
||||
fill: $eve-security-color-08;
|
||||
background-color: var(--eve-security-color-08);
|
||||
fill: var(--eve-security-color-08);
|
||||
}
|
||||
|
||||
.eve-security-background-07 {
|
||||
background-color: $eve-security-color-07;
|
||||
fill: $eve-security-color-07;
|
||||
background-color: var(--eve-security-color-07);
|
||||
fill: var(--eve-security-color-07);
|
||||
}
|
||||
|
||||
.eve-security-background-06 {
|
||||
background-color: $eve-security-color-06;
|
||||
fill: $eve-security-color-06;
|
||||
background-color: var(--eve-security-color-06);
|
||||
fill: var(--eve-security-color-06);
|
||||
}
|
||||
|
||||
.eve-security-background-05 {
|
||||
background-color: $eve-security-color-05;
|
||||
fill: $eve-security-color-05;
|
||||
background-color: var(--eve-security-color-05);
|
||||
fill: var(--eve-security-color-05);
|
||||
}
|
||||
|
||||
.eve-security-background-04 {
|
||||
background-color: $eve-security-color-04;
|
||||
fill: $eve-security-color-04;
|
||||
background-color: var(--eve-security-color-04);
|
||||
fill: var(--eve-security-color-04);
|
||||
}
|
||||
|
||||
.eve-security-background-03 {
|
||||
background-color: $eve-security-color-03;
|
||||
fill: $eve-security-color-03;
|
||||
background-color: var(--eve-security-color-03);
|
||||
fill: var(--eve-security-color-03);
|
||||
}
|
||||
|
||||
.eve-security-background-02 {
|
||||
background-color: $eve-security-color-02;
|
||||
fill: $eve-security-color-02;
|
||||
background-color: var(--eve-security-color-02);
|
||||
fill: var(--eve-security-color-02);
|
||||
}
|
||||
|
||||
.eve-security-background-01 {
|
||||
background-color: $eve-security-color-01;
|
||||
fill: $eve-security-color-01;
|
||||
background-color: var(--eve-security-color-01);
|
||||
fill: var(--eve-security-color-01);
|
||||
}
|
||||
|
||||
.eve-security-background-00 {
|
||||
background-color: $eve-security-color-00;
|
||||
fill: $eve-security-color-00;
|
||||
background-color: var(--eve-security-color-00);
|
||||
fill: var(--eve-security-color-00);
|
||||
}
|
||||
|
||||
.eve-security-background-m-01 {
|
||||
background-color: $eve-security-color-m-01;
|
||||
fill: $eve-security-color-m-01;
|
||||
background-color: var(--eve-security-color-m-01);
|
||||
fill: var(--eve-security-color-m-01);
|
||||
}
|
||||
|
||||
.eve-security-background-m-02 {
|
||||
background-color: $eve-security-color-m-02;
|
||||
fill: $eve-security-color-m-02;
|
||||
background-color: var(--eve-security-color-m-02);
|
||||
fill: var(--eve-security-color-m-02);
|
||||
}
|
||||
|
||||
.eve-security-background-m-03 {
|
||||
background-color: $eve-security-color-m-03;
|
||||
fill: $eve-security-color-m-03;
|
||||
background-color: var(--eve-security-color-m-03);
|
||||
fill: var(--eve-security-color-m-03);
|
||||
}
|
||||
|
||||
.eve-security-background-m-04 {
|
||||
background-color: $eve-security-color-m-04;
|
||||
fill: $eve-security-color-m-04;
|
||||
background-color: var(--eve-security-color-m-04);
|
||||
fill: var(--eve-security-color-m-04);
|
||||
}
|
||||
|
||||
.eve-security-background-m-05 {
|
||||
background-color: $eve-security-color-m-05;
|
||||
fill: $eve-security-color-m-05;
|
||||
background-color: var(--eve-security-color-m-05);
|
||||
fill: var(--eve-security-color-m-05);
|
||||
}
|
||||
|
||||
.eve-security-background-m-06 {
|
||||
background-color: $eve-security-color-m-06;
|
||||
fill: $eve-security-color-m-06;
|
||||
background-color: var(--eve-security-color-m-06);
|
||||
fill: var(--eve-security-color-m-06);
|
||||
}
|
||||
|
||||
.eve-security-background-m-07 {
|
||||
background-color: $eve-security-color-m-07;
|
||||
fill: $eve-security-color-m-07;
|
||||
background-color: var(--eve-security-color-m-07);
|
||||
fill: var(--eve-security-color-m-07);
|
||||
}
|
||||
|
||||
.eve-security-background-m-08 {
|
||||
background-color: $eve-security-color-m-08;
|
||||
fill: $eve-security-color-m-08;
|
||||
background-color: var(--eve-security-color-m-08);
|
||||
fill: var(--eve-security-color-m-08);
|
||||
}
|
||||
|
||||
.eve-security-background-m-09 {
|
||||
background-color: $eve-security-color-m-09;
|
||||
fill: $eve-security-color-m-09;
|
||||
background-color: var(--eve-security-color-m-09);
|
||||
fill: var(--eve-security-color-m-09);
|
||||
}
|
||||
|
||||
.eve-security-background-m-10 {
|
||||
background-color: $eve-security-color-m-10;
|
||||
fill: $eve-security-color-m-10;
|
||||
background-color: var(--eve-security-color-m-10);
|
||||
fill: var(--eve-security-color-m-10);
|
||||
}
|
||||
|
||||
/* WH Type color classes */
|
||||
.eve-wh-type-color-high {
|
||||
color: $eve-wh-type-color-high;
|
||||
fill: $eve-wh-type-color-high;
|
||||
color: var(--eve-wh-type-color-high) !important;
|
||||
fill: var(--eve-wh-type-color-high);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-low {
|
||||
color: $eve-wh-type-color-low;
|
||||
fill: $eve-wh-type-color-low;
|
||||
color: var(--eve-wh-type-color-low) !important;
|
||||
fill: var(--eve-wh-type-color-low);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-null {
|
||||
color: $eve-wh-type-color-null;
|
||||
fill: $eve-wh-type-color-null;
|
||||
color: var(--eve-wh-type-color-null) !important;
|
||||
fill: var(--eve-wh-type-color-null);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c1 {
|
||||
color: $eve-wh-type-color-c1 !important;
|
||||
fill: $eve-wh-type-color-c1;
|
||||
color: var(--eve-wh-type-color-c1) !important;
|
||||
fill: var(--eve-wh-type-color-c1);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c2 {
|
||||
color: $eve-wh-type-color-c2 !important;
|
||||
fill: $eve-wh-type-color-c2;
|
||||
color: var(--eve-wh-type-color-c2) !important;
|
||||
fill: var(--eve-wh-type-color-c2);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c3 {
|
||||
color: $eve-wh-type-color-c3 !important;
|
||||
fill: $eve-wh-type-color-c3;
|
||||
color: var(--eve-wh-type-color-c3) !important;
|
||||
fill: var(--eve-wh-type-color-c3);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c4 {
|
||||
color: $eve-wh-type-color-c4 !important;
|
||||
fill: $eve-wh-type-color-c4;
|
||||
color: var(--eve-wh-type-color-c4) !important;
|
||||
fill: var(--eve-wh-type-color-c4);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c5 {
|
||||
color: $eve-wh-type-color-c5 !important;
|
||||
fill: $eve-wh-type-color-c5;
|
||||
color: var(--eve-wh-type-color-c5) !important;
|
||||
fill: var(--eve-wh-type-color-c5);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c6 {
|
||||
color: $eve-wh-type-color-c6 !important;
|
||||
fill: $eve-wh-type-color-c6;
|
||||
color: var(--eve-wh-type-color-c6) !important;
|
||||
fill: var(--eve-wh-type-color-c6);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c13 {
|
||||
color: $eve-wh-type-color-c13 !important;
|
||||
fill: $eve-wh-type-color-c13;
|
||||
color: var(--eve-wh-type-color-c13) !important;
|
||||
fill: var(--eve-wh-type-color-c13);
|
||||
}
|
||||
|
||||
.eve-wh-type-color-drifter {
|
||||
color: $eve-wh-type-color-drifter !important;
|
||||
fill: $eve-wh-type-color-drifter;
|
||||
color: var(--eve-wh-type-color-drifter) !important;
|
||||
fill: var(--eve-wh-type-color-drifter);
|
||||
}
|
||||
|
||||
.eve-wh-type-color-thera {
|
||||
color: $eve-wh-type-color-thera !important;
|
||||
fill: $eve-wh-type-color-thera;
|
||||
color: var(--eve-wh-type-color-thera) !important;
|
||||
fill: var(--eve-wh-type-color-thera);
|
||||
}
|
||||
|
||||
/* WH Type backgrounds */
|
||||
.eve-wh-type-background-high {
|
||||
background-color: $eve-wh-type-color-high;
|
||||
background-color: var(--eve-wh-type-color-high);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-low {
|
||||
background-color: $eve-wh-type-color-low;
|
||||
background-color: var(--eve-wh-type-color-low);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-null {
|
||||
background-color: $eve-wh-type-color-null;
|
||||
background-color: var(--eve-wh-type-color-null);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c1 {
|
||||
background-color: $eve-wh-type-color-c1;
|
||||
background-color: var(--eve-wh-type-color-c1);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c2 {
|
||||
background-color: $eve-wh-type-color-c2;
|
||||
background-color: var(--eve-wh-type-color-c2);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c3 {
|
||||
background-color: $eve-wh-type-color-c3;
|
||||
background-color: var(--eve-wh-type-color-c3);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c4 {
|
||||
background-color: $eve-wh-type-color-c4;
|
||||
background-color: var(--eve-wh-type-color-c4);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c5 {
|
||||
background-color: $eve-wh-type-color-c5;
|
||||
background-color: var(--eve-wh-type-color-c5);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c6 {
|
||||
background-color: $eve-wh-type-color-c6;
|
||||
background-color: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c13 {
|
||||
background-color: $eve-wh-type-color-c13;
|
||||
background-color: var(--eve-wh-type-color-c13);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-drifter {
|
||||
background-color: $eve-wh-type-color-drifter;
|
||||
background-color: var(--eve-wh-type-color-drifter);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-thera {
|
||||
background-color: $eve-wh-type-color-thera;
|
||||
background-color: var(--eve-wh-type-color-thera);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-zarzakh {
|
||||
background-color: $eve-wh-type-color-zarzakh;
|
||||
background-color: var(--eve-wh-type-color-zarzakh);
|
||||
}
|
||||
|
||||
/* Kind color classes */
|
||||
.eve-kind-color-high {
|
||||
color: $eve-wh-type-color-high;
|
||||
fill: $eve-wh-type-color-high;
|
||||
color: var(--eve-wh-type-color-high);
|
||||
fill: var(--eve-wh-type-color-high);
|
||||
}
|
||||
|
||||
.eve-kind-color-low {
|
||||
color: $eve-wh-type-color-low;
|
||||
fill: $eve-wh-type-color-low;
|
||||
color: var(--eve-wh-type-color-low);
|
||||
fill: var(--eve-wh-type-color-low);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.eve-kind-color-null {
|
||||
color: $eve-wh-type-color-null;
|
||||
fill: $eve-wh-type-color-null;
|
||||
color: var(--eve-wh-type-color-null);
|
||||
fill: var(--eve-wh-type-color-null);
|
||||
}
|
||||
|
||||
.eve-kind-color-wh {
|
||||
color: $eve-wh-type-color-c6;
|
||||
fill: $eve-wh-type-color-c6;
|
||||
color: var(--eve-wh-type-color-c6);
|
||||
fill: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-color-thera {
|
||||
color: $eve-wh-type-color-thera;
|
||||
fill: $eve-wh-type-color-thera;
|
||||
color: var(--eve-wh-type-color-thera);
|
||||
fill: var(--eve-wh-type-color-thera);
|
||||
}
|
||||
|
||||
.eve-kind-color-abyss {
|
||||
color: $eve-wh-type-color-c6;
|
||||
fill: $eve-wh-type-color-c6;
|
||||
color: var(--eve-wh-type-color-c6);
|
||||
fill: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-color-penalty {
|
||||
color: $eve-wh-type-color-c6;
|
||||
fill: $eve-wh-type-color-c6;
|
||||
color: var(--eve-wh-type-color-c6);
|
||||
fill: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-color-pochven {
|
||||
color: $eve-wh-type-color-c6;
|
||||
fill: $eve-wh-type-color-c6;
|
||||
color: var(--eve-wh-type-color-c6);
|
||||
fill: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-color-zarzakh {
|
||||
color: $eve-wh-type-color-zarzakh;
|
||||
fill: $eve-wh-type-color-zarzakh;
|
||||
color: var(--eve-wh-type-color-zarzakh);
|
||||
fill: var(--eve-wh-type-color-zarzakh);
|
||||
}
|
||||
|
||||
/* Kind backgrounds */
|
||||
.eve-kind-background-high {
|
||||
background-color: $eve-wh-type-color-high;
|
||||
background-color: var(--eve-wh-type-color-high);
|
||||
}
|
||||
|
||||
.eve-kind-background-low {
|
||||
background-color: $eve-wh-type-color-low;
|
||||
background-color: var(--eve-wh-type-color-low);
|
||||
}
|
||||
|
||||
.eve-kind-background-null {
|
||||
background-color: $eve-wh-type-color-null;
|
||||
background-color: var(--eve-wh-type-color-null);
|
||||
}
|
||||
|
||||
.eve-kind-background-wh {
|
||||
background-color: $eve-wh-type-color-c6;
|
||||
background-color: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-background-thera {
|
||||
background-color: $eve-wh-type-color-thera;
|
||||
background-color: var(--eve-wh-type-color-thera);
|
||||
}
|
||||
|
||||
.eve-kind-background-abyss {
|
||||
background-color: $eve-wh-type-color-c6;
|
||||
background-color: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-background-penalty {
|
||||
background-color: $eve-wh-type-color-c6;
|
||||
background-color: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-background-pochven {
|
||||
background-color: $eve-wh-type-color-c6;
|
||||
background-color: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-background-zarzakh {
|
||||
background-color: $eve-wh-type-color-zarzakh;
|
||||
background-color: var(--eve-wh-type-color-zarzakh);
|
||||
}
|
||||
|
||||
/* System status color classes */
|
||||
.eve-system-status-color-clear {
|
||||
color: $eve-solar-system-status-color-unknown;
|
||||
color: var(--eve-solar-system-status-color-unknown);
|
||||
}
|
||||
|
||||
.eve-system-status-color-home {
|
||||
color: $eve-solar-system-status-color-home;
|
||||
color: var(--eve-solar-system-status-color-home);
|
||||
}
|
||||
|
||||
.eve-system-status-color-friendly {
|
||||
color: $eve-solar-system-status-color-friendly;
|
||||
color: var(--eve-solar-system-status-color-friendly);
|
||||
}
|
||||
|
||||
.eve-system-status-color-lookingFor {
|
||||
color: $eve-solar-system-status-color-lookingFor;
|
||||
color: var(--eve-solar-system-status-color-lookingFor);
|
||||
}
|
||||
|
||||
.eve-system-status-color-warning {
|
||||
color: $eve-solar-system-status-color-warning;
|
||||
color: var(--eve-solar-system-status-color-warning);
|
||||
}
|
||||
|
||||
.eve-system-status-color-target {
|
||||
color: $eve-solar-system-status-color-target;
|
||||
color: var(--eve-solar-system-status-color-target);
|
||||
}
|
||||
|
||||
.eve-system-status-color-dangerous {
|
||||
color: $eve-solar-system-status-color-dangerous;
|
||||
color: var(--eve-solar-system-status-color-dangerous);
|
||||
}
|
||||
|
||||
/* Shapes */
|
||||
.wd-route-system-shape-triangle {
|
||||
clip-path: polygon(50% 0, 0 100%, 100% 100%);
|
||||
}
|
||||
|
||||
.wd-route-system-shape-circle {
|
||||
border-radius: 40%;
|
||||
}
|
||||
|
||||
/* Some additional background classes */
|
||||
.wd-marker-bookmark-color-shattered {
|
||||
background-color: #833ca4;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
|
||||
.wd-marker-bookmark-color-custom {
|
||||
background-color: #282828;
|
||||
border: 1px solid #4c4c4c;
|
||||
|
||||
2
assets/js/hooks/Mapper/components/map/styles/index.scss
Normal file
2
assets/js/hooks/Mapper/components/map/styles/index.scss
Normal file
@@ -0,0 +1,2 @@
|
||||
@import './neon-theme.scss';
|
||||
@import './pathfinder-theme.scss';
|
||||
@@ -1,74 +1,72 @@
|
||||
$pastel-blue: #5a7d9a;
|
||||
$pastel-pink: #d291bc;
|
||||
$pastel-green: #88b04b;
|
||||
$pastel-yellow: #ffdd59;
|
||||
$dark-bg: #2d2d2d;
|
||||
$text-color: #ffffff;
|
||||
$tooltip-bg: #202020;
|
||||
@import './eve-common-variables';
|
||||
@import './eve-common';
|
||||
|
||||
.react-flow {
|
||||
// background-color: $dark-bg;
|
||||
color: $text-color;
|
||||
.neon-theme {
|
||||
--rf-bg-color: #000000;
|
||||
--rf-soft-bg-color: #171717;
|
||||
|
||||
&__node {
|
||||
//cursor: auto;
|
||||
--rf-node-bg-color: #202020;
|
||||
--rf-node-soft-bg-color: #2b2b2b;
|
||||
--rf-text-color: #ffffff;
|
||||
|
||||
|
||||
--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;
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
&__pane {
|
||||
cursor: auto;
|
||||
}
|
||||
//&__edge {
|
||||
// stroke: $pastel-pink;
|
||||
// stroke-width: 2px;
|
||||
//
|
||||
// &.selected {
|
||||
// stroke: $pastel-yellow;
|
||||
// }
|
||||
//}
|
||||
|
||||
&__handle {
|
||||
//background-color: $pastel-green;
|
||||
//box-shadow: 0 0 5px rgba($pastel-green, 0.5);
|
||||
.context-menu-active {
|
||||
background-color: rgba(131, 131, 131, 0.33);
|
||||
}
|
||||
|
||||
&__minimap {
|
||||
background-color: rgba(66, 66, 66, 1);
|
||||
opacity: 0.7;
|
||||
//backdrop-filter: blur(5px);
|
||||
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;
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
$pastel-blue: #5a7d9a;
|
||||
$pastel-pink: #d291bc;
|
||||
$pastel-green: #88b04b;
|
||||
$pastel-yellow: #ffdd59;
|
||||
$dark-bg: #2d2d2d;
|
||||
$text-color: #ffffff;
|
||||
$tooltip-bg: #202020;
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
@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-node-bg-color: #202020;
|
||||
--rf-node-soft-bg-color: #313335;
|
||||
--rf-node-font-weight: bold;
|
||||
|
||||
--rf-text-color: #adadad;
|
||||
--tooltip-bg: #202020;
|
||||
|
||||
--rf-bg-variant: "lines";
|
||||
--rf-bg-gap: 32;
|
||||
--rf-bg-size: 1;
|
||||
--rf-bg-pattern-color: #313131;
|
||||
|
||||
--eve-effect-pulsar: #428bca;
|
||||
--eve-effect-magnetar: #e06fdf;
|
||||
--eve-effect-wolfRayet: #e28a0d;
|
||||
--eve-effect-blackHole: #000000;
|
||||
--eve-effect-cataclysmicVariable: #ffffbb;
|
||||
--eve-effect-redGiant: #d9534f;
|
||||
|
||||
--eve-wh-type-color-high: #5cb85c;
|
||||
--eve-wh-type-color-low: #e28a0d;
|
||||
--eve-wh-type-color-null: #d9534f;
|
||||
--eve-wh-type-color-c1: #428bca;
|
||||
--eve-wh-type-color-c2: #428bca;
|
||||
--eve-wh-type-color-c3: #e28a0d;
|
||||
--eve-wh-type-color-c4: #e28a0d;
|
||||
--eve-wh-type-color-c5: #d9534f;
|
||||
--eve-wh-type-color-c6: #d9534f;
|
||||
--eve-wh-type-color-c13: #7986cb;
|
||||
--eve-wh-type-color-drifter: #44aa82;
|
||||
|
||||
|
||||
--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);
|
||||
|
||||
--rf-tag-color: #fbbf24;
|
||||
--rf-has-user-characters: #5cb85c;
|
||||
}
|
||||
174
assets/js/hooks/Mapper/components/map/utils/changes.ts
Normal file
174
assets/js/hooks/Mapper/components/map/utils/changes.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { EdgeChange, NodeChange, Node, Edge } from 'reactflow';
|
||||
|
||||
// This function applies changes to nodes or edges that are triggered by React Flow internally.
|
||||
// When you drag a node for example, React Flow will send a position change update.
|
||||
// This function then applies the changes and returns the updated elements.
|
||||
function applyChanges(changes: any[], elements: any[]): any[] {
|
||||
// we need this hack to handle the setNodes and setEdges function of the useReactFlow hook for controlled flows
|
||||
if (changes.some(c => c.type === 'reset')) {
|
||||
return changes.filter(c => c.type === 'reset').map(c => c.item);
|
||||
}
|
||||
|
||||
const updatedElements: any[] = [];
|
||||
// By storing a map of changes for each element, we can a quick lookup as we
|
||||
// iterate over the elements array!
|
||||
const changesMap = new Map<any, any[]>();
|
||||
const addItemChanges: any[] = [];
|
||||
|
||||
for (const change of changes) {
|
||||
if (change.type === 'add') {
|
||||
addItemChanges.push(change);
|
||||
continue;
|
||||
} else if (change.type === 'remove' || change.type === 'replace') {
|
||||
// For a 'remove' change we can safely ignore any other changes queued for
|
||||
// the same element, it's going to be removed anyway!
|
||||
changesMap.set(change.id, [change]);
|
||||
} else {
|
||||
const elementChanges = changesMap.get(change.id);
|
||||
|
||||
if (elementChanges) {
|
||||
// If we have some changes queued already, we can do a mutable update of
|
||||
// that array and save ourselves some copying.
|
||||
elementChanges.push(change);
|
||||
} else {
|
||||
changesMap.set(change.id, [change]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const element of elements) {
|
||||
const changes = changesMap.get(element.id);
|
||||
|
||||
// When there are no changes for an element we can just push it unmodified,
|
||||
// no need to copy it.
|
||||
if (!changes) {
|
||||
updatedElements.push(element);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we have a 'remove' change queued, it'll be the only change in the array
|
||||
if (changes[0].type === 'remove') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (changes[0].type === 'replace') {
|
||||
updatedElements.push({ ...changes[0].item });
|
||||
continue;
|
||||
}
|
||||
|
||||
// For other types of changes, we want to start with a shallow copy of the
|
||||
// object so React knows this element has changed. Sequential changes will
|
||||
/// each _mutate_ this object, so there's only ever one copy.
|
||||
const updatedElement = { ...element };
|
||||
|
||||
for (const change of changes) {
|
||||
applyChange(change, updatedElement);
|
||||
}
|
||||
|
||||
updatedElements.push(updatedElement);
|
||||
}
|
||||
|
||||
// we need to wait for all changes to be applied before adding new items
|
||||
// to be able to add them at the correct index
|
||||
if (addItemChanges.length) {
|
||||
addItemChanges.forEach(change => {
|
||||
if (change.index !== undefined) {
|
||||
updatedElements.splice(change.index, 0, { ...change.item });
|
||||
} else {
|
||||
updatedElements.push({ ...change.item });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return updatedElements;
|
||||
}
|
||||
|
||||
// Applies a single change to an element. This is a *mutable* update.
|
||||
function applyChange(change: any, element: any): any {
|
||||
switch (change.type) {
|
||||
case 'select': {
|
||||
element.selected = change.selected;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'position': {
|
||||
if (typeof change.position !== 'undefined') {
|
||||
element.position = change.position;
|
||||
}
|
||||
|
||||
if (typeof change.dragging !== 'undefined') {
|
||||
element.dragging = change.dragging;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'dimensions': {
|
||||
if (typeof change.dimensions !== 'undefined') {
|
||||
element.measured ??= {};
|
||||
element.measured.width = change.dimensions.width;
|
||||
element.measured.height = change.dimensions.height;
|
||||
|
||||
if (change.setAttributes) {
|
||||
element.width = change.dimensions.width;
|
||||
element.height = change.dimensions.height;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof change.resizing === 'boolean') {
|
||||
element.resizing = change.resizing;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop in function that applies node changes to an array of nodes.
|
||||
* @public
|
||||
* @remarks Various events on the <ReactFlow /> component can produce an {@link NodeChange} that describes how to update the edges of your flow in some way.
|
||||
If you don't need any custom behaviour, this util can be used to take an array of these changes and apply them to your edges.
|
||||
* @param changes - Array of changes to apply
|
||||
* @param nodes - Array of nodes to apply the changes to
|
||||
* @returns Array of updated nodes
|
||||
* @example
|
||||
* const onNodesChange = useCallback(
|
||||
(changes) => {
|
||||
setNodes((oldNodes) => applyNodeChanges(changes, oldNodes));
|
||||
},
|
||||
[setNodes],
|
||||
);
|
||||
|
||||
return (
|
||||
<ReactFLow nodes={nodes} edges={edges} onNodesChange={onNodesChange} />
|
||||
);
|
||||
*/
|
||||
export function applyNodeChanges<NodeType extends Node = Node>(changes: NodeChange[], nodes: NodeType[]): NodeType[] {
|
||||
return applyChanges(changes, nodes) as NodeType[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop in function that applies edge changes to an array of edges.
|
||||
* @public
|
||||
* @remarks Various events on the <ReactFlow /> component can produce an {@link EdgeChange} that describes how to update the edges of your flow in some way.
|
||||
If you don't need any custom behaviour, this util can be used to take an array of these changes and apply them to your edges.
|
||||
* @param changes - Array of changes to apply
|
||||
* @param edges - Array of edge to apply the changes to
|
||||
* @returns Array of updated edges
|
||||
* @example
|
||||
* const onEdgesChange = useCallback(
|
||||
(changes) => {
|
||||
setEdges((oldEdges) => applyEdgeChanges(changes, oldEdges));
|
||||
},
|
||||
[setEdges],
|
||||
);
|
||||
|
||||
return (
|
||||
<ReactFlow nodes={nodes} edges={edges} onEdgesChange={onEdgesChange} />
|
||||
);
|
||||
*/
|
||||
export function applyEdgeChanges<EdgeType extends Edge = Edge>(changes: EdgeChange[], edges: EdgeType[]): EdgeType[] {
|
||||
return applyChanges(changes, edges) as EdgeType[];
|
||||
}
|
||||
@@ -48,6 +48,7 @@ const restoreWindowsFromLS = (): WidgetGridItem[] => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const raw = localStorage.getItem(SESSION_KEY.windows);
|
||||
if (!raw) {
|
||||
console.warn('No windows found in local storage!!');
|
||||
return DEFAULT_WINDOWS;
|
||||
}
|
||||
|
||||
@@ -63,7 +64,7 @@ const restoreWindowsFromLS = (): WidgetGridItem[] => {
|
||||
};
|
||||
|
||||
export const MapInterface = () => {
|
||||
const [items, setItems] = useState<WidgetGridItem[]>(restoreWindowsFromLS());
|
||||
const [items, setItems] = useState<WidgetGridItem[]>(restoreWindowsFromLS);
|
||||
|
||||
return (
|
||||
<WidgetsGrid
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
.SearchItem {
|
||||
& > * {
|
||||
font-size: 13px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.SearchItemEffect {
|
||||
font-weight: initial !important;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { Button } from 'primereact/button';
|
||||
import { IconField } from 'primereact/iconfield';
|
||||
import { AutoComplete } from 'primereact/autocomplete';
|
||||
import { OutCommand, SearchSystemItem } from '@/hooks/Mapper/types';
|
||||
import { SystemViewStandalone, WHClassView, WHEffectView } from '@/hooks/Mapper/components/ui-kit';
|
||||
import classes from './AddSystemDialog.module.scss';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
|
||||
import { sortWHClasses } from '@/hooks/Mapper/helpers';
|
||||
|
||||
export type SearchOnSubmitCallback = (item: SearchSystemItem) => void;
|
||||
|
||||
interface AddSystemDialogProps {
|
||||
title?: string;
|
||||
visible: boolean;
|
||||
setVisible: (visible: boolean) => void;
|
||||
onSubmit?: SearchOnSubmitCallback;
|
||||
excludedSystems?: number[];
|
||||
}
|
||||
|
||||
export const AddSystemDialog = ({
|
||||
title = 'Add system',
|
||||
visible,
|
||||
setVisible,
|
||||
onSubmit,
|
||||
excludedSystems = [],
|
||||
}: AddSystemDialogProps) => {
|
||||
const {
|
||||
outCommand,
|
||||
data: { wormholesData },
|
||||
} = useMapRootState();
|
||||
|
||||
const inputRef = useRef<any>();
|
||||
const onShow = useCallback(() => {
|
||||
inputRef.current?.focus();
|
||||
}, []);
|
||||
|
||||
const [filteredItems, setFilteredItems] = useState<SearchSystemItem[]>([]);
|
||||
const [selectedItem, setSelectedItem] = useState<SearchSystemItem[] | null>(null);
|
||||
|
||||
const searchItems = useCallback(
|
||||
async (event: { query: string }) => {
|
||||
if (event.query.length < 2) {
|
||||
setFilteredItems([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const query = event.query;
|
||||
|
||||
if (query.length === 0) {
|
||||
setFilteredItems([]);
|
||||
} else {
|
||||
try {
|
||||
const result = await outCommand({
|
||||
type: OutCommand.searchSystems,
|
||||
data: {
|
||||
text: query,
|
||||
},
|
||||
});
|
||||
|
||||
let prepared = (result.systems as SearchSystemItem[]).sort((a, b) => {
|
||||
const amatch = a.label.indexOf(query);
|
||||
const bmatch = b.label.indexOf(query);
|
||||
return amatch - bmatch;
|
||||
});
|
||||
|
||||
if (excludedSystems) {
|
||||
prepared = prepared.filter(x => !excludedSystems.includes(x.system_static_info.solar_system_id));
|
||||
}
|
||||
|
||||
setFilteredItems(prepared);
|
||||
} catch (error) {
|
||||
console.error('Error fetching data:', error);
|
||||
setFilteredItems([]);
|
||||
}
|
||||
}
|
||||
},
|
||||
[excludedSystems, outCommand],
|
||||
);
|
||||
|
||||
const ref = useRef({ onSubmit, selectedItem });
|
||||
ref.current = { onSubmit, selectedItem };
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
const { onSubmit, selectedItem } = ref.current;
|
||||
setFilteredItems([]);
|
||||
setSelectedItem([]);
|
||||
|
||||
if (!selectedItem) {
|
||||
setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
onSubmit?.(selectedItem[0]);
|
||||
setVisible(false);
|
||||
}, [setVisible]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
header={title}
|
||||
visible={visible}
|
||||
draggable={false}
|
||||
style={{ width: '520px' }}
|
||||
onShow={onShow}
|
||||
onHide={() => {
|
||||
if (!visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col gap-3 px-1.5">
|
||||
<div className="flex flex-col gap-2 py-3.5">
|
||||
<div className="flex flex-col gap-1">
|
||||
<IconField>
|
||||
<AutoComplete
|
||||
ref={inputRef}
|
||||
multiple
|
||||
showEmptyMessage
|
||||
scrollHeight="300px"
|
||||
value={selectedItem}
|
||||
suggestions={filteredItems}
|
||||
completeMethod={searchItems}
|
||||
onChange={e => {
|
||||
setSelectedItem(e.value.length < 2 ? e.value : [e.value[e.value.length - 1]]);
|
||||
}}
|
||||
emptyMessage="Not found any system..."
|
||||
placeholder="Type here..."
|
||||
field="label"
|
||||
id="value"
|
||||
className="w-full"
|
||||
itemTemplate={(item: SearchSystemItem) => {
|
||||
const { security, system_class, effect_power, effect_name, statics } = item.system_static_info;
|
||||
const sortedStatics = sortWHClasses(wormholesData, statics);
|
||||
const isWH = isWormholeSpace(system_class);
|
||||
|
||||
return (
|
||||
<div className={clsx('flex gap-1.5', classes.SearchItem)}>
|
||||
<SystemViewStandalone
|
||||
security={security}
|
||||
system_class={system_class}
|
||||
solar_system_id={item.value}
|
||||
class_title={item.class_title}
|
||||
solar_system_name={item.label}
|
||||
region_name={item.region_name}
|
||||
/>
|
||||
|
||||
{effect_name && isWH && (
|
||||
<WHEffectView
|
||||
effectName={effect_name}
|
||||
effectPower={effect_power}
|
||||
className={classes.SearchItemEffect}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isWH && (
|
||||
<div className="flex gap-1 grow justify-between">
|
||||
<div></div>
|
||||
<div className="flex gap-1">
|
||||
{sortedStatics.map(x => (
|
||||
<WHClassView key={x} whClassName={x} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
selectedItemTemplate={(item: SearchSystemItem) => (
|
||||
<SystemViewStandalone
|
||||
security={item.system_static_info.security}
|
||||
system_class={item.system_static_info.system_class}
|
||||
solar_system_id={item.value}
|
||||
class_title={item.class_title}
|
||||
solar_system_name={item.label}
|
||||
region_name={item.region_name}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</IconField>
|
||||
|
||||
<span className="text-[12px] text-stone-400 ml-1">*to search type at least 2 symbols.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
outlined
|
||||
disabled={!selectedItem || selectedItem.length !== 1}
|
||||
size="small"
|
||||
label="Submit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './AddSystemDialog';
|
||||
@@ -6,6 +6,7 @@ import { SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { CommandLinkSignatureToSystem } from '@/hooks/Mapper/types';
|
||||
import { SystemSignaturesContent } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignaturesContent';
|
||||
import { SHOW_DESCRIPTION_COLUMN_SETTING } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignatures';
|
||||
import {
|
||||
Setting,
|
||||
COSMIC_SIGNATURE,
|
||||
@@ -20,6 +21,7 @@ interface SystemLinkSignatureDialogProps {
|
||||
const signatureSettings: Setting[] = [
|
||||
{ key: COSMIC_SIGNATURE, name: 'Show Cosmic Signatures', value: true },
|
||||
{ key: SignatureGroup.Wormhole, name: 'Wormhole', value: true },
|
||||
{ key: SHOW_DESCRIPTION_COLUMN_SETTING, name: 'Show Description Column', value: true, isFilter: false },
|
||||
];
|
||||
|
||||
export const SystemLinkSignatureDialog = ({ data, setVisible }: SystemLinkSignatureDialogProps) => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { InputTextarea } from 'primereact/inputtextarea';
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { getSystemById } from '@/hooks/Mapper/helpers';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Button } from 'primereact/button';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
@@ -22,30 +23,21 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
|
||||
outCommand,
|
||||
} = useMapRootState();
|
||||
|
||||
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
|
||||
|
||||
const system = getSystemById(systems, systemId);
|
||||
|
||||
const [name, setName] = useState('');
|
||||
const [label, setLabel] = useState('');
|
||||
const [temporaryName, setTemporaryName] = useState('');
|
||||
const [description, setDescription] = useState('');
|
||||
const inputRef = useRef<HTMLInputElement>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!system) {
|
||||
return;
|
||||
}
|
||||
|
||||
const labels = new LabelsManager(system.labels || '');
|
||||
|
||||
setName(system.name || '');
|
||||
setLabel(labels.customLabel);
|
||||
setDescription(system.description || '');
|
||||
}, [system]);
|
||||
|
||||
const ref = useRef({ name, description, label, outCommand, systemId, system });
|
||||
ref.current = { name, description, label, outCommand, systemId, system };
|
||||
const ref = useRef({ name, description, temporaryName, label, outCommand, systemId, system });
|
||||
ref.current = { name, description, label, temporaryName, outCommand, systemId, system };
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
const { name, description, label, outCommand, systemId, system } = ref.current;
|
||||
const { name, description, label, temporaryName, outCommand, systemId, system } = ref.current;
|
||||
|
||||
const outLabel = new LabelsManager(system?.labels ?? '');
|
||||
outLabel.updateCustomLabel(label);
|
||||
@@ -58,6 +50,14 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
|
||||
},
|
||||
});
|
||||
|
||||
outCommand({
|
||||
type: OutCommand.updateSystemTemporaryName,
|
||||
data: {
|
||||
system_id: systemId,
|
||||
value: temporaryName,
|
||||
},
|
||||
});
|
||||
|
||||
outCommand({
|
||||
type: OutCommand.updateSystemName,
|
||||
data: {
|
||||
@@ -93,6 +93,21 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
|
||||
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9\-[\](){}]/g, '');
|
||||
}, []);
|
||||
|
||||
// Attention: this effect should be call only on mount.
|
||||
useEffect(() => {
|
||||
const { system } = ref.current;
|
||||
if (!system) {
|
||||
return;
|
||||
}
|
||||
|
||||
const labels = new LabelsManager(system.labels || '');
|
||||
|
||||
setName(system.name || '');
|
||||
setLabel(labels.customLabel);
|
||||
setTemporaryName(system.temporary_name || '');
|
||||
setDescription(system.description || '');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
header="System settings"
|
||||
@@ -167,6 +182,35 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
|
||||
</IconField>
|
||||
</div>
|
||||
|
||||
{isTempSystemNameEnabled && (
|
||||
<div className="flex flex-col gap-1">
|
||||
<label htmlFor="username">Temporary Name</label>
|
||||
|
||||
<IconField>
|
||||
{temporaryName !== '' && (
|
||||
<WdImgButton
|
||||
className="pi pi-trash text-red-400"
|
||||
textSize={WdImageSize.large}
|
||||
tooltip={{
|
||||
content: 'Remove temporary name',
|
||||
className: 'pi p-input-icon',
|
||||
position: TooltipPosition.top,
|
||||
}}
|
||||
onClick={() => setTemporaryName('')}
|
||||
/>
|
||||
)}
|
||||
<InputText
|
||||
id="temporaryName"
|
||||
aria-describedby="temporaryName"
|
||||
autoComplete="off"
|
||||
value={temporaryName}
|
||||
maxLength={10}
|
||||
onChange={e => setTemporaryName(e.target.value)}
|
||||
/>
|
||||
</IconField>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<label htmlFor="username">Description</label>
|
||||
<InputTextarea
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback, useMemo, useRef } from 'react';
|
||||
import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { VirtualScroller, VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
|
||||
@@ -8,6 +8,10 @@ import { CharacterTypeRaw, WithIsOwnCharacter } from '@/hooks/Mapper/types';
|
||||
import { CharacterCard, LayoutEventBlocker, WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { sortCharacters } from '@/hooks/Mapper/components/mapInterface/helpers/sortCharacters.ts';
|
||||
import useLocalStorageState from 'use-local-storage-state';
|
||||
import { useMapCheckPermissions, useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
|
||||
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
|
||||
type CharItemProps = {
|
||||
compact: boolean;
|
||||
@@ -62,6 +66,14 @@ export const LocalCharacters = () => {
|
||||
|
||||
const [systemId] = selectedSystems;
|
||||
|
||||
const restrictOfflineShowing = useMapGetOption('restrict_offline_showing');
|
||||
const isAdminOrManager = useMapCheckPermissions([UserPermission.MANAGE_MAP]);
|
||||
|
||||
const showOffline = useMemo(
|
||||
() => !restrictOfflineShowing || isAdminOrManager,
|
||||
[isAdminOrManager, restrictOfflineShowing],
|
||||
);
|
||||
|
||||
const itemTemplate = useItemTemplate();
|
||||
|
||||
const sorted = useMemo(() => {
|
||||
@@ -70,32 +82,39 @@ export const LocalCharacters = () => {
|
||||
.map(x => ({ ...x, isOwn: userCharacters.includes(x.eve_id), compact: settings.compact }))
|
||||
.sort(sortCharacters);
|
||||
|
||||
if (!settings.showOffline) {
|
||||
if (!showOffline || !settings.showOffline) {
|
||||
return sorted.filter(c => c.online);
|
||||
}
|
||||
|
||||
return sorted;
|
||||
// eslint-disable-next-line
|
||||
}, [characters, settings.showOffline, settings.compact, systemId, userCharacters, presentCharacters]);
|
||||
}, [showOffline, characters, settings.showOffline, settings.compact, systemId, userCharacters, presentCharacters]);
|
||||
|
||||
const isNobodyHere = sorted.length === 0;
|
||||
const isNotSelectedSystem = selectedSystems.length !== 1;
|
||||
const showList = sorted.length > 0 && selectedSystems.length === 1;
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const compact = useMaxWidth(ref, 145);
|
||||
|
||||
return (
|
||||
<Widget
|
||||
label={
|
||||
<div className="flex justify-between items-center text-xs w-full">
|
||||
<div className="flex justify-between items-center text-xs w-full" ref={ref}>
|
||||
<span className="select-none">Local{showList ? ` [${sorted.length}]` : ''}</span>
|
||||
<LayoutEventBlocker className="flex items-center gap-2">
|
||||
<WdCheckbox
|
||||
size="xs"
|
||||
labelSide="left"
|
||||
label={'Show offline'}
|
||||
value={settings.showOffline}
|
||||
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
|
||||
onChange={() => setSettings(() => ({ ...settings, showOffline: !settings.showOffline }))}
|
||||
/>
|
||||
{showOffline && (
|
||||
<WdTooltipWrapper content="Show offline characters in system">
|
||||
<WdCheckbox
|
||||
size="xs"
|
||||
labelSide="left"
|
||||
label={compact ? '' : 'Show offline'}
|
||||
value={settings.showOffline}
|
||||
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
|
||||
onChange={() => setSettings(() => ({ ...settings, showOffline: !settings.showOffline }))}
|
||||
/>
|
||||
</WdTooltipWrapper>
|
||||
)}
|
||||
|
||||
<span
|
||||
className={clsx('w-4 h-4 cursor-pointer', {
|
||||
@@ -115,7 +134,9 @@ export const LocalCharacters = () => {
|
||||
)}
|
||||
|
||||
{isNobodyHere && !isNotSelectedSystem && (
|
||||
<div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm">Nobody here</div>
|
||||
<div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm">
|
||||
Nobody here
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showList && (
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
.RouteSystem {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #ffffff;
|
||||
|
||||
cursor: pointer;
|
||||
transition: opacity 200ms;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Button } from 'primereact/button';
|
||||
import { WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
|
||||
import {
|
||||
RoutesType,
|
||||
useRouteProvider,
|
||||
} from '@/hooks/Mapper/components/mapInterface/widgets/RoutesWidget/RoutesProvider.tsx';
|
||||
import { CheckboxChangeEvent } from 'primereact/checkbox';
|
||||
import { PrettySwitchbox } from '@/hooks/Mapper/components/mapRootContent/components/MapSettings/components';
|
||||
|
||||
interface RoutesSettingsDialog {
|
||||
visible: boolean;
|
||||
@@ -38,8 +37,8 @@ export const RoutesSettingsDialog = ({ visible, setVisible }: RoutesSettingsDial
|
||||
currentData.current = data;
|
||||
|
||||
const handleChangeEvent = useCallback(
|
||||
(propName: keyof RoutesType) => (event: CheckboxChangeEvent) => {
|
||||
optionsRef.current = { ...optionsRef.current, [propName]: event.checked };
|
||||
(propName: keyof RoutesType) => (event: boolean) => {
|
||||
optionsRef.current = { ...optionsRef.current, [propName]: event };
|
||||
updateKey(x => x + 1);
|
||||
},
|
||||
[],
|
||||
@@ -71,14 +70,14 @@ export const RoutesSettingsDialog = ({ visible, setVisible }: RoutesSettingsDial
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex flex-col gap-3 p-2.5">
|
||||
<div className="flex flex-col gap-2 mb-2">
|
||||
{checkboxes.map(({ label, propName }) => (
|
||||
<WdCheckbox
|
||||
<PrettySwitchbox
|
||||
key={propName}
|
||||
label={label}
|
||||
value={optionsRef.current[propName]}
|
||||
onChange={handleChangeEvent(propName)}
|
||||
checked={optionsRef.current[propName]}
|
||||
setChecked={handleChangeEvent(propName)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -19,6 +19,13 @@ import { PrimeIcons } from 'primereact/api';
|
||||
import { RoutesSettingsDialog } from './RoutesSettingsDialog';
|
||||
import { RoutesProvider, useRouteProvider } from './RoutesProvider.tsx';
|
||||
import { ContextMenuSystemInfo, useContextMenuSystemInfoHandlers } from '@/hooks/Mapper/components/contexts';
|
||||
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
import {
|
||||
AddSystemDialog,
|
||||
SearchOnSubmitCallback,
|
||||
} from '@/hooks/Mapper/components/mapInterface/components/AddSystemDialog';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
|
||||
const sortByDist = (a: Route, b: Route) => {
|
||||
const distA = a.has_connection ? a.systems?.length || 0 : Infinity;
|
||||
@@ -161,6 +168,12 @@ export const RoutesWidgetContent = () => {
|
||||
export const RoutesWidgetComp = () => {
|
||||
const [routeSettingsVisible, setRouteSettingsVisible] = useState(false);
|
||||
const { data, update } = useRouteProvider();
|
||||
const {
|
||||
data: { hubs = [] },
|
||||
outCommand,
|
||||
} = useMapRootState();
|
||||
|
||||
const preparedHubs = useMemo(() => hubs.map(x => parseInt(x)), [hubs]);
|
||||
|
||||
const isSecure = data.path_type === 'secure';
|
||||
const handleSecureChange = useCallback(() => {
|
||||
@@ -170,27 +183,70 @@ export const RoutesWidgetComp = () => {
|
||||
});
|
||||
}, [data, update]);
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const compact = useMaxWidth(ref, 155);
|
||||
const [openAddSystem, setOpenAddSystem] = useState<boolean>(false);
|
||||
|
||||
const onAddSystem = useCallback(() => setOpenAddSystem(true), []);
|
||||
|
||||
const handleSubmitAddSystem: SearchOnSubmitCallback = useCallback(
|
||||
async item => {
|
||||
if (preparedHubs.includes(item.value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await outCommand({
|
||||
type: OutCommand.addHub,
|
||||
data: { system_id: item.value },
|
||||
});
|
||||
},
|
||||
[hubs, outCommand],
|
||||
);
|
||||
|
||||
return (
|
||||
<Widget
|
||||
label={
|
||||
<div className="flex justify-between items-center text-xs w-full">
|
||||
<div className="flex justify-between items-center text-xs w-full" ref={ref}>
|
||||
<span className="select-none">Routes</span>
|
||||
<LayoutEventBlocker className="flex items-center gap-2">
|
||||
<WdCheckbox
|
||||
size="xs"
|
||||
labelSide="left"
|
||||
label={'Show shortest'}
|
||||
value={!isSecure}
|
||||
onChange={handleSecureChange}
|
||||
classNameLabel={clsx('text-red-400')}
|
||||
<WdImgButton
|
||||
className={PrimeIcons.PLUS_CIRCLE}
|
||||
onClick={onAddSystem}
|
||||
tooltip={{
|
||||
content: 'Click here to add new system to routes',
|
||||
}}
|
||||
/>
|
||||
|
||||
<WdTooltipWrapper content="Show shortest route">
|
||||
<WdCheckbox
|
||||
size="xs"
|
||||
labelSide="left"
|
||||
label={compact ? '' : 'Show shortest'}
|
||||
value={!isSecure}
|
||||
onChange={handleSecureChange}
|
||||
classNameLabel={clsx('text-red-400')}
|
||||
/>
|
||||
</WdTooltipWrapper>
|
||||
<WdImgButton
|
||||
className={PrimeIcons.SLIDERS_H}
|
||||
onClick={() => setRouteSettingsVisible(true)}
|
||||
tooltip={{
|
||||
content: 'Click here to open Routes settings',
|
||||
}}
|
||||
/>
|
||||
<WdImgButton className={PrimeIcons.SLIDERS_H} onClick={() => setRouteSettingsVisible(true)} />
|
||||
</LayoutEventBlocker>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<RoutesWidgetContent />
|
||||
<RoutesSettingsDialog visible={routeSettingsVisible} setVisible={setRouteSettingsVisible} />
|
||||
|
||||
<AddSystemDialog
|
||||
title="Add system to routes"
|
||||
visible={openAddSystem}
|
||||
setVisible={() => setOpenAddSystem(false)}
|
||||
onSubmit={handleSubmitAddSystem}
|
||||
/>
|
||||
</Widget>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { Button } from 'primereact/button';
|
||||
import { Checkbox } from 'primereact/checkbox';
|
||||
import { TabPanel, TabView } from 'primereact/tabview';
|
||||
import styles from './SystemSignatureSettingsDialog.module.scss';
|
||||
import { PrettySwitchbox } from '@/hooks/Mapper/components/mapRootContent/components/MapSettings/components';
|
||||
|
||||
export type Setting = { key: string; name: string; value: boolean; isFilter?: boolean };
|
||||
|
||||
@@ -41,8 +41,8 @@ export const SystemSignatureSettingsDialog = ({
|
||||
}, [onSave, settings]);
|
||||
|
||||
return (
|
||||
<Dialog header="System Signatures Settings" visible={true} onHide={onCancel} className="w-full max-w-lg">
|
||||
<div className="flex flex-col gap-3">
|
||||
<Dialog header="System Signatures Settings" visible={true} onHide={onCancel} className="w-full max-w-lg h-[500px]">
|
||||
<div className="flex flex-col gap-3 justify-between h-full">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className={styles.verticalTabsContainer}>
|
||||
<TabView
|
||||
@@ -54,16 +54,12 @@ export const SystemSignatureSettingsDialog = ({
|
||||
<div className="w-full h-full flex flex-col gap-1">
|
||||
{filterSettings.map(setting => {
|
||||
return (
|
||||
<div key={setting.key} className="flex items-center">
|
||||
<Checkbox
|
||||
inputId={setting.key}
|
||||
checked={setting.value}
|
||||
onChange={() => handleSettingsChange(setting.key)}
|
||||
/>
|
||||
<label htmlFor={setting.key} className="ml-2">
|
||||
{setting.name}
|
||||
</label>
|
||||
</div>
|
||||
<PrettySwitchbox
|
||||
key={setting.key}
|
||||
label={setting.name}
|
||||
checked={setting.value}
|
||||
setChecked={() => handleSettingsChange(setting.key)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
@@ -72,16 +68,12 @@ export const SystemSignatureSettingsDialog = ({
|
||||
<div className="w-full h-full flex flex-col gap-1">
|
||||
{userSettings.map(setting => {
|
||||
return (
|
||||
<div key={setting.key} className="flex items-center">
|
||||
<Checkbox
|
||||
inputId={setting.key}
|
||||
checked={setting.value}
|
||||
onChange={() => handleSettingsChange(setting.key)}
|
||||
/>
|
||||
<label htmlFor={setting.key} className="ml-2">
|
||||
{setting.name}
|
||||
</label>
|
||||
</div>
|
||||
<PrettySwitchbox
|
||||
key={setting.key}
|
||||
label={setting.name}
|
||||
checked={setting.value}
|
||||
setChecked={() => handleSettingsChange(setting.key)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
@@ -1,32 +1,46 @@
|
||||
import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
|
||||
import { InfoDrawer, LayoutEventBlocker, TooltipPosition, WdImgButton } from '@/hooks/Mapper/components/ui-kit';
|
||||
import {
|
||||
InfoDrawer,
|
||||
LayoutEventBlocker,
|
||||
SystemView,
|
||||
TooltipPosition,
|
||||
WdCheckbox,
|
||||
WdImgButton,
|
||||
} from '@/hooks/Mapper/components/ui-kit';
|
||||
import { SystemSignaturesContent } from './SystemSignaturesContent';
|
||||
import {
|
||||
Setting,
|
||||
SystemSignatureSettingsDialog,
|
||||
COSMIC_SIGNATURE,
|
||||
COSMIC_ANOMALY,
|
||||
COSMIC_SIGNATURE,
|
||||
DEPLOYABLE,
|
||||
STRUCTURE,
|
||||
STARBASE,
|
||||
SHIP,
|
||||
DRONE,
|
||||
Setting,
|
||||
SHIP,
|
||||
STARBASE,
|
||||
STRUCTURE,
|
||||
SystemSignatureSettingsDialog,
|
||||
} from './SystemSignatureSettingsDialog';
|
||||
import { SignatureGroup } from '@/hooks/Mapper/types';
|
||||
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { PrimeIcons } from 'primereact/api';
|
||||
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { CheckboxChangeEvent } from 'primereact/checkbox';
|
||||
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
|
||||
const SIGNATURE_SETTINGS_KEY = 'wanderer_system_signature_settings_v4_1';
|
||||
const SIGNATURE_SETTINGS_KEY = 'wanderer_system_signature_settings_v5_2';
|
||||
export const SHOW_DESCRIPTION_COLUMN_SETTING = 'show_description_column_setting';
|
||||
export const SHOW_INSERTED_COLUMN_SETTING = 'show_inserted_column_setting';
|
||||
export const SHOW_UPDATED_COLUMN_SETTING = 'SHOW_UPDATED_COLUMN_SETTING';
|
||||
export const LAZY_DELETE_SIGNATURES_SETTING = 'LAZY_DELETE_SIGNATURES_SETTING';
|
||||
export const KEEP_LAZY_DELETE_SETTING = 'KEEP_LAZY_DELETE_ENABLED_SETTING';
|
||||
|
||||
const settings: Setting[] = [
|
||||
{ key: SHOW_INSERTED_COLUMN_SETTING, name: 'Show Inserted Column', value: false, isFilter: false },
|
||||
{ key: SHOW_UPDATED_COLUMN_SETTING, name: 'Show Updated Column', value: false, isFilter: false },
|
||||
{ key: SHOW_DESCRIPTION_COLUMN_SETTING, name: 'Show Description Column', value: false, isFilter: false },
|
||||
{ key: LAZY_DELETE_SIGNATURES_SETTING, name: 'Lazy Delete Signatures', value: false, isFilter: false },
|
||||
{ key: KEEP_LAZY_DELETE_SETTING, name: 'Keep "Lazy Delete" Enabled', value: false, isFilter: false },
|
||||
{ key: COSMIC_ANOMALY, name: 'Show Anomalies', value: true, isFilter: true },
|
||||
{ key: COSMIC_SIGNATURE, name: 'Show Cosmic Signatures', value: true, isFilter: true },
|
||||
{ key: DEPLOYABLE, name: 'Show Deployables', value: true, isFilter: true },
|
||||
@@ -58,12 +72,25 @@ export const SystemSignatures = () => {
|
||||
|
||||
const isNotSelectedSystem = selectedSystems.length !== 1;
|
||||
|
||||
const lazyDeleteValue = useMemo(() => {
|
||||
return settings.find(setting => setting.key === LAZY_DELETE_SIGNATURES_SETTING)!.value;
|
||||
}, [settings]);
|
||||
|
||||
const handleSettingsChange = useCallback((settings: Setting[]) => {
|
||||
setSettings(settings);
|
||||
localStorage.setItem(SIGNATURE_SETTINGS_KEY, JSON.stringify(settings));
|
||||
setVisible(false);
|
||||
}, []);
|
||||
|
||||
const handleLazyDeleteChange = useCallback((value: boolean) => {
|
||||
setSettings(settings => {
|
||||
const lazyDelete = settings.find(setting => setting.key === LAZY_DELETE_SIGNATURES_SETTING)!;
|
||||
lazyDelete.value = value;
|
||||
localStorage.setItem(SIGNATURE_SETTINGS_KEY, JSON.stringify(settings));
|
||||
return [...settings];
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const restoredSettings = localStorage.getItem(SIGNATURE_SETTINGS_KEY);
|
||||
|
||||
@@ -72,13 +99,34 @@ export const SystemSignatures = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const compact = useMaxWidth(ref, 260);
|
||||
|
||||
return (
|
||||
<Widget
|
||||
label={
|
||||
<div className="flex justify-between items-center text-xs w-full h-full">
|
||||
<div className="flex gap-1">System Signatures</div>
|
||||
<div className="flex justify-between items-center text-xs w-full h-full" ref={ref}>
|
||||
<div className="flex justify-between items-center gap-1">
|
||||
{!compact && (
|
||||
<div className="flex whitespace-nowrap text-ellipsis overflow-hidden text-stone-400">
|
||||
Signatures {isNotSelectedSystem ? '' : 'in'}
|
||||
</div>
|
||||
)}
|
||||
{!isNotSelectedSystem && <SystemView systemId={systemId} className="select-none text-center" hideRegion />}
|
||||
</div>
|
||||
|
||||
<LayoutEventBlocker className="flex gap-2.5">
|
||||
<WdTooltipWrapper content="Enable Lazy delete">
|
||||
<WdCheckbox
|
||||
size="xs"
|
||||
labelSide="left"
|
||||
label={compact ? '' : 'Lazy delete'}
|
||||
value={lazyDeleteValue}
|
||||
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300 whitespace-nowrap text-ellipsis overflow-hidden"
|
||||
onChange={(event: CheckboxChangeEvent) => handleLazyDeleteChange(!!event.checked)}
|
||||
/>
|
||||
</WdTooltipWrapper>
|
||||
|
||||
<WdImgButton
|
||||
className={PrimeIcons.QUESTION_CIRCLE}
|
||||
tooltip={{
|
||||
@@ -102,7 +150,7 @@ export const SystemSignatures = () => {
|
||||
</InfoDrawer>
|
||||
<InfoDrawer title={<b className="text-slate-50">How to delete?</b>}>
|
||||
For delete any signature first of all you need select before
|
||||
<br /> and then use <b className="text-sky-500">Backspace</b>
|
||||
<br /> and then use <b className="text-sky-500">Del</b>
|
||||
</InfoDrawer>
|
||||
</div>
|
||||
) as React.ReactNode,
|
||||
@@ -118,7 +166,7 @@ export const SystemSignatures = () => {
|
||||
System is not selected
|
||||
</div>
|
||||
) : (
|
||||
<SystemSignaturesContent systemId={systemId} settings={settings} />
|
||||
<SystemSignaturesContent systemId={systemId} settings={settings} onLazyDeleteChange={handleLazyDeleteChange} />
|
||||
)}
|
||||
{visible && (
|
||||
<SystemSignatureSettingsDialog
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useClipboard } from '@/hooks/Mapper/hooks/useClipboard';
|
||||
import { parseSignatures } from '@/hooks/Mapper/helpers';
|
||||
import { Commands, OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { WdTooltip, WdTooltipHandlers } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { GROUPS_LIST } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
|
||||
import {
|
||||
getGroupIdByRawGroup,
|
||||
GROUPS_LIST,
|
||||
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
|
||||
|
||||
import { DataTable, DataTableRowClickEvent, DataTableRowMouseEvent, SortOrder } from 'primereact/datatable';
|
||||
import { Column } from 'primereact/column';
|
||||
@@ -12,6 +14,7 @@ import useRefState from 'react-usestateref';
|
||||
import { Setting } from '../SystemSignatureSettingsDialog';
|
||||
import { useHotkey } from '@/hooks/Mapper/hooks';
|
||||
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
|
||||
import { useClipboard } from '@/hooks/Mapper/hooks/useClipboard';
|
||||
|
||||
import classes from './SystemSignaturesContent.module.scss';
|
||||
import clsx from 'clsx';
|
||||
@@ -22,10 +25,10 @@ import {
|
||||
getRowColorByTimeLeft,
|
||||
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/helpers';
|
||||
import {
|
||||
renderAddedTimeLeft,
|
||||
renderDescription,
|
||||
renderIcon,
|
||||
renderInfoColumn,
|
||||
renderInsertedTimeLeft,
|
||||
renderUpdatedTimeLeft,
|
||||
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders';
|
||||
import useLocalStorageState from 'use-local-storage-state';
|
||||
@@ -36,7 +39,9 @@ import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrap
|
||||
import { COSMIC_SIGNATURE } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignatureSettingsDialog';
|
||||
import {
|
||||
SHOW_DESCRIPTION_COLUMN_SETTING,
|
||||
SHOW_INSERTED_COLUMN_SETTING,
|
||||
SHOW_UPDATED_COLUMN_SETTING,
|
||||
LAZY_DELETE_SIGNATURES_SETTING,
|
||||
KEEP_LAZY_DELETE_SETTING,
|
||||
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures';
|
||||
type SystemSignaturesSortSettings = {
|
||||
sortField: string;
|
||||
@@ -44,7 +49,7 @@ type SystemSignaturesSortSettings = {
|
||||
};
|
||||
|
||||
const SORT_DEFAULT_VALUES: SystemSignaturesSortSettings = {
|
||||
sortField: 'updated_at',
|
||||
sortField: 'inserted_at',
|
||||
sortOrder: -1,
|
||||
};
|
||||
|
||||
@@ -54,6 +59,7 @@ interface SystemSignaturesContentProps {
|
||||
hideLinkedSignatures?: boolean;
|
||||
selectable?: boolean;
|
||||
onSelect?: (signature: SystemSignature) => void;
|
||||
onLazyDeleteChange?: (value: boolean) => void;
|
||||
}
|
||||
export const SystemSignaturesContent = ({
|
||||
systemId,
|
||||
@@ -61,14 +67,13 @@ export const SystemSignaturesContent = ({
|
||||
hideLinkedSignatures,
|
||||
selectable,
|
||||
onSelect,
|
||||
onLazyDeleteChange,
|
||||
}: SystemSignaturesContentProps) => {
|
||||
const { outCommand } = useMapRootState();
|
||||
|
||||
const [signatures, setSignatures, signaturesRef] = useRefState<SystemSignature[]>([]);
|
||||
const [selectedSignatures, setSelectedSignatures] = useState<SystemSignature[]>([]);
|
||||
const [nameColumnWidth, setNameColumnWidth] = useState('auto');
|
||||
const [parsedSignatures, setParsedSignatures] = useState<SystemSignature[]>([]);
|
||||
const [askUser, setAskUser] = useState(false);
|
||||
const [selectedSignature, setSelectedSignature] = useState<SystemSignature | null>(null);
|
||||
|
||||
const [hoveredSig, setHoveredSig] = useState<SystemSignature | null>(null);
|
||||
@@ -80,10 +85,20 @@ export const SystemSignaturesContent = ({
|
||||
const tableRef = useRef<HTMLDivElement>(null);
|
||||
const compact = useMaxWidth(tableRef, 260);
|
||||
const medium = useMaxWidth(tableRef, 380);
|
||||
const refData = useRef({ selectable });
|
||||
refData.current = { selectable };
|
||||
|
||||
const tooltipRef = useRef<WdTooltipHandlers>(null);
|
||||
|
||||
const { clipboardContent } = useClipboard();
|
||||
const { clipboardContent, setClipboardContent } = useClipboard();
|
||||
|
||||
const lazyDeleteValue = useMemo(() => {
|
||||
return settings.find(setting => setting.key === LAZY_DELETE_SIGNATURES_SETTING)?.value ?? false;
|
||||
}, [settings]);
|
||||
|
||||
const keepLazyDeleteValue = useMemo(() => {
|
||||
return settings.find(setting => setting.key === KEEP_LAZY_DELETE_SETTING)?.value ?? false;
|
||||
}, [settings]);
|
||||
|
||||
const handleResize = useCallback(() => {
|
||||
if (tableRef.current) {
|
||||
@@ -100,10 +115,7 @@ export const SystemSignaturesContent = ({
|
||||
[settings],
|
||||
);
|
||||
|
||||
const showInsertedColumn = useMemo(
|
||||
() => settings.find(s => s.key === SHOW_INSERTED_COLUMN_SETTING)?.value,
|
||||
[settings],
|
||||
);
|
||||
const showUpdatedColumn = useMemo(() => settings.find(s => s.key === SHOW_UPDATED_COLUMN_SETTING)?.value, [settings]);
|
||||
|
||||
const filteredSignatures = useMemo(() => {
|
||||
return signatures
|
||||
@@ -113,13 +125,14 @@ export const SystemSignaturesContent = ({
|
||||
}
|
||||
|
||||
const isCosmicSignature = x.kind === COSMIC_SIGNATURE;
|
||||
const preparedGroup = getGroupIdByRawGroup(x.group);
|
||||
|
||||
if (isCosmicSignature) {
|
||||
const showCosmicSignatures = settings.find(y => y.key === COSMIC_SIGNATURE)?.value;
|
||||
if (showCosmicSignatures) {
|
||||
return !x.group || groupSettings.find(y => y.key === x.group)?.value;
|
||||
return !x.group || groupSettings.find(y => y.key === preparedGroup)?.value;
|
||||
} else {
|
||||
return !!x.group && groupSettings.find(y => y.key === x.group)?.value;
|
||||
return !!x.group && groupSettings.find(y => y.key === preparedGroup)?.value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,13 +149,17 @@ export const SystemSignaturesContent = ({
|
||||
data: { system_id: systemId },
|
||||
});
|
||||
|
||||
setAskUser(false);
|
||||
setSignatures(signatures);
|
||||
}, [outCommand, systemId]);
|
||||
|
||||
const handleUpdateSignatures = useCallback(
|
||||
async (newSignatures: SystemSignature[], updateOnly: boolean) => {
|
||||
const { added, updated, removed } = getActualSigs(signaturesRef.current, newSignatures, updateOnly);
|
||||
async (newSignatures: SystemSignature[], updateOnly: boolean, skipUpdateUntouched?: boolean) => {
|
||||
const { added, updated, removed } = getActualSigs(
|
||||
signaturesRef.current,
|
||||
newSignatures,
|
||||
updateOnly,
|
||||
skipUpdateUntouched,
|
||||
);
|
||||
|
||||
const { signatures: updatedSignatures } = await outCommand({
|
||||
type: OutCommand.updateSignatures,
|
||||
@@ -160,34 +177,32 @@ export const SystemSignaturesContent = ({
|
||||
[outCommand, systemId],
|
||||
);
|
||||
|
||||
const handleDeleteSelected = useCallback(async () => {
|
||||
if (selectable) {
|
||||
return;
|
||||
}
|
||||
if (selectedSignatures.length === 0) {
|
||||
return;
|
||||
}
|
||||
const selectedSignaturesEveIds = selectedSignatures.map(x => x.eve_id);
|
||||
await handleUpdateSignatures(
|
||||
signatures.filter(x => !selectedSignaturesEveIds.includes(x.eve_id)),
|
||||
false,
|
||||
);
|
||||
}, [handleUpdateSignatures, selectable, signatures, selectedSignatures]);
|
||||
const handleDeleteSelected = useCallback(
|
||||
async (e: KeyboardEvent) => {
|
||||
if (selectable) {
|
||||
return;
|
||||
}
|
||||
if (selectedSignatures.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const selectedSignaturesEveIds = selectedSignatures.map(x => x.eve_id);
|
||||
await handleUpdateSignatures(
|
||||
signatures.filter(x => !selectedSignaturesEveIds.includes(x.eve_id)),
|
||||
false,
|
||||
true,
|
||||
);
|
||||
},
|
||||
[handleUpdateSignatures, selectable, signatures, selectedSignatures],
|
||||
);
|
||||
|
||||
const handleSelectAll = useCallback(() => {
|
||||
setSelectedSignatures(signatures);
|
||||
}, [signatures]);
|
||||
|
||||
const handleReplaceAll = useCallback(() => {
|
||||
handleUpdateSignatures(parsedSignatures, false);
|
||||
setAskUser(false);
|
||||
}, [parsedSignatures, handleUpdateSignatures]);
|
||||
|
||||
const handleUpdateOnly = useCallback(() => {
|
||||
handleUpdateSignatures(parsedSignatures, true);
|
||||
setAskUser(false);
|
||||
}, [parsedSignatures, handleUpdateSignatures]);
|
||||
|
||||
const handleSelectSignatures = useCallback(
|
||||
// TODO still will be good to define types if we use typescript
|
||||
// @ts-ignore
|
||||
@@ -201,38 +216,51 @@ export const SystemSignaturesContent = ({
|
||||
[onSelect, selectable],
|
||||
);
|
||||
|
||||
useHotkey(true, ['a'], handleSelectAll);
|
||||
|
||||
useHotkey(false, ['Backspace'], handleDeleteSelected);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectable) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!clipboardContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handlePaste = async (clipboardContent: string) => {
|
||||
const newSignatures = parseSignatures(
|
||||
clipboardContent,
|
||||
settings.map(x => x.key),
|
||||
);
|
||||
|
||||
const { removed } = getActualSigs(signaturesRef.current, newSignatures, false);
|
||||
handleUpdateSignatures(newSignatures, !lazyDeleteValue);
|
||||
|
||||
if (!signaturesRef.current || !signaturesRef.current.length || !removed.length) {
|
||||
handleUpdateSignatures(newSignatures, false);
|
||||
} else {
|
||||
setParsedSignatures(newSignatures);
|
||||
setAskUser(true);
|
||||
if (lazyDeleteValue && !keepLazyDeleteValue) {
|
||||
onLazyDeleteChange?.(false);
|
||||
}
|
||||
}, [clipboardContent, selectable]);
|
||||
};
|
||||
|
||||
const handleEnterRow = useCallback(
|
||||
(e: DataTableRowMouseEvent) => {
|
||||
setHoveredSig(filteredSignatures[e.index]);
|
||||
tooltipRef.current?.show(e.originalEvent);
|
||||
},
|
||||
[filteredSignatures],
|
||||
);
|
||||
|
||||
const handleLeaveRow = useCallback((e: DataTableRowMouseEvent) => {
|
||||
tooltipRef.current?.hide(e.originalEvent);
|
||||
setHoveredSig(null);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (refData.current.selectable) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!clipboardContent?.text) {
|
||||
return;
|
||||
}
|
||||
|
||||
handlePaste(clipboardContent.text);
|
||||
setClipboardContent(null);
|
||||
}, [clipboardContent, selectable, lazyDeleteValue, keepLazyDeleteValue]);
|
||||
|
||||
useHotkey(true, ['a'], handleSelectAll);
|
||||
useHotkey(false, ['Backspace', 'Delete'], handleDeleteSelected);
|
||||
|
||||
useEffect(() => {
|
||||
if (!systemId) {
|
||||
setSignatures([]);
|
||||
setAskUser(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -266,19 +294,6 @@ export const SystemSignaturesContent = ({
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleEnterRow = useCallback(
|
||||
(e: DataTableRowMouseEvent) => {
|
||||
setHoveredSig(filteredSignatures[e.index]);
|
||||
tooltipRef.current?.show(e.originalEvent);
|
||||
},
|
||||
[filteredSignatures],
|
||||
);
|
||||
|
||||
const handleLeaveRow = useCallback((e: DataTableRowMouseEvent) => {
|
||||
tooltipRef.current?.hide(e.originalEvent);
|
||||
setHoveredSig(null);
|
||||
}, []);
|
||||
|
||||
const renderToolbar = (/*row: SystemSignature*/) => {
|
||||
return (
|
||||
<div className="flex justify-end items-center gap-2 mr-[4px]">
|
||||
@@ -330,7 +345,7 @@ export const SystemSignaturesContent = ({
|
||||
return clsx(classes.TableRowCompact, 'bg-amber-500/50 hover:bg-amber-500/70 transition duration-200');
|
||||
}
|
||||
|
||||
const dateClass = getRowColorByTimeLeft(row.updated_at ? new Date(row.updated_at) : undefined);
|
||||
const dateClass = getRowColorByTimeLeft(row.inserted_at ? new Date(row.inserted_at) : undefined);
|
||||
if (!dateClass) {
|
||||
return clsx(classes.TableRowCompact, 'hover:bg-purple-400/20 transition duration-200');
|
||||
}
|
||||
@@ -357,11 +372,11 @@ export const SystemSignaturesContent = ({
|
||||
header="Group"
|
||||
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
|
||||
hidden={compact}
|
||||
style={{ maxWidth: 110, minWidth: 110, width: 110 }}
|
||||
sortable
|
||||
></Column>
|
||||
<Column
|
||||
field="info"
|
||||
// header="Info"
|
||||
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
|
||||
body={renderInfoColumn}
|
||||
style={{ maxWidth: nameColumnWidth }}
|
||||
@@ -378,26 +393,26 @@ export const SystemSignaturesContent = ({
|
||||
></Column>
|
||||
)}
|
||||
|
||||
{showInsertedColumn && (
|
||||
<Column
|
||||
field="inserted_at"
|
||||
header="Added"
|
||||
dataType="date"
|
||||
bodyClassName="w-[70px] text-ellipsis overflow-hidden whitespace-nowrap"
|
||||
body={renderAddedTimeLeft}
|
||||
sortable
|
||||
></Column>
|
||||
|
||||
{showUpdatedColumn && (
|
||||
<Column
|
||||
field="inserted_at"
|
||||
header="Inserted"
|
||||
field="updated_at"
|
||||
header="Updated"
|
||||
dataType="date"
|
||||
bodyClassName="w-[70px] text-ellipsis overflow-hidden whitespace-nowrap"
|
||||
body={renderInsertedTimeLeft}
|
||||
body={renderUpdatedTimeLeft}
|
||||
sortable
|
||||
></Column>
|
||||
)}
|
||||
|
||||
<Column
|
||||
field="updated_at"
|
||||
header="Updated"
|
||||
dataType="date"
|
||||
bodyClassName="w-[70px] text-ellipsis overflow-hidden whitespace-nowrap"
|
||||
body={renderUpdatedTimeLeft}
|
||||
sortable
|
||||
></Column>
|
||||
|
||||
{!selectable && (
|
||||
<Column
|
||||
bodyClassName="p-0 pl-1 pr-2"
|
||||
@@ -424,27 +439,6 @@ export const SystemSignaturesContent = ({
|
||||
signatureData={selectedSignature}
|
||||
/>
|
||||
)}
|
||||
|
||||
{askUser && (
|
||||
<div className="absolute left-[1px] top-[29px] h-[calc(100%-30px)] w-[calc(100%-3px)] bg-stone-900/10 backdrop-blur-sm">
|
||||
<div className="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center">
|
||||
<div className="text-stone-400/80 text-sm">
|
||||
<div className="flex flex-col text-center gap-2">
|
||||
<button className="p-button p-component p-button-outlined p-button-sm btn-wide">
|
||||
<span className="p-button-label p-c" onClick={handleUpdateOnly}>
|
||||
Update
|
||||
</span>
|
||||
</button>
|
||||
<button className="p-button p-component p-button-outlined p-button-sm btn-wide">
|
||||
<span className="p-button-label p-c" onClick={handleReplaceAll}>
|
||||
Update & Delete missing
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { GroupType, SignatureGroup } from '@/hooks/Mapper/types';
|
||||
import {
|
||||
GroupType,
|
||||
SignatureGroup,
|
||||
SignatureGroupENG,
|
||||
SignatureGroupRU,
|
||||
SignatureKind,
|
||||
SignatureKindENG,
|
||||
SignatureKindRU,
|
||||
} from '@/hooks/Mapper/types';
|
||||
|
||||
export const TIME_ONE_MINUTE = 1000 * 60;
|
||||
export const TIME_TEN_MINUTES = 1000 * 60 * 10;
|
||||
@@ -24,3 +32,43 @@ export const GROUPS: Record<SignatureGroup, GroupType> = {
|
||||
[SignatureGroup.Wormhole]: { id: SignatureGroup.Wormhole, icon: '/icons/brackets/wormhole.png', ...wh },
|
||||
[SignatureGroup.CosmicSignature]: { id: SignatureGroup.CosmicSignature, icon: '/icons/x_close14.png', w: 9, h: 9 },
|
||||
};
|
||||
|
||||
export const MAPPING_GROUP_TO_ENG = {
|
||||
// ENGLISH
|
||||
[SignatureGroupENG.GasSite]: SignatureGroup.GasSite,
|
||||
[SignatureGroupENG.RelicSite]: SignatureGroup.RelicSite,
|
||||
[SignatureGroupENG.DataSite]: SignatureGroup.DataSite,
|
||||
[SignatureGroupENG.OreSite]: SignatureGroup.OreSite,
|
||||
[SignatureGroupENG.CombatSite]: SignatureGroup.CombatSite,
|
||||
[SignatureGroupENG.Wormhole]: SignatureGroup.Wormhole,
|
||||
[SignatureGroupENG.CosmicSignature]: SignatureGroup.CosmicSignature,
|
||||
|
||||
// RUSSIAN
|
||||
[SignatureGroupRU.GasSite]: SignatureGroup.GasSite,
|
||||
[SignatureGroupRU.RelicSite]: SignatureGroup.RelicSite,
|
||||
[SignatureGroupRU.DataSite]: SignatureGroup.DataSite,
|
||||
[SignatureGroupRU.OreSite]: SignatureGroup.OreSite,
|
||||
[SignatureGroupRU.CombatSite]: SignatureGroup.CombatSite,
|
||||
[SignatureGroupRU.Wormhole]: SignatureGroup.Wormhole,
|
||||
[SignatureGroupRU.CosmicSignature]: SignatureGroup.CosmicSignature,
|
||||
};
|
||||
|
||||
export const MAPPING_TYPE_TO_ENG = {
|
||||
// ENGLISH
|
||||
[SignatureKindENG.CosmicSignature]: SignatureKind.CosmicSignature,
|
||||
[SignatureKindENG.CosmicAnomaly]: SignatureKind.CosmicAnomaly,
|
||||
[SignatureKindENG.Structure]: SignatureKind.Structure,
|
||||
[SignatureKindENG.Ship]: SignatureKind.Ship,
|
||||
[SignatureKindENG.Deployable]: SignatureKind.Deployable,
|
||||
[SignatureKindENG.Drone]: SignatureKind.Drone,
|
||||
|
||||
// RUSSIAN
|
||||
[SignatureKindRU.CosmicSignature]: SignatureKind.CosmicSignature,
|
||||
[SignatureKindRU.CosmicAnomaly]: SignatureKind.CosmicAnomaly,
|
||||
[SignatureKindRU.Structure]: SignatureKind.Structure,
|
||||
[SignatureKindRU.Ship]: SignatureKind.Ship,
|
||||
[SignatureKindRU.Deployable]: SignatureKind.Deployable,
|
||||
[SignatureKindRU.Drone]: SignatureKind.Drone,
|
||||
};
|
||||
|
||||
export const getGroupIdByRawGroup = (val: string) => MAPPING_GROUP_TO_ENG[val as SignatureGroup];
|
||||
|
||||
@@ -6,6 +6,7 @@ export const getActualSigs = (
|
||||
oldSignatures: SystemSignature[],
|
||||
newSignatures: SystemSignature[],
|
||||
updateOnly: boolean,
|
||||
skipUpdateUntouched?: boolean,
|
||||
): { added: SystemSignature[]; updated: SystemSignature[]; removed: SystemSignature[] } => {
|
||||
const updated: SystemSignature[] = [];
|
||||
const removed: SystemSignature[] = [];
|
||||
@@ -19,7 +20,7 @@ export const getActualSigs = (
|
||||
const isNeedUpgrade = getState(GROUPS_LIST, newSig) > getState(GROUPS_LIST, oldSig);
|
||||
if (isNeedUpgrade) {
|
||||
updated.push({ ...oldSig, group: newSig.group, name: newSig.name });
|
||||
} else {
|
||||
} else if (!skipUpdateUntouched) {
|
||||
updated.push({ ...oldSig });
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export * from './renderIcon';
|
||||
export * from './renderDescription';
|
||||
export * from './renderName';
|
||||
export * from './renderInsertedTimeLeft';
|
||||
export * from './renderAddedTimeLeft';
|
||||
export * from './renderUpdatedTimeLeft';
|
||||
export * from './renderLinkedSystem';
|
||||
export * from './renderInfoColumn';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { TimeLeft } from '@/hooks/Mapper/components/ui-kit';
|
||||
|
||||
export const renderInsertedTimeLeft = (row: SystemSignature) => {
|
||||
export const renderAddedTimeLeft = (row: SystemSignature) => {
|
||||
return (
|
||||
<div className="flex w-full items-center">
|
||||
<TimeLeft cDate={row.inserted_at ? new Date(row.inserted_at) : undefined} />
|
||||
@@ -1,3 +0,0 @@
|
||||
.whFontSize {
|
||||
font-size: 11px !important;
|
||||
}
|
||||
@@ -2,21 +2,31 @@ import { PrimeIcons } from 'primereact/api';
|
||||
import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { SystemViewStandalone, WHClassView } from '@/hooks/Mapper/components/ui-kit';
|
||||
|
||||
import {
|
||||
k162Types,
|
||||
renderK162Type,
|
||||
} from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureK162TypeSelect';
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { renderName } from './renderName.tsx';
|
||||
import classes from './renderInfoColumn.module.scss';
|
||||
|
||||
export const renderInfoColumn = (row: SystemSignature) => {
|
||||
if (!row.group || row.group === SignatureGroup.Wormhole) {
|
||||
let k162TypeOption = null;
|
||||
if (row.custom_info) {
|
||||
const customInfo = JSON.parse(row.custom_info);
|
||||
if (customInfo.k162Type) {
|
||||
k162TypeOption = k162Types.find(x => x.value === customInfo.k162Type);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex justify-start items-center gap-[6px]">
|
||||
<div className="flex justify-start items-center gap-[4px]">
|
||||
{row.type && (
|
||||
<WHClassView
|
||||
className="text-[11px]"
|
||||
classNameWh={classes.whFontSize}
|
||||
highlightName
|
||||
classNameWh="!text-[11px] !font-bold"
|
||||
hideWhClass={!!row.linked_system}
|
||||
whClassName={row.type}
|
||||
noOffset
|
||||
@@ -24,9 +34,10 @@ export const renderInfoColumn = (row: SystemSignature) => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{!row.linked_system && row.type === 'K162' && !!k162TypeOption && <>{renderK162Type(k162TypeOption)}</>}
|
||||
|
||||
{row.linked_system && (
|
||||
<>
|
||||
{/*<span className="w-4 h-4 hero-arrow-long-right"></span>*/}
|
||||
<span title={row.linked_system?.solar_system_name}>
|
||||
<SystemViewStandalone
|
||||
className={clsx('select-none text-center cursor-context-menu')}
|
||||
|
||||
@@ -16,6 +16,8 @@ export const MapRootContent = ({}: MapRootContentProps) => {
|
||||
const { interfaceSettings } = useMapRootState();
|
||||
const { isShowMenu } = interfaceSettings;
|
||||
|
||||
const themeClass = `${interfaceSettings.theme ?? 'neon'}-theme`;
|
||||
|
||||
const [showOnTheMap, setShowOnTheMap] = useState(false);
|
||||
const [showMapSettings, setShowMapSettings] = useState(false);
|
||||
const mapInterface = <MapInterface />;
|
||||
@@ -26,27 +28,29 @@ export const MapRootContent = ({}: MapRootContentProps) => {
|
||||
useSkipContextMenu();
|
||||
|
||||
return (
|
||||
<Layout map={<MapWrapper />}>
|
||||
{!isShowMenu ? (
|
||||
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
|
||||
<div className="absolute top-0 left-0 w-[calc(100%-3.5rem)] h-full pointer-events-none">
|
||||
<Topbar />
|
||||
<div className={themeClass}>
|
||||
<Layout map={<MapWrapper />}>
|
||||
{!isShowMenu ? (
|
||||
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
|
||||
<div className="absolute top-0 left-0 w-[calc(100%-3.5rem)] h-full pointer-events-none">
|
||||
<Topbar />
|
||||
{mapInterface}
|
||||
</div>
|
||||
<div className="absolute top-0 right-0 w-14 h-[calc(100%+3.5rem)] pointer-events-auto">
|
||||
<RightBar onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
|
||||
<Topbar>
|
||||
<MapContextMenu onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
|
||||
</Topbar>
|
||||
{mapInterface}
|
||||
</div>
|
||||
<div className="absolute top-0 right-0 w-14 h-[calc(100%+3.5rem)] pointer-events-auto">
|
||||
<RightBar onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
|
||||
<Topbar>
|
||||
<MapContextMenu onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
|
||||
</Topbar>
|
||||
{mapInterface}
|
||||
</div>
|
||||
)}
|
||||
<OnTheMap show={showOnTheMap} onHide={() => setShowOnTheMap(false)} />
|
||||
<MapSettings show={showMapSettings} onHide={() => setShowMapSettings(false)} />
|
||||
</Layout>
|
||||
)}
|
||||
<OnTheMap show={showOnTheMap} onHide={() => setShowOnTheMap(false)} />
|
||||
<MapSettings show={showMapSettings} onHide={() => setShowMapSettings(false)} />
|
||||
</Layout>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,12 +5,14 @@ import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { VirtualScroller, VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
ConnectionType,
|
||||
ConnectionOutput,
|
||||
ConnectionInfoOutput,
|
||||
OutCommand,
|
||||
Passage,
|
||||
SolarSystemConnection,
|
||||
} from '@/hooks/Mapper/types';
|
||||
|
||||
import { PassageCard } from './PassageCard';
|
||||
import { InfoDrawer, SystemView } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { kgToTons } from '@/hooks/Mapper/utils/kgToTons.ts';
|
||||
@@ -75,8 +77,12 @@ export const Connections = ({ selectedConnection, onHide }: OnTheMapProps) => {
|
||||
return connections.find(x => x.source === selectedConnection.source && x.target === selectedConnection.target);
|
||||
}, [connections, selectedConnection]);
|
||||
|
||||
const isWormhole = useMemo(() => {
|
||||
return cnInfo?.type !== ConnectionType.gate;
|
||||
}, [cnInfo]);
|
||||
|
||||
const [passages, setPassages] = useState<Passage[]>([]);
|
||||
const [info, setInfo] = useState<ConnectionInfoOutput>(null);
|
||||
const [info, setInfo] = useState<ConnectionInfoOutput | null>(null);
|
||||
|
||||
const loadInfo = useCallback(
|
||||
async (connection: SolarSystemConnection) => {
|
||||
@@ -135,7 +141,7 @@ export const Connections = ({ selectedConnection, onHide }: OnTheMapProps) => {
|
||||
>
|
||||
<div className={clsx(classes.SidebarContent, '')}>
|
||||
{/* Connection Info */}
|
||||
<div className="px-2 pb-3 flex flex-col gap-2">
|
||||
<div className="px-2 flex flex-col gap-2">
|
||||
{/* Connection Info Row */}
|
||||
<InfoDrawer title="Connection" rightSide>
|
||||
<div className="flex justify-end gap-2 items-center">
|
||||
@@ -153,14 +159,25 @@ export const Connections = ({ selectedConnection, onHide }: OnTheMapProps) => {
|
||||
</div>
|
||||
</InfoDrawer>
|
||||
|
||||
{/* Connection Info Row */}
|
||||
<InfoDrawer title="Approximate mass of passages" rightSide>
|
||||
{kgToTons(approximateMass)}
|
||||
</InfoDrawer>
|
||||
<div className="flex justify-between gap-2">
|
||||
{/*Left column*/}
|
||||
<div>
|
||||
{isWormhole && info?.marl_eol_time && (
|
||||
<InfoDrawer title="Mark EOL Time">
|
||||
<TimeAgo timestamp={info.marl_eol_time} />
|
||||
</InfoDrawer>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<InfoDrawer title="Mark EOL Time" rightSide>
|
||||
{info?.marl_eol_time ? <TimeAgo timestamp={info.marl_eol_time} /> : ' unknown '}
|
||||
</InfoDrawer>
|
||||
{/*Right column*/}
|
||||
<div>
|
||||
{isWormhole && (
|
||||
<InfoDrawer title="Approximate mass of passages" rightSide>
|
||||
{kgToTons(approximateMass)}
|
||||
</InfoDrawer>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2"></div>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,8 @@ import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrap
|
||||
import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
import { MenuItem } from 'primereact/menuitem';
|
||||
import { useMapCheckPermissions } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
|
||||
|
||||
export interface MapContextMenuProps {
|
||||
onShowOnTheMap?: () => void;
|
||||
@@ -14,6 +16,8 @@ export interface MapContextMenuProps {
|
||||
export const MapContextMenu = ({ onShowOnTheMap, onShowMapSettings }: MapContextMenuProps) => {
|
||||
const { outCommand, setInterfaceSettings } = useMapRootState();
|
||||
|
||||
const canTrackCharacters = useMapCheckPermissions([UserPermission.TRACK_CHARACTER]);
|
||||
|
||||
const menuRight = useRef<Menu>(null);
|
||||
|
||||
const handleAddCharacter = useCallback(() => {
|
||||
@@ -24,34 +28,40 @@ export const MapContextMenu = ({ onShowOnTheMap, onShowMapSettings }: MapContext
|
||||
}, [outCommand]);
|
||||
|
||||
const items = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
label: 'Tracking',
|
||||
icon: 'pi pi-user-plus',
|
||||
command: handleAddCharacter,
|
||||
},
|
||||
{
|
||||
label: 'On the map',
|
||||
icon: 'pi pi-hashtag',
|
||||
command: onShowOnTheMap,
|
||||
},
|
||||
{ separator: true },
|
||||
{
|
||||
label: 'Settings',
|
||||
icon: `pi pi-cog`,
|
||||
command: onShowMapSettings,
|
||||
},
|
||||
{
|
||||
label: 'Dock menu',
|
||||
icon: 'pi pi-window-maximize',
|
||||
command: () =>
|
||||
setInterfaceSettings(x => ({
|
||||
...x,
|
||||
isShowMenu: !x.isShowMenu,
|
||||
})),
|
||||
},
|
||||
] as MenuItem[];
|
||||
}, [handleAddCharacter, onShowMapSettings, onShowOnTheMap, setInterfaceSettings]);
|
||||
return (
|
||||
[
|
||||
{
|
||||
label: 'Tracking',
|
||||
icon: 'pi pi-user-plus',
|
||||
command: handleAddCharacter,
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
label: 'On the map',
|
||||
icon: 'pi pi-hashtag',
|
||||
command: onShowOnTheMap,
|
||||
visible: canTrackCharacters,
|
||||
},
|
||||
{ separator: true, visible: true },
|
||||
{
|
||||
label: 'Settings',
|
||||
icon: `pi pi-cog`,
|
||||
command: onShowMapSettings,
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
label: 'Dock menu',
|
||||
icon: 'pi pi-window-maximize',
|
||||
command: () =>
|
||||
setInterfaceSettings(x => ({
|
||||
...x,
|
||||
isShowMenu: !x.isShowMenu,
|
||||
})),
|
||||
visible: true,
|
||||
},
|
||||
] as MenuItem[]
|
||||
).filter(item => item.visible);
|
||||
}, [canTrackCharacters, handleAddCharacter, onShowMapSettings, onShowOnTheMap, setInterfaceSettings]);
|
||||
|
||||
return (
|
||||
<div className="ml-1">
|
||||
|
||||
@@ -3,8 +3,13 @@ import { Dialog } from 'primereact/dialog';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { TabPanel, TabView } from 'primereact/tabview';
|
||||
import { PrettySwitchbox } from './components';
|
||||
import { InterfaceStoredSettings, InterfaceStoredSettingsProps, useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import {
|
||||
InterfaceStoredSettingsProps,
|
||||
useMapRootState,
|
||||
InterfaceStoredSettings,
|
||||
} from '@/hooks/Mapper/mapRootProvider';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
import { Dropdown } from 'primereact/dropdown';
|
||||
|
||||
export enum UserSettingsRemoteProps {
|
||||
link_signature_on_splash = 'link_signature_on_splash',
|
||||
@@ -37,107 +42,182 @@ 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' },
|
||||
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' },
|
||||
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.isThickConnections, label: 'Thicker connections' },
|
||||
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: '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 });
|
||||
const [userRemoteSettings, setUserRemoteSettings] = useState<UserSettingsRemote>({
|
||||
...DEFAULT_REMOTE_SETTINGS,
|
||||
});
|
||||
|
||||
const mergedSettings = useMemo(() => {
|
||||
return {
|
||||
...interfaceSettings,
|
||||
...userRemoteSettings,
|
||||
...interfaceSettings,
|
||||
};
|
||||
}, [userRemoteSettings, interfaceSettings]);
|
||||
|
||||
|
||||
const handleShow = async () => {
|
||||
const { user_settings } = await outCommand({
|
||||
type: OutCommand.getUserSettings,
|
||||
data: null,
|
||||
});
|
||||
|
||||
setUserRemoteSettings({
|
||||
...user_settings,
|
||||
});
|
||||
};
|
||||
|
||||
const handleChangeChecked = useCallback(
|
||||
(prop: keyof UserSettings) => async (checked: boolean) => {
|
||||
// @ts-ignore
|
||||
if (UserSettingsRemoteList.includes(prop)) {
|
||||
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,
|
||||
});
|
||||
},
|
||||
[interfaceSettings, outCommand, setInterfaceSettings, userRemoteSettings],
|
||||
[userRemoteSettings, interfaceSettings, outCommand, setInterfaceSettings],
|
||||
);
|
||||
|
||||
const renderCheckboxesList = (list: CheckboxesList) => {
|
||||
return list.map(x => {
|
||||
const renderSettingItem = (item: SettingsListItem) => {
|
||||
const currentValue = mergedSettings[item.prop];
|
||||
|
||||
if (item.type === 'checkbox') {
|
||||
return (
|
||||
<PrettySwitchbox
|
||||
key={x.prop}
|
||||
label={x.label}
|
||||
checked={mergedSettings[x.prop]}
|
||||
setChecked={handleChangeChecked(x.prop)}
|
||||
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 renderSettingsList = (list: SettingsListItem[]) => {
|
||||
return list.map(renderSettingItem);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
header="Map settings"
|
||||
header="Map user settings"
|
||||
visible={show}
|
||||
draggable={false}
|
||||
style={{ width: '550px' }}
|
||||
onShow={handleShow}
|
||||
onHide={() => {
|
||||
if (!show) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!show) return;
|
||||
setActiveIndex(0);
|
||||
onHide();
|
||||
}}
|
||||
@@ -147,25 +227,35 @@ export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
|
||||
<div className={styles.verticalTabsContainer}>
|
||||
<TabView
|
||||
activeIndex={activeIndex}
|
||||
onTabChange={e => setActiveIndex(e.index)}
|
||||
onTabChange={(e) => setActiveIndex(e.index)}
|
||||
className={styles.verticalTabView}
|
||||
>
|
||||
<TabPanel header="Common" headerClassName={styles.verticalTabHeader}>
|
||||
<div className="w-full h-full flex flex-col gap-1">{renderCheckboxesList(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(COMMON_CHECKBOXES_PROPS)}
|
||||
</div>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel header="Systems" headerClassName={styles.verticalTabHeader}>
|
||||
<div className="w-full h-full flex flex-col gap-1">
|
||||
{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}>
|
||||
{renderSettingItem(THEME_SETTING)}
|
||||
</TabPanel>
|
||||
</TabView>
|
||||
</div>
|
||||
|
||||
@@ -8,6 +8,8 @@ import clsx from 'clsx';
|
||||
import { CharacterTypeRaw, WithIsOwnCharacter } from '@/hooks/Mapper/types';
|
||||
import { CharacterCard, WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
|
||||
import useLocalStorageState from 'use-local-storage-state';
|
||||
import { useMapCheckPermissions, useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
|
||||
|
||||
type WindowLocalSettingsType = {
|
||||
compact: boolean;
|
||||
@@ -50,14 +52,22 @@ export const OnTheMap = ({ show, onHide }: OnTheMapProps) => {
|
||||
defaultValue: STORED_DEFAULT_VALUES,
|
||||
});
|
||||
|
||||
const restrictOfflineShowing = useMapGetOption('restrict_offline_showing');
|
||||
const isAdminOrManager = useMapCheckPermissions([UserPermission.MANAGE_MAP]);
|
||||
|
||||
const showOffline = useMemo(
|
||||
() => !restrictOfflineShowing || isAdminOrManager,
|
||||
[isAdminOrManager, restrictOfflineShowing],
|
||||
);
|
||||
|
||||
const sorted = useMemo(() => {
|
||||
const out = characters.map(x => ({ ...x, isOwn: userCharacters.includes(x.eve_id) })).sort(sortCharacters);
|
||||
if (!settings.hideOffline) {
|
||||
if (showOffline && !settings.hideOffline) {
|
||||
return out;
|
||||
}
|
||||
|
||||
return out.filter(x => x.online);
|
||||
}, [characters, settings.hideOffline, userCharacters]);
|
||||
}, [showOffline, characters, settings.hideOffline, userCharacters]);
|
||||
|
||||
return (
|
||||
<Sidebar
|
||||
@@ -70,14 +80,16 @@ export const OnTheMap = ({ show, onHide }: OnTheMapProps) => {
|
||||
>
|
||||
<div className={clsx(classes.SidebarContent, '')}>
|
||||
<div className={'flex justify-end items-center gap-2 px-3'}>
|
||||
<WdCheckbox
|
||||
size="m"
|
||||
labelSide="left"
|
||||
label={'Hide offline'}
|
||||
value={settings.hideOffline}
|
||||
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
|
||||
onChange={() => setSettings(() => ({ ...settings, hideOffline: !settings.hideOffline }))}
|
||||
/>
|
||||
{showOffline && (
|
||||
<WdCheckbox
|
||||
size="m"
|
||||
labelSide="left"
|
||||
label={'Hide offline'}
|
||||
value={settings.hideOffline}
|
||||
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
|
||||
onChange={() => setSettings(() => ({ ...settings, hideOffline: !settings.hideOffline }))}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<VirtualScroller
|
||||
|
||||
@@ -6,6 +6,9 @@ import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
|
||||
|
||||
import { useMapCheckPermissions } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
|
||||
|
||||
interface RightBarProps {
|
||||
onShowOnTheMap?: () => void;
|
||||
onShowMapSettings?: () => void;
|
||||
@@ -14,6 +17,8 @@ interface RightBarProps {
|
||||
export const RightBar = ({ onShowOnTheMap, onShowMapSettings }: RightBarProps) => {
|
||||
const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState();
|
||||
|
||||
const canTrackCharacters = useMapCheckPermissions([UserPermission.TRACK_CHARACTER]);
|
||||
|
||||
const isShowMinimap = interfaceSettings.isShowMinimap === undefined ? true : interfaceSettings.isShowMinimap;
|
||||
|
||||
const handleAddCharacter = useCallback(() => {
|
||||
@@ -64,19 +69,21 @@ export const RightBar = ({ onShowOnTheMap, onShowMapSettings }: RightBarProps) =
|
||||
</button>
|
||||
</WdTooltipWrapper>
|
||||
|
||||
<WdTooltipWrapper content="Show on the map" position={TooltipPosition.left}>
|
||||
<button
|
||||
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
|
||||
type="button"
|
||||
onClick={onShowOnTheMap}
|
||||
>
|
||||
<i className="pi pi-hashtag"></i>
|
||||
</button>
|
||||
</WdTooltipWrapper>
|
||||
{canTrackCharacters && (
|
||||
<WdTooltipWrapper content="Show on the map" position={TooltipPosition.left}>
|
||||
<button
|
||||
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
|
||||
type="button"
|
||||
onClick={onShowOnTheMap}
|
||||
>
|
||||
<i className="pi pi-hashtag"></i>
|
||||
</button>
|
||||
</WdTooltipWrapper>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center mb-2 gap-1">
|
||||
<WdTooltipWrapper content="User settings" position={TooltipPosition.left}>
|
||||
<WdTooltipWrapper content="Map user settings" position={TooltipPosition.left}>
|
||||
<button
|
||||
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
|
||||
type="button"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
// import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { OutCommand, SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { Controller, FormProvider, useForm } from 'react-hook-form';
|
||||
import {
|
||||
@@ -25,102 +24,120 @@ export const SignatureSettings = ({ systemId, show, onHide, signatureData }: Map
|
||||
const { outCommand } = useMapRootState();
|
||||
|
||||
const handleShow = async () => {};
|
||||
const form = useForm<Partial<SystemSignaturePrepared>>({});
|
||||
const signatureForm = useForm<Partial<SystemSignaturePrepared>>({});
|
||||
|
||||
const handleSave = useCallback(async () => {
|
||||
if (!signatureData) {
|
||||
return;
|
||||
}
|
||||
const handleSave = useCallback(
|
||||
async (e: any) => {
|
||||
e?.preventDefault();
|
||||
if (!signatureData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { group, ...values } = form.getValues();
|
||||
let out = { ...signatureData };
|
||||
const { group, ...values } = signatureForm.getValues();
|
||||
let out = { ...signatureData };
|
||||
|
||||
switch (group) {
|
||||
case SignatureGroup.Wormhole:
|
||||
if (values.linked_system) {
|
||||
await outCommand({
|
||||
type: OutCommand.linkSignatureToSystem,
|
||||
data: {
|
||||
signature_eve_id: signatureData.eve_id,
|
||||
solar_system_source: systemId,
|
||||
solar_system_target: values.linked_system,
|
||||
},
|
||||
});
|
||||
}
|
||||
switch (group) {
|
||||
case SignatureGroup.Wormhole:
|
||||
if (values.linked_system) {
|
||||
await outCommand({
|
||||
type: OutCommand.linkSignatureToSystem,
|
||||
data: {
|
||||
signature_eve_id: signatureData.eve_id,
|
||||
solar_system_source: systemId,
|
||||
solar_system_target: values.linked_system,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (values.type != null) {
|
||||
out = { ...out, type: values.type };
|
||||
}
|
||||
out = {
|
||||
...out,
|
||||
custom_info: JSON.stringify({
|
||||
k162Type: values.k162Type,
|
||||
}),
|
||||
};
|
||||
|
||||
if (signatureData.group !== SignatureGroup.Wormhole) {
|
||||
out = { ...out, name: '' };
|
||||
}
|
||||
if (values.type != null) {
|
||||
out = { ...out, type: values.type };
|
||||
}
|
||||
|
||||
break;
|
||||
case SignatureGroup.CosmicSignature:
|
||||
out = { ...out, type: '', name: '' };
|
||||
break;
|
||||
default:
|
||||
if (values.name != null) {
|
||||
out = { ...out, name: values.name ?? '' };
|
||||
}
|
||||
}
|
||||
if (signatureData.group !== SignatureGroup.Wormhole) {
|
||||
out = { ...out, name: '' };
|
||||
}
|
||||
|
||||
if (values.description != null) {
|
||||
out = { ...out, description: values.description };
|
||||
}
|
||||
break;
|
||||
case SignatureGroup.CosmicSignature:
|
||||
out = { ...out, type: '', name: '' };
|
||||
break;
|
||||
default:
|
||||
if (values.name != null) {
|
||||
out = { ...out, name: values.name ?? '' };
|
||||
}
|
||||
}
|
||||
|
||||
if (values.description != null) {
|
||||
out = { ...out, description: values.description };
|
||||
}
|
||||
|
||||
// Note: when type of signature changed from WH to other type - we should drop name
|
||||
if (
|
||||
group !== SignatureGroup.Wormhole && // new
|
||||
signatureData.group === SignatureGroup.Wormhole && // prev
|
||||
signatureData.linked_system
|
||||
) {
|
||||
await outCommand({
|
||||
type: OutCommand.unlinkSignature,
|
||||
data: { signature_eve_id: signatureData.eve_id, solar_system_source: systemId },
|
||||
});
|
||||
|
||||
out = { ...out, type: '' };
|
||||
}
|
||||
|
||||
if (group === SignatureGroup.Wormhole && signatureData.linked_system != null && values.linked_system === null) {
|
||||
await outCommand({
|
||||
type: OutCommand.unlinkSignature,
|
||||
data: { signature_eve_id: signatureData.eve_id, solar_system_source: systemId },
|
||||
});
|
||||
}
|
||||
|
||||
// Note: despite groups have optional type - this will always set
|
||||
out = { ...out, group: group! };
|
||||
|
||||
// Note: when type of signature changed from WH to other type - we should drop name
|
||||
if (
|
||||
group !== SignatureGroup.Wormhole && // new
|
||||
signatureData.group === SignatureGroup.Wormhole && // prev
|
||||
signatureData.linked_system
|
||||
) {
|
||||
await outCommand({
|
||||
type: OutCommand.unlinkSignature,
|
||||
data: { signature_eve_id: signatureData.eve_id, solar_system_source: systemId },
|
||||
type: OutCommand.updateSignatures,
|
||||
data: {
|
||||
system_id: systemId,
|
||||
added: [],
|
||||
updated: [out],
|
||||
removed: [],
|
||||
},
|
||||
});
|
||||
|
||||
out = { ...out, type: '' };
|
||||
}
|
||||
|
||||
if (group === SignatureGroup.Wormhole && signatureData.linked_system != null && values.linked_system === null) {
|
||||
await outCommand({
|
||||
type: OutCommand.unlinkSignature,
|
||||
data: { signature_eve_id: signatureData.eve_id, solar_system_source: systemId },
|
||||
});
|
||||
}
|
||||
|
||||
// Note: despite groups have optional type - this will always set
|
||||
out = { ...out, group: group! };
|
||||
|
||||
await outCommand({
|
||||
type: OutCommand.updateSignatures,
|
||||
data: {
|
||||
system_id: systemId,
|
||||
added: [],
|
||||
updated: [out],
|
||||
removed: [],
|
||||
},
|
||||
});
|
||||
|
||||
form.reset();
|
||||
onHide();
|
||||
}, [form, onHide, outCommand, signatureData, systemId]);
|
||||
signatureForm.reset();
|
||||
onHide();
|
||||
},
|
||||
[signatureForm, onHide, outCommand, signatureData, systemId],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!signatureData) {
|
||||
form.reset();
|
||||
signatureForm.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
const { linked_system, ...rest } = signatureData;
|
||||
const { linked_system, custom_info, ...rest } = signatureData;
|
||||
|
||||
form.reset({
|
||||
let k162Type = null;
|
||||
if (custom_info) {
|
||||
const customInfo = JSON.parse(custom_info);
|
||||
k162Type = customInfo.k162Type;
|
||||
}
|
||||
|
||||
signatureForm.reset({
|
||||
linked_system: linked_system?.solar_system_id.toString() ?? undefined,
|
||||
k162Type: k162Type,
|
||||
...rest,
|
||||
});
|
||||
}, [form, signatureData]);
|
||||
}, [signatureForm, signatureData]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
@@ -138,32 +155,34 @@ export const SignatureSettings = ({ systemId, show, onHide, signatureData }: Map
|
||||
}}
|
||||
>
|
||||
<SystemsSettingsProvider initialValue={{ systemId }}>
|
||||
<FormProvider {...form}>
|
||||
<div className="flex flex-col gap-2 justify-between">
|
||||
<div className="w-full flex flex-col gap-1 p-1 min-h-[150px]">
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||
<span>Group:</span>
|
||||
<SignatureGroupSelect name="group" />
|
||||
</label>
|
||||
<FormProvider {...signatureForm}>
|
||||
<form onSubmit={handleSave}>
|
||||
<div className="flex flex-col gap-2 justify-between">
|
||||
<div className="w-full flex flex-col gap-1 p-1 min-h-[150px]">
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||
<span>Group:</span>
|
||||
<SignatureGroupSelect name="group" />
|
||||
</label>
|
||||
|
||||
<SignatureGroupContent />
|
||||
<SignatureGroupContent />
|
||||
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||
<span>Description:</span>
|
||||
<Controller
|
||||
name="description"
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<InputText placeholder="Type description" value={field.value} onChange={field.onChange} />
|
||||
)}
|
||||
/>
|
||||
</label>
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||
<span>Description:</span>
|
||||
<Controller
|
||||
name="description"
|
||||
control={signatureForm.control}
|
||||
render={({ field }) => (
|
||||
<InputText placeholder="Type description" value={field.value} onChange={field.onChange} />
|
||||
)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button onClick={handleSave} outlined size="small" label="Save"></Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button onClick={handleSave} outlined size="small" label="Save"></Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</FormProvider>
|
||||
</SystemsSettingsProvider>
|
||||
</Dialog>
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { SignatureWormholeTypeSelect } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureWormholeTypeSelect';
|
||||
import { SignatureK162TypeSelect } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureK162TypeSelect';
|
||||
import { SignatureLeadsToSelect } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureLeadsToSelect';
|
||||
|
||||
export const SignatureGroupContentWormholes = () => {
|
||||
const { watch } = useFormContext<SystemSignature>();
|
||||
const type = watch('type');
|
||||
|
||||
return (
|
||||
<>
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||
@@ -9,6 +15,13 @@ export const SignatureGroupContentWormholes = () => {
|
||||
<SignatureWormholeTypeSelect name="type" />
|
||||
</label>
|
||||
|
||||
{type === 'K162' && (
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||
<span>K162 Type:</span>
|
||||
<SignatureK162TypeSelect name="k162Type" />
|
||||
</label>
|
||||
)}
|
||||
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||
<span>Leads To:</span>
|
||||
<SignatureLeadsToSelect name="linked_system" />
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
import { Dropdown } from 'primereact/dropdown';
|
||||
import clsx from 'clsx';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { useMemo } from 'react';
|
||||
import { SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { WHClassView } from '@/hooks/Mapper/components/ui-kit';
|
||||
|
||||
export const k162Types = [
|
||||
{
|
||||
label: 'Hi-Sec',
|
||||
value: 'hs',
|
||||
whClassName: 'A641',
|
||||
},
|
||||
{
|
||||
label: 'Low-Sec',
|
||||
value: 'ls',
|
||||
whClassName: 'J377',
|
||||
},
|
||||
{
|
||||
label: 'Null-Sec',
|
||||
value: 'ns',
|
||||
whClassName: 'C248',
|
||||
},
|
||||
{
|
||||
label: 'C1',
|
||||
value: 'c1',
|
||||
whClassName: 'E004',
|
||||
},
|
||||
{
|
||||
label: 'C2',
|
||||
value: 'c2',
|
||||
whClassName: 'D382',
|
||||
},
|
||||
{
|
||||
label: 'C3',
|
||||
value: 'c3',
|
||||
whClassName: 'L477',
|
||||
},
|
||||
{
|
||||
label: 'C4',
|
||||
value: 'c4',
|
||||
whClassName: 'M001',
|
||||
},
|
||||
{
|
||||
label: 'C5',
|
||||
value: 'c5',
|
||||
whClassName: 'L614',
|
||||
},
|
||||
{
|
||||
label: 'C6',
|
||||
value: 'c6',
|
||||
whClassName: 'G008',
|
||||
},
|
||||
{
|
||||
label: 'C13',
|
||||
value: 'c13',
|
||||
whClassName: 'A009',
|
||||
},
|
||||
{
|
||||
label: 'Thera',
|
||||
value: 'thera',
|
||||
whClassName: 'F353',
|
||||
},
|
||||
{
|
||||
label: 'Pochven',
|
||||
value: 'pochven',
|
||||
whClassName: 'F216',
|
||||
},
|
||||
];
|
||||
|
||||
const renderNoValue = () => <div className="flex gap-2 items-center">-Unknown-</div>;
|
||||
|
||||
// @ts-ignore
|
||||
export const renderK162Type = (option: {
|
||||
label?: string;
|
||||
value: string;
|
||||
security?: string;
|
||||
system_class?: number;
|
||||
whClassName?: string;
|
||||
}) => {
|
||||
if (!option) {
|
||||
return renderNoValue();
|
||||
}
|
||||
const { value, whClassName = '' } = option;
|
||||
if (value == null) {
|
||||
return renderNoValue();
|
||||
}
|
||||
|
||||
return (
|
||||
<WHClassView
|
||||
classNameWh="!text-[11px] !font-bold"
|
||||
hideWhClassName
|
||||
hideTooltip
|
||||
whClassName={whClassName}
|
||||
noOffset
|
||||
useShortTitle
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export interface SignatureK162TypeSelectProps {
|
||||
name: string;
|
||||
defaultValue?: string;
|
||||
}
|
||||
|
||||
export const SignatureK162TypeSelect = ({ name, defaultValue = '' }: SignatureK162TypeSelectProps) => {
|
||||
const { control } = useFormContext<SystemSignature>();
|
||||
|
||||
const options = useMemo(() => {
|
||||
return [{ value: null }, ...k162Types];
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Controller
|
||||
// @ts-ignore
|
||||
name={name}
|
||||
control={control}
|
||||
defaultValue={defaultValue}
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<Dropdown
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
options={options}
|
||||
optionValue="value"
|
||||
placeholder="Select K162 type"
|
||||
className={clsx('w-full')}
|
||||
scrollHeight="240px"
|
||||
itemTemplate={renderK162Type}
|
||||
valueTemplate={renderK162Type}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './SignatureK162TypeSelect.tsx';
|
||||
@@ -13,13 +13,14 @@ import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
|
||||
// @ts-ignore
|
||||
const renderLinkedSystemItem = (option: { value: string }) => {
|
||||
if (option.value == null) {
|
||||
return <div className="flex gap-2 items-center">No linked system</div>;
|
||||
const { value } = option;
|
||||
if (value == null) {
|
||||
return <div className="flex gap-2 items-center">- Unknown -</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex gap-2 items-center">
|
||||
<SystemView systemId={option.value} className={classes.SystemView} />
|
||||
<SystemView systemId={value} className={classes.SystemView} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -65,6 +66,7 @@ export const SignatureLeadsToSelect = ({ name, defaultValue = '' }: SignatureLea
|
||||
const leadsToOptions = useMemo(() => {
|
||||
return [
|
||||
{ value: null },
|
||||
|
||||
...leadsTo
|
||||
.filter(systemId => {
|
||||
const systemStatic = systemStatics.get(parseInt(systemId));
|
||||
|
||||
@@ -17,7 +17,17 @@ const getPossibleWormholes = (systemStatic: SolarSystemStaticInfoRaw, wormholes:
|
||||
|
||||
// @ts-ignore
|
||||
const spawnClassGroup = SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS[whType];
|
||||
const possibleWHTypes = wormholes.filter(x => x.src.includes(spawnClassGroup));
|
||||
const possibleWHTypes = wormholes.filter(x => {
|
||||
return x.src.some(x => {
|
||||
const [group, type] = x.split('-');
|
||||
|
||||
if (type === 'shattered') {
|
||||
return systemStatic.is_shattered && group === spawnClassGroup;
|
||||
}
|
||||
|
||||
return group === spawnClassGroup;
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
statics: possibleWHTypes
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './SignatureGroupSelect';
|
||||
export * from './SignatureGroupContent';
|
||||
export * from './SignatureK162TypeSelect';
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Map } from '@/hooks/Mapper/components/map/Map.tsx';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { OutCommand, OutCommandHandler, SolarSystemConnection } from '@/hooks/Mapper/types';
|
||||
import { MapRootData, useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts';
|
||||
import { OnMapAddSystemCallback, OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts';
|
||||
import isEqual from 'lodash.isequal';
|
||||
import { ContextMenuSystem, useContextMenuSystemHandlers } from '@/hooks/Mapper/components/contexts';
|
||||
import {
|
||||
@@ -14,14 +14,18 @@ import classes from './MapWrapper.module.scss';
|
||||
import { Connections } from '@/hooks/Mapper/components/mapRootContent/components/Connections';
|
||||
import { ContextMenuSystemMultiple, useContextMenuSystemMultipleHandlers } from '../contexts/ContextMenuSystemMultiple';
|
||||
import { getSystemById } from '@/hooks/Mapper/helpers';
|
||||
import { Node } from 'reactflow';
|
||||
import { Node, XYPosition } from 'reactflow';
|
||||
|
||||
import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { useMapEventListener } from '@/hooks/Mapper/events';
|
||||
import { emitMapEvent, useMapEventListener } from '@/hooks/Mapper/events';
|
||||
|
||||
import { STORED_INTERFACE_DEFAULT_VALUES } from '@/hooks/Mapper/mapRootProvider/MapRootProvider';
|
||||
import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks';
|
||||
import { useCommonMapEventProcessor } from '@/hooks/Mapper/components/mapWrapper/hooks/useCommonMapEventProcessor.ts';
|
||||
import {
|
||||
AddSystemDialog,
|
||||
SearchOnSubmitCallback,
|
||||
} from '@/hooks/Mapper/components/mapInterface/components/AddSystemDialog';
|
||||
|
||||
// TODO: INFO - this component needs for abstract work with Map instance
|
||||
export const MapWrapper = () => {
|
||||
@@ -34,6 +38,9 @@ export const MapWrapper = () => {
|
||||
isShowMinimap = STORED_INTERFACE_DEFAULT_VALUES.isShowMinimap,
|
||||
isShowKSpace,
|
||||
isThickConnections,
|
||||
isShowBackgroundPattern,
|
||||
isSoftBackground,
|
||||
theme,
|
||||
},
|
||||
} = useMapRootState();
|
||||
const { deleteSystems } = useDeleteSystems();
|
||||
@@ -45,6 +52,7 @@ export const MapWrapper = () => {
|
||||
const [openSettings, setOpenSettings] = useState<string | null>(null);
|
||||
const [openLinkSignatures, setOpenLinkSignatures] = useState<any | null>(null);
|
||||
const [openCustomLabel, setOpenCustomLabel] = useState<string | null>(null);
|
||||
const [openAddSystem, setOpenAddSystem] = useState<XYPosition | null>(null);
|
||||
const [selectedConnection, setSelectedConnection] = useState<SolarSystemConnection | null>(null);
|
||||
|
||||
const ref = useRef({ selectedConnections, selectedSystems, systemContextProps, systems, deleteSystems });
|
||||
@@ -121,6 +129,28 @@ export const MapWrapper = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onAddSystem: OnMapAddSystemCallback = useCallback(({ coordinates }) => {
|
||||
setOpenAddSystem(coordinates);
|
||||
}, []);
|
||||
|
||||
const handleSubmitAddSystem: SearchOnSubmitCallback = useCallback(
|
||||
async item => {
|
||||
if (ref.current.systems.some(x => x.system_static_info.solar_system_id === item.value)) {
|
||||
emitMapEvent({
|
||||
name: Commands.centerSystem,
|
||||
data: item.value.toString(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await outCommand({
|
||||
type: OutCommand.manualAddSystem,
|
||||
data: { coordinates: openAddSystem, solar_system_id: item.value },
|
||||
});
|
||||
},
|
||||
[openAddSystem, outCommand],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Map
|
||||
@@ -135,6 +165,10 @@ export const MapWrapper = () => {
|
||||
showKSpaceBG={isShowKSpace}
|
||||
onManualDelete={handleManualDelete}
|
||||
isThickConnections={isThickConnections}
|
||||
isShowBackgroundPattern={isShowBackgroundPattern}
|
||||
isSoftBackground={isSoftBackground}
|
||||
theme={theme}
|
||||
onAddSystem={onAddSystem}
|
||||
/>
|
||||
|
||||
{openSettings != null && (
|
||||
@@ -149,6 +183,12 @@ export const MapWrapper = () => {
|
||||
<SystemLinkSignatureDialog data={openLinkSignatures} setVisible={() => setOpenLinkSignatures(null)} />
|
||||
)}
|
||||
|
||||
<AddSystemDialog
|
||||
visible={!!openAddSystem}
|
||||
setVisible={() => setOpenAddSystem(null)}
|
||||
onSubmit={handleSubmitAddSystem}
|
||||
/>
|
||||
|
||||
<Connections selectedConnection={selectedConnection} onHide={() => setSelectedConnection(null)} />
|
||||
|
||||
<ContextMenuSystem
|
||||
|
||||
@@ -18,7 +18,7 @@ const Topbar = ({ children }: WithChildren) => {
|
||||
<nav
|
||||
className={clsx(
|
||||
'px-2 flex items-center justify-center min-w-0 h-12 pointer-events-auto',
|
||||
'border-b border-gray-900 bg-gray-800 bg-opacity-5',
|
||||
'border-b border-stone-800 bg-gray-800 bg-opacity-5',
|
||||
'bg-opacity-70 bg-neutral-900',
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -3,15 +3,23 @@ import { SystemViewStandalone } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts';
|
||||
import { useMemo } from 'react';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { SolarSystemStaticInfoRaw } from '@/hooks/Mapper/types';
|
||||
|
||||
export type SystemViewProps = {
|
||||
systemId: string;
|
||||
systemInfo?: SolarSystemStaticInfoRaw;
|
||||
hideRegion?: boolean;
|
||||
useSystemsCache?: boolean;
|
||||
showCustomName?: boolean;
|
||||
} & WithClassName;
|
||||
|
||||
export const SystemView = ({ systemId, hideRegion, className, showCustomName }: SystemViewProps) => {
|
||||
export const SystemView = ({
|
||||
systemId,
|
||||
systemInfo: customSystemInfo,
|
||||
hideRegion,
|
||||
className,
|
||||
showCustomName,
|
||||
}: SystemViewProps) => {
|
||||
const memSystems = useMemo(() => [systemId], [systemId]);
|
||||
const { systems, loading } = useLoadSystemStatic({ systems: memSystems });
|
||||
|
||||
@@ -20,9 +28,12 @@ export const SystemView = ({ systemId, hideRegion, className, showCustomName }:
|
||||
} = useMapRootState();
|
||||
|
||||
const systemInfo = useMemo(() => {
|
||||
if (!systemId) {
|
||||
return customSystemInfo;
|
||||
}
|
||||
return systems.get(parseInt(systemId));
|
||||
// eslint-disable-next-line
|
||||
}, [systemId, systems, loading]);
|
||||
}, [customSystemInfo, systemId, systems, loading]);
|
||||
|
||||
const mapSystemInfo = useMemo(() => {
|
||||
if (!showCustomName) {
|
||||
|
||||
@@ -25,7 +25,6 @@ export const SystemViewStandalone = ({
|
||||
className,
|
||||
hideRegion,
|
||||
customName,
|
||||
|
||||
class_title,
|
||||
system_class,
|
||||
solar_system_name,
|
||||
@@ -38,6 +37,7 @@ export const SystemViewStandalone = ({
|
||||
...props
|
||||
}: SystemViewStandaloneProps) => {
|
||||
const classTitleColor = getSystemClassStyles({ systemClass: system_class, security });
|
||||
|
||||
const isWH = isWormholeSpace(system_class);
|
||||
|
||||
const handleClick = useCallback(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
.WHClassViewRoot {
|
||||
|
||||
}
|
||||
|
||||
.WHClassViewContent {
|
||||
|
||||
@@ -18,7 +18,9 @@ export interface WHClassViewProps {
|
||||
whClassName: string;
|
||||
noOffset?: boolean;
|
||||
useShortTitle?: boolean;
|
||||
hideTooltip?: boolean;
|
||||
hideWhClass?: boolean;
|
||||
hideWhClassName?: boolean;
|
||||
highlightName?: boolean;
|
||||
className?: string;
|
||||
classNameWh?: string;
|
||||
@@ -28,7 +30,9 @@ export const WHClassView = ({
|
||||
whClassName,
|
||||
noOffset,
|
||||
useShortTitle,
|
||||
hideTooltip,
|
||||
hideWhClass,
|
||||
hideWhClassName,
|
||||
highlightName,
|
||||
className,
|
||||
classNameWh,
|
||||
@@ -45,25 +49,27 @@ export const WHClassView = ({
|
||||
|
||||
return (
|
||||
<div className={clsx(classes.WHClassViewRoot, className)}>
|
||||
<Tooltip
|
||||
target={`.wh-name${whClassName}${uid}`}
|
||||
position="right"
|
||||
mouseTrack
|
||||
mouseTrackLeft={20}
|
||||
mouseTrackTop={30}
|
||||
className="border border-green-300 rounded border-opacity-10 bg-stone-900 bg-opacity-90 "
|
||||
>
|
||||
<div className="flex gap-3">
|
||||
<div className="flex flex-col gap-1">
|
||||
<InfoDrawer title="Total mass">{prepareMass(whData.total_mass)}</InfoDrawer>
|
||||
<InfoDrawer title="Jump mass">{prepareMass(whData.max_mass_per_jump)}</InfoDrawer>
|
||||
{!hideTooltip && (
|
||||
<Tooltip
|
||||
target={`.wh-name${whClassName}${uid}`}
|
||||
position="right"
|
||||
mouseTrack
|
||||
mouseTrackLeft={20}
|
||||
mouseTrackTop={30}
|
||||
className="border border-green-300 rounded border-opacity-10 bg-stone-900 bg-opacity-90 "
|
||||
>
|
||||
<div className="flex gap-3">
|
||||
<div className="flex flex-col gap-1">
|
||||
<InfoDrawer title="Total mass">{prepareMass(whData.total_mass)}</InfoDrawer>
|
||||
<InfoDrawer title="Jump mass">{prepareMass(whData.max_mass_per_jump)}</InfoDrawer>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<InfoDrawer title="Lifetime">{whData.lifetime}h</InfoDrawer>
|
||||
<InfoDrawer title="Mass regen">{prepareMass(whData.mass_regen)}</InfoDrawer>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<InfoDrawer title="Lifetime">{whData.lifetime}h</InfoDrawer>
|
||||
<InfoDrawer title="Mass regen">{prepareMass(whData.mass_regen)}</InfoDrawer>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={clsx(
|
||||
@@ -73,7 +79,7 @@ export const WHClassView = ({
|
||||
`wh-name${whClassName}${uid}`,
|
||||
)}
|
||||
>
|
||||
<span className={clsx({ [whClassStyle]: highlightName })}>{whClassName}</span>
|
||||
{!hideWhClassName && <span className={clsx({ [whClassStyle]: highlightName })}>{whClassName}</span>}
|
||||
{!hideWhClass && whClass && (
|
||||
<span className={clsx(classes.WHClassName, whClassStyle, classNameWh)}>
|
||||
{useShortTitle ? whClass.shortTitle : whClass.shortName}
|
||||
|
||||
@@ -31,12 +31,15 @@ const prepareEffects = (effects: Record<string, EffectRaw>, effectName: string,
|
||||
return out;
|
||||
};
|
||||
|
||||
let counter = 0;
|
||||
|
||||
export interface WHEffectViewProps {
|
||||
effectName: string;
|
||||
effectPower: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const WHEffectView = ({ effectName, effectPower }: WHEffectViewProps) => {
|
||||
export const WHEffectView = ({ effectName, effectPower, className }: WHEffectViewProps) => {
|
||||
const {
|
||||
data: { effects },
|
||||
} = useMapRootState();
|
||||
@@ -49,7 +52,7 @@ export const WHEffectView = ({ effectName, effectPower }: WHEffectViewProps) =>
|
||||
[effectName, effectPower, effects],
|
||||
);
|
||||
|
||||
const targetClass = `wh-effect-name${effectInfo.id}`;
|
||||
const targetClass = useMemo(() => `wh-effect-name${effectInfo.id}-${counter++}`, []);
|
||||
|
||||
return (
|
||||
<div className={classes.WHEffectViewRoot}>
|
||||
@@ -84,7 +87,7 @@ export const WHEffectView = ({ effectName, effectPower }: WHEffectViewProps) =>
|
||||
</div>
|
||||
</FixedTooltip>
|
||||
|
||||
<div className={clsx('font-bold select-none cursor-help w-min-content', effectClass, targetClass)}>
|
||||
<div className={clsx('font-bold select-none cursor-help w-min-content', effectClass, targetClass, className)}>
|
||||
{effectName}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { COSMIC_SIGNATURE } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignatureSettingsDialog';
|
||||
import { SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { SignatureGroup, SignatureKind, SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { MAPPING_TYPE_TO_ENG } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
|
||||
|
||||
export const parseSignatures = (value: string, availableKeys: string[]): SystemSignature[] => {
|
||||
const outArr: SystemSignature[] = [];
|
||||
@@ -14,10 +14,12 @@ export const parseSignatures = (value: string, availableKeys: string[]): SystemS
|
||||
continue;
|
||||
}
|
||||
|
||||
const kind = MAPPING_TYPE_TO_ENG[sigArrInfo[1] as SignatureKind];
|
||||
|
||||
outArr.push({
|
||||
eve_id: sigArrInfo[0],
|
||||
kind: availableKeys.includes(sigArrInfo[1]) ? sigArrInfo[1] : COSMIC_SIGNATURE,
|
||||
group: sigArrInfo[2],
|
||||
kind: availableKeys.includes(kind) ? kind : SignatureKind.CosmicSignature,
|
||||
group: sigArrInfo[2] as SignatureGroup,
|
||||
name: sigArrInfo[3],
|
||||
type: '',
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
|
||||
export const useClipboard = () => {
|
||||
const [clipboardContent, setClipboardContent] = useState<string | null>(null);
|
||||
const [clipboardContent, setClipboardContent] = useState<{ text: string } | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const getClipboardContent = useCallback(async () => {
|
||||
try {
|
||||
const text = await navigator.clipboard.readText();
|
||||
setClipboardContent(text);
|
||||
setClipboardContent({ text });
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError('Failed to read clipboard content.');
|
||||
@@ -18,7 +18,7 @@ export const useClipboard = () => {
|
||||
const handlePaste = (event: ClipboardEvent) => {
|
||||
const text = event.clipboardData?.getData('text');
|
||||
if (text) {
|
||||
setClipboardContent(text);
|
||||
setClipboardContent({ text });
|
||||
setError(null);
|
||||
}
|
||||
};
|
||||
@@ -30,5 +30,5 @@ export const useClipboard = () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { clipboardContent, error, getClipboardContent };
|
||||
return { clipboardContent, error, getClipboardContent, setClipboardContent };
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const useHotkey = (isMetaKey: boolean, hotkeys: string[], callback: () => void) => {
|
||||
export const useHotkey = (isMetaKey: boolean, hotkeys: string[], callback: (e: KeyboardEvent) => void) => {
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if ((!isMetaKey || event.ctrlKey || event.metaKey) && hotkeys.includes(event.key)) {
|
||||
@@ -8,14 +8,14 @@ export const useHotkey = (isMetaKey: boolean, hotkeys: string[], callback: () =>
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
callback();
|
||||
callback(event);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
window.addEventListener('keydown', handleKeyDown, { capture: true });
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
window.removeEventListener('keydown', handleKeyDown, { capture: true });
|
||||
};
|
||||
}, [isMetaKey, hotkeys, callback]);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ContextStoreDataUpdate, useContextStore } from '@/hooks/Mapper/utils';
|
||||
import { createContext, Dispatch, ForwardedRef, forwardRef, RefObject, SetStateAction, useContext } from 'react';
|
||||
import { MapHandlers, MapUnionTypes, OutCommandHandler, SolarSystemConnection } from '@/hooks/Mapper/types';
|
||||
import { createContext, Dispatch, ForwardedRef, forwardRef, SetStateAction, useContext } from 'react';
|
||||
import { MapUnionTypes, OutCommandHandler, SolarSystemConnection } from '@/hooks/Mapper/types';
|
||||
import { useMapRootHandlers } from '@/hooks/Mapper/mapRootProvider/hooks';
|
||||
import { WithChildren } from '@/hooks/Mapper/types/common.ts';
|
||||
import useLocalStorageState from 'use-local-storage-state';
|
||||
@@ -25,6 +25,8 @@ const INITIAL_DATA: MapRootData = {
|
||||
|
||||
selectedSystems: [],
|
||||
selectedConnections: [],
|
||||
userPermissions: {},
|
||||
options: {},
|
||||
};
|
||||
|
||||
export enum InterfaceStoredSettingsProps {
|
||||
@@ -32,6 +34,10 @@ export enum InterfaceStoredSettingsProps {
|
||||
isShowMinimap = 'isShowMinimap',
|
||||
isShowKSpace = 'isShowKSpace',
|
||||
isThickConnections = 'isThickConnections',
|
||||
isShowUnsplashedSignatures = 'isShowUnsplashedSignatures',
|
||||
isShowBackgroundPattern = 'isShowBackgroundPattern',
|
||||
isSoftBackground = 'isSoftBackground',
|
||||
theme = 'theme',
|
||||
}
|
||||
|
||||
export type InterfaceStoredSettings = {
|
||||
@@ -39,6 +45,10 @@ export type InterfaceStoredSettings = {
|
||||
isShowMinimap: boolean;
|
||||
isShowKSpace: boolean;
|
||||
isThickConnections: boolean;
|
||||
isShowUnsplashedSignatures: boolean;
|
||||
isShowBackgroundPattern: boolean;
|
||||
isSoftBackground: boolean;
|
||||
theme: string;
|
||||
};
|
||||
|
||||
export const STORED_INTERFACE_DEFAULT_VALUES: InterfaceStoredSettings = {
|
||||
@@ -46,7 +56,11 @@ export const STORED_INTERFACE_DEFAULT_VALUES: InterfaceStoredSettings = {
|
||||
isShowMinimap: true,
|
||||
isShowKSpace: false,
|
||||
isThickConnections: false,
|
||||
};
|
||||
isShowUnsplashedSignatures: false,
|
||||
isShowBackgroundPattern: true,
|
||||
isSoftBackground: false,
|
||||
theme: 'neon',
|
||||
}
|
||||
|
||||
export interface MapRootContextProps {
|
||||
update: ContextStoreDataUpdate<MapRootData>;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export * from './useMapInit';
|
||||
export * from './useMapUpdated';
|
||||
export * from './useMapCheckPermissions';
|
||||
export * from './useMapGetOption';
|
||||
export * from './useRoutes';
|
||||
export * from './useCommandsConnections';
|
||||
export * from './useCommandsSystems';
|
||||
|
||||
@@ -2,53 +2,86 @@ import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { CommandAddSystems, CommandRemoveSystems, CommandUpdateSystems } from '@/hooks/Mapper/types';
|
||||
import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts';
|
||||
|
||||
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { emitMapEvent } from '@/hooks/Mapper/events';
|
||||
import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
export const useCommandsSystems = () => {
|
||||
const {
|
||||
update,
|
||||
data: { systems },
|
||||
outCommand,
|
||||
} = useMapRootState();
|
||||
|
||||
const { addSystemStatic } = useLoadSystemStatic({ systems: [] });
|
||||
|
||||
const ref = useRef({ systems, update });
|
||||
ref.current = { systems, update };
|
||||
const ref = useRef({ systems, update, addSystemStatic });
|
||||
ref.current = { systems, update, addSystemStatic };
|
||||
|
||||
const addSystems = useCallback(
|
||||
(addSystems: CommandAddSystems) => {
|
||||
addSystems.forEach(sys => {
|
||||
const addSystems = useCallback((systemsToAdd: CommandAddSystems) => {
|
||||
const { update, addSystemStatic, systems } = ref.current;
|
||||
|
||||
systemsToAdd.forEach(sys => {
|
||||
if (sys.system_static_info) {
|
||||
addSystemStatic(sys.system_static_info);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
update({
|
||||
systems: [...ref.current.systems.filter(sys => !addSystems.some(x => sys.id === x.id)), ...addSystems],
|
||||
});
|
||||
},
|
||||
[addSystemStatic, update],
|
||||
);
|
||||
update(
|
||||
{
|
||||
systems: [...systems.filter(sys => !systemsToAdd.some(x => sys.id === x.id)), ...systemsToAdd],
|
||||
},
|
||||
true,
|
||||
);
|
||||
}, []);
|
||||
|
||||
const removeSystems = useCallback((toRemove: CommandRemoveSystems) => {
|
||||
const { update, systems } = ref.current;
|
||||
update({
|
||||
systems: systems.filter(x => !toRemove.includes(parseInt(x.id))),
|
||||
});
|
||||
update(
|
||||
{
|
||||
systems: systems.filter(x => !toRemove.includes(parseInt(x.id))),
|
||||
},
|
||||
true,
|
||||
);
|
||||
}, []);
|
||||
|
||||
const updateSystems = useCallback(
|
||||
(systems: CommandUpdateSystems) => {
|
||||
const out = ref.current.systems.map(current => {
|
||||
const newSystem = systems.find(x => current.id === x.id);
|
||||
if (!newSystem) {
|
||||
return current;
|
||||
}
|
||||
const updateSystems = useCallback((updatedSystems: CommandUpdateSystems) => {
|
||||
const { update, systems } = ref.current;
|
||||
|
||||
return newSystem;
|
||||
const out = systems.map(current => {
|
||||
const newSystem = updatedSystems.find(x => current.id === x.id);
|
||||
if (!newSystem) {
|
||||
return current;
|
||||
}
|
||||
|
||||
return newSystem;
|
||||
});
|
||||
|
||||
update({ systems: out }, true);
|
||||
}, []);
|
||||
|
||||
const updateSystemSignatures = useCallback(
|
||||
async (systemId: string) => {
|
||||
const { update, systems } = ref.current;
|
||||
|
||||
const { signatures } = await outCommand({
|
||||
type: OutCommand.getSignatures,
|
||||
data: { system_id: `${systemId}` },
|
||||
});
|
||||
|
||||
update({ systems: out });
|
||||
const out = systems.map(current => {
|
||||
if (current.id === `${systemId}`) {
|
||||
return { ...current, system_signatures: signatures };
|
||||
}
|
||||
|
||||
return current;
|
||||
});
|
||||
|
||||
update({ systems: out }, true);
|
||||
|
||||
emitMapEvent({ name: Commands.updateSystems, data: out });
|
||||
},
|
||||
[update],
|
||||
[outCommand],
|
||||
);
|
||||
|
||||
return { addSystems, removeSystems, updateSystems };
|
||||
return { addSystems, removeSystems, updateSystems, updateSystemSignatures };
|
||||
};
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
|
||||
|
||||
export const useMapCheckPermissions = (permissions: UserPermission[]) => {
|
||||
const {
|
||||
data: { userPermissions },
|
||||
} = useMapRootState();
|
||||
|
||||
return useMemo(() => permissions.every(x => userPermissions[x]), [permissions, userPermissions]);
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
|
||||
export const useMapGetOption = (option: string) => {
|
||||
const {
|
||||
data: { options },
|
||||
} = useMapRootState();
|
||||
|
||||
return useMemo(() => options[option], [option, options]);
|
||||
};
|
||||
@@ -19,6 +19,8 @@ export const useMapInit = () => {
|
||||
user_characters,
|
||||
present_characters,
|
||||
hubs,
|
||||
user_permissions,
|
||||
options,
|
||||
}: CommandInit) => {
|
||||
const updateData: Partial<MapRootData> = {};
|
||||
|
||||
@@ -51,10 +53,18 @@ export const useMapInit = () => {
|
||||
updateData.connections = connections;
|
||||
}
|
||||
|
||||
if (user_permissions) {
|
||||
updateData.userPermissions = user_permissions;
|
||||
}
|
||||
|
||||
if (hubs) {
|
||||
updateData.hubs = hubs;
|
||||
}
|
||||
|
||||
if (options) {
|
||||
updateData.options = options;
|
||||
}
|
||||
|
||||
if (system_static_infos) {
|
||||
system_static_infos.forEach(static_info => {
|
||||
addSystemStatic(static_info);
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
CommandRemoveSystems,
|
||||
CommandRoutes,
|
||||
Commands,
|
||||
CommandSignaturesUpdated,
|
||||
CommandUpdateConnection,
|
||||
CommandUpdateSystems,
|
||||
MapHandlers,
|
||||
@@ -31,7 +32,7 @@ import { emitMapEvent } from '@/hooks/Mapper/events';
|
||||
|
||||
export const useMapRootHandlers = (ref: ForwardedRef<MapHandlers>) => {
|
||||
const mapInit = useMapInit();
|
||||
const { addSystems, removeSystems, updateSystems } = useCommandsSystems();
|
||||
const { addSystems, removeSystems, updateSystems, updateSystemSignatures } = useCommandsSystems();
|
||||
const { addConnections, removeConnections, updateConnection } = useCommandsConnections();
|
||||
const { charactersUpdated, characterAdded, characterRemoved, characterUpdated, presentCharacters } =
|
||||
useCommandsCharacters();
|
||||
@@ -87,6 +88,14 @@ export const useMapRootHandlers = (ref: ForwardedRef<MapHandlers>) => {
|
||||
mapRoutes(data as CommandRoutes);
|
||||
break;
|
||||
|
||||
case Commands.signaturesUpdated: // USED
|
||||
updateSystemSignatures(data as CommandSignaturesUpdated);
|
||||
break;
|
||||
|
||||
case Commands.linkSignatureToSystem: // USED
|
||||
// do nothing here
|
||||
break;
|
||||
|
||||
case Commands.centerSystem: // USED
|
||||
// do nothing here
|
||||
break;
|
||||
@@ -95,22 +104,10 @@ export const useMapRootHandlers = (ref: ForwardedRef<MapHandlers>) => {
|
||||
// do nothing here
|
||||
break;
|
||||
|
||||
// case Commands.linkSignatureToSystem:
|
||||
// // TODO command data type lost
|
||||
// // @ts-ignore
|
||||
// emitMapEvent({ name: Commands.linkSignatureToSystem, data });
|
||||
// break;
|
||||
|
||||
case Commands.killsUpdated:
|
||||
// do nothing here
|
||||
break;
|
||||
|
||||
// case Commands.signaturesUpdated:
|
||||
// // TODO command data type lost
|
||||
// // @ts-ignore
|
||||
// emitMapEvent({ name: Commands.signaturesUpdated, data });
|
||||
// break;
|
||||
|
||||
default:
|
||||
console.warn(`JOipP Interface handlers: Unknown command: ${type}`, data);
|
||||
break;
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
export enum ConnectionType {
|
||||
wormhole,
|
||||
gate,
|
||||
}
|
||||
|
||||
export enum MassState {
|
||||
normal,
|
||||
half,
|
||||
@@ -9,16 +14,12 @@ export enum TimeStatus {
|
||||
eol,
|
||||
}
|
||||
|
||||
// export enum ShipSizeStatus {
|
||||
// small, // frigates, destroyers - less than 5K t
|
||||
// medium, // less than 20K t
|
||||
// large, // less than 375K t
|
||||
// capital, // less than 1.8M t
|
||||
// }
|
||||
|
||||
export enum ShipSizeStatus {
|
||||
small, // frigates, destroyers - less than 5K t
|
||||
normal,
|
||||
small = 0, // frigates, destroyers - less than 5K t
|
||||
medium = 1, // less than 62K t
|
||||
large = 2, // less than 375K t
|
||||
freight = 3, // less than 1M t
|
||||
capital = 4, // less than 1.8M t
|
||||
}
|
||||
|
||||
export type SolarSystemConnection = {
|
||||
@@ -32,4 +33,6 @@ export type SolarSystemConnection = {
|
||||
|
||||
source: string;
|
||||
target: string;
|
||||
|
||||
type?: ConnectionType;
|
||||
};
|
||||
|
||||
@@ -6,3 +6,4 @@ export * from './system';
|
||||
export * from './mapUnionTypes';
|
||||
export * from './signatures';
|
||||
export * from './connectionPassages';
|
||||
export * from './permissions';
|
||||
|
||||
@@ -4,6 +4,7 @@ import { WormholeDataRaw } from '@/hooks/Mapper/types/wormholes.ts';
|
||||
import { CharacterTypeRaw } from '@/hooks/Mapper/types/character.ts';
|
||||
import { RoutesList } from '@/hooks/Mapper/types/routes.ts';
|
||||
import { Kill } from '@/hooks/Mapper/types/kills.ts';
|
||||
import { UserPermissions } from '@/hooks/Mapper/types';
|
||||
|
||||
export enum Commands {
|
||||
init = 'init',
|
||||
@@ -58,9 +59,10 @@ export type CommandInit = {
|
||||
characters: CharacterTypeRaw[];
|
||||
present_characters: string[];
|
||||
user_characters: string[];
|
||||
user_permissions: any;
|
||||
user_permissions: UserPermissions;
|
||||
hubs: string[];
|
||||
routes: RoutesList;
|
||||
options: Record<string, string | boolean>;
|
||||
reset?: boolean;
|
||||
};
|
||||
export type CommandAddSystems = SolarSystemRawType[];
|
||||
@@ -120,12 +122,14 @@ export enum OutCommand {
|
||||
getSystemStaticInfos = 'get_system_static_infos',
|
||||
getConnectionInfo = 'get_connection_info',
|
||||
updateConnectionTimeStatus = 'update_connection_time_status',
|
||||
updateConnectionType = 'update_connection_type',
|
||||
updateConnectionMassStatus = 'update_connection_mass_status',
|
||||
updateConnectionShipSizeType = 'update_connection_ship_size_type',
|
||||
updateConnectionLocked = 'update_connection_locked',
|
||||
updateConnectionCustomInfo = 'update_connection_custom_info',
|
||||
updateSignatures = 'update_signatures',
|
||||
updateSystemName = 'update_system_name',
|
||||
updateSystemTemporaryName = 'update_system_temporary_name',
|
||||
updateSystemDescription = 'update_system_description',
|
||||
updateSystemLabels = 'update_system_labels',
|
||||
updateSystemLocked = 'update_system_locked',
|
||||
@@ -150,6 +154,7 @@ export enum OutCommand {
|
||||
getUserSettings = 'get_user_settings',
|
||||
updateUserSettings = 'update_user_settings',
|
||||
unlinkSignature = 'unlink_signature',
|
||||
searchSystems = 'search_systems',
|
||||
}
|
||||
|
||||
export type OutCommandHandler = <T = any>(event: { type: OutCommand; data: any }) => Promise<T>;
|
||||
|
||||
@@ -4,6 +4,7 @@ import { CharacterTypeRaw } from '@/hooks/Mapper/types/character.ts';
|
||||
import { SolarSystemRawType } from '@/hooks/Mapper/types/system.ts';
|
||||
import { RoutesList } from '@/hooks/Mapper/types/routes.ts';
|
||||
import { SolarSystemConnection } from '@/hooks/Mapper/types/connection.ts';
|
||||
import { UserPermissions } from '@/hooks/Mapper/types';
|
||||
|
||||
export type MapUnionTypes = {
|
||||
wormholesData: Record<string, WormholeDataRaw>;
|
||||
@@ -17,4 +18,6 @@ export type MapUnionTypes = {
|
||||
routes?: RoutesList;
|
||||
kills: Record<number, number>;
|
||||
connections: SolarSystemConnection[];
|
||||
userPermissions: Partial<UserPermissions>;
|
||||
options: Record<string, string | boolean>;
|
||||
};
|
||||
|
||||
19
assets/js/hooks/Mapper/types/permissions.ts
Normal file
19
assets/js/hooks/Mapper/types/permissions.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export enum UserPermission {
|
||||
ADMIN_MAP = 'admin_map',
|
||||
MANAGE_MAP = 'manage_map',
|
||||
VIEW_SYSTEM = 'view_system',
|
||||
VIEW_CHARACTER = 'view_character',
|
||||
VIEW_CONNECTION = 'view_connection',
|
||||
ADD_SYSTEM = 'add_system',
|
||||
ADD_CONNECTION = 'add_connection',
|
||||
UPDATE_SYSTEM = 'update_system',
|
||||
TRACK_CHARACTER = 'track_character',
|
||||
DELETE_CONNECTION = 'delete_connection',
|
||||
DELETE_SYSTEM = 'delete_system',
|
||||
LOCK_SYSTEM = 'lock_system',
|
||||
ADD_ACL = 'add_acl',
|
||||
DELETE_ACL = 'delete_acl',
|
||||
DELETE_MAP = 'delete_map',
|
||||
}
|
||||
|
||||
export type UserPermissions = Record<UserPermission, boolean>;
|
||||
@@ -10,6 +10,15 @@ export enum SignatureGroup {
|
||||
CombatSite = 'Combat Site',
|
||||
}
|
||||
|
||||
export enum SignatureKind {
|
||||
CosmicSignature = 'Cosmic Signature',
|
||||
CosmicAnomaly = 'Cosmic Anomaly',
|
||||
Structure = 'Structure',
|
||||
Ship = 'Ship',
|
||||
Deployable = 'Deployable',
|
||||
Drone = 'Drone',
|
||||
}
|
||||
|
||||
export type GroupType = {
|
||||
id: string;
|
||||
icon: string;
|
||||
@@ -19,12 +28,52 @@ export type GroupType = {
|
||||
|
||||
export type SystemSignature = {
|
||||
eve_id: string;
|
||||
kind: string;
|
||||
kind: SignatureKind;
|
||||
name: string;
|
||||
custom_info?: string;
|
||||
description?: string;
|
||||
group: SignatureGroup;
|
||||
type: string;
|
||||
k162Type?: string;
|
||||
linked_system?: SolarSystemStaticInfoRaw;
|
||||
inserted_at?: string;
|
||||
updated_at?: string;
|
||||
};
|
||||
|
||||
export enum SignatureKindENG {
|
||||
CosmicSignature = 'Cosmic Signature',
|
||||
CosmicAnomaly = 'Cosmic Anomaly',
|
||||
Structure = 'Structure',
|
||||
Ship = 'Ship',
|
||||
Deployable = 'Deployable',
|
||||
Drone = 'Drone',
|
||||
}
|
||||
|
||||
export enum SignatureKindRU {
|
||||
CosmicSignature = 'Скрытый сигнал',
|
||||
CosmicAnomaly = 'Космическая аномалия',
|
||||
Structure = 'Сооружение',
|
||||
Ship = 'Корабль',
|
||||
Deployable = 'Полевые блоки',
|
||||
Drone = 'Дрон',
|
||||
}
|
||||
|
||||
export enum SignatureGroupENG {
|
||||
CosmicSignature = 'Cosmic Signature',
|
||||
Wormhole = 'Wormhole',
|
||||
GasSite = 'Gas Site',
|
||||
RelicSite = 'Relic Site',
|
||||
DataSite = 'Data Site',
|
||||
OreSite = 'Ore Site',
|
||||
CombatSite = 'Combat Site',
|
||||
}
|
||||
|
||||
export enum SignatureGroupRU {
|
||||
CosmicSignature = 'Скрытый сигнал',
|
||||
Wormhole = 'Червоточина',
|
||||
GasSite = 'Газовый район',
|
||||
RelicSite = 'Археологический район',
|
||||
DataSite = 'Информационный район',
|
||||
OreSite = 'Астероидный район',
|
||||
CombatSite = 'Боевой район',
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { XYPosition } from 'reactflow';
|
||||
|
||||
import { SystemSignature } from '@/hooks/Mapper/types/signatures';
|
||||
|
||||
export enum SolarSystemStaticInfoRawNames {
|
||||
regionId = 'region_id',
|
||||
constellationId = 'constellation_id',
|
||||
@@ -114,6 +116,18 @@ export type SolarSystemRawType = {
|
||||
tag: string | null;
|
||||
status: number;
|
||||
name: string | null;
|
||||
temporary_name: string | null;
|
||||
|
||||
system_static_info: SolarSystemStaticInfoRaw;
|
||||
system_signatures: SystemSignature[];
|
||||
};
|
||||
|
||||
export type SearchSystemItem = {
|
||||
class_title: string;
|
||||
constellation_name: string;
|
||||
label: string;
|
||||
region_name: string;
|
||||
system_static_info: SolarSystemStaticInfoRaw;
|
||||
value: number;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
export default {
|
||||
mounted() {
|
||||
const hook = this;
|
||||
const url = hook.el.dataset.url;
|
||||
const button = hook.el;
|
||||
const button = this.el;
|
||||
|
||||
button.addEventListener('click', function () {
|
||||
// Get the URL from the data attribute
|
||||
|
||||
button.classList.remove('copied');
|
||||
|
||||
// Copy the URL to the clipboard
|
||||
navigator.clipboard
|
||||
.writeText(url)
|
||||
.writeText(button.dataset.url)
|
||||
.then(() => {
|
||||
button.classList.add('copied');
|
||||
})
|
||||
|
||||
@@ -8,6 +8,7 @@ import CopyToClipboard from './copyToClipboard';
|
||||
import DownloadJson from './downloadJson';
|
||||
import NewVersionUpdate from './newVersionUpdate';
|
||||
import MapAction from './maps/mapAction';
|
||||
import ShowCharactersAddAlert from './showCharactersAddAlert';
|
||||
|
||||
export default {
|
||||
DownloadJson,
|
||||
@@ -20,4 +21,5 @@ export default {
|
||||
Ping,
|
||||
CopyToClipboard,
|
||||
NewVersionUpdate,
|
||||
ShowCharactersAddAlert,
|
||||
};
|
||||
|
||||
@@ -4,13 +4,26 @@ export default {
|
||||
mounted() {
|
||||
const hook = this;
|
||||
|
||||
const button = hook.el.querySelector('.update-button');
|
||||
const refreshZone = hook.el.querySelector('#refresh-area');
|
||||
|
||||
button.addEventListener('click', function () {
|
||||
const lastVersion = hook.el.dataset.version;
|
||||
localStorage.setItem(LAST_VERSION_KEY, lastVersion);
|
||||
window.location.reload();
|
||||
});
|
||||
const handleUpdate = function (e: Event) {
|
||||
const hexBricks = hook.el.querySelectorAll('.hex-brick');
|
||||
|
||||
// Add a new class to each element
|
||||
hexBricks.forEach(el => {
|
||||
el.classList.add('hex-brick--active');
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
const lastVersion = hook.el.dataset.version;
|
||||
localStorage.setItem(LAST_VERSION_KEY, lastVersion);
|
||||
|
||||
window.location.reload();
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
refreshZone.addEventListener('click', handleUpdate);
|
||||
refreshZone.addEventListener('mouseover', handleUpdate);
|
||||
|
||||
this.updated();
|
||||
},
|
||||
|
||||
11
assets/js/hooks/showCharactersAddAlert.ts
Normal file
11
assets/js/hooks/showCharactersAddAlert.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export default {
|
||||
mounted() {
|
||||
this.pushEvent('restore_show_characters_add_alert', {
|
||||
value: localStorage.getItem('wanderer:hide_characters_add_alert') !== 'true',
|
||||
});
|
||||
|
||||
document.getElementById('characters-add-alert-hide')?.addEventListener('click', e => {
|
||||
localStorage.setItem('wanderer:hide_characters_add_alert', 'true');
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -13,8 +13,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "0.7.0",
|
||||
"@react-rxjs/core": "^0.10.7",
|
||||
"@react-rxjs/utils": "^0.9.7",
|
||||
"@shopify/draggable": "^1.1.3",
|
||||
"clsx": "^2.1.1",
|
||||
"daisyui": "^4.11.1",
|
||||
@@ -34,7 +32,6 @@
|
||||
"react-hook-form": "^7.53.1",
|
||||
"react-usestateref": "^1.0.9",
|
||||
"reactflow": "^11.11.4",
|
||||
"rxjs": "^7.8.1",
|
||||
"tailwindcss": "^3.3.6",
|
||||
"topbar": "^3.0.0",
|
||||
"use-local-storage-state": "^19.3.1"
|
||||
|
||||
BIN
assets/static/images/news/01-05-map-public-api/generate-key.png
Normal file
BIN
assets/static/images/news/01-05-map-public-api/generate-key.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user