Add a toggle switch for image capture to gallery
This commit is contained in:
parent
2bcc98094f
commit
af18850c41
3 changed files with 190 additions and 40 deletions
123
webapp/src/components/genericComponents/toggleSwitch.vue
Normal file
123
webapp/src/components/genericComponents/toggleSwitch.vue
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p v-if="labelText !== undefined" class="toggle-label uk-form-label">{{ labelText }}</p>
|
||||||
|
<div class="toggle-switch" :class="{ checked: modelValue }" @click="toggle" />
|
||||||
|
<p v-if="showStateLabel" class="toggle-state-label">{{ stateLabelText }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "ToggleSwitch",
|
||||||
|
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
required: true,
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
labelText: {
|
||||||
|
required: false,
|
||||||
|
type: String,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
offLabel: {
|
||||||
|
required: false,
|
||||||
|
type: String,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
onLabel: {
|
||||||
|
required: false,
|
||||||
|
type: String,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
emits: ["update:modelValue"],
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
showStateLabel() {
|
||||||
|
return this.onLabel !== undefined && this.offLabel !== undefined;
|
||||||
|
},
|
||||||
|
stateLabelText() {
|
||||||
|
return this.modelValue ? this.onLabel : this.offLabel;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
toggle() {
|
||||||
|
this.$emit("update:modelValue", !this.modelValue);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import url("../../assets/less/variables.less");
|
||||||
|
|
||||||
|
.toggle-label {
|
||||||
|
margin-bottom: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-state-label {
|
||||||
|
margin-top: 0.2rem;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch {
|
||||||
|
background: #ddd;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.08);
|
||||||
|
cursor: pointer;
|
||||||
|
flex: none;
|
||||||
|
height: 24px;
|
||||||
|
position: relative;
|
||||||
|
transition: background-color 150ms;
|
||||||
|
width: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch::before {
|
||||||
|
background: #fff;
|
||||||
|
background-image: radial-gradient(
|
||||||
|
circle at 6px 6px,
|
||||||
|
rgba(0, 0, 0, 0) 0,
|
||||||
|
rgba(0, 0, 0, 0.05) 16px
|
||||||
|
);
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.08);
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
height: 20px;
|
||||||
|
left: 2px;
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
transition: left 150ms;
|
||||||
|
width: 20px;
|
||||||
|
will-change: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checked {
|
||||||
|
background-color: @global-primary-background;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checked::before {
|
||||||
|
background-image: radial-gradient(
|
||||||
|
circle at 6px 6px,
|
||||||
|
rgba(0, 0, 0, 0) 0,
|
||||||
|
rgba(0, 0, 0, 0.05) 16px
|
||||||
|
);
|
||||||
|
left: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch:hover {
|
||||||
|
box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch:hover::before {
|
||||||
|
background-image: radial-gradient(
|
||||||
|
circle at 6px 6px,
|
||||||
|
rgba(0, 0, 0, 0) 0,
|
||||||
|
rgba(0, 0, 0, 0.1375) 16px
|
||||||
|
);
|
||||||
|
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
<template>
|
||||||
|
<div id="captureControl">
|
||||||
|
<p><b>Image Capture</b></p>
|
||||||
|
|
||||||
|
<toggle-switch
|
||||||
|
v-model="saveToGallery"
|
||||||
|
label-text="Save to Gallery?"
|
||||||
|
on-label="Saving"
|
||||||
|
off-label="Downloading"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="uk-margin">
|
||||||
|
<action-button
|
||||||
|
thing="camera"
|
||||||
|
action="capture"
|
||||||
|
:submit-data="{ capture_mode: 'standard' }"
|
||||||
|
submit-label="Capture"
|
||||||
|
:submit-on-event="'globalCaptureEvent'"
|
||||||
|
@response="handleCaptureResponse"
|
||||||
|
@error="modalError"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from "axios";
|
||||||
|
import toggleSwitch from "@/components/genericComponents/toggleSwitch.vue";
|
||||||
|
import ActionButton from "@/components/labThingsComponents/actionButton.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "CaptureControl",
|
||||||
|
|
||||||
|
components: {
|
||||||
|
toggleSwitch,
|
||||||
|
ActionButton,
|
||||||
|
},
|
||||||
|
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
saveToGallery: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
handleCaptureResponse: async function (response) {
|
||||||
|
// Retrieve the captured image and save it
|
||||||
|
let imageUri = response.output.href;
|
||||||
|
if (!imageUri) {
|
||||||
|
this.modalError("No image URI returned from capture task.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// To save the returned data, we make a virtual link and click it
|
||||||
|
let imageResponse = await axios.get(imageUri, { responseType: "blob" });
|
||||||
|
const url = window.URL.createObjectURL(new Blob([imageResponse.data]));
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = url;
|
||||||
|
link.setAttribute("download", `OFM_${new Date().toISOString()}.jpeg`);
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
@ -2,34 +2,20 @@
|
||||||
<div id="paneControl" class="uk-padding-small">
|
<div id="paneControl" class="uk-padding-small">
|
||||||
<positionControl v-if="stageAvailable" />
|
<positionControl v-if="stageAvailable" />
|
||||||
<autofocusControl v-if="autofocusAvailable" />
|
<autofocusControl v-if="autofocusAvailable" />
|
||||||
<p>Image Capture</p>
|
<captureControl />
|
||||||
|
|
||||||
<div class="uk-margin">
|
|
||||||
<action-button
|
|
||||||
thing="camera"
|
|
||||||
action="capture"
|
|
||||||
:submit-data="{ capture_mode: 'standard' }"
|
|
||||||
submit-label="Capture"
|
|
||||||
:submit-on-event="'globalCaptureEvent'"
|
|
||||||
@response="handleCaptureResponse"
|
|
||||||
@error="modalError"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from "axios";
|
import captureControl from "./captureControl.vue";
|
||||||
import ActionButton from "../../labThingsComponents/actionButton.vue";
|
|
||||||
import positionControl from "./positionControl.vue";
|
import positionControl from "./positionControl.vue";
|
||||||
import autofocusControl from "./autofocusControl.vue";
|
import autofocusControl from "./autofocusControl.vue";
|
||||||
import { eventBus } from "../../../eventBus.js";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "PaneControl",
|
name: "PaneControl",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ActionButton,
|
captureControl,
|
||||||
positionControl,
|
positionControl,
|
||||||
autofocusControl,
|
autofocusControl,
|
||||||
},
|
},
|
||||||
|
|
@ -42,28 +28,5 @@ export default {
|
||||||
return this.thingAvailable("autofocus");
|
return this.thingAvailable("autofocus");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
|
||||||
handleCaptureResponse: async function (response) {
|
|
||||||
// Retrieve the captured image and save it
|
|
||||||
let imageUri = response.output.href;
|
|
||||||
if (!imageUri) {
|
|
||||||
this.modalError("No image URI returned from capture task.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Emit flash capture stream animation
|
|
||||||
eventBus.emit("globalFlashStream");
|
|
||||||
|
|
||||||
// To save the returned data, we make a virtual link and click it
|
|
||||||
let imageResponse = await axios.get(imageUri, { responseType: "blob" });
|
|
||||||
|
|
||||||
const url = window.URL.createObjectURL(new Blob([imageResponse.data]));
|
|
||||||
const link = document.createElement("a");
|
|
||||||
link.href = url;
|
|
||||||
link.setAttribute("download", `OFM_${new Date().toISOString()}.jpeg`);
|
|
||||||
document.body.appendChild(link);
|
|
||||||
link.click();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue