mirror of
https://github.com/nasa/openmct.git
synced 2024-12-19 21:27:52 +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>
|
||||
<div class="c-conductor"
|
||||
:class="[isFixed ? 'is-fixed-mode' : 'is-realtime-mode']">
|
||||
<form class="u-contents" ref="conductorForm"
|
||||
@submit="isFixed ? setBoundsFromView($event) : setOffsetsFromView($event)">
|
||||
|
||||
<form class="u-contents" ref="conductorForm" @submit.prevent="updateTimeFromConductor">
|
||||
<button class="c-input--submit" type="submit" ref="submitButton"></button>
|
||||
<ConductorModeIcon class="c-conductor__mode-icon"></ConductorModeIcon>
|
||||
|
||||
<div class="c-ctrl-wrapper c-conductor-input c-conductor__start-fixed"
|
||||
@ -35,7 +34,7 @@
|
||||
type="text" autocorrect="off" spellcheck="false"
|
||||
ref="startDate"
|
||||
v-model="formattedBounds.start"
|
||||
@change="validateBounds('start', $event.target); setBoundsFromView()" />
|
||||
@change="validateAllBounds(); submitForm()" />
|
||||
<date-picker
|
||||
:default-date-time="formattedBounds.start"
|
||||
:formatter="timeFormatter"
|
||||
@ -48,9 +47,10 @@
|
||||
<div class="c-direction-indicator icon-minus"></div>
|
||||
<input class="c-input--hrs-min-sec"
|
||||
type="text" autocorrect="off"
|
||||
ref="startOffset"
|
||||
spellcheck="false"
|
||||
v-model="offsets.start"
|
||||
@change="validateOffsets($event); setOffsetsFromView()">
|
||||
@change="validateAllOffsets(); submitForm()">
|
||||
</div>
|
||||
|
||||
<div class="c-ctrl-wrapper c-conductor-input c-conductor__end-fixed">
|
||||
@ -63,7 +63,7 @@
|
||||
v-model="formattedBounds.end"
|
||||
:disabled="!isFixed"
|
||||
ref="endDate"
|
||||
@change="validateBounds('end', $event.target); setBoundsFromView()">
|
||||
@change="validateAllBounds(); submitForm()">
|
||||
<date-picker
|
||||
class="c-ctrl-wrapper--menus-left"
|
||||
:default-date-time="formattedBounds.end"
|
||||
@ -80,8 +80,9 @@
|
||||
type="text"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
ref="endOffset"
|
||||
v-model="offsets.end"
|
||||
@change="validateOffsets($event); setOffsetsFromView()">
|
||||
@change="validateAllOffsets(); submitForm()">
|
||||
</div>
|
||||
|
||||
<conductor-axis
|
||||
@ -101,6 +102,14 @@
|
||||
<style lang="scss">
|
||||
@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 */
|
||||
.c-conductor {
|
||||
display: grid;
|
||||
@ -357,6 +366,7 @@ export default {
|
||||
},
|
||||
setViewFromClock(clock) {
|
||||
this.isFixed = clock === undefined;
|
||||
this.clearAllValidation();
|
||||
},
|
||||
setViewFromBounds(bounds) {
|
||||
this.formattedBounds.start = this.timeFormatter.format(bounds.start);
|
||||
@ -368,45 +378,87 @@ export default {
|
||||
this.offsets.start = this.durationFormatter.format(Math.abs(offsets.start));
|
||||
this.offsets.end = this.durationFormatter.format(Math.abs(offsets.end));
|
||||
},
|
||||
validateBounds(startOrEnd, input) {
|
||||
let validationResult = true;
|
||||
|
||||
if (!this.timeFormatter.validate(input.value)){
|
||||
validationResult = 'Invalid date value';
|
||||
updateTimeFromConductor() {
|
||||
if (this.isFixed) {
|
||||
this.setBoundsFromView();
|
||||
} else {
|
||||
let boundsValues = {
|
||||
start: this.timeFormatter.parse(this.formattedBounds.start),
|
||||
end: this.timeFormatter.parse(this.formattedBounds.end)
|
||||
};
|
||||
validationResult = this.openmct.time.validateBounds(boundsValues);
|
||||
}
|
||||
|
||||
if (validationResult !== true){
|
||||
input.setCustomValidity(validationResult);
|
||||
} else {
|
||||
input.setCustomValidity('');
|
||||
this.setOffsetsFromView();
|
||||
}
|
||||
},
|
||||
validateOffsets(event) {
|
||||
let input = event.target;
|
||||
let validationResult = true;
|
||||
|
||||
if (!this.durationFormatter.validate(input.value)) {
|
||||
validationResult = 'Invalid offset value';
|
||||
} else {
|
||||
let offsetValues = {
|
||||
start: 0 - this.durationFormatter.parse(this.offsets.start),
|
||||
end: this.durationFormatter.parse(this.offsets.end)
|
||||
};
|
||||
validationResult = this.openmct.time.validateOffsets(offsetValues);
|
||||
}
|
||||
|
||||
if (validationResult !== true){
|
||||
input.setCustomValidity(validationResult);
|
||||
} else {
|
||||
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 formattedDate;
|
||||
|
||||
if (input === this.$refs.startDate) {
|
||||
formattedDate = this.formattedBounds.start;
|
||||
} else {
|
||||
formattedDate = this.formattedBounds.end;
|
||||
}
|
||||
|
||||
if (!this.timeFormatter.validate(formattedDate)){
|
||||
validationResult = 'Invalid date';
|
||||
} else {
|
||||
let boundsValues = {
|
||||
start: this.timeFormatter.parse(this.formattedBounds.start),
|
||||
end: this.timeFormatter.parse(this.formattedBounds.end)
|
||||
};
|
||||
validationResult = this.openmct.time.validateBounds(boundsValues);
|
||||
}
|
||||
|
||||
if (validationResult !== true){
|
||||
input.setCustomValidity(validationResult);
|
||||
input.title = validationResult;
|
||||
return false;
|
||||
} else {
|
||||
input.setCustomValidity('');
|
||||
input.title = '';
|
||||
return true;
|
||||
}
|
||||
});
|
||||
},
|
||||
validateAllOffsets(event) {
|
||||
return [this.$refs.startOffset, this.$refs.endOffset].every((input) => {
|
||||
let validationResult = true;
|
||||
let formattedOffset;
|
||||
|
||||
if (input === this.$refs.startOffset) {
|
||||
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 {
|
||||
let offsetValues = {
|
||||
start: 0 - this.durationFormatter.parse(this.offsets.start),
|
||||
end: this.durationFormatter.parse(this.offsets.end)
|
||||
};
|
||||
validationResult = this.openmct.time.validateOffsets(offsetValues);
|
||||
}
|
||||
|
||||
if (validationResult !== true){
|
||||
input.setCustomValidity(validationResult);
|
||||
input.title = validationResult;
|
||||
return false;
|
||||
} else {
|
||||
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) {
|
||||
return this.openmct.telemetry.getValueFormatter({
|
||||
@ -415,13 +467,13 @@ export default {
|
||||
},
|
||||
startDateSelected(date){
|
||||
this.formattedBounds.start = this.timeFormatter.format(date);
|
||||
this.validateBounds('start', this.$refs.startDate);
|
||||
this.setBoundsFromView();
|
||||
this.validateAllBounds();
|
||||
this.submitForm();
|
||||
},
|
||||
endDateSelected(date){
|
||||
this.formattedBounds.end = this.timeFormatter.format(date);
|
||||
this.validateBounds('end', this.$refs.endDate);
|
||||
this.setBoundsFromView();
|
||||
this.validateAllBounds();
|
||||
this.submitForm();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
|
@ -22,11 +22,11 @@
|
||||
<template>
|
||||
<div class="c-ctrl-wrapper c-ctrl-wrapper--menus-up">
|
||||
<button class="c-button--menu c-mode-button"
|
||||
@click="toggleMenu($event)">
|
||||
@click.prevent="toggle">
|
||||
<span class="c-button__label">{{selectedMode.name}}</span>
|
||||
</button>
|
||||
<div class="c-menu c-super-menu c-conductor__mode-menu"
|
||||
v-if="showMenu">
|
||||
v-if="open">
|
||||
<div class="c-super-menu__menu">
|
||||
<ul>
|
||||
<li v-for="mode in modes"
|
||||
@ -69,8 +69,11 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import toggleMixin from '../../ui/mixins/toggle-mixin';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'configuration'],
|
||||
mixins: [toggleMixin],
|
||||
data: function () {
|
||||
let activeClock = this.openmct.time.clock();
|
||||
if (activeClock !== undefined) {
|
||||
@ -81,8 +84,7 @@ export default {
|
||||
selectedMode: this.getModeOptionForClock(activeClock),
|
||||
selectedTimeSystem: JSON.parse(JSON.stringify(this.openmct.time.timeSystem())),
|
||||
modes: [],
|
||||
hoveredMode: {},
|
||||
showMenu: false
|
||||
hoveredMode: {}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@ -177,17 +179,7 @@ export default {
|
||||
|
||||
setViewFromClock(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 () {
|
||||
this.loadClocksFromConfiguration();
|
||||
|
@ -24,10 +24,10 @@
|
||||
v-if="selectedTimeSystem.name">
|
||||
<button class="c-button--menu c-time-system-button"
|
||||
:class="selectedTimeSystem.cssClass"
|
||||
@click="toggleMenu($event)">
|
||||
@click.prevent="toggle">
|
||||
<span class="c-button__label">{{selectedTimeSystem.name}}</span>
|
||||
</button>
|
||||
<div class="c-menu" v-if="showMenu">
|
||||
<div class="c-menu" v-if="open">
|
||||
<ul>
|
||||
<li @click="setTimeSystemFromView(timeSystem)"
|
||||
v-for="timeSystem in timeSystems"
|
||||
@ -41,15 +41,17 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import toggleMixin from '../../ui/mixins/toggle-mixin';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'configuration'],
|
||||
mixins: [toggleMixin],
|
||||
data: function () {
|
||||
let activeClock = this.openmct.time.clock();
|
||||
|
||||
return {
|
||||
selectedTimeSystem: JSON.parse(JSON.stringify(this.openmct.time.timeSystem())),
|
||||
timeSystems: this.getValidTimesystemsForClock(activeClock),
|
||||
showMenu: false
|
||||
timeSystems: this.getValidTimesystemsForClock(activeClock)
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@ -94,16 +96,6 @@ export default {
|
||||
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) {
|
||||
this.selectedTimeSystem = timeSystem;
|
||||
},
|
||||
|
@ -22,12 +22,12 @@
|
||||
<template>
|
||||
<div class="c-ctrl-wrapper c-ctrl-wrapper--menus-up c-datetime-picker__wrapper" ref="calendarHolder">
|
||||
<a class="c-click-icon icon-calendar"
|
||||
@click="togglePicker()"></a>
|
||||
@click="toggle"></a>
|
||||
<div class="c-menu c-menu--mobile-modal c-datetime-picker"
|
||||
v-if="showPicker">
|
||||
v-if="open">
|
||||
<div class="c-datetime-picker__close-button">
|
||||
<button class="c-click-icon icon-x-in-circle"
|
||||
@click="togglePicker()"></button>
|
||||
@click="toggle"></button>
|
||||
</div>
|
||||
<div class="c-datetime-picker__pager c-pager l-month-year-pager">
|
||||
<div class="c-pager__prev c-click-icon icon-arrow-left"
|
||||
@ -160,6 +160,7 @@
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import toggleMixin from '../../ui/mixins/toggle-mixin';
|
||||
|
||||
const TIME_NAMES = {
|
||||
'hours': "Hour",
|
||||
@ -181,13 +182,13 @@ const TIME_OPTIONS = (function makeRanges() {
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
mixins: [toggleMixin],
|
||||
props: {
|
||||
defaultDateTime: String,
|
||||
formatter: Object
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
showPicker: false,
|
||||
picker: {
|
||||
year: undefined,
|
||||
month: undefined,
|
||||
@ -285,7 +286,6 @@ export default {
|
||||
this.date.year = cell.year;
|
||||
this.date.day = cell.day;
|
||||
this.updateFromView();
|
||||
this.showPicker = false;
|
||||
},
|
||||
|
||||
dateEquals(d1, d2) {
|
||||
@ -315,23 +315,6 @@ export default {
|
||||
optionsFor(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 () {
|
||||
this.updateFromModel(this.defaultDateTime);
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
<script>
|
||||
|
||||
import toggleMixin from './toggle-mixin';
|
||||
import toggleMixin from '../../mixins/toggle-mixin';
|
||||
|
||||
export default {
|
||||
mixins: [toggleMixin],
|
||||
|
@ -22,7 +22,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import toggle from './toggle-mixin';
|
||||
import toggle from '../../mixins/toggle-mixin';
|
||||
export default {
|
||||
mixins: [toggle],
|
||||
props: {
|
||||
|
@ -19,7 +19,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import toggleMixin from './toggle-mixin';
|
||||
import toggleMixin from '../../mixins/toggle-mixin';
|
||||
|
||||
export default {
|
||||
mixins: [toggleMixin],
|
||||
|
Loading…
Reference in New Issue
Block a user