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:
Graham
2025-05-06 18:21:41 -04:00
committed by GitHub
parent 29f76bc1c3
commit bec8092660
54 changed files with 1661 additions and 1843 deletions

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 }}

View File

@@ -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>

View File

@@ -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"

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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"

View File

@@ -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>

View File

@@ -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>