mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
style: format Layers panel components
This commit is contained in:
1 parent
07cf1695ff
commit
e34d619949
3 files changed
+158
-205
No files matched your search
+51
-156
@@ -10,47 +10,27 @@
|
||||
"src/data/localGeojsonCore.js",
|
||||
"src/data/localGeojsonLod.js"
|
||||
],
|
||||
"external": [
|
||||
"cesium"
|
||||
]
|
||||
"external": ["cesium"]
|
||||
},
|
||||
"application": {
|
||||
"exports": [
|
||||
"./application"
|
||||
],
|
||||
"modules": [
|
||||
"src/app/application.js"
|
||||
],
|
||||
"exports": ["./application"],
|
||||
"modules": ["src/app/application.js"],
|
||||
"external": []
|
||||
},
|
||||
"viewer": {
|
||||
"exports": [
|
||||
"./application/viewer"
|
||||
],
|
||||
"modules": [
|
||||
"src/app/viewer.js"
|
||||
],
|
||||
"external": [
|
||||
"cesium"
|
||||
]
|
||||
"exports": ["./application/viewer"],
|
||||
"modules": ["src/app/viewer.js"],
|
||||
"external": ["cesium"]
|
||||
},
|
||||
"vite-build": {
|
||||
"runtime": "node",
|
||||
"exports": [
|
||||
"./build/vite"
|
||||
],
|
||||
"modules": [
|
||||
"build/vite.js"
|
||||
],
|
||||
"external": [
|
||||
"vite-plugin-cesium"
|
||||
]
|
||||
"exports": ["./build/vite"],
|
||||
"modules": ["build/vite.js"],
|
||||
"external": ["vite-plugin-cesium"]
|
||||
},
|
||||
"live-providers": {
|
||||
"runtime": "node",
|
||||
"exports": [
|
||||
"./server/providers/live"
|
||||
],
|
||||
"exports": ["./server/providers/live"],
|
||||
"modules": [
|
||||
"server/providers/live.js",
|
||||
"server/providers/common/http.js",
|
||||
@@ -65,24 +45,16 @@
|
||||
"src/data/aisStreamAdapter.js",
|
||||
"src/data/aisWatchdog.js"
|
||||
],
|
||||
"external": [
|
||||
"ws"
|
||||
]
|
||||
"external": ["ws"]
|
||||
},
|
||||
"adsb-lol-source": {
|
||||
"exports": [
|
||||
"./sources/adsb-lol"
|
||||
],
|
||||
"modules": [
|
||||
"src/data/adsbLolFallback.js"
|
||||
],
|
||||
"exports": ["./sources/adsb-lol"],
|
||||
"modules": ["src/data/adsbLolFallback.js"],
|
||||
"external": []
|
||||
},
|
||||
"place-providers": {
|
||||
"runtime": "node",
|
||||
"exports": [
|
||||
"./server/providers/places"
|
||||
],
|
||||
"exports": ["./server/providers/places"],
|
||||
"modules": [
|
||||
"server/providers/places.js",
|
||||
"server/providers/places/google.js",
|
||||
@@ -97,19 +69,13 @@
|
||||
"external": []
|
||||
},
|
||||
"place-payloads": {
|
||||
"exports": [
|
||||
"./sources/places"
|
||||
],
|
||||
"modules": [
|
||||
"src/data/placeProviderPayloads.js"
|
||||
],
|
||||
"exports": ["./sources/places"],
|
||||
"modules": ["src/data/placeProviderPayloads.js"],
|
||||
"external": []
|
||||
},
|
||||
"space-providers": {
|
||||
"runtime": "node",
|
||||
"exports": [
|
||||
"./server/providers/space"
|
||||
],
|
||||
"exports": ["./server/providers/space"],
|
||||
"modules": [
|
||||
"server/providers/space.js",
|
||||
"server/providers/space/celestrak.js",
|
||||
@@ -120,19 +86,13 @@
|
||||
"external": []
|
||||
},
|
||||
"space-requests": {
|
||||
"exports": [
|
||||
"./sources/space"
|
||||
],
|
||||
"modules": [
|
||||
"src/data/spaceProviderRequests.js"
|
||||
],
|
||||
"exports": ["./sources/space"],
|
||||
"modules": ["src/data/spaceProviderRequests.js"],
|
||||
"external": []
|
||||
},
|
||||
"terrain-provider": {
|
||||
"runtime": "node",
|
||||
"exports": [
|
||||
"./server/providers/terrain"
|
||||
],
|
||||
"exports": ["./server/providers/terrain"],
|
||||
"modules": [
|
||||
"server/providers/terrain.js",
|
||||
"src/data/terrainHeightsProxy.js"
|
||||
@@ -140,50 +100,30 @@
|
||||
"external": []
|
||||
},
|
||||
"terrain-source": {
|
||||
"exports": [
|
||||
"./sources/terrain"
|
||||
],
|
||||
"modules": [
|
||||
"src/data/terrainHeightsProxy.js"
|
||||
],
|
||||
"exports": ["./sources/terrain"],
|
||||
"modules": ["src/data/terrainHeightsProxy.js"],
|
||||
"external": []
|
||||
},
|
||||
"traffic-provider": {
|
||||
"runtime": "node",
|
||||
"exports": [
|
||||
"./server/providers/traffic"
|
||||
],
|
||||
"modules": [
|
||||
"server/providers/traffic.js",
|
||||
"src/data/tomtomTiles.js"
|
||||
],
|
||||
"exports": ["./server/providers/traffic"],
|
||||
"modules": ["server/providers/traffic.js", "src/data/tomtomTiles.js"],
|
||||
"external": []
|
||||
},
|
||||
"traffic-source": {
|
||||
"exports": [
|
||||
"./sources/traffic"
|
||||
],
|
||||
"modules": [
|
||||
"src/data/tomtomTiles.js"
|
||||
],
|
||||
"exports": ["./sources/traffic"],
|
||||
"modules": ["src/data/tomtomTiles.js"],
|
||||
"external": []
|
||||
},
|
||||
"firms-provider": {
|
||||
"runtime": "node",
|
||||
"exports": [
|
||||
"./server/providers/firms"
|
||||
],
|
||||
"modules": [
|
||||
"server/providers/firms.js",
|
||||
"src/data/firmsCsv.js"
|
||||
],
|
||||
"exports": ["./server/providers/firms"],
|
||||
"modules": ["server/providers/firms.js", "src/data/firmsCsv.js"],
|
||||
"external": []
|
||||
},
|
||||
"gbfs-provider": {
|
||||
"runtime": "node",
|
||||
"exports": [
|
||||
"./server/providers/gbfs"
|
||||
],
|
||||
"exports": ["./server/providers/gbfs"],
|
||||
"modules": [
|
||||
"server/providers/gbfs.js",
|
||||
"server/providers/common/http.js",
|
||||
@@ -192,19 +132,13 @@
|
||||
"external": []
|
||||
},
|
||||
"gbfs-source": {
|
||||
"exports": [
|
||||
"./sources/gbfs"
|
||||
],
|
||||
"modules": [
|
||||
"src/data/gbfsSource.js"
|
||||
],
|
||||
"exports": ["./sources/gbfs"],
|
||||
"modules": ["src/data/gbfsSource.js"],
|
||||
"external": []
|
||||
},
|
||||
"cctv-provider": {
|
||||
"runtime": "node",
|
||||
"exports": [
|
||||
"./server/providers/cctv"
|
||||
],
|
||||
"exports": ["./server/providers/cctv"],
|
||||
"modules": [
|
||||
"server/providers/cctv.js",
|
||||
"server/providers/cctv/catalog.js",
|
||||
@@ -221,9 +155,7 @@
|
||||
},
|
||||
"radio-provider": {
|
||||
"runtime": "node",
|
||||
"exports": [
|
||||
"./server/providers/radio"
|
||||
],
|
||||
"exports": ["./server/providers/radio"],
|
||||
"modules": [
|
||||
"server/providers/radio.js",
|
||||
"server/providers/radio/catalog.js",
|
||||
@@ -237,9 +169,7 @@
|
||||
},
|
||||
"overpass-provider": {
|
||||
"runtime": "node",
|
||||
"exports": [
|
||||
"./server/providers/overpass"
|
||||
],
|
||||
"exports": ["./server/providers/overpass"],
|
||||
"modules": [
|
||||
"server/providers/common/geo.js",
|
||||
"server/providers/common/http.js",
|
||||
@@ -258,9 +188,7 @@
|
||||
},
|
||||
"military-installations-provider": {
|
||||
"runtime": "node",
|
||||
"exports": [
|
||||
"./server/providers/military-installations"
|
||||
],
|
||||
"exports": ["./server/providers/military-installations"],
|
||||
"modules": [
|
||||
"server/providers/common/http.js",
|
||||
"server/providers/common/query.js",
|
||||
@@ -277,9 +205,7 @@
|
||||
},
|
||||
"regional-provider": {
|
||||
"runtime": "node",
|
||||
"exports": [
|
||||
"./server/providers/regional"
|
||||
],
|
||||
"exports": ["./server/providers/regional"],
|
||||
"modules": [
|
||||
"server/providers/common/http.js",
|
||||
"server/providers/common/query.js",
|
||||
@@ -298,9 +224,7 @@
|
||||
},
|
||||
"openai-provider": {
|
||||
"runtime": "node",
|
||||
"exports": [
|
||||
"./server/providers/openai"
|
||||
],
|
||||
"exports": ["./server/providers/openai"],
|
||||
"modules": [
|
||||
"server/providers/common/rate-limit.js",
|
||||
"server/providers/common/request.js",
|
||||
@@ -320,9 +244,7 @@
|
||||
},
|
||||
"standalone-key-setup": {
|
||||
"runtime": "node",
|
||||
"exports": [
|
||||
"./server/standalone/key-setup"
|
||||
],
|
||||
"exports": ["./server/standalone/key-setup"],
|
||||
"modules": [
|
||||
"scripts/pinokio-environment.mjs",
|
||||
"server/providers/common/source-root.js",
|
||||
@@ -333,9 +255,7 @@
|
||||
"external": []
|
||||
},
|
||||
"place-search": {
|
||||
"exports": [
|
||||
"./search"
|
||||
],
|
||||
"exports": ["./search"],
|
||||
"modules": [
|
||||
"src/search/index.js",
|
||||
"src/search/placeSearch.js",
|
||||
@@ -345,27 +265,17 @@
|
||||
"external": []
|
||||
},
|
||||
"panel-disclosure": {
|
||||
"exports": [
|
||||
"./ui/panels"
|
||||
],
|
||||
"modules": [
|
||||
"src/ui/panelDisclosure.js"
|
||||
],
|
||||
"exports": ["./ui/panels"],
|
||||
"modules": ["src/ui/panelDisclosure.js"],
|
||||
"external": []
|
||||
},
|
||||
"surface-keyboard": {
|
||||
"exports": [
|
||||
"./ui/surfaces"
|
||||
],
|
||||
"modules": [
|
||||
"src/ui/surfaceKeyboard.js"
|
||||
],
|
||||
"exports": ["./ui/surfaces"],
|
||||
"modules": ["src/ui/surfaceKeyboard.js"],
|
||||
"external": []
|
||||
},
|
||||
"panel-rail-layout": {
|
||||
"exports": [
|
||||
"./ui/layout"
|
||||
],
|
||||
"exports": ["./ui/layout"],
|
||||
"modules": [
|
||||
"src/ui/panelRails.js",
|
||||
"src/ui/leftPanelRail.js",
|
||||
@@ -377,9 +287,7 @@
|
||||
"external": []
|
||||
},
|
||||
"visual-input": {
|
||||
"exports": [
|
||||
"./ui/input"
|
||||
],
|
||||
"exports": ["./ui/input"],
|
||||
"modules": [
|
||||
"src/ui/visualInput.js",
|
||||
"src/ui/applicationShortcuts.js",
|
||||
@@ -388,19 +296,12 @@
|
||||
"external": []
|
||||
},
|
||||
"display-controls": {
|
||||
"exports": [
|
||||
"./ui/display"
|
||||
],
|
||||
"modules": [
|
||||
"src/ui/displayControls.js"
|
||||
],
|
||||
"exports": ["./ui/display"],
|
||||
"modules": ["src/ui/displayControls.js"],
|
||||
"external": []
|
||||
},
|
||||
"visual-effects": {
|
||||
"exports": [
|
||||
"./ui/effects",
|
||||
"./ui/effects/bloom"
|
||||
],
|
||||
"exports": ["./ui/effects", "./ui/effects/bloom"],
|
||||
"modules": [
|
||||
"src/ui/effects.js",
|
||||
"src/ui/visualEffects.js",
|
||||
@@ -413,14 +314,10 @@
|
||||
"src/styles/noir.js",
|
||||
"src/styles/snow.js"
|
||||
],
|
||||
"external": [
|
||||
"cesium"
|
||||
]
|
||||
"external": ["cesium"]
|
||||
},
|
||||
"map-source-controls": {
|
||||
"exports": [
|
||||
"./ui/maps"
|
||||
],
|
||||
"exports": ["./ui/maps"],
|
||||
"modules": [
|
||||
"src/ui/mapSource.js",
|
||||
"src/ui/mapSourceControls.js",
|
||||
@@ -430,9 +327,7 @@
|
||||
"external": []
|
||||
},
|
||||
"layer-panel": {
|
||||
"exports": [
|
||||
"./ui/layers"
|
||||
],
|
||||
"exports": ["./ui/layers"],
|
||||
"modules": [
|
||||
"src/ui/layers.js",
|
||||
"src/ui/layerPanel.js",
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
/** Keep the clear action focusable while busy; the caller owns its transaction. */
|
||||
export function bindClearLayersControl(button, clear) {
|
||||
let destroyed = false;
|
||||
const click = () => { if (!destroyed) void clear(); };
|
||||
const click = () => {
|
||||
if (!destroyed) void clear();
|
||||
};
|
||||
button?.addEventListener('click', click);
|
||||
return {
|
||||
setBusy(busy) {
|
||||
if (destroyed || !button) return;
|
||||
button.setAttribute('aria-disabled', String(busy));
|
||||
button.setAttribute('aria-busy', String(busy));
|
||||
button.setAttribute('aria-label', busy ? 'Clearing selected data layers' : 'Clear selected data layers');
|
||||
button.setAttribute(
|
||||
'aria-label',
|
||||
busy ? 'Clearing selected data layers' : 'Clear selected data layers',
|
||||
);
|
||||
},
|
||||
destroy() {
|
||||
destroyed = true;
|
||||
|
||||
+100
-47
@@ -15,16 +15,21 @@ const FEED_STATE_LABELS = Object.freeze({
|
||||
*/
|
||||
export function layerFeedState(stats = {}) {
|
||||
const state = stats || {};
|
||||
const status = typeof state.status === 'string' ? state.status.toLowerCase() : '';
|
||||
const status =
|
||||
typeof state.status === 'string' ? state.status.toLowerCase() : '';
|
||||
const source = `${state.source || ''} ${state.coverage || ''}`;
|
||||
const hasExplicitFallback = typeof state.fallback === 'boolean';
|
||||
const hasPriorData = Number(state.count) > 0 || Boolean(state.lastUpdate);
|
||||
const presentedError = state.error || state.lastError || state.managerRefreshError;
|
||||
if (['unavailable', 'offline', 'down', 'error'].includes(status)) return 'unavailable';
|
||||
const presentedError =
|
||||
state.error || state.lastError || state.managerRefreshError;
|
||||
if (['unavailable', 'offline', 'down', 'error'].includes(status))
|
||||
return 'unavailable';
|
||||
if (
|
||||
(presentedError || state.unavailable === true || state.available === false)
|
||||
&& !hasPriorData
|
||||
&& !GUIDANCE_STATUSES.includes(status)
|
||||
(presentedError ||
|
||||
state.unavailable === true ||
|
||||
state.available === false) &&
|
||||
!hasPriorData &&
|
||||
!GUIDANCE_STATUSES.includes(status)
|
||||
) {
|
||||
return 'unavailable';
|
||||
}
|
||||
@@ -37,29 +42,37 @@ export function layerFeedState(stats = {}) {
|
||||
return state.stale ? 'stale' : 'nominal';
|
||||
}
|
||||
if (
|
||||
state.fallback === true
|
||||
|| status === 'fallback'
|
||||
|| state.mode === 'sim'
|
||||
|| /\bfallback\b/i.test(source)
|
||||
|| (!hasExplicitFallback && /\badsb\.lol\b/i.test(source))
|
||||
state.fallback === true ||
|
||||
status === 'fallback' ||
|
||||
state.mode === 'sim' ||
|
||||
/\bfallback\b/i.test(source) ||
|
||||
(!hasExplicitFallback && /\badsb\.lol\b/i.test(source))
|
||||
) {
|
||||
return 'fallback';
|
||||
}
|
||||
if (state.stale || status === 'stale') return 'stale';
|
||||
if (
|
||||
state.degraded
|
||||
|| presentedError
|
||||
|| state.unavailable === true
|
||||
|| state.available === false
|
||||
) return 'degraded';
|
||||
state.degraded ||
|
||||
presentedError ||
|
||||
state.unavailable === true ||
|
||||
state.available === false
|
||||
)
|
||||
return 'degraded';
|
||||
return 'nominal';
|
||||
}
|
||||
|
||||
|
||||
/** Layer row presentation over supplied state and actions; no layer imports. */
|
||||
export class LayerPanel {
|
||||
constructor({ getLayers, isEnabled, setEnabled, setLayerParams, getRowControls,
|
||||
hasRowControls, subscribeRowControls, onHiddenRefresh = () => {} }) {
|
||||
constructor({
|
||||
getLayers,
|
||||
isEnabled,
|
||||
setEnabled,
|
||||
setLayerParams,
|
||||
getRowControls,
|
||||
hasRowControls,
|
||||
subscribeRowControls,
|
||||
onHiddenRefresh = () => {},
|
||||
}) {
|
||||
this.getAll = getLayers;
|
||||
this.isEnabled = isEnabled;
|
||||
this.setEnabled = setEnabled;
|
||||
@@ -123,7 +136,9 @@ export class LayerPanel {
|
||||
|
||||
const count = document.createElement('span');
|
||||
count.className = 'data-count';
|
||||
count.textContent = layer.stats.count ? this._formatCount(layer.stats.count) : '—';
|
||||
count.textContent = layer.stats.count
|
||||
? this._formatCount(layer.stats.count)
|
||||
: '—';
|
||||
|
||||
const toggle = document.createElement('button');
|
||||
toggle.type = 'button';
|
||||
@@ -133,16 +148,24 @@ export class LayerPanel {
|
||||
// Native `disabled` immediately evicts keyboard focus in Chromium. Keep
|
||||
// the lifecycle control focusable while it is busy, and enforce the
|
||||
// same single-flight interaction contract through ARIA instead.
|
||||
if (this._destroyed || this._generation !== generation || toggle.getAttribute('aria-disabled') === 'true') return;
|
||||
if (
|
||||
this._destroyed ||
|
||||
this._generation !== generation ||
|
||||
toggle.getAttribute('aria-disabled') === 'true'
|
||||
)
|
||||
return;
|
||||
toggle.setAttribute('aria-disabled', 'true');
|
||||
toggle.setAttribute('aria-busy', 'true');
|
||||
try {
|
||||
await this.setEnabled(layer.id, !this.isEnabled(layer.id), { origin: 'user' });
|
||||
await this.setEnabled(layer.id, !this.isEnabled(layer.id), {
|
||||
origin: 'user',
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn(`[Data] ${layer.id} toggle error:`, error);
|
||||
} finally {
|
||||
const current = this.getAll().find(({ id }) => id === layer.id);
|
||||
if (!this._destroyed && current && this._generation === generation) this._syncToggleButton(toggle, current);
|
||||
if (!this._destroyed && current && this._generation === generation)
|
||||
this._syncToggleButton(toggle, current);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -165,7 +188,9 @@ export class LayerPanel {
|
||||
// A layer whose controls settle asynchronously (a chunked catalog load
|
||||
// that can also fail) pushes a re-render through this; nothing else
|
||||
// would repaint the row before its next scheduled refresh.
|
||||
const unsubscribe = this.subscribeRowControls(layer.id, () => this._refreshTogglePanel());
|
||||
const unsubscribe = this.subscribeRowControls(layer.id, () =>
|
||||
this._refreshTogglePanel(),
|
||||
);
|
||||
if (unsubscribe) this._removers.push(unsubscribe);
|
||||
const controls = document.createElement('div');
|
||||
controls.className = 'data-toggle-controls';
|
||||
@@ -174,9 +199,11 @@ export class LayerPanel {
|
||||
if (!button || button.disabled) return;
|
||||
// Re-read the live descriptor rather than trusting the rendered
|
||||
// chip, so a stale row can never apply an inverted toggle.
|
||||
const chip = this._rowControlsFor(layer.id)?.chips
|
||||
?.find((entry) => entry.id === button.dataset.chipId);
|
||||
if (chip?.params) this.setLayerParams(layer.id, chip.params, { origin: 'user' });
|
||||
const chip = this._rowControlsFor(layer.id)?.chips?.find(
|
||||
(entry) => entry.id === button.dataset.chipId,
|
||||
);
|
||||
if (chip?.params)
|
||||
this.setLayerParams(layer.id, chip.params, { origin: 'user' });
|
||||
});
|
||||
row.appendChild(controls);
|
||||
this._syncRowControls(controls, layer);
|
||||
@@ -215,7 +242,10 @@ export class LayerPanel {
|
||||
container.hidden = chips.length === 0 && legend.length === 0;
|
||||
|
||||
for (const node of [...container.children]) {
|
||||
if (String(node.className).split(/\s+/).includes('data-toggle-legend-item')) node.remove();
|
||||
if (
|
||||
String(node.className).split(/\s+/).includes('data-toggle-legend-item')
|
||||
)
|
||||
node.remove();
|
||||
}
|
||||
|
||||
const stale = new Map();
|
||||
@@ -265,7 +295,9 @@ export class LayerPanel {
|
||||
return;
|
||||
}
|
||||
for (const layer of this.getAll()) {
|
||||
const row = this._toggleContainer.querySelector(`[data-layer-id="${layer.id}"]`);
|
||||
const row = this._toggleContainer.querySelector(
|
||||
`[data-layer-id="${layer.id}"]`,
|
||||
);
|
||||
if (!row) continue;
|
||||
|
||||
const btn = row.querySelector('.data-toggle-btn');
|
||||
@@ -275,7 +307,9 @@ export class LayerPanel {
|
||||
|
||||
const count = row.querySelector('.data-count');
|
||||
if (count) {
|
||||
count.textContent = layer.stats.count ? this._formatCount(layer.stats.count) : '—';
|
||||
count.textContent = layer.stats.count
|
||||
? this._formatCount(layer.stats.count)
|
||||
: '—';
|
||||
}
|
||||
|
||||
const meta = row.querySelector('.data-toggle-meta');
|
||||
@@ -292,14 +326,16 @@ export class LayerPanel {
|
||||
const feedState = layerFeedState(stats);
|
||||
const stateLabel = FEED_STATE_LABELS[feedState];
|
||||
const source = stats.source || layer.source;
|
||||
const lifecycleState = layer.lifecycleState || (layer.enabled ? 'enabled' : 'disabled');
|
||||
const lifecycleState =
|
||||
layer.lifecycleState || (layer.enabled ? 'enabled' : 'disabled');
|
||||
if (lifecycleState === 'enabling' || lifecycleState === 'disabling') {
|
||||
return `${lifecycleState.toUpperCase()} · ${source}`;
|
||||
}
|
||||
if (layer.lifecycleUncertain) {
|
||||
return `UNCERTAIN · ${source} · lifecycle state requires reconciliation`;
|
||||
}
|
||||
const presentedError = stats.error || stats.lastError || stats.managerRefreshError;
|
||||
const presentedError =
|
||||
stats.error || stats.lastError || stats.managerRefreshError;
|
||||
if (presentedError) {
|
||||
if (typeof stats.retryInSec === 'number' && stats.retryInSec > 0) {
|
||||
return `${stateLabel} · ${source} · ${presentedError} · retry ${stats.retryInSec}s`;
|
||||
@@ -308,27 +344,33 @@ export class LayerPanel {
|
||||
}
|
||||
// A guidance status carries its prompt in `statusMessage`, not `error`, so
|
||||
// the row still tells the operator what to do without reporting a fault.
|
||||
if (GUIDANCE_STATUSES.includes(String(stats.status || '').toLowerCase())
|
||||
&& typeof stats.statusMessage === 'string' && stats.statusMessage.trim()) {
|
||||
if (
|
||||
GUIDANCE_STATUSES.includes(String(stats.status || '').toLowerCase()) &&
|
||||
typeof stats.statusMessage === 'string' &&
|
||||
stats.statusMessage.trim()
|
||||
) {
|
||||
return `${source} · ${stats.statusMessage.trim()}`;
|
||||
}
|
||||
const ago = stats.lastUpdate ? this._timeAgo(stats.lastUpdate) : 'never';
|
||||
if (stats.loading) {
|
||||
const loadingLabel = typeof stats.loadingLabel === 'string' && stats.loadingLabel.trim()
|
||||
? stats.loadingLabel.trim()
|
||||
: 'loading...';
|
||||
const loadingLabel =
|
||||
typeof stats.loadingLabel === 'string' && stats.loadingLabel.trim()
|
||||
? stats.loadingLabel.trim()
|
||||
: 'loading...';
|
||||
return `${source} · ${loadingLabel}`;
|
||||
}
|
||||
if (feedState === 'fallback') {
|
||||
const detail = typeof stats.loadingLabel === 'string' && stats.loadingLabel.trim()
|
||||
? stats.loadingLabel.trim()
|
||||
: (stats.coverage || ago);
|
||||
const detail =
|
||||
typeof stats.loadingLabel === 'string' && stats.loadingLabel.trim()
|
||||
? stats.loadingLabel.trim()
|
||||
: stats.coverage || ago;
|
||||
return `${stateLabel} · ${source} · ${detail}`;
|
||||
}
|
||||
if (feedState === 'stale') {
|
||||
const retry = typeof stats.retryInSec === 'number' && stats.retryInSec > 0
|
||||
? ` · retrying in ${stats.retryInSec}s`
|
||||
: '';
|
||||
const retry =
|
||||
typeof stats.retryInSec === 'number' && stats.retryInSec > 0
|
||||
? ` · retrying in ${stats.retryInSec}s`
|
||||
: '';
|
||||
return `${stateLabel} · ${source} · ${ago}${retry}`;
|
||||
}
|
||||
if (typeof stats.loadingLabel === 'string' && stats.loadingLabel.trim()) {
|
||||
@@ -339,7 +381,9 @@ export class LayerPanel {
|
||||
|
||||
_syncToggleButton(button, layer) {
|
||||
const feedState = layer.enabled ? layerFeedState(layer.stats) : 'off';
|
||||
const transitioning = layer.lifecycleState === 'enabling' || layer.lifecycleState === 'disabling';
|
||||
const transitioning =
|
||||
layer.lifecycleState === 'enabling' ||
|
||||
layer.lifecycleState === 'disabling';
|
||||
const uncertain = Boolean(layer.lifecycleUncertain);
|
||||
button.classList.toggle('active', layer.enabled);
|
||||
button.classList.toggle('transitioning', transitioning);
|
||||
@@ -347,11 +391,16 @@ export class LayerPanel {
|
||||
button.classList.toggle('disabling', layer.lifecycleState === 'disabling');
|
||||
button.classList.toggle('lifecycle-uncertain', uncertain);
|
||||
for (const state of Object.keys(FEED_STATE_LABELS)) {
|
||||
button.classList.toggle(`feed-${state}`, layer.enabled && !uncertain && feedState === state);
|
||||
button.classList.toggle(
|
||||
`feed-${state}`,
|
||||
layer.enabled && !uncertain && feedState === state,
|
||||
);
|
||||
}
|
||||
button.dataset.feedState = transitioning
|
||||
? layer.lifecycleState
|
||||
: (uncertain ? 'uncertain' : feedState);
|
||||
: uncertain
|
||||
? 'uncertain'
|
||||
: feedState;
|
||||
// A busy toggle remains the keyboard focus owner. `aria-disabled` plus the
|
||||
// click guard above prevents repeat activation without the focus loss caused
|
||||
// by native `disabled`.
|
||||
@@ -360,7 +409,11 @@ export class LayerPanel {
|
||||
button.setAttribute('aria-busy', String(transitioning));
|
||||
button.textContent = transitioning
|
||||
? layer.lifecycleState.toUpperCase()
|
||||
: (uncertain ? 'UNCERTAIN' : (layer.enabled ? FEED_STATE_LABELS[feedState] : 'OFF'));
|
||||
: uncertain
|
||||
? 'UNCERTAIN'
|
||||
: layer.enabled
|
||||
? FEED_STATE_LABELS[feedState]
|
||||
: 'OFF';
|
||||
button.setAttribute('aria-label', `${layer.name}: ${button.textContent}`);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user