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