ide-assi 0.443.0 → 0.445.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.
@@ -10973,6 +10973,7 @@ class ninegrid {
10973
10973
  ROW_MOVED : "rowmoved",
10974
10974
  //CAN_FIXED_CHANGE : "canfixedchange",
10975
10975
  //FIXED_CHANGED : "fixedchanged",
10976
+ TAB_LOAD : "tabload",
10976
10977
  };
10977
10978
 
10978
10979
  static BAND = {
@@ -119282,7 +119283,21 @@ class NineGridContainer extends HTMLElement
119282
119283
 
119283
119284
  //export { NineGridContainer };
119284
119285
 
119285
- customElements.define("nine-grid", NineGridContainer);
119286
+ customElements.define("nine-grid", NineGridContainer);
119287
+
119288
+ class NineGridSourceContainer extends NineGridContainer
119289
+ {
119290
+ constructor() {
119291
+
119292
+ super();
119293
+ }
119294
+
119295
+ connectedCallback() {
119296
+ super.connectedCallback();
119297
+ };
119298
+ }
119299
+
119300
+ customElements.define("ng-source", NineGridSourceContainer);
119286
119301
 
119287
119302
  class nxDialog extends HTMLElement
119288
119303
  {
@@ -119422,76 +119437,6 @@ class nxDialog extends HTMLElement
119422
119437
 
119423
119438
  customElements.define("nx-dialog", nxDialog);
119424
119439
 
119425
- class nxDiv extends HTMLElement {
119426
- constructor() {
119427
- super();
119428
- this.attachShadow({ mode: "open" });
119429
- this.isCollapsed = false; // 검색 박스 상태 (true: 축소됨, false: 확장됨)
119430
- }
119431
-
119432
- connectedCallback() {
119433
- this.#init();
119434
- }
119435
-
119436
- getJsonData = () => {
119437
- const parent = this.shadowRoot.querySelector(".search-content");
119438
- const elements = parent.querySelectorAll("[id]");
119439
- const jsonData = {};
119440
-
119441
- elements.forEach(element => {
119442
- if (element.tagName === "INPUT" || element.tagName === "TEXTAREA" || element.tagName === "SELECT") {
119443
- jsonData[element.id] = element.value;
119444
- } else {
119445
- jsonData[element.id] = element.textContent.trim();
119446
- }
119447
- });
119448
-
119449
- return jsonData;
119450
- };
119451
-
119452
- getSearchOptions = () => {
119453
-
119454
- };
119455
-
119456
- #init = () => {
119457
- const contents = this.innerHTML.trim();
119458
- this.innerHTML = ""; // 기존 내부 HTML 제거
119459
-
119460
- const htmlTmpl = document.createElement("template");
119461
- htmlTmpl.innerHTML = `
119462
- <style>
119463
- @import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/nxDiv.css";
119464
- ${ninegrid.getCustomPath(this,"nxDiv.css")}
119465
-
119466
- :host {
119467
- }
119468
- </style>
119469
-
119470
- <div class="search-container">
119471
- <span class="toggle-icon"></span>
119472
- <div class="search-content">${contents}</div>
119473
- </div>
119474
- `;
119475
-
119476
- this.shadowRoot.appendChild(htmlTmpl.content.cloneNode(true));
119477
-
119478
- this.#setupToggle();
119479
- }
119480
-
119481
- #setupToggle = () => {
119482
- const toggleIcon = this.shadowRoot.querySelector(".toggle-icon");
119483
- const searchContainer = this.shadowRoot.querySelector(".search-container");
119484
-
119485
- toggleIcon.addEventListener("click", () => {
119486
- this.isCollapsed = !this.isCollapsed;
119487
- //toggleIcon.innerHTML = this.isCollapsed ? "" : "X";
119488
- searchContainer.classList.toggle("collapse", this.isCollapsed);
119489
- });
119490
- }
119491
- }
119492
-
119493
- customElements.define("nx-div", nxDiv);
119494
-
119495
119440
  class nxI18nExt extends HTMLElement
