mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
More work on responsive design for small screens
This commit is contained in:
@@ -106,10 +106,7 @@ export function OlDropdown(props: {
|
||||
type="button"
|
||||
>
|
||||
{props.leftIcon && (
|
||||
<FontAwesomeIcon
|
||||
icon={props.leftIcon}
|
||||
className={`mr-3`}
|
||||
/>
|
||||
<FontAwesomeIcon icon={props.leftIcon} className={`mr-3`} />
|
||||
)}
|
||||
<span className="overflow-hidden text-ellipsis text-nowrap">
|
||||
{props.label}
|
||||
@@ -138,7 +135,7 @@ export function OlDropdown(props: {
|
||||
ref={contentRef}
|
||||
data-open={open}
|
||||
className={`
|
||||
absolute z-ui-2 w-full divide-y divide-gray-100 overflow-y-scroll
|
||||
absolute z-ui-4 w-full divide-y divide-gray-100 overflow-y-scroll
|
||||
rounded-lg bg-white p-2 shadow
|
||||
dark:bg-gray-700
|
||||
data-[open='false']:hidden
|
||||
|
||||
@@ -38,7 +38,7 @@ export function LoginModal(props: {
|
||||
<Modal
|
||||
className={`
|
||||
inline-flex h-[75%] max-h-[530px] w-[80%] max-w-[1100px] overflow-y-auto
|
||||
scroll-smooth bg-white
|
||||
scroll-smooth bg-white z-ui-5
|
||||
dark:bg-olympus-800
|
||||
max-md:h-full max-md:max-h-full max-md:w-full max-md:rounded-none
|
||||
max-md:border-none
|
||||
@@ -111,9 +111,7 @@ export function LoginModal(props: {
|
||||
></img>
|
||||
</span>
|
||||
<div
|
||||
className={`
|
||||
flex flex-col items-start gap-1
|
||||
`}
|
||||
className={`flex flex-col items-start gap-1`}
|
||||
>
|
||||
<h1
|
||||
className={`
|
||||
@@ -143,10 +141,7 @@ export function LoginModal(props: {
|
||||
{props.commandMode === null ? (
|
||||
<>
|
||||
<div
|
||||
className={`
|
||||
flex flex-col items-start
|
||||
gap-2
|
||||
`}
|
||||
className={`flex flex-col items-start gap-2`}
|
||||
>
|
||||
<label
|
||||
className={`
|
||||
@@ -157,7 +152,7 @@ export function LoginModal(props: {
|
||||
Password{" "}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
type="password"
|
||||
onChange={(ev) =>
|
||||
setPassword(ev.currentTarget.value)
|
||||
}
|
||||
|
||||
@@ -14,10 +14,11 @@ export function Menu(props: {
|
||||
<div
|
||||
data-open={props.open}
|
||||
className={`
|
||||
absolute left-16 top-[62px] w-[430px] z-ui-0 h-screen overflow-y-auto
|
||||
bg-gray-200 backdrop-blur-lg backdrop-grayscale transition-transform
|
||||
absolute left-16 right-0 top-[60px] bg-gray-200 backdrop-grayscale
|
||||
z-ui-3 h-screen overflow-y-auto backdrop-blur-lg transition-transform
|
||||
dark:bg-olympus-800/90
|
||||
data-[open='false']:-translate-x-full
|
||||
sm:w-[400px]
|
||||
`}
|
||||
tabIndex={-1}
|
||||
>
|
||||
|
||||
39
frontend/react/src/ui/panels/mapstatepanel.tsx
Normal file
39
frontend/react/src/ui/panels/mapstatepanel.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import React, { useState } from "react";
|
||||
import { FaHandPointer } from "react-icons/fa6";
|
||||
import { IDLE, SPAWN_UNIT } from "../../constants/constants";
|
||||
|
||||
export function MapStatePanel(props: {}) {
|
||||
const [mapState, setMapState] = useState(IDLE);
|
||||
|
||||
document.addEventListener("mapStateChanged", (ev) => {
|
||||
setMapState((ev as CustomEvent).detail);
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`
|
||||
absolute bottom-6 right-[10px] w-[288px] z-ui-5 flex items-center
|
||||
justify-between rounded-lg bg-gray-200 p-3 text-sm backdrop-blur-lg
|
||||
backdrop-grayscale
|
||||
dark:bg-olympus-800/90 dark:text-gray-200
|
||||
`}
|
||||
>
|
||||
<div className={`flex w-full items-center gap-2 font-semibold`}>
|
||||
{mapState === IDLE && "IDLE"}
|
||||
{mapState === SPAWN_UNIT && (
|
||||
<div className={`flex w-full items-center`}>
|
||||
<FaHandPointer className="mr-2 text-sm text-white" />
|
||||
<div
|
||||
className={`
|
||||
w-full animate-pulse border-l-[1px] px-2 text-center
|
||||
dark:text-white
|
||||
`}
|
||||
>
|
||||
Click on the map to spawn
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { zeroAppend } from "../../other/utils";
|
||||
import { DateAndTime } from "../../interfaces";
|
||||
import { getApp } from "../../olympusapp";
|
||||
import { FaChevronDown, FaChevronUp } from "react-icons/fa6";
|
||||
|
||||
export function MiniMapPanel(props: {}) {
|
||||
var [frameRate, setFrameRate] = useState(0);
|
||||
@@ -14,14 +16,16 @@ export function MiniMapPanel(props: {}) {
|
||||
var [connected, setConnected] = useState(false);
|
||||
var [paused, setPaused] = useState(false);
|
||||
var [showMissionTime, setShowMissionTime] = useState(false);
|
||||
var [showMinimap, setShowMinimap] = useState(false);
|
||||
|
||||
document.addEventListener("serverStatusUpdated", (ev) => {
|
||||
setFrameRate(ev.detail.frameRate);
|
||||
setLoad(ev.detail.load);
|
||||
setElapsedTime(ev.detail.elapsedTime);
|
||||
setMissionTime(ev.detail.missionTime);
|
||||
setConnected(ev.detail.connected);
|
||||
setPaused(ev.detail.paused);
|
||||
const detail = (ev as CustomEvent).detail;
|
||||
setFrameRate(detail.frameRate);
|
||||
setLoad(detail.load);
|
||||
setElapsedTime(detail.elapsedTime);
|
||||
setMissionTime(detail.missionTime);
|
||||
setConnected(detail.connected);
|
||||
setPaused(detail.paused);
|
||||
});
|
||||
|
||||
// A bit of a hack to set the rounded borders to the minimap
|
||||
@@ -32,6 +36,10 @@ export function MiniMapPanel(props: {}) {
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("mapOptionsChanged", (event) => {
|
||||
setShowMinimap(getApp().getMap().getOptions().showMinimap);
|
||||
});
|
||||
|
||||
// Compute the time string depending on mission or elapsed time
|
||||
let hours = 0;
|
||||
let minutes = 0;
|
||||
@@ -63,62 +71,57 @@ export function MiniMapPanel(props: {}) {
|
||||
<div
|
||||
onClick={() => setShowMissionTime(!showMissionTime)}
|
||||
className={`
|
||||
absolute right-[10px] top-[233px] w-[288px] z-ui-0 flex items-center
|
||||
justify-between rounded-b-lg bg-gray-200 p-3 text-sm backdrop-blur-lg
|
||||
backdrop-grayscale
|
||||
absolute right-[10px]
|
||||
${showMinimap ? `top-[232px]` : `top-[70px]`}
|
||||
w-[288px] z-ui-0 flex items-center justify-between
|
||||
${showMinimap ? `rounded-b-lg` : `rounded-lg`}
|
||||
bg-gray-200 p-3 text-sm backdrop-blur-lg backdrop-grayscale
|
||||
dark:bg-olympus-800/90 dark:text-gray-200
|
||||
`}
|
||||
>
|
||||
{!connected ? (
|
||||
<div
|
||||
className={`
|
||||
flex animate-pulse items-center gap-2 font-semibold
|
||||
`}
|
||||
>
|
||||
<div
|
||||
className={`relative h-4 w-4 rounded-full bg-[#F05252]`}
|
||||
></div>
|
||||
<div className={`flex animate-pulse items-center gap-2 font-semibold`}>
|
||||
<div className={`relative h-4 w-4 rounded-full bg-[#F05252]`}></div>
|
||||
Server disconnected
|
||||
</div>
|
||||
) : paused ? (
|
||||
<div
|
||||
className={`
|
||||
flex animate-pulse items-center gap-2 font-semibold
|
||||
`}
|
||||
>
|
||||
<div
|
||||
className={`relative h-4 w-4 rounded-full bg-[#FF9900]`}
|
||||
></div>
|
||||
<div className={`flex animate-pulse items-center gap-2 font-semibold`}>
|
||||
<div className={`relative h-4 w-4 rounded-full bg-[#FF9900]`}></div>
|
||||
Server paused
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="font-semibold">
|
||||
FPS:{" "}
|
||||
<span
|
||||
style={{ color: frameRateColor }}
|
||||
className={`font-semibold`}
|
||||
>
|
||||
<span style={{ color: frameRateColor }} className={`font-semibold`}>
|
||||
{frameRate}
|
||||
</span>{" "}
|
||||
</div>
|
||||
<div className="font-semibold">
|
||||
Load:{" "}
|
||||
<span
|
||||
style={{ color: loadColor }}
|
||||
className={`font-semibold`}
|
||||
>
|
||||
<span style={{ color: loadColor }} className={`font-semibold`}>
|
||||
{load}
|
||||
</span>{" "}
|
||||
</div>
|
||||
<div className="font-semibold">
|
||||
{showMissionTime ? "MT" : "ET"}: {timeString}{" "}
|
||||
</div>
|
||||
<div
|
||||
className={`relative h-4 w-4 rounded-full bg-[#8BFF63]`}
|
||||
></div>
|
||||
<div className={`relative h-4 w-4 rounded-full bg-[#8BFF63]`}></div>
|
||||
</>
|
||||
)}
|
||||
{showMinimap ? (
|
||||
<FaChevronUp
|
||||
onClick={() => {
|
||||
getApp().getMap().setOption("showMinimap", false);
|
||||
}}
|
||||
></FaChevronUp>
|
||||
) : (
|
||||
<FaChevronDown
|
||||
onClick={() => {
|
||||
getApp().getMap().setOption("showMinimap", true);
|
||||
}}
|
||||
></FaChevronDown>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -196,6 +196,33 @@ export function Options(props: {
|
||||
G
|
||||
</kbd>
|
||||
</div>
|
||||
<div
|
||||
className={`
|
||||
group flex flex-row rounded-md justify-content cursor-pointer gap-4
|
||||
p-2
|
||||
dark:hover:bg-olympus-400
|
||||
`}
|
||||
onClick={() => {
|
||||
getApp()
|
||||
.getMap()
|
||||
.setOption("showMinimap", !props.options.showMinimap);
|
||||
}}
|
||||
>
|
||||
<OlCheckbox
|
||||
checked={props.options.showMinimap}
|
||||
onChange={() => {}}
|
||||
></OlCheckbox>
|
||||
<span>Show minimap</span>
|
||||
<kbd
|
||||
className={`
|
||||
ml-auto rounded-lg border border-gray-200 bg-gray-100 px-2 py-1.5
|
||||
text-xs font-semibold text-gray-800
|
||||
dark:border-gray-500 dark:bg-gray-600 dark:text-gray-100
|
||||
`}
|
||||
>
|
||||
?
|
||||
</kbd>
|
||||
</div>
|
||||
|
||||
{/*
|
||||
<hr className="w-auto m-2 my-1 bg-gray-700 border-[1px] dark:border-olympus-500"></hr>
|
||||
|
||||
@@ -21,16 +21,20 @@ export function SideBar() {
|
||||
{(events) => (
|
||||
<nav
|
||||
className={`
|
||||
flex flex-col z-ui-1 h-full bg-gray-300
|
||||
flex flex-col z-ui-4 h-full bg-gray-300
|
||||
dark:bg-olympus-900
|
||||
`}
|
||||
>
|
||||
<div className={`
|
||||
w-16 flex-1 flex-wrap items-center justify-center p-4
|
||||
`}>
|
||||
<div className={`
|
||||
<div
|
||||
className={`
|
||||
w-16 flex-1 flex-wrap items-center justify-center p-4
|
||||
`}
|
||||
>
|
||||
<div
|
||||
className={`
|
||||
flex flex-col items-center justify-center gap-2.5
|
||||
`}>
|
||||
`}
|
||||
>
|
||||
<OlStateButton
|
||||
onClick={events.toggleMainMenuVisible}
|
||||
checked={appState.mainMenuVisible}
|
||||
@@ -64,9 +68,11 @@ export function SideBar() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-16 flex-wrap content-end justify-center p-4">
|
||||
<div className={`
|
||||
<div
|
||||
className={`
|
||||
flex flex-col items-center justify-center gap-2.5
|
||||
`}>
|
||||
`}
|
||||
>
|
||||
<OlStateButton
|
||||
onClick={() =>
|
||||
window.open("https://github.com/Pax1601/DCSOlympus/wiki")
|
||||
|
||||
@@ -35,8 +35,9 @@ import {
|
||||
import { Coalition } from "../../types/types";
|
||||
import { ftToM, knotsToMs, mToFt, msToKnots } from "../../other/utils";
|
||||
|
||||
export function UnitControlMenu() {
|
||||
var [open, setOpen] = useState(false);
|
||||
export function UnitControlMenu(props: {
|
||||
open: boolean
|
||||
}) {
|
||||
var [selectedUnits, setSelectedUnits] = useState([] as Unit[]);
|
||||
|
||||
var [selectedUnitsData, setSelectedUnitsData] = useState({
|
||||
@@ -94,7 +95,6 @@ export function UnitControlMenu() {
|
||||
|
||||
/* When a unit is selected, open the menu */
|
||||
document.addEventListener("unitsSelection", (ev: CustomEventInit) => {
|
||||
setOpen(true);
|
||||
setSelectedUnits(ev.detail as Unit[]);
|
||||
|
||||
updateData();
|
||||
@@ -108,7 +108,6 @@ export function UnitControlMenu() {
|
||||
|
||||
/* When all units are deselected clean the view */
|
||||
document.addEventListener("clearSelection", () => {
|
||||
setOpen(false);
|
||||
setSelectedUnits([]);
|
||||
});
|
||||
|
||||
@@ -185,7 +184,7 @@ export function UnitControlMenu() {
|
||||
getApp()?.getUnitsManager()?.getSelectedUnitsCategories() ?? [];
|
||||
|
||||
return (
|
||||
<Menu open={open} title="Units selected (x)" onClose={() => {}}>
|
||||
<Menu open={props.open} title="Units selected (x)" onClose={() => {}}>
|
||||
{/* Units list */}
|
||||
<div
|
||||
className={`
|
||||
@@ -240,9 +239,11 @@ export function UnitControlMenu() {
|
||||
return ["Aircraft", "Helicopter"].includes(category);
|
||||
}) && (
|
||||
<div>
|
||||
<div className={`
|
||||
flex flex-row content-center items-center justify-between
|
||||
`}>
|
||||
<div
|
||||
className={`
|
||||
flex flex-row content-center items-center justify-between
|
||||
`}
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
<span
|
||||
className={`
|
||||
@@ -313,9 +314,11 @@ export function UnitControlMenu() {
|
||||
}
|
||||
{/* Airspeed selector */}
|
||||
<div>
|
||||
<div className={`
|
||||
flex flex-row content-center items-center justify-between
|
||||
`}>
|
||||
<div
|
||||
className={`
|
||||
flex flex-row content-center items-center justify-between
|
||||
`}
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
<span
|
||||
className={`
|
||||
@@ -417,9 +420,7 @@ export function UnitControlMenu() {
|
||||
}) && (
|
||||
<>
|
||||
{" "}
|
||||
<div
|
||||
className={`flex flex-col gap-2`}
|
||||
>
|
||||
<div className={`flex flex-col gap-2`}>
|
||||
<span
|
||||
className={`
|
||||
font-normal
|
||||
@@ -573,9 +574,7 @@ export function UnitControlMenu() {
|
||||
}) && (
|
||||
<>
|
||||
{" "}
|
||||
<div
|
||||
className={`flex flex-col gap-2`}
|
||||
>
|
||||
<div className={`flex flex-col gap-2`}>
|
||||
<span
|
||||
className={`
|
||||
font-normal
|
||||
|
||||
@@ -40,7 +40,7 @@ export function UnitMouseControlBar(props: {}) {
|
||||
|
||||
/* Deselect the context action when exiting state */
|
||||
document.addEventListener("mapStateChanged", (ev) => {
|
||||
setOpen(ev.detail === CONTEXT_ACTION);
|
||||
setOpen((ev as CustomEvent).detail === CONTEXT_ACTION);
|
||||
});
|
||||
|
||||
/* Update the current values of the shown data */
|
||||
@@ -65,8 +65,8 @@ export function UnitMouseControlBar(props: {}) {
|
||||
<>
|
||||
<div
|
||||
className={`
|
||||
absolute left-[50%] top-20 flex translate-x-[-50%] gap-2
|
||||
rounded-md bg-gray-200 p-2 z-ui-2
|
||||
absolute left-[50%] top-16 flex translate-x-[calc(-50%+2rem)]
|
||||
gap-2 rounded-md bg-gray-200 p-2 z-ui-2
|
||||
dark:bg-olympus-900
|
||||
`}
|
||||
>
|
||||
@@ -84,11 +84,9 @@ export function UnitMouseControlBar(props: {}) {
|
||||
} else {
|
||||
if (activeContextAction != contextAction) {
|
||||
setActiveContextAction(contextAction);
|
||||
getApp()
|
||||
.getMap()
|
||||
.setState(CONTEXT_ACTION, {
|
||||
contextAction: contextAction,
|
||||
});
|
||||
getApp().getMap().setState(CONTEXT_ACTION, {
|
||||
contextAction: contextAction,
|
||||
});
|
||||
} else {
|
||||
setActiveContextAction(null);
|
||||
getApp()
|
||||
@@ -105,16 +103,21 @@ export function UnitMouseControlBar(props: {}) {
|
||||
{activeContextAction && (
|
||||
<div
|
||||
className={`
|
||||
absolute left-[50%] top-36 flex translate-x-[-50%] items-center
|
||||
gap-2 rounded-md bg-gray-200 p-4 z-ui-1
|
||||
absolute left-[50%] top-32 flex translate-x-[calc(-50%+2rem)]
|
||||
items-center gap-2 rounded-md bg-gray-200 p-4 z-ui-1
|
||||
min-w-[300px]
|
||||
dark:bg-olympus-800
|
||||
`}
|
||||
>
|
||||
<FaInfoCircle className="mr-2 text-sm text-blue-500" />
|
||||
<FaInfoCircle className={`
|
||||
mr-2 hidden min-w-8 text-sm text-blue-500
|
||||
md:block
|
||||
`} />
|
||||
<div
|
||||
className={`
|
||||
border-l-[1px] px-5
|
||||
px-2
|
||||
dark:text-gray-400
|
||||
md:border-l-[1px] md:px-5
|
||||
`}
|
||||
>
|
||||
{activeContextAction.getDescription()}
|
||||
|
||||
@@ -24,6 +24,7 @@ import { LoginModal } from "./modals/login";
|
||||
import { sha256 } from "js-sha256";
|
||||
import { MiniMapPanel } from "./panels/minimappanel";
|
||||
import { UnitMouseControlBar } from "./panels/unitmousecontrolbar";
|
||||
import { MapStatePanel } from "./panels/mapstatepanel";
|
||||
|
||||
export type OlympusState = {
|
||||
mainMenuVisible: boolean;
|
||||
@@ -188,9 +189,11 @@ export function UI() {
|
||||
<div className="flex h-full">
|
||||
{loginModalVisible && (
|
||||
<>
|
||||
<div className={`
|
||||
fixed left-0 top-0 h-full w-full z-ui-3 bg-[#111111]/95
|
||||
`}></div>
|
||||
<div
|
||||
className={`
|
||||
fixed left-0 top-0 h-full w-full z-ui-5 bg-[#111111]/95
|
||||
`}
|
||||
></div>
|
||||
<LoginModal
|
||||
onLogin={(password) => {
|
||||
checkPassword(password);
|
||||
@@ -222,9 +225,10 @@ export function UI() {
|
||||
options={mapOptions}
|
||||
/>
|
||||
<MiniMapPanel />
|
||||
<UnitControlMenu />
|
||||
<UnitControlMenu open={unitControlMenuVisible} />
|
||||
<div id="map-container" className="h-full w-screen" />
|
||||
<UnitMouseControlBar />
|
||||
<MapStatePanel />
|
||||
</div>
|
||||
</div>
|
||||
</EventsProvider>
|
||||
|
||||
Reference in New Issue
Block a user