Implement comprehensive mobile experience framework for entire application

This commit is contained in:
Eric Gullickson
2025-07-27 21:03:06 -05:00
parent f46d471453
commit ea055f1c38
20 changed files with 713 additions and 174 deletions

View File

@@ -375,6 +375,149 @@ function initTagSelector(input, noDataList) {
input.tagsinput();
}
}
// ===== MOBILE EXPERIENCE FRAMEWORK =====
// Mobile detection utility
function isMobileDevice() {
return window.matchMedia("(max-width: 768px)").matches ||
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}
// Mobile-optimized tag selector
function initMobileTagSelector(input) {
if (input.length) {
// Initialize with mobile-friendly options
input.tagsinput({
maxTags: 5, // Limit tags on mobile
trimValue: true,
confirmKeys: [13, 44, 32], // Enter, comma, space
focusClass: 'focus',
freeInput: true
});
// Add mobile-specific styling
input.parent().addClass('mobile-tag-input');
// Increase touch target size for mobile
input.parent().find('.bootstrap-tagsinput').css({
'min-height': '44px',
'padding': '8px',
'font-size': '16px' // Prevent zoom on iOS
});
// Style the input within tags
input.parent().find('.bootstrap-tagsinput input').css({
'font-size': '16px',
'min-height': '30px'
});
}
}
// Universal mobile modal initializer
function initMobileModal(config) {
if (!isMobileDevice()) return;
var modalId = config.modalId || '#modal';
var dateInputId = config.dateInputId;
var tagSelectorId = config.tagSelectorId;
var modeToggleId = config.modeToggleId;
var simpleModeDefault = config.simpleModeDefault || false;
// Convert date input to native HTML5 on mobile
if (dateInputId) {
var dateInput = $(dateInputId);
if (dateInput.length) {
dateInput.attr('type', 'date');
dateInput.removeClass('datepicker'); // Remove Bootstrap datepicker class
}
}
// Default to simple mode on mobile if toggle exists
if (modeToggleId && simpleModeDefault) {
var modeToggle = $(modeToggleId);
if (modeToggle.length && !modeToggle.is(':checked')) {
modeToggle.prop('checked', true);
// Try to call the mode toggle function if it exists
if (typeof toggleFuelEntryMode === 'function') {
toggleFuelEntryMode(true);
}
}
}
// Initialize mobile-friendly tag selector
if (tagSelectorId) {
initMobileTagSelector($(tagSelectorId));
}
// Initialize swipe to dismiss
initSwipeToDismiss(modalId);
}
// Swipe to dismiss functionality for mobile modals
function initSwipeToDismiss(modalSelector) {
if (!isMobileDevice()) return;
modalSelector = modalSelector || '#modal';
var modal = $(modalSelector);
var modalContent = modal.find('.modal-content');
var startY = 0;
var currentY = 0;
var isDragging = false;
var threshold = 100; // Minimum swipe distance to dismiss
// Remove any existing touch handlers to avoid conflicts
modalContent.off('touchstart touchmove touchend');
// Touch start
modalContent.on('touchstart', function(e) {
startY = e.originalEvent.touches[0].clientY;
isDragging = true;
modalContent.css('transition', 'none');
});
// Touch move
modalContent.on('touchmove', function(e) {
if (!isDragging) return;
currentY = e.originalEvent.touches[0].clientY;
var deltaY = currentY - startY;
// Only allow downward swipes
if (deltaY > 0) {
modalContent.css('transform', `translateY(${deltaY}px)`);
}
});
// Touch end
modalContent.on('touchend', function(e) {
if (!isDragging) return;
isDragging = false;
var deltaY = currentY - startY;
modalContent.css('transition', 'transform 0.3s ease-out');
if (deltaY > threshold) {
// Dismiss modal
modalContent.css('transform', 'translateY(100%)');
setTimeout(function() {
modal.modal('hide');
modalContent.css('transform', '');
}, 300);
} else {
// Snap back
modalContent.css('transform', '');
}
});
}
// Initialize form inputs based on device type (legacy compatibility)
function initializeFormInputs() {
// This function is maintained for backward compatibility with gasrecord.js
// New modals should use initMobileModal() instead
console.warn('initializeFormInputs() is deprecated. Use initMobileModal() instead.');
}
function getAndValidateSelectedVehicle() {
var selectedVehiclesArray = [];
$("#vehicleSelector :checked").map(function () {