119496
119441
  {
119497
119442
  #enableHtml;
@@ -120119,6 +120064,20 @@ class nxSideMenu extends HTMLElement
120119
120064
  ninegrid.waitForInnerHTML(this)
120120
120065
  .then(() => this.#init())
120121
120066
  .catch(error => console.error(error));
120067
+
120068
+ setTimeout(() => {
120069
+ if (!ninegrid.querySelector("nx-side-menu-item.active", this.shadowRoot)) {
120070
+ const rawUrl = window.location.href; // 전체 URL
120071
+ const parsedUrl = new URL(rawUrl); // URL 객체로 파싱
120072
+ const href = parsedUrl.pathname; // 👉 경로만 추출
120073
+
120074
+ const el = ninegrid.querySelector(`nx-side-menu-item[href="${href}"]`, this.shadowRoot);
120075
+ if (el) {
120076
+ el.classList.add("active");
120077
+ }
120078
+ }
120079
+ }, 300);
120080
+
120122
120081
  };
120123
120082
 
120124
120083
  expand = () => {
@@ -120650,203 +120609,6 @@ class nxSpan extends HTMLElement
120650
120609
 
120651
120610
  customElements.define("nx-span", nxSpan);
120652
120611
 
120653
- class nxTab extends HTMLElement {
120654
- constructor() {
120655
- super();
120656
- this.attachShadow({ mode: 'open' });
120657
-
120658
- this.shadowRoot.innerHTML = `
120659
- <style>
120660
- :host(.theme-1) {
120661
- .tabs {
120662
- --border-bottom: 1px solid #eee;
120663
- gap: 3px;
120664
- }
120665
- .tab-button {
120666
- color: #666;
120667
- font-weight: 500;
120668
- transform: translateY(1px);
120669
- z-index: 1;
120670
- --border-bottom: 1px solid #ccc;
120671
- }
120672
- .tab-button.active {
120673
- color: #666;
120674
- font-weight: 700;
120675
- border-bottom: 1px solid white;
120676
- }
120677
- .tab-pages {
120678
- border: 1px solid #ccc;
120679
- }
120680
- }
120681
-
120682
- :host(.theme-2) {
120683
- .tabs {
120684
- border-bottom: 1px solid #eee;
120685
- gap: 40px;
120686
- }
120687
- .tab-button {
120688
- color: #666;
120689
- font-weight: 500;
120690
- border: none;
120691
- }
120692
- .tab-button.active {
120693
- color: green;
120694
- font-weight: 700;
120695
- border-bottom: 3px solid green;
120696
- }
120697
- }
120698
-
120699
- .tabs {
120700
- display: flex;
120701
- cursor: pointer;
120702
-
120703
- }
120704
- .tab-button {
120705
- padding: 8px;
120706
- border: 1px solid #ccc;
120707
- text-align: center;
120708
- outline: 0;
120709
- }
120710
- .tab-button:hover {
120711
- --filter: brightness(80%);
120712
- }
120713
- .tab-pages {
120714
- position: relative;
120715
- width: 100%;
120716
- overflow: hidden;
120717
- transition: height 0.5s ease-in-out;
120718
- }
120719
- .tab-page {
120720
- position: absolute;
120721
- width: 100%;
120722
- top: 0;
120723
- transition: left 0.5s ease-in-out;
120724
- padding: 20px;
120725
- box-sizing: border-box;
120726
- white-space: nowrap;
120727
- overflow: hidden;
120728
- text-overflow: ellipsis;
120729
- text-align: left;
120730
- }
120731
- .tab-page.active {
120732
- left: 0;
120733
- }
120734
- .tab-page.exit-left {
120735
- left: -100%;
120736
- }
120737
- .tab-page.exit-right {
120738
- left: 100%;
120739
- }
120740
- </style>
120741
- <div class="tabs"></div>
120742
- <div class="tab-pages"></div>
120743
- `;
120744
-
120745
- //console.log(this.shadowRoot.querySelector('.tab-page'));
120746
- //this.shadowRoot.querySelector('.tab-pages').style.height = this.shadowRoot.querySelector('.tab-page').style.height;
120747
-
120748
- this.switchTabHandler = this.#switchTab.bind(this);
120749
- }
120750
-
120751
- connectedCallback() {
120752
- this.classList.add(this.getAttribute("theme") || "theme-1");
120753
- this.#renderTabs();
120754
- this.shadowRoot.querySelectorAll('.tab-button').forEach(tab => {
120755
- tab.addEventListener('click', this.switchTabHandler);
120756
- });
120757
-
120758
- const firstTab = this.shadowRoot.querySelector('.tab-button');
120759
- const firstContent = this.shadowRoot.querySelector('.tab-page');
120760
- if (firstTab && firstContent) {
120761
- firstTab.classList.add('active');
120762
- firstContent.classList.add('active');
120763
- setTimeout(() => {
120764
- this.shadowRoot.querySelector('.tab-pages').style.height = `${firstContent.scrollHeight}px`;
120765
- }, 100);
120766
- //this.shadowRoot.querySelector('.tab-pages').style.height = `${firstContent.scrollHeight}px`;
120767
-
120768
- console.log(firstContent.style.height, firstContent.scrollHeight);
120769
- }
120770
-
120771
- this.shadowRoot.querySelectorAll('.tab-page:not(.active)').forEach(el => { el.classList.add('exit-right'); });
120772
- }
120773
-
120774
- #renderTabs() {
120775
- const tabs = this.shadowRoot.querySelector('.tabs');
120776
- const contents = this.shadowRoot.querySelector('.tab-pages');
120777
- const tabItems = this.querySelectorAll('nx-tab-page');
120778
-
120779
- tabItems.forEach((item, index) => {
120780
- const tab = document.createElement('div');
120781
- tab.classList.add('tab-button');
120782
- tab.textContent = item.getAttribute('caption');
120783
- tab.setAttribute('data-target', `content${index}`);
120784
- tabs.appendChild(tab);
120785
-
120786
- const content = document.createElement('div');
120787
- content.id = `content${index}`;
120788
- content.classList.add('tab-page');
120789
- content.innerHTML = item.innerHTML;
120790
- contents.appendChild(content);
120791
- });
120792
-
120793
- tabItems.forEach((item) => {
120794
- item.remove();
120795
- });
120796
- }
120797
-
120798
- #switchTab(event) {
120799
- const target = event.target;
120800
- if (!target.classList.contains('tab-button')) return;
120801
-
120802
- const targetId = target.getAttribute('data-target');
120803
- const activeTab = this.shadowRoot.querySelector('.tab-button.active');
120804
- const activeContent = this.shadowRoot.querySelector('.tab-page.active');
120805
- const newContent = this.shadowRoot.getElementById(targetId);
120806
-
120807
- if (activeTab === target) return; // 현재 탭을 클릭했을 때 아무런 변화가 없도록 함
120808
-
120809
- if (activeTab && activeContent) {
120810
- activeTab.classList.remove('active');
120811
- activeContent.classList.remove('active','exit-left','exit-right');
120812
- //newContent.classList.remove('exit-left','exit-right');
120813
- activeContent.style.left = '';
120814
-
120815
- if (activeTab.compareDocumentPosition(target) & Node.DOCUMENT_POSITION_FOLLOWING) {
120816
- activeContent.classList.add('exit-left');
120817
- } else {
120818
- activeContent.classList.add('exit-right');
120819
- }
120820
- //console.log(index);
120821
- //if (index == 1) return;
120822
-
120823
- newContent.classList.add('active');
120824
- newContent.style.left = '';
120825
-
120826
- newContent.classList.remove('exit-left','exit-right');
120827
- }
120828
-
120829
- target.classList.add('active');
120830
- //newContent.classList.add('active');
120831
- this.shadowRoot.querySelector('.tab-pages').style.height = `${newContent.scrollHeight}px`;
120832
-
120833
- //console.log(newContent.style.height, newContent.scrollHeight);
120834
- }
120835
- }
120836
-
120837
- class nxTabPage extends HTMLElement {
120838
- constructor() {
120839
- super();
120840
- }
120841
-
120842
- connectedCallback() {
120843
- this.caption = this.getAttribute('caption');
120844
- }
120845
- }
120846
-
120847
- customElements.define('nx-tab', nxTab);
120848
- customElements.define('nx-tab-page', nxTabPage);
120849
-
120850
120612
  class nxTest1 extends HTMLElement
