Compare commits

...

5 Commits

Author SHA1 Message Date
Deep Tailor
fe00a098f5 add reorder capabilities 2021-01-05 17:31:52 -08:00
Deep Tailor
2ea60ee638 slight improvements 2021-01-05 17:22:45 -08:00
Deep Tailor
30997e677d add drag to move 2021-01-05 17:01:18 -08:00
Deep Tailor
8f3492963a add absolute positioning of activities 2021-01-05 15:00:08 -08:00
Deep Tailor
9e68843651 first pass proto 2021-01-05 14:34:12 -08:00
8 changed files with 307 additions and 2 deletions

View File

@@ -193,6 +193,7 @@
['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'],
{indicator: true}
));
openmct.install(openmct.plugins.ApresTimeline());
openmct.start();
</script>
</html>

View File

@@ -0,0 +1,64 @@
<template>
<div style="min-width: 100%">
<ul style="min-width: 100%; min-height: 100%; position: relative;">
<timeline-activity
v-for="(activityDomainObject, index) in activities"
:key="activityDomainObject.identifier.key"
:domainObject="activityDomainObject"
:index="index"
:isEditing="isEditing"
/>
</ul>
</div>
</template>
<script>
import TimelineActivity from './timelineActivity.vue';
export default {
inject: ['openmct', 'objectPath', 'domainObject'],
props: {
isEditing: {
type: Boolean
}
},
components: {
TimelineActivity
},
methods: {
addActivity(activityDomainObject) {
this.activities.push(activityDomainObject);
},
removeActivity(activityIndentifier) {
console.log(activityIndentifier);
},
reorderActivities(reorderPlan) {
let oldActivities = this.activities.slice();
reorderPlan.forEach((reorderEvent) => {
this.$set(this.activities, reorderEvent.newIndex, oldActivities[reorderEvent.oldIndex]);
});
}
},
data() {
return {
activities: [],
composition: this.openmct.composition.get(this.domainObject),
activityHeight: 0
}
},
mounted() {
this.composition.on('add', this.addActivity);
this.composition.on('remove', this.removeActivity);
this.composition.on('reorder', this.reorderActivities);
this.composition.load();
},
beforeDestroy() {
this.composition.off('add', this.addActivity);
this.composition.off('remove', this.removeActivity);
}
}
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,110 @@
<template>
<li
:style="activityStyle"
@mousedown="onMouseDown"
>
{{ name }}
</li>
</template>
<script>
const PIXEL_MULTIPLIER = 30;
export default {
inject: ['openmct'],
props: {
domainObject: {
type: Object,
required: true,
default() {
return {
configuration: {}
}
}
},
index: {
type: Number
},
isEditing: {
type: Boolean
}
},
computed: {
activityStyle() {
return {
'position': 'absolute',
'top': `${this.index * (this.activityHeight + 4)}px`,
'left': `${this.start * PIXEL_MULTIPLIER}px`,
'backgroundColor': this.color,
'width': `${this.width * PIXEL_MULTIPLIER}px`,
'padding': '10px'
};
}
},
data() {
let configuration = this.domainObject.configuration;
return {
name: this.domainObject.name,
start: configuration.startTime,
end: configuration.endTime,
width: configuration.endTime - configuration.startTime,
color: configuration.color,
activityHeight: 0
}
},
methods: {
onDomainObjectChange(domainObject) {
let configuration = domainObject.configuration;
this.name = domainObject.name;
this.start = configuration.startTime;
this.end = configuration.endTime;
this.width = this.end - this.start;
this.color = configuration.color;
},
onMouseDown(event) {
if (!this.isEditing) {
return;
}
event.preventDefault();
document.addEventListener('mousemove', this.move);
document.addEventListener('mouseup', this.endMove);
this.clientX = event.clientX;
},
move(event) {
let delta = (event.clientX - this.clientX) / PIXEL_MULTIPLIER;
this.start += delta;
this.end = this.start + this.width;
this.clientX = event.clientX;
},
endMove() {
document.removeEventListener('mousemove', this.move);
document.removeEventListener('mouseup', this.endMove);
this.persistMove();
},
persistMove() {
let configuration = {
startTime: this.start,
endTime: this.end,
color: this.color
};
this.openmct.objects.mutate(this.domainObject, 'configuration', configuration);
}
},
mounted() {
this.unsubscribeFromDomainObjectChanges = this.openmct.objects.observe(this.domainObject, '*', this.onDomainObjectChange);
let boundingClientRect = this.$el.getBoundingClientRect();
this.activityHeight = boundingClientRect.height;
},
beforeDestroy() {
this.unsubscribeFromDomainObjectChanges();
}
}
</script>

