Compare commits

..

3 Commits

Author SHA1 Message Date
Dmitry Popov
b1f1098df6 chore: release version v1.13.7 2024-10-31 22:56:59 +01:00
Dmitry Popov
ed5d824c0a refactor(Map): Map event handling refactoring 2024-10-31 19:19:23 +01:00
Dmitry Popov
d8e4631981 refactor(Map): Map event handling refactoring 2024-10-31 19:19:07 +01:00
560 changed files with 11448 additions and 63413 deletions

View File

@@ -1,28 +1,12 @@
FROM elixir:1.17-otp-27
RUN apt install -yq curl gnupg
# Install OS packages and Node.js (via nodesource),
# plus inotify-tools and yarn
RUN apt-get update && apt-get install -y --no-install-recommends \
sudo \
curl \
make \
git \
bash \
build-essential \
ca-certificates \
jq \
vim \
net-tools \
procps \
# Optionally add any other tools you need, e.g. vim, wget...
&& curl -sL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y --no-install-recommends nodejs inotify-tools \
&& npm install -g yarn \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
FROM elixir:1.16-otp-25
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

@@ -1,30 +1,15 @@
{
"name": "wanderer-dev",
"dockerComposeFile": ["./docker-compose.yml"],
"customizations": {
"vscode": {
"extensions": [
"jakebecker.elixir-ls",
"JakeBecker.elixir-ls",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
],
"settings": {
"editor.formatOnSave": true,
"search.exclude": {
"**/doc": true
},
"elixirLS.dialyzerEnabled": false
}
}
},
"extensions": ["jakebecker.elixir-ls"],
"service": "wanderer",
"workspaceFolder": "/app",
"shutdownAction": "stopCompose",
"features": {
"ghcr.io/devcontainers/features/common-utils:2": {
"networkArgs": ["--add-host=host.docker.internal:host-gateway"]
}
},
"forwardPorts": [4444]
"settings": {
"editor.formatOnSave": true,
"search.exclude": {
"**/doc": true
},
"elixirLS.dialyzerEnabled": false
}
}

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,11 +10,11 @@ services:
ports:
- "5432:5432"
volumes:
- db-new:/var/lib/postgresql/data
- db:/var/lib/postgresql/data
wanderer:
environment:
PORT: 4444
PORT: 8000
DB_HOST: db
WEB_APP_URL: "http://localhost:4444"
ERL_AFLAGS: "-kernel shell_history enabled"
@@ -22,7 +22,7 @@ services:
context: .
dockerfile: Dockerfile
ports:
- 4444:4444
- 8000:8000
volumes:
- ..:/app:delegated
- ~/.gitconfig:/root/.gitconfig
@@ -33,4 +33,4 @@ services:
volumes:
elixir-artifacts: {}
db-new: {}
db: {}

View File

@@ -6,6 +6,3 @@ export EVE_CLIENT_WITH_WALLET_ID="<EVE_CLIENT_WITH_WALLET_ID>"
export EVE_CLIENT_WITH_WALLET_SECRET="<EVE_CLIENT_WITH_WALLET_SECRET>"
export GIT_SHA="1111"
export WANDERER_INVITES="false"
export WANDERER_PUBLIC_API_DISABLED="false"
export WANDERER_CHARACTER_API_DISABLED="false"
export WANDERER_ZKILL_PRELOAD_DISABLED="false"

View File

@@ -1,6 +1,8 @@
name: Build
on:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
push:
branches:
- main
@@ -39,28 +41,8 @@ jobs:
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
manual-approval:
name: Manual Approval
runs-on: ubuntu-latest
needs: deploy-test
if: success()
permissions:
issues: write
steps:
- name: Await Manual Approval
uses: trstringer/manual-approval@v1
with:
secret: ${{ github.TOKEN }}
approvers: DmitryPopov
minimum-approvals: 1
issue-title: "Manual Approval Required for Release"
issue-body: "Please approve or deny the deployment."
build:
name: 🛠 Build
needs: manual-approval
runs-on: ubuntu-22.04
if: ${{ (github.ref == 'refs/heads/main') && github.event_name == 'push' }}
permissions:
@@ -96,23 +78,22 @@ jobs:
fetch-depth: 0
- name: 😅 Cache deps
id: cache-deps
uses: actions/cache@v4
uses: actions/cache@v3
env:
cache-name: cache-elixir-deps
with:
path: |
deps
key: ${{ runner.os }}-mix-${{ matrix.elixir }}-${{ matrix.otp }}-${{ hashFiles('**/mix.lock') }}
path: deps
key: ${{ runner.os }}-mix-${{ env.cache-name }}-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ runner.os }}-mix-${{ matrix.elixir }}-${{ matrix.otp }}-
${{ runner.os }}-mix-${{ env.cache-name }}-
- name: 😅 Cache compiled build
id: cache-build
uses: actions/cache@v4
uses: actions/cache@v3
env:
cache-name: cache-compiled-build
with:
path: |
_build
**/_build
key: ${{ runner.os }}-build-${{ hashFiles('**/mix.lock') }}-${{ hashFiles( '**/lib/**/*.{ex,eex}', '**/config/*.exs', '**/mix.exs' ) }}
restore-keys: |
${{ runner.os }}-build-${{ hashFiles('**/mix.lock') }}-
@@ -141,9 +122,6 @@ jobs:
name: 🛠 Build Docker Images
needs: build
runs-on: ubuntu-22.04
outputs:
release-tag: ${{ steps.get-latest-tag.outputs.tag }}
release-notes: ${{ steps.get-content.outputs.string }}
permissions:
checks: write
contents: write
@@ -157,7 +135,6 @@ jobs:
matrix:
platform:
- linux/amd64
- linux/arm64
steps:
- name: Prepare
run: |
@@ -206,28 +183,15 @@ jobs:
push: true
context: .
file: ./Dockerfile
cache-from: type=gha
cache-to: type=gha,mode=max
tags: ${{ env.REGISTRY_IMAGE }}:latest,${{ env.REGISTRY_IMAGE }}:${{ steps.get-latest-tag.outputs.tag }}
labels: ${{ steps.meta.outputs.labels }}
platforms: ${{ matrix.platform }}
outputs: type=image,"name=${{ env.REGISTRY_IMAGE }}",push-by-digest=true,name-canonical=true,push=true
build-args: |
MIX_ENV=prod
BUILD_METADATA=${{ steps.meta.outputs.json }}
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
- name: Image digest
run: echo ${{ steps.build.outputs.digest }}
- uses: markpatterson27/markdown-to-output@v1
id: extract-changelog
@@ -247,54 +211,16 @@ jobs:
maxLength: 500
truncationSymbol: "…"
merge:
runs-on: ubuntu-latest
needs:
- docker
steps:
- name: Download digests
uses: actions/download-artifact@v4
- name: Discord Webhook Action
uses: tsickert/discord-webhook@v5.3.0
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.WANDERER_DOCKER_USER }}
password: ${{ secrets.WANDERER_DOCKER_PASSWORD }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY_IMAGE }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{version}},value=${{ needs.docker.outputs.release-tag }}
- name: Create manifest list and push
working-directory: /tmp/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
content: ${{ steps.get-content.outputs.string }}
create-release:
name: 🏷 Create Release
runs-on: ubuntu-22.04
needs: [docker, merge]
needs: docker
if: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }}
steps:
- name: ⬇️ Checkout repo
@@ -302,11 +228,17 @@ jobs:
with:
fetch-depth: 0
- name: Get Release Tag
id: get-latest-tag
uses: "WyriHaximus/github-action-get-previous-tag@v1"
with:
fallback: 1.0.0
- name: 🏷 Create Draft Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ needs.docker.outputs.release-tag }}
name: Release ${{ needs.docker.outputs.release-tag }}
tag_name: ${{ steps.get-latest-tag.outputs.tag }}
name: Release ${{ steps.get-latest-tag.outputs.tag }}
body: |
## Info
Commit ${{ github.sha }} was deployed to `staging`. [See code diff](${{ github.event.compare }}).
@@ -316,9 +248,3 @@ jobs:
## How to Promote?
In order to promote this to prod, edit the draft and press **"Publish release"**.
draft: true
- name: Discord Webhook Action
uses: tsickert/discord-webhook@v5.3.0
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
content: ${{ needs.docker.outputs.release-notes }}

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 }}

1
.gitignore vendored
View File

@@ -26,7 +26,6 @@
.env
*.local.env
test/manual/.auto*
.direnv/
.cache/

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

File diff suppressed because it is too large Load Diff

View File

@@ -9,9 +9,6 @@ WORKDIR /app
# set build ENV
ENV MIX_ENV="prod"
# Set ERL_FLAGS for ARM compatibility
ENV ERL_FLAGS="+JPperf true"
# install mix dependencies
COPY mix.exs mix.lock ./
RUN rm -Rf _build deps && mix deps.get --only $MIX_ENV

View File

@@ -1,4 +1,4 @@
.PHONY: deploy install cleanup start yarn migrate format test coverage versions standalone-tests
.PHONY: deploy install cleanup start yarn migrate format test coverage versions
ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
SHELL := /bin/bash
@@ -35,11 +35,6 @@ test t:
coverage cover co:
mix test --cover
unit-tests ut:
@echo "Running unit tests..."
@find test/unit -name "*.exs" -exec elixir {} \;
@echo "All unit tests completed."
versions v:
@echo "Tool Versions"
@cat .tool-versions

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,14 +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# npm install -g yarn`
- `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

@@ -1,3 +1,6 @@
// import '@fontsource-variable/inter'
// import '@fontsource-variable/jetbrains-mono'
// import './lib/tailwind/index.css';
import './css/app.css';
import './lib/phoenix';

View File

@@ -3,8 +3,6 @@
@import 'primereact/resources/themes/arya-blue/theme.css' layer(primereact);
/*@import 'primereact/resources/themes/bootstrap4-dark-blue/theme.css' layer(primereact);*/
@import '../js/hooks/Mapper/components/map/styles/index.scss';
@layer tailwind-base {
@tailwind base;
}
@@ -25,10 +23,6 @@ body {
width: 400px; /* As IE6 ignores !important it will set width as 400px; */
}
body > div:first-of-type {
min-height: 500px !important;
}
.lending-normal {
font-family: 'Shentox', 'Rogan', sans-serif !important;
font-weight: 500;
@@ -112,19 +106,19 @@ body > div:first-of-type {
}
.wd-characters-icons {
/*display: flex;*/
/*transition:*/
/* border-color 250ms,*/
/* opacity 250ms;*/
/*width: 35px;*/
/*height: 35px;*/
/*border-radius: 50%;*/
/*border-width: 2px;*/
/*border-style: solid;*/
/*border-color: #5a5a5a;*/
/*background-color: rgba(0, 0, 0, 0);*/
/*cursor: pointer;*/
/*opacity: 0.6;*/
display: flex;
transition:
border-color 250ms,
opacity 250ms;
width: 35px;
height: 35px;
border-radius: 50%;
border-width: 2px;
border-style: solid;
border-color: #5a5a5a;
background-color: rgba(0, 0, 0, 0);
cursor: pointer;
opacity: 0.6;
}
.wd-bg-default {
@@ -472,530 +466,3 @@ body > div:first-of-type {
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;
}
.verticalTabsContainer {
display: flex;
width: 100%;
min-height: 300px;
}
.verticalTabsContainer .p-tabview {
width: 100%;
display: flex;
align-items: flex-start;
}
.verticalTabsContainer .p-tabview-panels {
padding: 6px 1rem !important;
flex-grow: 1;
height: 100%;
}
.verticalTabsContainer .p-tabview-nav-container {
border-right: none;
height: 100%;
}
.verticalTabsContainer .p-tabview-nav {
flex-direction: column;
width: 150px;
min-height: 100%;
border: none;
}
.verticalTabsContainer .p-tabview-nav li {
width: 100%;
border-right: 4px solid var(--surface-hover);
background-color: var(--surface-card);
transition:
background-color 200ms,
border-right-color 200ms;
}
.verticalTabsContainer .p-tabview-nav li:hover {
background-color: var(--surface-hover);
border-right: 4px solid var(--surface-100);
}
.verticalTabsContainer .p-tabview-nav li .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;
}
.verticalTabsContainer .p-tabview-nav li.p-tabview-selected {
background-color: var(--surface-50);
border-right: 4px solid var(--primary-color);
}
.verticalTabsContainer .p-tabview-nav li.p-tabview-selected .p-tabview-nav-link {
font-weight: 600;
color: var(--primary-color);
}
.verticalTabsContainer .p-tabview-nav li.p-tabview-selected:hover {
border-right: 4px solid var(--primary-color);
}
.verticalTabsContainer .p-tabview-panel {
flex-grow: 1;
}

View File

@@ -1,26 +1,27 @@
import { PrimeReactProvider } from 'primereact/api';
import { ErrorBoundary } from 'react-error-boundary';
import { PrimeReactProvider } from 'primereact/api';
import { ReactFlowProvider } from 'reactflow';
import { MapHandlers } from '@/hooks/Mapper/types/mapHandlers.ts';
import { ErrorInfo, useCallback, useEffect, useRef } from 'react';
import { ReactFlowProvider } from 'reactflow';
import { useMapperHandlers } from './useMapperHandlers';
import { MapRootContent } from '@/hooks/Mapper/components/mapRootContent/MapRootContent.tsx';
import { MapRootProvider } from '@/hooks/Mapper/mapRootProvider';
import './common-styles/main.scss';
import { MapRootProvider } from '@/hooks/Mapper/mapRootProvider';
import { MapRootContent } from '@/hooks/Mapper/components/mapRootContent/MapRootContent.tsx';
const ErrorFallback = () => {
return <div className="!z-100 absolute w-screen h-screen bg-transparent"></div>;
};
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 } = useMapperHandlers(mapperHandlerRefs.current, hooksRef);
const { handleCommand, handleMapEvent, handleMapEvents } = useMapperHandlers(mapperHandlerRefs.current, hooksRef);
const logError = useCallback((error: Error, info: ErrorInfo) => {
if (!hooksRef.current) {
@@ -35,11 +36,12 @@ export default function MapRoot({ hooks }) {
}
hooksRef.current.handleEvent('map_event', handleMapEvent);
hooksRef.current.handleEvent('map_events', handleMapEvents);
}, []);
return (
<PrimeReactProvider>
<MapRootProvider fwdRef={providerRef} outCommand={handleCommand}>
<MapRootProvider fwdRef={providerRef} outCommand={handleCommand} mapRef={mapRef}>
<ErrorBoundary FallbackComponent={ErrorFallback} onError={logError}>
<ReactFlowProvider>
<MapRootContent />

View File

@@ -67,22 +67,16 @@
}
}
.p-datatable-thead {
th, th.p-sortable-column {
font-size: 12px;
font-weight: bold;
padding: 3px 4px;
}
.p-sortable-column {
font-size: 12px;
font-weight: bold;
padding: 3px 4px;
}
.p-selectable-row td {
padding: 4px 4px;
}
.p-datatable.p-datatable-sm .p-datatable-tbody > tr > td {
padding: 3px 4px;
}
.p-sortable-column > .p-column-header-content > span:last-child {
transform: scale(0.7);
@@ -114,69 +108,3 @@
.p-dropdown-empty-message {
padding: 0.25rem 0.5rem;
}
.p-autocomplete .p-autocomplete-multiple-container .p-autocomplete-token {
margin-right: 0 !important;
}
/* Fixed sizes of Input switch */
.p-inputswitch {
width: 2.0rem;
height: 1.15rem;
.p-inputswitch-slider:before {
width: 0.8rem;
height: 0.8rem;
left: 0.14rem;
margin-top: -0.385rem;
}
&.p-highlight .p-inputswitch-slider:before {
transform: translateX(0.8rem);
}
&:not(.p-disabled):has(.p-inputswitch-input:hover) .p-inputswitch-slider {
background: rgb(255 255 255 / 21%);
}
&.p-highlight .p-inputswitch-slider {
background: #966d3d;
}
}
.p-datatable-wrapper {
height: 100%;
& {
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.5) transparent;
}
&::-webkit-scrollbar {
width: 10px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background-color: rgba(255, 255, 255, 0.5);
border-radius: 5px;
border: 2px solid transparent;
background-clip: content-box;
}
&::-webkit-scrollbar-thumb:hover {
background-color: rgba(255, 255, 255, 0.7);
}
&::-webkit-scrollbar-button {
display: none;
height: 0;
width: 0;
}
}
.p-datatable .p-datatable-tbody > tr.p-highlight {
background: initial;
}

View File

@@ -1,8 +1,11 @@
/* Основной класс диалога */
body .p-dialog {
display: flex;
flex-direction: column;
//position: absolute;
top: 0;
left: 0;
//visibility: hidden;
overflow: hidden;
border-radius: 2px;
box-shadow: 0 2px 10px 0 rgba(0,0,0,0.2);
@@ -26,10 +29,12 @@ body .p-dialog {
}
}
/* Стиль видимого диалога */
.p-dialog-visible {
visibility: visible;
}
/* Анимации */
.p-dialog-enter {
opacity: 0;
}
@@ -48,27 +53,31 @@ body .p-dialog {
transition: opacity 0.3s;
}
/* Заголовок диалога */
.p-dialog-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem;
background: #f4f4f4;
height: 40px;
//border-bottom: 1px solid #ddd;
}
/* Содержимое диалога */
.p-dialog-content {
padding: 0.5rem;
overflow-y: auto;
flex: 1;
}
/* Подвал диалога */
.p-dialog-footer {
padding: 1rem;
border-top: 1px solid #ddd;
background: #f4f4f4;
}
/* Кнопка закрытия диалога */
.p-dialog-header-close {
display: flex;
align-items: center;
@@ -84,12 +93,3 @@ body .p-dialog {
.p-dialog-header-close .pi {
font-size: 1.25rem;
}
.p-dialog {
.p-dialog-title {
font-size: 1rem !important;
}
.p-dialog-header-icons {
align-self: initial !important;
}
}

View File

@@ -1,45 +0,0 @@
.p-confirm-popup {
display: flex;
flex-direction: column;
gap: 6px;
@apply p-[12px];
&::before, &::after {
display: none;
}
.p-confirm-popup-content, .p-confirm-popup-footer {
@apply p-0 m-0;
}
.p-confirm-popup-content {
display: flex;
gap: 6px;
}
.p-confirm-popup-footer {
display: flex;
justify-content: flex-end;
gap: 4px;
}
.p-confirm-popup-icon {
font-size: 14px;
}
.p-confirm-popup-message {
@apply m-0;
font-size: 12px;
}
.p-confirm-popup-reject.p-button-sm,
.p-confirm-popup-accept.p-button-sm {
@apply px-1.5 py-1 m-0;
& > span {
font-size: 12px;
line-height: 12px;
}
}
}

View File

@@ -1,77 +0,0 @@
.vertical-tabs-container {
display: flex;
width: 100%;
min-height: 300px;
.p-tabview {
width: 100%;
display: flex;
align-items: flex-start;
}
.p-tabview-panels {
padding: 6px 1rem;
flex-grow: 1;
height: 100%;
}
.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,6 +1,4 @@
@import "fix-dialog";
@import "fix-popup";
@import "fix-tabs";
//@import "fix-input";
//@import "theme";

View File

@@ -1,18 +0,0 @@
.Docked {
content: " ";
display: inline-block;
width: 11px;
height: 11px;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
position: absolute;
z-index: 1;
overflow: hidden;
border-radius: 1px;
background-image: url(/images/citadelLarge.png);
left: 2px;
top: 22px;
transform: rotateZ(0deg);
}

View File

@@ -1,31 +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';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import classes from './Characters.module.scss';
import { isDocked } from '@/hooks/Mapper/helpers/isDocked.ts';
import { PrimeIcons } from 'primereact/api';
interface CharactersProps {
data: CharacterTypeRaw[];
}
export const Characters = ({ data }: CharactersProps) => {
const Characters = ({ data }: { data: CharacterTypeRaw[] }) => {
const [parent] = useAutoAnimate();
const { mapRef } = useMapRootState();
const {
data: { mainCharacterEveId, followingCharacterEveId },
} = 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
@@ -33,58 +22,21 @@ export const Characters = ({ data }: CharactersProps) => {
className="flex flex-col items-center justify-center"
onClick={() => handleSelect(character)}
>
<div
className={clsx(
'overflow-hidden relative',
'flex w-[35px] h-[35px] rounded-[4px] border-[1px] border-solid bg-transparent cursor-pointer',
'transition-colors duration-250',
{
['border-stone-800/90']: !character.online,
['border-lime-600/70']: character.online,
},
)}
title={character.name}
>
{mainCharacterEveId === character.eve_id && (
<span
className={clsx(
'absolute top-[2px] left-[22px] w-[9px] h-[9px]',
'text-yellow-500 text-[9px] rounded-[1px] z-10',
'pi',
PrimeIcons.STAR_FILL,
)}
/>
)}
{followingCharacterEveId === character.eve_id && (
<span
className={clsx(
'absolute top-[23px] left-[22px] w-[10px] h-[10px]',
'text-sky-300 text-[10px] rounded-[1px] z-10',
'pi pi-angle-double-right',
)}
/>
)}
{isDocked(character.location) && <div className={classes.Docked} />}
<div
className={clsx(
'flex w-full h-full bg-transparent cursor-pointer',
'bg-center bg-no-repeat bg-[length:100%]',
'transition-opacity',
'shadow-[inset_0_1px_6px_1px_#000000]',
{
['opacity-60']: !character.online,
['opacity-100']: character.online,
},
)}
<div className="tooltip tooltip-bottom" title={character.name}>
<a
className={clsx('wd-characters-icons wd-bg-default', { ['character-online']: character.online })}
style={{ backgroundImage: `url(https://images.evetech.net/characters/${character.eve_id}/portrait)` }}
></div>
></a>
</div>
</li>
));
return (
<ul className="flex gap-1 characters" id="characters" ref={parent}>
<ul className="flex characters" id="characters" ref={parent}>
{items}
</ul>
);
};
// eslint-disable-next-line react/display-name
export default Characters;

