feat(WIP): add composables and dynamically calculate popup position

This commit is contained in:
Mazzella, Jesse D. (ARC-TI)[KBR Wyle Services, LLC] 2024-01-25 17:14:24 -08:00
parent f11e4aa7a1
commit 458b2822c3
3 changed files with 176 additions and 19 deletions
src
plugins/userIndicator/components
ui/composables

@ -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

@ -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));
}

@ -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 };
}