120851
120613
  {
120852
120614
  constructor() {
@@ -121031,6 +120793,704 @@ Array.prototype.nineBinarySearch = function(target)
121031
120793
  return -1;
121032
120794
  };
121033
120795
 
120796
+ class nxDiv extends HTMLElement {
120797
+ constructor() {
120798
+ super();
120799
+ this.attachShadow({ mode: "open" });
120800
+ this.isCollapsed = false; // 검색 박스 상태 (true: 축소됨, false: 확장됨)
120801
+ }
120802
+
120803
+ connectedCallback() {
120804
+ this.#init();
120805
+ }
120806
+
120807
+ getJsonData = () => {
120808
+ const parent = this.shadowRoot.querySelector(".search-content");
120809
+ const elements = parent.querySelectorAll("[id]");
120810
+ const jsonData = {};
120811
+
120812
+ elements.forEach(element => {
120813
+ if (element.tagName === "INPUT" || element.tagName === "TEXTAREA" || element.tagName === "SELECT") {
120814
+ jsonData[element.id] = element.value;
120815
+ } else {
120816
+ jsonData[element.id] = element.textContent.trim();
120817
+ }
120818
+ });
120819
+
120820
+ return jsonData;
120821
+ };
120822
+
120823
+ getSearchOptions = () => {
120824
+
120825
+ };
120826
+
120827
+ #init = () => {
120828
+ const contents = this.innerHTML.trim();
120829
+ this.innerHTML = ""; // 기존 내부 HTML 제거
120830
+
120831
+ const htmlTmpl = document.createElement("template");
120832
+ htmlTmpl.innerHTML = `
120833
+ <style>
120834
+ @import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/nxDiv.css";
120835
+ ${ninegrid.getCustomPath(this,"nxDiv.css")}
120836
+ </style>
120837
+
120838
+ <div class="search-container">
120839
+ <span class="toggle-icon"></span>
120840
+ <div class="search-content">${contents}</div>
120841
+ </div>
120842
+ `;
120843
+
120844
+ this.shadowRoot.appendChild(htmlTmpl.content.cloneNode(true));
120845
+
120846
+ this.#setupToggle();
120847
+ }
120848
+
120849
+ #setupToggle = () => {
120850
+ const toggleIcon = this.shadowRoot.querySelector(".toggle-icon");
120851
+ const searchContainer = this.shadowRoot.querySelector(".search-container");
120852
+
120853
+ toggleIcon.addEventListener("click", () => {
120854
+ this.isCollapsed = !this.isCollapsed;
120855
+ //toggleIcon.innerHTML = this.isCollapsed ? "" : "X";
120856
+ searchContainer.classList.toggle("collapse", this.isCollapsed);
120857
+ });
120858
+ }
120859
+ }
120860
+
120861
+ customElements.define("nx-div", nxDiv);
120862
+
120863
+ class nxCollapse extends HTMLElement {
120864
+ #target;
120865
+ #targetPrevDisplay;
120866
+
120867
+ constructor() {
120868
+ super();
120869
+ this.attachShadow({ mode: "open" });
120870
+ }
120871
+
120872
+ connectedCallback() {
120873
+ const targetSelector = this.getAttribute("target");
120874
+ this.#target = ninegrid.querySelector(targetSelector);
120875
+ //if (!this.#target) return;
120876
+
120877
+ this.#init();
120878
+ }
120879
+
120880
+ #init = () => {
120881
+ this.innerHTML.trim();
120882
+ this.innerHTML = ""; // 기존 내부 HTML 제거
120883
+
120884
+ const htmlTmpl = document.createElement("template");
120885
+ htmlTmpl.innerHTML = `
120886
+ <style>
120887
+ @import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/nxCollapse.css";
120888
+ ${ninegrid.getCustomPath(this,"nxCollapse.css")}
120889
+ </style>
120890
+
120891
+ <button></button>
120892
+ `;
120893
+
120894
+ this.shadowRoot.appendChild(htmlTmpl.content.cloneNode(true));
120895
+
120896
+ if (this.#target && this.#target) {
120897
+ const collapseBtn = document.createElement("span");
120898
+ collapseBtn.className = "collapse-toggle";
120899
+
120900
+ const shadowRoot = this.#target.shadowRoot;
120901
+ (shadowRoot || this.#target).appendChild(collapseBtn);
120902
+
120903
+ const style = document.createElement("style");
120904
+ style.textContent = `
120905
+ span.collapse-toggle {
120906
+ position: absolute;
120907
+ top: 0;
120908
+ right: 0;
120909
+ cursor: pointer;
120910
+ width: 16px;
120911
+ height: 16px;
120912
+ border: none;
120913
+ transition: all 0.3s ease;
120914
+ background-repeat: no-repeat;
120915
+ background-position: center;
120916
+ background-size: auto;
120917
+ background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="darkgray" class="bi bi-box-arrow-in-up-right" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M6.364 13.5a.5.5 0 0 0 .5.5H13.5a1.5 1.5 0 0 0 1.5-1.5v-10A1.5 1.5 0 0 0 13.5 1h-10A1.5 1.5 0 0 0 2 2.5v6.636a.5.5 0 1 0 1 0V2.5a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 .5.5v10a.5.5 0 0 1-.5.5H6.864a.5.5 0 0 0-.5.5"/><path fill-rule="evenodd" d="M11 5.5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793l-8.147 8.146a.5.5 0 0 0 .708.708L10 6.707V10.5a.5.5 0 0 0 1 0z"/></svg>');
120918
+ }
120919
+
120920
+ span.collapse-toggle:hover {
120921
+ background-color: rgba(60, 60, 60, 0.1);
120922
+ box-shadow: 0 0 6px rgba(60, 60, 60, 0.4);
120923
+ transform: scale(1.2); /* 살짝 확대 */
120924
+ }
120925
+
120926
+ ${shadowRoot ? ":host(.nx-collapse)" : ".nx-collapse"} {
120927
+ animation: collapseShrink 0.7s ease forwards;
120928
+ pointer-events: none; /* 클릭 방지 */
120929
+ }
120930
+
120931
+ ${shadowRoot ? ":host(.nx-expand)" : ".nx-expand"} {
120932
+ animation: expandShrink 0.6s ease forwards;
120933
+ pointer-events: none; /* 클릭 방지 */
120934
+ }
120935
+
120936
+ @keyframes collapseShrink {
120937
+ 0% {
120938
+ opacity: 1;
120939
+ transform: scale(1);
120940
+ transform-origin: top right;
120941
+ }
120942
+ 100% {
120943
+ opacity: 0;
120944
+ transform: scale(0);
120945
+ transform-origin: top right;
120946
+ }
120947
+ }
120948
+
120949
+ @keyframes expandShrink {
120950
+ 0% {
120951
+ opacity: 0;
120952
+ transform: scale(0.5);
120953
+ transform-origin: top right;
120954
+ }
120955
+ 100% {
120956
+ opacity: 1;
120957
+ transform: scale(1);
120958
+ transform-origin: top right;
120959
+ }
120960
+ }
120961
+ `;
120962
+
120963
+ (shadowRoot || this.#target).appendChild(style);
120964
+
120965
+ // collapse 시 target 숨기고 복원 버튼 등장
120966
+ collapseBtn.addEventListener("click", () => {
120967
+ // fade-out 클래스 먼저 넣고 → 일정 시간 후 display: none
120968
+ // STEP 1: 복원 버튼 애니메이션 시작 (살짝 빠르게 먼저 등장)
120969
+ this.#target.classList.add("nx-collapse");
120970
+
120971
+ setTimeout(() => {
120972
+ // STEP 2: target이 줄어드는 shrinking 애니메이션 시작
120973
+ //this.style.display = "inline-block";
120974
+ this.classList.add("nx-collapse");
120975
+ }, 100); // appearing 시작 후 100ms 딜레이로 target 축소 시작
120976
+
120977
+ setTimeout(() => {
120978
+ // STEP 3: 실제 target 숨기기
120979
+ this.#targetPrevDisplay = this.#target.style.display;
120980
+ this.#target.style.display = "none";
120981
+ }, 500); // shrinking 애니메이션이 끝나고 실제 제거
120982
+ });
120983
+ }
120984
+
120985
+ // 복원 시 target 보이고 복원 버튼 사라짐
120986
+ /**
120987
+ this.shadowRoot.querySelector("button").addEventListener("click", () => {
120988
+ this.#target.classList.remove("nx-collapse");
120989
+ this.classList.remove("nx-collapse");
120990
+
120991
+ this.#target.style.display = this.#targetPrevDisplay;
120992
+ this.style.display = "none";
120993
+ }); */
120994
+
120995
+ this.shadowRoot.querySelector("button").addEventListener("click", () => {
120996
+ // STEP 1: 복원 대상에 애니메이션 클래스 부여
120997
+ this.#target.style.display = this.#targetPrevDisplay;
120998
+ this.#target.classList.remove("nx-collapse");
120999
+ this.#target.classList.add("nx-expand"); // expandReveal 적용
121000
+
121001
+ setTimeout(() => {
121002
+ // STEP 2: target이 줄어드는 shrinking 애니메이션 시작
121003
+ this.classList.remove("nx-collapse");
121004
+ //this.style.display = "inline-block";
121005
+ this.classList.add("nx-expand");
121006
+ }, 100); // appearing 시작 후 100ms 딜레이로 target 축소 시작
121007
+
121008
+
121009
+ setTimeout(() => {
121010
+ // STEP 2: 실제 화면에 표시
121011
+
121012
+ //this.style.display = "none"; // 복원 버튼은 사라짐
121013
+
121014
+ // STEP 3: 애니메이션이 끝난 후 pointer-events 복구 (선택적)
121015
+ this.#target.classList.remove("nx-expand");
121016
+ this.classList.remove("nx-expand");
121017
+ //this.#target.style.pointerEvents = "auto";
121018
+ }, 600); // 애니메이션과 동일한 시간
121019
+ });
121020
+
121021
+ }
121022
+ }
121023
+
121024
+ customElements.define("nx-collapse", nxCollapse);
121025
+
121026
+ class nxTab extends HTMLElement {
121027
+ constructor() {
121028
+ super();
121029
+ this.attachShadow({ mode: 'open' });
121030
+
121031
+ this.shadowRoot.innerHTML = `
121032
+ <style>
121033
+ @import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/nxTab.css";
121034
+ ${ninegrid.getCustomPath(this,"nxTab.css")}
121035
+ </style>
121036
+
121037
+ <div class="tabs"></div>
121038
+ <div class="tab-pages"></div>
121039
+ `;
121040
+
121041
+ //console.log(this.shadowRoot.querySelector('.tab-page'));
121042
+ //this.shadowRoot.querySelector('.tab-pages').style.height = this.shadowRoot.querySelector('.tab-page').style.height;
121043
+
121044
+ this.switchTabHandler = this.#switchTab.bind(this);
121045
+ }
121046
+
121047
+ connectedCallback() {
121048
+ this.#init();
121049
+ this.dispatchEvent(new CustomEvent(ninegrid.EVENT.TAB_LOAD, { bubbles: true, detail: {} }));
121050
+ }
121051
+
121052
+ #renderTabs() {
121053
+ const tabs = this.shadowRoot.querySelector('.tabs');
121054
+ const contents = this.shadowRoot.querySelector('.tab-pages');
121055
+ const tabItems = this.querySelectorAll('nx-tab-page');
121056
+
121057
+ tabItems.forEach((item, index) => {
121058
+ const tab = document.createElement('div');
121059
+ tab.classList.add('tab-button');
121060
+ tab.textContent = item.getAttribute('caption');
121061
+ tab.setAttribute('data-target', `content${index}`);
121062
+ tabs.appendChild(tab);
121063
+
121064
+ const content = document.createElement('div');
121065
+ content.id = `content${index}`;
121066
+ content.classList.add('tab-page');
121067
+ content.innerHTML = item.innerHTML;
121068
+ contents.appendChild(content);
121069
+ });
121070
+
121071
+ tabItems.forEach((item) => {
121072
+ item.remove();
121073
+ });
121074
+ }
121075
+
121076
+ #switchTab(event) {
121077
+ const target = event.target;
121078
+ if (!target.classList.contains('tab-button')) return;
121079
+
121080
+ const targetId = target.getAttribute('data-target');
121081
+ const activeTab = this.shadowRoot.querySelector('.tab-button.active');
121082
+ const activeContent = this.shadowRoot.querySelector('.tab-page.active');
121083
+ const newContent = this.shadowRoot.getElementById(targetId);
121084
+
121085
+ if (activeTab === target) return; // 현재 탭을 클릭했을 때 아무런 변화가 없도록 함
121086
+
121087
+ if (activeTab && activeContent) {
121088
+ activeTab.classList.remove('active');
121089
+ activeContent.classList.remove('active','exit-left','exit-right');
121090
+ //newContent.classList.remove('exit-left','exit-right');
121091
+ activeContent.style.left = '';
121092
+
121093
+ if (activeTab.compareDocumentPosition(target) & Node.DOCUMENT_POSITION_FOLLOWING) {
121094
+ activeContent.classList.add('exit-left');
121095
+ } else {
121096
+ activeContent.classList.add('exit-right');
121097
+ }
121098
+ //console.log(index);
121099
+ //if (index == 1) return;
121100
+
121101
+ newContent.classList.add('active');
121102
+ newContent.style.left = '';
121103
+
121104
+ newContent.classList.remove('exit-left','exit-right');
121105
+ }
121106
+
121107
+ target.classList.add('active');
121108
+ //newContent.classList.add('active');
121109
+ this.shadowRoot.querySelector('.tab-pages').style.height = `${newContent.scrollHeight}px`;
121110
+
121111
+ //console.log(newContent.style.height, newContent.scrollHeight);
121112
+ };
121113
+
121114
+ #init = () => {
121115
+
121116
+ this.classList.add(this.getAttribute("theme") || "theme-1");
121117
+ this.#renderTabs();
121118
+ this.shadowRoot.querySelectorAll('.tab-button').forEach(tab => {
121119
+ tab.addEventListener('click', this.switchTabHandler);
121120
+ });
121121
+
121122
+ const firstTab = this.shadowRoot.querySelector('.tab-button');
121123
+ const firstContent = this.shadowRoot.querySelector('.tab-page');
121124
+ if (firstTab && firstContent) {
121125
+ firstTab.classList.add('active');
121126
+ firstContent.classList.add('active');
121127
+ setTimeout(() => {
121128
+ this.shadowRoot.querySelector('.tab-pages').style.height = `${firstContent.scrollHeight}px`;
121129
+ }, 100);
121130
+ //this.shadowRoot.querySelector('.tab-pages').style.height = `${firstContent.scrollHeight}px`;
121131
+
121132
+ console.log(firstContent.style.height, firstContent.scrollHeight);
121133
+ }
121134
+
121135
+ this.shadowRoot.querySelectorAll('.tab-page:not(.active)').forEach(el => { el.classList.add('exit-right'); });
121136
+
121137
+ const resizeObserver = new ResizeObserver(() => {
121138
+ const activeContent = this.shadowRoot.querySelector('.tab-page.active');
121139
+ if (activeContent) {
121140
+ this.shadowRoot.querySelector('.tab-pages').style.height = `${activeContent.scrollHeight}px`;
121141
+ }
121142
+ });
121143
+
121144
+ const activeContent = this.shadowRoot.querySelector('.tab-page.active');
121145
+ if (activeContent) resizeObserver.observe(activeContent);
121146
+ };
121147
+ }
121148
+
121149
+ class nxTabPage extends HTMLElement {
121150
+ constructor() {
121151
+ super();
121152
+ }
121153
+
121154
+ connectedCallback() {
121155
+ this.caption = this.getAttribute('caption');
121156
+ }
121157
+ }
121158
+
121159
+ customElements.define('nx-tab', nxTab);
121160
+ customElements.define('nx-tab-page', nxTabPage);
121161
+
121162
+ class nxSplitter extends HTMLElement {
121163
+ #mode;
121164
+
121165
+ constructor() {
121166
+ super();
121167
+ this.attachShadow({ mode: "open" });
121168
+ }
121169
+
121170
+ connectedCallback() {
121171
+ this.#init();
121172
+ }
121173
+
121174
+ #detectMode = (el) => {
121175
+ const prev = el.previousElementSibling;
121176
+ const next = el.nextElementSibling;
121177
+
121178
+ const prevRect = prev?.getBoundingClientRect();
121179
+ const nextRect = next?.getBoundingClientRect();
121180
+
121181
+ this.#mode = (!prevRect || !nextRect) || (Math.abs(prevRect.top - nextRect.top) < 5) ? "h" : "v";
121182
+ };
121183
+
121184
+ #startDrag = (e) => {
121185
+ e.preventDefault();
121186
+ e.stopPropagation();
121187
+
121188
+ const splitterRect = this.getBoundingClientRect();
121189
+ const isHorizontal = this.#mode === "h";
121190
+
121191
+ const clickOffset = isHorizontal
121192
+ ? e.clientX - splitterRect.left
121193
+ : e.clientY - splitterRect.top;
121194
+
121195
+ const dragBar = document.createElement("div");
121196
+ dragBar.className = `nx-splitter-drag-bar-${this.#mode}`;
121197
+
121198
+ Object.assign(dragBar.style, {
121199
+ position: "absolute",
121200
+ zIndex: "999",
121201
+ background: "#666",
121202
+ opacity: "0.6",
121203
+ pointerEvents: "none"
121204
+ });
121205
+
121206
+ const root = this.getRootNode({ composed: true });
121207
+ const parent = root instanceof ShadowRoot ? root.host : this.parentElement;
121208
+
121209
+ const prev = this.previousElementSibling;
121210
+ const next = this.nextElementSibling;
121211
+
121212
+ if (!parent || !prev || !next) {
121213
+ console.warn("Spliter's parent or siblings not found.");
121214
+ return;
121215
+ }
121216
+
121217
+ (parent.shadowRoot || parent).appendChild(dragBar);
121218
+
121219
+ const dragBarOffsetParent = dragBar.offsetParent;
121220
+
121221
+ if (!dragBarOffsetParent) {
121222
+ console.error("dragBar's offsetParent could not be determined. Ensure parent or an ancestor has a position property (e.g., relative).");
121223
+ dragBar.remove();
121224
+ return;
121225
+ }
121226
+
121227
+ const dragBarOffsetParentRect = dragBarOffsetParent.getBoundingClientRect();
121228
+ const prevRect = prev.getBoundingClientRect();
121229
+ const nextRect = next.getBoundingClientRect();
121230
+
121231
+ let initialPosInOffsetParent;
121232
+ if (isHorizontal) {
121233
+ initialPosInOffsetParent = e.clientX - dragBarOffsetParentRect.left;
121234
+ dragBar.style.top = "0";
121235
+ dragBar.style.left = `${initialPosInOffsetParent}px`;
121236
+ dragBar.style.width = "1px";
121237
+ dragBar.style.height = "100%";
121238
+ } else {
121239
+ initialPosInOffsetParent = e.clientY - dragBarOffsetParentRect.top;
121240
+ dragBar.style.left = "0";
121241
+ dragBar.style.top = `${initialPosInOffsetParent}px`;
121242
+ dragBar.style.height = "1px";
121243
+ dragBar.style.width = "100%";
121244
+ }
121245
+
121246
+ const minLimit = isHorizontal
121247
+ ? prevRect.left - dragBarOffsetParentRect.left
121248
+ : prevRect.top - dragBarOffsetParentRect.top;
121249
+ const maxLimit = isHorizontal
121250
+ ? nextRect.right - dragBarOffsetParentRect.left
121251
+ : nextRect.bottom - dragBarOffsetParentRect.top;
121252
+
121253
+
121254
+ const onMove = moveEvent => {
121255
+ const clientPos = isHorizontal ? moveEvent.clientX : moveEvent.clientY;
121256
+ const currentPosInOffsetParent = isHorizontal
121257
+ ? clientPos - dragBarOffsetParentRect.left
121258
+ : clientPos - dragBarOffsetParentRect.top;
121259
+
121260
+ const clampedPos = Math.max(minLimit, Math.min(currentPosInOffsetParent, maxLimit));
121261
+
121262
+ if (isHorizontal) {
121263
+ dragBar.style.left = `${clampedPos}px`;
121264
+ } else {
121265
+ dragBar.style.top = `${clampedPos}px`;
121266
+ }
121267
+
121268
+ console.log(clampedPos);
121269
+ };
121270
+
121271
+ const onUp = (upEvent) => {
121272
+ window.removeEventListener("mousemove", onMove);
121273
+ window.removeEventListener("mouseup", onUp);
121274
+ dragBar.remove();
121275
+
121276
+ const finalDragBarPosInOffsetParent = isHorizontal
121277
+ ? parseFloat(dragBar.style.left)
121278
+ : parseFloat(dragBar.style.top);
121279
+
121280
+ // ⭐⭐ offsetParentTotalSize 변수를 여기서 정의합니다 ⭐⭐
121281
+ const offsetParentTotalSize = isHorizontal
121282
+ ? dragBarOffsetParentRect.width
121283
+ : dragBarOffsetParentRect.height;
121284
+
121285
+ const splitterThickness = isHorizontal ? splitterRect.width : splitterRect.height;
121286
+
121287
+ // prev의 시작점 (offsetParent 기준)
121288
+ const prevStartPosInOffsetParent = isHorizontal
121289
+ ? prevRect.left - dragBarOffsetParentRect.left
121290
+ : prevRect.top - dragBarOffsetParentRect.top;
121291
+
121292
+ // prev의 새로운 크기 (dragBar의 왼쪽 위치 - prev의 왼쪽 위치)
121293
+ let finalPrevSize = finalDragBarPosInOffsetParent - prevStartPosInOffsetParent;
121294
+
121295
+ // next의 새로운 크기 (offsetParent의 전체 크기 - prev 크기 - 스플리터 두께)
121296
+ // 이 방법이 전체 공간을 정확히 채우면서 오차를 마지막 패널에 몰아넣는 데 더 견고합니다.
121297
+ const MIN_PANEL_SIZE_PX = 1;
121298
+ finalPrevSize = Math.max(MIN_PANEL_SIZE_PX, finalPrevSize) - clickOffset;
121299
+ let finalNextSize = Math.max(MIN_PANEL_SIZE_PX, offsetParentTotalSize - finalPrevSize - splitterThickness);
121300
+
121301
+ // flex 속성 저장 및 복원
121302
+ const originalPrevFlex = prev.style.flex;
121303
+ const originalNextFlex = next.style.flex;
121304
+
121305
+ // 드래그 중에는 flex를 "none"으로 설정하여 직접 크기 조절
121306
+ prev.style.flex = "none";
121307
+ next.style.flex = "none";
121308
+
121309
+ console.log(finalPrevSize, finalNextSize, offsetParentTotalSize);
121310
+
121311
+ if (isHorizontal) {
121312
+ prev.style.width = `${finalPrevSize}px`;
121313
+ next.style.width = `${finalNextSize}px`;
121314
+ } else {
121315
+ prev.style.height = `${finalPrevSize}px`;
121316
+ next.style.height = `${finalNextSize}px`;
121317
+ }
121318
+
121319
+ // 작업 완료 후 원래 flex 값으로 복원
121320
+ prev.style.flex = originalPrevFlex;
121321
+ next.style.flex = originalNextFlex;
121322
+ };
121323
+
121324
+ window.addEventListener("mousemove", onMove);
121325
+ window.addEventListener("mouseup", onUp);
121326
+ };
121327
+
121328
+
121329
+ #init = () => {
121330
+ this.#detectMode(this);
121331
+
121332
+ this.classList.add(this.#mode);
121333
+
121334
+ const contents = this.innerHTML.trim();
121335
+ const gripClass = `grip-${this.#mode}`;
121336
+ const gripTmpl = (contents === "") ? `<div class="${gripClass}"></div>`: `<div class="${gripClass}"></div><div class="inner-container">${contents}</div><div class="${gripClass}"></div>`;
121337
+
121338
+ this.innerHTML = "";
121339
+ const htmlTmpl = document.createElement("template");
121340
+ htmlTmpl.innerHTML = `
121341
+ <style>
121342
+ @import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/nxSplitter.css";
121343
+ ${ninegrid.getCustomPath(this,"nxSplitter.css")}
121344
+ </style>
121345
+
121346
+ ${gripTmpl}
121347
+ `;
121348
+
121349
+ this.shadowRoot.appendChild(htmlTmpl.content.cloneNode(true));
121350
+
121351
+ this.shadowRoot.querySelectorAll(".grip-h,.grip-v").forEach(el => {
121352
+ el.addEventListener("mousedown", e => this.#startDrag(e));
121353
+ });
121354
+
121355
+ this.#prepareLayout();
121356
+
121357
+ window.addEventListener("resize", () => this.#prepareLayout());
121358
+ };
121359
+
121360
+ #isLastSplitter = () => {
121361
+ const parent = this.parentElement;
121362
+ if (!parent) return false;
121363
+
121364
+ const allSplitters = [...parent.querySelectorAll("nx-splitter")];
121365
+ return allSplitters.at(-1) === this;
121366
+ };
121367
+
121368
+ #getSiblingSizeSum = () => {
121369
+ const parent = this.parentElement;
121370
+ if (!parent) return 0;
121371
+
121372
+ const isHorizontal = this.#mode === "h";
121373
+
121374
+ return Array.from(parent.children).reduce((sum, el) => {
121375
+ const size = isHorizontal ? el.offsetWidth : el.offsetHeight;
121376
+ return sum + size;
121377
+ }, 0);
121378
+ };
121379
+
121380
+
121381
+ #prepareLayout = () => {
121382
+
121383
+ const isHorizontal = this.#mode === "h";
121384
+ const prev = this.previousElementSibling;
121385
+ const next = this.nextElementSibling;
121386
+ const parent = this.parentElement;
121387
+ if (!prev || !next || !parent) return;
121388
+
121389
+ const currentTotal = this.#getSiblingSizeSum();
121390
+ const nextTotal = isHorizontal
121391
+ ? parent.getBoundingClientRect().width
121392
+ : parent.getBoundingClientRect().height;
121393
+
121394
+ const prevSize = isHorizontal ? prev.offsetWidth : prev.offsetHeight;
121395
+ const nextSize = isHorizontal ? next.offsetWidth : next.offsetHeight;
121396
+
121397
+ const newPrevSize = nextTotal * prevSize / currentTotal;
121398
+ const newNextSize = nextTotal * nextSize / currentTotal;
121399
+
121400
+
121401
+ if (isHorizontal) {
121402
+ prev.style.width = `${newPrevSize}px`;
121403
+
121404
+ if (this.#isLastSplitter()) {
121405
+ next.style.width = `${newNextSize}px`;
121406
+ }
121407
+ } else {
121408
+ prev.style.height = `${newPrevSize}px`;
121409
+
121410
+ if (this.#isLastSplitter()) {
121411
+ next.style.height = `${newNextSize}px`;
121412
+ }
121413
+ }
121414
+ };
121415
+
121416
+ }
121417
+
121418
+ customElements.define("nx-splitter", nxSplitter);
121419
+
121420
+ class nxForm extends HTMLElement {
121421
+ #mode;
121422
+
121423
+ constructor() {
121424
+ super();
121425
+ this.attachShadow({ mode: "open" });
121426
+ }
121427
+
121428
+ connectedCallback() {
121429
+ this.#init();
121430
+ }
121431
+
121432
+ getData_BAK = () => {
121433
+ const jsonData = {};
121434
+
121435
+ this.shadowRoot.querySelectorAll("[id]").forEach(el => {
121436
+ if (el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.tagName === "SELECT") {
121437
+ jsonData[el.id] = el.value;
121438
+ } else {
121439
+ jsonData[el.id] = el.textContent.trim();
121440
+ }
121441
+ });
121442
+
121443
+ return jsonData;
121444
+ };
121445
+
121446
+ getData = () => {
121447
+ const jsonData = {};
121448
+
121449
+ this.shadowRoot.querySelectorAll("input, textarea, select, [id], [name]").forEach(el => {
121450
+ const key = el.id || el.name; // id가 있으면 우선, 없으면 name 사용
121451
+ if (!key) return; // id도 name도 없으면 건너뛰기
121452
+
121453
+ const value = (el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.tagName === "SELECT")
121454
+ ? el.value
121455
+ : el.textContent.trim();
121456
+
121457
+ // 중복 키를 대비한 배열 처리
121458
+ if (jsonData[key]) {
121459
+ if (!Array.isArray(jsonData[key])) {
121460
+ jsonData[key] = [jsonData[key]];
121461
+ }
121462
+ jsonData[key].push(value);
121463
+ } else {
121464
+ jsonData[key] = value;
121465
+ }
121466
+ });
121467
+
121468
+ return jsonData;
121469
+ };
121470
+
121471
+
121472
+ #init = () => {
121473
+
121474
+ const contents = this.innerHTML.trim();
121475
+
121476
+ this.innerHTML = ""; // 기존 내부 HTML 제거
121477
+ const htmlTmpl = document.createElement("template");
121478
+ htmlTmpl.innerHTML = `
121479
+ <style>
121480
+ @import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/nxForm.css";
121481
+ ${ninegrid.getCustomPath(this,"nxForm.css")}
121482
+ </style>
121483
+
121484
+ ${contents}
121485
+ `;
121486
+
121487
+ this.shadowRoot.appendChild(htmlTmpl.content.cloneNode(true));
121488
+ };
121489
+
121490
+ }
121491
+
121492
+ customElements.define("nx-form", nxForm);
121493
+
121034
121494
  class aiSettings extends HTMLElement
