// The label's measurements, turned into the CSS custom properties the preview // and @page read. // // This exists because of one failure mode. A number input that is cleared does // not hold 0, it holds an empty string, and `'' + 'in'` is not a length. The // browser drops the whole declaration, the box falls back to `height: auto`, // and an image sized `height: 100%` of an auto-height parent computes to ZERO. // The label then renders 176x0: full width, no height, no barcode - which // reads as "the code stopped rendering" and not as "a field is empty". // // So nothing here may emit NaN, an empty string, or a negative length. Every // value falls back to something printable, and the fallbacks are the defaults // the page ships with. export const LABEL_VAR_NAMES = [ '--tool-label-w', '--tool-label-h', '--tool-pad', '--tool-code', '--tool-barh', '--tool-quiet', '--tool-gap', '--tool-font', '--tool-align', '--tool-valign', '--tool-content-x', '--tool-content-y', '--tool-nudge-x', '--tool-nudge-y', '--tool-pic', '--tool-pic-opacity', ] export const VAR_FALLBACKS = { labelwidth: 1, labelheight: 0.5, padding: 0, codesize: 0.4286, barheight: 0.3, quiet: 0.035, gap: 0.03, labelfont: 7, picturesize: 0.5, pictureopacity: 0.15, } /** A length that must be greater than zero for the box to exist at all. */ function positive(value, fallback) { const number = Number(value) return Number.isFinite(number) && number > 0 ? number : fallback } /** A length that may legitimately be zero - padding, a quiet zone, a gap. */ function nonNegative(value, fallback) { const number = Number(value) return Number.isFinite(number) && number >= 0 ? number : fallback } /** An offset, which is signed: -0.05in is a real answer. */ function offset(value) { const number = Number(value) return Number.isFinite(number) ? number : 0 } const ALIGNMENTS = ['flex-start', 'center', 'flex-end'] function alignment(value) { return ALIGNMENTS.includes(value) ? value : 'center' } /** * Map the generator's settings to CSS custom properties, in inches. * * Returns a plain object of property name to value, ready to hand to * `style.setProperty`. Every entry is a valid CSS value whatever it was given. */ export function labelVars(state = {}) { const inches = value => value + 'in' const opacity = Number(state.pictureopacity) return { '--tool-label-w': inches(positive(state.labelwidth, VAR_FALLBACKS.labelwidth)), '--tool-label-h': inches(positive(state.labelheight, VAR_FALLBACKS.labelheight)), '--tool-pad': inches(nonNegative(state.padding, VAR_FALLBACKS.padding)), '--tool-code': inches(positive(state.codesize, VAR_FALLBACKS.codesize)), '--tool-barh': inches(positive(state.barheight, VAR_FALLBACKS.barheight)), '--tool-quiet': inches(nonNegative(state.quiet, VAR_FALLBACKS.quiet)), '--tool-gap': inches(nonNegative(state.gap, VAR_FALLBACKS.gap)), '--tool-font': positive(state.labelfont, VAR_FALLBACKS.labelfont) + 'pt', '--tool-align': alignment(state.align), '--tool-valign': alignment(state.valign), '--tool-content-x': inches(offset(state.contentx)), '--tool-content-y': inches(offset(state.contenty)), '--tool-nudge-x': inches(offset(state.nudgex)), '--tool-nudge-y': inches(offset(state.nudgey)), '--tool-pic': inches(positive(state.picturesize, VAR_FALLBACKS.picturesize)), '--tool-pic-opacity': String( Number.isFinite(opacity) && opacity > 0 && opacity <= 1 ? opacity : VAR_FALLBACKS.pictureopacity), } }