Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions packages/web/src/editor/canvas/elements/barcode-element.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,42 @@ export function BarcodeElement({ element, isSelected }: Props) {
};

const cacheKey = useMemo(
() => `${p.content || ""}|${p.format || "CODE128"}|${p.displayValue}`,
[p.content, p.format, p.displayValue],
() => `${p.content || ""}|${p.format || "CODE128"}|${p.displayValue}|${element.width}|${element.height}`,
[p.content, p.format, p.displayValue, element.width, element.height],
);

const [image, setImage] = useState<HTMLImageElement | null>(null);

useEffect(() => {
try {
const showVal = p.displayValue ?? true;
const fontSz = showVal
? Math.max(9, Math.min(18, Math.round(element.height * 0.22)))
: 0;
const barH = Math.max(12, Math.round(element.height - (showVal ? fontSz + 8 : 4)));

// Measure natural module width using a 1px test render
const testCanvas = document.createElement("canvas");
JsBarcode(testCanvas, p.content || "1234567890", {
format: p.format || "CODE128",
displayValue: false,
margin: 2,
width: 1,
height: 10,
});

const modules = testCanvas.width > 4 ? testCanvas.width - 4 : 100;
const targetBarWidth = Math.max(1, Math.min(6, (element.width - 6) / modules));

const canvas = document.createElement("canvas");
JsBarcode(canvas, p.content || "0000", {
JsBarcode(canvas, p.content || "1234567890", {
format: p.format || "CODE128",
displayValue: p.displayValue ?? true,
displayValue: showVal,
fontSize: fontSz,
textMargin: 1,
margin: 2,
height: 60,
width: targetBarWidth,
height: barH,
background: "#ffffff",
lineColor: "#000000",
});
Expand All @@ -46,7 +68,7 @@ export function BarcodeElement({ element, isSelected }: Props) {
} catch {
// Invalid barcode content for format
}
}, [cacheKey, p.content, p.format, p.displayValue]);
}, [cacheKey]);

if (!image) {
return (
Expand Down
50 changes: 48 additions & 2 deletions packages/web/src/editor/inspector/sections/transform-section.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {
AlignHorizontalJustifyCenter,
AlignVerticalJustifyCenter,
Focus,
Maximize,
} from "lucide-react";
import type { BaseElement } from "../../../store/editor-store.ts";
import { useEditorV2Store } from "../../../store/editor-store.ts";
Expand All @@ -27,6 +29,43 @@ export function TransformSection({ element }: Props) {
y: Math.round((label.heightPx - element.height) / 2),
});

const fitToLabel = () => {
const p = element.props as { naturalWidth?: number; naturalHeight?: number };
const marginFactor = 0.9; // 90% of label size (10% padding)
const maxW = label.widthPx * marginFactor;
const maxH = label.heightPx * marginFactor;

let newW = maxW;
let newH = maxH;

if (element.type === "qrcode") {
const size = Math.round(Math.min(maxW, maxH));
newW = size;
newH = size;
} else if (p.naturalWidth && p.naturalHeight) {
const ratio = Math.min(maxW / p.naturalWidth, maxH / p.naturalHeight);
newW = Math.round(p.naturalWidth * ratio);
newH = Math.round(p.naturalHeight * ratio);
} else if (element.type === "barcode") {
newW = Math.round(maxW);
newH = Math.round(Math.min(maxH, maxW * 0.45));
} else if (element.width && element.height) {
const ratio = Math.min(maxW / element.width, maxH / element.height);
newW = Math.round(element.width * ratio);
newH = Math.round(element.height * ratio);
} else {
newW = Math.round(maxW);
newH = Math.round(maxH);
}

update({
width: newW,
height: newH,
x: Math.round((label.widthPx - newW) / 2),
y: Math.round((label.heightPx - newH) / 2),
});
};

return (
<Section title="Transform">
<div className="grid grid-cols-2 gap-1.5">
Expand Down Expand Up @@ -64,10 +103,17 @@ export function TransformSection({ element }: Props) {
</button>
<button
onClick={alignBoth}
className="flex-1 h-7 rounded-md bg-ink-800 border border-white/5 hover:bg-ink-750 text-ui-xs font-mono text-ink-300 hover:text-ink-100"
className="flex-1 h-7 rounded-md bg-ink-800 border border-white/5 hover:bg-ink-750 text-ink-300 hover:text-ink-100 flex items-center justify-center"
title="Center both"
>
CTR
<Focus size={14} />
</button>
<button
onClick={fitToLabel}
className="flex-1 h-7 rounded-md bg-ink-800 border border-white/5 hover:bg-ink-750 text-ink-300 hover:text-ink-100 flex items-center justify-center"
title="Fit to label / Maximize"
>
<Maximize size={14} />
</button>
</div>
</div>
Expand Down
28 changes: 18 additions & 10 deletions packages/web/src/lib/keyboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,28 +35,36 @@ function addTextEl() {
}

function addQrEl() {
const { addElement } = useEditorV2Store.getState();
const { label, addElement } = useEditorV2Store.getState();
const maxDim = Math.round(Math.min(label.widthPx, label.heightPx) * 0.8);
const size = Math.max(24, Math.min(120, maxDim));
const x = Math.round((label.widthPx - size) / 2);
const y = Math.round((label.heightPx - size) / 2);
addElement({
id: uid(),
type: "qrcode",
x: 20,
y: 20,
width: 120,
height: 120,
x,
y,
width: size,
height: size,
rotation: 0,
props: { content: "https://google.com", errorCorrectionLevel: "M" },
});
}

function addBarcodeEl() {
const { addElement } = useEditorV2Store.getState();
const { label, addElement } = useEditorV2Store.getState();
const w = Math.round(Math.min(220, label.widthPx * 0.85));
const h = Math.round(Math.min(80, label.heightPx * 0.7));
const x = Math.round((label.widthPx - w) / 2);
const y = Math.round((label.heightPx - h) / 2);
addElement({
id: uid(),
type: "barcode",
x: 20,
y: 40,
width: 220,
height: 80,
x,
y,
width: w,
height: h,
rotation: 0,
props: { content: "1234567890", format: "CODE128", displayValue: true },
});
Expand Down