Tree Grid
계층형 리소스를 정리하고 편집하면서 2차원 격자 이동을 유지합니다.
용법
행 펼치기
하위 응용 프로그램과 기능이 부모 리소스에 어떻게 연결되는지 프로젝트 목록에서 확인합니다.
<!-- Tree Grid / Expanded -->
<script setup lang="ts">
import { reactive } from 'vue'
import { TreeGridCell, TreeGridDisclosure, TreeGridEditor, TreeGridRoot, TreeGridRow } from '@sectile/vue/tree-grid'
const rows = [
{ id: 'platform', parentID: null, cells: ['platform-name', 'platform-owner', 'platform-status'] },
{ id: 'storefront', parentID: 'platform', cells: ['storefront-name', 'storefront-owner', 'storefront-status'] },
{ id: 'checkout', parentID: 'storefront', cells: ['checkout-name', 'checkout-owner', 'checkout-status'] },
]
const values = reactive(new Map([
['platform-name', 'Commerce platform'], ['platform-owner', 'Platform team'], ['platform-status', 'Healthy'],
['storefront-name', 'Storefront'], ['storefront-owner', 'Mina Kim'], ['storefront-status', 'Modified'],
['checkout-name', 'Checkout flow'], ['checkout-owner', 'Alex Chen'], ['checkout-status', 'In review'],
]))
const valueFor = (id: string) => values.get(id) ?? ''
const updateValue = (id: string, value: string) => values.set(id, value)
</script>
<template>
<TreeGridRoot
:rows="rows"
:get-cell-value="valueFor"
:set-cell-value="updateValue"
:default-expanded-value="['platform', 'storefront']"
aria-label="Project inventory"
v-slot="{ expandedValue }"
>
<div role="row"><span role="columnheader">Resource</span><span role="columnheader">Owner</span><span role="columnheader">Status</span></div>
<TreeGridRow value="platform" :row-index="1" :expandable="true">
<TreeGridCell value="platform-name" :column-index="1">
<TreeGridDisclosure for="platform">Toggle</TreeGridDisclosure>
{{ valueFor('platform-name') }}
</TreeGridCell>
<TreeGridCell value="platform-owner" :column-index="2" v-slot="{ editing }">
<span v-if="!editing">{{ valueFor('platform-owner') }}</span>
<TreeGridEditor for="platform-owner" label="Commerce platform owner" />
</TreeGridCell>
<TreeGridCell value="platform-status" :column-index="3">{{ valueFor('platform-status') }}</TreeGridCell>
</TreeGridRow>
<TreeGridRow v-if="expandedValue.includes('platform')" value="storefront" :row-index="2" :level="2" :expandable="true">
<TreeGridCell value="storefront-name" :column-index="1">
<TreeGridDisclosure for="storefront">Toggle</TreeGridDisclosure>
{{ valueFor('storefront-name') }}
</TreeGridCell>
<TreeGridCell value="storefront-owner" :column-index="2" v-slot="{ editing }">
<span v-if="!editing">{{ valueFor('storefront-owner') }}</span>
<TreeGridEditor for="storefront-owner" label="Storefront owner" />
</TreeGridCell>
<TreeGridCell value="storefront-status" :column-index="3">{{ valueFor('storefront-status') }}</TreeGridCell>
</TreeGridRow>
<TreeGridRow v-if="expandedValue.includes('storefront')" value="checkout" :row-index="3" :level="3">
<TreeGridCell value="checkout-name" :column-index="1">{{ valueFor('checkout-name') }}</TreeGridCell>
<TreeGridCell value="checkout-owner" :column-index="2" v-slot="{ editing }">
<span v-if="!editing">{{ valueFor('checkout-owner') }}</span>
<TreeGridEditor for="checkout-owner" label="Checkout owner" />
</TreeGridCell>
<TreeGridCell value="checkout-status" :column-index="3">{{ valueFor('checkout-status') }}</TreeGridCell>
</TreeGridRow>
</TreeGridRoot>
</template>셀 편집
행과 열의 키보드 이동을 유지하면서 하위 리소스 담당자를 편집합니다.
<!-- Tree Grid / Editable -->
<script setup lang="ts">
import { reactive } from 'vue'
import { TreeGridCell, TreeGridDisclosure, TreeGridEditor, TreeGridRoot, TreeGridRow } from '@sectile/vue/tree-grid'
const rows = [
{ id: 'platform', parentID: null, cells: ['platform-name', 'platform-owner', 'platform-status'] },
{ id: 'storefront', parentID: 'platform', cells: ['storefront-name', 'storefront-owner', 'storefront-status'] },
{ id: 'checkout', parentID: 'storefront', cells: ['checkout-name', 'checkout-owner', 'checkout-status'] },
]
const values = reactive(new Map([
['platform-name', 'Commerce platform'], ['platform-owner', 'Platform team'], ['platform-status', 'Healthy'],
['storefront-name', 'Storefront'], ['storefront-owner', 'Mina Kim'], ['storefront-status', 'Modified'],
['checkout-name', 'Checkout flow'], ['checkout-owner', 'Alex Chen'], ['checkout-status', 'In review'],
]))
const valueFor = (id: string) => values.get(id) ?? ''
const updateValue = (id: string, value: string) => values.set(id, value)
</script>
<template>
<TreeGridRoot
:rows="rows"
:get-cell-value="valueFor"
:set-cell-value="updateValue"
:default-expanded-value="['platform', 'storefront']"
default-highlighted-value="storefront-owner"
default-edit-mode="editing"
aria-label="Project inventory"
v-slot="{ expandedValue }"
>
<div role="row"><span role="columnheader">Resource</span><span role="columnheader">Owner</span><span role="columnheader">Status</span></div>
<TreeGridRow value="platform" :row-index="1" :expandable="true">
<TreeGridCell value="platform-name" :column-index="1">
<TreeGridDisclosure for="platform">Toggle</TreeGridDisclosure>
{{ valueFor('platform-name') }}
</TreeGridCell>
<TreeGridCell value="platform-owner" :column-index="2" v-slot="{ editing }">
<span v-if="!editing">{{ valueFor('platform-owner') }}</span>
<TreeGridEditor for="platform-owner" label="Commerce platform owner" />
</TreeGridCell>
<TreeGridCell value="platform-status" :column-index="3">{{ valueFor('platform-status') }}</TreeGridCell>
</TreeGridRow>
<TreeGridRow v-if="expandedValue.includes('platform')" value="storefront" :row-index="2" :level="2" :expandable="true">
<TreeGridCell value="storefront-name" :column-index="1">
<TreeGridDisclosure for="storefront">Toggle</TreeGridDisclosure>
{{ valueFor('storefront-name') }}
</TreeGridCell>
<TreeGridCell value="storefront-owner" :column-index="2" v-slot="{ editing }">
<span v-if="!editing">{{ valueFor('storefront-owner') }}</span>
<TreeGridEditor for="storefront-owner" label="Storefront owner" />
</TreeGridCell>
<TreeGridCell value="storefront-status" :column-index="3">{{ valueFor('storefront-status') }}</TreeGridCell>
</TreeGridRow>
<TreeGridRow v-if="expandedValue.includes('storefront')" value="checkout" :row-index="3" :level="3">
<TreeGridCell value="checkout-name" :column-index="1">{{ valueFor('checkout-name') }}</TreeGridCell>
<TreeGridCell value="checkout-owner" :column-index="2" v-slot="{ editing }">
<span v-if="!editing">{{ valueFor('checkout-owner') }}</span>
<TreeGridEditor for="checkout-owner" label="Checkout owner" />
</TreeGridCell>
<TreeGridCell value="checkout-status" :column-index="3">{{ valueFor('checkout-status') }}</TreeGridCell>
</TreeGridRow>
</TreeGridRoot>
</template>외부 상태 관리
현재 값은 부모가 관리하고, 허용된 변경을 컴포넌트에 다시 전달합니다.
<!-- Tree Grid / Controlled -->
<script setup lang="ts">
import { reactive } from 'vue'
import { TreeGridCell, TreeGridDisclosure, TreeGridEditor, TreeGridRoot, TreeGridRow } from '@sectile/vue/tree-grid'
const rows = [
{ id: 'platform', parentID: null, cells: ['platform-name', 'platform-owner', 'platform-status'] },
{ id: 'storefront', parentID: 'platform', cells: ['storefront-name', 'storefront-owner', 'storefront-status'] },
{ id: 'checkout', parentID: 'storefront', cells: ['checkout-name', 'checkout-owner', 'checkout-status'] },
]
const values = reactive(new Map([
['platform-name', 'Commerce platform'], ['platform-owner', 'Platform team'], ['platform-status', 'Healthy'],
['storefront-name', 'Storefront'], ['storefront-owner', 'Mina Kim'], ['storefront-status', 'Modified'],
['checkout-name', 'Checkout flow'], ['checkout-owner', 'Alex Chen'], ['checkout-status', 'In review'],
]))
const valueFor = (id: string) => values.get(id) ?? ''
const updateValue = (id: string, value: string) => values.set(id, value)
</script>
<template>
<TreeGridRoot
:rows="rows"
:get-cell-value="valueFor"
:set-cell-value="updateValue"
:default-expanded-value="['platform', 'storefront']"
aria-label="Project inventory"
v-slot="{ expandedValue }"
>
<div role="row"><span role="columnheader">Resource</span><span role="columnheader">Owner</span><span role="columnheader">Status</span></div>
<TreeGridRow value="platform" :row-index="1" :expandable="true">
<TreeGridCell value="platform-name" :column-index="1">
<TreeGridDisclosure for="platform">Toggle</TreeGridDisclosure>
{{ valueFor('platform-name') }}
</TreeGridCell>
<TreeGridCell value="platform-owner" :column-index="2" v-slot="{ editing }">
<span v-if="!editing">{{ valueFor('platform-owner') }}</span>
<TreeGridEditor for="platform-owner" label="Commerce platform owner" />
</TreeGridCell>
<TreeGridCell value="platform-status" :column-index="3">{{ valueFor('platform-status') }}</TreeGridCell>
</TreeGridRow>
<TreeGridRow v-if="expandedValue.includes('platform')" value="storefront" :row-index="2" :level="2" :expandable="true">
<TreeGridCell value="storefront-name" :column-index="1">
<TreeGridDisclosure for="storefront">Toggle</TreeGridDisclosure>
{{ valueFor('storefront-name') }}
</TreeGridCell>
<TreeGridCell value="storefront-owner" :column-index="2" v-slot="{ editing }">
<span v-if="!editing">{{ valueFor('storefront-owner') }}</span>
<TreeGridEditor for="storefront-owner" label="Storefront owner" />
</TreeGridCell>
<TreeGridCell value="storefront-status" :column-index="3">{{ valueFor('storefront-status') }}</TreeGridCell>
</TreeGridRow>
<TreeGridRow v-if="expandedValue.includes('storefront')" value="checkout" :row-index="3" :level="3">
<TreeGridCell value="checkout-name" :column-index="1">{{ valueFor('checkout-name') }}</TreeGridCell>
<TreeGridCell value="checkout-owner" :column-index="2" v-slot="{ editing }">
<span v-if="!editing">{{ valueFor('checkout-owner') }}</span>
<TreeGridEditor for="checkout-owner" label="Checkout owner" />
</TreeGridCell>
<TreeGridCell value="checkout-status" :column-index="3">{{ valueFor('checkout-status') }}</TreeGridCell>
</TreeGridRow>
</TreeGridRoot>
</template>API
Vue 패키지: @sectile/vue/tree-grid
TreeGridRootTreeGridRowTreeGridCellTreeGridDisclosureTreeGridEditor
Props
TreeGridRootProps
as이 파트가 렌더링할 요소 또는 컴포넌트입니다.
asChild하나뿐인 자식 요소에 파트 속성을 직접 합칠지 여부입니다.
defaultEditMode컴포넌트가 관리하는 초기 편집 상태입니다.
defaultExpandedValue컴포넌트가 관리하는 처음 펼친 값입니다.
defaultHighlightedValue컴포넌트가 관리하는 처음 강조 값입니다.
defaultValue컴포넌트가 값을 관리할 때 사용할 초깃값입니다.
disabled사용자 조작을 막을지 여부입니다.
editMode외부에서 제어하는 편집 상태입니다.
expandedValue하위 항목을 표시할 외부 제어 값입니다.
getCellValue격자 셀이 나타내는 편집값을 읽는 함수입니다.
highlightedValue키보드 조작 대상으로 강조된 현재 값입니다.
modelValue부모가 상태를 관리할 때 사용할 현재 값입니다.
policies검증, 이동, 선택 동작을 조정하는 정책입니다.
readonly값 확인만 허용하는 읽기 전용 상태 여부입니다.
rows컴포넌트가 관리할 2차원 항목 구조입니다.
setCellValue격자 셀의 새 편집값을 확정하는 함수입니다.
TreeGridPartProps
as이 파트가 렌더링할 요소 또는 컴포넌트입니다.
asChild하나뿐인 자식 요소에 파트 속성을 직접 합칠지 여부입니다.
슬롯
TreeGridRootSlotProps
disabled사용자 조작을 막을지 여부입니다.
editMode현재 편집 상태입니다.
expandedValue하위 항목을 표시 중인 값입니다.
highlightedValue조작 대상으로 강조된 현재 값입니다.
readonly값 확인만 허용하는 읽기 전용 상태 여부입니다.
value이 계약이 노출하는 현재 값입니다.
TreeGridRowSlotProps
disabled사용자 조작을 막을지 여부입니다.
editMode현재 편집 상태입니다.
expanded하위 항목이 보이는지 여부입니다.
expandedValue하위 항목을 표시 중인 값입니다.
highlightedValue조작 대상으로 강조된 현재 값입니다.
readonly값 확인만 허용하는 읽기 전용 상태 여부입니다.
value이 계약이 노출하는 현재 값입니다.
TreeGridCellSlotProps
disabled사용자 조작을 막을지 여부입니다.
editing현재 편집 중인지 여부입니다.
editMode현재 편집 상태입니다.
expandedValue하위 항목을 표시 중인 값입니다.
highlighted조작 대상으로 강조된 항목인지 여부입니다.
highlightedValue조작 대상으로 강조된 현재 값입니다.
readonly값 확인만 허용하는 읽기 전용 상태 여부입니다.
selected현재 선택된 항목인지 여부입니다.
value이 계약이 노출하는 현재 값입니다.
이벤트
TreeGridRoot
update:editMode새 편집 상태를 요청할 때 발생합니다.
update:expandedValue새 펼침 값을 요청할 때 발생합니다.
update:highlightedValue새 강조 값을 요청할 때 발생합니다.
update:modelValue컴포넌트가 외부 제어 값의 변경을 요청할 때 발생합니다.
기타 타입
TreeGridCellValueResolver
type TreeGridCellValueResolver = NonNullable<TreeGridRootProps['getCellValue']>TreeGridCellValueSetter
type TreeGridCellValueSetter = NonNullable<TreeGridRootProps['setCellValue']>TreeGridValueChangeHandler
type TreeGridValueChangeHandler = (value: string | null) => voidTreeGridExpandedValueChangeHandler
type TreeGridExpandedValueChangeHandler = (value: readonly string[]) => voidTreeGridHighlightedValueChangeHandler
type TreeGridHighlightedValueChangeHandler = (value: string | null) => voidTreeGridEditModeChangeHandler
type TreeGridEditModeChangeHandler = (value: TreeGridEditMode) => voidTreeGridEditMode
type TreeGridEditMode = 'navigation' | 'editing'TreeGridPolicies
| 이름 | 타입 | 필수 |
|---|---|---|
eligible | (id: CellID) => boolean | — |
boundary | AxisBoundaryPolicy | — |
maxScan | number | — |
TreeGridRowInput
| 이름 | 타입 | 필수 |
|---|---|---|
id | RowID | 필수 |
parentID | RowID | null | 필수 |
cells | readonly (CellID | null)[] | 필수 |
파트
공통 범위: [data-scope="tree-grid"]. 컴포넌트 내부로 스타일을 제한할 때 파트 선택자와 함께 사용합니다.
| 파트 | 선택자 | 역할 | 추가 속성 |
|---|---|---|---|
root | [data-part="root"] | 컴포넌트 경계와 내부 파트를 묶습니다. | — |
row | [data-part="row"] | 같은 그리드 행에 속한 셀을 묶습니다. | — |
cell | [data-part="cell"] | 탐색하거나 선택할 수 있는 그리드 값 하나입니다. | — |
disclosure | [data-part="disclosure"] | 하위 콘텐츠를 펼치거나 접습니다. | — |
editor | [data-part="editor"] | 활성 그리드 또는 트리 그리드 셀을 편집합니다. | — |
키보드 동작
| 키 | 동작 |
|---|---|
| Arrow keys | 격자 칸 사이를 이동합니다. |
| Space | 현재 칸이나 행을 선택합니다. |
| Enter / F2 | 현재 칸이 편집을 지원하면 편집 모드로 들어갑니다. |
| Escape | 현재 편집을 취소합니다. |
접근성
이름이 있는 격자 안에서 행과 칸이 계층·위치·펼침·선택·편집 상태를 노출합니다.
관련 WAI-ARIA 패턴에서 호스트 접근성 규칙을 확인할 수 있습니다.
