mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-11-25 10:34:42 +00:00
Compare commits
199 Commits
map-events
...
v1.37.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1149cecaf | ||
|
|
8f28d2be65 | ||
|
|
d758b54ef8 | ||
|
|
58293b4dc4 | ||
|
|
f2083f4256 | ||
|
|
6c7bd5804e | ||
|
|
483ae21e89 | ||
|
|
f734565844 | ||
|
|
8c718ba181 | ||
|
|
c8d8734601 | ||
|
|
5c757e8255 | ||
|
|
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 | ||
|
|
5ba21f5386 | ||
|
|
10eeae5295 | ||
|
|
a5bead15d0 | ||
|
|
0de674adde | ||
|
|
1db65965d0 | ||
|
|
bbed17f631 | ||
|
|
0af4a3a731 | ||
|
|
49d503705a | ||
|
|
c55dd7b8d9 | ||
|
|
7ddcab3537 | ||
|
|
040b46c345 | ||
|
|
cd11ab6775 | ||
|
|
a5d776f3b1 | ||
|
|
e02caf341d | ||
|
|
3c04caa67c | ||
|
|
e76b564cbf | ||
|
|
5b2de88c3d | ||
|
|
82080b232f | ||
|
|
666bc7af43 | ||
|
|
dc077d5a5b | ||
|
|
29c840c64a | ||
|
|
65e0f89f33 | ||
|
|
d3b9b36332 | ||
|
|
90bbf29ea1 | ||
|
|
57f73684e8 | ||
|
|
7833cdebb2 | ||
|
|
67a5ae2985 | ||
|
|
f58c52d26b | ||
|
|
41e7739461 | ||
|
|
332152b677 | ||
|
|
85b49fe1f0 | ||
|
|
e7924532be | ||
|
|
475d950ad6 | ||
|
|
e6cfb29c6f | ||
|
|
dee6e86db1 | ||
|
|
72f088331f | ||
|
|
bbf536d10e | ||
|
|
149ac98297 | ||
|
|
b90a2910c9 | ||
|
|
c4da8a3a8d | ||
|
|
3ca75583d2 | ||
|
|
5f4607ae6f | ||
|
|
d880c6873f | ||
|
|
937649b2ed | ||
|
|
78e912c886 | ||
|
|
696c7d2cd1 | ||
|
|
49c0cb026b |
@@ -1,12 +1,7 @@
|
|||||||
FROM elixir:1.16-otp-25
|
FROM elixir:1.17-otp-27
|
||||||
|
|
||||||
RUN apt update -yq
|
RUN apt install -yq curl gnupg
|
||||||
RUN apt install -yq curl gnupg mc inotify-tools
|
|
||||||
RUN apt --fix-broken install
|
RUN apt --fix-broken install
|
||||||
RUN apt remove -y nodejs nodejs-doc
|
|
||||||
RUN curl -sL https://deb.nodesource.com/setup_18.x | bash -
|
|
||||||
RUN apt install -y nodejs
|
|
||||||
RUN npm install --global yarn
|
|
||||||
|
|
||||||
RUN mix local.hex --force
|
RUN mix local.hex --force
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ version: "0.1"
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
image: postgres:14.3
|
image: postgres:13-alpine
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
@@ -10,13 +10,13 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
volumes:
|
volumes:
|
||||||
- db:/var/lib/postgresql/data
|
- db-new:/var/lib/postgresql/data
|
||||||
|
|
||||||
wanderer:
|
wanderer:
|
||||||
environment:
|
environment:
|
||||||
PORT: 8000
|
PORT: 8000
|
||||||
DB_HOST: db
|
DB_HOST: db
|
||||||
WEB_APP_URL: "http://localhost:4444"
|
WEB_APP_URL: "http://localhost:8000"
|
||||||
ERL_AFLAGS: "-kernel shell_history enabled"
|
ERL_AFLAGS: "-kernel shell_history enabled"
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
@@ -33,4 +33,4 @@ services:
|
|||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
elixir-artifacts: {}
|
elixir-artifacts: {}
|
||||||
db: {}
|
db-new: {}
|
||||||
|
|||||||
@@ -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 EVE_CLIENT_WITH_WALLET_SECRET="<EVE_CLIENT_WITH_WALLET_SECRET>"
|
||||||
export GIT_SHA="1111"
|
export GIT_SHA="1111"
|
||||||
export WANDERER_INVITES="false"
|
export WANDERER_INVITES="false"
|
||||||
|
export WANDERER_PUBLIC_API_DISABLED="false"
|
||||||
|
|||||||
2
.github/workflows/release_actions.yml
vendored
2
.github/workflows/release_actions.yml
vendored
@@ -18,4 +18,4 @@ jobs:
|
|||||||
key: ${{ secrets.SSH_KEY }}
|
key: ${{ secrets.SSH_KEY }}
|
||||||
port: ${{ secrets.PORT }}
|
port: ${{ secrets.PORT }}
|
||||||
script: |
|
script: |
|
||||||
/app/release/linux/deploy.sh ${{ github.event.release.tag_name }}
|
/home/wanderer/app/deploy.sh ${{ github.event.release.tag_name }}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
erlang 25.3
|
erlang 26.2.5.5
|
||||||
elixir 1.16-otp-25
|
elixir 1.17.3-otp-26
|
||||||
nodejs 18.0.0
|
nodejs 18.0.0
|
||||||
|
|||||||
1054
CHANGELOG.md
1054
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
18
README.md
18
README.md
@@ -20,11 +20,11 @@ Interested to learn more? [Check more on our website](https://wanderer.ltd/news)
|
|||||||
|
|
||||||
Wanderer is open source project and we have a free as in beer and self-hosted solution called [Wanderer Community Edition (CE)](https://wanderer.ltd/news/community-edition). Here are the differences between Wanderer and Wanderer CE:
|
Wanderer is open source project and we have a free as in beer and self-hosted solution called [Wanderer Community Edition (CE)](https://wanderer.ltd/news/community-edition). Here are the differences between Wanderer and Wanderer CE:
|
||||||
|
|
||||||
| | Wanderer Cloud | Wanderer Community Edition |
|
| | Wanderer Cloud | Wanderer Community Edition |
|
||||||
| ------------- | ------------- | ------------- |
|
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| **Infrastructure management** | Easy and convenient. It takes 2 minutes to register your character and create a map. We manage everything so you don’t have to worry about anything and can focus on gameplay. | You do it all yourself. You need to get a server and you need to manage your infrastructure. You are responsible for installation, maintenance, upgrades, server capacity, uptime, backup, security, stability, consistency, loading time and so on.|
|
| **Infrastructure management** | Easy and convenient. It takes 2 minutes to register your character and create a map. We manage everything so you don’t have to worry about anything and can focus on gameplay. | You do it all yourself. You need to get a server and you need to manage your infrastructure. You are responsible for installation, maintenance, upgrades, server capacity, uptime, backup, security, stability, consistency, loading time and so on. |
|
||||||
| **Release schedule** | Continuously developed and improved with new features and updates multiple times per week. | Latest features and improvements won't be immediately available.|
|
| **Release schedule** | Continuously developed and improved with new features and updates multiple times per week. | Latest features and improvements won't be immediately available. |
|
||||||
| **Server location** | All visitor data is exclusively processed on EU-owned cloud infrastructure. We keep your site data on a secure, encrypted and green energy powered server in Germany. This ensures that your site data is protected by the strict European Union data privacy laws and ensures compliance with GDPR. Your website data never leaves the EU. | You have full control and can host your instance on any server in any country that you wish. Host it on a server in your basement or host it with any cloud provider wherever you want, even those that are not GDPR compliant.|
|
| **Server location** | All visitor data is exclusively processed on EU-owned cloud infrastructure. We keep your site data on a secure, encrypted and green energy powered server in Germany. This ensures that your site data is protected by the strict European Union data privacy laws and ensures compliance with GDPR. Your website data never leaves the EU. | You have full control and can host your instance on any server in any country that you wish. Host it on a server in your basement or host it with any cloud provider wherever you want, even those that are not GDPR compliant. |
|
||||||
|
|
||||||
Interested in self-hosting Wanderer CE on your server? Take a look at our [Wanderer CE installation instructions](https://github.com/wanderer-industries/community-edition/).
|
Interested in self-hosting Wanderer CE on your server? Take a look at our [Wanderer CE installation instructions](https://github.com/wanderer-industries/community-edition/).
|
||||||
|
|
||||||
@@ -54,7 +54,13 @@ Now you can visit [`localhost:8000`](http://localhost:8000) from your browser.
|
|||||||
#### Using .devcontainer
|
#### Using .devcontainer
|
||||||
|
|
||||||
- Run devcontainer
|
- Run devcontainer
|
||||||
- See how to start server in #setup section
|
- Install additional dependencies inside Dev container
|
||||||
|
- `root@0d0a785313b6:/app# apt update`
|
||||||
|
- `root@0d0a785313b6:/app# curl -sL https://deb.nodesource.com/setup_18.x | bash -`
|
||||||
|
- `root@0d0a785313b6:/app# apt-get install nodejs inotify-tools -y`
|
||||||
|
- `root@0d0a785313b6:/app# mix setup`
|
||||||
|
|
||||||
|
- See how to run server in #Run section
|
||||||
|
|
||||||
#### Using nix flakes
|
#### Using nix flakes
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
@import 'primereact/resources/themes/arya-blue/theme.css' layer(primereact);
|
@import 'primereact/resources/themes/arya-blue/theme.css' layer(primereact);
|
||||||
/*@import 'primereact/resources/themes/bootstrap4-dark-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 {
|
@layer tailwind-base {
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
}
|
}
|
||||||
@@ -466,3 +468,467 @@ body {
|
|||||||
transform: rotate(-360deg);
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,11 +15,10 @@ const ErrorFallback = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function MapRoot({ hooks }) {
|
export default function MapRoot({ hooks }) {
|
||||||
const mapRef = useRef<MapHandlers>(null);
|
|
||||||
const providerRef = useRef<MapHandlers>(null);
|
const providerRef = useRef<MapHandlers>(null);
|
||||||
const hooksRef = useRef<any>(hooks);
|
const hooksRef = useRef<any>(hooks);
|
||||||
|
|
||||||
const mapperHandlerRefs = useRef([mapRef, providerRef]);
|
const mapperHandlerRefs = useRef([providerRef]);
|
||||||
|
|
||||||
const { handleCommand, handleMapEvent, handleMapEvents } = useMapperHandlers(mapperHandlerRefs.current, hooksRef);
|
const { handleCommand, handleMapEvent, handleMapEvents } = useMapperHandlers(mapperHandlerRefs.current, hooksRef);
|
||||||
|
|
||||||
@@ -41,7 +40,7 @@ export default function MapRoot({ hooks }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PrimeReactProvider>
|
<PrimeReactProvider>
|
||||||
<MapRootProvider fwdRef={providerRef} outCommand={handleCommand} mapRef={mapRef}>
|
<MapRootProvider fwdRef={providerRef} outCommand={handleCommand}>
|
||||||
<ErrorBoundary FallbackComponent={ErrorFallback} onError={logError}>
|
<ErrorBoundary FallbackComponent={ErrorFallback} onError={logError}>
|
||||||
<ReactFlowProvider>
|
<ReactFlowProvider>
|
||||||
<MapRootContent />
|
<MapRootContent />
|
||||||
|
|||||||
@@ -108,3 +108,7 @@
|
|||||||
.p-dropdown-empty-message {
|
.p-dropdown-empty-message {
|
||||||
padding: 0.25rem 0.5rem;
|
padding: 0.25rem 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.p-autocomplete .p-autocomplete-multiple-container .p-autocomplete-token {
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useAutoAnimate } from '@formkit/auto-animate/react';
|
import { useAutoAnimate } from '@formkit/auto-animate/react';
|
||||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
|
||||||
import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
|
import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||||
import { CharacterTypeRaw } from '@/hooks/Mapper/types';
|
import { CharacterTypeRaw } from '@/hooks/Mapper/types';
|
||||||
|
import { emitMapEvent } from '@/hooks/Mapper/events';
|
||||||
|
|
||||||
const Characters = ({ data }: { data: CharacterTypeRaw[] }) => {
|
const Characters = ({ data }: { data: CharacterTypeRaw[] }) => {
|
||||||
const [parent] = useAutoAnimate();
|
const [parent] = useAutoAnimate();
|
||||||
const { mapRef } = useMapRootState();
|
|
||||||
|
|
||||||
const handleSelect = useCallback(
|
const handleSelect = useCallback((character: CharacterTypeRaw) => {
|
||||||
(character: CharacterTypeRaw) => {
|
emitMapEvent({
|
||||||
mapRef.current?.command(Commands.centerSystem, character?.location?.solar_system_id?.toString());
|
name: Commands.centerSystem,
|
||||||
},
|
data: character?.location?.solar_system_id?.toString(),
|
||||||
[mapRef],
|
});
|
||||||
);
|
}, []);
|
||||||
|
|
||||||
const items = data.map(character => (
|
const items = data.map(character => (
|
||||||
<li
|
<li
|
||||||
|
|||||||
@@ -88,6 +88,23 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
|
|||||||
setSystem(undefined);
|
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 onSystemStatus = useCallback((status: number) => {
|
||||||
const { system, outCommand } = ref.current;
|
const { system, outCommand } = ref.current;
|
||||||
if (!system) {
|
if (!system) {
|
||||||
@@ -161,6 +178,7 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
|
|||||||
onLockToggle,
|
onLockToggle,
|
||||||
onHubToggle,
|
onHubToggle,
|
||||||
onSystemTag,
|
onSystemTag,
|
||||||
|
onSystemTemporaryName,
|
||||||
onSystemStatus,
|
onSystemStatus,
|
||||||
onSystemLabels,
|
onSystemLabels,
|
||||||
onOpenSettings,
|
onOpenSettings,
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import { PrimeIcons } from 'primereact/api';
|
|||||||
import { ContextMenuSystemProps } from '@/hooks/Mapper/components/contexts';
|
import { ContextMenuSystemProps } from '@/hooks/Mapper/components/contexts';
|
||||||
import { useWaypointMenu } from '@/hooks/Mapper/components/contexts/hooks';
|
import { useWaypointMenu } from '@/hooks/Mapper/components/contexts/hooks';
|
||||||
import { FastSystemActions } from '@/hooks/Mapper/components/contexts/components';
|
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 = ({
|
export const useContextMenuSystemItems = ({
|
||||||
onDeleteSystem,
|
onDeleteSystem,
|
||||||
@@ -25,6 +28,7 @@ export const useContextMenuSystemItems = ({
|
|||||||
const getStatus = useStatusMenu(systems, systemId, onSystemStatus);
|
const getStatus = useStatusMenu(systems, systemId, onSystemStatus);
|
||||||
const getLabels = useLabelsMenu(systems, systemId, onSystemLabels, onCustomLabelDialog);
|
const getLabels = useLabelsMenu(systems, systemId, onSystemLabels, onCustomLabelDialog);
|
||||||
const getWaypointMenu = useWaypointMenu(onWaypointSet);
|
const getWaypointMenu = useWaypointMenu(onWaypointSet);
|
||||||
|
const canLockSystem = useMapCheckPermissions([UserPermission.LOCK_SYSTEM]);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const system = systemId ? getSystemById(systems, systemId) : undefined;
|
const system = systemId ? getSystemById(systems, systemId) : undefined;
|
||||||
@@ -41,6 +45,8 @@ export const useContextMenuSystemItems = ({
|
|||||||
<FastSystemActions
|
<FastSystemActions
|
||||||
systemId={systemId}
|
systemId={systemId}
|
||||||
systemName={system.system_static_info.solar_system_name}
|
systemName={system.system_static_info.solar_system_name}
|
||||||
|
regionName={system.system_static_info.region_name}
|
||||||
|
isWH={isWormholeSpace(system.system_static_info.system_class)}
|
||||||
showEdit
|
showEdit
|
||||||
onOpenSettings={onOpenSettings}
|
onOpenSettings={onOpenSettings}
|
||||||
/>
|
/>
|
||||||
@@ -58,19 +64,25 @@ export const useContextMenuSystemItems = ({
|
|||||||
command: onHubToggle,
|
command: onHubToggle,
|
||||||
},
|
},
|
||||||
...(system.locked
|
...(system.locked
|
||||||
? [
|
? canLockSystem
|
||||||
{
|
? [
|
||||||
label: 'Unlock',
|
{
|
||||||
icon: PrimeIcons.LOCK_OPEN,
|
label: 'Unlock',
|
||||||
command: onLockToggle,
|
icon: PrimeIcons.LOCK_OPEN,
|
||||||
},
|
command: onLockToggle,
|
||||||
]
|
},
|
||||||
|
]
|
||||||
|
: []
|
||||||
: [
|
: [
|
||||||
{
|
...(canLockSystem
|
||||||
label: 'Lock',
|
? [
|
||||||
icon: PrimeIcons.LOCK,
|
{
|
||||||
command: onLockToggle,
|
label: 'Lock',
|
||||||
},
|
icon: PrimeIcons.LOCK,
|
||||||
|
command: onLockToggle,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
{ separator: true },
|
{ separator: true },
|
||||||
{
|
{
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
@@ -80,6 +92,7 @@ export const useContextMenuSystemItems = ({
|
|||||||
]),
|
]),
|
||||||
];
|
];
|
||||||
}, [
|
}, [
|
||||||
|
canLockSystem,
|
||||||
systems,
|
systems,
|
||||||
systemId,
|
systemId,
|
||||||
getTags,
|
getTags,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/ty
|
|||||||
import { FastSystemActions } from '@/hooks/Mapper/components/contexts/components';
|
import { FastSystemActions } from '@/hooks/Mapper/components/contexts/components';
|
||||||
import { useJumpPlannerMenu } from '@/hooks/Mapper/components/contexts/hooks';
|
import { useJumpPlannerMenu } from '@/hooks/Mapper/components/contexts/hooks';
|
||||||
import { Route } from '@/hooks/Mapper/types/routes.ts';
|
import { Route } from '@/hooks/Mapper/types/routes.ts';
|
||||||
|
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
|
||||||
|
|
||||||
export interface ContextMenuSystemInfoProps {
|
export interface ContextMenuSystemInfoProps {
|
||||||
systemStatics: Map<number, SolarSystemStaticInfoRaw>;
|
systemStatics: Map<number, SolarSystemStaticInfoRaw>;
|
||||||
@@ -48,7 +49,6 @@ export const ContextMenuSystemInfo: React.FC<ContextMenuSystemInfoProps> = ({
|
|||||||
if (!systemId || !system) {
|
if (!systemId || !system) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
className: classes.FastActions,
|
className: classes.FastActions,
|
||||||
@@ -57,6 +57,8 @@ export const ContextMenuSystemInfo: React.FC<ContextMenuSystemInfoProps> = ({
|
|||||||
<FastSystemActions
|
<FastSystemActions
|
||||||
systemId={systemId}
|
systemId={systemId}
|
||||||
systemName={system.solar_system_name}
|
systemName={system.solar_system_name}
|
||||||
|
regionName={system.region_name}
|
||||||
|
isWH={isWormholeSpace(system.system_class)}
|
||||||
onOpenSettings={onOpenSettings}
|
onOpenSettings={onOpenSettings}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import { RefObject, useCallback, useRef, useState } from 'react';
|
import * as React from 'react';
|
||||||
|
import { useCallback, useRef, useState } from 'react';
|
||||||
import { ContextMenu } from 'primereact/contextmenu';
|
import { ContextMenu } from 'primereact/contextmenu';
|
||||||
import { Commands, MapHandlers, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
|
import { Commands, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||||
import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/types.ts';
|
import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/types.ts';
|
||||||
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||||
import * as React from 'react';
|
|
||||||
import { SolarSystemStaticInfoRaw } from '@/hooks/Mapper/types';
|
import { SolarSystemStaticInfoRaw } from '@/hooks/Mapper/types';
|
||||||
|
import { emitMapEvent } from '@/hooks/Mapper/events';
|
||||||
|
|
||||||
interface UseContextMenuSystemHandlersProps {
|
interface UseContextMenuSystemHandlersProps {
|
||||||
hubs: string[];
|
hubs: string[];
|
||||||
outCommand: OutCommandHandler;
|
outCommand: OutCommandHandler;
|
||||||
mapRef: RefObject<MapHandlers>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useContextMenuSystemInfoHandlers = ({ hubs, outCommand, mapRef }: UseContextMenuSystemHandlersProps) => {
|
export const useContextMenuSystemInfoHandlers = ({ hubs, outCommand }: UseContextMenuSystemHandlersProps) => {
|
||||||
const contextMenuRef = useRef<ContextMenu | null>(null);
|
const contextMenuRef = useRef<ContextMenu | null>(null);
|
||||||
|
|
||||||
const [system, setSystem] = useState<string>();
|
const [system, setSystem] = useState<string>();
|
||||||
const routeRef = useRef<(SolarSystemStaticInfoRaw | undefined)[]>([]);
|
const routeRef = useRef<(SolarSystemStaticInfoRaw | undefined)[]>([]);
|
||||||
|
|
||||||
const ref = useRef({ hubs, system, outCommand, mapRef });
|
const ref = useRef({ hubs, system, outCommand });
|
||||||
ref.current = { hubs, system, outCommand, mapRef };
|
ref.current = { hubs, system, outCommand };
|
||||||
|
|
||||||
const open = useCallback(
|
const open = useCallback(
|
||||||
(ev: React.SyntheticEvent, systemId: string, route: (SolarSystemStaticInfoRaw | undefined)[]) => {
|
(ev: React.SyntheticEvent, systemId: string, route: (SolarSystemStaticInfoRaw | undefined)[]) => {
|
||||||
@@ -48,7 +48,7 @@ export const useContextMenuSystemInfoHandlers = ({ hubs, outCommand, mapRef }: U
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onAddSystem = useCallback(() => {
|
const onAddSystem = useCallback(() => {
|
||||||
const { system: solarSystemId, outCommand, mapRef } = ref.current;
|
const { system: solarSystemId, outCommand } = ref.current;
|
||||||
if (!solarSystemId) {
|
if (!solarSystemId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,11 @@ export const useContextMenuSystemInfoHandlers = ({ hubs, outCommand, mapRef }: U
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
mapRef.current?.command(Commands.centerSystem, solarSystemId);
|
emitMapEvent({
|
||||||
|
name: Commands.centerSystem,
|
||||||
|
data: solarSystemId,
|
||||||
|
});
|
||||||
|
|
||||||
setSystem(undefined);
|
setSystem(undefined);
|
||||||
}, 200);
|
}, 200);
|
||||||
}, []);
|
}, []);
|
||||||
|
|||||||
@@ -9,13 +9,22 @@ import { PrimeIcons } from 'primereact/api';
|
|||||||
export interface FastSystemActionsProps {
|
export interface FastSystemActionsProps {
|
||||||
systemId: string;
|
systemId: string;
|
||||||
systemName: string;
|
systemName: string;
|
||||||
|
regionName: string;
|
||||||
|
isWH: boolean;
|
||||||
showEdit?: boolean;
|
showEdit?: boolean;
|
||||||
onOpenSettings(): void;
|
onOpenSettings(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FastSystemActions = ({ systemId, systemName, onOpenSettings, showEdit }: FastSystemActionsProps) => {
|
export const FastSystemActions = ({
|
||||||
const ref = useRef({ systemId, systemName });
|
systemId,
|
||||||
ref.current = { systemId, systemName };
|
systemName,
|
||||||
|
regionName,
|
||||||
|
isWH,
|
||||||
|
onOpenSettings,
|
||||||
|
showEdit,
|
||||||
|
}: FastSystemActionsProps) => {
|
||||||
|
const ref = useRef({ systemId, systemName, regionName, isWH });
|
||||||
|
ref.current = { systemId, systemName, regionName, isWH };
|
||||||
|
|
||||||
const handleOpenZKB = useCallback(
|
const handleOpenZKB = useCallback(
|
||||||
() => window.open(`https://zkillboard.com/system/${ref.current.systemId}`, '_blank'),
|
() => window.open(`https://zkillboard.com/system/${ref.current.systemId}`, '_blank'),
|
||||||
@@ -27,10 +36,17 @@ export const FastSystemActions = ({ systemId, systemName, onOpenSettings, showEd
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOpenDotlan = useCallback(
|
const handleOpenDotlan = useCallback(() => {
|
||||||
() => window.open(`https://evemaps.dotlan.net/system/${ref.current.systemName}`, '_blank'),
|
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 () => {
|
const copySystemNameToClipboard = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -43,9 +59,9 @@ export const FastSystemActions = ({ systemId, systemName, onOpenSettings, showEd
|
|||||||
return (
|
return (
|
||||||
<LayoutEventBlocker className={clsx('flex px-2 gap-2 justify-between items-center h-full')}>
|
<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)}>
|
<div className={clsx('flex gap-2 items-center h-full', classes.Links)}>
|
||||||
<WdImgButton source={ZKB_ICON} onClick={handleOpenZKB} />
|
<WdImgButton tooltip={{ content: 'Open zkillboard' }} source={ZKB_ICON} onClick={handleOpenZKB} />
|
||||||
<WdImgButton source={ANOIK_ICON} onClick={handleOpenAnoikis} />
|
<WdImgButton tooltip={{ content: 'Open Anoikis' }} source={ANOIK_ICON} onClick={handleOpenAnoikis} />
|
||||||
<WdImgButton source={DOTLAN_ICON} onClick={handleOpenDotlan} />
|
<WdImgButton tooltip={{ content: 'Open Dotlan' }} source={DOTLAN_ICON} onClick={handleOpenDotlan} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-2 items-center pl-1">
|
<div className="flex gap-2 items-center pl-1">
|
||||||
|
|||||||
173
assets/js/hooks/Mapper/components/hooks/useSolarSystemNode.ts
Normal file
173
assets/js/hooks/Mapper/components/hooks/useSolarSystemNode.ts
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
// feel free to rename these imports or the file path as you see fit
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||||
|
import { useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||||
|
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
|
||||||
|
import { useDoubleClick } from '@/hooks/Mapper/hooks/useDoubleClick.ts';
|
||||||
|
import { REGIONS_MAP, Spaces } from '@/hooks/Mapper/constants';
|
||||||
|
import { MapSolarSystemType } from '../../map.types';
|
||||||
|
import {
|
||||||
|
LABELS_INFO,
|
||||||
|
LABELS_ORDER,
|
||||||
|
getActivityType,
|
||||||
|
} from '@/hooks/Mapper/components/map/constants.ts';
|
||||||
|
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
|
||||||
|
import { getSystemClassStyles, prepareUnsplashedChunks } from '@/hooks/Mapper/components/map/helpers';
|
||||||
|
import { sortWHClasses } from '@/hooks/Mapper/helpers';
|
||||||
|
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager.ts';
|
||||||
|
import { OutCommand } from '@/hooks/Mapper/types';
|
||||||
|
|
||||||
|
const SpaceToClass: Record<string, string> = {
|
||||||
|
[Spaces.Caldari]: 'Caldaria',
|
||||||
|
[Spaces.Matar]: 'Mataria',
|
||||||
|
[Spaces.Amarr]: 'Amarria',
|
||||||
|
[Spaces.Gallente]: 'Gallente',
|
||||||
|
};
|
||||||
|
|
||||||
|
const sortedLabels = (labels: string[]) => {
|
||||||
|
if (!labels) return [];
|
||||||
|
return LABELS_ORDER.filter(x => labels.includes(x)).map(x => LABELS_INFO[x]);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface UseSolarSystemNodeParams {
|
||||||
|
data: MapSolarSystemType;
|
||||||
|
selected: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSolarSystemNode({ data, selected }: UseSolarSystemNodeParams) {
|
||||||
|
// 1) Bring in relevant global state
|
||||||
|
const { interfaceSettings } = useMapRootState();
|
||||||
|
const { isShowUnsplashedSignatures } = interfaceSettings;
|
||||||
|
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: {
|
||||||
|
characters,
|
||||||
|
presentCharacters,
|
||||||
|
wormholesData,
|
||||||
|
hubs,
|
||||||
|
kills,
|
||||||
|
userCharacters,
|
||||||
|
isConnecting,
|
||||||
|
hoverNodeId,
|
||||||
|
visibleNodes,
|
||||||
|
showKSpaceBG,
|
||||||
|
isThickConnections,
|
||||||
|
},
|
||||||
|
outCommand,
|
||||||
|
} = useMapState();
|
||||||
|
|
||||||
|
// 2) Extract data from the node
|
||||||
|
const {
|
||||||
|
system_class,
|
||||||
|
security,
|
||||||
|
class_title,
|
||||||
|
solar_system_id,
|
||||||
|
statics,
|
||||||
|
effect_name,
|
||||||
|
region_name,
|
||||||
|
region_id,
|
||||||
|
is_shattered,
|
||||||
|
solar_system_name,
|
||||||
|
} = data.system_static_info;
|
||||||
|
|
||||||
|
const { locked, name, tag, status, labels, id, temporary_name: temporaryName } = data || {};
|
||||||
|
const signatures = data.system_signatures;
|
||||||
|
|
||||||
|
// 3) Compute derived values
|
||||||
|
const visible = useMemo(() => visibleNodes.has(id), [id, visibleNodes]);
|
||||||
|
|
||||||
|
const charactersInSystem = useMemo(() => {
|
||||||
|
return characters
|
||||||
|
.filter(c => c.location?.solar_system_id === solar_system_id)
|
||||||
|
.filter(c => c.online);
|
||||||
|
}, [characters, presentCharacters, solar_system_id]);
|
||||||
|
|
||||||
|
const isWormhole = isWormholeSpace(system_class);
|
||||||
|
|
||||||
|
const classTitleColor = useMemo(
|
||||||
|
() => getSystemClassStyles({ systemClass: system_class, security }),
|
||||||
|
[security, system_class],
|
||||||
|
);
|
||||||
|
|
||||||
|
const sortedStatics = useMemo(
|
||||||
|
() => sortWHClasses(wormholesData, statics),
|
||||||
|
[wormholesData, statics],
|
||||||
|
);
|
||||||
|
|
||||||
|
const labelsManager = useMemo(() => new LabelsManager(labels ?? ''), [labels]);
|
||||||
|
const labelsInfo = useMemo(() => sortedLabels(labelsManager.list), [labelsManager]);
|
||||||
|
const labelCustom = useMemo(() => labelsManager.customLabel, [labelsManager]);
|
||||||
|
|
||||||
|
const killsCount = useMemo(() => {
|
||||||
|
const systemKills = kills[solar_system_id];
|
||||||
|
if (!systemKills) return null;
|
||||||
|
return systemKills;
|
||||||
|
}, [kills, solar_system_id]);
|
||||||
|
|
||||||
|
const hasUserCharacters = useMemo(() => {
|
||||||
|
return charactersInSystem.some(x => userCharacters.includes(x.eve_id));
|
||||||
|
}, [charactersInSystem, userCharacters]);
|
||||||
|
|
||||||
|
const dbClick = useDoubleClick(() => {
|
||||||
|
outCommand({
|
||||||
|
type: OutCommand.openSettings,
|
||||||
|
data: {
|
||||||
|
system_id: solar_system_id.toString(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const showHandlers = isConnecting || hoverNodeId === id;
|
||||||
|
|
||||||
|
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
|
||||||
|
const regionClass = showKSpaceBG ? SpaceToClass[space] : null;
|
||||||
|
|
||||||
|
const systemName = (isTempSystemNameEnabled && temporaryName) || solar_system_name;
|
||||||
|
const customName = (isTempSystemNameEnabled && temporaryName && name)
|
||||||
|
|| (solar_system_name !== name && name);
|
||||||
|
|
||||||
|
const [unsplashedLeft, unsplashedRight] = useMemo(() => {
|
||||||
|
if (!isShowUnsplashedSignatures) {
|
||||||
|
return [[], []];
|
||||||
|
}
|
||||||
|
return prepareUnsplashedChunks(
|
||||||
|
signatures
|
||||||
|
.filter(s => s.group === 'Wormhole' && !s.linked_system)
|
||||||
|
.map(s => ({
|
||||||
|
eve_id: s.eve_id,
|
||||||
|
type: s.type,
|
||||||
|
custom_info: s.custom_info,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
}, [isShowUnsplashedSignatures, signatures]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
selected,
|
||||||
|
visible,
|
||||||
|
isWormhole,
|
||||||
|
classTitleColor,
|
||||||
|
killsCount,
|
||||||
|
hasUserCharacters,
|
||||||
|
showHandlers,
|
||||||
|
regionClass,
|
||||||
|
systemName,
|
||||||
|
customName,
|
||||||
|
labelCustom,
|
||||||
|
is_shattered,
|
||||||
|
tag,
|
||||||
|
status,
|
||||||
|
labelsInfo,
|
||||||
|
dbClick,
|
||||||
|
sortedStatics,
|
||||||
|
effect_name,
|
||||||
|
region_name,
|
||||||
|
solar_system_id,
|
||||||
|
locked,
|
||||||
|
hubs,
|
||||||
|
charactersInSystem,
|
||||||
|
unsplashedLeft,
|
||||||
|
unsplashedRight,
|
||||||
|
isThickConnections,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,4 +1,11 @@
|
|||||||
.MapRoot {
|
.MapRoot {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,41 @@
|
|||||||
import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect, useRef } from 'react';
|
import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect, useMemo } from 'react';
|
||||||
import ReactFlow, {
|
import ReactFlow, {
|
||||||
Background,
|
Background,
|
||||||
ConnectionMode,
|
ConnectionMode,
|
||||||
Edge,
|
Edge,
|
||||||
MiniMap,
|
MiniMap,
|
||||||
Node,
|
Node,
|
||||||
|
NodeChange,
|
||||||
NodeDragHandler,
|
NodeDragHandler,
|
||||||
OnConnect,
|
OnConnect,
|
||||||
OnMoveEnd,
|
OnMoveEnd,
|
||||||
OnSelectionChangeFunc,
|
OnSelectionChangeFunc,
|
||||||
SelectionDragHandler,
|
SelectionDragHandler,
|
||||||
SelectionMode,
|
SelectionMode,
|
||||||
useEdgesState,
|
|
||||||
useNodesState,
|
|
||||||
NodeChange,
|
|
||||||
useReactFlow,
|
useReactFlow,
|
||||||
} from 'reactflow';
|
} from 'reactflow';
|
||||||
import 'reactflow/dist/style.css';
|
import 'reactflow/dist/style.css';
|
||||||
import classes from './Map.module.scss';
|
import classes from './Map.module.scss';
|
||||||
import './styles/neon-theme.scss';
|
|
||||||
import './styles/eve-common.scss';
|
|
||||||
import { MapProvider, useMapState } from './MapProvider';
|
import { MapProvider, useMapState } from './MapProvider';
|
||||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
import { useNodesState, useEdgesState, useMapHandlers, useUpdateNodes } from './hooks';
|
||||||
import { useMapHandlers, useUpdateNodes } from './hooks';
|
|
||||||
import { MapHandlers, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
|
import { MapHandlers, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||||
import {
|
import {
|
||||||
ContextMenuConnection,
|
ContextMenuConnection,
|
||||||
ContextMenuRoot,
|
ContextMenuRoot,
|
||||||
SolarSystemEdge,
|
SolarSystemEdge,
|
||||||
SolarSystemNode,
|
SolarSystemNodeDefault,
|
||||||
|
SolarSystemNodeTheme,
|
||||||
useContextMenuConnectionHandlers,
|
useContextMenuConnectionHandlers,
|
||||||
useContextMenuRootHandlers,
|
useContextMenuRootHandlers,
|
||||||
} from './components';
|
} from './components';
|
||||||
import { OnMapSelectionChange } from './map.types';
|
import { wrapNode } from './utils/wrapNode';
|
||||||
|
import { OnMapAddSystemCallback, OnMapSelectionChange } from './map.types';
|
||||||
import { SESSION_KEY } from '@/hooks/Mapper/constants.ts';
|
import { SESSION_KEY } from '@/hooks/Mapper/constants.ts';
|
||||||
import { SolarSystemConnection, SolarSystemRawType } from '@/hooks/Mapper/types';
|
import { SolarSystemConnection, SolarSystemRawType } from '@/hooks/Mapper/types';
|
||||||
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||||
import { NodeSelectionMouseHandler } from '@/hooks/Mapper/components/contexts/types.ts';
|
import { NodeSelectionMouseHandler } from '@/hooks/Mapper/components/contexts/types.ts';
|
||||||
import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks';
|
import clsx from 'clsx';
|
||||||
|
import { useBackgroundVars } from './hooks/useBackgroundVars';
|
||||||
|
|
||||||
const DEFAULT_VIEW_PORT = { zoom: 1, x: 0, y: 0 };
|
const DEFAULT_VIEW_PORT = { zoom: 1, x: 0, y: 0 };
|
||||||
|
|
||||||
@@ -79,11 +77,8 @@ const initialEdges = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const nodeTypes = {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
custom: SolarSystemNode,
|
|
||||||
} as never;
|
|
||||||
|
|
||||||
const edgeTypes = {
|
const edgeTypes = {
|
||||||
floating: SolarSystemEdge,
|
floating: SolarSystemEdge,
|
||||||
@@ -93,12 +88,18 @@ interface MapCompProps {
|
|||||||
refn: ForwardedRef<MapHandlers>;
|
refn: ForwardedRef<MapHandlers>;
|
||||||
onCommand: OutCommandHandler;
|
onCommand: OutCommandHandler;
|
||||||
onSelectionChange: OnMapSelectionChange;
|
onSelectionChange: OnMapSelectionChange;
|
||||||
|
onManualDelete(systems: string[]): void;
|
||||||
onConnectionInfoClick?(e: SolarSystemConnection): void;
|
onConnectionInfoClick?(e: SolarSystemConnection): void;
|
||||||
|
onAddSystem?: OnMapAddSystemCallback;
|
||||||
onSelectionContextMenu?: NodeSelectionMouseHandler;
|
onSelectionContextMenu?: NodeSelectionMouseHandler;
|
||||||
minimapClasses?: string;
|
minimapClasses?: string;
|
||||||
isShowMinimap?: boolean;
|
isShowMinimap?: boolean;
|
||||||
onSystemContextMenu: (event: MouseEvent<Element>, systemId: string) => void;
|
onSystemContextMenu: (event: MouseEvent<Element>, systemId: string) => void;
|
||||||
showKSpaceBG?: boolean;
|
showKSpaceBG?: boolean;
|
||||||
|
isThickConnections?: boolean;
|
||||||
|
isShowBackgroundPattern?: boolean;
|
||||||
|
isSoftBackground?: boolean;
|
||||||
|
theme?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MapComp = ({
|
const MapComp = ({
|
||||||
@@ -109,26 +110,36 @@ const MapComp = ({
|
|||||||
onSystemContextMenu,
|
onSystemContextMenu,
|
||||||
onConnectionInfoClick,
|
onConnectionInfoClick,
|
||||||
onSelectionContextMenu,
|
onSelectionContextMenu,
|
||||||
|
onManualDelete,
|
||||||
isShowMinimap,
|
isShowMinimap,
|
||||||
showKSpaceBG,
|
showKSpaceBG,
|
||||||
|
isThickConnections,
|
||||||
|
isShowBackgroundPattern,
|
||||||
|
isSoftBackground,
|
||||||
|
theme,
|
||||||
|
onAddSystem,
|
||||||
}: MapCompProps) => {
|
}: MapCompProps) => {
|
||||||
const { getNode } = useReactFlow();
|
const { getNode, getNodes } = useReactFlow();
|
||||||
const [nodes, , onNodesChange] = useNodesState<SolarSystemRawType>(initialNodes);
|
const [nodes, , onNodesChange] = useNodesState<Node<SolarSystemRawType>>(initialNodes);
|
||||||
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>[]>(initialEdges);
|
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>>(initialEdges);
|
||||||
|
|
||||||
|
|
||||||
|
const nodeTypes = useMemo(() => {
|
||||||
|
return {
|
||||||
|
custom:
|
||||||
|
theme !== '' && theme !== 'default'
|
||||||
|
? wrapNode(SolarSystemNodeTheme)
|
||||||
|
: wrapNode(SolarSystemNodeDefault),
|
||||||
|
};
|
||||||
|
}, [theme]);
|
||||||
|
|
||||||
|
|
||||||
useMapHandlers(refn, onSelectionChange);
|
useMapHandlers(refn, onSelectionChange);
|
||||||
useUpdateNodes(nodes);
|
useUpdateNodes(nodes);
|
||||||
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers();
|
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers({ onAddSystem });
|
||||||
const { handleConnectionContext, ...connectionCtxProps } = useContextMenuConnectionHandlers();
|
const { handleConnectionContext, ...connectionCtxProps } = useContextMenuConnectionHandlers();
|
||||||
const { update } = useMapState();
|
const { update } = useMapState();
|
||||||
const {
|
const { variant, gap, size, color } = useBackgroundVars(theme);
|
||||||
data: { systems },
|
|
||||||
} = useMapRootState();
|
|
||||||
|
|
||||||
const { deleteSystems } = useDeleteSystems();
|
|
||||||
|
|
||||||
const systemsRef = useRef({ systems });
|
|
||||||
systemsRef.current = { systems };
|
|
||||||
|
|
||||||
const onConnect: OnConnect = useCallback(
|
const onConnect: OnConnect = useCallback(
|
||||||
params => {
|
params => {
|
||||||
@@ -186,39 +197,51 @@ const MapComp = ({
|
|||||||
const handleNodesChange = useCallback(
|
const handleNodesChange = useCallback(
|
||||||
(changes: NodeChange[]) => {
|
(changes: NodeChange[]) => {
|
||||||
const systemsIdsToRemove: string[] = [];
|
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) => {
|
const nextChanges = changes.reduce((acc, change) => {
|
||||||
if (change.type === 'remove') {
|
if (change.type !== 'remove') {
|
||||||
const node = getNode(change.id);
|
return [...acc, change];
|
||||||
const { systems = [] } = systemsRef.current;
|
}
|
||||||
if (node?.data?.id && !systems.map(s => s.id).includes(node?.data?.id)) {
|
|
||||||
return [...acc, change];
|
const node = getNode(change.id);
|
||||||
} else if (!node?.data?.locked) {
|
if (!node) {
|
||||||
systemsIdsToRemove.push(node?.data?.id);
|
return [...acc, change];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (node.data.locked) {
|
||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
systemsIdsToRemove.push(node.data.id);
|
||||||
return [...acc, change];
|
return [...acc, change];
|
||||||
}, [] as NodeChange[]);
|
}, [] as NodeChange[]);
|
||||||
|
|
||||||
if (systemsIdsToRemove.length) {
|
if (systemsIdsToRemove.length > 0) {
|
||||||
deleteSystems(systemsIdsToRemove);
|
onManualDelete(systemsIdsToRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
onNodesChange(nextChanges);
|
onNodesChange(nextChanges);
|
||||||
},
|
},
|
||||||
[deleteSystems, getNode, onNodesChange],
|
[getNode, onManualDelete, onNodesChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
update(x => ({
|
update(x => ({
|
||||||
...x,
|
...x,
|
||||||
showKSpaceBG: showKSpaceBG,
|
showKSpaceBG: showKSpaceBG,
|
||||||
|
isThickConnections: isThickConnections,
|
||||||
}));
|
}));
|
||||||
}, [showKSpaceBG, update]);
|
}, [showKSpaceBG, isThickConnections, update]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={classes.MapRoot}>
|
<div className={clsx(classes.MapRoot, { [classes.BackgroundAlternateColor]: isSoftBackground })}>
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
edges={edges}
|
edges={edges}
|
||||||
@@ -238,6 +261,7 @@ const MapComp = ({
|
|||||||
onConnectStart={() => update({ isConnecting: true })}
|
onConnectStart={() => update({ isConnecting: true })}
|
||||||
onConnectEnd={() => update({ isConnecting: false })}
|
onConnectEnd={() => update({ isConnecting: false })}
|
||||||
onNodeMouseEnter={(_, node) => update({ hoverNodeId: node.id })}
|
onNodeMouseEnter={(_, node) => update({ hoverNodeId: node.id })}
|
||||||
|
// onKeyUp=
|
||||||
onNodeMouseLeave={() => update({ hoverNodeId: null })}
|
onNodeMouseLeave={() => update({ hoverNodeId: null })}
|
||||||
onEdgeClick={(_, t) => {
|
onEdgeClick={(_, t) => {
|
||||||
onConnectionInfoClick?.(t.data);
|
onConnectionInfoClick?.(t.data);
|
||||||
@@ -264,7 +288,7 @@ const MapComp = ({
|
|||||||
selectionMode={SelectionMode.Partial}
|
selectionMode={SelectionMode.Partial}
|
||||||
>
|
>
|
||||||
{isShowMinimap && <MiniMap pannable zoomable ariaLabel="Mini map" className={minimapClasses} />}
|
{isShowMinimap && <MiniMap pannable zoomable ariaLabel="Mini map" className={minimapClasses} />}
|
||||||
<Background />
|
{isShowBackgroundPattern && <Background variant={variant} gap={gap} size={size} color={color} />}
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
{/* <button className="z-auto btn btn-primary absolute top-20 right-20" onClick={handleGetPassages}>
|
{/* <button className="z-auto btn btn-primary absolute top-20 right-20" onClick={handleGetPassages}>
|
||||||
Test // DON NOT REMOVE
|
Test // DON NOT REMOVE
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export type MapData = MapUnionTypes & {
|
|||||||
hoverNodeId: string | null;
|
hoverNodeId: string | null;
|
||||||
visibleNodes: Set<string>;
|
visibleNodes: Set<string>;
|
||||||
showKSpaceBG: boolean;
|
showKSpaceBG: boolean;
|
||||||
|
isThickConnections: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface MapProviderProps {
|
interface MapProviderProps {
|
||||||
@@ -17,6 +18,7 @@ interface MapProviderProps {
|
|||||||
|
|
||||||
const INITIAL_DATA: MapData = {
|
const INITIAL_DATA: MapData = {
|
||||||
wormholesData: {},
|
wormholesData: {},
|
||||||
|
wormholes: [],
|
||||||
effects: {},
|
effects: {},
|
||||||
characters: [],
|
characters: [],
|
||||||
userCharacters: [],
|
userCharacters: [],
|
||||||
@@ -29,6 +31,8 @@ const INITIAL_DATA: MapData = {
|
|||||||
hoverNodeId: null,
|
hoverNodeId: null,
|
||||||
visibleNodes: new Set(),
|
visibleNodes: new Set(),
|
||||||
showKSpaceBG: false,
|
showKSpaceBG: false,
|
||||||
|
isThickConnections: false,
|
||||||
|
userPermissions: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface MapContextProps {
|
export interface MapContextProps {
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
|
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
|
||||||
|
|
||||||
.ConnectionTimeEOL {
|
.ConnectionTimeEOL {
|
||||||
background-image: linear-gradient(207deg, transparent, #7452c3e3);
|
background-image: linear-gradient(207deg, transparent, var(--conn-time-eol));
|
||||||
}
|
}
|
||||||
|
|
||||||
.ConnectionFrigate {
|
.ConnectionFrigate {
|
||||||
background-image: linear-gradient(207deg, transparent, #325d88);
|
background-image: linear-gradient(207deg, transparent, var(--conn-frigate));
|
||||||
}
|
}
|
||||||
|
|
||||||
.ConnectionSave {
|
.ConnectionSave {
|
||||||
background-image: linear-gradient(207deg, transparent, rgba(155, 102, 45, 0.85));
|
background-image: linear-gradient(207deg, transparent, var(--conn-save));
|
||||||
}
|
}
|
||||||
|
|
||||||
.SelectedItem {
|
.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 { PrimeIcons } from 'primereact/api';
|
||||||
import { MenuItem } from 'primereact/menuitem';
|
import { MenuItem } from 'primereact/menuitem';
|
||||||
import { Edge } from '@reactflow/core/dist/esm/types/edges';
|
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 clsx from 'clsx';
|
||||||
import classes from './ContextMenuConnection.module.scss';
|
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 {
|
export interface ContextMenuConnectionProps {
|
||||||
contextMenuRef: RefObject<ContextMenu>;
|
contextMenuRef: RefObject<ContextMenu>;
|
||||||
@@ -35,46 +42,72 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isFrigateSize = edge.data?.ship_size_type === ShipSizeStatus.small;
|
const isFrigateSize = edge.data?.ship_size_type === ShipSizeStatus.small;
|
||||||
|
const isWormhole = edge.data?.type !== ConnectionType.gate;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
...(isWormhole
|
||||||
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
|
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
label: `Mass status`,
|
label: `EOL`,
|
||||||
icon: PrimeIcons.CHART_PIE,
|
className: clsx({
|
||||||
items: MASS_STATE_NAMES_ORDER.map(x => ({
|
[classes.ConnectionTimeEOL]: edge.data?.time_status === TimeStatus.eol,
|
||||||
label: MASS_STATE_NAMES[x],
|
}),
|
||||||
|
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({
|
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,
|
command: onDeleteConnection,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}, [edge, onChangeTimeState, onDeleteConnection, onChangeMassState, onChangeShipSizeStatus]);
|
}, [edge, onChangeTimeState, onDeleteConnection, onChangeShipSizeStatus, onToggleMassSave, onChangeMassState]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { ContextMenu } from 'primereact/contextmenu';
|
|||||||
import { useMapState } from '../../MapProvider.tsx';
|
import { useMapState } from '../../MapProvider.tsx';
|
||||||
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||||
import { Edge } from '@reactflow/core/dist/esm/types/edges';
|
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';
|
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||||
|
|
||||||
export const useContextMenuConnectionHandlers = () => {
|
export const useContextMenuConnectionHandlers = () => {
|
||||||
@@ -47,6 +47,23 @@ export const useContextMenuConnectionHandlers = () => {
|
|||||||
setEdge(undefined);
|
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 onChangeMassState = useCallback((status: MassState) => {
|
||||||
const { edge, outCommand } = ref.current;
|
const { edge, outCommand } = ref.current;
|
||||||
|
|
||||||
@@ -80,14 +97,16 @@ export const useContextMenuConnectionHandlers = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
outCommand({
|
if (status === ShipSizeStatus.small) {
|
||||||
type: OutCommand.updateConnectionMassStatus,
|
outCommand({
|
||||||
data: {
|
type: OutCommand.updateConnectionMassStatus,
|
||||||
source: edge.source,
|
data: {
|
||||||
target: edge.target,
|
source: edge.source,
|
||||||
value: MassState.normal,
|
target: edge.target,
|
||||||
},
|
value: MassState.normal,
|
||||||
});
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onToggleMassSave = useCallback((locked: boolean) => {
|
const onToggleMassSave = useCallback((locked: boolean) => {
|
||||||
@@ -118,6 +137,7 @@ export const useContextMenuConnectionHandlers = () => {
|
|||||||
contextMenuRef,
|
contextMenuRef,
|
||||||
onDeleteConnection,
|
onDeleteConnection,
|
||||||
onChangeTimeState,
|
onChangeTimeState,
|
||||||
|
onChangeType,
|
||||||
onChangeMassState,
|
onChangeMassState,
|
||||||
onChangeShipSizeStatus,
|
onChangeShipSizeStatus,
|
||||||
onToggleMassSave,
|
onToggleMassSave,
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { useReactFlow, XYPosition } from 'reactflow';
|
import { useReactFlow, XYPosition } from 'reactflow';
|
||||||
import React, { useRef, useState } from 'react';
|
import React, { useCallback, useRef, useState } from 'react';
|
||||||
import { ContextMenu } from 'primereact/contextmenu';
|
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 { 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 rf = useReactFlow();
|
||||||
const contextMenuRef = useRef<ContextMenu | null>(null);
|
const contextMenuRef = useRef<ContextMenu | null>(null);
|
||||||
const { outCommand } = useMapState();
|
|
||||||
const [position, setPosition] = useState<XYPosition | null>(null);
|
const [position, setPosition] = useState<XYPosition | null>(null);
|
||||||
|
|
||||||
const handleRootContext = (e: React.MouseEvent<HTMLDivElement>) => {
|
const handleRootContext = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
@@ -18,14 +20,17 @@ export const useContextMenuRootHandlers = () => {
|
|||||||
contextMenuRef.current?.show(e);
|
contextMenuRef.current?.show(e);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onAddSystem = () => {
|
const ref = useRef({ onAddSystem, position });
|
||||||
outCommand({ type: OutCommand.manualAddSystem, data: { coordinates: position } });
|
ref.current = { onAddSystem, position };
|
||||||
};
|
|
||||||
|
const onAddSystemCallback = useCallback(() => {
|
||||||
|
ref.current.onAddSystem?.({ coordinates: position });
|
||||||
|
}, [position]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handleRootContext,
|
handleRootContext,
|
||||||
|
|
||||||
contextMenuRef,
|
contextMenuRef,
|
||||||
onAddSystem,
|
onAddSystem: onAddSystemCallback,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,51 +1,7 @@
|
|||||||
@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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.EdgePathFront {
|
|
||||||
fill: none;
|
|
||||||
|
|
||||||
stroke: #2c3844;
|
|
||||||
stroke-width: 2px;
|
|
||||||
|
|
||||||
&.MassVerge:not(&.Frigate) {
|
|
||||||
stroke: #af2900;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.MassHalf:not(&.Frigate) {
|
|
||||||
stroke: #a85f00;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.Frigate {
|
|
||||||
stroke: #4e62c9;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.Hovered {
|
|
||||||
stroke: #4e5d6c;
|
|
||||||
stroke-width: 2px;
|
|
||||||
|
|
||||||
&.MassVerge:not(&.Frigate) {
|
|
||||||
stroke: #9d4c34;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.MassHalf:not(&.Frigate) {
|
|
||||||
stroke: #ec992c;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.Frigate {
|
|
||||||
stroke: #41acd7;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.EdgePathBack {
|
.EdgePathBack {
|
||||||
fill: none;
|
fill: none;
|
||||||
|
|
||||||
stroke: #80a5c5;
|
stroke: #80a5c5;
|
||||||
stroke-width: 3px;
|
stroke-width: 3px;
|
||||||
|
|
||||||
@@ -61,6 +17,65 @@
|
|||||||
stroke: #ef7dce;
|
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: #af0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.MassHalf:not(&.Frigate) {
|
||||||
|
stroke: #ffd700;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Frigate {
|
||||||
|
stroke: #d4f0ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Gate {
|
||||||
|
stroke: #1c1e15;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Hovered {
|
||||||
|
stroke: #4e5d6c;
|
||||||
|
stroke-width: 2px;
|
||||||
|
|
||||||
|
&.MassVerge:not(&.Frigate) {
|
||||||
|
stroke: #9d4c34;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.MassHalf:not(&.Frigate) {
|
||||||
|
stroke: #ec992c;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Frigate {
|
||||||
|
stroke: #d4f0ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Tick {
|
||||||
|
stroke-width: 3px;
|
||||||
|
|
||||||
|
&.Hovered {
|
||||||
|
stroke-width: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ClickPath {
|
.ClickPath {
|
||||||
@@ -69,7 +84,23 @@
|
|||||||
stroke-width: 8px;
|
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;
|
font-size: 9px;
|
||||||
line-height: 10px;
|
line-height: 10px;
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
@@ -85,13 +116,3 @@
|
|||||||
height: 8px;
|
height: 8px;
|
||||||
font-size: 8px;
|
font-size: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Handle {
|
|
||||||
min-width: initial;
|
|
||||||
min-height: initial;
|
|
||||||
border: 1px solid #5a7d9a;
|
|
||||||
width: 5px;
|
|
||||||
height: 5px;
|
|
||||||
z-index: 1001;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +1,73 @@
|
|||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import classes from './SolarSystemEdge.module.scss';
|
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 { getEdgeParams } from '@/hooks/Mapper/components/map/utils.ts';
|
||||||
import clsx from 'clsx';
|
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 { PrimeIcons } from 'primereact/api';
|
||||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
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> = {
|
const MAP_TRANSLATES: Record<string, string> = {
|
||||||
[Position.Top]: 'translate(-50%, 0%)',
|
[Position.Top]: 'translate(-48%, 0%)',
|
||||||
[Position.Bottom]: 'translate(-50%, -100%)',
|
[Position.Bottom]: 'translate(-50%, -100%)',
|
||||||
[Position.Left]: 'translate(0%, -50%)',
|
[Position.Left]: 'translate(0%, -50%)',
|
||||||
[Position.Right]: 'translate(-100%, -50%)',
|
[Position.Right]: 'translate(-100%, -50%)',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MAP_OFFSETS_TICK: Record<string, { x: number; y: number }> = {
|
||||||
|
[Position.Top]: { x: 0, y: 3 },
|
||||||
|
[Position.Bottom]: { x: 0, y: -3 },
|
||||||
|
[Position.Left]: { x: 3, y: 0 },
|
||||||
|
[Position.Right]: { x: -3, y: 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const MAP_OFFSETS: Record<string, { x: number; y: number }> = {
|
||||||
|
[Position.Top]: { x: 0, y: 0 },
|
||||||
|
[Position.Bottom]: { x: 0, y: 0 },
|
||||||
|
[Position.Left]: { x: 0, y: 0 },
|
||||||
|
[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>) => {
|
export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }: EdgeProps<SolarSystemConnection>) => {
|
||||||
const sourceNode = useStore(useCallback(store => store.nodeInternals.get(source), [source]));
|
const sourceNode = useStore(useCallback(store => store.nodeInternals.get(source), [source]));
|
||||||
const targetNode = useStore(useCallback(store => store.nodeInternals.get(target), [target]));
|
const targetNode = useStore(useCallback(store => store.nodeInternals.get(target), [target]));
|
||||||
|
const isWormhole = data?.type !== ConnectionType.gate;
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: { isThickConnections },
|
||||||
|
} = useMapState();
|
||||||
|
|
||||||
const [hovered, setHovered] = useState(false);
|
const [hovered, setHovered] = useState(false);
|
||||||
|
|
||||||
const [path, labelX, labelY, sx, sy, tx, ty, sourcePos, targetPos] = useMemo(() => {
|
const [path, labelX, labelY, sx, sy, tx, ty, sourcePos, targetPos] = useMemo(() => {
|
||||||
const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(sourceNode, targetNode);
|
const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(sourceNode, targetNode);
|
||||||
|
|
||||||
const [edgePath, labelX, labelY] = getBezierPath({
|
const offset = isThickConnections ? MAP_OFFSETS_TICK[targetPos] : MAP_OFFSETS[targetPos];
|
||||||
sourceX: sx,
|
|
||||||
sourceY: sy,
|
const method = isWormhole ? getBezierPath : getSmoothStepPath;
|
||||||
|
|
||||||
|
const [edgePath, labelX, labelY] = method({
|
||||||
|
sourceX: sx - offset.x,
|
||||||
|
sourceY: sy - offset.y,
|
||||||
sourcePosition: sourcePos,
|
sourcePosition: sourcePos,
|
||||||
targetPosition: targetPos,
|
targetPosition: targetPos,
|
||||||
targetX: tx,
|
targetX: tx + offset.x,
|
||||||
targetY: ty,
|
targetY: ty + offset.y,
|
||||||
});
|
});
|
||||||
|
|
||||||
return [edgePath, labelX, labelY, sx, sy, tx, ty, sourcePos, targetPos];
|
return [edgePath, labelX, labelY, sx, sy, tx, ty, sourcePos, targetPos];
|
||||||
}, [sourceNode, targetNode]);
|
}, [isThickConnections, sourceNode, targetNode, isWormhole]);
|
||||||
|
|
||||||
if (!sourceNode || !targetNode || !data) {
|
if (!sourceNode || !targetNode || !data) {
|
||||||
return null;
|
return null;
|
||||||
@@ -44,8 +78,10 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
|
|||||||
<path
|
<path
|
||||||
id={`back_${id}`}
|
id={`back_${id}`}
|
||||||
className={clsx(classes.EdgePathBack, {
|
className={clsx(classes.EdgePathBack, {
|
||||||
[classes.TimeCrit]: data.time_status === TimeStatus.eol,
|
[classes.Tick]: isThickConnections,
|
||||||
|
[classes.TimeCrit]: isWormhole && data.time_status === TimeStatus.eol,
|
||||||
[classes.Hovered]: hovered,
|
[classes.Hovered]: hovered,
|
||||||
|
[classes.Gate]: !isWormhole,
|
||||||
})}
|
})}
|
||||||
d={path}
|
d={path}
|
||||||
markerEnd={markerEnd}
|
markerEnd={markerEnd}
|
||||||
@@ -54,10 +90,12 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
|
|||||||
<path
|
<path
|
||||||
id={`front_${id}`}
|
id={`front_${id}`}
|
||||||
className={clsx(classes.EdgePathFront, {
|
className={clsx(classes.EdgePathFront, {
|
||||||
|
[classes.Tick]: isThickConnections,
|
||||||
[classes.Hovered]: hovered,
|
[classes.Hovered]: hovered,
|
||||||
[classes.MassVerge]: data.mass_status === MassState.verge,
|
[classes.MassVerge]: isWormhole && data.mass_status === MassState.verge,
|
||||||
[classes.MassHalf]: data.mass_status === MassState.half,
|
[classes.MassHalf]: isWormhole && data.mass_status === MassState.half,
|
||||||
[classes.Frigate]: data.ship_size_type === ShipSizeStatus.small,
|
[classes.Frigate]: isWormhole && data.ship_size_type === ShipSizeStatus.small,
|
||||||
|
[classes.Gate]: !isWormhole,
|
||||||
})}
|
})}
|
||||||
d={path}
|
d={path}
|
||||||
markerEnd={markerEnd}
|
markerEnd={markerEnd}
|
||||||
@@ -75,21 +113,29 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
|
|||||||
|
|
||||||
<EdgeLabelRenderer>
|
<EdgeLabelRenderer>
|
||||||
<div
|
<div
|
||||||
className={clsx(classes.Handle, 'react-flow__handle absolute nodrag pointer-events-none')}
|
className={clsx(
|
||||||
|
classes.Handle,
|
||||||
|
{ [classes.Tick]: isThickConnections, [classes.Right]: Position.Right === sourcePos },
|
||||||
|
'react-flow__handle absolute nodrag pointer-events-none',
|
||||||
|
)}
|
||||||
style={{ transform: `${MAP_TRANSLATES[sourcePos]} translate(${sx}px,${sy}px)` }}
|
style={{ transform: `${MAP_TRANSLATES[sourcePos]} translate(${sx}px,${sy}px)` }}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={clsx(classes.Handle, 'react-flow__handle absolute nodrag pointer-events-none')}
|
className={clsx(
|
||||||
|
classes.Handle,
|
||||||
|
{ [classes.Tick]: isThickConnections },
|
||||||
|
'react-flow__handle absolute nodrag pointer-events-none',
|
||||||
|
)}
|
||||||
style={{ transform: `${MAP_TRANSLATES[targetPos]} translate(${tx}px,${ty}px)` }}
|
style={{ transform: `${MAP_TRANSLATES[targetPos]} translate(${tx}px,${ty}px)` }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="absolute flex items-center gap-1"
|
className="absolute flex items-center gap-1 pointer-events-none"
|
||||||
style={{
|
style={{
|
||||||
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
|
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{data.locked && (
|
{isWormhole && data.locked && (
|
||||||
<WdTooltipWrapper
|
<WdTooltipWrapper
|
||||||
content="Save mass"
|
content="Save mass"
|
||||||
className={clsx(
|
className={clsx(
|
||||||
@@ -100,6 +146,19 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
|
|||||||
<span className={clsx(PrimeIcons.LOCK, classes.icon)} />
|
<span className={clsx(PrimeIcons.LOCK, classes.icon)} />
|
||||||
</WdTooltipWrapper>
|
</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>
|
</div>
|
||||||
</EdgeLabelRenderer>
|
</EdgeLabelRenderer>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -0,0 +1,377 @@
|
|||||||
|
import { memo, useMemo } from 'react';
|
||||||
|
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,
|
||||||
|
LABELS_ORDER,
|
||||||
|
MARKER_BOOKMARK_BG_STYLES,
|
||||||
|
STATUS_CLASSES,
|
||||||
|
} 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, 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';
|
||||||
|
import { OutCommand } from '@/hooks/Mapper/types';
|
||||||
|
import { useDoubleClick } from '@/hooks/Mapper/hooks/useDoubleClick.ts';
|
||||||
|
import { REGIONS_MAP, Spaces } from '@/hooks/Mapper/constants';
|
||||||
|
|
||||||
|
const SpaceToClass: Record<string, string> = {
|
||||||
|
[Spaces.Caldari]: classes.Caldaria,
|
||||||
|
[Spaces.Matar]: classes.Mataria,
|
||||||
|
[Spaces.Amarr]: classes.Amarria,
|
||||||
|
[Spaces.Gallente]: classes.Gallente,
|
||||||
|
};
|
||||||
|
|
||||||
|
const sortedLabels = (labels: string[]) => {
|
||||||
|
if (!labels) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return LABELS_ORDER.filter(x => labels.includes(x)).map(x => LABELS_INFO[x]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getActivityType = (count: number) => {
|
||||||
|
if (count <= 5) {
|
||||||
|
return 'activityNormal';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count <= 30) {
|
||||||
|
return 'activityWarn';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'activityDanger';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
class_title,
|
||||||
|
solar_system_id,
|
||||||
|
statics,
|
||||||
|
effect_name,
|
||||||
|
region_name,
|
||||||
|
region_id,
|
||||||
|
is_shattered,
|
||||||
|
solar_system_name,
|
||||||
|
} = data.system_static_info;
|
||||||
|
|
||||||
|
const signatures = data.system_signatures;
|
||||||
|
|
||||||
|
const { locked, name, tag, status, labels, id, temporary_name: temporaryName } = data || {};
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: {
|
||||||
|
characters,
|
||||||
|
wormholesData,
|
||||||
|
hubs,
|
||||||
|
kills,
|
||||||
|
isConnecting,
|
||||||
|
hoverNodeId,
|
||||||
|
visibleNodes,
|
||||||
|
showKSpaceBG,
|
||||||
|
isThickConnections,
|
||||||
|
},
|
||||||
|
outCommand,
|
||||||
|
} = useMapState();
|
||||||
|
|
||||||
|
const visible = useMemo(() => visibleNodes.has(id), [id, visibleNodes]);
|
||||||
|
|
||||||
|
const charactersInSystem = useMemo(() => {
|
||||||
|
return characters.filter(c => c.location?.solar_system_id === solar_system_id).filter(c => c.online);
|
||||||
|
}, [characters, solar_system_id]);
|
||||||
|
|
||||||
|
const isWormhole = isWormholeSpace(system_class);
|
||||||
|
const classTitleColor = useMemo(
|
||||||
|
() => getSystemClassStyles({ systemClass: system_class, security }),
|
||||||
|
[security, system_class],
|
||||||
|
);
|
||||||
|
const sortedStatics = useMemo(() => sortWHClasses(wormholesData, statics), [wormholesData, statics]);
|
||||||
|
const lebM = useMemo(() => new LabelsManager(labels ?? ''), [labels]);
|
||||||
|
const labelsInfo = useMemo(() => sortedLabels(lebM.list), [lebM]);
|
||||||
|
const labelCustom = useMemo(() => lebM.customLabel, [lebM]);
|
||||||
|
|
||||||
|
const killsCount = useMemo(() => {
|
||||||
|
const systemKills = kills[solar_system_id];
|
||||||
|
if (!systemKills) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return systemKills;
|
||||||
|
}, [kills, solar_system_id]);
|
||||||
|
|
||||||
|
const dbClick = useDoubleClick(() => {
|
||||||
|
outCommand({
|
||||||
|
type: OutCommand.openSettings,
|
||||||
|
data: {
|
||||||
|
system_id: solar_system_id.toString(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const showHandlers = isConnecting || hoverNodeId === id;
|
||||||
|
const dropHandler = isConnecting ? 'all' : 'none';
|
||||||
|
|
||||||
|
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
|
||||||
|
const regionClass = showKSpaceBG ? SpaceToClass[space] : null;
|
||||||
|
|
||||||
|
const hasTempName = isTempSystemNameEnabled && temporaryName;
|
||||||
|
|
||||||
|
// systemName: if temporary name is enabled and present, use it; otherwise use solar_system_name
|
||||||
|
const systemName = hasTempName
|
||||||
|
? temporaryName
|
||||||
|
: solar_system_name;
|
||||||
|
|
||||||
|
// hsCustomLabel: if temporary name is enabled and present, show region_name, otherwise labelCustom
|
||||||
|
const hsCustomLabel = hasTempName
|
||||||
|
? region_name
|
||||||
|
: labelCustom;
|
||||||
|
|
||||||
|
// whCustomLabel: default to solar_system_name; if that's falsy, use labelCustom
|
||||||
|
const whCustomLabel = solar_system_name || labelCustom;
|
||||||
|
|
||||||
|
// customLabel: if wormhole, use whCustomLabel; otherwise hsCustomLabel
|
||||||
|
const customLabel = isWormhole
|
||||||
|
? whCustomLabel
|
||||||
|
: hsCustomLabel;
|
||||||
|
|
||||||
|
// whCustomName: if name differs from solar_system_name, use name; otherwise blank
|
||||||
|
const whCustomName = (name !== solar_system_name)
|
||||||
|
? name
|
||||||
|
: '';
|
||||||
|
|
||||||
|
// hsSuffix: if name differs from solar_system_name, append name; otherwise blank
|
||||||
|
const needsHsSuffix = (name !== solar_system_name);
|
||||||
|
const hsSuffix = needsHsSuffix
|
||||||
|
? name
|
||||||
|
: '';
|
||||||
|
|
||||||
|
// hsCustomName: if there's a temp name, show "solar_system_name + suffix", otherwise "region_name + suffix"
|
||||||
|
const hsCustomName = hasTempName
|
||||||
|
? `${solar_system_name} ${hsSuffix}`
|
||||||
|
: `${region_name} ${hsSuffix}`;
|
||||||
|
|
||||||
|
// customName: if wormhole, use whCustomName; otherwise hsCustomName
|
||||||
|
const customName = isWormhole
|
||||||
|
? whCustomName
|
||||||
|
: hsCustomName;
|
||||||
|
|
||||||
|
const [unsplashedLeft, unsplashedRight] = useMemo(() => {
|
||||||
|
if (!isShowUnsplashedSignatures) {
|
||||||
|
return [[], []];
|
||||||
|
}
|
||||||
|
return prepareUnsplashedChunks(
|
||||||
|
signatures
|
||||||
|
.filter(s => s.group === 'Wormhole' && !s.linked_system)
|
||||||
|
.map(s => ({
|
||||||
|
eve_id: s.eve_id,
|
||||||
|
type: s.type,
|
||||||
|
custom_info: s.custom_info,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
}, [isShowUnsplashedSignatures, signatures]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{visible && (
|
||||||
|
<div className={classes.Bookmarks}>
|
||||||
|
{customLabel !== '' && (
|
||||||
|
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
|
||||||
|
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] ">{customLabel}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{is_shattered && (
|
||||||
|
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered)}>
|
||||||
|
<span className={clsx('pi pi-chart-pie 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, 'text-[0.65rem]')} />
|
||||||
|
<span className={clsx(classes.text)}>{killsCount}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{labelsInfo.map(x => (
|
||||||
|
<div key={x.id} className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[x.id])}>
|
||||||
|
{x.shortName}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
classes.RootCustomNode,
|
||||||
|
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={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>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
classes.classSystemName,
|
||||||
|
'flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{systemName}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isWormhole && (
|
||||||
|
<div className={clsx(classes.statics, 'flex gap-[2px] text-[8px]')}>
|
||||||
|
{sortedStatics.map(x => (
|
||||||
|
<WormholeClassComp key={x} id={x} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{effect_name !== null && isWormhole && (
|
||||||
|
<div className={clsx(classes.effect, EFFECT_BACKGROUND_STYLES[effect_name])}></div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={clsx(classes.BottomRow, 'flex items-center gap-[3px]')}>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{tag != null && tag !== '' && (
|
||||||
|
<div className={clsx(classes.TagTitle, 'font-medium')}>{`[${tag}]`}</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className={clsx(classes.customName)}
|
||||||
|
title={`${customName ?? ''} ${labelCustom ?? ''}`}
|
||||||
|
>
|
||||||
|
{customName} {labelCustom}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center ml-auto gap-[2px]">
|
||||||
|
{locked && <i className={clsx(PrimeIcons.LOCK, 'text-[0.45rem] font-bold')} />}
|
||||||
|
{hubs.includes(solar_system_id.toString()) && (
|
||||||
|
<i className={clsx(PrimeIcons.MAP_MARKER, 'text-[0.45rem] font-bold')} />
|
||||||
|
)}
|
||||||
|
{charactersInSystem.length > 0 && (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
classes.localCounter,
|
||||||
|
'flex gap-[2px]'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span className="font-sans text-[0.65rem]">{charactersInSystem.length}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{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 onMouseDownCapture={dbClick} className={classes.Handlers}>
|
||||||
|
<Handle
|
||||||
|
type="target"
|
||||||
|
position={Position.Bottom}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
background: 'none',
|
||||||
|
cursor: 'cell',
|
||||||
|
pointerEvents: dropHandler,
|
||||||
|
opacity: 0,
|
||||||
|
borderRadius: 0 }}
|
||||||
|
id="whole-node-target"
|
||||||
|
/>
|
||||||
|
<Handle
|
||||||
|
type="source"
|
||||||
|
className={clsx(classes.Handle, classes.HandleTop, {
|
||||||
|
[classes.selected]: selected,
|
||||||
|
[classes.Tick]: isThickConnections,
|
||||||
|
})}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: '55%',
|
||||||
|
background: 'none',
|
||||||
|
cursor: 'cell',
|
||||||
|
opacity: 0,
|
||||||
|
borderRadius: 0,
|
||||||
|
visibility: showHandlers ? 'visible' : 'hidden',}}
|
||||||
|
position={Position.Top}
|
||||||
|
id="a"
|
||||||
|
/>
|
||||||
|
<Handle
|
||||||
|
type="source"
|
||||||
|
className={clsx(classes.Handle, classes.HandleRight, {
|
||||||
|
[classes.selected]: selected,
|
||||||
|
[classes.Tick]: isThickConnections,
|
||||||
|
})}
|
||||||
|
style={{ visibility: showHandlers ? 'visible' : 'hidden', cursor: 'cell', zIndex: 10 }}
|
||||||
|
position={Position.Right}
|
||||||
|
id="b"
|
||||||
|
/>
|
||||||
|
<Handle
|
||||||
|
type="source"
|
||||||
|
className={clsx(classes.Handle, classes.HandleBottom, {
|
||||||
|
[classes.selected]: selected,
|
||||||
|
[classes.Tick]: isThickConnections,
|
||||||
|
})}
|
||||||
|
style={{ visibility: 'hidden', cursor: 'cell' }}
|
||||||
|
position={Position.Bottom}
|
||||||
|
id="c"
|
||||||
|
/>
|
||||||
|
<Handle
|
||||||
|
type="source"
|
||||||
|
className={clsx(classes.Handle, classes.HandleLeft, {
|
||||||
|
[classes.selected]: selected,
|
||||||
|
[classes.Tick]: isThickConnections,
|
||||||
|
})}
|
||||||
|
style={{ visibility: showHandlers ? 'visible' : 'hidden', cursor: 'cell', zIndex: 10 }}
|
||||||
|
position={Position.Left}
|
||||||
|
id="d"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -1,271 +0,0 @@
|
|||||||
import { memo, useMemo } from 'react';
|
|
||||||
import { Handle, Position, WrapNodeProps } from 'reactflow';
|
|
||||||
import { MapSolarSystemType } from '../../map.types';
|
|
||||||
import classes from './SolarSystemNode.module.scss';
|
|
||||||
import clsx from 'clsx';
|
|
||||||
import {
|
|
||||||
EFFECT_BACKGROUND_STYLES,
|
|
||||||
LABELS_INFO,
|
|
||||||
LABELS_ORDER,
|
|
||||||
MARKER_BOOKMARK_BG_STYLES,
|
|
||||||
STATUS_CLASSES,
|
|
||||||
} 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 { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
|
|
||||||
import { getSystemClassStyles } 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';
|
|
||||||
import { OutCommand } from '@/hooks/Mapper/types';
|
|
||||||
import { useDoubleClick } from '@/hooks/Mapper/hooks/useDoubleClick.ts';
|
|
||||||
import { REGIONS_MAP, Spaces } from '@/hooks/Mapper/constants';
|
|
||||||
|
|
||||||
const SpaceToClass: Record<string, string> = {
|
|
||||||
[Spaces.Caldari]: classes.Caldaria,
|
|
||||||
[Spaces.Matar]: classes.Mataria,
|
|
||||||
[Spaces.Amarr]: classes.Amarria,
|
|
||||||
[Spaces.Gallente]: classes.Gallente,
|
|
||||||
};
|
|
||||||
|
|
||||||
const sortedLabels = (labels: string[]) => {
|
|
||||||
if (!labels) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return LABELS_ORDER.filter(x => labels.includes(x)).map(x => LABELS_INFO[x]);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getActivityType = (count: number) => {
|
|
||||||
if (count <= 5) {
|
|
||||||
return 'activityNormal';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count <= 30) {
|
|
||||||
return 'activityWarn';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'activityDanger';
|
|
||||||
};
|
|
||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
|
||||||
export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarSystemType>) => {
|
|
||||||
const {
|
|
||||||
system_class,
|
|
||||||
security,
|
|
||||||
class_title,
|
|
||||||
solar_system_id,
|
|
||||||
statics,
|
|
||||||
effect_name,
|
|
||||||
region_name,
|
|
||||||
region_id,
|
|
||||||
is_shattered,
|
|
||||||
solar_system_name,
|
|
||||||
} = data.system_static_info;
|
|
||||||
|
|
||||||
const { locked, name, tag, status, labels, id } = data || {};
|
|
||||||
|
|
||||||
const customName = solar_system_name !== name ? name : undefined;
|
|
||||||
|
|
||||||
const {
|
|
||||||
data: {
|
|
||||||
characters,
|
|
||||||
presentCharacters,
|
|
||||||
wormholesData,
|
|
||||||
hubs,
|
|
||||||
kills,
|
|
||||||
userCharacters,
|
|
||||||
isConnecting,
|
|
||||||
hoverNodeId,
|
|
||||||
visibleNodes,
|
|
||||||
showKSpaceBG,
|
|
||||||
},
|
|
||||||
outCommand,
|
|
||||||
} = useMapState();
|
|
||||||
|
|
||||||
const visible = useMemo(() => visibleNodes.has(id), [id, visibleNodes]);
|
|
||||||
|
|
||||||
const charactersInSystem = useMemo(() => {
|
|
||||||
return characters.filter(c => c.location?.solar_system_id === solar_system_id).filter(c => c.online);
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, [characters, presentCharacters, solar_system_id]);
|
|
||||||
|
|
||||||
const isWormhole = isWormholeSpace(system_class);
|
|
||||||
const classTitleColor = useMemo(
|
|
||||||
() => getSystemClassStyles({ systemClass: system_class, security }),
|
|
||||||
[security, system_class],
|
|
||||||
);
|
|
||||||
const sortedStatics = useMemo(() => sortWHClasses(wormholesData, statics), [wormholesData, statics]);
|
|
||||||
const lebM = useMemo(() => new LabelsManager(labels ?? ''), [labels]);
|
|
||||||
const labelsInfo = useMemo(() => sortedLabels(lebM.list), [lebM]);
|
|
||||||
const labelCustom = useMemo(() => lebM.customLabel, [lebM]);
|
|
||||||
|
|
||||||
const killsCount = useMemo(() => {
|
|
||||||
const systemKills = kills[solar_system_id];
|
|
||||||
if (!systemKills) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return systemKills;
|
|
||||||
}, [kills, solar_system_id]);
|
|
||||||
|
|
||||||
const hasUserCharacters = useMemo(() => {
|
|
||||||
return charactersInSystem.some(x => userCharacters.includes(x.eve_id));
|
|
||||||
}, [charactersInSystem, userCharacters]);
|
|
||||||
|
|
||||||
const dbClick = useDoubleClick(() => {
|
|
||||||
outCommand({
|
|
||||||
type: OutCommand.openSettings,
|
|
||||||
data: {
|
|
||||||
system_id: solar_system_id.toString(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const showHandlers = isConnecting || hoverNodeId === id;
|
|
||||||
|
|
||||||
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
|
|
||||||
const regionClass = showKSpaceBG ? SpaceToClass[space] : null;
|
|
||||||
|
|
||||||
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>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{is_shattered && (
|
|
||||||
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered)}>
|
|
||||||
<span className={clsx('pi pi-chart-pie', classes.icon)} />
|
|
||||||
</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(classes.text)}>{killsCount}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{labelsInfo.map(x => (
|
|
||||||
<div key={x.id} className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[x.id])}>
|
|
||||||
{x.shortName}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div
|
|
||||||
className={clsx(classes.RootCustomNode, regionClass, classes[STATUS_CLASSES[status]], {
|
|
||||||
[classes.selected]: selected,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{visible && (
|
|
||||||
<>
|
|
||||||
<div className={classes.HeadRow}>
|
|
||||||
<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.classSystemName,
|
|
||||||
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{solar_system_name}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isWormhole && (
|
|
||||||
<div className={classes.statics}>
|
|
||||||
{sortedStatics.map(x => (
|
|
||||||
<WormholeClassComp key={x} id={x} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{effect_name !== null && isWormhole && (
|
|
||||||
<div className={clsx(classes.effect, EFFECT_BACKGROUND_STYLES[effect_name])}></div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
|
|
||||||
{customName && (
|
|
||||||
<div className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-blue-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5">
|
|
||||||
{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 />}
|
|
||||||
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div onMouseDownCapture={dbClick} className={classes.Handlers}>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
className={clsx(classes.Handle, classes.HandleTop, { [classes.selected]: selected })}
|
|
||||||
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
|
|
||||||
position={Position.Top}
|
|
||||||
id="a"
|
|
||||||
/>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
className={clsx(classes.Handle, classes.HandleRight, { [classes.selected]: selected })}
|
|
||||||
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
|
|
||||||
position={Position.Right}
|
|
||||||
id="b"
|
|
||||||
/>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
className={clsx(classes.Handle, classes.HandleBottom, { [classes.selected]: selected })}
|
|
||||||
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
|
|
||||||
position={Position.Bottom}
|
|
||||||
id="c"
|
|
||||||
/>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
className={clsx(classes.Handle, classes.HandleLeft, { [classes.selected]: selected })}
|
|
||||||
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
|
|
||||||
position={Position.Left}
|
|
||||||
id="d"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
@@ -6,7 +6,7 @@ $pastel-green: #88b04b;
|
|||||||
$pastel-yellow: #ffdd59;
|
$pastel-yellow: #ffdd59;
|
||||||
$dark-bg: #2d2d2d;
|
$dark-bg: #2d2d2d;
|
||||||
$text-color: #ffffff;
|
$text-color: #ffffff;
|
||||||
$tooltip-bg: #202020; // Темный фон для подсказок
|
$tooltip-bg: #202020; // Dark background for tooltips
|
||||||
|
|
||||||
.RootCustomNode {
|
.RootCustomNode {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -80,8 +80,8 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.selected {
|
.selected {
|
||||||
border-color: $pastel-pink;
|
border-color: var($pastel-pink, #d291bc);
|
||||||
box-shadow: 0 0 10px #9a1af1c2;
|
box-shadow: 0 0 10px #9a1af1c2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,50 +93,62 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
|||||||
border: 1px solid $pastel-pink;
|
border: 1px solid $pastel-pink;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.eve-system-status-home {
|
.eve-system-status-home {
|
||||||
border: 1px solid darken($eve-solar-system-status-color-home, 30%);
|
border: 1px solid var(--eve-solar-system-status-color-home-dark30);
|
||||||
background-image: linear-gradient(275deg, $eve-solar-system-status-friendly, transparent);
|
background-image: linear-gradient(
|
||||||
|
275deg,
|
||||||
|
var(--eve-solar-system-status-friendly),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
&.selected {
|
&.selected {
|
||||||
border-color: $eve-solar-system-status-color-home;
|
border-color: var(--eve-solar-system-status-color-home);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.eve-system-status-friendly {
|
||||||
&.eve-system-status-friendly {
|
border: 1px solid var(--eve-solar-system-status-color-friendly-dark20);
|
||||||
border: 1px solid darken($eve-solar-system-status-color-friendly, 20%);
|
background-image: linear-gradient(
|
||||||
background-image: linear-gradient(275deg, darken($eve-solar-system-status-friendly, 30%), transparent);
|
275deg,
|
||||||
|
var(--eve-solar-system-status-friendly-dark30),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
&.selected {
|
&.selected {
|
||||||
border-color: darken($eve-solar-system-status-color-friendly, 5%);
|
border-color: var(--eve-solar-system-status-color-friendly-dark5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.eve-system-status-lookingFor {
|
||||||
&.eve-system-status-lookingFor {
|
border: 1px solid var(--eve-solar-system-status-color-lookingFor-dark15);
|
||||||
border: 1px solid darken($eve-solar-system-status-color-lookingFor, 15%);
|
|
||||||
background-image: linear-gradient(275deg, #45ff8f2f, #457fff2f);
|
background-image: linear-gradient(275deg, #45ff8f2f, #457fff2f);
|
||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
border-color: $pastel-pink;
|
border-color: var(--pastel-pink, #d291bc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.eve-system-status-warning {
|
||||||
&.eve-system-status-warning {
|
background-image: linear-gradient(
|
||||||
background-image: linear-gradient(275deg, $eve-solar-system-status-warning, transparent);
|
275deg,
|
||||||
|
var(--eve-solar-system-status-warning),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
.eve-system-status-dangerous {
|
||||||
&.eve-system-status-dangerous {
|
background-image: linear-gradient(
|
||||||
background-image: linear-gradient(275deg, $eve-solar-system-status-dangerous, transparent);
|
275deg,
|
||||||
|
var(--eve-solar-system-status-dangerous),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
.eve-system-status-target {
|
||||||
&.eve-system-status-target {
|
background-image: linear-gradient(
|
||||||
background-image: linear-gradient(275deg, $eve-solar-system-status-target, transparent);
|
275deg,
|
||||||
|
var(--eve-solar-system-status-target),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.Bookmarks {
|
.Bookmarks {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 0;
|
z-index: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
left: 4px;
|
left: 4px;
|
||||||
|
|
||||||
@@ -182,6 +194,42 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Unsplashed {
|
||||||
|
position: absolute;
|
||||||
|
width: calc(50% - 4px);
|
||||||
|
z-index: -1;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 2px;
|
||||||
|
left: 2px;
|
||||||
|
|
||||||
|
&--right {
|
||||||
|
left: calc(50% + 6px);
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .Signature {
|
||||||
|
width: 13px;
|
||||||
|
height: 4px;
|
||||||
|
position: relative;
|
||||||
|
top: 3px;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 8px;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 2px;
|
||||||
|
font-weight: bolder;
|
||||||
|
padding-left: 3px;
|
||||||
|
padding-right: 3px;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
background-color: #833ca4;
|
||||||
|
|
||||||
|
&:not(:first-child) {
|
||||||
|
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
height: 8px;
|
height: 8px;
|
||||||
@@ -206,10 +254,18 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
|||||||
|
|
||||||
.TagTitle {
|
.TagTitle {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: bold;
|
font-weight: medium;
|
||||||
text-shadow: 0 0 2px rgba(231, 146, 52, 0.73);
|
text-shadow: 0 0 2px rgba(231, 146, 52, 0.73);
|
||||||
|
|
||||||
color: #ffb01d;
|
color: var(--rf-tag-color, #38BDF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Firefox kostyl */
|
||||||
|
@-moz-document url-prefix() {
|
||||||
|
.classSystemName {
|
||||||
|
font-family: inherit !important;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.classSystemName {
|
.classSystemName {
|
||||||
@@ -262,6 +318,12 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
|||||||
& > * {
|
& > * {
|
||||||
line-height: 10px;
|
line-height: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Firefox kostyl */
|
||||||
|
@-moz-document url-prefix() {
|
||||||
|
position: relative;
|
||||||
|
top: -1px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.Handlers {
|
.Handlers {
|
||||||
@@ -299,4 +361,25 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
|||||||
&.HandleLeft {
|
&.HandleLeft {
|
||||||
left: -2px;
|
left: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.Tick {
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
|
||||||
|
&.HandleTop {
|
||||||
|
top: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.HandleRight {
|
||||||
|
right: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.HandleBottom {
|
||||||
|
bottom: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.HandleLeft {
|
||||||
|
left: -3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
import { memo } from 'react';
|
||||||
|
import { Handle, Position } from 'reactflow';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
|
import classes from './SolarSystemNodeDefault.module.scss';
|
||||||
|
import { PrimeIcons } from 'primereact/api';
|
||||||
|
|
||||||
|
import { useSolarSystemNode } from '../../hooks/useSolarSystemNode';
|
||||||
|
|
||||||
|
import {
|
||||||
|
MARKER_BOOKMARK_BG_STYLES,
|
||||||
|
STATUS_CLASSES,
|
||||||
|
EFFECT_BACKGROUND_STYLES,
|
||||||
|
} from '@/hooks/Mapper/components/map/constants';
|
||||||
|
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
|
||||||
|
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
|
||||||
|
|
||||||
|
|
||||||
|
export const SolarSystemNodeDefault = memo((props) => {
|
||||||
|
const nodeVars = useSolarSystemNode(props);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{nodeVars.visible && (
|
||||||
|
<div className={classes.Bookmarks}>
|
||||||
|
{nodeVars.labelCustom !== '' && (
|
||||||
|
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
|
||||||
|
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] ">
|
||||||
|
{nodeVars.labelCustom}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{nodeVars.isShattered && (
|
||||||
|
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered)}>
|
||||||
|
<span className={clsx('pi pi-chart-pie', classes.icon)} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{nodeVars.killsCount && (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
classes.Bookmark,
|
||||||
|
MARKER_BOOKMARK_BG_STYLES[nodeVars.killsActivityType!]
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className={clsx(classes.BookmarkWithIcon)}>
|
||||||
|
<span className={clsx(PrimeIcons.BOLT, classes.icon)} />
|
||||||
|
<span className={clsx(classes.text)}>{nodeVars.killsCount}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{nodeVars.labelsInfo.map(x => (
|
||||||
|
<div
|
||||||
|
key={x.id}
|
||||||
|
className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[x.id])}
|
||||||
|
>
|
||||||
|
{x.shortName}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
classes.RootCustomNode,
|
||||||
|
nodeVars.regionClass && classes[nodeVars.regionClass],
|
||||||
|
classes[STATUS_CLASSES[nodeVars.status]],
|
||||||
|
{ [classes.selected]: nodeVars.selected },
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{nodeVars.visible && (
|
||||||
|
<>
|
||||||
|
<div className={classes.HeadRow}>
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
classes.classTitle,
|
||||||
|
nodeVars.classTitleColor,
|
||||||
|
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{nodeVars.classTitle ?? '-'}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{nodeVars.tag != null && nodeVars.tag !== '' && (
|
||||||
|
<div className={clsx(classes.TagTitle, 'text-sky-400 font-medium')}>
|
||||||
|
{nodeVars.tag}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
classes.classSystemName,
|
||||||
|
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{nodeVars.systemName}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{nodeVars.isWormhole && (
|
||||||
|
<div className={classes.statics}>
|
||||||
|
{nodeVars.sortedStatics.map(whClass => (
|
||||||
|
<WormholeClassComp key={whClass} id={whClass} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{nodeVars.effectName !== null && nodeVars.isWormhole && (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
classes.effect,
|
||||||
|
EFFECT_BACKGROUND_STYLES[nodeVars.effectName],
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
|
||||||
|
{nodeVars.customName && (
|
||||||
|
<div className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-blue-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5">
|
||||||
|
{nodeVars.customName}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!nodeVars.isWormhole && !nodeVars.customName && (
|
||||||
|
<div
|
||||||
|
className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-stone-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5"
|
||||||
|
>
|
||||||
|
{nodeVars.regionName}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{nodeVars.isWormhole && !nodeVars.customName && <div />}
|
||||||
|
|
||||||
|
<div className="flex items-center justify-end">
|
||||||
|
<div className="flex gap-1 items-center">
|
||||||
|
{nodeVars.locked && (
|
||||||
|
<i
|
||||||
|
className={PrimeIcons.LOCK}
|
||||||
|
style={{ fontSize: '0.45rem', fontWeight: 'bold' }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{nodeVars.hubs.includes(nodeVars.solarSystemId.toString()) && (
|
||||||
|
<i
|
||||||
|
className={PrimeIcons.MAP_MARKER}
|
||||||
|
style={{ fontSize: '0.45rem', fontWeight: 'bold' }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{nodeVars.charactersInSystem.length > 0 && (
|
||||||
|
<div
|
||||||
|
className={clsx(classes.localCounter, {
|
||||||
|
['text-amber-300']: nodeVars.hasUserCharacters,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<i className="pi pi-users" style={{ fontSize: '0.50rem' }} />
|
||||||
|
<span className="font-sans">
|
||||||
|
{nodeVars.charactersInSystem.length}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{nodeVars.visible && (
|
||||||
|
<>
|
||||||
|
{nodeVars.unsplashedLeft.length > 0 && (
|
||||||
|
<div className={classes.Unsplashed}>
|
||||||
|
{nodeVars.unsplashedLeft.map(sig => (
|
||||||
|
<UnsplashedSignature key={sig.sig_id} signature={sig} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{nodeVars.unsplashedRight.length > 0 && (
|
||||||
|
<div className={clsx(classes.Unsplashed, classes['Unsplashed--right'])}>
|
||||||
|
{nodeVars.unsplashedRight.map(sig => (
|
||||||
|
<UnsplashedSignature key={sig.sig_id} signature={sig} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div onMouseDownCapture={nodeVars.dbClick} className={classes.Handlers}>
|
||||||
|
<Handle
|
||||||
|
type="source"
|
||||||
|
className={clsx(classes.Handle, classes.HandleTop, {
|
||||||
|
[classes.selected]: nodeVars.selected,
|
||||||
|
[classes.Tick]: nodeVars.isThickConnections,
|
||||||
|
})}
|
||||||
|
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
|
||||||
|
position={Position.Top}
|
||||||
|
id="a"
|
||||||
|
/>
|
||||||
|
<Handle
|
||||||
|
type="source"
|
||||||
|
className={clsx(classes.Handle, classes.HandleRight, {
|
||||||
|
[classes.selected]: nodeVars.selected,
|
||||||
|
[classes.Tick]: nodeVars.isThickConnections,
|
||||||
|
})}
|
||||||
|
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
|
||||||
|
position={Position.Right}
|
||||||
|
id="b"
|
||||||
|
/>
|
||||||
|
<Handle
|
||||||
|
type="source"
|
||||||
|
className={clsx(classes.Handle, classes.HandleBottom, {
|
||||||
|
[classes.selected]: nodeVars.selected,
|
||||||
|
[classes.Tick]: nodeVars.isThickConnections,
|
||||||
|
})}
|
||||||
|
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
|
||||||
|
position={Position.Bottom}
|
||||||
|
id="c"
|
||||||
|
/>
|
||||||
|
<Handle
|
||||||
|
type="source"
|
||||||
|
className={clsx(classes.Handle, classes.HandleLeft, {
|
||||||
|
[classes.selected]: nodeVars.selected,
|
||||||
|
[classes.Tick]: nodeVars.isThickConnections,
|
||||||
|
})}
|
||||||
|
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
|
||||||
|
position={Position.Left}
|
||||||
|
id="d"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -0,0 +1,402 @@
|
|||||||
|
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
|
||||||
|
|
||||||
|
$pastel-blue: #5a7d9a;
|
||||||
|
$pastel-pink: #d291bc;
|
||||||
|
$pastel-green: #88b04b;
|
||||||
|
$pastel-yellow: #ffdd59;
|
||||||
|
$dark-bg: #2d2d2d;
|
||||||
|
$text-color: #ffffff;
|
||||||
|
$tooltip-bg: #202020; // Dark background for tooltips
|
||||||
|
|
||||||
|
.RootCustomNode {
|
||||||
|
display: flex;
|
||||||
|
width: 130px;
|
||||||
|
height: 34px;
|
||||||
|
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 2px 6px;
|
||||||
|
font-size: 10px;
|
||||||
|
|
||||||
|
background-color: var(--rf-node-bg-color, #202020) !important;
|
||||||
|
color: var(--rf-text-color, #ffffff);
|
||||||
|
font-family: var(--rf-node-font-family, inherit) !important;
|
||||||
|
font-weight: var(--rf-node-font-weight, inherit) !important;
|
||||||
|
|
||||||
|
box-shadow: 0 0 5px rgba($dark-bg, 0.5);
|
||||||
|
border: 1px solid darken($pastel-blue, 10%);
|
||||||
|
border-radius: 5px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&.Mataria,
|
||||||
|
&.Amarria,
|
||||||
|
&.Gallente,
|
||||||
|
&.Caldaria {
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: 50% 50%;
|
||||||
|
z-index: -1;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Mataria {
|
||||||
|
&::before {
|
||||||
|
background-image: url('/images/mataria-180.png');
|
||||||
|
opacity: 0.6;
|
||||||
|
background-position-x: 1px;
|
||||||
|
background-position-y: -14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Caldaria {
|
||||||
|
&::before {
|
||||||
|
background-image: url('/images/caldaria-180.png');
|
||||||
|
opacity: 0.6;
|
||||||
|
background-position-x: 1px;
|
||||||
|
background-position-y: -10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Amarria {
|
||||||
|
&::before {
|
||||||
|
opacity: 0.45;
|
||||||
|
background-image: url('/images/amarr-180.png');
|
||||||
|
background-position-x: 0;
|
||||||
|
background-position-y: -13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Gallente {
|
||||||
|
&::before {
|
||||||
|
opacity: 0.5;
|
||||||
|
background-image: url('/images/gallente-180.png');
|
||||||
|
background-position-x: 1px;
|
||||||
|
background-position-y: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
border-color: var($pastel-pink, #d291bc);
|
||||||
|
box-shadow: 0 0 10px #9a1af1c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
background-color: $tooltip-bg;
|
||||||
|
color: $text-color;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid $pastel-pink;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eve-system-status-home {
|
||||||
|
border: 1px solid var(--eve-solar-system-status-color-home-dark30);
|
||||||
|
background-image: linear-gradient(
|
||||||
|
275deg,
|
||||||
|
var(--eve-solar-system-status-friendly),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
&.selected {
|
||||||
|
border-color: var(--eve-solar-system-status-color-home);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eve-system-status-friendly {
|
||||||
|
border: 1px solid var(--eve-solar-system-status-color-friendly-dark20);
|
||||||
|
background-image: linear-gradient(
|
||||||
|
275deg,
|
||||||
|
var(--eve-solar-system-status-friendly-dark30),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
&.selected {
|
||||||
|
border-color: var(--eve-solar-system-status-color-friendly-dark5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eve-system-status-lookingFor {
|
||||||
|
border: 1px solid var(--eve-solar-system-status-color-lookingFor-dark15);
|
||||||
|
background-image: linear-gradient(275deg, #45ff8f2f, #457fff2f);
|
||||||
|
&.selected {
|
||||||
|
border-color: var(--pastel-pink, #d291bc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eve-system-status-warning {
|
||||||
|
background-image: linear-gradient(
|
||||||
|
275deg,
|
||||||
|
var(--eve-solar-system-status-warning),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
.eve-system-status-dangerous {
|
||||||
|
background-image: linear-gradient(
|
||||||
|
275deg,
|
||||||
|
var(--eve-solar-system-status-dangerous),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
.eve-system-status-target {
|
||||||
|
background-image: linear-gradient(
|
||||||
|
275deg,
|
||||||
|
var(--eve-solar-system-status-target),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Bookmarks {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
left: 4px;
|
||||||
|
|
||||||
|
& > .Bookmark {
|
||||||
|
min-width: 13px;
|
||||||
|
height: 22px;
|
||||||
|
position: relative;
|
||||||
|
top: -13px;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 8px;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 2px;
|
||||||
|
font-weight: bolder;
|
||||||
|
padding-left: 3px;
|
||||||
|
padding-right: 3px;
|
||||||
|
|
||||||
|
//background-color: #833ca4;
|
||||||
|
|
||||||
|
&:not(:first-child) {
|
||||||
|
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.BookmarkWithIcon {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: -2px;
|
||||||
|
text-shadow: 0 0 3px rgba(0, 0, 0, 1);
|
||||||
|
padding-right: 2px;
|
||||||
|
|
||||||
|
& > .icon {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
font-size: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .text {
|
||||||
|
margin-top: 1px;
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Unsplashed {
|
||||||
|
position: absolute;
|
||||||
|
width: calc(50% - 4px);
|
||||||
|
z-index: -1;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 2px;
|
||||||
|
left: 2px;
|
||||||
|
|
||||||
|
&--right {
|
||||||
|
left: calc(50% + 6px);
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .Signature {
|
||||||
|
width: 13px;
|
||||||
|
height: 4px;
|
||||||
|
position: relative;
|
||||||
|
top: 3px;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 8px;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 2px;
|
||||||
|
font-weight: bolder;
|
||||||
|
padding-left: 3px;
|
||||||
|
padding-right: 3px;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
background-color: #833ca4;
|
||||||
|
|
||||||
|
&:not(:first-child) {
|
||||||
|
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
font-size: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.HeadRow {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 3px;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
|
||||||
|
.classTitle {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-shadow: 0 0 2px rgb(0 0 0 / 73%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.TagTitle {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: medium;
|
||||||
|
text-shadow: 0 0 2px rgba(231, 146, 52, 0.73);
|
||||||
|
|
||||||
|
color: var(--rf-tag-color, #38BDF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Firefox kostyl */
|
||||||
|
@-moz-document url-prefix() {
|
||||||
|
.classSystemName {
|
||||||
|
font-family: inherit !important;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.classSystemName {
|
||||||
|
font-family: inherit !important;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.solarSystemName {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.BottomRow {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
height: 19px;
|
||||||
|
|
||||||
|
.regionName {
|
||||||
|
color: var(--rf-region-name, #D6D3D1)
|
||||||
|
}
|
||||||
|
|
||||||
|
.customName {
|
||||||
|
color: var(--rf-custom-name, #93C5FD)
|
||||||
|
}
|
||||||
|
|
||||||
|
.localCounter {
|
||||||
|
display: flex;
|
||||||
|
//align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
|
||||||
|
.hasUserCharacters {
|
||||||
|
color: var(--rf-has-user-characters, #fbbf24);
|
||||||
|
}
|
||||||
|
|
||||||
|
& > i {
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
font-size: 9px;
|
||||||
|
line-height: 9px;
|
||||||
|
font-weight: 500;
|
||||||
|
//margin-top: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.effect {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
margin-top: -2px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-left: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statics {
|
||||||
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
font-size: 8px;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
line-height: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Firefox kostyl */
|
||||||
|
@-moz-document url-prefix() {
|
||||||
|
position: relative;
|
||||||
|
top: -1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Handlers {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 2;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Handle {
|
||||||
|
min-width: initial;
|
||||||
|
min-height: initial;
|
||||||
|
border: 1px solid $pastel-blue;
|
||||||
|
width: 5px;
|
||||||
|
height: 5px;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
border-color: $pastel-pink;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.HandleTop {
|
||||||
|
top: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.HandleRight {
|
||||||
|
right: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.HandleBottom {
|
||||||
|
bottom: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.HandleLeft {
|
||||||
|
left: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Tick {
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
|
||||||
|
&.HandleTop {
|
||||||
|
top: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.HandleRight {
|
||||||
|
right: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.HandleBottom {
|
||||||
|
bottom: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.HandleLeft {
|
||||||
|
left: -3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
import { memo } from 'react';
|
||||||
|
import { Handle, Position } from 'reactflow';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
|
import classes from './SolarSystemNodeTheme.module.scss';
|
||||||
|
import { PrimeIcons } from 'primereact/api';
|
||||||
|
|
||||||
|
import { useSolarSystemNode } from '../../hooks/useSolarSystemNode';
|
||||||
|
|
||||||
|
import {
|
||||||
|
MARKER_BOOKMARK_BG_STYLES,
|
||||||
|
STATUS_CLASSES,
|
||||||
|
EFFECT_BACKGROUND_STYLES,
|
||||||
|
} from '@/hooks/Mapper/components/map/constants';
|
||||||
|
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
|
||||||
|
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
|
||||||
|
|
||||||
|
|
||||||
|
export const SolarSystemNodeTheme = memo((props) => {
|
||||||
|
const nodeVars = useSolarSystemNode(props);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{nodeVars.visible && (
|
||||||
|
<div className={classes.Bookmarks}>
|
||||||
|
{nodeVars.labelCustom !== '' && (
|
||||||
|
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
|
||||||
|
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] ">
|
||||||
|
{nodeVars.labelCustom}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{nodeVars.isShattered && (
|
||||||
|
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered)}>
|
||||||
|
<span className={clsx('pi pi-chart-pie', classes.icon)} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{nodeVars.killsCount && (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
classes.Bookmark,
|
||||||
|
MARKER_BOOKMARK_BG_STYLES[nodeVars.killsActivityType!]
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className={clsx(classes.BookmarkWithIcon)}>
|
||||||
|
<span className={clsx(PrimeIcons.BOLT, classes.icon)} />
|
||||||
|
<span className={clsx(classes.text)}>{nodeVars.killsCount}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{nodeVars.labelsInfo.map(x => (
|
||||||
|
<div
|
||||||
|
key={x.id}
|
||||||
|
className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[x.id])}
|
||||||
|
>
|
||||||
|
{x.shortName}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
classes.RootCustomNode,
|
||||||
|
nodeVars.regionClass && classes[nodeVars.regionClass],
|
||||||
|
classes[STATUS_CLASSES[nodeVars.status]],
|
||||||
|
{ [classes.selected]: nodeVars.selected },
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{nodeVars.visible && (
|
||||||
|
<>
|
||||||
|
<div className={classes.HeadRow}>
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
classes.classTitle,
|
||||||
|
nodeVars.classTitleColor,
|
||||||
|
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{nodeVars.classTitle ?? '-'}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{nodeVars.tag != null && nodeVars.tag !== '' && (
|
||||||
|
<div className={clsx(classes.TagTitle)}>
|
||||||
|
{nodeVars.tag}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
classes.classSystemName,
|
||||||
|
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] flex-grow overflow-hidden text-ellipsis whitespace-nowrap',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{nodeVars.systemName}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{nodeVars.isWormhole && (
|
||||||
|
<div className={classes.statics}>
|
||||||
|
{nodeVars.sortedStatics.map(whClass => (
|
||||||
|
<WormholeClassComp key={whClass} id={whClass} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{nodeVars.effectName !== null && nodeVars.isWormhole && (
|
||||||
|
<div className={clsx(classes.effect, EFFECT_BACKGROUND_STYLES[nodeVars.effectName])} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
|
||||||
|
{nodeVars.customName && (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
classes.CustomName,
|
||||||
|
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] whitespace-nowrap overflow-hidden text-ellipsis mr-0.5',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{nodeVars.customName}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!nodeVars.isWormhole && !nodeVars.customName && (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
classes.RegionName,
|
||||||
|
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] whitespace-nowrap overflow-hidden text-ellipsis mr-0.5',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{nodeVars.regionName}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{nodeVars.isWormhole && !nodeVars.customName && <div />}
|
||||||
|
|
||||||
|
<div className="flex items-center justify-end">
|
||||||
|
<div className="flex gap-1 items-center">
|
||||||
|
{nodeVars.locked && (
|
||||||
|
<i
|
||||||
|
className={PrimeIcons.LOCK}
|
||||||
|
style={{ fontSize: '0.45rem', fontWeight: 'bold' }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{nodeVars.hubs.includes(nodeVars.solarSystemId.toString()) && (
|
||||||
|
<i
|
||||||
|
className={PrimeIcons.MAP_MARKER}
|
||||||
|
style={{ fontSize: '0.45rem', fontWeight: 'bold' }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{nodeVars.charactersInSystem.length > 0 && (
|
||||||
|
<div
|
||||||
|
className={clsx(classes.localCounter, {
|
||||||
|
[classes.hasUserCharacters]: nodeVars.hasUserCharacters,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<i className="pi pi-users" style={{ fontSize: '0.50rem' }} />
|
||||||
|
<span className="font-sans">{nodeVars.charactersInSystem.length}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{nodeVars.visible && (
|
||||||
|
<>
|
||||||
|
{nodeVars.unsplashedLeft.length > 0 && (
|
||||||
|
<div className={classes.Unsplashed}>
|
||||||
|
{nodeVars.unsplashedLeft.map(sig => (
|
||||||
|
<UnsplashedSignature key={sig.sig_id} signature={sig} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{nodeVars.unsplashedRight.length > 0 && (
|
||||||
|
<div className={clsx(classes.Unsplashed, classes['Unsplashed--right'])}>
|
||||||
|
{nodeVars.unsplashedRight.map(sig => (
|
||||||
|
<UnsplashedSignature key={sig.sig_id} signature={sig} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div onMouseDownCapture={nodeVars.dbClick} className={classes.Handlers}>
|
||||||
|
<Handle
|
||||||
|
type="source"
|
||||||
|
className={clsx(classes.Handle, classes.HandleTop, {
|
||||||
|
[classes.selected]: nodeVars.selected,
|
||||||
|
[classes.Tick]: nodeVars.isThickConnections,
|
||||||
|
})}
|
||||||
|
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
|
||||||
|
position={Position.Top}
|
||||||
|
id="a"
|
||||||
|
/>
|
||||||
|
<Handle
|
||||||
|
type="source"
|
||||||
|
className={clsx(classes.Handle, classes.HandleRight, {
|
||||||
|
[classes.selected]: nodeVars.selected,
|
||||||
|
[classes.Tick]: nodeVars.isThickConnections,
|
||||||
|
})}
|
||||||
|
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
|
||||||
|
position={Position.Right}
|
||||||
|
id="b"
|
||||||
|
/>
|
||||||
|
<Handle
|
||||||
|
type="source"
|
||||||
|
className={clsx(classes.Handle, classes.HandleBottom, {
|
||||||
|
[classes.selected]: nodeVars.selected,
|
||||||
|
[classes.Tick]: nodeVars.isThickConnections,
|
||||||
|
})}
|
||||||
|
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
|
||||||
|
position={Position.Bottom}
|
||||||
|
id="c"
|
||||||
|
/>
|
||||||
|
<Handle
|
||||||
|
type="source"
|
||||||
|
className={clsx(classes.Handle, classes.HandleLeft, {
|
||||||
|
[classes.selected]: nodeVars.selected,
|
||||||
|
[classes.Tick]: nodeVars.isThickConnections,
|
||||||
|
})}
|
||||||
|
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
|
||||||
|
position={Position.Left}
|
||||||
|
id="d"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -1 +1,2 @@
|
|||||||
export * from './SolarSystemNode';
|
export * from './SolarSystemNodeDefault';
|
||||||
|
export * from './SolarSystemNodeTheme';
|
||||||
|
|||||||
@@ -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 {
|
export enum SOLAR_SYSTEM_CLASS_IDS {
|
||||||
ccp1 = -1,
|
ccp1 = -1,
|
||||||
@@ -712,6 +712,13 @@ export const STATUS_CLASSES: Record<number, string> = {
|
|||||||
[STATUSES.dangerous]: 'eve-system-status-dangerous',
|
[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_ORDER = [MassState.verge, MassState.half, MassState.normal];
|
||||||
|
|
||||||
export const MASS_STATE_NAMES = {
|
export const MASS_STATE_NAMES = {
|
||||||
@@ -720,16 +727,41 @@ export const MASS_STATE_NAMES = {
|
|||||||
[MassState.verge]: 'Verge of collapse',
|
[MassState.verge]: 'Verge of collapse',
|
||||||
};
|
};
|
||||||
|
|
||||||
// export const SHIP_SIZES_NAMES_ORDER = [
|
export const SHIP_SIZES_NAMES_ORDER = [
|
||||||
// ShipSizeStatus.small,
|
ShipSizeStatus.small,
|
||||||
// ShipSizeStatus.normal,
|
ShipSizeStatus.medium,
|
||||||
// // ShipSizeStatus.large,
|
ShipSizeStatus.large,
|
||||||
// // ShipSizeStatus.capital,
|
ShipSizeStatus.freight,
|
||||||
// ];
|
ShipSizeStatus.capital,
|
||||||
//
|
];
|
||||||
// export const SHIP_SIZES_NAMES = {
|
|
||||||
// [ShipSizeStatus.small]: 'Frigate',
|
export const SHIP_SIZES_NAMES = {
|
||||||
// [ShipSizeStatus.normal]: 'Normal',
|
[ShipSizeStatus.small]: 'Frigate',
|
||||||
// // [ShipSizeStatus.large]: 'Normal',
|
[ShipSizeStatus.medium]: 'Medium',
|
||||||
// // [ShipSizeStatus.capital]: 'Normal',
|
[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 './getSystemClassStyles';
|
||||||
export * from './getShapeClass';
|
export * from './getShapeClass';
|
||||||
export * from './getBackgroundClass';
|
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) => {
|
return useCallback((systems: CommandAddSystems) => {
|
||||||
const { rf } = ref.current;
|
const { rf } = ref.current;
|
||||||
const nodes = rf.getNodes();
|
const nodes = rf.getNodes();
|
||||||
|
|
||||||
const prepared: Node[] = systems.filter(x => !nodes.some(y => x.id === y.id)).map(convertSystem2Node);
|
const prepared: Node[] = systems.filter(x => !nodes.some(y => x.id === y.id)).map(convertSystem2Node);
|
||||||
rf.addNodes(prepared);
|
rf.addNodes(prepared);
|
||||||
}, []);
|
}, []);
|
||||||
|
|||||||
@@ -5,24 +5,21 @@ import { OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts
|
|||||||
|
|
||||||
export const useMapRemoveSystems = (onSelectionChange: OnMapSelectionChange) => {
|
export const useMapRemoveSystems = (onSelectionChange: OnMapSelectionChange) => {
|
||||||
const rf = useReactFlow();
|
const rf = useReactFlow();
|
||||||
const ref = useRef({ onSelectionChange });
|
const ref = useRef({ onSelectionChange, rf });
|
||||||
ref.current = { onSelectionChange };
|
ref.current = { onSelectionChange, rf };
|
||||||
|
|
||||||
return useCallback(
|
return useCallback((systems: CommandRemoveSystems) => {
|
||||||
(systems: CommandRemoveSystems) => {
|
ref.current.rf.deleteElements({ nodes: systems.map(x => ({ id: `${x}` })) });
|
||||||
rf.deleteElements({ nodes: systems.map(x => ({ id: `${x}` })) });
|
|
||||||
|
|
||||||
const newSelection = rf
|
const newSelection = ref.current.rf
|
||||||
.getNodes()
|
.getNodes()
|
||||||
.filter(x => !systems.includes(parseInt(x.id)))
|
.filter(x => !systems.includes(parseInt(x.id)))
|
||||||
.filter(x => x.selected)
|
.filter(x => x.selected)
|
||||||
.map(x => x.id);
|
.map(x => x.id);
|
||||||
|
|
||||||
ref.current.onSelectionChange({
|
ref.current.onSelectionChange({
|
||||||
systems: newSelection,
|
systems: newSelection,
|
||||||
connections: [],
|
connections: [],
|
||||||
});
|
});
|
||||||
},
|
}, []);
|
||||||
[rf],
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from './useMapHandlers';
|
export * from './useMapHandlers';
|
||||||
export * from './useUpdateNodes';
|
export * from './useUpdateNodes';
|
||||||
|
export * from './useNodesEdgesState';
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
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(() => {
|
||||||
|
// match any element whose entire `class` attribute ends with "-theme"
|
||||||
|
let themeEl = document.querySelector('[class$="-theme"]');
|
||||||
|
|
||||||
|
// If none is found, fall back to the <html> element
|
||||||
|
if (!themeEl) {
|
||||||
|
themeEl = document.documentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 };
|
||||||
|
}
|
||||||
@@ -19,8 +19,6 @@ import {
|
|||||||
MapHandlers,
|
MapHandlers,
|
||||||
} from '@/hooks/Mapper/types/mapHandlers.ts';
|
} from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||||
|
|
||||||
import { useMapEventListener } from '@/hooks/Mapper/events';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useCommandsCharacters,
|
useCommandsCharacters,
|
||||||
useCommandsConnections,
|
useCommandsConnections,
|
||||||
@@ -60,13 +58,16 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
|
|||||||
mapInit(data as CommandInit);
|
mapInit(data as CommandInit);
|
||||||
break;
|
break;
|
||||||
case Commands.addSystems:
|
case Commands.addSystems:
|
||||||
|
setTimeout(() => mapAddSystems(data as CommandAddSystems), 100);
|
||||||
break;
|
break;
|
||||||
case Commands.updateSystems:
|
case Commands.updateSystems:
|
||||||
mapUpdateSystems(data as CommandUpdateSystems);
|
mapUpdateSystems(data as CommandUpdateSystems);
|
||||||
break;
|
break;
|
||||||
case Commands.removeSystems:
|
case Commands.removeSystems:
|
||||||
|
setTimeout(() => removeSystems(data as CommandRemoveSystems), 100);
|
||||||
break;
|
break;
|
||||||
case Commands.addConnections:
|
case Commands.addConnections:
|
||||||
|
setTimeout(() => addConnections(data as CommandAddConnections), 100);
|
||||||
break;
|
break;
|
||||||
case Commands.removeConnections:
|
case Commands.removeConnections:
|
||||||
removeConnections(data as CommandRemoveConnections);
|
removeConnections(data as CommandRemoveConnections);
|
||||||
@@ -111,13 +112,17 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
|
|||||||
connections: [],
|
connections: [],
|
||||||
});
|
});
|
||||||
selectSystem(systemId as CommandSelectSystem);
|
selectSystem(systemId as CommandSelectSystem);
|
||||||
}, 100);
|
}, 500);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Commands.routes:
|
case Commands.routes:
|
||||||
// do nothing here
|
// do nothing here
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Commands.signaturesUpdated:
|
||||||
|
// do nothing here
|
||||||
|
break;
|
||||||
|
|
||||||
case Commands.linkSignatureToSystem:
|
case Commands.linkSignatureToSystem:
|
||||||
// do nothing here
|
// do nothing here
|
||||||
break;
|
break;
|
||||||
@@ -131,20 +136,4 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
|
|||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
useMapEventListener(event => {
|
|
||||||
switch (event.name) {
|
|
||||||
case Commands.addConnections:
|
|
||||||
addConnections(event.data as CommandAddConnections);
|
|
||||||
break;
|
|
||||||
case Commands.addSystems:
|
|
||||||
mapAddSystems(event.data as CommandAddSystems);
|
|
||||||
break;
|
|
||||||
case Commands.removeSystems:
|
|
||||||
removeSystems(event.data as CommandRemoveSystems);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
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];
|
||||||
|
}
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||||
|
import { useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||||
|
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider';
|
||||||
|
import { useDoubleClick } from '@/hooks/Mapper/hooks/useDoubleClick';
|
||||||
|
import { REGIONS_MAP, Spaces } from '@/hooks/Mapper/constants';
|
||||||
|
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace';
|
||||||
|
import { getSystemClassStyles, prepareUnsplashedChunks } from '@/hooks/Mapper/components/map/helpers';
|
||||||
|
import { sortWHClasses } from '@/hooks/Mapper/helpers';
|
||||||
|
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager';
|
||||||
|
import { OutCommand } from '@/hooks/Mapper/types';
|
||||||
|
import { LABELS_INFO, LABELS_ORDER } from '@/hooks/Mapper/components/map/constants';
|
||||||
|
|
||||||
|
function getActivityType(count: number) {
|
||||||
|
if (count <= 5) return 'activityNormal';
|
||||||
|
if (count <= 30) return 'activityWarn';
|
||||||
|
return 'activityDanger';
|
||||||
|
}
|
||||||
|
|
||||||
|
const SpaceToClass: Record<string, string> = {
|
||||||
|
[Spaces.Caldari]: 'Caldaria',
|
||||||
|
[Spaces.Matar]: 'Mataria',
|
||||||
|
[Spaces.Amarr]: 'Amarria',
|
||||||
|
[Spaces.Gallente]: 'Gallente',
|
||||||
|
};
|
||||||
|
|
||||||
|
function sortedLabels(labels: string[]) {
|
||||||
|
if (!labels) return [];
|
||||||
|
return LABELS_ORDER.filter(x => labels.includes(x)).map(x => LABELS_INFO[x]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSolarSystemNode(props: any) {
|
||||||
|
const { data, selected, id } = props;
|
||||||
|
const { system_static_info, system_signatures, locked, name, tag, status, labels, temporary_name } = data;
|
||||||
|
|
||||||
|
const {
|
||||||
|
system_class,
|
||||||
|
security,
|
||||||
|
class_title,
|
||||||
|
solar_system_id,
|
||||||
|
statics,
|
||||||
|
effect_name,
|
||||||
|
region_name,
|
||||||
|
region_id,
|
||||||
|
is_shattered,
|
||||||
|
solar_system_name,
|
||||||
|
} = system_static_info;
|
||||||
|
|
||||||
|
// Global map state
|
||||||
|
const { interfaceSettings } = useMapRootState();
|
||||||
|
const { isShowUnsplashedSignatures } = interfaceSettings;
|
||||||
|
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: {
|
||||||
|
characters,
|
||||||
|
presentCharacters,
|
||||||
|
wormholesData,
|
||||||
|
hubs,
|
||||||
|
kills,
|
||||||
|
userCharacters,
|
||||||
|
isConnecting,
|
||||||
|
hoverNodeId,
|
||||||
|
visibleNodes,
|
||||||
|
showKSpaceBG,
|
||||||
|
isThickConnections,
|
||||||
|
},
|
||||||
|
outCommand,
|
||||||
|
} = useMapState();
|
||||||
|
|
||||||
|
// logic
|
||||||
|
const visible = useMemo(() => visibleNodes.has(id), [id, visibleNodes]);
|
||||||
|
|
||||||
|
const charactersInSystem = useMemo(() => {
|
||||||
|
return characters.filter(c => c.location?.solar_system_id === solar_system_id).filter(c => c.online);
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, [characters, presentCharacters, solar_system_id]);
|
||||||
|
|
||||||
|
const isWormhole = isWormholeSpace(system_class);
|
||||||
|
|
||||||
|
const classTitleColor = useMemo(
|
||||||
|
() => getSystemClassStyles({ systemClass: system_class, security }),
|
||||||
|
[security, system_class],
|
||||||
|
);
|
||||||
|
|
||||||
|
const sortedStatics = useMemo(() => sortWHClasses(wormholesData, statics), [wormholesData, statics]);
|
||||||
|
|
||||||
|
const labelsManager = useMemo(() => new LabelsManager(labels ?? ''), [labels]);
|
||||||
|
const labelsInfo = useMemo(() => sortedLabels(labelsManager.list), [labelsManager]);
|
||||||
|
const labelCustom = useMemo(() => labelsManager.customLabel, [labelsManager]);
|
||||||
|
|
||||||
|
const killsCount = useMemo(() => kills[solar_system_id] ?? null, [kills, solar_system_id]);
|
||||||
|
const killsActivityType = killsCount ? getActivityType(killsCount) : null;
|
||||||
|
|
||||||
|
const hasUserCharacters = useMemo(() => {
|
||||||
|
return charactersInSystem.some(x => userCharacters.includes(x.eve_id));
|
||||||
|
}, [charactersInSystem, userCharacters]);
|
||||||
|
|
||||||
|
const dbClick = useDoubleClick(() => {
|
||||||
|
outCommand({
|
||||||
|
type: OutCommand.openSettings,
|
||||||
|
data: { system_id: solar_system_id.toString() },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const showHandlers = isConnecting || hoverNodeId === id;
|
||||||
|
|
||||||
|
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
|
||||||
|
const regionClass = showKSpaceBG ? SpaceToClass[space] : null;
|
||||||
|
|
||||||
|
const systemName = (isTempSystemNameEnabled && temporary_name) || solar_system_name;
|
||||||
|
const customName =
|
||||||
|
(isTempSystemNameEnabled && temporary_name && name) || (solar_system_name !== name && name);
|
||||||
|
|
||||||
|
const [unsplashedLeft, unsplashedRight] = useMemo(() => {
|
||||||
|
if (!isShowUnsplashedSignatures) {
|
||||||
|
return [[], []];
|
||||||
|
}
|
||||||
|
return prepareUnsplashedChunks(
|
||||||
|
system_signatures
|
||||||
|
.filter(s => s.group === 'Wormhole' && !s.linked_system)
|
||||||
|
.map(s => ({
|
||||||
|
eve_id: s.eve_id,
|
||||||
|
type: s.type,
|
||||||
|
custom_info: s.custom_info,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
}, [isShowUnsplashedSignatures, system_signatures]);
|
||||||
|
|
||||||
|
const nodeVars = {
|
||||||
|
// original props
|
||||||
|
id,
|
||||||
|
selected,
|
||||||
|
// computed
|
||||||
|
visible,
|
||||||
|
isWormhole,
|
||||||
|
classTitleColor,
|
||||||
|
killsCount,
|
||||||
|
killsActivityType,
|
||||||
|
hasUserCharacters,
|
||||||
|
showHandlers,
|
||||||
|
regionClass,
|
||||||
|
systemName,
|
||||||
|
customName,
|
||||||
|
labelCustom,
|
||||||
|
isShattered: is_shattered,
|
||||||
|
tag,
|
||||||
|
status,
|
||||||
|
labelsInfo,
|
||||||
|
dbClick,
|
||||||
|
sortedStatics,
|
||||||
|
effectName: effect_name,
|
||||||
|
regionName: region_name,
|
||||||
|
solarSystemId: solar_system_id,
|
||||||
|
solarSystemName: solar_system_name,
|
||||||
|
locked,
|
||||||
|
hubs,
|
||||||
|
name: name,
|
||||||
|
isConnecting,
|
||||||
|
hoverNodeId,
|
||||||
|
charactersInSystem,
|
||||||
|
unsplashedLeft,
|
||||||
|
unsplashedRight,
|
||||||
|
isThickConnections,
|
||||||
|
classTitle: class_title,
|
||||||
|
temporaryName: temporary_name,
|
||||||
|
};
|
||||||
|
|
||||||
|
return nodeVars;
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { SolarSystemRawType } from '@/hooks/Mapper/types/system';
|
import { SolarSystemRawType } from '@/hooks/Mapper/types/system';
|
||||||
import { SolarSystemConnection } from '@/hooks/Mapper/types';
|
import { SolarSystemConnection } from '@/hooks/Mapper/types';
|
||||||
|
import { XYPosition } from 'reactflow';
|
||||||
|
|
||||||
export type MapSolarSystemType = Omit<SolarSystemRawType, 'position'>;
|
export type MapSolarSystemType = Omit<SolarSystemRawType, 'position'>;
|
||||||
|
|
||||||
@@ -7,3 +8,5 @@ export type OnMapSelectionChange = (event: {
|
|||||||
systems: string[];
|
systems: string[];
|
||||||
connections: Pick<SolarSystemConnection, 'source' | 'target'>[];
|
connections: Pick<SolarSystemConnection, 'source' | 'target'>[];
|
||||||
}) => void;
|
}) => void;
|
||||||
|
|
||||||
|
export type OnMapAddSystemCallback = (props: { coordinates: XYPosition | null }) => void;
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
@import './eve-common-variables';
|
||||||
|
@import './eve-common';
|
||||||
|
|
||||||
|
.default-theme {
|
||||||
|
--rf-bg-color: #000000;
|
||||||
|
--rf-soft-bg-color: #171717;
|
||||||
|
|
||||||
|
--rf-node-bg-color: #202020;
|
||||||
|
--rf-node-soft-bg-color: #2b2b2b;
|
||||||
|
--rf-text-color: #ffffff;
|
||||||
|
--rf-tag-color: #38BDF8;
|
||||||
|
--rf-region-name: #D6D3D1;
|
||||||
|
--rf-custom-name: #93C5FD;
|
||||||
|
|
||||||
|
|
||||||
|
--rf-bg-variant: "dots";
|
||||||
|
--rf-bg-gap: 16;
|
||||||
|
--rf-bg-size: 1;
|
||||||
|
--rf-bg-pattern-color: #81818a;
|
||||||
|
|
||||||
|
--pastel-blue: #5a7d9a;
|
||||||
|
--pastel-pink: #d291bc;
|
||||||
|
--pastel-green: #88b04b;
|
||||||
|
--pastel-yellow: #ffdd59;
|
||||||
|
|
||||||
|
--dark-bg: #2d2d2d;
|
||||||
|
--text-color: #ffffff;
|
||||||
|
--tooltip-bg: #202020;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,78 +1,114 @@
|
|||||||
$eve-link-color-default: #333;
|
:root {
|
||||||
$eve-link-color-top-mass-0: #333;
|
--pastel-blue: #5a7d9a;
|
||||||
$eve-link-color-top-mass-1: #5a4520;
|
--pastel-pink: #d291bc;
|
||||||
$eve-link-color-top-mass-2: #672c2c;
|
--pastel-green: #88b04b;
|
||||||
$eve-link-color-middle-mass-0: #333;
|
--pastel-yellow: #ffdd59;
|
||||||
$eve-link-color-middle-mass-1: #333;
|
--dark-bg: #2d2d2d;
|
||||||
$eve-link-color-middle-mass-2: #333;
|
--text-color: #ffffff;
|
||||||
$eve-link-color-middle-time-0: #5c5c5c;
|
--tooltip-bg: #202020;
|
||||||
$eve-link-color-middle-time-1: #ff00cd;
|
|
||||||
$eve-link-color-middle-time-1-border: #99f3ff;
|
|
||||||
|
|
||||||
$eve-link-color-top-mass-1-time-1: #796300;
|
--pastel-blue-darken10: #4f6b86;
|
||||||
$eve-link-color-top-mass-2-time-1: #8c1717;
|
--pastel-blue-lighten10: #6da3af;
|
||||||
$eve-link-color-temp: orange;
|
--pastel-pink-darken10: #bb7ca9;
|
||||||
|
--pastel-pink-lighten10: #e0a6cb;
|
||||||
|
|
||||||
$eve-effect-pulsar: #40aef5;
|
--pastel-green-darken10: #79a244;
|
||||||
$eve-effect-magnetar: #f058f8;
|
--pastel-green-lighten10: #99cf52;
|
||||||
$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-wh-type-color-high: #5dffd2;
|
--pastel-yellow-darken10: #e6c44f;
|
||||||
$eve-wh-type-color-low: #f79400;
|
--pastel-yellow-lighten10: #ffe874;
|
||||||
$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;
|
|
||||||
|
|
||||||
$eve-security-color-10: #2c74df;
|
/* Eve Link Colors */
|
||||||
$eve-security-color-09: #3998e8;
|
--eve-link-color-default: #333;
|
||||||
$eve-security-color-08: #4dcbf5;
|
--eve-link-color-top-mass-0: #333;
|
||||||
$eve-security-color-07: #60d8a2;
|
--eve-link-color-top-mass-1: #5a4520;
|
||||||
$eve-security-color-06: #71e454;
|
--eve-link-color-top-mass-2: #672c2c;
|
||||||
$eve-security-color-05: #f2fc81;
|
--eve-link-color-middle-mass-0: #333;
|
||||||
$eve-security-color-04: #d96c07;
|
--eve-link-color-middle-mass-1: #333;
|
||||||
$eve-security-color-03: #cb440f;
|
--eve-link-color-middle-mass-2: #333;
|
||||||
$eve-security-color-02: #b91117;
|
--eve-link-color-middle-time-0: #5c5c5c;
|
||||||
$eve-security-color-01: #732020;
|
--eve-link-color-middle-time-1: #ff00cd;
|
||||||
$eve-security-color-00: #8b3263;
|
--eve-link-color-middle-time-1-border: #99f3ff;
|
||||||
$eve-security-color-m-01: #8b3263;
|
--eve-link-color-top-mass-1-time-1: #796300;
|
||||||
$eve-security-color-m-02: #8b3263;
|
--eve-link-color-top-mass-2-time-1: #8c1717;
|
||||||
$eve-security-color-m-03: #8b3263;
|
--eve-link-color-temp: orange;
|
||||||
$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-solar-system-status-unknown: transparent;
|
/* Wormhole Effects */
|
||||||
$eve-solar-system-status-friendly: #3bbd3952;
|
--eve-effect-pulsar: #40aef5;
|
||||||
$eve-solar-system-status-warning: #906518a6;
|
--eve-effect-magnetar: #f058f8;
|
||||||
$eve-solar-system-status-target: #b439ff6b;
|
--eve-effect-wolfRayet: #ef7843;
|
||||||
$eve-solar-system-status-dangerous: #d54040;
|
--eve-effect-blackHole: #1b1b1b;
|
||||||
$eve-solar-system-status-lookingFor: rgba(67, 176, 253, 0.48);
|
--eve-effect-cataclysmicVariable: #ffea90;
|
||||||
$eve-solar-system-status-home: rgb(197, 253, 67);
|
--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;
|
/* WH Types */
|
||||||
$eve-solar-system-status-color-friendly: #3bbd39;
|
--eve-wh-type-color-high: #5dffd2;
|
||||||
$eve-solar-system-status-color-warning: #ffb93b;
|
--eve-wh-type-color-low: #f79400;
|
||||||
$eve-solar-system-status-color-target: #b439ff;
|
--eve-wh-type-color-null: #fc3c3c;
|
||||||
$eve-solar-system-status-color-dangerous: #d54040;
|
--eve-wh-type-color-c1: #69bfce;
|
||||||
$eve-solar-system-status-color-lookingFor: #43c2fd;
|
--eve-wh-type-color-c2: #6991ce;
|
||||||
$eve-solar-system-status-color-home: rgb(197, 253, 67);
|
--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 {
|
.eve-wh-effect-color-pulsar {
|
||||||
fill: $eve-effect-pulsar;
|
fill: var(--eve-effect-pulsar);
|
||||||
background-color: $eve-effect-pulsar;
|
background-color: var(--eve-effect-pulsar);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-wh-effect-color-magnetar {
|
.eve-wh-effect-color-magnetar {
|
||||||
fill: $eve-effect-magnetar;
|
fill: var(--eve-effect-magnetar);
|
||||||
background-color: $eve-effect-magnetar;
|
background-color: var(--eve-effect-magnetar);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-wh-effect-color-wolfRayet {
|
.eve-wh-effect-color-wolfRayet {
|
||||||
fill: $eve-effect-wolfRayet;
|
fill: var(--eve-effect-wolfRayet);
|
||||||
background-color: $eve-effect-wolfRayet;
|
background-color: var(--eve-effect-wolfRayet);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-wh-effect-color-blackHole {
|
.eve-wh-effect-color-blackHole {
|
||||||
fill: $eve-effect-blackHole;
|
fill: var(--eve-effect-blackHole);
|
||||||
background-color: $eve-effect-blackHole;
|
background-color: var(--eve-effect-blackHole);
|
||||||
box-shadow: 0 0 8px rgba(255 255 255 / 33);
|
box-shadow: 0 0 8px rgba(255, 255, 255, 0.33);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-wh-effect-color-cataclysmicVariable {
|
.eve-wh-effect-color-cataclysmicVariable {
|
||||||
fill: $eve-effect-cataclysmicVariable;
|
fill: var(--eve-effect-cataclysmicVariable);
|
||||||
background-color: $eve-effect-cataclysmicVariable;
|
background-color: var(--eve-effect-cataclysmicVariable);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-wh-effect-color-redGiant {
|
.eve-wh-effect-color-redGiant {
|
||||||
fill: $eve-effect-redGiant;
|
fill: var(--eve-effect-redGiant);
|
||||||
background-color: $eve-effect-redGiant;
|
background-color: var(--eve-effect-redGiant);
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-eve-wh-effect-color-pulsar {
|
.text-eve-wh-effect-color-pulsar {
|
||||||
color: $eve-effect-pulsar;
|
color: var(--eve-effect-pulsar);
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-eve-wh-effect-color-magnetar {
|
.text-eve-wh-effect-color-magnetar {
|
||||||
color: $eve-effect-magnetar;
|
color: var(--eve-effect-magnetar);
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-eve-wh-effect-color-wolfRayet {
|
.text-eve-wh-effect-color-wolfRayet {
|
||||||
color: $eve-effect-wolfRayet;
|
color: var(--eve-effect-wolfRayet);
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-eve-wh-effect-color-blackHole {
|
.text-eve-wh-effect-color-blackHole {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-eve-wh-effect-color-cataclysmicVariable {
|
.text-eve-wh-effect-color-cataclysmicVariable {
|
||||||
color: $eve-effect-cataclysmicVariable;
|
color: var(--eve-effect-cataclysmicVariable);
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-eve-wh-effect-color-redGiant {
|
.text-eve-wh-effect-color-redGiant {
|
||||||
color: $eve-effect-redGiant;
|
color: var(--eve-effect-redGiant);
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-eve-wh-effect-color-dazhLiminalityLocus {
|
.text-eve-wh-effect-color-dazhLiminalityLocus {
|
||||||
color: $eve-effect-dazhLiminalityLocus;
|
color: var(--eve-effect-dazhLiminalityLocus);
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-eve-wh-effect-color-imperialStellarObservatory {
|
.text-eve-wh-effect-color-imperialStellarObservatory {
|
||||||
color: $eve-effect-imperialStellarObservatory;
|
color: var(--eve-effect-imperialStellarObservatory);
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-eve-wh-effect-color-stateStellarObservatory {
|
.text-eve-wh-effect-color-stateStellarObservatory {
|
||||||
color: $eve-effect-stateStellarObservatory;
|
color: var(--eve-effect-stateStellarObservatory);
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-eve-wh-effect-color-republicStellarObservatory {
|
.text-eve-wh-effect-color-republicStellarObservatory {
|
||||||
color: $eve-effect-republicStellarObservatory;
|
color: var(--eve-effect-republicStellarObservatory);
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-eve-wh-effect-color-federalStellarObservatory {
|
.text-eve-wh-effect-color-federalStellarObservatory {
|
||||||
color: $eve-effect-federalStellarObservatory;
|
color: var(--eve-effect-federalStellarObservatory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Security color classes */
|
||||||
.eve-security-color-10 {
|
.eve-security-color-10 {
|
||||||
color: $eve-security-color-10 !important;
|
color: var(--eve-security-color-10) !important;
|
||||||
fill: $eve-security-color-10;
|
fill: var(--eve-security-color-10);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-09 {
|
.eve-security-color-09 {
|
||||||
color: $eve-security-color-09 !important;
|
color: var(--eve-security-color-09) !important;
|
||||||
fill: $eve-security-color-09;
|
fill: var(--eve-security-color-09);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-08 {
|
.eve-security-color-08 {
|
||||||
color: $eve-security-color-08 !important;
|
color: var(--eve-security-color-08) !important;
|
||||||
fill: $eve-security-color-08;
|
fill: var(--eve-security-color-08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-07 {
|
.eve-security-color-07 {
|
||||||
color: $eve-security-color-07 !important;
|
color: var(--eve-security-color-07) !important;
|
||||||
fill: $eve-security-color-07;
|
fill: var(--eve-security-color-07);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-06 {
|
.eve-security-color-06 {
|
||||||
color: $eve-security-color-06 !important;
|
color: var(--eve-security-color-06) !important;
|
||||||
fill: $eve-security-color-06;
|
fill: var(--eve-security-color-06);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-05 {
|
.eve-security-color-05 {
|
||||||
color: $eve-security-color-05 !important;
|
color: var(--eve-security-color-05) !important;
|
||||||
fill: $eve-security-color-05;
|
fill: var(--eve-security-color-05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-04 {
|
.eve-security-color-04 {
|
||||||
color: $eve-security-color-04 !important;
|
color: var(--eve-security-color-04) !important;
|
||||||
fill: $eve-security-color-04;
|
fill: var(--eve-security-color-04);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-03 {
|
.eve-security-color-03 {
|
||||||
color: $eve-security-color-03 !important;
|
color: var(--eve-security-color-03) !important;
|
||||||
fill: $eve-security-color-03;
|
fill: var(--eve-security-color-03);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-02 {
|
.eve-security-color-02 {
|
||||||
color: $eve-security-color-02 !important;
|
color: var(--eve-security-color-02) !important;
|
||||||
fill: $eve-security-color-02;
|
fill: var(--eve-security-color-02);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-01 {
|
.eve-security-color-01 {
|
||||||
color: $eve-security-color-01 !important;
|
color: var(--eve-security-color-01) !important;
|
||||||
fill: $eve-security-color-01;
|
fill: var(--eve-security-color-01);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-00 {
|
.eve-security-color-00 {
|
||||||
color: $eve-security-color-00 !important;
|
color: var(--eve-security-color-00) !important;
|
||||||
fill: $eve-security-color-00;
|
fill: var(--eve-security-color-00);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-m-01 {
|
.eve-security-color-m-01 {
|
||||||
color: $eve-security-color-m-01 !important;
|
color: var(--eve-security-color-m-01) !important;
|
||||||
fill: $eve-security-color-m-01;
|
fill: var(--eve-security-color-m-01);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-m-02 {
|
.eve-security-color-m-02 {
|
||||||
color: $eve-security-color-m-02 !important;
|
color: var(--eve-security-color-m-02) !important;
|
||||||
fill: $eve-security-color-m-02;
|
fill: var(--eve-security-color-m-02);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-m-03 {
|
.eve-security-color-m-03 {
|
||||||
color: $eve-security-color-m-03 !important;
|
color: var(--eve-security-color-m-03) !important;
|
||||||
fill: $eve-security-color-m-03;
|
fill: var(--eve-security-color-m-03);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-m-04 {
|
.eve-security-color-m-04 {
|
||||||
color: $eve-security-color-m-04 !important;
|
color: var(--eve-security-color-m-04) !important;
|
||||||
fill: $eve-security-color-m-04;
|
fill: var(--eve-security-color-m-04);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-m-05 {
|
.eve-security-color-m-05 {
|
||||||
color: $eve-security-color-m-05 !important;
|
color: var(--eve-security-color-m-05) !important;
|
||||||
fill: $eve-security-color-m-05;
|
fill: var(--eve-security-color-m-05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-m-06 {
|
.eve-security-color-m-06 {
|
||||||
color: $eve-security-color-m-06 !important;
|
color: var(--eve-security-color-m-06) !important;
|
||||||
fill: $eve-security-color-m-06;
|
fill: var(--eve-security-color-m-06);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-m-07 {
|
.eve-security-color-m-07 {
|
||||||
color: $eve-security-color-m-07 !important;
|
color: var(--eve-security-color-m-07) !important;
|
||||||
fill: $eve-security-color-m-07;
|
fill: var(--eve-security-color-m-07);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-m-08 {
|
.eve-security-color-m-08 {
|
||||||
color: $eve-security-color-m-08 !important;
|
color: var(--eve-security-color-m-08) !important;
|
||||||
fill: $eve-security-color-m-08;
|
fill: var(--eve-security-color-m-08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-m-09 {
|
.eve-security-color-m-09 {
|
||||||
color: $eve-security-color-m-09 !important;
|
color: var(--eve-security-color-m-09) !important;
|
||||||
fill: $eve-security-color-m-09;
|
fill: var(--eve-security-color-m-09);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-color-m-10 {
|
.eve-security-color-m-10 {
|
||||||
color: $eve-security-color-m-10 !important;
|
color: var(--eve-security-color-m-10) !important;
|
||||||
fill: $eve-security-color-m-10;
|
fill: var(--eve-security-color-m-10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Security backgrounds */
|
||||||
.eve-security-background-10 {
|
.eve-security-background-10 {
|
||||||
background-color: $eve-security-color-10;
|
background-color: var(--eve-security-color-10);
|
||||||
fill: $eve-security-color-10;
|
fill: var(--eve-security-color-10);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-09 {
|
.eve-security-background-09 {
|
||||||
background-color: $eve-security-color-09;
|
background-color: var(--eve-security-color-09);
|
||||||
fill: $eve-security-color-09;
|
fill: var(--eve-security-color-09);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-08 {
|
.eve-security-background-08 {
|
||||||
background-color: $eve-security-color-08;
|
background-color: var(--eve-security-color-08);
|
||||||
fill: $eve-security-color-08;
|
fill: var(--eve-security-color-08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-07 {
|
.eve-security-background-07 {
|
||||||
background-color: $eve-security-color-07;
|
background-color: var(--eve-security-color-07);
|
||||||
fill: $eve-security-color-07;
|
fill: var(--eve-security-color-07);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-06 {
|
.eve-security-background-06 {
|
||||||
background-color: $eve-security-color-06;
|
background-color: var(--eve-security-color-06);
|
||||||
fill: $eve-security-color-06;
|
fill: var(--eve-security-color-06);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-05 {
|
.eve-security-background-05 {
|
||||||
background-color: $eve-security-color-05;
|
background-color: var(--eve-security-color-05);
|
||||||
fill: $eve-security-color-05;
|
fill: var(--eve-security-color-05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-04 {
|
.eve-security-background-04 {
|
||||||
background-color: $eve-security-color-04;
|
background-color: var(--eve-security-color-04);
|
||||||
fill: $eve-security-color-04;
|
fill: var(--eve-security-color-04);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-03 {
|
.eve-security-background-03 {
|
||||||
background-color: $eve-security-color-03;
|
background-color: var(--eve-security-color-03);
|
||||||
fill: $eve-security-color-03;
|
fill: var(--eve-security-color-03);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-02 {
|
.eve-security-background-02 {
|
||||||
background-color: $eve-security-color-02;
|
background-color: var(--eve-security-color-02);
|
||||||
fill: $eve-security-color-02;
|
fill: var(--eve-security-color-02);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-01 {
|
.eve-security-background-01 {
|
||||||
background-color: $eve-security-color-01;
|
background-color: var(--eve-security-color-01);
|
||||||
fill: $eve-security-color-01;
|
fill: var(--eve-security-color-01);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-00 {
|
.eve-security-background-00 {
|
||||||
background-color: $eve-security-color-00;
|
background-color: var(--eve-security-color-00);
|
||||||
fill: $eve-security-color-00;
|
fill: var(--eve-security-color-00);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-m-01 {
|
.eve-security-background-m-01 {
|
||||||
background-color: $eve-security-color-m-01;
|
background-color: var(--eve-security-color-m-01);
|
||||||
fill: $eve-security-color-m-01;
|
fill: var(--eve-security-color-m-01);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-m-02 {
|
.eve-security-background-m-02 {
|
||||||
background-color: $eve-security-color-m-02;
|
background-color: var(--eve-security-color-m-02);
|
||||||
fill: $eve-security-color-m-02;
|
fill: var(--eve-security-color-m-02);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-m-03 {
|
.eve-security-background-m-03 {
|
||||||
background-color: $eve-security-color-m-03;
|
background-color: var(--eve-security-color-m-03);
|
||||||
fill: $eve-security-color-m-03;
|
fill: var(--eve-security-color-m-03);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-m-04 {
|
.eve-security-background-m-04 {
|
||||||
background-color: $eve-security-color-m-04;
|
background-color: var(--eve-security-color-m-04);
|
||||||
fill: $eve-security-color-m-04;
|
fill: var(--eve-security-color-m-04);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-m-05 {
|
.eve-security-background-m-05 {
|
||||||
background-color: $eve-security-color-m-05;
|
background-color: var(--eve-security-color-m-05);
|
||||||
fill: $eve-security-color-m-05;
|
fill: var(--eve-security-color-m-05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-m-06 {
|
.eve-security-background-m-06 {
|
||||||
background-color: $eve-security-color-m-06;
|
background-color: var(--eve-security-color-m-06);
|
||||||
fill: $eve-security-color-m-06;
|
fill: var(--eve-security-color-m-06);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-m-07 {
|
.eve-security-background-m-07 {
|
||||||
background-color: $eve-security-color-m-07;
|
background-color: var(--eve-security-color-m-07);
|
||||||
fill: $eve-security-color-m-07;
|
fill: var(--eve-security-color-m-07);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-m-08 {
|
.eve-security-background-m-08 {
|
||||||
background-color: $eve-security-color-m-08;
|
background-color: var(--eve-security-color-m-08);
|
||||||
fill: $eve-security-color-m-08;
|
fill: var(--eve-security-color-m-08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-m-09 {
|
.eve-security-background-m-09 {
|
||||||
background-color: $eve-security-color-m-09;
|
background-color: var(--eve-security-color-m-09);
|
||||||
fill: $eve-security-color-m-09;
|
fill: var(--eve-security-color-m-09);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-security-background-m-10 {
|
.eve-security-background-m-10 {
|
||||||
background-color: $eve-security-color-m-10;
|
background-color: var(--eve-security-color-m-10);
|
||||||
fill: $eve-security-color-m-10;
|
fill: var(--eve-security-color-m-10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* WH Type color classes */
|
||||||
.eve-wh-type-color-high {
|
.eve-wh-type-color-high {
|
||||||
color: $eve-wh-type-color-high;
|
color: var(--eve-wh-type-color-high) !important;
|
||||||
fill: $eve-wh-type-color-high;
|
fill: var(--eve-wh-type-color-high);
|
||||||
font-weight: bold !important;
|
font-weight: bold !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-wh-type-color-low {
|
.eve-wh-type-color-low {
|
||||||
color: $eve-wh-type-color-low;
|
color: var(--eve-wh-type-color-low) !important;
|
||||||
fill: $eve-wh-type-color-low;
|
fill: var(--eve-wh-type-color-low);
|
||||||
font-weight: bold !important;
|
font-weight: bold !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-wh-type-color-null {
|
.eve-wh-type-color-null {
|
||||||
color: $eve-wh-type-color-null;
|
color: var(--eve-wh-type-color-null) !important;
|
||||||
fill: $eve-wh-type-color-null;
|
fill: var(--eve-wh-type-color-null);
|
||||||
font-weight: bold !important;
|
font-weight: bold !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-wh-type-color-c1 {
|
.eve-wh-type-color-c1 {
|
||||||
color: $eve-wh-type-color-c1 !important;
|
color: var(--eve-wh-type-color-c1) !important;
|
||||||
fill: $eve-wh-type-color-c1;
|
fill: var(--eve-wh-type-color-c1);
|
||||||
font-weight: bold !important;
|
font-weight: bold !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-wh-type-color-c2 {
|
.eve-wh-type-color-c2 {
|
||||||
color: $eve-wh-type-color-c2 !important;
|
color: var(--eve-wh-type-color-c2) !important;
|
||||||
fill: $eve-wh-type-color-c2;
|
fill: var(--eve-wh-type-color-c2);
|
||||||
font-weight: bold !important;
|
font-weight: bold !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-wh-type-color-c3 {
|
.eve-wh-type-color-c3 {
|
||||||
color: $eve-wh-type-color-c3 !important;
|
color: var(--eve-wh-type-color-c3) !important;
|
||||||
fill: $eve-wh-type-color-c3;
|
fill: var(--eve-wh-type-color-c3);
|
||||||
font-weight: bold !important;
|
font-weight: bold !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-wh-type-color-c4 {
|
.eve-wh-type-color-c4 {
|
||||||
color: $eve-wh-type-color-c4 !important;
|
color: var(--eve-wh-type-color-c4) !important;
|
||||||
fill: $eve-wh-type-color-c4;
|
fill: var(--eve-wh-type-color-c4);
|
||||||
font-weight: bold !important;
|
font-weight: bold !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-wh-type-color-c5 {
|
.eve-wh-type-color-c5 {
|
||||||
color: $eve-wh-type-color-c5 !important;
|
color: var(--eve-wh-type-color-c5) !important;
|
||||||
fill: $eve-wh-type-color-c5;
|
fill: var(--eve-wh-type-color-c5);
|
||||||
font-weight: bold !important;
|
font-weight: bold !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-wh-type-color-c6 {
|
.eve-wh-type-color-c6 {
|
||||||
color: $eve-wh-type-color-c6 !important;
|
color: var(--eve-wh-type-color-c6) !important;
|
||||||
fill: $eve-wh-type-color-c6;
|
fill: var(--eve-wh-type-color-c6);
|
||||||
font-weight: bold !important;
|
font-weight: bold !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-wh-type-color-c13 {
|
.eve-wh-type-color-c13 {
|
||||||
color: $eve-wh-type-color-c13 !important;
|
color: var(--eve-wh-type-color-c13) !important;
|
||||||
fill: $eve-wh-type-color-c13;
|
fill: var(--eve-wh-type-color-c13);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-wh-type-color-drifter {
|
.eve-wh-type-color-drifter {
|
||||||
color: $eve-wh-type-color-drifter !important;
|
color: var(--eve-wh-type-color-drifter) !important;
|
||||||
fill: $eve-wh-type-color-drifter;
|
fill: var(--eve-wh-type-color-drifter);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-wh-type-color-thera {
|
.eve-wh-type-color-thera {
|
||||||
color: $eve-wh-type-color-thera !important;
|
color: var(--eve-wh-type-color-thera) !important;
|
||||||
fill: $eve-wh-type-color-thera;
|
fill: var(--eve-wh-type-color-thera);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* WH Type backgrounds */
|
||||||
.eve-wh-type-background-high {
|
.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 {
|
.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 {
|
.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 {
|
.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 {
|
.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 {
|
.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 {
|
.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 {
|
.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 {
|
.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 {
|
.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 {
|
.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 {
|
.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 {
|
.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 {
|
.eve-kind-color-high {
|
||||||
color: $eve-wh-type-color-high;
|
color: var(--eve-wh-type-color-high);
|
||||||
fill: $eve-wh-type-color-high;
|
fill: var(--eve-wh-type-color-high);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-kind-color-low {
|
.eve-kind-color-low {
|
||||||
color: $eve-wh-type-color-low;
|
color: var(--eve-wh-type-color-low);
|
||||||
fill: $eve-wh-type-color-low;
|
fill: var(--eve-wh-type-color-low);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-kind-color-null {
|
.eve-kind-color-null {
|
||||||
color: $eve-wh-type-color-null;
|
color: var(--eve-wh-type-color-null);
|
||||||
fill: $eve-wh-type-color-null;
|
fill: var(--eve-wh-type-color-null);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-kind-color-wh {
|
.eve-kind-color-wh {
|
||||||
color: $eve-wh-type-color-c6;
|
color: var(--eve-wh-type-color-c6);
|
||||||
fill: $eve-wh-type-color-c6;
|
fill: var(--eve-wh-type-color-c6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-kind-color-thera {
|
.eve-kind-color-thera {
|
||||||
color: $eve-wh-type-color-thera;
|
color: var(--eve-wh-type-color-thera);
|
||||||
fill: $eve-wh-type-color-thera;
|
fill: var(--eve-wh-type-color-thera);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-kind-color-abyss {
|
.eve-kind-color-abyss {
|
||||||
color: $eve-wh-type-color-c6;
|
color: var(--eve-wh-type-color-c6);
|
||||||
fill: $eve-wh-type-color-c6;
|
fill: var(--eve-wh-type-color-c6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-kind-color-penalty {
|
.eve-kind-color-penalty {
|
||||||
color: $eve-wh-type-color-c6;
|
color: var(--eve-wh-type-color-c6);
|
||||||
fill: $eve-wh-type-color-c6;
|
fill: var(--eve-wh-type-color-c6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-kind-color-pochven {
|
.eve-kind-color-pochven {
|
||||||
color: $eve-wh-type-color-c6;
|
color: var(--eve-wh-type-color-c6);
|
||||||
fill: $eve-wh-type-color-c6;
|
fill: var(--eve-wh-type-color-c6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-kind-color-zarzakh {
|
.eve-kind-color-zarzakh {
|
||||||
color: $eve-wh-type-color-zarzakh;
|
color: var(--eve-wh-type-color-zarzakh);
|
||||||
fill: $eve-wh-type-color-zarzakh;
|
fill: var(--eve-wh-type-color-zarzakh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Kind backgrounds */
|
||||||
.eve-kind-background-high {
|
.eve-kind-background-high {
|
||||||
background-color: $eve-wh-type-color-high;
|
background-color: var(--eve-wh-type-color-high);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-kind-background-low {
|
.eve-kind-background-low {
|
||||||
background-color: $eve-wh-type-color-low;
|
background-color: var(--eve-wh-type-color-low);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-kind-background-null {
|
.eve-kind-background-null {
|
||||||
background-color: $eve-wh-type-color-null;
|
background-color: var(--eve-wh-type-color-null);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-kind-background-wh {
|
.eve-kind-background-wh {
|
||||||
background-color: $eve-wh-type-color-c6;
|
background-color: var(--eve-wh-type-color-c6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-kind-background-thera {
|
.eve-kind-background-thera {
|
||||||
background-color: $eve-wh-type-color-thera;
|
background-color: var(--eve-wh-type-color-thera);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-kind-background-abyss {
|
.eve-kind-background-abyss {
|
||||||
background-color: $eve-wh-type-color-c6;
|
background-color: var(--eve-wh-type-color-c6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-kind-background-penalty {
|
.eve-kind-background-penalty {
|
||||||
background-color: $eve-wh-type-color-c6;
|
background-color: var(--eve-wh-type-color-c6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-kind-background-pochven {
|
.eve-kind-background-pochven {
|
||||||
background-color: $eve-wh-type-color-c6;
|
background-color: var(--eve-wh-type-color-c6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eve-kind-background-zarzakh {
|
.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 {
|
.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 {
|
.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 {
|
.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 {
|
.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 {
|
.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 {
|
.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 {
|
.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 {
|
.wd-route-system-shape-triangle {
|
||||||
clip-path: polygon(50% 0, 0 100%, 100% 100%);
|
clip-path: polygon(50% 0, 0 100%, 100% 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-route-system-shape-circle {
|
.wd-route-system-shape-circle {
|
||||||
border-radius: 40%;
|
border-radius: 40%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Some additional background classes */
|
||||||
.wd-marker-bookmark-color-shattered {
|
.wd-marker-bookmark-color-shattered {
|
||||||
background-color: #833ca4;
|
background-color: #833ca4;
|
||||||
margin-top: 1px;
|
margin-top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.wd-marker-bookmark-color-custom {
|
.wd-marker-bookmark-color-custom {
|
||||||
background-color: #282828;
|
background-color: #282828;
|
||||||
border: 1px solid #4c4c4c;
|
border: 1px solid #4c4c4c;
|
||||||
@@ -572,3 +485,49 @@
|
|||||||
.wd-marker-bookmark-color-danger {
|
.wd-marker-bookmark-color-danger {
|
||||||
background-color: #d10600;
|
background-color: #d10600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.react-flow {
|
||||||
|
color: var(--text-color);
|
||||||
|
|
||||||
|
&__pane {
|
||||||
|
cursor: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__minimap {
|
||||||
|
background-color: rgba(66, 66, 66, 1);
|
||||||
|
opacity: 0.7;
|
||||||
|
border: 1px solid #2f2f2f;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__minimap-mask {
|
||||||
|
fill: rgba(28, 28, 28, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__controls {
|
||||||
|
filter: brightness(1.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__minimap-node {
|
||||||
|
fill: #ffb03a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.context-menu-active {
|
||||||
|
background-color: rgba(131, 131, 131, 0.33);
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-dialog {
|
||||||
|
.p-dialog-header {
|
||||||
|
height: 40px;
|
||||||
|
padding: 1rem;
|
||||||
|
padding-right: 10px !important;
|
||||||
|
}
|
||||||
|
.p-dialog-title {
|
||||||
|
font-size: 1rem !important;
|
||||||
|
}
|
||||||
|
.p-dialog-header-icons {
|
||||||
|
align-self: initial !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
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 './default-theme.scss';
|
||||||
|
@import './pathfinder-theme.scss';
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
$pastel-blue: #5a7d9a;
|
|
||||||
$pastel-pink: #d291bc;
|
|
||||||
$pastel-green: #88b04b;
|
|
||||||
$pastel-yellow: #ffdd59;
|
|
||||||
$dark-bg: #2d2d2d;
|
|
||||||
$text-color: #ffffff;
|
|
||||||
$tooltip-bg: #202020;
|
|
||||||
|
|
||||||
.react-flow {
|
|
||||||
// background-color: $dark-bg;
|
|
||||||
color: $text-color;
|
|
||||||
|
|
||||||
&__node {
|
|
||||||
//cursor: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__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);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,51 @@
|
|||||||
|
|
||||||
|
@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;
|
||||||
|
--rf-region-name: var(--rf-text-color);
|
||||||
|
--rf-custom-name: var(--rf-text-color);
|
||||||
|
|
||||||
|
--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[];
|
||||||
|
}
|
||||||
11
assets/js/hooks/Mapper/components/map/utils/wrapNode.tsx
Normal file
11
assets/js/hooks/Mapper/components/map/utils/wrapNode.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// wrapNode.ts
|
||||||
|
import { NodeProps } from 'reactflow';
|
||||||
|
import { SolarSystemNodeProps } from '../components/SolarSystemNode';
|
||||||
|
|
||||||
|
export function wrapNode<T>(
|
||||||
|
SolarSystemNode: React.FC<SolarSystemNodeProps<T>>
|
||||||
|
): React.FC<NodeProps<T>> {
|
||||||
|
return function NodeAdapter(props) {
|
||||||
|
return <SolarSystemNode {...props} />;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -48,6 +48,7 @@ const restoreWindowsFromLS = (): WidgetGridItem[] => {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const raw = localStorage.getItem(SESSION_KEY.windows);
|
const raw = localStorage.getItem(SESSION_KEY.windows);
|
||||||
if (!raw) {
|
if (!raw) {
|
||||||
|
console.warn('No windows found in local storage!!');
|
||||||
return DEFAULT_WINDOWS;
|
return DEFAULT_WINDOWS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +64,7 @@ const restoreWindowsFromLS = (): WidgetGridItem[] => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const MapInterface = () => {
|
export const MapInterface = () => {
|
||||||
const [items, setItems] = useState<WidgetGridItem[]>(restoreWindowsFromLS());
|
const [items, setItems] = useState<WidgetGridItem[]>(restoreWindowsFromLS);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WidgetsGrid
|
<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,17 +6,23 @@ import { SystemSignature } from '@/hooks/Mapper/types';
|
|||||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||||
import { CommandLinkSignatureToSystem } from '@/hooks/Mapper/types';
|
import { CommandLinkSignatureToSystem } from '@/hooks/Mapper/types';
|
||||||
import { SystemSignaturesContent } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignaturesContent';
|
import { SystemSignaturesContent } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignaturesContent';
|
||||||
|
import { SHOW_DESCRIPTION_COLUMN_SETTING } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignatures';
|
||||||
import {
|
import {
|
||||||
Setting,
|
Setting,
|
||||||
COSMIC_SIGNATURE,
|
COSMIC_SIGNATURE,
|
||||||
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignatureSettingsDialog';
|
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignatureSettingsDialog';
|
||||||
|
import { SignatureGroup } from '@/hooks/Mapper/types';
|
||||||
|
|
||||||
interface SystemLinkSignatureDialogProps {
|
interface SystemLinkSignatureDialogProps {
|
||||||
data: CommandLinkSignatureToSystem;
|
data: CommandLinkSignatureToSystem;
|
||||||
setVisible: (visible: boolean) => void;
|
setVisible: (visible: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const signatureSettings: Setting[] = [{ key: COSMIC_SIGNATURE, name: 'Show Cosmic Signatures', value: true }];
|
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) => {
|
export const SystemLinkSignatureDialog = ({ data, setVisible }: SystemLinkSignatureDialogProps) => {
|
||||||
const { outCommand } = useMapRootState();
|
const { outCommand } = useMapRootState();
|
||||||
@@ -59,6 +65,7 @@ export const SystemLinkSignatureDialog = ({ data, setVisible }: SystemLinkSignat
|
|||||||
>
|
>
|
||||||
<SystemSignaturesContent
|
<SystemSignaturesContent
|
||||||
systemId={`${data.solar_system_source}`}
|
systemId={`${data.solar_system_source}`}
|
||||||
|
hideLinkedSignatures
|
||||||
settings={signatureSettings}
|
settings={signatureSettings}
|
||||||
onSelect={handleSelect}
|
onSelect={handleSelect}
|
||||||
selectable={true}
|
selectable={true}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { InputTextarea } from 'primereact/inputtextarea';
|
|||||||
import { Dialog } from 'primereact/dialog';
|
import { Dialog } from 'primereact/dialog';
|
||||||
import { getSystemById } from '@/hooks/Mapper/helpers';
|
import { getSystemById } from '@/hooks/Mapper/helpers';
|
||||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||||
|
import { useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { Button } from 'primereact/button';
|
import { Button } from 'primereact/button';
|
||||||
import { OutCommand } from '@/hooks/Mapper/types';
|
import { OutCommand } from '@/hooks/Mapper/types';
|
||||||
@@ -22,30 +23,21 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
|
|||||||
outCommand,
|
outCommand,
|
||||||
} = useMapRootState();
|
} = useMapRootState();
|
||||||
|
|
||||||
|
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
|
||||||
|
|
||||||
const system = getSystemById(systems, systemId);
|
const system = getSystemById(systems, systemId);
|
||||||
|
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const [label, setLabel] = useState('');
|
const [label, setLabel] = useState('');
|
||||||
|
const [temporaryName, setTemporaryName] = useState('');
|
||||||
const [description, setDescription] = useState('');
|
const [description, setDescription] = useState('');
|
||||||
const inputRef = useRef<HTMLInputElement>();
|
const inputRef = useRef<HTMLInputElement>();
|
||||||
|
|
||||||
useEffect(() => {
|
const ref = useRef({ name, description, temporaryName, label, outCommand, systemId, system });
|
||||||
if (!system) {
|
ref.current = { name, description, label, temporaryName, outCommand, systemId, 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 handleSave = useCallback(() => {
|
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 ?? '');
|
const outLabel = new LabelsManager(system?.labels ?? '');
|
||||||
outLabel.updateCustomLabel(label);
|
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({
|
outCommand({
|
||||||
type: OutCommand.updateSystemName,
|
type: OutCommand.updateSystemName,
|
||||||
data: {
|
data: {
|
||||||
@@ -93,6 +93,21 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
|
|||||||
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9\-[\](){}]/g, '');
|
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 (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
header="System settings"
|
header="System settings"
|
||||||
@@ -167,6 +182,35 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
|
|||||||
</IconField>
|
</IconField>
|
||||||
</div>
|
</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">
|
<div className="flex flex-col gap-1">
|
||||||
<label htmlFor="username">Description</label>
|
<label htmlFor="username">Description</label>
|
||||||
<InputTextarea
|
<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 { Widget } from '@/hooks/Mapper/components/mapInterface/components';
|
||||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||||
import { VirtualScroller, VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
|
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 { CharacterCard, LayoutEventBlocker, WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
|
||||||
import { sortCharacters } from '@/hooks/Mapper/components/mapInterface/helpers/sortCharacters.ts';
|
import { sortCharacters } from '@/hooks/Mapper/components/mapInterface/helpers/sortCharacters.ts';
|
||||||
import useLocalStorageState from 'use-local-storage-state';
|
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 = {
|
type CharItemProps = {
|
||||||
compact: boolean;
|
compact: boolean;
|
||||||
@@ -62,6 +66,14 @@ export const LocalCharacters = () => {
|
|||||||
|
|
||||||
const [systemId] = selectedSystems;
|
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 itemTemplate = useItemTemplate();
|
||||||
|
|
||||||
const sorted = useMemo(() => {
|
const sorted = useMemo(() => {
|
||||||
@@ -70,32 +82,39 @@ export const LocalCharacters = () => {
|
|||||||
.map(x => ({ ...x, isOwn: userCharacters.includes(x.eve_id), compact: settings.compact }))
|
.map(x => ({ ...x, isOwn: userCharacters.includes(x.eve_id), compact: settings.compact }))
|
||||||
.sort(sortCharacters);
|
.sort(sortCharacters);
|
||||||
|
|
||||||
if (!settings.showOffline) {
|
if (!showOffline || !settings.showOffline) {
|
||||||
return sorted.filter(c => c.online);
|
return sorted.filter(c => c.online);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sorted;
|
return sorted;
|
||||||
// eslint-disable-next-line
|
// 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 isNobodyHere = sorted.length === 0;
|
||||||
const isNotSelectedSystem = selectedSystems.length !== 1;
|
const isNotSelectedSystem = selectedSystems.length !== 1;
|
||||||
const showList = sorted.length > 0 && selectedSystems.length === 1;
|
const showList = sorted.length > 0 && selectedSystems.length === 1;
|
||||||
|
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
const compact = useMaxWidth(ref, 145);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Widget
|
<Widget
|
||||||
label={
|
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>
|
<span className="select-none">Local{showList ? ` [${sorted.length}]` : ''}</span>
|
||||||
<LayoutEventBlocker className="flex items-center gap-2">
|
<LayoutEventBlocker className="flex items-center gap-2">
|
||||||
<WdCheckbox
|
{showOffline && (
|
||||||
size="xs"
|
<WdTooltipWrapper content="Show offline characters in system">
|
||||||
labelSide="left"
|
<WdCheckbox
|
||||||
label={'Show offline'}
|
size="xs"
|
||||||
value={settings.showOffline}
|
labelSide="left"
|
||||||
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
|
label={compact ? '' : 'Show offline'}
|
||||||
onChange={() => setSettings(() => ({ ...settings, showOffline: !settings.showOffline }))}
|
value={settings.showOffline}
|
||||||
/>
|
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
|
||||||
|
onChange={() => setSettings(() => ({ ...settings, showOffline: !settings.showOffline }))}
|
||||||
|
/>
|
||||||
|
</WdTooltipWrapper>
|
||||||
|
)}
|
||||||
|
|
||||||
<span
|
<span
|
||||||
className={clsx('w-4 h-4 cursor-pointer', {
|
className={clsx('w-4 h-4 cursor-pointer', {
|
||||||
@@ -115,7 +134,9 @@ export const LocalCharacters = () => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{isNobodyHere && !isNotSelectedSystem && (
|
{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 && (
|
{showList && (
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
.RouteSystem {
|
.RouteSystem {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
height: 8px;
|
height: 8px;
|
||||||
background: #ffffff;
|
|
||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: opacity 200ms;
|
transition: opacity 200ms;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { SystemViewStandalone, WdTooltip, WdTooltipHandlers } from '@/hooks/Mapp
|
|||||||
import { getBackgroundClass, getShapeClass } from '@/hooks/Mapper/components/map/helpers';
|
import { getBackgroundClass, getShapeClass } from '@/hooks/Mapper/components/map/helpers';
|
||||||
import { MouseEvent, useCallback, useRef, useState } from 'react';
|
import { MouseEvent, useCallback, useRef, useState } from 'react';
|
||||||
import { Commands } from '@/hooks/Mapper/types';
|
import { Commands } from '@/hooks/Mapper/types';
|
||||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
import { emitMapEvent } from '@/hooks/Mapper/events';
|
||||||
|
|
||||||
export type RouteSystemProps = {
|
export type RouteSystemProps = {
|
||||||
destination: number;
|
destination: number;
|
||||||
@@ -88,11 +88,10 @@ export interface RoutesListProps {
|
|||||||
|
|
||||||
export const RoutesList = ({ data, onContextMenu }: RoutesListProps) => {
|
export const RoutesList = ({ data, onContextMenu }: RoutesListProps) => {
|
||||||
const [selected, setSelected] = useState<number | null>(null);
|
const [selected, setSelected] = useState<number | null>(null);
|
||||||
const { mapRef } = useMapRootState();
|
|
||||||
|
|
||||||
const handleClick = useCallback(
|
const handleClick = useCallback(
|
||||||
(systemId: number) => mapRef.current?.command(Commands.centerSystem, systemId.toString()),
|
(systemId: number) => emitMapEvent({ name: Commands.centerSystem, data: systemId?.toString() }),
|
||||||
[mapRef],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!data.has_connection) {
|
if (!data.has_connection) {
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { Dialog } from 'primereact/dialog';
|
import { Dialog } from 'primereact/dialog';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { Button } from 'primereact/button';
|
import { Button } from 'primereact/button';
|
||||||
import { WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
|
|
||||||
import {
|
import {
|
||||||
RoutesType,
|
RoutesType,
|
||||||
useRouteProvider,
|
useRouteProvider,
|
||||||
} from '@/hooks/Mapper/components/mapInterface/widgets/RoutesWidget/RoutesProvider.tsx';
|
} 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 {
|
interface RoutesSettingsDialog {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
@@ -38,8 +37,8 @@ export const RoutesSettingsDialog = ({ visible, setVisible }: RoutesSettingsDial
|
|||||||
currentData.current = data;
|
currentData.current = data;
|
||||||
|
|
||||||
const handleChangeEvent = useCallback(
|
const handleChangeEvent = useCallback(
|
||||||
(propName: keyof RoutesType) => (event: CheckboxChangeEvent) => {
|
(propName: keyof RoutesType) => (event: boolean) => {
|
||||||
optionsRef.current = { ...optionsRef.current, [propName]: event.checked };
|
optionsRef.current = { ...optionsRef.current, [propName]: event };
|
||||||
updateKey(x => x + 1);
|
updateKey(x => x + 1);
|
||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
@@ -71,14 +70,14 @@ export const RoutesSettingsDialog = ({ visible, setVisible }: RoutesSettingsDial
|
|||||||
setVisible(false);
|
setVisible(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3 p-2.5">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2 mb-2">
|
||||||
{checkboxes.map(({ label, propName }) => (
|
{checkboxes.map(({ label, propName }) => (
|
||||||
<WdCheckbox
|
<PrettySwitchbox
|
||||||
key={propName}
|
key={propName}
|
||||||
label={label}
|
label={label}
|
||||||
value={optionsRef.current[propName]}
|
checked={optionsRef.current[propName]}
|
||||||
onChange={handleChangeEvent(propName)}
|
setChecked={handleChangeEvent(propName)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,6 +19,13 @@ import { PrimeIcons } from 'primereact/api';
|
|||||||
import { RoutesSettingsDialog } from './RoutesSettingsDialog';
|
import { RoutesSettingsDialog } from './RoutesSettingsDialog';
|
||||||
import { RoutesProvider, useRouteProvider } from './RoutesProvider.tsx';
|
import { RoutesProvider, useRouteProvider } from './RoutesProvider.tsx';
|
||||||
import { ContextMenuSystemInfo, useContextMenuSystemInfoHandlers } from '@/hooks/Mapper/components/contexts';
|
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 sortByDist = (a: Route, b: Route) => {
|
||||||
const distA = a.has_connection ? a.systems?.length || 0 : Infinity;
|
const distA = a.has_connection ? a.systems?.length || 0 : Infinity;
|
||||||
@@ -30,7 +37,6 @@ const sortByDist = (a: Route, b: Route) => {
|
|||||||
export const RoutesWidgetContent = () => {
|
export const RoutesWidgetContent = () => {
|
||||||
const {
|
const {
|
||||||
data: { selectedSystems, hubs = [], systems, routes },
|
data: { selectedSystems, hubs = [], systems, routes },
|
||||||
mapRef,
|
|
||||||
outCommand,
|
outCommand,
|
||||||
} = useMapRootState();
|
} = useMapRootState();
|
||||||
|
|
||||||
@@ -42,7 +48,6 @@ export const RoutesWidgetContent = () => {
|
|||||||
const { open, ...systemCtxProps } = useContextMenuSystemInfoHandlers({
|
const { open, ...systemCtxProps } = useContextMenuSystemInfoHandlers({
|
||||||
outCommand,
|
outCommand,
|
||||||
hubs,
|
hubs,
|
||||||
mapRef,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const preparedHubs = useMemo(() => {
|
const preparedHubs = useMemo(() => {
|
||||||
@@ -163,6 +168,12 @@ export const RoutesWidgetContent = () => {
|
|||||||
export const RoutesWidgetComp = () => {
|
export const RoutesWidgetComp = () => {
|
||||||
const [routeSettingsVisible, setRouteSettingsVisible] = useState(false);
|
const [routeSettingsVisible, setRouteSettingsVisible] = useState(false);
|
||||||
const { data, update } = useRouteProvider();
|
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 isSecure = data.path_type === 'secure';
|
||||||
const handleSecureChange = useCallback(() => {
|
const handleSecureChange = useCallback(() => {
|
||||||
@@ -172,27 +183,70 @@ export const RoutesWidgetComp = () => {
|
|||||||
});
|
});
|
||||||
}, [data, update]);
|
}, [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 (
|
return (
|
||||||
<Widget
|
<Widget
|
||||||
label={
|
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>
|
<span className="select-none">Routes</span>
|
||||||
<LayoutEventBlocker className="flex items-center gap-2">
|
<LayoutEventBlocker className="flex items-center gap-2">
|
||||||
<WdCheckbox
|
<WdImgButton
|
||||||
size="xs"
|
className={PrimeIcons.PLUS_CIRCLE}
|
||||||
labelSide="left"
|
onClick={onAddSystem}
|
||||||
label={'Show shortest'}
|
tooltip={{
|
||||||
value={!isSecure}
|
content: 'Click here to add new system to routes',
|
||||||
onChange={handleSecureChange}
|
}}
|
||||||
classNameLabel={clsx('text-red-400')}
|
/>
|
||||||
|
|
||||||
|
<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>
|
</LayoutEventBlocker>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<RoutesWidgetContent />
|
<RoutesWidgetContent />
|
||||||
<RoutesSettingsDialog visible={routeSettingsVisible} setVisible={setRouteSettingsVisible} />
|
<RoutesSettingsDialog visible={routeSettingsVisible} setVisible={setRouteSettingsVisible} />
|
||||||
|
|
||||||
|
<AddSystemDialog
|
||||||
|
title="Add system to routes"
|
||||||
|
visible={openAddSystem}
|
||||||
|
setVisible={() => setOpenAddSystem(false)}
|
||||||
|
onSubmit={handleSubmitAddSystem}
|
||||||
|
/>
|
||||||
</Widget>
|
</Widget>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
.verticalTabsContainer {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 300px;
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.p-tabview {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-tabview-panels {
|
||||||
|
padding: 6px 1rem !important;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-tabview-nav-container {
|
||||||
|
border-right: none;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-tabview-nav {
|
||||||
|
flex-direction: column;
|
||||||
|
width: 150px;
|
||||||
|
min-height: 100%;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
li {
|
||||||
|
width: 100%;
|
||||||
|
border-right: 4px solid var(--surface-hover);
|
||||||
|
background-color: var(--surface-card);
|
||||||
|
|
||||||
|
transition:
|
||||||
|
background-color 200ms,
|
||||||
|
border-right-color 200ms;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--surface-hover);
|
||||||
|
border-right: 4px solid var(--surface-100);
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-tabview-nav-link {
|
||||||
|
transition: color 200ms;
|
||||||
|
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding: 10px;
|
||||||
|
//background-color: var(--surface-card);
|
||||||
|
background-color: initial;
|
||||||
|
border: none;
|
||||||
|
color: var(--gray-400);
|
||||||
|
|
||||||
|
border-radius: initial;
|
||||||
|
font-weight: 400;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.p-tabview-selected {
|
||||||
|
background-color: var(--surface-50);
|
||||||
|
border-right: 4px solid var(--primary-color);
|
||||||
|
|
||||||
|
.p-tabview-nav-link {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
//background-color: var(--surface-hover);
|
||||||
|
border-right: 4px solid var(--primary-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-tabview-panel {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import { Dialog } from 'primereact/dialog';
|
import { Dialog } from 'primereact/dialog';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { Button } from 'primereact/button';
|
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 };
|
export type Setting = { key: string; name: string; value: boolean; isFilter?: boolean };
|
||||||
|
|
||||||
export const COSMIC_SIGNATURE = 'Cosmic Signature';
|
export const COSMIC_SIGNATURE = 'Cosmic Signature';
|
||||||
export const COSMIC_ANOMALY = 'Cosmic Anomaly';
|
export const COSMIC_ANOMALY = 'Cosmic Anomaly';
|
||||||
@@ -24,8 +26,12 @@ export const SystemSignatureSettingsDialog = ({
|
|||||||
onSave,
|
onSave,
|
||||||
onCancel,
|
onCancel,
|
||||||
}: SystemSignatureSettingsDialogProps) => {
|
}: SystemSignatureSettingsDialogProps) => {
|
||||||
|
const [activeIndex, setActiveIndex] = useState(0);
|
||||||
const [settings, setSettings] = useState<Setting[]>(defaultSettings);
|
const [settings, setSettings] = useState<Setting[]>(defaultSettings);
|
||||||
|
|
||||||
|
const filterSettings = settings.filter(setting => setting.isFilter);
|
||||||
|
const userSettings = settings.filter(setting => !setting.isFilter);
|
||||||
|
|
||||||
const handleSettingsChange = (key: string) => {
|
const handleSettingsChange = (key: string) => {
|
||||||
setSettings(prevState => prevState.map(item => (item.key === key ? { ...item, value: !item.value } : item)));
|
setSettings(prevState => prevState.map(item => (item.key === key ? { ...item, value: !item.value } : item)));
|
||||||
};
|
};
|
||||||
@@ -35,23 +41,45 @@ export const SystemSignatureSettingsDialog = ({
|
|||||||
}, [onSave, settings]);
|
}, [onSave, settings]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog header="Filter signatures" visible draggable={false} style={{ width: '300px' }} onHide={onCancel}>
|
<Dialog header="System Signatures Settings" visible={true} onHide={onCancel} className="w-full max-w-lg h-[500px]">
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3 justify-between h-full">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
{settings.map(setting => {
|
<div className={styles.verticalTabsContainer}>
|
||||||
return (
|
<TabView
|
||||||
<div key={setting.key} className="flex items-center">
|
activeIndex={activeIndex}
|
||||||
<Checkbox
|
onTabChange={e => setActiveIndex(e.index)}
|
||||||
inputId={setting.key}
|
className={styles.verticalTabView}
|
||||||
checked={setting.value}
|
>
|
||||||
onChange={() => handleSettingsChange(setting.key)}
|
<TabPanel header="Filters" headerClassName={styles.verticalTabHeader}>
|
||||||
/>
|
<div className="w-full h-full flex flex-col gap-1">
|
||||||
<label htmlFor={setting.key} className="ml-2">
|
{filterSettings.map(setting => {
|
||||||
{setting.name}
|
return (
|
||||||
</label>
|
<PrettySwitchbox
|
||||||
</div>
|
key={setting.key}
|
||||||
);
|
label={setting.name}
|
||||||
})}
|
checked={setting.value}
|
||||||
|
setChecked={() => handleSettingsChange(setting.key)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel header="User Interface" headerClassName={styles.verticalTabHeader}>
|
||||||
|
<div className="w-full h-full flex flex-col gap-1">
|
||||||
|
{userSettings.map(setting => {
|
||||||
|
return (
|
||||||
|
<PrettySwitchbox
|
||||||
|
key={setting.key}
|
||||||
|
label={setting.name}
|
||||||
|
checked={setting.value}
|
||||||
|
setChecked={() => handleSettingsChange(setting.key)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
|
</TabView>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-2 justify-end">
|
<div className="flex gap-2 justify-end">
|
||||||
|
|||||||
@@ -1,36 +1,61 @@
|
|||||||
import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
|
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 { SystemSignaturesContent } from './SystemSignaturesContent';
|
||||||
import {
|
import {
|
||||||
Setting,
|
|
||||||
SystemSignatureSettingsDialog,
|
|
||||||
COSMIC_SIGNATURE,
|
|
||||||
COSMIC_ANOMALY,
|
COSMIC_ANOMALY,
|
||||||
|
COSMIC_SIGNATURE,
|
||||||
DEPLOYABLE,
|
DEPLOYABLE,
|
||||||
STRUCTURE,
|
|
||||||
STARBASE,
|
|
||||||
SHIP,
|
|
||||||
DRONE,
|
DRONE,
|
||||||
|
Setting,
|
||||||
|
SHIP,
|
||||||
|
STARBASE,
|
||||||
|
STRUCTURE,
|
||||||
|
SystemSignatureSettingsDialog,
|
||||||
} from './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 { PrimeIcons } from 'primereact/api';
|
||||||
|
|
||||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
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_v5_2';
|
||||||
|
export const SHOW_DESCRIPTION_COLUMN_SETTING = 'show_description_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[] = [
|
const settings: Setting[] = [
|
||||||
{ key: COSMIC_ANOMALY, name: 'Show Anomalies', value: true },
|
{ key: SHOW_UPDATED_COLUMN_SETTING, name: 'Show Updated Column', value: false, isFilter: false },
|
||||||
{ key: COSMIC_SIGNATURE, name: 'Show Cosmic Signatures', value: true },
|
{ key: SHOW_DESCRIPTION_COLUMN_SETTING, name: 'Show Description Column', value: false, isFilter: false },
|
||||||
{ key: DEPLOYABLE, name: 'Show Deployables', value: true },
|
{ key: LAZY_DELETE_SIGNATURES_SETTING, name: 'Lazy Delete Signatures', value: false, isFilter: false },
|
||||||
{ key: STRUCTURE, name: 'Show Structures', value: true },
|
{ key: KEEP_LAZY_DELETE_SETTING, name: 'Keep "Lazy Delete" Enabled', value: false, isFilter: false },
|
||||||
{ key: STARBASE, name: 'Show Starbase', value: true },
|
{ key: COSMIC_ANOMALY, name: 'Show Anomalies', value: true, isFilter: true },
|
||||||
{ key: SHIP, name: 'Show Ships', value: true },
|
{ key: COSMIC_SIGNATURE, name: 'Show Cosmic Signatures', value: true, isFilter: true },
|
||||||
{ key: DRONE, name: 'Show Drones And Charges', value: true },
|
{ key: DEPLOYABLE, name: 'Show Deployables', value: true, isFilter: true },
|
||||||
|
{ key: STRUCTURE, name: 'Show Structures', value: true, isFilter: true },
|
||||||
|
{ key: STARBASE, name: 'Show Starbase', value: true, isFilter: true },
|
||||||
|
{ key: SHIP, name: 'Show Ships', value: true, isFilter: true },
|
||||||
|
{ key: DRONE, name: 'Show Drones And Charges', value: true, isFilter: true },
|
||||||
|
{ key: SignatureGroup.Wormhole, name: 'Show Wormholes', value: true, isFilter: true },
|
||||||
|
{ key: SignatureGroup.RelicSite, name: 'Show Relic Sites', value: true, isFilter: true },
|
||||||
|
{ key: SignatureGroup.DataSite, name: 'Show Data Sites', value: true, isFilter: true },
|
||||||
|
{ key: SignatureGroup.OreSite, name: 'Show Ore Sites', value: true, isFilter: true },
|
||||||
|
{ key: SignatureGroup.GasSite, name: 'Show Gas Sites', value: true, isFilter: true },
|
||||||
|
{ key: SignatureGroup.CombatSite, name: 'Show Combat Sites', value: true, isFilter: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
const SIGNATURE_SETTINGS_KEY = 'wanderer_system_signature_settings';
|
|
||||||
|
|
||||||
const defaultSettings = () => {
|
const defaultSettings = () => {
|
||||||
return [...settings];
|
return [...settings];
|
||||||
};
|
};
|
||||||
@@ -47,12 +72,25 @@ export const SystemSignatures = () => {
|
|||||||
|
|
||||||
const isNotSelectedSystem = selectedSystems.length !== 1;
|
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[]) => {
|
const handleSettingsChange = useCallback((settings: Setting[]) => {
|
||||||
setSettings(settings);
|
setSettings(settings);
|
||||||
localStorage.setItem(SIGNATURE_SETTINGS_KEY, JSON.stringify(settings));
|
localStorage.setItem(SIGNATURE_SETTINGS_KEY, JSON.stringify(settings));
|
||||||
setVisible(false);
|
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(() => {
|
useEffect(() => {
|
||||||
const restoredSettings = localStorage.getItem(SIGNATURE_SETTINGS_KEY);
|
const restoredSettings = localStorage.getItem(SIGNATURE_SETTINGS_KEY);
|
||||||
|
|
||||||
@@ -61,13 +99,34 @@ export const SystemSignatures = () => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
const compact = useMaxWidth(ref, 260);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Widget
|
<Widget
|
||||||
label={
|
label={
|
||||||
<div className="flex justify-between items-center text-xs w-full h-full">
|
<div className="flex justify-between items-center text-xs w-full h-full" ref={ref}>
|
||||||
<div className="flex gap-1">System Signatures</div>
|
<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">
|
<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
|
<WdImgButton
|
||||||
className={PrimeIcons.QUESTION_CIRCLE}
|
className={PrimeIcons.QUESTION_CIRCLE}
|
||||||
tooltip={{
|
tooltip={{
|
||||||
@@ -91,8 +150,7 @@ export const SystemSignatures = () => {
|
|||||||
</InfoDrawer>
|
</InfoDrawer>
|
||||||
<InfoDrawer title={<b className="text-slate-50">How to delete?</b>}>
|
<InfoDrawer title={<b className="text-slate-50">How to delete?</b>}>
|
||||||
For delete any signature first of all you need select before
|
For delete any signature first of all you need select before
|
||||||
<br /> and then use <b className="text-sky-500">Del</b> or{' '}
|
<br /> and then use <b className="text-sky-500">Del</b>
|
||||||
<b className="text-sky-500">Backspace</b>
|
|
||||||
</InfoDrawer>
|
</InfoDrawer>
|
||||||
</div>
|
</div>
|
||||||
) as React.ReactNode,
|
) as React.ReactNode,
|
||||||
@@ -108,7 +166,7 @@ export const SystemSignatures = () => {
|
|||||||
System is not selected
|
System is not selected
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<SystemSignaturesContent systemId={systemId} settings={settings} />
|
<SystemSignaturesContent systemId={systemId} settings={settings} onLazyDeleteChange={handleLazyDeleteChange} />
|
||||||
)}
|
)}
|
||||||
{visible && (
|
{visible && (
|
||||||
<SystemSignatureSettingsDialog
|
<SystemSignatureSettingsDialog
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||||
import { useClipboard } from '@/hooks/Mapper/hooks/useClipboard';
|
|
||||||
import { parseSignatures } from '@/hooks/Mapper/helpers';
|
import { parseSignatures } from '@/hooks/Mapper/helpers';
|
||||||
import { Commands, OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
import { Commands, OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||||
import { WdTooltip, WdTooltipHandlers } from '@/hooks/Mapper/components/ui-kit';
|
import { WdTooltip, WdTooltipHandlers } from '@/hooks/Mapper/components/ui-kit';
|
||||||
|
import {
|
||||||
|
getGroupIdByRawGroup,
|
||||||
|
GROUPS_LIST,
|
||||||
|
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
|
||||||
|
|
||||||
import { DataTable, DataTableRowClickEvent, DataTableRowMouseEvent, SortOrder } from 'primereact/datatable';
|
import { DataTable, DataTableRowClickEvent, DataTableRowMouseEvent, SortOrder } from 'primereact/datatable';
|
||||||
import { Column } from 'primereact/column';
|
import { Column } from 'primereact/column';
|
||||||
@@ -11,6 +14,7 @@ import useRefState from 'react-usestateref';
|
|||||||
import { Setting } from '../SystemSignatureSettingsDialog';
|
import { Setting } from '../SystemSignatureSettingsDialog';
|
||||||
import { useHotkey } from '@/hooks/Mapper/hooks';
|
import { useHotkey } from '@/hooks/Mapper/hooks';
|
||||||
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
|
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
|
||||||
|
import { useClipboard } from '@/hooks/Mapper/hooks/useClipboard';
|
||||||
|
|
||||||
import classes from './SystemSignaturesContent.module.scss';
|
import classes from './SystemSignaturesContent.module.scss';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
@@ -21,40 +25,55 @@ import {
|
|||||||
getRowColorByTimeLeft,
|
getRowColorByTimeLeft,
|
||||||
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/helpers';
|
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/helpers';
|
||||||
import {
|
import {
|
||||||
|
renderAddedTimeLeft,
|
||||||
|
renderDescription,
|
||||||
renderIcon,
|
renderIcon,
|
||||||
renderInfoColumn,
|
renderInfoColumn,
|
||||||
renderTimeLeft,
|
renderUpdatedTimeLeft,
|
||||||
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders';
|
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders';
|
||||||
import useLocalStorageState from 'use-local-storage-state';
|
import useLocalStorageState from 'use-local-storage-state';
|
||||||
import { PrimeIcons } from 'primereact/api';
|
import { PrimeIcons } from 'primereact/api';
|
||||||
import { SignatureSettings } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings';
|
import { SignatureSettings } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings';
|
||||||
import { useMapEventListener } from '@/hooks/Mapper/events';
|
import { useMapEventListener } from '@/hooks/Mapper/events';
|
||||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||||
|
import { COSMIC_SIGNATURE } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignatureSettingsDialog';
|
||||||
|
import {
|
||||||
|
SHOW_DESCRIPTION_COLUMN_SETTING,
|
||||||
|
SHOW_UPDATED_COLUMN_SETTING,
|
||||||
|
LAZY_DELETE_SIGNATURES_SETTING,
|
||||||
|
KEEP_LAZY_DELETE_SETTING,
|
||||||
|
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures';
|
||||||
type SystemSignaturesSortSettings = {
|
type SystemSignaturesSortSettings = {
|
||||||
sortField: string;
|
sortField: string;
|
||||||
sortOrder: SortOrder;
|
sortOrder: SortOrder;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SORT_DEFAULT_VALUES: SystemSignaturesSortSettings = {
|
const SORT_DEFAULT_VALUES: SystemSignaturesSortSettings = {
|
||||||
sortField: 'updated_at',
|
sortField: 'inserted_at',
|
||||||
sortOrder: -1,
|
sortOrder: -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
interface SystemSignaturesContentProps {
|
interface SystemSignaturesContentProps {
|
||||||
systemId: string;
|
systemId: string;
|
||||||
settings: Setting[];
|
settings: Setting[];
|
||||||
|
hideLinkedSignatures?: boolean;
|
||||||
selectable?: boolean;
|
selectable?: boolean;
|
||||||
onSelect?: (signature: SystemSignature) => void;
|
onSelect?: (signature: SystemSignature) => void;
|
||||||
|
onLazyDeleteChange?: (value: boolean) => void;
|
||||||
}
|
}
|
||||||
export const SystemSignaturesContent = ({ systemId, settings, selectable, onSelect }: SystemSignaturesContentProps) => {
|
export const SystemSignaturesContent = ({
|
||||||
|
systemId,
|
||||||
|
settings,
|
||||||
|
hideLinkedSignatures,
|
||||||
|
selectable,
|
||||||
|
onSelect,
|
||||||
|
onLazyDeleteChange,
|
||||||
|
}: SystemSignaturesContentProps) => {
|
||||||
const { outCommand } = useMapRootState();
|
const { outCommand } = useMapRootState();
|
||||||
|
|
||||||
const [signatures, setSignatures, signaturesRef] = useRefState<SystemSignature[]>([]);
|
const [signatures, setSignatures, signaturesRef] = useRefState<SystemSignature[]>([]);
|
||||||
const [selectedSignatures, setSelectedSignatures] = useState<SystemSignature[]>([]);
|
const [selectedSignatures, setSelectedSignatures] = useState<SystemSignature[]>([]);
|
||||||
const [nameColumnWidth, setNameColumnWidth] = useState('auto');
|
const [nameColumnWidth, setNameColumnWidth] = useState('auto');
|
||||||
const [parsedSignatures, setParsedSignatures] = useState<SystemSignature[]>([]);
|
|
||||||
const [askUser, setAskUser] = useState(false);
|
|
||||||
const [selectedSignature, setSelectedSignature] = useState<SystemSignature | null>(null);
|
const [selectedSignature, setSelectedSignature] = useState<SystemSignature | null>(null);
|
||||||
|
|
||||||
const [hoveredSig, setHoveredSig] = useState<SystemSignature | null>(null);
|
const [hoveredSig, setHoveredSig] = useState<SystemSignature | null>(null);
|
||||||
@@ -66,10 +85,20 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
|||||||
const tableRef = useRef<HTMLDivElement>(null);
|
const tableRef = useRef<HTMLDivElement>(null);
|
||||||
const compact = useMaxWidth(tableRef, 260);
|
const compact = useMaxWidth(tableRef, 260);
|
||||||
const medium = useMaxWidth(tableRef, 380);
|
const medium = useMaxWidth(tableRef, 380);
|
||||||
|
const refData = useRef({ selectable });
|
||||||
|
refData.current = { selectable };
|
||||||
|
|
||||||
const tooltipRef = useRef<WdTooltipHandlers>(null);
|
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(() => {
|
const handleResize = useCallback(() => {
|
||||||
if (tableRef.current) {
|
if (tableRef.current) {
|
||||||
@@ -80,13 +109,39 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const groupSettings = useMemo(() => settings.filter(s => (GROUPS_LIST as string[]).includes(s.key)), [settings]);
|
||||||
|
const showDescriptionColumn = useMemo(
|
||||||
|
() => settings.find(s => s.key === SHOW_DESCRIPTION_COLUMN_SETTING)?.value,
|
||||||
|
[settings],
|
||||||
|
);
|
||||||
|
|
||||||
|
const showUpdatedColumn = useMemo(() => settings.find(s => s.key === SHOW_UPDATED_COLUMN_SETTING)?.value, [settings]);
|
||||||
|
|
||||||
const filteredSignatures = useMemo(() => {
|
const filteredSignatures = useMemo(() => {
|
||||||
return signatures
|
return signatures
|
||||||
.filter(x => settings.find(y => y.key === x.kind)?.value)
|
.filter(x => {
|
||||||
|
if (hideLinkedSignatures && !!x.linked_system) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 === preparedGroup)?.value;
|
||||||
|
} else {
|
||||||
|
return !!x.group && groupSettings.find(y => y.key === preparedGroup)?.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings.find(y => y.key === x.kind)?.value;
|
||||||
|
})
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
return new Date(b.updated_at || 0).getTime() - new Date(a.updated_at || 0).getTime();
|
return new Date(b.updated_at || 0).getTime() - new Date(a.updated_at || 0).getTime();
|
||||||
});
|
});
|
||||||
}, [signatures, settings]);
|
}, [signatures, settings, groupSettings, hideLinkedSignatures]);
|
||||||
|
|
||||||
const handleGetSignatures = useCallback(async () => {
|
const handleGetSignatures = useCallback(async () => {
|
||||||
const { signatures } = await outCommand({
|
const { signatures } = await outCommand({
|
||||||
@@ -94,33 +149,17 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
|||||||
data: { system_id: systemId },
|
data: { system_id: systemId },
|
||||||
});
|
});
|
||||||
|
|
||||||
setAskUser(false);
|
|
||||||
setSignatures(signatures);
|
setSignatures(signatures);
|
||||||
}, [outCommand, systemId]);
|
}, [outCommand, systemId]);
|
||||||
|
|
||||||
// const updateSignatures = useCallback(
|
|
||||||
// async (newSignatures: SystemSignature[], updateOnly: boolean) => {
|
|
||||||
// const { added, updated, removed } = getActualSigs(signaturesRef.current, newSignatures, updateOnly);
|
|
||||||
|
|
||||||
// const { signatures: updatedSignatures } = await outCommand({
|
|
||||||
// type: OutCommand.updateSignatures,
|
|
||||||
// data: {
|
|
||||||
// system_id: systemId,
|
|
||||||
// added,
|
|
||||||
// updated,
|
|
||||||
// removed,
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
|
|
||||||
// setSignatures(() => updatedSignatures);
|
|
||||||
// setSelectedSignatures([]);
|
|
||||||
// },
|
|
||||||
// [outCommand, systemId],
|
|
||||||
// );
|
|
||||||
|
|
||||||
const handleUpdateSignatures = useCallback(
|
const handleUpdateSignatures = useCallback(
|
||||||
async (newSignatures: SystemSignature[], updateOnly: boolean) => {
|
async (newSignatures: SystemSignature[], updateOnly: boolean, skipUpdateUntouched?: boolean) => {
|
||||||
const { added, updated, removed } = getActualSigs(signaturesRef.current, newSignatures, updateOnly);
|
const { added, updated, removed } = getActualSigs(
|
||||||
|
signaturesRef.current,
|
||||||
|
newSignatures,
|
||||||
|
updateOnly,
|
||||||
|
skipUpdateUntouched,
|
||||||
|
);
|
||||||
|
|
||||||
const { signatures: updatedSignatures } = await outCommand({
|
const { signatures: updatedSignatures } = await outCommand({
|
||||||
type: OutCommand.updateSignatures,
|
type: OutCommand.updateSignatures,
|
||||||
@@ -138,34 +177,32 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
|||||||
[outCommand, systemId],
|
[outCommand, systemId],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDeleteSelected = useCallback(async () => {
|
const handleDeleteSelected = useCallback(
|
||||||
if (selectable) {
|
async (e: KeyboardEvent) => {
|
||||||
return;
|
if (selectable) {
|
||||||
}
|
return;
|
||||||
if (selectedSignatures.length === 0) {
|
}
|
||||||
return;
|
if (selectedSignatures.length === 0) {
|
||||||
}
|
return;
|
||||||
const selectedSignaturesEveIds = selectedSignatures.map(x => x.eve_id);
|
}
|
||||||
await handleUpdateSignatures(
|
|
||||||
signatures.filter(x => !selectedSignaturesEveIds.includes(x.eve_id)),
|
e.preventDefault();
|
||||||
false,
|
e.stopPropagation();
|
||||||
);
|
|
||||||
}, [handleUpdateSignatures, selectable, signatures, selectedSignatures]);
|
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(() => {
|
const handleSelectAll = useCallback(() => {
|
||||||
setSelectedSignatures(signatures);
|
setSelectedSignatures(signatures);
|
||||||
}, [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(
|
const handleSelectSignatures = useCallback(
|
||||||
// TODO still will be good to define types if we use typescript
|
// TODO still will be good to define types if we use typescript
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -179,38 +216,51 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
|||||||
[onSelect, selectable],
|
[onSelect, selectable],
|
||||||
);
|
);
|
||||||
|
|
||||||
useHotkey(true, ['a'], handleSelectAll);
|
const handlePaste = async (clipboardContent: string) => {
|
||||||
|
|
||||||
useHotkey(false, ['Backspace', 'Delete'], handleDeleteSelected);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!clipboardContent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newSignatures = parseSignatures(
|
const newSignatures = parseSignatures(
|
||||||
clipboardContent,
|
clipboardContent,
|
||||||
settings.map(x => x.key),
|
settings.map(x => x.key),
|
||||||
);
|
);
|
||||||
|
|
||||||
const { removed } = getActualSigs(signaturesRef.current, newSignatures, false);
|
handleUpdateSignatures(newSignatures, !lazyDeleteValue);
|
||||||
|
|
||||||
if (!signaturesRef.current || !signaturesRef.current.length || !removed.length) {
|
if (lazyDeleteValue && !keepLazyDeleteValue) {
|
||||||
handleUpdateSignatures(newSignatures, false);
|
onLazyDeleteChange?.(false);
|
||||||
} else {
|
|
||||||
setParsedSignatures(newSignatures);
|
|
||||||
setAskUser(true);
|
|
||||||
}
|
}
|
||||||
}, [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(() => {
|
useEffect(() => {
|
||||||
if (!systemId) {
|
if (!systemId) {
|
||||||
setSignatures([]);
|
setSignatures([]);
|
||||||
setAskUser(false);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,19 +294,6 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
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*/) => {
|
const renderToolbar = (/*row: SystemSignature*/) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-end items-center gap-2 mr-[4px]">
|
<div className="flex justify-end items-center gap-2 mr-[4px]">
|
||||||
@@ -308,7 +345,7 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
|||||||
return clsx(classes.TableRowCompact, 'bg-amber-500/50 hover:bg-amber-500/70 transition duration-200');
|
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) {
|
if (!dateClass) {
|
||||||
return clsx(classes.TableRowCompact, 'hover:bg-purple-400/20 transition duration-200');
|
return clsx(classes.TableRowCompact, 'hover:bg-purple-400/20 transition duration-200');
|
||||||
}
|
}
|
||||||
@@ -335,32 +372,56 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
|||||||
header="Group"
|
header="Group"
|
||||||
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
|
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
|
||||||
hidden={compact}
|
hidden={compact}
|
||||||
|
style={{ maxWidth: 110, minWidth: 110, width: 110 }}
|
||||||
sortable
|
sortable
|
||||||
></Column>
|
></Column>
|
||||||
<Column
|
<Column
|
||||||
field="info"
|
field="info"
|
||||||
// header="Info"
|
|
||||||
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
|
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
|
||||||
body={renderInfoColumn}
|
body={renderInfoColumn}
|
||||||
style={{ maxWidth: nameColumnWidth }}
|
style={{ maxWidth: nameColumnWidth }}
|
||||||
hidden={compact || medium}
|
hidden={compact || medium}
|
||||||
></Column>
|
></Column>
|
||||||
|
{showDescriptionColumn && (
|
||||||
|
<Column
|
||||||
|
field="description"
|
||||||
|
header="Description"
|
||||||
|
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
|
||||||
|
body={renderDescription}
|
||||||
|
hidden={compact}
|
||||||
|
sortable
|
||||||
|
></Column>
|
||||||
|
)}
|
||||||
|
|
||||||
<Column
|
<Column
|
||||||
field="updated_at"
|
field="inserted_at"
|
||||||
header="Updated"
|
header="Added"
|
||||||
dataType="date"
|
dataType="date"
|
||||||
bodyClassName="w-[70px] text-ellipsis overflow-hidden whitespace-nowrap"
|
bodyClassName="w-[70px] text-ellipsis overflow-hidden whitespace-nowrap"
|
||||||
body={renderTimeLeft}
|
body={renderAddedTimeLeft}
|
||||||
sortable
|
sortable
|
||||||
></Column>
|
></Column>
|
||||||
|
|
||||||
<Column
|
{showUpdatedColumn && (
|
||||||
bodyClassName="p-0 pl-1 pr-2"
|
<Column
|
||||||
field="group"
|
field="updated_at"
|
||||||
body={renderToolbar}
|
header="Updated"
|
||||||
// headerClassName={headerClasses}
|
dataType="date"
|
||||||
style={{ maxWidth: 26, minWidth: 26, width: 26 }}
|
bodyClassName="w-[70px] text-ellipsis overflow-hidden whitespace-nowrap"
|
||||||
></Column>
|
body={renderUpdatedTimeLeft}
|
||||||
|
sortable
|
||||||
|
></Column>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!selectable && (
|
||||||
|
<Column
|
||||||
|
bodyClassName="p-0 pl-1 pr-2"
|
||||||
|
field="group"
|
||||||
|
body={renderToolbar}
|
||||||
|
// headerClassName={headerClasses}
|
||||||
|
style={{ maxWidth: 26, minWidth: 26, width: 26 }}
|
||||||
|
></Column>
|
||||||
|
)}
|
||||||
</DataTable>
|
</DataTable>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -370,32 +431,13 @@ export const SystemSignaturesContent = ({ systemId, settings, selectable, onSele
|
|||||||
content={hoveredSig ? <SignatureView {...hoveredSig} /> : null}
|
content={hoveredSig ? <SignatureView {...hoveredSig} /> : null}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SignatureSettings
|
{showSignatureSettings && (
|
||||||
systemId={systemId}
|
<SignatureSettings
|
||||||
show={showSignatureSettings}
|
systemId={systemId}
|
||||||
onHide={() => setShowSignatureSettings(false)}
|
show
|
||||||
signatureData={selectedSignature}
|
onHide={() => setShowSignatureSettings(false)}
|
||||||
/>
|
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>
|
</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_ONE_MINUTE = 1000 * 60;
|
||||||
export const TIME_TEN_MINUTES = 1000 * 60 * 10;
|
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.Wormhole]: { id: SignatureGroup.Wormhole, icon: '/icons/brackets/wormhole.png', ...wh },
|
||||||
[SignatureGroup.CosmicSignature]: { id: SignatureGroup.CosmicSignature, icon: '/icons/x_close14.png', w: 9, h: 9 },
|
[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[],
|
oldSignatures: SystemSignature[],
|
||||||
newSignatures: SystemSignature[],
|
newSignatures: SystemSignature[],
|
||||||
updateOnly: boolean,
|
updateOnly: boolean,
|
||||||
|
skipUpdateUntouched?: boolean,
|
||||||
): { added: SystemSignature[]; updated: SystemSignature[]; removed: SystemSignature[] } => {
|
): { added: SystemSignature[]; updated: SystemSignature[]; removed: SystemSignature[] } => {
|
||||||
const updated: SystemSignature[] = [];
|
const updated: SystemSignature[] = [];
|
||||||
const removed: SystemSignature[] = [];
|
const removed: SystemSignature[] = [];
|
||||||
@@ -19,6 +20,8 @@ export const getActualSigs = (
|
|||||||
const isNeedUpgrade = getState(GROUPS_LIST, newSig) > getState(GROUPS_LIST, oldSig);
|
const isNeedUpgrade = getState(GROUPS_LIST, newSig) > getState(GROUPS_LIST, oldSig);
|
||||||
if (isNeedUpgrade) {
|
if (isNeedUpgrade) {
|
||||||
updated.push({ ...oldSig, group: newSig.group, name: newSig.name });
|
updated.push({ ...oldSig, group: newSig.group, name: newSig.name });
|
||||||
|
} else if (!skipUpdateUntouched) {
|
||||||
|
updated.push({ ...oldSig });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!updateOnly) {
|
if (!updateOnly) {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
export * from './renderIcon';
|
export * from './renderIcon';
|
||||||
|
export * from './renderDescription';
|
||||||
export * from './renderName';
|
export * from './renderName';
|
||||||
export * from './renderTimeLeft';
|
export * from './renderAddedTimeLeft';
|
||||||
|
export * from './renderUpdatedTimeLeft';
|
||||||
export * from './renderLinkedSystem';
|
export * from './renderLinkedSystem';
|
||||||
export * from './renderInfoColumn';
|
export * from './renderInfoColumn';
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { SystemSignature } from '@/hooks/Mapper/types';
|
||||||
|
import { TimeLeft } from '@/hooks/Mapper/components/ui-kit';
|
||||||
|
|
||||||
|
export const renderAddedTimeLeft = (row: SystemSignature) => {
|
||||||
|
return (
|
||||||
|
<div className="flex w-full items-center">
|
||||||
|
<TimeLeft cDate={row.inserted_at ? new Date(row.inserted_at) : undefined} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { SystemSignature } from '@/hooks/Mapper/types';
|
||||||
|
|
||||||
|
export const renderDescription = (row: SystemSignature) => {
|
||||||
|
return <span title={row?.description}>{row?.description}</span>;
|
||||||
|
};
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
.whFontSize {
|
|
||||||
font-size: 11px !important;
|
|
||||||
}
|
|
||||||
@@ -1,18 +1,32 @@
|
|||||||
|
import { PrimeIcons } from 'primereact/api';
|
||||||
import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
|
import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
|
||||||
import { SystemViewStandalone, WHClassView } from '@/hooks/Mapper/components/ui-kit';
|
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 clsx from 'clsx';
|
||||||
import { renderName } from './renderName.tsx';
|
import { renderName } from './renderName.tsx';
|
||||||
import classes from './renderInfoColumn.module.scss';
|
|
||||||
|
|
||||||
export const renderInfoColumn = (row: SystemSignature) => {
|
export const renderInfoColumn = (row: SystemSignature) => {
|
||||||
if (!row.group || row.group === SignatureGroup.Wormhole) {
|
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 (
|
return (
|
||||||
<div className="flex justify-start items-center gap-[6px]">
|
<div className="flex justify-start items-center gap-[4px]">
|
||||||
{row.type && (
|
{row.type && (
|
||||||
<WHClassView
|
<WHClassView
|
||||||
className="text-[11px]"
|
className="text-[11px]"
|
||||||
classNameWh={classes.whFontSize}
|
classNameWh="!text-[11px] !font-bold"
|
||||||
highlightName
|
|
||||||
hideWhClass={!!row.linked_system}
|
hideWhClass={!!row.linked_system}
|
||||||
whClassName={row.type}
|
whClassName={row.type}
|
||||||
noOffset
|
noOffset
|
||||||
@@ -20,9 +34,10 @@ export const renderInfoColumn = (row: SystemSignature) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{!row.linked_system && row.type === 'K162' && !!k162TypeOption && <>{renderK162Type(k162TypeOption)}</>}
|
||||||
|
|
||||||
{row.linked_system && (
|
{row.linked_system && (
|
||||||
<>
|
<>
|
||||||
{/*<span className="w-4 h-4 hero-arrow-long-right"></span>*/}
|
|
||||||
<span title={row.linked_system?.solar_system_name}>
|
<span title={row.linked_system?.solar_system_name}>
|
||||||
<SystemViewStandalone
|
<SystemViewStandalone
|
||||||
className={clsx('select-none text-center cursor-context-menu')}
|
className={clsx('select-none text-center cursor-context-menu')}
|
||||||
@@ -32,13 +47,23 @@ export const renderInfoColumn = (row: SystemSignature) => {
|
|||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{row.description && (
|
||||||
|
<WdTooltipWrapper content={row.description}>
|
||||||
|
<span className={clsx(PrimeIcons.EXCLAMATION_CIRCLE, 'text-[12px]')}></span>
|
||||||
|
</WdTooltipWrapper>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row.description != null && row.description.length > 0) {
|
return (
|
||||||
return <span title={row.description}>{row.description}</span>;
|
<div className="flex gap-1 items-center">
|
||||||
}
|
{renderName(row)}{' '}
|
||||||
|
{row.description && (
|
||||||
return renderName(row);
|
<WdTooltipWrapper content={row.description}>
|
||||||
|
<span className={clsx(PrimeIcons.EXCLAMATION_CIRCLE, 'text-[12px]')}></span>
|
||||||
|
</WdTooltipWrapper>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { SystemSignature } from '@/hooks/Mapper/types';
|
import { SystemSignature } from '@/hooks/Mapper/types';
|
||||||
import { TimeLeft } from '@/hooks/Mapper/components/ui-kit';
|
import { TimeLeft } from '@/hooks/Mapper/components/ui-kit';
|
||||||
|
|
||||||
export const renderTimeLeft = (row: SystemSignature) => {
|
export const renderUpdatedTimeLeft = (row: SystemSignature) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full items-center">
|
<div className="flex w-full items-center">
|
||||||
<TimeLeft cDate={row.updated_at ? new Date(row.updated_at) : undefined} />
|
<TimeLeft cDate={row.updated_at ? new Date(row.updated_at) : undefined} />
|
||||||
@@ -7,15 +7,17 @@ import { useCallback, useState } from 'react';
|
|||||||
import { OnTheMap, RightBar } from '@/hooks/Mapper/components/mapRootContent/components';
|
import { OnTheMap, RightBar } from '@/hooks/Mapper/components/mapRootContent/components';
|
||||||
import { MapContextMenu } from '@/hooks/Mapper/components/mapRootContent/components/MapContextMenu/MapContextMenu.tsx';
|
import { MapContextMenu } from '@/hooks/Mapper/components/mapRootContent/components/MapContextMenu/MapContextMenu.tsx';
|
||||||
import { useSkipContextMenu } from '@/hooks/Mapper/hooks/useSkipContextMenu';
|
import { useSkipContextMenu } from '@/hooks/Mapper/hooks/useSkipContextMenu';
|
||||||
import { MapSettings } from "@/hooks/Mapper/components/mapRootContent/components/MapSettings";
|
import { MapSettings } from '@/hooks/Mapper/components/mapRootContent/components/MapSettings';
|
||||||
|
|
||||||
export interface MapRootContentProps {}
|
export interface MapRootContentProps {}
|
||||||
|
|
||||||
// eslint-disable-next-line no-empty-pattern
|
// eslint-disable-next-line no-empty-pattern
|
||||||
export const MapRootContent = ({}: MapRootContentProps) => {
|
export const MapRootContent = ({}: MapRootContentProps) => {
|
||||||
const { mapRef, interfaceSettings } = useMapRootState();
|
const { interfaceSettings } = useMapRootState();
|
||||||
const { isShowMenu } = interfaceSettings;
|
const { isShowMenu } = interfaceSettings;
|
||||||
|
|
||||||
|
const themeClass = `${interfaceSettings.theme ?? 'default'}-theme`;
|
||||||
|
|
||||||
const [showOnTheMap, setShowOnTheMap] = useState(false);
|
const [showOnTheMap, setShowOnTheMap] = useState(false);
|
||||||
const [showMapSettings, setShowMapSettings] = useState(false);
|
const [showMapSettings, setShowMapSettings] = useState(false);
|
||||||
const mapInterface = <MapInterface />;
|
const mapInterface = <MapInterface />;
|
||||||
@@ -26,27 +28,29 @@ export const MapRootContent = ({}: MapRootContentProps) => {
|
|||||||
useSkipContextMenu();
|
useSkipContextMenu();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout map={<MapWrapper refn={mapRef} />}>
|
<div className={themeClass}>
|
||||||
{!isShowMenu ? (
|
<Layout map={<MapWrapper />}>
|
||||||
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
|
{!isShowMenu ? (
|
||||||
<div className="absolute top-0 left-0 w-[calc(100%-3.5rem)] h-full pointer-events-none">
|
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
|
||||||
<Topbar />
|
<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}
|
{mapInterface}
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute top-0 right-0 w-14 h-[calc(100%+3.5rem)] pointer-events-auto">
|
)}
|
||||||
<RightBar onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
|
<OnTheMap show={showOnTheMap} onHide={() => setShowOnTheMap(false)} />
|
||||||
</div>
|
<MapSettings show={showMapSettings} onHide={() => setShowMapSettings(false)} />
|
||||||
</div>
|
</Layout>
|
||||||
) : (
|
</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>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
import classes from './Connections.module.scss';
|
import classes from './Connections.module.scss';
|
||||||
import { Sidebar } from 'primereact/sidebar';
|
import { Sidebar } from 'primereact/sidebar';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState, useCallback } from 'react';
|
||||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||||
import { VirtualScroller, VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
|
import { VirtualScroller, VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { ConnectionOutput, OutCommand, Passage, SolarSystemConnection } from '@/hooks/Mapper/types';
|
import {
|
||||||
|
ConnectionType,
|
||||||
|
ConnectionOutput,
|
||||||
|
ConnectionInfoOutput,
|
||||||
|
OutCommand,
|
||||||
|
Passage,
|
||||||
|
SolarSystemConnection,
|
||||||
|
} from '@/hooks/Mapper/types';
|
||||||
|
|
||||||
import { PassageCard } from './PassageCard';
|
import { PassageCard } from './PassageCard';
|
||||||
import { InfoDrawer, SystemView } from '@/hooks/Mapper/components/ui-kit';
|
import { InfoDrawer, SystemView } from '@/hooks/Mapper/components/ui-kit';
|
||||||
import { kgToTons } from '@/hooks/Mapper/utils/kgToTons.ts';
|
import { kgToTons } from '@/hooks/Mapper/utils/kgToTons.ts';
|
||||||
|
import { TimeAgo } from '@/hooks/Mapper/components/ui-kit';
|
||||||
|
|
||||||
const sortByDate = (a: string, b: string) => new Date(a).getTime() - new Date(b).getTime();
|
const sortByDate = (a: string, b: string) => new Date(a).getTime() - new Date(b).getTime();
|
||||||
|
|
||||||
@@ -68,26 +77,49 @@ export const Connections = ({ selectedConnection, onHide }: OnTheMapProps) => {
|
|||||||
return connections.find(x => x.source === selectedConnection.source && x.target === selectedConnection.target);
|
return connections.find(x => x.source === selectedConnection.source && x.target === selectedConnection.target);
|
||||||
}, [connections, selectedConnection]);
|
}, [connections, selectedConnection]);
|
||||||
|
|
||||||
|
const isWormhole = useMemo(() => {
|
||||||
|
return cnInfo?.type !== ConnectionType.gate;
|
||||||
|
}, [cnInfo]);
|
||||||
|
|
||||||
const [passages, setPassages] = useState<Passage[]>([]);
|
const [passages, setPassages] = useState<Passage[]>([]);
|
||||||
|
const [info, setInfo] = useState<ConnectionInfoOutput | null>(null);
|
||||||
|
|
||||||
|
const loadInfo = useCallback(
|
||||||
|
async (connection: SolarSystemConnection) => {
|
||||||
|
const result = await outCommand<ConnectionInfoOutput>({
|
||||||
|
type: OutCommand.getConnectionInfo,
|
||||||
|
data: {
|
||||||
|
from: connection.source,
|
||||||
|
to: connection.target,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
setInfo(result);
|
||||||
|
},
|
||||||
|
[outCommand],
|
||||||
|
);
|
||||||
|
|
||||||
|
const loadPassages = useCallback(
|
||||||
|
async (connection: SolarSystemConnection) => {
|
||||||
|
const result = await outCommand<ConnectionOutput>({
|
||||||
|
type: OutCommand.getPassages,
|
||||||
|
data: {
|
||||||
|
from: connection.source,
|
||||||
|
to: connection.target,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
setPassages(result.passages.sort((a, b) => sortByDate(b.inserted_at, a.inserted_at)));
|
||||||
|
},
|
||||||
|
[outCommand],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!selectedConnection) {
|
if (!selectedConnection) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
loadInfo(selectedConnection);
|
||||||
const loadInfo = async () => {
|
loadPassages(selectedConnection);
|
||||||
const result = await outCommand<ConnectionOutput>({
|
|
||||||
type: OutCommand.getPassages,
|
|
||||||
data: {
|
|
||||||
from: selectedConnection.source,
|
|
||||||
to: selectedConnection.target,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
setPassages(result.passages.sort((a, b) => sortByDate(b.inserted_at, a.inserted_at)));
|
|
||||||
};
|
|
||||||
|
|
||||||
loadInfo();
|
|
||||||
}, [selectedConnection]);
|
}, [selectedConnection]);
|
||||||
|
|
||||||
const approximateMass = useMemo(() => {
|
const approximateMass = useMemo(() => {
|
||||||
@@ -109,7 +141,7 @@ export const Connections = ({ selectedConnection, onHide }: OnTheMapProps) => {
|
|||||||
>
|
>
|
||||||
<div className={clsx(classes.SidebarContent, '')}>
|
<div className={clsx(classes.SidebarContent, '')}>
|
||||||
{/* Connection Info */}
|
{/* 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 */}
|
{/* Connection Info Row */}
|
||||||
<InfoDrawer title="Connection" rightSide>
|
<InfoDrawer title="Connection" rightSide>
|
||||||
<div className="flex justify-end gap-2 items-center">
|
<div className="flex justify-end gap-2 items-center">
|
||||||
@@ -127,10 +159,25 @@ export const Connections = ({ selectedConnection, onHide }: OnTheMapProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</InfoDrawer>
|
</InfoDrawer>
|
||||||
|
|
||||||
{/* Connection Info Row */}
|
<div className="flex justify-between gap-2">
|
||||||
<InfoDrawer title="Approximate mass of passages" rightSide>
|
{/*Left column*/}
|
||||||
{kgToTons(approximateMass)}
|
<div>
|
||||||
</InfoDrawer>
|
{isWormhole && info?.marl_eol_time && (
|
||||||
|
<InfoDrawer title="Mark EOL Time">
|
||||||
|
<TimeAgo timestamp={info.marl_eol_time} />
|
||||||
|
</InfoDrawer>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/*Right column*/}
|
||||||
|
<div>
|
||||||
|
{isWormhole && (
|
||||||
|
<InfoDrawer title="Approximate mass of passages" rightSide>
|
||||||
|
{kgToTons(approximateMass)}
|
||||||
|
</InfoDrawer>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-2"></div>
|
<div className="flex gap-2"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrap
|
|||||||
import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
|
import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
|
||||||
import { OutCommand } from '@/hooks/Mapper/types';
|
import { OutCommand } from '@/hooks/Mapper/types';
|
||||||
import { MenuItem } from 'primereact/menuitem';
|
import { MenuItem } from 'primereact/menuitem';
|
||||||
|
import { useMapCheckPermissions } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||||
|
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
|
||||||
|
|
||||||
export interface MapContextMenuProps {
|
export interface MapContextMenuProps {
|
||||||
onShowOnTheMap?: () => void;
|
onShowOnTheMap?: () => void;
|
||||||
@@ -14,6 +16,8 @@ export interface MapContextMenuProps {
|
|||||||
export const MapContextMenu = ({ onShowOnTheMap, onShowMapSettings }: MapContextMenuProps) => {
|
export const MapContextMenu = ({ onShowOnTheMap, onShowMapSettings }: MapContextMenuProps) => {
|
||||||
const { outCommand, setInterfaceSettings } = useMapRootState();
|
const { outCommand, setInterfaceSettings } = useMapRootState();
|
||||||
|
|
||||||
|
const canTrackCharacters = useMapCheckPermissions([UserPermission.TRACK_CHARACTER]);
|
||||||
|
|
||||||
const menuRight = useRef<Menu>(null);
|
const menuRight = useRef<Menu>(null);
|
||||||
|
|
||||||
const handleAddCharacter = useCallback(() => {
|
const handleAddCharacter = useCallback(() => {
|
||||||
@@ -24,34 +28,40 @@ export const MapContextMenu = ({ onShowOnTheMap, onShowMapSettings }: MapContext
|
|||||||
}, [outCommand]);
|
}, [outCommand]);
|
||||||
|
|
||||||
const items = useMemo(() => {
|
const items = useMemo(() => {
|
||||||
return [
|
return (
|
||||||
{
|
[
|
||||||
label: 'Tracking',
|
{
|
||||||
icon: 'pi pi-user-plus',
|
label: 'Tracking',
|
||||||
command: handleAddCharacter,
|
icon: 'pi pi-user-plus',
|
||||||
},
|
command: handleAddCharacter,
|
||||||
{
|
visible: true,
|
||||||
label: 'On the map',
|
},
|
||||||
icon: 'pi pi-hashtag',
|
{
|
||||||
command: onShowOnTheMap,
|
label: 'On the map',
|
||||||
},
|
icon: 'pi pi-hashtag',
|
||||||
{ separator: true },
|
command: onShowOnTheMap,
|
||||||
{
|
visible: canTrackCharacters,
|
||||||
label: 'Settings',
|
},
|
||||||
icon: `pi pi-cog`,
|
{ separator: true, visible: true },
|
||||||
command: onShowMapSettings,
|
{
|
||||||
},
|
label: 'Settings',
|
||||||
{
|
icon: `pi pi-cog`,
|
||||||
label: 'Dock menu',
|
command: onShowMapSettings,
|
||||||
icon: 'pi pi-window-maximize',
|
visible: true,
|
||||||
command: () =>
|
},
|
||||||
setInterfaceSettings(x => ({
|
{
|
||||||
...x,
|
label: 'Dock menu',
|
||||||
isShowMenu: !x.isShowMenu,
|
icon: 'pi pi-window-maximize',
|
||||||
})),
|
command: () =>
|
||||||
},
|
setInterfaceSettings(x => ({
|
||||||
] as MenuItem[];
|
...x,
|
||||||
}, [handleAddCharacter, onShowMapSettings, onShowOnTheMap, setInterfaceSettings]);
|
isShowMenu: !x.isShowMenu,
|
||||||
|
})),
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
] as MenuItem[]
|
||||||
|
).filter(item => item.visible);
|
||||||
|
}, [canTrackCharacters, handleAddCharacter, onShowMapSettings, onShowOnTheMap, setInterfaceSettings]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ml-1">
|
<div className="ml-1">
|
||||||
|
|||||||
@@ -3,8 +3,13 @@ import { Dialog } from 'primereact/dialog';
|
|||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { TabPanel, TabView } from 'primereact/tabview';
|
import { TabPanel, TabView } from 'primereact/tabview';
|
||||||
import { PrettySwitchbox } from './components';
|
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 { OutCommand } from '@/hooks/Mapper/types';
|
||||||
|
import { Dropdown } from 'primereact/dropdown';
|
||||||
|
|
||||||
export enum UserSettingsRemoteProps {
|
export enum UserSettingsRemoteProps {
|
||||||
link_signature_on_splash = 'link_signature_on_splash',
|
link_signature_on_splash = 'link_signature_on_splash',
|
||||||
@@ -37,106 +42,182 @@ export interface MapSettingsProps {
|
|||||||
onHide: () => void;
|
onHide: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type CheckboxesList = {
|
type SettingsListItem = {
|
||||||
prop: keyof UserSettings;
|
prop: keyof UserSettings;
|
||||||
label: string;
|
label: string;
|
||||||
}[];
|
type: 'checkbox' | 'dropdown';
|
||||||
|
options?: { label: string; value: string }[];
|
||||||
|
};
|
||||||
|
|
||||||
const COMMON_CHECKBOXES_PROPS: CheckboxesList = [
|
const COMMON_CHECKBOXES_PROPS: SettingsListItem[] = [
|
||||||
{ prop: InterfaceStoredSettingsProps.isShowMinimap, label: 'Show Minimap' },
|
{
|
||||||
|
prop: InterfaceStoredSettingsProps.isShowMinimap,
|
||||||
|
label: 'Show Minimap',
|
||||||
|
type: 'checkbox',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const SYSTEMS_CHECKBOXES_PROPS: CheckboxesList = [
|
const SYSTEMS_CHECKBOXES_PROPS: SettingsListItem[] = [
|
||||||
{ prop: InterfaceStoredSettingsProps.isShowKSpace, label: 'Highlight Low/High-security systems' },
|
{
|
||||||
{ prop: UserSettingsRemoteProps.select_on_spash, label: 'Auto-select splashed' },
|
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 = [
|
const SIGNATURES_CHECKBOXES_PROPS: SettingsListItem[] = [
|
||||||
{ prop: UserSettingsRemoteProps.link_signature_on_splash, label: 'Link signature on splash' },
|
{
|
||||||
|
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 = [
|
const CONNECTIONS_CHECKBOXES_PROPS: SettingsListItem[] = [
|
||||||
{ prop: UserSettingsRemoteProps.delete_connection_with_sigs, label: 'Delete connections to linked signatures' },
|
{
|
||||||
|
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 = [
|
const UI_CHECKBOXES_PROPS: SettingsListItem[] = [
|
||||||
{ prop: InterfaceStoredSettingsProps.isShowMenu, label: 'Enable compact map menu bar' },
|
{
|
||||||
|
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: 'default' },
|
||||||
|
{ label: 'Pathfinder', value: 'pathfinder' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const THEME_SETTING: SettingsListItem = {
|
||||||
|
prop: 'theme',
|
||||||
|
label: 'Theme',
|
||||||
|
type: 'dropdown',
|
||||||
|
options: THEME_OPTIONS,
|
||||||
|
};
|
||||||
|
|
||||||
export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
|
export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
|
||||||
const [activeIndex, setActiveIndex] = useState(0);
|
const [activeIndex, setActiveIndex] = useState(0);
|
||||||
const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState();
|
const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState();
|
||||||
const [userRemoteSettings, setUserRemoteSettings] = useState<UserSettingsRemote>({ ...DEFAULT_REMOTE_SETTINGS });
|
const [userRemoteSettings, setUserRemoteSettings] = useState<UserSettingsRemote>({
|
||||||
|
...DEFAULT_REMOTE_SETTINGS,
|
||||||
|
});
|
||||||
|
|
||||||
const mergedSettings = useMemo(() => {
|
const mergedSettings = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
...interfaceSettings,
|
|
||||||
...userRemoteSettings,
|
...userRemoteSettings,
|
||||||
|
...interfaceSettings,
|
||||||
};
|
};
|
||||||
}, [userRemoteSettings, interfaceSettings]);
|
}, [userRemoteSettings, interfaceSettings]);
|
||||||
|
|
||||||
|
|
||||||
const handleShow = async () => {
|
const handleShow = async () => {
|
||||||
const { user_settings } = await outCommand({
|
const { user_settings } = await outCommand({
|
||||||
type: OutCommand.getUserSettings,
|
type: OutCommand.getUserSettings,
|
||||||
data: null,
|
data: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
setUserRemoteSettings({
|
setUserRemoteSettings({
|
||||||
...user_settings,
|
...user_settings,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChangeChecked = useCallback(
|
const handleSettingChange = useCallback(
|
||||||
(prop: keyof UserSettings) => async (checked: boolean) => {
|
async (prop: keyof UserSettings, value: boolean | string) => {
|
||||||
// @ts-ignore
|
if (UserSettingsRemoteList.includes(prop as any)) {
|
||||||
if (UserSettingsRemoteList.includes(prop)) {
|
|
||||||
const newRemoteSettings = {
|
const newRemoteSettings = {
|
||||||
...userRemoteSettings,
|
...userRemoteSettings,
|
||||||
[prop]: checked,
|
[prop]: value,
|
||||||
};
|
};
|
||||||
|
|
||||||
await outCommand({
|
await outCommand({
|
||||||
type: OutCommand.updateUserSettings,
|
type: OutCommand.updateUserSettings,
|
||||||
data: newRemoteSettings,
|
data: newRemoteSettings,
|
||||||
});
|
});
|
||||||
|
|
||||||
setUserRemoteSettings(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) => {
|
const renderSettingItem = (item: SettingsListItem) => {
|
||||||
return list.map(x => {
|
const currentValue = mergedSettings[item.prop];
|
||||||
|
|
||||||
|
if (item.type === 'checkbox') {
|
||||||
return (
|
return (
|
||||||
<PrettySwitchbox
|
<PrettySwitchbox
|
||||||
key={x.prop}
|
key={item.prop}
|
||||||
label={x.label}
|
label={item.label}
|
||||||
checked={mergedSettings[x.prop]}
|
checked={!!currentValue}
|
||||||
setChecked={handleChangeChecked(x.prop)}
|
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 (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
header="Map settings"
|
header="Map user settings"
|
||||||
visible={show}
|
visible={show}
|
||||||
draggable={false}
|
draggable={false}
|
||||||
style={{ width: '550px' }}
|
style={{ width: '550px' }}
|
||||||
onShow={handleShow}
|
onShow={handleShow}
|
||||||
onHide={() => {
|
onHide={() => {
|
||||||
if (!show) {
|
if (!show) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setActiveIndex(0);
|
setActiveIndex(0);
|
||||||
onHide();
|
onHide();
|
||||||
}}
|
}}
|
||||||
@@ -146,25 +227,35 @@ export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
|
|||||||
<div className={styles.verticalTabsContainer}>
|
<div className={styles.verticalTabsContainer}>
|
||||||
<TabView
|
<TabView
|
||||||
activeIndex={activeIndex}
|
activeIndex={activeIndex}
|
||||||
onTabChange={e => setActiveIndex(e.index)}
|
onTabChange={(e) => setActiveIndex(e.index)}
|
||||||
className={styles.verticalTabView}
|
className={styles.verticalTabView}
|
||||||
>
|
>
|
||||||
<TabPanel header="Common" headerClassName={styles.verticalTabHeader}>
|
<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">
|
<div className="w-full h-full flex flex-col gap-1">
|
||||||
{renderCheckboxesList(SYSTEMS_CHECKBOXES_PROPS)}
|
{renderSettingsList(COMMON_CHECKBOXES_PROPS)}
|
||||||
</div>
|
</div>
|
||||||
</TabPanel>
|
</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}>
|
<TabPanel header="Connections" headerClassName={styles.verticalTabHeader}>
|
||||||
{renderCheckboxesList(CONNECTIONS_CHECKBOXES_PROPS)}
|
{renderSettingsList(CONNECTIONS_CHECKBOXES_PROPS)}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel header="Signatures" headerClassName={styles.verticalTabHeader}>
|
<TabPanel header="Signatures" headerClassName={styles.verticalTabHeader}>
|
||||||
{renderCheckboxesList(SIGNATURES_CHECKBOXES_PROPS)}
|
{renderSettingsList(SIGNATURES_CHECKBOXES_PROPS)}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel header="User Interface" headerClassName={styles.verticalTabHeader}>
|
<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>
|
</TabPanel>
|
||||||
</TabView>
|
</TabView>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import clsx from 'clsx';
|
|||||||
import { CharacterTypeRaw, WithIsOwnCharacter } from '@/hooks/Mapper/types';
|
import { CharacterTypeRaw, WithIsOwnCharacter } from '@/hooks/Mapper/types';
|
||||||
import { CharacterCard, WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
|
import { CharacterCard, WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
|
||||||
import useLocalStorageState from 'use-local-storage-state';
|
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 = {
|
type WindowLocalSettingsType = {
|
||||||
compact: boolean;
|
compact: boolean;
|
||||||
@@ -50,14 +52,22 @@ export const OnTheMap = ({ show, onHide }: OnTheMapProps) => {
|
|||||||
defaultValue: STORED_DEFAULT_VALUES,
|
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 sorted = useMemo(() => {
|
||||||
const out = characters.map(x => ({ ...x, isOwn: userCharacters.includes(x.eve_id) })).sort(sortCharacters);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return out.filter(x => x.online);
|
return out.filter(x => x.online);
|
||||||
}, [characters, settings.hideOffline, userCharacters]);
|
}, [showOffline, characters, settings.hideOffline, userCharacters]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sidebar
|
<Sidebar
|
||||||
@@ -70,14 +80,16 @@ export const OnTheMap = ({ show, onHide }: OnTheMapProps) => {
|
|||||||
>
|
>
|
||||||
<div className={clsx(classes.SidebarContent, '')}>
|
<div className={clsx(classes.SidebarContent, '')}>
|
||||||
<div className={'flex justify-end items-center gap-2 px-3'}>
|
<div className={'flex justify-end items-center gap-2 px-3'}>
|
||||||
<WdCheckbox
|
{showOffline && (
|
||||||
size="m"
|
<WdCheckbox
|
||||||
labelSide="left"
|
size="m"
|
||||||
label={'Hide offline'}
|
labelSide="left"
|
||||||
value={settings.hideOffline}
|
label={'Hide offline'}
|
||||||
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
|
value={settings.hideOffline}
|
||||||
onChange={() => setSettings(() => ({ ...settings, hideOffline: !settings.hideOffline }))}
|
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
|
||||||
/>
|
onChange={() => setSettings(() => ({ ...settings, hideOffline: !settings.hideOffline }))}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<VirtualScroller
|
<VirtualScroller
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
|||||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||||
import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
|
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 {
|
interface RightBarProps {
|
||||||
onShowOnTheMap?: () => void;
|
onShowOnTheMap?: () => void;
|
||||||
onShowMapSettings?: () => void;
|
onShowMapSettings?: () => void;
|
||||||
@@ -14,6 +17,8 @@ interface RightBarProps {
|
|||||||
export const RightBar = ({ onShowOnTheMap, onShowMapSettings }: RightBarProps) => {
|
export const RightBar = ({ onShowOnTheMap, onShowMapSettings }: RightBarProps) => {
|
||||||
const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState();
|
const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState();
|
||||||
|
|
||||||
|
const canTrackCharacters = useMapCheckPermissions([UserPermission.TRACK_CHARACTER]);
|
||||||
|
|
||||||
const isShowMinimap = interfaceSettings.isShowMinimap === undefined ? true : interfaceSettings.isShowMinimap;
|
const isShowMinimap = interfaceSettings.isShowMinimap === undefined ? true : interfaceSettings.isShowMinimap;
|
||||||
|
|
||||||
const handleAddCharacter = useCallback(() => {
|
const handleAddCharacter = useCallback(() => {
|
||||||
@@ -64,19 +69,21 @@ export const RightBar = ({ onShowOnTheMap, onShowMapSettings }: RightBarProps) =
|
|||||||
</button>
|
</button>
|
||||||
</WdTooltipWrapper>
|
</WdTooltipWrapper>
|
||||||
|
|
||||||
<WdTooltipWrapper content="Show on the map" position={TooltipPosition.left}>
|
{canTrackCharacters && (
|
||||||
<button
|
<WdTooltipWrapper content="Show on the map" position={TooltipPosition.left}>
|
||||||
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
|
<button
|
||||||
type="button"
|
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
|
||||||
onClick={onShowOnTheMap}
|
type="button"
|
||||||
>
|
onClick={onShowOnTheMap}
|
||||||
<i className="pi pi-hashtag"></i>
|
>
|
||||||
</button>
|
<i className="pi pi-hashtag"></i>
|
||||||
</WdTooltipWrapper>
|
</button>
|
||||||
|
</WdTooltipWrapper>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col items-center mb-2 gap-1">
|
<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
|
<button
|
||||||
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
|
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Dialog } from 'primereact/dialog';
|
import { Dialog } from 'primereact/dialog';
|
||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
// import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
|
||||||
import { OutCommand, SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
|
import { OutCommand, SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
|
||||||
import { Controller, FormProvider, useForm } from 'react-hook-form';
|
import { Controller, FormProvider, useForm } from 'react-hook-form';
|
||||||
import {
|
import {
|
||||||
@@ -25,102 +24,120 @@ export const SignatureSettings = ({ systemId, show, onHide, signatureData }: Map
|
|||||||
const { outCommand } = useMapRootState();
|
const { outCommand } = useMapRootState();
|
||||||
|
|
||||||
const handleShow = async () => {};
|
const handleShow = async () => {};
|
||||||
const form = useForm<Partial<SystemSignaturePrepared>>({});
|
const signatureForm = useForm<Partial<SystemSignaturePrepared>>({});
|
||||||
|
|
||||||
const handleSave = useCallback(async () => {
|
const handleSave = useCallback(
|
||||||
if (!signatureData) {
|
async (e: any) => {
|
||||||
return;
|
e?.preventDefault();
|
||||||
}
|
if (!signatureData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { group, ...values } = form.getValues();
|
const { group, ...values } = signatureForm.getValues();
|
||||||
let out = { ...signatureData };
|
let out = { ...signatureData };
|
||||||
|
|
||||||
switch (group) {
|
switch (group) {
|
||||||
case SignatureGroup.Wormhole:
|
case SignatureGroup.Wormhole:
|
||||||
if (values.linked_system) {
|
if (values.linked_system) {
|
||||||
await outCommand({
|
await outCommand({
|
||||||
type: OutCommand.linkSignatureToSystem,
|
type: OutCommand.linkSignatureToSystem,
|
||||||
data: {
|
data: {
|
||||||
signature_eve_id: signatureData.eve_id,
|
signature_eve_id: signatureData.eve_id,
|
||||||
solar_system_source: systemId,
|
solar_system_source: systemId,
|
||||||
solar_system_target: values.linked_system,
|
solar_system_target: values.linked_system,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values.type != null) {
|
out = {
|
||||||
out = { ...out, type: values.type };
|
...out,
|
||||||
}
|
custom_info: JSON.stringify({
|
||||||
|
k162Type: values.k162Type,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
if (signatureData.group !== SignatureGroup.Wormhole) {
|
if (values.type != null) {
|
||||||
out = { ...out, name: '' };
|
out = { ...out, type: values.type };
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
if (signatureData.group !== SignatureGroup.Wormhole) {
|
||||||
case SignatureGroup.CosmicSignature:
|
out = { ...out, name: '' };
|
||||||
out = { ...out, type: '', name: '' };
|
}
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (values.name != null) {
|
|
||||||
out = { ...out, name: values.name ?? '' };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (values.description != null) {
|
break;
|
||||||
out = { ...out, description: values.description };
|
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({
|
await outCommand({
|
||||||
type: OutCommand.unlinkSignature,
|
type: OutCommand.updateSignatures,
|
||||||
data: { signature_eve_id: signatureData.eve_id, solar_system_source: systemId },
|
data: {
|
||||||
|
system_id: systemId,
|
||||||
|
added: [],
|
||||||
|
updated: [out],
|
||||||
|
removed: [],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
out = { ...out, type: '' };
|
signatureForm.reset();
|
||||||
}
|
onHide();
|
||||||
|
},
|
||||||
if (group === SignatureGroup.Wormhole && signatureData.linked_system != null && values.linked_system === null) {
|
[signatureForm, onHide, outCommand, signatureData, systemId],
|
||||||
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]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!signatureData) {
|
if (!signatureData) {
|
||||||
form.reset();
|
signatureForm.reset();
|
||||||
return;
|
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,
|
linked_system: linked_system?.solar_system_id.toString() ?? undefined,
|
||||||
|
k162Type: k162Type,
|
||||||
...rest,
|
...rest,
|
||||||
});
|
});
|
||||||
}, [form, signatureData]);
|
}, [signatureForm, signatureData]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
@@ -138,32 +155,34 @@ export const SignatureSettings = ({ systemId, show, onHide, signatureData }: Map
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SystemsSettingsProvider initialValue={{ systemId }}>
|
<SystemsSettingsProvider initialValue={{ systemId }}>
|
||||||
<FormProvider {...form}>
|
<FormProvider {...signatureForm}>
|
||||||
<div className="flex flex-col gap-2 justify-between">
|
<form onSubmit={handleSave}>
|
||||||
<div className="w-full flex flex-col gap-1 p-1 min-h-[150px]">
|
<div className="flex flex-col gap-2 justify-between">
|
||||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
<div className="w-full flex flex-col gap-1 p-1 min-h-[150px]">
|
||||||
<span>Group:</span>
|
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||||
<SignatureGroupSelect name="group" />
|
<span>Group:</span>
|
||||||
</label>
|
<SignatureGroupSelect name="group" />
|
||||||
|
</label>
|
||||||
|
|
||||||
<SignatureGroupContent />
|
<SignatureGroupContent />
|
||||||
|
|
||||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||||
<span>Description:</span>
|
<span>Description:</span>
|
||||||
<Controller
|
<Controller
|
||||||
name="description"
|
name="description"
|
||||||
control={form.control}
|
control={signatureForm.control}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<InputText placeholder="Type description" value={field.value} onChange={field.onChange} />
|
<InputText placeholder="Type description" value={field.value} onChange={field.onChange} />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex gap-2 justify-end">
|
||||||
|
<Button onClick={handleSave} outlined size="small" label="Save"></Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
<div className="flex gap-2 justify-end">
|
|
||||||
<Button onClick={handleSave} outlined size="small" label="Save"></Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
</SystemsSettingsProvider>
|
</SystemsSettingsProvider>
|
||||||
</Dialog>
|
</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 { 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';
|
import { SignatureLeadsToSelect } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureLeadsToSelect';
|
||||||
|
|
||||||
export const SignatureGroupContentWormholes = () => {
|
export const SignatureGroupContentWormholes = () => {
|
||||||
|
const { watch } = useFormContext<SystemSignature>();
|
||||||
|
const type = watch('type');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||||
@@ -9,6 +15,13 @@ export const SignatureGroupContentWormholes = () => {
|
|||||||
<SignatureWormholeTypeSelect name="type" />
|
<SignatureWormholeTypeSelect name="type" />
|
||||||
</label>
|
</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]">
|
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||||
<span>Leads To:</span>
|
<span>Leads To:</span>
|
||||||
<SignatureLeadsToSelect name="linked_system" />
|
<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
|
// @ts-ignore
|
||||||
const renderLinkedSystemItem = (option: { value: string }) => {
|
const renderLinkedSystemItem = (option: { value: string }) => {
|
||||||
if (option.value == null) {
|
const { value } = option;
|
||||||
return <div className="flex gap-2 items-center">No linked system</div>;
|
if (value == null) {
|
||||||
|
return <div className="flex gap-2 items-center">- Unknown -</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
<SystemView systemId={option.value} className={classes.SystemView} />
|
<SystemView systemId={value} className={classes.SystemView} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -65,6 +66,7 @@ export const SignatureLeadsToSelect = ({ name, defaultValue = '' }: SignatureLea
|
|||||||
const leadsToOptions = useMemo(() => {
|
const leadsToOptions = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
{ value: null },
|
{ value: null },
|
||||||
|
|
||||||
...leadsTo
|
...leadsTo
|
||||||
.filter(systemId => {
|
.filter(systemId => {
|
||||||
const systemStatic = systemStatics.get(parseInt(systemId));
|
const systemStatic = systemStatics.get(parseInt(systemId));
|
||||||
|
|||||||
@@ -17,7 +17,17 @@ const getPossibleWormholes = (systemStatic: SolarSystemStaticInfoRaw, wormholes:
|
|||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const spawnClassGroup = SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS[whType];
|
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 {
|
return {
|
||||||
statics: possibleWHTypes
|
statics: possibleWHTypes
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from './SignatureGroupSelect';
|
export * from './SignatureGroupSelect';
|
||||||
export * from './SignatureGroupContent';
|
export * from './SignatureGroupContent';
|
||||||
|
export * from './SignatureK162TypeSelect';
|
||||||
|
|||||||
@@ -1,44 +1,72 @@
|
|||||||
import { Map } from '@/hooks/Mapper/components/map/Map.tsx';
|
import { Map } from '@/hooks/Mapper/components/map/Map.tsx';
|
||||||
import { ForwardedRef, useCallback, useRef, useState } from 'react';
|
import { useCallback, useRef, useState } from 'react';
|
||||||
import { MapHandlers, OutCommand, OutCommandHandler, SolarSystemConnection } from '@/hooks/Mapper/types';
|
import { OutCommand, OutCommandHandler, SolarSystemConnection } from '@/hooks/Mapper/types';
|
||||||
import { MapRootData, useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
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 isEqual from 'lodash.isequal';
|
||||||
import { ContextMenuSystem, useContextMenuSystemHandlers } from '@/hooks/Mapper/components/contexts';
|
import { ContextMenuSystem, useContextMenuSystemHandlers } from '@/hooks/Mapper/components/contexts';
|
||||||
import {
|
import {
|
||||||
SystemCustomLabelDialog,
|
SystemCustomLabelDialog,
|
||||||
SystemSettingsDialog,
|
|
||||||
SystemLinkSignatureDialog,
|
SystemLinkSignatureDialog,
|
||||||
|
SystemSettingsDialog,
|
||||||
} from '@/hooks/Mapper/components/mapInterface/components';
|
} from '@/hooks/Mapper/components/mapInterface/components';
|
||||||
import classes from './MapWrapper.module.scss';
|
import classes from './MapWrapper.module.scss';
|
||||||
import { Connections } from '@/hooks/Mapper/components/mapRootContent/components/Connections';
|
import { Connections } from '@/hooks/Mapper/components/mapRootContent/components/Connections';
|
||||||
import { ContextMenuSystemMultiple, useContextMenuSystemMultipleHandlers } from '../contexts/ContextMenuSystemMultiple';
|
import { ContextMenuSystemMultiple, useContextMenuSystemMultipleHandlers } from '../contexts/ContextMenuSystemMultiple';
|
||||||
import { getSystemById } from '@/hooks/Mapper/helpers';
|
import { getSystemById } from '@/hooks/Mapper/helpers';
|
||||||
import { Node } from 'reactflow';
|
import { Node, XYPosition } from 'reactflow';
|
||||||
|
|
||||||
import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
|
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 { STORED_INTERFACE_DEFAULT_VALUES } from '@/hooks/Mapper/mapRootProvider/MapRootProvider';
|
||||||
|
import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks';
|
||||||
interface MapWrapperProps {
|
import { useCommonMapEventProcessor } from '@/hooks/Mapper/components/mapWrapper/hooks/useCommonMapEventProcessor.ts';
|
||||||
refn: ForwardedRef<MapHandlers>;
|
import {
|
||||||
}
|
AddSystemDialog,
|
||||||
|
SearchOnSubmitCallback,
|
||||||
|
} from '@/hooks/Mapper/components/mapInterface/components/AddSystemDialog';
|
||||||
|
|
||||||
// TODO: INFO - this component needs for abstract work with Map instance
|
// TODO: INFO - this component needs for abstract work with Map instance
|
||||||
export const MapWrapper = ({ refn }: MapWrapperProps) => {
|
export const MapWrapper = () => {
|
||||||
const {
|
const {
|
||||||
update,
|
update,
|
||||||
outCommand,
|
outCommand,
|
||||||
data: { selectedConnections, selectedSystems, hubs, systems },
|
data: { selectedConnections, selectedSystems, hubs, systems },
|
||||||
interfaceSettings: { isShowMenu, isShowMinimap = STORED_INTERFACE_DEFAULT_VALUES.isShowMinimap, isShowKSpace },
|
interfaceSettings: {
|
||||||
|
isShowMenu,
|
||||||
|
isShowMinimap = STORED_INTERFACE_DEFAULT_VALUES.isShowMinimap,
|
||||||
|
isShowKSpace,
|
||||||
|
isThickConnections,
|
||||||
|
isShowBackgroundPattern,
|
||||||
|
isSoftBackground,
|
||||||
|
theme,
|
||||||
|
},
|
||||||
} = useMapRootState();
|
} = useMapRootState();
|
||||||
|
const { deleteSystems } = useDeleteSystems();
|
||||||
|
const { mapRef, runCommand } = useCommonMapEventProcessor();
|
||||||
|
|
||||||
const { open, ...systemContextProps } = useContextMenuSystemHandlers({ systems, hubs, outCommand });
|
const { open, ...systemContextProps } = useContextMenuSystemHandlers({ systems, hubs, outCommand });
|
||||||
const { handleSystemMultipleContext, ...systemMultipleCtxProps } = useContextMenuSystemMultipleHandlers();
|
const { handleSystemMultipleContext, ...systemMultipleCtxProps } = useContextMenuSystemMultipleHandlers();
|
||||||
|
|
||||||
const ref = useRef({ selectedConnections, selectedSystems, systemContextProps, systems });
|
const [openSettings, setOpenSettings] = useState<string | null>(null);
|
||||||
ref.current = { selectedConnections, selectedSystems, systemContextProps, systems };
|
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 });
|
||||||
|
ref.current = { selectedConnections, selectedSystems, systemContextProps, systems, deleteSystems };
|
||||||
|
|
||||||
|
useMapEventListener(event => {
|
||||||
|
switch (event.name) {
|
||||||
|
case Commands.linkSignatureToSystem:
|
||||||
|
setOpenLinkSignatures(event.data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
runCommand(event);
|
||||||
|
});
|
||||||
|
|
||||||
const onSelectionChange: OnMapSelectionChange = useCallback(
|
const onSelectionChange: OnMapSelectionChange = useCallback(
|
||||||
({ systems, connections }) => {
|
({ systems, connections }) => {
|
||||||
@@ -59,9 +87,6 @@ export const MapWrapper = ({ refn }: MapWrapperProps) => {
|
|||||||
[update],
|
[update],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [openSettings, setOpenSettings] = useState<string | null>(null);
|
|
||||||
const [openLinkSignatures, setOpenLinkSignatures] = useState<any | null>(null);
|
|
||||||
const [openCustomLabel, setOpenCustomLabel] = useState<string | null>(null);
|
|
||||||
const handleCommand: OutCommandHandler = useCallback(
|
const handleCommand: OutCommandHandler = useCallback(
|
||||||
event => {
|
event => {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
@@ -95,22 +120,41 @@ export const MapWrapper = ({ refn }: MapWrapperProps) => {
|
|||||||
[open],
|
[open],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [selectedConnection, setSelectedConnection] = useState<SolarSystemConnection | null>(null);
|
|
||||||
|
|
||||||
const handleConnectionDbClick = useCallback((e: SolarSystemConnection) => setSelectedConnection(e), []);
|
const handleConnectionDbClick = useCallback((e: SolarSystemConnection) => setSelectedConnection(e), []);
|
||||||
|
|
||||||
useMapEventListener(event => {
|
const handleManualDelete = useCallback((toDelete: string[]) => {
|
||||||
switch (event.name) {
|
const restDel = toDelete.filter(x => ref.current.systems.some(y => y.id === x));
|
||||||
case Commands.linkSignatureToSystem:
|
if (restDel.length > 0) {
|
||||||
setOpenLinkSignatures(event.data);
|
ref.current.deleteSystems(restDel);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
});
|
}, []);
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Map
|
<Map
|
||||||
ref={refn}
|
ref={mapRef}
|
||||||
onCommand={handleCommand}
|
onCommand={handleCommand}
|
||||||
onSelectionChange={onSelectionChange}
|
onSelectionChange={onSelectionChange}
|
||||||
onConnectionInfoClick={handleConnectionDbClick}
|
onConnectionInfoClick={handleConnectionDbClick}
|
||||||
@@ -119,6 +163,12 @@ export const MapWrapper = ({ refn }: MapWrapperProps) => {
|
|||||||
minimapClasses={!isShowMenu ? classes.MiniMap : undefined}
|
minimapClasses={!isShowMenu ? classes.MiniMap : undefined}
|
||||||
isShowMinimap={isShowMinimap}
|
isShowMinimap={isShowMinimap}
|
||||||
showKSpaceBG={isShowKSpace}
|
showKSpaceBG={isShowKSpace}
|
||||||
|
onManualDelete={handleManualDelete}
|
||||||
|
isThickConnections={isThickConnections}
|
||||||
|
isShowBackgroundPattern={isShowBackgroundPattern}
|
||||||
|
isSoftBackground={isSoftBackground}
|
||||||
|
theme={theme}
|
||||||
|
onAddSystem={onAddSystem}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{openSettings != null && (
|
{openSettings != null && (
|
||||||
@@ -133,6 +183,12 @@ export const MapWrapper = ({ refn }: MapWrapperProps) => {
|
|||||||
<SystemLinkSignatureDialog data={openLinkSignatures} setVisible={() => setOpenLinkSignatures(null)} />
|
<SystemLinkSignatureDialog data={openLinkSignatures} setVisible={() => setOpenLinkSignatures(null)} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<AddSystemDialog
|
||||||
|
visible={!!openAddSystem}
|
||||||
|
setVisible={() => setOpenAddSystem(null)}
|
||||||
|
onSubmit={handleSubmitAddSystem}
|
||||||
|
/>
|
||||||
|
|
||||||
<Connections selectedConnection={selectedConnection} onHide={() => setSelectedConnection(null)} />
|
<Connections selectedConnection={selectedConnection} onHide={() => setSelectedConnection(null)} />
|
||||||
|
|
||||||
<ContextMenuSystem
|
<ContextMenuSystem
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { MutableRefObject, useCallback, useEffect, useRef } from 'react';
|
||||||
|
import { Command, Commands, MapHandlers } from '@/hooks/Mapper/types';
|
||||||
|
import { MapEvent } from '@/hooks/Mapper/events';
|
||||||
|
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||||
|
|
||||||
|
export const useCommonMapEventProcessor = () => {
|
||||||
|
const mapRef = useRef<MapHandlers>() as MutableRefObject<MapHandlers>;
|
||||||
|
const {
|
||||||
|
data: { systems },
|
||||||
|
} = useMapRootState();
|
||||||
|
|
||||||
|
const refQueue = useRef<MapEvent<Command>[]>([]);
|
||||||
|
|
||||||
|
// const ref = useRef({})
|
||||||
|
|
||||||
|
const runCommand = useCallback(({ name, data }: MapEvent<Command>) => {
|
||||||
|
switch (name) {
|
||||||
|
case Commands.addSystems:
|
||||||
|
case Commands.removeSystems:
|
||||||
|
// case Commands.addConnections:
|
||||||
|
refQueue.current.push({ name, data });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore hz why here type error
|
||||||
|
mapRef.current?.command(name, data);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
refQueue.current.forEach(x => mapRef.current?.command(x.name, x.data));
|
||||||
|
refQueue.current = [];
|
||||||
|
}, [systems]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
mapRef,
|
||||||
|
runCommand,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -18,7 +18,7 @@ const Topbar = ({ children }: WithChildren) => {
|
|||||||
<nav
|
<nav
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'px-2 flex items-center justify-center min-w-0 h-12 pointer-events-auto',
|
'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',
|
'bg-opacity-70 bg-neutral-900',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import classes from './CharacterCard.module.scss';
|
|||||||
import { SystemView } from '@/hooks/Mapper/components/ui-kit/SystemView';
|
import { SystemView } from '@/hooks/Mapper/components/ui-kit/SystemView';
|
||||||
import { CharacterTypeRaw, WithIsOwnCharacter } from '@/hooks/Mapper/types';
|
import { CharacterTypeRaw, WithIsOwnCharacter } from '@/hooks/Mapper/types';
|
||||||
import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
|
import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
import { emitMapEvent } from '@/hooks/Mapper/events';
|
||||||
|
|
||||||
type CharacterCardProps = {
|
type CharacterCardProps = {
|
||||||
compact?: boolean;
|
compact?: boolean;
|
||||||
@@ -34,11 +34,12 @@ export const CharacterCard = ({
|
|||||||
useSystemsCache,
|
useSystemsCache,
|
||||||
...char
|
...char
|
||||||
}: CharacterCardProps) => {
|
}: CharacterCardProps) => {
|
||||||
const { mapRef } = useMapRootState();
|
|
||||||
|
|
||||||
const handleSelect = useCallback(() => {
|
const handleSelect = useCallback(() => {
|
||||||
mapRef.current?.command(Commands.centerSystem, char?.location?.solar_system_id?.toString());
|
emitMapEvent({
|
||||||
}, [mapRef, char]);
|
name: Commands.centerSystem,
|
||||||
|
data: char?.location?.solar_system_id?.toString(),
|
||||||
|
});
|
||||||
|
}, [char]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={clsx(classes.CharacterCard, 'w-full text-xs', 'flex flex-col box-border')} onClick={handleSelect}>
|
<div className={clsx(classes.CharacterCard, 'w-full text-xs', 'flex flex-col box-border')} onClick={handleSelect}>
|
||||||
|
|||||||
@@ -3,15 +3,23 @@ import { SystemViewStandalone } from '@/hooks/Mapper/components/ui-kit';
|
|||||||
import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts';
|
import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||||
|
import { SolarSystemStaticInfoRaw } from '@/hooks/Mapper/types';
|
||||||
|
|
||||||
export type SystemViewProps = {
|
export type SystemViewProps = {
|
||||||
systemId: string;
|
systemId: string;
|
||||||
|
systemInfo?: SolarSystemStaticInfoRaw;
|
||||||
hideRegion?: boolean;
|
hideRegion?: boolean;
|
||||||
useSystemsCache?: boolean;
|
useSystemsCache?: boolean;
|
||||||
showCustomName?: boolean;
|
showCustomName?: boolean;
|
||||||
} & WithClassName;
|
} & 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 memSystems = useMemo(() => [systemId], [systemId]);
|
||||||
const { systems, loading } = useLoadSystemStatic({ systems: memSystems });
|
const { systems, loading } = useLoadSystemStatic({ systems: memSystems });
|
||||||
|
|
||||||
@@ -20,9 +28,12 @@ export const SystemView = ({ systemId, hideRegion, className, showCustomName }:
|
|||||||
} = useMapRootState();
|
} = useMapRootState();
|
||||||
|
|
||||||
const systemInfo = useMemo(() => {
|
const systemInfo = useMemo(() => {
|
||||||
|
if (!systemId) {
|
||||||
|
return customSystemInfo;
|
||||||
|
}
|
||||||
return systems.get(parseInt(systemId));
|
return systems.get(parseInt(systemId));
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, [systemId, systems, loading]);
|
}, [customSystemInfo, systemId, systems, loading]);
|
||||||
|
|
||||||
const mapSystemInfo = useMemo(() => {
|
const mapSystemInfo = useMemo(() => {
|
||||||
if (!showCustomName) {
|
if (!showCustomName) {
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ export const SystemViewStandalone = ({
|
|||||||
className,
|
className,
|
||||||
hideRegion,
|
hideRegion,
|
||||||
customName,
|
customName,
|
||||||
|
|
||||||
class_title,
|
class_title,
|
||||||
system_class,
|
system_class,
|
||||||
solar_system_name,
|
solar_system_name,
|
||||||
@@ -38,6 +37,7 @@ export const SystemViewStandalone = ({
|
|||||||
...props
|
...props
|
||||||
}: SystemViewStandaloneProps) => {
|
}: SystemViewStandaloneProps) => {
|
||||||
const classTitleColor = getSystemClassStyles({ systemClass: system_class, security });
|
const classTitleColor = getSystemClassStyles({ systemClass: system_class, security });
|
||||||
|
|
||||||
const isWH = isWormholeSpace(system_class);
|
const isWH = isWormholeSpace(system_class);
|
||||||
|
|
||||||
const handleClick = useCallback(
|
const handleClick = useCallback(
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
.WHClassViewRoot {
|
.WHClassViewRoot {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.WHClassViewContent {
|
.WHClassViewContent {
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ export interface WHClassViewProps {
|
|||||||
whClassName: string;
|
whClassName: string;
|
||||||
noOffset?: boolean;
|
noOffset?: boolean;
|
||||||
useShortTitle?: boolean;
|
useShortTitle?: boolean;
|
||||||
|
hideTooltip?: boolean;
|
||||||
hideWhClass?: boolean;
|
hideWhClass?: boolean;
|
||||||
|
hideWhClassName?: boolean;
|
||||||
highlightName?: boolean;
|
highlightName?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
classNameWh?: string;
|
classNameWh?: string;
|
||||||
@@ -28,7 +30,9 @@ export const WHClassView = ({
|
|||||||
whClassName,
|
whClassName,
|
||||||
noOffset,
|
noOffset,
|
||||||
useShortTitle,
|
useShortTitle,
|
||||||
|
hideTooltip,
|
||||||
hideWhClass,
|
hideWhClass,
|
||||||
|
hideWhClassName,
|
||||||
highlightName,
|
highlightName,
|
||||||
className,
|
className,
|
||||||
classNameWh,
|
classNameWh,
|
||||||
@@ -45,25 +49,27 @@ export const WHClassView = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={clsx(classes.WHClassViewRoot, className)}>
|
<div className={clsx(classes.WHClassViewRoot, className)}>
|
||||||
<Tooltip
|
{!hideTooltip && (
|
||||||
target={`.wh-name${whClassName}${uid}`}
|
<Tooltip
|
||||||
position="right"
|
target={`.wh-name${whClassName}${uid}`}
|
||||||
mouseTrack
|
position="right"
|
||||||
mouseTrackLeft={20}
|
mouseTrack
|
||||||
mouseTrackTop={30}
|
mouseTrackLeft={20}
|
||||||
className="border border-green-300 rounded border-opacity-10 bg-stone-900 bg-opacity-90 "
|
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">
|
<div className="flex gap-3">
|
||||||
<InfoDrawer title="Total mass">{prepareMass(whData.total_mass)}</InfoDrawer>
|
<div className="flex flex-col gap-1">
|
||||||
<InfoDrawer title="Jump mass">{prepareMass(whData.max_mass_per_jump)}</InfoDrawer>
|
<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>
|
||||||
<div className="flex flex-col gap-1">
|
</Tooltip>
|
||||||
<InfoDrawer title="Lifetime">{whData.lifetime}h</InfoDrawer>
|
)}
|
||||||
<InfoDrawer title="Mass regen">{prepareMass(whData.mass_regen)}</InfoDrawer>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
@@ -73,7 +79,7 @@ export const WHClassView = ({
|
|||||||
`wh-name${whClassName}${uid}`,
|
`wh-name${whClassName}${uid}`,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span className={clsx({ [whClassStyle]: highlightName })}>{whClassName}</span>
|
{!hideWhClassName && <span className={clsx({ [whClassStyle]: highlightName })}>{whClassName}</span>}
|
||||||
{!hideWhClass && whClass && (
|
{!hideWhClass && whClass && (
|
||||||
<span className={clsx(classes.WHClassName, whClassStyle, classNameWh)}>
|
<span className={clsx(classes.WHClassName, whClassStyle, classNameWh)}>
|
||||||
{useShortTitle ? whClass.shortTitle : whClass.shortName}
|
{useShortTitle ? whClass.shortTitle : whClass.shortName}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user