stash
This commit is contained in:
parent
9c9f0f2d9b
commit
6a9ed35bcd
11 changed files with 166 additions and 186 deletions
|
|
@ -4,6 +4,23 @@ import {encodeHandleForUrl} from "@/router";
|
|||
// docs/implementation.md#template-layout-tree.
|
||||
const GAP = {type: "empty", "min-width": "1mm", "min-height": "1mm"};
|
||||
|
||||
// Single registry for everything that varies per content kind. See docs/implementation.md#content-kinds-registry.
|
||||
export const CONTENT_KINDS = [
|
||||
{
|
||||
id: "item", label: "Item", typedPrefix: "i",
|
||||
shortIdKind: "item", groupShortIdKind: "group_item", localIdField: "item_local_id",
|
||||
buildUrl: ({origin, userHandle, id}) => `${origin}/i/${encodeHandleForUrl(userHandle)}/${id}`,
|
||||
},
|
||||
{
|
||||
id: "storage-location", label: "Storage location", typedPrefix: "s",
|
||||
shortIdKind: "storage_location", groupShortIdKind: "group_storage_location",
|
||||
localIdField: "storage_location_id",
|
||||
buildUrl: ({origin, userHandle, id}) => `${origin}/s/${encodeHandleForUrl(userHandle)}/${id}`,
|
||||
},
|
||||
];
|
||||
|
||||
export const CONTENT_KINDS_BY_ID = Object.fromEntries(CONTENT_KINDS.map(k => [k.id, k]));
|
||||
|
||||
|
||||
// QR-types
|
||||
// qr-l, qr, qr-q, qr-h, mqr-d, mqr-l, mqr, mqr-q, rmqr, rmqr-min, rmqr-max, rmqr-h, rmqr-h-min, rmqr-h-max
|
||||
|
|
@ -22,53 +39,45 @@ export const LABEL_TEMPLATES = [
|
|||
layout: [{type: "qr-h", content: c => c.shortUrl}]
|
||||
},
|
||||
{
|
||||
id: "rmqr-url", name: "rMQR Token + Item ID",
|
||||
description: "The code encoding the short link, with the item id printed alongside for a human to read.",
|
||||
required_vars: ["shortUrl", "itemId"], tags: ["external"],
|
||||
id: "rmqr-url", name: "rMQR Token + ID",
|
||||
description: "The code encoding the short link, with the id printed alongside for a human to read.",
|
||||
required_vars: ["shortUrl", "id"], tags: ["external"],
|
||||
layout: [{type: "rmqr", content: c => c.shortUrl}, {
|
||||
type: "text",
|
||||
content: c => c.itemId?.toString().padStart(4, "0")
|
||||
content: c => c.id?.toString().padStart(4, "0")
|
||||
}]
|
||||
},
|
||||
{
|
||||
id: "rmqr-url-url", name: "rMQR Token + Item ID + URL",
|
||||
description: "The code encoding the short link, with the item id printed alongside for a human to read.",
|
||||
required_vars: ["shortUrl", "itemId"], tags: ["external"],
|
||||
id: "rmqr-url-url", name: "rMQR Token + ID + URL",
|
||||
description: "The code encoding the short link, with the id printed alongside for a human to read.",
|
||||
required_vars: ["shortUrl", "id", "typedPrefix", "url"], tags: ["external"],
|
||||
layout: [[[{type: "rmqr", content: c => c.shortUrl}, {
|
||||
type: "text",
|
||||
content: c => "I"+c.itemId?.toString().padStart(4, "0")
|
||||
}],{type: "text", content: c => c.itemUrl}]]
|
||||
content: c => c.typedPrefix?.toUpperCase() + c.id?.toString().padStart(4, "0")
|
||||
}],{type: "text", content: c => c.url}]]
|
||||
},
|
||||
{
|
||||
id: "mqr-token-id", name: "MQR Token + Item ID",
|
||||
description: "The code encoding the internal short id, with the item id printed alongside for a human to read.",
|
||||
required_vars: ["shortId", "itemId"], tags: ["internal"],
|
||||
id: "mqr-token-id", name: "MQR Token + ID",
|
||||
description: "The code encoding the internal short id, with the id printed alongside for a human to read.",
|
||||
required_vars: ["shortId", "id"], tags: ["internal"],
|
||||
layout: [{type: "mqr", content: c => c.shortId}, {
|
||||
type: "text",
|
||||
content: c => c.itemId?.toString().padStart(4, "0")
|
||||
content: c => c.id?.toString().padStart(4, "0")
|
||||
}]
|
||||
},
|
||||
{
|
||||
id: "location-rmqr-url", name: "rMQR Token + Location ID",
|
||||
description: "The code encoding the short link, with the storage location id printed alongside for a "
|
||||
+ "human to read.",
|
||||
required_vars: ["shortUrl", "locationId"], tags: ["external"],
|
||||
layout: [{type: "rmqr", content: c => c.shortUrl}, {
|
||||
type: "text",
|
||||
content: c => c.locationId?.toString().padStart(4, "0")
|
||||
}]
|
||||
id: "domain-short-id-qr", name: "Any-Instance Token (QR)",
|
||||
description: "The code encoding the domain-qualified short id (domain:~token) - resolvable "
|
||||
+ "from any Toolshed instance's own scanner, not just this one, with no full URL needed.",
|
||||
required_vars: ["domainShortId"], tags: ["cross-instance"],
|
||||
layout: [{type: "qr", content: c => c.domainShortId}]
|
||||
},
|
||||
{
|
||||
id: "location-mqr-token-id",
|
||||
name: "MQR Token + Location ID",
|
||||
description: "The code encoding the internal short id, with the storage location id printed alongside "
|
||||
+ "for a human to read.",
|
||||
required_vars: ["shortId", "locationId"],
|
||||
tags: ["internal"],
|
||||
layout: [{type: "mqr", content: c => c.shortId}, {
|
||||
type: "text",
|
||||
content: c => c.locationId?.toString().padStart(4, "0")
|
||||
}]
|
||||
id: "domain-short-id-text", name: "Any-Instance Token",
|
||||
description: "Just the domain-qualified short id (domain:~token) as text, not a URL - meant "
|
||||
+ "to be typed or scanned into any Toolshed instance's own resolver.",
|
||||
required_vars: ["domainShortId"], tags: ["cross-instance"],
|
||||
layout: [{type: "text", content: c => c.domainShortId}]
|
||||
},
|
||||
|
||||
{
|
||||
|
|
@ -85,23 +94,18 @@ export const LABEL_TEMPLATES = [
|
|||
{
|
||||
id: "id-qr-text-vertical", name: "ID + QR code + text below",
|
||||
description: "The code with the encoded text printed below it.",
|
||||
required_vars: ["itemId", "text", "userHandle"],
|
||||
layout: [[{type: "text", content: c => "Item: " + c.itemId}, {
|
||||
required_vars: ["kind", "id", "text", "userHandle"],
|
||||
layout: [[{type: "text", content: c => c.kind + ": " + c.id}, {
|
||||
type: "qr",
|
||||
content: c => c.text
|
||||
}, {type: "text", content: c => c.userHandle}]]
|
||||
},
|
||||
{
|
||||
id: "item-handle", name: "Item handle",
|
||||
description: "The compact owner@domain:i<id> User-Qualified ID - meaningful in-app, not scannable on its own.",
|
||||
required_vars: ["itemHandle"], tags: ["internal"],
|
||||
layout: [{type: "text", content: c => c.itemHandle}]
|
||||
},
|
||||
{
|
||||
id: "location-handle", name: "Storage location handle",
|
||||
description: "The compact owner@domain:s<id> User-Qualified ID - meaningful in-app, not scannable on its own.",
|
||||
required_vars: ["locationHandle"], tags: ["internal"],
|
||||
layout: [{type: "text", content: c => c.locationHandle}]
|
||||
id: "qualified-handle", name: "Qualified handle",
|
||||
description: "The compact owner@domain:i<id> / owner@domain:s<id> User-Qualified ID - meaningful "
|
||||
+ "in-app, not scannable on its own.",
|
||||
required_vars: ["qualifiedHandle"], tags: ["internal"],
|
||||
layout: [{type: "text", content: c => c.qualifiedHandle}]
|
||||
},
|
||||
{
|
||||
id: "owner-handle", name: "Owner handle", description: "Just the owning user's handle, as text only.",
|
||||
|
|
@ -109,56 +113,45 @@ export const LABEL_TEMPLATES = [
|
|||
layout: [{type: "text", content: c => c.userHandle}]
|
||||
},
|
||||
{
|
||||
id: "item-id", name: "Item ID", description: "Just the bare item id, as text only.",
|
||||
required_vars: ["itemId"], tags: ["internal"],
|
||||
layout: [{type: "text", content: c => c.itemId}]
|
||||
id: "id", name: "ID", description: "Just the bare id, as text only.",
|
||||
required_vars: ["id"], tags: ["internal"],
|
||||
layout: [{type: "text", content: c => c.id}]
|
||||
},
|
||||
{
|
||||
id: "owner-id-text-multiline", name: "Owner + item ID (same size)",
|
||||
description: "The owner's handle and the item id, as two lines of text - no code.",
|
||||
required_vars: ["userHandle", "itemId"], tags: ["internal"],
|
||||
layout: [{type: "text", content: c => [c.userHandle, c.itemId]}]
|
||||
id: "owner-id-text-multiline", name: "Owner + ID (same size)",
|
||||
description: "The owner's handle and the id, as two lines of text - no code.",
|
||||
required_vars: ["userHandle", "id"], tags: ["internal"],
|
||||
layout: [{type: "text", content: c => [c.userHandle, c.id]}]
|
||||
},
|
||||
{
|
||||
id: "owner-id-text", name: "Owner + item ID",
|
||||
description: "The owner's handle and the item id, as two lines of text - no code.",
|
||||
required_vars: ["userHandle", "itemId"], tags: ["internal"],
|
||||
layout: [[{type: "text", content: c => [c.userHandle]},{type: "text", content: c => [c.itemId]}]]
|
||||
id: "owner-id-text", name: "Owner + ID",
|
||||
description: "The owner's handle and the id, as two lines of text - no code.",
|
||||
required_vars: ["userHandle", "id"], tags: ["internal"],
|
||||
layout: [[{type: "text", content: c => [c.userHandle]},{type: "text", content: c => [c.id]}]]
|
||||
},
|
||||
{
|
||||
id: "location-id", name: "Location ID", description: "Just the bare storage location id, as text only.",
|
||||
required_vars: ["locationId"], tags: ["internal"],
|
||||
layout: [{type: "text", content: c => c.locationId}]
|
||||
id: "url-qr-handle", name: "URL + handle",
|
||||
description: "Scannable URL, with the compact handle printed alongside.",
|
||||
required_vars: ["url", "qualifiedHandle"], tags: ["external"],
|
||||
layout: [{type: "qr", content: c => c.url}, {type: "text", content: c => c.qualifiedHandle}]
|
||||
},
|
||||
{
|
||||
id: "owner-id-text-location", name: "Owner + location ID",
|
||||
description: "The owner's handle and the location id, as two lines of text - no code.",
|
||||
required_vars: ["userHandle", "locationId"], tags: ["internal"],
|
||||
layout: [{type: "text", content: c => [c.userHandle, c.locationId]}]
|
||||
id: "url-qr-owner", name: "URL + owner",
|
||||
description: "Scannable URL, with the owner's handle printed alongside.",
|
||||
required_vars: ["url", "userHandle"], tags: ["external"],
|
||||
layout: [{type: "qr", content: c => c.url}, {type: "text", content: c => c.userHandle}]
|
||||
},
|
||||
{
|
||||
id: "item-url-qr-handle", name: "Item URL + handle",
|
||||
description: "Scannable item URL, with the item's compact handle printed alongside.",
|
||||
required_vars: ["itemUrl", "itemHandle"], tags: ["external"],
|
||||
layout: [{type: "qr", content: c => c.itemUrl}, {type: "text", content: c => c.itemHandle}]
|
||||
id: "url-qr-id", name: "URL + ID",
|
||||
description: "Scannable URL, with the bare id printed alongside.",
|
||||
required_vars: ["url", "id"], tags: ["external"],
|
||||
layout: [{type: "qr", content: c => c.url}, {type: "text", content: c => c.id}]
|
||||
},
|
||||
{
|
||||
id: "item-url-qr-owner", name: "Item URL + owner",
|
||||
description: "Scannable item URL, with the owner's handle printed alongside.",
|
||||
required_vars: ["itemUrl", "userHandle"], tags: ["external"],
|
||||
layout: [{type: "qr", content: c => c.itemUrl}, {type: "text", content: c => c.userHandle}]
|
||||
},
|
||||
{
|
||||
id: "item-url-qr-id", name: "Item URL + item ID",
|
||||
description: "Scannable item URL, with the bare item id printed alongside.",
|
||||
required_vars: ["itemUrl", "itemId"], tags: ["external"],
|
||||
layout: [{type: "qr", content: c => c.itemUrl}, {type: "text", content: c => c.itemId}]
|
||||
},
|
||||
{
|
||||
id: "item-url-qr-owner-id", name: "Item URL + owner + ID",
|
||||
description: "Scannable item URL, with the owner's handle and the item id on two lines alongside.",
|
||||
required_vars: ["itemUrl", "userHandle", "itemId"], tags: ["external"],
|
||||
layout: [{type: "qr", content: c => c.itemUrl}, {type: "text", content: c => [c.userHandle, c.itemId]}]
|
||||
id: "url-qr-owner-id", name: "URL + owner + ID",
|
||||
description: "Scannable URL, with the owner's handle and the id on two lines alongside.",
|
||||
required_vars: ["url", "userHandle", "id"], tags: ["external"],
|
||||
layout: [{type: "qr", content: c => c.url}, {type: "text", content: c => [c.userHandle, c.id]}]
|
||||
},
|
||||
{
|
||||
id: "short-url-qr", name: "Short link (QR code)",
|
||||
|
|
@ -168,13 +161,13 @@ export const LABEL_TEMPLATES = [
|
|||
layout: [{type: "qr", content: c => c.shortUrl}]
|
||||
},
|
||||
{
|
||||
id: "item-url-qr-owner-id2", name: "Item URL + owner + ID",
|
||||
description: "Scannable item URL, with the owner's handle and the item id on two lines alongside.",
|
||||
required_vars: ["itemUrl", "userHandle", "itemId"], tags: ["external"],
|
||||
layout: [{type: "qr", content: c => c.itemUrl}, [{
|
||||
id: "url-qr-owner-id2", name: "URL + owner + ID",
|
||||
description: "Scannable URL, with the owner's handle and the id on two lines alongside.",
|
||||
required_vars: ["url", "userHandle", "id"], tags: ["external"],
|
||||
layout: [{type: "qr", content: c => c.url}, [{
|
||||
type: "text",
|
||||
content: c => c.userHandle
|
||||
}, {type: "text", content: c => c.itemId}]]
|
||||
}, {type: "text", content: c => c.id}]]
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -187,17 +180,19 @@ export const DERIVED_VARS = {
|
|||
inputs: ["user", "domain"],
|
||||
calc: (f) => `${f.user}@${f.domain}`,
|
||||
},
|
||||
itemUrl: {
|
||||
inputs: ["webdomain", "userHandle", "itemId"],
|
||||
calc: (f) => `${f.webdomain}/i/${encodeHandleForUrl(f.userHandle)}/${f.itemId}`,
|
||||
// handles-and-shortids.md's kind letter (i/s) for f.kind - must stay declared before qualifiedHandle, which depends on it.
|
||||
typedPrefix: {
|
||||
inputs: ["kind"],
|
||||
calc: (f) => CONTENT_KINDS_BY_ID[f.kind]?.typedPrefix,
|
||||
},
|
||||
itemHandle: {
|
||||
inputs: ["userHandle", "itemId"],
|
||||
calc: (f) => `${f.userHandle}:i${f.itemId}`,
|
||||
qualifiedHandle: {
|
||||
inputs: ["userHandle", "typedPrefix", "id"],
|
||||
calc: (f) => `${f.userHandle}:${f.typedPrefix}${f.id}`,
|
||||
},
|
||||
locationHandle: {
|
||||
inputs: ["userHandle", "locationId"],
|
||||
calc: (f) => `${f.userHandle}:s${f.locationId}`,
|
||||
// Undefined for a kind whose CONTENT_KINDS entry has no buildUrl - same "unresolved" signal templateIsAvailable already treats every other missing var as.
|
||||
url: {
|
||||
inputs: ["webdomain", "userHandle", "kind", "id"],
|
||||
calc: (f) => CONTENT_KINDS_BY_ID[f.kind]?.buildUrl?.({origin: f.webdomain, userHandle: f.userHandle, id: f.id}),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import {loadAnyDCode} from "../vendor/anyd-qr.js";
|
||||
import {encodeHandleForUrl} from "@/router"
|
||||
import {drawPixelText, preloadPixelFontRenderer} from "@/pixel-font.js";
|
||||
import {DERIVED_VARS} from "@/label-layouts.js";
|
||||
import {DERIVED_VARS, CONTENT_KINDS_BY_ID} from "@/label-layouts.js";
|
||||
|
||||
// Re-exported so every caller can preload both the QR encoder and the bitmap-font rasterizer
|
||||
// (see fontTierFor's `pixel` tiers below) the same way, alongside this file's own preloadQrEncoder.
|
||||
|
|
@ -568,28 +567,17 @@ export function drawFallbackLabel(canvas, content, orientation = "along") {
|
|||
return {textSizesPx, warnings, qrInfo, textInfo};
|
||||
}
|
||||
|
||||
// Turns a {kind, components} prefill (see Print.vue's `prefill` prop) into the literal string a
|
||||
// print label should show/encode; keyed by `kind` so each kind's format is defined in one place.
|
||||
export const LABEL_CONTENT_BUILDERS = {
|
||||
// The self-contained Item URL (see docs/design-in-progress/items-labels.md), built from just
|
||||
// the prefill's {userHandle, item}; the short link (Print.vue's `shortUrl`) needs an async store
|
||||
// lookup, so it stays a separate field/template rather than being baked in here.
|
||||
"item": ({userHandle, item}) => `${window.location.origin}/i/${encodeHandleForUrl(userHandle)}/${item}`,
|
||||
// Storage locations have no long-form URL route yet (see router.js), so `text` starts blank;
|
||||
// the short link and any future location template still work via the base vars below.
|
||||
};
|
||||
|
||||
// Turns a {kind, components} prefill (see Print.vue's `prefill` prop) into the literal string a print label should show/encode, via that kind's CONTENT_KINDS_BY_ID entry.
|
||||
export function buildLabelContent(prefill) {
|
||||
if (!prefill) {
|
||||
const entry = prefill && CONTENT_KINDS_BY_ID[prefill.kind];
|
||||
const {userHandle, id} = prefill?.components ?? {};
|
||||
if (!entry?.buildUrl || !userHandle || !id) {
|
||||
return "";
|
||||
}
|
||||
const build = LABEL_CONTENT_BUILDERS[prefill.kind];
|
||||
return build ? build(prefill.components) : "";
|
||||
return entry.buildUrl({origin: window.location.origin, userHandle, id});
|
||||
}
|
||||
|
||||
// Splits a prefill's {userHandle, item/location} into label-layouts.js's user/domain base vars (the same way
|
||||
// store.js's lookupServer does), tagging on whichever id field the resource's templates key
|
||||
// required_vars by.
|
||||
// Splits a prefill's {userHandle, id} into label-layouts.js's user/domain base vars (the same way store.js's lookupServer does).
|
||||
function splitUserHandle(userHandle) {
|
||||
if (!userHandle) {
|
||||
return null;
|
||||
|
|
@ -601,32 +589,17 @@ function splitUserHandle(userHandle) {
|
|||
};
|
||||
}
|
||||
|
||||
// Seeds label-layouts.js's BASE_VARS, keyed by `kind`; derived vars (userHandle, itemUrl, …) are
|
||||
// computed live elsewhere (see DERIVED_VARS, Print.vue's `shortUrl`), and omitting a field
|
||||
// (rather than leaving it present-but-empty) signals "not available" to contentIsAvailable.
|
||||
const LABEL_FIELD_BUILDERS = {
|
||||
"item": ({userHandle, item}) => {
|
||||
const split = splitUserHandle(userHandle);
|
||||
if (!split || !item) {
|
||||
return {};
|
||||
}
|
||||
return {...split, itemId: String(item)};
|
||||
},
|
||||
"storage-location": ({userHandle, location}) => {
|
||||
const split = splitUserHandle(userHandle);
|
||||
if (!split || !location) {
|
||||
return {};
|
||||
}
|
||||
return {...split, locationId: String(location)};
|
||||
},
|
||||
};
|
||||
|
||||
// Seeds label-layouts.js's BASE_VARS from a {kind, components: {userHandle, id}} prefill - one generic function suffices now that every kind's prefill shares this shape (see CONTENT_KINDS_BY_ID).
|
||||
export function buildLabelFields(prefill) {
|
||||
if (!prefill) {
|
||||
if (!prefill || !(prefill.kind in CONTENT_KINDS_BY_ID)) {
|
||||
return {};
|
||||
}
|
||||
const build = LABEL_FIELD_BUILDERS[prefill.kind];
|
||||
return build ? build(prefill.components) : {};
|
||||
const {userHandle, id} = prefill.components ?? {};
|
||||
const split = splitUserHandle(userHandle);
|
||||
if (!split || !id) {
|
||||
return {};
|
||||
}
|
||||
return {...split, kind: prefill.kind, id: String(id)};
|
||||
}
|
||||
|
||||
// Runs each DERIVED_VARS calc against `fields`, adding the result wherever its inputs are
|
||||
|
|
|
|||
|
|
@ -251,6 +251,10 @@ const routes = [{path: '/', component: Dashboard, meta: {requiresAuth: true}}, {
|
|||
component: StorageLocationEdit,
|
||||
meta: {requiresAuth: true},
|
||||
props: true
|
||||
}, {
|
||||
// Label/short-link entry point for storage locations, the counterpart of /i/:handle/:id. See docs/implementation.md#item-short-link-redirect-route.
|
||||
path: '/s/:handle/:id',
|
||||
redirect: to => `/storage-locations/${to.params.handle}/${to.params.id}`
|
||||
}, {
|
||||
// See docs/implementation.md#owner-filtered-overview-routes-use-path-segments-not-query-strings.
|
||||
path: '/storage-locations/new/:group?',
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ export default {
|
|||
printLinkFor(item) {
|
||||
const userHandle = item.owner || item.owner_group
|
||||
if (!userHandle) return null
|
||||
return {path: '/print', query: {kind: 'item', userHandle, item: item.id}}
|
||||
return {path: '/print', query: {kind: 'item', userHandle, id: item.id}}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@
|
|||
Delete
|
||||
</button>
|
||||
<button v-if="canPrint" class="btn btn-secondary"
|
||||
@click="$router.push({path: '/print', query: {kind: 'item', userHandle: decodedHandle, item: id}})">
|
||||
@click="$router.push({path: '/print', query: {kind: 'item', userHandle: decodedHandle, id: id}})">
|
||||
<b-icon-printer></b-icon-printer>
|
||||
Print label
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -153,6 +153,13 @@
|
|||
<textarea v-if="v === 'text'" class="form-control" rows="3"
|
||||
v-model="varValues[v]"
|
||||
placeholder="https://example.com/…"></textarea>
|
||||
<select v-else-if="v === 'kind'" class="form-control"
|
||||
v-model="varValues[v]">
|
||||
<option value="">–</option>
|
||||
<option v-for="k in contentKinds" :key="k.id" :value="k.id">{{
|
||||
k.label
|
||||
}}</option>
|
||||
</select>
|
||||
<input v-else type="text" class="form-control" v-model="varValues[v]">
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -201,14 +208,15 @@ import {
|
|||
withDerivedVars,
|
||||
templateContent
|
||||
} from "@/label.js";
|
||||
import {LABEL_TEMPLATES, BASE_VARS, DERIVED_VARS} from "@/label-layouts.js";
|
||||
import {LABEL_TEMPLATES, BASE_VARS, DERIVED_VARS, CONTENT_KINDS, CONTENT_KINDS_BY_ID} from "@/label-layouts.js";
|
||||
import {shortenedRoute} from "@/router";
|
||||
|
||||
// Print.vue-local calculated fields on top of label-layouts.js's DERIVED_VARS. See
|
||||
// docs/implementation.md#calculated-short-link-fields.
|
||||
// Print.vue-local calculated fields on top of label-layouts.js's DERIVED_VARS. See docs/implementation.md#calculated-short-link-fields.
|
||||
const SHORT_URL_VAR = "shortUrl";
|
||||
// See docs/implementation.md#calculated-short-link-fields.
|
||||
const SHORT_ID_VAR = "shortId";
|
||||
// See docs/implementation.md#calculated-short-link-fields.
|
||||
const DOMAIN_SHORT_ID_VAR = "domainShortId";
|
||||
|
||||
// Served unbundled so its wasm sibling stays resolvable. See docs/implementation.md#libweblabel-served-unbundled.
|
||||
const BLOB_URL = "/vendor/libweblabel.js";
|
||||
|
|
@ -275,7 +283,7 @@ export default {
|
|||
// Scan-reliability messages from label.js's drawLabel (too few px per QR module, or too small in mm) - the label still rendered/prints fine, just flagged as a risk.
|
||||
warnings: [],
|
||||
|
||||
// One input per BASE_VARS entry; derived vars (userHandle/itemUrl/itemHandle) are calculated-only (see the `fields` computed), never stored here. Prefilled from query params but left editable. queryFields is applied last so an explicit ?text=... etc. always wins over a `kind` builder's computed value.
|
||||
// One input per BASE_VARS entry; derived vars (userHandle/url/qualifiedHandle) are calculated-only (see the `fields` computed), never stored here. Prefilled from query params but left editable. queryFields is applied last so an explicit ?text=... etc. always wins over buildLabelFields' computed value.
|
||||
varValues: {
|
||||
...Object.fromEntries(BASE_VARS.map(v => [v, ""])),
|
||||
text: buildLabelContent(this.prefill),
|
||||
|
|
@ -308,10 +316,13 @@ export default {
|
|||
return at === -1 ? null : this.user.slice(at + 1);
|
||||
},
|
||||
baseVars() {
|
||||
return BASE_VARS.filter(v => v !== SHORT_URL_VAR && v !== SHORT_ID_VAR);
|
||||
return BASE_VARS.filter(v => v !== SHORT_URL_VAR && v !== SHORT_ID_VAR && v !== DOMAIN_SHORT_ID_VAR);
|
||||
},
|
||||
contentKinds() {
|
||||
return CONTENT_KINDS;
|
||||
},
|
||||
derivedVars() {
|
||||
return [...Object.keys(DERIVED_VARS), SHORT_ID_VAR, SHORT_URL_VAR];
|
||||
return [...Object.keys(DERIVED_VARS), SHORT_ID_VAR, DOMAIN_SHORT_ID_VAR, SHORT_URL_VAR];
|
||||
},
|
||||
// Named content fields the templates draw from, dropping blank values. See docs/implementation.md#fields-computed-dropping-blank-values.
|
||||
fields() {
|
||||
|
|
@ -325,9 +336,12 @@ export default {
|
|||
const shortId = this.shortId(derived);
|
||||
if (shortId) {
|
||||
derived[SHORT_ID_VAR] = shortId;
|
||||
// Bare, same Home-Instance trust model as shortId - webdomain only picks a frontend, not a backend. See docs/implementation.md#calculated-short-link-fields.
|
||||
if (derived.webdomain) {
|
||||
derived[SHORT_URL_VAR] = derived.webdomain + "/"
|
||||
+ (this.homeDomain ? this.homeDomain + ":" + shortId : shortId);
|
||||
derived[SHORT_URL_VAR] = derived.webdomain + "/" + shortId;
|
||||
}
|
||||
if (this.homeDomain) {
|
||||
derived[DOMAIN_SHORT_ID_VAR] = this.homeDomain + ":" + shortId;
|
||||
}
|
||||
}
|
||||
return derived;
|
||||
|
|
@ -447,12 +461,14 @@ export default {
|
|||
methods: {
|
||||
...mapActions(["fetchIdMap"]),
|
||||
|
||||
// camelCase -> Title Case (e.g. "itemHandle" -> "Item Handle") so a new template var needs no hand-written label.
|
||||
// camelCase -> Title Case (e.g. "qualifiedHandle" -> "Qualified Handle") so a new template var needs no hand-written label.
|
||||
varLabel(v) {
|
||||
return v.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/^./, c => c.toUpperCase());
|
||||
},
|
||||
// Which short-id.js kind applies depends on f.kind directly. See docs/implementation.md#content-kinds-registry.
|
||||
shortId(f) {
|
||||
if (!f.userHandle) {
|
||||
const entry = f.kind && CONTENT_KINDS_BY_ID[f.kind];
|
||||
if (!f.userHandle || !entry || !f.id) {
|
||||
return null;
|
||||
}
|
||||
if (f.userHandle.startsWith("#")) {
|
||||
|
|
@ -460,29 +476,17 @@ export default {
|
|||
if (owner_group_id === undefined) {
|
||||
return null;
|
||||
}
|
||||
if (f.itemId) {
|
||||
return shortenedRoute({kind: "group_item", owner_group_id, item_local_id: f.itemId}).slice(1);
|
||||
}
|
||||
if (f.locationId) {
|
||||
return shortenedRoute({
|
||||
kind: "group_storage_location", owner_group_id, storage_location_id: f.locationId
|
||||
}).slice(1);
|
||||
}
|
||||
return null;
|
||||
return shortenedRoute({
|
||||
kind: entry.groupShortIdKind, owner_group_id, [entry.localIdField]: f.id
|
||||
}).slice(1);
|
||||
}
|
||||
const owner_identity_id = this.identityIdByHandle[f.userHandle];
|
||||
if (owner_identity_id === undefined) {
|
||||
return null;
|
||||
}
|
||||
if (f.itemId) {
|
||||
return shortenedRoute({kind: "item", owner_identity_id, item_local_id: f.itemId}).slice(1);
|
||||
}
|
||||
if (f.locationId) {
|
||||
return shortenedRoute({
|
||||
kind: "storage_location", owner_identity_id, storage_location_id: f.locationId
|
||||
}).slice(1);
|
||||
}
|
||||
return null;
|
||||
return shortenedRoute({
|
||||
kind: entry.shortIdKind, owner_identity_id, [entry.localIdField]: f.id
|
||||
}).slice(1);
|
||||
},
|
||||
|
||||
// Ticks 0..totalMm via tapePxPerMm, shared by both ruler computeds; spacing comes from the shared rulerTier, so both rulers coarsen together.
|
||||
|
|
|
|||
|
|
@ -200,10 +200,10 @@ export default {
|
|||
return shortenedRoute({kind: 'storage_location', owner_identity_id, storage_location_id: location.id})
|
||||
},
|
||||
// Routes to Print.vue with this location's raw identity, same shape as Inventory.vue's
|
||||
// printLinkFor. See docs/implementation.md#print-link-shape-for-storage-locations.
|
||||
// printLinkFor. See docs/implementation.md#print-link-shape.
|
||||
printLinkFor(location) {
|
||||
const userHandle = location.owner_group || location.owner
|
||||
return {path: '/print', query: {kind: 'storage-location', userHandle, location: location.id}}
|
||||
return {path: '/print', query: {kind: 'storage-location', userHandle, id: location.id}}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
Delete
|
||||
</button>
|
||||
<button class="btn btn-secondary"
|
||||
@click="$router.push({path: '/print', query: {kind: 'storage-location', userHandle: decodedHandle, location: id}})">
|
||||
@click="$router.push({path: '/print', query: {kind: 'storage-location', userHandle: decodedHandle, id: id}})">
|
||||
<b-icon-printer></b-icon-printer>
|
||||
Print label
|
||||
</button>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue