폴더 및 하위 파일 업로드

This commit is contained in:
sotam0316
2026-04-22 21:11:57 +09:00
commit 484b1d2a44
209 changed files with 29552 additions and 0 deletions
@@ -0,0 +1,95 @@
import { state } from '../../state.js';
import { logger } from '../../utils/logger.js';
import { t } from '../../i18n.js';
import { getX6NodeConfig } from '../styles.js';
/**
* initDropHandling - Sets up drag and drop from the asset library to the graph
*/
export function initDropHandling(container) {
container.addEventListener('dragover', (e) => e.preventDefault());
container.addEventListener('drop', (e) => {
e.preventDefault();
const assetId = e.dataTransfer.getData('assetId');
const asset = state.assetMap[assetId];
const pos = state.graph.clientToLocal(e.clientX, e.clientY);
logger.info(`Drop Event at {${pos.x}, ${pos.y}}`, {
assetId: assetId,
found: !!asset,
id: asset?.id,
path: asset?.path,
type: asset?.type,
rawAsset: asset
});
if (!asset) {
logger.high(`Asset [${assetId}] not found in map`);
return;
}
// Logical Layer Guard: Block dropping new nodes
const activeLayer = state.layers.find(l => l.id === state.activeLayerId);
if (activeLayer && activeLayer.type === 'logical') {
import('../../ui/utils.js').then(m => m.showToast(t('err_logical_layer_drop'), 'warning'));
return;
}
const isPrimitive = ['rect', 'rounded-rect', 'circle', 'text-box', 'label'].includes(asset.type);
const prefix = asset.id === 'fixed-group' ? 'group_' : (asset.type === 'blank' || asset.type === 'dot' ? 'p_' : (isPrimitive ? 'shp_' : 'node_'));
const id = prefix + Math.random().toString(36).substr(2, 4);
// Use translate keys or asset label first, or fallback to generic name
let initialLabel = asset.label || t(asset.id) || (asset.id === 'fixed-group' ? 'New Group' : 'New ' + (asset.type || 'Object'));
if (asset.id === 'fixed-group') initialLabel = t('group') || 'New Group';
// Find if dropped over a group (prefer the smallest/innermost one)
const candidates = state.graph.getNodes().filter(n => {
if (!n.getData()?.is_group) return false;
const bbox = n.getBBox();
return bbox.containsPoint(pos);
});
const parent = candidates.length > 0
? candidates.reduce((smallest, current) => {
const smallestBBox = smallest.getBBox();
const currentBBox = current.getBBox();
const currentArea = currentBBox.width * currentBBox.height;
const smallestArea = smallestBBox.width * smallestBBox.height;
if (currentArea < smallestArea) return current;
if (currentArea > smallestArea) return smallest;
// If areas are equal, pick the one that is a descendant of the other
if (current.getAncestors().some(a => a.id === smallest.id)) return current;
return smallest;
})
: undefined;
// 2. Add to X6 immediately
const config = getX6NodeConfig({
id: id,
label: initialLabel,
type: asset.type || asset.category || asset.label || 'Unknown',
assetId: assetId, // Store unique composite ID for lookup
assetPath: asset.id === 'fixed-group' ? undefined : asset.path,
is_group: asset.id === 'fixed-group' || asset.is_group === true,
pos: { x: pos.x, y: pos.y },
parent: parent ? parent.id : undefined,
layerId: state.activeLayerId,
description: "",
asset_tag: "",
tags: []
});
const newNode = state.graph.addNode(config);
if (parent) {
parent.addChild(newNode);
}
// Ensure the new node follows the active layer's interaction rules immediately
import('../layers.js').then(m => m.applyLayerFilters());
});
}