mirror of
https://github.com/nasa/openmct.git
synced 2024-12-29 17:38:53 +00:00
Time conductor validation (#2273)
* Fixed validation issues for bounds * Validate offsets correctly * Reset validation on mode change * Use toggle mixin for Conductor menus
This commit is contained in:
parent
075d4deecb
commit
ac2b9acccb
@ -22,9 +22,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="c-conductor"
|
<div class="c-conductor"
|
||||||
:class="[isFixed ? 'is-fixed-mode' : 'is-realtime-mode']">
|
:class="[isFixed ? 'is-fixed-mode' : 'is-realtime-mode']">
|
||||||
<form class="u-contents" ref="conductorForm"
|
<form class="u-contents" ref="conductorForm" @submit.prevent="updateTimeFromConductor">
|
||||||
@submit="isFixed ? setBoundsFromView($event) : setOffsetsFromView($event)">
|
<button class="c-input--submit" type="submit" ref="submitButton"></button>
|
||||||
|
|
||||||
<ConductorModeIcon class="c-conductor__mode-icon"></ConductorModeIcon>
|
<ConductorModeIcon class="c-conductor__mode-icon"></ConductorModeIcon>
|
||||||
|
|
||||||
<div class="c-ctrl-wrapper c-conductor-input c-conductor__start-fixed"
|
<div class="c-ctrl-wrapper c-conductor-input c-conductor__start-fixed"
|
||||||
@ -35,7 +34,7 @@
|
|||||||
type="text" autocorrect="off" spellcheck="false"
|
type="text" autocorrect="off" spellcheck="false"
|
||||||
ref="startDate"
|
ref="startDate"
|
||||||
v-model="formattedBounds.start"
|
v-model="formattedBounds.start"
|
||||||
@change="validateBounds('start', $event.target); setBoundsFromView()" />
|
@change="validateAllBounds(); submitForm()" />
|
||||||
<date-picker
|
<date-picker
|
||||||
:default-date-time="formattedBounds.start"
|
:default-date-time="formattedBounds.start"
|
||||||
:formatter="timeFormatter"
|
:formatter="timeFormatter"
|
||||||
@ -48,9 +47,10 @@
|
|||||||
<div class="c-direction-indicator icon-minus"></div>
|
<div class="c-direction-indicator icon-minus"></div>
|
||||||
<input class="c-input--hrs-min-sec"
|
<input class="c-input--hrs-min-sec"
|
||||||
type="text" autocorrect="off"
|
type="text" autocorrect="off"
|
||||||
|
ref="startOffset"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
v-model="offsets.start"
|
v-model="offsets.start"
|
||||||
@change="validateOffsets($event); setOffsetsFromView()">
|
@change="validateAllOffsets(); submitForm()">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="c-ctrl-wrapper c-conductor-input c-conductor__end-fixed">
|
<div class="c-ctrl-wrapper c-conductor-input c-conductor__end-fixed">
|
||||||
@ -63,7 +63,7 @@
|
|||||||
v-model="formattedBounds.end"
|
v-model="formattedBounds.end"
|
||||||
:disabled="!isFixed"
|
:disabled="!isFixed"
|
||||||
ref="endDate"
|
ref="endDate"
|
||||||
@change="validateBounds('end', $event.target); setBoundsFromView()">
|
@change="validateAllBounds(); submitForm()">
|
||||||
<date-picker
|
<date-picker
|
||||||
class="c-ctrl-wrapper--menus-left"
|
class="c-ctrl-wrapper--menus-left"
|
||||||
:default-date-time="formattedBounds.end"
|
:default-date-time="formattedBounds.end"
|
||||||
@ -80,8 +80,9 @@
|
|||||||
type="text"
|
type="text"
|
||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
|
ref="endOffset"
|
||||||
v-model="offsets.end"
|
v-model="offsets.end"
|
||||||
@change="validateOffsets($event); setOffsetsFromView()">
|
@change="validateAllOffsets(); submitForm()">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<conductor-axis
|
<conductor-axis
|
||||||
@ -101,6 +102,14 @@
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import "~styles/sass-base";
|
@import "~styles/sass-base";
|
||||||
|
|
||||||
|
.c-input--submit {
|
||||||
|
// Can't use display: none because some browsers will pretend the input doesn't exist, and enter won't work
|
||||||
|
visibility: none;
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*********************************************** CONDUCTOR LAYOUT */
|
/*********************************************** CONDUCTOR LAYOUT */
|
||||||
.c-conductor {
|
.c-conductor {
|
||||||
display: grid;
|
display: grid;
|
||||||
@ -357,6 +366,7 @@ export default {
|
|||||||
},
|
},
|
||||||
setViewFromClock(clock) {
|
setViewFromClock(clock) {
|
||||||
this.isFixed = clock === undefined;
|
this.isFixed = clock === undefined;
|
||||||
|
this.clearAllValidation();
|
||||||
},
|
},
|
||||||
setViewFromBounds(bounds) {
|
setViewFromBounds(bounds) {
|
||||||
this.formattedBounds.start = this.timeFormatter.format(bounds.start);
|
this.formattedBounds.start = this.timeFormatter.format(bounds.start);
|
||||||
@ -368,11 +378,32 @@ export default {
|
|||||||
this.offsets.start = this.durationFormatter.format(Math.abs(offsets.start));
|
this.offsets.start = this.durationFormatter.format(Math.abs(offsets.start));
|
||||||
this.offsets.end = this.durationFormatter.format(Math.abs(offsets.end));
|
this.offsets.end = this.durationFormatter.format(Math.abs(offsets.end));
|
||||||
},
|
},
|
||||||
validateBounds(startOrEnd, input) {
|
updateTimeFromConductor() {
|
||||||
|
if (this.isFixed) {
|
||||||
|
this.setBoundsFromView();
|
||||||
|
} else {
|
||||||
|
this.setOffsetsFromView();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clearAllValidation() {
|
||||||
|
[this.$refs.startDate, this.$refs.endDate, this.$refs.startOffset, this.$refs.endOffset].forEach((input) => {
|
||||||
|
input.setCustomValidity('');
|
||||||
|
input.title = '';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
validateAllBounds() {
|
||||||
|
return [this.$refs.startDate, this.$refs.endDate].every((input) => {
|
||||||
let validationResult = true;
|
let validationResult = true;
|
||||||
|
let formattedDate;
|
||||||
|
|
||||||
if (!this.timeFormatter.validate(input.value)){
|
if (input === this.$refs.startDate) {
|
||||||
validationResult = 'Invalid date value';
|
formattedDate = this.formattedBounds.start;
|
||||||
|
} else {
|
||||||
|
formattedDate = this.formattedBounds.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.timeFormatter.validate(formattedDate)){
|
||||||
|
validationResult = 'Invalid date';
|
||||||
} else {
|
} else {
|
||||||
let boundsValues = {
|
let boundsValues = {
|
||||||
start: this.timeFormatter.parse(this.formattedBounds.start),
|
start: this.timeFormatter.parse(this.formattedBounds.start),
|
||||||
@ -383,16 +414,28 @@ export default {
|
|||||||
|
|
||||||
if (validationResult !== true){
|
if (validationResult !== true){
|
||||||
input.setCustomValidity(validationResult);
|
input.setCustomValidity(validationResult);
|
||||||
|
input.title = validationResult;
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
input.setCustomValidity('');
|
input.setCustomValidity('');
|
||||||
|
input.title = '';
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
validateOffsets(event) {
|
validateAllOffsets(event) {
|
||||||
let input = event.target;
|
return [this.$refs.startOffset, this.$refs.endOffset].every((input) => {
|
||||||
let validationResult = true;
|
let validationResult = true;
|
||||||
|
let formattedOffset;
|
||||||
|
|
||||||
if (!this.durationFormatter.validate(input.value)) {
|
if (input === this.$refs.startOffset) {
|
||||||
validationResult = 'Invalid offset value';
|
formattedOffset = this.offsets.start;
|
||||||
|
} else {
|
||||||
|
formattedOffset = this.offsets.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.durationFormatter.validate(formattedOffset)) {
|
||||||
|
validationResult = 'Offsets must be in the format hh:mm:ss and less than 24 hours in duration';
|
||||||
} else {
|
} else {
|
||||||
let offsetValues = {
|
let offsetValues = {
|
||||||
start: 0 - this.durationFormatter.parse(this.offsets.start),
|
start: 0 - this.durationFormatter.parse(this.offsets.start),
|
||||||
@ -403,10 +446,19 @@ export default {
|
|||||||
|
|
||||||
if (validationResult !== true){
|
if (validationResult !== true){
|
||||||
input.setCustomValidity(validationResult);
|
input.setCustomValidity(validationResult);
|
||||||
|
input.title = validationResult;
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
input.setCustomValidity('');
|
input.setCustomValidity('');
|
||||||
|
input.title = '';
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
submitForm() {
|
||||||
|
// Allow Vue model to catch up to user input.
|
||||||
|
// Submitting form will cause validation messages to display (but only if triggered by button click)
|
||||||
|
this.$nextTick(() => this.$refs.submitButton.click());
|
||||||
},
|
},
|
||||||
getFormatter(key) {
|
getFormatter(key) {
|
||||||
return this.openmct.telemetry.getValueFormatter({
|
return this.openmct.telemetry.getValueFormatter({
|
||||||
@ -415,13 +467,13 @@ export default {
|
|||||||
},
|
},
|
||||||
startDateSelected(date){
|
startDateSelected(date){
|
||||||
this.formattedBounds.start = this.timeFormatter.format(date);
|
this.formattedBounds.start = this.timeFormatter.format(date);
|
||||||
this.validateBounds('start', this.$refs.startDate);
|
this.validateAllBounds();
|
||||||
this.setBoundsFromView();
|
this.submitForm();
|
||||||
},
|
},
|
||||||
endDateSelected(date){
|
endDateSelected(date){
|
||||||
this.formattedBounds.end = this.timeFormatter.format(date);
|
this.formattedBounds.end = this.timeFormatter.format(date);
|
||||||
this.validateBounds('end', this.$refs.endDate);
|
this.validateAllBounds();
|
||||||
this.setBoundsFromView();
|
this.submitForm();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -22,11 +22,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="c-ctrl-wrapper c-ctrl-wrapper--menus-up">
|
<div class="c-ctrl-wrapper c-ctrl-wrapper--menus-up">
|
||||||
<button class="c-button--menu c-mode-button"
|
<button class="c-button--menu c-mode-button"
|
||||||
@click="toggleMenu($event)">
|
@click.prevent="toggle">
|
||||||
<span class="c-button__label">{{selectedMode.name}}</span>
|
<span class="c-button__label">{{selectedMode.name}}</span>
|
||||||
</button>
|
</button>
|
||||||
<div class="c-menu c-super-menu c-conductor__mode-menu"
|
<div class="c-menu c-super-menu c-conductor__mode-menu"
|
||||||
v-if="showMenu">
|
v-if="open">
|
||||||
<div class="c-super-menu__menu">
|
<div class="c-super-menu__menu">
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="mode in modes"
|
<li v-for="mode in modes"
|
||||||
@ -69,8 +69,11 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import toggleMixin from '../../ui/mixins/toggle-mixin';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct', 'configuration'],
|
inject: ['openmct', 'configuration'],
|
||||||
|
mixins: [toggleMixin],
|
||||||
data: function () {
|
data: function () {
|
||||||
let activeClock = this.openmct.time.clock();
|
let activeClock = this.openmct.time.clock();
|
||||||
if (activeClock !== undefined) {
|
if (activeClock !== undefined) {
|
||||||
@ -81,8 +84,7 @@ export default {
|
|||||||
selectedMode: this.getModeOptionForClock(activeClock),
|
selectedMode: this.getModeOptionForClock(activeClock),
|
||||||
selectedTimeSystem: JSON.parse(JSON.stringify(this.openmct.time.timeSystem())),
|
selectedTimeSystem: JSON.parse(JSON.stringify(this.openmct.time.timeSystem())),
|
||||||
modes: [],
|
modes: [],
|
||||||
hoveredMode: {},
|
hoveredMode: {}
|
||||||
showMenu: false
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -177,18 +179,8 @@ export default {
|
|||||||
|
|
||||||
setViewFromClock(clock) {
|
setViewFromClock(clock) {
|
||||||
this.selectedMode = this.getModeOptionForClock(clock);
|
this.selectedMode = this.getModeOptionForClock(clock);
|
||||||
},
|
|
||||||
|
|
||||||
toggleMenu(event) {
|
|
||||||
this.showMenu = !this.showMenu;
|
|
||||||
|
|
||||||
if (this.showMenu) {
|
|
||||||
document.addEventListener('click', this.toggleMenu, true);
|
|
||||||
} else {
|
|
||||||
document.removeEventListener('click', this.toggleMenu, true);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
this.loadClocksFromConfiguration();
|
this.loadClocksFromConfiguration();
|
||||||
|
|
||||||
|
@ -24,10 +24,10 @@
|
|||||||
v-if="selectedTimeSystem.name">
|
v-if="selectedTimeSystem.name">
|
||||||
<button class="c-button--menu c-time-system-button"
|
<button class="c-button--menu c-time-system-button"
|
||||||
:class="selectedTimeSystem.cssClass"
|
:class="selectedTimeSystem.cssClass"
|
||||||
@click="toggleMenu($event)">
|
@click.prevent="toggle">
|
||||||
<span class="c-button__label">{{selectedTimeSystem.name}}</span>
|
<span class="c-button__label">{{selectedTimeSystem.name}}</span>
|
||||||
</button>
|
</button>
|
||||||
<div class="c-menu" v-if="showMenu">
|
<div class="c-menu" v-if="open">
|
||||||
<ul>
|
<ul>
|
||||||
<li @click="setTimeSystemFromView(timeSystem)"
|
<li @click="setTimeSystemFromView(timeSystem)"
|
||||||
v-for="timeSystem in timeSystems"
|
v-for="timeSystem in timeSystems"
|
||||||
@ -41,15 +41,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import toggleMixin from '../../ui/mixins/toggle-mixin';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct', 'configuration'],
|
inject: ['openmct', 'configuration'],
|
||||||
|
mixins: [toggleMixin],
|
||||||
data: function () {
|
data: function () {
|
||||||
let activeClock = this.openmct.time.clock();
|
let activeClock = this.openmct.time.clock();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
selectedTimeSystem: JSON.parse(JSON.stringify(this.openmct.time.timeSystem())),
|
selectedTimeSystem: JSON.parse(JSON.stringify(this.openmct.time.timeSystem())),
|
||||||
timeSystems: this.getValidTimesystemsForClock(activeClock),
|
timeSystems: this.getValidTimesystemsForClock(activeClock)
|
||||||
showMenu: false
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -94,16 +96,6 @@ export default {
|
|||||||
return this.configuration.menuOptions.filter(configMatches)[0];
|
return this.configuration.menuOptions.filter(configMatches)[0];
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleMenu(event) {
|
|
||||||
this.showMenu = !this.showMenu;
|
|
||||||
|
|
||||||
if (this.showMenu) {
|
|
||||||
document.addEventListener('click', this.toggleMenu, true);
|
|
||||||
} else {
|
|
||||||
document.removeEventListener('click', this.toggleMenu, true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setViewFromTimeSystem(timeSystem) {
|
setViewFromTimeSystem(timeSystem) {
|
||||||
this.selectedTimeSystem = timeSystem;
|
this.selectedTimeSystem = timeSystem;
|
||||||
},
|
},
|
||||||
|
@ -22,12 +22,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="c-ctrl-wrapper c-ctrl-wrapper--menus-up c-datetime-picker__wrapper" ref="calendarHolder">
|
<div class="c-ctrl-wrapper c-ctrl-wrapper--menus-up c-datetime-picker__wrapper" ref="calendarHolder">
|
||||||
<a class="c-click-icon icon-calendar"
|
<a class="c-click-icon icon-calendar"
|
||||||
@click="togglePicker()"></a>
|
@click="toggle"></a>
|
||||||
<div class="c-menu c-menu--mobile-modal c-datetime-picker"
|
<div class="c-menu c-menu--mobile-modal c-datetime-picker"
|
||||||
v-if="showPicker">
|
v-if="open">
|
||||||
<div class="c-datetime-picker__close-button">
|
<div class="c-datetime-picker__close-button">
|
||||||
<button class="c-click-icon icon-x-in-circle"
|
<button class="c-click-icon icon-x-in-circle"
|
||||||
@click="togglePicker()"></button>
|
@click="toggle"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-datetime-picker__pager c-pager l-month-year-pager">
|
<div class="c-datetime-picker__pager c-pager l-month-year-pager">
|
||||||
<div class="c-pager__prev c-click-icon icon-arrow-left"
|
<div class="c-pager__prev c-click-icon icon-arrow-left"
|
||||||
@ -160,6 +160,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import toggleMixin from '../../ui/mixins/toggle-mixin';
|
||||||
|
|
||||||
const TIME_NAMES = {
|
const TIME_NAMES = {
|
||||||
'hours': "Hour",
|
'hours': "Hour",
|
||||||
@ -181,13 +182,13 @@ const TIME_OPTIONS = (function makeRanges() {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
|
mixins: [toggleMixin],
|
||||||
props: {
|
props: {
|
||||||
defaultDateTime: String,
|
defaultDateTime: String,
|
||||||
formatter: Object
|
formatter: Object
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
showPicker: false,
|
|
||||||
picker: {
|
picker: {
|
||||||
year: undefined,
|
year: undefined,
|
||||||
month: undefined,
|
month: undefined,
|
||||||
@ -285,7 +286,6 @@ export default {
|
|||||||
this.date.year = cell.year;
|
this.date.year = cell.year;
|
||||||
this.date.day = cell.day;
|
this.date.day = cell.day;
|
||||||
this.updateFromView();
|
this.updateFromView();
|
||||||
this.showPicker = false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
dateEquals(d1, d2) {
|
dateEquals(d1, d2) {
|
||||||
@ -315,23 +315,6 @@ export default {
|
|||||||
optionsFor(key) {
|
optionsFor(key) {
|
||||||
return TIME_OPTIONS[key];
|
return TIME_OPTIONS[key];
|
||||||
},
|
},
|
||||||
|
|
||||||
hidePicker(event) {
|
|
||||||
let path = event.composedPath();
|
|
||||||
if (path.indexOf(this.$refs.calendarHolder) === -1) {
|
|
||||||
this.showPicker = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
togglePicker() {
|
|
||||||
this.showPicker = !this.showPicker;
|
|
||||||
|
|
||||||
if (this.showPicker) {
|
|
||||||
document.addEventListener('click', this.hidePicker, {
|
|
||||||
capture: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
this.updateFromModel(this.defaultDateTime);
|
this.updateFromModel(this.defaultDateTime);
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import toggleMixin from './toggle-mixin';
|
import toggleMixin from '../../mixins/toggle-mixin';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [toggleMixin],
|
mixins: [toggleMixin],
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import toggle from './toggle-mixin';
|
import toggle from '../../mixins/toggle-mixin';
|
||||||
export default {
|
export default {
|
||||||
mixins: [toggle],
|
mixins: [toggle],
|
||||||
props: {
|
props: {
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import toggleMixin from './toggle-mixin';
|
import toggleMixin from '../../mixins/toggle-mixin';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [toggleMixin],
|
mixins: [toggleMixin],
|
||||||
|
Loading…
Reference in New Issue
Block a user