121035
121495
  {
121036
121496
  constructor() {
@@ -121405,6 +121865,80 @@ class aiMyMessage extends HTMLElement
121405
121865
  };
121406
121866
  }
121407
121867
 
121868
+ // aiProgressMessage 클래스 정의
121869
+ class aiProgressMessage extends HTMLElement {
121870
+ #progressData = []; // 진행 상태 데이터를 저장할 배열
121871
+ #progressElements = new Map(); // 각 진행 단계의 DOM 요소를 저장할 Map
121872
+
121873
+ constructor() {
121874
+ super();
121875
+ this.attachShadow({ mode: 'open' });
121876
+ }
121877
+
121878
+ connectedCallback() {
121879
+ this.shadowRoot.innerHTML = `
121880
+ <style>
121881
+ @import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/ai.css";
121882
+ ${ninegrid.getCustomPath(this, "ai.css")}
121883
+ </style>
121884
+ <div class="chat-message progress-message">
121885
+ <ul class="progress-list"></ul>
121886
+ </div>
121887
+ `;
121888
+ }
121889
+
121890
+ /**
121891
+ * 진행 상태 데이터를 초기화하고 화면에 표시합니다.
121892
+ * @param {Array<Object>} data - [{id: 'step1', message: '분석중입니다...', completedMessage: '분석이 완료되었습니다.'}, ...]
121893
+ */
121894
+ initialize(data) {
121895
+ this.#progressData = data.map(item => ({ ...item, status: 'pending' })); // 초기 상태는 'pending'
121896
+ this.#renderProgress();
121897
+ }
121898
+
121899
+ /**
121900
+ * 특정 단계의 진행 상태를 업데이트합니다.
121901
+ * @param {string} id - 업데이트할 단계의 고유 ID
121902
+ * @param {string} status - 'completed' 또는 'pending'
121903
+ */
121904
+ updateProgress(id, status) {
121905
+ const itemIndex = this.#progressData.findIndex(item => item.id === id);
121906
+ if (itemIndex > -1) {
121907
+ this.#progressData[itemIndex].status = status;
121908
+ this.#updateProgressItem(this.#progressData[itemIndex]);
121909
+ }
121910
+ }
121911
+
121912
+ #renderProgress() {
121913
+ const progressList = this.shadowRoot.querySelector(".progress-list");
121914
+ progressList.innerHTML = ''; // 기존 목록 초기화
121915
+ this.#progressElements.clear(); // 기존 요소 Map 초기화
121916
+
121917
+ this.#progressData.forEach((item, index) => {
121918
+ const li = document.createElement("li");
121919
+ li.setAttribute("data-id", item.id);
121920
+ li.classList.add("progress-item", item.status);
121921
+ li.innerHTML = `
121922
+ <span class="icon"></span>
121923
+ <span class="text">${item.message}</span>
121924
+ `;
121925
+ progressList.appendChild(li);
121926
+ this.#progressElements.set(item.id, li); // DOM 요소 참조 저장
121927
+ });
121928
+ }
121929
+
121930
+ #updateProgressItem(item) {
121931
+ const li = this.#progressElements.get(item.id);
121932
+ if (li) {
121933
+ li.classList.remove('pending', 'completed'); // 기존 클래스 제거
121934
+ li.classList.add(item.status); // 새 상태 클래스 추가
121935
+ li.querySelector(".text").textContent = item.status === 'completed' ? item.completedMessage : item.message;
121936
+ }
121937
+ }
121938
+ }
121939
+
121940
+
121941
+
121408
121942
  class aiChat extends HTMLElement
