优化
This commit is contained in:
@@ -3,6 +3,7 @@ import { LabelTemplate, TemplateElement } from '../types';
|
|||||||
import { mmToPx, MM_TO_PX, shouldRenderElement } from '../utils';
|
import { mmToPx, MM_TO_PX, shouldRenderElement } from '../utils';
|
||||||
import { useIsNarrowScreen } from '../hooks/useIsMobile';
|
import { useIsNarrowScreen } from '../hooks/useIsMobile';
|
||||||
import { CanvasLabelImage } from './CanvasLabelImage';
|
import { CanvasLabelImage } from './CanvasLabelImage';
|
||||||
|
import { useAppDialog } from './AppDialog';
|
||||||
import {
|
import {
|
||||||
ZoomIn,
|
ZoomIn,
|
||||||
ZoomOut,
|
ZoomOut,
|
||||||
@@ -85,6 +86,56 @@ function blurActiveField() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 在容器内等比约束画布显示尺寸,避免宽/高单独触顶导致变形 */
|
||||||
|
function constrainCanvasDisplaySize(
|
||||||
|
widthMm: number,
|
||||||
|
heightMm: number,
|
||||||
|
zoom: number,
|
||||||
|
maxW: number,
|
||||||
|
maxH: number
|
||||||
|
) {
|
||||||
|
let displayW = mmToPx(widthMm, zoom);
|
||||||
|
let displayH = mmToPx(heightMm, zoom);
|
||||||
|
|
||||||
|
if (displayW > maxW) {
|
||||||
|
displayW = maxW;
|
||||||
|
const cappedZoom = displayW / (widthMm * MM_TO_PX);
|
||||||
|
displayH = mmToPx(heightMm, cappedZoom);
|
||||||
|
}
|
||||||
|
if (displayH > maxH) {
|
||||||
|
displayH = maxH;
|
||||||
|
const cappedZoom = displayH / (heightMm * MM_TO_PX);
|
||||||
|
displayW = mmToPx(widthMm, cappedZoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayZoom = displayW / (widthMm * MM_TO_PX);
|
||||||
|
const canvasRenderScale = Math.max(
|
||||||
|
1,
|
||||||
|
Math.round(MM_TO_PX * displayZoom * 100) / 100
|
||||||
|
);
|
||||||
|
|
||||||
|
return { displayW, displayH, displayZoom, canvasRenderScale };
|
||||||
|
}
|
||||||
|
|
||||||
|
const ZOOM_MIN = 0.5;
|
||||||
|
const ZOOM_MAX = 5;
|
||||||
|
|
||||||
|
function getMaxZoomForContainer(
|
||||||
|
widthMm: number,
|
||||||
|
heightMm: number,
|
||||||
|
maxW: number,
|
||||||
|
maxH: number
|
||||||
|
) {
|
||||||
|
if (maxW <= 0 || maxH <= 0) return ZOOM_MAX;
|
||||||
|
const byW = maxW / (widthMm * MM_TO_PX);
|
||||||
|
const byH = maxH / (heightMm * MM_TO_PX);
|
||||||
|
return Math.max(ZOOM_MIN, Math.min(ZOOM_MAX, byW, byH));
|
||||||
|
}
|
||||||
|
|
||||||
|
function clampZoomValue(value: number, maxZoom: number) {
|
||||||
|
return Math.min(maxZoom, Math.max(ZOOM_MIN, Math.round(value * 100) / 100));
|
||||||
|
}
|
||||||
|
|
||||||
function rectFullyContains(
|
function rectFullyContains(
|
||||||
outer: { left: number; top: number; right: number; bottom: number },
|
outer: { left: number; top: number; right: number; bottom: number },
|
||||||
inner: { left: number; top: number; right: number; bottom: number }
|
inner: { left: number; top: number; right: number; bottom: number }
|
||||||
@@ -137,10 +188,12 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
|||||||
onSelectElements,
|
onSelectElements,
|
||||||
variant = 'desktop',
|
variant = 'desktop',
|
||||||
}) => {
|
}) => {
|
||||||
|
const { showConfirm } = useAppDialog();
|
||||||
const selectedElementId = selectedElementIds[0] || null;
|
const selectedElementId = selectedElementIds[0] || null;
|
||||||
const isMobileLayout = variant === 'mobile';
|
const isMobileLayout = variant === 'mobile';
|
||||||
const isNarrow = useIsNarrowScreen() || isMobileLayout;
|
const isNarrow = useIsNarrowScreen() || isMobileLayout;
|
||||||
const [zoom, setZoom] = useState<number>(MODERATE_ZOOM_MAX);
|
const [zoom, setZoom] = useState<number>(MODERATE_ZOOM_MAX);
|
||||||
|
const [canvasAreaSize, setCanvasAreaSize] = useState({ w: 0, h: 0 });
|
||||||
const [activeTool, setActiveTool] = useState<ActiveTool>('move');
|
const [activeTool, setActiveTool] = useState<ActiveTool>('move');
|
||||||
const canvasAreaRef = useRef<HTMLDivElement | null>(null);
|
const canvasAreaRef = useRef<HTMLDivElement | null>(null);
|
||||||
const canvasRef = useRef<HTMLDivElement | null>(null);
|
const canvasRef = useRef<HTMLDivElement | null>(null);
|
||||||
@@ -296,20 +349,36 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
|||||||
[template, onChange, onSelectElements]
|
[template, onChange, onSelectElements]
|
||||||
);
|
);
|
||||||
|
|
||||||
const deleteSelectedElements = useCallback(() => {
|
const deleteSelectedElements = useCallback(async () => {
|
||||||
const idSet = new Set(
|
const ids =
|
||||||
selectedElementIds.length > 0
|
selectedElementIds.length > 0
|
||||||
? selectedElementIds
|
? selectedElementIds
|
||||||
: selectedElementId
|
: selectedElementId
|
||||||
? [selectedElementId]
|
? [selectedElementId]
|
||||||
: []
|
: [];
|
||||||
);
|
const deletable = ids.filter((id) => {
|
||||||
if (idSet.size === 0) return;
|
const el = template.elements.find((item) => item.id === id);
|
||||||
|
return el && !el.locked;
|
||||||
|
});
|
||||||
|
if (deletable.length === 0) return;
|
||||||
|
|
||||||
const remaining = template.elements.filter((el) => !idSet.has(el.id) || el.locked);
|
const message =
|
||||||
|
deletable.length === 1
|
||||||
|
? `确定删除「${template.elements.find((item) => item.id === deletable[0])?.name ?? '未命名'}」吗?此操作无法撤销。`
|
||||||
|
: `确定删除选中的 ${deletable.length} 个元素吗?此操作无法撤销。`;
|
||||||
|
|
||||||
|
const confirmed = await showConfirm(message, {
|
||||||
|
title: '删除元素',
|
||||||
|
confirmLabel: '删除',
|
||||||
|
variant: 'error',
|
||||||
|
});
|
||||||
|
if (!confirmed) return;
|
||||||
|
|
||||||
|
const idSet = new Set(deletable);
|
||||||
|
const remaining = template.elements.filter((el) => !idSet.has(el.id));
|
||||||
onChange({ ...template, elements: remaining });
|
onChange({ ...template, elements: remaining });
|
||||||
onSelectElements(selectedElementIds.filter((id) => remaining.some((el) => el.id === id)));
|
onSelectElements(selectedElementIds.filter((id) => remaining.some((el) => el.id === id)));
|
||||||
}, [selectedElementIds, selectedElementId, template, onChange, onSelectElements]);
|
}, [selectedElementIds, selectedElementId, template, onChange, onSelectElements, showConfirm]);
|
||||||
|
|
||||||
const handleToolClick = (tool: ActiveTool) => {
|
const handleToolClick = (tool: ActiveTool) => {
|
||||||
if (tool === 'move') {
|
if (tool === 'move') {
|
||||||
@@ -352,7 +421,7 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
|||||||
}
|
}
|
||||||
if (e.key === 'Delete' || e.key === 'Backspace') {
|
if (e.key === 'Delete' || e.key === 'Backspace') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
deleteSelectedElements();
|
void deleteSelectedElements();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,6 +539,61 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
|||||||
applyModerateZoom();
|
applyModerateZoom();
|
||||||
}, [template.id, applyModerateZoom]);
|
}, [template.id, applyModerateZoom]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const area = canvasAreaRef.current;
|
||||||
|
if (!area) return;
|
||||||
|
|
||||||
|
const updateSize = () => {
|
||||||
|
setCanvasAreaSize({ w: area.clientWidth, h: area.clientHeight });
|
||||||
|
};
|
||||||
|
updateSize();
|
||||||
|
const observer = new ResizeObserver(updateSize);
|
||||||
|
observer.observe(area);
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const canvasPad = 32;
|
||||||
|
const canvasLayout = useMemo(() => {
|
||||||
|
const maxW = canvasAreaSize.w - canvasPad;
|
||||||
|
const maxH = canvasAreaSize.h - canvasPad;
|
||||||
|
const maxZoom = getMaxZoomForContainer(template.width, template.height, maxW, maxH);
|
||||||
|
if (maxW <= 0 || maxH <= 0) {
|
||||||
|
const effectiveZoom = clampZoomValue(zoom, maxZoom);
|
||||||
|
const displayW = mmToPx(template.width, effectiveZoom);
|
||||||
|
const displayH = mmToPx(template.height, effectiveZoom);
|
||||||
|
return {
|
||||||
|
displayW,
|
||||||
|
displayH,
|
||||||
|
displayZoom: effectiveZoom,
|
||||||
|
canvasRenderScale: Math.max(1, Math.round(MM_TO_PX * effectiveZoom * 100) / 100),
|
||||||
|
maxZoom,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const constrained = constrainCanvasDisplaySize(
|
||||||
|
template.width,
|
||||||
|
template.height,
|
||||||
|
zoom,
|
||||||
|
maxW,
|
||||||
|
maxH
|
||||||
|
);
|
||||||
|
return { ...constrained, maxZoom };
|
||||||
|
}, [template.width, template.height, zoom, canvasAreaSize.w, canvasAreaSize.h]);
|
||||||
|
const {
|
||||||
|
displayW: canvasDisplayW,
|
||||||
|
displayH: canvasDisplayH,
|
||||||
|
displayZoom,
|
||||||
|
canvasRenderScale,
|
||||||
|
maxZoom,
|
||||||
|
} = canvasLayout;
|
||||||
|
const displayZoomRef = useRef(displayZoom);
|
||||||
|
displayZoomRef.current = displayZoom;
|
||||||
|
const maxZoomRef = useRef(maxZoom);
|
||||||
|
maxZoomRef.current = maxZoom;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setZoom((z) => (z > maxZoom ? clampZoomValue(z, maxZoom) : z));
|
||||||
|
}, [maxZoom]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const area = canvasAreaRef.current;
|
const area = canvasAreaRef.current;
|
||||||
if (!area) return;
|
if (!area) return;
|
||||||
@@ -478,7 +602,7 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
|||||||
if (e.ctrlKey || e.metaKey) {
|
if (e.ctrlKey || e.metaKey) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const delta = e.deltaY > 0 ? -0.25 : 0.25;
|
const delta = e.deltaY > 0 ? -0.25 : 0.25;
|
||||||
setZoom((z) => Math.min(5, Math.max(0.5, Math.round((z + delta) * 100) / 100)));
|
setZoom((z) => clampZoomValue(z + delta, maxZoomRef.current));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -533,10 +657,10 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
|||||||
.filter((el) => {
|
.filter((el) => {
|
||||||
if (el.locked) return false;
|
if (el.locked) return false;
|
||||||
const elRect = {
|
const elRect = {
|
||||||
left: mmToPx(el.x, zoom),
|
left: mmToPx(el.x, displayZoomRef.current),
|
||||||
top: mmToPx(el.y, zoom),
|
top: mmToPx(el.y, displayZoomRef.current),
|
||||||
right: mmToPx(el.x + el.width, zoom),
|
right: mmToPx(el.x + el.width, displayZoomRef.current),
|
||||||
bottom: mmToPx(el.y + el.height, zoom),
|
bottom: mmToPx(el.y + el.height, displayZoomRef.current),
|
||||||
};
|
};
|
||||||
return rectFullyContains(marqueeRect, elRect);
|
return rectFullyContains(marqueeRect, elRect);
|
||||||
})
|
})
|
||||||
@@ -558,7 +682,7 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
|||||||
window.removeEventListener('pointerup', handlePointerUp);
|
window.removeEventListener('pointerup', handlePointerUp);
|
||||||
window.removeEventListener('pointercancel', handlePointerUp);
|
window.removeEventListener('pointercancel', handlePointerUp);
|
||||||
};
|
};
|
||||||
}, [marqueeActive, zoom, onSelectElements]);
|
}, [marqueeActive, displayZoom, onSelectElements]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!dragState) return;
|
if (!dragState) return;
|
||||||
@@ -578,7 +702,7 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
|||||||
if (dragState.type === 'drag' && pastDragThreshold) {
|
if (dragState.type === 'drag' && pastDragThreshold) {
|
||||||
dragMovedRef.current = true;
|
dragMovedRef.current = true;
|
||||||
}
|
}
|
||||||
const MM_PER_PX = 1 / (3.78 * zoom);
|
const MM_PER_PX = 1 / (3.78 * displayZoomRef.current);
|
||||||
const deltaXMm = deltaX * MM_PER_PX;
|
const deltaXMm = deltaX * MM_PER_PX;
|
||||||
const deltaYMm = deltaY * MM_PER_PX;
|
const deltaYMm = deltaY * MM_PER_PX;
|
||||||
|
|
||||||
@@ -610,6 +734,7 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
|||||||
const elem = template.elements.find((el) => el.id === dragState.elementId);
|
const elem = template.elements.find((el) => el.id === dragState.elementId);
|
||||||
if (!elem || elem.locked) return;
|
if (!elem || elem.locked) return;
|
||||||
|
|
||||||
|
dragMovedRef.current = true;
|
||||||
const corner = dragState.resizeCorner ?? 'se';
|
const corner = dragState.resizeCorner ?? 'se';
|
||||||
const rect =
|
const rect =
|
||||||
elem.type === 'qrcode'
|
elem.type === 'qrcode'
|
||||||
@@ -664,8 +789,12 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
|||||||
localElementsRef.current = pendingDragElementsRef.current;
|
localElementsRef.current = pendingDragElementsRef.current;
|
||||||
pendingDragElementsRef.current = null;
|
pendingDragElementsRef.current = null;
|
||||||
}
|
}
|
||||||
if (localElementsRef.current && dragMovedRef.current) {
|
if (localElementsRef.current) {
|
||||||
onChange({ ...template, elements: localElementsRef.current });
|
const shouldCommit =
|
||||||
|
dragState.type === 'resize' || dragMovedRef.current;
|
||||||
|
if (shouldCommit) {
|
||||||
|
onChange({ ...template, elements: localElementsRef.current });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
localElementsRef.current = null;
|
localElementsRef.current = null;
|
||||||
setLocalElements(null);
|
setLocalElements(null);
|
||||||
@@ -681,7 +810,7 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
|||||||
window.removeEventListener('pointerup', handlePointerUp);
|
window.removeEventListener('pointerup', handlePointerUp);
|
||||||
window.removeEventListener('pointercancel', handlePointerUp);
|
window.removeEventListener('pointercancel', handlePointerUp);
|
||||||
};
|
};
|
||||||
}, [dragState, template, zoom, onChange, cancelActiveInteraction]);
|
}, [dragState, template, displayZoom, onChange, cancelActiveInteraction]);
|
||||||
|
|
||||||
const buildGroupStartPositions = (element: TemplateElement) => {
|
const buildGroupStartPositions = (element: TemplateElement) => {
|
||||||
const moveIds =
|
const moveIds =
|
||||||
@@ -855,7 +984,6 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
|||||||
frozenCanvasElements ?? template.elements
|
frozenCanvasElements ?? template.elements
|
||||||
);
|
);
|
||||||
const overlayElements = filterVisibleElements(localElements ?? template.elements);
|
const overlayElements = filterVisibleElements(localElements ?? template.elements);
|
||||||
const canvasRenderScale = Math.max(1, Math.round(MM_TO_PX * zoom * 100) / 100);
|
|
||||||
|
|
||||||
const tools: { id: ActiveTool; icon: React.ReactNode; label: string; shortcut?: string }[] = [
|
const tools: { id: ActiveTool; icon: React.ReactNode; label: string; shortcut?: string }[] = [
|
||||||
{ id: 'move', icon: <Move className="w-[18px] h-[18px]" />, label: '移动工具', shortcut: 'V' },
|
{ id: 'move', icon: <Move className="w-[18px] h-[18px]" />, label: '移动工具', shortcut: 'V' },
|
||||||
@@ -897,7 +1025,7 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
|||||||
selectedElementIds.length > 0 ? (
|
selectedElementIds.length > 0 ? (
|
||||||
<button
|
<button
|
||||||
className={`ps-tool-btn hover:!bg-red-900/40 hover:!text-red-400 ${isMobileLayout ? 'mobile-design-tool-btn' : ''}`}
|
className={`ps-tool-btn hover:!bg-red-900/40 hover:!text-red-400 ${isMobileLayout ? 'mobile-design-tool-btn' : ''}`}
|
||||||
onClick={deleteSelectedElements}
|
onClick={() => void deleteSelectedElements()}
|
||||||
title="删除选中元素"
|
title="删除选中元素"
|
||||||
>
|
>
|
||||||
<Trash2 className="w-[18px] h-[18px]" />
|
<Trash2 className="w-[18px] h-[18px]" />
|
||||||
@@ -907,20 +1035,29 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
|||||||
const zoomControls = (
|
const zoomControls = (
|
||||||
<div className="flex items-center gap-1.5 shrink-0">
|
<div className="flex items-center gap-1.5 shrink-0">
|
||||||
<div className="ps-zoom-control">
|
<div className="ps-zoom-control">
|
||||||
<button className="ps-zoom-btn" onClick={() => setZoom(Math.max(0.5, zoom - 0.25))} title="缩小">
|
<button
|
||||||
|
className="ps-zoom-btn"
|
||||||
|
onClick={() => setZoom((z) => clampZoomValue(z - 0.25, maxZoom))}
|
||||||
|
title="缩小"
|
||||||
|
>
|
||||||
<ZoomOut className="w-3 h-3" />
|
<ZoomOut className="w-3 h-3" />
|
||||||
</button>
|
</button>
|
||||||
<span className="px-1.5 text-[10px] font-mono text-[#ccc] min-w-[42px] text-center select-none">
|
<span className="px-1.5 text-[10px] font-mono text-[#ccc] min-w-[42px] text-center select-none">
|
||||||
{Math.round(zoom * 100)}%
|
{Math.round(displayZoom * 100)}%
|
||||||
</span>
|
</span>
|
||||||
<button className="ps-zoom-btn" onClick={() => setZoom(Math.min(5, zoom + 0.25))} title="放大">
|
<button
|
||||||
|
className="ps-zoom-btn"
|
||||||
|
onClick={() => setZoom((z) => clampZoomValue(z + 0.25, maxZoom))}
|
||||||
|
disabled={displayZoom >= maxZoom - 0.001}
|
||||||
|
title="放大"
|
||||||
|
>
|
||||||
<ZoomIn className="w-3 h-3" />
|
<ZoomIn className="w-3 h-3" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={fitToView}
|
onClick={fitToView}
|
||||||
className="text-[10px] text-[#aaa] hover:text-white px-1.5 py-0.5 border border-[#4a4a4a] hover:border-[#666] transition cursor-pointer"
|
className="h-[24px] text-[10px] text-[#aaa] hover:text-white px-1.5 py-0.5 border border-[#4a4a4a] hover:border-[#666] transition cursor-pointer"
|
||||||
title="适合窗口"
|
title="适合窗口"
|
||||||
>
|
>
|
||||||
适合
|
适合
|
||||||
@@ -947,7 +1084,7 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
|||||||
<div className="flex flex-col flex-1 min-w-0 min-h-0">
|
<div className="flex flex-col flex-1 min-w-0 min-h-0">
|
||||||
<div
|
<div
|
||||||
ref={canvasAreaRef}
|
ref={canvasAreaRef}
|
||||||
className="flex-1 overflow-auto ps-workspace-bg ps-design-canvas-touch flex items-center justify-center"
|
className="flex-1 overflow-auto ps-workspace-bg ps-design-canvas-touch flex items-center justify-center min-h-0 min-w-0 p-4"
|
||||||
onPointerDown={handleWorkspacePointerDown}
|
onPointerDown={handleWorkspacePointerDown}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
if (ignoreCanvasClickRef.current) {
|
if (ignoreCanvasClickRef.current) {
|
||||||
@@ -961,10 +1098,10 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
ref={canvasRef}
|
ref={canvasRef}
|
||||||
className="relative bg-white shadow-[0_2px_20px_rgba(0,0,0,0.5)] select-none"
|
className="relative shrink-0 grow-0 bg-white shadow-[0_2px_20px_rgba(0,0,0,0.5)] select-none"
|
||||||
style={{
|
style={{
|
||||||
width: `${mmToPx(template.width, zoom)}px`,
|
width: `${canvasDisplayW}px`,
|
||||||
height: `${mmToPx(template.height, zoom)}px`,
|
height: `${canvasDisplayH}px`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* <div className="absolute -top-5 left-0 text-[10px] font-mono text-[#aaa]">
|
{/* <div className="absolute -top-5 left-0 text-[10px] font-mono text-[#aaa]">
|
||||||
@@ -1015,10 +1152,10 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
|||||||
const isLocked = !!element.locked;
|
const isLocked = !!element.locked;
|
||||||
const isSelected = !isLocked && selectedElementIds.includes(element.id);
|
const isSelected = !isLocked && selectedElementIds.includes(element.id);
|
||||||
const isPrimary = selectedElementIds.indexOf(element.id) === 0;
|
const isPrimary = selectedElementIds.indexOf(element.id) === 0;
|
||||||
const elemX = mmToPx(element.x, zoom);
|
const elemX = mmToPx(element.x, displayZoom);
|
||||||
const elemY = mmToPx(element.y, zoom);
|
const elemY = mmToPx(element.y, displayZoom);
|
||||||
const elemW = mmToPx(element.width, zoom);
|
const elemW = mmToPx(element.width, displayZoom);
|
||||||
const elemH = mmToPx(element.height, zoom);
|
const elemH = mmToPx(element.height, displayZoom);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -649,6 +649,15 @@ body {
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ps-layer-item-locked {
|
||||||
|
cursor: default;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ps-layer-item-locked:hover {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.ps-marquee-select {
|
.ps-marquee-select {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border: 1px solid #31a8ff;
|
border: 1px solid #31a8ff;
|
||||||
|
|||||||
Reference in New Issue
Block a user