mirror of
https://github.com/nasa/openmct.git
synced 2024-12-27 00:31:06 +00:00
use resizeObserver
instead of window listeners
safer access to `localStorage` initial state ie. legacy localStorage having `expanded` but not `multiline`
This commit is contained in:
parent
1e8e744b23
commit
89e8ed310e
@ -33,22 +33,17 @@
|
|||||||
:class="{
|
:class="{
|
||||||
'l-shell__head--expanded': headExpanded,
|
'l-shell__head--expanded': headExpanded,
|
||||||
'l-shell__head--minify-indicators': !headExpanded,
|
'l-shell__head--minify-indicators': !headExpanded,
|
||||||
'l-shell__head--indicators-single-line': !indicatorsMultiline,
|
'l-shell__head--indicators-single-line': !indicatorsMultiline
|
||||||
'--indicators-overflowing': indicatorsOverflowing
|
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<CreateButton class="l-shell__create-button" />
|
<CreateButton class="l-shell__create-button" />
|
||||||
<GrandSearch ref="grand-search" />
|
<GrandSearch ref="grand-search" />
|
||||||
<StatusIndicators
|
<StatusIndicators ref="indicatorsComponent" />
|
||||||
:head-expanded="headExpanded"
|
|
||||||
:indicators-multiline="indicatorsMultiline"
|
|
||||||
@indicators-overflowing="indicatorsOverflowUpdate"
|
|
||||||
/>
|
|
||||||
<button
|
<button
|
||||||
class="l-shell__head__button"
|
class="l-shell__head__button"
|
||||||
:class="[indicatorsMultilineCssClass, indicatorsOverflowingCssClass]"
|
:class="indicatorsMultilineCssClass"
|
||||||
:aria-label="`Display as ${indicatorsMultiline ? 'single line' : 'multiple lines'}`"
|
:aria-label="indicatorsMultilineLabel"
|
||||||
:title="`Display as ${indicatorsMultiline ? 'single line' : 'multiple lines'}`"
|
:title="indicatorsMultilineLabel"
|
||||||
@click="toggleIndicatorsMultiline"
|
@click="toggleIndicatorsMultiline"
|
||||||
></button>
|
></button>
|
||||||
<button
|
<button
|
||||||
@ -176,6 +171,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
|
||||||
import ObjectView from '../components/ObjectView.vue';
|
import ObjectView from '../components/ObjectView.vue';
|
||||||
import Inspector from '../inspector/InspectorPanel.vue';
|
import Inspector from '../inspector/InspectorPanel.vue';
|
||||||
import Toolbar from '../toolbar/ToolbarContainer.vue';
|
import Toolbar from '../toolbar/ToolbarContainer.vue';
|
||||||
@ -190,6 +187,10 @@ import GrandSearch from './search/GrandSearch.vue';
|
|||||||
import NotificationBanner from './status-bar/NotificationBanner.vue';
|
import NotificationBanner from './status-bar/NotificationBanner.vue';
|
||||||
import StatusIndicators from './status-bar/StatusIndicators.vue';
|
import StatusIndicators from './status-bar/StatusIndicators.vue';
|
||||||
|
|
||||||
|
const SHELL_HEAD_LOCAL_STORAGE_KEY = 'openmct-shell-head';
|
||||||
|
const DEFAULT_HEAD_EXPANDED = true;
|
||||||
|
const DEFAULT_INDICATORS_MULTILINE = true;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Inspector,
|
Inspector,
|
||||||
@ -207,15 +208,120 @@ export default {
|
|||||||
RecentObjectsList
|
RecentObjectsList
|
||||||
},
|
},
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
data: function () {
|
setup() {
|
||||||
let storedHeadProps = window.localStorage.getItem('openmct-shell-head');
|
let resizeObserver;
|
||||||
let headExpanded = true;
|
let element;
|
||||||
let indicatorsMultiline = true;
|
|
||||||
if (storedHeadProps) {
|
const storedHeadProps = localStorage.getItem(SHELL_HEAD_LOCAL_STORAGE_KEY);
|
||||||
headExpanded = JSON.parse(storedHeadProps).expanded;
|
const storedHeadPropsObject = JSON.parse(storedHeadProps);
|
||||||
indicatorsMultiline = JSON.parse(storedHeadProps).multiline;
|
const storedHeadExpanded = storedHeadPropsObject?.expanded;
|
||||||
|
const storedIndicatorsMultiline = storedHeadPropsObject?.multiline;
|
||||||
|
|
||||||
|
// template ref of StatusIndicators component
|
||||||
|
const indicatorsComponent = ref(null);
|
||||||
|
|
||||||
|
const width = ref(null);
|
||||||
|
const scrollWidth = ref(null);
|
||||||
|
const headExpanded = ref(storedHeadExpanded ?? DEFAULT_HEAD_EXPANDED);
|
||||||
|
const indicatorsMultiline = ref(storedIndicatorsMultiline ?? DEFAULT_INDICATORS_MULTILINE);
|
||||||
|
|
||||||
|
const isOverflowing = computed(() => scrollWidth.value > width.value);
|
||||||
|
const indicatorsMultilineCssClass = computed(() => {
|
||||||
|
const multilineClass = indicatorsMultiline.value ? 'icon-singleline' : 'icon-multiline';
|
||||||
|
const overflowingClass =
|
||||||
|
isOverflowing.value && !indicatorsMultiline.value
|
||||||
|
? 'c-button c-button--major'
|
||||||
|
: 'c-icon-button';
|
||||||
|
return `${multilineClass} ${overflowingClass}`;
|
||||||
|
});
|
||||||
|
const indicatorsMultilineLabel = computed(() => {
|
||||||
|
return `Display as ${indicatorsMultiline.value ? 'single line' : 'multiple lines'}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialHeadProps = JSON.stringify({
|
||||||
|
expanded: headExpanded.value,
|
||||||
|
multiline: indicatorsMultiline.value
|
||||||
|
});
|
||||||
|
|
||||||
|
if (initialHeadProps !== storedHeadProps) {
|
||||||
|
localStorage.setItem(SHELL_HEAD_LOCAL_STORAGE_KEY, initialHeadProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
resizeObserver = new ResizeObserver((entries) => {
|
||||||
|
width.value = entries[0].target.clientWidth;
|
||||||
|
scrollWidth.value = entries[0].target.scrollWidth;
|
||||||
|
});
|
||||||
|
|
||||||
|
// indicatorsContainer is a template ref inside of indicatorsComponent
|
||||||
|
element = indicatorsComponent.value.$refs.indicatorsContainer;
|
||||||
|
|
||||||
|
if (!indicatorsMultiline.value) {
|
||||||
|
observeIndicatorsOverflow();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
});
|
||||||
|
|
||||||
|
function observeIndicatorsOverflow() {
|
||||||
|
resizeObserver.observe(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unObserveIndicatorsOverflow() {
|
||||||
|
resizeObserver.unobserve(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkIndicatorsElementWidths() {
|
||||||
|
if (!indicatorsMultiline.value) {
|
||||||
|
width.value = element.clientWidth;
|
||||||
|
scrollWidth.value = element.scrollWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toggleShellHead() {
|
||||||
|
headExpanded.value = !headExpanded.value;
|
||||||
|
setLocalStorageShellHead();
|
||||||
|
|
||||||
|
// nextTick is used because the element width on toggle is updated using css
|
||||||
|
await nextTick();
|
||||||
|
checkIndicatorsElementWidths();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleIndicatorsMultiline() {
|
||||||
|
indicatorsMultiline.value = !indicatorsMultiline.value;
|
||||||
|
setLocalStorageShellHead();
|
||||||
|
|
||||||
|
if (indicatorsMultiline.value) {
|
||||||
|
unObserveIndicatorsOverflow();
|
||||||
|
} else {
|
||||||
|
observeIndicatorsOverflow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLocalStorageShellHead() {
|
||||||
|
localStorage.setItem(
|
||||||
|
SHELL_HEAD_LOCAL_STORAGE_KEY,
|
||||||
|
JSON.stringify({
|
||||||
|
expanded: headExpanded.value,
|
||||||
|
multiline: indicatorsMultiline.value
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
indicatorsComponent,
|
||||||
|
isOverflowing,
|
||||||
|
headExpanded,
|
||||||
|
indicatorsMultiline,
|
||||||
|
indicatorsMultilineCssClass,
|
||||||
|
indicatorsMultilineLabel,
|
||||||
|
toggleIndicatorsMultiline,
|
||||||
|
toggleShellHead
|
||||||
|
};
|
||||||
|
},
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
fullScreen: false,
|
fullScreen: false,
|
||||||
conductorComponent: undefined,
|
conductorComponent: undefined,
|
||||||
@ -224,9 +330,6 @@ export default {
|
|||||||
actionCollection: undefined,
|
actionCollection: undefined,
|
||||||
triggerSync: false,
|
triggerSync: false,
|
||||||
triggerReset: false,
|
triggerReset: false,
|
||||||
headExpanded,
|
|
||||||
indicatorsMultiline,
|
|
||||||
indicatorsOverflowing: false,
|
|
||||||
isResizing: false,
|
isResizing: false,
|
||||||
disableClearButton: false
|
disableClearButton: false
|
||||||
};
|
};
|
||||||
@ -237,12 +340,6 @@ export default {
|
|||||||
},
|
},
|
||||||
resizingClass() {
|
resizingClass() {
|
||||||
return this.isResizing ? 'l-shell__resizing' : '';
|
return this.isResizing ? 'l-shell__resizing' : '';
|
||||||
},
|
|
||||||
indicatorsMultilineCssClass() {
|
|
||||||
return this.indicatorsMultiline ? 'icon-singleline' : 'icon-multiline';
|
|
||||||
},
|
|
||||||
indicatorsOverflowingCssClass() {
|
|
||||||
return this.indicatorsOverflowing ? 'c-button c-button--major' : 'c-icon-button';
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -280,23 +377,6 @@ export default {
|
|||||||
document.msExitFullscreen();
|
document.msExitFullscreen();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toggleShellHead() {
|
|
||||||
this.headExpanded = !this.headExpanded;
|
|
||||||
this.setLocalStorageShellHead();
|
|
||||||
},
|
|
||||||
toggleIndicatorsMultiline() {
|
|
||||||
this.indicatorsMultiline = !this.indicatorsMultiline;
|
|
||||||
this.setLocalStorageShellHead();
|
|
||||||
},
|
|
||||||
setLocalStorageShellHead() {
|
|
||||||
window.localStorage.setItem(
|
|
||||||
'openmct-shell-head',
|
|
||||||
JSON.stringify({
|
|
||||||
expanded: this.headExpanded,
|
|
||||||
multiline: this.indicatorsMultiline
|
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
|
||||||
fullScreenToggle() {
|
fullScreenToggle() {
|
||||||
if (this.fullScreen) {
|
if (this.fullScreen) {
|
||||||
this.fullScreen = false;
|
this.fullScreen = false;
|
||||||
@ -344,9 +424,6 @@ export default {
|
|||||||
},
|
},
|
||||||
setClearButtonDisabled(isDisabled) {
|
setClearButtonDisabled(isDisabled) {
|
||||||
this.disableClearButton = isDisabled;
|
this.disableClearButton = isDisabled;
|
||||||
},
|
|
||||||
indicatorsOverflowUpdate(isOverflowing) {
|
|
||||||
this.indicatorsOverflowing = isOverflowing;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -274,10 +274,6 @@
|
|||||||
margin-left: auto; // Mimics justify-content: flex-end when in single line mode.
|
margin-left: auto; // Mimics justify-content: flex-end when in single line mode.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.--is-overflowing {
|
|
||||||
background: rgba(deeppink, 0.1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************* MAIN AREA */
|
/******************************* MAIN AREA */
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div ref="indicators" class="l-shell__head-section l-shell__indicators">
|
<div ref="indicatorsContainer" class="l-shell__head-section l-shell__indicators">
|
||||||
<component
|
<component
|
||||||
:is="indicator.value.vueComponent"
|
:is="indicator.value.vueComponent"
|
||||||
v-for="indicator in sortedIndicators"
|
v-for="indicator in sortedIndicators"
|
||||||
@ -28,21 +28,15 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { shallowRef } from 'vue';
|
import { defineExpose, ref, shallowRef } from 'vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
props: {
|
setup() {
|
||||||
headExpanded: {
|
const indicatorsContainer = ref(null);
|
||||||
type: Boolean,
|
|
||||||
required: true
|
defineExpose({ indicatorsContainer });
|
||||||
},
|
|
||||||
indicatorsMultiline: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
emits: ['indicators-overflowing'],
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
indicators: this.openmct.indicators.getIndicatorObjectsByPriority().map(shallowRef)
|
indicators: this.openmct.indicators.getIndicatorObjectsByPriority().map(shallowRef)
|
||||||
@ -57,30 +51,8 @@ export default {
|
|||||||
return [...this.indicators].sort((a, b) => b.value.priority - a.value.priority);
|
return [...this.indicators].sort((a, b) => b.value.priority - a.value.priority);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
headExpanded() {
|
|
||||||
this.checkOverflowNextTick();
|
|
||||||
},
|
|
||||||
indicatorsMultiline() {
|
|
||||||
if (!this.indicatorsMultiline) {
|
|
||||||
window.addEventListener('resize', this.checkOverflow);
|
|
||||||
} else {
|
|
||||||
window.removeEventListener('resize', this.checkOverflow);
|
|
||||||
}
|
|
||||||
this.checkOverflowNextTick();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
if (!this.indicatorsMultiline) {
|
|
||||||
// `load` listener is necessary because the width of the Indicators has to be eval'd after other components have loaded.
|
|
||||||
window.addEventListener('load', this.checkOverflow);
|
|
||||||
window.addEventListener('resize', this.checkOverflow);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
this.openmct.indicators.off('addIndicator', this.addIndicator);
|
this.openmct.indicators.off('addIndicator', this.addIndicator);
|
||||||
window.removeEventListener('load', this.checkOverflow);
|
|
||||||
window.removeEventListener('resize', this.checkOverflow);
|
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.openmct.indicators.on('addIndicator', this.addIndicator);
|
this.openmct.indicators.on('addIndicator', this.addIndicator);
|
||||||
@ -88,15 +60,6 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
addIndicator(indicator) {
|
addIndicator(indicator) {
|
||||||
this.indicators.push(shallowRef(indicator));
|
this.indicators.push(shallowRef(indicator));
|
||||||
},
|
|
||||||
checkOverflow() {
|
|
||||||
const element = this.$refs.indicators;
|
|
||||||
this.$emit('indicators-overflowing', element.scrollWidth > element.clientWidth);
|
|
||||||
},
|
|
||||||
checkOverflowNextTick() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.checkOverflow();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user