mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Testing work with flowbite
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
import { createContext } from "react";
|
||||
|
||||
export const EventsContext = createContext({
|
||||
showSpawnMenu: (e: boolean) => {},
|
||||
toggleSpawnMenu: () => {},
|
||||
showUnitControlMenu: (e: boolean) => {},
|
||||
toggleUnitControlMenu: () => {},
|
||||
showMeasureMenu: (e: boolean) => {},
|
||||
toggleMeasureMenu: () => {},
|
||||
showDrawingMenu: (e: boolean) => {},
|
||||
toggleDrawingMenu: () => {}
|
||||
setSpawnMenuVisible: (e: boolean) => {},
|
||||
setUnitControlMenuVisible: (e: boolean) => {},
|
||||
setMeasureMenuVisible: (e: boolean) => {},
|
||||
setDrawingMenuVisible: (e: boolean) => {},
|
||||
toggleSpawnMenuVisible: () => {},
|
||||
toggleUnitControlMenuVisible: () => {},
|
||||
toggleMeasureMenuVisible: () => {},
|
||||
toggleDrawingMenuVisible: () => {},
|
||||
})
|
||||
|
||||
export const EventsProvider = EventsContext.Provider;
|
||||
|
||||
@@ -1,73 +1,5 @@
|
||||
@import "../node_modules/leaflet/dist/leaflet.css";
|
||||
|
||||
:root {
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/***************** UI *******************/
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import UI from './ui.js'
|
||||
import './index.css'
|
||||
import { setupApp } from './olympusapp.js'
|
||||
import 'flowbite';
|
||||
import { UI } from './ui.js';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
|
||||
@@ -23,6 +23,14 @@ import { DestinationPreviewHandle } from "./markers/destinationpreviewHandle";
|
||||
import { ContextActionSet } from "../unit/contextactionset";
|
||||
import { DCSLayer } from "./dcslayer";
|
||||
|
||||
import './markers/stylesheets/airbase.css'
|
||||
import './markers/stylesheets/bullseye.css'
|
||||
import './markers/stylesheets/units.css'
|
||||
|
||||
// Temporary
|
||||
import './theme.css'
|
||||
|
||||
|
||||
var hasTouchScreen = false;
|
||||
//if ("maxTouchPoints" in navigator)
|
||||
// hasTouchScreen = navigator.maxTouchPoints > 0;
|
||||
|
||||
27
frontend/react/src/map/markers/stylesheets/airbase.css
Normal file
27
frontend/react/src/map/markers/stylesheets/airbase.css
Normal file
@@ -0,0 +1,27 @@
|
||||
.airbase-icon {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.airbase-icon svg {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.airbase-icon[data-coalition="red"] svg * {
|
||||
stroke: var(--unit-background-red);
|
||||
}
|
||||
|
||||
.airbase-icon[data-coalition="blue"] svg * {
|
||||
stroke: var(--unit-background-blue);
|
||||
}
|
||||
|
||||
.airbase-icon[data-coalition="neutral"] svg * {
|
||||
stroke: var(--unit-background-neutral);
|
||||
}
|
||||
|
||||
24
frontend/react/src/map/markers/stylesheets/bullseye.css
Normal file
24
frontend/react/src/map/markers/stylesheets/bullseye.css
Normal file
@@ -0,0 +1,24 @@
|
||||
.bullseye-icon {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bullseye-icon[data-coalition="red"] svg * {
|
||||
stroke: var(--unit-background-red);
|
||||
fill: var(--unit-background-red);
|
||||
}
|
||||
|
||||
.bullseye-icon[data-coalition="blue"] svg * {
|
||||
stroke: var(--unit-background-blue);
|
||||
fill: var(--unit-background-blue);
|
||||
}
|
||||
|
||||
.bullseye-icon[data-coalition="neutral"] svg * {
|
||||
stroke: var(--unit-background-neutral);
|
||||
fill: var(--unit-background-neutral);
|
||||
}
|
||||
383
frontend/react/src/map/markers/stylesheets/units.css
Normal file
383
frontend/react/src/map/markers/stylesheets/units.css
Normal file
@@ -0,0 +1,383 @@
|
||||
/*** Unit marker elements ***/
|
||||
[data-object|="unit"] {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.unit-vvi {
|
||||
align-self: center;
|
||||
background: var(--secondary-gunmetal-grey);
|
||||
display: flex;
|
||||
justify-self: center;
|
||||
padding-bottom: calc((var(--unit-width) / 2) + var(--unit-stroke-width));
|
||||
position: absolute;
|
||||
transform-origin: bottom;
|
||||
translate: 0 -50%;
|
||||
width: var(--unit-vvi-width);
|
||||
}
|
||||
|
||||
.unit-hotgroup {
|
||||
align-content: center;
|
||||
background-color: var(--background-steel);
|
||||
border-radius: var(--border-radius-xs);
|
||||
display: none;
|
||||
height: 15px;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
transform: rotate(-45deg);
|
||||
translate: 0 -200%;
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
.unit-hotgroup-id {
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
font-size: 9px;
|
||||
font-weight: bolder;
|
||||
transform: rotate(45deg);
|
||||
translate: -1px 1px;
|
||||
}
|
||||
|
||||
.unit-icon {
|
||||
height: var(--unit-height);
|
||||
position: absolute;
|
||||
transform-origin: center;
|
||||
width: var(--unit-width);
|
||||
}
|
||||
|
||||
.unit-icon svg {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
[data-is-selected] .unit-icon::before {
|
||||
background-color: var(--unit-spotlight-fill);
|
||||
border-radius: 50%;
|
||||
content: "";
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
/*** Basic colours ***/
|
||||
[data-coalition="blue"] .unit-icon svg>*:first-child {
|
||||
fill: var(--unit-background-blue);
|
||||
}
|
||||
|
||||
[data-coalition="red"] .unit-icon svg>*:first-child {
|
||||
fill: var(--unit-background-red);
|
||||
}
|
||||
|
||||
[data-coalition="neutral"] .unit-icon svg>*:first-child {
|
||||
fill: var(--unit-background-neutral);
|
||||
}
|
||||
|
||||
[data-is-selected] .unit-icon svg>*:first-child {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
[data-is-highlighted] .unit-icon svg>*:first-child {
|
||||
stroke: white;
|
||||
}
|
||||
|
||||
/*** Cursors ***/
|
||||
[data-is-dead],
|
||||
[data-object|="unit-missile"],
|
||||
[data-object|="unit-bomb"] {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/*** Labels ***/
|
||||
[data-object|="unit"] .unit-short-label {
|
||||
color: var(--secondary-gunmetal-grey);
|
||||
font-size: var(--unit-font-size);
|
||||
font-weight: var(--unit-font-weight);
|
||||
line-height: normal;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
[data-object|="unit-groundunit"] .unit-short-label {
|
||||
transform: translateY(7px);
|
||||
}
|
||||
|
||||
/*** Health indicator ***/
|
||||
[data-object|="unit"] .unit-health {
|
||||
background: white;
|
||||
border: var(--unit-health-border-width) solid var(--secondary-dark-steel);
|
||||
border-radius: var(--border-radius-sm);
|
||||
display: none;
|
||||
height: var(--unit-health-height);
|
||||
position: absolute;
|
||||
translate: var(--unit-health-x) var(--unit-health-y);
|
||||
width: var(--unit-health-width);
|
||||
}
|
||||
|
||||
/*** Fuel indicator ***/
|
||||
[data-object|="unit"] .unit-fuel {
|
||||
background: white;
|
||||
border: var(--unit-fuel-border-width) solid var(--secondary-dark-steel);
|
||||
border-radius: var(--border-radius-sm);
|
||||
display: none;
|
||||
height: var(--unit-fuel-height);
|
||||
position: absolute;
|
||||
translate: var(--unit-fuel-x) var(--unit-fuel-y);
|
||||
width: var(--unit-fuel-width);
|
||||
}
|
||||
|
||||
[data-object|="unit"] .unit-fuel-level,
|
||||
[data-object|="unit"] .unit-health-level {
|
||||
background-color: var(--secondary-light-grey);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/*** Ammo indicator ***/
|
||||
[data-object|="unit"] .unit-ammo {
|
||||
column-gap: var(--unit-ammo-spacing);
|
||||
display: none;
|
||||
height: fit-content;
|
||||
position: absolute;
|
||||
translate: var(--unit-ammo-x) var(--unit-ammo-y);
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
[data-object|="unit"] .unit-ammo>* {
|
||||
background-color: white;
|
||||
border: var(--unit-ammo-border-width) solid var(--secondary-dark-steel);
|
||||
border-radius: 50%;
|
||||
padding: var(--unit-ammo-radius);
|
||||
}
|
||||
|
||||
/*** Unit summary ***/
|
||||
[data-object|="unit"] .unit-summary {
|
||||
color: white;
|
||||
column-gap: 6px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
justify-content: right;
|
||||
line-height: 12px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
row-gap: 1px;
|
||||
text-shadow:
|
||||
-1px -1px 0 #000,
|
||||
1px -1px 0 #000,
|
||||
-1px 1px 0 #000,
|
||||
1px 1px 0 #000;
|
||||
right: 100%;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
[data-hide-labels] [data-object|="unit"] .unit-summary {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-object|="unit"] .unit-summary>* {
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
[data-object|="unit"] .unit-summary .unit-callsign {
|
||||
color: white;
|
||||
overflow: hidden;
|
||||
text-align: right;
|
||||
transform-origin: right;
|
||||
white-space: nowrap;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
[data-object|="unit"]:hover .unit-summary .unit-callsign{
|
||||
direction: rtl;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/*** Common ***/
|
||||
[data-object|="unit"]:hover .unit-ammo,
|
||||
[data-object|="unit"]:hover .unit-health ,
|
||||
[data-object|="unit"]:hover .unit-fuel {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-has-low-fuel] .unit-fuel, [data-object|="unit"][data-has-low-health] .unit-health {
|
||||
animation: pulse 1.5s linear infinite;
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-is-in-hotgroup] .unit-hotgroup,
|
||||
[data-object|="unit"][data-is-selected] .unit-ammo,
|
||||
[data-object|="unit"][data-is-selected] .unit-fuel,
|
||||
[data-object|="unit"][data-is-selected] .unit-health,
|
||||
[data-object|="unit"][data-is-selected] .unit-selected-spotlight {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-has-fox-1] .unit-ammo>div:nth-child(1),
|
||||
[data-object|="unit"][data-has-fox-2] .unit-ammo>div:nth-child(2),
|
||||
[data-object|="unit"][data-has-fox-3] .unit-ammo>div:nth-child(3),
|
||||
[data-object|="unit"][data-has-other-ammo] .unit-ammo>div:nth-child(4) {
|
||||
background-color: var(--secondary-gunmetal-grey);
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-coalition="blue"][data-is-selected] .unit-short-label {
|
||||
color: var(--secondary-blue-text);
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-coalition="blue"] .unit-fuel-level,
|
||||
[data-object|="unit"][data-coalition="blue"] .unit-health-level,
|
||||
[data-object|="unit"][data-coalition="blue"][data-has-fox-1] .unit-ammo>div:nth-child(1),
|
||||
[data-object|="unit"][data-coalition="blue"][data-has-fox-2] .unit-ammo>div:nth-child(2),
|
||||
[data-object|="unit"][data-coalition="blue"][data-has-fox-3] .unit-ammo>div:nth-child(3),
|
||||
[data-object|="unit"][data-coalition="blue"][data-has-other-ammo] .unit-ammo>div:nth-child(4) {
|
||||
background-color: var(--primary-blue);
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-coalition="blue"] .unit-vvi {
|
||||
background-color: var(--secondary-blue-outline);
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-coalition="red"][data-is-selected] .unit-short-label {
|
||||
color: var(--secondary-red-text);
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-coalition="red"] .unit-fuel-level,
|
||||
[data-object|="unit"][data-coalition="red"] .unit-health-level,
|
||||
[data-object|="unit"][data-coalition="red"][data-has-fox-1] .unit-ammo>div:nth-child(1),
|
||||
[data-object|="unit"][data-coalition="red"][data-has-fox-2] .unit-ammo>div:nth-child(2),
|
||||
[data-object|="unit"][data-coalition="red"][data-has-fox-3] .unit-ammo>div:nth-child(3),
|
||||
[data-object|="unit"][data-coalition="red"][data-has-other-ammo] .unit-ammo>div:nth-child(4) {
|
||||
background-color: var(--primary-red);
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-coalition="blue"] .unit-vvi {
|
||||
background-color: var(--secondary-red-outline);
|
||||
}
|
||||
|
||||
/*** Unit state ***/
|
||||
[data-object|="unit"] .unit-state {
|
||||
background-repeat: no-repeat;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="rtb"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/rtb.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="land"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/rtb.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="idle"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/idle.svg");
|
||||
}
|
||||
|
||||
[data-object*="groundunit"][data-state="idle"] .unit-state,
|
||||
[data-object*="navyunit"][data-state="idle"] .unit-state {
|
||||
background-image: url(""); /* To avoid clutter, dont show the idle state for non flying units */
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="attack"] .unit-state,
|
||||
[data-object|="unit"][data-state="bomb-point"] .unit-state,
|
||||
[data-object|="unit"][data-state="carpet-bombing"] .unit-state,
|
||||
[data-object|="unit"][data-state="fire-at-area"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/attack.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="follow"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/follow.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="refuel"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/refuel.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="human"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/human.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="dcs"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/dcs.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="land-at-point"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/land-at-point.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="no-task"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/no-task.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="off"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/off.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="tanker"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/tanker.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="AWACS"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/awacs.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="miss-on-purpose"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/miss-on-purpose.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="scenic-aaa"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/scenic-aaa.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="simulate-fire-fight"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/simulate-fire-fight.svg");
|
||||
}
|
||||
|
||||
|
||||
[data-object|="unit"] .unit-health::before {
|
||||
background-image: url("/resources/theme/images/icons/health.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
content: " ";
|
||||
height: 6px;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
translate: -10px -2px;
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
|
||||
/*** Dead unit ***/
|
||||
[data-object|="unit"][data-is-dead] .unit-selected-spotlight,
|
||||
[data-object|="unit"][data-is-dead] .unit-short-label,
|
||||
[data-object|="unit"][data-is-dead] .unit-vvi,
|
||||
[data-object|="unit"][data-is-dead] .unit-hotgroup,
|
||||
[data-object|="unit"][data-is-dead] .unit-hotgroup-id,
|
||||
[data-object|="unit"][data-is-dead] .unit-state,
|
||||
[data-object|="unit"][data-is-dead] .unit-fuel,
|
||||
[data-object|="unit"][data-is-dead] .unit-health,
|
||||
[data-object|="unit"][data-is-dead] .unit-ammo,
|
||||
[data-object|="unit"][data-is-dead]:hover .unit-fuel,
|
||||
[data-object|="unit"][data-is-dead]:hover .unit-ammo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-is-dead] .unit-summary>* {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-is-dead] .unit-summary .unit-callsign {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ol-temporary-marker {
|
||||
opacity: 0.5;
|
||||
}
|
||||
99
frontend/react/src/map/theme.css
Normal file
99
frontend/react/src/map/theme.css
Normal file
@@ -0,0 +1,99 @@
|
||||
:root {
|
||||
/** Colours **/
|
||||
|
||||
/*** Coalition: neutral ***/
|
||||
--primary-neutral: #949ba7;
|
||||
--secondary-neutral-outline: #111111;
|
||||
--secondary-neutral-text: #111111;
|
||||
--unit-background-neutral: #CFD9E8;
|
||||
|
||||
/*** Coalition: blue ***/
|
||||
--primary-blue: #247be2;
|
||||
--secondary-blue-outline: #082e44;
|
||||
--secondary-blue-text: #017DC1;
|
||||
--unit-background-blue: #3BB9FF;
|
||||
|
||||
/*** Coalition: red ***/
|
||||
--primary-red: #ff5858;
|
||||
--secondary-red-outline: #262222;
|
||||
--secondary-red-text: #D42121;
|
||||
--unit-background-red: #FF5858;
|
||||
|
||||
/*** UI Colours **/
|
||||
--accent-amber: #ffd828;
|
||||
--accent-green: #8bff63;
|
||||
--accent-light-blue: #5ca7ff;
|
||||
--accent-dark-blue: #017DC1;
|
||||
--transparent-accent-light-blue: rgba(92, 167, 255, .33);
|
||||
--accent-light-red: #F5B6B6;
|
||||
|
||||
--background-grey: #3d4651;
|
||||
--background-dark-grey: #35393d;
|
||||
--background-slate-blue: #363c43;
|
||||
--background-offwhite: #f2f2f3;
|
||||
--background-steel: #202831;
|
||||
|
||||
--secondary-dark-steel: #181e25;
|
||||
--secondary-gunmetal-grey: #2f2f2f;
|
||||
--secondary-lighter-grey: #949ba7;
|
||||
--secondary-light-grey: #797e83;
|
||||
--secondary-semitransparent-white: #FFFFFFAA;
|
||||
--secondary-transparent-white: #FFFFFF30;
|
||||
--secondary-yellow: #ffd46893;
|
||||
|
||||
--background-hover: #f2f2f333;
|
||||
|
||||
--nav-text: #ECECEC;
|
||||
|
||||
--ol-select-secondary: #545F6C;
|
||||
--ol-switch-off:#686868;
|
||||
--ol-switch-undefined:#383838;
|
||||
|
||||
--ol-dialog-disabled-text-color: #ffffff20;
|
||||
|
||||
/*** General border radii **/
|
||||
--border-radius-xs: 2px;
|
||||
--border-radius-sm: 5px;
|
||||
--border-radius-md: 10px;
|
||||
--border-radius-lg: 15px;
|
||||
|
||||
/*** Fonts **/
|
||||
--font-weight-bolder: 600;
|
||||
|
||||
/*** Unit marker settings ***/
|
||||
/*** All markers **/
|
||||
--unit-border-radius: var(--border-radius-xs);
|
||||
--unit-font-size: 14px;
|
||||
--unit-font-weight: bolder;
|
||||
--unit-label-border-width: 2px;
|
||||
--unit-spotlight-fill: var(--secondary-yellow);
|
||||
--unit-spotlight-radius: 26px;
|
||||
--unit-stroke-width: 3px;
|
||||
--unit-height: 50px;
|
||||
--unit-width: 50px;
|
||||
|
||||
--unit-health-border-width: 2px;
|
||||
--unit-health-height: 6px;
|
||||
--unit-health-width: 36px;
|
||||
--unit-health-x: 0px;
|
||||
--unit-health-y: 26px;
|
||||
|
||||
/*** Air units ***/
|
||||
--unit-ammo-gap: calc(2px + var(--unit-stroke-width));
|
||||
--unit-ammo-border-radius: 50%;
|
||||
--unit-ammo-border-width: 2px;
|
||||
--unit-ammo-radius: 2px;
|
||||
--unit-ammo-spacing: 2px;
|
||||
--unit-ammo-x: 0px;
|
||||
--unit-ammo-y: 30px;
|
||||
--unit-fuel-border-width: 2px;
|
||||
--unit-fuel-height: 6px;
|
||||
--unit-fuel-width: 36px;
|
||||
--unit-fuel-x: 0px;
|
||||
--unit-fuel-y: 22px;
|
||||
--unit-vvi-width: 4px;
|
||||
}
|
||||
|
||||
* {
|
||||
font-weight:600;
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { LatLng } from 'leaflet';
|
||||
import { getApp } from '../olympusapp';
|
||||
import { AIRBASES_URI, BULLSEYE_URI, COMMANDS_URI, LOGS_URI, MISSION_URI, NONE, ROEs, UNITS_URI, WEAPONS_URI, emissionsCountermeasures, reactionsToThreat } from '../constants/constants';
|
||||
import { ServerStatusPanel } from '../panels/serverstatuspanel';
|
||||
import { LogPanel } from '../panels/logpanel';
|
||||
import { Popup } from '../popups/popup';
|
||||
import { ConnectionStatusPanel } from '../panels/connectionstatuspanel';
|
||||
//import { ServerStatusPanel } from '../panels/serverstatuspanel';
|
||||
//import { LogPanel } from '../panels/logpanel';
|
||||
//import { Popup } from '../popups/popup';
|
||||
//import { ConnectionStatusPanel } from '../panels/connectionstatuspanel';
|
||||
import { AirbasesData, BullseyesData, GeneralSettings, MissionData, Radio, ServerRequestOptions, TACAN } from '../interfaces';
|
||||
import { zeroAppend } from '../other/utils';
|
||||
|
||||
@@ -453,7 +453,7 @@ export class ServerManager {
|
||||
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
|
||||
this.getLogs((data: any) => {
|
||||
this.checkSessionHash(data.sessionHash);
|
||||
(getApp().getPanelsManager().get("log") as LogPanel).appendLogs(data.logs)
|
||||
//(getApp().getPanelsManager().get("log") as LogPanel).appendLogs(data.logs)
|
||||
return data.time;
|
||||
});
|
||||
}
|
||||
@@ -488,17 +488,17 @@ export class ServerManager {
|
||||
this.#serverIsPaused = (elapsedMissionTime === this.#previousMissionElapsedTime);
|
||||
this.#previousMissionElapsedTime = elapsedMissionTime;
|
||||
|
||||
const csp = (getApp().getPanelsManager().get("connectionStatus") as ConnectionStatusPanel);
|
||||
//const csp = (getApp().getPanelsManager().get("connectionStatus") as ConnectionStatusPanel);
|
||||
|
||||
if (this.getConnected()) {
|
||||
if (this.getServerIsPaused()) {
|
||||
csp.showServerPaused();
|
||||
} else {
|
||||
csp.showConnected();
|
||||
}
|
||||
} else {
|
||||
csp.showDisconnected();
|
||||
}
|
||||
//if (this.getConnected()) {
|
||||
// if (this.getServerIsPaused()) {
|
||||
// csp.showServerPaused();
|
||||
// } else {
|
||||
// csp.showConnected();
|
||||
// }
|
||||
//} else {
|
||||
// csp.showDisconnected();
|
||||
//}
|
||||
|
||||
}
|
||||
}, (this.getServerIsPaused() ? 500 : 5000)));
|
||||
@@ -512,11 +512,11 @@ export class ServerManager {
|
||||
|
||||
const elapsedMissionTime = getApp().getMissionManager().getDateAndTime().elapsedTime;
|
||||
|
||||
const csp = (getApp().getPanelsManager().get("connectionStatus") as ConnectionStatusPanel);
|
||||
const mt = getApp().getMissionManager().getDateAndTime().time;
|
||||
//const csp = (getApp().getPanelsManager().get("connectionStatus") as ConnectionStatusPanel);
|
||||
//const mt = getApp().getMissionManager().getDateAndTime().time;
|
||||
|
||||
csp.setMissionTime([mt.h, mt.m, mt.s].map(n => zeroAppend(n, 2)).join(":"));
|
||||
csp.setElapsedTime(new Date(elapsedMissionTime * 1000).toISOString().substring(11, 19));
|
||||
//csp.setMissionTime([mt.h, mt.m, mt.s].map(n => zeroAppend(n, 2)).join(":"));
|
||||
//csp.setElapsedTime(new Date(elapsedMissionTime * 1000).toISOString().substring(11, 19));
|
||||
|
||||
}, 1000));
|
||||
|
||||
@@ -545,7 +545,7 @@ export class ServerManager {
|
||||
|
||||
this.getLogs((data: any) => {
|
||||
this.checkSessionHash(data.sessionHash);
|
||||
(getApp().getPanelsManager().get("log") as LogPanel).appendLogs(data.logs)
|
||||
//(getApp().getPanelsManager().get("log") as LogPanel).appendLogs(data.logs)
|
||||
return data.time;
|
||||
});
|
||||
|
||||
@@ -587,7 +587,7 @@ export class ServerManager {
|
||||
|
||||
setPaused(newPaused: boolean) {
|
||||
this.#paused = newPaused;
|
||||
this.#paused ? (getApp().getPopupsManager().get("infoPopup") as Popup).setText("View paused") : (getApp().getPopupsManager().get("infoPopup") as Popup).setText("View unpaused");
|
||||
//this.#paused ? (getApp().getPopupsManager().get("infoPopup") as Popup).setText("View paused") : (getApp().getPopupsManager().get("infoPopup") as Popup).setText("View unpaused");
|
||||
}
|
||||
|
||||
getPaused() {
|
||||
|
||||
@@ -1,40 +1 @@
|
||||
#root {
|
||||
padding: 0px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
transition: filter 300ms;
|
||||
}
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
.logo.react:hover {
|
||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
||||
}
|
||||
|
||||
@keyframes logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
a:nth-of-type(2) .logo {
|
||||
animation: logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import React from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import './ui.css'
|
||||
|
||||
import { MapContainer, TileLayer } from 'react-leaflet'
|
||||
|
||||
import { Map } from './map/map'
|
||||
import { Header } from './ui/panels/header'
|
||||
import { EventsProvider } from './eventscontext'
|
||||
import { StateProvider } from './statecontext'
|
||||
import { SpawnMenu } from './ui/panels/spawnmenu'
|
||||
|
||||
const position = [51.505, -0.09]
|
||||
import { UnitControlMenu } from './ui/panels/unitcontrolmenu'
|
||||
|
||||
export type OlympusState = {
|
||||
spawnMenuVisible: boolean,
|
||||
@@ -17,129 +13,48 @@ export type OlympusState = {
|
||||
measureMenuVisible: boolean,
|
||||
drawingMenuVisible: boolean
|
||||
}
|
||||
1
|
||||
export function UI(props) {
|
||||
var [spawnMenuVisible, setSpawnMenuVisible] = useState(false);
|
||||
var [unitControlMenuVisible, setUnitControlMenuVisible] = useState(false);
|
||||
var [measureMenuVisible, setMeasureMenuVisible] = useState(false);
|
||||
var [drawingMenuVisible, setDrawingMenuVisible] = useState(false);
|
||||
|
||||
export default class UI extends React.Component<{}, OlympusState> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
/* State initialization */
|
||||
this.state = {
|
||||
spawnMenuVisible: false,
|
||||
unitControlMenuVisible: false,
|
||||
measureMenuVisible: false,
|
||||
drawingMenuVisible: false
|
||||
}
|
||||
|
||||
/* Methods bindings */
|
||||
this.showSpawnMenu = this.showSpawnMenu.bind(this);
|
||||
this.toggleSpawnMenu = this.toggleSpawnMenu.bind(this);
|
||||
this.showUnitControlMenu = this.showUnitControlMenu.bind(this);
|
||||
this.toggleUnitControlMenu = this.toggleUnitControlMenu.bind(this);
|
||||
this.showMeasureMenu = this.showMeasureMenu.bind(this);
|
||||
this.toggleMeasureMenu = this.toggleMeasureMenu.bind(this);
|
||||
this.showDrawingMenu = this.showDrawingMenu.bind(this);
|
||||
this.toggleDrawingMenu = this.toggleDrawingMenu.bind(this);
|
||||
function hideAllMenus() {
|
||||
setSpawnMenuVisible(false);
|
||||
setUnitControlMenuVisible(false);
|
||||
setMeasureMenuVisible(false);
|
||||
setDrawingMenuVisible(false);
|
||||
}
|
||||
|
||||
showSpawnMenu(show: boolean) {
|
||||
this.setState({
|
||||
spawnMenuVisible: show,
|
||||
unitControlMenuVisible: false,
|
||||
measureMenuVisible: false,
|
||||
drawingMenuVisible: false
|
||||
});
|
||||
}
|
||||
|
||||
toggleSpawnMenu() {
|
||||
this.setState({
|
||||
spawnMenuVisible: !this.state.spawnMenuVisible,
|
||||
unitControlMenuVisible: false,
|
||||
measureMenuVisible: false,
|
||||
drawingMenuVisible: false
|
||||
});
|
||||
}
|
||||
|
||||
showUnitControlMenu(show: boolean) {
|
||||
this.setState({
|
||||
spawnMenuVisible: false,
|
||||
unitControlMenuVisible: show,
|
||||
measureMenuVisible: false,
|
||||
drawingMenuVisible: false
|
||||
});
|
||||
}
|
||||
|
||||
toggleUnitControlMenu() {
|
||||
this.setState({
|
||||
spawnMenuVisible: false,
|
||||
unitControlMenuVisible: !this.state.unitControlMenuVisible,
|
||||
measureMenuVisible: false,
|
||||
drawingMenuVisible: false
|
||||
});
|
||||
}
|
||||
|
||||
showMeasureMenu(show: boolean) {
|
||||
this.setState({
|
||||
spawnMenuVisible: false,
|
||||
unitControlMenuVisible: false,
|
||||
measureMenuVisible: show,
|
||||
drawingMenuVisible: false
|
||||
});
|
||||
}
|
||||
|
||||
toggleMeasureMenu() {
|
||||
this.setState({
|
||||
spawnMenuVisible: false,
|
||||
unitControlMenuVisible: false,
|
||||
measureMenuVisible: !this.state.measureMenuVisible,
|
||||
drawingMenuVisible: false
|
||||
});
|
||||
}
|
||||
|
||||
showDrawingMenu(show: boolean) {
|
||||
this.setState({
|
||||
spawnMenuVisible: false,
|
||||
unitControlMenuVisible: false,
|
||||
measureMenuVisible: false,
|
||||
drawingMenuVisible: show
|
||||
});
|
||||
}
|
||||
|
||||
toggleDrawingMenu() {
|
||||
this.setState({
|
||||
spawnMenuVisible: false,
|
||||
unitControlMenuVisible: false,
|
||||
measureMenuVisible: false,
|
||||
drawingMenuVisible: !this.state.drawingMenuVisible
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="h-full w-full font-sans">
|
||||
<StateProvider value={this.state}>
|
||||
<EventsProvider value={
|
||||
{
|
||||
showSpawnMenu: this.showSpawnMenu,
|
||||
toggleSpawnMenu: this.toggleSpawnMenu,
|
||||
showUnitControlMenu: this.showUnitControlMenu,
|
||||
toggleUnitControlMenu: this.toggleUnitControlMenu,
|
||||
showMeasureMenu: this.showMeasureMenu,
|
||||
toggleMeasureMenu: this.toggleMeasureMenu,
|
||||
showDrawingMenu: this.showMeasureMenu,
|
||||
toggleDrawingMenu: this.toggleDrawingMenu
|
||||
}
|
||||
}>
|
||||
<div id='map-container' className='absolute top-0 left-0 w-full h-full'>
|
||||
|
||||
</div>
|
||||
<div className='absolute top-0 left-0 z-ui w-full h-full flex flex-col'>
|
||||
<Header ></Header>
|
||||
<SpawnMenu ></SpawnMenu>
|
||||
</div>
|
||||
|
||||
</EventsProvider>
|
||||
</StateProvider>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div className="absolute top-0 left-0 h-screen w-screen font-sans overflow-hidden">
|
||||
<StateProvider value={{
|
||||
spawnMenuVisible: spawnMenuVisible,
|
||||
unitControlMenuVisible: unitControlMenuVisible,
|
||||
measureMenuVisible: measureMenuVisible,
|
||||
drawingMenuVisible: drawingMenuVisible
|
||||
}}>
|
||||
<EventsProvider value={
|
||||
{
|
||||
setSpawnMenuVisible: setSpawnMenuVisible,
|
||||
setUnitControlMenuVisible: setUnitControlMenuVisible,
|
||||
setDrawingMenuVisible: setDrawingMenuVisible,
|
||||
setMeasureMenuVisible: setMeasureMenuVisible,
|
||||
toggleSpawnMenuVisible: () => {hideAllMenus(); setSpawnMenuVisible(!spawnMenuVisible)},
|
||||
toggleUnitControlMenuVisible: () => {hideAllMenus(); setUnitControlMenuVisible(!unitControlMenuVisible)},
|
||||
toggleMeasureMenuVisible: () => {hideAllMenus(); setMeasureMenuVisible(!measureMenuVisible)},
|
||||
toggleDrawingMenuVisible: () => {hideAllMenus(); setDrawingMenuVisible(!drawingMenuVisible)},
|
||||
}
|
||||
}>
|
||||
<div className='absolute top-0 left-0 h-full w-full flex flex-col'>
|
||||
<Header />
|
||||
<div id='map-container' className='relative h-screen w-screen top-0 left-0' />
|
||||
<SpawnMenu open={spawnMenuVisible} closeCallback={() => setSpawnMenuVisible(false)}/>
|
||||
<UnitControlMenu open={unitControlMenuVisible} closeCallback={() => setUnitControlMenuVisible(false)}/>
|
||||
</div>
|
||||
</EventsProvider>
|
||||
</StateProvider>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import React, { MouseEventHandler } from 'react'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { IconProp } from '@fortawesome/fontawesome-svg-core'
|
||||
|
||||
type ButtonProps = {
|
||||
icon: string,
|
||||
onClick: CallableFunction,
|
||||
active: boolean
|
||||
}
|
||||
|
||||
export class StateButton extends React.Component<ButtonProps, {}> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
active: true
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
var computedClassName = "";
|
||||
computedClassName += this.props.active? 'bg-white text-background-darker': 'bg-transparent text-white border-white';
|
||||
|
||||
return (
|
||||
<FontAwesomeIcon icon={this.props.icon as IconProp} className={computedClassName + " rounded w-5 h-5 p-2 border-2"} onClick={this.props.onClick as MouseEventHandler}>
|
||||
</FontAwesomeIcon>
|
||||
);
|
||||
}
|
||||
}
|
||||
30
frontend/react/src/ui/components/oldropdown.tsx
Normal file
30
frontend/react/src/ui/components/oldropdown.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
import React, { useId, useState } from "react";
|
||||
import { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons';
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
library.add(faChevronDown, faChevronUp);
|
||||
|
||||
export function OlDropdown(props) {
|
||||
var [value, setValue] = useState(props.items[0] ?? "N/A" )
|
||||
const buttonId = useId();
|
||||
const dropdownId = useId()
|
||||
|
||||
return <div>
|
||||
<button id={buttonId} data-dropdown-toggle={dropdownId} className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center border-[1px] dark:border-gray-600 dark:text-gray-400 dark:bg-gray-700 dark:hover:bg-gray-800 dark:focus:ring-blue-800" type="button"><FontAwesomeIcon icon={props.leftIcon} className="mr-3" />{value}<svg className="w-2.5 h-2.5 ms-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div id={dropdownId} className="z-10 hidden bg-white divide-y divide-gray-100 rounded-lg shadow w-44 dark:bg-gray-700">
|
||||
<ul className="py-2 text-sm text-gray-700 dark:text-gray-200" aria-labelledby={buttonId}>
|
||||
{props.items.map((item) => {
|
||||
return <li>
|
||||
<a href="#" onClick={() => setValue(item)} className="block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white">{item}</a>
|
||||
</li>
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
8
frontend/react/src/ui/components/olstatebutton.tsx
Normal file
8
frontend/react/src/ui/components/olstatebutton.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
||||
import React from "react"
|
||||
|
||||
export function OlStateButton(props) {
|
||||
return <button {...props} type="button" className="h-[40px] w-[40px] m-auto text-gray-900 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-100 font-medium rounded-lg text-sm dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-600 dark:focus:ring-gray-700">
|
||||
<FontAwesomeIcon icon={props.icon} />
|
||||
</button>
|
||||
}
|
||||
38
frontend/react/src/ui/components/oltoggle.tsx
Normal file
38
frontend/react/src/ui/components/oltoggle.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import React, { ChangeEvent } from "react";
|
||||
|
||||
type OlToggleState = {
|
||||
checked: boolean
|
||||
}
|
||||
|
||||
type OlToggleProps = {
|
||||
checkedLabel: string,
|
||||
uncheckedLabel: string
|
||||
}
|
||||
|
||||
export class OlToggle extends React.Component<OlToggleProps, OlToggleState> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
checked: false
|
||||
}
|
||||
|
||||
this.onToggle = this.onToggle.bind(this);
|
||||
}
|
||||
|
||||
onToggle(e: ChangeEvent<HTMLInputElement>) {
|
||||
this.setState({
|
||||
checked: e.target.checked
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
return <label className="flex items-center cursor-pointer -mr-8">
|
||||
<input type="checkbox" value="" className="sr-only peer" onChange={this.onToggle}/>
|
||||
<div className="relative w-16 h-6 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-500 peer-checked:after:translate-x-[200%] rtl:peer-checked:after:-translate-x-[200%] peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-interaction-blue"></div>
|
||||
<span className={(this.state.checked? "-translate-x-[250%]": "-translate-x-[180%]") + " ms-3 text-sm font-medium text-gray-900 dark:text-white transition-all select-none relative w-7"}>
|
||||
{this.state.checked? this.props.checkedLabel: this.props.uncheckedLabel}
|
||||
</span>
|
||||
</label>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import React from "react";
|
||||
import { UnitBlueprint } from "../../../interfaces";
|
||||
import { IconProp, library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
library.add(faChevronLeft);
|
||||
|
||||
export type BlueprintsAccordionProps = {
|
||||
title: string,
|
||||
icon: string,
|
||||
blueprints: { [key: string]: UnitBlueprint },
|
||||
searchString: string,
|
||||
callback: CallableFunction
|
||||
}
|
||||
|
||||
export type BlueprintsAccordionState = {
|
||||
open: boolean
|
||||
}
|
||||
|
||||
export class BlueprintsAccordion extends React.Component<BlueprintsAccordionProps, BlueprintsAccordionState> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
open: this.props.searchString !== ''
|
||||
}
|
||||
|
||||
this.toggleOpen = this.toggleOpen.bind(this);
|
||||
}
|
||||
|
||||
toggleOpen() {
|
||||
this.setState({ open: !this.state.open });
|
||||
}
|
||||
|
||||
checkSearch(key) {
|
||||
const blueprint = this.props.blueprints[key];
|
||||
if (blueprint.label.includes(this.props.searchString))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.props.searchString !== '' && !this.state.open)
|
||||
this.setState({ open: true });
|
||||
|
||||
return <div>
|
||||
<div className="cursor-pointer select-none flex justify-between p-2 items-center" onClick={this.toggleOpen}>
|
||||
<div className="">{this.props.title}</div>
|
||||
<FontAwesomeIcon icon="chevron-left" className={"transition-transform " + (this.state.open ? "-rotate-90" : "")}></FontAwesomeIcon>
|
||||
</div>
|
||||
<div className="flex flex-col px-3">
|
||||
{
|
||||
this.state.open &&
|
||||
Object.keys(this.props.blueprints).filter((key) => {
|
||||
return this.checkSearch(key);
|
||||
}).map((key) => {
|
||||
return <div key={key} className="cursor-pointer select-none flex justify-between items-center hover:bg-white hover:bg-opacity-10 px-2 py-1 rounded-sm" onClick={() => this.props.callback(this.props.blueprints[key])}>
|
||||
<FontAwesomeIcon icon={this.props.icon as IconProp} className="text-sm"></FontAwesomeIcon>
|
||||
<div className="font-normal text-left flex-1 px-2">{this.props.blueprints[key].label}</div>
|
||||
<div className="bg-black bg-opacity-20 text-gray-400 rounded-full px-2 py-0.5 text-xs">{this.props.blueprints[key].era === "WW2" ? "WW2" : this.props.blueprints[key].era.split(" ").map((word) => {
|
||||
return word.charAt(0).toLocaleUpperCase();
|
||||
})}</div>
|
||||
</div>
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
26
frontend/react/src/ui/panels/components/menu.tsx
Normal file
26
frontend/react/src/ui/panels/components/menu.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Drawer, DrawerInterface } from "flowbite";
|
||||
import React, { useEffect, useId, useRef } from "react";
|
||||
|
||||
export function Menu(props) {
|
||||
const ref = useRef(null);
|
||||
const labelId = useId();
|
||||
|
||||
useEffect(() => {
|
||||
const drawer: DrawerInterface = new Drawer(ref.current, { backdrop: false });
|
||||
props.open ? drawer.show() : drawer.hide();
|
||||
})
|
||||
|
||||
return <div ref={ref} className="w-[430px] absolute top-16 left-0 z-ui h-screen p-4 overflow-y-auto transition-transform -translate-x-full bg-white dark:bg-gray-800" tabIndex={-1} aria-labelledby={labelId}>
|
||||
<h5 id={labelId} className="inline-flex items-center mb-4 text-base font-semibold text-gray-500 dark:text-gray-400">
|
||||
<FontAwesomeIcon icon={props.titleIcon} className="mr-2"/>
|
||||
{props.title}
|
||||
</h5>
|
||||
<button type="button" onClick={props.closeCallback} className="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 absolute top-2.5 end-2.5 flex items-center justify-center dark:hover:bg-gray-600 dark:hover:text-white" >
|
||||
<svg className="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
|
||||
</svg>
|
||||
<span className="sr-only">Close menu</span>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
export class MenuTitle extends React.Component<{title: string}, {}> {
|
||||
render() {
|
||||
return <div className="h-12 w-full bg-background-dark flex items-center px-4 font-semibold">
|
||||
{this.props.title}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,31 @@
|
||||
import React from 'react'
|
||||
import { StateButton } from '../buttons/statebutton';
|
||||
import { faPlus, faGamepad, faRuler, faPencil } from '@fortawesome/free-solid-svg-icons';
|
||||
import { OlStateButton } from '../components/olstatebutton';
|
||||
import { faPlus, faGamepad, faRuler, faPencil, faMap } from '@fortawesome/free-solid-svg-icons';
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { EventsConsumer, EventsContext } from '../../eventscontext';
|
||||
import { EventsConsumer } from '../../eventscontext';
|
||||
import { StateConsumer } from '../../statecontext';
|
||||
import { OlDropdown } from '../components/oldropdown';
|
||||
|
||||
library.add(faPlus, faGamepad, faRuler, faPencil)
|
||||
library.add(faPlus, faGamepad, faRuler, faPencil, faMap);
|
||||
|
||||
export class Header extends React.Component<{}, {}> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<StateConsumer>
|
||||
{(appState) =>
|
||||
<EventsConsumer>
|
||||
{(events) =>
|
||||
<div className='h-16 w-full bg-background-darker flex flex-row items-center px-5'>
|
||||
<div className="flex flex-row items-center gap-1">
|
||||
<StateButton onClick={events.toggleSpawnMenu} active={appState.spawnMenuVisible} icon="fa-solid fa-plus"></StateButton>
|
||||
<StateButton onClick={events.toggleUnitControlMenu} active={appState.unitControlMenuVisible} icon="fa-solid fa-gamepad"></StateButton>
|
||||
<StateButton onClick={events.toggleMeasureMenu} active={appState.measureMenuVisible} icon="fa-solid fa-ruler"></StateButton>
|
||||
<StateButton onClick={events.toggleDrawingMenu} active={appState.drawingMenuVisible} icon="fa-solid fa-pencil"></StateButton>
|
||||
</div>
|
||||
export function Header(props) {
|
||||
return <StateConsumer>
|
||||
{(appState) =>
|
||||
<EventsConsumer>
|
||||
{(events) =>
|
||||
<nav className="bg-white border-gray-200 dark:bg-gray-800 dark:border-gray-700">
|
||||
<div className="max-w-screen flex flex-wrap items-center justify-between p-4">
|
||||
<div className="flex flex-row items-center justify-center gap-1">
|
||||
<OlStateButton onClick={events.toggleSpawnMenuVisible} checked={appState.spawnMenuVisible} icon="fa-solid fa-plus"></OlStateButton>
|
||||
<OlStateButton onClick={events.toggleUnitControlMenuVisible} checked={appState.unitControlMenuVisible} icon="fa-solid fa-gamepad"></OlStateButton>
|
||||
<OlStateButton onClick={events.toggleMeasureMenuVisible} checked={appState.measureMenuVisible} icon="fa-solid fa-ruler"></OlStateButton>
|
||||
<OlStateButton onClick={events.toggleDrawingMenuVisible} checked={appState.drawingMenuVisible} icon="fa-solid fa-pencil"></OlStateButton>
|
||||
</div>
|
||||
}
|
||||
</EventsConsumer>
|
||||
<OlDropdown items={["DCS Sat", "DCS Alt"]} leftIcon='fa-solid fa-map' />
|
||||
</div>
|
||||
</nav>
|
||||
}
|
||||
</StateConsumer>
|
||||
);
|
||||
}
|
||||
</EventsConsumer>
|
||||
}
|
||||
</StateConsumer>
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import React from "react";
|
||||
import { MenuTitle } from "./components/menutitle";
|
||||
import { Menu } from "./components/menu";
|
||||
import { faPlus } from '@fortawesome/free-solid-svg-icons';
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
|
||||
export class SpawnMenu extends React.Component<{}, {}> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
library.add(faPlus);
|
||||
|
||||
render() {
|
||||
return <div className="h-full w-96 bg-background-neutral z-ui">
|
||||
<MenuTitle title="Spawn menu"></MenuTitle>
|
||||
</div>
|
||||
}
|
||||
export function SpawnMenu(props) {
|
||||
|
||||
return <Menu {...props} title="Spawn menu" titleIcon="fa-solid fa-plus">
|
||||
|
||||
</Menu>
|
||||
}
|
||||
13
frontend/react/src/ui/panels/unitcontrolmenu.tsx
Normal file
13
frontend/react/src/ui/panels/unitcontrolmenu.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from "react";
|
||||
import { Menu } from "./components/menu";
|
||||
import { faGamepad } from '@fortawesome/free-solid-svg-icons';
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
|
||||
library.add(faGamepad);
|
||||
|
||||
export function UnitControlMenu(props) {
|
||||
|
||||
return <Menu {...props} title="Unit control menu" titleIcon="fa-solid fa-gamepad">
|
||||
|
||||
</Menu>
|
||||
}
|
||||
@@ -4,7 +4,8 @@ import { UnitDatabase } from "./unitdatabase"
|
||||
|
||||
export class AircraftDatabase extends UnitDatabase {
|
||||
constructor() {
|
||||
super('api/databases/units/aircraftdatabase');
|
||||
//Temporary
|
||||
super('http://localhost:3000/api/databases/units/aircraftdatabase');
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
|
||||
@@ -4,7 +4,8 @@ import { UnitDatabase } from "./unitdatabase"
|
||||
|
||||
export class GroundUnitDatabase extends UnitDatabase {
|
||||
constructor() {
|
||||
super('api/databases/units/groundunitdatabase');
|
||||
// Temporary
|
||||
super('http://localhost:3000/api/databases/units/groundunitdatabase');
|
||||
}
|
||||
|
||||
getSpawnPointsByName(name: string) {
|
||||
|
||||
@@ -4,7 +4,8 @@ import { UnitDatabase } from "./unitdatabase"
|
||||
|
||||
export class HelicopterDatabase extends UnitDatabase {
|
||||
constructor() {
|
||||
super('api/databases/units/helicopterdatabase');
|
||||
// Temporary
|
||||
super('http://localhost:3000/api/databases/units/helicopterdatabase');
|
||||
}
|
||||
|
||||
getSpawnPointsByName(name: string) {
|
||||
|
||||
@@ -4,7 +4,8 @@ import { UnitDatabase } from "./unitdatabase"
|
||||
|
||||
export class NavyUnitDatabase extends UnitDatabase {
|
||||
constructor() {
|
||||
super('api/databases/units/navyunitdatabase');
|
||||
// Temporary
|
||||
super('http://localhost:3000/api/databases/units/navyunitdatabase');
|
||||
}
|
||||
|
||||
getSpawnPointsByName(name: string) {
|
||||
|
||||
Reference in New Issue
Block a user