From a4681b5b9725ada40b33f78f3dc35fcde740aef6 Mon Sep 17 00:00:00 2001 From: Grzegorz Kucmierz Date: Thu, 12 Feb 2026 18:17:06 +0100 Subject: [PATCH] fix: improve mobile scrollbar visibility and resize handling --- dev-dist/sw.js | 2 +- package-lock.json | 4 ++-- package.json | 4 ++-- src/components/Cell.vue | 13 +++++++++-- src/components/GameBoard.vue | 44 +++++++++++++++++++++++++++++++----- vite.config.js | 3 +++ 6 files changed, 57 insertions(+), 13 deletions(-) diff --git a/dev-dist/sw.js b/dev-dist/sw.js index f5e07aa..3b5e5ef 100644 --- a/dev-dist/sw.js +++ b/dev-dist/sw.js @@ -82,7 +82,7 @@ define(['./workbox-7a5e81cd'], (function (workbox) { 'use strict'; */ workbox.precacheAndRoute([{ "url": "index.html", - "revision": "0.ohmkvc7m8mo" + "revision": "0.0dmrmul42fg" }], {}); workbox.cleanupOutdatedCaches(); workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { diff --git a/package-lock.json b/package-lock.json index 06a4b02..bab73db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vue-nonograms-solid", - "version": "1.11.1", + "version": "1.11.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vue-nonograms-solid", - "version": "1.11.1", + "version": "1.11.2", "dependencies": { "fireworks-js": "^2.10.8", "flag-icons": "^7.5.0", diff --git a/package.json b/package.json index 9e6fc39..c0be63d 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "vue-nonograms-solid", - "version": "1.11.1", + "version": "1.11.2", "homepage": "https://nonograms.7u.pl/", "type": "module", "scripts": { - "dev": "vite", + "dev": "vite --host", "build": "vite build", "preview": "vite preview", "test": "vitest" diff --git a/src/components/Cell.vue b/src/components/Cell.vue index 13ea95b..3fa6c2d 100644 --- a/src/components/Cell.vue +++ b/src/components/Cell.vue @@ -43,21 +43,30 @@ const handlePointerDown = (e) => { const now = Date.now(); if (now - lastTap < 300) { // Double tap -> X (Force) + clearLongPress(); emit('start-drag', props.r, props.c, true, true); lastTap = 0; } else { // Single tap / Start drag -> Fill emit('start-drag', props.r, props.c, false, false); lastTap = now; + + // Start Long Press Timer + clearLongPress(); + longPressTimer = setTimeout(() => { + if (navigator.vibrate) navigator.vibrate(50); + // Switch to Cross (Right click logic, force=true to overwrite the just-placed Fill) + emit('start-drag', props.r, props.c, true, true); + }, 500); } }; const handlePointerUp = (e) => { - // Handled in pointerdown + clearLongPress(); }; const handlePointerCancel = (e) => { - // Handled in pointerdown + clearLongPress(); }; diff --git a/src/components/GameBoard.vue b/src/components/GameBoard.vue index 0614ea0..4a50cf5 100644 --- a/src/components/GameBoard.vue +++ b/src/components/GameBoard.vue @@ -27,17 +27,28 @@ let dragStartLeft = 0; const checkScroll = () => { const el = scrollWrapper.value; if (!el) return; + + const content = el.firstElementChild; + const contentWidth = content ? content.offsetWidth : el.scrollWidth; const sw = el.scrollWidth; const cw = el.clientWidth; // Only show custom scrollbar on mobile/tablet (width < 768px) and if content overflows const isMobile = window.innerWidth <= 768; - showScrollbar.value = isMobile && (sw > cw + 1); + // Use contentWidth to check for overflow, as scrollWidth might be misleading + showScrollbar.value = isMobile && (contentWidth > cw + 1); if (showScrollbar.value) { // Thumb width percentage = (viewport / total) * 100 - const ratio = cw / sw; + // Use contentWidth for more accurate ratio + const ratio = cw / contentWidth; thumbWidth.value = Math.max(10, ratio * 100); + + // Hide if content fits or almost fits (prevent useless scrollbar) + // Increased tolerance to 95% + if (ratio >= 0.95) { + showScrollbar.value = false; + } } }; @@ -175,21 +186,38 @@ const handleGridLeave = () => { activeCol.value = null; }; +const handleResize = () => { + computeCellSize(); + checkScroll(); + // Re-check after potential layout animation/transition + setTimeout(() => { + computeCellSize(); + checkScroll(); + }, 300); +}; + onMounted(() => { nextTick(() => { computeCellSize(); + checkScroll(); + // Extra check for slow layout/font loading or orientation changes + setTimeout(() => { + computeCellSize(); + checkScroll(); + }, 300); }); isFinePointer.value = window.matchMedia('(pointer: fine)').matches; - window.addEventListener('resize', computeCellSize); - window.addEventListener('resize', checkScroll); + + window.addEventListener('resize', handleResize); + window.addEventListener('orientationchange', handleResize); window.addEventListener('mouseup', handleGlobalMouseUp); window.addEventListener('pointerup', handleGlobalPointerUp); window.addEventListener('touchend', handleGlobalPointerUp, { passive: true }); }); onUnmounted(() => { - window.removeEventListener('resize', computeCellSize); - window.removeEventListener('resize', checkScroll); + window.removeEventListener('resize', handleResize); + window.removeEventListener('orientationchange', handleResize); window.removeEventListener('mouseup', handleGlobalMouseUp); window.removeEventListener('pointerup', handleGlobalPointerUp); window.removeEventListener('touchend', handleGlobalPointerUp); @@ -199,6 +227,10 @@ watch(() => store.size, async () => { await nextTick(); computeCellSize(); checkScroll(); + setTimeout(() => { + computeCellSize(); + checkScroll(); + }, 300); }); diff --git a/vite.config.js b/vite.config.js index 66de840..645835c 100644 --- a/vite.config.js +++ b/vite.config.js @@ -48,5 +48,8 @@ export default defineConfig({ alias: { '@': path.resolve(__dirname, './src') } + }, + server: { + allowedHosts: true } })