This commit is contained in:
iqudoo
2026-06-12 20:58:51 +08:00
parent c9a187bf9b
commit 239e2eeebd
9 changed files with 499 additions and 340 deletions

View File

@@ -10,14 +10,12 @@ import {
} from '../utils';
import { VisibilityRuleDialog } from './VisibilityRuleDialog';
import { VisibilityRuleSummary } from './VisibilityRuleSummary';
import { toggleSide, isSideEnabled } from '../elementBox';
import {
alignToSelectionBounds,
alignSelectionToCanvas,
tileSelectedEvenly,
type AlignMode,
} from '../elementAlign';
import type { ElementSide } from '../types';
import { CollapsiblePanelSection } from './CollapsiblePanelSection';
import { CommitInput } from './CommitInput';
import {
@@ -779,7 +777,64 @@ export const PropSidebar: React.FC<PropSidebarProps> = ({
/>
</div>
{/* Text Formatting Controls */}
{/* Alignment Tools */}
<div className="space-y-1.5">
<h5 className="ps-section-title">
</h5>
<div className="grid grid-cols-3 gap-1.5">
<AlignToolButton
icon={<AlignHorizontalJustifyStart className="w-3.5 h-3.5" />}
label="左对齐"
title="靠左对齐 (X=0)"
onClick={() => handleElemChange({ ...selectedElem, x: 0 })}
/>
<AlignToolButton
icon={<AlignHorizontalJustifyCenter className="w-3.5 h-3.5" />}
label="水平居中"
title="水平居中对齐"
onClick={() => {
const newX = Math.round(((template.width - selectedElem.width) / 2) * 10) / 10;
handleElemChange({ ...selectedElem, x: Math.max(0, newX) });
}}
/>
<AlignToolButton
icon={<AlignHorizontalJustifyEnd className="w-3.5 h-3.5" />}
label="右对齐"
title="靠右对齐"
onClick={() => {
const newX = Math.round((template.width - selectedElem.width) * 10) / 10;
handleElemChange({ ...selectedElem, x: Math.max(0, newX) });
}}
/>
<AlignToolButton
icon={<AlignVerticalJustifyStart className="w-3.5 h-3.5" />}
label="顶对齐"
title="靠顶对齐 (Y=0)"
onClick={() => handleElemChange({ ...selectedElem, y: 0 })}
/>
<AlignToolButton
icon={<AlignVerticalJustifyCenter className="w-3.5 h-3.5" />}
label="垂直居中"
title="垂直居中对齐"
onClick={() => {
const newY = Math.round(((template.height - selectedElem.height) / 2) * 10) / 10;
handleElemChange({ ...selectedElem, y: Math.max(0, newY) });
}}
/>
<AlignToolButton
icon={<AlignVerticalJustifyEnd className="w-3.5 h-3.5" />}
label="底对齐"
title="靠底对齐"
onClick={() => {
const newY = Math.round((template.height - selectedElem.height) * 10) / 10;
handleElemChange({ ...selectedElem, y: Math.max(0, newY) });
}}
/>
</div>
</div>
{/* Text Settings */}
{selectedElem.type === 'text' && (
<div className="space-y-3 ps-section-divider">
<h5 className="ps-section-title">
@@ -1096,7 +1151,7 @@ export const PropSidebar: React.FC<PropSidebarProps> = ({
</div>
)}
{/* Barcode Formats Options */}
{/* Barcode Settings */}
{selectedElem.type === 'barcode' && (
<div className="space-y-3 ps-section-divider">
<h5 className="ps-section-title">
@@ -1134,6 +1189,7 @@ export const PropSidebar: React.FC<PropSidebarProps> = ({
</div>
)}
{/* Image Settings */}
{selectedElem.type === 'image' && (
<div className="space-y-3 ps-section-divider">
<h5 className="ps-section-title">
@@ -1367,7 +1423,7 @@ export const PropSidebar: React.FC<PropSidebarProps> = ({
className="ps-range"
/>
</div>
<div className="border-t border-[#3a3a3a] pt-2.5">
<div className="border-t border-[#3a3a3a] pt-2.5 space-y-2">
<label className="ps-field-label"> (mm)</label>
<PropInput
type="number"
@@ -1383,6 +1439,41 @@ export const PropSidebar: React.FC<PropSidebarProps> = ({
}
className="ps-field font-mono"
/>
<div>
<label className="ps-field-label"> (mm)</label>
<div className="grid grid-cols-2 gap-1.5">
{(
[
['paddingTop', '上'],
['paddingRight', '右'],
['paddingBottom', '下'],
['paddingLeft', '左'],
] as const
).map(([key, label]) => (
<div key={key} className="flex items-center gap-1">
<span className="text-[9px] text-[#777] w-6 shrink-0">{label}</span>
<PropInput
type="number"
min="0"
max="10"
step="0.5"
value={selectedElem[key] ?? ''}
onCommit={(val) => {
const updated = { ...selectedElem };
if (val === '' || val === undefined) {
delete updated[key];
} else {
updated[key] = Math.max(0, Math.min(10, Number(val) || 0));
}
handleElemChange(updated);
}}
className="ps-field font-mono text-[10px] flex-1 min-w-0"
placeholder="—"
/>
</div>
))}
</div>
</div>
</div>
</div>
@@ -1459,39 +1550,85 @@ export const PropSidebar: React.FC<PropSidebarProps> = ({
</div>
<div>
<label className="ps-field-label mb-1"></label>
<div className="grid grid-cols-4 gap-1">
<label className="ps-field-label"> (mm0 )</label>
<div className="grid grid-cols-2 gap-1.5">
{(
[
['top', '上'],
['right', '右'],
['bottom', '下'],
['left', '左'],
] as [ElementSide, string][]
).map(([side, label]) => (
<button
key={side}
type="button"
onClick={() =>
handleElemChange({
...selectedElem,
borderSides: toggleSide(selectedElem.borderSides, side),
borderWidth:
(selectedElem.borderWidth ?? 0) > 0
? selectedElem.borderWidth
: 0.3,
borderColor: selectedElem.borderColor || '#111827',
})
}
className={`ps-btn text-[10px] justify-center ${
isSideEnabled(selectedElem.borderSides, side) &&
(selectedElem.borderWidth ?? 0) > 0
? 'active'
: ''
}`}
>
{label}
</button>
['borderWidthTop', '上'],
['borderWidthRight', '右'],
['borderWidthBottom', '下'],
['borderWidthLeft', '左'],
] as const
).map(([key, label]) => (
<div key={key} className="flex items-center gap-1">
<span className="text-[9px] text-[#777] w-6 shrink-0">{label}</span>
<PropInput
type="number"
min="0"
max="5"
step="0.1"
value={selectedElem[key] ?? ''}
onCommit={(val) => {
const updated = { ...selectedElem };
if (val === '' || val === undefined) {
delete updated[key];
} else {
updated[key] = Math.max(0, Math.min(5, Number(val) || 0));
}
handleElemChange(updated);
}}
className="ps-field font-mono text-[10px] flex-1 min-w-0"
placeholder="—"
/>
</div>
))}
</div>
</div>
<div>
<label className="ps-field-label"> ()</label>
<div className="grid grid-cols-2 gap-1.5">
{(
[
['borderColorTop', '上'],
['borderColorRight', '右'],
['borderColorBottom', '下'],
['borderColorLeft', '左'],
] as const
).map(([key, label]) => (
<div key={key} className="flex items-center gap-1 min-w-0">
<span className="text-[9px] text-[#777] w-6 shrink-0">{label}</span>
<input
type="color"
value={
selectedElem[key]?.trim() ||
selectedElem.borderColor ||
'#111827'
}
onChange={(e) => {
const updated = { ...selectedElem };
updated[key] = e.target.value;
handleElemChange(updated);
}}
className="color-input shrink-0 scale-90"
title={`${label}边边框颜色`}
/>
<PropInput
type="text"
value={selectedElem[key] ?? ''}
onCommit={(val) => {
const updated = { ...selectedElem };
if (val === '' || val === undefined) {
delete updated[key];
} else {
updated[key] = val;
}
handleElemChange(updated);
}}
className="ps-field font-mono text-[10px] flex-1 min-w-0"
placeholder="—"
/>
</div>
))}
</div>
</div>
@@ -1533,62 +1670,6 @@ export const PropSidebar: React.FC<PropSidebarProps> = ({
</div>
</div>
{/* Alignment Tools */}
<div className="space-y-1.5">
<h5 className="ps-section-title">
</h5>
<div className="grid grid-cols-3 gap-1.5">
<AlignToolButton
icon={<AlignHorizontalJustifyStart className="w-3.5 h-3.5" />}
label="左对齐"
title="靠左对齐 (X=0)"
onClick={() => handleElemChange({ ...selectedElem, x: 0 })}
/>
<AlignToolButton
icon={<AlignHorizontalJustifyCenter className="w-3.5 h-3.5" />}
label="水平居中"
title="水平居中对齐"
onClick={() => {
const newX = Math.round(((template.width - selectedElem.width) / 2) * 10) / 10;
handleElemChange({ ...selectedElem, x: Math.max(0, newX) });
}}
/>
<AlignToolButton
icon={<AlignHorizontalJustifyEnd className="w-3.5 h-3.5" />}
label="右对齐"
title="靠右对齐"
onClick={() => {
const newX = Math.round((template.width - selectedElem.width) * 10) / 10;
handleElemChange({ ...selectedElem, x: Math.max(0, newX) });
}}
/>
<AlignToolButton
icon={<AlignVerticalJustifyStart className="w-3.5 h-3.5" />}
label="顶对齐"
title="靠顶对齐 (Y=0)"
onClick={() => handleElemChange({ ...selectedElem, y: 0 })}
/>
<AlignToolButton
icon={<AlignVerticalJustifyCenter className="w-3.5 h-3.5" />}
label="垂直居中"
title="垂直居中对齐"
onClick={() => {
const newY = Math.round(((template.height - selectedElem.height) / 2) * 10) / 10;
handleElemChange({ ...selectedElem, y: Math.max(0, newY) });
}}
/>
<AlignToolButton
icon={<AlignVerticalJustifyEnd className="w-3.5 h-3.5" />}
label="底对齐"
title="靠底对齐"
onClick={() => {
const newY = Math.round((template.height - selectedElem.height) * 10) / 10;
handleElemChange({ ...selectedElem, y: Math.max(0, newY) });
}}
/>
</div>
</div>
</div>
)}