|
@@ -1,5 +1,12 @@
|
|
|
<template>
|
|
|
<div class="media-control">
|
|
|
+ <button-input type="transparent"
|
|
|
+ style="color: var(--on_error)"
|
|
|
+ :class="{disabled: !hasNext}"
|
|
|
+ @click="$emit('control', !control)">
|
|
|
+ {{ control ? '▼' : '▲' }}
|
|
|
+ </button-input>
|
|
|
+
|
|
|
<button-input type="transparent"
|
|
|
style="color: var(--on_error)"
|
|
|
:class="{disabled: !hasPrevious}"
|
|
@@ -26,12 +33,26 @@
|
|
|
>
|
|
|
</button-input>
|
|
|
|
|
|
- <button-input type="transparent"
|
|
|
- style="color: var(--on_error)"
|
|
|
- :class="{disabled: !hasNext}"
|
|
|
- @click="$emit('control', !control)">
|
|
|
- {{ control ? '▼' : '▲' }}
|
|
|
+ <button-input type="transparent">
|
|
|
+ <img alt="label" src="@/assets/icons/tag.svg"
|
|
|
+ :class="{tagged: currentLabel}"
|
|
|
+ @touchstart.stop @mousedown.stop
|
|
|
+ @click.stop="showLabelSelection = true">
|
|
|
</button-input>
|
|
|
+
|
|
|
+ <select v-if="showLabelSelection"
|
|
|
+ @touchstart.stop @mousedown.stop
|
|
|
+ @change="labelSelf"
|
|
|
+ @focusout="showLabelSelection = false">
|
|
|
+ <option value="">None</option>
|
|
|
+
|
|
|
+ <option v-for="label in labelList"
|
|
|
+ :key="label.id"
|
|
|
+ :value="label.id"
|
|
|
+ :selected="label === currentLabel">
|
|
|
+ {{ label.name }}
|
|
|
+ </option>
|
|
|
+ </select>
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
@@ -43,15 +64,48 @@ import ButtonRow from "@/components/base/button-row";
|
|
|
export default {
|
|
|
name: "media-control",
|
|
|
components: {ButtonRow, ButtonInput},
|
|
|
- props: ['hasPrevious', 'hasNext', 'control'],
|
|
|
+ props: ['hasPrevious', 'hasNext', 'control', 'data', 'project', 'socket'],
|
|
|
+ data: function () {
|
|
|
+ return {
|
|
|
+ showLabelSelection: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ mediaUrl: function () {
|
|
|
+ return this.socket.media(this.project.id, this.data.id);
|
|
|
+ },
|
|
|
+ labelList: function () {
|
|
|
+ return Object.keys(this.project.labels).map(key => this.project.labels[key]);
|
|
|
+ },
|
|
|
+ currentLabel: function () {
|
|
|
+ const predictions = Object.keys(this.data.predictionResults).map(k => this.data.predictionResults[k]);
|
|
|
+ for (let result of predictions) {
|
|
|
+ if (!('x' in result || 'y' in result || 'w' in result || 'h' in result)) {
|
|
|
+ return this.project.labels[result.label];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ },
|
|
|
methods: {
|
|
|
- previous: function() {
|
|
|
+ previous: function () {
|
|
|
if (this.hasPrevious)
|
|
|
this.$emit('previous', null);
|
|
|
},
|
|
|
- next: function() {
|
|
|
+ next: function () {
|
|
|
if (this.hasNext)
|
|
|
this.$emit('next', null);
|
|
|
+ },
|
|
|
+ labelSelf: function (event) {
|
|
|
+ const options = event.target.options;
|
|
|
+ const option = options[options.selectedIndex];
|
|
|
+ const value = option.value;
|
|
|
+
|
|
|
+ this.socket.post(this.mediaUrl, {
|
|
|
+ label: value ? value : false
|
|
|
+ });
|
|
|
+ this.showLabelSelection = false;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -59,12 +113,29 @@ export default {
|
|
|
|
|
|
<style scoped>
|
|
|
.media-control {
|
|
|
+ position: relative;
|
|
|
display: flex;
|
|
|
justify-content: center;
|
|
|
align-items: center;
|
|
|
+ margin: 0 0.5rem;
|
|
|
}
|
|
|
|
|
|
.disabled {
|
|
|
opacity: 0.4;
|
|
|
}
|
|
|
+
|
|
|
+img {
|
|
|
+ filter: invert(1);
|
|
|
+ opacity: 0.4;
|
|
|
+}
|
|
|
+
|
|
|
+img.tagged {
|
|
|
+ opacity: 1;
|
|
|
+}
|
|
|
+
|
|
|
+select {
|
|
|
+ position: absolute;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
</style>
|