Usage
트리 구성 요소를 사용하여 항목의 계층 구조를 표시합니다.
<script setup lang="ts">
import type { TreeItem } from '@nuxt/ui'
const items = ref<TreeItem[]>([
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{
label: 'useAuth.ts',
icon: 'i-vscode-icons-file-type-typescript'
},
{
label: 'useUser.ts',
icon: 'i-vscode-icons-file-type-typescript'
}
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{
label: 'Card.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'Button.vue',
icon: 'i-vscode-icons-file-type-vue'
}
]
}
]
},
{
label: 'app.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'nuxt.config.ts',
icon: 'i-vscode-icons-file-type-nuxt'
}
])
</script>
<template>
<UTree :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { TreeItem } from '@nuxt/ui'
const items = ref<TreeItem[]>([
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{
label: 'useAuth.ts',
icon: 'i-vscode-icons-file-type-typescript'
},
{
label: 'useUser.ts',
icon: 'i-vscode-icons-file-type-typescript'
}
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{
label: 'Card.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'Button.vue',
icon: 'i-vscode-icons-file-type-vue'
}
]
}
]
},
{
label: 'app.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'nuxt.config.ts',
icon: 'i-vscode-icons-file-type-nuxt'
}
])
</script>
<template>
<UTree :items="items" />
</template>Items 항목
items prop을 다음 속성을 가진 오브젝트 배열로 사용합니다.
icon?: stringXph037x{lang="ts-type"}label?: stringtrailingIcon?: stringdefaultExpanded?: booleandisabled?: booleanslot?: stringchildren?: TreeItem[]onToggle?: (e: TreeItemToggleEvent<TreeItem>) => void의 발음을 -onToggle?: (e: TreeItemToggleEvent<TreeItem>) => voidonSelect?: (e: TreeItemSelectEvent<TreeItem>) => void- {lang="ts-type"}class?: anyui?: { item?: ClassNameValue, itemWithChildren?: ClassNameValue, link?: ClassNameValue, linkLeadingIcon?: ClassNameValue, linkLabel?: ClassNameValue, linkTrailing?: ClassNameValue, linkTrailingIcon?: ClassNameValue, listWithChildren?: ClassNameValue }
get-key가 제공되지 않은 경우 구성 요소는 label prop을 식별자로 사용합니다. 고유 식별자를 반환하려면 get-key 함수 prop을 제공하는 것이 좋습니다. 또는 labelKey prop을 사용하여 고유 식별자로 사용할 속성을 지정할 수도 있습니다.<script setup lang="ts">
import type { TreeItem } from '@nuxt/ui'
const items = ref<TreeItem[]>([
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{
label: 'useAuth.ts',
icon: 'i-vscode-icons-file-type-typescript'
},
{
label: 'useUser.ts',
icon: 'i-vscode-icons-file-type-typescript'
}
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{
label: 'Card.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'Button.vue',
icon: 'i-vscode-icons-file-type-vue'
}
]
}
]
},
{
label: 'app.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'nuxt.config.ts',
icon: 'i-vscode-icons-file-type-nuxt'
}
])
</script>
<template>
<UTree :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { TreeItem } from '@nuxt/ui'
const items = ref<TreeItem[]>([
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{
label: 'useAuth.ts',
icon: 'i-vscode-icons-file-type-typescript'
},
{
label: 'useUser.ts',
icon: 'i-vscode-icons-file-type-typescript'
}
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{
label: 'Card.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'Button.vue',
icon: 'i-vscode-icons-file-type-vue'
}
]
}
]
},
{
label: 'app.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'nuxt.config.ts',
icon: 'i-vscode-icons-file-type-nuxt'
}
])
</script>
<template>
<UTree :items="items" />
</template>Multiple 다중
multiple prop을 사용하여 여러 항목 선택을 허용합니다.
<script setup lang="ts">
import type { TreeItem } from '@nuxt/ui'
const items = ref<TreeItem[]>([
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{
label: 'useAuth.ts',
icon: 'i-vscode-icons-file-type-typescript'
},
{
label: 'useUser.ts',
icon: 'i-vscode-icons-file-type-typescript'
}
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{
label: 'Card.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'Button.vue',
icon: 'i-vscode-icons-file-type-vue'
}
]
}
]
},
{
label: 'app.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'nuxt.config.ts',
icon: 'i-vscode-icons-file-type-nuxt'
}
])
</script>
<template>
<UTree multiple :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { TreeItem } from '@nuxt/ui'
const items = ref<TreeItem[]>([
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{
label: 'useAuth.ts',
icon: 'i-vscode-icons-file-type-typescript'
},
{
label: 'useUser.ts',
icon: 'i-vscode-icons-file-type-typescript'
}
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{
label: 'Card.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'Button.vue',
icon: 'i-vscode-icons-file-type-vue'
}
]
}
]
},
{
label: 'app.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'nuxt.config.ts',
icon: 'i-vscode-icons-file-type-nuxt'
}
])
</script>
<template>
<UTree multiple :items="items" />
</template>Nested: badge{label="4.1+" class="align-text-top"}
nested 소품을 사용하여 트리를 중첩된 구조로 렌더링할지 아니면 플랫 목록으로 렌더링할지를 제어합니다. 기본값은 true입니다.
<script setup lang="ts">
import type { TreeItem } from '@nuxt/ui'
const items = ref<TreeItem[]>([
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{
label: 'useAuth.ts',
icon: 'i-vscode-icons-file-type-typescript'
},
{
label: 'useUser.ts',
icon: 'i-vscode-icons-file-type-typescript'
}
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{
label: 'Card.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'Button.vue',
icon: 'i-vscode-icons-file-type-vue'
}
]
}
]
},
{
label: 'app.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'nuxt.config.ts',
icon: 'i-vscode-icons-file-type-nuxt'
}
])
</script>
<template>
<UTree :nested="false" :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { TreeItem } from '@nuxt/ui'
const items = ref<TreeItem[]>([
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{
label: 'useAuth.ts',
icon: 'i-vscode-icons-file-type-typescript'
},
{
label: 'useUser.ts',
icon: 'i-vscode-icons-file-type-typescript'
}
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{
label: 'Card.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'Button.vue',
icon: 'i-vscode-icons-file-type-vue'
}
]
}
]
},
{
label: 'app.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'nuxt.config.ts',
icon: 'i-vscode-icons-file-type-nuxt'
}
])
</script>
<template>
<UTree :nested="false" :items="items" />
</template>color
color Prop을 사용하여 나무의 색상을 변경합니다.
<script setup lang="ts">
import type { TreeItem } from '@nuxt/ui'
const items = ref<TreeItem[]>([
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{
label: 'useAuth.ts',
icon: 'i-vscode-icons-file-type-typescript'
},
{
label: 'useUser.ts',
icon: 'i-vscode-icons-file-type-typescript'
}
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{
label: 'Card.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'Button.vue',
icon: 'i-vscode-icons-file-type-vue'
}
]
}
]
},
{
label: 'app.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'nuxt.config.ts',
icon: 'i-vscode-icons-file-type-nuxt'
}
])
</script>
<template>
<UTree color="neutral" :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { TreeItem } from '@nuxt/ui'
const items = ref<TreeItem[]>([
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{
label: 'useAuth.ts',
icon: 'i-vscode-icons-file-type-typescript'
},
{
label: 'useUser.ts',
icon: 'i-vscode-icons-file-type-typescript'
}
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{
label: 'Card.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'Button.vue',
icon: 'i-vscode-icons-file-type-vue'
}
]
}
]
},
{
label: 'app.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'nuxt.config.ts',
icon: 'i-vscode-icons-file-type-nuxt'
}
])
</script>
<template>
<UTree color="neutral" :items="items" />
</template>Size 파일
size prop을 사용하여 나무의 크기를 변경합니다.
<script setup lang="ts">
import type { TreeItem } from '@nuxt/ui'
const items = ref<TreeItem[]>([
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{
label: 'useAuth.ts',
icon: 'i-vscode-icons-file-type-typescript'
},
{
label: 'useUser.ts',
icon: 'i-vscode-icons-file-type-typescript'
}
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{
label: 'Card.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'Button.vue',
icon: 'i-vscode-icons-file-type-vue'
}
]
}
]
},
{
label: 'app.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'nuxt.config.ts',
icon: 'i-vscode-icons-file-type-nuxt'
}
])
</script>
<template>
<UTree size="xl" :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { TreeItem } from '@nuxt/ui'
const items = ref<TreeItem[]>([
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{
label: 'useAuth.ts',
icon: 'i-vscode-icons-file-type-typescript'
},
{
label: 'useUser.ts',
icon: 'i-vscode-icons-file-type-typescript'
}
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{
label: 'Card.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'Button.vue',
icon: 'i-vscode-icons-file-type-vue'
}
]
}
]
},
{
label: 'app.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'nuxt.config.ts',
icon: 'i-vscode-icons-file-type-nuxt'
}
])
</script>
<template>
<UTree size="xl" :items="items" />
</template>트레일 아이콘
trailing-icon 소품을 사용하여 상위 노드의 후행 Icon를 사용자 정의합니다. 기본값은 i-lucide-chevron-down입니다.
<script setup lang="ts">
import type { TreeItem } from '@nuxt/ui'
const items = ref<TreeItem[]>([
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
trailingIcon: 'i-lucide-chevron-down',
children: [
{
label: 'useAuth.ts',
icon: 'i-vscode-icons-file-type-typescript'
},
{
label: 'useUser.ts',
icon: 'i-vscode-icons-file-type-typescript'
}
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{
label: 'Card.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'Button.vue',
icon: 'i-vscode-icons-file-type-vue'
}
]
}
]
},
{
label: 'app.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'nuxt.config.ts',
icon: 'i-vscode-icons-file-type-nuxt'
}
])
</script>
<template>
<UTree trailing-icon="i-lucide-arrow-down" :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { TreeItem } from '@nuxt/ui'
const items = ref<TreeItem[]>([
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
trailingIcon: 'i-lucide-chevron-down',
children: [
{
label: 'useAuth.ts',
icon: 'i-vscode-icons-file-type-typescript'
},
{
label: 'useUser.ts',
icon: 'i-vscode-icons-file-type-typescript'
}
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{
label: 'Card.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'Button.vue',
icon: 'i-vscode-icons-file-type-vue'
}
]
}
]
},
{
label: 'app.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'nuxt.config.ts',
icon: 'i-vscode-icons-file-type-nuxt'
}
])
</script>
<template>
<UTree trailing-icon="i-lucide-arrow-down" :items="items" />
</template>확장 아이콘
expanded-icon 및 collapsed-icon 소품을 사용하여 상위 노드가 확장되거나 축소될 때 해당 아이콘을 사용자 정의합니다. 기본값은 각각 i-lucide-folder-open 및 i-lucide-folder입니다.
<script setup lang="ts">
import type { TreeItem } from '@nuxt/ui'
const items = ref<TreeItem[]>([
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{
label: 'useAuth.ts',
icon: 'i-vscode-icons-file-type-typescript'
},
{
label: 'useUser.ts',
icon: 'i-vscode-icons-file-type-typescript'
}
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{
label: 'Card.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'Button.vue',
icon: 'i-vscode-icons-file-type-vue'
}
]
}
]
},
{
label: 'app.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'nuxt.config.ts',
icon: 'i-vscode-icons-file-type-nuxt'
}
])
</script>
<template>
<UTree expanded-icon="i-lucide-book-open" collapsed-icon="i-lucide-book" :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { TreeItem } from '@nuxt/ui'
const items = ref<TreeItem[]>([
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{
label: 'useAuth.ts',
icon: 'i-vscode-icons-file-type-typescript'
},
{
label: 'useUser.ts',
icon: 'i-vscode-icons-file-type-typescript'
}
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{
label: 'Card.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'Button.vue',
icon: 'i-vscode-icons-file-type-vue'
}
]
}
]
},
{
label: 'app.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'nuxt.config.ts',
icon: 'i-vscode-icons-file-type-nuxt'
}
])
</script>
<template>
<UTree expanded-icon="i-lucide-book-open" collapsed-icon="i-lucide-book" :items="items" />
</template>Disabled 사용 안 함
disabled prop을 사용하여 사용자가 트리와 상호 작용하지 않도록 합니다.
<script setup lang="ts">
import type { TreeItem } from '@nuxt/ui'
const items = ref<TreeItem[]>([
{
label: 'app',
icon: 'i-lucide-folder',
defaultExpanded: true,
children: [
{
label: 'composables',
icon: 'i-lucide-folder',
children: [
{
label: 'useAuth.ts',
icon: 'i-vscode-icons-file-type-typescript'
},
{
label: 'useUser.ts',
icon: 'i-vscode-icons-file-type-typescript'
}
]
},
{
label: 'components',
icon: 'i-lucide-folder',
children: [
{
label: 'Home',
icon: 'i-lucide-folder',
children: [
{
label: 'Card.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'Button.vue',
icon: 'i-vscode-icons-file-type-vue'
}
]
}
]
}
]
},
{
label: 'app.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'nuxt.config.ts',
icon: 'i-vscode-icons-file-type-nuxt'
}
])
</script>
<template>
<UTree disabled :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { TreeItem } from '@nuxt/ui'
const items = ref<TreeItem[]>([
{
label: 'app',
icon: 'i-lucide-folder',
defaultExpanded: true,
children: [
{
label: 'composables',
icon: 'i-lucide-folder',
children: [
{
label: 'useAuth.ts',
icon: 'i-vscode-icons-file-type-typescript'
},
{
label: 'useUser.ts',
icon: 'i-vscode-icons-file-type-typescript'
}
]
},
{
label: 'components',
icon: 'i-lucide-folder',
children: [
{
label: 'Home',
icon: 'i-lucide-folder',
children: [
{
label: 'Card.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'Button.vue',
icon: 'i-vscode-icons-file-type-vue'
}
]
}
]
}
]
},
{
label: 'app.vue',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'nuxt.config.ts',
icon: 'i-vscode-icons-file-type-nuxt'
}
])
</script>
<template>
<UTree disabled :items="items" />
</template>item.disabled를 사용하여 개별 항목을 비활성화할 수도 있습니다.examples 예제
Control 선택된 항목
default-value prop 또는 v-model 지시문을 사용하여 선택된 항목을 제어할 수 있습니다.
<script setup lang="ts">
import type { TreeItem } from '@nuxt/ui'
const items: TreeItem[] = [
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
]
}
]
},
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
]
const value = ref()
</script>
<template>
<UTree v-model="value" :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { TreeItem } from '@nuxt/ui'
const items: TreeItem[] = [
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
]
}
]
},
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
]
const value = ref()
</script>
<template>
<UTree v-model="value" :items="items" />
</template>
get-key Prop을 사용하여 v-model 또는 default-value가 제공 될 때 각 항목에서 고유 키를 얻는 데 사용되는 기능을 변경합니다.항목이 선택되지 않도록 하려면 item.onSelect() 속성 또는 전역 select 이벤트를 사용할 수 있습니다.
<script setup lang="ts">
import type { TreeItemSelectEvent } from 'reka-ui'
import type { TreeItem } from '@nuxt/ui'
const items: TreeItem[] = [
{
label: 'app/',
defaultExpanded: true,
onSelect: (e: Event) => {
e.preventDefault()
},
children: [
{
label: 'composables/',
children: [
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
]
}
]
},
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
]
function onSelect(e: TreeItemSelectEvent<TreeItem>) {
if (e.detail.originalEvent.type === 'click') {
e.preventDefault()
}
}
</script>
<template>
<UTree :items="items" @select="onSelect" />
</template>
Control 확장된 항목
default-expanded prop 또는 v-model 지시문을 사용하여 확장 된 항목을 제어 할 수 있습니다.
<script setup lang="ts">
import type { TreeItem } from '@nuxt/ui'
const items = [
{
label: 'app/',
id: 'app',
children: [
{
label: 'composables/',
id: 'app/composables',
children: [
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
]
},
{
label: 'components/',
id: 'app/components',
children: [
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
]
}
]
},
{ label: 'app.vue', id: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'nuxt.config.ts', id: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
] satisfies TreeItem[]
const expanded = ref(['app', 'app/composables'])
</script>
<template>
<UTree v-model:expanded="expanded" :items="items" :get-key="i => i.id" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { TreeItem } from '@nuxt/ui'
const items = [
{
label: 'app/',
id: 'app',
children: [
{
label: 'composables/',
id: 'app/composables',
children: [
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
]
},
{
label: 'components/',
id: 'app/components',
children: [
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
]
}
]
},
{ label: 'app.vue', id: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'nuxt.config.ts', id: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
] satisfies TreeItem[]
const expanded = ref(['app', 'app/composables'])
</script>
<template>
<UTree v-model:expanded="expanded" :items="items" :get-key="i => i.id" />
</template>
항목이 확장되지 않도록 하려면 item.onToggle() 속성 또는 전역 toggle 이벤트를 사용할 수 있습니다.
<script setup lang="ts">
import type { TreeItemToggleEvent } from 'reka-ui'
import type { TreeItem } from '@nuxt/ui'
const items: TreeItem[] = [
{
label: 'app/',
defaultExpanded: true,
onToggle: (e: Event) => {
e.preventDefault()
},
children: [
{
label: 'composables/',
children: [
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
]
}
]
},
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
]
function onToggle(e: TreeItemToggleEvent<TreeItem>) {
if (e.detail.originalEvent.type === 'keydown') {
e.preventDefault()
}
}
</script>
<template>
<UTree :items="items" @toggle="onToggle" />
</template>
항목에 체크 박스가 있습니다 : badge{label="4.1+" class="align-text-top"}
item-leading 슬롯을 사용하여 Checkbox를 항목에 추가할 수 있습니다. multiple, propagate-select 및 bubble-select 소품을 사용하여 상위-하위 관계와 select 및 xph44x 이벤트를 사용하여 다중 선택을 활성화하여 항목의 선택 및 확장 상태를 제어합니다.
- app/
- composables/
- components/
- Card.vue
- Button.vue
- app.vue
- nuxt.config.ts
<script setup lang="ts">
import type { TreeItemSelectEvent } from 'reka-ui'
import type { TreeItem } from '@nuxt/ui'
const items: TreeItem[] = [
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{ label: 'useAuth.ts' },
{ label: 'useUser.ts' }
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{ label: 'Card.vue' },
{ label: 'Button.vue' }
]
}
]
},
{ label: 'app.vue' },
{ label: 'nuxt.config.ts' }
]
const value = ref<(typeof items)>([])
function onSelect(e: TreeItemSelectEvent<TreeItem>) {
if (e.detail.originalEvent.type === 'click') {
e.preventDefault()
}
}
</script>
<template>
<UTree
v-model="value"
:as="{ link: 'div' }"
:items="items"
multiple
propagate-select
bubble-select
@select="onSelect"
>
<template #item-leading="{ selected, indeterminate, handleSelect }">
<UCheckbox
:model-value="indeterminate ? 'indeterminate' : selected"
tabindex="-1"
@change="handleSelect"
@click.stop
/>
</template>
</UTree>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { TreeItemSelectEvent } from 'reka-ui'
import type { TreeItem } from '@nuxt/ui'
const items: TreeItem[] = [
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{ label: 'useAuth.ts' },
{ label: 'useUser.ts' }
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{ label: 'Card.vue' },
{ label: 'Button.vue' }
]
}
]
},
{ label: 'app.vue' },
{ label: 'nuxt.config.ts' }
]
const value = ref<(typeof items)>([])
function onSelect(e: TreeItemSelectEvent<TreeItem>) {
if (e.detail.originalEvent.type === 'click') {
e.preventDefault()
}
}
</script>
<template>
<UTree
v-model="value"
:as="{ link: 'div' }"
:items="items"
multiple
propagate-select
bubble-select
@select="onSelect"
>
<template #item-leading="{ selected, indeterminate, handleSelect }">
<UCheckbox
:model-value="indeterminate ? 'indeterminate' : selected"
tabindex="-1"
@change="handleSelect"
@click.stop
/>
</template>
</UTree>
</template>
드래그 앤 드롭 사용 : badge{label="4.1+" class="align-text-top"}
@vueuse/integrations의 [useSortablehttps://vueuse.org/integrations/useSortable/) 컴포지블을 사용하여 트리에서 드래그 앤 드롭 기능을 사용할 수 있습니다. 이 통합은 Sortable.js를 감싸서 원활한 드래그 앤 드롭 환경을 제공합니다.
<script setup lang="ts">
import type { TreeItem } from '@nuxt/ui'
import { useSortable } from '@vueuse/integrations/useSortable'
const items = shallowRef<TreeItem[]>([
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
]
}
]
},
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
])
function flatten(
items: TreeItem[],
parent = items
): { item: TreeItem; parent: TreeItem[]; index: number }[] {
return items.flatMap((item, index) => [
{ item, parent, index },
...(item.children?.length && item.defaultExpanded ? flatten(item.children, item.children) : [])
])
}
function moveItem(oldIndex: number, newIndex: number) {
if (oldIndex === newIndex) return
const flat = flatten(items.value)
const source = flat[oldIndex]
const target = flat[newIndex]
if (!source || !target) return
const [moved] = source.parent.splice(source.index, 1)
if (!moved) return
const updatedFlat = flatten(items.value)
const updatedTarget = updatedFlat.find(({ item }) => item === target.item)
if (!updatedTarget) return
const insertIndex = oldIndex < newIndex ? updatedTarget.index + 1 : updatedTarget.index
updatedTarget.parent.splice(insertIndex, 0, moved)
}
const tree = useTemplateRef<HTMLElement>('tree')
useSortable(tree, items, {
animation: 150,
ghostClass: 'opacity-50',
onUpdate: (e: any) => moveItem(e.oldIndex, e.newIndex)
})
</script>
<template>
<UTree ref="tree" :nested="false" :unmount-on-hide="false" :items="items" />
</template><script setup lang="ts">
import { shallowRef, useTemplateRef } from 'vue'
import type { TreeItem } from '@nuxt/ui'
import { useSortable } from '@vueuse/integrations/useSortable'
const items = shallowRef<TreeItem[]>([
{
label: 'app/',
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
]
}
]
},
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
])
function flatten(
items: TreeItem[],
parent = items
): { item: TreeItem; parent: TreeItem[]; index: number }[] {
return items.flatMap((item, index) => [
{ item, parent, index },
...(item.children?.length && item.defaultExpanded ? flatten(item.children, item.children) : [])
])
}
function moveItem(oldIndex: number, newIndex: number) {
if (oldIndex === newIndex) return
const flat = flatten(items.value)
const source = flat[oldIndex]
const target = flat[newIndex]
if (!source || !target) return
const [moved] = source.parent.splice(source.index, 1)
if (!moved) return
const updatedFlat = flatten(items.value)
const updatedTarget = updatedFlat.find(({ item }) => item === target.item)
if (!updatedTarget) return
const insertIndex = oldIndex < newIndex ? updatedTarget.index + 1 : updatedTarget.index
updatedTarget.parent.splice(insertIndex, 0, moved)
}
const tree = useTemplateRef<HTMLElement>('tree')
useSortable(tree, items, {
animation: 150,
ghostClass: 'opacity-50',
onUpdate: (e: any) => moveItem(e.oldIndex, e.newIndex)
})
</script>
<template>
<UTree ref="tree" :nested="false" :unmount-on-hide="false" :items="items" />
</template>nested prop을 false로 설정하여 항목을 드래그 앤 드롭할 수 있도록 항목의 평평한 목록을 갖습니다.가상화 지원: badge{label="4.1+" class="align-text-top"}
virtualize prop을 사용하여 큰 목록에 대해 부울 또는 { estimateSize: 32, overscan: 12 }와 같은 옵션이있는 개체로 가상화를 활성화합니다.
nested prop을 false로 설정하는 것과 유사하게 트리 구조가 편평화됩니다.<script setup lang="ts">
import type { TreeItem } from '@nuxt/ui'
const items: TreeItem[] = Array(1000)
.fill(0)
.map((_, i) => ({
label: `Item ${i + 1}`,
children: [
{ label: `Child ${i + 1}-1`, icon: 'i-lucide-file' },
{ label: `Child ${i + 1}-2`, icon: 'i-lucide-file' }
]
}))
</script>
<template>
<UTree virtualize :items="items" class="h-80" />
</template>사용자 지정 슬롯 포함
slot 속성을 사용하여 특정 항목을 사용자 정의합니다.
다음과 같은 슬롯에 액세스할 수 있습니다.
#{{ item.slot }}-wrapper#{{ item.slot }}#{{ item.slot }}-leading#{{ item.slot }}-label#{{ item.slot }}-trailing
<script setup lang="ts">
import type { TreeItem } from '@nuxt/ui'
const items = [
{
label: 'app/',
slot: 'app' as const,
defaultExpanded: true,
children: [
{
label: 'composables/',
children: [
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
]
},
{
label: 'components/',
defaultExpanded: true,
children: [
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
]
}
]
},
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
] satisfies TreeItem[]
</script>
<template>
<UTree :items="items">
<template #app="{ item }">
<p class="italic font-bold">
{{ item.label }}
</p>
</template>
</UTree>
</template>
API 사용
Props (### Props)
| Prop | Default | Type |
|---|---|---|
as | 'ul' | anyThe element or component this component should render as. |
color | 'primary' | "primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral" |
size | 'md' | "md" | "xs" | "sm" | "lg" | "xl" |
getKey | (val: T[number]): stringThis function is passed the index of each item and should return a unique key for that item | |
labelKey | 'label' | keyof Extract<NestedItem<T>, object> & string | DotPathKeys<Extract<NestedItem<T>, object>>The key used to get the label from the item. |
trailingIcon | appConfig.ui.icons.chevronDown | anyThe icon displayed on the right side of a parent node. |
expandedIcon | appConfig.ui.icons.folderOpen | anyThe icon displayed when a parent node is expanded. |
collapsedIcon | appConfig.ui.icons.folder | anyThe icon displayed when a parent node is collapsed. |
items | T | |
modelValue | M extends true ? T[number][] : T[number]The controlled value of the Tree. Can be bind as | |
defaultValue | M extends true ? T[number][] : T[number]The value of the Tree when initially rendered. Use when you do not need to control the state of the Tree. | |
multiple | MWhether multiple options can be selected or not. | |
nested | true | boolean Use nested DOM structure (children inside parents) vs flattened structure (all items at same level).
When |
virtualize | false | boolean | { overscan?: number ; estimateSize?: number | ((index: number) => number) | undefined; } | undefinedEnable virtualization for large lists.
Note: when enabled, the tree structure is flattened like if |
onSelect | (e: SelectEvent<T[number]>, item: T[number]): void | |
onToggle | (e: ToggleEvent<T[number]>, item: T[number]): void | |
expanded | string[]The controlled value of the expanded item. Can be binded with | |
defaultExpanded | string[]The value of the expanded tree when initially rendered. Use when you do not need to control the state of the expanded tree | |
selectionBehavior | "replace" | "toggle"How multiple selection should behave in the collection. | |
propagateSelect | boolean When | |
disabled | boolean When | |
bubbleSelect | boolean When | |
ui | { root?: SlotClass; item?: SlotClass; listWithChildren?: SlotClass; itemWithChildren?: SlotClass; link?: SlotClass; linkLeadingIcon?: SlotClass; linkLabel?: SlotClass; linkTrailing?: SlotClass; linkTrailingIcon?: SlotClass; } |
Slots
| Slot | Type |
|---|---|
item-wrapper | { item: T[number]; index: number; level: number; expanded: boolean; selected: boolean; indeterminate: boolean | undefined; handleSelect: () => void; handleToggle: () => void; ui: object; } |
item | { item: T[number]; index: number; level: number; expanded: boolean; selected: boolean; indeterminate: boolean | undefined; handleSelect: () => void; handleToggle: () => void; ui: object; } |
item-leading | { item: T[number]; index: number; level: number; expanded: boolean; selected: boolean; indeterminate: boolean | undefined; handleSelect: () => void; handleToggle: () => void; ui: object; } |
item-label | { item: T[number]; index: number; level: number; expanded: boolean; selected: boolean; indeterminate: boolean | undefined; handleSelect: () => void; handleToggle: () => void; ui: object; } |
item-trailing | { item: T[number]; index: number; level: number; expanded: boolean; selected: boolean; indeterminate: boolean | undefined; handleSelect: () => void; handleToggle: () => void; ui: object; } |
Emits
| Event | Type |
|---|---|
update:modelValue | [val: M extends true ? T[number][] : T[number]] |
update:expanded | [val: string[]] |
테마
export default defineAppConfig({
ui: {
tree: {
slots: {
root: 'relative isolate',
item: 'w-full',
listWithChildren: 'border-s border-default',
itemWithChildren: 'ps-1.5 -ms-px',
link: 'relative group w-full flex items-center text-sm select-none before:absolute before:inset-y-px before:inset-x-0 before:z-[-1] before:rounded-md focus:outline-none focus-visible:outline-none focus-visible:before:outline-3',
linkLeadingIcon: 'shrink-0 relative',
linkLabel: 'truncate',
linkTrailing: 'ms-auto inline-flex gap-1.5 items-center',
linkTrailingIcon: 'shrink-0 transform transition-transform duration-200 ease-out motion-reduce:transition-none group-data-expanded:rotate-180'
},
variants: {
virtualize: {
true: {
root: 'overflow-y-auto'
}
},
color: {
primary: {
link: 'before:outline-primary/25'
},
secondary: {
link: 'before:outline-secondary/25'
},
success: {
link: 'before:outline-success/25'
},
info: {
link: 'before:outline-info/25'
},
warning: {
link: 'before:outline-warning/25'
},
error: {
link: 'before:outline-error/25'
},
neutral: {
link: 'before:outline-inverted/25'
}
},
size: {
xs: {
listWithChildren: 'ms-4',
link: 'px-2 py-1 text-xs gap-1',
linkLeadingIcon: 'size-4',
linkTrailingIcon: 'size-4'
},
sm: {
listWithChildren: 'ms-4.5',
link: 'px-2.5 py-1.5 text-xs gap-1.5',
linkLeadingIcon: 'size-4',
linkTrailingIcon: 'size-4'
},
md: {
listWithChildren: 'ms-5',
link: 'px-2.5 py-1.5 text-sm gap-1.5',
linkLeadingIcon: 'size-5',
linkTrailingIcon: 'size-5'
},
lg: {
listWithChildren: 'ms-5.5',
link: 'px-3 py-2 text-sm gap-2',
linkLeadingIcon: 'size-5',
linkTrailingIcon: 'size-5'
},
xl: {
listWithChildren: 'ms-6',
link: 'px-3 py-2 text-base gap-2',
linkLeadingIcon: 'size-6',
linkTrailingIcon: 'size-6'
}
},
selected: {
true: {
link: 'before:bg-elevated'
}
},
disabled: {
true: {
link: 'cursor-not-allowed opacity-75'
}
}
},
compoundVariants: [
{
color: 'primary',
selected: true,
class: {
link: 'text-primary'
}
},
{
color: 'neutral',
selected: true,
class: {
link: 'text-highlighted'
}
},
{
selected: false,
disabled: false,
class: {
link: [
'hover:text-highlighted hover:before:bg-elevated/50',
'transition-colors before:transition-colors'
]
}
}
],
defaultVariants: {
color: 'primary',
size: 'md'
}
}
}
})import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
tree: {
slots: {
root: 'relative isolate',
item: 'w-full',
listWithChildren: 'border-s border-default',
itemWithChildren: 'ps-1.5 -ms-px',
link: 'relative group w-full flex items-center text-sm select-none before:absolute before:inset-y-px before:inset-x-0 before:z-[-1] before:rounded-md focus:outline-none focus-visible:outline-none focus-visible:before:outline-3',
linkLeadingIcon: 'shrink-0 relative',
linkLabel: 'truncate',
linkTrailing: 'ms-auto inline-flex gap-1.5 items-center',
linkTrailingIcon: 'shrink-0 transform transition-transform duration-200 ease-out motion-reduce:transition-none group-data-expanded:rotate-180'
},
variants: {
virtualize: {
true: {
root: 'overflow-y-auto'
}
},
color: {
primary: {
link: 'before:outline-primary/25'
},
secondary: {
link: 'before:outline-secondary/25'
},
success: {
link: 'before:outline-success/25'
},
info: {
link: 'before:outline-info/25'
},
warning: {
link: 'before:outline-warning/25'
},
error: {
link: 'before:outline-error/25'
},
neutral: {
link: 'before:outline-inverted/25'
}
},
size: {
xs: {
listWithChildren: 'ms-4',
link: 'px-2 py-1 text-xs gap-1',
linkLeadingIcon: 'size-4',
linkTrailingIcon: 'size-4'
},
sm: {
listWithChildren: 'ms-4.5',
link: 'px-2.5 py-1.5 text-xs gap-1.5',
linkLeadingIcon: 'size-4',
linkTrailingIcon: 'size-4'
},
md: {
listWithChildren: 'ms-5',
link: 'px-2.5 py-1.5 text-sm gap-1.5',
linkLeadingIcon: 'size-5',
linkTrailingIcon: 'size-5'
},
lg: {
listWithChildren: 'ms-5.5',
link: 'px-3 py-2 text-sm gap-2',
linkLeadingIcon: 'size-5',
linkTrailingIcon: 'size-5'
},
xl: {
listWithChildren: 'ms-6',
link: 'px-3 py-2 text-base gap-2',
linkLeadingIcon: 'size-6',
linkTrailingIcon: 'size-6'
}
},
selected: {
true: {
link: 'before:bg-elevated'
}
},
disabled: {
true: {
link: 'cursor-not-allowed opacity-75'
}
}
},
compoundVariants: [
{
color: 'primary',
selected: true,
class: {
link: 'text-primary'
}
},
{
color: 'neutral',
selected: true,
class: {
link: 'text-highlighted'
}
},
{
selected: false,
disabled: false,
class: {
link: [
'hover:text-highlighted hover:before:bg-elevated/50',
'transition-colors before:transition-colors'
]
}
}
],
defaultVariants: {
color: 'primary',
size: 'md'
}
}
}
})
]
})