92 lines
4.5 KiB
Vue
92 lines
4.5 KiB
Vue
<script setup>
|
|
const props = defineProps({
|
|
/** 공개 API `primary` 트리 노드 */
|
|
nodes: {
|
|
type: Array,
|
|
default: () => []
|
|
}
|
|
})
|
|
|
|
const expandedSet = inject('sidebarPrimaryNavExpandedSet')
|
|
const toggleBranch = inject('sidebarPrimaryNavToggle')
|
|
|
|
/**
|
|
* 노드가 펼쳐져 있는지
|
|
* @param {string} id - 노드 id
|
|
* @returns {boolean}
|
|
*/
|
|
const isExpanded = (id) => expandedSet?.value?.has(String(id)) ?? true
|
|
|
|
/**
|
|
* 노드 URL이 실제 링크로 쓸 수 있는지
|
|
* @param {string} url - URL
|
|
* @returns {boolean}
|
|
*/
|
|
const hasNavigableUrl = (url) => Boolean(url && String(url).trim() !== '' && String(url).trim() !== '#')
|
|
</script>
|
|
|
|
<template>
|
|
<ul class="sidebar-primary-nav-list flex flex-col gap-[3px] text-[15px] text-[var(--site-text)]">
|
|
<template v-for="node in nodes" :key="node.id">
|
|
<li
|
|
v-if="node.children?.length"
|
|
class="sidebar-primary-nav-list__branch group relative flex w-full flex-col"
|
|
>
|
|
<div
|
|
class="sidebar-primary-nav-list__branch-row flex w-full items-stretch gap-0.5 rounded-[10px] py-1.5 pr-1 leading-tight"
|
|
:class="isExpanded(node.id) ? 'bg-[color:color-mix(in_srgb,var(--site-text)_6%,transparent)]' : ''"
|
|
>
|
|
<button
|
|
class="sidebar-primary-nav-list__chevron site-panel-hover grid w-8 shrink-0 place-items-center rounded-lg text-xs text-[var(--site-muted)] transition-colors hover:text-[var(--site-text)]"
|
|
type="button"
|
|
:aria-expanded="isExpanded(node.id)"
|
|
:aria-label="isExpanded(node.id) ? '하위 메뉴 접기' : '하위 메뉴 펼치기'"
|
|
@click="toggleBranch(node.id)"
|
|
>
|
|
<span class="select-none">{{ isExpanded(node.id) ? '⌃' : '⌄' }}</span>
|
|
</button>
|
|
<NuxtLink
|
|
v-if="hasNavigableUrl(node.url)"
|
|
class="sidebar-primary-nav-list__nav-link site-panel-hover flex min-w-0 flex-1 items-center gap-2 rounded-[10px] py-1.5 pr-3 pl-0 leading-tight transition-[padding,background-color] duration-200 before:h-4 before:w-1 before:flex-none before:rounded-sm before:rounded-l-none before:bg-[var(--site-line)] before:transition-[width,height,border-radius,background-color] before:duration-200 hover:px-3 hover:before:h-2 hover:before:w-2 hover:before:rounded-full hover:before:bg-[color:color-mix(in_srgb,var(--site-text)_28%,var(--site-line))]"
|
|
:to="node.url"
|
|
>
|
|
<span class="sidebar-primary-nav-list__label flex-1 truncate transition-transform duration-200 group-hover:translate-x-[3px]">{{ node.label }}</span>
|
|
</NuxtLink>
|
|
<span
|
|
v-else
|
|
class="sidebar-primary-nav-list__folder-label flex min-w-0 flex-1 items-center gap-2 py-1.5 pr-3 pl-2 text-[var(--site-text)]"
|
|
>
|
|
<span class="sidebar-primary-nav-list__dot h-1.5 w-1.5 shrink-0 rounded-full bg-[var(--site-muted)]" />
|
|
<span class="truncate font-medium">{{ node.label }}</span>
|
|
</span>
|
|
</div>
|
|
<ul
|
|
v-show="isExpanded(node.id)"
|
|
class="sidebar-primary-nav-list__sub ml-2 mt-0.5 border-l border-[var(--site-line)] pl-2"
|
|
>
|
|
<SidebarPrimaryNavList :nodes="node.children" />
|
|
</ul>
|
|
</li>
|
|
<li
|
|
v-else
|
|
class="sidebar-primary-nav-list__leaf group relative flex w-full items-center"
|
|
>
|
|
<NuxtLink
|
|
v-if="hasNavigableUrl(node.url)"
|
|
class="sidebar-primary-nav-list__nav-link site-panel-hover flex flex-1 items-center gap-2 rounded-[10px] py-1.5 pr-3 pl-0 leading-tight transition-[padding,background-color] duration-200 before:h-4 before:w-1 before:flex-none before:rounded-sm before:rounded-l-none before:bg-[var(--site-line)] before:transition-[width,height,border-radius,background-color] before:duration-200 hover:px-3 hover:before:h-2 hover:before:w-2 hover:before:rounded-full hover:before:bg-[color:color-mix(in_srgb,var(--site-text)_28%,var(--site-line))]"
|
|
:to="node.url"
|
|
>
|
|
<span class="sidebar-primary-nav-list__label flex-1 transition-transform duration-200 group-hover:translate-x-[3px]">{{ node.label }}</span>
|
|
</NuxtLink>
|
|
<span
|
|
v-else
|
|
class="sidebar-primary-nav-list__leaf-static flex flex-1 items-center gap-2 rounded-[10px] py-1.5 pr-3 pl-2 text-[var(--site-text)]"
|
|
>
|
|
<span class="h-1.5 w-1.5 shrink-0 rounded-full bg-[var(--site-muted)]" />
|
|
<span class="font-medium">{{ node.label }}</span>
|
|
</span>
|
|
</li>
|
|
</template>
|
|
</ul>
|
|
</template>
|