allow panels to hold commands

This commit is contained in:
2025-12-28 11:48:11 -05:00
parent 59a0c81eb4
commit c3253f3204
11 changed files with 385 additions and 51 deletions

View File

@@ -1,6 +1,12 @@
<script setup lang="ts">
import type { OptionalDynamicComponentData } from '@/composables/dynamic.ts';
import { computed, defineAsyncComponent } from 'vue';
import {
AnyTypes,
type CommandParameterData,
type DynamicDataType,
type OptionalDynamicComponentData,
} from '@/composables/dynamic.ts';
import { computed, defineAsyncComponent, inject, type Ref, ref } from 'vue';
import CommandParameterListConfigurator from '@/components/CommandParameterListConfigurator.vue';
const TelemetryValue = defineAsyncComponent(
() => import('@/components/TelemetryValue.vue'),
@@ -8,6 +14,9 @@ const TelemetryValue = defineAsyncComponent(
const GridLayout = defineAsyncComponent(
() => import('@/components/layout/GridLayout.vue'),
);
const CommandInput = defineAsyncComponent(
() => import('@/components/CommandInput.vue'),
);
const model = defineModel<OptionalDynamicComponentData>('data', {
required: true,
@@ -19,13 +28,26 @@ const props = defineProps<{
editable: boolean;
}>();
const busy = ref(false);
// Provide a fallback option
const inputs = inject<Ref<{ [id: string]: DynamicDataType }>>(
'inputs',
ref({}),
);
const thisSymbol = Symbol();
const isSelected = computed(() => {
return selection.value == thisSymbol && props.editable;
});
function selectThis() {
function selectThis(e: Event) {
if (props.editable) {
// Only do this when we are editable
e.stopPropagation();
e.preventDefault();
}
selection.value = thisSymbol;
}
@@ -100,6 +122,54 @@ function deleteColumn() {
model.value = grid;
}
}
function makeInput() {
model.value = {
type: 'input',
id: [...Array(32)]
.map(() => Math.floor(Math.random() * 16).toString(16))
.join(''),
data_type: 'Float32',
};
}
function makeCommandButton() {
model.value = {
type: 'command_button',
text: 'Button Text',
command_name: '',
parameters: {},
};
}
async function sendCommand(command: {
command_name: string;
parameters: { [key: string]: CommandParameterData };
}) {
busy.value = true;
const params: { [key: string]: DynamicDataType } = {};
for (const param_name in command.parameters) {
const parameter = command.parameters[param_name];
switch (parameter.type) {
case 'constant':
params[param_name] = parameter.value;
break;
case 'input':
params[param_name] = inputs.value[parameter.id];
break;
}
}
await fetch(`/api/cmd/${command.command_name}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(params),
});
busy.value = false;
}
</script>
<template>
@@ -123,20 +193,30 @@ function deleteColumn() {
<button v-if="model.type != 'grid'" @click.stop.prevent="makeGrid">
Make Grid
</button>
<button
v-if="model.type != 'input'"
@click.stop.prevent="makeInput"
>
Make Input
</button>
<button
v-if="model.type != 'command_button'"
@click.stop.prevent="makeCommandButton"
>
Make Command Button
</button>
</div>
</Teleport>
<template v-if="model.type == 'none'">
<!-- Intentionally Left Empty -->
<span
v-if="editable"
:class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
@click.stop.prevent="selectThis"
@click="selectThis"
></span>
</template>
<template v-else-if="model.type == 'text'">
<span
:class="`${model.justify_right ? 'justify-right' : ''} ${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
@click.stop.prevent="selectThis"
@click="selectThis"
>
{{ model.text }}
</span>
@@ -155,7 +235,7 @@ function deleteColumn() {
<span
v-if="editable"
:class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
@click.stop.prevent="selectThis"
@click="selectThis"
>
{{ '{' }} {{ model.data }} {{ '}' }}
</span>
@@ -163,7 +243,7 @@ function deleteColumn() {
v-else
:class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
:data="model.data"
@click.stop.prevent="selectThis"
@click="selectThis"
></TelemetryValue>
<Teleport v-if="isSelected" to="#inspector">
<label>Telemetry Item: </label>
@@ -175,7 +255,7 @@ function deleteColumn() {
:class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
:cols="model.columns"
:equal_col_width="model.equal_width"
@click.stop.prevent="selectThis"
@click="selectThis"
>
<template v-for="x in model.cells.length" :key="x">
<template v-for="y in model.columns" :key="y">
@@ -210,6 +290,60 @@ function deleteColumn() {
</div>
</Teleport>
</template>
<template v-else-if="model.type == 'input'">
<span
v-if="editable"
:class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
@click="selectThis"
>
{{ '[' }} {{ model.id }} {{ ']' }}
</span>
<CommandInput
v-else
:type="model.data_type"
v-model="inputs[model.id]"
></CommandInput>
<Teleport v-if="isSelected" to="#inspector">
<div class="row">
<label>Input ID: </label>
<input v-model="model.id" />
</div>
<div class="row">
<label>Data Type: </label>
<select v-model="model.data_type">
<option v-for="type in AnyTypes" :key="type" :value="type">
{{ type }}
</option>
</select>
</div>
</Teleport>
</template>
<template v-else-if="model.type == 'command_button'">
<button
:disabled="busy"
:class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
@click.stop.prevent="
(e) => (editable ? selectThis(e) : sendCommand(model as any))
"
>
{{ model.text }}
</button>
<Teleport v-if="isSelected" to="#inspector">
<div class="row">
<label>Button Text: </label>
<input v-model="model.text" />
</div>
<div class="row">
<label>Command: </label>
<input v-model="model.command_name" />
</div>
<CommandParameterListConfigurator
:key="model.command_name"
:command_name="model.command_name"
v-model="model.parameters"
></CommandParameterListConfigurator>
</Teleport>
</template>
<template v-else> ERROR: Unknown data: {{ model }} </template>
</template>