View File

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

View File

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

View File

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

View File

@@ -88,23 +88,6 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
setSystem(undefined);
}, []);
const onSystemTemporaryName = useCallback((temporaryName?: string) => {
const { system, outCommand } = ref.current;
if (!system) {
return;
}
outCommand({
type: OutCommand.updateSystemTemporaryName,
data: {
system_id: system,
value: temporaryName ?? '',
},
});
setSystem(undefined);
}, []);
const onSystemStatus = useCallback((status: number) => {
const { system, outCommand } = ref.current;
if (!system) {
@@ -178,7 +161,6 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
onLockToggle,
onHubToggle,
onSystemTag,
onSystemTemporaryName,
onSystemStatus,
onSystemLabels,
onOpenSettings,

View File

@@ -6,10 +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';
import { getSystemStaticInfo } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic';
export const useContextMenuSystemItems = ({
onDeleteSystem,
@@ -29,11 +25,9 @@ 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;
const systemStaticInfo = getSystemStaticInfo(systemId)!;
if (!system || !systemId) {
return [];
@@ -46,9 +40,7 @@ export const useContextMenuSystemItems = ({
return (
<FastSystemActions
systemId={systemId}
systemName={systemStaticInfo.solar_system_name}
regionName={systemStaticInfo.region_name}
isWH={isWormholeSpace(systemStaticInfo.system_class)}
systemName={system.system_static_info.solar_system_name}
showEdit
onOpenSettings={onOpenSettings}
/>
@@ -59,32 +51,26 @@ export const useContextMenuSystemItems = ({
getTags(),
getStatus(),
...getLabels(),
...getWaypointMenu(systemId, systemStaticInfo.system_class),
...getWaypointMenu(systemId, system.system_static_info.system_class),
{
label: !hubs.includes(systemId) ? 'Add in Routes' : 'Remove from Routes',
icon: PrimeIcons.MAP_MARKER,
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',
@@ -94,7 +80,6 @@ export const useContextMenuSystemItems = ({
]),
];
}, [
canLockSystem,
systems,
systemId,
getTags,

View File

@@ -10,7 +10,6 @@ import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/ty
import { FastSystemActions } from '@/hooks/Mapper/components/contexts/components';
import { useJumpPlannerMenu } from '@/hooks/Mapper/components/contexts/hooks';
import { Route } from '@/hooks/Mapper/types/routes.ts';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
export interface ContextMenuSystemInfoProps {
systemStatics: Map<number, SolarSystemStaticInfoRaw>;
@@ -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

@@ -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

@@ -4,8 +4,8 @@ import { useCallback } from 'react';
import { isPossibleSpace } from '@/hooks/Mapper/components/map/helpers/isKnownSpace.ts';
import { Route } from '@/hooks/Mapper/types/routes.ts';
import { SolarSystemRawType, SolarSystemStaticInfoRaw } from '@/hooks/Mapper/types';
import { getSystemById } from '@/hooks/Mapper/helpers';
import { SOLAR_SYSTEM_CLASS_IDS } from '@/hooks/Mapper/components/map/constants.ts';
import { getSystemStaticInfo } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic';
const imperialSpace = [SOLAR_SYSTEM_CLASS_IDS.hs, SOLAR_SYSTEM_CLASS_IDS.ls, SOLAR_SYSTEM_CLASS_IDS.ns];
const criminalSpace = [SOLAR_SYSTEM_CLASS_IDS.ls, SOLAR_SYSTEM_CLASS_IDS.ns];
@@ -47,7 +47,7 @@ export const useJumpPlannerMenu = (
return [];
}
const origin = getSystemStaticInfo(systemIdFrom);
const origin = getSystemById(systems, systemIdFrom)?.system_static_info;
if (!origin) {
return [];

View File

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

View File

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

View File

@@ -1,43 +0,0 @@
import { useState, useLayoutEffect, RefObject } from 'react';
/**
* useElementWidth
*
* A custom hook that accepts a ref to an HTML element and returns its current width.
* It uses a ResizeObserver and window resize listener to update the width when necessary.
*
* @param ref - A RefObject pointing to an HTML element.
* @returns The current width of the element.
*/
export function useElementWidth<T extends HTMLElement>(ref: RefObject<T>): number {
const [width, setWidth] = useState<number>(0);
useLayoutEffect(() => {
const updateWidth = () => {
if (ref.current) {
const newWidth = ref.current.getBoundingClientRect().width;
if (newWidth > 0) {
setWidth(newWidth);
}
}
};
updateWidth(); // Initial measurement
const observer = new ResizeObserver(() => {
const id = setTimeout(updateWidth, 100);
return () => clearTimeout(id);
});
if (ref.current) {
observer.observe(ref.current);
}
window.addEventListener("resize", updateWidth);
return () => {
observer.disconnect();
window.removeEventListener("resize", updateWidth);
};
}, [ref]);
return width;
}

View File

@@ -1,7 +1,7 @@
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useMemo } from 'react';
import { getSystemById } from '@/hooks/Mapper/helpers';
import { getSystemStaticInfo } from '../../mapRootProvider/hooks/useLoadSystemStatic';
import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts';
interface UseSystemInfoProps {
systemId: string;
@@ -12,8 +12,10 @@ export const useSystemInfo = ({ systemId }: UseSystemInfoProps) => {
data: { systems, connections },
} = useMapRootState();
const { systems: systemStatics } = useLoadSystemStatic({ systems: [systemId] });
return useMemo(() => {
const staticInfo = getSystemStaticInfo(parseInt(systemId));
const staticInfo = systemStatics.get(parseInt(systemId));
const dynamicInfo = getSystemById(systems, systemId);
if (!staticInfo || !dynamicInfo) {
@@ -27,5 +29,5 @@ export const useSystemInfo = ({ systemId }: UseSystemInfoProps) => {
.filter(x => x !== systemId);
return { dynamicInfo, staticInfo, leadsTo };
}, [systemId, systems, connections]);
}, [systemStatics, systemId, systems, connections]);
};

View File

@@ -1,11 +1,4 @@
.MapRoot {
width: 100%;
height: 100%;
background-color: var(--rf-bg-color, #0C0A09);
&.BackgroundAlternateColor {
background-color: var(--rf-soft-bg-color, #171717);
--rf-node-bg-color: var(--rf-node-soft-bg-color, #202020);
}
}

View File

@@ -1,38 +1,43 @@
import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect, useMemo } from 'react';
import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect, useRef } from 'react';
import ReactFlow, {
Background,
ConnectionMode,
Edge,
MiniMap,
Node,
NodeChange,
NodeDragHandler,
OnConnect,
OnMoveEnd,
OnSelectionChangeFunc,
SelectionDragHandler,
SelectionMode,
useEdgesState,
useNodesState,
NodeChange,
useReactFlow,
} from 'reactflow';
import 'reactflow/dist/style.css';
import classes from './Map.module.scss';
import './styles/neon-theme.scss';
import './styles/eve-common.scss';
import { MapProvider, useMapState } from './MapProvider';
import { useEdgesState, useMapHandlers, useNodesState, useUpdateNodes } from './hooks';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useMapHandlers, useUpdateNodes } from './hooks';
import { MapHandlers, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
import {
ContextMenuConnection,
ContextMenuRoot,
SolarSystemEdge,
SolarSystemNode,
useContextMenuConnectionHandlers,
useContextMenuRootHandlers,
} from './components';
import { getBehaviorForTheme } from './helpers/getThemeBehavior';
import { OnMapAddSystemCallback, OnMapSelectionChange } from './map.types';
import { OnMapSelectionChange } from './map.types';
import { SESSION_KEY } from '@/hooks/Mapper/constants.ts';
import { SolarSystemConnection, SolarSystemRawType } from '@/hooks/Mapper/types';
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
import { NodeSelectionMouseHandler } from '@/hooks/Mapper/components/contexts/types.ts';
import clsx from 'clsx';
import { useBackgroundVars } from './hooks/useBackgroundVars';
import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks';
const DEFAULT_VIEW_PORT = { zoom: 1, x: 0, y: 0 };
@@ -74,27 +79,26 @@ const initialEdges = [
},
];
const nodeTypes = {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
custom: SolarSystemNode,
} as never;
const edgeTypes = {
floating: SolarSystemEdge,
};
export const MAP_ROOT_ID = 'MAP_ROOT_ID';
interface MapCompProps {
refn: ForwardedRef<MapHandlers>;
onCommand: OutCommandHandler;
onSelectionChange: OnMapSelectionChange;
onConnectionInfoClick?(e: SolarSystemConnection): void;
onAddSystem?: OnMapAddSystemCallback;
onSelectionContextMenu?: NodeSelectionMouseHandler;
minimapClasses?: string;
isShowMinimap?: boolean;
onSystemContextMenu: (event: MouseEvent<Element>, systemId: string) => void;
showKSpaceBG?: boolean;
isThickConnections?: boolean;
isShowBackgroundPattern?: boolean;
isSoftBackground?: boolean;
theme?: string;
}
const MapComp = ({
@@ -107,29 +111,24 @@ const MapComp = ({
onSelectionContextMenu,
isShowMinimap,
showKSpaceBG,
isThickConnections,
isShowBackgroundPattern,
isSoftBackground,
theme,
onAddSystem,
}: MapCompProps) => {
const { getNodes } = useReactFlow();
const [nodes, , onNodesChange] = useNodesState<Node<SolarSystemRawType>>(initialNodes);
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>>(initialEdges);
const { getNode } = useReactFlow();
const [nodes, , onNodesChange] = useNodesState<SolarSystemRawType>(initialNodes);
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>[]>(initialEdges);
useMapHandlers(refn, onSelectionChange);
useUpdateNodes(nodes);
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers({ onAddSystem });
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers();
const { handleConnectionContext, ...connectionCtxProps } = useContextMenuConnectionHandlers();
const { update } = useMapState();
const { variant, gap, size, color } = useBackgroundVars(theme);
const { isPanAndDrag, nodeComponent, connectionMode } = getBehaviorForTheme(theme || 'default');
const {
data: { systems },
} = useMapRootState();
const nodeTypes = useMemo(() => {
return {
custom: nodeComponent,
};
}, [nodeComponent]);
const { deleteSystems } = useDeleteSystems();
const systemsRef = useRef({ systems });
systemsRef.current = { systems };
const onConnect: OnConnect = useCallback(
params => {
@@ -186,35 +185,40 @@ const MapComp = ({
const handleNodesChange = useCallback(
(changes: NodeChange[]) => {
// prevents single node deselection on background / same node click
// allows deseletion of all nodes if multiple are currently selected
if (changes.length === 1 && changes[0].type == 'select' && changes[0].selected === false) {
changes[0].selected = getNodes().filter(node => node.selected).length === 1;
}
const systemsIdsToRemove: string[] = [];
const nextChanges = changes.reduce((acc, change) => {
if (change.type === 'remove') {
const node = getNode(change.id);
const { systems = [] } = systemsRef.current;
if (node?.data?.id && !systems.map(s => s.id).includes(node?.data?.id)) {
return [...acc, change];
} else if (!node?.data?.locked) {
systemsIdsToRemove.push(node?.data?.id);
}
return acc;
}
return [...acc, change];
}, [] as NodeChange[]);
if (systemsIdsToRemove.length) {
deleteSystems(systemsIdsToRemove);
}
onNodesChange(nextChanges);
},
[getNodes, onNodesChange],
[deleteSystems, getNode, onNodesChange],
);
useEffect(() => {
update(x => ({
...x,
showKSpaceBG: showKSpaceBG,
isThickConnections: isThickConnections,
}));
}, [showKSpaceBG, isThickConnections, update]);
}, [showKSpaceBG, update]);
return (
<>
<div
data-window-id={MAP_ROOT_ID}
className={clsx(classes.MapRoot, { [classes.BackgroundAlternateColor]: isSoftBackground })}
>
<div className={classes.MapRoot}>
<ReactFlow
nodes={nodes}
edges={edges}
@@ -226,7 +230,7 @@ const MapComp = ({
defaultViewport={getViewPortFromStore()}
edgeTypes={edgeTypes}
nodeTypes={nodeTypes}
connectionMode={connectionMode}
connectionMode={ConnectionMode.Loose}
snapToGrid
nodeDragThreshold={10}
onNodeDragStop={handleDragStop}
@@ -234,11 +238,6 @@ const MapComp = ({
onConnectStart={() => update({ isConnecting: true })}
onConnectEnd={() => update({ isConnecting: false })}
onNodeMouseEnter={(_, node) => update({ hoverNodeId: node.id })}
onPaneClick={event => {
event.preventDefault();
event.stopPropagation();
}}
// onKeyUp=
onNodeMouseLeave={() => update({ hoverNodeId: null })}
onEdgeClick={(_, t) => {
onConnectionInfoClick?.(t.data);
@@ -258,20 +257,14 @@ const MapComp = ({
minZoom={0.2}
maxZoom={1.5}
elevateNodesOnSelect
deleteKeyCode={['']}
{...(isPanAndDrag
? {
selectionOnDrag: true,
panOnDrag: [2],
}
: {})}
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
selectionMode={SelectionMode.Partial}
>
{isShowMinimap && <MiniMap pannable zoomable ariaLabel="Mini map" className={minimapClasses} />}
{isShowBackgroundPattern && <Background variant={variant} gap={gap} size={size} color={color} />}
<Background />
</ReactFlow>
{/* <button className="z-auto btn btn-primary absolute top-20 right-20" onClick={handleGetPassages}>
Test // DON NOT REMOVE

View File

@@ -1,6 +1,6 @@
import React, { createContext, useContext } from 'react';
import { OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
import { MapUnionTypes, SystemSignature } from '@/hooks/Mapper/types';
import { MapUnionTypes } from '@/hooks/Mapper/types';
import { ContextStoreDataUpdate, useContextStore } from '@/hooks/Mapper/utils';
export type MapData = MapUnionTypes & {
@@ -8,8 +8,6 @@ export type MapData = MapUnionTypes & {
hoverNodeId: string | null;
visibleNodes: Set<string>;
showKSpaceBG: boolean;
isThickConnections: boolean;
linkedSigEveId: string;
};
interface MapProviderProps {
@@ -19,7 +17,6 @@ interface MapProviderProps {
const INITIAL_DATA: MapData = {
wormholesData: {},
wormholes: [],
effects: {},
characters: [],
userCharacters: [],
@@ -30,14 +27,8 @@ const INITIAL_DATA: MapData = {
isConnecting: false,
connections: [],
hoverNodeId: null,
linkedSigEveId: '',
visibleNodes: new Set(),
showKSpaceBG: false,
isThickConnections: false,
userPermissions: {},
systemSignatures: {} as Record<string, SystemSignature[]>,
options: {} as Record<string, string | boolean>,
isSubscriptionActive: false,
};
export interface MapContextProps {
@@ -53,7 +44,7 @@ const MapContext = createContext<MapContextProps>({
outCommand: async () => void 0,
});
export const MapProvider = ({ children, onCommand }: MapProviderProps) => {
export const MapProvider: React.FC<MapProviderProps> = ({ children, onCommand }) => {
const { update, ref } = useContextStore<MapData>({ ...INITIAL_DATA });
return (

View File

@@ -1,17 +1,15 @@
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
.ConnectionTimeEOL {
background-image: linear-gradient(207deg, transparent, var(--conn-time-eol));
background-image: linear-gradient(207deg, transparent, #7452c3e3);
}
.ConnectionFrigate {
background-image: linear-gradient(207deg, transparent, var(--conn-frigate));
background-image: linear-gradient(207deg, transparent, #325d88);
}
.ConnectionSave {
background-image: linear-gradient(207deg, transparent, var(--conn-save));
background-image: linear-gradient(207deg, transparent, rgba(155, 102, 45, 0.85));
}
.SelectedItem {
background-color: var(--selected-item-bg);
background-color: rgba(98, 98, 98, 0.33);
}

View File

@@ -2,18 +2,11 @@ import React, { RefObject, useMemo } from 'react';
import { ContextMenu } from 'primereact/contextmenu';
import { PrimeIcons } from 'primereact/api';
import { MenuItem } from 'primereact/menuitem';
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
import { Edge } from '@reactflow/core/dist/esm/types/edges';
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,
SHIP_SIZES_NAMES,
SHIP_SIZES_NAMES_ORDER,
SHIP_SIZES_NAMES_SHORT,
SHIP_SIZES_SIZE,
} from '@/hooks/Mapper/components/map/constants.ts';
import { Edge } from 'reactflow';
import { MASS_STATE_NAMES, MASS_STATE_NAMES_ORDER } from '@/hooks/Mapper/components/map/constants.ts';
export interface ContextMenuConnectionProps {
contextMenuRef: RefObject<ContextMenu>;
@@ -42,72 +35,46 @@ 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,
},
{
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: `EOL`,
className: clsx({
[classes.ConnectionTimeEOL]: edge.data?.time_status === TimeStatus.eol,
}),
icon: PrimeIcons.CLOCK,
command: onChangeTimeState,
},
{
label: `Frigate`,
className: clsx({
[classes.ConnectionFrigate]: isFrigateSize,
}),
icon: PrimeIcons.CLOUD,
command: () =>
onChangeShipSizeStatus(
edge.data?.ship_size_type === ShipSizeStatus.small ? ShipSizeStatus.large : ShipSizeStatus.small,
),
},
{
label: `Save mass`,
className: clsx({
[classes.ConnectionSave]: edge.data?.locked,
}),
icon: PrimeIcons.LOCK,
command: () => onToggleMassSave(!edge.data?.locked),
},
...(!isFrigateSize
? [
{
label: `Mass status`,
icon: PrimeIcons.CHART_PIE,
items: MASS_STATE_NAMES_ORDER.map(x => ({
label: MASS_STATE_NAMES[x],
className: clsx({
[classes.SelectedItem]: edge.data?.mass_status === x,
}),
command: () => onChangeMassState(x),
})),
},
]
: []),
{
label: `Ship Size`,
icon: PrimeIcons.CLOUD,
items: SHIP_SIZES_NAMES_ORDER.map(x => ({
label: (
<div className="grid grid-cols-[20px_120px_1fr_40px] gap-2 items-center">
<div className="text-[12px] font-bold text-stone-400">{SHIP_SIZES_NAMES_SHORT[x]}</div>
<div>{SHIP_SIZES_NAMES[x]}</div>
<div></div>
<div className="flex justify-end whitespace-nowrap text-[12px] font-bold text-stone-500">
{SHIP_SIZES_SIZE[x]} t.
</div>
</div>
) as unknown as string, // TODO my lovely kostyl
label: `Mass status`,
icon: PrimeIcons.CHART_PIE,
items: MASS_STATE_NAMES_ORDER.map(x => ({
label: MASS_STATE_NAMES[x],
className: clsx({
[classes.SelectedItem]: edge.data?.ship_size_type === x,
[classes.SelectedItem]: edge.data?.mass_status === x,
}),
command: () => onChangeShipSizeStatus(x),
command: () => onChangeMassState(x),
})),
},
]
@@ -118,7 +85,7 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
command: onDeleteConnection,
},
];
}, [edge, onChangeTimeState, onDeleteConnection, onChangeShipSizeStatus, onToggleMassSave, onChangeMassState]);
}, [edge, onChangeTimeState, onDeleteConnection, onChangeMassState, onChangeShipSizeStatus]);
return (
<>

View File

@@ -1,9 +1,10 @@
import { Edge, EdgeMouseHandler } from 'reactflow';
import { EdgeMouseHandler } from 'reactflow';
import { useCallback, useRef, useState } from 'react';
import { ContextMenu } from 'primereact/contextmenu';
import { useMapState } from '../../MapProvider.tsx';
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
import { Edge } from '@reactflow/core/dist/esm/types/edges';
import { MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
export const useContextMenuConnectionHandlers = () => {
@@ -46,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;
@@ -96,16 +80,14 @@ export const useContextMenuConnectionHandlers = () => {
},
});
if (status === ShipSizeStatus.small) {
outCommand({
type: OutCommand.updateConnectionMassStatus,
data: {
source: edge.source,
target: edge.target,
value: MassState.normal,
},
});
}
outCommand({
type: OutCommand.updateConnectionMassStatus,
data: {
source: edge.source,
target: edge.target,
value: MassState.normal,
},
});
}, []);
const onToggleMassSave = useCallback((locked: boolean) => {
@@ -136,7 +118,6 @@ export const useContextMenuConnectionHandlers = () => {
contextMenuRef,
onDeleteConnection,
onChangeTimeState,
onChangeType,
onChangeMassState,
onChangeShipSizeStatus,
onToggleMassSave,

View File

@@ -1,16 +1,14 @@
import { useReactFlow, XYPosition } from 'reactflow';
import React, { useCallback, useRef, useState } from 'react';
import React, { useRef, useState } from 'react';
import { ContextMenu } from 'primereact/contextmenu';
import { useMapState } from '../../MapProvider.tsx';
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
import { OnMapAddSystemCallback } from '@/hooks/Mapper/components/map/map.types.ts';
type UseContextMenuRootHandlers = {
onAddSystem?: OnMapAddSystemCallback;
};
export const useContextMenuRootHandlers = ({ onAddSystem }: UseContextMenuRootHandlers = {}) => {
export const useContextMenuRootHandlers = () => {
const rf = useReactFlow();
const contextMenuRef = useRef<ContextMenu | null>(null);
const { outCommand } = useMapState();
const [position, setPosition] = useState<XYPosition | null>(null);
const handleRootContext = (e: React.MouseEvent<HTMLDivElement>) => {
@@ -20,17 +18,14 @@ export const useContextMenuRootHandlers = ({ onAddSystem }: UseContextMenuRootHa
contextMenuRef.current?.show(e);
};
const ref = useRef({ onAddSystem, position });
ref.current = { onAddSystem, position };
const onAddSystemCallback = useCallback(() => {
ref.current.onAddSystem?.({ coordinates: position });
}, [position]);
const onAddSystem = () => {
outCommand({ type: OutCommand.manualAddSystem, data: { coordinates: position } });
};
return {
handleRootContext,
contextMenuRef,
onAddSystem: onAddSystemCallback,
onAddSystem,
};
};

View File

@@ -1,55 +1,27 @@
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
@import "@/hooks/Mapper/components/map/styles/neon-variables";
.EdgePathBack {
fill: none;
stroke: #80a5c5;
stroke-width: 3px;
&.TimeCrit {
stroke: #f11ab2;
stroke-width: 4px;
}
&.Hovered {
stroke: #b5c8d9;
&.TimeCrit {
stroke: #ef7dce;
}
}
&.Tick {
stroke-width: 5px;
&.TimeCrit {
stroke-width: 6px;
}
}
&.Gate {
stroke: #9aff40;
.react-flow__edge.selected {
.EdgePathBack {
stroke: $pastel-yellow;
}
}
.EdgePathFront {
fill: none;
stroke: #2c3844;
stroke-width: 2px;
&.MassVerge:not(&.Frigate) {
stroke: #af0000;
stroke: #af2900;
}
&.MassHalf:not(&.Frigate) {
stroke: #ffd700;
stroke: #a85f00;
}
&.Frigate {
stroke: #d4f0ff;
}
&.Gate {
stroke: #1c1e15;
stroke: #4e62c9;
}
&.Hovered {
@@ -65,15 +37,28 @@
}
&.Frigate {
stroke: #d4f0ff;
stroke: #41acd7;
}
}
}
.EdgePathBack {
fill: none;
stroke: #80a5c5;
stroke-width: 3px;
&.TimeCrit {
stroke: #f11ab2;
stroke-width: 4px;
}
&.Tick {
stroke-width: 3px;
&.Hovered {
stroke: #b5c8d9;
&.Hovered {
stroke-width: 3px;
&.TimeCrit {
stroke: #ef7dce;
}
}
}
@@ -84,23 +69,7 @@
stroke-width: 8px;
}
.Handle {
border: 1px solid var(--pastel-blue);
width: 5px;
height: 5px;
z-index: 1001;
&.Tick {
width: 7px;
height: 7px;
}
&.Right {
margin-left: 0px;
}
}
.LinkLabel {
.LinkLabel{
font-size: 9px;
line-height: 10px;
padding: 2px 4px;
@@ -116,3 +85,13 @@
height: 8px;
font-size: 8px;
}
.Handle {
min-width: initial;
min-height: initial;
border: 1px solid #5a7d9a;
width: 5px;
height: 5px;
z-index: 1001;
}

View File

@@ -1,73 +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';
import { SHIP_SIZES_DESCRIPTION, SHIP_SIZES_NAMES_SHORT } from '@/hooks/Mapper/components/map/constants.ts';
const MAP_TRANSLATES: Record<string, string> = {
[Position.Top]: 'translate(-48%, 0%)',
[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 SHIP_SIZES_COLORS = {
[ShipSizeStatus.small]: 'bg-indigo-400',
[ShipSizeStatus.medium]: 'bg-cyan-500',
[ShipSizeStatus.large]: '',
[ShipSizeStatus.freight]: 'bg-lime-400',
[ShipSizeStatus.capital]: 'bg-red-400',
};
export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }: EdgeProps<SolarSystemConnection>) => {
const sourceNode = useStore(useCallback(store => store.nodeInternals.get(source), [source]));
const targetNode = useStore(useCallback(store => store.nodeInternals.get(target), [target]));
const isWormhole = data?.type !== ConnectionType.gate;
const {
data: { isThickConnections },
} = 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;
@@ -78,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}
@@ -90,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}
@@ -113,29 +75,21 @@ 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)` }}
/>
<div
className="absolute flex items-center gap-1 pointer-events-none"
className="absolute flex items-center gap-1"
style={{
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
}}
>
{isWormhole && data.locked && (
{data.locked && (
<WdTooltipWrapper
content="Save mass"
className={clsx(
@@ -146,19 +100,6 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
<span className={clsx(PrimeIcons.LOCK, classes.icon)} />
</WdTooltipWrapper>
)}
{isWormhole && data.ship_size_type !== ShipSizeStatus.large && (
<WdTooltipWrapper
content={SHIP_SIZES_DESCRIPTION[data.ship_size_type]}
className={clsx(
classes.LinkLabel,
'pointer-events-auto rounded opacity-100 cursor-auto text-neutral-900 font-bold',
SHIP_SIZES_COLORS[data.ship_size_type],
)}
>
{SHIP_SIZES_NAMES_SHORT[data.ship_size_type]}
</WdTooltipWrapper>
)}
</div>
</EdgeLabelRenderer>
</>

View File

@@ -1,48 +0,0 @@
.KillsBookmark {
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 8px;
font-weight: 700;
border: 0;
border-radius: 5px 5px 0 0;
padding: 4px 3px;
}
.KillsBookmarkWithIcon {
display: flex;
align-items: center;
justify-content: center;
margin-top: -2px;
text-shadow: 0 0 3px #000;
padding-right: 2px;
height: 8px;
font-size: 8px;
line-height: 12px;
font-weight: 700;
text-size-adjust: 100%;
.pi {
font-size: 9px;
}
.text {
font-size: 9px;
font-family: var(--rf-node-font-family, inherit) !important;
font-weight: var(--rf-node-font-weight, inherit) !important;
}
}
.TooltipContainer {
background-color: #1a1a1a;
color: #fff;
padding: 3px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
border-radius: 2px;
pointer-events: auto;
max-width: 500px;
max-height: 300px;
overflow-y: auto;
overflow-x: hidden;
}

View File

@@ -1,68 +0,0 @@
import { useMemo } from 'react';
import { useKillsCounter } from '../../hooks/useKillsCounter';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import { WithChildren, WithClassName } from '@/hooks/Mapper/types/common';
import {
KILLS_ROW_HEIGHT,
SystemKillsList,
} from '@/hooks/Mapper/components/mapInterface/widgets/WSystemKills/SystemKillsList';
import { TooltipSize } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper/utils.ts';
const MIN_TOOLTIP_HEIGHT = 40;
type KillsBookmarkTooltipProps = {
killsCount: number;
killsActivityType: string | null;
systemId: string;
className?: string;
size?: TooltipSize;
} & WithChildren &
WithClassName;
export const KillsCounter = ({
killsCount,
systemId,
className,
children,
size = TooltipSize.xs,
}: KillsBookmarkTooltipProps) => {
const {
isLoading,
kills: detailedKills,
systemNameMap,
} = useKillsCounter({
realSystemId: systemId,
});
const limitedKills = useMemo(() => {
if (!detailedKills || detailedKills.length === 0) return [];
return detailedKills.slice(0, killsCount);
}, [detailedKills, killsCount]);
if (!killsCount || limitedKills.length === 0 || !systemId || isLoading) {
return null;
}
// Calculate height based on number of kills, but ensure a minimum height
const killsNeededHeight = limitedKills.length * KILLS_ROW_HEIGHT;
// Add a small buffer (10px) to prevent scrollbar from appearing unnecessarily
const tooltipHeight = Math.max(MIN_TOOLTIP_HEIGHT, Math.min(killsNeededHeight + 10, 500));
return (
<WdTooltipWrapper
content={
<div className="overflow-hidden flex w-[450px] flex-col" style={{ height: `${tooltipHeight}px` }}>
<div className="flex-1 h-full">
<SystemKillsList kills={limitedKills} onlyOneSystem />
</div>
</div>
}
className={className}
tooltipClassName="!px-0"
size={size}
interactive={true}
>
{children}
</WdTooltipWrapper>
);
};

View File

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

View File

@@ -1,61 +0,0 @@
import { useMemo } from 'react';
import clsx from 'clsx';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit/WdTooltip';
import { CharItemProps, LocalCharactersList } from '../../../mapInterface/widgets/LocalCharacters/components';
import { useLocalCharactersItemTemplate } from '../../../mapInterface/widgets/LocalCharacters/hooks/useLocalCharacters';
import { useLocalCharacterWidgetSettings } from '../../../mapInterface/widgets/LocalCharacters/hooks/useLocalWidgetSettings';
import classes from './SolarSystemLocalCounter.module.scss';
import { AvailableThemes } from '@/hooks/Mapper/mapRootProvider';
import { useTheme } from '@/hooks/Mapper/hooks/useTheme.ts';
interface LocalCounterProps {
localCounterCharacters: Array<CharItemProps>;
hasUserCharacters: boolean;
showIcon?: boolean;
}
export const LocalCounter = ({ localCounterCharacters, hasUserCharacters, showIcon = true }: LocalCounterProps) => {
const [settings] = useLocalCharacterWidgetSettings();
const itemTemplate = useLocalCharactersItemTemplate(settings.showShipName);
const theme = useTheme();
const pilotTooltipContent = useMemo(() => {
return (
<div
style={{
width: '100%',
minWidth: '300px',
overflow: 'hidden',
}}
>
<LocalCharactersList items={localCounterCharacters} itemTemplate={itemTemplate} itemSize={26} autoSize={true} />
</div>
);
}, [localCounterCharacters, itemTemplate]);
if (localCounterCharacters.length === 0) {
return null;
}
return (
<div
className={clsx(classes.TooltipActive, {
[classes.Pathfinder]: theme === AvailableThemes.pathfinder,
})}
>
<WdTooltipWrapper content={pilotTooltipContent} position={TooltipPosition.right} offset={0} interactive={true}>
<div className={clsx(classes.hoverTarget)}>
<div
className={clsx(classes.localCounter, {
[classes.hasUserCharacters]: hasUserCharacters,
})}
>
{showIcon && <i className="pi pi-users" />}
<span>{localCounterCharacters.length}</span>
</div>
</div>
</WdTooltipWrapper>
</div>
);
};

View File

@@ -2,30 +2,27 @@
$pastel-blue: #5a7d9a;
$pastel-pink: #d291bc;
$pastel-green: #88b04b;
$pastel-yellow: #ffdd59;
$dark-bg: #2d2d2d;
$text-color: #ffffff;
$tooltip-bg: #202020;
$tooltip-bg: #202020; // Темный фон для подсказок
.RootCustomNode {
display: flex;
width: 130px;
height: 34px;
font-family: var(--rf-node-font-family, inherit) !important;
font-weight: var(--rf-node-font-weight, inherit) !important;
flex-direction: column;
padding: 2px 6px;
font-size: 10px;
background-color: var(--rf-node-bg-color, #202020) !important;
color: var(--rf-text-color, #ffffff);
background-color: $tooltip-bg;
box-shadow: 0 0 5px rgba($dark-bg, 0.5);
border: 1px solid darken($pastel-blue, 10%);
border-radius: 5px;
position: relative;
z-index: 3;
z-index: 1;
overflow: hidden;
&.Mataria,
@@ -88,47 +85,58 @@ $tooltip-bg: #202020;
box-shadow: 0 0 10px #9a1af1c2;
}
.tooltip {
background-color: $tooltip-bg;
color: $text-color;
padding: 5px 10px;
border-radius: 3px;
border: 1px solid $pastel-pink;
}
&.eve-system-status-home {
border: 1px solid var(--eve-solar-system-status-color-home-dark30);
background-image: linear-gradient(45deg, var(--eve-solar-system-status-color-background), transparent);
border: 1px solid darken($eve-solar-system-status-color-home, 30%);
background-image: linear-gradient(275deg, $eve-solar-system-status-friendly, transparent);
&.selected {
border-color: var(--eve-solar-system-status-color-home);
border-color: $eve-solar-system-status-color-home;
}
}
&.eve-system-status-friendly {
border: 1px solid var(--eve-solar-system-status-color-friendly-dark20);
background-image: linear-gradient(275deg, var(--eve-solar-system-status-friendly-dark30), transparent);
border: 1px solid darken($eve-solar-system-status-color-friendly, 20%);
background-image: linear-gradient(275deg, darken($eve-solar-system-status-friendly, 30%), transparent);
&.selected {
border-color: var(--eve-solar-system-status-color-friendly-dark5);
border-color: darken($eve-solar-system-status-color-friendly, 5%);
}
}
&.eve-system-status-lookingFor {
border: 1px solid var(--eve-solar-system-status-color-lookingFor-dark15);
border: 1px solid darken($eve-solar-system-status-color-lookingFor, 15%);
background-image: linear-gradient(275deg, #45ff8f2f, #457fff2f);
&.selected {
border-color: $pastel-pink;
}
}
&.eve-system-status-warning {
background-image: linear-gradient(275deg, var(--eve-solar-system-status-warning), transparent);
background-image: linear-gradient(275deg, $eve-solar-system-status-warning, transparent);
}
&.eve-system-status-dangerous {
background-image: linear-gradient(275deg, var(--eve-solar-system-status-dangerous), transparent);
background-image: linear-gradient(275deg, $eve-solar-system-status-dangerous, transparent);
}
&.eve-system-status-target {
background-image: linear-gradient(275deg, var(--eve-solar-system-status-target), transparent);
background-image: linear-gradient(275deg, $eve-solar-system-status-target, transparent);
}
}
.Bookmarks {
position: absolute;
width: 100%;
z-index: 1;
z-index: 0;
display: flex;
left: 4px;
@@ -146,6 +154,8 @@ $tooltip-bg: #202020;
padding-left: 3px;
padding-right: 3px;
//background-color: #833ca4;
&:not(:first-child) {
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
}
@@ -172,42 +182,6 @@ $tooltip-bg: #202020;
}
}
.Unsplashed {
position: absolute;
width: calc(50% - 4px);
z-index: -1;
display: flex;
flex-wrap: wrap;
gap: 2px;
left: 2px;
&--right {
left: calc(50% + 6px);
}
& > .Signature {
width: 13px;
height: 4px;
position: relative;
top: 3px;
border-radius: 5px;
color: #ffffff;
font-size: 8px;
text-align: center;
padding-top: 2px;
font-weight: bolder;
padding-left: 3px;
padding-right: 3px;
display: block;
background-color: #833ca4;
&:not(:first-child) {
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
}
}
}
.icon {
width: 8px;
height: 8px;
@@ -232,16 +206,17 @@ $tooltip-bg: #202020;
.TagTitle {
font-size: 11px;
font-weight: 500;
font-weight: bold;
text-shadow: 0 0 2px rgba(231, 146, 52, 0.73);
color: var(--rf-tag-color, #38bdf8);
color: #ffb01d;
}
/* Firefox kostyl */
@-moz-document url-prefix() {
.classSystemName {
font-weight: bold;
}
.classSystemName {
//font-weight: bold;
}
.solarSystemName {
}
}
@@ -251,23 +226,22 @@ $tooltip-bg: #202020;
align-items: center;
height: 19px;
.hasLocalCounter {
margin-right: 2px;
&.countAbove9 {
margin-right: 1.5rem;
.localCounter {
display: flex;
//align-items: center;
gap: 2px;
& > i {
position: relative;
top: 1px;
}
}
.lockIcon {
font-size: 0.45rem;
font-weight: bold;
position: relative;
}
.mapMarker {
font-size: 0.45rem;
font-weight: bold;
position: relative;
& > span {
font-size: 9px;
line-height: 9px;
font-weight: 500;
//margin-top: 1px;
}
}
}
@@ -288,18 +262,11 @@ $tooltip-bg: #202020;
& > * {
line-height: 10px;
}
/* Firefox kostyl */
@-moz-document url-prefix() {
position: relative;
top: -1px;
}
}
.Handlers {
position: absolute;
z-index: 4;
pointer-events: none;
z-index: 2;
top: 0;
left: 0;
width: 100%;
@@ -312,7 +279,6 @@ $tooltip-bg: #202020;
border: 1px solid $pastel-blue;
width: 5px;
height: 5px;
pointer-events: auto;
&.selected {
border-color: $pastel-pink;
@@ -333,37 +299,4 @@ $tooltip-bg: #202020;
&.HandleLeft {
left: -2px;
}
&.Tick {
width: 7px;
height: 7px;
&.HandleTop {
top: -3px;
}
&.HandleRight {
right: -3px;
}
&.HandleBottom {
bottom: -3px;
}
&.HandleLeft {
left: -3px;
}
}
}
.ShatteredIcon {
position: relative;
//top: -1px;
left: -1px;
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
background-image: url(/images/chart-network-svgrepo-com.svg)
}

View File

@@ -0,0 +1,271 @@
import { memo, useMemo } from 'react';
import { Handle, Position, WrapNodeProps } from 'reactflow';
import { MapSolarSystemType } from '../../map.types';
import classes from './SolarSystemNode.module.scss';
import clsx from 'clsx';
import {
EFFECT_BACKGROUND_STYLES,
LABELS_INFO,
LABELS_ORDER,
MARKER_BOOKMARK_BG_STYLES,
STATUS_CLASSES,
} from '@/hooks/Mapper/components/map/constants.ts';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
import { getSystemClassStyles } from '@/hooks/Mapper/components/map/helpers';
import { sortWHClasses } from '@/hooks/Mapper/helpers';
import { PrimeIcons } from 'primereact/api';
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager.ts';
import { OutCommand } from '@/hooks/Mapper/types';
import { useDoubleClick } from '@/hooks/Mapper/hooks/useDoubleClick.ts';
import { REGIONS_MAP, Spaces } from '@/hooks/Mapper/constants';
const SpaceToClass: Record<string, string> = {
[Spaces.Caldari]: classes.Caldaria,
[Spaces.Matar]: classes.Mataria,
[Spaces.Amarr]: classes.Amarria,
[Spaces.Gallente]: classes.Gallente,
};
const sortedLabels = (labels: string[]) => {
if (!labels) {
return [];
}
return LABELS_ORDER.filter(x => labels.includes(x)).map(x => LABELS_INFO[x]);
};
export const getActivityType = (count: number) => {
if (count <= 5) {
return 'activityNormal';
}
if (count <= 30) {
return 'activityWarn';
}
return 'activityDanger';
};
// eslint-disable-next-line react/display-name
export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarSystemType>) => {
const {
system_class,
security,
class_title,
solar_system_id,
statics,
effect_name,
region_name,
region_id,
is_shattered,
solar_system_name,
} = data.system_static_info;
const { locked, name, tag, status, labels, id } = data || {};
const customName = solar_system_name !== name ? name : undefined;
const {
data: {
characters,
presentCharacters,
wormholesData,
hubs,
kills,
userCharacters,
isConnecting,
hoverNodeId,
visibleNodes,
showKSpaceBG,
},
outCommand,
} = useMapState();
const visible = useMemo(() => visibleNodes.has(id), [id, visibleNodes]);
const charactersInSystem = useMemo(() => {
return characters.filter(c => c.location?.solar_system_id === solar_system_id).filter(c => c.online);
// eslint-disable-next-line
}, [characters, presentCharacters, solar_system_id]);
const isWormhole = isWormholeSpace(system_class);
const classTitleColor = useMemo(
() => getSystemClassStyles({ systemClass: system_class, security }),
[security, system_class],
);
const sortedStatics = useMemo(() => sortWHClasses(wormholesData, statics), [wormholesData, statics]);
const lebM = useMemo(() => new LabelsManager(labels ?? ''), [labels]);
const labelsInfo = useMemo(() => sortedLabels(lebM.list), [lebM]);
const labelCustom = useMemo(() => lebM.customLabel, [lebM]);
const killsCount = useMemo(() => {
const systemKills = kills[solar_system_id];
if (!systemKills) {
return null;
}
return systemKills;
}, [kills, solar_system_id]);
const hasUserCharacters = useMemo(() => {
return charactersInSystem.some(x => userCharacters.includes(x.eve_id));
}, [charactersInSystem, userCharacters]);
const dbClick = useDoubleClick(() => {
outCommand({
type: OutCommand.openSettings,
data: {
system_id: solar_system_id.toString(),
},
});
});
const showHandlers = isConnecting || hoverNodeId === id;
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
const regionClass = showKSpaceBG ? SpaceToClass[space] : null;
return (
<>
{visible && (
<div className={classes.Bookmarks}>
{labelCustom !== '' && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] ">{labelCustom}</span>
</div>
)}
{is_shattered && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered)}>
<span className={clsx('pi pi-chart-pie', classes.icon)} />
</div>
)}
{killsCount && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[getActivityType(killsCount)])}>
<div className={clsx(classes.BookmarkWithIcon)}>
<span className={clsx(PrimeIcons.BOLT, classes.icon)} />
<span className={clsx(classes.text)}>{killsCount}</span>
</div>
</div>
)}
{labelsInfo.map(x => (
<div key={x.id} className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[x.id])}>
{x.shortName}
</div>
))}
</div>
)}
<div
className={clsx(classes.RootCustomNode, regionClass, classes[STATUS_CLASSES[status]], {
[classes.selected]: selected,
})}
>
{visible && (
<>
<div className={classes.HeadRow}>
<div className={clsx(classes.classTitle, classTitleColor, '[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]')}>
{class_title ?? '-'}
</div>
{tag != null && tag !== '' && (
<div className={clsx(classes.TagTitle, 'text-sky-400 font-medium')}>{tag}</div>
)}
<div
className={clsx(
classes.classSystemName,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans',
)}
>
{solar_system_name}
</div>
{isWormhole && (
<div className={classes.statics}>
{sortedStatics.map(x => (
<WormholeClassComp key={x} id={x} />
))}
</div>
)}
{effect_name !== null && isWormhole && (
<div className={clsx(classes.effect, EFFECT_BACKGROUND_STYLES[effect_name])}></div>
)}
</div>
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
{customName && (
<div className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-blue-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5">
{customName}
</div>
)}
{!isWormhole && !customName && (
<div
className={clsx(
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-stone-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5',
)}
>
{region_name}
</div>
)}
{isWormhole && !customName && <div />}
<div className="flex items-center justify-end">
<div className="flex gap-1 items-center">
{locked && <i className={PrimeIcons.LOCK} style={{ fontSize: '0.45rem', fontWeight: 'bold' }}></i>}
{hubs.includes(solar_system_id.toString()) && (
<i className={PrimeIcons.MAP_MARKER} style={{ fontSize: '0.45rem', fontWeight: 'bold' }}></i>
)}
{charactersInSystem.length > 0 && (
<div className={clsx(classes.localCounter, { ['text-amber-300']: hasUserCharacters })}>
<i className="pi pi-users" style={{ fontSize: '0.50rem' }}></i>
<span className="font-sans">{charactersInSystem.length}</span>
</div>
)}
</div>
</div>
</div>
</>
)}
</div>
<div onMouseDownCapture={dbClick} className={classes.Handlers}>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleTop, { [classes.selected]: selected })}
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
position={Position.Top}
id="a"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleRight, { [classes.selected]: selected })}
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
position={Position.Right}
id="b"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleBottom, { [classes.selected]: selected })}
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
position={Position.Bottom}
id="c"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleLeft, { [classes.selected]: selected })}
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
position={Position.Left}
id="d"
/>
</div>
</>
);
});

View File

@@ -1,213 +0,0 @@
import { memo } from 'react';
import { MapSolarSystemType } from '../../map.types';
import { Handle, NodeProps, Position } from 'reactflow';
import clsx from 'clsx';
import classes from './SolarSystemNodeDefault.module.scss';
import { PrimeIcons } from 'primereact/api';
import { useLocalCounter, useNodeKillsCount, useSolarSystemNode } from '../../hooks';
import {
EFFECT_BACKGROUND_STYLES,
MARKER_BOOKMARK_BG_STYLES,
STATUS_CLASSES,
} from '@/hooks/Mapper/components/map/constants';
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
import { LocalCounter } from './SolarSystemLocalCounter';
import { KillsCounter } from './SolarSystemKillsCounter';
import { TooltipSize } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper/utils.ts';
import { TooltipPosition, WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>) => {
const nodeVars = useSolarSystemNode(props);
const { localCounterCharacters } = useLocalCounter(nodeVars);
const localKillsCount = useNodeKillsCount(nodeVars.solarSystemId, nodeVars.killsCount);
return (
<>
{nodeVars.visible && (
<div className={classes.Bookmarks}>
{nodeVars.labelCustom !== '' && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]">{nodeVars.labelCustom}</span>
</div>
)}
{nodeVars.isShattered && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered, '!pr-[2px]')}>
<WdTooltipWrapper content="Shattered" position={TooltipPosition.top}>
<span className={clsx('block w-[10px] h-[10px]', classes.ShatteredIcon)} />
</WdTooltipWrapper>
</div>
)}
{localKillsCount && localKillsCount > 0 && nodeVars.solarSystemId && (
<KillsCounter
killsCount={localKillsCount}
systemId={nodeVars.solarSystemId}
size={TooltipSize.lg}
killsActivityType={nodeVars.killsActivityType}
className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[nodeVars.killsActivityType!])}
>
<div className={clsx(classes.BookmarkWithIcon)}>
<span className={clsx(PrimeIcons.BOLT, classes.icon)} />
<span className={clsx(classes.text)}>{nodeVars.killsCount}</span>
</div>
</KillsCounter>
)}
{nodeVars.labelsInfo.map(x => (
<div key={x.id} className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[x.id])}>
{x.shortName}
</div>
))}
</div>
)}
<div
className={clsx(
classes.RootCustomNode,
nodeVars.regionClass && classes[nodeVars.regionClass],
nodeVars.status !== undefined ? classes[STATUS_CLASSES[nodeVars.status]] : '',
{ [classes.selected]: nodeVars.selected },
)}
onMouseDownCapture={e => nodeVars.dbClick(e)}
>
{nodeVars.visible && (
<>
<div className={classes.HeadRow}>
<div
className={clsx(
classes.classTitle,
nodeVars.classTitleColor,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]',
)}
>
{nodeVars.classTitle ?? '-'}
</div>
{nodeVars.tag != null && nodeVars.tag !== '' && (
<div className={clsx(classes.TagTitle, 'text-sky-400 font-medium')}>{nodeVars.tag}</div>
)}
<div
className={clsx(
classes.classSystemName,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans',
)}
>
{nodeVars.systemName}
</div>
{nodeVars.isWormhole && (
<div className={classes.statics}>
{nodeVars.sortedStatics.map(whClass => (
<WormholeClassComp key={String(whClass)} id={String(whClass)} />
))}
</div>
)}
{nodeVars.effectName !== null && nodeVars.isWormhole && (
<div className={clsx(classes.effect, EFFECT_BACKGROUND_STYLES[nodeVars.effectName])} />
)}
</div>
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
{nodeVars.customName && (
<div className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-blue-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5">
{nodeVars.customName}
</div>
)}
{!nodeVars.isWormhole && !nodeVars.customName && (
<div className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-stone-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5">
{nodeVars.regionName}
</div>
)}
{nodeVars.isWormhole && !nodeVars.customName && <div />}
<div className="flex items-center gap-1 justify-end">
<div className={clsx('flex items-center gap-1')}>
{nodeVars.locked && <i className={clsx(PrimeIcons.LOCK, classes.lockIcon)} />}
{nodeVars.hubs.includes(nodeVars.solarSystemId) && (
<i className={clsx(PrimeIcons.MAP_MARKER, classes.mapMarker)} />
)}
</div>
<LocalCounter
hasUserCharacters={nodeVars.hasUserCharacters}
localCounterCharacters={localCounterCharacters}
/>
</div>
</div>
</>
)}
</div>
{nodeVars.visible && (
<>
{nodeVars.unsplashedLeft.length > 0 && (
<div className={classes.Unsplashed}>
{nodeVars.unsplashedLeft.map(sig => (
<UnsplashedSignature key={sig.eve_id} signature={sig} />
))}
</div>
)}
{nodeVars.unsplashedRight.length > 0 && (
<div className={clsx(classes.Unsplashed, classes['Unsplashed--right'])}>
{nodeVars.unsplashedRight.map(sig => (
<UnsplashedSignature key={sig.eve_id} signature={sig} />
))}
</div>
)}
</>
)}
<div className={classes.Handlers}>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleTop, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Top}
id="a"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleRight, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Right}
id="b"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleBottom, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Bottom}
id="c"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleLeft, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Left}
id="d"
/>
</div>
</>
);
});
SolarSystemNodeDefault.displayName = 'SolarSystemNodeDefault';