121409
121943
  {
121410
121944
  constructor() {
@@ -121475,11 +122009,47 @@ class aiChat extends HTMLElement
121475
122009
  el.scrollIntoView({ behavior: "smooth", block: "end" });
121476
122010
  }, 200);
121477
122011
  };
122012
+
122013
+ /**
122014
+ * 챗 메시지를 추가합니다.
122015
+ * @param {string} sender - 메시지 발신자 ('me', 'ing', 'ai', 'progress')
122016
+ * @param {string} message - 메시지 내용 (progress 타입의 경우 사용되지 않음)
122017
+ * @param {Array<Object>} info - nine-grid 정보 (ai 타입에만 해당)
122018
+ * @param {Array<Object>} data - nine-grid 데이터 (ai 타입에만 해당)
122019
+ * @param {Array<string>|string} unique - nine-grid 고유 키 (ai 타입에만 해당)
122020
+ * @param {Array<Object>} progressData - aiProgressMessage의 초기 진행 데이터 (progress 타입에만 해당)
122021
+ * @returns {HTMLElement|null} 생성된 메시지 엘리먼트 (특히 aiProgressMessage의 경우 업데이트를 위해 반환)
122022
+ */
122023
+ addProgress = (progressData = null) => { // progressData 파라미터 추가
122024
+
122025
+ const target = this.shadowRoot.querySelector(".chat-body");
122026
+
122027
+ this.shadowRoot.querySelectorAll("nx-ai-ing-message").forEach(el => {
122028
+ el.remove();
122029
+ });
122030
+
122031
+ let el = document.createElement("nx-ai-progress-message");
122032
+ if (progressData) {
122033
+ el.initialize(progressData); // progressData로 초기화
122034
+ }
122035
+ target.appendChild(el);
122036
+
122037
+ setTimeout(() => {
122038
+ if (el) { // el이 null이 아닐 때만 scrollIntoView 호출
122039
+ el.scrollIntoView({ behavior: "smooth", block: "end" });
122040
+ }
122041
+ }, 200);
122042
+
122043
+ return el; // 생성된 엘리먼트를 반환
122044
+ };
121478
122045
  }
