From 7edf8a577e23f7d24f7ac13bd051e4b8f157f955 Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Sun, 1 Nov 2026 13:23:51 -0800 Subject: [PATCH] feat(globe): answer the clear and share actions through the globe actions store The top-center bar is now GlobeActions.vue. The shell publishes whether a clear is under way and answers the clear and share buttons through the globe actions store, instead of binding listeners and writing attributes on the buttons. A shell disposed mid-clear resets the busy state, so the next shell's bar is not left busy. bindClearLayersControl stays exported and is deprecated. --- CHANGELOG.md | 8 ++- docs/VUE-MIGRATION.md | 5 +- scripts/format-scope.json | 1 + scripts/package-boundaries.json | 10 +++- scripts/qa-layer-panel.mjs | 10 +++- src/contextTabKeyboard.test.mjs | 10 ++-- src/sharelink.celestial.test.mjs | 3 +- src/ui/applicationShell.js | 83 +++++++++++++++++----------- src/ui/clearLayersControl.js | 8 ++- src/ui/globeActions.test.mjs | 69 +++++++++++++++++++++++ src/ui/shareButton.test.mjs | 33 +++++++++-- src/ui/shellElements.js | 2 - src/ui/stores/globeActions.js | 22 ++++++++ src/ui/views/SceneChromeView.vue | 20 +------ src/ui/views/panels/GlobeActions.vue | 33 +++++++++++ 15 files changed, 242 insertions(+), 75 deletions(-) create mode 100644 src/ui/globeActions.test.mjs create mode 100644 src/ui/stores/globeActions.js create mode 100644 src/ui/views/panels/GlobeActions.vue diff --git a/CHANGELOG.md b/CHANGELOG.md index e30a553f3..0b54ac70b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,9 +72,11 @@ are the kit's new `UiChip`. The selects are the kit's new `UiSelect`, the status badges its new `UiBadge` and the one-line empty states its new `UiEmpty`. - `syncScopeControls` - (`./ui/display`) and `shouldHideCollapsedRightPanels` (`./ui/layout`) are - deprecated: the Display view and the rail now own what they did. `./ui/input` imports Vue + The top-center clear and share buttons call the shell through the new + globe actions store. `syncScopeControls` (`./ui/display`), + `shouldHideCollapsedRightPanels` (`./ui/layout`) and + `bindClearLayersControl` (`./ui/layers`) are deprecated: the Display view, + the rail and the globe actions view now own what they did. `./ui/input` imports Vue (samehkhamis). - `./ui/cctv` and `./ui/radio` import Vue and Pinia: their controllers diff --git a/docs/VUE-MIGRATION.md b/docs/VUE-MIGRATION.md index 950691aff..17156d7a6 100644 --- a/docs/VUE-MIGRATION.md +++ b/docs/VUE-MIGRATION.md @@ -225,7 +225,10 @@ a feature supplying only its accent. `splitFlap.js` plans, writing only the label's one text node; the toast renders what each owner's `createToast()` publishes there, on that owner's own timer, and the title bar's FPS readout what - `createFrameRateMonitor()` publishes), the Scene actions panel (`SceneInteractions` + `createFrameRateMonitor()` publishes), the globe actions (in progress: + `GlobeActions` renders the top-center bar; its clear and share buttons + call the shell through the `gev-globe-actions` store, which also carries + a clear under way), the Scene actions panel (`SceneInteractions` teleports what the scene interactions owner publishes to the `gev-scene-interactions` store into the viewer's container; the owner keeps the pick handler and focuses the picked action), the data-pack diff --git a/scripts/format-scope.json b/scripts/format-scope.json index 4fe6adf6f..35d4e7df4 100644 --- a/scripts/format-scope.json +++ b/scripts/format-scope.json @@ -1134,6 +1134,7 @@ "src/ui/kit/UiButton.test.mjs", "src/ui/kit/UiChip.vue", "src/ui/kit/UiChip.test.mjs", + "src/ui/globeActions.test.mjs", "src/ui/kit/UiSelect.js", "src/ui/kit/UiSelect.test.mjs", "src/ui/kit/UiBadge.vue", diff --git a/scripts/package-boundaries.json b/scripts/package-boundaries.json index e33d96f57..0c5be407d 100644 --- a/scripts/package-boundaries.json +++ b/scripts/package-boundaries.json @@ -138,7 +138,9 @@ "src/ui/kit/UiChip.vue", "src/ui/kit/UiSelect.js", "src/ui/kit/UiEmpty.vue", - "src/ui/kit/UiBadge.vue" + "src/ui/kit/UiBadge.vue", + "src/ui/stores/globeActions.js", + "src/ui/views/panels/GlobeActions.vue" ], "external": ["vue", "pinia"] }, @@ -1076,7 +1078,8 @@ "src/ui/rail/fieldValue.js", "src/ui/kit/UiSlider.js", "src/ui/kit/UiButton.vue", - "src/ui/kit/UiBadge.vue" + "src/ui/kit/UiBadge.vue", + "src/ui/stores/globeActions.js" ], "external": ["cesium", "vue", "pinia"] }, @@ -3089,7 +3092,8 @@ "src/ui/kit/UiSlider.js", "src/ui/kit/UiButton.vue", "src/ui/kit/UiEmpty.vue", - "src/ui/kit/UiBadge.vue" + "src/ui/kit/UiBadge.vue", + "src/ui/stores/globeActions.js" ], "external": [ "hls.js", diff --git a/scripts/qa-layer-panel.mjs b/scripts/qa-layer-panel.mjs index 1282c6b50..5cfa08905 100644 --- a/scripts/qa-layer-panel.mjs +++ b/scripts/qa-layer-panel.mjs @@ -87,17 +87,21 @@ try { row().querySelector('.data-toggle-btn').dataset.feedState === 'stale', ]); const ui = window.__godsEyeView.styleManager; - ui._clearSelectedLayersBtn.click(); + const clearButton = document.getElementById('clear-selected-layers'); + clearButton.click(); + // The bar renders the busy state on Vue's next flush. + await Promise.resolve(); result.push([ 'native clear activation presents busy state', - ui._clearSelectedLayersBtn.getAttribute('aria-busy') === 'true' && + clearButton.getAttribute('aria-busy') === 'true' && !!ui._contextControls._clearSelectedLayersPromise, ]); await ui._contextControls._clearSelectedLayersPromise; + await Promise.resolve(); result.push([ 'clear settles through the existing layer transaction', !manager.isEnabled(id) && - ui._clearSelectedLayersBtn.getAttribute('aria-busy') === 'false', + clearButton.getAttribute('aria-busy') === 'false', ]); await manager.setEnabled(id, true); const final = row().querySelector('.data-toggle-btn'); diff --git a/src/contextTabKeyboard.test.mjs b/src/contextTabKeyboard.test.mjs index 868122ec9..f50b30469 100644 --- a/src/contextTabKeyboard.test.mjs +++ b/src/contextTabKeyboard.test.mjs @@ -87,11 +87,13 @@ test('Context async action buttons remain focused while busy', () => { assert.ok(enable, 'Radio Enable button is missing'); assert.doesNotMatch(enable, /disabled(?!=")|:disabled/); assert.match(enable, /:aria-busy=/); - assert.doesNotMatch(clear, /_clearSelectedLayersBtn\.disabled\s*=\s*true/); assert.match(clear, /this\.setClearBusy\(true\)/); - const control = readFileSync(new URL('./ui/clearLayersControl.js', import.meta.url), 'utf8'); - assert.match(control, /button\.setAttribute\('aria-busy', String\(busy\)\)/); - assert.doesNotMatch(control, /button\.disabled\s*=/); + // The globe actions view marks the clear button busy, never disabled. + const globeActions = readFileSync(new URL('./ui/views/panels/GlobeActions.vue', import.meta.url), 'utf8'); + const clearButton = globeActions.match(/ - - - - - +
{{ loading ? loading.detail : '' }} diff --git a/src/ui/views/panels/GlobeActions.vue b/src/ui/views/panels/GlobeActions.vue new file mode 100644 index 000000000..ea058cdba --- /dev/null +++ b/src/ui/views/panels/GlobeActions.vue @@ -0,0 +1,33 @@ + + +