View File

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

View File

@@ -1,223 +0,0 @@
import { memo } from 'react';
import { MapSolarSystemType } from '../../map.types';
import { Handle, NodeProps, Position } from 'reactflow';
import clsx from 'clsx';
import classes from './SolarSystemNodeTheme.module.scss';
import { PrimeIcons } from 'primereact/api';
import { useLocalCounter, useNodeKillsCount, useSolarSystemNode } from '../../hooks';
import {
EFFECT_BACKGROUND_STYLES,
MARKER_BOOKMARK_BG_STYLES,
STATUS_CLASSES,
} from '@/hooks/Mapper/components/map/constants';
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
import { LocalCounter } from './SolarSystemLocalCounter';
import { KillsCounter } from './SolarSystemKillsCounter';
import { TooltipPosition, WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
import { TooltipSize } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper/utils.ts';
export const SolarSystemNodeTheme = memo((props: NodeProps<MapSolarSystemType>) => {
const nodeVars = useSolarSystemNode(props);
const { localCounterCharacters } = useLocalCounter(nodeVars);
const localKillsCount = useNodeKillsCount(nodeVars.solarSystemId, nodeVars.killsCount);
return (
<>
{nodeVars.visible && (
<div className={classes.Bookmarks}>
{nodeVars.labelCustom !== '' && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]">{nodeVars.labelCustom}</span>
</div>
)}
{nodeVars.isShattered && (
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered, '!pr-[2px]')}>
<WdTooltipWrapper content="Shattered" position={TooltipPosition.top}>
<span className={clsx('block w-[10px] h-[10px]', classes.ShatteredIcon)} />
</WdTooltipWrapper>
</div>
)}
{localKillsCount && localKillsCount > 0 && nodeVars.solarSystemId && (
<KillsCounter
killsCount={localKillsCount}
systemId={nodeVars.solarSystemId}
size={TooltipSize.lg}
killsActivityType={nodeVars.killsActivityType}
className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[nodeVars.killsActivityType!])}
>
<div className={clsx(classes.BookmarkWithIcon)}>
<span className={clsx(PrimeIcons.BOLT, classes.icon)} />
<span className={clsx(classes.text)}>{nodeVars.killsCount}</span>
</div>
</KillsCounter>
)}
{nodeVars.labelsInfo.map(x => (
<div key={x.id} className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[x.id])}>
{x.shortName}
</div>
))}
</div>
)}
<div
className={clsx(
classes.RootCustomNode,
nodeVars.regionClass && classes[nodeVars.regionClass],
nodeVars.status !== undefined ? classes[STATUS_CLASSES[nodeVars.status]] : '',
{ [classes.selected]: nodeVars.selected },
)}
onMouseDownCapture={e => nodeVars.dbClick(e)}
>
{nodeVars.visible && (
<>
<div className={classes.HeadRow}>
<div
className={clsx(
classes.classTitle,
nodeVars.classTitleColor,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]',
)}
>
{nodeVars.classTitle ?? '-'}
</div>
{nodeVars.tag != null && nodeVars.tag !== '' && (
<div className={clsx(classes.TagTitle)}>{nodeVars.tag}</div>
)}
<div
className={clsx(
classes.classSystemName,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] flex-grow overflow-hidden text-ellipsis whitespace-nowrap',
)}
>
{nodeVars.systemName}
</div>
{nodeVars.isWormhole && (
<div className={classes.statics}>
{nodeVars.sortedStatics.map(whClass => (
<WormholeClassComp key={String(whClass)} id={String(whClass)} />
))}
</div>
)}
{nodeVars.effectName !== null && nodeVars.isWormhole && (
<div className={clsx(classes.effect, EFFECT_BACKGROUND_STYLES[nodeVars.effectName])} />
)}
</div>
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
{nodeVars.customName && (
<div
className={clsx(
classes.CustomName,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] whitespace-nowrap overflow-hidden text-ellipsis mr-0.5',
)}
>
{nodeVars.customName}
</div>
)}
{!nodeVars.isWormhole && !nodeVars.customName && (
<div
className={clsx(
classes.RegionName,
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] whitespace-nowrap overflow-hidden text-ellipsis mr-0.5',
)}
>
{nodeVars.regionName}
</div>
)}
{nodeVars.isWormhole && !nodeVars.customName && <div />}
<div className="flex items-center gap-1 justify-end">
<div className={clsx('flex items-center gap-1')}>
{nodeVars.locked && <i className={clsx(PrimeIcons.LOCK, classes.lockIcon)} />}
{nodeVars.hubs.includes(nodeVars.solarSystemId) && (
<i className={clsx(PrimeIcons.MAP_MARKER, classes.mapMarker)} />
)}
</div>
<LocalCounter
hasUserCharacters={nodeVars.hasUserCharacters}
localCounterCharacters={localCounterCharacters}
/>
</div>
</div>
</>
)}
</div>
{nodeVars.visible && (
<>
{nodeVars.unsplashedLeft.length > 0 && (
<div className={classes.Unsplashed}>
{nodeVars.unsplashedLeft.map(sig => (
<UnsplashedSignature key={sig.eve_id} signature={sig} />
))}
</div>
)}
{nodeVars.unsplashedRight.length > 0 && (
<div className={clsx(classes.Unsplashed, classes['Unsplashed--right'])}>
{nodeVars.unsplashedRight.map(sig => (
<UnsplashedSignature key={sig.eve_id} signature={sig} />
))}
</div>
)}
</>
)}
<div className={classes.Handlers}>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleTop, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Top}
id="a"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleRight, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Right}
id="b"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleBottom, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Bottom}
id="c"
/>
<Handle
type="source"
className={clsx(classes.Handle, classes.HandleLeft, {
[classes.selected]: nodeVars.selected,
[classes.Tick]: nodeVars.isThickConnections,
})}
style={{ visibility: nodeVars.showHandlers ? 'visible' : 'hidden' }}
position={Position.Left}
id="d"
/>
</div>
</>
);
});
SolarSystemNodeTheme.displayName = 'SolarSystemNodeTheme';

