Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
16 changes: 2 additions & 14 deletions src/components/Canvas/BarcodeObject.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import React, { useCallback, useRef } from "react";
import bwipjs from "bwip-js/browser";
import { Image as KImage, Group, Rect, Text } from "react-konva";
import type Konva from "konva";
import type { LabelObject } from "../../registry";
import { BARCODE_1D_TYPES, ObjectRegistry } from "../../registry";
import type { ObjectChanges } from "../../store/labelStore";
import { dotsToPx, pxToDots } from "../../lib/coordinates";
import type { KonvaObjectProps } from "./konvaObjectProps";
import {
buildBwipOptions,
getDisplaySize,
Expand All @@ -23,17 +22,6 @@ import {
EAN_UPC_TYPES,
} from "./bwipConstants";

interface Props {
obj: LabelObject;
scale: number;
dpmm: number;
offsetX: number;
offsetY: number;
isSelected: boolean;
onSelect: (addToSelection: boolean) => void;
onChange: (changes: ObjectChanges) => void;
snap: (dots: number) => number;
}
export function BarcodeObject({
obj,
scale,
Expand All @@ -44,7 +32,7 @@ export function BarcodeObject({
onSelect,
onChange,
snap,
}: Props) {
}: KonvaObjectProps) {
const groupRef = useRef<Konva.Group>(null);
const textRef = useRef<Konva.Text>(null);

Expand Down
116 changes: 116 additions & 0 deletions src/components/Canvas/ImageObject.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { useState, useEffect, useRef } from "react";
import { Group, Image as KImage, Rect, Text } from "react-konva";
import type Konva from "konva";
import type { LabelObject } from "../../registry";
import { dotsToPx, pxToDots } from "../../lib/coordinates";
import { getImage } from "../../lib/imageCache";
import type { KonvaObjectProps } from "./konvaObjectProps";

type ImageLabelObject = Extract<LabelObject, { type: "image" }>;
type Props = Omit<KonvaObjectProps, "obj"> & { obj: ImageLabelObject };

/** Image renderer. Hosted as its own component so hooks (useState/
* useEffect for async image loading) can run without violating
* rules-of-hooks. The dispatcher in KonvaObject narrows `obj`
* before passing — no runtime cast needed here. */
export function ImageObject({
obj,
scale,
dpmm,
offsetX,
offsetY,
isSelected,
onSelect,
onChange,
}: Props) {
Comment thread
u8array marked this conversation as resolved.
Comment thread
u8array marked this conversation as resolved.
const p = obj.props;
const cached = getImage(p.imageId);
const w = dotsToPx(p.widthDots, scale, dpmm);
const h = cached ? w * (cached.height / cached.width) : w;
Comment thread
u8array marked this conversation as resolved.
Outdated
const x = offsetX + dotsToPx(obj.x, scale, dpmm);
const y = offsetY + dotsToPx(obj.y, scale, dpmm);

const [htmlImg, setHtmlImg] = useState<HTMLImageElement | null>(null);
// Reset the cached HTMLImageElement during render when the source changes,
// instead of inside an effect. The "set state during render on prop change"
// pattern is the official React workaround for what would otherwise be a
// setState-in-effect anti-pattern. The next render observes prevDataUrl
// already updated, so this does not loop.
const prevDataUrlRef = useRef<string | undefined>(cached?.dataUrl);
if (prevDataUrlRef.current !== cached?.dataUrl) {
prevDataUrlRef.current = cached?.dataUrl;
setHtmlImg(null);
}
useEffect(() => {
if (!cached) return;
let active = true;
const img = new window.Image();
img.src = cached.dataUrl;
img.onload = () => {
if (active) setHtmlImg(img);
};
return () => {
active = false;
};
}, [cached]);

const handleDragMove = (e: Konva.KonvaEventObject<DragEvent>) => {
onChange({
x: pxToDots(e.target.x() - offsetX, scale, dpmm),
y: pxToDots(e.target.y() - offsetY, scale, dpmm),
});
};
Comment thread
u8array marked this conversation as resolved.
Comment thread
u8array marked this conversation as resolved.

if (htmlImg && cached) {
return (
<KImage
id={obj.id}
x={x}
y={y}
image={htmlImg}
width={w}
height={h}
stroke={isSelected ? "#6366f1" : undefined}
strokeWidth={isSelected ? 2 : 0}
draggable
onClick={(e) =>
onSelect(e.evt.shiftKey || e.evt.ctrlKey || e.evt.metaKey)
}
onTap={() => onSelect(false)}
onDragMove={handleDragMove}
onDragEnd={handleDragMove}
Comment thread
u8array marked this conversation as resolved.
Outdated
/>
);
}

return (
<Group
id={obj.id}
x={x}
y={y}
draggable
onClick={(e) =>
onSelect(e.evt.shiftKey || e.evt.ctrlKey || e.evt.metaKey)
}
onTap={() => onSelect(false)}
onDragMove={handleDragMove}
onDragEnd={handleDragMove}
Comment thread
u8array marked this conversation as resolved.
Outdated
>
<Rect
width={w}
height={h}
fill="#f9fafb"
stroke={isSelected ? "#6366f1" : "#9ca3af"}
strokeWidth={isSelected ? 2 : 1}
dash={[4, 2]}
/>
<Text
x={6}
y={6}
text="🖼"
fontSize={Math.max(w * 0.3, 12)}
fill="#374151"
/>
</Group>
);
}
Loading