mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
feat(scenes): render the Scene actions panel from a store
The scene interactions owner keeps the pick handler and the interaction session and presents the panel to the gev-scene-interactions store; the SceneInteractions view teleports it into the viewer's container with the same markup and inline styles. Each activation keys its own panel, each button carries the owner it was rendered for, and only the owner the store shows may change or remove it.
This commit is contained in:
1 parent
6398103b60
commit
59e3aaa271
8 files changed
+376
-104
No files matched your search
@@ -0,0 +1,35 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { markRaw, shallowRef } from 'vue';
|
||||
|
||||
/**
|
||||
* The active shot's Scene actions panel as its view renders it. The scene
|
||||
* interactions owner (src/scenes/interactions.js) presents the actions, the
|
||||
* status line and the source card here; the view sends action clicks back to
|
||||
* the connected controls.
|
||||
*/
|
||||
export const useSceneInteractionsStore = defineStore(
|
||||
'gev-scene-interactions',
|
||||
() => {
|
||||
/**
|
||||
* `{id, owner, container, items, status, busy, selected, outlined,
|
||||
* card}`, or null while the shot has no actions. `container` is the
|
||||
* element it sits in; `owner` is passed back with each dispatch.
|
||||
*/
|
||||
const panel = shallowRef(null);
|
||||
|
||||
/** @param {?object} next A fresh presentation; every publish re-renders. */
|
||||
function publish(next) {
|
||||
panel.value = next && markRaw(next);
|
||||
}
|
||||
|
||||
/** The controls whose `dispatch(id, owner)` an action button calls. */
|
||||
const controls = shallowRef(null);
|
||||
|
||||
/** @param {?object} next The controls, or null to disconnect. */
|
||||
function connect(next) {
|
||||
controls.value = next && markRaw(next);
|
||||
}
|
||||
|
||||
return { panel, publish, controls, connect };
|
||||
},
|
||||
);
|
||||
@@ -6,6 +6,7 @@ import { useLocationStore } from '../stores/location.js';
|
||||
import { useStatusStore } from '../stores/status.js';
|
||||
import { vClassToggle } from '../rail/classToggle.js';
|
||||
import ImagerySplitView from './panels/ImagerySplitView.vue';
|
||||
import SceneInteractions from './panels/SceneInteractions.vue';
|
||||
import SplitFlapText from './panels/SplitFlapText.vue';
|
||||
|
||||
// The ACTIVE STYLE corner shows the style VisualSettings publishes.
|
||||
@@ -95,4 +96,5 @@ const vToastText = {
|
||||
</Teleport>
|
||||
|
||||
<ImagerySplitView />
|
||||
<SceneInteractions />
|
||||
</template>
|
||||
@@ -0,0 +1,50 @@
|
||||
<script setup>
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { t } from '../../../i18n/index.js';
|
||||
import { useSceneInteractionsStore } from '../../stores/sceneInteractions.js';
|
||||
|
||||
// The active shot's Scene actions panel, inside the viewer's container, as
|
||||
// the scene interactions owner (src/scenes/interactions.js) presents it.
|
||||
const { panel, controls } = storeToRefs(useSceneInteractionsStore());
|
||||
|
||||
const panelStyle = {
|
||||
position: 'absolute',
|
||||
left: '12px',
|
||||
bottom: '80px',
|
||||
zIndex: '31',
|
||||
maxWidth: '300px',
|
||||
maxHeight: '40vh',
|
||||
overflowY: 'auto',
|
||||
background: '#08141eed',
|
||||
color: '#e3faff',
|
||||
border: '1px solid #2491a8',
|
||||
padding: '10px',
|
||||
font: '13px sans-serif',
|
||||
};
|
||||
const buttonStyle = {
|
||||
display: 'block',
|
||||
background: '#10242e',
|
||||
color: '#e3faff',
|
||||
border: '1px solid #397080',
|
||||
borderRadius: '3px',
|
||||
padding: '6px 10px',
|
||||
cursor: 'pointer',
|
||||
font: 'inherit',
|
||||
margin: '6px 0',
|
||||
maxWidth: '100%',
|
||||
whiteSpace: 'normal',
|
||||
};
|
||||
// Each button keeps the panel it was rendered for, so one left over from an
|
||||
// earlier panel cannot run the current actions.
|
||||
const onClick = (owner, id) => () => controls.value?.dispatch(id, owner);
|
||||
const outline = (id) => ({
|
||||
...buttonStyle,
|
||||
outline: panel.value.outlined.includes(id) ? '2px solid #6eeaff' : '',
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport v-if="panel" :to="panel.container">
|
||||
<section :key="panel.id" data-director-interactions="" :aria-label="t('Scene actions')" :style="panelStyle"><strong>{{ t('Scene actions') }}</strong><p role="status">{{ panel.status }}</p><button v-for="item in panel.items" :key="item.id" type="button" :data-director-action="item.id" :disabled="panel.busy" :aria-pressed="String(panel.selected === item.id)" :style="outline(item.id)" :onClick="onClick(panel.owner, item.id)">{{ item.label }}</button><div data-director-action-card=""><template v-if="panel.card"><p>{{ panel.card.text }}</p><a v-if="panel.card.url" :href="panel.card.url" target="_blank" rel="noopener noreferrer" style="color: #6eeaff">{{ t('Source') }}</a></template></div></section>
|
||||
</Teleport>
|
||||
</template>
|
||||
Reference in new issue
Block a user