View File

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

View File

@@ -1,22 +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: var(--text-color);
font-size: 8px;
text-align: center;
font-weight: bolder;
display: block;
}
& > .Eol {
display: block;
}
}

View File

@@ -1,70 +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 { K162_TYPES_MAP } from '@/hooks/Mapper/constants.ts';
import { parseSignatureCustomInfo } from '@/hooks/Mapper/helpers/parseSignatureCustomInfo.ts';
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 customInfo = useMemo(() => {
return parseSignatureCustomInfo(signature.custom_info);
}, [signature]);
const k162TypeOption = useMemo(() => {
if (!customInfo?.k162Type) {
return null;
}
return K162_TYPES_MAP[customInfo.k162Type];
}, [customInfo]);
const isEOL = useMemo(() => {
return customInfo?.isEOL;
}, [customInfo]);
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)}
// @ts-ignore
content={
<div className="flex flex-col gap-1">
<InfoDrawer title={<b className="text-slate-50">{signature.eve_id}</b>}>
{renderInfoColumn(signature)}
</InfoDrawer>
</div>
}
>
<div className={clsx(classes.Box, whClassStyle)}>
<svg width="13" height="8" viewBox="0 0 13 8" xmlns="http://www.w3.org/2000/svg">
<rect y="1" width="13" height="4" rx="2" className={whClassStyle} fill="currentColor" />
{isEOL && <rect x="4" width="5" height="6" rx="1" className={clsx(classes.Eol)} fill="#a153ac" />}
</svg>
</div>
</WdTooltipWrapper>
);
};

View File

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

View File

