Compare commits

..

3 Commits

Author SHA1 Message Date
Dmitry Popov
fe64c27e85 feat(Map): Auto delete linked connections 2024-10-22 09:45:28 +02:00
Dmitry Popov
4ec7b40eb7 Merge branch 'main' into auto-delete-connections 2024-10-21 16:08:56 +02:00
Dmitry Popov
9fab757bea feat(Map): Auto delete linked connections 2024-10-21 10:30:11 +02:00
204 changed files with 6873 additions and 14898 deletions

View File

@@ -1,7 +1,12 @@
FROM elixir:1.17-otp-27
FROM elixir:1.16-otp-25
RUN apt install -yq curl gnupg
RUN apt update -yq
RUN apt install -yq curl gnupg mc inotify-tools
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

View File

@@ -2,7 +2,7 @@ version: "0.1"
services:
db:
image: postgres:13-alpine
image: postgres:14.3
restart: always
environment:
POSTGRES_USER: postgres
@@ -10,13 +10,13 @@ services:
ports:
- "5432:5432"
volumes:
- db-new:/var/lib/postgresql/data
- db:/var/lib/postgresql/data
wanderer:
environment:
PORT: 8000
DB_HOST: db
WEB_APP_URL: "http://localhost:8000"
WEB_APP_URL: "http://localhost:4444"
ERL_AFLAGS: "-kernel shell_history enabled"
build:
context: .
@@ -33,4 +33,4 @@ services:
volumes:
elixir-artifacts: {}
db-new: {}
db: {}

View File

@@ -58,8 +58,6 @@ jobs:
otp: ["27"]
elixir: ["1.17"]
node-version: ["18.x"]
outputs:
commit_hash: ${{ steps.generate-changelog.outputs.commit_hash }}
steps:
- name: Prepare
run: |
@@ -110,17 +108,16 @@ jobs:
run: mix compile
- name: Generate Changelog & Update Tag Version
id: generate-changelog
run: |
git config --global user.name 'CI'
git config --global user.email 'ci@users.noreply.github.com'
mix git_ops.release --force-patch --yes
git push --follow-tags
echo "commit_hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
docker:
name: 🛠 Build Docker Images
needs: build
needs:
- build
runs-on: ubuntu-22.04
permissions:
checks: write
@@ -144,7 +141,6 @@ jobs:
- name: ⬇️ Checkout repo
uses: actions/checkout@v3
with:
ref: ${{ needs.build.outputs.commit_hash }}
fetch-depth: 0
- name: Prepare Changelog
@@ -193,30 +189,6 @@ jobs:
- name: Image digest
run: echo ${{ steps.build.outputs.digest }}
- uses: markpatterson27/markdown-to-output@v1
id: extract-changelog
with:
filepath: CHANGELOG.md
- name: Get content
uses: 2428392/gh-truncate-string-action@v1.3.0
id: get-content
with:
stringToTruncate: |
📣 Wanderer new release available 🎉
**Version**: ${{ steps.get-latest-tag.outputs.tag }}
${{ steps.extract-changelog.outputs.body }}
maxLength: 500
truncationSymbol: "…"
- name: Discord Webhook Action
uses: tsickert/discord-webhook@v5.3.0
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
content: ${{ steps.get-content.outputs.string }}
create-release:
name: 🏷 Create Release
runs-on: ubuntu-22.04

View File

@@ -18,4 +18,4 @@ jobs:
key: ${{ secrets.SSH_KEY }}
port: ${{ secrets.PORT }}
script: |
/home/wanderer/app/deploy.sh ${{ github.event.release.tag_name }}
/app/release/linux/deploy.sh ${{ github.event.release.tag_name }}

View File

@@ -1,3 +1,3 @@
erlang 26.2.5.5
elixir 1.17.3-otp-26
erlang 25.3
elixir 1.16-otp-25
nodejs 18.0.0

View File