121479
122046
 
122047
+
122048
+
121480
122049
  customElements.define("nx-ai-message", aiMessage);
121481
122050
  customElements.define("nx-ai-ing-message", aiIngMessage);
121482
122051
  customElements.define("nx-ai-my-message", aiMyMessage);
122052
+ customElements.define("nx-ai-progress-message", aiProgressMessage);
121483
122053
  customElements.define("nx-ai-chat", aiChat);
121484
122054
 
121485
122055
  /**
@@ -201847,351 +202417,6 @@ class IdeAssi extends HTMLElement
201847
202417
  //this.shadowRoot.querySelector('ide-diff-popup').remove();
201848
202418
 
201849
202419
  this.shadowRoot.appendChild(document.createElement('ide-diff-popup'));
201850
-
201851
- //setTimeout(() => {
201852
- let src1 = `
201853
- import React, { useRef, useEffect } from "react" adfa;
201854
- import { api, ai } from "ide-assi";
201855
- import ninegrid from "ninegrid2";
201856
-
201857
- const DocManager = () => {
201858
- const tabRef = useRef(null);
201859
- const gridRef = useRef(null);
201860
-
201861
- const selectList = async (params) => {
201862
- if (!gridRef.current) return;
201863
- gridRef.current.classList.add("loading");
201864
- api.post(\`/api/tmpl-a/doc-manager/selectList\`, params).then((res) => {
201865
- gridRef.current.data.source = res.list;
201866
- });
201867
- };
201868
-
201869
- const handleNaturalLanguageSearch = async () => {
201870
- const searchTextElement = ninegrid.querySelector("#searchText", tabRef.current);
201871
- const searchText = searchTextElement ? searchTextElement.value : "";
201872
-
201873
- if (!gridRef.current) return;
201874
- gridRef.current.classList.add("loading");
201875
-
201876
- let params = {};
201877
- if (searchText) {
201878
- params = {
201879
- _whereClause: await ai.generateWhereCause(
201880
- "tmpla/DocManagerMapper.xml",
201881
- "selectList",
201882
- searchText,
201883
- import.meta.env.VITE_GEMINI_API_KEY
201884
- ),
201885
- };
201886
- }
201887
- selectList(params);
201888
- };
201889
-
201890
- const handleClassicSearch = () => {
201891
- if (!gridRef.current) return;
201892
- gridRef.current.classList.add("loading");
201893
-
201894
- const form2Element = ninegrid.querySelector(".form2", tabRef.current);
201895
- const params = form2Element ? form2Element.getData() : {};
201896
- selectList(params);
201897
- };
201898
-
201899
- useEffect(() => {
201900
- selectList({});
201901
-
201902
- const searchTextElement = ninegrid.querySelector("#searchText", tabRef.current);
201903
- const searchButton = ninegrid.querySelector(".search", tabRef.current);
201904
-
201905
- const handleKeyDown = (e) => {
201906
- if (e.key === "Enter" && !e.isComposing) {
201907
- handleNaturalLanguageSearch();
201908
- }
201909
- };
201910
-
201911
- const handleClick = () => {
201912
- handleClassicSearch();
201913
- };
201914
-
201915
- if (searchTextElement) {
201916
- searchTextElement.addEventListener("keydown", handleKeyDown);
201917
- }
201918
- if (searchButton) {
201919
- searchButton.addEventListener("click", handleClick);
201920
- }
201921
-
201922
- return () => {
201923
- if (searchTextElement) {
201924
- searchTextElement.removeEventListener("keydown", handleKeyDown);
201925
- }
201926
- if (searchButton) {
201927
- searchButton.removeEventListener("click", handleClick);
201928
- }
201929
- };
201930
- }, []);
201931
-
201932
- return (
201933
- <div className="wrapper">
201934
- <nx-collapse target="nx-tab" className="search-collapse"></nx-collapse>
201935
- <div className="list-wrapper">
201936
- <nx-tab theme="theme-3" ref={tabRef}>
201937
- <nx-tab-page caption="자연어 검색">
201938
- <nx-form className="form1">
201939
- <input
201940
- type="text"
201941
- id="searchText"
201942
- name="searchText"
201943
- placeholder="자연어 검색어를 입력하세요 (ex: 작성자가 홍길동인 데이타를 찾아줘)"
201944
- />
201945
- </nx-form>
201946
- </nx-tab-page>
201947
- <nx-tab-page caption="클래식 검색">
201948
- <nx-form className="form2">
201949
- <label>문서명: <input type="text" name="docNm" /></label>
201950
- <label>매출액:
201951
- <input type="number" name="minAmt" placeholder="최소" /> ~
201952
- <input type="number" name="maxAmt" placeholder="최대" />
201953
- </label>
201954
- </nx-form>
201955
- <button className="search">검색</button>
201956
- </nx-tab-page>
201957
- </nx-tab>
201958
-
201959
- <div className="grid-wrapper">
201960
- <nine-grid
201961
- ref={gridRef}
201962
- caption="문서관리"
201963
- select-type="row"
201964
- show-title-bar="true"
201965
- show-menu-icon="true"
201966
- show-status-bar="true"
201967
- enable-fixed-col="true"
201968
- row-resizable="false"
201969
- col-movable="true"
201970
- >
201971
- <table>
201972
- <colgroup>
201973
- <col width="50" fixed="left" background-color="gray" />
201974
- <col width="100" />
201975
- <col width="100" />
201976
- <col width="200" />
201977
- <col width="120" />
201978
- <col width="100" />
201979
- <col width="150" />
201980
- <col width="150" />
201981
- </colgroup>
201982
- <thead>
201983
- <tr>
201984
- <th>No.</th>
201985
- <th>최종수정자</th>
201986
- <th>문서ID</th>
201987
- <th>문서명</th>
201988
- <th>매출액</th>
201989
- <th>최초등록자</th>
201990
- <th>최초등록일</th>
201991
- <th>최종수정일</th>
201992
- </tr>
201993
- </thead>
201994
- <tbody>
201995
- <tr>
201996
- <th><ng-row-indicator /></th>
201997
- <td data-bind="updateUser" text-align="center"></td>
201998
- <td data-bind="docId" text-align="center"></td>
201999
- <td data-bind="docNm" text-align="left"></td>
202000
- <td
202001
- data-bind="amt"
202002
- data-expr="data.amt.toLocaleString()"
202003
- text-align="right"
202004
- show-icon="true"
202005
- icon-type="sphere"
202006
- icon-color="data.amt >= 2000 ? 'red' : 'gray'"
202007
- ></td>
202008
- <td data-bind="insertUser" text-align="center"></td>
202009
- <td data-bind="insertDt" text-align="center"></td>
202010
- <td data-bind="updateDt" text-align="center"></td>
202011
- </tr>
202012
- </tbody>
202013
- </table>
202014
- </nine-grid>
202015
- </div>
202016
- </div>
202017
- </div>
202018
- );
202019
- };
202020
-
202021
- export default DocManager;
202022
- `;
202023
-
202024
- let src2 = `
202025
- import React, { useRef, useEffect } from "react";
202026
- import { api, ai } from "ide-assi";
202027
- import ninegrid from "ninegrid2";
202028
-
202029
- const DocManager = () => {
202030
- const tabRef = useRef(null);
202031
- const gridRef = useRef(null);
202032
-
202033
- const selectList = async (params) => {
202034
- if (!gridRef.current) return;
202035
- gridRef.current.classList.add("loading");
202036
- api.post(\`/api/tmpl-a/doc-manager/selectList\`, params).then((res) => {
202037
- gridRef.current.data.source = res.list;
202038
- });
202039
- };
202040
-
202041
- const handleNaturalLanguageSearch = async () => {
202042
- const searchTextElement = ninegrid.querySelector("#searchText", tabRef.current);
202043
- const searchText = searchTextElement ? searchTextElement.value : "";
202044
-
202045
- if (!gridRef.current) return;
202046
- gridRef.current.classList.add("loading");
202047
-
202048
- let params = {};
202049
- if (searchText) {
202050
- params = {
202051
- _whereClause: await ai.generateWhereCause(
202052
- "tmpla/DocManagerMapper.xml",
202053
- "selectList",
202054
- searchText,
202055
- import.meta.env.VITE_GEMINI_API_KEY
202056
- ),
202057
- };
202058
- }
202059
- selectList(params);
202060
- };
202061
-
202062
- const handleClassicSearch = () => {
202063
- if (!gridRef.current) return;
202064
- gridRef.current.classList.add("loading");
202065
-
202066
- const form2Element = ninegrid.querySelector(".form2", tabRef.current);
202067
- const params = form2Element ? form2Element.getData() : {};
202068
- selectList(params);
202069
- };
202070
-
202071
- useEffect(() => {
202072
- selectList({});
202073
-
202074
- const searchTextElement = ninegrid.querySelector("#searchText", tabRef.current);
202075
- const searchButton = ninegrid.querySelector(".search", tabRef.current);
202076
-
202077
- const handleKeyDown = (e) => {
202078
- if (e.key === "Enter" && !e.isComposing) {
202079
- handleNaturalLanguageSearch();
202080
- }
202081
- };
202082
-
202083
- const handleClick = () => {
202084
- handleClassicSearch();
202085
- };
202086
-
202087
- if (searchTextElement) {
202088
- searchTextElement.addEventListener("keydown", handleKeyDown);
202089
- }
202090
- if (searchButton) {
202091
- searchButton.addEventListener("click", handleClick);
202092
- }
202093
-
202094
- return () => {
202095
- if (searchTextElement) {
202096
- searchTextElement.removeEventListener("keydown", handleKeyDown);
202097
- }
202098
- if (searchButton) {
202099
- searchButton.removeEventListener("click", handleClick);
202100
- }
202101
- };
202102
- }, []);
202103
-
202104
- return (
202105
- <div className="wrapper">
202106
- <nx-collapse target="nx-tab" className="search-collapse"></nx-collapse>
202107
- <div className="list-wrapper">
202108
- <nx-tab theme="theme-3" ref={tabRef}>
202109
- <nx-tab-page caption="자연어 검색">
202110
- <nx-form className="form1">
202111
- <input
202112
- type="text"
202113
- id="searchText"
202114
- name="searchText"
202115
- placeholder="자연어 검색어를 입력하세요 (ex: 작성자가 홍길동인 데이타를 찾아줘)"
202116
- />
202117
- </nx-form>
202118
- </nx-tab-page>
202119
- <nx-tab-page caption="클래식 검색">
202120
- <nx-form className="form2">
202121
- <label>문서명: <input type="text" name="docNm" /></label>
202122
- <label>매출액:
202123
- <input type="number" name="minAmt" placeholder="최소" /> ~
202124
- <input type="number" name="maxAmt" placeholder="최대" />
202125
- </label>
202126
- </nx-form>
202127
- <button className="search">검색</button>
202128
- </nx-tab-page>
202129
- </nx-tab>
202130
-
202131
- <div className="grid-wrapper">
202132
- <nine-grid
202133
- ref={gridRef}
202134
- caption="매출 문서 관리"
202135
- select-type="row"
202136
- show-title-bar="true"
202137
- show-menu-icon="true"
202138
- show-status-bar="true"
202139
- enable-fixed-col="true"
202140
- row-resizable="false"
202141
- col-movable="true"
202142
- >
202143
- <table>
202144
- <colgroup>
202145
- <col width="50" fixed="left" background-color="gray" />
202146
- <col width="100" />
202147
- <col width="120" />
202148
- <col width="100" />
202149
- <col width="200" />
202150
- <col width="100" />
202151
- <col width="150" />
202152
- <col width="150" />
202153
- </colgroup>
202154
- <thead>
202155
- <tr>
202156
- <th>No.</th>
202157
- <th>문서ID</th>
202158
- <th>매출액</th>
202159
- <th>최종수정자</th>
202160
- <th>문서명</th>
202161
- <th>최초등록자</th>
202162
- <th>최초등록일</th>
202163
- <th>최종수정일</th>
202164
- </tr>
202165
- </thead>
202166
- <tbody>
202167
- <tr>
202168
- <th><ng-row-indicator /></th>
202169
- <td data-bind="docId" text-align="center"></td>
202170
- <td
202171
- data-bind="amt"
202172
- data-expr="data.amt.toLocaleString()"
202173
- text-align="right"
202174
- show-icon="true"
202175
- icon-type="sphere"
202176
- icon-color="data.amt >= 2000 ? 'red' : 'gray'"
202177
- ></td>
202178
- <td data-bind="updateUser" text-align="center"></td>
202179
- <td data-bind="docNm" text-align="left"></td>
202180
- <td data-bind="insertUser" text-align="center"></td>
202181
- <td data-bind="insertDt" text-align="center"></td>
202182
- <td data-bind="updateDt" text-align="center"></td>
202183
- </tr>
202184
- </tbody>
202185
- </table>
202186
- </nine-grid>
202187
- </div>
202188
- </div>
202189
- </div>
202190
- );
202191
- };
202192
-
202193
- export default DocManager1;
202194
- `;
202195
202420
  /**
202196
202421
  src1 = `
202197
202422
  <tr>
@@ -202218,9 +202443,48 @@ export default DocManager1;
202218
202443
  </tr>
202219
202444
  `; */
