Panel

Panel 소개

Panel 컴포넌트는 두 개 이상의 컴포넌트를 그룹으로 묶어서 원하는 형식으로 배치할 수 있는 컴포넌트입니다.

여러 개 컴포넌트를 컴포넌트 내에 표시한다는 점에서 Div 컴포넌트와 비슷하지만 Panel 컴포넌트는 컨테이너 컴포넌트가 아닙니다. 컨테이너 컴포넌트는 하위 컴포넌트를 가지지만 Panel 컴포넌트는 동등한 계층의 컴포넌트를 논리적으로 묶어주는 역할만 합니다.

Div 컴포넌트의 코드를 보면 Div 컴포넌트 Layout 하위에 Button 컴포넌트가 배치되는 것을 볼 수 있습니다. Button00 컴포넌트에 접근하려면 바로 접근할 수 없고 Div 컴포넌트를 거쳐서 접근해야 합니다.

<Div id="Div00">
	<Layouts>
		<Layout>
			<Button id="Button00" left="10" top="10" width="120" height="50"/>
			<Button id="Button01" left="150" top="10" width="120" height="50"/>
		</Layout>
	</Layouts>
</Div>

하지만 Panel 컴포넌트와 연결된 컴포넌트는 Form 오브젝트 Layout 하위에 Panel 컴포넌트와 같은 계층으로 배치되어 있습니다. Panel 컴포넌트에서는 PanelItem으로 Button 컴포넌트를 연결하고 있을 뿐입니다.

<Form>
	<Layouts>
		<Layout>
			<Panel>
				<Contents>
					<PanelItem id="PanelItem00" componentid="Button00"/>
					<PanelItem id="PanelItem02" componentid="Button01"/>
				</Contents>
			</Panel>
			<Button id="Button00"/>
			<Button id="Button01"/>
		</Layout>
	</Layouts>
</Form>

Div 컴포넌트에 배치된 Button 컴포넌트와 Panel 컴포넌트에 연결된 Button 컴포넌트에 접근할 때는 아래와 같이 접근합니다.

this.Div00;
this.Div00.form.Button00;

this.Panel00;
this.Button00;

Outlineview 창에서는 Div 컴포넌트처럼 계층으로 구분된 것처럼 표시됩니다. 작업 편의를 위해 구분해 표시할 뿐 실제 계층 구조는 Div 컴포넌트와 Panel 컴포넌트가 다릅니다.

Panel 컴포넌트에 아이템 추가하기

Panel 컴포넌트에 아이템을 추가하는 방법은 3가지입니다.

Group by Panel

화면에 컴포넌트를 배치하고 그룹으로 묶을 컴포넌트를 선택한 다음에 컨텍스트 메뉴에서 [Group by Panel] 항목을 선택합니다.

그룹을 해제할 때는 Panel 컴포넌트를 선택한 후 컨텍스트 메뉴에서 [Ungroup Panels] 항목을 선택합니다.

컴포넌트 드래그해서 배치하기

Div 컴포넌트 내에 컴포넌트를 배치하는 것과 비슷합니다. 다만 Panel 컴포넌트는 좌표를 지정할 수 없고 수평, 수직, 테이블 형식 중 하나만 선택할 수 있습니다.

Panel 컴포넌트의 items 속성 설정하기

Panel 컴포넌트를 선택하고 속성창에서 items 속성을 설정할 수 있습니다.

컴포넌트 그룹으로 묶기

Panel 컴포넌트를 사용해 2개 이상의 컴포넌트를 그룹으로 묶고 하나의 컴포넌트처럼 움직이게 할 수 있습니다.

예제

상위에 있는 Div 컴포넌트 크기 변경 시 Panel 컴포넌트로 묶은 컴포넌트는 하나의 컴포넌트처럼 처리됩니다.

sample_panel_01.xfdl

예제에서 사용한 핵심 기능

flexwrap

컴포넌트 배치 시 Layout 컨테이너 영역을 넘어가면 컴포넌트 단위로 줄 바꿈을 처리합니다. 예제에서는 개별 컴포넌트로 있을 때와 Panel 컴포넌트로 묶었을 때 차이를 구분해 봅니다.

예제 구현 방법

1

화면에 Div 컴포넌트를 배치합니다.

2

Div 컴포넌트의 속성값을 아래와 같이 변경합니다.

type: "horizontal"

flexwrap: "wrap"

verticalgap: 5

3

Div 컴포넌트 내에 Static 컴포넌트와 Edit 컴포넌트를 아래 그림처럼 배치합니다.

Static 컴포넌트의 width는 100, Edit 컴포넌트의 width는 120으로 차이가 나게 지정해줍니다.

4

Div 컴포넌트 옆에 Button 컴포넌트를 배치합니다.

5

Button 컴포넌트의 onclick 이벤트 핸들러 함수를 아래와 같이 추가합니다.

Button 컴포넌트를 클릭하면 Div 컴포넌트의 크기를 반으로 줄이고 fittocontents 속성을 사용해 높이를 컨텐츠에 맞게 조정합니다.

