ux mejoras
This commit is contained in:
@@ -27,26 +27,19 @@
|
||||
</div>
|
||||
|
||||
<template v-if="isFinal">
|
||||
<div v-if="adminUnlocked" class="modal-actions">
|
||||
<button @click="onPrev" class="btn btn-prev-variant">
|
||||
⏪ {{ previousVariantLabel }}
|
||||
</button>
|
||||
<button @click="onRestart" class="btn btn-restart-variant">
|
||||
🔄 {{ currentVariant }}
|
||||
</button>
|
||||
<button @click="onNext" class="btn btn-next-variant">
|
||||
{{ nextVariantLabel }} ⏩
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="round-info clickable" @click="onRoundInfoClick" :title="roundInfoTitle">
|
||||
(espera a que el administrador continúe la partida)
|
||||
<div class="modal-actions">
|
||||
<button @click="onControlAttempt('prev')" class="btn btn-prev-variant" :class="{ locked: !adminUnlocked }" :aria-disabled="!adminUnlocked">Anterior</button>
|
||||
<button @click="onControlAttempt('restart')" class="btn btn-restart-variant" :class="{ locked: !adminUnlocked }" :aria-disabled="!adminUnlocked">Repetir</button>
|
||||
<button @click="onControlAttempt('next')" class="btn btn-next-variant" :class="{ locked: !adminUnlocked }" :aria-disabled="!adminUnlocked">Siguiente</button>
|
||||
</div>
|
||||
<div v-if="!adminUnlocked && showHint" class="round-info">Controles bloqueados — clics: {{ clickCount }}/5 para habilitarlos</div>
|
||||
<div v-else-if="adminUnlocked" class="round-info">Controles habilitados</div>
|
||||
</template>
|
||||
<div v-else class="round-info">
|
||||
Ronda {{ round }} de {{ totalRounds }} — aún quedan rondas por jugar. La siguiente comenzará en breve.
|
||||
</div>
|
||||
|
||||
<div class="hint">Se cerrará en {{ remainingSeconds }}s</div>
|
||||
<div v-if="!isFinal" class="hint">Se cerrará en {{ remainingSeconds }}s</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -100,10 +93,7 @@ function stopTimer() {
|
||||
if (timeoutId) { clearTimeout(timeoutId); timeoutId = null; }
|
||||
}
|
||||
|
||||
watch(() => props.visible, (v) => {
|
||||
if (v) startTimer();
|
||||
else stopTimer();
|
||||
}, { immediate: true });
|
||||
// (moved below isFinal definition)
|
||||
|
||||
onBeforeUnmount(() => { stopTimer(); });
|
||||
|
||||
@@ -111,28 +101,46 @@ const totalRounds = computed(() => Math.max(1, Number(props.totalRounds || 3)));
|
||||
const round = computed(() => Math.max(1, Number(props.round || 1)));
|
||||
const isFinal = computed(() => round.value >= totalRounds.value);
|
||||
|
||||
// Hidden admin unlock via 5 rapid clicks on the round info text
|
||||
// Auto-cierre solo cuando no es final
|
||||
watch([() => props.visible, () => isFinal.value], ([vis, final]) => {
|
||||
if (vis && !final) startTimer();
|
||||
else stopTimer();
|
||||
}, { immediate: true });
|
||||
|
||||
// Hidden admin unlock via 5 rapid clicks on the control buttons
|
||||
const adminUnlocked = ref(false);
|
||||
const clickCount = ref(0);
|
||||
const showHint = ref(false);
|
||||
let clickResetTimer: any = null;
|
||||
|
||||
function onRoundInfoClick() {
|
||||
function onControlAttempt(kind: 'prev'|'next'|'restart') {
|
||||
if (adminUnlocked.value) {
|
||||
if (kind === 'prev') onPrev();
|
||||
else if (kind === 'next') onNext();
|
||||
else onRestart();
|
||||
return;
|
||||
}
|
||||
// Locked: count clicks and show hint
|
||||
showHint.value = true;
|
||||
if (clickResetTimer) { clearTimeout(clickResetTimer); clickResetTimer = null; }
|
||||
clickCount.value += 1;
|
||||
if (clickCount.value >= 5) {
|
||||
adminUnlocked.value = true;
|
||||
// Perform the action that was attempted on the unlocking click
|
||||
if (kind === 'prev') onPrev();
|
||||
else if (kind === 'next') onNext();
|
||||
else onRestart();
|
||||
} else {
|
||||
// Small window to keep clicks "seguido"
|
||||
clickResetTimer = setTimeout(() => { clickCount.value = 0; }, 1200);
|
||||
// Small window to keep clicks consecutivos
|
||||
clickResetTimer = setTimeout(() => { clickCount.value = 0; showHint.value = false; }, 1200);
|
||||
}
|
||||
}
|
||||
|
||||
const roundInfoTitle = computed(() => adminUnlocked.value ? 'Controles de variante desbloqueados' : `Clicks: ${clickCount.value}/5 para desbloquear`);
|
||||
|
||||
watch(() => props.visible, (v) => {
|
||||
if (v) {
|
||||
adminUnlocked.value = false;
|
||||
clickCount.value = 0;
|
||||
showHint.value = false;
|
||||
if (clickResetTimer) { clearTimeout(clickResetTimer); clickResetTimer = null; }
|
||||
}
|
||||
});
|
||||
@@ -199,46 +207,29 @@ function onRestart() { emit('restart-variant'); }
|
||||
.end-modal .score-row .role { font-size:12px; padding:2px 8px; border-radius:10px; background:#f0f0f0; color:#555; }
|
||||
.end-modal .score-row .role.P1,
|
||||
.end-modal .score-row .role.P2 { background: color-mix(in srgb, var(--primary) 15%, white); color: var(--primary); }
|
||||
.end-modal .modal-actions {
|
||||
margin: 16px 0;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.end-modal .btn-next-variant, .end-modal .btn-prev-variant, .end-modal .btn-restart-variant {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
||||
min-width: 85px;
|
||||
}
|
||||
.end-modal .btn-prev-variant {
|
||||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||
box-shadow: 0 4px 12px rgba(240, 147, 251, 0.3);
|
||||
}
|
||||
.end-modal .btn-restart-variant {
|
||||
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||
box-shadow: 0 4px 12px rgba(79, 172, 254, 0.3);
|
||||
}
|
||||
.end-modal .btn-next-variant:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
.end-modal .btn-prev-variant:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(240, 147, 251, 0.4);
|
||||
}
|
||||
.end-modal .btn-restart-variant:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(79, 172, 254, 0.4);
|
||||
.end-modal .modal-actions { margin: 12px 0 8px; display: flex; gap: 6px; justify-content: center; align-items: center; flex-wrap: wrap; }
|
||||
|
||||
/* Glassy compact buttons, aligned with home style */
|
||||
.end-modal .btn {
|
||||
appearance: none;
|
||||
background: linear-gradient(135deg, rgba(102,126,234,0.24) 0%, rgba(118,75,162,0.24) 100%);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(255,255,255,0.55);
|
||||
color: #243147;
|
||||
padding: 6px 10px;
|
||||
border-radius: 10px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: transform 0.15s ease, box-shadow 0.2s ease, background 0.2s ease;
|
||||
box-shadow: 0 6px 18px rgba(102,126,234,0.12);
|
||||
min-width: 72px;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.end-modal .btn:hover { transform: translateY(-1px); box-shadow: 0 6px 16px rgba(118,75,162,0.16); }
|
||||
.end-modal .btn:active { transform: translateY(0); box-shadow: 0 2px 8px rgba(0,0,0,0.12); }
|
||||
.end-modal .btn:focus, .end-modal .btn:focus-visible { outline: none; }
|
||||
.end-modal .hint { font-size: 12px; color:#6b7280; text-align:right; }
|
||||
.round-info { margin: 10px 2px 2px; font-size: 13px; font-weight:600; color:#334155; text-align:center; }
|
||||
.round-info.clickable { cursor: pointer; user-select: none; }
|
||||
|
||||
Reference in New Issue
Block a user