Mobile-first i pixel-perfect: wspólne --cell-size, grid dla podpowiedzi, dynamiczny rozmiar komórki z uwzględnieniem paddingów i szerokości opisów; poprawa touch/pointer i double-tap na mobile; wyrównanie layoutu bez nachodzenia na desktopie

This commit is contained in:
2026-02-08 15:02:59 +01:00
parent f4b4360021
commit bd24ca16bc
5 changed files with 145 additions and 24 deletions

View File

@@ -1,5 +1,5 @@
<script setup>
import { onMounted, onUnmounted, computed } from 'vue';
import { onMounted, onUnmounted, computed, ref, watch, nextTick } from 'vue';
import { usePuzzleStore } from '@/stores/puzzle';
import { useHints } from '@/composables/useHints';
import { useNonogram } from '@/composables/useNonogram';
@@ -10,30 +10,81 @@ const store = usePuzzleStore();
const { rowHints, colHints } = useHints(computed(() => store.solution));
const { startDrag, onMouseEnter, stopDrag } = useNonogram();
// Global mouseup to stop dragging even if mouse leaves grid
const cellSize = ref(30);
const rowHintsRef = ref(null);
const getRowHintsWidth = () => {
const el = rowHintsRef.value?.$el;
if (!el) return 0;
return el.offsetWidth || 0;
};
const computeCellSize = () => {
const vw = Math.min(window.innerWidth, 900);
const rootStyles = getComputedStyle(document.documentElement);
const hintWidth = getRowHintsWidth();
const gapRaw = rootStyles.getPropertyValue('--gap-size') || '2px';
const gridPadRaw = rootStyles.getPropertyValue('--grid-padding') || '5px';
const gap = parseFloat(gapRaw);
const gridPad = parseFloat(gridPadRaw);
const bodyStyles = getComputedStyle(document.body);
const bodyPadding = parseFloat(bodyStyles.paddingLeft) + parseFloat(bodyStyles.paddingRight);
const availableForGrid = vw - bodyPadding - hintWidth;
const size = Math.floor((availableForGrid - gridPad * 2 - (store.size - 1) * gap) / store.size);
cellSize.value = Math.max(18, Math.min(36, size));
};
const handleGlobalMouseUp = () => {
stopDrag();
stopDrag();
};
const handleGlobalPointerUp = () => {
stopDrag();
};
const handlePointerMove = (e) => {
const el = document.elementFromPoint(e.clientX, e.clientY);
if (!el) return;
const r = el.getAttribute('data-r');
const c = el.getAttribute('data-c');
if (r != null && c != null) {
onMouseEnter(Number(r), Number(c));
}
};
onMounted(() => {
window.addEventListener('mouseup', handleGlobalMouseUp);
nextTick(() => {
computeCellSize();
});
window.addEventListener('resize', computeCellSize);
window.addEventListener('mouseup', handleGlobalMouseUp);
window.addEventListener('pointerup', handleGlobalPointerUp);
window.addEventListener('touchend', handleGlobalPointerUp, { passive: true });
});
onUnmounted(() => {
window.removeEventListener('mouseup', handleGlobalMouseUp);
window.removeEventListener('resize', computeCellSize);
window.removeEventListener('mouseup', handleGlobalMouseUp);
window.removeEventListener('pointerup', handleGlobalPointerUp);
window.removeEventListener('touchend', handleGlobalPointerUp);
});
watch(() => store.size, async () => {
await nextTick();
computeCellSize();
});
</script>
<template>
<div class="game-board-wrapper">
<div class="game-container">
<div class="game-container" :style="{ '--cell-size': `${cellSize}px` }">
<div class="corner-spacer"></div>
<!-- Column Hints -->
<Hints :hints="colHints" orientation="col" />
<Hints :hints="colHints" orientation="col" :size="store.size" />
<!-- Row Hints -->
<Hints :hints="rowHints" orientation="row" />
<Hints ref="rowHintsRef" :hints="rowHints" orientation="row" :size="store.size" />
<!-- Grid -->
<div
@@ -42,6 +93,7 @@ onUnmounted(() => {
gridTemplateColumns: `repeat(${store.size}, var(--cell-size))`,
gridTemplateRows: `repeat(${store.size}, var(--cell-size))`
}"
@pointermove.prevent="handlePointerMove"
@mouseleave="stopDrag"
>
<template v-for="(row, r) in store.playerGrid" :key="r">
@@ -84,14 +136,13 @@ onUnmounted(() => {
}
.corner-spacer {
width: 100px; /* Must match Row Hints width */
height: auto; /* Adapts to Col Hints height */
}
.grid {
display: grid;
gap: var(--gap-size);
padding: 5px;
padding: var(--grid-padding);
background: rgba(255, 255, 255, 0.05);
border-radius: 4px;
}