mirror of
https://github.com/nasa/openmct.git
synced 2025-04-11 13:20:18 +00:00
feat(WIP): add composables and dynamically calculate popup position
This commit is contained in:
parent
f11e4aa7a1
commit
458b2822c3
src
@ -36,14 +36,15 @@
|
||||
</span>
|
||||
</div>
|
||||
<Teleport to="body">
|
||||
<div v-if="isPopupVisible" class="c-user-control-panel">
|
||||
<MissionStatusPopup v-if="canSetMissionStatus" />
|
||||
<div v-show="isPopupVisible" ref="popup" class="c-user-control-panel" :style="popupStyle">
|
||||
<MissionStatusPopup v-show="canSetMissionStatus" />
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ActiveRoleSynchronizer from '../../../api/user/ActiveRoleSynchronizer.js';
|
||||
import { useWindowResize } from '../../../ui/composables/resize.js';
|
||||
import MissionStatusPopup from './MissionStatusPopup.vue';
|
||||
|
||||
export default {
|
||||
@ -53,6 +54,11 @@ export default {
|
||||
},
|
||||
inject: ['openmct'],
|
||||
inheritAttrs: false,
|
||||
setup() {
|
||||
const { windowSize } = useWindowResize();
|
||||
|
||||
return { windowSize };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
userName: undefined,
|
||||
@ -66,35 +72,41 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
popupStyle() {
|
||||
return {
|
||||
top: `${this.position.top}px`,
|
||||
left: `${this.position.left}px`
|
||||
};
|
||||
},
|
||||
position() {
|
||||
const indicator = this.$refs.userIndicator;
|
||||
if (!indicator) {
|
||||
return {
|
||||
top: 0,
|
||||
left: 0
|
||||
};
|
||||
if (!this.isPopupVisible) {
|
||||
return { top: 0, left: 0 };
|
||||
}
|
||||
const indicator = this.$refs.userIndicator;
|
||||
const indicatorRect = indicator.getBoundingClientRect();
|
||||
const popup = this.$refs.popup;
|
||||
let top = indicatorRect.bottom;
|
||||
let left = indicatorRect.left;
|
||||
if (popup) {
|
||||
const popupRect = popup.getBoundingClientRect();
|
||||
top += popupRect.height;
|
||||
left += indicatorRect.width + popupRect.width;
|
||||
|
||||
const popupRect = this.$refs.popup.getBoundingClientRect();
|
||||
const popupWidth = popupRect.width;
|
||||
const popupHeight = popupRect.height;
|
||||
|
||||
// Check if the popup goes beyond the right edge of the window
|
||||
if (left + popupWidth > this.windowSize.width) {
|
||||
left = this.windowSize.width - popupWidth; // Adjust left to fit within the window
|
||||
}
|
||||
|
||||
return {
|
||||
top,
|
||||
left
|
||||
};
|
||||
// Check if the popup goes beyond the bottom edge of the window
|
||||
if (top + popupHeight > this.windowSize.height) {
|
||||
top = indicatorRect.top - popupHeight; // Place popup above the indicator
|
||||
}
|
||||
|
||||
return { top, left };
|
||||
}
|
||||
},
|
||||
|
||||
async created() {
|
||||
await this.getUserInfo();
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// need to wait for openmct to be loaded before using openmct.overlays.selection
|
||||
// as document.body could be null
|
||||
|
77
src/ui/composables/event.js
Normal file
77
src/ui/composables/event.js
Normal file
@ -0,0 +1,77 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2024, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/* eslint-disable func-style */
|
||||
|
||||
import { isRef, onBeforeMount, onBeforeUnmount, onMounted, watch } from 'vue';
|
||||
|
||||
/**
|
||||
* Registers an event listener on the specified target and automatically removes it when the
|
||||
* component is unmounted.
|
||||
* This is a Vue composition API utility function.
|
||||
* @param {EventTarget} target - The target to attach the event listener to.
|
||||
* @param {string} event - The name of the event to listen for.
|
||||
* @param {Function} callback - The callback function to execute when the event is triggered.
|
||||
*/
|
||||
export function useEventListener(target, event, handler) {
|
||||
const addListener = (el) => {
|
||||
if (el) {
|
||||
el.addEventListener(event, handler);
|
||||
}
|
||||
};
|
||||
|
||||
const removeListener = (el) => {
|
||||
if (el) {
|
||||
el.removeEventListener(event, handler);
|
||||
}
|
||||
};
|
||||
|
||||
// If target is a reactive ref, watch it for changes
|
||||
if (isRef(target)) {
|
||||
watch(
|
||||
target,
|
||||
(newTarget, oldTarget) => {
|
||||
if (newTarget !== oldTarget) {
|
||||
removeListener(oldTarget);
|
||||
addListener(newTarget);
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
} else {
|
||||
// Otherwise use lifecycle hooks to add/remove listener
|
||||
onMounted(() => addListener(target));
|
||||
onBeforeUnmount(() => removeListener(target));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an event listener on the specified EventEmitter instance and automatically removes it
|
||||
* when the component is unmounted.
|
||||
* This is a Vue composition API utility function.
|
||||
* @param {import('eventemitter3').EventEmitter} emitter - The EventEmitter instance to attach the event listener to.
|
||||
* @param {string} event - The name of the event to listen for.
|
||||
* @param {Function} callback - The callback function to execute when the event is triggered.
|
||||
*/
|
||||
export function useEventEmitter(emitter, event, callback) {
|
||||
onBeforeMount(() => emitter.on(event, callback));
|
||||
onBeforeUnmount(() => emitter.off(event, callback));
|
||||
}
|
68
src/ui/composables/resize.js
Normal file
68
src/ui/composables/resize.js
Normal file
@ -0,0 +1,68 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2024, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/* eslint-disable func-style */
|
||||
import { onBeforeUnmount, reactive } from 'vue';
|
||||
|
||||
import throttle from '../../utils/throttle.js';
|
||||
import { useEventListener } from './event.js';
|
||||
|
||||
export function useResizeObserver() {
|
||||
const size = reactive({ width: 0, height: 0 });
|
||||
let observer;
|
||||
|
||||
const startObserving = (element) => {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
observer = new ResizeObserver((entries) => {
|
||||
if (entries[0]) {
|
||||
const { width, height } = entries[0].contentRect;
|
||||
size.width = width;
|
||||
size.height = height;
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(element);
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (observer) {
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
return { size, startObserving };
|
||||
}
|
||||
|
||||
export function useWindowResize() {
|
||||
const windowSize = reactive({ width: window.innerWidth, height: window.innerHeight });
|
||||
|
||||
const handleResize = throttle(() => {
|
||||
windowSize.width = window.innerWidth;
|
||||
windowSize.height = window.innerHeight;
|
||||
}, 100);
|
||||
|
||||
useEventListener(window, 'resize', handleResize);
|
||||
|
||||
return { windowSize };
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user