mirror of
https://github.com/nasa/openmct.git
synced 2025-06-06 01:11:41 +00:00
WIP: use flex layout container sizing for timeline
This commit is contained in:
parent
25a372e725
commit
4345722fe0
17
src/plugins/timeline/Container.js
Normal file
17
src/plugins/timeline/Container.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
class Container {
|
||||||
|
constructor(domainObject, size) {
|
||||||
|
this.domainObjectIdentifier = domainObject.identifier;
|
||||||
|
this.size = size;
|
||||||
|
this.scale = getContainerScale(domainObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContainerScale(domainObject) {
|
||||||
|
if (domainObject.type === 'telemetry.plot.stacked') {
|
||||||
|
return domainObject?.composition?.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Container;
|
@ -24,7 +24,6 @@
|
|||||||
<SwimLane
|
<SwimLane
|
||||||
:icon-class="item.type.definition.cssClass"
|
:icon-class="item.type.definition.cssClass"
|
||||||
:status="status"
|
:status="status"
|
||||||
:min-height="item.height"
|
|
||||||
:show-ucontents="isPlanLikeObject(item.domainObject)"
|
:show-ucontents="isPlanLikeObject(item.domainObject)"
|
||||||
:span-rows-count="item.rowCount"
|
:span-rows-count="item.rowCount"
|
||||||
:domain-object="item.domainObject"
|
:domain-object="item.domainObject"
|
||||||
@ -33,6 +32,7 @@
|
|||||||
:hide-button="!hasEventTelemetry()"
|
:hide-button="!hasEventTelemetry()"
|
||||||
:button-click-on="enableExtendEventLines"
|
:button-click-on="enableExtendEventLines"
|
||||||
:button-click-off="disableExtendEventLines"
|
:button-click-off="disableExtendEventLines"
|
||||||
|
:style="[{ 'flex-basis': sizeString }]"
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
{{ item.domainObject.name }}
|
{{ item.domainObject.name }}
|
||||||
@ -67,6 +67,10 @@ export default {
|
|||||||
extendedLinesBus: {
|
extendedLinesBus: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -76,6 +80,11 @@ export default {
|
|||||||
status: ''
|
status: ''
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
sizeString() {
|
||||||
|
return `${this.size}px`;
|
||||||
|
}
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
item(newItem) {
|
item(newItem) {
|
||||||
if (!this.context) {
|
if (!this.context) {
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
<TimelineObjectView
|
<TimelineObjectView
|
||||||
class="c-timeline__content js-timeline__content"
|
class="c-timeline__content js-timeline__content"
|
||||||
:item="item"
|
:item="item"
|
||||||
|
:size="getContainerSize(item)"
|
||||||
:extended-lines-bus
|
:extended-lines-bus
|
||||||
/>
|
/>
|
||||||
<ResizeHandle
|
<ResizeHandle
|
||||||
@ -71,8 +72,10 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import { identifierEquals } from 'objectUtils';
|
||||||
import { useDragResizer } from 'utils/vue/useDragResizer.js';
|
import { useDragResizer } from 'utils/vue/useDragResizer.js';
|
||||||
import { inject, provide } from 'vue';
|
import { useFlexContainers } from 'utils/vue/useFlexContainers.js';
|
||||||
|
import { inject, onMounted, provide, ref } from 'vue';
|
||||||
|
|
||||||
import SwimLane from '@/ui/components/swim-lane/SwimLane.vue';
|
import SwimLane from '@/ui/components/swim-lane/SwimLane.vue';
|
||||||
import ResizeHandle from '@/ui/layout/ResizeHandle/ResizeHandle.vue';
|
import ResizeHandle from '@/ui/layout/ResizeHandle/ResizeHandle.vue';
|
||||||
@ -80,6 +83,7 @@ import ResizeHandle from '@/ui/layout/ResizeHandle/ResizeHandle.vue';
|
|||||||
import TimelineAxis from '../../ui/components/TimeSystemAxis.vue';
|
import TimelineAxis from '../../ui/components/TimeSystemAxis.vue';
|
||||||
import { useAlignment } from '../../ui/composables/alignmentContext.js';
|
import { useAlignment } from '../../ui/composables/alignmentContext.js';
|
||||||
import { getValidatedData, getValidatedGroups } from '../plan/util.js';
|
import { getValidatedData, getValidatedGroups } from '../plan/util.js';
|
||||||
|
import Container from './Container.js';
|
||||||
import ExtendedLinesOverlay from './ExtendedLinesOverlay.vue';
|
import ExtendedLinesOverlay from './ExtendedLinesOverlay.vue';
|
||||||
import TimelineObjectView from './TimelineObjectView.vue';
|
import TimelineObjectView from './TimelineObjectView.vue';
|
||||||
|
|
||||||
@ -112,6 +116,8 @@ export default {
|
|||||||
const domainObject = inject('domainObject');
|
const domainObject = inject('domainObject');
|
||||||
const path = inject('path');
|
const path = inject('path');
|
||||||
const openmct = inject('openmct');
|
const openmct = inject('openmct');
|
||||||
|
const items = ref([]);
|
||||||
|
|
||||||
const { alignment: alignmentData, reset: resetAlignment } = useAlignment(
|
const { alignment: alignmentData, reset: resetAlignment } = useAlignment(
|
||||||
domainObject,
|
domainObject,
|
||||||
path,
|
path,
|
||||||
@ -124,11 +130,46 @@ export default {
|
|||||||
provide('swimLaneLabelWidth', swimLaneLabelWidth);
|
provide('swimLaneLabelWidth', swimLaneLabelWidth);
|
||||||
provide('mousedown', mousedown);
|
provide('mousedown', mousedown);
|
||||||
|
|
||||||
return { alignmentData, resetAlignment };
|
// Flex containers - Swimlane height
|
||||||
|
const timelineHolder = ref(null);
|
||||||
|
onMounted(() => {
|
||||||
|
console.log(timelineHolder.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
addContainer,
|
||||||
|
containers,
|
||||||
|
startContainerResizing,
|
||||||
|
containerResizing,
|
||||||
|
endContainerResizing
|
||||||
|
} = useFlexContainers(timelineHolder, {
|
||||||
|
containerClass: Container,
|
||||||
|
rowsLayout: true
|
||||||
|
});
|
||||||
|
|
||||||
|
function getContainerSize(item) {
|
||||||
|
const containerforItem = containers.value.find((container) =>
|
||||||
|
identifierEquals(container.domainObjectIdentifier, item.identifier)
|
||||||
|
);
|
||||||
|
|
||||||
|
return containerforItem.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
containers,
|
||||||
|
getContainerSize,
|
||||||
|
timelineHolder,
|
||||||
|
items,
|
||||||
|
addContainer,
|
||||||
|
alignmentData,
|
||||||
|
resetAlignment,
|
||||||
|
startContainerResizing,
|
||||||
|
containerResizing,
|
||||||
|
endContainerResizing
|
||||||
|
};
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
items: [],
|
|
||||||
timeSystems: [],
|
timeSystems: [],
|
||||||
height: 0,
|
height: 0,
|
||||||
useIndependentTime: this.domainObject.configuration.useIndependentTime === true,
|
useIndependentTime: this.domainObject.configuration.useIndependentTime === true,
|
||||||
@ -205,6 +246,7 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.items.push(item);
|
this.items.push(item);
|
||||||
|
this.addContainer(item);
|
||||||
},
|
},
|
||||||
removeItem(identifier) {
|
removeItem(identifier) {
|
||||||
let index = this.items.findIndex((item) =>
|
let index = this.items.findIndex((item) =>
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
display: contents;
|
display: contents;
|
||||||
|
|
||||||
.c-swimlane {
|
.c-swimlane {
|
||||||
min-height: 100px; // TEMP!! Will be replaced when heights are set by user
|
// min-height: 100px; // TEMP!! Will be replaced when heights are set by user
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
97
src/utils/vue/useFlexContainers.js
Normal file
97
src/utils/vue/useFlexContainers.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
export function useFlexContainers(element, { rowsLayout, minContainerSize = 5, callback }) {
|
||||||
|
const containers = ref([]);
|
||||||
|
const maxMoveSize = ref(null);
|
||||||
|
let sizingContainers = 0;
|
||||||
|
|
||||||
|
function addContainer(container) {
|
||||||
|
containers.value.push(container);
|
||||||
|
|
||||||
|
if (container.scale) {
|
||||||
|
sizingContainers += container.scale;
|
||||||
|
} else {
|
||||||
|
sizingContainers++;
|
||||||
|
}
|
||||||
|
|
||||||
|
sizeItems(containers.value, container);
|
||||||
|
|
||||||
|
callback?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
function startContainerResizing(index) {
|
||||||
|
const beforeContainer = containers.value[index];
|
||||||
|
const afterContainer = containers.value[index + 1];
|
||||||
|
|
||||||
|
maxMoveSize.value = beforeContainer.size + afterContainer.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
function containerResizing(index, delta, event) {
|
||||||
|
let percentageMoved = Math.round((delta / getElSize()) * 100);
|
||||||
|
let beforeContainer = containers.value[index];
|
||||||
|
let afterContainer = containers.value[index + 1];
|
||||||
|
|
||||||
|
beforeContainer.size = getContainerSize(beforeContainer.size + percentageMoved);
|
||||||
|
afterContainer.size = getContainerSize(afterContainer.size - percentageMoved);
|
||||||
|
}
|
||||||
|
|
||||||
|
function endContainerResizing() {
|
||||||
|
// persist the new container sizes
|
||||||
|
callback?.(containers.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getElSize() {
|
||||||
|
if (rowsLayout) {
|
||||||
|
return element.value.offsetHeight;
|
||||||
|
} else {
|
||||||
|
return element.value.offsetWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContainerSize(size) {
|
||||||
|
if (size < minContainerSize) {
|
||||||
|
return minContainerSize;
|
||||||
|
} else if (size > maxMoveSize.value - minContainerSize) {
|
||||||
|
return maxMoveSize.value - minContainerSize;
|
||||||
|
} else {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize items so that newItem fits proportionally (newItem must be an element
|
||||||
|
// of items). If newItem does not have a size or is sized at 100%, newItem will
|
||||||
|
// have size set to 1/n * 100, where n is the total number of items.
|
||||||
|
function sizeItems(items, newItem) {
|
||||||
|
const newItemScale = newItem.scale || 1;
|
||||||
|
|
||||||
|
if (items.length === 1) {
|
||||||
|
newItem.size = 100;
|
||||||
|
} else {
|
||||||
|
if (!newItem.size || newItem.size === 100) {
|
||||||
|
newItem.size = Math.round((100 * newItemScale) / sizingContainers);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize oldItems to fit inside remaining space;
|
||||||
|
const oldItems = items.filter((item) => item !== newItem);
|
||||||
|
const remainingSize = 100 - newItem.size;
|
||||||
|
|
||||||
|
oldItems.forEach((item) => {
|
||||||
|
const itemScale = item.scale || 1;
|
||||||
|
item.size = Math.round((item.size * itemScale * remainingSize) / 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ensure items add up to 100 in case of rounding error.
|
||||||
|
let total = items.reduce((t, item) => t + item.size, 0);
|
||||||
|
let excess = Math.round(100 - total);
|
||||||
|
oldItems[oldItems.length - 1].size += excess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
addContainer,
|
||||||
|
containers,
|
||||||
|
startContainerResizing,
|
||||||
|
containerResizing,
|
||||||
|
endContainerResizing
|
||||||
|
};
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user