feat(badges): configurable header image vertical offset per template
Adds cfg_json.header_margin_top to BadgeTemplateCfg. Badge view replaces hardcoded mt-8 (2rem) with this value; falls back to 2rem when unset. Template form exposes the field in the Header & Branding section. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -36,6 +36,13 @@ export interface BadgeTemplateCfg {
|
|||||||
// Leave unset (or "0") for no bleed.
|
// Leave unset (or "0") for no bleed.
|
||||||
bleed?: string;
|
bleed?: string;
|
||||||
|
|
||||||
|
// Header image vertical offset. CSS length applied as margin-top on the badge_header div.
|
||||||
|
// Default (unset) = "2rem" (matches the prior hardcoded mt-8).
|
||||||
|
// Negative values shift the image toward the top edge; larger values push it down.
|
||||||
|
// Useful when a background image's designed zone doesn't align with the default position.
|
||||||
|
// Any CSS length works: "-0.5in", "1rem", "8px".
|
||||||
|
header_margin_top?: string;
|
||||||
|
|
||||||
// Allow arbitrary extra keys to preserve forward-compatibility.
|
// Allow arbitrary extra keys to preserve forward-compatibility.
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -319,6 +319,13 @@ let bleed_offset = $derived.by(() => {
|
|||||||
return `-${b}`;
|
return `-${b}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Header image vertical position. Defaults to "2rem" (= prior hardcoded mt-8).
|
||||||
|
// Set cfg_json.header_margin_top on the template to shift the image up (negative) or down.
|
||||||
|
let header_margin_top = $derived.by(() => {
|
||||||
|
const v = template_cfg?.header_margin_top;
|
||||||
|
return v && typeof v === 'string' && v.trim() ? v.trim() : '2rem';
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Layout-aware section heights for Element_fit_text.
|
* Layout-aware section heights for Element_fit_text.
|
||||||
*
|
*
|
||||||
@@ -662,13 +669,14 @@ const code_to_icon: {
|
|||||||
<div
|
<div
|
||||||
class="badge_header
|
class="badge_header
|
||||||
image
|
image
|
||||||
m-0 mt-8
|
m-0
|
||||||
max-h-[1.10in]
|
max-h-[1.10in]
|
||||||
min-h-[.50in]
|
min-h-[.50in]
|
||||||
max-w-full overflow-hidden
|
max-w-full overflow-hidden
|
||||||
p-2
|
p-2
|
||||||
hover:outline-2 hover:outline-gray-500/75 hover:outline-dashed
|
hover:outline-2 hover:outline-gray-500/75 hover:outline-dashed
|
||||||
">
|
"
|
||||||
|
style="margin-top: {header_margin_top};">
|
||||||
<img
|
<img
|
||||||
class="header_image"
|
class="header_image"
|
||||||
class:header_full_width={banner_full_width}
|
class:header_full_width={banner_full_width}
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ let cfg_controls_auth_editable: string[] = $state([]);
|
|||||||
let cfg_body_text_color = $state('#000000');
|
let cfg_body_text_color = $state('#000000');
|
||||||
// Background image bleed (CSS length, e.g. "0.125in", "3mm"). Empty = no bleed.
|
// Background image bleed (CSS length, e.g. "0.125in", "3mm"). Empty = no bleed.
|
||||||
let cfg_bleed = $state('');
|
let cfg_bleed = $state('');
|
||||||
|
// Header image vertical offset (CSS length). Empty = default 2rem. Negative = shift up.
|
||||||
|
let cfg_header_margin_top = $state('');
|
||||||
// Alignment overrides: 'left' | 'center' | 'right' | 'justify'
|
// Alignment overrides: 'left' | 'center' | 'right' | 'justify'
|
||||||
let cfg_align_name = $state('center');
|
let cfg_align_name = $state('center');
|
||||||
let cfg_align_title = $state('center');
|
let cfg_align_title = $state('center');
|
||||||
@@ -160,6 +162,7 @@ async function load_template(id: string) {
|
|||||||
|
|
||||||
// Background bleed (CSS length, e.g. "0.125in"). Empty = no bleed.
|
// Background bleed (CSS length, e.g. "0.125in"). Empty = no bleed.
|
||||||
cfg_bleed = parsed_cfg.bleed ?? '';
|
cfg_bleed = parsed_cfg.bleed ?? '';
|
||||||
|
cfg_header_margin_top = parsed_cfg.header_margin_top ?? '';
|
||||||
|
|
||||||
// Alignment overrides (nested under cfg_json.align and cfg_json.qr_alignment)
|
// Alignment overrides (nested under cfg_json.align and cfg_json.qr_alignment)
|
||||||
cfg_align_name = parsed_cfg?.align?.name ?? parsed_cfg.align_name ?? 'center';
|
cfg_align_name = parsed_cfg?.align?.name ?? parsed_cfg.align_name ?? 'center';
|
||||||
@@ -238,6 +241,13 @@ async function handle_submit() {
|
|||||||
delete cfg_obj.bleed;
|
delete cfg_obj.bleed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Header image vertical offset: save if set, remove key when cleared (falls back to 2rem default)
|
||||||
|
if (cfg_header_margin_top.trim()) {
|
||||||
|
cfg_obj.header_margin_top = cfg_header_margin_top.trim();
|
||||||
|
} else {
|
||||||
|
delete cfg_obj.header_margin_top;
|
||||||
|
}
|
||||||
|
|
||||||
const data_to_save: key_val = {
|
const data_to_save: key_val = {
|
||||||
name,
|
name,
|
||||||
background_image_path,
|
background_image_path,
|
||||||
@@ -343,6 +353,14 @@ function toggle_cfg_controls_auth_editable(key: string) {
|
|||||||
<span>Header Path (URL) — top banner image (used when no background image)</span>
|
<span>Header Path (URL) — top banner image (used when no background image)</span>
|
||||||
<input type="text" bind:value={header_path} class="input" />
|
<input type="text" bind:value={header_path} class="input" />
|
||||||
</label>
|
</label>
|
||||||
|
<label class="label">
|
||||||
|
<span>Header Image Vertical Offset</span>
|
||||||
|
<input type="text" bind:value={cfg_header_margin_top} class="input" placeholder="e.g. 0.5in, -4px, 1rem (default: 2rem)" />
|
||||||
|
<p class="text-xs text-surface-400 italic">
|
||||||
|
Shifts the header image up (negative) or down (positive) within the badge front.
|
||||||
|
Any CSS length works. Leave blank to use the default (2rem).
|
||||||
|
</p>
|
||||||
|
</label>
|
||||||
<label class="label">
|
<label class="label">
|
||||||
<span>Logo Path (URL, if no Header Path)</span>
|
<span>Logo Path (URL, if no Header Path)</span>
|
||||||
<input type="text" bind:value={logo_path} class="input" />
|
<input type="text" bind:value={logo_path} class="input" />
|
||||||
|
|||||||
Reference in New Issue
Block a user