mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +00:00
feat(cockpit): render the Contact and briefing cards' collapse from the store
Cockpit presence now carries contextCollapsed and signalCollapsed. setContextCollapsed and setSignalCollapsed update presence and republish it only when the value changes, instead of writing the dataset and the toggle attributes. CockpitContext and CockpitBriefing bind data-collapsed and their toggles' aria-expanded, aria-label, title and icon from presence. The Cockpit utility QA script waits a frame for Vue to render before it reads the toggles.
This commit is contained in:
1 parent
95523e09cd
commit
a3fc475cbe
10 files changed
+106
-50
No files matched your search
@@ -118,6 +118,11 @@
|
|||||||
that there is no panel to open instead of writing its classes
|
that there is no panel to open instead of writing its classes
|
||||||
(samehkhamis).
|
(samehkhamis).
|
||||||
|
|
||||||
|
- The Cockpit Contact and briefing cards' collapse renders from the
|
||||||
|
`gev-cockpit` store: the controller publishes `contextCollapsed` and
|
||||||
|
`signalCollapsed` in its presence, and each card's `data-collapsed` and
|
||||||
|
its toggle's state, words and icon follow it (samehkhamis).
|
||||||
|
|
||||||
## [0.3.0] — 2026-10-27 — Street Level, On Your Phone, in Your Language
|
## [0.3.0] — 2026-10-27 — Street Level, On Your Phone, in Your Language
|
||||||
|
|
||||||
- Show a Street Level view facing 359.5° or more as 0°. The viewer caption
|
- Show a Street Level view facing 359.5° or more as 0°. The viewer caption
|
||||||
|
|||||||
@@ -240,9 +240,11 @@ a feature supplying only its accent.
|
|||||||
publishes which surfaces are shown: the entry button and TR-3B chip in
|
publishes which surfaces are shown: the entry button and TR-3B chip in
|
||||||
the Context panel, the HUD with its exit and reset buttons and briefing
|
the Context panel, the HUD with its exit and reset buttons and briefing
|
||||||
card, and the Contact card. Focus moves to the exit button, and back to
|
card, and the Contact card. Focus moves to the exit button, and back to
|
||||||
the entry, only after Vue renders them. The controls keep the
|
the entry, only after Vue renders them. The cards' collapse is part of
|
||||||
controller's listeners, and the cards' collapse and layout stay with the
|
that presence too: each card's `data-collapsed` and its toggle's words
|
||||||
controller, which reads their visibility when it lays them out.
|
and icon render from it. The controls keep the controller's listeners,
|
||||||
|
and the cards' layout stays with the controller, which reads their
|
||||||
|
visibility when it lays them out.
|
||||||
`CockpitUtilityControls` renders the Display
|
`CockpitUtilityControls` renders the Display
|
||||||
and Radio launchers, which popover is open and the compact Radio
|
and Radio launchers, which popover is open and the compact Radio
|
||||||
controls from the `gev-radio` store that RadioControls publishes to; the
|
controls from the `gev-radio` store that RadioControls publishes to; the
|
||||||
|
|||||||
@@ -2087,7 +2087,7 @@ try {
|
|||||||
}
|
}
|
||||||
await page.evaluate(() => window.__godsEyeView.styleManager._setHudVariant('tactical'));
|
await page.evaluate(() => window.__godsEyeView.styleManager._setHudVariant('tactical'));
|
||||||
|
|
||||||
const signalTransition = await page.evaluate(() => {
|
const signalTransition = await page.evaluate(async () => {
|
||||||
const manager = window.__godsEyeView.styleManager;
|
const manager = window.__godsEyeView.styleManager;
|
||||||
const cockpit = manager.cockpitView;
|
const cockpit = manager.cockpitView;
|
||||||
const signalToggle = document.getElementById('cockpit-signal-toggle');
|
const signalToggle = document.getElementById('cockpit-signal-toggle');
|
||||||
@@ -2114,6 +2114,8 @@ try {
|
|||||||
manager._setCockpitDisclosure('radio', true);
|
manager._setCockpitDisclosure('radio', true);
|
||||||
manager._setCockpitDisclosure('radio', false);
|
manager._setCockpitDisclosure('radio', false);
|
||||||
const manualCollapseRetained = cockpit.signalCollapsed && cockpit.signalUserCollapsed;
|
const manualCollapseRetained = cockpit.signalCollapsed && cockpit.signalUserCollapsed;
|
||||||
|
// The toggle renders the published collapse on Vue's next flush.
|
||||||
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
||||||
return {
|
return {
|
||||||
afterExpansion,
|
afterExpansion,
|
||||||
afterRepeatedExpansion,
|
afterRepeatedExpansion,
|
||||||
@@ -2140,7 +2142,7 @@ try {
|
|||||||
&& signalTransition.signalAriaExpanded === 'false',
|
&& signalTransition.signalAriaExpanded === 'false',
|
||||||
JSON.stringify(signalTransition),
|
JSON.stringify(signalTransition),
|
||||||
);
|
);
|
||||||
const contextTransition = await page.evaluate(() => {
|
const contextTransition = await page.evaluate(async () => {
|
||||||
const cockpit = window.__godsEyeView.styleManager.cockpitView;
|
const cockpit = window.__godsEyeView.styleManager.cockpitView;
|
||||||
const priorCollapsed = cockpit.contextCollapsed;
|
const priorCollapsed = cockpit.contextCollapsed;
|
||||||
const contextToggle = document.getElementById('cockpit-context-toggle');
|
const contextToggle = document.getElementById('cockpit-context-toggle');
|
||||||
@@ -2152,6 +2154,8 @@ try {
|
|||||||
cockpit.setContextCollapsed(false);
|
cockpit.setContextCollapsed(false);
|
||||||
const afterExpansion = expandedEvents;
|
const afterExpansion = expandedEvents;
|
||||||
cockpit.setContextCollapsed(false);
|
cockpit.setContextCollapsed(false);
|
||||||
|
// The toggle renders the published collapse on Vue's next flush.
|
||||||
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
||||||
return {
|
return {
|
||||||
afterExpansion,
|
afterExpansion,
|
||||||
afterRepeatedExpansion: expandedEvents,
|
afterRepeatedExpansion: expandedEvents,
|
||||||
|
|||||||
@@ -1197,9 +1197,13 @@ test('cockpit summary presents the focused item as Contact', () => {
|
|||||||
);
|
);
|
||||||
assert.match(match[0], /aria-label="Collapse Contact panel"/);
|
assert.match(match[0], /aria-label="Collapse Contact panel"/);
|
||||||
assert.doesNotMatch(match[0], />GLOBAL CONTEXT</);
|
assert.doesNotMatch(match[0], />GLOBAL CONTEXT</);
|
||||||
|
// The toggle renders the collapse the controller presents.
|
||||||
assert.match(
|
assert.match(
|
||||||
setContextCollapsed.toString(),
|
fs.readFileSync(
|
||||||
/expanded \? t\('Collapse Contact panel'\) : t\('Expand Contact panel'\)/,
|
new URL('./ui/views/panels/CockpitContext.vue', import.meta.url),
|
||||||
|
'utf8',
|
||||||
|
),
|
||||||
|
/collapsed \? t\('Expand Contact panel'\) : t\('Collapse Contact panel'\)/,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
showBriefPage,
|
showBriefPage,
|
||||||
updateLocalPosition,
|
updateLocalPosition,
|
||||||
} from './cockpitBriefing.js';
|
} from './cockpitBriefing.js';
|
||||||
|
import { setSignalCollapsed } from './cockpitLayout.js';
|
||||||
import { useCockpitStore } from './stores/cockpit.js';
|
import { useCockpitStore } from './stores/cockpit.js';
|
||||||
import CockpitBriefing from './views/panels/CockpitBriefing.vue';
|
import CockpitBriefing from './views/panels/CockpitBriefing.vue';
|
||||||
|
|
||||||
@@ -259,3 +260,30 @@ test('the card is labelled in the UI language', async (t) => {
|
|||||||
await nextTick();
|
await nextTick();
|
||||||
assert.equal(text('#cockpit-brief-kicker'), 'НОВОСТИ РЕГИОНА');
|
assert.equal(text('#cockpit-brief-kicker'), 'НОВОСТИ РЕГИОНА');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('the card and its toggle render the collapse the controller presents', async (t) => {
|
||||||
|
const { $, owner } = fixture(t);
|
||||||
|
const store = useCockpitStore();
|
||||||
|
Object.assign(owner, {
|
||||||
|
active: true,
|
||||||
|
signalCollapsed: false,
|
||||||
|
presence: { active: true, signalCollapsed: false },
|
||||||
|
publishPresenceView: () => store.publishPresence({ ...owner.presence }),
|
||||||
|
});
|
||||||
|
const card = $('#cockpit-signal-stream');
|
||||||
|
const toggle = $('#cockpit-signal-toggle');
|
||||||
|
assert.equal(card.hasAttribute('data-collapsed'), false);
|
||||||
|
assert.equal(toggle.getAttribute('aria-expanded'), 'true');
|
||||||
|
setSignalCollapsed.call(owner, true, { user: true });
|
||||||
|
await nextTick();
|
||||||
|
assert.equal(card.dataset.collapsed, 'true');
|
||||||
|
assert.equal(toggle.getAttribute('aria-expanded'), 'false');
|
||||||
|
assert.equal(toggle.getAttribute('aria-label'), 'Expand cockpit briefing panel');
|
||||||
|
assert.equal(toggle.title, 'Expand briefing panel');
|
||||||
|
assert.equal(toggle.textContent.trim(), 'right_panel_open');
|
||||||
|
assert.equal(owner.signalUserCollapsed, true);
|
||||||
|
setSignalCollapsed.call(owner, false);
|
||||||
|
await nextTick();
|
||||||
|
assert.equal(card.dataset.collapsed, 'false');
|
||||||
|
assert.equal(toggle.textContent.trim(), 'right_panel_close');
|
||||||
|
});
|
||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
} from './cockpitContext.js';
|
} from './cockpitContext.js';
|
||||||
import { createContextState } from './cockpitContextState.js';
|
import { createContextState } from './cockpitContextState.js';
|
||||||
import { syncWeatherToggle } from './cockpitInstruments.js';
|
import { syncWeatherToggle } from './cockpitInstruments.js';
|
||||||
|
import { setContextCollapsed } from './cockpitLayout.js';
|
||||||
import { useCockpitStore } from './stores/cockpit.js';
|
import { useCockpitStore } from './stores/cockpit.js';
|
||||||
import CockpitContext from './views/panels/CockpitContext.vue';
|
import CockpitContext from './views/panels/CockpitContext.vue';
|
||||||
|
|
||||||
@@ -218,11 +219,12 @@ test('the weather toggle renders its state and the action it offers', async (t)
|
|||||||
assert.equal(owner.contextView.weather, true);
|
assert.equal(owner.contextView.weather, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('re-renders leave the card visibility and layout to the controller', async (t) => {
|
test('re-renders keep the collapse the controller presents and leave it the layout', async (t) => {
|
||||||
const { $, owner } = fixture(t, snapshot());
|
const { $, owner } = fixture(t, snapshot());
|
||||||
updateContext.call(owner, INFO, 0);
|
updateContext.call(owner, INFO, 0);
|
||||||
const card = $('#cockpit-context');
|
const card = $('#cockpit-context');
|
||||||
card.dataset.collapsed = 'true';
|
owner.presence.contextCollapsed = true;
|
||||||
|
owner.publishPresenceView();
|
||||||
card.dataset.layoutMode = 'compact-bottom';
|
card.dataset.layoutMode = 'compact-bottom';
|
||||||
owner.snapshot = snapshot({ evaluatedAt: Date.UTC(2026, 9, 31, 12, 35) });
|
owner.snapshot = snapshot({ evaluatedAt: Date.UTC(2026, 9, 31, 12, 35) });
|
||||||
updateContext.call(owner, INFO, 0);
|
updateContext.call(owner, INFO, 0);
|
||||||
@@ -230,6 +232,11 @@ test('re-renders leave the card visibility and layout to the controller', async
|
|||||||
assert.equal(card.hidden, false);
|
assert.equal(card.hidden, false);
|
||||||
assert.equal(card.dataset.collapsed, 'true');
|
assert.equal(card.dataset.collapsed, 'true');
|
||||||
assert.equal(card.dataset.layoutMode, 'compact-bottom');
|
assert.equal(card.dataset.layoutMode, 'compact-bottom');
|
||||||
|
const toggle = $('#cockpit-context-toggle');
|
||||||
|
assert.equal(toggle.getAttribute('aria-expanded'), 'false');
|
||||||
|
assert.equal(toggle.getAttribute('aria-label'), 'Expand Contact panel');
|
||||||
|
assert.equal(toggle.title, 'Expand contact panel');
|
||||||
|
assert.equal(toggle.textContent.trim(), 'chevron_right');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the card is labelled in the UI language', async (t) => {
|
test('the card is labelled in the UI language', async (t) => {
|
||||||
@@ -248,3 +255,26 @@ test('the card is labelled in the UI language', async (t) => {
|
|||||||
translate('Disable cockpit weather effects'),
|
translate('Disable cockpit weather effects'),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('collapsing the card publishes once per change', async (t) => {
|
||||||
|
const { $, owner } = fixture(t);
|
||||||
|
let published = 0;
|
||||||
|
const publish = owner.publishPresenceView;
|
||||||
|
Object.assign(owner, {
|
||||||
|
contextCollapsed: false,
|
||||||
|
presence: { context: true, contextCollapsed: false },
|
||||||
|
publishPresenceView: () => {
|
||||||
|
published += 1;
|
||||||
|
publish();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setContextCollapsed.call(owner, true);
|
||||||
|
setContextCollapsed.call(owner, true);
|
||||||
|
assert.equal(published, 1);
|
||||||
|
await nextTick();
|
||||||
|
assert.equal($('#cockpit-context').dataset.collapsed, 'true');
|
||||||
|
assert.equal(
|
||||||
|
$('#cockpit-context-toggle').getAttribute('aria-expanded'),
|
||||||
|
'false',
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -156,6 +156,9 @@ export class CockpitViewController {
|
|||||||
entryAvailable: false,
|
entryAvailable: false,
|
||||||
tr3b: null,
|
tr3b: null,
|
||||||
context: false,
|
context: false,
|
||||||
|
// The Contact and briefing cards' collapse, which their toggles show.
|
||||||
|
contextCollapsed: false,
|
||||||
|
signalCollapsed: false,
|
||||||
};
|
};
|
||||||
this.instruments = createInstrumentState();
|
this.instruments = createInstrumentState();
|
||||||
this.routeView = createRouteState();
|
this.routeView = createRouteState();
|
||||||
|
|||||||
+8
-34
@@ -1,5 +1,4 @@
|
|||||||
/** Measure Cockpit panels against visible HUD and viewport obstacles. */
|
/** Measure Cockpit panels against visible HUD and viewport obstacles. */
|
||||||
import { t, msg } from '../i18n/index.js';
|
|
||||||
import {
|
import {
|
||||||
resolveCockpitUtilityAnchor,
|
resolveCockpitUtilityAnchor,
|
||||||
resolveCockpitUtilityLayout,
|
resolveCockpitUtilityLayout,
|
||||||
@@ -35,20 +34,10 @@ export function scheduleContextLayout() {
|
|||||||
export function setContextCollapsed(collapsed) {
|
export function setContextCollapsed(collapsed) {
|
||||||
const wasCollapsed = this.contextCollapsed;
|
const wasCollapsed = this.contextCollapsed;
|
||||||
this.contextCollapsed = Boolean(collapsed);
|
this.contextCollapsed = Boolean(collapsed);
|
||||||
if (this.context)
|
// The Contact card and its toggle render the collapse from the presence.
|
||||||
this.context.dataset.collapsed = String(this.contextCollapsed);
|
if (this.presence.contextCollapsed !== this.contextCollapsed) {
|
||||||
if (this.contextToggle) {
|
this.presence.contextCollapsed = this.contextCollapsed;
|
||||||
const expanded = !this.contextCollapsed;
|
this.publishPresenceView();
|
||||||
this.contextToggle.setAttribute('aria-expanded', String(expanded));
|
|
||||||
this.contextToggle.setAttribute(
|
|
||||||
'aria-label',
|
|
||||||
expanded ? t('Collapse Contact panel') : t('Expand Contact panel'),
|
|
||||||
);
|
|
||||||
this.contextToggle.title = t(
|
|
||||||
expanded ? msg('Collapse contact panel') : msg('Expand contact panel'),
|
|
||||||
);
|
|
||||||
const icon = this.contextToggle.querySelector('.material-symbols-outlined');
|
|
||||||
if (icon) icon.textContent = expanded ? 'chevron_left' : 'chevron_right';
|
|
||||||
}
|
}
|
||||||
if (this.active && wasCollapsed && !this.contextCollapsed) {
|
if (this.active && wasCollapsed && !this.contextCollapsed) {
|
||||||
window.dispatchEvent(new CustomEvent('gev:cockpit-context-expanded'));
|
window.dispatchEvent(new CustomEvent('gev:cockpit-context-expanded'));
|
||||||
@@ -60,25 +49,10 @@ export function setSignalCollapsed(collapsed, { user = false } = {}) {
|
|||||||
const wasCollapsed = this.signalCollapsed;
|
const wasCollapsed = this.signalCollapsed;
|
||||||
this.signalCollapsed = Boolean(collapsed);
|
this.signalCollapsed = Boolean(collapsed);
|
||||||
if (user) this.signalUserCollapsed = this.signalCollapsed;
|
if (user) this.signalUserCollapsed = this.signalCollapsed;
|
||||||
if (this.signalStream)
|
// The briefing card and its toggle render the collapse from the presence.
|
||||||
this.signalStream.dataset.collapsed = String(this.signalCollapsed);
|
if (this.presence.signalCollapsed !== this.signalCollapsed) {
|
||||||
if (this.signalToggle) {
|
this.presence.signalCollapsed = this.signalCollapsed;
|
||||||
const expanded = !this.signalCollapsed;
|
this.publishPresenceView();
|
||||||
this.signalToggle.setAttribute('aria-expanded', String(expanded));
|
|
||||||
this.signalToggle.setAttribute(
|
|
||||||
'aria-label',
|
|
||||||
t(
|
|
||||||
expanded
|
|
||||||
? msg('Collapse cockpit briefing panel')
|
|
||||||
: msg('Expand cockpit briefing panel'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
this.signalToggle.title = t(
|
|
||||||
expanded ? msg('Collapse briefing panel') : msg('Expand briefing panel'),
|
|
||||||
);
|
|
||||||
const icon = this.signalToggle.querySelector('.material-symbols-outlined');
|
|
||||||
if (icon)
|
|
||||||
icon.textContent = expanded ? 'right_panel_close' : 'right_panel_open';
|
|
||||||
}
|
}
|
||||||
if (this.signalCollapsed) this.stopBriefRotation();
|
if (this.signalCollapsed) this.stopBriefRotation();
|
||||||
else this.startBriefRotation({ reset: true });
|
else this.startBriefRotation({ reset: true });
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { computed, useTemplateRef } from 'vue';
|
import { computed, useTemplateRef } from 'vue';
|
||||||
|
import { t } from '../../../i18n/index.js';
|
||||||
import { createBriefState } from '../../cockpitBriefPages.js';
|
import { createBriefState } from '../../cockpitBriefPages.js';
|
||||||
import { useCockpitStore } from '../../stores/cockpit.js';
|
import { useCockpitStore } from '../../stores/cockpit.js';
|
||||||
import CockpitRoute from './CockpitRoute.vue';
|
import CockpitRoute from './CockpitRoute.vue';
|
||||||
@@ -13,10 +14,13 @@ import CockpitSignalList from './CockpitSignalList.vue';
|
|||||||
const { presence, brief } = storeToRefs(useCockpitStore());
|
const { presence, brief } = storeToRefs(useCockpitStore());
|
||||||
const initial = createBriefState();
|
const initial = createBriefState();
|
||||||
const b = computed(() => brief.value ?? initial);
|
const b = computed(() => brief.value ?? initial);
|
||||||
|
// The card's collapse, which its toggle and data-collapsed show.
|
||||||
|
const collapsed = computed(() => !!presence.value?.signalCollapsed);
|
||||||
// The card's page and the region's answer, as state hooks on the card.
|
// The card's page and the region's answer, as state hooks on the card.
|
||||||
const streamState = computed(() => ({
|
const streamState = computed(() => ({
|
||||||
'data-brief-page': brief.value?.pageId,
|
'data-brief-page': brief.value?.pageId,
|
||||||
'data-regional-status': brief.value?.regionalStatus || undefined,
|
'data-regional-status': brief.value?.regionalStatus || undefined,
|
||||||
|
'data-collapsed': presence.value ? String(collapsed.value) : undefined,
|
||||||
}));
|
}));
|
||||||
// A departed contact cannot stay selectable. Its row hands focus to the
|
// A departed contact cannot stay selectable. Its row hands focus to the
|
||||||
// shown page's tab, which stays, so keyboard traversal carries on from the
|
// shown page's tab, which stays, so keyboard traversal carries on from the
|
||||||
@@ -48,8 +52,8 @@ function leaveSignals() {
|
|||||||
<span class="material-symbols-outlined" aria-hidden="true">chevron_right</span>
|
<span class="material-symbols-outlined" aria-hidden="true">chevron_right</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="cockpit-brief-auto" type="button" :aria-label="b.autoLabel" :aria-pressed="String(b.autoRotate)" :title="b.autoHelp">{{ b.autoLabel }}</button>
|
<button id="cockpit-brief-auto" type="button" :aria-label="b.autoLabel" :aria-pressed="String(b.autoRotate)" :title="b.autoHelp">{{ b.autoLabel }}</button>
|
||||||
<button id="cockpit-signal-toggle" type="button" aria-label="Collapse cockpit briefing panel" aria-expanded="true" title="Collapse briefing panel" data-i18n-aria-label data-i18n-title>
|
<button id="cockpit-signal-toggle" type="button" :aria-label="collapsed ? t('Expand cockpit briefing panel') : t('Collapse cockpit briefing panel')" :aria-expanded="String(!collapsed)" :title="collapsed ? t('Expand briefing panel') : t('Collapse briefing panel')">
|
||||||
<span class="material-symbols-outlined" aria-hidden="true">right_panel_close</span>
|
<span class="material-symbols-outlined" aria-hidden="true">{{ collapsed ? 'right_panel_open' : 'right_panel_close' }}</span>
|
||||||
</button>
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ import { useCockpitStore } from '../../stores/cockpit.js';
|
|||||||
|
|
||||||
// The Cockpit Contact card. The Cockpit controller presents its scope,
|
// The Cockpit Contact card. The Cockpit controller presents its scope,
|
||||||
// navigation, cohort counts, nearest contact and weather toggle; until it
|
// navigation, cohort counts, nearest contact and weather toggle; until it
|
||||||
// does, the card shows the state it starts in. Its visibility, collapse and
|
// does, the card shows the state it starts in. Its visibility and collapse
|
||||||
// layout stay with the controller, and the controls keep its listeners.
|
// render from the presence; its layout stays with the controller, and the
|
||||||
|
// controls keep its listeners.
|
||||||
const { presence, context } = storeToRefs(useCockpitStore());
|
const { presence, context } = storeToRefs(useCockpitStore());
|
||||||
const initial = createContextState();
|
const initial = createContextState();
|
||||||
const c = computed(() => context.value ?? initial);
|
const c = computed(() => context.value ?? initial);
|
||||||
@@ -18,6 +19,7 @@ const directionStyle = computed(() =>
|
|||||||
? undefined
|
? undefined
|
||||||
: { transform: `rotate(${c.value.direction.angle}deg)` },
|
: { transform: `rotate(${c.value.direction.angle}deg)` },
|
||||||
);
|
);
|
||||||
|
const collapsed = computed(() => !!presence.value?.contextCollapsed);
|
||||||
const weatherLabel = computed(() =>
|
const weatherLabel = computed(() =>
|
||||||
c.value.weather
|
c.value.weather
|
||||||
? t('Disable cockpit weather effects')
|
? t('Disable cockpit weather effects')
|
||||||
@@ -26,7 +28,7 @@ const weatherLabel = computed(() =>
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<aside id="cockpit-context" class="cockpit-context-window" aria-label="Contact cockpit summary" :hidden="!presence?.context" data-i18n-aria-label :data-state="c.state ?? undefined">
|
<aside id="cockpit-context" class="cockpit-context-window" aria-label="Contact cockpit summary" :hidden="!presence?.context" data-i18n-aria-label :data-state="c.state ?? undefined" :data-collapsed="presence ? String(collapsed) : undefined">
|
||||||
<header class="cockpit-context-header">
|
<header class="cockpit-context-header">
|
||||||
<div>
|
<div>
|
||||||
<span data-i18n class="cockpit-context-kicker">CONTACT</span>
|
<span data-i18n class="cockpit-context-kicker">CONTACT</span>
|
||||||
@@ -39,8 +41,8 @@ const weatherLabel = computed(() =>
|
|||||||
<button id="cockpit-context-next" type="button" :disabled="!c.canNext" aria-label="Next — nearest unvisited contact in the 250 km window" title="Next — nearest unvisited contact in the 250 km window" data-i18n-aria-label data-i18n-title>
|
<button id="cockpit-context-next" type="button" :disabled="!c.canNext" aria-label="Next — nearest unvisited contact in the 250 km window" title="Next — nearest unvisited contact in the 250 km window" data-i18n-aria-label data-i18n-title>
|
||||||
<span class="material-symbols-outlined" aria-hidden="true">skip_next</span>
|
<span class="material-symbols-outlined" aria-hidden="true">skip_next</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="cockpit-context-toggle" type="button" aria-label="Collapse Contact panel" aria-expanded="true" title="Collapse contact panel" data-i18n-aria-label data-i18n-title>
|
<button id="cockpit-context-toggle" type="button" :aria-label="collapsed ? t('Expand Contact panel') : t('Collapse Contact panel')" :aria-expanded="String(!collapsed)" :title="collapsed ? t('Expand contact panel') : t('Collapse contact panel')">
|
||||||
<span class="material-symbols-outlined" aria-hidden="true">chevron_left</span>
|
<span class="material-symbols-outlined" aria-hidden="true">{{ collapsed ? 'chevron_right' : 'chevron_left' }}</span>
|
||||||
</button>
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
<span class="cockpit-context-qualifier" data-i18n>CONTEXT ONLY</span>
|
<span class="cockpit-context-qualifier" data-i18n>CONTEXT ONLY</span>
|
||||||
|
|||||||
Reference in new issue
Block a user