#GWC UI Library : Memo

웹 UI 라이브러리인 GWC에서 제공하는 Memo 컴포넌트에 대한 예제 코드입니다.

먼저 DOM 구성은 다음과 같습니다. gwc-resizable-panel 태그로 감싸서 크기 조정이 가능하도록 했습니다. 이는 옵션입니다.

그리고 CSS 구성은 다음과 같구요.


.center {
    display: flex;
    width: 100%;
    height: 100%;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    gap: 0.5em;
}

.vcenter {
    display: flex;
    align-items: center;
    gap: 0.3em;
}

.hcenter {
    flex-direction: column;
    display: flex;
    _align-items: center;
}

gwc-memo {
    width: 100%;
    height: 100%;
}

js 코드는 다음과 같습니다.

window.onload = () => {
    button1.onclick = () => {
        memo.value = "메모에 대한 내용은 코드로 변경할 수 있어요.\n줄 바꿈도 가능하답니다.";
    }

    button2.onclick = () => {
        gwcMessage(memo.value);
    }

    button3.onclick = () => {
        console.log(memo.disabled);
        memo.disabled = !memo.disabled;
    }

    button4.onclick = () => {
        console.log(memo.readonly);
        memo.readonly = !memo.readonly;
    }

    memo.addEventListener("change", (event) => {
        labelEvent.content = `${memo.value.length}자가 입력됨 (입력가능 문자수 ${memo.maxLength})`;
    })

    GeoServiceWebComponentManager.instance.update();
   
};

실행 결과는 다음과 같습니다.

gwc-memo 태그 선언을 통해 value 속성에 문자열을 지정할 때 문자열에 쌍따옴표가 있을 경우 변환이 필요합니다.

<gwc-memo value="${params.content.replaceAll("\"", "&quot;")}"></gwc-memo>

#GWC UI Library : PopupMenu

웹 UI 라이브러리인 GWC에서 제공하는 PopupMenu 컴포넌트에 대한 예제 코드입니다.

큰 의미는 없으나 DOM 구성은 다음과 같습니다.

그리고 CSS 구성은 다음과 같구요.

.center {
    display: flex;
    width: 100%;
    height: 100%;
    justify-content: center;
    align-items: center;
    gap: 1em;
}

js 코드는 다음과 같습니다.

window.onload = () => {
    const popupMenu = gwcCreatePopupMenu();

    popupMenu.addMenuItem("menu1", {
        text: "녹음 시작",
        icon: "images/icon1.png",
        onClick: (menuId) => { 
            gwcMessage(`녹음 시작 클릭(${menuId})`);
            popupMenu.hide();                
        }
    });

    popupMenu.addMenuItem("menu2", {
        text: "WiFi 활성화",
        icon: "images/icon2.png",
        checked: false,
        onClick: (menuId) => { 
            const bChecked = popupMenu.getMenuChecked(menuId);
            popupMenu.setMenuChecked(menuId, !bChecked);

        }
    });

    popupMenu.addMenuItem("menu3", {
        text: "꿈꾸기",
        icon: "images/icon3.png",
        onClick: (menuId) => { 
            gwcMessage(`상세화하기(${menuId})`);
            popupMenu.hide();
        }
    });

    popupMenu.addMenuItem("menu4", {
        text: "구체화하기",
        icon: "images/icon4.png",
        onClick: (menuId) => { 
            gwcMessage(`구체화하기(${menuId})`);
            popupMenu.hide();
        }
    });

    popupMenu.addMenuItem("menu5", {
        text: "실현하기",
        icon: "images/icon5.png",
        onClick: (menuId) => { 
            gwcMessage(`실현하기(${menuId})`);
            popupMenu.hide();
        }
    });

    const popupMenuZone = document.querySelector(".center");
    popupMenuZone.addEventListener("click", (event) => {
        if(popupMenu.isShown()) {
            popupMenu.hide();
            return;
        }

        if(event.target === popupMenuZone) {
            // 팝업창의 표시를 위해 
            // event.currentTarget.getBoundingClientRect()의 결과값에 대한 left, top을 사용하는 것이 좋음
            popupMenu.show(event.offsetX, event.offsetY);
        }
    });
};

실행 결과는 다음과 같습니다.

