Tailwind
This commit is contained in:
1 parent
13bea68195
commit
e4ed55cac9
15 files changed
+947
-678
No files matched your search
@@ -0,0 +1,3 @@
|
||||
public
|
||||
pnpm-lock.yaml
|
||||
node_modules
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"printWidth": 120,
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"trailingComma": "es5",
|
||||
"singleQuote": true,
|
||||
"arrowParens": "always",
|
||||
"plugins": ["prettier-plugin-organize-imports", "prettier-plugin-tailwindcss"]
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
module.exports = {
|
||||
printWidth: 120,
|
||||
tabWidth: 2,
|
||||
semi: true,
|
||||
trailingComma: "es5",
|
||||
singleQuote: true,
|
||||
arrowParens: "always",
|
||||
plugins: ["prettier-plugin-organize-imports"],
|
||||
};
|
||||
+3
-1
@@ -2,8 +2,10 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<link href="/src/style.css" rel="stylesheet">
|
||||
|
||||
<title>MD-11 NavData Browser</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"lint": "eslint . --ext .ts,.tsx --fix",
|
||||
"preview": "vite preview",
|
||||
"parser": "node --experimental-strip-types src/parser/node.ts"
|
||||
},
|
||||
@@ -18,12 +18,14 @@
|
||||
"magvar": "^2.0.0",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-leaflet": "^5.0.0"
|
||||
"react-leaflet": "^5.0.0",
|
||||
"tailwindcss": "^4.1.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.31.0",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@types/leaflet": "^1.9.20",
|
||||
"@types/node": "^24.0.13",
|
||||
"@types/node": "^24.0.14",
|
||||
"@types/object-hash": "^3.0.6",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
@@ -35,8 +37,9 @@
|
||||
"object-hash": "^3.0.0",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-organize-imports": "^4.1.0",
|
||||
"prettier-plugin-tailwindcss": "^0.6.14",
|
||||
"typescript": "~5.8.3",
|
||||
"typescript-eslint": "^8.36.0",
|
||||
"typescript-eslint": "^8.37.0",
|
||||
"vite": "^7.0.4"
|
||||
}
|
||||
}
|
||||
Generated
+816
-543
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1 @@
|
||||
[{"ID":1,"Name":"KATMAI LODGE","ICAO":"00AN","PrimaryID":null,"Latitude":59.09347222,"Longitude":-156.45583333,"Elevation":80,"TransAlt":18000},{"ID":2,"Name":"ARLAND","ICAO":"00AR","PrimaryID":null,"Latitude":38.96965,"Longitude":-97.60155556,"Elevation":1352,"TransAlt":null},{"ID":3,"Name":"WILLIAMS AG","ICAO":"00CL","PrimaryID":null,"Latitude":39.42718889,"Longitude":-121.76342778,"Elevation":87,"TransAlt":null},{"ID":4,"Name":"GOLTL","ICAO":"00IG","PrimaryID":null,"Latitude":39.72402778,"Longitude":-101.39599444,"Elevation":3359,"TransAlt":null},{"ID":5,"Name":"THOMPSON FARMS","ICAO":"00TN","PrimaryID":null,"Latitude":36.12045,"Longitude":-86.35953333,"Elevation":675,"TransAlt":null},{"ID":6,"Name":"NOWHERE","ICAO":"00TT","PrimaryID":null,"Latitude":34.51660556,"Longitude":-99.93694167,"Elevation":1752,"TransAlt":null},{"ID":7,"Name":"DELVA","ICAO":"00TX","PrimaryID":null,"Latitude":32.54445278,"Longitude":-95.67313056,"Elevation":545,"TransAlt":18000},{"ID":8,"Name":"WEESKI RANCH","ICAO":"00XA","PrimaryID":null,"Latitude":30.22403889,"Longitude":-96.01415278,"Elevation":271,"TransAlt":null},{"ID":9,"Name":"MC HONE HEIGHTS","ICAO":"01AN","PrimaryID":null,"Latitude":61.64902778,"Longitude":-149.33902778,"Elevation":610,"TransAlt":null},{"ID":10,"Name":"SWANSBORO COUNTRY","ICAO":"01CL","PrimaryID":null,"Latitude":38.79990278,"Longitude":-120.734375,"Elevation":2594,"TransAlt":18000},{"ID":11,"Name":"COLEMAN HOMESTEAD","ICAO":"01CN","PrimaryID":null,"Latitude":62.10698333,"Longitude":-150.10929722,"Elevation":282,"TransAlt":null},{"ID":12,"Name":"THE FARM","ICAO":"01GE","PrimaryID":null,"Latitude":32.67515,"Longitude":-82.77099444,"Elevation":369,"TransAlt":null},{"ID":13,"Name":"LAVA HOT SPRINGS","ICAO":"01ID","PrimaryID":null,"Latitude":42.60825,"Longitude":-112.03246111,"Elevation":5268,"TransAlt":18000},{"ID":14,"Name":"GRANVILLE","ICAO":"01NK","PrimaryID":null,"Latitude":43.42294444,"Longitude":-73.268025,"Elevation":421,"TransAlt":18000},{"ID":15,"Name":"SPRING CREEK RANCH EAST","ICAO":"01OL","PrimaryID":null,"Latitude":34.37256944,"Longitude":-96.71603611,"Elevation":1060,"TransAlt":null},{"ID":16,"Name":"BEULAH FLD","ICAO":"01TX","PrimaryID":null,"Latitude":33.17058056,"Longitude":-98.62222222,"Elevation":1120,"TransAlt":null},{"ID":17,"Name":"BASS","ICAO":"02AL","PrimaryID":null,"Latitude":30.37146389,"Longitude":-87.76444444,"Elevation":61,"TransAlt":18000},{"ID":18,"Name":"EMERALD RIDGE","ICAO":"02AN","PrimaryID":null,"Latitude":59.70422222,"Longitude":-151.29586944,"Elevation":221,"TransAlt":null},{"ID":19,"Name":"OSBORN","ICAO":"02FA","PrimaryID":null,"Latitude":28.52666667,"Longitude":-81.8748,"Elevation":121,"TransAlt":18000},{"ID":20,"Name":"TRIPLE R RANCH","ICAO":"02FD","PrimaryID":null,"Latitude":30.95234722,"Longitude":-86.63541667,"Elevation":209,"TransAlt":null},{"ID":21,"Name":"FAIRPLAINS AIRPARK","ICAO":"02MI","PrimaryID":null,"Latitude":43.15700278,"Longitude":-85.14892778,"Elevation":850,"TransAlt":null},{"ID":22,"Name":"GREGORY","ICAO":"02MN","PrimaryID":null,"Latitude":45.62973611,"Longitude":-93.17497222,"Elevation":950,"TransAlt":null},{"ID":23,"Name":"TROY","ICAO":"02MO","PrimaryID":null,"Latitude":39.04626111,"Longitude":-91.02821944,"Elevation":630,"TransAlt":18000},{"ID":24,"Name":"FFR ANIMAS LANDING STRIP","ICAO":"02NM","PrimaryID":null,"Latitude":32.09071111,"Longitude":-108.87073611,"Elevation":4240,"TransAlt":null},{"ID":25,"Name":"CAWLEYS SOUTH PRAIRIE","ICAO":"02WA","PrimaryID":null,"Latitude":47.152325,"Longitude":-122.09400278,"Elevation":690,"TransAlt":18000},{"ID":26,"Name":"BLUE WATER","ICAO":"03CO","PrimaryID":null,"Latitude":38.09791944,"Longitude":-106.08975556,"Elevation":7710,"TransAlt":null},{"ID":27,"Name":"GREENS FARM FLD","ICAO":"03FL","PrimaryID":null,"Latitude":26.70547778,"Longitude":-82.05236944,"Elevation":15,"TransAlt":null},{"ID":28,"Name":"FLATLANDER","ICAO":"03IA","PrimaryID":null,"Latitude":41.14141111,"Longitude":-95.19438056,"Elevation":1081,"TransAlt":null},{"ID":29,"Name":"DORTON","ICAO":"03MO","PrimaryID":null,"Latitude":39.28883056,"Longitude":-93.99242222,"Elevation":930,"TransAlt":null},{"ID":30,"Name":"BLUEBIRD","ICAO":"03OL","PrimaryID":null,"Latitude":35.01233333,"Longitude":-97.70273611,"Elevation":1232,"TransAlt":null},{"ID":31,"Name":"BRAZOS POLO","ICAO":"03TT","PrimaryID":null,"Latitude":29.63211667,"Longitude":-95.93248056,"Elevation":117,"TransAlt":null},{"ID":32,"Name":"AEROSTONE AIRFIELD","ICAO":"03WN","PrimaryID":null,"Latitude":45.87545556,"Longitude":-120.672325,"Elevation":2342,"TransAlt":null},{"ID":33,"Name":"NORTH 40","ICAO":"04AN","PrimaryID":null,"Latitude":61.73751111,"Longitude":-148.700725,"Elevation":943,"TransAlt":null},{"ID":34,"Name":"WHITE HILLS","ICAO":"04AZ","PrimaryID":null,"Latitude":35.72177778,"Longitude":-114.45053889,"Elevation":2470,"TransAlt":null},{"ID":35,"Name":"GRAY BUTTE","ICAO":"04CA","PrimaryID":null,"Latitude":34.56664444,"Longitude":-117.67077222,"Elevation":3039,"TransAlt":18000},{"ID":36Line truncated
|
||||
+8
-109
@@ -1,8 +1,5 @@
|
||||
import { default as L, default as Leaflet } from 'leaflet';
|
||||
import 'leaflet-svg-shape-markers';
|
||||
import hash from 'object-hash';
|
||||
import { createRef, useEffect, useState } from 'react';
|
||||
import { GeoJSON, MapContainer, TileLayer } from 'react-leaflet';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Map } from './components/map/Map';
|
||||
import Parser from './parser/parser';
|
||||
|
||||
const parser = await Parser.instance();
|
||||
@@ -13,122 +10,24 @@ function App() {
|
||||
const [selectedTerminal, setSelectedTerminal] = useState(terminals[0]);
|
||||
const [procedures, setProcedures] = useState<object[]>([]);
|
||||
|
||||
const mapRef = createRef<Leaflet.Map>();
|
||||
const layerRef = createRef<Leaflet.GeoJSON>();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
setProcedures(await parser.parse(selectedTerminal));
|
||||
})();
|
||||
}, [selectedTerminal]);
|
||||
|
||||
useEffect(() => {
|
||||
if (layerRef.current && mapRef.current) {
|
||||
mapRef.current.flyToBounds(layerRef.current.getBounds(), {
|
||||
animate: false,
|
||||
padding: [50, 50],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', height: '100vh', width: '100vw' }}>
|
||||
<MapContainer
|
||||
center={[51.505, -0.09]}
|
||||
zoom={13}
|
||||
zoomSnap={0}
|
||||
zoomDelta={0.1}
|
||||
wheelPxPerZoomLevel={1000}
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
ref={mapRef}
|
||||
>
|
||||
<TileLayer
|
||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
/>
|
||||
{procedures.map((procedure) => (
|
||||
<>
|
||||
<GeoJSON
|
||||
key={hash(procedure ?? '') + 'lines'}
|
||||
data={procedure}
|
||||
style={({ properties }) => ({
|
||||
color: '#ff00ff',
|
||||
stroke: true,
|
||||
weight: 5,
|
||||
opacity: 1,
|
||||
dashArray: properties.isManual ? '20, 20' : undefined,
|
||||
})}
|
||||
filter={(feature) => feature.geometry.type !== 'Point'}
|
||||
ref={layerRef}
|
||||
/>
|
||||
<GeoJSON
|
||||
key={hash(procedure ?? '') + 'points'}
|
||||
data={procedure}
|
||||
style={{
|
||||
color: 'black',
|
||||
fill: true,
|
||||
fillColor: 'transparent',
|
||||
stroke: true,
|
||||
weight: 3,
|
||||
}}
|
||||
pointToLayer={({ properties }, latlng) => {
|
||||
if (properties.isFlyOver)
|
||||
return L.shapeMarker(latlng, {
|
||||
shape: 'triangle',
|
||||
radius: 6,
|
||||
});
|
||||
if (properties.isIntersection) return L.circleMarker(latlng, { radius: 6 });
|
||||
|
||||
return L.shapeMarker(latlng, {
|
||||
shape: 'star-4',
|
||||
radius: 10,
|
||||
rotation: 45,
|
||||
});
|
||||
}}
|
||||
onEachFeature={({ geometry, properties }, layer) => {
|
||||
if (geometry.type === 'Point') {
|
||||
layer.bindPopup(
|
||||
`${properties.name}<br>
|
||||
${properties.altitude} ft<br>
|
||||
${properties.speed} kts<br>
|
||||
CNSTR:
|
||||
${properties.altitudeConstraint ?? ''}
|
||||
${properties.speedConstraint ?? ''}<br>`
|
||||
);
|
||||
}
|
||||
}}
|
||||
filter={(feature) => feature.geometry.type === 'Point'}
|
||||
/>
|
||||
</>
|
||||
))}
|
||||
</MapContainer>
|
||||
<div
|
||||
style={{
|
||||
overflowY: 'scroll',
|
||||
width: '200px',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
padding: '5px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<div className="flex h-dvh w-dvw">
|
||||
<Map procedures={procedures} />
|
||||
<div className="w-[200px] overflow-y-auto">
|
||||
<div className="flex flex-col gap-1 p-1">
|
||||
{terminals.map((terminal) => (
|
||||
<div
|
||||
key={terminal}
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
background: '#eeeeee',
|
||||
border: selectedTerminal === terminal ? '1px solid black' : '1px solid #eeeeee',
|
||||
padding: '5px',
|
||||
}}
|
||||
className={`flex cursor-pointer flex-col border bg-gray-300 p-1 ${selectedTerminal === terminal ? 'border-black' : 'border-gray-300'}`}
|
||||
onClick={() => setSelectedTerminal(terminal)}
|
||||
>
|
||||
<span style={{ whiteSpace: 'nowrap' }}>
|
||||
<span className="whitespace-nowrap">
|
||||
{(() => {
|
||||
const t = parser.terminals.find(({ ID }) => ID === terminal);
|
||||
return `${t?.ICAO} - ${t?.FullName}`;
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
import { default as L } from 'leaflet';
|
||||
import 'leaflet-svg-shape-markers';
|
||||
import hash from 'object-hash';
|
||||
import { createRef, Fragment, useEffect, type FC } from 'react';
|
||||
import { GeoJSON, MapContainer, TileLayer } from 'react-leaflet';
|
||||
|
||||
interface MapProps {
|
||||
procedures: object[];
|
||||
}
|
||||
|
||||
export const Map: FC<MapProps> = ({ procedures }) => {
|
||||
const mapRef = createRef<L.Map>();
|
||||
const layerRef = createRef<L.GeoJSON>();
|
||||
|
||||
useEffect(() => {
|
||||
if (layerRef.current && mapRef.current) {
|
||||
mapRef.current.flyToBounds(layerRef.current.getBounds(), {
|
||||
animate: false,
|
||||
padding: [50, 50],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<MapContainer
|
||||
center={[51.505, -0.09]}
|
||||
zoom={13}
|
||||
zoomSnap={0}
|
||||
zoomDelta={0.1}
|
||||
wheelPxPerZoomLevel={1000}
|
||||
ref={mapRef}
|
||||
className="h-full w-full"
|
||||
>
|
||||
<TileLayer
|
||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
/>
|
||||
{procedures.map((procedure) => (
|
||||
<Fragment key={hash(procedure ?? '')}>
|
||||
<GeoJSON
|
||||
key={hash(procedure ?? '') + 'lines'}
|
||||
data={procedure}
|
||||
style={({ properties }) => ({
|
||||
color: '#ff00ff',
|
||||
stroke: true,
|
||||
weight: 5,
|
||||
opacity: 1,
|
||||
dashArray: properties.isManual ? '20, 20' : undefined,
|
||||
})}
|
||||
filter={(feature) => feature.geometry.type !== 'Point'}
|
||||
ref={layerRef}
|
||||
/>
|
||||
<GeoJSON
|
||||
key={hash(procedure ?? '') + 'points'}
|
||||
data={procedure}
|
||||
style={{
|
||||
color: 'black',
|
||||
fill: true,
|
||||
fillColor: 'transparent',
|
||||
stroke: true,
|
||||
weight: 3,
|
||||
}}
|
||||
pointToLayer={({ properties }, latlng) => {
|
||||
if (properties.isFlyOver)
|
||||
return L.shapeMarker(latlng, {
|
||||
shape: 'triangle',
|
||||
radius: 6,
|
||||
});
|
||||
if (properties.isIntersection) return L.circleMarker(latlng, { radius: 6 });
|
||||
|
||||
return L.shapeMarker(latlng, {
|
||||
shape: 'star-4',
|
||||
radius: 10,
|
||||
rotation: 45,
|
||||
});
|
||||
}}
|
||||
onEachFeature={({ geometry, properties }, layer) => {
|
||||
if (geometry.type === 'Point') {
|
||||
layer.bindPopup(
|
||||
`${properties.name}<br>
|
||||
${properties.altitude} ft<br>
|
||||
${properties.speed} kts<br>
|
||||
CNSTR:
|
||||
${properties.altitudeConstraint ?? ''}
|
||||
${properties.speedConstraint ?? ''}<br>`
|
||||
);
|
||||
}
|
||||
}}
|
||||
filter={(feature) => feature.geometry.type === 'Point'}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
</MapContainer>
|
||||
);
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
@@ -3,7 +3,6 @@ import { createRoot } from 'react-dom/client';
|
||||
import App from './App.tsx';
|
||||
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import './index.css';
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
|
||||
@@ -20,7 +20,7 @@ export const TerminatorsFC = (
|
||||
const line: LineSegment[] = [[previousFix.longitude, previousFix.latitude]];
|
||||
|
||||
if (previousFix.isFlyOver) {
|
||||
let crsIntoEndpoint = trackIntoEndpoint;
|
||||
const crsIntoEndpoint = trackIntoEndpoint;
|
||||
|
||||
// Check if there even is an arc
|
||||
if (!crsIntoEndpoint.equal(lastCourse)) {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
@import 'tailwindcss';
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
//eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
|
||||
import 'leaflet';
|
||||
|
||||
declare module 'leaflet' {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import react from '@vitejs/plugin-react-swc';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
plugins: [tailwindcss(), react()],
|
||||
});
|
||||
Reference in new issue
Block a user