feat: add mobile keyboard handling to prevent layout squashing

Implemented Visual Viewport API integration to detect and handle mobile
keyboard appearance, preventing contenteditable fields from squashing
the panel layout when the keyboard appears.

**JavaScript Changes (index.js:1886-1948, 522-523):**
- Created setupMobileKeyboardHandling() function:
  * Uses Visual Viewport API to monitor viewport height changes
  * Detects keyboard appearance (viewport < 75% of window height)
  * Adds .rpg-keyboard-visible class when keyboard shows
  * Removes class when keyboard dismisses
  * Gracefully falls back if API not supported
- Created setupContentEditableScrolling() function:
  * Uses event delegation for all contenteditable fields
  * Automatically scrolls focused field into view with 300ms delay
  * Centers field in viewport using scrollIntoView with 'center' block
  * Smooth scrolling animation for better UX
- Added both function calls to initUI() for automatic setup

**CSS Changes (style.css:3232-3252):**
- Added .rpg-keyboard-visible state styling:
  * 20px bottom padding to prevent content being pushed too far up
  * Compact section padding (8px 12px) for stats/info/thoughts
  * Reduced stat bar gap (4px) to maintain layout integrity
- Scoped to mobile viewport only (@media max-width: 1000px)

**Benefits:**
- No layout squashing when keyboard appears on mobile
- Stat bars and charts remain intact and visible
- Focused fields automatically scroll into view
- Smooth animations for professional UX
- Works with all contenteditable fields:
  * Stats (health, energy, hygiene, sustenance, arousal)
  * Inventory and mood/conditions
  * Info box widgets (date, weather, time, location, temperature)
  * Character traits and relationship badges
  * Character thoughts
- Backward compatible (graceful fallback)
- Desktop users completely unaffected

**How It Works:**
1. Visual Viewport API monitors viewport height changes
2. When height drops below 75% threshold, keyboard detected
3. Panel gets .rpg-keyboard-visible class for compact styling
4. Tapped contenteditable field smoothly scrolls to center
5. When keyboard dismisses, layout returns to normal

**Testing Completed:**
- Inventory field editing works without squashing
- Stat values editable with keyboard visible
- Info box widget editing maintains layout
- Character traits and thoughts remain accessible
- No layout breaks or chart distortion
- Smooth scrolling and transitions verified
This commit is contained in:
Lucas 'Paperboy' Rose-Winters
2025-10-16 14:37:43 +11:00
parent 644e893a3d
commit 58b7ea4d21
2 changed files with 88 additions and 0 deletions
+66
View File
@@ -519,6 +519,8 @@ async function initUI() {
setupSettingsPopup();
addDiceQuickReply();
setupPlotButtons();
setupMobileKeyboardHandling();
setupContentEditableScrolling();
}
/**
@@ -1883,6 +1885,70 @@ function removeMobileTabs() {
$('.rpg-divider').show();
}
/**
* Sets up mobile keyboard handling using Visual Viewport API.
* Prevents layout squashing when keyboard appears by detecting
* viewport changes and adding CSS classes for adjustment.
*/
function setupMobileKeyboardHandling() {
if (!window.visualViewport) {
// console.log('[RPG Mobile] Visual Viewport API not supported');
return;
}
const $panel = $('#rpg-companion-panel');
let keyboardVisible = false;
// Listen for viewport resize (keyboard show/hide)
window.visualViewport.addEventListener('resize', () => {
// Only handle if panel is open on mobile
if (!$panel.hasClass('rpg-mobile-open')) return;
const viewportHeight = window.visualViewport.height;
const windowHeight = window.innerHeight;
// Keyboard visible if viewport significantly smaller than window
// Using 75% threshold to account for browser UI variations
const isKeyboardShowing = viewportHeight < windowHeight * 0.75;
if (isKeyboardShowing && !keyboardVisible) {
// Keyboard just appeared
keyboardVisible = true;
$panel.addClass('rpg-keyboard-visible');
// console.log('[RPG Mobile] Keyboard opened');
} else if (!isKeyboardShowing && keyboardVisible) {
// Keyboard just disappeared
keyboardVisible = false;
$panel.removeClass('rpg-keyboard-visible');
// console.log('[RPG Mobile] Keyboard closed');
}
});
}
/**
* Handles focus on contenteditable fields to ensure they're visible when keyboard appears.
* Uses smooth scrolling to bring focused field into view with proper padding.
*/
function setupContentEditableScrolling() {
const $panel = $('#rpg-companion-panel');
// Use event delegation for all contenteditable fields
$panel.on('focusin', '[contenteditable="true"]', function(e) {
const $field = $(this);
// Small delay to let keyboard animate in
setTimeout(() => {
// Scroll field into view with padding
// Using 'center' to ensure field is in middle of viewport
$field[0].scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'nearest'
});
}, 300);
});
}
/**
* Sets up the collapse/expand toggle button for side panels.
*/
+22
View File
@@ -3229,6 +3229,28 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
}
/* ========================================
MOBILE KEYBOARD HANDLING
======================================== */
/* When mobile keyboard is visible, adjust panel layout to prevent squashing */
.rpg-panel.rpg-keyboard-visible {
/* Prevent content from being pushed too far up */
padding-bottom: 20px;
}
/* Make sections more compact when keyboard visible */
.rpg-panel.rpg-keyboard-visible .rpg-stats-section,
.rpg-panel.rpg-keyboard-visible .rpg-info-section,
.rpg-panel.rpg-keyboard-visible .rpg-thoughts-section {
padding: 8px 12px;
}
/* Reduce spacing in stat bars when keyboard visible */
.rpg-panel.rpg-keyboard-visible .rpg-stats-grid {
gap: 4px;
}
/* Disable collapsed state on mobile */
.rpg-panel.rpg-collapsed {
max-width: 100dvw !important;