Stop a caption from eating the barcode, and put the labels beside the settings
Reported as the CODE128 not appearing in the preview. It was appearing, in 44 of the 45 preset and layout combinations swept - as a 36 pixel stub. The code box and the caption were both allowed to grow, so a caption of any length took half the label and then kept taking, and on 1.00 x 0.50in stock a few words of caption left a barcode too narrow to scan. Present, useless, and indisinguishable from a failed render. The caption now takes the room it needs and no more, and a CODE128 holds at least 40% of the label, or all of it when stacked: past that there are too few narrow bars left to read, and a caption that wraps or clips is the better trade. So that a real failure can never again look like this one, an empty code box now says so in red in the preview. It is screen-only - the marker must never reach a printed label. The settings were a narrow column with the preview trailing below them, which put the labels being adjusted off the bottom of the screen. Settings on the left, labels on the right in a column that stays in view and scrolls on its own, folding back to one column on a narrow window. That scrolling column is a print hazard worth naming: an ancestor with its own overflow clips a multi-page print to whatever happened to be on screen. Print resets it, and the check is a six-page two-sided run coming out as six pages with the label as the only clipping box in the chain.
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
<template>
|
||||
<div class="code-generator">
|
||||
<div class="no-print">
|
||||
<div class="page-header">
|
||||
<h1>Barcode / QR Generator</h1>
|
||||
<router-link to="/tools" class="btn btn-secondary">Back to Tech Tools</router-link>
|
||||
</div>
|
||||
<div class="page-header no-print">
|
||||
<h1>Barcode / QR Generator</h1>
|
||||
<router-link to="/tools" class="btn btn-secondary">Back to Tech Tools</router-link>
|
||||
</div>
|
||||
|
||||
<div class="controls card">
|
||||
<div class="workspace">
|
||||
<div class="options no-print">
|
||||
<div class="controls card">
|
||||
<!-- What to encode -->
|
||||
<div class="control-row">
|
||||
<label>
|
||||
@@ -285,33 +286,52 @@
|
||||
{{ overlong.length }} row(s) could not be encoded as CODE128 or were empty:
|
||||
{{ overlong.slice(0, 3).join(', ') }}{{ overlong.length > 3 ? ' ...' : '' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Preview doubles as the print surface: what is on screen is what prints. -->
|
||||
<div class="sheet">
|
||||
<div
|
||||
v-for="item in visible"
|
||||
:key="item.index"
|
||||
class="label"
|
||||
:class="labelClasses(item.page)"
|
||||
>
|
||||
<img v-if="showPicture(item.page) && pictureposition === 'behind'"
|
||||
:src="pictureUrl" class="picture-behind" alt="" />
|
||||
<div class="content">
|
||||
<div v-if="showPicture(item.page) && pictureposition !== 'behind'" class="picturebox">
|
||||
<img :src="pictureUrl" class="picture-img" alt="" />
|
||||
</div>
|
||||
<div class="codegroup">
|
||||
<div v-if="layoutFor(item.page) !== 'textonly'" class="codebox">
|
||||
<img v-if="images[item.index]" :src="images[item.index]" class="code-img" alt="" />
|
||||
</div>
|
||||
<div v-if="layoutFor(item.page) !== 'codeonly' && item.page.label" class="label-text">
|
||||
{{ item.page.label }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span v-if="backenabled" class="side-flag">{{ item.page.side }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Preview doubles as the print surface: what is on screen is what
|
||||
prints, so everything around it is chrome that print must strip. -->
|
||||
<div class="preview">
|
||||
<div class="preview-head no-print">
|
||||
<h4 class="section-heading">Preview</h4>
|
||||
<span class="preview-count">
|
||||
{{ cardCount }} label{{ cardCount === 1 ? '' : 's' }}<span
|
||||
v-if="backenabled">, {{ pages.length }} pages</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="sheet">
|
||||
<div
|
||||
v-for="item in visible"
|
||||
:key="item.index"
|
||||
class="label"
|
||||
:class="labelClasses(item.page)"
|
||||
>
|
||||
<img v-if="showPicture(item.page) && pictureposition === 'behind'"
|
||||
:src="pictureUrl" class="picture-behind" alt="" />
|
||||
<div class="content">
|
||||
<div v-if="showPicture(item.page) && pictureposition !== 'behind'" class="picturebox">
|
||||
<img :src="pictureUrl" class="picture-img" alt="" />
|
||||
</div>
|
||||
<div class="codegroup">
|
||||
<div v-if="layoutFor(item.page) !== 'textonly'" class="codebox">
|
||||
<img v-if="images[item.index]" :src="images[item.index]" class="code-img" alt="" />
|
||||
<!-- An empty code box is indistinguishable from a label that
|
||||
simply has no code on it, which is how a failed render
|
||||
reads as "the barcode does not appear". Say so, on screen
|
||||
only - the printed label must never carry this. -->
|
||||
<span v-else class="code-missing">no code</span>
|
||||
</div>
|
||||
<div v-if="layoutFor(item.page) !== 'codeonly' && item.page.label" class="label-text">
|
||||
{{ item.page.label }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span v-if="backenabled" class="side-flag">{{ item.page.side }}</span>
|
||||
</div>
|
||||
<p v-if="!pages.length" class="preview-empty no-print">
|
||||
Nothing to print yet - enter content above.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -732,6 +752,47 @@ watch([pages, codetype, backcodetype, errorcorrection], renderAll)
|
||||
|
||||
.code-generator { width: 100%; }
|
||||
|
||||
/* Settings on the left, the labels themselves on the right, where they stay in
|
||||
view while the settings that change them are being worked. The preview
|
||||
column scrolls on its own so a 500-label run cannot push the controls off
|
||||
the screen. */
|
||||
.workspace {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 3fr) minmax(300px, 2fr);
|
||||
gap: 20px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.preview {
|
||||
position: sticky;
|
||||
top: 12px;
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 12px 16px 16px;
|
||||
max-height: calc(100vh - var(--main-pad-top) - 24px);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.preview-head {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.preview-head .section-heading { margin-top: 0.25rem; }
|
||||
|
||||
.preview-count { font-size: 0.8125rem; color: var(--text-light); }
|
||||
|
||||
.preview-empty { color: var(--text-light); font-size: 0.8125rem; margin: 0; }
|
||||
|
||||
/* One column once the two would be too narrow to work in. */
|
||||
@media (max-width: 1200px) {
|
||||
.workspace { grid-template-columns: minmax(0, 1fr); }
|
||||
.preview { position: static; max-height: none; }
|
||||
}
|
||||
|
||||
.controls {
|
||||
background: var(--bg-card);
|
||||
color: var(--text);
|
||||
@@ -905,18 +966,35 @@ watch([pages, codetype, backcodetype, errorcorrection], renderAll)
|
||||
.label.type-barcode .codebox {
|
||||
flex: 1 1 auto;
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
/* Never below 40% of the label: past that a CODE128 is too few narrow bars
|
||||
to scan, so it is better for the caption to wrap or clip. */
|
||||
min-width: 40%;
|
||||
height: var(--tool-barh);
|
||||
}
|
||||
|
||||
.label.layout-stack.type-barcode .codebox { min-width: 100%; }
|
||||
|
||||
/* Screen-only, and deliberately loud: an empty box means the render failed. */
|
||||
.code-missing {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 6px;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.code-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* The caption takes what it needs and no more. Both boxes used to grow, so a
|
||||
caption of any length halved the barcode and then kept going - at 1x0.5in
|
||||
with a couple of words it left a 36px stub that reads as "the code did not
|
||||
render". A code that cannot be scanned is not a label. */
|
||||
.label-text {
|
||||
flex: 1 1 auto;
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
text-align: center;
|
||||
font-family: "Arial Narrow", Arial, Helvetica, sans-serif;
|
||||
@@ -943,7 +1021,20 @@ watch([pages, codetype, backcodetype, errorcorrection], renderAll)
|
||||
|
||||
@media print {
|
||||
.side-flag { display: none !important; }
|
||||
.code-missing { display: none !important; }
|
||||
.sheet { padding: 0; display: block; gap: 0; }
|
||||
/* The preview column is chrome. Its overflow matters most: a scrolling
|
||||
ancestor clips a multi-page print to whatever was on screen. */
|
||||
.workspace { display: block; }
|
||||
.preview {
|
||||
position: static;
|
||||
max-height: none;
|
||||
overflow: visible;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
padding: 0;
|
||||
background: none;
|
||||
}
|
||||
/* The back's dotted variant outspecifies a bare .label reset, so it printed
|
||||
a dashed rule down the edge of every second card. */
|
||||
.label,
|
||||
|
||||
Reference in New Issue
Block a user