ux mejoras
This commit is contained in:
@@ -41,6 +41,12 @@
|
|||||||
placeholder="Ingresa tu nombre"
|
placeholder="Ingresa tu nombre"
|
||||||
class="name-input"
|
class="name-input"
|
||||||
maxlength="20"
|
maxlength="20"
|
||||||
|
autocomplete="off"
|
||||||
|
autocapitalize="off"
|
||||||
|
autocorrect="off"
|
||||||
|
spellcheck="false"
|
||||||
|
name="sg_player_name"
|
||||||
|
inputmode="text"
|
||||||
/>
|
/>
|
||||||
<button @click="updateName" class="btn btn-secondary">Confirmar Nombre</button>
|
<button @click="updateName" class="btn btn-secondary">Confirmar Nombre</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -182,7 +188,7 @@ onMounted(async () => {
|
|||||||
|
|
||||||
const room = await colyseusService.joinLobby();
|
const room = await colyseusService.joinLobby();
|
||||||
colorInput.value = colyseusService.playerColor.value || '#667eea';
|
colorInput.value = colyseusService.playerColor.value || '#667eea';
|
||||||
inputName.value = colyseusService.playerName.value || '';
|
// Mantener el input vacío por defecto; no precargar nombres previos
|
||||||
|
|
||||||
let resumed = false;
|
let resumed = false;
|
||||||
const guardResume = () => { if (resumed) return true; resumed = true; return false; };
|
const guardResume = () => { if (resumed) return true; resumed = true; return false; };
|
||||||
@@ -461,7 +467,8 @@ function dismissBanner() {
|
|||||||
|
|
||||||
function startEditName() {
|
function startEditName() {
|
||||||
editingName.value = true;
|
editingName.value = true;
|
||||||
inputName.value = colyseusService.playerName.value || '';
|
// Siempre iniciar la edición con el campo vacío (sin sugerencias)
|
||||||
|
inputName.value = '';
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
nameInputRef.value?.focus();
|
nameInputRef.value?.focus();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -27,26 +27,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-if="isFinal">
|
<template v-if="isFinal">
|
||||||
<div v-if="adminUnlocked" class="modal-actions">
|
<div class="modal-actions">
|
||||||
<button @click="onPrev" class="btn btn-prev-variant">
|
<button @click="onControlAttempt('prev')" class="btn btn-prev-variant" :class="{ locked: !adminUnlocked }" :aria-disabled="!adminUnlocked">Anterior</button>
|
||||||
⏪ {{ previousVariantLabel }}
|
<button @click="onControlAttempt('restart')" class="btn btn-restart-variant" :class="{ locked: !adminUnlocked }" :aria-disabled="!adminUnlocked">Repetir</button>
|
||||||
</button>
|
<button @click="onControlAttempt('next')" class="btn btn-next-variant" :class="{ locked: !adminUnlocked }" :aria-disabled="!adminUnlocked">Siguiente</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>
|
</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>
|
</template>
|
||||||
<div v-else class="round-info">
|
<div v-else class="round-info">
|
||||||
Ronda {{ round }} de {{ totalRounds }} — aún quedan rondas por jugar. La siguiente comenzará en breve.
|
Ronda {{ round }} de {{ totalRounds }} — aún quedan rondas por jugar. La siguiente comenzará en breve.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="hint">Se cerrará en {{ remainingSeconds }}s</div>
|
<div v-if="!isFinal" class="hint">Se cerrará en {{ remainingSeconds }}s</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -100,10 +93,7 @@ function stopTimer() {
|
|||||||
if (timeoutId) { clearTimeout(timeoutId); timeoutId = null; }
|
if (timeoutId) { clearTimeout(timeoutId); timeoutId = null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => props.visible, (v) => {
|
// (moved below isFinal definition)
|
||||||
if (v) startTimer();
|
|
||||||
else stopTimer();
|
|
||||||
}, { immediate: true });
|
|
||||||
|
|
||||||
onBeforeUnmount(() => { stopTimer(); });
|
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 round = computed(() => Math.max(1, Number(props.round || 1)));
|
||||||
const isFinal = computed(() => round.value >= totalRounds.value);
|
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 adminUnlocked = ref(false);
|
||||||
const clickCount = ref(0);
|
const clickCount = ref(0);
|
||||||
|
const showHint = ref(false);
|
||||||
let clickResetTimer: any = null;
|
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; }
|
if (clickResetTimer) { clearTimeout(clickResetTimer); clickResetTimer = null; }
|
||||||
clickCount.value += 1;
|
clickCount.value += 1;
|
||||||
if (clickCount.value >= 5) {
|
if (clickCount.value >= 5) {
|
||||||
adminUnlocked.value = true;
|
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 {
|
} else {
|
||||||
// Small window to keep clicks "seguido"
|
// Small window to keep clicks consecutivos
|
||||||
clickResetTimer = setTimeout(() => { clickCount.value = 0; }, 1200);
|
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) => {
|
watch(() => props.visible, (v) => {
|
||||||
if (v) {
|
if (v) {
|
||||||
adminUnlocked.value = false;
|
adminUnlocked.value = false;
|
||||||
clickCount.value = 0;
|
clickCount.value = 0;
|
||||||
|
showHint.value = false;
|
||||||
if (clickResetTimer) { clearTimeout(clickResetTimer); clickResetTimer = null; }
|
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 { font-size:12px; padding:2px 8px; border-radius:10px; background:#f0f0f0; color:#555; }
|
||||||
.end-modal .score-row .role.P1,
|
.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 .score-row .role.P2 { background: color-mix(in srgb, var(--primary) 15%, white); color: var(--primary); }
|
||||||
.end-modal .modal-actions {
|
.end-modal .modal-actions { margin: 12px 0 8px; display: flex; gap: 6px; justify-content: center; align-items: center; flex-wrap: wrap; }
|
||||||
margin: 16px 0;
|
|
||||||
display: flex;
|
/* Glassy compact buttons, aligned with home style */
|
||||||
gap: 12px;
|
.end-modal .btn {
|
||||||
justify-content: center;
|
appearance: none;
|
||||||
align-items: center;
|
background: linear-gradient(135deg, rgba(102,126,234,0.24) 0%, rgba(118,75,162,0.24) 100%);
|
||||||
}
|
backdrop-filter: blur(12px);
|
||||||
.end-modal .btn-next-variant, .end-modal .btn-prev-variant, .end-modal .btn-restart-variant {
|
-webkit-backdrop-filter: blur(12px);
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
border: 1px solid rgba(255,255,255,0.55);
|
||||||
color: white;
|
color: #243147;
|
||||||
border: none;
|
padding: 6px 10px;
|
||||||
padding: 12px 16px;
|
border-radius: 10px;
|
||||||
border-radius: 8px;
|
font-size: 12px;
|
||||||
font-size: 14px;
|
font-weight: 600;
|
||||||
font-weight: 600;
|
cursor: pointer;
|
||||||
cursor: pointer;
|
transition: transform 0.15s ease, box-shadow 0.2s ease, background 0.2s ease;
|
||||||
transition: all 0.3s ease;
|
box-shadow: 0 6px 18px rgba(102,126,234,0.12);
|
||||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
min-width: 72px;
|
||||||
min-width: 85px;
|
-webkit-tap-highlight-color: transparent;
|
||||||
}
|
|
||||||
.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 .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; }
|
.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 { margin: 10px 2px 2px; font-size: 13px; font-weight:600; color:#334155; text-align:center; }
|
||||||
.round-info.clickable { cursor: pointer; user-select: none; }
|
.round-info.clickable { cursor: pointer; user-select: none; }
|
||||||
|
|||||||
Reference in New Issue
Block a user