-
-
-
-
- Инструкция
-
-
- - Клик у края: Новая линия
- - Перетаскивание: Изменить размер
- - Двойной клик/ПКМ: Удалить
-
-
-
-
- 0
- {config.drawer.width} мм
-
-
-
-
- 0
- {config.drawer.depth} мм
-
+ {/* INFO BAR */}
+
+
+ {mode === 'lines' ? (
+
+ ЛКМ: Линия
+ ПКМ: Удалить
+
+ ) : (
+
+ ЛКМ в ячейке: Стенка (T-соединения)
+ Драг: Двигать
+ 2xЛКМ: Удалить
+
+ )}
+
+ {/* WORKSPACE */}
+
+
1 ? 'auto' : '100%',
+ height: aspectRatio > 1 ? '100%' : 'auto',
aspectRatio: `${1/aspectRatio}`,
- cursor: dragging ? 'grabbing' : hoveredSplit ? 'grab' : 'crosshair',
- maxHeight: '75vh'
+ maxHeight: '100%', maxWidth: '100%',
+ cursor: mode === 'lines' ? (dragging ? 'grabbing' : hoveredMainSplit ? 'col-resize' : 'crosshair')
+ : (dragging ? 'grabbing' : hoveredPartition ? 'grab' : hoveredCell ? 'crosshair' : 'default'),
}}
>
-
+
+ {/* --- SIDEBAR FOR EDITING --- */}
+ {mode === 'cells' && selectedData && (
+
+
+
+ Настройки стенки
+
+
+
+
+
+ {/* Height */}
+
+
+ Высота {selectedData.part.height} мм
+
+
updatePartition(selectedData.key, selectedData.part.id, { height: parseFloat(e.target.value) })}
+ className="w-full h-1 bg-slate-600 rounded-lg appearance-none cursor-pointer accent-purple-500"
+ />
+
+
+ {/* Rounded */}
+
+
+ updatePartition(selectedData.key, selectedData.part.id, { rounded: e.target.checked })}
+ className="w-4 h-4 rounded bg-slate-700 border-slate-600 text-purple-500 focus:ring-0 cursor-pointer"
+ />
+
+
+ {/* Delete */}
+
+
+
+
+ Выделите стенку для настройки.
Двойной клик удаляет её.
+
+
+ )}
-
);
};
\ No newline at end of file
diff --git a/src/components/PreviewStep.tsx b/src/components/PreviewStep.tsx
index c9f9aca..8334b76 100644
--- a/src/components/PreviewStep.tsx
+++ b/src/components/PreviewStep.tsx
@@ -8,7 +8,7 @@ import { createBinGeometry, generateSTL, exportSTL } from '../services/geometryG
import { Download, Package, Info, Loader2, Share2, Check, Ruler } from 'lucide-react';
import { generateShareUrl } from '../utils/share';
-// --- DrawerFrame (Каркас) ---
+// --- DrawerFrame (Каркас ящика) ---
const DrawerFrame = ({ config }: { config: AppConfig }) => {
const { width, depth, height } = config.drawer;
const offset = 0.5;
@@ -32,25 +32,37 @@ interface BinMeshProps {
}
const BinMesh: React.FC
= ({ part, thickness, cornerRadius, isSelected, onClick }) => {
+ // 1. Создаем геометрию, учитывая ВНУТРЕННИЕ ПЕРЕГОРОДКИ
const geometry = useMemo(() => {
- return createBinGeometry(part.width, part.depth, part.height, thickness, cornerRadius);
+ return createBinGeometry(
+ part.width,
+ part.depth,
+ part.height,
+ thickness,
+ cornerRadius,
+ part.internalPartitions // <--- ВАЖНО: передаем перегородки в генератор
+ );
}, [part, thickness, cornerRadius]);
+ // 2. Создаем контур выделения (EdgesGeometry)
+ // Threshold 20 градусов скрывает линии на плавных скруглениях
const edgesGeometry = useMemo(() => {
return new THREE.EdgesGeometry(geometry, 20);
}, [geometry]);
return (
+ {/* Сама модель */}
{ e.stopPropagation(); onClick(); }}>
+ {/* Белая подсветка при выборе */}
{isSelected && (
@@ -60,7 +72,7 @@ const BinMesh: React.FC = ({ part, thickness, cornerRadius, isSele
);
};
-// --- PreviewStep (Основной) ---
+// --- PreviewStep (Основной компонент) ---
interface Props {
parts: GeneratedPart[];
config: AppConfig;
@@ -73,25 +85,42 @@ export const PreviewStep: React.FC = ({ parts, config, splits }) => {
const [shareUrlCopied, setShareUrlCopied] = useState(false);
const itemRefs = useRef<{ [key: string]: HTMLDivElement | null }>({});
+ // Скролл к выбранной детали в списке
useEffect(() => {
if (selectedId && itemRefs.current[selectedId]) {
itemRefs.current[selectedId]?.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}, [selectedId]);
+ // Скачивание одной детали
const handleDownload = (part: GeneratedPart) => {
- const geometry = createBinGeometry(part.width, part.depth, part.height, config.wallThickness, config.cornerRadius);
+ const geometry = createBinGeometry(
+ part.width,
+ part.depth,
+ part.height,
+ config.wallThickness,
+ config.cornerRadius,
+ part.internalPartitions // <--- ВАЖНО для STL
+ );
const mesh = new THREE.Mesh(geometry, new THREE.MeshStandardMaterial());
exportSTL(mesh, `${part.name.replace(/\s+/g, '_')}.stl`);
};
+ // Скачивание всего архивом
const handleDownloadAll = async () => {
if (isZipping) return;
setIsZipping(true);
try {
const zip = new JSZip();
parts.forEach(part => {
- const geometry = createBinGeometry(part.width, part.depth, part.height, config.wallThickness, config.cornerRadius);
+ const geometry = createBinGeometry(
+ part.width,
+ part.depth,
+ part.height,
+ config.wallThickness,
+ config.cornerRadius,
+ part.internalPartitions // <--- ВАЖНО для STL
+ );
const mesh = new THREE.Mesh(geometry, new THREE.MeshStandardMaterial());
const stlData = generateSTL(mesh);
zip.file(`${part.name.replace(/\s+/g, '_')}.stl`, stlData);
@@ -110,6 +139,7 @@ export const PreviewStep: React.FC = ({ parts, config, splits }) => {
}
};
+ // Поделиться ссылкой
const handleShare = async () => {
const url = generateShareUrl(config, splits);
let success = false;
@@ -143,8 +173,9 @@ export const PreviewStep: React.FC = ({ parts, config, splits }) => {
return (
- {/* Верхняя панель */}
+ {/* Верхняя панель: Размеры + Поделиться */}
+
@@ -166,6 +197,7 @@ export const PreviewStep: React.FC
= ({ parts, config, splits }) => {
мм
+