mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
merge: refresh file import integration against main
This commit is contained in:
commit
4092c2e9a2
106 files changed
+11089
-1389
No files matched your search
@@ -704,6 +704,51 @@ test('narrow-screen rails pin every hosted panel glow inside its panel box', ()
|
||||
}
|
||||
});
|
||||
|
||||
test('right layout ignores a CSS-hidden expanded panel when deciding exclusivity', () => {
|
||||
const f = fixture('right');
|
||||
const weather = element('weather-panel', { height: 300 });
|
||||
weather.computed.display = 'none';
|
||||
f.stack.children.push(weather);
|
||||
weather.parentElement = f.stack;
|
||||
|
||||
f.run();
|
||||
|
||||
assert.equal(f.stack.classList.contains('layout-exclusive'), false);
|
||||
assert.equal(f.stack.dataset.expandedCount, '0');
|
||||
assert.equal(f.first.getAttribute('aria-hidden'), undefined);
|
||||
assert.equal(f.second.getAttribute('aria-hidden'), undefined);
|
||||
assert.equal(
|
||||
weather.style.getPropertyValue('--right-panel-allocated-height'),
|
||||
'',
|
||||
);
|
||||
});
|
||||
|
||||
test('right layout still restores panels that exclusive presentation hides', () => {
|
||||
const f = fixture('right');
|
||||
// Model layers.css: an exclusive rail hides its collapsed panels.
|
||||
f.options.windowRef.getComputedStyle = (node) => ({
|
||||
...node.computed,
|
||||
display:
|
||||
f.stack.classList.contains('layout-exclusive') &&
|
||||
node.classList.contains('collapsed')
|
||||
? 'none'
|
||||
: node.computed.display,
|
||||
});
|
||||
f.expand(f.first, 250);
|
||||
f.second.classList.add('layout-auto-collapsed');
|
||||
f.stack.classList.add('layout-exclusive');
|
||||
|
||||
f.run();
|
||||
assert.equal(f.stack.classList.contains('layout-exclusive'), true);
|
||||
assert.equal(f.second.getAttribute('aria-hidden'), 'true');
|
||||
|
||||
f.options.hud.visible = false;
|
||||
f.run();
|
||||
assert.equal(f.second.classList.contains('collapsed'), false);
|
||||
assert.equal(f.second.classList.contains('layout-auto-collapsed'), false);
|
||||
assert.deepEqual(f.collapsed, ['second']);
|
||||
});
|
||||
|
||||
test('right layout ignores a hidden panel: no lane, no gap, no auto-collapse', () => {
|
||||
const f = fixture('right');
|
||||
const imagery = element('recent-imagery-panel', { height: 0 });
|
||||
|
||||
@@ -69,10 +69,17 @@ export function layoutRightPanelRail({
|
||||
// A collapse may schedule one follow-up, which only measures and allocates.
|
||||
const isCollapseRetry = pendingCollapseRetries.delete(stack);
|
||||
|
||||
// A floating (lifted-out) panel is not laid out by the rail.
|
||||
// A floating (lifted-out) panel is not laid out by the rail. Neither is one
|
||||
// that other CSS hides, such as an event view. Exclusive presentation hides
|
||||
// collapsed panels itself; those stay in the rail so they can be restored.
|
||||
const hiddenByRail = (panel) =>
|
||||
stack.classList.contains('layout-exclusive') &&
|
||||
panel.classList.contains('collapsed');
|
||||
const panels = [...stack.children].filter(
|
||||
(panel) =>
|
||||
panel.matches('[data-panel-id]:not(.panel-floating)') && !panel.hidden,
|
||||
panel.matches('[data-panel-id]:not(.panel-floating)') &&
|
||||
!panel.hidden &&
|
||||
(hiddenByRail(panel) || getComputedStyle(panel).display !== 'none'),
|
||||
);
|
||||
if (!hud.visible || hud.variant !== 'tactical') {
|
||||
for (const panel of panels.filter((item) =>
|
||||
|
||||
@@ -73,6 +73,7 @@ export class StreetLevelControls {
|
||||
sinceRow: byId('sl-since-row'),
|
||||
sinceRange: byId('sl-since'),
|
||||
sinceLabel: byId('sl-since-label'),
|
||||
sinceNote: byId('sl-since-note'),
|
||||
legend: byId('sl-legend'),
|
||||
followBtn: byId('sl-follow-btn'),
|
||||
viewerWrap: byId('sl-viewer-wrap'),
|
||||
@@ -527,6 +528,9 @@ export class StreetLevelControls {
|
||||
const shows = view.filterControls || { pano: true, since: true };
|
||||
setProp(el.panoRow, 'hidden', !shows.pano);
|
||||
setProp(el.sinceRow, 'hidden', !shows.since);
|
||||
// A lit source that dates by upload says so under the slider.
|
||||
setProp(el.sinceNote, 'textContent', view.sinceNote || '');
|
||||
setProp(el.sinceNote, 'hidden', !view.sinceNote);
|
||||
setProp(el.controls, 'hidden', !shows.pano && !shows.since);
|
||||
setProp(el.legend, 'hidden', view.legend.length === 0);
|
||||
this._renderRadios(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import test from 'node:test';
|
||||
import { StreetLevelControls } from './streetLevelControls.js';
|
||||
import { createStreetLevelLayer } from '../layers/streetLevel/index.js';
|
||||
@@ -313,6 +314,7 @@ function panelDom() {
|
||||
const sinceRow = add(controls, 'div', { id: 'sl-since-row' });
|
||||
add(sinceRow, 'input', { id: 'sl-since' });
|
||||
add(sinceRow, 'output', { id: 'sl-since-label' });
|
||||
add(sinceRow, 'small', { id: 'sl-since-note' });
|
||||
add(settings, 'ul', { id: 'sl-legend' });
|
||||
const metaRow = add(settings, 'div', { classes: ['sl-meta-row'] });
|
||||
add(metaRow, 'div', { id: 'sl-coverage-meta' });
|
||||
@@ -1224,6 +1226,26 @@ test('a swap names every registered provider, also those hidden on this map', ()
|
||||
]);
|
||||
}));
|
||||
|
||||
test('a note under SINCE says what date it compares, only while one is given', () =>
|
||||
withDom(async (dom) => {
|
||||
const state = uiState();
|
||||
const { layer } = stubPanel(dom, state);
|
||||
const note = () => dom.root.querySelector('#sl-since-note');
|
||||
assert.equal(note().hidden, true, 'none for Mapillary');
|
||||
layer.publish({ ...state, sinceNote: 'KartaView: by upload date' });
|
||||
assert.equal(note().hidden, false);
|
||||
assert.equal(note().textContent, 'KartaView: by upload date');
|
||||
layer.publish(state);
|
||||
assert.equal(note().hidden, true);
|
||||
// The panel's own markup has it, read out with the slider.
|
||||
const template = fs.readFileSync(
|
||||
new URL('./templates/layer-panels.html', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
assert.match(template, /<small id="sl-since-note"[^>]* hidden>/);
|
||||
assert.match(template, /aria-describedby="sl-since-label sl-since-note"/);
|
||||
}));
|
||||
|
||||
test('Street View lit hides the imagery filters and the legend; Mapillary lit shows them', () =>
|
||||
withDom(async (dom) => {
|
||||
const state = uiState();
|
||||
|
||||
@@ -193,7 +193,7 @@ function presentMeta(state) {
|
||||
const hint = state.coverage.hint || '';
|
||||
if (state.coverage.count > 0)
|
||||
return [
|
||||
`${state.coverage.count.toLocaleString()} sequences in view · click a line for its photos`,
|
||||
`${state.coverage.count.toLocaleString()} ${state.coverage.label?.noun || 'sequences'} in view · ${state.coverage.label?.action || 'click a line for its photos'}`,
|
||||
hint,
|
||||
]
|
||||
.filter(Boolean)
|
||||
@@ -224,6 +224,7 @@ export function presentStreetLevelPanel(state, { now = Date.now() } = {}) {
|
||||
since: state.filterControls?.since !== false,
|
||||
},
|
||||
since: presentSince(filter.sinceDays, now),
|
||||
sinceNote: state.sinceNote || null,
|
||||
legend: state.legend || [],
|
||||
viewer: presentViewer(state),
|
||||
meta: presentMeta(state),
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#street-level-panel,
|
||||
.sl-viewer-wrap {
|
||||
--sl-green: #05cb63;
|
||||
/* KartaView's purple for lines; a lighter one where it is text (WCAG AA). */
|
||||
--sl-kartaview: #635bff;
|
||||
--sl-kartaview-text: #8f8aff;
|
||||
--sl-text: 0.62rem;
|
||||
--sl-text-small: 0.6rem;
|
||||
}
|
||||
@@ -174,6 +177,11 @@ button.sl-status:focus-visible {
|
||||
);
|
||||
}
|
||||
|
||||
/* #635bff text on its own tint is 3.7:1 at this size; the lighter one is ~6:1. */
|
||||
.sl-chips .data-toggle-chip.active[data-chip-id='kartaview'] {
|
||||
color: var(--sl-kartaview-text);
|
||||
}
|
||||
|
||||
.sl-chips .data-toggle-chip.active::before {
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -291,6 +299,17 @@ button.sl-status:focus-visible {
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.sl-since-note {
|
||||
grid-column: 2;
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--sl-text-small);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.sl-since-note[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sl-since-label {
|
||||
grid-column: 2;
|
||||
font-family: var(--font-mono);
|
||||
@@ -613,6 +632,97 @@ button.sl-status:focus-visible {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
/* ── KartaView photo viewer ──────────────────────────────────────────────── */
|
||||
.sl-kv-viewer {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: #000;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sl-kv-viewer:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* The photo and canvas fill the viewer and would cover a ring drawn on it:
|
||||
draw the ring on a layer above them (clicks pass through). */
|
||||
.sl-kv-viewer:focus-visible::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 3;
|
||||
border: 2px solid var(--sl-kartaview-text);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.sl-kv-photo,
|
||||
.sl-kv-pano {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.sl-kv-photo {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.sl-kv-pano {
|
||||
cursor: grab;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.sl-kv-pano.sl-kv-dragging {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.sl-kv-photo[hidden],
|
||||
.sl-kv-pano[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sl-kv-step {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
z-index: 2;
|
||||
width: 34px;
|
||||
height: 44px;
|
||||
margin-top: -22px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
border-radius: 8px;
|
||||
background: rgba(10, 10, 15, 0.62);
|
||||
color: #fff;
|
||||
font-size: 1.5rem;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sl-kv-step-prev {
|
||||
left: 6px;
|
||||
}
|
||||
|
||||
.sl-kv-step-next {
|
||||
right: 6px;
|
||||
}
|
||||
|
||||
.sl-kv-step:hover:not(:disabled),
|
||||
.sl-kv-step:focus-visible {
|
||||
background: color-mix(in srgb, var(--sl-kartaview) 85%, transparent);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.sl-kv-step:disabled {
|
||||
opacity: 0.3;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Nothing to step through until a photo shows. */
|
||||
.sl-kv-viewer:not([data-kind='flat']):not([data-kind='pano']) .sl-kv-step {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ── Short viewports ─────────────────────────────────────────────────────── */
|
||||
@media (max-height: 640px) {
|
||||
.street-level-panel-inner {
|
||||
|
||||
@@ -128,8 +128,9 @@
|
||||
</div>
|
||||
<div id="sl-since-row" 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" />
|
||||
<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 sl-since-note" />
|
||||
<output id="sl-since-label" class="sl-since-label" for="sl-since">ANY DATE</output>
|
||||
<small id="sl-since-note" class="sl-since-note" hidden></small>
|
||||
</div>
|
||||
</fieldset>
|
||||
<ul id="sl-legend" class="sl-legend" aria-label="Coverage colours"></ul>
|
||||
|
||||
Reference in new issue
Block a user