202220
202445
 
202221
- this.shadowRoot.querySelector("ide-diff-popup").popup(src1, src2);
202446
+ //this.shadowRoot.querySelector("ide-diff-popup").popup(src1, src2);
202447
+
202448
+ //return;
202449
+
202450
+
202451
+ // 1. 초기 진행 상태 메시지 추가
202452
+ const initialProgressData = [
202453
+ { id: 'analysis', message: '1. 분석중입니다...', completedMessage: '1-1. 분석이 완료되었습니다.' },
202454
+ { id: 'design', message: '2. 설계중...', completedMessage: '2-1. 설계가 완료되었습니다.' },
202455
+ { id: 'development', message: '3. 개발중...', completedMessage: '3-1. 개발이 완료되었습니다.' }
202456
+ ];
202457
+
202458
+ // aiChat.add 호출 시, progressData를 마지막 인자로 전달하고 생성된 엘리먼트를 받습니다.
202459
+ const progressMessageInstance = elAiChat.addProgress(initialProgressData);
202460
+
202461
+ // 2. 시간이 지난 후, 특정 단계의 완료를 알림
202462
+ // 예를 들어, 3초 후에 분석 완료
202463
+ setTimeout(() => {
202464
+ if (progressMessageInstance) {
202465
+ progressMessageInstance.updateProgress('analysis', 'completed');
202466
+ console.log("분석 완료 메시지 업데이트");
202467
+ }
202468
+ }, 3000);
202469
+
202470
+ // 예를 들어, 6초 후에 설계 완료
202471
+ setTimeout(() => {
202472
+ if (progressMessageInstance) {
202473
+ progressMessageInstance.updateProgress('design', 'completed');
202474
+ console.log("설계 완료 메시지 업데이트");
202475
+ }
202476
+ }, 6000);
202477
+
202478
+ // 모든 단계가 완료된 후, 일반 AI 메시지 추가 (선택 사항)
202479
+ setTimeout(() => {
202480
+ elAiChat.add("ai", "모든 작업이 성공적으로 완료되었습니다!", [], []);
202481
+ }, 9000);
202482
+
202483
+
202222
202484
 
