increase frontend flexibility
This commit is contained in:
@@ -2,23 +2,47 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: flex-start;
|
|
||||||
align-content: flex-start;
|
align-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gap_half {
|
.align-start {
|
||||||
gap: 0.5em;
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.align-center {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.align-end {
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify-start {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
.justify-center {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.justify-between {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.justify-end {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gap-half {
|
||||||
|
gap: 0.0625em;
|
||||||
|
}
|
||||||
|
.gap-full {
|
||||||
|
gap: 0.125em;
|
||||||
|
}
|
||||||
|
.gap-wide {
|
||||||
|
gap: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: flex-start;
|
|
||||||
align-content: center;
|
align-content: center;
|
||||||
gap: 1em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.center {
|
.center {
|
||||||
@@ -27,15 +51,6 @@
|
|||||||
align-content: center;
|
align-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.screen {
|
|
||||||
min-height: 100vh;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.screen.limited {
|
|
||||||
max-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
padding: 20vh 10vw;
|
padding: 20vh 10vw;
|
||||||
}
|
}
|
||||||
@@ -54,26 +69,6 @@
|
|||||||
padding-bottom: 5vw;
|
padding-bottom: 5vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
//.content {
|
|
||||||
// margin: 20vh 10vw;
|
|
||||||
// max-width: 1200px;
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//.content.full {
|
|
||||||
// margin: 5vh 5vw;
|
|
||||||
// max-width: none;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//.screen.content {
|
|
||||||
// width: calc(100% - 20vw);
|
|
||||||
// height: calc(100vh - 40vh);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//.screen.content.full {
|
|
||||||
// width: calc(100% - 10vw);
|
|
||||||
// height: calc(100vh - 10vh);
|
|
||||||
//}
|
|
||||||
|
|
||||||
.stretch {
|
.stretch {
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
@@ -105,3 +100,13 @@
|
|||||||
.scroll {
|
.scroll {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-auto-flow: row;
|
||||||
|
grid-auto-rows: 1fr;
|
||||||
|
row-gap: 0.125em;
|
||||||
|
column-gap: 0.5em;
|
||||||
|
justify-items: stretch;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|||||||
113
frontend/src/components/CopyableDynamicSpan.vue
Normal file
113
frontend/src/components/CopyableDynamicSpan.vue
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, onMounted, onUnmounted, useTemplateRef } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
value: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const overlay = computed(() => {
|
||||||
|
return '0'.repeat(props.value.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
const span = useTemplateRef<HTMLSpanElement>('data-span');
|
||||||
|
|
||||||
|
function copyHandler(event: ClipboardEvent) {
|
||||||
|
const selection = document.getSelection();
|
||||||
|
const span_value: HTMLSpanElement | null = span.value;
|
||||||
|
if (selection && span_value && selection.containsNode(span_value, true)) {
|
||||||
|
let copy_result = '';
|
||||||
|
for (let i = 0; i < selection.rangeCount; i++) {
|
||||||
|
const node_iter = document.createNodeIterator(
|
||||||
|
selection.getRangeAt(i).commonAncestorContainer,
|
||||||
|
NodeFilter.SHOW_ALL,
|
||||||
|
);
|
||||||
|
let found_start = false;
|
||||||
|
while (true) {
|
||||||
|
const node = node_iter.nextNode();
|
||||||
|
if (node) {
|
||||||
|
if (node == selection?.getRangeAt(i).startContainer) {
|
||||||
|
found_start = true;
|
||||||
|
}
|
||||||
|
if (found_start && node.nodeType == Node.TEXT_NODE) {
|
||||||
|
let append_to_copy = node.textContent?.trim() || '';
|
||||||
|
const parent = node.parentElement;
|
||||||
|
if (parent) {
|
||||||
|
const copy_value =
|
||||||
|
parent.getAttribute('copy-value');
|
||||||
|
if (copy_value) {
|
||||||
|
append_to_copy = copy_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node == selection?.getRangeAt(i).endContainer) {
|
||||||
|
append_to_copy = append_to_copy.substring(
|
||||||
|
0,
|
||||||
|
selection?.getRangeAt(i).endOffset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (node == selection?.getRangeAt(i).startContainer) {
|
||||||
|
append_to_copy = append_to_copy.substring(
|
||||||
|
selection?.getRangeAt(i).startOffset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (copy_result.length > 0) {
|
||||||
|
copy_result += ' ';
|
||||||
|
}
|
||||||
|
copy_result += append_to_copy;
|
||||||
|
}
|
||||||
|
if (node == selection?.getRangeAt(i).endContainer) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
event.clipboardData?.setData('text/plain', copy_result);
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
document.body.addEventListener('copy', copyHandler);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
document.body.removeEventListener('copy', copyHandler);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span class="test monospace" :data-after-content="value">
|
||||||
|
<span ref="data-span" class="transparent" :copy-value="value">
|
||||||
|
{{ overlay }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use '@/assets/variables';
|
||||||
|
|
||||||
|
.monospace {
|
||||||
|
font-family: variables.$monospace-text-font;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transparent {
|
||||||
|
color: transparent;
|
||||||
|
border: transparent;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test::after {
|
||||||
|
content: attr(data-after-content);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,14 +1,20 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, watch } from 'vue';
|
import { computed, watch } from 'vue';
|
||||||
|
import CopyableDynamicSpan from '@/components/CopyableDynamicSpan.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
value: number;
|
value: number;
|
||||||
max_width: number;
|
max_width: number;
|
||||||
|
copyable?: boolean;
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'update', value: string): void;
|
(e: 'update', value: string): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const copyable = computed(() => {
|
||||||
|
return props.copyable || false;
|
||||||
|
});
|
||||||
|
|
||||||
const display_value = computed(() => {
|
const display_value = computed(() => {
|
||||||
if (props.value == 0) {
|
if (props.value == 0) {
|
||||||
return '0';
|
return '0';
|
||||||
@@ -61,7 +67,12 @@ watch([display_value], ([display_str]) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<template v-if="copyable">
|
||||||
|
<CopyableDynamicSpan :value="display_value"></CopyableDynamicSpan>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
{{ display_value }}
|
{{ display_value }}
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
|||||||
66
frontend/src/components/TelemetryValue.vue
Normal file
66
frontend/src/components/TelemetryValue.vue
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useTelemetry } from '@/composables/telemetry.ts';
|
||||||
|
import { computed, inject, type ShallowRef } from 'vue';
|
||||||
|
import {
|
||||||
|
WEBSOCKET_SYMBOL,
|
||||||
|
type WebsocketHandle,
|
||||||
|
} from '@/composables/websocket.ts';
|
||||||
|
import NumericText from '@/components/NumericText.vue';
|
||||||
|
|
||||||
|
const max_update_rate = 50; // ms
|
||||||
|
const default_update_rate = 200; // ms
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
data: string;
|
||||||
|
max_update_period?: number;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const max_update_period = computed(() => {
|
||||||
|
return Math.min(
|
||||||
|
props.max_update_period || default_update_rate,
|
||||||
|
max_update_rate,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: telemetry_data } = useTelemetry(() => props.data);
|
||||||
|
const websocket = inject<ShallowRef<WebsocketHandle>>(WEBSOCKET_SYMBOL)!;
|
||||||
|
const value = websocket.value.listen_to_telemetry(
|
||||||
|
telemetry_data,
|
||||||
|
max_update_period,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
const is_data_present = computed(() => {
|
||||||
|
return value.value != null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const numeric_data = computed(() => {
|
||||||
|
const val = value.value;
|
||||||
|
if (val) {
|
||||||
|
const type = telemetry_data.value!.data_type;
|
||||||
|
const item_val = val.value[type];
|
||||||
|
if (typeof item_val == 'number') {
|
||||||
|
return item_val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span v-if="!is_data_present"> No Data </span>
|
||||||
|
<template v-else>
|
||||||
|
<NumericText
|
||||||
|
v-if="numeric_data"
|
||||||
|
:value="numeric_data"
|
||||||
|
:max_width="10"
|
||||||
|
></NumericText>
|
||||||
|
<span v-else>
|
||||||
|
Cannot Display Data of Type {{ telemetry_data!.data_type }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use '@/assets/variables';
|
||||||
|
</style>
|
||||||
19
frontend/src/components/layout/GridLayout.vue
Normal file
19
frontend/src/components/layout/GridLayout.vue
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{
|
||||||
|
cols: number;
|
||||||
|
equal_col_width?: boolean;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="`grid`"
|
||||||
|
:style="`grid-template-columns: repeat(${cols}, ${equal_col_width ? '1fr' : 'auto'});`"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use '@/assets/variables';
|
||||||
|
</style>
|
||||||
33
frontend/src/components/layout/LinearLayout.vue
Normal file
33
frontend/src/components/layout/LinearLayout.vue
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { Direction } from '@/composables/Direction.ts';
|
||||||
|
import { Alignment } from '@/composables/Alignment.ts';
|
||||||
|
import { Justification } from '@/composables/Justification.ts';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { GapSize } from '@/composables/GapSize.ts';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
direction: Direction;
|
||||||
|
stretch?: boolean;
|
||||||
|
// Direction
|
||||||
|
justify?: Justification;
|
||||||
|
// Cross Direction
|
||||||
|
align?: Alignment;
|
||||||
|
gap?: GapSize;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const justification = computed(() => props.justify || Justification.Start);
|
||||||
|
const alignment = computed(() => props.align || Alignment.Start);
|
||||||
|
const gap_size = computed(() => props.gap || GapSize.Normal);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="`${direction} linear ${stretch ? 'stretch' : ''} ${justification} ${alignment} ${gap_size}`"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use '@/assets/variables';
|
||||||
|
</style>
|
||||||
30
frontend/src/components/layout/ScreenLayout.vue
Normal file
30
frontend/src/components/layout/ScreenLayout.vue
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { ScreenType } from '@/composables/ScreenType.ts';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
// Whether this should be limited to the height of the viewport
|
||||||
|
// This allows scroll bars to be pushed to inner components
|
||||||
|
limit?: boolean;
|
||||||
|
type: ScreenType;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="`column stretch screen ${limit ? 'limited' : ''} content ${type}`"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use '@/assets/variables';
|
||||||
|
|
||||||
|
.screen {
|
||||||
|
min-height: 100vh;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.screen.limited {
|
||||||
|
max-height: 100vh;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
5
frontend/src/composables/Alignment.ts
Normal file
5
frontend/src/composables/Alignment.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export enum Alignment {
|
||||||
|
Start = 'align-start',
|
||||||
|
Center = 'align-center',
|
||||||
|
End = 'align-end',
|
||||||
|
}
|
||||||
4
frontend/src/composables/Direction.ts
Normal file
4
frontend/src/composables/Direction.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export enum Direction {
|
||||||
|
Row = 'row',
|
||||||
|
Column = 'column',
|
||||||
|
}
|
||||||
6
frontend/src/composables/GapSize.ts
Normal file
6
frontend/src/composables/GapSize.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export enum GapSize {
|
||||||
|
None = '',
|
||||||
|
Thin = 'gap-half',
|
||||||
|
Normal = 'gap-full',
|
||||||
|
Wide = 'gap-wide',
|
||||||
|
}
|
||||||
6
frontend/src/composables/Justification.ts
Normal file
6
frontend/src/composables/Justification.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export enum Justification {
|
||||||
|
Start = 'justify-start',
|
||||||
|
Center = 'justify-center',
|
||||||
|
Between = 'justify-between',
|
||||||
|
End = 'justify-end',
|
||||||
|
}
|
||||||
7
frontend/src/composables/ScreenType.ts
Normal file
7
frontend/src/composables/ScreenType.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export enum ScreenType {
|
||||||
|
Standard = '',
|
||||||
|
Page = 'page',
|
||||||
|
Wide = 'wide',
|
||||||
|
Tall = 'tall',
|
||||||
|
WideTall = 'wide tall',
|
||||||
|
}
|
||||||
@@ -43,6 +43,12 @@ export function getPanelHeirarchy(): PanelHeirarchyChildren {
|
|||||||
type: PanelHeirarchyType.LEAF,
|
type: PanelHeirarchyType.LEAF,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
result.push({
|
||||||
|
name: 'Panel Test',
|
||||||
|
to: { name: 'panel_test' },
|
||||||
|
type: PanelHeirarchyType.LEAF,
|
||||||
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ const router = createRouter({
|
|||||||
name: 'chart',
|
name: 'chart',
|
||||||
component: () => import('../views/ChartView.vue'),
|
component: () => import('../views/ChartView.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/panel_test',
|
||||||
|
name: 'panel_test',
|
||||||
|
component: () => import('../views/PanelTest.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import InlineIcon from '@/components/InlineIcon.vue';
|
|||||||
import ChartDefinitionView from '@/views/ChartDefinitionView.vue';
|
import ChartDefinitionView from '@/views/ChartDefinitionView.vue';
|
||||||
import type { TelemetryDefinition } from '@/composables/telemetry.ts';
|
import type { TelemetryDefinition } from '@/composables/telemetry.ts';
|
||||||
import ChartRenderView from '@/views/ChartRenderView.vue';
|
import ChartRenderView from '@/views/ChartRenderView.vue';
|
||||||
|
import ScreenLayout from '@/components/layout/ScreenLayout.vue';
|
||||||
|
import { Direction } from '@/composables/Direction.ts';
|
||||||
|
import { ScreenType } from '@/composables/ScreenType.ts';
|
||||||
|
|
||||||
const settingsOpen = ref(true);
|
const settingsOpen = ref(true);
|
||||||
const settings = ref<TelemetryDefinition[][][]>([]);
|
const settings = ref<TelemetryDefinition[][][]>([]);
|
||||||
@@ -17,7 +20,7 @@ function closeSettings() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="column stretch screen content tall wide">
|
<ScreenLayout :direction="Direction.Column" :type="ScreenType.WideTall">
|
||||||
<template v-if="!settingsOpen">
|
<template v-if="!settingsOpen">
|
||||||
<div class="settings column" @click="openSettings">
|
<div class="settings column" @click="openSettings">
|
||||||
<InlineIcon icon="hamburger menu"></InlineIcon>
|
<InlineIcon icon="hamburger menu"></InlineIcon>
|
||||||
@@ -30,7 +33,7 @@ function closeSettings() {
|
|||||||
</div>
|
</div>
|
||||||
<ChartDefinitionView v-model="settings"></ChartDefinitionView>
|
<ChartDefinitionView v-model="settings"></ChartDefinitionView>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</ScreenLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import {
|
|||||||
import PanelHeirarchy from '@/components/PanelHeirarchy.vue';
|
import PanelHeirarchy from '@/components/PanelHeirarchy.vue';
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import TextInput from '@/components/TextInput.vue';
|
import TextInput from '@/components/TextInput.vue';
|
||||||
|
import ScreenLayout from '@/components/layout/ScreenLayout.vue';
|
||||||
|
import { Direction } from '@/composables/Direction.ts';
|
||||||
|
import { ScreenType } from '@/composables/ScreenType.ts';
|
||||||
|
|
||||||
const searchValue = ref('');
|
const searchValue = ref('');
|
||||||
|
|
||||||
@@ -30,7 +33,7 @@ function onEnter() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="column stretch screen limited content page">
|
<ScreenLayout :direction="Direction.Column" :type="ScreenType.Page" limit>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<TextInput
|
<TextInput
|
||||||
autofocus
|
autofocus
|
||||||
@@ -52,7 +55,7 @@ function onEnter() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ScreenLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
28
frontend/src/views/PanelTest.vue
Normal file
28
frontend/src/views/PanelTest.vue
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import GridLayout from '@/components/layout/GridLayout.vue';
|
||||||
|
import TelemetryValue from '@/components/TelemetryValue.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<GridLayout :cols="2" equal_col_width>
|
||||||
|
<span class="justify-right"> simple_producer/cos </span>
|
||||||
|
<TelemetryValue data="simple_producer/cos"></TelemetryValue>
|
||||||
|
|
||||||
|
<span class="justify-right"> simple_producer/sin </span>
|
||||||
|
<TelemetryValue data="simple_producer/sin"></TelemetryValue>
|
||||||
|
|
||||||
|
<span class="justify-right"> simple_producer/cos2 </span>
|
||||||
|
<TelemetryValue data="simple_producer/cos2"></TelemetryValue>
|
||||||
|
|
||||||
|
<span class="justify-right"> simple_producer/sin2 </span>
|
||||||
|
<TelemetryValue data="simple_producer/sin2"></TelemetryValue>
|
||||||
|
</GridLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use '@/assets/variables';
|
||||||
|
|
||||||
|
.justify-right {
|
||||||
|
justify-self: end;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -5,6 +5,9 @@ import TelemetryList from '@/components/TelemetryList.vue';
|
|||||||
import type { TelemetryDefinition } from '@/composables/telemetry';
|
import type { TelemetryDefinition } from '@/composables/telemetry';
|
||||||
import TelemetryInfo from '@/components/TelemetryInfo.vue';
|
import TelemetryInfo from '@/components/TelemetryInfo.vue';
|
||||||
import FlexDivider from '@/components/FlexDivider.vue';
|
import FlexDivider from '@/components/FlexDivider.vue';
|
||||||
|
import ScreenLayout from '@/components/layout/ScreenLayout.vue';
|
||||||
|
import { Direction } from '@/composables/Direction.ts';
|
||||||
|
import { ScreenType } from '@/composables/ScreenType.ts';
|
||||||
|
|
||||||
const searchValue = ref('');
|
const searchValue = ref('');
|
||||||
|
|
||||||
@@ -13,7 +16,7 @@ const mousedover = ref<TelemetryDefinition | null>(null);
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="row stretch screen limited content">
|
<ScreenLayout :direction="Direction.Row" :type="ScreenType.Standard" limit>
|
||||||
<div class="column grow2 stretch no-min-height no-basis">
|
<div class="column grow2 stretch no-min-height no-basis">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<TextInput
|
<TextInput
|
||||||
@@ -44,7 +47,7 @@ const mousedover = ref<TelemetryDefinition | null>(null);
|
|||||||
:selection="selected"
|
:selection="selected"
|
||||||
></TelemetryInfo>
|
></TelemetryInfo>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ScreenLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
Reference in New Issue
Block a user