PopupMenu를 표시하기 전에 어떤 메뉴 항목을 감추거나 다시 표시해야 할 필요가 있습니다. 이에 대한 이벤트는 showing인데요. 예제 코드는 다음과 같습니다.

popupMenu.addEventListener("showing", (event) => {
    popupMenu.setMenuVisible("menu1", true);
    popupMenu.setMenuVisible("menu2", false);

    // event.cancel = true; -> 이 코드가 작동하면 팝업 메뉴가 표시되지 않음
});

gwcCreatePopupMenu를 응용해서 일반적인 메뉴를 구성할 수 있는데, 이때 서브 메뉴에 대한 구성도 가능합니다. 아래는 코드 예시과 그 결과 이미지입니다.

const menu = gwcCreatePopupMenu();
const shpCsvMenu = gwcCreatePopupMenu();

shpCsvMenu.addMenuItem("menu-shp2csv", {
    text: "SHP 데이터를 CSV로 변환",
    icon: "images/change.svg",
    onClick: (menuId) => {}
});

shpCsvMenu.addMenuItem("menu-xycsv2shp", {
    text: "경위도 좌표 데이터(CSV)를 SHP으로 변환",
    icon: "images/change.svg",
    onClick: (menuId) => {}
});

menu.addSubMenu("menu-shpcsv", shpCsvMenu, { text: "SHP/CSV 변환" })

gwcCreateModalDialog의 resizing 코드 예 (gwc-card에도 적용됨)

하나의 모달 대화상자를 gwcCreateModalDialog 함수를 이용해 만들때 class 단위로 만들면 전체적인 시스템의 UI 기능이 효과적으로 분리됩니다. 먼저 모달 대화상자에 대한 코드를 class로 만듭니다.

class ArchiveManagerDialog {
    constructor() {
        const dlg = gwcCreateModalDialog("아카이브 관리자");
        dlg.content = `
            
`; dlg.width = "50em"; dlg.resizablePanel.resizableLeft = true; dlg.resizablePanel.resizableRight = true; dlg.resizablePanel.resizableTop = true; dlg.resizablePanel.resizableBottom = true; dlg.resizablePanel.minWidth = 450; dlg.resizablePanel.minHeight = 300; dlg.resizablePanel.addEventListener("change", (event) => { if(event.target === dlg.resizablePanel) { const { mode, oldHeight, newHeight } = event.detail; if(mode === "BOTTOM" || mode == "TOP") { const domScrollView = dlg.content.querySelector("gwc-vscrollview"); const height = parseFloat(window.getComputedStyle(domScrollView).getPropertyValue("height")); domScrollView.style.height = `${height - (oldHeight - newHeight)}px`; domScrollView.refresh(); } } }); dlg.show(); GeoServiceWebComponentManager.instance.update(); } }

CSS에 대한 코드는 다음과 같습니다.

.vertical-linear-layout {
    display: flex;
    flex-direction: column;
    gap: 0.3em;
}

.horizontal-linear-layout {
    display: flex;
    gap: 0.3em;
    flex-direction: row;
    _padding: 0 1em;
}

.v-center {
    align-items: center;
}

.h-center {
    justify-content: center;
}

.v-space {
    margin-top: 0.5em;
    margin-bottom: 0.5em;
}

.archive-manager-dialog {
    padding: 0.5em 0.5em 0.5em 0.5em;
}

.archive-manager-dialog gwc-textinput {
    width: 10em;
}

.archive-manager-dialog .search-part {
    margin-left: auto;
}

.archive-manager-dialog gwc-vscrollview {
    height: 30em; /* js 코드로 크기를 조정해야 함 */
    margin: 0 0.2em;
    background: rgba(0,0,0,0.3);
    box-shadow: inset -0.6px -0.6px 0.6px rgba(255,255,255,0.4), 
        inset 0.6px 0.6px 0.6px rgba(0,0,0,0.5);
    border-radius: 0.5em;    
}

.archive-manager-dialog gwc-tree {
    width: 100%;
    padding: 0.5em 0.5em 0.5em 0.5em;
    _border: 1px solid red;    
}

실행 결과는 다음과 같습니다.

#GWC UI Library : gwcCreateProgressDialog

웹 UI 라이브러리인 GWC에서 제공하는 gwcCreateProgressDialog 함수에 대한 예제 코드입니다.

먼저 DOM 구성은 다음과 같습니다.

그리고 CSS 구성은 다음과 같구요.