@@ -2,377 +2,6 @@
<!-- changelog -->
## [v1.29.5](https://github.com/wanderer-industries/wanderer/compare/v1.29.4...v1.29.5) (2024-12-14)
### Bug Fixes:
* Core: Fix character trackers cleanup
## [v1.29.4](https://github.com/wanderer-industries/wanderer/compare/v1.29.3...v1.29.4) (2024-12-10)
### Bug Fixes:
* Core: Small fixes
## [v1.29.3](https://github.com/wanderer-industries/wanderer/compare/v1.29.2...v1.29.3) (2024-12-07)
### Bug Fixes:
* Core: Increased eve DB data download timeout
## [v1.29.2](https://github.com/wanderer-industries/wanderer/compare/v1.29.1...v1.29.2) (2024-12-07)
### Bug Fixes:
* Core: Fix unpkg CDN issues, fix Abyssals sites adding as systems on map
## [v1.29.1](https://github.com/wanderer-industries/wanderer/compare/v1.29.0...v1.29.1) (2024-12-05)
## [v1.29.0](https://github.com/wanderer-industries/wanderer/compare/v1.28.1...v1.29.0) (2024-12-05)
### Features:
* Signatures: Show 'Unsplashed' signatures on the map (optionally)
## [v1.28.1](https://github.com/wanderer-industries/wanderer/compare/v1.28.0...v1.28.1) (2024-12-04)
## [v1.28.0](https://github.com/wanderer-industries/wanderer/compare/v1.27.1...v1.28.0) (2024-12-04)
### Features:
* Map: Added an option to show 'Offline characters' to map admins & managers only
## [v1.27.1](https://github.com/wanderer-industries/wanderer/compare/v1.27.0...v1.27.1) (2024-12-04)
### Bug Fixes:
* Map: Fix 'On the map' visibility
## [v1.27.0](https://github.com/wanderer-industries/wanderer/compare/v1.26.1...v1.27.0) (2024-12-03)
### Features:
* Map: Hide 'On the map' list for 'Viewer' role
## [v1.26.1](https://github.com/wanderer-industries/wanderer/compare/v1.26.0...v1.26.1) (2024-12-03)
### Bug Fixes:
* Signatures: Fix error on splash wh
## [v1.26.0](https://github.com/wanderer-industries/wanderer/compare/v1.25.2...v1.26.0) (2024-12-03)
### Features:
* Signatures: Keep 'Lazy delete' enabled setting
## [v1.25.2](https://github.com/wanderer-industries/wanderer/compare/v1.25.1...v1.25.2) (2024-12-01)
### Bug Fixes:
* Signatures: Fix lazy delete on system switch
## [v1.25.1](https://github.com/wanderer-industries/wanderer/compare/v1.25.0...v1.25.1) (2024-11-28)
### Bug Fixes:
* Signatures: Fix colors & add 'Backspace' hotkey to delete signatures
## [v1.25.0](https://github.com/wanderer-industries/wanderer/compare/v1.24.2...v1.25.0) (2024-11-28)
### Features:
* Signatures: Automatically remove signature if linked system removed
## [v1.24.2](https://github.com/wanderer-industries/wanderer/compare/v1.24.1...v1.24.2) (2024-11-27)
### Bug Fixes:
* Signatures: Fix paste signatures
## [v1.24.1](https://github.com/wanderer-industries/wanderer/compare/v1.24.0...v1.24.1) (2024-11-27)
## [v1.24.0](https://github.com/wanderer-industries/wanderer/compare/v1.23.0...v1.24.0) (2024-11-27)
### Features:
* Signatures: Added "Lazy delete" option & got rid of update popup
## [v1.23.0](https://github.com/wanderer-industries/wanderer/compare/v1.22.0...v1.23.0) (2024-11-26)
### Features:
* Map: Lock systems available to manager/admin roles only (#75)
* Map: Lock systems available to manager/admin roles only
* Map: Fix add system & add acl member select behaviour
## [v1.22.0](https://github.com/wanderer-industries/wanderer/compare/v1.21.0...v1.22.0) (2024-11-26)
### Features:
* Map: Rework design of checkboxes in Signatures settings dialog. Rework design of checkboxes in Routes settings dialog. Now signature will deleteing by Delete hotkey was Backspace. Fixed size of group column in signatures list. Instead Updated column will be Added, updated may be turn on in settings. (#76)
## [v1.21.0](https://github.com/wanderer-industries/wanderer/compare/v1.20.1...v1.21.0) (2024-11-24)
### Features:
* Map: add new gate design, change EOL placement
## [v1.20.1](https://github.com/wanderer-industries/wanderer/compare/v1.20.0...v1.20.1) (2024-11-22)
## [v1.20.0](https://github.com/wanderer-industries/wanderer/compare/v1.19.3...v1.20.0) (2024-11-22)
### Features:
* Core: Add connection type for Gates, add new Update logic
## [v1.19.3](https://github.com/wanderer-industries/wanderer/compare/v1.19.2...v1.19.3) (2024-11-20)
### Bug Fixes:
* Core: Fix adding systems on splash (#71)
* Core: Fix adding systems on splash
## [v1.19.2](https://github.com/wanderer-industries/wanderer/compare/v1.19.1...v1.19.2) (2024-11-19)
## [v1.19.1](https://github.com/wanderer-industries/wanderer/compare/v1.19.0...v1.19.1) (2024-11-19)
## [v1.19.0](https://github.com/wanderer-industries/wanderer/compare/v1.18.1...v1.19.0) (2024-11-19)
### Features:
* Signatures: Add user setting to show Inserted time in a separate column
## [v1.18.1](https://github.com/wanderer-industries/wanderer/compare/v1.18.0...v1.18.1) (2024-11-17)
## [v1.18.0](https://github.com/wanderer-industries/wanderer/compare/v1.17.0...v1.18.0) (2024-11-16)
### Features:
* Map: a lot of design issues
## [v1.17.0](https://github.com/wanderer-industries/wanderer/compare/v1.16.1...v1.17.0) (2024-11-15)
### Features:
* Signatures: Add user setting to show Description in a separate column
## [v1.16.1](https://github.com/wanderer-industries/wanderer/compare/v1.16.0...v1.16.1) (2024-11-15)
### Bug Fixes:
* Signatures: Fix signature stored filters
## [v1.16.0](https://github.com/wanderer-industries/wanderer/compare/v1.15.5...v1.16.0) (2024-11-15)
### Features:
* Signatures: Add additional filters support to signature list, show description icon
## [v1.15.5](https://github.com/wanderer-industries/wanderer/compare/v1.15.4...v1.15.5) (2024-11-14)
## [v1.15.4](https://github.com/wanderer-industries/wanderer/compare/v1.15.3...v1.15.4) (2024-11-14)
### Bug Fixes:
* Core: Untracked characters still tracked on map (#63)
## [v1.15.3](https://github.com/wanderer-industries/wanderer/compare/v1.15.2...v1.15.3) (2024-11-13)
## [v1.15.2](https://github.com/wanderer-industries/wanderer/compare/v1.15.1...v1.15.2) (2024-11-07)
## [v1.15.1](https://github.com/wanderer-industries/wanderer/compare/v1.15.0...v1.15.1) (2024-11-07)
### Bug Fixes:
* Dev: Update .devcontainer instructions
## [v1.15.0](https://github.com/wanderer-industries/wanderer/compare/v1.14.1...v1.15.0) (2024-11-07)
### Features:
* Connections: Add connection mark EOL time (#56)
## [v1.14.1](https://github.com/wanderer-industries/wanderer/compare/v1.14.0...v1.14.1) (2024-11-06)
### Bug Fixes:
* Core: Fix character tracking permissions
## [v1.14.0](https://github.com/wanderer-industries/wanderer/compare/v1.13.12...v1.14.0) (2024-11-05)
### Features:
* ACL: Add an ability to assign member role without DnD
## [v1.13.12](https://github.com/wanderer-industries/wanderer/compare/v1.13.11...v1.13.12) (2024-11-04)
### Bug Fixes:
* Map: Fix system revert issues
## [v1.13.11](https://github.com/wanderer-industries/wanderer/compare/v1.13.10...v1.13.11) (2024-11-02)
### Bug Fixes:
* Map: Fix system revert issues
## [v1.13.10](https://github.com/wanderer-industries/wanderer/compare/v1.13.9...v1.13.10) (2024-11-01)
### Bug Fixes:
* Map: Fix system revert issues
## [v1.13.9](https://github.com/wanderer-industries/wanderer/compare/v1.13.8...v1.13.9) (2024-11-01)
## [v1.13.8](https://github.com/wanderer-industries/wanderer/compare/v1.13.7...v1.13.8) (2024-10-28)
## [v1.13.0](https://github.com/wanderer-industries/wanderer/compare/v1.12.11...v1.13.0) (2024-10-28)
### Features:
* Core: Use ESI /characters/affiliation API
## [v1.12.4](https://github.com/wanderer-industries/wanderer/compare/v1.12.3...v1.12.4) (2024-10-21)
@@ -391,6 +20,11 @@
* Map: Fix regression issues
## [v1.12.2](https://github.com/wanderer-industries/wanderer/compare/v1.12.1...v1.12.2) (2024-10-16)
## [v1.12.1](https://github.com/wanderer-industries/wanderer/compare/v1.12.0...v1.12.1) (2024-10-16)
@@ -409,6 +43,31 @@
* Map: Prettify user settings
## [v1.11.5](https://github.com/wanderer-industries/wanderer/compare/v1.11.4...v1.11.5) (2024-10-16)
## [v1.11.4](https://github.com/wanderer-industries/wanderer/compare/v1.11.3...v1.11.4) (2024-10-16)
## [v1.11.3](https://github.com/wanderer-industries/wanderer/compare/v1.11.2...v1.11.3) (2024-10-16)
## [v1.11.2](https://github.com/wanderer-industries/wanderer/compare/v1.11.1...v1.11.2) (2024-10-15)
## [v1.11.1](https://github.com/wanderer-industries/wanderer/compare/v1.11.0...v1.11.1) (2024-10-14)
## [v1.11.0](https://github.com/wanderer-industries/wanderer/compare/v1.10.0...v1.11.0) (2024-10-14)

View File

@@ -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 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 dont 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. |
| **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. |
| | 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 dont 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.|
| **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/).
@@ -54,13 +54,7 @@ Now you can visit [`localhost:8000`](http://localhost:8000) from your browser.
#### Using .devcontainer
- Run devcontainer
- 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
- See how to start server in #setup section
#### Using nix flakes

View File

@@ -466,467 +466,3 @@ body {
transform: rotate(-360deg);
}
}
/* Map refresh */
.socket {
scale: 0.5;
width: 150px;
height: 150px;
left: 50%;
/* margin-left: -75px; */
top: 50%;
/* margin-top: -50px; */
}
.hex-brick {
background: #000;
width: 30px;
height: 17px;
position: absolute;
top: 5px;
animation-name: fade;
animation-duration: 2s;
animation-iteration-count: infinite;
-webkit-animation-name: fade;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
}
.hex-brick--active {
animation-name: fade-active;
-webkit-animation-name: fade-active;
}
.h2 {
transform: rotate(60deg);
-webkit-transform: rotate(60deg);
}
.h3 {
transform: rotate(-60deg);
-webkit-transform: rotate(-60deg);
}
.gel {
height: 30px;
width: 30px;
transition: all 0.3s;
-webkit-transition: all 0.3s;
position: absolute;
top: 50%;
left: 50%;
}
.center-gel {
margin-left: -15px;
margin-top: -15px;
animation-name: pulse-version;
animation-duration: 2s;
animation-iteration-count: infinite;
-webkit-animation-name: pulse-version;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
}
.c1 {
margin-left: -47px;
margin-top: -15px;
}
.c2 {
margin-left: -31px;
margin-top: -43px;
}
.c3 {
margin-left: 1px;
margin-top: -43px;
}
.c4 {
margin-left: 17px;
margin-top: -15px;
}
.c5 {
margin-left: -31px;
margin-top: 13px;
}
.c6 {
margin-left: 1px;
margin-top: 13px;
}
.c7 {
margin-left: -63px;
margin-top: -43px;
}
.c8 {
margin-left: 33px;
margin-top: -43px;
}
.c9 {
margin-left: -15px;
margin-top: 41px;
}
.c10 {
margin-left: -63px;
margin-top: 13px;
}
.c11 {
margin-left: 33px;
margin-top: 13px;
}
.c12 {
margin-left: -15px;
margin-top: -71px;
}
.c13 {
margin-left: -47px;
margin-top: -71px;
}
.c14 {
margin-left: 17px;
margin-top: -71px;
}
.c15 {
margin-left: -47px;
margin-top: 41px;
}
.c16 {
margin-left: 17px;
margin-top: 41px;
}
.c17 {
margin-left: -79px;
margin-top: -15px;
}
.c18 {
margin-left: 49px;
margin-top: -15px;
}
.c19 {
margin-left: -63px;
margin-top: -99px;
}
.c20 {
margin-left: 33px;
margin-top: -99px;
}
.c21 {
margin-left: 1px;
margin-top: -99px;
}
.c22 {
margin-left: -31px;
margin-top: -99px;
}
.c23 {
margin-left: -63px;
margin-top: 69px;
}
.c24 {
margin-left: 33px;
margin-top: 69px;
}
.c25 {
margin-left: 1px;
margin-top: 69px;
}
.c26 {
margin-left: -31px;
margin-top: 69px;
}
.c27 {
margin-left: -79px;
margin-top: -15px;
}
.c28 {
margin-left: -95px;
margin-top: -43px;
}
.c29 {
margin-left: -95px;
margin-top: 13px;
}
.c30 {
margin-left: 49px;
margin-top: 41px;
}
.c31 {
margin-left: -79px;
margin-top: -71px;
}
.c32 {
margin-left: -111px;
margin-top: -15px;
}
.c33 {
margin-left: 65px;
margin-top: -43px;
}
.c34 {
margin-left: 65px;
margin-top: 13px;
}
.c35 {
margin-left: -79px;
margin-top: 41px;
}
.c36 {
margin-left: 49px;
margin-top: -71px;
}
.c37 {
margin-left: 81px;
margin-top: -15px;
}
.r1 {
animation-name: pulse-version;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-delay: 0.2s;
-webkit-animation-name: pulse-version;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-delay: 0.2s;
}
.r2 {
animation-name: pulse-version;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-delay: 0.4s;
-webkit-animation-name: pulse-version;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-delay: 0.4s;
}
.r3 {
animation-name: pulse-version;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-delay: 0.6s;
-webkit-animation-name: pulse-version;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-delay: 0.6s;
}
.r1 > .hex-brick {
animation-name: fade;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-delay: 0.2s;
-webkit-animation-name: fade;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-delay: 0.2s;
}
.r1 > .hex-brick--active {
animation-name: fade-active;
-webkit-animation-name: fade-active;
}
.r2 > .hex-brick {
animation-name: fade;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-delay: 0.4s;
-webkit-animation-name: fade;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-delay: 0.4s;
}
.r2 > .hex-brick--active {
animation-name: fade-active;
-webkit-animation-name: fade-active;
}
.r3 > .hex-brick {
animation-name: fade;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-delay: 0.6s;
-webkit-animation-name: fade;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-delay: 0.6s;
}
.r3 > .hex-brick--active {
animation-name: fade-active;
-webkit-animation-name: fade-active;
}
@keyframes pulse-version {
0% {
-webkit-transform: scale(1);
transform: scale(1);
}
50% {
-webkit-transform: scale(0.01);
transform: scale(0.01);
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
}
}
@keyframes fade {
0% {
background: #09d0e2;
}
50% {
background: #8ae6ee;
}
100% {
background: #09d0e2;
}
}
@keyframes fade-active {
0% {
background: #ff52d9;
}
50% {
background: #ff52d9;
}
100% {
background: #ff52d9;
}
}
@-webkit-keyframes pulse {
0% {
-webkit-transform: scale(1);
transform: scale(1);
}
50% {
-webkit-transform: scale(0.01);
transform: scale(0.01);
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
}
}
@-webkit-keyframes fade {
0% {
background: #abf8ff;
}
50% {
background: #389ca6;
}
100% {
background: #abf8ff;
}
}
/* Map refresh END */
.inputContainer {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
}
.inputContainer > span:nth-child(1),
.inputContainer > label:nth-child(1) {
color: var(--gray-200);
font-size: 13px;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
.inputContainer > :nth-child(2) {
border-bottom: 2px dotted #3f3f3f;
height: 1px;
margin: 0 12px;
}
.smallInputSwitch {
height: 100%;
display: flex;
align-items: center;
}
.smallInputSwitch .p-inputswitch {
height: 1rem;
width: 2rem;
}
.smallInputSwitch .p-inputswitch.p-inputswitch-checked .p-inputswitch-slider::before {
transform: translateX(1rem);
}
.smallInputSwitch .p-inputswitch.p-highlight .p-inputswitch-slider:before {
transform: translateX(1rem);
}
.smallInputSwitch .p-inputswitch .p-inputswitch-slider::before {
width: 0.8rem;
height: 0.8rem;
margin-top: -0.4rem;
margin-left: -3px;
}
.checkboxRoot.sizeXS {
width: 14px;
height: 14px;
}
.checkboxRoot.sizeXS .p-checkbox-box,
.checkboxRoot.sizeXS .p-checkbox-input {
width: 14px;
height: 14px;
}
.checkboxRoot.sizeM {
width: 16px;
height: 16px;
}
.checkboxRoot.sizeM .p-checkbox-box,
.checkboxRoot.sizeM .p-checkbox-input {
width: 16px;
height: 16px;
}

View File

@@ -15,10 +15,11 @@ const ErrorFallback = () => {
};
export default function MapRoot({ hooks }) {
const mapRef = useRef<MapHandlers>(null);
const providerRef = useRef<MapHandlers>(null);
const hooksRef = useRef<any>(hooks);
const mapperHandlerRefs = useRef([providerRef]);
const mapperHandlerRefs = useRef([mapRef, providerRef]);
const { handleCommand, handleMapEvent, handleMapEvents } = useMapperHandlers(mapperHandlerRefs.current, hooksRef);
@@ -40,7 +41,7 @@ export default function MapRoot({ hooks }) {
return (
<PrimeReactProvider>
<MapRootProvider fwdRef={providerRef} outCommand={handleCommand}>
<MapRootProvider fwdRef={providerRef} outCommand={handleCommand} mapRef={mapRef}>
<ErrorBoundary FallbackComponent={ErrorFallback} onError={logError}>
<ReactFlowProvider>
<MapRootContent />

View File

@@ -85,26 +85,3 @@
}
}
.p-dropdown-label, .p-inputtext {
padding: 0.25rem 0.75rem;
font-size: 14px;
}
.p-dropdown-item {
padding: 0.25rem 0.5rem;
font-size: 14px;
}
.p-dropdown-item-group {
padding: 0.25rem 0.75rem;
font-size: 14px;
}
.p-dropdown-trigger {
width: 14px;
margin: 0 12px;
}
.p-dropdown-empty-message {
padding: 0.25rem 0.5rem;
}

View File

@@ -1,19 +1,20 @@
import { useCallback } from 'react';
import clsx from 'clsx';
import { useAutoAnimate } from '@formkit/auto-animate/react';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
import { CharacterTypeRaw } from '@/hooks/Mapper/types';
import { emitMapEvent } from '@/hooks/Mapper/events';
const Characters = ({ data }: { data: CharacterTypeRaw[] }) => {
const [parent] = useAutoAnimate();
const { mapRef } = useMapRootState();
const handleSelect = useCallback((character: CharacterTypeRaw) => {
emitMapEvent({
name: Commands.centerSystem,
data: character?.location?.solar_system_id?.toString(),
});
}, []);
const handleSelect = useCallback(
(character: CharacterTypeRaw) => {
mapRef.current?.command(Commands.centerSystem, character?.location?.solar_system_id?.toString());
},
[mapRef],
);
const items = data.map(character => (
<li

View File

@@ -18,7 +18,7 @@ export const useTagMenu = (
ref.current = { onSystemTag, systems, systemId };
return useCallback(() => {
const { onSystemTag, systemId, systems } = ref.current;
const { onSystemTag, systemId , systems} = ref.current;
const system = systemId ? getSystemById(systems, systemId) : undefined;
const isSelectedLetters = AVAILABLE_LETTERS.includes(system?.tag ?? '');

View File

@@ -4,7 +4,6 @@ import { OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.
import { SolarSystemRawType } from '@/hooks/Mapper/types';
import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/types.ts';
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks';
interface UseContextMenuSystemHandlersProps {
hubs: string[];
@@ -17,10 +16,8 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
const [system, setSystem] = useState<string>();
const { deleteSystems } = useDeleteSystems();
const ref = useRef({ hubs, system, systems, outCommand, deleteSystems });
ref.current = { hubs, system, systems, outCommand, deleteSystems };
const ref = useRef({ hubs, system, systems, outCommand });
ref.current = { hubs, system, systems, outCommand };
const open = useCallback((ev: any, systemId: string) => {
setSystem(systemId);
@@ -30,12 +27,12 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
}, []);
const onDeleteSystem = useCallback(() => {
const { system, deleteSystems } = ref.current;
const { system, outCommand } = ref.current;
if (!system) {
return;
}
deleteSystems([system]);
outCommand({ type: OutCommand.deleteSystems, data: [system] });
setSystem(undefined);
}, []);

View File

@@ -6,9 +6,6 @@ import { PrimeIcons } from 'primereact/api';
import { ContextMenuSystemProps } from '@/hooks/Mapper/components/contexts';
import { useWaypointMenu } from '@/hooks/Mapper/components/contexts/hooks';
import { FastSystemActions } from '@/hooks/Mapper/components/contexts/components';
import { useMapCheckPermissions } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
export const useContextMenuSystemItems = ({
onDeleteSystem,
@@ -28,7 +25,6 @@ export const useContextMenuSystemItems = ({
const getStatus = useStatusMenu(systems, systemId, onSystemStatus);
const getLabels = useLabelsMenu(systems, systemId, onSystemLabels, onCustomLabelDialog);
const getWaypointMenu = useWaypointMenu(onWaypointSet);
const canLockSystem = useMapCheckPermissions([UserPermission.LOCK_SYSTEM]);
return useMemo(() => {
const system = systemId ? getSystemById(systems, systemId) : undefined;
@@ -45,8 +41,6 @@ export const useContextMenuSystemItems = ({
<FastSystemActions
systemId={systemId}
systemName={system.system_static_info.solar_system_name}
regionName={system.system_static_info.region_name}
isWH={isWormholeSpace(system.system_static_info.system_class)}
showEdit
onOpenSettings={onOpenSettings}
/>
@@ -64,25 +58,19 @@ export const useContextMenuSystemItems = ({
command: onHubToggle,
},
...(system.locked
? canLockSystem
? [
{
label: 'Unlock',
icon: PrimeIcons.LOCK_OPEN,
command: onLockToggle,
},
]
: []
? [
{
label: 'Unlock',
icon: PrimeIcons.LOCK_OPEN,
command: onLockToggle,
},
]
: [
...(canLockSystem
? [
{
label: 'Lock',
icon: PrimeIcons.LOCK,
command: onLockToggle,
},
]
: []),
{
label: 'Lock',
icon: PrimeIcons.LOCK,
command: onLockToggle,
},
{ separator: true },
{
label: 'Delete',
@@ -92,7 +80,6 @@ export const useContextMenuSystemItems = ({
]),
];
}, [
canLockSystem,
systems,
systemId,
getTags,

View File

@@ -8,9 +8,8 @@ import { getSystemById } from '@/hooks/Mapper/helpers';
import { useWaypointMenu } from '@/hooks/Mapper/components/contexts/hooks';
import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/types.ts';
import { FastSystemActions } from '@/hooks/Mapper/components/contexts/components';
import { useJumpPlannerMenu } from '@/hooks/Mapper/components/contexts/hooks';
import { useJumpPlannerMenu } from '@/hooks/Mapper/components/contexts/hooks/useJumpPlannerMenu';
import { Route } from '@/hooks/Mapper/types/routes.ts';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
export interface ContextMenuSystemInfoProps {
systemStatics: Map<number, SolarSystemStaticInfoRaw>;
@@ -49,6 +48,7 @@ export const ContextMenuSystemInfo: React.FC<ContextMenuSystemInfoProps> = ({
if (!systemId || !system) {
return [];
}
return [
{
className: classes.FastActions,
@@ -57,8 +57,6 @@ export const ContextMenuSystemInfo: React.FC<ContextMenuSystemInfoProps> = ({
<FastSystemActions
systemId={systemId}
systemName={system.solar_system_name}
regionName={system.region_name}
isWH={isWormholeSpace(system.system_class)}
onOpenSettings={onOpenSettings}
/>
);

View File

@@ -1,25 +1,25 @@
import * as React from 'react';
import { useCallback, useRef, useState } from 'react';
import { RefObject, useCallback, useRef, useState } from 'react';
import { ContextMenu } from 'primereact/contextmenu';
import { Commands, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
import { Commands, MapHandlers, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/types.ts';
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
import * as React from 'react';
import { SolarSystemStaticInfoRaw } from '@/hooks/Mapper/types';
import { emitMapEvent } from '@/hooks/Mapper/events';
interface UseContextMenuSystemHandlersProps {
hubs: string[];
outCommand: OutCommandHandler;
mapRef: RefObject<MapHandlers>;
}
export const useContextMenuSystemInfoHandlers = ({ hubs, outCommand }: UseContextMenuSystemHandlersProps) => {
export const useContextMenuSystemInfoHandlers = ({ hubs, outCommand, mapRef }: UseContextMenuSystemHandlersProps) => {
const contextMenuRef = useRef<ContextMenu | null>(null);
const [system, setSystem] = useState<string>();
const routeRef = useRef<(SolarSystemStaticInfoRaw | undefined)[]>([]);
const ref = useRef({ hubs, system, outCommand });
ref.current = { hubs, system, outCommand };
const ref = useRef({ hubs, system, outCommand, mapRef });
ref.current = { hubs, system, outCommand, mapRef };
const open = useCallback(
(ev: React.SyntheticEvent, systemId: string, route: (SolarSystemStaticInfoRaw | undefined)[]) => {
@@ -48,7 +48,7 @@ export const useContextMenuSystemInfoHandlers = ({ hubs, outCommand }: UseContex
}, []);
const onAddSystem = useCallback(() => {
const { system: solarSystemId, outCommand } = ref.current;
const { system: solarSystemId, outCommand, mapRef } = ref.current;
if (!solarSystemId) {
return;
}
@@ -60,11 +60,7 @@ export const useContextMenuSystemInfoHandlers = ({ hubs, outCommand }: UseContex
},
});
setTimeout(() => {
emitMapEvent({
name: Commands.centerSystem,
data: solarSystemId,
});
mapRef.current?.command(Commands.centerSystem, solarSystemId);
setSystem(undefined);
}, 200);
}, []);

View File

@@ -1,17 +1,17 @@
import { Node } from 'reactflow';
import { useCallback, useRef, useState } from 'react';
import { useRef, useState } from 'react';
import { ContextMenu } from 'primereact/contextmenu';
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
import { SolarSystemRawType } from '@/hooks/Mapper/types';
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { NodeSelectionMouseHandler } from '@/hooks/Mapper/components/contexts/types.ts';
import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks';
export const useContextMenuSystemMultipleHandlers = () => {
const contextMenuRef = useRef<ContextMenu | null>(null);
const { outCommand } = useMapRootState();
const [systems, setSystems] = useState<Node<SolarSystemRawType>[]>();
const { deleteSystems } = useDeleteSystems();
const handleSystemMultipleContext: NodeSelectionMouseHandler = (ev, systems_) => {
setSystems(systems_);
ev.preventDefault();
@@ -19,7 +19,7 @@ export const useContextMenuSystemMultipleHandlers = () => {
contextMenuRef.current?.show(ev);
};
const onDeleteSystems = useCallback(() => {
const onDeleteSystems = () => {
if (!systems) {
return;
}
@@ -29,11 +29,12 @@ export const useContextMenuSystemMultipleHandlers = () => {
return;
}
deleteSystems(sysToDel);
}, [deleteSystems, systems]);
outCommand({ type: OutCommand.deleteSystems, data: sysToDel });
};
return {
handleSystemMultipleContext,
contextMenuRef,
onDeleteSystems,
};

View File

@@ -9,22 +9,13 @@ import { PrimeIcons } from 'primereact/api';
export interface FastSystemActionsProps {
systemId: string;
systemName: string;
regionName: string;
isWH: boolean;
showEdit?: boolean;
onOpenSettings(): void;
}
export const FastSystemActions = ({
systemId,
systemName,
regionName,
isWH,
onOpenSettings,
showEdit,
}: FastSystemActionsProps) => {
const ref = useRef({ systemId, systemName, regionName, isWH });
ref.current = { systemId, systemName, regionName, isWH };
export const FastSystemActions = ({ systemId, systemName, onOpenSettings, showEdit }: FastSystemActionsProps) => {
const ref = useRef({ systemId, systemName });
ref.current = { systemId, systemName };
const handleOpenZKB = useCallback(
() => window.open(`https://zkillboard.com/system/${ref.current.systemId}`, '_blank'),
@@ -36,17 +27,10 @@ export const FastSystemActions = ({
[],
);
const handleOpenDotlan = useCallback(() => {
if (ref.current.isWH) {
window.open(`https://evemaps.dotlan.net/system/${ref.current.systemName}`, '_blank');
return;
}
return window.open(
`https://evemaps.dotlan.net/map/${ref.current.regionName.replace(/ /gim, '_')}/${ref.current.systemName}#jumps`,
'_blank',
);
}, []);
const handleOpenDotlan = useCallback(
() => window.open(`https://evemaps.dotlan.net/system/${ref.current.systemName}`, '_blank'),
[],
);
const copySystemNameToClipboard = useCallback(async () => {
try {
@@ -59,9 +43,9 @@ export const FastSystemActions = ({
return (
<LayoutEventBlocker className={clsx('flex px-2 gap-2 justify-between items-center h-full')}>
<div className={clsx('flex gap-2 items-center h-full', classes.Links)}>
<WdImgButton tooltip={{ content: 'Open zkillboard' }} source={ZKB_ICON} onClick={handleOpenZKB} />
<WdImgButton tooltip={{ content: 'Open Anoikis' }} source={ANOIK_ICON} onClick={handleOpenAnoikis} />
<WdImgButton tooltip={{ content: 'Open Dotlan' }} source={DOTLAN_ICON} onClick={handleOpenDotlan} />
<WdImgButton source={ZKB_ICON} onClick={handleOpenZKB} />
<WdImgButton source={ANOIK_ICON} onClick={handleOpenAnoikis} />
<WdImgButton source={DOTLAN_ICON} onClick={handleOpenDotlan} />
</div>
<div className="flex gap-2 items-center pl-1">

View File

@@ -1,3 +1 @@
export * from './useWaypointMenu';
export * from './useJumpPlannerMenu';
export * from './useDeleteSystems';

View File

@@ -1,18 +0,0 @@
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
export const useDeleteSystems = () => {
const { outCommand } = useMapRootState();
const deleteSystems = (systemIds: string[]) => {
if (!systemIds || !systemIds.length) {
return;
}
outCommand({ type: OutCommand.deleteSystems, data: systemIds });
};
return {
deleteSystems,
};
};

View File

@@ -1,2 +0,0 @@
export * from './useSystemInfo';
export * from './useGetOwnOnlineCharacters';

View File

@@ -1,36 +0,0 @@
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useMemo } from 'react';
import { getSystemById } from '@/hooks/Mapper/helpers';
import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts';
interface UseSystemInfoProps {
systemId: string;
}
export const useSystemInfo = ({ systemId }: UseSystemInfoProps) => {
const {
data: { systems, connections },
} = useMapRootState();
const { systems: systemStatics } = useLoadSystemStatic({ systems: [systemId] });
// eslint-disable-next-line no-console
console.log('JOipP', `systemStatics`, systemStatics);
return useMemo(() => {
const staticInfo = systemStatics.get(parseInt(systemId));
const dynamicInfo = getSystemById(systems, systemId);
if (!staticInfo || !dynamicInfo) {
throw new Error(`Error on getting system ${systemId}`);
}
const leadsTo = connections
.filter(x => [x.source, x.target].includes(systemId))
.map(x => [x.source, x.target])
.flat()
.filter(x => x !== systemId);
return { dynamicInfo, staticInfo, leadsTo };
}, [systemStatics, systemId, systems, connections]);
};

View File

@@ -2,7 +2,3 @@
width: 100%;
height: 100%;
}
.BackgroundAlternateColor {
}

View File

@@ -5,21 +5,21 @@ import ReactFlow, {
Edge,
MiniMap,
Node,
NodeChange,
NodeDragHandler,
OnConnect,
OnMoveEnd,
OnSelectionChangeFunc,
SelectionDragHandler,
SelectionMode,
useReactFlow,
useEdgesState,
useNodesState,
} from 'reactflow';
import 'reactflow/dist/style.css';
import classes from './Map.module.scss';
import './styles/neon-theme.scss';
import './styles/eve-common.scss';
import { MapProvider, useMapState } from './MapProvider';
import { useNodesState, useEdgesState, useMapHandlers, useUpdateNodes } from './hooks';
import { useMapHandlers, useUpdateNodes } from './hooks';
import { MapHandlers, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
import {
ContextMenuConnection,
@@ -34,7 +34,6 @@ import { SESSION_KEY } from '@/hooks/Mapper/constants.ts';
import { SolarSystemConnection, SolarSystemRawType } from '@/hooks/Mapper/types';
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
import { NodeSelectionMouseHandler } from '@/hooks/Mapper/components/contexts/types.ts';
import clsx from 'clsx';
const DEFAULT_VIEW_PORT = { zoom: 1, x: 0, y: 0 };
@@ -90,16 +89,12 @@ interface MapCompProps {
refn: ForwardedRef<MapHandlers>;
onCommand: OutCommandHandler;
onSelectionChange: OnMapSelectionChange;
onManualDelete(systems: string[]): void;
onConnectionInfoClick?(e: SolarSystemConnection): void;
onSelectionContextMenu?: NodeSelectionMouseHandler;
minimapClasses?: string;
isShowMinimap?: boolean;
onSystemContextMenu: (event: MouseEvent<Element>, systemId: string) => void;
showKSpaceBG?: boolean;
isThickConnections?: boolean;
isShowBackgroundPattern?: boolean;
isSoftBackground?: boolean;
}
const MapComp = ({
@@ -110,21 +105,17 @@ const MapComp = ({
onSystemContextMenu,
onConnectionInfoClick,
onSelectionContextMenu,
onManualDelete,
isShowMinimap,
showKSpaceBG,
isThickConnections,
isShowBackgroundPattern,
isSoftBackground,
}: MapCompProps) => {
const { getNode } = useReactFlow();
const [nodes, , onNodesChange] = useNodesState<Node<SolarSystemRawType>>(initialNodes);
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>>(initialEdges);
const [nodes, , onNodesChange] = useNodesState<SolarSystemRawType>(initialNodes);
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>[]>(initialEdges);
useMapHandlers(refn, onSelectionChange);
useUpdateNodes(nodes);
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers();
const { handleConnectionContext, ...connectionCtxProps } = useContextMenuConnectionHandlers();
const { update } = useMapState();
const onConnect: OnConnect = useCallback(
@@ -180,52 +171,20 @@ const MapComp = ({
localStorage.setItem(SESSION_KEY.viewPort, JSON.stringify(viewport));
};
const handleNodesChange = useCallback(
(changes: NodeChange[]) => {
const systemsIdsToRemove: string[] = [];
const nextChanges = changes.reduce((acc, change) => {
if (change.type !== 'remove') {
return [...acc, change];
}
const node = getNode(change.id);
if (!node) {
return [...acc, change];
}
if (node.data.locked) {
return acc;
}
systemsIdsToRemove.push(node.data.id);
return [...acc, change];
}, [] as NodeChange[]);
if (systemsIdsToRemove.length > 0) {
onManualDelete(systemsIdsToRemove);
}
onNodesChange(nextChanges);
},
[getNode, onManualDelete, onNodesChange],
);
useEffect(() => {
update(x => ({
...x,
showKSpaceBG: showKSpaceBG,
isThickConnections: isThickConnections,
}));
}, [showKSpaceBG, isThickConnections, update]);
}, [showKSpaceBG, update]);
return (
<>
<div className={clsx(classes.MapRoot, { ['bg-neutral-900']: isSoftBackground })}>
<div className={classes.MapRoot}>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={handleNodesChange}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
// TODO we need save into session all of this
@@ -241,7 +200,6 @@ const MapComp = ({
onConnectStart={() => update({ isConnecting: true })}
onConnectEnd={() => update({ isConnecting: false })}
onNodeMouseEnter={(_, node) => update({ hoverNodeId: node.id })}
// onKeyUp=
onNodeMouseLeave={() => update({ hoverNodeId: null })}
onEdgeClick={(_, t) => {
onConnectionInfoClick?.(t.data);
@@ -261,14 +219,14 @@ const MapComp = ({
minZoom={0.2}
maxZoom={1.5}
elevateNodesOnSelect
deleteKeyCode={['Delete']}
// TODO need create clear example with problem with that flag
// if system is not visible edge not drawing (and any render in Custom node is not happening)
// onlyRenderVisibleElements
deleteKeyCode={null}
selectionMode={SelectionMode.Partial}
>
{isShowMinimap && <MiniMap pannable zoomable ariaLabel="Mini map" className={minimapClasses} />}
{isShowBackgroundPattern && <Background />}
<Background />
</ReactFlow>
{/* <button className="z-auto btn btn-primary absolute top-20 right-20" onClick={handleGetPassages}>
Test // DON NOT REMOVE

View File

@@ -8,7 +8,6 @@ export type MapData = MapUnionTypes & {
hoverNodeId: string | null;
visibleNodes: Set<string>;
showKSpaceBG: boolean;
isThickConnections: boolean;
};
interface MapProviderProps {
@@ -18,7 +17,6 @@ interface MapProviderProps {
const INITIAL_DATA: MapData = {
wormholesData: {},
wormholes: [],
effects: {},
characters: [],
userCharacters: [],
@@ -31,8 +29,6 @@ const INITIAL_DATA: MapData = {
hoverNodeId: null,
visibleNodes: new Set(),
showKSpaceBG: false,
isThickConnections: false,
userPermissions: {},
};
export interface MapContextProps {

View File

@@ -3,7 +3,7 @@ import { ContextMenu } from 'primereact/contextmenu';
import { PrimeIcons } from 'primereact/api';
import { MenuItem } from 'primereact/menuitem';
import { Edge } from '@reactflow/core/dist/esm/types/edges';
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
import { MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
import clsx from 'clsx';
import classes from './ContextMenuConnection.module.scss';
import { MASS_STATE_NAMES, MASS_STATE_NAMES_ORDER } from '@/hooks/Mapper/components/map/constants.ts';
@@ -35,49 +35,36 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
}
const isFrigateSize = edge.data?.ship_size_type === ShipSizeStatus.small;
const isWormhole = edge.data?.type !== ConnectionType.gate;
return [
...(isWormhole
? [
{
label: `EOL`,
className: clsx({
[classes.ConnectionTimeEOL]: edge.data?.time_status === TimeStatus.eol,
}),
icon: PrimeIcons.CLOCK,
command: onChangeTimeState,
},
]
: []),
...(isWormhole
? [
{
label: `Frigate`,
className: clsx({
[classes.ConnectionFrigate]: isFrigateSize,
}),
icon: PrimeIcons.CLOUD,
command: () =>
onChangeShipSizeStatus(
edge.data?.ship_size_type === ShipSizeStatus.small ? ShipSizeStatus.normal : ShipSizeStatus.small,
),
},
]
: []),
...(isWormhole
? [
{
label: `Save mass`,
className: clsx({
[classes.ConnectionSave]: edge.data?.locked,
}),
icon: PrimeIcons.LOCK,
command: () => onToggleMassSave(!edge.data?.locked),
},
]
: []),
...(isWormhole && !isFrigateSize
{
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`,

View File

@@ -4,7 +4,7 @@ import { ContextMenu } from 'primereact/contextmenu';
import { useMapState } from '../../MapProvider.tsx';
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
import { Edge } from '@reactflow/core/dist/esm/types/edges';
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
import { MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
export const useContextMenuConnectionHandlers = () => {
@@ -47,23 +47,6 @@ export const useContextMenuConnectionHandlers = () => {
setEdge(undefined);
};
const onChangeType = useCallback((type: ConnectionType) => {
const { edge, outCommand } = ref.current;
if (!edge) {
return;
}
outCommand({
type: OutCommand.updateConnectionType,
data: {
source: edge.source,
target: edge.target,
value: type,
},
});
}, []);
const onChangeMassState = useCallback((status: MassState) => {
const { edge, outCommand } = ref.current;
@@ -135,7 +118,6 @@ export const useContextMenuConnectionHandlers = () => {
contextMenuRef,
onDeleteConnection,
onChangeTimeState,
onChangeType,
onChangeMassState,
onChangeShipSizeStatus,
onToggleMassSave,

View File

@@ -21,11 +21,7 @@
}
&.Frigate {
stroke: #d4f0ff;
}
&.Gate {
stroke: #1c1e15;
stroke: #4e62c9;
}
&.Hovered {
@@ -41,16 +37,9 @@
}
&.Frigate {
stroke: #d4f0ff;
stroke: #41acd7;
}
}
&.Tick {
stroke-width: 3px;
&.Hovered {
stroke-width: 3px;
}
}
}
@@ -72,19 +61,6 @@
stroke: #ef7dce;
}
}
&.Tick {
stroke-width: 5px;
&.TimeCrit {
stroke-width: 6px;
}
}
&.Gate {
stroke: #9aff40;
}
}
.ClickPath {
@@ -117,14 +93,5 @@
width: 5px;
height: 5px;
z-index: 1001;
&.Tick {
width: 7px;
height: 7px;
&.Right {
margin-left: 0px;
}
}
}

View File

@@ -1,64 +1,39 @@
import { useCallback, useMemo, useState } from 'react';
import classes from './SolarSystemEdge.module.scss';
import { EdgeLabelRenderer, EdgeProps, getBezierPath, getSmoothStepPath, Position, useStore } from 'reactflow';
import { EdgeLabelRenderer, EdgeProps, getBezierPath, Position, useStore } from 'reactflow';
import { getEdgeParams } from '@/hooks/Mapper/components/map/utils.ts';
import clsx from 'clsx';
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
import { MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
import { PrimeIcons } from 'primereact/api';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
const MAP_TRANSLATES: Record<string, string> = {
[Position.Top]: 'translate(-48%, 0%)',
[Position.Top]: 'translate(-50%, 0%)',
[Position.Bottom]: 'translate(-50%, -100%)',
[Position.Left]: 'translate(0%, -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 SolarSystemEdge = ({ id, source, target, markerEnd, style, data }: EdgeProps<SolarSystemConnection>) => {
const sourceNode = useStore(useCallback(store => store.nodeInternals.get(source), [source]));
const targetNode = useStore(useCallback(store => store.nodeInternals.get(target), [target]));
const isWormhole = data?.type !== ConnectionType.gate;
const {
data: { isThickConnections },
} = useMapState();
const [hovered, setHovered] = useState(false);
const [path, labelX, labelY, sx, sy, tx, ty, sourcePos, targetPos] = useMemo(() => {
const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(sourceNode, targetNode);
const offset = isThickConnections ? MAP_OFFSETS_TICK[targetPos] : MAP_OFFSETS[targetPos];
const method = isWormhole ? getBezierPath : getSmoothStepPath;
const [edgePath, labelX, labelY] = method({
sourceX: sx - offset.x,
sourceY: sy - offset.y,
const [edgePath, labelX, labelY] = getBezierPath({
sourceX: sx,
sourceY: sy,
sourcePosition: sourcePos,
targetPosition: targetPos,
targetX: tx + offset.x,
targetY: ty + offset.y,
targetX: tx,
targetY: ty,
});
return [edgePath, labelX, labelY, sx, sy, tx, ty, sourcePos, targetPos];
}, [isThickConnections, sourceNode, targetNode, isWormhole]);
}, [sourceNode, targetNode]);
if (!sourceNode || !targetNode || !data) {
return null;
@@ -69,10 +44,8 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
<path
id={`back_${id}`}
className={clsx(classes.EdgePathBack, {
[classes.Tick]: isThickConnections,
[classes.TimeCrit]: isWormhole && data.time_status === TimeStatus.eol,
[classes.TimeCrit]: data.time_status === TimeStatus.eol,
[classes.Hovered]: hovered,
[classes.Gate]: !isWormhole,
})}
d={path}
markerEnd={markerEnd}
@@ -81,12 +54,10 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
<path
id={`front_${id}`}
className={clsx(classes.EdgePathFront, {
[classes.Tick]: isThickConnections,
[classes.Hovered]: hovered,
[classes.MassVerge]: isWormhole && data.mass_status === MassState.verge,
[classes.MassHalf]: isWormhole && data.mass_status === MassState.half,
[classes.Frigate]: isWormhole && data.ship_size_type === ShipSizeStatus.small,
[classes.Gate]: !isWormhole,
[classes.MassVerge]: data.mass_status === MassState.verge,
[classes.MassHalf]: data.mass_status === MassState.half,
[classes.Frigate]: data.ship_size_type === ShipSizeStatus.small,
})}
d={path}
markerEnd={markerEnd}
@@ -104,19 +75,11 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
<EdgeLabelRenderer>
<div
className={clsx(
classes.Handle,
{ [classes.Tick]: isThickConnections, [classes.Right]: Position.Right === sourcePos },
'react-flow__handle absolute nodrag pointer-events-none',
)}
className={clsx(classes.Handle, 'react-flow__handle absolute nodrag pointer-events-none')}
style={{ transform: `${MAP_TRANSLATES[sourcePos]} translate(${sx}px,${sy}px)` }}
/>
<div
className={clsx(
classes.Handle,
{ [classes.Tick]: isThickConnections },
'react-flow__handle absolute nodrag pointer-events-none',
)}
className={clsx(classes.Handle, 'react-flow__handle absolute nodrag pointer-events-none')}
style={{ transform: `${MAP_TRANSLATES[targetPos]} translate(${tx}px,${ty}px)` }}
/>
@@ -126,7 +89,7 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
}}
>
{isWormhole && data.locked && (
{data.locked && (
<WdTooltipWrapper
content="Save mass"
className={clsx(

View File

@@ -1,4 +1,4 @@
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
@import "@/hooks/Mapper/components/map/styles/eve-common-variables";
$pastel-blue: #5a7d9a;
$pastel-pink: #d291bc;
@@ -6,7 +6,7 @@ $pastel-green: #88b04b;
$pastel-yellow: #ffdd59;
$dark-bg: #2d2d2d;
$text-color: #ffffff;
$tooltip-bg: #202020; // Dark background for tooltips
$tooltip-bg: #202020; // Темный фон для подсказок
.RootCustomNode {
display: flex;
@@ -25,11 +25,9 @@ $tooltip-bg: #202020; // Dark background for tooltips
z-index: 1;
overflow: hidden;
&.Mataria,
&.Amarria,
&.Gallente,
&.Caldaria {
&::before {
&.Mataria, &.Amarria, &.Gallente, &.Caldaria {
&::Before {
content: '';
position: absolute;
top: 0;
@@ -46,40 +44,42 @@ $tooltip-bg: #202020; // Dark background for tooltips
&.Mataria {
&::before {
background-image: url('/images/mataria-180.png');
background-image: url("/images/mataria.png");
opacity: 0.6;
background-position-x: 1px;
background-position-y: -14px;
background-position-x: -28px;
background-position-y: -3px;
}
}
&.Caldaria {
&::before {
background-image: url('/images/caldaria-180.png');
background-image: url("/images/caldaria.png");
opacity: 0.6;
background-position-x: 1px;
background-position-y: -10px;
background-position-x: -16px;
background-position-y: -17px;
}
}
&.Amarria {
&::before {
opacity: 0.45;
background-image: url('/images/amarr-180.png');
background-position-x: 0;
background-position-y: -13px;
background-image: url("/images/amarr.png");
background-position-x: 0px;
background-position-y: -1px;
width: calc(100% + 10px)
}
}
&.Gallente {
&::before {
opacity: 0.5;
background-image: url('/images/gallente-180.png');
background-position-x: 1px;
background-position-y: 0;
opacity: 0.6;
background-image: url("/images/gallente.png");
background-position-x: -1px;
background-position-y: -10px;
}
}
&.selected {
border-color: $pastel-pink;
box-shadow: 0 0 10px #9a1af1c2;
@@ -95,7 +95,7 @@ $tooltip-bg: #202020; // Dark background for tooltips
&.eve-system-status-home {
border: 1px solid darken($eve-solar-system-status-color-home, 30%);
background-image: linear-gradient(275deg, $eve-solar-system-status-friendly, transparent);
background-image: linear-gradient(45deg, $eve-solar-system-status-friendly, transparent);
&.selected {
border-color: $eve-solar-system-status-color-home;
@@ -104,7 +104,7 @@ $tooltip-bg: #202020; // Dark background for tooltips
&.eve-system-status-friendly {
border: 1px solid darken($eve-solar-system-status-color-friendly, 20%);
background-image: linear-gradient(275deg, darken($eve-solar-system-status-friendly, 30%), transparent);
background-image: linear-gradient(45deg, darken($eve-solar-system-status-friendly, 30%), transparent);
&.selected {
border-color: darken($eve-solar-system-status-color-friendly, 5%);
@@ -113,7 +113,7 @@ $tooltip-bg: #202020; // Dark background for tooltips
&.eve-system-status-lookingFor {
border: 1px solid darken($eve-solar-system-status-color-lookingFor, 15%);
background-image: linear-gradient(275deg, #45ff8f2f, #457fff2f);
background-image: linear-gradient(45deg, #45ff8f2f, #457fff2f);
&.selected {
border-color: $pastel-pink;
@@ -121,22 +121,23 @@ $tooltip-bg: #202020; // Dark background for tooltips
}
&.eve-system-status-warning {
background-image: linear-gradient(275deg, $eve-solar-system-status-warning, transparent);
background-image: linear-gradient(45deg, $eve-solar-system-status-warning, transparent);
}
&.eve-system-status-dangerous {
background-image: linear-gradient(275deg, $eve-solar-system-status-dangerous, transparent);
background-image: linear-gradient(45deg, $eve-solar-system-status-dangerous, transparent);
}
&.eve-system-status-target {
background-image: linear-gradient(275deg, $eve-solar-system-status-target, transparent);
background-image: linear-gradient(45deg, $eve-solar-system-status-target, transparent);
}
}
.Bookmarks {
position: absolute;
width: 100%;
z-index: 1;
z-index: 0;
display: flex;
left: 4px;
@@ -157,7 +158,7 @@ $tooltip-bg: #202020; // Dark background for tooltips
//background-color: #833ca4;
&:not(:first-child) {
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, .3);
}
}
@@ -180,42 +181,7 @@ $tooltip-bg: #202020; // Dark background for tooltips
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 {
@@ -248,20 +214,14 @@ $tooltip-bg: #202020; // Dark background for tooltips
color: #ffb01d;
}
/* Firefox kostyl */
@-moz-document url-prefix() {
.classSystemName {
font-family: inherit !important;
font-weight: bold;
}
}
.classSystemName {
//font-weight: bold;
}
.solarSystemName {
}
}
.BottomRow {
@@ -306,12 +266,6 @@ $tooltip-bg: #202020; // Dark background for tooltips
& > * {
line-height: 10px;
}
/* Firefox kostyl */
@-moz-document url-prefix() {
position: relative;
top: -1px;
}
}
.Handlers {
@@ -334,40 +288,11 @@ $tooltip-bg: #202020; // Dark background for tooltips
border-color: $pastel-pink;
}
&.HandleTop {
top: -2px;
}
&.HandleTop { top: -2px }
&.HandleRight {
right: -2px;
}
&.HandleRight { right: -2px }
&.HandleBottom {
bottom: -2px;
}
&.HandleBottom { bottom: -2px }
&.HandleLeft {
left: -2px;
}
&.Tick {
width: 7px;
height: 7px;
&.HandleTop {
top: -3px;
}
&.HandleRight {
right: -3px;
}
&.HandleBottom {
bottom: -3px;
}
&.HandleLeft {
left: -3px;
}
}
&.HandleLeft { left: -2px }
}

View File

@@ -3,8 +3,6 @@ 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 {
EFFECT_BACKGROUND_STYLES,
LABELS_INFO,
@@ -14,9 +12,8 @@ import {
} from '@/hooks/Mapper/components/map/constants.ts';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
import { getSystemClassStyles, prepareUnsplashedChunks } from '@/hooks/Mapper/components/map/helpers';
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';
@@ -53,9 +50,6 @@ export const getActivityType = (count: number) => {
// eslint-disable-next-line react/display-name
export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarSystemType>) => {
const { interfaceSettings } = useMapRootState();
const { isShowUnsplashedSignatures } = interfaceSettings;
const {
system_class,
security,
@@ -69,8 +63,6 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
solar_system_name,
} = data.system_static_info;
const signatures = data.system_signatures;
const { locked, name, tag, status, labels, id } = data || {};
const customName = solar_system_name !== name ? name : undefined;
@@ -87,7 +79,6 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
hoverNodeId,
visibleNodes,
showKSpaceBG,
isThickConnections,
},
outCommand,
} = useMapState();
@@ -136,29 +127,13 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
const regionClass = showKSpaceBG ? SpaceToClass[space] : null;
const [unsplashedLeft, unsplashedRight] = useMemo(() => {
if (!isShowUnsplashedSignatures) {
return [[], []];
}
return prepareUnsplashedChunks(
signatures
.filter(s => s.group === 'Wormhole' && !s.linked_system)
.map(s => ({
eve_id: s.eve_id,
type: s.type,
custom_info: s.custom_info,
})),
);
}, [isShowUnsplashedSignatures, signatures]);
return (
<>
{visible && (
<div className={classes.Bookmarks}>
{labelCustom !== '' && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] ">{labelCustom}</span>
<div>{labelCustom}</div>
</div>
)}
@@ -193,16 +168,14 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
{visible && (
<>
<div className={classes.HeadRow}>
<div className={clsx(classes.classTitle, classTitleColor, '[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]')}>
{class_title ?? '-'}
</div>
<div className={clsx(classes.classTitle, classTitleColor)}>{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',
'flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans',
)}
>
{solar_system_name}
@@ -223,16 +196,16 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
<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>
<div className="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',
)}
className={clsx('text-stone-400 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5', {
['text-teal-100 font-bold']: space === Spaces.Caldari,
['text-yellow-100 font-bold']: space === Spaces.Amarr || space === Spaces.Matar,
['text-lime-200/80 font-bold']: space === Spaces.Gallente,
})}
>
{region_name}
</div>
@@ -242,10 +215,10 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
<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>}
{locked && <i className={PrimeIcons.LOCK} style={{ fontSize: '0.45rem' }}></i>}
{hubs.includes(solar_system_id.toString()) && (
<i className={PrimeIcons.MAP_MARKER} style={{ fontSize: '0.45rem', fontWeight: 'bold' }}></i>
<i className={PrimeIcons.MAP_MARKER} style={{ fontSize: '0.45rem' }}></i>
)}
{charactersInSystem.length > 0 && (
@@ -261,59 +234,31 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
)}
</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="source"
className={clsx(classes.Handle, classes.HandleTop, {
[classes.selected]: selected,
[classes.Tick]: isThickConnections,
})}
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,
[classes.Tick]: isThickConnections,
})}
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,
[classes.Tick]: isThickConnections,
})}
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,
[classes.Tick]: isThickConnections,
})}
className={clsx(classes.Handle, classes.HandleLeft, { [classes.selected]: selected })}
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
position={Position.Left}
id="d"

View File

@@ -1,18 +0,0 @@
@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: #ffffff;
font-size: 8px;
text-align: center;
font-weight: bolder;
display: block;
}
}

View File

@@ -1,65 +0,0 @@
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>
);
};

View File

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

View File

@@ -18,9 +18,5 @@ export const WormholeClassComp = ({ id }: WormholeClassComp) => {
}
const colorClass = WORMHOLE_CLASS_STYLES[wormholeDataAdditional.wormholeClassID.toString()];
return (
<div className={clsx(colorClass, '[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]')}>
{wormholeDataAdditional.shortName}
</div>
);
return <div className={clsx(colorClass)}>{wormholeDataAdditional.shortName}</div>;
};

View File

@@ -1,4 +1,4 @@
import { ConnectionType, MassState } from '@/hooks/Mapper/types';
import { MassState } from '@/hooks/Mapper/types';
export enum SOLAR_SYSTEM_CLASS_IDS {
ccp1 = -1,
@@ -30,77 +30,11 @@ export enum SOLAR_SYSTEM_CLASS_IDS {
zarzakh = 10100,
}
export enum SOLAR_SYSTEM_CLASS_GROUPS {
ccp = 'ccp',
c1 = 'c1',
c2 = 'c2',
c3 = 'c3',
c4 = 'c4',
c5 = 'c5',
c6 = 'c6',
hs = 'hs',
ls = 'ls',
ns = 'ns',
thera = 'thera',
c13 = 'c13',
drifter = 'drifter',
unknown = 'unknown',
pochven = 'pochven',
jovian = 'jovian',
}
export const SOLAR_SYSTEM_TO_CLASS_GROUPS_CLASSES = {
c1: ['c1'],
c2: ['c2'],
c3: ['c3'],
c4: ['c4'],
c5: ['c5'],
c6: ['c6'],
hs: ['hs'],
ls: ['ls'],
ns: ['ns'],
thera: ['thera'],
c13: ['c13'],
pochven: ['pochven'],
drifter: ['sentinel', 'barbican', 'vidette', 'conflux', 'redoubt'],
jove: ['jove'],
};
export const SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS = {
ccp1: SOLAR_SYSTEM_CLASS_GROUPS.ccp,
c1: SOLAR_SYSTEM_CLASS_GROUPS.c1,
c2: SOLAR_SYSTEM_CLASS_GROUPS.c2,
c3: SOLAR_SYSTEM_CLASS_GROUPS.c3,
c4: SOLAR_SYSTEM_CLASS_GROUPS.c4,
c5: SOLAR_SYSTEM_CLASS_GROUPS.c5,
c6: SOLAR_SYSTEM_CLASS_GROUPS.c6,
hs: SOLAR_SYSTEM_CLASS_GROUPS.hs,
ls: SOLAR_SYSTEM_CLASS_GROUPS.ls,
ns: SOLAR_SYSTEM_CLASS_GROUPS.ns,
ccp2: SOLAR_SYSTEM_CLASS_GROUPS.ccp,
ccp3: SOLAR_SYSTEM_CLASS_GROUPS.ccp,
thera: SOLAR_SYSTEM_CLASS_GROUPS.thera,
c13: SOLAR_SYSTEM_CLASS_GROUPS.c13,
sentinel: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
baribican: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
vidette: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
conflux: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
redoubt: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
a1: SOLAR_SYSTEM_CLASS_GROUPS.unknown,
a2: SOLAR_SYSTEM_CLASS_GROUPS.unknown,
a3: SOLAR_SYSTEM_CLASS_GROUPS.unknown,
a4: SOLAR_SYSTEM_CLASS_GROUPS.unknown,
a5: SOLAR_SYSTEM_CLASS_GROUPS.unknown,
ccp4: SOLAR_SYSTEM_CLASS_GROUPS.ccp,
pochven: SOLAR_SYSTEM_CLASS_GROUPS.pochven,
};
type WormholesAdditionalInfoType = {
id: string;
shortName: string;
wormholeClassID: number;
title: string;
shortTitle: string;
effectPower?: number;
};
@@ -111,7 +45,6 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
shortName: 'CCP',
wormholeClassID: -1,
title: 'CCP System',
shortTitle: 'CCP',
},
{
id: 'c1',
@@ -119,7 +52,6 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 1,
effectPower: 1,
title: 'Class 1',
shortTitle: 'C1',
},
{
id: 'c2',
@@ -127,7 +59,6 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 2,
effectPower: 2,
title: 'Class 2',
shortTitle: 'C2',
},
{
id: 'c3',
@@ -135,7 +66,6 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 3,
effectPower: 3,
title: 'Class 3',
shortTitle: 'C3',
},
{
id: 'c4',
@@ -143,7 +73,6 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 4,
effectPower: 4,
title: 'Class 4',
shortTitle: 'C4',
},
{
id: 'c5',
@@ -151,7 +80,6 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 5,
effectPower: 5,
title: 'Class 5',
shortTitle: 'C5',
},
{
id: 'c6',
@@ -159,49 +87,42 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 6,
effectPower: 6,
title: 'Class 6',
shortTitle: 'C6',
},
{
id: 'hs',
shortName: 'H',
wormholeClassID: 7,
title: 'High-sec',
shortTitle: 'High-sec',
},
{
id: 'ls',
shortName: 'L',
wormholeClassID: 8,
title: 'Low-sec',
shortTitle: 'Low-sec',
},
{
id: 'ns',
shortName: 'N',
wormholeClassID: 9,
title: 'Null-sec',
shortTitle: 'Null-sec',
},
{
id: 'ccp2',
shortName: 'CCP',
wormholeClassID: 10,
title: 'CCP System',
shortTitle: 'CCP',
},
{
id: 'ccp3',
shortName: 'CCP',
wormholeClassID: 11,
title: 'CCP System',
shortTitle: 'CCP',
},
{
id: 'thera',
shortName: 'T',
wormholeClassID: 12,
title: 'Class 12 (Thera)',
shortTitle: 'Thera',
},
{
id: 'c13',
@@ -209,7 +130,6 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 13,
effectPower: 6,
title: 'Class 13 (Shattered Frigate)',
shortTitle: 'C13',
},
{
id: 'sentinel',
@@ -217,7 +137,6 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 14,
effectPower: 2,
title: 'Class 14 (Sentinel Drifter)',
shortTitle: 'Sentinel',
},
{
id: 'barbican',
@@ -225,7 +144,6 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 15,
effectPower: 2,
title: 'Class 15 (Barbican Drifter)',
shortTitle: 'Barbican',
},
{
id: 'vidette',
@@ -233,7 +151,6 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 16,
effectPower: 2,
title: 'Class 16 (Vidette Drifter)',
shortTitle: 'Vidette',
},
{
id: 'conflux',
@@ -241,7 +158,6 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 17,
effectPower: 2,
title: 'Class 17 (Conflux Drifter)',
shortTitle: 'Conflux',
},
{
id: 'redoubt',
@@ -249,79 +165,59 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 18,
effectPower: 2,
title: 'Class 18 (Redoubt Drifter)',
shortTitle: 'Redoubt',
},
{
id: 'a1',
shortName: 'A1',
wormholeClassID: 19,
title: '(Abyssal class 1)',
shortTitle: 'A1',
},
{
id: 'a2',
shortName: 'A2',
wormholeClassID: 20,
title: '(Abyssal class 2)',
shortTitle: 'A2',
},
{
id: 'a3',
shortName: 'A3',
wormholeClassID: 21,
title: '(Abyssal class 3)',
shortTitle: 'A3',
},
{
id: 'a4',
shortName: 'A4',
wormholeClassID: 22,
title: '(Abyssal class 4)',
shortTitle: 'A4',
},
{
id: 'a5',
shortName: 'A5',
wormholeClassID: 23,
title: '(Abyssal class 5)',
shortTitle: 'A5',
},
{
id: 'ccp4',
shortName: 'CCP',
wormholeClassID: 24,
title: 'CCP System (Penalty)',
shortTitle: 'CCP',
},
{
id: 'pochven',
shortName: 'P',
wormholeClassID: 25,
title: 'Triglavian space (Pochven)',
shortTitle: 'Pochven',
},
{
id: 'zarzakh',
shortName: 'N',
wormholeClassID: 10100,
title: 'Pirate space',
shortTitle: 'Zarzakh',
},
{
id: 'k162',
shortName: 'K162',
wormholeClassID: 10101,
title: 'Reverse',
shortTitle: 'K162',
},
];
export const WORMHOLES_ADDITIONAL_INFO: Record<string, WormholesAdditionalInfoType> =
WORMHOLES_ADDITIONAL_INFO_RAW.reduce((acc, x) => ({ ...acc, [x.id]: x }), {});
export const WORMHOLES_ADDITIONAL_INFO_BY_CLASS_ID: Record<string, WormholesAdditionalInfoType> =
WORMHOLES_ADDITIONAL_INFO_RAW.reduce((acc, x) => ({ ...acc, [x.wormholeClassID]: x }), {});
// export const SOLAR_SYSTEM_CLASS_NAMES = {
// ccp1 = ,
// c1 = ,
@@ -712,13 +608,6 @@ export const STATUS_CLASSES: Record<number, string> = {
[STATUSES.dangerous]: 'eve-system-status-dangerous',
};
export const TYPE_NAMES_ORDER = [ConnectionType.wormhole, ConnectionType.gate];
export const TYPE_NAMES = {
[ConnectionType.wormhole]: 'Wormhole',
[ConnectionType.gate]: 'Gate',
};
export const MASS_STATE_NAMES_ORDER = [MassState.verge, MassState.half, MassState.normal];
export const MASS_STATE_NAMES = {

View File

@@ -3,4 +3,3 @@ export * from './convertSystem2Node';
export * from './getSystemClassStyles';
export * from './getShapeClass';
export * from './getBackgroundClass';
export * from './prepareUnsplashedChunks';

View File

@@ -1,27 +0,0 @@
// 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];
};

View File

@@ -2,18 +2,28 @@ import { Node, useReactFlow } from 'reactflow';
import { useCallback, useRef } from 'react';
import { CommandAddSystems } from '@/hooks/Mapper/types/mapHandlers.ts';
import { convertSystem2Node } from '../../helpers';
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
export const useMapAddSystems = () => {
const rf = useReactFlow();
const {
data: { systems },
update,
} = useMapState();
const ref = useRef({ rf });
ref.current = { rf };
const ref = useRef({ rf, systems, update });
ref.current = { update, systems, rf };
return useCallback((systems: CommandAddSystems) => {
const { rf } = ref.current;
const nodes = rf.getNodes();
return useCallback(
(systems: CommandAddSystems) => {
const nodes = rf.getNodes();
const prepared: Node[] = systems.filter(x => !nodes.some(y => x.id === y.id)).map(convertSystem2Node);
rf.addNodes(prepared);
const prepared: Node[] = systems.filter(x => !nodes.some(y => x.id === y.id)).map(convertSystem2Node);
rf.addNodes(prepared);
}, []);
ref.current.update({
systems: [...ref.current.systems.filter(sys => systems.some(x => sys.id !== x.id)), ...systems],
});
},
[rf],
);
};

View File

@@ -5,21 +5,24 @@ import { OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts
export const useMapRemoveSystems = (onSelectionChange: OnMapSelectionChange) => {
const rf = useReactFlow();
const ref = useRef({ onSelectionChange, rf });
ref.current = { onSelectionChange, rf };
const ref = useRef({ onSelectionChange });
ref.current = { onSelectionChange };
return useCallback((systems: CommandRemoveSystems) => {
ref.current.rf.deleteElements({ nodes: systems.map(x => ({ id: `${x}` })) });
return useCallback(
(systems: CommandRemoveSystems) => {
rf.deleteElements({ nodes: systems.map(x => ({ id: `${x}` })) });
const newSelection = ref.current.rf
.getNodes()
.filter(x => !systems.includes(parseInt(x.id)))
.filter(x => x.selected)
.map(x => x.id);
const newSelection = rf
.getNodes()
.filter(x => !systems.includes(parseInt(x.id)))
.filter(x => x.selected)
.map(x => x.id);
ref.current.onSelectionChange({
systems: newSelection,
connections: [],
});
}, []);
ref.current.onSelectionChange({
systems: newSelection,
connections: [],
});
},
[rf],
);
};

View File

@@ -1,3 +1,2 @@
export * from './useMapHandlers';
export * from './useUpdateNodes';
export * from './useNodesEdgesState';

View File

@@ -18,7 +18,6 @@ import {
CommandUpdateSystems,
MapHandlers,
} from '@/hooks/Mapper/types/mapHandlers.ts';
import {
useCommandsCharacters,
useCommandsConnections,
@@ -58,16 +57,16 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
mapInit(data as CommandInit);
break;
case Commands.addSystems:
setTimeout(() => mapAddSystems(data as CommandAddSystems), 100);
mapAddSystems(data as CommandAddSystems);
break;
case Commands.updateSystems:
mapUpdateSystems(data as CommandUpdateSystems);
break;
case Commands.removeSystems:
setTimeout(() => removeSystems(data as CommandRemoveSystems), 100);
removeSystems(data as CommandRemoveSystems);
break;
case Commands.addConnections:
setTimeout(() => addConnections(data as CommandAddConnections), 100);
addConnections(data as CommandAddConnections);
break;
case Commands.removeConnections:
removeConnections(data as CommandRemoveConnections);
@@ -112,17 +111,13 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
connections: [],
});
selectSystem(systemId as CommandSelectSystem);
}, 500);
}, 100);
break;
case Commands.routes:
// do nothing here
break;
case Commands.signaturesUpdated:
// do nothing here
break;
case Commands.linkSignatureToSystem:
// do nothing here
break;

View File

@@ -1,36 +0,0 @@
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];
}

View File

@@ -1,174 +0,0 @@
/* 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[];
}

View File

@@ -48,7 +48,6 @@ const restoreWindowsFromLS = (): WidgetGridItem[] => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const raw = localStorage.getItem(SESSION_KEY.windows);
if (!raw) {
console.warn('No windows found in local storage!!');
return DEFAULT_WINDOWS;
}
@@ -64,7 +63,7 @@ const restoreWindowsFromLS = (): WidgetGridItem[] => {
};
export const MapInterface = () => {
const [items, setItems] = useState<WidgetGridItem[]>(restoreWindowsFromLS);
const [items, setItems] = useState<WidgetGridItem[]>(restoreWindowsFromLS());
return (
<WidgetsGrid

View File

@@ -79,7 +79,7 @@ export const SystemCustomLabelDialog = ({ systemId, visible, setVisible }: Syste
// @ts-ignore
const handleInput = useCallback(e => {
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9\-[\](){}]/g, '');
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9[\](){}]/g, '');
}, []);
return (

View File

@@ -6,23 +6,17 @@ import { SystemSignature } from '@/hooks/Mapper/types';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { CommandLinkSignatureToSystem } from '@/hooks/Mapper/types';
import { SystemSignaturesContent } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignaturesContent';
import { SHOW_DESCRIPTION_COLUMN_SETTING } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignatures';
import {
Setting,
COSMIC_SIGNATURE,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignatureSettingsDialog';
import { SignatureGroup } from '@/hooks/Mapper/types';
interface SystemLinkSignatureDialogProps {
data: CommandLinkSignatureToSystem;
setVisible: (visible: boolean) => void;
}
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 },
];
const signatureSettings: Setting[] = [{ key: COSMIC_SIGNATURE, name: 'Show Cosmic Signatures', value: true }];
export const SystemLinkSignatureDialog = ({ data, setVisible }: SystemLinkSignatureDialogProps) => {
const { outCommand } = useMapRootState();
@@ -65,7 +59,6 @@ export const SystemLinkSignatureDialog = ({ data, setVisible }: SystemLinkSignat
>
<SystemSignaturesContent
systemId={`${data.solar_system_source}`}
hideLinkedSignatures
settings={signatureSettings}
onSelect={handleSelect}
selectable={true}

View File

@@ -90,7 +90,7 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
}, []);
const handleInput = useCallback((e: any) => {
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9\-[\](){}]/g, '');
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9[\](){}]/g, '');
}, []);
return (

View File

@@ -1,4 +1,4 @@
import { useCallback, useMemo, useRef } from 'react';
import { useCallback, useMemo } from 'react';
import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { VirtualScroller, VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
@@ -8,10 +8,6 @@ import { CharacterTypeRaw, WithIsOwnCharacter } from '@/hooks/Mapper/types';
import { CharacterCard, LayoutEventBlocker, WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
import { sortCharacters } from '@/hooks/Mapper/components/mapInterface/helpers/sortCharacters.ts';
import useLocalStorageState from 'use-local-storage-state';
import { useMapCheckPermissions, useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
type CharItemProps = {
compact: boolean;
@@ -66,14 +62,6 @@ export const LocalCharacters = () => {
const [systemId] = selectedSystems;
const restrictOfflineShowing = useMapGetOption('restrict_offline_showing');
const isAdminOrManager = useMapCheckPermissions([UserPermission.MANAGE_MAP]);
const showOffline = useMemo(
() => !restrictOfflineShowing || isAdminOrManager,
[isAdminOrManager, restrictOfflineShowing],
);
const itemTemplate = useItemTemplate();
const sorted = useMemo(() => {
@@ -82,39 +70,32 @@ export const LocalCharacters = () => {
.map(x => ({ ...x, isOwn: userCharacters.includes(x.eve_id), compact: settings.compact }))
.sort(sortCharacters);
if (!showOffline || !settings.showOffline) {
if (!settings.showOffline) {
return sorted.filter(c => c.online);
}
return sorted;
// eslint-disable-next-line
}, [showOffline, characters, settings.showOffline, settings.compact, systemId, userCharacters, presentCharacters]);
}, [characters, settings.showOffline, settings.compact, systemId, userCharacters, presentCharacters]);
const isNobodyHere = sorted.length === 0;
const isNotSelectedSystem = selectedSystems.length !== 1;
const showList = sorted.length > 0 && selectedSystems.length === 1;
const ref = useRef<HTMLDivElement>(null);
const compact = useMaxWidth(ref, 145);
return (
<Widget
label={
<div className="flex justify-between items-center text-xs w-full" ref={ref}>
<div className="flex justify-between items-center text-xs w-full">
<span className="select-none">Local{showList ? ` [${sorted.length}]` : ''}</span>
<LayoutEventBlocker className="flex items-center gap-2">
{showOffline && (
<WdTooltipWrapper content="Show offline characters in system">
<WdCheckbox
size="xs"
labelSide="left"
label={compact ? '' : 'Show offline'}
value={settings.showOffline}
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
onChange={() => setSettings(() => ({ ...settings, showOffline: !settings.showOffline }))}
/>
</WdTooltipWrapper>
)}
<WdCheckbox
size="xs"
labelSide="left"
label={'Show offline'}
value={settings.showOffline}
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
onChange={() => setSettings(() => ({ ...settings, showOffline: !settings.showOffline }))}
/>
<span
className={clsx('w-4 h-4 cursor-pointer', {
@@ -134,9 +115,7 @@ export const LocalCharacters = () => {
)}
{isNobodyHere && !isNotSelectedSystem && (
<div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm">
Nobody here
</div>
<div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm">Nobody here</div>
)}
{showList && (

View File

@@ -5,7 +5,7 @@ import { SystemViewStandalone, WdTooltip, WdTooltipHandlers } from '@/hooks/Mapp
import { getBackgroundClass, getShapeClass } from '@/hooks/Mapper/components/map/helpers';
import { MouseEvent, useCallback, useRef, useState } from 'react';
import { Commands } from '@/hooks/Mapper/types';
import { emitMapEvent } from '@/hooks/Mapper/events';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
export type RouteSystemProps = {
destination: number;
@@ -88,10 +88,11 @@ export interface RoutesListProps {
export const RoutesList = ({ data, onContextMenu }: RoutesListProps) => {
const [selected, setSelected] = useState<number | null>(null);
const { mapRef } = useMapRootState();
const handleClick = useCallback(
(systemId: number) => emitMapEvent({ name: Commands.centerSystem, data: systemId?.toString() }),
[],
(systemId: number) => mapRef.current?.command(Commands.centerSystem, systemId.toString()),
[mapRef],
);
if (!data.has_connection) {

View File

@@ -1,11 +1,12 @@
import { Dialog } from 'primereact/dialog';
import { useCallback, useEffect, useRef, useState } from 'react';
import { Button } from 'primereact/button';
import { WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
import {
RoutesType,
useRouteProvider,
} from '@/hooks/Mapper/components/mapInterface/widgets/RoutesWidget/RoutesProvider.tsx';
import { PrettySwitchbox } from '@/hooks/Mapper/components/mapRootContent/components/MapSettings/components';
import { CheckboxChangeEvent } from 'primereact/checkbox';
interface RoutesSettingsDialog {
visible: boolean;
@@ -37,8 +38,8 @@ export const RoutesSettingsDialog = ({ visible, setVisible }: RoutesSettingsDial
currentData.current = data;
const handleChangeEvent = useCallback(
(propName: keyof RoutesType) => (event: boolean) => {
optionsRef.current = { ...optionsRef.current, [propName]: event };
(propName: keyof RoutesType) => (event: CheckboxChangeEvent) => {
optionsRef.current = { ...optionsRef.current, [propName]: event.checked };
updateKey(x => x + 1);
},
[],
@@ -70,14 +71,14 @@ export const RoutesSettingsDialog = ({ visible, setVisible }: RoutesSettingsDial
setVisible(false);
}}
>
<div className="flex flex-col gap-3 p-2.5">
<div className="flex flex-col gap-2 mb-2">
<div className="flex flex-col gap-3">
<div className="flex flex-col gap-2">
{checkboxes.map(({ label, propName }) => (
<PrettySwitchbox
<WdCheckbox
key={propName}
label={label}
checked={optionsRef.current[propName]}
setChecked={handleChangeEvent(propName)}
value={optionsRef.current[propName]}
onChange={handleChangeEvent(propName)}
/>
))}
</div>

View File

@@ -19,8 +19,6 @@ import { PrimeIcons } from 'primereact/api';
import { RoutesSettingsDialog } from './RoutesSettingsDialog';
import { RoutesProvider, useRouteProvider } from './RoutesProvider.tsx';
import { ContextMenuSystemInfo, useContextMenuSystemInfoHandlers } from '@/hooks/Mapper/components/contexts';
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
const sortByDist = (a: Route, b: Route) => {
const distA = a.has_connection ? a.systems?.length || 0 : Infinity;
@@ -32,6 +30,7 @@ const sortByDist = (a: Route, b: Route) => {
export const RoutesWidgetContent = () => {
const {
data: { selectedSystems, hubs = [], systems, routes },
mapRef,
outCommand,
} = useMapRootState();
@@ -43,6 +42,7 @@ export const RoutesWidgetContent = () => {
const { open, ...systemCtxProps } = useContextMenuSystemInfoHandlers({
outCommand,
hubs,
mapRef,
});
const preparedHubs = useMemo(() => {
@@ -172,25 +172,20 @@ export const RoutesWidgetComp = () => {
});
}, [data, update]);
const ref = useRef<HTMLDivElement>(null);
const compact = useMaxWidth(ref, 155);
return (
<Widget
label={
<div className="flex justify-between items-center text-xs w-full" ref={ref}>
<div className="flex justify-between items-center text-xs w-full">
<span className="select-none">Routes</span>
<LayoutEventBlocker className="flex items-center gap-2">
<WdTooltipWrapper content="Show shortest route">
<WdCheckbox
size="xs"
labelSide="left"
label={compact ? '' : 'Show shortest'}
value={!isSecure}
onChange={handleSecureChange}
classNameLabel={clsx('text-red-400')}
/>
</WdTooltipWrapper>
<WdCheckbox
size="xs"
labelSide="left"
label={'Show shortest'}
value={!isSecure}
onChange={handleSecureChange}
classNameLabel={clsx('text-red-400')}
/>
<WdImgButton className={PrimeIcons.SLIDERS_H} onClick={() => setRouteSettingsVisible(true)} />
</LayoutEventBlocker>
</div>

View File

@@ -13,10 +13,9 @@ export const SystemInfoContent = ({ systemId }: SystemInfoContentProps) => {
data: { systems, wormholesData },
} = useMapRootState();
const sys = getSystemById(systems, systemId)! || {};
const sys = getSystemById(systems, systemId)!;
const { description } = sys;
const { system_class, region_name, constellation_name, statics, effect_name, effect_power } =
sys.system_static_info || {};
const { system_class, region_name, constellation_name, statics, effect_name, effect_power } = sys.system_static_info;
const isWH = isWormholeSpace(system_class);
const sortedStatics = useMemo(() => sortWHClasses(wormholesData, statics), [wormholesData, statics]);

View File

@@ -1,79 +0,0 @@
.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;
}
}
}

View File

@@ -1,11 +1,9 @@
import { Dialog } from 'primereact/dialog';
import { useCallback, useState } from 'react';
import { Button } from 'primereact/button';
import { TabPanel, TabView } from 'primereact/tabview';
import styles from './SystemSignatureSettingsDialog.module.scss';
import { PrettySwitchbox } from '@/hooks/Mapper/components/mapRootContent/components/MapSettings/components';
import { Checkbox } from 'primereact/checkbox';
export type Setting = { key: string; name: string; value: boolean; isFilter?: boolean };
export type Setting = { key: string; name: string; value: boolean };
export const COSMIC_SIGNATURE = 'Cosmic Signature';
export const COSMIC_ANOMALY = 'Cosmic Anomaly';
@@ -26,12 +24,8 @@ export const SystemSignatureSettingsDialog = ({
onSave,
onCancel,
}: SystemSignatureSettingsDialogProps) => {
const [activeIndex, setActiveIndex] = useState(0);
const [settings, setSettings] = useState<Setting[]>(defaultSettings);
const filterSettings = settings.filter(setting => setting.isFilter);
const userSettings = settings.filter(setting => !setting.isFilter);
const handleSettingsChange = (key: string) => {
setSettings(prevState => prevState.map(item => (item.key === key ? { ...item, value: !item.value } : item)));
};
@@ -41,45 +35,23 @@ export const SystemSignatureSettingsDialog = ({
}, [onSave, settings]);
return (
<Dialog header="System Signatures Settings" visible={true} onHide={onCancel} className="w-full max-w-lg h-[500px]">
<div className="flex flex-col gap-3 justify-between h-full">
<Dialog header="Filter signatures" visible draggable={false} style={{ width: '300px' }} onHide={onCancel}>
<div className="flex flex-col gap-3">
<div className="flex flex-col gap-2">
<div className={styles.verticalTabsContainer}>
<TabView
activeIndex={activeIndex}
onTabChange={e => setActiveIndex(e.index)}
className={styles.verticalTabView}
>
<TabPanel header="Filters" headerClassName={styles.verticalTabHeader}>
<div className="w-full h-full flex flex-col gap-1">
{filterSettings.map(setting => {
return (
<PrettySwitchbox
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>
{settings.map(setting => {
return (
<div key={setting.key} className="flex items-center">
<Checkbox
inputId={setting.key}
checked={setting.value}
onChange={() => handleSettingsChange(setting.key)}
/>
<label htmlFor={setting.key} className="ml-2">
{setting.name}
</label>
</div>
);
})}
</div>
<div className="flex gap-2 justify-end">

View File

@@ -1,11 +1,5 @@
import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
import {
InfoDrawer,
LayoutEventBlocker,
TooltipPosition,
WdImgButton,
WdCheckbox,
} from '@/hooks/Mapper/components/ui-kit';
import { InfoDrawer, LayoutEventBlocker, TooltipPosition, WdImgButton } from '@/hooks/Mapper/components/ui-kit';
import { SystemSignaturesContent } from './SystemSignaturesContent';
import {
Setting,
@@ -18,43 +12,25 @@ import {
SHIP,
DRONE,
} from './SystemSignatureSettingsDialog';
import { SignatureGroup } from '@/hooks/Mapper/types';
import React, { useCallback, useEffect, useState, useMemo, useRef } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { PrimeIcons } from 'primereact/api';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { CheckboxChangeEvent } from 'primereact/checkbox';
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
const SIGNATURE_SETTINGS_KEY = 'wanderer_system_signature_settings_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[] = [
{ key: SHOW_UPDATED_COLUMN_SETTING, name: 'Show Updated Column', value: false, isFilter: false },
{ key: SHOW_DESCRIPTION_COLUMN_SETTING, name: 'Show Description Column', value: false, isFilter: false },
{ key: LAZY_DELETE_SIGNATURES_SETTING, name: 'Lazy Delete Signatures', value: false, isFilter: false },
{ key: KEEP_LAZY_DELETE_SETTING, name: 'Keep "Lazy Delete" Enabled', value: false, isFilter: false },
{ key: COSMIC_ANOMALY, name: 'Show Anomalies', value: true, isFilter: true },
{ key: COSMIC_SIGNATURE, name: 'Show Cosmic Signatures', value: true, isFilter: true },
{ key: DEPLOYABLE, name: 'Show Deployables', value: true, isFilter: true },
{ 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 },
{ key: COSMIC_ANOMALY, name: 'Show Anomalies', value: true },
{ key: COSMIC_SIGNATURE, name: 'Show Cosmic Signatures', value: true },
{ key: DEPLOYABLE, name: 'Show Deployables', value: true },
{ key: STRUCTURE, name: 'Show Structures', value: true },
{ key: STARBASE, name: 'Show Starbase', value: true },
{ key: SHIP, name: 'Show Ships', value: true },
{ key: DRONE, name: 'Show Drones And Charges', value: true },
];
const SIGNATURE_SETTINGS_KEY = 'wanderer_system_signature_settings';
const defaultSettings = () => {
return [...settings];
};
@@ -71,25 +47,12 @@ export const SystemSignatures = () => {
const isNotSelectedSystem = selectedSystems.length !== 1;
const lazyDeleteValue = useMemo(() => {
return settings.find(setting => setting.key === LAZY_DELETE_SIGNATURES_SETTING)!.value;
}, [settings]);
const handleSettingsChange = useCallback((settings: Setting[]) => {
setSettings(settings);
localStorage.setItem(SIGNATURE_SETTINGS_KEY, JSON.stringify(settings));
setVisible(false);
}, []);
const handleLazyDeleteChange = useCallback((value: boolean) => {
setSettings(settings => {
const lazyDelete = settings.find(setting => setting.key === LAZY_DELETE_SIGNATURES_SETTING)!;
lazyDelete.value = value;
localStorage.setItem(SIGNATURE_SETTINGS_KEY, JSON.stringify(settings));
return [...settings];
});
}, []);
useEffect(() => {
const restoredSettings = localStorage.getItem(SIGNATURE_SETTINGS_KEY);
@@ -98,27 +61,13 @@ export const SystemSignatures = () => {
}
}, []);
const ref = useRef<HTMLDivElement>(null);
const compact = useMaxWidth(ref, 260);
return (
<Widget
label={
<div className="flex justify-between items-center text-xs w-full h-full" ref={ref}>
<div className="flex gap-1 whitespace-nowrap text-ellipsis overflow-hidden">System Signatures</div>
<div className="flex justify-between items-center text-xs w-full h-full">
<div className="flex gap-1">System Signatures</div>
<LayoutEventBlocker className="flex gap-2.5">
<WdTooltipWrapper content="Enable Lazy delete">
<WdCheckbox
size="xs"
labelSide="left"
label={compact ? '' : 'Lazy delete'}
value={lazyDeleteValue}
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300 whitespace-nowrap text-ellipsis overflow-hidden"
onChange={(event: CheckboxChangeEvent) => handleLazyDeleteChange(!!event.checked)}
/>
</WdTooltipWrapper>
<WdImgButton
className={PrimeIcons.QUESTION_CIRCLE}
tooltip={{
@@ -142,7 +91,8 @@ export const SystemSignatures = () => {
</InfoDrawer>
<InfoDrawer title={<b className="text-slate-50">How to delete?</b>}>
For delete any signature first of all you need select before
<br /> and then use <b className="text-sky-500">Del</b>
<br /> and then use <b className="text-sky-500">Del</b> or{' '}
<b className="text-sky-500">Backspace</b>
</InfoDrawer>
</div>
) as React.ReactNode,
@@ -158,7 +108,7 @@ export const SystemSignatures = () => {
System is not selected
</div>
) : (
<SystemSignaturesContent systemId={systemId} settings={settings} onLazyDeleteChange={handleLazyDeleteChange} />
<SystemSignaturesContent systemId={systemId} settings={settings} />
)}
{visible && (
<SystemSignatureSettingsDialog

View File

@@ -1,17 +1,16 @@
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useClipboard } from '@/hooks/Mapper/hooks/useClipboard';
import { parseSignatures } from '@/hooks/Mapper/helpers';
import { Commands, OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
import { WdTooltip, WdTooltipHandlers } from '@/hooks/Mapper/components/ui-kit';
import { GROUPS_LIST } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
import { DataTable, DataTableRowClickEvent, DataTableRowMouseEvent, SortOrder } from 'primereact/datatable';
import { DataTable, DataTableRowMouseEvent, SortOrder } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import useRefState from 'react-usestateref';
import { Setting } from '../SystemSignatureSettingsDialog';
import { useHotkey } from '@/hooks/Mapper/hooks';
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
import { useClipboard } from '@/hooks/Mapper/hooks/useClipboard';
import classes from './SystemSignaturesContent.module.scss';
import clsx from 'clsx';
@@ -22,56 +21,38 @@ import {
getRowColorByTimeLeft,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/helpers';
import {
renderAddedTimeLeft,
renderDescription,
renderIcon,
renderInfoColumn,
renderUpdatedTimeLeft,
renderName,
renderTimeLeft,
renderLinkedSystem,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders';
// import { PrimeIcons } from 'primereact/api';
import useLocalStorageState from 'use-local-storage-state';
import { PrimeIcons } from 'primereact/api';
import { SignatureSettings } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings';
import { useMapEventListener } from '@/hooks/Mapper/events';
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 = {
sortField: string;
sortOrder: SortOrder;
};
const SORT_DEFAULT_VALUES: SystemSignaturesSortSettings = {
sortField: 'inserted_at',
sortField: 'updated_at',
sortOrder: -1,
};
interface SystemSignaturesContentProps {
systemId: string;
settings: Setting[];
hideLinkedSignatures?: boolean;
selectable?: boolean;
onSelect?: (signature: SystemSignature) => void;
onLazyDeleteChange?: (value: boolean) => void;
}
export const SystemSignaturesContent = ({
systemId,
settings,
hideLinkedSignatures,
selectable,
onSelect,
onLazyDeleteChange,
}: SystemSignaturesContentProps) => {
export const SystemSignaturesContent = ({ systemId, settings, selectable, onSelect }: SystemSignaturesContentProps) => {
const { outCommand } = useMapRootState();
const [signatures, setSignatures, signaturesRef] = useRefState<SystemSignature[]>([]);
const [selectedSignatures, setSelectedSignatures] = useState<SystemSignature[]>([]);
const [nameColumnWidth, setNameColumnWidth] = useState('auto');
const [selectedSignature, setSelectedSignature] = useState<SystemSignature | null>(null);
const [parsedSignatures, setParsedSignatures] = useState<SystemSignature[]>([]);
const [askUser, setAskUser] = useState(false);
const [hoveredSig, setHoveredSig] = useState<SystemSignature | null>(null);
@@ -82,20 +63,10 @@ export const SystemSignaturesContent = ({
const tableRef = useRef<HTMLDivElement>(null);
const compact = useMaxWidth(tableRef, 260);
const medium = useMaxWidth(tableRef, 380);
const refData = useRef({ selectable });
refData.current = { selectable };
const tooltipRef = useRef<WdTooltipHandlers>(null);
const { clipboardContent, 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 { clipboardContent } = useClipboard();
const handleResize = useCallback(() => {
if (tableRef.current) {
@@ -106,38 +77,13 @@ export const SystemSignaturesContent = ({
}
}, []);
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(() => {
return signatures
.filter(x => {
if (hideLinkedSignatures && !!x.linked_system) {
return false;
}
const isCosmicSignature = x.kind === COSMIC_SIGNATURE;
if (isCosmicSignature) {
const showCosmicSignatures = settings.find(y => y.key === COSMIC_SIGNATURE)?.value;
if (showCosmicSignatures) {
return !x.group || groupSettings.find(y => y.key === x.group)?.value;
} else {
return !!x.group && groupSettings.find(y => y.key === x.group)?.value;
}
}
return settings.find(y => y.key === x.kind)?.value;
})
.filter(x => settings.find(y => y.key === x.kind)?.value)
.sort((a, b) => {
return new Date(b.updated_at || 0).getTime() - new Date(a.updated_at || 0).getTime();
});
}, [signatures, settings, groupSettings, hideLinkedSignatures]);
}, [signatures, settings]);
const handleGetSignatures = useCallback(async () => {
const { signatures } = await outCommand({
@@ -145,17 +91,33 @@ export const SystemSignaturesContent = ({
data: { system_id: systemId },
});
setAskUser(false);
setSignatures(signatures);
}, [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(
async (newSignatures: SystemSignature[], updateOnly: boolean, skipUpdateUntouched?: boolean) => {
const { added, updated, removed } = getActualSigs(
signaturesRef.current,
newSignatures,
updateOnly,
skipUpdateUntouched,
);
async (newSignatures: SystemSignature[], updateOnly: boolean) => {
const { added, updated, removed } = getActualSigs(signaturesRef.current, newSignatures, updateOnly);
const { signatures: updatedSignatures } = await outCommand({
type: OutCommand.updateSignatures,
@@ -173,35 +135,35 @@ export const SystemSignaturesContent = ({
[outCommand, systemId],
);
const handleDeleteSelected = useCallback(
async (e: KeyboardEvent) => {
if (selectable) {
return;
}
if (selectedSignatures.length === 0) {
return;
}
e.preventDefault();
e.stopPropagation();
const selectedSignaturesEveIds = selectedSignatures.map(x => x.eve_id);
await handleUpdateSignatures(
signatures.filter(x => !selectedSignaturesEveIds.includes(x.eve_id)),
false,
true,
);
},
[handleUpdateSignatures, selectable, signatures, selectedSignatures],
);
const handleDeleteSelected = useCallback(async () => {
if (selectable) {
return;
}
if (selectedSignatures.length === 0) {
return;
}
const selectedSignaturesEveIds = selectedSignatures.map(x => x.eve_id);
await handleUpdateSignatures(
signatures.filter(x => !selectedSignaturesEveIds.includes(x.eve_id)),
false,
);
}, [handleUpdateSignatures, selectable, signatures, selectedSignatures]);
const handleSelectAll = useCallback(() => {
setSelectedSignatures(signatures);
}, [signatures]);
const handleReplaceAll = useCallback(() => {
handleUpdateSignatures(parsedSignatures, false);
setAskUser(false);
}, [parsedSignatures, handleUpdateSignatures]);
const handleUpdateOnly = useCallback(() => {
handleUpdateSignatures(parsedSignatures, true);
setAskUser(false);
}, [parsedSignatures, handleUpdateSignatures]);
const handleSelectSignatures = useCallback(
// TODO still will be good to define types if we use typescript
// @ts-ignore
e => {
if (selectable) {
onSelect?.(e.value);
@@ -212,69 +174,44 @@ export const SystemSignaturesContent = ({
[onSelect, selectable],
);
const handlePaste = async (clipboardContent: string) => {
useHotkey(true, ['a'], handleSelectAll);
useHotkey(false, ['Backspace', 'Delete'], handleDeleteSelected);
useEffect(() => {
if (selectable) {
return;
}
if (!clipboardContent) {
return;
}
const newSignatures = parseSignatures(
clipboardContent,
settings.map(x => x.key),
);
handleUpdateSignatures(newSignatures, !lazyDeleteValue);
const { removed } = getActualSigs(signaturesRef.current, newSignatures, false);
if (lazyDeleteValue && !keepLazyDeleteValue) {
onLazyDeleteChange?.(false);
if (!signaturesRef.current || !signaturesRef.current.length || !removed.length) {
handleUpdateSignatures(newSignatures, false);
} else {
setParsedSignatures(newSignatures);
setAskUser(true);
}
};
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);
}, [clipboardContent, selectable]);
useEffect(() => {
if (!systemId) {
setSignatures([]);
setAskUser(false);
return;
}
handleGetSignatures();
}, [systemId]);
useMapEventListener(event => {
switch (event.name) {
case Commands.signaturesUpdated:
if (event.data?.toString() !== systemId.toString()) {
return;
}
handleGetSignatures();
return true;
}
});
useEffect(() => {
const observer = new ResizeObserver(handleResize);
if (tableRef.current) {
@@ -290,22 +227,26 @@ export const SystemSignaturesContent = ({
};
}, []);
const renderToolbar = (/*row: SystemSignature*/) => {
return (
<div className="flex justify-end items-center gap-2 mr-[4px]">
<WdTooltipWrapper content="To Edit Signature do double click">
<span className={clsx(PrimeIcons.PENCIL, 'text-[10px]')}></span>
</WdTooltipWrapper>
</div>
);
};
const handleEnterRow = useCallback(
(e: DataTableRowMouseEvent) => {
setHoveredSig(filteredSignatures[e.index]);
tooltipRef.current?.show(e.originalEvent);
},
[filteredSignatures],
);
const [showSignatureSettings, setShowSignatureSettings] = useState(false);
const handleLeaveRow = useCallback((e: DataTableRowMouseEvent) => {
tooltipRef.current?.hide(e.originalEvent);
setHoveredSig(null);
}, []);
const handleRowClick = (e: DataTableRowClickEvent) => {
setSelectedSignature(e.data as SystemSignature);
setShowSignatureSettings(true);
};
// const renderToolbar = (/*row: SystemSignature*/) => {
// return (
// <div className="flex justify-end items-center gap-2">
// <span className={clsx(PrimeIcons.PENCIL, 'text-[10px]')}></span>
// </div>
// );
// };
return (
<>
@@ -316,7 +257,6 @@ export const SystemSignaturesContent = ({
</div>
) : (
<>
{/* @ts-ignore */}
<DataTable
className={classes.Table}
value={filteredSignatures}
@@ -328,7 +268,6 @@ export const SystemSignaturesContent = ({
dataKey="eve_id"
tableClassName="w-full select-none"
resizableColumns={false}
onRowDoubleClick={handleRowClick}
rowHover
selectAll
sortField={sortSettings.sortField}
@@ -341,7 +280,7 @@ export const SystemSignaturesContent = ({
return clsx(classes.TableRowCompact, 'bg-amber-500/50 hover:bg-amber-500/70 transition duration-200');
}
const dateClass = getRowColorByTimeLeft(row.inserted_at ? new Date(row.inserted_at) : undefined);
const dateClass = getRowColorByTimeLeft(row.updated_at ? new Date(row.updated_at) : undefined);
if (!dateClass) {
return clsx(classes.TableRowCompact, 'hover:bg-purple-400/20 transition duration-200');
}
@@ -352,7 +291,7 @@ export const SystemSignaturesContent = ({
<Column
bodyClassName="p-0 px-1"
field="group"
body={x => renderIcon(x)}
body={renderIcon}
style={{ maxWidth: 26, minWidth: 26, width: 26, height: 25 }}
></Column>
@@ -368,56 +307,44 @@ export const SystemSignaturesContent = ({
header="Group"
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
hidden={compact}
style={{ maxWidth: 110, minWidth: 110, width: 110 }}
sortable
></Column>
<Column
field="info"
field="name"
header="Name"
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
body={renderInfoColumn}
body={renderName}
style={{ maxWidth: nameColumnWidth }}
hidden={compact || medium}
sortable
></Column>
{showDescriptionColumn && (
<Column
field="description"
header="Description"
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
body={renderDescription}
hidden={compact}
sortable
></Column>
)}
<Column
field="inserted_at"
header="Added"
dataType="date"
bodyClassName="w-[70px] text-ellipsis overflow-hidden whitespace-nowrap"
body={renderAddedTimeLeft}
field="linked_system"
header="Leads To"
headerClassName="whitespace-nowrap"
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
body={renderLinkedSystem}
style={{ maxWidth: nameColumnWidth }}
hidden={compact}
sortable
></Column>
{showUpdatedColumn && (
<Column
field="updated_at"
header="Updated"
dataType="date"
bodyClassName="w-[70px] text-ellipsis overflow-hidden whitespace-nowrap"
body={renderUpdatedTimeLeft}
sortable
></Column>
)}
<Column
field="updated_at"
header="Updated"
dataType="date"
bodyClassName="w-[80px] text-ellipsis overflow-hidden whitespace-nowrap"
body={renderTimeLeft}
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>
)}
{/*<Column*/}
{/* bodyClassName="p-0 pl-1 pr-2"*/}
{/* field="group"*/}
{/* body={renderToolbar}*/}
{/* headerClassName={headerClasses}*/}
{/* style={{ maxWidth: 26, minWidth: 26, width: 26 }}*/}
{/*></Column>*/}
</DataTable>
</>
)}
@@ -426,14 +353,25 @@ export const SystemSignaturesContent = ({
ref={tooltipRef}
content={hoveredSig ? <SignatureView {...hoveredSig} /> : null}
/>
{showSignatureSettings && (
<SignatureSettings
systemId={systemId}
show
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>
</>

View File

@@ -6,7 +6,6 @@ export const getActualSigs = (
oldSignatures: SystemSignature[],
newSignatures: SystemSignature[],
updateOnly: boolean,
skipUpdateUntouched?: boolean,
): { added: SystemSignature[]; updated: SystemSignature[]; removed: SystemSignature[] } => {
const updated: SystemSignature[] = [];
const removed: SystemSignature[] = [];
@@ -20,8 +19,6 @@ export const getActualSigs = (
const isNeedUpgrade = getState(GROUPS_LIST, newSig) > getState(GROUPS_LIST, oldSig);
if (isNeedUpgrade) {
updated.push({ ...oldSig, group: newSig.group, name: newSig.name });
} else if (!skipUpdateUntouched) {
updated.push({ ...oldSig });
}
} else {
if (!updateOnly) {

View File

@@ -1,7 +1,4 @@
export * from './renderIcon';
export * from './renderDescription';
export * from './renderName';
export * from './renderAddedTimeLeft';
export * from './renderUpdatedTimeLeft';
export * from './renderTimeLeft';
export * from './renderLinkedSystem';
export * from './renderInfoColumn';

View File

@@ -1,10 +0,0 @@
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>
);
};

View File

@@ -1,5 +0,0 @@
import { SystemSignature } from '@/hooks/Mapper/types';
export const renderDescription = (row: SystemSignature) => {
return <span title={row?.description}>{row?.description}</span>;
};

View File

@@ -1,7 +1,7 @@
import { GroupType, SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
import { GROUPS } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
export const renderIcon = (row: SystemSignature, customSize?: Omit<GroupType, 'icon' | 'id'>) => {
export const renderIcon = (row: SystemSignature) => {
if (row.group == null) {
return null;
}
@@ -13,7 +13,7 @@ export const renderIcon = (row: SystemSignature, customSize?: Omit<GroupType, 'i
return (
<div className="flex justify-center items-center">
<img src={group.icon} style={{ width: customSize?.w ?? group.w, height: customSize?.h ?? group.h }} />
<img src={group.icon} style={{ width: group.w, height: group.h }} />
</div>
);
};

View File

@@ -1,69 +0,0 @@
import { PrimeIcons } from 'primereact/api';
import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
import { SystemViewStandalone, WHClassView } from '@/hooks/Mapper/components/ui-kit';
import {
k162Types,
renderK162Type,
} from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureK162TypeSelect';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import clsx from 'clsx';
import { renderName } from './renderName.tsx';
export const renderInfoColumn = (row: SystemSignature) => {
if (!row.group || row.group === SignatureGroup.Wormhole) {
let k162TypeOption = null;
if (row.custom_info) {
const customInfo = JSON.parse(row.custom_info);
if (customInfo.k162Type) {
k162TypeOption = k162Types.find(x => x.value === customInfo.k162Type);
}
}
return (
<div className="flex justify-start items-center gap-[4px]">
{row.type && (
<WHClassView
className="text-[11px]"
classNameWh="!text-[11px] !font-bold"
hideWhClass={!!row.linked_system}
whClassName={row.type}
noOffset
useShortTitle
/>
)}
{!row.linked_system && row.type === 'K162' && !!k162TypeOption && <>{renderK162Type(k162TypeOption)}</>}
{row.linked_system && (
<>
<span title={row.linked_system?.solar_system_name}>
<SystemViewStandalone
className={clsx('select-none text-center cursor-context-menu')}
hideRegion
{...row.linked_system}
/>
</span>
</>
)}
{row.description && (
<WdTooltipWrapper content={row.description}>
<span className={clsx(PrimeIcons.EXCLAMATION_CIRCLE, 'text-[12px]')}></span>
</WdTooltipWrapper>
)}
</div>
);
}
return (
<div className="flex gap-1 items-center">
{renderName(row)}{' '}
{row.description && (
<WdTooltipWrapper content={row.description}>
<span className={clsx(PrimeIcons.EXCLAMATION_CIRCLE, 'text-[12px]')}></span>
</WdTooltipWrapper>
)}
</div>
);
};

View File

@@ -1,7 +1,7 @@
import { SystemSignature } from '@/hooks/Mapper/types';
import { TimeLeft } from '@/hooks/Mapper/components/ui-kit';
export const renderUpdatedTimeLeft = (row: SystemSignature) => {
export const renderTimeLeft = (row: SystemSignature) => {
return (
<div className="flex w-full items-center">
<TimeLeft cDate={row.updated_at ? new Date(row.updated_at) : undefined} />

View File

@@ -7,13 +7,13 @@ import { useCallback, useState } from 'react';
import { OnTheMap, RightBar } from '@/hooks/Mapper/components/mapRootContent/components';
import { MapContextMenu } from '@/hooks/Mapper/components/mapRootContent/components/MapContextMenu/MapContextMenu.tsx';
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 {}
// eslint-disable-next-line no-empty-pattern
export const MapRootContent = ({}: MapRootContentProps) => {
const { interfaceSettings } = useMapRootState();
const { mapRef, interfaceSettings } = useMapRootState();
const { isShowMenu } = interfaceSettings;
const [showOnTheMap, setShowOnTheMap] = useState(false);
@@ -26,7 +26,7 @@ export const MapRootContent = ({}: MapRootContentProps) => {
useSkipContextMenu();
return (
<Layout map={<MapWrapper />}>
<Layout map={<MapWrapper refn={mapRef} />}>
{!isShowMenu ? (
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
<div className="absolute top-0 left-0 w-[calc(100%-3.5rem)] h-full pointer-events-none">

View File

@@ -1,22 +1,13 @@
import classes from './Connections.module.scss';
import { Sidebar } from 'primereact/sidebar';
import { useEffect, useMemo, useState, useCallback } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { VirtualScroller, VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
import clsx from 'clsx';
import {
ConnectionType,
ConnectionOutput,
ConnectionInfoOutput,
OutCommand,
Passage,
SolarSystemConnection,
} from '@/hooks/Mapper/types';
import { ConnectionOutput, OutCommand, Passage, SolarSystemConnection } from '@/hooks/Mapper/types';
import { PassageCard } from './PassageCard';
import { InfoDrawer, SystemView } from '@/hooks/Mapper/components/ui-kit';
import { kgToTons } from '@/hooks/Mapper/utils/kgToTons.ts';
import { TimeAgo } from '@/hooks/Mapper/components/ui-kit';
const sortByDate = (a: string, b: string) => new Date(a).getTime() - new Date(b).getTime();
@@ -77,49 +68,26 @@ export const Connections = ({ selectedConnection, onHide }: OnTheMapProps) => {
return connections.find(x => x.source === selectedConnection.source && x.target === selectedConnection.target);
}, [connections, selectedConnection]);
const isWormhole = useMemo(() => {
return cnInfo?.type !== ConnectionType.gate;
}, [cnInfo]);
const [passages, setPassages] = useState<Passage[]>([]);
const [info, setInfo] = useState<ConnectionInfoOutput | null>(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(() => {
if (!selectedConnection) {
return;
}
loadInfo(selectedConnection);
loadPassages(selectedConnection);
const loadInfo = async () => {
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]);
const approximateMass = useMemo(() => {
@@ -141,7 +109,7 @@ export const Connections = ({ selectedConnection, onHide }: OnTheMapProps) => {
>
<div className={clsx(classes.SidebarContent, '')}>
{/* Connection Info */}
<div className="px-2 flex flex-col gap-2">
<div className="px-2 pb-3 flex flex-col gap-2">
{/* Connection Info Row */}
<InfoDrawer title="Connection" rightSide>
<div className="flex justify-end gap-2 items-center">
@@ -159,25 +127,10 @@ export const Connections = ({ selectedConnection, onHide }: OnTheMapProps) => {
</div>
</InfoDrawer>
<div className="flex justify-between gap-2">
{/*Left column*/}
<div>
{isWormhole && info?.marl_eol_time && (
<InfoDrawer title="Mark EOL Time">
<TimeAgo timestamp={info.marl_eol_time} />
</InfoDrawer>
)}
</div>
{/*Right column*/}
<div>
{isWormhole && (
<InfoDrawer title="Approximate mass of passages" rightSide>
{kgToTons(approximateMass)}
</InfoDrawer>
)}
</div>
</div>
{/* Connection Info Row */}
<InfoDrawer title="Approximate mass of passages" rightSide>
{kgToTons(approximateMass)}
</InfoDrawer>
<div className="flex gap-2"></div>
</div>

View File

@@ -5,8 +5,6 @@ import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrap
import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
import { OutCommand } from '@/hooks/Mapper/types';
import { MenuItem } from 'primereact/menuitem';
import { useMapCheckPermissions } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
export interface MapContextMenuProps {
onShowOnTheMap?: () => void;
@@ -16,8 +14,6 @@ export interface MapContextMenuProps {
export const MapContextMenu = ({ onShowOnTheMap, onShowMapSettings }: MapContextMenuProps) => {
const { outCommand, setInterfaceSettings } = useMapRootState();
const canTrackCharacters = useMapCheckPermissions([UserPermission.TRACK_CHARACTER]);
const menuRight = useRef<Menu>(null);
const handleAddCharacter = useCallback(() => {
@@ -28,40 +24,34 @@ export const MapContextMenu = ({ onShowOnTheMap, onShowMapSettings }: MapContext
}, [outCommand]);
const items = useMemo(() => {
return (
[
{
label: 'Tracking',
icon: 'pi pi-user-plus',
command: handleAddCharacter,
visible: true,
},
{
label: 'On the map',
icon: 'pi pi-hashtag',
command: onShowOnTheMap,
visible: canTrackCharacters,
},
{ separator: true, visible: true },
{
label: 'Settings',
icon: `pi pi-cog`,
command: onShowMapSettings,
visible: true,
},
{
label: 'Dock menu',
icon: 'pi pi-window-maximize',
command: () =>
setInterfaceSettings(x => ({
...x,
isShowMenu: !x.isShowMenu,
})),
visible: true,
},
] as MenuItem[]
).filter(item => item.visible);
}, [canTrackCharacters, handleAddCharacter, onShowMapSettings, onShowOnTheMap, setInterfaceSettings]);
return [
{
label: 'Tracking',
icon: 'pi pi-user-plus',
command: handleAddCharacter,
},
{
label: 'On the map',
icon: 'pi pi-hashtag',
command: onShowOnTheMap,
},
{ separator: true },
{
label: 'Settings',
icon: `pi pi-cog`,
command: onShowMapSettings,
},
{
label: 'Dock menu',
icon: 'pi pi-window-maximize',
command: () =>
setInterfaceSettings(x => ({
...x,
isShowMenu: !x.isShowMenu,
})),
},
] as MenuItem[];
}, [handleAddCharacter, onShowMapSettings, onShowOnTheMap, setInterfaceSettings]);
return (
<div className="ml-1">

View File

@@ -53,7 +53,6 @@ const SYSTEMS_CHECKBOXES_PROPS: CheckboxesList = [
const SIGNATURES_CHECKBOXES_PROPS: CheckboxesList = [
{ prop: UserSettingsRemoteProps.link_signature_on_splash, label: 'Link signature on splash' },
{ prop: InterfaceStoredSettingsProps.isShowUnsplashedSignatures, label: 'Show unsplashed signatures' },
];
const CONNECTIONS_CHECKBOXES_PROPS: CheckboxesList = [
@@ -62,9 +61,6 @@ const CONNECTIONS_CHECKBOXES_PROPS: CheckboxesList = [
const UI_CHECKBOXES_PROPS: CheckboxesList = [
{ prop: InterfaceStoredSettingsProps.isShowMenu, label: 'Enable compact map menu bar' },
{ prop: InterfaceStoredSettingsProps.isThickConnections, label: 'Thicker connections' },
{ prop: InterfaceStoredSettingsProps.isShowBackgroundPattern, label: 'Show background pattern' },
{ prop: InterfaceStoredSettingsProps.isSoftBackground, label: 'Enable soft background' },
];
export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
@@ -131,7 +127,7 @@ export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
return (
<Dialog
header="Map user settings"
header="Map settings"
visible={show}
draggable={false}
style={{ width: '550px' }}

View File

@@ -8,8 +8,6 @@ import clsx from 'clsx';
import { CharacterTypeRaw, WithIsOwnCharacter } from '@/hooks/Mapper/types';
import { CharacterCard, WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
import useLocalStorageState from 'use-local-storage-state';
import { useMapCheckPermissions, useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
type WindowLocalSettingsType = {
compact: boolean;
@@ -52,22 +50,14 @@ export const OnTheMap = ({ show, onHide }: OnTheMapProps) => {
defaultValue: STORED_DEFAULT_VALUES,
});
const restrictOfflineShowing = useMapGetOption('restrict_offline_showing');
const isAdminOrManager = useMapCheckPermissions([UserPermission.MANAGE_MAP]);
const showOffline = useMemo(
() => !restrictOfflineShowing || isAdminOrManager,
[isAdminOrManager, restrictOfflineShowing],
);
const sorted = useMemo(() => {
const out = characters.map(x => ({ ...x, isOwn: userCharacters.includes(x.eve_id) })).sort(sortCharacters);
if (showOffline && !settings.hideOffline) {
if (!settings.hideOffline) {
return out;
}
return out.filter(x => x.online);
}, [showOffline, characters, settings.hideOffline, userCharacters]);
}, [characters, settings.hideOffline, userCharacters]);
return (
<Sidebar
@@ -80,16 +70,14 @@ export const OnTheMap = ({ show, onHide }: OnTheMapProps) => {
>
<div className={clsx(classes.SidebarContent, '')}>
<div className={'flex justify-end items-center gap-2 px-3'}>
{showOffline && (
<WdCheckbox
size="m"
labelSide="left"
label={'Hide offline'}
value={settings.hideOffline}
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
onChange={() => setSettings(() => ({ ...settings, hideOffline: !settings.hideOffline }))}
/>
)}
<WdCheckbox
size="m"
labelSide="left"
label={'Hide offline'}
value={settings.hideOffline}
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
onChange={() => setSettings(() => ({ ...settings, hideOffline: !settings.hideOffline }))}
/>
</div>
<VirtualScroller

View File

@@ -6,9 +6,6 @@ import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
import { useMapCheckPermissions } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
interface RightBarProps {
onShowOnTheMap?: () => void;
onShowMapSettings?: () => void;
@@ -17,8 +14,6 @@ interface RightBarProps {
export const RightBar = ({ onShowOnTheMap, onShowMapSettings }: RightBarProps) => {
const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState();
const canTrackCharacters = useMapCheckPermissions([UserPermission.TRACK_CHARACTER]);
const isShowMinimap = interfaceSettings.isShowMinimap === undefined ? true : interfaceSettings.isShowMinimap;
const handleAddCharacter = useCallback(() => {
@@ -69,21 +64,19 @@ export const RightBar = ({ onShowOnTheMap, onShowMapSettings }: RightBarProps) =
</button>
</WdTooltipWrapper>
{canTrackCharacters && (
<WdTooltipWrapper content="Show on the map" position={TooltipPosition.left}>
<button
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
type="button"
onClick={onShowOnTheMap}
>
<i className="pi pi-hashtag"></i>
</button>
</WdTooltipWrapper>
)}
<WdTooltipWrapper content="Show on the map" position={TooltipPosition.left}>
<button
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
type="button"
onClick={onShowOnTheMap}
>
<i className="pi pi-hashtag"></i>
</button>
</WdTooltipWrapper>
</div>
<div className="flex flex-col items-center mb-2 gap-1">
<WdTooltipWrapper content="Map user settings" position={TooltipPosition.left}>
<WdTooltipWrapper content="User settings" position={TooltipPosition.left}>
<button
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
type="button"

View File

@@ -1,10 +0,0 @@
import { createGenericContext } from '@/hooks/Mapper/utils/abstractContextProvider.tsx';
export interface SystemsSettingsProvider {
systemId: string;
}
const { Provider, useContextValue } = createGenericContext<SystemsSettingsProvider>();
export const SystemsSettingsProvider = Provider;
export const useSystemsSettingsProvider = useContextValue;

View File

@@ -1,81 +0,0 @@
.verticalTabsContainer {
width: 100%;
min-height: 300px;
}
.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: 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 {
border-right: 4px solid var(--primary-color);
}
}
}
}
.p-tabview-panel {
flex-grow: 1;
}
}
}

View File

@@ -1,190 +0,0 @@
import { Dialog } from 'primereact/dialog';
import { useCallback, useEffect } from 'react';
import { OutCommand, SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
import { Controller, FormProvider, useForm } from 'react-hook-form';
import {
SignatureGroupContent,
SignatureGroupSelect,
} from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components';
import { InputText } from 'primereact/inputtext';
import { SystemsSettingsProvider } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/Provider.tsx';
import { Button } from 'primereact/button';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
type SystemSignaturePrepared = Omit<SystemSignature, 'linked_system'> & { linked_system: string };
export interface MapSettingsProps {
systemId: string;
show: boolean;
onHide: () => void;
signatureData: SystemSignature | null;
}
export const SignatureSettings = ({ systemId, show, onHide, signatureData }: MapSettingsProps) => {
const { outCommand } = useMapRootState();
const handleShow = async () => {};
const signatureForm = useForm<Partial<SystemSignaturePrepared>>({});
const handleSave = useCallback(
async (e: any) => {
e?.preventDefault();
if (!signatureData) {
return;
}
const { group, ...values } = signatureForm.getValues();
let out = { ...signatureData };
switch (group) {
case SignatureGroup.Wormhole:
if (values.linked_system) {
await outCommand({
type: OutCommand.linkSignatureToSystem,
data: {
signature_eve_id: signatureData.eve_id,
solar_system_source: systemId,
solar_system_target: values.linked_system,
},
});
}
out = {
...out,
custom_info: JSON.stringify({
k162Type: values.k162Type,
}),
};
if (values.type != null) {
out = { ...out, type: values.type };
}
if (signatureData.group !== SignatureGroup.Wormhole) {
out = { ...out, name: '' };
}
break;
case SignatureGroup.CosmicSignature:
out = { ...out, type: '', name: '' };
break;
default:
if (values.name != null) {
out = { ...out, name: values.name ?? '' };
}
}
if (values.description != null) {
out = { ...out, description: values.description };
}
// Note: when type of signature changed from WH to other type - we should drop name
if (
group !== SignatureGroup.Wormhole && // new
signatureData.group === SignatureGroup.Wormhole && // prev
signatureData.linked_system
) {
await outCommand({
type: OutCommand.unlinkSignature,
data: { signature_eve_id: signatureData.eve_id, solar_system_source: systemId },
});
out = { ...out, type: '' };
}
if (group === SignatureGroup.Wormhole && signatureData.linked_system != null && values.linked_system === null) {
await outCommand({
type: OutCommand.unlinkSignature,
data: { signature_eve_id: signatureData.eve_id, solar_system_source: systemId },
});
}
// Note: despite groups have optional type - this will always set
out = { ...out, group: group! };
await outCommand({
type: OutCommand.updateSignatures,
data: {
system_id: systemId,
added: [],
updated: [out],
removed: [],
},
});
signatureForm.reset();
onHide();
},
[signatureForm, onHide, outCommand, signatureData, systemId],
);
useEffect(() => {
if (!signatureData) {
signatureForm.reset();
return;
}
const { linked_system, custom_info, ...rest } = signatureData;
let k162Type = null;
if (custom_info) {
const customInfo = JSON.parse(custom_info);
k162Type = customInfo.k162Type;
}
signatureForm.reset({
linked_system: linked_system?.solar_system_id.toString() ?? undefined,
k162Type: k162Type,
...rest,
});
}, [signatureForm, signatureData]);
return (
<Dialog
header={`Signature Edit [${signatureData?.eve_id}]`}
visible={show}
draggable={false}
style={{ width: '390px' }}
onShow={handleShow}
onHide={() => {
if (!show) {
return;
}
onHide();
}}
>
<SystemsSettingsProvider initialValue={{ systemId }}>
<FormProvider {...signatureForm}>
<form onSubmit={handleSave}>
<div className="flex flex-col gap-2 justify-between">
<div className="w-full flex flex-col gap-1 p-1 min-h-[150px]">
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
<span>Group:</span>
<SignatureGroupSelect name="group" />
</label>
<SignatureGroupContent />
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
<span>Description:</span>
<Controller
name="description"
control={signatureForm.control}
render={({ field }) => (
<InputText placeholder="Type description" value={field.value} onChange={field.onChange} />
)}
/>
</label>
</div>
<div className="flex gap-2 justify-end">
<Button onClick={handleSave} outlined size="small" label="Save"></Button>
</div>
</div>
</form>
</FormProvider>
</SystemsSettingsProvider>
</Dialog>
);
};

View File

@@ -1,45 +0,0 @@
import { Controller, useFormContext } from 'react-hook-form';
import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
import { useSystemsSettingsProvider } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/Provider.tsx';
import { SignatureGroupContentWormholes } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureGroupContentWormholes.tsx';
import { InputText } from 'primereact/inputtext';
export interface SignatureGroupContentProps {}
export const SignatureGroupContent = ({}: SignatureGroupContentProps) => {
const { watch, control } = useFormContext<SystemSignature>();
const group = watch('group');
const {
value: { systemId },
} = useSystemsSettingsProvider();
if (!systemId) {
return null;
}
if (group === SignatureGroup.Wormhole) {
return (
<>
<SignatureGroupContentWormholes />
</>
);
}
if (group === SignatureGroup.CosmicSignature) {
return <div></div>;
}
return (
<div>
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
<span>Name:</span>
<Controller
name="name"
control={control}
render={({ field }) => <InputText placeholder="Name" value={field.value} onChange={field.onChange} />}
/>
</label>
</div>
);
};

View File

@@ -1,31 +0,0 @@
import { useFormContext } from 'react-hook-form';
import { SystemSignature } from '@/hooks/Mapper/types';
import { SignatureWormholeTypeSelect } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureWormholeTypeSelect';
import { SignatureK162TypeSelect } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureK162TypeSelect';
import { SignatureLeadsToSelect } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureLeadsToSelect';
export const SignatureGroupContentWormholes = () => {
const { watch } = useFormContext<SystemSignature>();
const type = watch('type');
return (
<>
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
<span>Type:</span>
<SignatureWormholeTypeSelect name="type" />
</label>
{type === 'K162' && (
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
<span>K162 Type:</span>
<SignatureK162TypeSelect name="k162Type" />
</label>
)}
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
<span>Leads To:</span>
<SignatureLeadsToSelect name="linked_system" />
</label>
</>
);
};

View File

@@ -1,59 +0,0 @@
import { Dropdown } from 'primereact/dropdown';
import clsx from 'clsx';
import { SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
import { renderIcon } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders';
import { Controller, useFormContext } from 'react-hook-form';
const signatureGroupOptions = Object.keys(SignatureGroup).map(x => ({
value: SignatureGroup[x as keyof typeof SignatureGroup],
label: SignatureGroup[x as keyof typeof SignatureGroup],
}));
// @ts-ignore
const renderSignatureTemplate = option => {
if (!option) {
return 'No group selected';
}
return (
<div className="flex gap-2 items-center">
<span className="w-[20px] mt-[1px] flex justify-center items-center">
{renderIcon(
{ group: option.label } as SystemSignature,
option.label === SignatureGroup.CosmicSignature ? { w: 10, h: 10 } : { w: 16, h: 16 },
)}
</span>
<span>{option.label}</span>
</div>
);
};
export interface SignatureGroupSelectProps {
name: string;
defaultValue?: string;
}
export const SignatureGroupSelect = ({ name, defaultValue = '' }: SignatureGroupSelectProps) => {
const { control } = useFormContext();
return (
<Controller
name={name}
control={control}
defaultValue={defaultValue}
render={({ field }) => (
<Dropdown
value={field.value}
onChange={field.onChange}
options={signatureGroupOptions}
optionLabel="label"
optionValue="value"
placeholder="Select group"
className={clsx('w-full')}
scrollHeight="240px"
itemTemplate={renderSignatureTemplate}
valueTemplate={renderSignatureTemplate}
/>
)}
/>
);
};

View File

@@ -1,136 +0,0 @@
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}
/>
);
}}
/>
);
};

View File

@@ -1,110 +0,0 @@
import { Dropdown } from 'primereact/dropdown';
import clsx from 'clsx';
import { Controller, useFormContext } from 'react-hook-form';
import { useSystemsSettingsProvider } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/Provider.tsx';
import { useSystemInfo } from '@/hooks/Mapper/components/hooks';
import { useMemo } from 'react';
import { SystemView } from '@/hooks/Mapper/components/ui-kit';
import classes from './SignatureLeadsToSelect.module.scss';
import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts';
import { SystemSignature } from '@/hooks/Mapper/types';
import { WORMHOLES_ADDITIONAL_INFO_BY_CLASS_ID } from '@/hooks/Mapper/components/map/constants.ts';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
// @ts-ignore
const renderLinkedSystemItem = (option: { value: string }) => {
const { value } = option;
if (value == null) {
return <div className="flex gap-2 items-center">- Unknown -</div>;
}
return (
<div className="flex gap-2 items-center">
<SystemView systemId={value} className={classes.SystemView} />
</div>
);
};
// @ts-ignore
const renderLinkedSystemValue = (option: { value: string }) => {
if (!option) {
return 'Select Leads To system';
}
if (option.value == null) {
return 'Select Leads To system';
}
return (
<div className="flex gap-2 items-center">
<SystemView systemId={option.value} className={classes.SystemView} />
</div>
);
};
const renderLeadsToEmpty = () => <div className="flex items-center text-[14px]">No wormhole to select</div>;
export interface SignatureLeadsToSelectProps {
name: string;
defaultValue?: string;
}
export const SignatureLeadsToSelect = ({ name, defaultValue = '' }: SignatureLeadsToSelectProps) => {
const { control, watch } = useFormContext<SystemSignature>();
const group = watch('type');
const {
value: { systemId },
} = useSystemsSettingsProvider();
const { leadsTo } = useSystemInfo({ systemId });
const { systems: systemStatics } = useLoadSystemStatic({ systems: leadsTo });
const {
data: { wormholes },
} = useMapRootState();
const leadsToOptions = useMemo(() => {
return [
{ value: null },
...leadsTo
.filter(systemId => {
const systemStatic = systemStatics.get(parseInt(systemId));
const whInfo = wormholes.find(x => x.name === group);
if (!systemStatic || !whInfo || group === 'K162') {
return true;
}
const { id: whType } = WORMHOLES_ADDITIONAL_INFO_BY_CLASS_ID[systemStatic.system_class];
return whInfo.dest === whType;
})
.map(x => ({ value: x })),
];
}, [group, leadsTo, systemStatics, wormholes]);
return (
<Controller
// @ts-ignore
name={name}
control={control}
defaultValue={defaultValue}
render={({ field }) => {
return (
<Dropdown
value={field.value}
onChange={field.onChange}
options={leadsToOptions}
optionValue="value"
placeholder="Select Leads To wormhole"
className={clsx('w-full')}
scrollHeight="240px"
itemTemplate={renderLinkedSystemItem}
valueTemplate={renderLinkedSystemValue}
emptyMessage={renderLeadsToEmpty}
/>
);
}}
/>
);
};

View File

@@ -1,152 +0,0 @@
import { Dropdown } from 'primereact/dropdown';
import clsx from 'clsx';
import { Respawn, SolarSystemStaticInfoRaw, WormholeDataRaw } from '@/hooks/Mapper/types';
import { Controller, useFormContext } from 'react-hook-form';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useSystemsSettingsProvider } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/Provider.tsx';
import { useSystemInfo } from '@/hooks/Mapper/components/hooks';
import {
SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS,
WORMHOLES_ADDITIONAL_INFO_BY_CLASS_ID,
} from '@/hooks/Mapper/components/map/constants.ts';
import { useMemo } from 'react';
import { WHClassView } from '@/hooks/Mapper/components/ui-kit';
const getPossibleWormholes = (systemStatic: SolarSystemStaticInfoRaw, wormholes: WormholeDataRaw[]) => {
const { id: whType } = WORMHOLES_ADDITIONAL_INFO_BY_CLASS_ID[systemStatic.system_class];
// @ts-ignore
const spawnClassGroup = SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS[whType];
const possibleWHTypes = wormholes.filter(x => {
return x.src.some(x => {
const [group, type] = x.split('-');
// eslint-disable-next-line no-console
console.log('JOipP', `group, type`, group, type);
if (type === 'shattered') {
return systemStatic.is_shattered && group === spawnClassGroup;
}
return group === spawnClassGroup;
});
});
// eslint-disable-next-line no-console
console.log('JOipP', `possibleWHTypes`, possibleWHTypes);
// debugger;
return {
statics: possibleWHTypes
.filter(x => x.respawn.some(y => y === Respawn.static))
.filter(x => systemStatic.statics.includes(x.name)),
k162: wormholes.find(x => x.name === 'K162')!,
wanderings: possibleWHTypes.filter(x => x.respawn.some(y => y === Respawn.wandering)),
};
};
// @ts-ignore
const renderWHTypeGroupTemplate = option => {
return (
<div className="flex gap-2 items-center">
<span>{option.label}</span>
</div>
);
};
// @ts-ignore
const renderWHTypeTemplateValue = (option: { label: string; data: WormholeDataRaw }) => {
if (!option) {
return 'Select wormhole type';
}
return (
<div className="flex gap-2 items-center">
<WHClassView whClassName={option.data.name} noOffset useShortTitle />
</div>
);
};
// @ts-ignore
const renderWHTypeTemplate = (option: { label: string; data: WormholeDataRaw }) => {
return (
<div className="flex gap-2 items-center ml-[1rem]">
<WHClassView whClassName={option.data.name} noOffset useShortTitle />
</div>
);
};
export interface SignatureGroupSelectProps {
name: string;
defaultValue?: string;
}
export const SignatureWormholeTypeSelect = ({ name, defaultValue = '' }: SignatureGroupSelectProps) => {
const { control } = useFormContext();
const {
data: { wormholes },
} = useMapRootState();
const {
value: { systemId },
} = useSystemsSettingsProvider();
const system = useSystemInfo({ systemId });
const possibleWormholesOptions = useMemo(() => {
const possibleWormholes = getPossibleWormholes(system.staticInfo, wormholes);
return [
{
label: 'Statics',
items: [
...possibleWormholes.statics.map(x => ({
label: x.name,
value: x.name,
data: x,
})),
{
value: possibleWormholes.k162.name,
label: possibleWormholes.k162.name,
data: possibleWormholes.k162,
},
],
},
{
label: 'Wanderings',
items: possibleWormholes.wanderings.map(x => ({
label: x.name,
value: x.name,
data: x,
})),
},
];
}, [system, wormholes]);
return (
<Controller
name={name}
control={control}
defaultValue={defaultValue}
render={({ field }) => (
<Dropdown
value={field.value}
onChange={field.onChange}
options={possibleWormholesOptions}
optionLabel="label"
optionValue="value"
placeholder="Select wormhole type"
optionGroupLabel="label"
optionGroupChildren="items"
className={clsx('w-full')}
scrollHeight="240px"
optionGroupTemplate={renderWHTypeGroupTemplate}
itemTemplate={renderWHTypeTemplate}
valueTemplate={renderWHTypeTemplateValue}
/>
)}
/>
);
};

View File

@@ -1,3 +0,0 @@
export * from './SignatureGroupSelect';
export * from './SignatureGroupContent';
export * from './SignatureK162TypeSelect';

View File

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

View File

@@ -1,14 +1,14 @@
import { Map } from '@/hooks/Mapper/components/map/Map.tsx';
import { useCallback, useRef, useState } from 'react';
import { OutCommand, OutCommandHandler, SolarSystemConnection } from '@/hooks/Mapper/types';
import { ForwardedRef, useCallback, useRef, useState } from 'react';
import { MapHandlers, OutCommand, OutCommandHandler, SolarSystemConnection } from '@/hooks/Mapper/types';
import { MapRootData, useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts';
import isEqual from 'lodash.isequal';
import { ContextMenuSystem, useContextMenuSystemHandlers } from '@/hooks/Mapper/components/contexts';
import {
SystemCustomLabelDialog,
SystemLinkSignatureDialog,
SystemSettingsDialog,
SystemLinkSignatureDialog,
} from '@/hooks/Mapper/components/mapInterface/components';
import classes from './MapWrapper.module.scss';
import { Connections } from '@/hooks/Mapper/components/mapRootContent/components/Connections';
@@ -20,47 +20,25 @@ import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
import { useMapEventListener } from '@/hooks/Mapper/events';
import { STORED_INTERFACE_DEFAULT_VALUES } from '@/hooks/Mapper/mapRootProvider/MapRootProvider';
import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks';
import { useCommonMapEventProcessor } from '@/hooks/Mapper/components/mapWrapper/hooks/useCommonMapEventProcessor.ts';
interface MapWrapperProps {
refn: ForwardedRef<MapHandlers>;
}
// TODO: INFO - this component needs for abstract work with Map instance
export const MapWrapper = () => {
export const MapWrapper = ({ refn }: MapWrapperProps) => {
const {
update,
outCommand,
data: { selectedConnections, selectedSystems, hubs, systems },
interfaceSettings: {
isShowMenu,
isShowMinimap = STORED_INTERFACE_DEFAULT_VALUES.isShowMinimap,
isShowKSpace,
isThickConnections,
isShowBackgroundPattern,
isSoftBackground,
},
interfaceSettings: { isShowMenu, isShowMinimap = STORED_INTERFACE_DEFAULT_VALUES.isShowMinimap, isShowKSpace },
} = useMapRootState();
const { deleteSystems } = useDeleteSystems();
const { mapRef, runCommand } = useCommonMapEventProcessor();
const { open, ...systemContextProps } = useContextMenuSystemHandlers({ systems, hubs, outCommand });
const { handleSystemMultipleContext, ...systemMultipleCtxProps } = useContextMenuSystemMultipleHandlers();
const [openSettings, setOpenSettings] = useState<string | null>(null);
const [openLinkSignatures, setOpenLinkSignatures] = useState<any | null>(null);
const [openCustomLabel, setOpenCustomLabel] = useState<string | 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 ref = useRef({ selectedConnections, selectedSystems, systemContextProps, systems });
ref.current = { selectedConnections, selectedSystems, systemContextProps, systems };
const onSelectionChange: OnMapSelectionChange = useCallback(
({ systems, connections }) => {
@@ -81,6 +59,9 @@ export const MapWrapper = () => {
[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(
event => {
switch (event.type) {
@@ -114,19 +95,22 @@ export const MapWrapper = () => {
[open],
);
const [selectedConnection, setSelectedConnection] = useState<SolarSystemConnection | null>(null);
const handleConnectionDbClick = useCallback((e: SolarSystemConnection) => setSelectedConnection(e), []);
const handleManualDelete = useCallback((toDelete: string[]) => {
const restDel = toDelete.filter(x => ref.current.systems.some(y => y.id === x));
if (restDel.length > 0) {
ref.current.deleteSystems(restDel);
useMapEventListener(event => {
switch (event.name) {
case Commands.linkSignatureToSystem:
setOpenLinkSignatures(event.data);
return true;
}
}, []);
});
return (
<>
<Map
ref={mapRef}
ref={refn}
onCommand={handleCommand}
onSelectionChange={onSelectionChange}
onConnectionInfoClick={handleConnectionDbClick}
@@ -135,10 +119,6 @@ export const MapWrapper = () => {
minimapClasses={!isShowMenu ? classes.MiniMap : undefined}
isShowMinimap={isShowMinimap}
showKSpaceBG={isShowKSpace}
onManualDelete={handleManualDelete}
isThickConnections={isThickConnections}
isShowBackgroundPattern={isShowBackgroundPattern}
isSoftBackground={isSoftBackground}
/>
{openSettings != null && (

View File

@@ -1,38 +0,0 @@
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,
};
};

View File

@@ -18,7 +18,7 @@ const Topbar = ({ children }: WithChildren) => {
<nav
className={clsx(
'px-2 flex items-center justify-center min-w-0 h-12 pointer-events-auto',
'border-b border-stone-800 bg-gray-800 bg-opacity-5',
'border-b border-gray-900 bg-gray-800 bg-opacity-5',
'bg-opacity-70 bg-neutral-900',
)}
>

View File

@@ -4,7 +4,7 @@ import classes from './CharacterCard.module.scss';
import { SystemView } from '@/hooks/Mapper/components/ui-kit/SystemView';
import { CharacterTypeRaw, WithIsOwnCharacter } from '@/hooks/Mapper/types';
import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
import { emitMapEvent } from '@/hooks/Mapper/events';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
type CharacterCardProps = {
compact?: boolean;
@@ -34,12 +34,11 @@ export const CharacterCard = ({
useSystemsCache,
...char
}: CharacterCardProps) => {
const { mapRef } = useMapRootState();
const handleSelect = useCallback(() => {
emitMapEvent({
name: Commands.centerSystem,
data: char?.location?.solar_system_id?.toString(),
});
}, [char]);
mapRef.current?.command(Commands.centerSystem, char?.location?.solar_system_id?.toString());
}, [mapRef, char]);
return (
<div className={clsx(classes.CharacterCard, 'w-full text-xs', 'flex flex-col box-border')} onClick={handleSelect}>

View File

@@ -3,23 +3,15 @@ import { SystemViewStandalone } from '@/hooks/Mapper/components/ui-kit';
import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts';
import { useMemo } from 'react';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { SolarSystemStaticInfoRaw } from '@/hooks/Mapper/types';
export type SystemViewProps = {
systemId: string;
systemInfo?: SolarSystemStaticInfoRaw;
hideRegion?: boolean;
useSystemsCache?: boolean;
showCustomName?: boolean;
} & WithClassName;
export const SystemView = ({
systemId,
systemInfo: customSystemInfo,
hideRegion,
className,
showCustomName,
}: SystemViewProps) => {
export const SystemView = ({ systemId, hideRegion, className, showCustomName }: SystemViewProps) => {
const memSystems = useMemo(() => [systemId], [systemId]);
const { systems, loading } = useLoadSystemStatic({ systems: memSystems });
@@ -28,12 +20,9 @@ export const SystemView = ({
} = useMapRootState();
const systemInfo = useMemo(() => {
if (!systemId) {
return customSystemInfo;
}
return systems.get(parseInt(systemId));
// eslint-disable-next-line
}, [customSystemInfo, systemId, systems, loading]);
}, [systemId, systems, loading]);
const mapSystemInfo = useMemo(() => {
if (!showCustomName) {

View File

@@ -25,6 +25,7 @@ export const SystemViewStandalone = ({
className,
hideRegion,
customName,
class_title,
system_class,
solar_system_name,
@@ -37,7 +38,6 @@ export const SystemViewStandalone = ({
...props
}: SystemViewStandaloneProps) => {
const classTitleColor = getSystemClassStyles({ systemClass: system_class, security });
const isWH = isWormholeSpace(system_class);
const handleClick = useCallback(

View File

@@ -1,14 +1,10 @@
.WHClassViewRoot {
}
.WHClassViewContent {
display: flex;
gap: 2px;
&.NoOffset {
gap: 4px;
align-items: center;
}
}
.WHClassName {
@@ -17,12 +13,3 @@
font-weight: bold;
top: -2px;
}
.NoOffset {
*.WHClassName {
position: relative;
font-size: 12px;
font-weight: initial !important;
top: initial !important;
}
}

View File

@@ -16,75 +16,42 @@ const prepareMass = (mass: number) => {
export interface WHClassViewProps {
whClassName: string;
noOffset?: boolean;
useShortTitle?: boolean;
hideTooltip?: boolean;
hideWhClass?: boolean;
hideWhClassName?: boolean;
highlightName?: boolean;
className?: string;
classNameWh?: string;
}
export const WHClassView = ({
whClassName,
noOffset,
useShortTitle,
hideTooltip,
hideWhClass,
hideWhClassName,
highlightName,
className,
classNameWh,
}: WHClassViewProps) => {
export const WHClassView = ({ whClassName }: WHClassViewProps) => {
const {
data: { wormholesData },
} = useMapRootState();
const whData = useMemo(() => wormholesData[whClassName], [whClassName, wormholesData]);
const whClass = useMemo(() => WORMHOLES_ADDITIONAL_INFO[whData.dest], [whData.dest]);
const whClassStyle = WORMHOLE_CLASS_STYLES[whClass?.wormholeClassID] ?? '';
const uid = useMemo(() => new Date().getTime().toString(), []);
const whClassStyle = WORMHOLE_CLASS_STYLES[whClass.wormholeClassID];
return (
<div className={clsx(classes.WHClassViewRoot, className)}>
{!hideTooltip && (
<Tooltip
target={`.wh-name${whClassName}${uid}`}
position="right"
mouseTrack
mouseTrackLeft={20}
mouseTrackTop={30}
className="border border-green-300 rounded border-opacity-10 bg-stone-900 bg-opacity-90 "
>
<div className="flex gap-3">
<div className="flex flex-col gap-1">
<InfoDrawer title="Total mass">{prepareMass(whData.total_mass)}</InfoDrawer>
<InfoDrawer title="Jump mass">{prepareMass(whData.max_mass_per_jump)}</InfoDrawer>
</div>
<div className="flex flex-col gap-1">
<InfoDrawer title="Lifetime">{whData.lifetime}h</InfoDrawer>
<InfoDrawer title="Mass regen">{prepareMass(whData.mass_regen)}</InfoDrawer>
</div>
</div>
</Tooltip>
)}
<div
className={clsx(
classes.WHClassViewContent,
{ [classes.NoOffset]: noOffset },
'wh-name select-none cursor-help',
`wh-name${whClassName}${uid}`,
)}
<div className={classes.WHClassViewRoot}>
<Tooltip
target={`.wh-name${whClassName}`}
position="right"
mouseTrack
mouseTrackLeft={20}
mouseTrackTop={30}
className="border border-green-300 rounded border-opacity-10 bg-stone-900 bg-opacity-70 "
>
{!hideWhClassName && <span className={clsx({ [whClassStyle]: highlightName })}>{whClassName}</span>}
{!hideWhClass && whClass && (
<span className={clsx(classes.WHClassName, whClassStyle, classNameWh)}>
{useShortTitle ? whClass.shortTitle : whClass.shortName}
</span>
)}
<div className="flex gap-3">
<div className="flex flex-col gap-1">
<InfoDrawer title="Total mass">{prepareMass(whData.total_mass)}</InfoDrawer>
<InfoDrawer title="Jump mass">{prepareMass(whData.max_mass_per_jump)}</InfoDrawer>
</div>
<div className="flex flex-col gap-1">
<InfoDrawer title="Lifetime">{whData.lifetime}h</InfoDrawer>
<InfoDrawer title="Mass regen">{prepareMass(whData.mass_regen)}</InfoDrawer>
</div>
</div>
</Tooltip>
<div className={clsx(classes.WHClassViewContent, 'wh-name select-none cursor-help', `wh-name${whClassName}`)}>
<span>{whClassName}</span>
<span className={clsx(classes.WHClassName, whClassStyle)}>{whClass.shortName}</span>
</div>
</div>
);

View File

@@ -1,12 +1,13 @@
import { createEvent } from 'react-event-hook';
import { Command, CommandData } from '@/hooks/Mapper/types/mapHandlers.ts';
export interface MapEvent<T extends Command> {
name: T;
data: CommandData[T];
export interface MapEvent {
name: string;
data: {
solar_system_source: number;
solar_system_target: number;
};
}
const { useMapEventListener, emitMapEvent } = createEvent('map-event')<MapEvent<Command>>();
const { useMapEventListener, emitMapEvent } = createEvent('map-event')<MapEvent>();
export { useMapEventListener, emitMapEvent };

View File

@@ -19,7 +19,6 @@ export const parseSignatures = (value: string, availableKeys: string[]): SystemS
kind: availableKeys.includes(sigArrInfo[1]) ? sigArrInfo[1] : COSMIC_SIGNATURE,
group: sigArrInfo[2],
name: sigArrInfo[3],
type: '',
});
}

View File

@@ -2,10 +2,6 @@ import { WORMHOLES_ADDITIONAL_INFO } from '@/hooks/Mapper/components/map/constan
import { WormholeDataRaw } from '@/hooks/Mapper/types';
export const sortWHClasses = (wormholesData: Record<string, WormholeDataRaw>, statics: string[]) => {
if (!statics) {
return [];
}
return statics
.map(x => wormholesData[x])
.map(x => ({ name: x.name, ...WORMHOLES_ADDITIONAL_INFO[x.dest] }))

View File

@@ -1,13 +1,13 @@
import { useState, useEffect, useCallback } from 'react';
export const useClipboard = () => {
const [clipboardContent, setClipboardContent] = useState<{ text: string } | null>(null);
const [clipboardContent, setClipboardContent] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const getClipboardContent = useCallback(async () => {
try {
const text = await navigator.clipboard.readText();
setClipboardContent({ text });
setClipboardContent(text);
setError(null);
} catch (err) {
setError('Failed to read clipboard content.');
@@ -18,7 +18,7 @@ export const useClipboard = () => {
const handlePaste = (event: ClipboardEvent) => {
const text = event.clipboardData?.getData('text');
if (text) {
setClipboardContent({ text });
setClipboardContent(text);
setError(null);
}
};
@@ -30,5 +30,5 @@ export const useClipboard = () => {
};
}, []);
return { clipboardContent, error, getClipboardContent, setClipboardContent };
return { clipboardContent, error, getClipboardContent };
};

View File

@@ -1,6 +1,6 @@
import { useEffect } from 'react';
export const useHotkey = (isMetaKey: boolean, hotkeys: string[], callback: (e: KeyboardEvent) => void) => {
export const useHotkey = (isMetaKey: boolean, hotkeys: string[], callback: () => void) => {
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if ((!isMetaKey || event.ctrlKey || event.metaKey) && hotkeys.includes(event.key)) {
@@ -8,14 +8,14 @@ export const useHotkey = (isMetaKey: boolean, hotkeys: string[], callback: (e: K
return;
}
event.preventDefault();
callback(event);
callback();
}
};
window.addEventListener('keydown', handleKeyDown, { capture: true });
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown, { capture: true });
window.removeEventListener('keydown', handleKeyDown);
};
}, [isMetaKey, hotkeys, callback]);
};

Some files were not shown because too many files have changed in this diff Show More