Files
shopdb-flask/plugins/tools/frontend/labelVars.js

81 lines
3.5 KiB
JavaScript

// 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),
}
}