stash
This commit is contained in:
parent
8d96bc97c4
commit
ed04d98bf1
54 changed files with 661 additions and 1214 deletions
|
|
@ -6,9 +6,9 @@
|
|||
<div class="card-body">
|
||||
<div class="template-grid d-flex flex-wrap align-items-start">
|
||||
<div v-for="t in labelTemplates" :key="t.id" class="template-option d-flex flex-column text-center"
|
||||
:class="{'template-option-disabled': !isAvailable(t)}"
|
||||
:title="isAvailable(t) ? '' : 'Not available - fill in the fields this layout needs above.'"
|
||||
role="button" @click="isAvailable(t) && $emit('input', t.id)">
|
||||
:class="{'template-option-disabled': !isSelectable(t)}"
|
||||
:title="unavailableReason(t)"
|
||||
role="button" @click="isSelectable(t) && $emit('input', t.id)">
|
||||
<canvas :ref="el => setTemplateCanvasRef(t.id, el)"
|
||||
class="img-thumbnail template-thumb-canvas"
|
||||
:class="{'border-primary': value === t.id}"></canvas>
|
||||
|
|
@ -24,8 +24,7 @@
|
|||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* This build pins Bootstrap 4 (no gap-* utilities, those are Bootstrap 5.1+), so the spacing
|
||||
here is plain CSS gap rather than a Bootstrap gap-N class. */
|
||||
/* This build pins Bootstrap 4 (no gap-* utilities, those are 5.1+) - hence plain CSS gap here. */
|
||||
.template-grid {
|
||||
gap: 1rem;
|
||||
}
|
||||
|
|
@ -43,9 +42,8 @@
|
|||
display: block;
|
||||
width: 100%;
|
||||
height: 10rem;
|
||||
/* The canvas itself is drawn at whatever size fits its content (see redraw/drawFallbackLabel)
|
||||
- object-fit scales that down to the thumbnail box the same way it would for an <img>,
|
||||
no manual zoom math needed. */
|
||||
/* Canvas draws at whatever size fits its content (see redraw/drawFallbackLabel); object-fit
|
||||
scales it into the thumbnail box like it would an <img>, no manual zoom math needed. */
|
||||
object-fit: contain;
|
||||
background: #fff;
|
||||
}
|
||||
|
|
@ -58,9 +56,9 @@ import {LABEL_TEMPLATES, templateIsAvailable, templateContent} from "@/label-lay
|
|||
export default {
|
||||
name: "LabelLayoutPreview",
|
||||
props: {
|
||||
// Named content fields the templates draw from (see label.js's buildLabelFields)
|
||||
// - kept in sync by the parent, not owned here. A field missing from this object (rather
|
||||
// than present-but-empty) means a template that needs it is unavailable right now.
|
||||
// Named content fields templates draw from (see label.js's buildLabelFields), kept in
|
||||
// sync by the parent. A field's absence (not just empty) means a template needing it is
|
||||
// unavailable.
|
||||
fields: {
|
||||
type: Object,
|
||||
required: true
|
||||
|
|
@ -70,12 +68,11 @@ export default {
|
|||
type: String,
|
||||
required: true
|
||||
},
|
||||
// Print.vue's global qr/micro-qr/rmqr choice (see label.js's QR_CODE_TYPES) - passed
|
||||
// straight through to drawFallbackLabel so these thumbnails match whatever symbology the
|
||||
// main preview is actually using.
|
||||
codeType: {
|
||||
type: String,
|
||||
default: "qr"
|
||||
// Ids of the most-recently printed/downloaded templates, most recent first (see
|
||||
// Print.vue's rememberPrintedTemplate); bubbled to the front of the grid below.
|
||||
recentTemplateIds: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
model: {
|
||||
|
|
@ -83,17 +80,29 @@ export default {
|
|||
event: "input"
|
||||
},
|
||||
emits: ["input"],
|
||||
data() {
|
||||
return {
|
||||
// Keyed by template id, holding the error message from its most recent redraw()
|
||||
// failure (e.g. text too long, or too many QR modules for the thumbnail's fixed
|
||||
// reference size - see label.js's snapQrToCrispSize). Absent, not just falsy, for a
|
||||
// template that last drew fine, so `t.id in failed` matches "has a message".
|
||||
failed: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// LABEL_TEMPLATES with recentTemplateIds' entries pulled to the front (most recent
|
||||
// first), everything else following in its original order.
|
||||
labelTemplates() {
|
||||
return LABEL_TEMPLATES;
|
||||
const recent = this.recentTemplateIds
|
||||
.map(id => LABEL_TEMPLATES.find(t => t.id === id))
|
||||
.filter(Boolean);
|
||||
const recentIds = new Set(recent.map(t => t.id));
|
||||
return [...recent, ...LABEL_TEMPLATES.filter(t => !recentIds.has(t.id))];
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
fields() {
|
||||
this.redraw();
|
||||
},
|
||||
codeType() {
|
||||
this.redraw();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -109,10 +118,24 @@ export default {
|
|||
return templateIsAvailable(t, this.fields);
|
||||
},
|
||||
|
||||
/* Live per-template thumbnails. Always uses the content-fit fallback renderer (rather
|
||||
than the tape-fed one), regardless of whether a real printer is connected - these are
|
||||
illustrative previews sized by CSS object-fit, not the accurate to-be-printed canvas
|
||||
the main preview is. */
|
||||
// Greyed out (and unclickable) either because a needed field isn't filled in yet
|
||||
// (isAvailable) or its content failed to render (see redraw()'s catch) - either way
|
||||
// there's nothing a click could select that would actually show anything.
|
||||
isSelectable(t) {
|
||||
return this.isAvailable(t) && !(t.id in this.failed);
|
||||
},
|
||||
|
||||
// The disabled thumbnail's tooltip - empty once it's selectable again.
|
||||
unavailableReason(t) {
|
||||
if (!this.isAvailable(t)) {
|
||||
return "Not available - fill in the fields this layout needs above.";
|
||||
}
|
||||
return this.failed[t.id] ?? "";
|
||||
},
|
||||
|
||||
// Live per-template thumbnails. Always uses the content-fit fallback renderer (not the
|
||||
// tape-fed one) regardless of printer connection - illustrative previews via CSS
|
||||
// object-fit, not the to-be-printed-accurate canvas the main preview is.
|
||||
redraw() {
|
||||
for (const t of LABEL_TEMPLATES) {
|
||||
const canvas = this.templateCanvases[t.id];
|
||||
|
|
@ -123,27 +146,32 @@ export default {
|
|||
if (!this.isAvailable(t) || !content) {
|
||||
canvas.width = 1;
|
||||
canvas.height = 1;
|
||||
delete this.failed[t.id];
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
drawFallbackLabel(canvas, content, "along", this.codeType);
|
||||
drawFallbackLabel(canvas, content, "along");
|
||||
delete this.failed[t.id];
|
||||
} catch (e) {
|
||||
// A thumbnail that can't render at this content length just stays blank
|
||||
// rather than surfacing an error for every keystroke.
|
||||
// Grey the thumbnail out (see isSelectable/unavailableReason) rather than
|
||||
// leaving it blank - selecting a template that can't render here would only
|
||||
// hand Print.vue's own redraw the exact same failure.
|
||||
canvas.width = 1;
|
||||
canvas.height = 1;
|
||||
this.failed[t.id] = e.message;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// Keyed by template id, populated by setTemplateCanvasRef() - a plain :ref="t.id" string
|
||||
// inside v-for would still get Vue's refInFor array-collecting behavior even though
|
||||
// each iteration uses a different name, turning this.$refs[t.id] into a one-element
|
||||
// array rather than the canvas itself. A function ref sidesteps that entirely.
|
||||
// in v-for still gets Vue's refInFor array-collecting behavior despite distinct names per
|
||||
// iteration, turning this.$refs[t.id] into a one-element array; a function ref avoids that.
|
||||
this.templateCanvases = {};
|
||||
},
|
||||
async mounted() {
|
||||
// See Print.vue's mounted() - every thumbnail here has a "qrcode" leaf, so there's
|
||||
// nothing worth drawing before this resolves.
|
||||
// See Print.vue's mounted() - every thumbnail here has a qr/mqr/rmqr leaf, so nothing's
|
||||
// worth drawing before the wasm resource resolves.
|
||||
await preloadQrEncoder();
|
||||
this.redraw();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue