refactor(street-level): static legend in markup; drop empty panel wrappers

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
German KhokhlovandClaude Opus 5.5 committed 2026-10-08 15:02:44 +04:00
1 parent 209dda5507
commit 7411ebaf1c
8 files changed
+56 -92

No files matched your search

+1 -12
View File
@@ -5,12 +5,11 @@ import { createCredit } from './credits.js';
import { createSelection } from './selection.js';
import { createViewerHost } from './viewerHost.js';
import { normalizeFilter, sameFilter } from './filter.js';
import { COLORS, STREET_LEVEL_LAYER_ID } from './policy.js';
import { STREET_LEVEL_LAYER_ID } from './policy.js';
import { createCoverage } from './providers/mapillary/coverage.js';
import { createSequences } from './providers/mapillary/sequences.js';
import { createMapillaryViewer } from './providers/mapillary/viewer.js';
import {
COLORS as MAPILLARY_COLORS,
MAPILLARY_CREDIT_HTML,
MAPILLARY_KEY_ID,
MAPILLARY_SOURCE_METHODS,
@@ -169,16 +168,6 @@ export function createStreetLevelLayer({
keyRejected,
filter: { ...state.filter },
coverage,
legend: state.providerOn
? [
{
key: 'mapillary',
label: 'Mapillary',
color: MAPILLARY_COLORS.coverage,
},
{ key: 'selected', label: 'Selected', color: COLORS.selected },
]
: [],
sequence: {
selectedId: state.sequence.selectedId,
images: state.sequence.images.length,
-1
View File
@@ -139,7 +139,6 @@ test('the CC BY-SA credit shows while Mapillary is on and goes with it', async (
assert.match(shown()[0], /CC BY-SA 4\.0/);
layer.setParams({ mapillary: false });
assert.deepEqual(shown(), [], 'hidden with the switch');
assert.deepEqual(layer.getUIState().legend, [], 'no legend either');
layer.setParams({ mapillary: true });
assert.deepEqual(shown(), [MAPILLARY_CREDIT_HTML]);
layer.disable();
+1 -19
View File
@@ -375,25 +375,7 @@ export class StreetLevelControls {
setAttr(el.sinceRange, 'aria-valuetext', view.since.label);
}
}
// Rebuild when the swatches change, not only their count.
const legendKey = view.legend
.map((entry) => `${entry.key}:${entry.color}:${entry.label}`)
.join('|');
if (el.legend && this._legendKey !== legendKey) {
this._legendKey = legendKey;
el.legend.replaceChildren(
...view.legend.map((entry) => {
const item = document.createElement('li');
const swatch = document.createElement('i');
swatch.className = 'sl-legend-swatch';
swatch.style.background = entry.color;
const label = document.createElement('span');
label.textContent = entry.label;
item.append(swatch, label);
return item;
}),
);
}
setProp(el.legend, 'hidden', !view.legend);
}
_renderViewer(view) {
+1 -5
View File
@@ -372,7 +372,6 @@ function uiState({
enabled = true,
on = true,
open = false,
legend,
pano = 'all',
renderMode = 'letterbox',
keyRequired = false,
@@ -384,10 +383,6 @@ function uiState({
keyRejected: false,
filter: { pano, sinceDays: 0 },
coverage: { count: 3, loading: false, hint: '', error: null },
legend: legend || [
{ key: 'mapillary', label: 'Mapillary', color: '#05cb63' },
{ key: 'selected', label: 'Selected', color: '#00d4ff' },
],
sequence: { selectedId: null, images: 0, loading: false },
street: {
open,
@@ -467,6 +462,7 @@ test('a layer that is on with Mapillary switched off reads OFF, and the pill bri
dom.root.querySelector('#sl-coverage-meta').textContent,
/Mapillary is off in this view/,
);
assert.equal(dom.root.querySelector('#sl-legend').hidden, true, 'no legend');
status.click();
await settle();
assert.deepEqual(calls.setParams, [
+1 -1
View File
@@ -121,7 +121,7 @@ export function presentStreetLevelPanel(state, { now = Date.now() } = {}) {
: state.street.error || state.coverage.error || null,
filter: { pano: filter.pano, sinceDays: Number(filter.sinceDays) || 0 },
since: presentSince(filter.sinceDays, now),
legend: state.legend || [],
legend: state.providerOn !== false,
viewer: presentViewer(state),
meta: presentMeta(state),
};
-4
View File
@@ -12,10 +12,6 @@ function snapshot(overrides = {}) {
keyRejected: false,
filter: { pano: 'all', sinceDays: 0 },
coverage: { loading: false, count: 0, hint: '', error: null },
legend: [
{ key: 'mapillary', label: 'Mapillary', color: '#05cb63' },
{ key: 'selected', label: 'Selected', color: '#00d4ff' },
],
sequence: { selectedId: null, images: 0, loading: false },
street: {
open: false,
+11 -8
View File
@@ -88,23 +88,16 @@
}
.sl-body,
.sl-main,
.sl-settings,
.sl-controls {
display: flex;
flex-direction: column;
gap: 8px;
}
.sl-body,
.sl-settings {
.sl-body {
min-height: 0;
}
.sl-main {
min-width: 0;
}
.sl-controls {
min-width: 0;
margin: 0;
@@ -222,11 +215,21 @@
color: var(--text-secondary);
}
.sl-legend[hidden] {
display: none;
}
.sl-legend-swatch {
width: 14px;
height: 3px;
border-radius: 2px;
display: inline-block;
background: var(--sl-green);
}
/* COLORS.selected in src/layers/streetLevel/policy.js */
.sl-legend-swatch.is-selected {
background: #00d4ff;
}
/* ── Embedded viewer ─────────────────────────────────────────────────────── */
+41 -42
View File
@@ -82,54 +82,53 @@
<button class="panel-collapse-btn" data-collapse-target="street-level-panel" title="Expand panel">◀</button>
</div>
<div id="sl-body" class="sl-body">
<div class="sl-main">
<div id="sl-viewer-wrap" class="sl-viewer-wrap" hidden>
<div class="sl-viewer-bar">
<div class="sl-viewer-tools">
<button id="sl-viewer-expand" class="scene-btn" type="button" aria-pressed="false" title="Show the street-level view full screen (Esc shrinks it)"><span class="sl-btn-icon" aria-hidden="true">⤢</span><span class="sl-btn-text">EXPAND</span></button>
<div class="sl-seg" role="radiogroup" aria-label="Image fit">
<button type="button" class="sl-seg-btn is-active" data-sl-render="letterbox" role="radio" aria-checked="true" title="Fit: show the whole photo, with dark bars where its shape differs from the frame">FIT</button>
<button type="button" class="sl-seg-btn" data-sl-render="fill" role="radio" aria-checked="false" tabindex="-1" title="Fill: fill the frame edge to edge, cropping the photo">FILL</button>
</div>
</div>
<div class="sl-viewer-tools sl-viewer-tools-right">
<button id="sl-viewer-close" class="sl-viewer-x" type="button" title="Close the image and deselect it on the map" aria-label="Close the street-level image">×</button>
<div id="sl-viewer-wrap" class="sl-viewer-wrap" hidden>
<div class="sl-viewer-bar">
<div class="sl-viewer-tools">
<button id="sl-viewer-expand" class="scene-btn" type="button" aria-pressed="false" title="Show the street-level view full screen (Esc shrinks it)"><span class="sl-btn-icon" aria-hidden="true">⤢</span><span class="sl-btn-text">EXPAND</span></button>
<div class="sl-seg" role="radiogroup" aria-label="Image fit">
<button type="button" class="sl-seg-btn is-active" data-sl-render="letterbox" role="radio" aria-checked="true" title="Fit: show the whole photo, with dark bars where its shape differs from the frame">FIT</button>
<button type="button" class="sl-seg-btn" data-sl-render="fill" role="radio" aria-checked="false" tabindex="-1" title="Fill: fill the frame edge to edge, cropping the photo">FILL</button>
</div>
</div>
<div class="sl-viewer-stage">
<div id="sl-viewer-placeholder" class="sl-viewer-placeholder" hidden></div>
<div id="sl-viewer" class="sl-viewer" aria-label="Street-level viewer"></div>
</div>
<div id="sl-image-meta" class="sl-image-meta">
<span id="sl-image-by" class="sl-image-by"></span>
<span class="sl-image-right">
<span id="sl-image-when" class="sl-image-when"></span>
<a id="sl-image-link" class="sl-image-link" href="#" target="_blank" rel="noopener noreferrer" title="Open this image on the provider's site" hidden></a>
</span>
<div class="sl-viewer-tools sl-viewer-tools-right">
<button id="sl-viewer-close" class="sl-viewer-x" type="button" title="Close the image and deselect it on the map" aria-label="Close the street-level image">×</button>
</div>
</div>
<div class="sl-settings">
<div id="sl-error" class="sl-error" role="alert" hidden><span aria-hidden="true">⚠</span> <span id="sl-error-text"></span></div>
<!-- A missing key gates the filters; the line above says how to add it. -->
<fieldset id="sl-controls" class="sl-controls">
<div class="sl-filters" role="group" aria-label="Imagery filters">
<span class="cctv-summary-label">IMAGERY</span>
<div class="sl-seg" role="radiogroup" aria-label="Panorama filter">
<button type="button" class="sl-seg-btn is-active" data-sl-pano="all" role="radio" aria-checked="true">ALL</button>
<button type="button" class="sl-seg-btn" data-sl-pano="pano" role="radio" aria-checked="false" tabindex="-1">360°</button>
<button type="button" class="sl-seg-btn" data-sl-pano="flat" role="radio" aria-checked="false" tabindex="-1">FLAT</button>
</div>
</div>
<div class="sl-since">
<label class="cctv-summary-label" for="sl-since">SINCE</label>
<input id="sl-since" class="sl-range" type="range" min="0" max="8" step="1" value="0" aria-valuetext="ANY DATE" aria-describedby="sl-since-label" />
<output id="sl-since-label" class="sl-since-label" for="sl-since">ANY DATE</output>
</div>
</fieldset>
<ul id="sl-legend" class="sl-legend" aria-label="Coverage colours"></ul>
<div id="sl-coverage-meta" class="sl-meta"></div>
<div class="sl-viewer-stage">
<div id="sl-viewer-placeholder" class="sl-viewer-placeholder" hidden></div>
<div id="sl-viewer" class="sl-viewer" aria-label="Street-level viewer"></div>
</div>
<div id="sl-image-meta" class="sl-image-meta">
<span id="sl-image-by" class="sl-image-by"></span>
<span class="sl-image-right">
<span id="sl-image-when" class="sl-image-when"></span>
<a id="sl-image-link" class="sl-image-link" href="#" target="_blank" rel="noopener noreferrer" title="Open this image on the provider's site" hidden></a>
</span>
</div>
</div>
<div id="sl-error" class="sl-error" role="alert" hidden><span aria-hidden="true">⚠</span> <span id="sl-error-text"></span></div>
<!-- A missing key gates the filters; the line above says how to add it. -->
<fieldset id="sl-controls" class="sl-controls">
<div class="sl-filters" role="group" aria-label="Imagery filters">
<span class="cctv-summary-label">IMAGERY</span>
<div class="sl-seg" role="radiogroup" aria-label="Panorama filter">
<button type="button" class="sl-seg-btn is-active" data-sl-pano="all" role="radio" aria-checked="true">ALL</button>
<button type="button" class="sl-seg-btn" data-sl-pano="pano" role="radio" aria-checked="false" tabindex="-1">360°</button>
<button type="button" class="sl-seg-btn" data-sl-pano="flat" role="radio" aria-checked="false" tabindex="-1">FLAT</button>
</div>
</div>
<div class="sl-since">
<label class="cctv-summary-label" for="sl-since">SINCE</label>
<input id="sl-since" class="sl-range" type="range" min="0" max="8" step="1" value="0" aria-valuetext="ANY DATE" aria-describedby="sl-since-label" />
<output id="sl-since-label" class="sl-since-label" for="sl-since">ANY DATE</output>
</div>
</fieldset>
<ul id="sl-legend" class="sl-legend" aria-label="Coverage colours">
<li><i class="sl-legend-swatch"></i><span>Mapillary</span></li>
<li><i class="sl-legend-swatch is-selected"></i><span>Selected</span></li>
</ul>
<div id="sl-coverage-meta" class="sl-meta"></div>
</div>
</div>
</div>