@@ -84,6 +84,11 @@ export function getControllerColor(facility: number): string {
8484 }
8585}
8686
87+ function copyControllerAtisToClipboard ( controller : ControllerShort | undefined ) {
88+ const atis = controller ?. atis ?. join ( "\n" ) || "" ;
89+ navigator . clipboard . writeText ( atis ) ;
90+ }
91+
8792export function AirportOverlay ( {
8893 cached,
8994 short,
@@ -93,29 +98,46 @@ export function AirportOverlay({
9398 short : Required < AirportShort > | null ;
9499 merged : ControllerMerged | null ;
95100} ) {
96- const [ activeController , setActiveController ] = useState ( null as string | null ) ;
101+ const [ hoveredController , setHoveredController ] = useState ( null as string | null ) ;
102+ const [ clickedController , setClickedController ] = useState ( null as string | null ) ;
103+ const [ copied , setCopied ] = useState ( false ) ;
97104
98105 const controllers = merged ?. controllers as Required < ControllerShort > [ ] | undefined ;
99106 const sortedControllers = controllers ?. sort ( ( a , b ) => b . facility - a . facility ) ;
100107
108+ const handleCopyClick = ( ) => {
109+ copyControllerAtisToClipboard ( sortedControllers ?. find ( ( c ) => c . callsign === hoveredController ) ) ;
110+ setCopied ( true ) ;
111+ setTimeout ( ( ) => setCopied ( false ) , 2000 ) ;
112+ } ;
113+
101114 return (
102115 < div className = "overlay-wrapper" >
103- { activeController && (
116+ { ( clickedController || hoveredController ) && (
104117 < div className = "overlay-atis" >
105118 < div className = "overlay-atis-item" >
106- { sortedControllers ?. find ( ( c ) => c . callsign === activeController ) ?. atis ?. join ( "\n" ) || "Currently unavailable" }
119+ { sortedControllers ?. find ( ( c ) => c . callsign === clickedController || c . callsign === hoveredController ) ?. atis ?. join ( "\n" ) ||
120+ "Currently unavailable" }
107121 </ div >
108122 </ div >
109123 ) }
110124 { sortedControllers && sortedControllers . length > 0 && (
111- < div className = "overlay-live controller" onPointerLeave = { ( ) => setActiveController ( null ) } >
125+ < div className = "overlay-live controller" onPointerLeave = { ( ) => setHoveredController ( null ) } >
112126 { sortedControllers ?. map ( ( c ) => {
113127 return (
114- < div key = { c . callsign } className = "overlay-live-item controller" onPointerEnter = { ( ) => setActiveController ( c . callsign ) } >
128+ < div
129+ key = { c . callsign }
130+ className = { `overlay-live-item controller${ clickedController === c . callsign ? " active" : "" } ` }
131+ onPointerEnter = { ( ) => setHoveredController ( c . callsign ) }
132+ onClick = { ( ) => setClickedController ( c . callsign ) }
133+ >
115134 < div className = "overlay-controller-color" style = { { backgroundColor : getControllerColor ( c . facility ) } } > </ div >
116135 < div className = "overlay-controller-callsign" > { c . callsign } </ div >
117136 < div className = "overlay-controller-frequency" > { ( c . frequency / 1000 ) . toFixed ( 3 ) } </ div >
118137 < div className = "overlay-controller-connections" > { c . connections } </ div >
138+ < button type = "button" className = "overlay-controller-save" onClick = { handleCopyClick } >
139+ { copied && hoveredController === c . callsign ? "✅" : "📋" }
140+ </ button >
119141 </ div >
120142 ) ;
121143 } ) }
@@ -160,28 +182,45 @@ export function AirportOverlay({
160182}
161183
162184export function SectorOverlay ( { cached, merged } : { cached : SimAwareTraconFeature | FIRFeature | null ; merged : ControllerMerged | null } ) {
163- const [ activeController , setActiveController ] = useState ( null as string | null ) ;
185+ const [ hoveredController , setHoveredController ] = useState ( null as string | null ) ;
186+ const [ clickedController , setClickedController ] = useState ( null as string | null ) ;
187+ const [ copied , setCopied ] = useState ( false ) ;
164188
165189 const controllers = merged ?. controllers as Required < ControllerShort > [ ] | undefined ;
166190
191+ const handleCopyClick = ( ) => {
192+ copyControllerAtisToClipboard ( controllers ?. find ( ( c ) => c . callsign === hoveredController ) ) ;
193+ setCopied ( true ) ;
194+ setTimeout ( ( ) => setCopied ( false ) , 2000 ) ;
195+ } ;
196+
167197 return (
168198 < div className = "overlay-wrapper" >
169- { activeController && (
199+ { ( clickedController || hoveredController ) && (
170200 < div className = "overlay-atis" >
171201 < div className = "overlay-atis-item" >
172- { controllers ?. find ( ( c ) => c . callsign === activeController ) ?. atis ?. join ( "\n" ) || "Currently unavailable" }
202+ { controllers ?. find ( ( c ) => c . callsign === clickedController || c . callsign === hoveredController ) ?. atis ?. join ( "\n" ) ||
203+ "Currently unavailable" }
173204 </ div >
174205 </ div >
175206 ) }
176207 { controllers && controllers . length > 0 && (
177- < div className = "overlay-live controller" onPointerLeave = { ( ) => setActiveController ( null ) } >
208+ < div className = "overlay-live controller" onPointerLeave = { ( ) => setHoveredController ( null ) } >
178209 { controllers ?. map ( ( c ) => {
179210 return (
180- < div key = { c . callsign } className = "overlay-live-item controller" onPointerEnter = { ( ) => setActiveController ( c . callsign ) } >
211+ < div
212+ key = { c . callsign }
213+ className = { `overlay-live-item controller${ clickedController === c . callsign ? " active" : "" } ` }
214+ onPointerEnter = { ( ) => setHoveredController ( c . callsign ) }
215+ onClick = { ( ) => setClickedController ( c . callsign ) }
216+ >
181217 < div className = "overlay-controller-color" style = { { backgroundColor : getControllerColor ( c . facility ) } } > </ div >
182218 < div className = "overlay-controller-callsign" > { c . callsign } </ div >
183219 < div className = "overlay-controller-frequency" > { ( c . frequency / 1000 ) . toFixed ( 3 ) } </ div >
184220 < div className = "overlay-controller-connections" > { c . connections } </ div >
221+ < button type = "button" className = "overlay-controller-save" onClick = { handleCopyClick } >
222+ { copied && hoveredController === c . callsign ? "✅" : "📋" }
223+ </ button >
185224 </ div >
186225 ) ;
187226 } ) }
0 commit comments