mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
Merge main into the Utilities panel, rebuilt on the Vue panel rail
main moved the panels to Vue views and a one-open-panel rail, and removed
the two templates this branch edited. This merge carries the branch onto
that architecture; it adds no behaviour of its own beyond the port.
- The Utilities panel is a RailPanel in LayerPanelsView with its own rail
item (handyman). LayerPanel renders the Directions rows into its list with
LayerRows, and falls back to Data Layers without it.
- Draw leaves DisplayPanel for DrawControls, DrawMarkList and DrawSwatches,
which render what the draw tool publishes to the display store and call
its actions, as GeoImport does. drawMarks.js words the rows; the DOM-built
drawMarkList.js is gone. A row's fly-to is an immediate navigation like
Import's ('drawing' gets its Cockpit refusal), the name field keeps a
draft through v-field-value, and the colours are one radio group with a
single tab stop.
- UiSegmented draws an option's icon, named by its label, for the shape
buttons.
- The catalogs take main's languages with this branch's strings, Arabic
included, and the tests follow the views to their new homes.
This commit is contained in:
commit
4ebb287d36
699 files changed
+46673
-20476
No files matched your search
+127
-156
@@ -1,5 +1,7 @@
|
||||
import { UiLifetime } from './uiLifetime.js';
|
||||
import { t, msg } from '../i18n/index.js';
|
||||
import { prefersCollapsed, readPanelState } from './rail/panelState.js';
|
||||
import { usePanelChromeStore } from './stores/panelChrome.js';
|
||||
import { applicationPinia } from './vueRuntime.js';
|
||||
import { displayPanelScroller } from './displayPanelScroll.js';
|
||||
import { PanelPositionControls } from './panelPositionControls.js';
|
||||
import { PanelLayoutController } from './panelLayoutController.js';
|
||||
@@ -8,6 +10,7 @@ import {
|
||||
collapsePanelOnEscape,
|
||||
createHoverDisclosure,
|
||||
} from './panelDisclosure.js';
|
||||
|
||||
const SHARE_PANEL_STATE_SPECS = Object.freeze([
|
||||
{ id: 'control-panel', pinnable: true },
|
||||
{ id: 'location-bar', pinnable: true },
|
||||
@@ -22,6 +25,12 @@ const SHARE_PANEL_STATE_SPECS = Object.freeze([
|
||||
{ id: 'pp-toggles' },
|
||||
{ id: 'param-slider-panel' },
|
||||
]);
|
||||
/** The command dock trays with a pin button. */
|
||||
const PINNABLE_PANEL_IDS = new Set(
|
||||
SHARE_PANEL_STATE_SPECS.filter((spec) => spec.pinnable).map(
|
||||
(spec) => spec.id,
|
||||
),
|
||||
);
|
||||
/** Standard map-view panels cleared out of the way on a fresh Cockpit entry. */
|
||||
const COCKPIT_ENTRY_COLLAPSE_PANEL_IDS = Object.freeze([
|
||||
'data-panel',
|
||||
@@ -48,6 +57,7 @@ export class PanelChrome {
|
||||
readScrollRestoreOwner,
|
||||
readDisplayScrollTop,
|
||||
readSharedCollapsed = () => undefined,
|
||||
store = usePanelChromeStore(applicationPinia()),
|
||||
}) {
|
||||
Object.assign(this, elements, operations, {
|
||||
readHud,
|
||||
@@ -60,10 +70,21 @@ export class PanelChrome {
|
||||
});
|
||||
this._disposed = false;
|
||||
this._lifetime = new UiLifetime();
|
||||
// The collapse, dock and pin buttons render each panel's state from the
|
||||
// store; their clicks take effect only once the chrome is initialized.
|
||||
this._chromeStore = store;
|
||||
this._chromeToken = store.connect({
|
||||
toggle: (panelId) => this._togglePanel(panelId),
|
||||
pin: (panelId) => {
|
||||
if (this._disclosurePanelIds)
|
||||
this._setCommandDockPanelPinState(panelId);
|
||||
},
|
||||
});
|
||||
this._cockpitPanelRestore = null;
|
||||
this._cockpitContextCollapsedForDataPanel = false;
|
||||
this._panelPosition = new PanelPositionControls({
|
||||
syncPanelCollapseButton: (panel) => this._syncPanelCollapseButton(panel),
|
||||
presentPanel: (panel, patch) => this._presentPanel(panel, patch),
|
||||
shownPanel: (panel) => this._shownPanel(panel),
|
||||
showToast: (message) => this._showToast(message),
|
||||
});
|
||||
this._panelLayout = new PanelLayoutController({
|
||||
@@ -72,7 +93,7 @@ export class PanelChrome {
|
||||
variant: this.hud.getVariant(),
|
||||
}),
|
||||
scheduleCockpitLayout: () => this.cockpitView?.scheduleContextLayout(),
|
||||
syncPanelCollapseButton: (panel) => this._syncPanelCollapseButton(panel),
|
||||
presentPanel: (panel, patch) => this._presentPanel(panel, patch),
|
||||
readDisplayScrollTop: () =>
|
||||
this._displayPortalScrollRestoreOwner === 'standard'
|
||||
? this._standardDisplayScrollTop
|
||||
@@ -102,24 +123,23 @@ export class PanelChrome {
|
||||
for (const control of this._panelDisclosureControls || [])
|
||||
control.destroy();
|
||||
this._panelDisclosureControls = [];
|
||||
const targets = new Map();
|
||||
document
|
||||
.querySelectorAll('.panel-collapse-btn[data-collapse-target]')
|
||||
.forEach((button) => {
|
||||
const targetId = button.dataset.collapseTarget;
|
||||
if (!targetId) return;
|
||||
if (!targets.has(targetId)) targets.set(targetId, []);
|
||||
targets.get(targetId).push(button);
|
||||
});
|
||||
for (const [targetId, buttons] of targets) {
|
||||
this._disclosurePanelIds = new Set();
|
||||
const targets = new Set(
|
||||
[
|
||||
...document.querySelectorAll(
|
||||
'.panel-collapse-btn[data-collapse-target]',
|
||||
),
|
||||
]
|
||||
.map((button) => button.dataset.collapseTarget)
|
||||
.filter(Boolean),
|
||||
);
|
||||
for (const targetId of targets) {
|
||||
const panel = document.getElementById(targetId);
|
||||
if (!panel) continue;
|
||||
this._disclosurePanelIds.add(targetId);
|
||||
this._panelDisclosureControls.push(
|
||||
bindPanelDisclosure({
|
||||
panel,
|
||||
buttons,
|
||||
onChange: (collapsed, options) =>
|
||||
this.setPanelCollapsed(targetId, collapsed, options),
|
||||
onEscape: (event) => this._collapsePanelOnEscape(event, targetId),
|
||||
}),
|
||||
);
|
||||
@@ -145,7 +165,6 @@ export class PanelChrome {
|
||||
openDelayMs: 140,
|
||||
closeDelayMs: 420,
|
||||
});
|
||||
this._initCommandDockPins();
|
||||
this._initCommandDockTrayMetrics();
|
||||
this._maybeNotifyLayoutReset();
|
||||
}
|
||||
@@ -159,24 +178,35 @@ export class PanelChrome {
|
||||
onChange: (collapsed, options) =>
|
||||
this.setPanelCollapsed(panelId, collapsed, options),
|
||||
beforeCollapse: () => {
|
||||
if (panelId !== 'location-bar' || !this._locationSearch) return;
|
||||
this._locationSearch.classList.remove('expanded');
|
||||
this._locationSearch.value = '';
|
||||
this._locationSearch.blur();
|
||||
if (panelId === 'location-bar') this._closeLocationSearch?.();
|
||||
},
|
||||
// A desktop side panel hides entirely once collapsed; its rail item
|
||||
// takes the focus its own disclosure can no longer hold.
|
||||
returnFocusTo: () => {
|
||||
const panel = document.getElementById(panelId);
|
||||
if (!panel?.getClientRects || panel.getClientRects().length > 0)
|
||||
return null;
|
||||
const item = document.querySelector?.(
|
||||
`.ui-rail [data-rail-item="${panelId}"]`,
|
||||
);
|
||||
return item &&
|
||||
!item.disabled &&
|
||||
item.getClientRects().length > 0 &&
|
||||
getComputedStyle(item).visibility === 'visible'
|
||||
? item
|
||||
: null;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
_initCommandDockPins() {
|
||||
document
|
||||
.querySelectorAll('.dock-pin-btn[data-pin-target]')
|
||||
.forEach((button) => {
|
||||
this._lifetime.listen(button, 'click', (event) => {
|
||||
event.stopPropagation();
|
||||
const panelId = button.dataset.pinTarget;
|
||||
this._setCommandDockPanelPinState(panelId);
|
||||
});
|
||||
});
|
||||
/** A collapse button's click: flip its panel. */
|
||||
_togglePanel(panelId) {
|
||||
if (this._disposed || !this._disclosurePanelIds?.has(panelId)) return;
|
||||
const panel = document.getElementById(panelId);
|
||||
if (!panel) return;
|
||||
this.setPanelCollapsed(panelId, !this._shownPanel(panel).collapsed, {
|
||||
explicit: true,
|
||||
});
|
||||
}
|
||||
|
||||
_setCommandDockPanelPinState(
|
||||
@@ -184,24 +214,23 @@ export class PanelChrome {
|
||||
pin,
|
||||
{ restore = false, persist = true, syncShare = true } = {},
|
||||
) {
|
||||
if (this._disposed || !PINNABLE_PANEL_IDS.has(panelId)) return undefined;
|
||||
const panelEl = document.getElementById(panelId);
|
||||
const button = document.querySelector(
|
||||
`.dock-pin-btn[data-pin-target="${panelId}"]`,
|
||||
);
|
||||
if (!panelEl || !button) return undefined;
|
||||
if (!panelEl) return undefined;
|
||||
const shouldPin =
|
||||
typeof pin === 'boolean'
|
||||
? pin
|
||||
: !panelEl.classList.contains('dock-pinned');
|
||||
panelEl.classList.toggle('dock-pinned', shouldPin);
|
||||
button.setAttribute('aria-pressed', String(shouldPin));
|
||||
document
|
||||
.querySelectorAll('#command-dock .dock-pinned-top')
|
||||
.forEach((pinnedPanel) => {
|
||||
pinnedPanel.classList.remove('dock-pinned-top');
|
||||
typeof pin === 'boolean' ? pin : !this._shownPanel(panelEl).pinned;
|
||||
// The tray pinned last draws on top; unpinning hands the top to the
|
||||
// other tray if it is pinned.
|
||||
this._presentPanel(panelEl, { pinned: shouldPin, pinnedTop: shouldPin });
|
||||
for (const otherId of PINNABLE_PANEL_IDS) {
|
||||
if (otherId === panelId) continue;
|
||||
const other = document.getElementById(otherId);
|
||||
if (!other) continue;
|
||||
this._presentPanel(other, {
|
||||
pinnedTop: !shouldPin && this._shownPanel(other).pinned,
|
||||
});
|
||||
}
|
||||
if (shouldPin) {
|
||||
panelEl.classList.add('dock-pinned-top');
|
||||
this.setPanelCollapsed(panelId, false, {
|
||||
explicit: !restore,
|
||||
restore,
|
||||
@@ -209,10 +238,6 @@ export class PanelChrome {
|
||||
syncShare: false,
|
||||
});
|
||||
} else {
|
||||
const remainingPinnedPanel = document.querySelector(
|
||||
'#command-dock .dock-pinned',
|
||||
);
|
||||
remainingPinnedPanel?.classList.add('dock-pinned-top');
|
||||
if (!restore && !panelEl.matches(':hover')) {
|
||||
this.setPanelCollapsed(panelId, true, {
|
||||
explicit: true,
|
||||
@@ -292,66 +317,32 @@ export class PanelChrome {
|
||||
return this._panelLayout._scheduleLeftPanelLayout(options0);
|
||||
}
|
||||
|
||||
_syncPanelCollapseButton(panelEl) {
|
||||
const isRightRail = [
|
||||
'pp-toggles',
|
||||
'cctv-panel',
|
||||
'weather-panel',
|
||||
'recent-imagery-panel',
|
||||
'street-level-panel',
|
||||
'global-context-panel',
|
||||
].includes(panelEl?.id);
|
||||
const collapsed = panelEl.classList.contains('collapsed');
|
||||
panelEl
|
||||
.querySelectorAll('.panel-collapse-btn[data-collapse-target]')
|
||||
.forEach((btn) => {
|
||||
const owner = btn.closest('[data-panel-id], #param-slider-panel');
|
||||
if (owner !== panelEl) return;
|
||||
if (isRightRail) {
|
||||
btn.textContent = collapsed ? '◀' : '▶';
|
||||
} else {
|
||||
btn.textContent = collapsed ? '+' : '−';
|
||||
}
|
||||
btn.setAttribute('aria-expanded', String(!collapsed));
|
||||
const panelName =
|
||||
panelEl
|
||||
.querySelector('.panel-title, .pp-header-label')
|
||||
?.textContent?.trim() || t('panel');
|
||||
const titleKey = collapsed
|
||||
? msg('Expand {name}')
|
||||
: msg('Collapse {name}');
|
||||
btn.title = t(titleKey, { name: panelName });
|
||||
btn.setAttribute('aria-label', t(titleKey, { name: panelName }));
|
||||
if (panelEl.id === 'radio-panel') {
|
||||
// Radio keeps the wording of its static button (context.html).
|
||||
btn.title = t(
|
||||
collapsed ? msg('Expand Radio') : msg('Collapse Radio'),
|
||||
);
|
||||
btn.setAttribute(
|
||||
'aria-label',
|
||||
t(
|
||||
collapsed
|
||||
? msg('Expand Radio section')
|
||||
: msg('Collapse Radio section'),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
const dockToggle = panelEl.querySelector(
|
||||
`[data-dock-toggle-target="${panelEl.id}"]`,
|
||||
);
|
||||
if (dockToggle) {
|
||||
const panelName =
|
||||
panelEl
|
||||
.querySelector('.panel-title, .location-toolbar-label')
|
||||
?.textContent?.trim() || t('panel');
|
||||
const titleKey = collapsed
|
||||
? msg('Expand {name}')
|
||||
: msg('Collapse {name}');
|
||||
dockToggle.setAttribute('aria-expanded', String(!collapsed));
|
||||
dockToggle.setAttribute('aria-label', t(titleKey, { name: panelName }));
|
||||
dockToggle.title = t(titleKey, { name: panelName });
|
||||
}
|
||||
/**
|
||||
* Present a panel's state, merged over what it shows now, to the panel
|
||||
* chrome store; its root and buttons draw it from there.
|
||||
* @param {Element|string} panelOrId
|
||||
* @param {Partial<import('./stores/panelChrome.js').PanelState>} [patch]
|
||||
*/
|
||||
_presentPanel(panelOrId, patch = {}) {
|
||||
const panelEl =
|
||||
typeof panelOrId === 'string'
|
||||
? document.getElementById(panelOrId)
|
||||
: panelOrId;
|
||||
if (!panelEl?.id) return;
|
||||
this._chromeStore.publish(this._chromeToken, panelEl.id, {
|
||||
...this._shownPanel(panelEl),
|
||||
...patch,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A panel's state as PanelChrome presented it, or its markup's before the
|
||||
* first presentation.
|
||||
* @param {Element} panelEl
|
||||
* @returns {import('./stores/panelChrome.js').PanelState}
|
||||
*/
|
||||
_shownPanel(panelEl) {
|
||||
return this._chromeStore.panels[panelEl.id] ?? readPanelState(panelEl);
|
||||
}
|
||||
|
||||
_buildSharePanelState() {
|
||||
@@ -361,18 +352,14 @@ export class PanelChrome {
|
||||
if (!panelEl) continue;
|
||||
// Responsive auto-collapse is presentation only; the recipient should
|
||||
// restore the user's explicit expanded preference at its own viewport.
|
||||
const collapsed =
|
||||
panelEl.classList.contains('layout-auto-collapsed') ||
|
||||
panelEl.classList.contains('cyber-accordion-collapsed')
|
||||
? false
|
||||
: panelEl.classList.contains('collapsed');
|
||||
const shown = this._shownPanel(panelEl);
|
||||
const collapsed = prefersCollapsed(shown);
|
||||
// A panel the phone shell only borrowed for a sheet shares its own state.
|
||||
const entry = {
|
||||
id: spec.id,
|
||||
collapsed: this.readSharedCollapsed(spec.id) ?? collapsed,
|
||||
};
|
||||
if (spec.pinnable)
|
||||
entry.pinned = panelEl.classList.contains('dock-pinned');
|
||||
if (spec.pinnable) entry.pinned = shown.pinned;
|
||||
specs.push(entry);
|
||||
}
|
||||
return specs.length ? { specs } : null;
|
||||
@@ -419,21 +406,22 @@ export class PanelChrome {
|
||||
if (explicit && !restore)
|
||||
this.shareLinkManager?.claimRestoreLane?.('panel', panelId);
|
||||
const nextCollapsed = Boolean(collapsed);
|
||||
const wasAutoCollapsed =
|
||||
panelEl.classList.contains('layout-auto-collapsed') ||
|
||||
panelEl.classList.contains('cyber-accordion-collapsed');
|
||||
// A user opening a Cyber rail panel owns the whole accordion, including
|
||||
// peers that were only presentation-collapsed during a saved-state restore.
|
||||
const wasAutoCollapsed = Boolean(this._shownPanel(panelEl).auto);
|
||||
// One panel is open per desktop side: opening a side panel, whether the
|
||||
// user asked or a feature revealed it (Street Level's viewer, a selected
|
||||
// radio station), owns the whole accordion, including peers that were
|
||||
// only presentation-collapsed during a saved-state restore.
|
||||
const sideStack = [this._leftPanelStack, this._rightPanelStack].find(
|
||||
(stack) => stack && panelEl.parentElement === stack,
|
||||
);
|
||||
if (
|
||||
explicit &&
|
||||
!restore &&
|
||||
!nextCollapsed &&
|
||||
document.documentElement?.dataset.uiTheme === 'cyber' &&
|
||||
panelEl.parentElement === this._rightPanelStack &&
|
||||
sideStack &&
|
||||
// Phone sheets show several rail panels at once.
|
||||
!document.body?.classList?.contains('mobile-shell')
|
||||
) {
|
||||
for (const peer of this._rightPanelStack.children) {
|
||||
for (const peer of sideStack.children) {
|
||||
if (
|
||||
peer !== panelEl &&
|
||||
peer.matches('[data-panel-id]') &&
|
||||
@@ -458,7 +446,7 @@ export class PanelChrome {
|
||||
: null;
|
||||
const priorLeftOwner = this._panelLayout._leftStackPreferredPanelId;
|
||||
const priorRightOwner = this._panelLayout._rightStackPreferredPanelId;
|
||||
if (explicit && !restore && !nextCollapsed && leftOwnerPanel) {
|
||||
if (!restore && !nextCollapsed && leftOwnerPanel) {
|
||||
this._panelLayout._leftStackPreferredPanelId = leftOwnerPanel.id;
|
||||
} else if (
|
||||
explicit &&
|
||||
@@ -468,7 +456,7 @@ export class PanelChrome {
|
||||
) {
|
||||
this._panelLayout._leftStackPreferredPanelId = null;
|
||||
}
|
||||
if (explicit && !restore && !nextCollapsed && rightOwnerPanel) {
|
||||
if (!restore && !nextCollapsed && rightOwnerPanel) {
|
||||
this._panelLayout._rightStackPreferredPanelId = rightOwnerPanel.id;
|
||||
} else if (
|
||||
explicit &&
|
||||
@@ -480,13 +468,12 @@ export class PanelChrome {
|
||||
}
|
||||
// Reveal Radio's owner before the no-op check: the nested section may
|
||||
// already be expanded while its Context parent is closed.
|
||||
const contextPanel = document.getElementById('global-context-panel');
|
||||
if (
|
||||
document.documentElement?.dataset.uiTheme === 'cyber' &&
|
||||
!nextCollapsed &&
|
||||
panelId === 'radio-panel' &&
|
||||
document
|
||||
.getElementById('global-context-panel')
|
||||
?.classList.contains('collapsed')
|
||||
contextPanel &&
|
||||
this._shownPanel(contextPanel).collapsed
|
||||
) {
|
||||
this.setPanelCollapsed('global-context-panel', false, {
|
||||
explicit,
|
||||
@@ -496,10 +483,9 @@ export class PanelChrome {
|
||||
});
|
||||
}
|
||||
if (
|
||||
panelEl.classList.contains('collapsed') === nextCollapsed &&
|
||||
this._shownPanel(panelEl).collapsed === nextCollapsed &&
|
||||
!wasAutoCollapsed
|
||||
) {
|
||||
this._syncPanelCollapseButton(panelEl);
|
||||
if (priorLeftOwner !== this._panelLayout._leftStackPreferredPanelId) {
|
||||
this._scheduleLeftPanelLayout({ reconsiderAutoCollapse: true });
|
||||
}
|
||||
@@ -508,10 +494,6 @@ export class PanelChrome {
|
||||
}
|
||||
return;
|
||||
}
|
||||
panelEl.classList.remove(
|
||||
'layout-auto-collapsed',
|
||||
'cyber-accordion-collapsed',
|
||||
);
|
||||
if (
|
||||
!nextCollapsed &&
|
||||
this.cockpitView?.active &&
|
||||
@@ -523,23 +505,9 @@ export class PanelChrome {
|
||||
this.cockpitView.setContextCollapsed(true);
|
||||
}
|
||||
}
|
||||
if (
|
||||
!nextCollapsed &&
|
||||
panelId === 'radio-panel' &&
|
||||
document.documentElement?.dataset.uiTheme !== 'cyber' &&
|
||||
document
|
||||
.getElementById('global-context-panel')
|
||||
?.classList.contains('collapsed')
|
||||
) {
|
||||
this.setPanelCollapsed('global-context-panel', false, {
|
||||
restore,
|
||||
persist,
|
||||
syncShare,
|
||||
});
|
||||
}
|
||||
if (!nextCollapsed && !restore && panelId === 'location-bar') {
|
||||
const otherPanel = document.getElementById('control-panel');
|
||||
if (otherPanel && !otherPanel.classList.contains('dock-pinned')) {
|
||||
if (otherPanel && !this._shownPanel(otherPanel).pinned) {
|
||||
this.setPanelCollapsed('control-panel', true, {
|
||||
restore,
|
||||
persist,
|
||||
@@ -548,7 +516,7 @@ export class PanelChrome {
|
||||
}
|
||||
} else if (!nextCollapsed && !restore && panelId === 'control-panel') {
|
||||
const otherPanel = document.getElementById('location-bar');
|
||||
if (otherPanel && !otherPanel.classList.contains('dock-pinned')) {
|
||||
if (otherPanel && !this._shownPanel(otherPanel).pinned) {
|
||||
this.setPanelCollapsed('location-bar', true, {
|
||||
restore,
|
||||
persist,
|
||||
@@ -556,7 +524,7 @@ export class PanelChrome {
|
||||
});
|
||||
}
|
||||
}
|
||||
panelEl.classList.toggle('collapsed', nextCollapsed);
|
||||
this._presentPanel(panelEl, { collapsed: nextCollapsed, auto: null });
|
||||
if (
|
||||
nextCollapsed &&
|
||||
this.cockpitView?.active &&
|
||||
@@ -566,7 +534,6 @@ export class PanelChrome {
|
||||
this._cockpitContextCollapsedForDataPanel = false;
|
||||
this.cockpitView.setContextCollapsed(false);
|
||||
}
|
||||
this._syncPanelCollapseButton(panelEl);
|
||||
if (persist !== false)
|
||||
this._savePanelCollapsedState(panelId, nextCollapsed);
|
||||
if (panelId === 'pp-toggles') {
|
||||
@@ -599,7 +566,7 @@ export class PanelChrome {
|
||||
if (panel) {
|
||||
this._cockpitPanelRestore.set(
|
||||
panelId,
|
||||
panel.classList.contains('collapsed'),
|
||||
this._shownPanel(panel).collapsed,
|
||||
);
|
||||
}
|
||||
this.setPanelCollapsed(panelId, true, {
|
||||
@@ -624,8 +591,12 @@ export class PanelChrome {
|
||||
}
|
||||
destroy() {
|
||||
if (this._disposed) return;
|
||||
// Cockpit's own teardown follows the chrome's: put back the panels its
|
||||
// entry closed while this owner can still present them.
|
||||
this.exitCockpit();
|
||||
this._disposed = true;
|
||||
this._lifetime.destroy();
|
||||
this._chromeStore.detach(this._chromeToken);
|
||||
this._panelLayout.destroy();
|
||||
for (const control of this._panelDisclosureControls || [])
|
||||
control.destroy();
|
||||
|
||||
Reference in new issue
Block a user