Compare commits
	
		
			65 Commits
		
	
	
		
			eslint-one
			...
			time-condu
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					a7fb75aec6 | ||
| 
						 | 
					bbac4df4b8 | ||
| 
						 | 
					ed2d728f1d | ||
| 
						 | 
					a4ae654de9 | ||
| 
						 | 
					378769fca7 | ||
| 
						 | 
					2aac221e51 | ||
| 
						 | 
					1055733ff5 | ||
| 
						 | 
					dc642f826e | ||
| 
						 | 
					920b7a0c72 | ||
| 
						 | 
					ba7e7321df | ||
| 
						 | 
					30ed5d8fdb | ||
| 
						 | 
					5dc8f2b0d2 | ||
| 
						 | 
					8161ed7ea6 | ||
| 
						 | 
					9644208e6d | ||
| 
						 | 
					42b6eb158a | ||
| 
						 | 
					bac9991855 | ||
| 
						 | 
					1f545cb969 | ||
| 
						 | 
					9271259a4c | ||
| 
						 | 
					5f51a7cc90 | ||
| 
						 | 
					445b9f3788 | ||
| 
						 | 
					acc0abc903 | ||
| 
						 | 
					27dfda904e | ||
| 
						 | 
					7aad1101b4 | ||
| 
						 | 
					46d8d95583 | ||
| 
						 | 
					f6bd76be0e | ||
| 
						 | 
					b527bf3810 | ||
| 
						 | 
					b729c5132b | ||
| 
						 | 
					4793fae5d1 | ||
| 
						 | 
					26ba2f889e | ||
| 
						 | 
					a4956edf7b | ||
| 
						 | 
					987e0c698c | ||
| 
						 | 
					a4200e81d9 | ||
| 
						 | 
					2b5706f757 | ||
| 
						 | 
					2c2d674b99 | ||
| 
						 | 
					aecbbf24b0 | ||
| 
						 | 
					0ad6a595b0 | ||
| 
						 | 
					a6ac54383d | ||
| 
						 | 
					490f25add8 | ||
| 
						 | 
					694255db6b | ||
| 
						 | 
					6910ae0a2b | ||
| 
						 | 
					b3bf7f2db1 | ||
| 
						 | 
					348ba9085b | ||
| 
						 | 
					9d6a5e2e17 | ||
| 
						 | 
					5cb0e2e885 | ||
| 
						 | 
					ee277f3547 | ||
| 
						 | 
					bf77d240c7 | ||
| 
						 | 
					684a3d2807 | ||
| 
						 | 
					27e01ef13f | ||
| 
						 | 
					4a2b1640e9 | ||
| 
						 | 
					38cb92b203 | ||
| 
						 | 
					0792ae0ae4 | ||
| 
						 | 
					d462f5d763 | ||
| 
						 | 
					cb8e97dc1c | ||
| 
						 | 
					aed3c19edd | ||
| 
						 | 
					a19c5cfd52 | ||
| 
						 | 
					03e5241041 | ||
| 
						 | 
					29ed251685 | ||
| 
						 | 
					60433a12b8 | ||
| 
						 | 
					69b8dd6c37 | ||
| 
						 | 
					e6c78c1826 | ||
| 
						 | 
					a1657817dc | ||
| 
						 | 
					29538e6e78 | ||
| 
						 | 
					68e9152d6a | ||
| 
						 | 
					02b2b47411 | ||
