mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-30 02:31:40 +00:00
Vue 3 Upgrade (#1900)
## Description Upgrades to Vue 3 and necessary associated dependencies. Also fixes some issues with the layout and adds validation for object detection models. Closes #885, closes #1943, closes #1449. ## Meta Merge checklist: - [x] Pull Request title is [short, imperative summary](https://cbea.ms/git-commit/) of proposed changes - [x] The description documents the _what_ and _why_ - [ ] If this PR changes behavior or adds a feature, user documentation is updated - [ ] If this PR touches photon-serde, all messages have been regenerated and hashes have not changed unexpectedly - [ ] If this PR touches configuration, this is backwards compatible with settings back to v2024.3.1 - [ ] If this PR touches pipeline settings or anything related to data exchange, the frontend typing is updated - [ ] If this PR addresses a bug, a regression test for it is added --------- Co-authored-by: Matt M <matthew.morley.ca@gmail.com> Co-authored-by: Gold856 <117957790+Gold856@users.noreply.github.com> Co-authored-by: samfreund <techguy763@gmail.com>
This commit is contained in:
@@ -24,7 +24,7 @@ const cameraInfoFor: any = (camera: PVCameraInfo) => {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<v-simple-table dense :style="{ backgroundColor: 'var(--v-primary-base)' }">
|
||||
<v-table density="compact" :style="{ backgroundColor: 'var(--v-primary-base)' }">
|
||||
<tbody>
|
||||
<tr v-if="cameraInfoFor(camera).dev !== undefined && cameraInfoFor(camera).dev !== null">
|
||||
<td>Device Number:</td>
|
||||
@@ -66,6 +66,6 @@ const cameraInfoFor: any = (camera: PVCameraInfo) => {
|
||||
<td>{{ cameraInfoFor(camera).otherPaths }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-simple-table>
|
||||
</v-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -29,7 +29,7 @@ const cameraInfoFor = (camera: PVCameraInfo): any => {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<v-simple-table dense :style="{ backgroundColor: 'var(--v-primary-base)' }">
|
||||
<v-table density="compact" :style="{ backgroundColor: 'var(--v-primary-base)' }">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th></th>
|
||||
@@ -112,7 +112,7 @@ const cameraInfoFor = (camera: PVCameraInfo): any => {
|
||||
<td>{{ cameraInfoFor(current).otherPaths }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-simple-table>
|
||||
</v-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
const props = withDefaults(
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
iconName: string;
|
||||
disabled?: boolean;
|
||||
@@ -18,20 +18,17 @@ const props = withDefaults(
|
||||
defineEmits<{
|
||||
(e: "click"): void;
|
||||
}>();
|
||||
|
||||
const hoverClass = props.hover ? "hover" : "";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<v-tooltip :right="right" :bottom="!right" nudge-right="10" :disabled="tooltip === undefined">
|
||||
<template #activator="{ on, attrs }">
|
||||
<v-tooltip :right="right" :location="!right ? 'bottom' : undefined" offset="10" :disabled="tooltip === undefined">
|
||||
<template #activator="{ props }">
|
||||
<v-icon
|
||||
:class="hoverClass"
|
||||
:class="hover ? 'hover' : ''"
|
||||
:color="color"
|
||||
v-bind="attrs"
|
||||
v-bind="props"
|
||||
:disabled="disabled"
|
||||
v-on="on"
|
||||
@click="$emit('click')"
|
||||
>
|
||||
{{ iconName }}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import TooltippedLabel from "@/components/common/pv-tooltipped-label.vue";
|
||||
|
||||
const value = defineModel<string>({ required: true });
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
label?: string;
|
||||
tooltip?: string;
|
||||
// TODO fully update v-model usage in custom components on Vue3 update
|
||||
value: string;
|
||||
disabled?: boolean;
|
||||
errorMessage?: string;
|
||||
placeholder?: string;
|
||||
@@ -22,23 +21,17 @@ const props = withDefaults(
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "input", value: string): void;
|
||||
(e: "onEnter", value: string): void;
|
||||
(e: "onEscape"): void;
|
||||
}>();
|
||||
|
||||
const localValue = computed({
|
||||
get: () => props.value,
|
||||
set: (v) => emit("input", v)
|
||||
});
|
||||
|
||||
const handleKeydown = ({ key }) => {
|
||||
switch (key) {
|
||||
case "Enter":
|
||||
// Explicitly check that all rule props return true
|
||||
if (!props.rules?.every((rule) => rule(localValue.value) === true)) return;
|
||||
if (!props.rules?.every((rule) => rule(value.value) === true)) return;
|
||||
|
||||
emit("onEnter", localValue.value);
|
||||
emit("onEnter", value.value);
|
||||
break;
|
||||
case "Escape":
|
||||
emit("onEscape");
|
||||
@@ -55,9 +48,8 @@ const handleKeydown = ({ key }) => {
|
||||
|
||||
<v-col :cols="inputCols" class="d-flex align-center pr-0">
|
||||
<v-text-field
|
||||
v-model="localValue"
|
||||
dark
|
||||
dense
|
||||
v-model="value"
|
||||
density="compact"
|
||||
color="accent"
|
||||
:placeholder="placeholder"
|
||||
:disabled="disabled"
|
||||
@@ -65,6 +57,7 @@ const handleKeydown = ({ key }) => {
|
||||
:rules="rules"
|
||||
hide-details="auto"
|
||||
class="light-error"
|
||||
variant="underlined"
|
||||
@keydown="handleKeydown"
|
||||
/>
|
||||
</v-col>
|
||||
@@ -75,9 +68,3 @@ const handleKeydown = ({ key }) => {
|
||||
margin-top: 0px;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.light-error .error--text {
|
||||
color: red !important;
|
||||
caret-color: red !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import TooltippedLabel from "@/components/common/pv-tooltipped-label.vue";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = withDefaults(
|
||||
const value = defineModel<number>({
|
||||
required: true
|
||||
});
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
label?: string;
|
||||
tooltip?: string;
|
||||
// TODO fully update v-model usage in custom components on Vue3 update
|
||||
value: number;
|
||||
disabled?: boolean;
|
||||
labelCols?: number;
|
||||
rules?: ((v: number) => boolean | string)[];
|
||||
@@ -20,13 +20,9 @@ const props = withDefaults(
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "input", value: number): void;
|
||||
}>();
|
||||
|
||||
const localValue = computed({
|
||||
get: () => props.value,
|
||||
set: (v) => emit("input", parseFloat(v as unknown as string))
|
||||
get: () => value.value,
|
||||
set: (v) => (value.value = parseFloat(v as unknown as string))
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -38,12 +34,13 @@ const localValue = computed({
|
||||
<v-col class="pr-0">
|
||||
<v-text-field
|
||||
v-model="localValue"
|
||||
dark
|
||||
class="mt-0 pt-0"
|
||||
density="compact"
|
||||
hide-details
|
||||
single-line
|
||||
color="accent"
|
||||
type="number"
|
||||
variant="underlined"
|
||||
style="width: 70px"
|
||||
:step="step"
|
||||
:disabled="disabled"
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import TooltippedLabel from "@/components/common/pv-tooltipped-label.vue";
|
||||
const value = defineModel<number>({
|
||||
required: true
|
||||
});
|
||||
|
||||
const props = withDefaults(
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
label?: string;
|
||||
tooltip?: string;
|
||||
// TODO fully update v-model usage in custom components on Vue3 update
|
||||
value: number;
|
||||
disabled?: boolean;
|
||||
inputCols?: number;
|
||||
list: string[];
|
||||
@@ -17,15 +17,6 @@ const props = withDefaults(
|
||||
inputCols: 8
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "input", value: number): void;
|
||||
}>();
|
||||
|
||||
const localValue = computed({
|
||||
get: () => props.value,
|
||||
set: (v) => emit("input", v)
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -34,13 +25,14 @@ const localValue = computed({
|
||||
<tooltipped-label :tooltip="tooltip" :label="label" />
|
||||
</v-col>
|
||||
<v-col :cols="inputCols" class="d-flex align-center pr-0">
|
||||
<v-radio-group v-model="localValue" row dark :mandatory="true" hide-details="auto">
|
||||
<v-radio-group v-model="value" row:mandatory="true" hide-details="auto">
|
||||
<v-radio
|
||||
v-for="(radioName, index) in list"
|
||||
:key="index"
|
||||
:value="index"
|
||||
color="#ffd843"
|
||||
:label="radioName"
|
||||
:value="index"
|
||||
:model-value="index"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</v-radio-group>
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import TooltippedLabel from "@/components/common/pv-tooltipped-label.vue";
|
||||
import type { WebsocketNumberPair } from "@/types/WebsocketDataTypes";
|
||||
|
||||
const value = defineModel<[number, number] | WebsocketNumberPair>({
|
||||
required: true
|
||||
});
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
label?: string;
|
||||
tooltip?: string;
|
||||
// TODO fully update v-model usage in custom components on Vue3 update
|
||||
// value: [number, number] | WebsocketNumberPair, // Vue doesnt like Union types for the value prop for some reason.
|
||||
value: [number, number];
|
||||
min: number;
|
||||
max: number;
|
||||
step?: number;
|
||||
@@ -24,19 +25,15 @@ const props = withDefaults(
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "input", value: [number, number]): void;
|
||||
}>();
|
||||
|
||||
const localValue = computed<[number, number]>({
|
||||
get: (): [number, number] => {
|
||||
return Object.values(props.value) as [number, number];
|
||||
return Object.values(value.value) as [number, number];
|
||||
},
|
||||
set: (v) => {
|
||||
for (let i = 0; i < v.length; i++) {
|
||||
v[i] = parseFloat(v[i] as unknown as string);
|
||||
}
|
||||
emit("input", v);
|
||||
value.value = v;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -69,8 +66,7 @@ const checkNumberRange = (v: string): boolean => {
|
||||
:min="min"
|
||||
:disabled="disabled"
|
||||
hide-details
|
||||
class="align-center"
|
||||
dark
|
||||
class="align-center ml-0 mr-0"
|
||||
:color="inverted ? 'rgba(255, 255, 255, 0.2)' : 'accent'"
|
||||
:track-color="inverted ? 'accent' : undefined"
|
||||
thumb-color="accent"
|
||||
@@ -78,36 +74,38 @@ const checkNumberRange = (v: string): boolean => {
|
||||
>
|
||||
<template #prepend>
|
||||
<v-text-field
|
||||
:value="localValue[0]"
|
||||
dark
|
||||
:model-value="localValue[0]"
|
||||
color="accent"
|
||||
class="mt-0 pt-0"
|
||||
density="compact"
|
||||
hide-details
|
||||
single-line
|
||||
variant="underlined"
|
||||
:max="max"
|
||||
:min="min"
|
||||
:step="step"
|
||||
:rules="[checkNumberRange]"
|
||||
type="number"
|
||||
style="width: 60px"
|
||||
@input="(v) => changeFromSlot(v, 0)"
|
||||
@update:modelValue="(v) => changeFromSlot(v, 0)"
|
||||
/>
|
||||
</template>
|
||||
<template #append>
|
||||
<v-text-field
|
||||
:value="localValue[1]"
|
||||
dark
|
||||
:model-value="localValue[1]"
|
||||
color="accent"
|
||||
class="mt-0 pt-0"
|
||||
density="compact"
|
||||
hide-details
|
||||
single-line
|
||||
variant="underlined"
|
||||
:max="max"
|
||||
:min="min"
|
||||
:step="step"
|
||||
:rules="[checkNumberRange]"
|
||||
type="number"
|
||||
style="width: 60px"
|
||||
@input="(v) => changeFromSlot(v, 1)"
|
||||
@update:modelValue="(v) => changeFromSlot(v, 1)"
|
||||
/>
|
||||
</template>
|
||||
</v-range-slider>
|
||||
|
||||
@@ -7,14 +7,13 @@ export interface SelectItem {
|
||||
value: string | number;
|
||||
disabled?: boolean;
|
||||
}
|
||||
const value = defineModel<string | number | undefined>({ required: true });
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
label?: string;
|
||||
tooltip?: string;
|
||||
selectCols?: number;
|
||||
// TODO fully update v-model usage in custom components on Vue3 update
|
||||
value: any;
|
||||
disabled?: boolean;
|
||||
items: string[] | number[] | SelectItem[];
|
||||
}>(),
|
||||
@@ -24,15 +23,6 @@ const props = withDefaults(
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "input", value: string): void;
|
||||
}>();
|
||||
|
||||
const localValue = computed({
|
||||
get: () => props.value,
|
||||
set: (v) => emit("input", v)
|
||||
});
|
||||
|
||||
// Computed in case items changes
|
||||
const items = computed<SelectItem[]>(() => {
|
||||
// Trivial case for empty list; we have no data
|
||||
@@ -55,16 +45,15 @@ const items = computed<SelectItem[]>(() => {
|
||||
</v-col>
|
||||
<v-col :cols="selectCols" class="d-flex align-center pr-0">
|
||||
<v-select
|
||||
v-model="localValue"
|
||||
v-model="value"
|
||||
:items="items"
|
||||
item-text="name"
|
||||
item-title="name"
|
||||
item-value="value"
|
||||
item-disabled="disabled"
|
||||
dark
|
||||
color="accent"
|
||||
item-color="secondary"
|
||||
item-props.disabled="disabled"
|
||||
:disabled="disabled"
|
||||
hide-details="auto"
|
||||
variant="underlined"
|
||||
density="compact"
|
||||
/>
|
||||
</v-col>
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import TooltippedLabel from "@/components/common/pv-tooltipped-label.vue";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
label?: string;
|
||||
tooltip?: string;
|
||||
// TODO fully update v-model usage in custom components on Vue3 update
|
||||
value: number;
|
||||
modelValue: number;
|
||||
min: number;
|
||||
max: number;
|
||||
step?: number;
|
||||
@@ -20,9 +19,8 @@ const props = withDefaults(
|
||||
sliderCols: 8
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "input", value: number): void;
|
||||
(e: "update:modelValue", value: number): void;
|
||||
}>();
|
||||
|
||||
// Debounce function
|
||||
@@ -35,11 +33,11 @@ function debounce(func: (...args: any[]) => void, wait: number) {
|
||||
}
|
||||
|
||||
const debouncedEmit = debounce((v: number) => {
|
||||
emit("input", v);
|
||||
emit("update:modelValue", v);
|
||||
}, 20);
|
||||
|
||||
const localValue = computed({
|
||||
get: () => props.value,
|
||||
get: () => props.modelValue,
|
||||
set: (v) => debouncedEmit(parseFloat(v as unknown as string))
|
||||
});
|
||||
</script>
|
||||
@@ -49,10 +47,9 @@ const localValue = computed({
|
||||
<v-col :cols="12 - sliderCols" class="pl-0 d-flex align-center">
|
||||
<tooltipped-label :tooltip="tooltip" :label="label" />
|
||||
</v-col>
|
||||
<v-col :cols="sliderCols - 1">
|
||||
<v-col :cols="sliderCols - 1" class="pl-0">
|
||||
<v-slider
|
||||
v-model="localValue"
|
||||
dark
|
||||
class="align-center"
|
||||
:max="max"
|
||||
:min="min"
|
||||
@@ -68,16 +65,17 @@ const localValue = computed({
|
||||
</v-col>
|
||||
<v-col :cols="1" class="pr-0">
|
||||
<v-text-field
|
||||
:value="localValue"
|
||||
dark
|
||||
:model-value="localValue"
|
||||
color="accent"
|
||||
:max="max"
|
||||
:min="min"
|
||||
:disabled="disabled"
|
||||
class="mt-0 pt-0"
|
||||
density="compact"
|
||||
hide-details
|
||||
single-line
|
||||
type="number"
|
||||
variant="underlined"
|
||||
style="width: 100%"
|
||||
:step="step"
|
||||
:hide-spin-buttons="true"
|
||||
|
||||
@@ -1,43 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import TooltippedLabel from "@/components/common/pv-tooltipped-label.vue";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = withDefaults(
|
||||
const value = defineModel<boolean>();
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
label?: string;
|
||||
tooltip?: string;
|
||||
// TODO fully update v-model usage in custom components on Vue3 update
|
||||
value: boolean;
|
||||
disabled?: boolean;
|
||||
labelCols?: number;
|
||||
switchCols?: number;
|
||||
dense?: boolean;
|
||||
}>(),
|
||||
{
|
||||
disabled: false,
|
||||
labelCols: 2,
|
||||
switchCols: 8,
|
||||
dense: false
|
||||
switchCols: 8
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "input", value: boolean): void;
|
||||
}>();
|
||||
|
||||
const localValue = computed({
|
||||
get: () => props.value,
|
||||
set: (v) => emit("input", v)
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="d-flex">
|
||||
<v-col :cols="12 - switchCols || labelCols" class="d-flex align-center pl-0">
|
||||
<v-col :cols="12 - switchCols || labelCols" class="d-flex align-center pl-0 pt-2 pb-2">
|
||||
<tooltipped-label :tooltip="tooltip" :label="label" />
|
||||
</v-col>
|
||||
<v-col :cols="switchCols || 12 - labelCols" class="d-flex align-center pr-0">
|
||||
<v-switch v-model="localValue" dark :disabled="disabled" color="#ffd843" hide-details="auto" class="pb-1" />
|
||||
<v-col :cols="switchCols || 12 - labelCols" class="d-flex align-center pr-0 pt-2 pb-2">
|
||||
<v-switch v-model="value" :disabled="disabled" color="#ffd843" hide-details density="compact" />
|
||||
</v-col>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -7,9 +7,9 @@ defineProps<{
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<v-tooltip :disabled="tooltip === undefined" right open-delay="300">
|
||||
<template #activator="{ on, attrs }">
|
||||
<span style="cursor: text !important" class="white--text" v-bind="attrs" v-on="on">{{ label }}</span>
|
||||
<v-tooltip :disabled="tooltip === undefined" location="right" open-delay="300">
|
||||
<template #activator="{ props }">
|
||||
<span style="cursor: text !important" class="text-white" v-bind="props">{{ label }}</span>
|
||||
</template>
|
||||
<span>{{ tooltip }}</span>
|
||||
</v-tooltip>
|
||||
|
||||
Reference in New Issue
Block a user