ide-assi 0.646.0 → 0.648.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11159,6 +11159,29 @@ class ninegrid {
11159
11159
  }, intervalTime);
11160
11160
  });
11161
11161
  };
11162
+
11163
+ static delayQuerySelector = (selector, root = document, maxAttempts = 3, intervalTime = 100) => {
11164
+ return new Promise((resolve, reject) => {
11165
+ let attempts = 0;
11166
+ const interval = setInterval(() => {
11167
+ const el = ninegrid.querySelector(selector, root);
11168
+
11169
+ if (el) {
11170
+ //console.log("InnerHTML 확인됨, init() 실행 가능!");
11171
+ clearInterval(interval);
11172
+ resolve(el);
11173
+ } else {
11174
+ attempts++;
11175
+ //console.log(`InnerHTML이 비어 있음, ${attempts}/${maxAttempts}번 재시도`);
11176
+
11177
+ if (attempts >= maxAttempts) {
11178
+ clearInterval(interval);
11179
+ reject("Element를 찾지 못했습니다.");
11180
+ }
11181
+ }
11182
+ }, intervalTime);
11183
+ });
11184
+ };
11162
11185
 
11163
11186
  static copyProperty = (target, source, properties) => {
11164
11187
 
@@ -11270,6 +11293,20 @@ class ninegrid {
11270
11293
  return formatedDate;
11271
11294
  };
11272
11295
 
11296
+ static goto = (selector, params) => {
11297
+
11298
+ const el = document.querySelector(selector);
11299
+ if (!el) return;
11300
+
11301
+ document.querySelectorAll(".list-wrapper.active,[class*='detail-wrapper-'].active").forEach((el) => {
11302
+ el.classList.remove("active");
11303
+ });
11304
+
11305
+ el.classList.add("active");
11306
+
11307
+ return el;
11308
+ };
11309
+
11273
11310
 
11274
11311
  static context;
11275
11312
  static measureTextSize = (_text, _font) => {
@@ -11514,6 +11551,14 @@ class ninegrid {
11514
11551
  return null; // 없으면 null 반환
11515
11552
  };
11516
11553
 
11554
+ static setOptions = (k,o) => {
11555
+ for (const prop in o) {
11556
+ if (ninegrid.options[k][prop]) {
11557
+ ninegrid.options[k][prop] = o[prop];
11558
+ }
11559
+ }
11560
+ };
11561
+
11517
11562
  static confirm(message, title, options) {
11518
11563
  return nxConfirmPopup.confirm(message, title, options);
11519
11564
  };
@@ -11522,6 +11567,62 @@ class ninegrid {
11522
11567
  return nxAlertPopup.alert(message, title, options);
11523
11568
  };
11524
11569
 
11570
+ static loading = {
11571
+ show: () => {
11572
+ let overlay = document.getElementById("global-loading-overlay");
11573
+ if (!overlay) {
11574
+ overlay = document.createElement("div");
11575
+ overlay.id = "global-loading-overlay";
11576
+ overlay.className = "loading-overlay";
11577
+
11578
+ overlay.style.position = "fixed";
11579
+ overlay.style.inset = "0";
11580
+ overlay.style.zIndex = "9999";
11581
+ overlay.style.background = "rgba(0, 0, 0, 0.4)";
11582
+ overlay.style.color = "white";
11583
+ overlay.style.display = "flex";
11584
+ overlay.style.justifyContent = "center";
11585
+ overlay.style.alignItems = "center";
11586
+ overlay.style.fontSize = "20px";
11587
+ overlay.style.pointerEvents = "all";
11588
+ overlay.style.backdropFilter = "blur(2px)";
11589
+ overlay.innerHTML = `
11590
+ <div style="text-align: center;">
11591
+ <div class="loading-spinner"></div>
11592
+ </div>
11593
+ `;
11594
+
11595
+ document.body.appendChild(overlay);
11596
+
11597
+ // CSS 스피너 스타일 삽입
11598
+ const style = document.createElement("style");
11599
+ style.innerHTML = `
11600
+ .loading-spinner {
11601
+ width: 48px;
11602
+ height: 48px;
11603
+ border: 5px solid rgba(255, 255, 255, 0.3);
11604
+ border-top-color: white;
11605
+ border-radius: 50%;
11606
+ animation: spin 1s linear infinite;
11607
+ }
11608
+
11609
+ @keyframes spin {
11610
+ to { transform: rotate(360deg); }
11611
+ }
11612
+ `;
11613
+
11614
+ document.head.appendChild(style);
11615
+ }
11616
+
11617
+ overlay.style.display = "flex";
11618
+ },
11619
+ hide: () => {
11620
+ const overlay = document.getElementById("global-loading-overlay");
11621
+ if (overlay) overlay.style.display = "none";
11622
+ },
11623
+ };
11624
+
11625
+
11525
11626
  static i18n = {
11526
11627
  convertArrayToJSON : (arr) => {
11527
11628
  let result = {};
@@ -118014,6 +118115,7 @@ customElements.define("ng-filter-panel", ngFilterPanel);
118014
118115
 
118015
118116
  class NineGridContainer extends HTMLElement
118016
118117
  {
118118
+ #created = false;
118017
118119
  #colResizer;
118018
118120
  #colMover;
118019
118121
  #rowMover;
@@ -118494,7 +118596,9 @@ class NineGridContainer extends HTMLElement
118494
118596
  };
118495
118597
 
118496
118598
  #initialize = () => {
118497
-
118599
+
118600
+ if (this.#created) return;
118601
+
118498
118602
  this.originFormat = this.outerHTML;
118499
118603
 
118500
118604
  this.uuid = ninegrid.randomUUID(); //crypto.randomUUID();
@@ -118813,6 +118917,7 @@ class NineGridContainer extends HTMLElement
118813
118917
  this.addEventListener(handler.type, handler.listener);
118814
118918
  } */
118815
118919
 
118920
+ this.#created = true;
118816
118921
  };
118817
118922
 
118818
118923
  #initOption = () => {
@@ -119313,18 +119418,18 @@ class nxDialog extends HTMLElement
119313
119418
  {
119314
119419
  //#owner;
119315
119420
  #shift;
119316
-
119421
+
119317
119422
  constructor () {
119318
119423
  super();
119319
119424
  }
119320
-
119425
+
119321
119426
  connectedCallback() {
119322
119427
 
119323
119428
  const v = this.innerHTML;
119324
119429
  const cssFile = this.getAttribute("css-file");
119325
119430
  const customFile1 = cssFile ? `@import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/${cssFile}";`: "";
119326
119431
  const customFile2 = cssFile ? ninegrid.getCustomPath(this, cssFile) : "";
119327
-
119432
+
119328
119433
  //console.log(ninegrid.getCustomPath(this,"nxDialog.css"));
119329
119434
 
119330
119435
  this.innerHTML = `
@@ -119364,18 +119469,18 @@ class nxDialog extends HTMLElement
119364
119469
  </div>
119365
119470
  </dialog>
119366
119471
  `;
119367
-
119472
+
119368
119473
  //this.#owner = this.getRootNode().host.closest("nine-grid");
119369
-
119474
+
119370
119475
  $(".title", this).html("Details");
119371
-
119476
+
119372
119477
  this.#init();
119373
119478
  };
119374
-
119479
+
119375
119480
  showModal = () => {
119376
119481
  this.querySelector('dialog').showModal();
119377
119482
  };
119378
-
119483
+
119379
119484
  close = () => {
119380
119485
  $(".head", this) .off('mousedown', this.#onMouseDown);
119381
119486
  $(".head", this) .off('touchstart', this.#onTouchStart);
@@ -119383,29 +119488,39 @@ class nxDialog extends HTMLElement
119383
119488
  $(document) .off('touchend', this.#onTouchEnd);
119384
119489
  $(document) .off('mousemove', this.#onMouseMove);
119385
119490
  $(document) .off("touchmove", this.#onTouchMove);
119386
-
119491
+
119387
119492
  this.querySelector('dialog').close();
119388
119493
  };
119389
-
119494
+
119390
119495
  #init = () => {
119391
- $(".close,.close2", this).on("click", e => {
119496
+ /**
119497
+ $(".close,.close2", this).on("click", e => {
119498
+ ninegrid.j.querySelectorAll('dialog', this).addClass("out");
119499
+ setTimeout(() => { this.close(); }, 300);
119500
+ }); */
119501
+
119502
+ $(".close", this).on("click", e => {
119392
119503
  ninegrid.j.querySelectorAll('dialog', this).addClass("out");
119393
119504
  setTimeout(() => { this.close(); }, 300);
119394
119505
  });
119395
-
119506
+
119507
+ $(".close2", this).on("click", e => {
119508
+ this.close();
119509
+ });
119510
+
119396
119511
  $(".head", this) .on('mousedown', this.#onMouseDown);
119397
119512
  $(".head", this) .on('touchstart', this.#onTouchStart);
119398
119513
  $(document) .on('mouseup', this.#onMouseUp);
119399
119514
  $(document) .on('touchend', this.#onTouchEnd);
119400
119515
  };
119401
-
119516
+
119402
119517
 
119403
119518
  #onMouseDown = e => {
119404
119519
  if (e.target.closest("buttons")) return;
119405
119520
  if (e.buttons != 1 || e.altKey || e.ctrlKey || e.shiftkey) return;
119406
119521
 
119407
119522
  var rect = this.querySelector('dialog').getBoundingClientRect();
119408
-
119523
+
119409
119524
  this.#shift = {
119410
119525
  x : e.clientX - rect.x,
119411
119526
  y : e.clientY - rect.y,
@@ -119413,33 +119528,33 @@ class nxDialog extends HTMLElement
119413
119528
 
119414
119529
  $(document).on('mousemove', this.#onMouseMove);
119415
119530
  };
119416
-
119417
-
119531
+
119532
+
119418
119533
  #onMouseMove = e => {
119419
119534
  $(this.querySelector('dialog')).offset( { left : e.pageX - this.#shift.x, top : e.pageY - this.#shift.y });
119420
119535
  };
119421
-
119536
+
119422
119537
  #onMouseUp = e => {
119423
119538
  $(document).off('mousemove', this.#onMouseMove);
119424
119539
  };
119425
-
119540
+
119426
119541
  #onTouchStart = e => {
119427
119542
  if (e.target.closest("buttons")) return;
119428
-
119543
+
119429
119544
  var rect = this.querySelector('dialog').getBoundingClientRect();
119430
-
119545
+
119431
119546
  this.#shift = {
119432
119547
  x : e.changedTouches[0].pageX - rect.x,
119433
119548
  y : e.changedTouches[0].pageY - rect.y,
119434
119549
  };
119435
-
119550
+
119436
119551
  $(document).on("touchmove", this.#onTouchMove);
119437
119552
  };
119438
-
119553
+
119439
119554
  #onTouchMove = e => {
119440
119555
  $(this.querySelector('dialog')).offset( { left : e.changedTouches[0].pageX - this.#shift.x, top : e.changedTouches[0].pageY - this.#shift.y });
119441
119556
  };
119442
-
119557
+
119443
119558
  #onTouchEnd = e => {
119444
119559
  $(document).off('touchmove', this.#onTouchMove);
119445
119560
  };
@@ -120409,20 +120524,40 @@ class nxSideMenuBody extends HTMLElement
120409
120524
 
120410
120525
  class nxSideMenuItem extends HTMLElement
120411
120526
  {
120527
+ #caption;
120412
120528
  #owner;
120413
120529
  #type;
120414
120530
 
120415
120531
  constructor() {
120532
+
120416
120533
  super();
120417
120534
  this.attachShadow({ mode: 'open' });
120418
-
120535
+
120536
+ this.test = this.innerText.trim();
120419
120537
  this.uuid = ninegrid.randomUUID();
120420
120538
  }
120421
120539
 
120422
120540
  connectedCallback() {
120423
- ninegrid.waitForInnerHTML(this)
120424
- .then(() => this.#init())
120425
- .catch(error => console.error(error));
120541
+ this.#caption = this.getAttribute("title");//this.innerText.trim();
120542
+
120543
+ //requestAnimationFrame(() => {
120544
+
120545
+
120546
+ //console.log("nxSideMenuItem", this.#caption);
120547
+ //this.data.caption = this.#caption;
120548
+ //this.setAttribute("data-caption", this.innerText.trim());
120549
+ //this.#init();
120550
+
120551
+
120552
+ ninegrid.waitForInnerHTML(this)
120553
+ .then(() => this.#init())
120554
+ .catch(error => console.error(error));
120555
+ //});
120556
+
120557
+ }
120558
+
120559
+ get caption() {
120560
+ return this.#caption;
120426
120561
  }
120427
120562
 
120428
120563
  #init = () => {
@@ -120510,6 +120645,8 @@ class nxSideMenuItem extends HTMLElement
120510
120645
 
120511
120646
  const iconClass = this.getAttribute('icon-class');
120512
120647
  const contents = this.innerHTML.trim();
120648
+
120649
+
120513
120650
 
120514
120651
  this.innerHTML = "";
120515
120652
 
@@ -120803,73 +120940,6 @@ Array.prototype.nineBinarySearch = function(target)
120803
120940
  return -1;
120804
120941
  };
120805
120942
 
120806
- class nxDiv extends HTMLElement {
120807
- constructor() {
120808
- super();
120809
- this.attachShadow({ mode: "open" });
120810
- this.isCollapsed = false; // 검색 박스 상태 (true: 축소됨, false: 확장됨)
120811
- }
120812
-
120813
- connectedCallback() {
120814
- this.#init();
120815
- }
120816
-
120817
- getJsonData = () => {
120818
- const parent = this.shadowRoot.querySelector(".search-content");
120819
- const elements = parent.querySelectorAll("[id]");
120820
- const jsonData = {};
120821
-
120822
- elements.forEach(element => {
120823
- if (element.tagName === "INPUT" || element.tagName === "TEXTAREA" || element.tagName === "SELECT") {
120824
- jsonData[element.id] = element.value;
120825
- } else {
120826
- jsonData[element.id] = element.textContent.trim();
120827
- }
120828
- });
120829
-
120830
- return jsonData;
120831
- };
120832
-
120833
- getSearchOptions = () => {
120834
-
120835
- };
120836
-
120837
- #init = () => {
120838
- const contents = this.innerHTML.trim();
120839
- this.innerHTML = ""; // 기존 내부 HTML 제거
120840
-
120841
- const htmlTmpl = document.createElement("template");
120842
- htmlTmpl.innerHTML = `
120843
- <style>
120844
- @import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/nxDiv.css";
120845
- ${ninegrid.getCustomPath(this,"nxDiv.css")}
120846
- </style>
120847
-
120848
- <div class="search-container">
120849
- <span class="toggle-icon"></span>
120850
- <div class="search-content">${contents}</div>
120851
- </div>
120852
- `;
120853
-
120854
- this.shadowRoot.appendChild(htmlTmpl.content.cloneNode(true));
120855
-
120856
- this.#setupToggle();
120857
- }
120858
-
120859
- #setupToggle = () => {
120860
- const toggleIcon = this.shadowRoot.querySelector(".toggle-icon");
120861
- const searchContainer = this.shadowRoot.querySelector(".search-container");
120862
-
120863
- toggleIcon.addEventListener("click", () => {
120864
- this.isCollapsed = !this.isCollapsed;
120865
- //toggleIcon.innerHTML = this.isCollapsed ? "" : "X";
120866
- searchContainer.classList.toggle("collapse", this.isCollapsed);
120867
- });
120868
- }
120869
- }
120870
-
120871
- customElements.define("nx-div", nxDiv);
120872
-
120873
120943
  class nxCollapse extends HTMLElement {
120874
120944
  #target;
120875
120945
  #targetPrevDisplay;
@@ -121249,11 +121319,21 @@ class nxSplitter extends HTMLElement {
121249
121319
  #detectMode = (el) => {
121250
121320
  const prev = el.previousElementSibling;
121251
121321
  const next = el.nextElementSibling;
121322
+ if (!prev || !next) {
121323
+ this.#mode = this.classList.contains('h') ? "h" : "v";
121324
+ return;
121325
+ }
121252
121326
 
121253
- const prevRect = prev?.getBoundingClientRect();
121254
- const nextRect = next?.getBoundingClientRect();
121327
+ const prevRect = prev.getBoundingClientRect();
121328
+ const nextRect = next.getBoundingClientRect();
121255
121329
 
121256
- this.#mode = (!prevRect || !nextRect) || (Math.abs(prevRect.top - nextRect.top) < 5) ? "h" : "v";
121330
+ if (this.classList.contains('h')) {
121331
+ this.#mode = "h";
121332
+ } else if (this.classList.contains('v')) {
121333
+ this.#mode = "v";
121334
+ } else {
121335
+ this.#mode = (Math.abs(prevRect.top - nextRect.top) < 5) ? "h" : "v";
121336
+ }
121257
121337
  };
121258
121338
 
121259
121339
  #startDrag = (e) => {
@@ -121263,6 +121343,7 @@ class nxSplitter extends HTMLElement {
121263
121343
  const splitterRect = this.getBoundingClientRect();
121264
121344
  const isHorizontal = this.#mode === "h";
121265
121345
 
121346
+ // 마우스 포인터와 스플리터 시작점 사이의 거리
121266
121347
  const clickOffset = isHorizontal
121267
121348
  ? e.clientX - splitterRect.left
121268
121349
  : e.clientY - splitterRect.top;
@@ -121280,7 +121361,6 @@ class nxSplitter extends HTMLElement {
121280
121361
 
121281
121362
  const root = this.getRootNode({ composed: true });
121282
121363
  const parent = root instanceof ShadowRoot ? root.host : this.parentElement;
121283
-
121284
121364
  const prev = this.previousElementSibling;
121285
121365
  const next = this.nextElementSibling;
121286
121366
 
@@ -121290,30 +121370,24 @@ class nxSplitter extends HTMLElement {
121290
121370
  }
121291
121371
 
121292
121372
  (parent.shadowRoot || parent).appendChild(dragBar);
121373
+ const dragBarOffsetParentRect = dragBar.offsetParent.getBoundingClientRect();
121293
121374
 
121294
- const dragBarOffsetParent = dragBar.offsetParent;
121295
-
121296
- if (!dragBarOffsetParent) {
121297
- console.error("dragBar's offsetParent could not be determined. Ensure parent or an ancestor has a position property (e.g., relative).");
121298
- dragBar.remove();
121299
- return;
121300
- }
121301
-
121302
- const dragBarOffsetParentRect = dragBarOffsetParent.getBoundingClientRect();
121303
121375
  const prevRect = prev.getBoundingClientRect();
121304
121376
  const nextRect = next.getBoundingClientRect();
121305
121377
 
121306
- let initialPosInOffsetParent;
121378
+ // 드래그 바의 초기 위치와 크기 설정
121379
+ const initialSplitterPosInParent = (isHorizontal
121380
+ ? splitterRect.left - dragBarOffsetParentRect.left
121381
+ : splitterRect.top - dragBarOffsetParentRect.top) + clickOffset;
121382
+
121307
121383
  if (isHorizontal) {
121308
- initialPosInOffsetParent = e.clientX - dragBarOffsetParentRect.left;
121309
121384
  dragBar.style.top = "0";
121310
- dragBar.style.left = `${initialPosInOffsetParent}px`;
121385
+ dragBar.style.left = `${initialSplitterPosInParent}px`;
121311
121386
  dragBar.style.width = "1px";
121312
121387
  dragBar.style.height = "100%";
121313
121388
  } else {
121314
- initialPosInOffsetParent = e.clientY - dragBarOffsetParentRect.top;
121315
121389
  dragBar.style.left = "0";
121316
- dragBar.style.top = `${initialPosInOffsetParent}px`;
121390
+ dragBar.style.top = `${initialSplitterPosInParent}px`;
121317
121391
  dragBar.style.height = "1px";
121318
121392
  dragBar.style.width = "100%";
121319
121393
  }
@@ -121322,104 +121396,111 @@ class nxSplitter extends HTMLElement {
121322
121396
  ? prevRect.left - dragBarOffsetParentRect.left
121323
121397
  : prevRect.top - dragBarOffsetParentRect.top;
121324
121398
  const maxLimit = isHorizontal
121325
- ? nextRect.right - dragBarOffsetParentRect.left
121326
- : nextRect.bottom - dragBarOffsetParentRect.top;
121327
-
121399
+ ? nextRect.right - dragBarOffsetParentRect.left - splitterRect.width
121400
+ : nextRect.bottom - dragBarOffsetParentRect.top - splitterRect.height;
121328
121401
 
121329
121402
  const onMove = moveEvent => {
121330
121403
  const clientPos = isHorizontal ? moveEvent.clientX : moveEvent.clientY;
121331
- const currentPosInOffsetParent = isHorizontal
121404
+ const currentPosInParent = isHorizontal
121332
121405
  ? clientPos - dragBarOffsetParentRect.left
121333
121406
  : clientPos - dragBarOffsetParentRect.top;
121334
121407
 
121335
- const clampedPos = Math.max(minLimit, Math.min(currentPosInOffsetParent, maxLimit));
121408
+ const clampedPos = Math.max(minLimit, Math.min(currentPosInParent, maxLimit));
121336
121409
 
121337
121410
  if (isHorizontal) {
121338
121411
  dragBar.style.left = `${clampedPos}px`;
121339
121412
  } else {
121340
121413
  dragBar.style.top = `${clampedPos}px`;
121341
121414
  }
121342
-
121343
- console.log(clampedPos);
121344
121415
  };
121345
121416
 
121346
- const onUp = (upEvent) => {
121417
+ const onUp = () => {
121347
121418
  window.removeEventListener("mousemove", onMove);
121348
121419
  window.removeEventListener("mouseup", onUp);
121349
121420
  dragBar.remove();
121350
121421
 
121351
- const finalDragBarPosInOffsetParent = isHorizontal
121352
- ? parseFloat(dragBar.style.left)
121353
- : parseFloat(dragBar.style.top);
121354
-
121355
- // ⭐⭐ offsetParentTotalSize 변수를 여기서 정의합니다 ⭐⭐
121356
- const offsetParentTotalSize = isHorizontal
121357
- ? dragBarOffsetParentRect.width
121358
- : dragBarOffsetParentRect.height;
121422
+ const allChildren = Array.from(parent.children);
121423
+ const allPanels = allChildren.filter(el => el.tagName.toLowerCase() !== 'nx-splitter');
121359
121424
 
121360
- const splitterThickness = isHorizontal ? splitterRect.width : splitterRect.height;
121425
+ // ⭐⭐ gap 크기 계산 ⭐⭐
121426
+ const style = window.getComputedStyle(parent);
121427
+ const gapValue = isHorizontal ? style.getPropertyValue('column-gap') : style.getPropertyValue('row-gap');
121428
+ const gapSize = parseFloat(gapValue) || 0;
121429
+ const gapCount = allChildren.length > 1 ? allChildren.length - 1 : 0;
121430
+ const totalGapSize = gapCount * gapSize;
121361
121431
 
121362
- // prev의 시작점 (offsetParent 기준)
121363
- const prevStartPosInOffsetParent = isHorizontal
121364
- ? prevRect.left - dragBarOffsetParentRect.left
121365
- : prevRect.top - dragBarOffsetParentRect.top;
121432
+ const finalDragBarPos = isHorizontal ? parseFloat(dragBar.style.left) : parseFloat(dragBar.style.top);
121433
+ const dragOffset = finalDragBarPos - initialSplitterPosInParent;
121366
121434
 
121367
- // prev의 새로운 크기 (dragBar의 왼쪽 위치 - prev의 왼쪽 위치)
121368
- let finalPrevSize = finalDragBarPosInOffsetParent - prevStartPosInOffsetParent;
121435
+ const prevSize = isHorizontal ? prev.getBoundingClientRect().width : prev.getBoundingClientRect().height;
121436
+ const nextSize = isHorizontal ? next.getBoundingClientRect().width : next.getBoundingClientRect().height;
121369
121437
 
121370
- // next의 새로운 크기 (offsetParent의 전체 크기 - prev 크기 - 스플리터 두께)
121371
- // 방법이 전체 공간을 정확히 채우면서 오차를 마지막 패널에 몰아넣는 데 더 견고합니다.
121372
- const MIN_PANEL_SIZE_PX = 1;
121373
- finalPrevSize = Math.max(MIN_PANEL_SIZE_PX, finalPrevSize) - clickOffset;
121374
- let finalNextSize = Math.max(MIN_PANEL_SIZE_PX, offsetParentTotalSize - finalPrevSize - splitterThickness);
121438
+ let newPrevSize = prevSize + dragOffset;
121439
+ let newNextSize = nextSize - dragOffset;
121375
121440
 
121376
- // flex 속성 저장 복원
121377
- const originalPrevFlex = prev.style.flex;
121378
- const originalNextFlex = next.style.flex;
121441
+ // 패널 크기가 음수가 되지 않도록 제한하고, 다른 패널에 차이를 보정
121442
+ if (newPrevSize < 0) {
121443
+ newNextSize += newPrevSize;
121444
+ newPrevSize = 0;
121445
+ }
121446
+ if (newNextSize < 0) {
121447
+ newPrevSize += newNextSize;
121448
+ newNextSize = 0;
121449
+ }
121379
121450
 
121380
- // 드래그 중에는 flex를 "none"으로 설정하여 직접 크기 조절
121381
- prev.style.flex = "none";
121382
- next.style.flex = "none";
121451
+ const initialSizes = allPanels.map(panel => isHorizontal ? panel.getBoundingClientRect().width : panel.getBoundingClientRect().height);
121383
121452
 
121384
- console.log(finalPrevSize, finalNextSize, offsetParentTotalSize);
121453
+ const totalSplitterSize = allChildren.reduce((sum, child) => {
121454
+ if (child.tagName.toLowerCase() === 'nx-splitter') {
121455
+ return sum + (isHorizontal ? child.getBoundingClientRect().width : child.getBoundingClientRect().height);
121456
+ }
121457
+ return sum;
121458
+ }, 0);
121459
+
121460
+ const totalContainerSize = (isHorizontal ? parent.getBoundingClientRect().width : parent.getBoundingClientRect().height);
121461
+ const totalFlexSpace = totalContainerSize - totalSplitterSize - totalGapSize;
121462
+
121463
+ let flexSum = 0;
121464
+ allPanels.forEach((panel, index) => {
121465
+ let newSize;
121466
+ if (panel === prev) {
121467
+ newSize = newPrevSize;
121468
+ } else if (panel === next) {
121469
+ newSize = newNextSize;
121470
+ } else {
121471
+ newSize = initialSizes[index];
121472
+ }
121385
121473
 
121386
- if (isHorizontal) {
121387
- prev.style.width = `${finalPrevSize}px`;
121388
- next.style.width = `${finalNextSize}px`;
121389
- } else {
121390
- prev.style.height = `${finalPrevSize}px`;
121391
- next.style.height = `${finalNextSize}px`;
121392
- }
121474
+ const newFlexBasis = newSize / totalFlexSpace;
121475
+ panel.style.flex = `${newFlexBasis} ${newFlexBasis} 0`;
121476
+ flexSum += newFlexBasis;
121477
+ });
121393
121478
 
121394
- // 작업 완료 후 원래 flex 값으로 복원
121395
- prev.style.flex = originalPrevFlex;
121396
- next.style.flex = originalNextFlex;
121479
+ console.log(`dragOffset: ${dragOffset}`);
121480
+ console.log(`Calculated FlexSum: ${flexSum}`);
121397
121481
  };
121398
121482
 
121399
121483
  window.addEventListener("mousemove", onMove);
121400
121484
  window.addEventListener("mouseup", onUp);
121401
121485
  };
121402
121486
 
121403
-
121404
121487
  #init = () => {
121405
121488
  this.#detectMode(this);
121406
-
121407
121489
  this.classList.add(this.#mode);
121408
121490
 
121409
121491
  const contents = this.innerHTML.trim();
121410
121492
  const gripClass = `grip-${this.#mode}`;
121411
- const gripTmpl = (contents === "") ? `<div class="${gripClass}"></div>`: `<div class="${gripClass}"></div><div class="inner-container">${contents}</div><div class="${gripClass}"></div>`;
121493
+ const gripTmpl = (contents === "") ? `<div class="${gripClass}"></div>` : `<div class="${gripClass}"></div><div class="inner-container">${contents}</div><div class="${gripClass}"></div>`;
121412
121494
 
121413
121495
  this.innerHTML = "";
121414
121496
  const htmlTmpl = document.createElement("template");
121415
121497
  htmlTmpl.innerHTML = `
121416
- <style>
121417
- @import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/nxSplitter.css";
121418
- ${ninegrid.getCustomPath(this,"nxSplitter.css")}
121419
- </style>
121420
-
121421
- ${gripTmpl}
121422
- `;
121498
+ <style>
121499
+ @import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/nxSplitter.css";
121500
+ ${ninegrid.getCustomPath(this,"nxSplitter.css")}
121501
+ </style>
121502
+ ${gripTmpl}
121503
+ `;
121423
121504
 
121424
121505
  this.shadowRoot.appendChild(htmlTmpl.content.cloneNode(true));
121425
121506
 
@@ -121432,62 +121513,26 @@ class nxSplitter extends HTMLElement {
121432
121513
  window.addEventListener("resize", () => this.#prepareLayout());
121433
121514
  };
121434
121515
 
121435
- #isLastSplitter = () => {
121436
- const parent = this.parentElement;
121437
- if (!parent) return false;
121438
-
121439
- const allSplitters = [...parent.querySelectorAll("nx-splitter")];
121440
- return allSplitters.at(-1) === this;
121441
- };
121442
-
121443
- #getSiblingSizeSum = () => {
121444
- const parent = this.parentElement;
121445
- if (!parent) return 0;
121446
-
121447
- const isHorizontal = this.#mode === "h";
121448
-
121449
- return Array.from(parent.children).reduce((sum, el) => {
121450
- const size = isHorizontal ? el.offsetWidth : el.offsetHeight;
121451
- return sum + size;
121452
- }, 0);
121453
- };
121454
-
121455
-
121456
121516
  #prepareLayout = () => {
121457
-
121458
121517
  const isHorizontal = this.#mode === "h";
121459
- const prev = this.previousElementSibling;
121460
- const next = this.nextElementSibling;
121461
121518
  const parent = this.parentElement;
121462
- if (!prev || !next || !parent) return;
121463
-
121464
- const currentTotal = this.#getSiblingSizeSum();
121465
- const nextTotal = isHorizontal
121466
- ? parent.getBoundingClientRect().width
121467
- : parent.getBoundingClientRect().height;
121519
+ const allPanels = Array.from(parent.children).filter(el => el.tagName.toLowerCase() !== 'nx-splitter');
121520
+ if (allPanels.length < 2) return;
121468
121521
 
121469
- const prevSize = isHorizontal ? prev.offsetWidth : prev.offsetHeight;
121470
- const nextSize = isHorizontal ? next.offsetWidth : next.offsetHeight;
121471
-
121472
- const newPrevSize = nextTotal * prevSize / currentTotal;
121473
- const newNextSize = nextTotal * nextSize / currentTotal;
121474
-
121475
-
121476
- if (isHorizontal) {
121477
- prev.style.width = `${newPrevSize}px`;
121478
-
121479
- if (this.#isLastSplitter()) {
121480
- next.style.width = `${newNextSize}px`;
121481
- }
121482
- } else {
121483
- prev.style.height = `${newPrevSize}px`;
121522
+ const parentRect = parent.getBoundingClientRect();
121523
+ const totalContentSize = allPanels.reduce((sum, el) => {
121524
+ const size = isHorizontal ? el.getBoundingClientRect().width : el.getBoundingClientRect().height;
121525
+ return sum + size;
121526
+ }, 0);
121527
+ const totalParentSize = isHorizontal ? parentRect.width : parentRect.height;
121484
121528
 
121485
- if (this.#isLastSplitter()) {
121486
- next.style.height = `${newNextSize}px`;
121487
- }
121488
- }
121529
+ allPanels.forEach(panel => {
121530
+ const size = isHorizontal ? panel.getBoundingClientRect().width : panel.getBoundingClientRect().height;
121531
+ const newSize = totalParentSize * (size / totalContentSize);
121532
+ const flexGrow = newSize / totalParentSize;
121533
+ panel.style.flex = `${flexGrow} ${flexGrow} 0`;
121534
+ });
121489
121535
  };
121490
-
121491
121536
  }
121492
121537
 
121493
121538
  customElements.define("nx-splitter", nxSplitter);
@@ -121566,6 +121611,640 @@ class nxForm extends HTMLElement {
121566
121611
 
121567
121612
  customElements.define("nx-form", nxForm);
121568
121613
 
121614
+ class nxTitle extends HTMLElement {
121615
+
121616
+ #breadcrumbPath = []; // Breadcrumb 경로를 저장할 내부 상태 초기화
121617
+ #dynamicPath = []; // 동적으로 추가할 빵 부스러기 경로 (상세 페이지 등)
121618
+
121619
+
121620
+ constructor() {
121621
+ super();
121622
+ this.attachShadow({ mode: "open" });
121623
+ }
121624
+
121625
+ connectedCallback() {
121626
+ this.#init();
121627
+ }
121628
+
121629
+ // 컴포넌트가 DOM에서 제거될 때 이벤트 리스너를 정리
121630
+ disconnectedCallback() {
121631
+ document.removeEventListener('DOMContentLoaded', this.#init);
121632
+ }
121633
+
121634
+ setDynamicPath(pathArray) {
121635
+ this.#dynamicPath = pathArray;
121636
+ this.#generateBreadcrumb(); // 빵 부스러기 재생성 및 렌더링
121637
+ this.#renderer();
121638
+ }
121639
+
121640
+ #init = () => {
121641
+ /**
121642
+ console.log("111");
121643
+ const activeMenuItem = ninegrid.delayQuerySelector('nx-side-menu-item.active');
121644
+ console.log(activeMenuItem);
121645
+
121646
+ this.#generateBreadcrumb(); // 빵 부스러기 데이터 생성 및 최종 렌더링 트리거
121647
+ this.#renderer(); // 초기 렌더링 (빵 부스러기 데이터는 아직 없음)
121648
+ */
121649
+ ninegrid.delayQuerySelector('nx-side-menu-item.active')
121650
+ .then((el) => {
121651
+ this.#generateBreadcrumb(el); // 빵 부스러기 데이터 생성 및 최종 렌더링 트리거
121652
+ this.#renderer(); // 초기 렌더링 (빵 부스러기 데이터는 아직 없음)
121653
+ });
121654
+ };
121655
+
121656
+ // Breadcrumb 경로를 구성하는 비공개 메서드
121657
+ #generateBreadcrumb = (activeMenuItem) => {
121658
+ this.#breadcrumbPath = [];
121659
+
121660
+
121661
+ // 'active' 클래스를 가진 가장 하위 nx-side-menu-item 찾기
121662
+ //const activeMenuItem = ninegrid.querySelector('nx-side-menu-item.active');
121663
+ //console.log(activeMenuItem);
121664
+
121665
+ if (activeMenuItem) {
121666
+
121667
+ // 1. 현재 활성화된 메뉴 항목의 캡션을 추가 (예: '심의자료')
121668
+ const currentActiveCaption = activeMenuItem.caption;//activeMenuItem.caption;
121669
+
121670
+ if (currentActiveCaption) {
121671
+ this.#breadcrumbPath.unshift({
121672
+ path: activeMenuItem.getAttribute("href"),
121673
+ caption: currentActiveCaption,
121674
+ });
121675
+ }
121676
+
121677
+ // 2. 현재 activeMenuItem의 이전 형제들을 탐색하여 'group'이 아닌 상위 메뉴(예: '심의자료')를 찾고,
121678
+ // 그 이전에 있는 'group' 메뉴(예: '자료관리')를 찾습니다.
121679
+ let currentElement = activeMenuItem;
121680
+ let foundGroupParent = false; // '자료관리' 그룹을 찾았는지 여부
121681
+
121682
+ // activeMenuItem부터 이전 형제들을 역으로 탐색
121683
+ while (currentElement) {
121684
+ // 현재 activeMenuItem이 'group' 속성이 없는 일반 메뉴일 경우,
121685
+ // 바로 그 자체가 두 번째 레벨 메뉴(예: '심의자료')의 역할을 합니다.
121686
+ // 이미 activeMenuItem의 캡션은 추가되었으므로 여기서는 건너뜁니다.
121687
+
121688
+ // 이전 형제 탐색
121689
+ currentElement = currentElement.previousElementSibling;
121690
+
121691
+ if (currentElement && currentElement.tagName === 'NX-SIDE-MENU-ITEM') {
121692
+ const caption = currentElement.caption;//.getAttribute('caption');
121693
+
121694
+ // 'group' 속성을 가진 메뉴 항목 찾기 (예: '자료관리')
121695
+ if (currentElement.getAttribute('type') === "group") {
121696
+ if (caption && !foundGroupParent) { // 이미 찾지 않은 경우에만 추가
121697
+
121698
+ this.#breadcrumbPath.unshift({
121699
+ path: currentElement.getAttribute("href"),
121700
+ caption: caption,
121701
+ });
121702
+ foundGroupParent = true;
121703
+ }
121704
+ // 그룹을 찾으면 탐색 중단 (가장 가까운 상위 그룹만 필요)
121705
+ break;
121706
+ }
121707
+ }
121708
+ }
121709
+
121710
+ // tempPath의 내용을 Home 다음으로 삽입
121711
+ // activeMenuItem이 '심의자료' 역할을 하고, 그 이전에 '자료관리' 그룹이 있다면
121712
+ // tempPath: ['자료관리', '심의자료']
121713
+
121714
+ }
121715
+
121716
+ // 마지막으로 현재 페이지의 구체적인 제목 추가
121717
+ // 현재 nx-title의 캡션을 현재 페이지 캡션으로 사용
121718
+ if (!this.closest(".list-wrapper")) {
121719
+ this.#breadcrumbPath.push({
121720
+ path: "",
121721
+ caption: this.getAttribute("caption") || "Current Page",
121722
+ });
121723
+ }
121724
+
121725
+ this.#breadcrumbPath.unshift({
121726
+ path: "/",
121727
+ caption: "Home",
121728
+ });
121729
+
121730
+ //console.log(this.#dynamicPath)
121731
+
121732
+ // 2. 동적으로 추가된 경로 (상세 페이지)
121733
+ // #dynamicPath 배열의 내용을 병합
121734
+ this.#breadcrumbPath = this.#breadcrumbPath.concat(this.#dynamicPath);
121735
+
121736
+ //console.log(this.#breadcrumbPath);
121737
+ //this.#breadcrumbPath = path;
121738
+ //this.#renderer();
121739
+ };
121740
+
121741
+ #renderer = () => {
121742
+ const el = ninegrid.querySelector('nx-side-menu-item.active');
121743
+ //console.log(el);
121744
+
121745
+ const caption = el ? el.caption : "No Caption";
121746
+
121747
+ // 내부 HTML (Light DOM)은 이 컴포넌트의 자식으로 넘어오는 콘텐츠이므로,
121748
+ // Breadcrumb에서는 사용하지 않는다면 제거하는 것이 맞습니다.
121749
+ // 여기서는 이전에 contents 변수에 저장했지만 사용하지 않았으므로, 제거 로직을 유지합니다.
121750
+ this.innerHTML.trim();
121751
+ this.innerHTML = ""; // 기존 내부 HTML 제거
121752
+
121753
+ // Shadow DOM 초기화 (새로운 콘텐츠를 위해)
121754
+ this.shadowRoot.innerHTML = "";
121755
+
121756
+
121757
+
121758
+ const htmlTmpl = document.createElement("template");
121759
+ htmlTmpl.innerHTML = `
121760
+ <style>
121761
+ @import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/nxTitle.css";
121762
+ ${ninegrid.getCustomPath(this,"nxTitle.css")}
121763
+ </style>
121764
+
121765
+ <div class="wrapper">
121766
+ <div class="title-wrapper">
121767
+ <div class="bar1"></div>
121768
+ <div class="bar2"></div>
121769
+ <div class="bar3"></div>
121770
+ <i class="title-icon"></i>
121771
+ <span class="title-text">${caption}</span>
121772
+ <div class="bar7"></div>
121773
+ <div class="bar8"></div>
121774
+ <div class="bar9"></div>
121775
+ </div>
121776
+ <div class="breadcrumb-container">
121777
+ ${this.#breadcrumbPath.map((item, index) => {
121778
+
121779
+ const isCurrent = index === this.#breadcrumbPath.length - 1;
121780
+ // static 메서드를 직접 호출하도록 수정
121781
+ const linkTag = item.path && !isCurrent
121782
+ ? `<a href="javascript:void(0);"
121783
+ class="breadcrumb-item breadcrumb-link"
121784
+ data-path="${item.path}"
121785
+ >${item.caption}</a>`
121786
+ : `<span class="breadcrumb-item ${isCurrent ? 'breadcrumb-current' : ''}">${item.caption}</span>`;
121787
+
121788
+ const separator = index < this.#breadcrumbPath.length - 1
121789
+ ? `<span class="breadcrumb-separator"> &gt; </span>`
121790
+ : '';
121791
+
121792
+ return `${linkTag}${separator}`;
121793
+ }).join('')}
121794
+ </div>
121795
+ </div>
121796
+ `;
121797
+
121798
+ this.shadowRoot.appendChild(htmlTmpl.content.cloneNode(true));
121799
+
121800
+ this.shadowRoot.querySelectorAll('.breadcrumb-link').forEach(link => {
121801
+ link.addEventListener('click', this.#menuClickHandler);
121802
+ });
121803
+ }
121804
+
121805
+ #menuClickHandler = (e) => {
121806
+ console.log(e);
121807
+ const path = e.target.dataset.path;
121808
+ if (path) {
121809
+ window.location.href = path;
121810
+ }
121811
+ }
121812
+ }
121813
+
121814
+ customElements.define("nx-title", nxTitle);
121815
+
121816
+ class nxDiv extends HTMLElement
121817
+ {
121818
+ originContents;
121819
+ #isInitialized = false;
121820
+
121821
+ constructor () {
121822
+ super();
121823
+ this.attachShadow({ mode: 'open' });
121824
+ }
121825
+
121826
+ connectedCallback() {
121827
+ if (!this.#isInitialized) {
121828
+ this.#init();
121829
+ this.#isInitialized = true; // 3. 초기화 후 플래그를 true로 변경합니다.
121830
+ return true;
121831
+ }
121832
+
121833
+ return false;
121834
+ }
121835
+
121836
+ getData = () => {
121837
+ const jsonData = {};
121838
+
121839
+ // Shadow DOM 내의 폼 요소만 선택
121840
+ ninegrid.querySelectorAll("input[name], textarea[name], select[name]", this.shadowRoot).forEach(el => {
121841
+ const key = el.name;
121842
+ if (!key) return; // name이 없는 요소는 건너뜁니다.
121843
+
121844
+ let value;
121845
+ if (el.tagName === "INPUT" && (el.type === "checkbox" || el.type === "radio")) {
121846
+ value = el.checked;
121847
+ } else {
121848
+ value = el.value;
121849
+ }
121850
+
121851
+ // 중복 name을 대비한 배열 처리
121852
+ if (jsonData[key]) {
121853
+ if (!Array.isArray(jsonData[key])) {
121854
+ jsonData[key] = [jsonData[key]];
121855
+ }
121856
+ jsonData[key].push(value);
121857
+ } else {
121858
+ jsonData[key] = value;
121859
+ }
121860
+ });
121861
+
121862
+ return jsonData;
121863
+ };
121864
+
121865
+ setData = (jsonData) => {
121866
+ ninegrid.querySelectorAll("input[name], textarea[name], select[name]", this.shadowRoot).forEach(el => {
121867
+ console.log("------", el);
121868
+ el.removeEventListener("input", this.#changeHandler);
121869
+ el.addEventListener("input", this.#changeHandler);
121870
+ });
121871
+
121872
+ if (!jsonData || typeof jsonData !== 'object') {
121873
+ console.error("setData: Invalid data provided. Expected an object.");
121874
+ return;
121875
+ }
121876
+
121877
+ let bChanged = false;
121878
+ ninegrid.querySelectorAll("input[name], textarea[name], select[name]", this.shadowRoot).forEach(el => {
121879
+ const key = el.name;
121880
+ if (!key || !jsonData.hasOwnProperty(key)) return;
121881
+
121882
+ const value = jsonData[key];
121883
+
121884
+ // 폼 요소에만 데이터를 설정합니다.
121885
+ if (el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.tagName === "SELECT") {
121886
+ if (el.type === "checkbox" || el.type === "radio") {
121887
+ if (el.checked !== (el.value === String(value))) bChanged = true;
121888
+ el.checked = (el.value === String(value));
121889
+ } else {
121890
+ if (el.value !== value) bChanged = true;
121891
+ el.value = value;
121892
+ }
121893
+ } else {
121894
+ // 폼 요소가 아닌 경우, textContent를 사용합니다.
121895
+ if (el.textContent !== value) bChanged = true;
121896
+ el.textContent = value;
121897
+ }
121898
+ });
121899
+
121900
+ if (bChanged) this.#changed(bChanged);
121901
+ };
121902
+
121903
+ initData = (jsonData) => {
121904
+ this.setData(jsonData);
121905
+ this.#changed(false);
121906
+ };
121907
+
121908
+ #changed = (v) => {
121909
+ const parent = this.closest(".detail-wrapper");
121910
+ if (parent) {
121911
+ const el = parent.querySelector("nx-title2");
121912
+ if (el) el.changed = v;
121913
+ }
121914
+ }
121915
+
121916
+ #changeHandler = (e) => {
121917
+ this.#changed(true);
121918
+ }
121919
+
121920
+ #init = () => {
121921
+
121922
+ //console.log(this);
121923
+ //console.log(this.querySelectorAll("input[name], textarea[name], select[name]"));
121924
+
121925
+ /**
121926
+ * css style 적용
121927
+ */
121928
+ for (const attr of this.attributes) {
121929
+ if (attr.name.startsWith("css-")) {
121930
+ // "css-" 접두사를 제거하고 CSS 속성명으로 변환
121931
+ this.style.setProperty(attr.name.substring(4), attr.value);
121932
+ }
121933
+ }
121934
+
121935
+ this.originContents = this.innerHTML.trim();
121936
+ this.innerHTML = ""; // 기존 내부 HTML 제거
121937
+ };
121938
+ }
121939
+
121940
+ class nxTitle2 extends nxDiv {
121941
+
121942
+ #changed = false;
121943
+
121944
+ constructor() {
121945
+ super();
121946
+ }
121947
+
121948
+ connectedCallback() {
121949
+ if (super.connectedCallback()) this.#init();
121950
+ };
121951
+
121952
+ get changed() {
121953
+ return this.#changed;
121954
+ };
121955
+ set changed(v) {
121956
+ this.#changed = v;
121957
+ v ? this.classList.add("changed") : this.classList.remove("changed");
121958
+ };
121959
+
121960
+ #init = () => {
121961
+ const caption = this.getAttribute("caption") || "";
121962
+ const htmlTmpl = document.createElement("template");
121963
+
121964
+ htmlTmpl.innerHTML = `
121965
+ <style>
121966
+ @import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/nxTitle2.css";
121967
+ ${ninegrid.getCustomPath(this,"nxTitle2.css")}
121968
+ </style>
121969
+
121970
+ <div class="left">
121971
+ <span>${caption}</span>
121972
+ </div>
121973
+ ${this.originContents}
121974
+ `;
121975
+
121976
+ this.shadowRoot.appendChild(htmlTmpl.content.cloneNode(true));
121977
+ }
121978
+ }
121979
+
121980
+ customElements.define("nx-title2", nxTitle2);
121981
+
121982
+ class NxLayout extends HTMLElement {
121983
+
121984
+ #originalChildren;
121985
+
121986
+ constructor() {
121987
+ super();
121988
+ }
121989
+
121990
+ connectedCallback() {
121991
+ this.#originalChildren = Array.from(this.children).filter(child => child.tagName.toLowerCase() !== 'nx-splitter');
121992
+ this.#render();
121993
+ }
121994
+
121995
+ attributeChangedCallback(name, oldValue, newValue) {
121996
+ if (this.#originalChildren && oldValue !== newValue) {
121997
+ this.#render();
121998
+ }
121999
+ }
122000
+
122001
+ static get observedAttributes() {
122002
+ return ['columns'];
122003
+ }
122004
+
122005
+ #render() {
122006
+ const children = this.#originalChildren;
122007
+ if (!children || children.length === 0) return;
122008
+
122009
+ // 기존 자식 노드를 모두 제거하여 초기화
122010
+ while (this.firstChild) {
122011
+ this.removeChild(this.firstChild);
122012
+ }
122013
+
122014
+ const columnsLayout = (this.getAttribute('columns') || '1').split(',').map(Number);
122015
+ const numRows = columnsLayout.length;
122016
+
122017
+ // 메인 컨테이너를 Flexbox로 설정
122018
+ //*this.style.display = 'flex';
122019
+ //*this.style.flexDirection = 'column'; // 'flex-direction' 대신 CamelCase 사용
122020
+ //*this.style.width = '100%';
122021
+ //*this.style.height = '100%';
122022
+ //this.style.overflow = 'hidden'; // 부모 요소에 스크롤 방지
122023
+
122024
+ let childIndex = 0;
122025
+
122026
+ for (let row = 0; row < numRows; row++) {
122027
+ const numColumns = columnsLayout[row];
122028
+
122029
+ // 행 래퍼 생성
122030
+ const rowWrapper = document.createElement('div');
122031
+ rowWrapper.classList.add('row');
122032
+ //*rowWrapper.style.display = 'flex';
122033
+ //*rowWrapper.style.flex = "1 1 0";//`1 1 ${flexBasis}%`; // 모든 행이 균등한 높이를 갖도록 설정
122034
+ //*rowWrapper.style.width = '100%';
122035
+ //rowWrapper.style.minWidth = '0';
122036
+ //rowWrapper.style.minHeight = '0';
122037
+ //rowWrapper.style.gap = '8px'; // Flexbox 간격 설정
122038
+
122039
+ for (let col = 0; col < numColumns; col++) {
122040
+ if (children[childIndex]) {
122041
+ const panel = children[childIndex];
122042
+ //*panel.style.flex = '1 1 0'; // Flexbox 환경에 맞게 flex 속성 추가
122043
+ rowWrapper.appendChild(panel);
122044
+ childIndex++;
122045
+ }
122046
+ if (col < numColumns - 1) {
122047
+ const splitter = document.createElement('nx-splitter');
122048
+ //splitter.classList.add("h");
122049
+ rowWrapper.appendChild(splitter);
122050
+ }
122051
+ }
122052
+
122053
+ this.appendChild(rowWrapper);
122054
+
122055
+ if (row < numRows - 1) {
122056
+ const splitter = document.createElement('nx-splitter');
122057
+ splitter.classList.add("v");
122058
+ this.appendChild(splitter);
122059
+ }
122060
+ }
122061
+ }
122062
+ }
122063
+
122064
+ customElements.define('nx-layout', NxLayout);
122065
+
122066
+ class NxLayout2 extends HTMLElement {
122067
+
122068
+ #originalChildren;
122069
+
122070
+ constructor() {
122071
+ super();
122072
+ }
122073
+
122074
+ connectedCallback() {
122075
+ this.#originalChildren = Array.from(this.children).filter(child => child.tagName.toLowerCase() !== 'nx-splitter');
122076
+ this.#render();
122077
+ }
122078
+
122079
+ attributeChangedCallback(name, oldValue, newValue) {
122080
+ if (this.#originalChildren && oldValue !== newValue) {
122081
+ this.#render();
122082
+ }
122083
+ }
122084
+
122085
+ static get observedAttributes() {
122086
+ return ['columns'];
122087
+ }
122088
+
122089
+ #render() {
122090
+ const children = this.#originalChildren;
122091
+ if (!children || children.length === 0) return;
122092
+
122093
+ // 기존 자식 노드를 모두 제거하여 초기화
122094
+ while (this.firstChild) {
122095
+ this.removeChild(this.firstChild);
122096
+ }
122097
+
122098
+ const columnsLayout = (this.getAttribute('columns') || '1').split(',').map(Number);
122099
+ const numRows = columnsLayout.length;
122100
+
122101
+ let childIndex = 0;
122102
+
122103
+ for (let row = 0; row < numRows; row++) {
122104
+ const numColumns = columnsLayout[row];
122105
+
122106
+ // 행 래퍼 생성
122107
+ const rowWrapper = document.createElement('div');
122108
+ rowWrapper.classList.add('row');
122109
+
122110
+ for (let col = 0; col < numColumns; col++) {
122111
+ if (children[childIndex]) {
122112
+ const child = children[childIndex];
122113
+ rowWrapper.appendChild(child);
122114
+ childIndex++;
122115
+ }
122116
+ }
122117
+
122118
+ this.appendChild(rowWrapper);
122119
+ }
122120
+
122121
+ setTimeout(() => {
122122
+ this.#position();
122123
+ }, 300);
122124
+ };
122125
+
122126
+ #position = () => {
122127
+ const getRenderedWidthWithClone = (element) => {
122128
+ // 1. 요소의 계산된 스타일을 가져옵니다.
122129
+ const computedStyle = window.getComputedStyle(element);
122130
+
122131
+ // 2. 복사본을 생성합니다.
122132
+ const clone = element.cloneNode(true);
122133
+
122134
+ // 3. 복사본에 필요한 스타일만 명시적으로 적용합니다.
122135
+ // 이렇게 하면 부모가 숨겨져 있어도 정확한 스타일을 얻을 수 있습니다.
122136
+ clone.style.fontSize = computedStyle.fontSize;
122137
+ clone.style.fontFamily = computedStyle.fontFamily;
122138
+ clone.style.fontWeight = computedStyle.fontWeight;
122139
+
122140
+ // 4. 복제본을 화면 밖으로 이동시킬 스타일을 적용합니다.
122141
+ clone.style.position = 'absolute';
122142
+ clone.style.left = '-9999px';
122143
+ clone.style.visibility = 'hidden';
122144
+
122145
+ // 5. 복제본을 body에 임시로 추가합니다.
122146
+ document.body.appendChild(clone);
122147
+
122148
+ // 6. 너비 측정
122149
+ const width = clone.getBoundingClientRect().width;
122150
+
122151
+ // 7. 측정이 끝나면 복제본을 제거합니다.
122152
+ document.body.removeChild(clone);
122153
+
122154
+ return width + 4;
122155
+ };
122156
+
122157
+ const allLabels = [];
122158
+
122159
+ this.querySelectorAll(".row").forEach((el) => {
122160
+ const labelsInRow = el.querySelectorAll(".label");
122161
+ labelsInRow.forEach((label, index) => {
122162
+ if (!allLabels[index]) {
122163
+ allLabels[index] = [];
122164
+ }
122165
+ allLabels[index].push(label);
122166
+ });
122167
+ });
122168
+
122169
+ allLabels.forEach((labelGroup) => {
122170
+ if (labelGroup && labelGroup.length > 0) {
122171
+ const renderedWidths = labelGroup.map(label => getRenderedWidthWithClone(label));
122172
+ const maxWidth = Math.max(...renderedWidths);
122173
+
122174
+ labelGroup.forEach(label => {
122175
+ label.style.width = `${maxWidth}px`;
122176
+ });
122177
+ }
122178
+ });
122179
+ };
122180
+ }
122181
+
122182
+ customElements.define('nx-layout2', NxLayout2);
122183
+
122184
+ class nxPanel extends nxDiv {
122185
+
122186
+ constructor() {
122187
+ super();
122188
+ }
122189
+
122190
+ connectedCallback() {
122191
+ if (super.connectedCallback()) this.#init();
122192
+ }
122193
+
122194
+ #init = () => {
122195
+ const caption = this.getAttribute("caption");
122196
+ const columns = this.getAttribute("columns") || "";
122197
+
122198
+ const htmlTmpl = document.createElement("template");
122199
+ htmlTmpl.innerHTML = `
122200
+ <style>
122201
+ @import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/nxPanel.css";
122202
+ ${ninegrid.getCustomPath(this,"nxPanel.css")}
122203
+ </style>
122204
+
122205
+ <div class="head ${caption ? '' : 'hide'}">
122206
+ <div class="caption"><span>${caption}</span></div>
122207
+ </div>
122208
+ <div class="body">
122209
+ <nx-layout2 columns="${columns}">
122210
+ ${this.originContents}
122211
+ </nx-layout2>
122212
+ </div>
122213
+ `;
122214
+
122215
+ this.shadowRoot.appendChild(htmlTmpl.content.cloneNode(true));
122216
+ }
122217
+ }
122218
+
122219
+ customElements.define("nx-panel", nxPanel);
122220
+
122221
+ class nxButtons extends nxDiv {
122222
+
122223
+ constructor() {
122224
+ super();
122225
+ }
122226
+
122227
+ connectedCallback() {
122228
+ if (super.connectedCallback()) this.#init();
122229
+ };
122230
+
122231
+ #init = () => {
122232
+ const htmlTmpl = document.createElement("template");
122233
+ htmlTmpl.innerHTML = `
122234
+ <style>
122235
+ @import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/nxButtons.css";
122236
+ ${ninegrid.getCustomPath(this,"nxButtons.css")}
122237
+ </style>
122238
+
122239
+ ${this.originContents}
122240
+ `;
122241
+
122242
+ this.shadowRoot.appendChild(htmlTmpl.content.cloneNode(true));
122243
+ }
122244
+ }
122245
+
122246
+ customElements.define("nx-buttons", nxButtons);
122247
+
121569
122248
  class aiSettings extends HTMLElement
121570
122249
  {
121571
122250
  constructor() {
@@ -202541,30 +203220,10 @@ console.log(el, href, title);
202541
203220
 
202542
203221
  console.log(where1);
202543
203222
 
202544
- const srcPath1 = this.#getSourcePath(where.menu.url);
203223
+ const srcPath1 = this.#getSourcePath(where1.menu.url);
202545
203224
  console.log(srcPath1);
202546
203225
 
202547
203226
  return;
202548
- //console.log(src);
202549
-
202550
- /**
202551
- const response = await fetch(srcPath.javascript);
202552
- src.javascript = await response.text();*/
202553
-
202554
- //console.log(src);
202555
- //const template = await fetch(path).then(res => res.text());
202556
- /*
202557
- arr.push({
202558
- //menuId: elem.getAttribute("menu-id"),
202559
- url: elem.getAttribute("href"),
202560
- title: elem.getAttribute("title"),
202561
- })*/
202562
-
202563
-
202564
-
202565
-
202566
-
202567
- const where = await this.#where(`${title}에서 ${userPrompt}`, [{url: href, title: title}], await this.#getTableList());
202568
203227
  };
202569
203228
 
202570
203229
  #generateDetailSource = async (userPrompt, apply, progressMessageInstance) => {