This commit is contained in:
iqudoo
2026-06-15 16:59:37 +08:00
parent fcd587bd50
commit 455946c51c
15 changed files with 1003 additions and 54 deletions

View File

@@ -2,6 +2,7 @@ import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react'
import { createPortal } from 'react-dom';
import { LabelTemplate, TemplateElement } from '../types';
import { mmToPx, MM_TO_PX, shouldRenderElement, centerElementOnLabel } from '../utils';
import { getSelectionBounds } from '../elementAlign';
import type { ElementClipboardActions } from '../hooks/useElementClipboard';
import {
createDefaultTableElement,
@@ -1447,6 +1448,33 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
suppressSelectionChrome || (dragState?.type === 'drag' && dragInteractionVisualActive);
const showUnselectedDimLayer = activeTool === 'move';
const showUnselectedDim = showUnselectedDimLayer && selectedElementIds.length > 0;
const multiSelectionBounds = useMemo(() => {
if (selectedElementIds.length < 2 || hideSelectionChrome) return null;
const selected = template.elements.filter(
(el) => selectedElementIds.includes(el.id) && !el.locked
);
if (selected.length < 2) return null;
return getSelectionBounds(selected);
}, [selectedElementIds, template.elements, hideSelectionChrome]);
const dragMultiSelectionBounds = useMemo(() => {
if (
selectedElementIds.length < 2 ||
dragState?.type !== 'drag' ||
!dragInteractionVisualActive ||
dragFloatElements.length < 2
) {
return null;
}
return getSelectionBounds(dragFloatElements);
}, [
selectedElementIds.length,
dragState?.type,
dragInteractionVisualActive,
dragFloatElements,
]);
const resizingPreviewElement =
dragState?.type === 'resize'
? interactionFloatElements?.find((el) => el.id === dragState.elementId)
@@ -1608,7 +1636,9 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
>
<div
data-drag-float-placeholder={el.id}
className="absolute inset-0 bg-white border border-[#31a8ff]/40 shadow-[0_1px_4px_rgba(0,0,0,0.12)]"
className={`absolute inset-0 bg-white shadow-[0_1px_4px_rgba(0,0,0,0.12)]${
dragMultiSelectionBounds ? '' : ' border border-[#31a8ff]/40'
}`}
/>
<img
ref={(node) => {
@@ -1645,6 +1675,24 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
</div>
);
})}
{dragMultiSelectionBounds && (
<div
className="absolute ps-resize-outline pointer-events-none z-[5]"
style={{
left: `${mmToPx(dragMultiSelectionBounds.minX, displayZoom)}px`,
top: `${mmToPx(dragMultiSelectionBounds.minY, displayZoom)}px`,
width: `${mmToPx(
dragMultiSelectionBounds.maxX - dragMultiSelectionBounds.minX,
displayZoom
)}px`,
height: `${mmToPx(
dragMultiSelectionBounds.maxY - dragMultiSelectionBounds.minY,
displayZoom
)}px`,
}}
aria-hidden
/>
)}
</div>
)}
{resizeFloatElements && resizeFloatElements.length > 0 && (
@@ -1775,32 +1823,7 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
showUnselectedDim ? ' ps-unselected-dim-layer--visible' : ''
}`}
aria-hidden={!showUnselectedDim}
>
{overlayElements.map((element) => {
if (selectedElementIds.includes(element.id)) return null;
if (dragState?.type === 'drag' && dragMovingIdSet?.has(element.id)) return null;
const elemX = mmToPx(element.x, displayZoom);
const elemY = mmToPx(element.y, displayZoom);
const elemW = mmToPx(element.width, displayZoom);
const elemH = mmToPx(element.height, displayZoom);
return (
<div
key={`dim-${element.id}`}
className="ps-unselected-dim absolute"
style={{
left: `${elemX}px`,
top: `${elemY}px`,
width: `${elemW}px`,
height: `${elemH}px`,
transform: element.rotation
? `rotate(${element.rotation}deg)`
: undefined,
transformOrigin: 'center center',
}}
/>
);
})}
</div>
/>
)}
{selectedLiftElements && (
@@ -1918,6 +1941,17 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
<div className="absolute inset-0 ps-resize-outline pointer-events-none z-[60]" aria-hidden />
)}
{isSelected &&
!hideSelectionChrome &&
!isLocked &&
selectedElementIds.length === 1 &&
!isBeingResized && (
<div
className="absolute inset-0 ps-resize-outline pointer-events-none z-[60]"
aria-hidden
/>
)}
{showResizeHandles && (
<>
<div
@@ -1997,6 +2031,25 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
</div>
);
})}
{multiSelectionBounds && (
<div
className="absolute ps-resize-outline pointer-events-none z-[1005]"
style={{
left: `${mmToPx(multiSelectionBounds.minX, displayZoom)}px`,
top: `${mmToPx(multiSelectionBounds.minY, displayZoom)}px`,
width: `${mmToPx(
multiSelectionBounds.maxX - multiSelectionBounds.minX,
displayZoom
)}px`,
height: `${mmToPx(
multiSelectionBounds.maxY - multiSelectionBounds.minY,
displayZoom
)}px`,
}}
aria-hidden
/>
)}
</div>
</div>
@@ -2028,14 +2081,17 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
<span>
: {template.width} × {template.height} mm
</span>
{selectedElemObj && (
{selectedElemObj && selectedElementIds.length === 1 && (
<span className="text-[#31a8ff]">
| {selectedElemObj.name} X:{selectedElemObj.x} Y:{selectedElemObj.y} W:
{selectedElemObj.width} H:{selectedElemObj.height} mm
</span>
)}
{selectedElementIds.length > 1 && (
<span className="text-emerald-400">| {selectedElementIds.length} </span>
{multiSelectionBounds && (
<span className="text-emerald-400">
| {selectedElementIds.length} X:{multiSelectionBounds.minX} Y:
{multiSelectionBounds.minY} mm
</span>
)}
{activeTool === 'move' && (
<span className="text-[#888] hidden sm:inline">