editor: explain slot refusals on-page; auto-scroll while dragging\n\nDrag state now records the tagged slot under the pointer. When the\ndragged type does not fit it, a badge on the zone names the accepted\ntypes (from the slot schema) and the pointer chip turns refused -\npreviously a refused slot silently fell back to a container drop.\nHolding the pointer near a viewport edge scrolls the page (ramped\nrAF loop, re-targeting after each step) so long pages are droppable\nend to end. Helpers are unit-tested; harness covers refusal badge,\naccept path and the scroll step.\n\n🤖 Generated with Codebuff\nCo-Authored-By: Codebuff <noreply@codebuff.com>

This commit is contained in:
mats committed 2026-11-07 11:53:47 +01:00
1 parent 8e8823421d
commit 40b33f89a4
6 files changed
+154 -12

No files matched your search

+22 -1
View File
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest'
import { dropTarget, moveTarget } from '../src/page/dnd'
import { dropTarget, edgeScroll, moveTarget, slotAt } from '../src/page/dnd'
import type { DndBlock, DndSlot } from '../src/page/dnd'
const r = (y: number, h = 100, x = 0, w = 600) => ({ x, y, w, h })
@@ -63,6 +63,27 @@ describe('dropTarget with tagged slots ([data-cms-slot])', () => {
it('without an acceptsSlot filter the slot still wins over the container', () => {
expect(dropTarget(pdp, { x: 50, y: 200 }, 'C', undefined, slots)).toMatchObject({ parentId: 'p1', handle: '3d-accessories' })
})
it('slotAt reports the zone under the pointer regardless of fit', () => {
const zones: DndSlot[] = [
{ handle: 'sidebar', parentId: 's1', rect: r(190, 150, 0, 600), kids: [] },
{ handle: 'hero', parentId: 's1', rect: r(10, 100, 0, 600), kids: [] },
]
expect(slotAt(zones, { x: 50, y: 200 })?.handle).toBe('sidebar')
expect(slotAt(zones, { x: 50, y: 50 })?.handle).toBe('hero')
expect(slotAt(zones, { x: 50, y: 500 })).toBeNull()
})
it('edgeScroll stays put inside the safe area and ramps to max at the edge', () => {
expect(edgeScroll(400, 300, 800, 600)).toEqual({ dx: 0, dy: 0 })
expect(edgeScroll(0, 300, 800, 600).dx).toBe(-24)
expect(edgeScroll(800, 300, 800, 600).dx).toBe(24)
expect(edgeScroll(400, 0, 800, 600).dy).toBe(-24)
expect(edgeScroll(400, 600, 800, 600).dy).toBe(24)
const half = edgeScroll(36, 300, 800, 600)
expect(half.dx).toBeLessThan(0)
expect(half.dx).toBeGreaterThan(-24)
})
it('moveTarget sorts within the dragged block\'s own slot', () => {
const two: DndBlock[] = [
{ id: 'p1', container: true, depth: 0, rect: r(0, 400) },