.center {
    display: flex;
    width: 100%;
    height: 100%;
    justify-content: center;
    align-items: center;
}

js 코드는 다음과 같습니다.

window.onload = () => {
    button.onclick = event => { 
        const dlg = gwcCreateProgressDialog({
            // onCancel 이벤트를 지정하지 않을 경우 "취소" 버튼 클릭시 remove 매서드가 자동으로 호출됨
            onCancel: () => {
                gwcMessage("사용자가 진행을 중단했습니다.", true);
                dlg.remove(); // onCancel 이벤트 지정했을 경우 의도적으로 remove 매서드를 호촐해야 함
            }
        });

        dlg.show();

        let p = 0;
        setInterval(() => {
            dlg.percent = p;
                if(p++ === 100) dlg.remove();
        }, 50);

        //dlg.label = "텍스트 메시지";
    };
};

실행 결과는 다음과 같습니다.

cancel() 매서드를 호출하면 onCancel 이벤트가 호출됩니다.

#GWC UI Library : gwcCreateModalDialog 함수

웹 UI 라이브러리인 GWC에서 제공하는 gwcCreateModalDialog 함수에 대한 예제 코드입니다.

먼저 DOM 구성은 다음과 같습니다.

그리고 CSS 구성은 다음과 같구요.

.center {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    width: 100%;
    height: 100vh;
}

.hcenter {
    display: flex;
    align-items: center;
    flex-direction: column;
    gap: 0.5em;
}
 
.content {
    padding: .5em;
}

.vcenter {
    display: flex;
    align-items: center;
}

gwc-textinput {
    width: 20em;
}

.something {
    width: 100%;
    height: 100px;
    background: rgba(0,0,0,0.4);
    border: 1px dashed rgba(255,255,255,0.5);
    display: flex;
    align-items: center;
    justify-content: center;
    color: wheat;
    padding: 20px;
}

js 코드는 다음과 같습니다.

window.onload = () => {
    btn.addEventListener("click", () => {
        const dlg = gwcCreateModalDialog("모달 대화상자");
        dlg.content = `
            
앨든링(Elden Ring)..
그대가 왕이될지.. 아니 어쩌면 신이될지..
`; dlg.resizablePanel.style.width = "30em"; // 대화상자의 크기 지정(대화상자 자체에 대한 크기 조절로는 안됨) dlg.resizablePanel.minWidth = 400; // 대화창의 크기 조절 시 가로 최소 크기 dlg.resizablePanel.minHeight = 200; // 대화창의 크기 조절 시 세로 최소 크기 // 대화 상자의 크기 조절에 따른 구성 UI의 크기 조절 // CSS 만으로 제어할 수 없는 구성 UI는 크기 변경 이벤트를 통해 제어할 수 있음 dlg.resizablePanel.addEventListener("change", (event) => { const { mode, oldHeight, newHeight } = event.detail; if(mode === "BOTTOM" || mode == "TOP") { const domSomething = dlg.content.querySelector(".something"); const height = parseFloat(window.getComputedStyle(domSomething).getPropertyValue("height")); domSomething.style.height = `${height - (oldHeight - newHeight)}px`; } }); dlg.content.querySelector("gwc-button").addEventListener("click", () => { dlg.remove(); }); dlg.show(); GeoServiceWebComponentManager.instance.update(); }); };

실행 결과는 다음과 같습니다.

대화창을 구성하는 UI의 상태(값)이 변경되면 변경에 대한 처리를 한곳에서 처리할 수 있는 state 관리가 가능합니다. 코드의 예는 다음과 같습니다.

dlg.setState((ui) => {
    console.log("변경통지", typeof ui);
});

setState로 상태 변경에 대한 이벤트를 등록할 수 있으며 변경 이벤트는 다음처럼 직접적으로 호출할 수도 있습니다.

this.#dialog.state.change(this.#selectedFile);

대화창이 닫힐때 수동이든 자동이든 remove 매서드가 호출되는데요. 이때 close 이벤트가 발생합니다. 이 이벤트를 통해 창이 닫힐때 무언가를 처리할 수 있고, 창을 정말로 닫을지 제어할 수 있습니다.

this.#dialog.addEventListener("close", (event) => {
    // cancel을 true로 설정하면 대화창이 닫히지 않습니다.
    event.cancel = true;
});