adds charts panel

This commit is contained in:
2025-02-15 15:42:33 -08:00
parent 69c0b0965d
commit e9751c2489
16 changed files with 600 additions and 42 deletions

View File

@@ -0,0 +1,309 @@
<script setup lang="ts">
import FlexDivider from '@/components/FlexDivider.vue';
import TelemetryList from '@/components/TelemetryList.vue';
import { onMounted, ref } from 'vue';
import TextInput from '@/components/TextInput.vue';
import type { TelemetryDefinition } from '@/composables/telemetry.ts';
import InlineIcon from '@/components/InlineIcon.vue';
const model = defineModel<TelemetryDefinition[][][]>({
required: true,
default: [],
});
const selected = ref(0);
const selected_cell_x = ref(0);
const selected_cell_y = ref(0);
const searchValue = ref('');
const options = [
[1, 1],
[2, 1],
[1, 2],
[2, 2],
[3, 2],
];
function selectOption(i: number) {
selected.value = i;
if (selected_cell_x.value >= options[i][0]) {
selected_cell_x.value = 0;
}
if (selected_cell_y.value >= options[i][1]) {
selected_cell_y.value = 0;
}
const initial_cells = model.value;
const result_cells: TelemetryDefinition[][][] = [];
for (let x = 0; x < options[i][0]; x++) {
result_cells.push([]);
for (let y = 0; y < options[i][1]; y++) {
if (x < initial_cells.length && y < initial_cells[x].length) {
result_cells[x].push(initial_cells[x][y]);
} else {
result_cells[x].push([]);
}
}
}
model.value = result_cells;
}
onMounted(() => {
const model_value = model.value;
let s = 0;
for (let i = 0; i < options.length; i++) {
// X length correct
if (options[i][0] == model_value.length) {
// Y length correct
if (options[i][1] == model_value[0].length) {
// selectOption(i);
s = i;
break;
}
}
}
// Fall back to option 0
selectOption(s);
});
function selectCell(x: number, y: number) {
selected_cell_x.value = x;
selected_cell_y.value = y;
}
function selectTelemetry(telemetry: TelemetryDefinition | null) {
if (telemetry != null) {
if (
!model.value[selected_cell_x.value][selected_cell_y.value].includes(
telemetry,
)
) {
model.value[selected_cell_x.value][selected_cell_y.value].push(
telemetry,
);
}
}
}
function moveUp(index: number) {
if (index > 0) {
const removed = model.value[selected_cell_x.value][
selected_cell_y.value
].splice(index - 1, 2);
removed.reverse();
model.value[selected_cell_x.value][selected_cell_y.value].splice(
index - 1,
0,
...removed,
);
}
}
function moveDown(index: number) {
if (
index + 1 <
model.value[selected_cell_x.value][selected_cell_y.value].length
) {
const removed = model.value[selected_cell_x.value][
selected_cell_y.value
].splice(index, 2);
removed.reverse();
model.value[selected_cell_x.value][selected_cell_y.value].splice(
index,
0,
...removed,
);
}
}
function removeTelemetry(index: number) {
model.value[selected_cell_x.value][selected_cell_y.value].splice(index, 1);
}
</script>
<template>
<div class="row grow stretch">
<div class="column grow stretch no-basis">
<div class="grow no-basis type_grid no-min-height scroll">
<svg
v-for="(option, i) in options"
:key="i"
:class="`layout ${selected == i ? 'selected' : ''}`"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100"
@click="selectOption(i)"
>
<template v-for="x in option[0]" :key="x">
<template v-for="y in option[1]" :key="y">
<rect
:x="1 + ((x - 1) * 98) / option[0]"
:y="1 + ((y - 1) * 98) / option[1]"
:width="98 / option[0]"
:height="98 / option[1]"
></rect>
</template>
</template>
</svg>
</div>
<FlexDivider class="horizontal_divider_margin"></FlexDivider>
<div class="row grow center no-basis">
<div
class="selected_grid grow"
:style="`grid-template: repeat(${options[selected][1]}, 1fr) / repeat(${options[selected][0]}, 1fr);`"
>
<template v-for="y in options[selected][1]" :key="y">
<template v-for="x in options[selected][0]" :key="x">
<div
:class="`cell ${selected_cell_x == x - 1 && selected_cell_y == y - 1 ? 'selected' : ''}`"
@click="selectCell(x - 1, y - 1)"
></div>
</template>
</template>
</div>
</div>
</div>
<FlexDivider></FlexDivider>
<div class="column grow2 stretch no-basis">
<div class="row grow stretch no-basis">
<div class="column grow stretch">
<template v-if="model.length > 0">
<div
class="row chosen"
v-for="(selected, i) in model[selected_cell_x][
selected_cell_y
]"
:key="i"
>
<span>
{{ selected.name }}
</span>
<span class="grow"></span>
<div class="column tiny_text">
<InlineIcon
icon="up arrow"
:class="`${i == 0 ? 'hidden' : 'button'}`"
@click="moveUp(i)"
></InlineIcon>
<InlineIcon
icon="down arrow"
:class="`${i == model[selected_cell_x][selected_cell_y].length - 1 ? 'hidden' : 'button'}`"
@click="moveDown(i)"
></InlineIcon>
</div>
<span
class="close icon button"
@click="removeTelemetry(i)"
>
<InlineIcon icon="close"></InlineIcon>
</span>
</div>
</template>
</div>
</div>
<FlexDivider class="horizontal_divider_margin"></FlexDivider>
<div class="row grow stretch no-basis">
<div class="column grow stretch no-basis">
<div class="row">
<TextInput
autofocus
class="grow"
v-model="searchValue"
placeholder="Search"
></TextInput>
</div>
<div class="row scroll grow no-min-height no-basis">
<div class="column grow stretch">
<TelemetryList
:search="searchValue"
:model-value="null"
@update:model-value="
(value) => selectTelemetry(value)
"
></TelemetryList>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
@use '@/assets/variables';
.type_grid {
display: grid;
grid-auto-flow: row;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
grid-auto-rows: max-content;
gap: 1em;
justify-items: stretch;
align-items: stretch;
justify-content: center;
align-content: center;
}
.type_grid > * {
aspect-ratio: 1 / 1;
}
.layout {
stroke: variables.$grid-line;
stroke-width: 1px;
fill: none;
cursor: pointer;
}
.selected {
background-color: variables.$light-background-color;
}
.selected_grid {
display: grid;
gap: 1em;
justify-items: stretch;
align-self: stretch;
justify-content: stretch;
align-content: stretch;
}
.cell {
border: variables.$grid-line 1px solid;
cursor: pointer;
}
.horizontal_divider_margin {
margin: 1em 0;
}
.chosen {
padding: 0.3em;
border: 0;
border-bottom: variables.$gray-3 solid 1px;
border-top: 0;
align-items: center;
}
.chosen:first-child {
border-top: variables.$gray-3 solid 1px;
}
.chosen:hover {
background-color: variables.$light2-background-color;
}
.close.icon.button {
height: 1em;
cursor: pointer;
}
.arrow.icon.button {
cursor: pointer;
}
.column.tiny_text {
font-size: variables.$normal-text-size / 2;
height: 100%;
justify-content: space-between;
}
</style>

