;
@@ -91,11 +98,48 @@ const GridSelectionOverlays = ({
spritePositions,
) !== 0,
);
+ const isMultiSelection = selectedElements.length > 1;
+ const rotationFrame =
+ selectionFrame &&
+ (selectionFrame.rotation !== 0 ||
+ selectionFrame.snapshot.hasRotatedContent ||
+ selectionFrame.snapshot.entries.some(
+ (entry) =>
+ entry.type === 'sprite' &&
+ (entry.idleTransform.x !== 0 ||
+ entry.idleTransform.y !== 0 ||
+ entry.idleTransform.scale !== 1),
+ ))
+ ? { bounds: selectionFrame.bounds, rotation: selectionFrame.rotation }
+ : undefined;
+ const groupData =
+ isMultiSelection && !rotatedWithoutFrame
+ ? calculateGroupBounds(
+ selectedElements,
+ positions,
+ statPositions,
+ graphPositions,
+ knobPositions,
+ mode,
+ pluginElements,
+ spritePositions,
+ )
+ : null;
+ // 그룹 핸들이 실제로 그리는 프레임과 같은 좌표·회전·프리뷰 사용
+ const groupOutlineFrame = groupData
+ ? {
+ bounds: previewGroupBounds ?? rotationFrame?.bounds ?? groupData,
+ rotation: rotationFrame?.rotation ?? 0,
+ }
+ : null;
+ const previewByElement = new Map(
+ previewElementBounds?.map(({ element, bounds }) => [
+ `${element.type}:${element.id}`,
+ bounds,
+ ]),
+ );
const selectionOutlines: ReactNode[] = [];
- if (
- !hasGradientEditSession &&
- (selectedElements.length === 1 || rotatedWithoutFrame)
- ) {
+ if (!hasGradientEditSession) {
selectedElements.forEach((element) => {
const bounds = getElementBounds(
element,
@@ -108,16 +152,12 @@ const GridSelectionOverlays = ({
spritePositions,
);
if (!bounds) return;
- const displayBounds =
- selectedElements.length === 1 && previewBounds ? previewBounds : bounds;
- const outlineLeft =
- displayBounds.x * zoom + panX - SELECTION_BORDER_WIDTH;
- const outlineTop = displayBounds.y * zoom + panY - SELECTION_BORDER_WIDTH;
- const outlineWidth =
- displayBounds.width * zoom + SELECTION_BORDER_WIDTH * 2;
- const outlineHeight =
- displayBounds.height * zoom + SELECTION_BORDER_WIDTH * 2;
- // 선택 틀과 핸들은 회전한 얼굴을 추종
+ const displayBounds = isMultiSelection
+ ? previewByElement.get(`${element.type}:${element.id}`) ?? bounds
+ : previewBounds ?? bounds;
+ const borderWidth = isMultiSelection
+ ? GROUP_SELECTION_BORDER_WIDTH
+ : SELECTION_BORDER_WIDTH;
const rotation = getElementRotation(
element,
positions,
@@ -127,19 +167,48 @@ const GridSelectionOverlays = ({
mode,
spritePositions,
);
+ const edges = getSelectionOutlineEdges(
+ displayBounds,
+ rotation,
+ groupOutlineFrame,
+ );
+ const nonResizable =
+ isMultiSelection &&
+ !isElementResizable(
+ element,
+ positions,
+ statPositions,
+ graphPositions,
+ knobPositions,
+ mode,
+ pluginElements,
+ );
+ const color = nonResizable
+ ? 'rgba(251, 146, 60, 0.9)'
+ : isMultiSelection
+ ? GROUP_SELECTION_BORDER_COLOR
+ : 'var(--ui-selection-border)';
selectionOutlines.push(
- entry.type === 'sprite' &&
- (entry.idleTransform.x !== 0 ||
- entry.idleTransform.y !== 0 ||
- entry.idleTransform.scale !== 1),
- ))
- ? {
- bounds: selectionFrame.bounds,
- rotation: selectionFrame.rotation,
- }
- : undefined
- }
+ rotationFrame={rotationFrame}
onGroupResizeStart={onResizeStart}
onGroupResize={onGroupResize}
onGroupResizeEnd={onGroupResizeEnd}