gtfs-garage-web 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -2
- package/dist/dataset.js +1 -1
- package/dist/derive-BnZIwdMc.js +122 -0
- package/dist/gtfs-garage-web.css +1 -1
- package/dist/map/layers.d.ts +12 -0
- package/dist/map/map.d.ts +85 -1
- package/dist/map.js +229 -104
- package/dist/markup.d.ts +8 -4
- package/dist/mount-Cblp0thn.js +2900 -0
- package/dist/mount.d.ts +61 -4
- package/dist/mount.js +21 -5
- package/dist/parquet.js +1016 -157
- package/dist/react.js +1 -1
- package/dist/sources/api.gen.d.ts +986 -60
- package/dist/sources/explore/derive.d.ts +81 -0
- package/dist/sources/explore/geojson.d.ts +65 -0
- package/dist/sources/explore/index.d.ts +33 -0
- package/dist/sources/explore/sql.d.ts +88 -0
- package/dist/sources/parquet.d.ts +47 -3
- package/dist/sources/rest.d.ts +11 -1
- package/dist/sources/types.d.ts +75 -0
- package/dist/state.d.ts +60 -1
- package/dist/ui/calendar-grid.d.ts +66 -0
- package/dist/ui/date-bar.d.ts +40 -0
- package/dist/ui/explore-format.d.ts +94 -0
- package/dist/ui/feed-loader.d.ts +22 -3
- package/dist/ui/feed-metrics.d.ts +6 -4
- package/dist/ui/history.d.ts +9 -1
- package/dist/ui/html.d.ts +32 -0
- package/dist/ui/navigation.d.ts +30 -2
- package/dist/ui/options-menu.d.ts +134 -0
- package/dist/ui/route-filters.d.ts +73 -0
- package/dist/ui/screen-pane.d.ts +52 -0
- package/dist/ui/screens/calendar.d.ts +22 -0
- package/dist/ui/screens/index.d.ts +26 -0
- package/dist/ui/screens/overview.d.ts +17 -0
- package/dist/ui/screens/routes.d.ts +38 -0
- package/dist/ui/screens/timetable.d.ts +46 -0
- package/dist/ui/screens/types.d.ts +152 -0
- package/dist/ui/service-periods.d.ts +56 -0
- package/dist/ui/sidebar.d.ts +9 -2
- package/package.json +1 -1
- package/dist/mount-EvMk5jp_.js +0 -1062
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# gtfs-garage-web
|
|
2
2
|
|
|
3
|
-
Browse a GTFS feed:
|
|
4
|
-
|
|
3
|
+
Browse a GTFS feed: an overview of what is in it, a calendar of the days it
|
|
4
|
+
runs, a page per route - and under all of that, a table with filters and
|
|
5
|
+
foreign-key navigation, and a map of its stops, shapes and zones. The viewer of
|
|
5
6
|
[GTFS Garage](https://github.com/MobilityData/gtfs-garage), packaged so it can
|
|
6
7
|
be mounted in another application.
|
|
7
8
|
|
|
@@ -71,6 +72,28 @@ from your own backend, either serve the same API — specified in
|
|
|
71
72
|
a backend of any other shape, for authentication, or for reading Parquet in the
|
|
72
73
|
browser with no server at all.
|
|
73
74
|
|
|
75
|
+
The derived views - the overview, the calendar and the route pages - are four
|
|
76
|
+
optional methods on that interface. `ParquetSource` implements all four against
|
|
77
|
+
Parquet in the browser, so a dataset on a static host gets them with no server
|
|
78
|
+
anywhere; a source that omits them shows the file browser alone, with no
|
|
79
|
+
Explore entries in the sidebar and nothing else changed.
|
|
80
|
+
|
|
81
|
+
## Changing what it shows
|
|
82
|
+
|
|
83
|
+
The derived views are **screens**, and the screens are a list:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { mount, defaultScreens } from "gtfs-garage-web";
|
|
87
|
+
|
|
88
|
+
mount(root, { screens: [...defaultScreens, myScreen] }); // add one
|
|
89
|
+
mount(root, { screens: [] }); // the file browser alone
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
A screen is an id, an optional sidebar label, an `available` predicate, a
|
|
93
|
+
`fetch` and a `render`. See
|
|
94
|
+
[`docs/INTEGRATION.md`](https://github.com/MobilityData/gtfs-garage/blob/main/docs/INTEGRATION.md)
|
|
95
|
+
for the whole interface and the helpers a screen can build on.
|
|
96
|
+
|
|
74
97
|
## Styling
|
|
75
98
|
|
|
76
99
|
Every rule is scoped under `.gtfs-viewer`, so the stylesheet cannot reach your
|
package/dist/dataset.js
CHANGED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
const l = [1, 2, 3, 4, 5, 6, 0], f = {
|
|
2
|
+
0: "Sunday",
|
|
3
|
+
1: "Monday",
|
|
4
|
+
2: "Tuesday",
|
|
5
|
+
3: "Wednesday",
|
|
6
|
+
4: "Thursday",
|
|
7
|
+
5: "Friday",
|
|
8
|
+
6: "Saturday"
|
|
9
|
+
}, d = [
|
|
10
|
+
"Jan",
|
|
11
|
+
"Feb",
|
|
12
|
+
"Mar",
|
|
13
|
+
"Apr",
|
|
14
|
+
"May",
|
|
15
|
+
"Jun",
|
|
16
|
+
"Jul",
|
|
17
|
+
"Aug",
|
|
18
|
+
"Sep",
|
|
19
|
+
"Oct",
|
|
20
|
+
"Nov",
|
|
21
|
+
"Dec"
|
|
22
|
+
], h = 1100, m = 1200;
|
|
23
|
+
function y(r, n, o) {
|
|
24
|
+
if (!o.length) return "No service";
|
|
25
|
+
if (r.length === 1) {
|
|
26
|
+
const [t, a, c] = r[0].split("-").map(Number);
|
|
27
|
+
return `${c} ${d[a - 1]} ${t}`;
|
|
28
|
+
}
|
|
29
|
+
const e = new Set([...n.entries()].filter(([, t]) => t).map(([t]) => t)), s = (...t) => t.length === e.size && t.every((a) => e.has(a));
|
|
30
|
+
return s(1, 2, 3, 4, 5) ? "Weekdays" : s(0, 6) ? "Weekends" : e.size === 1 ? `${f[[...e][0]]}s` : s(1, 2, 3, 4, 5, 6) ? "Monday to Saturday" : e.size === 7 ? "Every day" : l.filter((t) => e.has(t)).map((t) => f[t].slice(0, 3)).join(", ");
|
|
31
|
+
}
|
|
32
|
+
function M(r) {
|
|
33
|
+
const n = /* @__PURE__ */ new Map();
|
|
34
|
+
for (const e of r) {
|
|
35
|
+
const s = JSON.stringify(e.services);
|
|
36
|
+
let t = n.get(s);
|
|
37
|
+
t || (t = { services: [...e.services], dates: [], weekdays: /* @__PURE__ */ new Map(), trips: 0, routes: 0 }, n.set(s, t)), t.dates.push(e.service_date), t.weekdays.set(e.weekday, (t.weekdays.get(e.weekday) ?? 0) + 1), t.trips = e.trips, t.routes = e.routes;
|
|
38
|
+
}
|
|
39
|
+
const o = [];
|
|
40
|
+
for (const e of n.values()) {
|
|
41
|
+
const s = [...e.dates].sort();
|
|
42
|
+
o.push({
|
|
43
|
+
name: y(s, e.weekdays, e.services),
|
|
44
|
+
services: e.services,
|
|
45
|
+
trips: e.trips,
|
|
46
|
+
routes: e.routes,
|
|
47
|
+
days: s.length,
|
|
48
|
+
weekdays: [...e.weekdays.entries()].filter(([, t]) => t).map(([t]) => t).sort((t, a) => t - a),
|
|
49
|
+
first_date: s[0],
|
|
50
|
+
last_date: s[s.length - 1],
|
|
51
|
+
sample_dates: s.slice(0, 5)
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return o.sort((e, s) => s.days - e.days || s.trips - e.trips || g(e.name, s.name));
|
|
55
|
+
}
|
|
56
|
+
function g(r, n) {
|
|
57
|
+
return r < n ? -1 : r > n ? 1 : 0;
|
|
58
|
+
}
|
|
59
|
+
function v(r) {
|
|
60
|
+
var o;
|
|
61
|
+
const n = new Map(l.map((e) => [e, []]));
|
|
62
|
+
for (const e of r) (o = n.get(e.weekday)) == null || o.push(e.trips);
|
|
63
|
+
return l.map((e) => {
|
|
64
|
+
const s = n.get(e) ?? [], t = s.filter((i) => i), a = /* @__PURE__ */ new Map();
|
|
65
|
+
for (const i of t) a.set(i, (a.get(i) ?? 0) + 1);
|
|
66
|
+
let c = 0, u = 0;
|
|
67
|
+
for (const [i, p] of a)
|
|
68
|
+
p > u && (u = p, c = i);
|
|
69
|
+
return {
|
|
70
|
+
weekday: e,
|
|
71
|
+
name: f[e],
|
|
72
|
+
dates: s.length,
|
|
73
|
+
dates_with_service: t.length,
|
|
74
|
+
typical_trips: c,
|
|
75
|
+
max_trips: s.length ? Math.max(...s) : 0
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function w(r) {
|
|
80
|
+
if (r == null) return null;
|
|
81
|
+
const n = (o) => String(o).padStart(2, "0");
|
|
82
|
+
return `${n(Math.floor(r / 3600))}:${n(Math.floor(r % 3600 / 60))}:${n(r % 60)}`;
|
|
83
|
+
}
|
|
84
|
+
const _ = 400;
|
|
85
|
+
function k(r) {
|
|
86
|
+
if (!r.length) return [];
|
|
87
|
+
let n = r[0];
|
|
88
|
+
for (const s of r) s.length > n.length && (n = s);
|
|
89
|
+
const o = [];
|
|
90
|
+
for (const s of n) o.includes(s) || o.push(s);
|
|
91
|
+
let e = new Map(o.map((s, t) => [s, t]));
|
|
92
|
+
for (const s of r) {
|
|
93
|
+
let t = -1;
|
|
94
|
+
for (const a of s) {
|
|
95
|
+
const c = e.get(a);
|
|
96
|
+
if (c !== void 0) {
|
|
97
|
+
t = c;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const u = t + 1;
|
|
101
|
+
o.splice(u, 0, a), e = new Map(o.map((i, p) => [i, p])), t = u;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return o;
|
|
105
|
+
}
|
|
106
|
+
function S(r) {
|
|
107
|
+
if (!r) return null;
|
|
108
|
+
const n = r.split(":").slice(0, 3).map((o) => Number(o));
|
|
109
|
+
if (n.some((o) => !Number.isInteger(o))) return null;
|
|
110
|
+
for (; n.length < 3; ) n.push(0);
|
|
111
|
+
return n[0] * 3600 + n[1] * 60 + n[2];
|
|
112
|
+
}
|
|
113
|
+
export {
|
|
114
|
+
_ as M,
|
|
115
|
+
M as a,
|
|
116
|
+
S as b,
|
|
117
|
+
w as c,
|
|
118
|
+
h as d,
|
|
119
|
+
m as e,
|
|
120
|
+
k as s,
|
|
121
|
+
v as w
|
|
122
|
+
};
|
package/dist/gtfs-garage-web.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.gtfs-viewer{--bg: #f7f7f8;--panel: #ffffff;--border: #dcdde0;--text: #1c1e21;--muted: #6b7280;--accent: #2563eb;--accent-bg: #eef2ff;--chip-bg: #eef1f5;--font: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-family:var(--font);color:var(--text);background:var(--bg);display:flex;flex-direction:column;height:100%;min-height:0;position:relative}.gtfs-viewer,.gtfs-viewer *{box-sizing:border-box}.gtfs-viewer header{display:flex;align-items:center;gap:12px;padding:10px 16px;background:var(--panel);border-bottom:1px solid var(--border);flex-shrink:0}.gtfs-viewer header h1{font-size:16px;margin:0}.gtfs-viewer [data-el=source-label]{color:var(--muted);font-size:13px;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.gtfs-viewer button{font:inherit;cursor:pointer;border:1px solid var(--border);background:var(--panel);border-radius:6px;padding:6px 10px}.gtfs-viewer button.primary{background:var(--accent);color:#fff;border-color:var(--accent)}.gtfs-viewer button:disabled{opacity:.45;cursor:default}.gtfs-viewer button:hover{filter:brightness(.97)}.gtfs-viewer [data-el=layout]{flex:1;display:flex;min-height:0}.gtfs-viewer [data-el=sidebar]{width:210px;flex-shrink:0;background:var(--panel);border-right:1px solid var(--border);overflow-y:auto;padding:8px}.gtfs-viewer .table-tab{display:flex;justify-content:space-between;padding:7px 10px;border-radius:6px;cursor:pointer;font-size:13px}.gtfs-viewer .table-tab:hover{background:var(--bg)}.gtfs-viewer .table-tab.active{background:var(--accent-bg);color:var(--accent);font-weight:600}.gtfs-viewer .table-tab .count{color:var(--muted);font-weight:400}.gtfs-viewer .table-tab.active .count{color:var(--accent)}.gtfs-viewer .table-tab.missing{color:var(--muted);cursor:default;font-style:italic}.gtfs-viewer .table-tab.missing:hover{background:none}.gtfs-viewer .table-tab.forbidden .count{font-style:italic}.gtfs-viewer [data-el=content]{flex:1;display:flex;min-width:0;min-height:0}.gtfs-viewer [data-el=table-pane]{flex:1.3;display:flex;flex-direction:column;min-width:0;border-right:1px solid var(--border)}.gtfs-viewer [data-el=map-pane]{flex:1;min-width:0;position:relative}.gtfs-viewer [data-el=map]{position:absolute;top:0;right:0;bottom:0;left:0}.gtfs-viewer [data-el=filter-bar]{display:flex;flex-wrap:wrap;gap:6px;align-items:center;padding:8px 10px;border-bottom:1px solid var(--border);background:var(--panel)}.gtfs-viewer .raw-toggle{display:flex;align-items:center;gap:5px;margin-left:auto;padding:0 4px;font-size:12px;color:var(--muted);cursor:pointer;-webkit-user-select:none;user-select:none}.gtfs-viewer .raw-toggle input{cursor:pointer}.gtfs-viewer .chip{display:flex;align-items:center;gap:6px;background:var(--chip-bg);border-radius:999px;padding:4px 10px;font-size:12px}.gtfs-viewer .chip button{border:none;background:none;padding:0;color:var(--muted);font-size:13px;line-height:1}.gtfs-viewer [data-el=add-filter-form]{display:none;gap:6px;align-items:center;padding:8px 10px;border-bottom:1px solid var(--border);background:var(--bg);font-size:13px}.gtfs-viewer [data-el=add-filter-form].open{display:flex}.gtfs-viewer [data-el=add-filter-form] select,.gtfs-viewer [data-el=add-filter-form] input{font:inherit;padding:4px 6px;border:1px solid var(--border);border-radius:4px}.gtfs-viewer [data-el=dataset-status]{flex:1;display:flex;align-items:center;justify-content:center;padding:24px}.gtfs-viewer [data-el=dataset-status] .is-error{color:#b42318}.gtfs-viewer [data-el=table-scroll]{flex:1;overflow:auto}.gtfs-viewer table{border-collapse:collapse;width:100%;font-size:12.5px}.gtfs-viewer th,.gtfs-viewer td{border-bottom:1px solid var(--border);padding:5px 10px;text-align:left;white-space:nowrap}.gtfs-viewer th{position:sticky;top:0;background:var(--panel);z-index:1}.gtfs-viewer tr:hover td{background:#fafafa}.gtfs-viewer a.fk-link{color:var(--accent);text-decoration:none}.gtfs-viewer a.fk-link:hover{text-decoration:underline}.gtfs-viewer .related-row{display:flex;gap:4px;flex-wrap:wrap;margin-top:2px}.gtfs-viewer .related-btn{font-size:10.5px;padding:1px 6px;border-radius:999px;background:var(--chip-bg);border:none;color:var(--muted)}.gtfs-viewer .related-btn:hover{background:var(--accent-bg);color:var(--accent)}.gtfs-viewer [data-el=pager]{display:flex;align-items:center;gap:8px;padding:6px 10px;border-top:1px solid var(--border);background:var(--panel);font-size:12.5px}.gtfs-viewer [data-el=pager] .spacer{flex:1}.gtfs-viewer [data-el=load-overlay]{position:fixed;top:0;right:0;bottom:0;left:0;background:#14141973;display:flex;align-items:center;justify-content:center;z-index:50}.gtfs-viewer [data-el=load-overlay].hidden{display:none}.gtfs-viewer [data-el=load-panel]{background:var(--panel);border-radius:10px;padding:24px;width:420px;max-width:90vw}.gtfs-viewer [data-el=load-panel] h2{margin-top:0;font-size:16px}.gtfs-viewer [data-el=dropzone]{border:2px dashed var(--border);border-radius:8px;padding:24px;text-align:center;color:var(--muted);font-size:13px;margin-bottom:12px}.gtfs-viewer [data-el=dropzone].drag{border-color:var(--accent);color:var(--accent)}.gtfs-viewer [data-el=load-panel] .or{text-align:center;color:var(--muted);font-size:12px;margin:8px 0}.gtfs-viewer [data-el=load-panel] input[type=text]{width:100%;padding:6px 8px;border:1px solid var(--border);border-radius:6px;font:inherit;margin-bottom:10px}.gtfs-viewer [data-el=load-panel] input[type=text]:focus{outline:2px solid var(--accent);outline-offset:-1px;border-color:var(--accent)}.gtfs-viewer .field-label{display:block;font-size:12px;color:var(--muted);margin-bottom:4px}.gtfs-viewer [data-el=pick-row]{display:flex;gap:8px;margin-bottom:4px}.gtfs-viewer [data-el=pick-row] button{flex:1}.gtfs-viewer [data-el=load-actions]{display:flex;gap:8px;align-items:center}.gtfs-viewer [data-el=load-status]{color:var(--muted);font-size:12.5px;margin-top:8px}.gtfs-viewer [data-el=load-error]{color:[data-el="b91c1c"];font-size:12.5px;margin-top:8px}.gtfs-viewer [data-el=empty-state]{padding:40px;color:var(--muted);text-align:center}.gtfs-viewer [data-el=metrics-btn]{color:var(--muted);font-size:12.5px;padding:4px 8px;flex-shrink:0;white-space:nowrap}.gtfs-viewer [data-el=metrics-panel]{position:absolute;top:46px;right:16px;z-index:5;background:var(--panel);border:1px solid var(--border);border-radius:10px;padding:12px 14px;font-size:12.5px;max-width:90vw;max-height:70vh;overflow:auto;box-shadow:0 6px 20px #0000001f}.gtfs-viewer [data-el=metrics-panel].hidden{display:none}.gtfs-viewer [data-el=metrics-header]{display:flex;align-items:center;gap:12px;margin-bottom:8px}.gtfs-viewer [data-el=metrics-header] strong{flex:1}.gtfs-viewer [data-el=metrics-header] button{padding:0 6px;line-height:1.4}.gtfs-viewer [data-el=metrics-phases]{display:grid;grid-template-columns:auto auto;gap:2px 14px;margin:0 0 12px}.gtfs-viewer [data-el=metrics-phases] dt{color:var(--muted)}.gtfs-viewer [data-el=metrics-phases] dd{margin:0;text-align:right;font-variant-numeric:tabular-nums}.gtfs-viewer [data-el=metrics-body] table{border-collapse:collapse;white-space:nowrap}.gtfs-viewer [data-el=metrics-body] th{text-align:left;color:var(--muted);font-weight:500;border-bottom:1px solid var(--border)}.gtfs-viewer [data-el=metrics-body] th,.gtfs-viewer [data-el=metrics-body] td{padding:3px 10px 3px 0}.gtfs-viewer [data-el=metrics-body] td:not(:first-child){text-align:right;font-variant-numeric:tabular-nums}.gtfs-viewer [data-el=map-progress]{position:absolute;left:12px;bottom:12px;z-index:2;background:var(--panel);border:1px solid var(--border);border-radius:6px;padding:5px 10px;color:var(--muted);font-size:12.5px;font-variant-numeric:tabular-nums;box-shadow:0 2px 8px #0000001a;pointer-events:none}.gtfs-viewer [data-el=table-scroll] td.value-numeric{text-align:right;font-variant-numeric:tabular-nums}.gtfs-viewer [data-el=table-scroll] td.value-implied{color:var(--muted);font-style:italic;cursor:help}.gtfs-viewer [data-el=table-scroll] td.value-malformed{cursor:help}.gtfs-viewer .value-swatch{display:inline-block;width:.75em;height:.75em;margin-right:6px;border:1px solid var(--border);border-radius:2px;vertical-align:baseline}.gtfs-viewer .value-link{color:var(--accent)}
|
|
1
|
+
.gtfs-viewer{--bg: #f7f7f8;--panel: #ffffff;--border: #dcdde0;--text: #1c1e21;--muted: #6b7280;--accent: #2563eb;--accent-bg: #eef2ff;--chip-bg: #eef1f5;--font: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-family:var(--font);color:var(--text);background:var(--bg);display:flex;flex-direction:column;height:100%;min-height:0;position:relative}.gtfs-viewer,.gtfs-viewer *{box-sizing:border-box}.gtfs-viewer header{display:flex;align-items:center;gap:12px;padding:10px 16px;background:var(--panel);border-bottom:1px solid var(--border);flex-shrink:0}.gtfs-viewer header h1{font-size:16px;margin:0}.gtfs-viewer [data-el=source-label]{color:var(--muted);font-size:13px;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.gtfs-viewer button{font:inherit;cursor:pointer;border:1px solid var(--border);background:var(--panel);border-radius:6px;padding:6px 10px}.gtfs-viewer button.primary{background:var(--accent);color:#fff;border-color:var(--accent)}.gtfs-viewer button:disabled{opacity:.45;cursor:default}.gtfs-viewer button:hover{filter:brightness(.97)}.gtfs-viewer [data-el=layout]{flex:1;display:flex;min-height:0}.gtfs-viewer [data-el=sidebar]{width:210px;flex-shrink:0;background:var(--panel);border-right:1px solid var(--border);overflow-y:auto;padding:8px}.gtfs-viewer .table-tab{display:flex;justify-content:space-between;padding:7px 10px;border-radius:6px;cursor:pointer;font-size:13px}.gtfs-viewer .table-tab:hover{background:var(--bg)}.gtfs-viewer .table-tab.active{background:var(--accent-bg);color:var(--accent);font-weight:600}.gtfs-viewer .table-tab .count{color:var(--muted);font-weight:400}.gtfs-viewer .table-tab.active .count{color:var(--accent)}.gtfs-viewer .table-tab.missing{color:var(--muted);cursor:default;font-style:italic}.gtfs-viewer .table-tab.missing:hover{background:none}.gtfs-viewer .table-tab.forbidden .count{font-style:italic}.gtfs-viewer [data-el=content]{flex:1;display:flex;flex-direction:column;min-width:0;min-height:0}.gtfs-viewer [data-el=panes]{flex:1;display:flex;min-width:0;min-height:0}.gtfs-viewer [data-el=browse-pane]{flex:1.3;display:flex;flex-direction:column;min-width:0;min-height:0;border-right:1px solid var(--border)}.gtfs-viewer [data-el=toolbar]{display:flex;align-items:center;gap:6px;padding:8px 10px;border-bottom:1px solid var(--border);background:var(--panel);flex-shrink:0}.gtfs-viewer [data-el=table-pane]{flex:1;display:flex;flex-direction:column;min-width:0;min-height:0}.gtfs-viewer [data-el=map-pane]{flex:1;min-width:0;min-height:0;position:relative}.gtfs-viewer [data-el=map]{position:absolute;top:0;right:0;bottom:0;left:0}.gtfs-viewer [data-el=filter-bar]{display:flex;flex-wrap:wrap;gap:6px;align-items:center;padding:8px 10px;border-bottom:1px solid var(--border);background:var(--panel)}.gtfs-viewer .raw-toggle{display:flex;align-items:center;gap:5px;margin-left:auto;padding:0 4px;font-size:12px;color:var(--muted);cursor:pointer;-webkit-user-select:none;user-select:none}.gtfs-viewer .raw-toggle input{cursor:pointer}.gtfs-viewer .chip{display:flex;align-items:center;gap:6px;background:var(--chip-bg);border-radius:999px;padding:4px 10px;font-size:12px}.gtfs-viewer .chip button{border:none;background:none;padding:0;color:var(--muted);font-size:13px;line-height:1}.gtfs-viewer [data-el=add-filter-form]{display:none;gap:6px;align-items:center;padding:8px 10px;border-bottom:1px solid var(--border);background:var(--bg);font-size:13px}.gtfs-viewer [data-el=add-filter-form].open{display:flex}.gtfs-viewer [data-el=add-filter-form] select,.gtfs-viewer [data-el=add-filter-form] input{font:inherit;padding:4px 6px;border:1px solid var(--border);border-radius:4px}.gtfs-viewer [data-el=dataset-status]{flex:1;display:flex;align-items:center;justify-content:center;padding:24px}.gtfs-viewer [data-el=dataset-status] .is-error{color:#b42318}.gtfs-viewer [data-el=table-scroll]{flex:1;overflow:auto}.gtfs-viewer table{border-collapse:collapse;width:100%;font-size:12.5px}.gtfs-viewer th,.gtfs-viewer td{border-bottom:1px solid var(--border);padding:5px 10px;text-align:left;white-space:nowrap}.gtfs-viewer th{position:sticky;top:0;background:var(--panel);z-index:1}.gtfs-viewer tr:hover td{background:#fafafa}.gtfs-viewer a.fk-link{color:var(--accent);text-decoration:none}.gtfs-viewer a.fk-link:hover{text-decoration:underline}.gtfs-viewer .related-row{display:flex;gap:4px;flex-wrap:wrap;margin-top:2px}.gtfs-viewer .related-btn{font-size:10.5px;padding:1px 6px;border-radius:999px;background:var(--chip-bg);border:none;color:var(--muted)}.gtfs-viewer .related-btn:hover{background:var(--accent-bg);color:var(--accent)}.gtfs-viewer [data-el=pager]{display:flex;align-items:center;gap:8px;padding:6px 10px;border-top:1px solid var(--border);background:var(--panel);font-size:12.5px}.gtfs-viewer [data-el=pager] .spacer{flex:1}.gtfs-viewer [data-el=load-overlay]{position:fixed;top:0;right:0;bottom:0;left:0;background:#14141973;display:flex;align-items:center;justify-content:center;z-index:50}.gtfs-viewer [data-el=load-overlay].hidden{display:none}.gtfs-viewer [data-el=load-panel]{background:var(--panel);border-radius:10px;padding:24px;width:420px;max-width:90vw}.gtfs-viewer [data-el=load-panel] h2{margin-top:0;font-size:16px}.gtfs-viewer [data-el=dropzone]{border:2px dashed var(--border);border-radius:8px;padding:24px;text-align:center;color:var(--muted);font-size:13px;margin-bottom:12px}.gtfs-viewer [data-el=dropzone].drag{border-color:var(--accent);color:var(--accent)}.gtfs-viewer [data-el=load-panel] .or{text-align:center;color:var(--muted);font-size:12px;margin:8px 0}.gtfs-viewer [data-el=load-panel] input[type=text]{width:100%;padding:6px 8px;border:1px solid var(--border);border-radius:6px;font:inherit;margin-bottom:10px}.gtfs-viewer [data-el=load-panel] input[type=text]:focus{outline:2px solid var(--accent);outline-offset:-1px;border-color:var(--accent)}.gtfs-viewer .field-label{display:block;font-size:12px;color:var(--muted);margin-bottom:4px}.gtfs-viewer [data-el=pick-row]{display:flex;gap:8px;margin-bottom:4px}.gtfs-viewer [data-el=pick-row] button{flex:1}.gtfs-viewer [data-el=load-actions]{display:flex;gap:8px;align-items:center}.gtfs-viewer [data-el=load-status]{color:var(--muted);font-size:12.5px;margin-top:8px}.gtfs-viewer [data-el=load-error]{color:[data-el="b91c1c"];font-size:12.5px;margin-top:8px}.gtfs-viewer [data-el=empty-state]{padding:40px;color:var(--muted);text-align:center}.gtfs-viewer [data-el=options-btn]{flex-shrink:0;white-space:nowrap}.gtfs-viewer [data-el=options-panel]{position:absolute;top:46px;right:16px;z-index:5;background:var(--panel);border:1px solid var(--border);border-radius:10px;padding:12px 14px;font-size:12.5px;width:460px;max-width:90vw;max-height:70vh;overflow:auto;box-shadow:0 6px 20px #0000001f}.gtfs-viewer [data-el=options-panel].hidden{display:none}.gtfs-viewer [data-el=options-header]{display:flex;align-items:center;gap:12px;margin-bottom:8px}.gtfs-viewer [data-el=options-header] strong{flex:1}.gtfs-viewer [data-el=options-header] button{padding:0 6px;line-height:1.4}.gtfs-viewer [data-el=options-panel] section+section{margin-top:14px;padding-top:12px;border-top:1px solid var(--border)}.gtfs-viewer [data-el=options-section-title]{font-weight:600;margin-bottom:6px}.gtfs-viewer [data-el=workdir-path]{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.5px;color:var(--muted);cursor:pointer;overflow-wrap:anywhere}.gtfs-viewer [data-el=workdir-summary],.gtfs-viewer [data-el=metrics-summary]{color:var(--muted);margin:2px 0 8px}.gtfs-viewer [data-el=workdir-feeds]{display:flex;flex-direction:column;gap:4px}.gtfs-viewer .workdir-feed{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:6px;background:var(--bg)}.gtfs-viewer .workdir-feed.current{background:var(--accent-bg);color:var(--accent)}.gtfs-viewer .workdir-feed-text{flex:1;min-width:0}.gtfs-viewer .workdir-feed-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.gtfs-viewer .workdir-feed-detail{color:var(--muted);font-size:11.5px}.gtfs-viewer .workdir-feed.current .workdir-feed-detail{color:var(--accent)}.gtfs-viewer .workdir-feed-actions{display:flex;gap:4px;flex-shrink:0}.gtfs-viewer .workdir-feed-actions button{font-size:11.5px;padding:3px 7px}.gtfs-viewer .workdir-remove{line-height:1.2}.gtfs-viewer .workdir-startup.on{background:var(--accent);border-color:var(--accent);color:var(--bg)}.gtfs-viewer .workdir-empty{color:var(--muted);padding:6px 8px}.gtfs-viewer [data-el=workdir-actions]{margin-top:8px}.gtfs-viewer [data-el=recent-feeds]{display:flex;flex-direction:column;gap:4px;margin-bottom:4px}.gtfs-viewer .recent-feed{display:flex;align-items:baseline;gap:8px;width:100%;text-align:left;padding:7px 10px}.gtfs-viewer .recent-feed-detail{color:var(--muted);font-size:11.5px;margin-left:auto;flex-shrink:0}.gtfs-viewer [data-el=workdir-status]{color:var(--muted);min-height:1.2em;margin-top:4px}.gtfs-viewer [data-el=workdir-section].busy{opacity:.6}.gtfs-viewer [data-el=metrics-body]{overflow-x:auto}.gtfs-viewer [data-el=metrics-phases]{display:grid;grid-template-columns:auto auto;gap:2px 14px;margin:0 0 12px}.gtfs-viewer [data-el=metrics-phases] dt{color:var(--muted)}.gtfs-viewer [data-el=metrics-phases] dd{margin:0;text-align:right;font-variant-numeric:tabular-nums}.gtfs-viewer [data-el=metrics-body] table{border-collapse:collapse;white-space:nowrap}.gtfs-viewer [data-el=metrics-body] th{text-align:left;color:var(--muted);font-weight:500;border-bottom:1px solid var(--border)}.gtfs-viewer [data-el=metrics-body] th,.gtfs-viewer [data-el=metrics-body] td{padding:3px 10px 3px 0}.gtfs-viewer [data-el=metrics-body] td:not(:first-child){text-align:right;font-variant-numeric:tabular-nums}.gtfs-viewer [data-el=map-progress]{position:absolute;left:12px;bottom:12px;z-index:2;background:var(--panel);border:1px solid var(--border);border-radius:6px;padding:5px 10px;color:var(--muted);font-size:12.5px;font-variant-numeric:tabular-nums;box-shadow:0 2px 8px #0000001a;pointer-events:none}.gtfs-viewer [data-el=table-scroll] td.value-numeric{text-align:right;font-variant-numeric:tabular-nums}.gtfs-viewer [data-el=table-scroll] td.value-implied{color:var(--muted);font-style:italic;cursor:help}.gtfs-viewer [data-el=table-scroll] td.value-malformed{cursor:help}.gtfs-viewer .value-swatch{display:inline-block;width:.75em;height:.75em;margin-right:6px;border:1px solid var(--border);border-radius:2px;vertical-align:baseline}.gtfs-viewer .value-link{color:var(--accent)}.gtfs-viewer [hidden]{display:none!important}.gtfs-viewer [data-el=screen-pane]{flex:1;min-width:0;min-height:0;overflow-y:auto;background:var(--bg)}.gtfs-viewer .sidebar-group{font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);padding:10px 10px 4px}.gtfs-viewer .explore-tab{justify-content:flex-start}.gtfs-viewer .explore{padding:16px 20px 32px}.gtfs-viewer .explore h2{margin:0 0 2px;font-size:18px}.gtfs-viewer .explore h3{margin:0 0 2px;font-size:13px;letter-spacing:.02em;text-transform:uppercase;color:var(--muted)}.gtfs-viewer .explore-hint{color:var(--muted);font-size:12px}.gtfs-viewer .explore-section{margin-top:22px}.gtfs-viewer .explore-section>p{margin:0 0 10px}.gtfs-viewer .explore-loading,.gtfs-viewer .explore-error{padding:24px;color:var(--muted);font-size:13px}.gtfs-viewer .explore-error strong{color:var(--text)}.gtfs-viewer .explore-link{color:var(--accent);text-decoration:none;font-size:12px;margin-right:8px;cursor:pointer}.gtfs-viewer .explore-link:hover{text-decoration:underline}.gtfs-viewer .overview-header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;flex-wrap:wrap;min-width:0}.gtfs-viewer .overview-header>div{min-width:0}.gtfs-viewer .overview-header h2{overflow-wrap:anywhere}.gtfs-viewer .window-badge{text-align:right;border:1px solid var(--border);border-radius:8px;padding:8px 12px;background:var(--panel);font:inherit;cursor:pointer}.gtfs-viewer .window-dates{font-size:12px;color:var(--muted)}.gtfs-viewer .window-remaining{font-weight:600;font-size:14px}.gtfs-viewer .window-ok .window-remaining{color:#15803d}.gtfs-viewer .window-warn .window-remaining{color:#b45309}.gtfs-viewer .window-gone .window-remaining{color:#b91c1c}.gtfs-viewer .stat-row{display:flex;flex-wrap:wrap;gap:10px}.gtfs-viewer .stat{background:var(--panel);border:1px solid var(--border);border-radius:8px;padding:10px 14px;min-width:104px}.gtfs-viewer .stat-clickable{cursor:pointer}.gtfs-viewer .stat-clickable:hover{border-color:var(--accent)}.gtfs-viewer .stat-value{font-size:20px;font-weight:600;font-variant-numeric:tabular-nums}.gtfs-viewer .stat-label{font-size:12px;color:var(--muted)}.gtfs-viewer .agency-list{list-style:none;margin:0;padding:0}.gtfs-viewer .agency-list li{display:flex;align-items:baseline;gap:10px;padding:6px 0;border-bottom:1px solid var(--border)}.gtfs-viewer .agency-name{font-weight:600;font-size:13px}.gtfs-viewer .chip-row{display:flex;flex-wrap:wrap;gap:8px}.gtfs-viewer .chip{display:inline-flex;align-items:center;gap:6px;background:var(--chip-bg);border:1px solid var(--border);border-radius:999px;padding:4px 12px;font-size:12px;cursor:pointer}.gtfs-viewer .chip:hover{border-color:var(--accent)}.gtfs-viewer .chip-count{color:var(--muted);font-variant-numeric:tabular-nums}.gtfs-viewer .feature-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:8px}.gtfs-viewer .feature{border:1px solid var(--border);border-radius:8px;padding:8px 10px;background:var(--panel)}.gtfs-viewer .feature-off{opacity:.55}.gtfs-viewer .feature-name{font-size:13px;font-weight:600}.gtfs-viewer .feature-links{margin-top:4px;display:flex;flex-wrap:wrap;gap:2px 8px}.gtfs-viewer .feature-links .explore-link{margin-right:0}.gtfs-viewer .explore-table{width:100%;border-collapse:collapse;background:var(--panel);font-size:13px}.gtfs-viewer .explore-table th{text-align:left;font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);border-bottom:1px solid var(--border);padding:6px 8px}.gtfs-viewer .explore-table td{padding:7px 8px;border-bottom:1px solid var(--border);vertical-align:top}.gtfs-viewer .explore-table .numeric{text-align:right;font-variant-numeric:tabular-nums}.gtfs-viewer .pattern-name{font-weight:600}.gtfs-viewer .pattern-empty{opacity:.6}.gtfs-viewer .service-ids{max-width:260px;display:flex;flex-wrap:wrap;gap:2px 8px}.gtfs-viewer .service-ids .explore-link{margin-right:0;overflow-wrap:anywhere}.gtfs-viewer .explore-scroll{overflow-x:auto;max-width:100%}.gtfs-viewer .weekday-chart{display:flex;flex-direction:column;gap:3px}.gtfs-viewer .weekday-row{display:flex;align-items:center;gap:8px;font-size:12px}.gtfs-viewer .weekday-name{width:74px;color:var(--muted)}.gtfs-viewer .weekday-track{flex:1;background:var(--chip-bg);border-radius:4px;height:14px}.gtfs-viewer .weekday-bar{display:block;height:100%;border-radius:4px;background:var(--accent);min-width:2px}.gtfs-viewer .weekday-value{width:56px;text-align:right;font-variant-numeric:tabular-nums}.gtfs-viewer .gap-list{list-style:none;margin:0;padding:0;font-size:13px}.gtfs-viewer .gap-list li{padding:3px 0}.gtfs-viewer .calendar-months{display:grid;grid-template-columns:repeat(auto-fill,minmax(168px,1fr));gap:14px}.gtfs-viewer .month-label{font-size:12px;color:var(--muted);margin-bottom:4px}.gtfs-viewer .month-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}.gtfs-viewer .month-weekday{font-size:9px;color:var(--muted);text-align:center}.gtfs-viewer .day{aspect-ratio:1;display:flex;align-items:center;justify-content:center;border-radius:3px;font-size:9px;color:var(--muted);background:var(--chip-bg)}.gtfs-viewer .day-0{background:var(--chip-bg)}.gtfs-viewer .day-1{background:#cfe0fb;color:#1e3a8a}.gtfs-viewer .day-2{background:#9dc1f7;color:#1e3a8a}.gtfs-viewer .day-3{background:#5d93ee;color:#fff}.gtfs-viewer .day-4{background:#2563eb;color:#fff}.gtfs-viewer .day-blank{background:none}.gtfs-viewer .route-search{font:inherit;font-size:13px;padding:6px 10px;border:1px solid var(--border);border-radius:6px;flex:1 1 160px;min-width:0;max-width:320px}.gtfs-viewer .route-group{margin-top:18px}.gtfs-viewer .route-group-label{font-size:12px;color:var(--muted);margin-bottom:4px}.gtfs-viewer .route-row{cursor:pointer}.gtfs-viewer .route-row:hover{background:var(--accent-bg)}.gtfs-viewer .route-name{font-weight:600}.gtfs-viewer .route-badge{display:inline-block;min-width:34px;text-align:center;padding:2px 8px;border-radius:5px;font-weight:700;font-size:13px;background:var(--chip-bg)}.gtfs-viewer .route-title{display:flex;align-items:center;gap:10px}.gtfs-viewer .route-actions{display:flex;gap:4px;align-items:center}.gtfs-viewer .direction-list{display:flex;flex-wrap:wrap;gap:10px}.gtfs-viewer .direction{border:1px solid var(--border);border-radius:8px;padding:10px 12px;background:var(--panel);min-width:180px}.gtfs-viewer .direction-label{font-weight:600;font-size:13px}.gtfs-viewer .direction .numeric{font-variant-numeric:tabular-nums;margin:4px 0 6px;font-size:12px}.gtfs-viewer .pattern-list{display:flex;flex-direction:column;gap:6px}.gtfs-viewer details.pattern{border:1px solid var(--border);border-radius:8px;background:var(--panel);padding:8px 12px}.gtfs-viewer details.pattern summary{cursor:pointer}.gtfs-viewer .pattern-head{display:inline-flex;gap:10px;align-items:baseline}.gtfs-viewer .stop-sequence{margin:8px 0 0;padding-left:22px;font-size:12px}.gtfs-viewer .stop-sequence li{padding:1px 0}.gtfs-viewer .hour-chart{display:flex;align-items:flex-end;gap:2px;height:110px;padding-top:8px}.gtfs-viewer .hour{flex:1;display:flex;flex-direction:column;justify-content:flex-end;height:100%}.gtfs-viewer .hour-bar{display:block;background:var(--accent);border-radius:2px 2px 0 0;min-height:1px}.gtfs-viewer .hour-label{font-size:9px;color:var(--muted);text-align:center;height:12px}.gtfs-viewer .calendar-legend{display:flex;align-items:center;gap:4px;margin-bottom:10px;font-variant-numeric:tabular-nums}.gtfs-viewer .calendar-legend .day{width:14px;height:14px;aspect-ratio:auto}.gtfs-viewer .calendar-legend .explore-hint{margin-right:6px}.gtfs-viewer .month-grid .day{border:1px solid transparent;font:inherit;font-size:9px;padding:0;cursor:pointer}.gtfs-viewer .month-grid .day-blank{cursor:default}.gtfs-viewer .month-grid .day:hover{border-color:var(--accent)}.gtfs-viewer .day-selected{outline:2px solid var(--accent);outline-offset:1px}.gtfs-viewer .day-panel{border:1px solid var(--accent);border-radius:8px;background:var(--panel);padding:10px 12px;margin-top:14px}.gtfs-viewer .day-panel-head{display:flex;gap:10px;align-items:baseline;flex-wrap:wrap;margin-bottom:8px}.gtfs-viewer .day-routes{display:flex;flex-wrap:wrap;gap:4px;align-items:center}.gtfs-viewer .day-actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-top:10px}.gtfs-viewer .route-chip{font:inherit;font-size:12px;font-weight:600;border:1px solid var(--border);background:var(--chip-bg);border-radius:5px;padding:2px 8px;cursor:pointer}.gtfs-viewer .route-chip:hover{border-color:var(--accent)}.gtfs-viewer .agency-more{padding-top:8px;border-bottom:none}.gtfs-viewer .map-controls{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:12px;padding:8px 10px;border:1px solid var(--border);border-radius:8px;background:var(--panel);font-size:12px}.gtfs-viewer .pattern-headsign{font-size:12px;color:var(--text)}.gtfs-viewer .pattern-body{margin-top:8px}.gtfs-viewer .pattern-links{margin-top:6px}.gtfs-viewer [data-el=content][data-layout=above] [data-el=panes]{flex-direction:column}.gtfs-viewer [data-el=content][data-layout=above] [data-el=map-pane]{flex:0 0 32%;border-right:none;border-bottom:1px solid var(--border);order:-1}.gtfs-viewer [data-el=content][data-layout=above] [data-el=browse-pane]{border-right:none}.gtfs-viewer [data-el=content][data-layout=none] [data-el=map-pane]{display:none}.gtfs-viewer [data-el=mode-switch]{display:inline-flex;border:1px solid var(--border);border-radius:6px;overflow:hidden}.gtfs-viewer button.mode{border:none;border-radius:0;padding:5px 14px;font-size:13px;background:var(--panel);color:var(--muted)}.gtfs-viewer button.mode.active{background:var(--accent-bg);color:var(--accent);font-weight:600}.gtfs-viewer .date-chip{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:4px 6px 4px 12px;border:1px solid var(--accent);background:var(--accent-bg);border-radius:999px;font-size:13px}.gtfs-viewer [data-el=date-chip-day]{font-weight:600;color:var(--accent)}.gtfs-viewer .date-chip button{border:none;background:none;padding:2px 7px;line-height:1;color:var(--accent);border-radius:999px}.gtfs-viewer .date-chip button:hover{background:var(--panel)}.gtfs-viewer [data-el=date-clear]{font-size:16px}.gtfs-viewer [data-el=toolbar]{position:relative}.gtfs-viewer .date-menu{position:absolute;top:100%;left:12px;z-index:20;min-width:280px;background:var(--panel);border:1px solid var(--border);border-radius:8px;box-shadow:0 6px 24px #0000001f;padding:4px}.gtfs-viewer .date-menu-item{display:flex;justify-content:space-between;gap:16px;width:100%;border:none;background:none;text-align:left;padding:7px 10px;border-radius:6px;font-size:13px}.gtfs-viewer .date-menu-item:hover{background:var(--accent-bg);color:var(--accent)}.gtfs-viewer .date-menu-date{color:var(--muted);font-variant-numeric:tabular-nums}.gtfs-viewer .periods{display:flex;flex-direction:column;gap:4px}.gtfs-viewer .period-row{display:flex;align-items:center;gap:10px;font-size:12px}.gtfs-viewer .period-name{width:150px;flex-shrink:0;text-align:right;color:var(--muted);overflow-wrap:anywhere}.gtfs-viewer .period-track{position:relative;flex:1;min-width:0;height:18px;background:var(--chip-bg);border-radius:4px}.gtfs-viewer .period-bar{position:absolute;top:0;height:100%;border:none;border-radius:4px;background:var(--accent);cursor:pointer;padding:0;min-width:3px}.gtfs-viewer .period-bar:hover{background:#1d4ed8}.gtfs-viewer .period-days{width:62px;text-align:right;color:var(--muted);font-variant-numeric:tabular-nums}.gtfs-viewer .period-axis .period-track{background:none;height:auto}.gtfs-viewer .axis-start,.gtfs-viewer .axis-end{color:var(--muted);font-size:11px}.gtfs-viewer .axis-end{float:right}.gtfs-viewer .change-list{list-style:none;margin:0;padding:0;font-size:13px}.gtfs-viewer .change-list li{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;padding:5px 0;border-bottom:1px solid var(--border)}.gtfs-viewer .change-date{font-weight:600;font-variant-numeric:tabular-nums}.gtfs-viewer .day-choices{display:flex;flex-wrap:wrap;gap:8px;margin-top:16px}.gtfs-viewer .day-choice{display:flex;flex-direction:column;gap:2px;align-items:flex-start;padding:10px 14px;border:1px solid var(--border);border-radius:8px;background:var(--panel);text-align:left}.gtfs-viewer .day-choice:hover{border-color:var(--accent)}.gtfs-viewer .day-choice-label{font-weight:600;font-size:13px}.gtfs-viewer .day-choice-date{color:var(--muted);font-size:12px}.gtfs-viewer .timetable-scroll{overflow:auto;max-height:100%;border:1px solid var(--border);border-radius:8px;background:var(--panel)}.gtfs-viewer table.timetable{border-collapse:separate;border-spacing:0;font-size:12px;font-variant-numeric:tabular-nums}.gtfs-viewer table.timetable th,.gtfs-viewer table.timetable td{padding:4px 8px;border-bottom:1px solid var(--border);white-space:nowrap}.gtfs-viewer table.timetable thead th{position:sticky;top:0;z-index:2;background:var(--panel);border-bottom:1px solid var(--border)}.gtfs-viewer table.timetable .stop-head,.gtfs-viewer table.timetable .stop-cell{position:sticky;left:0;z-index:1;background:var(--panel);border-right:1px solid var(--border);text-align:left;min-width:170px}.gtfs-viewer table.timetable thead .stop-head{z-index:3}.gtfs-viewer .trip-head{cursor:pointer;font-weight:600;text-align:right}.gtfs-viewer .trip-head:hover{background:var(--accent-bg)}.gtfs-viewer .trip-head.selected{background:var(--accent-bg);color:var(--accent)}.gtfs-viewer .trip-block{font-weight:400;font-size:10px;color:var(--muted)}.gtfs-viewer .time-cell{text-align:right}.gtfs-viewer .time-linked{cursor:pointer}.gtfs-viewer .time-linked:hover{background:var(--accent-bg);color:var(--accent)}.gtfs-viewer .stop-name{border:none;background:none;padding:0;font:inherit;color:var(--text);text-align:left;cursor:pointer}.gtfs-viewer .stop-name:hover{color:var(--accent);text-decoration:underline}.gtfs-viewer .stop-cell a.explore-link{margin-left:8px;font-size:10.5px;color:var(--muted)}.gtfs-viewer .stop-cell a.explore-link:hover{color:var(--accent)}.gtfs-viewer .stop-cell.selected{background:var(--accent-bg)}.gtfs-viewer .stop-cell.selected .stop-name{color:var(--accent);font-weight:600}.gtfs-viewer .timetable-controls{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.gtfs-viewer .timetable-controls .raw-toggle{margin-left:auto}.gtfs-viewer .map-note{margin-top:8px}.gtfs-viewer .stop-mark{width:18px;height:18px;border-radius:50%;border:2px solid var(--accent);background:#2563eb2e;box-shadow:0 0 #2563eb80;animation:gtfs-stop-mark 1.4s ease-out 3}@keyframes gtfs-stop-mark{0%{box-shadow:0 0 #2563eb80}to{box-shadow:0 0 0 16px #2563eb00}}@media(prefers-reduced-motion:reduce){.gtfs-viewer .stop-mark{animation:none}}.gtfs-viewer .time-blank{color:var(--border)}.gtfs-viewer .timetable-grid-wrap{margin-top:14px;min-height:0}.gtfs-viewer .chip.active{background:var(--accent-bg);color:var(--accent);border-color:var(--accent)}.gtfs-viewer .facets{display:flex;flex-direction:column;gap:6px;margin-top:14px}.gtfs-viewer .facet-row{display:flex;align-items:baseline;flex-wrap:wrap;gap:6px}.gtfs-viewer .facet-label{width:110px;flex-shrink:0;font-size:11px;letter-spacing:.05em;text-transform:uppercase;color:var(--muted)}.gtfs-viewer .facet-chip{padding:3px 10px;font-size:12px}.gtfs-viewer .facet-chip.active{background:var(--accent);color:#fff;border-color:var(--accent)}.gtfs-viewer .facet-chip.active .chip-count{color:#fffc}.gtfs-viewer .facet-chip.facet-empty{opacity:.45}.gtfs-viewer .facet-summary{display:flex;align-items:baseline;flex-wrap:wrap;gap:10px;margin-top:12px;font-size:13px;font-weight:600}.gtfs-viewer .facet-summary .explore-hint{font-weight:400}
|
package/dist/map/layers.d.ts
CHANGED
|
@@ -16,6 +16,18 @@ export declare const EMPTY_FEATURE_COLLECTION: {
|
|
|
16
16
|
type: "FeatureCollection";
|
|
17
17
|
features: never[];
|
|
18
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* How stops are drawn, and how they are drawn when they are the subject.
|
|
21
|
+
*
|
|
22
|
+
* Scaled by zoom rather than hidden at low zoom: an inspection tool has to
|
|
23
|
+
* show where the stops are before you know where to zoom. The focused pair is
|
|
24
|
+
* exported beside the ordinary one so the map can swap between them without a
|
|
25
|
+
* second copy of either drifting out of step.
|
|
26
|
+
*/
|
|
27
|
+
export declare const STOP_RADIUS: (string | number | string[])[];
|
|
28
|
+
export declare const STOP_OPACITY = 0.45;
|
|
29
|
+
export declare const STOP_RADIUS_FOCUSED: (string | number | string[])[];
|
|
30
|
+
export declare const STOP_OPACITY_FOCUSED = 0.95;
|
|
19
31
|
export declare const SOURCE_IDS: readonly ["locations", "routes", "shapes", "stops", "highlight"];
|
|
20
32
|
export type SourceId = (typeof SOURCE_IDS)[number];
|
|
21
33
|
/**
|
package/dist/map/map.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Dom } from "../dom";
|
|
2
|
+
import type { FocusResult } from "../mount";
|
|
2
3
|
import type { FeatureCollection, ViewerSource } from "../sources/types";
|
|
3
4
|
import type { AppState } from "../state";
|
|
4
5
|
export declare class MapController {
|
|
@@ -25,13 +26,87 @@ export declare class MapController {
|
|
|
25
26
|
private pendingRefresh;
|
|
26
27
|
/** Layers the server thinned to fit, and by how much. */
|
|
27
28
|
private readonly simplifiedLayers;
|
|
29
|
+
/**
|
|
30
|
+
* The route features as drawn, kept for framing a focus.
|
|
31
|
+
*
|
|
32
|
+
* `querySourceFeatures` only returns what is currently in view, so framing
|
|
33
|
+
* on it cannot move the map to a route that is off screen - which is every
|
|
34
|
+
* route, the moment a feed covers more than one city. This is the same
|
|
35
|
+
* object MapLibre already holds, so keeping it costs nothing.
|
|
36
|
+
*/
|
|
37
|
+
private routeFeatures;
|
|
38
|
+
/** The stops as drawn, for framing a focus that has no lines to frame on. */
|
|
39
|
+
private stopFeatures;
|
|
40
|
+
/** The mark left by `focusStop`, so the next call can move it. */
|
|
41
|
+
private stopMark;
|
|
42
|
+
/**
|
|
43
|
+
* What is currently in focus, so a redraw can put it back.
|
|
44
|
+
*
|
|
45
|
+
* `isolate` is the difference between "this route and nothing else" and
|
|
46
|
+
* "this route, picked out against the rest of the network". Both keep the
|
|
47
|
+
* route emphasised; only the first hides everything around it.
|
|
48
|
+
*/
|
|
49
|
+
private focus;
|
|
28
50
|
private constructor();
|
|
51
|
+
/** See `MapHandle.resize`. */
|
|
52
|
+
resize(): void;
|
|
29
53
|
isVisible(): boolean;
|
|
30
54
|
/** Restore the saved preference; defaults to shown. */
|
|
31
55
|
restoreVisibility(): void;
|
|
32
56
|
setVisible(visible: boolean): void;
|
|
33
57
|
private setSourceData;
|
|
34
58
|
private fitTo;
|
|
59
|
+
/**
|
|
60
|
+
* Show these routes and nothing else, or go back to the whole network.
|
|
61
|
+
*
|
|
62
|
+
* The cheap half is a filter on the layer the feed is already drawn into:
|
|
63
|
+
* the `routes` source carries `route_id` on every feature, so narrowing to
|
|
64
|
+
* one costs a style update and no request, which is what makes it usable on
|
|
65
|
+
* a feed whose shapes took half a minute to arrive.
|
|
66
|
+
*
|
|
67
|
+
* That layer holds **one representative shape per route**, though, so for a
|
|
68
|
+
* single route the caller can pass the route's own shapes in `extra` and get
|
|
69
|
+
* all of its variants rather than one of them. A route whose trips name no
|
|
70
|
+
* shape at all has neither, which is why this reports how much it drew: an
|
|
71
|
+
* empty map with a panel claiming to show a route is worse than saying there
|
|
72
|
+
* is nothing to show.
|
|
73
|
+
*
|
|
74
|
+
* `shapes` holds the lines no route claims, and `stops` every stop in the
|
|
75
|
+
* feed. Neither can be filtered to a route - their features carry no
|
|
76
|
+
* route_id - so both are hidden while a focus is on, but only when the focus
|
|
77
|
+
* has something of its own to put there.
|
|
78
|
+
*/
|
|
79
|
+
focusRoutes(routeIds: string[] | null, extra?: FeatureCollection | null, isolate?: boolean, stopIds?: string[]): Promise<FocusResult>;
|
|
80
|
+
/**
|
|
81
|
+
* Put the current focus on the map, and say how many features it drew.
|
|
82
|
+
*
|
|
83
|
+
* Separate from `focusRoutes` because a redraw has to reapply it: `refresh`
|
|
84
|
+
* clears every source, and a focus set while the feed was still streaming
|
|
85
|
+
* would otherwise be wiped by the draw it was waiting for.
|
|
86
|
+
*/
|
|
87
|
+
private applyFocus;
|
|
88
|
+
/**
|
|
89
|
+
* Frame the map on what the focus is showing.
|
|
90
|
+
*
|
|
91
|
+
* `stopsOnly` frames on the focused stops instead of the route's line, for a
|
|
92
|
+
* caller that has narrowed to part of a route - one trip of it, say. The
|
|
93
|
+
* line is still drawn, but framing on it would hold the viewport over the
|
|
94
|
+
* whole route and a short working would move the map not at all, which reads
|
|
95
|
+
* as a click that did nothing.
|
|
96
|
+
*/
|
|
97
|
+
frameFocus(routeIds: string[], options?: {
|
|
98
|
+
stopsOnly?: boolean;
|
|
99
|
+
}): Promise<void>;
|
|
100
|
+
/**
|
|
101
|
+
* Centre on one stop and mark it, so a row in a table has a place on the map.
|
|
102
|
+
*
|
|
103
|
+
* The mark is a DOM marker rather than a layer: the `highlight` source is
|
|
104
|
+
* already carrying the focused route's own geometry, and borrowing it here
|
|
105
|
+
* would erase the route to show one of its stops. Returns whether the stop
|
|
106
|
+
* has a position at all - plenty of feeds have stops with no coordinates,
|
|
107
|
+
* and a reader deserves to be told rather than watch nothing happen.
|
|
108
|
+
*/
|
|
109
|
+
focusStop(stopId: string): Promise<boolean>;
|
|
35
110
|
highlight(collection: FeatureCollection, options?: {
|
|
36
111
|
point?: boolean;
|
|
37
112
|
}): Promise<void>;
|
|
@@ -46,8 +121,17 @@ export declare class MapController {
|
|
|
46
121
|
private drawLayer;
|
|
47
122
|
/** Names the layers the server had to reduce, once everything is drawn. */
|
|
48
123
|
private simplificationNote;
|
|
49
|
-
/**
|
|
124
|
+
/**
|
|
125
|
+
* Fetch and draw the whole feed. Skipped entirely while the map is hidden.
|
|
126
|
+
*
|
|
127
|
+
* Publishes itself as `pendingRefresh` for the whole of its run, so anything
|
|
128
|
+
* that draws - a highlight, a screen's focus - waits for it instead of being
|
|
129
|
+
* cleared by the pass it was racing. That the *application's* first draw was
|
|
130
|
+
* not published this way is what made a deep-linked route report "nothing to
|
|
131
|
+
* draw": it asked an empty layer and believed the answer.
|
|
132
|
+
*/
|
|
50
133
|
refresh(): Promise<void>;
|
|
134
|
+
private draw;
|
|
51
135
|
clear(): Promise<void>;
|
|
52
136
|
/**
|
|
53
137
|
* Give up the map and everything holding it.
|