@@ -1,4 +1,4 @@
import { ConnectionType, MassState, ShipSizeStatus } from '@/hooks/Mapper/types';
import { MassState } from '@/hooks/Mapper/types';
export enum SOLAR_SYSTEM_CLASS_IDS {
ccp1 = -1,
@@ -16,7 +16,7 @@ export enum SOLAR_SYSTEM_CLASS_IDS {
thera = 12,
c13 = 13,
sentinel = 14,
barbican = 15,
baribican = 15,
vidette = 16,
conflux = 17,
redoubt = 18,
@@ -82,7 +82,7 @@ export const SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS = {
thera: SOLAR_SYSTEM_CLASS_GROUPS.thera,
c13: SOLAR_SYSTEM_CLASS_GROUPS.c13,
sentinel: SOLAR_SYSTEM_CLASS_GROUPS.drifter,
barbican: 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,
@@ -217,7 +217,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 14,
effectPower: 2,
title: 'Class 14 (Sentinel Drifter)',
shortTitle: 'Sentinel MZ',
shortTitle: 'Sentinel',
},
{
id: 'barbican',
@@ -225,7 +225,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 15,
effectPower: 2,
title: 'Class 15 (Barbican Drifter)',
shortTitle: 'Liberated Barbican',
shortTitle: 'Barbican',
},
{
id: 'vidette',
@@ -233,7 +233,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 16,
effectPower: 2,
title: 'Class 16 (Vidette Drifter)',
shortTitle: 'Sanctified Vidette',
shortTitle: 'Vidette',
},
{
id: 'conflux',
@@ -241,7 +241,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 17,
effectPower: 2,
title: 'Class 17 (Conflux Drifter)',
shortTitle: 'Conflux Eyrie',
shortTitle: 'Conflux',
},
{
id: 'redoubt',
@@ -249,7 +249,7 @@ export const WORMHOLES_ADDITIONAL_INFO_RAW: WormholesAdditionalInfoType[] = [
wormholeClassID: 18,
effectPower: 2,
title: 'Class 18 (Redoubt Drifter)',
shortTitle: 'Azdaja Redoubt',
shortTitle: 'Redoubt',
},
{
id: 'a1',
@@ -322,9 +322,6 @@ export const WORMHOLES_ADDITIONAL_INFO: Record<string, WormholesAdditionalInfoTy
export const WORMHOLES_ADDITIONAL_INFO_BY_CLASS_ID: Record<string, WormholesAdditionalInfoType> =
WORMHOLES_ADDITIONAL_INFO_RAW.reduce((acc, x) => ({ ...acc, [x.wormholeClassID]: x }), {});
export const WORMHOLES_ADDITIONAL_INFO_BY_SHORT_NAME: Record<string, WormholesAdditionalInfoType> =
WORMHOLES_ADDITIONAL_INFO_RAW.reduce((acc, x) => ({ ...acc, [x.shortName]: x }), {});
// export const SOLAR_SYSTEM_CLASS_NAMES = {
// ccp1 = ,
// c1 = ,
@@ -653,7 +650,6 @@ export enum LABELS {
l3 = '3',
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const LABELS_INFO: Record<string, any> = {
[LABELS.clear]: { id: 'clear', name: 'Clear', shortName: '', icon: '' },
[LABELS.la]: { id: 'la', name: 'Label A', shortName: 'A', icon: '' },
@@ -716,13 +712,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 = {
@@ -731,52 +720,16 @@ export const MASS_STATE_NAMES = {
[MassState.verge]: 'Verge of collapse',
};
export const SHIP_SIZES_NAMES_ORDER = [
ShipSizeStatus.small,
ShipSizeStatus.medium,
ShipSizeStatus.large,
ShipSizeStatus.freight,
ShipSizeStatus.capital,
];
export const SHIP_SIZES_NAMES = {
[ShipSizeStatus.small]: 'Frigate',
[ShipSizeStatus.medium]: 'Medium',
[ShipSizeStatus.large]: 'Normal',
[ShipSizeStatus.freight]: 'Huge',
[ShipSizeStatus.capital]: 'Capital',
};
export const SHIP_SIZES_SIZE = {
[ShipSizeStatus.small]: '5K',
[ShipSizeStatus.medium]: '62K',
[ShipSizeStatus.large]: '375K',
[ShipSizeStatus.freight]: '1M',
[ShipSizeStatus.capital]: '2M',
};
export const SHIP_MASSES_SIZE: Record<number, ShipSizeStatus> = {
5_000_000: ShipSizeStatus.small,
62_000_000: ShipSizeStatus.medium,
300_000_000: ShipSizeStatus.large,
375_000_000: ShipSizeStatus.large,
1_000_000_000: ShipSizeStatus.freight,
1_350_000_000: ShipSizeStatus.capital,
1_800_000_000: ShipSizeStatus.capital,
2_000_000_000: ShipSizeStatus.capital,
};
export const SHIP_SIZES_DESCRIPTION = {
[ShipSizeStatus.small]: 'Frigate wormhole - up to Destroyer | 5K t.',
[ShipSizeStatus.medium]: 'Cruise wormhole - up to Battlecruiser | 62K t.',
[ShipSizeStatus.large]: 'Large wormhole - up to Battleship | 375K t.',
[ShipSizeStatus.freight]: 'Huge wormhole - up to Freighter | 1M t.',
[ShipSizeStatus.capital]: 'Capital wormhole - up to Capital | 2M t.',
};
export const SHIP_SIZES_NAMES_SHORT = {
[ShipSizeStatus.small]: 'S',
[ShipSizeStatus.medium]: 'M',
[ShipSizeStatus.large]: 'L',
[ShipSizeStatus.freight]: 'H',
[ShipSizeStatus.capital]: 'XL',
};
// export const SHIP_SIZES_NAMES_ORDER = [
// ShipSizeStatus.small,
// ShipSizeStatus.normal,
// // ShipSizeStatus.large,
// // ShipSizeStatus.capital,
// ];
//
// export const SHIP_SIZES_NAMES = {
// [ShipSizeStatus.small]: 'Frigate',
// [ShipSizeStatus.normal]: 'Normal',
// // [ShipSizeStatus.large]: 'Normal',
// // [ShipSizeStatus.capital]: 'Normal',
// };

View File

@@ -10,6 +10,5 @@ export const convertSystem2Node = (sys: SolarSystemRawType): Node => {
position: sys.position,
data: sys,
draggable: !sys.locked,
deletable: !sys.locked,
};
};

View File

@@ -1,32 +0,0 @@
import { SolarSystemNodeDefault, SolarSystemNodeTheme } from '../components/SolarSystemNode';
import type { NodeProps } from 'reactflow';
import type { ComponentType } from 'react';
import { MapSolarSystemType } from '../map.types';
import { ConnectionMode } from 'reactflow';
export type SolarSystemNodeComponent = ComponentType<NodeProps<MapSolarSystemType>>;
interface ThemeBehavior {
isPanAndDrag: boolean;
nodeComponent: SolarSystemNodeComponent;
connectionMode: ConnectionMode;
}
const THEME_BEHAVIORS: {
[key: string]: ThemeBehavior;
} = {
default: {
isPanAndDrag: false,
nodeComponent: SolarSystemNodeDefault,
connectionMode: ConnectionMode.Loose,
},
pathfinder: {
isPanAndDrag: true,
nodeComponent: SolarSystemNodeTheme,
connectionMode: ConnectionMode.Loose,
},
};
export function getBehaviorForTheme(themeName: string) {
return THEME_BEHAVIORS[themeName] ?? THEME_BEHAVIORS.default;
}

View File

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

View File

@@ -10,7 +10,7 @@ export const isWormholeSpace = (wormholeClassID: number) => {
case SOLAR_SYSTEM_CLASS_IDS.c6:
case SOLAR_SYSTEM_CLASS_IDS.c13:
case SOLAR_SYSTEM_CLASS_IDS.thera:
case SOLAR_SYSTEM_CLASS_IDS.barbican:
case SOLAR_SYSTEM_CLASS_IDS.baribican:
case SOLAR_SYSTEM_CLASS_IDS.vidette:
case SOLAR_SYSTEM_CLASS_IDS.conflux:
case SOLAR_SYSTEM_CLASS_IDS.redoubt:

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,24 +2,17 @@ import { Node, useReactFlow } from 'reactflow';
import { useCallback, useRef } from 'react';
import { CommandAddSystems } from '@/hooks/Mapper/types/mapHandlers.ts';
import { convertSystem2Node } from '../../helpers';
import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic';
export const useMapAddSystems = () => {
const rf = useReactFlow();
const { addSystemStatic } = useLoadSystemStatic({ systems: [] });
const ref = useRef({ rf });
ref.current = { rf };
return useCallback((systems: CommandAddSystems) => {
const { rf } = ref.current;
const nodes = rf.getNodes();
const newSystems = systems.filter(x => !nodes.some(y => x.id === y.id));
newSystems.forEach(x => addSystemStatic(x.system_static_info));
const prepared: Node[] = newSystems.map(convertSystem2Node);
const prepared: Node[] = systems.filter(x => !nodes.some(y => x.id === y.id)).map(convertSystem2Node);
rf.addNodes(prepared);
}, []);
};

View File

@@ -14,7 +14,6 @@ export const useMapInit = () => {
return useCallback(
({
systems,
system_signatures,
kills,
connections,
wormholes,
@@ -52,10 +51,6 @@ export const useMapInit = () => {
updateData.systems = systems;
}
if (system_signatures) {
updateData.systemSignatures = system_signatures;
}
if (kills) {
updateData.kills = kills.reduce((acc, x) => ({ ...acc, [x.solar_system_id]: x.kills }), {});
}

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

@@ -42,7 +42,7 @@ export const useMapUpdateSystems = () => {
return newSystem;
});
update({ systems: out }, true);
update({ systems: out });
},
[rf, update],
);

View File

@@ -1,11 +1,2 @@
export * from './useMapHandlers';
export * from './useUpdateNodes';
export * from './useNodesEdgesState';
export * from './useBackgroundVars';
export * from './useKillsCounter';
export * from './useSystemName';
export * from './useNodesEdgesState';
export * from './useSolarSystemNode';
export * from './useUnsplashedSignatures';
export * from './useUpdateNodes';
export * from './useNodeKillsCount';

View File

@@ -1,48 +0,0 @@
import { useEffect, useState } from 'react';
import { BackgroundVariant } from 'reactflow';
export function useBackgroundVars(themeName?: string) {
const [variant, setVariant] = useState<BackgroundVariant>(BackgroundVariant.Dots);
const [gap, setGap] = useState<number>(16);
const [size, setSize] = useState<number>(1);
const [color, setColor] = useState('#81818b');
const [snapSize, setSnapSize] = useState<number>(25);
useEffect(() => {
// match any element whose entire `class` attribute ends with "-theme"
let themeEl = document.querySelector('[class$="-theme"]');
// If none is found, fall back to the <html> element
if (!themeEl) {
themeEl = document.documentElement;
}
const style = getComputedStyle(themeEl as HTMLElement);
const rawVariant = style.getPropertyValue('--rf-bg-variant').replace(/['"]/g, '').trim().toLowerCase();
let finalVariant: BackgroundVariant = BackgroundVariant.Dots;
if (rawVariant === 'lines') {
finalVariant = BackgroundVariant.Lines;
} else if (rawVariant === 'cross') {
finalVariant = BackgroundVariant.Cross;
}
const cssVarGap = style.getPropertyValue('--rf-bg-gap');
const cssVarSize = style.getPropertyValue('--rf-bg-size');
const cssVarSnapSize = style.getPropertyValue('--rf-snap-size');
const cssColor = style.getPropertyValue('--rf-bg-pattern-color');
const gapNum = parseInt(cssVarGap, 10) || 16;
const sizeNum = parseInt(cssVarSize, 10) || 1;
const snapSize = parseInt(cssVarSnapSize, 10) || 25; //react-flow default
setVariant(finalVariant);
setGap(gapNum);
setSize(sizeNum);
setColor(cssColor);
setSnapSize(snapSize);
}, [themeName]);
return { variant, gap, size, color, snapSize };
}

View File

@@ -1,43 +0,0 @@
import { useMemo } from 'react';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useSystemKills } from '@/hooks/Mapper/components/mapInterface/widgets/WSystemKills/hooks/useSystemKills.ts';
interface UseKillsCounterProps {
realSystemId: string;
}
export function useKillsCounter({ realSystemId }: UseKillsCounterProps) {
const { data: mapData, outCommand } = useMapRootState();
const { systems } = mapData;
const systemNameMap = useMemo(() => {
const m: Record<string, string> = {};
systems.forEach(sys => {
m[sys.id] = sys.temporary_name || sys.name || '???';
});
return m;
}, [systems]);
const { kills: allKills, isLoading } = useSystemKills({
systemId: realSystemId,
outCommand,
showAllVisible: false,
});
const filteredKills = useMemo(() => {
if (!allKills || allKills.length === 0) return [];
// Sort kills by time, most recent first, but don't limit the number of kills
return [...allKills].sort((a, b) => {
const aTime = a.kill_time ? new Date(a.kill_time).getTime() : 0;
const bTime = b.kill_time ? new Date(b.kill_time).getTime() : 0;
return bTime - aTime;
});
}, [allKills]);
return {
isLoading,
kills: filteredKills,
systemNameMap,
};
}

View File

@@ -1,31 +0,0 @@
import { useMemo } from 'react';
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager';
import { LABELS_INFO, LABELS_ORDER } from '@/hooks/Mapper/components/map/constants';
interface UseLabelsInfoParams {
labels: string | null;
linkedSigPrefix: string | null;
isShowLinkedSigId: boolean;
}
export type LabelInfo = {
id: string;
shortName: string;
};
function sortedLabels(labels: string[]): LabelInfo[] {
if (!labels) return [];
return LABELS_ORDER.filter(x => labels.includes(x)).map(x => LABELS_INFO[x] as LabelInfo);
}
export function useLabelsInfo({ labels, linkedSigPrefix, isShowLinkedSigId }: UseLabelsInfoParams) {
const labelsManager = useMemo(() => new LabelsManager(labels ?? ''), [labels]);
const labelsInfo = useMemo(() => sortedLabels(labelsManager.list), [labelsManager]);
const labelCustom = useMemo(() => {
if (isShowLinkedSigId && linkedSigPrefix) {
return labelsManager.customLabel ? `${linkedSigPrefix}${labelsManager.customLabel}` : linkedSigPrefix;
}
return labelsManager.customLabel;
}, [linkedSigPrefix, isShowLinkedSigId, labelsManager]);
return { labelsInfo, labelCustom };
}

View File

@@ -19,6 +19,8 @@ import {
MapHandlers,
} from '@/hooks/Mapper/types/mapHandlers.ts';
import { useMapEventListener } from '@/hooks/Mapper/events';
import {
useCommandsCharacters,
useCommandsConnections,
@@ -58,19 +60,16 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
mapInit(data as CommandInit);
break;
case Commands.addSystems:
setTimeout(() => mapAddSystems(data as CommandAddSystems), 100);
break;
case Commands.updateSystems:
mapUpdateSystems(data as CommandUpdateSystems);
break;
case Commands.removeSystems:
setTimeout(() => removeSystems(data as CommandRemoveSystems), 100);
break;
case Commands.addConnections:
setTimeout(() => addConnections(data as CommandAddConnections), 100);
break;
case Commands.removeConnections:
setTimeout(() => removeConnections(data as CommandRemoveConnections), 100);
removeConnections(data as CommandRemoveConnections);
break;
case Commands.charactersUpdated:
charactersUpdated(data as CommandCharactersUpdated);
@@ -112,40 +111,17 @@ 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;
case Commands.detailedKillsUpdated:
// do nothing here
break;
case Commands.characterActivityData:
break;
case Commands.trackingCharactersData:
break;
case Commands.updateActivity:
break;
case Commands.updateTracking:
break;
case Commands.userSettingsUpdated:
break;
default:
console.warn(`Map handlers: Unknown command: ${type}`, data);
break;
@@ -155,4 +131,20 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
},
[],
);
useMapEventListener(event => {
switch (event.name) {
case Commands.addConnections:
addConnections(event.data as CommandAddConnections);
break;
case Commands.addSystems:
mapAddSystems(event.data as CommandAddSystems);
break;
case Commands.removeSystems:
removeSystems(event.data as CommandRemoveSystems);
break;
default:
break;
}
});
};

View File

@@ -1,42 +0,0 @@
import { useEffect, useState, useCallback } from 'react';
import { useMapEventListener } from '@/hooks/Mapper/events';
import { Commands } from '@/hooks/Mapper/types';
interface Kill {
solar_system_id: number | string;
kills: number;
}
interface MapEvent {
name: Commands;
data?: any;
payload?: Kill[];
}
export function useNodeKillsCount(
systemId: number | string,
initialKillsCount: number | null
): number | null {
const [killsCount, setKillsCount] = useState<number | null>(initialKillsCount);
useEffect(() => {
setKillsCount(initialKillsCount);
}, [initialKillsCount]);
const handleEvent = useCallback((event: MapEvent): boolean => {
if (event.name === Commands.killsUpdated && Array.isArray(event.payload)) {
const killForSystem = event.payload.find(
kill => kill.solar_system_id.toString() === systemId.toString()
);
if (killForSystem && typeof killForSystem.kills === 'number') {
setKillsCount(killForSystem.kills);
}
return true;
}
return false;
}, [systemId]);
useMapEventListener(handleEvent);
return killsCount;
}

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,234 +0,0 @@
import { useMemo } from 'react';
import { MapSolarSystemType } from '../map.types';
import { NodeProps } from 'reactflow';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider';
import { useDoubleClick } from '@/hooks/Mapper/hooks/useDoubleClick';
import { REGIONS_MAP, Spaces } from '@/hooks/Mapper/constants';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace';
import { getSystemClassStyles } from '@/hooks/Mapper/components/map/helpers';
import { sortWHClasses } from '@/hooks/Mapper/helpers';
import { CharacterTypeRaw, OutCommand, SystemSignature } from '@/hooks/Mapper/types';
import { useUnsplashedSignatures } from './useUnsplashedSignatures';
import { useSystemName } from './useSystemName';
import { LabelInfo, useLabelsInfo } from './useLabelsInfo';
import { getSystemStaticInfo } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic';
function getActivityType(count: number): string {
if (count <= 5) return 'activityNormal';
if (count <= 30) return 'activityWarn';
return 'activityDanger';
}
const SpaceToClass: Record<string, string> = {
[Spaces.Caldari]: 'Caldaria',
[Spaces.Matar]: 'Mataria',
[Spaces.Amarr]: 'Amarria',
[Spaces.Gallente]: 'Gallente',
};
export function useLocalCounter(nodeVars: SolarSystemNodeVars) {
const localCounterCharacters = useMemo(() => {
return nodeVars.charactersInSystem
.map(char => ({
...char,
compact: true,
isOwn: nodeVars.userCharacters.includes(char.eve_id),
}))
.sort((a, b) => a.name.localeCompare(b.name));
}, [nodeVars.charactersInSystem, nodeVars.userCharacters]);
return { localCounterCharacters };
}
export function useSolarSystemNode(props: NodeProps<MapSolarSystemType>): SolarSystemNodeVars {
const { id, data, selected } = props;
const {
id: solar_system_id,
locked,
name,
tag,
status,
labels,
temporary_name,
linked_sig_eve_id: linkedSigEveId = '',
} = data;
const {
interfaceSettings,
data: { systemSignatures: mapSystemSignatures },
} = useMapRootState();
const systemStaticInfo = useMemo(() => {
return getSystemStaticInfo(solar_system_id)!;
}, [solar_system_id]);
const {
system_class,
security,
class_title,
statics,
effect_name,
region_name,
region_id,
is_shattered,
solar_system_name,
} = systemStaticInfo;
const { isShowUnsplashedSignatures } = interfaceSettings;
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
const isShowLinkedSigId = useMapGetOption('show_linked_signature_id') === 'true';
const isShowLinkedSigIdTempName = useMapGetOption('show_linked_signature_id_temp_name') === 'true';
const {
data: {
characters,
wormholesData,
hubs,
kills,
userCharacters,
isConnecting,
hoverNodeId,
visibleNodes,
showKSpaceBG,
isThickConnections,
},
outCommand,
} = useMapState();
const visible = useMemo(() => visibleNodes.has(id), [id, visibleNodes]);
const systemSigs = useMemo(() => mapSystemSignatures[solar_system_id] || [], [solar_system_id, mapSystemSignatures]);
const charactersInSystem = useMemo(() => {
return characters.filter(c => c.location?.solar_system_id === parseInt(solar_system_id) && c.online);
}, [characters, solar_system_id]);
const isWormhole = isWormholeSpace(system_class);
const classTitleColor = useMemo(
() => getSystemClassStyles({ systemClass: system_class, security }),
[security, system_class],
);
const sortedStatics = useMemo(() => sortWHClasses(wormholesData, statics), [wormholesData, statics]);
const linkedSigPrefix = useMemo(() => (linkedSigEveId ? linkedSigEveId.split('-')[0] : null), [linkedSigEveId]);
const { labelsInfo, labelCustom } = useLabelsInfo({
labels,
linkedSigPrefix,
isShowLinkedSigId,
});
const killsCount = useMemo(() => kills[parseInt(solar_system_id)] ?? null, [kills, solar_system_id]);
const killsActivityType = killsCount ? getActivityType(killsCount) : null;
const hasUserCharacters = useMemo(
() => charactersInSystem.some(x => userCharacters.includes(x.eve_id)),
[charactersInSystem, userCharacters],
);
const dbClick = useDoubleClick(() => {
outCommand({
type: OutCommand.openSettings,
data: { system_id: solar_system_id },
});
});
const showHandlers = isConnecting || hoverNodeId === id;
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
const regionClass = showKSpaceBG ? SpaceToClass[space] || null : null;
const { systemName, computedTemporaryName, customName } = useSystemName({
isTempSystemNameEnabled,
temporary_name,
isShowLinkedSigIdTempName,
linkedSigPrefix,
name,
systemStaticInfo,
});
const { unsplashedLeft, unsplashedRight } = useUnsplashedSignatures(systemSigs, isShowUnsplashedSignatures);
const hubsAsStrings = useMemo(() => hubs.map(item => item.toString()), [hubs]);
const nodeVars: SolarSystemNodeVars = {
id,
selected,
visible,
isWormhole,
classTitleColor,
killsCount,
killsActivityType,
hasUserCharacters,
userCharacters,
showHandlers,
regionClass,
systemName,
customName,
labelCustom,
isShattered: is_shattered,
tag,
status,
labelsInfo,
dbClick,
sortedStatics,
effectName: effect_name,
solarSystemId: solar_system_id.toString(),
locked,
hubs: hubsAsStrings,
name,
isConnecting,
hoverNodeId,
charactersInSystem,
unsplashedLeft,
unsplashedRight,
isThickConnections,
classTitle: class_title,
temporaryName: computedTemporaryName,
regionName: region_name,
solarSystemName: solar_system_name,
};
return nodeVars;
}
export interface SolarSystemNodeVars {
id: string;
selected: boolean;
visible: boolean;
isWormhole: boolean;
classTitleColor: string | null;
killsCount: number | null;
killsActivityType: string | null;
hasUserCharacters: boolean;
showHandlers: boolean;
regionClass: string | null;
systemName: string;
customName?: string | null;
labelCustom: string | null;
isShattered: boolean;
tag?: string | null;
status?: number;
labelsInfo: LabelInfo[];
dbClick: (event: React.MouseEvent<HTMLDivElement>) => void;
sortedStatics: Array<string | number>;
effectName: string | null;
regionName: string | null;
solarSystemId: string;
solarSystemName: string | null;
locked: boolean;
hubs: string[];
name: string | null;
isConnecting: boolean;
hoverNodeId: string | null;
charactersInSystem: Array<CharacterTypeRaw>;
userCharacters: string[];
unsplashedLeft: Array<SystemSignature>;
unsplashedRight: Array<SystemSignature>;
isThickConnections: boolean;
classTitle: string | null;
temporaryName?: string | null;
}

View File

@@ -1,56 +0,0 @@
import { useMemo } from 'react';
import { SolarSystemStaticInfoRaw } from '@/hooks/Mapper/types';
interface UseSystemNameParams {
isTempSystemNameEnabled: boolean;
temporary_name?: string | null;
isShowLinkedSigIdTempName: boolean;
linkedSigPrefix: string | null;
name?: string | null;
systemStaticInfo: SolarSystemStaticInfoRaw;
}
export const useSystemName = ({
isTempSystemNameEnabled,
temporary_name,
isShowLinkedSigIdTempName,
linkedSigPrefix,
name,
systemStaticInfo,
}: UseSystemNameParams) => {
const { solar_system_name = '' } = systemStaticInfo;
const computedTemporaryName = useMemo(() => {
if (!isTempSystemNameEnabled) {
return '';
}
if (isShowLinkedSigIdTempName && linkedSigPrefix) {
return temporary_name ? `${linkedSigPrefix}${temporary_name}` : `${linkedSigPrefix}${solar_system_name}`;
}
return temporary_name ?? '';
}, [isTempSystemNameEnabled, temporary_name, solar_system_name, isShowLinkedSigIdTempName, linkedSigPrefix]);
const systemName = useMemo(() => {
if (isTempSystemNameEnabled && computedTemporaryName) {
return computedTemporaryName;
}
return solar_system_name;
}, [isTempSystemNameEnabled, computedTemporaryName, solar_system_name]);
const customName = useMemo(() => {
if (isTempSystemNameEnabled && computedTemporaryName && name) {
return name;
}
if (solar_system_name !== name && name) {
return name;
}
return null;
}, [isTempSystemNameEnabled, computedTemporaryName, name, solar_system_name]);
return { systemName, computedTemporaryName, customName };
};

View File

@@ -1,30 +0,0 @@
import { useMemo } from 'react';
import { SystemSignature } from '@/hooks/Mapper/types';
import { prepareUnsplashedChunks } from '@/hooks/Mapper/components/map/helpers';
export type UnsplashedSignatureType = SystemSignature & { sig_id: string };
export function useUnsplashedSignatures(systemSigs: SystemSignature[], isShowUnsplashedSignatures: boolean) {
return useMemo(() => {
if (!isShowUnsplashedSignatures) {
return {
unsplashedLeft: [] as SystemSignature[],
unsplashedRight: [] as SystemSignature[],
};
}
const chunks = prepareUnsplashedChunks(
systemSigs
.filter(s => s.group === 'Wormhole' && !s.linked_system)
.map(s => ({
eve_id: s.eve_id,
type: s.type,
custom_info: s.custom_info,
kind: s.kind,
name: s.name,
group: s.group,
})) as UnsplashedSignatureType[],
);
const [unsplashedLeft, unsplashedRight] = chunks;
return { unsplashedLeft, unsplashedRight };
}, [isShowUnsplashedSignatures, systemSigs]);
}

View File

@@ -6,9 +6,9 @@ import { SolarSystemRawType } from '@/hooks/Mapper/types';
const useThrottle = () => {
const throttleSeed = useRef<number | null>(null);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const throttleFunction = useRef((func: any, delay = 200) => {
if (!throttleSeed.current) {
// Call the callback immediately for the first time
func();
throttleSeed.current = setTimeout(() => {
throttleSeed.current = null;
@@ -75,7 +75,7 @@ export const useUpdateNodes = (nodes: Node<SolarSystemRawType>[]) => {
const visibleNodes = new Set(nodes.filter(x => isNodeVisible(x, viewport)).map(x => x.id));
update({ visibleNodes });
}, [getViewport, nodes, update]);
}, [nodes]);
useOnViewportChange({
onChange: () => throttle(updateNodesVisibility.bind(this)),
@@ -84,5 +84,5 @@ export const useUpdateNodes = (nodes: Node<SolarSystemRawType>[]) => {
useEffect(() => {
updateNodesVisibility();
}, [nodes, updateNodesVisibility]);
}, [nodes]);
};

View File

@@ -1,6 +1,5 @@
import { SolarSystemRawType } from '@/hooks/Mapper/types/system';
import { SolarSystemConnection } from '@/hooks/Mapper/types';
import { XYPosition } from 'reactflow';
export type MapSolarSystemType = Omit<SolarSystemRawType, 'position'>;
@@ -8,5 +7,3 @@ export type OnMapSelectionChange = (event: {
systems: string[];
connections: Pick<SolarSystemConnection, 'source' | 'target'>[];
}) => void;
export type OnMapAddSystemCallback = (props: { coordinates: XYPosition | null }) => void;

View File

@@ -1,36 +0,0 @@
@import './eve-common-variables';
@import './eve-common';
.default-theme {
--rf-bg-color: #0C0A09;
--rf-soft-bg-color: #171717;
--rf-node-bg-color: #202020;
--rf-node-soft-bg-color: #202020;
--rf-text-color: #ffffff;
--rf-tag-color: #38BDF8;
--rf-region-name: #D6D3D1;
--rf-custom-name: #93C5FD;
--rf-node-font-family: 'Shentox', 'Rogan', sans-serif !important;
--rf-node-font-weight: 500;
--rf-bg-variant: "dots";
--rf-bg-gap: 15;
--rf-bg-size: 1;
--rf-bg-pattern-color: #81818a;
--pastel-blue: #5a7d9a;
--pastel-pink: #d291bc;
--pastel-green: #88b04b;
--pastel-yellow: #ffdd59;
--dark-bg: #2d2d2d;
--text-color: #ffffff;
--tooltip-bg: #202020;
--window-corner: #72716f;
--rf-local-counter-font-weight: 500;
--rf-node-local-counter: inherit;
--rf-has-user-characters: #ffc75d;
}

View File

@@ -1,122 +1,78 @@
$eve-link-color-default: #333;
$eve-link-color-top-mass-0: #333;
$eve-link-color-top-mass-1: #5a4520;
$eve-link-color-top-mass-2: #672c2c;
$eve-link-color-middle-mass-0: #333;
$eve-link-color-middle-mass-1: #333;
$eve-link-color-middle-mass-2: #333;
$eve-link-color-middle-time-0: #5c5c5c;
$eve-link-color-middle-time-1: #ff00cd;
$eve-link-color-middle-time-1-border: #99f3ff;
$friendlyBase: #3bbd39;
$friendlyAlpha: #3bbd3952;
$friendlyDark20: darken($friendlyBase, 20%);
$friendlyDark30: darken($friendlyBase, 30%);
$friendlyDark5: darken($friendlyBase, 5%);
$eve-link-color-top-mass-1-time-1: #796300;
$eve-link-color-top-mass-2-time-1: #8c1717;
$eve-link-color-temp: orange;
$lookingForBase: #43c2fd;
$lookingForAlpha: rgba(67, 176, 253, 0.48);
$lookingForDark15: darken($lookingForBase, 15%);
$eve-effect-pulsar: #40aef5;
$eve-effect-magnetar: #f058f8;
$eve-effect-wolfRayet: #ef7843;
$eve-effect-blackHole: #1b1b1b;
$eve-effect-cataclysmicVariable: #ffea90;
$eve-effect-redGiant: #fd3c3c;
$eve-effect-dazhLiminalityLocus: #ff6464;
$eve-effect-imperialStellarObservatory: #6991ce;
$eve-effect-stateStellarObservatory: #6991ce;
$eve-effect-republicStellarObservatory: #6991ce;
$eve-effect-federalStellarObservatory: #6991ce;
$homeBase: rgb(179, 253, 67);
$homeAlpha: rgba(186, 248, 48, 0.32);
$homeBackground: #a0fa5636;
$homeDark30: darken($homeBase, 30%);
$eve-wh-type-color-high: #5dffd2;
$eve-wh-type-color-low: #f79400;
$eve-wh-type-color-null: #fc3c3c;
$eve-wh-type-color-c1: #69bfce;
$eve-wh-type-color-c2: #6991ce;
$eve-wh-type-color-c3: #a8cb70;
$eve-wh-type-color-c4: #e39c68;
$eve-wh-type-color-c5: #de8686;
$eve-wh-type-color-c6: #e76363;
$eve-wh-type-color-c13: #988cb5;
$eve-wh-type-color-drifter: #ff44f6;
$eve-wh-type-color-thera: #ffffff;
$eve-wh-type-color-zarzakh: #212121;
:root {
--pastel-blue: #5a7d9a;
--pastel-pink: #d291bc;
--pastel-green: #88b04b;
--pastel-yellow: #ffdd59;
--dark-bg: #2d2d2d;
--text-color: #ffffff;
--tooltip-bg: #202020;
$eve-security-color-10: #2c74df;
$eve-security-color-09: #3998e8;
$eve-security-color-08: #4dcbf5;
$eve-security-color-07: #60d8a2;
$eve-security-color-06: #71e454;
$eve-security-color-05: #f2fc81;
$eve-security-color-04: #d96c07;
$eve-security-color-03: #cb440f;
$eve-security-color-02: #b91117;
$eve-security-color-01: #732020;
$eve-security-color-00: #8b3263;
$eve-security-color-m-01: #8b3263;
$eve-security-color-m-02: #8b3263;
$eve-security-color-m-03: #8b3263;
$eve-security-color-m-04: #8b3263;
$eve-security-color-m-05: #8b3263;
$eve-security-color-m-06: #8b3263;
$eve-security-color-m-07: #8b3263;
$eve-security-color-m-08: #8b3263;
$eve-security-color-m-09: #8b3263;
$eve-security-color-m-10: #8b3263;
--pastel-blue-darken10: #4f6b86;
--pastel-blue-lighten10: #6da3af;
--pastel-pink-darken10: #bb7ca9;
--pastel-pink-lighten10: #e0a6cb;
--pastel-green-darken10: #79a244;
--pastel-green-lighten10: #99cf52;
--pastel-yellow-darken10: #e6c44f;
--pastel-yellow-lighten10: #ffe874;
$eve-solar-system-status-unknown: transparent;
$eve-solar-system-status-friendly: #3bbd3952;
$eve-solar-system-status-warning: #906518a6;
$eve-solar-system-status-target: #b439ff6b;
$eve-solar-system-status-dangerous: #d54040;
$eve-solar-system-status-lookingFor: rgba(67, 176, 253, 0.48);
$eve-solar-system-status-home: rgb(197, 253, 67);
--eve-link-color-default: #333;
--eve-link-color-top-mass-0: #333;
--eve-link-color-top-mass-1: #5a4520;
--eve-link-color-top-mass-2: #672c2c;
--eve-link-color-middle-mass-0: #333;
--eve-link-color-middle-mass-1: #333;
--eve-link-color-middle-mass-2: #333;
--eve-link-color-middle-time-0: #5c5c5c;
--eve-link-color-middle-time-1: #ff00cd;
--eve-link-color-middle-time-1-border: #99f3ff;
--eve-link-color-top-mass-1-time-1: #796300;
--eve-link-color-top-mass-2-time-1: #8c1717;
--eve-link-color-temp: orange;
--eve-effect-pulsar: #40aef5;
--eve-effect-magnetar: #f058f8;
--eve-effect-wolfRayet: #ef7843;
--eve-effect-blackHole: #1b1b1b;
--eve-effect-cataclysmicVariable: #ffea90;
--eve-effect-redGiant: #fd3c3c;
--eve-effect-dazhLiminalityLocus: #ff6464;
--eve-effect-imperialStellarObservatory: #6991ce;
--eve-effect-stateStellarObservatory: #6991ce;
--eve-effect-republicStellarObservatory: #6991ce;
--eve-effect-federalStellarObservatory: #6991ce;
--eve-wh-type-color-high: #5dffd2;
--eve-wh-type-color-low: #f79400;
--eve-wh-type-color-null: #fc3c3c;
--eve-wh-type-color-c1: #69bfce;
--eve-wh-type-color-c2: #6991ce;
--eve-wh-type-color-c3: #a8cb70;
--eve-wh-type-color-c4: #e39c68;
--eve-wh-type-color-c5: #de8686;
--eve-wh-type-color-c6: #e76363;
--eve-wh-type-color-c13: #988cb5;
--eve-wh-type-color-drifter: #ff44f6;
--eve-wh-type-color-thera: #ffffff;
--eve-wh-type-color-zarzakh: #212121;
--eve-security-color-10: #2c74df;
--eve-security-color-09: #3998e8;
--eve-security-color-08: #4dcbf5;
--eve-security-color-07: #60d8a2;
--eve-security-color-06: #71e454;
--eve-security-color-05: #f2fc81;
--eve-security-color-04: #d96c07;
--eve-security-color-03: #cb440f;
--eve-security-color-02: #b91117;
--eve-security-color-01: #732020;
--eve-security-color-00: #8b3263;
--eve-security-color-m-01: #8b3263;
--eve-security-color-m-02: #8b3263;
--eve-security-color-m-03: #8b3263;
--eve-security-color-m-04: #8b3263;
--eve-security-color-m-05: #8b3263;
--eve-security-color-m-06: #8b3263;
--eve-security-color-m-07: #8b3263;
--eve-security-color-m-08: #8b3263;
--eve-security-color-m-09: #8b3263;
--eve-security-color-m-10: #8b3263;
--eve-solar-system-status-unknown: transparent;
--eve-solar-system-status-color-unknown: transparent;
--eve-solar-system-status-home: #{$homeAlpha};
--eve-solar-system-status-color-home: #{$homeBase};
--eve-solar-system-status-color-background: #{$homeBackground};
--eve-solar-system-status-color-home-dark30: #{$homeDark30};
--eve-solar-system-status-friendly: #{$friendlyAlpha};
--eve-solar-system-status-color-friendly: #{$friendlyBase};
--eve-solar-system-status-friendly-dark30: #{$friendlyDark30};
--eve-solar-system-status-color-friendly-dark20: #{$friendlyDark20};
--eve-solar-system-status-color-friendly-dark5: #{$friendlyDark5};
--eve-solar-system-status-lookingFor: #{$lookingForAlpha};
--eve-solar-system-status-color-lookingFor: #{$lookingForBase};
--eve-solar-system-status-color-lookingFor-dark15: #{$lookingForDark15};
--eve-solar-system-status-warning: #906518a6;
--eve-solar-system-status-color-warning: #ffb93b;
--eve-solar-system-status-target: #b439ff6b;
--eve-solar-system-status-color-target: #b439ff;
--eve-solar-system-status-dangerous: #d54040;
--eve-solar-system-status-color-dangerous: #d54040;
--conn-time-eol: #7452c3e3;
--conn-frigate: #325d88;
--conn-save: rgba(155, 102, 45, 0.85);
--selected-item-bg: rgba(98, 98, 98, 0.33);
}
$eve-solar-system-status-color-unknown: transparent;
$eve-solar-system-status-color-friendly: #3bbd39;
$eve-solar-system-status-color-warning: #ffb93b;
$eve-solar-system-status-color-target: #b439ff;
$eve-solar-system-status-color-dangerous: #d54040;
$eve-solar-system-status-color-lookingFor: #43c2fd;
$eve-solar-system-status-color-home: rgb(197, 253, 67);

View File

@@ -1,504 +1,535 @@
@import './eve-common-variables';
@import "eve-common-variables";
.eve-wh-effect-color-pulsar {
fill: var(--eve-effect-pulsar);
background-color: var(--eve-effect-pulsar);
fill: $eve-effect-pulsar;
background-color: $eve-effect-pulsar;
}
.eve-wh-effect-color-magnetar {
fill: var(--eve-effect-magnetar);
background-color: var(--eve-effect-magnetar);
fill: $eve-effect-magnetar;
background-color: $eve-effect-magnetar;
}
.eve-wh-effect-color-wolfRayet {
fill: var(--eve-effect-wolfRayet);
background-color: var(--eve-effect-wolfRayet);
fill: $eve-effect-wolfRayet;
background-color: $eve-effect-wolfRayet;
}
.eve-wh-effect-color-blackHole {
fill: var(--eve-effect-blackHole);
background-color: var(--eve-effect-blackHole);
box-shadow: 0 0 8px rgba(255, 255, 255, 0.33);
fill: $eve-effect-blackHole;
background-color: $eve-effect-blackHole;
box-shadow: 0 0 8px rgba(255 255 255 / 33);
}
.eve-wh-effect-color-cataclysmicVariable {
fill: var(--eve-effect-cataclysmicVariable);
background-color: var(--eve-effect-cataclysmicVariable);
fill: $eve-effect-cataclysmicVariable;
background-color: $eve-effect-cataclysmicVariable;
}
.eve-wh-effect-color-redGiant {
fill: var(--eve-effect-redGiant);
background-color: var(--eve-effect-redGiant);
fill: $eve-effect-redGiant;
background-color: $eve-effect-redGiant;
}
.text-eve-wh-effect-color-pulsar {
color: var(--eve-effect-pulsar);
color: $eve-effect-pulsar;
}
.text-eve-wh-effect-color-magnetar {
color: var(--eve-effect-magnetar);
color: $eve-effect-magnetar;
}
.text-eve-wh-effect-color-wolfRayet {
color: var(--eve-effect-wolfRayet);
color: $eve-effect-wolfRayet;
}
.text-eve-wh-effect-color-blackHole {
color: #fff;
}
.text-eve-wh-effect-color-cataclysmicVariable {
color: var(--eve-effect-cataclysmicVariable);
color: $eve-effect-cataclysmicVariable;
}
.text-eve-wh-effect-color-redGiant {
color: var(--eve-effect-redGiant);
color: $eve-effect-redGiant;
}
.text-eve-wh-effect-color-dazhLiminalityLocus {
color: var(--eve-effect-dazhLiminalityLocus);
color: $eve-effect-dazhLiminalityLocus;
}
.text-eve-wh-effect-color-imperialStellarObservatory {
color: var(--eve-effect-imperialStellarObservatory);
color: $eve-effect-imperialStellarObservatory;
}
.text-eve-wh-effect-color-stateStellarObservatory {
color: var(--eve-effect-stateStellarObservatory);
color: $eve-effect-stateStellarObservatory;
}
.text-eve-wh-effect-color-republicStellarObservatory {
color: var(--eve-effect-republicStellarObservatory);
color: $eve-effect-republicStellarObservatory;
}
.text-eve-wh-effect-color-federalStellarObservatory {
color: var(--eve-effect-federalStellarObservatory);
color: $eve-effect-federalStellarObservatory;
}
/* Security color classes */
.eve-security-color-10 {
color: var(--eve-security-color-10) !important;
fill: var(--eve-security-color-10);
color: $eve-security-color-10 !important;
fill: $eve-security-color-10;
}
.eve-security-color-09 {
color: var(--eve-security-color-09) !important;
fill: var(--eve-security-color-09);
color: $eve-security-color-09 !important;
fill: $eve-security-color-09;
}
.eve-security-color-08 {
color: var(--eve-security-color-08) !important;
fill: var(--eve-security-color-08);
color: $eve-security-color-08 !important;
fill: $eve-security-color-08;
}
.eve-security-color-07 {
color: var(--eve-security-color-07) !important;
fill: var(--eve-security-color-07);
color: $eve-security-color-07 !important;
fill: $eve-security-color-07;
}
.eve-security-color-06 {
color: var(--eve-security-color-06) !important;
fill: var(--eve-security-color-06);
color: $eve-security-color-06 !important;
fill: $eve-security-color-06;
}
.eve-security-color-05 {
color: var(--eve-security-color-05) !important;
fill: var(--eve-security-color-05);
color: $eve-security-color-05 !important;
fill: $eve-security-color-05;
}
.eve-security-color-04 {
color: var(--eve-security-color-04) !important;
fill: var(--eve-security-color-04);
color: $eve-security-color-04 !important;
fill: $eve-security-color-04;
}
.eve-security-color-03 {
color: var(--eve-security-color-03) !important;
fill: var(--eve-security-color-03);
color: $eve-security-color-03 !important;
fill: $eve-security-color-03;
}
.eve-security-color-02 {
color: var(--eve-security-color-02) !important;
fill: var(--eve-security-color-02);
color: $eve-security-color-02 !important;
fill: $eve-security-color-02;
}
.eve-security-color-01 {
color: var(--eve-security-color-01) !important;
fill: var(--eve-security-color-01);
color: $eve-security-color-01 !important;
fill: $eve-security-color-01;
}
.eve-security-color-00 {
color: var(--eve-security-color-00) !important;
fill: var(--eve-security-color-00);
color: $eve-security-color-00 !important;
fill: $eve-security-color-00;
}
.eve-security-color-m-01 {
color: var(--eve-security-color-m-01) !important;
fill: var(--eve-security-color-m-01);
color: $eve-security-color-m-01 !important;
fill: $eve-security-color-m-01;
}
.eve-security-color-m-02 {
color: var(--eve-security-color-m-02) !important;
fill: var(--eve-security-color-m-02);
color: $eve-security-color-m-02 !important;
fill: $eve-security-color-m-02;
}
.eve-security-color-m-03 {
color: var(--eve-security-color-m-03) !important;
fill: var(--eve-security-color-m-03);
color: $eve-security-color-m-03 !important;
fill: $eve-security-color-m-03;
}
.eve-security-color-m-04 {
color: var(--eve-security-color-m-04) !important;
fill: var(--eve-security-color-m-04);
color: $eve-security-color-m-04 !important;
fill: $eve-security-color-m-04;
}
.eve-security-color-m-05 {
color: var(--eve-security-color-m-05) !important;
fill: var(--eve-security-color-m-05);
color: $eve-security-color-m-05 !important;
fill: $eve-security-color-m-05;
}
.eve-security-color-m-06 {
color: var(--eve-security-color-m-06) !important;
fill: var(--eve-security-color-m-06);
color: $eve-security-color-m-06 !important;
fill: $eve-security-color-m-06;
}
.eve-security-color-m-07 {
color: var(--eve-security-color-m-07) !important;
fill: var(--eve-security-color-m-07);
color: $eve-security-color-m-07 !important;
fill: $eve-security-color-m-07;
}
.eve-security-color-m-08 {
color: var(--eve-security-color-m-08) !important;
fill: var(--eve-security-color-m-08);
color: $eve-security-color-m-08 !important;
fill: $eve-security-color-m-08;
}
.eve-security-color-m-09 {
color: var(--eve-security-color-m-09) !important;
fill: var(--eve-security-color-m-09);
color: $eve-security-color-m-09 !important;
fill: $eve-security-color-m-09;
}
.eve-security-color-m-10 {
color: var(--eve-security-color-m-10) !important;
fill: var(--eve-security-color-m-10);
color: $eve-security-color-m-10 !important;
fill: $eve-security-color-m-10;
}
/* Security backgrounds */
.eve-security-background-10 {
background-color: var(--eve-security-color-10);
fill: var(--eve-security-color-10);
background-color: $eve-security-color-10;
fill: $eve-security-color-10;
}
.eve-security-background-09 {
background-color: var(--eve-security-color-09);
fill: var(--eve-security-color-09);
background-color: $eve-security-color-09;
fill: $eve-security-color-09;
}
.eve-security-background-08 {
background-color: var(--eve-security-color-08);
fill: var(--eve-security-color-08);
background-color: $eve-security-color-08;
fill: $eve-security-color-08;
}
.eve-security-background-07 {
background-color: var(--eve-security-color-07);
fill: var(--eve-security-color-07);
background-color: $eve-security-color-07;
fill: $eve-security-color-07;
}
.eve-security-background-06 {
background-color: var(--eve-security-color-06);
fill: var(--eve-security-color-06);
background-color: $eve-security-color-06;
fill: $eve-security-color-06;
}
.eve-security-background-05 {
background-color: var(--eve-security-color-05);
fill: var(--eve-security-color-05);
background-color: $eve-security-color-05;
fill: $eve-security-color-05;
}
.eve-security-background-04 {
background-color: var(--eve-security-color-04);
fill: var(--eve-security-color-04);
background-color: $eve-security-color-04;
fill: $eve-security-color-04;
}
.eve-security-background-03 {
background-color: var(--eve-security-color-03);
fill: var(--eve-security-color-03);
background-color: $eve-security-color-03;
fill: $eve-security-color-03;
}
.eve-security-background-02 {
background-color: var(--eve-security-color-02);
fill: var(--eve-security-color-02);
background-color: $eve-security-color-02;
fill: $eve-security-color-02;
}
.eve-security-background-01 {
background-color: var(--eve-security-color-01);
fill: var(--eve-security-color-01);
background-color: $eve-security-color-01;
fill: $eve-security-color-01;
}
.eve-security-background-00 {
background-color: var(--eve-security-color-00);
fill: var(--eve-security-color-00);
background-color: $eve-security-color-00;
fill: $eve-security-color-00;
}
.eve-security-background-m-01 {
background-color: var(--eve-security-color-m-01);
fill: var(--eve-security-color-m-01);
background-color: $eve-security-color-m-01;
fill: $eve-security-color-m-01;
}
.eve-security-background-m-02 {
background-color: var(--eve-security-color-m-02);
fill: var(--eve-security-color-m-02);
background-color: $eve-security-color-m-02;
fill: $eve-security-color-m-02;
}
.eve-security-background-m-03 {
background-color: var(--eve-security-color-m-03);
fill: var(--eve-security-color-m-03);
background-color: $eve-security-color-m-03;
fill: $eve-security-color-m-03;
}
.eve-security-background-m-04 {
background-color: var(--eve-security-color-m-04);
fill: var(--eve-security-color-m-04);
background-color: $eve-security-color-m-04;
fill: $eve-security-color-m-04;
}
.eve-security-background-m-05 {
background-color: var(--eve-security-color-m-05);
fill: var(--eve-security-color-m-05);
background-color: $eve-security-color-m-05;
fill: $eve-security-color-m-05;
}
.eve-security-background-m-06 {
background-color: var(--eve-security-color-m-06);
fill: var(--eve-security-color-m-06);
background-color: $eve-security-color-m-06;
fill: $eve-security-color-m-06;
}
.eve-security-background-m-07 {
background-color: var(--eve-security-color-m-07);
fill: var(--eve-security-color-m-07);
background-color: $eve-security-color-m-07;
fill: $eve-security-color-m-07;
}
.eve-security-background-m-08 {
background-color: var(--eve-security-color-m-08);
fill: var(--eve-security-color-m-08);
background-color: $eve-security-color-m-08;
fill: $eve-security-color-m-08;
}
.eve-security-background-m-09 {
background-color: var(--eve-security-color-m-09);
fill: var(--eve-security-color-m-09);
background-color: $eve-security-color-m-09;
fill: $eve-security-color-m-09;
}
.eve-security-background-m-10 {
background-color: var(--eve-security-color-m-10);
fill: var(--eve-security-color-m-10);
background-color: $eve-security-color-m-10;
fill: $eve-security-color-m-10;
}
/* WH Type color classes */
.eve-wh-type-color-high {
color: var(--eve-wh-type-color-high) !important;
fill: var(--eve-wh-type-color-high);
color: $eve-wh-type-color-high;
fill: $eve-wh-type-color-high;
font-weight: bold !important;
}
.eve-wh-type-color-low {
color: var(--eve-wh-type-color-low) !important;
fill: var(--eve-wh-type-color-low);
color: $eve-wh-type-color-low;
fill: $eve-wh-type-color-low;
font-weight: bold !important;
}
.eve-wh-type-color-null {
color: var(--eve-wh-type-color-null) !important;
fill: var(--eve-wh-type-color-null);
color: $eve-wh-type-color-null;
fill: $eve-wh-type-color-null;
font-weight: bold !important;
}
.eve-wh-type-color-c1 {
color: var(--eve-wh-type-color-c1) !important;
fill: var(--eve-wh-type-color-c1);
color: $eve-wh-type-color-c1 !important;
fill: $eve-wh-type-color-c1;
font-weight: bold !important;
}
.eve-wh-type-color-c2 {
color: var(--eve-wh-type-color-c2) !important;
fill: var(--eve-wh-type-color-c2);
color: $eve-wh-type-color-c2 !important;
fill: $eve-wh-type-color-c2;
font-weight: bold !important;
}
.eve-wh-type-color-c3 {
color: var(--eve-wh-type-color-c3) !important;
fill: var(--eve-wh-type-color-c3);
color: $eve-wh-type-color-c3 !important;
fill: $eve-wh-type-color-c3;
font-weight: bold !important;
}
.eve-wh-type-color-c4 {
color: var(--eve-wh-type-color-c4) !important;
fill: var(--eve-wh-type-color-c4);
color: $eve-wh-type-color-c4 !important;
fill: $eve-wh-type-color-c4;
font-weight: bold !important;
}
.eve-wh-type-color-c5 {
color: var(--eve-wh-type-color-c5) !important;
fill: var(--eve-wh-type-color-c5);
color: $eve-wh-type-color-c5 !important;
fill: $eve-wh-type-color-c5;
font-weight: bold !important;
}
.eve-wh-type-color-c6 {
color: var(--eve-wh-type-color-c6) !important;
fill: var(--eve-wh-type-color-c6);
color: $eve-wh-type-color-c6 !important;
fill: $eve-wh-type-color-c6;
font-weight: bold !important;
}
.eve-wh-type-color-c13 {
color: var(--eve-wh-type-color-c13) !important;
fill: var(--eve-wh-type-color-c13);
color: $eve-wh-type-color-c13 !important;
fill: $eve-wh-type-color-c13;
}
.eve-wh-type-color-drifter {
color: var(--eve-wh-type-color-drifter) !important;
fill: var(--eve-wh-type-color-drifter);
color: $eve-wh-type-color-drifter !important;
fill: $eve-wh-type-color-drifter;
}
.eve-wh-type-color-thera {
color: var(--eve-wh-type-color-thera) !important;
fill: var(--eve-wh-type-color-thera);
color: $eve-wh-type-color-thera !important;
fill: $eve-wh-type-color-thera;
}
/* WH Type backgrounds */
.eve-wh-type-background-high {
background-color: var(--eve-wh-type-color-high);
}
.eve-wh-type-background-low {
background-color: var(--eve-wh-type-color-low);
}
.eve-wh-type-background-null {
background-color: var(--eve-wh-type-color-null);
}
.eve-wh-type-background-c1 {
background-color: var(--eve-wh-type-color-c1);
}
.eve-wh-type-background-c2 {
background-color: var(--eve-wh-type-color-c2);
}
.eve-wh-type-background-c3 {
background-color: var(--eve-wh-type-color-c3);
}
.eve-wh-type-background-c4 {
background-color: var(--eve-wh-type-color-c4);
}
.eve-wh-type-background-c5 {
background-color: var(--eve-wh-type-color-c5);
}
.eve-wh-type-background-c6 {
background-color: var(--eve-wh-type-color-c6);
}
.eve-wh-type-background-c13 {
background-color: var(--eve-wh-type-color-c13);
}
.eve-wh-type-background-drifter {
background-color: var(--eve-wh-type-color-drifter);
}
.eve-wh-type-background-thera {
background-color: var(--eve-wh-type-color-thera);
}
.eve-wh-type-background-zarzakh {
background-color: var(--eve-wh-type-color-zarzakh);
background-color: $eve-wh-type-color-high;
}
/* Kind color classes */
.eve-kind-color-high {
color: var(--eve-wh-type-color-high);
fill: var(--eve-wh-type-color-high);
.eve-wh-type-background-low {
background-color: $eve-wh-type-color-low;
}
.eve-wh-type-background-null {
background-color: $eve-wh-type-color-null;
}
.eve-wh-type-background-c1 {
background-color: $eve-wh-type-color-c1;
}
.eve-wh-type-background-c2 {
background-color: $eve-wh-type-color-c2;
}
.eve-wh-type-background-c3 {
background-color: $eve-wh-type-color-c3;
}
.eve-wh-type-background-c4 {
background-color: $eve-wh-type-color-c4;
}
.eve-wh-type-background-c5 {
background-color: $eve-wh-type-color-c5;
}
.eve-wh-type-background-c6 {
background-color: $eve-wh-type-color-c6;
}
.eve-wh-type-background-c13 {
background-color: $eve-wh-type-color-c13;
}
.eve-wh-type-background-drifter {
background-color: $eve-wh-type-color-drifter;
}
.eve-wh-type-background-thera {
background-color: $eve-wh-type-color-thera;
}
.eve-wh-type-background-zarzakh {
background-color: $eve-wh-type-color-zarzakh;
}
.eve-kind-color-high {
color: $eve-wh-type-color-high;
fill: $eve-wh-type-color-high;
}
.eve-kind-color-low {
color: var(--eve-wh-type-color-low);
fill: var(--eve-wh-type-color-low);
color: $eve-wh-type-color-low;
fill: $eve-wh-type-color-low;
font-weight: bold;
}
.eve-kind-color-null {
color: var(--eve-wh-type-color-null);
fill: var(--eve-wh-type-color-null);
color: $eve-wh-type-color-null;
fill: $eve-wh-type-color-null;
}
.eve-kind-color-wh {
color: var(--eve-wh-type-color-c6);
fill: var(--eve-wh-type-color-c6);
color: $eve-wh-type-color-c6;
fill: $eve-wh-type-color-c6;
}
.eve-kind-color-thera {
color: var(--eve-wh-type-color-thera);
fill: var(--eve-wh-type-color-thera);
color: $eve-wh-type-color-thera;
fill: $eve-wh-type-color-thera;
}
.eve-kind-color-abyss {
color: var(--eve-wh-type-color-c6);
fill: var(--eve-wh-type-color-c6);
color: $eve-wh-type-color-c6;
fill: $eve-wh-type-color-c6;
}
.eve-kind-color-penalty {
color: var(--eve-wh-type-color-c6);
fill: var(--eve-wh-type-color-c6);
color: $eve-wh-type-color-c6;
fill: $eve-wh-type-color-c6;
}
.eve-kind-color-pochven {
color: var(--eve-wh-type-color-c6);
fill: var(--eve-wh-type-color-c6);
color: $eve-wh-type-color-c6;
fill: $eve-wh-type-color-c6;
}
.eve-kind-color-zarzakh {
color: var(--eve-wh-type-color-zarzakh);
fill: var(--eve-wh-type-color-zarzakh);
color: $eve-wh-type-color-zarzakh;
fill: $eve-wh-type-color-zarzakh;
}
/* Kind backgrounds */
.eve-kind-background-high {
background-color: var(--eve-wh-type-color-high);
background-color: $eve-wh-type-color-high;
}
.eve-kind-background-low {
background-color: var(--eve-wh-type-color-low);
background-color: $eve-wh-type-color-low;
}
.eve-kind-background-null {
background-color: var(--eve-wh-type-color-null);
background-color: $eve-wh-type-color-null;
}
.eve-kind-background-wh {
background-color: var(--eve-wh-type-color-c6);
background-color: $eve-wh-type-color-c6;
}
.eve-kind-background-thera {
background-color: var(--eve-wh-type-color-thera);
background-color: $eve-wh-type-color-thera;
}
.eve-kind-background-abyss {
background-color: var(--eve-wh-type-color-c6);
background-color: $eve-wh-type-color-c6;
}
.eve-kind-background-penalty {
background-color: var(--eve-wh-type-color-c6);
background-color: $eve-wh-type-color-c6;
}
.eve-kind-background-pochven {
background-color: var(--eve-wh-type-color-c6);
background-color: $eve-wh-type-color-c6;
}
.eve-kind-background-zarzakh {
background-color: var(--eve-wh-type-color-zarzakh);
background-color: $eve-wh-type-color-zarzakh;
}
/* System status color classes */
.eve-system-status-color-clear {
color: var(--eve-solar-system-status-color-unknown);
color: $eve-solar-system-status-color-unknown;
}
.eve-system-status-color-home {
color: var(--eve-solar-system-status-color-home);
color: $eve-solar-system-status-color-home;
}
.eve-system-status-color-friendly {
color: var(--eve-solar-system-status-color-friendly);
color: $eve-solar-system-status-color-friendly;
}
.eve-system-status-color-lookingFor {
color: var(--eve-solar-system-status-color-lookingFor);
color: $eve-solar-system-status-color-lookingFor;
}
.eve-system-status-color-warning {
color: var(--eve-solar-system-status-color-warning);
color: $eve-solar-system-status-color-warning;
}
.eve-system-status-color-target {
color: var(--eve-solar-system-status-color-target);
color: $eve-solar-system-status-color-target;
}
.eve-system-status-color-dangerous {
color: var(--eve-solar-system-status-color-dangerous);
}
.eve-system-status-clear {
background-color: var(--eve-solar-system-status-unknown);
}
.eve-system-status-home {
background-color: var(--eve-solar-system-status-home);
}
.eve-system-status-friendly {
background-color: var(--eve-solar-system-status-friendly);
}
.eve-system-status-lookingFor {
background-color: var(--eve-solar-system-status-lookingFor);
}
.eve-system-status-warning {
background-color: var(--eve-solar-system-status-warning);
}
.eve-system-status-target {
background-color: var(--eve-solar-system-status-target);
}
.eve-system-status-dangerous {
background-color: var(--eve-solar-system-status-dangerous);
}
.eve-system-status-clear {
background-color: var(--eve-solar-system-status-unknown);
color: var(--eve-solar-system-status-color-unknown);
}
.eve-system-status-home {
background-color: var(--eve-solar-system-status-home);
color: var(--eve-solar-system-status-color-home);
}
.eve-system-status-friendly {
background-color: var(--eve-solar-system-status-friendly);
color: var(--eve-solar-system-status-color-friendly);
}
.eve-system-status-lookingFor {
background-color: var(--eve-solar-system-status-lookingFor);
color: var(--eve-solar-system-status-color-lookingFor);
}
.eve-system-status-warning {
background-color: var(--eve-solar-system-status-warning);
color: var(--eve-solar-system-status-color-warning);
}
.eve-system-status-target {
background-color: var(--eve-solar-system-status-target);
color: var(--eve-solar-system-status-color-target);
}
.eve-system-status-dangerous {
background-color: var(--eve-solar-system-status-dangerous);
color: var(--eve-solar-system-status-color-dangerous);
color: $eve-solar-system-status-color-dangerous;
}
.wd-route-system-shape-triangle {
clip-path: polygon(50% 0, 0 100%, 100% 100%);
}
.wd-route-system-shape-circle {
border-radius: 40%;
}
/* Some additional background classes */
.wd-marker-bookmark-color-shattered {
background-color: #833ca4;
margin-top: 1px;
}
.wd-marker-bookmark-color-custom {
background-color: #282828;
border: 1px solid #4c4c4c;
@@ -541,33 +572,3 @@
.wd-marker-bookmark-color-danger {
background-color: #d10600;
}
.react-flow__minimap-node {
fill: #ffb03a;
}
.react-flow__minimap {
border: 1px solid #282828;
border-radius: 4px;
background-color: rgb(47 37 37) !important;
overflow: hidden;
}
.react-flow__minimap-mask {
stroke-width: 2px;
fill: rgba(0, 0, 0, 0.5);
mix-blend-mode: overlay;
}
.react-flow__minimap-mask {
stroke-width: 2px;
fill: rgb(0 0 0 / 50%) !important;
mix-blend-mode: inherit;
opacity: 1;
stroke: #fff;
}
.context-menu-active {
background-color: rgba(131, 131, 131, 0.33);
}

View File

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

View File

@@ -0,0 +1,74 @@
$pastel-blue: #5a7d9a;
$pastel-pink: #d291bc;
$pastel-green: #88b04b;
$pastel-yellow: #ffdd59;
$dark-bg: #2d2d2d;
$text-color: #ffffff;
$tooltip-bg: #202020;
.react-flow {
// background-color: $dark-bg;
color: $text-color;
&__node {
//cursor: auto;
}
&__pane {
cursor: auto;
}
//&__edge {
// stroke: $pastel-pink;
// stroke-width: 2px;
//
// &.selected {
// stroke: $pastel-yellow;
// }
//}
&__handle {
//background-color: $pastel-green;
//box-shadow: 0 0 5px rgba($pastel-green, 0.5);
}
&__minimap {
background-color: rgba(66, 66, 66, 1);
opacity: 0.7;
//backdrop-filter: blur(5px);
border: 1px solid #2f2f2f;
border-radius: 4px;
overflow: hidden;
}
&__minimap-mask {
fill: rgba(28, 28, 28, 0.75);
}
&__controls {
filter: brightness(1.5);
}
&__minimap-node {
fill: #ffb03a;
}
}
.context-menu-active {
background-color: rgba(131, 131, 131, 0.33);
}
.p-dialog {
.p-dialog-header {
height: 40px;
padding: 1rem;
padding-right: 10px !important;
}
.p-dialog-title {
font-size: 1rem !important;
}
.p-dialog-header-icons {
align-self: initial !important;
}
}

View File

@@ -0,0 +1,7 @@
$pastel-blue: #5a7d9a;
$pastel-pink: #d291bc;
$pastel-green: #88b04b;
$pastel-yellow: #ffdd59;
$dark-bg: #2d2d2d;
$text-color: #ffffff;
$tooltip-bg: #202020;

View File

@@ -1,54 +0,0 @@
@import './eve-common-variables';
@import './eve-common';
@import url('https://fonts.googleapis.com/css2?family=Oxygen:wght@300;400;700&display=swap');
$homeBase: rgb(197, 253, 67);
$homeAlpha: rgba(197, 253, 67, 0.32);
$homeDark30: darken($homeBase, 30%);
.pathfinder-theme {
/* -- Override values from the default theme -- */
--rf-bg-color: #000000;
--rf-soft-bg-color: #282828;
--rf-node-soft-bg-color: #313335;
--rf-node-font-weight: bold;
--rf-text-color: #adadad;
--rf-region-name: var(--rf-text-color);
--rf-custom-name: var(--rf-text-color);
--rf-bg-variant: "lines";
--rf-bg-gap: 34;
--rf-snap-size: 17;
--rf-bg-pattern-color: #313131;
--rf-local-counter-font-weight: 700;
/* Additional node-specific overrides */
--rf-node-line-height: normal;
--rf-node-font-family: 'Oxygen', sans-serif;
--rf-tag-color: #fbbf24;
/* -- theme-specific variables -- */
--eve-effect-pulsar: #428bca;
--eve-effect-magnetar: #e06fdf;
--eve-effect-wolfRayet: #e28a0d;
--eve-effect-blackHole: #000000;
--eve-effect-cataclysmicVariable: #ffffbb;
--eve-effect-redGiant: #d9534f;
--eve-wh-type-color-high: #5cb85c;
--eve-wh-type-color-low: #e28a0d;
--eve-wh-type-color-null: #d9534f;
--eve-wh-type-color-c1: #428bca;
--eve-wh-type-color-c2: #428bca;
--eve-wh-type-color-c3: #e28a0d;
--eve-wh-type-color-c4: #e28a0d;
--eve-wh-type-color-c5: #d9534f;
--eve-wh-type-color-c6: #d9534f;
--eve-wh-type-color-c13: #7986cb;
--eve-wh-type-color-drifter: #44aa82;
--rf-node-local-counter: #5cb85c;
--rf-has-user-characters: #ffc75d;
--eve-solar-system-status-home: #{$homeAlpha};
--eve-solar-system-status-color-home: #{$homeBase};
}

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

@@ -1,30 +1,77 @@
import { useMemo } from 'react';
import { WindowManager } from '@/hooks/Mapper/components/ui-kit/WindowManager';
import { DEFAULT_WIDGETS } from '@/hooks/Mapper/components/mapInterface/constants.tsx';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import 'react-grid-layout/css/styles.css';
import 'react-resizable/css/styles.css';
import { WidgetGridItem, WidgetsGrid } from '@/hooks/Mapper/components/mapInterface/components';
import {
LocalCharacters,
RoutesWidget,
SystemInfo,
SystemSignatures,
} from '@/hooks/Mapper/components/mapInterface/widgets';
import { useState } from 'react';
import { SESSION_KEY } from '@/hooks/Mapper/constants.ts';
// import { debounce } from 'lodash/debounce';
const DEFAULT_WINDOWS = [
{
name: 'info',
rightOffset: 5,
width: 5,
height: 4,
item: () => <SystemInfo />,
},
{
name: 'local',
rightOffset: 5,
topOffset: 4,
width: 5,
height: 4,
item: () => <LocalCharacters />,
},
{ name: 'signatures', width: 8, height: 4, topOffset: 8, rightOffset: 12, item: () => <SystemSignatures /> },
{
name: 'routes',
rightOffset: 0,
topOffset: 8,
width: 5,
height: 6,
item: () => <RoutesWidget />,
},
];
const saveWindowsToLS = (toSaveItems: WidgetGridItem[]) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const out = toSaveItems.map(({ item, ...rest }) => rest);
localStorage.setItem(SESSION_KEY.windows, JSON.stringify(out));
};
const restoreWindowsFromLS = (): WidgetGridItem[] => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const raw = localStorage.getItem(SESSION_KEY.windows);
if (!raw) {
return DEFAULT_WINDOWS;
}
// eslint-disable-next-line no-debugger
const out = (JSON.parse(raw) as Omit<WidgetGridItem, 'item'>[])
.filter(x => DEFAULT_WINDOWS.find(def => def.name === x.name))
.map(x => {
const windowItem = DEFAULT_WINDOWS.find(def => def.name === x.name)?.item;
return { ...x, item: windowItem! };
});
return out;
};
export const MapInterface = () => {
// const [items, setItems] = useState<WindowProps[]>(restoreWindowsFromLS);
const { windowsSettings, updateWidgetSettings } = useMapRootState();
const items = useMemo(() => {
return windowsSettings.windows
.map(x => {
const content = DEFAULT_WIDGETS.find(y => y.id === x.id)?.content;
return {
...x,
content: content!,
};
})
.filter(x => windowsSettings.visible.some(j => x.id === j));
}, [windowsSettings]);
const [items, setItems] = useState<WidgetGridItem[]>(restoreWindowsFromLS());
return (
<WindowManager
windows={items}
viewPort={windowsSettings.viewPort}
dragSelector=".react-grid-dragHandleExample"
onChange={updateWidgetSettings}
<WidgetsGrid
items={items}
onChange={x => {
saveWindowsToLS(x);
setItems(x);
}}
/>
);
};

View File

@@ -1,10 +0,0 @@
.SearchItem {
& > * {
font-size: 13px !important;
}
}
.SearchItemEffect {
font-weight: initial !important;
}

View File

@@ -1,203 +0,0 @@
import { Dialog } from 'primereact/dialog';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useCallback, useRef, useState } from 'react';
import { Button } from 'primereact/button';
import { IconField } from 'primereact/iconfield';
import { AutoComplete } from 'primereact/autocomplete';
import { OutCommand, SearchSystemItem } from '@/hooks/Mapper/types';
import { SystemViewStandalone, WHClassView, WHEffectView } from '@/hooks/Mapper/components/ui-kit';
import classes from './AddSystemDialog.module.scss';
import clsx from 'clsx';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
import { sortWHClasses } from '@/hooks/Mapper/helpers';
export type SearchOnSubmitCallback = (item: SearchSystemItem) => void;
interface AddSystemDialogProps {
title?: string;
visible: boolean;
setVisible: (visible: boolean) => void;
onSubmit?: SearchOnSubmitCallback;
excludedSystems?: number[];
}
export const AddSystemDialog = ({
title = 'Add system',
visible,
setVisible,
onSubmit,
excludedSystems = [],
}: AddSystemDialogProps) => {
const {
outCommand,
data: { wormholesData },
} = useMapRootState();
const inputRef = useRef<any>();
const onShow = useCallback(() => {
inputRef.current?.focus();
}, []);
const [filteredItems, setFilteredItems] = useState<SearchSystemItem[]>([]);
const [selectedItem, setSelectedItem] = useState<SearchSystemItem[] | null>(null);
const searchItems = useCallback(
async (event: { query: string }) => {
if (event.query.length < 2) {
setFilteredItems([]);
return;
}
const query = event.query;
if (query.length === 0) {
setFilteredItems([]);
} else {
try {
const result = await outCommand({
type: OutCommand.searchSystems,
data: {
text: query,
},
});
let prepared = (result.systems as SearchSystemItem[]).sort((a, b) => {
const amatch = a.label.indexOf(query);
const bmatch = b.label.indexOf(query);
return amatch - bmatch;
});
if (excludedSystems) {
prepared = prepared.filter(x => !excludedSystems.includes(x.system_static_info.solar_system_id));
}
setFilteredItems(prepared);
} catch (error) {
console.error('Error fetching data:', error);
setFilteredItems([]);
}
}
},
[excludedSystems, outCommand],
);
const ref = useRef({ onSubmit, selectedItem });
ref.current = { onSubmit, selectedItem };
const handleSubmit = useCallback(() => {
const { onSubmit, selectedItem } = ref.current;
setFilteredItems([]);
setSelectedItem([]);
if (!selectedItem) {
setVisible(false);
return;
}
onSubmit?.(selectedItem[0]);
setVisible(false);
}, [setVisible]);
return (
<Dialog
header={title}
visible={visible}
draggable={false}
style={{ width: '520px' }}
onShow={onShow}
onHide={() => {
if (!visible) {
return;
}
setVisible(false);
}}
>
<div className="flex flex-col gap-3 px-1.5">
<div className="flex flex-col gap-2 py-3.5">
<div className="flex flex-col gap-1">
<IconField>
<AutoComplete
ref={inputRef}
multiple
showEmptyMessage
scrollHeight="300px"
value={selectedItem}
suggestions={filteredItems}
completeMethod={searchItems}
onChange={e => {
setSelectedItem(e.value.length < 2 ? e.value : [e.value[e.value.length - 1]]);
}}
emptyMessage="Not found any system..."
placeholder="Type here..."
field="label"
id="value"
className="w-full"
itemTemplate={(item: SearchSystemItem) => {
const { security, system_class, effect_power, effect_name, statics } = item.system_static_info;
const sortedStatics = sortWHClasses(wormholesData, statics);
const isWH = isWormholeSpace(system_class);
return (
<div className={clsx('flex gap-1.5', classes.SearchItem)}>
<SystemViewStandalone
security={security}
system_class={system_class}
solar_system_id={item.value}
class_title={item.class_title}
solar_system_name={item.label}
region_name={item.region_name}
/>
{effect_name && isWH && (
<WHEffectView
effectName={effect_name}
effectPower={effect_power}
className={classes.SearchItemEffect}
/>
)}
{isWH && (
<div className="flex gap-1 grow justify-between">
<div></div>
<div className="flex gap-1">
{sortedStatics.map(x => (
<WHClassView key={x} whClassName={x} />
))}
</div>
</div>
)}
</div>
);
}}
selectedItemTemplate={(item: SearchSystemItem) => (
<SystemViewStandalone
security={item.system_static_info.security}
system_class={item.system_static_info.system_class}
solar_system_id={item.value}
class_title={item.class_title}
solar_system_name={item.label}
region_name={item.region_name}
/>
)}
/>
</IconField>
<span className="text-[12px] text-stone-400 ml-1">*to search type at least 2 symbols.</span>
</div>
</div>
<div className="flex gap-2 justify-end">
<Button
onClick={handleSubmit}
outlined
disabled={!selectedItem || selectedItem.length !== 1}
size="small"
label="Submit"
/>
</div>
</div>
</Dialog>
);
};

View File

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

View File

@@ -1,52 +0,0 @@
import { MarkdownComment } from '@/hooks/Mapper/components/mapInterface/components/Comments/components';
import { useEffect, useRef, useState } from 'react';
import { CommentType } from '@/hooks/Mapper/types';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
export interface CommentsProps {}
// eslint-disable-next-line no-empty-pattern
export const Comments = ({}: CommentsProps) => {
const [commentsList, setCommentsList] = useState<CommentType[]>([]);
const {
data: { selectedSystems },
comments: { loadComments, comments, lastUpdateKey },
} = useMapRootState();
const [systemId] = selectedSystems;
const ref = useRef({ loadComments, systemId });
ref.current = { loadComments, systemId };
useEffect(() => {
const commentsBySystem = comments.get(systemId);
if (!commentsBySystem) {
return;
}
const els = [...commentsBySystem.comments].sort((a, b) => +new Date(b.updated_at) - +new Date(a.updated_at));
setCommentsList(els);
}, [systemId, lastUpdateKey, comments]);
useEffect(() => {
ref.current.loadComments(systemId);
}, [systemId]);
if (commentsList.length === 0) {
return (
<div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm">
Not comments found here
</div>
);
}
return (
<div className="flex flex-col gap-1 whitespace-nowrap overflow-auto text-ellipsis custom-scrollbar">
{commentsList.map(({ id, text, updated_at, characterEveId }) => (
<MarkdownComment key={id} text={text} time={updated_at} characterEveId={characterEveId} id={id} />
))}
</div>
);
};

View File

@@ -1,70 +0,0 @@
.MarkdownCommentRoot {
border-left-width: 3px;
@apply text-[12px] leading-[1.2] text-stone-300 break-words;
@apply bg-gradient-to-r from-stone-600/40 via-stone-600/10 to-stone-600/0;
.h1 {
@apply text-[12px] font-normal m-0 p-0 border-none break-words whitespace-normal;
}
.h2 {
@apply text-[12px] font-normal m-0 p-0 border-none break-words whitespace-normal;
}
.h3 {
@apply text-[12px] font-normal m-0 p-0 border-none break-words whitespace-normal;
}
.h4 {
@apply text-[12px] font-normal m-0 p-0 border-none break-words whitespace-normal;
}
.h5 {
@apply text-[12px] font-normal m-0 p-0 border-none break-words whitespace-normal;
}
.h6 {
@apply text-[12px] font-normal m-0 p-0 border-none break-words whitespace-normal;
}
p {
@apply m-0 p-0 break-words whitespace-normal;
}
ul, ol {
@apply m-0 p-0 list-none;
}
li {
@apply m-0 break-words whitespace-normal;
}
blockquote {
@apply border-l-4 border-cyan-400 p-2 m-0 font-normal text-stone-300 italic break-words whitespace-normal;
}
a {
@apply text-violet-400 cursor-pointer transition-colors duration-200 break-words whitespace-normal;
&:hover {
@apply underline;
}
}
b, strong {
@apply font-bold text-green-400 break-words whitespace-normal;
}
i, em {
@apply italic text-pink-400 break-words whitespace-normal;
}
del {
@apply line-through text-stone-500 break-words whitespace-normal;
}
hr {
@apply border-none h-[1px] bg-cyan-400 opacity-50 my-2;
}
}

View File

@@ -1,95 +0,0 @@
import classes from './MarkdownComment.module.scss';
import clsx from 'clsx';
import Markdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { InfoDrawer, TimeAgo, TooltipPosition, WdImgButton } from '@/hooks/Mapper/components/ui-kit';
import remarkBreaks from 'remark-breaks';
import { useGetCacheCharacter } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { useCallback, useRef, useState } from 'react';
import { WdTransition } from '@/hooks/Mapper/components/ui-kit/WdTransition/WdTransition.tsx';
import { PrimeIcons } from 'primereact/api';
import { ConfirmPopup } from 'primereact/confirmpopup';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { OutCommand } from '@/hooks/Mapper/types';
const TOOLTIP_PROPS = { content: 'Remove comment', position: TooltipPosition.top };
const REMARK_PLUGINS = [remarkGfm, remarkBreaks];
export interface MarkdownCommentProps {
text: string;
time: string;
characterEveId: string;
id: string;
}
export const MarkdownComment = ({ text, time, characterEveId, id }: MarkdownCommentProps) => {
const char = useGetCacheCharacter(characterEveId);
const [hovered, setHovered] = useState(false);
const cpRemoveBtnRef = useRef<HTMLElement>();
const [cpRemoveVisible, setCpRemoveVisible] = useState(false);
const { outCommand } = useMapRootState();
const ref = useRef({ outCommand, id });
ref.current = { outCommand, id };
const handleDelete = useCallback(async () => {
await ref.current.outCommand({
type: OutCommand.deleteSystemComment,
data: ref.current.id,
});
}, []);
const handleMouseEnter = useCallback(() => setHovered(true), []);
const handleMouseLeave = useCallback(() => setHovered(false), []);
const handleShowCP = useCallback(() => setCpRemoveVisible(true), []);
const handleHideCP = useCallback(() => setCpRemoveVisible(false), []);
return (
<>
<InfoDrawer
labelClassName="mb-[3px]"
className={clsx(classes.MarkdownCommentRoot, 'p-1 bg-stone-700/20 ')}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
title={
<div className="flex items-center justify-between">
<div>
<span className="text-stone-500">
by <span className="text-orange-300/70">{char?.data?.name ?? ''}</span>
</span>
</div>
<WdTransition active={hovered} timeout={100}>
<div className="text-stone-500 max-h-[12px]">
{!hovered && <TimeAgo timestamp={time} />}
{hovered && (
// @ts-ignore
<div ref={cpRemoveBtnRef}>
<WdImgButton
className={clsx(PrimeIcons.TRASH, 'hover:text-red-400')}
tooltip={TOOLTIP_PROPS}
onClick={handleShowCP}
/>
</div>
)}
</div>
</WdTransition>
</div>
}
>
<Markdown remarkPlugins={REMARK_PLUGINS}>{text}</Markdown>
</InfoDrawer>
<ConfirmPopup
target={cpRemoveBtnRef.current}
visible={cpRemoveVisible}
onHide={handleHideCP}
message="Are you sure you want to delete?"
icon="pi pi-exclamation-triangle"
accept={handleDelete}
/>
</>
);
};

View File

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

View File

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

View File

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

View File

@@ -1,26 +0,0 @@
export const markdown = `
# Heading 1
## Heading 2
### Heading 3
#### Heading 4
##### Heading 5
###### Heading 6
---
## Paragraphs
This is a regular text paragraph.
Another paragraph, but with **bold** and *italic* text, as well as ~~strikethrough~~.
> This is a block quote.
> Second line of the quote.
## Links
[Link to Google](https://www.google.com)
## Horizontal Line
A block quote with ~strikethrough~ and a URL: https://reactjs.org.
---
`;

View File

@@ -1,72 +0,0 @@
import { TooltipPosition, WdImageSize, WdImgButton } from '@/hooks/Mapper/components/ui-kit';
import clsx from 'clsx';
import { PrimeIcons } from 'primereact/api';
import { MarkdownEditor } from '@/hooks/Mapper/components/mapInterface/components/MarkdownEditor';
import { useHotkey } from '@/hooks/Mapper/hooks';
import { useCallback, useRef, useState } from 'react';
import { OutCommand } from '@/hooks/Mapper/types';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
export interface CommentsEditorProps {}
export const CommentsEditor = ({}: CommentsEditorProps) => {
const [textVal, setTextVal] = useState('');
const {
data: { selectedSystems },
outCommand,
} = useMapRootState();
const [systemId] = selectedSystems;
const ref = useRef({ outCommand, systemId, textVal });
ref.current = { outCommand, systemId, textVal };
const handleFinishEdit = useCallback(async () => {
if (ref.current.textVal === '') {
return;
}
await ref.current.outCommand({
type: OutCommand.addSystemComment,
data: {
solarSystemId: ref.current.systemId,
value: ref.current.textVal,
},
});
setTextVal('');
}, []);
const handleClick = async () => {
await handleFinishEdit();
};
useHotkey(true, ['Enter'], async () => {
await handleFinishEdit();
});
return (
<MarkdownEditor
value={textVal}
onChange={setTextVal}
overlayContent={
<div className="w-full h-full flex justify-end items-end pointer-events-none pb-[1px] pr-[8px]">
<WdImgButton
disabled={textVal.length === 0}
tooltip={{
position: TooltipPosition.bottom,
content: (
<span>
Also you may use <span className="text-cyan-400">Meta + Enter</span> hotkey.
</span>
),
}}
textSize={WdImageSize.large}
className={clsx(PrimeIcons.SEND, 'text-[14px]')}
onClick={handleClick}
/>
</div>
}
/>
);
};

View File

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

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