mirror of
https://github.com/nasa/openmct.git
synced 2025-03-22 12:05:19 +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 -->
|
||||
<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"
|
||||
v-model="offsets.start"
|
||||
class="c-input--hrs-min-sec"
|
||||
type="text"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
@change="validateAllOffsets(); submitForm()"
|
||||
>
|
||||
{{ offsets.start }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="c-ctrl-wrapper c-conductor-input c-conductor__end-fixed">
|
||||
@ -114,15 +121,22 @@
|
||||
>
|
||||
<!-- RT end -->
|
||||
<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"
|
||||
v-model="offsets.end"
|
||||
class="c-input--hrs-min-sec"
|
||||
type="text"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
@change="validateAllOffsets(); submitForm()"
|
||||
class="c-button c-conductor__delta-button"
|
||||
@click="showTimePopupEnd"
|
||||
>
|
||||
{{offsets.end}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<conductor-axis
|
||||
@ -164,6 +178,7 @@ import DatePicker from './DatePicker.vue';
|
||||
import ConductorAxis from './ConductorAxis.vue';
|
||||
import ConductorModeIcon from './ConductorModeIcon.vue';
|
||||
import ConductorHistory from './ConductorHistory.vue';
|
||||
import TimePopup from './timePopup.vue';
|
||||
|
||||
const DEFAULT_DURATION_FORMATTER = 'duration';
|
||||
|
||||
@ -174,7 +189,8 @@ export default {
|
||||
DatePicker,
|
||||
ConductorAxis,
|
||||
ConductorModeIcon,
|
||||
ConductorHistory
|
||||
ConductorHistory,
|
||||
TimePopup
|
||||
},
|
||||
inject: ['openmct', 'configuration'],
|
||||
data() {
|
||||
@ -209,7 +225,9 @@ export default {
|
||||
showDatePicker: false,
|
||||
altPressed: false,
|
||||
isPanning: false,
|
||||
isZooming: false
|
||||
isZooming: false,
|
||||
showTCInputStart: false,
|
||||
showTCInputEnd: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -458,6 +476,23 @@ export default {
|
||||
this.formattedBounds.end = this.timeFormatter.format(date);
|
||||
this.validateAllBounds('endDate');
|
||||
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 {
|
||||
button {
|
||||
.c-conductor__controls button,
|
||||
.c-conductor__delta-button {
|
||||
@include themedButton($colorTimeBg);
|
||||
color: $colorTimeFg;
|
||||
|
||||
&:hover {
|
||||
background: $colorTimeHov !important;
|
||||
color: $colorTimeFg !important;
|
||||
}
|
||||
}
|
||||
|
||||
.c-conductor-input {
|
||||
@ -233,6 +229,59 @@
|
||||
box-shadow: none;
|
||||
color: $colorTime;
|
||||
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 {
|
||||
box-shadow: $shdwInputFoc;
|
||||
}
|
||||
|
||||
&::selection {
|
||||
background: rgba($colorKeySelectedBg, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin inlineInput() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user