Skip to content

Commit 933a32a

Browse files
authored
Merge pull request #64 from u8array/refactor/groups-followup
refactor(groups): post-merge cleanup bundle
2 parents 3ddc4ca + a1e8123 commit 933a32a

25 files changed

Lines changed: 205 additions & 65 deletions

‎src/components/Canvas/ImageObject.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useState, useEffect, useRef } from "react";
22
import { Group, Image as KImage, Rect, Text } from "react-konva";
33
import type Konva from "konva";
4-
import type { LabelObject } from "../../registry";
4+
import type { LabelObject } from "../../types/Group";
55
import { dotsToPx, pxToDots } from "../../lib/coordinates";
66
import { getImage } from "../../lib/imageCache";
77
import { useColorScheme } from "../../lib/useColorScheme";

‎src/components/Canvas/LabelCanvas.tsx‎

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import type { PaletteDragData } from "../../dnd/types";
1313
import { Stage, Layer, Group, Rect, Transformer } from "react-konva";
1414
import type Konva from "konva";
1515
import { useLabelStore, useCurrentObjects, currentObjects, getCurrentObjects } from "../../store/labelStore";
16-
import { isGroup, getAllLeaves, expandSelection, selectionTargetId, findObjectById } from "../../types/Group";
16+
import { isGroup, getAllLeaves, expandSelection, selectionTargetId, findObjectById, type LabelObject } from "../../types/Group";
1717
import { pxToDots, SCREEN_PX_PER_MM } from "../../lib/coordinates";
1818
import { SNAP_OPTIONS } from "../../lib/units";
1919
import type { Unit } from "../../lib/units";
@@ -26,7 +26,7 @@ import { Grid } from "./Grid";
2626
import { GuideLines } from "./GuideLines";
2727
import { Ruler, RULER_SIZE } from "./Ruler";
2828
import { ObjectRegistry } from "../../registry";
29-
import type { LabelObject, LeafObject } from "../../registry";
29+
import type { LeafObject } from "../../registry";
3030
import { useColorScheme } from "../../lib/useColorScheme";
3131
import { objectIdsAtPoint } from "./hitTesting";
3232
import { useT } from "../../lib/useT";
@@ -131,21 +131,18 @@ export const LabelCanvas = forwardRef<LabelCanvasHandle, Props>(function LabelCa
131131
// without each consumer having to walk ancestors.
132132
const visibleLeaves = useMemo(() => {
133133
const out: LeafObject[] = [];
134-
const walk = (nodes: LabelObject[], inheritedLocked: boolean, inheritedHidden: boolean) => {
134+
const walk = (nodes: LabelObject[], inheritedLocked: boolean) => {
135135
for (const n of nodes) {
136+
if (n.visible === false) continue;
136137
const locked = inheritedLocked || !!n.locked;
137-
const hidden = inheritedHidden || n.visible === false;
138-
if (hidden) continue;
139138
if (isGroup(n)) {
140-
walk(n.children, locked, hidden);
139+
walk(n.children, locked);
141140
} else {
142-
// Preserve object identity when nothing was inherited so React
143-
// memoisation keeps unaffected leaves stable across renders.
144141
out.push(locked && !n.locked ? ({ ...n, locked: true } as LeafObject) : n);
145142
}
146143
}
147144
};
148-
walk(objects, false, false);
145+
walk(objects, false);
149146
return out;
150147
}, [objects]);
151148

‎src/components/Canvas/LineObject.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useRef, useState } from "react";
22
import { Group, Line as KLine, Rect } from "react-konva";
33
import type Konva from "konva";
4-
import type { LabelObject } from "../../registry";
4+
import type { LabelObject } from "../../types/Group";
55
import { dotsToPx, pxToDots } from "../../lib/coordinates";
66
import { constrainLine, type ConstrainMode } from "../../lib/lineConstrain";
77
import { useColorScheme } from "../../lib/useColorScheme";

‎src/components/Canvas/bwipHelpers.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
* bwipHelpers.test.ts ensures every BCID-registered type has a case.
1313
*/
1414

15-
import type { LabelObject, LeafObject } from "../../registry";
15+
import type { LeafObject } from "../../registry";
16+
import type { LabelObject } from "../../types/Group";
1617
import type { Gs1DatabarProps } from "../../registry/gs1databar";
1718
import { objectRotation } from "../../registry/rotation";
1819
import { dotsToPx } from "../../lib/coordinates";

‎src/components/Canvas/hooks/useCanvasLasso.ts‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { useState, useRef } from "react";
22
import type Konva from "konva";
33
import { getCurrentObjects } from "../../../store/labelStore";
4-
import type { LabelObject } from "../../../registry";
5-
import { isGroup } from "../../../types/Group";
4+
import { isGroup, type LabelObject } from "../../../types/Group";
65
import { getIdsIntersectingRect, type LassoRect } from "../lassoGeometry";
76

87
interface Options {

‎src/components/Canvas/transformPosition.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { describe, it, expect } from "vitest";
22
import { modelPositionFromRenderedTopLeft } from "./transformPosition";
33
import { QR_FO_Y_OFFSET_DOTS } from "./bwipConstants";
4-
import type { LabelObject } from "../../registry";
4+
import type { LabelObject } from "../../types/Group";
55

66
const qrFo: LabelObject = {
77
id: "q1",

‎src/components/Canvas/transformPosition.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { BARCODE_1D_TYPES, type LabelObject } from "../../registry";
1+
import { BARCODE_1D_TYPES } from "../../registry";
2+
import type { LabelObject } from "../../types/Group";
23
import { QR_FO_Y_OFFSET_DOTS } from "./bwipConstants";
34

45
/**

‎src/components/Properties/LayerRow.tsx‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import {
1010
LinkSlashIcon,
1111
} from '@heroicons/react/16/solid';
1212
import { ObjectRegistry } from '../../registry';
13-
import type { LabelObject } from '../../registry';
14-
import { isGroup } from '../../types/Group';
13+
import { isGroup, type LabelObject } from '../../types/Group';
1514
import { useT } from '../../lib/useT';
1615
import { DragHandleIcon } from '../ui/DragHandleIcon';
1716
import { INDENT_STEP } from './layerLayout';

‎src/components/Properties/useLayerDnd.ts‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import { useEffect, useMemo, useRef, useState } from 'react';
22
import { PointerSensor, closestCenter, useSensor, useSensors } from '@dnd-kit/core';
33
import type { DragEndEvent, DragOverEvent } from '@dnd-kit/core';
44
import { isGroup, findObjectById, findAncestors } from '../../types/Group';
5-
import type { GroupObject } from '../../types/Group';
6-
import type { LabelObject } from '../../registry';
5+
import type { GroupObject, LabelObject } from '../../types/Group';
76
import { INDENT_STEP } from './layerLayout';
87

98
/** Sentinel container id for the top-level objects list. Group containers

‎src/lib/designFile.test.ts‎

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, it, expect } from 'vitest';
22
import { parseDesignFile, serializeDesign } from './designFile';
3-
import type { LabelObject } from '../registry';
3+
import type { LabelObject } from '../types/Group';
44

55
const SAMPLE_OBJECTS: LabelObject[] = [
66
{
@@ -85,4 +85,75 @@ describe('parseDesignFile', () => {
8585
expect(result.value.pages[0]?.objects).toHaveLength(1);
8686
expect(result.value.pages[1]?.objects).toHaveLength(1);
8787
});
88+
89+
it('roundtrips a design containing nested groups without structural loss', () => {
90+
const designWithGroups: LabelObject[] = [
91+
SAMPLE_OBJECTS[0]!,
92+
{
93+
id: 'grp-outer',
94+
type: 'group',
95+
x: 0,
96+
y: 0,
97+
rotation: 0,
98+
name: 'Header',
99+
children: [
100+
{
101+
id: 'grp-inner',
102+
type: 'group',
103+
x: 0,
104+
y: 0,
105+
rotation: 0,
106+
children: [
107+
{
108+
id: 'obj-2',
109+
type: 'box',
110+
x: 5,
111+
y: 5,
112+
rotation: 0,
113+
props: { width: 20, height: 10, thickness: 1, filled: true, color: 'B', rounding: 0 },
114+
},
115+
],
116+
} as LabelObject,
117+
],
118+
} as LabelObject,
119+
];
120+
const json = serializeDesign(
121+
{ widthMm: 100, heightMm: 60, dpmm: 8 },
122+
[{ objects: designWithGroups }],
123+
);
124+
const result = parseDesignFile(json);
125+
expect(result.ok).toBe(true);
126+
if (!result.ok) return;
127+
expect(result.value.pages[0]?.objects).toEqual(designWithGroups);
128+
});
129+
130+
it('rejects a leaf object that is missing its props', () => {
131+
const malformed = JSON.stringify({
132+
label: { widthMm: 100, heightMm: 60, dpmm: 8 },
133+
pages: [
134+
{
135+
objects: [{ id: 'a', type: 'box', x: 0, y: 0, rotation: 0 }],
136+
},
137+
],
138+
});
139+
const result = parseDesignFile(malformed);
140+
expect(result.ok).toBe(false);
141+
if (result.ok) return;
142+
expect(result.error).toBe('invalid_schema');
143+
});
144+
145+
it('rejects a group object that is missing its children', () => {
146+
const malformed = JSON.stringify({
147+
label: { widthMm: 100, heightMm: 60, dpmm: 8 },
148+
pages: [
149+
{
150+
objects: [{ id: 'g', type: 'group', x: 0, y: 0, rotation: 0 }],
151+
},
152+
],
153+
});
154+
const result = parseDesignFile(malformed);
155+
expect(result.ok).toBe(false);
156+
if (result.ok) return;
157+
expect(result.error).toBe('invalid_schema');
158+
});
88159
});

0 commit comments

Comments
 (0)