this.Button00_onclick = function(obj:nexacro.Button,e:nexacro.ClickEventInfo)
{
	this.Div00.width = this.Div00.width * 0.5;
	this.Div00.fittocontents = "height";
	this.resetScroll();
};

6

QuickView(Ctrl + F6)를 실행하여 Button 컴포넌트를 클릭해 컴포넌트 배치가 어떻게 바뀌는지 확인합니다.

Div 컴포넌트의 크기가 줄면서 Static 컴포넌트와 Edit 컴포넌트를 한 줄에 표시하지 못하고 각각 줄 바꿈이 되어 배치됩니다.

7

다시 넥사크로 스튜디오로 돌아와서 Static 컴포넌트와 Edit 컴포넌트를 선택하고 컨텍스트 메뉴에서 [Group by Panel] 항목을 선택합니다.

Outlineview에서 보면 아래와 같은 형태가 됩니다. Static00, Edit00을 Panel00으로 묶어주었고 Static01, Edit01을 Panel01로 묶어주었습니다.

8

QuickView(Ctrl + F6)를 실행하여 Button 컴포넌트를 클릭해 컴포넌트 배치가 어떻게 바뀌는지 확인합니다.

줄 바꿈 처리가 Static 컴포넌트나 Edit 컴포넌트가 아니라 Panel 컴포넌트 단위로 처리되는 것을 확인할 수 있습니다.

메가 메뉴 만들기

메가 메뉴(Mega Drop-down Menu)는 최상위 메뉴에서 선택한 항목에 대한 하위 항목(또는 모든 하위 항목)을 사이트맵처럼 표시하는 방식입니다. 예제에서는 화면 로딩 시 동적으로 최상위 메뉴 항목과 하위 항목을 배치하고 표시하는 방법을 살펴봅니다. 또한 권한에 따라 메뉴 항목을 재설정하는 방법도 살펴봅니다.

예제는 넥사크로 데모 사이트 예제를 일부 참고해 작성했습니다.

https://demo.tobesoft.com/?menu_id=2008

예제

최상위 메뉴 선택 시 하위 항목 메뉴가 펼쳐집니다. "Admin User" 항목 체크 상태에 따라 "Admin" 메뉴를 제외하거나 포함합니다.

sample_panel_02.xfdl

예제에서 사용한 핵심 기능

setFluidLayoutProperty

Fluid Layout 관련 속성을 동적으로 설정하는 메서드입니다. 현재 버전 기준으로는 tabletemplate 속성만 설정할 수 있습니다.

예제 구현 방법

1

화면에 Div, Static 컴포넌트를 배치합니다.

Div 컴포넌트는 메뉴 항목을 표시할 영역이고 Static 컴포넌트는 선택한 메뉴에 대한 정보를 표시하는 영역입니다. 일반적으로는 메뉴 선택 시 해당하는 화면으로 이동하는데 예제에서는 간단하게 정보만 표시합니다.

2

Div 컴포넌트의 type 속성값을 "table"로 변경하고 background 속성값을 "lightgray"로 설정합니다.

3

하위 메뉴를 표시할 Div 컴포넌트(divSubMenu)의 visible 속성값을 false로 설정합니다.

4

Dataset 오브젝트를 추가하고 데이터를 아래와 같이 설정합니다.

MENU_LVL 칼럼값이 1인 항목이 최상위 메뉴에 표시되고 2인 항목이 상세 메뉴에 표시됩니다. 전체 데이터는 샘플 코드를 참고해 주세요.

5

Form에 onload 이벤트 핸들러 함수를 추가하고 아래와 같이 코드를 작성합니다.

initializeMegaMenu 함수에서 Dataset 오브젝트의 데이터를 확인하고 메뉴 항목을 배치합니다. 아래와 같은 동작을 처리합니다.

const LEFT = 0;
const TOP = 0;
const FULL_WIDTH = "100%"
const FULL_HEIGHT = "100%"
const MENU_HEIGHT = 30;	

this.sample_panel_02_onload = function(obj:nexacro.Form,e:nexacro.LoadEventInfo)
{
	this.initializeMegaMenu();
};