View File

@@ -0,0 +1,51 @@
<script setup lang="ts">
import type { TelemetryDefinition } from '@/composables/telemetry.ts';
import SvgGraph from '@/components/SvgGraph.vue';
import { GraphSide } from '@/graph/graph.ts';
import GraphAxis from '@/components/GraphAxis.vue';
import TelemetryLine from '@/components/TelemetryLine.vue';
defineProps<{
charts: TelemetryDefinition[][][];
}>();
</script>
<template>
<div
class="chart grid grow"
:style="`grid-template: repeat(${charts[0].length} , 1fr) / repeat(${charts.length}, 1fr);`"
v-if="charts.length > 0"
>
<template v-for="y in charts[0].length" :key="y">
<template v-for="x in charts.length" :key="x">
<div class="no-min-height">
<SvgGraph
right_axis
cursor
:legend="GraphSide.Left"
include_controls
>
<GraphAxis>
<TelemetryLine
v-for="tlm in charts[x - 1][y - 1]"
:key="tlm.uuid"
:data="tlm.name"
></TelemetryLine>
</GraphAxis>
</SvgGraph>
</div>
</template>
</template>
</div>
</template>
<style scoped lang="scss">
@use '@/assets/variables';
.chart.grid {
display: grid;
grid-auto-flow: row;
place-content: stretch;
height: 100%;
}
</style>

View File

@@ -0,0 +1,50 @@
<script setup lang="ts">
import { ref } from 'vue';
import InlineIcon from '@/components/InlineIcon.vue';
import ChartDefinitionView from '@/views/ChartDefinitionView.vue';
import type { TelemetryDefinition } from '@/composables/telemetry.ts';
import ChartRenderView from '@/views/ChartRenderView.vue';
const settingsOpen = ref(true);
const settings = ref<TelemetryDefinition[][][]>([]);
function openSettings() {
settingsOpen.value = true;
}
function closeSettings() {
settingsOpen.value = false;
}
</script>
<template>
<div class="column stretch screen content full center divider">
<template v-if="!settingsOpen">
<div class="settings column center" @click="openSettings">
<InlineIcon icon="hamburger menu"></InlineIcon>
</div>
<ChartRenderView :charts="settings"></ChartRenderView>
</template>
<template v-else>
<div class="settings column center" @click="closeSettings">
<InlineIcon icon="close"></InlineIcon>
</div>
<ChartDefinitionView v-model="settings"></ChartDefinitionView>
</template>
</div>
</template>
<style scoped lang="scss">
@use '@/assets/variables';
.settings {
position: absolute;
top: 0;
right: 0;
background-color: variables.$dark-background-color;
border: 1px solid variables.$light2-background-color;
padding: 0.5em;
margin: 1px;
aspect-ratio: 1 / 1;
cursor: pointer;
}
</style>

View File

@@ -8,9 +8,8 @@ import FlexDivider from '@/components/FlexDivider.vue';
const searchValue = ref('');
const selectValue = ref<
[TelemetryDefinition | null, TelemetryDefinition | null]
>([null, null]);
const selected = ref<TelemetryDefinition | null>(null);
const mousedover = ref<TelemetryDefinition | null>(null);
</script>
<template>
@@ -29,7 +28,11 @@ const selectValue = ref<
<div class="column grow stretch">
<TelemetryList
:search="searchValue"
@select="(selection) => (selectValue = selection)"
v-model="selected"
@mouseover="
(mousedover_value) =>
(mousedover = mousedover_value)
"
></TelemetryList>
</div>
</div>
@@ -37,8 +40,8 @@ const selectValue = ref<
<FlexDivider></FlexDivider>
<div class="column grow stretch no-basis">
<TelemetryInfo
:telemetry_definition="selectValue[0]"
:secondary="selectValue[1]"
:mouseover="mousedover"
:selection="selected"
></TelemetryInfo>
</div>
</div>