diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md
index 8a27636d95e..7907c50a344 100644
--- a/.github/CHANGELOG.md
+++ b/.github/CHANGELOG.md
@@ -23,6 +23,7 @@
1. [A380X/EWD] QoL: Add soft keys to EWD checklists, can be enabled via EFB - @flogross89 (floridude)
1. [A380X/LIGHTS] Implemented LOGO LT switch funcionality - @ImenesFBW (Imenes)
1. [A380X/SD] Added correct ECP ALL button SD page cycling to the A380X - @frankkopp (Frank Kopp)
+1. [A380X/SURV] Fixed BTV setup from F/O side, and ROW issues at G/A with early THR RED - @flogross89 (floridude)
1. [A380/MFD] Limit keyboard inputs to keys present in the KCCU and remap comma to decimal dot - @beheh (Benedict Etzel)
1. [GENERAL] Mitigated issue with pop-ups (i.e. pause on TOD notification) being unable to be dismissed while camera is in freelook + ALT-TAB - @2hwk (2cas)
1. [A380X/AP] Improved support of simulation rate 4x - @aguther (Andreas Guther)
@@ -31,6 +32,15 @@
1. [A380X/MFD] Fixed DIRECT TO selection of active waypoint does nothing - @sognodelx (Sven Gross)
1. [A380X/MODEL] Optimized 4K textures - @Repsol
1. [A380X/EFB] Moved cabin lighting from ambient light knob to EFB Quick Settings - @2hwk (2cas)
+1. [A380X/AFS] Fixed CP V/S knob push levelling off the aircraft when it should have no action - @tracernz (Mike)
+1. [A380X/MFD] MFD/SURV: Fixed TCAS not switching status when using DEFAULT SETTINGS button - @flogross89 (floridude)
+1. [A380/FUEL] Recalibrated inital fuel settings - @sschiphorst (Yahtzee94)
+1. [A380X/ENGINES] Another adjustment to taxi thrust - @donstim (donbikes)
+1. [A380/ANIM] Animation of flaps now from FPPU position. Interim fix for spoiler low end animation - @Crocket63 (crocket)
+1. [A380X/ENG] Improve oil pressure lookup table - @tracernz (Mike)
+1. [A380/WING_FLEX] Reduced stiffness of wings for more tip up bend - @Crocket63 (crocket)
+1. [A380X/FWS] Add V1 callout - @flogross89 (floridude)
+1. [FMS] Fix existing T-P moving when inserting temporary flight plan - @Benjozork (Benjamin Dupont)
1. [GPU] Improved handling of ground power for more immersive use @Maximilian-Reuter (\_chaoz_)
## 0.12.0
diff --git a/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/Airliners/FlyByWire_A320_Neo/FMC/A32NX_FMCMainDisplay.js b/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/Airliners/FlyByWire_A320_Neo/FMC/A32NX_FMCMainDisplay.js
index 432469cf1c8..06c528a647b 100644
--- a/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/Airliners/FlyByWire_A320_Neo/FMC/A32NX_FMCMainDisplay.js
+++ b/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/Airliners/FlyByWire_A320_Neo/FMC/A32NX_FMCMainDisplay.js
@@ -2555,11 +2555,16 @@ class FMCMainDisplay extends BaseAirliners {
insertTemporaryFlightPlan(callback = EmptyCallback.Void) {
if (this.flightPlanService.hasTemporary) {
const oldCostIndex = this.costIndex;
- const oldDestination = this.currFlightPlanService.active.destinationAirport.ident;
+ const oldDestination = this.currFlightPlanService.active.destinationAirport
+ ? this.currFlightPlanService.active.destinationAirport.ident
+ : undefined;
const oldCruiseLevel = this.cruiseLevel;
this.flightPlanService.temporaryInsert();
this.checkCostIndex(oldCostIndex);
- this.checkDestination(oldDestination);
+ // FIXME I don't know if it is actually possible to insert TMPY with no FROM/TO, but we should not crash here, so check this for now
+ if (oldDestination !== undefined) {
+ this.checkDestination(oldDestination);
+ }
this.checkCruiseLevel(oldCruiseLevel);
SimVar.SetSimVarValue("L:FMC_FLIGHT_PLAN_IS_TEMPORARY", "number", 0);
diff --git a/fbw-a32nx/src/systems/fmgc/src/flightplanning/FlightPlanService.ts b/fbw-a32nx/src/systems/fmgc/src/flightplanning/FlightPlanService.ts
index 3e573d101cb..aa950d59366 100644
--- a/fbw-a32nx/src/systems/fmgc/src/flightplanning/FlightPlanService.ts
+++ b/fbw-a32nx/src/systems/fmgc/src/flightplanning/FlightPlanService.ts
@@ -9,7 +9,7 @@ import { FlightPlanLeg, FlightPlanLegFlags } from '@fmgc/flightplanning/legs/Fli
import { Fix, NXDataStore, Waypoint } from '@flybywiresim/fbw-sdk';
import { NavigationDatabase } from '@fmgc/NavigationDatabase';
import { Coordinates, Degrees } from 'msfs-geo';
-import { EventBus } from '@microsoft/msfs-sdk';
+import { BitFlags, EventBus } from '@microsoft/msfs-sdk';
import { FixInfoEntry } from '@fmgc/flightplanning/plans/FixInfo';
import { HoldData } from '@fmgc/flightplanning/data/flightplan';
import { FlightPlanLegDefinition } from '@fmgc/flightplanning/legs/FlightPlanLegDefinition';
@@ -121,13 +121,36 @@ export class FlightPlanService
{
+ if (!this.ignoreSync) {
+ if (event.planIndex !== this.index || isAlternatePlan !== event.forAlternate) {
+ return;
+ }
+
+ const element = this.legElementAt(event.atIndex);
+
+ element.flags = event.newFlags;
+
+ this.incrementVersion();
+
+ flightPlanEventsPub.pub('flightPlan.legFlagsEdit', event);
+ }
+ }),
+ );
+
this.subscriptions.push(
subs.on('SYNC_flightPlan.legDefinitionEdit').handle((event) => {
if (!this.ignoreSync) {
@@ -452,6 +470,19 @@ export abstract class BaseFlightPlan
, notify = true): void {
const leg = this.legElementAt(index);
@@ -2295,6 +2336,7 @@ export abstract class BaseFlightPlan
IF if no element, or discontinuity before, or 0th leg
if (element && element.isDiscontinuity === false && element.type !== LegType.IF) {
- if (!prevElement || (prevElement && prevElement.isDiscontinuity === true) || i === 0) {
+ // T-P legs need to always be CF so they can create a direct-to-fix transition outbound of them
+ const isLegTurningPoint = BitFlags.isAny(element.flags, FlightPlanLegFlags.DirectToTurningPoint);
+
+ // TODO sync
+ if (!isLegTurningPoint && (!prevElement || (prevElement && prevElement.isDiscontinuity === true) || i === 0)) {
element.type = LegType.IF;
}
}
diff --git a/fbw-a32nx/src/systems/fmgc/src/flightplanning/plans/FlightPlan.ts b/fbw-a32nx/src/systems/fmgc/src/flightplanning/plans/FlightPlan.ts
index a4bd7ac8cc1..25b49d99231 100644
--- a/fbw-a32nx/src/systems/fmgc/src/flightplanning/plans/FlightPlan.ts
+++ b/fbw-a32nx/src/systems/fmgc/src/flightplanning/plans/FlightPlan.ts
@@ -19,6 +19,7 @@ import {
FlightPlanPerformanceDataProperties,
} from '@fmgc/flightplanning/plans/performance/FlightPlanPerformanceData';
import { BaseFlightPlan, FlightPlanQueuedOperation, SerializedFlightPlan } from './BaseFlightPlan';
+import { FlightPlanIndex } from '@fmgc/flightplanning/FlightPlanManager';
export class FlightPlan
extends BaseFlightPlan
{
static empty
(
@@ -148,6 +149,9 @@ export class FlightPlan
{
if (leg.isDiscontinuity === false) {
leg.flags &= ~FlightPlanLegFlags.Origin;
diff --git a/fbw-a32nx/src/systems/fmgc/src/flightplanning/sync/FlightPlanEvents.ts b/fbw-a32nx/src/systems/fmgc/src/flightplanning/sync/FlightPlanEvents.ts
index 4f387e6c754..8036b042b7a 100644
--- a/fbw-a32nx/src/systems/fmgc/src/flightplanning/sync/FlightPlanEvents.ts
+++ b/fbw-a32nx/src/systems/fmgc/src/flightplanning/sync/FlightPlanEvents.ts
@@ -39,6 +39,11 @@ export interface FlightPlanSetSegmentLegsEvent extends FlightPlanEditSyncEvent {
legs: (SerializedFlightPlanLeg | Discontinuity)[];
}
+export interface FlightPlanLegFlagsEditEvent extends FlightPlanEditSyncEvent {
+ atIndex: number;
+ newFlags: number;
+}
+
export interface FlightPlanLegDefinitionEditEvent extends FlightPlanEditSyncEvent {
atIndex: number;
newDefinition: FlightPlanLegDefinition;
@@ -81,6 +86,7 @@ export interface FlightPlanEvents {
'flightPlan.setActiveLegIndex': FlightPlanSetActiveLegIndexEvent;
'flightPlan.setSegmentLegs': FlightPlanSetSegmentLegsEvent;
+ 'flightPlan.legFlagsEdit': FlightPlanLegFlagsEditEvent;
'flightPlan.legDefinitionEdit': FlightPlanLegDefinitionEditEvent;
'flightPlan.setLegCruiseStep': FlightPlanLegCruiseStepEditEvent;
'flightPlan.setFixInfoEntry': FlightPlanSetFixInfoEntryEvent;
diff --git a/fbw-a380x/mach.config.js b/fbw-a380x/mach.config.js
index 0f53acc371e..1a751a10370 100644
--- a/fbw-a380x/mach.config.js
+++ b/fbw-a380x/mach.config.js
@@ -28,6 +28,7 @@ module.exports = {
typecheckingPlugin(),
],
instruments: [
+ msfsAvionicsInstrument('AtcMailbox'),
msfsAvionicsInstrument('Clock'),
msfsAvionicsInstrument('EWD'),
msfsAvionicsInstrument('FCU', 'FcuBaseInstrument.ts'),
diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/engines.cfg b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/engines.cfg
index 4448d42e8d2..ad921691431 100644
--- a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/engines.cfg
+++ b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/engines.cfg
@@ -45,7 +45,7 @@ AfterBurnThrustSpecificFuelConsumption = 0.0
mach_0_corrected_commanded_ne_table = 0:1.000000:1.201723:1.453784:2.175007:3.364756:4.472461:5.415178,0.000000:60.000000:61.058056:62.174137:64.603148:67.341070:69.189046:69.189046,0.100000:66.862170:68.041236:69.284962:71.991777:75.042834:77.102162:77.102162,0.200000:73.020528:74.308192:75.666471:78.622599:81.954674:84.203677:84.203677,0.400000:81.642229:83.081930:84.600585:87.905749:91.631250:94.145798:94.145798,0.600000:86.217009:87.737383:89.341135:92.831502:96.765760:99.421209:99.421209,0.750000:89.296188:90.870861:92.531889:96.146913:100.221680:102.971967:102.971967,0.900000:95.014663:96.690177:106.000000:102.304105:106.639817:109.566230:109.566230,1.000000:100.000000:101.763427:103.623561:107.671913:112.235116:115.315076:115.315076
mach_hi_corrected_commanded_ne_table = 0.9:1.000000:1.201723:1.453784:2.175007:3.364756:4.472461:5.415178,0.000000:55.660639:56.642174:57.677536:59.930874:62.470783:64.185108:64.185108,0.100000:62.026518:63.120311:64.274087:66.785139:69.615535:71.525927:71.525927,0.200000:67.739487:68.934024:70.194069:72.936401:76.027492:78.113841:78.113841,0.400000:75.737643:77.073222:78.482043:81.548169:85.004232:87.336921:87.336921,0.600000:79.981563:81.391980:82.879744:86.117679:89.767400:92.230800:92.230800,0.750000:82.838047:84.298836:85.839735:89.193310:92.973379:95.524757:95.524757,0.900000:88.142947:89.697284:98.333795:94.905197:98.927339:101.642106:101.642106,1.000000:92.767731:94.403623:96.129227:99.884791:104.117971:106.975180:106.975180
corrected_n2_from_ff_table = 0.000184:20.234723,0.017030:60.000352,0.018464:65.542906,0.108271:69.501874,0.141371:73.460841,0.168471:76.540038,0.190658:79.179350,0.208824:81.818662,0.213632:87.977056,0.269273:100.000000
-n1_and_mach_on_thrust_table=0:0:0.1:0.2:0.3:0.4:0.5:0.6:0.7:0.8:0.9,0:0:0:0:0:0:0:0:0:0:0,20:0.065:0.008:0.015:0.0337152:-0.0207408:-0.0447408:-0.0684924:-0.2470092:-0.3784788:-0.5864604,25:0.061:0.0536:0.018:0.0364848:-0.0115116:-0.0318852:-0.0503988:-0.1815936:-0.3194448:-0.5268336,30:0.16:0.16:0.020:0.0392976:0.0208668:0.004788:-0.0323052:-0.0621768:-0.0983352:-0.4432692,35:0.3146484:0.1512564:0.0926472:0.0551052:0.0296628:0.0072744:-0.0033744:-0.02736:-0.0530976:-0.3523572,40:0.396276:0.1953084:0.1489056:0.0834948:0.069486:0.0595452:0.035748:-0.0025668:-0.0309732:-0.264756,45:0.4719516:0.3001152:0.1880484:0.1349028:0.1097016:0.0921084:0.067308:0.0222108:-0.00885:-0.186144,50:0.5428044:0.3732792:0.2536236:0.1898088:0.1529148:0.125898:0.0974052:0.0569028:0.0132744:-0.1181688,55:0.6113616:0.4482816:0.3239532:0.2509272:0.20238:0.1646676:0.1300596:0.090792:0.0344448:-0.0593628,60:0.7135368:0.527946:0.4015548:0.3209724:0.2613276:0.2122788:0.1696848:0.1285776:0.0774672:-0.0060432,65:0.792042:0.6151248:0.4885812:0.402066:0.3323136:0.2720028:0.2203524:0.17502:0.1253292:0.0468144,70:0.8803212:0.7122072:0.5862852:0.4951476:0.4165956:0.345852:0.2850708:0.2341704:0.182982:0.1047228,75:0.9824316:0.820656:0.6945072:0.5994168:0.5135268:0.4339248:0.3650892:0.3086364:0.254406:0.1728504,80:1.0924128:0.940554:0.8111784:0.7117992:0.6199728:0.72061164:0.61995618:0.53846208:0.46148454:0.34453998,85:1.230198:1.0701876:0.9318576:0.8264304:0.7297536:0.8638002:0.76034862:0.6782778:0.6008094:0.47774934,90:1.3884588:1.205634:1.04928:0.9341592:0.8331012:1.00279782:0.90328122:0.82806786:0.75678138:0.63212886,95:1.5375996:1.3403916:1.1529288:1.0220796:0.916146:1.1192418:1.03216032:0.97414164:0.91949256:0.80217054,100:1.629522:1.4650128:1.2286368:1.0730808:0.9604224:1.18825056:1.12376808:1.06059942:1.00110006:0.8733663,105:1.6545912:1.4875512:1.2581976:1.0989:0.9835308:1.21684194:1.1508075:1.08611928:1.02518784:0.89437932,110:1.6713048:1.5025776:1.2831864:1.1207244:1.0030632:1.24100748:1.17366246:1.10768958:1.045548:0.91214262
+n1_and_mach_on_thrust_table=0:0:0.1:0.2:0.3:0.4:0.5:0.6:0.7:0.8:0.9,0:0.015:-0.15:0:0:0:0:0:0:0:0,20:0.1286:-0.008:0.015:0.0337152:-0.0207408:-0.0447408:-0.0684924:-0.2470092:-0.3784788:-0.5864604,25:0.155:0.0236:0.018:0.0364848:-0.0115116:-0.0318852:-0.0503988:-0.1815936:-0.3194448:-0.5268336,30:0.16:0.16:0.020:0.0392976:0.0208668:0.004788:-0.0323052:-0.0621768:-0.0983352:-0.4432692,35:0.3146484:0.1512564:0.0926472:0.0551052:0.0296628:0.0072744:-0.0033744:-0.02736:-0.0530976:-0.3523572,40:0.396276:0.1953084:0.1489056:0.0834948:0.069486:0.0595452:0.035748:-0.0025668:-0.0309732:-0.264756,45:0.4719516:0.3001152:0.1880484:0.1349028:0.1097016:0.0921084:0.067308:0.0222108:-0.00885:-0.186144,50:0.5428044:0.3732792:0.2536236:0.1898088:0.1529148:0.125898:0.0974052:0.0569028:0.0132744:-0.1181688,55:0.6113616:0.4482816:0.3239532:0.2509272:0.20238:0.1646676:0.1300596:0.090792:0.0344448:-0.0593628,60:0.7135368:0.527946:0.4015548:0.3209724:0.2613276:0.2122788:0.1696848:0.1285776:0.0774672:-0.0060432,65:0.792042:0.6151248:0.4885812:0.402066:0.3323136:0.2720028:0.2203524:0.17502:0.1253292:0.0468144,70:0.8803212:0.7122072:0.5862852:0.4951476:0.4165956:0.345852:0.2850708:0.2341704:0.182982:0.1047228,75:0.9824316:0.820656:0.6945072:0.5994168:0.5135268:0.4339248:0.3650892:0.3086364:0.254406:0.1728504,80:1.0924128:0.940554:0.8111784:0.7117992:0.6199728:0.72061164:0.61995618:0.53846208:0.46148454:0.34453998,85:1.230198:1.0701876:0.9318576:0.8264304:0.7297536:0.8638002:0.76034862:0.6782778:0.6008094:0.47774934,90:1.3884588:1.205634:1.04928:0.9341592:0.8331012:1.00279782:0.90328122:0.82806786:0.75678138:0.63212886,95:1.5375996:1.3403916:1.1529288:1.0220796:0.916146:1.1192418:1.03216032:0.97414164:0.91949256:0.80217054,100:1.629522:1.4650128:1.2286368:1.0730808:0.9604224:1.18825056:1.12376808:1.06059942:1.00110006:0.8733663,105:1.6545912:1.4875512:1.2581976:1.0989:0.9835308:1.21684194:1.1508075:1.08611928:1.02518784:0.89437932,110:1.6713048:1.5025776:1.2831864:1.1207244:1.0030632:1.24100748:1.17366246:1.10768958:1.045548:0.91214262
n2_to_n1_table = 0:0:0.2:0.9,16.01173:0:0:17,19.354839:1.62525110132159:1.62525110132159:17.34466,22.8739:2.13848810572687:2.13848810572687:18.127184,50.146628:10.9490607929515:10.9490607929515:26.627184,60:16.2995594713656:16.2995594713656:33.727774,67.741935:22.2402801762115:22.2402801762115:40.08227,73.020528:26.8767286343612:26.8767286343612:43.853613,78.29912:35.0472960352423:35.0472960352423:48.89878,81.642229:43.6251656387665:43.6251656387665:53.556563,85.337243:63.106796:63.106796:63.106796,87.97654:74.757282:74.757282:74.757282,97.8:97.2:97.2:97.2,118:115.346535:115.346535:115.346535
corrected_airflow_table = 0.000000:0.000000:0.100000:0.200000:0.300000:0.400000:0.500000:0.600000:0.700000:0.800000:0.900000,0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,20.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,25.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,30.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,35.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,40.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,45.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,50.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,55.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,60.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,65.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,70.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,75.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,80.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,85.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,90.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,95.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,100.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,105.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000,110.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000:0.000000
epr_max = 1.4
@@ -55,9 +55,10 @@ oil_temp_cooling_constant = 0.21
oil_temp_heating_constant = 700
oil_temp_tc = 0.03
oil_temp_tuning_constant = 1
-oil_press_max = 60480
+oil_press_max = 21456 ; ~149 psi
oil_press_tc = 0.8
oil_press_tuning_constant = 1
+n1_to_oil_pressure_table = 0:0,0.01:0,0.1095:0.403,0.269:0.604,1.154:1 ; % of max Oil Pressure for % N1.
itt_peak_temperature = 2250
itt_tc = 2
itt_tuning_constant = 1
@@ -83,7 +84,6 @@ min_n2_for_starter_cutoff = 200 ; % - Minimum N2 at which the starter is automat
min_n2_for_apu_bleed_air_cutoff = 200 ; % - Minimum N2 at which the APU Bleed Air is automatically disabled
n2_from_bleed_air_psi_table = 0:0, 4:8, 10:18, 18:25 ; Table which contains the n2 generated depending on the pression from the bleed air from the APU (in psi).
bleed_air_on_n2_tc = 1 ; Time constant which contains the speed at which the bleed air pressure affect the N2 of the turbine.
-N1_to_oil_pressure_table = 0:0, 0.21:0.71, 0.9:0.989, 1:1 ; % of max Oil Pressure for % N1.
;fuel_flow_max_itt_factor = 2.0
;fuel_flow_min_itt_factor = 0.9
n1_cooling_factor = 0.01
diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/model/A380_EXTERIOR.xml b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/model/A380_EXTERIOR.xml
index d829d54c9be..199ee21448c 100755
--- a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/model/A380_EXTERIOR.xml
+++ b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/model/A380_EXTERIOR.xml
@@ -309,49 +309,49 @@
l_spoiler1_key
- (L:A32NX_HYD_SPOILER_1_LEFT_DEFLECTION, number)
+ (L:A32NX_HYD_SPOILER_1_LEFT_DEFLECTION_UNSMOOTH, number)
l_spoiler2_key
- (L:A32NX_HYD_SPOILER_2_LEFT_DEFLECTION, number)
+ (L:A32NX_HYD_SPOILER_2_LEFT_DEFLECTION_UNSMOOTH, number)
l_spoiler3_key
- (L:A32NX_HYD_SPOILER_3_LEFT_DEFLECTION, number)
+ (L:A32NX_HYD_SPOILER_3_LEFT_DEFLECTION_UNSMOOTH, number)
l_spoiler4_key
- (L:A32NX_HYD_SPOILER_4_LEFT_DEFLECTION, number)
+ (L:A32NX_HYD_SPOILER_4_LEFT_DEFLECTION_UNSMOOTH, number)
l_spoiler5_key
- (L:A32NX_HYD_SPOILER_5_LEFT_DEFLECTION, number)
+ (L:A32NX_HYD_SPOILER_5_LEFT_DEFLECTION_UNSMOOTH, number)
l_spoiler6_key
- (L:A32NX_HYD_SPOILER_6_LEFT_DEFLECTION, number)
+ (L:A32NX_HYD_SPOILER_6_LEFT_DEFLECTION_UNSMOOTH, number)
l_spoiler7_key
- (L:A32NX_HYD_SPOILER_7_LEFT_DEFLECTION, number)
+ (L:A32NX_HYD_SPOILER_7_LEFT_DEFLECTION_UNSMOOTH, number)
l_spoiler8_key
- (L:A32NX_HYD_SPOILER_8_LEFT_DEFLECTION, number)
+ (L:A32NX_HYD_SPOILER_8_LEFT_DEFLECTION_UNSMOOTH, number)
@@ -360,49 +360,49 @@
r_spoiler1_key
- (L:A32NX_HYD_SPOILER_1_RIGHT_DEFLECTION, number)
+ (L:A32NX_HYD_SPOILER_1_RIGHT_DEFLECTION_UNSMOOTH, number)
r_spoiler2_key
- (L:A32NX_HYD_SPOILER_2_RIGHT_DEFLECTION, number)
+ (L:A32NX_HYD_SPOILER_2_RIGHT_DEFLECTION_UNSMOOTH, number)
r_spoiler3_key
- (L:A32NX_HYD_SPOILER_3_RIGHT_DEFLECTION, number)
+ (L:A32NX_HYD_SPOILER_3_RIGHT_DEFLECTION_UNSMOOTH, number)
r_spoiler4_key
- (L:A32NX_HYD_SPOILER_4_RIGHT_DEFLECTION, number)
+ (L:A32NX_HYD_SPOILER_4_RIGHT_DEFLECTION_UNSMOOTH, number)
r_spoiler5_key
- (L:A32NX_HYD_SPOILER_5_RIGHT_DEFLECTION, number)
+ (L:A32NX_HYD_SPOILER_5_RIGHT_DEFLECTION_UNSMOOTH, number)
r_spoiler6_key
- (L:A32NX_HYD_SPOILER_6_RIGHT_DEFLECTION, number)
+ (L:A32NX_HYD_SPOILER_6_RIGHT_DEFLECTION_UNSMOOTH, number)
r_spoiler7_key
- (L:A32NX_HYD_SPOILER_7_RIGHT_DEFLECTION, number)
+ (L:A32NX_HYD_SPOILER_7_RIGHT_DEFLECTION_UNSMOOTH, number)
r_spoiler8_key
- (L:A32NX_HYD_SPOILER_8_RIGHT_DEFLECTION, number)
+ (L:A32NX_HYD_SPOILER_8_RIGHT_DEFLECTION_UNSMOOTH, number)
@@ -449,26 +449,17 @@
-
- l_flap_percent_key
- r_flap_percent_key
-
- CAM_FLAPS01
- 0.01
- Both
- CAM_FLAPS02
- 0.15
- Both
- CAM_FLAPS03
- 0.29
- Both
- CAM_FLAPS04
- 0.43
- Both
- CAM_FLAPS05
- 0.58
- Both
+
+ l_flap_percent_key
+ (L:A32NX_LEFT_FLAPS_ANIMATION_POSITION)
+ 100
+
+
+ r_flap_percent_key
+ (L:A32NX_RIGHT_FLAPS_ANIMATION_POSITION)
+ 100
+
diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/model/behaviour/legacy/generated/A32NX_Interior_FCU.xml b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/model/behaviour/legacy/generated/A32NX_Interior_FCU.xml
index d964817466c..f570d6abe16 100644
--- a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/model/behaviour/legacy/generated/A32NX_Interior_FCU.xml
+++ b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/model/behaviour/legacy/generated/A32NX_Interior_FCU.xml
@@ -238,20 +238,11 @@
-
+
1
-
-
-
- FBW_Airbus_Autopilot_Knob_VerticalSpeed_SubTemplate
-
-
-
-
-
- AUTOPILOT_Knob_VerticalSpeed#SUFFIX_ID#
- AUTOPILOT_Knob_VerticalSpeed#SUFFIX_ID#
- AUTOPILOT_Knob_VerticalSpeed_PushPull#SUFFIX_ID#
+ AUTOPILOT_Knob_VerticalSpeed
+ AUTOPILOT_Knob_VerticalSpeed
+ AUTOPILOT_Knob_VerticalSpeed_PushPull
AUTOPILOT_Knob_VerticalSpeed
QNHknob
36
@@ -265,13 +256,7 @@
TurnRight
%((L:A32NX_TRK_FPA_MODE_ACTIVE, bool))%{if}Engage flight path angle%{else}Engage vertical speed%{end}
DownArrow
-
-
-
-
-
-
-
+
@@ -295,10 +280,7 @@
(>H:A320_Neo_FCU_VS_PULL)
(>H:A320_Neo_CDU_VS)
-
- (>H:A320_Neo_FCU_VS_PUSH)
- (>H:A320_Neo_CDU_VS)
-
+
0
QNHknob
QNHknob
diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.cfg b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.cfg
index bf497acb6f7..60212725b7b 100644
--- a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.cfg
+++ b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.cfg
@@ -46,6 +46,7 @@ pixel_size=768,1024
texture=$SCREEN_DU_SD
htmlgauge00=A380X/SD/sd.html?duID=7, 0,0,768,1024
+htmlgauge01=A380X/AtcMailbox/atcmailbox.html?duID=7, 0,0,768,1024
[VCockpit05]
size_mm=768,1024
diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/sound/sound.xml b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/sound/sound.xml
index a44ab12a513..97a7214fa9c 100644
--- a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/sound/sound.xml
+++ b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/sound/sound.xml
@@ -1177,6 +1177,14 @@
+
+
+
+
+
+
+
+
@@ -1216,7 +1224,6 @@
-
diff --git a/fbw-a380x/src/systems/instruments/src/AtcMailbox/.eslintrc.js b/fbw-a380x/src/systems/instruments/src/AtcMailbox/.eslintrc.js
new file mode 100644
index 00000000000..5001200a0c2
--- /dev/null
+++ b/fbw-a380x/src/systems/instruments/src/AtcMailbox/.eslintrc.js
@@ -0,0 +1,15 @@
+'use strict';
+
+module.exports = {
+ extends: ['../../../../../../.eslintrc.js', 'plugin:jsdoc/recommended-typescript-error'],
+
+ plugins: ['eslint-plugin-jsdoc'],
+
+ // overrides airbnb, use sparingly
+ rules: {
+ 'react/no-unknown-property': 'off',
+ 'react/style-prop-object': 'off',
+ 'arrow-body-style': 'off',
+ camelcase: 'off',
+ },
+};
diff --git a/fbw-a380x/src/systems/instruments/src/AtcMailbox/AtcMailbox.tsx b/fbw-a380x/src/systems/instruments/src/AtcMailbox/AtcMailbox.tsx
new file mode 100644
index 00000000000..01f0a8b12e2
--- /dev/null
+++ b/fbw-a380x/src/systems/instruments/src/AtcMailbox/AtcMailbox.tsx
@@ -0,0 +1,61 @@
+// Copyright (c) 2024 FlyByWire Simulations
+// SPDX-License-Identifier: GPL-3.0
+
+import { DisplayComponent, EventBus, FSComponent, Subject, VNode } from '@microsoft/msfs-sdk';
+import { Button } from 'instruments/src/MFD/pages/common/Button';
+import { MouseCursor } from 'instruments/src/MFD/pages/common/MouseCursor';
+import { CdsDisplayUnit, DisplayUnitID } from '../MsfsAvionicsCommon/CdsDisplayUnit';
+
+import './style.scss';
+
+export interface AtcMailboxProps {
+ readonly bus: EventBus;
+}
+
+export class AtcMailbox extends DisplayComponent {
+ private readonly topRef = FSComponent.createRef();
+
+ private readonly mouseCursorRef = FSComponent.createRef();
+
+ private onMouseMove(ev: MouseEvent) {
+ this.mouseCursorRef.getOrDefault()?.updatePosition(ev.clientX, ev.clientY - 768);
+ }
+
+ private onMouseMoveHandler = this.onMouseMove.bind(this);
+
+ public onAfterRender(node: VNode): void {
+ super.onAfterRender(node);
+
+ this.topRef.instance.addEventListener('mousemove', this.onMouseMoveHandler);
+ }
+
+ destroy(): void {
+ this.topRef.getOrDefault()?.removeEventListener('mousemove', this.onMouseMoveHandler);
+
+ super.destroy();
+ }
+
+ render(): VNode | null {
+ return (
+
+
+
+ {}} buttonStyle="height: 50px;">
+
+
+
+ {}} buttonStyle="height: 50px; justify-content: flex-end;">
+ {}} buttonStyle="height: 50px; justify-content: flex-end;">
+
+
+
+
+ );
+ }
+}
diff --git a/fbw-a380x/src/systems/instruments/src/AtcMailbox/config.json b/fbw-a380x/src/systems/instruments/src/AtcMailbox/config.json
new file mode 100644
index 00000000000..16331a90ebb
--- /dev/null
+++ b/fbw-a380x/src/systems/instruments/src/AtcMailbox/config.json
@@ -0,0 +1,5 @@
+{
+ "index": "./instrument.tsx",
+ "name": "AtcMailbox",
+ "isInteractive": true
+}
diff --git a/fbw-a380x/src/systems/instruments/src/AtcMailbox/instrument.tsx b/fbw-a380x/src/systems/instruments/src/AtcMailbox/instrument.tsx
new file mode 100644
index 00000000000..21404330143
--- /dev/null
+++ b/fbw-a380x/src/systems/instruments/src/AtcMailbox/instrument.tsx
@@ -0,0 +1,105 @@
+import {
+ FSComponent,
+ EventBus,
+ HEventPublisher,
+ InstrumentBackplane,
+ FsInstrument,
+ FsBaseInstrument,
+ ClockPublisher,
+} from '@microsoft/msfs-sdk';
+import { FailuresConsumer } from '@flybywiresim/fbw-sdk';
+import { AtcMailbox } from 'instruments/src/AtcMailbox/AtcMailbox';
+
+class AtcMailboxInstrument implements FsInstrument {
+ private readonly bus = new EventBus();
+
+ private readonly backplane = new InstrumentBackplane();
+
+ private readonly clockPublisher = new ClockPublisher(this.bus);
+
+ private readonly hEventPublisher = new HEventPublisher(this.bus);
+
+ private readonly failuresConsumer = new FailuresConsumer('A32NX');
+
+ constructor(public readonly instrument: BaseInstrument) {
+ this.hEventPublisher = new HEventPublisher(this.bus);
+
+ this.backplane.addPublisher('hEvent', this.hEventPublisher);
+ this.backplane.addPublisher('clock', this.clockPublisher);
+
+ this.doInit();
+ }
+
+ public doInit(): void {
+ this.backplane.init();
+
+ const atcMailbox = document.getElementById('AtcMailbox_CONTENT');
+
+ FSComponent.render( , document.getElementById('AtcMailbox_CONTENT'));
+
+ // Remove "instrument didn't load" text
+ atcMailbox?.querySelector(':scope > h1')?.remove();
+ }
+
+ /**
+ * A callback called when the instrument gets a frame update.
+ */
+ public Update(): void {
+ this.backplane.onUpdate();
+ this.failuresConsumer.update();
+ }
+
+ public onInteractionEvent(args: string[]): void {
+ this.hEventPublisher.dispatchHEvent(args[0]);
+ }
+
+ public onGameStateChanged(_oldState: GameState, _newState: GameState): void {
+ // noop
+ }
+
+ public onFlightStart(): void {
+ // noop
+ }
+
+ public onSoundEnd(_soundEventId: Name_Z): void {
+ // noop
+ }
+
+ public onPowerOn(): void {
+ // noop
+ }
+
+ public onPowerOff(): void {
+ // noop
+ }
+}
+
+class A380X_AtcMailbox extends FsBaseInstrument {
+ public constructInstrument(): AtcMailboxInstrument {
+ return new AtcMailboxInstrument(this);
+ }
+
+ public get isInteractive(): boolean {
+ return true;
+ }
+
+ public get templateID(): string {
+ return 'A380X_AtcMailbox';
+ }
+
+ /** @inheritdoc */
+ public onPowerOn(): void {
+ super.onPowerOn();
+
+ this.fsInstrument.onPowerOn();
+ }
+
+ /** @inheritdoc */
+ public onShutDown(): void {
+ super.onShutDown();
+
+ this.fsInstrument.onPowerOff();
+ }
+}
+
+registerInstrument('a380x-atc-mailbox', A380X_AtcMailbox);
diff --git a/fbw-a380x/src/systems/instruments/src/AtcMailbox/style.scss b/fbw-a380x/src/systems/instruments/src/AtcMailbox/style.scss
new file mode 100644
index 00000000000..d2d156b4c68
--- /dev/null
+++ b/fbw-a380x/src/systems/instruments/src/AtcMailbox/style.scss
@@ -0,0 +1,54 @@
+// Copyright (c) 2024 FlyByWire Simulations
+// SPDX-License-Identifier: GPL-3.0
+
+@import "../MsfsAvionicsCommon/definitions.scss";
+
+.atc-mailbox-top-layout {
+ display: grid;
+ grid-template-columns: 13% auto 18%;
+ height: 256px;
+ width: 768px;
+ position: absolute;
+ top: 768px;
+}
+
+.atc-mailbox-left-layout {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-end;
+ border-right: 2px solid $display-white;
+}
+
+.atc-mailbox-center-layout {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-end;
+}
+
+.atc-mailbox-right-layout {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ border-left: 2px solid $display-white;
+}
+
+.atc-mailbox-center-bottom {
+ display: flex;
+ flex: 1;
+ flex-direction: row;
+}
+
+.atc-mailbox-center-top {
+ display: flex;
+ flex: 8;
+ border-bottom: 2px solid white;
+}
+
+.atc-mailbox-cb-1 {
+ flex: 1;
+ border-right: 2px solid white;
+}
+
+.atc-mailbox-cb-2 {
+ flex: 1;
+}
diff --git a/fbw-a380x/src/systems/instruments/src/AtcMailbox/tsconfig.json b/fbw-a380x/src/systems/instruments/src/AtcMailbox/tsconfig.json
new file mode 100644
index 00000000000..b051de3a024
--- /dev/null
+++ b/fbw-a380x/src/systems/instruments/src/AtcMailbox/tsconfig.json
@@ -0,0 +1,33 @@
+{
+ "extends": "../../../tsconfig.json",
+
+ "compilerOptions": {
+ "incremental": false /* Enables incremental builds */,
+ "target": "es2017" /* Specifies the ES2017 target, compatible with Coherent GT */,
+ "module": "es2015" /* Ensures that modules are at least es2015 */,
+ "strict": true /* Enables strict type checking, highly recommended but optional */,
+ "esModuleInterop": true /* Emits additional JS to work with CommonJS modules */,
+ "skipLibCheck": true /* Skip type checking on library .d.ts files */,
+ "forceConsistentCasingInFileNames": true /* Ensures correct import casing */,
+ "moduleResolution": "node" /* Enables compatibility with MSFS SDK bare global imports */,
+ "jsxFactory": "FSComponent.buildComponent" /* Required for FSComponent framework JSX */,
+ "jsxFragmentFactory": "FSComponent.Fragment" /* Required for FSComponent framework JSX */,
+ "jsx": "react", /* Required for FSComponent framework JSX */
+ "paths": {
+ "@datalink/aoc": ["../../../fbw-common/src/systems/datalink/aoc/src/index.ts"],
+ "@datalink/atc": ["../../../fbw-common/src/systems/datalink/atc/src/index.ts"],
+ "@datalink/common": ["../../../fbw-common/src/systems/datalink/common/src/index.ts"],
+ "@datalink/router": ["../../../fbw-common/src/systems/datalink/router/src/index.ts"],
+ "@failures": ["./failures/src/index.ts"],
+ "@fmgc/*": ["./fmgc/src/*"],
+ "@instruments/common/*": ["./instruments/src/Common/*"],
+ "@localization/*": ["../localization/*"],
+ "@sentry/*": ["./sentry-client/src/*"],
+ "@simbridge/*": ["./simbridge-client/src/*"],
+ "@shared/*": ["./shared/src/*"],
+ "@tcas/*": ["./tcas/src/*"],
+ "@typings/*": ["../../../fbw-common/src/typings/*"],
+ "@flybywiresim/fbw-sdk": ["../../../fbw-common/src/systems/index-no-react.ts"],
+ }
+ }
+}
diff --git a/fbw-a380x/src/systems/instruments/src/Common/LegacyCdsDisplayUnit.tsx b/fbw-a380x/src/systems/instruments/src/Common/LegacyCdsDisplayUnit.tsx
index 015199ccdfd..93f210bbed8 100644
--- a/fbw-a380x/src/systems/instruments/src/Common/LegacyCdsDisplayUnit.tsx
+++ b/fbw-a380x/src/systems/instruments/src/Common/LegacyCdsDisplayUnit.tsx
@@ -45,6 +45,7 @@ const DisplayUnitToPotentiometer: { [k in DisplayUnitID]: number } = {
interface DisplayUnitProps {
displayUnitId: DisplayUnitID;
failed?: boolean;
+ hideBootTestScreens?: boolean;
}
enum DisplayUnitState {
@@ -63,7 +64,7 @@ function BacklightBleed(props) {
}
export const LegacyCdsDisplayUnit = forwardRef>(
- ({ displayUnitId, failed, children }, ref) => {
+ ({ displayUnitId, failed, hideBootTestScreens, children }, ref) => {
const [coldDark] = useSimVar('L:A32NX_COLD_AND_DARK_SPAWN' /* TODO 380 simvar */, 'Bool', 200);
const [state, setState] = useState(coldDark ? DisplayUnitState.Off : DisplayUnitState.Standby);
const [timer, setTimer] = useState(null);
@@ -167,7 +168,7 @@ export const LegacyCdsDisplayUnit = forwardRef
@@ -181,7 +182,7 @@ export const LegacyCdsDisplayUnit = forwardRef
diff --git a/fbw-a380x/src/systems/instruments/src/FCU/Managers/VerticalSpeedManager.ts b/fbw-a380x/src/systems/instruments/src/FCU/Managers/VerticalSpeedManager.ts
index 3a1545337f1..b371fcc44a6 100644
--- a/fbw-a380x/src/systems/instruments/src/FCU/Managers/VerticalSpeedManager.ts
+++ b/fbw-a380x/src/systems/instruments/src/FCU/Managers/VerticalSpeedManager.ts
@@ -60,22 +60,6 @@ export class VerticalSpeedManager extends TemporaryHax implements Instrument {
this.refresh(false, false, 0, 0, true);
}
- private onPush(): void {
- const mode = SimVar.GetSimVarValue('L:A32NX_FMA_VERTICAL_MODE', 'Number');
- if (mode >= 32 && mode <= 34) {
- return;
- }
- clearTimeout(this._resetSelectionTimeout);
- this.forceUpdate = true;
-
- this.currentState = A320_Neo_FCU_VSpeed_State.Zeroing;
-
- this.selectedVs = 0;
- this.selectedFpa = 0;
-
- SimVar.SetSimVarValue('K:A32NX.FCU_TO_AP_VS_PUSH', 'number', 0);
- }
-
private onRotate(): void {
if (
this.currentState === A320_Neo_FCU_VSpeed_State.Idle ||
@@ -275,8 +259,6 @@ export class VerticalSpeedManager extends TemporaryHax implements Instrument {
this.ABS_MINMAX_FPA,
);
this.onRotate();
- } else if (_event === 'VS_PUSH') {
- this.onPush();
} else if (_event === 'VS_PULL') {
this.onPull();
} else if (_event === 'VS_SET') {
diff --git a/fbw-a380x/src/systems/instruments/src/MFD/MFD.tsx b/fbw-a380x/src/systems/instruments/src/MFD/MFD.tsx
index 87c5fe4136e..f3089fdd04b 100644
--- a/fbw-a380x/src/systems/instruments/src/MFD/MFD.tsx
+++ b/fbw-a380x/src/systems/instruments/src/MFD/MFD.tsx
@@ -263,11 +263,15 @@ export class MfdComponent extends DisplayComponent implements
this.activeUriChanged(uri);
});
- this.topRef.instance.addEventListener('mousemove', (ev) => {
- this.mouseCursorRef.getOrDefault()?.updatePosition(ev.clientX, ev.clientY);
- });
+ this.topRef.instance.addEventListener('mousemove', this.onMouseMoveHandler);
+ }
+
+ private onMouseMove(ev: MouseEvent) {
+ this.mouseCursorRef.getOrDefault()?.updatePosition(ev.clientX, ev.clientY);
}
+ private onMouseMoveHandler = this.onMouseMove.bind(this);
+
private activeUriChanged(uri: ActiveUriInformation) {
if (!this.props.fmcService.master) {
return;
@@ -325,6 +329,12 @@ export class MfdComponent extends DisplayComponent implements
// Will be called if the FMC providing all the data has changed.
}
+ destroy(): void {
+ this.topRef.getOrDefault()?.removeEventListener('mousemove', this.onMouseMoveHandler);
+
+ super.destroy();
+ }
+
render(): VNode {
return (
{
}),
);
- this.identRef.getOrDefault()?.addEventListener('click', () => {
- if (this.props.data.get()?.originalLegIndex !== null && this.props.data.get()?.originalLegIndex !== undefined) {
- this.props.openRevisionsMenuCallback();
- this.selectedForRevision.set(true);
- }
- });
+ this.identRef.getOrDefault()?.addEventListener('click', this.onIdentClickedHandler);
+
+ this.timeRef.getOrDefault()?.parentElement?.addEventListener('click', this.props.callbacks.rta);
+ }
- this.timeRef.getOrDefault()?.parentElement?.addEventListener('click', () => this.props.callbacks.rta());
+ private onIdentClicked() {
+ if (this.props.data.get()?.originalLegIndex !== null && this.props.data.get()?.originalLegIndex !== undefined) {
+ this.props.openRevisionsMenuCallback();
+ this.selectedForRevision.set(true);
+ }
}
+ private onIdentClickedHandler = this.onIdentClicked.bind(this);
+
public destroy(): void {
this.subs.forEach((sub) => sub.destroy());
this.lineColor.destroy();
+
+ this.identRef.getOrDefault()?.removeEventListener('click', this.onIdentClickedHandler);
+ this.timeRef.getOrDefault()?.parentElement?.removeEventListener('click', this.props.callbacks.rta);
+
+ if (this.props.displayEfobAndWind.get()) {
+ this.altRef.getOrDefault()?.parentElement?.addEventListener('click', this.props.callbacks.wind);
+ } else {
+ this.altRef.getOrDefault()?.parentElement?.addEventListener('click', this.props.callbacks.altitude);
+ this.speedRef.getOrDefault()?.parentElement?.addEventListener('click', this.props.callbacks.speed);
+ }
+
super.destroy();
}
@@ -1346,12 +1361,12 @@ class FplnLegLine extends DisplayComponent {
}
while (this.speedRef.instance.firstChild) {
- this.speedRef.instance.parentElement?.removeEventListener('click', () => this.props.callbacks.speed());
+ this.speedRef.instance.parentElement?.removeEventListener('click', this.props.callbacks.speed);
this.speedRef.instance.removeChild(this.speedRef.instance.firstChild);
}
while (this.altRef.instance.firstChild) {
- this.altRef.instance.parentElement?.removeEventListener('click', () => this.props.callbacks.altitude());
- this.altRef.instance.parentElement?.removeEventListener('click', () => this.props.callbacks.wind());
+ this.altRef.instance.parentElement?.removeEventListener('click', this.props.callbacks.altitude);
+ this.altRef.instance.parentElement?.removeEventListener('click', this.props.callbacks.wind);
this.altRef.instance.removeChild(this.altRef.instance.firstChild);
}
FSComponent.render(this.efobOrSpeed(data), this.speedRef.instance);
@@ -1360,7 +1375,7 @@ class FplnLegLine extends DisplayComponent {
if (this.props.displayEfobAndWind.get()) {
this.altRef.instance.style.alignSelf = 'flex-end';
this.altRef.instance.style.paddingRight = '20px';
- this.altRef.instance.parentElement?.addEventListener('click', () => this.props.callbacks.wind());
+ this.altRef.instance.parentElement?.addEventListener('click', this.props.callbacks.wind);
this.speedRef.instance.style.paddingLeft = '10px';
if (this.speedRef.instance.parentElement) {
this.speedRef.instance.parentElement.className = 'mfd-fms-fpln-label-small';
@@ -1368,9 +1383,9 @@ class FplnLegLine extends DisplayComponent {
} else {
this.altRef.instance.style.alignSelf = '';
this.altRef.instance.style.paddingRight = '';
- this.altRef.instance.parentElement?.addEventListener('click', () => this.props.callbacks.altitude());
+ this.altRef.instance.parentElement?.addEventListener('click', this.props.callbacks.altitude);
this.speedRef.instance.style.paddingLeft = '';
- this.speedRef.instance.parentElement?.addEventListener('click', () => this.props.callbacks.speed());
+ this.speedRef.instance.parentElement?.addEventListener('click', this.props.callbacks.speed);
if (this.speedRef.instance.parentElement) {
this.speedRef.instance.parentElement.className = 'mfd-fms-fpln-label-small-clickable';
}
diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/F-PLN/MfdFmsFplnDuplicateNames.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/F-PLN/MfdFmsFplnDuplicateNames.tsx
index de00607f3c8..28b58c29b52 100644
--- a/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/F-PLN/MfdFmsFplnDuplicateNames.tsx
+++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/F-PLN/MfdFmsFplnDuplicateNames.tsx
@@ -139,15 +139,16 @@ export class MfdFmsFplnDuplicateNames extends DisplayComponent {
- this.callback(this.duplicateOptions.get()[i].fixData);
- this.props.visible.set(false);
- });
+ document.getElementById(`mfd-fms-dupl-${i}`)?.addEventListener('click', this.itemClickedHandler.bind(this, i));
}
}
}
+ private itemClickedHandler(i: number) {
+ this.callback(this.duplicateOptions.get()[i].fixData);
+ this.props.visible.set(false);
+ }
+
// Entry point after opening this dialog
public deduplicateFacilities>(
items: T[],
@@ -163,6 +164,14 @@ export class MfdFmsFplnDuplicateNames extends DisplayComponent x.destroy());
+ for (let i = 0; i < this.duplicateOptions.get().length; i++) {
+ if (this.duplicateOptions.get()[i] !== undefined) {
+ document
+ .getElementById(`mfd-fms-dupl-${i}`)
+ ?.removeEventListener('click', this.itemClickedHandler.bind(this, i));
+ }
+ }
+
super.destroy();
}
diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/SURV/MfdSurvControls.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/SURV/MfdSurvControls.tsx
index da951ca61f5..b1534218054 100644
--- a/fbw-a380x/src/systems/instruments/src/MFD/pages/SURV/MfdSurvControls.tsx
+++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/SURV/MfdSurvControls.tsx
@@ -165,8 +165,8 @@ export class MfdSurvControls extends DisplayComponent {
}
if (!this.tcasFailed.get()) {
- this.tcasTaraSelectedIndex.set(0);
- this.tcasNormAbvBlwSelectedIndex.set(0);
+ this.props.bus.getPublisher().pub('mfd_tcas_alert_level', 2, true); // TA/RA
+ this.props.bus.getPublisher().pub('mfd_tcas_alt_select', 0, true); // NORM
}
if (!this.wxrFailed.get()) {
diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/Button.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/Button.tsx
index 82c83e5654e..2d898840907 100644
--- a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/Button.tsx
+++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/Button.tsx
@@ -46,12 +46,21 @@ export class Button extends DisplayComponent {
private renderedMenuItems: ButtonMenuItem[] = [];
- private clickHandler(): void {
+ private onClick() {
if (!this.props.disabled?.get()) {
this.props.onClick();
}
}
+ private onClickHandler = this.onClick.bind(this);
+
+ onDropdownMenuElementClick(val: ButtonMenuItem) {
+ val.action();
+ this.dropdownIsOpened.set(false);
+ }
+
+ private onDropdownMenuElementClickHandler = this.onDropdownMenuElementClick.bind(this);
+
private scrollMenuTo(elementIndex: number) {
// Assume 36px height for each menu item div
this.dropdownMenuRef.instance.scrollTo({ behavior: 'instant', left: 0, top: elementIndex * 36 });
@@ -78,7 +87,7 @@ export class Button extends DisplayComponent {
if (this.props.showArrow === undefined) {
this.props.showArrow = true;
}
- this.buttonRef.instance.addEventListener('click', () => this.clickHandler());
+ this.buttonRef.instance.addEventListener('click', this.onClickHandler);
this.subs.push(
this.props.disabled.sub((val) => {
@@ -106,10 +115,9 @@ export class Button extends DisplayComponent {
this.props.menuItems.sub((items) => {
// Delete click handler, delete dropdownMenuRef children, render dropdownMenuRef children,
this.renderedMenuItems?.forEach((val, i) => {
- document.getElementById(`${this.props.idPrefix}_${i}`)?.removeEventListener('click', () => {
- val.action();
- this.dropdownIsOpened.set(false);
- });
+ document
+ .getElementById(`${this.props.idPrefix}_${i}`)
+ ?.removeEventListener('click', this.onDropdownMenuElementClickHandler.bind(this, val));
});
// Delete dropdownMenuRef's children
@@ -136,10 +144,9 @@ export class Button extends DisplayComponent {
// Add click event listener
items?.forEach((val, i) => {
- document.getElementById(`${this.props.idPrefix}_${i}`)?.addEventListener('click', () => {
- val.action();
- this.dropdownIsOpened.set(false);
- });
+ document
+ .getElementById(`${this.props.idPrefix}_${i}`)
+ ?.addEventListener('click', this.onDropdownMenuElementClickHandler.bind(this, val));
});
// Check if menu would overflow vertically (i.e. leave screen at the bottom). If so, open menu upwards
@@ -191,17 +198,9 @@ export class Button extends DisplayComponent {
);
// Close dropdown menu if clicked outside
- document.getElementById('MFD_CONTENT')?.addEventListener('click', (e) => {
- if (!this.topRef.getOrDefault()?.contains(e.target as Node) && this.dropdownIsOpened.get()) {
- this.dropdownIsOpened.set(false);
- }
- });
-
- this.buttonRef.instance.addEventListener('click', () => {
- if (this.props.menuItems && this.props.menuItems.get().length > 0 && !this.props.disabled?.get()) {
- this.dropdownIsOpened.set(!this.dropdownIsOpened.get());
- }
- });
+ document.getElementById('MFD_CONTENT')?.addEventListener('click', this.onCloseDropdownHandler);
+
+ this.buttonRef.instance.addEventListener('click', this.onButtonClickHandler);
this.subs.push(
this.dropdownIsOpened.sub((val) => {
@@ -230,10 +229,30 @@ export class Button extends DisplayComponent {
}
}
+ private onCloseDropdown(e: MouseEvent) {
+ if (!this.topRef.getOrDefault()?.contains(e.target as Node) && this.dropdownIsOpened.get()) {
+ this.dropdownIsOpened.set(false);
+ }
+ }
+
+ private onCloseDropdownHandler = this.onCloseDropdown.bind(this);
+
+ private onButtonClick() {
+ if (this.props.menuItems && this.props.menuItems.get().length > 0 && !this.props.disabled?.get()) {
+ this.dropdownIsOpened.set(!this.dropdownIsOpened.get());
+ }
+ }
+
+ private onButtonClickHandler = this.onButtonClick.bind(this);
+
public destroy(): void {
// Destroy all subscriptions to remove all references to this instance.
this.subs.forEach((x) => x.destroy());
+ this.buttonRef.getOrDefault()?.removeEventListener('click', this.onClickHandler);
+ document.getElementById('MFD_CONTENT')?.removeEventListener('click', this.onCloseDropdownHandler);
+ this.buttonRef.getOrDefault()?.removeEventListener('click', this.onButtonClickHandler);
+
super.destroy();
}
diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/ContextMenu.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/ContextMenu.tsx
index 152e835a609..43ccdd7da09 100644
--- a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/ContextMenu.tsx
+++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/ContextMenu.tsx
@@ -60,12 +60,9 @@ export class ContextMenu extends DisplayComponent {
this.props.values.sub((items) => {
// Delete click handler, delete contextMenuRef children, render contextMenuRef children,
this.renderedMenuItems?.forEach((val, i) => {
- document.getElementById(`${this.props.idPrefix}_${i}`)?.removeEventListener('click', () => {
- if (!val.disabled) {
- this.hideMenu();
- val.onPressed();
- }
- });
+ document
+ .getElementById(`${this.props.idPrefix}_${i}`)
+ ?.removeEventListener('click', this.onItemClick.bind(this, val));
});
// Delete contextMenuRef's children
@@ -95,24 +92,32 @@ export class ContextMenu extends DisplayComponent {
// Add click event listener
items?.forEach((val, i) => {
- document.getElementById(`${this.props.idPrefix}_${i}`)?.addEventListener('click', () => {
- if (!val.disabled) {
- this.hideMenu();
- val.onPressed();
- }
- });
+ document
+ .getElementById(`${this.props.idPrefix}_${i}`)
+ ?.addEventListener('click', this.onItemClick.bind(this, val));
});
}, true),
);
// Close dropdown menu if clicked outside
- document.getElementById('MFD_CONTENT')?.addEventListener('click', () => {
- if (Date.now() - this.openedAt > 100 && this.props.opened.get() === true) {
- this.hideMenu();
- }
- });
+ document.getElementById('MFD_CONTENT')?.addEventListener('click', this.onCloseHandler);
}
+ private onItemClick(val: ContextMenuElement) {
+ if (!val.disabled) {
+ this.hideMenu();
+ val.onPressed();
+ }
+ }
+
+ private onClose() {
+ if (Date.now() - this.openedAt > 100 && this.props.opened.get() === true) {
+ this.hideMenu();
+ }
+ }
+
+ private onCloseHandler = this.onClose.bind(this);
+
render(): VNode {
return ;
}
diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DropdownMenu.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DropdownMenu.tsx
index 6fdb9167a21..893e388d4dd 100644
--- a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DropdownMenu.tsx
+++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DropdownMenu.tsx
@@ -71,15 +71,15 @@ export class DropdownMenu extends DisplayComponent {
true,
);
- clickHandler(i: number, thisArg: DropdownMenu) {
+ private onClick(i: number) {
if (!this.props.inactive?.get()) {
this.freeTextEntered = false;
- if (thisArg.props.onModified) {
- thisArg.props.onModified(this.renderedDropdownOptionsIndices[i], '');
+ if (this.props.onModified) {
+ this.props.onModified(this.renderedDropdownOptionsIndices[i], '');
} else {
- thisArg.props.selectedIndex.set(this.renderedDropdownOptionsIndices[i]);
+ this.props.selectedIndex.set(this.renderedDropdownOptionsIndices[i]);
}
- thisArg.dropdownIsOpened.set(false);
+ this.dropdownIsOpened.set(false);
this.filterList('');
}
}
@@ -102,7 +102,7 @@ export class DropdownMenu extends DisplayComponent {
private filterList(text: string) {
const arr = this.props.values.getArray();
- this.renderedDropdownOptionsIndices = arr.map((val, idx) => idx).filter((val, idx) => arr[idx].startsWith(text));
+ this.renderedDropdownOptionsIndices = arr.map((val, idx) => idx).filter((_, idx) => arr[idx].startsWith(text));
this.renderedDropdownOptions.set(arr.filter((val) => val.startsWith(text)));
}
@@ -134,7 +134,7 @@ export class DropdownMenu extends DisplayComponent {
if (document.getElementById(`${this.props.idPrefix}_${i}`)) {
document
.getElementById(`${this.props.idPrefix}_${i}`)
- ?.removeEventListener('click', () => this.clickHandler(i, this));
+ ?.removeEventListener('click', this.onClick.bind(this, i));
}
});
@@ -158,9 +158,7 @@ export class DropdownMenu extends DisplayComponent {
// Add click handlers
array.forEach((val, i) => {
- document
- .getElementById(`${this.props.idPrefix}_${i}`)
- ?.addEventListener('click', () => this.clickHandler(i, this));
+ document.getElementById(`${this.props.idPrefix}_${i}`)?.addEventListener('click', this.onClick.bind(this, i));
});
}),
);
@@ -186,18 +184,10 @@ export class DropdownMenu extends DisplayComponent {
}),
);
- this.dropdownSelectorRef.instance.addEventListener('click', () => {
- if (!this.props.inactive?.get()) {
- this.dropdownIsOpened.set(!this.dropdownIsOpened.get());
- }
- });
+ this.dropdownSelectorRef.instance.addEventListener('click', this.onOpenCloseClickHandler);
// Close dropdown menu if clicked outside
- document.getElementById('MFD_CONTENT')?.addEventListener('click', (e) => {
- if (!this.topRef.getOrDefault()?.contains(e.target as Node) && this.dropdownIsOpened.get()) {
- this.dropdownIsOpened.set(false);
- }
- });
+ document.getElementById('MFD_CONTENT')?.addEventListener('click', this.onClickedOutsideHandler);
this.subs.push(
this.dropdownIsOpened.sub((val) => {
@@ -233,6 +223,22 @@ export class DropdownMenu extends DisplayComponent {
// TODO add KCCU events
}
+ private onOpenCloseClick() {
+ if (!this.props.inactive?.get()) {
+ this.dropdownIsOpened.set(!this.dropdownIsOpened.get());
+ }
+ }
+
+ private onOpenCloseClickHandler = this.onOpenCloseClick.bind(this);
+
+ private onClickedOutside(e: MouseEvent) {
+ if (!this.topRef.getOrDefault()?.contains(e.target as Node) && this.dropdownIsOpened.get()) {
+ this.dropdownIsOpened.set(false);
+ }
+ }
+
+ private onClickedOutsideHandler = this.onClickedOutside.bind(this);
+
/**
* Scrolls the dropdown list to the given index
*
@@ -252,6 +258,9 @@ export class DropdownMenu extends DisplayComponent {
// Destroy all subscriptions to remove all references to this instance.
this.subs.forEach((x) => x.destroy());
+ this.dropdownSelectorRef.getOrDefault()?.removeEventListener('click', this.onOpenCloseClickHandler);
+ document.getElementById('MFD_CONTENT')?.removeEventListener('click', this.onClickedOutsideHandler);
+
super.destroy();
}
diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/IconButton.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/IconButton.tsx
index 26d5f84b01d..c4fcc53fcf6 100644
--- a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/IconButton.tsx
+++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/IconButton.tsx
@@ -29,12 +29,14 @@ export class IconButton extends DisplayComponent {
private fillColor = Subject.create('white');
- clickHandler(): void {
+ private onClick() {
if (!this.props?.disabled?.get() && this.props.onClick !== undefined) {
this.props.onClick();
}
}
+ private onClickHandler = this.onClick.bind(this);
+
updateSvgColor(color: string) {
if (this.svgGroupRef) {
this.svgGroupRef.getOrDefault()?.setAttribute('fill', color);
@@ -58,13 +60,15 @@ export class IconButton extends DisplayComponent {
}, true),
);
- this.spanRef.instance.addEventListener('click', () => this.clickHandler());
+ this.spanRef.instance.addEventListener('click', this.onClickHandler);
}
public destroy(): void {
// Destroy all subscriptions to remove all references to this instance.
this.subs.forEach((x) => x.destroy());
+ this.spanRef.getOrDefault()?.removeEventListener('click', this.onClickHandler);
+
super.destroy();
}
diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/InputField.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/InputField.tsx
index bdb567abc7d..1f01fc8a02c 100644
--- a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/InputField.tsx
+++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/InputField.tsx
@@ -176,6 +176,8 @@ export class InputField extends DisplayComponent> {
}
}
+ private onKeyDownHandler = this.onKeyDown.bind(this);
+
private handleBackspace() {
if (this.modifiedFieldValue.get() === null && this.props.canBeCleared?.get()) {
this.modifiedFieldValue.set('');
@@ -188,7 +190,7 @@ export class InputField extends DisplayComponent> {
this.onInput();
}
- private onKeyPress(ev: KeyboardEvent) {
+ private onKeyPress = (ev: KeyboardEvent) => {
if (!this.isFocused.get()) {
return;
}
@@ -210,9 +212,11 @@ export class InputField extends DisplayComponent> {
if (key === ',') {
this.handleKeyInput('.');
}
- }
+ };
- private handleKeyInput(key: string) {
+ private onKeyPressHandler = this.onKeyPress.bind(this);
+
+ private handleKeyInput = (key: string) => {
if (this.modifiedFieldValue.get() === null) {
this.modifiedFieldValue.set('');
this.spanningDivRef.instance.style.justifyContent = 'flex-start';
@@ -224,7 +228,7 @@ export class InputField extends DisplayComponent> {
}
this.onInput();
- }
+ };
private handleEnter() {
if (this.props.handleFocusBlurExternally) {
@@ -263,6 +267,8 @@ export class InputField extends DisplayComponent> {
}
}
+ private onFocusHandler = this.onFocus.bind(this);
+
public async onBlur(validateAndUpdate: boolean = true) {
if (!this.props.disabled?.get() && !this.props.inactive?.get() && this.isFocused.get()) {
if (this.props.interactionMode.get() === InteractionMode.Touchscreen) {
@@ -353,6 +359,12 @@ export class InputField extends DisplayComponent> {
this.isValidating.set(false);
}
+ private onFocusTextInput() {
+ this.textInputRef.instance.focus();
+ }
+
+ private onFocusTextInputHandler = this.onFocusTextInput.bind(this);
+
onAfterRender(node: VNode): void {
super.onAfterRender(node);
@@ -483,23 +495,15 @@ export class InputField extends DisplayComponent> {
this.subs.push(this.props.dataEntryFormat.reFormatTrigger.sub(() => this.updateDisplayElement()));
}
- this.textInputRef.instance.addEventListener('keypress', (ev) => this.onKeyPress(ev));
- this.textInputRef.instance.addEventListener('keydown', (ev) => this.onKeyDown(ev));
+ this.textInputRef.instance.addEventListener('keypress', this.onKeyPressHandler);
+ this.textInputRef.instance.addEventListener('keydown', this.onKeyDownHandler);
if (!this.props.handleFocusBlurExternally) {
- this.textInputRef.instance.addEventListener('focus', () => this.onFocus());
- this.textInputRef.instance.addEventListener('blur', () => {
- this.onBlur();
- });
- this.spanningDivRef.instance.addEventListener('click', () => {
- this.textInputRef.instance.focus();
- });
- this.leadingUnitRef.instance.addEventListener('click', () => {
- this.textInputRef.instance.focus();
- });
- this.trailingUnitRef.instance.addEventListener('click', () => {
- this.textInputRef.instance.focus();
- });
+ this.textInputRef.instance.addEventListener('focus', this.onFocusHandler);
+ this.textInputRef.instance.addEventListener('blur', this.onBlur.bind(this, true));
+ this.spanningDivRef.instance.addEventListener('click', this.onFocusTextInputHandler);
+ this.leadingUnitRef.instance.addEventListener('click', this.onFocusTextInputHandler);
+ this.trailingUnitRef.instance.addEventListener('click', this.onFocusTextInputHandler);
}
this.props.hEventConsumer.handle((key) => {
@@ -568,6 +572,24 @@ export class InputField extends DisplayComponent> {
} */
}
+ destroy(): void {
+ // Destroy all subscriptions to remove all references to this instance.
+ this.subs.forEach((x) => x.destroy());
+
+ this.textInputRef.getOrDefault()?.removeEventListener('keypress', this.onKeyPressHandler);
+ this.textInputRef.getOrDefault()?.removeEventListener('keydown', this.onKeyDownHandler);
+
+ if (!this.props.handleFocusBlurExternally) {
+ this.textInputRef.getOrDefault()?.removeEventListener('focus', this.onFocusHandler);
+ this.textInputRef.getOrDefault()?.removeEventListener('blur', this.onBlur.bind(this, true));
+ this.spanningDivRef.getOrDefault()?.removeEventListener('click', this.onFocusTextInputHandler);
+ this.leadingUnitRef.getOrDefault()?.removeEventListener('click', this.onFocusTextInputHandler);
+ this.trailingUnitRef.getOrDefault()?.removeEventListener('click', this.onFocusTextInputHandler);
+ }
+
+ super.destroy();
+ }
+
render(): VNode {
return (