this.initializeMegaMenu = function (adminFilter="")
{
	// 기존에 생성된 메뉴 항목을 초기화합니다.
	this.fnRemoveComponent(this.divUpMenu);
	this.fnRemoveComponent(this.divSubMenu);
	
	// Dataset 오브젝트에서 최상위 메뉴에 표시할 항목을 필터링합니다.
	this.dsMegaMenu.filter("MENU_LVL == 1"+adminFilter);
	const MENU_COUNT = this.dsMegaMenu.rowcount;
	
	const TABLETEMPLATE_VALUE = this.generateMenuString(MENU_COUNT);
	// 표시할 메뉴 항목 수에 맞게 Div 컴포넌트의 tabletemplate 속성값을 설정합니다.
	this.divUpMenu.form.setFluidLayoutProperty("default", "tabletemplate", TABLETEMPLATE_VALUE);
	this.divSubMenu.form.setFluidLayoutProperty("default", "tabletemplate", TABLETEMPLATE_VALUE);
	
	let subMenuMaxCount = 0;
	
	for (let i=0;i<MENU_COUNT;i++) {
		let menuCd = this.dsMegaMenu.getColumn(i, "MENU_CD");
		let menuNm = this.dsMegaMenu.getColumn(i, "MENU_NM");
		
		// 최상위 메뉴에 표시할 Static 컴포넌트를 생성합니다.
		this.createStatic("sta_"+menuCd, FULL_HEIGHT, menuNm, i, "center", this.divUpMenu);
		
		// 상세 메뉴 영역을 처리할 Panel 컴포넌트를 생성합니다.
		// Div 컴포넌트는 동적으로 type 속성값을 설정할 수 없어 Panel 컴포넌트를 사용합니다.
		let subMenuPanel = this.createPanel("panel_"+menuCd, FULL_HEIGHT, i, this.divSubMenu);
		
		// 상세 메뉴에 표시할 Static 컴포넌트를 생성합니다.
		let subMenuTitleStatic = this.createStatic("sta_sub_"+menuCd, MENU_HEIGHT, menuNm, i, "center", this.divSubMenu);
		subMenuTitleStatic.color = "red";
		subMenuPanel.addItem(subMenuTitleStatic.name, subMenuTitleStatic.id);		
		
		this.dsMegaMenu.filter("UP_MENU_CD == '" + menuCd + "'");
		
		let nSubCount = this.dsMegaMenu.rowcount;
		if(nSubCount > subMenuMaxCount) subMenuMaxCount = nSubCount;
		
		for (let j=0;j<nSubCount;j++) {
			let subMenuCd = this.dsMegaMenu.getColumn(j, "MENU_CD");
			let subMenuNm = this.dsMegaMenu.getColumn(j, "MENU_NM");
			
			let objSubMenuSta = this.createStatic("subSta_"+subMenuCd, MENU_HEIGHT, subMenuNm, i, "left", this.divSubMenu);
			subMenuPanel.addItem(objSubMenuSta.name, objSubMenuSta.id);
		}
		this.dsMegaMenu.filter("MENU_LVL == 1"+adminFilter);
	}
	this.divSubMenu.height = (subMenuMaxCount+1)*MENU_HEIGHT;
};

// 메뉴 항목을 Static 컴포넌트로 생성하고 배치할 Div 컴포넌트의 Child로 추가합니다.
this.createStatic = function (id, height, text, tablecellindex, textAlign, targetDiv) {
    let objStatic = new Static();
    objStatic.init(id, LEFT, TOP, FULL_WIDTH, height);
    objStatic.text = text;
    objStatic.tablecellarea = "0 / "+tablecellindex;
    objStatic.textAlign = textAlign;
	objStatic.addEventHandler("onclick", this.fnMenuOnClick, this);
	targetDiv.addChild(objStatic.name, objStatic);
	objStatic.show();	
    return objStatic;
};

// 상세 메뉴 항목을 배치할 Panel 컴포넌트를 생성하고 Div 컴포넌트의 Child로 추가합니다.
this.createPanel = function (id, height, tablecellindex, targetDiv) {
    let objPanel = new Panel();
    objPanel.init(id, LEFT, TOP, FULL_WIDTH, height);
    objPanel.tablecellarea = "0 / "+tablecellindex;;
	objPanel.type = "vertical";
	targetDiv.addChild(objPanel.name, objPanel);
	objPanel.show();	
    return objPanel;
};

// 메뉴 개수에 따라 tabletemplate 속성값으로 설정할 문자열을 생성합니다.
this.generateMenuString = function(menuCount) {
    const repeatedString = "1* ".repeat(menuCount).trim();
    return "1* / "+repeatedString;
}

실행 화면에 라인을 추가해서 확인해 보면 아래와 같이 최상위 메뉴, 상세 메뉴에 5개의 칼럼이 만들어져 있고, 상세 메뉴의 Panel 컴포넌트는 세로 방향으로 컴포넌트를 추가할 수 있도록 설정되어 있습니다.

6

메뉴 클릭 시 이벤트를 처리할 이벤트 핸들러 함수를 추가합니다.

this.toggleSubMenuVisibility = function () {
    this.divSubMenu.visible = !this.divSubMenu.visible;
};

this.fnMenuOnClick = function(obj:nexacro.Static,e:nexacro.ClickEventInfo)
{
	this.toggleSubMenuVisibility();
	this.staMenuClick.text = obj.text;
}

7

화면에 CheckBox 컴포넌트를 추가하고 onchanged 이벤트 핸들러 함수를 추가합니다.

this.fnRemoveComponent = function(targetDiv) {
    if (targetDiv.form.components.length > 0) {
        for (let i = targetDiv.form.components.length - 1; i >= 0; i--) {
            let component = targetDiv.form.components[i];
            targetDiv.removeChild(component.name);
            component.destroy();
        }
    }
};

this.ckAdminUser_onchanged = function(obj:nexacro.CheckBox,e:nexacro.CheckBoxChangedEventInfo)
{
	if(e.postvalue)
		this.initializeMegaMenu();
	else
		this.initializeMegaMenu(" && ADMIN_MENU != 1");
};

8

QuickView(Ctrl + F6)를 실행하여 메뉴 아이템이 표시되는 것을 확인합니다.