@@ -1394,6 +1500,9 @@ function planningCanvas() {
_connTick: 0,
// Storyboard
+ sbMenuEditorOpen: false,
+ sbMenuDraft: [],
+ _sbMenuDrag: null,
sb: {
docInfo: { projectName: '', unitTask: '', version: 'D1.0' },
menuTree: [
@@ -2138,17 +2247,85 @@ function planningCanvas() {
},
sbEditMenu() {
- const json = JSON.stringify(this.sb.menuTree, null, 2);
- const input = prompt('메뉴 트리 JSON을 편집하세요:\n(취소하면 변경 없음)', json);
- if (input === null) return;
- try {
- this.sb.menuTree = JSON.parse(input);
- this.autoSave();
- } catch (err) {
- alert('JSON 형식이 올바르지 않습니다.');
+ // deep copy menuTree → draft (+ _open 플래그 추가)
+ this.sbMenuDraft = JSON.parse(JSON.stringify(this.sb.menuTree)).map(m => {
+ m.children = m.children || [];
+ m._open = true;
+ return m;
+ });
+ this.sbMenuEditorOpen = true;
+ },
+
+ sbMenuApply() {
+ // _open 플래그 제거 후 적용
+ this.sb.menuTree = this.sbMenuDraft.map(m => {
+ const { _open, ...rest } = m;
+ rest.children = (rest.children || []).map(c => ({ name: c.name }));
+ return rest;
+ }).filter(m => m.name.trim() !== '');
+ this.sbMenuEditorOpen = false;
+ this.autoSave();
+ },
+
+ sbMenuToggle(mi) {
+ this.sbMenuDraft[mi]._open = this.sbMenuDraft[mi]._open === false ? true : false;
+ },
+
+ sbMenuAddChild(mi) {
+ if (!this.sbMenuDraft[mi].children) this.sbMenuDraft[mi].children = [];
+ this.sbMenuDraft[mi].children.push({ name: '' });
+ this.sbMenuDraft[mi]._open = true;
+ },
+
+ sbMenuRemove(mi) {
+ if (this.sbMenuDraft[mi].children?.length > 0 && !confirm('하위 메뉴도 함께 삭제됩니다. 계속하시겠습니까?')) return;
+ this.sbMenuDraft.splice(mi, 1);
+ },
+
+ sbMenuDragStart(level, key, e) {
+ this._sbMenuDrag = { level, key };
+ e.dataTransfer.effectAllowed = 'move';
+ },
+
+ sbMenuDragOver(level, key, e) {
+ if (!this._sbMenuDrag) return;
+ e.dataTransfer.dropEffect = 'move';
+ },
+
+ sbMenuDrop(level, key, e) {
+ if (!this._sbMenuDrag) return;
+ const src = this._sbMenuDrag;
+ this._sbMenuDrag = null;
+ if (src.level !== level) return;
+
+ if (level === 'root') {
+ const fromIdx = parseInt(src.key);
+ const toIdx = parseInt(key);
+ if (fromIdx === toIdx) return;
+ const item = this.sbMenuDraft.splice(fromIdx, 1)[0];
+ this.sbMenuDraft.splice(toIdx, 0, item);
+ } else {
+ // child: key format = "parentIdx-childIdx"
+ const [spi, sci] = src.key.split('-').map(Number);
+ const [dpi, dci] = key.split('-').map(Number);
+ if (spi === dpi && sci === dci) return;
+ if (spi === dpi) {
+ // 같은 부모 내 이동
+ const children = this.sbMenuDraft[spi].children;
+ const item = children.splice(sci, 1)[0];
+ children.splice(dci, 0, item);
+ } else {
+ // 다른 부모로 이동
+ const item = this.sbMenuDraft[spi].children.splice(sci, 1)[0];
+ this.sbMenuDraft[dpi].children.splice(dci, 0, item);
+ }
}
},
+ sbMenuDragEnd() {
+ this._sbMenuDrag = null;
+ },
+
sbExportHtml() {
let html = '
' +
(this.sb.docInfo.projectName || 'Storyboard') + '' +