diff --git a/frontend/react/src/constants/constants.ts b/frontend/react/src/constants/constants.ts index ce5c1728..94847291 100644 --- a/frontend/react/src/constants/constants.ts +++ b/frontend/react/src/constants/constants.ts @@ -515,7 +515,159 @@ export enum ContextActionType { DELETE, } -export const CONTEXT_ACTION_COLORS = [null, "white", "green", "purple", "blue", "red"]; + +export enum colors { + ALICE_BLUE = "#F0F8FF", + ANTIQUE_WHITE = "#FAEBD7", + AQUA = "#00FFFF", + AQUAMARINE = "#7FFFD4", + AZURE = "#F0FFFF", + BEIGE = "#F5F5DC", + BISQUE = "#FFE4C4", + BLACK = "#000000", + BLANCHED_ALMOND = "#FFEBCD", + BLUE = "#0000FF", + BLUE_VIOLET = "#8A2BE2", + BROWN = "#A52A2A", + BURLY_WOOD = "#DEB887", + CADET_BLUE = "#5F9EA0", + CHARTREUSE = "#7FFF00", + CHOCOLATE = "#D2691E", + CORAL = "#FF7F50", + CORNFLOWER_BLUE = "#6495ED", + CORNSILK = "#FFF8DC", + CRIMSON = "#DC143C", + CYAN = "#00FFFF", + DARK_BLUE = "#00008B", + DARK_CYAN = "#008B8B", + DARK_GOLDEN_ROD = "#B8860B", + DARK_GRAY = "#A9A9A9", + DARK_GREEN = "#006400", + DARK_KHAKI = "#BDB76B", + DARK_MAGENTA = "#8B008B", + DARK_OLIVE_GREEN = "#556B2F", + DARKORANGE = "#FF8C00", + DARK_ORCHID = "#9932CC", + DARK_RED = "#8B0000", + DARK_SALMON = "#E9967A", + DARK_SEA_GREEN = "#8FBC8F", + DARK_SLATE_BLUE = "#483D8B", + DARK_SLATE_GRAY = "#2F4F4F", + DARK_TURQUOISE = "#00CED1", + DARK_VIOLET = "#9400D3", + DEEP_PINK = "#FF1493", + DEEP_SKY_BLUE = "#00BFFF", + DIM_GRAY = "#696969", + DODGER_BLUE = "#1E90FF", + FIRE_BRICK = "#B22222", + FLORAL_WHITE = "#FFFAF0", + FOREST_GREEN = "#228B22", + FUCHSIA = "#FF00FF", + GAINSBORO = "#DCDCDC", + GHOST_WHITE = "#F8F8FF", + GOLD = "#FFD700", + GOLDEN_ROD = "#DAA520", + GRAY = "#808080", + GREEN = "#008000", + GREEN_YELLOW = "#ADFF2F", + HONEY_DEW = "#F0FFF0", + HOT_PINK = "#FF69B4", + INDIAN_RED = "#CD5C5C", + INDIGO = "#4B0082", + IVORY = "#FFFFF0", + KHAKI = "#F0E68C", + LAVENDER = "#E6E6FA", + LAVENDER_BLUSH = "#FFF0F5", + LAWN_GREEN = "#7CFC00", + LEMON_CHIFFON = "#FFFACD", + LIGHT_BLUE = "#ADD8E6", + LIGHT_CORAL = "#F08080", + LIGHT_CYAN = "#E0FFFF", + LIGHT_GOLDEN_ROD_YELLOW = "#FAFAD2", + LIGHT_GREY = "#D3D3D3", + LIGHT_GREEN = "#90EE90", + LIGHT_PINK = "#FFB6C1", + LIGHT_SALMON = "#FFA07A", + LIGHT_SEA_GREEN = "#20B2AA", + LIGHT_SKY_BLUE = "#87CEFA", + LIGHT_SLATE_GRAY = "#778899", + LIGHT_STEEL_BLUE = "#B0C4DE", + LIGHT_YELLOW = "#FFFFE0", + LIME = "#00FF00", + LIME_GREEN = "#32CD32", + LINEN = "#FAF0E6", + MAGENTA = "#FF00FF", + MAROON = "#800000", + MEDIUM_AQUA_MARINE = "#66CDAA", + MEDIUM_BLUE = "#0000CD", + MEDIUM_ORCHID = "#BA55D3", + MEDIUM_PURPLE = "#9370D8", + MEDIUM_SEA_GREEN = "#3CB371", + MEDIUM_SLATE_BLUE = "#7B68EE", + MEDIUM_SPRING_GREEN = "#00FA9A", + MEDIUM_TURQUOISE = "#48D1CC", + MEDIUM_VIOLET_RED = "#C71585", + MIDNIGHT_BLUE = "#191970", + MINT_CREAM = "#F5FFFA", + MISTY_ROSE = "#FFE4E1", + MOCCASIN = "#FFE4B5", + NAVAJO_WHITE = "#FFDEAD", + NAVY = "#000080", + OLD_LACE = "#FDF5E6", + OLIVE = "#808000", + OLIVE_DRAB = "#6B8E23", + ORANGE = "#FFA500", + ORANGE_RED = "#FF4500", + ORCHID = "#DA70D6", + PALE_GOLDEN_ROD = "#EEE8AA", + PALE_GREEN = "#98FB98", + PALE_TURQUOISE = "#AFEEEE", + PALE_VIOLET_RED = "#D87093", + PAPAYA_WHIP = "#FFEFD5", + PEACH_PUFF = "#FFDAB9", + PERU = "#CD853F", + PINK = "#FFC0CB", + PLUM = "#DDA0DD", + POWDER_BLUE = "#B0E0E6", + PURPLE = "#800080", + RED = "#FF0000", + ROSY_BROWN = "#BC8F8F", + ROYAL_BLUE = "#4169E1", + SADDLE_BROWN = "#8B4513", + SALMON = "#FA8072", + SANDY_BROWN = "#F4A460", + SEA_GREEN = "#2E8B57", + SEA_SHELL = "#FFF5EE", + SIENNA = "#A0522D", + SILVER = "#C0C0C0", + SKY_BLUE = "#87CEEB", + SLATE_BLUE = "#6A5ACD", + SLATE_GRAY = "#708090", + SNOW = "#FFFAFA", + SPRING_GREEN = "#00FF7F", + STEEL_BLUE = "#4682B4", + TAN = "#D2B48C", + TEAL = "#008080", + THISTLE = "#D8BFD8", + TOMATO = "#FF6347", + TURQUOISE = "#40E0D0", + VIOLET = "#EE82EE", + WHEAT = "#F5DEB3", + WHITE = "#FFFFFF", + WHITE_SMOKE = "#F5F5F5", + YELLOW = "#FFFF00", + YELLOW_GREEN = "#9ACD32", + BLUE_COALITION = "#2563EB", + NEUTRAL_COALITION = "#9CA3AF", + RED_COALITION = "#EF4444", + OLYMPUS_LIGHT_BLUE = "#3B82F6", + OLYMPUS_BLUE = "#243141", + OLYMPUS_RED = "#F05252", + OLYMPUS_ORANGE = "#FF9900", + OLYMPUS_GREEN = "#8BFF63" +} + +export const CONTEXT_ACTION_COLORS = [undefined, colors.WHITE, colors.GREEN, colors.PURPLE, colors.BLUE, colors.RED]; export namespace ContextActions { export const STOP = new ContextAction( diff --git a/frontend/react/src/map/coalitionarea/coalitioncircle.ts b/frontend/react/src/map/coalitionarea/coalitioncircle.ts index 5563aeba..3c8b1f73 100644 --- a/frontend/react/src/map/coalitionarea/coalitioncircle.ts +++ b/frontend/react/src/map/coalitionarea/coalitioncircle.ts @@ -1,7 +1,7 @@ import { LatLngExpression, Map, Circle, DivIcon, Marker, CircleOptions, LatLng } from "leaflet"; import { getApp } from "../../olympusapp"; import { CoalitionAreaHandle } from "./coalitionareahandle"; -import { BLUE_COMMANDER, RED_COMMANDER } from "../../constants/constants"; +import { BLUE_COMMANDER, colors, RED_COMMANDER } from "../../constants/constants"; import { Coalition } from "../../types/types"; import * as turf from "@turf/turf"; import { CoalitionAreaChangedEvent, CoalitionAreaSelectedEvent } from "../../events"; @@ -125,12 +125,12 @@ export class CoalitionCircle extends Circle { } #setColors() { - let coalitionColor = "#FFFFFF"; - if (this.getCoalition() === "blue") coalitionColor = "#247be2"; - else if (this.getCoalition() === "red") coalitionColor = "#ff5858"; + let coalitionColor = colors.NEUTRAL_COALITION; + if (this.getCoalition() === "blue") coalitionColor = colors.BLUE_COALITION; + else if (this.getCoalition() === "red") coalitionColor = colors.RED_COALITION; this.setStyle({ - color: this.getSelected() ? "white" : coalitionColor, + color: this.getSelected() ? colors.WHITE : coalitionColor, fillColor: coalitionColor, }); } diff --git a/frontend/react/src/map/coalitionarea/coalitionpolygon.ts b/frontend/react/src/map/coalitionarea/coalitionpolygon.ts index eff68b80..a0408358 100644 --- a/frontend/react/src/map/coalitionarea/coalitionpolygon.ts +++ b/frontend/react/src/map/coalitionarea/coalitionpolygon.ts @@ -2,7 +2,7 @@ import { LatLng, LatLngExpression, Map, Point, Polygon, PolylineOptions, DivIcon import { getApp } from "../../olympusapp"; import { CoalitionAreaHandle } from "./coalitionareahandle"; import { CoalitionAreaMiddleHandle } from "./coalitionareamiddlehandle"; -import { BLUE_COMMANDER, RED_COMMANDER } from "../../constants/constants"; +import { BLUE_COMMANDER, colors, RED_COMMANDER } from "../../constants/constants"; import { Coalition } from "../../types/types"; import { polyCenter } from "../../other/utils"; import { CoalitionAreaChangedEvent, CoalitionAreaSelectedEvent } from "../../events"; @@ -150,9 +150,9 @@ export class CoalitionPolygon extends Polygon { } #setColors() { - let coalitionColor = "#FFFFFF"; - if (this.getCoalition() === "blue") coalitionColor = "#247be2"; - else if (this.getCoalition() === "red") coalitionColor = "#ff5858"; + let coalitionColor = colors.NEUTRAL_COALITION; + if (this.getCoalition() === "blue") coalitionColor = colors.BLUE_COALITION; + else if (this.getCoalition() === "red") coalitionColor = colors.RED_COALITION; this.setStyle({ color: this.getSelected() ? "white" : coalitionColor, diff --git a/frontend/react/src/map/map.ts b/frontend/react/src/map/map.ts index cd00d896..b1c2cec7 100644 --- a/frontend/react/src/map/map.ts +++ b/frontend/react/src/map/map.ts @@ -25,6 +25,7 @@ import { SHORT_PRESS_MILLISECONDS, DEBOUNCE_MILLISECONDS, DrawSubState, + colors, } from "../constants/constants"; import { MapHiddenTypes, MapOptions } from "../types/types"; import { EffectRequestTable, OlympusConfig, SpawnRequestTable } from "../interfaces"; @@ -177,7 +178,7 @@ export class Map extends L.Map { maxZoom: 13, }); this.#miniMapLayerGroup = new L.LayerGroup([minimapLayer]); - this.#miniMapPolyline = new L.Polyline([], { color: "#202831" }); + this.#miniMapPolyline = new L.Polyline([], { color: colors.DARK_GRAY }); this.#miniMapPolyline.addTo(this.#miniMapLayerGroup); /* Register event handles */ diff --git a/frontend/react/src/other/utils.ts b/frontend/react/src/other/utils.ts index c519c991..2c721da6 100644 --- a/frontend/react/src/other/utils.ts +++ b/frontend/react/src/other/utils.ts @@ -525,4 +525,79 @@ export function toDCSFormationOffset(offset: {x: number, y: number, z: number}) export function fromDCSFormationOffset(offset: {x: number, y: number, z: number}) { return { x: offset.z, y: -offset.x, z: offset.y }; +} + +/** + * Adjusts the brightness of a color. + * @param {string} color - The color in hashtag format (e.g., "#RRGGBB"). + * @param {number} percent - The percentage to adjust the brightness (positive to lighten, negative to darken). + * @returns {string} - The adjusted color in hashtag format. + */ +export function adjustBrightness(color, percent) { + // Ensure the color is in the correct format + if (!/^#[0-9A-F]{6}$/i.test(color)) { + throw new Error('Invalid color format. Use #RRGGBB.'); + } + + // Parse the color components + let r = parseInt(color.slice(1, 3), 16); + let g = parseInt(color.slice(3, 5), 16); + let b = parseInt(color.slice(5, 7), 16); + + // Adjust the brightness + r = Math.min(255, Math.max(0, r + Math.round((percent / 100) * 255))); + g = Math.min(255, Math.max(0, g + Math.round((percent / 100) * 255))); + b = Math.min(255, Math.max(0, b + Math.round((percent / 100) * 255))); + + // Convert back to hexadecimal and return the adjusted color + return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase()}`; +} + +/** + * Sets the opacity of a color. + * @param {string} color - The color in hashtag format (e.g., "#RRGGBB"). + * @param {number} opacity - The opacity value (between 0 and 1). + * @returns {string} - The color in rgba format. + */ +export function setOpacity(color, opacity) { + // Ensure the color is in the correct format + if (!/^#[0-9A-F]{6}$/i.test(color)) { + throw new Error('Invalid color format. Use #RRGGBB.'); + } + + // Ensure the opacity is within the valid range + if (opacity < 0 || opacity > 1) { + throw new Error('Opacity must be between 0 and 1.'); + } + + // Parse the color components + let r = parseInt(color.slice(1, 3), 16); + let g = parseInt(color.slice(3, 5), 16); + let b = parseInt(color.slice(5, 7), 16); + + // Return the color in rgba format + return `rgba(${r}, ${g}, ${b}, ${opacity})`; +} + +/** + * Computes the brightness of a color. + * @param {string} color - The color in hashtag format (e.g., "#RRGGBB"). + * @returns {number} - The brightness value (0 to 255). + */ +export function computeBrightness(color) { + // Ensure the color is in the correct format + if (!/^#[0-9A-F]{6}$/i.test(color)) { + throw new Error('Invalid color format. Use #RRGGBB.'); + } + + // Parse the color components + let r = parseInt(color.slice(1, 3), 16); + let g = parseInt(color.slice(3, 5), 16); + let b = parseInt(color.slice(5, 7), 16); + + // Compute the brightness using the luminance formula + // The formula is: 0.299*R + 0.587*G + 0.114*B + let brightness = 0.299 * r + 0.587 * g + 0.114 * b; + + return brightness; } \ No newline at end of file diff --git a/frontend/react/src/ui/components/oldropdown.tsx b/frontend/react/src/ui/components/oldropdown.tsx index a0999707..e24d62c2 100644 --- a/frontend/react/src/ui/components/oldropdown.tsx +++ b/frontend/react/src/ui/components/oldropdown.tsx @@ -104,9 +104,12 @@ export function OlDropdown(props: { `} type="button" > - {props.leftIcon && } + {props.leftIcon && ( + + )} {props.label ?? ""} void; className?: string; children?: string | JSX.Element | JSX.Element[] }) { +export function OlDropdownItem(props: { onClick?: () => void; className?: string; borderColor?: string; children?: string | JSX.Element | JSX.Element[] }) { return ( diff --git a/frontend/react/src/ui/components/olstatebutton.tsx b/frontend/react/src/ui/components/olstatebutton.tsx index 2ed67987..3f81b24a 100644 --- a/frontend/react/src/ui/components/olstatebutton.tsx +++ b/frontend/react/src/ui/components/olstatebutton.tsx @@ -1,12 +1,14 @@ import { IconProp } from "@fortawesome/fontawesome-svg-core"; -import { faExternalLink, faLock, faLockOpen, faUnlock, faUnlockAlt } from "@fortawesome/free-solid-svg-icons"; +import { faLock, faUnlockAlt } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import React, { useRef, useState } from "react"; import { OlTooltip } from "./oltooltip"; +import { computeBrightness, setOpacity } from "../../other/utils"; +import { colors } from "../../constants/constants"; export function OlStateButton(props: { className?: string; - buttonColor?: string | null; + buttonColor?: string; checked: boolean; icon?: IconProp; tooltip?: string | (() => JSX.Element | JSX.Element[]); @@ -27,13 +29,6 @@ export function OlStateButton(props: { dark:bg-olympus-600 dark:text-gray-300 `; - let textColor = "white"; - if ((props.checked || hover) && props.buttonColor == "white") { - textColor = "#243141"; - } - - const opacity = hover && !props.checked ? "AA" : "FF"; - return ( <>
- {props.icon && } + {props.icon && 200} className={` + m-auto text-gray-200 + data-[bright='true']:text-gray-800 + `} />} {props.children}
diff --git a/frontend/react/src/ui/contextmenus/mapcontextmenu.tsx b/frontend/react/src/ui/contextmenus/mapcontextmenu.tsx index d729fedd..49576e72 100644 --- a/frontend/react/src/ui/contextmenus/mapcontextmenu.tsx +++ b/frontend/react/src/ui/contextmenus/mapcontextmenu.tsx @@ -110,17 +110,13 @@ export function MapContextMenu(props: {}) { > {contextActionSet && reorderedActions.map((contextActionIt) => { - const colorString = ` - border-2 - border-${CONTEXT_ACTION_COLORS[contextActionIt.getOptions().type]}-500 - `; - return ( { if (contextActionIt.getTarget() === ContextActionTarget.NONE) { contextActionIt.executeCallback(null, null); diff --git a/frontend/react/src/ui/contextmenus/spawncontextmenu.tsx b/frontend/react/src/ui/contextmenus/spawncontextmenu.tsx index ab8a3eed..089727db 100644 --- a/frontend/react/src/ui/contextmenus/spawncontextmenu.tsx +++ b/frontend/react/src/ui/contextmenus/spawncontextmenu.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from "react"; -import { BLUE_COMMANDER, COMMAND_MODE_OPTIONS_DEFAULTS, GAME_MASTER, NO_SUBSTATE, OlympusState, OlympusSubState } from "../../constants/constants"; +import { BLUE_COMMANDER, colors, COMMAND_MODE_OPTIONS_DEFAULTS, GAME_MASTER, NO_SUBSTATE, OlympusState, OlympusSubState } from "../../constants/constants"; import { LatLng } from "leaflet"; import { AppStateChangedEvent, @@ -187,35 +187,35 @@ export function SpawnContextMenu(props: {}) { onClick={() => (openAccordion !== CategoryGroup.AIRCRAFT ? setOpenAccordion(CategoryGroup.AIRCRAFT) : setOpenAccordion(CategoryGroup.NONE))} icon={olButtonsVisibilityAircraft} tooltip="Show aircraft units" - buttonColor={spawnCoalition === "blue" ? "#2563eb" : spawnCoalition === "neutral" ? "#9ca3af" : "#ef4444"} + buttonColor={spawnCoalition === "blue" ? colors.BLUE_COALITION : spawnCoalition === "neutral" ? colors.NEUTRAL_COALITION : colors.RED_COALITION} /> (openAccordion !== CategoryGroup.HELICOPTER ? setOpenAccordion(CategoryGroup.HELICOPTER) : setOpenAccordion(CategoryGroup.NONE))} icon={olButtonsVisibilityHelicopter} tooltip="Show helicopter units" - buttonColor={spawnCoalition === "blue" ? "#2563eb" : spawnCoalition === "neutral" ? "#9ca3af" : "#ef4444"} + buttonColor={spawnCoalition === "blue" ? colors.BLUE_COALITION : spawnCoalition === "neutral" ? colors.NEUTRAL_COALITION : colors.RED_COALITION} /> (openAccordion !== CategoryGroup.AIR_DEFENCE ? setOpenAccordion(CategoryGroup.AIR_DEFENCE) : setOpenAccordion(CategoryGroup.NONE))} icon={olButtonsVisibilityGroundunitSam} tooltip="Show air defence units" - buttonColor={spawnCoalition === "blue" ? "#2563eb" : spawnCoalition === "neutral" ? "#9ca3af" : "#ef4444"} + buttonColor={spawnCoalition === "blue" ? colors.BLUE_COALITION : spawnCoalition === "neutral" ? colors.NEUTRAL_COALITION : colors.RED_COALITION} /> (openAccordion !== CategoryGroup.GROUND_UNIT ? setOpenAccordion(CategoryGroup.GROUND_UNIT) : setOpenAccordion(CategoryGroup.NONE))} icon={olButtonsVisibilityGroundunit} tooltip="Show ground units" - buttonColor={spawnCoalition === "blue" ? "#2563eb" : spawnCoalition === "neutral" ? "#9ca3af" : "#ef4444"} + buttonColor={spawnCoalition === "blue" ? colors.BLUE_COALITION : spawnCoalition === "neutral" ? colors.NEUTRAL_COALITION : colors.RED_COALITION} /> (openAccordion !== CategoryGroup.NAVY_UNIT ? setOpenAccordion(CategoryGroup.NAVY_UNIT) : setOpenAccordion(CategoryGroup.NONE))} icon={olButtonsVisibilityNavyunit} tooltip="Show navy units" - buttonColor={spawnCoalition === "blue" ? "#2563eb" : spawnCoalition === "neutral" ? "#9ca3af" : "#ef4444"} + buttonColor={spawnCoalition === "blue" ? colors.BLUE_COALITION : spawnCoalition === "neutral" ? colors.NEUTRAL_COALITION : colors.RED_COALITION} /> setShowMore(!showMore)} icon={faEllipsisVertical} tooltip="Show more options" /> {showMore && ( diff --git a/frontend/react/src/ui/panels/maptoolbar.tsx b/frontend/react/src/ui/panels/maptoolbar.tsx index 981027e3..a088a49c 100644 --- a/frontend/react/src/ui/panels/maptoolbar.tsx +++ b/frontend/react/src/ui/panels/maptoolbar.tsx @@ -244,11 +244,8 @@ export function MapToolBar(props: {}) { tooltipPosition="side" buttonColor={CONTEXT_ACTION_COLORS[contextActionIt.getOptions().type ?? 0]} onClick={() => { - if (contextActionIt.getTarget() === ContextActionTarget.NONE) { - contextActionIt.executeCallback(null, null); - } else { - contextActionIt !== contextAction ? getApp().getMap().setContextAction(contextActionIt) : getApp().getMap().setContextAction(null); - } + if (contextActionIt.getTarget() === ContextActionTarget.NONE) contextActionIt.executeCallback(null, null); + else contextActionIt !== contextAction ? getApp().getMap().setContextAction(contextActionIt) : getApp().getMap().setContextAction(null); }} /> diff --git a/frontend/react/src/ui/panels/minimappanel.tsx b/frontend/react/src/ui/panels/minimappanel.tsx index d0a201f9..8a1b03a5 100644 --- a/frontend/react/src/ui/panels/minimappanel.tsx +++ b/frontend/react/src/ui/panels/minimappanel.tsx @@ -4,7 +4,7 @@ import { DateAndTime, ServerStatus } from "../../interfaces"; import { getApp } from "../../olympusapp"; import { FaChevronDown, FaChevronUp } from "react-icons/fa6"; import { MapOptionsChangedEvent, ServerStatusUpdatedEvent } from "../../events"; -import { MAP_OPTIONS_DEFAULTS } from "../../constants/constants"; +import { colors, MAP_OPTIONS_DEFAULTS } from "../../constants/constants"; export function MiniMapPanel(props: {}) { const [serverStatus, setServerStatus] = useState({} as ServerStatus); @@ -41,15 +41,13 @@ export function MiniMapPanel(props: {}) { let timeString = `${zeroAppend(hours, 2)}:${zeroAppend(minutes, 2)}:${zeroAppend(seconds, 2)}`; - // Choose frame rate string color - let frameRateColor = "#8BFF63"; - if (serverStatus.frameRate < 30) frameRateColor = "#F05252"; - else if (serverStatus.frameRate >= 30 && serverStatus.frameRate < 60) frameRateColor = "#FF9900"; + let loadColor = colors.OLYMPUS_GREEN; + if (serverStatus.load > 1000) loadColor = colors.OLYMPUS_RED; + else if (serverStatus.load >= 100 && serverStatus.load < 1000) loadColor = colors.OLYMPUS_ORANGE; - // Choose load string color - let loadColor = "#8BFF63"; - if (serverStatus.load > 1000) loadColor = "#F05252"; - else if (serverStatus.load >= 100 && serverStatus.load < 1000) loadColor = "#FF9900"; + let frameRateColor = colors.OLYMPUS_GREEN; + if (serverStatus.frameRate < 30) frameRateColor = colors.OLYMPUS_RED; + else if (serverStatus.frameRate >= 30 && serverStatus.frameRate < 60) frameRateColor = colors.OLYMPUS_ORANGE; return (
setServerStatus(status)); }, []); - let loadColor = "#8BFF63"; - if (serverStatus.load > 1000) loadColor = "#F05252"; - else if (serverStatus.load >= 100 && serverStatus.load < 1000) loadColor = "#FF9900"; - - let frameRateColor = "#8BFF63"; - if (serverStatus.frameRate < 30) frameRateColor = "#F05252"; - else if (serverStatus.frameRate >= 30 && serverStatus.frameRate < 60) frameRateColor = "#FF9900"; + let loadColor = colors.OLYMPUS_GREEN; + if (serverStatus.load > 1000) loadColor = colors.OLYMPUS_RED; + else if (serverStatus.load >= 100 && serverStatus.load < 1000) loadColor = colors.OLYMPUS_ORANGE; + let frameRateColor = colors.OLYMPUS_GREEN; + if (serverStatus.frameRate < 30) frameRateColor = colors.OLYMPUS_RED; + else if (serverStatus.frameRate >= 30 && serverStatus.frameRate < 60) frameRateColor = colors.OLYMPUS_ORANGE; const MThours = serverStatus.missionTime? serverStatus.missionTime.h: 0; const MTminutes = serverStatus.missionTime? serverStatus.missionTime.m: 0; diff --git a/frontend/react/src/unit/importexport/unitdatafile.ts b/frontend/react/src/unit/importexport/unitdatafile.ts deleted file mode 100644 index e53e0f4e..00000000 --- a/frontend/react/src/unit/importexport/unitdatafile.ts +++ /dev/null @@ -1,63 +0,0 @@ -//import { Dialog } from "../../dialog/dialog"; -//import { createCheckboxOption } from "../../other/utils"; - -var categoryMap = { - Aircraft: "Aircraft", - Helicopter: "Helicopter", - GroundUnit: "Ground units", - NavyUnit: "Naval units", -}; - -export abstract class UnitDataFile { - protected data: any; - //protected dialog!: Dialog; - - constructor() {} - - buildCategoryCoalitionTable() { - const categories = this.#getCategoriesFromData(); - const coalitions = ["blue", "neutral", "red"]; - - let headersHTML: string = ``; - let matrixHTML: string = ``; - - //categories.forEach((category: string, index) => { - // matrixHTML += `${categoryMap[category as keyof typeof categoryMap]}`; - // - // coalitions.forEach((coalition: string) => { - // if (index === 0) - // headersHTML += `${coalition[0].toUpperCase() + coalition.substring(1)}`; - // - // const optionIsValid = this.data[category].hasOwnProperty(coalition); - // let checkboxHTML = createCheckboxOption(``, category, optionIsValid, () => { }, { - // "disabled": !optionIsValid, - // "name": "category-coalition-selection", - // "readOnly": !optionIsValid, - // "value" : `${category}:${coalition}` - // }).outerHTML; - // - // if (optionIsValid) - // checkboxHTML = checkboxHTML.replace(`"checkbox"`, `"checkbox" checked`); // inner and outerHTML screw default checked up - // - // matrixHTML += `${checkboxHTML}`; - // - // }); - // matrixHTML += ""; - //}); - // - //const table = this.dialog.getElement().querySelector("table.categories-coalitions"); - - //(table.tHead).innerHTML = ` ${headersHTML}`; - //(table.querySelector(`tbody`)).innerHTML = matrixHTML; - } - - #getCategoriesFromData() { - const categories = Object.keys(this.data); - categories.sort(); - return categories; - } - - getData() { - return this.data; - } -} diff --git a/frontend/react/src/unit/importexport/unitdatafileexport.ts b/frontend/react/src/unit/importexport/unitdatafileexport.ts deleted file mode 100644 index 0e3fbee2..00000000 --- a/frontend/react/src/unit/importexport/unitdatafileexport.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { getApp } from "../../olympusapp"; -//import { Dialog } from "../../dialog/dialog"; -import { zeroAppend } from "../../other/utils"; -import { Unit } from "../unit"; -import { UnitDataFile } from "./unitdatafile"; - -export class UnitDataFileExport extends UnitDataFile { - protected data!: any; - //protected dialog: Dialog; - #element!: HTMLElement; - #filename: string = "export.json"; - - constructor(elementId: string) { - super(); - //this.dialog = new Dialog(elementId); - //this.#element = this.dialog.getElement(); - - this.#element.querySelector(".start-transfer")?.addEventListener("click", (ev: MouseEventInit) => { - this.#doExport(); - }); - } - - /** - * Show the form to start the export journey - */ - showForm(units: Unit[]) { - //this.dialog.getElement().querySelectorAll("[data-on-error]").forEach((el:Element) => { - // el.classList.toggle("hide", el.getAttribute("data-on-error") === "show"); - //}); - // - //const data: any = {}; - //const unitCanBeExported = (unit: Unit) => !["Aircraft", "Helicopter"].includes(unit.getCategory()); - // - //units.filter((unit: Unit) => unit.getAlive() && unitCanBeExported(unit)).forEach((unit: Unit) => { - // const category = unit.getCategory(); - // const coalition = unit.getCoalition(); - // - // if (!data.hasOwnProperty(category)) { - // data[category] = {}; - // } - // - // if (!data[category].hasOwnProperty(coalition)) - // data[category][coalition] = []; - // - // data[category][coalition].push(unit); - //}); - // - //this.data = data; - //this.buildCategoryCoalitionTable(); - //this.dialog.show(); - // - //const date = new Date(); - //this.#filename = `olympus_${getApp().getMissionManager().getTheatre().replace(/[^\w]/gi, "").toLowerCase()}_${date.getFullYear()}${zeroAppend(date.getMonth() + 1, 2)}${zeroAppend(date.getDate(), 2)}_${zeroAppend(date.getHours(), 2)}${zeroAppend(date.getMinutes(), 2)}${zeroAppend(date.getSeconds(), 2)}.json`; - //var input = this.#element.querySelector("#export-filename") as HTMLInputElement; - //input.onchange = (ev: Event) => { - // this.#filename = (ev.currentTarget as HTMLInputElement).value; - //} - //if (input) - // input.value = this.#filename; - } - - #doExport() { - let selectedUnits: Unit[] = []; - - this.#element - .querySelectorAll(`input[type="checkbox"][name="category-coalition-selection"]:checked`) - .forEach((checkbox: HTMLInputElement) => { - if (checkbox instanceof HTMLInputElement) { - const [category, coalition] = checkbox.value.split(":"); // e.g. "category:coalition" - selectedUnits = selectedUnits.concat(this.data[category][coalition]); - } - }); - - if (selectedUnits.length === 0) { - alert("Please select at least one option for export."); - return; - } - - var unitsToExport: { [key: string]: any } = {}; - selectedUnits.forEach((unit: Unit) => { - var data: any = unit.getData(); - if (unit.getGroupName() in unitsToExport) unitsToExport[unit.getGroupName()].push(data); - else unitsToExport[unit.getGroupName()] = [data]; - }); - - const a = document.createElement("a"); - const file = new Blob([JSON.stringify(unitsToExport)], { - type: "text/plain", - }); - a.href = URL.createObjectURL(file); - - var filename = this.#filename; - if (!this.#filename.toLowerCase().endsWith(".json")) filename += ".json"; - a.download = filename; - a.click(); - //this.dialog.hide(); - } -} diff --git a/frontend/react/src/unit/importexport/unitdatafileimport.ts b/frontend/react/src/unit/importexport/unitdatafileimport.ts deleted file mode 100644 index b6f74dad..00000000 --- a/frontend/react/src/unit/importexport/unitdatafileimport.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { getApp } from "../../olympusapp"; -//import { Dialog } from "../../dialog/dialog"; -import { UnitData } from "../../interfaces"; -//import { ImportFileJSONSchemaValidator } from "../../schemas/schema"; -import { UnitDataFile } from "./unitdatafile"; - -export class UnitDataFileImport extends UnitDataFile { - protected data!: any; - //protected dialog: Dialog; - #fileData!: { [key: string]: UnitData[] }; - - constructor(elementId: string) { - super(); - //this.dialog = new Dialog(elementId); - //this.dialog.getElement().querySelector(".start-transfer")?.addEventListener("click", (ev: MouseEventInit) => { - // this.#doImport(); - // this.dialog.hide(); - //}); - } - - #doImport() { - //let selectedCategories: any = {}; - //const unitsManager = getApp().getUnitsManager(); - // - //this.dialog.getElement().querySelectorAll(`input[type="checkbox"][name="category-coalition-selection"]:checked`).forEach((checkbox: HTMLInputElement) => { - // if (checkbox instanceof HTMLInputElement) { - // const [category, coalition] = checkbox.value.split(":"); // e.g. "category:coalition" - // selectedCategories[category] = selectedCategories[category] || {}; - // selectedCategories[category][coalition] = true; - // } - //}); - // - //for (const [groupName, groupData] of Object.entries(this.#fileData)) { - // if (groupName === "" || groupData.length === 0 || !this.#unitGroupDataCanBeImported(groupData)) - // continue; - // - // let { category, coalition } = groupData[0]; - // - // if (!selectedCategories.hasOwnProperty(category) - // || !selectedCategories[category].hasOwnProperty(coalition) - // || selectedCategories[category][coalition] !== true) - // continue; - // - // let unitsToSpawn = groupData.filter((unitData: UnitData) => this.#unitDataCanBeImported(unitData)).map((unitData: UnitData) => { - // return { unitType: unitData.name, location: unitData.position, liveryID: "", skill: "High" } - // }); - // - // unitsManager.spawnUnits(category, unitsToSpawn, coalition, false); - //} - } - - selectFile() { - var input = document.createElement("input"); - input.type = "file"; - input.addEventListener("change", (e: any) => { - var file = e.target.files[0]; - if (!file) { - return; - } - var reader = new FileReader(); - reader.onload = (e: any) => { - try { - this.#fileData = JSON.parse(e.target.result); - - //const validator = new ImportFileJSONSchemaValidator(); - //if (!validator.validate(this.#fileData)) { - // const errors = validator.getErrors().reduce((acc:any, error:any) => { - // let errorString = error.instancePath.substring(1) + ": " + error.message; - // if (error.params) { - // const {allowedValues} = error.params; - // if (allowedValues) - // errorString += ": " + allowedValues.join(', '); - // } - // acc.push(errorString); - // return acc; - // }, [] as string[]); - // this.#showFileDataErrors(errors); - //} else { - // this.#showForm(); - //} - } catch (e: any) { - this.#showFileDataErrors([e]); - } - }; - reader.readAsText(file); - }); - input.click(); - } - - #showFileDataErrors(reasons: string[]) { - //this.dialog.getElement().querySelectorAll("[data-on-error]").forEach((el:Element) => { - // el.classList.toggle("hide", el.getAttribute("data-on-error") === "hide"); - //}); - // - //const reasonsList = this.dialog.getElement().querySelector(".import-error-reasons"); - //if (reasonsList instanceof HTMLElement) - // reasonsList.innerHTML = `
  • ${reasons.join("
  • ")}
  • `; - // - //this.dialog.show(); - } - - #showForm() { - //this.dialog.getElement().querySelectorAll("[data-on-error]").forEach((el:Element) => { - // el.classList.toggle("hide", el.getAttribute("data-on-error") === "show"); - //}); - // - //const data: any = {}; - // - //for (const [group, units] of Object.entries(this.#fileData)) { - // if (group === "" || units.length === 0) - // continue; - // - // if (units.some((unit: UnitData) => !this.#unitDataCanBeImported(unit))) - // continue; - // - // const category = units[0].category; - // - // if (!data.hasOwnProperty(category)) { - // data[category] = {}; - // } - // - // units.forEach((unit: UnitData) => { - // if (!data[category].hasOwnProperty(unit.coalition)) - // data[category][unit.coalition] = []; - // - // data[category][unit.coalition].push(unit); - // }); - // - //} - // - //this.data = data; - //this.buildCategoryCoalitionTable(); - //this.dialog.show(); - } - - #unitDataCanBeImported(unitData: UnitData) { - return unitData.alive && this.#unitGroupDataCanBeImported([unitData]); - } - - #unitGroupDataCanBeImported(unitGroupData: UnitData[]) { - return ( - unitGroupData.every((unitData: UnitData) => { - return !["Aircraft", "Helicopter"].includes(unitData.category); - }) && unitGroupData.some((unitData: UnitData) => unitData.alive) - ); - } -} diff --git a/frontend/react/src/unit/unit.ts b/frontend/react/src/unit/unit.ts index 7e13b76f..26deaedb 100644 --- a/frontend/react/src/unit/unit.ts +++ b/frontend/react/src/unit/unit.ts @@ -16,6 +16,7 @@ import { nmToM, zeroAppend, computeBearingRangeString, + adjustBrightness, } from "../other/utils"; import { CustomMarker } from "../map/markers/custommarker"; import { SVGInjector } from "@tanem/svg-injector"; @@ -43,6 +44,7 @@ import { ContextActionTarget, SHORT_PRESS_MILLISECONDS, TRAIL_LENGTH, + colors, } from "../constants/constants"; import { DataExtractor } from "../server/dataextractor"; import { Weapon } from "../weapon/weapon"; @@ -319,7 +321,7 @@ export abstract class Unit extends CustomMarker { this.ID = ID; this.#pathPolyline = new Polyline([], { - color: "#2d3e50", + color: colors.GRAY, weight: 3, opacity: 0.5, smoothFactor: 1, @@ -327,7 +329,7 @@ export abstract class Unit extends CustomMarker { this.#pathPolyline.addTo(getApp().getMap()); this.#targetPositionMarker = new TargetMarker(new LatLng(0, 0)); this.#targetPositionPolyline = new Polyline([], { - color: "#FF0000", + color: colors.WHITE, weight: 3, opacity: 0.5, smoothFactor: 1, @@ -601,8 +603,9 @@ export abstract class Unit extends CustomMarker { if (!this.#blueprint && this.#name !== "") { const blueprint = getApp().getUnitsManager().getDatabase().getByName(this.#name); this.#blueprint = blueprint ?? null; - /* Refresh the marker */ - this.#redrawMarker(); + if (this.#blueprint) + /* Refresh the marker */ + this.#redrawMarker(); } /* Update the blueprint to use when the unit is grouped */ @@ -1438,9 +1441,9 @@ export abstract class Unit extends CustomMarker { if (this.#alive && drawMiniMapMarker) { if (this.#miniMapMarker == null) { this.#miniMapMarker = new CircleMarker(new LatLng(this.#position.lat, this.#position.lng), { radius: 0.5 }); - if (this.#coalition == "neutral") this.#miniMapMarker.setStyle({ color: "#CFD9E8", radius: 2 }); - else if (this.#coalition == "red") this.#miniMapMarker.setStyle({ color: "#ff5858", radius: 2 }); - else this.#miniMapMarker.setStyle({ color: "#247be2", radius: 2 }); + if (this.#coalition == "neutral") this.#miniMapMarker.setStyle({ color: colors.NEUTRAL_COALITION, radius: 2 }); + else if (this.#coalition == "red") this.#miniMapMarker.setStyle({ color: colors.RED_COALITION, radius: 2 }); + else this.#miniMapMarker.setStyle({ color: colors.BLUE_COALITION, radius: 2 }); this.#miniMapMarker.addTo(getApp().getMap().getMiniMapLayerGroup()); this.#miniMapMarker.bringToBack(); } else { @@ -1585,7 +1588,7 @@ export abstract class Unit extends CustomMarker { /* Draw the contact trail */ if (/*TODO getApp().getMap().getOptions().AWACSMode*/ false) { this.#trailPolylines = this.#trailPositions.map( - (latlng, idx) => new Polyline([latlng, latlng], { color: "#FFFFFF", opacity: 1 - (idx + 1) / TRAIL_LENGTH }) + (latlng, idx) => new Polyline([latlng, latlng], { color: colors.WHITE, opacity: 1 - (idx + 1) / TRAIL_LENGTH }) ); this.#trailPolylines.forEach((polyline) => polyline.addTo(getApp().getMap())); } @@ -1678,10 +1681,10 @@ export abstract class Unit extends CustomMarker { } else endLatLng = new LatLng(contact.getPosition().lat, contact.getPosition().lng); var color; - if (contactData.detectionMethod === VISUAL || contactData.detectionMethod === OPTIC) color = "#FF00FF"; - else if (contactData.detectionMethod === RADAR || contactData.detectionMethod === IRST) color = "#FFFF00"; - else if (contactData.detectionMethod === RWR) color = "#00FF00"; - else color = "#FFFFFF"; + if (contactData.detectionMethod === VISUAL || contactData.detectionMethod === OPTIC) color = colors.MAGENTA; + else if (contactData.detectionMethod === RADAR || contactData.detectionMethod === IRST) color = colors.YELLOW; + else if (contactData.detectionMethod === RWR) color = colors.GREEN; + else color = colors.WHITE; var contactPolyline = new Polyline([startLatLng, endLatLng], { color: color, weight: 3, @@ -1742,13 +1745,13 @@ export abstract class Unit extends CustomMarker { this.#acquisitionCircle.addTo(getApp().getMap()); switch (this.getCoalition()) { case "red": - this.#acquisitionCircle.options.color = "#D42121"; + this.#acquisitionCircle.options.color = adjustBrightness(colors.RED_COALITION, -20); break; case "blue": - this.#acquisitionCircle.options.color = "#017DC1"; + this.#acquisitionCircle.options.color = adjustBrightness(colors.BLUE_COALITION, -20); break; default: - this.#acquisitionCircle.options.color = "#111111"; + this.#acquisitionCircle.options.color = adjustBrightness(colors.NEUTRAL_COALITION, -20); break; } } @@ -1768,13 +1771,13 @@ export abstract class Unit extends CustomMarker { this.#engagementCircle.addTo(getApp().getMap()); switch (this.getCoalition()) { case "red": - this.#engagementCircle.options.color = "#FF5858"; + this.#engagementCircle.options.color = colors.RED_COALITION; break; case "blue": - this.#engagementCircle.options.color = "#3BB9FF"; + this.#engagementCircle.options.color = colors.BLUE_COALITION; break; default: - this.#engagementCircle.options.color = "#CFD9E8"; + this.#engagementCircle.options.color = colors.NEUTRAL_COALITION; break; } } diff --git a/frontend/react/src/unit/unitsmanager.ts b/frontend/react/src/unit/unitsmanager.ts index aff29e9b..648f7ec3 100644 --- a/frontend/react/src/unit/unitsmanager.ts +++ b/frontend/react/src/unit/unitsmanager.ts @@ -19,8 +19,6 @@ import { citiesDatabase } from "./databases/citiesdatabase"; import { TemporaryUnitMarker } from "../map/markers/temporaryunitmarker"; import { Contact, UnitBlueprint, UnitData, UnitSpawnTable } from "../interfaces"; import { Group } from "./group"; -import { UnitDataFileExport } from "./importexport/unitdatafileexport"; -import { UnitDataFileImport } from "./importexport/unitdatafileimport"; import { CoalitionCircle } from "../map/coalitionarea/coalitioncircle"; import { ContextActionSet } from "./contextactionset"; import { @@ -52,8 +50,6 @@ export class UnitsManager { #selectionEventDisabled: boolean = false; #units: { [ID: number]: Unit } = {}; #groups: { [groupName: string]: Group } = {}; - #unitDataExport!: UnitDataFileExport; - #unitDataImport!: UnitDataFileImport; #unitDatabase: UnitDatabase; #protectionCallback: (units: Unit[]) => void = (units) => {}; #AWACSReference: Unit | null = null; @@ -1421,22 +1417,6 @@ export class UnitsManager { }); } - /** Export all the ground and navy units to file. Does not work on Aircraft and Helicopter units. - * TODO: Extend to aircraft and helicopters - */ - exportToFile() { - if (!this.#unitDataExport) this.#unitDataExport = new UnitDataFileExport("unit-export-dialog"); - this.#unitDataExport.showForm(Object.values(this.#units)); - } - - /** Import ground and navy units from file - * TODO: extend to support aircraft and helicopters - */ - importFromFile() { - if (!this.#unitDataImport) this.#unitDataImport = new UnitDataFileImport("unit-import-dialog"); - this.#unitDataImport.selectFile(); - } - /** Spawn a new group of units * * @param category Category of the new units