feat(18.1-01): task 4 flat reorder in the admin SPA
- ReorderView reads flat from the reorder data; ReorderBranch takes a flat prop and shows only move up and move down - ArrowRight and ArrowLeft do nothing, an inside drop lands after the row (tree.ts flatZone, flatMove) and flatten sends parent_id null - vitest flat cases; docs describe the flat page; rebuilt dist
This commit is contained in:
@@ -1,26 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
import { inject } from 'vue'
|
||||
import { computed, inject } from 'vue'
|
||||
import { ChevronDown, ChevronUp, GripVertical, IndentDecrease, IndentIncrease } from '@lucide/vue'
|
||||
import { t } from '../../app/i18n'
|
||||
import { REORDER_PAGE } from './context'
|
||||
import type { Move, TreeNode } from './tree'
|
||||
import { flatMove, type Move, type TreeNode } from './tree'
|
||||
|
||||
// One level of the reorder tree (UI-SPEC "Tree"): a list item per node with
|
||||
// its row (drag handle, name, move up, move down, outdent, indent) and its
|
||||
// children's list inside it. The page owns the tree and every move.
|
||||
defineProps<{ nodes: TreeNode[]; depth: number }>()
|
||||
// children's list inside it. The page owns the tree and every move. A flat
|
||||
// reorder (D-17) shows only move up and move down.
|
||||
const props = withDefaults(defineProps<{ nodes: TreeNode[]; depth: number; flat?: boolean }>(), { flat: false })
|
||||
|
||||
const page = inject(REORDER_PAGE)!
|
||||
|
||||
const iconButton =
|
||||
'inline-flex size-8 shrink-0 items-center justify-center rounded-pager text-muted transition-colors duration-150 ease-out hover:bg-hover hover:text-text disabled:cursor-not-allowed disabled:opacity-40'
|
||||
|
||||
const moves: Array<{ move: Move; icon: typeof ChevronUp; label: string; action: string }> = [
|
||||
const allMoves: Array<{ move: Move; icon: typeof ChevronUp; label: string; action: string }> = [
|
||||
{ move: 'up', icon: ChevronUp, label: 'backend::lang.reorder.move_up', action: 'reorder-up' },
|
||||
{ move: 'down', icon: ChevronDown, label: 'backend::lang.reorder.move_down', action: 'reorder-down' },
|
||||
{ move: 'outdent', icon: IndentDecrease, label: 'backend::lang.reorder.outdent', action: 'reorder-outdent' },
|
||||
{ move: 'indent', icon: IndentIncrease, label: 'backend::lang.reorder.indent', action: 'reorder-indent' },
|
||||
]
|
||||
const moves = computed(() => allMoves.filter((item) => flatMove(item.move, props.flat)))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -80,7 +82,7 @@ const moves: Array<{ move: Move; icon: typeof ChevronUp; label: string; action:
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<ReorderBranch v-if="node.children.length > 0" :nodes="node.children" :depth="depth + 1" />
|
||||
<ReorderBranch v-if="node.children.length > 0" :nodes="node.children" :depth="depth + 1" :flat="flat" />
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
@@ -58,12 +58,15 @@ export function buildTree(nodes: readonly AdminReorderNode[]): TreeNode[] {
|
||||
return roots
|
||||
}
|
||||
|
||||
/** The tree as sent: pre-order, sort_order counting from 1, parent null at the top. */
|
||||
export function flatten(tree: readonly TreeNode[]): FlatNode[] {
|
||||
/**
|
||||
* The tree as sent: pre-order, sort_order counting from 1, parent null at the
|
||||
* top. A flat reorder (D-17) sends parent null on every node.
|
||||
*/
|
||||
export function flatten(tree: readonly TreeNode[], flat = false): FlatNode[] {
|
||||
const out: FlatNode[] = []
|
||||
const walk = (list: readonly TreeNode[], parent: number | null) => {
|
||||
for (const node of list) {
|
||||
out.push({ id: node.id, parent_id: parent, sort_order: out.length + 1 })
|
||||
out.push({ id: node.id, parent_id: flat ? null : parent, sort_order: out.length + 1 })
|
||||
walk(node.children, node.id)
|
||||
}
|
||||
}
|
||||
@@ -150,6 +153,16 @@ export function applyMove(tree: TreeNode[], id: number, move: Move): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
/** The drop zone a flat reorder (D-17) uses: it never nests, so inside lands after the row. */
|
||||
export function flatZone(zone: Zone, flat: boolean): Zone {
|
||||
return flat && zone === 'inside' ? 'after' : zone
|
||||
}
|
||||
|
||||
/** The moves a flat reorder offers: siblings only, never indent or outdent. */
|
||||
export function flatMove(move: Move, flat: boolean): boolean {
|
||||
return !flat || move === 'up' || move === 'down'
|
||||
}
|
||||
|
||||
/** Whether node `dragged` may land on `target` in zone: never in its own subtree. */
|
||||
export function canDrop(tree: TreeNode[], dragged: number, target: number): boolean {
|
||||
const at = locate(tree, dragged)
|
||||
|
||||
Reference in New Issue
Block a user