MasterDetailLayout
먼저 보고, 그다음 계약을 읽습니다
반응형 2컬럼 master/detail 패턴(모바일에서 스택 + back).
Component intent의도와 경계 읽기
좌측 목록(list/tree)과 우측 상세(detail)를 데스크톱에서는 가로로, 모바일(< breakpoint)에서는 한 컬럼씩 스택으로 보여준다. Cortex MemoryBrowser 의 list+detail 모바일 토글 패턴을 SSOT 로 흡수했다. 모바일 detail 화면 상단에 back 버튼이 자동으로 붙고, master↔detail 전이 시 포커스를 기억·복원한다. mobileShowDetail + onMobileBack 을 주면 controlled, 안 주면 detail !== null 여부로 자동 전환하는 uncontrolled 로 동작한다.
예제
SSOT에 등록된 실제 API 기준 snippet입니다. standalone 배지만 독립 실행 단위이며, fragment는 주변 state·handler 문맥을 생략합니다.
import { MasterDetailLayout } from "@axe/ui";import { useState } from "react";function Browser() { const [id, setId] = useState<string | null>(null); return ( <MasterDetailLayout master={<List onSelect={setId} />} detail={id ? <Detail id={id} /> : null} masterWidth="320px" /> );}import { MasterDetailLayout } from "@axe/ui";<MasterDetailLayout master={<List onSelect={open} />} detail={selected ? <Detail item={selected} /> : null} breakpoint={768} mobileBackLabel="목록으로" mobileShowDetail={showDetail} onMobileBack={() => setShowDetail(false)}/>Props
TSX 소스가 진실입니다. 주요 export 컴포넌트의 public API만 노출합니다.
| 이름 | 타입 | 필수 | 기본값 | 설명 |
|---|---|---|---|---|
master | React.ReactNode | 필수 | — | 좌측 컬럼 내용(list/tree). |
detail | React.ReactNode | null | 필수 | — | 우측 detail 내용. null 이면 emptyState(또는 기본 placeholder) 표시. |
masterWidth | number | string | — | "320px" | 데스크톱 master 컬럼 폭(--axe-md-master-width). number=px. |
breakpoint | number | — | 768 | 모바일 전환 임계 px(< breakpoint = 스택 모드). |
mobileBackLabel | string | — | "목록으로" | 모바일 detail 상단 back 버튼 텍스트. |
mobileShowDetail | boolean | — | — | controlled — true 면 모바일에서 detail 화면. onMobileBack 와 함께 사용. |
onMobileBack | () => void | — | — | controlled back 핸들러. mobileShowDetail 와 함께 주면 controlled 모드로 전환. |
emptyState | React.ReactNode | — | — | detail === null 일 때 표시할 빈 자리 노드(미지정 시 기본 placeholder). |
...rest | Omit<React.HTMLAttributes<HTMLDivElement>, "children"> | — | — | className 은 병합, style 은 내부 CSS 변수와 병합, 나머지는 루트 div 로 전파. |
.axe-* 클래스 계약
React 밖에서도 같은 표면을 그리는 공개 계약입니다. stable은 minor 버전 안에서 이름을 바꾸지 않습니다.
| 클래스 | 안정성 | 용도 |
|---|---|---|
.axe-master-detail | stable | 루트 flex row(데스크톱 2컬럼, gap --space-4). |
.axe-master-detail--mobile | stable | JS 측정 후 부여 — flex column(스택), gap 0. |
.axe-master-detail__master | stable | 좌측 <aside> — 폭 --axe-md-master-width(기본 320px), overflow auto. |
.axe-master-detail__master--hidden | stable | 모바일 detail 화면에서 master 숨김(display:none). |
.axe-master-detail__detail | stable | 우측 <section> — flex:1 로 남는 공간 차지, overflow auto. |
.axe-master-detail__detail--hidden | stable | 모바일 master 화면에서 detail 숨김(display:none). |
.axe-master-detail__back | stable | 모바일 detail 상단 back 버튼(hover/focus 트림). |
.axe-master-detail__back-arrow | stable | back 버튼 ← 화살표 span(aria-hidden). |
.axe-master-detail__empty | stable | detail 미지정 + emptyState 없을 때의 기본 placeholder("항목을 선택하세요"). |
접근성
키보드, ARIA, 구현 노트를 함께 검토합니다.
- back 버튼은 네이티브 <button> — Enter/Space 로 목록 복귀
master=<aside>, detail=<section>. 모바일에서 숨는 컬럼에 aria-hidden 을 부여해 AX 트리에서 제외한다. back 버튼 aria-label = "<mobileBackLabel> 돌아가기", 화살표 span 은 aria-hidden.
모바일 전이 시 포커스 관리: master→detail 진입 때 직전 master 내 포커스를 기억하고 back 버튼으로 이동(rAF 다음 프레임), detail→master 복귀 때 기억한 element 로 복원. 이 포커스 관리는 모바일(isMobile)에서만 동작한다. 뷰포트 감지는 matchMedia 기반 SSR-safe — 초기엔 데스크톱(false)으로 렌더 후 mount 시 보정한다.