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