| 
						 | 
					70624c2c5c | 
							
								
								
									
										35
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								index.html
									
									
									
									
									
								
							@@ -34,8 +34,8 @@
 | 
			
		||||
    <body>
 | 
			
		||||
    </body>
 | 
			
		||||
    <script>
 | 
			
		||||
        const FIVE_MINUTES = 5 * 60 * 1000;
 | 
			
		||||
        const THIRTY_MINUTES = 30 * 60 * 1000;
 | 
			
		||||
        const THIRTY_SECONDS = 30 * 1000;
 | 
			
		||||
        const THIRTY_MINUTES = THIRTY_SECONDS * 60;
 | 
			
		||||
 | 
			
		||||
        [
 | 
			
		||||
            'example/eventGenerator'
 | 
			
		||||
@@ -63,7 +63,34 @@
 | 
			
		||||
                    bounds: {
 | 
			
		||||
                        start: Date.now() - THIRTY_MINUTES,
 | 
			
		||||
                        end: Date.now()
 | 
			
		||||
                    }
 | 
			
		||||
                    },
 | 
			
		||||
                    presets: [
 | 
			
		||||
                        {
 | 
			
		||||
                            label: 'Last Day',
 | 
			
		||||
                            bounds: {
 | 
			
		||||
                                start: Date.now() - 1000 * 60 * 60 * 24,
 | 
			
		||||
                                end: Date.now()
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            label: 'Last 2 hours',
 | 
			
		||||
                            bounds: {
 | 
			
		||||
                                start: Date.now() - 1000 * 60 * 60 * 2,
 | 
			
		||||
                                end: Date.now()
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            label: 'Last hour',
 | 
			
		||||
                            bounds: {
 | 
			
		||||
                                start: Date.now() - 1000 * 60 * 60,
 | 
			
		||||
                                end: Date.now()
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    ],
 | 
			
		||||
                    // maximum entries to retain in conductor history
 | 
			
		||||
                    records: 10,
 | 
			
		||||
                    // maximum duration between start and end bounds
 | 
			
		||||
                    limit: 1000 * 60 * 60 * 24
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: "Realtime",
 | 
			
		||||
@@ -71,7 +98,7 @@
 | 
			
		||||
                    clock: 'local',
 | 
			
		||||
                    clockOffsets: {
 | 
			
		||||
                        start: - THIRTY_MINUTES,
 | 
			
		||||
                        end: FIVE_MINUTES
 | 
			
		||||
                        end: THIRTY_SECONDS
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,7 @@ define([], function () {
 | 
			
		||||
        this.timeFormat = 'local-format';
 | 
			
		||||
        this.durationFormat = 'duration';
 | 
			
		||||
 | 
			
		||||
        this.isUTCBased = false;
 | 
			
		||||
        this.isUTCBased = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return LocalTimeSystem;
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,12 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div
 | 
			
		||||
    class="c-conductor"
 | 
			
		||||
    :class="[isFixed ? 'is-fixed-mode' : 'is-realtime-mode']"
 | 
			
		||||
    :class="[
 | 
			
		||||
        { 'is-zooming': isZooming },
 | 
			
		||||
        { 'is-panning': isPanning },
 | 
			
		||||
        { 'alt-pressed': altPressed },
 | 
			
		||||
        isFixed ? 'is-fixed-mode' : 'is-realtime-mode'
 | 
			
		||||
    ]"
 | 
			
		||||
>
 | 
			
		||||
    <form
 | 
			
		||||
        ref="conductorForm"
 | 
			
		||||
@@ -52,7 +57,7 @@
 | 
			
		||||
                    type="text"
 | 
			
		||||
                    autocorrect="off"
 | 
			
		||||
                    spellcheck="false"
 | 
			
		||||
                    @change="validateAllBounds(); submitForm()"
 | 
			
		||||
                    @change="validateAllBounds('startDate'); submitForm()"
 | 
			
		||||
                >
 | 
			
		||||
                <date-picker
 | 
			
		||||
                    v-if="isFixed && isUTCBased"
 | 
			
		||||
@@ -92,7 +97,7 @@
 | 
			
		||||
                    autocorrect="off"
 | 
			
		||||
                    spellcheck="false"
 | 
			
		||||
                    :disabled="!isFixed"
 | 
			
		||||
                    @change="validateAllBounds(); submitForm()"
 | 
			
		||||
                    @change="validateAllBounds('endDate'); submitForm()"
 | 
			
		||||
                >
 | 
			
		||||
                <date-picker
 | 
			
		||||
                    v-if="isFixed && isUTCBased"
 | 
			
		||||
@@ -122,14 +127,25 @@
 | 
			
		||||
 | 
			
		||||
            <conductor-axis
 | 
			
		||||
                class="c-conductor__ticks"
 | 
			
		||||
                :bounds="rawBounds"
 | 
			
		||||
                @panAxis="setViewFromBounds"
 | 
			
		||||
                :view-bounds="viewBounds"
 | 
			
		||||
                :is-fixed="isFixed"
 | 
			
		||||
                :alt-pressed="altPressed"
 | 
			
		||||
                @endPan="endPan"
 | 
			
		||||
                @endZoom="endZoom"
 | 
			
		||||
                @panAxis="pan"
 | 
			
		||||
                @zoomAxis="zoom"
 | 
			
		||||
            />
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="c-conductor__controls">
 | 
			
		||||
            <!-- Mode, time system menu buttons and duration slider -->
 | 
			
		||||
            <ConductorMode class="c-conductor__mode-select" />
 | 
			
		||||
            <ConductorTimeSystem class="c-conductor__time-system-select" />
 | 
			
		||||
            <ConductorHistory
 | 
			
		||||
                v-if="isFixed"
 | 
			
		||||
                class="c-conductor__history-select"
 | 
			
		||||
                :bounds="openmct.time.bounds()"
 | 
			
		||||
                :time-system="timeSystem"
 | 
			
		||||
            />
 | 
			
		||||
        </div>
 | 
			
		||||
        <input
 | 
			
		||||
            type="submit"
 | 
			
		||||
@@ -145,6 +161,7 @@ import ConductorTimeSystem from './ConductorTimeSystem.vue';
 | 
			
		||||
import DatePicker from './DatePicker.vue';
 | 
			
		||||
import ConductorAxis from './ConductorAxis.vue';
 | 
			
		||||
import ConductorModeIcon from './ConductorModeIcon.vue';
 | 
			
		||||
import ConductorHistory from './ConductorHistory.vue'
 | 
			
		||||
 | 
			
		||||
const DEFAULT_DURATION_FORMATTER = 'duration';
 | 
			
		||||
 | 
			
		||||
@@ -155,7 +172,8 @@ export default {
 | 
			
		||||
        ConductorTimeSystem,
 | 
			
		||||
        DatePicker,
 | 
			
		||||
        ConductorAxis,
 | 
			
		||||
        ConductorModeIcon
 | 
			
		||||
        ConductorModeIcon,
 | 
			
		||||
        ConductorHistory
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        let bounds = this.openmct.time.bounds();
 | 
			
		||||
@@ -165,6 +183,7 @@ export default {
 | 
			
		||||
        let durationFormatter = this.getFormatter(timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER);
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            timeSystem: timeSystem,
 | 
			
		||||
            timeFormatter: timeFormatter,
 | 
			
		||||
            durationFormatter: durationFormatter,
 | 
			
		||||
            offsets: {
 | 
			
		||||
@@ -175,29 +194,64 @@ export default {
 | 
			
		||||
                start: timeFormatter.format(bounds.start),
 | 
			
		||||
                end: timeFormatter.format(bounds.end)
 | 
			
		||||
            },
 | 
			
		||||
            rawBounds: {
 | 
			
		||||
            viewBounds: {
 | 
			
		||||
                start: bounds.start,
 | 
			
		||||
                end: bounds.end
 | 
			
		||||
            },
 | 
			
		||||
            isFixed: this.openmct.time.clock() === undefined,
 | 
			
		||||
            isUTCBased: timeSystem.isUTCBased,
 | 
			
		||||
            showDatePicker: false
 | 
			
		||||
            showDatePicker: false,
 | 
			
		||||
            altPressed: false,
 | 
			
		||||
            isPanning: false,
 | 
			
		||||
            isZooming: false
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    created() {
 | 
			
		||||
        document.addEventListener('keydown', (e) => {
 | 
			
		||||
            if (e.key === 'Alt') {
 | 
			
		||||
                this.altPressed = true;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        document.addEventListener('keyup', (e) => {
 | 
			
		||||
            if (e.key === 'Alt') {
 | 
			
		||||
                this.altPressed = false;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.setTimeSystem(JSON.parse(JSON.stringify(this.openmct.time.timeSystem())));
 | 
			
		||||
 | 
			
		||||
        this.openmct.time.on('bounds', this.setViewFromBounds);
 | 
			
		||||
        this.openmct.time.on('timeSystem', this.setTimeSystem);
 | 
			
		||||
        this.openmct.time.on('clock', this.setViewFromClock);
 | 
			
		||||
        this.openmct.time.on('clockOffsets', this.setViewFromOffsets)
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        pan(bounds) {
 | 
			
		||||
            this.isPanning = true;
 | 
			
		||||
            this.setViewFromBounds(bounds);
 | 
			
		||||
        },
 | 
			
		||||
        endPan(bounds) {
 | 
			
		||||
            this.isPanning = false;
 | 
			
		||||
            if (bounds) {
 | 
			
		||||
                this.openmct.time.bounds(bounds);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        zoom(bounds) {
 | 
			
		||||
            this.isZooming = true;
 | 
			
		||||
            this.formattedBounds.start = this.timeFormatter.format(bounds.start);
 | 
			
		||||
            this.formattedBounds.end = this.timeFormatter.format(bounds.end);
 | 
			
		||||
        },
 | 
			
		||||
        endZoom(bounds) {
 | 
			
		||||
            const _bounds = bounds ? bounds : this.openmct.time.bounds();
 | 
			
		||||
            this.isZooming = false;
 | 
			
		||||
 | 
			
		||||
            this.openmct.time.bounds(_bounds);
 | 
			
		||||
        },
 | 
			
		||||
        setTimeSystem(timeSystem) {
 | 
			
		||||
            this.timeSystem = timeSystem
 | 
			
		||||
            this.timeFormatter = this.getFormatter(timeSystem.timeFormat);
 | 
			
		||||
            this.durationFormatter = this.getFormatter(
 | 
			
		||||
                timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER);
 | 
			
		||||
 | 
			
		||||
            this.isUTCBased = timeSystem.isUTCBased;
 | 
			
		||||
        },
 | 
			
		||||
        setOffsetsFromView($event) {
 | 
			
		||||
@@ -237,8 +291,8 @@ export default {
 | 
			
		||||
        setViewFromBounds(bounds) {
 | 
			
		||||
            this.formattedBounds.start = this.timeFormatter.format(bounds.start);
 | 
			
		||||
            this.formattedBounds.end = this.timeFormatter.format(bounds.end);
 | 
			
		||||
            this.rawBounds.start = bounds.start;
 | 
			
		||||
            this.rawBounds.end = bounds.end;
 | 
			
		||||
            this.viewBounds.start = bounds.start;
 | 
			
		||||
            this.viewBounds.end = bounds.end;
 | 
			
		||||
        },
 | 
			
		||||
        setViewFromOffsets(offsets) {
 | 
			
		||||
            this.offsets.start = this.durationFormatter.format(Math.abs(offsets.start));
 | 
			
		||||
@@ -251,6 +305,15 @@ export default {
 | 
			
		||||
                this.setOffsetsFromView();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        getBoundsLimit() {
 | 
			
		||||
            const configuration = this.configuration.menuOptions
 | 
			
		||||
                .filter(option => option.timeSystem ===  this.timeSystem.key)
 | 
			
		||||
                .find(option => option.limit);
 | 
			
		||||
 | 
			
		||||
            const limit = configuration ? configuration.limit : undefined;
 | 
			
		||||
 | 
			
		||||
            return limit;
 | 
			
		||||
        },
 | 
			
		||||
        clearAllValidation() {
 | 
			
		||||
            if (this.isFixed) {
 | 
			
		||||
                [this.$refs.startDate, this.$refs.endDate].forEach(this.clearValidationForInput);
 | 
			
		||||
@@ -262,37 +325,37 @@ export default {
 | 
			
		||||
            input.setCustomValidity('');
 | 
			
		||||
            input.title = '';
 | 
			
		||||
        },
 | 
			
		||||
        validateAllBounds() {
 | 
			
		||||
            return [this.$refs.startDate, this.$refs.endDate].every((input) => {
 | 
			
		||||
                let validationResult = true;
 | 
			
		||||
                let formattedDate;
 | 
			
		||||
        validateAllBounds(ref) {
 | 
			
		||||
            const input = this.$refs[ref];
 | 
			
		||||
            let validationResult = true;
 | 
			
		||||
 | 
			
		||||
                if (input === this.$refs.startDate) {
 | 
			
		||||
                    formattedDate = this.formattedBounds.start;
 | 
			
		||||
                } else {
 | 
			
		||||
                    formattedDate = this.formattedBounds.end;
 | 
			
		||||
                }
 | 
			
		||||
            const formattedDate = input === this.$refs.startDate
 | 
			
		||||
                ? this.formattedBounds.start
 | 
			
		||||
                : this.formattedBounds.end
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
                if (!this.timeFormatter.validate(formattedDate)) {
 | 
			
		||||
                    validationResult = 'Invalid date';
 | 
			
		||||
            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)
 | 
			
		||||
                };
 | 
			
		||||
                const limit = this.getBoundsLimit();
 | 
			
		||||
 | 
			
		||||
                if (
 | 
			
		||||
                    this.timeSystem.isUTCBased
 | 
			
		||||
                    && limit
 | 
			
		||||
                    && boundsValues.end - boundsValues.start > limit
 | 
			
		||||
                ) {
 | 
			
		||||
                    validationResult = "Start and end difference exceeds allowable limit";
 | 
			
		||||
                } 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;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.handleValidationResult(input, validationResult);
 | 
			
		||||
        },
 | 
			
		||||
        validateAllOffsets(event) {
 | 
			
		||||
            return [this.$refs.startOffset, this.$refs.endOffset].every((input) => {
 | 
			
		||||
@@ -315,17 +378,20 @@ export default {
 | 
			
		||||
                    validationResult = this.openmct.time.validateOffsets(offsetValues);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (validationResult !== true) {
 | 
			
		||||
                    input.setCustomValidity(validationResult);
 | 
			
		||||
                    input.title = validationResult;
 | 
			
		||||
                    return false;
 | 
			
		||||
                } else {
 | 
			
		||||
                    input.setCustomValidity('');
 | 
			
		||||
                    input.title = '';
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
                this.handleValidationResult(input, validationResult);
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        handleValidationResult(input, validationResult) {
 | 
			
		||||
            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)
 | 
			
		||||
@@ -338,12 +404,12 @@ export default {
 | 
			
		||||
        },
 | 
			
		||||
        startDateSelected(date) {
 | 
			
		||||
            this.formattedBounds.start = this.timeFormatter.format(date);
 | 
			
		||||
            this.validateAllBounds();
 | 
			
		||||
            this.validateAllBounds('startDate');
 | 
			
		||||
            this.submitForm();
 | 
			
		||||
        },
 | 
			
		||||
        endDateSelected(date) {
 | 
			
		||||
            this.formattedBounds.end = this.timeFormatter.format(date);
 | 
			
		||||
            this.validateAllBounds();
 | 
			
		||||
            this.validateAllBounds('endDate');
 | 
			
		||||
            this.submitForm();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,12 @@
 | 
			
		||||
    ref="axisHolder"
 | 
			
		||||
    class="c-conductor-axis"
 | 
			
		||||
    @mousedown="dragStart($event)"
 | 
			
		||||
></div>
 | 
			
		||||
>
 | 
			
		||||
    <div
 | 
			
		||||
        class="c-conductor-axis__zoom-indicator"
 | 
			
		||||
        :style="zoomStyle"
 | 
			
		||||
    ></div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
@@ -43,14 +48,35 @@ const PIXELS_PER_TICK_WIDE = 200;
 | 
			
		||||
export default {
 | 
			
		||||
    inject: ['openmct'],
 | 
			
		||||
    props: {
 | 
			
		||||
        bounds: {
 | 
			
		||||
        viewBounds: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            required: true
 | 
			
		||||
        },
 | 
			
		||||
        isFixed: {
 | 
			
		||||
            type: Boolean,
 | 
			
		||||
            required: true
 | 
			
		||||
        },
 | 
			
		||||
        altPressed: {
 | 
			
		||||
            type: Boolean,
 | 
			
		||||
            required: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            inPanMode: false,
 | 
			
		||||
            dragStartX: undefined,
 | 
			
		||||
            dragX: undefined,
 | 
			
		||||
            zoomStyle: {}
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        inZoomMode() {
 | 
			
		||||
            return !this.inPanMode;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
        bounds: {
 | 
			
		||||
            handler(bounds) {
 | 
			
		||||
        viewBounds: {
 | 
			
		||||
            handler() {
 | 
			
		||||
                this.setScale();
 | 
			
		||||
            },
 | 
			
		||||
            deep: true
 | 
			
		||||
@@ -58,18 +84,23 @@ export default {
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        let axisHolder = this.$refs.axisHolder;
 | 
			
		||||
        let height = axisHolder.offsetHeight;
 | 
			
		||||
        this.height = axisHolder.offsetHeight;
 | 
			
		||||
        this.width = axisHolder.clientWidth;
 | 
			
		||||
        const rect = axisHolder.getBoundingClientRect();
 | 
			
		||||
        this.left = Math.round(rect.left);
 | 
			
		||||
 | 
			
		||||
        let vis = d3Selection.select(axisHolder)
 | 
			
		||||
            .append("svg:svg")
 | 
			
		||||
            .attr("width", "100%")
 | 
			
		||||
            .attr("height", height);
 | 
			
		||||
            .attr("height", this.height);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        this.width = this.$refs.axisHolder.clientWidth;
 | 
			
		||||
        this.xAxis = d3Axis.axisTop();
 | 
			
		||||
        this.dragging = false;
 | 
			
		||||
 | 
			
		||||
        // draw x axis with labels. CSS is used to position them.
 | 
			
		||||
        this.axisElement = vis.append("g");
 | 
			
		||||
        this.axisElement = vis.append("g")
 | 
			
		||||
            .attr("class", "axis");
 | 
			
		||||
 | 
			
		||||
        this.setViewFromTimeSystem(this.openmct.time.timeSystem());
 | 
			
		||||
        this.setScale();
 | 
			
		||||
@@ -83,12 +114,15 @@ export default {
 | 
			
		||||
    methods: {
 | 
			
		||||
        setScale() {
 | 
			
		||||
            let timeSystem = this.openmct.time.timeSystem();
 | 
			
		||||
            let bounds = this.bounds;
 | 
			
		||||
 | 
			
		||||
            if (timeSystem.isUTCBased) {
 | 
			
		||||
                this.xScale.domain([new Date(bounds.start), new Date(bounds.end)]);
 | 
			
		||||
                this.xScale.domain(
 | 
			
		||||
                    [new Date(this.viewBounds.start), new Date(this.viewBounds.end)]
 | 
			
		||||
                );
 | 
			
		||||
            } else {
 | 
			
		||||
                this.xScale.domain([bounds.start, bounds.end]);
 | 
			
		||||
                this.xScale.domain(
 | 
			
		||||
                    [this.viewBounds.start, this.viewBounds.end]
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.xAxis.scale(this.xScale);
 | 
			
		||||
@@ -102,7 +136,7 @@ export default {
 | 
			
		||||
                this.xAxis.ticks(this.width / PIXELS_PER_TICK);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.msPerPixel = (bounds.end - bounds.start) / this.width;
 | 
			
		||||
            this.msPerPixel = (this.viewBounds.end - this.viewBounds.start) / this.width;
 | 
			
		||||
        },
 | 
			
		||||
        setViewFromTimeSystem(timeSystem) {
 | 
			
		||||
            //The D3 scale used depends on the type of time system as d3
 | 
			
		||||
@@ -120,9 +154,8 @@ export default {
 | 
			
		||||
        },
 | 
			
		||||
        getActiveFormatter() {
 | 
			
		||||
            let timeSystem = this.openmct.time.timeSystem();
 | 
			
		||||
            let isFixed = this.openmct.time.clock() === undefined;
 | 
			
		||||
 | 
			
		||||
            if (isFixed) {
 | 
			
		||||
            if (this.isFixed) {
 | 
			
		||||
                return this.getFormatter(timeSystem.timeFormat);
 | 
			
		||||
            } else {
 | 
			
		||||
                return this.getFormatter(timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER);
 | 
			
		||||
@@ -134,42 +167,128 @@ export default {
 | 
			
		||||
            }).formatter;
 | 
			
		||||
        },
 | 
			
		||||
        dragStart($event) {
 | 
			
		||||
            let isFixed = this.openmct.time.clock() === undefined;
 | 
			
		||||
            if (isFixed) {
 | 
			
		||||
            if (this.isFixed) {
 | 
			
		||||
                this.dragStartX = $event.clientX;
 | 
			
		||||
 | 
			
		||||
                if (this.altPressed) {
 | 
			
		||||
                    this.inPanMode = true;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                document.addEventListener('mousemove', this.drag);
 | 
			
		||||
                document.addEventListener('mouseup', this.dragEnd, {
 | 
			
		||||
                    once: true
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                if (this.inZoomMode) {
 | 
			
		||||
                    this.startZoom();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        drag($event) {
 | 
			
		||||
            if (!this.dragging) {
 | 
			
		||||
                this.dragging = true;
 | 
			
		||||
                requestAnimationFrame(()=>{
 | 
			
		||||
                    let deltaX = $event.clientX - this.dragStartX;
 | 
			
		||||
                    let percX = deltaX / this.width;
 | 
			
		||||
                    let bounds = this.openmct.time.bounds();
 | 
			
		||||
                    let deltaTime = bounds.end - bounds.start;
 | 
			
		||||
                    let newStart = bounds.start - percX * deltaTime;
 | 
			
		||||
                    this.$emit('panAxis',{
 | 
			
		||||
                        start: newStart,
 | 
			
		||||
                        end: newStart + deltaTime
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                requestAnimationFrame(() => {
 | 
			
		||||
                    this.dragX = $event.clientX;
 | 
			
		||||
                    this.inPanMode ? this.pan() : this.zoom();
 | 
			
		||||
                    this.dragging = false;
 | 
			
		||||
                })
 | 
			
		||||
            } else {
 | 
			
		||||
                console.log('Rejected drag due to RAF cap');
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        dragEnd() {
 | 
			
		||||
            this.inPanMode ? this.endPan() : this.endZoom();
 | 
			
		||||
 | 
			
		||||
            document.removeEventListener('mousemove', this.drag);
 | 
			
		||||
            this.openmct.time.bounds({
 | 
			
		||||
                start: this.bounds.start,
 | 
			
		||||
                end: this.bounds.end
 | 
			
		||||
            this.dragStartX = undefined;
 | 
			
		||||
            this.dragX = undefined;
 | 
			
		||||
        },
 | 
			
		||||
        pan() {
 | 
			
		||||
            const panBounds = this.getPanBounds();
 | 
			
		||||
            this.$emit('panAxis', panBounds);
 | 
			
		||||
        },
 | 
			
		||||
        endPan() {
 | 
			
		||||
            const panBounds = this.dragStartX && this.dragX && this.dragStartX !== this.dragX
 | 
			
		||||
                ? this.getPanBounds()
 | 
			
		||||
                : undefined;
 | 
			
		||||
            this.$emit('endPan', panBounds);
 | 
			
		||||
            this.inPanMode = false;
 | 
			
		||||
        },
 | 
			
		||||
        getPanBounds() {
 | 
			
		||||
            const bounds = this.openmct.time.bounds();
 | 
			
		||||
            const deltaTime = bounds.end - bounds.start;
 | 
			
		||||
            const deltaX = this.dragX - this.dragStartX;
 | 
			
		||||
            const percX = deltaX / this.width;
 | 
			
		||||
            const panStart = bounds.start - percX * deltaTime;
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                start: panStart,
 | 
			
		||||
                end: panStart + deltaTime
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        startZoom() {
 | 
			
		||||
            const x = this.scaleToBounds(this.dragStartX);
 | 
			
		||||
 | 
			
		||||
            this.zoomStyle = {
 | 
			
		||||
                left: `${this.dragStartX - this.left}px`
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.$emit('zoomAxis', {
 | 
			
		||||
                start: x,
 | 
			
		||||
                end: x
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        zoom() {
 | 
			
		||||
            const zoomRange = this.getZoomRange();
 | 
			
		||||
 | 
			
		||||
            this.zoomStyle = {
 | 
			
		||||
                left: `${zoomRange.start - this.left}px`,
 | 
			
		||||
                width: `${zoomRange.end - zoomRange.start}px`
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.$emit('zoomAxis', {
 | 
			
		||||
                start: this.scaleToBounds(zoomRange.start),
 | 
			
		||||
                end: this.scaleToBounds(zoomRange.end)
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        endZoom() {
 | 
			
		||||
            const zoomRange = this.dragStartX && this.dragX && this.dragStartX !== this.dragX
 | 
			
		||||
                ? this.getZoomRange()
 | 
			
		||||
                : undefined;
 | 
			
		||||
 | 
			
		||||
            const zoomBounds = zoomRange
 | 
			
		||||
                ? {
 | 
			
		||||
                    start: this.scaleToBounds(zoomRange.start),
 | 
			
		||||
                    end: this.scaleToBounds(zoomRange.end)
 | 
			
		||||
                }
 | 
			
		||||
                : this.openmct.time.bounds();
 | 
			
		||||
 | 
			
		||||
            this.zoomStyle = {};
 | 
			
		||||
            this.$emit('endZoom', zoomBounds);
 | 
			
		||||
        },
 | 
			
		||||
        getZoomRange() {
 | 
			
		||||
            const leftBound = this.left;
 | 
			
		||||
            const rightBound = this.left + this.width;
 | 
			
		||||
 | 
			
		||||
            const zoomStart = this.dragX < leftBound
 | 
			
		||||
                ? leftBound
 | 
			
		||||
                : Math.min(this.dragX, this.dragStartX);
 | 
			
		||||
 | 
			
		||||
            const zoomEnd = this.dragX > rightBound
 | 
			
		||||
                ? rightBound
 | 
			
		||||
                : Math.max(this.dragX, this.dragStartX);
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                start: zoomStart,
 | 
			
		||||
                end: zoomEnd
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        scaleToBounds(value) {
 | 
			
		||||
            const bounds = this.openmct.time.bounds();
 | 
			
		||||
            const timeDelta = bounds.end - bounds.start;
 | 
			
		||||
            const valueDelta = value - this.left;
 | 
			
		||||
            const offset = valueDelta / this.width * timeDelta;
 | 
			
		||||
            return bounds.start + offset;
 | 
			
		||||
        },
 | 
			
		||||
        resize() {
 | 
			
		||||
            if (this.$refs.axisHolder.clientWidth !== this.width) {
 | 
			
		||||
                this.width = this.$refs.axisHolder.clientWidth;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										198
									
								
								src/plugins/timeConductor/ConductorHistory.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								src/plugins/timeConductor/ConductorHistory.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,198 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2018, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
<template>
 | 
			
		||||
<div class="c-ctrl-wrapper c-ctrl-wrapper--menus-up">
 | 
			
		||||
    <button class="c-button--menu c-history-button icon-history"
 | 
			
		||||
            @click.prevent="toggle"
 | 
			
		||||
    >
 | 
			
		||||
        <span class="c-button__label">History</span>
 | 
			
		||||
    </button>
 | 
			
		||||
    <div v-if="open"
 | 
			
		||||
         class="c-menu c-conductor__history-menu"
 | 
			
		||||
    >
 | 
			
		||||
        <ul v-if="hasHistoryPresets">
 | 
			
		||||
            <li
 | 
			
		||||
                v-for="preset in presets"
 | 
			
		||||
                :key="preset.label"
 | 
			
		||||
                class="icon-clock"
 | 
			
		||||
                @click="selectTimespan(preset.bounds)"
 | 
			
		||||
            >
 | 
			
		||||
                {{ preset.label }}
 | 
			
		||||
            </li>
 | 
			
		||||
        </ul>
 | 
			
		||||
 | 
			
		||||
        <div
 | 
			
		||||
            v-if="hasHistoryPresets"
 | 
			
		||||
            class="c-menu__section-separator"
 | 
			
		||||
        ></div>
 | 
			
		||||
 | 
			
		||||
        <div class="c-menu__section-hint">
 | 
			
		||||
            Past timeframes, ordered by latest first
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <ul>
 | 
			
		||||
            <li
 | 
			
		||||
                v-for="(timespan, index) in historyForCurrentTimeSystem"
 | 
			
		||||
                :key="index"
 | 
			
		||||
                class="icon-history"
 | 
			
		||||
                @click="selectTimespan(timespan)"
 | 
			
		||||
            >
 | 
			
		||||
                {{ formatTime(timespan.start) }} - {{ formatTime(timespan.end) }}
 | 
			
		||||
            </li>
 | 
			
		||||
        </ul>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import toggleMixin from '../../ui/mixins/toggle-mixin';
 | 
			
		||||
 | 
			
		||||
const LOCAL_STORAGE_HISTORY_KEY = 'tcHistory';
 | 
			
		||||
const DEFAULT_RECORDS = 10;
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    inject: ['openmct', 'configuration'],
 | 
			
		||||
    mixins: [toggleMixin],
 | 
			
		||||
    props: {
 | 
			
		||||
        bounds: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            required: true
 | 
			
		||||
        },
 | 
			
		||||
        timeSystem: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            required: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            history: {}, // contains arrays of timespans {start, end}, array key is time system key
 | 
			
		||||
            presets: []
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        hasHistoryPresets() {
 | 
			
		||||
            return this.timeSystem.isUTCBased && this.presets.length;
 | 
			
		||||
        },
 | 
			
		||||
        historyForCurrentTimeSystem() {
 | 
			
		||||
            const history = this.history[this.timeSystem.key];
 | 
			
		||||
 | 
			
		||||
            return history;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
        bounds: {
 | 
			
		||||
            handler() {
 | 
			
		||||
                this.addTimespan();
 | 
			
		||||
            },
 | 
			
		||||
            deep: true
 | 
			
		||||
        },
 | 
			
		||||
        timeSystem: {
 | 
			
		||||
            handler() {
 | 
			
		||||
                this.loadConfiguration();
 | 
			
		||||
                this.addTimespan();
 | 
			
		||||
            },
 | 
			
		||||
            deep: true
 | 
			
		||||
        },
 | 
			
		||||
        history: {
 | 
			
		||||
            handler() {
 | 
			
		||||
                this.persistHistoryToLocalStorage();
 | 
			
		||||
            },
 | 
			
		||||
            deep: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.getHistoryFromLocalStorage();
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        getHistoryFromLocalStorage() {
 | 
			
		||||
            if (localStorage.getItem(LOCAL_STORAGE_HISTORY_KEY)) {
 | 
			
		||||
                this.history = JSON.parse(localStorage.getItem(LOCAL_STORAGE_HISTORY_KEY))
 | 
			
		||||
            } else {
 | 
			
		||||
                this.history = {};
 | 
			
		||||
                this.persistHistoryToLocalStorage();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        persistHistoryToLocalStorage() {
 | 
			
		||||
            localStorage.setItem(LOCAL_STORAGE_HISTORY_KEY, JSON.stringify(this.history));
 | 
			
		||||
        },
 | 
			
		||||
        addTimespan() {
 | 
			
		||||
            const key = this.timeSystem.key;
 | 
			
		||||
            let [...currentHistory] = this.history[key] || [];
 | 
			
		||||
            const timespan = {
 | 
			
		||||
                start: this.bounds.start,
 | 
			
		||||
                end: this.bounds.end
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            const isNotEqual = function (entry) {
 | 
			
		||||
                const start = entry.start !== this.start;
 | 
			
		||||
                const end = entry.end !== this.end;
 | 
			
		||||
 | 
			
		||||
                return start || end;
 | 
			
		||||
            };
 | 
			
		||||
            currentHistory = currentHistory.filter(isNotEqual, timespan);
 | 
			
		||||
 | 
			
		||||
            while (currentHistory.length >= this.records) {
 | 
			
		||||
                currentHistory.pop();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            currentHistory.unshift(timespan);
 | 
			
		||||
            this.history[key] = currentHistory;
 | 
			
		||||
        },
 | 
			
		||||
        selectTimespan(timespan) {
 | 
			
		||||
            this.openmct.time.bounds(timespan);
 | 
			
		||||
        },
 | 
			
		||||
        selectHours(hours) {
 | 
			
		||||
            const now = Date.now();
 | 
			
		||||
            this.selectTimespan({
 | 
			
		||||
                start: now - hours * 60 * 60 * 1000,
 | 
			
		||||
                end: now
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        loadConfiguration() {
 | 
			
		||||
            const configurations = this.configuration.menuOptions
 | 
			
		||||
                .filter(option => option.timeSystem ===  this.timeSystem.key);
 | 
			
		||||
 | 
			
		||||
            this.presets = this.loadPresets(configurations);
 | 
			
		||||
            this.records = this.loadRecords(configurations);
 | 
			
		||||
        },
 | 
			
		||||
        loadPresets(configurations) {
 | 
			
		||||
            const configuration = configurations.find(option => option.presets);
 | 
			
		||||
            const presets = configuration ? configuration.presets : [];
 | 
			
		||||
 | 
			
		||||
            return presets;
 | 
			
		||||
        },
 | 
			
		||||
        loadRecords(configurations) {
 | 
			
		||||
            const configuration = configurations.find(option => option.records);
 | 
			
		||||
            const records = configuration ? configuration.records : DEFAULT_RECORDS;
 | 
			
		||||
 | 
			
		||||
            return records;
 | 
			
		||||
        },
 | 
			
		||||
        formatTime(time) {
 | 
			
		||||
            const formatter = this.openmct.telemetry.getValueFormatter({
 | 
			
		||||
                format: this.timeSystem.timeFormat
 | 
			
		||||
            }).formatter;
 | 
			
		||||
 | 
			
		||||
            return formatter.format(time);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
@@ -110,7 +110,7 @@ export default {
 | 
			
		||||
            if (clock === undefined) {
 | 
			
		||||
                return {
 | 
			
		||||
                    key: 'fixed',
 | 
			
		||||
                    name: 'Fixed Timespan Mode',
 | 
			
		||||
                    name: 'Fixed Timespan',
 | 
			
		||||
                    description: 'Query and explore data that falls between two fixed datetimes.',
 | 
			
		||||
                    cssClass: 'icon-tabular'
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@
 | 
			
		||||
        text-rendering: geometricPrecision;
 | 
			
		||||
        width: 100%;
 | 
			
		||||
        height: 100%;
 | 
			
		||||
        > g {
 | 
			
		||||
        > g.axis {
 | 
			
		||||
            // Overall Tick holder
 | 
			
		||||
            transform: translateY($tickYPos);
 | 
			
		||||
            path {
 | 
			
		||||
@@ -44,7 +44,6 @@
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    body.desktop .is-fixed-mode & {
 | 
			
		||||
        @include cursorGrab();
 | 
			
		||||
        background-size: 3px 30%;
 | 
			
		||||
        background-color: $colorBodyBgSubtle;
 | 
			
		||||
        box-shadow: inset rgba(black, 0.4) 0 1px 1px;
 | 
			
		||||
@@ -55,17 +54,6 @@
 | 
			
		||||
            stroke: $colorBodyBgSubtle;
 | 
			
		||||
            transition: $transOut;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &:hover,
 | 
			
		||||
        &:active {
 | 
			
		||||
            $c: $colorKeySubtle;
 | 
			
		||||
            background-color: $c;
 | 
			
		||||
            transition: $transIn;
 | 
			
		||||
            svg text {
 | 
			
		||||
                stroke: $c;
 | 
			
		||||
                transition: $transIn;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .is-realtime-mode & {
 | 
			
		||||
 
 | 
			
		||||
@@ -57,6 +57,65 @@
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.is-fixed-mode {
 | 
			
		||||
        .c-conductor-axis {
 | 
			
		||||
            &__zoom-indicator {
 | 
			
		||||
                border: 1px solid transparent;
 | 
			
		||||
                display: none; // Hidden by default
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &:not(.is-panning),
 | 
			
		||||
        &:not(.is-zooming) {
 | 
			
		||||
            .c-conductor-axis {
 | 
			
		||||
                &:hover,
 | 
			
		||||
                &:active {
 | 
			
		||||
                    cursor: col-resize;
 | 
			
		||||
                    filter: $timeConductorAxisHoverFilter;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &.is-panning,
 | 
			
		||||
        &.is-zooming {
 | 
			
		||||
            .c-conductor-input input {
 | 
			
		||||
                // Styles for inputs while zooming or panning
 | 
			
		||||
                background: rgba($timeConductorActiveBg, 0.4);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &.alt-pressed {
 | 
			
		||||
            .c-conductor-axis:hover {
 | 
			
		||||
                // When alt is being pressed and user is hovering over the axis, set the cursor
 | 
			
		||||
                @include cursorGrab();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &.is-panning {
 | 
			
		||||
            .c-conductor-axis {
 | 
			
		||||
                @include cursorGrab();
 | 
			
		||||
                background-color: $timeConductorActivePanBg;
 | 
			
		||||
                transition: $transIn;
 | 
			
		||||
 | 
			
		||||
                svg text {
 | 
			
		||||
                    stroke: $timeConductorActivePanBg;
 | 
			
		||||
                    transition: $transIn;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &.is-zooming {
 | 
			
		||||
            .c-conductor-axis__zoom-indicator {
 | 
			
		||||
                display: block;
 | 
			
		||||
                position: absolute;
 | 
			
		||||
                background: rgba($timeConductorActiveBg, 0.4);
 | 
			
		||||
                border-left-color: $timeConductorActiveBg;
 | 
			
		||||
                border-right-color: $timeConductorActiveBg;
 | 
			
		||||
                top: 0; bottom: 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.is-realtime-mode {
 | 
			
		||||
        .c-conductor__time-bounds {
 | 
			
		||||
            grid-template-columns: 20px auto 1fr auto auto;
 | 
			
		||||
 
 | 
			
		||||
@@ -142,6 +142,9 @@ $colorTimeHov: pullForward($colorTime, 10%);
 | 
			
		||||
$colorTimeSubtle: pushBack($colorTime, 20%);
 | 
			
		||||
$colorTOI: $colorBodyFg; // was $timeControllerToiLineColor
 | 
			
		||||
$colorTOIHov: $colorTime; // was $timeControllerToiLineColorHov
 | 
			
		||||
$timeConductorAxisHoverFilter: brightness(1.2);
 | 
			
		||||
$timeConductorActiveBg: $colorKey;
 | 
			
		||||
$timeConductorActivePanBg: #226074;
 | 
			
		||||
 | 
			
		||||
/************************************************** BROWSING */
 | 
			
		||||
$browseFrameColor: pullForward($colorBodyBg, 10%);
 | 
			
		||||
 
 | 
			
		||||
@@ -146,6 +146,9 @@ $colorTimeHov: pullForward($colorTime, 10%);
 | 
			
		||||
$colorTimeSubtle: pushBack($colorTime, 20%);
 | 
			
		||||
$colorTOI: $colorBodyFg; // was $timeControllerToiLineColor
 | 
			
		||||
$colorTOIHov: $colorTime; // was $timeControllerToiLineColorHov
 | 
			
		||||
$timeConductorAxisHoverFilter: brightness(1.2);
 | 
			
		||||
$timeConductorActiveBg: $colorKey;
 | 
			
		||||
$timeConductorActivePanBg: #226074;
 | 
			
		||||
 | 
			
		||||
/************************************************** BROWSING */
 | 
			
		||||
$browseFrameColor: pullForward($colorBodyBg, 10%);
 | 
			
		||||
 
 | 
			
		||||
@@ -132,7 +132,7 @@ $colorPausedFg: #fff;
 | 
			
		||||
// Base variations
 | 
			
		||||
$colorBodyBgSubtle: pullForward($colorBodyBg, 5%);
 | 
			
		||||
$colorBodyBgSubtleHov: pushBack($colorKey, 50%);
 | 
			
		||||
$colorKeySubtle: pushBack($colorKey, 10%);
 | 
			
		||||
$colorKeySubtle: pushBack($colorKey, 20%);
 | 
			
		||||
 | 
			
		||||
// Time Colors
 | 
			
		||||
$colorTime: #618cff;
 | 
			
		||||
@@ -142,6 +142,9 @@ $colorTimeHov: pushBack($colorTime, 5%);
 | 
			
		||||
$colorTimeSubtle: pushBack($colorTime, 20%);
 | 
			
		||||
$colorTOI: $colorBodyFg; // was $timeControllerToiLineColor
 | 
			
		||||
$colorTOIHov: $colorTime; // was $timeControllerToiLineColorHov
 | 
			
		||||
$timeConductorAxisHoverFilter: brightness(0.8);
 | 
			
		||||
$timeConductorActiveBg: $colorKey;
 | 
			
		||||
$timeConductorActivePanBg: #A0CDE1;
 | 
			
		||||
 | 
			
		||||
/************************************************** BROWSING */
 | 
			
		||||
$browseFrameColor: pullForward($colorBodyBg, 10%);
 | 
			
		||||
 
 | 
			
		||||
@@ -462,9 +462,17 @@ select {
 | 
			
		||||
    text-shadow: $shdwMenuText;
 | 
			
		||||
    padding: $interiorMarginSm;
 | 
			
		||||
    box-shadow: $shdwMenu;
 | 
			
		||||
    display: block;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    z-index: 100;
 | 
			
		||||
 | 
			
		||||
    > * {
 | 
			
		||||
        flex: 0 0 auto;
 | 
			
		||||
        //+ * {
 | 
			
		||||
        //    margin-top: $interiorMarginSm;
 | 
			
		||||
        //}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@mixin menuInner() {
 | 
			
		||||
@@ -502,6 +510,23 @@ select {
 | 
			
		||||
.c-menu {
 | 
			
		||||
    @include menuOuter();
 | 
			
		||||
    @include menuInner();
 | 
			
		||||
 | 
			
		||||
    &__section-hint {
 | 
			
		||||
        $m: $interiorMargin;
 | 
			
		||||
        margin: $m 0;
 | 
			
		||||
        padding: $m nth($menuItemPad, 2) 0 nth($menuItemPad, 2);
 | 
			
		||||
 | 
			
		||||
        opacity: 0.6;
 | 
			
		||||
        font-size: 0.9em;
 | 
			
		||||
        font-style: italic;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__section-separator {
 | 
			
		||||
        $m: $interiorMargin;
 | 
			
		||||
        border-top: 1px solid $colorInteriorBorder;
 | 
			
		||||
        margin: $m 0;
 | 
			
		||||
        padding: $m nth($menuItemPad, 2) 0 nth($menuItemPad, 2);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-super-menu {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user