This commit is contained in:
j3d1 2026-08-17 18:58:42 +02:00
parent 7d7730354e
commit accbaf3603

View file

@ -117,7 +117,7 @@
Disconnect
</button>
<button v-else-if="d.supported" class="btn btn-sm btn-outline-primary"
:disabled="busy || connected !== null" @click="connect(d.index)">
:disabled="busy" @click="connect(d.index)">
Connect
</button>
</li>
@ -267,6 +267,8 @@ export default {
devices: [],
connected: null,
tape: null,
labelBitmap: null,
value: this.prefill || "",
copies: 1,
@ -305,6 +307,16 @@ export default {
this.redrawFallback();
}
},
// Covers connecting/disconnecting/switching printers - anything that changes the
// tape dimensions redraw() sizes the canvas from. flush: 'post' because the canvas
// itself only exists once `tape` is truthy (see the v-if/v-else in the template), so
// this has to run after Vue has actually mounted it, not before.
tape: {
handler() {
this.redraw();
},
flush: 'post'
},
},
methods: {
async guard(fn) {
@ -344,25 +356,32 @@ export default {
});
},
async closeConnection() {
if (this.connected) {
await this.blob.close();
this.connected = null;
this.tape = null;
}
},
connect(index) {
this.guard(async () => {
// Only one open device connection at a time - pressing Connect on a different
// printer while one is already open implicitly disconnects it first, rather
// than requiring an explicit Disconnect click.
await this.closeConnection();
const device = this.devices[index];
this.blob.setDevices([device]);
await this.blob.open(device.vendorId, device.productId);
this.connected = device;
const status = await this.blob.status();
// Setting `tape` alone is enough to redraw - see the tape watcher above.
this.tape = this.blob.can("print") ? tapeFromStatus(status) : null;
await nextTick();
this.redraw();
});
},
disconnect() {
this.guard(async () => {
await this.blob.close();
this.connected = null;
this.tape = null;
});
this.guard(() => this.closeConnection());
},
redraw() {
@ -374,6 +393,7 @@ export default {
if (!canvas) {
return;
}
this.resizeObserver.observe(canvas.parentElement);
try {
const qr = QRCode.create(this.value);
drawQrLabel(canvas, qr, this.tape);
@ -397,6 +417,7 @@ export default {
if (!canvas) {
return;
}
this.resizeObserver.observe(canvas.parentElement);
try {
const qr = QRCode.create(this.value);
drawQrSquare(canvas, qr);
@ -441,13 +462,17 @@ export default {
});
},
handleResize() {
/* Re-fits whichever canvas sits in a resized container - covers a window resize, but
also a sidebar toggle, a font finishing loading, or any other layout change that
isn't a window resize at all. Debounced since ResizeObserver can fire in bursts. */
handleContainerResize(entries) {
clearTimeout(this.resizeTimer);
this.resizeTimer = setTimeout(() => {
if (this.usbSupported) {
this.redraw();
} else {
this.redrawFallback();
for (const entry of entries) {
const canvas = entry.target.querySelector("canvas");
if (canvas) {
this.fitZoom(canvas);
}
}
}, 100);
},
@ -458,12 +483,11 @@ export default {
},
created() {
this.blob = null;
this.tape = null;
this.labelBitmap = null;
this.resizeObserver = null;
this.resizeTimer = null;
},
async mounted() {
window.addEventListener("resize", this.handleResize);
this.resizeObserver = new ResizeObserver(this.handleContainerResize);
if (!("usb" in navigator)) {
this.usbSupported = false;
await nextTick();
@ -486,7 +510,7 @@ export default {
navigator.usb.removeEventListener("connect", this.onUsbChange);
navigator.usb.removeEventListener("disconnect", this.onUsbChange);
}
window.removeEventListener("resize", this.handleResize);
this.resizeObserver.disconnect();
clearTimeout(this.resizeTimer);
},
}