스키마 정의
탭 노드 타입의 스키마 정의:
{
tabs: {
content: 'block+',
group: 'block',
attrs: {
activeTab: { default: 0 }
},
}
}모델 표현
모델 표현 예제:
{
type: 'tabs',
attrs: { activeTab: 0 },
children: [
{
type: 'paragraph',
children: [{ type: 'text', text: '탭 1 콘텐츠' }]
},
{
type: 'paragraph',
children: [{ type: 'text', text: '탭 2 콘텐츠' }]
}
]
}HTML 직렬화
모델을 HTML로 변환:
function serializeTabs(node) {
return '<div data-type="tabs" data-active="' + node.attrs.activeTab + '">' +
serializeChildren(node.children) + '</div>';
}HTML 역직렬화
HTML을 모델로 파싱:
function parseTabs(domNode) {
return {
type: 'tabs',
attrs: { activeTab: parseInt(domNode.getAttribute('data-active')) || 0 },
children: Array.from(domNode.childNodes)
.map(node => parseNode(node))
.filter(Boolean)
};
}뷰 연동
뷰 연동 노트: 이 노드 타입을 뷰 레이어에서 구현할 때 contenteditable 동작, 선택 처리, 이벤트 관리에 특히 주의하세요.
뷰 연동 코드:
// 탭 렌더링
const tabs = document.createElement('div');
tabs.setAttribute('data-type', 'tabs');
tabs.setAttribute('data-active', node.attrs.activeTab);
tabs.contentEditable = 'true';
node.children.forEach((child, index) => {
const tab = renderNode(child);
tab.setAttribute('data-tab-index', index);
tabs.appendChild(tab);
});
// 탭 전환
function switchTab(tabs, index) {
tabs.setAttribute('data-active', index);
updateTabVisibility(tabs, index);
}일반적인 문제
일반적인 함정: 이 노드 타입을 구현할 때 자주 발생하는 문제들입니다. 구현 전에 주의 깊게 검토하세요.
일반적인 문제 및 해결 방법:
// 문제: 탭 상태 관리
// 해결: 모델과 뷰 간 활성 탭 동기화
function syncTabState(tabs, model) {
tabs.setAttribute('data-active', model.attrs.activeTab);
}
// 문제: 탭 가시성
// 해결: 활성 상태에 따라 탭 표시/숨김
function updateTabVisibility(tabs, activeIndex) {
tabs.querySelectorAll('[data-tab-index]').forEach((tab, index) => {
tab.style.display = index === activeIndex ? 'block' : 'none';
});
}구현
완전한 구현 예제:
class TabsNode {
constructor(attrs, children) {
this.type = 'tabs';
this.attrs = { activeTab: attrs?.activeTab || 0 };
this.children = children || [];
}
toDOM() {
const tabs = document.createElement('div');
tabs.setAttribute('data-type', 'tabs');
tabs.setAttribute('data-active', this.attrs.activeTab);
this.children.forEach((child, index) => {
const tab = child.toDOM();
tab.setAttribute('data-tab-index', index);
tabs.appendChild(tab);
});
return tabs;
}
static fromDOM(domNode) {
return new TabsNode(
{ activeTab: parseInt(domNode.getAttribute('data-active')) || 0 },
Array.from(domNode.childNodes)
.map(node => parseNode(node))
.filter(Boolean)
);
}
}