202223
202485
  return;
202486
+
202487
+ const elAiChat = this.shadowRoot.querySelector("nx-ai-chat");
202224
202488
  }
202225
202489
 
202226
202490
  #toggleCollapseHandler = () => {
@@ -234916,11 +235180,11 @@ class MergeButtonWidget extends WidgetType {
234916
235180
  if (this.isAsisButton) {
234917
235181
  // ASIS (왼쪽) 에디터에 붙는 버튼: AI가 삭제한 것을 '삭제' (ASIS에서 제거)
234918
235182
  button.className = "cm-merge-button revert"; // 'revert' 클래스 사용 (빨간색)
234919
- button.textContent = "삭제"; // 텍스트 변경
235183
+ //button.textContent = "삭제"; // 텍스트 변경
234920
235184
  } else {
234921
235185
  // TOBE (오른쪽) 에디터에 붙는 버튼: AI가 추가한 것을 '적용' (ASIS에 반영)
234922
235186
  button.className = "cm-merge-button accept"; // 'accept' 클래스 사용 (녹색)
234923
- button.textContent = "← 적용"; // 화살표 방향 변경
235187
+ //button.textContent = "← 적용"; // 화살표 방향 변경
234924
235188
  }
234925
235189
 
234926
235190
  // 클릭 이벤트 핸들러