Update index.html
Browse files- index.html +9 -212
index.html
CHANGED
|
@@ -51,7 +51,6 @@
|
|
| 51 |
transition: transform 0.2s, box-shadow 0.2s;
|
| 52 |
touch-action: none;
|
| 53 |
user-select: none;
|
| 54 |
-
position: relative;
|
| 55 |
}
|
| 56 |
.member-card:active { cursor: grabbing; transform: scale(0.98); }
|
| 57 |
.member-card:hover {
|
|
@@ -59,58 +58,6 @@
|
|
| 59 |
border-color: #3b82f6;
|
| 60 |
}
|
| 61 |
|
| 62 |
-
/* iPhone-style drag handle (челка) */
|
| 63 |
-
.drag-handle {
|
| 64 |
-
position: absolute;
|
| 65 |
-
top: 8px;
|
| 66 |
-
left: 50%;
|
| 67 |
-
transform: translateX(-50%);
|
| 68 |
-
width: 36px;
|
| 69 |
-
height: 5px;
|
| 70 |
-
background: rgba(255, 255, 255, 0.5);
|
| 71 |
-
border-radius: 2.5px;
|
| 72 |
-
z-index: 10;
|
| 73 |
-
cursor: grab;
|
| 74 |
-
display: none; /* Hidden by default on desktop */
|
| 75 |
-
}
|
| 76 |
-
|
| 77 |
-
/* Show drag handle on mobile and when hovering */
|
| 78 |
-
@media (max-width: 768px) {
|
| 79 |
-
.drag-handle {
|
| 80 |
-
display: block;
|
| 81 |
-
}
|
| 82 |
-
}
|
| 83 |
-
.member-card:hover .drag-handle {
|
| 84 |
-
display: block;
|
| 85 |
-
background: rgba(255, 255, 255, 0.7);
|
| 86 |
-
}
|
| 87 |
-
.member-card.dragging .drag-handle {
|
| 88 |
-
display: block;
|
| 89 |
-
background: #3b82f6;
|
| 90 |
-
}
|
| 91 |
-
|
| 92 |
-
/* Mobile drag ghost with челка */
|
| 93 |
-
.drag-ghost {
|
| 94 |
-
position: fixed;
|
| 95 |
-
width: 140px;
|
| 96 |
-
height: 187px;
|
| 97 |
-
background: rgba(30, 41, 59, 0.95);
|
| 98 |
-
border: 2px solid #3b82f6;
|
| 99 |
-
border-radius: 16px;
|
| 100 |
-
z-index: 9999;
|
| 101 |
-
pointer-events: none;
|
| 102 |
-
display: flex;
|
| 103 |
-
flex-direction: column;
|
| 104 |
-
justify-content: flex-end;
|
| 105 |
-
overflow: hidden;
|
| 106 |
-
transform: translate(-50%, -50%);
|
| 107 |
-
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
|
| 108 |
-
}
|
| 109 |
-
.drag-ghost .drag-handle {
|
| 110 |
-
display: block;
|
| 111 |
-
background: #3b82f6;
|
| 112 |
-
}
|
| 113 |
-
|
| 114 |
/* Spinner */
|
| 115 |
.spinner {
|
| 116 |
border: 3px solid rgba(255,255,255,0.3);
|
|
@@ -129,14 +76,6 @@
|
|
| 129 |
@media (max-width: 768px) {
|
| 130 |
.roster-slot { min-width: 100px; }
|
| 131 |
#playersContainer { grid-template-columns: repeat(2, 1fr); }
|
| 132 |
-
.drag-ghost {
|
| 133 |
-
width: 120px;
|
| 134 |
-
height: 160px;
|
| 135 |
-
}
|
| 136 |
-
.drag-handle {
|
| 137 |
-
width: 32px;
|
| 138 |
-
height: 4px;
|
| 139 |
-
}
|
| 140 |
}
|
| 141 |
|
| 142 |
/* Share modal */
|
|
@@ -285,15 +224,6 @@
|
|
| 285 |
</div>
|
| 286 |
</div>
|
| 287 |
|
| 288 |
-
<!-- DRAG GHOST FOR MOBILE (with челка) -->
|
| 289 |
-
<div id="dragGhost" class="drag-ghost hidden">
|
| 290 |
-
<div class="drag-handle"></div>
|
| 291 |
-
<div class="relative z-10 text-center w-full bg-gradient-to-t from-black via-black/80 to-transparent pt-6 pb-2 px-1">
|
| 292 |
-
<div id="ghostRole" class="text-[9px] font-bold uppercase tracking-widest text-cyan-400 mb-0.5"></div>
|
| 293 |
-
<div id="ghostName" class="text-xs font-bold text-white truncate w-full leading-tight shadow-black drop-shadow-md"></div>
|
| 294 |
-
</div>
|
| 295 |
-
</div>
|
| 296 |
-
|
| 297 |
<!-- SHARE MODAL -->
|
| 298 |
<div id="shareModal" class="share-modal">
|
| 299 |
<div class="share-content">
|
|
@@ -347,16 +277,11 @@
|
|
| 347 |
window.teamsCache = {};
|
| 348 |
window.isSharedMode = false;
|
| 349 |
|
| 350 |
-
//
|
| 351 |
-
let
|
| 352 |
-
let dragData = null;
|
| 353 |
-
let currentDragCard = null;
|
| 354 |
-
let draggedFromSlot = null;
|
| 355 |
-
let touchStartTime = 0;
|
| 356 |
let touchStartX = 0;
|
| 357 |
let touchStartY = 0;
|
| 358 |
-
let
|
| 359 |
-
let longPressTimer = null;
|
| 360 |
|
| 361 |
// Check if we're in shared mode (has teamId in URL)
|
| 362 |
const urlParams = new URLSearchParams(window.location.search);
|
|
@@ -420,22 +345,6 @@
|
|
| 420 |
|
| 421 |
// --- TOUCH DRAG & DROP (Mobile Support) ---
|
| 422 |
window.touchStart = function(event) {
|
| 423 |
-
const target = event.target;
|
| 424 |
-
|
| 425 |
-
// If clicking on drag handle, start drag immediately
|
| 426 |
-
if (target.classList.contains('drag-handle') || target.closest('.drag-handle')) {
|
| 427 |
-
const card = target.closest('.member-card');
|
| 428 |
-
if (card) {
|
| 429 |
-
event.preventDefault();
|
| 430 |
-
const touch = event.touches[0];
|
| 431 |
-
touchStartX = touch.clientX;
|
| 432 |
-
touchStartY = touch.clientY;
|
| 433 |
-
touchStartTime = Date.now();
|
| 434 |
-
startDragFromTouch(card, touch.clientX, touch.clientY);
|
| 435 |
-
}
|
| 436 |
-
return;
|
| 437 |
-
}
|
| 438 |
-
|
| 439 |
touchStartX = event.touches[0].clientX;
|
| 440 |
touchStartY = event.touches[0].clientY;
|
| 441 |
touchStartTime = Date.now();
|
|
@@ -457,58 +366,16 @@
|
|
| 457 |
const timeDiff = touchEndTime - touchStartTime;
|
| 458 |
|
| 459 |
// If it's a tap (small movement, short time) and we have drag data, drop it
|
| 460 |
-
if (distX < 10 && distY < 10 && timeDiff < 500 &&
|
| 461 |
const slot = event.target.closest('.roster-slot');
|
| 462 |
if (slot) {
|
| 463 |
-
handleDrop(slot,
|
| 464 |
-
|
| 465 |
-
isDragging = false;
|
| 466 |
-
if (currentDragCard) {
|
| 467 |
-
currentDragCard.classList.remove('dragging');
|
| 468 |
-
currentDragCard = null;
|
| 469 |
-
}
|
| 470 |
}
|
| 471 |
}
|
| 472 |
event.preventDefault();
|
| 473 |
}
|
| 474 |
|
| 475 |
-
// Start drag from touch
|
| 476 |
-
function startDragFromTouch(card, clientX, clientY) {
|
| 477 |
-
const name = card.querySelector('.name-in').value;
|
| 478 |
-
const role = card.querySelector('.role-in').value;
|
| 479 |
-
|
| 480 |
-
if (!name) {
|
| 481 |
-
showNotif("Please enter player name first", "error");
|
| 482 |
-
return;
|
| 483 |
-
}
|
| 484 |
-
|
| 485 |
-
// Get Image from data-url or src
|
| 486 |
-
const imgObj = card.querySelector('.member-img');
|
| 487 |
-
let img = '';
|
| 488 |
-
if (imgObj.getAttribute('data-url')) {
|
| 489 |
-
img = imgObj.getAttribute('data-url');
|
| 490 |
-
} else if (imgObj.src && imgObj.src !== window.location.href && !imgObj.src.startsWith('data:')) {
|
| 491 |
-
img = imgObj.src;
|
| 492 |
-
}
|
| 493 |
-
|
| 494 |
-
const id = card.getAttribute('data-id') || 'temp_' + Date.now();
|
| 495 |
-
|
| 496 |
-
dragData = { id, name, role, img };
|
| 497 |
-
currentDragCard = card;
|
| 498 |
-
isDragging = true;
|
| 499 |
-
|
| 500 |
-
// Check if dragging from a slot
|
| 501 |
-
const slot = card.closest('.roster-slot');
|
| 502 |
-
if (slot) {
|
| 503 |
-
draggedFromSlot = slot;
|
| 504 |
-
}
|
| 505 |
-
|
| 506 |
-
// Show ghost for mobile
|
| 507 |
-
showDragGhost(name, role, img, clientX, clientY);
|
| 508 |
-
|
| 509 |
-
card.classList.add('dragging');
|
| 510 |
-
}
|
| 511 |
-
|
| 512 |
// --- DRAG & DROP LOGIC (Desktop & Mobile) ---
|
| 513 |
const ROLE_LIMITS = { 'coach': 1, 'striker': 2, 'keeper': 1, 'sweeper': 1, 'libero': 1, 'guide': 1 };
|
| 514 |
|
|
@@ -547,9 +414,7 @@
|
|
| 547 |
ev.dataTransfer.setData("text/plain", payload);
|
| 548 |
|
| 549 |
// For mobile touch
|
| 550 |
-
|
| 551 |
-
currentDragCard = card;
|
| 552 |
-
card.classList.add('dragging');
|
| 553 |
}
|
| 554 |
|
| 555 |
window.drop = function(ev) {
|
|
@@ -587,71 +452,6 @@
|
|
| 587 |
renderSlot(slot, data);
|
| 588 |
}
|
| 589 |
|
| 590 |
-
// Show drag ghost
|
| 591 |
-
function showDragGhost(name, role, img, x, y) {
|
| 592 |
-
const ghost = document.getElementById('dragGhost');
|
| 593 |
-
document.getElementById('ghostName').textContent = name;
|
| 594 |
-
document.getElementById('ghostRole').textContent = role;
|
| 595 |
-
|
| 596 |
-
if (img) {
|
| 597 |
-
ghost.style.backgroundImage = `url('${img}')`;
|
| 598 |
-
ghost.style.backgroundSize = 'cover';
|
| 599 |
-
ghost.style.backgroundPosition = 'center';
|
| 600 |
-
} else {
|
| 601 |
-
ghost.style.background = 'rgba(30, 41, 59, 0.95)';
|
| 602 |
-
}
|
| 603 |
-
|
| 604 |
-
ghost.style.left = x + 'px';
|
| 605 |
-
ghost.style.top = y + 'px';
|
| 606 |
-
ghost.classList.remove('hidden');
|
| 607 |
-
}
|
| 608 |
-
|
| 609 |
-
// Update ghost position on touch move
|
| 610 |
-
document.addEventListener('touchmove', function(event) {
|
| 611 |
-
if (isDragging && dragData) {
|
| 612 |
-
event.preventDefault();
|
| 613 |
-
const touch = event.touches[0];
|
| 614 |
-
const ghost = document.getElementById('dragGhost');
|
| 615 |
-
ghost.style.left = touch.clientX + 'px';
|
| 616 |
-
ghost.style.top = touch.clientY + 'px';
|
| 617 |
-
|
| 618 |
-
// Check if over a slot
|
| 619 |
-
const elements = document.elementsFromPoint(touch.clientX, touch.clientY);
|
| 620 |
-
const slot = elements.find(el => el.classList.contains('roster-slot'));
|
| 621 |
-
|
| 622 |
-
// Remove drag-over from all slots
|
| 623 |
-
document.querySelectorAll('.roster-slot.drag-over').forEach(s => {
|
| 624 |
-
s.classList.remove('drag-over');
|
| 625 |
-
});
|
| 626 |
-
|
| 627 |
-
// Add drag-over to current slot
|
| 628 |
-
if (slot) {
|
| 629 |
-
slot.classList.add('drag-over');
|
| 630 |
-
}
|
| 631 |
-
}
|
| 632 |
-
}, { passive: false });
|
| 633 |
-
|
| 634 |
-
// Hide ghost on touch end
|
| 635 |
-
document.addEventListener('touchend', function(event) {
|
| 636 |
-
if (isDragging) {
|
| 637 |
-
const ghost = document.getElementById('dragGhost');
|
| 638 |
-
ghost.classList.add('hidden');
|
| 639 |
-
|
| 640 |
-
// Remove drag-over from all slots
|
| 641 |
-
document.querySelectorAll('.roster-slot.drag-over').forEach(s => {
|
| 642 |
-
s.classList.remove('drag-over');
|
| 643 |
-
});
|
| 644 |
-
|
| 645 |
-
if (currentDragCard) {
|
| 646 |
-
currentDragCard.classList.remove('dragging');
|
| 647 |
-
}
|
| 648 |
-
|
| 649 |
-
isDragging = false;
|
| 650 |
-
dragData = null;
|
| 651 |
-
currentDragCard = null;
|
| 652 |
-
}
|
| 653 |
-
});
|
| 654 |
-
|
| 655 |
// --- RENDER SLOT ---
|
| 656 |
function renderSlot(slot, data) {
|
| 657 |
slot.setAttribute('data-player-id', data.id);
|
|
@@ -922,13 +722,10 @@
|
|
| 922 |
|
| 923 |
const html = `
|
| 924 |
<div id="row-${uid}" draggable="true" ondragstart="drag(event)"
|
| 925 |
-
ontouchstart="
|
| 926 |
class="member-card aspect-[3/4] bg-slate-800 border border-slate-700 rounded-xl flex flex-col relative group overflow-hidden"
|
| 927 |
data-id="${pid}">
|
| 928 |
|
| 929 |
-
<!-- iPhone-style drag handle (челка) -->
|
| 930 |
-
<div class="drag-handle"></div>
|
| 931 |
-
|
| 932 |
<button onclick="document.getElementById('row-${uid}').remove()"
|
| 933 |
class="absolute top-2 right-2 z-20 bg-black/60 hover:bg-red-600 text-white rounded-full w-6 h-6 flex items-center justify-center text-xs opacity-0 group-hover:opacity-100 transition-opacity">
|
| 934 |
✕
|
|
@@ -986,4 +783,4 @@
|
|
| 986 |
});
|
| 987 |
</script>
|
| 988 |
</body>
|
| 989 |
-
</html>
|
|
|
|
| 51 |
transition: transform 0.2s, box-shadow 0.2s;
|
| 52 |
touch-action: none;
|
| 53 |
user-select: none;
|
|
|
|
| 54 |
}
|
| 55 |
.member-card:active { cursor: grabbing; transform: scale(0.98); }
|
| 56 |
.member-card:hover {
|
|
|
|
| 58 |
border-color: #3b82f6;
|
| 59 |
}
|
| 60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
/* Spinner */
|
| 62 |
.spinner {
|
| 63 |
border: 3px solid rgba(255,255,255,0.3);
|
|
|
|
| 76 |
@media (max-width: 768px) {
|
| 77 |
.roster-slot { min-width: 100px; }
|
| 78 |
#playersContainer { grid-template-columns: repeat(2, 1fr); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
}
|
| 80 |
|
| 81 |
/* Share modal */
|
|
|
|
| 224 |
</div>
|
| 225 |
</div>
|
| 226 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
<!-- SHARE MODAL -->
|
| 228 |
<div id="shareModal" class="share-modal">
|
| 229 |
<div class="share-content">
|
|
|
|
| 277 |
window.teamsCache = {};
|
| 278 |
window.isSharedMode = false;
|
| 279 |
|
| 280 |
+
// Touch drag & drop variables
|
| 281 |
+
let touchDragData = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 282 |
let touchStartX = 0;
|
| 283 |
let touchStartY = 0;
|
| 284 |
+
let touchStartTime = 0;
|
|
|
|
| 285 |
|
| 286 |
// Check if we're in shared mode (has teamId in URL)
|
| 287 |
const urlParams = new URLSearchParams(window.location.search);
|
|
|
|
| 345 |
|
| 346 |
// --- TOUCH DRAG & DROP (Mobile Support) ---
|
| 347 |
window.touchStart = function(event) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 348 |
touchStartX = event.touches[0].clientX;
|
| 349 |
touchStartY = event.touches[0].clientY;
|
| 350 |
touchStartTime = Date.now();
|
|
|
|
| 366 |
const timeDiff = touchEndTime - touchStartTime;
|
| 367 |
|
| 368 |
// If it's a tap (small movement, short time) and we have drag data, drop it
|
| 369 |
+
if (distX < 10 && distY < 10 && timeDiff < 500 && touchDragData) {
|
| 370 |
const slot = event.target.closest('.roster-slot');
|
| 371 |
if (slot) {
|
| 372 |
+
handleDrop(slot, touchDragData);
|
| 373 |
+
touchDragData = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 374 |
}
|
| 375 |
}
|
| 376 |
event.preventDefault();
|
| 377 |
}
|
| 378 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 379 |
// --- DRAG & DROP LOGIC (Desktop & Mobile) ---
|
| 380 |
const ROLE_LIMITS = { 'coach': 1, 'striker': 2, 'keeper': 1, 'sweeper': 1, 'libero': 1, 'guide': 1 };
|
| 381 |
|
|
|
|
| 414 |
ev.dataTransfer.setData("text/plain", payload);
|
| 415 |
|
| 416 |
// For mobile touch
|
| 417 |
+
touchDragData = { id, name, role, img };
|
|
|
|
|
|
|
| 418 |
}
|
| 419 |
|
| 420 |
window.drop = function(ev) {
|
|
|
|
| 452 |
renderSlot(slot, data);
|
| 453 |
}
|
| 454 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 455 |
// --- RENDER SLOT ---
|
| 456 |
function renderSlot(slot, data) {
|
| 457 |
slot.setAttribute('data-player-id', data.id);
|
|
|
|
| 722 |
|
| 723 |
const html = `
|
| 724 |
<div id="row-${uid}" draggable="true" ondragstart="drag(event)"
|
| 725 |
+
ontouchstart="drag(event)"
|
| 726 |
class="member-card aspect-[3/4] bg-slate-800 border border-slate-700 rounded-xl flex flex-col relative group overflow-hidden"
|
| 727 |
data-id="${pid}">
|
| 728 |
|
|
|
|
|
|
|
|
|
|
| 729 |
<button onclick="document.getElementById('row-${uid}').remove()"
|
| 730 |
class="absolute top-2 right-2 z-20 bg-black/60 hover:bg-red-600 text-white rounded-full w-6 h-6 flex items-center justify-center text-xs opacity-0 group-hover:opacity-100 transition-opacity">
|
| 731 |
✕
|
|
|
|
| 783 |
});
|
| 784 |
</script>
|
| 785 |
</body>
|
| 786 |
+
</html>
|