View File

@@ -0,0 +1,72 @@
import ActivityViewProvider from './activityViewProvider';
import TimelineViewProvider from './timelineViewProvider';
export default function () {
return function install(openmct) {
openmct.types.addType(
'apres.timeline.type',
{
name: 'Apres Timeline',
cssClass: 'icon-timeline',
creatable: true,
initialize: function (domainObject) {
domainObject.composition = [];
}
}
);
openmct.types.addType(
'apres.activity.type',
{
name: 'Apres Timeline Activity',
cssClass: 'icon-activity',
creatable: true,
initialize: function (domainObject) {
domainObject.configuration = {
startTime: 0,
endTime: 10,
color: 'rebeccapurple'
};
},
form: [
{
name: "Start Time",
control: "numberfield",
cssClass: "l-input-sm l-numeric",
key: "startTime",
required: true,
property: [
"configuration",
"startTime"
]
},
{
name: "End Time",
control: "numberfield",
cssClass: "l-input-sm l-numeric",
key: "endTime",
required: true,
property: [
"configuration",
"endTime"
]
},
{
name: "Color",
control: "textfield",
cssClass: "l-input-sm l-text",
key: "color",
required: true,
property: [
"configuration",
"color"
]
}
]
}
);
openmct.objectViews.addProvider(new TimelineViewProvider(openmct));
// openmct.objectViews.addProvider(new ActivityViewProvider(openmct));
}
};

View File

@@ -0,0 +1,55 @@
import Vue from 'vue';
import timelineComponent from './components/timeline.vue';
export default class TimelineViewProvider{
constructor(openmct) {
this._openmct = openmct;
this.name = 'Timeline';
this.key = 'apres.timeline.view';
this.priority = 1;
}
canView(domainObject) {
return domainObject.type === 'apres.timeline.type';
}
canEdit(domainObject) {
return domainObject.type === 'apres.timeline.type';
}
view(domainObject, objectPath, isEditing) {
let component;
return {
show: (element) => {
component = new Vue({
el: element,
components: {
timelineComponent: timelineComponent
},
data() {
return {
isEditing: isEditing
}
},
provide: {
openmct,
domainObject,
objectPath
},
template: ` <timeline-component
:isEditing="isEditing"
>
</timeline-component>`
});
},
onEditModeChange: (isEditing) => {
component.isEditing = isEditing;
},
destroy: () => {
component.$destroy();
}
}
}
};

View File

@@ -61,7 +61,8 @@ define([
'./timeline/plugin',
'./viewDatumAction/plugin',
'./interceptors/plugin',
'./performanceIndicator/plugin'
'./performanceIndicator/plugin',
'./apresTimeline/plugin'
], function (
_,
UTCTimeSystem,
@@ -103,7 +104,8 @@ define([
Timeline,
ViewDatumAction,
ObjectInterceptors,
PerformanceIndicator
PerformanceIndicator,
ApresTimeline
) {
const bundleMap = {
LocalStorage: 'platform/persistence/local',
@@ -200,6 +202,7 @@ define([
plugins.ViewDatumAction = ViewDatumAction.default;
plugins.ObjectInterceptors = ObjectInterceptors.default;
plugins.PerformanceIndicator = PerformanceIndicator.default;
plugins.ApresTimeline = ApresTimeline.default;
return plugins;
});