Skip to content

Commit 0dba432

Browse files
committed
Make rotation fully work
1 parent 7f1b0b6 commit 0dba432

File tree

3 files changed

+71
-55
lines changed

3 files changed

+71
-55
lines changed

packages/backend/src/altNodes/jsonNodeConversion.ts

Lines changed: 68 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -177,15 +177,15 @@ const canBeFlattened = (node: Node): boolean => {
177177
* @param settings Plugin settings
178178
* @param parentNode Optional parent node reference to set
179179
* @param parentCumulativeRotation Optional parent cumulative rotation to inherit
180-
* @returns Potentially modified jsonNode
180+
* @returns Potentially modified jsonNode, array of nodes (for inlined groups), or null
181181
*/
182182
const processNodePair = async (
183183
jsonNode: Node,
184184
figmaNode: SceneNode,
185185
settings: PluginSettings,
186186
parentNode?: Node,
187187
parentCumulativeRotation: number = 0,
188-
): Promise<Node | null> => {
188+
): Promise<Node | Node[] | null> => {
189189
if (!jsonNode.id) return null;
190190
if (jsonNode.visible === false) return null;
191191

@@ -195,6 +195,9 @@ const processNodePair = async (
195195
// Handle node type-specific conversions (from convertNodeToAltNode)
196196
const nodeType = jsonNode.type;
197197

198+
// Store the cumulative rotation (parent's cumulative + node's own)
199+
jsonNode.cumulativeRotation = parentCumulativeRotation;
200+
198201
// Handle empty frames and convert to rectangles
199202
if (
200203
(nodeType === "FRAME" ||
@@ -214,21 +217,46 @@ const processNodePair = async (
214217
);
215218
}
216219

217-
// Handle single-child groups that should be ungrouped
218-
if (
219-
nodeType === "GROUP" &&
220-
jsonNode.children &&
221-
jsonNode.children.length === 1 &&
222-
jsonNode.visible
223-
) {
224-
// Process the child directly, but preserve parent reference
225-
return processNodePair(
226-
jsonNode.children[0],
227-
(figmaNode as GroupNode).children[0],
228-
settings,
229-
parentNode,
230-
parentCumulativeRotation,
231-
);
220+
if ("rotation" in jsonNode) {
221+
jsonNode.rotation = -jsonNode.rotation * (180 / Math.PI);
222+
}
223+
224+
// Inline all GROUP nodes by processing their children directly
225+
if (nodeType === "GROUP" && jsonNode.children) {
226+
const processedChildren = [];
227+
228+
if (
229+
Array.isArray(jsonNode.children) &&
230+
figmaNode &&
231+
"children" in figmaNode &&
232+
figmaNode.children.length === jsonNode.children.length
233+
) {
234+
for (let i = 0; i < jsonNode.children.length; i++) {
235+
const child = jsonNode.children[i];
236+
const figmaChild = figmaNode.children[i];
237+
if (!figmaChild) continue;
238+
239+
const processedChild = await processNodePair(
240+
child,
241+
figmaChild,
242+
settings,
243+
parentNode, // The group’s parent
244+
parentCumulativeRotation + (jsonNode.rotation || 0),
245+
);
246+
247+
// Push the processed group children directly
248+
if (processedChild !== null) {
249+
if (Array.isArray(processedChild)) {
250+
processedChildren.push(...processedChild);
251+
} else {
252+
processedChildren.push(processedChild);
253+
}
254+
}
255+
}
256+
}
257+
258+
// Simply return the processed children; skip splicing parent’s children
259+
return processedChildren;
232260
}
233261

234262
// Return null for unsupported nodes
@@ -241,11 +269,6 @@ const processNodePair = async (
241269
(jsonNode as any).parent = parentNode;
242270
}
243271

244-
// Store the cumulative rotation (parent's cumulative + node's own)
245-
if (parentNode?.type === "GROUP") {
246-
jsonNode.cumulativeRotation = parentCumulativeRotation;
247-
}
248-
249272
// Ensure node has a unique name with simple numbering
250273
const cleanName = jsonNode.name.trim();
251274

@@ -342,10 +365,6 @@ const processNodePair = async (
342365
jsonNode.y = figmaNode.y;
343366
}
344367

345-
if ("rotation" in jsonNode) {
346-
jsonNode.rotation = jsonNode.rotation * (180 / Math.PI);
347-
}
348-
349368
if ("individualStrokeWeights" in jsonNode) {
350369
jsonNode.strokeTopWeight = jsonNode.individualStrokeWeights.top;
351370
jsonNode.strokeBottomWeight = jsonNode.individualStrokeWeights.bottom;
@@ -424,7 +443,11 @@ const processNodePair = async (
424443
);
425444

426445
if (processedChild !== null) {
427-
processedChildren.push(processedChild);
446+
if (Array.isArray(processedChild)) {
447+
processedChildren.push(...processedChild);
448+
} else {
449+
processedChildren.push(processedChild);
450+
}
428451
}
429452
}
430453

@@ -488,12 +511,28 @@ export const nodesToJSON = async (
488511

489512
// Now process each top-level node pair (JSON node + Figma node)
490513
const processNodesStart = Date.now();
514+
const result: Node[] = [];
515+
491516
for (let i = 0; i < nodes.length; i++) {
492-
await processNodePair(nodeJson[i], nodes[i], settings);
517+
const processedNode = await processNodePair(
518+
nodeJson[i],
519+
nodes[i],
520+
settings,
521+
);
522+
if (processedNode !== null) {
523+
if (Array.isArray(processedNode)) {
524+
// If processNodePair returns an array (inlined group), add all nodes
525+
result.push(...processedNode);
526+
} else {
527+
// If it returns a single node, add it directly
528+
result.push(processedNode);
529+
}
530+
}
493531
}
532+
494533
console.log(
495534
`[benchmark][inside nodesToJSON] Process node pairs: ${Date.now() - processNodesStart}ms`,
496535
);
497536

498-
return nodeJson;
537+
return result;
499538
};

packages/backend/src/common/commonPosition.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const getCommonPositionValue = (
1212
x: x,
1313
y: y,
1414
},
15-
node.cumulativeRotation || node.rotation || 0,
15+
-(node.rotation + (node.cumulativeRotation || 0)),
1616
);
1717

1818
return { x: rect.left, y: rect.top };

packages/backend/src/html/builderImpl/htmlBlend.ts

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -111,31 +111,8 @@ export const htmlVisibility = (
111111
* if rotation was changed, let it be perceived. Therefore, 1 => 45
112112
*/
113113
export const htmlRotation = (node: LayoutMixin, isJsx: boolean): string[] => {
114-
// For some reason, a group with rotation also has rotated nodes.
115-
// - group 1 - rotation 45°
116-
// - child 1 - rotation 45°
117-
//
118-
// if the child is also rotated 45° the effect is doubled
119-
// - group 1 - rotation 45°
120-
// - child 1 - rotation 90°
121-
//
122-
// because of this, we subtract the rotation of the parent from the children.
123-
const parent =
124-
"parent" in node && node.parent ? (node.parent as LayoutMixin) : null;
125-
const parentRotation: number =
126-
parent && "rotation" in parent ? parent.rotation : 0;
127-
128-
const nodeRotation = -node.rotation || 0;
129-
const rotation = Math.round(parentRotation - nodeRotation) ?? 0;
130-
131-
if (
132-
roundToNearestHundreth(parentRotation) !== 0 &&
133-
roundToNearestHundreth(rotation) !== 0
134-
) {
135-
addWarning(
136-
"Rotated elements within rotated containers are not currently supported.",
137-
);
138-
}
114+
const rotation =
115+
-Math.round((node.rotation || 0) + (node.cumulativeRotation || 0)) || 0;
139116

140117
if (rotation !== 0) {
141118
return [

0 commit comments

Comments
 (0)