feat: импорт Roblox .rbxl карт (тест-фича МИНа) #33
@ -269,24 +269,51 @@ const HierarchyPanel = ({
|
||||
const [renaming, setRenaming] = useState(null);
|
||||
|
||||
// Авто-скролл к выбранному элементу: когда юзер выделяет объект в 3D-сцене
|
||||
// (или приходит выделение извне) — прокручиваем иерархию к нему.
|
||||
// Работает по data-sel-id который ItemRow получает через style (см. ниже).
|
||||
// (или приходит выделение извне) — раскрываем родительские папки и
|
||||
// прокручиваем иерархию к нему.
|
||||
const hierarchyRootRef = useRef(null);
|
||||
useEffect(() => {
|
||||
if (!selection) return;
|
||||
const root = hierarchyRootRef.current;
|
||||
if (!root) return;
|
||||
let selId = null;
|
||||
if (selection.type === 'primitive') selId = `primitive:${selection.id}`;
|
||||
else if (selection.type === 'model') selId = `model:${selection.instanceId}`;
|
||||
else if (selection.type === 'block') selId = `block:${selection.gridX},${selection.gridY},${selection.gridZ}`;
|
||||
if (!selId) return;
|
||||
// querySelector через CSS.escape для безопасности с двоеточием и запятыми
|
||||
const el = root.querySelector(`[data-sel-id="${CSS.escape(selId)}"]`);
|
||||
if (el && typeof el.scrollIntoView === 'function') {
|
||||
el.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
||||
|
||||
// 1) Раскрываем ВСЁ что может скрывать выбранный объект:
|
||||
// - все папки (folders) - вдруг примитив сидит в Folder
|
||||
// - rootPrimsOpen - группа "Примитивы" в корне
|
||||
// - sceneOpen — корень сцены
|
||||
if (folders && folders.length > 0) {
|
||||
setOpenFolders(prev => {
|
||||
const next = new Set(prev);
|
||||
let changed = false;
|
||||
for (const f of folders) {
|
||||
if (!next.has(f.id)) { next.add(f.id); changed = true; }
|
||||
}
|
||||
return changed ? next : prev;
|
||||
});
|
||||
}
|
||||
}, [selection?.type, selection?.id, selection?.instanceId, selection?.gridX, selection?.gridY, selection?.gridZ]);
|
||||
if (!workspaceOpen) setWorkspaceOpen(true);
|
||||
if (selection.type === 'primitive' && !rootPrimsOpen) setRootPrimsOpen(true);
|
||||
if (selection.type === 'block' && !rootBlocksOpen) setRootBlocksOpen(true);
|
||||
if (selection.type === 'model' && !rootModelsOpen) setRootModelsOpen(true);
|
||||
|
||||
// 2) Скролл через 2 кадра (даём React перерендерить после раскрытия)
|
||||
const tick = () => {
|
||||
const root = hierarchyRootRef.current;
|
||||
if (!root) return;
|
||||
const el = root.querySelector(`[data-sel-id="${CSS.escape(selId)}"]`);
|
||||
if (el && typeof el.scrollIntoView === 'function') {
|
||||
el.scrollIntoView({ block: 'center', behavior: 'smooth' });
|
||||
}
|
||||
};
|
||||
const raf1 = requestAnimationFrame(() => {
|
||||
const raf2 = requestAnimationFrame(tick);
|
||||
return () => cancelAnimationFrame(raf2);
|
||||
});
|
||||
return () => cancelAnimationFrame(raf1);
|
||||
}, [selection?.type, selection?.id, selection?.instanceId, selection?.gridX, selection?.gridY, selection?.gridZ, folders?.length, workspaceOpen, rootPrimsOpen, rootBlocksOpen, rootModelsOpen, openFolders]);
|
||||
|
||||
const startRename = (kind, refKey, currentValue) => {
|
||||
setRenaming({ kind, refKey, value: currentValue || '' });
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user