(() => { const board = document.querySelector('[data-pinboard-surface]'); if (!board) { return; } const notes = Array.from(board.querySelectorAll('[data-pin-note]')); if (!notes.length) { return; } let zCounter = 200; const randomBetween = (min, max) => Math.random() * (max - min) + min; const placeNotes = () => { const boardRect = board.getBoundingClientRect(); const maxXBase = Math.max(24, boardRect.width - 300); const maxYBase = Math.max(24, boardRect.height - 220); notes.forEach((note, index) => { const maxX = Math.max(16, maxXBase - note.offsetWidth * 0.2); const maxY = Math.max(16, maxYBase - note.offsetHeight * 0.2); const x = randomBetween(12, maxX); const y = randomBetween(12, maxY); const rotation = randomBetween(-7, 7); const highlighted = note.dataset.highlighted === '1'; const level = highlighted ? 1000 + index : 100 + index; note.dataset.baseRotation = String(rotation); note.dataset.x = String(x); note.dataset.y = String(y); note.style.zIndex = String(level); note.style.transform = `translate(${x}px, ${y}px) rotate(${rotation}deg)`; }); }; const clampToBoard = (note, x, y) => { const boardRect = board.getBoundingClientRect(); const noteRect = note.getBoundingClientRect(); const maxX = Math.max(0, boardRect.width - noteRect.width); const maxY = Math.max(0, boardRect.height - noteRect.height); return { x: Math.min(Math.max(0, x), maxX), y: Math.min(Math.max(0, y), maxY), }; }; const enableDrag = (note) => { let dragging = false; let pointerId = null; let offsetX = 0; let offsetY = 0; note.addEventListener('pointerdown', (event) => { if (event.target instanceof Element && event.target.closest('a, button, input, select, textarea, label')) { return; } dragging = true; pointerId = event.pointerId; note.setPointerCapture(pointerId); note.classList.add('is-dragging'); note.style.zIndex = String(++zCounter + 2000); const startX = Number.parseFloat(note.dataset.x ?? '0'); const startY = Number.parseFloat(note.dataset.y ?? '0'); offsetX = event.clientX - startX; offsetY = event.clientY - startY; }); note.addEventListener('pointermove', (event) => { if (!dragging || event.pointerId !== pointerId) { return; } const baseRotation = Number.parseFloat(note.dataset.baseRotation ?? '0'); const nextX = event.clientX - offsetX; const nextY = event.clientY - offsetY; const clamped = clampToBoard(note, nextX, nextY); note.dataset.x = String(clamped.x); note.dataset.y = String(clamped.y); note.style.transform = `translate(${clamped.x}px, ${clamped.y}px) rotate(${baseRotation}deg)`; }); const releaseDrag = (event) => { if (!dragging || event.pointerId !== pointerId) { return; } dragging = false; note.classList.remove('is-dragging'); note.releasePointerCapture(pointerId); pointerId = null; }; note.addEventListener('pointerup', releaseDrag); note.addEventListener('pointercancel', releaseDrag); }; const adjustBoardHeight = () => { let requiredHeight = 620; notes.forEach((note) => { const y = Number.parseFloat(note.dataset.y ?? '0'); requiredHeight = Math.max(requiredHeight, y + note.offsetHeight + 36); }); board.style.minHeight = `${Math.ceil(requiredHeight)}px`; }; placeNotes(); notes.forEach(enableDrag); adjustBoardHeight(); window.addEventListener('resize', () => { placeNotes(); adjustBoardHeight(); }); })();