Files
sori.studio/components/admin/AdminNavPrimaryBranch.vue

161 lines
4.5 KiB
Vue

<script setup>
import AdminNavPrimaryBranch from './AdminNavPrimaryBranch.vue'
const props = defineProps({
/** buildNavigationEditorTree 결과 */
wraps: {
type: Array,
required: true
},
/** 들여쓰기 단계 */
depth: {
type: Number,
default: 0
},
/** 루트면 `'root'`, 아니면 부모 항목 id */
parentKey: {
type: String,
default: 'root'
},
/** 드래그 중인 항목 id */
draggingId: {
type: String,
default: ''
},
/** 드롭 대상 위에 올린 항목 id */
dragOverId: {
type: String,
default: ''
}
})
const emit = defineEmits([
'drag-start',
'drag-over',
'drag-end',
'drop',
'add-child',
'remove'
])
/**
* 드래그 시작
* @param {DragEvent} event - 이벤트
* @param {string} itemId - 항목 id
* @returns {void}
*/
const onDragStart = (event, itemId) => {
if (!event.dataTransfer) {
return
}
emit('drag-start', { parentKey: props.parentKey, itemId })
event.dataTransfer.effectAllowed = 'move'
}
/**
* 드래그 오버
* @param {DragEvent} event - 이벤트
* @param {string} itemId - 항목 id
* @returns {void}
*/
const onDragOver = (event, itemId) => {
event.preventDefault()
emit('drag-over', itemId)
}
/**
* 드롭
* @param {DragEvent} event - 이벤트
* @param {string} itemId - 대상 id
* @returns {void}
*/
const onDrop = (event, itemId) => {
event.preventDefault()
emit('drop', { parentKey: props.parentKey, targetId: itemId })
}
/**
* 드래그 종료
* @returns {void}
*/
const onDragEnd = () => {
emit('drag-end')
}
</script>
<template>
<ul class="admin-nav-primary-branch space-y-2" :class="depth ? 'mt-1 border-l border-line pl-3' : ''">
<li
v-for="wrap in wraps"
:key="wrap.item.id"
class="admin-nav-primary-branch__row rounded border border-transparent bg-white px-2 py-2 transition-colors"
:class="dragOverId === wrap.item.id ? 'border-ink/20 bg-[#f5f5f2]' : draggingId === wrap.item.id ? 'opacity-60' : ''"
>
<div class="admin-nav-primary-branch__controls flex flex-wrap items-center gap-2" :style="{ paddingLeft: `${depth * 4}px` }">
<span
class="admin-nav-primary-branch__handle cursor-grab select-none text-muted active:cursor-grabbing"
draggable="true"
title="드래그하여 순서 변경"
@dragstart="onDragStart($event, wrap.item.id)"
@dragover="onDragOver($event, wrap.item.id)"
@drop="onDrop($event, wrap.item.id)"
@dragend="onDragEnd"
>
::
</span>
<label class="admin-nav-primary-branch__visible flex items-center gap-1 text-xs text-muted">
<input v-model="wrap.item.isVisible" class="h-4 w-4" type="checkbox">
표시
</label>
<label class="admin-nav-primary-branch__folder flex items-center gap-1 text-xs text-muted">
<input v-model="wrap.item.isFolder" class="h-4 w-4" type="checkbox">
폴더
</label>
<input
v-model="wrap.item.label"
class="admin-nav-primary-branch__label min-w-[120px] flex-1 rounded border border-line px-2 py-1.5 text-sm"
type="text"
placeholder="라벨"
required
>
<input
v-model="wrap.item.url"
class="admin-nav-primary-branch__url min-w-[160px] flex-1 rounded border border-line px-2 py-1.5 text-sm font-mono"
type="text"
placeholder="URL (# 또는 /경로)"
required
>
<button
class="admin-nav-primary-branch__add-child rounded border border-line px-2 py-1 text-xs font-semibold"
type="button"
@click="emit('add-child', wrap.item.id)"
>
하위
</button>
<button
class="admin-nav-primary-branch__remove rounded border border-red-200 px-2 py-1 text-xs font-semibold text-red-700"
type="button"
@click="emit('remove', wrap.item.id)"
>
삭제
</button>
</div>
<AdminNavPrimaryBranch
v-if="wrap.children.length"
class="admin-nav-primary-branch__children mt-2"
:wraps="wrap.children"
:depth="depth + 1"
:parent-key="String(wrap.item.id)"
:dragging-id="draggingId"
:drag-over-id="dragOverId"
@drag-start="emit('drag-start', $event)"
@drag-over="emit('drag-over', $event)"
@drag-end="emit('drag-end')"
@drop="emit('drop', $event)"
@add-child="emit('add-child', $event)"
@remove="emit('remove', $event)"
/>
</li>
</ul>
</template>