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