adds legend tooltips
This commit is contained in:
@@ -7,7 +7,9 @@
|
|||||||
<title>Vite App</title>
|
<title>Vite App</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<main>
|
||||||
|
<div id="app"></div>
|
||||||
|
</main>
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@use 'sass:color';
|
||||||
|
|
||||||
$gray-1: oklch(90% 0 0);
|
$gray-1: oklch(90% 0 0);
|
||||||
$gray-2: oklch(80% 0 0);
|
$gray-2: oklch(80% 0 0);
|
||||||
$gray-3: oklch(70% 0 0);
|
$gray-3: oklch(70% 0 0);
|
||||||
@@ -24,6 +26,8 @@ $magenta-2: oklch(75% 0.2 330);
|
|||||||
|
|
||||||
$text-color: $gray-1;
|
$text-color: $gray-1;
|
||||||
$background-color: $gray-7;
|
$background-color: $gray-7;
|
||||||
|
$light-background-color: color.adjust($background-color, $lightness: 5%);
|
||||||
|
$dark-background-color: color.adjust($background-color, $lightness: -5%);
|
||||||
|
|
||||||
$time-tick: $gray-1;
|
$time-tick: $gray-1;
|
||||||
$grid-line: $gray-1;
|
$grid-line: $gray-1;
|
||||||
@@ -31,6 +35,7 @@ $major-tick: $gray-4;
|
|||||||
$minor-tick: $gray-5;
|
$minor-tick: $gray-5;
|
||||||
|
|
||||||
$text-font: Helvetica, sans-serif;
|
$text-font: Helvetica, sans-serif;
|
||||||
|
$large-text-size: 20px;
|
||||||
$normal-text-size: 16px;
|
$normal-text-size: 16px;
|
||||||
$small-text-size: 12px;
|
$small-text-size: 12px;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onUnmounted, onWatcherCleanup, provide, ref, useTemplateRef, watch } from 'vue';
|
import {
|
||||||
|
computed,
|
||||||
|
onUnmounted,
|
||||||
|
onWatcherCleanup,
|
||||||
|
provide,
|
||||||
|
ref,
|
||||||
|
useTemplateRef,
|
||||||
|
watch,
|
||||||
|
} from 'vue';
|
||||||
import { useNow } from '@/composables/ticker';
|
import { useNow } from '@/composables/ticker';
|
||||||
import { GRAPH_DATA, type GraphData, GraphSide } from '@/graph/graph';
|
import { GRAPH_DATA, type GraphData, GraphSide } from '@/graph/graph';
|
||||||
import TimeText from '@/components/TimeText.vue';
|
import TimeText from '@/components/TimeText.vue';
|
||||||
@@ -15,7 +23,7 @@ const props = defineProps<{
|
|||||||
legend?: GraphSide;
|
legend?: GraphSide;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const divRef = useTemplateRef<HTMLDivElement>("graph-div");
|
const divRef = useTemplateRef<HTMLDivElement>('graph-div');
|
||||||
|
|
||||||
const width = ref(0);
|
const width = ref(0);
|
||||||
const height = ref(0);
|
const height = ref(0);
|
||||||
@@ -26,7 +34,9 @@ const resize_observer = new ResizeObserver((elements) => {
|
|||||||
for (const element of elements) {
|
for (const element of elements) {
|
||||||
if (element.target == divRef.value) {
|
if (element.target == divRef.value) {
|
||||||
width.value = element.contentBoxSize[0].inlineSize;
|
width.value = element.contentBoxSize[0].inlineSize;
|
||||||
height.value = element.contentBoxSize[0].blockSize - (props.include_controls ? controls_height : 0);
|
height.value =
|
||||||
|
element.contentBoxSize[0].blockSize -
|
||||||
|
(props.include_controls ? controls_height : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -126,8 +136,10 @@ provide<GraphData>(GRAPH_DATA, {
|
|||||||
border_top: border_top,
|
border_top: border_top,
|
||||||
min_x: min_x,
|
min_x: min_x,
|
||||||
max_x: now,
|
max_x: now,
|
||||||
width: () => Math.max(width.value - border_left.value - border_right.value, 0),
|
width: () =>
|
||||||
height: () => Math.max(height.value - border_top.value - border_bottom.value, 0),
|
Math.max(width.value - border_left.value - border_right.value, 0),
|
||||||
|
height: () =>
|
||||||
|
Math.max(height.value - border_top.value - border_bottom.value, 0),
|
||||||
x_map: x_map,
|
x_map: x_map,
|
||||||
lines: telemetry_lines,
|
lines: telemetry_lines,
|
||||||
max_update_rate: 1000 / 10,
|
max_update_rate: 1000 / 10,
|
||||||
@@ -160,7 +172,11 @@ const lines = computed(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div ref="graph-div" class="full-size">
|
<div ref="graph-div" class="full-size">
|
||||||
<div v-if="include_controls" class="controls-header" :style="`height: ${controls_height}px`">
|
<div
|
||||||
|
v-if="include_controls"
|
||||||
|
class="controls-header"
|
||||||
|
:style="`height: ${controls_height}px`"
|
||||||
|
>
|
||||||
<div class="grow"></div>
|
<div class="grow"></div>
|
||||||
<div>
|
<div>
|
||||||
<span>Duration Dropdown</span>
|
<span>Duration Dropdown</span>
|
||||||
@@ -173,7 +189,9 @@ const lines = computed(() => {
|
|||||||
:x="border_left"
|
:x="border_left"
|
||||||
:y="border_top"
|
:y="border_top"
|
||||||
:width="Math.max(width - border_left - border_right, 0)"
|
:width="Math.max(width - border_left - border_right, 0)"
|
||||||
:height="Math.max(height - border_top - border_bottom, 0)"
|
:height="
|
||||||
|
Math.max(height - border_top - border_bottom, 0)
|
||||||
|
"
|
||||||
></rect>
|
></rect>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<clipPath id="y_ticker">
|
<clipPath id="y_ticker">
|
||||||
@@ -181,7 +199,9 @@ const lines = computed(() => {
|
|||||||
:x="0"
|
:x="0"
|
||||||
:y="border_top"
|
:y="border_top"
|
||||||
:width="width"
|
:width="width"
|
||||||
:height="Math.max(height - border_top - border_bottom, 0)"
|
:height="
|
||||||
|
Math.max(height - border_top - border_bottom, 0)
|
||||||
|
"
|
||||||
></rect>
|
></rect>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<clipPath id="x_ticker">
|
<clipPath id="x_ticker">
|
||||||
@@ -247,5 +267,4 @@ div.controls-header {
|
|||||||
div.controls-header > div.grow {
|
div.controls-header > div.grow {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
type ShallowRef,
|
type ShallowRef,
|
||||||
toValue,
|
toValue,
|
||||||
triggerRef,
|
triggerRef,
|
||||||
|
useTemplateRef,
|
||||||
watch,
|
watch,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import {
|
import {
|
||||||
@@ -21,6 +22,7 @@ import { GRAPH_DATA, type GraphData } from '@/graph/graph';
|
|||||||
import { AXIS_DATA, type AxisData } from '@/graph/axis';
|
import { AXIS_DATA, type AxisData } from '@/graph/axis';
|
||||||
import ValueLabel from '@/components/ValueLabel.vue';
|
import ValueLabel from '@/components/ValueLabel.vue';
|
||||||
import { type Point, PointLine } from '@/graph/line';
|
import { type Point, PointLine } from '@/graph/line';
|
||||||
|
import TooltipDialog from '@/components/TooltipDialog.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
data: string;
|
data: string;
|
||||||
@@ -39,9 +41,9 @@ const min_sep = computed(() =>
|
|||||||
Math.min(props.minimum_separation || 0, maximum_minimum_separation_live),
|
Math.min(props.minimum_separation || 0, maximum_minimum_separation_live),
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data } = useTelemetry(() => props.data);
|
const { data: telemetry_data } = useTelemetry(() => props.data);
|
||||||
const websocket = inject<ShallowRef<WebsocketHandle>>(WEBSOCKET_SYMBOL)!;
|
const websocket = inject<ShallowRef<WebsocketHandle>>(WEBSOCKET_SYMBOL)!;
|
||||||
const value = websocket.value.listen_to_telemetry(data, min_sep);
|
const value = websocket.value.listen_to_telemetry(telemetry_data, min_sep);
|
||||||
|
|
||||||
const graph_data = inject<GraphData>(GRAPH_DATA)!;
|
const graph_data = inject<GraphData>(GRAPH_DATA)!;
|
||||||
const axis_data = inject<AxisData>(AXIS_DATA)!;
|
const axis_data = inject<AxisData>(AXIS_DATA)!;
|
||||||
@@ -82,7 +84,9 @@ watch([value], ([val]) => {
|
|||||||
if (val) {
|
if (val) {
|
||||||
const val_t = Date.parse(val.timestamp);
|
const val_t = Date.parse(val.timestamp);
|
||||||
if (val_t >= min_x) {
|
if (val_t >= min_x) {
|
||||||
const item_val = val.value[data.value!.data_type] as number;
|
const item_val = val.value[
|
||||||
|
telemetry_data.value!.data_type
|
||||||
|
] as number;
|
||||||
const new_item = {
|
const new_item = {
|
||||||
x: val_t,
|
x: val_t,
|
||||||
y: item_val,
|
y: item_val,
|
||||||
@@ -100,7 +104,7 @@ watch([value], ([val]) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
watch(
|
watch(
|
||||||
[data, () => props.fetch_history],
|
[telemetry_data, () => props.fetch_history],
|
||||||
async ([data]) => {
|
async ([data]) => {
|
||||||
if (data) {
|
if (data) {
|
||||||
const uuid = data.uuid;
|
const uuid = data.uuid;
|
||||||
@@ -213,7 +217,7 @@ const group_transform = computed(() => {
|
|||||||
|
|
||||||
watch([recompute_points], () => {
|
watch([recompute_points], () => {
|
||||||
let new_points = '';
|
let new_points = '';
|
||||||
if (memo.value.data.length == 0 || data.value == null) {
|
if (memo.value.data.length == 0 || telemetry_data.value == null) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +250,7 @@ watch([recompute_points], () => {
|
|||||||
const current_value = computed(() => {
|
const current_value = computed(() => {
|
||||||
const val = value.value;
|
const val = value.value;
|
||||||
if (val) {
|
if (val) {
|
||||||
return val.value[data.value!.data_type] as number;
|
return val.value[telemetry_data.value!.data_type] as number;
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
});
|
});
|
||||||
@@ -270,7 +274,7 @@ const legend_text = computed(() => {
|
|||||||
(toValue(graph_data.legend_width) -
|
(toValue(graph_data.legend_width) -
|
||||||
legend_line_length -
|
legend_line_length -
|
||||||
legend_text_offset * 2) /
|
legend_text_offset * 2) /
|
||||||
7;
|
6.5;
|
||||||
const start_text = props.data;
|
const start_text = props.data;
|
||||||
if (start_text.length > max_chars) {
|
if (start_text.length > max_chars) {
|
||||||
return (
|
return (
|
||||||
@@ -284,9 +288,27 @@ const legend_text = computed(() => {
|
|||||||
|
|
||||||
const legend_line = computed(() => {
|
const legend_line = computed(() => {
|
||||||
const x = legend_x.value;
|
const x = legend_x.value;
|
||||||
const y = legend_y.value;
|
const y = legend_y.value + 1 + toValue(graph_data.legend_y_stride) / 2;
|
||||||
return `${x},${y} ${x + legend_line_length},${y}`;
|
return `${x},${y} ${x + legend_line_length},${y}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const is_selected = ref(false);
|
||||||
|
const legendRectRef = useTemplateRef<SVGRectElement>('legend-ref');
|
||||||
|
|
||||||
|
function onOpenLegend() {
|
||||||
|
if (!is_selected.value) {
|
||||||
|
setTimeout(() => {
|
||||||
|
is_selected.value = true;
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onCloseLegend() {
|
||||||
|
if (is_selected.value) {
|
||||||
|
setTimeout(() => {
|
||||||
|
is_selected.value = false;
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -306,13 +328,61 @@ const legend_line = computed(() => {
|
|||||||
>
|
>
|
||||||
</ValueLabel>
|
</ValueLabel>
|
||||||
<template v-if="toValue(graph_data.legend_enabled)">
|
<template v-if="toValue(graph_data.legend_enabled)">
|
||||||
<polyline fill="none" :points="legend_line"></polyline>
|
<rect
|
||||||
<text
|
ref="legend-ref"
|
||||||
:x="legend_x + legend_line_length + legend_text_offset"
|
:class="`legend ${is_selected ? 'selected' : ''}`"
|
||||||
|
:x="legend_x - legend_text_offset"
|
||||||
:y="legend_y"
|
:y="legend_y"
|
||||||
|
:width="toValue(graph_data.legend_width)"
|
||||||
|
:height="toValue(graph_data.legend_y_stride)"
|
||||||
|
@click="onOpenLegend"
|
||||||
|
>
|
||||||
|
</rect>
|
||||||
|
<polyline
|
||||||
|
class="legend"
|
||||||
|
fill="none"
|
||||||
|
:points="legend_line"
|
||||||
|
@click="onOpenLegend"
|
||||||
|
></polyline>
|
||||||
|
<text
|
||||||
|
class="legend"
|
||||||
|
:x="legend_x + legend_line_length + legend_text_offset"
|
||||||
|
:y="legend_y + 1 + toValue(graph_data.legend_y_stride) / 2"
|
||||||
|
@click="onOpenLegend"
|
||||||
>
|
>
|
||||||
{{ legend_text }}
|
{{ legend_text }}
|
||||||
</text>
|
</text>
|
||||||
|
<foreignObject height="0" width="0">
|
||||||
|
<TooltipDialog
|
||||||
|
:show="is_selected"
|
||||||
|
:element="legendRectRef"
|
||||||
|
@close="onCloseLegend"
|
||||||
|
>
|
||||||
|
<div class="column">
|
||||||
|
<div class="row header">
|
||||||
|
<div
|
||||||
|
:class="`indexed-color color-${index} colored dash`"
|
||||||
|
></div>
|
||||||
|
<span class="large">{{ props.data }}</span>
|
||||||
|
</div>
|
||||||
|
<template v-if="telemetry_data">
|
||||||
|
<div class="row">
|
||||||
|
<span class="small">{{
|
||||||
|
telemetry_data?.uuid
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span>{{ telemetry_data?.data_type }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="row">
|
||||||
|
<span>Unknown Signal</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</TooltipDialog>
|
||||||
|
</foreignObject>
|
||||||
</template>
|
</template>
|
||||||
</g>
|
</g>
|
||||||
</template>
|
</template>
|
||||||
@@ -333,4 +403,59 @@ text {
|
|||||||
dominant-baseline: middle;
|
dominant-baseline: middle;
|
||||||
font-size: variables.$small-monospace-text-size;
|
font-size: variables.$small-monospace-text-size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rect.legend {
|
||||||
|
stroke: none;
|
||||||
|
stroke-width: 0;
|
||||||
|
fill: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
rect.legend.selected,
|
||||||
|
rect.legend:hover,
|
||||||
|
rect.legend:has(~ .legend:hover) {
|
||||||
|
stroke: variables.$gray-2;
|
||||||
|
stroke-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.column {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-start;
|
||||||
|
align-content: flex-start;
|
||||||
|
gap: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.row {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
align-content: center;
|
||||||
|
gap: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.header.row {
|
||||||
|
margin-bottom: 1ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.colored.dash {
|
||||||
|
background-color: var(--indexed-color);
|
||||||
|
width: 0.75em;
|
||||||
|
height: 2px;
|
||||||
|
display: inline;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.large {
|
||||||
|
font-size: variables.$large-text-size;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.small {
|
||||||
|
font-size: variables.$small-text-size;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
37
frontend/src/components/TooltipDialog.vue
Normal file
37
frontend/src/components/TooltipDialog.vue
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import TooltipDialogContents from '@/components/TooltipDialogContents.vue';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
show: boolean;
|
||||||
|
element: Element | null;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'close'): void;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Teleport to="main">
|
||||||
|
<Transition name="dialog">
|
||||||
|
<TooltipDialogContents
|
||||||
|
v-if="show"
|
||||||
|
:element="element"
|
||||||
|
@close="emit('close')"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
</TooltipDialogContents>
|
||||||
|
</Transition>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use '@/assets/variables';
|
||||||
|
.dialog-enter-from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
83
frontend/src/components/TooltipDialogContents.vue
Normal file
83
frontend/src/components/TooltipDialogContents.vue
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
element: Element | null;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'close'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
function onClick() {
|
||||||
|
emit('close');
|
||||||
|
}
|
||||||
|
|
||||||
|
const top = ref(0);
|
||||||
|
const width = ref(0);
|
||||||
|
const left = ref(0);
|
||||||
|
const height = ref(0);
|
||||||
|
|
||||||
|
function update_for_element(element: Element) {
|
||||||
|
top.value = element.getBoundingClientRect().top + window.scrollY;
|
||||||
|
left.value = element.getBoundingClientRect().left + window.screenX;
|
||||||
|
width.value = element.getBoundingClientRect().width;
|
||||||
|
height.value = element.getBoundingClientRect().height;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new MutationObserver((elements) => {
|
||||||
|
for (const element of elements) {
|
||||||
|
if (element.target == props.element && props.element != null) {
|
||||||
|
update_for_element(props.element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
document.body.addEventListener('click', onClick);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
document.body.removeEventListener('click', onClick);
|
||||||
|
observer.disconnect();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (props.element) {
|
||||||
|
update_for_element(props.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialog_style = computed(() => {
|
||||||
|
const midpoint_x = left.value + width.value / 2;
|
||||||
|
if (midpoint_x > window.innerWidth / 2) {
|
||||||
|
return `top: ${top.value + height.value / 2}px; right: ${window.innerWidth - midpoint_x}px;`;
|
||||||
|
}
|
||||||
|
return `top: ${top.value + height.value / 2}px; left: ${midpoint_x}px;`;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="dialog" :style="dialog_style" @click.stop="">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use '@/assets/variables';
|
||||||
|
|
||||||
|
.dialog {
|
||||||
|
position: absolute;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
z-index: 99;
|
||||||
|
padding: 1em;
|
||||||
|
border: variables.$light-background-color 2px solid;
|
||||||
|
background-color: variables.$dark-background-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-enter-from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -24,4 +24,3 @@ export interface GraphData {
|
|||||||
legend_y_stride: MaybeRefOrGetter<number>;
|
legend_y_stride: MaybeRefOrGetter<number>;
|
||||||
legend_width: MaybeRefOrGetter<number>;
|
legend_width: MaybeRefOrGetter<number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,63 +11,57 @@ provide(WEBSOCKET_SYMBOL, websocket);
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main>
|
<div style="width: 100vw; height: 50vh">
|
||||||
<div style="width: 100vw; height: 50vh">
|
<SvgGraph :legend="GraphSide.Left" right_axis include_controls>
|
||||||
<SvgGraph
|
<Axis>
|
||||||
:legend="GraphSide.Left"
|
<Line data="simple_producer/time_offset"></Line>
|
||||||
right_axis
|
<Line data="simple_producer/publish_offset"></Line>
|
||||||
include_controls
|
<Line data="simple_producer/await_offset"></Line>
|
||||||
>
|
</Axis>
|
||||||
<Axis>
|
</SvgGraph>
|
||||||
<Line data="simple_producer/time_offset"></Line>
|
</div>
|
||||||
<Line data="simple_producer/publish_offset"></Line>
|
<div style="width: 100vw; height: 50vh">
|
||||||
<Line data="simple_producer/await_offset"></Line>
|
<SvgGraph
|
||||||
</Axis>
|
:duration="60 * 1000 * 10"
|
||||||
</SvgGraph>
|
:legend="GraphSide.Right"
|
||||||
</div>
|
right_axis
|
||||||
<div style="width: 100vw; height: 50vh">
|
>
|
||||||
<SvgGraph
|
<Axis>
|
||||||
:duration="60 * 1000 * 10"
|
<Line
|
||||||
:legend="GraphSide.Right"
|
data="simple_producer/sin"
|
||||||
right_axis
|
:minimum_separation="1000"
|
||||||
>
|
></Line>
|
||||||
<Axis>
|
<Line
|
||||||
<Line
|
data="simple_producer/cos4"
|
||||||
data="simple_producer/sin"
|
:minimum_separation="1000"
|
||||||
:minimum_separation="1000"
|
></Line>
|
||||||
></Line>
|
<Line
|
||||||
<Line
|
data="simple_producer/sin2"
|
||||||
data="simple_producer/cos4"
|
:minimum_separation="1000"
|
||||||
:minimum_separation="1000"
|
></Line>
|
||||||
></Line>
|
<Line
|
||||||
<Line
|
data="simple_producer/cos"
|
||||||
data="simple_producer/sin2"
|
:minimum_separation="1000"
|
||||||
:minimum_separation="1000"
|
></Line>
|
||||||
></Line>
|
<Line
|
||||||
<Line
|
data="simple_producer/sin3"
|
||||||
data="simple_producer/cos"
|
:minimum_separation="1000"
|
||||||
:minimum_separation="1000"
|
></Line>
|
||||||
></Line>
|
<Line
|
||||||
<Line
|
data="simple_producer/cos2"
|
||||||
data="simple_producer/sin3"
|
:minimum_separation="1000"
|
||||||
:minimum_separation="1000"
|
></Line>
|
||||||
></Line>
|
<Line
|
||||||
<Line
|
data="simple_producer/sin4"
|
||||||
data="simple_producer/cos2"
|
:minimum_separation="1000"
|
||||||
:minimum_separation="1000"
|
></Line>
|
||||||
></Line>
|
<Line
|
||||||
<Line
|
data="simple_producer/cos3"
|
||||||
data="simple_producer/sin4"
|
:minimum_separation="1000"
|
||||||
:minimum_separation="1000"
|
></Line>
|
||||||
></Line>
|
</Axis>
|
||||||
<Line
|
</SvgGraph>
|
||||||
data="simple_producer/cos3"
|
</div>
|
||||||
:minimum_separation="1000"
|
|
||||||
></Line>
|
|
||||||
</Axis>
|
|
||||||
</SvgGraph>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
Reference in New Issue
Block a user