From 22717a914b830410881ecc2a325ece77478c1876 Mon Sep 17 00:00:00 2001 From: Bryon Lewis <61746913+BryonLewis@users.noreply.github.com> Date: Tue, 30 Jan 2024 10:47:35 -0500 Subject: [PATCH] Editor Improvements (#26) * adding legend capabilities * add hover state and improve legend * toggle icon bar * defaulting grid to off * editing improvements * merge fix --- client/src/components/AnnotationList.vue | 47 ++++-- client/src/components/SpectrogramViewer.vue | 83 ++++++++++- client/src/components/geoJS/LayerManager.vue | 24 +++- client/src/components/geoJS/geoJSUtils.ts | 29 +++- .../geoJS/layers/editAnnotationLayer.ts | 12 +- client/src/views/Spectrogram.vue | 134 +++++++++++++----- 6 files changed, 268 insertions(+), 61 deletions(-) diff --git a/client/src/components/AnnotationList.vue b/client/src/components/AnnotationList.vue index 47dbdf8..37f10e9 100644 --- a/client/src/components/AnnotationList.vue +++ b/client/src/components/AnnotationList.vue @@ -3,6 +3,7 @@ import { defineComponent, PropType } from "vue"; import { SpectroInfo } from './geoJS/geoJSUtils'; import { SpectrogramAnnotation } from "../api/api"; import useState from "../use/useState"; +import { watch } from "vue"; export default defineComponent({ name: "AnnotationList", @@ -20,11 +21,28 @@ export default defineComponent({ selectedId: { type: Number as PropType, default: null, + }, + mode: { + type: String as PropType<'creation' | 'editing' | 'disabled'>, + required: true, } }, emits: ['select'], - setup() { + setup(props) { const { annotationState, setAnnotationState } = useState(); + const scrollToId = (id: number) => { + const el = document.getElementById(`annotation-${id}`); + if (el) { + el.scrollIntoView({block: 'end', behavior: 'smooth'}); + } + }; + watch(() => props.selectedId, () => { + if (props.selectedId !== null) { + scrollToId(props.selectedId); + } + }); + + return { annotationState, setAnnotationState, @@ -40,7 +58,7 @@ export default defineComponent({ Annotations Addmdi-plus @@ -48,24 +66,26 @@ export default defineComponent({ + + + Time + Frequency + + - - {{ annotation.id }} - - {{ annotation.start_time }}ms to {{ annotation.end_time }}ms + {{ annotation.start_time }}-{{ annotation.end_time }}ms - {{ annotation.low_freq }}hz to {{ annotation.high_freq }}hz + {{ (annotation.low_freq/1000).toFixed(1) }}-{{ (annotation.high_freq/1000).toFixed() }}Khz import { defineComponent, PropType, ref, Ref, watch } from "vue"; -import { SpectroInfo, useGeoJS } from "./geoJS/geoJSUtils"; +import { SpectroInfo, spectroToCenter, useGeoJS } from "./geoJS/geoJSUtils"; import { patchAnnotation, putAnnotation, SpectrogramAnnotation } from "../api/api"; import LayerManager from "./geoJS/LayerManager.vue"; import { GeoEvent } from "geojs"; @@ -37,11 +37,38 @@ export default defineComponent({ default: false, }, }, - emits: ["update:annotation", "create:annotation", "selected", "geoViewerRef", "hoverData"], + emits: ["update:annotation", "create:annotation", "selected", "geoViewerRef", "hoverData", 'set-mode',], setup(props, { emit }) { const containerRef: Ref = ref(); const geoJS = useGeoJS(); const initialized = ref(false); + const cursor = ref(''); + const imageCursorRef: Ref = ref(); + const setCursor = (newCursor: string) => { + cursor.value = newCursor; + }; + + const cursorHandler = { + handleMouseLeave() { + if (imageCursorRef.value) { + imageCursorRef.value.style.display = 'none'; + } + }, + handleMouseEnter() { + if (imageCursorRef.value) { + imageCursorRef.value.style.display = 'block'; + } + }, + handleMouseMove(evt: MouseEvent) { + const offsetX = evt.clientX + 10; + const offsetY = evt.clientY - 25; + window.requestAnimationFrame(() => { + if (imageCursorRef.value) { + imageCursorRef.value.style.transform = `translate(${offsetX}px, ${offsetY}px)`; + } + }); + }, + }; const mouseMoveEvent = (e: GeoEvent) => { const { x, y } = e.geo; @@ -108,6 +135,7 @@ export default defineComponent({ } }; + const createAnnotation = async (annotation: SpectrogramAnnotation) => { // We call the patch on the selected annotation if (props.recordingId !== null) { @@ -115,13 +143,39 @@ export default defineComponent({ emit("create:annotation", response.data.id); } }; + let skipNextSelected = false; + watch(() => props.selectedId, () => { + if (skipNextSelected) { + skipNextSelected = false; + return; + } + const found = props.annotations.find((item) => item.id === props.selectedId); + if (found && props.spectroInfo) { + + const center = spectroToCenter(found, props.spectroInfo); + const x = center[0]; + const y = center[1]; + geoJS.getGeoViewer().value.center({x, y}); + } + }); + + + const clickSelected = (annotation: SpectrogramAnnotation) => { + skipNextSelected = true; + emit('selected', annotation); + }; return { containerRef, geoViewerRef: geoJS.getGeoViewer(), initialized, + cursor, + clickSelected, + setCursor, updateAnnotation, createAnnotation, + cursorHandler, + imageCursorRef, }; }, }); @@ -133,6 +187,10 @@ export default defineComponent({ id="spectro" ref="containerRef" class="playback-container" + :style="{cursor : cursor }" + @mousemove="cursorHandler.handleMouseMove" + @mouseleave="cursorHandler.handleMouseLeave" + @mouseover="cursorHandler.handleMouseEnter" /> +
+ + {{ cursor }} + +
@@ -184,4 +252,13 @@ export default defineComponent({ cursor: inherit; } } +.imageCursor { + z-index: 9999; //So it will be above the annotator layers + position: fixed; + backface-visibility: hidden; + top: 0; + left: 0; + pointer-events: none; +} + diff --git a/client/src/components/geoJS/LayerManager.vue b/client/src/components/geoJS/LayerManager.vue index 34b79ee..a65a5ae 100644 --- a/client/src/components/geoJS/LayerManager.vue +++ b/client/src/components/geoJS/LayerManager.vue @@ -1,5 +1,5 @@