mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Started working on unit control panel, typed props
This commit is contained in:
parent
f18212dac4
commit
c7ecd2422a
@ -47,4 +47,6 @@ export type MGRS = {
|
||||
rowLetter: string,
|
||||
string: string,
|
||||
zoneNumber: string
|
||||
}
|
||||
}
|
||||
|
||||
export type Coalition = "blue" | "neutral" | "red";
|
||||
@ -1,8 +1,12 @@
|
||||
import React, { useId, useEffect, useRef, useState } from "react"
|
||||
import React, { useEffect, useRef, useState } from "react"
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faArrowCircleDown } from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
export function OlAccordion(props) {
|
||||
export function OlAccordion(props: {
|
||||
title: string,
|
||||
children?: JSX.Element | JSX.Element[],
|
||||
showArrows?: boolean
|
||||
}) {
|
||||
var [open, setOpen] = useState(false);
|
||||
var [scrolledUp, setScrolledUp] = useState(true);
|
||||
var [scrolledDown, setScrolledDown] = useState(false);
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import React from "react";
|
||||
import React, { ChangeEvent } from "react";
|
||||
|
||||
export function OlCheckbox(props) {
|
||||
export function OlCheckbox(props: {
|
||||
checked: boolean,
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => void
|
||||
}) {
|
||||
return <input onChange={props.onChange}
|
||||
type="checkbox"
|
||||
checked={props.checked}
|
||||
|
||||
@ -1,9 +1,19 @@
|
||||
import React from "react";
|
||||
import { Coalition } from "../../types/types";
|
||||
|
||||
export function OlCoalitionToggle() {
|
||||
return <div className="inline-flex items-center cursor-pointer">
|
||||
<input type="checkbox" value="" className="sr-only peer" />
|
||||
<div className="relative w-14 h-7 bg-gray-200 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:start-[4px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-6 after:w-6 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
|
||||
<span className="ms-3 text-sm font-medium text-gray-900 dark:text-gray-300">Large toggle</span>
|
||||
export function OlCoalitionToggle(props: {
|
||||
coalition: Coalition,
|
||||
onClick: () => void
|
||||
}) {
|
||||
return <div className="inline-flex items-center cursor-pointer" onClick={props.onClick}>
|
||||
<button className="sr-only peer" />
|
||||
<div data-coalition={props.coalition} className={"relative w-14 h-7 bg-gray-200 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-blue-300 " +
|
||||
"dark:peer-focus:ring-blue-800 rounded-full peer " +
|
||||
" after:content-[''] after:absolute after:top-0.5 after:start-[4px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-6 " +
|
||||
"after:w-6 after:transition-all dark:border-gray-600 " +
|
||||
"data-[coalition='neutral']:after:translate-x-[50%] rtl:data-[coalition='neutral']:after:-translate-x-[50%] data-[coalition='neutral']:after:border-white " +
|
||||
"data-[coalition='red']:after:translate-x-full rtl:data-[coalition='red']:after:-translate-x-full data-[coalition='red']:after:border-white " +
|
||||
" data-[coalition='blue']:bg-blue-600 data-[coalition='neutral']:bg-gray-400 data-[coalition='red']:bg-red-500"}></div>
|
||||
<span className="ms-3 text-sm font-medium text-gray-900 dark:text-gray-300">Coalition ({props.coalition[0].toLocaleUpperCase() + props.coalition.substring(1)})</span>
|
||||
</div>
|
||||
}
|
||||
@ -1,8 +1,15 @@
|
||||
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||
|
||||
export function OlDropdown(props) {
|
||||
export function OlDropdown(props: {
|
||||
className: string,
|
||||
leftIcon?: IconProp,
|
||||
rightIcon?: IconProp,
|
||||
label: string,
|
||||
children?: JSX.Element | JSX.Element[]
|
||||
}) {
|
||||
var [open, setOpen] = useState(false);
|
||||
var contentRef = useRef(null);
|
||||
var buttonRef = useRef(null);
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
import React, { useState } from "react";
|
||||
import React from "react";
|
||||
|
||||
export function OlLabelToggle(props) {
|
||||
var [toggled, setToggled] = useState(false);
|
||||
|
||||
return <button onClick={() => {setToggled(!toggled)}} className=" relative flex flex-row flex-none my-auto contents-center justify-between w-32 h-10 border-[1px] dark:border-transparent dark:bg-[#2A3949] rounded-lg py-[5px] px-1 select-none cursor-pointer focus:ring-2 focus:outline-none focus:ring-blue-300 dark:hover:bg-gray-800 dark:focus:ring-blue-800">
|
||||
<span data-toggled={toggled} className="absolute my-auto h-[28px] w-[54px] bg-blue-500 rounded-md data-[toggled='true']:translate-x-16 transition-transform"></span>
|
||||
<span data-active={!toggled} className="my-auto dark:data-[active='true']:text-white font-normal dark:data-[active='false']:text-gray-400 pl-3 z-ui-2 transition-colors">{props.leftLabel}</span>
|
||||
<span data-active={toggled} className="my-auto dark:data-[active='true']:text-white font-normal dark:data-[active='false']:text-gray-400 pr-3 z-ui-2 transition-colors">{props.rightLabel}</span>
|
||||
export function OlLabelToggle(props: {
|
||||
toggled: boolean | undefined,
|
||||
leftLabel: string,
|
||||
rightLabel: string,
|
||||
onClick: () => void
|
||||
}) {
|
||||
return <button onClick={props.onClick} className=" relative flex flex-row flex-none my-auto contents-center justify-between w-32 h-10 border-[1px] dark:border-transparent dark:bg-[#2A3949] rounded-lg py-[5px] px-1 select-none cursor-pointer focus:ring-2 focus:outline-none focus:ring-blue-300 dark:hover:bg-gray-800 dark:focus:ring-blue-800">
|
||||
<span data-toggled={props.toggled} className="absolute my-auto h-[28px] w-[54px] bg-blue-500 rounded-md data-[toggled='true']:translate-x-16 transition-transform"></span>
|
||||
<span data-active={!props.toggled} className="my-auto dark:data-[active='true']:text-white font-normal dark:data-[active='false']:text-gray-400 pl-3 z-ui-2 transition-colors">{props.leftLabel}</span>
|
||||
<span data-active={props.toggled} className="my-auto dark:data-[active='true']:text-white font-normal dark:data-[active='false']:text-gray-400 pr-3 z-ui-2 transition-colors">{props.rightLabel}</span>
|
||||
</button>
|
||||
}
|
||||
@ -1,15 +1,22 @@
|
||||
import React, {useEffect, useId} from "react";
|
||||
import React, {ChangeEvent, useEffect, useId} from "react";
|
||||
|
||||
export function OlNumberInput(props) {
|
||||
export function OlNumberInput(props: {
|
||||
value: number,
|
||||
min: number,
|
||||
max: number,
|
||||
onDecrease: () => void,
|
||||
onIncrease: () => void,
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => void
|
||||
}) {
|
||||
return <div className="w-fit">
|
||||
<div className="relative flex items-center max-w-[8rem]">
|
||||
<button type="button" className="bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-600 dark:border-gray-600 hover:bg-gray-200 border border-gray-300 rounded-s-lg p-3 h-10 focus:ring-gray-100 dark:focus:ring-gray-700 focus:ring-2 focus:outline-none">
|
||||
<button type="button" onClick={props.onDecrease} className="bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-600 dark:border-gray-600 hover:bg-gray-200 border border-gray-300 rounded-s-lg p-3 h-10 focus:ring-gray-100 dark:focus:ring-gray-700 focus:ring-2 focus:outline-none">
|
||||
<svg className="w-3 h-3 text-gray-900 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 2">
|
||||
<path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M1 1h16"/>
|
||||
</svg>
|
||||
</button>
|
||||
<input type="text" className="bg-gray-50 border-x-0 border-gray-300 h-10 text-center text-gray-900 text-sm focus:ring-blue-500 focus:border-blue-500 block w-full py-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder={props.placeHolder} />
|
||||
<button type="button" className="bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-600 dark:border-gray-600 hover:bg-gray-200 border border-gray-300 rounded-e-lg p-3 h-10 focus:ring-gray-100 dark:focus:ring-gray-700 focus:ring-2 focus:outline-none">
|
||||
<input type="text" onChange={props.onChange} min={props.min} max={props.max} className="bg-gray-50 border-x-0 border-gray-300 h-10 text-center text-gray-900 text-sm focus:ring-blue-500 focus:border-blue-500 block w-full py-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" value={props.value} />
|
||||
<button type="button" onClick={props.onIncrease} className="bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-600 dark:border-gray-600 hover:bg-gray-200 border border-gray-300 rounded-e-lg p-3 h-10 focus:ring-gray-100 dark:focus:ring-gray-700 focus:ring-2 focus:outline-none">
|
||||
<svg className="w-3 h-3 text-gray-900 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 18">
|
||||
<path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 1v16M1 9h16"/>
|
||||
</svg>
|
||||
|
||||
@ -1,6 +1,12 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import React, { ChangeEvent, useEffect, useRef } from "react";
|
||||
|
||||
export function OlRangeSlider(props) {
|
||||
export function OlRangeSlider(props: {
|
||||
value: number | undefined,
|
||||
min?: number,
|
||||
max?: number,
|
||||
step?: number,
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => void
|
||||
}) {
|
||||
var elementRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
@ -14,10 +20,10 @@ export function OlRangeSlider(props) {
|
||||
|
||||
return <input type="range"
|
||||
ref={elementRef}
|
||||
onChange={(ev) => { props.onChange(Number(ev.target?.value ?? props.value)) }}
|
||||
onChange={props.onChange}
|
||||
value={props.value}
|
||||
min={props.minValue ?? 0}
|
||||
max={props.maxValue ?? 100}
|
||||
min={props.min ?? 0}
|
||||
max={props.max ?? 100}
|
||||
step={props.step ?? 1}
|
||||
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700" />
|
||||
}
|
||||
@ -1,8 +1,10 @@
|
||||
import { faMultiply, faSearch } from "@fortawesome/free-solid-svg-icons"
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
||||
import React, {useId, useRef} from "react"
|
||||
import React, {ChangeEvent, useId, useRef} from "react"
|
||||
|
||||
export function OlSearchBar(props) {
|
||||
export function OlSearchBar(props: {
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => void
|
||||
}) {
|
||||
const searchId = useId();
|
||||
const inputRef = useRef(null);
|
||||
|
||||
@ -10,13 +12,13 @@ export function OlSearchBar(props) {
|
||||
inputRef.current && ((inputRef.current as HTMLInputElement).value = '');
|
||||
}
|
||||
|
||||
return <div {...props}>
|
||||
return <div>
|
||||
<label htmlFor={searchId} className="mb-2 text-sm font-medium text-gray-900 sr-only dark:text-white">Search</label>
|
||||
<div className="relative">
|
||||
<div className="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
|
||||
<FontAwesomeIcon icon={faSearch} className="dark:text-gray-400" />
|
||||
</div>
|
||||
<input type="search" ref={inputRef} id={searchId} className="block w-full p-3 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Search" required />
|
||||
<input type="search" ref={inputRef} id={searchId} onChange= {props.onChange} className="block w-full p-3 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Search" required />
|
||||
<FontAwesomeIcon icon={faMultiply} className="absolute cursor-pointer end-4 bottom-4 my-auto dark:text-gray-400 text-sm" onClick={resetSearch}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,13 @@
|
||||
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
||||
import React from "react"
|
||||
|
||||
export function OlStateButton(props) {
|
||||
export function OlStateButton(props: {
|
||||
className?: string,
|
||||
checked: boolean,
|
||||
icon: IconProp,
|
||||
onClick: () => void
|
||||
}) {
|
||||
const className = (props.className ?? '') + ` h-[40px] w-[40px] flex-none font-medium rounded-md text-sm dark:bg-[#2A3949] dark:data-[checked='true']:bg-blue-500 dark:text-white dark:border-gray-600 `;
|
||||
|
||||
return <button onClick={props.onClick} data-checked={props.checked} type="button" className={className}>
|
||||
@ -9,7 +15,12 @@ export function OlStateButton(props) {
|
||||
</button>
|
||||
}
|
||||
|
||||
export function OlRoundStateButton(props) {
|
||||
export function OlRoundStateButton(props: {
|
||||
className?: string,
|
||||
checked: boolean,
|
||||
icon: IconProp,
|
||||
onClick: () => void
|
||||
}) {
|
||||
const className = (props.className ?? '') + ` h-8 w-8 flex-none m-auto border border-gray-900 font-medium rounded-full text-sm dark:bg-[transparent] dark:data-[checked='true']:bg-white dark:text-white dark:data-[checked='true']:text-gray-900 dark:border-gray-600 `;
|
||||
|
||||
return <button onClick={props.onClick} data-checked={props.checked} type="button" className={className}>
|
||||
|
||||
@ -1,8 +1,14 @@
|
||||
import React from "react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||
import { UnitBlueprint } from "../../interfaces";
|
||||
|
||||
export function OlUnitEntryList(props) {
|
||||
return <div {...props} className="relative text-sm cursor-pointer select-none flex justify-between items-center dark:text-gray-300 dark:hover:bg-white dark:hover:bg-opacity-10 px-2 py-1 rounded-sm mr-2">
|
||||
export function OlUnitEntryList(props: {
|
||||
icon: IconProp,
|
||||
blueprint: UnitBlueprint,
|
||||
onClick: () => void
|
||||
}) {
|
||||
return <div onClick={props.onClick} className="relative text-sm cursor-pointer select-none flex justify-between items-center dark:text-gray-300 dark:hover:bg-white dark:hover:bg-opacity-10 px-2 py-1 rounded-sm mr-2">
|
||||
<FontAwesomeIcon icon={props.icon} className="text-sm"></FontAwesomeIcon>
|
||||
<div className="font-normal text-left flex-1 px-2">{props.blueprint.label}</div>
|
||||
<div className="bg-black bg-opacity-20 dark:text-gray-400 rounded-full px-2 py-0.5 text-xs">{props.blueprint.era === "WW2" ? "WW2" : props.blueprint.era.split(" ").map((word) => {
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
import React from "react";
|
||||
import { UnitBlueprint } from "../../interfaces";
|
||||
import { Coalition } from "../../types/types";
|
||||
|
||||
export function OlUnitSummary(props: {blueprint: UnitBlueprint}) {
|
||||
return <div {...props} className="relative border-l-4 border-blue-600 flex flex-col gap-2 p-2 pt-4 items-start shadow-lg bg-white hover:bg-gray-100 dark:bg-[#243141] dark:hover:bg-gray-700">
|
||||
export function OlUnitSummary(props: {
|
||||
blueprint: UnitBlueprint,
|
||||
coalition: Coalition
|
||||
}) {
|
||||
return <div data-coalition={props.coalition} className="relative border-l-4 flex flex-col gap-2 p-2 pt-4 items-start shadow-lg bg-white hover:bg-gray-100 dark:bg-[#243141] dark:hover:bg-gray-700 data-[coalition='blue']:border-blue-600 data-[coalition='neutral']:border-gray-400 data-[coalition='red']:border-red-500">
|
||||
<div className="flex flex-row gap-2 content-center">
|
||||
<img className="object-cover h-8 ml-2 rounded-tl-md rotate-180 invert" src={"images/units/"+props.blueprint.filename} alt="" />
|
||||
<div className="my-auto w-full font-bold tracking-tight text-gray-900 dark:text-white">{props.blueprint.label}</div>
|
||||
|
||||
@ -2,16 +2,23 @@ import { faArrowLeft } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import React from "react";
|
||||
|
||||
export function Menu(props) {
|
||||
return <div data-open={props.open} className="w-[430px] absolute top-[62px] left-16 z-ui-0 h-screen overflow-y-auto transition-transform data-[open='false']:-translate-x-full bg-gray-200 dark:bg-gray-800" tabIndex={-1}>
|
||||
<h5 className="w-full inline-flex items-center pb-3 p-4 shadow-lg dark:border-gray-700 text-base font-semibold text-gray-900 dark:text-gray-400">
|
||||
{props.showBackButton && <FontAwesomeIcon onClick={props.onBackCallback ?? (() => {})} icon={faArrowLeft} className="mr-4 cursor-pointer dark:hover:bg-gray-600 p-2 rounded-md"/>} {props.title}
|
||||
<button type="button" onClick={props.closeCallback} className="text-gray-900 dark:text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 flex items-center justify-center dark:hover:bg-gray-600 dark:hover:text-white ml-auto" >
|
||||
<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" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
|
||||
</svg>
|
||||
</button>
|
||||
</h5>
|
||||
{props.children}
|
||||
</div>
|
||||
export function Menu(props: {
|
||||
title: string,
|
||||
open: boolean,
|
||||
onClose: () => void,
|
||||
onBack?: () => void,
|
||||
showBackButton?: boolean,
|
||||
children?: JSX.Element | JSX.Element[],
|
||||
}) {
|
||||
return <div data-open={props.open} className="w-[430px] absolute top-[62px] left-16 z-ui-0 h-screen overflow-y-auto transition-transform data-[open='false']:-translate-x-full bg-gray-200 dark:bg-gray-800" tabIndex={-1}>
|
||||
<h5 className="w-full inline-flex items-center pb-3 p-4 shadow-lg dark:border-gray-700 text-base font-semibold text-gray-900 dark:text-gray-400">
|
||||
{props.showBackButton && <FontAwesomeIcon onClick={props.onBack ?? (() => { })} icon={faArrowLeft} className="mr-4 cursor-pointer dark:hover:bg-gray-600 p-2 rounded-md" />} {props.title}
|
||||
<button type="button" onClick={props.onClose} className="text-gray-900 dark:text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 flex items-center justify-center dark:hover:bg-gray-600 dark:hover:text-white ml-auto" >
|
||||
<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" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
|
||||
</svg>
|
||||
</button>
|
||||
</h5>
|
||||
{props.children}
|
||||
</div>
|
||||
}
|
||||
@ -7,7 +7,7 @@ import { OlDropdownItem, OlDropdown } from '../components/oldropdown';
|
||||
import { OlLabelToggle } from '../components/ollabeltoggle';
|
||||
import { getApp } from '../../olympusapp';
|
||||
|
||||
export function Header(props) {
|
||||
export function Header() {
|
||||
return <StateConsumer>
|
||||
{(appState) =>
|
||||
<EventsConsumer>
|
||||
@ -18,7 +18,7 @@ export function Header(props) {
|
||||
<img src="images/icon.png" className='h-10 w-10 p-0 rounded-md mr-2 cursor-pointer'></img>
|
||||
</div>
|
||||
<div className="ml-auto">
|
||||
<OlRoundStateButton icon={faLock} />
|
||||
<OlRoundStateButton icon={faLock} checked={false} onClick={() => {}}/>
|
||||
</div>
|
||||
<div className="flex flex-row h-fit items-center justify-start gap-2">
|
||||
{
|
||||
@ -65,8 +65,8 @@ export function Header(props) {
|
||||
checked={!appState.mapHiddenTypes['neutral']}
|
||||
icon={faShield} className={"!text-gray-500"} />
|
||||
</div>
|
||||
<OlLabelToggle value={false} leftLabel={"Live"} rightLabel={"Map"}></OlLabelToggle>
|
||||
<OlStateButton icon={faCamera} />
|
||||
<OlLabelToggle toggled={false} leftLabel={"Live"} rightLabel={"Map"} onClick={() => {}}></OlLabelToggle>
|
||||
<OlStateButton checked={false} icon={faCamera} onClick={() => {}} />
|
||||
<OlDropdown label="DCS Sat" className="w-40">
|
||||
<OlDropdownItem className="w-full">DCS Sat</OlDropdownItem>
|
||||
<OlDropdownItem className="w-full">DCS Alt</OlDropdownItem>
|
||||
|
||||
@ -5,8 +5,17 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { VERSION } from "../../olympusapp";
|
||||
import { faGithub } from "@fortawesome/free-brands-svg-icons";
|
||||
|
||||
export function MainMenu(props) {
|
||||
return <Menu {...props} title="DCS Olympus">
|
||||
export function MainMenu(props: {
|
||||
open: boolean,
|
||||
onClose: () => void,
|
||||
children?: JSX.Element | JSX.Element[],
|
||||
}) {
|
||||
return <Menu
|
||||
title="DCS Olympus"
|
||||
open={props.open}
|
||||
showBackButton={false}
|
||||
onClose={props.onClose}
|
||||
>
|
||||
<div className="flex flex-col p-5 gap-2 text-md font-normal font text-gray-900 dark:text-white">
|
||||
<div className="flex gap-3 p-1 cursor-pointer select-none hover:bg-gray-900/10 dark:hover:bg-white/10 rounded-sm content-center text-green-700 dark:text-[#8BFF63]"><FontAwesomeIcon icon={faCheckCircle} className="my-auto" />Version {VERSION}</div>
|
||||
<div className="flex gap-3 p-1 cursor-pointer select-none hover:bg-gray-900/10 dark:hover:bg-white/10 rounded-sm content-center"><FontAwesomeIcon icon={faGithub} className="my-auto w-4 text-gray-800 dark:text-[#435367]" />Overview</div>
|
||||
|
||||
@ -4,7 +4,7 @@ import { faPlus, faGamepad, faRuler, faPencil, faListDots } from '@fortawesome/f
|
||||
import { EventsConsumer } from '../../eventscontext';
|
||||
import { StateConsumer } from '../../statecontext';
|
||||
|
||||
export function SideBar(props) {
|
||||
export function SideBar() {
|
||||
return <StateConsumer>
|
||||
{(appState) =>
|
||||
<EventsConsumer>
|
||||
@ -14,7 +14,7 @@ export function SideBar(props) {
|
||||
<div className="flex flex-col items-center justify-center gap-2.5">
|
||||
<OlStateButton onClick={events.toggleMainMenuVisible} checked={appState.mainMenuVisible} icon={faListDots}></OlStateButton>
|
||||
<OlStateButton onClick={events.toggleSpawnMenuVisible} checked={appState.spawnMenuVisible} icon={faPlus}></OlStateButton>
|
||||
<OlStateButton /* onClick={events.toggleUnitControlMenuVisible} checked={appState.unitControlMenuVisible} */ icon={faGamepad}></OlStateButton>
|
||||
<OlStateButton onClick={events.toggleUnitControlMenuVisible} checked={appState.unitControlMenuVisible} icon={faGamepad}></OlStateButton>
|
||||
<OlStateButton onClick={events.toggleMeasureMenuVisible} checked={appState.measureMenuVisible} icon={faRuler}></OlStateButton>
|
||||
<OlStateButton onClick={events.toggleDrawingMenuVisible} checked={appState.drawingMenuVisible} icon={faPencil}></OlStateButton>
|
||||
</div>
|
||||
|
||||
@ -7,16 +7,35 @@ import { OlAccordion } from "../components/olaccordion";
|
||||
import { getApp } from "../../olympusapp";
|
||||
import { OlUnitEntryList } from "../components/olunitlistentry";
|
||||
import { UnitSpawnMenu } from "./unitspawnmenu";
|
||||
import { UnitBlueprint } from "../../interfaces";
|
||||
|
||||
library.add(faPlus);
|
||||
|
||||
export function SpawnMenu(props) {
|
||||
var [blueprint, setBlueprint] = useState(null);
|
||||
function filterUnits(blueprints: {[key: string]: UnitBlueprint}, filterString: string) {
|
||||
var filteredUnits = {};
|
||||
if (blueprints) {
|
||||
Object.entries(blueprints).forEach(([key, value]) => {
|
||||
if (value.enabled && (filterString === "" || value.label.includes(filterString)))
|
||||
filteredUnits[key] = value;
|
||||
});
|
||||
}
|
||||
return filteredUnits;
|
||||
}
|
||||
|
||||
const filteredAircraft = getApp()?.getAircraftDatabase()?.blueprints ?? {};
|
||||
const filteredHelicopters = getApp()?.getHelicopterDatabase()?.blueprints ?? {};
|
||||
const filteredNavyUnits = getApp()?.getNavyUnitDatabase()?.blueprints ?? {};
|
||||
export function SpawnMenu(props: {
|
||||
open: boolean,
|
||||
onClose: () => void,
|
||||
children?: JSX.Element | JSX.Element[],
|
||||
}) {
|
||||
var [blueprint, setBlueprint] = useState(null as (null | UnitBlueprint));
|
||||
var [filterString, setFilterString] = useState("");
|
||||
|
||||
/* Filter aircrafts, helicopters, and navyunits */
|
||||
const filteredAircraft = filterUnits(getApp()?.getAircraftDatabase()?.blueprints, filterString);
|
||||
const filteredHelicopters = filterUnits(getApp()?.getHelicopterDatabase()?.blueprints, filterString);
|
||||
const filteredNavyUnits = filterUnits(getApp()?.getNavyUnitDatabase()?.blueprints, filterString);
|
||||
|
||||
/* Split ground units between air defence and all others */
|
||||
var filteredAirDefense = {};
|
||||
var filteredGroundUnits = {};
|
||||
Object.keys(getApp()?.getGroundUnitDatabase()?.blueprints ?? {}).forEach((key) => {
|
||||
@ -28,58 +47,64 @@ export function SpawnMenu(props) {
|
||||
filteredGroundUnits[key] = blueprint;
|
||||
}
|
||||
});
|
||||
filteredAirDefense = filterUnits(filteredAirDefense, filterString);
|
||||
filteredGroundUnits = filterUnits(filteredGroundUnits, filterString);
|
||||
|
||||
return <Menu {...props}
|
||||
title="Spawn menu"
|
||||
titleIcon="fa-solid fa-plus"
|
||||
showBackButton={blueprint !== null}
|
||||
onBackCallback={() => setBlueprint(null)}
|
||||
onBack={() => setBlueprint(null)}
|
||||
>
|
||||
{!blueprint && <div className="p-5">
|
||||
<OlSearchBar className="mb-4" />
|
||||
<OlAccordion title="Aircrafts">
|
||||
<div className="flex flex-col gap-1 max-h-80 overflow-y-scroll">
|
||||
{Object.keys(filteredAircraft).map((key) => {
|
||||
const blueprint = getApp().getAircraftDatabase().blueprints[key];
|
||||
return <OlUnitEntryList key={key} icon={faJetFighter} blueprint={blueprint} onClick={() => setBlueprint(blueprint)} />
|
||||
})}
|
||||
</div>
|
||||
</OlAccordion>
|
||||
<OlAccordion title="Helicopters">
|
||||
<div className="flex flex-col gap-1 max-h-80 overflow-y-scroll">
|
||||
{Object.keys(filteredHelicopters).map((key) => {
|
||||
return <OlUnitEntryList key={key} icon={faHelicopter} blueprint={getApp().getHelicopterDatabase().blueprints[key]} />
|
||||
})}
|
||||
</div>
|
||||
</OlAccordion>
|
||||
<OlAccordion title="Air defence (SAM & AAA)">
|
||||
<div className="flex flex-col gap-1 max-h-80 overflow-y-scroll">
|
||||
{Object.keys(filteredAirDefense).map((key) => {
|
||||
return <OlUnitEntryList key={key} icon={faShieldAlt} blueprint={getApp().getGroundUnitDatabase().blueprints[key]} />
|
||||
})}
|
||||
</div>
|
||||
</OlAccordion>
|
||||
<OlAccordion title="Ground units">
|
||||
<div className="flex flex-col gap-1 max-h-80 overflow-y-scroll">
|
||||
{Object.keys(filteredGroundUnits).map((key) => {
|
||||
const blueprint = getApp().getGroundUnitDatabase().blueprints[key];
|
||||
return <OlUnitEntryList key={key} icon={faTruck} blueprint={blueprint} onClick={() => setBlueprint(blueprint)} />
|
||||
})}
|
||||
</div>
|
||||
</OlAccordion>
|
||||
<OlAccordion title="Ships and submarines">
|
||||
<div className="flex flex-col gap-1 max-h-80 overflow-y-scroll">
|
||||
{Object.keys(filteredNavyUnits).map((key) => {
|
||||
return <OlUnitEntryList key={key} icon={faShip} blueprint={getApp().getNavyUnitDatabase().blueprints[key]} />
|
||||
})}
|
||||
</div>
|
||||
</OlAccordion>
|
||||
<OlAccordion title="Effects (smokes, explosions etc)">
|
||||
<>
|
||||
{(blueprint === null) && <div className="p-5">
|
||||
<OlSearchBar onChange={(ev) => setFilterString(ev.target.value)}/>
|
||||
<OlAccordion title={`Aircrafts (${Object.keys(filteredAircraft).length})`}>
|
||||
<div className="flex flex-col gap-1 max-h-80 overflow-y-scroll">
|
||||
{Object.keys(filteredAircraft).map((key) => {
|
||||
const blueprint = getApp().getAircraftDatabase().blueprints[key];
|
||||
return <OlUnitEntryList key={key} icon={faJetFighter} blueprint={blueprint} onClick={() => setBlueprint(blueprint)} />
|
||||
})}
|
||||
</div>
|
||||
</OlAccordion>
|
||||
<OlAccordion title={`Helicopters (${Object.keys(filteredHelicopters).length})`}>
|
||||
<div className="flex flex-col gap-1 max-h-80 overflow-y-scroll">
|
||||
{Object.keys(filteredHelicopters).map((key) => {
|
||||
const blueprint = getApp().getHelicopterDatabase().blueprints[key];
|
||||
return <OlUnitEntryList key={key} icon={faHelicopter} blueprint={blueprint} onClick={() => setBlueprint(blueprint)} />
|
||||
})}
|
||||
</div>
|
||||
</OlAccordion>
|
||||
<OlAccordion title={`SAM & AAA (${Object.keys(filteredAirDefense).length})`}>
|
||||
<div className="flex flex-col gap-1 max-h-80 overflow-y-scroll">
|
||||
{Object.keys(filteredAirDefense).map((key) => {
|
||||
const blueprint = getApp().getGroundUnitDatabase().blueprints[key];
|
||||
return <OlUnitEntryList key={key} icon={faShieldAlt} blueprint={blueprint} onClick={() => setBlueprint(blueprint)} />
|
||||
})}
|
||||
</div>
|
||||
</OlAccordion>
|
||||
<OlAccordion title={`Ground Units (${Object.keys(filteredGroundUnits).length})`}>
|
||||
<div className="flex flex-col gap-1 max-h-80 overflow-y-scroll">
|
||||
{Object.keys(filteredGroundUnits).map((key) => {
|
||||
const blueprint = getApp().getGroundUnitDatabase().blueprints[key];
|
||||
return <OlUnitEntryList key={key} icon={faTruck} blueprint={blueprint} onClick={() => setBlueprint(blueprint)} />
|
||||
})}
|
||||
</div>
|
||||
</OlAccordion>
|
||||
<OlAccordion title={`Ships and submarines (${Object.keys(filteredNavyUnits).length})`}>
|
||||
<div className="flex flex-col gap-1 max-h-80 overflow-y-scroll">
|
||||
{Object.keys(filteredNavyUnits).map((key) => {
|
||||
const blueprint = getApp().getNavyUnitDatabase().blueprints[key];
|
||||
return <OlUnitEntryList key={key} icon={faShip} blueprint={blueprint} onClick={() => setBlueprint(blueprint)} />
|
||||
})}
|
||||
</div>
|
||||
</OlAccordion>
|
||||
<OlAccordion title="Effects (smokes, explosions etc)">
|
||||
|
||||
</OlAccordion>
|
||||
</div>
|
||||
}
|
||||
</OlAccordion>
|
||||
</div>
|
||||
}
|
||||
|
||||
{blueprint && <UnitSpawnMenu blueprint={blueprint} />}
|
||||
{!(blueprint === null) && <UnitSpawnMenu blueprint={blueprint} />}
|
||||
</>
|
||||
</Menu>
|
||||
}
|
||||
@ -3,61 +3,125 @@ import { Menu } from "./components/menu";
|
||||
import { faGamepad } from '@fortawesome/free-solid-svg-icons';
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { Unit } from "../../unit/unit";
|
||||
import { OlLabelToggle } from "../components/ollabeltoggle";
|
||||
import { OlRangeSlider } from "../components/olrangeslider";
|
||||
import { getApp } from "../../olympusapp";
|
||||
|
||||
library.add(faGamepad);
|
||||
const defaultUnitControlPanelData = {
|
||||
desiredAltitude: undefined as undefined | number,
|
||||
desiredAltitudeType: undefined as undefined | boolean
|
||||
}
|
||||
|
||||
export function UnitControlMenu(props) {
|
||||
var [open, setOpen] = useState(false);
|
||||
var [selectedUnits, setSelectedUnits] = useState([] as Unit[]);
|
||||
export function UnitControlMenu() {
|
||||
var [open, setOpen] = useState(false);
|
||||
var [selectedUnits, setSelectedUnits] = useState([] as Unit[]);
|
||||
|
||||
document.addEventListener("unitsSelection", (ev: CustomEventInit) => {
|
||||
setOpen(true);
|
||||
setSelectedUnits(ev.detail as Unit[])
|
||||
})
|
||||
var [selectedUnitsData, setSelectedUnitsData] = useState(defaultUnitControlPanelData);
|
||||
var [selectedUnitsRequestedData, setSelectedUnitsRequestedData] = useState(defaultUnitControlPanelData);
|
||||
|
||||
document.addEventListener("unitDeselection", (ev: CustomEventInit) => {
|
||||
/* */
|
||||
const minAltitude = 0;
|
||||
const maxAltitude = 60000;
|
||||
const altitudeStep = 500;
|
||||
|
||||
})
|
||||
/* When a unit is selected, open the menu */
|
||||
document.addEventListener("unitsSelection", (ev: CustomEventInit) => {
|
||||
setOpen(true);
|
||||
setSelectedUnits(ev.detail as Unit[])
|
||||
})
|
||||
|
||||
document.addEventListener("clearSelection", () => {
|
||||
setOpen(false);
|
||||
setSelectedUnits([])
|
||||
})
|
||||
/* When a unit is deselected, refresh the view */
|
||||
document.addEventListener("unitDeselection", (ev: CustomEventInit) => {
|
||||
|
||||
var unitOccurences = {
|
||||
blue: {},
|
||||
red: {},
|
||||
neutral: {}
|
||||
}
|
||||
})
|
||||
|
||||
selectedUnits.forEach((unit) => {
|
||||
if (!(unit.getName() in unitOccurences[unit.getCoalition()]))
|
||||
unitOccurences[unit.getCoalition()][unit.getName()] = 1;
|
||||
else
|
||||
unitOccurences[unit.getCoalition()][unit.getName()]++;
|
||||
})
|
||||
/* When all units are selected clean the view */
|
||||
document.addEventListener("clearSelection", () => {
|
||||
setOpen(false);
|
||||
setSelectedUnits([])
|
||||
})
|
||||
|
||||
return <Menu open={open} title="Unit control menu" titleIcon="fa-solid fa-gamepad">
|
||||
<div className="dark:bg-[#243141] h-fit p-0 flex flex-col gap-0">
|
||||
{
|
||||
<>
|
||||
{
|
||||
['blue', 'red', 'neutral'].map((coalition) => {
|
||||
return Object.keys(unitOccurences[coalition]).map((name) => {
|
||||
return <div data-coalition={coalition} className="flex justify-between content-center border-l-4 data-[coalition='blue']:border-blue-500 data-[coalition='neutral']:border-gray-500 data-[coalition='red']:border-red-500 p-2">
|
||||
<span className="dark:text-gray-300 text-sm font-medium my-auto">
|
||||
{name}
|
||||
</span>
|
||||
<span className="dark:text-gray-500 text-sm my-auto">
|
||||
x{unitOccurences[coalition][name]}
|
||||
</span>
|
||||
</div>
|
||||
})
|
||||
})
|
||||
}
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
document.addEventListener("unitUpdated", () => {
|
||||
|
||||
</Menu>
|
||||
})
|
||||
|
||||
/* Count how many units are selected of each type, divided by coalition */
|
||||
var unitOccurences = {
|
||||
blue: {},
|
||||
red: {},
|
||||
neutral: {}
|
||||
}
|
||||
selectedUnits.forEach((unit) => {
|
||||
if (!(unit.getName() in unitOccurences[unit.getCoalition()]))
|
||||
unitOccurences[unit.getCoalition()][unit.getName()] = 1;
|
||||
else
|
||||
unitOccurences[unit.getCoalition()][unit.getName()]++;
|
||||
})
|
||||
|
||||
return <Menu
|
||||
open={open}
|
||||
title="Unit control menu"
|
||||
onClose={() => { }}
|
||||
>
|
||||
<div className="dark:bg-[#243141] h-fit p-0 flex flex-col gap-0">
|
||||
<div>
|
||||
{
|
||||
<>
|
||||
{
|
||||
['blue', 'red', 'neutral'].map((coalition) => {
|
||||
return Object.keys(unitOccurences[coalition]).map((name) => {
|
||||
return <div data-coalition={coalition} className="flex justify-between content-center border-l-4 data-[coalition='blue']:border-blue-500 data-[coalition='neutral']:border-gray-500 data-[coalition='red']:border-red-500 p-2">
|
||||
<span className="dark:text-gray-300 text-sm font-medium my-auto">
|
||||
{name}
|
||||
</span>
|
||||
<span className="dark:text-gray-500 text-sm my-auto">
|
||||
x{unitOccurences[coalition][name]}
|
||||
</span>
|
||||
</div>
|
||||
})
|
||||
})
|
||||
}
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-5">
|
||||
<div>
|
||||
<div className="flex flex-row content-center justify-between">
|
||||
<div className="flex flex-col">
|
||||
<span className="font-normal dark:text-white">Altitude</span>
|
||||
<span className="dark:text-blue-500">{`${selectedUnitsRequestedData.desiredAltitude} FT`}</span>
|
||||
</div>
|
||||
<OlLabelToggle
|
||||
toggled={selectedUnitsRequestedData.desiredAltitudeType}
|
||||
leftLabel={"AGL"}
|
||||
rightLabel={"ASL"}
|
||||
onClick={() => {
|
||||
selectedUnits.forEach((unit) => {
|
||||
unit.setAltitudeType((!selectedUnitsRequestedData.desiredAltitudeType) ? "AGL" : "ASL");
|
||||
setSelectedUnitsRequestedData({
|
||||
...selectedUnitsRequestedData,
|
||||
desiredAltitudeType: !selectedUnitsRequestedData.desiredAltitudeType
|
||||
})
|
||||
})
|
||||
}} />
|
||||
</div>
|
||||
<OlRangeSlider
|
||||
onChange={(ev) => {
|
||||
selectedUnits.forEach((unit) => {
|
||||
unit.setAltitude(Number(ev.target.value));
|
||||
setSelectedUnitsRequestedData({
|
||||
...selectedUnitsRequestedData,
|
||||
desiredAltitude: Number(ev.target.value)
|
||||
})
|
||||
})
|
||||
}}
|
||||
value={selectedUnitsRequestedData.desiredAltitude}
|
||||
min={minAltitude}
|
||||
max={maxAltitude}
|
||||
step={altitudeStep} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Menu>
|
||||
}
|
||||
@ -6,11 +6,25 @@ import { OlLabelToggle } from "../components/ollabeltoggle";
|
||||
import { OlRangeSlider } from "../components/olrangeslider";
|
||||
import { OlDropdownItem, OlDropdown } from '../components/oldropdown';
|
||||
import { LoadoutBlueprint, UnitBlueprint } from "../../interfaces";
|
||||
import { Coalition } from "../../types/types";
|
||||
|
||||
export function UnitSpawnMenu(props) {
|
||||
export function UnitSpawnMenu(props: {
|
||||
blueprint: UnitBlueprint
|
||||
}) {
|
||||
/* Compute the min and max values depending on the unit type */
|
||||
const minNumber = 1;
|
||||
const maxNumber = 4;
|
||||
const minAltitude = 0;
|
||||
const maxAltitude = 30000;
|
||||
const altitudeStep = 500;
|
||||
|
||||
/* State initialization */
|
||||
var [spawnCoalition, setSpawnCoalition] = useState("blue" as Coalition);
|
||||
var [spawnNumber, setSpawnNumber] = useState(1);
|
||||
var [spawnRole, setSpawnRole] = useState("");
|
||||
var [spawnLoadoutName, setSpawnLoadout] = useState("");
|
||||
var [spawnAltitude, setSpawnAltitude] = useState(1000);
|
||||
var [spawnAltitude, setSpawnAltitude] = useState((maxAltitude - minAltitude) / 2);
|
||||
var [spawnAltitudeType, setSpawnAltitudeType] = useState(false);
|
||||
|
||||
/* Get a list of all the roles */
|
||||
const roles: string[] = [];
|
||||
@ -31,15 +45,28 @@ export function UnitSpawnMenu(props) {
|
||||
|
||||
/* Initialize the loadout */
|
||||
spawnLoadoutName === "" && loadouts.length > 0 && setSpawnLoadout(loadouts[0].name)
|
||||
|
||||
const spawnLoadout = props.blueprint.loadouts.find((loadout) => { return loadout.name === spawnLoadoutName; })
|
||||
const spawnLoadout = props.blueprint.loadouts?.find((loadout) => { return loadout.name === spawnLoadoutName; })
|
||||
|
||||
return <div className="flex flex-col gap-3">
|
||||
<OlUnitSummary blueprint={props.blueprint} />
|
||||
<OlUnitSummary blueprint={props.blueprint} coalition={spawnCoalition} />
|
||||
<div className="p-5 h-fit flex flex-col gap-2">
|
||||
<div className="flex flex-row content-center justify-between w-full">
|
||||
<OlCoalitionToggle />
|
||||
<OlNumberInput placeHolder={1} minValue={1} maxValue={4} />
|
||||
<OlCoalitionToggle
|
||||
coalition={spawnCoalition}
|
||||
onClick={() => {
|
||||
spawnCoalition === 'blue' && setSpawnCoalition('neutral');
|
||||
spawnCoalition === 'neutral' && setSpawnCoalition('red');
|
||||
spawnCoalition === 'red' && setSpawnCoalition('blue');
|
||||
}}
|
||||
/>
|
||||
<OlNumberInput
|
||||
value={spawnNumber}
|
||||
min={minNumber}
|
||||
max={maxNumber}
|
||||
onDecrease={() => { setSpawnNumber(Math.max(minNumber, spawnNumber - 1)) }}
|
||||
onIncrease={() => { setSpawnNumber(Math.min(maxNumber, spawnNumber + 1)) }}
|
||||
onChange={(ev) => { !isNaN(Number(ev.target.value)) && setSpawnNumber(Math.max(minNumber, Math.min(maxNumber, Number(ev.target.value)))) }}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex flex-row content-center justify-between">
|
||||
@ -47,9 +74,9 @@ export function UnitSpawnMenu(props) {
|
||||
<span className="font-normal dark:text-white">Altitude</span>
|
||||
<span className="dark:text-blue-500">{`${spawnAltitude} FT`}</span>
|
||||
</div>
|
||||
<OlLabelToggle value={false} leftLabel={"AGL"} rightLabel={"ASL"} />
|
||||
<OlLabelToggle toggled={spawnAltitudeType} leftLabel={"AGL"} rightLabel={"ASL"} onClick={() => setSpawnAltitudeType(!spawnAltitudeType)} />
|
||||
</div>
|
||||
<OlRangeSlider onChange={setSpawnAltitude} value={spawnAltitude} minValue={0} maxValue={30000} step={500} />
|
||||
<OlRangeSlider onChange={(ev) => setSpawnAltitude(Number(ev.target.value))} value={spawnAltitude} min={minAltitude} max={maxAltitude} step={altitudeStep} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex flex-row content-center justify-between">
|
||||
|
||||
@ -23,7 +23,7 @@ export type OlympusState = {
|
||||
mapOptions: MapOptions;
|
||||
}
|
||||
|
||||
export function UI(props) {
|
||||
export function UI() {
|
||||
var [mainMenuVisible, setMainMenuVisible] = useState(false);
|
||||
var [spawnMenuVisible, setSpawnMenuVisible] = useState(false);
|
||||
var [unitControlMenuVisible, setUnitControlMenuVisible] = useState(false);
|
||||
@ -78,9 +78,9 @@ export function UI(props) {
|
||||
<div className='flex h-full'>
|
||||
<SideBar />
|
||||
<div id='map-container' className='relative h-full w-screen top-0 left-0' />
|
||||
<MainMenu open={mainMenuVisible} closeCallback={() => setMainMenuVisible(false)} />
|
||||
<SpawnMenu open={spawnMenuVisible} closeCallback={() => setSpawnMenuVisible(false)} />
|
||||
<UnitControlMenu open={unitControlMenuVisible} closeCallback={() => setUnitControlMenuVisible(false)} />
|
||||
<MainMenu open={mainMenuVisible} onClose={() => setMainMenuVisible(false)} />
|
||||
<SpawnMenu open={spawnMenuVisible} onClose={() => setSpawnMenuVisible(false)} />
|
||||
<UnitControlMenu />
|
||||
</div>
|
||||
</div>
|
||||
</EventsProvider>
|
||||
|
||||
@ -1041,7 +1041,7 @@ export class UnitsManager {
|
||||
* @param variableGetter CallableFunction that returns the requested variable. Example: getUnitsVariable((unit: Unit) => unit.getName(), foo) will return a string value if all the units have the same name, otherwise it will return undefined.
|
||||
* @returns The value of the variable if all units have the same value, else undefined
|
||||
*/
|
||||
getSelectedUnitsVariable(variableGetter: CallableFunction) {
|
||||
getSelectedUnitsVariable(variableGetter: (unit: Unit) => any) {
|
||||
return this.getUnitsVariable(variableGetter, this.getSelectedUnits());
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user