Refine display options and add Independent Time Conductor option for Time List view (#7161)

* Apply sort settings immediately - even when in edit mode.

* Adds test for sort order

* Enable independent time conductor for time list view

* Remove time frame duration options.

* Remove immediate sorting in edit mode.

* Closes #7113
- Color of current events changed to bring more in-line with color conventions.
- Changed Time List rgba colors to solids.
- Removed bolding on current events text.

* Fix tests to include new changes

---------

Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
This commit is contained in:
Shefali Joshi 2023-11-01 08:47:43 -07:00 committed by GitHub
parent a0fd1f0171
commit ae22920576
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 99 additions and 166 deletions

View File

@ -116,8 +116,10 @@ export default {
},
mounted() {
this.isEditing = this.openmct.editor.isEditing();
this.timestamp = this.openmct.time.now();
this.openmct.time.on(TIME_CONTEXT_EVENTS.modeChanged, this.setFixedTime);
this.updateTimestamp = _.throttle(this.updateTimestamp, 1000);
this.setTimeContext();
this.timestamp = this.timeContext.now();
this.getPlanDataAndSetConfig(this.domainObject);
@ -137,8 +139,6 @@ export default {
);
this.status = this.openmct.status.get(this.domainObject.identifier);
this.updateTimestamp = _.throttle(this.updateTimestamp, 1000);
this.openmct.time.on('tick', this.updateTimestamp);
this.openmct.editor.on('isEditing', this.setEditState);
this.deferAutoScroll = _.debounce(this.deferAutoScroll, 500);
@ -150,7 +150,7 @@ export default {
this.composition.load();
}
this.setFixedTime(this.openmct.time.getMode());
this.setFixedTime(this.timeContext.getMode());
},
beforeUnmount() {
if (this.unlisten) {
@ -166,8 +166,7 @@ export default {
}
this.openmct.editor.off('isEditing', this.setEditState);
this.openmct.time.off('tick', this.updateTimestamp);
this.openmct.time.off(TIME_CONTEXT_EVENTS.modeChanged, this.setFixedTime);
this.stopFollowingTimeContext();
this.$el.parentElement?.removeEventListener('scroll', this.deferAutoScroll, true);
if (this.clearAutoScrollDisabledTimer) {
@ -180,6 +179,21 @@ export default {
}
},
methods: {
setTimeContext() {
this.stopFollowingTimeContext();
this.timeContext = this.openmct.time.getContextForView(this.path);
this.followTimeContext();
},
followTimeContext() {
this.timeContext.on(TIME_CONTEXT_EVENTS.modeChanged, this.setFixedTime);
this.timeContext.on(TIME_CONTEXT_EVENTS.tick, this.updateTimestamp);
},
stopFollowingTimeContext() {
if (this.timeContext) {
this.timeContext.off(TIME_CONTEXT_EVENTS.modeChanged, this.setFixedTime);
this.timeContext.off(TIME_CONTEXT_EVENTS.tick, this.updateTimestamp);
}
},
planFileUpdated(selectFile) {
this.getPlanData({
selectFile,
@ -198,7 +212,6 @@ export default {
} else {
this.filterValue = configuration.filter;
this.setSort();
this.setViewBounds();
this.listActivities();
}
},
@ -208,7 +221,7 @@ export default {
},
setFixedTime() {
this.filterValue = this.domainObject.configuration.filter;
this.isFixedTime = !this.openmct.time.isRealTime();
this.isFixedTime = !this.timeContext.isRealTime();
if (this.isFixedTime) {
this.hideAll = false;
}
@ -269,71 +282,6 @@ export default {
getPlanData(domainObject) {
this.planData = getValidatedData(domainObject);
},
setViewBounds() {
const pastEventsIndex = this.domainObject.configuration.pastEventsIndex;
const currentEventsIndex = this.domainObject.configuration.currentEventsIndex;
const futureEventsIndex = this.domainObject.configuration.futureEventsIndex;
const pastEventsDuration = this.domainObject.configuration.pastEventsDuration;
const pastEventsDurationIndex = this.domainObject.configuration.pastEventsDurationIndex;
const futureEventsDuration = this.domainObject.configuration.futureEventsDuration;
const futureEventsDurationIndex = this.domainObject.configuration.futureEventsDurationIndex;
if (pastEventsIndex === 0 && futureEventsIndex === 0 && currentEventsIndex === 0) {
this.viewBounds = undefined;
this.hideAll = true;
return;
}
this.hideAll = false;
if (pastEventsIndex === 1 && futureEventsIndex === 1 && currentEventsIndex === 1) {
this.viewBounds = undefined;
return;
}
this.viewBounds = {};
if (pastEventsIndex !== 1) {
const pastDurationInMS = this.getDurationInMilliSeconds(
pastEventsDuration,
pastEventsDurationIndex
);
this.viewBounds.pastEnd = (timestamp) => {
if (pastEventsIndex === 2) {
return timestamp - pastDurationInMS;
} else if (pastEventsIndex === 0) {
return timestamp + 1;
}
};
}
if (futureEventsIndex !== 1) {
const futureDurationInMS = this.getDurationInMilliSeconds(
futureEventsDuration,
futureEventsDurationIndex
);
this.viewBounds.futureStart = (timestamp) => {
if (futureEventsIndex === 2) {
return timestamp + futureDurationInMS;
} else if (futureEventsIndex === 0) {
return 0;
}
};
}
},
getDurationInMilliSeconds(duration, durationIndex) {
if (duration > 0) {
if (durationIndex === 0) {
return duration * 1000;
} else if (durationIndex === 1) {
return duration * 60 * 1000;
} else if (durationIndex === 2) {
return duration * 60 * 60 * 1000;
}
}
},
listActivities() {
let groups = Object.keys(this.planData);
let activities = [];
@ -356,18 +304,18 @@ export default {
},
isActivityInBounds(activity) {
const startInBounds =
activity.start >= this.openmct.time.bounds()?.start &&
activity.start <= this.openmct.time.bounds()?.end;
activity.start >= this.timeContext.bounds()?.start &&
activity.start <= this.timeContext.bounds()?.end;
const endInBounds =
activity.end >= this.openmct.time.bounds()?.start &&
activity.end <= this.openmct.time.bounds()?.end;
activity.end >= this.timeContext.bounds()?.start &&
activity.end <= this.timeContext.bounds()?.end;
const middleInBounds =
activity.start <= this.openmct.time.bounds()?.start &&
activity.end >= this.openmct.time.bounds()?.end;
activity.start <= this.timeContext.bounds()?.start &&
activity.end >= this.timeContext.bounds()?.end;
return startInBounds || endInBounds || middleInBounds;
},
filterActivities(activity, index) {
filterActivities(activity) {
if (this.isEditing) {
return true;
}
@ -381,15 +329,12 @@ export default {
return false;
}
//current event or future start event or past end event
const isCurrent = this.timestamp >= activity.start && this.timestamp <= activity.end;
const isPast =
this.timestamp > activity.end &&
(this.viewBounds?.pastEnd === undefined ||
activity.end >= this.viewBounds?.pastEnd(this.timestamp));
const isFuture =
this.timestamp < activity.start &&
(this.viewBounds?.futureStart === undefined ||
activity.start <= this.viewBounds?.futureStart(this.timestamp));
const showCurrentEvents = this.domainObject.configuration.currentEventsIndex > 0;
const isCurrent =
showCurrentEvents && this.timestamp >= activity.start && this.timestamp <= activity.end;
const isPast = this.timestamp > activity.end;
const isFuture = this.timestamp < activity.start;
return isCurrent || isPast || isFuture;
},

View File

@ -32,34 +32,15 @@
{{ activityOption }}
</option>
</select>
<input
v-if="index === 2"
v-model="duration"
class="c-input c-input--sm"
type="text"
@change="updateForm('duration')"
/>
<select v-if="index === 2" v-model="durationIndex" @change="updateForm('durationIndex')">
<option
v-for="(durationOption, durationKey) in durationOptions"
:key="durationKey"
:value="durationKey"
>
{{ durationOption }}
</option>
</select>
</div>
<div v-else class="c-inspect-properties__value">
{{ activitiesOptions[index] }}
<span v-if="index > 1">{{ duration }} {{ durationOptions[durationIndex] }}</span>
</div>
</li>
</template>
<script>
const ACTIVITIES_OPTIONS = ["Don't show", 'Show all', 'Show starts within', 'Show after end for'];
const DURATION_OPTIONS = ['seconds', 'minutes', 'hours'];
const ACTIVITIES_OPTIONS = ["Don't show", 'Show all'];
export default {
inject: ['openmct', 'domainObject'],
@ -76,11 +57,8 @@ export default {
emits: ['updated'],
data() {
return {
index: this.domainObject.configuration[`${this.prefix}Index`],
durationIndex: this.domainObject.configuration[`${this.prefix}DurationIndex`],
duration: this.domainObject.configuration[`${this.prefix}Duration`],
index: this.domainObject.configuration[`${this.prefix}Index`] % 2, //this is modulo since we previously had more options and index could have been > 1
activitiesOptions: ACTIVITIES_OPTIONS,
durationOptions: DURATION_OPTIONS,
isEditing: this.openmct.editor.isEditing()
};
},
@ -116,7 +94,7 @@ export default {
});
},
isValid() {
return this.index < 2 || (this.durationIndex >= 0 && this.duration > 0);
return this.index <= 1;
},
setEditState(isEditing) {
this.isEditing = isEditing;

View File

@ -24,7 +24,7 @@
<div class="c-timelist-properties">
<div class="c-inspect-properties">
<ul class="c-inspect-properties__section">
<div class="c-inspect-properties_header" title="'Timeframe options'">Timeframe</div>
<div class="c-inspect-properties_header" title="'Display options'">Display Options</div>
<li class="c-inspect-properties__row">
<div v-if="canEdit" class="c-inspect-properties__hint span-all">
These settings don't affect the view while editing, but will be applied after editing is
@ -72,17 +72,9 @@ import EventProperties from './EventProperties.vue';
import Filtering from './FilteringComponent.vue';
const EVENT_TYPES = [
{
label: 'Future Events',
prefix: 'futureEvents'
},
{
label: 'Current Events',
prefix: 'currentEvents'
},
{
label: 'Past Events',
prefix: 'pastEvents'
}
];

View File

@ -46,6 +46,7 @@ describe('the plugin', function () {
let twoHoursPast = now - 1000 * 60 * 60 * 2;
let oneHourPast = now - 1000 * 60 * 60;
let twoHoursFuture = now + 1000 * 60 * 60 * 2;
let threeHoursFuture = now + 1000 * 60 * 60 * 3;
let planObject = {
identifier: {
key: 'test-plan-object',
@ -71,6 +72,14 @@ describe('the plugin', function () {
type: 'TEST-GROUP',
color: 'fuchsia',
textColor: 'black'
},
{
name: 'Sed ut perspiciatis two',
start: now,
end: threeHoursFuture,
type: 'TEST-GROUP',
color: 'fuchsia',
textColor: 'black'
}
]
})
@ -85,13 +94,13 @@ describe('the plugin', function () {
openmct = createOpenMct({
timeSystemKey: 'utc',
bounds: {
start: twoHoursPast,
end: twoHoursFuture
start: twoHoursFuture,
end: threeHoursFuture
}
});
openmct.time.setMode(FIXED_MODE_KEY, {
start: twoHoursPast,
end: twoHoursFuture
start: twoHoursFuture,
end: threeHoursFuture
});
openmct.install(new TimelistPlugin());
@ -215,7 +224,7 @@ describe('the plugin', function () {
it('displays the activities', () => {
const items = element.querySelectorAll(LIST_ITEM_CLASS);
expect(items.length).toEqual(2);
expect(items.length).toEqual(1);
});
it('displays the activity headers', () => {
@ -230,14 +239,10 @@ describe('the plugin', function () {
const itemEls = element.querySelectorAll(LIST_ITEM_CLASS);
const itemValues = itemEls[0].querySelectorAll(LIST_ITEM_VALUE_CLASS);
expect(itemValues.length).toEqual(4);
expect(itemValues[3].innerHTML.trim()).toEqual(
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua'
);
expect(itemValues[0].innerHTML.trim()).toEqual(
timeFormatter.format(twoHoursPast, TIME_FORMAT)
);
expect(itemValues[3].innerHTML.trim()).toEqual('Sed ut perspiciatis');
expect(itemValues[0].innerHTML.trim()).toEqual(timeFormatter.format(now, TIME_FORMAT));
expect(itemValues[1].innerHTML.trim()).toEqual(
timeFormatter.format(oneHourPast, TIME_FORMAT)
timeFormatter.format(twoHoursFuture, TIME_FORMAT)
);
done();
@ -313,7 +318,7 @@ describe('the plugin', function () {
type: TIMELIST_TYPE,
id: 'test-object',
configuration: {
sortOrderIndex: 0,
sortOrderIndex: 2,
futureEventsIndex: 1,
futureEventsDurationIndex: 0,
futureEventsDuration: 0,
@ -350,7 +355,26 @@ describe('the plugin', function () {
return nextTick(() => {
const items = element.querySelectorAll(LIST_ITEM_CLASS);
expect(items.length).toEqual(1);
expect(items.length).toEqual(2);
});
});
it('activities and sorts them correctly', () => {
mockComposition.emit('add', planObject);
return nextTick(() => {
const timeFormat = openmct.time.timeSystem().timeFormat;
const timeFormatter = openmct.telemetry.getValueFormatter({ format: timeFormat }).formatter;
const items = element.querySelectorAll(LIST_ITEM_CLASS);
expect(items.length).toEqual(2);
const itemValues = items[1].querySelectorAll(LIST_ITEM_VALUE_CLASS);
expect(itemValues[0].innerHTML.trim()).toEqual(timeFormatter.format(now, TIME_FORMAT));
expect(itemValues[1].innerHTML.trim()).toEqual(
timeFormatter.format(threeHoursFuture, TIME_FORMAT)
);
expect(itemValues[3].innerHTML.trim()).toEqual('Sed ut perspiciatis two');
});
});
});
@ -405,7 +429,7 @@ describe('the plugin', function () {
return nextTick(() => {
const items = element.querySelectorAll(LIST_ITEM_CLASS);
expect(items.length).toEqual(1);
expect(items.length).toEqual(2);
});
});
});

View File

@ -42,7 +42,6 @@
background-color: $colorCurrentBg;
border-top: 1px solid $colorCurrentBorder !important;
color: $colorCurrentFg;
font-weight: bold;
}
&.--is-future {

View File

@ -421,10 +421,10 @@ $colorGaugeLimitLow: $colorGaugeLimitHigh;
$transitionTimeGauge: 150ms; // CSS transition time used to smooth needle gauge and meter value transitions
$marginGaugeMeterValue: 10%; // Margin between meter value bar and bounds edges
// Time Strip and Lists
$colorCurrentBg: rgba($colorStatusAlert, 0.3);
$colorCurrentFg: pullForward($colorBodyFg, 20%);
$colorCurrentBg: $colorTimeRealtimeBg;
$colorCurrentFg: $colorTimeRealtimeFg;
$colorCurrentBorder: $colorBodyBg;
$colorFutureBg: rgba($colorKey, 0.2);
$colorFutureBg: #1b5263;
$colorFutureFg: $colorCurrentFg;
$colorFutureBorder: $colorCurrentBorder;
$colorGanttSelectedBorder: rgba(#fff, 0.3);

View File

@ -424,10 +424,10 @@ $colorGaugeLimitLow: $colorGaugeLimitHigh;
$transitionTimeGauge: 150ms; // CSS transition time used to smooth needle gauge and meter value transitions
$marginGaugeMeterValue: 10%; // Margin between meter value bar and bounds edges
// Time Strip and Lists
$colorCurrentBg: rgba($colorStatusAlert, 0.3);
$colorCurrentFg: pullForward($colorBodyFg, 20%);
$colorCurrentBorder: #fff;
$colorFutureBg: rgba($colorKey, 0.2);
$colorCurrentBg: $colorTimeRealtimeBg;
$colorCurrentFg: $colorTimeRealtimeFg;
$colorCurrentBorder: $colorBodyBg;
$colorFutureBg: #1b5263;
$colorFutureFg: $colorCurrentFg;
$colorFutureBorder: $colorCurrentBorder;
$colorGanttSelectedBorder: #fff;

View File

@ -421,11 +421,11 @@ $colorGaugeLimitLow: $colorGaugeLimitHigh;
$transitionTimeGauge: 150ms; // CSS transition time used to smooth needle gauge and meter value transitions
$marginGaugeMeterValue: 10%; // Margin between meter value bar and bounds edges
// Time Strip and Lists
$colorCurrentBg: rgba($colorStatusAlert, 0.3);
$colorCurrentFg: pullForward($colorBodyFg, 20%);
$colorCurrentBg: #5872bd;
$colorCurrentFg: #eee;
$colorCurrentBorder: #fff;
$colorFutureBg: rgba($colorKey, 0.2);
$colorFutureFg: $colorCurrentFg;
$colorFutureBg: #c6f0ff;
$colorFutureFg: $colorBodyFg;
$colorFutureBorder: $colorCurrentBorder;
$colorGanttSelectedBorder: #fff;

View File

@ -101,18 +101,11 @@ import NotebookMenuSwitcher from '@/plugins/notebook/components/NotebookMenuSwit
import IndependentTimeConductor from '@/plugins/timeConductor/independent/IndependentTimeConductor.vue';
import tooltipHelpers from '../../api/tooltips/tooltipMixins';
import { SupportedViewTypes } from '../../utils/constants.js';
import ObjectView from './ObjectView.vue';
const SIMPLE_CONTENT_TYPES = ['clock', 'timer', 'summary-widget', 'hyperlink', 'conditionWidget'];
const CSS_WIDTH_LESS_STR = '--width-less-than-';
const SupportedViewTypes = [
'plot-stacked',
'plot-overlay',
'bar-graph.view',
'scatter-plot.view',
'time-strip.view',
'example.imagery'
];
export default {
components: {

View File

@ -143,15 +143,9 @@ import NotebookMenuSwitcher from '@/plugins/notebook/components/NotebookMenuSwit
import IndependentTimeConductor from '@/plugins/timeConductor/independent/IndependentTimeConductor.vue';
import tooltipHelpers from '../../api/tooltips/tooltipMixins';
import { SupportedViewTypes } from '../../utils/constants.js';
import ViewSwitcher from './ViewSwitcher.vue';
const SupportedViewTypes = [
'plot-stacked',
'plot-overlay',
'bar-graph.view',
'time-strip.view',
'example.imagery'
];
const PLACEHOLDER_OBJECT = {};
export default {

8
src/utils/constants.js Normal file
View File

@ -0,0 +1,8 @@
export const SupportedViewTypes = [
'plot-stacked',
'plot-overlay',
'bar-graph.view',
'time-strip.view',
'example.imagery',
'timelist.view'
];