Migrate to react proper
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import { FC, useEffect, useRef, useState } from 'react';
|
||||
import OFP from './components/ofp/ofp';
|
||||
import TLR from './components/tlr/tlr';
|
||||
|
||||
interface AppProps {
|
||||
type: string;
|
||||
}
|
||||
|
||||
const App: FC<AppProps> = ({ type }) => {
|
||||
const [contentOFP, setContentOFP] = useState('');
|
||||
const [contentTLR, setContentTLR] = useState('');
|
||||
const [page, setPage] = useState(0);
|
||||
const [position, setPosition] = useState(1);
|
||||
const loopRef = useRef<number | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
loopRef.current = setInterval(() => {
|
||||
const flag = SimVar.GetSimVarValue('L:KH_FE_FPLAN_NEW_DATA', 'bool');
|
||||
if (flag) {
|
||||
SimVar.SetSimVarValue('L:KH_FE_FPLAN_NEW_DATA', 'bool', 0);
|
||||
getSB();
|
||||
}
|
||||
}, 100);
|
||||
|
||||
return () => clearInterval(loopRef.current);
|
||||
});
|
||||
|
||||
const sbID = () => {
|
||||
const config = GetStoredData('FSS_B727_EFB_CONFIG_PREFLIGHT');
|
||||
try {
|
||||
return JSON.parse(config).simBriefId as string;
|
||||
} catch (e) {
|
||||
console.error('Failed loading config.', e);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const getSB = async () => {
|
||||
try {
|
||||
const res = await fetch(`https://www.simbrief.com/api/xml.fetcher.php?username=${sbID()}&json=1`);
|
||||
if (res.ok) {
|
||||
try {
|
||||
const data = await res.json();
|
||||
|
||||
let ofp: string = data.text.plan_html;
|
||||
ofp = ofp.replace(/href=".*?"/g, '');
|
||||
|
||||
setContentOFP(ofp);
|
||||
setContentTLR(data.text.tlr_section);
|
||||
} catch (e) {
|
||||
console.error('JSON DECODE ERR', e);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('FETCH ERR', e);
|
||||
}
|
||||
};
|
||||
|
||||
const reloadSB = () => {
|
||||
SimVar.SetSimVarValue('L:KH_FE_FPLAN_NEW_DATA', 'bool', 1);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{type === 'ofp' && (
|
||||
<OFP
|
||||
content={contentOFP}
|
||||
position={position}
|
||||
page={page}
|
||||
reload={reloadSB}
|
||||
setPosition={setPosition}
|
||||
setPage={setPage}
|
||||
/>
|
||||
)}
|
||||
{type === 'tlr' && (
|
||||
<TLR
|
||||
content={contentTLR}
|
||||
position={position}
|
||||
page={page}
|
||||
reload={reloadSB}
|
||||
setPosition={setPosition}
|
||||
setPage={setPage}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 8.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1,30 @@
|
||||
#KH_CTRL {
|
||||
height: 80px;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
|
||||
.button {
|
||||
border-radius: 5px;
|
||||
border: solid 1px #000;
|
||||
padding-left: 7px;
|
||||
padding-right: 7px;
|
||||
|
||||
.icon {
|
||||
width: 30px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
&:hover:not([disabled]) {
|
||||
background-color: var(--buttonHoverColor);
|
||||
}
|
||||
|
||||
&.d180 {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
&.d90 {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Dispatch, FC, RefObject, SetStateAction } from 'react';
|
||||
import './controls.scss';
|
||||
|
||||
interface ControlsProps {
|
||||
containerRef: RefObject<HTMLDivElement | null>;
|
||||
position: number;
|
||||
page: number;
|
||||
reload: () => void;
|
||||
setPosition: Dispatch<SetStateAction<number>>;
|
||||
setPage: Dispatch<SetStateAction<number>>;
|
||||
}
|
||||
|
||||
const Controls: FC<ControlsProps> = ({ containerRef, position, page, reload, setPosition, setPage }) => {
|
||||
const cycle = () => {
|
||||
setPage((prev) => {
|
||||
const _new = (prev + 1) % 2;
|
||||
SimVar.SetSimVarValue('KH_FE_FPLAN_P1', 'bool', _new);
|
||||
return _new;
|
||||
});
|
||||
};
|
||||
|
||||
const toTop = () => {
|
||||
if (!containerRef.current) return;
|
||||
|
||||
containerRef.current.scrollTop = 0;
|
||||
};
|
||||
|
||||
const switchPosition = () => {
|
||||
setPosition((prev) => {
|
||||
let _new = prev;
|
||||
if (position === 1) _new = 2;
|
||||
else if (position === 2) _new = 1;
|
||||
SimVar.SetSimVarValue('KH_FE_FPLAN_BOARD', 'number', _new);
|
||||
return _new;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div id="KH_CTRL">
|
||||
{page === 1 && (
|
||||
<div className="button" onClick={cycle}>
|
||||
<img className="icon" src="/Pages/VCockpit/Instruments/FSS_B727/EFB/Images/get.png" />
|
||||
</div>
|
||||
)}
|
||||
<div className="button d90" onClick={toTop}>
|
||||
<img className="icon" src="/Pages/VCockpit/Instruments/FSS_B727/EFB/Images/get.png" />
|
||||
</div>
|
||||
<div className="button" onClick={reload}>
|
||||
<img className="icon" src="/Pages/VCockpit/Instruments/FSS_B727/EFB/Images/cloud.png" />
|
||||
</div>
|
||||
<div className="button" onClick={switchPosition}>
|
||||
<img
|
||||
className="icon"
|
||||
src={
|
||||
position === 1
|
||||
? '/Pages/VCockpit/Instruments/FSS_B727/KH_FE_FPLAN/assets/img/compass.png'
|
||||
: position === 2
|
||||
? '/Pages/VCockpit/Instruments/FSS_B727/KH_FE_FPLAN/assets/img/wrench.png'
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{page === 0 && (
|
||||
<div className="button d180" onClick={cycle}>
|
||||
<img className="icon" src="/Pages/VCockpit/Instruments/FSS_B727/EFB/Images/get.png" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Controls;
|
||||
@@ -0,0 +1,79 @@
|
||||
import { createRef, Dispatch, FC, SetStateAction, useEffect } from 'react';
|
||||
import Controls from '../controls/controls';
|
||||
|
||||
interface TLRProps {
|
||||
content: string;
|
||||
position: number;
|
||||
page: number;
|
||||
reload: () => void;
|
||||
setPosition: Dispatch<SetStateAction<number>>;
|
||||
setPage: Dispatch<SetStateAction<number>>;
|
||||
}
|
||||
|
||||
const OFP: FC<TLRProps> = ({ content, position, page, reload, setPosition, setPage }) => {
|
||||
const containerRef = createRef<HTMLDivElement>();
|
||||
|
||||
const defineDragScroll = (horizontalScroll = true, verticalScroll = true) => {
|
||||
if (!containerRef.current) return;
|
||||
|
||||
let pos = { top: 0, left: 0, x: 0, y: 0 };
|
||||
|
||||
const mouseDownHandler = (e: MouseEvent) => {
|
||||
if (!containerRef.current) return;
|
||||
|
||||
pos = {
|
||||
left: containerRef.current.scrollLeft,
|
||||
top: containerRef.current.scrollTop,
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
};
|
||||
document.addEventListener('mousemove', mouseMoveHandler);
|
||||
document.addEventListener('mouseup', mouseUpHandler);
|
||||
document.addEventListener('mouseleave', mouseUpHandler);
|
||||
};
|
||||
|
||||
const mouseMoveHandler = (e: MouseEvent) => {
|
||||
if (!containerRef.current) return;
|
||||
|
||||
const dx = e.clientX - pos.x;
|
||||
const dy = e.clientY - pos.y;
|
||||
|
||||
if (verticalScroll) {
|
||||
containerRef.current.scrollTop = pos.top - dy;
|
||||
}
|
||||
if (horizontalScroll) {
|
||||
containerRef.current.scrollLeft = pos.left - dx;
|
||||
}
|
||||
};
|
||||
|
||||
const mouseUpHandler = (e: MouseEvent) => {
|
||||
document.removeEventListener('mousemove', mouseMoveHandler);
|
||||
document.removeEventListener('mouseup', mouseUpHandler);
|
||||
document.removeEventListener('mouseleave', mouseUpHandler);
|
||||
};
|
||||
|
||||
containerRef.current.addEventListener('mousedown', mouseDownHandler);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
defineDragScroll();
|
||||
}, [containerRef.current]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div ref={containerRef} id="KH_FE_FPLAN" className="p2">
|
||||
<div id="OFP" dangerouslySetInnerHTML={{ __html: content }} />
|
||||
</div>
|
||||
<Controls
|
||||
containerRef={containerRef}
|
||||
position={position}
|
||||
page={page}
|
||||
reload={reload}
|
||||
setPosition={setPosition}
|
||||
setPage={setPage}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default OFP;
|
||||
@@ -0,0 +1,83 @@
|
||||
import { createRef, Dispatch, FC, SetStateAction, useEffect } from 'react';
|
||||
import Controls from '../controls/controls';
|
||||
|
||||
interface TLRProps {
|
||||
content: string;
|
||||
position: number;
|
||||
page: number;
|
||||
reload: () => void;
|
||||
setPosition: Dispatch<SetStateAction<number>>;
|
||||
setPage: Dispatch<SetStateAction<number>>;
|
||||
}
|
||||
|
||||
const TLR: FC<TLRProps> = ({ content, position, page, reload, setPosition, setPage }) => {
|
||||
const containerRef = createRef<HTMLDivElement>();
|
||||
|
||||
const defineDragScroll = (horizontalScroll = true, verticalScroll = true) => {
|
||||
if (!containerRef.current) return;
|
||||
|
||||
let pos = { top: 0, left: 0, x: 0, y: 0 };
|
||||
|
||||
const mouseDownHandler = (e: MouseEvent) => {
|
||||
if (!containerRef.current) return;
|
||||
|
||||
pos = {
|
||||
left: containerRef.current.scrollLeft,
|
||||
top: containerRef.current.scrollTop,
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
};
|
||||
document.addEventListener('mousemove', mouseMoveHandler);
|
||||
document.addEventListener('mouseup', mouseUpHandler);
|
||||
document.addEventListener('mouseleave', mouseUpHandler);
|
||||
};
|
||||
|
||||
const mouseMoveHandler = (e: MouseEvent) => {
|
||||
if (!containerRef.current) return;
|
||||
|
||||
const dx = e.clientX - pos.x;
|
||||
const dy = e.clientY - pos.y;
|
||||
|
||||
if (verticalScroll) {
|
||||
containerRef.current.scrollTop = pos.top - dy;
|
||||
}
|
||||
if (horizontalScroll) {
|
||||
containerRef.current.scrollLeft = pos.left - dx;
|
||||
}
|
||||
};
|
||||
|
||||
const mouseUpHandler = (e: MouseEvent) => {
|
||||
document.removeEventListener('mousemove', mouseMoveHandler);
|
||||
document.removeEventListener('mouseup', mouseUpHandler);
|
||||
document.removeEventListener('mouseleave', mouseUpHandler);
|
||||
};
|
||||
|
||||
containerRef.current.addEventListener('mousedown', mouseDownHandler);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
defineDragScroll();
|
||||
}, [containerRef.current]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div ref={containerRef} id="KH_FE_FPLAN" className="p2">
|
||||
<div id="TLR">
|
||||
<div>
|
||||
<pre>{content}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Controls
|
||||
containerRef={containerRef}
|
||||
position={position}
|
||||
page={page}
|
||||
reload={reload}
|
||||
setPosition={setPosition}
|
||||
setPage={setPage}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TLR;
|
||||
@@ -0,0 +1,7 @@
|
||||
<link rel="stylesheet" href="index.css" />
|
||||
<script type="text/html" import-script="/Pages/VCockpit/Instruments/FSS_B727/KH_FE_FPLAN/index.js"></script>
|
||||
<script type="text/html" import-script="/JS/dataStorage.js"></script>
|
||||
|
||||
<script type="text/html" id="kh-fe-fplan">
|
||||
<div id="root"></div>
|
||||
</script>
|
||||
@@ -0,0 +1,68 @@
|
||||
@font-face {
|
||||
font-family: 'Consolas';
|
||||
src: url('./assets/fonts/Consolas.ttf') format('truetype');
|
||||
font-weight: 100;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: lightgray;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: gray;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: darkgray;
|
||||
}
|
||||
|
||||
#root {
|
||||
--buttonHoverColor: lightgray;
|
||||
--fss-select-hover: lightgray;
|
||||
/* No idea why, zero. I looked at the EFB.css and it has 100%, but doing so screws this over hard */
|
||||
width: 594px;
|
||||
height: 100%;
|
||||
background-image: url(../EFB/Images/bg.png);
|
||||
background-size: 100% 100%;
|
||||
color: #000;
|
||||
font-size: 25px;
|
||||
padding: 3vw;
|
||||
|
||||
#KH_FE_FPLAN {
|
||||
height: calc(100vh - 6vw - 180px);
|
||||
width: 100%;
|
||||
margin-top: 100px;
|
||||
margin-bottom: 3vw;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
|
||||
#TLR div,
|
||||
#OFP div {
|
||||
line-height: unset !important;
|
||||
font-size: unset !important;
|
||||
}
|
||||
|
||||
#TLR pre,
|
||||
#OFP pre {
|
||||
white-space: pre;
|
||||
line-height: 14px;
|
||||
font-size: 13px;
|
||||
font-family: 'Consolas' !important;
|
||||
}
|
||||
|
||||
#OFP img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.p2 {
|
||||
height: calc(100vh - 6vw - 240px);
|
||||
margin-top: 160px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/// <reference types="@microsoft/msfs-types/pages/vcockpit/core/vcockpit" />
|
||||
/// <reference types="@microsoft/msfs-types/pages/vcockpit/instruments/shared/baseinstrument" />
|
||||
/// <reference types="@microsoft/msfs-types/js/datastorage" />
|
||||
/// <reference types="@microsoft/msfs-types/js/simvar" />
|
||||
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import App from './App';
|
||||
import './index.scss';
|
||||
|
||||
class KH_FE_FPLAN extends BaseInstrument {
|
||||
get templateID(): string {
|
||||
return 'kh-fe-fplan';
|
||||
}
|
||||
|
||||
get isInteractive() {
|
||||
return true;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
protected Update(): void {
|
||||
super.Update();
|
||||
}
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
|
||||
//@ts-expect-error
|
||||
const url = new URL(this.getAttribute('Url') ?? '');
|
||||
const type = url.searchParams.get('type') ?? '';
|
||||
|
||||
const container = document.getElementById('root');
|
||||
if (container) {
|
||||
const root = createRoot(container);
|
||||
root.render(<App type={type} />);
|
||||
}
|
||||
|
||||
/*
|
||||
FSComponent.render(
|
||||
<>
|
||||
{type === 'ofp' && <OFP content={this.contentOFP} position={this.position} reload={this.reloadSB} />}
|
||||
{type === 'tlr' && <TLR content={this.contentTLR} position={this.position} reload={this.reloadSB} />}
|
||||
</>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
//@ts-expect-error
|
||||
registerInstrument('kh-fe-fplan', KH_FE_FPLAN);
|
||||
Reference in New Issue
Block a user