mirror of
https://github.com/nasa/openmct.git
synced 2025-05-10 04:22:53 +00:00
New Time Conductor time input for real-time (#3409)
* Time Conductor Real Time input popup Co-authored-by: Jamie Vigliotta <jamie.j.vigliotta@nasa.gov> Co-authored-by: Andrew Henry <akhenry@gmail.com>
This commit is contained in:
parent
633bac2ed5
commit
fe899cbcc8
@ -73,15 +73,22 @@
|
|||||||
>
|
>
|
||||||
<!-- RT start -->
|
<!-- RT start -->
|
||||||
<div class="c-direction-indicator icon-minus"></div>
|
<div class="c-direction-indicator icon-minus"></div>
|
||||||
<input
|
<time-popup
|
||||||
|
v-if="showTCInputStart"
|
||||||
|
class="pr-tc-input-menu--start"
|
||||||
|
:type="'start'"
|
||||||
|
:offset="offsets.start"
|
||||||
|
@focus.native="$event.target.select()"
|
||||||
|
@hide="hideAllTimePopups"
|
||||||
|
@update="timePopUpdate"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
@click="showTimePopupStart"
|
||||||
|
class="c-button c-conductor__delta-button"
|
||||||
ref="startOffset"
|
ref="startOffset"
|
||||||
v-model="offsets.start"
|
|
||||||
class="c-input--hrs-min-sec"
|
|
||||||
type="text"
|
|
||||||
autocorrect="off"
|
|
||||||
spellcheck="false"
|
|
||||||
@change="validateAllOffsets(); submitForm()"
|
|
||||||
>
|
>
|
||||||
|
{{ offsets.start }}
|
||||||
|
</button>
|
||||||
</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">
|
||||||
@ -114,15 +121,22 @@
|
|||||||
>
|
>
|
||||||
<!-- RT end -->
|
<!-- RT end -->
|
||||||
<div class="c-direction-indicator icon-plus"></div>
|
<div class="c-direction-indicator icon-plus"></div>
|
||||||
<input
|
<time-popup
|
||||||
|
v-if="showTCInputEnd"
|
||||||
|
class="pr-tc-input-menu--end"
|
||||||
|
:type="'end'"
|
||||||
|
:offset="offsets.end"
|
||||||
|
@focus.native="$event.target.select()"
|
||||||
|
@hide="hideAllTimePopups"
|
||||||
|
@update="timePopUpdate"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
ref="endOffset"
|
ref="endOffset"
|
||||||
v-model="offsets.end"
|
class="c-button c-conductor__delta-button"
|
||||||
class="c-input--hrs-min-sec"
|
@click="showTimePopupEnd"
|
||||||
type="text"
|
|
||||||
autocorrect="off"
|
|
||||||
spellcheck="false"
|
|
||||||
@change="validateAllOffsets(); submitForm()"
|
|
||||||
>
|
>
|
||||||
|
{{offsets.end}}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<conductor-axis
|
<conductor-axis
|
||||||
@ -164,6 +178,7 @@ import DatePicker from './DatePicker.vue';
|
|||||||
import ConductorAxis from './ConductorAxis.vue';
|
import ConductorAxis from './ConductorAxis.vue';
|
||||||
import ConductorModeIcon from './ConductorModeIcon.vue';
|
import ConductorModeIcon from './ConductorModeIcon.vue';
|
||||||
import ConductorHistory from './ConductorHistory.vue';
|
import ConductorHistory from './ConductorHistory.vue';
|
||||||
|
import TimePopup from './timePopup.vue';
|
||||||
|
|
||||||
const DEFAULT_DURATION_FORMATTER = 'duration';
|
const DEFAULT_DURATION_FORMATTER = 'duration';
|
||||||
|
|
||||||
@ -174,7 +189,8 @@ export default {
|
|||||||
DatePicker,
|
DatePicker,
|
||||||
ConductorAxis,
|
ConductorAxis,
|
||||||
ConductorModeIcon,
|
ConductorModeIcon,
|
||||||
ConductorHistory
|
ConductorHistory,
|
||||||
|
TimePopup
|
||||||
},
|
},
|
||||||
inject: ['openmct', 'configuration'],
|
inject: ['openmct', 'configuration'],
|
||||||
data() {
|
data() {
|
||||||
@ -209,7 +225,9 @@ export default {
|
|||||||
showDatePicker: false,
|
showDatePicker: false,
|
||||||
altPressed: false,
|
altPressed: false,
|
||||||
isPanning: false,
|
isPanning: false,
|
||||||
isZooming: false
|
isZooming: false,
|
||||||
|
showTCInputStart: false,
|
||||||
|
showTCInputEnd: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -458,6 +476,23 @@ export default {
|
|||||||
this.formattedBounds.end = this.timeFormatter.format(date);
|
this.formattedBounds.end = this.timeFormatter.format(date);
|
||||||
this.validateAllBounds('endDate');
|
this.validateAllBounds('endDate');
|
||||||
this.submitForm();
|
this.submitForm();
|
||||||
|
},
|
||||||
|
hideAllTimePopups() {
|
||||||
|
this.showTCInputStart = false;
|
||||||
|
this.showTCInputEnd = false;
|
||||||
|
},
|
||||||
|
showTimePopupStart() {
|
||||||
|
this.hideAllTimePopups();
|
||||||
|
this.showTCInputStart = !this.showTCInputStart;
|
||||||
|
},
|
||||||
|
showTimePopupEnd() {
|
||||||
|
this.hideAllTimePopups();
|
||||||
|
this.showTCInputEnd = !this.showTCInputEnd;
|
||||||
|
},
|
||||||
|
timePopUpdate({ type, hours, minutes, seconds }) {
|
||||||
|
this.offsets[type] = [hours, minutes, seconds].join(':');
|
||||||
|
this.setOffsetsFromView();
|
||||||
|
this.hideAllTimePopups();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -207,14 +207,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.is-realtime-mode {
|
.is-realtime-mode {
|
||||||
button {
|
.c-conductor__controls button,
|
||||||
|
.c-conductor__delta-button {
|
||||||
@include themedButton($colorTimeBg);
|
@include themedButton($colorTimeBg);
|
||||||
color: $colorTimeFg;
|
color: $colorTimeFg;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: $colorTimeHov !important;
|
|
||||||
color: $colorTimeFg !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-conductor-input {
|
.c-conductor-input {
|
||||||
@ -233,6 +229,59 @@
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
color: $colorTime;
|
color: $colorTime;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
|
&[disabled] {
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prototype
|
||||||
|
[class^='pr-tc-input-menu'] {
|
||||||
|
// Uses ^= here to target both start and end menus
|
||||||
|
background: $colorBodyBg;
|
||||||
|
border-radius: $controlCr;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 2fr;
|
||||||
|
grid-column-gap: 3px;
|
||||||
|
grid-row-gap: 4px;
|
||||||
|
align-items: start;
|
||||||
|
|
||||||
|
filter: brightness(1.4);
|
||||||
|
box-shadow: $shdwMenu;
|
||||||
|
padding: $interiorMargin;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 24px;
|
||||||
|
z-index: 99;
|
||||||
|
|
||||||
|
&[class*='--start'] {
|
||||||
|
left: -25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[class*='--end'] {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[class^='pr-time'] {
|
||||||
|
&[class*='label'] {
|
||||||
|
font-size: 0.8em;
|
||||||
|
opacity: 0.6;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[class*='controls'] {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
input {
|
||||||
|
height: 22px;
|
||||||
|
line-height: 22px;
|
||||||
|
margin-right: $interiorMarginSm;
|
||||||
|
font-size: 1.25em;
|
||||||
|
width: 42px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
171
src/plugins/timeConductor/timePopup.vue
Normal file
171
src/plugins/timeConductor/timePopup.vue
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="pr-tc-input-menu"
|
||||||
|
@keydown.enter.prevent
|
||||||
|
@keyup.enter.prevent="submit"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<div class="pr-time-label__hrs">Hrs</div>
|
||||||
|
<div class="pr-time-label__mins">Mins</div>
|
||||||
|
<div class="pr-time-label__secs">Secs</div>
|
||||||
|
|
||||||
|
<div class="pr-time-controls">
|
||||||
|
<input
|
||||||
|
ref="inputHrs"
|
||||||
|
v-model="inputHrs"
|
||||||
|
class="pr-time-controls__hrs"
|
||||||
|
step="1"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="23"
|
||||||
|
title="Enter 0 - 23"
|
||||||
|
@change="validate()"
|
||||||
|
@keyup="validate()"
|
||||||
|
@focusin="selectAll($event)"
|
||||||
|
@focusout="format('inputHrs')"
|
||||||
|
@wheel="increment($event, 'inputHrs')"
|
||||||
|
>
|
||||||
|
:
|
||||||
|
</div>
|
||||||
|
<div class="pr-time-controls">
|
||||||
|
<input
|
||||||
|
ref="inputMins"
|
||||||
|
v-model="inputMins"
|
||||||
|
type="number"
|
||||||
|
class="pr-time-controls__mins"
|
||||||
|
min="0"
|
||||||
|
max="59"
|
||||||
|
title="Enter 0 - 59"
|
||||||
|
step="1"
|
||||||
|
@change="validate()"
|
||||||
|
@keyup="validate()"
|
||||||
|
@focusin="selectAll($event)"
|
||||||
|
@focusout="format('inputMins')"
|
||||||
|
@wheel="increment($event, 'inputMins')"
|
||||||
|
>
|
||||||
|
:
|
||||||
|
</div>
|
||||||
|
<div class="pr-time-controls">
|
||||||
|
<input
|
||||||
|
ref="inputSecs"
|
||||||
|
v-model="inputSecs"
|
||||||
|
type="number"
|
||||||
|
class="pr-time-controls__secs"
|
||||||
|
min="0"
|
||||||
|
max="59"
|
||||||
|
title="Enter 0 - 59"
|
||||||
|
step="1"
|
||||||
|
@change="validate()"
|
||||||
|
@keyup="validate()"
|
||||||
|
@focusin="selectAll($event)"
|
||||||
|
@focusout="format('inputSecs')"
|
||||||
|
@wheel="increment($event, 'inputSecs')"
|
||||||
|
>
|
||||||
|
<div class="pr-time__buttons">
|
||||||
|
<button
|
||||||
|
class="c-button c-button--major icon-check"
|
||||||
|
:disabled="isDisabled"
|
||||||
|
@click.prevent="submit"
|
||||||
|
></button>
|
||||||
|
<button class="c-button icon-x"
|
||||||
|
@click.prevent="hide"
|
||||||
|
></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
offset: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
inputHrs: '00',
|
||||||
|
inputMins: '00',
|
||||||
|
inputSecs: '00',
|
||||||
|
isDisabled: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.setOffset();
|
||||||
|
document.addEventListener('click', this.hide);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
document.removeEventListener('click', this.hide);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
format(ref) {
|
||||||
|
const curVal = this[ref];
|
||||||
|
this[ref] = curVal.padStart(2, '0');
|
||||||
|
},
|
||||||
|
validate() {
|
||||||
|
let disabled = false;
|
||||||
|
let refs = ['inputHrs', 'inputMins', 'inputSecs'];
|
||||||
|
|
||||||
|
for (let ref of refs) {
|
||||||
|
let min = Number(this.$refs[ref].min);
|
||||||
|
let max = Number(this.$refs[ref].max);
|
||||||
|
let value = Number(this.$refs[ref].value);
|
||||||
|
|
||||||
|
if (value > max || value < min) {
|
||||||
|
disabled = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isDisabled = disabled;
|
||||||
|
},
|
||||||
|
submit() {
|
||||||
|
this.$emit('update', {
|
||||||
|
type: this.type,
|
||||||
|
hours: this.inputHrs,
|
||||||
|
minutes: this.inputMins,
|
||||||
|
seconds: this.inputSecs
|
||||||
|
});
|
||||||
|
},
|
||||||
|
hide() {
|
||||||
|
this.$emit('hide');
|
||||||
|
},
|
||||||
|
increment($ev, ref) {
|
||||||
|
$ev.preventDefault();
|
||||||
|
const step = (ref === 'inputHrs') ? 1 : 5;
|
||||||
|
const maxVal = (ref === 'inputHrs') ? 23 : 59;
|
||||||
|
let cv = Math.round(parseInt(this[ref], 10) / step) * step;
|
||||||
|
cv = Math.min(maxVal, Math.max(0, ($ev.deltaY < 0) ? cv + step : cv - step));
|
||||||
|
this[ref] = cv.toString().padStart(2, '0');
|
||||||
|
this.validate();
|
||||||
|
},
|
||||||
|
setOffset() {
|
||||||
|
[this.inputHrs, this.inputMins, this.inputSecs] = this.offset.split(':');
|
||||||
|
this.numberSelect('inputHrs');
|
||||||
|
},
|
||||||
|
numberSelect(input) {
|
||||||
|
this.$refs[input].focus();
|
||||||
|
|
||||||
|
// change to text, select, then change back to number
|
||||||
|
// number inputs do not support select()
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs[input].setAttribute('type', 'text');
|
||||||
|
this.$refs[input].select();
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs[input].setAttribute('type', 'number');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
selectAll($ev) {
|
||||||
|
$ev.target.select();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@ -375,6 +375,10 @@
|
|||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: $shdwInputFoc;
|
box-shadow: $shdwInputFoc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&::selection {
|
||||||
|
background: rgba($colorKeySelectedBg, 0.5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin inlineInput() {
|
@mixin inlineInput() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user