蒙版
This commit is contained in:
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user