stash
This commit is contained in:
parent
8d96bc97c4
commit
ed04d98bf1
54 changed files with 661 additions and 1214 deletions
|
|
@ -1,20 +1,9 @@
|
|||
// Each template's `layout` is a tree as described in label.js, with leaves whose `type` is one of
|
||||
// label.js's QR_LEAF_TYPES keys or "text", and whose `content` is a function from the resolved
|
||||
// field values (see label.js's buildLabelFields) to what they render - `null`/`undefined` from
|
||||
// that function means the field isn't available yet (see templateIsAvailable below). A template
|
||||
// is only selectable once every leaf's `content` resolves to a value.
|
||||
// A template's layout tree, content resolution, and selectability contract. See
|
||||
// docs/implementation.md#template-layout-tree.
|
||||
const GAP = {type: "empty", "min-width": "1mm", "min-height": "1mm"};
|
||||
|
||||
// The full "just the code" matrix: every {symbology, error-correction level, [rMQR] size
|
||||
// strategy} label.js's QR_LEAF_TYPES supports, one template each - a plain `id` (no suffix) is
|
||||
// always anyd's own defaults, ecc "M" and (rMQR only) size "balanced". Coverage isn't uniform
|
||||
// (see QR_LEAF_TYPES): full QR gets all four ecc grades L/M/Q/H; Micro QR swaps "L" (QR's actual
|
||||
// lowest) for the even-lower, M1-only, detection-only "Detection" and has no "H" at all; rMQR
|
||||
// only ever supports ecc "M" or "H", each of those crossed with all three size strategies
|
||||
// (balanced/min/max). Generated (rather than hand-writing every near-duplicate entry) so a
|
||||
// symbology/level/size this matrix is missing is one new row here, not a new block to keep in
|
||||
// sync with its neighbors. `id` doubles as the layout's leaf `type`, since that's exactly what
|
||||
// QR_LEAF_TYPES is keyed by.
|
||||
// Generated matrix of "just the code" templates covering every QR_LEAF_TYPES combo. See
|
||||
// docs/implementation.md#generated-qr-only-template-matrix.
|
||||
const QR_ONLY_TEMPLATES = [
|
||||
{
|
||||
id: "qr-l", name: "QR code only (low error correction)",
|
||||
|
|
@ -194,52 +183,40 @@ export const LABEL_TEMPLATES = [
|
|||
// Every field name any template's required_vars names, in first-seen order.
|
||||
export const KNOWN_VARS = [...new Set(LABEL_TEMPLATES.flatMap(t => t.required_vars))];
|
||||
|
||||
// A derived var is a format string calculated from other vars rather than typed directly - it
|
||||
// doesn't get its own input, just a read-only, live-recalculated display next to the ones that
|
||||
// do (see Print.vue and withDerivedVars below). `inputs` names every var (base or, in principle,
|
||||
// another derived one - see itemUrl/itemHandle below, which both read the derived userHandle)
|
||||
// `calc` reads - declared up front rather than inferred from calc's body so BASE_VARS below can
|
||||
// include a var like "webdomain" that only feeds a calculation and that no template ever
|
||||
// references directly. Declaration order matters here: withDerivedVars runs these in a single
|
||||
// pass, so a derived var must be declared after every other derived var it depends on.
|
||||
// DERIVED_VARS shape and declaration-order invariant. See
|
||||
// docs/implementation.md#derived-vars-shape-and-ordering.
|
||||
export const DERIVED_VARS = {
|
||||
// The full owner handle (see federation.md's Unique Handles section / ToolshedUser's
|
||||
// separate username/domain columns) - kept as two base vars (user, domain) rather than one,
|
||||
// since that's how the account itself is actually shaped, with this just the display/URL form.
|
||||
// Full user@domain handle, kept as separate user/domain base vars since that's how the
|
||||
// account is actually shaped (see federation.md's Unique Handles); this is just the
|
||||
// display/URL form.
|
||||
userHandle: {
|
||||
inputs: ["user", "domain"],
|
||||
calc: (f) => `${f.user}@${f.domain}`,
|
||||
},
|
||||
// The self-contained Item URL (see docs/design-in-progress/items-labels.md) - what a printed
|
||||
// label actually encodes, since scanning it has to resolve the right frontend/backend/item
|
||||
// with no other context, not just this browser's history. `webdomain` defaults to this
|
||||
// browser's own origin (see Print.vue) but is editable, since any frontend can resolve any
|
||||
// handle - the label doesn't have to point back at whichever frontend happened to print it.
|
||||
// Self-contained Item URL (see docs/design-in-progress/items-labels.md) - what a printed
|
||||
// label encodes, since scanning it must resolve everything with no other context. `webdomain`
|
||||
// defaults to this origin but is editable, since any frontend can resolve any handle.
|
||||
itemUrl: {
|
||||
inputs: ["webdomain", "userHandle", "itemId"],
|
||||
calc: (f) => `${f.webdomain}/i/${f.userHandle}/${f.itemId}`,
|
||||
},
|
||||
// The compact "owner handle + id" form from docs/design-in-progress/items-labels.md -
|
||||
// meaningful only where context already makes clear it's a Toolshed item, unlike itemUrl.
|
||||
// Compact "owner handle + id" form (see docs/design-in-progress/items-labels.md) - meaningful
|
||||
// only where context already makes clear it's a Toolshed item, unlike itemUrl.
|
||||
itemHandle: {
|
||||
inputs: ["userHandle", "itemId"],
|
||||
calc: (f) => `${f.userHandle}:${f.itemId}`,
|
||||
},
|
||||
};
|
||||
|
||||
// What Print.vue's content form offers a plain text input for: every KNOWN_VAR a template
|
||||
// references directly, minus the derived ones, plus every var a DERIVED_VARS calculation itself
|
||||
// needs (like "webdomain", which no template ever names). A template still lights up only once
|
||||
// every one of its own required_vars, base or derived, has a value (see templateIsAvailable
|
||||
// below).
|
||||
// Text-input vars = template KNOWN_VARS minus derived ones, plus any var a DERIVED_VARS calc
|
||||
// needs (e.g. "webdomain") even if no template names it directly.
|
||||
export const BASE_VARS = [...new Set([
|
||||
...KNOWN_VARS.filter(v => !(v in DERIVED_VARS)),
|
||||
...Object.values(DERIVED_VARS).flatMap(d => d.inputs).filter(v => !(v in DERIVED_VARS)),
|
||||
])];
|
||||
|
||||
// Runs every DERIVED_VARS calculation against `fields` (already holding the base vars - see
|
||||
// Print.vue), returning a copy with each one's result added wherever all of its own inputs are
|
||||
// present, so a caller never has to know DERIVED_VARS' internal {inputs, calc} shape.
|
||||
// Runs each DERIVED_VARS calc against `fields`, adding the result wherever its inputs are
|
||||
// present, so callers never need to know DERIVED_VARS' {inputs, calc} shape.
|
||||
export function withDerivedVars(fields) {
|
||||
const result = {...fields};
|
||||
for (const [name, {inputs, calc}] of Object.entries(DERIVED_VARS)) {
|
||||
|
|
@ -262,16 +239,14 @@ function mapTree(node, fn) {
|
|||
return Array.isArray(node) ? node.map(child => mapTree(child, fn)) : fn(node);
|
||||
}
|
||||
|
||||
// A content leaf's resolved value counts as present only if every part of it is - a single
|
||||
// string for a QR-family leaf (any label.js QR_LEAF_TYPES entry) or plain "text", every line for a
|
||||
// multi-line "text" (see LABEL_TEMPLATES' "owner-id-text" and "item-url-qr-owner-id").
|
||||
// Resolved only if every part is - a single string for a QR-family/"text" leaf, every line for a
|
||||
// multi-line "text" (see "owner-id-text" and "item-url-qr-owner-id" above).
|
||||
function isResolved(value) {
|
||||
return Array.isArray(value) ? value.every(isResolved) : value !== undefined && value !== null;
|
||||
}
|
||||
|
||||
// LabelLayoutPreview.vue's thumbnail grid and Print.vue's big preview both resolve a template
|
||||
// through these two functions rather than each re-implementing the leaf-walking/field-resolving
|
||||
// logic itself.
|
||||
// Shared by LabelLayoutPreview.vue's grid and Print.vue's preview, so neither reimplements
|
||||
// leaf-walking/field-resolving itself.
|
||||
export function templateIsAvailable(t, fields) {
|
||||
let available = true;
|
||||
walkLeaves(t.layout, leaf => {
|
||||
|
|
@ -282,9 +257,8 @@ export function templateIsAvailable(t, fields) {
|
|||
return available;
|
||||
}
|
||||
|
||||
/* Resolves a template's `content` functions against actual field values, turning its layout
|
||||
tree into one ready for label.js's drawLabel/drawFallbackLabel - or null if there's nothing to
|
||||
render yet (every leaf's value is still empty, e.g. before the user has typed anything). */
|
||||
// Resolves t's content leaves against fields into a tree for label.js's
|
||||
// drawLabel/drawFallbackLabel, or null if nothing to render yet.
|
||||
export function templateContent(t, fields) {
|
||||
const tree = mapTree(t.layout, leaf => leaf.type === "empty" ? leaf : {...leaf, value: leaf.content(fields)});
|
||||
let hasContent = false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue